Linux内核添加设备驱动方法

来源:互联网 发布:淘宝跳蚤街在哪 编辑:程序博客网 时间:2024/05/15 13:21

有时我们希望驱动可以在Linux编译的时候通过make menuconfig添加和移除,可通过下面方法实现:

1.LED驱动为例

(1)在kernel\drivers\char\目录下创建一个led目录,之后编写的led驱动代码将放在这个目录中。

(2)修改kernel\drivers\char\目录中的Makefile将led目录包含进去。

即:在Makefile中添加   obj-y += led/  即可。

obj-y:表示由xx.c 或者 foo.s 文件编译得到foo.o 并连接进内核.
obj-m: 则表示该文件作为模块编译.
除了y、m以外的obj-x 形式的目标都不会被编译。

(3)在led目录中添加Makefile文件和Kconfig文件,并添加如下内容

Makefile文件:

obj-$(CONFIG_MY_LED_DRIVER) += my-led.o

Kconfig文件:

config MY_LED_DRIVER
bool "my led driver"
default y
help
compile for leddriver,y for kernel,m for module.

(4)编写my_led.c驱动代码。

#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <mach/hardware.h>#include <asm/mach-types.h>#include <linux/gpio.h>#include <asm/gpio.h>#include <asm/delay.h>#include <linux/clk.h>#include <plat/gpio-cfg.h>/* * LED1 -> D22 -> GPJ0_3 * LED2 -> D23 -> GPJ0_4 * LED3 -> D24 -> GPJ0_5 * LED4 -> D25 -> GPD0_1 */static int __my_led_status[4] = { 0 };static void __my_led_probe(void){int ret;ret = gpio_request(S5PV210_GPJ0(3), "GPJ0");if(ret)printk("my-led: request gpio GPJ0(3) fail\n");s3c_gpio_setpull(S5PV210_GPJ0(3), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPJ0(3), S3C_GPIO_SFN(1));gpio_set_value(S5PV210_GPJ0(3), 1);ret = gpio_request(S5PV210_GPJ0(4), "GPJ0");if(ret)printk("my-led: request gpio GPJ0(4) fail\n");s3c_gpio_setpull(S5PV210_GPJ0(4), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPJ0(4), S3C_GPIO_SFN(1));gpio_set_value(S5PV210_GPJ0(4), 1);ret = gpio_request(S5PV210_GPJ0(5), "GPJ0");if(ret)printk("my-led: request gpio GPJ0(5) fail\n");s3c_gpio_setpull(S5PV210_GPJ0(5), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPJ0(5), S3C_GPIO_SFN(1));gpio_set_value(S5PV210_GPJ0(5), 1);ret = gpio_request(S5PV210_GPD0(1), "GPD0");if(ret)printk("my-led: request gpio GPD0(1) fail\n");s3c_gpio_setpull(S5PV210_GPD0(1), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPD0(1), S3C_GPIO_SFN(1));gpio_set_value(S5PV210_GPD0(1), 1);__my_led_status[0] = 0;__my_led_status[1] = 0;__my_led_status[2] = 0;__my_led_status[3] = 0;}static void __my_led_remove(void){gpio_free(S5PV210_GPJ0(3));gpio_free(S5PV210_GPJ0(4));gpio_free(S5PV210_GPJ0(5));gpio_free(S5PV210_GPD0(1));}static ssize_t my_led_read(struct device *dev, struct device_attribute *attr, char *buf){if(!strcmp(attr->attr.name, "led1")){if(__my_led_status[0] != 0)return strlcpy(buf, "1\n", 3);elsereturn strlcpy(buf, "0\n", 3);}else if(!strcmp(attr->attr.name, "led2")){if(__my_led_status[1] != 0)return strlcpy(buf, "1\n", 3);elsereturn strlcpy(buf, "0\n", 3);}else if(!strcmp(attr->attr.name, "led3")){if(__my_led_status[2] != 0)return strlcpy(buf, "1\n", 3);elsereturn strlcpy(buf, "0\n", 3);}else if(!strcmp(attr->attr.name, "led4")){if(__my_led_status[3] != 0)return strlcpy(buf, "1\n", 3);elsereturn strlcpy(buf, "0\n", 3);}}static ssize_t my_led_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){unsigned long on = simple_strtoul(buf, NULL, 10);if(!strcmp(attr->attr.name, "led1")){if(on){gpio_set_value(S5PV210_GPJ0(3), 0);__my_led_status[0] = 1;}else{gpio_set_value(S5PV210_GPJ0(3), 1);__my_led_status[0] = 0;}}else if(!strcmp(attr->attr.name, "led2")){if(on){gpio_set_value(S5PV210_GPJ0(4), 0);__my_led_status[1] = 1;}else{gpio_set_value(S5PV210_GPJ0(4), 1);__my_led_status[1] = 0;}}else if(!strcmp(attr->attr.name, "led3")){if(on){gpio_set_value(S5PV210_GPJ0(5), 0);__my_led_status[2] = 1;}else{gpio_set_value(S5PV210_GPJ0(5), 1);__my_led_status[2] = 0;}}else if(!strcmp(attr->attr.name, "led4")){if(on){gpio_set_value(S5PV210_GPD0(1), 0);__my_led_status[3] = 1;}else{gpio_set_value(S5PV210_GPD0(1), 1);__my_led_status[3] = 0;}}return count;}static DEVICE_ATTR(led1, 0666, my_led_read, my_led_write);static DEVICE_ATTR(led2, 0666, my_led_read, my_led_write);static DEVICE_ATTR(led3, 0666, my_led_read, my_led_write);static DEVICE_ATTR(led4, 0666, my_led_read, my_led_write);static struct attribute * my_led_sysfs_entries[] = {&dev_attr_led1.attr,&dev_attr_led2.attr,&dev_attr_led3.attr,&dev_attr_led4.attr,NULL,};static struct attribute_group my_led_attr_group = {.name= NULL,.attrs= my_led_sysfs_entries,};static int my_led_probe(struct platform_device *pdev){__my_led_probe();return sysfs_create_group(&pdev->dev.kobj, &my_led_attr_group);}static int my_led_remove(struct platform_device *pdev){__my_led_remove();sysfs_remove_group(&pdev->dev.kobj, &my_led_attr_group);return 0;}static int my_led_suspend(struct platform_device *pdev, pm_message_t state){return 0;}static int my_led_resume(struct platform_device *pdev){return 0;}static struct platform_driver my_led_driver = {.probe= my_led_probe,.remove= my_led_remove,.suspend= my_led_suspend,.resume= my_led_resume,.driver= {.name= "my-led",},};static struct platform_device my_led_device = {.name      = "my-led",.id        = -1,};static int __devinit my_led_init(void){int ret;printk("my led driver\r\n");ret = platform_device_register(&my_led_device);if(ret)printk("failed to register my led device\n");ret = platform_driver_register(&my_led_driver);if(ret)printk("failed to register my led driver\n");return ret;}static void my_led_exit(void){platform_driver_unregister(&my_led_driver);//注销平台驱动}module_init(my_led_init);module_exit(my_led_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("huajianzou");MODULE_DESCRIPTION("my led driver");


扩展知识:
如果一个内核模块从多个源文件构建,KBuild就必须要知道你想从哪些部分构建模块。因此你需要设置$(<module_name>-objs)变量来告诉KBuild。
Example: 
  #drivers/isdn/i4l/Makefile 
  obj-$(CONFIG_ISDN) += isdn.o 
  isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
在这个例子中,模块名是isdn.o,Kbuild将会编译列在$(isdn-objs)的object文件,然后在这些文件的列表中调用"$(LD) -r"来产生isdn.o。
Kbuild使用后缀-objs,-y来识别混合的object文件。这允许Makefiles使用变量CONFIG_sambol来决定一个object是否是混合object的的一部分。

参考博客:

KBuild MakeFile介绍: https://www.cnblogs.com/cecwxf/archive/2012/04/26/2470968.html