栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux驱动之总线详解

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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); 
int (*resume)(struct device *dev); 
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);

#include      //初始化头文件
#include    //最基本的文件,支持动态添加和卸载模块。
#include  //平台设备所需要的头文件

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}};

static int device_init(void)
{
	// 设备信息注册到 Linux 内核
	platform_device_register(&beep_device);
	printk("platform_device_register ok n");
	return 0;
}

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);

#include    //初始化头文件
#include   //最基本的文件,支持动态添加和卸载模块。
#include //平台设备所需要的头文件

int beep_probe(struct platform_device *pdev){
	printk("beep_proben");
	return 0;
	
}

int beep_remove(struct platform_device *pdev){
	printk("beep_removen");
	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	
};

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;
}

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

struct i2c_adapter {
    struct module *owner;
    unsigned int class;               
    const struct i2c_algorithm *algo; 
    void *algo_data;

    
    struct rt_mutex bus_lock;

    int timeout;                    
    int retries;
    struct device dev;              

    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 {
    
    
    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);

    
    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指向负责该设备的adapter
    struct i2c_client {
            unsigned short flags;           
            unsigned short addr;            

            char name[I2C_NAME_SIZE];
            struct i2c_adapter *adapter;    
            struct device dev;              
            int init_irq;                   
            int irq;                        
            struct list_head detected;
            #if IS_ENABLED(CONFIG_I2C_SLAVE)
                    i2c_slave_cb_t slave_cb;        
            #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; 
    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核心提供一些API

int 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;     
          __u16 flags; //flags: 消息传输方向和特性。I2C_M_RD:表示读取消息;0:表示发送消息。
          ...
          __u16 len;              
          __u8 *buf;              
  };
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);

            
            return (ret == 1) ? count : ret;
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/694771.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号