【驱动】USB驱动·入门

本文涉及的产品
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 1个月
简介:

Preface

   USB是目前最流行的系统总线之一。随着计算机周围硬件的不断扩展,各种设备使用不同的总线接口,导致计算机外部总线种类繁多,管理困难。USB总线正是因此而诞生的。

USB总线提供了所有外部设备的统一连接方式,并且支持热插拔,方便了厂商开发设备和用户使用设备。



USB遵循原则

   USB的设计目标是对现有的PC机体系进行扩充,但是目前不仅是PC机,许多的嵌入式系统都开始支持USB总线和接口标准。USB设计主要遵循下面几个原则

    • 易于扩充外部设备:USB支持一个接口最多127个设备。

    • 灵活的传输协议: 支持同步和异步数据传输。

    • 设备兼容性好: 可以兼容不同类型的设备。

    • 接口标准统一:不同的设备之间使用相同的设备接口。



USB体系概述

   USB接口标准支持主机和外部设备之间进行数据传输。

   在USB体系结构中,主机预定了各种类型外部设备使用的总线带宽。当外部设备和主机在运行时,USB总线允许添加、设置、使用和拆除外设。

   在USB体系结构中,一个USB系统可以分成USB互联、USB设备和USB主机三个部分。

   USB互联是USB设备和USB主机之间进行连接通信的操作,主要包括:

    • 总线拓扑结构:USB主机和USB设备之间的连接方式。

    • 数据流模式:描述USB通信系统中数据如何从产生方传递到使用方。

    • USB调度:USB总线是一个共享连接,对可以使用的连接进行了调试以支持同步数据传输,并且避免优先级判定的开销。

   USB的物理连接是一个有层次的星形结构。

   在一个节点上连接多个设备需要使用 USB集线器(USB HUB)。

   USB体系结构规定,在一个 USB系统中,只有唯一的一个主机。USB和主机系统的接口称做主机控制器,主机控制器由主机控制器芯片、固件程序和软件共同实现的。

   USB设备包括USB集线器和功能器件。其中USB集线器的作用是扩展总线端点,向总线提供更多的连接点;功能器件是用户使用的外部设备,如键盘,鼠标等。

   USB设备需要支持 USB总线协议,对主机的操作提供反馈并且提供设备性能的描述信息。



USB体系工作流程

   USB总线采用轮询方式控制,主机控制设置初始化所有的数据传输。

   USB总线每次执行传输动作最多可以传输三个数据包。每次开始传输时,主机控制器发送一个描述符描述传输动作的种类和方向,这个数据包称作标志数据包(Token Packet)。USB设备收到主机发送的标志数据包后解析出数据自己的数据。

   USB数据传输的方向只有两种:主机到设备或者设备到主机。

   在一个数据传输开始时,由标志包标示数据的传输方向,然后发送端开始发送包含信息的数据。接收端发送一个握手的数据包表明数据是否传送成功。

   在主机和设备之间的USB数据传输可以看做一个通道。USB数据传输有流和消息两种通道。消息是有格式的数据,而流是没有数据格式的。

   USB有一个缺省的控制消息通道,在设备启动的时候被创建,因此设备的设置查询和输入控制信息都可以使用缺省消息控制通道完成。



USB驱动程序框架

   Linux内核提供了完整的USB驱动程序框架。

   USB总线采用树形结构,在一条总线上只能有唯一的主机设备。

   Linux内核从主机和设备两个角度观察USB总线结构。



Linux内核USB驱动框架



   左侧是主机驱动结构。

   主机驱动的最底层是 USB主机控制器,提供了 OHCI/EHCI/UHCI这3种类型的总线控制功能。

   在USB控制器的上一层是主机控制器的驱动,分别对应OHCI/EHCI/UHCI这3种类型的总线接口。

   USB核心部分连接了 USB控制器驱动和设备驱动,是两者之间的转换接口。

   USB设备驱动层提供了各种设备的驱动程序。

   所有类型的 USB设备都是用相同的电气接口,使用的传输协议也基本相同。

   向用户提供某种特定类型的 USB设备时,需要处理 USB总线协议。内核完成所有的 USB总线协议处理,并且向用户提供编程接口。


   右侧是设备驱动结构。

   与USB主机类似,USB设备提供了相同的层次结构与之对应。但是在 USB设备一侧使用名为 Gadget API的结构作为核心。

   Gadget API是 Linux内核实现的对应 USB设备的核心结构。Gadget API屏蔽了 USB设备控制器的细节,控制具体的 USB设备实现。



设备

   每个 USB设备提供了不同级别的配置信息。

   一个 USB设备可以包含一个或多个配置,不同的配置使设备表现出不同的特点。其中,设备的配置是通过接口组成的。

   Linux内核定义了 USB设备描述结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//源定义在Usb_ch9.h
/* USB_DT_DEVICE: Device descriptor */
struct  usb_device_descriptor {
     __u8  bLength;   //设备描述符长度
     __u8  bDescriptorType;   //设备类型
     __le16 bcdUSB;   // USB版本号(使用 BCD编码)
     __u8  bDeviceClass;  //  USB设备类型
     __u8  bDeviceSubClass;   //  USB设备子类型
     __u8  bDeviceProtocol;   //  USB设备协议号
     __u8  bMaxPacketSize0;   //传输数据的最大包长
     __le16 idVendor;     //厂商编号
     __le16 idProduct;    //产品编号
     __le16 bcdDevice;    //设备出厂号
     __u8  iManufacturer;     //厂商字符串索引
     __u8  iProduct;  //产品字符串索引
     __u8  iSerialNumber;     //产品序列号索引
     __u8  bNumConfigurations;    //最大的配置数量
} __attribute__ ((packed));


   从 usb_device_descrptor结构定义看出,一个设备描述定义了与 USB设备有关的所有信息。



接口

   在 USB体系中,接口是由多个端点组成的。

   一个接口代表一个基本的功能,是 USB设备驱动程序控制的对象。

   一个 USB设备最少有一个接口,功能复杂的 USB设备可以有多个接口。接口描述定义如下:Usb

1
2
3
4
5
6
7
8
9
10
11
12
13
//源定义在 Usb_ch9.h
/* USB_DT_INTERFACE: Interface descriptor */
struct  usb_interface_descriptor {
     __u8  bLength;   //描述符长度
     __u8  bDescriptorType;   //描述符类型
     __u8  bInterfaceNumber;  //接口编号
     __u8  bAlternateSetting;     //备用接口编号
     __u8  bNumEndpoints;     //端点数量
     __u8  bInterfaceClass;   //接口类型
     __u8  bInterfaceSubClass;    //接口子类型
     __u8  bInterfaceProtocol;    //接口使用的协议
     __u8  iInterface;    //接口索引字符串数值
} __attribute__ ((packed));



端点

   端点是 USB总线通信的基本形式,每个 USB设备接口可以认为是端点的集合。

   主机只能通过端点与设备通信。

   USB体系结构规定每个端点都有一个唯一的地址,由设备地址和端点号决定端点地址。

   端点还包括了与主机通信用到的属性,如传输方式、总线访问频率、带宽和端点号等。

   端点的通信是单向的,通过端点传输的数据只能是从主机到设备或者从设备到主机。

   端点的定义描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct  usb_endpoint_descriptor {
     __u8  bLength;   //描述符长度
     __u8  bDescriptorType;   //描述符类型
     __u8  bEndpointAddress;  //端点地址
     __u8  bmAttributes;  //端点属性
     __le16 wMaxPacketSize;   //端点接收的最大数据包长度
     __u8  bInterval;
     /* NOTE:  these two are _only_ in audio endpoints. */
     /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
     __u8  bRefresh;
     __u8  bSynchAddress;
} __attribute__ ((packed));



配置

   配置是一个接口的集合。

   Linux内核配置的定义如下:

1
2
3
4
5
6
7
8
9
10
struct  usb_config_descriptor {
     __u8  bLength;   //描述符长度
     __u8  bDescriptorType;   //描述符类型
     __le16 wTotalLength;     //配置返回数据长度
     __u8  bNumInterfaces;    //最大接口数
     __u8  bConfigurationValue;   //配置参数值
     __u8  iConfiguration;    //配置描述字符串索引
     __u8  bmAttributes;  //供电模式
     __u8  bMaxPower;     //接口的最大电流
} __attribute__ ((packed));



主机驱动结构

   USB主机控制器有三种类型:

    • OHCI,英文全称是Open Host Controller Interface。OHCI是用于SiS和Ali芯片组的USB控制器。

    • UHCI,英文全称是Universal Host Controller Interface。UHCI用于Intel和AMD芯片组的USB控制器。UHCI类型的控制器比OHCI控制器硬件结构要简单,但是需要额外的驱动支持,因此从理论上说速度要慢。

    • EHCI,USB2.0规范提出的一种控制器标准,可以兼容UHCI和OHCI。



USB主机控制器驱动

   Linux内核使用 usb_hcd结构描述 USB主机控制器驱动。

   usb_hcd结构描述了 USB主机控制器的硬件信息、状态和操作函数。定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//源定义在Hcd.h
struct  usb_hcd {     /* usb_bus.hcpriv points to this */
     /*
      * housekeeping //控制器基本信息
      */
     struct  usb_bus      self;        /* hcd is-a bus */
     const  char       *product_desc;   /* product/vendor string */  //厂商名称字符串
     char             irq_descr[24];   /* driver + bus # */     //驱动和总线类型
     struct  timer_list   rh_timer;    /* drives root-hub polling */    //根 hub轮询时间间隔
     struct  urb      *status_urb;     /* the current status urb */     //当前 urb状态
     /*
      * hardware info/state  //硬件信息和状态
      */
     const  struct  hc_driver  *driver;     /* hw-specific hooks */  //控制器驱动使用的回调函数
     /* Flags that need to be manipulated atomically */
     unsigned  long        flags;
#define HCD_FLAG_HW_ACCESSIBLE  0x00000001
#define HCD_FLAG_SAW_IRQ    0x00000002
     unsigned        rh_registered:1; /* is root hub registered? */    //是否注册根 hub
     /* The next flag is a stopgap, to be removed when all the HCDs
      * support the new root-hub polling mechanism. */
     unsigned        uses_new_polling:1;  //是否允许轮询根 hub状态
     unsigned        poll_rh:1;   /* poll for rh status? */
     unsigned        poll_pending:1;  /* status has changed? */    //状态是否改变
     int          irq;         /* irq allocated */  //控制器的中断请求号
     void  __iomem        *regs;       /* device memory/io */   //控制器使用的内存和 I/O
     u64         rsrc_start;  /* memory/io resource start */   //控制器使用的内存和 I/O起始地址
     u64         rsrc_len;    /* memory/io resource length */  //控制器使用的内存和 I/O资源长度
     unsigned        power_budget;    /* in mA, 0 = no limit */
#define HCD_BUFFER_POOLS    4
     struct  dma_pool     *pool [HCD_BUFFER_POOLS];
     int          state;
#   define  __ACTIVE        0x01
#   define  __SUSPEND       0x04
#   define  __TRANSIENT     0x80
#   define  HC_STATE_HALT       0
#   define  HC_STATE_RUNNING    (__ACTIVE)
#   define  HC_STATE_QUIESCING  (__SUSPEND|__TRANSIENT|__ACTIVE)
#   define  HC_STATE_RESUMING   (__SUSPEND|__TRANSIENT)
#   define  HC_STATE_SUSPENDED  (__SUSPEND)
#define HC_IS_RUNNING(state) ((state) & __ACTIVE)
#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
     /* more shared queuing code would be good; it should support
      * smarter scheduling, handle transaction translators, etc;
      * input size of periodic table to an interrupt scheduler.
      * (ohci 32, uhci 1024, ehci 256/512/1024).
      */
     /* The HC driver's private data is stored at the end of
      * this structure.
      */
     unsigned  long  hcd_priv[0]
             __attribute__ ((aligned ( sizeof (unsigned  long ))));
};



OHCI控制器驱动

   usb_hcd结构可以理解为一个通用的 USB控制器描述结构,OHCI主机控制器是 usb_hcd结构的具体实现。

   内核使用 ohci_hcd结构描述 OHCI主机控制器,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
struct  ohci_hcd {
     spinlock_t      lock;
     /*
      * I/O memory used to communicate with the HC (dma-consistent)  //用于 HC通信的 I/O内存地址
      */
     struct  ohci_regs __iomem *regs;
     /*
      * main memory used to communicate with the HC (dma-consistent)。    //用于 HC 通告的主内存地址
      * hcd adds to schedule for a live hc any time, but removals finish
      * only at the start of the next frame.
      */
     struct  ohci_hcca    *hcca;
     dma_addr_t      hcca_dma;
     struct  ed       *ed_rm_list;         /* to be removed */  //将被移除列表
     struct  ed       *ed_bulktail;        /* last in bulk list */  //列表最后一项
     struct  ed       *ed_controltail;     /* last in ctrl list */  //控制列表最后一项
     struct  ed       *periodic [NUM_INTS];    /* shadow int_table */
     /*
      * OTG controllers and transceivers need software interaction;
      * other external transceivers should be software-transparent
      */
     struct  otg_transceiver  *transceiver;
     /*
      * memory management for queue data structures  //内存管理队列使用的数据结构
      */
     struct  dma_pool     *td_cache;
     struct  dma_pool     *ed_cache;
     struct  td       *td_hash [TD_HASH_SIZE];
     struct  list_head    pending;
     /*
      * driver state
      */
     int          num_ports;
     int          load [NUM_INTS];
     u32             hc_control;  /* copy of hc control reg */     // HC控制寄存器复制
     unsigned  long        next_statechange;    /* suspend/resume */     //挂起 恢复
     u32         fminterval;      /* saved register */     //保存的寄存器
     struct  notifier_block   reboot_notifier;
     unsigned  long        flags;       /* for HC bugs */
#define OHCI_QUIRK_AMD756   0x01            /* erratum #4 */
#define OHCI_QUIRK_SUPERIO  0x02            /* natsemi */
#define OHCI_QUIRK_INITRESET    0x04            /* SiS, OPTi, ... */
#define OHCI_BIG_ENDIAN     0x08            /* big endian HC */
#define OHCI_QUIRK_ZFMICRO  0x10            /* Compaq ZFMicro chipset*/
     // there are also chip quirks/bugs in init logic    //芯片的初始化逻辑里也同样会有怪异的 Bug
};

   OHCI主机控制器是嵌入式系统最常用的一种 USB主机控制器。



设备驱动结构

   USB协议规定了许多种USB设备类型。Linux内核实现了音频设备、通信设备、人机接口、存储设备、电源设备、打印设备等几种USB设备类。



基本概念

   Linux内核实现的 USB设备驱动都是针对通用的设备类型设计的。

   只要 USB存储设备是按照标准的 USB存储设备规范实现的,就可以直接被内核 USB存储设备驱动。如果一个 USB设备是非标准的,则需要编写对应设备的驱动程序。



设备驱动结构

   内核使用 usb_driver结构体描述 USB设备驱动,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct  usb_driver {
     const  char  *name;
     int  (*probe) ( struct  usb_interface *intf,
               const  struct  usb_device_id *id);   //探测函数
     void  (*disconnect) ( struct  usb_interface *intf);     //断开连接函数
     int  (*ioctl) ( struct  usb_interface *intf, unsigned  int  code,
             void  *buf);  // I/O控制函数
     int  (*suspend) ( struct  usb_interface *intf, pm_message_t message);   //挂起函数
     int  (*resume) ( struct  usb_interface *intf);  //恢复函数
     void  (*pre_reset) ( struct  usb_interface *intf);
     void  (*post_reset) ( struct  usb_interface *intf);
     const  struct  usb_device_id *id_table;
     struct  usb_dynids dynids;
     struct  device_driver driver;
     unsigned  int  no_dynamic_id:1;
};
    • 实现一个 USB设备的驱动主要是实现 probe()和 disconnect()函数接口。

    • probe()函数在插入 USB设备的时候被调用,disconnect()函数在拔出 USB设备的时候被调用。



USB请求块

   USB请求块(USB request block,urb)的功能类似于网络设备中的 sk_buff,用于描述 USB设备与主机通信的基本数据结构。

   urb结构在内核中定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//源定义在 Usb.h
struct  urb
{
     /* private: usb core and host controller only fields in the urb */
     struct  kref kref;        /* reference count of the URB */     // urb引用计数
     spinlock_t lock;         /* lock for the URB */   // urb锁
     void  *hcpriv;            /* private data for host controller */   //主机控制器私有数据
     int  bandwidth;           /* bandwidth for INT/ISO request */  //请求带宽
     atomic_t use_count;      /* concurrent submissions counter */     //并发传输计数
     u8 reject;           /* submissions will fail */  //传输即将失败标志
     /* public: documented fields in the urb that can be used by drivers */   //公有数据,可以被驱动使用
     struct  list_head urb_list;   /* list head for use by the urb's   //链表头
                      * current owner */
     struct  usb_device *dev;      /* (in) pointer to associated device */  //关联的 USB设备
     unsigned  int  pipe;       /* (in) pipe information */  //管道信息
     int  status;          /* (return) non-ISO status */    //当前信息
     unsigned  int  transfer_flags;     /* (in) URB_SHORT_NOT_OK | ...*/
     void  *transfer_buffer;       /* (in) associated data buffer */    //数据缓冲区
     dma_addr_t transfer_dma;     /* (in) dma addr for transfer_buffer */  //DMA使用的缓冲区
     int  transfer_buffer_length;  /* (in) data buffer length */    //缓冲区大小
     int  actual_length;       /* (return) actual transfer length */    //实际接收或发送数据的长度
     unsigned  char  *setup_packet;     /* (in) setup packet (control only) */
     dma_addr_t setup_dma;        /* (in) dma addr for setup_packet */     //设置数据包缓冲区
     int  start_frame;         /* (modify) start frame (ISO) */     //等时传输中返回初始帧
     int  number_of_packets;       /* (in) number of ISO packets */     //等时传输中缓冲区数据
     int  interval;            /* (modify) transfer interval   //轮询的时间间隔
                      * (INT/ISO) */
     int  error_count;         /* (return) number of ISO errors */  //出错次数
     void  *context;           /* (in) context for completion */
     usb_complete_t complete;     /* (in) completion routine */
     struct  usb_iso_packet_descriptor iso_frame_desc[0];
                     /* (in) ISO ONLY */
};

   内核提供了一组函数 urb类型的结构变量。urb的使用流程如下:

    1. 创建 urb。在使用之前,USB设备驱动需要调用 usb_alloc_urb()函数创建一个 urb;内核还提供释放 urb的函数,在不使用 urb的时候(退出驱动种马或者挂起驱动),需要使用 usb_free_urb()函数释放 urb。

    2. 初始化 urb。设置 USB设备的端点。使用内核提供的 usb_init_urb()函数设置 urb初始结构。

    3. 提交 urb到 USB核心。在分配并设置 urb完毕后,使用 urb_submit_urb()函数把新的 urb提交到 USB核心。



USB驱动程序框架

   Linux内核代码driver/usb/usb-skeleton.c文件是一个标准的USB设备驱动程序。

   编写一个USB设备的驱动可以参考usb-skeleton.c文件,实际上,可以直接修改该文件驱动新的USB设备。



基本数据结构

   usb-skel设备使用自定义结构 usb_skel记录设备驱动用到的所有描述符,该结构定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Structure to hold all of our device specific stuff */
struct  usb_skel {
     struct  usb_device * udev;            /* the usb device for this device */
         // USB设备描述符
     struct  usb_interface *  interface;       /* the interface for this device */
         // USB接口描述符
     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_skel结构存放在了 urb结构的 context指针里。通过 urb,设备的所有操作函数都可以访问到 usb_skel结构。

   其中,limit_sem成员是一个信号量,当多个 usb-skel类型的设备存在于系统中的时候,需要控制设备之间的数据同步。



驱动程序初始化和注销

   与其他所有的 Linux设备驱动程序一样,usb-skel驱动使用 module_init()宏设置初始化函数,使用 module_exit()宏设置注销函数。

   usb-skel驱动的初始化函数是 usb_skel_init()函数,定义如下:

1
2
3
4
5
6
7
8
9
static  int  __init usb_skel_init( void )
{
     int  result;
     /* register this driver with the USB subsystem */
     result = usb_register(&skel_driver);     //注册 USB设备驱动
     if  (result)
         err( "usb_register failed. Error number %d" , result);
     return  result;
}

   usb_skel_init()函数调用内核提供的 usb_register()函数注册了一个 usb_driver类型的结构变量,该变量定义如下:

1
2
3
4
5
6
static  struct  usb_driver skel_driver = {
     .name =      "skeleton" // USB设备名称
     .probe =    skel_probe,  // USB设备初始化函数
     .disconnect =   skel_disconnect,     // USB设备注销函数
     .id_table = skel_table,  // USB设备 ID映射表
};

   skel_driver结构变量中,定义了 usb-skel设备的名、设备初始化函数、设备注销函数和 USB ID映射表。

   其中 usb-skel设备的 USB ID映射表定义如下:

1
2
3
4
5
/* 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 */
};

   skel_table中只有一项,定义了一个默认的 usb-skel设备的 ID,其中,USB_SKEL_VENDOR_ID是 USB设备的厂商 ID,USB_SKEL_PRODUCT_ID是 USB设备 ID。



设备初始化

   从 skel_driver结构可以知道 usb-skel设备的初始化函数是 skel_probe()函数。

设备初始化主要是探测设备类型,分配 USB设备用到的 urb资源,注册 USB设备操作函数等。

skel_class结构变量记录了 usb-skel设备信息,定义如下:

1
2
3
4
5
6
7
8
9
/*
  * usb class driver info in order to get a minor number from the usb core,
  * and to have the device registered with the driver core
  */
static  struct  usb_class_driver skel_class = {
     .name =      "skel%d" ,    //设备名称
     .fops =     &skel_fops,  //设备操作函数
     .minor_base =   USB_SKEL_MINOR_BASE,
};

   name变量使用 %d通配符表示一个整形变量,当一个 usb-skel类型的设备连接到 USB总线后会按照子设备编号自动设置设备名称。

   fops是设备操作函数结构变量,定义如下:

1
2
3
4
5
6
7
static  struct  file_operations skel_fops = {
     .owner =    THIS_MODULE,
     .read =     skel_read,   //读操作
     .write =    skel_write,  //写操作
     .open =     skel_open,   //打开操作
     .release =  skel_release,    //关闭操作
};

   skel_ops定义了 usb-skel设备的操作函数。当在 usb-skel设备上发生相关事件时,USB文件系统会调用对应的函数处理。



设备注销

   skel_disconnect()函数在注销设备的时候被调用,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static  void  skel_disconnect( struct  usb_interface *interface)
{
     struct  usb_skel *dev;
     int  minor = interface->minor;
     /* prevent skel_open() from racing skel_disconnect() */
     lock_kernel();   //在操作之前加锁
     dev = usb_get_intfdata(interface);   //获得 USB设备接口描述
     usb_set_intfdata(interface, NULL);   //设置 USB设备接口描述无效
     /* give back our minor */
     usb_deregister_dev(interface, &skel_class);  //注销 USB设备操作供述
     unlock_kernel();     //操作完毕解锁
     /* decrement our usage count */
     kref_put(&dev->kref, skel_delete);    //减小引用计数
     info( "USB Skeleton #%d now disconnected" , minor);
}
static  struct  usb_driver skel_driver = {
     .name =      "skeleton" // USB设备名称
     .probe =    skel_probe,  // USB设备初始化函数
     .disconnect =   skel_disconnect,     // USB设备注销函数
     .id_table = skel_table,  // USB设备 ID映射表
};

   skel_disconnect()函数释放 usb-skel设备用到的资源。

   首先获取 USB设备接口描述,之后设置为无效;然后调用 usb_deregister_dev()函数注销 USB设备的操作描述符,注销操作本身需要加锁;注销设备描述符后,更新内核对 usb-skel设备的引用计数。



本文转自infohacker 51CTO博客,原文链接:http://blog.51cto.com/liucw/1226257


相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
相关文章
|
4月前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
本文介绍了如何在基于Amlogic T972的Android 9.0系统上使用Platform平台驱动框架和设备树(DTS),实现设备与驱动的分离,并通过静态枚举在设备树中描述设备,自动触发驱动程序的加载和设备创建。
89 0
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
|
7月前
驱动常用技巧
。。。未完,待续。。。
52 0
|
Linux
一文搞懂 USB 设备端驱动框架
hello 大家好,今天带领大家学习一下USB 设备端驱动 内核版本:4.4.94
1208 0
|
敏捷开发 消息中间件 缓存
什么是领域驱动
领域驱动的概念
217 0
|
Linux 调度
嵌入式实践教程--设备树驱动下的中断开发
嵌入式实践教程--设备树驱动下的中断开发