从零开始学USB(二十六、usb鼠标驱动驱动实例分析[1]简介)

这个驱动是在上一节的最简单的usb驱动基础上增加了输入子系统和usb包的获取和请求。

首先先给出一个稍微简化了一下的usb的鼠标驱动,这里只做了鼠标的三个按键。为了方便验证,这里用鼠标的左键,右键和中间分别作为标准按键的l   s    enter 键。

#include 
#include 
#include 
#include 
#include 
#include 


struct usb_mouse_as_key {
    struct input_dev *dev;
    struct usb_device *usbdev;  
    struct urb *key_urb;
    int maxp;
    signed char *data;
    dma_addr_t data_dma;

};


static void usb_key_func(struct urb *urb)
{
    struct usb_mouse_as_key *uk = urb->context;
    static signed char key_val;
    int status;

    //保证鼠标左键的键值有变化再上报
    if( (key_val & 0x01) != (uk->data[1] & 0x01) ) { 
        input_report_key(uk->dev, KEY_L , uk->data[1] & 0x01 );
        input_sync(uk->dev);
    }   
    

    if( (key_val & 0x02) != (uk->data[1] & 0x02) ) {
        input_report_key(uk->dev, KEY_L , uk->data[1] & 0x02 );
        input_sync(uk->dev);
    }

    if( (key_val & 0x04) != (uk->data[1] & 0x04) ) {
        input_report_key(uk->dev, KEY_ENTER , uk->data[1] & 0x04 );
        input_sync(uk->dev);
    }

    key_val = uk->data[1];

//    printk(KERN_INFO"usb_key_func\n ");

    status = usb_submit_urb(uk->key_urb, GFP_ATOMIC);

    if (status)
        dev_err(&uk->usbdev->dev,
            "can't resubmit intr, %s-%s/input0, status %d\n",
            uk->usbdev->bus->bus_name,
            uk->usbdev->devpath, status);

}


static int usb_mouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_mouse_as_key *uk;
    struct usb_device *udev = interface_to_usbdev(intf);
    struct input_dev *input_dev;
    struct usb_endpoint_descriptor *endpoint;
    struct usb_host_interface *interface;
    int pipe;
    int err;


    /* usb是一个接口需要一个驱动程序,所以获取当前接口 */
    interface = intf->cur_altsetting;


    /* 鼠标设备,除了控制端点0之外,只有一个中断类型的输入端点 */
    if (interface->desc.bNumEndpoints != 1)
        return -ENODEV;


    /* 
     * 一个接口有会有一个或多个端点,鼠标通常是只有一个输入端点 
     * 所以这里这里获取接口里面的第0个端点的端点描述符
     */
    endpoint = &interface->endpoint[0].desc;

    /* 根据端点描述符中的信息,确认是输入类型的中断端点 */
   if (!usb_endpoint_is_int_in(endpoint))
        return -ENODEV;


    /* 
     * input system 步骤
     * 1.分配输入设备
     * 2.设置能力
     * 3.注册这个设备
     * 4.硬件相关操作
     */

    /* 1.分配一个输入类设备 */
    input_dev = devm_input_allocate_device(&udev->dev);
    if(!input_dev) {
        dev_err(&udev->dev, "devm_input_allocate_device fail\n");
        return -ENOMEM;
    }

    /* 2.设置输入设备的能力 */
    input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
    input_set_capability(input_dev,EV_KEY,KEY_L);
    input_set_capability(input_dev,EV_KEY,KEY_S);
    input_set_capability(input_dev,EV_KEY,KEY_ENTER);



  /* 3.注册这个设备 */
    err = input_register_device(input_dev);
    if(err) {
        dev_err(&udev->dev, "input_register_device fail\n");
        return -ENODEV;
    }
    input_dev->dev.parent = &intf->dev;


    /* 4.硬件相关的操作 */
    /*
     * usb数据传输的3要素
     * 源, 目的, 长度
     */

    uk = kzalloc(sizeof(struct usb_mouse_as_key), GFP_KERNEL);
    if(!uk) {
        dev_err(&udev->dev, "usb kzalloc fail\n");
        return -ENOMEM;
    }
    uk->dev = input_dev;
    uk->usbdev = udev;

    /* 4.1 源:一个usb设备的传输目的,也就是端点 */
    /*
     * 因为端点里面有效的传输目的只有端点号,而作为具体设备我们要查询的
     * 是要指定设备号,端点号,传输类型,传输方向 这些的集合就是一条usb
     * 主机控制器到某个具体设备上具体端点的一条管道
     */
   pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);


    /* 4.2 长度,获取这个端点上传输包数据的最大长度 */
    uk->maxp = endpoint->wMaxPacketSize;
    printk(KERN_INFO"wMaxPacketSize = %d\n", uk->maxp);

    /* 4.3 目的,获取发送缓冲区, uk->data是分配maxp长度内存的虚拟地址,uk->data_dma是分配内存的物理地址 */
    uk->data = usb_alloc_coherent(udev, 8 , GFP_ATOMIC, &uk->data_dma);
    if(!uk->data) {
        dev_err(&udev->dev, "usb_alloc_coherent fail\n");
        return -ENOMEM;
    }


    /* 4.4 获取一个usb request block,主要是来组织管理usb一包数据的提交 */
    uk->key_urb = usb_alloc_urb(0, GFP_KERNEL);
    if(!uk->key_urb) {
        dev_err(&udev->dev, "usb_alloc_urb fail\n");
        return -ENOMEM;
    }

    /* 4.5 把这个urb设置为一个中断类型的urb */
    usb_fill_int_urb(uk->key_urb, udev, pipe, uk->data, uk->maxp, usb_key_func, uk, endpoint->bInterval);


    /*
     * 4.7 因为usb总线,使用只能使用物理地址,内存使用的是虚拟地址,
     * 我们在获取这块内存时,已经拿到了物理地址,不需要再映射一次了
     */
    uk->key_urb->transfer_dma = uk->data_dma;
    uk->key_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //加了这个标志,表示不需要在映射

    /* 把uk,设置到接口的driver_data中,方便通过接口直接获取uk */
    usb_set_intfdata(intf, uk);

    usb_submit_urb(uk->key_urb, GFP_ATOMIC);

    return 0;
}



static void usb_mouse_as_key_disconnect(struct usb_interface *intf)
{
    /* 从接口获取uk */
    struct usb_mouse_as_key *uk = usb_get_intfdata (intf);

    usb_set_intfdata(intf, NULL);

    if(uk)  {
        /* 释放probe中申请的资源 */
        usb_kill_urb(uk->key_urb);
        usb_free_urb(uk->key_urb);
        usb_free_coherent(uk->usbdev, 8, uk->data, uk->data_dma);
        input_unregister_device(uk->dev);
        input_free_device(uk->dev);
        kfree(uk);
    }

}


static const struct usb_device_id usb_mouse_as_key_id_table[] = {
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
        USB_INTERFACE_PROTOCOL_MOUSE) },
    {} /* Terminating entry */
};

static struct usb_driver usb_mouse_as_key_driver = {
    .name       = "usb_mouse_as_key",
    .probe      = usb_mouse_as_key_probe,
    .disconnect = usb_mouse_as_key_disconnect,
    .id_table   = usb_mouse_as_key_id_table,
};


module_usb_driver(usb_mouse_as_key_driver);
MODULE_LICENSE("GPL");

 

关于输入子系统的内容,这里不再描述,之前的章节有过详细的学习。

https://blog.csdn.net/qq_16777851/article/category/7916623

https://blog.csdn.net/qq_16777851/article/details/82936790

 

 

这里主要对之前没接触过的usb的一些数据结构和函数的功能进行使用和学习。

同时这里会大量用到usb协议中的内容和术语,如果看不懂。请看我本系列博客中前20章的关于usb协议的知识。

 

 

我们知道,一个USB设备,可以包含一个或多个配置,一个配置可以包含一个或多个接口,一个接口可以包含一个或多个端点。

我们通常写驱动,都是针对一个具体的接口,一个接口通常需要一个驱动程序。

 

通常对于一个usb鼠标,属于一个简单的usb设备,所以它这个设备中只包含一种配置,配置中也只包含一种接口,而这个接口也只包含一个输入端点。(控制端点0是每个usb设备都存在的)

 

这里我们就从代码中最开始接触到的ubs_interface说起,当然这个 数据结构也是usb驱动程序中特备重要的,也是我们经常使用的一个。


/**
 * struct usb_interface - what usb device drivers talk to
 * @altsetting: array of interface structures, one for each alternate
 *	setting that may be selected.  Each one includes a set of
 *	endpoint configurations.  They will be in no particular order.
 * @cur_altsetting: the current altsetting.
 * @num_altsetting: number of altsettings defined.
 * @intf_assoc: interface association descriptor
 * @minor: the minor number assigned to this interface, if this
 *	interface is bound to a driver that uses the USB major number.
 *	If this interface does not use the USB major, this field should
 *	be unused.  The driver should set this value in the probe()
 *	function of the driver, after it has been assigned a minor
 *	number from the USB core by calling usb_register_dev().
 * @condition: binding state of the interface: not bound, binding
 *	(in probe()), bound to a driver, or unbinding (in disconnect())
 * @sysfs_files_created: sysfs attributes exist
 * @ep_devs_created: endpoint child pseudo-devices exist
 * @unregistering: flag set when the interface is being unregistered
 * @needs_remote_wakeup: flag set when the driver requires remote-wakeup
 *	capability during autosuspend.
 * @needs_altsetting0: flag set when a set-interface request for altsetting 0
 *	has been deferred.
 * @needs_binding: flag set when the driver should be re-probed or unbound
 *	following a reset or suspend operation it doesn't support.
 * @authorized: This allows to (de)authorize individual interfaces instead
 *	a whole device in contrast to the device authorization.
 * @dev: driver model's view of this device
 * @usb_dev: if an interface is bound to the USB major, this will point
 *	to the sysfs representation for that device.
 * @pm_usage_cnt: PM usage counter for this interface
 * @reset_ws: Used for scheduling resets from atomic context.
 * @resetting_device: USB core reset the device, so use alt setting 0 as
 *	current; needs bandwidth alloc after reset.
 *
 * USB device drivers attach to interfaces on a physical device.  Each
 * interface encapsulates a single high level function, such as feeding
 * an audio stream to a speaker or reporting a change in a volume control.
 * Many USB devices only have one interface.  The protocol used to talk to
 * an interface's endpoints can be defined in a usb "class" specification,
 * or by a product's vendor.  The (default) control endpoint is part of
 * every interface, but is never listed among the interface's descriptors.
 *
 * The driver that is bound to the interface can use standard driver model
 * calls such as dev_get_drvdata() on the dev member of this structure.
 *
 * Each interface may have alternate settings.  The initial configuration
 * of a device sets altsetting 0, but the device driver can change
 * that setting using usb_set_interface().  Alternate settings are often
 * used to control the use of periodic endpoints, such as by having
 * different endpoints use different amounts of reserved USB bandwidth.
 * All standards-conformant USB devices that use isochronous endpoints
 * will use them in non-default settings.
 *
 * The USB specification says that alternate setting numbers must run from
 * 0 to one less than the total number of alternate settings.  But some
 * devices manage to mess this up, and the structures aren't necessarily
 * stored in numerical order anyhow.  Use usb_altnum_to_altsetting() to
 * look up an alternate setting in the altsetting array based on its number.
 */
struct usb_interface {
	/* array of alternate settings for this interface,
	 * stored in no particular order */
	struct usb_host_interface *altsetting;

	struct usb_host_interface *cur_altsetting;	/* the currently
					 * active alternate setting */
	unsigned num_altsetting;	/* number of alternate settings */

	/* If there is an interface association descriptor then it will list
	 * the associated interfaces */
	struct usb_interface_assoc_descriptor *intf_assoc;

	int minor;			/* minor number this interface is
					 * bound to */
	enum usb_interface_condition condition;		/* state of binding */
	unsigned sysfs_files_created:1;	/* the sysfs attributes exist */
	unsigned ep_devs_created:1;	/* endpoint "devices" exist */
	unsigned unregistering:1;	/* unregistration is in progress */
	unsigned needs_remote_wakeup:1;	/* driver requires remote wakeup */
	unsigned needs_altsetting0:1;	/* switch to altsetting 0 is pending */
	unsigned needs_binding:1;	/* needs delayed unbind/rebind */
	unsigned resetting_device:1;	/* true: bandwidth alloc after reset */
	unsigned authorized:1;		/* used for interface authorization */

	struct device dev;		/* interface specific device info */
	struct device *usb_dev;
	atomic_t pm_usage_cnt;		/* usage counter for autosuspend */
	struct work_struct reset_ws;	/* for resets in atomic context */
};

很感谢负责usb这块的大神,注释给的这么多!

  •  altsetting:接口结构数组,每个备用设置可以选择一个。每个包括一组端点配置。他们没有特别的顺序。
  •  cur_altsetting:当前正在使用的设置。
  •  num_altsetting:接口具有可选设置的数量。
  •  intf_assoc:接口关联描述符
  •  minor:分配给此接口的次要编号,如果此接口绑定到使用USB主编号的驱动程序。如果此接口不使用USB major,则应该不使用此字段。在通过调用usb_register_dev()从USB内核分配minor number后,驱动程序应在驱动程序的probe()函数中设置此值。
  •  condition:接口的绑定状态:未绑定,绑定(在probe()中),绑定到驱动程序或解除绑定(在disconnect()中)
  • sysfs_files_created:存在sysfs属性
  •  ep_devs_created:存在端点子伪设备
  •  unregistering:取消注册接口时设置标志
  •  needs_remote_wakeup:当自动暂停期间驱动程序需要远程唤醒功能时设置标志。
  •  needs_altsetting0:当延迟对altsetting 0的set-interface请求时设置标志。
  •  needs_binding:在重置探测驱动程序或在不支持的重置或挂起操作后未绑定时设置标志。
  •  authorized:与设备授权相比,这允许(de)授权单个接口而不是整个设备。
  •  dev:驱动程序模型的此设备视图。
  •  usb_dev:如果接口绑定到USB major,则会指向该设备的sysfs表示。
  •  pm_usage_cnt:此接口的PM使用计数器
  •  reset_ws:用于从原子上下文调度重置。
  •  resetting_device:USB内核重置设备,因此使用alt设置0作为当前;重置后需要带宽分配。

 

按照usb的说法,接口差不多可以是一个usb按功能来说的功能最小的划分了。

但usb在这里还增加了一个的设置功能,比如一个usb的扬声器,它具有变音功能,而这个变音功能就是属于它的设置。

 

我们这里的usb鼠标属于一个简单的usb设备,只有一个接口,一个设置。

这个设置中会包含有接口描述符合端点这个设置里面的端点信息。

 

具体到接口描述符,它当然就是描述接口本身的信息的。一个接口可以有多个设置,使用不同的设置, 描述接口的信息会有些不同, 所以接口描述符并没有放在 struct usb_interface结 构 里 , 而 是 放 在 表 示 接 口 设 置 的 struct usb_host_interface 结 构 里 。

/* host-side wrapper for one interface setting's parsed descriptors */
struct usb_host_interface {
	struct usb_interface_descriptor	desc;            //接口描述符

    /*  extralen,有关额外的描述符的。除了前面提到的四大描述符还有字符串描述符外,还有为一组设备也
     *  就是一类设备定义的描述符,和厂商为设备特别定义的描述符, extra 指的就是它们, extralen 表
     *  示它们的长度。 */
	int extralen;
	unsigned char *extra;   /* Extra descriptors */

	/* array of desc.bNumEndpoints endpoints associated with this
	 * interface setting.  these will be in no particular order.
	 */
    /* endpoint,一个数组,表示这个设置所使用到端点 */
	struct usb_host_endpoint *endpoint;             

	char *string;		/* iInterface string, if present  用来保存从设备里取出来的字符串描述符信息的,既然字符串描述符可有可无,那这里的指针也有可能为空了 */
};

接口描述符这里不再分析,很早之前就已经学习过了。

 


/**
 * struct usb_host_endpoint - host-side endpoint descriptor and queue
 * @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder
 * @ss_ep_comp: SuperSpeed companion descriptor for this endpoint
 * @ssp_isoc_ep_comp: SuperSpeedPlus isoc companion descriptor for this endpoint
 * @urb_list: urbs queued to this endpoint; maintained by usbcore
 * @hcpriv: for use by HCD; typically holds hardware dma queue head (QH)
 *	with one or more transfer descriptors (TDs) per urb
 * @ep_dev: ep_device for sysfs info
 * @extra: descriptors following this endpoint in the configuration
 * @extralen: how many bytes of "extra" are valid
 * @enabled: URBs may be submitted to this endpoint
 * @streams: number of USB-3 streams allocated on the endpoint
 *
 * USB requests are always queued to a given endpoint, identified by a
 * descriptor within an active interface in a given USB configuration.
 */
struct usb_host_endpoint {
	struct usb_endpoint_descriptor		desc;
	struct usb_ss_ep_comp_descriptor	ss_ep_comp;
	struct usb_ssp_isoc_ep_comp_descriptor	ssp_isoc_ep_comp;
	struct list_head		urb_list;
	void				*hcpriv;
	struct ep_device		*ep_dev;	/* For sysfs info */

	unsigned char *extra;   /* Extra descriptors */
	int extralen;
	int enabled;
	int streams;
};
  • desc:此端点的描述符,本机字节顺序中的wMaxPacketSize
  • ss_ep_comp:此端点的SuperSpeed伴随描述符
  • ssp_isoc_ep_comp:此端点的SuperSpeed Plus isoc伴随描述符
  • urb_list:排队到此端点的urbs; 由usbcore维护
  • hcpriv:供HCD使用; 通常保持硬件dma队列头(QH),每个urb具有一个或多个传输描述符(TD)
  • ep_dev:sysfs信息的ep_device
  • extra:配置中此端点后面的描述符
  • extralen:“extra”的字节数是多少有效
  • enabled:URB可以提交给此端点
  • streams:端点上分配的USB-3流的数量
  • USB请求始终排队到给定端点,由给定USB配置中的活动接口内的描述符标识。

usb3.0相关的我们暂时就不分析了,先学好2.0在说。

 

这里要说在usb协议代码中用到的很常见的一个内联函数。

static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf)
{
	return to_usb_device(intf->dev.parent);
}

从接口的 struct usb_interface 结构体获得 usb 设备的 struct usb_device 结构体。
 

struct usb_interface {
	/* array of alternate settings for this interface,
	 * stored in no particular order */
	struct usb_host_interface *altsetting;

	struct usb_host_interface *cur_altsetting;	/* the currently
					 * active alternate setting */
	unsigned num_altsetting;	/* number of alternate settings */

	/* If there is an interface association descriptor then it will list
	 * the associated interfaces */
	struct usb_interface_assoc_descriptor *intf_assoc;

	int minor;			/* minor number this interface is
					 * bound to */
	enum usb_interface_condition condition;		/* state of binding */
	unsigned sysfs_files_created:1;	/* the sysfs attributes exist */
	unsigned ep_devs_created:1;	/* endpoint "devices" exist */
	unsigned unregistering:1;	/* unregistration is in progress */
	unsigned needs_remote_wakeup:1;	/* driver requires remote wakeup */
	unsigned needs_altsetting0:1;	/* switch to altsetting 0 is pending */
	unsigned needs_binding:1;	/* needs delayed unbind/rebind */
	unsigned resetting_device:1;	/* true: bandwidth alloc after reset */
	unsigned authorized:1;		/* used for interface authorization */

	struct device dev;		/* interface specific device info 一个接口代表一个功能,所以有一个dev结构体    */
	struct device *usb_dev;
	atomic_t pm_usage_cnt;		/* usage counter for autosuspend */
	struct work_struct reset_ws;	/* for resets in atomic context */
};

 

设备中我们拿出主要的这个用到的parent。这个parent通常指向这个设备的上一层的设备(即依赖关系)

struct device {
	struct device		*parent;

	......
};

对于一个usb设备,这里我们也拿出我们这里用到的这个dev。

struct usb_device {
	int		devnum;

    ......

	struct device dev;

    ......
};

 

接下来就很明显了,我们也能猜到,在usb中,一个设备,包含一个或多个配置,一个配置包含一个或多个接口......。所以肯定接口的parent是设备。

usb_interface.dev.parent = &usb_device.dev
#define container_of(ptr, type, member) ({          \
	const typeof(((type *)0)->member)*__mptr = (ptr);    \
		     (type *)((char *)__mptr - offsetof(type, member)); })



#define	to_usb_device(d) container_of(d, struct usb_device, dev)

static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf)
{
	return to_usb_device(intf->dev.parent);
}

很明显知道了usb_device.dev的地址,很容易就知道了usb_device的地址。

 

我们在input注册设备的时候,下面也有这样的关联关系。比较这个usb的输入设备,必须是依赖这个usb接口的。要是usb接口驱动卸载了,那么这个输入功能肯定也不能存在的。

  input_dev->dev.parent = &intf->dev;

 

接下来就是上面的代码分析了:

 

从源说起:

端点是USB设备的唯一可识别部分,其是主机和设备之间的通信流的终点。

端点通常由端点号表示,但是通常是端点号是在一个usb设备内所说的。而我们要发送是把信息发送给一个usb设备的具体端点。这里把一个usb设备上的某个端点和这个设备关联起来就需要用到管道这个概念了。

管道(pipe):USB管道是设备上的端点与主机上的软件之间的关联。 管道表示通过内存缓冲区和设备上的端点在主机上的软件之间移动数据的能力。

因为端点是有方向的,所以管道也是有方向的。所以一般我们说的时候都是会说输入端点2,输出端点3这样。

下面表示一个usb设备内端点。端点0,每个usb设备都应该支持。

struct usb_device {
	int		devnum;

    ....    

	struct usb_host_endpoint *ep_in[16];
	struct usb_host_endpoint *ep_out[16];

    .....
};

 

usb的普通端点是有固定方向,有固定属性(控制,中断,批量,等时),固定端点号。如果再加上usb主控制器给分配的设备号。这样的结合在逻辑层面,就做管道。

下一节学习一个管道相关的数据结构。

 

你可能感兴趣的:(USB协议,从零开始学USB)