Linux设备之I2C体系

    xiaoxiao2023-03-16  20

    I2C体系结构分为三个组成部分 1、I2C核心:I2C核心提供了总线驱动和设备驱动的注册、注销的方法,I2C通信方法(即algorithem提供的通信函数),与具体适配器无关的代码以及检测设备地址的代码等。这部分是与平台无关的.与其对应的是Linux内核源代码中的drivers目录下的i2c-core.c. 2、I2C总线驱动:对I2C硬件体系结构中适配器的实现,I2C总线驱动主要包含了I2C适配器数据结构i2c_adapter,I2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数,控制I2C总线驱动的代码,控制I2C适配器以主控方式产生开始位,停止位,读写以及设备读写方式,产生ack等。不同的CPU平台对应着不同的I2C总线驱动,下面我们用的是i2c-s3c2410.c 3、I2C客户驱动程序:是对I2C硬件体系结构设备端得实现。主要实现I2C设备驱动中的i2c_driver和i2c_client。i2c_driver只是实现设备和总线的挂接,而挂接在总线上的设备是千差万别的,比如实现对i2c设备的检测和提供应用接口file_operations 结构体

    Linux的I2C驱动框架中的主要数据结构及其关系 i2c_adapter结构体表示一个物理的i2c总线控制器

    struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */ 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; };

    上面包含i2c_algorithm,其对应一套通信方法

    struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);//I2C传输函数指针 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);//SMBUS传输函数指针 u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能 }

    一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期,比如发送7位设备地址或者S信号P信号之类的就得调用i2c_algorithm的master_xfer函数来发送信号,master_xfer产生的信号以i2c_msg为单位

    struct i2c_msg { __u16 addr; /* 设备地址 */ __u16 flags; /* 标志,0代表写,I2C_M_RD表示读 */ __u16 len; /* 数据长度 */ __u8 *buf; /* 消息数据 */ };

    i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述.

    struct i2c_client { unsigned short flags; /* 标志 */ unsigned short addr; /* 低七位为芯片地址 */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* 依附的i2c_adapter */ struct i2c_driver *driver; /* 依附的i2c_driver */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head detected; };

    i2c_driver对应一套驱动方法,不对应于任何的物理实体

    struct i2c_driver { unsigned int class; int (*attach_adapter)(struct i2c_adapter *) __deprecated;//适配器函数指针 int (*detach_adapter)(struct i2c_adapter *) __deprecated; /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); void (*alert)(struct i2c_client *, unsigned int data); int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; };

    i2c_driver和i2c_client是一对多的关系,一个适配器上可以支持多个同等类型的i2c_client。i2c_client依附于i2c_adpater,这与I2C硬件体系中适配器和设备的关系一致。.i2c_driver提供了i2c_cli2ent与i2c_adapter产生联系的函数.当attach_a2dapter()函数探测物理设备时,如果确定存在一个client,则把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter,driver指针指向该i2c_driver,并调用i2c_adapter的client_register()函数来注册此设备.相反的过程发生在i2c_driver的detach_client()函数被调用的时候. 当i2c_add_driver分配一个i2c_driver结构体的时候,系统会把i2c_driver放入链表,然后从adapter链表里取出”适配器”,然后调用i2c_driver的attach_adapter函数,attach_adapter函数会执行i2c_probe(adapter,设备地址,function);其实就是用adapter的master_xfer发信号,确定有无设备,如果有就调用function处理函数。

    I2C总线驱动 1.分配i2c_adapter 2.设置algorithm结构提中的master_xfer结构体(发送i2c信号的函数)等其他设置 3.注册i2c_add_adapter 其实I2C总线驱动就是提供I2C适配器的硬件驱动,探测,初始化I2C适配器(比如申请i2c的io地址和中断号),驱动CPU控制的i2c适配器从硬件上产生各种信号以及处理I2C中断等。并且提供I2C适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针。

    下面我们来写一个I2C设备AT24C08的设备驱动程序 首先是在入口函数中分配一个i2c_driver结构体

    static int at24cxx_init(void) { i2c_add_driver(&at24cxx.driver); }

    构造at24cxx.driver结构体

    static struct i2c_driver at24cxx.driver = { .driver = { .name = "at24cxx", }, .attach_adapter = at24cxx_attach_adapter, .detach_client = at24cxx_detach_client, };

    at24cxx_attach_adapter

    static int at24cxx_attach_adapter(struct i2c_adapter* adapter) { return i2c_probe(adapter,&addr_data,at24cxx_detect); }

    对于addr_data的实现我们可以这样

    static unsigned short ignore[] = {I2C_CLIENT_END}; static unsigned short normal_addr[] = {0x50,I2C_CLIENT_END}; static struct i2c_client_address_data addr_data = { .normal_i2c = normal_addr,/*要发出S信号和设备地址信号并得到ACK信号才能确认这个设备存在*/ .probe = ignore, .ignore = ignore, //.force /*强制认为存在这个设备*/ };

    at24cxx_detect函数

    static struct i2c_client *at24cxx_client; static int at24cxx_detect(struct i2c_adpter *adapter,int address,int kind) { at24cxx_client = kzalloc(sizeof(struct i2c_client),GFP_KERNEL); at24cxx_client->addr = address; at24cxx_client->adapter = adapter; at24cxx_client->driver = &at24cxx_driver; at24cxx_client->flags = 0; strcpy(at24cxx_client->name,"at24cxx"); i2c_attach_client(at24cxx_client); }

    相反的 at24cxx_detach_client函数

    static int at24cxx_detach_client(struct i2c_client *client) { i2c_detach_client(client); kfree(i2c_get_clientdata(client)); return 0; }

    大致的i2c设备的i2c_driver实现设备与总线的挂载已经完成,但是i2c设备是千差万别的,比如是字符设备 我们还是得register_chrdev();然后实现文件操作接口,即实现file_operations的read,write等函数,如果是声卡那就得实现ALSA驱动又回到了那一套,先创建一个类,然后在类下创建设备,实际上就是udev机制。 其实udev完全在用户态工作,利用设备加入或移除时内核发生的热拔插事件来工作,在热拔插时,设备的详细信息会由内核输出到位于/sys的sysfs文件系统。udev的设备命名策略,权限控制和事件处理都是在用户太完成的,它利用sysfs中的信息创建设备借点等工作。

    转载请注明原文地址: https://ju.6miu.com/read-1152709.html
    最新回复(0)