直接來(lái)源:嵌入式客棧
出處:https://www.cnblogs.com/yongleili717/p/10744252.html
作者:三石li
由于需要對(duì)ADC進(jìn)行驅(qū)動(dòng)設(shè)計(jì),因此學(xué)習(xí)了一下Linux驅(qū)動(dòng)的IIO子系統(tǒng)。本文翻譯自《Linux Device Drivers Development 》--John Madieu
工業(yè)I/O(IIO)是專(zhuān)用于模數(shù)轉(zhuǎn)換器(ADC)和數(shù)模轉(zhuǎn)換器(DAC)的內(nèi)核子系統(tǒng)。隨著越來(lái)越多的具有不同代碼實(shí)現(xiàn)的傳感器(具有模擬到數(shù)字或數(shù)字到模擬,功能的測(cè)量設(shè)備)分散在內(nèi)核源上,收集它們變得必要。這就是IIO框架以通用的方式所做的事情。自2009年以來(lái),Jonathan Cameron和Linux-IIO社區(qū)一直在開(kāi)發(fā)它。
加速度計(jì),陀螺儀,電流/電壓測(cè)量芯片,光傳感器,壓力傳感器等都屬于IIO系列器件。
IIO模型基于設(shè)備和通道架構(gòu):
IIO芯片是物理和硬件傳感器/轉(zhuǎn)換器。它作為字符設(shè)備(當(dāng)支持觸發(fā)緩沖時(shí))暴露給用戶(hù)空間,以及包含一組文件的sysfs目錄條目,其中一些文件代表通道。單個(gè)通道用單個(gè)sysfs文件條目表示。
下面是從用戶(hù)空間與IIO驅(qū)動(dòng)程序交互的兩種方式:

上圖顯示了如何在內(nèi)核和用戶(hù)空間之間組織IIO框架。驅(qū)動(dòng)程序使用IIO核心公開(kāi)的一組工具和API來(lái)管理硬件并向IIO核心報(bào)告處理。然后,IIO子系統(tǒng)通過(guò)sysfs接口和字符設(shè)備將整個(gè)底層機(jī)制抽象到用戶(hù)空間,用戶(hù)可以在其上執(zhí)行系統(tǒng)調(diào)用。
IIO API分布在多個(gè)頭文件中,如下所示:
#include <linux/iio/iio.h> /* mandatory */
#include <linux/iio/sysfs.h> /* mandatory since sysfs is used */
#include <linux/iio/events.h> /* For advanced users, to manage iio events */
#include <linux/iio/buffer.h> /* mandatory to use triggered buffers */
#include <linux/iio/trigger.h>/* Only if you implement trigger in your driver (rarely used)*/
在以下文章中,我們將描述和處理IIO框架的每個(gè)概念,例如
IIO設(shè)備在內(nèi)核中表示為struct iio_dev結(jié)構(gòu)體的一個(gè)實(shí)例,并由struct iio_info結(jié)構(gòu)體描述。所有重要的IIO結(jié)構(gòu)都在include/linux/iio/iio.h中定義。
該結(jié)構(gòu)代表IIO設(shè)備,描述設(shè)備和驅(qū)動(dòng)程序。它告訴我們:
設(shè)備上有多少個(gè)通道?
設(shè)備可以在哪些模式下運(yùn)行:?jiǎn)未危|發(fā)緩沖?
這個(gè)驅(qū)動(dòng)程序可以使用哪些hooks鉤子?
struct iio_dev {
[...]
int modes;
int currentmode;
struct device dev;
struct iio_buffer *buffer;
int scan_bytes;
const unsigned long *available_scan_masks;
const unsigned long *active_scan_mask;
bool scan_timestamp;
struct iio_trigger *trig;
struct iio_poll_func *pollfunc;
struct iio_chan_spec const *channels;
int num_channels;
const char *name;
const struct iio_info *info;
const struct iio_buffer_setup_ops *setup_ops;
struct cdev chrdev;
};
完整的結(jié)構(gòu)在IIO頭文件中定義。我們將不感興趣的字段在此處刪除。
modes: 這表示設(shè)備支持的不同模式。支持的模式有:
currentmode: 這表示設(shè)備實(shí)際使用的模式。
dev: 這表示IIO設(shè)備所依賴(lài)的struct設(shè)備(根據(jù)Linux設(shè)備型號(hào))。
buffer: 這是您的數(shù)據(jù)緩沖區(qū),在使用觸發(fā)緩沖區(qū)模式時(shí)會(huì)推送到用戶(hù)空間。使用iio_triggered_buffer_setup函數(shù)啟用觸發(fā)緩沖區(qū)支持時(shí),它會(huì)自動(dòng)分配并與您的設(shè)備關(guān)聯(lián)。
scan_bytes: 這是捕獲并饋送到緩沖區(qū)的字節(jié)數(shù)。當(dāng)從用戶(hù)空間使用觸發(fā)緩沖區(qū)時(shí),緩沖區(qū)應(yīng)至少為indio-> scan_bytes字節(jié)大。
available_scan_masks: 這是允許的位掩碼的可選數(shù)組。使用觸發(fā)緩沖器時(shí),可以啟用通道捕獲并將其饋入IIO緩沖區(qū)。如果您不想允許某些通道啟用,則應(yīng)僅使用允許的通道填充此數(shù)組。以下是為加速度計(jì)(帶有X,Y和Z通道)提供掃描掩碼的示例:
/*
* Bitmasks 0x7 (0b111) and 0 (0b000) are allowed.
* It means one can enable none or all of them.
* one can't for example enable only channel X and Y
*/
static const unsigned long my_scan_masks[] = {0x7, 0};
indio_dev->available_scan_masks = my_scan_masks;
active_scan_mask: 這是啟用通道的位掩碼。只有來(lái)自這些通道的數(shù)據(jù)能被推入緩沖區(qū)。例如,對(duì)于8通道ADC轉(zhuǎn)換器,如果只啟用第一個(gè)(0),第三個(gè)(2)和最后一個(gè)(7)通道,則位掩碼將為0b10000101(0x85)。active_scan_mask將設(shè)置為0x85。然后,驅(qū)動(dòng)程序可以使用for_each_set_bit宏遍歷每個(gè)設(shè)置位,根據(jù)通道獲取數(shù)據(jù),并填充緩沖區(qū)。
scan_timestamp: 這告訴我們是否將捕獲時(shí)間戳推入緩沖區(qū)。如果為true,則將時(shí)間戳作為緩沖區(qū)的最后一個(gè)元素。時(shí)間戳大8字節(jié)(64位)。
trig: 這是當(dāng)前設(shè)備觸發(fā)器(支持緩沖模式時(shí))。
pollfunc:這是在接收的觸發(fā)器上運(yùn)行的函數(shù)。
channels: 這表示通道規(guī)范結(jié)構(gòu),用于描述設(shè)備具有的每個(gè)通道。
num_channels: 這表示通道中指定的通道數(shù)。
name: 這表示設(shè)備名稱(chēng)。
info: 來(lái)自驅(qū)動(dòng)程序的回調(diào)和持續(xù)信息。
setup_ops: 啟用/禁用緩沖區(qū)之前和之后調(diào)用的回調(diào)函數(shù)集。這個(gè)結(jié)構(gòu)在include / linux / iio / iio.h中定義,如下所示:
struct iio_buffer_setup_ops {
int (* preenable) (struct iio_dev *);
int (* postenable) (struct iio_dev *);
int (* predisable) (struct iio_dev *);
int (* postdisable) (struct iio_dev *);
bool (* validate_scan_mask) (struct iio_dev *indio_dev,
const unsigned long *scan_mask);
};
setup_ops: 如果未指定,則IIO內(nèi)核使用drivers / iio / buffer / industrialio-triggered-buffer.c中定義的缺省iio_triggered_buffer_setup_ops。
chrdev: 這是由IIO核心創(chuàng)建的關(guān)聯(lián)字符設(shè)備。
用于為IIO設(shè)備分配內(nèi)存的函數(shù)是iio_device_alloc():
struct iio_dev * iio_device_alloc(int sizeof_priv)
///struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
/* Resource-managed iio_device_alloc()*/
/*Managed iio_device_alloc. iio_dev allocated with this function is automatically freed on driver detach.
If an iio_dev allocated with this function needs to be freed separately, devm_iio_device_free() must be used. */
dev是為其分配iio_dev的設(shè)備,sizeof_priv是用于為任何私有結(jié)構(gòu)分配的內(nèi)存空間。這樣,傳遞每個(gè)設(shè)備(私有)數(shù)據(jù)結(jié)構(gòu)非常簡(jiǎn)單。如果分配失敗,該函數(shù)返回NULL:
struct iio_dev *indio_dev;
struct my_private_data *data;
indio_dev = iio_device_alloc(sizeof(*data));
if (!indio_dev)
return -ENOMEM;
/*data is given the address of reserved momory for private data */
data = iio_priv(indio_dev);
在分配IIO設(shè)備存儲(chǔ)器之后,下一步是填充不同的字段。完成后,必須使用iio_device_register函數(shù)向IIO子系統(tǒng)注冊(cè)設(shè)備:
int iio_device_register(struct iio_dev *indio_dev)
//devm_iio_device_register(dev, indio_dev)
/* Resource-managed iio_device_register() */
在執(zhí)行此功能后,設(shè)備將準(zhǔn)備好接受來(lái)自用戶(hù)空間的請(qǐng)求。反向操作(通常在釋放函數(shù)中完成)是iio_device_unregister():
void iio_device_unregister(struct iio_dev *indio_dev)
// void devm_iio_device_unregister(struct device * dev, struct iio_dev * indio_dev)
一旦取消注冊(cè),iio_device_alloc分配的內(nèi)存可以用iio_device_free釋放:
void iio_device_free(struct iio_dev *iio_dev)
// void devm_iio_device_free(struct device * dev, struct iio_dev * iio_dev)
給定IIO設(shè)備作為參數(shù),可以通過(guò)以下方式檢索私有數(shù)據(jù):
struct my_private_data *the_data = iio_priv(indio_dev);
struct iio_info結(jié)構(gòu)用于聲明IIO內(nèi)核使用的鉤子,以讀取/寫(xiě)入通道/屬性值:
struct iio_info {
struct module *driver_module;
const struct attribute_group *attrs;
int (*read_raw)(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask);
int (*write_raw)(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask);
[...]
};
driver_module: 這是用于確保chrdev正確擁有的模塊結(jié)構(gòu),通常設(shè)置為T(mén)HIS_MODULE。
attrs: 這表示設(shè)備屬性。
read_raw: 這是用戶(hù)讀取設(shè)備sysfs文件屬性時(shí)的回調(diào)運(yùn)行。mask參數(shù)是一個(gè)位掩碼,它允許我們知道請(qǐng)求了哪種類(lèi)型的值。channel參數(shù)讓我們知道相關(guān)的通道。它可以是采樣頻率,用于將原始值轉(zhuǎn)換為可用值的比例,或原始值本身。
write_raw: 這是用于將值寫(xiě)入設(shè)備的回調(diào)。例如,可以使用它來(lái)設(shè)置采樣頻率。
以下代碼顯示了如何設(shè)置struct iio_info結(jié)構(gòu):
static const struct iio_info iio_dummy_info = {
.driver_module = THIS_MODULE,
.read_raw = &iio_dummy_read_raw,
.write_raw = &iio_dummy_write_raw,
[...]
/*
* Provide device type specific interface functions and
* constant data. 提供設(shè)備類(lèi)型特定的接口功能和常量數(shù)據(jù)。
*/
indio_dev->info = &iio_dummy_info;
通道代表單條采集線(xiàn)。例如加速度計(jì)具有3個(gè)通道(X,Y,Z),因?yàn)槊總€(gè)軸代表單個(gè)采集線(xiàn)。struct iio_chan_spec是表示和描述內(nèi)核中單個(gè)通道的結(jié)構(gòu):
struct iio_chan_spec {
enum iio_chan_type type;
int channel;
int channel2;
unsigned long address;
int scan_index;
struct {
charsign;
u8 realbits;
u8 storagebits;
u8 shift;
u8 repeat;
enum iio_endian endianness;
} scan_type;
long info_mask_separate;
long info_mask_shared_by_type;
long info_mask_shared_by_dir;
long info_mask_shared_by_all;
const struct iio_event_spec *event_spec;
unsigned int num_event_specs;
const struct iio_chan_spec_ext_info *ext_info;
const char *extend_name;
const char *datasheet_name;
unsigned modified:1;
unsigned indexed:1;
unsigned output:1;
unsigned differential:1;
};
各個(gè)參數(shù)意義:
type: 這指定了通道的測(cè)量類(lèi)型。在電壓測(cè)量的情況下,它應(yīng)該是IIO_VOLTAGE。對(duì)于光傳感器,它是IIO_LIGHT。對(duì)于加速度計(jì),使用IIO_ACCEL。所有可用類(lèi)型都在include / uapi / linux / iio / types.h中定義,如enum iio_chan_type。要為給定轉(zhuǎn)換器編寫(xiě)驅(qū)動(dòng)程序,請(qǐng)查看該文件以查看每個(gè)通道所屬的類(lèi)型。
channel: 這指定.indexed設(shè)置為1時(shí)的通道索引。
channel2: 這指定.modified設(shè)置為1時(shí)的通道修飾。
modified: 這指定是否將修飾符應(yīng)用于此通道屬性名稱(chēng)。在這種情況下,修飾符設(shè)置在.channel2中。(例如,IIO_MOD_X,IIO_MOD_Y,IIO_MOD_Z是關(guān)于xyz軸的軸向傳感器的修改器)。可用修飾符列表在內(nèi)核IIO頭中定義為枚舉iio_modifier。修飾符只會(huì)破壞sysfs中的通道屬性名稱(chēng),而不是值。
indexed: 這指定通道屬性名稱(chēng)是否具有索引。如果是,則在.channel字段中指定索引。
scan_index and scan_type: 當(dāng)使用緩沖區(qū)觸發(fā)器時(shí),這些字段用于標(biāo)識(shí)緩沖區(qū)中的元素。scan_index設(shè)置緩沖區(qū)內(nèi)捕獲的通道的位置。具有較低scan_index的通道將放置在具有較高索引的通道之前。將.scan_index設(shè)置為-1將阻止通道進(jìn)行緩沖捕獲(scan_elements目錄中沒(méi)有條目)。
暴露給用戶(hù)空間的通道sysfs屬性以位掩碼的形式指定。根據(jù)共享信息,可以將屬性設(shè)置為以下掩碼之一:
info_mask_separate 將屬性標(biāo)記為特定于此通
info_mask_shared_by_type 將該屬性標(biāo)記為由相同類(lèi)型的所有通道共享。導(dǎo)出的信息由相同類(lèi)型的所有通道共享。
info_mask_shared_by_dir 將屬性標(biāo)記為由同一方向的所有通道共享。導(dǎo)出的信息由同一方向的所有通道共享。
info_mask_shared_by_all 將屬性標(biāo)記為所有通道共享,無(wú)論其類(lèi)型或方向如何。導(dǎo)出的信息由所有渠道共享。用于枚舉這些屬性的位掩碼都在include / linux / iio / iio.h中定義:
enum iio_chan_info_enum {
IIO_CHAN_INFO_RAW = 0,
IIO_CHAN_INFO_PROCESSED,
IIO_CHAN_INFO_SCALE,
IIO_CHAN_INFO_OFFSET,
IIO_CHAN_INFO_CALIBSCALE,
[...]
IIO_CHAN_INFO_SAMP_FREQ,
IIO_CHAN_INFO_FREQUENCY,
IIO_CHAN_INFO_PHASE,
IIO_CHAN_INFO_HARDWAREGAIN,
IIO_CHAN_INFO_HYSTERESIS,
[...]
};
字節(jié)序字段應(yīng)為以下之一:
enum iio_endian {
IIO_CPU,
IIO_BE,
IIO_LE,
};
通道屬性命名約定
屬性的名稱(chēng)由IIO核心自動(dòng)生成,具有以下模式:{direction} _ {type} _ {index} _ {modifier} _ {info_mask}:
static const char * const iio_direction[] = {
[0] = "in",
[1] = "out",
};
static const char * const iio_chan_type_name_spec[] = {
[IIO_VOLTAGE] = "voltage",
[IIO_CURRENT] = "current",
[IIO_POWER] = "power",
[IIO_ACCEL] = "accel",
[...]
[IIO_UVINDEX] = "uvindex",
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
[IIO_COUNT] = "count",
[IIO_INDEX] = "index",
[IIO_GRAVITY] = "gravity",
};
static const char * const iio_modifier_names[] = {
[IIO_MOD_X] = "x",
[IIO_MOD_Y] = "y",
[IIO_MOD_Z] = "z",
[IIO_MOD_X_AND_Y] = "x&y",
[IIO_MOD_X_AND_Z] = "x&z",
[IIO_MOD_Y_AND_Z] = "y&z",
[...]
[IIO_MOD_CO2] = "co2",
[IIO_MOD_VOC] = "voc",
};
l info_mask取決于char數(shù)組iio_chan_info_postfix中的通道信息掩碼,私有或共享索引值:
/* relies on pairs of these shared then separate依賴(lài)于這些共享的對(duì),然后分離*/
static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_RAW] = "raw",
[IIO_CHAN_INFO_PROCESSED] = "input",
[IIO_CHAN_INFO_SCALE] = "scale",
[IIO_CHAN_INFO_CALIBBIAS] = "calibbias",
[...]
[IIO_CHAN_INFO_SAMP_FREQ] = "sampling_frequency",
[IIO_CHAN_INFO_FREQUENCY] = "frequency",
[...]
};
當(dāng)每種通道類(lèi)型有多個(gè)數(shù)據(jù)通道時(shí),您可能會(huì)遇到麻煩。困境將是:如何識(shí)別它們。有兩種解決方案:索引和修飾符。
使用索引:給定具有一個(gè)通道線(xiàn)的ADC器件,不需要索引。通道定義如下:
static const struct iio_chan_spec adc_channels[] = {
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
}
由前面描述的通道產(chǎn)生的屬性名稱(chēng)將是in_voltage_raw。
/sys/bus/iio/iio:deviceX/in_voltage_raw
現(xiàn)在讓我們看一下有4個(gè)甚至8個(gè)通道的轉(zhuǎn)換器。我們?nèi)绾巫R(shí)別它們?解決方案是使用索引。將.indexed字段設(shè)置為1將使用.channel值替換{index}模式來(lái)替換通道屬性名稱(chēng):
static const struct iio_chan_spec adc_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
{
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
{
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 2,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
{
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 3,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
}
生成的通道屬性為:
/sys/bus/iio/iio:deviceX/in_voltage0_raw
/sys/bus/iio/iio:deviceX/in_voltage1_raw
/sys/bus/iio/iio:deviceX/in_voltage2_raw
/sys/bus/iio/iio:deviceX/in_voltage3_raw
使用修飾符:給定一個(gè)帶有兩個(gè)通道的光傳感器 - 一個(gè)用于紅外光,一個(gè)用于紅外和可見(jiàn)光,沒(méi)有索引或修改器,屬性名稱(chēng)將為in_intensity_raw。在這里使用索引可能容易出錯(cuò),因?yàn)槭褂胕n_intensity0_ir_raw和in_intensity1_ir_raw是沒(méi)有意義的。使用修飾符將有助于提供有意義的屬性名稱(chēng)。通道的定義如下:
static const struct iio_chan_spec mylight_channels[] = {
{
.type = IIO_INTENSITY,
.modified = 1,
.channel2 = IIO_MOD_LIGHT_IR,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),
},
{
.type = IIO_INTENSITY,
.modified = 1,
.channel2 = IIO_MOD_LIGHT_BOTH,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),
},
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.info_mask_shared =BIT(IIO_CHAN_INFO_SAMP_FREQ),
},
}
屬性結(jié)果:
l /sys/bus/iio/iio:deviceX/in_intensity_ir_raw 用于測(cè)量IR強(qiáng)度的通道
l /sys/bus/iio/iio:deviceX/in_intensity_both_raw用于測(cè)量紅外和可見(jiàn)光的通道
l /sys/bus/iio/iio:deviceX/in_illuminance_input用于處理后的數(shù)據(jù)
l /sys/bus/iio/iio:deviceX/sampling_frequency 用于采樣頻率,由所有人共享
這也適用于加速度計(jì),我們將在案例研究中進(jìn)一步了解。現(xiàn)在,讓我們總結(jié)一下我們到目前為止在虛擬IIO驅(qū)動(dòng)程序中討論過(guò)的內(nèi)容。
讓我們總結(jié)一下迄今為止我們?cè)谝粋€(gè)簡(jiǎn)單的虛擬驅(qū)動(dòng)器中看到的內(nèi)容,它將暴露出四個(gè)電壓通道。我們將忽略read()或write()函數(shù):
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/buffer.h>
#define FAKE_VOLTAGE_CHANNEL(num) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (num), \
.address = (num), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type =BIT(IIO_CHAN_INFO_SCALE) \
}
struct my_private_data {
int foo;
int bar;
struct mutex lock;
};
static int fake_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long mask)
{
return 0;
}
static int fake_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
return 0;
}
static const struct iio_chan_spec fake_channels[] = {
FAKE_VOLTAGE_CHANNEL(0),
FAKE_VOLTAGE_CHANNEL(1),
FAKE_VOLTAGE_CHANNEL(2),
FAKE_VOLTAGE_CHANNEL(3),
};
static const struct of_device_id iio_dummy_ids[] = {
{ .compatible = "packt,iio-dummy-random", },
{ /* sentinel */ }
};
static const struct iio_info fake_iio_info = {
.read_raw = fake_read_raw,
.write_raw = fake_write_raw,
.driver_module = THIS_MODULE,
};
static int my_pdrv_probe (struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct my_private_data *data;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&pdev->dev, "iio allocation failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
mutex_init(&data->lock);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &fake_iio_info;
indio_dev->name = KBUILD_MODNAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = fake_channels;
indio_dev->num_channels = ARRAY_SIZE(fake_channels);
indio_dev->available_scan_masks = 0xF;
iio_device_register(indio_dev);
platform_set_drvdata(pdev, indio_dev);
return 0;
}
static void my_pdrv_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
}
static struct platform_driver mypdrv = {
.probe = my_pdrv_probe,
.remove = my_pdrv_remove,
.driver = {
.name = "iio-dummy-random",
.of_match_table = of_match_ptr(iio_dummy_ids),
.owner = THIS_MODULE,
},
};
module_platform_driver(mypdrv);
MODULE_AUTHOR("John Madieu <john.madieu@gmail.com>");
MODULE_LICENSE("GPL");
加載上述模塊后, 我們將有以下輸出, 顯示我們的設(shè)備確實(shí)對(duì)應(yīng)于我們已注冊(cè)的平臺(tái)設(shè)備:
~# ls -l /sys/bus/iio/devices/
lrwxrwxrwx 1 root root 0 Jul 31 20:26 iio:device0 -> ../../../devices/platform/iio-dummy-random.0/iio:device0
lrwxrwxrwx 1 root root 0 Jul 31 20:23 iio_sysfs_trigger -> ../../../devices/iio_sysfs_trigger
下面的列表顯示了此設(shè)備的通道及其名稱(chēng), 這些通道與我們?cè)隍?qū)動(dòng)程序中描述的內(nèi)容完全對(duì)應(yīng):
~# ls /sys/bus/iio/devices/iio\:device0/
dev in_voltage2_raw name uevent
in_voltage0_raw in_voltage3_raw power
in_voltage1_raw in_voltage_scale subsystem
~# cat /sys/bus/iio/devices/iio:device0/name
iio_dummy_random
飛機(jī)上一般是什么操作系統(tǒng)?
高速CAN、容錯(cuò)CAN、LIN總線(xiàn)有什么區(qū)別?
大佬終于把鴻蒙OS講明白了,收藏了!