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

来源:https://www.cnblogs.com/yongleili717/archive/2019/04/23/10758691.html
-Advertisement-
Play Games

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文件中寫入觸發器的名稱,可以將觸發器與我們的設備相關聯。

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選項啟用它。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基礎準備 1.創建asp.net core Web 應用程式選擇Api 2.appsettings.json 配置consul伺服器地址,以及本機ip和埠號信息 3.程式入口(program.cs)配置useurls,ip和port從配置文件(或者命令行中)讀取(命令行啟動方式:dotnet Co ...
  • " 1、如何通過 EF6 來連接 MySQL? " " 2、如何通過 EF6 來實現 CRUD? " "2.1、Create 添加" "2.2、Retrieve 查詢" "2.3、Update 修改" "2.4、Delete 刪除" " 3、如何更好的運用 EF6 來完成工作? " "3.1、傳說中 ...
  • 環境準備 vs開發環境:vs2017 consul版本: 1.4.4 netcore版本:2.1 安裝Consul 1.從官網下載consul到本地,選擇系統對應的版本進行下載到本地,下載地址:https://www.consul.io/downloads.html 2.下載到本地之後解壓壓縮文件, ...
  • 前言 作為一個Windows系統下的開發者,我對於Core的使用機會幾乎為0,但是考慮到微軟的戰略規劃,我覺得,Core還是有先瞭解起來的必要。 因為,目前微軟已經搞出了兩個框架了,一個是Net標準(.NetFramework),一個是Net Core。 而新特性的更新幾乎都是在Net Core這個 ...
  • 當進行數據遷移的時候提示 修改appsettings配置連接串的Trusted_Connection 屬性 Trusted_Connection 當為 false 時,將在連接中指定用戶 ID 和密碼。當為 true 時,將使用當前的 Windows 帳戶憑據進行身份驗證。 可識別的值為 true、 ...
  • 當進行cobbler配置後,併進行web登錄時,出現錯誤: 先查看其日誌位置 #cat /etc/httpd/conf.d/ssl.conf 在其中位置發現其錯誤的日誌位置為/etc/httpd/logs/ssl_error_log 打開文件#cat /etc/httpd/logs/ssl_erro ...
  • 安裝32位程式運行支持 可能報錯: 解決方案: 若沒有aptitude需先安裝該軟體 在提示中第一處選n, 第二處選y, 如下: 安裝arm編譯gcc 安裝 測試是否安裝成功 在目錄/opt/gcc 4.4.4 glibc 2.11.1 multilib 1.0/arm fsl linux gnue ...
  • 因業務需要在DHCP伺服器上綁定設備MAC,提示”指定的IP地址或硬體地址正被其他客戶端使用”,與業務同事溝通之前該設備做過地址保留,具體對應地址遺忘了。問題描述:a.按照用戶需求添加地址保留:b.提示:指定的IP地址或硬體地址正被其他客戶端使用;添加失敗。環境描述:Windows DHCP Ser... ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...