USB设备驱动基础 >>Linux设备驱动程序

最常用的设备却不懂它的工作原理,岂不是最大的不尊敬,感谢USB为我们带来的便利;
今天也要继续坚定的前行的…

[0x100] 特征与概念

[0x110] USB接口特征

  • 主从树形点对点结构,主机控制器轮询连接,从机控制器回应数据;
  • 四线结构双线传输,地线、电源、接收信号线、发送信号线;
  • 非结构类型的通讯方式,屏蔽了下层的硬件设备,以串行数据流的方式来传输数据;
  • 支持热插拔功能;

[0x120] USB驱动类型

  1. USB设备驱动 :宿主机控制硬件作为USB设备与主机通信;
  2. USB器件驱动 :USB设备如何控制连接到计算机,之前只能传送数据,现在可以传送指令,由器件驱动去执行;

[0x130] USB设备基本组成

在这里插入图片描述

[0x131] 端点[struct usb_host_endpoint]

  • 定义 :单方向传输数据的管道类通道,共有四种类型;
  1. 控制端点 :配置新入设备初始化,获取设备状态信息,向设备发送命令;
  2. 中断端点 :用于接收少量高频率的数据,如 键盘、鼠标等等;
  3. 批量端点 :不可靠的异步大量数据传输,;
  4. 实时端点 :可靠恒定速率大量数据传输,用于高实时的数据传输;

[0x132] 接口[struct usb_host_interface]

  • 定义 :同逻辑类型USB端口的绑定抽象
  1. 不同的接口执行不同的单一逻辑职能,不同的带宽
  2. 每个接口可以拥有多个可选配置
  3. 接口的计数从0开始计数;
  4. 实际驱动程序中要使用的结构 struct usb_device 而不是结构结构体本身;

[0x133] 配置规则[struct usb_host_config]

  1. USB 设备捆绑多个接口,接口捆绑多个配置
  2. 一个接口只可以激活一个配置,设备可以绑定多个接口,一次有多个配置;
  3. 需要绑定到接口使用,不能单独使用

[0x140] USB request Block [struct urb]

  • 定义:异步向指定设备的指定端口发送/接收数据;
  1. 每个端口可以接收多个urb的数据收发,也可以多个端口共享一个urb;
  2. 一种端口间的异步通讯机制;

[0x200]相关数据结构

[0x210] USB 端点描述结构

#include <linux/usb.h>  
 */struct usb_host_endpoint {     /*描述了USB端点组成信息*/

       struct usb_endpoint_descriptor  /*USB标准数据结构*/
       {
        __u8  bLength;               
        __u8  bDescriptorType;

        __u8  bEndpointAddress;    /*8位 表示端点的USB地址,包括了方向和位置标识*/
        __u8  bmAttributes;        /*8位 表示端点的类型属性*/
        __le16 wMaxPacketSize;     /*16位 数据块限定大小*/
        __u8  bInterval;           /*与中断类型端点关联使用,端点的请求中断的间隔*/
        __u8  bRefresh;
        __u8  bSynchAddress;
      } __attribute__ ((packed))desc;
        struct usb_ss_ep_comp_descriptor        ss_ep_comp;
        struct list_head                        urb_list;       /*URB队列头*/
        void                                    *hcpriv;        /*DMA队列头*/
        struct ep_device                        *ep_dev;        /*设备文件系统信息*/
        /*描述接口的作用 没啥用*/
        unsigned char *extra;                                  /* Extra descriptors */
        int extralen;
        
        int enabled;                                            /*端点URB 激活提交*/
};
/*端点的类型属性*/
#define USB_ENDPOINT_XFERTYPE_MASK      0x03   /*掩码bmAttributes用来获取当前位*/
#define USB_ENDPOINT_XFER_CONTROL       0      /*控制端点*/
#define USB_ENDPOINT_XFER_ISOC          1      /*实时端点*/
#define USB_ENDPOINT_XFER_BULK          2      /*批量端点*/
#define USB_ENDPOINT_XFER_INT           3      /*中断端点*/

[0x220] USB 接口描述结构

#include <linux/usb.h> 
struct usb_interface {
        struct usb_host_interface *altsetting;              /*接口结构数组 用于记录可选的接口信息数组 无序*/
        struct usb_host_interface *cur_altsetting;          /* 当前接口激活的可选设置 */
        unsigned num_altsetting;                            /* 可选设置的数量 */
        struct usb_interface_assoc_descriptor *intf_assoc;  /*组接口配置绑定*/
        /*驱动程序包含了USB设备的主设备号,该选项表示为接口的次设备号 */
        int minor;   
        enum usb_interface_condition condition;            /* 接口绑定状态*/
        /*操作配置功能参数位*/
        unsigned sysfs_files_created:1; /* 文件系统已存在该设备文件*/
        unsigned ep_devs_created:1;     /* 终端设备已存在*/
        unsigned unregistering:1;       /* 进程未注册*/
        unsigned needs_remote_wakeup:1; /* 驱动远程唤醒*/
        unsigned needs_altsetting0:1;   /* 开启抑制altsetting 0 */
        unsigned needs_binding:1;       /* 需要延迟绑定*/
        unsigned reset_running:1;
        unsigned resetting_device:1;    /*重置后带宽重分配*/

        struct device dev;
        struct device *usb_dev;
        atomic_t pm_usage_cnt;            /*引用变量 用于确定接口是否正在使用*/         
        struct work_struct reset_ws;      /*工作队列结构*/
};

[0x230] USB 接口配置结构

#include <linux/usb.h> 
struct usb_host_config {
        /*配置参数信息*/
        struct usb_config_descriptor    desc;
        char *string; 
        /*接口描述绑定数组 无序*/         
        struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; 
       /*接口参数信息绑定数组 无序*/   
        struct usb_interface *interface[USB_MAXINTERFACES];
        struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];

        unsigned char *extra;   /* Extra descriptors */
        int extralen;
};

[0x240] USB urb结构

#include <linux/usb.h>
struct urb {
        /* private: usb core and host controller only fields in the urb */
        struct kref kref;               /* reference count of the URB */
        void *hcpriv;                   /* private data for host controller */
        atomic_t use_count;             /* concurrent submissions counter */
        atomic_t reject;                /* submissions will fail */
        int unlinked;                   /* unlink error code */

        /* 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 list_head anchor_list;   /* the URB may be anchored */
        struct usb_anchor *anchor;
        struct usb_device *dev;         /* 需要绑定Urb的USB设备结构,必须由对应的USB驱动初始化 */
        struct usb_host_endpoint *ep;   /* (internal) pointer to endpoint */
        unsigned int pipe;              /* 需要绑定Urb的USB端点属性,决定了传输数据的方向 */
        unsigned int stream_id;         /* (in) stream ID */
        int status;                     /* 实时端口状态 */
        unsigned int transfer_flags;    /* urb端口传输类型标识*/
        void *transfer_buffer;          /* 普通数据缓冲区中转 必须使用kmalloc 建立空间*/
        dma_addr_t transfer_dma;        /* DMA作为数据缓冲区中转 */
        struct scatterlist *sg;         /* (in) scatter gather buffer list */
        int num_mapped_sgs;             /* (internal) mapped sg entries */
        int num_sgs;                    /* (in) number of entries in the sg list */
        u32 transfer_buffer_length;     /* 传输数据缓冲区大小*/
        u32 actual_length;              /* (return) actual transfer length */
        unsigned char *setup_packet;    /* 设置数据包指针,优先于普通缓冲区中数据传送到目标,只允许控制类型端口使用 */
        dma_addr_t setup_dma;           /* 设置数据包指针,优先于DMA缓冲区中数据传送到目标,只允许控制类型端口使用 */
        int start_frame;                /* 仅实时类型数据包使用,起始帧数量 */
        int number_of_packets;          /* (in) number of ISO packets */
        int interval;                   /* urb发送到USB核心之前设置,处理终端间隔 仅对中断类型端口与实时端口有效 */
        int error_count;                /* USB核心设置,报告实时端口传输结束后的实时传输错误数量 */
        void *context;                   /* 终结传输无数据小包位置 */
        usb_complete_t complete;         /* 终结函数指针 void (*usb_complete_t)(struct urb *);控制权返回驱动*/
        struct usb_iso_packet_descriptor /*可以一次定义多个实时传输 */
        {
          unsigned int offset;
          unsigned int length;            /* expected length */
          unsigned int actual_length;
          int status;
        };
};

[0x250]用户空间USB设备列表

#include <linux/usb.h>
struct usb_device_id {
       /*用来匹配识别USB设备方式 不直接设置 #include linux/mod_devicetable.h USB_DEVICE_*()*/
        __u16           match_flags;    
        __u16           idVendor;      /*设备厂商编号*/
        __u16           idProduct;     /*产品型号*/
        __u16           bcdDevice_lo;  /*设备特征版本编号最小值*/
        __u16           bcdDevice_hi;  /*设备特征版本编号最大值*/

        /* Used for device class matches */
        __u8            bDeviceClass;    /*设备主类编号*/
        __u8            bDeviceSubClass; /*绑定主类的设备次类编号*/
        __u8            bDeviceProtocol; /*绑定主类的设备协议编号*/

        /* Used for interface class matches */
        __u8            bInterfaceClass;    /*接口主类编号*/
        __u8            bInterfaceSubClass; /*绑定主类的接口次类编号*/
        __u8            bInterfaceProtocol; /*绑定主类的接口协议编号*/

        /* not matched against */
        kernel_ulong_t  driver_info; /*驱动描述或者设备描述标识*/
};

[0x260]USB驱动数据结构

#include <linux/usb.h>
struct usb_driver {
        /*驱动程序标识名称,应该与模块名称一致*/
        const char *name;
        /*探测函数指针 可选配置*/
        int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);
        /*设备断开连接或者驱动程序没有加载的清理工作*/
        void (*disconnect) (struct usb_interface *intf);
        /*USBfs 配置函数,用于驱动程序与用户空间的通信*/
        int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,void *buf);
        /*USB核心设备抑制、唤醒、重置替代唤醒 处理函数回调*/
        int (*suspend) (struct usb_interface *intf, pm_message_t message);
        int (*resume) (struct usb_interface *intf);
        int (*reset_resume)(struct usb_interface *intf);
        /*重置检测URB激活状态*/
        int (*pre_reset)(struct usb_interface *intf);
        int (*post_reset)(struct usb_interface *intf);
        /*USB外设列表*/
        const struct usb_device_id *id_table;
 
        struct usb_dynids dynids;
        struct usbdrv_wrap drvwrap;
        
        unsigned int no_dynamic_id:1;
        unsigned int supports_autosuspend:1;
        unsigned int soft_unbind:1;
};

[0x270]USB设备 字符设备类结构

struct usb_class_driver {
        char *name;                                          //sysfs 中设备路径名
        char *(*devnode)(struct device *dev, umode_t *mode); //为设备创建devfs文件处理函数
        const struct file_operations *fops;                  //设备文件操作函数集
        int minor_base;                                      //次设备号起始值
};

[0x300]USB request Block

[0x310] urb生命周期

Created with Raphaël 2.2.0 USB设备驱动创建Urb 分配USB设备是否成功? USB核心 USB主控器驱动 分配Urb的设备开始向主机发送数据 通知USB驱动 yes no

[0x320] urb端点属性

[0x321] urb端口类型与传输方向标识[unsigned int pipe]

功能 操作位 描述
数据方向 【bit 7】 0 = 主机->设备 1 = 设备到主机
设备编号 【bits 08-14】 位地址指导来自于uhci-hcd控制器
端点编号 【bits 15-18】 位地址指导来自于uhci-hcd控制器
端点类型 【bits 30-31】 (00 = 实时, 01 = 中断,10 = 控制命令, 11 = 异步批量)
#include <linux/usb.h>
#define PIPE_ISOCHRONOUS                0               /*实时端口类型*/
#define PIPE_INTERRUPT                  1               /*中断端口类型*/
#define PIPE_CONTROL                    2               /*控制端口类型*/
#define PIPE_BULK                       3               /*异步批量端口*/

#define USB_DIR_OUT                     0               /* 主机向USB外设发送数据标识 */
#define USB_DIR_IN                      0x80            /* 外设向USB宿主机发送数据标识 */

#define usb_pipein(pipe)        ((pipe) & USB_DIR_IN)   /*设备向主机送数据*/
#define usb_pipeout(pipe)       (!usb_pipein(pipe))     /*主机向外设送数据*/

#define usb_pipedevice(pipe)    (((pipe) >> 8) & 0x7f)  /*获取USB设备编号 bit08-bit14*/
#define usb_pipeendpoint(pipe)  (((pipe) >> 15) & 0xf)  /*获取USB端口编号 bit15-bit18*/

#define usb_pipetype(pipe)      (((pipe) >> 30) & 3)    /*端口类型决定传输数据类型*/
#define usb_pipeisoc(pipe)      (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) /*设置 实时类型端口*/
#define usb_pipeint(pipe)       (usb_pipetype((pipe)) == PIPE_INTERRUPT)   /*设置 中断类型端口*/
#define usb_pipecontrol(pipe)   (usb_pipetype((pipe)) == PIPE_CONTROL)     /*设置 控制类型端口*/
#define usb_pipebulk(pipe)      (usb_pipetype((pipe)) == PIPE_BULK)        /*设置 批量类型端口*/

/* Create various pipes... */
#define usb_sndctrlpipe(usb_device, endpoint)  \                          /*设置 控制类型 发送端口*/
        ((PIPE_CONTROL << 30) | __create_pipe(usb_device, endpoint))
#define usb_rcvctrlpipe(usb_device, endpoint)  \                          /*设置 控制类型 接收端口*/
        ((PIPE_CONTROL << 30) | __create_pipe(usb_device, endpoint) | USB_DIR_IN)
#define usb_sndisocpipe(usb_device, endpoint)  \                          /*设置 实时类型 发送端口*/
        ((PIPE_ISOCHRONOUS << 30) | __create_pipe(usb_device, endpoint))
#define usb_rcvisocpipe(usb_device, endpoint)  \                          /*设置 实时类型 接收端口*/
        ((PIPE_ISOCHRONOUS << 30) | __create_pipe(usb_device, endpoint) | USB_DIR_IN)
#define usb_sndbulkpipe(usb_device, endpoint)  \                          /*设置 批量类型 发送端口*/
        ((PIPE_BULK << 30) | __create_pipe(usb_device, endpoint))
#define usb_rcvbulkpipe(usb_device, endpoint)  \                          /*设置 批量类型 接收端口*/
        ((PIPE_BULK << 30) | __create_pipe(usb_device, endpoint) | USB_DIR_IN)
#define usb_sndintpipe(usb_device, endpoint)   \                          /*设置 中断类型 发送端口*/
        ((PIPE_INTERRUPT << 30) | __create_pipe(usb_device, endpoint))
#define usb_rcvintpipe(usb_device, endpoint)   \                          /*设置 中断类型 接收端口*/
        ((PIPE_INTERRUPT << 30) | __create_pipe(usb_device, endpoint) | USB_DIR_IN)
/*核心函数实现*/        
static inline unsigned int __create_pipe(struct usb_device *dev,unsigned int endpoint)
{
        return (dev->devnum << 8) | (endpoint << 15);
}

[0x322] urb端口传输类型标识[unsigned int transfer_flags]

#define URB_SHORT_NOT_OK        0x0001  /* report short reads as errors */
#define URB_ISO_ASAP            0x0002  /* iso-only, urb->start_frame ignored */
#define URB_NO_TRANSFER_DMA_MAP 0x0004  /* urb->transfer_dma valid on submit */
#define URB_NO_FSBR             0x0020  /* UHCI-specific */
#define URB_ZERO_PACKET         0x0040  /* Finish bulk OUT with short packet */
#define URB_NO_INTERRUPT        0x0080  /* HINT: no non-error interruptneeded */
#define URB_FREE_BUFFER         0x0100  /* Free transfer buffer with the URB */

/* The following flags are used internally by usbcore and HCDs */
#define URB_DIR_IN              0x0200  /* Transfer from device to host */
#define URB_DIR_OUT             0
#define URB_DIR_MASK            URB_DIR_IN
功能 描述
URB_SHORT_NOT_OK 对于外设向主机传送的短数据,将被USB核心认定为错误
URB_ISO_ASAP 只有实时类型的端口 强制等待指定start_frame
URB_NO_TRANSFER_DMA_MAP USB核心向DMA缓冲区传输数据,即向transfer_dma而不是transfer_buffer
URB_NO_FSBR 主控器设置的标识位,不执行前端总线回收逻辑
URB_ZERO_PACKET 主机到外设发送数据时,使用无数据小数据包结束发送
URB_NO_INTERRUPT 目标设备不会产生中断,通常用于USB核心到DMA缓冲区传输数据
URB_FREE_BUFFER 清空Urb端口的传输缓冲区

[0x330] urb函数接口

[0x331] 创建与销毁urb结构空间

Func :创建一个urb端口结构
args1:实时包计数;
args2:用于kmalloc 内存分配的类型标识,通常GFP_kernel;
retal :成功返回struct urb 地址 失败 为NULL

/*implement drivers/usb/core/urb.c*/
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
        struct urb *urb;

        urb = kmalloc(sizeof(struct urb) +
                iso_packets * sizeof(struct usb_iso_packet_descriptor),
                mem_flags);
        if (!urb) {
                printk(KERN_ERR "alloc_urb: kmalloc failed\n");
                return NULL;
        }
        usb_init_urb(urb);
        return urb;
}
/*销毁urb结构空间*/
void usb_free_urb(struct urb *urb)
{
        if (urb)
                kref_put(&urb->kref, urb_destroy);
}
  • 不可以手动创建struct urb,会破坏USB核心的引用计数机制;
  • 如果不想创建实时类型端口,第一个参数应该为0;

[0x332] 填充不同类型的urb端口

参数 描述 需配置端口类型
struct urb *urb 初始化urb指针 详见[0x331]创建urb 通用标准
struct usb_device *dev USB外设结构 通用标准
unsigned int pipe 发送方向与端口类型,详见[0x321] urb端口类型与传输方向标识 通用标准
void *transfer_buffer 数据缓冲区,必须是kmalloc创建的位置 通用标准
int buffer_length 数据缓冲区大小 通用标准
usb_complete_t complete_fn urb 结束后的销毁规程函数指针 通用标准
void *context 置零的小数据块用于结束发送 通用标准
int interval urb允许中断间隔 终端端口与实时端口
unsigned char *setup_packet, 发送至端点的配置数据的地址 控制端口
unsigned int transfer_flags 传输类型标识 详见 [0x322] urb端口传输类型标识 实时端口
int number_of_packets 实时包数量 实时端口
struct usb_iso_packet_descriptor iso_frame_desc[0] 定义多个实时传输实例 实时端口
#include <linux/usb.h>
/*创建批量urb*/
static inline 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_fn,
                                    void *context,
                                    int interval)

/*创建批量urb*/
static inline 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_fn,
                                     void *context)
/*创建控制urb*/
static inline void usb_fill_control_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_fn,
                                        void *context)
/*创建实时urb,抱歉没有 随便其他人做的驱动找的*/
                urb->complete   = urb_complete_iso;     /* handler */
                urb->dev        = udev;
                urb->context    = video->front;
                urb->pipe       = usb_rcvisocpipe(udev, video->endpoint_addr);
                urb->interval   = 1;
                urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
                urb->number_of_packets  = PK_PER_URB;
                urb->transfer_buffer    = mem;
                urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;

                for (j = 0; j < PK_PER_URB; j++) {
                        urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
                        urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
                }

[0x333] 注册urb端口

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)

Func :将urb数据结构提交到USB核心注册端口设备,允许在中断上下文使用,控制权到USB核心;
args1:初始化urb指针 详见[0x331]创建urb;
args2:用于kmalloc 内存分配的类型标识,
根据不同使用环境 不同 不允许休眠=GFP_ATOMIC 块设备路径 GFP_NOIO其它都是GFP_KERNEL;
retal :正确返回0 错误错误码

[0x334]初始化与释放 urb 缓冲区

/*创建一个 DMA urb 缓冲区*/
static inline void *
usb_buffer_alloc(struct usb_device *dev, size_t size,gfp_t mem_flags, dma_addr_t *dma)
{
        return usb_alloc_coherent(dev, size, mem_flags, dma);
}
/*销毁一个 DMA urb 缓冲区*/
static inline void usb_buffer_free(struct usb_device *dev, size_t size,void *addr, dma_addr_t dma)
{
        return usb_free_coherent(dev, size, addr, dma);
}

[0x335] 取消urb端口

  • urb端口提交成功 ,可以查看 struct urb 结构中 status 状态值,0为成功,错误则返回错误码;
  • 驱动解除提交urb ;
/*断开当前USB设备*/
void usb_kill_urb(struct urb *urb)
{
        might_sleep();
        if (!(urb && urb->dev && urb->ep))
                return;
        atomic_inc(&urb->reject);

        usb_hcd_unlink_urb(urb, -ENOENT);
        wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);

        atomic_dec(&urb->reject);
}
/*解链接 urb端口 后由驱动程序的usb_complete_t complete_fn释放 需要设置transfer_flags|=URB_ASYNC_UNLINK */
nt usb_unlink_urb(struct urb *urb)
{       
        if (!urb)
                return -EINVAL;
        if (!urb->dev) 
                return -ENODEV;
        if (!urb->ep)
                return -EIDRM;
        return usb_hcd_unlink_urb(urb, -ECONNRESET);
}

[0x400] USB驱动程序

[0x410] 填充USB设备列表[struct usb_device_id]

/*填充厂商编号和产品编号来填充struct usb_device_id*/
#define USB_DEVICE(vend, prod) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
        .idVendor = (vend), \
        .idProduct = (prod)
/*填充厂商编号、产品编号、接口协议*/
#define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
                       USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
        .idVendor = (vend), \
        .idProduct = (prod), \
        .bInterfaceProtocol = (pr)
/*填充设备主类、设备次类、设备协议*/
#define USB_DEVICE_INFO(cl, sc, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, \
        .bDeviceClass = (cl), \
        .bDeviceSubClass = (sc), \
        .bDeviceProtocol = (pr)
/*填充接口主类、接口次类、接口协议*/
#define USB_INTERFACE_INFO(cl, sc, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
        .bInterfaceClass = (cl), \
        .bInterfaceSubClass = (sc), \
        .bInterfaceProtocol = (pr)
  1. USB核心单线程发现设备–>检查获取设备信息–>添加到usb_device_id–>通知USB集线器
  2. 传递设备信息–>启动探测函数
  3. 匹配驱动程序–>初始化USB设备结构

[0x420] 注册与注销驱动程序到核心

  • 需要在模块初始化的过程中完成,并 需要填充struct usb_driver
  • 本函数会填充new_driver->drvwrap, 并在USB核心分配主设备号,来标识驱动程序;
/*注册上层宏接口*/
#define usb_register(driver) usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
/*将驱动程序注册到USB子系统中*/
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,const char *mod_name)
{
        int retval = 0;

        if (usb_disabled())
                return -ENODEV;
        /*设置为接口驱动=0 设备驱动 = 非0*/
        new_driver->drvwrap.for_devices = 0;
        
        new_driver->drvwrap.driver.name = (char *) new_driver->name;
        new_driver->drvwrap.driver.bus = &usb_bus_type;
        new_driver->drvwrap.driver.probe = usb_probe_interface;
        new_driver->drvwrap.driver.remove = usb_unbind_interface;
        new_driver->drvwrap.driver.owner = owner;
        new_driver->drvwrap.driver.mod_name = mod_name;
        spin_lock_init(&new_driver->dynids.lock);
        INIT_LIST_HEAD(&new_driver->dynids.list);
         /*核心函数 struct device_driver*/
        retval = driver_register(&new_driver->drvwrap.driver);
        if (retval)
                goto out;

        usbfs_update_special();

        retval = usb_create_newid_files(new_driver);
        if (retval)
                goto out_newid;

        pr_info("%s: registered new interface driver %s\n",
                        usbcore_name, new_driver->name);

out:
        return retval;

out_newid:
        driver_unregister(&new_driver->drvwrap.driver);

        printk(KERN_ERR "%s: error %d registering interface "
                        "       driver %s\n",
                        usbcore_name, retval, new_driver->name);
        goto out;
}
/*注销USB驱动程序核心函数*/
void usb_deregister(struct usb_driver *driver)
{
        pr_info("%s: deregistering interface driver %s\n",
                        usbcore_name, driver->name);

        usb_remove_newid_files(driver);
        /*核心函数 struct device_driver*/
        driver_unregister(&driver->drvwrap.driver);
        usb_free_dynids(driver);

        usbfs_update_special();
}

[0x430] 新插入设备获取读写端点

  1. USB核心 调用USB集线器线程 启动探测函数;
  2. 检查新插入的设备信息是否匹配当前的驱动程序;
  3. 确定新插入设备是否为 输入输出端点,并保存端点地址到USB设备结构中;
struct usb_interface_descriptor endpoint;
/*获取当前接口中的多个端口配置*/
 struct usb_host_interface * iface_desc = interface->cur_altsetting;
 /*获取最大端点数量遍历端点*/
    for(i = 0;i < iface_desc->desc.bNumEndpoints;++i)
    {
         /*获取接口中端口描述*/
          endpoint = &iface_desc->endpoint[i].desc;
          /*当输入端口地址未指定时,查找批量IN类型的端点*/
       if(!dev->bulk_in_endpointAddr && 
              (endpoint->bEndpointAddress & USB_DIR_IN)&&
              ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE-MASK)==USB_ENDPOINT_XFER_BULK))
            {
              /*发现一个的批量IN类型的端点*/
                  /*设置端点缓冲区为最大数据包大小*/
                  buffer_size = endpoint->WMaxPacketsize;
                  dev->bulk_in_size = buffer_size;
                  /*指定USB设备使用该端点*/
                  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;
             }
       /*当输出端口地址未指定时,查找批量OUT类型的端点*/
       if(!dev->bulk_in_endpointAddr && 
              !(endpoint->bEndpointAddress & USB_DIR_IN)&&
              ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE-MASK)==USB_ENDPOINT_XFER_BULK))
           { /*发现一个批量OUT类型的端点并指定设备使用该端点*/
            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;

[0x440] USB接口 获取与保存 USB端点数据

/*将之前获取的读写端口 USB设备结构体中数据保存到USB接口结构中*/
static inline void usb_set_intfdata(struct usb_interface *intf, void *data)
{
        dev_set_drvdata(&intf->dev, data);
}
/*获取USB接口中的数据*/
static inline void *usb_get_intfdata(struct usb_interface *intf)
{
        return dev_get_drvdata(&intf->dev);
}

[0x450] USB接口设备注册与注销

  • 注册成字符设备与注销
  • 有了 struct usb_interface 其中的读写端口
  • 还需要 struct usb_class_driver USB接口字符驱动转换结构
/*注册函数*/
int usb_register_dev(struct usb_interface *intf,struct usb_class_driver *class_driver)
{
        int retval;
        int minor_base = class_driver->minor_base;
        int minor;
        char name[20];
        char *temp;

#ifdef CONFIG_USB_DYNAMIC_MINORS
        minor_base = 0;
#endif

        if (class_driver->fops == NULL)
                return -EINVAL;
        if (intf->minor >= 0)
                return -EADDRINUSE;

        retval = init_usb_class();
        if (retval)
                return retval;

        dev_dbg(&intf->dev, "looking for a minor, starting at %d", minor_base);

        down_write(&minor_rwsem);
        for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
                if (usb_minors[minor])
                        continue;

                usb_minors[minor] = class_driver->fops;
                intf->minor = minor;
                break;
        }
        up_write(&minor_rwsem);
        if (intf->minor < 0)
                return -EXFULL;

        /* create a usb class device for this usb interface */
        snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
        temp = strrchr(name, '/');
        if (temp && (temp[1] != '\0'))
                ++temp;
        else
                temp = name;
        intf->usb_dev = device_create(usb_class->class, &intf->dev,
                                      MKDEV(USB_MAJOR, minor), class_driver,
                                      "%s", temp);
        if (IS_ERR(intf->usb_dev)) {
                down_write(&minor_rwsem);
                usb_minors[minor] = NULL;
                intf->minor = -1;
                up_write(&minor_rwsem);
                retval = PTR_ERR(intf->usb_dev);
        }
        return retval;
}
/*如果使用上述函数注册USB接口设备 就要用这个注销才能执行断开操作*/
void usb_deregister_dev(struct usb_interface *intf,
                        struct usb_class_driver *class_driver)
{
        if (intf->minor == -1)
                return;

        dbg ("removing %d minor", intf->minor);

        down_write(&minor_rwsem);
        usb_minors[intf->minor] = NULL;
        up_write(&minor_rwsem);

        device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
        intf->usb_dev = NULL;
        intf->minor = -1;
        destroy_usb_class();
}
  • 断开USB设备清理
static void skel_disconnect(struct usb_interface *interface)
{
        struct usb_skel *dev;
        int minor = interface->minor;
       /*获取设备信息*/
        dev = usb_get_intfdata(interface);
        /*清理接口信息*/
        usb_set_intfdata(interface, NULL);
       
        /* 返还次设备编号 */
        usb_deregister_dev(interface, &skel_class);

        /* prevent more I/O from starting */
        mutex_lock(&dev->io_mutex);
        dev->interface = NULL;
        mutex_unlock(&dev->io_mutex);
        /*删除urb请求端口*/
        usb_kill_anchored_urbs(&dev->submitted);

        /* 降低引用计数 */
        kref_put(&dev->kref, skel_delete);

        dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}

[0x460] 分配urb端口

  1. [0x331] 创建urb结构空间
  2. [0x334] 初始化 urb 缓冲区
  3. [0x332] 填充不同类型的urb端口
  4. [0x333] 注册urb端口
  5. [0x334] 释放 urb 缓冲区

[0x500] 非urb驱动程序

  1. 当只需要简单的发送数据时,可以使用以下函数接口;
  2. 不能使用在中断上下文,该函数有可能休眠;
  • 创建大量数据传输端口
int 
usb_bulk_msg(struct usb_device *usb_dev, 
             unsigned int pipe,void *data, int len, int *actual_length, int timeout)
{
        struct urb *urb;
        struct usb_host_endpoint *ep;

        ep = usb_pipe_endpoint(usb_dev, pipe);
        if (!ep || len < 0)
                return -EINVAL;

        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!urb)
                return -ENOMEM;

        if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
                        USB_ENDPOINT_XFER_INT) {
                pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
                usb_fill_int_urb(urb, usb_dev, pipe, data, len,
                                usb_api_blocking_completion, NULL,
                                ep->desc.bInterval);
        } else
                usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
                                usb_api_blocking_completion, NULL);

        return usb_start_wait_urb(urb, timeout, actual_length);
}

Func : 创建一个批量的urb指向发送目标USB设备的 usb_device;
args1 : 目标USB设备属性指针;
args2 : 设置批量发送特定端点标识
args3 : 数据缓冲区指针
args4 : 缓冲区大小
args5 : 传输后最终位置的数据指针
args6 : 等待超时时间
retval : 回值为0 如果调用成功 错误 为错误码

  • 创建控制信息传输端口
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
                    __u8 requesttype, __u16 value, __u16 index, void *data,
                    __u16 size, int timeout)
{
        struct usb_ctrlrequest *dr;
        int ret;

        dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
        if (!dr)
                return -ENOMEM;

        dr->bRequestType = requesttype;
        dr->bRequest = request;
        dr->wValue = cpu_to_le16(value);
        dr->wIndex = cpu_to_le16(index);
        dr->wLength = cpu_to_le16(size);

        /* dbg("usb_control_msg"); */

        ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);

        kfree(dr);

        return ret;
}

Func : 创建一个控制类型的urb指向发送目标USB设备的 usb_device;
args1 : 目标USB设备属性指针;
args2 : 设置控制信息发送特定端点标识;
args3 : 控制信息USB请求值;
args4 : 控制信息USB请求类型值;
args5 : 控制信息USB请求消息值;
args6 : 控制信息USB请求索引值;
args7 : 传输数据缓冲区;
args8 : 传输数据缓冲区大小;
args9 : 等待计时器;
retval : 如果调用成功 返回实际 传输字节数 错误 = 负数错误码;

猜你喜欢

转载自blog.csdn.net/god1992/article/details/85751092