📄 usb.c
字号:
/* * Copyright (C) 2004 Jan Kiszka * * 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"#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)#define WRAP_ALLOC_URB(a, b) usb_alloc_urb(a)#define WRAP_SUBMIT_URB(a, b) usb_submit_urb(a)#else#define WRAP_ALLOC_URB(a, b) usb_alloc_urb(a, b)#define WRAP_SUBMIT_URB(a, b) usb_submit_urb(a, b)#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,5)#define CUR_ALT_SETTING(intf) (intf)->cur_altsetting#else#define CUR_ALT_SETTING(intf) (intf)->altsetting[(intf)->act_altsetting]#endifstatic struct list_head completed_irps;static KIRQL completed_irps_lock;void usb_transfer_complete_tasklet(unsigned long dummy);DECLARE_TASKLET(completed_irps_tasklet, usb_transfer_complete_tasklet, 0);static struct list_head canceled_irps;void usb_cancel_worker(void *dummy);static struct work_struct cancel_usb_irp_work;extern KSPIN_LOCK irp_cancel_lock;#ifdef DUMPURBS#define DUMP_URB(urb) do { \ int i; \ char dbg[40], *t; \ if ((urb)->pipe & USB_DIR_IN) \ USBTRACE("URB coming back"); \ else \ USBTRACE("URB going down"); \ printk(KERN_DEBUG "length: %x", \ urb->transfer_buffer_length); \ t = dbg; \ for (i = 0; i < urb->transfer_buffer_length && \ t < &dbg[sizeof(dbg) - 2]; i++) \ t += sprintf(t, "%02X ", \ *(((UCHAR *)urb->transfer_buffer)+i)); \ dbg[sizeof(dbg)-1] = 0; \ printk(KERN_DEBUG "%s\n", dbg); \ } while (0)#else#define DUMP_URB(urb)#endif /* DUMPURBS */static inline int wrap_submit_urb(struct urb *urb, int flags){ int ret; struct irp *irp = urb->context; ret = WRAP_SUBMIT_URB(urb, flags); if (ret) { ERROR("usb_submit_urb() = %d", ret); usb_free_urb(urb); if (IRP_DRIVER_CONTEXT(irp)[2]) kfree(IRP_DRIVER_CONTEXT(irp)[2]); } return ret;}int usb_init(void){ kspin_lock_init(&completed_irps_lock); INIT_LIST_HEAD(&canceled_irps); INIT_LIST_HEAD(&completed_irps); INIT_WORK(&cancel_usb_irp_work, usb_cancel_worker, NULL); return 0;}void usb_exit(void){ return;}#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)void usb_transfer_complete(struct urb *urb, struct pt_regs *regs)#elsevoid usb_transfer_complete(struct urb *urb)#endif{ struct irp *irp = urb->context; int cancel; USBTRACEENTER("urb = %p", urb); /* canceled via usb_unlink_urb? */ if ((urb->status == -ENOENT) || (urb->status == -ECONNRESET)) USBTRACEEXIT(return); /* canceled but not yet unlinked? */ kspin_lock(&irp_cancel_lock); irp->cancel_routine = NULL; cancel = irp->cancel; kspin_unlock(&irp_cancel_lock); if (cancel) USBTRACEEXIT(return); kspin_lock(&completed_irps_lock); list_add_tail(&irp->completed_list, &completed_irps); kspin_unlock(&completed_irps_lock); tasklet_schedule(&completed_irps_tasklet); USBTRACEEXIT(return);}void usb_transfer_complete_tasklet(unsigned long dummy){ struct irp *irp; struct urb *urb; struct io_stack_location *stack; union nt_urb *nt_urb; unsigned long flags; while (1) { kspin_lock_irqsave(&completed_irps_lock, flags); if (list_empty(&completed_irps)) { kspin_unlock_irqrestore(&completed_irps_lock, flags); USBTRACEEXIT(return); } irp = list_entry(completed_irps.next, struct irp, completed_list); list_del(&irp->completed_list); kspin_unlock_irqrestore(&completed_irps_lock, flags); urb = IRP_DRIVER_CONTEXT(irp)[3]; stack = IRP_CUR_STACK_LOC(irp) - 1; nt_urb = stack->params.generic.arg1; USBTRACE("irp = %p, urb = %p, status = %d", irp, urb, urb->status); if (urb->setup_packet) kfree(urb->setup_packet); irp->pending_returned = 1; if (urb->status) irp->io_status.status = STATUS_FAILURE; else irp->io_status.status = STATUS_SUCCESS; irp->io_status.status_info = urb->actual_length; /* also applies to ctrlDescReq or venClsReq */ nt_urb->bulkIntrTrans.transferBufLen = urb->actual_length; DUMP_URB(urb); if (IRP_DRIVER_CONTEXT(irp)[2]) { if (urb->pipe & USB_DIR_IN) { /* also applies to ctrlDescReq or venClsReq */ memcpy(nt_urb->bulkIntrTrans.transferBuf, IRP_DRIVER_CONTEXT(irp)[2], nt_urb->bulkIntrTrans.transferBufLen); } kfree(IRP_DRIVER_CONTEXT(irp)[2]); } IofCompleteRequest(FASTCALL_ARGS_2(irp, 0)); USBTRACE("freeing urb %p", urb); usb_free_urb(urb); }}/* this is called holding irp_cancel_lock */STDCALL void usb_cancel_transfer(struct device_object *dev_obj, struct irp *irp){ struct urb *urb; USBTRACEENTER("irp = %p", irp); urb = IRP_DRIVER_CONTEXT(irp)[3]; USBTRACE("adding urb %p to cancel", urb); /* while this function can run at DISPATCH_LEVEL, * usb_unlink/kill_urb will only work successfully in * schedulable context */ list_add_tail(&irp->cancel_list, &canceled_irps); schedule_work(&cancel_usb_irp_work);}void usb_cancel_worker(void *dummy){ struct irp *irp; struct urb *urb; USBTRACEENTER("%s", ""); while (1) { kspin_lock(&irp_cancel_lock); if (list_empty(&canceled_irps)) { kspin_unlock(&irp_cancel_lock); USBTRACEEXIT(return); } irp = list_entry(canceled_irps.next, struct irp, cancel_list); list_del(&irp->cancel_list); kspin_unlock(&irp_cancel_lock); urb = IRP_DRIVER_CONTEXT(irp)[3]; USBTRACE("freeing urb = %p", urb);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8) usb_kill_urb(urb);#else if (usb_unlink_urb(urb) < 0) USBTRACEEXIT(return);#endif if (urb->setup_packet) kfree(urb->setup_packet); usb_free_urb(urb); if (IRP_DRIVER_CONTEXT(irp)[2]) kfree(IRP_DRIVER_CONTEXT(irp)[2]); irp->io_status.status = STATUS_CANCELLED; irp->io_status.status_info = 0; IofCompleteRequest(FASTCALL_ARGS_2(irp, 0)); }}unsigned long usb_bulk_or_intr_trans(struct usb_device *dev, union nt_urb *nt_urb, struct irp *irp){ union pipe_handle pipe_handle; struct urb *urb; unsigned int pipe; int ret; UCHAR endpoint; ASSERT(!nt_urb->bulkIntrTrans.transferBufMdl); ASSERT(!nt_urb->bulkIntrTrans.urbLink); USBTRACE("flags = %lX, length = %lu, buffer = %p", nt_urb->bulkIntrTrans.transferFlags, nt_urb->bulkIntrTrans.transferBufLen, nt_urb->bulkIntrTrans.transferBuf); /* FIXME: we should better check what GFP_ is required */ urb = WRAP_ALLOC_URB(0, GFP_ATOMIC); if (!urb) return -ENOMEM; /* store the linux-urb in the nt-irp and set the cancel routine */ IRP_DRIVER_CONTEXT(irp)[3] = urb; irp->cancel_routine = usb_cancel_transfer; pipe_handle = nt_urb->bulkIntrTrans.pipeHandle; endpoint = pipe_handle.encoded.endpointAddr; switch(pipe_handle.encoded.pipeType) { case USB_ENDPOINT_XFER_CONTROL: if (nt_urb->bulkIntrTrans.transferFlags & USBD_TRANSFER_DIRECTION_IN) pipe = usb_rcvctrlpipe(dev, endpoint); else pipe = usb_sndctrlpipe(dev, endpoint); usb_fill_control_urb(urb, dev, pipe, urb->setup_packet, nt_urb->bulkIntrTrans.transferBuf, nt_urb->bulkIntrTrans.transferBufLen, usb_transfer_complete, irp); break; case USB_ENDPOINT_XFER_ISOC: if (nt_urb->bulkIntrTrans.transferFlags & USBD_TRANSFER_DIRECTION_IN) pipe = usb_rcvisocpipe(dev, endpoint); else pipe = usb_sndisocpipe(dev, endpoint); break; case USB_ENDPOINT_XFER_BULK: if (nt_urb->bulkIntrTrans.transferFlags & USBD_TRANSFER_DIRECTION_IN) pipe = usb_rcvbulkpipe(dev, endpoint); else pipe = usb_sndbulkpipe(dev, endpoint); usb_fill_bulk_urb(urb, dev, pipe, nt_urb->bulkIntrTrans.transferBuf, nt_urb->bulkIntrTrans.transferBufLen, usb_transfer_complete, irp); break; case USB_ENDPOINT_XFER_INT: if (nt_urb->bulkIntrTrans.transferFlags & USBD_TRANSFER_DIRECTION_IN) pipe = usb_rcvintpipe(dev, endpoint); else pipe = usb_sndintpipe(dev, endpoint); usb_fill_int_urb(urb, dev, pipe, nt_urb->bulkIntrTrans.transferBuf, nt_urb->bulkIntrTrans.transferBufLen, usb_transfer_complete, irp, pipe_handle.encoded.interval); break; default: ERROR("unknown pipe type: %d", pipe_handle.encoded.pipeType); return -EINVAL; } if ((nt_urb->venClsReq.transferFlags & (USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK)) == USBD_TRANSFER_DIRECTION_IN) urb->transfer_flags |= URB_SHORT_NOT_OK; DUMP_URB(urb); /* non-DMA-capable buffers have to be mirrored */ IRP_DRIVER_CONTEXT(irp)[2] = NULL; if (!virt_addr_valid(nt_urb->bulkIntrTrans.transferBuf)) { IRP_DRIVER_CONTEXT(irp)[2] = kmalloc(nt_urb->bulkIntrTrans.transferBufLen, GFP_ATOMIC); if (!IRP_DRIVER_CONTEXT(irp)[2]) { ERROR("%s", "kmalloc failed!"); usb_free_urb(urb); return -ENOMEM; } if (!(pipe & USB_DIR_IN)) memcpy(IRP_DRIVER_CONTEXT(irp)[2], nt_urb->bulkIntrTrans.transferBuf, nt_urb->bulkIntrTrans.transferBufLen); urb->transfer_buffer = IRP_DRIVER_CONTEXT(irp)[2]; USBTRACE("mirroring non-DMA buffer"); } /* mark setup_packet as unused for cleanup procedure */ urb->setup_packet = NULL; USBTRACE("submitting urb %p on pipe %p", urb, pipe_handle.handle); /* FIXME: we should better check what GFP_ is required */ ret = wrap_submit_urb(urb, GFP_ATOMIC); return ret;}unsigned long usb_vendor_or_class_intf(struct usb_device *dev, union nt_urb *nt_urb, struct irp *irp){ struct urb *urb; struct usb_ctrlrequest *dr; char req_type; unsigned int pipe; int ret; ASSERT(!nt_urb->venClsReq.transferBufMdl); ASSERT(!nt_urb->venClsReq.urbLink); USBTRACE("reservedBits = %x, request = %x, " "value = %d, index = %d, transferFlags = %lx, " "transferBuf = %p, transferBufLen = %ld", nt_urb->venClsReq.reservedBits, nt_urb->venClsReq.request, nt_urb->venClsReq.value, nt_urb->venClsReq.index, nt_urb->venClsReq.transferFlags, nt_urb->venClsReq.transferBuf, nt_urb->venClsReq.transferBufLen); /* FIXME: we should better check what GFP_ is required */ urb = WRAP_ALLOC_URB(0, GFP_ATOMIC); if (!urb) { ERROR("%s", "usb_alloc_urb failed!"); return -ENOMEM; } req_type = USB_TYPE_VENDOR | USB_RECIP_DEVICE | nt_urb->venClsReq.reservedBits; if (nt_urb->venClsReq.transferFlags & USBD_TRANSFER_DIRECTION_IN) { pipe = usb_rcvctrlpipe(dev, 0); req_type |= USB_DIR_IN; } else { pipe = usb_sndctrlpipe(dev, 0); req_type |= USB_DIR_OUT; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -