有时我们希望驱动可以在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);
else
return strlcpy(buf, "0\n", 3);
}
else if(!strcmp(attr->attr.name, "led2"))
{
if(__my_led_status[1] != 0)
return strlcpy(buf, "1\n", 3);
else
return strlcpy(buf, "0\n", 3);
}
else if(!strcmp(attr->attr.name, "led3"))
{
if(__my_led_status[2] != 0)
return strlcpy(buf, "1\n", 3);
else
return strlcpy(buf, "0\n", 3);
}
else if(!strcmp(attr->attr.name, "led4"))
{
if(__my_led_status[3] != 0)
return strlcpy(buf, "1\n", 3);
else
return 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