工程中的Linux设备驱动
一、platform 设备驱动
1.1 platform 总线、设备与驱动
在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
Linux发明了一种虚拟的总线,称为platform总线, 相应的设备成为platform_device, 而驱动成为platform_driver。
platform_device结构体
struct platform_device {
const char *name; //设备名
u32id;
struct device dev;
u32num_resource; //设备所使用的各类资源数量
struct resource * resource; //资源
};
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 (*suspend_late) (struct platform_device *, pm_message_t state);
int (*resume_early) (struct plartform_device *);
int (*resume) (struct platform_device *);
struct pm_ext_ops *pm;
struct device_driver driver;
};
系统中为platform总线定义了一个bus_type的实例platform_bus_type,其定义如代码清单:
struct bus_type platform_bus_type = {
.name= "platform",
.dev_attrs= platform_dev_attrs,
.match = platform_match,
.uevent= platform_uevent,
.pm= PLATFORM_PM_OPS_PTR,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
这里要重点关注其match() 成员函数, 正是此成员函数确定了platform_device 和 platform_driver之间如何匹配,代码如下:
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);
}; //匹配platform_device和platform_driver主要看两者的name字段是否相同。
对platform_device的定义通常在BSP的板文件中实现,在板文件中,将platform_device归纳为一个数组,最终通过platform_add_devices()函数统一注册。platform_add_devices()函数可以将平台设备添加到系统中,这个函数的原型为:
int platform_add_devices(struct platform_device **devs, int num); //该函数的第一个参数为平台设备数组的指针,第二个参数为平台设备的数量,它的内部调用了platform_device_register() 函数用于注册单个的平台设备。
1.2 输入设备驱动
输入核心提供了底层输入设备驱动程序所需的API,如分配/释放一个输入设备:
struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev * dev);
input_allocate_device()返回的是一个input_dev 的结构体,此结构体用于表征1个输入设备。
注册/注销输入设备用的接口如下:
int _ _must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);
报告输入事件用的接口如下:
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); //报告指定type、code的输入事件
void input_report_key(struct input_dev *dev, unsigned int code, int value); //报告键值
void input_report_rel(struct input_dev *dev, unsigned int code, int value); //报告相对坐标
void input_report_abs(struct input_dev *dev, unsigned int code, int value); //报告绝对坐标
void input_sync(struct input_dev *dev); //报告同步事件
而所有的输入事件,内核都用统一的数据结构来描述,这个数据结构是input_event, 形如代码如下:
struct input_event {
struct timeval time;
_ _u16 type;
_ _u16 code;
_ _s32 value;
};
二:输入设备驱动
输入核心提供了底层输入设备驱动程序所需的API,如分配、释放一个初入设备:
struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev);
注册/注销输入设备用的接口如下:
int _ _must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);
报告输入事件用的接口如下:
/*报告制定type、code的输入事件*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
/*报告键值*/
void input_report_key(struct input_dev *dev, unsigned int code, int value);
/*报告相对坐标*/
void input_report_rel(struct input_dev *dev, unsigned int code, int value);
/*报告绝对坐标*/
void input_report_abs(struct input_dev *dev, unsigned int code, int value);
/*报告同步事件*/
void input_sync(struct input_dev *dev);
而所有的输入事件,内核都用统一的数据结构来描述,这个数据结构是input_event,形如代码清单:
struct input_event {
struct timeval time;
_ _u16 type;
_ _u16 code;
_ _s32 value;
};
三、Linux SPI主机和设备驱动
SPI(同步外设接口)是由摩托罗拉公司开发的全双工同步串行总线,其接口由MISO(串行数据输入)、MOSI(串行数据输出)、SCK(串行移位时钟)、SS(从使能信号)4种信号构成,SS决定了惟一的与主设备通信的从设备,主设备通过产生移位时钟来发起通信。通信时,数据由MOSI输出,MISO输入,数据在时钟的上升或下降沿由MOSI输出,在紧接着的下降或上升沿由MISO读入,这样经过8/16次时钟的改变,完成8/16位数据的传输。
SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性(CPOL)和相位(CPHA)可以进行配置。如果CPOL = 0,串行同步时钟的空闲状态为低电平;如果CPOL = 1,串行同步时钟的空闲状态为高电平。如果CPHA = 0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA = 1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。
在Linux中,用代码清单的spi_master结构体来描述一个SPI主机控制器驱动,其主要成员是主机控制器的序号(系统中可能存在多个SPI主机控制器)、片选数量、SPI模式和时钟设置用到的函数、数据传输用到的函数等。
struct spi_master {
struct device dev;
s16 bus_num;
u16 num_chipselect;
int (*setup) (struct spi_device *spi); //设置模式和时钟
int (*transfer) (struct spi_device *spi, struct spi_message *mesg); //双向数据传输
void (*cleanup) (struct spi_device *spi);
}
分配、注册和注销SPI主机的API由SPI核心提供:
struct spi_master * spi_alloc_master(struct device *host, unsigned size);
int spi_register_master(struct spi_master *master);
void spi_unregister_master(struct spi_master *master);
在Linux中,用代码清单spi_driver结构体来描述一个SPI外设驱动,可以认为是spi_master的client驱动。
struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(sturct spi_device *spi);
int (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
};
在SPI外设驱动中,当透过SPI总线进行数据传输的时候,使用了一套与CPU无关的统一的接口。这套接口的第一个关键数据结构就是spi_transfer,它用于描述SPI传输,如代码清单所示:
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
unsigned cs_chang:1;
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head stransfer_list;
};
而一次完整的SPI传输流程可能不只包含一次spi_transfer, 他可能包含一个或多个spi_transfer, 这些spi_transfer最终通过spi_message组织在一起,定义代码如下:
struct spi_message {
struct list_head transfers;
struct spi_device *spi;
unsigned is_dma_mapped:1;
/*完成被一个callback报告*/
void (*complete) (void *context);
void *context;
unsigned actual_length;
int status;
struct list_head queue;
void *state;
};
通过spi_message_init() 可以初始化spi_message,而将spi_transfer添加到spi_message队列的方法则是:
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);
发起一次spi_message的传输有同步和异步两种方式,同步API时,会阻塞等待这个消息被处理完。同步操作时使用的API是:
int spi_sync( struct spi_device *spi, struct spi_message *message);
使用异步API时,不会阻塞等待这个消息被处理完,但是可以在spi_message的complete字段接一个回调函数,当消息被处理完成后,该函数会被调用。异步操作时使用的API是:
int spi_async(struct spi_device *spi, struct spi_message *message);
工程中的Linux设备驱动相关推荐
- 《Linux设备驱动开发详解(第2版)》隆重出版
Linux设备驱动开发详解(第2版)(前一版狂销3万册,畅销书最新升级) [新品] 点击看大图 基本信息 * 作者: 宋宝华 * 出版社:人民邮电出版社 * ISBN:97 ...
- linux设备驱动总结,《Linux设备驱动开发详解(第3版)》海量更新总结
本博实时更新<Linux设备驱动开发详解(第3版)>的最新进展. 2015.2.26 几乎完成初稿. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux ...
- 《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)进展同步更新
本博实时更新<Linux设备驱动开发详解(第3版)>的最新进展. 目前已经完成稿件. 2015年8月9日,china-pub开始上线预售: http://product.china-pub ...
- 《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)网购链接
<Linux设备驱动开发详解:基于最新的Linux 4.0内核> china-pub 天猫 dangdang 京东 China-pub 8月新书销售榜 推荐序一 技术日新月 ...
- linux设备驱动第五篇:驱动中的并发与竟态
目录[-] 综述 信号量与互斥锁 Completions 机制 自旋锁 其他的一些选择 不加锁算法 原子变量与位操作 seqlock(顺序锁) 读取-拷贝-更新(RCU) 小结 综述 在上一篇介绍了l ...
- Linux设备驱动中的并发控制总结
并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单元对共享资源(硬件资源和软件上的全局.静态变量)的访问则容易导致竞态(race conditions). SMP是一 ...
- linux 两个驱动 竞态,第7章 Linux设备驱动中的并发控制之一(并发与竞态)
本章导读 Linux设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态(竞争状态). Linux提供了多种解决竞态问题的方式,这些方式适合不同的应用场景. 7.1讲解了并 ...
- Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用
关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...
- Linux中kobject的作用,Linux设备驱动模型-- 数据结构Kset/KObject
前言 Kset和kobject是Linux设备驱动模型中的核心数据结构,其主要作用是将系统中的设备抽象出来,以树状结构组织,方便系统统一管理. 而这个统一管理的地方,就是sysfs,先放一张示例图,阐 ...
最新文章
- CCNA 第二学期答案
- Mips KVM TrapEmulate implemented in Linux
- python发邮件详解_python实现发送邮件详解
- centos安装mysql-python_在Centos上安装mysql python
- MySQL(基础技能)
- ubuntu 启动时显示initramfs 无法进入系统
- AWR6843芯片使用JFlash下载外部NorFlash
- 计算机与医疗PPT,《人工智能医药》ppt课件
- 超硬核 | 一文带你入门用户画像
- 微一案做php,微一案:真正的高效率,都是这么炼成的
- 自媒体必备工具:免费的音文对齐生成SRT字幕,快速打轴匹配声音及文字的在线工具
- 算法题_位运算_9_出现一次的数字和出现k次的数字
- hdu2018 母牛的故事(模拟)
- Unity学习笔记:私有变量private如何在编译器可见 公有变量public在编译器隐藏
- 求三个数的和及平均值简单
- Android来电、去电监听
- 支持苹果IPV6 ONLY 的socket 修改方法
- 计算机php学习,php学习计划流程_IT/计算机_专业资料
- #【精华】转换大写人民币
- DDR扫盲——DDR的发展简史
热门文章
- 不同scheam下查询视图报错ORA-01031的故障解决
- AI洞观 | 周鸿祎秀小猪佩奇手表背后 看看360的AI战略
- linux自动生成证书,Linux生成TLS证书
- 有哪些值得关注的技术博客
- 父亲节python代码半个心_2019父亲节感恩父亲的话 父亲节对爸爸说的暖心话句子...
- 地质灾害监测的主要内容
- cesium and three.js
- 二叉树的高度和深度的理解
- 网络验证 api.php,青柠网络验证webapi接口说明
- 【问题4】:kaggle练习题《自行车租赁业务预测》--带数据分析,用了随机森林,支持向量机,岭回归等