Linux设备驱动之IIO子系统——Triggered buffer support触发缓冲支持
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文件中寫入觸發器的名稱,可以將觸發器與我們的設備相關聯。
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緩沖區設置
將要讀取數據并將其推入緩沖區的通道稱為掃描元素(scanelement)。 可以從用戶空間通過/ 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
");
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!
");
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
");
goto err_suspend;
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&spi->dev, "iio_device_register failed
");
iio_triggered_buffer_cleanup(indio_dev);
goto err_suspend;
}
return 0;
err_suspend:
return bma220_deinit(spi);
}
可以通過CONFIG_BMA220內核選項啟用此驅動程序。 也就是說,這只能從內核中的v4.8開始提供。 可以在較舊的內核版本上使用的最接近的設備是BMA180,可以使用CONFIG_BMA180選項啟用它。
總結
以上是生活随笔為你收集整理的Linux设备驱动之IIO子系统——Triggered buffer support触发缓冲支持的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 搭建rabbitmq镜像模式集群
- 下一篇: [X][xrandr][archlinu