Chinaunix首页 | 论坛 | 博客
  • 博客访问: 18406
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2015-10-05 19:54
文章分类

全部博文(5)

文章存档

2016年(3)

2015年(2)

我的朋友

分类: 嵌入式

2015-10-25 22:25:51

3.1       LinuxIIC驱动开发

3.2.1  IIC总线驱动

在系统开机时,首先装载的是I2C总线驱动。一个总线驱动用于支持一条特定的I2C总线的读写。一个总线驱动通常需要两个模块,一个struct i2c_adapter和一个struct i2c_algorithm来描述:

static struct i2c_adapter pb1550_board_adapter =

 {

  name:              "pb1550 adapter",

  id:                I2C_HW_AU1550_PSC,

  algo:              NULL,

  algo_data:         &pb1550_i2c_info,

  inc_use:           pb1550_inc_use,

  dec_use:           pb1550_dec_use,

  client_register:   pb1550_reg,

  client_unregister: pb1550_unreg,

  client_count:      0,

 };

这个样例挂接了一个叫做“pb1550 adapter”的驱动。但这个模块并未提供读写函数,具体的读写方法由第二个模块,struct i2c_algorithm提供。

static struct i2c_algorithm au1550_algo =

 {

  .name         = "Au1550 algorithm",

  .id      = I2C_ALGO_AU1550,

  .master_xfer = au1550_xfer,

  .functionality     = au1550_func,

 };

 i2c_adap->algo = &au1550_algo;

这个样例给上述总线驱动增加了读写算法。通常情况下每个I2C总线驱动都定义一个自己的读写算法,但鉴于有些总线使用相同的算法,因而可以共用同一套读写函数。本例中的驱动定义了自己的读写算法模块,起名叫“Au1550 algorithm”

全部填妥后,通过调用:

i2c_add_adapter(i2c_adap);

将这两个模块注册到操作系统里,总线驱动就算装上了。对于AMD au1550,这部分已经由AMD提供了。

3.2.2  IIC设备驱动

I2C的设备驱动是通过i2c_add_driver(&my_driver)i2c-core注册的,my_driver中的核心是detach_client attach_adapter函数,在attach_adapter中通过probe探测到总线上的设备并把设备和驱动建立连接以完成设备的初始化。

设备驱动透过I2C总线同具体的设备进行通讯。一个设备驱动有两个模块来描述,struct i2c_driverstruct i2c_client

当系统开机、I2C总线驱动装入完成后,就可以装入设备驱动了。首先装入如下结构:

static struct i2c_driver zlg7290_driver = {

     .owner           = THIS_MODULE,

     .name            = DEV_NAME,

//   .id          = I2C_DRIVERID_ZLG7290,

     .flags             = I2C_DF_NOTIFY,

     .attach_adapter     = zlg7290_attach_adapter,

     .detach_client       = zlg7290_detach_client,

};

  

i2c_add_driver(&zlg7290_driver);

这个i2c_driver一旦装入完成,其中的attach_adapter函数就会被调用。

如何探测总线上的设备、Linuxi2c设备地址如何标识并引用?

I2C, 典型的attach_adapter如下所示:

static int my_attach(struct i2c_adapter *adapter)

{

return i2c_probe(adapter, &addr_data, zlg7290_detect);

}

 

static int zlg7290_detect (struct i2c_adapter *adapter, int address, int kind)

{

    ..........

}

3.2.2.1            IIC设备探测

注意探测可能会找到多个设备,因而不仅一个I2C总线可以挂多个不同类型的设备,一个设备驱动也可以同时为挂在多个不同I2C总线上的设备服务。

每当设备驱动探测到了一个它能支持的设备,它就创建一个struct i2c_client来标识这个设备:

struct i2c_client *new_client;

……

    new_client->addr = address;

    new_client->adapter = adapter;

    new_client->driver = &zlg7290_driver;

    new_client->flags = 0;

 

    /* Fill in the remaining client fields */

    strlcpy(new_client->name, DEV_NAME, I2C_NAME_SIZE);

 

    /* Tell the I2C layer a new client has arrived */

    if ((err = i2c_attach_client(new_client))){

        printk("i2c attach client error.\n");

        goto exit_kfree;

    }

可见,一个i2c_client代表着位于adapter总线上,地址为address,使用zlg7290_driver来驱动的一个设备。它将总线驱动与设备驱动,以及设备地址绑定在了一起。一个i2c_client就代表着一个I2C设备。

当得到I2C设备后,就可以直接对此设备进行读写:

/*

  * The master routines are the ones normally used to transmit data to devices

  * on a bus (or read from them). Apart from two basic transfer functions to

  * transmit one message at a time, a more complex version can be used to

  * transmit an arbitrary number of messages without interruption.

  */

 extern int i2c_master_send(struct i2c_client *,const char* ,int);

 extern int i2c_master_recv(struct i2c_client *,char* ,int);

3.2.2.2            linuxIIC设备地址

addr_data是在 include/linux/i2c.h 中定义的或自己在自己驱动程序中定义的一个i2c_client_address_data结构:

static struct i2c_client_address_data addr_data = {            \

    .normal_i2c    = normal_i2c,                    \

    .probe        = probe,                    \

    .ignore        = ignore,                    \

    .forces        = forces,                    \

}

若自己不定义,则用i2c.h中的默认定义。

/* i2c_client_address_data is the struct for holding default client addresses for a driver and for the parameters supplied on the command line */

struct i2c_client_address_data {

    unsigned short *normal_i2c;

    unsigned short *probe;

    unsigned short *ignore;

    unsigned short **forces;

};

根据作者自行定义设备地址与否,有两种情形:

a. 采用默认定义,一般是不会work,毕竟大多数i2c-core中是不可能提前知道所接设备地址的,这样通过i2c_probe()探测肯定不可能找到,也不可能建立两者之间的联系;况且,i2c_probe()属于i2c-core中的函数,i2c-core中管理着所有注册过的设备和驱动列表,i2c_probe()中也不能随意传入地址,否则容易导致系统混乱或有潜在的风险,所以i2c-core也不允许这么做!

b. 作者自行定义地址结构

典型例子如下:

若自行定义,则参考如下:

/* Addresses to scan */

static unsigned short normal_i2c[] = {I2C_KS0127_ADDON>>1,

                       I2C_KS0127_ONBOARD>>1, I2C_CLIENT_END};/// 实际设备的地址List

static unsigned short probe[2] =    {I2C_CLIENT_END, I2C_CLIENT_END};

static unsigned short ignore[2] =     {I2C_CLIENT_END, I2C_CLIENT_END};

static struct i2c_client_address_data addr_data = {

    normal_i2c,

    probe,

    ignore,

};

或者根本就不定义完整的i2c_client_address_data结构,只根据需要定义normal_i2c[],probe[],ignore[]forces[][],然后调用i2c_probe(adapter,&addr_data, &my_probe) 即可。

my_probe()中把实际的地址赋于i2c_client,调用i2c_set_clientdata()设置i2c_client->dev->drv_data,并调用i2c_attach_client(client)向系统注册设备。

最后,i2c_probe()中探测时的地址优先级:

forces[][], probe[], normal_i2c[](其中忽略ignore[]中的项)

I2c设备在实际使用中比较广泛,sensor,rtc,audio, codec,etc. 因设备复杂性不同,Linux中有些驱动中对地址的定义不在同一文件,这时多数情况都在arch中对设备作为platform_device进行初始化并注册的代码中。

阅读(1151) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~