工程中的Linux设备驱动
2013-11-27 20:19
176 查看
一、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);
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);
相关文章推荐
- 跳频通信有关工程概念
- HDOJ 1875 畅通工程再续
- 最近一个工程出现的问题 以前工程可用的代码到这个工程里也会显示 The following classes could not be found: - ImageView (Change to and
- linux设备驱动的编译与下载
- hdu 1875 畅通工程再续(Kruskal算法)
- eclipse工程转studio,生成build.gradle时报错:make sure all dependencies are opend
- 在IDEA中使用MyBatis Generator逆向工程生成代码
- 机器学习特征提取 | 自动特征工程featuretools
- 使用PowerDesigner 15对现有数据库进行反向工程(图解教程一) by Yulh
- android工程下的文件资源类型
- 在tomcat下部署工程
- 普通android工程转换为C/C++工程之后,再还原成android工程的解决方案
- hdoj 1863 畅通工程【最小生成树,kruskal&&prim】
- 在Airbnb收购ChangeTip的工程团队后,ChangeTip想要出售剩余资产
- iOS工程调试小技巧一:交换dealloc、字体方法,重写description
- Spring --web工程下xml配置文件部署
- VS2010使用.net 4.0中的ASP.NET MVC 2 模板建立工程后无法提交HTML代码解决方案一则
- HDU 1863 畅通工程
- [复制别人的工程报错一]Multiple markers at this line
- eclipse下环境变量设置:eclipse导入工程出现 Unbound classpath variable Error