3

我想我实际上有两个单独的问题,但我认为它们足够相关以包含它们两者。上下文是一个Linux USB设备驱动程序(不是用户空间)。Linux USB驱动程序:中断URBs

  1. 发送请求URB后,如何收到回应一旦我的complete回调被调用?
  2. 如何使用中断URB作为单个请求/响应对,而不是作为实际的连续中断轮询(如其预期的那样)?

所以对于一些背景,我工作的一个驱动程序的Microchip MCP2210一个USB至SPI协议转换器,GPIO(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微秒发送一次我的请求?如果是这样,我怎么说“好吧,现在停止这个中断”。 MCP2210只提供一种配置,一种接口,并且只有两个中断端点。 (我知道一切都有控制界面,不知道哪里适合图片虽然)

而不是垃圾邮件这个问题与lsusb -v,我要pastebin它。

回答

3

典型地,请求/响应通信的工作原理如下:

  1. 提交响应URB;
  2. 提交请求URB;
  3. 在请求完成处理程序中,如果请求没有被实际发送,则取消响应URB并中止;
  4. 在响应完成处理程序中处理响应数据。

如果您有一个几乎立即完成的单个URB,那么所有异步完成处理程序的东西都是一件很麻烦的事情;因此,有帮助功能usb_interrupt_msg()它同步工作。

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

+1

非常感谢您的回应!这对我来说似乎很奇怪,你先提交响应URB,然后再提交请求。也许这是因为即使您的任务在发送请求后被抢占,USB主机驱动程序也可以*接收*响应。然而,我可以同时发送它们,当我可能处于可以睡眠并分配'GFP_KERNEL'的环境中,而不是在“完成”功能中(我相信)您无法入睡(我知道在一些spi主函数中就是这种情况)。 – 2013-05-05 04:12:17

+3

嘿,我想我会在3年多的时间里为你拍摄更新。 [驱动程序](https://github.com/daniel-santos/mcp2210-linux)运行得非常好,开始被很多人使用。感谢您帮助我到达那里! :) – 2016-02-11 06:07:21