1. Overview 2. Data Structure 3. Adapter 4. I2C-core 5. Slave Device 1. Overview 1.1 Definition · I2C Inter-Integrated Circuit · SMBUS System Management Bus, the I2C subset 1.2 Characteristics · The amount of data exchanged is small. · The required data transfer rate is low. 1.3 Speed · Fast speed 400 kbps · Full speed 100 kbps 1.4 Topology 2 Data Structure 理解數據結構對理解整個驅動程序子系統是很重要的。I2C的主要有兩大數據結構,struct i2c_client 和 struct i2c_adapter。 2.1 i2c_client struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct i2c_driver *driver; /* and our access routines */ struct device dev; /* the device structure */ int irq; /* irq issued by device (or -1) */ char driver_name[KOBJ_NAME_LEN]; struct list_head list; /* DEPRECATED */ struct completion released; }; struct i2c_client代表一個掛載到i2c總線上的i2c從設備,該設備所需要的數據結構,其中包括 · 該i2c從設備所依附的i2c主設備 struct i2c_adapter *adapter · 該i2c從設備的驅動程序struct i2c_driver *driver · 作為i2c從設備所通用的成員變量,比如addr, name等 · 該i2c從設備驅動所特有的數據,依附于dev->driver_data下 2.2 i2c_adapter struct i2c_adapter { struct module *owner; unsigned int id; unsigned int class; const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; ... ... }; struct i2c_adapter代表主芯片所支持的一個i2c主設備,該設備所需要的數據結構, 其中,struct i2c_algorithm *algo是該i2c主設備傳輸數據的一種算法,或者說是在i2c總線上完成主從設備間數 據通信的一種能力。 struct i2c_algorithm { 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); u32 (*functionality) (struct i2c_adapter *); }; 接下來,要實現整個i2c子系統的驅動,便圍繞這兩個數據結構展開,其主要步驟可總結為以下三步, · 實現i2c主設備驅動 (drivers/i2c/bus/*) · 注冊i2c從設備的i2c_client (drivers/i2c/i2c-core) · 實現i2c從設備驅動 3 Adapter 內核目錄drivers/i2c下有兩個文件夾,algorithm和bus,其中bus存放i2c主設備的驅動,主設備驅動完成兩大 任務, · 提供該i2c主設備與從設備間完成數據通信的能力 · 完成該i2c_adapter和所有已知的i2c_client的注冊 以i2c-pxa.c為例, /* drivers/i2c/bus/i2c-pxa.c */ static int __init i2c_adap_pxa_init(void) { return platform_driver_register(&i2c_pxa_driver); } static struct platform_driver i2c_pxa_driver = { .probe = i2c_pxa_probe, ... ... .id_table = i2c_pxa_id_table, }; static int i2c_pxa_probe(struct platform_device *dev) { struct pxa_i2c *i2c; i2c->adap.algo = i2c_pxa_algorithm; // 提供該i2c主設備與從設備間完成數據通信的能力 i2c_add_numbered_adapter(&i2c->adap); // 調用i2c-core.c中的接口函數,完成該i2c_adapter和 i2c_client的注冊 ... ... } static const struct i2c_algorithm i2c_pxa_algorithm = { .master_xfer = i2c_pxa_xfer, // 根據pxa具體芯片的要求,完成i2c數據傳輸 .functionality = i2c_pxa_functionality, }; 4 I2C-core 內核目錄drivers/i2c下的i2c-core.c,顧名思義,是內核為I2C提供的統一系統接口。 看看i2c_add_numbered_adapter做了些什么, int i2c_add_numbered_adapter(struct i2c_adapter *adap) { ... ... status = i2c_register_adapter(adap); return status; } static int i2c_register_adapter(struct i2c_adapter *adap) { ... ... device_register(&adap->dev); //完成I2C主設備adapter的注冊,即注冊object和發送uevent等 i2c_scan_static_board_info(adap); ... ... } i2c_scan_static_board_info(adap),此函數為整個I2C子系統的核心,它會去遍歷一個由I2C從設備組成的雙向 循環鏈表,并完成所有I2C從設備的i2c_client的注冊,具體過程如下, static void i2c_scan_static_board_info(struct i2c_adapter *adapter) { struct i2c_devinfo *devinfo; //已經建立好了的I2C從設備鏈表 list_for_each_entry(devinfo, &__i2c_board_list, list) { i2c_new_device(adapter,&devinfo->board_info); ... ... } } struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { ... ... i2c_attach_client(client); ... ... } int i2c_attach_client(struct i2c_client *client) { ... ... device_register(&client->dev); //完成I2C從設備client的注冊 ... ... } 那么,這個I2C從設備組成的雙向循環鏈表,是什么時候通過什么方式建立起來的呢? 以某重力感應設備為例, /* /arch/arm/mach-pxa/starwood_p1.c */ static void __init saar_init(void) { ... ... i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_bma220_info)); ... ... } static struct i2c_board_info saar_i2c_bma220_info[] = { { .driver_name = "bma220", .addr = 0x0B, .irq = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO15)), }, }; /* drivers/i2c/i2c-boardinfo.c */ int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) { ... ... struct i2c_devinfo *devinfo; devinfo->board_info = *info; list_add_tail(&devinfo->list, &__i2c_board_list); //將I2C從設備加入該鏈表中 ... ... } 所以,在系統初始化的過程中,我們可以通過 i2c_register_board_info,將所需要的I2C從設備加入一個名為 __i2c_board_list雙向循環鏈表,系統在成功加載I2C主設備adapt后,就會對這張鏈表里所有I2C從設備逐一地 完成 i2c_client的注冊。 5 Slave Driver 如果說硬件方面,I2C主設備已經集成在主芯片內,軟件方面,linux也為我們提供了相應的驅動程序,位于 drivers/i2c/bus下,那么接下來I2C從設備驅動就變得容易得多。既然系統加載I2C主設備驅動時已經注冊了 i2c_adapter和i2c_client,那么I2C從設備主要完成三大任務, · 系統初始化時添加以i2c_board_info為結構的I2C從設備的信息 · 在I2C從設備驅動程序里使用i2c_adapter里所提供的算法,即實現I2C通信。 · 將I2C從設備的特有數據結構掛在到i2c_client.dev->driver_data下。 以重力感應裝置為例, static int __init BMA220_init(void) { return i2c_add_driver(&bma220_driver); } static struct i2c_driver bma220_driver = { .driver = { .owner = THIS_MODULE, .name = "bma220", }, .class = I2C_CLASS_HWMON, .probe = bma220_probe, .remove = bma220_remove, }; static int bma220_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bma220_data *data; i2c_check_functionality(client->adapter, I2C_FUNC_I2C) i2c_smbus_read_word_data(client, 0x00); // i2c-core提供的接口,利用i2c_adapter的算法實現I2C通信 i2c_set_clientdata(bma220_client, data); // 將設備的數據結構掛到i2c_client.dev->driver_data下 misc_register(&bma_device); request_irq(client->irq, bma220_irq_handler, IRQF_TRIGGER_RISING, "bma220", &data->bma220); bma220_set_en_tt_xyz(0); bma220_reset_int(); ... ... } 信盈達靠技術打天下 以下課程可免費試聽C語言、電子、PCB、STM32、Linux、FPGA、JAVA、安卓等。 想學習的你和我聯系預約就可以免費聽課了。 宋工企鵝號:35--24-65--90-88 Tel/WX:173--17--95--19--08 |