驱动探测函数研究

本文详细解析了USB骨架设备驱动程序中的skel_probe函数。该函数负责为USB设备分配内存并初始化状态,设置端点信息,并注册设备。文章深入介绍了函数内部的重要步骤和技术细节。
AI助手已提取文章相关产品:

static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_skel *dev;

struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
int i;
int retval = -ENOMEM;

/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
err("Out of memory");
goto error;
}
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->err_lock);
init_usb_anchor(&dev->submitted);

dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;

/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;

if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
err("Could not allocate bulk_in_buffer");
goto error;
}
}

if (!dev->bulk_out_endpointAddr &&
usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
err("Could not find both bulk-in and bulk-out endpoints");
goto error;
}

/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);

/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class);
if (retval) {
/* something prevented us from registering this driver */
err("Not able to get a minor for this device.");
usb_set_intfdata(interface, NULL);
goto error;
}

/* let the user know what node this device is now attached to */
dev_info(&interface->dev,
"USB Skeleton device now attached to USBSkel-%d",
interface->minor);
return 0;

error:
if (dev)
/* this frees allocated memory */
kref_put(&dev->kref, skel_delete);
return retval;
}



kref_init 函数

sema_init 函数

mutex_init 函数

spin_lock_init函数

init_usb_anchor函数

usb_get_dev函数

您可能感兴趣的与本文相关内容

<think>好的,用户现在问的是“探测函数干嘛的”,我需要先回顾之前的对话历史。之前用户询问了8250串口控制器的注册流程,我在回答中提到了探测函数,比如在步骤4中调用`serial8250_register_port()`。用户现在可能对探测函数的具体作用不太清楚,需要进一步解释。 首先,用户可能对驱动开发中的探测函数不太熟悉,或者在使用过程中遇到了相关问题。探测函数在Linux内核驱动中是一个关键概念,尤其是在设备与驱动匹配时。我需要解释清楚探测函数的作用、触发时机以及它执行的具体任务。 接下来,我需要考虑用户可能的背景。用户可能正在学习Linux驱动开发,或者在实际工作中遇到了设备初始化的问题。他们需要理解探测函数在整个设备注册流程中的位置和重要性,以及如何正确实现它。 在之前的回答中,我提到了探测函数用于添加端口,但用户现在需要更详细的解释。探测函数通常负责检测设备是否存在,初始化硬件,分配资源,注册中断处理函数,以及将设备添加到系统中。这些步骤对于设备的正常运行至关重要。 需要区分不同的总线类型,比如PCI和平台设备,它们的探测机制可能不同。用户可能想知道探测函数是如何被调用的,比如通过设备树匹配或者ACPI信息。此外,可能需要举例说明探测函数的结构,比如在平台驱动中实现.probe函数,并展示代码示例。 还要注意用户可能遇到的常见问题,比如资源冲突、中断注册失败等,这些都需要在探测函数中进行错误处理。同时,电源管理和设备树的集成也是现代驱动中的重要部分,需要简要提及。 最后,确保语言通俗易懂,结构清晰,逐步引导用户理解探测函数的作用和实现方法。可能需要分点列出探测函数的主要任务,触发时机,以及实际代码示例,帮助用户更好地理解和应用。</think>在Linux内核驱动开发中,**探测函数(Probe Function)** 是设备初始化的核心机制,其主要作用是通过以下流程实现硬件设备的识别与激活: --- ### 一、核心作用解析 1. **设备身份验证** - 验证设备ID是否匹配驱动支持的硬件(如PCI设备的vendor/device ID) - 检查设备树(Device Tree)中`compatible`属性是否匹配 2. **资源获取** ```c // 典型资源获取代码示例 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 获取内存资源 irq = platform_get_irq(pdev, 0); // 获取中断号 ``` 3. **硬件初始化** - 执行I/O映射(`ioremap`) - 配置时钟(`clk_prepare_enable`) - 重置控制器(reset_control_deassert) 4. **数据结构构建** ```c struct uart_port *port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); port->ops = &serial8250_pops; // 挂载操作函数集 ``` 5. **子系统集成** - 调用`uart_add_one_port()`将设备注册到串口子系统 - 向内核注册中断处理函数(`request_irq`) --- ### 二、触发时机 1. **设备-驱动匹配时** - 当`platform_device`与`platform_driver`的`compatible`字符串匹配时 - 通过总线机制(如PCI/ACPI)自动触发 2. **热插拔场景** - USB串口转换器插入时 - PCIe设备动态加载时 --- ### 三、典型代码结构 以8250驱动中的探测函数为例: ```c static int serial8250_probe(struct platform_device *pdev) { // 1. 获取平台数据 struct plat_serial8250_port *pdata = dev_get_platdata(&pdev->dev); // 2. 映射物理地址 port.mapbase = pdata->iobase; port.membase = ioremap(port.mapbase, pdata->mapsize); // 3. 配置端口参数 port.irq = pdata->irq; port.uartclk = pdata->uartclk; // 4. 注册到串口核心层 ret = serial8250_register_8250_port(&port); // 5. 保存私有数据 platform_set_drvdata(pdev, priv_data); return 0; } ``` --- ### 四、错误处理模式 1. **资源回滚机制** ```c if (!port.membase) { dev_err(dev, "ioremap failed\n"); return -ENOMEM; // 自动触发资源释放 } ``` 2. **渐进式初始化** - 按`资源获取->硬件配置->注册设备`顺序执行 - 每个步骤失败时立即回滚 --- ### 五、特殊场景处理 1. **多端口设备** ```c for (i = 0; i < MAX_PORTS; i++) { if (pdata[i].flags == 0) break; // 遇到终止标记退出循环 serial8250_probe_single_port(&pdata[i]); } ``` 2. **电源管理集成** - 在探测函数中初始化`pm_ops` - 配置唤醒源(`device_init_wakeup`) --- ### 六、调试技巧 1. 通过`dev_dbg(&pdev->dev, "Probing device at 0x%lx\n", port->mapbase)`输出调试信息 2. 使用`cat /sys/kernel/debug/device_component/registers`查看寄存器状态 3. 通过`echo 1 > /sys/module/serial8250/parameters/debug`启用驱动级调试 探测函数的正确实现直接决定了设备能否被内核正确识别和管理,是驱动开发中最需要精心设计的部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值