| 有了前面两个实验,我们就可以依葫芦画瓢,实现自己想要的驱动了。本实验通过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
 |