上一章我們簡要說明了IIO子系統的架構,本章我們通過數據結構的定義,分析IIO子系統的設計實現,本章的主要內容如下:
一、IIO子系統各數據結構說明
二、數據結構間的關聯說明
一、IIO子系統各數據結構說明
在上一章我們大概說明了IIO子系統的框架,IIO子系統大概包含幾個主要的部分,此處我們再說明一下:
- 對於連續數據採集相關功能,主要由iio buffer實現;
- 連續數據採集的觸發機制,主要由iio trigger實現;
- Iio device的事件觸發機制,主要由iio event實現;
- 提供單次原始數據的採集功能,主要通過syfs屬性文件實現。
基本就是這些內容,iio子系統主要藉助字符設備文件以及sysfs屬性文件實現數據的獲取與參數設定等操作。而在iio子系統中,主要包括如下幾個數據結構:
- struct iio_dev,描述一個iio device
- struct iio_event_interface,描述iio device的事件觸發模塊的數據結構;
- struct iio_buffer,描述iio device連續數據採集功能相關的數據結構;
- struct iio_trigger,描述iio device的trigger機制相關的數據結構
- struct iio_chan_spec,描述iio device的一個通道的屬性信息;
- struct iio_info,描述iio device各通道的原始數據讀取接口、event使能與event參數讀寫相關接口、trigger有效性檢測接口、設備樹節點解析等接口;
- struct iio_buffer_setup_ops,描述iio buffer使能與否的接口(建立iio buffer與iio tigger的關聯,從而保證iio trigger觸發後,可將數據刷新到對應的iio buffer中);
- struct iio_chan_spec_ext_info,描述一個channel擴展屬性相關的信息,包括屬性名稱、讀寫接口等
- struct iio_trigger_ops,表示iio trigger的操作接口,包括設置trigger的狀態(使能與否)、重新使能trigger、設備有效性判斷等接口;
- struct iio_buffer_access_funcs,描述iio buffer的access接口,包括數據寫入到iio buffer的緩存、緩存數據是否有效、從緩存中讀取數據等等接口。
基本上就是這些數據結構,iio子系統的數據結構比較多,但是數據結構間的關聯並不太複雜,主
要就是數據結構太多了。下面我們對每一個數據結構進行簡要說明
struct iio_dev
該數據結構表示一個iio device,它屬於IIO子系統中的核心數據結構,它負責將所有的IIO子系統的數據結構關聯起來,下面即是該數據結構主要的內容,下面我們分幾部分進行說明。
- modes與currentmode表示該iio device支持的模式及當前所處的模式,目前支持的模式如下,DIRECT_MODE表示不對採集數據進行緩存,可直接讀取單次的數據(可通過訪問sysfs下的屬性文件方式,讀取數據);INDIO_BUFFER_XXX表示支持對iio device採集數據進行緩存的模式,可理解為採集連續的數據(這些數據則需要通過訪問字符設備文件進行讀取);INDIO_EVENT_TRIGGRED則主要表示事件觸發功能,如針對溫度傳感器可監控當前溫度是否超過溫度告警上限或下限,當出現溫度告警後則向SOC發送中斷信號,若支持這類功能則增加INDIO_EVENT_TRIGGRED模式的支持即可(事件告警信息也是通過訪問字符設備文件讀取,但這個字符設備文件有點特殊,其是一個匿名文件)。
/* Device operating modes */
define INDIO_BUFFER_TRIGGERED 0x02
define INDIO_BUFFER_HARDWARE 0x08
#define INDIO_EVENT_TRIGGERED 0x10
- dev則主要藉助系統的設備驅動模型,實現對iio device的引用計數,並綁定至iio總線上,同時藉助設備驅動模型可在sysfs目錄下創建該iio dev的目錄,並創建該iio device所有屬性文件等等;
- event_interface表示event事件相關的數據結構,該數據結構內部包含一個kfifo,存儲iio device push的event信息;
- buffer表示該iio device對應的iio buffer,若系統不支持buffer模式,則無需創建該buffer;
- buffer_list則在一個iio buffer enable時,將active iio buffer加入到該鍊表中(目前基本上即將iio_dev->buffer添加到該鍊表上);
- scan_bytes表示單次採集數據的長度,該值主要根據當前active channel的個數、每一個通道採集數據的長度計算而得;
- available_scan_masks表示當前iio device可使用的channel的掩碼(如當前由8個通道,僅前四個通道可用,則可以設置available_scan_maks值為{0x0F}),而active_scan_mask則表示當前已enable的channelmask,該mask是available_scan_masks的子集
scan_bytes、available_scan_masks、active_scan_mask主要由iio buffer使用,scan_bytes是單次採集數據的長度,因此通過字符設備文件讀取buffer採集數據時,傳遞的內存長度至少應為scan_bytes;
- scan_timestamp、scan_index_timestamp主要對於通過buffer採集的數據是否需要時間戳,如果需要對採集的數據增加時間戳,則增加IIO_TIMESTAMP類型的虛擬channel,主要用於對採集的數據增加時間戳;
- trig表示一個trigger,針對event、buffer而言,均需要結合trigger機製作為數據可採集的信號,一般在trigger中將event信息、數據信息刷新到event的kfifo或buffer中去(不過目前event信息一般並沒有使用trigger機制,大多數event信息均是在event irq的中斷處理函數中push到event 的kfifo中,雖然IIO子系統設計上期望通過trigger將數據push到buffer或event上去,但event信息一般並不是連續事件,且trigger內部又實現了虛擬的irq chip,而在虛擬irq的中斷處理函數中實現數據push到buffer或event的kfifo中。顯然對於event信息處理而言,若使用trigger機制,則多了一個虛擬中斷的觸發與處理操作,也沒有必要啊,因此現有系統中基本上沒有使用trigger機制將event信息push到event kfifo的驅動);
- pollfunc、pollfunc_event則為buffer、event的中斷處理函數的接口信息(使用的中斷即為trigger中virtual irq chip註冊的irq),現在僅需要關注pollfunc即可,基本上沒有使用pollfunc_event的;
- channels是該iio device所有channel相關的參數信息,我們在iio_chan_spec中將詳細說明;
- channel_attr_list鍊表包含了IIO子系統為所有channel創建的動態屬性(針對hwmon子系統我們之前也分析過,其主要通過在sysfs下創建屬性文件實現與hwmon device的通信,而IIO子系統也類似,其也在sysfs下創建屬性文件實現與iio device的通信,而channel_attr_lis則主要是channel相關的屬性的集合);
- groups中包含了所有的group指針,包括channel、event、buffer子模塊創建的group,而在調用device_add將該iio device對應的struct device類型變量註冊到設備驅動模型子系統中,即遍歷該數組,創建屬性文件或目錄。
- setup_ops則主要是建立buffer與trigger的關聯,在該ops中的enable接口中,主要是申請trigger的virt irq chip提供的中斷及中斷處理函數;在該ops的disable接口中則釋放中斷。

struct iio_event_interface
該數據結構主要是對event子模塊的定義,其中:
- 等待隊列wait,當應用程式讀取觸發事件信息時,若當前無數據可讀,則將當前進程加入到該等待隊列,待調用iio_push_event將觸發事件信息加入kfifo後,則wakeup該隊列中的進程;
- 定義kfifo,存儲所有觸發的事件信息,供應用程式獲取;
- 將even子模塊動態定義的event attribute均添加至該鍊表中(屬性名稱格式為{iio_dir}_{iio_channel_type}{channel-Index/channel_modify}_{ev_type}_{ev_dir}_{ev_info});
- flags標記該event是否已使能(即應用程式是否通過ioctl調用創建一個匿名fd,若使能則置位IIO_BUSY_BIT_POS)
struct iio_buffer與struct iio_buffer_setup_ops
iio_buffer主要是用於存儲連續採集數據的緩存,其主要包括兩個主要的數據結構struct iio_buffer、struct iio_buffer_access_func(其實是三個數據結構,還有數據結構struct iio_kfifo,其內部包含struct iio_buffer類型變量和struct kfifo類型變量用於緩存數據)。
針對struct iio_buffer主要包括如下幾個方面的內容:
- iio_buffer緩存數據的個數(即length);
- iio_buffer每一次採集數據的長度(bytes_per_datum,而bytes_per_datum*length即為kfifo存儲數據的內存空間大小);
- Scan_el_dev_attr_list主要用於將所有iio_buffer子模塊創建的屬性變量集合在一起(iio_buffer);
- scan_el_attrs存儲各設備驅動自行定義的靜態屬性(生成的屬性文件在scan_elements子目錄下);
- attrs也是存儲存儲各設備驅動自行定義的靜態屬性(該變量定義的屬性文件在buffer子目錄下);
- buffer_group、scan_el_group包含iio buffer子模塊下所有屬性,其中buffer_group裡的屬性均在buffer子目錄下創建對應的屬性文件;scan_el_group裡的屬性均在scan_elements子目錄下創建對應的屬性文件;
- pollq為等待隊列,主要為iio device的字符設備文件使用(該字符設備文件對應的讀接口和poll接口使用,當buffer中不存在數據時則sleep在該等待隊列中);
- watermark為緩存多少個數據後,喚醒pollq(實際內存空間大小為watermark*bytes_per_datum)。
針對struct iio_buffer_access_funcs則是該iio_buffer對應的緩存空間的訪問訪問,目前使用kfifo緩存數據,則其訪問方法為iio_store_to_kfifo、iio_read_first_n_kfifo等,主要是將數據存儲至kfifo或從kfifo中取出緩存數據等
struct iio_trigger與struct iio_trigger_ops
這兩個數據結構主要實現iio 的trigger機制,類似於led子系統的led trigger。主要內容如下:
- id表示trigger的id、name為名稱;
- 該iio trigger也使用struct device類型的變量加入到iio總線上,iio trigger與iio device均註冊到iio總線上,因此它們在sysfs目錄下是同級的;
- list用於將struct iio trigger添加系統全局鍊表iio_trigger_list中;
- alloc_list主要用於同一類型的trigger可註冊多個trigger實例的請求,如trigger-period則使用該變量將trigger插入到iio_prtc_trigger_list中,目前使用這一變量的trigger並不多;
- 使用計數use_count;
- 而subirq_chip、subirq_base、subirqs、pool則主要用於創建虛擬的irq chip,在trigger內部,當多個trigger consumer註冊時,則trigger內部會為其分配一個虛擬的irq,並根據trigger consumer提供給pollfunc,為該irq註冊中斷處理函數,這樣當該trigger觸發後,則會遍歷所有該trigger上已註冊的虛擬irq,調用其中斷處理函數從而執行trigger consumer提供的處理函數(關於linux中斷子系統的內容可參考我之前寫的中斷子系統專欄,我在中斷子系統專欄也實現了一個虛擬的irq chip,實現的原理和此處trigger實現的虛擬irq chip的原理是一樣的)。
iio trigger也提供了操作接口,set_trigger_state主要設置trigger的狀態(使能與否)、reenable接口(try_reenable),validate_device(如實現的trigger只允許父device相同的iio device綁定,則可以實現該接口進行限制操作)
struct iio_chan_spec
該數據結構主要說明iio device一個channel的信息,主要涉及struct iio_chan_spec、struct iio_event_spec、struct iio_chan_spec_ext_info
針對struct iio_chan_spec主要涉及如下內容:
- 該channel的類型,channel類型的定義為等等enum iio_chan_type,包括IIO_TEMP、IIO_VOLTAGE等;
- channel表示該channel的index,當indexed為1時,才使用該index表示channel裡的屬性參數;
- channel2表示channel的別稱,當modified為1時,則使用該index對應的string描述channel的別稱(如針對三軸陀螺儀而言,如果還使用channel0、channel1進行識別的話不好區分,可使用modified識別為channelX、channelY、channelZ等)
- info_mask_separate表示channel的某一個屬性為channel專屬的
- Shared_by_type則表示該iio device下所有相同類型channel所共享的屬性;
- Shared_by_dir則表示該iio device下所有相同方向channel所共享的屬性;
- Shared_by_all則表示該iio device下所有channel所共享的屬性
- scan_index、scan_type則表示採集數據的index及數據的類型等(這兩個變量主要由buffer使用)
- event_spec定義event相關的信息;
該數據結構定義的變量,主要用於創建channel相關的屬性參數,命名規則為{iio_dir}_{iio_channel_type}{channel-Index/channel_modify}_xxx。
而struct iio_event_spec主要內容如下:
- 該event的type、dir,而mask_separate等參數與iio_chan_spec中的info_mask_xxx的意義類似;而該數據結構體也主要用於創建該channel中event相關的屬性參數,屬性參數在sysfs文件系統創建的屬性文件的名稱規則為{iio_dir}_{iio_channel_type}{channel-Index/channel_modify}_{ev_type}_{ev_dir}_{ev_info})
struct iio_info
該數據結構主要定義了通過syfs讀寫channel屬性的接口,其中read_raw、write_raw可用於讀取通道的raw數據等;而write_event_value則主要用於event事件觸發的閾值參數的設置與讀取等、而read_event_config、write_event_config則可以用於實現event的使能與否;而event_attrs、attrs則主要用於設備驅動自定義的屬性參數(包括event屬性參數以及iio device相關的屬性參數)
二、數據結構間的關聯說明
如下為上述這些數據結構間的關聯圖,通過struct iio_dev,將所有定義的數據結構關聯起來,這些數據結構基本上是在iio_device_register時完成關聯的。藉助這種數據結構間的關聯也方便我們較好的理解該子系統的設計實現。
以上即是本章的主要內容,對於iio子系統而言,其定義的數據結構種類雖然多,但大多數數據結構主要用於創建sysfs的attribute而設計的,而真正體現到架構設計部分的設計,主要就是trigger、buffer、event這幾個方面,而我認為設計的比較好的就是其trigger機制中,每一個trigger均實現一個虛擬irq chip,對於所有attach到該trigger上的consumer均為其分配虛擬的irq並註冊對應的中斷處理函數,當一個trigger觸發後,則通過generic_handle_irq觸發所有已註冊的虛擬中斷的處理操作。下一章我們就介紹iio trigger的設計。