Linux USB驱动程序:中断URB

时间:2013-04-27 17:30:57

标签: linux-kernel usb linux-device-driver embedded-linux device-driver

我想我实际上有两个不同的问题,但我认为它们足以将它们包括在内。上下文是Linux USB设备驱动程序(不是用户空间)。

  1. 在发送请求URB后,如何在调用complete回调后收到响应?
  2. 如何对单个请求/响应对使用中断URB,而不是作为实际的连续中断轮询(如预期那样)?
  3. 因此,在某些背景下,我正在为Microchip MCP2210开发一个带有GPIO的USB-to-SPI协议转换器(USB 2.0,datasheet here)。该设备通告为通用HID,并公开两个中断端点(一个输入和输出)以及它的控制端点。

    我从一个人工作的(但是α-质量的)demo driver开始,并与社区分享。但是,这是一个HID驱动程序,它用来与设备通信的机制非常昂贵! (发送64字节消息需要分配6k HID报告结构,有时在中断的上下文中执行分配,需要GFP_ATOMIC!)。我们将从嵌入式低内存设备访问它。

    我是USB驱动程序的新手,并且在Linux设备驱动程序方面仍然非常环保。但是,我正在尝试将其转换为普通的USB驱动程序(而不是HID),因此我可以使用较便宜的中断URB进行通信。这是我传输请求的代码。为了(尝试)简洁,我不包括我的结构的定义等,但如果您需要更多我的代码,请告诉我。 dev->cur_cmd是我保留当前正在处理的命令的地方。

    /* 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;
    }
    

    这是我完整的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);
        };
    }
    

    我至少在这附近吗?其次,dev->int_out_ep->desc.bIntervaldev->int_in_ep->desc.bInterval都等于1,这会持续每125微秒发送一次请求吗?如果是这样,我怎么说“ok,ty,现在停止这个中断”。 MCP2210仅提供一种配置,一个接口,并且只有两个中断端点。 (我知道所有东西都有控制界面,不知道它在哪里适合图片。)

    不是用lsusb -v垃圾邮件这个问题,而是用它来粘贴它。

1 个答案:

答案 0 :(得分:3)

通常,请求/响应通信的工作方式如下:

  1. 提交回应URB;
  2. 提交请求URB;
  3. 在请求完成处理程序中,如果请求未实际发送,则取消响应URB并中止;
  4. 在响应完成处理程序中,处理响应数据。
  5. 如果你有一个几乎立即完成的URB,所有异步完成处理程序的东西都是一个很大的麻烦;因此,辅助函数usb_interrupt_msg()可以同步工作。

    必须重新提交要用于轮询的URB(通常来自完成处理程序)。 如果您不重新提交URB,则不会进行轮询。