久久国产精品一区二区三区四区,久色婷婷小香蕉久久,国产日韩欧美在线播放不卡,另类av一区二区

SPI 子系統之SPI Driver
來源:嵌入式Linux系統開發    時間:2022-05-16 05:57:05
1、SPI 驅動源文件目錄

Linux common spi driver。

kernel-4.14/drivers/spi/spi.c Linux 提供的通用接口封裝層驅動kernel-4.14/drivers/spi/spidev.c linux 提供的 SPI 通用設備驅動程序kernel-4.14/include/linux/spi/spi.h linux 提供的包含 SPI 的主要數據結構和函數

spi 控制器驅動,IC 廠商提供,不同廠商命名不同。

kernel-4.14/drivers/spi/spi-mt65xx.c MTK SPI 控制器驅動kernel-4.14/drivers/spi/spi-mt65xx-dev.ckernel-4.14/include/linux/platform_data/spi-mt65xx.h

dts。

kernel-4.14/arch/arm/boot/dts/...kernel-4.14/arch/arm64/boot/dts/...

以上文件對應如下 SPI 驅動軟件架構:

SPI 控制器驅動程序

SPI 控制器不用關心設備的具體功能,它只負責把上層協議驅動準備好的數據按 SPI 總線的時序要求發送給 SPI 設備,同時把從設備收到的數據返回給上層的協議驅動,因此,內核把 SPI 控制器的驅動程序獨立出來。

SPI 控制器驅動負責控制具體的控制器硬件,諸如 DMA 和中斷操作等等,因為多個上層的協議驅動可能會通過控制器請求數據傳輸操作,所以,SPI 控制器驅動同時也要負責對這些請求進行隊列管理,保證先進先出的原則。

SPI 通用接口封裝層

為了簡化 SPI 驅動程序的編程工作,同時也為了降低【協議驅動程序】和【控制器驅動程序】的耦合程度,內核把控制器驅動和協議驅動的一些通用操作封裝成標準的接口,加上一些通用的邏輯處理操作,組成了 SPI 通用接口封裝層。

這樣的好處是,對于控制器驅動程序,只要實現標準的接口回調 API,并把它注冊到通用接口層即可,無需直接和協議層驅動程序進行交互。而對于協議層驅動來說,只需通過通用接口層提供的 API 即可完成設備和驅動的注冊,并通過通用接口層的 API 完成數據的傳輸,無需關注 SPI 控制器驅動的實現細節。

SPI 協議驅動程序

SPI 設備的具體功能是由 SPI 協議驅動程序完成的,SPI 協議驅動程序了解設備的功能和通信數據的協議格式。向下,協議驅動通過通用接口層和控制器交換數據,向上,協議驅動通常會根據設備具體的功能和內核的其它子系統進行交互。

例如,和 MTD 層交互以便把 SPI 接口的存儲設備實現為某個文件系統,和 TTY 子系統交互把 SPI 設備實現為一個 TTY 設備,和網絡子系統交互以便把一個 SPI 設備實現為一個網絡設備。如果是一個專有的 SPI 設備,我們也可以按設備的協議要求,實現自己的專有協議驅動。

SPI 通用設備驅動程序

考慮到連接在 SPI 控制器上的設備的可變性,在內核沒有配備相應的協議驅動程序,對于這種情況,內核為我們準備了通用的 SPI 設備驅動程序,該通用設備驅動程序向用戶空間提供了控制 SPI 控制的控制接口,具體的協議控制和數據傳輸工作交由用戶空間根據具體的設備來完成,在這種方式中,只能采用同步的方式和 SPI 設備進行通信,所以通常用于一些數據量較少的簡單 SPI 設備。

2、SPI 通用接口層SPI 通用接口層把具體的 SPI 設備的協議驅動和 SPI 控制器驅動連接在一起。負責 SPI 系統與 Linux 設備模型相關的初始化工作。為協議驅動和控制器驅動提供一系列的標準接口 API 及其數據結構。SPI 設備、SPI 協議驅動、SPI 控制器的數據抽象協助數據傳輸而定義的數據結構。

kernel-4.14/drivers/spi/spi.c。

static int __init spi_init(void){ int status; buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); if (!buf) { status = -ENOMEM; goto err0; } // 創建 /sys/bus/spi 節點 status = bus_register(&spi_bus_type); if (status < 0) goto err1; //創建 /sys/class/spi_master 節點 status = class_register(&spi_master_class); if (status < 0) goto err2; if (IS_ENABLED(CONFIG_SPI_SLAVE)) { status = class_register(&spi_slave_class); if (status < 0) goto err3; } ......}

在這里創建了 SPI 總線,創建 /sys/bus/spi 節點和 /sys/class/spi_master 節點。

重要數據結構:

spi_devicespi_driverspi_board_infospi_controller/spi_masterspi_transferspi_message

重要 API。

spi_message_initspi_message_add_tailspi_syncspi_asyncspi_writespi_read

接下來詳細解析結構體和API,只講解重點部分,完整解析請參考官方文檔:

https://www.kernel.org/doc/html/v4.14//driver-api/spi.html。

只有熟悉每個結構體存儲的是什么東西,才能真正搞懂 SPI 模塊。

spi_master/spi_controller:描述一個 spi 主機設備。

struct spi_master { //Linux 驅動模型中的設備 struct device dev; //此 spi_master 設備在全局 spi_master 鏈表中的節點 struct list_head list; //此 spi_master 編號 s16 bus_num; //此 spi_master 支持的片選信號數量 u16 num_chipselect; //dma 地址對齊 u16 dma_alignment; //此 spi_master 支持傳輸的 mode u16 mode_bits; u32 bits_per_word_mask; /* limits on transfer speed */ u32 min_speed_hz; u32 max_speed_hz; /* other constraints relevant to this driver */ u16 flags; /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock;//總線自旋鎖 struct mutex bus_lock_mutex;//總線互斥鎖 //總線是否處于 lock 狀態 bool bus_lock_flag; //準備傳輸,設置傳輸的參數 int (*setup)(struct spi_device *spi); //傳輸數據 int (*transfer)(struct spi_device *spi, struct spi_message *mesg); // 設備 release 時的清除工作 void (*cleanup)(struct spi_device *spi); bool (*can_dma)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer); bool queued;//是否采用系統的序列化傳輸 struct kthread_worker kworker;//序列化傳輸時的線程 worker struct task_struct *kworker_task;//序列化傳輸的線程 struct kthread_work pump_messages;//序列化傳輸時的處理函數 spinlock_t queue_lock;//序列化傳輸時的queue_lock struct list_head queue;//序列化傳輸時的 msg 隊列頭 struct spi_message *cur_msg;//序列化傳輸時當前的 msg bool idling; bool busy;//序列化傳輸時線程是否處于busy狀態 bool running;//序列化傳輸時線程是否在運行 bool rt;//是否實時傳輸 ...... int (*prepare_transfer_hardware)(struct spi_master *master); //一個 msg 的傳輸實現 int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); ...... /* gpio chip select */ int *cs_gpios; ......};

spi_device:描述一個 spi 從機設備。

struct spi_device { //Linux驅動模型中的設備 struct device dev; struct spi_master *master;//設備所連接的 spi 主機設備 u32 max_speed_hz;//該設備最大傳輸速率 u8 chip_select;//CS片選信號編號 u8 bits_per_word;//每次傳輸長度 u16 mode;//傳輸模式 ...... int irq;//軟件中斷號 void *controller_state;//控制器狀態 void *controller_data;//控制參數 char modalias[SPI_NAME_SIZE];//設備名稱 //CS 片選信號對應的 GPIO number int cs_gpio; /* chip select gpio */ /* the statistics */ struct spi_statistics statistics;};

spi_driver:描述一個 spi 設備驅動。

struct spi_driver { //此driver所支持的 spi 設備 list const struct spi_device_id *id_table; int (*probe)(struct spi_device *spi); int (*remove)(struct spi_device *spi); //系統 shutdown 時的回調函數 void (*shutdown)(struct spi_device *spi); struct device_driver driver;};

spi_board_info:描述一個 spi 從機設備板級信息,無設備樹時使用。

struct spi_board_info { //設備名稱 char modalias[SPI_NAME_SIZE]; const void *platform_data;//設備的平臺數據 void *controller_data;//設備的控制器數據 int irq;//設備的中斷號 u32 max_speed_hz;//設備支持的最大速率 u16 bus_num;//設備連接的 spi 總線編號 u16 chip_select;//設備連接的 CS 信號編號 u16 mode;//設備使用的傳輸 mode};

spi_transfer:描述 spi 傳輸的具體數據。

struct spi_transfer { const void *tx_buf;//spi_transfer 的發送 buf void *rx_buf;//spi_transfer 的接收 buf unsigned len;//spi_transfer 發送和接收的長度 dma_addr_t tx_dma;//tx_buf 對應的 dma 地址 dma_addr_t rx_dma;//rx_buf 對應的 dma 地址 struct sg_table tx_sg; struct sg_table rx_sg; //spi_transfer傳輸完成后是否要改變 CS 片選信號 unsigned cs_change:1; unsigned tx_nbits:3; unsigned rx_nbits:3; ...... u8 bits_per_word;//spi_transfer 中一個 word 占的bits u16 delay_usecs;//兩個 spi_transfer 直接的等待延遲 u32 speed_hz;//spi_transfer 的傳輸速率 struct list_head transfer_list;//spi_transfer掛載到的 message 節點};

spi_message:描述一次 spi 傳輸的信息。

struct spi_message { //掛載在此 msg 上的 transfer 鏈表頭 struct list_head transfers; //此 msg 需要通信的 spi 從機設備 struct spi_device *spi; //所使用的地址是否是 dma 地址 unsigned is_dma_mapped:1; //msg 發送完成后的處理函數 void (*complete)(void *context); void *context;//complete函數的參數 unsigned frame_length; unsigned actual_length;//此 msg 實際成功發送的字節數 int status;//此 msg 的發送狀態,0:成功,負數,失敗 struct list_head queue;//此 msg 在所有 msg 中的鏈表節點 void *state;//此 msg 的私有數據};隊列化

SPI 數據傳輸可以有兩種方式:同步方式和異步方式。

同步方式:數據傳輸的發起者必須等待本次傳輸的結束,期間不能做其它事情,用代碼來解釋就是,調用傳輸的函數后,直到數據傳輸完成,函數才會返回。

異步方式:數據傳輸的發起者無需等待傳輸的結束,數據傳輸期間還可以做其它事情,用代碼來解釋就是,調用傳輸的函數后,函數會立刻返回而不用等待數據傳輸完成,我們只需設置一個回調函數,傳輸完成后,該回調函數會被調用以通知發起者數據傳送已經完成。

同步方式簡單易用,很適合處理那些少量數據的單次傳輸。但是對于數據量大、次數多的傳輸來說,異步方式就顯得更加合適。

對于 SPI 控制器來說,要支持異步方式必須要考慮以下兩種狀況:

對于同一個數據傳輸的發起者,既然異步方式無需等待數據傳輸完成即可返回,返回后,該發起者可以立刻又發起一個 message,而這時上一個message還沒有處理完。對于另外一個不同的發起者來說,也有可能同時發起一次message傳輸請求。

隊列化正是為了為了解決以上的問題,所謂隊列化,是指把等待傳輸的 message 放入一個等待隊列中,發起一個傳輸操作,其實就是把對應的 message 按先后順序放入一個等待隊列中,系統會在不斷檢測隊列中是否有等待傳輸的 message,如果有就不停地調度數據傳輸內核線程,逐個取出隊列中的 message 進行處理,直到隊列變空為止。SPI 通用接口層為我們實現了隊列化的基本框架。

spi_message 就是一次 SPI 數據交換的原子操作,不可打斷。

3、SPI 控制器驅動層

SPI 控制器驅動層負責最底層的數據收發,主要有以下功能:

申請必要的硬件資源,比如中斷、DMA 通道、DMA 內存緩沖區等等。配置 SPI 控制器的工作模式和參數,使之可以和相應的設備進行正確的數據交換。向通用接口層提供接口,使得上層的協議驅動可以通過通用接口層訪問控制器驅動。配合通用接口層,完成數據消息隊列的排隊和處理,直到消息隊列變空為止。

SPI 主機驅動就是 SOC 的 SPI 控制器驅動。Linux 內核使用 spi_master/spi_controller 表示 SPI 主機驅動,spi_master 是個結構體,定義在 include/linux/spi/spi.h 文件中。

SPI 主機驅動的核心就是申請 spi_master,然后初始化 spi_master,最后向 Linux 內核注冊 spi_master。

API 如下:

spi_alloc_master 函數:申請 spi_master。spi_master_put 函數:釋放 spi_master。spi_register_master函數:注冊 spi_master。spi_unregister_master 函數:注銷 spi_master。spi_bitbang_start函數:注冊 spi_master。spi_bitbang_stop 函數:注銷 spi_master。SPI 主機驅動的加載

以 MTK 為例,源碼來自于小米開源項目:

https://github.com/MiCode/Xiaomi_Kernel_OpenSource。

小米每做一個項目,都會把 kernel 部分開源,因為需要遵循 Linux GPL 開源協議。

【設備】聲明在設備樹中

kernel-4.14/arch/arm64/boot/dts/mediatek/mt6885.dts【驅動】

kernel-4.14/drivers/spi/spi-mt65xx.c。

匹配以后,probe 函數執行,申請 spi_master,初始化 spi_master,最后向 Linux 內核注冊 spi_master。

4、軟件流程

看懂該圖,對 SPI 驅動框架就有完整的了解了。

1、2、3 按順執行,首先有 spi 總線的注冊,然后是 spi 控制器驅動加載,然后是設備驅動加載。

區別在于,spi 控制器驅動加載時,是靠 platform 總線匹配設備(控制器)與驅動。spi 設備驅動加載時,是靠 spi 總線匹配設備(外設IC)與驅動。

init flowspi_register_master 的調用序列圖隊列化的工作機制及過程

當協議驅動程序通過 spi_async 發起一個 message 請求時,隊列化和工作線程被激活,觸發一些列的操作,最終完成 message 的傳輸操作。

spi_sync 與 spi_async 類似,只是有一個等待過程。

5、SPI 設備驅動

【設備】聲明在設備樹中。

注意:設備的聲明,slave device node 應該包含在你所要掛載的 &spi node 下,將 device 綁定在 master 上。然后通過 pinctrl 方式指定 GPIO,并在驅動中操作 pinctrl 句柄。

【驅動】demo

Linux 內核使用 spi_driver 結構體來表示 spi 設備驅動,我們在編寫 SPI 設備驅動的時候需要實現 spi_driver。spi_driver 結構體定義在 include/linux/spi/spi.h 文件中。

spi_register_driver:注冊 spi_driverspi_unregister_driver:銷掉 spi_driver

/* probe 函數 */static int xxx_probe(struct spi_device *spi){ /* 具體函數內容 */ return 0;}/* remove 函數 */static int xxx_remove(struct spi_device *spi){ /* 具體函數內容 */ return 0;}/* 傳統匹配方式 ID 列表 */static const struct spi_device_id xxx_id[] = { {"xxx", 0}, {}};/* 設備樹匹配列表 */static const struct of_device_id xxx_of_match[] = { { .compatible = "xxx" }, { /* Sentinel */ }};/* SPI 驅動結構體 */static struct spi_driver xxx_driver = { .probe = xxx_probe, .remove = xxx_remove, .driver = { .owner = THIS_MODULE, .name = "xxx", .of_match_table = xxx_of_match, }, .id_table = xxx_id,};/* 驅動入口函數 */static int __init xxx_init(void){ return spi_register_driver(&xxx_driver);}/* 驅動出口函數 */static void __exit xxx_exit(void){ spi_unregister_driver(&xxx_driver);}module_init(xxx_init);module_exit(xxx_exit);

在驅動入口函數中調用 spi_register_driver 來注冊 spi_driver。

在驅動出口函數中調用 spi_unregister_driver 來注銷 spi_driver。

spi 讀寫數據demo。

/* SPI 多字節發送 */static int spi_send(struct spi_device *spi, u8 *buf, int len){ int ret; struct spi_message m; struct spi_transfer t = { .tx_buf = buf, .len = len, }; spi_message_init(&m); /* 初始化 spi_message */ spi_message_add_tail(t, &m);/* 將 spi_transfer 添加到 spi_message 隊列 */ ret = spi_sync(spi, &m); /* 同步傳輸 */ return ret;}/* SPI 多字節接收 */static int spi_receive(struct spi_device *spi, u8 *buf, int len){ int ret; struct spi_message m; struct spi_transfer t = { .rx_buf = buf, .len = len, }; spi_message_init(&m); /* 初始化 spi_message */ spi_message_add_tail(t, &m);/* 將 spi_transfer 添加到 spi_message 隊列 */ ret = spi_sync(spi, &m); /* 同步傳輸 */ return ret;}

除了 init、exit、probe、remove、read、write 函數外,其他的函數看需求實現,這幾個是最基本的。

6、總結

Linux 是 總線、設備、驅動 的框架,理解了這個框架,就能理解所有的模塊驅動框架。

SPI 驅動比 I2C 驅動還是簡單很多的。

關鍵詞: 驅動程序 通用接口 數據傳輸 異步方式 數據結構

上一篇:

下一篇:

X 關閉

X 關閉

久久国产精品一区二区三区四区,久色婷婷小香蕉久久,国产日韩欧美在线播放不卡,另类av一区二区
韩国成人精品a∨在线观看| 亚洲精品免费一区二区三区| 国产精品综合| 欧美久久综合| 亚洲国产精品成人精品| 亚洲精品综合精品自拍| 国产精品永久免费视频| 国产精品狠色婷| 欧美在线视频观看免费网站| 久久精品国产亚洲5555| 国产精品久久亚洲7777| 欧美午夜一区| 亚洲在线一区二区| 国产日韩久久| 国产精品一区二区久激情瑜伽| 国产日韩精品电影| 欧美激情第8页| 亚洲无线一线二线三线区别av| 免费高清在线视频一区·| 欧美成人精精品一区二区频| 国产综合色产在线精品| 99国产精品久久久久久久成人热| 欧美国内亚洲| 国产精品视频久久| 在线成人激情| 欧美电影资源| 国产精品一二一区| 美国成人直播| 亚洲影院一区| 欧美视频在线免费| 久久深夜福利| 亚洲区中文字幕| 麻豆成人精品| 国产午夜精品久久久久久免费视| 一区精品在线播放| 国产精品九色蝌蚪自拍| 亚洲电影免费在线观看| 国产精品一区二区在线| 久久综合九色欧美综合狠狠| 亚洲视频图片小说| 奶水喷射视频一区| 99视频+国产日韩欧美| 欧美福利视频在线观看| 亚洲香蕉视频| 亚洲国产精品悠悠久久琪琪| 亚洲综合色激情五月| 亚洲综合丁香| 免费一级欧美片在线观看| 亚洲视频在线一区| 久久综合九色99| 国产精品福利在线观看| 亚洲一区二区久久| 免费欧美高清视频| 在线观看日韩av电影| 国产欧美精品一区二区三区介绍| 欧美韩国日本综合| 欧美日韩精品久久久| 亚洲欧美日本日韩| 亚洲精品一二| 雨宫琴音一区二区在线| 久久视频在线看| 国模吧视频一区| 亚洲午夜一区二区三区| 性欧美暴力猛交另类hd| 韩国v欧美v日本v亚洲v| 一区二区三区在线观看国产| 欧美性感一类影片在线播放| 性做久久久久久久久| 午夜精品久久99蜜桃的功能介绍| 亚洲精品影院| 亚洲专区在线视频| 影音先锋一区| 欧美国产1区2区| 久久精品日韩| 亚洲一区二区三区高清| 国产精品久久久久毛片大屁完整版| 亚洲福利视频一区| 欧美午夜精品久久久久免费视| 久久精品女人| 久久成人一区二区| 韩日视频一区| 久久精品国产亚洲一区二区| 久久伊人精品天天| 亚洲小视频在线观看| 亚洲在线电影| 欧美在线播放| 亚洲欧美日韩成人高清在线一区| 亚洲福利久久| 亚洲成人在线观看视频| 亚洲女同同性videoxma| 亚洲激情影视| 在线视频精品一区| 久久国产福利| 免费视频一区二区三区在线观看| 欧美日韩国产成人在线观看| 亚洲精品视频中文字幕| 国产女主播视频一区二区| 亚洲综合色噜噜狠狠| 亚洲乱码国产乱码精品精可以看| 99精品国产热久久91蜜凸| 一区二区三区日韩精品| 亚洲欧美高清| 久久久久国产精品午夜一区| 欧美综合国产精品久久丁香| 亚洲国产1区| 亚洲成人在线观看视频| 国产精品免费一区豆花| 亚洲国产精品第一区二区| 欧美韩日一区| 亚洲小视频在线观看| 日韩视频免费观看高清在线视频| 亚洲成人在线网站| 欧美一区二区三区四区夜夜大片| 国产精品日本欧美一区二区三区| 亚洲激情在线播放| 欧美日韩麻豆| 亚洲激情一区| 亚洲图片自拍偷拍| 亚洲第一色中文字幕| 国产精品二区二区三区| 国产一区视频在线观看免费| 欧美电影在线观看完整版| 国产精品麻豆欧美日韩ww| 国产综合色在线视频区| 欧美视频在线观看视频极品| 欧美日韩日日骚| 亚洲欧美日韩精品久久久久| 欧美成人情趣视频| 国产精品夜夜夜一区二区三区尤| 国产精品一级久久久| 久久久久久久久伊人| 亚洲香蕉在线观看| 欧美日产在线观看| 国产女人水真多18毛片18精品视频| 免播放器亚洲一区| 亚洲第一页自拍| 亚洲欧美成人综合| 久久视频一区二区| 美日韩精品免费观看视频| 亚洲欧美日韩另类| 亚洲网站在线观看| 久久久亚洲精品一区二区三区| 国产一区白浆| 久久精品国产999大香线蕉| 在线观看欧美视频| 亚洲精品欧美极品| 欧美精品一区二区三区在线看午夜| 欧美一乱一性一交一视频| 韩日在线一区| 久久综合狠狠综合久久激情| 亚洲国产成人在线| 狠狠色综合色综合网络| 欧美精品一区视频| 国产精品第十页| 久久精选视频| 国产精品xnxxcom| 国产日韩欧美自拍| 99一区二区| 欧美成人午夜影院| 欧美午夜影院| 欧美 日韩 国产精品免费观看| 久久久夜精品| 国产精品久99| 91久久精品日日躁夜夜躁国产| 免费一级欧美片在线观看| 狂野欧美性猛交xxxx巴西| 亚洲欧美变态国产另类| 亚洲美女免费精品视频在线观看| 欧美成人在线影院| 国产一区二区成人| 欧美亚洲一区二区三区| 欧美专区中文字幕| 激情伊人五月天久久综合| 国产精品久久久久国产精品日日| 国产精品视频在线观看| 日韩视频在线观看国产| 亚洲高清免费| 亚洲裸体在线观看| 国产欧美二区| 欧美午夜不卡在线观看免费| 欧美成人精品h版在线观看| 久久久久9999亚洲精品| 欧美美女操人视频| 国产综合色在线视频区| 久久成人综合网| 久久在线免费观看视频| 夜夜嗨av色一区二区不卡| 精品96久久久久久中文字幕无| 国产精品不卡在线| 国语自产在线不卡| 亚洲一级黄色| 欧美性久久久| 亚洲午夜一二三区视频| 久久久777| 国产婷婷色综合av蜜臀av| 欧美亚洲免费| 亚洲国产精品久久久久秋霞影院| 亚洲国产精品va在线看黑人| 黄色成人片子| 激情一区二区三区|