继续写usb gadget驱动(解决枚举失败问题)

上个小patch吧... 

关于昨天的usb枚举失败(获取配置描述符失败)

 

简要描述下:

1. 我的gadget配置成了usb3.2版本,  (设置成1.0, 2.0也遇到一些问题, 暂表不论)

Protocols Supported:
 USB 1.1:                         no
 USB 2.0:                         no
 USB 3.0:                         yes

 

2. 根据usb3协议, 相比usb1, usb2, 有些特殊且必要的描述符信息....

直接贴log...

[ 8825.746380] get desc for USB_DT_CONFIG
[ 8825.754287] buf:
[ 8825.756108] 0x9 0x2 0x1f 0x0 0x1 0x1 0x0 0xc0 0x1      // 0x2: 配置描述符, 后续跟接口, 端点等描述符信息, 故总长度是0x1f
[ 8825.760892] 0x9 0x4 0x0 0x0 0x1 0xff 0x0 0x0 0xfb        // 0x4: 接口描述符, 总长度0x9
[ 8825.765684] 0x7 0x5 0x1 0x2 0x40 0x0 0x0 0x6 0x30     // 0x5: 端点描述符, 总长度0x7; 0x30: 端点伙伴(companion)描述符...
[ 8825.770466] 0x0 0x0 0x0 0x0 

 

是的,  之前一直配置失败的原因是缺少了SuperSpeed Endpoint Companion描述信息, 即超速设备端点伙伴描述符...

usb3.0版本即super speed端点必须含有这个描述符,  且跟随在端点描述符后面...

 


果然不仔细看协议文档, 光调试代码, 很难找到缺少了什么...

anaway... 可以继续往下走了...


马上又遇到下一个问题, 

[ 8825.787376] setup request: 6, desc_type: 0xf, w_lenght:255
[ 8825.792853] get desc for USB_DT_BOS

对的, 继续添加BOS描述符信息...

 

什么是BOS... (Binary Device Object Store)

看了内容, 这玩意是扩展设备描述符的, Device Type是0xf。

可以添加设备级的一些能力描述, 即device-level capability extensions。

如usb2/3的LPM能力啥的, 即Link Power Management, 链路电路管理。具体又有相应的协议....


ok, 不管了,  上代码, 枚举成功。

#include <linux/module.h>
#include <linux/kernel.h>  /* min() */
#include <linux/printk.h>
#include <linux/usb/gadget.h>
#include <linux/stacktrace.h>
#include <linux/string.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("River");

#define USB_BUFSIZE 256

struct g_loop_dev {
    struct usb_gadget *gadget;      // gadget device
    struct usb_request *req;       // for ep0 request
    struct usb_ep *out_ep;          // TBD: for ep out transfer
    // struct cdev cdev;               // TBD: char dev
};

#define STR_ID_MANUFACTURER 25
#define STR_ID_PRODUCT 42
#define STR_ID_SERIAL 101
#define STR_ID_LOOPBACK 251

/* my specific vid&pid */
#define DRIVER_VENDOR_NUM 0x1234
#define DRIVER_PRODUCT_NUM 0x5678

// #define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
// #define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */

static struct usb_device_descriptor device_desc = {
    .bLength = sizeof (device_desc),
    .bDescriptorType = USB_DT_DEVICE,

    .bcdUSB = __constant_cpu_to_le16(0x0320),
    // .bcdUSB = __constant_cpu_to_le16(0x0201),
    .bDeviceClass = USB_CLASS_VENDOR_SPEC,
    .bMaxPacketSize0 = 9,
    .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
    .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
    .iManufacturer = STR_ID_MANUFACTURER,
    .iProduct = STR_ID_PRODUCT,
    .iSerialNumber = STR_ID_SERIAL,
    .bNumConfigurations = 1,
};

static struct usb_config_descriptor g_loop_config = {
    .bLength = sizeof g_loop_config,
    .bDescriptorType = USB_DT_CONFIG,

    .bNumInterfaces = 1,
    .bConfigurationValue = 1,
    .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
    .bMaxPower = 1, /* self-powered */
};

/* interface descriptor */
static struct usb_interface_descriptor g_loop_interface = {
    .bLength = sizeof(g_loop_interface),
    .bDescriptorType = USB_DT_INTERFACE,

    .bNumEndpoints = 1,
    .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
    .iInterface = STR_ID_LOOPBACK,
};

/* endpoint descriptor */
static struct usb_endpoint_descriptor fs_sink_desc = {
    .bLength = USB_DT_ENDPOINT_SIZE,
    .bDescriptorType= USB_DT_ENDPOINT,

    .bEndpointAddress = USB_DIR_OUT,    // for pc host, it is out ep.
    .bmAttributes = USB_ENDPOINT_XFER_BULK,
    // .wMaxPacketSize = __constant_cpu_to_le16(1024),
};

static struct usb_ss_ep_comp_descriptor ss_fs_sink_comp_desc = {
    .bLength = sizeof(ss_fs_sink_comp_desc),
    .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
	.bMaxBurst = 0,
	.bmAttributes = 0,
	.wBytesPerInterval = 0,
};

/* function/interface description... */
static const struct usb_descriptor_header *fs_g_loop_function[] = {
    (struct usb_descriptor_header *) &g_loop_interface,
    (struct usb_descriptor_header *) &fs_sink_desc,
    (struct usb_descriptor_header *) &ss_fs_sink_comp_desc,
    NULL,
};

static struct usb_string strings[] = {
    {STR_ID_MANUFACTURER, "river run", },
    {STR_ID_PRODUCT,      "river loop", },
    {STR_ID_SERIAL,       "1234567890", },
    {STR_ID_LOOPBACK,       "loop in to out", },
    {  },
};

static struct usb_gadget_strings string_table = {
    .language = 0x0409,     /* en-us */
    .strings = strings,
};

static void g_loop_setup_complete(struct usb_ep *ep,
        struct usb_request *req) {

    printk(KERN_INFO "g_loop_setup complete --> %d, %d, %d\n",
                    req->status, req->actual, req->length);

    return;
}

static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
{
    kfree(req->buf);
    usb_ep_free_request(ep, req);
}

static int g_loop_dev_prepare(struct g_loop_dev *dev) {
	struct usb_gadget *gadget = dev->gadget;
	int ret;

    /* reset ep autoconfig state */
	usb_ep_autoconfig_reset(gadget);

    /* config & prepare an endpoint */
    dev->out_ep = usb_ep_autoconfig(gadget, &fs_sink_desc);

    if (!dev->out_ep) {
        printk(KERN_ERR "usb_ep_autoconfig failed\n");
        ret = - ENOMEM;
        goto config_fail;
    }

    printk(KERN_INFO "ep_desc = %p\n", dev->out_ep->desc);
    printk(KERN_INFO "fs_sink_desc = %p\n", &fs_sink_desc);
    printk(KERN_INFO "ep->addr = %d\n", dev->out_ep->address);
    printk(KERN_INFO "ep->claimed = %d\n", dev->out_ep->claimed);
    printk(KERN_INFO "ep->enabled = %d\n", dev->out_ep->enabled);

    printk(KERN_INFO "usb_ep_autoconfig succeed\n");

	/* preallocate control response and buffer */
	dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
	if (!dev->req) {
		ret = -ENOMEM;
        goto ep_fail;
    }

	dev->req->buf = kmalloc(USB_BUFSIZE, GFP_KERNEL);
	if (!dev->req->buf)
		goto malloc_fail;

	dev->req->complete = g_loop_setup_complete;
	dev->req->context = dev;
	gadget->ep0->driver_data = dev;

	usb_gadget_set_selfpowered(gadget);

	return 0;

malloc_fail:
	usb_ep_free_request(gadget->ep0, dev->req);
	dev->req = NULL;

ep_fail:

config_fail:


    return ret;
}

static int g_loop_bind(struct usb_gadget *gadget,
        struct usb_gadget_driver *driver) {
    int ret = 0;
    struct g_loop_dev *dev;

    if (!gadget ||!driver) {
        ret = -EINVAL;
        goto invalid_param;
    }

    printk(KERN_INFO "gadget info: speed(%d), max_speed(%d)",
                gadget->speed, gadget->max_speed);

    printk(KERN_INFO "gadget info: maxpacket(%d), claimed(%d), enabled(%d)",
                gadget->ep0->maxpacket, gadget->ep0->claimed?1:0,
                gadget->ep0->enabled?1:0);


    dev = kzalloc(sizeof(struct g_loop_dev), GFP_KERNEL);
    if (!dev) {
        printk(KERN_ERR "malloc failed for g_loop_dev\n");
        ret = -ENOMEM;
        goto malloc_fail;
    }

	dev->gadget = gadget;
	set_gadget_data(gadget, dev);

    /* bMaxPacketSize format is 2^bMaxPacketSize0, eg, 2^9 = 512 */
    // device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;

    ret = g_loop_dev_prepare(dev);
    if (ret < 0) {
        printk(KERN_ERR "g_loop_dev_prepare failed...\n");
        goto prepare_fail;
    }

    printk(KERN_INFO "simple composite ready...\n");

    return ret;

prepare_fail:
    if (dev) {
        kfree(dev);
        dev = NULL;
    }

malloc_fail:
invalid_param:

    return ret;
}

static void g_loop_unbind(struct usb_gadget *gadget) {
    struct g_loop_dev *dev = get_gadget_data(gadget);

    if (dev->req) {
        dev->req->length= USB_BUFSIZE;
        free_ep_req(gadget->ep0, dev->req);
    }

    kfree(dev);

    set_gadget_data(gadget, NULL);

    printk(KERN_INFO "g_loop_unbind callback complete\n");

    return;
}

static int config_buf(struct usb_gadget *gadget,
                      u8 *buf, u8 type, unsigned index)
{
    //int is_source_sink;
    int len;
    const struct usb_descriptor_header **function;

    printk(KERN_INFO "config_buf, type = %d, index = %d\n",
                type, index);

    function = fs_g_loop_function;

    len = usb_gadget_config_buf(&g_loop_config,
                      buf, USB_BUFSIZE, function);
    if (len < 0)
        return len;

    ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
    return len;
}

/**
 * bos_desc() - prepares the BOS descriptor.
 * @cdev: pointer to usb_composite device to generate the bos
 *	descriptor for
 *
 * This function generates the BOS (Binary Device Object)
 * descriptor and its device capabilities descriptors. The BOS
 * descriptor should be supported by a SuperSpeed device.
 */
static int bos_desc(struct usb_gadget *gadget,
                    const struct usb_request *req) {
	struct usb_ext_cap_descriptor	*usb_ext;
	struct usb_dcd_config_params	dcd_config_params;
	struct usb_bos_descriptor	*bos = req->buf;

	bos->bLength = USB_DT_BOS_SIZE;
	bos->bDescriptorType = USB_DT_BOS;

	bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
	bos->bNumDeviceCaps = 0;

	/*
	 * A SuperSpeed device shall include the USB2.0 extension descriptor
	 * and shall support LPM when operating in USB2.0 HS mode.
	 */
	usb_ext = req->buf + le16_to_cpu(bos->wTotalLength);
	bos->bNumDeviceCaps++;
	le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE);
	usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
	usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
	usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
	usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);

	/*
	 * The Superspeed USB Capability descriptor shall be implemented by all
	 * SuperSpeed devices.
	 */
	if (gadget_is_superspeed(gadget)) {
		struct usb_ss_cap_descriptor *ss_cap;

		ss_cap = req->buf + le16_to_cpu(bos->wTotalLength);
		bos->bNumDeviceCaps++;
		le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
		ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
		ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
		ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
		ss_cap->bmAttributes = 0; /* LTM is not supported yet */
		ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
						      USB_FULL_SPEED_OPERATION |
						      USB_HIGH_SPEED_OPERATION |
						      USB_5GBPS_OPERATION);
		ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;

		/* Get Controller configuration */
		if (gadget->ops->get_config_params) {
			gadget->ops->get_config_params(
				&dcd_config_params);
		} else {
			dcd_config_params.bU1devExitLat =
				USB_DEFAULT_U1_DEV_EXIT_LAT;
			dcd_config_params.bU2DevExitLat =
				cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
		}
		ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
		ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
	}

	return le16_to_cpu(bos->wTotalLength);
}

/*
 * ep0 setup callback, we need to tell what kind of
 * device we are...
 * according to ch9, we need to response to get_descriptor
 * control command...
 */
static int g_loop_setup(struct usb_gadget *gadget,
                    const struct usb_ctrlrequest *ctl_req) {
    struct g_loop_dev *loop_dev = get_gadget_data(gadget);
    struct usb_request *req = NULL;
    int value = 0;
    // u16 w_index = le16_to_cpu(ctl_req->wIndex);
    u16 w_value = le16_to_cpu(ctl_req->wValue);
    u16 w_length = le16_to_cpu(ctl_req->wLength);
    int ret = 0;
    int i = 0;
    unsigned char *buf;

    printk(KERN_INFO "g_loop_setup callback\n");

    if (!loop_dev) {
        ret = -EFAULT;
        goto unknown;
    }

    req = loop_dev->req;
    buf = req->buf;

    // dump_stack();

    printk(KERN_INFO "setup request: %d, desc_type: 0x%x, "
            "w_lenght:%d\n",
            ctl_req->bRequest, w_value>>8, w_length);

    switch (ctl_req->bRequest) {

    /* handle some standard request... */
    case USB_REQ_GET_DESCRIPTOR: {
        if (ctl_req->bRequestType != USB_DIR_IN ) {
            printk(KERN_ERR "error request type(0x%x)\n",
                    ctl_req->bRequestType);
            ret = -EFAULT;
            goto unknown;
        }

        /* high byte means descriptor type,
         * descriptor index in low byte...
         */
        switch (w_value>>8) {

        case USB_DT_DEVICE:
            printk(KERN_INFO "get desc for USB_DT_DEVICE\n");
            value = min(w_length, (u16) sizeof device_desc);
            memcpy(req->buf, &device_desc,
                    value);

            break;
        case USB_DT_CONFIG:
            printk(KERN_INFO "get desc for USB_DT_CONFIG\n");
            /* use some help function fill config descriptor... */
            value = config_buf(gadget, req->buf,
                        w_value >> 8,
                        w_value & 0xff);
            if (value >= 0)
                value = min(w_length, (u16) value);

            break;
        case USB_DT_STRING:
            printk(KERN_INFO "get desc for USB_DT_STRING\n");
            value = usb_gadget_get_string(&string_table, w_value&0xff,
                                    req->buf);
            if (value >= 0)
                value = min(w_length, (u16) value);
            break;

        case USB_DT_OTG:
            printk(KERN_INFO "get desc for USB_DT_OTG, TBD...\n");
            break;

        case USB_DT_BOS:
            printk(KERN_INFO "get desc for USB_DT_BOS\n");
			if (gadget_is_superspeed(gadget)) {
				value = bos_desc(gadget, req);
				value = min(w_length, (u16) value);
			}
			break;

        default:
            printk(KERN_INFO "descriptor type: %d, do nothing\n",
                    w_value>>8);
            break;
        }
    }

    break;

    case USB_REQ_SET_DESCRIPTOR:
        printk(KERN_INFO "set descriptor request, do nothing...\n");
        break;

    case USB_REQ_GET_CONFIGURATION:
        printk(KERN_INFO "get configuration request, do nothing...\n");
        break;

    case USB_REQ_SET_CONFIGURATION:
        printk(KERN_INFO "set configuration request, do nothing...\n");
        break;

    case USB_REQ_GET_INTERFACE:
        printk(KERN_INFO "get interfafe request, do nothing...\n");
        break;

    case USB_REQ_SET_INTERFACE:
        printk(KERN_INFO "set interfafe request, do nothing...\n");
        break;

    default:
        printk(KERN_INFO "bRequestType - %d, no handler...\n",
                    ctl_req->bRequestType);
        break;
    }

    if (value <= 0) {
        printk(KERN_ERR "no data needs to be sent\n");
        ret = -EFAULT;
        goto do_nothing;
    }

    printk(KERN_INFO "buf:\n");
    for (i=0; i<value; i++) {
        printk(KERN_CONT "0x%x ", buf[i]);
        if ((i+1)%9 == 0) {
            printk(KERN_CONT "\n");
        }
    }
    printk(KERN_CONT "\n");

    req->length = value;
    req->zero = value < w_length;
    printk(KERN_INFO "value: %d, w_length: %d\n",
            value, w_length);
    printk(KERN_INFO "req->zero: %d\n", req->zero);

    ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
    if (ret < 0) {
        printk(KERN_ERR "usb_ep_queue failed, ret(%d)\n", ret);
        req->status = 0;
        g_loop_setup_complete(gadget->ep0, req);
    }

    return ret;

do_nothing:
unknown:
    return ret;
}

static void g_loop_reset(struct usb_gadget *gadget) {
    printk(KERN_INFO "g_loop_reset callback\n");

    usb_ep_autoconfig_reset(gadget);

    return;
}

static void g_loop_disconnect(struct usb_gadget *gadget) {
    printk(KERN_INFO "g_loop_disconnect callback\n");

    return;
}

static struct usb_gadget_driver g_loop_driver = {
    .function = "gadget_g_loop",
    .max_speed = USB_SPEED_SUPER,
    // .max_speed = USB_SPEED_HIGH,
    .bind = g_loop_bind,
    .unbind = g_loop_unbind,
    .setup = g_loop_setup,
    .reset = g_loop_reset,
    .disconnect = g_loop_disconnect,
};

static int __init g_loop_init(void) {
    int ret = 0;

    printk(KERN_INFO "g_loop_init in\n");

    ret = usb_gadget_probe_driver(&g_loop_driver);

    if (ret < 0) {
        printk(KERN_ERR "usb gaget driver register failed, "
                "ret(%d)\n", ret);
        goto probe_failed;
    }

    printk(KERN_INFO "g_loop_init succeed\n");

    return 0;

probe_failed:

    return ret;
}

static void __exit g_loop_exit(void) {
    printk(KERN_INFO "g_loop_exit in\n");

    usb_gadget_unregister_driver(&g_loop_driver);

    printk(KERN_INFO "g_loop_exit succeed\n");

    return;
}

module_init(g_loop_init);
module_exit(g_loop_exit);

ok, 枚举成功, 说是基本成功了一大半...下面就是传输部分,  希望顺利...

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值