Linux設備驅動之IIO子系統——Triggered buffer support觸發緩衝支持

Triggered buffer support觸發緩衝支持html

  在許多數據分析應用中,可以基於某些外部信號(觸發器)捕獲數據是比較有用的。 這些觸發器多是:redis

    • 數據就緒信號
    • 鏈接到某個外部系統的IRQ線路(GPIO或其餘)
    • 處理器週期性中斷
    • 用戶空間在sysfs中讀/寫特定文件

  IIO設備驅動程序與觸發器徹底無關。 觸發器能夠初始化一個或多個設備上的數據捕獲。 這些觸發器用於填充緩衝區,而後做爲字符設備暴露給用戶空間。api

  能夠開發一個本身的觸發驅動程序,但這超出了本書的範圍。 咱們將嘗試僅關注現有的。 這些是:ide

  • 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子系統中鏈接。post

  IIO核心提供了一組輔助函數來設置觸發緩衝區,能夠在drivers / iio / industrialio-triggered-buffer.c中找到。this

  如下是從驅動程序中支持觸發緩衝區的步驟:編碼

1.若是須要,填寫iio_buffer_setup_ops結構:spa

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緩衝區設置  

  將要讀取數據並將其推入緩衝區的通道稱爲掃描元素(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選項啓用它。

相關文章
相關標籤/搜索