Timeline
Timeline
2026-02-15
init
This article introduces the IIO (Industrial Input/Output) subsystem in the Linux kernel, which is specifically used to handle ADC, DAC, and other industrial sensor devices. The article first explains the development background and characteristics of the IIO subsystem, including providing a unified interface, supporting multiple device types, and event and trigger mechanisms. Then, taking the RK3568 platform as an example, it analyzes in detail the registration process of IIO devices: finding the ADC node from the device tree, matching the driver file, and by`struct iio_dev`describing the IIO device, and using`struct rockchip_saradc`as private data associated with the IIO device. The article also analyzes the memory allocation in the probe function, the export of the device matching table, the definition of the channel specification array, and`devm_iio_device_register()`the specific calling process of the registration function, demonstrating how the IIO subsystem uniformly manages and operates various industrial I/O devices.
Linux Driver Notes
| Table of Contents | Links |
|---|---|
| 1. Linux Driver Framework | |
| 2. Linux Driver Loading Logic | |
| 3. Character Device Basics | |
| 4. Concurrency and Race Conditions | |
| 5. Advanced Character Device Topics | |
| 6. Interrupts | |
| 7. Platform Bus | |
| 8. Device Tree | |
| 9. Device Model | |
| 10. Hotplug | |
| 11. pinctrl Subsystem | |
| 12. GPIO subsystem | |
| 13. Input subsystem | |
| 14. 1-Wire | |
| 15. I2C | |
| 16. SPI | |
| 17. UART | |
| 18. PWM | |
| 19. RTC | |
| 20. Watchdog | |
| 21. CAN | |
| 22. Network devices | |
| 23. ADC | |
| 24. IIO | |
| 25. USB | |
| 26. LCD |
Introduction to the IIO Subsystem
IIO(Industrial Input/Output, Industrial Input/Output) is a subsystem in the Linux kernel, specifically used to handle devices related to analog-to-digital converters (ADC), digital-to-analog converters (DAC), and other industrial sensors.
The development of the IIO subsystem began in 2009. With the development of embedded systems and the Internet of Things (IoT) technology, more and more devices need to interact with analog or digital signals. For example:
- Analog-to-digital conversion (ADC): converts analog signals (such as temperature, pressure, light intensity, etc.) into digital signals.
- Digital-to-analog conversion (DAC): converts digital signals into analog signals (such as controlling motor speed or volume).
- Other sensors: accelerometers, gyroscopes, magnetometers, etc.
These devices have different functions, but all need to interact with the operating system. Without a unified framework, each device would require a separate driver, leading to code duplication, maintenance difficulties, and performance issues. Therefore, the Linux community introduced the IIO subsystem to solve these problems.
Features of IIO:
- Unified interface: IIO provides a standardized API and data structures, allowing different types of devices to interact using the same interface. User-space applications can access IIO devices through the standard file system interface (
/sys/bus/iio/devices/) to access IIO devices, thereby achieving unified management and operation of hardware such as sensors and converters. - Support for multiple device types: The IIO subsystem can support multiple device types, including ADC (analog-to-digital converter), DAC (digital-to-analog converter), temperature sensors, light sensors, pressure sensors, as well as accelerometers and gyroscopes. This broad device compatibility makes IIO an ideal choice for handling industrial and consumer-grade sensors.
- Event and trigger mechanisms: IIO supports event-based trigger mechanisms, such as triggering an interrupt when a sensor value exceeds a threshold. It also supports hardware triggers and software triggers, suitable for real-time data acquisition scenarios and capable of meeting time-sensitive application requirements.
Analysis of the IIO Registration Process
First, find the ADC device node in the RK3568 device tree. The specific content is as follows:
1234567891011121314151617181920212223242526272829303132333435363738394041424344 | //ADC Buttons adc_keys: adc-keys { compatible = "adc-keys"; io-channels = <&saradc 0>; io-channel-names = "buttons"; keyup-threshold-microvolt = <1800000>; poll-interval = <100>; vol-up-key { label = "volume up"; linux,code = <KEY_VOLUMEUP>; press-threshold-microvolt = <1750>; }; vol-down-key { label = "volume down"; linux,code = <KEY_VOLUMEDOWN>; press-threshold-microvolt = <297500>; }; menu-key { label = "menu"; linux,code = <KEY_MENU>; press-threshold-microvolt = <980000>; }; back-key { label = "back"; linux,code = <KEY_BACK>; press-threshold-microvolt = <1305500>; }; };saradc: saradc@fe720000 { compatible = "rockchip,rk3568-saradc", "rockchip,rk3399-saradc"; reg = <0x0 0xfe720000 0x0 0x100>; interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru CLK_SARADC>, <&cru PCLK_SARADC>; clock-names = "saradc", "apb_pclk"; resets = <&cru SRST_P_SARADC>; reset-names = "saradc-apb"; status = "disabled";}; |
saradc refers to Successive Approximation Register Analog-to-Digital Converter, a successive approximation type analog-to-digital converter.
Through thesaradcofcompatibleThe matching value finds the corresponding driver file asdrivers/iio/adc/rockchip_saradc.c
struct iio_dev
struct iio_devA variable of type, which is used to describe the IIO device and contains all key information and resources of the device. It is specifically defined ininclude/linux/iio/iio.hfile. Through this structure, the IIO subsystem can uniformly manage and operate various industrial I/O devices (such as ADC, DAC, sensors, etc.)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 | /** * struct iio_dev - industrial I/O device * @id: [INTERN] used to identify device internally * @driver_module: [INTERN] used to make it harder to undercut users * @modes: [DRIVER] operating modes supported by device * @currentmode: [DRIVER] current operating mode * @dev: [DRIVER] device structure, should be assigned a parent * and owner * @buffer: [DRIVER] any buffer present * @scan_bytes: [INTERN] num bytes captured to be fed to buffer demux * @mlock: [INTERN] lock used to prevent simultaneous device state * changes * @available_scan_masks: [DRIVER] optional array of allowed bitmasks * @masklength: [INTERN] the length of the mask established from * channels * @active_scan_mask: [INTERN] union of all scan masks requested by buffers * @scan_timestamp: [INTERN] set if any buffers have requested timestamp * @scan_index_timestamp:[INTERN] cache of the index to the timestamp * @trig: [INTERN] current device trigger (buffer modes) * @trig_readonly: [INTERN] mark the current trigger immutable * @pollfunc: [DRIVER] function run on trigger being received * @pollfunc_event: [DRIVER] function run on events trigger being received * @channels: [DRIVER] channel specification structure table * @num_channels: [DRIVER] number of channels specified in @channels. * @name: [DRIVER] name of the device. * @label: [DRIVER] unique name to identify which device this is * @info: [DRIVER] callbacks and constant info from driver * @clock_id: [INTERN] timestamping clock posix identifier * @info_exist_lock: [INTERN] lock to prevent use during removal * @setup_ops: [DRIVER] callbacks to call before and after buffer * enable/disable * @chrdev: [INTERN] associated character device * @groups: [INTERN] attribute groups * @groupcounter: [INTERN] index of next attribute group * @flags: [INTERN] file ops related flags including busy flag. * @priv: [DRIVER] reference to driver's private information * **MUST** be accessed **ONLY** via iio_priv() helper */struct iio_dev { int id; // Device unique identifier struct module *driver_module; // Pointer to the driver module int modes; // Supported operating modes (e.g., direct mode, buffered mode) int currentmode; // Current operating mode struct device dev; // Nested Linux device model structure struct iio_buffer *buffer; // Data buffer pointer int scan_bytes; // Number of bytes of scan data struct mutex mlock; // Mutex, protects shared resources const unsigned long *available_scan_masks; // Available scan mask unsigned masklength; // Mask length const unsigned long *active_scan_mask; // Currently active scan mask bool scan_timestamp; // Whether to include timestamp in scan data unsigned scan_index_timestamp; // Index of timestamp in scan data struct iio_trigger *trig; // Trigger pointer bool trig_readonly; // Whether the trigger is read-only struct iio_poll_func *pollfunc; // Poll function pointer struct iio_poll_func *pollfunc_event; // Event poll function pointer struct iio_chan_spec const *channels; // Channel specification array int num_channels; // Number of channels const char *name; // device name const char *label; const struct iio_info *info; // IIO device operation function set clockid_t clock_id; // Clock ID struct mutex info_exist_lock; // Information existence lock const struct iio_buffer_setup_ops *setup_ops; // Buffer setup operations function set struct cdev chrdev; // Character device structure const struct attribute_group *groups[IIO_MAX_GROUPS + 1]; // Attribute group array int groupcounter; // Attribute group counter unsigned long flags; // Flag bits, used to store device state void *priv;}; |
Can bestruct iio_devViewed as a base class, it is used to describe IIO devices, but ADC and DAC devices both belong to IIO. IIO actually distinguishes different devices through the probe function’sstruct rockchip_saradcdescribed by the structure,rockchip_saradcThe specific content of the structure is as follows:
struct rockchip_saradc
1234567891011121314151617181920212223242526272829303132 | struct rockchip_saradc;struct rockchip_saradc_data { const struct iio_chan_spec *channels; int num_channels; unsigned long clk_rate; void (*start)(struct rockchip_saradc *info, int chn); int (*read)(struct rockchip_saradc *info); void (*power_down)(struct rockchip_saradc *info);};struct rockchip_saradc { void __iomem *regs; // Virtual address mapping of registers, used to access hardware registers struct clk *pclk; // APB bus clock pointer struct clk *clk; // SARADC converter clock pointer struct completion completion; // Completion variable, used for synchronization operations (such as waiting for conversion to complete) struct regulator *vref; // Reference voltage regulator pointer int uv_vref; // Actual value of the reference voltage (unit: microvolt) struct reset_control *reset; // Reset controller pointer const struct rockchip_saradc_data *data; // Device-specific data (such as configuration parameters) u16 last_val; // Last ADC conversion result const struct iio_chan_spec *last_chan; struct notifier_block nb; bool suspended; // Flag indicating whether the device is in a suspended state bool test; // Timer, used for testing channels u32 chn; // Flag indicating whether it is in test mode spinlock_t lock; // Spinlock, used to protect shared resources struct workqueue_struct *wq; struct delayed_work work;}; |
rockchip_saradc_probe()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 | // drivers/iio/adc/rockchip_saradc.c`static int rockchip_saradc_probe(struct platform_device *pdev){ struct rockchip_saradc *info = NULL; // Define the SARADC device private data structure pointer struct device_node *np = pdev->dev.of_node; // Get the device tree node struct iio_dev *indio_dev = NULL; // Define the IIO device structure pointer struct resource *mem; // Used to obtain memory resources const struct of_device_id *match; // Used to match device tree nodes int ret; int irq; // Check whether the device tree node exists if (!np) return -ENODEV; // Allocate the IIO device structure and allocate private data space for it indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); if (!indio_dev) { dev_err(&pdev->dev, "failed allocating iio device\n"); return -ENOMEM; } info = iio_priv(indio_dev); // Get the private data area of the IIO device // Match the device ID in the device tree match = of_match_device(rockchip_saradc_match, &pdev->dev); if (!match) { dev_err(&pdev->dev, "failed to match device\n"); return -ENODEV; } info->data = match->data; // Get the device matching data (such as configuration parameters) /* Sanity check for possible later IP variants with more channels */ if (info->data->num_channels > SARADC_MAX_CHANNELS) { dev_err(&pdev->dev, "max channels exceeded"); return -EINVAL; } // Get the device's register address resources mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); // Map registers to virtual address space info->regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); /* * The reset should be an optional property, as it should work * with old devicetrees as well */ /* * Reset controller is an optional property,For compatibility with old device trees,Allow this property to be omitted */ info->reset = devm_reset_control_get_exclusive(&pdev->dev, "saradc-apb"); if (IS_ERR(info->reset)) {// Failed to get reset controller ret = PTR_ERR(info->reset); if (ret != -ENOENT) return ret; dev_dbg(&pdev->dev, "no reset control found\n"); info->reset = NULL; } // Initialize completion for synchronization operations init_completion(&info->completion); // Get interrupt number irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; // Register interrupt handler ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr, 0, dev_name(&pdev->dev), info); if (ret < 0) { dev_err(&pdev->dev, "failed requesting irq %d\n", irq); return ret; } // Get the APB bus clock info->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); if (IS_ERR(info->pclk)) { dev_err(&pdev->dev, "failed to get pclk\n"); return PTR_ERR(info->pclk); } // Get the SARADC converter clock info->clk = devm_clk_get(&pdev->dev, "saradc"); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get adc clock\n"); return PTR_ERR(info->clk); } // Get the reference voltage regulator info->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(info->vref)) { dev_err(&pdev->dev, "failed to get regulator, %ld\n", PTR_ERR(info->vref)); return PTR_ERR(info->vref); } // If a reset controller exists, perform reset operation if (info->reset) rockchip_saradc_reset_controller(info->reset); /* * Use a default value for the converter clock. * This may become user-configurable in the future. */ /* * Set the converter clock frequency,Use the clock frequency in device data by default * May support user configuration in the future */ ret = clk_set_rate(info->clk, info->data->clk_rate); if (ret < 0) { dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret); return ret; } // Enable the reference voltage regulator ret = regulator_enable(info->vref); if (ret < 0) { dev_err(&pdev->dev, "failed to enable vref regulator\n"); return ret; } // Register a cleanup action to disable the regulator when the device is removed ret = devm_add_action_or_reset(&pdev->dev, rockchip_saradc_regulator_disable, info); if (ret) { dev_err(&pdev->dev, "failed to register devm action, %d\n", ret); return ret; } // Get the actual value of the reference voltage ret = regulator_get_voltage(info->vref); if (ret < 0) { dev_err(&pdev->dev, "failed to get voltage\n"); return ret; } // Assign the actual reference voltage value to info->uv_vref info->uv_vref = ret; // Enable the APB bus clock ret = clk_prepare_enable(info->pclk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable pclk\n"); return ret; } // Register a cleanup action to disable the APB clock when the device is removed ret = devm_add_action_or_reset(&pdev->dev, rockchip_saradc_pclk_disable, info); if (ret) { dev_err(&pdev->dev, "failed to register devm action, %d\n", ret); return ret; } // Enable the SARADC converter clock ret = clk_prepare_enable(info->clk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable converter clock\n"); return ret; } // Register a cleanup action to disable the converter clock when the device is removed ret = devm_add_action_or_reset(&pdev->dev, rockchip_saradc_clk_disable, info); if (ret) { dev_err(&pdev->dev, "failed to register devm action, %d\n", ret); return ret; } // Associate the IIO device with the platform device platform_set_drvdata(pdev, indio_dev); // Initialize the basic information of the IIO device indio_dev->name = dev_name(&pdev->dev); // device name indio_dev->info = &rockchip_saradc_iio_info; // IIO device operation function set indio_dev->modes = INDIO_DIRECT_MODE; // Set the working mode to direct mode indio_dev->channels = info->data->channels; // Channel list indio_dev->num_channels = info->data->num_channels; // Number of channels ret = devm_iio_triggered_buffer_setup(&indio_dev->dev, indio_dev, NULL, rockchip_saradc_trigger_handler, NULL); if (ret) return ret; info->nb.notifier_call = rockchip_saradc_volt_notify; ret = regulator_register_notifier(info->vref, &info->nb); if (ret) return ret; ret = devm_add_action_or_reset(&pdev->dev, rockchip_saradc_regulator_unreg_notifier, info); if (ret) return ret; info->wq = create_singlethread_workqueue("adc_wq"); // Initialize a work queue INIT_DELAYED_WORK(&info->work, rockchip_saradc_test_work); // Initialize a delayed work spin_lock_init(&info->lock); ret = sysfs_create_group(&pdev->dev.kobj, &rockchip_saradc_attr_group); if (ret) return ret; // Register a cleanup action to delete the sysfs attribute group when the device is removed ret = devm_add_action_or_reset(&pdev->dev, rockchip_saradc_remove_sysgroup, pdev); if (ret) { dev_err(&pdev->dev, "failed to register devm action, %d\n", ret); return ret; } ret = devm_add_action_or_reset(&pdev->dev, rockchip_saradc_destroy_wq, info); if (ret) { dev_err(&pdev->dev, "failed to register destroy_wq, %d\n", ret); return ret; } // Register the IIO device return devm_iio_device_register(&pdev->dev, indio_dev);} |
It can be seen thatstruct rockchip_saradcThere is no iio-related description in the structure, so how is it connected to the iio device?
In the probe function, first, throughdevm_iio_device_allocThe function allocates a contiguous block of memory to storeiio_devand its private data (rockchip_saradc). Then, useiio_privthe function to get a pointer to the private data area, and the twoiio_devandrockchip_saradctypes of structure variables are connected together, as shown in the following code:
1234567 | // Allocate the IIO device structure and allocate private data space for itindio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));if (!indio_dev) { dev_err(&pdev->dev, "failed allocating iio device\n"); return -ENOMEM;}info = iio_priv(indio_dev); // Get the private data area of the IIO device |
In the probe source code, in order to match multiple SoC chips, throughof_match_devicethe function, with the value passed from the device tree,rockchip_saradc_matchperforms matching:
12345 | match = of_match_device(rockchip_saradc_match, &pdev->dev);if (!match) { dev_err(&pdev->dev, "failed to match device\n"); return -ENODEV;} |
rockchip_saradc_matchThe content of the structure array variable is as follows
1234567891011121314151617181920212223242526272829 | static const struct of_device_id rockchip_saradc_match[] = { { .compatible = "rockchip,saradc", .data = &saradc_data, }, { .compatible = "rockchip,rk3066-tsadc", .data = &rk3066_tsadc_data, }, { .compatible = "rockchip,rk3399-saradc", .data = &rk3399_saradc_data, }, { .compatible = "rockchip,rk3528-saradc", .data = &rk3528_saradc_data, }, { .compatible = "rockchip,rk3562-saradc", .data = &rk3562_saradc_data, }, { .compatible = "rockchip,rk3568-saradc", .data = &rk3568_saradc_data, }, { .compatible = "rockchip,rk3588-saradc", .data = &rk3588_saradc_data, }, { .compatible = "rockchip,rv1106-saradc", .data = &rv1106_saradc_data, }, {},};MODULE_DEVICE_TABLE(of, rockchip_saradc_match); |
MODULE_DEVICE_TABLE()is a macro in the Linux kernel, used to export the device match table into the module’s ELF metadata, so that the user-space automatic loading mechanism (udev / modprobe) knows:
- Which devices does this driver support?
- When the kernel detects the corresponding device, this module can be loaded automatically.
The data corresponding to rk3568 points tork3568_saradc_dataThe specific content of this variable is as follows. The main purpose of this code is to define the channel information and specific device parameters of a SARADC device.
1234567891011 | static const struct rockchip_saradc_data rk3568_saradc_data = { // points to the channel specification array .channels = rockchip_rk3568_saradc_iio_channels, // Number of channels .num_channels = ARRAY_SIZE(rockchip_rk3568_saradc_iio_channels), // Converter clock frequency: 1 MHz .clk_rate = 1000000, .start = rockchip_saradc_start_v1, .read = rockchip_saradc_read_v1, .power_down = rockchip_saradc_power_down_v1,}; |
rockchip_rk3568_saradc_iio_channelsRepresents the channel specification array. The specific content of this array is as follows. It can be seen that RK3568 has 8 ADC channels.
12345678910 | static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = { SARADC_CHANNEL(0, "adc0", 10), // Define ADC channel 0, named "adc0" SARADC_CHANNEL(1, "adc1", 10), // Define ADC channel 1, named "adc1" SARADC_CHANNEL(2, "adc2", 10), // Define ADC channel 2, named "adc2" SARADC_CHANNEL(3, "adc3", 10), // Define ADC channel 3, named "adc3" SARADC_CHANNEL(4, "adc4", 10), // Define ADC channel 4, named "adc4" SARADC_CHANNEL(5, "adc5", 10), // Define ADC channel 5, named "adc5" SARADC_CHANNEL(6, "adc6", 10), // Define ADC channel 6, named "adc6" SARADC_CHANNEL(7, "adc7", 10), // Define ADC channel 7, named "adc7"}; |
The structure used to describe channel rules isstruct iio_chan_specIt defines the channel type, number, data format, supported attributes and events, etc., and is used to distinguish different types of ADC or DAC devices (such as ADC buttons, ambient light sensors, accelerometers, etc.). The specific content of this structure is as follows:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 | /** * struct iio_chan_spec - specification of a single channel * @type: What type of measurement is the channel making. * @channel: What number do we wish to assign the channel. * @channel2: If there is a second number for a differential * channel then this is it. If modified is set then the * value here specifies the modifier. * @address: Driver specific identifier. * @scan_index: Monotonic index to give ordering in scans when read * from a buffer. * @scan_type: struct describing the scan type * @scan_type.sign: 's' or 'u' to specify signed or unsigned * @scan_type.realbits: Number of valid bits of data * @scan_type.storagebits: Realbits + padding * @scan_type.shift: Shift right by this before masking out * realbits. * @scan_type.repeat: Number of times real/storage bits repeats. * When the repeat element is more than 1, then * the type element in sysfs will show a repeat * value. Otherwise, the number of repetitions * is omitted. * @scan_type.endianness: little or big endian * @info_mask_separate: What information is to be exported that is specific to * this channel. * @info_mask_separate_available: What availability information is to be * exported that is specific to this channel. * @info_mask_shared_by_type: What information is to be exported that is shared * by all channels of the same type. * @info_mask_shared_by_type_available: What availability information is to be * exported that is shared by all channels of the same * type. * @info_mask_shared_by_dir: What information is to be exported that is shared * by all channels of the same direction. * @info_mask_shared_by_dir_available: What availability information is to be * exported that is shared by all channels of the same * direction. * @info_mask_shared_by_all: What information is to be exported that is shared * by all channels. * @info_mask_shared_by_all_available: What availability information is to be * exported that is shared by all channels. * @event_spec: Array of events which should be registered for this * channel. * @num_event_specs: Size of the event_spec array. * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should * have its name field set to NULL. * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. * @datasheet_name: A name used in in-kernel mapping of channels. It should * correspond to the first name that the channel is referred * to by in the datasheet (e.g. IND), or the nearest * possible compound name (e.g. IND-INC). * @modified: Does a modifier apply to this channel. What these are * depends on the channel type. Modifier is set in * channel2. Examples are IIO_MOD_X for axial sensors about * the 'x' axis. * @indexed: Specify the channel has a numerical index. If not, * the channel index number will be suppressed for sysfs * attributes but not for event codes. * @output: Channel is output. * @differential: Channel is differential. */struct iio_chan_spec { enum iio_chan_type type; int channel; int channel2; unsigned long address; int scan_index; struct { char sign; u8 realbits; u8 storagebits; u8 shift; u8 repeat; enum iio_endian endianness; } scan_type; long info_mask_separate; long info_mask_separate_available; long info_mask_shared_by_type; long info_mask_shared_by_type_available; long info_mask_shared_by_dir; long info_mask_shared_by_dir_available; long info_mask_shared_by_all; long info_mask_shared_by_all_available; 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;} |
The definition of the RK3568 ADC channel uses theSARADC_CHANNELmacro, and the specific content of this macro is as follows:
123456789101112131415 | .indexed = 1, \ .channel = _index, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .datasheet_name = _id, \ .scan_index = _index, \ .scan_type = { \ .sign = 'u', \ .realbits = _res, \ .storagebits = 16, \ .endianness = IIO_CPU, \ }, \} |
.type = IIO_VOLTAGEChannel type: voltage measurement.indexed = 1Enable index mode, indicating thatchannelfield is used as the index.channel = _indexMain channel number, specified by the macro parameter_indexspecified.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)Individually supported attribute: raw value (RAW).info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)Attribute shared by type: scale.datasheet_name = _idThe name in the datasheet, determined by the macro parameter_idspecified
devm_iio_device_register()
The probe function finally returnsreturn devm_iio_device_register(&pdev->dev, indio_dev);, anddevm_iio_device_registerdefined as follows
123456789101112131415 | /** * devm_iio_device_register - Resource-managed iio_device_register() * @dev: Device to allocate iio_dev for * @indio_dev: Device structure filled by the device driver * * Managed iio_device_register. The IIO device registered with this * function is automatically unregistered on driver detach. This function * calls iio_device_register() internally. Refer to that function for more * information. * * RETURNS: * 0 on success, negative error number on failure. */ |
__devm_iio_device_register()
123456789101112131415161718192021222324252627 | // drivers/iio/industrialio-core.cint __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, struct module *this_mod){ struct iio_dev **ptr; // Define a pointer variable to store the allocated resource management structure. int ret; // Use devres_alloc allocates the device resource management structure and specifies the release function as devm._iio_device_unreg // sizeof(*ptr) indicates that the allocated size is the size of an iio_dev pointer. ptr = devres_alloc(devm_iio_device_unreg, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; // Point the allocated resource management pointer to the passed-in iio_dev structure. *ptr = indio_dev; // Call the underlying device registration function. __iio_device_register registers iio_dev device ret = __iio_device_register(indio_dev, this_mod); if (!ret) devres_add(dev, ptr);// If registration succeeds, add the allocated resource management structure to the device's resource management linked list. else devres_free(ptr);// If registration fails, release the previously allocated resource management structure. // Return the result of registration. return ret;}EXPORT_SYMBOL_GPL(__devm_iio_device_register); |
__iio_device_register()
__devm_iio_device_registercall in__iio_device_registerto register the IIO device
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 | // drivers/iio/industrialio-core.cint __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod){ int ret; if (!indio_dev->info) return -EINVAL; // Assign the current module pointer to indio._dev's driver_module field indio_dev->driver_module = this_mod; /* If the calling driver did not initialize of_node, do it here */ /* If the calling driver has not initialized of_node, initialize it here. */ // If the device's of_node is NULL, but its parent device's of_node exists, then inherit the parent device's of_node. if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; indio_dev->label = of_get_property(indio_dev->dev.of_node, "label", NULL); // Check whether the scan index is unique. ret = iio_check_unique_scan_index(indio_dev); if (ret < 0) return ret; /* configure elements for the chrdev */ /* Configure the relevant elements of the character device. */ // Generate the device number using the major and minor device numbers. indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); // Register debugfs interface. iio_device_register_debugfs(indio_dev); // Allocate buffer-related sysfs interfaces and masks. ret = iio_buffer_alloc_sysfs_and_mask(indio_dev); if (ret) { dev_err(indio_dev->dev.parent, "Failed to create buffer sysfs interfaces\n"); goto error_unreg_debugfs; } // Register the device's sysfs interface. ret = iio_device_register_sysfs(indio_dev); if (ret) { dev_err(indio_dev->dev.parent, "Failed to register sysfs interfaces\n"); goto error_buffer_free_sysfs; } // Register event set. ret = iio_device_register_eventset(indio_dev); if (ret) { dev_err(indio_dev->dev.parent, "Failed to register event set\n"); goto error_free_sysfs; } // If the device supports all trigger modes, register the trigger consumer if (indio_dev->modes & INDIO_ALL_TRIGGERED_MODES) iio_device_register_trigger_consumer(indio_dev); // If the device supports all buffer modes and setup is not set_ops, then use the default noop._ring_setup_ops if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) && indio_dev->setup_ops == NULL) indio_dev->setup_ops = &noop_ring_setup_ops; // Initialize the character device structure and bind file operation functions. cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); // Set the module owner of the character device. indio_dev->chrdev.owner = this_mod; // Add the character device and device structure to the system. ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev); if (ret < 0) goto error_unreg_eventset; return 0;error_unreg_eventset: iio_device_unregister_eventset(indio_dev); // Unregister event set.error_free_sysfs: iio_device_unregister_sysfs(indio_dev); // Unregister sysfs interface.error_buffer_free_sysfs: iio_buffer_free_sysfs_and_mask(indio_dev); // Release buffer-related sysfs interfaces and masks.error_unreg_debugfs: iio_device_unregister_debugfs(indio_dev); // Unregister debugfs interface. return ret;}EXPORT_SYMBOL(__iio_device_register); |
- Line 6: Assign the current module pointer to
indio_dev->driver_module, recording the kernel module to which the device belongs. - Lines 10-11: By checking
indio_dev->dev.of_nodeandindio_dev->dev.parent->of_nodeInitialize the device tree node (of_node), inheriting the parent device’s device tree node. - Lines 14-16: Through
iio_check_unique_scan_indexfunction checks whether the channel scan index is unique, ensuring no duplicate indices. - Line 20: Through
MKDEVfunction generates the device number, assigning unique identifiers to the major and minor devices. - Line 23: Through
iio_device_register_debugfsThe function registers the debugfs interface, used for debugging and monitoring device status. - Line 31: Through
iio_buffer_alloc_sysfs_and_maskThe function allocates buffer-related sysfs interfaces and masks, but after jumping to it, the function is found to be empty, so this part of the code has no practical significance. - Line 39: Through
iio_device_register_sysfsThe function registers the device’s sysfs interface, exposing device attributes to user space. - Line 47: Through
iio_device_register_eventsetThe function registers an event set to manage events triggered by the device. - Lines 55-56: Through
iio_device_register_trigger_consumerThe function registers a trigger consumer, enabling the device to respond to external trigger signals. - Lines 59-61: By setting
indio_dev->setup_opsisnoop_ring_setup_opsProvide default buffer operation functions. - Line 64: Through
cdev_initThe function initializes the character device structure and binds file operation functions. - Line 67: By setting
indio_dev->chrdev.owneristhis_mod, ensuring resources are released when the module is unloaded. - Line 70: Through
cdev_device_addThe function adds the character device and device structure to the system.
The focus of this function is on line 47’siio_device_register_eventsetfunction, which is used to register IIO events, defined indrivers/iio/industrialio-event.cfile, the specific content of the function is as follows
iio_device_register_eventset()
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 | int iio_device_register_eventset(struct iio_dev *indio_dev){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_event_interface *ev_int; struct iio_dev_attr *p; // Pointer used to traverse the device attribute list int ret = 0, attrcount_orig = 0, attrcount, attrn; // Define return value, original attribute count, total attribute count, and current attribute index. struct attribute **attr; // Pointer to the attribute array // If the device has no event attributes and no dynamic events, return directly. if (!(indio_dev->info->event_attrs || iio_check_for_dynamic_events(indio_dev))) return 0; // Allocate memory to store the event interface structure ev_int = kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL); if (ev_int == NULL) return -ENOMEM; iio_dev_opaque->event_interface = ev_int; // Initialize the device attribute list head of the event interface INIT_LIST_HEAD(&ev_int->dev_attr_list); // Set the interrupt handler function of the event interface iio_setup_ev_int(ev_int); // If the device has predefined event attributes, count their number if (indio_dev->info->event_attrs != NULL) { attr = indio_dev->info->event_attrs->attrs; while (*attr++ != NULL) // Iterate through the attribute array to count the number of original attributes attrcount_orig++; } // Initialize the total attribute count to the number of original attributes attrcount = attrcount_orig; // If the device has channels, try to add dynamic event configuration attributes if (indio_dev->channels) { ret = __iio_add_event_config_attrs(indio_dev); // Add dynamic event attributes if (ret < 0) // If adding fails, jump to error handling. goto error_free_setup_event_lines; attrcount += ret; // Update the total attribute count } ev_int->group.name = iio_event_group_name; // Allocate memory to store the attribute array of the event group ev_int->group.attrs = kcalloc(attrcount + 1, sizeof(ev_int->group.attrs[0]), GFP_KERNEL); // If the device has predefined event attributes, copy them to the attribute array of the event group if (ev_int->group.attrs == NULL) { ret = -ENOMEM; goto error_free_setup_event_lines; } if (indio_dev->info->event_attrs) memcpy(ev_int->group.attrs, indio_dev->info->event_attrs->attrs, sizeof(ev_int->group.attrs[0]) * attrcount_orig); // Set the current attribute index to the number of original attributes attrn = attrcount_orig; /* Add all elements from the list. */ // Iterate through the device attribute list and add all dynamic attributes to the attribute array of the event group list_for_each_entry(p, &ev_int->dev_attr_list, l) ev_int->group.attrs[attrn++] = &p->dev_attr.attr; // Add the event group to the device's attribute group list indio_dev->groups[indio_dev->groupcounter++] = &ev_int->group; return 0;error_free_setup_event_lines: // Error handling: release the device attribute list iio_free_chan_devattr_list(&ev_int->dev_attr_list); // Free the memory of the event interface kfree(ev_int); // Set the pointer to NULL iio_dev_opaque->event_interface = NULL; return ret;} |
In the IIO subsystem, common events include threshold events, data-ready events, state change events, etc. In the iio subsystem, the structure used to describe the corresponding event isstruct iio_event_interface, the specific content of this structure is as follows:
1234567891011121314151617181920212223242526272829 | /** * struct iio_event_interface - chrdev interface for an event line * @wait: wait queue to allow blocking reads of events * @det_events: list of detected events * @dev_attr_list: list of event interface sysfs attribute * @flags: file operations related flags including busy flag. * @group: event interface sysfs attribute group * @read_lock: lock to protect kfifo read operations */struct iio_event_interface { // Wait queue head, used for the event notification mechanism. User space or kernel threads can wait for events to occur through this queue. wait_queue_head_t wait; // Define a fixed-size FIFO queue (size 16) to store detected event data. // Each event data is a `struct iio_event_data` structure of type. DECLARE_KFIFO(det_events, struct iio_event_data, 16); // Device attribute list head, used to manage device attributes related to events (such as thresholds, configurations, etc.) struct list_head dev_attr_list; // Flag bit, used to record the status or characteristics of the event interface. For example, whether certain features are enabled. unsigned long flags; // Attribute group, used to organize event-related attributes (such as thresholds, configurations, etc.) together and expose them to sysfs. struct attribute_group group; // Mutex, used to protect concurrent access to event data and ensure read safety in a multi-threaded environment. struct mutex read_lock;}; |
Based on the members of this structure variable, it can be inferred that the IIO subsystem uses a FIFO queue to cache detected event data, a wait queue to implement an event notification mechanism to wake up listeners, a linked list to dynamically maintain event-related attributes and expose them to user space via sysfs for configuration and monitoring, a mutex to ensure safe concurrent access in a multi-threaded environment, and flag bits to record the status or characteristics of the event interface, thereby efficiently completing event detection, configuration, storage, notification, and processing.
IIO device driver analysis
IIO file operation set function analysis
Before__iio_device_registerCalled in the function.cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);Initialize the character device.
12345678910 | static const struct file_operations iio_buffer_fileops = { .read = iio_buffer_read_outer_addr, .release = iio_chrdev_release, .open = iio_chrdev_open, .poll = iio_buffer_poll_addr, .owner = THIS_MODULE, .llseek = noop_llseek, .unlocked_ioctl = iio_ioctl, .compat_ioctl = compat_ptr_ioctl,}; |
iio_ioctl function
12345678910111213141516171819202122232425 | /* Somewhat of a cross file organization violation - ioctls here are actually * event related */static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){ struct iio_dev *indio_dev = filp->private_data; // Get the IIO device instance from the file structure int __user *ip = (int __user *)arg; // Convert the parameters passed from user space into user space pointers int fd; // Used to store the event file descriptor // If the device's information structure is not initialized, return error code -ENODEV (no device) if (!indio_dev->info) return -ENODEV; // Determine whether it is the IOCTL command to obtain the event file descriptor if (cmd == IIO_GET_EVENT_FD_IOCTL) { fd = iio_event_getfd(indio_dev); // Call the function to obtain the event file descriptor if (fd < 0) // If acquisition fails, return the error code directly return fd; // Copy the obtained file descriptor to user space if (copy_to_user(ip, &fd, sizeof(fd))) return -EFAULT; return 0; } // If the command does not match, return error code -EINVAL (invalid argument) return -EINVAL;} |
In the ioctl function, there is only one command, which isIIO_GET_EVENT_FD_IOCTL, and then callediio_event_getfdfunction to obtain the event file descriptor, and throughcopy_to_userfunction copies the obtained file descriptor to user space,iio_event_getfdThe specific content of the function is as follows:
12345678910111213141516171819202122232425262728293031323334353637383940 | int iio_event_getfd(struct iio_dev *indio_dev){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); // Get the device's event interface struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; // Used to store the returned file descriptor int fd; // If the event interface is not initialized, return -ENODEV (no device) if (ev_int == NULL) return -ENODEV; // Attempt to acquire the mutex to ensure mutually exclusive access to device operations fd = mutex_lock_interruptible(&indio_dev->mlock); if (fd) return fd; // Check whether the event interface is already occupied (via the flag IIO_BUSY_BIT_POS check) if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { fd = -EBUSY; goto unlock; } // Increase the device's reference count to prevent the device from being released while in use. iio_device_get(indio_dev); // Create an anonymous inode file and return a file descriptor. fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, indio_dev, O_RDONLY | O_CLOEXEC); if (fd < 0) { clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); // Clear the occupancy flag. iio_device_put(indio_dev); // Decrease the device reference count. } else { kfifo_reset_out(&ev_int->det_events); // If successful, reset the output pointer of the FIFO queue to prepare for receiving new events. }unlock: mutex_unlock(&indio_dev->mlock); // Release the mutex lock return fd;} |
The return value of this function is the file descriptor fd, which isfd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops,indio_dev, O_RDONLY | O_CLOEXEC);returned,anon_inode_getfdThe function’s purpose is to create an anonymous file descriptor fd. An anonymous file descriptor is a special type of file descriptor that is not associated with an actual filesystem path or device node, but is a virtual file dynamically created by the kernel.
The anonymous file descriptor fd also has its own file operation set, so we can create virtual file descriptors through ioctl, thereby providing users with additional system call interfaces to interact with certain functions or data in the kernel without relying on the traditional filesystem.
The file operation set corresponding to the anonymous file descriptor fd isanon_inode_getfdThe second parameter of the functioniio_event_chrdev_fileops, and the corresponding specific contents are as follows:
1234567 | static const struct file_operations iio_event_chrdev_fileops = { .read = iio_event_chrdev_read, // Handle the read() system call to read event data. .poll = iio_event_poll, // Handle poll() or select() calls to monitor whether events are available. .release = iio_event_chrdev_release, // Handle the close() call to release resources. .owner = THIS_MODULE, // Specify the module owner to ensure safe module unloading. .llseek = noop_llseek, // File offset operations are not supported (lseek is invalid).}; |
iio_event_chrdev_read()
Regarding its read function,iio_event_chrdev_readan analysis is performed. The specific contents of this function are as follows:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | static ssize_t iio_event_chrdev_read(struct file *filep, char __user *buf, size_t count, loff_t *f_ps){ struct iio_dev *indio_dev = filep->private_data; // Get the IIO device instance. struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; // Get the event interface. unsigned int copied; // Record the amount of data actually copied to user space. int ret; if (!indio_dev->info) // Check whether the device has been initialized. return -ENODEV; if (count < sizeof(struct iio_event_data)) // Ensure the buffer is large enough to hold event data return -EINVAL; do { if (kfifo_is_empty(&ev_int->det_events)) { // Check whether the event queue is empty if (filep->f_flags & O_NONBLOCK) // If in non-blocking mode, return -EAGAIN directly return -EAGAIN; // Block waiting for data in the event queue or device removal ret = wait_event_interruptible(ev_int->wait, !kfifo_is_empty(&ev_int->det_events) || indio_dev->info == NULL); if (ret) // If the wait is interrupted, return an error code return ret; if (indio_dev->info == NULL) // If the device is removed, return -ENODEV return -ENODEV; } // Lock to protect access to the event queue if (mutex_lock_interruptible(&ev_int->read_lock)) return -ERESTARTSYS; // Copy event data from the kernel FIFO queue to user space ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); mutex_unlock(&ev_int->read_lock); // Unlock if (ret) // If copying fails, return an error code return ret; /* * If we couldn't read anything from the fifo (a different * thread might have been faster) we either return -EAGAIN if * the file descriptor is non-blocking, otherwise we go back to * sleep and wait for more data to arrive. */ // If no data is read and it is non-blocking mode, return -EAGAIN if (copied == 0 && (filep->f_flags & O_NONBLOCK)) return -EAGAIN; } while (copied == 0); // If no data is read, continue looping and waiting return copied; // Return the actual number of bytes read} |
The function’s do-while loop determines whether to wait for event data by checking whether the kernel FIFO event queue is empty;
In non-blocking mode, return directly-EAGAINwhile in blocking mode, it suspends the process via a wait queue until there is data or the device is removed; then it uses a mutex to protect concurrent access to the FIFO queue, ensuring thread safety;
Finally, it copies the event data from kernel space to user space, and handles special cases in non-blocking mode based on the read result. If no data is read, it continues looping and waiting until data is successfully read, then returns the actual number of bytes read or an error code.
iio_event_poll()
12345678910111213141516171819202122232425262728293031323334 | /** * iio_event_poll() - poll the event queue to find out if it has data * @filep: File structure pointer to identify the device * @wait: Poll table pointer to add the wait queue on * * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading * or a negative error code on failure */static __poll_t iio_event_poll(struct file *filep, struct poll_table_struct *wait){ // Get the IIO device structure associated with the file struct iio_dev *indio_dev = filep->private_data; struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); // Get the event interface structure of the device. struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; // Initialize the returned event mask to 0 __poll_t events = 0; // If the device has no valid info structure, return an empty event (no events) directly if (!indio_dev->info) return events; // Add the current file descriptor to the wait queue so that it can be woken up when an event occurs poll_wait(filep, &ev_int->wait, wait); // Check whether the event FIFO queue is non-empty if (!kfifo_is_empty(&ev_int->det_events)) // If there are pending events, set the POLLIN and POLLRDNORM flags events = EPOLLIN | EPOLLRDNORM; // Return the event mask return events;} |
The function usespoll_wait(filep, &ev_int->wait, wait);The function adds the current file descriptor to the wait queue so that it can be woken up when an event arrives. Then it checks whether the event FIFO queue is non-empty. If there are pending events, it setsEPOLLIN | EPOLLRDNORMThe flag indicates that data is readable, and finally returns the detected event.
iio_event_chrdev_release()
12345678910111213141516 | static int iio_event_chrdev_release(struct inode *inode, struct file *filep){ // Get the IIO device structure associated with the device from the file structure. struct iio_dev *indio_dev = filep->private_data; struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); // Get the event interface structure of the device. struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; // Clear the BUSY status bit in the event interface flags (IIO_BUSY_BIT_POS) clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); // Decrease the reference count of the IIO device, releasing the device occupation iio_device_put(indio_dev); return 0;} |
iio_event_chrdev_releaseThe function is used to perform cleanup operations when the device file is closed in user space. It marks the device as available by clearing the BUSY status flag in the event interface, and decreases the reference count of the IIO device to release resources, ensuring that the device can be safely used by other processes or further cleaned up when no longer needed.
In the IIO subsystem, multiple system calls need to be supported. An anonymous file descriptor can be created via the ioctl function, and a separate set of file operations can be bound to this anonymous file descriptor. This approach not only extends functionality but also avoids polluting the file system, because anonymous file descriptors leave no trace in the file system, thus keeping the system clean and efficient.
iio_chrdev_open()
1234567891011121314151617181920212223242526 | /** * iio_chrdev_open() - chrdev file open for buffer access and ioctls * @inode: Inode structure for identifying the device in the file system * @filp: File structure for iio device used to keep and later access * private data * * Return: 0 on success or -EBUSY if the device is already opened **/static int iio_chrdev_open(struct inode *inode, struct file *filp){ // Get the corresponding IIO device structure from the inode. struct iio_dev *indio_dev = container_of(inode->i_cdev, struct iio_dev, chrdev); // Check whether the device is already occupied; if busy, return -EBUSY. if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags)) return -EBUSY; // Increase the device reference count to prevent the device from being released. iio_device_get(indio_dev); // Store the device structure pointer in the file's private data. filp->private_data = indio_dev; return 0;} |
This functioniio_chrdev_openUsed to open the IIO character device, first throughcontainer_ofThe macro gets the corresponding IIO device structure from the inode, then checks whether the device is already occupied (by testing and setting the busy flag). If the device is busy, it returns-EBUSY. Then it callsiio_device_getThe function increases the device reference count to prevent the device from being released during use, and stores the device structure pointer in the file’s private data for subsequent operations. Finally, it returns 0 to indicate that the device was successfully opened.
iio_device_getThe specific content of the function is as follows:
12345678910 | /** * iio_device_get() - increment reference count for the device * @indio_dev: IIO device structure * * Returns: The passed IIO device **/static inline struct iio_dev *iio_device_get(struct iio_dev *indio_dev){ return indio_dev ? dev_to_iio_dev(get_device(&indio_dev->dev)) : NULL;} |
The purpose of this function is to increase the reference count of the IIO device, ensuring that the device will not be accidentally released during use, and return the device pointer for subsequent operations. It first checks whether the passed-instruct indio_devis NULL. If it is not NULL, then throughget_deviceincrease the device reference count, and convert the device pointer from dev back toiio_devtype and return it; if it is NULL, return NULL directly. This implementation both ensures the validity of the device and avoids operations on a NULL pointer.
iio_buffer_read_outer_addr()
Next, the read function in the file operations setiio_buffer_read_outer_addris analyzed:
12 | // drivers/iio/iio_core.h |
iio_buffer_read_outerdefined as follows
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 | // drivers/iio/industrialio-buffer.c/** * iio_buffer_read_outer() - chrdev read for buffer access * @filp: File structure pointer for the char device * @buf: Destination buffer for iio buffer read * @n: First n bytes to read * @f_ps: Long offset provided by the user as a seek position * * This function relies on all buffer implementations having an * iio_buffer as their first element. * * Return: negative values corresponding to error codes or ret != 0 * for ending the reading activity **/ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, size_t n, loff_t *f_ps){ struct iio_dev *indio_dev = filp->private_data; // indio_dev = an IIO device struct iio_buffer *rb = indio_dev->buffer; // rb = bound ring buffer, rb->access->read is the actual buffer implementation (DMA / kfifo / hw buffer) DEFINE_WAIT_FUNC(wait, woken_wake_function); size_t datum_size; size_t to_wait; int ret = 0; // Validity check if (!indio_dev->info) return -ENODEV; if (!rb || !rb->access->read) return -EINVAL; datum_size = rb->bytes_per_datum; // A datum = the byte size of one sample of data. /* * If datum_size is 0 there will never be anything to read from the * buffer, so signal end of file now. */ if (!datum_size) // Indicates that this buffer produces no data at all. return 0; if (filp->f_flags & O_NONBLOCK) // Non-blocking mode to_wait = 0; else // Blocking mode to_wait = min_t(size_t, n / datum_size, rb->watermark); add_wait_queue(&rb->pollq, &wait); do { if (!indio_dev->info) { ret = -ENODEV; break; } // Is there already enough data in the buffer? if (!iio_buffer_ready(indio_dev, rb, to_wait, n / datum_size)) { // If there is no signal if (signal_pending(current)) { // Check for signals, user Ctrl+C ret = -ERESTARTSYS; // Return -ERESTARTSYS break; } // Sleep, the process enters interruptible sleep wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); continue; } ret = rb->access->read(rb, n, buf); // Actually read data if (ret == 0 && (filp->f_flags & O_NONBLOCK)) // Special handling for non-blocking mode ret = -EAGAIN; } while (ret == 0); // Keep waiting as long as no data is read remove_wait_queue(&rb->pollq, &wait); // Clean up the wait queue return ret;} |
Return value semantics:
> 0: number of bytes successfully read0: EOF (no data and never will be)< 0: error code
This is a typical implementation of a ‘blocking read buffer’ in the Linux kernel.
12345678 while (没有数据) { 如果非阻塞 → EAGAIN 如果被信号打断 → 退出 睡眠等待}读数据返回
wherestruct iio_bufferdefined as follows
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 | /** * struct iio_buffer - general buffer structure * * Note that the internals of this structure should only be of interest to * those writing new buffer implementations. */struct iio_buffer { /** @length: Number of datums in buffer. */ unsigned int length;// Number of data units in the buffer /** @bytes_per_datum: Size of individual datum including timestamp. */ size_t bytes_per_datum; // Size of a single data unit (including timestamp) /** * @access: Buffer access functions associated with the * implementation. */ const struct iio_buffer_access_funcs *access; // Buffer access functions, providing operations such as read and write /** @scan_mask: Bitmask used in masking scan mode elements. */ long *scan_mask; // Bitmask of scan mode elements, used to select enabled channels /** @demux_list: List of operations required to demux the scan. */ struct list_head demux_list; // List of operations required for demultiplexing scans /** @pollq: Wait queue to allow for polling on the buffer. */ wait_queue_head_t pollq; // Wait queue, used to support polling operations on the buffer /** @watermark: Number of datums to wait for poll/read. */ unsigned int watermark; // Watermark value, indicating the number of data units to wait for when polling or reading /* private: */ /* @scan_timestamp: Does the scan mode include a timestamp. */ bool scan_timestamp; // Whether the scan mode includes a timestamp /* @scan_el_dev_attr_list: List of scan element related attributes. */ struct list_head scan_el_dev_attr_list; // List of attributes related to scan elements /* @buffer_group: Attributes of the buffer group. */ struct attribute_group buffer_group; // Buffer group attributes /* * @scan_el_group: Attribute group for those attributes not * created from the iio_chan_info array. */ struct attribute_group scan_el_group; // Attribute group, used for those not from iio_chan_attributes created by the info array /* @attrs: Standard attributes of the buffer. */ const struct attribute **attrs; // Standard attributes of the buffer /* @demux_bounce: Buffer for doing gather from incoming scan. */ void *demux_bounce; // Buffer used to collect data from incoming scans /* @buffer_list: Entry in the devices list of current buffers. */ struct list_head buffer_list; // Entry in the device's current buffer list /* @ref: Reference count of the buffer. */ struct kref ref; // Reference count of the buffer, used to manage resource release}; |
Andiio_bufferinconst struct iio_buffer_access_funcs *accessdefined as follows
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 | /** * struct iio_buffer_access_funcs - access functions for buffers. * @store_to: actually store stuff to the buffer * @read: try to get a specified number of bytes (must exist) * @data_available: indicates how much data is available for reading from * the buffer. * @request_update: if a parameter change has been marked, update underlying * storage. * @set_bytes_per_datum:set number of bytes per datum * @set_length: set number of datums in buffer * @enable: called if the buffer is attached to a device and the * device starts sampling. Calls are balanced with * @disable. * @disable: called if the buffer is attached to a device and the * device stops sampling. Calles are balanced with @enable. * @release: called when the last reference to the buffer is dropped, * should free all resources allocated by the buffer. * @modes: Supported operating modes by this buffer type * @flags: A bitmask combination of INDIO_BUFFER_FLAG_* * * The purpose of this structure is to make the buffer element * modular as event for a given driver, different usecases may require * different buffer designs (space efficiency vs speed for example). * * It is worth noting that a given buffer implementation may only support a * small proportion of these functions. The core code 'should' cope fine with * any of them not existing. **/struct iio_buffer_access_funcs { // Store data into the buffer int (*store_to)(struct iio_buffer *buffer, const void *data); // Read up to n bytes of data from the buffer into the user-space buffer buf int (*read)(struct iio_buffer *buffer, size_t n, char __user *buf); // Return the amount of data currently available in the buffer (in bytes or data units) size_t (*data_available)(struct iio_buffer *buffer); // Request to update buffer configuration, usually to apply new parameters or state int (*request_update)(struct iio_buffer *buffer); // Set the size of each data unit (bytes per datum) to adjust the buffer format int (*set_bytes_per_datum)(struct iio_buffer *buffer, size_t bpd); // Set the length of the buffer (number of data units) int (*set_length)(struct iio_buffer *buffer, unsigned int length); // Enable the buffer, usually associated with the device and start data acquisition int (*enable)(struct iio_buffer *buffer, struct iio_dev *indio_dev); // Disable the buffer, stop data acquisition and clean up related resources int (*disable)(struct iio_buffer *buffer, struct iio_dev *indio_dev); // Release buffer resources, usually called when the buffer is destroyed void (*release)(struct iio_buffer *buffer); // Modes supported by the buffer (e.g., blocking, non-blocking, etc.) unsigned int modes; // Flags of the buffer, used to indicate specific functions or states unsigned int flags;}; |
This structure defines a set of function pointers and flags to implement the operation interface for the IIO buffer. Through these interfaces, the IIO subsystem can flexibly manage the storage, reading, configuration, and lifecycle of the buffer, while supporting multiple working modes and functional extensions, iniio_buffer_read_outer_addrIn the function, useret = rb->access->read(rb, n, buf);Actually read data.
iio_chrdev_release()
1234567891011121314151617181920212223 | /** * iio_chrdev_release() - chrdev file close buffer access and ioctls * @inode: Inode structure pointer for the char device * @filp: File structure pointer for the char device * * Return: 0 for successful release */static int iio_chrdev_release(struct inode *inode, struct file *filp){ // Obtain the IIO device structure associated with the character device from the inode via the container_of macro struct iio_dev *indio_dev = container_of(inode->i_cdev, struct iio_dev, chrdev); // Clear the BUSY status flag of the IIO device, indicating that the device is no longer occupied clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); // Decrease the reference count of the IIO device, releasing the device occupation // If the reference count drops to 0, further cleanup or release operations of the device may be triggered iio_device_put(indio_dev); // Return 0 indicates that the device resources were successfully released. return 0;} |
The core role of this function is to clean up the device state and release related resources when the device file is closed in user space. Throughclear_bitthe function clears the device’s BUSY status flag and reduces the device’s reference count, thereby ensuring that the device can be safely used by other processes or further cleaned up when no longer needed.
iio_buffer_poll_addr()
1 | |
iio_buffer_polldefined as follows:
123456789101112131415161718192021222324252627282930 | /** * iio_buffer_poll() - poll the buffer to find out if it has data * @filp: File structure pointer for device access * @wait: Poll table structure pointer for which the driver adds * a wait queue * * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading * or 0 for other cases */__poll_t iio_buffer_poll(struct file *filp, struct poll_table_struct *wait){ // Get the IIO device structure associated with the device from the file structure. struct iio_dev *indio_dev = filp->private_data; // Get the buffer structure of the device. struct iio_buffer *rb = indio_dev->buffer; // If the device has no valid info structure, or the buffer is not initialized, return 0 to indicate no event. if (!indio_dev->info || rb == NULL) return 0; // Add the current file descriptor to the buffer's wait queue so that it can be woken up when an event arrives. poll_wait(filp, &rb->pollq, wait); if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) return EPOLLIN | EPOLLRDNORM;// If there is data to read, return EPOLLIN | EPOLLRDNORM to indicate that data is readable. // If there is no data to read, return 0 to indicate no event. return 0;} |
This function implements the polling mechanism of the IIO buffer to check whether there is data available to read in the buffer. It first obtains the device and buffer information from the file structure and verifies its validity; then throughpoll_waitadding the current file descriptor to the wait queue so that it can be woken up when data arrives; finally throughiio_buffer_readycheck whether the buffer meets the read condition (such as reaching the watermark value), and if so, returnEPOLLIN | EPOLLRDNORMindicating that data is readable, otherwise return 0 to indicate no event.
iio_device_register_sysfs()
When explaining the IIO device registration process, we__iio_device_registerconducted a brief analysis, and in that function it also callsret = iio_device_register_sysfs(indio_dev);function to register the sysfs interface of the IIO device.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 | static int iio_device_register_sysfs(struct iio_dev *indio_dev){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int i, ret = 0, attrcount, attrn, attrcount_orig = 0; struct iio_dev_attr *p; struct attribute **attr, *clk = NULL; /* First count elements in any existing group */ /* First, count the number of elements in the existing attribute group. */ if (indio_dev->info->attrs) { attr = indio_dev->info->attrs->attrs; while (*attr++ != NULL) attrcount_orig++; } attrcount = attrcount_orig; /* * New channel registration method - relies on the fact a group does * not need to be initialized if its name is NULL. */ /* * New channel registration method - relies on a fact:If the group name is empty,then there is no need to initialize the group。 */ if (indio_dev->channels) // If the device has channel definitions for (i = 0; i < indio_dev->num_channels; i++) { // iterate over all channels const struct iio_chan_spec *chan = &indio_dev->channels[i]; // If the channel type is timestamp, record the timestamp clock attribute. if (chan->type == IIO_TIMESTAMP) clk = &dev_attr_current_timestamp_clock.attr; // Add the channel's sysfs attributes to the system. ret = iio_device_add_channel_sysfs(indio_dev, chan); if (ret < 0) // If adding fails, jump to error handling. goto error_clear_attrs; attrcount += ret; // Accumulate the number of newly added attributes. } // If the device supports the event interface, record the timestamp clock attribute. if (iio_dev_opaque->event_interface) clk = &dev_attr_current_timestamp_clock.attr; // If the device has a name, increment the attribute count. if (indio_dev->name) attrcount++; if (indio_dev->label) attrcount++; // If a timestamp clock attribute exists, increment the attribute count. if (clk) attrcount++; // Allocate memory to store all attribute pointers. iio_dev_opaque->chan_attr_group.attrs = kcalloc(attrcount + 1, sizeof(iio_dev_opaque->chan_attr_group.attrs[0]), GFP_KERNEL); if (iio_dev_opaque->chan_attr_group.attrs == NULL) { // If allocation fails, return an out-of-memory error. ret = -ENOMEM; goto error_clear_attrs; } /* Copy across original attributes */ // Copy the original attributes to the new attribute array. if (indio_dev->info->attrs) memcpy(iio_dev_opaque->chan_attr_group.attrs, indio_dev->info->attrs->attrs, sizeof(iio_dev_opaque->chan_attr_group.attrs[0]) *attrcount_orig); attrn = attrcount_orig; // Record the index of the current attribute array. /* Add all elements from the list. */ // Add all attributes from the channel attribute list to the attribute array. list_for_each_entry(p, &iio_dev_opaque->channel_attr_list, l) iio_dev_opaque->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr; // If the device has a name, add the name attribute to the attribute array. if (indio_dev->name) iio_dev_opaque->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr; if (indio_dev->label) iio_dev_opaque->chan_attr_group.attrs[attrn++] = &dev_attr_label.attr; // If a timestamp clock attribute exists, add it to the attribute array. if (clk) iio_dev_opaque->chan_attr_group.attrs[attrn++] = clk; // Add the attribute group to the device's attribute group list. indio_dev->groups[indio_dev->groupcounter++] = &iio_dev_opaque->chan_attr_group; return 0; // Return successerror_clear_attrs: iio_free_chan_devattr_list(&iio_dev_opaque->channel_attr_list); return ret;} |
The core operation of this function isret = iio_device_add_channel_sysfs(indio_dev, chan);Function, through which the channel’s sysfs attributes can be added to the system. The specific content of this function is as follows:
iio_device_add_channel_sysfs()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 | static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, struct iio_chan_spec const *chan){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int ret, attrcount = 0; // Define the return value and attribute counter. const struct iio_chan_spec_ext_info *ext_info; // Extended information pointer. // If the channel number is less than 0, return directly (indicating the channel is invalid). if (chan->channel < 0) return 0; // Add the independent type information mask attribute (IIO_SEPARATE) ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SEPARATE, &chan->info_mask_separate); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Add the independent type available information mask attribute (IIO_SEPARATE_AVAILABLE) ret = iio_device_add_info_mask_type_avail(indio_dev, chan, IIO_SEPARATE, &chan-> info_mask_separate_available); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Add the info mask attribute shared by type (IIO_SHARED_BY_TYPE) ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_TYPE, &chan->info_mask_shared_by_type); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Add the available information mask attribute shared by type (IIO_SHARED_BY_TYPE_AVAILABLE) ret = iio_device_add_info_mask_type_avail(indio_dev, chan, IIO_SHARED_BY_TYPE, &chan-> info_mask_shared_by_type_available); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Add the information mask attribute shared by direction (IIO_SHARED_BY_DIR) ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_DIR, &chan->info_mask_shared_by_dir); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Add the available information mask attribute shared by direction (IIO_SHARED_BY_DIR_AVAILABLE) ret = iio_device_add_info_mask_type_avail(indio_dev, chan, IIO_SHARED_BY_DIR, &chan->info_mask_shared_by_dir_available); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Add the globally shared information mask attribute (IIO_SHARED_BY_ALL) ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_ALL, &chan->info_mask_shared_by_all); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Add the globally shared available information mask attribute (IIO_SHARED_BY_ALL_AVAILABLE) ret = iio_device_add_info_mask_type_avail(indio_dev, chan, IIO_SHARED_BY_ALL, &chan->info_mask_shared_by_all_available); if (ret < 0) // If adding fails, return an error code. return ret; attrcount += ret; // Accumulate the number of newly added attributes. // Process extended information (ext_info). if (chan->ext_info) { unsigned int i = 0; // Record the index of extended information. for (ext_info = chan->ext_info; ext_info->name; ext_info++) { // Traverse extended information. // Add extended information attributes to sysfs. ret = __iio_add_chan_devattr(ext_info->name, // Attribute name chan, // Channel pointer ext_info->read ? // Whether there is a read callback. &iio_read_channel_ext_info : NULL, ext_info->write ? // Whether there is a write callback. &iio_write_channel_ext_info : NULL, i, // Extended information index ext_info->shared, // Whether shared &indio_dev->dev, // Device pointer &iio_dev_opaque->channel_attr_list); // Attribute list i++; // Add index // If -EBUSY is returned and the extended information is shared, skip it. if (ret == -EBUSY && ext_info->shared) continue; // If any other error occurs, return the error code directly. if (ret) return ret; attrcount++; // Successfully add an attribute and increment the count } } return attrcount; // Return the total number of attributes successfully added} |
These code snippets, by callingiio_device_add_info_mask_typeandiio_device_add_info_mask_type_availThe function adds channel attributes and their available attributes one by one according to different sharing types. Each type corresponds to a specific set of functions, and after each operation, the return value is checked to ensure errors can be captured and handled in a timely manner. At the same time, the total number of successfully added attributes is recorded by accumulating return values, thereby achieving flexible classification management and dynamic expansion of hardware functions. Next, the two functions just mentioned are explained. The function prototypes of the two functions are as follows:
iio_device_add_info_mask_typeThe function is defined as follows:
12345678910111213141516171819202122232425262728 | static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, enum iio_shared_by shared_by, const long *infomask){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int i, ret, attrcount = 0; for_each_set_bit(i, infomask, sizeof(*infomask)*8) { if (i >= ARRAY_SIZE(iio_chan_info_postfix)) return -EINVAL; ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], chan, &iio_read_channel_info, &iio_write_channel_info, i, shared_by, &indio_dev->dev, &iio_dev_opaque->channel_attr_list); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; else if (ret < 0) return ret; attrcount++; } return attrcount;} |
iio_device_add_info_mask_type_availthe function is defined as follows
123456789101112131415161718192021222324252627282930313233343536 | static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, enum iio_shared_by shared_by, const long *infomask){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int i, ret, attrcount = 0; char *avail_postfix; for_each_set_bit(i, infomask, sizeof(*infomask) * 8) { if (i >= ARRAY_SIZE(iio_chan_info_postfix)) return -EINVAL; avail_postfix = kasprintf(GFP_KERNEL, "%s_available", iio_chan_info_postfix[i]); if (!avail_postfix) return -ENOMEM; ret = __iio_add_chan_devattr(avail_postfix, chan, &iio_read_channel_info_avail, NULL, i, shared_by, &indio_dev->dev, &iio_dev_opaque->channel_attr_list); kfree(avail_postfix); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; else if (ret < 0) return ret; attrcount++; } return attrcount;} |
It can be seen that the parameters of the two functions are the same, but their functions are slightly different,iio_device_add_info_mask_typeUsed to add information mask attributes related to the channel,iio_device_add_info_mask_type_availUsed to add available information mask attributes, which are typically used to describe which functions are dynamically available. The corresponding parameter descriptions are as follows:
indio_dev: Pointer to the IIO device, representing the device currently being operated on. It contains all information about the device, such as channels and attributes, and is the core connecting hardware and user space.chan: Pointer to the channel, representing the specific channel to be operated on. Each channel corresponds to a sensor or signal interface (such as temperature, pressure, etc.) and defines the functionality of that channel.shared_by: Specifies the sharing type of the attribute, determining how attributes are shared among channels. The types used in the above code include:IIO_SEPARATE: Independent attribute, belonging only to a specific channel.IIO_SHARED_BY_TYPE: Shared by type, channels of the same type share attributes.IIO_SHARED_BY_DIR: Shared by direction, channels with the same direction share attributes.IIO_SHARED_BY_ALL: Globally shared, all channels of the entire device share attributes.
infomask: Pointer to the address of the information mask, defining the set of attributes to be exposed. Each bit corresponds to a specific function or feature, iniio_device_add_info_mask_typein,infomaskDescribes the basic functions of the channel (such as read, write, etc.). Iniio_device_add_info_mask_type_availAmong them, infomask describes dynamically available functions (such as additional functions enabled in certain modes). The mask contents used in the above code are described in the following table:
| Mask name | Core function | Shareability | Typical application scenarios |
|---|---|---|---|
info_mask_separate | Export information specific to the current channel | Independent (current channel only) | Channel-specific statistics, queue configuration, single-channel link status |
info_mask_separate_available | Export the ‘availability’ flag for current channel-specific information (indicating whether such information can be queried) | Independent (current channel only) | Indicate whether the current channel’s statistics have been collected and whether the configuration is effective |
info_mask_shared_by_type | Export common information shared by all channels of the same type | Shared by type (channels of the same type) | Device type attributes, driver version, global statistics for channels of the same type (e.g., common configuration for all 10G network ports) |
info_mask_shared_by_type_available | Export the ‘availability’ flag for information shared by channels of the same type | Shared by type (channels of the same type) | Indicate whether the common configuration for channels of the same type has been loaded and whether driver version information is readable |
info_mask_shared_by_dir | Export information shared by all channels in the same direction | Shared by direction (channels in the same direction) | RX/TX direction-specific statistics (e.g., total bytes of all receive channels), direction-related hardware configuration |
info_mask_shared_by_dir_available | Export the ‘availability’ flag for information shared by channels in the same direction | Shared by direction (channels in the same direction) | Indicate whether statistics for channels in a certain direction are available and whether the direction configuration is effective |
info_mask_shared_by_all | Export global information common to all channels | Globally shared (all channels) | Device MAC address, firmware version, overall device operation status, total transmit/receive statistics for all channels |
info_mask_shared_by_all_available | Export the ‘availability’ flag for globally shared information | Globally shared (all channels) | Indicate whether the device global status can be queried and whether firmware version information was read successfully |
Here we only added the attribute information. So when is the attribute setting completed? In the earlier analysisrockchip_saradc_probeof the function, it was mentioned that the RK3568 ADC channel definition usesSARADC_CHANNELmacro, and the specific content of this macro is as follows:
123456789101112131415 | .indexed = 1, \ .channel = _index, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .datasheet_name = _id, \ .scan_index = _index, \ .scan_type = { \ .sign = 'u', \ .realbits = _res, \ .storagebits = 16, \ .endianness = IIO_CPU, \ }, \} |
.type = IIO_VOLTAGEChannel type: voltage measurement.indexed = 1Enable index mode, indicating thatchannelfield is used as the index.channel = _indexMain channel number, specified by the macro parameter_indexspecified.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)Individually supported attribute: raw value (RAW).info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)Attribute shared by type: scale.datasheet_name = _idThe name in the datasheet, determined by the macro parameter_idspecified
It can be seen thatinfo_mask_separateandinfo_mask_shared_by_typeis used to specify individually supported attributes and shared attributes.
Explanation of the IIO channel attribute addition function
iio_device_add_info_mask_type()
12345678910111213141516171819202122232425262728293031323334353637 | static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, enum iio_shared_by shared_by, const long *infomask){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int i, ret, attrcount = 0; // Iterate over each valid bit in the information mask (i.e., bits set to 1) for_each_set_bit(i, infomask, sizeof(*infomask)*8) { // Check whether the index exceeds the range of the suffix array if (i >= ARRAY_SIZE(iio_chan_info_postfix)) return -EINVAL; // If it exceeds the range, return an invalid argument error // Call the function to add the attribute to the sysfs interface ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], // Attribute name suffix chan, // Channel pointer &iio_read_channel_info, // Read callback function &iio_write_channel_info, // Write callback function i, // Attribute index shared_by, // Shared type &indio_dev->dev, // Device pointer &iio_dev_opaque->channel_attr_list); // Attribute list // If -EBUSY is returned and the attribute is not of independent type, skip this attribute if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; // If any other error occurs, return the error code directly else if (ret < 0) return ret; // Successfully add an attribute and increment the count attrcount++; } // Return the total number of attributes successfully added return attrcount;} |
iio_device_add_info_mask_type_avail()
123456789101112131415161718192021222324252627282930313233343536373839404142434445 | static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, enum iio_shared_by shared_by, const long *infomask){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int i, ret, attrcount = 0; char *avail_postfix; // Iterate over each valid bit in the information mask (i.e., bits set to 1) for_each_set_bit(i, infomask, sizeof(*infomask) * 8) { if (i >= ARRAY_SIZE(iio_chan_info_postfix)) return -EINVAL; // Dynamically generate an attribute name suffix in the format "attribute_name_available". avail_postfix = kasprintf(GFP_KERNEL, "%s_available", iio_chan_info_postfix[i]); if (!avail_postfix) return -ENOMEM; // Call the function to add the available attribute to the sysfs interface. ret = __iio_add_chan_devattr(avail_postfix, // Attribute name suffix chan, // Channel pointer &iio_read_channel_info_avail, // Read callback function NULL, // Write callback function (not writable) i, // Attribute index shared_by, // Shared type &indio_dev->dev, // Device pointer &iio_dev_opaque->channel_attr_list); // Attribute list // Free the dynamically allocated attribute name suffix. kfree(avail_postfix); // If -EBUSY is returned and the attribute is not of independent type, skip this attribute if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; else if (ret < 0) // If any other error occurs, return the error code directly return ret; // Successfully add an attribute and increment the count attrcount++; } // Return the total number of attributes successfully added return attrcount;} |
The implementation logic of these two functions is the same, and the main content is infor_each_set_bitIt is implemented in this for loop. The for loop iterates over each valid bit in the mask, parses the corresponding function, and registers it as a sysfs attribute. Here, the function used to add the available attribute to the sysfs interface is__iio_add_chan_devattrfunction, whose specific content is as follows
__iio_add_chan_devattr()
The parameters are as follows:
const char *postfixThe suffix of the attribute name (such as “raw” or “available”), used to generate the complete attribute name.struct iio_chan_spec const *chanPointer to the channel, representing the channel object currently being operated on.ssize_t (*)(struct device *dev, struct device_attribute *attr, char *buf) readfuncRead callback function, used to implement the read operation of the attribute. The return value is the length of data read or an error code.ssize_t (*)(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) writefuncWrite callback function, used to implement the write operation of the attribute. The return value is the length of data written or an error code.u64 maskThe mask address of the attribute, usually representing the unique identifier of the function or feature corresponding to the attribute.enum iio_shared_by shared_byThe sharing type of the attribute, which determines how the attribute is shared among channels (e.g., independent, shared by type, etc.).struct device * devPointer to the device, indicating the target device of the current operation.struct list_head * attr_listHead pointer of the attribute list, used to add newly created attributes to the linked list.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | int __iio_add_chan_devattr(const char *postfix, struct iio_chan_spec const *chan, ssize_t (*readfunc)(struct device *dev, struct device_attribute *attr, char *buf), ssize_t (*writefunc)(struct device *dev, struct device_attribute *attr, const char *buf, size_t len), u64 mask, enum iio_shared_by shared_by, struct device *dev, struct list_head *attr_list){ int ret; struct iio_dev_attr *iio_attr, *t; // Allocate memory to create a new iio_dev_attr object iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL); if (iio_attr == NULL) return -ENOMEM; // Initialize device attributes (including name, read/write callback functions, etc.) ret = __iio_device_attr_init(&iio_attr->dev_attr, postfix, chan, readfunc, writefunc, shared_by); if (ret) goto error_iio_dev_attr_free; // Set the channel pointer and mask address iio_attr->c = chan; iio_attr->address = mask; // Traverse the attribute list to check whether an attribute with the same name already exists list_for_each_entry(t, attr_list, l) if (strcmp(t->dev_attr.attr.name, iio_attr->dev_attr.attr.name) == 0) { // If the sharing type is independent (IIO_SEPARATE), log an error if (shared_by == IIO_SEPARATE) dev_err(dev, "tried to double register : %s\n", t->dev_attr.attr.name); ret = -EBUSY; goto error_device_attr_deinit; } // Add the newly created attribute to the attribute list list_add(&iio_attr->l, attr_list); return 0;error_device_attr_deinit: // Error handling: deinitialize device attributes __iio_device_attr_deinit(&iio_attr->dev_attr);error_iio_dev_attr_free: // Error handling: free the allocated memory kfree(iio_attr); return ret;} |
Allocate memory and create a newiio_dev_attrobject. The corresponding structure content is shown below. This structure is used to describe the attributes of an IIO device (such as name, read/write callback functions), the functional identifier of the attribute, and the channel information to which it belongs.
12345678910111213 | /** * struct iio_dev_attr - iio specific device attribute * @dev_attr: underlying device attribute * @address: associated register address * @l: list head for maintaining list of dynamically created attrs * @c: specification for the underlying channel */struct iio_dev_attr { struct device_attribute dev_attr; // Device attribute, including name and read/write callback functions u64 address; // The address or mask value of the attribute, used to identify the function struct list_head l; // Linked list node, used to link into the attribute linked list struct iio_chan_spec const *c; // Pointer to the channel to which it belongs}; |
__iio_device_attr_init()
__iio_add_chan_devattrCalled in the function.ret = __iio_device_attr_init(&iio_attr->dev_attr, postfix, chan,readfunc, writefunc, shared_by);Initialize IIO device attributes. The detailed introduction of this function is as follows:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 | staticint __iio_device_attr_init(struct device_attribute *dev_attr, const char *postfix, struct iio_chan_spec const *chan, ssize_t (*readfunc)(struct device *dev, struct device_attribute *attr, char *buf), ssize_t (*writefunc)(struct device *dev, struct device_attribute *attr, const char *buf, size_t len), enum iio_shared_by shared_by){ int ret = 0; // Return value, used to record the execution result of the function char *name = NULL; // Attribute name char *full_postfix; // Full suffix string sysfs_attr_init(&dev_attr->attr); // Initialize device attribute structure /* Build up postfix of <extend_name>_<modifier>_postfix */ /* Construct suffix: format is <extend_name>_<modifier>_postfix */ if (chan->modified && (shared_by == IIO_SEPARATE)) { // If the channel is modified and the shared type is separate (IIO_SEPARATE) if (chan->extend_name) // If the channel has an extended name, generate the full suffix full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", iio_modifier_names[chan ->channel2], chan->extend_name, postfix); else // If there is no extended name, only use the modifier and suffix full_postfix = kasprintf(GFP_KERNEL, "%s_%s", iio_modifier_names[chan ->channel2], postfix); } else { // Non-modified channel or non-separate shared type if (chan->extend_name == NULL || shared_by != IIO_SEPARATE) // If there is no extended name or the shared type is not separate, copy the suffix directly full_postfix = kstrdup(postfix, GFP_KERNEL); else // Otherwise, combine the extended name and suffix full_postfix = kasprintf(GFP_KERNEL, "%s_%s", chan->extend_name, postfix); } // Check whether the suffix was allocated successfully if (full_postfix == NULL) return -ENOMEM; // Construct the attribute name based on the channel type (differential or single-ended) and shared type if (chan->differential) { /* Differential can not have modifier */ /* 差分通道 */ switch (shared_by) { case IIO_SHARED_BY_ALL: // Global shared: use only the suffix name = kasprintf(GFP_KERNEL, "%s", full_postfix); break; case IIO_SHARED_BY_DIR: // Shared by direction: add direction prefix name = kasprintf(GFP_KERNEL, "%s_%s", iio_direction[chan->output], full_postfix); break; case IIO_SHARED_BY_TYPE: // Shared by type: add direction and type information name = kasprintf(GFP_KERNEL, "%s_%s-%s_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], iio_chan_type_name_spec[chan->type], full_postfix); break; case IIO_SEPARATE: // Separate attribute: check whether it is indexed if (!chan->indexed) { WARN(1, "Differential channels must be indexed\n"); ret = -EINVAL; goto error_free_full_postfix; } // Construct the full differential channel name name = kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], chan->channel, iio_chan_type_name_spec[chan->type], chan->channel2, full_postfix); break; } } else { /* Single ended */ /* 单端通道 */ switch (shared_by) { case IIO_SHARED_BY_ALL: // Global shared: use only the suffix name = kasprintf(GFP_KERNEL, "%s", full_postfix); break; case IIO_SHARED_BY_DIR: // Shared by direction: add direction prefix name = kasprintf(GFP_KERNEL, "%s_%s", iio_direction[chan->output], full_postfix); break; case IIO_SHARED_BY_TYPE: // Shared by type: add direction and type information name = kasprintf(GFP_KERNEL, "%s_%s_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], full_postfix); break; case IIO_SEPARATE: // Independent attribute: construct the name according to whether it is indexed if (chan->indexed) name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], chan->channel, full_postfix); else name = kasprintf(GFP_KERNEL, "%s_%s_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], full_postfix); break; } } if (name == NULL) { // Check whether the attribute name was allocated successfully ret = -ENOMEM; goto error_free_full_postfix; } dev_attr->attr.name = name; // Set attribute name // Set read callback function and permissions if (readfunc) { dev_attr->attr.mode |= S_IRUGO; // Set read permission dev_attr->show = readfunc; // Bind read callback function } // Set write callback function and permissions if (writefunc) { dev_attr->attr.mode |= S_IWUSR; // Set writable permission dev_attr->store = writefunc; // Bind write callback function }error_free_full_postfix: kfree(full_postfix); // Free the dynamically allocated suffix string return ret;} |
The main function of this function is to dynamically construct a sysfs attribute name based on the channel type, sharing type, and other parameters. The attribute name is set at line 123.dev_attr->attr.name = name;The implementation is carried out there, and before that, the program can be divided into three parts based on if statements, corresponding to:Constructing the suffix string、Differential channel attribute name、Single-ended channel attribute namelogic. Next, we will explain these three parts in detail.
Lines 21-47 are used to construct the suffix stringfull_postfix, based on whether the channel is modified (chan->modified) and the sharing type (shared_by) dynamically generate the suffix stringfull_postfix. If the channel is modified and the sharing type is independent (IIO_SEPARATE), then based on whether there is an extended name (chan->extend_name) determine whether to include the modifier and extended name; otherwise, directly construct the suffix based on whether there is an extended name or the sharing type.
| Condition | Format |
|---|---|
Channel is modified and sharing mode isIIO_SEPARATEHas extended name (chan->extend_name) | [modifier name][extended name][suffix] |
Channel is modified and sharing mode isIIO_SEPARATENo extended name | [modifier name][suffix] |
Channel not modified or shared_by ≠IIO_SEPARATENo extended name or sharing mode ≠IIO_SEPARATE | [suffix] |
Channel not modified or shared_by ≠IIO_SEPARATEHas extended name and shared_by isIIO_SEPARATE(listed for completeness only) | [extended name][suffix] |
Lines 53-86, for differential channels, based on sharing type (shared_by) construct attribute names.
- For global sharing (
IIO_SHARED_BY_ALL), only the suffix is used; - For direction-based sharing (
IIO_SHARED_BY_DIR), add a direction prefix; - For type-based sharing (
IIO_SHARED_BY_TYPE), further add direction and type information; - For independent sharing (
IIO_SEPARATE), the channel must be indexed (chan->indexed), and include detailed information such as direction, type, and channel index in the name; if not indexed, a warning is issued and an error is returned.
| Sharing method (shared_by) | Attribute name format |
|---|---|
IIO_SHARED_BY_ALL | [full_postfix] |
IIO_SHARED_BY_DIR | [output direction]_[full_postfix] |
IIO_SHARED_BY_TYPE | [output direction][channel type]-[channel type][full_postfix] |
IIO_SEPARATE | [output direction][channel type][channel index]-[channel type][channel2 index][full_postfix] |
Lines 88-118, for single-ended channels, based on sharing type (shared_by) construct attribute names. For global sharing (IIO_SHARED_BY_ALL), only the suffix is used; for direction-based sharing (IIO_SHARED_BY_DIR), add a direction prefix; for type-based sharing (IIO_SHARED_BY_TYPE), add direction and type information; for independent shared (IIO_SEPARATE), decide whether to include the channel index in the name based on whether it is indexed; when not indexed, omit the index part.
| Sharing method (shared_by) | Attribute name format |
|---|---|
IIO_SHARED_BY_ALL | [full_postfix] |
IIO_SHARED_BY_DIR | [output direction]_[full_postfix] |
IIO_SHARED_BY_TYPE | [Output direction][Channel type][full_postfix] |
IIO_SEPARATE(with index,chan->indexed) | [Output direction][Channel type][Channel index][full_postfix] |
IIO_SEPARATE(without index) | [Output direction][Channel type]_[full_postfix] |
Next, take ADC channel 3 of RK3568 as an example to explain how the sysfs attribute name is created. The specific description of the RK3568 channels is as follows:
12345678910 | static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = { SARADC_CHANNEL(0, "adc0", 10), // Define ADC channel 0, named "adc0" SARADC_CHANNEL(1, "adc1", 10), // Define ADC channel 1, named "adc1" SARADC_CHANNEL(2, "adc2", 10), // Define ADC channel 2, named "adc2" SARADC_CHANNEL(3, "adc3", 10), // Define ADC channel 3, named "adc3" SARADC_CHANNEL(4, "adc4", 10), // Define ADC channel 4, named "adc4" SARADC_CHANNEL(5, "adc5", 10), // Define ADC channel 5, named "adc5" SARADC_CHANNEL(6, "adc6", 10), // Define ADC channel 6, named "adc6" SARADC_CHANNEL(7, "adc7", 10), // Define ADC channel 7, named "adc7"}; |
The specific content of this macro is as follows:
123456789101112131415 | .indexed = 1, \ .channel = _index, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .datasheet_name = _id, \ .scan_index = _index, \ .scan_type = { \ .sign = 'u', \ .realbits = _res, \ .storagebits = 16, \ .endianness = IIO_CPU, \ }, \} |
.type = IIO_VOLTAGEChannel type: voltage measurement.indexed = 1Enable index mode, indicating thatchannelfield is used as the index.channel = _indexMain channel number, specified by the macro parameter_indexspecified.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)Individually supported attribute: raw value (RAW).info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)Attribute shared by type: scale.datasheet_name = _idThe name in the datasheet, determined by the macro parameter_idspecified
After substituting into ADC3, the content is as follows:
123456789101112131415 | SARADC_CHANNEL(3, "adc3"){ .type = IIO_VOLTAGE, .indexed = 1, .channel = 3, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .datasheet_name = "adc3", .scan_index = 3, .scan_type = { .sign = 'u', .realbits = 10, .storagebits = 16, .endianness = IIO_CPU, },} |
It can be seen thatinfo_mask_separateandinfo_mask_shared_by_typeis used to specify individually supported attributes and shared attributes.
In lines 5 and 6, respectively, setseparateandshared_by_type, so iniio_device_add_channel_sysfsthe function, the following two sections will be executed
12345678910111213141516 | // Add the independent type information mask attribute (IIO_SEPARATE)ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SEPARATE, &chan->info_mask_separate);if (ret < 0) // If adding fails, return an error code. return ret;attrcount += ret; // Accumulate the number of newly added attributes.// Add the independent type available information mask attribute (IIO_SEPARATE_AVAILABLE)ret = iio_device_add_info_mask_type_avail(indio_dev, chan, IIO_SEPARATE, &chan-> info_mask_separate_available);if (ret < 0) // If adding fails, return an error code. return ret;attrcount += ret; // Accumulate the number of newly added attributes. |
Here, first analyze the addition of the independent type information mask attribute. ADC3’sinfo_mask_separateattribute is set toBIT(IIO_CHAN_INFO_RAW), andIIO_CHAN_INFO_RAWhas a value of 0, so__iio_add_chan_devattrThe first parameter of the function can be determined, thereby determining that the suffix of this attribute isiio_chan_info_postfix[0]i.e., raw.
Then we continue to analyze further, determine the full name of the attribute, and then enteriio_device_attr_initfunction, first judge the constructed suffix. Since in the definition of ADC3 there is nomodifiedattribute, andextend_nameIt is not defined, so the suffix is directly copied, that is, the RAW determined above.
Then the following conditions are evaluated. Since ADC3 does not havedifferentialattribute, it enters the conditional branch for single-ended channels, and then ADC3’sshared_byattribute isIIO_SEPARATEandindexedis 1, so the final name determination code is:
1234567 | case IIO_SEPARATE: // Independent attribute: construct the name according to whether it is indexed if (chan->indexed) name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], chan->channel, full_postfix); |
chan->outputis not set, soiio_directiontakes 0, and the obtained value isin,chan->typeisIIO_VOLTAGE, substituting intoiio_chan_type_name_specwe can get the valuevoltage,chan->channelas 3,full_postfixis the suffix value raw, so the first attribute name of ADC3 isin_voltage3_raw. Then the same method can be used to analyze the second attribute name, which will not be repeated here. The second attribute name can be obtained asin_voltage_scale。
IIO device node creation analysis
iio_device_add_channel_sysfsThe function determines the attribute names of the ADC channel, then collects the device’s channel attributes, device name, and timestamp attributes, and binds them withindio_devthe device, and finally creates corresponding nodes in sysfs, so that users can/sys/bus/iio/devices/iio:device0access these attributes in the directory
Of course, this code only collects these attributes intochan_attr_groupbut does not create these attributes in the sys directory. So where is the code that creates the related attribute files?
device_create()
There are two ways to create device nodes. The first way is throughmknodcommand to manually create device nodes. The second way is to automatically create device nodes, and the function called isdevice_create, and the iio device node is automatically created,device_createThe function content is as follows:
123456789101112131415161718192021222324252627282930313233343536373839404142434445 | // drivers/base/core.c/** * device_create - creates a device and registers it with sysfs * @class: pointer to the struct class that this device should be registered to * @parent: pointer to the parent struct device of this new device, if any * @devt: the dev_t for the char device to be added * @drvdata: the data to be added to the device for callbacks * @fmt: string for the device's name * * This function can be used by char device classes. A struct device * will be created in sysfs, registered to the specified class. * * A "dev" file will be created, showing the dev_t for the device, if * the dev_t is not 0,0. * If a pointer to a parent struct device is passed in, the newly created * struct device will be a child of that device in sysfs. * The pointer to the struct device will be returned from the call. * Any further sysfs files that might be required can be created using this * pointer. * * Returns &struct device pointer on success, or ERR_PTR() on error. * * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...){ // Define a variable argument list variable vargs, used to store the variable arguments passed to the function. va_list vargs; // Define a pointer variable dev to struct device, used to store the created device object. struct device *dev; // Initialize the variable argument list vargs; fmt is the last fixed parameter, and the following parameters are variable. va_start(vargs, fmt); // Call device_create_vargs function, passing in the class, parent device, device number, driver data, and variable arguments. // This function creates a device object based on the passed parameters and returns a pointer to the object. dev = device_create_groups_vargs(class, parent, devt, drvdata, NULL, fmt, vargs); va_end(vargs); return dev;}EXPORT_SYMBOL_GPL(device_create); |
This function is used to create a device object in the Linux kernel; its key point isdevice_create_vargsfunction,device_create_vargsThe specific content of the function is as follows:
device_create_vargs()
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 | static __printf(6, 0) struct device *device_create_groups_vargs(struct class *class, struct device *parent, dev_t devt, void *drvdata, const struct attribute_group **groups, const char *fmt, va_list args){ // Define a pointer variable dev to struct device, initialized to NULL. struct device *dev = NULL; // Define an integer variable retval to store the error code, with a default value of -ENODEV (indicating the device does not exist). int retval = -ENODEV; // Check whether the passed class is NULL or invalid (detected via IS_ERR). if (class == NULL || IS_ERR(class)) goto error; // Allocate memory to create a new device object, with size sizeof(*dev), using the GFP_KERNEL flag. dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { // Check whether the memory allocation succeeded. retval = -ENOMEM; goto error; } // Initialize the device object device_initialize(dev); // Set the attributes of the device object. dev->devt = devt; // Set the device number. dev->class = class; // Set the class to which the device belongs. dev->parent = parent; // Set the parent device. dev->groups = groups; // Set the attribute group of the device. dev->release = device_create_release; // Set the callback function for device release. dev_set_drvdata(dev, drvdata); // Set the driver's private data. // Use the variable arguments args to set the device name (via the format string fmt). retval = kobject_set_name_vargs(&dev->kobj, fmt, args); if (retval) // If setting the name fails. goto error; // Add the device to the system. retval = device_add(dev); if (retval) goto error; return dev;error: put_device(dev); return ERR_PTR(retval);} |
Finally, in this function, throughdevice_initialize()The function initializes the device and finally callsdevice_add()
device_add()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 | /** * device_add - add device to device hierarchy. * @dev: device. * * This is part 2 of device_register(), though may be called * separately _iff_ device_initialize() has been called separately. * * This adds @dev to the kobject hierarchy via kobject_add(), adds it * to the global and sibling lists for the device, then * adds it to the other relevant subsystems of the driver model. * * Do not call this routine or device_register() more than once for * any device structure. The driver model core is not designed to work * with devices that get unregistered and then spring back to life. * (Among other things, it's very hard to guarantee that all references * to the previous incarnation of @dev have been dropped.) Allocate * and register a fresh new struct device instead. * * NOTE: _Never_ directly free @dev after calling this function, even * if it returned an error! Always use put_device() to give up your * reference instead. * * Rule of thumb is: if device_add() succeeds, you should call * device_del() when you want to get rid of it. If device_add() has * *not* succeeded, use *only* put_device() to drop the reference * count. */int device_add(struct device *dev){ struct device *parent; struct kobject *kobj; struct class_interface *class_intf; int error = -EINVAL; struct kobject *glue_dir = NULL; // Get a reference to the device to ensure the device is valid. dev = get_device(dev); if (!dev) goto done; // Initialize device private data if (!dev->p) { error = device_private_init(dev); if (error) goto done; } /* * for statically allocated devices, which should all be converted * some day, we need to initialize the name. We prevent reading back * the name, and force the use of dev_name() */ // Set the device name. if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name); dev->init_name = NULL; } /* subsystems can specify simple device enumeration */ if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); if (!dev_name(dev)) { error = -EINVAL; goto name_error; } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); // Get the parent device and the kobject parent object. parent = get_device(dev->parent); kobj = get_device_parent(dev, parent); if (IS_ERR(kobj)) { error = PTR_ERR(kobj); goto parent_error; } if (kobj) dev->kobj.parent = kobj; /* use parent numa_node */ // Inherit the NUMA node of the parent device if (parent && (dev_to_node(dev) == NUMA_NO_NODE)) set_dev_node(dev, dev_to_node(parent)); /* first, register with generic layer. */ /* we require the name to be set before, and pass NULL */ // Register the device to the generic layer error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); if (error) { glue_dir = get_glue_dir(dev); goto Error; } /* notify platform of device entry */ error = device_platform_notify(dev, KOBJ_ADD); if (error) goto platform_error; // Create device attribute files and symbolic links error = device_create_file(dev, &dev_attr_uevent); if (error) goto attrError; error = device_add_class_symlinks(dev); if (error) goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; // Add the device to the bus and power management subsystem error = bus_add_device(dev); if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(dev); // If the device number is valid, create related files and nodes if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); if (error) goto DevAttrError; error = device_create_sys_dev_entry(dev); if (error) goto SysEntryError; devtmpfs_create_node(dev); } /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ // Notify clients that the device has been added if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); // Send KOBJ_ADD uevent event kobject_uevent(&dev->kobj, KOBJ_ADD); /* * Check if any of the other devices (consumers) have been waiting for * this device (supplier) to be added so that they can create a device * link to it. * * This needs to happen after device_pm_add() because device_link_add() * requires the supplier be registered before it's called. * * But this also needs to happen before bus_probe_device() to make sure * waiting consumers can link to it before the driver is bound to the * device and the driver sync_state callback is called for this device. */ // Handle device links (consumer-supplier relationship) if (dev->fwnode && !dev->fwnode->dev) { dev->fwnode->dev = dev; fw_devlink_link_device(dev); } // Probe the device and bind the driver bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); // If the device belongs to a class, add the device to the class if (dev->class) { mutex_lock(&dev->class->p->mutex); /* tie the class to the device */ klist_add_tail(&dev->p->knode_class, &dev->class->p->klist_devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->p->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->mutex); }done: put_device(dev); return error; SysEntryError: if (MAJOR(dev->devt)) device_remove_file(dev, &dev_attr_dev); DevAttrError: device_pm_remove(dev); dpm_sysfs_remove(dev); DPMError: bus_remove_device(dev); BusError: device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: device_remove_file(dev, &dev_attr_uevent); attrError: device_platform_notify(dev, KOBJ_REMOVE);platform_error: kobject_uevent(&dev->kobj, KOBJ_REMOVE); glue_dir = get_glue_dir(dev); kobject_del(&dev->kobj); Error: cleanup_glue_dir(dev, glue_dir);parent_error: put_device(parent);name_error: kfree(dev->p); dev->p = NULL; goto done;}EXPORT_SYMBOL_GPL(device_add); |
This part of the above code:
123456789101112 | // If the device number is valid, create related files and nodesif (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); if (error) goto DevAttrError; error = device_create_sys_dev_entry(dev); if (error) goto SysEntryError; devtmpfs_create_node(dev);} |
is a conditional check; if a device number exists, executedevtmpfs_create_nodefunction to create the device node. If there is no device number, it callskobject_ueventfunction creates device nodes via udev. At this point, the review of device node creation is complete, and finally by callingdevice_addfunction to create device nodes. So where is this function called for the IIO device’s device node?
devm_iio_device_alloc()
drivers/iio/adc/rockchip_saradc.cIn the file’s probe function, at the start of the probe function, it usesdevm_iio_device_allocfunction to allocate memory. The specific content of this function is as follows:
1234567891011121314151617181920212223242526272829303132 | /** * devm_iio_device_alloc - Resource-managed iio_device_alloc() * @parent: Device to allocate iio_dev for, and parent for this IIO device * @sizeof_priv: Space to allocate for private structure. * * Managed iio_device_alloc. iio_dev allocated with this function is * automatically freed on driver detach. * * RETURNS: * Pointer to allocated iio_dev on success, NULL on failure. */struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv){ struct iio_dev **ptr, *iio_dev; // Allocate a pointer for managing device resources, using the devres mechanism ptr = devres_alloc(devm_iio_device_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return NULL; // Allocate the IIO device structure and reserve private data space iio_dev = iio_device_alloc(parent, sizeof_priv); if (iio_dev) { *ptr = iio_dev; devres_add(parent, ptr); // Add the resource to the device resource linked list } else { devres_free(ptr); // If allocation fails, release devres resources } return iio_dev;}EXPORT_SYMBOL_GPL(devm_iio_device_alloc); |
iio_device_alloc()
This function callsiio_device_allocfunction to allocate the IIO device structure,iio_device_allocThe specific content of the function is as follows:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 | /** * iio_device_alloc() - allocate an iio_dev from a driver * @parent: Parent device. * @sizeof_priv: Space to allocate for private structure. **/struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv){ struct iio_dev_opaque *iio_dev_opaque; struct iio_dev *dev; size_t alloc_size; // Calculate the total memory size to allocate, including the iio_dev structure and private data alloc_size = sizeof(struct iio_dev_opaque); if (sizeof_priv) { // If private data space needs to be allocated alloc_size = ALIGN(alloc_size, IIO_ALIGN); // Align to IIO_ALIGN alloc_size += sizeof_priv; // Add the private data size } iio_dev_opaque = kzalloc(alloc_size, GFP_KERNEL); if (!iio_dev_opaque) return NULL; dev = &iio_dev_opaque->indio_dev; dev->priv = (char *)iio_dev_opaque + ALIGN(sizeof(struct iio_dev_opaque), IIO_ALIGN); // Initialize the basic attributes of the device dev->dev.parent = parent; dev->dev.groups = dev->groups; // Set the default attribute group of the device dev->dev.type = &iio_device_type; // Set device type dev->dev.bus = &iio_bus_type; // Set the bus type to which the device belongs device_initialize(&dev->dev); // Initialize the device object dev_set_drvdata(&dev->dev, (void *)dev); // Set device private data mutex_init(&dev->mlock); mutex_init(&dev->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); // Initialize the channel attribute linked list // Allocate device ID dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); if (dev->id < 0) { /* cannot use a dev_err as the name isn't available */ pr_err("failed to get device id\n"); kfree(iio_dev_opaque); return NULL; } // Set the device name. dev_set_name(&dev->dev, "iio:device%d", dev->id); INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); // Initialize the buffer linked list return dev;}EXPORT_SYMBOL(iio_device_alloc); |
Finally, this function usesdev_set_namea function to set the iio device name, which is under the dev directoryiio\:device0node name, and earlier throughdevice_initializethe iio device was initialized, and at this point, the series of operations before creating the device node is completed.
Then return torockchip_saradc.cthe probe function of the file, and at the end of this function, throughdevm_iio_device_registera function to register the iio device, and at the end of this function, it callscdev_device_addused to register the character device and create the device node at the same time. The specific content of this function is as follows:
1234567891011121314151617181920212223242526272829303132333435363738394041424344 | /** * cdev_device_add() - add a char device and it's corresponding * struct device, linkink * @dev: the device structure * @cdev: the cdev structure * * cdev_device_add() adds the char device represented by @cdev to the system, * just as cdev_add does. It then adds @dev to the system using device_add * The dev_t for the char device will be taken from the struct device which * needs to be initialized first. This helper function correctly takes a * reference to the parent device so the parent will not get released until * all references to the cdev are released. * * This helper uses dev->devt for the device number. If it is not set * it will not add the cdev and it will be equivalent to device_add. * * This function should be used whenever the struct cdev and the * struct device are members of the same structure whose lifetime is * managed by the struct device. * * NOTE: Callers must assume that userspace was able to open the cdev and * can call cdev fops callbacks at any time, even if this function fails. */int cdev_device_add(struct cdev *cdev, struct device *dev){ int rc = 0; // If the device dev has a valid device number (devt) if (dev->devt) { // Set the parent object of cdev to the device dev cdev_set_parent(cdev, &dev->kobj); // Register the character device, associating cdev with the device number dev->devt rc = cdev_add(cdev, dev->devt, 1); if (rc) return rc; } // Register the device object dev, make it visible in sysfs, and create the /dev device node rc = device_add(dev); if (rc) // If device_add fails, delete the registered cdev to roll back cdev_del(cdev); return rc;} |
You can see the call to this function.device_addfunction, through which the iio device node is created. At this point, the analysis of how the iio device node is created is complete.
IIO Trigger
First, in the serial terminal, enter/sys/bus/iio/devicesdirectory, as shown in the figure below:

In the previous chapter, under the sysfs directoryiio:device0the creation of the folder was explained, and the one in the same directoryiio_sysfs_triggerthe folder is actually an iio trigger, and in the kernel you need to checkSYSFS triggeronly then will this directory appear, and the specific path is as follows:
1234 | -> Device Drivers -> Industrial I/O support (IIO [=y]) -> Triggers - standalone <*> SYSFS trigger |
The corresponding driver source code isdrivers/iio/trigger/iio-trig-sysfs.c
iio_sysfs_trig_init()
1234567891011121314151617181920 | static int __init iio_sysfs_trig_init(void){ int ret; // Initialize the device structure. device_initialize(&iio_sysfs_trig_dev); // Set the device name to "iio_sysfs_trigger dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger"); // Add the device to the kernel. ret = device_add(&iio_sysfs_trig_dev); if (ret) put_device(&iio_sysfs_trig_dev); return ret;}module_init(iio_sysfs_trig_init);static void __exit iio_sysfs_trig_exit(void){ device_unregister(&iio_sysfs_trig_dev);}module_exit(iio_sysfs_trig_exit); |
- Line 4, call
device_initializefunction to initialize the device object, ensuring the device structure is in a usable state. - Line 7, call
dev_set_namefunction to set a name for the device, used to identify the device; the name set here isiio_sysfs_trigger, which is the directory seen in the sysfs subsystem. - Line 9, call
device_addfunction to register the device into the kernel’s device model, making it part of the system.
These three functions all have a common parameteriio_sysfs_trig_dev, which is astruct devicevariable of structure type, and its specific content is as follows:
struct device iio_sysfs_trig_dev
12345 | static struct device iio_sysfs_trig_dev = { .bus = &iio_bus_type, .groups = iio_sysfs_trig_groups, .release = &iio_trigger_sysfs_release,}; |
struct bus_type iio_bus_type
1234 | struct bus_type iio_bus_type = { .name = "iio",};EXPORT_SYMBOL(iio_bus_type); |
struct attribute_group iio_sysfs_trig_groups
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 | static ssize_t iio_sysfs_trig_add(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ int ret; unsigned long input; ret = kstrtoul(buf, 10, &input); if (ret) return ret; ret = iio_sysfs_trigger_probe(input); if (ret) return ret; return len;}static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add);static int iio_sysfs_trigger_remove(int id);static ssize_t iio_sysfs_trig_remove(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ int ret; unsigned long input; ret = kstrtoul(buf, 10, &input); if (ret) return ret; ret = iio_sysfs_trigger_remove(input); if (ret) return ret; return len;}static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove);static struct attribute *iio_sysfs_trig_attrs[] = { &dev_attr_add_trigger.attr, &dev_attr_remove_trigger.attr, NULL,};static const struct attribute_group iio_sysfs_trig_group = { .attrs = iio_sysfs_trig_attrs,};static const struct attribute_group *iio_sysfs_trig_groups[] = { &iio_sysfs_trig_group, NULL}; |
iio_trigger_sysfs_release()
1234 | /* Nothing to actually do upon release */static void iio_trigger_sysfs_release(struct device *dev){} |
After a series of calls, it can be determined that the attribute to be created ultimately isdev_attr_add_trigger.attranddev_attr_remove_trigger.attr, these two attributes are exactly the ones under the sysfs directoryadd_triggerandremove_trigger, as shown in the figure below:

iio_sysfs_trig_add()
1234567891011121314151617181920 | static ssize_t iio_sysfs_trig_add(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ int ret; unsigned long input; // Convert the string entered by the user to an unsigned long integer ret = kstrtoul(buf, 10, &input); if (ret) return ret; // Call the trigger probe function to attempt to add a trigger with the specified number ret = iio_sysfs_trigger_probe(input); if (ret) return ret; // On success, return the length of the input data return len;}// Define a device attribute file "add_trigger", write-only for users (S_IWUSR), and when written, it calls the iio_sysfs_trig_add function to handle itstatic DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add); |
Go to/sys/bus/iio/devices/iio_sysfs_triggerdirectory, and write toadd_triggerwrite 0, and a folder namedtrigger0folder, as shown in the figure below:

iio_sysfs_trig_remove()
12345678910111213141516171819202122 | static int iio_sysfs_trigger_remove(int id);static ssize_t iio_sysfs_trig_remove(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ int ret; unsigned long input; // Convert the string entered by the user to an unsigned long integer ret = kstrtoul(buf, 10, &input); if (ret) return ret; // Call the trigger remove function to attempt to remove the trigger with the specified number ret = iio_sysfs_trigger_remove(input); if (ret) return ret; // On success, return the length of the input data return len;}static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove); |
If you want to delete the trigger just created, simply write toremove_triggerwrite 0, as shown below:

iio_sysfs_trigger_probe()
So how is the above phenomenon implemented? Here we first explain theiio_sysfs_trig_addfunction. The core content of this function is iniio_sysfs_trigger_probefunction. The detailed content of the function is as follows:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 | static int iio_sysfs_trigger_probe(int id){ // Define a pointer to the iio_sysfs_trig structure pointer t, used for subsequent operations struct iio_sysfs_trig *t; int ret; // Used to store the return value bool foundit = false; // Flag variable, used to determine whether a duplicate trigger ID is found // Lock to ensure that the shared resource iio_sysfs_Access to trig_list is thread-safe mutex_lock(&iio_sysfs_trig_list_mut); // Traverse iio_sysfs_the trig_list linked list, check whether the same ID exists list_for_each_entry(t, &iio_sysfs_trig_list, l) if (id == t->id) { // If the same ID is found foundit = true; // Set the flag to true break; } // If a duplicate ID is found, return error code -EINVAL (invalid argument) if (foundit) { ret = -EINVAL; goto out1; // Jump to the code section that unlocks and returns } // Allocate memory to create a new iio_sysfs_trig structure t = kmalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) { // If memory allocation fails ret = -ENOMEM; goto out1; } // Initialize the new trigger's ID t->id = id; // Allocate a new IIO trigger and name it "sysfstrig%d", where %d is the passed-in ID t->trig = iio_trigger_alloc("sysfstrig%d", id); if (!t->trig) { // If trigger allocation fails ret = -ENOMEM; goto free_t; } // Set the trigger's attribute group t->trig->dev.groups = iio_sysfs_trigger_attr_groups; // Set the trigger's operation function set t->trig->ops = &iio_sysfs_trigger_ops; // Set the trigger's parent device t->trig->dev.parent = &iio_sysfs_trig_dev; // Set the trigger's private data to the current iio_sysfs_trig structure iio_trigger_set_drvdata(t->trig, t); // Initialize the interrupt work queue to handle the trigger's work init_irq_work(&t->work, iio_sysfs_trigger_work); // Register the trigger with the IIO subsystem ret = iio_trigger_register(t->trig); if (ret) goto out2; // Add the new trigger to the global linked list iio_sysfs_in trig_list list_add(&t->l, &iio_sysfs_trig_list); // Increment the module's reference count to prevent the module from being unloaded __module_get(THIS_MODULE); // Unlock the mutex to allow other threads to access shared resources mutex_unlock(&iio_sysfs_trig_list_mut); return 0;out2: // If trigger registration fails, release the trigger resources iio_trigger_free(t->trig);free_t: // If trigger allocation fails, release iio_sysfs_the memory of the trig structure kfree(t);out1: // Unlock the mutex to ensure the lock is released in any case mutex_unlock(&iio_sysfs_trig_list_mut); return ret;} |
This function has only one parameter, id, which represents the ID of the trigger to be created. Next, we analyze this function in detail.
- Lines 11-15: Traverse the list to check whether a trigger with the same id exists.
- Lines 22-34: Dynamically allocate memory to store the new trigger structure, then initialize the trigger object and create the file name based on the passed ID value. For example, when the passed ID is 0, the file name is
sysfstrig0 - Lines 37-43: Set the trigger’s attribute group, operation functions, parent device, and private data respectively.
- Lines 46-57: Initialize the interrupt workqueue used to handle trigger work, and call
iio_trigger_registerThe function registers the trigger to the IIO subsystem.
The key point of this function is at line 49iio_trigger_registerfunction, which is defined ininclude/linux/iio/trigger.hin, and its specific content is as follows:
12345678 | /** * iio_trigger_register() - register a trigger with the IIO core * @trig_info: trigger to be registered **/int __iio_trigger_register(struct iio_trigger *trig_info, struct module *this_mod); |
__iio_trigger_register()
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 | int __iio_trigger_register(struct iio_trigger *trig_info, struct module *this_mod){ int ret; // Set the module owner of the trigger to the current module trig_info->owner = this_mod; // Allocate a unique ID for the trigger, using ida_simple_get obtains it from the global ID allocator trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL); if (trig_info->id < 0) return trig_info->id; /* Set the name used for the sysfs directory etc */ // Set the name of the trigger device, used for sysfs directories, etc., with the format "trigger%ld" dev_set_name(&trig_info->dev, "trigger%ld", (unsigned long) trig_info->id); // Add the trigger device to the device model ret = device_add(&trig_info->dev); if (ret) // If adding the device fails, jump to the error handling label error_unregister_id goto error_unregister_id; /* Add to list of available triggers held by the IIO core */ // Lock the trigger list to ensure thread safety mutex_lock(&iio_trigger_list_lock); // Check whether a trigger with the same name already exists if (__iio_trigger_find_by_name(trig_info->name)) { // If a duplicate name exists, print an error log and jump to the error handling label error_device_del pr_err("Duplicate trigger name '%s'\n", trig_info->name); ret = -EEXIST; goto error_device_del; } // Add the trigger to the trigger list maintained by the IIO core list_add_tail(&trig_info->list, &iio_trigger_list); mutex_unlock(&iio_trigger_list_lock); // Unlock the trigger list return 0;error_device_del: mutex_unlock(&iio_trigger_list_lock); device_del(&trig_info->dev);error_unregister_id: ida_simple_remove(&iio_trigger_ida, trig_info->id); return ret;}EXPORT_SYMBOL(__iio_trigger_register); |
The main function of this function is to register an IIO trigger to the system. First, it allocates a unique ID for the trigger and sets the device name. Then, at line 20, it adds the trigger device to the Linux device model. Finally, at line 35, it adds the trigger to the global trigger linked list maintained by the IIO core.
iio_trigger_alloc()
iio_sysfs_trigger_probe()At line 30, callt->trig = iio_trigger_alloc("sysfstrig%d", id);, in whichiio_trigger_allocthe function is defined as follows
12345678910111213141516 | struct iio_trigger *iio_trigger_alloc(const char *fmt, ...){ struct iio_trigger *trig; va_list vargs; // initialize the variable argument list va_start(vargs, fmt); // call viio_trigger_alloc function, passing the format string and variable arguments, to allocate and initialize the trigger trig = viio_trigger_alloc(fmt, vargs); // end the use of the variable argument list va_end(vargs); // return the pointer to the allocated trigger structure, or NULL if allocation fails return trig;}EXPORT_SYMBOL(iio_trigger_alloc); |
The core of this function is theviio_trigger_allocfunction, used to pass in the format string and variable arguments, allocate and initialize the trigger,viio_trigger_allocThe specific content of the function is as follows:
viio_trigger_alloc()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 | static __printf(1, 0)struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs){ struct iio_trigger *trig; int i; // allocate memory for an iio_trigger structure and initialize it to 0 trig = kzalloc(sizeof *trig, GFP_KERNEL); if (!trig) // if allocation fails, return NULL return NULL; // set the type and bus type of the trigger device trig->dev.type = &iio_trig_type; trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); // Initialize the device structure. mutex_init(&trig->pool_lock); // initialize the trigger's mutex for resource protection // allocate interrupt descriptors for the trigger's child interrupts trig->subirq_base = irq_alloc_descs(-1, 0, CONFIG_IIO_CONSUMERS_PER_TRIGGER, 0); if (trig->subirq_base < 0) // if allocation fails, jump to error handling goto free_trig; // generate the trigger name based on the format string trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); if (trig->name == NULL) goto free_descs; // configure the relevant information of the child interrupt chip trig->subirq_chip.name = trig->name; // set the interrupt chip name trig->subirq_chip.irq_mask = &iio_trig_subirqmask; // set the interrupt mask function trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask; // set the interrupt unmask function // configure the behavior of each child interrupt for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { irq_set_chip(trig->subirq_base + i, &trig->subirq_chip); // set the interrupt chip irq_set_handler(trig->subirq_base + i, &handle_simple_irq); // Set interrupt handler irq_modify_status(trig->subirq_base + i, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); // modify the interrupt status flag } return trig; // return the trigger structure on successfree_descs: // Release the allocated interrupt descriptor irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER);free_trig: // Free the trigger structure memory kfree(trig); return NULL;} |
This functionviio_trigger_allocis to dynamically allocate and initialize an IIO trigger (iio_trigger) structure, set its device type, bus type, name, interrupt descriptor, and sub-interrupt related configuration, the here-refinedtrig->subirq_chipmember is used where? In the previous chapter, when explaining the IIO device registration function, in__iio_device_registerlines 55-56 of the function also have IIO trigger-related code, as shown below:
123 | // If the device supports all trigger modes, register the trigger consumerif (indio_dev->modes & INDIO_ALL_TRIGGERED_MODES) iio_device_register_trigger_consumer(indio_dev); |
iio_device_register_trigger_consumerfunction is used to register the trigger consumer, which is defined indrivers/iio/industrialio-trigger.cfile, and the specific content is as follows:
123456 | void iio_device_register_trigger_consumer(struct iio_dev *indio_dev){ // Add the trigger consumer attribute group to the attribute group list of the IIO device indio_dev->groups[indio_dev->groupcounter++] = &iio_trigger_consumer_attr_group;} |
The attribute to be created by this function isiio_trigger_consumer_attr_group, and its structure variable content is as follows:
12345678910111213 | static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, iio_trigger_read_current, iio_trigger_write_current);static struct attribute *iio_trigger_consumer_attrs[] = { &dev_attr_current_trigger.attr, NULL,};static const struct attribute_group iio_trigger_consumer_attr_group = { .name = "trigger", .attrs = iio_trigger_consumer_attrs,}; |
After a series of tracing, it can be finally determined that an attribute namedcurrent_triggerwill be created in the sysfs directory, and this attribute hasiio_trigger_read_currenta read function, andiio_trigger_write_currenta write function.
iio_trigger_read_current()
iio_trigger_read_currentThe content related to the read function is as follows:
123456789101112131415161718192021222324252627 | /** * iio_trigger_read_current() - trigger consumer sysfs query current trigger * @dev: device associated with an industrial I/O device * @attr: pointer to the device_attribute structure that * is being processed * @buf: buffer where the current trigger name will be printed into * * For trigger consumers the current_trigger interface allows the trigger * used by the device to be queried. * * Return: a negative number on failure, the number of characters written * on success or 0 if no trigger is available */static ssize_t iio_trigger_read_current(struct device *dev, struct device_attribute *attr, char *buf){ // Convert the device structure to the IIO device structure struct iio_dev *indio_dev = dev_to_iio_dev(dev); // If the IIO device is currently bound to a trigger (trig is not NULL) if (indio_dev->trig) // Write the trigger name to the buffer buf, and return the number of characters written return sprintf(buf, "%s\n", indio_dev->trig->name); // If no trigger is bound, return 0, indicating there is no data to read return 0;} |
iio_trigger_write_current()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 | /** * iio_trigger_write_current() - trigger consumer sysfs set current trigger * @dev: device associated with an industrial I/O device * @attr: device attribute that is being processed * @buf: string buffer that holds the name of the trigger * @len: length of the trigger name held by buf * * For trigger consumers the current_trigger interface allows the trigger * used for this device to be specified at run time based on the trigger's * name. * * Return: negative error code on failure or length of the buffer * on success */static ssize_t iio_trigger_write_current(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ // Convert the device structure to the IIO device structure struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_trigger *oldtrig = indio_dev->trig; // Save the currently bound trigger struct iio_trigger *trig; // New trigger pointer int ret; mutex_lock(&indio_dev->mlock); // Lock to protect device state if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { // Convert the device structure to the IIO device structure mutex_unlock(&indio_dev->mlock); // Unlock and return -EBUSY (device busy) return -EBUSY; } if (indio_dev->trig_readonly) { // If the trigger is read-only mutex_unlock(&indio_dev->mlock); // Unlock and return -EPERM (no permission) return -EPERM; } mutex_unlock(&indio_dev->mlock); // Unlock trig = iio_trigger_acquire_by_name(buf); // Get the new trigger based on the input name if (oldtrig == trig) { // If the new and old triggers are the same, return success directly ret = len; goto out_trigger_put; } if (trig && indio_dev->info->validate_trigger) { // Verify whether the new trigger is compatible with the device ret = indio_dev->info->validate_trigger(indio_dev, trig); if (ret) // If verification fails, jump to error handling goto out_trigger_put; } if (trig && trig->ops && trig->ops->validate_device) { // Verify whether the device is compatible with the trigger ret = trig->ops->validate_device(trig, indio_dev); if (ret) // If verification fails, jump to error handling goto out_trigger_put; } indio_dev->trig = trig; // Update the device's trigger to the new trigger if (oldtrig) { // If an old trigger exists, disassociate it from the device if (indio_dev->modes & INDIO_EVENT_TRIGGERED) iio_trigger_detach_poll_func(oldtrig, indio_dev->pollfunc_event); iio_trigger_put(oldtrig); // Decrease the reference count of the old trigger } if (indio_dev->trig) { // If the new trigger exists, associate it with the device if (indio_dev->modes & INDIO_EVENT_TRIGGERED) iio_trigger_attach_poll_func(indio_dev->trig, indio_dev->pollfunc_event); } return len; // Return the write length, indicating successout_trigger_put: if (trig) // If the new trigger exists, decrease its reference count iio_trigger_put(trig); return ret;} |
Called at line 65iio_trigger_attach_poll_funcfunction, whose specific content is as follows:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 | /* Complexity in here. With certain triggers (datardy) an acknowledgement * may be needed if the pollfuncs do not include the data read for the * triggering device. * This is not currently handled. Alternative of not enabling trigger unless * the relevant function is in there may be the best option. *//* Worth protecting against double additions? */int iio_trigger_attach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf){ int ret = 0; // Check whether the trigger's resource pool is empty to determine whether the trigger is unused bool notinuse = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER); /* Prevent the module from being removed whilst attached to a trigger */ /* Prevent the module from being unloaded while the trigger is in use */ __module_get(pf->indio_dev->driver_module); /* Get irq number */ /* Get the interrupt number of the trigger */ pf->irq = iio_trigger_get_irq(trig); if (pf->irq < 0) { pr_err("Could not find an available irq for trigger %s, CONFIG_IIO_CONSUMERS_PER_TRIGGER=%d limit might be exceeded\n", trig->name, CONFIG_IIO_CONSUMERS_PER_TRIGGER); goto out_put_module; } /* Request irq */ /* Request a threaded interrupt */ ret = request_threaded_irq(pf->irq, pf->h, pf->thread, pf->type, pf->name, pf); if (ret < 0) goto out_put_irq; /* Enable trigger in driver */ /* If the trigger supports setting state and is not in use, enable the trigger */ if (trig->ops && trig->ops->set_trigger_state && notinuse) { ret = trig->ops->set_trigger_state(trig, true); if (ret < 0) goto out_free_irq; } /* * Check if we just registered to our own trigger: we determine that * this is the case if the IIO device and the trigger device share the * same parent device. */ /* * Check whether it is registered to its own trigger: * The basis for the judgment is IIO Whether the device and the trigger device have the same parent device。 */ if (pf->indio_dev->dev.parent == trig->dev.parent) trig->attached_own_device = true; return ret;out_free_irq: free_irq(pf->irq, pf);out_put_irq: iio_trigger_put_irq(trig, pf->irq);out_put_module: module_put(pf->indio_dev->driver_module); return ret;} |
This function is used to attach a polling function to a specified trigger, and perform operations such as interrupt request and trigger state setting. The interrupt here is exactly the one mentioned aboveviio_trigger_allocthat was perfectedtrig->subirq_chipmember. The IIO trigger executes the corresponding event by means of an interrupt.
IIO Data Read Analysis
When writing the ADC driver experiment, you need to calliio_read_channel_rawfunction to read the actual value of the ADC channel, but that function was not explained in detail. In this chapter, we williio_read_channel_rawexplain the read function in detail. This function is defined indrivers/iio/inkern.cfile, and the specific content is as follows:
iio_read_channel_raw()
123456789101112131415161718192021 | // drivers/iio/inkern.cint iio_read_channel_raw(struct iio_channel *chan, int *val){ int ret; // Lock to protect the IIO device information structure to prevent data inconsistency caused by concurrent access. mutex_lock(&chan->indio_dev->info_exist_lock); // Check whether the IIO device information structure exists. If it is NULL, the device is unavailable. if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; } // Call iio_channel_read function to read the raw data of the channel (IIO_CHAN_INFO_RAW) ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);err_unlock: mutex_unlock(&chan->indio_dev->info_exist_lock); return ret;}EXPORT_SYMBOL_GPL(iio_read_channel_raw); |
iio_channel_read()
123456789101112131415161718192021222324252627282930 | static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info){ int unused; // Define an unused variable to handle the case where val2 is NULL. int vals[INDIO_MAX_RAW_ELEMENTS]; // Used to store the result of multi-value reading. int ret; // Save the function return value. int val_len = 2; // Read two values by default (val and val2). if (val2 == NULL) val2 = &unused; // Check whether the channel supports the specified information type (info). If not, return -EINVAL. if (!iio_channel_has_info(chan->channel, info)) return -EINVAL; // If the device supports batch read (read_raw_multi), then call this function if (chan->indio_dev->info->read_raw_multi) { ret = chan->indio_dev->info->read_raw_multi(chan->indio_dev, chan->channel, INDIO_MAX_RAW_ELEMENTS, vals, &val_len, info); *val = vals[0]; // Assign the first value to val. *val2 = vals[1]; // Assign the second value to val2. } else // Otherwise, call the single-value read function read_raw ret = chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel, val, val2, info); // Return the read result. Return 0 on success, and a negative error code on failure. return ret;} |
Since the batch read function is not implementedindio_dev->info->read_raw_multiSo it enters the second branch, i.e., throughindio_dev->info->read_rawthe function to perform the reading.
iio_device_add_info_mask_type()
So how do attribute files in the sysfs subsystem read IIO values? In the previous explanationiio_device_register_sysfsof the function, we analyzed that it ultimately callsiio_device_add_info_mask_typefunction, in whichiio_device_add_info_mask_typethe read function is included.iio_device_add_info_mask_typeThe function is as follows:
1234567891011121314151617181920212223242526272829303132333435363738 | // drivers/iio/industrialio-core.cstatic int iio_device_add_info_mask_type(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, enum iio_shared_by shared_by, const long *infomask){ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int i, ret, attrcount = 0; // Iterate over each valid bit in the information mask (i.e., bits set to 1) for_each_set_bit(i, infomask, sizeof(*infomask)*8) { // Check whether the index exceeds the range of the suffix array if (i >= ARRAY_SIZE(iio_chan_info_postfix)) return -EINVAL; // If it exceeds the range, return an invalid argument error // Call the function to add the attribute to the sysfs interface ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], // Attribute name suffix chan, // Channel pointer &iio_read_channel_info, // Read callback function &iio_write_channel_info, // Write callback function i, // Attribute index shared_by, // Shared type &indio_dev->dev, // Device pointer &iio_dev_opaque->channel_attr_list); // Attribute list // If -EBUSY is returned and the attribute is not of independent type, skip this attribute if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; // If any other error occurs, return the error code directly else if (ret < 0) return ret; // Successfully add an attribute and increment the count attrcount++; } // Return the total number of attributes successfully added return attrcount;} |
In line 19 of this function, the read function of the IIO channel is specifiediio_read_channel_infoThe specific content of this function is as follows:
iio_read_channel_info()
12345678910111213141516171819202122232425262728 | static ssize_t iio_read_channel_info(struct device *dev, struct device_attribute *attr, char *buf){ struct iio_dev *indio_dev = dev_to_iio_dev(dev); // Convert the device structure to the IIO device structure struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); // Convert the device attribute to the IIO attribute structure int vals[INDIO_MAX_RAW_ELEMENTS]; // Used to store the multi-value data read int ret; // Store the return value of the read operation int val_len = 2; // Read two values by default // If the device supports batch read (read_raw_multi), then call this function if (indio_dev->info->read_raw_multi) ret = indio_dev->info->read_raw_multi(indio_dev, this_attr->c, INDIO_MAX_RAW_ELEMENTS, vals, &val_len, this_attr->address); else // Otherwise, call the single-value read function read_raw ret = indio_dev->info->read_raw(indio_dev, this_attr->c, &vals[0], &vals[1], this_attr->address); // If the read fails, return the error code directly if (ret < 0) return ret; // Format the read value and write it into the buffer buf, returning the number of bytes written return iio_format_value(buf, ret, val_len, vals);} |
The core content of this function is the logic judgment in lines 12-20, and since the batch read function is not implementedread_raw_multiSo it enters the second branch, which is the same as the previous analysisiio_read_channel_rawthe same, also throughindio_dev->info->read_rawfunction to perform the read. Andindio_dev->infois indrivers/iio/adc/rockchip_saradc.cthe probe function in the file, throughindio_dev->info = &rockchip_saradc_iio_info;, androckchip_saradc_iio_infoAs follows:
123 | static const struct iio_info rockchip_saradc_iio_info = { .read_raw = rockchip_saradc_read_raw,}; |
ultimately points torockchip_saradc_read_rawFunction, the specific content of which is as follows:
rockchip_saradc_read_raw()
123456789101112131415161718192021222324252627282930313233343536373839404142 | static int rockchip_saradc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask){ struct rockchip_saradc *info = iio_priv(indio_dev); // Get the private data of the IIO device int ret; if (info->test) // If in test mode, return 0 directly return 0; switch (mask) { // Select the read type according to the mask parameter case IIO_CHAN_INFO_RAW: // Read raw ADC data mutex_lock(&indio_dev->mlock); // Lock to protect device state if (info->suspended) { // If the device is suspended, return -EBUSY (device busy) mutex_unlock(&indio_dev->mlock); return -EBUSY; } ret = rockchip_saradc_conversion(info, chan); // Conversion if (ret) { rockchip_saradc_power_down(info); mutex_unlock(&indio_dev->mlock); return ret; } *val = info->last_val; mutex_unlock(&indio_dev->mlock); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: // Read the scaling factor /* It is a dummy regulator */ if (info->uv_vref < 0) /* If the reference voltage is invalid, return the error code directly */ return info->uv_vref; *val = info->uv_vref / 1000; // Calculate the reference voltage (unit: millivolts) *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; }} |
rockchip_saradc_isr()
In the ADC device tree node, there is a description related to interrupts. When the conversion is completed, a corresponding interrupt signal is generated. In the driver, the request for the interrupt service function is defined in the probe function, by callingret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr, 0, dev_name(&pdev->dev), info);Through thedevm_request_irqThe function defines one namedrockchip_saradc_isrthe interrupt service function, the specific content of which is as follows:
123456789101112131415161718192021222324252627 | static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id){ struct rockchip_saradc *info = dev_id; // Get device private data unsigned long flags; // Define a variable to save the interrupt state /* Read value */ /* Read the ADC conversion result */ info->last_val = rockchip_saradc_read(info); info->last_val &= GENMASK(info->last_chan->scan_type.realbits - 1, 0); rockchip_saradc_power_down(info); complete(&info->completion); spin_lock_irqsave(&info->lock, flags); if (info->test) { // If in test mode pr_info("chn[%d] val = %d\n", info->chn, info->last_val); mod_delayed_work(info->wq, &info->work, msecs_to_jiffies(100)); } spin_unlock_irqrestore(&info->lock, flags); return IRQ_HANDLED;} |
The final read data will be assigned torockchip_saradc_read_rawthe function’s variable val
ADC key driver analysis
Device tree:
123456789101112131415161718192021222324252627282930313233 | //ADC Buttons adc_keys: adc-keys { compatible = "adc-keys"; io-channels = <&saradc 0>; io-channel-names = "buttons"; keyup-threshold-microvolt = <1800000>; poll-interval = <100>; vol-up-key { label = "volume up"; linux,code = <KEY_VOLUMEUP>; press-threshold-microvolt = <1750>; }; vol-down-key { label = "volume down"; linux,code = <KEY_VOLUMEDOWN>; press-threshold-microvolt = <297500>; }; menu-key { label = "menu"; linux,code = <KEY_MENU>; press-threshold-microvolt = <980000>; }; back-key { label = "back"; linux,code = <KEY_BACK>; press-threshold-microvolt = <1305500>; }; }; |
adc_keys_probe()
The driver corresponding to the ADC key isinput/keyboard/adc-keys.c
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 | static int adc_keys_probe(struct platform_device *pdev){ struct device *dev = &pdev->dev; // Get the device structure struct adc_keys_state *st; // Define a state structure pointer struct input_dev *input; // Define an input device structure pointer enum iio_chan_type type; // Define the IIO channel type int i, value; // Define loop variables and temporary variables int error; // Define an error code // Allocate memory for the state structure adc_keys_state, using devm_kzalloc to ensure automatic release when the device is unloaded st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); if (!st) return -ENOMEM; // Get the IIO channel to read the analog signal of the key st->channel = devm_iio_channel_get(dev, "buttons"); if (IS_ERR(st->channel)) return PTR_ERR(st->channel); // Check whether the channel is valid if (!st->channel->indio_dev) return -ENXIO; // Get the channel type (such as voltage, current, etc.) error = iio_get_channel_type(st->channel, &type); if (error < 0) return error; // Ensure the channel type is voltage if (type != IIO_VOLTAGE) { dev_err(dev, "Incompatible channel type %d\n", type); return -EINVAL; } // Read the "keyup-threshold-microvolt" attribute value from the device tree, which represents the voltage threshold when the key is released if (device_property_read_u32(dev, "keyup-threshold-microvolt", &st->keyup_voltage)) { dev_err(dev, "Invalid or missing keyup voltage\n"); return -EINVAL; } // Convert microvolts to millivolts st->keyup_voltage /= 1000; // Load the key mapping table error = adc_keys_load_keymap(dev, st); if (error) return error; // Allocate an input device input = devm_input_allocate_device(dev); if (!input) { dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; } input_set_drvdata(input, st); // Set the basic information of the input device input->name = pdev->name; // Set the device name. input->phys = "adc-keys/input0"; // Set the physical path // Set the ID information of the input device input->id.bustype = BUS_HOST; // The bus type is host input->id.vendor = 0x0001; // Vendor ID input->id.product = 0x0001; // Product ID input->id.version = 0x0100; // Version number // Set the supported event type to key event __set_bit(EV_KEY, input->evbit); for (i = 0; i < st->num_keys; i++) // Iterate through the key mapping table __set_bit(st->map[i].keycode, input->keybit); // Set the supported key codes // If the "autorepeat" property is set in the device tree, enable the auto-repeat function if (device_property_read_bool(dev, "autorepeat")) __set_bit(EV_REP, input->evbit); // Register the polling input device error = input_setup_polling(input, adc_keys_poll); if (error) { dev_err(dev, "Unable to set up polling: %d\n", error); return error; } // Read the "poll-interval" property value from the device tree and set the polling interval if (!device_property_read_u32(dev, "poll-interval", &value)) input_set_poll_interval(input, value); error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device: %d\n", error); return error; } return 0;} |
struct input_devandinput_register_register()They are all functions in the input subsystem.

