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(),
.def_trigger = "",
},
{
.name = "led1",
.gpio = S5PV210_GPJ0(),
.def_trigger = "",
},
{
.name = "led2",
.gpio = S5PV210_GPJ0(),
.def_trigger = "",
}
};
static struct platform_device s5pv210_led[] = {
{
.name = "s5pv210_led",
.id = ,
.dev = {
.platform_data = &s5pv210_led_pdata[],
.release = s5pv210_led_release,
}
},
{
.name = "s5pv210_led",
.id = ,
.dev = {
.platform_data = &s5pv210_led_pdata[],
.release = s5pv210_led_release,
}
},
{
.name = "s5pv210_led",
.id = ,
.dev = {
.platform_data = &s5pv210_led_pdata[],
.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[],
&s5pv210_led[],
&s5pv210_led[]
}; static int __init s5pv210_led_init(void)
{
int ret = -;
printk(KERN_INFO "device.c : s5pv210_led_init successful \n");
ret = platform_add_devices(ps5pv210_led, ARRAY_SIZE(ps5pv210_led));
if (ret < )
{
printk(KERN_WARNING "platform_add_devices fail \n");
}
return ret;
} static void __exit s5pv210_led_exit(void)
{
int i = ;
printk(KERN_INFO "device.c : s5pv210_led_exit successful \n"); for (i = ; 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 = ; for (i = ; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= )
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 ;
} static int s5pv210_led_probe(struct platform_device *dev)
{
int ret = -; 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 < )
{ 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 ; 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. s5p-ehci
android_pmem. s3c-csis s3c-ts s5p-hpd
arm-pmu. s3c-fimc. s3c2410-wdt s5p-tvout
dm9000. s3c-fimc. s3c2440-i2c. s5pv210-nand
pm-wifi s3c-fimc. s3c2440-i2c. s5pv210-uart.
power. s3c-g2d s3c2440-i2c. s5pv210-uart.
pvrsrvkm s3c-jpg s3c24xx-pwm. s5pv210-uart.
pwm-backlight s3c-keypad s3c24xx-pwm. s5pv210-uart.
reg-s5pv210-pd. s3c-mfc s3c24xx-pwm. s5pv210_led.
reg-s5pv210-pd. s3c-pl330. s3c24xx-pwm. s5pv210_led.
reg-s5pv210-pd. s3c-pl330. s3c64xx-iis. s5pv210_led.
reg-s5pv210-pd. s3c-pl330. s3c64xx-iis. sec-fake-battery
reg-s5pv210-pd. s3c-sdhci. s3c_lcd smdkc110-rtc
reg-s5pv210-pd. s3c-sdhci. s3cfb soc-audio.
regulatory. s3c-sdhci. 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 > brightness
[ 3464.992199] s5pv210_led1_set successful
[root@musk210 led0]# echo > brightness
[ 3472.096432] s5pv210_led1_set successful
[root@musk210 led0]# cd ../led1
[root@musk210 led1]# echo > brightness
[ 3484.145032] s5pv210_led1_set successful
[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
linux驱动模型——platform(2)的更多相关文章
- linux驱动模型——platform(1)
一.驱动模型包含什么? 1.1. 类class 1.1.2. 它能够自动创建/dev下的设备节点,不需要mknod /dev/xxx c x x创建.当然class还有其另外的作用,且自动创建设备节点 ...
- Linux驱动模型解析bus之platform bus
这是内核启动之后要调用的驱动模型的开始代码: drivers/base/init.c/** * driver_init - initialize driver model. * * Call the ...
- 从串口驱动的移植看linux2.6内核中的驱动模型 platform device & platform driver【转】
转自:http://blog.csdn.net/bonnshore/article/details/7979705 写在前面的话: 博主新开了个人站点:你也可以在这里看到这篇文章,点击打开链接 本文是 ...
- Linux 驱动框架---platform驱动框架
Linux系统的驱动框架主要就是三个主要部分组成,驱动.总线.设备.现在常见的嵌入式SOC已经不是单纯的CPU的概念了,它们都会在片上集成很多外设电路,这些外设都挂接在SOC内部的总线上,不同与IIC ...
- linux驱动模型<输入子系统>
在linux中提供一种输入子系统的驱动模型,其主要是实现在input.c中. 在输入子系统这套模型中,他把驱动分层分类.首先分为上下两层,上层为input.c .下层为驱动的实现,下层分为两部分,一部 ...
- 总线设备驱动模型---platform篇
总线设备驱动模型----驱动篇 http://blog.chinaunix.net/uid-27664726-id-3334923.html http://blog.chinaunix.net/uid ...
- Linux设备驱动模型之platform(平台)总线详解
/********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...
- linux内核驱动模型
linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构 ...
- linux设备模型_转
建议原博文查看,效果更佳. 转自:http://www.cnblogs.com/wwang/category/269350.html Linux设备模型 (1) 随着计算机的周边外设越来越丰富,设备管 ...
随机推荐
- BOM-window
窗口位置 screenLeft和screenTop screenLeft 和 screenTop 属性,分别用于表示窗口相对于屏幕左边和上边的位置.Firefox 则在screenX 和 screen ...
- css将两个元素水平对齐,兼容IE8
css实现元素水平对齐 css实现水平对齐,如图 有人会说css实现这种水平对齐要兼容ie8还不简单吗?使用float: left,或者display: inline-block,不就可以了吗?是的, ...
- springboot easyexcel
pom..xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel&l ...
- oracle删除数据文件
在我们详细介绍之前,我们必须说清楚一点:Oracle不提供如删除表.视图一样删除数据文件的方法,数据文件是表空间的一部分,所以不能“移走”表空间. 一.使用offline数据文件的方法 非归档模式使用 ...
- SpringToolSuit(STS)添加了Lombok后仍然报错
参见这篇博客 https://blog.csdn.net/qq_27442469/article/details/90612918 上面步骤做完后,在项目右键->Maven->Update ...
- 【LOJ#6036】[雅礼集训2017Day4]编码
传送门 题意简述 判定 n 个含 ? 字符的二进制串是否存在一种把 0/1 填入 ? 中的方案使得任意两个串不具有前缀关系. (一个串最多一个 ?) Sol 二进制串 ,并且一个串最多一个 '?' 很 ...
- 大数加法(A + B Problem Plus)问题
解题思路 两个⼤数可以⽤数组来逐位保存,然后在数组中逐位进⾏相加,再判断该位相加后是否需要进位.为了⽅便计算,可以把数字的低位放到数组的前面,高位放在后面. 样例输入 3 18 22 56 744 5 ...
- CSS3画五角星和六角星
最终想要实现的效果 一.五角星 在画五角星之前首先分析这个五角星是如何实现,由哪几个部分构成的,示意图如下: 三个顶角向上的三角形,通过设置旋转和定位相互重叠和拼接实现最终的五角星效果. 为了语义化和 ...
- HTML和CSS实现图片翻转效果
实现图片翻转,首先来分析一下我们希望实现的是怎样的翻转效果?又该如何去实现呢? 一.希望实现的效果 页面上的图片在光标悬停在上面的时候会发生翻转效果,翻转过后显示出背面的说明文字. 鼠标没有悬停在上面 ...
- adb打开系统设置的命令
adb命令打开手机设置页面 设置主页面adb shell am start com.android.settings/com.android.settings.Settings 安全adb shell ...