WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

[Xen-changelog] [linux-2.6.18-xen] PVUSB: backend driver

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] [linux-2.6.18-xen] PVUSB: backend driver
From: "Xen patchbot-linux-2.6.18-xen" <patchbot-linux-2.6.18-xen@xxxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Mar 2009 07:25:31 -0700
Delivery-date: Wed, 18 Mar 2009 07:27:50 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1237376604 0
# Node ID f799db0570f2e8a81142f7940184c1557a4f7f40
# Parent  8f996719f2ffb35354b82e1540eda1b3928136da
PVUSB: backend driver

Signed-off-by: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
---
 drivers/xen/usbback/Makefile    |    4 
 drivers/xen/usbback/interface.c |  208 +++++++
 drivers/xen/usbback/usbback.c   | 1075 ++++++++++++++++++++++++++++++++++++++++
 drivers/xen/usbback/usbback.h   |  158 +++++
 drivers/xen/usbback/usbstub.c   |  447 ++++++++++++++++
 drivers/xen/usbback/xenbus.c    |  269 ++++++++++
 6 files changed, 2161 insertions(+)

diff -r 8f996719f2ff -r f799db0570f2 drivers/xen/usbback/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/Makefile      Wed Mar 18 11:43:24 2009 +0000
@@ -0,0 +1,4 @@
+obj-$(CONFIG_XEN_USB_BACKEND) := usbbk.o
+
+usbbk-y   := usbstub.o xenbus.o interface.o usbback.o
+
diff -r 8f996719f2ff -r f799db0570f2 drivers/xen/usbback/interface.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/interface.c   Wed Mar 18 11:43:24 2009 +0000
@@ -0,0 +1,208 @@
+/*
+ * interface.c
+ *
+ * Xen USB backend interface management.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * or,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "usbback.h"
+
+static LIST_HEAD(usbif_list);
+static DEFINE_SPINLOCK(usbif_list_lock);
+
+usbif_t *find_usbif(int dom_id, int dev_id)
+{
+       usbif_t *usbif;
+       int found = 0;
+       unsigned long flags;
+
+       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) {
+                       found = 1;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&usbif_list_lock, flags);
+
+       if (found)
+               return usbif;
+
+       return NULL;
+}
+
+usbif_t *usbif_alloc(domid_t domid, unsigned int handle)
+{
+       usbif_t *usbif;
+       unsigned long flags;
+       int i;
+
+       usbif = kzalloc(sizeof(usbif_t), GFP_KERNEL);
+       if (!usbif)
+               return NULL;
+
+       usbif->domid = domid;
+       usbif->handle = handle;
+       spin_lock_init(&usbif->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->addr_lock);
+       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);
+       spin_unlock_irqrestore(&usbif_list_lock, flags);
+
+       return usbif;
+}
+
+static int map_frontend_page(usbif_t *usbif, unsigned long shared_page)
+{
+       struct gnttab_map_grant_ref op;
+
+       gnttab_set_map_op(&op, (unsigned long)usbif->ring_area->addr,
+                         GNTMAP_host_map, shared_page, 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");
+               return op.status;
+       }
+
+       usbif->shmem_ref = shared_page;
+       usbif->shmem_handle = op.handle;
+
+       return 0;
+}
+
+static void unmap_frontend_page(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);
+
+       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;
+
+       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);
+               return err;
+       }
+
+       sring = (usbif_sring_t *) usbif->ring_area->addr;
+       BACK_RING_INIT(&usbif->ring, sring, PAGE_SIZE);
+
+       err = bind_interdomain_evtchn_to_irqhandler(
+                       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;
+       }
+       usbif->irq = err;
+
+       return 0;
+}
+
+void usbif_disconnect(usbif_t *usbif)
+{
+       struct usbstub *stub, *tmp;
+       unsigned long flags;
+
+       if (usbif->xenusbd) {
+               kthread_stop(usbif->xenusbd);
+               usbif->xenusbd = NULL;
+       }
+
+       spin_lock_irqsave(&usbif->plug_lock, flags);
+       list_for_each_entry_safe(stub, tmp, &usbif->plugged_devices, 
plugged_list) {
+               usbbk_unlink_urbs(stub);
+               detach_device_without_lock(usbif, stub);
+       }
+       spin_unlock_irqrestore(&usbif->plug_lock, flags);
+
+       wait_event(usbif->waiting_to_free, atomic_read(&usbif->refcnt) == 0);
+
+       if (usbif->irq) {
+               unbind_from_irqhandler(usbif->irq, usbif);
+               usbif->irq = 0;
+       }
+
+       if (usbif->ring.sring) {
+               unmap_frontend_page(usbif);
+               free_vm_area(usbif->ring_area);
+               usbif->ring.sring = NULL;
+       }
+}
+
+void usbif_free(usbif_t *usbif)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&usbif_list_lock, flags);
+       list_del(&usbif->usbif_list);
+       spin_unlock_irqrestore(&usbif_list_lock, flags);
+       kfree(usbif);
+}
diff -r 8f996719f2ff -r f799db0570f2 drivers/xen/usbback/usbback.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/usbback.c     Wed Mar 18 11:43:24 2009 +0000
@@ -0,0 +1,1075 @@
+/*
+ * usbback.c
+ *
+ * Xen USB backend driver
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * or,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/mm.h>
+#include <xen/balloon.h>
+#include "usbback.h"
+
+#if 0
+#include "../../usb/core/hub.h"
+#endif
+
+int usbif_reqs = USBIF_BACK_MAX_PENDING_REQS;
+module_param_named(reqs, usbif_reqs, int, 0);
+MODULE_PARM_DESC(reqs, "Number of usbback requests to allocate");
+
+struct pending_req_segment {
+       uint16_t offset;
+       uint16_t length;
+};
+
+typedef struct {
+       usbif_t *usbif;
+
+       uint16_t id; /* request id */
+
+       struct usbstub *stub;
+       struct list_head urb_list;
+
+       /* urb */
+       struct urb *urb;
+       void *buffer;
+       dma_addr_t transfer_dma;
+       struct usb_ctrlrequest *setup;
+       dma_addr_t setup_dma;
+
+       /* request segments */
+       uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */
+       uint16_t nr_extra_segs; /* number of iso_frame_desc segments (ISO) */
+       struct pending_req_segment *seg;
+
+       struct list_head free_list;
+} pending_req_t;
+
+static pending_req_t *pending_reqs;
+static struct list_head pending_free;
+static DEFINE_SPINLOCK(pending_free_lock);
+static DECLARE_WAIT_QUEUE_HEAD(pending_free_wq);
+
+#define USBBACK_INVALID_HANDLE (~0)
+
+static struct page **pending_pages;
+static grant_handle_t *pending_grant_handles;
+
+static inline int vaddr_pagenr(pending_req_t *req, int seg)
+{
+       return (req - pending_reqs) * USBIF_MAX_SEGMENTS_PER_REQUEST + seg;
+}
+
+static inline unsigned long vaddr(pending_req_t *req, int seg)
+{
+       unsigned long pfn = page_to_pfn(pending_pages[vaddr_pagenr(req, seg)]);
+       return (unsigned long)pfn_to_kaddr(pfn);
+}
+
+#define pending_handle(_req, _seg) \
+       (pending_grant_handles[vaddr_pagenr(_req, _seg)])
+
+static pending_req_t* alloc_req(void)
+{
+       pending_req_t *req = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pending_free_lock, flags);
+       if (!list_empty(&pending_free)) {
+               req = list_entry(pending_free.next, pending_req_t, free_list);
+               list_del(&req->free_list);
+       }
+       spin_unlock_irqrestore(&pending_free_lock, flags);
+       return req;
+}
+
+static void free_req(pending_req_t *req)
+{
+       unsigned long flags;
+       int was_empty;
+
+       spin_lock_irqsave(&pending_free_lock, flags);
+       was_empty = list_empty(&pending_free);
+       list_add(&req->free_list, &pending_free);
+       spin_unlock_irqrestore(&pending_free_lock, flags);
+       if (was_empty)
+               wake_up(&pending_free_wq);
+}
+
+static inline void add_req_to_submitting_list(struct usbstub *stub, 
pending_req_t *pending_req)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&stub->submitting_lock, flags);
+       list_add_tail(&pending_req->urb_list, &stub->submitting_list);
+       spin_unlock_irqrestore(&stub->submitting_lock, flags);
+}
+
+static inline void remove_req_from_submitting_list(struct usbstub *stub, 
pending_req_t *pending_req)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&stub->submitting_lock, flags);
+       list_del_init(&pending_req->urb_list);
+       spin_unlock_irqrestore(&stub->submitting_lock, flags);
+}
+
+void usbbk_unlink_urbs(struct usbstub *stub)
+{
+       pending_req_t *req, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&stub->submitting_lock, flags);
+       list_for_each_entry_safe(req, tmp, &stub->submitting_list, urb_list) {
+               usb_unlink_urb(req->urb);
+       }
+       spin_unlock_irqrestore(&stub->submitting_lock, flags);
+}
+
+static void fast_flush_area(pending_req_t *pending_req)
+{
+       struct gnttab_unmap_grant_ref unmap[USBIF_MAX_SEGMENTS_PER_REQUEST];
+       unsigned int i, nr_segs, invcount = 0;
+       grant_handle_t handle;
+       int ret;
+
+       nr_segs = pending_req->nr_buffer_segs + pending_req->nr_extra_segs;
+
+       if (nr_segs) {
+               for (i = 0; i < nr_segs; i++) {
+                       handle = pending_handle(pending_req, i);
+                       if (handle == USBBACK_INVALID_HANDLE)
+                               continue;
+                       gnttab_set_unmap_op(&unmap[invcount], 
vaddr(pending_req, i),
+                                           GNTMAP_host_map, handle);
+                       pending_handle(pending_req, i) = USBBACK_INVALID_HANDLE;
+                       invcount++;
+               }
+
+               ret = HYPERVISOR_grant_table_op(
+                       GNTTABOP_unmap_grant_ref, unmap, invcount);
+               BUG_ON(ret);
+
+               kfree(pending_req->seg);
+       }
+
+       return;
+}
+
+static void copy_buff_to_pages(void *buff, pending_req_t *pending_req,
+               int start, int nr_pages)
+{
+       unsigned long copied = 0;
+       int i;
+
+       for (i = start; i < start + nr_pages; i++) {
+               memcpy((void *) vaddr(pending_req, i) + 
pending_req->seg[i].offset,
+                       buff + copied,
+                       pending_req->seg[i].length);
+               copied += pending_req->seg[i].length;
+       }
+}
+
+static void copy_pages_to_buff(void *buff, pending_req_t *pending_req,
+               int start, int nr_pages)
+{
+       unsigned long copied = 0;
+       int i;
+
+       for (i = start; i < start + nr_pages; i++) {
+               memcpy(buff + copied,
+                       (void *) vaddr(pending_req, i) + 
pending_req->seg[i].offset,
+                       pending_req->seg[i].length);
+               copied += pending_req->seg[i].length;
+       }
+}
+
+static int usbbk_alloc_urb(usbif_request_t *req, pending_req_t *pending_req)
+{
+       int ret;
+
+       if (usb_pipeisoc(req->pipe))
+               pending_req->urb = usb_alloc_urb(req->u.isoc.number_of_packets, 
GFP_KERNEL);
+       else
+               pending_req->urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!pending_req->urb) {
+               printk(KERN_ERR "usbback: can't alloc urb\n");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       if (req->buffer_length) {
+               pending_req->buffer = usb_buffer_alloc(pending_req->stub->udev,
+                               req->buffer_length, GFP_KERNEL,
+                               &pending_req->transfer_dma);
+               if (!pending_req->buffer) {
+                       printk(KERN_ERR "usbback: can't alloc urb buffer\n");
+                       ret = -ENOMEM;
+                       goto fail_free_urb;
+               }
+       }
+
+       if (usb_pipecontrol(req->pipe)) {
+               pending_req->setup = usb_buffer_alloc(pending_req->stub->udev,
+                               sizeof(struct usb_ctrlrequest), GFP_KERNEL,
+                               &pending_req->setup_dma);
+               if (!pending_req->setup) {
+                       printk(KERN_ERR "usbback: can't alloc 
usb_ctrlrequest\n");
+                       ret = -ENOMEM;
+                       goto fail_free_buffer;
+               }
+       }
+
+       return 0;
+
+fail_free_buffer:
+       if (req->buffer_length)
+               usb_buffer_free(pending_req->stub->udev, req->buffer_length,
+                               pending_req->buffer, pending_req->transfer_dma);
+fail_free_urb:
+       usb_free_urb(pending_req->urb);
+fail:
+       return ret;
+}
+
+static void usbbk_free_urb(struct urb *urb)
+{
+       if (usb_pipecontrol(urb->pipe))
+               usb_buffer_free(urb->dev, sizeof(struct usb_ctrlrequest),
+                               urb->setup_packet, urb->setup_dma);
+       if (urb->transfer_buffer_length)
+               usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+                               urb->transfer_buffer, urb->transfer_dma);
+       barrier();
+       usb_free_urb(urb);
+}
+
+static void usbbk_notify_work(usbif_t *usbif)
+{
+       usbif->waiting_reqs = 1;
+       wake_up(&usbif->wq);
+}
+
+irqreturn_t usbbk_be_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+       usbbk_notify_work(dev_id);
+       return IRQ_HANDLED;
+}
+
+static void usbbk_do_response(pending_req_t *pending_req, int32_t status,
+                                       int32_t actual_length, int32_t 
error_count, uint16_t start_frame)
+{
+       usbif_t *usbif = pending_req->usbif;
+       usbif_response_t *ring_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);
+
+       if (notify)
+               notify_remote_via_irq(usbif->irq);
+}
+
+static void usbbk_urb_complete(struct urb *urb, struct pt_regs *regs)
+{
+       pending_req_t *pending_req = (pending_req_t *)urb->context;
+
+       if (usb_pipein(urb->pipe) && urb->status == 0 && urb->actual_length > 0)
+               copy_buff_to_pages(pending_req->buffer, pending_req,
+                                       0, pending_req->nr_buffer_segs);
+
+       if (usb_pipeisoc(urb->pipe))
+               copy_buff_to_pages(&urb->iso_frame_desc[0], pending_req,
+                                       pending_req->nr_buffer_segs, 
pending_req->nr_extra_segs);
+
+       barrier();
+
+       fast_flush_area(pending_req);
+
+       usbbk_do_response(pending_req, urb->status, urb->actual_length,
+                                       urb->error_count, urb->start_frame);
+
+       remove_req_from_submitting_list(pending_req->stub, pending_req);
+
+       barrier();
+       usbbk_free_urb(urb);
+       usbif_put(pending_req->usbif);
+       free_req(pending_req);
+}
+
+static int usbbk_gnttab_map(usbif_t *usbif,
+                       usbif_request_t *req, pending_req_t *pending_req)
+{
+       int i, ret;
+       unsigned int nr_segs;
+       uint32_t flags;
+       struct gnttab_map_grant_ref map[USBIF_MAX_SEGMENTS_PER_REQUEST];
+
+       nr_segs = pending_req->nr_buffer_segs + pending_req->nr_extra_segs;
+
+       if (nr_segs > USBIF_MAX_SEGMENTS_PER_REQUEST) {
+               printk(KERN_ERR "Bad number of segments in request\n");
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       if (nr_segs) {
+               pending_req->seg = kmalloc(sizeof(struct pending_req_segment)
+                               * nr_segs, GFP_KERNEL);
+               if (!pending_req->seg) {
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               if (pending_req->nr_buffer_segs) {
+                       flags = GNTMAP_host_map;
+                       if (usb_pipeout(req->pipe))
+                               flags |= GNTMAP_readonly;
+                       for (i = 0; i < pending_req->nr_buffer_segs; i++)
+                               gnttab_set_map_op(&map[i], vaddr(
+                                               pending_req, i), flags,
+                                               req->seg[i].gref,
+                                               usbif->domid);
+               }
+
+               if (pending_req->nr_extra_segs) {
+                       flags = GNTMAP_host_map;
+                       for (i = req->nr_buffer_segs; i < nr_segs; i++)
+                               gnttab_set_map_op(&map[i], vaddr(
+                                               pending_req, i), flags,
+                                               req->seg[i].gref,
+                                               usbif->domid);
+               }
+
+               ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref,
+                                       map, nr_segs);
+               BUG_ON(ret);
+
+               for (i = 0; i < nr_segs; i++) {
+                       if (unlikely(map[i].status != 0)) {
+                               printk(KERN_ERR "usbback: invalid buffer -- 
could not remap it\n");
+                               map[i].handle = USBBACK_INVALID_HANDLE;
+                               ret |= 1;
+                       }
+
+                       pending_handle(pending_req, i) = map[i].handle;
+
+                       if (ret)
+                               continue;
+
+                       set_phys_to_machine(__pa(vaddr(
+                               pending_req, i)) >> PAGE_SHIFT,
+                               FOREIGN_FRAME(map[i].dev_bus_addr >> 
PAGE_SHIFT));
+
+                       pending_req->seg[i].offset = req->seg[i].offset;
+                       pending_req->seg[i].length = req->seg[i].length;
+
+                       barrier();
+
+                       if (pending_req->seg[i].offset >= PAGE_SIZE ||
+                                       pending_req->seg[i].length > PAGE_SIZE 
||
+                                       pending_req->seg[i].offset + 
pending_req->seg[i].length > PAGE_SIZE)
+                                       ret |= 1;
+               }
+
+               if (ret)
+                       goto fail_flush;
+       }
+
+       return 0;
+
+fail_flush:
+       fast_flush_area(pending_req);
+       ret = -ENOMEM;
+
+fail:
+       return ret;
+}
+
+static void usbbk_init_urb(usbif_request_t *req, pending_req_t *pending_req)
+{
+       unsigned int pipe;
+       struct usb_device *udev = pending_req->stub->udev;
+       struct urb *urb = pending_req->urb;
+
+       switch (usb_pipetype(req->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               if (usb_pipein(req->pipe))
+                       pipe = usb_rcvisocpipe(udev, 
usb_pipeendpoint(req->pipe));
+               else
+                       pipe = usb_sndisocpipe(udev, 
usb_pipeendpoint(req->pipe));
+
+               urb->dev = udev;
+               urb->pipe = pipe;
+               urb->transfer_flags = req->transfer_flags;
+               urb->transfer_flags |= URB_ISO_ASAP;
+               urb->transfer_buffer = pending_req->buffer;
+               urb->transfer_buffer_length = req->buffer_length;
+               urb->complete = usbbk_urb_complete;
+               urb->context = pending_req;
+               urb->interval = req->u.isoc.interval;
+               urb->start_frame = req->u.isoc.start_frame;
+               urb->number_of_packets = req->u.isoc.number_of_packets;
+
+               break;
+       case PIPE_INTERRUPT:
+               if (usb_pipein(req->pipe))
+                       pipe = usb_rcvintpipe(udev, 
usb_pipeendpoint(req->pipe));
+               else
+                       pipe = usb_sndintpipe(udev, 
usb_pipeendpoint(req->pipe));
+
+               usb_fill_int_urb(urb, udev, pipe,
+                               pending_req->buffer, req->buffer_length,
+                               usbbk_urb_complete,
+                               pending_req, req->u.intr.interval);
+               urb->transfer_flags = req->transfer_flags;
+
+               break;
+       case PIPE_CONTROL:
+               if (usb_pipein(req->pipe))
+                       pipe = usb_rcvctrlpipe(udev, 0);
+               else
+                       pipe = usb_sndctrlpipe(udev, 0);
+
+               usb_fill_control_urb(urb, udev, pipe,
+                               (unsigned char *) pending_req->setup,
+                               pending_req->buffer, req->buffer_length,
+                               usbbk_urb_complete, pending_req);
+               memcpy(pending_req->setup, req->u.ctrl, 8);
+               urb->setup_dma = pending_req->setup_dma;
+               urb->transfer_flags = req->transfer_flags;
+               urb->transfer_flags |= URB_NO_SETUP_DMA_MAP;
+
+               break;
+       case PIPE_BULK:
+               if (usb_pipein(req->pipe))
+                       pipe = usb_rcvbulkpipe(udev, 
usb_pipeendpoint(req->pipe));
+               else
+                       pipe = usb_sndbulkpipe(udev, 
usb_pipeendpoint(req->pipe));
+
+               usb_fill_bulk_urb(urb, udev, pipe,
+                               pending_req->buffer, req->buffer_length,
+                               usbbk_urb_complete, pending_req);
+               urb->transfer_flags = req->transfer_flags;
+
+               break;
+       default:
+               break;
+       }
+
+       if (req->buffer_length) {
+               urb->transfer_dma = pending_req->transfer_dma;
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       }
+}
+
+struct set_interface_request {
+       pending_req_t *pending_req;
+       int interface;
+       int alternate;
+       struct work_struct work;
+};
+
+static void usbbk_set_interface_work(void *data)
+{
+       struct set_interface_request *req = (struct set_interface_request *) 
data;
+       pending_req_t *pending_req = req->pending_req;
+       struct usb_device *udev = req->pending_req->stub->udev;
+
+       int ret;
+
+       usb_lock_device(udev);
+       ret = usb_set_interface(udev, req->interface, req->alternate);
+       usb_unlock_device(udev);
+       usb_put_dev(udev);
+
+       usbbk_do_response(pending_req, ret, 0, 0, 0);
+       usbif_put(pending_req->usbif);
+       free_req(pending_req);
+       kfree(req);
+}
+
+static int usbbk_set_interface(pending_req_t *pending_req, int interface, int 
alternate)
+{
+       struct set_interface_request *req;
+       struct usb_device *udev = pending_req->stub->udev;
+
+       req = kmalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+       req->pending_req = pending_req;
+       req->interface = interface;
+       req->alternate = alternate;
+       INIT_WORK(&req->work, usbbk_set_interface_work, req);
+       usb_get_dev(udev);
+       schedule_work(&req->work);
+       return 0;
+}
+
+struct clear_halt_request {
+       pending_req_t *pending_req;
+       int pipe;
+       struct work_struct work;
+};
+
+static void usbbk_clear_halt_work(void *data)
+{
+       struct clear_halt_request *req = (struct clear_halt_request *) data;
+       pending_req_t *pending_req = req->pending_req;
+       struct usb_device *udev = req->pending_req->stub->udev;
+       int ret;
+
+       usb_lock_device(udev);
+       ret = usb_clear_halt(req->pending_req->stub->udev, req->pipe);
+       usb_unlock_device(udev);
+       usb_put_dev(udev);
+
+       usbbk_do_response(pending_req, ret, 0, 0, 0);
+       usbif_put(pending_req->usbif);
+       free_req(pending_req);
+       kfree(req);
+}
+
+static int usbbk_clear_halt(pending_req_t *pending_req, int pipe)
+{
+       struct clear_halt_request *req;
+       struct usb_device *udev = pending_req->stub->udev;
+
+       req = kmalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+       req->pending_req = pending_req;
+       req->pipe = pipe;
+       INIT_WORK(&req->work, usbbk_clear_halt_work, req);
+
+       usb_get_dev(udev);
+       schedule_work(&req->work);
+       return 0;
+}
+
+#if 0
+struct port_reset_request {
+       pending_req_t *pending_req;
+       struct work_struct work;
+};
+
+static void usbbk_port_reset_work(void *data)
+{
+       struct port_reset_request *req = (struct port_reset_request *) data;
+       pending_req_t *pending_req = req->pending_req;
+       struct usb_device *udev = pending_req->stub->udev;
+       int ret, ret_lock;
+
+       ret = ret_lock = usb_lock_device_for_reset(udev, NULL);
+       if (ret_lock >= 0) {
+               ret = usb_reset_device(udev);
+               if (ret_lock)
+                       usb_unlock_device(udev);
+       }
+       usb_put_dev(udev);
+
+       usbbk_do_response(pending_req, ret, 0, 0, 0);
+       usbif_put(pending_req->usbif);
+       free_req(pending_req);
+       kfree(req);
+}
+
+static int usbbk_port_reset(pending_req_t *pending_req)
+{
+       struct port_reset_request *req;
+       struct usb_device *udev = pending_req->stub->udev;
+
+       req = kmalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       req->pending_req = pending_req;
+       INIT_WORK(&req->work, usbbk_port_reset_work, req);
+
+       usb_get_dev(udev);
+       schedule_work(&req->work);
+       return 0;
+}
+#endif
+
+static void usbbk_set_address(usbif_t *usbif, struct usbstub *stub, int 
cur_addr, int new_addr)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&usbif->addr_lock, flags);
+       if (cur_addr)
+               usbif->addr_table[cur_addr] = NULL;
+       if (new_addr)
+               usbif->addr_table[new_addr] = stub;
+       stub->addr = new_addr;
+       spin_unlock_irqrestore(&usbif->addr_lock, flags);
+}
+
+struct usbstub *find_attached_device(usbif_t *usbif, int portnum)
+{
+       struct usbstub *stub;
+       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) {
+                       found = 1;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&usbif->plug_lock, flags);
+
+       if (found)
+               return stub;
+
+       return NULL;
+}
+
+static int check_and_submit_special_ctrlreq(usbif_t *usbif, usbif_request_t 
*req, pending_req_t *pending_req)
+{
+       int devnum;
+       struct usbstub *stub = NULL;
+       struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *) req->u.ctrl;
+       int ret;
+       int done = 0;
+
+       devnum = usb_pipedevice(req->pipe);
+
+       /*
+        * When the device is first connected or reseted, USB device has no 
address.
+        * In this initial state, following requests are send to device address 
(#0),
+        *
+        *  1. GET_DESCRIPTOR (with Descriptor Type is "DEVICE") is send,
+        *     and OS knows what device is connected to.
+        *
+        *  2. SET_ADDRESS is send, and then, device has its address.
+        *
+        * In the next step, SET_CONFIGURATION is send to addressed device, and 
then,
+        * the device is finally ready to use.
+        */
+       if (unlikely(devnum == 0)) {
+               stub = find_attached_device(usbif, 
usbif_pipeportnum(req->pipe));
+               if (unlikely(!stub)) {
+                       ret = -ENODEV;
+                       goto fail_response;
+               }
+
+               switch (ctrl->bRequest) {
+               case USB_REQ_GET_DESCRIPTOR:
+                       /*
+                        * GET_DESCRIPTOR request to device #0.
+                        * through to normal urb transfer.
+                        */
+                       pending_req->stub = stub;
+                       return 0;
+                       break;
+               case USB_REQ_SET_ADDRESS:
+                       /*
+                        * SET_ADDRESS request to device #0.
+                        * add attached device to addr_table.
+                        */
+                       {
+                               __u16 addr = le16_to_cpu(ctrl->wValue);
+                               usbbk_set_address(usbif, stub, 0, addr);
+                       }
+                       ret = 0;
+                       goto fail_response;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto fail_response;
+               }
+       } else {
+               if (unlikely(!usbif->addr_table[devnum])) {
+                       ret = -ENODEV;
+                       goto fail_response;
+               }
+               pending_req->stub = usbif->addr_table[devnum];
+       }
+
+       /*
+        * Check special request
+        */
+       switch (ctrl->bRequest) {
+       case USB_REQ_SET_ADDRESS:
+               /*
+                * SET_ADDRESS request to addressed device.
+                * change addr or remove from addr_table.
+                */
+               {
+                       __u16 addr = le16_to_cpu(ctrl->wValue);
+                       usbbk_set_address(usbif, stub, devnum, addr);
+               }
+               ret = 0;
+               goto fail_response;
+               break;
+#if 0
+       case USB_REQ_SET_CONFIGURATION:
+               /*
+                * linux 2.6.27 or later version only!
+                */
+               if (ctrl->RequestType == USB_RECIP_DEVICE) {
+                       __u16 config = le16_to_cpu(ctrl->wValue);
+                       usb_driver_set_configuration(pending_req->stub->udev, 
config);
+                       done = 1;
+               }
+               break;
+#endif
+       case USB_REQ_SET_INTERFACE:
+               if (ctrl->bRequestType == USB_RECIP_INTERFACE) {
+                       __u16 alt = le16_to_cpu(ctrl->wValue);
+                       __u16 intf = le16_to_cpu(ctrl->wIndex);
+                       usbbk_set_interface(pending_req, intf, alt);
+                       done = 1;
+               }
+               break;
+       case USB_REQ_CLEAR_FEATURE:
+               if (ctrl->bRequestType == USB_RECIP_ENDPOINT
+                       && ctrl->wValue == USB_ENDPOINT_HALT) {
+                       int pipe;
+                       int ep = le16_to_cpu(ctrl->wIndex) & 0x0f;
+                       int dir = le16_to_cpu(ctrl->wIndex)
+                                       & USB_DIR_IN;
+                       if (dir)
+                               pipe = usb_rcvctrlpipe(pending_req->stub->udev, 
ep);
+                       else
+                               pipe = usb_sndctrlpipe(pending_req->stub->udev, 
ep);
+                       usbbk_clear_halt(pending_req, pipe);
+                       done = 1;
+               }
+               break;
+#if 0 /* not tested yet */
+       case USB_REQ_SET_FEATURE:
+               if (ctrl->bRequestType == USB_RT_PORT) {
+                       __u16 feat = le16_to_cpu(ctrl->wValue);
+                       if (feat == USB_PORT_FEAT_RESET) {
+                               usbbk_port_reset(pending_req);
+                               done = 1;
+                       }
+               }
+               break;
+#endif
+       default:
+               break;
+       }
+
+       return done;
+
+fail_response:
+       usbbk_do_response(pending_req, ret, 0, 0, 0);
+       usbif_put(usbif);
+       free_req(pending_req);
+       return 1;
+}
+
+static void dispatch_request_to_pending_reqs(usbif_t *usbif,
+               usbif_request_t *req,
+               pending_req_t *pending_req)
+{
+       int ret;
+
+       pending_req->id = req->id;
+       pending_req->usbif = usbif;
+
+       barrier();
+
+       /*
+        * TODO:
+        * receive unlink request and cancel the urb in backend
+        */
+#if 0
+       if (unlikely(usb_pipeunlink(req->pipe))) {
+
+       }
+#endif
+
+       usbif_get(usbif);
+
+       if (usb_pipecontrol(req->pipe)) {
+               if (check_and_submit_special_ctrlreq(usbif, req, pending_req))
+                       return;
+       } else {
+               int devnum = usb_pipedevice(req->pipe);
+               if (unlikely(!usbif->addr_table[devnum])) {
+                       ret = -ENODEV;
+                       goto fail_response;
+               }
+               pending_req->stub = usbif->addr_table[devnum];
+       }
+
+       barrier();
+
+       ret = usbbk_alloc_urb(req, pending_req);
+       if (ret) {
+               ret = -ESHUTDOWN;
+               goto fail_response;
+       }
+
+       add_req_to_submitting_list(pending_req->stub, pending_req);
+
+       barrier();
+
+       usbbk_init_urb(req, pending_req);
+
+       barrier();
+
+       pending_req->nr_buffer_segs = req->nr_buffer_segs;
+       if (usb_pipeisoc(req->pipe))
+               pending_req->nr_extra_segs = req->u.isoc.nr_frame_desc_segs;
+       else
+               pending_req->nr_extra_segs = 0;
+
+       barrier();
+
+       ret = usbbk_gnttab_map(usbif, req, pending_req);
+       if (ret) {
+               printk(KERN_ERR "usbback: invalid buffer\n");
+               ret = -ESHUTDOWN;
+               goto fail_free_urb;
+       }
+
+       barrier();
+
+       if (usb_pipeout(req->pipe) && req->buffer_length)
+               copy_pages_to_buff(pending_req->buffer,
+                                       pending_req,
+                                       0,
+                                       pending_req->nr_buffer_segs);
+       if (usb_pipeisoc(req->pipe)) {
+               copy_pages_to_buff(&pending_req->urb->iso_frame_desc[0],
+                       pending_req,
+                       pending_req->nr_buffer_segs,
+                       pending_req->nr_extra_segs);
+       }
+
+       barrier();
+
+       ret = usb_submit_urb(pending_req->urb, GFP_KERNEL);
+       if (ret) {
+               printk(KERN_ERR "usbback: failed submitting urb, error %d\n", 
ret);
+               ret = -ESHUTDOWN;
+               goto fail_flush_area;
+       }
+       return;
+
+fail_flush_area:
+       fast_flush_area(pending_req);
+fail_free_urb:
+       remove_req_from_submitting_list(pending_req->stub, pending_req);
+       barrier();
+       usbbk_free_urb(pending_req->urb);
+fail_response:
+       usbbk_do_response(pending_req, ret, 0, 0, 0);
+       usbif_put(usbif);
+       free_req(pending_req);
+}
+
+static int usbbk_start_submit_urb(usbif_t *usbif)
+{
+       usbif_back_ring_t *usb_ring = &usbif->ring;
+       usbif_request_t *ring_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;
+       rmb();
+
+       while (rc != rp) {
+               if (RING_REQUEST_CONS_OVERFLOW(usb_ring, rc)) {
+                       printk(KERN_WARNING "RING_REQUEST_CONS_OVERFLOW\n");
+                       break;
+               }
+
+               pending_req = alloc_req();
+               if (NULL == pending_req) {
+                       more_to_do = 1;
+                       break;
+               }
+
+               ring_req = RING_GET_REQUEST(usb_ring, rc);
+               usb_ring->req_cons = ++rc;
+
+               dispatch_request_to_pending_reqs(usbif, ring_req,
+                                                       pending_req);
+       }
+
+       RING_FINAL_CHECK_FOR_REQUESTS(&usbif->ring, more_to_do);
+
+       cond_resched();
+
+       return more_to_do;
+}
+
+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;
+}
+
+/*
+ * attach the grabbed 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;
+       stub->usbif = usbif;
+}
+
+/*
+ * detach the grabbed device from usbif.
+ */
+void usbbk_unplug_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;
+       stub->usbif = NULL;
+}
+
+void detach_device_without_lock(usbif_t *usbif, struct usbstub *stub)
+{
+       if (stub->addr)
+               usbbk_set_address(usbif, stub, stub->addr, 0);
+       list_del(&stub->plugged_list);
+       stub->plugged = 0;
+       stub->usbif = NULL;
+}
+
+static int __init usbback_init(void)
+{
+       int i, mmap_pages;
+
+       if (!is_running_on_xen())
+               return -ENODEV;
+
+       if (usbstub_init())
+               return -ENODEV;
+
+       mmap_pages = usbif_reqs * USBIF_MAX_SEGMENTS_PER_REQUEST;
+       pending_reqs = kmalloc(sizeof(pending_reqs[0]) *
+                       usbif_reqs, GFP_KERNEL);
+       pending_grant_handles = kmalloc(sizeof(pending_grant_handles[0]) *
+                       mmap_pages, GFP_KERNEL);
+       pending_pages = alloc_empty_pages_and_pagevec(mmap_pages);
+
+       if (!pending_reqs || !pending_grant_handles || !pending_pages)
+               goto out_of_memory;
+
+       for (i = 0; i < mmap_pages; i++)
+               pending_grant_handles[i] = USBBACK_INVALID_HANDLE;
+
+       memset(pending_reqs, 0, sizeof(pending_reqs));
+       INIT_LIST_HEAD(&pending_free);
+
+       for (i = 0; i < usbif_reqs; i++) {
+               list_add_tail(&pending_reqs[i].free_list, &pending_free);
+       }
+
+       usbback_xenbus_init();
+
+       return 0;
+
+ out_of_memory:
+        kfree(pending_reqs);
+        kfree(pending_grant_handles);
+        free_empty_pages_and_pagevec(pending_pages, mmap_pages);
+        printk("%s: out of memory\n", __FUNCTION__);
+        return -ENOMEM;
+}
+
+static void __exit usbback_exit(void)
+{
+       usbback_xenbus_exit();
+       usbstub_exit();
+       kfree(pending_reqs);
+       kfree(pending_grant_handles);
+       free_empty_pages_and_pagevec(pending_pages, usbif_reqs * 
USBIF_MAX_SEGMENTS_PER_REQUEST);
+}
+
+module_init(usbback_init);
+module_exit(usbback_exit);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("Xen USB backend driver (usbback)");
+MODULE_LICENSE("Dual BSD/GPL");
diff -r 8f996719f2ff -r f799db0570f2 drivers/xen/usbback/usbback.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/usbback.h     Wed Mar 18 11:43:24 2009 +0000
@@ -0,0 +1,158 @@
+/*
+ * usbback.h
+ *
+ * This file is part of Xen USB backend driver.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * or,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __XEN_USBBACK_H__
+#define __XEN_USBBACK_H__
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/kref.h>
+#include <xen/evtchn.h>
+#include <xen/gnttab.h>
+#include <xen/driver_util.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/io/usbif.h>
+
+struct usbstub;
+
+#define USB_DEV_ADDR_SIZE 128
+
+typedef struct usbif_st {
+       domid_t           domid;
+       unsigned int      handle;
+       struct xenbus_device *xbdev;
+       struct list_head usbif_list;
+
+       unsigned int      irq;
+
+       usbif_back_ring_t ring;
+       struct vm_struct *ring_area;
+
+       spinlock_t ring_lock;
+       atomic_t refcnt;
+       grant_handle_t shmem_handle;
+       grant_ref_t shmem_ref;
+
+       /* device address lookup table */
+       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;
+
+       /* 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 list_head id_list;
+
+       char bus_id[BUS_ID_SIZE];
+       int dom_id;
+       int dev_id;
+       int portnum;
+};
+
+struct usbstub
+{
+       struct usbstub_id *id;
+       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;
+
+       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);
+
+#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); \
+       } while (0)
+
+void usbback_xenbus_init(void);
+void usbback_xenbus_exit(void);
+
+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 detach_device_without_lock(usbif_t *usbif, struct usbstub *stub);
+void usbbk_unlink_urbs(struct usbstub *stub);
+
+int usbstub_init(void);
+void usbstub_exit(void);
+
+#endif /* __XEN_USBBACK_H__ */
diff -r 8f996719f2ff -r f799db0570f2 drivers/xen/usbback/usbstub.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/usbstub.c     Wed Mar 18 11:43:24 2009 +0000
@@ -0,0 +1,447 @@
+/*
+ * usbstub.c
+ *
+ * USB stub driver - grabbing and managing USB devices.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * or,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#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;
+       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) {
+                       found = 1;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&grabbed_devices_lock, flags);
+
+       if (found)
+               return stub;
+
+       return NULL;
+}
+
+static struct usbstub *usbstub_alloc(struct usb_interface *interface,
+                                               struct usbstub_id *stub_id)
+{
+       struct usbstub *stub;
+
+       stub = kzalloc(sizeof(*stub), GFP_KERNEL);
+       if (!stub) {
+               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;
+       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;
+
+       usb_put_dev(stub->udev);
+       stub->interface = NULL;
+       stub->udev = NULL;
+       stub->id = 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;
+
+       /* 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;
+                       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);
+               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;
+
+       return retval;
+}
+
+static void usbstub_disconnect(struct usb_interface *interface)
+{
+       struct usbstub *stub
+               = (struct usbstub *) usb_get_intfdata(interface);
+
+       usb_set_intfdata(interface, NULL);
+
+       if (!stub)
+               return;
+
+       if (stub->usbif) {
+               usbback_reconfigure(stub->usbif);
+               usbbk_unplug_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,
+               char *buf)
+{
+       struct usbstub_id *stub_id;
+       size_t count = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&usbstub_ids_lock, flags);
+       list_for_each_entry(stub_id, &usbstub_ids, 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);
+
+       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);
+
+/* table of devices that matches any usbdevice */
+static struct usb_device_id usbstub_table[] = {
+               { .driver_info = 1 }, /* wildcard, see usb_match_id() */
+               { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, usbstub_table);
+
+static struct usb_driver usbback_usb_driver = {
+               .name = "usbback",
+               .probe = usbstub_probe,
+               .disconnect = usbstub_disconnect,
+               .id_table = usbstub_table,
+};
+
+int __init usbstub_init(void)
+{
+       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)
+               usbstub_exit();
+
+out:
+       return err;
+}
+
+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);
+
+       usb_deregister(&usbback_usb_driver);
+}
diff -r 8f996719f2ff -r f799db0570f2 drivers/xen/usbback/xenbus.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/xenbus.c      Wed Mar 18 11:43:24 2009 +0000
@@ -0,0 +1,269 @@
+/*
+ * xenbus.c
+ *
+ * Xenbus interface for USB backend driver.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * or,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * 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;
+}
+
+static int usbback_remove(struct xenbus_device *dev)
+{
+       usbif_t *usbif = dev->dev.driver_data;
+
+       if (usbif) {
+               usbif_disconnect(usbif);
+               usbif_free(usbif);;
+       }
+       dev->dev.driver_data = NULL;
+
+       return 0;
+}
+
+static int usbback_probe(struct xenbus_device *dev,
+                         const struct xenbus_device_id *id)
+{
+       usbif_t *usbif;
+       unsigned int handle;
+       int err;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       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");
+               return -ENOMEM;
+       }
+       usbif->xbdev = dev;
+       dev->dev.driver_data = usbif;
+
+       err = xenbus_switch_state(dev, XenbusStateInitWait);
+       if (err)
+               goto fail;
+
+       return 0;
+
+fail:
+       usbback_remove(dev);
+       return err;
+}
+
+static int connect_ring(usbif_t *usbif)
+{
+       struct xenbus_device *dev = usbif->xbdev;
+       unsigned long ring_ref;
+       unsigned int evtchn;
+       int err;
+
+       err = xenbus_gather(XBT_NIL, dev->otherend,
+                           "ring-ref", "%lu", &ring_ref,
+                           "event-channel", "%u", &evtchn, NULL);
+       if (err) {
+               xenbus_dev_fatal(dev, err,
+                                "reading %s/ring-ref and event-channel",
+                                dev->otherend);
+               return err;
+       }
+
+       printk("usbback: ring-ref %ld, event-channel %d\n",
+              ring_ref, evtchn);
+
+       err = usbif_map(usbif, ring_ref, evtchn);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u",
+                                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,
+                                    enum xenbus_state frontend_state)
+{
+       usbif_t *usbif = dev->dev.driver_data;
+       int err;
+
+       switch (frontend_state) {
+       case XenbusStateInitialising:
+               if (dev->state == XenbusStateClosed) {
+                       printk("%s: %s: prepare for reconnect\n",
+                              __FUNCTION__, dev->nodename);
+                       xenbus_switch_state(dev, XenbusStateInitWait);
+               }
+               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;
+               xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+
+       case XenbusStateClosing:
+               usbif_disconnect(usbif);
+               xenbus_switch_state(dev, XenbusStateClosing);
+               break;
+
+       case XenbusStateClosed:
+               xenbus_switch_state(dev, XenbusStateClosed);
+               break;
+
+       case XenbusStateReconfiguring:
+               usbback_do_hotplug(usbif);
+               xenbus_switch_state(dev, XenbusStateReconfigured);
+               break;
+
+       case XenbusStateUnknown:
+               device_unregister(&dev->dev);
+               break;
+
+       default:
+               xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+                                frontend_state);
+               break;
+       }
+}
+
+static const struct xenbus_device_id usbback_ids[] = {
+       { "vusb" },
+       { "" },
+};
+
+static struct xenbus_driver usbback_driver = {
+       .name = "vusb",
+       .owner = THIS_MODULE,
+       .ids = usbback_ids,
+       .probe = usbback_probe,
+       .otherend_changed = frontend_changed,
+       .remove = usbback_remove,
+};
+
+void usbback_xenbus_init(void)
+{
+       xenbus_register_backend(&usbback_driver);
+}
+
+void usbback_xenbus_exit(void)
+{
+       xenbus_unregister_driver(&usbback_driver);
+}

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] [linux-2.6.18-xen] PVUSB: backend driver, Xen patchbot-linux-2.6.18-xen <=