I2C子系統驅動框架: 應用程序層(app層) ——————————————————————————————————– i2c driver層: 從設備驅動層(TS Sensor等) 1. 須要和應用層交互(fops cdev) 2. 封裝數據,可是不知道數據如何寫入到硬件,須要調用adapter層的相關函數去寫 ——————————————————————————————————– i2c core:維護i2c bus, 包括i2c driver和i2c client鏈表 1. 實現i2c client和i2c driver的匹配 ——————————————————————————————————– i2c adapter層: i2c控制器層,初始化i2c控制器,實現i2c時序 1. 將數據寫入或讀取從設備 2. 不知道具體數據(i2c driver提供的數據)是什麼,但知道具體如何操做(讀/寫)從設備 這一層是具體的廠商實現的,好比三星:driver/i2c/busser/i2c-s3c2410.cjava
框架中,i2c core是由Linux內核實現的(i2c-core.c),i2c adapter是由具體的芯片廠商實現的,好比三星的芯片adapter實現都在driver/i2c/busser/i2c-s3c2410.c。因此這連個部分須要編譯到uImage中(make menuconfig -> device driver -> <*> i2c support -> i2c hardware Bus support -> S3C2410 I2C driver)。 若是在/sys/bus/i2c/devices/i2c-0/1/2 表示有i2c-adapter 存在node
在總結的時候看到有其餘博友整理的框圖很是好,我就借過來給你們分享! linux
從i2c驅動架構圖中能夠看出,linux內核對i2c架構抽象了一個叫核心層core的中間件,它分離了設備驅動device driver和硬件控制的實現細節(如操做i2c的寄存器),core層不但爲上面的設備驅動提供封裝後的內核註冊函數,並且還爲下面的硬件事件提供註冊接口(也就是i2c總線註冊接口i2c_add_register),能夠說core層起到了承上啓下的做用。算法
相關的重要結構體和函數: 1. i2c_client 每個i2c從設備都須要用一個i2c_client結構體來描述,i2c_client對應真實的i2c物理設備device,可是i2c_client不是咱們本身寫程序去建立的,而是經過如下經常使用的方式自動建立的(這個地方不作詳細說明,以介紹整體框架爲主): platform建立: 1. 註冊i2c_board_info 2. 獲取對應的adapter,而後i2c_new_device devicetree建立: 3. 經過設備樹的一個節點去描述一個從設備,設備樹在解析的時候會自動建立client架構
struct i2c_client { unsigned short flags; //標誌位 (讀寫) unsigned short addr; //7位的設備地址(低7位) char name[I2C_NAME_SIZE]; //設備的名字,用來和i2c_driver匹配 struct i2c_adapter *adapter; //依附的適配器(adapter),適配器指明所屬的總線(i2c0/1/2_bus) struct device dev; //繼承的設備結構體 int irq; //設備申請的中斷號 struct list_head detected; //已經被發現的設備鏈表 };
2. i2c_driver driver是指向從設備的驅動程序,由咱們本身去實現並經過i2c_add_register註冊到i2c的bus中,和i2c clinet進行匹配,匹配成功則調用probe函數。app
struct i2c_driver { int (*probe)(struct i2c_client *, const struct i2c_device_id *); //設備匹配成功調用的函數 int (*remove)(struct i2c_client *); //設備移除以後調用的函數 struct device_driver driver; //設備驅動結構體 const struct i2c_device_id *id_table; //設備的ID表,匹配用platform建立的client };
3. i2c_adapter i2c總線適配器其實就是一個i2c總線控制器,本質上是一個物理設備,主要用來完成i2c總線控制器相關的數據通訊 由芯片廠商去實現的。框架
struct i2c_adapter { struct module *owner; unsigned int class; //容許匹配的設備的類型 const struct i2c_algorithm *algo; //指向適配器的驅動程序,實現發送數據的算法 struct device dev; //指向適配器的設備結構體 char name[48]; //適配器的名字 };
4. i2c_algorithm i2c算法,適配器對應的驅動程序,每個適配器對應一個驅動程序,用來描述適配器和設備之間的通訊方法 由芯片廠商去實現的。函數
struct i2c_algorithm { //傳輸函數指針,指向實現IIC總線通訊協議的函數 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); };
5. i2c_msg 把要發送的數據封裝成msg結構體(好比16個字節進行拆分)ui
struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; /* 1 - 讀 0 - 寫 */ __u16 len; /* msg length */ __u8 *buf; /* 要發送的數據 */ };
6. i2c_add_register 註冊一個i2c_driver結構體,經過name或者id_tables或者of_match_table去匹配一個i2c_client,若是匹配成功,則會調用i2c_driver結構體裏面的probe函數,並將對應的i2c_client結構體傳過來。 #define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver) int i2c_register_driver(struct module *owner, struct i2c_driver *driver)spa
7. i2c_transfer 負責經過對應的i2c總線對依附於這個adapter的從機設備(i2c_client)進行讀寫數據(雙向的)。其中要讀寫的數據要封裝成爲一個i2c_msg結構體,根據msg的flags標誌位是0仍是1來決定是讀仍是寫。其實i2c_transfer是對master_xfer的封裝。
/** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Note that there is no requirement that each message be sent to * the same slave address, although that is the most common model. */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) eg. /* i2c_msg指明要操做的從機地址,方向,緩衝區 */ struct i2c_msg msg[] = { {client->addr, 0, 1, &txbuf}, //0表示寫,嚮往從機寫要操做的寄存器的地址 {client->addr, 1, 1, &rxbuf}, //讀數據 }; /* 經過i2c_transfer函數操做msg */ ret = i2c_transfer(client->adapter, msg, 2); //執行2條msg
這幾個重要結構體之間的關係: a – i2c_adapter與i2c_algorithm i2c_adapter對應與物理上的一個適配器,而i2c_algorithm對應一套通訊方法,一個i2c適配器須要i2c_algorithm中提供的(i2c_algorithm中的又是更下層與硬件相關的代碼提供)通訊函數來控制適配器上產生特定的訪問週期。缺乏i2c_algorithm的i2c_adapter什麼也作不了,所以i2c_adapter中包含其使用i2c_algorithm的指針。 i2c_algorithm中的關鍵函數master_xfer()用於產生i2c訪問週期須要的start、stop、ack信號,以i2c_msg爲單位發送和接收通訊數據。 i2c_msg也很是關鍵,調用驅動中的發送接收函數須要填充該結構體
b –i2c_driver和i2c_client i2c_driver對應一套驅動方法 i2c_client對應真實的i2c物理設備device,每一個i2c設備都須要一個i2c_client來描述 i2c_driver與i2c_client的關係是一對多。一個i2c_driver上能夠支持多個同等類型的i2c_client.
c – i2c_adapter和i2c_client i2c_adapter和i2c_client的關係與i2c硬件體系中適配器和設備的關係一致,即i2c_client依附於i2c_adapter,因爲一個適配器上能夠鏈接多個i2c設備,因此i2c_adapter中包含依附於它的i2c_client的鏈表
下面給出一個i2c子系統實例代碼(用設備樹實現): 主機 - 三星的某款cpu 從機 - mpu6050三軸加速度傳感器
設備樹描述: 當設備樹被內核解析後會生成一個依附於i2c-0這個adapter的i2c_client
@i2c-0 {//表示這個i2c_client所依附的adapter是i2c-0 //對應i2c_client的name = "invensense,mpu6050" compatible = "invensense,mpu6050"; //對應i2c_client的addr = 0x69 -- 從機設備的地址 reg = <0x69>; //對應i2c_client的irq interrupts = <70>; };
driver代碼:
http://blog.csdn.net/hanp_linux/article/details/72832158#include <linux/module.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/cdev.h> #include <linux/fs.h> #include <asm/uaccess.h> #include "mpu6050.h" MODULE_LICENSE("GPL"); #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define PWR_MGMT_1 0x6B int MAJOR = 255; int MINOR = 0; struct mpu6050_device { struct cdev cdev; dev_t devno; struct i2c_client * client; }mpu6050_dev; /* 讀取mpu6050中一個字節的數據,將讀取的數據的地址返回 */ static int mpu6050_read_byte(struct i2c_client * client, unsigned char reg_add) { int ret; /* 要讀取的那個寄存器的地址 */ char txbuf = reg_add; /* 用來接收讀到的數據 */ char rxbuf[1]; /* i2c_msg指明要操做的從機地址,方向,緩衝區 */ struct i2c_msg msg[] = { {client->addr, 0, 1, &txbuf}, //0表示寫,嚮往從機寫要操做的寄存器的地址 {client->addr, I2C_M_RD, 1, rxbuf}, //讀數據 }; /* 經過i2c_transfer函數操做msg */ ret = i2c_transfer(client->adapter, msg, 2); //執行2條msg if (ret < 0) { printk("i2c_transfer read err\n"); return -1; } return rxbuf[0]; } static int mpu6050_write_byte(struct i2c_client * client, unsigned char reg_addr, unsigned char data) { int ret; /* 要寫的那個寄存器的地址和要寫的數據 */ char txbuf[] = {reg_addr, data}; /* 1個msg,寫兩次 */ struct i2c_msg msg[] = { {client->addr, 0, 2, txbuf} }; ret = i2c_transfer(client->adapter, msg, 1); if (ret < 0) { printk("i2c_transfer write err\n"); return -1; } return 0; } static int mpu6050_open(struct inode * inodep, struct file * filep) { printk("%s called\n", __func__); mpu6050_write_byte(mpu6050_dev.client, PWR_MGMT_1, 0x00); mpu6050_write_byte(mpu6050_dev.client, SMPLRT_DIV, 0x07); mpu6050_write_byte(mpu6050_dev.client, CONFIG, 0x06); mpu6050_write_byte(mpu6050_dev.client, GYRO_CONFIG, 0xF8); mpu6050_write_byte(mpu6050_dev.client, ACCEL_CONFIG, 0x19); return 0; } static int mpu6050_release(struct inode * inodep, struct file * filep) { printk("%s called\n", __func__); return 0; } void get_temp(union mpu6050_data * data) { data->temp = mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_L); data->temp |= mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_H) << 8; } static long mpu6050_ioctl(struct file * filep, unsigned int cmd, unsigned long arg) { union mpu6050_data data; switch (cmd) { case GET_TEMP: get_temp(&data); break; default: break; } if (copy_to_user((unsigned int *)arg, &data, sizeof(data))) return -1; return 0; } struct file_operations mpu6050_fops = { .owner = THIS_MODULE, .open = mpu6050_open, .release = mpu6050_release, .unlocked_ioctl = mpu6050_ioctl, }; /* 匹配函數,設備樹中的mpu6050結點對應轉換爲一個client結構體 */ static int mpu6050_probe(struct i2c_client * client, const struct i2c_device_id * id) { int ret; printk("mpu6050 match ok!\n"); mpu6050_dev.client = client; /* 註冊設備號 */ mpu6050_dev.devno = MKDEV(MAJOR, MINOR); ret = register_chrdev_region(mpu6050_dev.devno, 1, "mpu6050"); if (ret < 0) goto err1; cdev_init(&mpu6050_dev.cdev, &mpu6050_fops); mpu6050_dev.cdev.owner = THIS_MODULE; ret = cdev_add(&mpu6050_dev.cdev, mpu6050_dev.devno, 1); if (ret < 0) goto err2; return 0; err2: unregister_chrdev_region(mpu6050_dev.devno, 1); err1: return -1; } static int mpu6050_remove(struct i2c_client * client) { printk("mpu6050 removed!\n"); cdev_del(&mpu6050_dev.cdev); unregister_chrdev_region(mpu6050_dev.devno, 1); return 0; } /* 用來匹配mpu6050的設備樹 */ static struct of_device_id mpu6050_of_match[] = { {.compatible = "invensense,mpu6050"}, {}, }; struct i2c_driver mpu6050_driver = { .driver = { .name = "mpu6050", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mpu6050_of_match), }, .probe = mpu6050_probe, .remove = mpu6050_remove, }; static int mpu6050_init(void) { printk("%s called\n", __func__); i2c_add_driver(&mpu6050_driver); return 0; } static void mpu6050_exit(void) { printk("%s called\n", __func__); i2c_del_driver(&mpu6050_driver); return ; } module_init(mpu6050_init); module_exit(mpu6050_exit);