[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 1/3] PVUSB update and bugfix: frontend part
Signed-off-by: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx> diff -r 4ac3e1d6605c -r 3be939975ad6 drivers/xen/usbfront/usbfront-dbg.c --- a/drivers/xen/usbfront/usbfront-dbg.c Tue Sep 29 11:23:06 2009 +0100 +++ b/drivers/xen/usbfront/usbfront-dbg.c Tue Oct 06 15:18:27 2009 +0900 @@ -60,7 +60,7 @@ spin_lock_irqsave(&info->lock, flags); - temp = scnprintf (next, size, + temp = scnprintf(next, size, "bus %s, device %s\n" "%s\n" "xenhcd, hcd state %d\n", @@ -74,7 +74,8 @@ #ifdef XENHCD_STATS temp = scnprintf(next, size, "complete %ld unlink %ld ring_full %ld\n", - info->stats.complete, info->stats.unlink, info->stats.ring_full); + info->stats.complete, info->stats.unlink, + info->stats.ring_full); size -= temp; next += temp; #endif diff -r 4ac3e1d6605c -r 3be939975ad6 drivers/xen/usbfront/usbfront-hcd.c --- a/drivers/xen/usbfront/usbfront-hcd.c Tue Sep 29 11:23:06 2009 +0100 +++ b/drivers/xen/usbfront/usbfront-hcd.c Tue Oct 06 15:18:27 2009 +0900 @@ -54,7 +54,7 @@ unsigned long flags; spin_lock_irqsave(&info->lock, flags); - if (HC_IS_RUNNING(info_to_hcd(info)->state)) { + if (likely(HC_IS_RUNNING(info_to_hcd(info)->state))) { timer_action_done(info, TIMER_RING_WATCHDOG); xenhcd_giveback_unlinked_urbs(info); xenhcd_kick_pending_urbs(info); @@ -70,9 +70,10 @@ struct usbfront_info *info = hcd_to_info(hcd); spin_lock_init(&info->lock); - INIT_LIST_HEAD(&info->pending_urbs); - INIT_LIST_HEAD(&info->inprogress_urbs); - INIT_LIST_HEAD(&info->unlinked_urbs); + INIT_LIST_HEAD(&info->pending_submit_list); + INIT_LIST_HEAD(&info->pending_unlink_list); + INIT_LIST_HEAD(&info->in_progress_list); + INIT_LIST_HEAD(&info->giveback_waiting_list); init_timer(&info->watchdog); info->watchdog.function = xenhcd_watchdog; info->watchdog.data = (unsigned long) info; @@ -101,70 +102,14 @@ del_timer_sync(&info->watchdog); remove_debug_file(info); spin_lock_irq(&info->lock); - /* - * TODO: port power off, cancel all urbs. - */ - - if (HC_IS_RUNNING(hcd->state)) - hcd->state = HC_STATE_HALT; + /* cancel all urbs */ + hcd->state = HC_STATE_HALT; + xenhcd_cancel_all_enqueued_urbs(info); + xenhcd_giveback_unlinked_urbs(info); spin_unlock_irq(&info->lock); } /* - * TODO: incomplete suspend/resume functions! - */ -#if 0 -#ifdef CONFIG_PM -/* - * suspend running HC - */ -static int xenhcd_suspend(struct usb_hcd *hcd, pm_message_t message) -{ - struct usbfront_info *info = hcd_to_info(hcd); - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&info->lock, flags); - if (hcd->state != HC_STATE_SUSPENDED) { - ret = -EINVAL; - goto done; - } - - /* - * TODO: - * canceling all transfer, clear all hc queue, - * stop kthread, - */ - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); -done: - spin_unlock_irqrestore(&info->lock, flags); - - return ret; -} - -/* - * resume HC - */ -static int xenhcd_resume(struct usb_hcd *hcd) -{ - struct usbfront_info *info = hcd_to_info(hcd); - int ret = -EINVAL; - - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* - * TODO: - * re-init HC. - * resume all roothub ports. - */ - - return ret; -} -#endif -#endif - -/* * called as .urb_enqueue() * non-error returns are promise to giveback the urb later */ @@ -197,11 +142,6 @@ /* * called as .urb_dequeue() - * - * just mark the urb as unlinked - * if the urb is in pending_urbs, move to unlinked_urbs - * TODO: - * canceling the urb transfer in backend */ static int xenhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) @@ -234,46 +174,54 @@ return 0; } -/* - * TODO: - * suspend/resume whole hcd and roothub - */ static const char hcd_name[] = "xen_hcd"; -struct hc_driver usbfront_hc_driver = { +struct hc_driver xen_usb20_hc_driver = { .description = hcd_name, - .product_desc = DRIVER_DESC, + .product_desc = "Xen USB2.0 Virtual Host Controller", .hcd_priv_size = sizeof(struct usbfront_info), .flags = HCD_USB2, - /* - * basic HC lifecycle operations - */ + /* basic HC lifecycle operations */ .reset = xenhcd_setup, .start = xenhcd_run, .stop = xenhcd_stop, -#if 0 -#ifdef CONFIG_PM - .suspend = xenhcd_suspend, - .resume = xenhcd_resume, -#endif -#endif - /* - * managing urb I/O - */ + + /* managing urb I/O */ .urb_enqueue = xenhcd_urb_enqueue, .urb_dequeue = xenhcd_urb_dequeue, .get_frame_number = xenhcd_get_frame, - /* - * root hub operations - */ + /* root hub operations */ .hub_status_data = xenhcd_hub_status_data, .hub_control = xenhcd_hub_control, -#if 0 #ifdef CONFIG_PM .bus_suspend = xenhcd_bus_suspend, .bus_resume = xenhcd_bus_resume, #endif +}; + +struct hc_driver xen_usb11_hc_driver = { + .description = hcd_name, + .product_desc = "Xen USB1.1 Virtual Host Controller", + .hcd_priv_size = sizeof(struct usbfront_info), + .flags = HCD_USB11, + + /* basic HC lifecycle operations */ + .reset = xenhcd_setup, + .start = xenhcd_run, + .stop = xenhcd_stop, + + /* managing urb I/O */ + .urb_enqueue = xenhcd_urb_enqueue, + .urb_dequeue = xenhcd_urb_dequeue, + .get_frame_number = xenhcd_get_frame, + + /* root hub operations */ + .hub_status_data = xenhcd_hub_status_data, + .hub_control = xenhcd_hub_control, +#ifdef CONFIG_PM + .bus_suspend = xenhcd_bus_suspend, + .bus_resume = xenhcd_bus_resume, #endif }; diff -r 4ac3e1d6605c -r 3be939975ad6 drivers/xen/usbfront/usbfront-hub.c --- a/drivers/xen/usbfront/usbfront-hub.c Tue Sep 29 11:23:06 2009 +0100 +++ b/drivers/xen/usbfront/usbfront-hub.c Tue Oct 06 15:18:27 2009 +0900 @@ -50,15 +50,16 @@ { int port; - port = portnum -1; + port = portnum - 1; if (info->ports[port].status & USB_PORT_STAT_POWER) { switch (info->devices[port].speed) { case USB_SPEED_UNKNOWN: - info->ports[port].status &= ~(USB_PORT_STAT_CONNECTION | - USB_PORT_STAT_ENABLE | - USB_PORT_STAT_LOW_SPEED | - USB_PORT_STAT_HIGH_SPEED | - USB_PORT_STAT_SUSPEND); + info->ports[port].status &= + ~(USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_SUSPEND); break; case USB_SPEED_LOW: info->ports[port].status |= USB_PORT_STAT_CONNECTION; @@ -86,6 +87,9 @@ { int port; + if (portnum < 1 || portnum > info->rh_numports) + return; /* invalid port number */ + port = portnum - 1; if (info->devices[port].speed != speed) { switch (speed) { @@ -107,30 +111,6 @@ } } -void rhport_disconnect(struct usbfront_info *info, int portnum) -{ - rhport_connect(info, portnum, USB_SPEED_UNKNOWN); -} - -void xenhcd_rhport_state_change(struct usbfront_info *info, - int portnum, enum usb_device_speed speed) -{ - int changed = 0; - unsigned long flags; - - if (portnum < 1 || portnum > info->rh_numports) - return; /* invalid port number */ - - spin_lock_irqsave(&info->lock, flags); - rhport_connect(info, portnum, speed); - if (info->ports[portnum-1].c_connection) - changed = 1; - spin_unlock_irqrestore(&info->lock, flags); - - if (changed) - usb_hcd_poll_rh_status(info_to_hcd(info)); -} - /* * SetPortFeature(PORT_SUSPENDED) */ @@ -214,7 +194,7 @@ { int port; - port = portnum -1; + port = portnum - 1; info->ports[port].status &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); @@ -227,57 +207,51 @@ info->ports[port].timeout = jiffies + msecs_to_jiffies(10); } -#if 0 #ifdef CONFIG_PM static int xenhcd_bus_suspend(struct usb_hcd *hcd) { struct usbfront_info *info = hcd_to_info(hcd); + int ret = 0; int i, ports; ports = info->rh_numports; spin_lock_irq(&info->lock); - - if (HC_IS_RUNNING(hcd->state)) { - /* - * TODO: - * clean queue, - * stop all transfers, - * ... - */ - hcd->state = HC_STATE_QUIESCING; + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &info->flags)) + ret = -ESHUTDOWN; + else if (!info->dead) { + /* suspend any active ports*/ + for (i = 1; i <= ports; i++) + rhport_suspend(info, i); } - - /* suspend any active ports*/ - for (i = 1; i <= ports; i++) { - rhport_suspend(info, i); - } + spin_unlock_irq(&info->lock); del_timer_sync(&info->watchdog); - spin_unlock_irq(&info->lock); - - return 0; + return ret; } static int xenhcd_bus_resume(struct usb_hcd *hcd) { struct usbfront_info *info = hcd_to_info(hcd); + int ret = 0; int i, ports; ports = info->rh_numports; spin_lock_irq(&info->lock); - /* resume any suspended ports*/ - for (i = 1; i <= ports; i++) { - rhport_resume(info, i); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &info->flags)) + ret = -ESHUTDOWN; + else if (!info->dead) { + /* resume any suspended ports*/ + for (i = 1; i <= ports; i++) + rhport_resume(info, i); } - hcd->state = HC_STATE_RUNNING; spin_unlock_irq(&info->lock); - return 0; + + return ret; } #endif -#endif static void xenhcd_hub_descriptor(struct usbfront_info *info, struct usb_hub_descriptor *desc) @@ -295,8 +269,8 @@ desc->bDescLength = 7 + 2 * temp; /* bitmaps for DeviceRemovable and PortPwrCtrlMask */ - memset (&desc->bitmap[0], 0, temp); - memset (&desc->bitmap[temp], 0xff, temp); + memset(&desc->bitmap[0], 0, temp); + memset(&desc->bitmap[temp], 0xff, temp); /* per-port over current reporting and no power switching */ temp = 0x000a; @@ -380,11 +354,6 @@ int i; int changed = 0; -#ifdef USBFRONT_DEBUG - WPRINTK("xenusb_hub_control(typeReq %x wValue %x wIndex %x)\n", - typeReq, wValue, wIndex); -#endif - spin_lock_irqsave(&info->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -394,7 +363,7 @@ if (!wIndex || wIndex > ports) goto error; - switch(wValue) { + switch (wValue) { case USB_PORT_FEAT_SUSPEND: rhport_resume(info, wIndex); break; @@ -414,7 +383,7 @@ break; case GetHubDescriptor: xenhcd_hub_descriptor(info, - (struct usb_hub_descriptor*) buf); + (struct usb_hub_descriptor *) buf); break; case GetHubStatus: /* always local power supply good and no over-current exists. */ @@ -444,7 +413,7 @@ info->devices[wIndex].status = USB_STATE_DEFAULT; } - switch(info->devices[wIndex].speed) { + switch (info->devices[wIndex].speed) { case USB_SPEED_LOW: info->ports[wIndex].status |= USB_PORT_STAT_LOW_SPEED; break; @@ -466,7 +435,7 @@ if (!wIndex || wIndex > ports) goto error; - switch(wValue) { + switch (wValue) { case USB_PORT_FEAT_POWER: rhport_power_on(info, wIndex); break; @@ -477,9 +446,8 @@ rhport_suspend(info, wIndex); break; default: - if ((info->ports[wIndex-1].status & USB_PORT_STAT_POWER) != 0) { + if ((info->ports[wIndex-1].status & USB_PORT_STAT_POWER) != 0) info->ports[wIndex-1].status |= (1 << wValue); - } } break; @@ -491,9 +459,8 @@ /* check status for each port */ for (i = 0; i < ports; i++) { - if (info->ports[i].status & PORT_C_MASK) { + if (info->ports[i].status & PORT_C_MASK) changed = 1; - } } if (changed) usb_hcd_poll_rh_status(hcd); diff -r 4ac3e1d6605c -r 3be939975ad6 drivers/xen/usbfront/usbfront-q.c --- a/drivers/xen/usbfront/usbfront-q.c Tue Sep 29 11:23:06 2009 +0100 +++ b/drivers/xen/usbfront/usbfront-q.c Tue Oct 06 15:18:27 2009 +0900 @@ -50,13 +50,13 @@ struct urb_priv *urbp; urbp = kmem_cache_zalloc(xenhcd_urbp_cachep, GFP_ATOMIC); - if (!urbp) { + if (!urbp) return NULL; - } urbp->urb = urb; urb->hcpriv = urbp; urbp->req_id = ~0; + urbp->unlink_req_id = ~0; INIT_LIST_HEAD(&urbp->list); return urbp; @@ -73,7 +73,7 @@ { unsigned long free; free = info->shadow_free; - BUG_ON(free > USB_RING_SIZE); + BUG_ON(free >= USB_URB_RING_SIZE); info->shadow_free = info->shadow[free].req.id; info->shadow[free].req.id = (unsigned int)0x0fff; /* debug */ return free; @@ -90,7 +90,7 @@ static inline int count_pages(void *addr, int length) { unsigned long start = (unsigned long) addr >> PAGE_SHIFT; - unsigned long end = (unsigned long) (addr + length + PAGE_SIZE -1) >> PAGE_SHIFT; + unsigned long end = (unsigned long) (addr + length + PAGE_SIZE - 1) >> PAGE_SHIFT; return end - start; } @@ -108,7 +108,7 @@ len = length; - for(i = 0;i < nr_pages;i++){ + for (i = 0; i < nr_pages; i++) { BUG_ON(!len); page = virt_to_page(addr); @@ -116,7 +116,7 @@ offset = offset_in_page(addr); bytes = PAGE_SIZE - offset; - if(bytes > len) + if (bytes > len) bytes = len; ref = gnttab_claim_grant_reference(gref_head); @@ -132,7 +132,7 @@ } static int map_urb_for_request(struct usbfront_info *info, struct urb *urb, - usbif_request_t *req) + usbif_urb_request_t *req) { grant_ref_t gref_head; int nr_buff_pages = 0; @@ -175,14 +175,12 @@ req->u.isoc.start_frame = urb->start_frame; req->u.isoc.number_of_packets = urb->number_of_packets; req->u.isoc.nr_frame_desc_segs = nr_isodesc_pages; - /* - * urb->number_of_packets must be > 0 - */ + /* urb->number_of_packets must be > 0 */ if (unlikely(urb->number_of_packets <= 0)) BUG(); xenhcd_gnttab_map(info, &urb->iso_frame_desc[0], - sizeof(struct usb_iso_packet_descriptor) * urb->number_of_packets, - &gref_head, &req->seg[nr_buff_pages], nr_isodesc_pages, 0); + sizeof(struct usb_iso_packet_descriptor) * urb->number_of_packets, + &gref_head, &req->seg[nr_buff_pages], nr_isodesc_pages, 0); gnttab_free_grant_references(gref_head); break; case PIPE_INTERRUPT: @@ -213,9 +211,12 @@ for (i = 0; i < nr_segs; i++) gnttab_end_foreign_access(shadow->req.seg[i].gref, 0UL); + + shadow->req.nr_buffer_segs = 0; + shadow->req.u.isoc.nr_frame_desc_segs = 0; } -static void xenhcd_giveback_urb(struct usbfront_info *info, struct urb *urb) +static void xenhcd_giveback_urb(struct usbfront_info *info, struct urb *urb, int status) __releases(info->lock) __acquires(info->lock) { @@ -228,6 +229,9 @@ case -ENOENT: COUNT(info->stats.unlink); break; + case -EINPROGRESS: + urb->status = status; + /* falling through */ default: COUNT(info->stats.complete); } @@ -238,28 +242,35 @@ static inline int xenhcd_do_request(struct usbfront_info *info, struct urb_priv *urbp) { - usbif_request_t *ring_req; + usbif_urb_request_t *req; struct urb *urb = urbp->urb; uint16_t id; int notify; int ret = 0; - ring_req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); + req = RING_GET_REQUEST(&info->urb_ring, info->urb_ring.req_prod_pvt); id = get_id_from_freelist(info); - ring_req->id = id; + req->id = id; - ret = map_urb_for_request(info, urb, ring_req); - if (ret < 0) { - add_id_to_freelist(info, id); - return ret; + if (unlikely(urbp->unlinked)) { + req->u.unlink.unlink_id = urbp->req_id; + req->pipe = usbif_setunlink_pipe(usbif_setportnum_pipe( + urb->pipe, urb->dev->portnum)); + urbp->unlink_req_id = id; + } else { + ret = map_urb_for_request(info, urb, req); + if (ret < 0) { + add_id_to_freelist(info, id); + return ret; + } + urbp->req_id = id; } - info->ring.req_prod_pvt++; + info->urb_ring.req_prod_pvt++; info->shadow[id].urb = urb; - info->shadow[id].req = *ring_req; - urbp->req_id = id; + info->shadow[id].req = *req; - RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->ring, notify); + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->urb_ring, notify); if (notify) notify_remote_via_irq(info->irq); @@ -271,19 +282,19 @@ struct urb_priv *urbp; int ret; - while (!list_empty(&info->pending_urbs)) { - if (RING_FULL(&info->ring)) { + while (!list_empty(&info->pending_submit_list)) { + if (RING_FULL(&info->urb_ring)) { COUNT(info->stats.ring_full); timer_action(info, TIMER_RING_WATCHDOG); goto done; } - urbp = list_entry(info->pending_urbs.next, struct urb_priv, list); + urbp = list_entry(info->pending_submit_list.next, struct urb_priv, list); ret = xenhcd_do_request(info, urbp); if (ret == 0) - list_move_tail(&urbp->list, &info->inprogress_urbs); + list_move_tail(&urbp->list, &info->in_progress_list); else - xenhcd_giveback_urb(info, urbp->urb); + xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN); } timer_action_done(info, TIMER_SCAN_PENDING_URBS); @@ -291,12 +302,41 @@ return; } +/* + * caller must lock info->lock + */ +static void xenhcd_cancel_all_enqueued_urbs(struct usbfront_info *info) +{ + struct urb_priv *urbp, *tmp; + + list_for_each_entry_safe(urbp, tmp, &info->in_progress_list, list) { + if (!urbp->unlinked) { + xenhcd_gnttab_done(&info->shadow[urbp->req_id]); + barrier(); + if (urbp->urb->status == -EINPROGRESS) /* not dequeued */ + xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN); + else /* dequeued */ + xenhcd_giveback_urb(info, urbp->urb, urbp->urb->status); + } + info->shadow[urbp->req_id].urb = NULL; + } + + list_for_each_entry_safe(urbp, tmp, &info->pending_submit_list, list) { + xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN); + } + + return; +} + +/* + * caller must lock info->lock + */ static void xenhcd_giveback_unlinked_urbs(struct usbfront_info *info) { struct urb_priv *urbp, *tmp; - list_for_each_entry_safe(urbp, tmp, &info->unlinked_urbs, list) { - xenhcd_giveback_urb(info, urbp->urb); + list_for_each_entry_safe(urbp, tmp, &info->giveback_waiting_list, list) { + xenhcd_giveback_urb(info, urbp->urb, urbp->urb->status); } } @@ -304,22 +344,22 @@ { int ret = 0; - if (RING_FULL(&info->ring)) { - list_add_tail(&urbp->list, &info->pending_urbs); + if (RING_FULL(&info->urb_ring)) { + list_add_tail(&urbp->list, &info->pending_submit_list); COUNT(info->stats.ring_full); timer_action(info, TIMER_RING_WATCHDOG); goto done; } - if (!list_empty(&info->pending_urbs)) { - list_add_tail(&urbp->list, &info->pending_urbs); + if (!list_empty(&info->pending_submit_list)) { + list_add_tail(&urbp->list, &info->pending_submit_list); timer_action(info, TIMER_SCAN_PENDING_URBS); goto done; } ret = xenhcd_do_request(info, urbp); if (ret == 0) - list_add_tail(&urbp->list, &info->inprogress_urbs); + list_add_tail(&urbp->list, &info->in_progress_list); done: return ret; @@ -327,26 +367,47 @@ static int xenhcd_unlink_urb(struct usbfront_info *info, struct urb_priv *urbp) { + int ret = 0; + + /* already unlinked? */ if (urbp->unlinked) return -EBUSY; + urbp->unlinked = 1; - /* if the urb is in pending_urbs */ + /* the urb is still in pending_submit queue */ if (urbp->req_id == ~0) { - list_move_tail(&urbp->list, &info->unlinked_urbs); + list_move_tail(&urbp->list, &info->giveback_waiting_list); timer_action(info, TIMER_SCAN_PENDING_URBS); + goto done; } - /* TODO: send cancel request to backend */ + /* send unlink request to backend */ + if (RING_FULL(&info->urb_ring)) { + list_move_tail(&urbp->list, &info->pending_unlink_list); + COUNT(info->stats.ring_full); + timer_action(info, TIMER_RING_WATCHDOG); + goto done; + } - return 0; + if (!list_empty(&info->pending_unlink_list)) { + list_move_tail(&urbp->list, &info->pending_unlink_list); + timer_action(info, TIMER_SCAN_PENDING_URBS); + goto done; + } + + ret = xenhcd_do_request(info, urbp); + if (ret == 0) + list_move_tail(&urbp->list, &info->in_progress_list); + +done: + return ret; } -static int xenhcd_end_submit_urb(struct usbfront_info *info) +static int xenhcd_urb_request_done(struct usbfront_info *info) { - usbif_response_t *ring_res; + usbif_urb_response_t *res; struct urb *urb; - struct urb_priv *urbp; RING_IDX i, rp; uint16_t id; @@ -354,33 +415,35 @@ unsigned long flags; spin_lock_irqsave(&info->lock, flags); - rp = info->ring.sring->rsp_prod; + + rp = info->urb_ring.sring->rsp_prod; rmb(); /* ensure we see queued responses up to "rp" */ - for (i = info->ring.rsp_cons; i != rp; i++) { - ring_res = RING_GET_RESPONSE(&info->ring, i); - id = ring_res->id; - xenhcd_gnttab_done(&info->shadow[id]); - urb = info->shadow[id].urb; - barrier(); + for (i = info->urb_ring.rsp_cons; i != rp; i++) { + res = RING_GET_RESPONSE(&info->urb_ring, i); + id = res->id; + + if (likely(usbif_pipesubmit(info->shadow[id].req.pipe))) { + xenhcd_gnttab_done(&info->shadow[id]); + urb = info->shadow[id].urb; + barrier(); + if (likely(urb)) { + urb->actual_length = res->actual_length; + urb->error_count = res->error_count; + urb->start_frame = res->start_frame; + barrier(); + xenhcd_giveback_urb(info, urb, res->status); + } + } + add_id_to_freelist(info, id); + } + info->urb_ring.rsp_cons = i; - urbp = (struct urb_priv *)urb->hcpriv; - if (likely(!urbp->unlinked)) { - urb->status = ring_res->status; - urb->actual_length = ring_res->actual_length; - urb->error_count = ring_res->error_count; - urb->start_frame = ring_res->start_frame; - } - barrier(); - xenhcd_giveback_urb(info, urb); - } - info->ring.rsp_cons = i; - - if (i != info->ring.req_prod_pvt) - RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do); + if (i != info->urb_ring.req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do); else - info->ring.sring->rsp_event = i + 1; + info->urb_ring.sring->rsp_event = i + 1; spin_unlock_irqrestore(&info->lock, flags); @@ -389,6 +452,61 @@ return more_to_do; } +static int xenhcd_conn_notify(struct usbfront_info *info) +{ + usbif_conn_response_t *res; + usbif_conn_request_t *req; + RING_IDX rc, rp; + uint16_t id; + uint8_t portnum, speed; + int more_to_do = 0; + int notify; + int port_changed = 0; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + + rc = info->conn_ring.rsp_cons; + rp = info->conn_ring.sring->rsp_prod; + rmb(); /* ensure we see queued responses up to "rp" */ + + while (rc != rp) { + res = RING_GET_RESPONSE(&info->conn_ring, rc); + id = res->id; + portnum = res->portnum; + speed = res->speed; + info->conn_ring.rsp_cons = ++rc; + + rhport_connect(info, portnum, speed); + if (info->ports[portnum-1].c_connection) + port_changed = 1; + + barrier(); + + req = RING_GET_REQUEST(&info->conn_ring, info->conn_ring.req_prod_pvt); + req->id = id; + info->conn_ring.req_prod_pvt++; + } + + if (rc != info->conn_ring.req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(&info->conn_ring, more_to_do); + else + info->conn_ring.sring->rsp_event = rc + 1; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify); + if (notify) + notify_remote_via_irq(info->irq); + + spin_unlock_irqrestore(&info->lock, flags); + + if (port_changed) + usb_hcd_poll_rh_status(info_to_hcd(info)); + + cond_resched(); + + return more_to_do; +} + int xenhcd_schedule(void *arg) { struct usbfront_info *info = (struct usbfront_info *) arg; @@ -400,7 +518,10 @@ info->waiting_resp = 0; smp_mb(); - if (xenhcd_end_submit_urb(info)) + if (xenhcd_urb_request_done(info)) + info->waiting_resp = 1; + + if (xenhcd_conn_notify(info)) info->waiting_resp = 1; } diff -r 4ac3e1d6605c -r 3be939975ad6 drivers/xen/usbfront/usbfront.h --- a/drivers/xen/usbfront/usbfront.h Tue Sep 29 11:23:06 2009 +0100 +++ b/drivers/xen/usbfront/usbfront.h Tue Oct 06 15:18:27 2009 +0900 @@ -66,8 +66,6 @@ #include "../../usb/core/hcd.h" #include "../../usb/core/hub.h" -#define DRIVER_DESC "Xen USB2.0 Virtual Host Controller driver (usbfront)" - static inline struct usbfront_info *hcd_to_info(struct usb_hcd *hcd) { return (struct usbfront_info *) (hcd->hcd_priv); @@ -75,17 +73,16 @@ static inline struct usb_hcd *info_to_hcd(struct usbfront_info *info) { - return container_of ((void *) info, struct usb_hcd, hcd_priv); + return container_of((void *) info, struct usb_hcd, hcd_priv); } -/* - * Private per-URB data - */ +/* Private per-URB data */ struct urb_priv { struct list_head list; struct urb *urb; - int req_id; /* RING_REQUEST id */ - unsigned unlinked:1; /* dequeued urb just marked */ + int req_id; /* RING_REQUEST id for submitting */ + int unlink_req_id; /* RING_REQUEST id for unlinking */ + unsigned unlinked:1; /* dequeued marker */ }; /* virtual roothub port status */ @@ -105,7 +102,7 @@ /* RING request shadow */ struct usb_shadow { - usbif_request_t req; + usbif_urb_request_t req; struct urb *urb; }; @@ -117,62 +114,46 @@ }; struct usbfront_info { - /* - * Virtual Host Controller has 3 queues. - * - * pending_urbs: - * If xenhcd_urb_enqueue() called in RING_FULL state, - * the enqueued urbs are added to this queue, and waits - * to be sent to the backend. - * - * inprogress_urbs: - * After xenhcd_urb_enqueue() called and RING_REQUEST sent, - * the urbs are added to this queue and waits for RING_RESPONSE. - * - * unlinked_urbs: - * When xenhcd_urb_dequeue() called, if the dequeued urb is - * listed in pending_urbs, that urb is moved to this queue - * and waits to be given back to the USB core. - */ - struct list_head pending_urbs; - struct list_head inprogress_urbs; - struct list_head unlinked_urbs; + /* Virtual Host Controller has 4 urb queues */ + struct list_head pending_submit_list; + struct list_head pending_unlink_list; + struct list_head in_progress_list; + struct list_head giveback_waiting_list; + spinlock_t lock; - /* - * timer function that kick pending_urbs and unlink_urbs. - */ + /* timer that kick pending and giveback waiting urbs */ + struct timer_list watchdog; unsigned long actions; - struct timer_list watchdog; - /* - * Virtual roothub: - * Emulates the hub ports and the attached devices status. - * USB_MAXCHILDREN is defined (16) in include/linux/usb.h - */ + /* virtual root hub */ int rh_numports; struct rhport_status ports[USB_MAXCHILDREN]; struct vdevice_status devices[USB_MAXCHILDREN]; + /* Xen related staff */ + struct xenbus_device *xbdev; + int urb_ring_ref; + int conn_ring_ref; + usbif_urb_front_ring_t urb_ring; + usbif_conn_front_ring_t conn_ring; + + unsigned int irq; /* event channel */ + struct usb_shadow shadow[USB_URB_RING_SIZE]; + unsigned long shadow_free; + + /* RING_RESPONSE thread */ + struct task_struct *kthread; + wait_queue_head_t wq; + unsigned int waiting_resp; + + /* xmit statistics */ #ifdef XENHCD_STATS struct xenhcd_stats stats; #define COUNT(x) do { (x)++; } while (0) #else #define COUNT(x) do {} while (0) #endif - - /* Xen related staff */ - struct xenbus_device *xbdev; - int ring_ref; - usbif_front_ring_t ring; - unsigned int irq; - struct usb_shadow shadow[USB_RING_SIZE]; - unsigned long shadow_free; - - /* RING_RESPONSE thread */ - struct task_struct *kthread; - wait_queue_head_t wq; - unsigned int waiting_resp; }; #define XENHCD_RING_JIFFIES (HZ/200) @@ -199,7 +180,7 @@ if (!test_and_set_bit(action, &info->actions)) { unsigned long t; - switch(action) { + switch (action) { case TIMER_RING_WATCHDOG: t = XENHCD_RING_JIFFIES; break; @@ -211,6 +192,12 @@ } } +extern struct kmem_cache *xenhcd_urbp_cachep; +extern struct hc_driver xen_usb20_hc_driver; +extern struct hc_driver xen_usb11_hc_driver; irqreturn_t xenhcd_int(int irq, void *dev_id, struct pt_regs *ptregs); +void xenhcd_rhport_state_change(struct usbfront_info *info, + int port, enum usb_device_speed speed); +int xenhcd_schedule(void *arg); #endif /* __XEN_USBFRONT_H__ */ diff -r 4ac3e1d6605c -r 3be939975ad6 drivers/xen/usbfront/xenbus.c --- a/drivers/xen/usbfront/xenbus.c Tue Sep 29 11:23:06 2009 +0100 +++ b/drivers/xen/usbfront/xenbus.c Tue Oct 06 15:18:27 2009 +0900 @@ -45,50 +45,70 @@ #include "usbfront.h" -extern struct hc_driver usbfront_hc_driver; -extern struct kmem_cache *xenhcd_urbp_cachep; -extern void xenhcd_rhport_state_change(struct usbfront_info *info, - int port, enum usb_device_speed speed); -extern int xenhcd_schedule(void *arg); - #define GRANT_INVALID_REF 0 -static void usbif_free(struct usbfront_info *info) +static void destroy_rings(struct usbfront_info *info) { - if (info->ring_ref != GRANT_INVALID_REF) { - gnttab_end_foreign_access(info->ring_ref, - (unsigned long)info->ring.sring); - info->ring_ref = GRANT_INVALID_REF; - info->ring.sring = NULL; - } if (info->irq) unbind_from_irqhandler(info->irq, info); info->irq = 0; + + if (info->urb_ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->urb_ring_ref, + (unsigned long)info->urb_ring.sring); + info->urb_ring_ref = GRANT_INVALID_REF; + } + info->urb_ring.sring = NULL; + + if (info->conn_ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->conn_ring_ref, + (unsigned long)info->conn_ring.sring); + info->conn_ring_ref = GRANT_INVALID_REF; + } + info->conn_ring.sring = NULL; } -static int setup_usbring(struct xenbus_device *dev, +static int setup_rings(struct xenbus_device *dev, struct usbfront_info *info) { - usbif_sring_t *sring; + usbif_urb_sring_t *urb_sring; + usbif_conn_sring_t *conn_sring; int err; - info->ring_ref= GRANT_INVALID_REF; + info->urb_ring_ref = GRANT_INVALID_REF; + info->conn_ring_ref = GRANT_INVALID_REF; - sring = (usbif_sring_t *)get_zeroed_page(GFP_NOIO|__GFP_HIGH); - if (!sring) { - xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring"); + urb_sring = (usbif_urb_sring_t *)get_zeroed_page(GFP_NOIO|__GFP_HIGH); + if (!urb_sring) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating urb ring"); return -ENOMEM; } - SHARED_RING_INIT(sring); - FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); + SHARED_RING_INIT(urb_sring); + FRONT_RING_INIT(&info->urb_ring, urb_sring, PAGE_SIZE); - err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring)); + err = xenbus_grant_ring(dev, virt_to_mfn(info->urb_ring.sring)); if (err < 0) { - free_page((unsigned long)sring); - info->ring.sring = NULL; + free_page((unsigned long)urb_sring); + info->urb_ring.sring = NULL; goto fail; } - info->ring_ref = err; + info->urb_ring_ref = err; + + conn_sring = (usbif_conn_sring_t *)get_zeroed_page(GFP_NOIO|__GFP_HIGH); + if (!conn_sring) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating conn ring"); + return -ENOMEM; + } + SHARED_RING_INIT(conn_sring); + FRONT_RING_INIT(&info->conn_ring, conn_sring, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(info->conn_ring.sring)); + if (err < 0) { + free_page((unsigned long)conn_sring); + info->conn_ring.sring = NULL; + goto fail; + } + info->conn_ring_ref = err; err = bind_listening_port_to_irqhandler( dev->otherend_id, xenhcd_int, SA_SAMPLE_RANDOM, "usbif", info); @@ -101,7 +121,7 @@ return 0; fail: - usbif_free(info); + destroy_rings(info); return err; } @@ -112,7 +132,7 @@ struct xenbus_transaction xbt; int err; - err = setup_usbring(dev, info); + err = setup_rings(dev, info); if (err) goto out; @@ -123,10 +143,17 @@ goto destroy_ring; } - err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u", - info->ring_ref); + err = xenbus_printf(xbt, dev->nodename, "urb-ring-ref", "%u", + info->urb_ring_ref); if (err) { - message = "writing ring-ref"; + message = "writing urb-ring-ref"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "conn-ring-ref", "%u", + info->conn_ring_ref); + if (err) { + message = "writing conn-ring-ref"; goto abort_transaction; } @@ -145,8 +172,6 @@ goto destroy_ring; } - xenbus_switch_state(dev, XenbusStateInitialised); - return 0; abort_transaction: @@ -154,17 +179,45 @@ xenbus_dev_fatal(dev, err, "%s", message); destroy_ring: - usbif_free(info); + destroy_rings(info); out: return err; } +static int connect(struct xenbus_device *dev) +{ + struct usbfront_info *info = dev->dev.driver_data; + + usbif_conn_request_t *req; + int i, idx, err; + int notify; + + err = talk_to_backend(dev, info); + if (err) + return err; + + /* prepare ring for hotplug notification */ + for (idx = 0, i = 0; i < USB_CONN_RING_SIZE; i++) { + req = RING_GET_REQUEST(&info->conn_ring, idx); + req->id = idx; + idx++; + } + info->conn_ring.req_prod_pvt = idx; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify); + if (notify) + notify_remote_via_irq(info->irq); + + return 0; +} + static struct usb_hcd *create_hcd(struct xenbus_device *dev) { int i; int err = 0; int num_ports; + int usb_ver; struct usb_hcd *hcd = NULL; struct usbfront_info *info = NULL; @@ -179,20 +232,38 @@ return ERR_PTR(-EINVAL); } - hcd = usb_create_hcd(&usbfront_hc_driver, &dev->dev, dev->dev.bus_id); + err = xenbus_scanf(XBT_NIL, dev->otherend, + "usb-ver", "%d", &usb_ver); + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading usb-ver"); + return ERR_PTR(-EINVAL); + } + switch (usb_ver) { + case USB_VER_USB11: + hcd = usb_create_hcd(&xen_usb11_hc_driver, &dev->dev, dev->dev.bus_id); + break; + case USB_VER_USB20: + hcd = usb_create_hcd(&xen_usb20_hc_driver, &dev->dev, dev->dev.bus_id); + break; + default: + xenbus_dev_fatal(dev, err, "invalid usb-ver"); + return ERR_PTR(-EINVAL); + } if (!hcd) { - xenbus_dev_fatal(dev, err, "fail to allocate USB host controller"); + xenbus_dev_fatal(dev, err, + "fail to allocate USB host controller"); return ERR_PTR(-ENOMEM); } + info = hcd_to_info(hcd); info->xbdev = dev; info->rh_numports = num_ports; - for (i = 0; i < USB_RING_SIZE; i++) { - info->shadow[i].req.id = i+1; + for (i = 0; i < USB_URB_RING_SIZE; i++) { + info->shadow[i].req.id = i + 1; info->shadow[i].urb = NULL; } - info->shadow[USB_RING_SIZE-1].req.id = 0x0fff; + info->shadow[USB_URB_RING_SIZE-1].req.id = 0x0fff; return hcd; } @@ -211,7 +282,8 @@ hcd = create_hcd(dev); if (IS_ERR(hcd)) { err = PTR_ERR(hcd); - xenbus_dev_fatal(dev, err, "fail to create usb host controller"); + xenbus_dev_fatal(dev, err, + "fail to create usb host controller"); goto fail; } @@ -220,22 +292,19 @@ err = usb_add_hcd(hcd, 0, 0); if (err != 0) { - xenbus_dev_fatal(dev, err, "fail to adding USB host controller"); + xenbus_dev_fatal(dev, err, + "fail to adding USB host controller"); goto fail; } init_waitqueue_head(&info->wq); snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum); info->kthread = kthread_run(xenhcd_schedule, info, name); - if (IS_ERR(info->kthread)) { - err = PTR_ERR(info->kthread); - info->kthread = NULL; - goto fail; - } - - err = talk_to_backend(dev, info); - if (err) + if (IS_ERR(info->kthread)) { + err = PTR_ERR(info->kthread); + info->kthread = NULL; goto fail; + } return 0; @@ -245,58 +314,41 @@ return err; } -/* - * 0=disconnected, 1=low_speed, 2=full_speed, 3=high_speed - */ -static void usbfront_do_hotplug(struct usbfront_info *info) +static void usbfront_disconnect(struct xenbus_device *dev) { - char port_str[8]; - int i; - int err; - int state; + struct usbfront_info *info = dev->dev.driver_data; + struct usb_hcd *hcd = info_to_hcd(info); - for (i = 1; i <= info->rh_numports; i++) { - sprintf(port_str, "port-%d", i); - err = xenbus_scanf(XBT_NIL, info->xbdev->otherend, - port_str, "%d", &state); - if (err == 1) - xenhcd_rhport_state_change(info, i, state); + usb_remove_hcd(hcd); + if (info->kthread) { + kthread_stop(info->kthread); + info->kthread = NULL; } + xenbus_frontend_closed(dev); } static void backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { - struct usbfront_info *info = dev->dev.driver_data; - switch (backend_state) { case XenbusStateInitialising: - case XenbusStateInitWait: case XenbusStateInitialised: + case XenbusStateConnected: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: case XenbusStateUnknown: case XenbusStateClosed: break; - case XenbusStateConnected: - if (dev->state == XenbusStateConnected) + case XenbusStateInitWait: + if (dev->state != XenbusStateInitialising) break; - if (dev->state == XenbusStateInitialised) - usbfront_do_hotplug(info); + connect(dev); xenbus_switch_state(dev, XenbusStateConnected); break; case XenbusStateClosing: - xenbus_frontend_closed(dev); - break; - - case XenbusStateReconfiguring: - if (dev->state == XenbusStateConnected) - xenbus_switch_state(dev, XenbusStateReconfiguring); - break; - - case XenbusStateReconfigured: - usbfront_do_hotplug(info); - xenbus_switch_state(dev, XenbusStateConnected); + usbfront_disconnect(dev); break; default: @@ -311,12 +363,7 @@ struct usbfront_info *info = dev->dev.driver_data; struct usb_hcd *hcd = info_to_hcd(info); - usb_remove_hcd(hcd); - if (info->kthread) { - kthread_stop(info->kthread); - info->kthread = NULL; - } - usbif_free(info); + destroy_rings(info); usb_put_hcd(hcd); return 0; @@ -361,5 +408,5 @@ module_exit(usbfront_exit); MODULE_AUTHOR(""); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_DESCRIPTION("Xen USB Virtual Host Controller driver (usbfront)"); MODULE_LICENSE("Dual BSD/GPL"); _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |