/* * 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_free_coherent(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 canceled: %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_alloc_coherent(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); }