linux驱动之总线详解

linux驱动

第一章 linux驱动之设备与驱动
第二章 linux驱动之设备树与GPIO子系统



一、总线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_xfer
       to NULL. If an adapter algorithm can do SMBus access, set
       smbus_xfer. If set to NULL, the SMBus protocol is simulated
       using common I2C messages */
    /* master_xfer should return the number of messages successfully
       processed, 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指向负责该设备的adapter
    struct 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核心提供一些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;     /* 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;
    }
  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值