linux平台总线驱动设备模型之点亮LED

来源:互联网 发布:上瘾网络剧见面会视频 编辑:程序博客网 时间:2024/05/01 04:53

上一节中,我们详细分析了平台驱动设备模型的源码,懂得了框架是如何构成的。

上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/17957637

这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备。

问:如何编写平台总线驱动设备这一套架构的设备驱动?

答:分为两个.c文件,一个是drv.c,另一个是dev.c;前者实现平台驱动,后者实现平台设备,平台总线不用我们自己实现。

问:编写平台驱动的核心内容有哪些?

答:分配、设置、注册一个platform_driver

问:如何注册平台驱动?

答:使用platform_driver_register(struct platform_driver *drv)函数,该函数的参数为platform_driver

问:如何定义platform_driver?

答:简单示例

static struct platform_driver led_driver = {.probe= led_probe,.remove= led_remove,.driver= {.name= "myled",.owner= THIS_MODULE,}};
问:probe函数什么时候被调用?

答:当系统中有同名的平台设备和平台驱动时,就会调用probe函数。

问:probe函数有什么作用?

答:该函数可以做什么由你决定,你可以只打印一条语句,也可以做很复杂的事情。例如,led_probe函数就做了获取资源,映射IO,注册字符设备。

led_drv.c源码参考:

#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/sched.h>#include <linux/pm.h>#include <linux/sysctl.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/input.h>#include <linux/gpio_keys.h>#include <asm/uaccess.h>   // copy_from_user#include <asm/io.h>  // ioremapstatic struct class *led_cls;static volatile unsigned long *gpio_con;static volatile unsigned long *gpio_dat;static int pin;static int major;static int led_open(struct inode * inode, struct file * filp){*gpio_con &= ~(0x3<<(pin*2));*gpio_con |= (0x1<<(pin*2));return 0;}static ssize_tled_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){int val;copy_from_user(&val, buf, count);if(val == 1){/* 点灯 */*gpio_dat  &= ~(1<<pin); }else{/* 灭灯 */*gpio_dat  |= (1<<pin); }return 0;}/* File operations struct for character device */static const struct file_operations led_fops = {.owner= THIS_MODULE,.open= led_open,.write      = led_write,};static int __devinit led_probe(struct platform_device *pdev){struct resource *res;printk("led_probe, found led\n");/* 根据platform_device的资源进行ioremap */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);gpio_con =  ioremap(res->start, res->end - res->start + 1);gpio_dat = gpio_con + 1;res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);pin =  res->start;/* 注册字符设备 */major = register_chrdev(0, "myled", &led_fops);led_cls = class_create(THIS_MODULE,"myled");device_create(led_cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */return 0;}static int __devexit led_remove(struct platform_device *pdev){printk("led_remove, remove led\n");device_destroy(led_cls, MKDEV(major, 0));class_destroy(led_cls);unregister_chrdev(major, "myled");iounmap(gpio_con);return 0;}static struct platform_driver led_driver = {.probe= led_probe,.remove= led_remove,.driver= {.name= "myled",.owner= THIS_MODULE,}};/* 分配/设置/注册一个platform_driver */static int led_drv_init(void){return platform_driver_register(&led_driver);}static void led_drv_exit(void){platform_driver_unregister(&led_driver);}module_init(led_drv_init);module_exit(led_drv_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("LWJ");MODULE_DESCRIPTION("Just for Demo");


问:编写平台设备驱动的核心内容有哪些?

答:分配、设置、注册一个platform_device 

问:如何注册平台设备?

答:使用platform_device_register(struct platform_device *pdev)函数,该函数的参数为platform_device

问:如何定义platform_device?

答:简单示例:led_device

static struct platform_device led_device = {    .id= -1,    .name= "myled",  /* 与led_driver的name一致 */    .resource= led_resources,    .num_resources= ARRAY_SIZE(led_resources),    .dev            ={    .release    = led_release,    },};
问:如何定义resource?

答:简单示例:

static struct resource led_resources[] = {[0] = {.start= 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */.end= 0x56000010 + 8 -1,.flags= IORESOURCE_MEM,},[1] = {.start= 5,/* LED1 */.end= 5,.flags= IORESOURCE_IRQ,},};
led_dev.c源码参考:

#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/interrupt.h>#include <linux/list.h>#include <linux/timer.h>#include <linux/init.h>#include <linux/serial_core.h>#include <linux/platform_device.h>static struct resource led_resources[] = {[0] = {.start= 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */.end= 0x56000010 + 8 -1,.flags= IORESOURCE_MEM,},[1] = {.start= 5,/* LED1 */.end= 5,.flags= IORESOURCE_IRQ,},};static void led_release(struct device * dev){}static struct platform_device led_device = {    .id= -1,    .name= "myled",  /* 与led_driver的name一致 */    .resource= led_resources,    .num_resources= ARRAY_SIZE(led_resources),    .dev            ={    .release    = led_release,    },};/* 分配/设置/注册一个platform_device */static int led_dev_init(void){return platform_device_register(&led_device);}static void led_dev_exit(void){platform_device_unregister(&led_device);}module_init(led_dev_init);module_exit(led_dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("LWJ");MODULE_DESCRIPTION("Just for Demo");
应用测试程序源码:

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>/* 9th_led_test on * 9th_led_test off */int main(int argc, char **argv){int fd;int val = 1;fd = open("/dev/led", O_RDWR);if (fd < 0){printf("can't open!\n");}if (argc != 2){printf("Usage :\n");printf("%s <on|off>\n", argv[0]);return 0;}if (strcmp(argv[1], "on") == 0){val  = 1;}else{val = 0;}write(fd, &val, 4);return 0;}
测试步骤:

9th_led_test        first_drv.ko        sddiskQt                  first_test          second_drv.koTQLedtest           fourth_drv.ko       second_testapp_test            fourth_test         sixth_drv.kobin                 home                sixth_testbusybox             led_dev.ko          sixthdrvtestbuttons_all_drv.ko  led_drv.ko          sysbuttons_all_test    lib                 third_drv.kobuttons_input.ko    linuxrc             third_testdev                 mnt                 tmpdriver_test         opt                 udisketc                 proc                usrfifth_drv.ko        root                varfifth_test          sbin                web[WJ2440]# insmod led_drv.ko [WJ2440]# insmod led_dev.ko led_probe, found led[WJ2440]# rmmod led_devled_remove, remove ledrmmod: module 'led_dev' not found[WJ2440]# lsmodled_drv 2800 0 - Live 0xbf003000[WJ2440]# insmod led_dev.ko led_probe, found led[WJ2440]# lsmodled_dev 1444 0 - Live 0xbf009000led_drv 2800 0 - Live 0xbf003000[WJ2440]# ls /dev/led -l crw-rw----    1 root     root      252,   0 Jan  2 07:44 /dev/led[WJ2440]# ./9th_led_test Usage :./9th_led_test <on|off>[WJ2440]# ./9th_led_test off[WJ2440]# ./9th_led_test on 
当执行./9th_led_test off时,led1被熄灭;当执行./9th_led_test on时 led1被点亮。如果你需要点亮led2,那么只需要修改led_dev的led_resources改为:
static struct resource led_resources[] = {[0] = {.start= 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */.end= 0x56000010 + 8 -1,.flags= IORESOURCE_MEM,},[1] = {.start= 6,/* LED2 */.end= 6,.flags= IORESOURCE_IRQ,},};
这样,应用程序不用更改,即可点亮led2,这样一来就实现了,稳定部分不用修改,只需要修改硬件易变部分,并且应用程序不需要任何更改。

5 0
原创粉丝点击