Linux內核添加設備驅動方法

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章