深入Linux USB框架:剖析硬件驱动的核心部分
1. 简介
Linux USB框架是Linux操作系统中负责处理USB设备的一个框架。它提供了一种标准且简单的接口,使得开发人员可以轻松地编写USB设备驱动程序,并与操作系统进行交互。本文将深入探讨Linux USB框架的核心部分,并解析硬件驱动的关键点。
2. USB驱动程序的注册
2.1 USB设备初始化
在开始编写USB驱动程序之前,需要对USB设备进行初始化。这涉及到在系统中注册USB设备。注册USB设备的过程可以通过下面的代码来演示:
struct usb_device_id my_usb_table[] = {
{ USB_DEVICE(0x1234, 0x5678) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, my_usb_table);
static struct usb_driver my_usb_driver = {
.name = "my_usb_driver",
.probe = my_usb_probe,
.disconnect = my_usb_disconnect,
.id_table = my_usb_table,
};
module_usb_driver(my_usb_driver);
在上述代码中,首先定义了一个usb_device_id
结构体数组my_usb_table
,用来描述支持的USB设备的Vendor ID和Product ID。
然后,通过MODULE_DEVICE_TABLE(usb, my_usb_table)
宏定义,将my_usb_table
注册到Linux内核的设备表中。
接下来,定义了一个usb_driver
结构体my_usb_driver
,其中包含了驱动程序的一些相关信息,如驱动程序名称、驱动程序的初始化函数probe
和断开连接函数disconnect
。
最后,通过module_usb_driver(my_usb_driver)
函数将驱动程序注册到Linux内核。
2.2 USB驱动程序的初始化函数
USB驱动程序的初始化函数在驱动程序被加载时被调用。在这个函数中,可以进行一些必要的设置和初始化工作。下面是一个示例的初始化函数:
static int my_usb_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
// 获取相关设备信息
struct usb_device *udev = interface_to_usbdev(interface);
int retval;
// 打印设备信息
dev_info(&interface->dev, "USB device detected\n");
// 设置USB设备操作
interface->driver_data = devm_kzalloc(&interface->dev, sizeof(struct my_usb), GFP_KERNEL);
if (!interface->driver_data) {
dev_err(&interface→dev, "Memory allocation failed\n");
return -ENOMEM;
}
// ...
return 0;
}
在上述代码中,首先通过interface_to_usbdev
函数将usb_interface
结构体转换为usb_device
结构体,以获取相关的设备信息。
然后,打印设备信息,以确认USB设备已被检测到。
接下来,通过devm_kzalloc
函数分配内存,并将其保存在usb_interface
结构体的driver_data
字段中。这里的示例是用来保存使用的自定义结构体my_usb
。
最后,通过返回0来表示设备的初始化成功。
3. USB传输
3.1 USB传输数据的流程
在USB驱动程序中,为实现数据的传输,需要使用到Linux USB框架提供的一些函数。下面是USB传输数据的一个基本流程:
1. 通过usb_bulk_msg
函数实现批量传输数据。
2. 通过usb_control_msg
函数实现控制传输数据。
3. 通过usb_interrupt_msg
函数实现中断传输数据。
3.2 批量传输数据示例
static int my_usb_send(struct usb_device *udev, void *data, int size)
{
struct usb_host_endpoint *ep;
struct urb *urb;
int retval;
// 获取发送数据的端点
ep = usb_sndbulkpipe(udev, EP_OUT);
// 分配USB请求块
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
// 初始化USB请求块
usb_fill_bulk_urb(urb, udev, ep, data, size, my_usb_send_callback, NULL);
// 设置传输的超时时间
urb->timeout = 5000;
// 提交USB请求块
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
dev_err(&interface->dev, "Failed to submit URB: %d\n", retval);
goto error;
}
// 等待传输完成
retval = usb_wait_for_completion_timeout(&urb->kref, urb->timeout);
if (retval == 0) {
dev_err(&interface->dev, "Transfer timed out\n");
usb_kill_urb(urb);
}
// 检查传输结果
if (urb->status) {
dev_err(&interface->dev, "Transfer failed with status: %d\n", urb->status);
retval = urb->status;
}
// 释放USB请求块
usb_free_urb(urb);
return retval;
error:
usb_free_urb(urb);
return retval;
}
在上述代码中,首先通过usb_sndbulkpipe
函数获取发送数据的批量端点。
然后,通过usb_alloc_urb
函数分配USB请求块,并使用usb_fill_bulk_urb
函数初始化请求块。
接下来,设置传输的超时时间,并提交请求块通过usb_submit_urb
函数执行传输操作。
然后,通过usb_wait_for_completion_timeout
函数等待传输完成。
最后,通过检查传输结果和释放请求块的方式来结束传输操作。
4. 总结
本文深入探讨了Linux USB框架的核心部分,并剖析了硬件驱动中的关键点。我们了解了USB驱动程序的注册过程,以及USB传输数据的流程和实现方法。了解了这些核心知识后,我们可以更加深入地理解Linux USB框架,并能够编写高效稳定的USB驱动程序。