这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备。
问:如何编写平台总线驱动设备这一套架构的设备驱动?
答:分为两个.c文件,一个是drv.c,另一个是dev.c;前者实现平台驱动,后者实现平台设备,平台总线不用我们自己实现。
问:编写平台驱动的核心内容有哪些?
答:分配、设置、注册一个platform_driver
问:如何注册平台驱动?
答:使用platform_driver_register(struct platform_driver *drv)函数,该函数的参数为platform_driver
问:如何定义platform_driver?
答:简单示例
plain?
- 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源码参考:
plain?
- #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> // ioremap
- static 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_t
- led_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
plain?
- 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?
答:简单示例:
plain?
- 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源码参考:
plain?
- #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] = {
//寄存器的哪一个引脚,以后可以修改这儿来操作让哪个led点亮 - .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");
应用测试程序源码:
plain?
- #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;
- }
测试步骤:
plain?
- 9th_led_test first_drv.ko sddisk
- Qt first_test second_drv.ko
- TQLedtest fourth_drv.ko second_test
- app_test fourth_test sixth_drv.ko
- bin home sixth_test
- busybox led_dev.ko sixthdrvtest
- buttons_all_drv.ko led_drv.ko sys
- buttons_all_test lib third_drv.ko
- buttons_input.ko linuxrc third_test
- dev mnt tmp
- driver_test opt udisk
- etc proc usr
- fifth_drv.ko root var
- fifth_test sbin web
- [WJ2440]# insmod led_drv.ko
- [WJ2440]# insmod led_dev.ko
- led_probe, found led
- [WJ2440]# rmmod led_dev
- led_remove, remove led
- rmmod: module 'led_dev' not found
- [WJ2440]# lsmod
- led_drv 2800 0 - Live 0xbf003000
- [WJ2440]# insmod led_dev.ko
- led_probe, found led
- [WJ2440]# lsmod
- led_dev 1444 0 - Live 0xbf009000
- led_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改为:
plain?
- 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,这样一来就实现了,稳定部分不用修改,只需要修改硬件易变部分,并且应用程序不需要任何更改。