1.以leds-s3c24xx.c为例来分析platform设备和驱动的注册过程
1)platform_driver_register
2)platform_device_register
2.platdata怎么玩
1)platdata其实就是设备注册时提供的设备有关的一些数据(譬如设备对应的gpio、使用到的中断号、设备名称••••)
2)这些数据在设备和驱动match之后,会由设备方转给驱动方。驱动拿到这些数据后,通过这些数据得知设备的具体信息,然后来操作设备。
3)这样做的好处是:驱动源码中不携带数据,只负责算法(对硬件的操作方法)。现代驱动设计理念就是算法和数据分离,这样最大程度保持驱动的独立性和适应性。
3.match函数的调用轨迹
4.probe函数的功能和意义
5.先初步改造添加platform_driver
1)第1步:先修改原来的代码到只有led1
2)第2步:添加probe和remove
6.检查mach-x210.c中是否有led相关的platform_device
7.参考mach-mini2440.c中添加led的platform_device定义
8.测试只有platform_device没有platform_driver时是怎样的
9.测试platform_device和platform_driver相遇时会怎样
10.probe函数
1)probe函数应该做什么
2)probe函数的数据从哪里来
3)编程实践
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <linux/platform_device.h>
#include <mach/leds-gpio.h>
#include <linux/slab.h>
#define X210_LED_OFF 1 // X210中LED是正极接电源,负极节GPIO
#define X210_LED_ON 0 // 所以1是灭,0是亮
struct s5pv210_gpio_led {
struct led_classdev cdev;
struct s5pv210_led_platdata *pdata;
};
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);
}
// 这个函数就是要去完成具体的硬件读写任务的
static void s5pv210_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct s5pv210_gpio_led *p = to_gpio(led_cdev);
printk(KERN_INFO "s5pv210_led_set\n");
// 在这里根据用户设置的值来操作硬件
// 用户设置的值就是value
if (value == LED_OFF)
{
// 用户给了个0,希望LED灭
gpio_set_value(p->pdata->gpio, X210_LED_OFF);
}
else
{
// 用户给的是非0,希望LED亮
gpio_set_value(p->pdata->gpio, X210_LED_ON);
}
}
static int s5pv210_led_probe(struct platform_device *dev)
{
// 用户insmod安装驱动模块时会调用该函数
// 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备
int ret = -1;
struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
struct s5pv210_gpio_led *led;
printk(KERN_INFO "----s5pv210_led_probe---\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);
// 在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源
if (gpio_request(pdata->gpio, pdata->name))
{
printk(KERN_ERR "gpio_request failed\n");
}
else
{
// 设置为输出模式,并且默认输出1让LED灯灭
gpio_direction_output(pdata->gpio, 1);
}
// led1
led->cdev.name = pdata->name;
led->cdev.brightness = 0;
led->cdev.brightness_set = s5pv210_led_set;
led->pdata = pdata;
ret = led_classdev_register(&dev->dev, &led->cdev);
if (ret < 0) {
printk(KERN_ERR "led_classdev_register failed\n");
return ret;
}
return 0;
}
static int s5pv210_led_remove(struct platform_device *dev)
{
struct s5pv210_gpio_led *p = pdev_to_gpio(dev);
led_classdev_unregister(&p->cdev);
gpio_free(p->pdata->gpio);
kfree(p); // kfee放在最后一步
return 0;
}
static struct platform_driver s5pv210_led_driver = {
.probe = s5pv210_led_probe,
.remove = s5pv210_led_remove,
.driver = {
.name = "s5pv210_led",
.owner = THIS_MODULE,
},
};
static int __init s5pv210_led_init(void)
{
return platform_driver_register(&s5pv210_led_driver);
}
static void __exit s5pv210_led_exit(void)
{
platform_driver_unregister(&s5pv210_led_driver);
}
module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston <[email protected]>"); // 描述模块的作者
MODULE_DESCRIPTION("s5pv210 led driver"); // 描述模块的介绍信息
MODULE_ALIAS("s5pv210_led"); // 描述模块的别名信息