32.【linux驱动】一文读懂linux设备驱动模型(常见总线bus)

更新时间:2023-06-14 04:11:34 阅读: 评论:0

32.【linux驱动】⼀⽂读懂linux设备驱动模型(常见总线bus)
总线分类
接上⼀篇讲了总线模型,总线从代码⾓度看都是虚拟概念,但是有的总线对应有实体硬件,有的没有。platform总线就没有实体硬件,这种称作虚拟总线。SPI,IIC这种有实体硬件的应该是真正的总线了=。=
platform总线
总线的设计是为了代码的复⽤,其中platform总线是最经常使⽤的虚拟总线,任何直接与CPU打交道的设备都挂接在platform虚拟总线上。
platform总线已经实现好的,只需要使⽤。使⽤的时候需要填充注册platform_device设备和platform_driver驱动。
struct platform_device {
const char* name;
int  id;
struct device dev;
u32  num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
填充完必要的参数使⽤int platform_device_add(struct platform_device *pdev);注册设备使⽤platform_device_del(struct platform_device *pdev);删除设备。
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;
};
填充完必要的参数使⽤int platform_driver_register(struct platform_driver *);注册驱动。使⽤void platform_driver_unregister(struct platform_driver *);卸载驱动。
设备和驱动的匹配有四种模式
1. 通过设备树
2. 通过ACPI风格
什么东西可以补肾3. 通过ID表(device设备名出现在driver的ID表⾥⾯就⾏)
4. 通过device和driver的名字
#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
#include<linux/platform_device.h>
struct platform_device mydev ={
.name ="distancer",
.id =0,
.dev ={
.
platform_data =NULL,
},
.resource =NULL,
};
//⽣成初始化函数和卸载函数,并⽤module_init, module_exit指定相应的函数
module_driver(mydev, platform_device_register, platform_device_unregister);
MODULE_LICENSE("GPL");
#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/fs.h>
int myprobe(struct platform_device *pdev)
{
printk("in myprobe : %s.%d, driver_data=%p\n", pdev->name, pdev->id, pdev->id_entry->driver_data); return0;
}
int myremove(struct platform_device *pdev)
{
printk("in myremove : %s.%d, driver_data=%p\n", pdev->name, pdev->id, pdev->id_entry->driver_data); return0;
}
struct platform_device_id ids[]={
{"mykey",0x11},
{"myir",0x22},
{"distancer",0x33},
{},//最后必须给⼀个空的元素,标明是最后⼀个
};
struct platform_driver mypdrv ={
蛇形跑技巧
.probe = myprobe,
.remove = myremove,
.driver ={
.owner = THIS_MODULE,
.name ="mydrv",
},
.
id_table = ids,
};
module_platform_driver(mypdrv);
MODULE_LICENSE("GPL");
SPI总线
为了实现代码的复⽤,将外设与总线控制驱动分离。SPI驱动架构中存在SPI总线,SPI控制器设备,SPI控制器驱动,SPI外设,SPI外设驱动这⼏个概念。SPI控制器设备描述了SPI控制器的信息,SPI控制器驱动加载后会提供SPI读写操作函数,SPI外设驱动只需要使⽤这些读写函数即可,SPI外设描述了使⽤那个SPI控制器。这样可以实现外设驱动不依赖SPI控制器,即时换了⼀个平台,外设驱动也不需要改⼀⾏代码。SPI总线对于SPI外设驱动⽽⾔就是⼀条通信管道。SPI控制器驱动好了之后对应在内核中⽣成spi_master对象。借⽤⽹上⼀张图⽚
外设驱动怎样写
基本结构
先看spi_driver结构体
struct spi_driver {
const struct spi_device_id *id_table;
int(*probe)(struct spi_device *spi);
int(*remove)(struct spi_device *spi);
少妇性欲强void(*shutdown)(struct spi_device *spi);
int(*suspend)(struct spi_device *spi, pm_message_t mesg);
int(*resume)(struct spi_device *spi);
struct device_driver driver;
};
简单使⽤填充好probe和remove这两个功能即可。驱动代码就不演⽰。
注册驱动
使⽤int spi_register_driver(struct spi_driver *sdrv);将上⾯的结构体注册到内核即可。
与外设通信
常⽤函数有:
int spi_write(struct spi_device *spi, const void *buf, size_t len);
int spi_read(struct spi_device *spi, void *buf, size_t len);
这两个函数都需要spi_device,在这个结构体⾥⾯就保存了使⽤那个控制器(spi_master),这个spi_device在probe函数被传进来的,需要保存下来。
注册设备
⼀般情况下spi设备驱动都是在开始的时候使⽤板级注册函数spi_register_board_info就注册好了,开机之后不让注
册。spi_register_board_info没有被导出,⽆法使⽤。但是先看⼀下spi_board_info结构体描述外设。
struct spi_board_info {
char  modalias[SPI_NAME_SIZE];//匹配名
英文段落const void*platform_data;//⾃定义数据
void*controller_data;//使⽤那个spi控制器
int  irq;//中断号
u32  max_speed_hz;//最⼤速度
u16  bus_num;//使⽤那个控制器
u16  chip_lect;//⽚选
u8  mode;//模式
};
下⾯是⼀个实例。
struct pl022_config_chip spi0_info ={
/* available POLLING_TRANSFER, INTERRUPT_TRANSFER, DMA_TRANSFER */
.com_mode = CFG_SPI0_COM_MODE,
.iface = SSP_INTERFACE_MOTOROLA_SPI,
/* We can only act as master but SSP_SLAVE is possible in theory */
.
hierarchy = SSP_MASTER,
/* 0 = drive TX even as slave, 1 = do not drive TX as slave */
.slave_tx_disable =1,
.rx_lev_trig = SSP_RX_4_OR_MORE_ELEM,
.tx_lev_trig = SSP_TX_4_OR_MORE_EMPTY_LOC,
.ctrl_len = SSP_BITS_8,
.wait_state = SSP_MWIRE_WAIT_ZERO,
.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
#if (CFG_SPI0_CS_GPIO_MODE)
.cs_control = spi0_cs,
#endif
如何煮螃蟹.
clkdelay = SSP_FEEDBACK_CLK_DELAY_1T,
};
static struct spi_board_info spi_plat_board[] __initdata ={
[0]={
.modalias        ="spidev",/* fixup */
.max_speed_hz    =3125000,/* max spi clock (SCK) speed in HZ */
.bus_num        =0,/* Note> t bus num, must be smaller than ARRAY_SIZE(spi_plat_device) */
.chip_lect    =0,/* Note> t chip lect num, must be smaller than spi cs_num */
.controller_data =&spi0_info,
.mode            = SPI_MODE_3 | SPI_CPOL | SPI_CPHA,
},
};
上⾯的参数⽐较复杂,留着后⾯参数。下⾯解决spi_register_board_info函数不能⽤的问题。看其源码
int __devinit
spi_register_board_info(struct spi_board_info const*info,unsigned n)
{
struct boardinfo *bi;
int i;
bi =kzalloc(n *sizeof(*bi), GFP_KERNEL);
if(!bi)
return-ENOMEM;
for(i =0; i < n; i++, bi++, info++){
白毛女观后感struct spi_master *master;
memcpy(&bi->board_info, info,sizeof(*info));
mutex_lock(&board_lock);
list_add_tail(&bi->list,&board_list);
list_for_each_entry(master,&spi_master_list, list)
家家乞巧望秋月
spi_match_master_to_boardinfo(master,&bi->board_info);
mutex_unlock(&board_lock);
}
return0;
}
重点在于拿到spi_master这个控制器设备就可以另辟蹊径注册设备,还好内核有给出他的获取函数spi_busnum_to_master,参考其他设备的写法得出注册⽅法。
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/device.h>
#include<mach/gpio.h>
#include<mach/platform.h>
#include<mach/platform.h>
#include<linux/spi/spi.h>
#include<linux/amba/pl022.h>
static struct fb_data fb1_plat_data ={
};
struct pl022_config_chip spi0_info ={
/* available POLLING_TRANSFER, INTERRUPT_TRANSFER, DMA_TRANSFER */
.com_mode = CFG_SPI0_COM_MODE,
.iface = SSP_INTERFACE_MOTOROLA_SPI,
/* We can only act as master but SSP_SLAVE is possible in theory */
.hierarchy = SSP_MASTER,
/* 0 = drive TX even as slave, 1 = do not drive TX as slave */
.slave_tx_disable =1,
.rx_lev_trig = SSP_RX_4_OR_MORE_ELEM,
.tx_lev_trig = SSP_TX_4_OR_MORE_EMPTY_LOC,
.ctrl_len = SSP_BITS_8,
.
wait_state = SSP_MWIRE_WAIT_ZERO,
.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
.clkdelay = SSP_FEEDBACK_CLK_DELAY_1T,
};
收益怎么算static struct spi_board_info spi_plat_board ={
.modalias        ="hello_spi_fb",/* fixup */
.max_speed_hz    =25000000,/* max spi clock (SCK) speed in HZ */
.bus_num        =0,/* Note> t bus num, must be smaller than ARRAY_SIZE(spi_plat_device) */ .chip_lect    =1,/* Note> t chip lect num, must be smaller than spi cs_num */
.controller_data =&spi0_info,
.mode            = SPI_MODE_3 | SPI_CPOL | SPI_CPHA,
.platform_data  =&fb1_plat_data,
};
__attribute__ ((unud))static void device_spi_delete(struct spi_master *master,unsigned cs)
{
struct device *dev;
char str[32];
snprintf(str,sizeof(str),"%s.%u",dev_name(&master->dev), cs);
dev =bus_find_device_by_name(&spi_bus_type,NULL, str);
if(dev){
printk(": Deleting %s\n", str);
device_del(dev);
}
}
static struct spi_device *spi_device;
static int __init hello_init(void){
struct spi_master *master;
master =spi_busnum_to_master(spi_plat_board.bus_num);
if(!master){
printk(":  spi_busnum_to_master(%d) returned NULL\n",
spi_plat_board.bus_num);
return-EINVAL;
}
/* make sure it's available */
device_spi_delete(master, spi_plat_board.chip_lect);
spi_device =spi_new_device(master,&spi_plat_board);
put_device(&master->dev);
if(!spi_device){
printk(":    spi_new_device() returned NULL\n");
return-EPERM;
}

本文发布于:2023-06-14 04:11:34,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1037555.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:驱动   总线   设备   函数   外设   注册   控制器
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图