Triggered buffer support觸發緩衝支持 在許多數據分析應用中,能夠基於某些外部信號(觸發器)捕獲數據是比較有用的。 這些觸發器可能是: 數據就緒信號 連接到某個外部系統的IRQ線路(GPIO或其他) 處理器周期性中斷 用戶空間在sysfs中讀/寫特定文件 數據就緒信號 連接到某 ...
Triggered buffer support觸發緩衝支持
在許多數據分析應用中,能夠基於某些外部信號(觸發器)捕獲數據是比較有用的。 這些觸發器可能是:
-
- 數據就緒信號
- 連接到某個外部系統的IRQ線路(GPIO或其他)
- 處理器周期性中斷
- 用戶空間在sysfs中讀/寫特定文件
IIO設備驅動程式與觸發器完全無關。 觸發器可以初始化一個或多個設備上的數據捕獲。 這些觸發器用於填充緩衝區,然後作為字元設備暴露給用戶空間。
可以開發一個自己的觸發驅動程式,但這超出了本書的範圍。 我們將嘗試僅關註現有的。 這些是:
- iio-trig-interrupt:這為使用任何IRQ作為IIO觸發器提供了支持。 在舊的內核版本中,它曾經是iio-trig-gpio。 啟用此觸發模式的內核選項是CONFIG_IIO_INTERRUPT_TRIGGER。 如果構建為模塊,則該模塊將被稱為iio-trig-interrupt。
- iio-trig-hrtimer:這提供了一個基於頻率的IIO觸發器,使用HRT作為中斷源(因為內核v4.5)。 在較舊的內核版本中,它曾經是iio-trig-rtc。 負責此觸發模式的內核選項是IIO_HRTIMER_TRIGGER。 如果構建為模塊,則該模塊將被稱為iio-trig-hrtimer。
- iio-trig-sysfs:這允許我們使用sysfs條目來觸發數據捕獲。 CONFIG_IIO_SYSFS_TRIGGER是添加此觸發模式支持的內核選項。
- iio-trig-bfin-timer:這允許我們使用blackfin定時器作為IIO觸發器(仍然在staging文件夾中)。
利用IIO公開的API,我們可以:
- 聲明任何給定數量的觸發器
- 選擇將其數據推入緩衝區的通道
當您的IIO設備支持觸發緩衝區時,您必須設置iio_dev.pollfunc,它在觸發器觸發時執行。 此處理程式負責通過indio_dev-> active_scan_mask查找已啟用的通道,檢索其數據,並使用iio_push_to_buffers_with_timestamp函數將它們提供給indio_dev-> buffer。 因此,緩衝區和觸發器需要在IIO子系統中連接。
IIO核心提供了一組輔助函數來設置觸發緩衝區,可以在drivers / iio / industrialio-triggered-buffer.c中找到。
以下是從驅動程式中支持觸發緩衝區的步驟:
1.如果需要,填寫iio_buffer_setup_ops結構:
1 const struct iio_buffer_setup_ops sensor_buffer_setup_ops = { 2 .preenable = my_sensor_buffer_preenable, 3 .postenable = my_sensor_buffer_postenable, 4 .postdisable = my_sensor_buffer_postdisable, 5 .predisable = my_sensor_buffer_predisable, 6 };
2. 寫下與觸發器關聯的上半部分。 在99%的情況下,只需提供與捕獲相關的時間戳:
1 irqreturn_t sensor_iio_pollfunc(int irq, void *p) 2 { 3 pf->timestamp = iio_get_time_ns((struct indio_dev *)p); 4 return IRQ_WAKE_THREAD; 5 }
3. 寫入觸發器下半部分,它將從每個啟用的通道獲取數據,並將它們提供給緩衝區:
1 irqreturn_t sensor_trigger_handler(int irq, void *p) 2 { 3 u16 buf[8]; 4 int bit, i = 0; 5 struct iio_poll_func *pf = p; 6 struct iio_dev *indio_dev = pf->indio_dev; 7 8 /* one can use lock here to protect the buffer */ 9 /* mutex_lock(&my_mutex); */ 10 /* read data for each active channel */ 11 for_each_set_bit(bit, indio_dev->active_scan_mask, 12 indio_dev->masklength) 13 buf[i++] = sensor_get_data(bit) 14 15 /* 16 * If iio_dev.scan_timestamp = true, the capture timestamp 17 * will be pushed and stored too, as the last element in the 18 * sample data buffer before pushing it to the device buffers. 19 */ 20 iio_push_to_buffers_with_timestamp(indio_dev, buf, timestamp); 21 22 /* Please unlock any lock */ 23 24 /* mutex_unlock(&my_mutex); */ 25 26 /* Notify trigger */ 27 28 iio_trigger_notify_done(indio_dev->trig); 29 return IRQ_HANDLED; 30 }
4. 最後,在probe函數中,必須在使用iio_device_register()註冊設備之前連接觸發器和緩衝區:
iio_triggered_buffer_setup(indio_dev, sensor_iio_polfunc,
sensor_trigger_handler,
sensor_buffer_setup_ops);
這裡的神奇函數是iio_triggered_buffer_setup。 這也將為您的設備提供INDIO_DIRECT_MODE功能。 當觸發器(從用戶空間)連接到您的設備時,您無法知道何時觸發捕獲。
當連續緩衝捕獲處於活動狀態時,應該阻止(通過返回錯誤)驅動程式執行sysfs每通道數據捕獲(由read_raw()掛鉤執行)以避免未確定的行為,因為觸發器處理程式和read_raw( )hook會嘗試同時訪問設備。 用於檢查是否實際使用緩衝模式的函數是iio_buffer_enabled()。 鉤子看起來像這樣:
static int my_read_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long mask) { [...] switch (mask) { case IIO_CHAN_INFO_RAW: if (iio_buffer_enabled(indio_dev)) return -EBUSY; [...] }
iio_buffer_enabled()函數只是確定是否為給定的IIO設備啟用了緩衝區。
使用中一些重要事項:
iio_buffer_setup_ops提供緩衝區設置函數,以便在緩衝區配置序列的固定步驟(在啟用/禁用之前/之後)調用。 如果未指定,則IIO內核將為您的設備提供預設的iio_triggered_buffer_setup_ops。
sensor_iio_pollfunc是觸發器的上半部分。 與每個上半部分一樣,它在中斷上下文中運行,並且必須儘可能少地處理。 在99%的情況下,您只需提供與捕獲相關的時間戳。 再次,可以使用預設的IIO iio_pollfunc_store_time函數。
sensor_trigger_handler是下半部分,它在內核線程中運行,允許我們進行任何處理,包括甚至獲取互斥或睡眠。 重處理應該在這裡進行。 它通常從設備讀取數據並將其與上半部分中記錄的時間戳一起存儲在內部緩衝區中,並將其推送到IIO設備緩衝區。
註意:觸發緩衝必須使用觸發器。 它告訴驅動程式何時從設備讀取樣本並將其放入緩衝區。 觸發緩衝對於編寫IIO設備驅動程式不是必需的。 通過讀取通道的原始屬性,也可以通過sysf使用單次捕獲,這隻會執行單次轉換(對於正在讀取的通道屬性)。 緩衝模式允許連續轉換,從而在單次捕獲多個通道。
IIO trigger and sysfs (user space)用戶空間觸發器和sysfs
sysfs中有兩個與觸發器相關的位置:
-
- /sys/bus/iio/devices/triggerY/:一旦IIO觸發器註冊到IIO核心並且對應於索引為Y的觸發器,就會創建該目錄。目錄中至少有一個屬性:
- name:這是可以在以後用於與設備關聯的觸發器名稱
- 另一個可能是採樣頻率或其他,和觸發器類型相關
- /sys/bus/iio/devices/iio:deviceX/trigger/*如果您的設備支持觸發緩衝區,將自動創建目錄。 通過在current_trigger文件中寫入觸發器的名稱,可以將觸發器與我們的設備相關聯。
- /sys/bus/iio/devices/triggerY/:一旦IIO觸發器註冊到IIO核心並且對應於索引為Y的觸發器,就會創建該目錄。目錄中至少有一個屬性:
Sysfs trigger interface Sysfs觸發器介面
通過CONFIG_IIO_SYSFS_TRIGGER = y config選項在內核中啟用sysfs觸發器,將自動創建/ sys / bus / iio / devices / iio_sysfs_trigger /文件夾,並可用於sysfs觸發器管理。 目錄中將有兩個文件add_trigger和remove_trigger。 它的驅動程式在drivers / iio / trigger / iio-trig-sysfs.c中。
add_trigger file
這用於創建新的sysfs觸發器。 您可以通過將正值(將用作觸發器ID)寫入該文件來創建新觸發器。 它將創建新的sysfs觸發器,可在/ sys / bus / iio / devices / triggerX中訪問,其中X是觸發器編號。
例如:# echo 2 > add_trigger
這將創建一個新的sysfs觸發器,可在/ sys / bus / iio / devices / trigger2中訪問。 如果系統中已存在具有指定ID的觸發器,則將返回無效的參數消息。 sysfs觸發器名稱模式為sysfstrig {ID}。 命令echo 2> add_trigger將創建名為sysfstrig2的trigger / sys / bus / iio / devices / trigger2:
$ cat /sys/bus/iio/devices/trigger2/name
sysfstrig2
每個sysfs觸發器至少包含一個文件:trigger_now。 將1寫入該文件將指示其current_trigger中具有相應觸發器名稱的所有設備開始捕獲,並將數據推送到其各自的緩衝區中。 每個設備緩衝區必須設置其大小,並且必須啟用(echo 1> / sys / bus / iio / devices / iio:deviceX / buffer / enable)。
remove_trigger file
要刪除觸發器,請使用以下命令:
# echo 2 > remove_trigger
使用觸發器綁定設備
將設備與給定觸發器相關聯包括將觸發器的名稱寫入設備觸發器目錄下可用的current_trigger文件。 例如,假設我們需要將設備與具有索引2的觸發器綁定:
# set trigger2 as current trigger for device0 # echo sysfstrig2 > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
要從設備分離觸發器,應該將空字元串寫入設備觸發器目錄的current_trigger文件,如下所示:
# echo "" > iio:device0/trigger/current_trigger
我們將在下一節進一步看到一個處理數據捕獲的sysfs觸發器的實際示例。
The interrupt trigger interface中斷觸發器介面
請考慮以下代碼示例
static struct resource iio_irq_trigger_resources[] = { [0] = { .start = IRQ_NR_FOR_YOUR_IRQ, .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, }, }; static struct platform_device iio_irq_trigger = { .name = "iio_interrupt_trigger", .num_resources = ARRAY_SIZE(iio_irq_trigger_resources), .resource = iio_irq_trigger_resources, }; platform_device_register(&iio_irq_trigger);
聲明我們的IRQ觸發器,它將載入IRQ觸發器獨立模塊。 如果其探測功能成功,則會有一個與觸發器對應的目錄。 IRQ觸發器名稱的格式為irqtrigX,其中X對應於剛剛傳遞的虛擬IRQ,您將在/ proc / interrupt中看到:
$ cd /sys/bus/iio/devices/trigger0/
$ cat name
正如我們對其他觸發器所做的那樣,您只需將該觸發器分配給設備current_trigger文件即可將該觸發器分配給您的設備。
# echo "irqtrig85" > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
現在,每次觸發中斷時,都會捕獲設備數據。
註意:IRQ觸發器驅動程式還不支持DT,這就是我們使用board init文件的原因。 但是這沒關係; 由於驅動程式需要資源,我們可以使用DT而無需更改任何代碼。
以下是聲明IRQ觸發器介面的設備樹節點的示例:
mylabel: my_trigger@0{ compatible = "iio_interrupt_trigger"; interrupt-parent = <&gpio4>; interrupts = <30 0x0>; };
該示例假設IRQ線是屬於GPIO控制器節點gpio4的GPIO#30。 這包括使用GPIO作為中斷源,這樣無論何時GPIO變為給定狀態,都會引發中斷,從而觸發捕獲。
hrtimer觸發器介面(4.5內核以下可能不支持)
hrtimer觸發器依賴於configfs文件系統(請參閱內核源代碼中的Documentation / iio / iio_configfs.txt),可以通過CONFIG_IIO_CONFIGFS配置選項啟用它,並掛載在我們的系統上(通常位於/ config目錄下):
# mkdir /config
# mount -t configfs none /config
現在,載入模塊iio-trig-hrtimer將創建在/ config / iio下可訪問的IIO組,允許用戶在/ config / iio / triggers / hrtimer下創建hrtimer觸發器。如:
# create a hrtimer trigger $ mkdir /config/iio/triggers/hrtimer/my_trigger_name # remove the trigger $ rmdir /config/iio/triggers/hrtimer/my_trigger_name
每個hrtimer觸發器在觸發器目錄中包含單個sampling_frequency屬性(/sys/bus/iio/devices/triggerY/文件夾下)。 在使用hrtimer觸發器的數據捕獲一節中的章節中進一步提供了完整且有效的示例。
IIO buffers IIO緩衝區
IIO緩衝區提供連續數據捕獲,可同時讀取多個數據通道。 可以通過/ dev / iio:device字元設備節點從用戶空間訪問緩衝區。 在觸發器處理程式中,用於填充緩衝區的函數是iio_push_to_buffers_with_timestamp。 負責為您的設備分配觸發緩衝區的函數是iio_triggered_buffer_setup()。
IIO緩衝sysfs介面
IIO緩衝區在/ sys / bus / iio / iio:deviceX / buffer / *下有一個關聯的屬性目錄。 以下是一些現有屬性:
- length: 緩衝區可以存儲的數據樣本總數(容量)。 這是緩衝區包含的掃描數。
- enable: 這將激活緩衝區捕獲,啟動緩衝區捕獲。
- watermark: 自內核版本v4.2起,此屬性已可用。 它是一個正數,指定阻塞讀取應等待的掃描元素數。 例如,如果使用輪詢,它將阻塞,直到達到水印。 只有當水印大於請求的讀取量時才有意義。 它不會影響非阻塞讀取。 可以在超時時阻止輪詢併在超時到期後讀取可用樣本,因此具有最大延遲保證。
IIO緩衝區設置
將要讀取數據並將其推入緩衝區的通道稱為掃描元素(scan element )。 可以從用戶空間通過/ sys / bus / iio / iio:deviceX / scan_elements / *目錄訪問它們的配置,其中包含以下屬性:
- en (實際上是屬性名稱的尾碼)用於啟用通道。 當且僅當其屬性為非零時,觸發捕獲將包含此通道的數據樣本。 例如,in_voltage0_en,in_voltage1_en等。
- type 描述了緩衝區內的掃描元素數據存儲,因此描述了從用戶空間讀取它的形式。 例如,in_voltage0_type。 格式為[be | le]:
[s|u]bits/storagebitsXrepeat[>>shift].
-
- be或le指定位元組序(大或小)
- s或u指定符號(帶符號(2的補碼)或無符號)。
- bits 是有效數據位的數量。
- storagebits :是此通道在緩衝區中占用的位數。 也就是說,一個值可以用12位(位)真正編碼,但在緩衝區中占用16位(存儲位)。 因此,必須將數據向右移動四次以獲得實際值。 此參數取決於設備,應參考其數據表。
- shift:表示在屏蔽掉未使用的位之前應該移位數據值的次數。 並不總是需要此參數。 如果有效位(位)的數量等於存儲位的數量,則移位將為0.還可以在器件數據手冊中找到該參數。
- repeat 指定位/存儲位重覆的數量。 當repeat元素為0或1時,則省略重覆值。
解釋這一部分的最好方法是通過內核文檔的摘錄,可以在這裡找到:https://www.kernel.org/doc/html/latest/driver-api/iio/buffers.html。 例如,用於3軸加速度計的驅動程式,具有12位解析度,其中數據存儲在兩個8位寄存器中,如下所示:
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
|D3 |D2 |D1 |D0 | X | X | X | X | (LOW byte, address 0x06)
+---+---+---+---+---+---+---+---+
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
|D11|D10|D9 |D8 |D7 |D6 |D5 |D4 | (HIGH byte, address 0x07)
+---+---+---+---+---+---+---+---+
每個軸將具有以下掃描元素類型:
$ cat /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_y_type le:s12/16>>4
人們應該將其解釋為16位大小的小端符號數據,需要在屏蔽12個有效數據位之前將其右移4位。
struct iio_chan_spec中負責確定如何將通道的值存儲到緩衝區中的元素是scant_type。
struct iio_chan_spec { [...] struct { char sign; /* Should be 'u' or 's' as explained above */ u8 realbits; u8 storagebits; u8 shift; u8 repeat; enum iio_endian endianness; } scan_type; [...] };
這個結構絕對匹配[be | le]:[s|u]bits/storagebitsXrepeat[>>shift], ,這是上一節中描述的模式。 讓我們看看結構的每個成員:
-
- sign表示數據的符號,並匹配模式中的[s | u]
- realbits對應於模式中的位
- storagebits與模式中的相同名稱匹配
- shift對應於模式的移位,重覆相同
- iio_indian表示位元組序,並匹配模式中的[be | le]
此時,可以編寫與前面解釋的類型相對應的IIO通道結構:
struct struct iio_chan_spec accel_channels[] = { { .type = IIO_ACCEL, .modified = 1, .channel2 = IIO_MOD_X, /* other stuff here */ .scan_index = 0, .scan_type = { .sign = 's', .realbits = 12, .storagebits = 16, .shift = 4, .endianness = IIO_LE, }, } /* similar for Y (with channel2 = IIO_MOD_Y, scan_index = 1) * and Z (with channel2 = IIO_MOD_Z, scan_index = 2) axis */ }
Putting it all together
讓我們仔細看看BOSH的數字三軸加速度感測器BMA220。 這是一個SPI / I2C相容器件,具有8位大小的寄存器,以及片上運動觸發中斷控制器,實際上可以感應傾斜,運動和衝擊振動。 其數據表可從以下網址獲得:http://www.mouser.fr/pdfdocs/BSTBMA220DS00308.PDF,其驅動程式自內核v4.8(CONFIG_BMA200)開始引入。 讓我們一起來看看:
首先,我們使用struct iio_chan_spec聲明我們的IIO通道。 一旦使用了觸發緩衝區,我們就需要填充.scan_index和.scan_type欄位:
#define BMA220_DATA_SHIFT 2 #define BMA220_DEVICE_NAME "bma220" #define BMA220_SCALE_AVAILABLE "0.623 1.248 2.491 4.983" #define BMA220_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .address = reg, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .scan_index = index, \ .scan_type = { \ .sign = 's', \ .realbits = 6, \ .storagebits = 8, \ .shift = BMA220_DATA_SHIFT, \ .endianness = IIO_CPU, \ }, \ } static const struct iio_chan_spec bma220_channels[] = { BMA220_ACCEL_CHANNEL(0, BMA220_REG_ACCEL_X, X), BMA220_ACCEL_CHANNEL(1, BMA220_REG_ACCEL_Y, Y), BMA220_ACCEL_CHANNEL(2, BMA220_REG_ACCEL_Z, Z), };
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)表示每個通道都有一個* _raw sysfs條目(屬性),而.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)表示所有相同類型的通道只有一個* _scale sysfs條目:
jma@jma:~$ ls -l /sys/bus/iio/devices/iio:device0/ (...) # without modifier, a channel name would have in_accel_raw (bad) -rw-r--r-- 1 root root 4096 jul 20 14:13 in_accel_scale -rw-r--r-- 1 root root 4096 jul 20 14:13 in_accel_x_raw -rw-r--r-- 1 root root 4096 jul 20 14:13 in_accel_y_raw -rw-r--r-- 1 root root 4096 jul 20 14:13 in_accel_z_raw (...)
讀取in_accel_scale會調用read_raw()掛鉤,並將掩碼設置為IIO_CHAN_INFO_SCALE。 讀取in_accel_x_raw會調用read_raw()掛鉤,並將掩碼設置為IIO_CHAN_INFO_RAW。 因此,實際值是raw_value * scale。
.scan_type所說的是每個通道返回的值是8位大小(將占用緩衝區中的8位),但有用的有效負載僅占用6位,並且數據必須在屏蔽之前右移2次 出未使用的位。 任何掃描元素類型將如下所示:
$ cat /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_x_type le:s6/8>>2
以下是我們的pollfunc(實際上是下半部分),它從設備讀取樣本並將讀取值推送到緩衝區(iio_push_to_buffers_with_timestamp())。 完成後,我們通知核心(iio_trigger_notify_done()):
static irqreturn_t bma220_trigger_handler(int irq, void *p) { int ret; struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bma220_data *data = iio_priv(indio_dev); struct spi_device *spi = data->spi_device; mutex_lock(&data->lock); data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK; ret = spi_write_then_read(spi, data->tx_buf, 1, data->buffer, ARRAY_SIZE(bma220_channels) - 1); if (ret < 0) goto err; iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, pf->timestamp); err: mutex_unlock(&data->lock); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; }
以下是讀取功能。 它是一個鉤子,每次讀取設備的sysfs條目時都會調用它:
static int bma220_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { int ret; u8 range_idx struct bma220_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_RAW: /* If buffer mode enabled, do not process single-channel read */ if (iio_buffer_enabled(indio_dev)) return -EBUSY; /* Else we read the channel */ ret = bma220_read_reg(data->spi_device, chan->address); if (ret < 0) return -EINVAL; *val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE); if (ret < 0) return ret; range_idx = ret & BMA220_RANGE_MASK; *val = bma220_scale_table[range_idx][0]; *val2 = bma220_scale_table[range_idx][1]; return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; }
當讀取*raw sysfs文件時,調用掛鉤程式,在mask參數中給定IIO_CHAN_INFO_RAW,併在* chan參數中調用相應的通道。 * val和val2實際上是輸出參數。 必須使用raw值設置它們(從設備讀取)。 在* scale sysfs文件上執行的任何讀取都將使用掩碼參數中的IIO_CHAN_INFO_SCALE調用掛鉤,依此類推每個屬性掩碼。
寫入功能也是如此,用於將值寫入設備。 您的驅動程式有80%的可能性不需要寫入功能。 此寫掛鉤允許用戶更改設備的比例:
static int bma220_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { int i; int ret; int index = -1; struct bma220_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_SCALE: for (i = 0; i < ARRAY_SIZE(bma220_scale_table); i++) if (val == bma220_scale_table[i][0] && val2 == bma220_scale_table[i][1]) { index = i; break; } if (index < 0) return -EINVAL; mutex_lock(&data->lock); data->tx_buf[0] = BMA220_REG_RANGE; data->tx_buf[1] = index; ret = spi_write(data->spi_device, data->tx_buf, sizeof(data->tx_buf)); if (ret < 0) dev_err(&data->spi_device->dev, "failed to set measurement range\n"); mutex_unlock(&data->lock); return 0; } return -EINVAL; }
只要將值寫入設備,就會調用此函數。 經常更改的參數是比例。 一個例子可能是:
echo <desired-scale> > /sys/bus/iio/devices/iio;devices0/in_accel_scale.
現在,它來填充一個結構iio_info結構,給我們的iio_device:
static const struct iio_info bma220_info = { .driver_module = THIS_MODULE, .read_raw = bma220_read_raw, .write_raw = bma220_write_raw, /* Only if your driver need it */ };
在probe函數中,我們分配並設置了一個struct iio_dev IIO設備。 私人數據的記憶體也被保留:
/* * We provide only two mask possibility, allowing to select none or every * channels. */ static const unsigned long bma220_accel_scan_masks[] = { BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), 0 }; static int bma220_probe(struct spi_device *spi) { int ret; struct iio_dev *indio_dev; struct bma220_data *data; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); if (!indio_dev) { dev_err(&spi->dev, "iio allocation failed!\n"); return -ENOMEM; } data = iio_priv(indio_dev); data->spi_device = spi; spi_set_drvdata(spi, indio_dev); mutex_init(&data->lock); indio_dev->dev.parent = &spi->dev; indio_dev->info = &bma220_info; indio_dev->name = BMA220_DEVICE_NAME; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = bma220_channels; indio_dev->num_channels = ARRAY_SIZE(bma220_channels); indio_dev->available_scan_masks = bma220_accel_scan_masks; ret = bma220_init(data->spi_device); if (ret < 0) return ret; /* this call will enable trigger buffer support for the device */ ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, bma220_trigger_handler, NULL); if (ret < 0) { dev_err(&spi->dev, "iio triggered buffer setup failed\n"); goto err_suspend; } ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&spi->dev, "iio_device_register failed\n"); iio_triggered_buffer_cleanup(indio_dev); goto err_suspend; } return 0; err_suspend: return bma220_deinit(spi); }
可以通過CONFIG_BMA220內核選項啟用此驅動程式。 也就是說,這隻能從內核中的v4.8開始提供。 可以在較舊的內核版本上使用的最接近的設備是BMA180,可以使用CONFIG_BMA180選項啟用它。