歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux的I2C驅動架構概述

最近因為工作需要涉及到了I2C總線。雖然我過去用過I2c,但看了 Linux kernel 後才發現,一個 layer 能被做到這樣完善。

1. Linux的I2C驅動架構

Linux中I2C總線的驅動分為兩個部分,總線驅動(BUS)和設備驅動(DEVICE)。其中總線驅動的職責,是為系統中每個I2C總線增加相應的讀寫方法。但是總線驅動本身並不會進行任何的通訊,它只是存在在那裡,等待設備驅動調用其函數。

 

設備驅動則是與掛在I2C總線上的具體的設備通訊的驅動。通過I2C總線驅動提供的函數,設備驅動可以忽略不同總線控制器的差異,不考慮其實現細節地與硬件設備通訊。

 

1.1 總線驅動

在系統開機時,首先裝載的是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提供了。

1.2 設備驅動

如前所述,總線驅動只是提供了對一條總線的讀寫機制,本身並不會去做通信。通信是由I2C設備驅動來做的,設備驅動透過I2C總線同具體的設備進行通訊。一個設備驅動有兩個模塊來描述,struct i2c_driver和struct i2c_client。

 

當系統開機、I2C總線驅動裝入完成後,就可以裝入設備驅動了。首先裝入如下結構:

 

static struct i2c_driver driver = {

.name = "i2c TV tuner driver",

.id = I2C_DRIVERID_TUNER,

.flags = I2C_DF_NOTIFY,

.attach_adapter = tuner_probe,

.detach_client = tuner_detach,

.command = tuner_command,

};

 

i2c_add_driver(&driver);

 

這個i2c_driver一旦裝入完成,其中的attach_adapter函數就會被調用。在其中可以遍歷系統中的每個i2c總線驅動,探測想要訪問的設備:

 

static int tuner_probe(struct i2c_adapter *adap)

{

return i2c_probe(adap, &addr_data, tuner_attach);

}

 

注意探測可能會找到多個設備,因而不僅一個I2C總線可以掛多個不同類型的設備,一個設備驅動也可以同時為掛在多個不同I2C總線上的設備服務。

 

每當設備驅動探測到了一個它能支持的設備,它就創建一個struct i2c_client來標識這個設備:

new_client->addr = address;

new_client->adapter = adapter;

new_client->driver = &driver;

 

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

err = i2c_attach_client(new_client);

if (err)

goto error;

 

可見,一個i2c_client代表著位於adapter總線上,地址為address,使用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);

 

與通常意義上的讀寫函數一樣,這兩個函數對i2c_client指針指定的設備,讀寫int個char。返回值為讀寫的字節數。對於我們現有的SLIC的驅動,只要將最後要往總線上進行讀寫的數據引出傳輸到這兩個函數中,移植工作就算完成了,我們將得到一個Linux版的I2C設備驅動。
 

Copyright © Linux教程網 All Rights Reserved