usb device driver 分为三部分, usb controller driver, gadget driver 和function driver.
gadget driver和funciton driver中间还有个composite driver 层。
本文从controller driver 开始, 以一个串口gadget 为例进行分析。
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;
else
gadget->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所做的工作如下:
每个端点在物理上主要对应一段缓冲区,其他端点相关的还有属性描述信息区域,最大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);
else
snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
INIT_LIST_HEAD(&hsep->queue); //controller usb request queue
INIT_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;
else
hsep->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);
else
s3c_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);
}
}
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;
else
config = 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);
else
otg_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");
else
VDBG(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;
}
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);
} else
return -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端点上报现在的状态*/
}
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;
else
WARN(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;
else
is_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;
else
WARN(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标记为发送结束。可以再次使用。
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;
}
接收是被动的,首先准备好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);
}