3
votes

I suppose I actually have two separate questions, but I think that they are related enough to include them both. The context is a Linux USB device driver (not userspace).

  1. After transmitting a request URB, how do I receive the response once my complete callback is called?
  2. How can I use interrupt URBs for single request/response pairs, and not as actual continuous interrupt polling (as they are intended)?

So for some background, I'm working on a driver for the Microchip MCP2210 a USB-to-SPI Protocol Converter with GPIO (USB 2.0, datasheet here). This device advertises as generic HID and exposes two interrupt endpoints (an in and an out) as well as it's control endpoint.

I am starting from a working, (but alpha-quality) demo driver written by somebody else and kindly shared with the community. However, this is a HID driver and the mechanism it uses to communicate with the device is very expensive! (sending a 64 byte message requires allocating a 6k HID report struct, and allocation is sometimes performed in the context of an interrupt, requiring GFP_ATOMIC!). We'll be accessing this from an embedded low-memory device.

I'm new to USB drivers and still pretty green with Linux device drivers in general. However, I'm trying to convert this to a plain-jane USB driver (not HID) so I can use the less expensive interrupt URBs for my communications. Here is my code for transmitting my request. For the sake of (attempted) brevity, I'm not including the definition of my structs, etc, but please let me know if you need more of my code. dev->cur_cmd is where I'm keeping the current command I'm processing.

/* use a local for brevity */
cmd = dev->cur_cmd;

if (cmd->state == MCP2210_CMD_STATE_NEW) {

    usb_fill_int_urb(dev->int_out_urb,
            dev->udev,
            usb_sndintpipe(dev->udev, dev->int_out_ep->desc.bEndpointAddress),
            &dev->out_buffer,
            sizeof(dev->out_buffer), /* always 64 bytes */
            cmd->type->complete,
            cmd,
            dev->int_out_ep->desc.bInterval);

    ret = usb_submit_urb(dev->int_out_urb, GFP_KERNEL);
    if (ret) {
        /* snipped: handle error */
    }
    cmd->state = MCP2210_CMD_STATE_XMITED;
}

And here is my complete fn:

/* note that by "ctrl" I mean a control command, not the control endpoint */
static void ctrl_complete(struct urb *)
{
    struct mcp2210_device *dev = urb->context;
    struct mcp2210_command *cmd = dev->cur_cmd;
    int ret;

    if (unlikely(!cmd || !cmd->dev)) {
        printk(KERN_ERR "mcp2210: ctrl_complete called w/o valid cmd "
                "or dev\n");
        return;
    }

    switch (cmd->state) {

    /* Time to rx the response */
    case MCP2210_CMD_STATE_XMITED:
        /* FIXME: I think that I need to check the response URB's 
         * status to find out if it was even transmitted or not */
        usb_fill_int_urb(dev->int_in_urb,
                dev->udev,
                usb_sndintpipe(dev->udev, dev->int_in_ep->desc
                    .bEndpointAddress),
                &dev->in_buffer,
                sizeof(dev->in_buffer),
                cmd->type->complete,
                dev,
                dev->int_in_ep->desc.bInterval);
        ret = usb_submit_urb(dev->int_in_urb, GFP_KERNEL);

        if (ret) {
            dev_err(&dev->udev->dev,
                "while attempting to rx response, "
                "usb_submit_urb returned %d\n", ret);
            free_cur_cmd(dev);
            return;
        }

        cmd->state = MCP2210_CMD_STATE_RXED;
        return;

    /* got response, now process it */
    case MCP2210_CMD_STATE_RXED:
        process_response(cmd);

    default:
        dev_err(&dev->udev->dev, "ctrl_complete called with unexpected state: %d", cmd->state);
        free_cur_cmd(dev);
    };
}

So am I at least close here? Secondly, both dev->int_out_ep->desc.bInterval and dev->int_in_ep->desc.bInterval are equal to 1, will this keep sending my request every 125 microseconds? And if so, how do I say "ok, ty, now stop this interrupt". The MCP2210 offers only one configuration, one interface and that has just the two interrupt endpoints. (I know everything has the control interface, not sure where that fits into the picture though.)

Rather than spam this question with the lsusb -v, I'm going to pastebin it.

1

1 Answers

3
votes

Typically, request/response communication works as follows:

  1. Submit the response URB;
  2. submit the request URB;
  3. in the request completion handler, if the request was not actually sent, cancel the response URB and abort;
  4. in the response completion handler, handle the response data.

All that asynchronous completion handler stuff is a big hassle if you have a single URB that is completed almost immediately; therefore, there is the helper function usb_interrupt_msg() which works synchronously.

URBs to be used for polling must be resubmitted (typically from the completion handler). If you do not resubmit the URB, no polling happens.