直接來源:嵌入式客棧
出處:https://www.cnblogs.com/yongleili717/p/10744252.html
作者:三石li
由于需要對ADC進行驅(qū)動設計,因此學習了一下Linux驅(qū)動的IIO子系統(tǒng)。本文翻譯自《Linux Device Drivers Development 》--John Madieu
IIO Framework
工業(yè)I/O(IIO)是專用于模數(shù)轉(zhuǎn)換器(ADC)和數(shù)模轉(zhuǎn)換器(DAC)的內(nèi)核子系統(tǒng)。隨著越來越多的具有不同代碼實現(xiàn)的傳感器(具有模擬到數(shù)字或數(shù)字到模擬,功能的測量設備)分散在內(nèi)核源上,收集它們變得必要。這就是IIO框架以通用的方式所做的事情。自2009年以來,Jonathan Cameron和Linux-IIO社區(qū)一直在開發(fā)它。
加速度計,陀螺儀,電流/電壓測量芯片,光傳感器,壓力傳感器等都屬于IIO系列器件。
IIO模型基于設備和通道架構(gòu):
-
設備代表芯片本身。它是層次結(jié)構(gòu)的頂級。 -
通道代表設備的單個采集線。設備可以具有一個或多個通道。例如,加速度計是具有 ?三個通道的裝置,每個通道對應一個軸(X,Y和Z)。
IIO芯片是物理和硬件傳感器/轉(zhuǎn)換器。它作為字符設備(當支持觸發(fā)緩沖時)暴露給用戶空間,以及包含一組文件的sysfs目錄條目,其中一些文件代表通道。單個通道用單個sysfs文件條目表示。
下面是從用戶空間與IIO驅(qū)動程序交互的兩種方式:
-
/sys/bus/iio/iio:deviceX/:表示傳感器及其通道 -
/dev/iio:deviceX: 表示導出設備事件和數(shù)據(jù)緩沖區(qū)的字符設備
IIO框架架構(gòu)和布局
上圖顯示了如何在內(nèi)核和用戶空間之間組織IIO框架。驅(qū)動程序使用IIO核心公開的一組工具和API來管理硬件并向IIO核心報告處理。然后,IIO子系統(tǒng)通過sysfs接口和字符設備將整個底層機制抽象到用戶空間,用戶可以在其上執(zhí)行系統(tǒng)調(diào)用。
IIO API分布在多個頭文件中,如下所示:
#include?????/*?mandatory?*/
#include???/*?mandatory?since?sysfs?is?used?*/
#include??/*?For?advanced?users,?to?manage?iio?events?*/
#include??/*?mandatory?to?use?triggered?buffers?*/
#include?/*?Only?if?you?implement?trigger?in?your?driver?(rarely?used)*/
在以下文章中,我們將描述和處理IIO框架的每個概念,例如
-
遍歷其數(shù)據(jù)結(jié)構(gòu)(設備,通道等) -
觸發(fā)緩沖支持和連續(xù)捕獲,以及其sysfs接口 -
探索現(xiàn)有的IIO觸發(fā)器 -
以單次模式或連續(xù)模式捕獲數(shù)據(jù) -
列出可用于幫助開發(fā)人員測試其設備的可用工具
IIO數(shù)據(jù)結(jié)構(gòu)
IIO設備在內(nèi)核中表示為struct iio_dev結(jié)構(gòu)體的一個實例,并由struct iio_info結(jié)構(gòu)體描述。所有重要的IIO結(jié)構(gòu)都在include/linux/iio/iio.h中定義。
iio_dev結(jié)構(gòu)
該結(jié)構(gòu)代表IIO設備,描述設備和驅(qū)動程序。它告訴我們:
-
設備上有多少個通道?
-
設備可以在哪些模式下運行:單次,觸發(fā)緩沖?
-
這個驅(qū)動程序可以使用哪些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: 這表示設備支持的不同模式。支持的模式有:
-
INDIO_DIRECT_MODE表示設備提供的sysfs接口。 -
INDIO_BUFFER_TRIGGERED表示設備支持硬件觸發(fā)器。使用iio_triggered_buffer_setup()函數(shù)設置觸發(fā)緩沖區(qū)時,此模式會自動添加到設備中. -
INDIO_BUFFER_HARDWARE表示設備具有硬件緩沖區(qū)。 -
INDIO_ALL_BUFFER_MODES是上述兩者的聯(lián)合。 -
currentmode: 這表示設備實際使用的模式。
-
dev: 這表示IIO設備所依賴的struct設備(根據(jù)Linux設備型號)。
-
buffer: 這是您的數(shù)據(jù)緩沖區(qū),在使用觸發(fā)緩沖區(qū)模式時會推送到用戶空間。使用iio_triggered_buffer_setup函數(shù)啟用觸發(fā)緩沖區(qū)支持時,它會自動分配并與您的設備關聯(lián)。
-
scan_bytes: 這是捕獲并饋送到緩沖區(qū)的字節(jié)數(shù)。當從用戶空間使用觸發(fā)緩沖區(qū)時,緩沖區(qū)應至少為indio-> scan_bytes字節(jié)大。
-
available_scan_masks: 這是允許的位掩碼的可選數(shù)組。使用觸發(fā)緩沖器時,可以啟用通道捕獲并將其饋入IIO緩沖區(qū)。如果您不想允許某些通道啟用,則應僅使用允許的通道填充此數(shù)組。以下是為加速度計(帶有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: 這是啟用通道的位掩碼。只有來自這些通道的數(shù)據(jù)能被推入緩沖區(qū)。例如,對于8通道ADC轉(zhuǎn)換器,如果只啟用第一個(0),第三個(2)和最后一個(7)通道,則位掩碼將為0b10000101(0x85)。active_scan_mask將設置為0x85。然后,驅(qū)動程序可以使用for_each_set_bit宏遍歷每個設置位,根據(jù)通道獲取數(shù)據(jù),并填充緩沖區(qū)。
-
scan_timestamp: 這告訴我們是否將捕獲時間戳推入緩沖區(qū)。如果為true,則將時間戳作為緩沖區(qū)的最后一個元素。時間戳大8字節(jié)(64位)。
-
trig: 這是當前設備觸發(fā)器(支持緩沖模式時)。
-
pollfunc:這是在接收的觸發(fā)器上運行的函數(shù)。
-
channels: 這表示通道規(guī)范結(jié)構(gòu),用于描述設備具有的每個通道。
-
num_channels: 這表示通道中指定的通道數(shù)。
-
name: 這表示設備名稱。
-
info: 來自驅(qū)動程序的回調(diào)和持續(xù)信息。
-
setup_ops: 啟用/禁用緩沖區(qū)之前和之后調(diào)用的回調(diào)函數(shù)集。這個結(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)建的關聯(lián)字符設備。
用于為IIO設備分配內(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的設備,sizeof_priv是用于為任何私有結(jié)構(gòu)分配的內(nèi)存空間。這樣,傳遞每個設備(私有)數(shù)據(jù)結(jié)構(gòu)非常簡單。如果分配失敗,該函數(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設備存儲器之后,下一步是填充不同的字段。完成后,必須使用iio_device_register函數(shù)向IIO子系統(tǒng)注冊設備:
int?iio_device_register(struct?iio_dev?*indio_dev)
???????//devm_iio_device_register(dev,?indio_dev)
/*?Resource-managed?iio_device_register()?*/
在執(zhí)行此功能后,設備將準備好接受來自用戶空間的請求。反向操作(通常在釋放函數(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)
一旦取消注冊,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ù)據(jù):
?struct?my_private_data?*the_data?=?iio_priv(indio_dev);
iio_info結(jié)構(gòu)體
struct iio_info結(jié)構(gòu)用于聲明IIO內(nèi)核使用的鉤子,以讀取/寫入通道/屬性值:
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),通常設置為THIS_MODULE。
-
attrs: 這表示設備屬性。
-
read_raw: 這是用戶讀取設備sysfs文件屬性時的回調(diào)運行。mask參數(shù)是一個位掩碼,它允許我們知道請求了哪種類型的值。channel參數(shù)讓我們知道相關的通道。它可以是采樣頻率,用于將原始值轉(zhuǎn)換為可用值的比例,或原始值本身。
-
write_raw: 這是用于將值寫入設備的回調(diào)。例如,可以使用它來設置采樣頻率。
以下代碼顯示了如何設置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ù)據(jù)。
?*/
indio_dev->info?=?&iio_dummy_info;
IIO channels
通道代表單條采集線。例如加速度計具有3個通道(X,Y,Z),因為每個軸代表單個采集線。struct iio_chan_spec是表示和描述內(nèi)核中單個通道的結(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;
????};
各個參數(shù)意義:
-
type: 這指定了通道的測量類型。在電壓測量的情況下,它應該是IIO_VOLTAGE。對于光傳感器,它是IIO_LIGHT。對于加速度計,使用IIO_ACCEL。所有可用類型都在include / uapi / linux / iio / types.h中定義,如enum iio_chan_type。要為給定轉(zhuǎn)換器編寫驅(qū)動程序,請查看該文件以查看每個通道所屬的類型。
-
channel: 這指定.indexed設置為1時的通道索引。
-
channel2: 這指定.modified設置為1時的通道修飾。
-
modified: 這指定是否將修飾符應用于此通道屬性名稱。在這種情況下,修飾符設置在.channel2中。(例如,IIO_MOD_X,IIO_MOD_Y,IIO_MOD_Z是關于xyz軸的軸向傳感器的修改器)??捎眯揎椃斜碓趦?nèi)核IIO頭中定義為枚舉iio_modifier。修飾符只會破壞sysfs中的通道屬性名稱,而不是值。
-
indexed: 這指定通道屬性名稱是否具有索引。如果是,則在.channel字段中指定索引。
-
scan_index and scan_type: 當使用緩沖區(qū)觸發(fā)器時,這些字段用于標識緩沖區(qū)中的元素。scan_index設置緩沖區(qū)內(nèi)捕獲的通道的位置。具有較低scan_index的通道將放置在具有較高索引的通道之前。將.scan_index設置為-1將阻止通道進行緩沖捕獲(scan_elements目錄中沒有條目)。
暴露給用戶空間的通道sysfs屬性以位掩碼的形式指定。根據(jù)共享信息,可以將屬性設置為以下掩碼之一:
-
info_mask_separate 將屬性標記為特定于此通
-
info_mask_shared_by_type 將該屬性標記為由相同類型的所有通道共享。導出的信息由相同類型的所有通道共享。
-
info_mask_shared_by_dir 將屬性標記為由同一方向的所有通道共享。導出的信息由同一方向的所有通道共享。
-
info_mask_shared_by_all 將屬性標記為所有通道共享,無論其類型或方向如何。導出的信息由所有渠道共享。用于枚舉這些屬性的位掩碼都在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é)序字段應為以下之一:
enum?iio_endian?{
??????IIO_CPU,
??????IIO_BE,
??????IIO_LE,
};
通道屬性命名約定
屬性的名稱由IIO核心自動生成,具有以下模式:{direction} _ {type} _ {index} _ {modifier} _ {info_mask}:
-
direction方向?qū)趯傩苑较?,根?jù)drivers / iio / industrialio-core.c中的struct iio_direction結(jié)構(gòu):
static?const?char?*?const?iio_direction[]?=?{
???[0]?=?"in",
???[1]?=?"out",?
};?
-
type對應于通道類型,根據(jù)char數(shù)組const iio_chan_type_name_spec:
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",
};
-
lindex 索引模式取決于是否設置了通道.indexed字段。如果設置,索引將從.channel字段中獲取,以替換{index}模式。 -
modifier 模式取決于通道所設置的.modified字段。如果設置,修飾符將從.channel2字段中獲取,{modifier}模式將根據(jù)char數(shù)組struct iio_modifier_names結(jié)構(gòu)替換:
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依賴于這些共享的對,然后分離*/
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",
???[...]
};
通道區(qū)分
當每種通道類型有多個數(shù)據(jù)通道時,您可能會遇到麻煩。困境將是:如何識別它們。有兩種解決方案:索引和修飾符。
使用索引:給定具有一個通道線的ADC器件,不需要索引。通道定義如下:
static?const?struct?iio_chan_spec?adc_channels[]?=?{
????????????????{
????????????????????????.type?=?IIO_VOLTAGE,
????????????????????????.info_mask_separate?=?BIT(IIO_CHAN_INFO_RAW),
???????????????},
}
由前面描述的通道產(chǎn)生的屬性名稱將是in_voltage_raw。
/sys/bus/iio/iio:deviceX/in_voltage_raw
現(xiàn)在讓我們看一下有4個甚至8個通道的轉(zhuǎn)換器。我們?nèi)绾巫R別它們?解決方案是使用索引。將.indexed字段設置為1將使用.channel值替換{index}模式來替換通道屬性名稱:
??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
使用修飾符:給定一個帶有兩個通道的光傳感器 - 一個用于紅外光,一個用于紅外和可見光,沒有索引或修改器,屬性名稱將為in_intensity_raw。在這里使用索引可能容易出錯,因為使用in_intensity0_ir_raw和in_intensity1_ir_raw是沒有意義的。使用修飾符將有助于提供有意義的屬性名稱。通道的定義如下:
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?用于測量IR強度的通道
l?/sys/bus/iio/iio:deviceX/in_intensity_both_raw用于測量紅外和可見光的通道
l?/sys/bus/iio/iio:deviceX/in_illuminance_input用于處理后的數(shù)據(jù)
l?/sys/bus/iio/iio:deviceX/sampling_frequency?用于采樣頻率,由所有人共享
這也適用于加速度計,我們將在案例研究中進一步了解?,F(xiàn)在,讓我們總結(jié)一下我們到目前為止在虛擬IIO驅(qū)動程序中討論過的內(nèi)容。
總結(jié)
讓我們總結(jié)一下迄今為止我們在一個簡單的虛擬驅(qū)動器中看到的內(nèi)容,它將暴露出四個電壓通道。我們將忽略read()或write()函數(shù):
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#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?" );
MODULE_LICENSE("GPL");
加載上述模塊后, 我們將有以下輸出, 顯示我們的設備確實對應于我們已注冊的平臺設備:
??~#?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
下面的列表顯示了此設備的通道及其名稱, 這些通道與我們在驅(qū)動程序中描述的內(nèi)容完全對應:
?~#?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
飛機上一般是什么操作系統(tǒng)?
高速CAN、容錯CAN、LIN總線有什么區(qū)別?
大佬終于把鴻蒙OS講明白了,收藏了!
免責聲明:本文內(nèi)容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!