Linux的 i2c 驱动框架分析
1.基本概念
總線設備驅動模型,是Linux 內核的一個基礎,基本理論可以說按照大企業的分工原則,每個人只要負責自己的事情,向其他部門給出標準的接口調用,后勤部就負責后勤工作,廚房有可能跟后勤部產生工作上的溝通,不能一個廚師炒菜就去找后勤部的某個人員拿一根大白菜,而是由廚房統一申請,由后勤部門去采購再給回廚房,寫代碼很多時候跟生活中相識,需要遵守一定的規則,如果喜歡打擦邊球,繞過規則的程序員,也很像那些走機動車道的開電動車的人們,有時候都能達到目的,但是存在車禍的風險。
總線
Linux 內核里面的總線很多,總線的工作主要是管理設備和驅動的,可以是驅動和設備耦合的媒婆,沒有總線,設備找不到驅動,驅動也找不到設備,所以如果一個設備,首先要確定它是屬于什么總線的,就是一個單身狗,他是想找哪個地方的妹子,需要向總線注冊,告訴總線我的name是什么,對于驅動也是一樣,這個驅動也要告訴總線,我是什么類型的驅動,實現了哪些接口,我的name是什么?
設備在注冊的時候,向總線的設備鏈表添加一個設備,然后通過name,后面還有一個table_id,來查找這個總線上有沒有已經實現了這個設備的驅動,如果有了,就執行這個驅動的probe函數。
驅動在注冊的時候也是一樣,通過name 和 table_id 匹配來查找設備,找到了就執行驅動對應的probe函數來做一系列事情。一個驅動是可以對應多個設備的,但是一個設備只能對應一個驅動。
總線設備
設備對應描述的是一個硬件,原來老的Linux 內核使用板級文件來描述設備,新的Linux 內核使用dts來描述設備,實際上是一個東西,都是用來描述設備,dts更能顯示面向對象思想,也更能題先程序員的能力,比如一個I2C設備,需要描述I2C地址,I2C gpio端口,設備掛載在哪路I2C總線上等等。
總線驅動
一個驅動程序,總是要有依賴的,既然是對應的總線設備,就需要對應的總線驅動來驅動它,讓硬件設備能夠正常工作起來,?驅動也就是操作設備的方式和操作設備的流程還有接口。
2.I2C傳輸協議
3.Linux下I2C驅動程序的體系結構
Linux下的i2c框架,分為了3個子模塊
1、I2C核心
I2C核心主要是i2c-core.c,里面涉及的adapter都是和i2c核心進行耦合的,設備和驅動不需要關心adapter部分。
2、I2C總線驅動
I2C總線驅動是對I2C硬件體系結構中適配器(i2c-adapter)端的實現,這部分主要是產生I2C協議的波形,操作硬件完成開始信號,數據傳輸,停止信號,設備應答檢測等等,還是看上面那個經典的圖片,adapter就是往下走的,所以就是操作到平臺cpu的部分了。
3、I2C設備驅動
I2C設備驅動(i2c-client)是對I2C硬件體系結構中設備端的實現,設備一般掛接在受CPU控制的I2C適配器上,通過I2C適配器與CPU交換數據。
重要的文件說明
\kernel\drivers\i2c\i2c-core.c
這個文件實現了 I2C 核心的功能以及/proc/bus/i2c*接口。同時對I2C底層的收發函數進行封裝。會調用i2c_transfer ,里面實現了adap->algo->master_xfer(adap, msgs, num)
kernel\drivers\i2c\i2c-dev.c
該函數注冊了一個設備文件的功能,也就是注冊了一個字符設備驅動程序,可以通過/dev/i2c-0(i2c-0, i2c-1,…, i2c-10,…)找到具體的I2C適配器,這個I2C設備的主設備號為89,次設備號0~255。通過訪問這個接口,可以通過open()、 write()、 read()、 ioctl()和 close()等來訪問這個設備。
kernel\drivers\i2c\busses\i2c-rk30.c
跟平臺相關的i2c-adapter,通過這些接口,達到控制cpu的I2C控制器寄存器,然后可以在i2c總線上產生i2c信號,驅動i2c設備。
比較重要的結構體和調用過程
i2c_driver
對應一套驅動方法,是純粹的用于輔助作用的數據結構,它不對應于任何的物理實體。
i2c_client
對應于真實的物理設備,每個 I2C 設備都需要一個 i2c_client 來描述。i2c_client 一般被包含在 I2C 字符設備的私有信息結構體中。
i2c_adpater
用來匹配i2c_driver與i2c_client。即 i2c_client 依附于 i2c_adpater。由于一個適配器上可以連接多個 I2C 設備, 所以一個 i2c_adpater 也可以被多個 i2c_client 依附, i2c_adpater 中包括依附于它的 i2c_client 的鏈表 。
i2c_algorithm
i2c_adapter對應與物理上的一個適配器,而i2c_algorithm對應一套通信方法,一個i2c適配器需要i2c_algorithm中提供的通信函數來控制適配器上產生特定的訪問周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指針。
i2c_algorithm中的關鍵函數master_xfer()用于產生i2c訪問周期需要的start stop ack信號,以i2c_msg(即i2c消息)為單位發送和接收通信數據。
i2c_msg也非常關鍵,調用驅動中的發送接收函數需要填充該結構體
我們在驅動里面調用
這個i2c_transfer繼續往下看
會跑到i2c_core.c里面的__i2c_transfer,里面會調用一個指針adap->algo->master_xfer
這個是一個指針,那我們就要找到這個指針的初始化位置
這個位置在 i2c_rk29.c里面
所以這個才是最終的產生I2C波形的位置。
貼出最終的代碼
static int rk29_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int start, int stop) { struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data; int ret = 0; if(msg->len == 0) { ret = -EINVAL; i2c_err(i2c->dev, "<error>msg->len = %d\n", msg->len); goto exit; } if(msg->flags & I2C_M_NEED_DELAY) i2c->udelay = msg->udelay; else i2c->udelay = 0; if((ret = rk29_send_address(i2c, msg, start))!= 0) { rk29_set_nak(i2c); i2c_err(i2c->dev, "<error>rk29_send_address timeout\n"); goto exit; } if(msg->flags & I2C_M_RD) { if(msg->flags & I2C_M_REG8_DIRECT) { struct i2c_msg msg1 = *msg; struct i2c_msg msg2 = *msg; msg1.len = 1; msg2.len = msg->len - 1; msg2.buf = msg->buf + 1; if((ret = rk29_i2c_send_msg(i2c, &msg1)) != 0) i2c_err(i2c->dev, "<error>rk29_i2c_send_msg timeout\n"); if((ret = rk29_i2c_recv_msg(i2c, &msg2)) != 0) { i2c_err(i2c->dev, "<error>rk29_i2c_recv_msg timeout\n"); goto exit; } } else if((ret = rk29_i2c_recv_msg(i2c, msg)) != 0) { i2c_err(i2c->dev, "<error>rk29_i2c_recv_msg timeout\n"); goto exit; } } else { if((ret = rk29_i2c_send_msg(i2c, msg)) != 0) { rk29_set_nak(i2c); i2c_err(i2c->dev, "<error>rk29_i2c_send_msg timeout\n"); goto exit; } } exit: if(stop || ret < 0) { rk29_i2c_stop(i2c); } return ret; }4.總結
相信大家學Linux 之前都做過單片機吧,不管什么總線協議,最終都是要發信號出去的,不發信號出去的總線協議框架是沒有任何意義的,所以我們在這里吹牛這么多,最終還是要回到代碼里面去fucking the code的。不要我說了一大堆,你還是沒有知道怎么看代碼,那也是沒有任何作用的。
也就是device與driver同時向i2c總線上注冊。當注冊在總線上時,可以通過id_table進行匹配,匹配上之后會調用driver的probe函數。對于一般的I2C設備,可以在probe函數中注冊一個字符設備驅動,從而應用層可以通過open函數打開/dev/i2c-0等設備節點對I2C設備進行讀寫操作。
I2C只是驅動的一部分,我們需要把I2C糅合到Input子系統里面,糅合到攝像頭V4L2里面等等,這些都是需要去看代碼了解里面的脈絡的,我很多時候總是擔心自己太水講了大家也不懂,然后就把圖貼上來,i2c到最后還是通過writel readl等函數讀取IO部分,只有你有個代碼,跟進去看看就知道了。
好了,就說這么多,使勁評論。
掃碼或長按關注
回復「?加群?」進入技術群聊
總結
以上是生活随笔為你收集整理的Linux的 i2c 驱动框架分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于大数据的房价数据可视化分析预测系统
- 下一篇: linux,内核(kernel)对AD9