linux驱动模型——platform(2)
一. platform 组织架构
1.1. platform工作体系都定义在drivers/base/platform.c中
1.2. platform相关函数声明在include/linux/platform_device.h
1.3. platform.c中两个重要结构体
1.3.1. platform_device结构体
a. 该结构体的name用于总线与device匹配
struct platform_device { const char * name; // 平台总线下设备的名字 int id; //当多个同类型设备时用以区分,比如s5pv210_led.0-->这里是name,0是id struct device dev; // 所有设备通用的属性部分 u32 num_resources; // 设备使用到的resource的个数 struct resource * resource; // 设备使用到的资源数组的首地址 const struct platform_device_id *id_entry; /* arch specific additions */ struct pdev_archdata archdata; //用来提供扩展性的 };
1.3.2. platform_driver结构体。
struct platform_driver { int (*probe)(struct platform_device *); // 驱动探测函数 int (*remove)(struct platform_device *); // 去掉一个设备 void (*shutdown)(struct platform_device *); // 关闭一个设备 int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; // 所有设备共用的一些属性 const struct platform_device_id *id_table; // 设备ID表 };
1.4. 平台总线体系的工作流程
1.4.1. 系统启动时在bus系统中注册platform
1.4.2. 内核移植的人负责提供platform_device
1.4.3. 写驱动的人负责提供platform_driver
1.4.4. platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了
1.5. platform.c中四个函数
1.5.1. platform_device_register
a. 向总线中注册device
/** * platform_device_register - add a platform-level device * @pdev: platform device we're adding */ int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); }
1.5.2. platform_device_unregister
a. 向总线中注销device
/** * platform_device_unregister - unregister a platform-level device * @pdev: platform device we're unregistering * * Unregistration is done in 2 steps. First we release all resources * and remove it from the subsystem, then we drop reference count by * calling platform_device_put(). */ void platform_device_unregister(struct platform_device *pdev) { platform_device_del(pdev); platform_device_put(pdev); }
1.5.3. platform_driver_register
a. 向总线中注册driver
/** * platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure */ int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); }
1.5.4. platform_driver_unregister
a. 向总线中注销driver
/** * platform_driver_unregister - unregister a driver for platform-level devices * @drv: platform driver structure */ void platform_driver_unregister(struct platform_driver *drv) { driver_unregister(&drv->driver); }
二.led device 代码介绍
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module_exit #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <mach/gpio.h> #include <linux/leds.h> #include <asm/string.h> #include <linux/platform_device.h> #include <mach/leds-gpio.h> void s5pv210_led_release(struct device *dev); static struct s5pv210_led_platdata s5pv210_led_pdata[] = { { .name = "led0", .gpio = S5PV210_GPJ0(3), .def_trigger = "", }, { .name = "led1", .gpio = S5PV210_GPJ0(4), .def_trigger = "", }, { .name = "led2", .gpio = S5PV210_GPJ0(5), .def_trigger = "", } }; static struct platform_device s5pv210_led[] = { { .name = "s5pv210_led", .id = 0, .dev = { .platform_data = &s5pv210_led_pdata[0], .release = s5pv210_led_release, } }, { .name = "s5pv210_led", .id = 1, .dev = { .platform_data = &s5pv210_led_pdata[1], .release = s5pv210_led_release, } }, { .name = "s5pv210_led", .id = 2, .dev = { .platform_data = &s5pv210_led_pdata[2], .release = s5pv210_led_release, } } }; void s5pv210_led_release(struct device *dev) { printk(KERN_WARNING "s5pv210_led_release successful \n"); } static struct platform_device *ps5pv210_led[] = { &s5pv210_led[0], &s5pv210_led[1], &s5pv210_led[2] }; static int __init s5pv210_led_init(void) { int ret = -1; printk(KERN_INFO "device.c : s5pv210_led_init successful \n"); ret = platform_add_devices(ps5pv210_led, ARRAY_SIZE(ps5pv210_led)); if (ret < 0) { printk(KERN_WARNING "platform_add_devices fail \n"); } return ret; } static void __exit s5pv210_led_exit(void) { int i = 0; printk(KERN_INFO "device.c : s5pv210_led_exit successful \n"); for (i = 0; i < ARRAY_SIZE(ps5pv210_led); i++) platform_device_unregister(ps5pv210_led[i]); } module_init(s5pv210_led_init); module_exit(s5pv210_led_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE("GPL"); // 描述模块的许可证 MODULE_AUTHOR("musk"); // 描述模块的作者 MODULE_DESCRIPTION("x210 LED device"); // 描述模块的介绍信息 MODULE_ALIAS("led_device"); // 描述模块的别名信息
2.1. led devicec程序两种组织方式
2.1.1. 把device相关必要的code 放在 mach-x210.c中
a. mach-x210.c放着很多个device设备。是大部分设备共有的文件,一般驱动开发时也是将所要开发驱动的device部分放在此文件
b.mach-x210.c中所有platform设备的定义的数据都会放在smdkc110_devices结构体中,该结构体在smdkc110_machine_init函数完成设备的register
c.mach-x210.c中device随着开发板启动完成注册,故不能被注销,因此不需要relase函数
2.1.2. 自己写个led_device.c
a. led_device.c 把led当模块处理,可以insmod 也可以rmmod,其中rmmod需要调用relase函数
b. led_device.c 中led device需要自己添加到总线上(platform_device_register)和从总线注销(platform_device_unregister)
c. 正式驱动开发不这样。我这样写为了便于理解
2.2. platform_add_devices函数
2.2.1. 这个函数是同时注册多个device
2.2.2. 这个函数struct platform_device **devs是个二位指针。我写程序时在这里栽了跟头
/** * platform_add_devices - add a numbers of platform devices * @devs: array of platform devices to add * @num: number of platform devices in array */ int platform_add_devices(struct platform_device **devs, int num) { int i, ret = 0; for (i = 0; i < num; i++) { ret = platform_device_register(devs[i]); if (ret) { while (--i >= 0) platform_device_unregister(devs[i]); break; } } return ret; }
三. led driver介绍
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module_exit #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <asm/io.h> //writel #include <mach/gpio.h> #include <linux/leds.h> #include <asm/string.h> #include <linux/platform_device.h> #include <mach/leds-gpio.h> #include <linux/slab.h> #define X210_LED_OFF 1U #define X210_LED_ON 0U static int s5pv210_led_probe(struct platform_device *dev); void s5pv210_led_set(struct led_classdev *led_cdev,enum led_brightness brightness); static int s5pv210_led_remove(struct platform_device *dev); static struct platform_driver s5pv210_led_driver = { .probe = s5pv210_led_probe, .remove = s5pv210_led_remove, .driver = { .name = "s5pv210_led", .owner = THIS_MODULE, }, }; struct s5pv210_gpio_led { struct led_classdev cdev; struct s5pv210_led_platdata *pdata; }; // static struct gpio x210_led_gpio[] = // { // { S5PV210_GPJ0(3), GPIOF_OUT_INIT_HIGH, "LED1" }, /* default to OFF */ // { S5PV210_GPJ0(4), GPIOF_OUT_INIT_HIGH, "LED2" }, /* default to OFF */ // { S5PV210_GPJ0(5), GPIOF_OUT_INIT_HIGH, "LED3" } /* default to OFF */ // }; static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev) { return platform_get_drvdata(dev); } static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev) { return container_of(led_cdev, struct s5pv210_gpio_led, cdev); } void s5pv210_led_set(struct led_classdev *led_cdev,enum led_brightness brightness) { struct s5pv210_gpio_led *led = to_gpio(led_cdev); struct s5pv210_led_platdata *pd = led->pdata; printk(KERN_INFO "s5pv210_led1_set successful %d\n",brightness); gpio_set_value(pd->gpio,(brightness == LED_OFF) ? X210_LED_OFF:X210_LED_ON); } static int s5pv210_led_remove(struct platform_device *dev) { struct s5pv210_gpio_led *led = pdev_to_gpio(dev); struct s5pv210_led_platdata *platdata = dev->dev.platform_data; // 取出platform_device结构体中的device结构体中的私有数据区的x210_platform指针 led_classdev_unregister(&led->cdev); // 卸载led驱动 gpio_set_value(platdata->gpio,X210_LED_OFF); // 关闭所有led gpio_free(platdata->gpio); // 释放申请的GPIO资源 kfree(led); // 释放内存,这个一定要放在最后 return 0; } static int s5pv210_led_probe(struct platform_device *dev) { int ret = -1; struct s5pv210_led_platdata *pdata = dev->dev.platform_data; struct s5pv210_gpio_led *led = NULL; printk(KERN_INFO "driver.c: s5pv210_led_probe successful \n"); led = kzalloc(sizeof(struct s5pv210_gpio_led), GFP_KERNEL); if (led == NULL) { dev_err(&dev->dev, "No memory for device\n"); return -ENOMEM; } platform_set_drvdata(dev, led); // 将pform值写入到传进来的platform_device结构体中的device结构体下的私有数据区中去,以便后面释放内存时用 led->cdev.brightness_set = s5pv210_led_set; led->cdev.name = pdata->name; led->pdata = pdata; ret = led_classdev_register(NULL, &led->cdev); //真正注册led驱动 if (ret < 0) { printk(KERN_WARNING "led_classdev_register fail \n"); goto class_err; } ret = gpio_request(pdata->gpio, pdata->name); // 向gpiolib管理器申请gpio资源 if (ret) { printk(KERN_WARNING "gpio_request_array fail \n"); goto req_err; } gpio_direction_output(pdata->gpio, X210_LED_OFF); return 0; req_err: platform_driver_unregister(&s5pv210_led_driver); class_err: kfree(led); return ret; } static int __init s5pv210_led_init(void) { printk(KERN_INFO "driver.c: s5pv210_led_init successful \n"); return platform_driver_register(&s5pv210_led_driver); } static void __exit s5pv210_led_exit(void) { printk(KERN_INFO "driver.c : s5pv210_led_exit successful \n"); platform_driver_unregister(&s5pv210_led_driver); } module_init(s5pv210_led_init); module_exit(s5pv210_led_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE("GPL"); // 描述模块的许可证 MODULE_AUTHOR("musk"); // 描述模块的作者 MODULE_DESCRIPTION("x210 LED driver"); // 描述模块的介绍信息 MODULE_ALIAS("led_driver"); // 描述模块的别名信息
3.1. 充分linux 数据和处理分离的思想
3.1.1. driver只负责数据的处理和加工,没有规定或写死任何数据
3.1.2. device提供driver所需要的数据
3.2. s5pv210_led_probe函数
3.2.1. 当我们注册driver时会去总线匹配device 如果匹配成功则会s5pv210_led_probe
3.2.2. 当我们注册device时也会去总线匹配driver如果匹配成功则会s5pv210_led_probe
四.实操。
[root@musk210 driver_test]# ls app_led_driver app_led_driver.o led_driver.ko app_led_driver.c led_device.ko [root@musk210 driver_test]# insmod led_device.ko [ 3362.294966] device.c : s5pv210_led_init successful [root@musk210 driver_test]# insmod led_driver.ko [ 3372.295723] driver.c: s5pv210_led_init successful [ 3372.300022] driver.c: s5pv210_led_probe successful [ 3372.305379] driver.c: s5pv210_led_probe successful [ 3372.310753] driver.c: s5pv210_led_probe successful [root@musk210 driver_test]# ls /sys/bus/platform/devices/ alarm s3c-adc s3c-sdhci.3 s5p-ehci android_pmem.1 s3c-csis s3c-ts s5p-hpd arm-pmu.0 s3c-fimc.0 s3c2410-wdt s5p-tvout dm9000.0 s3c-fimc.1 s3c2440-i2c.0 s5pv210-nand pm-wifi s3c-fimc.2 s3c2440-i2c.1 s5pv210-uart.0 power.0 s3c-g2d s3c2440-i2c.2 s5pv210-uart.1 pvrsrvkm s3c-jpg s3c24xx-pwm.0 s5pv210-uart.2 pwm-backlight s3c-keypad s3c24xx-pwm.1 s5pv210-uart.3 reg-s5pv210-pd.0 s3c-mfc s3c24xx-pwm.2 s5pv210_led.0 reg-s5pv210-pd.1 s3c-pl330.0 s3c24xx-pwm.3 s5pv210_led.1 reg-s5pv210-pd.2 s3c-pl330.1 s3c64xx-iis.0 s5pv210_led.2 reg-s5pv210-pd.3 s3c-pl330.2 s3c64xx-iis.1 sec-fake-battery reg-s5pv210-pd.4 s3c-sdhci.0 s3c_lcd smdkc110-rtc reg-s5pv210-pd.5 s3c-sdhci.1 s3cfb soc-audio.1 regulatory.0 s3c-sdhci.2 s5p-cec switch-gpio [root@musk210 driver_test]# ls /sys/bus/platform/driver/ ls: /sys/bus/platform/driver/: No such file or directory [root@musk210 driver_test]# ls /sys/bus/platform/drivers alarm ram_console s3c-sdhci s5pv210-uart android_pmem reg-s5pv210-pd s3c-ts s5pv210_led arm-pmu s3c-adc s3c24xx-pwm sec-fake-battery dm9000 s3c-csis s3c64xx-iis smdkc110-rtc i2c-gpio s3c-fimc s3cfb soc-audio max8698-pmic s3c-g2d s5p-cec switch-gpio pm-wifi s3c-i2c s5p-ehci timed-gpio power s3c-jpg s5p-hpd pvrsrvkm s3c-mfc s5p-tvout pwm-backlight s3c-pl330 s5pv210-nand [root@musk210 driver_test]# cd /sys/class/leds/ [root@musk210 leds]# ls led0 led1 led2 mmc0:: mmc1:: mmc2:: mmc3:: [root@musk210 leds]# cd led0 [root@musk210 led0]# ls brightness max_brightness power subsystem uevent [root@musk210 led0]# echo 1 > brightness [ 3464.992199] s5pv210_led1_set successful 1 [root@musk210 led0]# echo 0 > brightness [ 3472.096432] s5pv210_led1_set successful 0 [root@musk210 led0]# cd ../led1 [root@musk210 led1]# echo 1 > brightness [ 3484.145032] s5pv210_led1_set successful 1 [root@musk210 led1]# cd - /sys/class/leds/led0 [root@musk210 led0]# cd /driver_test/ [root@musk210 driver_test]# rmmod led_device.ko [ 3502.670697] device.c : s5pv210_led_exit successful [ 3502.676979] s5pv210_led_release successful [ 3502.683547] s5pv210_led_release successful [ 3502.689179] s5pv210_led_release successful [root@musk210 driver_test]# rmmod led_driver.ko [ 3509.670803] driver.c : s5pv210_led_exit successful