[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC][PATCH 3/4] PVUSB: backend driver



This patch adds the PVUSB backendend driver.

Signed-off-by: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
diff -r 51decc39e5e7 -r 9ef2e8c6cf3d drivers/xen/usbback/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/Makefile      Mon Mar 16 18:41:12 2009 +0900
@@ -0,0 +1,4 @@
+obj-$(CONFIG_XEN_USB_BACKEND) := usbbk.o
+
+usbbk-y   := usbstub.o xenbus.o interface.o usbback.o
+
diff -r 51decc39e5e7 -r 9ef2e8c6cf3d drivers/xen/usbback/interface.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/interface.c   Mon Mar 16 18:41:12 2009 +0900
@@ -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 51decc39e5e7 -r 9ef2e8c6cf3d drivers/xen/usbback/usbback.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/usbback.c     Mon Mar 16 18:41:12 2009 +0900
@@ -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 51decc39e5e7 -r 9ef2e8c6cf3d drivers/xen/usbback/usbback.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/usbback.h     Mon Mar 16 18:41:12 2009 +0900
@@ -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 51decc39e5e7 -r 9ef2e8c6cf3d drivers/xen/usbback/usbstub.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/usbstub.c     Mon Mar 16 18:41:12 2009 +0900
@@ -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 51decc39e5e7 -r 9ef2e8c6cf3d drivers/xen/usbback/xenbus.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbback/xenbus.c      Mon Mar 16 18:41:12 2009 +0900
@@ -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-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.