USB gadget

usb device driver 分为三部分, usb controller driver, gadget driver 和function driver.
gadget driver和funciton driver中间还有个composite driver 层。

本文从controller driver 开始, 以一个串口gadget 为例进行分析。

USB Controller Driver

usb controller driver 负责操作寄存器,填充相应的接口供上层使用。

static int s3c_hsudc_probe(struct platform_device *pdev)
{。。。。。。。hsudc->gadget.max_speed = USB_SPEED_HIGH;hsudc->gadget.ops = &s3c_hsudc_gadget_ops;hsudc->gadget.name = dev_name(dev);hsudc->gadget.ep0 = &hsudc->ep[0].ep;hsudc->gadget.is_otg = 0;hsudc->gadget.is_a_peripheral = 0;hsudc->gadget.speed = USB_SPEED_UNKNOWN;s3c_hsudc_setup_ep(hsudc);。。。。。。ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget);if (ret)goto err_add_udc;pm_runtime_enable(dev);return 0;
。。。。。
}

struct usb_gadget

struct usb_gadget {struct work_struct        work;struct usb_udc         *udc;/* readonly to gadget driver */const struct usb_gadget_ops *ops;struct usb_ep          *ep0; /*ep0端点*/struct list_head     ep_list;    /* of usb_ep 除ep0之外的端点*/enum usb_device_speed       speed;enum usb_device_speed     max_speed;enum usb_device_state     state;const char            *name;struct device         dev;unsigned            out_epnum;unsigned          in_epnum;unsigned           mA;struct usb_otg_caps      *otg_caps;unsigned          sg_supported:1;unsigned         is_otg:1;unsigned           is_a_peripheral:1;unsigned          b_hnp_enable:1;unsigned         a_hnp_support:1;unsigned            a_alt_hnp_support:1;unsigned            hnp_polling_support:1;unsigned          host_request_flag:1;unsigned            quirk_ep_out_aligned_size:1;unsigned            quirk_altset_not_supp:1;unsigned            quirk_stall_not_supp:1;unsigned         quirk_zlp_not_supp:1;unsigned           quirk_avoids_skb_reserve:1;unsigned         is_selfpowered:1;unsigned           deactivated:1;unsigned          connected:1;
};

需要实现的接口

struct usb_gadget_ops {int   (*get_frame)(struct usb_gadget *);int   (*wakeup)(struct usb_gadget *);int  (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);int    (*vbus_session) (struct usb_gadget *, int is_active);int    (*vbus_draw) (struct usb_gadget *, unsigned mA);int (*pullup) (struct usb_gadget *, int is_on);int  (*ioctl)(struct usb_gadget *,unsigned code, unsigned long param);void   (*get_config_params)(struct usb_dcd_config_params *);int    (*udc_start)(struct usb_gadget *,struct usb_gadget_driver *);int    (*udc_stop)(struct usb_gadget *);struct usb_ep *(*match_ep)(struct usb_gadget *,struct usb_endpoint_descriptor *,struct usb_ss_ep_comp_descriptor *);
};

使用usb_add_gadget_udc() 函数添加gadget. usb_add_gadget_udc()是usb_add_gadget_udc_release()函数的封装。

//driver/usb/gadget/udc/core.c
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,void (*release)(struct device *dev))
{struct usb_udc     *udc;int            ret = -ENOMEM;udc = kzalloc(sizeof(*udc), GFP_KERNEL);if (!udc)goto err1;dev_set_name(&gadget->dev, "gadget");INIT_WORK(&gadget->work, usb_gadget_state_work);gadget->dev.parent = parent;if (release)gadget->dev.release = release;elsegadget->dev.release = usb_udc_nop_release;ret = device_register(&gadget->dev);if (ret)goto err2;device_initialize(&udc->dev);udc->dev.release = usb_udc_release;udc->dev.class = udc_class;udc->dev.groups = usb_udc_attr_groups;udc->dev.parent = parent;ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));if (ret)goto err3;udc->gadget = gadget;gadget->udc = udc;mutex_lock(&udc_lock);list_add_tail(&udc->list, &udc_list);ret = device_add(&udc->dev);if (ret)goto err4;usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);udc->vbus = true;/* pick up one of pending gadget drivers 选择gadget driver进行匹配*/ret = check_pending_gadget_drivers(udc);if (ret)goto err5;mutex_unlock(&udc_lock);return 0;err5:device_del(&udc->dev);err4:list_del(&udc->list);mutex_unlock(&udc_lock);err3:put_device(&udc->dev);device_del(&gadget->dev);err2:put_device(&gadget->dev);kfree(udc);err1:return ret;
}
/* should be called with udc_lock held */
static int check_pending_gadget_drivers(struct usb_udc *udc)
{struct usb_gadget_driver *driver;int ret = 0;list_for_each_entry(driver, &gadget_driver_pending_list, pending)if (!driver->udc_name || strcmp(driver->udc_name,dev_name(&udc->dev)) == 0) {ret = udc_bind_to_driver(udc, driver);if (ret != -EPROBE_DEFER)list_del(&driver->pending);break;}return ret;
}
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{int ret;dev_dbg(&udc->dev, "registering UDC driver [%s]\n",driver->function);udc->driver = driver;udc->dev.driver = &driver->driver;udc->gadget->dev.driver = &driver->driver;ret = driver->bind(udc->gadget, driver);if (ret)goto err1;ret = usb_gadget_udc_start(udc);if (ret) {driver->unbind(udc->gadget);goto err1;}usb_udc_connect_control(udc);kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);return 0;
err1:if (ret != -EISNAM)dev_err(&udc->dev, "failed to start %s: %d\n",udc->driver->function, ret);udc->driver = NULL;udc->dev.driver = NULL;udc->gadget->dev.driver = NULL;return ret;
}

static inline int usb_gadget_udc_start(struct usb_udc *udc)
{return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
}

调用udc_start() 回调函数,回调函数的用法。
udc_start()主要做的事情就是是能udc的中断,将ep0的状态准备好。

static const struct usb_gadget_ops s3c_hsudc_gadget_ops = {.get_frame   = s3c_hsudc_gadget_getframe,.udc_start = s3c_hsudc_start,.udc_stop    = s3c_hsudc_stop,.vbus_draw    = s3c_hsudc_vbus_draw,
};
static int s3c_hsudc_start(struct usb_gadget *gadget,struct usb_gadget_driver *driver)
{struct s3c_hsudc *hsudc = to_hsudc(gadget);int ret;if (!driver|| driver->max_speed < USB_SPEED_FULL|| !driver->setup)return -EINVAL;if (!hsudc)return -ENODEV;if (hsudc->driver)return -EBUSY;hsudc->driver = driver;ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies),hsudc->supplies);if (ret != 0) {dev_err(hsudc->dev, "failed to enable supplies: %d\n", ret);goto err_supplies;}/* connect to bus through transceiver */if (!IS_ERR_OR_NULL(hsudc->transceiver)) {ret = otg_set_peripheral(hsudc->transceiver->otg,&hsudc->gadget);if (ret) {dev_err(hsudc->dev, "%s: can't bind to transceiver\n",hsudc->gadget.name);goto err_otg;}}enable_irq(hsudc->irq);s3c_hsudc_reconfig(hsudc);pm_runtime_get_sync(hsudc->dev);s3c_hsudc_init_phy();if (hsudc->pd->gpio_init)hsudc->pd->gpio_init();return 0;
err_otg:regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
err_supplies:hsudc->driver = NULL;return ret;
}static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
{writel(0xAA, hsudc->regs + S3C_EDR);writel(1, hsudc->regs + S3C_EIER);writel(0, hsudc->regs + S3C_TR);writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);writel(0, hsudc->regs + S3C_EP0CR);s3c_hsudc_setup_ep(hsudc);
}

总结上边usb controller driver所做的工作如下:

  1. driver中定义的device controller描述结构中包含gadget结构,driver probe中填充里边的回调函数。供上层使用。
  2. 注册这个gadget结构,创建与之关联的usb_udc, 注册usb_udc时,进行gadget于gadget driver的匹配。

添加端点信息

每个端点在物理上主要对应一段缓冲区,其他端点相关的还有属性描述信息区域,最大packet限制,端点号由端点的位置index给出。

static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
{int epnum;hsudc->ep0state = WAIT_FOR_SETUP;INIT_LIST_HEAD(&hsudc->gadget.ep_list);for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
}
static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,struct s3c_hsudc_ep *hsep, int epnum)
{char *dir;if ((epnum % 2) == 0) {dir = "out";} else {dir = "in";hsep->bEndpointAddress = USB_DIR_IN;}hsep->bEndpointAddress |= epnum;if (epnum)snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);elsesnprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);INIT_LIST_HEAD(&hsep->queue);  //controller usb request queueINIT_LIST_HEAD(&hsep->ep.ep_list);if (epnum)list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);hsep->dev = hsudc;hsep->ep.name = hsep->name;usb_ep_set_maxpacket_limit(&hsep->ep, epnum ? 512 : 64);hsep->ep.ops = &s3c_hsudc_ep_ops;hsep->fifo = hsudc->regs + S3C_BR(epnum);hsep->ep.desc = NULL;hsep->stopped = 0;hsep->wedge = 0;if (epnum == 0) {hsep->ep.caps.type_control = true;hsep->ep.caps.dir_in = true;hsep->ep.caps.dir_out = true;} else {hsep->ep.caps.type_iso = true;hsep->ep.caps.type_bulk = true;hsep->ep.caps.type_int = true;}if (epnum & 1)hsep->ep.caps.dir_in = true;elsehsep->ep.caps.dir_out = true;set_index(hsudc, epnum);writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
}struct usb_ep {void            *driver_data;const char     *name;const struct usb_ep_ops   *ops;struct list_head   ep_list;struct usb_ep_caps  caps;bool           claimed;bool            enabled;unsigned        maxpacket:16;unsigned       maxpacket_limit:16;unsigned     max_streams:16;unsigned     mult:2;unsigned     maxburst:5;u8           address;const struct usb_endpoint_descriptor    *desc;const struct usb_ss_ep_comp_descriptor    *comp_desc;
};struct usb_ep_ops {int (*enable) (struct usb_ep *ep,const struct usb_endpoint_descriptor *desc);int (*disable) (struct usb_ep *ep);struct usb_request *(*alloc_request) (struct usb_ep *ep,gfp_t gfp_flags);void (*free_request) (struct usb_ep *ep, struct usb_request *req);int (*queue) (struct usb_ep *ep, struct usb_request *req,gfp_t gfp_flags);int (*dequeue) (struct usb_ep *ep, struct usb_request *req);int (*set_halt) (struct usb_ep *ep, int value);int (*set_wedge) (struct usb_ep *ep);int (*fifo_status) (struct usb_ep *ep);void (*fifo_flush) (struct usb_ep *ep);
};
static struct usb_ep_ops s3c_hsudc_ep_ops = {.enable = s3c_hsudc_ep_enable,.disable = s3c_hsudc_ep_disable,.alloc_request = s3c_hsudc_alloc_request,.free_request = s3c_hsudc_free_request,.queue = s3c_hsudc_queue,.dequeue = s3c_hsudc_dequeue,.set_halt = s3c_hsudc_set_halt,.set_wedge = s3c_hsudc_set_wedge,
};
struct usb_request {void         *buf;unsigned       length; /*数据的长度*/dma_addr_t     dma;struct scatterlist  *sg;unsigned        num_sgs;unsigned        num_mapped_sgs;unsigned     stream_id:16;unsigned       no_interrupt:1;unsigned     zero:1;unsigned     short_not_ok:1;//结束后的回调函数void           (*complete)(struct usb_ep *ep,struct usb_request *req);void         *context;struct list_head   list;int            status;unsigned     actual; /*已发送数据的长度,或者已经接收到的数据长度*/
};
static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,gfp_t gfp_flags)
{struct s3c_hsudc_req *hsreq;hsreq = kzalloc(sizeof(*hsreq), gfp_flags);if (!hsreq)return NULL;INIT_LIST_HEAD(&hsreq->queue);return &hsreq->req;
}
static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
{struct s3c_hsudc_req *hsreq;hsreq = our_req(_req);WARN_ON(!list_empty(&hsreq->queue));kfree(hsreq);
}

由于USB 是一种主从总线,每次通信的发起者都是主机,最重要的USB enumeration在ep0上进行。通信的发起都是有中断驱动。
这里必须从USB enumeration开始,中断产生,检查时ep0中断,调用s3c_hsudc_handle_ep0_intr(hsudc)处理ep0中断。

static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
{struct s3c_hsudc *hsudc = _dev;struct s3c_hsudc_ep *hsep;u32 ep_intr;u32 sys_status;u32 ep_idx;spin_lock(&hsudc->lock);sys_status = readl(hsudc->regs + S3C_SSR);ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {spin_unlock(&hsudc->lock);return IRQ_HANDLED;}if (sys_status) {if (sys_status & S3C_SSR_VBUSON)writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);if (sys_status & S3C_SSR_ERR)writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);if (sys_status & S3C_SSR_SDE) {writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?USB_SPEED_HIGH : USB_SPEED_FULL;}if (sys_status & S3C_SSR_SUSPEND) {writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);if (hsudc->gadget.speed != USB_SPEED_UNKNOWN&& hsudc->driver && hsudc->driver->suspend)hsudc->driver->suspend(&hsudc->gadget);}if (sys_status & S3C_SSR_RESUME) {writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);if (hsudc->gadget.speed != USB_SPEED_UNKNOWN&& hsudc->driver && hsudc->driver->resume)hsudc->driver->resume(&hsudc->gadget);}if (sys_status & S3C_SSR_RESET) {writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {hsep = &hsudc->ep[ep_idx];hsep->stopped = 1;s3c_hsudc_nuke_ep(hsep, -ECONNRESET);}s3c_hsudc_reconfig(hsudc);hsudc->ep0state = WAIT_FOR_SETUP;}}if (ep_intr & S3C_EIR_EP0) {writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);set_index(hsudc, 0);s3c_hsudc_handle_ep0_intr(hsudc);}ep_intr >>= 1;ep_idx = 1;while (ep_intr) {if (ep_intr & 1)  {hsep = &hsudc->ep[ep_idx];set_index(hsudc, ep_idx);writel(1 << ep_idx, hsudc->regs + S3C_EIR);if (ep_is_in(hsep))s3c_hsudc_epin_intr(hsudc, ep_idx);elses3c_hsudc_epout_intr(hsudc, ep_idx);}ep_intr >>= 1;ep_idx++;}spin_unlock(&hsudc->lock);return IRQ_HANDLED;
}

s3c_hsudc_handle_ep0_intr() 处理ep0中断,

static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
{struct s3c_hsudc_ep *hsep = &hsudc->ep[0];struct s3c_hsudc_req *hsreq;u32 csr = readl(hsudc->regs + S3C_EP0SR);u32 ecr;if (csr & S3C_EP0SR_STALL) {ecr = readl(hsudc->regs + S3C_EP0CR);ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);writel(ecr, hsudc->regs + S3C_EP0CR);writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);hsep->stopped = 0;s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);hsudc->ep0state = WAIT_FOR_SETUP;hsep->bEndpointAddress &= ~USB_DIR_IN;return;}if (csr & S3C_EP0SR_TX_SUCCESS) {writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);if (ep_is_in(hsep)) {if (list_empty(&hsep->queue))return;hsreq = list_entry(hsep->queue.next,struct s3c_hsudc_req, queue);s3c_hsudc_write_fifo(hsep, hsreq);}}if (csr & S3C_EP0SR_RX_SUCCESS) {if (hsudc->ep0state == WAIT_FOR_SETUP)s3c_hsudc_process_setup(hsudc);else {if (!ep_is_in(hsep)) {if (list_empty(&hsep->queue))return;hsreq = list_entry(hsep->queue.next,struct s3c_hsudc_req, queue);s3c_hsudc_read_fifo(hsep, hsreq);}}}
}
static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
{struct s3c_hsudc_ep *hsep = &hsudc->ep[0];struct usb_ctrlrequest ctrl = {0};int ret;s3c_hsudc_nuke_ep(hsep, -EPROTO);s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);if (ctrl.bRequestType & USB_DIR_IN) {hsep->bEndpointAddress |= USB_DIR_IN;hsudc->ep0state = DATA_STATE_XMIT;} else {hsep->bEndpointAddress &= ~USB_DIR_IN;hsudc->ep0state = DATA_STATE_RECV;}switch (ctrl.bRequest) {case USB_REQ_SET_ADDRESS:if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))break;hsudc->ep0state = WAIT_FOR_SETUP;return;case USB_REQ_GET_STATUS:if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)break;s3c_hsudc_process_req_status(hsudc, &ctrl);return;case USB_REQ_SET_FEATURE:case USB_REQ_CLEAR_FEATURE:if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)break;s3c_hsudc_handle_reqfeat(hsudc, &ctrl);hsudc->ep0state = WAIT_FOR_SETUP;return;}if (hsudc->driver) {spin_unlock(&hsudc->lock);ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);spin_lock(&hsudc->lock);if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {hsep->bEndpointAddress &= ~USB_DIR_IN;hsudc->ep0state = WAIT_FOR_SETUP;}if (ret < 0) {dev_err(hsudc->dev, "setup failed, returned %d\n",ret);s3c_hsudc_set_halt(&hsep->ep, 1);hsudc->ep0state = WAIT_FOR_SETUP;hsep->bEndpointAddress &= ~USB_DIR_IN;}}
}/*** s3c_hsudc_epin_intr - Handle in-endpoint interrupt.* @hsudc - Device controller for which the interrupt is to be handled.* @ep_idx - Endpoint number on which an interrupt is pending.** Handles interrupt for a in-endpoint. The interrupts that are handled are* stall and data transmit complete interrupt.*/
static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
{struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];struct s3c_hsudc_req *hsreq;u32 csr;csr = readl(hsudc->regs + S3C_ESR);if (csr & S3C_ESR_STALL) {writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);return;}if (csr & S3C_ESR_TX_SUCCESS) {writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);if (list_empty(&hsep->queue))return;hsreq = list_entry(hsep->queue.next,struct s3c_hsudc_req, queue);if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&(csr & S3C_ESR_PSIF_TWO))s3c_hsudc_write_fifo(hsep, hsreq);}
}

USB enumeration

s3c_hsudc_process_setup(hsudc) 处理control tranfer 的setup transaction 其中最主要的工作由gadget_driver的回调函数setup()完成,因为USB enumeration时标准化的过程,他的处理在composite layer这层处理,处理完标准申请后,调用function的setup回调函数处理功能相关的request。

ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
static const struct usb_gadget_ops s3c_hsudc_gadget_ops = {.get_frame   = s3c_hsudc_gadget_getframe,.udc_start = s3c_hsudc_start,.udc_stop    = s3c_hsudc_stop,.vbus_draw    = s3c_hsudc_vbus_draw,
};
static const struct usb_gadget_driver composite_driver_template = {.bind        = composite_bind,.unbind       = composite_unbind,.setup      = composite_setup,.reset       = composite_disconnect,.disconnect = composite_disconnect,.suspend    = composite_suspend,.resume        = composite_resume,.filteroutdata  = composite_filteroutdata,.driver  = {.owner      = THIS_MODULE,},
};

下面是composite_setup函数

int
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{struct usb_composite_dev   *cdev = get_gadget_data(gadget);struct usb_request     *req = cdev->req; /*用来处理control transfer的request*/int               value = -EOPNOTSUPP;int                status = 0;u16             w_index = le16_to_cpu(ctrl->wIndex);u8              intf = w_index & 0xFF;u16              w_value = le16_to_cpu(ctrl->wValue);u16             w_length = le16_to_cpu(ctrl->wLength);struct usb_function       *f = NULL;u8               endp;/* partial re-init of the response message; the function or the* gadget might need to intercept e.g. a control-OUT completion* when we delegate to it.*/req->zero = 0;req->context = cdev;req->complete = composite_setup_complete;req->length = 0;gadget->ep0->driver_data = cdev;/** Don't let non-standard requests match any of the cases below* by accident.* 若不是标准的request 跳到unknown位置,调用function相关的处理过程*/if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)goto unknown;switch (ctrl->bRequest) {/* we handle all standard USB descriptors 处理标准的setup request*/case USB_REQ_GET_DESCRIPTOR:if (ctrl->bRequestType != USB_DIR_IN)goto unknown;switch (w_value >> 8) {/*获取设备描述符*/case USB_DT_DEVICE:cdev->desc.bNumConfigurations =count_configs(cdev, USB_DT_DEVICE); /*获取configuration的数目*/cdev->desc.bMaxPacketSize0 =cdev->gadget->ep0->maxpacket;if (gadget_is_superspeed(gadget)) {if (gadget->speed >= USB_SPEED_SUPER) {cdev->desc.bcdUSB = cpu_to_le16(0x0310);cdev->desc.bMaxPacketSize0 = 9;} else {cdev->desc.bcdUSB = cpu_to_le16(0x0210);}} else {cdev->desc.bcdUSB = cpu_to_le16(0x0200);}value = min(w_length, (u16) sizeof cdev->desc);memcpy(req->buf, &cdev->desc, value);  //拷贝到req的缓冲区break;case USB_DT_DEVICE_QUALIFIER:if (!gadget_is_dualspeed(gadget) ||gadget->speed >= USB_SPEED_SUPER)break;device_qual(cdev);value = min_t(int, w_length,sizeof(struct usb_qualifier_descriptor));break;case USB_DT_OTHER_SPEED_CONFIG:if (!gadget_is_dualspeed(gadget) ||gadget->speed >= USB_SPEED_SUPER)break;/* FALLTHROUGH */case USB_DT_CONFIG:value = config_desc(cdev, w_value);if (value >= 0)value = min(w_length, (u16) value);break;case USB_DT_STRING:value = get_string(cdev, req->buf,w_index, w_value & 0xff);if (value >= 0)value = min(w_length, (u16) value);break;case USB_DT_BOS:if (gadget_is_superspeed(gadget)) {value = bos_desc(cdev);value = min(w_length, (u16) value);}break;case USB_DT_OTG:if (gadget_is_otg(gadget)) {struct usb_configuration *config;int otg_desc_len = 0;if (cdev->config)config = cdev->config;elseconfig = list_first_entry(&cdev->configs,struct usb_configuration, list);if (!config)goto done;if (gadget->otg_caps &&(gadget->otg_caps->otg_rev >= 0x0200))otg_desc_len += sizeof(struct usb_otg20_descriptor);elseotg_desc_len += sizeof(struct usb_otg_descriptor);value = min_t(int, w_length, otg_desc_len);memcpy(req->buf, config->descriptors[0], value);}break;}break;/* any number of configs can work */case USB_REQ_SET_CONFIGURATION:if (ctrl->bRequestType != 0)goto unknown;if (gadget_is_otg(gadget)) {if (gadget->a_hnp_support)DBG(cdev, "HNP available\n");else if (gadget->a_alt_hnp_support)DBG(cdev, "HNP on another port\n");elseVDBG(cdev, "HNP inactive\n");}spin_lock(&cdev->lock);value = set_config(cdev, ctrl, w_value);spin_unlock(&cdev->lock);break;case USB_REQ_GET_CONFIGURATION:if (ctrl->bRequestType != USB_DIR_IN)goto unknown;if (cdev->config)*(u8 *)req->buf = cdev->config->bConfigurationValue;else*(u8 *)req->buf = 0;value = min(w_length, (u16) 1);break;/* function drivers must handle get/set altsetting */case USB_REQ_SET_INTERFACE:if (ctrl->bRequestType != USB_RECIP_INTERFACE)goto unknown;if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)break;f = cdev->config->interface[intf];if (!f)break;/** If there's no get_alt() method, we know only altsetting zero* works. There is no need to check if set_alt() is not NULL* as we check this in usb_add_function().*/if (w_value && !f->get_alt)break;spin_lock(&cdev->lock);value = f->set_alt(f, w_index, w_value);if (value == USB_GADGET_DELAYED_STATUS) {DBG(cdev,"%s: interface %d (%s) requested delayed status\n",__func__, intf, f->name);cdev->delayed_status++;DBG(cdev, "delayed_status count %d\n",cdev->delayed_status);}spin_unlock(&cdev->lock);break;case USB_REQ_GET_INTERFACE:if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))goto unknown;if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)break;f = cdev->config->interface[intf];if (!f)break;/* lots of interfaces only need altsetting zero... */value = f->get_alt ? f->get_alt(f, w_index) : 0;if (value < 0)break;*((u8 *)req->buf) = value;value = min(w_length, (u16) 1);break;case USB_REQ_GET_STATUS:if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&(w_index == OTG_STS_SELECTOR)) {if (ctrl->bRequestType != (USB_DIR_IN |USB_RECIP_DEVICE))goto unknown;*((u8 *)req->buf) = gadget->host_request_flag;value = 1;break;}/** USB 3.0 additions:* Function driver should handle get_status request. If such cb* wasn't supplied we respond with default value = 0* Note: function driver should supply such cb only for the* first interface of the function*/if (!gadget_is_superspeed(gadget))goto unknown;if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))goto unknown;value = 2;  /* This is the length of the get_status reply */put_unaligned_le16(0, req->buf);if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)break;f = cdev->config->interface[intf];if (!f)break;status = f->get_status ? f->get_status(f) : 0;if (status < 0)break;put_unaligned_le16(status & 0x0000ffff, req->buf);break;/** Function drivers should handle SetFeature/ClearFeature* (FUNCTION_SUSPEND) request. function_suspend cb should be supplied* only for the first interface of the function*/case USB_REQ_CLEAR_FEATURE:case USB_REQ_SET_FEATURE:if (!gadget_is_superspeed(gadget))goto unknown;if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))goto unknown;switch (w_value) {case USB_INTRF_FUNC_SUSPEND:if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)break;f = cdev->config->interface[intf];if (!f)break;value = 0;if (f->func_suspend)value = f->func_suspend(f, w_index >> 8);if (value < 0) {ERROR(cdev,"func_suspend() returned error %d\n",value);value = 0;}break;}break;default:
unknown:/** OS descriptors handling*/if (cdev->use_os_string && cdev->os_desc_config &&(ctrl->bRequestType & USB_TYPE_VENDOR) &&ctrl->bRequest == cdev->b_vendor_code) {struct usb_request     *req;struct usb_configuration   *os_desc_cfg;u8             *buf;int                interface;int               count = 0;req = cdev->os_desc_req;req->context = cdev;req->complete = composite_setup_complete;buf = req->buf;os_desc_cfg = cdev->os_desc_config;w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);memset(buf, 0, w_length);buf[5] = 0x01;switch (ctrl->bRequestType & USB_RECIP_MASK) {case USB_RECIP_DEVICE:if (w_index != 0x4 || (w_value >> 8))break;buf[6] = w_index;if (w_length == 0x10) {/* Number of ext compat interfaces */count = count_ext_compat(os_desc_cfg);buf[8] = count;count *= 24; /* 24 B/ext compat desc */count += 16; /* header */put_unaligned_le32(count, buf);value = w_length;} else {/* "extended compatibility ID"s */count = count_ext_compat(os_desc_cfg);buf[8] = count;count *= 24; /* 24 B/ext compat desc */count += 16; /* header */put_unaligned_le32(count, buf);buf += 16;value = fill_ext_compat(os_desc_cfg, buf);value = min_t(u16, w_length, value);}break;case USB_RECIP_INTERFACE:if (w_index != 0x5 || (w_value >> 8))break;interface = w_value & 0xFF;buf[6] = w_index;if (w_length == 0x0A) {count = count_ext_prop(os_desc_cfg,interface);put_unaligned_le16(count, buf + 8);count = len_ext_prop(os_desc_cfg,interface);put_unaligned_le32(count, buf);value = w_length;} else {count = count_ext_prop(os_desc_cfg,interface);put_unaligned_le16(count, buf + 8);count = len_ext_prop(os_desc_cfg,interface);put_unaligned_le32(count, buf);buf += 10;value = fill_ext_prop(os_desc_cfg,interface, buf);if (value < 0)return value;value = min_t(u16, w_length, value);}break;}if (value >= 0) {req->length = value;req->context = cdev;req->zero = value < w_length;value = composite_ep0_queue(cdev, req,GFP_ATOMIC);if (value < 0) {DBG(cdev, "ep_queue --> %d\n", value);req->status = 0;composite_setup_complete(gadget->ep0,req);}}return value;}VDBG(cdev,"non-core control req%02x.%02x v%04x i%04x l%d\n",ctrl->bRequestType, ctrl->bRequest,w_value, w_index, w_length);/* functions always handle their interfaces and endpoints...* punt other recipients (other, WUSB, ...) to the current* configuration code.*/if (cdev->config) {list_for_each_entry(f, &cdev->config->functions, list)if (f->req_match &&f->req_match(f, ctrl, false))goto try_fun_setup;} else {struct usb_configuration *c;list_for_each_entry(c, &cdev->configs, list)list_for_each_entry(f, &c->functions, list)if (f->req_match &&f->req_match(f, ctrl, true))goto try_fun_setup;}f = NULL;switch (ctrl->bRequestType & USB_RECIP_MASK) {case USB_RECIP_INTERFACE:if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)break;f = cdev->config->interface[intf];break;case USB_RECIP_ENDPOINT:if (!cdev->config)break;endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);list_for_each_entry(f, &cdev->config->functions, list) {if (test_bit(endp, f->endpoints))break;}if (&f->list == &cdev->config->functions)f = NULL;break;case USB_RECIP_DEVICE:if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)break;f = cdev->config->interface[intf];}break;}
try_fun_setup:if (f && f->setup)value = f->setup(f, ctrl);else {struct usb_configuration *c;c = cdev->config;if (!c)goto done;/* try current config's setup */if (c->setup) {value = c->setup(c, ctrl);goto done;}/* try the only function in the current config */if (!list_is_singular(&c->functions))goto done;f = list_first_entry(&c->functions, struct usb_function,list);if (f->setup)value = f->setup(f, ctrl);}goto done;}/* respond with data transfer before status phase? */if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {req->length = value;req->context = cdev;req->zero = value < w_length;value = composite_ep0_queue(cdev, req, GFP_ATOMIC); /*将request 加入到gadget->ep0队列中*/if (value < 0) {DBG(cdev, "ep_queue --> %d\n", value);req->status = 0;composite_setup_complete(gadget->ep0, req);}} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {WARN(cdev,"%s: Delayed status not supported for w_length != 0",__func__);}done:/* device either stalls (value < 0) or reports success */return value;
}

set configure

static int set_config(struct usb_composite_dev *cdev,const struct usb_ctrlrequest *ctrl, unsigned number)
{struct usb_gadget  *gadget = cdev->gadget;struct usb_configuration *c = NULL;int          result = -EINVAL;unsigned      power = gadget_is_otg(gadget) ? 8 : 100;int            tmp;if (number) {list_for_each_entry(c, &cdev->configs, list) {if (c->bConfigurationValue == number) { /*查找添加的配置的配置号*//** We disable the FDs of the previous* configuration only if the new configuration* is a valid one*/if (cdev->config)    /*若device已经被配置过,先reset掉这个配置*/reset_config(cdev);result = 0;break;}}if (result < 0)goto done;} else { /* Zero configuration value - need to reset the config 若指定的配置号为0,停止这个配置*/if (cdev->config)reset_config(cdev);result = 0;}INFO(cdev, "%s config #%d: %s\n",usb_speed_string(gadget->speed),number, c ? c->label : "unconfigured");if (!c)goto done;usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);cdev->config = c;/* Initialize all interfaces by setting them to altsetting zero. */for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { /*遍历配置中的interface*/struct usb_function   *f = c->interface[tmp];struct usb_descriptor_header **descriptors;if (!f)break;/** Record which endpoints are used by the function. This is used* to dispatch control requests targeted at that endpoint to the* function's setup callback instead of the current* configuration's setup callback.*/descriptors = function_descriptors(f, gadget->speed);for (; *descriptors; ++descriptors) {struct usb_endpoint_descriptor *ep;int addr;if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)continue;ep = (struct usb_endpoint_descriptor *)*descriptors;addr = ((ep->bEndpointAddress & 0x80) >> 3)|  (ep->bEndpointAddress & 0x0f);set_bit(addr, f->endpoints);}result = f->set_alt(f, tmp, 0);   /*使能这个接口*/if (result < 0) {DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",tmp, f->name, f, result);reset_config(cdev);goto done;}if (result == USB_GADGET_DELAYED_STATUS) {DBG(cdev,"%s: interface %d (%s) requested delayed status\n",__func__, tmp, f->name);cdev->delayed_status++;DBG(cdev, "delayed_status count %d\n",cdev->delayed_status);}}/* when we return, be sure our power usage is valid */power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
done:usb_gadget_vbus_draw(gadget, power);if (result >= 0 && cdev->delayed_status)result = USB_GADGET_DELAYED_STATUS;return result;
}

static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{struct f_acm       *acm = func_to_acm(f);struct usb_composite_dev *cdev = f->config->cdev;/* we know alt == 0, so this is an activation or a reset */if (intf == acm->ctrl_id) { /*控制接口*/dev_vdbg(&cdev->gadget->dev,"reset acm control interface %d\n", intf);usb_ep_disable(acm->notify);if (!acm->notify->desc)if (config_ep_by_speed(cdev->gadget, f, acm->notify))return -EINVAL;usb_ep_enable(acm->notify);} else if (intf == acm->data_id) { /*数据接口*/if (acm->notify->enabled) {dev_dbg(&cdev->gadget->dev,"reset acm ttyGS%d\n", acm->port_num);gserial_disconnect(&acm->port);}if (!acm->port.in->desc || !acm->port.out->desc) {dev_dbg(&cdev->gadget->dev,"activate acm ttyGS%d\n", acm->port_num);if (config_ep_by_speed(cdev->gadget, f,acm->port.in) ||config_ep_by_speed(cdev->gadget, f,acm->port.out)) {acm->port.in->desc = NULL;acm->port.out->desc = NULL;return -EINVAL;}}gserial_connect(&acm->port, acm->port_num);} elsereturn -EINVAL;return 0;
}
int gserial_connect(struct gserial *gser, u8 port_num)
{struct gs_port *port;unsigned long flags;int       status;if (port_num >= MAX_U_SERIAL_PORTS)return -ENXIO;port = ports[port_num].port;if (!port) {pr_err("serial line %d not allocated.\n", port_num);return -EINVAL;}if (port->port_usb) {pr_err("serial line %d is in use.\n", port_num);return -EBUSY;}/* activate the endpoints */status = usb_ep_enable(gser->in); /*使能端点*/if (status < 0)return status;gser->in->driver_data = port;status = usb_ep_enable(gser->out);if (status < 0)goto fail_out;gser->out->driver_data = port;/* then tell the tty glue that I/O can work */spin_lock_irqsave(&port->port_lock, flags);gser->ioport = port;port->port_usb = gser;/* REVISIT unclear how best to handle this state...* we don't really couple it with the Linux TTY.*/gser->port_line_coding = port->port_line_coding;/* REVISIT if waiting on "carrier detect", signal. *//* if it's already open, start I/O ... and notify the serial* protocol about open/close status (connect/disconnect).*/if (port->port.count) {pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);gs_start_io(port);if (gser->connect)gser->connect(gser);} else {if (gser->disconnect)gser->disconnect(gser);}status = gs_console_connect(port_num);spin_unlock_irqrestore(&port->port_lock, flags);return status;fail_out:usb_ep_disable(gser->in);return status;
}
static void acm_connect(struct gserial *port)
{struct f_acm       *acm = port_to_acm(port);acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;acm_notify_serial_state(acm); /*通过notify端点上报现在的状态*/
}

control transfer 发送数据

composite_ep0_queue就是简单的调用usb_ep_queue()

static int composite_ep0_queue(struct usb_composite_dev *cdev,struct usb_request *req, gfp_t gfp_flags)
{int ret;ret = usb_ep_queue(cdev->gadget->ep0, req, gfp_flags);if (ret == 0) {if (cdev->req == req)cdev->setup_pending = true;else if (cdev->os_desc_req == req)cdev->os_desc_pending = true;elseWARN(1, "unknown request %p\n", req);}return ret;
}
/*调用端点的queue回调函数*/
int usb_ep_queue(struct usb_ep *ep,struct usb_request *req, gfp_t gfp_flags)
{int ret = 0;if (WARN_ON_ONCE(!ep->enabled && ep->address)) {ret = -ESHUTDOWN;goto out;}ret = ep->ops->queue(ep, req, gfp_flags);out:trace_usb_ep_queue(ep, req, ret);return ret;
}static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,gfp_t gfp_flags)
{struct s3c_hsudc_req *hsreq;struct s3c_hsudc_ep *hsep;struct s3c_hsudc *hsudc;unsigned long flags;u32 offset;u32 csr;hsreq = our_req(_req);if ((!_req || !_req->complete || !_req->buf ||!list_empty(&hsreq->queue)))return -EINVAL;hsep = our_ep(_ep);hsudc = hsep->dev;if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)return -ESHUTDOWN;spin_lock_irqsave(&hsudc->lock, flags);set_index(hsudc, hsep->bEndpointAddress);_req->status = -EINPROGRESS;_req->actual = 0;/*若0号端点数据长度为0 数据长度为0*/if (!ep_index(hsep) && _req->length == 0) {hsudc->ep0state = WAIT_FOR_SETUP;s3c_hsudc_complete_request(hsep, hsreq, 0);spin_unlock_irqrestore(&hsudc->lock, flags);return 0;}
/*如果端点的传输队列为空,可以直接向fifo中写数据*/if (list_empty(&hsep->queue) && !hsep->stopped) {offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;if (ep_is_in(hsep)) {csr = readl(hsudc->regs + offset);if (!(csr & S3C_ESR_TX_SUCCESS) &&(s3c_hsudc_write_fifo(hsep, hsreq) == 1)) /*这个request全部写完时,不用把这个request加入队列*/hsreq = NULL;} else {csr = readl(hsudc->regs + offset);if ((csr & S3C_ESR_RX_SUCCESS)&& (s3c_hsudc_read_fifo(hsep, hsreq) == 1))hsreq = NULL;}}
/*如果这个request没有传输完成,则需要把该request加入ep0传输队列*/if (hsreq)list_add_tail(&hsreq->queue, &hsep->queue);spin_unlock_irqrestore(&hsudc->lock, flags);return 0;
}
static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,struct s3c_hsudc_req *hsreq)
{u16 *buf;u32 max = ep_maxpacket(hsep);u32 count, length;bool is_last;void __iomem *fifo = hsep->fifo;buf = hsreq->req.buf + hsreq->req.actual;prefetch(buf);length = hsreq->req.length - hsreq->req.actual;length = min(length, max);hsreq->req.actual += length;writel(length, hsep->dev->regs + S3C_BWCR);for (count = 0; count < length; count += 2)writel(*buf++, fifo);if (count != max) {is_last = true;} else {if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)is_last = false;elseis_last = true;}if (is_last) {s3c_hsudc_complete_request(hsep, hsreq, 0);return 1;}return 0;
}
static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,struct s3c_hsudc_req *hsreq, int status)
{unsigned int stopped = hsep->stopped;struct s3c_hsudc *hsudc = hsep->dev;list_del_init(&hsreq->queue);hsreq->req.status = status;if (!ep_index(hsep)) {hsudc->ep0state = WAIT_FOR_SETUP;hsep->bEndpointAddress &= ~USB_DIR_IN;}hsep->stopped = 1;spin_unlock(&hsudc->lock);usb_gadget_giveback_request(&hsep->ep, &hsreq->req);spin_lock(&hsudc->lock);hsep->stopped = stopped;
}
void usb_gadget_giveback_request(struct usb_ep *ep,struct usb_request *req)
{if (likely(req->status == 0))usb_led_activity(USB_LED_EVENT_GADGET);trace_usb_gadget_giveback_request(ep, req, 0);req->complete(ep, req);
}
//linux-4.9\drivers\usb\gadget\composite.c
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
{struct usb_composite_dev *cdev;if (req->status || req->actual != req->length)DBG((struct usb_composite_dev *) ep->driver_data,"setup complete --> %d, %d/%d\n",req->status, req->actual, req->length);/** REVIST The same ep0 requests are shared with function drivers* so they don't have to maintain the same ->complete() stubs.** Because of that, we need to check for the validity of ->context* here, even though we know we've set it to something useful.*/if (!req->context)return;cdev = req->context;if (cdev->req == req)cdev->setup_pending = false;else if (cdev->os_desc_req == req)cdev->os_desc_pending = false;elseWARN(1, "unknown request %p\n", req);
}

在s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc) 处理ep0中断的函数中,会不断读取ep0端点下的request,将数据写入fifo,直到ep0端点内的usb_request list为空。

if (csr & S3C_EP0SR_TX_SUCCESS) {writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);if (ep_is_in(hsep)) {if (list_empty(&hsep->queue))return;hsreq = list_entry(hsep->queue.next,struct s3c_hsudc_req, queue);s3c_hsudc_write_fifo(hsep, hsreq);}}

write_fifo函数会把发送完成的request标记为发送结束。可以再次使用。

IN transfer 发送数据

static int gs_start_io(struct gs_port *port)
{struct list_head   *head = &port->read_pool;struct usb_ep      *ep = port->port_usb->out;int            status;unsigned     started;/* Allocate RX and TX I/O buffers.  We can't easily do this much* earlier (with GFP_KERNEL) because the requests are coupled to* endpoints, as are the packet sizes we'll be using.  Different* configurations may use different endpoints with a given port;* and high speed vs full speed changes packet sizes too.*/status = gs_alloc_requests(ep, head, gs_read_complete,&port->read_allocated);if (status)return status;status = gs_alloc_requests(port->port_usb->in, &port->write_pool,gs_write_complete, &port->write_allocated);if (status) {gs_free_requests(ep, head, &port->read_allocated);return status;}/* queue read requests */port->n_read = 0;started = gs_start_rx(port);/* unblock any pending writes into our circular buffer */if (started) {tty_wakeup(port->port.tty);} else {gs_free_requests(ep, head, &port->read_allocated);gs_free_requests(port->port_usb->in, &port->write_pool,&port->write_allocated);status = -EIO;}return status;
}static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
{struct gs_port *port = ep->driver_data;spin_lock(&port->port_lock);list_add(&req->list, &port->write_pool);port->write_started--;switch (req->status) {default:/* presumably a transient fault */pr_warning("%s: unexpected %s status %d\n",__func__, ep->name, req->status);/* FALL THROUGH */case 0:/* normal completion */gs_start_tx(port);break;case -ESHUTDOWN:/* disconnect */pr_vdebug("%s: %s shutdown\n", __func__, ep->name);break;}spin_unlock(&port->port_lock);
}static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
{struct gs_port *port = tty->driver_data;unsigned long  flags;pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",port->port_num, tty, count);spin_lock_irqsave(&port->port_lock, flags);if (count)count = gs_buf_put(&port->port_write_buf, buf, count);/* treat count == 0 as flush_chars() */if (port->port_usb)gs_start_tx(port);spin_unlock_irqrestore(&port->port_lock, flags);return count;
}static int gs_start_tx(struct gs_port *port)
/*
__releases(&port->port_lock)
__acquires(&port->port_lock)
*/
{struct list_head   *pool = &port->write_pool;struct usb_ep     *in;int         status = 0;bool            do_tty_wake = false;if (!port->port_usb)return status;in = port->port_usb->in;while (!port->write_busy && !list_empty(pool)) {struct usb_request  *req;int            len;if (port->write_started >= QUEUE_SIZE)break;req = list_entry(pool->next, struct usb_request, list);len = gs_send_packet(port, req->buf, in->maxpacket);if (len == 0) {wake_up_interruptible(&port->drain_wait);break;}do_tty_wake = true;req->length = len;list_del(&req->list);req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",port->port_num, len, *((u8 *)req->buf),*((u8 *)req->buf+1), *((u8 *)req->buf+2));/* Drop lock while we call out of driver; completions* could be issued while we do so.  Disconnection may* happen too; maybe immediately before we queue this!** NOTE that we may keep sending data for a while after* the TTY closed (dev->ioport->port_tty is NULL).*/port->write_busy = true;spin_unlock(&port->port_lock);status = usb_ep_queue(in, req, GFP_ATOMIC);spin_lock(&port->port_lock);port->write_busy = false;if (status) {pr_debug("%s: %s %s err %d\n",__func__, "queue", in->name, status);list_add(&req->list, pool);break;}port->write_started++;/* abort immediately after disconnect */if (!port->port_usb)break;}if (do_tty_wake && port->port.tty)tty_wakeup(port->port.tty);return status;
}

OUT transfer 接收数据

接收是被动的,首先准备好rx buffer,由中断填充,中断

static unsigned gs_start_rx(struct gs_port *port)
/*
__releases(&port->port_lock)
__acquires(&port->port_lock)
*/
{struct list_head   *pool = &port->read_pool;struct usb_ep      *out = port->port_usb->out;while (!list_empty(pool)) {struct usb_request *req;int            status;struct tty_struct    *tty;/* no more rx if closed */tty = port->port.tty;if (!tty)break;if (port->read_started >= QUEUE_SIZE)break;req = list_entry(pool->next, struct usb_request, list);list_del(&req->list);req->length = out->maxpacket;/* drop lock while we call out; the controller driver* may need to call us back (e.g. for disconnect)*/spin_unlock(&port->port_lock);status = usb_ep_queue(out, req, GFP_ATOMIC);spin_lock(&port->port_lock);if (status) {pr_debug("%s: %s %s err %d\n",__func__, "queue", out->name, status);list_add(&req->list, pool);break;}port->read_started++;/* abort immediately after disconnect */if (!port->port_usb)break;}return port->read_started;
}
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
{struct gs_port *port = ep->driver_data;/* Queue all received data until the tty layer is ready for it. */spin_lock(&port->port_lock);list_add_tail(&req->list, &port->read_queue);tasklet_schedule(&port->push);spin_unlock(&port->port_lock);
}

USB gadget(1)----controller driver相关推荐

  1. USB gadget(1)----gadget driver

    USB gadget----gadget driver USB gadget Driver USB gadget(1)----controller driver中,匹配gadget driver时,调 ...

  2. USB Gadget iMX6U LL开发板模拟U盘

    IMX6ULL开发板模拟U盘 Linux版本:4.1.15 使用开发板:IMX6ULL 编译环境:Ubuntu 14.04 步骤如下: 1. 首先配置内核 进入 Device Drivers ---& ...

  3. UDC (usb device controller) Framework - USB gadget driver framework

    http://blog.csdn.net/u011279649/article/details/11059433 USB gadget driver的框架可分为三部分:UDC-core, compos ...

  4. 关于kernel2.6中USB host controller driver 的问题

    2.6在s3c2410上usb host不工作的直接结果就是提示110错误:  usb 1-1: device descriptor read/64, error -110 追踪错误代码,我们来看看能 ...

  5. USB gadget driver framework

    USB gadget driver的框架可分为三部分:UDC-core, composite.c and android.c,其中 composite.c是核心,其他两部分都要bind 到 compo ...

  6. usb gadget driver 之一UDC driver

    linux内核版本是2.6.32.2 1.platform_driver_register(&udc_driver_24x0); UDC驱动是作为platform driver向platfor ...

  7. linux选择usb功能,USB gadget设备驱动解析(1)——功能体验

    利用Linux USB gadget设备驱动可以实现一些比较有意思的功能,举两个例子: 1.一个嵌入式产品中的某个存储设备,或是一个存储设备的某个分区,可以作为一个U盘被PC:设别,从而非常方便的完成 ...

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

    上个小patch吧... 关于昨天的usb枚举失败(获取配置描述符失败) 简要描述下: 1. 我的gadget配置成了usb3.2版本,  (设置成1.0, 2.0也遇到一些问题, 暂表不论) Pro ...

  9. linux usb gadget 日志

    1,USB 协议入门 几种USB控制器类型:OHCI,UHCI,EHCI,XHCI 遇到过一些关于USB的东西(如下),一直没搞明白什么USB1.0/1.1/2.0/3.0之类的,当然我知道它们的各自 ...

最新文章

  1. linux如何获取raw中的文件路径,如何使用Linux获取Touchscreen Rawdata的坐标
  2. HTML5 之 简单汇总
  3. 曲苑杂坛--修改数据库名和文件组名
  4. 玩转oracle 11g(42):增加表空间
  5. [译] Architecture Components 之 Adding Components to your Project
  6. 【实验记录】Fashion-Mnist分类实验记录
  7. python自带sqlite_python内置的sqlite3模块,使用其内置数据库
  8. [C++]##(两个井号)和#(一个井号)都是什么意思
  9. springboot 微服务_Spring Boot在微服务中的最佳实践
  10. 快速启动工具入门——以Launchy为例(二)
  11. 工作频率对系统功率、穿透能力、设备体积、系统性能的影响
  12. 在angular2项目里使用ng-zorro的icon
  13. 电脑如何控制点击android手机,安卓手机怎么控制电脑?红米手机远程控制操作电脑方法...
  14. 中移物联网联合上研院推出快速定位服务,助力智慧物联网发展
  15. 二叉树前序遍历--递归
  16. PTA-C理论B类题库6-3使用函数求最大公约数(辗转相除法的实现)
  17. 新型冠状病毒的持续了解
  18. Briefings in Bioinformatics2021 | 药物挖掘分子设计--生成模型综述
  19. 正负用c语言表示,用C表示负数?
  20. 二十六个英语字母相关的公司商标

热门文章

  1. Intel新CEO敲定,斯旺终”转正“ 1
  2. webrtc详细教程
  3. 软件测试学习教程(一)-学习路线图
  4. 使用python批量将word转为pdf
  5. Working with Errors in Go 1.13
  6. 帮助你拿到offer的金融测试面试题
  7. 赋范空间与巴拿赫空间
  8. ios 弱网 数据丢失_在不丢失数据的情况下将您的iOS设备与新计算机同步
  9. SM2加密解决java与iOS端加解密不配套问题
  10. python怎么取数字区间_python – 如何检查数字是否在一个区间内