linux下I2C驱动体系结构及在FL2440开发板上的具体实现

最近一段时间,我在网上看了一些关于linux下i2c的文档,对i2c有了一些较浅层次了解。写这篇博客,主要是对现在已经掌握知识的巩固。

Linux下I2C驱动体系结构

Linux下I2C驱动体系结构由 三大部分 构成:

1. I2C核心

I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法。它也提供了I2C通信方法(algorithm)上层的、与具体适配器无关的代码 i2c_transfer() 函数,并通过这个函数将总线驱动、用户层串起来。

2.I2C总线驱动

I2C总线驱动就是linux内核中I2C适配器的软件实现,它告诉I2C适配器该怎么工作,从而实现I2C适配器与从设备的通信功能。

我们可以通过这个驱动,使用i2c总线去操控挂接在总线上的设备。

I2C总线驱动由 i2c_adapteri2c_algorithm 这两个结构体来描述。

3.I2C设备驱动

I2C设备驱动是对I2C从设备的软件实现,包括对设备的读、写等操作。

一个具体的I2C设备驱动包括两个部分:

  1. ①设备驱动代码。用于将设备注册到内核中,并提供了对设备的读写操作。
    ②设备硬件信息i2c_client。

我们也可以通过设备驱动,跳过i2c总线,直接对设备进行操控。

I2C设备驱动由 i2c_driveri2c_client 这两个结构体来描述。

从上面我们知道了,对于i2c总线上的设备,我们可以通过两种不同的方式去控制。一种是利用i2c设备驱动,另一种是利用i2c总线驱动

所有的I2C驱动代码位于drivers/i2c目录下:

(1) i2c-core.c

这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。

(2)i2c-dev.c

通用i2c设备驱动。就是这个文件实现了用户层利用i2c总线驱动去控制设备的功能,我们会在后面着重分析这个文件。

(3)chips文件夹

这个目录中包含了一些特定的I2C设备驱动,如Dallas公司的DS1337实时钟芯片、EPSON公司的RTC8564实时钟芯片和I2C接口的EEPROM驱动等。

(4)busses文件夹

这个文件中包含了一些I2C总线的驱动,如S3C2410的I2C控制器驱动为i2c-s3c2410.c

(5)algos文件夹

实现了一些I2C总线适配器的algorithm。

I2C核心(i2c-core.c)

初始化模块

static int __init i2c_init(void)/*i2c初始化*/
 {
     int retval;

     retval = bus_register(&i2c_bus_type);/*i2c适配器驱动的注册*/
     if (retval)
         return retval;
 #ifdef CONFIG_I2C_COMPAT
     i2c_adapter_compat_class = class_compat_register("i2c-adapter");
     if (!i2c_adapter_compat_class) {
         retval = -ENOMEM;
         goto bus_err;
     }
 #endif
     retval = i2c_add_driver(&dummy_driver);/*i2c设备驱动的注册*/
     if (retval)
         goto class_err;
     return 0;

 class_err:
 #ifdef CONFIG_I2C_COMPAT
     class_compat_unregister(i2c_adapter_compat_class);
 bus_err:
 #endif
     bus_unregister(&i2c_bus_type);
     return retval;
 }

我们可知i2c_init()函数主要完成的就是i2c总线驱动和i2c设备驱动的注册。

i2c传输,发送和接收

/*用户空间调用write()时,内核调用此函数,此函数最终调用i2c_transfer()*/  
int i2c_master_send(const struct i2c_client *client, constchar *buf, int count)

/*用户空间调用read()时,内核调用此函数,此函数最终调用i2c_transfer()*/ 
int i2c_master_recv(const struct i2c_client *client, char*buf, int count)  

/*用于传输数据,此函数是i2c适配器工作的基石,我们放在总线驱动中分析*/    
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

由此我们i2c核心具体完成的就是:

  1. 实现了I2C总线驱动和设备驱动的注册;
  2. 提供接口函数i2c_master_send(),i2c_master_recv(),i2c_transfer()实现了用户界面通过适配器去控制i2c设备的功能。

我们接下来看看I2C设备驱动,也就是第一种操控i2c设备的方法。

I2C设备驱动

要想让内核中的i2c设备驱动使能,我们就应该去内核中的make menuconfig中添加设备的支持,但是这个设备在i2c总线上,所以我们必须要先去内核中添加i2c总线的支持,在添加设备支持。我们这里就只拿eeprom设备作为例子去分析。

这里写图片描述
这里写图片描述
这里写图片描述

在添加完i2c总线的支持后,我们再去添加eeprom,这时候,在eeprom下就会出现I2C EEPROM这个选项。

这里写图片描述
这里写图片描述
这里写图片描述

添加完i2c总线和eeprom设备支持后,在sys/devices/platform/s3c2440-i2c/i2c-0中就会出现相对应的设备。
这里写图片描述

这里写图片描述

那接下来,我们就来看看i2c设备驱动吧。

从前面我们知道i2c设备驱动是由i2c_driver和i2c_client这两个结构体描述的。

我们首先看一下i2c_driver这个结构体

struct i2c_driver 
{  
  unsignedint class;  

  int(*attach_adapter)(struct i2c_adapter *) __deprecated; /*依附i2c适配器函数指针*/  
  int(*detach_adapter)(struct i2c_adapter *) __deprecated;/*脱离i2c适配器函数指针*/  

  int (*probe)(struct i2c_client*, const struct i2c_device_id *);  
  int (*remove)(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;  /* 该驱动所支持的设备ID表 */  

 /*Device detection callback for automatic device creation */  
  int(*detect)(struct i2c_client *, struct i2c_board_info *);  
  constunsigned short *address_list;  
  structlist_head clients;  
};  

我们可以看出i2c_driver结构对应一套具体的驱动方法。

那么i2c_driver这个结构体是怎么实现的呢,drivers/misc/eeprom/at24.c 这个文件就是驱动代码的实现,并且支持大多数I2C接口的eeprom。

i2c_driver的实现

static struct i2c_driver at24_driver = {
     .driver = {
         .name = "at24",       /*设备名字*/
         .owner = THIS_MODULE,
    },
     .probe = at24_probe,     /*at24_probe函数*/
     .remove = __devexit_p(at24_remove),
     .id_table = at24_ids,    /*设备id表*/
 };

在这个结构体中,我们对i2c_driver结构体中的driver,probe,remove,id_table这些成员进行了赋值。

i2c初始化函数

static int __init at24_init(void)
 {
    if (!io_limit) {
        pr_err("at24: io_limit must not be 0!\n");
        return -EINVAL;
     }

    io_limit = rounddown_pow_of_two(io_limit);
    return i2c_add_driver(&at24_driver);
 }

在前面的代码中,我们通过构建了at24_driver这个结构体,完成了对i2c_driver结构体的赋值,在初始化函数中,我们调用核心层函数,注册i2c_driver结构体。这样,我们的这个I2C设备层驱动就被作为模块加载到内核中了。

i2c_client

在linux系统下,我们习惯于将驱动代码与硬件信息分隔开,这样可以使我们的驱动代码具有可移植性。因此在驱动i2c_driver的构建之后,我们需要去完成硬件信息的构造,即i2c_client。

我们先看一下i2c_client 结构体
struct i2c_client {  
unsigned short flags;                 /*I2C_CLIENT_TEN:使用10位从地址,I2C_CLIENT_PEC:使用SMBus包错误检测*/  
         unsignedshort addr;                 /* chipaddress - NOTE: 7bit    */  
         charname[I2C_NAME_SIZE];  
         struct i2c_adapter *adapter; /* 依附的i2c_adapter   */  
         struct i2c_driver *driver;  /* 依附的i2c_driver*/  
         structdevice dev;             /* the devicestructure             */  
         intirq;                         /* irq issuedby device               */  
         structlist_head detected;  
};

我们可以看出结构体这个结构体对应于真实的物理设备,其中包含了芯片地址,设备名称,设备所依附的适配器,设备所依附的驱动,设备使用的中断号等内容。

i2c_client在开发板文件中的实现

At24c不依赖于具体的CPU和I2C控制器硬件特性,因此如果电路板包含该外设,只需要添加对应的i2c_board_info,下面是at24c02 i2c_client在板文件中的实现:

修改文件: linux/arch/arm/mach-s3c2440/mach-smdk2440.c

#include <linux/i2c/at24.h>  
static struct at24_platform_data at24c02 = {  
    .byte_len   = SZ_2K / 8,        /* eeprom的存储大小,单位Byte */
    .page_size  = 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值