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



  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值