Linux SPI总线和设备驱动架构之二:SPI通用接口层
通過(guò)上一篇文章的介紹,我們知道,SPI通用接口層用于把具體SPI設(shè)備的協(xié)議驅(qū)動(dòng)和SPI控制器驅(qū)動(dòng)聯(lián)接在一起,通用接口層除了為協(xié)議驅(qū)動(dòng)和控制器驅(qū)動(dòng)提供一系列的標(biāo)準(zhǔn)接口API,同時(shí)還為這些接口API定義了相應(yīng)的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)一部分是SPI設(shè)備、SPI協(xié)議驅(qū)動(dòng)和SPI控制器的數(shù)據(jù)抽象,一部分是為了協(xié)助數(shù)據(jù)傳輸而定義的數(shù)據(jù)結(jié)構(gòu)。另外,通用接口層還負(fù)責(zé)SPI系統(tǒng)與Linux設(shè)備模型相關(guān)的初始化工作。本章的我們就通過(guò)這些數(shù)據(jù)結(jié)構(gòu)和API的討論來(lái)對(duì)整個(gè)通用接口層進(jìn)行深入的了解。
/*****************************************************************************************************/
聲明:本博內(nèi)容均由http://blog.csdn.net/droidphone原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處,謝謝!
/*****************************************************************************************************/
SPI通用接口層的代碼集中在:/drivers/spi/spi.c中。
SPI設(shè)備模型的初始化
通常地,根據(jù)linux設(shè)備模型的組織方式,各種設(shè)備會(huì)掛在合適的總線上,設(shè)備驅(qū)動(dòng)和設(shè)備通過(guò)總線互相進(jìn)行匹配,使得設(shè)備能夠找到正確的驅(qū)動(dòng)程序進(jìn)行控制和驅(qū)動(dòng)。同時(shí),性質(zhì)相似的設(shè)備可以歸為某一個(gè)類(lèi)的設(shè)備,它們具有某些共同的設(shè)備屬性,在設(shè)備模型上就是所謂的class。SPI設(shè)備也不例外,它們也遵循linux的設(shè)備模型的規(guī)則:
[cpp]?view plaincopy
- sys/bus/spi
- sys/class/spi_master
spi_master結(jié)構(gòu)
SPI控制器負(fù)責(zé)按照設(shè)定的物理信號(hào)格式在主控和spi設(shè)備之間交換數(shù)據(jù),SPI控制器數(shù)據(jù)是如何被傳輸?shù)?#xff0c;而不關(guān)心數(shù)據(jù)的內(nèi)容。SPI通用接口層用spi_master結(jié)構(gòu)來(lái)表示一個(gè)spi控制器,我們看看它的主要字段的意義:
| struct device ? dev | spi控制器對(duì)應(yīng)的device結(jié)構(gòu) |
| struct list_head list | 系統(tǒng)可能有多個(gè)控制器,用該鏈表鏈接在一個(gè)全局鏈表變量上 |
| s16 ? ? ? ? ? ? bus_num | 該控制器對(duì)應(yīng)的spi總線編號(hào),從0開(kāi)始,通常由板級(jí)代碼設(shè)定 |
| u16 ? ? ? ? ? ? num_chipselect | 連接到該spi控制器的片選信號(hào)的個(gè)數(shù) |
| u16 ? ? ? ? ? ? mode_bits | 工作模式,由驅(qū)動(dòng)解釋該模式的意義 |
| u32 ? ? ? ? ? ? min_speed_hz | 最低工作時(shí)鐘 |
| u32 ? ? ? ? ? ? max_speed_hz | 最高工作時(shí)鐘 |
| u16 ? ? ? ? ? ? flags | 用于設(shè)定某些限制條件的標(biāo)志位 |
| int ? ? ? ? ? ? (*setup)(struct spi_device *spi) | 回調(diào)函數(shù),用于設(shè)置某個(gè)spi設(shè)備在該控制器上的工作參數(shù) |
| int ? ? ? ? ? ? (*transfer)(......) | 回調(diào)函數(shù),用于把包含數(shù)據(jù)信息的mesg結(jié)構(gòu)加入控制器的消息鏈表中 |
| void ? ? ? ? ? (*cleanup)(struct spi_device *spi) | 回調(diào)函數(shù),當(dāng)spi_master被釋放時(shí),該函數(shù)被調(diào)用 |
| struct kthread_worker ? kworker | 用于管理數(shù)據(jù)傳輸消息隊(duì)列的工作隊(duì)列線程 |
| struct kthread_work ? ? pump_messages | 具體實(shí)現(xiàn)數(shù)據(jù)傳輸隊(duì)列的工作隊(duì)列 |
| struct list_head ? ? ? ?queue | 該控制器的消息隊(duì)列,所有等待傳輸?shù)南㈥?duì)列掛在該鏈表下 |
| struct spi_message ? ? ?*cur_msg | 當(dāng)前正帶處理的消息隊(duì)列 |
| int (*prepare_transfer_hardware)(......) | 回調(diào)函數(shù),正式發(fā)起傳送前會(huì)被調(diào)用,用于準(zhǔn)備硬件資源 |
| int (*transfer_one_message)(......) | 單個(gè)消息的原子傳送回調(diào)函數(shù),隊(duì)列中的每個(gè)消息都會(huì)調(diào)用一次該回調(diào)來(lái)完成傳輸工作 |
| int (*unprepare_transfer_hardware)(......) | 清理回調(diào)函數(shù) |
| int ? ? ? ? ? ? ? ? ? ? *cs_gpios | 片選信號(hào)所用到的gpio |
| ? | ? |
spi_master結(jié)構(gòu)通常由控制器驅(qū)動(dòng)定義,然后通過(guò)以下通用接口層的API注冊(cè)到系統(tǒng)中:
- int spi_register_master(struct spi_master *master);
spi_device結(jié)構(gòu)
SPI通用接口層用spi_device結(jié)構(gòu)來(lái)表示一個(gè)spi設(shè)備,它的各個(gè)字段的意義如下:
| struct device ? ? ? ? ? dev | 代表該spi設(shè)備的device結(jié)構(gòu) |
| struct spi_master ? ? ? *master | 指向該spi設(shè)備所使用的控制器 |
| u32 ? ? max_speed_hz | 該設(shè)備的最大工作時(shí)鐘頻率 |
| u8 ? ? ?chip_select | 在控制器中的片選引腳編號(hào)索引 |
| u16 ? ? mode | 設(shè)備的工作模式,包括時(shí)鐘格式,片選信號(hào)的有效電平等等 |
| u8 ? ? ?bits_per_word | 設(shè)備每個(gè)單位數(shù)據(jù)所需要的比特?cái)?shù) |
| int ? ? irq | 設(shè)備使用的irq編號(hào) |
| char ? ?modalias[SPI_NAME_SIZE] | 該設(shè)備的名字,用于spi總線和驅(qū)動(dòng)進(jìn)行配對(duì) |
| int ? ? cs_gpio | 片選信號(hào)的gpio編號(hào),通常不用我們自己設(shè)置,接口層會(huì)根據(jù)上面的chip_select字段在spi_master結(jié)構(gòu)中進(jìn)行查找并賦值 |
要完成向系統(tǒng)增加并注冊(cè)一個(gè)SPI設(shè)備,我們還需要另一個(gè)數(shù)據(jù)結(jié)構(gòu):
[cpp]?view plaincopy
- struct spi_device *spi_new_device(struct spi_master *master,?struct spi_board_info *chip);
- int spi_register_board_info(struct spi_board_info const *info, unsigned n);
spi_driver結(jié)構(gòu)
根據(jù)linux的設(shè)備模型,有device就必定有driver與之對(duì)應(yīng),上一節(jié)介紹的spi_device結(jié)構(gòu)中內(nèi)嵌了device結(jié)構(gòu)字段dev,同樣地,代表驅(qū)動(dòng)程序的spi_driver結(jié)構(gòu)也內(nèi)嵌了device_driver結(jié)構(gòu):
[cpp]?view plaincopy
[cpp]?view plaincopy
需要注意的是,這里的spi_driver結(jié)構(gòu)代表的是具體的SPI協(xié)議驅(qū)動(dòng)程序。
spi_message和spi_transfer結(jié)構(gòu)
要完成和SPI設(shè)備的數(shù)據(jù)傳輸工作,我們還需要另外兩個(gè)數(shù)據(jù)結(jié)構(gòu):spi_message和spi_transfer。spi_message包含了一個(gè)的spi_transfer結(jié)構(gòu)序列,一旦控制器接收了一個(gè)spi_message,其中的spi_transfer應(yīng)該按順序被發(fā)送,并且不能被其它spi_message打斷,所以我們認(rèn)為spi_message就是一次SPI數(shù)據(jù)交換的原子操作。下面我們看看這兩個(gè)數(shù)據(jù)結(jié)構(gòu)的定義:
[cpp]?view plaincopy
[cpp]?view plaincopy
通用接口層為我們提供了一系列用于操作和維護(hù)spi_message和spi_transfer的API函數(shù),這里也列一下。
用于初始化spi_message結(jié)構(gòu):
- void spi_message_init(struct spi_message *m);
- void?spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);
- ?void?spi_transfer_del(struct spi_transfer *t);
- void?spi_message_init_with_transfers(struct spi_message *m,?struct spi_transfer *xfers, unsigned int num_xfers);
- struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags);
- 異步版本 ? ?int spi_async(struct spi_device *spi, struct spi_message *message);
- 同步版本 ? ?int spi_sync(struct spi_device *spi, struct spi_message *message);
總結(jié)一下,協(xié)議驅(qū)動(dòng)發(fā)送數(shù)據(jù)的流程大致是這樣的:
- 定義一個(gè)spi_message結(jié)構(gòu);
- 用spi_message_init函數(shù)初始化spi_message;
- 定義一個(gè)或數(shù)個(gè)spi_transfer結(jié)構(gòu),初始化并為數(shù)據(jù)準(zhǔn)備緩沖區(qū)并賦值給spi_transfer相應(yīng)的字段(tx_buf,rx_buf等);
- 通過(guò)spi_message_init函數(shù)把這些spi_transfer掛在spi_message結(jié)構(gòu)下;
- 如果使用同步方式,調(diào)用spi_sync(),如果使用異步方式,調(diào)用spi_async();
另外,通用接口層也為一些簡(jiǎn)單的數(shù)據(jù)傳輸提供了獨(dú)立的API來(lái)完成上述的組合過(guò)程:
- int spi_write(struct spi_device *spi, const void *buf, size_t len); ? ?---- ? ?同步方式發(fā)送數(shù)據(jù)。
- int?spi_read(struct spi_device *spi, void *buf, size_t len); ? ?---- ? ?同步方式接收數(shù)據(jù)。
- int spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers, unsigned int num_xfers); ? ?---- ? 同步方式,直接傳送數(shù)個(gè)spi_transfer,接收和發(fā)送。
- int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx); ? ?---- ? ?先寫(xiě)后讀。
- ssize_t spi_w8r8(struct spi_device *spi, u8 cmd); ? ?---- ? ?寫(xiě)8位,然后讀8位。
- ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);?? ?---- ? ?寫(xiě)8位,然后讀16位。
總結(jié)
以上是生活随笔為你收集整理的Linux SPI总线和设备驱动架构之二:SPI通用接口层的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux SPI总线和设备驱动架构之一
- 下一篇: Linux SPI总线和设备驱动架构之三