分类: 嵌入式
2016-03-20 17:51:16
原文地址:《linux设备驱动开发详解》读书笔记七 作者:jerry20000
1.1 platform总线、设备与驱动
在Linux 2.6的设备驱动模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2 C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等却不依附于此类总线。基于这一背景,Linux发明了一种虚拟的称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct pm_ext_ops *pm;
struct device_driver driver;
};
platform_device和platform_driver如何进行匹配?通过platform_match来比较name字段是否相同!
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
……
};
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev;
pdev = container_of(dev, struct platform_device, dev);
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
1.2 实现platform设备的步骤
(1).platform_device的编写
(2).platform_driver的编写
(3).设备资源和数据的定义
设备资源由resource结构体描述:
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
我们通常关心start、end和flags这3个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。start、end的含义会随着flags而变更,如当 flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;当 flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。对于同种类型的资源而言,可以有多份,譬如说某设备占据了2个内存区域,则可以定义2个IORESOURCE_MEM资源。
对resource的定义通常在BSP的板文件中进行,而在具体的设备驱动中透过platform_get_resource()这样的API来获取,此API的原型为:
struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
设备除了可以在BSP中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道以外,可能还会有一些配置信息,而这些配置信息也依赖于板,不适宜直接放置在设备驱动本身,因此,platform也提供了platform_data的支持。platform_data 的形式是自定义的。
2 input输入子系统框架
下图是input输入子系统框架,输入子系统由输入子系统核心层( Input Core ),驱动层和事件处理层(Event Handler)三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。
注意:keyboard.c不会在/dev/input下产生节点,而是作为ttyn终端(不包括串口终端)的输入。
3. SPI通信
该总线通信基于主-从配置。它有以下4个信号:
MOSI:主出/从入
MISO:主入/从出
SCK:串行时钟
SS:从属选择
SPI传输串行数据时首先传输最高位。波特率可以高达5Mbps,具体速度大小取决于SPI硬件。例如,Xicor公司的SPI串行器件传输速度能达到5MHz。
S3C2440的SPI驱动在这个文件里讲得很详细:Spi_s3c24xx.c (drivers\spi)
4. 设备驱动中的电源管理
这又是一个很大的话题,这里只说一点:
挂起:suspend();
恢复:resume();
并且这两个函数在platform_driver结构体中已经包含。
5. misc 设备驱动
设备结构体定义在include/linux/miscdevice.h中:
struct miscdevice {
int minor; //次设备号,若为 MISC_DYNAMIC_MINOR 自动分配
const char *name; //设备名
const struct file_operations *fops; //设备文件操作结构体
struct list_head list; //misc_list链表头
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
这个主设备号是10,所有注册为misc的设备都有相同的主设备号。在使用过程中我们主要是通过次设备号来区分各个设备。这一点不难理解,内核将所有注册为misc的设备都归为一大类。
结构体中的list_head结构体类型的list成员的作用是什么呢?
内核自己会维护一个misc_list链表,所有注册为misc的设备都必须挂在这个链表上,这个list就是该链表的链表头。
结构体中的两个device结构体类型指针作用是什么呢?
作用就是创建设备文件。
我们如何定义自己的misc类型的设备呢?
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
其中的设备文件操作结构体和字符设备类似,这里就不再细讲。
定义了自己的misc设备,那么我们如何向内核注册/注销设备呢?
使用如下两个函数:
int misc_register(struct miscdevice * misc); //在加载模块时会自动创建设备文件,是主设备号为10的字符设备
int misc_deregister(struct miscdevice *misc); //在卸载模块时会自动删除设备文件
6.Android和linux的区别是什么?
Android平台是基于Linxu内核搭建的,Linux内核的优势在于大内存管理、进程管理、基于权限的安全模型、统一的驱动模型、共享库支持、代码开源等。
Android平台在设计过程中,针对移动终端资源有限的特点,对Linux进行了一定程度的裁剪:砍掉了原生的窗口系统、去除了对GNU Libc的支持(引入了更高效、针对嵌入式优化过的Bionic)、裁剪掉了一些标准Linux工具的部分特性等。
另外Android针对移动终端的特点还对Linux内核在闹钟(Alarm)、Low Memory Killer、Ashmem、内核调试(Kernel Debugger)、进程间通信(Binder)、日志(Logger)、电源管理(Power Management)等方面做了大量的优化。
其中Low Memory Killer相对于Linux标准OOM(Out Of Memory)机制更加灵活,它可以根据需要杀死进程来释放需要的内存。Low Memory Killer的实现主要位于aurora\msm\msm drivers/staging/android/lowmemorykiller.c文件中。
Ashmem为进程间提供大块共享内存,同时为内核提供和管理这个内存的机制。 Ashmem的实现位于system\core\libcutils\ashmem-dev.c文件中。
参考文献:
http://blog.csdn.net/changjiang654/article/details/6154622
http://blog.csdn.net/chenlong12580/article/details/7339127
http://www.eefocus.com/ayayayaya/blog/2012-05/228130_1d55e.html