Logo Search packages:      
Sourcecode: linux-qcm-msm version File versions  Download package

usb.c

/*
 *  Copyright (C) 2004 Jan Kiszka
 *  Copyright (C) 2005 Giridhar Pemmasani
 *
 *  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.
 *
 */

#include "ndis.h"
#include "usb.h"
#include "usb_exports.h"

#ifdef USB_DEBUG
static unsigned int urb_id = 0;

#define DUMP_WRAP_URB(wrap_urb, dir)                              \
      USBTRACE("urb %p (%d) %s: buf: %p, len: %d, pipe: 0x%x, %d",      \
             (wrap_urb)->urb, (wrap_urb)->id,               \
             (dir == USB_DIR_OUT) ? "going down" : "coming back", \
             (wrap_urb)->urb->transfer_buffer,              \
             (wrap_urb)->urb->transfer_buffer_length,       \
             (wrap_urb)->urb->pipe, (wrap_urb)->urb->status)

#define DUMP_URB_BUFFER(urb, dir)                           \
      while (debug >= 2) {                                  \
            int i;                                          \
            char msg[20], *t;                         \
            if (!urb->transfer_buffer)                      \
                  break;                                    \
            if (!((usb_pipein(urb->pipe) && dir == USB_DIR_IN) || \
                  (usb_pipeout(urb->pipe) && dir == USB_DIR_OUT)))      \
                  break;                                    \
            t = msg;                                  \
            t += sprintf(t, "%d: ", (urb)->actual_length);        \
            for (i = 0; i < urb->actual_length &&                 \
                       t < &msg[sizeof(msg) - 4]; i++)            \
                  t += sprintf(t, "%02X ",                  \
                             ((char *)urb->transfer_buffer)[i]); \
            *t = 0;                                         \
            USBTRACE("%s", msg);                            \
            break;                                          \
      }

#else

#define DUMP_WRAP_URB(wrap_urb, dir) (void)0
#define DUMP_URB_BUFFER(urb, dir) (void)0

#endif

#define CUR_ALT_SETTING(intf) (intf)->cur_altsetting

#ifndef USB_CTRL_SET_TIMEOUT
#define USB_CTRL_SET_TIMEOUT 5000
#endif

#ifndef USB_CTRL_GET_TIMEOUT
#define USB_CTRL_GET_TIMEOUT 5000
#endif

#ifndef URB_NO_TRANSFER_DMA_MAP
#define URB_NO_TRANSFER_DMA_MAP 0
#endif

/* wrap_urb->flags */
/* transfer_buffer for urb is allocated; free it in wrap_free_urb */
#define WRAP_URB_COPY_BUFFER 0x01

static int inline wrap_cancel_urb(struct wrap_urb *wrap_urb)
{
      int ret;
      USBTRACE("%p, %p, %d", wrap_urb, wrap_urb->urb, wrap_urb->state);
      if (wrap_urb->state != URB_SUBMITTED)
            USBEXIT(return -1);
      ret = usb_unlink_urb(wrap_urb->urb);
      USBTRACE("ret: %d", ret);
      if (ret == -EINPROGRESS)
            return 0;
      else {
            WARNING("unlink failed: %d", ret);
            return ret;
      }
}

#define URB_STATUS(wrap_urb) (wrap_urb->urb->status)

static struct nt_list wrap_urb_complete_list;
static spinlock_t wrap_urb_complete_list_lock;

static work_struct_t wrap_urb_complete_work;
static void wrap_urb_complete_worker(worker_param_t dummy);

static void kill_all_urbs(struct wrap_device *wd, int complete)
{
      struct nt_list *ent;
      struct wrap_urb *wrap_urb;
      KIRQL irql;

      USBTRACE("%d", wd->usb.num_alloc_urbs);
      while (1) {
            IoAcquireCancelSpinLock(&irql);
            ent = RemoveHeadList(&wd->usb.wrap_urb_list);
            IoReleaseCancelSpinLock(irql);
            if (!ent)
                  break;
            wrap_urb = container_of(ent, struct wrap_urb, list);
            if (wrap_urb->state == URB_SUBMITTED) {
                  WARNING("Windows driver %s didn't free urb: %p",
                        wd->driver->name, wrap_urb->urb);
                  if (!complete)
                        wrap_urb->urb->complete = NULL;
                  usb_kill_urb(wrap_urb->urb);
            }
            USBTRACE("%p, %p", wrap_urb, wrap_urb->urb);
            usb_free_urb(wrap_urb->urb);
            kfree(wrap_urb);
      }
      wd->usb.num_alloc_urbs = 0;
}

/* for a given Linux urb status code, return corresponding NT urb status */
static USBD_STATUS wrap_urb_status(int urb_status)
{
      switch (urb_status) {
      case 0:
            return USBD_STATUS_SUCCESS;
      case -EPROTO:
            return USBD_STATUS_TIMEOUT;
      case -EILSEQ:
            return USBD_STATUS_CRC;
      case -EPIPE:
            return USBD_STATUS_INVALID_PIPE_HANDLE;
      case -ECOMM:
            return USBD_STATUS_DATA_OVERRUN;
      case -ENOSR:
            return USBD_STATUS_DATA_UNDERRUN;
      case -EOVERFLOW:
            return USBD_STATUS_BABBLE_DETECTED;
      case -EREMOTEIO:
            return USBD_STATUS_ERROR_SHORT_TRANSFER;;
      case -ENODEV:
      case -ESHUTDOWN:
      case -ENOENT:
            return USBD_STATUS_DEVICE_GONE;
      case -ENOMEM:
            return USBD_STATUS_NO_MEMORY;
      case -EINVAL:
            return USBD_STATUS_REQUEST_FAILED;
      default:
            return USBD_STATUS_NOT_SUPPORTED;
      }
}

/* for a given USBD_STATUS, return its corresponding NTSTATUS (for irp) */
static NTSTATUS nt_urb_irp_status(USBD_STATUS nt_urb_status)
{
      switch (nt_urb_status) {
      case USBD_STATUS_SUCCESS:
            return STATUS_SUCCESS;
      case USBD_STATUS_DEVICE_GONE:
            return STATUS_DEVICE_REMOVED;
      case USBD_STATUS_PENDING:
            return STATUS_PENDING;
      case USBD_STATUS_NOT_SUPPORTED:
            return STATUS_NOT_IMPLEMENTED;
      case USBD_STATUS_NO_MEMORY:
            return STATUS_NO_MEMORY;
      case USBD_STATUS_REQUEST_FAILED:
            return STATUS_NOT_SUPPORTED;
      default:
            return STATUS_FAILURE;
      }
}

static void wrap_free_urb(struct urb *urb)
{
      struct irp *irp;
      struct wrap_urb *wrap_urb;

      USBTRACE("freeing urb: %p", urb);
      wrap_urb = urb->context;
      irp = wrap_urb->irp;
      if (wrap_urb->flags & WRAP_URB_COPY_BUFFER) {
            USBTRACE("freeing DMA buffer for URB: %p %p",
                   urb, urb->transfer_buffer);
            usb_buffer_free(IRP_WRAP_DEVICE(irp)->usb.udev,
                        urb->transfer_buffer_length,
                        urb->transfer_buffer, urb->transfer_dma);
      }
      if (urb->setup_packet)
            kfree(urb->setup_packet);
      if (IRP_WRAP_DEVICE(irp)->usb.num_alloc_urbs > MAX_ALLOCATED_URBS) {
            IoAcquireCancelSpinLock(&irp->cancel_irql);
            RemoveEntryList(&wrap_urb->list);
            IRP_WRAP_DEVICE(irp)->usb.num_alloc_urbs--;
            IoReleaseCancelSpinLock(irp->cancel_irql);
            usb_free_urb(urb);
            kfree(wrap_urb);
      } else {
            wrap_urb->state = URB_FREE;
            wrap_urb->flags = 0;
            wrap_urb->irp = NULL;
      }
      return;
}

void wrap_suspend_urbs(struct wrap_device *wd)
{
      /* TODO: do we need to cancel urbs? */
      USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs);
}

void wrap_resume_urbs(struct wrap_device *wd)
{
      /* TODO: do we need to resubmit urbs? */
      USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs);
}

wstdcall void wrap_cancel_irp(struct device_object *dev_obj, struct irp *irp)
{
      struct urb *urb;

      /* NB: this function is called holding Cancel spinlock */
      USBENTER("irp: %p", irp);
      urb = IRP_WRAP_URB(irp)->urb;
      USBTRACE("canceling urb %p", urb);
      if (wrap_cancel_urb(IRP_WRAP_URB(irp))) {
            irp->cancel = FALSE;
            ERROR("urb %p can't be canceld: %d", urb,
                  IRP_WRAP_URB(irp)->state);
      } else
            USBTRACE("urb %p canceled", urb);
      IoReleaseCancelSpinLock(irp->cancel_irql);
      return;
}
WIN_FUNC_DECL(wrap_cancel_irp,2)

static struct urb *wrap_alloc_urb(struct irp *irp, unsigned int pipe,
                          void *buf, unsigned int buf_len)
{
      struct urb *urb;
      gfp_t alloc_flags;
      struct wrap_urb *wrap_urb;
      struct wrap_device *wd;

      USBENTER("irp: %p", irp);
      wd = IRP_WRAP_DEVICE(irp);
      alloc_flags = irql_gfp();
      IoAcquireCancelSpinLock(&irp->cancel_irql);
      urb = NULL;
      nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) {
            if (cmpxchg(&wrap_urb->state, URB_FREE,
                      URB_ALLOCATED) == URB_FREE) {
                  urb = wrap_urb->urb;
                  usb_init_urb(urb);
                  break;
            }
      }
      if (!urb) {
            IoReleaseCancelSpinLock(irp->cancel_irql);
            wrap_urb = kzalloc(sizeof(*wrap_urb), alloc_flags);
            if (!wrap_urb) {
                  WARNING("couldn't allocate memory");
                  return NULL;
            }
            urb = usb_alloc_urb(0, alloc_flags);
            if (!urb) {
                  WARNING("couldn't allocate urb");
                  kfree(wrap_urb);
                  return NULL;
            }
            IoAcquireCancelSpinLock(&irp->cancel_irql);
            wrap_urb->urb = urb;
            wrap_urb->state = URB_ALLOCATED;
            InsertTailList(&wd->usb.wrap_urb_list, &wrap_urb->list);
            wd->usb.num_alloc_urbs++;
      }

#ifdef URB_ASYNC_UNLINK
      urb->transfer_flags |= URB_ASYNC_UNLINK;
#elif defined(USB_ASYNC_UNLINK)
      urb->transfer_flags |= USB_ASYNC_UNLINK;
#endif
      urb->context = wrap_urb;
      wrap_urb->irp = irp;
      IRP_WRAP_URB(irp) = wrap_urb;
      /* called as Windows function */
      irp->cancel_routine = WIN_FUNC_PTR(wrap_cancel_irp,2);
      IoReleaseCancelSpinLock(irp->cancel_irql);
      USBTRACE("urb: %p", urb);

      urb->transfer_buffer_length = buf_len;
      if (buf_len && buf && (!virt_addr_valid(buf)
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_HIGHMEM4G)
                         || PageHighMem(virt_to_page(buf))
#endif
                )) {
            urb->transfer_buffer =
                  usb_buffer_alloc(wd->usb.udev, buf_len, alloc_flags,
                               &urb->transfer_dma);
            if (!urb->transfer_buffer) {
                  WARNING("couldn't allocate dma buf");
                  IoAcquireCancelSpinLock(&irp->cancel_irql);
                  wrap_urb->state = URB_FREE;
                  wrap_urb->irp = NULL;
                  IRP_WRAP_URB(irp) = NULL;
                  IoReleaseCancelSpinLock(irp->cancel_irql);
                  return NULL;
            }
            if (urb->transfer_dma)
                  urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
            wrap_urb->flags |= WRAP_URB_COPY_BUFFER;
            if (usb_pipeout(pipe))
                  memcpy(urb->transfer_buffer, buf, buf_len);
            USBTRACE("DMA buf for urb %p: %p", urb, urb->transfer_buffer);
      } else
            urb->transfer_buffer = buf;
      return urb;
}

static USBD_STATUS wrap_submit_urb(struct irp *irp)
{
      int ret;
      struct urb *urb;
      union nt_urb *nt_urb;

      urb = IRP_WRAP_URB(irp)->urb;
      nt_urb = IRP_URB(irp);
#ifdef USB_DEBUG
      if (IRP_WRAP_URB(irp)->state != URB_ALLOCATED) {
            ERROR("urb %p is in wrong state: %d",
                  urb, IRP_WRAP_URB(irp)->state);
            NT_URB_STATUS(nt_urb) = USBD_STATUS_REQUEST_FAILED;
            return NT_URB_STATUS(nt_urb);
      }
      IRP_WRAP_URB(irp)->id = pre_atomic_add(urb_id, 1);
#endif
      DUMP_WRAP_URB(IRP_WRAP_URB(irp), USB_DIR_OUT);
      irp->io_status.status = STATUS_PENDING;
      irp->io_status.info = 0;
      NT_URB_STATUS(nt_urb) = USBD_STATUS_PENDING;
      IoMarkIrpPending(irp);
      DUMP_URB_BUFFER(urb, USB_DIR_OUT);
      USBTRACE("%p", urb);
      IRP_WRAP_URB(irp)->state = URB_SUBMITTED;
      ret = usb_submit_urb(urb, irql_gfp());
      if (ret) {
            USBTRACE("ret: %d", ret);
            wrap_free_urb(urb);
            /* we assume that IRP was not in pending state before */
            IoUnmarkIrpPending(irp);
            NT_URB_STATUS(nt_urb) = wrap_urb_status(ret);
            USBEXIT(return NT_URB_STATUS(nt_urb));
      } else
            USBEXIT(return USBD_STATUS_PENDING);
}

static void wrap_urb_complete(struct urb *urb ISR_PT_REGS_PARAM_DECL)
{
      struct irp *irp;
      struct wrap_urb *wrap_urb;

      wrap_urb = urb->context;
      USBTRACE("%p (%p) completed", wrap_urb, urb);
      irp = wrap_urb->irp;
      DUMP_WRAP_URB(wrap_urb, USB_DIR_IN);
      irp->cancel_routine = NULL;
#ifdef USB_DEBUG
      if (wrap_urb->state != URB_SUBMITTED) {
            WARNING("urb %p in wrong state: %d (%d)", urb, wrap_urb->state,
                  urb->status);
            return;
      }
#endif
      wrap_urb->state = URB_COMPLETED;
      spin_lock(&wrap_urb_complete_list_lock);
      InsertTailList(&wrap_urb_complete_list, &wrap_urb->complete_list);
      spin_unlock(&wrap_urb_complete_list_lock);
      schedule_ntos_work(&wrap_urb_complete_work);
}

/* one worker for all devices */
static void wrap_urb_complete_worker(worker_param_t dummy)
{
      struct irp *irp;
      struct urb *urb;
      struct usbd_bulk_or_intr_transfer *bulk_int_tx;
      struct usbd_vendor_or_class_request *vc_req;
      union nt_urb *nt_urb;
      struct wrap_urb *wrap_urb;
      struct nt_list *ent;
      unsigned long flags;

      USBENTER("");
      while (1) {
            spin_lock_irqsave(&wrap_urb_complete_list_lock, flags);
            ent = RemoveHeadList(&wrap_urb_complete_list);
            spin_unlock_irqrestore(&wrap_urb_complete_list_lock, flags);
            if (!ent)
                  break;
            wrap_urb = container_of(ent, struct wrap_urb, complete_list);
            urb = wrap_urb->urb;
#ifdef USB_DEBUG
            if (wrap_urb->state != URB_COMPLETED &&
                wrap_urb->state != URB_INT_UNLINKED)
                  WARNING("urb %p in wrong state: %d",
                        urb, wrap_urb->state);
#endif
            irp = wrap_urb->irp;
            DUMP_IRP(irp);
            nt_urb = IRP_URB(irp);
            USBTRACE("urb: %p, nt_urb: %p, status: %d",
                   urb, nt_urb, urb->status);
            switch (urb->status) {
            case 0:
                  /* succesfully transferred */
                  irp->io_status.info = urb->actual_length;
                  if (nt_urb->header.function ==
                      URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) {
                        bulk_int_tx = &nt_urb->bulk_int_transfer;
                        bulk_int_tx->transfer_buffer_length =
                              urb->actual_length;
                        DUMP_URB_BUFFER(urb, USB_DIR_IN);
                        if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) &&
                            usb_pipein(urb->pipe))
                              memcpy(bulk_int_tx->transfer_buffer,
                                     urb->transfer_buffer,
                                     urb->actual_length);
                  } else { // vendor or class request
                        vc_req = &nt_urb->vendor_class_request;
                        vc_req->transfer_buffer_length =
                              urb->actual_length;
                        DUMP_URB_BUFFER(urb, USB_DIR_IN);
                        if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) &&
                            usb_pipein(urb->pipe))
                              memcpy(vc_req->transfer_buffer,
                                     urb->transfer_buffer,
                                     urb->actual_length);
                  }
                  NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS;
                  irp->io_status.status = STATUS_SUCCESS;
                  break;
            case -ENOENT:
            case -ECONNRESET:
                  /* urb canceled */
                  irp->io_status.info = 0;
                  TRACE2("urb %p canceled", urb);
                  NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS;
                  irp->io_status.status = STATUS_CANCELLED;
                  break;
            default:
                  TRACE2("irp: %p, urb: %p, status: %d/%d",
                         irp, urb, urb->status, wrap_urb->state);
                  irp->io_status.info = 0;
                  NT_URB_STATUS(nt_urb) = wrap_urb_status(urb->status);
                  irp->io_status.status =
                        nt_urb_irp_status(NT_URB_STATUS(nt_urb));
                  break;
            }
            wrap_free_urb(urb);
            IoCompleteRequest(irp, IO_NO_INCREMENT);
      }
      USBEXIT(return);
}

static USBD_STATUS wrap_bulk_or_intr_trans(struct irp *irp)
{
      usbd_pipe_handle pipe_handle;
      struct urb *urb;
      unsigned int pipe;
      struct usbd_bulk_or_intr_transfer *bulk_int_tx;
      USBD_STATUS status;
      struct usb_device *udev;
      union nt_urb *nt_urb;

      nt_urb = IRP_URB(irp);
      udev = IRP_WRAP_DEVICE(irp)->usb.udev;
      bulk_int_tx = &nt_urb->bulk_int_transfer;
      pipe_handle = bulk_int_tx->pipe_handle;
      USBTRACE("flags: 0x%x, length: %u, buffer: %p, handle: %p",
             bulk_int_tx->transfer_flags,
             bulk_int_tx->transfer_buffer_length,
             bulk_int_tx->transfer_buffer, pipe_handle);

      if (USBD_IS_BULK_PIPE(pipe_handle)) {
            if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN)
                  pipe = usb_rcvbulkpipe(udev,
                                     pipe_handle->bEndpointAddress);
            else
                  pipe = usb_sndbulkpipe(udev,
                                     pipe_handle->bEndpointAddress);
      } else {
            if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN)
                  pipe = usb_rcvintpipe(udev,
                                    pipe_handle->bEndpointAddress);
            else
                  pipe = usb_sndintpipe(udev,
                                    pipe_handle->bEndpointAddress);
      }

      DUMP_IRP(irp);
      urb = wrap_alloc_urb(irp, pipe, bulk_int_tx->transfer_buffer,
                       bulk_int_tx->transfer_buffer_length);
      if (!urb) {
            ERROR("couldn't allocate urb");
            return USBD_STATUS_NO_MEMORY;
      }
      if (usb_pipein(pipe) &&
          (!(bulk_int_tx->transfer_flags & USBD_SHORT_TRANSFER_OK))) {
            USBTRACE("short not ok");
            urb->transfer_flags |= URB_SHORT_NOT_OK;
      }
      if (usb_pipebulk(pipe)) {
            usb_fill_bulk_urb(urb, udev, pipe, urb->transfer_buffer,
                          bulk_int_tx->transfer_buffer_length,
                          wrap_urb_complete, urb->context);
            USBTRACE("submitting bulk urb %p on pipe 0x%x (ep 0x%x)",
                   urb, urb->pipe, pipe_handle->bEndpointAddress);
      } else {
            usb_fill_int_urb(urb, udev, pipe, urb->transfer_buffer,
                         bulk_int_tx->transfer_buffer_length,
                         wrap_urb_complete, urb->context,
                         pipe_handle->bInterval);
            USBTRACE("submitting interrupt urb %p on pipe 0x%x (ep 0x%x), "
                   "intvl: %d", urb, urb->pipe,
                   pipe_handle->bEndpointAddress, pipe_handle->bInterval);
      }
      status = wrap_submit_urb(irp);
      USBTRACE("status: %08X", status);
      USBEXIT(return status);
}

static USBD_STATUS wrap_vendor_or_class_req(struct irp *irp)
{
      u8 req_type;
      unsigned int pipe;
      struct usbd_vendor_or_class_request *vc_req;
      struct usb_device *udev;
      union nt_urb *nt_urb;
      USBD_STATUS status;
      struct urb *urb;
      struct usb_ctrlrequest *dr;

      nt_urb = IRP_URB(irp);
      udev = IRP_WRAP_DEVICE(irp)->usb.udev;
      vc_req = &nt_urb->vendor_class_request;
      USBTRACE("bits: %x, req: %x, val: %08x, index: %08x, flags: %x,"
             "buf: %p, len: %d", vc_req->reserved_bits, vc_req->request,
             vc_req->value, vc_req->index, vc_req->transfer_flags,
             vc_req->transfer_buffer, vc_req->transfer_buffer_length);

      USBTRACE("%x", nt_urb->header.function);
      switch (nt_urb->header.function) {
      case URB_FUNCTION_VENDOR_DEVICE:
            req_type = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
            break;
      case URB_FUNCTION_VENDOR_INTERFACE:
            req_type = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
            break;
      case URB_FUNCTION_VENDOR_ENDPOINT:
            req_type = USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
            break;
      case URB_FUNCTION_VENDOR_OTHER:
            req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER;
            break;
      case URB_FUNCTION_CLASS_DEVICE:
            req_type = USB_TYPE_CLASS | USB_RECIP_DEVICE;
            break;
      case URB_FUNCTION_CLASS_INTERFACE:
            req_type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
            break;
      case URB_FUNCTION_CLASS_ENDPOINT:
            req_type = USB_TYPE_CLASS | USB_RECIP_ENDPOINT;
            break;
      case URB_FUNCTION_CLASS_OTHER:
            req_type = USB_TYPE_CLASS | USB_RECIP_OTHER;
            break;
      default:
            ERROR("unknown request type: %x", nt_urb->header.function);
            req_type = 0;
            break;
      }

      req_type |= vc_req->reserved_bits;
      USBTRACE("req type: %08x", req_type);

      if (vc_req->transfer_flags & USBD_TRANSFER_DIRECTION_IN) {
            pipe = usb_rcvctrlpipe(udev, 0);
            req_type |= USB_DIR_IN;
            USBTRACE("pipe: %x, dir in", pipe);
      } else {
            pipe = usb_sndctrlpipe(udev, 0);
            req_type |= USB_DIR_OUT;
            USBTRACE("pipe: %x, dir out", pipe);
      }
      urb = wrap_alloc_urb(irp, pipe, vc_req->transfer_buffer,
                       vc_req->transfer_buffer_length);
      if (!urb) {
            ERROR("couldn't allocate urb");
            return USBD_STATUS_NO_MEMORY;
      }

      if (usb_pipein(pipe) &&
          (!(vc_req->transfer_flags & USBD_SHORT_TRANSFER_OK))) {
            USBTRACE("short not ok");
            urb->transfer_flags |= URB_SHORT_NOT_OK;
      }

      dr = kzalloc(sizeof(*dr), GFP_ATOMIC);
      if (!dr) {
            ERROR("couldn't allocate memory");
            wrap_free_urb(urb);
            return USBD_STATUS_NO_MEMORY;
      }
      dr->bRequestType = req_type;
      dr->bRequest = vc_req->request;
      dr->wValue = cpu_to_le16(vc_req->value);
      dr->wIndex = cpu_to_le16((u16)vc_req->index);
      dr->wLength = cpu_to_le16((u16)urb->transfer_buffer_length);

      usb_fill_control_urb(urb, udev, pipe, (unsigned char *)dr,
                       urb->transfer_buffer, urb->transfer_buffer_length,
                       wrap_urb_complete, urb->context);
      status = wrap_submit_urb(irp);
      USBTRACE("status: %08X", status);
      USBEXIT(return status);
}

static USBD_STATUS wrap_reset_pipe(struct usb_device *udev, struct irp *irp)
{
      int ret;
      union nt_urb *nt_urb;
      usbd_pipe_handle pipe_handle;
      unsigned int pipe1, pipe2;

      nt_urb = IRP_URB(irp);
      pipe_handle = nt_urb->pipe_req.pipe_handle;
      /* TODO: not clear if both directions should be cleared? */
      if (USBD_IS_BULK_PIPE(pipe_handle)) {
            pipe1 = usb_rcvbulkpipe(udev, pipe_handle->bEndpointAddress);
            pipe2 = usb_sndbulkpipe(udev, pipe_handle->bEndpointAddress);
      } else if (USBD_IS_INT_PIPE(pipe_handle)) {
            pipe1 = usb_rcvintpipe(udev, pipe_handle->bEndpointAddress);
            pipe2 = pipe1;
      } else {
            WARNING("invalid pipe %d", pipe_handle->bEndpointAddress);
            return USBD_STATUS_INVALID_PIPE_HANDLE;
      }
      USBTRACE("ep: %d, pipe: 0x%x", pipe_handle->bEndpointAddress, pipe1);
      ret = usb_clear_halt(udev, pipe1);
      if (ret)
            USBTRACE("resetting pipe %d failed: %d", pipe1, ret);
      if (pipe2 != pipe1) {
            ret = usb_clear_halt(udev, pipe2);
            if (ret)
                  USBTRACE("resetting pipe %d failed: %d", pipe2, ret);
      }
//    return wrap_urb_status(ret);
      return USBD_STATUS_SUCCESS;
}

static USBD_STATUS wrap_abort_pipe(struct usb_device *udev, struct irp *irp)
{
      union nt_urb *nt_urb;
      usbd_pipe_handle pipe_handle;
      struct wrap_urb *wrap_urb;
      struct wrap_device *wd;
      KIRQL irql;

      wd = IRP_WRAP_DEVICE(irp);
      nt_urb = IRP_URB(irp);
      pipe_handle = nt_urb->pipe_req.pipe_handle;
      USBENTER("%p, %x", irp, pipe_handle->bEndpointAddress);
      IoAcquireCancelSpinLock(&irql);
      nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) {
            USBTRACE("%p, %p, %d, %x, %x", wrap_urb, wrap_urb->urb,
                   wrap_urb->state, wrap_urb->urb->pipe,
                   usb_pipeendpoint(wrap_urb->urb->pipe));
            /* for WG111T driver, urbs for endpoint 0 should also
             * be canceled */
            if ((usb_pipeendpoint(wrap_urb->urb->pipe) ==
                 pipe_handle->bEndpointAddress) ||
                (usb_pipeendpoint(wrap_urb->urb->pipe) == 0)) {
                  if (wrap_cancel_urb(wrap_urb) == 0)
                        USBTRACE("canceled wrap_urb: %p", wrap_urb);
            }
      }
      IoReleaseCancelSpinLock(irql);
      NT_URB_STATUS(nt_urb) = USBD_STATUS_CANCELED;
      USBEXIT(return USBD_STATUS_SUCCESS);
}

static USBD_STATUS wrap_set_clear_feature(struct usb_device *udev,
                                struct irp *irp)
{
      union nt_urb *nt_urb;
      struct urb_control_feature_request *feat_req;
      int ret = 0;
      __u8 request, type;
      __u16 feature;

      nt_urb = IRP_URB(irp);
      feat_req = &nt_urb->feat_req;
      feature = feat_req->feature_selector;
      switch (nt_urb->header.function) {
      case URB_FUNCTION_SET_FEATURE_TO_DEVICE:
            request = USB_REQ_SET_FEATURE;
            type = USB_DT_DEVICE;
            break;
      case URB_FUNCTION_SET_FEATURE_TO_INTERFACE:
            request = USB_REQ_SET_FEATURE;
            type =  USB_DT_INTERFACE;
            break;
      case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT:
            request = USB_REQ_SET_FEATURE;
            type =  USB_DT_ENDPOINT;
            break;
      case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE:
            request = USB_REQ_CLEAR_FEATURE;
            type =  USB_DT_DEVICE;
            break;
      case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE:
            request = USB_REQ_CLEAR_FEATURE;
            type =  USB_DT_INTERFACE;
            break;
      case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT:
            request = USB_REQ_CLEAR_FEATURE;
            type =  USB_DT_ENDPOINT;
            break;
      default:
            WARNING("invalid function: %x", nt_urb->header.function);
            NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED;
            return NT_URB_STATUS(nt_urb);
      }
      ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), request, type,
                        feature, feat_req->index, NULL, 0, 1000);
      NT_URB_STATUS(nt_urb) = wrap_urb_status(ret);
      USBEXIT(return NT_URB_STATUS(nt_urb));
}

static USBD_STATUS wrap_get_status_request(struct usb_device *udev,
                                 struct irp *irp)
{
      union nt_urb *nt_urb;
      struct urb_control_get_status_request *status_req;
      int ret = 0;
      __u8 type;

      nt_urb = IRP_URB(irp);
      status_req = &nt_urb->status_req;
      switch (nt_urb->header.function) {
      case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
            type = USB_RECIP_DEVICE;
            break;
      case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:
            type = USB_RECIP_INTERFACE;
            break;
      case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:
            type = USB_RECIP_ENDPOINT;
            break;
      default:
            WARNING("invalid function: %x", nt_urb->header.function);
            NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED;
            return NT_URB_STATUS(nt_urb);
      }
      assert(status_req->transfer_buffer_length == sizeof(u16));
      ret = usb_get_status(udev, type, status_req->index,
                       status_req->transfer_buffer);
      if (ret >= 0) {
            assert(ret <= status_req->transfer_buffer_length);
            status_req->transfer_buffer_length = ret;
            NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS;
      } else
            NT_URB_STATUS(nt_urb) = wrap_urb_status(ret);
      USBEXIT(return NT_URB_STATUS(nt_urb));
}

static void set_intf_pipe_info(struct wrap_device *wd,
                         struct usb_interface *usb_intf,
                         struct usbd_interface_information *intf)
{
      int i;
      struct usb_endpoint_descriptor *ep;
      struct usbd_pipe_information *pipe;

      for (i = 0; i < CUR_ALT_SETTING(usb_intf)->desc.bNumEndpoints; i++) {
            ep = &(CUR_ALT_SETTING(usb_intf)->endpoint[i]).desc;
            if (i >= intf->bNumEndpoints) {
                  ERROR("intf %p has only %d endpoints, "
                        "ignoring endpoints above %d",
                        intf, intf->bNumEndpoints, i);
                  break;
            }
            pipe = &intf->pipes[i];

            if (pipe->flags & USBD_PF_CHANGE_MAX_PACKET)
                  USBTRACE("pkt_sz: %d: %d", pipe->wMaxPacketSize,
                         pipe->max_tx_size);
            USBTRACE("driver wants max_tx_size to %d",
                   pipe->max_tx_size);

            pipe->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
            pipe->bEndpointAddress = ep->bEndpointAddress;
            pipe->type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
            if (pipe->type == UsbdPipeTypeInterrupt) {
                  /* Windows and Linux differ in how the
                   * bInterval is interpretted */
                  /* for low speed:
                     interval (Windows) -> frames per ms (Linux)
                     0 to 15    -> 8
                     16 to 35   -> 16
                     36 to 255  -> 32

                     for full speed: interval -> frames per ms
                     1          -> 1
                     2 to 3     -> 2
                     4 to 7     -> 4
                     8 to 15    -> 8
                     16 to 31   -> 16
                     32 to 255  -> 32

                     for high speed: interval -> microframes
                     1          -> 1
                     2          -> 2
                     3          -> 4
                     4          -> 8
                     5          -> 16
                     6          -> 32
                     7 to 255   -> 32
                  */
                  if (wd->usb.udev->speed == USB_SPEED_LOW)
                        pipe->bInterval = ep->bInterval + 5;
                  else if (wd->usb.udev->speed == USB_SPEED_FULL)
                        pipe->bInterval = ep->bInterval;
                  else {
                        int j, k;
                        for (j = k = 1; j < ep->bInterval; k++)
                              j *= 2;
                        pipe->bInterval = k;
                  }
            }
            pipe->handle = ep;
            USBTRACE("%d: ep 0x%x, type %d, pkt_sz %d, intv %d (%d),"
                   "type: %d, handle %p", i, ep->bEndpointAddress,
                   ep->bmAttributes, pipe->wMaxPacketSize, ep->bInterval,
                   pipe->bInterval, pipe->type, pipe->handle);
      }
}

static USBD_STATUS wrap_select_configuration(struct wrap_device *wd,
                                   union nt_urb *nt_urb,
                                   struct irp *irp)
{
      int i, ret;
      struct usbd_select_configuration *sel_conf;
      struct usb_device *udev;
      struct usbd_interface_information *intf;
      struct usb_config_descriptor *config;
      struct usb_interface *usb_intf;

      udev = wd->usb.udev;
      sel_conf = &nt_urb->select_conf;
      config = sel_conf->config;
      USBTRACE("%p", config);
      if (config == NULL) {
            kill_all_urbs(wd, 1);
            ret = usb_reset_configuration(udev);
            return wrap_urb_status(ret);
      }

      USBTRACE("conf: %d, type: %d, length: %d, numif: %d, attr: %08x",
             config->bConfigurationValue, config->bDescriptorType,
             config->wTotalLength, config->bNumInterfaces,
             config->bmAttributes);
      ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                        USB_REQ_SET_CONFIGURATION, 0,
                        config->bConfigurationValue, 0,
                        NULL, 0, USB_CTRL_SET_TIMEOUT);
      if (ret < 0) {
            ERROR("ret: %d", ret);
            return wrap_urb_status(ret);
      }
      sel_conf->handle = udev->actconfig;
      intf = &sel_conf->intf;
      for (i = 0; i < config->bNumInterfaces && intf->bLength > 0;
           i++, intf = (((void *)intf) + intf->bLength)) {

            USBTRACE("intf: %d, alt setting: %d",
                   intf->bInterfaceNumber, intf->bAlternateSetting);
            ret = usb_set_interface(udev, intf->bInterfaceNumber,
                              intf->bAlternateSetting);
            if (ret < 0) {
                  ERROR("failed with %d", ret);
                  return wrap_urb_status(ret);
            }
            usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber);
            if (!usb_intf) {
                  ERROR("couldn't obtain ifnum");
                  return USBD_STATUS_REQUEST_FAILED;
            }
            USBTRACE("intf: %p, num ep: %d", intf, intf->bNumEndpoints);
            set_intf_pipe_info(wd, usb_intf, intf);
      }
      return USBD_STATUS_SUCCESS;
}

static USBD_STATUS wrap_select_interface(struct wrap_device *wd,
                               union nt_urb *nt_urb,
                               struct irp *irp)
{
      int ret;
      struct usbd_select_interface *sel_intf;
      struct usb_device *udev;
      struct usbd_interface_information *intf;
      struct usb_interface *usb_intf;

      udev = wd->usb.udev;
      sel_intf = &nt_urb->select_intf;
      intf = &sel_intf->intf;

      ret = usb_set_interface(udev, intf->bInterfaceNumber,
                        intf->bAlternateSetting);
      if (ret < 0) {
            ERROR("failed with %d", ret);
            return wrap_urb_status(ret);
      }
      usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber);
      if (!usb_intf) {
            ERROR("couldn't get interface information");
            return USBD_STATUS_REQUEST_FAILED;
      }
      USBTRACE("intf: %p, num ep: %d", usb_intf, intf->bNumEndpoints);
      set_intf_pipe_info(wd, usb_intf, intf);
      return USBD_STATUS_SUCCESS;
}

static int wrap_usb_get_string(struct usb_device *udev, unsigned short langid,
                         unsigned char index, void *buf, int size)
{
      int i, ret;
      /* if langid is 0, return array of langauges supported in
       * buf */
      for (i = 0; i < 3; i++) {
            ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
                              USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                              (USB_DT_STRING << 8) + index, langid,
                              buf, size, USB_CTRL_GET_TIMEOUT);
            if (ret > 0 || ret == -EPIPE)
                  break;
      }
      return ret;
}

static USBD_STATUS wrap_get_descriptor(struct wrap_device *wd,
                               union nt_urb *nt_urb, struct irp *irp)
{
      struct usbd_control_descriptor_request *control_desc;
      int ret = 0;
      struct usb_device *udev;

      udev = wd->usb.udev;
      control_desc = &nt_urb->control_desc;
      USBTRACE("desctype = %d, descindex = %d, transfer_buffer = %p,"
             "transfer_buffer_length = %d", control_desc->desc_type,
             control_desc->index, control_desc->transfer_buffer,
             control_desc->transfer_buffer_length);

      if (control_desc->desc_type == USB_DT_STRING) {
            USBTRACE("langid: %x", control_desc->language_id);
            ret = wrap_usb_get_string(udev, control_desc->language_id,
                                control_desc->index,
                                control_desc->transfer_buffer,
                                control_desc->transfer_buffer_length);
      } else {
            ret = usb_get_descriptor(udev, control_desc->desc_type,
                               control_desc->index,
                               control_desc->transfer_buffer,
                               control_desc->transfer_buffer_length);
      }
      if (ret < 0) {
            USBTRACE("request %d failed: %d", control_desc->desc_type, ret);
            control_desc->transfer_buffer_length = 0;
            return wrap_urb_status(ret);
      } else {
            USBTRACE("ret: %08x", ret);
            control_desc->transfer_buffer_length = ret;
            irp->io_status.info = ret;
            return USBD_STATUS_SUCCESS;
      }
}

static USBD_STATUS wrap_process_nt_urb(struct irp *irp)
{
      union nt_urb *nt_urb;
      struct usb_device *udev;
      USBD_STATUS status;
      struct wrap_device *wd;

      wd = IRP_WRAP_DEVICE(irp);
      udev = wd->usb.udev;
      nt_urb = IRP_URB(irp);
      USBENTER("nt_urb = %p, irp = %p, length = %d, function = %x",
             nt_urb, irp, nt_urb->header.length, nt_urb->header.function);

      DUMP_IRP(irp);
      switch (nt_urb->header.function) {
            /* bulk/int and vendor/class urbs are submitted to
             * Linux USB core; if the call is sucessful, urb's
             * completion worker will return IRP later */
      case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
            USBTRACE("submitting bulk/int irp: %p", irp);
            status = wrap_bulk_or_intr_trans(irp);
            break;

      case URB_FUNCTION_VENDOR_DEVICE:
      case URB_FUNCTION_VENDOR_INTERFACE:
      case URB_FUNCTION_VENDOR_ENDPOINT:
      case URB_FUNCTION_VENDOR_OTHER:
      case URB_FUNCTION_CLASS_DEVICE:
      case URB_FUNCTION_CLASS_INTERFACE:
      case URB_FUNCTION_CLASS_ENDPOINT:
      case URB_FUNCTION_CLASS_OTHER:
            USBTRACE("submitting vendor/class irp: %p", irp);
            status = wrap_vendor_or_class_req(irp);
            break;

            /* rest are synchronous */
      case URB_FUNCTION_SELECT_CONFIGURATION:
            status = wrap_select_configuration(wd, nt_urb, irp);
            NT_URB_STATUS(nt_urb) = status;
            break;

      case URB_FUNCTION_SELECT_INTERFACE:
            status = wrap_select_interface(wd, nt_urb, irp);
            NT_URB_STATUS(nt_urb) = status;
            break;

      case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
            status = wrap_get_descriptor(wd, nt_urb, irp);
            NT_URB_STATUS(nt_urb) = status;
            break;

      case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL:
            status = wrap_reset_pipe(udev, irp);
            NT_URB_STATUS(nt_urb) = status;
            break;

      case URB_FUNCTION_ABORT_PIPE:
            status = wrap_abort_pipe(udev, irp);
            break;

      case URB_FUNCTION_SET_FEATURE_TO_DEVICE:
      case URB_FUNCTION_SET_FEATURE_TO_INTERFACE:
      case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT:
      case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE:
      case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE:
      case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT:
            status = wrap_set_clear_feature(udev, irp);
            break;

      case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
      case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:
      case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:
            status = wrap_get_status_request(udev, irp);
            break;

      default:
            ERROR("function %x not implemented", nt_urb->header.function);
            status = NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED;
            break;
      }
      USBTRACE("status: %08X", status);
      return status;
}

static USBD_STATUS wrap_reset_port(struct irp *irp)
{
      no_warn_unused int ret, lock = 0;
      struct wrap_device *wd;

      wd = IRP_WRAP_DEVICE(irp);
      USBENTER("%p, %p", wd, wd->usb.udev);
      lock = usb_lock_device_for_reset(wd->usb.udev, wd->usb.intf);
      if (lock < 0) {
            WARNING("locking failed: %d", lock);
//          return wrap_urb_status(lock);
            return USBD_STATUS_SUCCESS;
      }
      ret = usb_reset_device(wd->usb.udev);
      if (ret < 0)
            USBTRACE("reset failed: %d", ret);
      /* TODO: should reconfigure? */
      if (lock)
            usb_unlock_device(wd->usb.udev);
//    return wrap_urb_status(ret);
      return USBD_STATUS_SUCCESS;
}

static USBD_STATUS wrap_get_port_status(struct irp *irp)
{
      struct wrap_device *wd;
      ULONG *status;
      enum usb_device_state state;

      wd = IRP_WRAP_DEVICE(irp);
      USBENTER("%p, %p", wd, wd->usb.udev);
      status = IoGetCurrentIrpStackLocation(irp)->params.others.arg1;
      state = wd->usb.udev->state;
      if (state != USB_STATE_NOTATTACHED &&
          state != USB_STATE_SUSPENDED) {
            *status |= USBD_PORT_CONNECTED;
            if (state == USB_STATE_CONFIGURED)
                  *status |= USBD_PORT_ENABLED;
      }
      USBTRACE("state: %d, *status: %08X", state, *status);
      return USBD_STATUS_SUCCESS;
}

NTSTATUS wrap_submit_irp(struct device_object *pdo, struct irp *irp)
{
      struct io_stack_location *irp_sl;
      struct wrap_device *wd;
      USBD_STATUS status;
      struct usbd_idle_callback *idle_callback;

      USBENTER("%p, %p", pdo, irp);
      wd = pdo->reserved;
      if (wd->usb.intf == NULL) {
            USBTRACE("%p", irp);
            irp->io_status.status = STATUS_DEVICE_REMOVED;
            irp->io_status.info = 0;
            USBEXIT(return STATUS_DEVICE_REMOVED);
      }
      IRP_WRAP_DEVICE(irp) = wd;
      irp_sl = IoGetCurrentIrpStackLocation(irp);
      switch (irp_sl->params.dev_ioctl.code) {
      case IOCTL_INTERNAL_USB_SUBMIT_URB:
            status = wrap_process_nt_urb(irp);
            break;
      case IOCTL_INTERNAL_USB_RESET_PORT:
            status = wrap_reset_port(irp);
            break;
      case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
            status = wrap_get_port_status(irp);
            break;
      case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
            idle_callback = irp_sl->params.dev_ioctl.type3_input_buf;
            USBTRACE("suspend function: %p", idle_callback->callback);
            status = USBD_STATUS_NOT_SUPPORTED;
            break;
      default:
            ERROR("ioctl %08X NOT IMPLEMENTED",
                  irp_sl->params.dev_ioctl.code);
            status = USBD_STATUS_NOT_SUPPORTED;
            break;
      }

      USBTRACE("status: %08X", status);
      if (status == USBD_STATUS_PENDING) {
            /* don't touch this IRP - it may have been already
             * completed/returned */
            return STATUS_PENDING;
      } else {
            irp->io_status.status = nt_urb_irp_status(status);
            if (status != USBD_STATUS_SUCCESS)
                  irp->io_status.info = 0;
            USBEXIT(return irp->io_status.status);
      }
}

/* TODO: The example on msdn in reference section suggests that second
 * argument should be an array of usbd_interface_information, but
 * description and examples elsewhere suggest that it should be
 * usbd_interface_list_entry structre. Which is correct? */

wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequestEx,2)
      (struct usb_config_descriptor *config,
       struct usbd_interface_list_entry *intf_list)
{
      int size, i, n;
      struct usbd_interface_information *intf;
      struct usbd_pipe_information *pipe;
      struct usb_interface_descriptor *intf_desc;
      struct usbd_select_configuration *select_conf;

      USBENTER("config = %p, intf_list = %p", config, intf_list);

      /* calculate size required; select_conf already has space for
       * one intf structure */
      size = sizeof(*select_conf) - sizeof(*intf);
      for (n = 0; n < config->bNumInterfaces; n++) {
            i = intf_list[n].intf_desc->bNumEndpoints;
            /* intf already has space for one pipe */
            size += sizeof(*intf) + (i - 1) * sizeof(*pipe);
      }
      /* don't use kmalloc - driver frees it with ExFreePool */
      select_conf = ExAllocatePoolWithTag(NonPagedPool, size,
                                  POOL_TAG('L', 'U', 'S', 'B'));
      if (!select_conf) {
            WARNING("couldn't allocate memory");
            return NULL;
      }
      memset(select_conf, 0, size);
      intf = &select_conf->intf;
      select_conf->handle = config;
      for (n = 0; n < config->bNumInterfaces && intf_list[n].intf_desc; n++) {
            /* initialize 'intf' fields in intf_list so they point
             * to appropriate entry; these may be read/written by
             * driver after this function returns */
            intf_list[n].intf = intf;
            intf_desc = intf_list[n].intf_desc;

            i = intf_desc->bNumEndpoints;
            intf->bLength = sizeof(*intf) + (i - 1) * sizeof(*pipe);

            intf->bInterfaceNumber = intf_desc->bInterfaceNumber;
            intf->bAlternateSetting = intf_desc->bAlternateSetting;
            intf->bInterfaceClass = intf_desc->bInterfaceClass;
            intf->bInterfaceSubClass = intf_desc->bInterfaceSubClass;
            intf->bInterfaceProtocol = intf_desc->bInterfaceProtocol;
            intf->bNumEndpoints = intf_desc->bNumEndpoints;

            pipe = &intf->pipes[0];
            for (i = 0; i < intf->bNumEndpoints; i++) {
                  memset(&pipe[i], 0, sizeof(*pipe));
                  pipe[i].max_tx_size =
                        USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
            }
            intf->handle = intf_desc;
            intf = (((void *)intf) + intf->bLength);
      }
      select_conf->header.function = URB_FUNCTION_SELECT_CONFIGURATION;
      select_conf->header.length = size;
      select_conf->config = config;
      USBEXIT(return (union nt_urb *)select_conf);
}

WIN_SYMBOL_MAP("_USBD_CreateConfigurationRequestEx@8", USBD_CreateConfigurationRequestEx)

wstdcall struct usb_interface_descriptor *
WIN_FUNC(USBD_ParseConfigurationDescriptorEx,7)
      (struct usb_config_descriptor *config, void *start,
       LONG bInterfaceNumber, LONG bAlternateSetting, LONG bInterfaceClass,
       LONG bInterfaceSubClass, LONG bInterfaceProtocol)
{
      void *pos;
      struct usb_interface_descriptor *intf;

      USBENTER("config = %p, start = %p, ifnum = %d, alt_setting = %d,"
             " class = %d, subclass = %d, proto = %d", config, start,
             bInterfaceNumber, bAlternateSetting, bInterfaceClass,
             bInterfaceSubClass, bInterfaceProtocol);

      for (pos = start;
           pos < ((void *)config + le16_to_cpu(config->wTotalLength));
           pos += intf->bLength) {

            intf = pos;

            if ((intf->bDescriptorType == USB_DT_INTERFACE) &&
                ((bInterfaceNumber == -1) ||
                 (intf->bInterfaceNumber == bInterfaceNumber)) &&
                ((bAlternateSetting == -1) ||
                 (intf->bAlternateSetting == bAlternateSetting)) &&
                ((bInterfaceClass == -1) ||
                 (intf->bInterfaceClass == bInterfaceClass)) &&
                ((bInterfaceSubClass == -1) ||
                 (intf->bInterfaceSubClass == bInterfaceSubClass)) &&
                ((bInterfaceProtocol == -1) ||
                 (intf->bInterfaceProtocol == bInterfaceProtocol))) {
                  USBTRACE("selected interface = %p", intf);
                  USBEXIT(return intf);
            }
      }
      USBEXIT(return NULL);
}

WIN_SYMBOL_MAP("_USBD_ParseConfigurationDescriptorEx@28", USBD_ParseConfigurationDescriptorEx)

wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequest,2)
      (struct usb_config_descriptor *config, USHORT *size)
{
      union nt_urb *nt_urb;
      struct usbd_interface_list_entry intf_list[2];
      struct usb_interface_descriptor *intf_desc;

      USBENTER("config = %p, urb_size = %p", config, size);

      intf_desc = USBD_ParseConfigurationDescriptorEx(config, config, -1, -1,
                                          -1, -1, -1);
      intf_list[0].intf_desc = intf_desc;
      intf_list[0].intf = NULL;
      intf_list[1].intf_desc = NULL;
      intf_list[1].intf = NULL;
      nt_urb = USBD_CreateConfigurationRequestEx(config, intf_list);
      if (!nt_urb)
            return NULL;

      *size = nt_urb->select_conf.header.length;
      USBEXIT(return nt_urb);
}

wstdcall struct usb_interface_descriptor *
WIN_FUNC(USBD_ParseConfigurationDescriptor,3)
      (struct usb_config_descriptor *config, UCHAR bInterfaceNumber,
       UCHAR bAlternateSetting)
{
      return USBD_ParseConfigurationDescriptorEx(config, config,
                                       bInterfaceNumber,
                                       bAlternateSetting,
                                       -1, -1, -1);
}

wstdcall usb_common_descriptor_t *WIN_FUNC(USBD_ParseDescriptors,4)
      (void *buf, ULONG length, void *start, LONG type)
{
      usb_common_descriptor_t *descr = start;

      while ((void *)descr < buf + length) {
            if (descr->bDescriptorType == type)
                  return descr;
            if (descr->bLength == 0)
                  break;
            descr = (void *)descr + descr->bLength;
      }
      USBEXIT(return NULL);
}

WIN_SYMBOL_MAP("_USBD_ParseDescriptors@16", USBD_ParseDescriptors)

wstdcall void WIN_FUNC(USBD_GetUSBDIVersion,1)
      (struct usbd_version_info *version_info)
{
      /* this function is obsolete in Windows XP */
      if (version_info) {
            version_info->usbdi_version = USBDI_VERSION_XP;
            /* TODO: how do we get this correctly? */
            version_info->supported_usb_version = 0x110;
      }
      USBEXIT(return);
}

wstdcall void
USBD_InterfaceGetUSBDIVersion(void *context,
                        struct usbd_version_info *version_info,
                        ULONG *hcd_capa)
{
      struct wrap_device *wd = context;

      if (version_info) {
            version_info->usbdi_version = USBDI_VERSION_XP;
            if (wd->usb.udev->speed == USB_SPEED_HIGH)
                  version_info->supported_usb_version = 0x200;
            else
                  version_info->supported_usb_version = 0x110;
      }
      *hcd_capa = USB_HCD_CAPS_SUPPORTS_RT_THREADS;
      USBEXIT(return);
}

wstdcall BOOLEAN USBD_InterfaceIsDeviceHighSpeed(void *context)
{
      struct wrap_device *wd = context;

      USBTRACE("wd: %p", wd);
      if (wd->usb.udev->speed == USB_SPEED_HIGH)
            USBEXIT(return TRUE);
      else
            USBEXIT(return FALSE);
}

wstdcall void USBD_InterfaceReference(void *context)
{
      USBTRACE("%p", context);
      TODO();
}

wstdcall void USBD_InterfaceDereference(void *context)
{
      USBTRACE("%p", context);
      TODO();
}

wstdcall NTSTATUS USBD_InterfaceQueryBusTime(void *context, ULONG *frame)
{
      struct wrap_device *wd = context;

      *frame = usb_get_current_frame_number(wd->usb.udev);
      USBEXIT(return STATUS_SUCCESS);
}

wstdcall NTSTATUS USBD_InterfaceSubmitIsoOutUrb(void *context,
                                     union nt_urb *nt_urb)
{
      /* TODO: implement this */
      TODO();
      USBEXIT(return STATUS_NOT_IMPLEMENTED);
}

wstdcall NTSTATUS
USBD_InterfaceQueryBusInformation(void *context, ULONG level, void *buf,
                          ULONG *buf_length, ULONG *buf_actual_length)
{
      struct wrap_device *wd = context;
      struct usb_bus_information_level *bus_info;
      struct usb_bus *bus;

      bus = wd->usb.udev->bus;
      bus_info = buf;
      TODO();
      USBEXIT(return STATUS_NOT_IMPLEMENTED);
}

wstdcall NTSTATUS
USBD_InterfaceLogEntry(void *context, ULONG driver_tag, ULONG enum_tag,
                   ULONG p1, ULONG p2)
{
      ERROR("%p, %x, %x, %x, %x", context, driver_tag, enum_tag, p1, p2);
      USBEXIT(return STATUS_SUCCESS);
}

int usb_init(void)
{
      InitializeListHead(&wrap_urb_complete_list);
      spin_lock_init(&wrap_urb_complete_list_lock);
      initialize_work(&wrap_urb_complete_work, wrap_urb_complete_worker, NULL);
#ifdef USB_DEBUG
      urb_id = 0;
#endif
      return 0;
}

void usb_exit(void)
{
      USBEXIT(return);
}

int usb_init_device(struct wrap_device *wd)
{
      InitializeListHead(&wd->usb.wrap_urb_list);
      wd->usb.num_alloc_urbs = 0;
      USBEXIT(return 0);
}

void usb_exit_device(struct wrap_device *wd)
{
      kill_all_urbs(wd, 0);
      USBEXIT(return);
}

Generated by  Doxygen 1.6.0   Back to index