12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * xen-hcd.c
- *
- * Xen USB Virtual Host Controller driver
- *
- * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
- * Author: Noboru Iwamatsu <[email protected]>
- */
- #include <linux/module.h>
- #include <linux/usb.h>
- #include <linux/list.h>
- #include <linux/usb/hcd.h>
- #include <linux/io.h>
- #include <xen/xen.h>
- #include <xen/xenbus.h>
- #include <xen/grant_table.h>
- #include <xen/events.h>
- #include <xen/page.h>
- #include <xen/interface/io/usbif.h>
- /* Private per-URB data */
- struct urb_priv {
- struct list_head list;
- struct urb *urb;
- int req_id; /* RING_REQUEST id for submitting */
- int unlink_req_id; /* RING_REQUEST id for unlinking */
- int status;
- bool unlinked; /* dequeued marker */
- };
- /* virtual roothub port status */
- struct rhport_status {
- __u32 status;
- bool resuming; /* in resuming */
- bool c_connection; /* connection changed */
- unsigned long timeout;
- };
- /* status of attached device */
- struct vdevice_status {
- int devnum;
- enum usb_device_state status;
- enum usb_device_speed speed;
- };
- /* RING request shadow */
- struct usb_shadow {
- struct xenusb_urb_request req;
- struct urb *urb;
- bool in_flight;
- };
- struct xenhcd_info {
- /* Virtual Host Controller has 4 urb queues */
- struct list_head pending_submit_list;
- struct list_head pending_unlink_list;
- struct list_head in_progress_list;
- struct list_head giveback_waiting_list;
- spinlock_t lock;
- /* timer that kick pending and giveback waiting urbs */
- struct timer_list watchdog;
- unsigned long actions;
- /* virtual root hub */
- int rh_numports;
- struct rhport_status ports[XENUSB_MAX_PORTNR];
- struct vdevice_status devices[XENUSB_MAX_PORTNR];
- /* Xen related staff */
- struct xenbus_device *xbdev;
- int urb_ring_ref;
- int conn_ring_ref;
- struct xenusb_urb_front_ring urb_ring;
- struct xenusb_conn_front_ring conn_ring;
- unsigned int evtchn;
- unsigned int irq;
- struct usb_shadow shadow[XENUSB_URB_RING_SIZE];
- unsigned int shadow_free;
- bool error;
- };
- #define XENHCD_RING_JIFFIES (HZ/200)
- #define XENHCD_SCAN_JIFFIES 1
- enum xenhcd_timer_action {
- TIMER_RING_WATCHDOG,
- TIMER_SCAN_PENDING_URBS,
- };
- static struct kmem_cache *xenhcd_urbp_cachep;
- static inline struct xenhcd_info *xenhcd_hcd_to_info(struct usb_hcd *hcd)
- {
- return (struct xenhcd_info *)hcd->hcd_priv;
- }
- static inline struct usb_hcd *xenhcd_info_to_hcd(struct xenhcd_info *info)
- {
- return container_of((void *)info, struct usb_hcd, hcd_priv);
- }
- static void xenhcd_set_error(struct xenhcd_info *info, const char *msg)
- {
- info->error = true;
- pr_alert("xen-hcd: protocol error: %s!\n", msg);
- }
- static inline void xenhcd_timer_action_done(struct xenhcd_info *info,
- enum xenhcd_timer_action action)
- {
- clear_bit(action, &info->actions);
- }
- static void xenhcd_timer_action(struct xenhcd_info *info,
- enum xenhcd_timer_action action)
- {
- if (timer_pending(&info->watchdog) &&
- test_bit(TIMER_SCAN_PENDING_URBS, &info->actions))
- return;
- if (!test_and_set_bit(action, &info->actions)) {
- unsigned long t;
- switch (action) {
- case TIMER_RING_WATCHDOG:
- t = XENHCD_RING_JIFFIES;
- break;
- default:
- t = XENHCD_SCAN_JIFFIES;
- break;
- }
- mod_timer(&info->watchdog, t + jiffies);
- }
- }
- /*
- * set virtual port connection status
- */
- static void xenhcd_set_connect_state(struct xenhcd_info *info, int portnum)
- {
- int port;
- port = portnum - 1;
- if (info->ports[port].status & USB_PORT_STAT_POWER) {
- switch (info->devices[port].speed) {
- case XENUSB_SPEED_NONE:
- info->ports[port].status &=
- ~(USB_PORT_STAT_CONNECTION |
- USB_PORT_STAT_ENABLE |
- USB_PORT_STAT_LOW_SPEED |
- USB_PORT_STAT_HIGH_SPEED |
- USB_PORT_STAT_SUSPEND);
- break;
- case XENUSB_SPEED_LOW:
- info->ports[port].status |= USB_PORT_STAT_CONNECTION;
- info->ports[port].status |= USB_PORT_STAT_LOW_SPEED;
- break;
- case XENUSB_SPEED_FULL:
- info->ports[port].status |= USB_PORT_STAT_CONNECTION;
- break;
- case XENUSB_SPEED_HIGH:
- info->ports[port].status |= USB_PORT_STAT_CONNECTION;
- info->ports[port].status |= USB_PORT_STAT_HIGH_SPEED;
- break;
- default: /* error */
- return;
- }
- info->ports[port].status |= (USB_PORT_STAT_C_CONNECTION << 16);
- }
- }
- /*
- * set virtual device connection status
- */
- static int xenhcd_rhport_connect(struct xenhcd_info *info, __u8 portnum,
- __u8 speed)
- {
- int port;
- if (portnum < 1 || portnum > info->rh_numports)
- return -EINVAL; /* invalid port number */
- port = portnum - 1;
- if (info->devices[port].speed != speed) {
- switch (speed) {
- case XENUSB_SPEED_NONE: /* disconnect */
- info->devices[port].status = USB_STATE_NOTATTACHED;
- break;
- case XENUSB_SPEED_LOW:
- case XENUSB_SPEED_FULL:
- case XENUSB_SPEED_HIGH:
- info->devices[port].status = USB_STATE_ATTACHED;
- break;
- default: /* error */
- return -EINVAL;
- }
- info->devices[port].speed = speed;
- info->ports[port].c_connection = true;
- xenhcd_set_connect_state(info, portnum);
- }
- return 0;
- }
- /*
- * SetPortFeature(PORT_SUSPENDED)
- */
- static void xenhcd_rhport_suspend(struct xenhcd_info *info, int portnum)
- {
- int port;
- port = portnum - 1;
- info->ports[port].status |= USB_PORT_STAT_SUSPEND;
- info->devices[port].status = USB_STATE_SUSPENDED;
- }
- /*
- * ClearPortFeature(PORT_SUSPENDED)
- */
- static void xenhcd_rhport_resume(struct xenhcd_info *info, int portnum)
- {
- int port;
- port = portnum - 1;
- if (info->ports[port].status & USB_PORT_STAT_SUSPEND) {
- info->ports[port].resuming = true;
- info->ports[port].timeout = jiffies + msecs_to_jiffies(20);
- }
- }
- /*
- * SetPortFeature(PORT_POWER)
- */
- static void xenhcd_rhport_power_on(struct xenhcd_info *info, int portnum)
- {
- int port;
- port = portnum - 1;
- if ((info->ports[port].status & USB_PORT_STAT_POWER) == 0) {
- info->ports[port].status |= USB_PORT_STAT_POWER;
- if (info->devices[port].status != USB_STATE_NOTATTACHED)
- info->devices[port].status = USB_STATE_POWERED;
- if (info->ports[port].c_connection)
- xenhcd_set_connect_state(info, portnum);
- }
- }
- /*
- * ClearPortFeature(PORT_POWER)
- * SetConfiguration(non-zero)
- * Power_Source_Off
- * Over-current
- */
- static void xenhcd_rhport_power_off(struct xenhcd_info *info, int portnum)
- {
- int port;
- port = portnum - 1;
- if (info->ports[port].status & USB_PORT_STAT_POWER) {
- info->ports[port].status = 0;
- if (info->devices[port].status != USB_STATE_NOTATTACHED)
- info->devices[port].status = USB_STATE_ATTACHED;
- }
- }
- /*
- * ClearPortFeature(PORT_ENABLE)
- */
- static void xenhcd_rhport_disable(struct xenhcd_info *info, int portnum)
- {
- int port;
- port = portnum - 1;
- info->ports[port].status &= ~USB_PORT_STAT_ENABLE;
- info->ports[port].status &= ~USB_PORT_STAT_SUSPEND;
- info->ports[port].resuming = false;
- if (info->devices[port].status != USB_STATE_NOTATTACHED)
- info->devices[port].status = USB_STATE_POWERED;
- }
- /*
- * SetPortFeature(PORT_RESET)
- */
- static void xenhcd_rhport_reset(struct xenhcd_info *info, int portnum)
- {
- int port;
- port = portnum - 1;
- info->ports[port].status &= ~(USB_PORT_STAT_ENABLE |
- USB_PORT_STAT_LOW_SPEED |
- USB_PORT_STAT_HIGH_SPEED);
- info->ports[port].status |= USB_PORT_STAT_RESET;
- if (info->devices[port].status != USB_STATE_NOTATTACHED)
- info->devices[port].status = USB_STATE_ATTACHED;
- /* 10msec reset signaling */
- info->ports[port].timeout = jiffies + msecs_to_jiffies(10);
- }
- #ifdef CONFIG_PM
- static int xenhcd_bus_suspend(struct usb_hcd *hcd)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- int ret = 0;
- int i, ports;
- ports = info->rh_numports;
- spin_lock_irq(&info->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
- ret = -ESHUTDOWN;
- } else {
- /* suspend any active ports*/
- for (i = 1; i <= ports; i++)
- xenhcd_rhport_suspend(info, i);
- }
- spin_unlock_irq(&info->lock);
- del_timer_sync(&info->watchdog);
- return ret;
- }
- static int xenhcd_bus_resume(struct usb_hcd *hcd)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- int ret = 0;
- int i, ports;
- ports = info->rh_numports;
- spin_lock_irq(&info->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
- ret = -ESHUTDOWN;
- } else {
- /* resume any suspended ports*/
- for (i = 1; i <= ports; i++)
- xenhcd_rhport_resume(info, i);
- }
- spin_unlock_irq(&info->lock);
- return ret;
- }
- #endif
- static void xenhcd_hub_descriptor(struct xenhcd_info *info,
- struct usb_hub_descriptor *desc)
- {
- __u16 temp;
- int ports = info->rh_numports;
- desc->bDescriptorType = 0x29;
- desc->bPwrOn2PwrGood = 10; /* EHCI says 20ms max */
- desc->bHubContrCurrent = 0;
- desc->bNbrPorts = ports;
- /* size of DeviceRemovable and PortPwrCtrlMask fields */
- temp = 1 + (ports / 8);
- desc->bDescLength = 7 + 2 * temp;
- /* bitmaps for DeviceRemovable and PortPwrCtrlMask */
- memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
- memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
- /* per-port over current reporting and no power switching */
- temp = 0x000a;
- desc->wHubCharacteristics = cpu_to_le16(temp);
- }
- /* port status change mask for hub_status_data */
- #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)
- /*
- * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap.
- * If port status changed, writes the bitmap to buf and return
- * that length(number of bytes).
- * If Nothing changed, return 0.
- */
- static int xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- int ports;
- int i;
- unsigned long flags;
- int ret;
- int changed = 0;
- /* initialize the status to no-changes */
- ports = info->rh_numports;
- ret = 1 + (ports / 8);
- memset(buf, 0, ret);
- spin_lock_irqsave(&info->lock, flags);
- for (i = 0; i < ports; i++) {
- /* check status for each port */
- if (info->ports[i].status & PORT_C_MASK) {
- buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
- changed = 1;
- }
- }
- if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
- usb_hcd_resume_root_hub(hcd);
- spin_unlock_irqrestore(&info->lock, flags);
- return changed ? ret : 0;
- }
- static int xenhcd_hub_control(struct usb_hcd *hcd, __u16 typeReq, __u16 wValue,
- __u16 wIndex, char *buf, __u16 wLength)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- int ports = info->rh_numports;
- unsigned long flags;
- int ret = 0;
- int i;
- int changed = 0;
- spin_lock_irqsave(&info->lock, flags);
- switch (typeReq) {
- case ClearHubFeature:
- /* ignore this request */
- break;
- case ClearPortFeature:
- if (!wIndex || wIndex > ports)
- goto error;
- switch (wValue) {
- case USB_PORT_FEAT_SUSPEND:
- xenhcd_rhport_resume(info, wIndex);
- break;
- case USB_PORT_FEAT_POWER:
- xenhcd_rhport_power_off(info, wIndex);
- break;
- case USB_PORT_FEAT_ENABLE:
- xenhcd_rhport_disable(info, wIndex);
- break;
- case USB_PORT_FEAT_C_CONNECTION:
- info->ports[wIndex - 1].c_connection = false;
- fallthrough;
- default:
- info->ports[wIndex - 1].status &= ~(1 << wValue);
- break;
- }
- break;
- case GetHubDescriptor:
- xenhcd_hub_descriptor(info, (struct usb_hub_descriptor *)buf);
- break;
- case GetHubStatus:
- /* always local power supply good and no over-current exists. */
- *(__le32 *)buf = cpu_to_le32(0);
- break;
- case GetPortStatus:
- if (!wIndex || wIndex > ports)
- goto error;
- wIndex--;
- /* resume completion */
- if (info->ports[wIndex].resuming &&
- time_after_eq(jiffies, info->ports[wIndex].timeout)) {
- info->ports[wIndex].status |=
- USB_PORT_STAT_C_SUSPEND << 16;
- info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND;
- }
- /* reset completion */
- if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 &&
- time_after_eq(jiffies, info->ports[wIndex].timeout)) {
- info->ports[wIndex].status |=
- USB_PORT_STAT_C_RESET << 16;
- info->ports[wIndex].status &= ~USB_PORT_STAT_RESET;
- if (info->devices[wIndex].status !=
- USB_STATE_NOTATTACHED) {
- info->ports[wIndex].status |=
- USB_PORT_STAT_ENABLE;
- info->devices[wIndex].status =
- USB_STATE_DEFAULT;
- }
- switch (info->devices[wIndex].speed) {
- case XENUSB_SPEED_LOW:
- info->ports[wIndex].status |=
- USB_PORT_STAT_LOW_SPEED;
- break;
- case XENUSB_SPEED_HIGH:
- info->ports[wIndex].status |=
- USB_PORT_STAT_HIGH_SPEED;
- break;
- default:
- break;
- }
- }
- *(__le32 *)buf = cpu_to_le32(info->ports[wIndex].status);
- break;
- case SetPortFeature:
- if (!wIndex || wIndex > ports)
- goto error;
- switch (wValue) {
- case USB_PORT_FEAT_POWER:
- xenhcd_rhport_power_on(info, wIndex);
- break;
- case USB_PORT_FEAT_RESET:
- xenhcd_rhport_reset(info, wIndex);
- break;
- case USB_PORT_FEAT_SUSPEND:
- xenhcd_rhport_suspend(info, wIndex);
- break;
- default:
- if (info->ports[wIndex-1].status & USB_PORT_STAT_POWER)
- info->ports[wIndex-1].status |= (1 << wValue);
- }
- break;
- case SetHubFeature:
- /* not supported */
- default:
- error:
- ret = -EPIPE;
- }
- spin_unlock_irqrestore(&info->lock, flags);
- /* check status for each port */
- for (i = 0; i < ports; i++) {
- if (info->ports[i].status & PORT_C_MASK)
- changed = 1;
- }
- if (changed)
- usb_hcd_poll_rh_status(hcd);
- return ret;
- }
- static void xenhcd_free_urb_priv(struct urb_priv *urbp)
- {
- urbp->urb->hcpriv = NULL;
- kmem_cache_free(xenhcd_urbp_cachep, urbp);
- }
- static inline unsigned int xenhcd_get_id_from_freelist(struct xenhcd_info *info)
- {
- unsigned int free;
- free = info->shadow_free;
- info->shadow_free = info->shadow[free].req.id;
- info->shadow[free].req.id = 0x0fff; /* debug */
- return free;
- }
- static inline void xenhcd_add_id_to_freelist(struct xenhcd_info *info,
- unsigned int id)
- {
- info->shadow[id].req.id = info->shadow_free;
- info->shadow[id].urb = NULL;
- info->shadow_free = id;
- }
- static inline int xenhcd_count_pages(void *addr, int length)
- {
- unsigned long vaddr = (unsigned long)addr;
- return PFN_UP(vaddr + length) - PFN_DOWN(vaddr);
- }
- static void xenhcd_gnttab_map(struct xenhcd_info *info, void *addr, int length,
- grant_ref_t *gref_head,
- struct xenusb_request_segment *seg,
- int nr_pages, int flags)
- {
- grant_ref_t ref;
- unsigned int offset;
- unsigned int len = length;
- unsigned int bytes;
- int i;
- for (i = 0; i < nr_pages; i++) {
- offset = offset_in_page(addr);
- bytes = PAGE_SIZE - offset;
- if (bytes > len)
- bytes = len;
- ref = gnttab_claim_grant_reference(gref_head);
- gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,
- virt_to_gfn(addr), flags);
- seg[i].gref = ref;
- seg[i].offset = (__u16)offset;
- seg[i].length = (__u16)bytes;
- addr += bytes;
- len -= bytes;
- }
- }
- static __u32 xenhcd_pipe_urb_to_xenusb(__u32 urb_pipe, __u8 port)
- {
- static __u32 pipe;
- pipe = usb_pipedevice(urb_pipe) << XENUSB_PIPE_DEV_SHIFT;
- pipe |= usb_pipeendpoint(urb_pipe) << XENUSB_PIPE_EP_SHIFT;
- if (usb_pipein(urb_pipe))
- pipe |= XENUSB_PIPE_DIR;
- switch (usb_pipetype(urb_pipe)) {
- case PIPE_ISOCHRONOUS:
- pipe |= XENUSB_PIPE_TYPE_ISOC << XENUSB_PIPE_TYPE_SHIFT;
- break;
- case PIPE_INTERRUPT:
- pipe |= XENUSB_PIPE_TYPE_INT << XENUSB_PIPE_TYPE_SHIFT;
- break;
- case PIPE_CONTROL:
- pipe |= XENUSB_PIPE_TYPE_CTRL << XENUSB_PIPE_TYPE_SHIFT;
- break;
- case PIPE_BULK:
- pipe |= XENUSB_PIPE_TYPE_BULK << XENUSB_PIPE_TYPE_SHIFT;
- break;
- }
- pipe = xenusb_setportnum_pipe(pipe, port);
- return pipe;
- }
- static int xenhcd_map_urb_for_request(struct xenhcd_info *info, struct urb *urb,
- struct xenusb_urb_request *req)
- {
- grant_ref_t gref_head;
- int nr_buff_pages = 0;
- int nr_isodesc_pages = 0;
- int nr_grants = 0;
- if (urb->transfer_buffer_length) {
- nr_buff_pages = xenhcd_count_pages(urb->transfer_buffer,
- urb->transfer_buffer_length);
- if (usb_pipeisoc(urb->pipe))
- nr_isodesc_pages = xenhcd_count_pages(
- &urb->iso_frame_desc[0],
- sizeof(struct usb_iso_packet_descriptor) *
- urb->number_of_packets);
- nr_grants = nr_buff_pages + nr_isodesc_pages;
- if (nr_grants > XENUSB_MAX_SEGMENTS_PER_REQUEST) {
- pr_err("xenhcd: error: %d grants\n", nr_grants);
- return -E2BIG;
- }
- if (gnttab_alloc_grant_references(nr_grants, &gref_head)) {
- pr_err("xenhcd: gnttab_alloc_grant_references() error\n");
- return -ENOMEM;
- }
- xenhcd_gnttab_map(info, urb->transfer_buffer,
- urb->transfer_buffer_length, &gref_head,
- &req->seg[0], nr_buff_pages,
- usb_pipein(urb->pipe) ? 0 : GTF_readonly);
- }
- req->pipe = xenhcd_pipe_urb_to_xenusb(urb->pipe, urb->dev->portnum);
- req->transfer_flags = 0;
- if (urb->transfer_flags & URB_SHORT_NOT_OK)
- req->transfer_flags |= XENUSB_SHORT_NOT_OK;
- req->buffer_length = urb->transfer_buffer_length;
- req->nr_buffer_segs = nr_buff_pages;
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_ISOCHRONOUS:
- req->u.isoc.interval = urb->interval;
- req->u.isoc.start_frame = urb->start_frame;
- req->u.isoc.number_of_packets = urb->number_of_packets;
- req->u.isoc.nr_frame_desc_segs = nr_isodesc_pages;
- xenhcd_gnttab_map(info, &urb->iso_frame_desc[0],
- sizeof(struct usb_iso_packet_descriptor) *
- urb->number_of_packets,
- &gref_head, &req->seg[nr_buff_pages],
- nr_isodesc_pages, 0);
- break;
- case PIPE_INTERRUPT:
- req->u.intr.interval = urb->interval;
- break;
- case PIPE_CONTROL:
- if (urb->setup_packet)
- memcpy(req->u.ctrl, urb->setup_packet, 8);
- break;
- case PIPE_BULK:
- break;
- default:
- break;
- }
- if (nr_grants)
- gnttab_free_grant_references(gref_head);
- return 0;
- }
- static void xenhcd_gnttab_done(struct xenhcd_info *info, unsigned int id)
- {
- struct usb_shadow *shadow = info->shadow + id;
- int nr_segs = 0;
- int i;
- if (!shadow->in_flight) {
- xenhcd_set_error(info, "Illegal request id");
- return;
- }
- shadow->in_flight = false;
- nr_segs = shadow->req.nr_buffer_segs;
- if (xenusb_pipeisoc(shadow->req.pipe))
- nr_segs += shadow->req.u.isoc.nr_frame_desc_segs;
- for (i = 0; i < nr_segs; i++) {
- if (!gnttab_try_end_foreign_access(shadow->req.seg[i].gref))
- xenhcd_set_error(info, "backend didn't release grant");
- }
- shadow->req.nr_buffer_segs = 0;
- shadow->req.u.isoc.nr_frame_desc_segs = 0;
- }
- static int xenhcd_translate_status(int status)
- {
- switch (status) {
- case XENUSB_STATUS_OK:
- return 0;
- case XENUSB_STATUS_NODEV:
- return -ENODEV;
- case XENUSB_STATUS_INVAL:
- return -EINVAL;
- case XENUSB_STATUS_STALL:
- return -EPIPE;
- case XENUSB_STATUS_IOERROR:
- return -EPROTO;
- case XENUSB_STATUS_BABBLE:
- return -EOVERFLOW;
- default:
- return -ESHUTDOWN;
- }
- }
- static void xenhcd_giveback_urb(struct xenhcd_info *info, struct urb *urb,
- int status)
- {
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int priv_status = urbp->status;
- list_del_init(&urbp->list);
- xenhcd_free_urb_priv(urbp);
- if (urb->status == -EINPROGRESS)
- urb->status = xenhcd_translate_status(status);
- spin_unlock(&info->lock);
- usb_hcd_giveback_urb(xenhcd_info_to_hcd(info), urb,
- priv_status <= 0 ? priv_status : urb->status);
- spin_lock(&info->lock);
- }
- static int xenhcd_do_request(struct xenhcd_info *info, struct urb_priv *urbp)
- {
- struct xenusb_urb_request *req;
- struct urb *urb = urbp->urb;
- unsigned int id;
- int notify;
- int ret;
- id = xenhcd_get_id_from_freelist(info);
- req = &info->shadow[id].req;
- req->id = id;
- if (unlikely(urbp->unlinked)) {
- req->u.unlink.unlink_id = urbp->req_id;
- req->pipe = xenusb_setunlink_pipe(xenhcd_pipe_urb_to_xenusb(
- urb->pipe, urb->dev->portnum));
- urbp->unlink_req_id = id;
- } else {
- ret = xenhcd_map_urb_for_request(info, urb, req);
- if (ret) {
- xenhcd_add_id_to_freelist(info, id);
- return ret;
- }
- urbp->req_id = id;
- }
- req = RING_GET_REQUEST(&info->urb_ring, info->urb_ring.req_prod_pvt);
- *req = info->shadow[id].req;
- info->urb_ring.req_prod_pvt++;
- info->shadow[id].urb = urb;
- info->shadow[id].in_flight = true;
- RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->urb_ring, notify);
- if (notify)
- notify_remote_via_irq(info->irq);
- return 0;
- }
- static void xenhcd_kick_pending_urbs(struct xenhcd_info *info)
- {
- struct urb_priv *urbp;
- while (!list_empty(&info->pending_submit_list)) {
- if (RING_FULL(&info->urb_ring)) {
- xenhcd_timer_action(info, TIMER_RING_WATCHDOG);
- return;
- }
- urbp = list_entry(info->pending_submit_list.next,
- struct urb_priv, list);
- if (!xenhcd_do_request(info, urbp))
- list_move_tail(&urbp->list, &info->in_progress_list);
- else
- xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN);
- }
- xenhcd_timer_action_done(info, TIMER_SCAN_PENDING_URBS);
- }
- /*
- * caller must lock info->lock
- */
- static void xenhcd_cancel_all_enqueued_urbs(struct xenhcd_info *info)
- {
- struct urb_priv *urbp, *tmp;
- int req_id;
- list_for_each_entry_safe(urbp, tmp, &info->in_progress_list, list) {
- req_id = urbp->req_id;
- if (!urbp->unlinked) {
- xenhcd_gnttab_done(info, req_id);
- if (info->error)
- return;
- if (urbp->urb->status == -EINPROGRESS)
- /* not dequeued */
- xenhcd_giveback_urb(info, urbp->urb,
- -ESHUTDOWN);
- else /* dequeued */
- xenhcd_giveback_urb(info, urbp->urb,
- urbp->urb->status);
- }
- info->shadow[req_id].urb = NULL;
- }
- list_for_each_entry_safe(urbp, tmp, &info->pending_submit_list, list)
- xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN);
- }
- /*
- * caller must lock info->lock
- */
- static void xenhcd_giveback_unlinked_urbs(struct xenhcd_info *info)
- {
- struct urb_priv *urbp, *tmp;
- list_for_each_entry_safe(urbp, tmp, &info->giveback_waiting_list, list)
- xenhcd_giveback_urb(info, urbp->urb, urbp->urb->status);
- }
- static int xenhcd_submit_urb(struct xenhcd_info *info, struct urb_priv *urbp)
- {
- int ret;
- if (RING_FULL(&info->urb_ring)) {
- list_add_tail(&urbp->list, &info->pending_submit_list);
- xenhcd_timer_action(info, TIMER_RING_WATCHDOG);
- return 0;
- }
- if (!list_empty(&info->pending_submit_list)) {
- list_add_tail(&urbp->list, &info->pending_submit_list);
- xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS);
- return 0;
- }
- ret = xenhcd_do_request(info, urbp);
- if (ret == 0)
- list_add_tail(&urbp->list, &info->in_progress_list);
- return ret;
- }
- static int xenhcd_unlink_urb(struct xenhcd_info *info, struct urb_priv *urbp)
- {
- int ret;
- /* already unlinked? */
- if (urbp->unlinked)
- return -EBUSY;
- urbp->unlinked = true;
- /* the urb is still in pending_submit queue */
- if (urbp->req_id == ~0) {
- list_move_tail(&urbp->list, &info->giveback_waiting_list);
- xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS);
- return 0;
- }
- /* send unlink request to backend */
- if (RING_FULL(&info->urb_ring)) {
- list_move_tail(&urbp->list, &info->pending_unlink_list);
- xenhcd_timer_action(info, TIMER_RING_WATCHDOG);
- return 0;
- }
- if (!list_empty(&info->pending_unlink_list)) {
- list_move_tail(&urbp->list, &info->pending_unlink_list);
- xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS);
- return 0;
- }
- ret = xenhcd_do_request(info, urbp);
- if (ret == 0)
- list_move_tail(&urbp->list, &info->in_progress_list);
- return ret;
- }
- static void xenhcd_res_to_urb(struct xenhcd_info *info,
- struct xenusb_urb_response *res, struct urb *urb)
- {
- if (unlikely(!urb))
- return;
- if (res->actual_length > urb->transfer_buffer_length)
- urb->actual_length = urb->transfer_buffer_length;
- else if (res->actual_length < 0)
- urb->actual_length = 0;
- else
- urb->actual_length = res->actual_length;
- urb->error_count = res->error_count;
- urb->start_frame = res->start_frame;
- xenhcd_giveback_urb(info, urb, res->status);
- }
- static int xenhcd_urb_request_done(struct xenhcd_info *info,
- unsigned int *eoiflag)
- {
- struct xenusb_urb_response res;
- RING_IDX i, rp;
- __u16 id;
- int more_to_do = 0;
- unsigned long flags;
- spin_lock_irqsave(&info->lock, flags);
- rp = info->urb_ring.sring->rsp_prod;
- if (RING_RESPONSE_PROD_OVERFLOW(&info->urb_ring, rp)) {
- xenhcd_set_error(info, "Illegal index on urb-ring");
- goto err;
- }
- rmb(); /* ensure we see queued responses up to "rp" */
- for (i = info->urb_ring.rsp_cons; i != rp; i++) {
- RING_COPY_RESPONSE(&info->urb_ring, i, &res);
- id = res.id;
- if (id >= XENUSB_URB_RING_SIZE) {
- xenhcd_set_error(info, "Illegal data on urb-ring");
- goto err;
- }
- if (likely(xenusb_pipesubmit(info->shadow[id].req.pipe))) {
- xenhcd_gnttab_done(info, id);
- if (info->error)
- goto err;
- xenhcd_res_to_urb(info, &res, info->shadow[id].urb);
- }
- xenhcd_add_id_to_freelist(info, id);
- *eoiflag = 0;
- }
- info->urb_ring.rsp_cons = i;
- if (i != info->urb_ring.req_prod_pvt)
- RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do);
- else
- info->urb_ring.sring->rsp_event = i + 1;
- spin_unlock_irqrestore(&info->lock, flags);
- return more_to_do;
- err:
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
- }
- static int xenhcd_conn_notify(struct xenhcd_info *info, unsigned int *eoiflag)
- {
- struct xenusb_conn_response res;
- struct xenusb_conn_request *req;
- RING_IDX rc, rp;
- __u16 id;
- __u8 portnum, speed;
- int more_to_do = 0;
- int notify;
- int port_changed = 0;
- unsigned long flags;
- spin_lock_irqsave(&info->lock, flags);
- rc = info->conn_ring.rsp_cons;
- rp = info->conn_ring.sring->rsp_prod;
- if (RING_RESPONSE_PROD_OVERFLOW(&info->conn_ring, rp)) {
- xenhcd_set_error(info, "Illegal index on conn-ring");
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
- }
- rmb(); /* ensure we see queued responses up to "rp" */
- while (rc != rp) {
- RING_COPY_RESPONSE(&info->conn_ring, rc, &res);
- id = res.id;
- portnum = res.portnum;
- speed = res.speed;
- info->conn_ring.rsp_cons = ++rc;
- if (xenhcd_rhport_connect(info, portnum, speed)) {
- xenhcd_set_error(info, "Illegal data on conn-ring");
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
- }
- if (info->ports[portnum - 1].c_connection)
- port_changed = 1;
- barrier();
- req = RING_GET_REQUEST(&info->conn_ring,
- info->conn_ring.req_prod_pvt);
- req->id = id;
- info->conn_ring.req_prod_pvt++;
- *eoiflag = 0;
- }
- if (rc != info->conn_ring.req_prod_pvt)
- RING_FINAL_CHECK_FOR_RESPONSES(&info->conn_ring, more_to_do);
- else
- info->conn_ring.sring->rsp_event = rc + 1;
- RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify);
- if (notify)
- notify_remote_via_irq(info->irq);
- spin_unlock_irqrestore(&info->lock, flags);
- if (port_changed)
- usb_hcd_poll_rh_status(xenhcd_info_to_hcd(info));
- return more_to_do;
- }
- static irqreturn_t xenhcd_int(int irq, void *dev_id)
- {
- struct xenhcd_info *info = (struct xenhcd_info *)dev_id;
- unsigned int eoiflag = XEN_EOI_FLAG_SPURIOUS;
- if (unlikely(info->error)) {
- xen_irq_lateeoi(irq, XEN_EOI_FLAG_SPURIOUS);
- return IRQ_HANDLED;
- }
- while (xenhcd_urb_request_done(info, &eoiflag) |
- xenhcd_conn_notify(info, &eoiflag))
- /* Yield point for this unbounded loop. */
- cond_resched();
- xen_irq_lateeoi(irq, eoiflag);
- return IRQ_HANDLED;
- }
- static void xenhcd_destroy_rings(struct xenhcd_info *info)
- {
- if (info->irq)
- unbind_from_irqhandler(info->irq, info);
- info->irq = 0;
- xenbus_teardown_ring((void **)&info->urb_ring.sring, 1,
- &info->urb_ring_ref);
- xenbus_teardown_ring((void **)&info->conn_ring.sring, 1,
- &info->conn_ring_ref);
- }
- static int xenhcd_setup_rings(struct xenbus_device *dev,
- struct xenhcd_info *info)
- {
- struct xenusb_urb_sring *urb_sring;
- struct xenusb_conn_sring *conn_sring;
- int err;
- info->conn_ring_ref = INVALID_GRANT_REF;
- err = xenbus_setup_ring(dev, GFP_NOIO | __GFP_HIGH,
- (void **)&urb_sring, 1, &info->urb_ring_ref);
- if (err) {
- xenbus_dev_fatal(dev, err, "allocating urb ring");
- return err;
- }
- XEN_FRONT_RING_INIT(&info->urb_ring, urb_sring, PAGE_SIZE);
- err = xenbus_setup_ring(dev, GFP_NOIO | __GFP_HIGH,
- (void **)&conn_sring, 1, &info->conn_ring_ref);
- if (err) {
- xenbus_dev_fatal(dev, err, "allocating conn ring");
- goto fail;
- }
- XEN_FRONT_RING_INIT(&info->conn_ring, conn_sring, PAGE_SIZE);
- err = xenbus_alloc_evtchn(dev, &info->evtchn);
- if (err) {
- xenbus_dev_fatal(dev, err, "xenbus_alloc_evtchn");
- goto fail;
- }
- err = bind_evtchn_to_irq_lateeoi(info->evtchn);
- if (err <= 0) {
- xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq_lateeoi");
- goto fail;
- }
- info->irq = err;
- err = request_threaded_irq(info->irq, NULL, xenhcd_int,
- IRQF_ONESHOT, "xenhcd", info);
- if (err) {
- xenbus_dev_fatal(dev, err, "request_threaded_irq");
- goto free_irq;
- }
- return 0;
- free_irq:
- unbind_from_irqhandler(info->irq, info);
- fail:
- xenhcd_destroy_rings(info);
- return err;
- }
- static int xenhcd_talk_to_backend(struct xenbus_device *dev,
- struct xenhcd_info *info)
- {
- const char *message;
- struct xenbus_transaction xbt;
- int err;
- err = xenhcd_setup_rings(dev, info);
- if (err)
- return err;
- again:
- err = xenbus_transaction_start(&xbt);
- if (err) {
- xenbus_dev_fatal(dev, err, "starting transaction");
- goto destroy_ring;
- }
- err = xenbus_printf(xbt, dev->nodename, "urb-ring-ref", "%u",
- info->urb_ring_ref);
- if (err) {
- message = "writing urb-ring-ref";
- goto abort_transaction;
- }
- err = xenbus_printf(xbt, dev->nodename, "conn-ring-ref", "%u",
- info->conn_ring_ref);
- if (err) {
- message = "writing conn-ring-ref";
- goto abort_transaction;
- }
- err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
- info->evtchn);
- if (err) {
- message = "writing event-channel";
- goto abort_transaction;
- }
- err = xenbus_transaction_end(xbt, 0);
- if (err) {
- if (err == -EAGAIN)
- goto again;
- xenbus_dev_fatal(dev, err, "completing transaction");
- goto destroy_ring;
- }
- return 0;
- abort_transaction:
- xenbus_transaction_end(xbt, 1);
- xenbus_dev_fatal(dev, err, "%s", message);
- destroy_ring:
- xenhcd_destroy_rings(info);
- return err;
- }
- static int xenhcd_connect(struct xenbus_device *dev)
- {
- struct xenhcd_info *info = dev_get_drvdata(&dev->dev);
- struct xenusb_conn_request *req;
- int idx, err;
- int notify;
- char name[TASK_COMM_LEN];
- struct usb_hcd *hcd;
- hcd = xenhcd_info_to_hcd(info);
- snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum);
- err = xenhcd_talk_to_backend(dev, info);
- if (err)
- return err;
- /* prepare ring for hotplug notification */
- for (idx = 0; idx < XENUSB_CONN_RING_SIZE; idx++) {
- req = RING_GET_REQUEST(&info->conn_ring, idx);
- req->id = idx;
- }
- info->conn_ring.req_prod_pvt = idx;
- RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify);
- if (notify)
- notify_remote_via_irq(info->irq);
- return 0;
- }
- static void xenhcd_disconnect(struct xenbus_device *dev)
- {
- struct xenhcd_info *info = dev_get_drvdata(&dev->dev);
- struct usb_hcd *hcd = xenhcd_info_to_hcd(info);
- usb_remove_hcd(hcd);
- xenbus_frontend_closed(dev);
- }
- static void xenhcd_watchdog(struct timer_list *timer)
- {
- struct xenhcd_info *info = from_timer(info, timer, watchdog);
- unsigned long flags;
- spin_lock_irqsave(&info->lock, flags);
- if (likely(HC_IS_RUNNING(xenhcd_info_to_hcd(info)->state))) {
- xenhcd_timer_action_done(info, TIMER_RING_WATCHDOG);
- xenhcd_giveback_unlinked_urbs(info);
- xenhcd_kick_pending_urbs(info);
- }
- spin_unlock_irqrestore(&info->lock, flags);
- }
- /*
- * one-time HC init
- */
- static int xenhcd_setup(struct usb_hcd *hcd)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- spin_lock_init(&info->lock);
- INIT_LIST_HEAD(&info->pending_submit_list);
- INIT_LIST_HEAD(&info->pending_unlink_list);
- INIT_LIST_HEAD(&info->in_progress_list);
- INIT_LIST_HEAD(&info->giveback_waiting_list);
- timer_setup(&info->watchdog, xenhcd_watchdog, 0);
- hcd->has_tt = (hcd->driver->flags & HCD_MASK) != HCD_USB11;
- return 0;
- }
- /*
- * start HC running
- */
- static int xenhcd_run(struct usb_hcd *hcd)
- {
- hcd->uses_new_polling = 1;
- clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- hcd->state = HC_STATE_RUNNING;
- return 0;
- }
- /*
- * stop running HC
- */
- static void xenhcd_stop(struct usb_hcd *hcd)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- del_timer_sync(&info->watchdog);
- spin_lock_irq(&info->lock);
- /* cancel all urbs */
- hcd->state = HC_STATE_HALT;
- xenhcd_cancel_all_enqueued_urbs(info);
- xenhcd_giveback_unlinked_urbs(info);
- spin_unlock_irq(&info->lock);
- }
- /*
- * called as .urb_enqueue()
- * non-error returns are promise to giveback the urb later
- */
- static int xenhcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
- gfp_t mem_flags)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- struct urb_priv *urbp;
- unsigned long flags;
- int ret;
- if (unlikely(info->error))
- return -ESHUTDOWN;
- urbp = kmem_cache_zalloc(xenhcd_urbp_cachep, mem_flags);
- if (!urbp)
- return -ENOMEM;
- spin_lock_irqsave(&info->lock, flags);
- urbp->urb = urb;
- urb->hcpriv = urbp;
- urbp->req_id = ~0;
- urbp->unlink_req_id = ~0;
- INIT_LIST_HEAD(&urbp->list);
- urbp->status = 1;
- urb->unlinked = false;
- ret = xenhcd_submit_urb(info, urbp);
- if (ret)
- xenhcd_free_urb_priv(urbp);
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
- }
- /*
- * called as .urb_dequeue()
- */
- static int xenhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
- {
- struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- struct urb_priv *urbp;
- unsigned long flags;
- int ret = 0;
- spin_lock_irqsave(&info->lock, flags);
- urbp = urb->hcpriv;
- if (urbp) {
- urbp->status = status;
- ret = xenhcd_unlink_urb(info, urbp);
- }
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
- }
- /*
- * called from usb_get_current_frame_number(),
- * but, almost all drivers not use such function.
- */
- static int xenhcd_get_frame(struct usb_hcd *hcd)
- {
- /* it means error, but probably no problem :-) */
- return 0;
- }
- static struct hc_driver xenhcd_usb20_hc_driver = {
- .description = "xen-hcd",
- .product_desc = "Xen USB2.0 Virtual Host Controller",
- .hcd_priv_size = sizeof(struct xenhcd_info),
- .flags = HCD_USB2,
- /* basic HC lifecycle operations */
- .reset = xenhcd_setup,
- .start = xenhcd_run,
- .stop = xenhcd_stop,
- /* managing urb I/O */
- .urb_enqueue = xenhcd_urb_enqueue,
- .urb_dequeue = xenhcd_urb_dequeue,
- .get_frame_number = xenhcd_get_frame,
- /* root hub operations */
- .hub_status_data = xenhcd_hub_status_data,
- .hub_control = xenhcd_hub_control,
- #ifdef CONFIG_PM
- .bus_suspend = xenhcd_bus_suspend,
- .bus_resume = xenhcd_bus_resume,
- #endif
- };
- static struct hc_driver xenhcd_usb11_hc_driver = {
- .description = "xen-hcd",
- .product_desc = "Xen USB1.1 Virtual Host Controller",
- .hcd_priv_size = sizeof(struct xenhcd_info),
- .flags = HCD_USB11,
- /* basic HC lifecycle operations */
- .reset = xenhcd_setup,
- .start = xenhcd_run,
- .stop = xenhcd_stop,
- /* managing urb I/O */
- .urb_enqueue = xenhcd_urb_enqueue,
- .urb_dequeue = xenhcd_urb_dequeue,
- .get_frame_number = xenhcd_get_frame,
- /* root hub operations */
- .hub_status_data = xenhcd_hub_status_data,
- .hub_control = xenhcd_hub_control,
- #ifdef CONFIG_PM
- .bus_suspend = xenhcd_bus_suspend,
- .bus_resume = xenhcd_bus_resume,
- #endif
- };
- static struct usb_hcd *xenhcd_create_hcd(struct xenbus_device *dev)
- {
- int i;
- int err = 0;
- int num_ports;
- int usb_ver;
- struct usb_hcd *hcd = NULL;
- struct xenhcd_info *info;
- err = xenbus_scanf(XBT_NIL, dev->otherend, "num-ports", "%d",
- &num_ports);
- if (err != 1) {
- xenbus_dev_fatal(dev, err, "reading num-ports");
- return ERR_PTR(-EINVAL);
- }
- if (num_ports < 1 || num_ports > XENUSB_MAX_PORTNR) {
- xenbus_dev_fatal(dev, err, "invalid num-ports");
- return ERR_PTR(-EINVAL);
- }
- err = xenbus_scanf(XBT_NIL, dev->otherend, "usb-ver", "%d", &usb_ver);
- if (err != 1) {
- xenbus_dev_fatal(dev, err, "reading usb-ver");
- return ERR_PTR(-EINVAL);
- }
- switch (usb_ver) {
- case XENUSB_VER_USB11:
- hcd = usb_create_hcd(&xenhcd_usb11_hc_driver, &dev->dev,
- dev_name(&dev->dev));
- break;
- case XENUSB_VER_USB20:
- hcd = usb_create_hcd(&xenhcd_usb20_hc_driver, &dev->dev,
- dev_name(&dev->dev));
- break;
- default:
- xenbus_dev_fatal(dev, err, "invalid usb-ver");
- return ERR_PTR(-EINVAL);
- }
- if (!hcd) {
- xenbus_dev_fatal(dev, err,
- "fail to allocate USB host controller");
- return ERR_PTR(-ENOMEM);
- }
- info = xenhcd_hcd_to_info(hcd);
- info->xbdev = dev;
- info->rh_numports = num_ports;
- for (i = 0; i < XENUSB_URB_RING_SIZE; i++) {
- info->shadow[i].req.id = i + 1;
- info->shadow[i].urb = NULL;
- info->shadow[i].in_flight = false;
- }
- info->shadow[XENUSB_URB_RING_SIZE - 1].req.id = 0x0fff;
- return hcd;
- }
- static void xenhcd_backend_changed(struct xenbus_device *dev,
- enum xenbus_state backend_state)
- {
- switch (backend_state) {
- case XenbusStateInitialising:
- case XenbusStateReconfiguring:
- case XenbusStateReconfigured:
- case XenbusStateUnknown:
- break;
- case XenbusStateInitWait:
- case XenbusStateInitialised:
- case XenbusStateConnected:
- if (dev->state != XenbusStateInitialising)
- break;
- if (!xenhcd_connect(dev))
- xenbus_switch_state(dev, XenbusStateConnected);
- break;
- case XenbusStateClosed:
- if (dev->state == XenbusStateClosed)
- break;
- fallthrough; /* Missed the backend's Closing state. */
- case XenbusStateClosing:
- xenhcd_disconnect(dev);
- break;
- default:
- xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
- backend_state);
- break;
- }
- }
- static int xenhcd_remove(struct xenbus_device *dev)
- {
- struct xenhcd_info *info = dev_get_drvdata(&dev->dev);
- struct usb_hcd *hcd = xenhcd_info_to_hcd(info);
- xenhcd_destroy_rings(info);
- usb_put_hcd(hcd);
- return 0;
- }
- static int xenhcd_probe(struct xenbus_device *dev,
- const struct xenbus_device_id *id)
- {
- int err;
- struct usb_hcd *hcd;
- struct xenhcd_info *info;
- if (usb_disabled())
- return -ENODEV;
- hcd = xenhcd_create_hcd(dev);
- if (IS_ERR(hcd)) {
- err = PTR_ERR(hcd);
- xenbus_dev_fatal(dev, err,
- "fail to create usb host controller");
- return err;
- }
- info = xenhcd_hcd_to_info(hcd);
- dev_set_drvdata(&dev->dev, info);
- err = usb_add_hcd(hcd, 0, 0);
- if (err) {
- xenbus_dev_fatal(dev, err, "fail to add USB host controller");
- usb_put_hcd(hcd);
- dev_set_drvdata(&dev->dev, NULL);
- }
- return err;
- }
- static const struct xenbus_device_id xenhcd_ids[] = {
- { "vusb" },
- { "" },
- };
- static struct xenbus_driver xenhcd_driver = {
- .ids = xenhcd_ids,
- .probe = xenhcd_probe,
- .otherend_changed = xenhcd_backend_changed,
- .remove = xenhcd_remove,
- };
- static int __init xenhcd_init(void)
- {
- if (!xen_domain())
- return -ENODEV;
- xenhcd_urbp_cachep = kmem_cache_create("xenhcd_urb_priv",
- sizeof(struct urb_priv), 0, 0, NULL);
- if (!xenhcd_urbp_cachep) {
- pr_err("xenhcd failed to create kmem cache\n");
- return -ENOMEM;
- }
- return xenbus_register_frontend(&xenhcd_driver);
- }
- module_init(xenhcd_init);
- static void __exit xenhcd_exit(void)
- {
- kmem_cache_destroy(xenhcd_urbp_cachep);
- xenbus_unregister_driver(&xenhcd_driver);
- }
- module_exit(xenhcd_exit);
- MODULE_ALIAS("xen:vusb");
- MODULE_AUTHOR("Juergen Gross <[email protected]>");
- MODULE_DESCRIPTION("Xen USB Virtual Host Controller driver (xen-hcd)");
- MODULE_LICENSE("Dual BSD/GPL");
|