📄 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"#ifdef USB_DEBUGstatic 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#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]#endif#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 0x01static 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; }}#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)#define URB_STATUS(wrap_urb) (wrap_urb->urb_status)static void *usb_buffer_alloc(struct usb_device *udev, size_t size, unsigned mem_flags, dma_addr_t *dma){ *dma = 0; return kmalloc(size, mem_flags);}static void usb_buffer_free(struct usb_device *udev, size_t size, void *addr, dma_addr_t dma){ kfree(addr);}static void usb_init_urb(struct urb *urb){ memset(urb, 0, sizeof(*urb));}static void usb_kill_urb(struct urb *urb){ usb_unlink_urb(urb);}#else#define URB_STATUS(wrap_urb) (wrap_urb->urb->status)#endifstatic struct nt_list wrap_urb_complete_list;static NT_SPIN_LOCK wrap_urb_complete_list_lock;/* use tasklet instead of worker to process completed urbs */#define USB_TASKLET 1#ifdef USB_TASKLETstatic struct tasklet_struct wrap_urb_complete_work;static void wrap_urb_complete_worker(unsigned long dummy);#elsestatic work_struct_t wrap_urb_complete_work;static void wrap_urb_complete_worker(worker_param_t dummy);#endifstatic void kill_all_urbs(struct wrap_device *wd, int complete){ struct nt_list *ent; struct wrap_urb *wrap_urb; KIRQL irql; USBENTER("%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_BTSTUFF; 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_NOT_CONNECTED; 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; unsigned int alloc_flags; struct wrap_urb *wrap_urb; struct wrap_device *wd; USBENTER("irp: %p", irp); wd = IRP_WRAP_DEVICE(irp); alloc_flags = gfp_irql(); 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 = kmalloc(sizeof(*wrap_urb), alloc_flags); if (!wrap_urb) { WARNING("couldn't allocate memory"); return NULL; }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) urb = usb_alloc_urb(0, alloc_flags);#else urb = usb_alloc_urb(0);#endif if (!urb) { WARNING("couldn't allocate urb"); kfree(wrap_urb); return NULL; } memset(wrap_urb, 0, sizeof(*wrap_urb)); 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("allocated 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 buffer for urb %p is %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;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ret = usb_submit_urb(urb, gfp_irql());#else ret = usb_submit_urb(urb);#endif 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);}#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)static void int_urb_unlink_complete(struct urb *urb){ struct wrap_urb *wrap_urb; wrap_urb = urb->context; USBTRACE("%p, %d, %d", urb, urb->status, wrap_urb->state); if (xchg(&wrap_urb->state, URB_INT_UNLINKED) != URB_COMPLETED) return; urb->status = URB_STATUS(wrap_urb); nt_spin_lock(&wrap_urb_complete_list_lock); InsertTailList(&wrap_urb_complete_list, &wrap_urb->complete_list); nt_spin_unlock(&wrap_urb_complete_list_lock);#ifdef USB_TASKLET tasklet_schedule(&wrap_urb_complete_work);#else schedule_ntos_work(&wrap_urb_complete_work);#endif}static void wrap_urb_complete(struct urb *urb)#elsestatic void wrap_urb_complete(struct urb *urb ISR_PT_REGS_PARAM_DECL)#endif{ 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;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) URB_STATUS(wrap_urb) = urb->status; /* prevent 2.4 kernels from resubmitting interrupt urbs; * completion function for unlink will process urb */ if (usb_pipeint(urb->pipe)) { USBTRACE("%p, %d", urb, urb->status); urb->complete = int_urb_unlink_complete; usb_unlink_urb(urb); return; }#endif nt_spin_lock(&wrap_urb_complete_list_lock); InsertTailList(&wrap_urb_complete_list, &wrap_urb->complete_list); nt_spin_unlock(&wrap_urb_complete_list_lock);#ifdef USB_TASKLET tasklet_schedule(&wrap_urb_complete_work);#else schedule_ntos_work(&wrap_urb_complete_work);#endif USBTRACE("scheduled worker for urb %p", urb);}/* one worker for all devices */#ifdef USB_TASKLETstatic void wrap_urb_complete_worker(unsigned long dummy)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -