有了前面两个实验,我们就可以依葫芦画瓢,实现自己想要的驱动了。本实验通过sysfs,即前面的kobject实现LED灯的控制。默认x4412开发板已经将LED驱动集成进去了,但是它使用的linux内核自带的驱动,尽管它本质上也是使用的sysfs文件系统,但是由于里面结构复杂,不便于新手理解,因此我们特别将它提取出来,编写了非常简单明了的驱动。用户真正消化本驱动后,再去研究自带的驱动,就显得得心应手了。 在kernel/drivers/char/x4412目录下创建x4412-led-sysfs.c文件,编辑源码如下: - #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/platform_device.h>
- #include <mach/hardware.h>
- #include <asm/mach-types.h>
- #include <linux/gpio.h>
- #include <plat/gpio-cfg.h>
- /*
- * X4412:
- *
- * LED1 -> D22 -> XEINT14
- * LED2 -> D23 -> XEINT15
- * LED3 -> D24 -> XEINT22
- * LED4 -> D25 -> XEINT23
- */
- static int __x4412_led_status[4] = { 0 };
- static void __x4412_led_probe(void) //初始化LED对应GPIO口
- {
- int ret;
- ret = gpio_request(EXYNOS4_GPX1(6), "GPX1");
- if(ret)
- printk("x4412-led: request gpio GPX1(6) fail\n");
- s3c_gpio_setpull(EXYNOS4_GPX1(6), S3C_GPIO_PULL_UP);
- gpio_direction_output(EXYNOS4_GPX1(6), 1);
- ret = gpio_request(EXYNOS4_GPX1(7), "GPX1");
- if(ret)
- printk("x4412-led: request gpio GPX1(7) fail\n");
- s3c_gpio_setpull(EXYNOS4_GPX1(7), S3C_GPIO_PULL_UP);
- gpio_direction_output(EXYNOS4_GPX1(7), 1);
- ret = gpio_request(EXYNOS4_GPX2(6), "GPX2");
- if(ret)
- printk("x4412-led: request gpio GPX2(6) fail\n");
- s3c_gpio_setpull(EXYNOS4_GPX2(6), S3C_GPIO_PULL_UP);
- gpio_direction_output(EXYNOS4_GPX2(6), 1);
- ret = gpio_request(EXYNOS4_GPX2(7), "GPX2");
- if(ret)
- printk("x4412-led: request gpio GPX2(7) fail\n");
- s3c_gpio_setpull(EXYNOS4_GPX2(7), S3C_GPIO_PULL_UP);
- gpio_direction_output(EXYNOS4_GPX2(7), 1);
- __x4412_led_status[0] = 0; //定义四盏LED灯的默认状态
- __x4412_led_status[1] = 0;
- __x4412_led_status[2] = 0;
- __x4412_led_status[3] = 0;
- }
- static void __x4412_led_remove(void) //释放GPIO口
- {
- gpio_free(EXYNOS4_GPX1(6));
- gpio_free(EXYNOS4_GPX1(7));
- gpio_free(EXYNOS4_GPX2(6));
- gpio_free(EXYNOS4_GPX2(7));
- }
- static ssize_t x4412_led_read(struct device *dev, struct device_attribute *attr, char *buf)
- {
- if(!strcmp(attr->attr.name, "led1")) //根据从应用读取的文件名称确定操作指定LED灯
- {
- if(__x4412_led_status[0] != 0) //返回给应用LED灯当前的状态
- return strlcpy(buf, "1\n", 3);
- else
- return strlcpy(buf, "0\n", 3);
- }
- else if(!strcmp(attr->attr.name, "led2"))
- {
- if(__x4412_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(__x4412_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(__x4412_led_status[3] != 0)
- return strlcpy(buf, "1\n", 3);
- else
- return strlcpy(buf, "0\n", 3);
- }
- return strlcpy(buf, "\n", 3);
- }
- static ssize_t x4412_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) //如果收到1,则将对应标志位置1,同时将对应GPIO置0,对应LED灯亮
- {
- gpio_direction_output(EXYNOS4_GPX1(6), 0);
- __x4412_led_status[0] = 1;
- }
- else //如果收到0,则将对应标志位清0,同时将对应GPIO置1,对应LED灯灭
- {
- gpio_direction_output(EXYNOS4_GPX1(6), 1);
- __x4412_led_status[0] = 0;
- }
- }
- else if(!strcmp(attr->attr.name, "led2"))
- {
- if(on)
- {
- gpio_direction_output(EXYNOS4_GPX1(7), 0);
- __x4412_led_status[1] = 1;
- }
- else
- {
- gpio_direction_output(EXYNOS4_GPX1(7), 1);
- __x4412_led_status[1] = 0;
- }
- }
- else if(!strcmp(attr->attr.name, "led3"))
- {
- if(on)
- {
- gpio_direction_output(EXYNOS4_GPX2(6), 0);
- __x4412_led_status[2] = 1;
- }
- else
- {
- gpio_direction_output(EXYNOS4_GPX2(6), 1);
- __x4412_led_status[2] = 0;
- }
- }
- else if(!strcmp(attr->attr.name, "led4"))
- {
- if(on)
- {
- gpio_direction_output(EXYNOS4_GPX2(7), 0);
- __x4412_led_status[3] = 1;
- }
- else
- {
- gpio_direction_output(EXYNOS4_GPX2(7), 1);
- __x4412_led_status[3] = 0;
- }
- }
-
- return count;
- }
- //kobject目录下的四个文件对应的属性,读写函数
- static DEVICE_ATTR(led1, 0666, x4412_led_read, x4412_led_write);
- static DEVICE_ATTR(led2, 0666, x4412_led_read, x4412_led_write);
- static DEVICE_ATTR(led3, 0666, x4412_led_read, x4412_led_write);
- static DEVICE_ATTR(led4, 0666, x4412_led_read, x4412_led_write);
- static struct attribute * x4412_led_sysfs_entries[] = { //对应kobject目录下的四个文件
- &dev_attr_led1.attr,
- &dev_attr_led2.attr,
- &dev_attr_led3.attr,
- &dev_attr_led4.attr,
- NULL,
- };
- static struct attribute_group x4412_led_attr_group = {
- .name = NULL,
- .attrs = x4412_led_sysfs_entries, //指定注册的kobject对应的文件属性接入点
- };
- static int x4412_led_probe(struct platform_device *pdev)
- {
- __x4412_led_probe(); //初始化LED对应GPIO口
- return sysfs_create_group(&pdev->dev.kobj, &x4412_led_attr_group);//注册kobject
- }
- static int x4412_led_remove(struct platform_device *pdev)
- {
- __x4412_led_remove(); //释放GPIO
- sysfs_remove_group(&pdev->dev.kobj, &x4412_led_attr_group);//注销kobject
- return 0;
- }
- static struct platform_driver x4412_led_driver = {
- .probe = x4412_led_probe, //平台驱动探测函数
- .remove = x4412_led_remove,
- .driver = {
- .name = "x4412-led",
- },
- };
- static struct platform_device x4412_led_device = {
- .name = "x4412-led",
- .id = -1,
- };
- static int __devinit x4412_led_init(void)
- {
- int ret;
- printk("x4412 led driver\r\n");
- ret = platform_device_register(&x4412_led_device); //注册平台设备
- if(ret)
- printk("failed to register x4412 led device\n");
- ret = platform_driver_register(&x4412_led_driver); //注册平台驱动
- if(ret)
- printk("failed to register x4412 led driver\n");
- return ret;
- }
- static void x4412_led_exit(void)
- {
- platform_driver_unregister(&x4412_led_driver);
- }
- module_init(x4412_led_init);
- module_exit(x4412_led_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("www.9tripod.com");
- MODULE_DESCRIPTION("x4412 led driver");
复制代码 本驱动并不像前面两个驱动那样,只是单一的驱动模块,而是引入了平台设备和平台驱动,然后再借助于平台驱动的探测函数,直接调用sysfs_create_group函数将kobject注册进平台设备,后面生成的相关文件将会出现在/sys/devices/platform目录,而不是/sys或/sys/kernel目录。而且,在平台设备下注册kobject,无需再调用kobject_init_and_add()函数或kobject_create_and_add()函数。 将上一章节的x4412-kobject驱动通过本章节创建LED驱动的方式注册到平台设备,参考源码如下: - #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/string.h>
- #include <linux/sysfs.h>
- #include <linux/platform_device.h>
- char str[] = "www.9tripod.com";
- static ssize_t x4412_kobj_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- return sprintf(buf, "%s\n", str);
- }
- static ssize_t x4412_kobj_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
- {
- sscanf(buf, "%s", str);
- return count;
- }
-
- static DEVICE_ATTR(xbox, S_IRWXUGO, x4412_kobj_show, x4412_kobj_store);
- static struct attribute * x4412_kobj_sysfs_entries[] = {
- &dev_attr_xbox.attr,
- NULL,
- };
-
- static struct attribute_group x4412_kobj_attr_group = {
- .name = NULL,
- .attrs = x4412_kobj_sysfs_entries,
- };
-
- static int x4412_kobj_probe(struct platform_device *pdev)
- {
- return sysfs_create_group(&pdev->dev.kobj, &x4412_kobj_attr_group);
- }
-
- static int x4412_kobj_remove(struct platform_device *pdev)
- {
- sysfs_remove_group(&pdev->dev.kobj, &x4412_kobj_attr_group);
- return 0;
- }
- static struct platform_driver x4412_kobj_driver = {
- .probe = x4412_kobj_probe,
- .remove = x4412_kobj_remove,
- .driver = {
- .name = "x4412-kobj",
- },
- };
-
- static struct platform_device x4412_kobj_device = {
- .name = "x4412-kobj",
- .id = -1,
- };
- static int x4412_kobj_init(void)
- {
- int ret;
- printk("x4412 kobj driver\r\n");
- ret = platform_device_register(&x4412_kobj_device);
- if(ret)
- printk("failed to register x4412 kobj device\n");
- ret = platform_driver_register(&x4412_kobj_driver);
- if(ret)
- printk("failed to register x4412 kobj driver\n");
- return ret;
- }
- static void x4412_kobj_exit(void)
- {
- platform_driver_unregister(&x4412_kobj_driver);
- }
- module_init(x4412_kobj_init);
- module_exit(x4412_kobj_exit);
- MODULE_AUTHOR("www.9tripod.com");
- MODULE_LICENSE("GPL");
复制代码 重新编译成模块后,加载驱动模块,测试结果如下: - [root@x4412 mnt]# insmod x4412-kobject.ko
- [ 44.455498] x4412 led driver
- [root@x4412 mnt]# cd /sys/devices/platform/x4412-kobj/
- [root@x4412 x4412-kobj]#
- [root@x4412 x4412-kobj]# ls
- driver@ modalias power/ subsystem@ uevent xbox*
- [root@x4412 x4412-kobj]# cat xbox
- www.9tripod.com
- [root@x4412 x4412-kobj]# echo www.bbs.9tripod.com > xbox
- [root@x4412 x4412-kobj]# cat xbox
- www.bbs.9tripod.com
- [root@x4412 x4412-kobj]#
复制代码 可见,驱动创建的文件已经移步到/sys/devices/platform目录了。通常移植驱动,都会采用这种方式,而很少采用前面的方式。 再回到LED驱动,在kernel/drivers/char/x4412/Kconfig中添加如下语句: - config X4412_LED_SYSFS_DRIVER
- tristate "x4412 led sysfs driver"
- default y
- help
- compile for x4412 sysfs driver,y for kernel,m for module.
复制代码 在kernel/drivers/char/x4412/Makefile中添加如下语句: - obj-$(CONFIG_X4412_LED_SYSFS_DRIVER) += x4412-led-sysfs.o
复制代码 编译源码,更新内核后,执行如下指令,即通过sysfs文件调用驱动中的读写函数,进而控制LED对应GPIO,实现LED灯的亮灭控制。 - echo 0 > sys/devices/platform/x4412-led/led1
- echo 1 > sys/devices/platform/x4412-led/led1
- echo 0 > sys/devices/platform/x4412-led/led2
- echo 1 > sys/devices/platform/x4412-led/led2
- echo 0 > sys/devices/platform/x4412-led/led3
- echo 1 > sys/devices/platform/x4412-led/led3
- echo 0 > sys/devices/platform/x4412-led/led4
- echo 1 > sys/devices/platform/x4412-led/led4
复制代码 |