?? vhci_hcd.c
字號(hào):
/* * $Id: vhci_hcd.c 66 2008-04-20 13:19:42Z hirofuchi $ * * Copyright (C) 2003-2008 Takahiro Hirofuchi * * * This 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 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */#include "usbip_common.h"#include "vhci.h"#define DRIVER_VERSION " $Id: vhci_hcd.c 66 2008-04-20 13:19:42Z hirofuchi $ "#define DRIVER_AUTHOR "Takahiro Hirofuchi"#define DRIVER_DESC "Virtual Host Controller Interface Driver for USB/IP"#define DRIVER_LICENCE "GPL"MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE(DRIVER_LICENCE);/* * TODO * - update root hub emulation * - move the emulation code to userland ? * porting to other operating systems * minimize kernel code * - add suspend/resume code * - clean up everything *//* See usb gadget dummy hcd */static int vhci_hub_status (struct usb_hcd *hcd, char *buff);static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buff, u16 wLength);static int vhci_urb_enqueue (struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct urb *urb, gfp_t mem_flags);static int vhci_urb_dequeue( struct usb_hcd *hcd, struct urb *urb);static int vhci_start(struct usb_hcd *vhci_hcd);static void vhci_stop(struct usb_hcd *hcd);static int vhci_get_frame_number(struct usb_hcd *hcd);static const char driver_name[] = "vhci_hcd";static const char driver_desc[] = "USB/IP Virtual Host Contoroller";struct vhci_hcd *the_controller = NULL;static const char *bit_desc[] = { "CONNECTION", /*0*/ "ENABLE", /*1*/ "SUSPEND", /*2*/ "OVER_CURRENT", /*3*/ "RESET", /*4*/ "R5", /*5*/ "R6", /*6*/ "R7", /*7*/ "POWER", /*8*/ "LOWSPEED", /*9*/ "HIGHSPEED", /*10*/ "PORT_TEST", /*11*/ "INDICATOR", /*12*/ "R13", /*13*/ "R14", /*14*/ "R15", /*15*/ "C_CONNECTION", /*16*/ "C_ENABLE", /*17*/ "C_SUSPEND", /*18*/ "C_OVER_CURRENT", /*19*/ "C_RESET", /*20*/ "R21", /*21*/ "R22", /*22*/ "R23", /*23*/ "R24", /*24*/ "R25", /*25*/ "R26", /*26*/ "R27", /*27*/ "R28", /*28*/ "R29", /*29*/ "R30", /*30*/ "R31", /*31*/};static void dump_port_status(u32 status){ int i = 0; printk("status %08x:", status); for (i = 0; i < 32; i++) { if (status & (1 << i)) { printk(" %s", bit_desc[i]); } } printk("\n");}void rh_port_connect(int rhport, enum usb_device_speed speed){ unsigned long flags; dbg_vhci_rh("rh_port_connect %d\n", rhport); spin_lock_irqsave(&the_controller->lock, flags); the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION); switch (speed) { case USB_SPEED_HIGH: the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; break; case USB_SPEED_LOW: the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; break; default: break; } //spin_lock(&the_controller->vdev[rhport].ud.lock); //the_controller->vdev[rhport].ud.status = VDEV_CONNECT; //spin_unlock(&the_controller->vdev[rhport].ud.lock); the_controller->pending_port = rhport; spin_unlock_irqrestore(&the_controller->lock, flags);}void rh_port_disconnect(int rhport){ unsigned long flags; dbg_vhci_rh("rh_port_disconnect %d\n", rhport); spin_lock_irqsave (&the_controller->lock, flags); //stop_activity (dum, driver); the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; the_controller->port_status[rhport] |= (1 << USB_PORT_FEAT_C_CONNECTION); /* not yet complete the disconnection */ //spin_lock(&vdev->ud.lock); //vdev->ud.status = VHC_ST_DISCONNECT; //spin_unlock(&vdev->ud.lock); spin_unlock_irqrestore (&the_controller->lock, flags);}/*----------------------------------------------------------------------*/#define PORT_C_MASK \ ((USB_PORT_STAT_C_CONNECTION \ | USB_PORT_STAT_C_ENABLE \ | USB_PORT_STAT_C_SUSPEND \ | USB_PORT_STAT_C_OVERCURRENT \ | USB_PORT_STAT_C_RESET) << 16)/* * This function is almostly the same as dummy_hcd.c:dummy_hub_status() without * suspend/resume support. But, it is modified to provide multiple ports. * * @buf: a bitmap to show which port status has been changed. * bit 0: reserved or used for another purpose? * bit 1: the status of port 0 has been changed. * bit 2: the status of port 1 has been changed. * ... * bit 7: the status of port 6 has been changed. * bit 8: the status of port 7 has been changed. * ... * bit 15: the status of port 14 has been changed. * * So, the maximum number of ports is 31 ( port 0 to port 30) ? * * The return value is the actual transfered length in byte. If nothing has * been changed, return 0. In the case that the number of ports is less than or * equal to 6 (VHCI_NPORTS==7), return 1. * */static int vhci_hub_status (struct usb_hcd *hcd, char *buf){ struct vhci_hcd *vhci; unsigned long flags; int retval = 0; /* the enough buffer is allocated according to USB_MAXCHILDREN */ unsigned long *event_bits = (unsigned long *) buf; int rhport; int changed = 0; *event_bits = 0; vhci = hcd_to_vhci(hcd); spin_lock_irqsave (&vhci->lock, flags); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { dbg_vhci_rh("hw accessible flag in on?\n"); goto done; } /* check pseudo status register for each port */ for(rhport = 0; rhport < VHCI_NPORTS; rhport++) { if ((vhci->port_status[rhport] & PORT_C_MASK)) { /* The status of a port has been changed, */ dbg_vhci_rh("port %d is changed\n", rhport); *event_bits |= 1 << ( rhport + 1); changed = 1; } } if (changed) retval = 1 + (VHCI_NPORTS / 8); else retval = 0;done: spin_unlock_irqrestore (&vhci->lock, flags); return retval;}/* See hub_configure in hub.c */static inline void hub_descriptor(struct usb_hub_descriptor *desc){ memset(desc, 0, sizeof(*desc)); desc->bDescriptorType = 0x29; desc->bDescLength = 9; desc->wHubCharacteristics = (__force __u16) (__constant_cpu_to_le16 (0x0001)); desc->bNbrPorts = VHCI_NPORTS; desc->bitmap [0] = 0xff; desc->bitmap [1] = 0xff;}static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength){ struct vhci_hcd *dum; int retval = 0; unsigned long flags; int rhport; u32 prev_port_status[VHCI_NPORTS]; if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) return -ETIMEDOUT; /* * NOTE: * wIndex shows the port number and begins from 1. */ dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, wIndex); if (wIndex > VHCI_NPORTS) uerr("invalid port number %d\n", wIndex); rhport = ((__u8 ) (wIndex & 0x00ff)) -1; dum = hcd_to_vhci(hcd); spin_lock_irqsave (&dum->lock, flags); /* store old status and compare now and old later */ if (dbg_flag_vhci_rh) { int i = 0; for (i = 0; i < VHCI_NPORTS; i++) prev_port_status[i] = dum->port_status[i]; } switch (typeReq) { case ClearHubFeature: dbg_vhci_rh(" ClearHubFeature\n"); break; case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (dum->port_status[rhport] & (1 << USB_PORT_FEAT_SUSPEND)) { /* 20msec signaling */ dum->resuming = 1; dum->re_timeout = jiffies + msecs_to_jiffies(20); } break; case USB_PORT_FEAT_POWER: dbg_vhci_rh(" ClearPortFeature: USB_PORT_FEAT_POWER\n"); dum->port_status[rhport] = 0; //dum->address = 0; //dum->hdev = 0; dum->resuming = 0; break; case USB_PORT_FEAT_C_RESET: dbg_vhci_rh(" ClearPortFeature: USB_PORT_FEAT_C_RESET\n"); switch (dum->vdev[rhport].speed) { case USB_SPEED_HIGH: dum->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; break; case USB_SPEED_LOW: dum->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; break; default: break; } default: dbg_vhci_rh(" ClearPortFeature: default %x\n", wValue); dum->port_status[rhport] &= ~(1 << wValue); } break; case GetHubDescriptor: dbg_vhci_rh(" GetHubDescriptor\n"); hub_descriptor ((struct usb_hub_descriptor *) buf); break; case GetHubStatus: dbg_vhci_rh(" GetHubStatus\n"); *(u32 *) buf = __constant_cpu_to_le32 (0); break; case GetPortStatus: dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); if (wIndex > VHCI_NPORTS || wIndex < 1) { uerr(" invalid port number %d\n", wIndex); retval = -EPIPE; } /* we do no care of resume. */ /* whoever resets or resumes must GetPortStatus to * complete it!! * */ if (dum->resuming && time_after (jiffies, dum->re_timeout)) { uerr(" not yet\n"); dum->port_status[rhport] |= (1 << USB_PORT_FEAT_C_SUSPEND); dum->port_status[rhport] &= ~(1 << USB_PORT_FEAT_SUSPEND); dum->resuming = 0; dum->re_timeout = 0; //if (dum->driver && dum->driver->resume) { // spin_unlock (&dum->lock); // dum->driver->resume (&dum->gadget); // spin_lock (&dum->lock); //} } if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) != 0 && time_after (jiffies, dum->re_timeout)) { dum->port_status[rhport] |= (1 << USB_PORT_FEAT_C_RESET); dum->port_status[rhport] &= ~(1 << USB_PORT_FEAT_RESET); dum->re_timeout = 0; if (dum->vdev[rhport].ud.status == VDEV_ST_NOTASSIGNED) { dbg_vhci_rh(" enable rhport %d (status %u)\n", rhport, dum->vdev[rhport].ud.status); dum->port_status[rhport] |= USB_PORT_STAT_ENABLE; }#if 0 if (dum->driver) { dum->port_status[rhport] |= USB_PORT_STAT_ENABLE; /* give it the best speed we agree on */ dum->gadget.speed = dum->driver->speed; dum->gadget.ep0->maxpacket = 64; switch (dum->gadget.speed) { case USB_SPEED_HIGH: dum->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; break; case USB_SPEED_LOW: dum->gadget.ep0->maxpacket = 8; dum->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; break; default: dum->gadget.speed = USB_SPEED_FULL; break; } }#endif } ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status[rhport]); ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status[rhport] >> 16); dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0], ((u16 *)buf)[1] ); break; case SetHubFeature: dbg_vhci_rh(" SetHubFeature\n"); retval = -EPIPE; break; case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: dbg_vhci_rh(" SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); uerr(" not yet\n");#if 0 dum->port_status[rhport] |= (1 << USB_PORT_FEAT_SUSPEND); if (dum->driver->suspend) { spin_unlock (&dum->lock); dum->driver->suspend (&dum->gadget); spin_lock (&dum->lock); }#endif break; case USB_PORT_FEAT_RESET: dbg_vhci_rh(" SetPortFeature: USB_PORT_FEAT_RESET\n"); /* if it's already running, disconnect first */ if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) { dum->port_status[rhport] &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED);#if 0 if (dum->driver) { dev_dbg (hardware, "disconnect\n"); stop_activity (dum, dum->driver); }#endif /* FIXME test that code path! */ } /* 50msec reset signaling */ dum->re_timeout = jiffies + msecs_to_jiffies(50); /* FALLTHROUGH */ default: dbg_vhci_rh(" SetPortFeature: default %d\n", wValue); dum->port_status[rhport] |= (1 << wValue); } break; default: uerr(" default: no such request\n"); //dev_dbg (hardware, // "hub control req%04x v%04x i%04x l%d\n", // typeReq, wValue, wIndex, wLength); /* "protocol stall" on error */ retval = -EPIPE; } if (dbg_flag_vhci_rh) { printk("port %d\n", rhport); dump_port_status(prev_port_status[rhport]); dump_port_status(dum->port_status[rhport]); } dbg_vhci_rh(" bye\n"); spin_unlock_irqrestore (&dum->lock, flags); return retval;}/*----------------------------------------------------------------------*/static struct vhci_device *get_vdev(struct usb_device *udev){ int i; if (!udev) return NULL; for (i=0; i < VHCI_NPORTS; i++) if (the_controller->vdev[i].udev == udev) return port_to_vdev(i); return NULL;}static void vhci_tx_urb(struct urb *urb){ struct vhci_device *vdev = get_vdev(urb->dev); struct vhci_priv *priv; unsigned long flag; if (!vdev) { err("could not get virtual device"); //BUG(); return; } spin_lock_irqsave(&vdev->priv_lock, flag); priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); if (!priv) { uerr("malloc vhci_priv\n"); spin_unlock_irqrestore(&vdev->priv_lock, flag); usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); return; } priv->seqnum = atomic_inc_return(&the_controller->seqnum); if (priv->seqnum == 0xffff) uinfo("seqnum max\n"); priv->vdev = vdev; priv->urb = urb; urb->hcpriv = (void *) priv; list_add_tail(&priv->list, &vdev->priv_tx); wake_up(&vdev->waitq_tx); spin_unlock_irqrestore(&vdev->priv_lock, flag);}static int vhci_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct urb *urb, gfp_t mem_flags){ int ret = 0; unsigned long flags; dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", hcd, urb, mem_flags); /* patch to usb_sg_init() is in 2.5.60 */ BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length); spin_lock_irqsave (&the_controller->lock, flags); /* check HC is active or not */ if (!HC_IS_RUNNING(hcd->state)) { uerr("HC is not running\n"); spin_unlock_irqrestore(&the_controller->lock, flags); return -ENODEV; } if (urb->status != -EINPROGRESS) { uerr("URB already unlinked!, status %d\n", urb->status); spin_unlock_irqrestore(&the_controller->lock, flags); return urb->status; } /* * The enumelation process is as follows; * * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0) * to get max packet length of default pipe * * 2. Set_Address request to DevAddr(0) EndPoint(0) * */ if (usb_pipedevice(urb->pipe) == 0) { __u8 type = usb_pipetype(urb->pipe); struct usb_ctrlrequest *ctrlreq = (struct usb_ctrlrequest *) urb->setup_packet; struct vhci_device *vdev = port_to_vdev(the_controller->pending_port); if (type != PIPE_CONTROL || !ctrlreq ) { uerr("invalid request to devnum 0\n"); ret = EINVAL; goto no_need_xmit; } switch (ctrlreq->bRequest) { case USB_REQ_SET_ADDRESS: /* set_address may come when a device is reset */ uinfo("SetAddress Request (%d) to port %d\n", ctrlreq->wValue, vdev->rhport); vdev->udev = urb->dev; spin_lock(&vdev->ud.lock); vdev->ud.status = VDEV_ST_USED; spin_unlock(&vdev->ud.lock); spin_lock(&urb->lock); if (urb->status == -EINPROGRESS) { /* This request is successfully completed. */ /* If not -EINPROGRESS, possibly unlinked. */ urb->status = 0; } spin_unlock(&urb->lock); goto no_need_xmit; case USB_REQ_GET_DESCRIPTOR: if (ctrlreq->wValue == (USB_DT_DEVICE << 8)) dbg_vhci_hc("Not yet?: Get_Descriptor to device 0 (get max pipe size)\n");
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -