diff -Nur linux-2.6.17.6/arch/um/Kconfig usb-2.6.17.6/arch/um/Kconfig --- linux-2.6.17.6/arch/um/Kconfig 2006-06-17 18:49:35.000000000 -0700 +++ usb-2.6.17.6/arch/um/Kconfig 2006-08-12 15:03:56.000000000 -0700 @@ -303,6 +303,8 @@ source "drivers/md/Kconfig" +source "drivers/usb/Kconfig" + if BROKEN source "drivers/mtd/Kconfig" endif diff -Nur linux-2.6.17.6/drivers/usb/Kconfig usb-2.6.17.6/drivers/usb/Kconfig --- linux-2.6.17.6/drivers/usb/Kconfig 2006-06-17 18:49:35.000000000 -0700 +++ usb-2.6.17.6/drivers/usb/Kconfig 2006-08-12 06:35:48.000000000 -0700 @@ -12,6 +12,7 @@ default y if USB_ARCH_HAS_OHCI default y if USB_ARCH_HAS_EHCI default y if ARM # SL-811 + default y if UML default PCI # many non-PCI SOC chips embed OHCI diff -Nur linux-2.6.17.6/drivers/usb/Makefile usb-2.6.17.6/drivers/usb/Makefile --- linux-2.6.17.6/drivers/usb/Makefile 2006-06-17 18:49:35.000000000 -0700 +++ usb-2.6.17.6/drivers/usb/Makefile 2006-08-12 06:35:48.000000000 -0700 @@ -14,6 +14,7 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ +obj-$(CONFIG_USB_UML_HCD) += host/ obj-$(CONFIG_ETRAX_USB_HOST) += host/ obj-$(CONFIG_USB_OHCI_AT91) += host/ diff -Nur linux-2.6.17.6/drivers/usb/core/hub.c usb-2.6.17.6/drivers/usb/core/hub.c --- linux-2.6.17.6/drivers/usb/core/hub.c 2006-06-17 18:49:35.000000000 -0700 +++ usb-2.6.17.6/drivers/usb/core/hub.c 2006-08-12 08:03:38.000000000 -0700 @@ -2239,6 +2239,7 @@ goto fail; } + if (udev->bus && udev->bus->controller && udev->bus->controller->driver) dev_info (&udev->dev, "%s %s speed USB device using %s and address %d\n", (udev->config) ? "reset" : "new", diff -Nur linux-2.6.17.6/drivers/usb/core/message.c usb-2.6.17.6/drivers/usb/core/message.c --- linux-2.6.17.6/drivers/usb/core/message.c 2006-06-17 18:49:35.000000000 -0700 +++ usb-2.6.17.6/drivers/usb/core/message.c 2006-08-12 06:35:48.000000000 -0700 @@ -4,6 +4,7 @@ #include #include /* for scatterlist macros */ +#include /* for scatterlist macros */ #include #include #include diff -Nur linux-2.6.17.6/drivers/usb/host/Kconfig usb-2.6.17.6/drivers/usb/host/Kconfig --- linux-2.6.17.6/drivers/usb/host/Kconfig 2006-06-17 18:49:35.000000000 -0700 +++ usb-2.6.17.6/drivers/usb/host/Kconfig 2006-08-12 06:35:48.000000000 -0700 @@ -148,3 +148,17 @@ To compile this driver as a module, choose M here: the module will be called "sl811_cs". +config USB_UML_HCD + tristate "UML HCD (user mode linux) support" + depends on USB && UML + default USB + ---help--- + The User Mode Linux Host Controller Interface is a function + for accessing the USB hardware in the PC (which is also called + the USB host controller) from User Mode Linux this allows + for debugging and testing USB drivers without requiring Kdb + or even root on the host machine. + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called uml-hcd. diff -Nur linux-2.6.17.6/drivers/usb/host/Makefile usb-2.6.17.6/drivers/usb/host/Makefile --- linux-2.6.17.6/drivers/usb/host/Makefile 2006-06-17 18:49:35.000000000 -0700 +++ usb-2.6.17.6/drivers/usb/host/Makefile 2006-08-12 06:35:48.000000000 -0700 @@ -15,3 +15,10 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o +obj-$(CONFIG_USB_UML_HCD) += uml-hcd.o + +uml-hcd-objs := uml-hcd_kern.o uml-hcd_user.o + +ifeq ($(ARCH),um) + include arch/um/scripts/Makefile.rules +endif diff -Nur linux-2.6.17.6/drivers/usb/host/uml-hcd_kern.c usb-2.6.17.6/drivers/usb/host/uml-hcd_kern.c --- linux-2.6.17.6/drivers/usb/host/uml-hcd_kern.c 1969-12-31 16:00:00.000000000 -0800 +++ usb-2.6.17.6/drivers/usb/host/uml-hcd_kern.c 2006-08-12 18:56:40.000000000 -0700 @@ -0,0 +1,930 @@ +/* + * uml-hcd_kern.c -- USB Host Controller for User Mode Linux + * + * Maintainer: James McMechan + * + * Copyright (C) 2006 James McMechan + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "asm/byteorder.h" +#include "linux/usbdevice_fs.h" + +/* 2.6.17.6 does everything with platform + * 2.6.12.2 does not even have that include */ +#define PLATFORM +#ifdef PLATFORM +#include +#include "../core/hub.h" +#endif /* PLATFORM */ + +/* extern from uml-hcd_user.c */ +extern int uml_open_proxy(char *usb_device_name); +extern int uml_close_proxy(int fd); +extern int uml_submit_urb_user(int fd, struct usbdevfs_urb *urb); +extern int uml_reap_urb_user(int fd, struct usbdevfs_urb **urb); + +#define DRIVER_DESC "USB User Mode Host Controller" +#define DRIVER_VERSION "12 Aug 2006" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James McMechan"); +MODULE_DESCRIPTION(DRIVER_DESC); +static char *usb_device_name = "/proc/bus/usb/002/002"; +module_param(usb_device_name,charp,7); +MODULE_PARM_DESC(usb_device_name,"usb device name to connect to e.g. /proc/bus/usb/001/002"); +static char usb_speed=1; +module_param(usb_speed,byte,7); +MODULE_PARM_DESC(usb_speed,"usb speed to pretend to use 1-low,2-full,3-high"); + +/* + * The User Mode Host Controller works by passing the URB + * to the host systems usb subsystem by calling out to the + * /proc/bus/usb/001/001 file and calling to submit urb ioctl + */ + +static const char driver_name[] = "uml_hcd"; + +#define NPORTS 1 /* number of ports to use */ +struct uml_priv { + spinlock_t lock; + struct timer_list timer; + u32 port_status; + int fd[NPORTS]; +}; + +/* the urb holder is used to keep track between the uml and host urbs */ +struct urb_holder { + struct usbdevfs_urb host_urb; + struct urb *uml_urb; + struct list_head urb_holder_list; + struct usb_hcd *hcd; + int fd; +}; + +static struct list_head urb_holder_list; + +static struct uml_priv *uml_hcd_get_priv(struct usb_hcd *hcd) +{ + return (struct uml_priv *) hcd->hcd_priv; +} + + +/* + * this cleans up for a paticular host_urb + * it has to walk the list to check that it was our urb + */ +static int uml_hcd_complete_urb(struct usbdevfs_urb *host_urb) +{ + struct urb_holder *urb_holder; + struct urb *uml_urb; + unsigned long flags; + struct uml_priv *uml_priv; + struct usb_hcd *hcd; + + if (host_urb == NULL) + return -EINVAL; + + urb_holder = container_of(host_urb, struct urb_holder, host_urb); + + if (host_urb->usercontext != urb_holder) + { + printk(KERN_ERR "inconsistant urbs context does not match\n"); + return -EINVAL; + } + + /* get back to the uml_urb */ + uml_urb = urb_holder->uml_urb; + + /* get back to the hcd */ + hcd = urb_holder->hcd; + + /* get back to the uml_priv */ + uml_priv = uml_hcd_get_priv(hcd); + + /* update the status from the host urb */ + uml_urb->status = host_urb->status; + + /* update the actual length from the host urb */ + uml_urb->actual_length = host_urb->actual_length; + + /* update the start_frame from the host urb */ + uml_urb->start_frame = host_urb->start_frame; + + /* update the interval from the host urb */ + /* we can't set it from usbdevice_fs? */ + //uml_urb->interval = host_urb->interval; + + /* update the error_count from the host urb */ + uml_urb->error_count = host_urb->error_count; + + /* check for the setup packet in the control urb */ + if (usb_pipetype(uml_urb->pipe) == PIPE_CONTROL) + { + /* + * copy back the data from the allocated + * buffer after the setup packet + */ + memcpy(uml_urb->transfer_buffer, + host_urb->buffer + + sizeof(struct usb_ctrlrequest), + host_urb->actual_length); + + /* free the allocated buffer */ + kfree(host_urb->buffer); + } + + /* remove from the list we dont want it referenced anymore */ + local_irq_save(flags); + spin_lock(¨_priv->lock); + + list_del(&urb_holder->urb_holder_list); + + spin_unlock(¨_priv->lock); + + /* call the giveback routine with diabled interrupts */ + usb_hcd_giveback_urb(hcd, uml_urb, NULL); + + /* restore interrupts completion done */ + local_irq_restore(flags); + + /* free the allocated urb holder */ + kfree(urb_holder); + + return 0; +} + +/* + * the timer runs to reap completed urbs + */ +static void uml_timer(unsigned long arg) +{ + struct usb_hcd *hcd = (void *) arg; + struct uml_priv *uml_priv = uml_hcd_get_priv(hcd); + //struct urb_holder *urb_holder; + int retval; + int i; + struct usbdevfs_urb *host_urb; + +#ifdef PLATFORM + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); +#endif /* PLATFORM */ + + for(i = 0;i < NPORTS; i++) + { + /* check for completed urbs on each open fd */ + if (uml_priv->fd[i] >= 0) + do + { + /* clear the host_urb we will take any */ + host_urb = 0; + retval = uml_reap_urb_user(uml_priv->fd[i], + &host_urb); + if (retval != -EAGAIN) + printk(KERN_DEBUG "reaped %d returned %p " + "status %d\n", + uml_priv->fd[i], + host_urb, + retval); + if (retval >= 0) + retval = uml_hcd_complete_urb(host_urb); + } while ( retval >= 0); + } + +#if 0 + /* only walk the list while locked */ + spin_lock_irqsave(¨_priv->lock, flags); + /* check for timeouts */ + list_for_each_entry(urb_holder, &urb_holder_list, urb_holder_list) + { + /* check each urb's fd */ + retval = uml_hcd_check_urb_user(urb_holder->fd); + if ((retval == -EAGAIN) && urb_holder->expires && (urb_holder->expires < jiffies)) + { + struct urb *uml_urb =urb_holder->uml_urb; + + uml_hcd_unlink_urb(uml_urb); + uml_urb->status = -ETIMEDOUT; + if (uml_urb->complete) + uml_urb->complete(uml_urb, NULL); + } + } + + /* done with list unlock */ + spin_unlock_irqrestore(¨_priv->lock, flags); +#endif /* 0 */ + + if (!list_empty(&urb_holder_list)) + mod_timer(¨_priv->timer, jiffies + msecs_to_jiffies(1)); + + return; +} + +static int uml_hcd_start(struct usb_hcd *hcd) +{ + struct uml_priv *uml_priv = uml_hcd_get_priv(hcd); + int retval = 0; + + dev_dbg(hcd->self.controller, "starting uml-hcd\n"); + + spin_lock_init (¨_priv->lock); + INIT_LIST_HEAD (&urb_holder_list); + + /* timeouts process the urb completion */ + init_timer (¨_priv->timer); + uml_priv->timer.function = uml_timer; + uml_priv->timer.data = (unsigned long) hcd; + +#ifdef PLATFORM + hcd->state = HC_STATE_RUNNING; + hcd->power_budget = 600; + hcd->uses_new_polling = 1; +#else + struct usb_device *root; + + /* allocate the root hub */ + root = usb_alloc_dev(NULL, &hcd->self, 0); + if (!root) + return -ENOMEM; + + hcd->state = HC_STATE_RUNNING; + if (usb_speed == 3) + root->speed = USB_SPEED_HIGH; + else if (usb_speed == 2) + root->speed = USB_SPEED_FULL; + else + root->speed = USB_SPEED_LOW; + + retval = usb_hcd_register_root_hub (root, hcd); + if (retval) + goto drop_root; + + /* set to 500mA since this is a virtual device */ + hub_set_power_budget(root, 500); +#endif /* PLATFORM */ + + uml_priv->port_status |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_POWER + | (1 << USB_PORT_FEAT_C_CONNECTION); + if (usb_speed == 3) + uml_priv->port_status |= USB_PORT_STAT_HIGH_SPEED; + else if (usb_speed == 1) + uml_priv->port_status |= USB_PORT_STAT_LOW_SPEED; + + uml_priv->fd[0] = uml_open_proxy(usb_device_name); + dev_dbg(hcd->self.controller, "uml-hcd fd = %d\n",uml_priv->fd[0]); + if (uml_priv->fd[0] < 0) + { + retval = -ENODEV; + goto drop_root; + } + + return retval; +drop_root: +#ifndef PLATFORM + usb_put_dev(root); +#endif /* !PLATFORM */ + hcd->state = HC_STATE_QUIESCING; + return retval; +} + +static void uml_hcd_stop(struct usb_hcd *hcd) +{ + struct uml_priv *uml_priv = uml_hcd_get_priv(hcd); + int i; + + dev_dbg(hcd->self.controller, "stopping uml-hcd\n"); + del_timer_sync(¨_priv->timer); + + for (i=0;ifd[i] >= 0) + { + dev_dbg(hcd->self.controller, "closing fd[%d] = %d\n", + i, uml_priv->fd[i]); + uml_close_proxy(uml_priv->fd[i]); + uml_priv->fd[i] = -1; + } +} + +static int uml_hcd_enqueue( struct usb_hcd *hcd, struct usb_host_endpoint *ep, + struct urb *uml_urb, +#ifdef PLATFORM + gfp_t mem_flags) +#else + int mem_flags) +#endif /* PLATFORM */ +{ + struct uml_priv *uml_priv = uml_hcd_get_priv(hcd); + unsigned long flags; + struct urb_holder *urb_holder; + struct usbdevfs_urb *host_urb; + int submit_urb = 1; + int retval = 1; + + dev_dbg(hcd->self.controller, "enqueuing urb %p\n", uml_urb); + + urb_holder = kmalloc(sizeof(*urb_holder),mem_flags); + if (!urb_holder) + return -ENOMEM; + + /* clear the urb holder */ + memset(urb_holder, 0, sizeof(*urb_holder)); + + /* it is somewhat clearer to read as we copy over uml_urb to host_urb*/ + host_urb = &urb_holder->host_urb; + + /* save the uml urb in the urb_holder */ + urb_holder->uml_urb = uml_urb; + + /* save the uml priv in the urb_holder */ + urb_holder->hcd = hcd; + + /* clear private until submitted under lock */ + uml_urb->hcpriv = 0; + + /* set the endpoint */ + host_urb->endpoint = (usb_pipeendpoint(uml_urb->pipe)) | (uml_urb->pipe & 0x80L); + + /* set the fd */ + urb_holder->fd = uml_priv->fd[0]; + dev_dbg(hcd->self.controller, "enqueue with fd[%d] = %d\n", + 0, urb_holder->fd); + + /* set the buffer */ + host_urb->buffer = uml_urb->transfer_buffer; + + /* set the buffer_length */ + host_urb->buffer_length = uml_urb->transfer_buffer_length; + + /* set the host_urb start_frame */ + host_urb->start_frame = uml_urb->start_frame; + + /* set the number_of_packets */ + host_urb->number_of_packets = uml_urb->number_of_packets; + + /* set the host_urb interval */ + /* we can't set it for usbdevice_fs? */ + //host_urb->interval = uml_urb->interval; + + /* set the host_urb flags from the uml flags */ + host_urb->flags = uml_urb->transfer_flags & (USBDEVFS_URB_ISO_ASAP | + USBDEVFS_URB_SHORT_NOT_OK | URB_NO_FSBR|URB_ZERO_PACKET); + + /* set the signr so no signal is sent */ + /* header says that -1 is dont use, but the code checks for 0 */ + host_urb->signr = 0; + + /* set the usercontext to point to the holder */ + host_urb->usercontext = urb_holder; + + /* set the uml_urb status */ + uml_urb->status = -EINPROGRESS; + + dev_dbg(hcd->self.controller,"processing urb holder %p for urb %p\n", + urb_holder,uml_urb); + /* now test for the extra stuff in control urbs */ + switch (usb_pipetype(uml_urb->pipe)) { + struct usb_ctrlrequest *cr; + case PIPE_CONTROL: + cr = (struct usb_ctrlrequest *) uml_urb->setup_packet; + dev_dbg(hcd->self.controller, "pipe control " + "ReqType 0x%04x Request 0x%04x " + "Value 0x%04x Index 0x%04x " + "Length 0x%04x\n", + cr->bRequestType,cr->bRequest, + cr->wValue,cr->wIndex, + cr->wLength); + host_urb->type = USBDEVFS_URB_TYPE_CONTROL; + + /* we need extra space for the setup packet */ + host_urb->buffer_length += + sizeof(struct usb_ctrlrequest); + + /* allocate a buffer big enough for all the data */ + host_urb->buffer = kmalloc(host_urb->buffer_length, + mem_flags); + if (!host_urb->buffer) + { + kfree(urb_holder); + return -ENOMEM; + } + + /* clear and then copy in the setup packet */ + memset(host_urb->buffer, 0, host_urb->buffer_length); + memcpy(host_urb->buffer, uml_urb->setup_packet, + sizeof(struct usb_ctrlrequest)); + memcpy(host_urb->buffer + + sizeof(struct usb_ctrlrequest), + uml_urb->transfer_buffer, + uml_urb->transfer_buffer_length); + if (cr->bRequestType == USB_TYPE_STANDARD) + switch (cr->bRequest) + { + case USB_REQ_SET_ADDRESS: + /* dont submit the host urb */ + submit_urb = 0; + /* we are successful */ + retval = 0; + uml_urb->status = 0; + break; + case USB_REQ_SET_CONFIGURATION: +/* turn off the entire REQ SET CONFIG simulation */ +#if 0 + + /* dont submit the host urb */ + submit_urb = 0; + /* we are successful */ + retval = 0; + uml_urb->status = 0; +#endif /* 0 */ + break; + default: + break; + } + break; + + case PIPE_BULK: + dev_dbg(hcd->self.controller, "pipe bulk\n"); + host_urb->type = USBDEVFS_URB_TYPE_BULK; + break; + + /* use a bulk instead of a interrupt urb */ + case PIPE_INTERRUPT: + dev_dbg(hcd->self.controller, "pipe interrupt\n"); + host_urb->type = USBDEVFS_URB_TYPE_BULK; + break; + + case PIPE_ISOCHRONOUS: + dev_dbg(hcd->self.controller, "pipe iso\n"); + host_urb->type = USBDEVFS_URB_TYPE_ISO; + kfree(urb_holder); + return -EINVAL; /* still not supported */ + break; + default: + dev_err(hcd->self.controller, "pipe type unkown?\n"); + kfree(urb_holder); + return -EINVAL; /* we got something else ?*/ + } + + /* only bother sending if we still need to find if it worked */ + if (submit_urb) + { + /* submit the host urb to the device */ + retval = uml_submit_urb_user(urb_holder->fd,host_urb); + + /* add the urb_holder to the list for the timer to check */ + if (retval >= 0) + { + spin_lock_irqsave(¨_priv->lock, flags); + + /* set our host controller private to the urb holder */ + uml_urb->hcpriv = urb_holder; + + /* schedule the timer */ + if (!timer_pending(¨_priv->timer)) + mod_timer(¨_priv->timer, jiffies + 1); + + /* queue it onto the list */ + list_add_tail(&urb_holder->urb_holder_list, + &urb_holder_list); + + spin_unlock_irqrestore(¨_priv->lock, flags); + } + /* failed so we did not submit our urb */ + else submit_urb = 0; + } + + /* no urb submitted release the urb holder */ + if (!submit_urb) + { + /* if we are reporting success give the urb back */ + if (retval == 0) + { + local_irq_save(flags); + usb_hcd_giveback_urb(hcd, uml_urb, NULL); + local_irq_restore(flags); + } + /* release the buffer if we allocated one */ + if (usb_pipetype(uml_urb->pipe) == PIPE_CONTROL) + kfree(host_urb->buffer); + kfree(urb_holder); + } + + return retval; +} + +static int uml_hcd_dequeue( struct usb_hcd *hcd, struct urb *uml_urb) +{ + struct uml_priv *uml_priv = uml_hcd_get_priv(hcd); + struct urb_holder *urb_holder; + unsigned long flags; + + dev_dbg(hcd->self.controller, "dequeuing urb %p\n", uml_urb); + + local_irq_save(flags); + spin_lock(¨_priv->lock); + + urb_holder = uml_urb->hcpriv; + if (urb_holder) + list_del_init(&urb_holder->urb_holder_list); + + uml_urb->hcpriv = 0; + + spin_unlock(¨_priv->lock); + + usb_hcd_giveback_urb(hcd, uml_urb, NULL); + + local_irq_restore(flags); + return 0; +} + +#if 0 +static int uml_hcd_get_frame_number( struct usb_hcd *hcd) +{ + static int frame = 0; + frame++; + return 0; +} +#endif /* 0 */ + +#define PORT_FEAT_C_MASK (\ + (1 << USB_PORT_FEAT_C_CONNECTION) |\ + (1 << USB_PORT_FEAT_C_ENABLE) |\ + (1 << USB_PORT_FEAT_C_SUSPEND) |\ + (1 << USB_PORT_FEAT_C_OVER_CURRENT) |\ + (1 << USB_PORT_FEAT_C_RESET) ) + +static int uml_hcd_hub_status_data( struct usb_hcd *hcd, char *buf) +{ + struct uml_priv *uml_priv = uml_hcd_get_priv(hcd); + //unsigned long flags; + int retval = 0; + + dev_dbg(hcd->self.controller, "uml_hcd_hub_status_data\n"); + //spin_lock_irqsave(¨_priv->lock, flags); + //for (i=0;i < NUMPORTS;i++) + //if (i > 7 + (8 * retval)) buf[++retval]=0; + //if (uml_priv->port_status[retval] + //buf[retval] |= (1 << (i + 1 - (8 * retval))); + //spin_unlock_irqrestore(¨_priv->lock, flags); + if (uml_priv->port_status & PORT_FEAT_C_MASK) + { + /* always just set port 1 to changed */ + *buf = (1 << 1); + dev_dbg (hcd->self.controller, "uml hub has status change " + "0x%08x\n",uml_priv->port_status); + retval = 1; + } + return retval; +} + +static int uml_hcd_hub_control( struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength) +{ + struct uml_priv *uml_priv = uml_hcd_get_priv(hcd); + unsigned long flags; + int retval = 0; + + dev_dbg (hcd->self.controller, "uml hub control type %04x " + "Value %04x Index %04x Length %d\n", + typeReq, wValue, wIndex, wLength); + spin_lock_irqsave(¨_priv->lock, flags); + switch (typeReq) + { + case ClearHubFeature: + dev_dbg (hcd->self.controller, + "uml hub control ClearHub\n"); + break; + case SetHubFeature: + dev_dbg (hcd->self.controller, + "uml hub control SetHubFeature\n"); + retval = -EPIPE; + break; + case ClearPortFeature: + dev_dbg (hcd->self.controller, + "uml hub control ClearPort\n"); + switch (wValue) { + case USB_PORT_FEAT_CONNECTION: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_CONNECTION\n"); + uml_priv->port_status &= ~(1 << wValue); + uml_priv->port_status |= + (1 << USB_PORT_FEAT_C_CONNECTION); + break; + case USB_PORT_FEAT_ENABLE: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_ENABLE\n"); + uml_priv->port_status &= ~(1 << wValue); + uml_priv->port_status |= + (1 << USB_PORT_FEAT_C_ENABLE); + break; + case USB_PORT_FEAT_SUSPEND: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_SUSPEND\n"); + uml_priv->port_status &= ~(1 << wValue); + uml_priv->port_status |= + (1 << USB_PORT_FEAT_C_SUSPEND); + break; + case USB_PORT_FEAT_OVER_CURRENT: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_OVER_CURRENT\n"); + uml_priv->port_status &= ~(1 << wValue); + uml_priv->port_status |= + (1 << USB_PORT_FEAT_C_OVER_CURRENT); + break; + case USB_PORT_FEAT_POWER: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_POWER\n"); + uml_priv->port_status = 0; + break; + case USB_PORT_FEAT_C_CONNECTION: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_C_CONNECTION\n"); + uml_priv->port_status &= ~(1 << wValue); + break; + case USB_PORT_FEAT_C_ENABLE: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_C_ENABLE\n"); + uml_priv->port_status &= ~(1 << wValue); + break; + case USB_PORT_FEAT_C_SUSPEND: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_C_SUSPEND\n"); + uml_priv->port_status &= ~(1 << wValue); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_C_OVER_CURRENT\n"); + uml_priv->port_status &= ~(1 << wValue); + break; + case USB_PORT_FEAT_C_RESET: + dev_dbg (hcd->self.controller, + "uml hub clear " + "FEAT_C_RESET\n"); + uml_priv->port_status |= + USB_PORT_STAT_ENABLE | + (1 << USB_PORT_FEAT_C_ENABLE); + uml_priv->port_status &= ~(1 << wValue); + break; + default: + dev_dbg (hcd->self.controller, + "uml hub clear " + "unknown %d\n",wValue); + uml_priv->port_status &= ~(1 << wValue); + break; + } + break; + case SetPortFeature: + switch (wValue) { + case USB_PORT_FEAT_POWER: + dev_dbg (hcd->self.controller, + "uml hub set " + "FEAT_POWER\n"); + uml_priv->port_status |= (1 << wValue); + break; + case USB_PORT_FEAT_RESET: + dev_dbg (hcd->self.controller, + "uml hub set " + "FEAT_RESET\n"); + uml_priv->port_status |= + USB_PORT_STAT_ENABLE | + (1<self.controller, + "uml hub control " + "SetPortFeature %d\n", + wValue); + uml_priv->port_status |= (1 << wValue); + break; + } + case GetHubDescriptor: + dev_dbg (hcd->self.controller, + "uml hub control GetHubDesc\n"); + { + struct usb_hub_descriptor *hub_desc = + (struct usb_hub_descriptor *) buf; + memset(hub_desc, 0, sizeof(*hub_desc)); + hub_desc->bDescriptorType = 0x29; + hub_desc->bDescLength = 9; + hub_desc->wHubCharacteristics = + cpu_to_le16(0x0001); + hub_desc->bNbrPorts = 1; + hub_desc->bitmap[0] = 0xff; + hub_desc->bitmap[1] = 0xff; + } + break; + case GetHubStatus: + dev_dbg (hcd->self.controller, + "uml hub GetHubStatus\n"); + *(u32 *)buf = cpu_to_le32(0); + break; + case GetPortStatus: + dev_dbg (hcd->self.controller, + "uml hub GetPortStatus 0x%08x\n", + uml_priv->port_status); + /* currently only one port */ + if (wIndex != 1) + return -EPIPE; + + ((u16 *) buf)[0] = cpu_to_le16(uml_priv->port_status); + ((u16 *) buf)[1] = cpu_to_le16(uml_priv->port_status >> 16); + /* one report that it is reseting nexttime complete */ + if (uml_priv->port_status & (1 << USB_PORT_FEAT_RESET)) + { + uml_priv->port_status |= + (1 << USB_PORT_FEAT_C_RESET) | + (1 << USB_PORT_FEAT_ENABLE) | + (1 << USB_PORT_FEAT_CONNECTION); + uml_priv->port_status &= + ~(1 << USB_PORT_FEAT_RESET); + } + break; + } + spin_unlock_irqrestore(¨_priv->lock, flags); + return retval; +} + +static struct hc_driver uml_hcd = { + .description = driver_name, + .product_desc = DRIVER_DESC, + .hcd_priv_size = sizeof(struct uml_priv), + /* well USB1/2 does not matter much to UML it is set from usb_speed */ + .flags = HCD_USB11, + .start = uml_hcd_start, + .stop = uml_hcd_stop, + .urb_enqueue = uml_hcd_enqueue, + .urb_dequeue = uml_hcd_dequeue, + //.get_frame_number = uml_hcd_get_frame_number, + .hub_status_data = uml_hcd_hub_status_data, + .hub_control = uml_hcd_hub_control, + //.bus_suspend = uml_bus_suspend, + //.bus_resume = uml_bus_resume, +}; + +#ifdef PLATFORM +static int uml_hcd_probe(struct platform_device *p_dev) +#else +static int uml_hcd_probe(struct device *p_dev) +#endif /* PLATFORM */ +{ + struct usb_hcd *hcd; + int retval; + + if (usb_speed == 3) /* high speed usb 2.0 */ + uml_hcd.flags = HCD_USB2; + else /* low and full only need usb 1.1 */ + uml_hcd.flags = HCD_USB11; + +#ifdef PLATFORM + dev_dbg(&p_dev->dev, DRIVER_DESC " driver probe " DRIVER_VERSION "\n"); + hcd = usb_create_hcd(¨_hcd, &p_dev->dev, p_dev->dev.bus_id); +#else + dev_dbg(p_dev, DRIVER_DESC " driver probe " DRIVER_VERSION "\n"); + hcd = usb_create_hcd(¨_hcd, p_dev, p_dev->bus_id); +#endif /* PLATFORM */ + + if (!hcd) + return -ENOMEM; + + retval = usb_add_hcd(hcd, 0, 0); + if (retval) { + usb_put_hcd (hcd); + } + + return retval; +} + +#ifdef PLATFORM +static int uml_hcd_remove (struct platform_device *p_dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(p_dev); + dev_dbg(&p_dev->dev, "uml hcd remove\n"); +#else +static int uml_hcd_remove (struct device *p_dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(p_dev); + dev_dbg(p_dev, "uml hcd remove\n"); +#endif /* PLATFORM */ + + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + + return 0; +} + +#ifdef PLATFORM +static struct platform_driver uml_hcd_driver = { + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + .bus = &platform_bus_type, + }, +#else +static struct device_driver uml_hcd_driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + .bus = &platform_bus_type, +#endif /* PLATFORM */ + .probe = uml_hcd_probe, + .remove = uml_hcd_remove, + //.suspend = uml_hcd_suspend, + //.resume = uml_hcd_resume, +}; + +static void uml_platform_release (struct device *dev) {} + +static struct platform_device uml_platform = { + .name = "uml-hcd", + .id = -1, + .dev = { +#ifndef PLATFORM + .driver = ¨_hcd_driver, +#endif /* !PLATFORM */ + .release = ¨_platform_release, + } +}; + +static int __init uml_hcd_init(void) +{ + int retval = -ENODEV; + + if (usb_disabled()) + return -ENODEV; + +#ifdef PLATFORM + retval = platform_driver_register (¨_hcd_driver); +#else + retval = driver_register (¨_hcd_driver); +#endif /* PLATFORM */ + if (retval < 0) + return retval; + + retval = platform_device_register (¨_platform); + if (retval < 0) + goto release_driver; + +#ifdef PLATFORM + retval = uml_hcd_probe( ¨_platform ); +#else + retval = uml_hcd_probe( ¨_platform.dev ); +#endif /* PLATFORM */ + if (retval < 0) + goto release_platform; + + return retval; + +/* error unwinding */ +release_platform: + platform_device_unregister (¨_platform); +release_driver: +#ifdef PLATFORM + platform_driver_unregister (¨_hcd_driver); +#else + driver_unregister (¨_hcd_driver); +#endif /* PLATFORM */ + return retval; +} +module_init (uml_hcd_init) + +static void __exit uml_hcd_cleanup (void) +{ + platform_device_unregister (¨_platform); +#ifdef PLATFORM + platform_driver_unregister (¨_hcd_driver); +#else + driver_unregister (¨_hcd_driver); +#endif /* PLATFORM */ +} +module_exit (uml_hcd_cleanup) diff -Nur linux-2.6.17.6/drivers/usb/host/uml-hcd_user.c usb-2.6.17.6/drivers/usb/host/uml-hcd_user.c --- linux-2.6.17.6/drivers/usb/host/uml-hcd_user.c 1969-12-31 16:00:00.000000000 -0800 +++ usb-2.6.17.6/drivers/usb/host/uml-hcd_user.c 2006-08-12 13:27:42.000000000 -0700 @@ -0,0 +1,75 @@ +/* + * uml-hcd_user.c -- USB Host Controller for User Mode Linux + * + * Maintainer: James McMechan + * + * Copyright (C) 2006 James McMechan + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +/* just to get rid of the warning */ +extern int printk(const char * fmt, ...); + +int uml_open_proxy(char *usb_device_name) +{ + int fd = open(usb_device_name, O_RDWR); + + if ( fd >=0 ) + return fd; + else + return -ENODEV; +} + +int uml_close_proxy(int fd) +{ + return close(fd); +} + +/* + * uml_hcd_submit_urb_user is the magic to having a userspace hcd + * it allows us to submit a urb to the device from userspace + * without requireing a stub driver in the host kernel. + */ +int uml_submit_urb_user(int fd, struct usbdevfs_urb *urb) +{ + int retval; + + retval = ioctl(fd, USBDEVFS_SUBMITURB, urb); + + return retval; +} +/* + * check to see if we have a completed urb + * this will act very odd if two userspace processes mix urbs + * to the same device and the wrong one gets the urb back + */ +int uml_reap_urb_user(int fd, struct usbdevfs_urb **urb) +{ + int retval; + + retval = ioctl(fd, USBDEVFS_REAPURBNDELAY, urb); + if (retval < 0) + retval = -errno; + + return retval; +}