有時我們希望驅動可以在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