第十三章--USB驱动程序

        Linux内核支持两种主要类型的USB驱动程序:宿主(host)系统上的驱动程序和设备(device)上的驱动程序。


一、USB设备基础
1.1、端点
        USB通信最基本的形式是通过一个名为端点(endpoint)的东西。USB端点只能往一个方向传送数据,从主机到设备(称为输出端点)或者从设备到主机(称为输入端点)。端点可以看作是单向的管道。
        USB端点有四种不同的类型,分别为:
        控制:
        控制端点用来控制对USB设备不同部分的访问。它们通常用于配置设备、获取设备信息、发送命令到设备,或者获取设备的状态报告。这些端点一般体积较小。每个USB设备都有一个名为“端点0”的控制端点,USB核心使用该端点在插入时进行设备的配置。USB协议保证这些传输始终有足够的保留带宽以传送数据到设备。
        中断:
        每当USB宿主要求设备传输数据时,中断端点就以一个固定的速率来传送少量的数据。这些端点是USB键盘和鼠标所使用的主要传输方式。他们通常还用于传输数据到USB设备以控制设备,不过一般不用来传输大量的数据。USB协议保证这些传输始终有足够的保留带宽以传送数据。
        批量:
        批量(bulk)端点传输大批量的数据。这些端点通常比中断端点大的多(他们可以一次持有更多的字符)。它们常见于需要确保没有数据丢失的传输的设备。USB协议不保证这些传输始终可以在特定的时间内完成。如果总线上的空间不足以发送整个批量包,它将被分割为多个包进行传输。这些端点通常出现在打印机、存储设备和网络设备上。
        等时:
        等时(isochronous)端点同样可以传送大批量的数据,但数据是否到达是没有保证的。这些端点用于可以应付数据丢失情况的设备,这类设备更注重于保持一个恒定的数据流。实时的数据收集(例如音频和视频设备)几乎毫无例外都使用这类端点。
        内核中使用struct usb_host_endpoint结构体来描述USB端点。该结构体在另一个名为struct usb_endpoint_descriptor的结构体中包含了真正的端点信息。主要字段有:
        bEndpointAddress:
        这是特定端点的USB地址。这个8位的值中还包含了端点的方向。该字段可以结合位掩码USB_DIR_OUT和USB_DIR_IN来使用,以确定该端点的数据是传向设备还是主机。
        bmAttributes:
        这是端点的类型。该值可以结合位掩码USB_ENDPOINT_XFERTYPE_MASK来使用,以确定此端点的类型是USB_ENDPOINT_XFER_ISOC、USB_ENDPOINT_XFER_BULK还是USB_ENDPOINT_XFER_INT。这些宏分别表述等时、批量和中断端点。
        wMaxPacketSize:
        这是该端点一次可以处理的最大字节数。注意,驱动程序可以发送数量大于此值的数据到端点,但是在实际传输到设备的时候,数据将被分割为wMaxPacketSize大小的块。对于高速设备,通过使用高位中一些额外的位,该字段可以用来支持端点的高带宽模式。
        bInterval:
        如果端点是中断类型,该值是端点的时间设置----也就是说,端点的中断请求间隔时间。该值以毫秒为单位。
1.2、接口
        USB端点被捆绑为接口。
        内核使用struct usb_interface结构体来描述USB接口。USB核心把该结构体传递给USB驱动程序,之后由USB驱动程序来负责控制该结构体。该结构体重要字段有:
        struct usb_host_interface *altsetting:
        一个接口结构体数组,包含了所有可能用于该接口的可选设置。每个struct usb_host_interface结构体包含一套由上述struct usb_host_endpoint结构体定义的端点配置,注意,这些接口结构体没有特定的次序。
        unsigned num_altsetting:
        altsetting指针所指的可选设置的数量。
        struct usb_host_interface *cur_altsetting:
        指向altsetting数组内部的指针,表示该接口的当前活动设置。
        int minor:
        如果捆绑到该接口的USB驱动程序使用USB主设备号,这个变量包含USB核心分配给该接口的次设备号。这仅在一个成功的usb_register_dev调用之后才有效。
1.3、配置
        USB接口本身被捆绑为配置。一个USB设备可以有多个配置,而且可以在配置之间切换以改变设备的状态。
        Linux使用struct usb_host_config结构体来描述USB配置,使用struct usb_device结构体来描述整个USB设备。
        逻辑单元之间的关系如下:
        设备通常具有一个或者更多的配置。
        配置经常具有一个或者更多的接口。
        接口通常具有一个或者更多的设置。
        接口没有或者具有一个以上的端点。

二、USB和Sysfs


三、USB urb
        Linux内核中的USB代码通过一个称为urb(USB请求块)的东西和所有的USB设备通信。这个请求块使用struct urb结构体来描述。urb被用来以一种异步的方式往/从特定的USB设备上的特定USB端点发送/接收数据。USB设备驱动程序可能会为单个端点分配许多urb,也可能对许多不同的端点重用单个的urb。
3.1、struct urb结构体内容

3.2、创建和销毁urb
        使用usb_alloc_urb函数创建。
        struct usb *usb_alloc_urb(int ios_packets, int mem_flags);
        iso_packets:是该urb应该包含的等时数据包的数量。如果不打算创建等时urb,该值应该设置为0。
        mem_flags:和传递给用于从内核分配内存的kmalloc函数的标志有相同的类型。
        释放urb:
        void usb_free_urb(struct urb *urb);
3.2.1、中断urb

3.2.2、批量urb

3.2.3、控制urb

3.2.4、等时urb

3.3、提交urb

3.4、结束urb:结束回调处理例程

3.5、取消urb


四、编写USB驱动程序
        驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否已经安装了硬件。

4.1、驱动程序支持哪些设备?
        struct usb_device_id结构体提供了一列不同类型的该驱动程序支持的USB设备。

4.2、注册USB驱动程序
        所有USB驱动程序都必须创建的主要结构体是struct usb_driver。该结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述了USB驱动程序。
        结构体成员内容如下:

        int (*probe) (struct usb_interface *intf, const struct usb_device_id *id)
        指向USB驱动程序中的探测函数的指针。当USB核心认为它有一个struct usb_interface可以由该驱动程序处理时,它将调用该函数。USB核心用来作判断的指向struct usb_device_id的指针也被传递给该函数。如果USB驱动程序确认传递给它的struct usb_interface,它应该恰当地初始化设备然后返回0。如果驱动程序不确认该设备,或者发生了错误,它应该返回一个负的错误值。
        使用usb_register_driver函数将struct usb_driver注册到USB核心。
        usb_deregister_driver注销USB设备驱动。
4.3、探测和断开的细节
        当一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用;探测函数应该检查传递给它的设备信息,确定驱动程序是否真的适合该设备。当驱动程序因为某种原因不应控制设备时,断开函数被调用,它可以做一些清理的工作。探测和断开回调函数都是在USB集线器内核线程的上下文中被调用的,因此在其中睡眠是合法的。
        就在USB设备的断开回调函数被调用之前,所有正在传输到设备的urb都被USB核心取消,因此驱动程序不必要对这些urb显式地调用usb_kill_urb。在USB设备已经被断开之后,如果驱动程序试图通过调用usb_submit_urb来提交一个urb给它,提交将会失败并返回错误值-EPIPE。

4.4、提交和控制urb
        当驱动程序有数据要发送到USB设备时(典型地发生在驱动程序的写函数中),必须分配一个urb来把数据传输给设备:
        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!urb) {
            retval = -ENOMEM;
            goto error;
        }
        在urb被成功地分配之后,还应该创建一个DMA缓冲区来以最高效的方式发送数据到设备,传递给驱动程序的数据应该复制到该缓冲区中:
        buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
        if (!buf) {
            retval = -ENOMEM;
            goto error;
        }
        if (copy_from_user(buf, user_buffer, count)) {
            retval = -EFAULT;
            goto error;
        }
        一旦数据从用户空间正确复制到了局部缓冲区中,urb必须在可以被提交给USB核心之前被正确地初始化:
        /* 正确地初始化urb */
        usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, count, skel_write_bulk_callback, dev);
        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        现在urb被正确地分配了,数据被正确地复制了,urb被正确地初始化了,它就可以提交给USB核心以传输到设备:
        /* 把数据从批量端口发出 */
        retval = usb_submit_urb(urb, GFP_KERNEL);
        if (retval) {
                err(%s - failed submitting write urb, error %d", __FUNCTION__, retval);
                goto error;
        }
        当urb回调函数正在运行时另一个urb被提交到设备是很常见的。这对于发送流式数据到设备很有用。不要忘了urb回调函数是运行在中断上下文中的,因此它不应该进行任何内存分配、持有任何信号量或者做任何其他可能导致进程睡眠的事情。当在回调函数内提交一个urb时,如果它在提交过程中需要分配新的内存块的话,使用GFP_ATOMIC标志来告诉USB核心不要睡眠。
4.5、不使用urb的USB传输


4.6、其他USB数据函数


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值