linux驱动之总线详解
linux驱动
第一章 linux驱动之设备与驱动
第二章 linux驱动之设备树与GPIO子系统
linux驱动之总线详解
- linux驱动
- 一、总线bus
- 1.bus在linux中文件结构
- 2.bus_type
- 3.常用的系统函数
- 二、Platform设备驱动
- 1.platform
- 2.platform设备
- 3.platform驱动
- 4.platform设备驱动细节
- 三、I2C
- 1.IIC总线
- 2.I2C在linux下的结构
- 2.I2C中主要的结构体
- 3.i2c的系统调用函数
一、总线bus
1.bus在linux中文件结构
在linux系统中,在sys/bus文件夹下存放的文件夹都是实例化的总线,比如platform,i2c等等,而这些文件夹下又存放着device和driver文件夹,这些则是存放在该总线下的设备与驱动。
每个实例化的bus会规定在该bus下的device和driver的匹配方式,并建立链表将所有device和driver建立联系,
2.bus_type
1.每个sys/bus文件夹下的文件夹都是bus_type的实例化对象,其中match指向device和driver和匹配方式
每个sys/bus文件夹下的文件夹都是bus_type的实例化对象,其中match指向device和driver和匹配方式函数
struct bus_type { const char *name; /* 总线名字 */
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs;
const struct attribute_group **bus_groups; /* 总线属性 */
const struct attribute_group **dev_groups; /* 设备属性 */ const struct attribute_group **drv_groups; /* 驱动属性 */ int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev); int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state); /*当设备收到 suspend 命令的时候,就会执行这个函数*/
int (*resume)(struct device *dev); /*当设备收到 resume 命令的时候,就会执行这个函数*/
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
}
2.每个实例化的bus(xbus)都会有这样的一个结构,xbus通过此结构与device和driver建立联系,同时此结构也规定了在xbus下的匹配方式等。
3.常用的系统函数
总线初始化: buses_init()
- 开机运行
- 创建kset,后面的相关操作会将所有的实例化的bus(xbus)连到kset
总线注册:bus_register()函数int bus_register(struct bus_type *bus)
- 在/sys/bus下建立xbus目录项,并创建属性文件
- 在/sys/bus/xbus下建立devices目录项,并创建属性文件
- 在/sys/bus/xbus下建立drivers目录项,并创建属性文件
- 初始化 priv->klist_devices链表头
- 初始化priv->klist_drivers链表头
设备注册:int device_register(struct device *dev)
- 在/sys/bus/xbus/devices下建立yyy目录项
- 加入bus-> priv->devices_kset链表
- 加入bus-> priv->klist_devices链表
- 遍历bus-> priv->klist_drivers,执行bus->match()寻找合适的drv
- dev关联driv,执行drv->probe()
驱动注册:int driver_register(struct device_driver *drv)
- 在/sys/bus/xbus/drivers下建立zzz目录项
- 加入bus-> priv->drivers_kset链表
- 加入bus-> priv->klist_drivers链表
- 遍历bus-> priv->klist_klist_devices链表,执行bus->match()寻找合适的dev
- dev关联dev,执行drv->probe()
二、Platform设备驱动
1.platform
platform作为bus的一个实例化的总线,系统提供了platform_device_register(&beep_device)和platform_driver_register(&beep_driver)向platform注册,同时系统也提供了platform_device_unregister(&beep_device)和platform_driver_unregister(&beep_device)向platform删除device和driver。每次注册和删除device和diver时,paltform的device和driver文件夹下都会添加和删除对应的文件。
struct bus_type platform_bus_type =
{
.name = "platform",
.dev_groups = platform_dev_groups, .match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops, };
2.platform设备
Platform设备常用函数和基本流程
1.定义平台设备机构体:struct platform_device。
2.注册平台设备:platform_device_register(&beep_device);
3.卸载平台设备:platform_device_unregister(&beep_device);
/** @Author: topeet* @Description: 基于平台设备模型的device.c*/
#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/platform_device.h> //平台设备所需要的头文件
/*** @description: 释放 flatform 设备模块的时候此函数会执行* @param {structdevice} *dev:要释放的设备* @return {*}*/
void beep_release(struct device *dev)
{printk("beep_release \n");
}
// 设备资源信息,也就是蜂鸣器所使用的所有寄存器
struct resource beep_res[] = {[0] = {.start = 0x020AC000,//寄存器组的起始地址.end = 0x020AC003,//寄存器组的终止地址.flags = IORESOURCE_MEM,.name = "GPIO5_DR",}};
// platform 设备结构体
struct platform_device beep_device = {.name = "beep_test",.id = -1,.resource = beep_res,.num_resources = ARRAY_SIZE(beep_res),.dev = {.release = beep_release}};
/*** @description: 设备模块加载* @param {*}无* @return {*}无*/
static int device_init(void)
{// 设备信息注册到 Linux 内核platform_device_register(&beep_device);printk("platform_device_register ok \n");return 0;
}
/*** @description: 设备模块注销* @param {*}无* @return {*}无*/
static void device_exit(void)
{// 设备信息卸载platform_device_unregister(&beep_device);printk("gooodbye! \n");
}
module_init(device_init);
module_exit(device_exit);
MODULE_LICENSE("GPL");
3.platform驱动
Platform设备常用函数和基本流程
1.定义平台设备机构体:struct platform_driver。
2.注册平台设备:platform_driver_register(&beep_driver);
3.卸载平台设备:platform_driver_unregister(&beep_device);
/** @Author:topeet* @Description: 基于平台设备模型的driver.c*/
#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/platform_device.h>//平台设备所需要的头文件
/*** @description: beep_probe,驱动和设备匹配成功会进入此函数* @param {structplatform_device} *pdev* @return {*}*/
int beep_probe(struct platform_device *pdev){printk("beep_probe\n");return 0;}
/*** @description: beep_remove,当driver和device任意一个remove的时候,就会执行这个函数* @param {structplatform_device} *pdev* @return {*}*/
int beep_remove(struct platform_device *pdev){printk("beep_remove\n");return 0;
}
// 该设备驱动支持的设备的列表 ,他是通过这个指针去指向 platform_device_id 类型的数组
const struct platform_device_id beep_idtable = {.name = "beep_test", //设备名字叫“beep_test”
};// platform 驱动结构体
struct platform_driver beep_driver = {.probe = beep_probe,.remove = beep_remove,.driver={.owner = THIS_MODULE,.name = "beep_test"},.id_table = &beep_idtable
};
/*** @description: 设备模块加载* @param {*}无* @return {*}无*/
static int beep_driver_init(void){int ret = 0;// platform驱动注册到 Linux 内核ret = platform_driver_register(&beep_driver);if(ret<0){printk("platform_driver_register error \n");}printk("platform_driver_register ok \n");return 0;
}
/*** @description: 设备模块注销* @param {*}无* @return {*}无*/
static void beep_driver_exit(void){// platform驱动卸载platform_driver_unregister(&beep_driver);printk("gooodbye! \n");
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
4.platform设备驱动细节
Platform注意要点:
1.Platform_driver中的.driver,.id_table都含有name变量,会自动与Platform_device中的name匹配,
.id_table的优先级要高于.driver,只有当.id_table没有匹配上时,才会匹配.driver。
2.Platform_driver和Platform_device匹配成功时会自动调用Platform中.probe指向的函数。
三、I2C
1.IIC总线
IIC作为BUS_TYPE中的一个实例化的对象,也继承了BUS_TYPE的结构。
linux启动之后,默认执行i2c_init,在i2c_init中调用bus_register(&i2c_bus_type)将IIC总线实例化。
struct bus_type i2c_bus_type = {.name = "i2c",.match = i2c_device_match,.probe = i2c_device_probe,.remove = i2c_device_remove,.shutdown = i2c_device_shutdown,};
2.I2C在linux下的结构
每个IIC设备都会指向一个IIC适配器,适配器结构体中存在Algorithm,Algorithm中存在一系列函数指针,这些函数指针指向真正硬件操作代码。
每个client都指向IIC适配器,每个适配器都是一个设备,都有对应的驱动。适配器的驱动是在platform下定义的,系统在开机后会自动完成每个适配器的驱动,并将适配器的信息记录到imx_i2c_struct中,此结构体不仅有适配器的信息,同时海记录了IIC的其他信息,供驱动开发人员查看。
2.I2C中主要的结构体
适配器:struct i2c_adapter
/** i2c_adapter is the structure used to identify a physical i2c bus along* with the access algorithms necessary to access it.*/
struct i2c_adapter {struct module *owner;unsigned int class; /* classes to allow probing for */const struct i2c_algorithm *algo; /* the algorithm to access the bus */void *algo_data;/* data fields that are valid for all devices */struct rt_mutex bus_lock;int timeout; /* in jiffies */int retries;struct device dev; /* the adapter device */int nr;char name[48];struct completion dev_released;struct mutex userspace_clients_lock;struct list_head userspace_clients;struct i2c_bus_recovery_info *bus_recovery_info;const struct i2c_adapter_quirks *quirks;
};struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, or a negative value on error */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *);#if IS_ENABLED(CONFIG_I2C_SLAVE)int (*reg_slave)(struct i2c_client *client);int (*unreg_slave)(struct i2c_client *client);
#endif
};
i2c从设备:struct i2c_client
adapter指向负责该设备的adapterstruct i2c_client {unsigned short flags; /* div., see below */unsigned short addr; /* chip address - NOTE: 7bit */char name[I2C_NAME_SIZE];struct i2c_adapter *adapter; /* the adapter we sit on */struct device dev; /* the device structure */int init_irq; /* irq set at initialization */int irq; /* irq issued by device */struct list_head detected;#if IS_ENABLED(CONFIG_I2C_SLAVE)i2c_slave_cb_t slave_cb; /* callback for slave mode */#endif};flags: :I2C_CLIENT_TEN表示设备使用10位芯片地址,I2C客户端PEC表示它使用SMBus数据包错误检查addr: addr在连接到父适配器的I2C总线上使用的地址。name: 表示设备的类型,通常是芯片名。adapter: struct i2c_adapter 结构体,管理托管这个I2C设备的总线段。dev: Driver model设备节点。init_irq: 作为从设备时的发送函数。irq: 表示该设备生成的中断号。detected: struct list_head i2c的成员_驱动程序.客户端列表或i2c核心的用户空间设备列表。slave_cb: 使用适配器的I2C从模式时回调。适配器调用它来将从属事件传递给从属驱动程序。i2c_客户端识别连接到i2c总线的单个设备(即芯片)。暴露在Linux下的行为是由管理设备的驱动程序定义的。
i2c设备驱动:struct i2c_driver
struct i2c_driver {unsigned int class;int (*probe)(struct i2c_client *, const struct i2c_device_id *);int (*remove)(struct i2c_client *);struct device_driver driver;const struct i2c_device_id *id_table;int (*detect)(struct i2c_client *, struct i2c_board_info *);const unsigned short *address_list;struct list_head clients;...};
probe: i2c设备和i2c驱动匹配后,回调该函数指针。
id_table: struct i2c_device_id 要匹配的从设备信息。
address_list: 设备地址
clients: 设备链表
detect: 设备探测函数
开机时,系统内核会调用i2c_adap_imx_init,开机后的系统已经注册了i2c_imx_driver驱动,此驱动会和IIC的设备树配对,完成adapter的设置。同时创建imx_i2c_struct结构体。此结构体记录了IIC的一些配置,系统可以通过此结构体获得IIC的参数。
I.MX6U 的 I2C 适配器驱动是个标准的 platform 驱动,由此可以看出,虽然 I2C 总线为别的设备提供了一种总线驱动框架,但是 I2C 适配器却是 platform驱动。
static int __init i2c_adap_imx_init(void)
{return platform_driver_register(&i2c_imx_driver);
}struct imx_i2c_struct {struct i2c_adapter adapter;//适配器struct clk *clk;void __iomem *base;wait_queue_head_t queue;unsigned long i2csr;unsigned int disable_delay;int stopped;unsigned int ifdr; /* IMX_I2C_IFDR */unsigned int cur_clk;unsigned int bitrate;const struct imx_i2c_hwdata *hwdata;struct imx_i2c_dma *dma;
};
clk: clk结构体保存时钟相关信息
bitrate: 保存i2c的波特率
dma: struct imx_i2c_dma 结构体 dam相关信息等等
3.i2c的系统调用函数
IIC核心提供一些APIint i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)struct i2c_adapter *i2c_get_adapter(int nr);//获取编号为nr的I2C适配器
void i2c_put_adapter(struct i2c_adapter *adap);//释放adap指向的适配器
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);//把 I2C 适配器和 I2C 器件关联起来
void i2c_unregister_device(struct i2c_client *client)//功能 注销一个 client。
数据传输系统函数,函数最终就是调用我们前面讲到的i2c_imx_xfer()函数来实现数据传输
struct i2c_msg {__u16 addr; /* slave address */__u16 flags; //flags: 消息传输方向和特性。I2C_M_RD:表示读取消息;0:表示发送消息。...__u16 len; /* msg length */__u8 *buf; /* pointer to msg data */};
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
int i2c_master_send(const struct i2c_client *client,const char *buf, int count)
int i2c_master_recv(const struct i2c_client *client,char *buf, int count)
int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,int count, u16 flags){int ret;struct i2c_msg msg = {.addr = client->addr,.flags = flags | (client->flags & I2C_M_TEN),.len = count,.buf = buf,};ret = i2c_transfer(client->adapter, &msg, 1);/** If everything went ok (i.e. 1 msg transferred), return #bytes* transferred, else error code.*/return (ret == 1) ? count : ret;}
linux驱动之总线详解相关推荐
- linux驱动双摄像头,详解linux 摄像头驱动编写
对于现代嵌入式设备,特别是手机来说,摄像头是很重要的一个设备.很多同学买手机,一看颜值,第二就看摄像头拍照如何.所以,从某个角度来说,摄像头是各个厂家主打的应用功能.那么,linux是如何支持摄像头的 ...
- 【正点原子Linux连载】第三十五章 Linux内核顶层Makefile详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
- 15、Windows驱动开发技术详解笔记(11) 基本概念
9.Windows驱动程序的入口函数规定为_DriverEntry@8,所以用C++编写时要用extern. 驱动程序中,不能使用编译器运行时函数,甚至C语言中的malloc,C++的new函数都不能 ...
- 《Windows驱动开发技术详解》学习笔记
Abstract 如果推荐 Windows 驱动开发的入门书,我强烈推荐<Windows驱动开发技术详解>.但是由于成书的时间较早,该书中提到的很多工具和环境都已不可用或找不到,而本文 ...
- STM32L475 SPI驱动LCD ST7789V2详解
概述 最近在学习正点原子潘多拉开发板,在此结合原子哥的代码, 对SPI驱动LCD做一个详细介绍. TFTLCD 和 SPI TFTLCD介绍 TFT-LCD 即薄膜晶体管液晶显示器.其英文全称为:Th ...
- linux命令之-dmesg详解
Linux命令dmesg用法详解 功能说明:显示开机信息. 语 法:dmesg [-cn][-s <缓冲区大小>] 补充说明:kernel会将开机信息存储在ring buffer中.您 ...
- Kali linux无线网络渗透详解笔记
Kali linux无线网络渗透详解笔记 第一章:搭建渗透环境测试环境 第二章:WiFi网络的构成 第三章:监听WiFi网络 第四章:捕获数据包 第五章: 分析数据包 第六章:获取信息 第七章:WPS ...
- linux远程date命令,linux之date命令详解
date命令的用处 1.用于显示特殊的时间格式,可以用于对日志文件的命名 2.用于设置时间 ,不过这方面用的比较少,因为一般的服务器都设置的有自动同步网络时间 用法: date [OPTION]... ...
- linux 命令xargs,Linux下xargs命令详解
Linux下xargs命令详解 1. 简介 之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要,所以就有了xargs命令,例如: find /sbin -pe ...
最新文章
- 数学中的span以及线性流形是什么意思
- Windows7瘦身和备份
- 做爱做的事,做有快感的事
- ASP.NET中进行消息处理(MSMQ) 二
- 基础 - jQuery选项卡
- 搭建高可用的rabbitmq集群 + Mirror Queue + 使用C#驱动连接
- Tomcat启动时自动加载Servlet
- Mapnik使用postgres中的栅格数据
- CentOS 使用yum update 更新时保留特定版本的软件
- 如何从零开始搭建自己的博客
- 设计模式笔录(一),什么是设计模式
- paip.c#.net 右键菜单带图标
- latex数学符号加粗_latex 数学符号加粗
- python关键词排名批量查排名_Python 批量获取Baidu关键词的排名并入库
- App架构设计经验谈
- JAVA--多线程管理
- 《软件设计师》备考笔记
- php破解referer防盗链解析,Referer原理与图片防盗链实现方法详解
- matlab 为双y轴加标签,[转载]matlab双y轴添加误差棒(转载)
- Java中将对象转换成String的三种方法
热门文章
- 矩阵开根号_CVPR2018论文阅读-Faster MPN-COV:迭代计算矩阵平方根以快速训练全局协方差池...
- Hands On Machine Learning with Scikit Learn and TensorFlow(第三章)
- wow服务器维护8月14,8月14日服务器例行维护公告(已完成)
- 腾讯海外游戏直播Android开发面经
- 花嫁之容氏浅浅最后怎么样了_花嫁之容氏浅浅大结局最后章节第2章免费看
- 网络技术入门(一):网络技术基础知识系统归结
- 10个实用的UX设计作品推销小窍门
- C++学习--台阶问题
- fig翻译_Fig. 2. (a) Loading equipment; (b) Distributi简体中文怎么写 - 什么意思? - 怎么翻译?...
- 香橙派全志H3烧入U-boot和Linux内核以及配置