# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1254901320 -3600
# Node ID 498ac445a2a9bb3e8e01f1bca46fc22e49ad23cb
# Parent 7d57b5843b6587d2f220db0e43cb20b85de9baf7
PVUSB: Fixes and updates
- xenbus state flow changed.
Whole of the flow is changed to be like netback/netfront.
Reconfiguring/Reconfiguring are removed.
- New RING for hotplug notification added.
- USBIF_MAX_SEGMENTS_PER_REQUEST value is changed (10) to (16).
According to this change, RING_SIZE is decreased from 32 to 16.
This affects the performance. My flash drive's read throughput
was dropped from 29MB/s to 18MB/s in the linux environment.
However, Windows guest send urb with 64kB buffer(64KB = 4kB * 16).
This is required.
- New port-setting interface
xenbus_watch_path2 is added to usbback, port-setting interface
is moved from sysfs to xenstore.
Now, the port-rule is directly written to xenstore entry.
Example.
# xenstore-write /local/domain/0/backend/vusb/1/0/port/1 "2-1"
(adding physical bus 2-1 to vusb-1-0 port 1)
- urb dequeue function completed.
usbfront send unlink-request to usbback, and can cancel the urb
that is submitted in the backend.
- New USB Spec version (USB1.1/USB2.0) selection support.
usbfront can act as both USB1.1 and USB2.0 virtual host controller
according to the xenstore entry key "usb-ver".
- experimental bus_suspend/bus_resume added to usbfront.
- various cleanups, bugfix, refactoring and codestyle-fix.
Signed-off-by: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
---
drivers/xen/usbback/interface.c | 135 ++++++---
drivers/xen/usbback/usbback.c | 227 ++++++++++------
drivers/xen/usbback/usbback.h | 89 +++---
drivers/xen/usbback/usbstub.c | 498 +++++++++++++-----------------------
drivers/xen/usbback/xenbus.c | 257 +++++++++++-------
drivers/xen/usbfront/usbfront-dbg.c | 5
drivers/xen/usbfront/usbfront-hcd.c | 130 ++-------
drivers/xen/usbfront/usbfront-hub.c | 131 +++------
drivers/xen/usbfront/usbfront-q.c | 279 ++++++++++++++------
drivers/xen/usbfront/usbfront.h | 97 +++----
drivers/xen/usbfront/xenbus.c | 289 ++++++++++++--------
include/xen/interface/io/usbif.h | 46 ++-
12 files changed, 1174 insertions(+), 1009 deletions(-)
diff -r 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbback/interface.c
--- a/drivers/xen/usbback/interface.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbback/interface.c Wed Oct 07 08:42:00 2009 +0100
@@ -48,7 +48,7 @@ static LIST_HEAD(usbif_list);
static LIST_HEAD(usbif_list);
static DEFINE_SPINLOCK(usbif_list_lock);
-usbif_t *find_usbif(int dom_id, int dev_id)
+usbif_t *find_usbif(domid_t domid, unsigned int handle)
{
usbif_t *usbif;
int found = 0;
@@ -56,8 +56,8 @@ usbif_t *find_usbif(int dom_id, int dev_
spin_lock_irqsave(&usbif_list_lock, flags);
list_for_each_entry(usbif, &usbif_list, usbif_list) {
- if (usbif->domid == dom_id
- && usbif->handle == dev_id) {
+ if (usbif->domid == domid
+ && usbif->handle == handle) {
found = 1;
break;
}
@@ -82,16 +82,16 @@ usbif_t *usbif_alloc(domid_t domid, unsi
usbif->domid = domid;
usbif->handle = handle;
- spin_lock_init(&usbif->ring_lock);
+ spin_lock_init(&usbif->urb_ring_lock);
+ spin_lock_init(&usbif->conn_ring_lock);
atomic_set(&usbif->refcnt, 0);
init_waitqueue_head(&usbif->wq);
init_waitqueue_head(&usbif->waiting_to_free);
- spin_lock_init(&usbif->plug_lock);
- INIT_LIST_HEAD(&usbif->plugged_devices);
+ spin_lock_init(&usbif->stub_lock);
+ INIT_LIST_HEAD(&usbif->stub_list);
spin_lock_init(&usbif->addr_lock);
- for (i = 0; i < USB_DEV_ADDR_SIZE; i++) {
+ for (i = 0; i < USB_DEV_ADDR_SIZE; i++)
usbif->addr_table[i] = NULL;
- }
spin_lock_irqsave(&usbif_list_lock, flags);
list_add(&usbif->usbif_list, &usbif_list);
@@ -100,70 +100,109 @@ usbif_t *usbif_alloc(domid_t domid, unsi
return usbif;
}
-static int map_frontend_page(usbif_t *usbif, unsigned long shared_page)
+static int map_frontend_pages(usbif_t *usbif,
+ grant_ref_t urb_ring_ref,
+ grant_ref_t conn_ring_ref)
{
struct gnttab_map_grant_ref op;
- gnttab_set_map_op(&op, (unsigned long)usbif->ring_area->addr,
- GNTMAP_host_map, shared_page, usbif->domid);
+ gnttab_set_map_op(&op, (unsigned long)usbif->urb_ring_area->addr,
+ GNTMAP_host_map, urb_ring_ref, usbif->domid);
if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
BUG();
if (op.status) {
- printk(KERN_ERR "grant table operation failure\n");
+ printk(KERN_ERR "grant table failure mapping urb_ring_ref\n");
return op.status;
}
- usbif->shmem_ref = shared_page;
- usbif->shmem_handle = op.handle;
+ usbif->urb_shmem_ref = urb_ring_ref;
+ usbif->urb_shmem_handle = op.handle;
+
+ gnttab_set_map_op(&op, (unsigned long)usbif->conn_ring_area->addr,
+ GNTMAP_host_map, conn_ring_ref, usbif->domid);
+
+ if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
+ BUG();
+
+ if (op.status) {
+ struct gnttab_unmap_grant_ref unop;
+ gnttab_set_unmap_op(&unop,
+ (unsigned long) usbif->urb_ring_area->addr,
+ GNTMAP_host_map, usbif->urb_shmem_handle);
+ VOID(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unop,
+ 1));
+ printk(KERN_ERR "grant table failure mapping conn_ring_ref\n");
+ return op.status;
+ }
+
+ usbif->conn_shmem_ref = conn_ring_ref;
+ usbif->conn_shmem_handle = op.handle;
return 0;
}
-static void unmap_frontend_page(usbif_t *usbif)
+static void unmap_frontend_pages(usbif_t *usbif)
{
struct gnttab_unmap_grant_ref op;
- gnttab_set_unmap_op(&op, (unsigned long)usbif->ring_area->addr,
- GNTMAP_host_map, usbif->shmem_handle);
+ gnttab_set_unmap_op(&op, (unsigned long)usbif->urb_ring_area->addr,
+ GNTMAP_host_map, usbif->urb_shmem_handle);
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
BUG();
-}
-
-int usbif_map(usbif_t *usbif, unsigned long shared_page, unsigned int evtchn)
-{
- int err;
- usbif_sring_t *sring;
+
+ gnttab_set_unmap_op(&op, (unsigned long)usbif->conn_ring_area->addr,
+ GNTMAP_host_map, usbif->conn_shmem_handle);
+
+ if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
+ BUG();
+}
+
+int usbif_map(usbif_t *usbif, unsigned long urb_ring_ref,
+ unsigned long conn_ring_ref, unsigned int evtchn)
+{
+ int err = -ENOMEM;
+
+ usbif_urb_sring_t *urb_sring;
+ usbif_conn_sring_t *conn_sring;
if (usbif->irq)
return 0;
- if ((usbif->ring_area = alloc_vm_area(PAGE_SIZE)) == NULL)
- return -ENOMEM;
-
- err = map_frontend_page(usbif, shared_page);
- if (err) {
- free_vm_area(usbif->ring_area);
+ if ((usbif->urb_ring_area = alloc_vm_area(PAGE_SIZE)) == NULL)
return err;
- }
-
- sring = (usbif_sring_t *) usbif->ring_area->addr;
- BACK_RING_INIT(&usbif->ring, sring, PAGE_SIZE);
+ if ((usbif->conn_ring_area = alloc_vm_area(PAGE_SIZE)) == NULL)
+ goto fail_alloc;
+
+ err = map_frontend_pages(usbif, urb_ring_ref, conn_ring_ref);
+ if (err)
+ goto fail_map;
err = bind_interdomain_evtchn_to_irqhandler(
- usbif->domid, evtchn, usbbk_be_int, 0, "usbif-backend",
usbif);
+ usbif->domid, evtchn, usbbk_be_int, 0,
+ "usbif-backend", usbif);
if (err < 0)
- {
- unmap_frontend_page(usbif);
- free_vm_area(usbif->ring_area);
- usbif->ring.sring = NULL;
- return err;
- }
+ goto fail_evtchn;
usbif->irq = err;
+ urb_sring = (usbif_urb_sring_t *) usbif->urb_ring_area->addr;
+ BACK_RING_INIT(&usbif->urb_ring, urb_sring, PAGE_SIZE);
+
+ conn_sring = (usbif_conn_sring_t *) usbif->conn_ring_area->addr;
+ BACK_RING_INIT(&usbif->conn_ring, conn_sring, PAGE_SIZE);
+
return 0;
+
+fail_evtchn:
+ unmap_frontend_pages(usbif);
+fail_map:
+ free_vm_area(usbif->conn_ring_area);
+fail_alloc:
+ free_vm_area(usbif->urb_ring_area);
+
+ return err;
}
void usbif_disconnect(usbif_t *usbif)
@@ -176,12 +215,12 @@ void usbif_disconnect(usbif_t *usbif)
usbif->xenusbd = NULL;
}
- spin_lock_irqsave(&usbif->plug_lock, flags);
- list_for_each_entry_safe(stub, tmp, &usbif->plugged_devices,
plugged_list) {
+ spin_lock_irqsave(&usbif->stub_lock, flags);
+ list_for_each_entry_safe(stub, tmp, &usbif->stub_list, dev_list) {
usbbk_unlink_urbs(stub);
detach_device_without_lock(usbif, stub);
}
- spin_unlock_irqrestore(&usbif->plug_lock, flags);
+ spin_unlock_irqrestore(&usbif->stub_lock, flags);
wait_event(usbif->waiting_to_free, atomic_read(&usbif->refcnt) == 0);
@@ -190,10 +229,12 @@ void usbif_disconnect(usbif_t *usbif)
usbif->irq = 0;
}
- if (usbif->ring.sring) {
- unmap_frontend_page(usbif);
- free_vm_area(usbif->ring_area);
- usbif->ring.sring = NULL;
+ if (usbif->urb_ring.sring) {
+ unmap_frontend_pages(usbif);
+ free_vm_area(usbif->urb_ring_area);
+ free_vm_area(usbif->conn_ring_area);
+ usbif->urb_ring.sring = NULL;
+ usbif->conn_ring.sring = NULL;
}
}
diff -r 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbback/usbback.c
--- a/drivers/xen/usbback/usbback.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbback/usbback.c Wed Oct 07 08:42:00 2009 +0100
@@ -107,7 +107,7 @@ static inline unsigned long vaddr(pendin
#define pending_handle(_req, _seg) \
(pending_grant_handles[vaddr_pagenr(_req, _seg)])
-static pending_req_t* alloc_req(void)
+static pending_req_t *alloc_req(void)
{
pending_req_t *req = NULL;
unsigned long flags;
@@ -222,7 +222,7 @@ static void copy_pages_to_buff(void *buf
}
}
-static int usbbk_alloc_urb(usbif_request_t *req, pending_req_t *pending_req)
+static int usbbk_alloc_urb(usbif_urb_request_t *req, pending_req_t
*pending_req)
{
int ret;
@@ -298,21 +298,21 @@ static void usbbk_do_response(pending_re
int32_t actual_length, int32_t
error_count, uint16_t start_frame)
{
usbif_t *usbif = pending_req->usbif;
- usbif_response_t *ring_res;
+ usbif_urb_response_t *res;
unsigned long flags;
int notify;
- spin_lock_irqsave(&usbif->ring_lock, flags);
- ring_res = RING_GET_RESPONSE(&usbif->ring, usbif->ring.rsp_prod_pvt);
- ring_res->id = pending_req->id;
- ring_res->status = status;
- ring_res->actual_length = actual_length;
- ring_res->error_count = error_count;
- ring_res->start_frame = start_frame;
- usbif->ring.rsp_prod_pvt++;
- barrier();
- RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->ring, notify);
- spin_unlock_irqrestore(&usbif->ring_lock, flags);
+ spin_lock_irqsave(&usbif->urb_ring_lock, flags);
+ res = RING_GET_RESPONSE(&usbif->urb_ring, usbif->urb_ring.rsp_prod_pvt);
+ res->id = pending_req->id;
+ res->status = status;
+ res->actual_length = actual_length;
+ res->error_count = error_count;
+ res->start_frame = start_frame;
+ usbif->urb_ring.rsp_prod_pvt++;
+ barrier();
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->urb_ring, notify);
+ spin_unlock_irqrestore(&usbif->urb_ring_lock, flags);
if (notify)
notify_remote_via_irq(usbif->irq);
@@ -346,7 +346,7 @@ static void usbbk_urb_complete(struct ur
}
static int usbbk_gnttab_map(usbif_t *usbif,
- usbif_request_t *req, pending_req_t *pending_req)
+ usbif_urb_request_t *req, pending_req_t *pending_req)
{
int i, ret;
unsigned int nr_segs;
@@ -434,7 +434,7 @@ fail:
return ret;
}
-static void usbbk_init_urb(usbif_request_t *req, pending_req_t *pending_req)
+static void usbbk_init_urb(usbif_urb_request_t *req, pending_req_t
*pending_req)
{
unsigned int pipe;
struct usb_device *udev = pending_req->stub->udev;
@@ -671,14 +671,14 @@ struct usbstub *find_attached_device(usb
int found = 0;
unsigned long flags;
- spin_lock_irqsave(&usbif->plug_lock, flags);
- list_for_each_entry(stub, &usbif->plugged_devices, plugged_list) {
- if (stub->id->portnum == portnum) {
+ spin_lock_irqsave(&usbif->stub_lock, flags);
+ list_for_each_entry(stub, &usbif->stub_list, dev_list) {
+ if (stub->portid->portnum == portnum) {
found = 1;
break;
}
}
- spin_unlock_irqrestore(&usbif->plug_lock, flags);
+ spin_unlock_irqrestore(&usbif->stub_lock, flags);
if (found)
return stub;
@@ -686,7 +686,47 @@ struct usbstub *find_attached_device(usb
return NULL;
}
-static int check_and_submit_special_ctrlreq(usbif_t *usbif, usbif_request_t
*req, pending_req_t *pending_req)
+static void process_unlink_req(usbif_t *usbif,
+ usbif_urb_request_t *req, pending_req_t *pending_req)
+{
+ pending_req_t *unlink_req = NULL;
+ int devnum;
+ int ret = 0;
+ unsigned long flags;
+
+ devnum = usb_pipedevice(req->pipe);
+ if (unlikely(devnum == 0)) {
+ pending_req->stub = find_attached_device(usbif,
usbif_pipeportnum(req->pipe));
+ if (unlikely(!pending_req->stub)) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ } else {
+ if (unlikely(!usbif->addr_table[devnum])) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ pending_req->stub = usbif->addr_table[devnum];
+ }
+
+ spin_lock_irqsave(&pending_req->stub->submitting_lock, flags);
+ list_for_each_entry(unlink_req, &pending_req->stub->submitting_list,
urb_list) {
+ if (unlink_req->id == req->u.unlink.unlink_id) {
+ ret = usb_unlink_urb(unlink_req->urb);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&pending_req->stub->submitting_lock, flags);
+
+fail_response:
+ usbbk_do_response(pending_req, ret, 0, 0, 0);
+ usbif_put(usbif);
+ free_req(pending_req);
+ return;
+}
+
+static int check_and_submit_special_ctrlreq(usbif_t *usbif,
+ usbif_urb_request_t *req, pending_req_t *pending_req)
{
int devnum;
struct usbstub *stub = NULL;
@@ -824,7 +864,7 @@ fail_response:
}
static void dispatch_request_to_pending_reqs(usbif_t *usbif,
- usbif_request_t *req,
+ usbif_urb_request_t *req,
pending_req_t *pending_req)
{
int ret;
@@ -834,17 +874,13 @@ static void dispatch_request_to_pending_
barrier();
- /*
- * TODO:
- * receive unlink request and cancel the urb in backend
- */
-#if 0
- if (unlikely(usb_pipeunlink(req->pipe))) {
-
- }
-#endif
-
usbif_get(usbif);
+
+ /* unlink request */
+ if (unlikely(usbif_pipeunlink(req->pipe))) {
+ process_unlink_req(usbif, req, pending_req);
+ return;
+ }
if (usb_pipecontrol(req->pipe)) {
if (check_and_submit_special_ctrlreq(usbif, req, pending_req))
@@ -927,18 +963,18 @@ fail_response:
static int usbbk_start_submit_urb(usbif_t *usbif)
{
- usbif_back_ring_t *usb_ring = &usbif->ring;
- usbif_request_t *ring_req;
+ usbif_urb_back_ring_t *urb_ring = &usbif->urb_ring;
+ usbif_urb_request_t *req;
pending_req_t *pending_req;
RING_IDX rc, rp;
int more_to_do = 0;
- rc = usb_ring->req_cons;
- rp = usb_ring->sring->req_prod;
+ rc = urb_ring->req_cons;
+ rp = urb_ring->sring->req_prod;
rmb();
while (rc != rp) {
- if (RING_REQUEST_CONS_OVERFLOW(usb_ring, rc)) {
+ if (RING_REQUEST_CONS_OVERFLOW(urb_ring, rc)) {
printk(KERN_WARNING "RING_REQUEST_CONS_OVERFLOW\n");
break;
}
@@ -949,73 +985,100 @@ static int usbbk_start_submit_urb(usbif_
break;
}
- ring_req = RING_GET_REQUEST(usb_ring, rc);
- usb_ring->req_cons = ++rc;
-
- dispatch_request_to_pending_reqs(usbif, ring_req,
+ req = RING_GET_REQUEST(urb_ring, rc);
+ urb_ring->req_cons = ++rc;
+
+ dispatch_request_to_pending_reqs(usbif, req,
pending_req);
}
- RING_FINAL_CHECK_FOR_REQUESTS(&usbif->ring, more_to_do);
+ RING_FINAL_CHECK_FOR_REQUESTS(&usbif->urb_ring, more_to_do);
cond_resched();
return more_to_do;
}
+void usbbk_hotplug_notify(usbif_t *usbif, int portnum, int speed)
+{
+ usbif_conn_back_ring_t *ring = &usbif->conn_ring;
+ usbif_conn_request_t *req;
+ usbif_conn_response_t *res;
+ unsigned long flags;
+ u16 id;
+ int notify;
+
+ spin_lock_irqsave(&usbif->conn_ring_lock, flags);
+
+ req = RING_GET_REQUEST(ring, ring->req_cons);;
+ id = req->id;
+ ring->req_cons++;
+ ring->sring->req_event = ring->req_cons + 1;
+
+ res = RING_GET_RESPONSE(ring, ring->rsp_prod_pvt);
+ res->id = id;
+ res->portnum = portnum;
+ res->speed = speed;
+ ring->rsp_prod_pvt++;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify);
+
+ spin_unlock_irqrestore(&usbif->conn_ring_lock, flags);
+
+ if (notify)
+ notify_remote_via_irq(usbif->irq);
+}
+
int usbbk_schedule(void *arg)
{
- usbif_t *usbif = (usbif_t *)arg;
-
- usbif_get(usbif);
-
- while(!kthread_should_stop()) {
- wait_event_interruptible(
- usbif->wq,
- usbif->waiting_reqs || kthread_should_stop());
- wait_event_interruptible(
- pending_free_wq,
- !list_empty(&pending_free) ||
kthread_should_stop());
- usbif->waiting_reqs = 0;
- smp_mb();
-
- if (usbbk_start_submit_urb(usbif))
- usbif->waiting_reqs = 1;
- }
-
- usbif->xenusbd = NULL;
- usbif_put(usbif);
-
- return 0;
+ usbif_t *usbif = (usbif_t *) arg;
+
+ usbif_get(usbif);
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(
+ usbif->wq,
+ usbif->waiting_reqs || kthread_should_stop());
+ wait_event_interruptible(
+ pending_free_wq,
+ !list_empty(&pending_free) || kthread_should_stop());
+ usbif->waiting_reqs = 0;
+ smp_mb();
+
+ if (usbbk_start_submit_urb(usbif))
+ usbif->waiting_reqs = 1;
+ }
+
+ usbif->xenusbd = NULL;
+ usbif_put(usbif);
+
+ return 0;
}
/*
- * attach the grabbed device to usbif.
+ * attach usbstub device to usbif.
*/
-void usbbk_plug_device(usbif_t *usbif, struct usbstub *stub)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&usbif->plug_lock, flags);
- list_add(&stub->plugged_list, &usbif->plugged_devices);
- spin_unlock_irqrestore(&usbif->plug_lock, flags);
- stub->plugged = 1;
+void usbbk_attach_device(usbif_t *usbif, struct usbstub *stub)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&usbif->stub_lock, flags);
+ list_add(&stub->dev_list, &usbif->stub_list);
+ spin_unlock_irqrestore(&usbif->stub_lock, flags);
stub->usbif = usbif;
}
/*
- * detach the grabbed device from usbif.
+ * detach usbstub device from usbif.
*/
-void usbbk_unplug_device(usbif_t *usbif, struct usbstub *stub)
+void usbbk_detach_device(usbif_t *usbif, struct usbstub *stub)
{
unsigned long flags;
if (stub->addr)
usbbk_set_address(usbif, stub, stub->addr, 0);
- spin_lock_irqsave(&usbif->plug_lock, flags);
- list_del(&stub->plugged_list);
- spin_unlock_irqrestore(&usbif->plug_lock, flags);
- stub->plugged = 0;
+ spin_lock_irqsave(&usbif->stub_lock, flags);
+ list_del(&stub->dev_list);
+ spin_unlock_irqrestore(&usbif->stub_lock, flags);
stub->usbif = NULL;
}
@@ -1023,8 +1086,7 @@ void detach_device_without_lock(usbif_t
{
if (stub->addr)
usbbk_set_address(usbif, stub, stub->addr, 0);
- list_del(&stub->plugged_list);
- stub->plugged = 0;
+ list_del(&stub->dev_list);
stub->usbif = NULL;
}
@@ -1054,9 +1116,8 @@ static int __init usbback_init(void)
memset(pending_reqs, 0, sizeof(pending_reqs));
INIT_LIST_HEAD(&pending_free);
- for (i = 0; i < usbif_reqs; i++) {
+ for (i = 0; i < usbif_reqs; i++)
list_add_tail(&pending_reqs[i].free_list, &pending_free);
- }
usbback_xenbus_init();
diff -r 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbback/usbback.h
--- a/drivers/xen/usbback/usbback.h Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbback/usbback.h Wed Oct 07 08:42:00 2009 +0100
@@ -59,6 +59,7 @@
#include <xen/gnttab.h>
#include <xen/driver_util.h>
#include <xen/interface/xen.h>
+#include <xen/xenbus.h>
#include <xen/interface/io/usbif.h>
struct usbstub;
@@ -66,89 +67,103 @@ struct usbstub;
#define USB_DEV_ADDR_SIZE 128
typedef struct usbif_st {
- domid_t domid;
- unsigned int handle;
+ domid_t domid;
+ unsigned int handle;
+ int num_ports;
+ enum usb_spec_version usb_ver;
+
struct xenbus_device *xbdev;
struct list_head usbif_list;
unsigned int irq;
- usbif_back_ring_t ring;
- struct vm_struct *ring_area;
+ usbif_urb_back_ring_t urb_ring;
+ usbif_conn_back_ring_t conn_ring;
+ struct vm_struct *urb_ring_area;
+ struct vm_struct *conn_ring_area;
- spinlock_t ring_lock;
+ spinlock_t urb_ring_lock;
+ spinlock_t conn_ring_lock;
atomic_t refcnt;
- grant_handle_t shmem_handle;
- grant_ref_t shmem_ref;
+
+ grant_handle_t urb_shmem_handle;
+ grant_ref_t urb_shmem_ref;
+ grant_handle_t conn_shmem_handle;
+ grant_ref_t conn_shmem_ref;
+
+ struct xenbus_watch backend_watch;
/* device address lookup table */
+ struct usbstub *addr_table[USB_DEV_ADDR_SIZE];
spinlock_t addr_lock;
- struct usbstub *addr_table[USB_DEV_ADDR_SIZE];
- /* plugged device list */
- unsigned plaggable:1;
- spinlock_t plug_lock;
- struct list_head plugged_devices;
+ /* connected device list */
+ struct list_head stub_list;
+ spinlock_t stub_lock;
/* request schedule */
struct task_struct *xenusbd;
unsigned int waiting_reqs;
wait_queue_head_t waiting_to_free;
wait_queue_head_t wq;
-
} usbif_t;
-struct usbstub_id
-{
+struct vusb_port_id {
struct list_head id_list;
- char bus_id[BUS_ID_SIZE];
- int dom_id;
- int dev_id;
+ char phys_bus[BUS_ID_SIZE];
+ domid_t domid;
+ unsigned int handle;
int portnum;
+ unsigned is_connected:1;
};
-struct usbstub
-{
- struct usbstub_id *id;
+struct usbstub {
+ struct kref kref;
+ struct list_head dev_list;
+
+ struct vusb_port_id *portid;
struct usb_device *udev;
- struct usb_interface *interface;
usbif_t *usbif;
-
- struct list_head grabbed_list;
-
- unsigned plugged:1;
- struct list_head plugged_list;
-
int addr;
+ struct list_head submitting_list;
spinlock_t submitting_lock;
- struct list_head submitting_list;
};
usbif_t *usbif_alloc(domid_t domid, unsigned int handle);
void usbif_disconnect(usbif_t *usbif);
void usbif_free(usbif_t *usbif);
-int usbif_map(usbif_t *usbif, unsigned long shared_page, unsigned int evtchn);
+int usbif_map(usbif_t *usbif, unsigned long urb_ring_ref,
+ unsigned long conn_ring_ref, unsigned int evtchn);
#define usbif_get(_b) (atomic_inc(&(_b)->refcnt))
#define usbif_put(_b) \
do { \
if (atomic_dec_and_test(&(_b)->refcnt)) \
- wake_up(&(_b)->waiting_to_free); \
+ wake_up(&(_b)->waiting_to_free); \
} while (0)
+usbif_t *find_usbif(domid_t domid, unsigned int handle);
void usbback_xenbus_init(void);
void usbback_xenbus_exit(void);
-
+struct vusb_port_id *find_portid_by_busid(const char *busid);
+struct vusb_port_id *find_portid(const domid_t domid,
+ const unsigned int handle,
+ const int portnum);
+int portid_add(const char *busid,
+ const domid_t domid,
+ const unsigned int handle,
+ const int portnum);
+int portid_remove(const domid_t domid,
+ const unsigned int handle,
+ const int portnum);
irqreturn_t usbbk_be_int(int irq, void *dev_id, struct pt_regs *regs);
int usbbk_schedule(void *arg);
struct usbstub *find_attached_device(usbif_t *usbif, int port);
-struct usbstub *find_grabbed_device(int dom_id, int dev_id, int port);
-usbif_t *find_usbif(int dom_id, int dev_id);
-void usbback_reconfigure(usbif_t *usbif);
-void usbbk_plug_device(usbif_t *usbif, struct usbstub *stub);
-void usbbk_unplug_device(usbif_t *usbif, struct usbstub *stub);
+void usbbk_attach_device(usbif_t *usbif, struct usbstub *stub);
+void usbbk_detach_device(usbif_t *usbif, struct usbstub *stub);
+void usbbk_hotplug_notify(usbif_t *usbif, int portnum, int speed);
void detach_device_without_lock(usbif_t *usbif, struct usbstub *stub);
void usbbk_unlink_urbs(struct usbstub *stub);
diff -r 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbback/usbstub.c
--- a/drivers/xen/usbback/usbstub.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbback/usbstub.c Wed Oct 07 08:42:00 2009 +0100
@@ -45,36 +45,106 @@
#include "usbback.h"
-static LIST_HEAD(usbstub_ids);
-static DEFINE_SPINLOCK(usbstub_ids_lock);
-static LIST_HEAD(grabbed_devices);
-static DEFINE_SPINLOCK(grabbed_devices_lock);
-
-struct usbstub *find_grabbed_device(int dom_id, int dev_id, int portnum)
-{
- struct usbstub *stub;
+static LIST_HEAD(port_list);
+static DEFINE_SPINLOCK(port_list_lock);
+
+struct vusb_port_id *find_portid_by_busid(const char *busid)
+{
+ struct vusb_port_id *portid;
int found = 0;
unsigned long flags;
- spin_lock_irqsave(&grabbed_devices_lock, flags);
- list_for_each_entry(stub, &grabbed_devices, grabbed_list) {
- if (stub->id->dom_id == dom_id
- && stub->id->dev_id == dev_id
- && stub->id->portnum == portnum) {
+ spin_lock_irqsave(&port_list_lock, flags);
+ list_for_each_entry(portid, &port_list, id_list) {
+ if (!(strncmp(portid->phys_bus, busid, BUS_ID_SIZE))) {
found = 1;
break;
}
}
- spin_unlock_irqrestore(&grabbed_devices_lock, flags);
+ spin_unlock_irqrestore(&port_list_lock, flags);
if (found)
- return stub;
+ return portid;
return NULL;
}
-static struct usbstub *usbstub_alloc(struct usb_interface *interface,
- struct usbstub_id *stub_id)
+struct vusb_port_id *find_portid(const domid_t domid,
+ const unsigned int handle,
+ const int portnum)
+{
+ struct vusb_port_id *portid;
+ int found = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port_list_lock, flags);
+ list_for_each_entry(portid, &port_list, id_list) {
+ if ((portid->domid == domid)
+ && (portid->handle == handle)
+ && (portid->portnum == portnum)) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&port_list_lock, flags);
+
+ if (found)
+ return portid;
+
+ return NULL;
+}
+
+int portid_add(const char *busid,
+ const domid_t domid,
+ const unsigned int handle,
+ const int portnum)
+{
+ struct vusb_port_id *portid;
+ unsigned long flags;
+
+ portid = kzalloc(sizeof(*portid), GFP_KERNEL);
+ if (!portid)
+ return -ENOMEM;
+
+ portid->domid = domid;
+ portid->handle = handle;
+ portid->portnum = portnum;
+
+ strncpy(portid->phys_bus, busid, BUS_ID_SIZE);
+
+ spin_lock_irqsave(&port_list_lock, flags);
+ list_add(&portid->id_list, &port_list);
+ spin_unlock_irqrestore(&port_list_lock, flags);
+
+ return 0;
+}
+
+int portid_remove(const domid_t domid,
+ const unsigned int handle,
+ const int portnum)
+{
+ struct vusb_port_id *portid, *tmp;
+ int err = -ENOENT;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port_list_lock, flags);
+ list_for_each_entry_safe(portid, tmp, &port_list, id_list) {
+ if (portid->domid == domid
+ && portid->handle == handle
+ && portid->portnum == portnum) {
+ list_del(&portid->id_list);
+ kfree(portid);
+
+ err = 0;
+ }
+ }
+ spin_unlock_irqrestore(&port_list_lock, flags);
+
+ return err;
+}
+
+static struct usbstub *usbstub_alloc(struct usb_device *udev,
+ struct vusb_port_id *portid)
{
struct usbstub *stub;
@@ -83,314 +153,135 @@ static struct usbstub *usbstub_alloc(str
printk(KERN_ERR "no memory for alloc usbstub\n");
return NULL;
}
-
- stub->udev = usb_get_dev(interface_to_usbdev(interface));
- stub->interface = interface;
- stub->id = stub_id;
+ kref_init(&stub->kref);
+ stub->udev = usb_get_dev(udev);
+ stub->portid = portid;
spin_lock_init(&stub->submitting_lock);
INIT_LIST_HEAD(&stub->submitting_list);
return stub;
}
-static int usbstub_free(struct usbstub *stub)
-{
- if (!stub)
- return -EINVAL;
+static void usbstub_release(struct kref *kref)
+{
+ struct usbstub *stub;
+
+ stub = container_of(kref, struct usbstub, kref);
usb_put_dev(stub->udev);
- stub->interface = NULL;
stub->udev = NULL;
- stub->id = NULL;
+ stub->portid = NULL;
kfree(stub);
-
- return 0;
-}
-
-static int usbstub_match_one(struct usb_interface *interface,
- struct usbstub_id *stub_id)
-{
- char *udev_busid = interface->dev.parent->bus_id;
-
- if (!(strncmp(stub_id->bus_id, udev_busid, BUS_ID_SIZE))) {
- return 1;
- }
-
- return 0;
-}
-
-static struct usbstub_id *usbstub_match(struct usb_interface *interface)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- struct usbstub_id *stub_id;
- unsigned long flags;
- int found = 0;
+}
+
+static inline void usbstub_get(struct usbstub *stub)
+{
+ kref_get(&stub->kref);
+}
+
+static inline void usbstub_put(struct usbstub *stub)
+{
+ kref_put(&stub->kref, usbstub_release);
+}
+
+static int usbstub_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ char *busid = intf->dev.parent->bus_id;
+ struct vusb_port_id *portid = NULL;
+ struct usbstub *stub = NULL;
+ usbif_t *usbif = NULL;
+ int retval = -ENODEV;
/* hub currently not supported, so skip. */
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
- return NULL;
-
- spin_lock_irqsave(&usbstub_ids_lock, flags);
- list_for_each_entry(stub_id, &usbstub_ids, id_list) {
- if (usbstub_match_one(interface, stub_id)) {
- found = 1;
+ goto out;
+
+ portid = find_portid_by_busid(busid);
+ if (!portid)
+ goto out;
+
+ usbif = find_usbif(portid->domid, portid->handle);
+ if (!usbif)
+ goto out;
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ break;
+ case USB_SPEED_HIGH:
+ if (usbif->usb_ver >= USB_VER_USB20)
break;
- }
- }
- spin_unlock_irqrestore(&usbstub_ids_lock, flags);
-
- if (found)
- return stub_id;
-
- return NULL;
-}
-
-static void add_to_grabbed_devices(struct usbstub *stub)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&grabbed_devices_lock, flags);
- list_add(&stub->grabbed_list, &grabbed_devices);
- spin_unlock_irqrestore(&grabbed_devices_lock, flags);
-}
-
-static void remove_from_grabbed_devices(struct usbstub *stub)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&grabbed_devices_lock, flags);
- list_del(&stub->grabbed_list);
- spin_unlock_irqrestore(&grabbed_devices_lock, flags);
-}
-
-static int usbstub_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usbstub_id *stub_id = NULL;
- struct usbstub *stub = NULL;
- usbif_t *usbif = NULL;
- int retval = 0;
-
- if ((stub_id = usbstub_match(interface))) {
- stub = usbstub_alloc(interface, stub_id);
+ /* fall through */
+ default:
+ goto out;
+ }
+
+ stub = find_attached_device(usbif, portid->portnum);
+ if (!stub) {
+ /* new connection */
+ stub = usbstub_alloc(udev, portid);
if (!stub)
return -ENOMEM;
-
- usb_set_intfdata(interface, stub);
- add_to_grabbed_devices(stub);
- usbif = find_usbif(stub_id->dom_id, stub_id->dev_id);
- if (usbif) {
- usbbk_plug_device(usbif, stub);
- usbback_reconfigure(usbif);
- }
-
- } else
- retval = -ENODEV;
-
+ usbbk_attach_device(usbif, stub);
+ usbbk_hotplug_notify(usbif, portid->portnum, udev->speed);
+ } else {
+ /* maybe already called and connected by other intf */
+ if (strncmp(stub->portid->phys_bus, busid, BUS_ID_SIZE))
+ goto out; /* invalid call */
+ }
+
+ usbstub_get(stub);
+ usb_set_intfdata(intf, stub);
+ retval = 0;
+
+out:
return retval;
}
-static void usbstub_disconnect(struct usb_interface *interface)
+static void usbstub_disconnect(struct usb_interface *intf)
{
struct usbstub *stub
- = (struct usbstub *) usb_get_intfdata(interface);
-
- usb_set_intfdata(interface, NULL);
+ = (struct usbstub *) usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
if (!stub)
return;
if (stub->usbif) {
- usbback_reconfigure(stub->usbif);
- usbbk_unplug_device(stub->usbif, stub);
- }
-
+ usbbk_hotplug_notify(stub->usbif, stub->portid->portnum, 0);
+ usbbk_detach_device(stub->usbif, stub);
+ }
usbbk_unlink_urbs(stub);
-
- remove_from_grabbed_devices(stub);
-
- usbstub_free(stub);
-
- return;
-}
-
-static inline int str_to_vport(const char *buf,
- char *phys_bus,
- int *dom_id,
- int *dev_id,
- int *port)
-{
- char *p;
- int len;
- int err;
-
- /* no physical bus */
- if (!(p = strchr(buf, ':')))
- return -EINVAL;
-
- len = p - buf;
-
- /* bad physical bus */
- if (len + 1 > BUS_ID_SIZE)
- return -EINVAL;
-
- strlcpy(phys_bus, buf, len + 1);
- err = sscanf(p + 1, "%d:%d:%d", dom_id, dev_id, port);
- if (err == 3)
- return 0;
- else
- return -EINVAL;
-}
-
-static int usbstub_id_add(const char *bus_id,
- const int dom_id,
- const int dev_id,
- const int portnum)
-{
- struct usbstub_id *stub_id;
- unsigned long flags;
-
- stub_id = kzalloc(sizeof(*stub_id), GFP_KERNEL);
- if (!stub_id)
- return -ENOMEM;
-
- stub_id->dom_id = dom_id;
- stub_id->dev_id = dev_id;
- stub_id->portnum = portnum;
-
- strncpy(stub_id->bus_id, bus_id, BUS_ID_SIZE);
-
- spin_lock_irqsave(&usbstub_ids_lock, flags);
- list_add(&stub_id->id_list, &usbstub_ids);
- spin_unlock_irqrestore(&usbstub_ids_lock, flags);
-
- return 0;
-}
-
-static int usbstub_id_remove(const char *phys_bus,
- const int dom_id,
- const int dev_id,
- const int portnum)
-{
- struct usbstub_id *stub_id, *tmp;
- int err = -ENOENT;
- unsigned long flags;
-
- spin_lock_irqsave(&usbstub_ids_lock, flags);
- list_for_each_entry_safe(stub_id, tmp, &usbstub_ids, id_list) {
- if (stub_id->dom_id == dom_id
- && stub_id->dev_id == dev_id
- && stub_id->portnum == portnum) {
- list_del(&stub_id->id_list);
- kfree(stub_id);
-
- err = 0;
- }
- }
- spin_unlock_irqrestore(&usbstub_ids_lock, flags);
-
- return err;
-}
-
-static ssize_t usbstub_vport_add(struct device_driver *driver,
- const char *buf, size_t count)
-{
- int err = 0;
-
- char bus_id[BUS_ID_SIZE];
- int dom_id;
- int dev_id;
- int portnum;
-
- err = str_to_vport(buf, &bus_id[0], &dom_id, &dev_id, &portnum);
- if (err)
- goto out;
-
- err = usbstub_id_add(&bus_id[0], dom_id, dev_id, portnum);
-
-out:
- if (!err)
- err = count;
- return err;
-}
-
-DRIVER_ATTR(new_vport, S_IWUSR, NULL, usbstub_vport_add);
-
-static ssize_t usbstub_vport_remove(struct device_driver *driver,
- const char *buf, size_t count)
-{
- int err = 0;
-
- char bus_id[BUS_ID_SIZE];
- int dom_id;
- int dev_id;
- int portnum;
-
- err = str_to_vport(buf, &bus_id[0], &dom_id, &dev_id, &portnum);
- if (err)
- goto out;
-
- err = usbstub_id_remove(&bus_id[0], dom_id, dev_id, portnum);
-
-out:
- if (!err)
- err = count;
- return err;
-}
-
-DRIVER_ATTR(remove_vport, S_IWUSR, NULL, usbstub_vport_remove);
-
-static ssize_t usbstub_vport_show(struct device_driver *driver,
+ usbstub_put(stub);
+}
+
+static ssize_t usbstub_show_portids(struct device_driver *driver,
char *buf)
{
- struct usbstub_id *stub_id;
+ struct vusb_port_id *portid;
size_t count = 0;
unsigned long flags;
- spin_lock_irqsave(&usbstub_ids_lock, flags);
- list_for_each_entry(stub_id, &usbstub_ids, id_list) {
+ spin_lock_irqsave(&port_list_lock, flags);
+ list_for_each_entry(portid, &port_list, id_list) {
if (count >= PAGE_SIZE)
break;
count += scnprintf((char *)buf + count, PAGE_SIZE - count,
"%s:%d:%d:%d\n",
- &stub_id->bus_id[0],
- stub_id->dom_id,
- stub_id->dev_id,
- stub_id->portnum);
- }
- spin_unlock_irqrestore(&usbstub_ids_lock, flags);
+ &portid->phys_bus[0],
+ portid->domid,
+ portid->handle,
+ portid->portnum);
+ }
+ spin_unlock_irqrestore(&port_list_lock, flags);
return count;
}
-DRIVER_ATTR(vports, S_IRUSR, usbstub_vport_show, NULL);
-
-static ssize_t usbstub_devices_show(struct device_driver *driver,
- char *buf)
-{
- struct usbstub *stub;
- size_t count = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&grabbed_devices_lock, flags);
- list_for_each_entry(stub, &grabbed_devices, grabbed_list) {
- if (count >= PAGE_SIZE)
- break;
-
- count += scnprintf((char *)buf + count, PAGE_SIZE - count,
- "%u-%s:%u.%u\n",
- stub->udev->bus->busnum,
- stub->udev->devpath,
-
stub->udev->config->desc.bConfigurationValue,
-
stub->interface->cur_altsetting->desc.bInterfaceNumber);
-
- }
- spin_unlock_irqrestore(&grabbed_devices_lock, flags);
-
- return count;
-}
-
-DRIVER_ATTR(grabbed_devices, S_IRUSR, usbstub_devices_show, NULL);
+DRIVER_ATTR(port_ids, S_IRUSR, usbstub_show_portids, NULL);
/* table of devices that matches any usbdevice */
static struct usb_device_id usbstub_table[] = {
@@ -404,44 +295,31 @@ static struct usb_driver usbback_usb_dri
.probe = usbstub_probe,
.disconnect = usbstub_disconnect,
.id_table = usbstub_table,
+ .no_dynamic_id = 1,
};
int __init usbstub_init(void)
{
- int err;
+ int err;
err = usb_register(&usbback_usb_driver);
- if (err < 0)
- goto out;
- if (!err)
- err = driver_create_file(&usbback_usb_driver.driver,
- &driver_attr_new_vport);
- if (!err)
- err = driver_create_file(&usbback_usb_driver.driver,
- &driver_attr_remove_vport);
- if (!err)
- err = driver_create_file(&usbback_usb_driver.driver,
- &driver_attr_vports);
- if (!err)
- err = driver_create_file(&usbback_usb_driver.driver,
- &driver_attr_grabbed_devices);
+ if (err < 0) {
+ printk(KERN_ERR "usbback: usb_register failed (error %d)\n",
err);
+ goto out;
+ }
+
+ err = driver_create_file(&usbback_usb_driver.driver,
+ &driver_attr_port_ids);
if (err)
- usbstub_exit();
+ usb_deregister(&usbback_usb_driver);
out:
return err;
}
-void usbstub_exit(void)
+void __exit usbstub_exit(void)
{
driver_remove_file(&usbback_usb_driver.driver,
- &driver_attr_new_vport);
- driver_remove_file(&usbback_usb_driver.driver,
- &driver_attr_remove_vport);
- driver_remove_file(&usbback_usb_driver.driver,
- &driver_attr_vports);
- driver_remove_file(&usbback_usb_driver.driver,
- &driver_attr_grabbed_devices);
-
+ &driver_attr_port_ids);
usb_deregister(&usbback_usb_driver);
}
diff -r 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbback/xenbus.c
--- a/drivers/xen/usbback/xenbus.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbback/xenbus.c Wed Oct 07 08:42:00 2009 +0100
@@ -43,29 +43,118 @@
* DEALINGS IN THE SOFTWARE.
*/
-#include <xen/xenbus.h>
#include "usbback.h"
static int start_xenusbd(usbif_t *usbif)
{
- int err = 0;
- char name[TASK_COMM_LEN];
-
- snprintf(name, TASK_COMM_LEN, "usbback.%d.%d", usbif->domid,
usbif->handle);
- usbif->xenusbd = kthread_run(usbbk_schedule, usbif, name);
- if (IS_ERR(usbif->xenusbd)) {
- err = PTR_ERR(usbif->xenusbd);
- usbif->xenusbd = NULL;
- xenbus_dev_error(usbif->xbdev, err, "start xenusbd");
- }
- return err;
+ int err = 0;
+ char name[TASK_COMM_LEN];
+
+ snprintf(name, TASK_COMM_LEN, "usbback.%d.%d", usbif->domid,
+ usbif->handle);
+ usbif->xenusbd = kthread_run(usbbk_schedule, usbif, name);
+ if (IS_ERR(usbif->xenusbd)) {
+ err = PTR_ERR(usbif->xenusbd);
+ usbif->xenusbd = NULL;
+ xenbus_dev_error(usbif->xbdev, err, "start xenusbd");
+ }
+
+ return err;
+}
+
+static void backend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ struct xenbus_transaction xbt;
+ int err;
+ int i;
+ char node[8];
+ char *busid;
+ struct vusb_port_id *portid = NULL;
+
+ usbif_t *usbif = container_of(watch, usbif_t, backend_watch);
+ struct xenbus_device *dev = usbif->xbdev;
+
+again:
+ err = xenbus_transaction_start(&xbt);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "starting transaction");
+ return;
+ }
+
+ for (i = 1; i <= usbif->num_ports; i++) {
+ sprintf(node, "port/%d", i);
+ busid = xenbus_read(xbt, dev->nodename, node, NULL);
+ if (IS_ERR(busid)) {
+ err = PTR_ERR(busid);
+ xenbus_dev_fatal(dev, err, "reading port/%d", i);
+ goto abort;
+ }
+
+ /*
+ * remove portid, if the port is not connected,
+ */
+ if (strlen(busid) == 0) {
+ portid = find_portid(usbif->domid, usbif->handle, i);
+ if (portid) {
+ if (portid->is_connected)
+ xenbus_dev_fatal(dev, err,
+ "can't remove port/%d, unbind
first", i);
+ else
+ portid_remove(usbif->domid,
usbif->handle, i);
+ }
+ continue; /* never configured, ignore */
+ }
+
+ /*
+ * add portid,
+ * if the port is not configured and not used from other usbif.
+ */
+ portid = find_portid(usbif->domid, usbif->handle, i);
+ if (portid) {
+ if ((strncmp(portid->phys_bus, busid, BUS_ID_SIZE)))
+ xenbus_dev_fatal(dev, err,
+ "can't add port/%d, remove first", i);
+ else
+ continue; /* already configured, ignore */
+ } else {
+ if (find_portid_by_busid(busid))
+ xenbus_dev_fatal(dev, err,
+ "can't add port/%d, busid already
used", i);
+ else
+ portid_add(busid, usbif->domid, usbif->handle,
i);
+ }
+ }
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err == -EAGAIN)
+ goto again;
+ if (err)
+ xenbus_dev_fatal(dev, err, "completing transaction");
+
+ return;
+
+abort:
+ xenbus_transaction_end(xbt, 1);
+
+ return;
}
static int usbback_remove(struct xenbus_device *dev)
{
usbif_t *usbif = dev->dev.driver_data;
+ int i;
+
+ if (usbif->backend_watch.node) {
+ unregister_xenbus_watch(&usbif->backend_watch);
+ kfree(usbif->backend_watch.node);
+ usbif->backend_watch.node = NULL;
+ }
if (usbif) {
+ /* remove all ports */
+ for (i = 1; i <= usbif->num_ports; i++)
+ portid_remove(usbif->domid, usbif->handle, i);
usbif_disconnect(usbif);
usbif_free(usbif);;
}
@@ -79,12 +168,14 @@ static int usbback_probe(struct xenbus_d
{
usbif_t *usbif;
unsigned int handle;
+ int num_ports;
+ int usb_ver;
int err;
if (usb_disabled())
return -ENODEV;
- handle = simple_strtoul(strrchr(dev->otherend,'/')+1, NULL, 0);
+ handle = simple_strtoul(strrchr(dev->otherend, '/') + 1, NULL, 0);
usbif = usbif_alloc(dev->otherend_id, handle);
if (!usbif) {
xenbus_dev_fatal(dev, -ENOMEM, "allocating backend interface");
@@ -93,6 +184,34 @@ static int usbback_probe(struct xenbus_d
usbif->xbdev = dev;
dev->dev.driver_data = usbif;
+ err = xenbus_scanf(XBT_NIL, dev->nodename,
+ "num-ports", "%d", &num_ports);
+ if (err != 1) {
+ xenbus_dev_fatal(dev, err, "reading num-ports");
+ goto fail;
+ }
+ if (num_ports < 1 || num_ports > USB_MAXCHILDREN) {
+ xenbus_dev_fatal(dev, err, "invalid num-ports");
+ goto fail;
+ }
+ usbif->num_ports = num_ports;
+
+ err = xenbus_scanf(XBT_NIL, dev->nodename,
+ "usb-ver", "%d", &usb_ver);
+ if (err != 1) {
+ xenbus_dev_fatal(dev, err, "reading usb-ver");
+ goto fail;
+ }
+ switch (usb_ver) {
+ case USB_VER_USB11:
+ case USB_VER_USB20:
+ usbif->usb_ver = usb_ver;
+ break;
+ default:
+ xenbus_dev_fatal(dev, err, "invalid usb-ver");
+ goto fail;
+ }
+
err = xenbus_switch_state(dev, XenbusStateInitWait);
if (err)
goto fail;
@@ -104,15 +223,17 @@ fail:
return err;
}
-static int connect_ring(usbif_t *usbif)
+static int connect_rings(usbif_t *usbif)
{
struct xenbus_device *dev = usbif->xbdev;
- unsigned long ring_ref;
+ unsigned long urb_ring_ref;
+ unsigned long conn_ring_ref;
unsigned int evtchn;
int err;
err = xenbus_gather(XBT_NIL, dev->otherend,
- "ring-ref", "%lu", &ring_ref,
+ "urb-ring-ref", "%lu", &urb_ring_ref,
+ "conn-ring-ref", "%lu", &conn_ring_ref,
"event-channel", "%u", &evtchn, NULL);
if (err) {
xenbus_dev_fatal(dev, err,
@@ -121,81 +242,32 @@ static int connect_ring(usbif_t *usbif)
return err;
}
- printk("usbback: ring-ref %ld, event-channel %d\n",
- ring_ref, evtchn);
-
- err = usbif_map(usbif, ring_ref, evtchn);
+ printk("usbback: urb-ring-ref %ld, conn-ring-ref %ld, event-channel
%d\n",
+ urb_ring_ref, conn_ring_ref, evtchn);
+
+ err = usbif_map(usbif, urb_ring_ref, conn_ring_ref, evtchn);
if (err) {
- xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u",
- ring_ref, evtchn);
+ xenbus_dev_fatal(dev, err,
+ "mapping urb-ring-ref %lu conn-ring-ref %lu
port %u",
+ urb_ring_ref, conn_ring_ref, evtchn);
return err;
}
return 0;
}
-void usbback_do_hotplug(usbif_t *usbif)
-{
- struct xenbus_transaction xbt;
- struct xenbus_device *dev = usbif->xbdev;
- struct usbstub *stub = NULL;
- int err;
- char port_str[8];
- int i;
- int num_ports;
- int state;
-
-again:
- err = xenbus_transaction_start(&xbt);
- if (err) {
- xenbus_dev_fatal(dev, err, "starting transaction");
- return;
- }
-
- err = xenbus_scanf(xbt, dev->nodename,
- "num-ports", "%d", &num_ports);
-
- for (i = 1; i <= num_ports; i++) {
- stub = find_attached_device(usbif, i);
- if (stub)
- state = stub->udev->speed;
- else
- state = 0;
- sprintf(port_str, "port-%d", i);
- err = xenbus_printf(xbt, dev->nodename, port_str, "%d",
state);
- if (err) {
- xenbus_dev_fatal(dev, err, "writing port-%d
state", i);
- goto abort;
- }
- }
-
- err = xenbus_transaction_end(xbt, 0);
- if (err == -EAGAIN)
- goto again;
- if (err)
- xenbus_dev_fatal(dev, err, "completing transaction");
-
- return;
-
-abort:
- xenbus_transaction_end(xbt, 1);
-}
-
-void usbback_reconfigure(usbif_t *usbif)
-{
- struct xenbus_device *dev = usbif->xbdev;
-
- if (dev->state == XenbusStateConnected)
- xenbus_switch_state(dev, XenbusStateReconfiguring);
-}
-
-void frontend_changed(struct xenbus_device *dev,
+static void frontend_changed(struct xenbus_device *dev,
enum xenbus_state frontend_state)
{
usbif_t *usbif = dev->dev.driver_data;
int err;
switch (frontend_state) {
+ case XenbusStateInitialised:
+ case XenbusStateReconfiguring:
+ case XenbusStateReconfigured:
+ break;
+
case XenbusStateInitialising:
if (dev->state == XenbusStateClosed) {
printk("%s: %s: prepare for reconnect\n",
@@ -204,17 +276,18 @@ void frontend_changed(struct xenbus_devi
}
break;
- case XenbusStateInitialised:
- err = connect_ring(usbif);
- if (err)
- break;
- start_xenusbd(usbif);
- usbback_do_hotplug(usbif);
- xenbus_switch_state(dev, XenbusStateConnected);
- break;
-
case XenbusStateConnected:
if (dev->state == XenbusStateConnected)
+ break;
+ err = connect_rings(usbif);
+ if (err)
+ break;
+ err = start_xenusbd(usbif);
+ if (err)
+ break;
+ err = xenbus_watch_path2(dev, dev->nodename, "port",
+ &usbif->backend_watch, backend_changed);
+ if (err)
break;
xenbus_switch_state(dev, XenbusStateConnected);
break;
@@ -226,13 +299,9 @@ void frontend_changed(struct xenbus_devi
case XenbusStateClosed:
xenbus_switch_state(dev, XenbusStateClosed);
- break;
-
- case XenbusStateReconfiguring:
- usbback_do_hotplug(usbif);
- xenbus_switch_state(dev, XenbusStateReconfigured);
- break;
-
+ if (xenbus_dev_is_online(dev))
+ break;
+ /* fall through if not online */
case XenbusStateUnknown:
device_unregister(&dev->dev);
break;
diff -r 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbfront/usbfront-dbg.c
--- a/drivers/xen/usbfront/usbfront-dbg.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbfront/usbfront-dbg.c Wed Oct 07 08:42:00 2009 +0100
@@ -60,7 +60,7 @@ static ssize_t show_statistics(struct cl
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 @@ static ssize_t show_statistics(struct cl
#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 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbfront/usbfront-hcd.c
--- a/drivers/xen/usbfront/usbfront-hcd.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbfront/usbfront-hcd.c Wed Oct 07 08:42:00 2009 +0100
@@ -54,7 +54,7 @@ static void xenhcd_watchdog(unsigned lon
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 @@ static int xenhcd_setup(struct usb_hcd *
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,68 +102,12 @@ static void xenhcd_stop(struct usb_hcd *
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()
@@ -197,11 +142,6 @@ done:
/*
* 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 @@ static int xenhcd_get_frame(struct usb_h
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 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbfront/usbfront-hub.c
--- a/drivers/xen/usbfront/usbfront-hub.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbfront/usbfront-hub.c Wed Oct 07 08:42:00 2009 +0100
@@ -50,15 +50,16 @@ void set_connect_state(struct usbfront_i
{
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;
@@ -85,6 +86,9 @@ void rhport_connect(struct usbfront_info
int portnum, enum usb_device_speed speed)
{
int port;
+
+ if (portnum < 1 || portnum > info->rh_numports)
+ return; /* invalid port number */
port = portnum - 1;
if (info->devices[port].speed != speed) {
@@ -105,30 +109,6 @@ void rhport_connect(struct usbfront_info
set_connect_state(info, portnum);
}
-}
-
-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));
}
/*
@@ -214,7 +194,7 @@ void rhport_reset(struct usbfront_info *
{
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,56 +207,50 @@ void rhport_reset(struct usbfront_info *
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;
- }
-
- /* suspend any active ports*/
- for (i = 1; i <= ports; i++) {
- rhport_suspend(info, i);
- }
+ 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);
+ }
+ spin_unlock_irq(&info->lock);
del_timer_sync(&info->watchdog);
+ 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);
+ 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);
+ }
spin_unlock_irq(&info->lock);
- return 0;
-}
-
-static int xenhcd_bus_resume(struct usb_hcd *hcd)
-{
- struct usbfront_info *info = hcd_to_info(hcd);
- 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);
- }
- hcd->state = HC_STATE_RUNNING;
- spin_unlock_irq(&info->lock);
- return 0;
-}
-#endif
+ return ret;
+}
#endif
static void xenhcd_hub_descriptor(struct usbfront_info *info,
@@ -295,8 +269,8 @@ static void xenhcd_hub_descriptor(struct
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 @@ static int xenhcd_hub_control(struct usb
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 @@ static int xenhcd_hub_control(struct usb
if (!wIndex || wIndex > ports)
goto error;
- switch(wValue) {
+ switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
rhport_resume(info, wIndex);
break;
@@ -414,7 +383,7 @@ static int xenhcd_hub_control(struct usb
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 @@ static int xenhcd_hub_control(struct usb
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 @@ static int xenhcd_hub_control(struct usb
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 @@ static int xenhcd_hub_control(struct usb
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 @@ error:
/* 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 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbfront/usbfront-q.c
--- a/drivers/xen/usbfront/usbfront-q.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbfront/usbfront-q.c Wed Oct 07 08:42:00 2009 +0100
@@ -50,13 +50,13 @@ static struct urb_priv *alloc_urb_priv(s
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 @@ static inline int get_id_from_freelist(
{
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
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 @@ static inline void xenhcd_gnttab_map(str
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 @@ static inline void xenhcd_gnttab_map(str
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 inline void xenhcd_gnttab_map(str
}
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 @@ static int map_urb_for_request(struct us
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 @@ static void xenhcd_gnttab_done(struct us
for (i = 0; i < nr_segs; i++)
gnttab_end_foreign_access(shadow->req.seg[i].gref, 0UL);
-}
-
-static void xenhcd_giveback_urb(struct usbfront_info *info, struct urb *urb)
+
+ 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,
int status)
__releases(info->lock)
__acquires(info->lock)
{
@@ -228,6 +229,9 @@ __acquires(info->lock)
case -ENOENT:
COUNT(info->stats.unlink);
break;
+ case -EINPROGRESS:
+ urb->status = status;
+ /* falling through */
default:
COUNT(info->stats.complete);
}
@@ -238,28 +242,35 @@ __acquires(info->lock)
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;
-
- ret = map_urb_for_request(info, urb, ring_req);
- if (ret < 0) {
- add_id_to_freelist(info, id);
- return ret;
- }
-
- info->ring.req_prod_pvt++;
+ req->id = id;
+
+ 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->urb_ring.req_prod_pvt++;
info->shadow[id].urb = urb;
- info->shadow[id].req = *ring_req;
- urbp->req_id = id;
-
- RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->ring, notify);
+ info->shadow[id].req = *req;
+
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->urb_ring, notify);
if (notify)
notify_remote_via_irq(info->irq);
@@ -271,19 +282,19 @@ static void xenhcd_kick_pending_urbs(str
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 @@ done:
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 @@ static int xenhcd_submit_urb(struct usbf
{
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 @@ done:
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);
- }
-
- /* TODO: send cancel request to backend */
-
- return 0;
-}
-
-static int xenhcd_end_submit_urb(struct usbfront_info *info)
-{
- usbif_response_t *ring_res;
+ goto done;
+ }
+
+ /* 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;
+ }
+
+ 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_urb_request_done(struct usbfront_info *info)
+{
+ usbif_urb_response_t *res;
struct urb *urb;
- struct urb_priv *urbp;
RING_IDX i, rp;
uint16_t id;
@@ -354,35 +415,92 @@ static int xenhcd_end_submit_urb(struct
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;
+ 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;
+
+ if (i != info->urb_ring.req_prod_pvt)
+ RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do);
+ else
+ info->urb_ring.sring->rsp_event = i + 1;
+
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ cond_resched();
+
+ 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();
- add_id_to_freelist(info, id);
-
- 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);
+
+ 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->ring.sring->rsp_event = i + 1;
+ 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();
@@ -400,7 +518,10 @@ int xenhcd_schedule(void *arg)
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 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbfront/usbfront.h
--- a/drivers/xen/usbfront/usbfront.h Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbfront/usbfront.h Wed Oct 07 08:42:00 2009 +0100
@@ -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 usbfront_info *hcd_
static inline struct usb_hcd *info_to_hcd(struct usbfront_info *info)
{
- return container_of ((void *) info, struct usb_hcd, hcd_priv);
-}
-
-/*
- * Private per-URB data
- */
+ return container_of((void *) info, struct usb_hcd, hcd_priv);
+}
+
+/* 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 @@ struct vdevice_status {
/* RING request shadow */
struct usb_shadow {
- usbif_request_t req;
+ usbif_urb_request_t req;
struct urb *urb;
};
@@ -117,62 +114,46 @@ struct xenhcd_stats {
};
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 @@ timer_action(struct usbfront_info *info,
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 @@ timer_action(struct usbfront_info *info,
}
}
+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 7d57b5843b65 -r 498ac445a2a9 drivers/xen/usbfront/xenbus.c
--- a/drivers/xen/usbfront/xenbus.c Wed Oct 07 07:33:40 2009 +0100
+++ b/drivers/xen/usbfront/xenbus.c Wed Oct 07 08:42:00 2009 +0100
@@ -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)
-{
- 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;
- }
+static void destroy_rings(struct usbfront_info *info)
+{
if (info->irq)
unbind_from_irqhandler(info->irq, info);
info->irq = 0;
-}
-
-static int setup_usbring(struct xenbus_device *dev,
+
+ 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_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;
-
- sring = (usbif_sring_t *)get_zeroed_page(GFP_NOIO|__GFP_HIGH);
- if (!sring) {
- xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
+ info->urb_ring_ref = GRANT_INVALID_REF;
+ info->conn_ring_ref = GRANT_INVALID_REF;
+
+ 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);
-
- err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
+ SHARED_RING_INIT(urb_sring);
+ FRONT_RING_INIT(&info->urb_ring, urb_sring, PAGE_SIZE);
+
+ err = xenbus_grant_ring(dev, virt_to_mfn(info->urb_ring.sring));
if (err < 0) {
- free_page((unsigned long)sring);
- info->ring.sring = NULL;
- goto fail;
- }
- info->ring_ref = err;
+ free_page((unsigned long)urb_sring);
+ info->urb_ring.sring = NULL;
+ goto fail;
+ }
+ 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 @@ static int setup_usbring(struct xenbus_d
return 0;
fail:
- usbif_free(info);
+ destroy_rings(info);
return err;
}
@@ -112,7 +132,7 @@ static int talk_to_backend(struct xenbus
struct xenbus_transaction xbt;
int err;
- err = setup_usbring(dev, info);
+ err = setup_rings(dev, info);
if (err)
goto out;
@@ -123,10 +143,17 @@ again:
goto destroy_ring;
}
- err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u",
- info->ring_ref);
- if (err) {
- message = "writing ring-ref";
+ err = xenbus_printf(xbt, dev->nodename, "urb-ring-ref", "%u",
+ info->urb_ring_ref);
+ if (err) {
+ 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 @@ again:
goto destroy_ring;
}
- xenbus_switch_state(dev, XenbusStateInitialised);
-
return 0;
abort_transaction:
@@ -154,10 +179,37 @@ abort_transaction:
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)
@@ -165,6 +217,7 @@ static struct usb_hcd *create_hcd(struct
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 @@ static struct usb_hcd *create_hcd(struct
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 @@ static int usbfront_probe(struct xenbus_
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 @@ static int usbfront_probe(struct xenbus_
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)
- goto fail;
+ if (IS_ERR(info->kthread)) {
+ err = PTR_ERR(info->kthread);
+ info->kthread = NULL;
+ goto fail;
+ }
return 0;
@@ -245,68 +314,7 @@ fail:
return err;
}
-/*
- * 0=disconnected, 1=low_speed, 2=full_speed, 3=high_speed
- */
-static void usbfront_do_hotplug(struct usbfront_info *info)
-{
- char port_str[8];
- int i;
- int err;
- int state;
-
- 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);
- }
-}
-
-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 XenbusStateUnknown:
- case XenbusStateClosed:
- break;
-
- case XenbusStateConnected:
- if (dev->state == XenbusStateConnected)
- break;
- if (dev->state == XenbusStateInitialised)
- usbfront_do_hotplug(info);
- 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);
- break;
-
- default:
- xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
- backend_state);
- break;
- }
-}
-
-static int usbfront_remove(struct xenbus_device *dev)
+static void usbfront_disconnect(struct xenbus_device *dev)
{
struct usbfront_info *info = dev->dev.driver_data;
struct usb_hcd *hcd = info_to_hcd(info);
@@ -316,7 +324,46 @@ static int usbfront_remove(struct xenbus
kthread_stop(info->kthread);
info->kthread = NULL;
}
- usbif_free(info);
+ xenbus_frontend_closed(dev);
+}
+
+static void backend_changed(struct xenbus_device *dev,
+ enum xenbus_state backend_state)
+{
+ switch (backend_state) {
+ case XenbusStateInitialising:
+ case XenbusStateInitialised:
+ case XenbusStateConnected:
+ case XenbusStateReconfiguring:
+ case XenbusStateReconfigured:
+ case XenbusStateUnknown:
+ case XenbusStateClosed:
+ break;
+
+ case XenbusStateInitWait:
+ if (dev->state != XenbusStateInitialising)
+ break;
+ connect(dev);
+ xenbus_switch_state(dev, XenbusStateConnected);
+ break;
+
+ case XenbusStateClosing:
+ usbfront_disconnect(dev);
+ break;
+
+ default:
+ xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+ backend_state);
+ break;
+ }
+}
+
+static int usbfront_remove(struct xenbus_device *dev)
+{
+ struct usbfront_info *info = dev->dev.driver_data;
+ struct usb_hcd *hcd = info_to_hcd(info);
+
+ destroy_rings(info);
usb_put_hcd(hcd);
return 0;
@@ -361,5 +408,5 @@ module_exit(usbfront_exit);
module_exit(usbfront_exit);
MODULE_AUTHOR("");
-MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_DESCRIPTION("Xen USB Virtual Host Controller driver (usbfront)");
MODULE_LICENSE("Dual BSD/GPL");
diff -r 7d57b5843b65 -r 498ac445a2a9 include/xen/interface/io/usbif.h
--- a/include/xen/interface/io/usbif.h Wed Oct 07 07:33:40 2009 +0100
+++ b/include/xen/interface/io/usbif.h Wed Oct 07 08:42:00 2009 +0100
@@ -31,6 +31,13 @@
#include "ring.h"
#include "../grant_table.h"
+enum usb_spec_version {
+ USB_VER_UNKNOWN = 0,
+ USB_VER_USB11,
+ USB_VER_USB20,
+ USB_VER_USB30, /* not supported yet */
+};
+
/*
* USB pipe in usbif_request
*
@@ -57,21 +64,26 @@
* 10 = control, 11 = bulk)
*/
#define usbif_pipeportnum(pipe) ((pipe) & 0x1f)
-#define usbif_setportnum_pipe(pipe,portnum) \
+#define usbif_setportnum_pipe(pipe, portnum) \
((pipe)|(portnum))
+
#define usbif_pipeunlink(pipe) ((pipe) & 0x20)
+#define usbif_pipesubmit(pipe) (!usbif_pipeunlink(pipe))
#define usbif_setunlink_pipe(pipe) ((pipe)|(0x20))
#define USBIF_BACK_MAX_PENDING_REQS (128)
-#define USBIF_MAX_SEGMENTS_PER_REQUEST (10)
+#define USBIF_MAX_SEGMENTS_PER_REQUEST (16)
+/*
+ * RING for transferring urbs.
+ */
struct usbif_request_segment {
grant_ref_t gref;
uint16_t offset;
uint16_t length;
};
-struct usbif_request {
+struct usbif_urb_request {
uint16_t id; /* request id */
uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */
@@ -104,18 +116,36 @@ struct usbif_request {
/* urb data segments */
struct usbif_request_segment seg[USBIF_MAX_SEGMENTS_PER_REQUEST];
};
-typedef struct usbif_request usbif_request_t;
+typedef struct usbif_urb_request usbif_urb_request_t;
-struct usbif_response {
+struct usbif_urb_response {
uint16_t id; /* request id */
uint16_t start_frame; /* start frame (ISO) */
int32_t status; /* status (non-ISO) */
int32_t actual_length; /* actual transfer length */
int32_t error_count; /* number of ISO errors */
};
-typedef struct usbif_response usbif_response_t;
+typedef struct usbif_urb_response usbif_urb_response_t;
-DEFINE_RING_TYPES(usbif, struct usbif_request, struct usbif_response);
-#define USB_RING_SIZE __RING_SIZE((struct usbif_sring *)0, PAGE_SIZE)
+DEFINE_RING_TYPES(usbif_urb, struct usbif_urb_request, struct
usbif_urb_response);
+#define USB_URB_RING_SIZE __RING_SIZE((struct usbif_urb_sring *)0, PAGE_SIZE)
+
+/*
+ * RING for notifying connect/disconnect events to frontend
+ */
+struct usbif_conn_request {
+ uint16_t id;
+};
+typedef struct usbif_conn_request usbif_conn_request_t;
+
+struct usbif_conn_response {
+ uint16_t id; /* request id */
+ uint8_t portnum; /* port number */
+ uint8_t speed; /* usb_device_speed */
+};
+typedef struct usbif_conn_response usbif_conn_response_t;
+
+DEFINE_RING_TYPES(usbif_conn, struct usbif_conn_request, struct
usbif_conn_response);
+#define USB_CONN_RING_SIZE __RING_SIZE((struct usbif_conn_sring *)0, PAGE_SIZE)
#endif /* __XEN_PUBLIC_IO_USBIF_H__ */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|