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
|