Linux usb 设备驱动 (1)

USB host、usb device、USB OTG的判断 USB host、usb device、USB OTG的判断经典博客 经典博客 OTG数据线与普通USB数据线有什么区别? https://www.jb51.net/shouji/332556.html USB OTG简介、与普通USB线的区别 https://blog.youkuaiyun.com/mao0514/article/details/46345861 linux usb驱动——host与dev... 阅读详情
Linux USB驱动框架分析(一)

     Linux的设备驱动都遵循一个惯例——表征驱动程序(用driver更贴切一些,应该称为驱动器比较好吧)的结构体,结构体里面应该包含了驱动程序所需要的所有资源。用术语来说,就是这个驱动器对象所拥有的属性及成员。由于Linux的内核用c来编写,所以我们也按照这种结构化的思想来分析代码,但我还是希望从OO的角度来阐述这些细节。这个结构体的名字有驱动开发人员决定,比如说,鼠标可能有一个叫做mouse_dev的struct,键盘可能由一个keyboard_dev的struct(dev for device,我们做的只是设备驱动)。而这次我们来分析一下Linux内核源码中的一个usb-skeleton(就是usb驱动的骨架咯),自然,他定义的设备结构体就叫做usb-skel:

struct usb_skel {

     struct usb_device *      udev;                 /* the usb device for this device */

     struct usb_interface *   interface;            /* the interface for this device */

     struct semaphore       limit_sem;         /* limiting the number of writes in progress */

     unsigned char *         bulk_in_buffer;     /* the buffer to receive data */

     size_t         bulk_in_size;                  /* the size of the receive buffer */

     __u8          bulk_in_endpointAddr;        /* the address of the bulk in endpoint */

     __u8          bulk_out_endpointAddr;      /* the address of the bulk out endpoint */

     struct kref   kref;

};

       这里我们得补充说明一下一些USB的协议规范细节。USB能够自动监测设备,并调用相应得驱动程序处理设备,所以其规范实际上是相当复杂的,幸好,我们不必理会大部分细节问题,因为Linux已经提供相应的解决方案。就我现在的理解来说,USB的驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,但我们至少要了解他的功能。形象得说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB通道到达该到的地方,这部分工作由usb_core来完成。当USB设备接到USB控制器接口时,usb_core就检测该设备的一些信息,例如生产厂商ID和产品的ID,或者是设备所属的class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备。里面复杂细节我们不用管,我们要做的是另一块工作——usb的设备驱动。也就是说,我们就等着usb_core告诉我们要工作了,我们才工作。

       从开发人员的角度看,每一个usb设备有若干个配置(configuration)组成,每个配置又可以有多个接口(interface),每个接口又有多个设置,而接口本身可能没有端点或者多个端点(end point)。USB的数据交换通过端点来进行,主机与各个端点之间建立起单向的管道来传输数据。而这些接口可以分为四类:

 控制(control)

       用于配置设备、获取设备信息、发送命令或者获取设备的状态报告

中断(interrupt)

       当USB宿主要求设备传输数据时,中断端点会以一个固定的速率传送少量数据,还用于发送数据到USB设备以控制设备,一般不用于传送大量数据。

批量(bulk)

       用于大量数据的可靠传输,如果总线上的空间不足以发送整个批量包,它会被分割成多个包传输。

等时(isochronous)

       大量数据的不可靠传输,不保证数据的到达,但保证恒定的数据流,多用于数据采集。

       Linux中用struct usb_host_endpoint来描述USB端点,每个usb_host_endpoint中包含一个struct usb_endpoint_descriptor结构体,当中包含该端点的信息以及设备自定义的各种信息,这些信息包括:

bEndpointAddress(b for byte)

       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

       端点一次处理的最大字节数。发送的BULK包可以大于这个数值,但会被分割传送。

bInterval

       如果端点是中断类型,该值是端点的间隔设置,以毫秒为单位。

       在逻辑上,一个USB设备的功能划分是通过接口来完成的。比如说一个USB扬声器,可能会包括有两个接口:一个用于键盘控制,另外一个用于音频流传输。而事实上,这种设备需要用到不同的两个驱动程序来操作,一个控制键盘,一个控制音频流。但也有例外,比如蓝牙设备,要求有两个接口,第一用于ACL跟EVENT的传输,另外一个用于SCO链路,但两者通过一个驱动控制。在Linux上,接口使用struct usb_interface来描述,以下是该结构体中比较重要的字段:

struct usb_host_interface *altsetting(注意不是usb_interface)

       其实据我理解,他应该是每个接口的设置,虽然名字上有点奇怪。该字段是一个设置的数组(一个接口可以有多个设置),每个usb_host_interface都包含一套由struct usb_host_endpoint定义的端点配置。但这些配置次序是不定的。

unsigned num_altstting

       可选设置的数量,即altsetting所指数组的元素个数。

struct usb_host_interface *cur_altsetting

       当前活动的设置,指向altsetting数组中的一个。

int minor

       当捆绑到该接口的USB驱动程序使用USB主设备号时,USB core分配的次设备号。仅在成功调用usb_register_dev之后才有效。    

       除了它可以用struct usb_host_config来描述之外,到现在为止,我对配置的了解不多。而整个USB设备则可以用struct usb_device来描述,但基本上只会用它来初始化函数的接口,真正用到的应该是我们之前所提到的自定义的一个结构体。

Linux USB驱动框架分析(二)

      了解过USB一些规范细节之后,我们现在来看看Linux的驱动框架。事实上,Linux的设备驱动,特别是这种hotplug的USB设备驱动,会被编译成模块,然后在需要挂载到内核。要写一个Linux的模块并不复杂,以一个helloworld为例:

#include <linux/init.h>

#include <linux/module.h>

MODULE_LICENSE(“GPL”);

static int hello_init(void)

{

     printk(KERN_ALERT “Hello World!\n”);

     return 0;

}

static int hello_exit(void)

{

     printk(KERN_ALERT “GOODBYE!\n”);

}

module_init(hello_init);

module_exit(hello_exit);

    这个简单的程序告诉大家应该怎么写一个模块,MODULE_LICENSE告诉内核该模块的版权信息,很多情况下,用GPL或者BSD,或者两个,因为一个私有模块一般很难得到社区的帮助。module_init和module_exit用于向内核注册模块的初始化函数和模块推出函数。如程序所示,初始化函数是hello_init,而退出函数是hello_exit。

    另外,要编译一个模块通常还需要用到内核源码树中的makefile,所以模块的Makefile可以写成:

ifneq ($(KERNELRELEASE),)

obj-m:= hello.o#usb-dongle.o

else

KDIR:= /usr/src/linux-headers-$(shell uname -r)

BDIR:= $(shell pwd)

default:

     $(MAKE) -C $(KDIR) M=$(PWD) modules

.PHONY: clean

clean:

     make -C $(KDIR) M=$(BDIR) clean

endif

    可以用insmod跟rmmod来验证模块的挂载跟卸载,但必须用root的身份登陆命令行,用普通用户加su或者sudo在Ubuntu上的测试是不行的。

Linux USB驱动框架分析(三)

    下面分析一下usb-skeleton的源码。这个范例程序可以在linux-2.6.17/drivers/usb下找到,其他版本的内核程序源码可能有所不同,但相差不大。大家可以先找到源码看一看,先有个整体印象。

    之前已经提到,模块先要向内核注册初始化跟销毁函数:

static int __init usb_skel_init(void)

{

     int result;

     /* register this driver with the USB subsystem */

     result = usb_register(&skel_driver);

     if (result)

         err("usb_register failed. Error number %d", result);

     return result;

}

static void __exit usb_skel_exit(void)

{

     /* deregister this driver with the USB subsystem */

     usb_deregister(&skel_driver);

}

module_init (usb_skel_init);

module_exit (usb_skel_exit);

MODULE_LICENSE("GPL");

 

    从代码开来,这个init跟exit函数的作用只是用来注册驱动程序,这个描述驱动程序的结构体是系统定义的标准结构struct usb_driver,注册和注销的方法很简单,usb_register(struct *usb_driver), usb_deregister(struct *usb_driver)。那这个结构体需要做些什么呢?他要向系统提供几个函数入口,跟驱动的名字:

static struct usb_driver skel_driver = {

     .name =      "skeleton",

     .probe =     skel_probe,

     .disconnect = skel_disconnect,

     .id_table =    skel_table,

};

    从代码看来,usb_driver需要初始化四个东西:模块的名字skeleton,probe函数skel_probe,disconnect函数skel_disconnect,以及id_table。

    在解释skel_driver各个成员之前,我们先来看看另外一个结构体。这个结构体的名字有开发人员自定义,它描述的是该驱动拥有的所有资源及状态:

struct usb_skel {

     struct usb_device *      udev;                 /* the usb device for this device */

     struct usb_interface *   interface;            /* the interface for this device */

     struct semaphore       limit_sem;         /* limiting the number of writes in progress */

     unsigned char *         bulk_in_buffer;     /* the buffer to receive data */

     size_t         bulk_in_size;                  /* the size of the receive buffer */

     __u8          bulk_in_endpointAddr;        /* the address of the bulk in endpoint */

     __u8          bulk_out_endpointAddr;      /* the address of the bulk out endpoint */

     struct kref   kref;

};

    我们先来对这个usb_skel作个简单分析,他拥有一个描述usb设备的结构体udev,一个接口interface,用于并发访问控制的semaphore(信号量) limit_sem,用于接收数据的缓冲bulk_in_buffer及其尺寸bulk_in_size,然后是批量输入输出端口地址bulk_in_endpointAddr、bulk_out_endpointAddr,最后是一个内核使用的引用计数器。他们的作用我们将在后面的代码中看到。

    我们再回过头来看看skel_driver。

    name用来告诉内核模块的名字是什么,这个注册之后由系统来使用,跟我们关系不大。

    id_table用来告诉内核该模块支持的设备。usb子系统通过设备的production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备,并调用相关的驱动程序作处理。我们可以看看这个id_table到底是什么东西:

/* Define these values to match your devices */

#define USB_SKEL_VENDOR_ID  0xfff0

#define USB_SKEL_PRODUCT_ID 0xfff0

/* table of devices that work with this driver */

static struct usb_device_id skel_table [] = {

     { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },

     { }                    /* Terminating entry */

};

MODULE_DEVICE_TABLE (usb, skel_table); 

    MODULE_DEVICE_TABLE的第一个参数是设备的类型,如果是USB设备,那自然是usb(如果是PCI设备,那将是pci,这两个子系统用同一个宏来注册所支持的设备。这涉及PCI设备的驱动了,在此先不深究)。后面一个参数是设备表,这个设备表的最后一个元素是空的,用于标识结束。代码定义了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备的vendor ID和product ID,如果它们的值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备的驱动。

Linux USB驱动框架分析(四)

       probe是usb子系统自动调用的一个函数,有USB设备接到硬件集线器时,usb子系统会根据production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备调用相应驱动程序的probe(探测)函数,对于skeleton来说,就是skel_probe。系统会传递给探测函数一个usb_interface *跟一个struct usb_device_id *作为参数。他们分别是该USB设备的接口描述(一般会是该设备的第0号接口,该接口的默认设置也是第0号设置)跟它的设备ID描述(包括Vendor ID、Production ID等)。probe函数比较长,我们分段来分析这个函数:

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

dev->interface = interface;

       在初始化了一些资源之后,可以看到第一个关键的函数调用——interface_to_usbdev。他通过一个usb_interface来得到该接口所在设备的设备描述结构。本来,要得到一个usb_device只要用interface_to_usbdev就够了,但因为要增加对该usb_device的引用计数,我们应该在做一个usb_get_dev的操作,来增加引用计数,并在释放设备时用usb_put_dev来减少引用计数。这里要解释的是,该引用计数值是对该usb_device的计数,并不是对本模块的计数,本模块的计数要由kref来维护。所以,probe一开始就有初始化kref。事实上,kref_init操作不单只初始化kref,还将其置设成1。所以在出错处理代码中有kref_put,它把kref的计数减1,如果kref计数已经为0,那么kref会被释放。kref_put的第二个参数是一个函数指针,指向一个清理函数。注意,该指针不能为空,或者kfree。该函数会在最后一个对kref的引用释放时被调用(如果我的理解不准确,请指正)。下面是内核源码中的一段注释及代码:

/**

 * kref_put - decrement refcount for object.

 * @kref: object.

 * @release: pointer to the function that will clean up the object when the

 *        last reference to the object is released.

 *        This pointer is required, and it is not acceptable to pass kfree

 *        in as this function.

 *

 * Decrement the refcount, and if 0, call release().

 * Return 1 if the object was removed, otherwise return 0.  Beware, if this

 * function returns 0, you still can not count on the kref from remaining in

 * memory.  Only use the return value if you want to see if the kref is now

 * gone, not present.

 */

int kref_put(struct kref *kref, void (*release)(struct kref *kref))

{

     WARN_ON(release == NULL);

     WARN_ON(release == (void (*)(struct kref *))kfree);

      /*

      * if current count is one, we are the last user and can release object

      * right now, avoiding an atomic operation on 'refcount'

      */

     if ((atomic_read(&kref->refcount) == 1) ||

         (atomic_dec_and_test(&kref->refcount))) {

         release(kref);

         return 1;

     }

     return 0;

}

       当我们执行打开操作时,我们要增加kref的计数,我们可以用kref_get来完成。所有对struct kref的操作都有内核代码确保其原子性。

      得到了该usb_device之后,我们要对我们自定义的usb_skel各个状态跟资源作初始化。这部分工作的任务主要是向usb_skel注册该usb设备的端点。这里可能要补充以下一些关于usb_interface_descriptor的知识,但因为内核源码对该结构体的注释不多,所以只能靠个人猜测。在一个usb_host_interface结构里面有一个usb_interface_descriptor叫做desc的成员,他应该是用于描述该interface的一些属性,其中bNumEndpoints是一个8位(b for byte)的数字,他代表了该接口的端点数。probe然后遍历所有的端点,检查他们的类型跟方向,注册到usb_skel中。

     /* 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 &&

                ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) = = USB_DIR_IN) &&

             ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) = = USB_ENDPOINT_XFER_BULK)){

              /* 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 &&

            ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)= =USB_DIR_OUT) && ((endpoint->bmAttributes &

USB_ENDPOINT_XFERTYPE_MASK)= = USB_ENDPOINT_XFER_BULK))

{

              /* 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;

     }

Linux USB驱动框架分析(五)

      接下来的工作是向系统注册一些以后会用到的信息。首先我们来说明一下usb_set_intfdata(),他向内核注册一个data,这个data的结构可以是任意的,这段程序向内核注册了一个usb_skel结构,就是我们刚刚看到的被初始化的那个,这个data可以在以后用usb_get_intfdata来得到。

 

usb_set_intfdata(interface, dev);

retval = usb_register_dev(interface, &skel_class);

      然后我们向这个interface注册一个skel_class结构。这个结构又是什么?我们就来看看这到底是个什么东西:

static struct usb_class_driver skel_class = {

     .name =       "skel%d",

     .fops =       &skel_fops,

     .minor_base = USB_SKEL_MINOR_BASE,

};

      它其实是一个系统定义的结构,里面包含了一名字、一个文件操作结构体还有一个次设备号的基准值。事实上它才是定义真正完成对设备IO操作的函数。所以他的核心内容应该是skel_fops。这里补充一些我个人的估计:因为usb设备可以有多个interface,每个interface所定义的IO操作可能不一样,所以向系统注册的usb_class_driver要求注册到某一个interface,而不是device,因此,usb_register_dev的第一个参数才是interface,而第二个参数就是某一个usb_class_driver。通常情况下,linux系统用主设备号来识别某类设备的驱动程序,用次设备号管理识别具体的设备,驱动程序可以依照次设备号来区分不同的设备,所以,这里的次设备好其实是用来管理不同的interface的,但由于这个范例只有一个interface,在代码上无法求证这个猜想。

static struct file_operations skel_fops = {

     .owner = THIS_MODULE,

     .read =       skel_read,

     .write =   skel_write,

     .open =       skel_open,

     .release =    skel_release,

};

这个文件操作结构中定义了对设备的读写、打开、释放(USB设备通常使用这个术语release)。他们都是函数指针,分别指向skel_read、skel_write、skel_open、skel_release这四个函数,这四个函数应该有开发人员自己实现。

 

     当设备被拔出集线器时,usb子系统会自动地调用disconnect,他做的事情不多,最重要的是注销class_driver(交还次设备号)和interface的data:

dev = usb_get_intfdata(interface);

usb_set_intfdata(interface, NULL);

/* give back our minor */

usb_deregister_dev(interface, &skel_class);

    然后他会用kref_put(&dev->kref, skel_delete)进行清理,kref_put的细节参见前文。

    到目前为止,我们已经分析完usb子系统要求的各个主要操作,下一部分我们在讨论一下对USB设备的IO操作。

Linux USB驱动框架分析(六)

    说到usb子系统的IO操作,不得不说usb request block,简称urb。事实上,可以打一个这样的比喻,usb总线就像一条高速公路,货物、人流之类的可以看成是系统与设备交互的数据,而urb就可以看成是汽车。在一开始对USB规范细节的介绍,我们就说过USB的endpoint有4种不同类型,也就是说能在这条高速公路上流动的数据就有四种。但是这对汽车是没有要求的,所以urb可以运载四种数据,不过你要先告诉司机你要运什么,目的地是什么。我们现在就看看struct urb的具体内容。它的内容很多,为了不让我的理解误导各位,大家最好还是看一看内核源码的注释,具体内容参见源码树下include/linux/usb.h。

    在这里我们重点介绍程序中出现的几个关键字段:

struct usb_device  *dev

    urb所发送的目标设备。

unsigned int pipe

    一个管道号码,该管道记录了目标设备的端点以及管道的类型。每个管道只有一种类型和一个方向,它与他的目标设备的端点相对应,我们可以通过以下几个函数来获得管道号并设置管道类型:

     unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个控制OUT端点。

     unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个控制IN端点。

     unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个批量OUT端点。

     unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个批量OUT端点。

     unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个中断OUT端点。

     unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个中断OUT端点。

     unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个等时OUT端点。

     unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)

           把指定USB设备的指定端点设置为一个等时OUT端点。

unsigned int transfer_flags

    当不使用DMA时,应该transfer_flags |= URB_NO_TRANSFER_DMA_MAP(按照代码的理解,希望没有错)。

int status

当一个urb把数据送到设备时,这个urb会由系统返回给驱动程序,并调用驱动程序的urb完成回调函数处理。这时,status记录了这次数据传输的有关状态,例如传送成功与否。成功的话会是0。

 

    要能够运货当然首先要有车,所以第一步当然要创建urb:

    struct urb *usb_alloc_urb(int isoc_packets, int mem_flags);

    第一个参数是等时包的数量,如果不是乘载等时包,应该为0,第二个参数与kmalloc的标志相同。

    要释放一个urb可以用:

    void usb_free_urb(struct urb *urb);

    要承载数据,还要告诉司机目的地信息跟要运的货物,对于不同的数据,系统提供了不同的函数,对于中断urb,我们用

    void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

                   void *transfer_buffer, int buffer_length,

                   usb_complete_t complete, void *context, int interval);

    这里要解释一下,transfer_buffer是一个要送/收的数据的缓冲,buffer_length是它的长度,complete是urb完成回调函数的入口,context由用户定义,可能会在回调函数中使用的数据,interval就是urb被调度的间隔。

    对于批量urb和控制urb,我们用:

    void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

                                    void *transfer_buffer, int buffer_length, usb_complete_t complete, void *context);

    void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

                                    unsigned char* setup_packet,void *transfer_buffer,

                    int buffer_length, usb_complete_t complete,void *context);

    控制包有一个特殊参数setup_packet,它指向即将被发送到端点的设置数据报的数据。

    对于等时urb,系统没有专门的fill函数,只能对各urb字段显示赋值。

    有了汽车,有了司机,下一步就是要开始运货了,我们可以用下面的函数来提交urb

    int usb_submit_urb(struct urb *urb, int mem_flags);

    mem_flags有几种:GFP_ATOMIC、GFP_NOIO、GFP_KERNEL,通常在中断上下文环境我们会用GFP_ATOMIC。

当我们的卡车运货之后,系统会把它调回来,并调用urb完成回调函数,并把这辆车作为函数传递给驱动程序。我们应该在回调函数里面检查status字段,以确定数据的成功传输与否。

下面是用urb来传送数据的细节。

/* initialize the urb properly */

usb_fill_bulk_urb(urb, dev->udev,

                    usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),

                    buf, writesize, skel_write_bulk_callback, dev);

urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

/* send the data out the bulk port */

retval = usb_submit_urb(urb, GFP_KERNEL);

    这里skel_write_bulk_callback就是一个完成回调函数,而他做的主要事情就是检查数据传输状态和释放urb:

dev = (struct usb_skel *)urb->context;

/* sync/async unlink faults aren't errors */

if (urb->status && !(urb->status = = -ENOENT || urb->status == -ECONNRESET || urb->status = = -ESHUTDOWN)) {

         dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);

}

/* free up our allocated buffer */

usb_buffer_free(urb->dev, urb->transfer_buffer_length,

              urb->transfer_buffer, urb->transfer_dma);

    事实上,如果数据的量不大,那么可以不一定用卡车来运货,系统还提供了一种不用urb的传输方式,而usb-skeleton的读操作正是采用这种方式实现:

/* do a blocking bulk read to get data from the device */

retval = usb_bulk_msg(dev->udev,

                           usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),

                           dev->bulk_in_buffer,

                           min(dev->bulk_in_size, count),

                           &bytes_read, 10000);

/* if the read was successful, copy the data to userspace */

if (!retval) {

         if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))

                retval = -EFAULT;

         else

                retval = bytes_read;

}

    程序使用了usb_bulk_msg来传送数据,它的原型如下:

    int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,void *data,

                 int len, int *actual length, int timeout)

    这个函数会阻塞等待数据传输完成或者等到超时,data是输入/输出缓冲,len是它的大小,actual length是实际传送的数据大小,timeout是阻塞超时。

    对于控制数据,系统提供了另外一个函数,他的原型是:

         Int usb_contrl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,

                                 __u8 requesttype, __u16 value, __u16 index, void *data,

                                 __u16 size, int timeout);

    request是控制消息的USB请求值、requesttype是控制消息的USB请求类型,value是控制消息的USB消息值,index是控制消息的USB消息索引。具体是什么,暂时不是很清楚,希望大家提供说明。

    至此,Linux下的USB驱动框架分析基本完成了。


【Linux 驱动开发】Linux 驱动开发与 STM32MP1 USB Host 实践全解 本文系统介绍了STM32MP1平台的Linux驱动开发与USB Host实践。主要内容包括:1) 开发环境搭建与驱动框架解析,涵盖SPI/USB子系统架构;2) 设备树配置方法,详细展示了SPI控制器节点定义、pinctrl设置及外设挂载示例;3) USB基础理论,包括拓扑结构、OTG主从模式切换机制及控制器类型;4) 完整驱动开发流程,从设备树节点创建到probe/remove回调实现。通过理论结合实践的方式,为开发者提供了从环境搭建到功能实现的完整技术路径。 阅读详情

相关推荐

ARC学习(6)arc 编译器overlap 地址重叠方式使用

摘要:本文介绍了ARC编译器中使用overlap地址重叠的方法。

张一西的博客 718

USB设备驱动简介

1. USB拓扑结构 设备、配置、接口和端点 1.2 设置、配置、接口、端点 在 USB 设备的逻辑组织中,包含设备、配置、接口和端点 4 个层次。 每个 USB 设备都提供了不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个) ,配置由多个接口组成。 每个配置中可以有多个接口,而设备接口是端点的汇集(collection) 。例如 USB 扬声器可以包含一个音频接口以及对旋钮和按钮的接口。一个配置中的所有接口可...

努力才会有幸福 1385

ctx->cvdl->cuvidGetDecoderCaps(&ctx->caps8) failed -> CUDA_ERROR_DEINITIALIZED: driver shutting down

ffplay -vcodec h264_cuvid sample_720p-2.h264 [quantum6@freeswitch freeswitch]$ ffplay -vcodec h264_cuvid sample_720p-2.h264 ffplay version 4.3.1 Copyright (c) 2003-2020 the FFmpeg developers built with gcc 4.8.5 (GCC) 20150623 (Red Hat 4.8.5-44) .

柳鲲鹏 2406

usb驱动流程分析

usb驱动流程分析(学习笔记)

qq_43355647的博客 1487

学习海康VisionMaster之顶点检测

2:应用场景:海康VisionMaster的顶点检测功能在工业视觉检测中具有广泛的应用场景,尤其在精密制造、电子元件检测和机械加工等领域,其通过高精度定位物体的关键点(如角点、交点或边缘顶点)来实现自动化测量与质量控制。基本参数这里就是画ROI,设定跟踪参数和之前的工具是一样的方法,这里画的方向需要注意,如下图所示,边缘的扫描方向正好对应需要查找的边缘点。一个不是很规则的物体需要检测一个凸台的顶角坐标,其实靠边缘与边缘交点是不太稳定的,如下图所示,边缘很短,很容易产生误抓边缘。1:什么是顶点检测?

yuhouxiyang的专栏 1524

STM32与威纶通触摸屏RS485通信实战:Modbus RTU协议与避坑指南

在工业自动化与嵌入式系统开发中,串行通信是实现设备间数据交换的基础技术。RS485总线凭借其差分信号传输、抗干扰能力强、支持多点通信及长距离传输等特性,成为工业现场广泛应用的物理层标准。其核心原理是利用一对双绞线进行平衡传输,有效抑制共模噪声。在协议层,Modbus RTU作为开放、简洁的主从式应用层协议,定义了统一的数据模型(如线圈、寄存器)和功能码,极大简化了不同厂商设备间的互操作性。结合STM32微控制器的UART外设与威纶通(Weinview)人机界面(HMI),可以构建稳定可靠的监控与控制系统。本

weixin_33925615的博客 329

USB Host和USB Device介绍以及接口参考设计

USB Host:是指驱动过后,板子就可以连接USB接口连接的器件,读取和装载数据了,类似PC。USB Device:是指驱动过后,板子可以当做一个USB移动设备,通过USB连接到电脑上,类似U盘。VBUS的作用1.电源供应:VBUS是USB接口中提供电源电压的信号线。通过VBUS,主机端可以向连接的设备提供所需的电源,使其正常工作。这对于需要从USB接口获取电源的设备非常关键,如移动设备、外部硬盘驱动器等。2.设备检测:VBUS还用于设备的检测和识别。

weixin_46158019的博客 1万+

Manus通用AI智能体完全使用指南(2025最新版)——从注册到高阶技巧全解析

Manus通用AI智能体完全使用指南(2025最新版)——从注册到高阶技巧全解析

m0_69718028的博客 8116

Linux设备驱动之usb设备驱动详解_usb数据什么驱动设备类型(1)

if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read)) //复制到用户空间。struct usb_host _interface *cur_altsetting是usb interface的当前可选设置。struct usb_host_interface *altsetting包含了usb interface的所有可选设置。以上三个结构体分别是usb_driver,usb_bus,usb_device设备结构体。

2401_83620690的博客 579

汽车工程师必看:从CAN到Ethernet,6种车载通信协议全解析(附应用场景对比)

本文全面解析了从CAN到Ethernet的6种车载通信协议,包括CAN、LIN、MOST、FlexRay等,并对比了它们的应用场景和技术特点。文章详细介绍了各协议的演进历程、技术优势及在现代汽车中的混合网络架构设计,为汽车工程师提供了实用的协议选型指南和未来通信技术发展趋势。

weixin_28683689的博客 492

059、RealBasicVSR 实战:真实场景视频超分的退化建模与优化技巧

本文分享了RealBasicVSR在真实场景视频超分中的实战经验。针对传统方法假设理想化下采样导致的性能瓶颈,重点阐述了三个关键技术点:1)通过可学习退化编码器建模复杂未知退化过程,避免手动设计退化参数的局限;2)采用双向传播和DCN对齐增强时序一致性;3)组合多种损失函数并优化训练策略(如余弦退火学习率、随机退化增强)。作者结合项目实践指出了显存管理、训练时长、评估指标等常见问题,并给出实用建议:优先验证数据退化复杂度,重视数据预处理,合理调整超参数。文章强调真实场景视频超分的核心在于让模型自适应未知退化

ai技术增长的博客 180

Linux设备驱动之USB hub驱动 <1>

------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ ------------------------------------------ 一:前言 继UHCI的驱动之后,我们对USB Control的运作有了一定的了解.在接下来的分析中,我们对...

FLY_THINK2012的博客 454

28、基准剂量法:原理、应用与计算详解

本文详细介绍了基准剂量法(BMD)的原理、在监管机构中的应用以及具体的计算方法。内容涵盖样本量与BMD的关系、主要监管机构如EPA对BMD的应用、BMD的模型选择与数据拟合过程、拟合优度评估、下置信限(BMDL)计算,以及实验设计对BMD结果的影响。同时,还讨论了剂量指标和响应选择的关键问题,为理解和应用基准剂量法提供了全面的参考。

info6的博客 280

linux选择usb功能,USB gadget设备驱动解析(1)——功能体验

利用Linux USB gadget设备驱动可以实现一些比较有意思的功能,举两个例子: 1、一个嵌入式产品中的某个存储设备,或是一个存储设备的某个分区,可以作为一个U盘被PC;设别,从而非常方便的完成文件交互,这个功能被广泛的应用于手机、数码相机等产品中。2、一个嵌入式设备通过USB连接到你的PC后,在你的PC端会出现一个新的网络连接,在嵌入式设备上也会有一个网卡设备,你可以配置它们的IP地址,并...

weixin_34061793的博客 1153

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

linux 找不到usb设备驱动程序,Linux驱动USB驱动程序之USB设备驱动程序1简单编写

#include static struct usb_device_id usbmouse_as_key_id_table [] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },//{USB_DEVICE(0x1234,0x567...

weixin_39930144的博客 276

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

linux usb设备驱动(1)

/* * USB Skeleton driver - 2.2 *功能:探测插入usb设备的次数,主要根据linux内核源码usb_skeleton.c修改,本事例以插入手机为例,因为系统原来已经有了u盘设备的驱动 * * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * * This program

冰雪的专栏 527

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Linux设备驱动程序笔记1—USB设备基础

在总结完USB设备操作的具体实现时候,再对《Linux设备驱动程序》的第十三章—USB驱动程序,的内容进行简单总结。这个章节详细描述了Linux的USB设备驱动程序以及USB核心之间的接口信息。 USB设备包括了设备、配置、接口、端点四部分,文章也是按照这个顺序进行讲述的,下面直接进入主题。 13.1.1端点 Usb最近本的通讯形式就是端点,USB 端点只能在一个方向承载数据, 或者从主机到

zhangxiaopeng0829的专栏 860

Linux驱动USB驱动程序之USB设备驱动程序1简单编写

1、驱动编写分析 (1)usb总线驱动程序在我们接入USB设备的时候会帮我们构造一个新的usb_device.注册到总线里面来。左边这一块已经帮我们做好了,我们要做的是右边这一块。我们要构造一个usb_driver结构体,然后注册进去。usb_driver结构体里面的id_table表示能够支持哪一些设备,里面的probe函数表示能够支持接入设备时会被调用。 (2)目的

我的专栏 1038

linux内核usb时钟配置,linux设备驱动程序之时钟管理(1)----最初的印象

PLL(Phase Locked Loop): 为锁相回路或,用来统一整合时脉讯号,使内存能正确的存取资料。PLL用于振荡器中的反馈技术。 许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步,利 用锁相环路就可以实现这个目的。比如三星s3c2410就有两个PLL,一个是MPLL,即machine PLL,主要用于CPU和一些外围器件,另一个是UPLL,即USB PLL,主要用于US...

weixin_39517560的博客 282

USB-libusb简单记录

简述对于连接到电脑的USB设备,一般都会需要安装对应的USB driver来支持。有一款开源的跨平台的USB driver,就是libusb,可以用来进行自定义的USB设备的驱动开发。libusblibusb是一个C语言编写的库,可以供上层的应用来调用,和连接在笔记本上的USB设备进行通信。易于移植,而且有对应的API的文档,可以用于Linux, OS X, Windows, Android, Op

suxiang198的博客 2854

USB学习5---android usb驱动源代码目录说明

kernel\msm-3.18\drivers\usb下目录内容   我们msm8937+android7.1平台编译out目录下usb目录下有编译到的目录如下: 我们先参考kernel\msm-3.18\drivers\usb\README对这些部分的介绍。列举了一下usb目录各个文件有什么作用。接下来就是看Kconfig、Makefile文件的内容了。任何linux下驱动模块都必...

LoongEmbedded的专栏 5212

linux usb做复合设备,usb从设备驱动usb_composite_driver复合设备

Linux/arm 3.10.104drivers/usb/gadget/composite.cstatic const struct usb_gadget_driver composite_driver_template = {.bind = composite_bind,.unbind = composite_unbind,.setup = compo...

weixin_29515393的博客 760

3分钟上手QtUsb:解锁跨平台USB开发的无限可能 [特殊字符]

还在为跨平台USB开发头疼吗?QtUsb正是你需要的解决方案!这个强大的跨平台USB模块基于libusb-1.0和libhidapi库构建,让你轻松实现与各种USB设备的通信交互。 ## 🛠️ 快速安装配置指南 无论你使用Ubuntu还是Windows,QtUsb都能快速部署。在Ubuntu上只需两条命令: ```bash sudo add-apt-repository ppa:fpous

gitblog_01151的博客 546
上一篇: S3C2440 USB 设备控制器(转)
下一篇: Linux usb设备驱动(2)---> usbmouse.c 源码分析
shareCode
博客等级 码龄15年 125粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值