/** * 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 */ structiio_dev { int id; // 设备的唯一标识符 structmodule *driver_module;// 指向驱动模块的指针
int modes; // 支持的工作模式(如直接模式、缓冲模式) int currentmode; // 当前的工作模式 structdevicedev;// 嵌套的 Linux 设备模型结构体
structiio_buffer *buffer;// 数据缓冲区指针 int scan_bytes; // 扫描数据的字节数 structmutexmlock;// 互斥锁,保护共享资源
// 匹配设备树中的设备 ID 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; // 获取设备匹配的数据(如配置参数)
/* 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; }
// 获取设备的寄存器地址资源 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 映射寄存器到虚拟地址空间 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 */ /* * 重置控制器是可选属性,为了兼容旧的设备树,允许不提供该属性 */ info->reset = devm_reset_control_get_exclusive(&pdev->dev, "saradc-apb"); if (IS_ERR(info->reset)) {// 获取复位控制器失败 ret = PTR_ERR(info->reset); if (ret != -ENOENT) return ret;
dev_dbg(&pdev->dev, "no reset control found\n"); info->reset = NULL; }
// 获取 APB 总线时钟 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); }
// 获取 SARADC 转换器时钟 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); }
// 获取参考电压调节器 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 (info->reset) rockchip_saradc_reset_controller(info->reset);
/* * Use a default value for the converter clock. * This may become user-configurable 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; }
// 启用参考电压调节器 ret = regulator_enable(info->vref); if (ret < 0) { dev_err(&pdev->dev, "failed to enable vref regulator\n"); return ret; } // 注册一个清理动作,用于在设备移除时禁用调节器 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; }
// 获取参考电压的实际值 ret = regulator_get_voltage(info->vref); if (ret < 0) { dev_err(&pdev->dev, "failed to get voltage\n"); return ret; } // 将参考电压的实际值赋值给info->uv_vref info->uv_vref = ret;
// 启用 APB 总线时钟 ret = clk_prepare_enable(info->pclk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable pclk\n"); return ret; } // 注册一个清理动作,用于在设备移除时禁用 APB 时钟 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; }
// 启用 SARADC 转换器时钟 ret = clk_prepare_enable(info->clk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable converter clock\n"); return ret; } // 注册一个清理动作,用于在设备移除时禁用转换器时钟 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; } // 将 IIO 设备与平台设备相关联 platform_set_drvdata(pdev, indio_dev);
/** * 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. */ structiio_chan_spec { enumiio_chan_typetype; int channel; int channel2; unsignedlong address; int scan_index; struct { char sign; u8 realbits; u8 storagebits; u8 shift; u8 repeat; enumiio_endianendianness; } 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; conststructiio_event_spec *event_spec; unsignedint num_event_specs; conststructiio_chan_spec_ext_info *ext_info; constchar *extend_name; constchar *datasheet_name; unsigned modified:1; unsigned indexed:1; unsigned output:1; unsigned differential:1; }
/** * 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. */ #define devm_iio_device_register(dev, indio_dev) \ __devm_iio_device_register((dev), (indio_dev), THIS_MODULE)
if (!indio_dev->info) // 检查设备是否已初始化 return -ENODEV;
if (count < sizeof(struct iio_event_data)) // 确保缓冲区足够大以容纳事件数据 return -EINVAL;
do { if (kfifo_is_empty(&ev_int->det_events)) { // 检查事件队列是否为空 if (filep->f_flags & O_NONBLOCK) // 如果是非阻塞模式,直接返回 -EAGAIN return -EAGAIN;
// 阻塞等待事件队列中有数据或设备被移除 ret = wait_event_interruptible(ev_int->wait, !kfifo_is_empty(&ev_int->det_events) || indio_dev->info == NULL); if (ret) // 如果等待被中断,返回错误码 return ret; if (indio_dev->info == NULL) // 如果设备被移除,返回 -ENODEV return -ENODEV; } // 加锁保护对事件队列的访问 if (mutex_lock_interruptible(&ev_int->read_lock)) return -ERESTARTSYS; // 将事件数据从内核 FIFO 队列复制到用户空间 ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); mutex_unlock(&ev_int->read_lock); // 解锁
if (ret) // 如果复制失败,返回错误码 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. */ // 如果未读取到任何数据且是非阻塞模式,返回 -EAGAIN if (copied == 0 && (filep->f_flags & O_NONBLOCK)) return -EAGAIN;
} while (copied == 0); // 如果未读取到数据,继续循环等待
return copied; // 返回实际读取的字节数 }
该函数的 do while 循环,它通过检查内核 FIFO 事件队列是否为空来决定是否需要等待事件数据; 在非阻塞模式下直接返回 -EAGAIN,而在阻塞模式下通过等待队列挂起进程直到有数据或设备被移除;接下来使用互斥锁保护对 FIFO 队列的并发访问,确保线程安全; 最后将事件数据从内核空间复制到用户空间,并根据读取结果处理非阻塞模式下的特殊情况,如果未读取到数据则继续循环等待,直至成功读取并返回实际读取的字节数或错误码。
/** * 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_tiio_event_poll(struct file *filep, struct poll_table_struct *wait) { // 获取与文件关联的 IIO 设备结构体 structiio_dev *indio_dev = filep->private_data; structiio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); // 获取该设备的事件接口结构体 structiio_event_interface *ev_int = iio_dev_opaque->event_interface; // 初始化返回的事件掩码为 0 __poll_t events = 0;
// 如果设备没有有效的 info 结构体,直接返回空事件(无事件) if (!indio_dev->info) return events;
/** * 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 **/ staticintiio_chrdev_open(struct inode *inode, struct file *filp) { // 从 inode 中获取对应的 IIO 设备结构体 structiio_dev *indio_dev = container_of(inode->i_cdev, struct iio_dev, chrdev);
// 检查设备是否已被占用,如果忙则返回 -EBUSY if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags)) return -EBUSY;
// 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_tiio_buffer_read_outer(struct file *filp, char __user *buf, size_t n, loff_t *f_ps) { structiio_dev *indio_dev = filp->private_data; // indio_dev = 一个 IIO 设备 structiio_buffer *rb = indio_dev->buffer; // rb = 绑定的 ring buffer,rb->access->read 是真正的 buffer 实现(DMA / kfifo / hw buffer) DEFINE_WAIT_FUNC(wait, woken_wake_function); size_t datum_size; size_t to_wait; int ret = 0;
// 合法性检查 if (!indio_dev->info) return -ENODEV;
if (!rb || !rb->access->read) return -EINVAL;
datum_size = rb->bytes_per_datum; // 一个 datum = 一次采样数据的字节大小。
/* * If datum_size is 0 there will never be anything to read from the * buffer, so signal end of file now. */ if (!datum_size) // 说明这个 buffer 根本不产生数据 return0;
if (filp->f_flags & O_NONBLOCK) // 非阻塞模式 to_wait = 0; else// 阻塞模式 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; }
// buffer 里是否已经有足够数据? if (!iio_buffer_ready(indio_dev, rb, to_wait, n / datum_size)) { // 如果没有信号 if (signal_pending(current)) { // 检查信号,用户 Ctrl+C ret = -ERESTARTSYS; // 返回 -ERESTARTSYS break; } // 睡眠,进程进入 interruptible sleep wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); continue; }
ret = rb->access->read(rb, n, buf); // 真正读数据 if (ret == 0 && (filp->f_flags & O_NONBLOCK)) // 非阻塞模式特殊处理 ret = -EAGAIN; } while (ret == 0); // 只要没读到数据就继续等 remove_wait_queue(&rb->pollq, &wait); // 清理等待队列
/** * struct iio_buffer - general buffer structure * * Note that the internals of this structure should only be of interest to * those writing new buffer implementations. */ structiio_buffer { /** @length: Number of datums in buffer. */ unsignedint length;// 缓冲区中数据单元的数量
/** @bytes_per_datum: Size of individual datum including timestamp. */ size_t bytes_per_datum; // 单个数据单元的大小(包括时间戳)
/** * @access: Buffer access functions associated with the * implementation. */ conststructiio_buffer_access_funcs *access;// 缓冲区访问函数,提供读取、写入等操作方法
/** @scan_mask: Bitmask used in masking scan mode elements. */ long *scan_mask; // 扫描模式元素的位掩码,用于选择启用的通道
/** @demux_list: List of operations required to demux the scan. */ structlist_headdemux_list;// 解复用扫描所需的操作列表
/** @pollq: Wait queue to allow for polling on the buffer. */ wait_queue_head_t pollq; // 等待队列,用于支持对缓冲区的轮询操作
/** @watermark: Number of datums to wait for poll/read. */ unsignedint watermark; // 水位值,表示轮询或读取时需要等待的数据单元数量
/* private: */ /* @scan_timestamp: Does the scan mode include a timestamp. */ bool scan_timestamp; // 扫描模式是否包含时间戳
/* @scan_el_dev_attr_list: List of scan element related attributes. */ structlist_headscan_el_dev_attr_list;// 与扫描元素相关的属性列表
/* @buffer_group: Attributes of the buffer group. */ structattribute_groupbuffer_group;// 缓冲区组的属性
/* * @scan_el_group: Attribute group for those attributes not * created from the iio_chan_info array. */ structattribute_groupscan_el_group;// 属性组,用于那些未从 iio_chan_info 数组创建的属性
/* @attrs: Standard attributes of the buffer. */ conststructattribute **attrs;// 缓冲区的标准属性
/* @demux_bounce: Buffer for doing gather from incoming scan. */ void *demux_bounce; // 用于从传入扫描中收集数据的缓冲区
/* @buffer_list: Entry in the devices list of current buffers. */ structlist_headbuffer_list;// 设备当前缓冲区列表中的条目
/* @ref: Reference count of the buffer. */ structkrefref;// 缓冲区的引用计数,用于管理资源释放 };
/** * 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. **/ structiio_buffer_access_funcs { // 将数据存储到缓冲区中 int (*store_to)(struct iio_buffer *buffer, constvoid *data); // 从缓冲区中读取最多 n 字节的数据到用户空间缓冲区 buf int (*read)(struct iio_buffer *buffer, size_t n, char __user *buf);
/** * 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_tiio_buffer_poll(struct file *filp, struct poll_table_struct *wait) { // 从文件结构体中获取与设备关联的 IIO 设备结构体 structiio_dev *indio_dev = filp->private_data;
staticintiio_device_register_sysfs(struct iio_dev *indio_dev) { structiio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int i, ret = 0, attrcount, attrn, attrcount_orig = 0; structiio_dev_attr *p; structattribute **attr, *clk =NULL;
/* First count elements in any existing 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. */ /* * 新的通道注册方法 - 依赖于一个事实:如果组名为空,则不需要初始化组。 */ if (indio_dev->channels) // 如果设备有通道定义 for (i = 0; i < indio_dev->num_channels; i++) { // 遍历所有通道 conststructiio_chan_spec *chan = &indio_dev->channels[i]; // 如果通道类型为时间戳,记录时间戳时钟属性 if (chan->type == IIO_TIMESTAMP) clk = &dev_attr_current_timestamp_clock.attr; // 将通道的 sysfs 属性添加到系统中 ret = iio_device_add_channel_sysfs(indio_dev, chan); if (ret < 0) // 如果添加失败,跳转到错误处理 goto error_clear_attrs; attrcount += ret; // 累加新增的属性数量 } // 如果设备支持事件接口,记录时间戳时钟属性 if (iio_dev_opaque->event_interface) clk = &dev_attr_current_timestamp_clock.attr; // 如果设备有名称,增加属性计数 if (indio_dev->name) attrcount++; if (indio_dev->label) attrcount++; // 如果存在时间戳时钟属性,增加属性计数 if (clk) attrcount++;
// 分配内存以存储所有属性指针 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) { // 如果分配失败,返回内存不足错误 ret = -ENOMEM; goto error_clear_attrs; } /* Copy across original attributes */ // 复制原有的属性到新的属性数组中 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; // 记录当前属性数组的索引 /* Add all elements from the list. */ // 将通道属性列表中的所有属性添加到属性数组中 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 (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 (clk) iio_dev_opaque->chan_attr_group.attrs[attrn++] = clk; // 将属性组添加到设备的属性组列表中 indio_dev->groups[indio_dev->groupcounter++] = &iio_dev_opaque->chan_attr_group;
case IIO_SEPARATE: // 独立属性:根据是否索引化构造名称 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);
// 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, constchar *fmt, ...) { // 定义一个可变参数列表变量 vargs,用于存储传递给函数的可变参数 va_list vargs;
/** * 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. */ intdevice_add(struct device *dev) { structdevice *parent; structkobject *kobj; structclass_interface *class_intf; int error = -EINVAL; structkobject *glue_dir =NULL;
// 获取设备引用,确保设备有效 dev = get_device(dev); if (!dev) goto done;
// 初始化设备私有数据 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() */ // 设置设备名称 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; }
/* use parent numa_node */ // 继承父设备的 NUMA 节点 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 */ // 注册设备到通用层 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;
// 创建设备属性文件和符号链接 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; // 将设备添加到总线和电源管理子系统 error = bus_add_device(dev); if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(dev);
// 如果设备号有效,创建相关文件和节点 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(). */ // 通知客户端设备已添加 if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); // 发送 KOBJ_ADD uevent 事件 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. */ // 处理设备链接(消费者-供应商关系) if (dev->fwnode && !dev->fwnode->dev) { dev->fwnode->dev = dev; fw_devlink_link_device(dev); }
// 探测设备并绑定驱动程序 bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children);
// 如果设备属于某个类,将设备添加到类中 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);
上面代码这部分:
1 2 3 4 5 6 7 8 9 10 11 12
// 如果设备号有效,创建相关文件和节点 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;
// 分配设备 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); returnNULL; } // 设置设备名称 dev_set_name(&dev->dev, "iio:device%d", dev->id); INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); // 初始化缓冲区链表
/** * 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. */ intcdev_device_add(struct cdev *cdev, struct device *dev) { int rc = 0; // 如果设备 dev 具有有效的设备号 (devt) if (dev->devt) { // 设定 cdev 的父对象为设备 dev cdev_set_parent(cdev, &dev->kobj);
/** * iio_trigger_register() - register a trigger with the IIO core * @trig_info: trigger to be registered **/ #define iio_trigger_register(trig_info) \ __iio_trigger_register((trig_info), THIS_MODULE) int __iio_trigger_register(struct iio_trigger *trig_info, struct module *this_mod);
int __iio_trigger_register(struct iio_trigger *trig_info, struct module *this_mod) { int ret; // 设置触发器的模块拥有者为当前模块 trig_info->owner = this_mod;
// 为触发器分配一个唯一的 ID,使用 ida_simple_get 从全局 ID 分配器中获取 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 */ // 设置触发器设备的名称,用于 sysfs 目录等,格式为 "trigger%ld" dev_set_name(&trig_info->dev, "trigger%ld", (unsignedlong) trig_info->id);
// 将触发器设备添加到设备模型中 ret = device_add(&trig_info->dev); if (ret) // 如果设备添加失败,跳转到错误处理标签 error_unregister_id goto error_unregister_id;
/* Add to list of available triggers held by the IIO core */ // 锁定触发器列表,确保线程安全 mutex_lock(&iio_trigger_list_lock); // 检查是否已经存在同名的触发器 if (__iio_trigger_find_by_name(trig_info->name)) { // 如果存在重复名称,打印错误日志并跳转到错误处理标签 error_device_del pr_err("Duplicate trigger name '%s'\n", trig_info->name); ret = -EEXIST; goto error_device_del; } // 将触发器添加到 IIO 核心维护的触发器列表中 list_add_tail(&trig_info->list, &iio_trigger_list); mutex_unlock(&iio_trigger_list_lock); // 解锁触发器列表
/** * 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 */ staticssize_tiio_trigger_read_current(struct device *dev, struct device_attribute *attr, char *buf) { // 将设备结构体转换为 IIO 设备结构体 structiio_dev *indio_dev = dev_to_iio_dev(dev);
/** * 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 */ staticssize_tiio_trigger_write_current(struct device *dev, struct device_attribute *attr, constchar *buf, size_t len) { // 将设备结构体转换为 IIO 设备结构体 structiio_dev *indio_dev = dev_to_iio_dev(dev); structiio_trigger *oldtrig = indio_dev->trig; // 保存当前绑定的触发器 structiio_trigger *trig;// 新触发器指针 int ret;
/* 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? */ intiio_trigger_attach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { int ret = 0; // 检查触发器的资源池是否为空,判断触发器是否未被使用 bool notinuse = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
/* Prevent the module from being removed whilst attached to a trigger */ /* 防止在触发器被使用时模块被卸载 */ __module_get(pf->indio_dev->driver_module);
/* Get irq number */ /* 获取触发器的中断号 */ 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; }
/* Enable trigger in driver */ /* 如果触发器支持设置状态且未被使用,则启用触发器 */ 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. */ /* * 检查是否注册到自己的触发器: * 判断依据是 IIO 设备和触发器设备是否有相同的父设备。 */ if (pf->indio_dev->dev.parent == trig->dev.parent) trig->attached_own_device = true;