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) 随着计算机的周边外设越来越丰富,设备管 ...
随机推荐
- Python中的网络扫描大杀器Scapy初探
Python中的网络扫描大杀器Scapy初探 最近经历了Twisted的打击,这个网络编程实在看不懂,都摸不透它的内在逻辑,看来网络编程不是那么好弄的.还好,看到了scapy,这种网络的大杀器 ...
- 错误消息对话框QErrorMessage
继承于 QDialog 样式: 这个复选框的作用:文本框中相同信息时是否再显示 import sys from PyQt5.QtWidgets import QApplication, QWi ...
- git clone项目失败,Host key verification failed.
在码云上创建了一个项目,配置好公钥后,克隆到我本地出现以下失败 百度了好久也没有找到解决办法,困扰了好久,后来还是百度到了, 原来是在提示 ey fingerprint is SHA256:FQGC9 ...
- SQL server 统计分组经计
SUM(A.AREA) OVER ( PARTITION BY A.ItemNo, A.PARTS ,A.WIDTH,A.HEIGHT) allotQty, SUM(A.SL) OVER ( PART ...
- node的cropto加密
本文转自https://blog.csdn.net/sinat_35670989/article/details/78224214 'use strict' //crypto(kri:pto)意为加密 ...
- 安卓手机和ios手机上图片未设置宽度可能导致ios上图片贼小
处理方法: 设置固定宽度,高度自适应
- 2.Vue子组件给父组件通信
子组件给父组件通信 如果子组件想要改变数据呢?这在vue中是不允许的,因为vue只允许单向数据传递,这时候我们可以通过触发事件来通知父组件改变数据,从而达到改变子组件数据的目的 子组件: <te ...
- 麦子lavarel---10、一些第三方应用注意
麦子lavarel---10.一些第三方应用注意 一.总结 一句话总结: 其实把重要的几个功能弄一个就好了,邮箱验证,手机号验证,支付验证,都是调用第三方接口,也很简单 1.关于页面和服务端校验的看法 ...
- Wowza 4.5 修改 manager 端口号
//编辑下面的文件, 搜索8088 有两处,改为想要的端口号即可 vim /usr/local/WowzaStreamingEngine/manager/bin/startmgr.sh // 重启服务 ...
- (转)datagridview 自定义列三步走
本文转载自:http://blog.csdn.net/zx13525079024/article/details/4814642 我们如果想自定义实现datagridview的某列,例如是datagr ...