xref: /openbmc/linux/drivers/usb/host/xen-hcd.c (revision 7cffcade)
1494ed399SJuergen Gross // SPDX-License-Identifier: GPL-2.0-or-later
2494ed399SJuergen Gross /*
3494ed399SJuergen Gross  * xen-hcd.c
4494ed399SJuergen Gross  *
5494ed399SJuergen Gross  * Xen USB Virtual Host Controller driver
6494ed399SJuergen Gross  *
7494ed399SJuergen Gross  * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
8494ed399SJuergen Gross  * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
9494ed399SJuergen Gross  */
10494ed399SJuergen Gross 
11494ed399SJuergen Gross #include <linux/module.h>
12494ed399SJuergen Gross #include <linux/usb.h>
13494ed399SJuergen Gross #include <linux/list.h>
14494ed399SJuergen Gross #include <linux/usb/hcd.h>
15494ed399SJuergen Gross #include <linux/io.h>
16494ed399SJuergen Gross 
17494ed399SJuergen Gross #include <xen/xen.h>
18494ed399SJuergen Gross #include <xen/xenbus.h>
19494ed399SJuergen Gross #include <xen/grant_table.h>
20494ed399SJuergen Gross #include <xen/events.h>
21494ed399SJuergen Gross #include <xen/page.h>
22494ed399SJuergen Gross 
23494ed399SJuergen Gross #include <xen/interface/io/usbif.h>
24494ed399SJuergen Gross 
25494ed399SJuergen Gross /* Private per-URB data */
26494ed399SJuergen Gross struct urb_priv {
27494ed399SJuergen Gross 	struct list_head list;
28494ed399SJuergen Gross 	struct urb *urb;
29494ed399SJuergen Gross 	int req_id;		/* RING_REQUEST id for submitting */
30494ed399SJuergen Gross 	int unlink_req_id;	/* RING_REQUEST id for unlinking */
31494ed399SJuergen Gross 	int status;
32494ed399SJuergen Gross 	bool unlinked;		/* dequeued marker */
33494ed399SJuergen Gross };
34494ed399SJuergen Gross 
35494ed399SJuergen Gross /* virtual roothub port status */
36494ed399SJuergen Gross struct rhport_status {
37494ed399SJuergen Gross 	__u32 status;
38494ed399SJuergen Gross 	bool resuming;		/* in resuming */
39494ed399SJuergen Gross 	bool c_connection;	/* connection changed */
40494ed399SJuergen Gross 	unsigned long timeout;
41494ed399SJuergen Gross };
42494ed399SJuergen Gross 
43494ed399SJuergen Gross /* status of attached device */
44494ed399SJuergen Gross struct vdevice_status {
45494ed399SJuergen Gross 	int devnum;
46494ed399SJuergen Gross 	enum usb_device_state status;
47494ed399SJuergen Gross 	enum usb_device_speed speed;
48494ed399SJuergen Gross };
49494ed399SJuergen Gross 
50494ed399SJuergen Gross /* RING request shadow */
51494ed399SJuergen Gross struct usb_shadow {
52494ed399SJuergen Gross 	struct xenusb_urb_request req;
53494ed399SJuergen Gross 	struct urb *urb;
54aff477cbSJuergen Gross 	bool in_flight;
55494ed399SJuergen Gross };
56494ed399SJuergen Gross 
57494ed399SJuergen Gross struct xenhcd_info {
58494ed399SJuergen Gross 	/* Virtual Host Controller has 4 urb queues */
59494ed399SJuergen Gross 	struct list_head pending_submit_list;
60494ed399SJuergen Gross 	struct list_head pending_unlink_list;
61494ed399SJuergen Gross 	struct list_head in_progress_list;
62494ed399SJuergen Gross 	struct list_head giveback_waiting_list;
63494ed399SJuergen Gross 
64494ed399SJuergen Gross 	spinlock_t lock;
65494ed399SJuergen Gross 
66494ed399SJuergen Gross 	/* timer that kick pending and giveback waiting urbs */
67494ed399SJuergen Gross 	struct timer_list watchdog;
68494ed399SJuergen Gross 	unsigned long actions;
69494ed399SJuergen Gross 
70494ed399SJuergen Gross 	/* virtual root hub */
71494ed399SJuergen Gross 	int rh_numports;
72494ed399SJuergen Gross 	struct rhport_status ports[XENUSB_MAX_PORTNR];
73494ed399SJuergen Gross 	struct vdevice_status devices[XENUSB_MAX_PORTNR];
74494ed399SJuergen Gross 
75494ed399SJuergen Gross 	/* Xen related staff */
76494ed399SJuergen Gross 	struct xenbus_device *xbdev;
77494ed399SJuergen Gross 	int urb_ring_ref;
78494ed399SJuergen Gross 	int conn_ring_ref;
79494ed399SJuergen Gross 	struct xenusb_urb_front_ring urb_ring;
80494ed399SJuergen Gross 	struct xenusb_conn_front_ring conn_ring;
81494ed399SJuergen Gross 
82494ed399SJuergen Gross 	unsigned int evtchn;
83494ed399SJuergen Gross 	unsigned int irq;
84494ed399SJuergen Gross 	struct usb_shadow shadow[XENUSB_URB_RING_SIZE];
85494ed399SJuergen Gross 	unsigned int shadow_free;
86494ed399SJuergen Gross 
87494ed399SJuergen Gross 	bool error;
88494ed399SJuergen Gross };
89494ed399SJuergen Gross 
90494ed399SJuergen Gross #define XENHCD_RING_JIFFIES (HZ/200)
91494ed399SJuergen Gross #define XENHCD_SCAN_JIFFIES 1
92494ed399SJuergen Gross 
93494ed399SJuergen Gross enum xenhcd_timer_action {
94494ed399SJuergen Gross 	TIMER_RING_WATCHDOG,
95494ed399SJuergen Gross 	TIMER_SCAN_PENDING_URBS,
96494ed399SJuergen Gross };
97494ed399SJuergen Gross 
98494ed399SJuergen Gross static struct kmem_cache *xenhcd_urbp_cachep;
99494ed399SJuergen Gross 
xenhcd_hcd_to_info(struct usb_hcd * hcd)100494ed399SJuergen Gross static inline struct xenhcd_info *xenhcd_hcd_to_info(struct usb_hcd *hcd)
101494ed399SJuergen Gross {
102494ed399SJuergen Gross 	return (struct xenhcd_info *)hcd->hcd_priv;
103494ed399SJuergen Gross }
104494ed399SJuergen Gross 
xenhcd_info_to_hcd(struct xenhcd_info * info)105494ed399SJuergen Gross static inline struct usb_hcd *xenhcd_info_to_hcd(struct xenhcd_info *info)
106494ed399SJuergen Gross {
107494ed399SJuergen Gross 	return container_of((void *)info, struct usb_hcd, hcd_priv);
108494ed399SJuergen Gross }
109494ed399SJuergen Gross 
xenhcd_set_error(struct xenhcd_info * info,const char * msg)110494ed399SJuergen Gross static void xenhcd_set_error(struct xenhcd_info *info, const char *msg)
111494ed399SJuergen Gross {
112494ed399SJuergen Gross 	info->error = true;
113494ed399SJuergen Gross 
114494ed399SJuergen Gross 	pr_alert("xen-hcd: protocol error: %s!\n", msg);
115494ed399SJuergen Gross }
116494ed399SJuergen Gross 
xenhcd_timer_action_done(struct xenhcd_info * info,enum xenhcd_timer_action action)117494ed399SJuergen Gross static inline void xenhcd_timer_action_done(struct xenhcd_info *info,
118494ed399SJuergen Gross 					    enum xenhcd_timer_action action)
119494ed399SJuergen Gross {
120494ed399SJuergen Gross 	clear_bit(action, &info->actions);
121494ed399SJuergen Gross }
122494ed399SJuergen Gross 
xenhcd_timer_action(struct xenhcd_info * info,enum xenhcd_timer_action action)123494ed399SJuergen Gross static void xenhcd_timer_action(struct xenhcd_info *info,
124494ed399SJuergen Gross 				enum xenhcd_timer_action action)
125494ed399SJuergen Gross {
126494ed399SJuergen Gross 	if (timer_pending(&info->watchdog) &&
127494ed399SJuergen Gross 	    test_bit(TIMER_SCAN_PENDING_URBS, &info->actions))
128494ed399SJuergen Gross 		return;
129494ed399SJuergen Gross 
130494ed399SJuergen Gross 	if (!test_and_set_bit(action, &info->actions)) {
131494ed399SJuergen Gross 		unsigned long t;
132494ed399SJuergen Gross 
133494ed399SJuergen Gross 		switch (action) {
134494ed399SJuergen Gross 		case TIMER_RING_WATCHDOG:
135494ed399SJuergen Gross 			t = XENHCD_RING_JIFFIES;
136494ed399SJuergen Gross 			break;
137494ed399SJuergen Gross 		default:
138494ed399SJuergen Gross 			t = XENHCD_SCAN_JIFFIES;
139494ed399SJuergen Gross 			break;
140494ed399SJuergen Gross 		}
141494ed399SJuergen Gross 		mod_timer(&info->watchdog, t + jiffies);
142494ed399SJuergen Gross 	}
143494ed399SJuergen Gross }
144494ed399SJuergen Gross 
145494ed399SJuergen Gross /*
146494ed399SJuergen Gross  * set virtual port connection status
147494ed399SJuergen Gross  */
xenhcd_set_connect_state(struct xenhcd_info * info,int portnum)148494ed399SJuergen Gross static void xenhcd_set_connect_state(struct xenhcd_info *info, int portnum)
149494ed399SJuergen Gross {
150494ed399SJuergen Gross 	int port;
151494ed399SJuergen Gross 
152494ed399SJuergen Gross 	port = portnum - 1;
153494ed399SJuergen Gross 	if (info->ports[port].status & USB_PORT_STAT_POWER) {
154494ed399SJuergen Gross 		switch (info->devices[port].speed) {
155494ed399SJuergen Gross 		case XENUSB_SPEED_NONE:
156494ed399SJuergen Gross 			info->ports[port].status &=
157494ed399SJuergen Gross 				~(USB_PORT_STAT_CONNECTION |
158494ed399SJuergen Gross 				  USB_PORT_STAT_ENABLE |
159494ed399SJuergen Gross 				  USB_PORT_STAT_LOW_SPEED |
160494ed399SJuergen Gross 				  USB_PORT_STAT_HIGH_SPEED |
161494ed399SJuergen Gross 				  USB_PORT_STAT_SUSPEND);
162494ed399SJuergen Gross 			break;
163494ed399SJuergen Gross 		case XENUSB_SPEED_LOW:
164494ed399SJuergen Gross 			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
165494ed399SJuergen Gross 			info->ports[port].status |= USB_PORT_STAT_LOW_SPEED;
166494ed399SJuergen Gross 			break;
167494ed399SJuergen Gross 		case XENUSB_SPEED_FULL:
168494ed399SJuergen Gross 			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
169494ed399SJuergen Gross 			break;
170494ed399SJuergen Gross 		case XENUSB_SPEED_HIGH:
171494ed399SJuergen Gross 			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
172494ed399SJuergen Gross 			info->ports[port].status |= USB_PORT_STAT_HIGH_SPEED;
173494ed399SJuergen Gross 			break;
174494ed399SJuergen Gross 		default: /* error */
175494ed399SJuergen Gross 			return;
176494ed399SJuergen Gross 		}
177494ed399SJuergen Gross 		info->ports[port].status |= (USB_PORT_STAT_C_CONNECTION << 16);
178494ed399SJuergen Gross 	}
179494ed399SJuergen Gross }
180494ed399SJuergen Gross 
181494ed399SJuergen Gross /*
182494ed399SJuergen Gross  * set virtual device connection status
183494ed399SJuergen Gross  */
xenhcd_rhport_connect(struct xenhcd_info * info,__u8 portnum,__u8 speed)184494ed399SJuergen Gross static int xenhcd_rhport_connect(struct xenhcd_info *info, __u8 portnum,
185494ed399SJuergen Gross 				 __u8 speed)
186494ed399SJuergen Gross {
187494ed399SJuergen Gross 	int port;
188494ed399SJuergen Gross 
189494ed399SJuergen Gross 	if (portnum < 1 || portnum > info->rh_numports)
190494ed399SJuergen Gross 		return -EINVAL; /* invalid port number */
191494ed399SJuergen Gross 
192494ed399SJuergen Gross 	port = portnum - 1;
193494ed399SJuergen Gross 	if (info->devices[port].speed != speed) {
194494ed399SJuergen Gross 		switch (speed) {
195494ed399SJuergen Gross 		case XENUSB_SPEED_NONE: /* disconnect */
196494ed399SJuergen Gross 			info->devices[port].status = USB_STATE_NOTATTACHED;
197494ed399SJuergen Gross 			break;
198494ed399SJuergen Gross 		case XENUSB_SPEED_LOW:
199494ed399SJuergen Gross 		case XENUSB_SPEED_FULL:
200494ed399SJuergen Gross 		case XENUSB_SPEED_HIGH:
201494ed399SJuergen Gross 			info->devices[port].status = USB_STATE_ATTACHED;
202494ed399SJuergen Gross 			break;
203494ed399SJuergen Gross 		default: /* error */
204494ed399SJuergen Gross 			return -EINVAL;
205494ed399SJuergen Gross 		}
206494ed399SJuergen Gross 		info->devices[port].speed = speed;
207494ed399SJuergen Gross 		info->ports[port].c_connection = true;
208494ed399SJuergen Gross 
209494ed399SJuergen Gross 		xenhcd_set_connect_state(info, portnum);
210494ed399SJuergen Gross 	}
211494ed399SJuergen Gross 
212494ed399SJuergen Gross 	return 0;
213494ed399SJuergen Gross }
214494ed399SJuergen Gross 
215494ed399SJuergen Gross /*
216494ed399SJuergen Gross  * SetPortFeature(PORT_SUSPENDED)
217494ed399SJuergen Gross  */
xenhcd_rhport_suspend(struct xenhcd_info * info,int portnum)218494ed399SJuergen Gross static void xenhcd_rhport_suspend(struct xenhcd_info *info, int portnum)
219494ed399SJuergen Gross {
220494ed399SJuergen Gross 	int port;
221494ed399SJuergen Gross 
222494ed399SJuergen Gross 	port = portnum - 1;
223494ed399SJuergen Gross 	info->ports[port].status |= USB_PORT_STAT_SUSPEND;
224494ed399SJuergen Gross 	info->devices[port].status = USB_STATE_SUSPENDED;
225494ed399SJuergen Gross }
226494ed399SJuergen Gross 
227494ed399SJuergen Gross /*
228494ed399SJuergen Gross  * ClearPortFeature(PORT_SUSPENDED)
229494ed399SJuergen Gross  */
xenhcd_rhport_resume(struct xenhcd_info * info,int portnum)230494ed399SJuergen Gross static void xenhcd_rhport_resume(struct xenhcd_info *info, int portnum)
231494ed399SJuergen Gross {
232494ed399SJuergen Gross 	int port;
233494ed399SJuergen Gross 
234494ed399SJuergen Gross 	port = portnum - 1;
235494ed399SJuergen Gross 	if (info->ports[port].status & USB_PORT_STAT_SUSPEND) {
236494ed399SJuergen Gross 		info->ports[port].resuming = true;
237494ed399SJuergen Gross 		info->ports[port].timeout = jiffies + msecs_to_jiffies(20);
238494ed399SJuergen Gross 	}
239494ed399SJuergen Gross }
240494ed399SJuergen Gross 
241494ed399SJuergen Gross /*
242494ed399SJuergen Gross  * SetPortFeature(PORT_POWER)
243494ed399SJuergen Gross  */
xenhcd_rhport_power_on(struct xenhcd_info * info,int portnum)244494ed399SJuergen Gross static void xenhcd_rhport_power_on(struct xenhcd_info *info, int portnum)
245494ed399SJuergen Gross {
246494ed399SJuergen Gross 	int port;
247494ed399SJuergen Gross 
248494ed399SJuergen Gross 	port = portnum - 1;
249494ed399SJuergen Gross 	if ((info->ports[port].status & USB_PORT_STAT_POWER) == 0) {
250494ed399SJuergen Gross 		info->ports[port].status |= USB_PORT_STAT_POWER;
251494ed399SJuergen Gross 		if (info->devices[port].status != USB_STATE_NOTATTACHED)
252494ed399SJuergen Gross 			info->devices[port].status = USB_STATE_POWERED;
253494ed399SJuergen Gross 		if (info->ports[port].c_connection)
254494ed399SJuergen Gross 			xenhcd_set_connect_state(info, portnum);
255494ed399SJuergen Gross 	}
256494ed399SJuergen Gross }
257494ed399SJuergen Gross 
258494ed399SJuergen Gross /*
259494ed399SJuergen Gross  * ClearPortFeature(PORT_POWER)
260494ed399SJuergen Gross  * SetConfiguration(non-zero)
261494ed399SJuergen Gross  * Power_Source_Off
262494ed399SJuergen Gross  * Over-current
263494ed399SJuergen Gross  */
xenhcd_rhport_power_off(struct xenhcd_info * info,int portnum)264494ed399SJuergen Gross static void xenhcd_rhport_power_off(struct xenhcd_info *info, int portnum)
265494ed399SJuergen Gross {
266494ed399SJuergen Gross 	int port;
267494ed399SJuergen Gross 
268494ed399SJuergen Gross 	port = portnum - 1;
269494ed399SJuergen Gross 	if (info->ports[port].status & USB_PORT_STAT_POWER) {
270494ed399SJuergen Gross 		info->ports[port].status = 0;
271494ed399SJuergen Gross 		if (info->devices[port].status != USB_STATE_NOTATTACHED)
272494ed399SJuergen Gross 			info->devices[port].status = USB_STATE_ATTACHED;
273494ed399SJuergen Gross 	}
274494ed399SJuergen Gross }
275494ed399SJuergen Gross 
276494ed399SJuergen Gross /*
277494ed399SJuergen Gross  * ClearPortFeature(PORT_ENABLE)
278494ed399SJuergen Gross  */
xenhcd_rhport_disable(struct xenhcd_info * info,int portnum)279494ed399SJuergen Gross static void xenhcd_rhport_disable(struct xenhcd_info *info, int portnum)
280494ed399SJuergen Gross {
281494ed399SJuergen Gross 	int port;
282494ed399SJuergen Gross 
283494ed399SJuergen Gross 	port = portnum - 1;
284494ed399SJuergen Gross 	info->ports[port].status &= ~USB_PORT_STAT_ENABLE;
285494ed399SJuergen Gross 	info->ports[port].status &= ~USB_PORT_STAT_SUSPEND;
286494ed399SJuergen Gross 	info->ports[port].resuming = false;
287494ed399SJuergen Gross 	if (info->devices[port].status != USB_STATE_NOTATTACHED)
288494ed399SJuergen Gross 		info->devices[port].status = USB_STATE_POWERED;
289494ed399SJuergen Gross }
290494ed399SJuergen Gross 
291494ed399SJuergen Gross /*
292494ed399SJuergen Gross  * SetPortFeature(PORT_RESET)
293494ed399SJuergen Gross  */
xenhcd_rhport_reset(struct xenhcd_info * info,int portnum)294494ed399SJuergen Gross static void xenhcd_rhport_reset(struct xenhcd_info *info, int portnum)
295494ed399SJuergen Gross {
296494ed399SJuergen Gross 	int port;
297494ed399SJuergen Gross 
298494ed399SJuergen Gross 	port = portnum - 1;
299494ed399SJuergen Gross 	info->ports[port].status &= ~(USB_PORT_STAT_ENABLE |
300494ed399SJuergen Gross 				      USB_PORT_STAT_LOW_SPEED |
301494ed399SJuergen Gross 				      USB_PORT_STAT_HIGH_SPEED);
302494ed399SJuergen Gross 	info->ports[port].status |= USB_PORT_STAT_RESET;
303494ed399SJuergen Gross 
304494ed399SJuergen Gross 	if (info->devices[port].status != USB_STATE_NOTATTACHED)
305494ed399SJuergen Gross 		info->devices[port].status = USB_STATE_ATTACHED;
306494ed399SJuergen Gross 
307494ed399SJuergen Gross 	/* 10msec reset signaling */
308494ed399SJuergen Gross 	info->ports[port].timeout = jiffies + msecs_to_jiffies(10);
309494ed399SJuergen Gross }
310494ed399SJuergen Gross 
311494ed399SJuergen Gross #ifdef CONFIG_PM
xenhcd_bus_suspend(struct usb_hcd * hcd)312494ed399SJuergen Gross static int xenhcd_bus_suspend(struct usb_hcd *hcd)
313494ed399SJuergen Gross {
314494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
315494ed399SJuergen Gross 	int ret = 0;
316494ed399SJuergen Gross 	int i, ports;
317494ed399SJuergen Gross 
318494ed399SJuergen Gross 	ports = info->rh_numports;
319494ed399SJuergen Gross 
320494ed399SJuergen Gross 	spin_lock_irq(&info->lock);
321494ed399SJuergen Gross 	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
322494ed399SJuergen Gross 		ret = -ESHUTDOWN;
323494ed399SJuergen Gross 	} else {
324494ed399SJuergen Gross 		/* suspend any active ports*/
325494ed399SJuergen Gross 		for (i = 1; i <= ports; i++)
326494ed399SJuergen Gross 			xenhcd_rhport_suspend(info, i);
327494ed399SJuergen Gross 	}
328494ed399SJuergen Gross 	spin_unlock_irq(&info->lock);
329494ed399SJuergen Gross 
330494ed399SJuergen Gross 	del_timer_sync(&info->watchdog);
331494ed399SJuergen Gross 
332494ed399SJuergen Gross 	return ret;
333494ed399SJuergen Gross }
334494ed399SJuergen Gross 
xenhcd_bus_resume(struct usb_hcd * hcd)335494ed399SJuergen Gross static int xenhcd_bus_resume(struct usb_hcd *hcd)
336494ed399SJuergen Gross {
337494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
338494ed399SJuergen Gross 	int ret = 0;
339494ed399SJuergen Gross 	int i, ports;
340494ed399SJuergen Gross 
341494ed399SJuergen Gross 	ports = info->rh_numports;
342494ed399SJuergen Gross 
343494ed399SJuergen Gross 	spin_lock_irq(&info->lock);
344494ed399SJuergen Gross 	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
345494ed399SJuergen Gross 		ret = -ESHUTDOWN;
346494ed399SJuergen Gross 	} else {
347494ed399SJuergen Gross 		/* resume any suspended ports*/
348494ed399SJuergen Gross 		for (i = 1; i <= ports; i++)
349494ed399SJuergen Gross 			xenhcd_rhport_resume(info, i);
350494ed399SJuergen Gross 	}
351494ed399SJuergen Gross 	spin_unlock_irq(&info->lock);
352494ed399SJuergen Gross 
353494ed399SJuergen Gross 	return ret;
354494ed399SJuergen Gross }
355494ed399SJuergen Gross #endif
356494ed399SJuergen Gross 
xenhcd_hub_descriptor(struct xenhcd_info * info,struct usb_hub_descriptor * desc)357494ed399SJuergen Gross static void xenhcd_hub_descriptor(struct xenhcd_info *info,
358494ed399SJuergen Gross 				  struct usb_hub_descriptor *desc)
359494ed399SJuergen Gross {
360494ed399SJuergen Gross 	__u16 temp;
361494ed399SJuergen Gross 	int ports = info->rh_numports;
362494ed399SJuergen Gross 
363494ed399SJuergen Gross 	desc->bDescriptorType = 0x29;
364494ed399SJuergen Gross 	desc->bPwrOn2PwrGood = 10; /* EHCI says 20ms max */
365494ed399SJuergen Gross 	desc->bHubContrCurrent = 0;
366494ed399SJuergen Gross 	desc->bNbrPorts = ports;
367494ed399SJuergen Gross 
368494ed399SJuergen Gross 	/* size of DeviceRemovable and PortPwrCtrlMask fields */
369494ed399SJuergen Gross 	temp = 1 + (ports / 8);
370494ed399SJuergen Gross 	desc->bDescLength = 7 + 2 * temp;
371494ed399SJuergen Gross 
372494ed399SJuergen Gross 	/* bitmaps for DeviceRemovable and PortPwrCtrlMask */
373494ed399SJuergen Gross 	memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
374494ed399SJuergen Gross 	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
375494ed399SJuergen Gross 
376494ed399SJuergen Gross 	/* per-port over current reporting and no power switching */
377494ed399SJuergen Gross 	temp = 0x000a;
378494ed399SJuergen Gross 	desc->wHubCharacteristics = cpu_to_le16(temp);
379494ed399SJuergen Gross }
380494ed399SJuergen Gross 
381494ed399SJuergen Gross /* port status change mask for hub_status_data */
382494ed399SJuergen Gross #define PORT_C_MASK	((USB_PORT_STAT_C_CONNECTION |		\
383494ed399SJuergen Gross 			  USB_PORT_STAT_C_ENABLE |		\
384494ed399SJuergen Gross 			  USB_PORT_STAT_C_SUSPEND |		\
385494ed399SJuergen Gross 			  USB_PORT_STAT_C_OVERCURRENT |		\
386494ed399SJuergen Gross 			  USB_PORT_STAT_C_RESET) << 16)
387494ed399SJuergen Gross 
388494ed399SJuergen Gross /*
389494ed399SJuergen Gross  * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap.
390494ed399SJuergen Gross  * If port status changed, writes the bitmap to buf and return
391494ed399SJuergen Gross  * that length(number of bytes).
392494ed399SJuergen Gross  * If Nothing changed, return 0.
393494ed399SJuergen Gross  */
xenhcd_hub_status_data(struct usb_hcd * hcd,char * buf)394494ed399SJuergen Gross static int xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
395494ed399SJuergen Gross {
396494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
397494ed399SJuergen Gross 	int ports;
398494ed399SJuergen Gross 	int i;
399494ed399SJuergen Gross 	unsigned long flags;
400494ed399SJuergen Gross 	int ret;
401494ed399SJuergen Gross 	int changed = 0;
402494ed399SJuergen Gross 
403494ed399SJuergen Gross 	/* initialize the status to no-changes */
404494ed399SJuergen Gross 	ports = info->rh_numports;
405494ed399SJuergen Gross 	ret = 1 + (ports / 8);
406494ed399SJuergen Gross 	memset(buf, 0, ret);
407494ed399SJuergen Gross 
408494ed399SJuergen Gross 	spin_lock_irqsave(&info->lock, flags);
409494ed399SJuergen Gross 
410494ed399SJuergen Gross 	for (i = 0; i < ports; i++) {
411494ed399SJuergen Gross 		/* check status for each port */
412494ed399SJuergen Gross 		if (info->ports[i].status & PORT_C_MASK) {
413494ed399SJuergen Gross 			buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
414494ed399SJuergen Gross 			changed = 1;
415494ed399SJuergen Gross 		}
416494ed399SJuergen Gross 	}
417494ed399SJuergen Gross 
418494ed399SJuergen Gross 	if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
419494ed399SJuergen Gross 		usb_hcd_resume_root_hub(hcd);
420494ed399SJuergen Gross 
421494ed399SJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
422494ed399SJuergen Gross 
423494ed399SJuergen Gross 	return changed ? ret : 0;
424494ed399SJuergen Gross }
425494ed399SJuergen Gross 
xenhcd_hub_control(struct usb_hcd * hcd,__u16 typeReq,__u16 wValue,__u16 wIndex,char * buf,__u16 wLength)426494ed399SJuergen Gross static int xenhcd_hub_control(struct usb_hcd *hcd, __u16 typeReq, __u16 wValue,
427494ed399SJuergen Gross 			      __u16 wIndex, char *buf, __u16 wLength)
428494ed399SJuergen Gross {
429494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
430494ed399SJuergen Gross 	int ports = info->rh_numports;
431494ed399SJuergen Gross 	unsigned long flags;
432494ed399SJuergen Gross 	int ret = 0;
433494ed399SJuergen Gross 	int i;
434494ed399SJuergen Gross 	int changed = 0;
435494ed399SJuergen Gross 
436494ed399SJuergen Gross 	spin_lock_irqsave(&info->lock, flags);
437494ed399SJuergen Gross 	switch (typeReq) {
438494ed399SJuergen Gross 	case ClearHubFeature:
439494ed399SJuergen Gross 		/* ignore this request */
440494ed399SJuergen Gross 		break;
441494ed399SJuergen Gross 	case ClearPortFeature:
442494ed399SJuergen Gross 		if (!wIndex || wIndex > ports)
443494ed399SJuergen Gross 			goto error;
444494ed399SJuergen Gross 
445494ed399SJuergen Gross 		switch (wValue) {
446494ed399SJuergen Gross 		case USB_PORT_FEAT_SUSPEND:
447494ed399SJuergen Gross 			xenhcd_rhport_resume(info, wIndex);
448494ed399SJuergen Gross 			break;
449494ed399SJuergen Gross 		case USB_PORT_FEAT_POWER:
450494ed399SJuergen Gross 			xenhcd_rhport_power_off(info, wIndex);
451494ed399SJuergen Gross 			break;
452494ed399SJuergen Gross 		case USB_PORT_FEAT_ENABLE:
453494ed399SJuergen Gross 			xenhcd_rhport_disable(info, wIndex);
454494ed399SJuergen Gross 			break;
455494ed399SJuergen Gross 		case USB_PORT_FEAT_C_CONNECTION:
456494ed399SJuergen Gross 			info->ports[wIndex - 1].c_connection = false;
457494ed399SJuergen Gross 			fallthrough;
458494ed399SJuergen Gross 		default:
459494ed399SJuergen Gross 			info->ports[wIndex - 1].status &= ~(1 << wValue);
460494ed399SJuergen Gross 			break;
461494ed399SJuergen Gross 		}
462494ed399SJuergen Gross 		break;
463494ed399SJuergen Gross 	case GetHubDescriptor:
464494ed399SJuergen Gross 		xenhcd_hub_descriptor(info, (struct usb_hub_descriptor *)buf);
465494ed399SJuergen Gross 		break;
466494ed399SJuergen Gross 	case GetHubStatus:
467494ed399SJuergen Gross 		/* always local power supply good and no over-current exists. */
468494ed399SJuergen Gross 		*(__le32 *)buf = cpu_to_le32(0);
469494ed399SJuergen Gross 		break;
470494ed399SJuergen Gross 	case GetPortStatus:
471494ed399SJuergen Gross 		if (!wIndex || wIndex > ports)
472494ed399SJuergen Gross 			goto error;
473494ed399SJuergen Gross 
474494ed399SJuergen Gross 		wIndex--;
475494ed399SJuergen Gross 
476494ed399SJuergen Gross 		/* resume completion */
477494ed399SJuergen Gross 		if (info->ports[wIndex].resuming &&
478494ed399SJuergen Gross 		    time_after_eq(jiffies, info->ports[wIndex].timeout)) {
479494ed399SJuergen Gross 			info->ports[wIndex].status |=
480494ed399SJuergen Gross 				USB_PORT_STAT_C_SUSPEND << 16;
481494ed399SJuergen Gross 			info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND;
482494ed399SJuergen Gross 		}
483494ed399SJuergen Gross 
484494ed399SJuergen Gross 		/* reset completion */
485494ed399SJuergen Gross 		if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 &&
486494ed399SJuergen Gross 		    time_after_eq(jiffies, info->ports[wIndex].timeout)) {
487494ed399SJuergen Gross 			info->ports[wIndex].status |=
488494ed399SJuergen Gross 				USB_PORT_STAT_C_RESET << 16;
489494ed399SJuergen Gross 			info->ports[wIndex].status &= ~USB_PORT_STAT_RESET;
490494ed399SJuergen Gross 
491494ed399SJuergen Gross 			if (info->devices[wIndex].status !=
492494ed399SJuergen Gross 			    USB_STATE_NOTATTACHED) {
493494ed399SJuergen Gross 				info->ports[wIndex].status |=
494494ed399SJuergen Gross 					USB_PORT_STAT_ENABLE;
495494ed399SJuergen Gross 				info->devices[wIndex].status =
496494ed399SJuergen Gross 					USB_STATE_DEFAULT;
497494ed399SJuergen Gross 			}
498494ed399SJuergen Gross 
499494ed399SJuergen Gross 			switch (info->devices[wIndex].speed) {
500494ed399SJuergen Gross 			case XENUSB_SPEED_LOW:
501494ed399SJuergen Gross 				info->ports[wIndex].status |=
502494ed399SJuergen Gross 					USB_PORT_STAT_LOW_SPEED;
503494ed399SJuergen Gross 				break;
504494ed399SJuergen Gross 			case XENUSB_SPEED_HIGH:
505494ed399SJuergen Gross 				info->ports[wIndex].status |=
506494ed399SJuergen Gross 					USB_PORT_STAT_HIGH_SPEED;
507494ed399SJuergen Gross 				break;
508494ed399SJuergen Gross 			default:
509494ed399SJuergen Gross 				break;
510494ed399SJuergen Gross 			}
511494ed399SJuergen Gross 		}
512494ed399SJuergen Gross 
513494ed399SJuergen Gross 		*(__le32 *)buf = cpu_to_le32(info->ports[wIndex].status);
514494ed399SJuergen Gross 		break;
515494ed399SJuergen Gross 	case SetPortFeature:
516494ed399SJuergen Gross 		if (!wIndex || wIndex > ports)
517494ed399SJuergen Gross 			goto error;
518494ed399SJuergen Gross 
519494ed399SJuergen Gross 		switch (wValue) {
520494ed399SJuergen Gross 		case USB_PORT_FEAT_POWER:
521494ed399SJuergen Gross 			xenhcd_rhport_power_on(info, wIndex);
522494ed399SJuergen Gross 			break;
523494ed399SJuergen Gross 		case USB_PORT_FEAT_RESET:
524494ed399SJuergen Gross 			xenhcd_rhport_reset(info, wIndex);
525494ed399SJuergen Gross 			break;
526494ed399SJuergen Gross 		case USB_PORT_FEAT_SUSPEND:
527494ed399SJuergen Gross 			xenhcd_rhport_suspend(info, wIndex);
528494ed399SJuergen Gross 			break;
529494ed399SJuergen Gross 		default:
530494ed399SJuergen Gross 			if (info->ports[wIndex-1].status & USB_PORT_STAT_POWER)
531494ed399SJuergen Gross 				info->ports[wIndex-1].status |= (1 << wValue);
532494ed399SJuergen Gross 		}
533494ed399SJuergen Gross 		break;
534494ed399SJuergen Gross 
535494ed399SJuergen Gross 	case SetHubFeature:
536494ed399SJuergen Gross 		/* not supported */
537494ed399SJuergen Gross 	default:
538494ed399SJuergen Gross error:
539494ed399SJuergen Gross 		ret = -EPIPE;
540494ed399SJuergen Gross 	}
541494ed399SJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
542494ed399SJuergen Gross 
543494ed399SJuergen Gross 	/* check status for each port */
544494ed399SJuergen Gross 	for (i = 0; i < ports; i++) {
545494ed399SJuergen Gross 		if (info->ports[i].status & PORT_C_MASK)
546494ed399SJuergen Gross 			changed = 1;
547494ed399SJuergen Gross 	}
548494ed399SJuergen Gross 	if (changed)
549494ed399SJuergen Gross 		usb_hcd_poll_rh_status(hcd);
550494ed399SJuergen Gross 
551494ed399SJuergen Gross 	return ret;
552494ed399SJuergen Gross }
553494ed399SJuergen Gross 
xenhcd_free_urb_priv(struct urb_priv * urbp)554494ed399SJuergen Gross static void xenhcd_free_urb_priv(struct urb_priv *urbp)
555494ed399SJuergen Gross {
556494ed399SJuergen Gross 	urbp->urb->hcpriv = NULL;
557494ed399SJuergen Gross 	kmem_cache_free(xenhcd_urbp_cachep, urbp);
558494ed399SJuergen Gross }
559494ed399SJuergen Gross 
xenhcd_get_id_from_freelist(struct xenhcd_info * info)560494ed399SJuergen Gross static inline unsigned int xenhcd_get_id_from_freelist(struct xenhcd_info *info)
561494ed399SJuergen Gross {
562494ed399SJuergen Gross 	unsigned int free;
563494ed399SJuergen Gross 
564494ed399SJuergen Gross 	free = info->shadow_free;
565494ed399SJuergen Gross 	info->shadow_free = info->shadow[free].req.id;
566494ed399SJuergen Gross 	info->shadow[free].req.id = 0x0fff; /* debug */
567494ed399SJuergen Gross 	return free;
568494ed399SJuergen Gross }
569494ed399SJuergen Gross 
xenhcd_add_id_to_freelist(struct xenhcd_info * info,unsigned int id)570494ed399SJuergen Gross static inline void xenhcd_add_id_to_freelist(struct xenhcd_info *info,
571494ed399SJuergen Gross 					     unsigned int id)
572494ed399SJuergen Gross {
573494ed399SJuergen Gross 	info->shadow[id].req.id	= info->shadow_free;
574494ed399SJuergen Gross 	info->shadow[id].urb = NULL;
575494ed399SJuergen Gross 	info->shadow_free = id;
576494ed399SJuergen Gross }
577494ed399SJuergen Gross 
xenhcd_count_pages(void * addr,int length)578494ed399SJuergen Gross static inline int xenhcd_count_pages(void *addr, int length)
579494ed399SJuergen Gross {
580494ed399SJuergen Gross 	unsigned long vaddr = (unsigned long)addr;
581494ed399SJuergen Gross 
582494ed399SJuergen Gross 	return PFN_UP(vaddr + length) - PFN_DOWN(vaddr);
583494ed399SJuergen Gross }
584494ed399SJuergen Gross 
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)585494ed399SJuergen Gross static void xenhcd_gnttab_map(struct xenhcd_info *info, void *addr, int length,
586494ed399SJuergen Gross 			      grant_ref_t *gref_head,
587494ed399SJuergen Gross 			      struct xenusb_request_segment *seg,
588494ed399SJuergen Gross 			      int nr_pages, int flags)
589494ed399SJuergen Gross {
590494ed399SJuergen Gross 	grant_ref_t ref;
591494ed399SJuergen Gross 	unsigned int offset;
592494ed399SJuergen Gross 	unsigned int len = length;
593494ed399SJuergen Gross 	unsigned int bytes;
594494ed399SJuergen Gross 	int i;
595494ed399SJuergen Gross 
596494ed399SJuergen Gross 	for (i = 0; i < nr_pages; i++) {
597494ed399SJuergen Gross 		offset = offset_in_page(addr);
598494ed399SJuergen Gross 
599494ed399SJuergen Gross 		bytes = PAGE_SIZE - offset;
600494ed399SJuergen Gross 		if (bytes > len)
601494ed399SJuergen Gross 			bytes = len;
602494ed399SJuergen Gross 
603494ed399SJuergen Gross 		ref = gnttab_claim_grant_reference(gref_head);
604494ed399SJuergen Gross 		gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,
605cee03ca3SJuergen Gross 						virt_to_gfn(addr), flags);
606494ed399SJuergen Gross 		seg[i].gref = ref;
607494ed399SJuergen Gross 		seg[i].offset = (__u16)offset;
608494ed399SJuergen Gross 		seg[i].length = (__u16)bytes;
609494ed399SJuergen Gross 
610494ed399SJuergen Gross 		addr += bytes;
611494ed399SJuergen Gross 		len -= bytes;
612494ed399SJuergen Gross 	}
613494ed399SJuergen Gross }
614494ed399SJuergen Gross 
xenhcd_pipe_urb_to_xenusb(__u32 urb_pipe,__u8 port)615494ed399SJuergen Gross static __u32 xenhcd_pipe_urb_to_xenusb(__u32 urb_pipe, __u8 port)
616494ed399SJuergen Gross {
617494ed399SJuergen Gross 	static __u32 pipe;
618494ed399SJuergen Gross 
619494ed399SJuergen Gross 	pipe = usb_pipedevice(urb_pipe) << XENUSB_PIPE_DEV_SHIFT;
620494ed399SJuergen Gross 	pipe |= usb_pipeendpoint(urb_pipe) << XENUSB_PIPE_EP_SHIFT;
621494ed399SJuergen Gross 	if (usb_pipein(urb_pipe))
622494ed399SJuergen Gross 		pipe |= XENUSB_PIPE_DIR;
623494ed399SJuergen Gross 	switch (usb_pipetype(urb_pipe)) {
624494ed399SJuergen Gross 	case PIPE_ISOCHRONOUS:
625494ed399SJuergen Gross 		pipe |= XENUSB_PIPE_TYPE_ISOC << XENUSB_PIPE_TYPE_SHIFT;
626494ed399SJuergen Gross 		break;
627494ed399SJuergen Gross 	case PIPE_INTERRUPT:
628494ed399SJuergen Gross 		pipe |= XENUSB_PIPE_TYPE_INT << XENUSB_PIPE_TYPE_SHIFT;
629494ed399SJuergen Gross 		break;
630494ed399SJuergen Gross 	case PIPE_CONTROL:
631494ed399SJuergen Gross 		pipe |= XENUSB_PIPE_TYPE_CTRL << XENUSB_PIPE_TYPE_SHIFT;
632494ed399SJuergen Gross 		break;
633494ed399SJuergen Gross 	case PIPE_BULK:
634494ed399SJuergen Gross 		pipe |= XENUSB_PIPE_TYPE_BULK << XENUSB_PIPE_TYPE_SHIFT;
635494ed399SJuergen Gross 		break;
636494ed399SJuergen Gross 	}
637494ed399SJuergen Gross 	pipe = xenusb_setportnum_pipe(pipe, port);
638494ed399SJuergen Gross 
639494ed399SJuergen Gross 	return pipe;
640494ed399SJuergen Gross }
641494ed399SJuergen Gross 
xenhcd_map_urb_for_request(struct xenhcd_info * info,struct urb * urb,struct xenusb_urb_request * req)642494ed399SJuergen Gross static int xenhcd_map_urb_for_request(struct xenhcd_info *info, struct urb *urb,
643494ed399SJuergen Gross 				      struct xenusb_urb_request *req)
644494ed399SJuergen Gross {
645494ed399SJuergen Gross 	grant_ref_t gref_head;
646494ed399SJuergen Gross 	int nr_buff_pages = 0;
647494ed399SJuergen Gross 	int nr_isodesc_pages = 0;
648494ed399SJuergen Gross 	int nr_grants = 0;
649494ed399SJuergen Gross 
650494ed399SJuergen Gross 	if (urb->transfer_buffer_length) {
651494ed399SJuergen Gross 		nr_buff_pages = xenhcd_count_pages(urb->transfer_buffer,
652494ed399SJuergen Gross 						urb->transfer_buffer_length);
653494ed399SJuergen Gross 
654494ed399SJuergen Gross 		if (usb_pipeisoc(urb->pipe))
655494ed399SJuergen Gross 			nr_isodesc_pages = xenhcd_count_pages(
656494ed399SJuergen Gross 				&urb->iso_frame_desc[0],
657494ed399SJuergen Gross 				sizeof(struct usb_iso_packet_descriptor) *
658494ed399SJuergen Gross 				urb->number_of_packets);
659494ed399SJuergen Gross 
660494ed399SJuergen Gross 		nr_grants = nr_buff_pages + nr_isodesc_pages;
661494ed399SJuergen Gross 		if (nr_grants > XENUSB_MAX_SEGMENTS_PER_REQUEST) {
662494ed399SJuergen Gross 			pr_err("xenhcd: error: %d grants\n", nr_grants);
663494ed399SJuergen Gross 			return -E2BIG;
664494ed399SJuergen Gross 		}
665494ed399SJuergen Gross 
666494ed399SJuergen Gross 		if (gnttab_alloc_grant_references(nr_grants, &gref_head)) {
667494ed399SJuergen Gross 			pr_err("xenhcd: gnttab_alloc_grant_references() error\n");
668494ed399SJuergen Gross 			return -ENOMEM;
669494ed399SJuergen Gross 		}
670494ed399SJuergen Gross 
671494ed399SJuergen Gross 		xenhcd_gnttab_map(info, urb->transfer_buffer,
672494ed399SJuergen Gross 				  urb->transfer_buffer_length, &gref_head,
673494ed399SJuergen Gross 				  &req->seg[0], nr_buff_pages,
674494ed399SJuergen Gross 				  usb_pipein(urb->pipe) ? 0 : GTF_readonly);
675494ed399SJuergen Gross 	}
676494ed399SJuergen Gross 
677494ed399SJuergen Gross 	req->pipe = xenhcd_pipe_urb_to_xenusb(urb->pipe, urb->dev->portnum);
678494ed399SJuergen Gross 	req->transfer_flags = 0;
679494ed399SJuergen Gross 	if (urb->transfer_flags & URB_SHORT_NOT_OK)
680494ed399SJuergen Gross 		req->transfer_flags |= XENUSB_SHORT_NOT_OK;
681494ed399SJuergen Gross 	req->buffer_length = urb->transfer_buffer_length;
682494ed399SJuergen Gross 	req->nr_buffer_segs = nr_buff_pages;
683494ed399SJuergen Gross 
684494ed399SJuergen Gross 	switch (usb_pipetype(urb->pipe)) {
685494ed399SJuergen Gross 	case PIPE_ISOCHRONOUS:
686494ed399SJuergen Gross 		req->u.isoc.interval = urb->interval;
687494ed399SJuergen Gross 		req->u.isoc.start_frame = urb->start_frame;
688494ed399SJuergen Gross 		req->u.isoc.number_of_packets = urb->number_of_packets;
689494ed399SJuergen Gross 		req->u.isoc.nr_frame_desc_segs = nr_isodesc_pages;
690494ed399SJuergen Gross 
691494ed399SJuergen Gross 		xenhcd_gnttab_map(info, &urb->iso_frame_desc[0],
692494ed399SJuergen Gross 				  sizeof(struct usb_iso_packet_descriptor) *
693494ed399SJuergen Gross 				  urb->number_of_packets,
694494ed399SJuergen Gross 				  &gref_head, &req->seg[nr_buff_pages],
695494ed399SJuergen Gross 				  nr_isodesc_pages, 0);
696494ed399SJuergen Gross 		break;
697494ed399SJuergen Gross 	case PIPE_INTERRUPT:
698494ed399SJuergen Gross 		req->u.intr.interval = urb->interval;
699494ed399SJuergen Gross 		break;
700494ed399SJuergen Gross 	case PIPE_CONTROL:
701494ed399SJuergen Gross 		if (urb->setup_packet)
702494ed399SJuergen Gross 			memcpy(req->u.ctrl, urb->setup_packet, 8);
703494ed399SJuergen Gross 		break;
704494ed399SJuergen Gross 	case PIPE_BULK:
705494ed399SJuergen Gross 		break;
706494ed399SJuergen Gross 	default:
707494ed399SJuergen Gross 		break;
708494ed399SJuergen Gross 	}
709494ed399SJuergen Gross 
710494ed399SJuergen Gross 	if (nr_grants)
711494ed399SJuergen Gross 		gnttab_free_grant_references(gref_head);
712494ed399SJuergen Gross 
713494ed399SJuergen Gross 	return 0;
714494ed399SJuergen Gross }
715494ed399SJuergen Gross 
xenhcd_gnttab_done(struct xenhcd_info * info,unsigned int id)716cd7bcfabSJuergen Gross static void xenhcd_gnttab_done(struct xenhcd_info *info, unsigned int id)
717494ed399SJuergen Gross {
718cd7bcfabSJuergen Gross 	struct usb_shadow *shadow = info->shadow + id;
719494ed399SJuergen Gross 	int nr_segs = 0;
720494ed399SJuergen Gross 	int i;
721494ed399SJuergen Gross 
722aff477cbSJuergen Gross 	if (!shadow->in_flight) {
723aff477cbSJuergen Gross 		xenhcd_set_error(info, "Illegal request id");
724aff477cbSJuergen Gross 		return;
725aff477cbSJuergen Gross 	}
726aff477cbSJuergen Gross 	shadow->in_flight = false;
727aff477cbSJuergen Gross 
728494ed399SJuergen Gross 	nr_segs = shadow->req.nr_buffer_segs;
729494ed399SJuergen Gross 
730494ed399SJuergen Gross 	if (xenusb_pipeisoc(shadow->req.pipe))
731494ed399SJuergen Gross 		nr_segs += shadow->req.u.isoc.nr_frame_desc_segs;
732494ed399SJuergen Gross 
733cd7bcfabSJuergen Gross 	for (i = 0; i < nr_segs; i++) {
734cd7bcfabSJuergen Gross 		if (!gnttab_try_end_foreign_access(shadow->req.seg[i].gref))
735cd7bcfabSJuergen Gross 			xenhcd_set_error(info, "backend didn't release grant");
736cd7bcfabSJuergen Gross 	}
737494ed399SJuergen Gross 
738494ed399SJuergen Gross 	shadow->req.nr_buffer_segs = 0;
739494ed399SJuergen Gross 	shadow->req.u.isoc.nr_frame_desc_segs = 0;
740494ed399SJuergen Gross }
741494ed399SJuergen Gross 
xenhcd_translate_status(int status)742494ed399SJuergen Gross static int xenhcd_translate_status(int status)
743494ed399SJuergen Gross {
744494ed399SJuergen Gross 	switch (status) {
745494ed399SJuergen Gross 	case XENUSB_STATUS_OK:
746494ed399SJuergen Gross 		return 0;
747494ed399SJuergen Gross 	case XENUSB_STATUS_NODEV:
748494ed399SJuergen Gross 		return -ENODEV;
749494ed399SJuergen Gross 	case XENUSB_STATUS_INVAL:
750494ed399SJuergen Gross 		return -EINVAL;
751494ed399SJuergen Gross 	case XENUSB_STATUS_STALL:
752494ed399SJuergen Gross 		return -EPIPE;
753494ed399SJuergen Gross 	case XENUSB_STATUS_IOERROR:
754494ed399SJuergen Gross 		return -EPROTO;
755494ed399SJuergen Gross 	case XENUSB_STATUS_BABBLE:
756494ed399SJuergen Gross 		return -EOVERFLOW;
757494ed399SJuergen Gross 	default:
758494ed399SJuergen Gross 		return -ESHUTDOWN;
759494ed399SJuergen Gross 	}
760494ed399SJuergen Gross }
761494ed399SJuergen Gross 
xenhcd_giveback_urb(struct xenhcd_info * info,struct urb * urb,int status)762494ed399SJuergen Gross static void xenhcd_giveback_urb(struct xenhcd_info *info, struct urb *urb,
763494ed399SJuergen Gross 				int status)
764494ed399SJuergen Gross {
765494ed399SJuergen Gross 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
766494ed399SJuergen Gross 	int priv_status = urbp->status;
767494ed399SJuergen Gross 
768494ed399SJuergen Gross 	list_del_init(&urbp->list);
769494ed399SJuergen Gross 	xenhcd_free_urb_priv(urbp);
770494ed399SJuergen Gross 
771494ed399SJuergen Gross 	if (urb->status == -EINPROGRESS)
772494ed399SJuergen Gross 		urb->status = xenhcd_translate_status(status);
773494ed399SJuergen Gross 
774494ed399SJuergen Gross 	spin_unlock(&info->lock);
775494ed399SJuergen Gross 	usb_hcd_giveback_urb(xenhcd_info_to_hcd(info), urb,
776494ed399SJuergen Gross 			     priv_status <= 0 ? priv_status : urb->status);
777494ed399SJuergen Gross 	spin_lock(&info->lock);
778494ed399SJuergen Gross }
779494ed399SJuergen Gross 
xenhcd_do_request(struct xenhcd_info * info,struct urb_priv * urbp)780494ed399SJuergen Gross static int xenhcd_do_request(struct xenhcd_info *info, struct urb_priv *urbp)
781494ed399SJuergen Gross {
782494ed399SJuergen Gross 	struct xenusb_urb_request *req;
783494ed399SJuergen Gross 	struct urb *urb = urbp->urb;
784494ed399SJuergen Gross 	unsigned int id;
785494ed399SJuergen Gross 	int notify;
786494ed399SJuergen Gross 	int ret;
787494ed399SJuergen Gross 
788494ed399SJuergen Gross 	id = xenhcd_get_id_from_freelist(info);
789494ed399SJuergen Gross 	req = &info->shadow[id].req;
790494ed399SJuergen Gross 	req->id = id;
791494ed399SJuergen Gross 
792494ed399SJuergen Gross 	if (unlikely(urbp->unlinked)) {
793494ed399SJuergen Gross 		req->u.unlink.unlink_id = urbp->req_id;
794494ed399SJuergen Gross 		req->pipe = xenusb_setunlink_pipe(xenhcd_pipe_urb_to_xenusb(
795494ed399SJuergen Gross 						 urb->pipe, urb->dev->portnum));
796494ed399SJuergen Gross 		urbp->unlink_req_id = id;
797494ed399SJuergen Gross 	} else {
798494ed399SJuergen Gross 		ret = xenhcd_map_urb_for_request(info, urb, req);
799494ed399SJuergen Gross 		if (ret) {
800494ed399SJuergen Gross 			xenhcd_add_id_to_freelist(info, id);
801494ed399SJuergen Gross 			return ret;
802494ed399SJuergen Gross 		}
803494ed399SJuergen Gross 		urbp->req_id = id;
804494ed399SJuergen Gross 	}
805494ed399SJuergen Gross 
806494ed399SJuergen Gross 	req = RING_GET_REQUEST(&info->urb_ring, info->urb_ring.req_prod_pvt);
807494ed399SJuergen Gross 	*req = info->shadow[id].req;
808494ed399SJuergen Gross 
809494ed399SJuergen Gross 	info->urb_ring.req_prod_pvt++;
810494ed399SJuergen Gross 	info->shadow[id].urb = urb;
811aff477cbSJuergen Gross 	info->shadow[id].in_flight = true;
812494ed399SJuergen Gross 
813494ed399SJuergen Gross 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->urb_ring, notify);
814494ed399SJuergen Gross 	if (notify)
815494ed399SJuergen Gross 		notify_remote_via_irq(info->irq);
816494ed399SJuergen Gross 
817494ed399SJuergen Gross 	return 0;
818494ed399SJuergen Gross }
819494ed399SJuergen Gross 
xenhcd_kick_pending_urbs(struct xenhcd_info * info)820494ed399SJuergen Gross static void xenhcd_kick_pending_urbs(struct xenhcd_info *info)
821494ed399SJuergen Gross {
822494ed399SJuergen Gross 	struct urb_priv *urbp;
823494ed399SJuergen Gross 
824494ed399SJuergen Gross 	while (!list_empty(&info->pending_submit_list)) {
825494ed399SJuergen Gross 		if (RING_FULL(&info->urb_ring)) {
826494ed399SJuergen Gross 			xenhcd_timer_action(info, TIMER_RING_WATCHDOG);
827494ed399SJuergen Gross 			return;
828494ed399SJuergen Gross 		}
829494ed399SJuergen Gross 
830494ed399SJuergen Gross 		urbp = list_entry(info->pending_submit_list.next,
831494ed399SJuergen Gross 				  struct urb_priv, list);
832494ed399SJuergen Gross 		if (!xenhcd_do_request(info, urbp))
833494ed399SJuergen Gross 			list_move_tail(&urbp->list, &info->in_progress_list);
834494ed399SJuergen Gross 		else
835494ed399SJuergen Gross 			xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN);
836494ed399SJuergen Gross 	}
837494ed399SJuergen Gross 	xenhcd_timer_action_done(info, TIMER_SCAN_PENDING_URBS);
838494ed399SJuergen Gross }
839494ed399SJuergen Gross 
840494ed399SJuergen Gross /*
841494ed399SJuergen Gross  * caller must lock info->lock
842494ed399SJuergen Gross  */
xenhcd_cancel_all_enqueued_urbs(struct xenhcd_info * info)843494ed399SJuergen Gross static void xenhcd_cancel_all_enqueued_urbs(struct xenhcd_info *info)
844494ed399SJuergen Gross {
845494ed399SJuergen Gross 	struct urb_priv *urbp, *tmp;
846494ed399SJuergen Gross 	int req_id;
847494ed399SJuergen Gross 
848494ed399SJuergen Gross 	list_for_each_entry_safe(urbp, tmp, &info->in_progress_list, list) {
849494ed399SJuergen Gross 		req_id = urbp->req_id;
850494ed399SJuergen Gross 		if (!urbp->unlinked) {
851cd7bcfabSJuergen Gross 			xenhcd_gnttab_done(info, req_id);
852cd7bcfabSJuergen Gross 			if (info->error)
853cd7bcfabSJuergen Gross 				return;
854494ed399SJuergen Gross 			if (urbp->urb->status == -EINPROGRESS)
855494ed399SJuergen Gross 				/* not dequeued */
856494ed399SJuergen Gross 				xenhcd_giveback_urb(info, urbp->urb,
857494ed399SJuergen Gross 						    -ESHUTDOWN);
858494ed399SJuergen Gross 			else	/* dequeued */
859494ed399SJuergen Gross 				xenhcd_giveback_urb(info, urbp->urb,
860494ed399SJuergen Gross 						    urbp->urb->status);
861494ed399SJuergen Gross 		}
862494ed399SJuergen Gross 		info->shadow[req_id].urb = NULL;
863494ed399SJuergen Gross 	}
864494ed399SJuergen Gross 
865494ed399SJuergen Gross 	list_for_each_entry_safe(urbp, tmp, &info->pending_submit_list, list)
866494ed399SJuergen Gross 		xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN);
867494ed399SJuergen Gross }
868494ed399SJuergen Gross 
869494ed399SJuergen Gross /*
870494ed399SJuergen Gross  * caller must lock info->lock
871494ed399SJuergen Gross  */
xenhcd_giveback_unlinked_urbs(struct xenhcd_info * info)872494ed399SJuergen Gross static void xenhcd_giveback_unlinked_urbs(struct xenhcd_info *info)
873494ed399SJuergen Gross {
874494ed399SJuergen Gross 	struct urb_priv *urbp, *tmp;
875494ed399SJuergen Gross 
876494ed399SJuergen Gross 	list_for_each_entry_safe(urbp, tmp, &info->giveback_waiting_list, list)
877494ed399SJuergen Gross 		xenhcd_giveback_urb(info, urbp->urb, urbp->urb->status);
878494ed399SJuergen Gross }
879494ed399SJuergen Gross 
xenhcd_submit_urb(struct xenhcd_info * info,struct urb_priv * urbp)880494ed399SJuergen Gross static int xenhcd_submit_urb(struct xenhcd_info *info, struct urb_priv *urbp)
881494ed399SJuergen Gross {
882494ed399SJuergen Gross 	int ret;
883494ed399SJuergen Gross 
884494ed399SJuergen Gross 	if (RING_FULL(&info->urb_ring)) {
885494ed399SJuergen Gross 		list_add_tail(&urbp->list, &info->pending_submit_list);
886494ed399SJuergen Gross 		xenhcd_timer_action(info, TIMER_RING_WATCHDOG);
887494ed399SJuergen Gross 		return 0;
888494ed399SJuergen Gross 	}
889494ed399SJuergen Gross 
890494ed399SJuergen Gross 	if (!list_empty(&info->pending_submit_list)) {
891494ed399SJuergen Gross 		list_add_tail(&urbp->list, &info->pending_submit_list);
892494ed399SJuergen Gross 		xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS);
893494ed399SJuergen Gross 		return 0;
894494ed399SJuergen Gross 	}
895494ed399SJuergen Gross 
896494ed399SJuergen Gross 	ret = xenhcd_do_request(info, urbp);
897494ed399SJuergen Gross 	if (ret == 0)
898494ed399SJuergen Gross 		list_add_tail(&urbp->list, &info->in_progress_list);
899494ed399SJuergen Gross 
900494ed399SJuergen Gross 	return ret;
901494ed399SJuergen Gross }
902494ed399SJuergen Gross 
xenhcd_unlink_urb(struct xenhcd_info * info,struct urb_priv * urbp)903494ed399SJuergen Gross static int xenhcd_unlink_urb(struct xenhcd_info *info, struct urb_priv *urbp)
904494ed399SJuergen Gross {
905494ed399SJuergen Gross 	int ret;
906494ed399SJuergen Gross 
907494ed399SJuergen Gross 	/* already unlinked? */
908494ed399SJuergen Gross 	if (urbp->unlinked)
909494ed399SJuergen Gross 		return -EBUSY;
910494ed399SJuergen Gross 
911494ed399SJuergen Gross 	urbp->unlinked = true;
912494ed399SJuergen Gross 
913494ed399SJuergen Gross 	/* the urb is still in pending_submit queue */
914494ed399SJuergen Gross 	if (urbp->req_id == ~0) {
915494ed399SJuergen Gross 		list_move_tail(&urbp->list, &info->giveback_waiting_list);
916494ed399SJuergen Gross 		xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS);
917494ed399SJuergen Gross 		return 0;
918494ed399SJuergen Gross 	}
919494ed399SJuergen Gross 
920494ed399SJuergen Gross 	/* send unlink request to backend */
921494ed399SJuergen Gross 	if (RING_FULL(&info->urb_ring)) {
922494ed399SJuergen Gross 		list_move_tail(&urbp->list, &info->pending_unlink_list);
923494ed399SJuergen Gross 		xenhcd_timer_action(info, TIMER_RING_WATCHDOG);
924494ed399SJuergen Gross 		return 0;
925494ed399SJuergen Gross 	}
926494ed399SJuergen Gross 
927494ed399SJuergen Gross 	if (!list_empty(&info->pending_unlink_list)) {
928494ed399SJuergen Gross 		list_move_tail(&urbp->list, &info->pending_unlink_list);
929494ed399SJuergen Gross 		xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS);
930494ed399SJuergen Gross 		return 0;
931494ed399SJuergen Gross 	}
932494ed399SJuergen Gross 
933494ed399SJuergen Gross 	ret = xenhcd_do_request(info, urbp);
934494ed399SJuergen Gross 	if (ret == 0)
935494ed399SJuergen Gross 		list_move_tail(&urbp->list, &info->in_progress_list);
936494ed399SJuergen Gross 
937494ed399SJuergen Gross 	return ret;
938494ed399SJuergen Gross }
939494ed399SJuergen Gross 
xenhcd_res_to_urb(struct xenhcd_info * info,struct xenusb_urb_response * res,struct urb * urb)940aff477cbSJuergen Gross static void xenhcd_res_to_urb(struct xenhcd_info *info,
941aff477cbSJuergen Gross 			      struct xenusb_urb_response *res, struct urb *urb)
942aff477cbSJuergen Gross {
943aff477cbSJuergen Gross 	if (unlikely(!urb))
944aff477cbSJuergen Gross 		return;
945aff477cbSJuergen Gross 
946aff477cbSJuergen Gross 	if (res->actual_length > urb->transfer_buffer_length)
947aff477cbSJuergen Gross 		urb->actual_length = urb->transfer_buffer_length;
948aff477cbSJuergen Gross 	else if (res->actual_length < 0)
949aff477cbSJuergen Gross 		urb->actual_length = 0;
950aff477cbSJuergen Gross 	else
951aff477cbSJuergen Gross 		urb->actual_length = res->actual_length;
952aff477cbSJuergen Gross 	urb->error_count = res->error_count;
953aff477cbSJuergen Gross 	urb->start_frame = res->start_frame;
954aff477cbSJuergen Gross 	xenhcd_giveback_urb(info, urb, res->status);
955aff477cbSJuergen Gross }
956aff477cbSJuergen Gross 
xenhcd_urb_request_done(struct xenhcd_info * info,unsigned int * eoiflag)957aff477cbSJuergen Gross static int xenhcd_urb_request_done(struct xenhcd_info *info,
958aff477cbSJuergen Gross 				   unsigned int *eoiflag)
959494ed399SJuergen Gross {
960494ed399SJuergen Gross 	struct xenusb_urb_response res;
961494ed399SJuergen Gross 	RING_IDX i, rp;
962494ed399SJuergen Gross 	__u16 id;
963494ed399SJuergen Gross 	int more_to_do = 0;
964494ed399SJuergen Gross 	unsigned long flags;
965494ed399SJuergen Gross 
966494ed399SJuergen Gross 	spin_lock_irqsave(&info->lock, flags);
967494ed399SJuergen Gross 
968494ed399SJuergen Gross 	rp = info->urb_ring.sring->rsp_prod;
969494ed399SJuergen Gross 	if (RING_RESPONSE_PROD_OVERFLOW(&info->urb_ring, rp)) {
970494ed399SJuergen Gross 		xenhcd_set_error(info, "Illegal index on urb-ring");
971cd7bcfabSJuergen Gross 		goto err;
972494ed399SJuergen Gross 	}
973494ed399SJuergen Gross 	rmb(); /* ensure we see queued responses up to "rp" */
974494ed399SJuergen Gross 
975494ed399SJuergen Gross 	for (i = info->urb_ring.rsp_cons; i != rp; i++) {
976494ed399SJuergen Gross 		RING_COPY_RESPONSE(&info->urb_ring, i, &res);
977494ed399SJuergen Gross 		id = res.id;
978494ed399SJuergen Gross 		if (id >= XENUSB_URB_RING_SIZE) {
979494ed399SJuergen Gross 			xenhcd_set_error(info, "Illegal data on urb-ring");
980cd7bcfabSJuergen Gross 			goto err;
981494ed399SJuergen Gross 		}
982494ed399SJuergen Gross 
983494ed399SJuergen Gross 		if (likely(xenusb_pipesubmit(info->shadow[id].req.pipe))) {
984cd7bcfabSJuergen Gross 			xenhcd_gnttab_done(info, id);
985cd7bcfabSJuergen Gross 			if (info->error)
986cd7bcfabSJuergen Gross 				goto err;
987aff477cbSJuergen Gross 			xenhcd_res_to_urb(info, &res, info->shadow[id].urb);
988494ed399SJuergen Gross 		}
989494ed399SJuergen Gross 
990494ed399SJuergen Gross 		xenhcd_add_id_to_freelist(info, id);
991aff477cbSJuergen Gross 
992aff477cbSJuergen Gross 		*eoiflag = 0;
993494ed399SJuergen Gross 	}
994494ed399SJuergen Gross 	info->urb_ring.rsp_cons = i;
995494ed399SJuergen Gross 
996494ed399SJuergen Gross 	if (i != info->urb_ring.req_prod_pvt)
997494ed399SJuergen Gross 		RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do);
998494ed399SJuergen Gross 	else
999494ed399SJuergen Gross 		info->urb_ring.sring->rsp_event = i + 1;
1000494ed399SJuergen Gross 
1001494ed399SJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
1002494ed399SJuergen Gross 
1003494ed399SJuergen Gross 	return more_to_do;
1004cd7bcfabSJuergen Gross 
1005cd7bcfabSJuergen Gross  err:
1006cd7bcfabSJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
1007cd7bcfabSJuergen Gross 	return 0;
1008494ed399SJuergen Gross }
1009494ed399SJuergen Gross 
xenhcd_conn_notify(struct xenhcd_info * info,unsigned int * eoiflag)1010aff477cbSJuergen Gross static int xenhcd_conn_notify(struct xenhcd_info *info, unsigned int *eoiflag)
1011494ed399SJuergen Gross {
1012494ed399SJuergen Gross 	struct xenusb_conn_response res;
1013494ed399SJuergen Gross 	struct xenusb_conn_request *req;
1014494ed399SJuergen Gross 	RING_IDX rc, rp;
1015494ed399SJuergen Gross 	__u16 id;
1016494ed399SJuergen Gross 	__u8 portnum, speed;
1017494ed399SJuergen Gross 	int more_to_do = 0;
1018494ed399SJuergen Gross 	int notify;
1019494ed399SJuergen Gross 	int port_changed = 0;
1020494ed399SJuergen Gross 	unsigned long flags;
1021494ed399SJuergen Gross 
1022494ed399SJuergen Gross 	spin_lock_irqsave(&info->lock, flags);
1023494ed399SJuergen Gross 
1024494ed399SJuergen Gross 	rc = info->conn_ring.rsp_cons;
1025494ed399SJuergen Gross 	rp = info->conn_ring.sring->rsp_prod;
1026494ed399SJuergen Gross 	if (RING_RESPONSE_PROD_OVERFLOW(&info->conn_ring, rp)) {
1027494ed399SJuergen Gross 		xenhcd_set_error(info, "Illegal index on conn-ring");
1028a1f79504SYang Yingliang 		spin_unlock_irqrestore(&info->lock, flags);
1029494ed399SJuergen Gross 		return 0;
1030494ed399SJuergen Gross 	}
1031494ed399SJuergen Gross 	rmb(); /* ensure we see queued responses up to "rp" */
1032494ed399SJuergen Gross 
1033494ed399SJuergen Gross 	while (rc != rp) {
1034494ed399SJuergen Gross 		RING_COPY_RESPONSE(&info->conn_ring, rc, &res);
1035494ed399SJuergen Gross 		id = res.id;
1036494ed399SJuergen Gross 		portnum = res.portnum;
1037494ed399SJuergen Gross 		speed = res.speed;
1038494ed399SJuergen Gross 		info->conn_ring.rsp_cons = ++rc;
1039494ed399SJuergen Gross 
1040494ed399SJuergen Gross 		if (xenhcd_rhport_connect(info, portnum, speed)) {
1041494ed399SJuergen Gross 			xenhcd_set_error(info, "Illegal data on conn-ring");
1042a1f79504SYang Yingliang 			spin_unlock_irqrestore(&info->lock, flags);
1043494ed399SJuergen Gross 			return 0;
1044494ed399SJuergen Gross 		}
1045494ed399SJuergen Gross 
1046494ed399SJuergen Gross 		if (info->ports[portnum - 1].c_connection)
1047494ed399SJuergen Gross 			port_changed = 1;
1048494ed399SJuergen Gross 
1049494ed399SJuergen Gross 		barrier();
1050494ed399SJuergen Gross 
1051494ed399SJuergen Gross 		req = RING_GET_REQUEST(&info->conn_ring,
1052494ed399SJuergen Gross 				       info->conn_ring.req_prod_pvt);
1053494ed399SJuergen Gross 		req->id = id;
1054494ed399SJuergen Gross 		info->conn_ring.req_prod_pvt++;
1055aff477cbSJuergen Gross 
1056aff477cbSJuergen Gross 		*eoiflag = 0;
1057494ed399SJuergen Gross 	}
1058494ed399SJuergen Gross 
1059494ed399SJuergen Gross 	if (rc != info->conn_ring.req_prod_pvt)
1060494ed399SJuergen Gross 		RING_FINAL_CHECK_FOR_RESPONSES(&info->conn_ring, more_to_do);
1061494ed399SJuergen Gross 	else
1062494ed399SJuergen Gross 		info->conn_ring.sring->rsp_event = rc + 1;
1063494ed399SJuergen Gross 
1064494ed399SJuergen Gross 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify);
1065494ed399SJuergen Gross 	if (notify)
1066494ed399SJuergen Gross 		notify_remote_via_irq(info->irq);
1067494ed399SJuergen Gross 
1068494ed399SJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
1069494ed399SJuergen Gross 
1070494ed399SJuergen Gross 	if (port_changed)
1071494ed399SJuergen Gross 		usb_hcd_poll_rh_status(xenhcd_info_to_hcd(info));
1072494ed399SJuergen Gross 
1073494ed399SJuergen Gross 	return more_to_do;
1074494ed399SJuergen Gross }
1075494ed399SJuergen Gross 
xenhcd_int(int irq,void * dev_id)1076494ed399SJuergen Gross static irqreturn_t xenhcd_int(int irq, void *dev_id)
1077494ed399SJuergen Gross {
1078494ed399SJuergen Gross 	struct xenhcd_info *info = (struct xenhcd_info *)dev_id;
1079aff477cbSJuergen Gross 	unsigned int eoiflag = XEN_EOI_FLAG_SPURIOUS;
1080494ed399SJuergen Gross 
1081aff477cbSJuergen Gross 	if (unlikely(info->error)) {
1082aff477cbSJuergen Gross 		xen_irq_lateeoi(irq, XEN_EOI_FLAG_SPURIOUS);
1083494ed399SJuergen Gross 		return IRQ_HANDLED;
1084aff477cbSJuergen Gross 	}
1085494ed399SJuergen Gross 
1086aff477cbSJuergen Gross 	while (xenhcd_urb_request_done(info, &eoiflag) |
1087aff477cbSJuergen Gross 	       xenhcd_conn_notify(info, &eoiflag))
1088494ed399SJuergen Gross 		/* Yield point for this unbounded loop. */
1089494ed399SJuergen Gross 		cond_resched();
1090494ed399SJuergen Gross 
1091aff477cbSJuergen Gross 	xen_irq_lateeoi(irq, eoiflag);
1092494ed399SJuergen Gross 	return IRQ_HANDLED;
1093494ed399SJuergen Gross }
1094494ed399SJuergen Gross 
xenhcd_destroy_rings(struct xenhcd_info * info)1095494ed399SJuergen Gross static void xenhcd_destroy_rings(struct xenhcd_info *info)
1096494ed399SJuergen Gross {
1097494ed399SJuergen Gross 	if (info->irq)
1098494ed399SJuergen Gross 		unbind_from_irqhandler(info->irq, info);
1099494ed399SJuergen Gross 	info->irq = 0;
1100494ed399SJuergen Gross 
11012b3daf08SJuergen Gross 	xenbus_teardown_ring((void **)&info->urb_ring.sring, 1,
11022b3daf08SJuergen Gross 			     &info->urb_ring_ref);
11032b3daf08SJuergen Gross 	xenbus_teardown_ring((void **)&info->conn_ring.sring, 1,
11042b3daf08SJuergen Gross 			     &info->conn_ring_ref);
1105494ed399SJuergen Gross }
1106494ed399SJuergen Gross 
xenhcd_setup_rings(struct xenbus_device * dev,struct xenhcd_info * info)1107494ed399SJuergen Gross static int xenhcd_setup_rings(struct xenbus_device *dev,
1108494ed399SJuergen Gross 			      struct xenhcd_info *info)
1109494ed399SJuergen Gross {
1110494ed399SJuergen Gross 	struct xenusb_urb_sring *urb_sring;
1111494ed399SJuergen Gross 	struct xenusb_conn_sring *conn_sring;
1112494ed399SJuergen Gross 	int err;
1113494ed399SJuergen Gross 
1114edd81e7cSJuergen Gross 	info->conn_ring_ref = INVALID_GRANT_REF;
11152b3daf08SJuergen Gross 	err = xenbus_setup_ring(dev, GFP_NOIO | __GFP_HIGH,
11162b3daf08SJuergen Gross 				(void **)&urb_sring, 1, &info->urb_ring_ref);
11172b3daf08SJuergen Gross 	if (err) {
11182b3daf08SJuergen Gross 		xenbus_dev_fatal(dev, err, "allocating urb ring");
11192b3daf08SJuergen Gross 		return err;
1120494ed399SJuergen Gross 	}
11212b3daf08SJuergen Gross 	XEN_FRONT_RING_INIT(&info->urb_ring, urb_sring, PAGE_SIZE);
1122494ed399SJuergen Gross 
11232b3daf08SJuergen Gross 	err = xenbus_setup_ring(dev, GFP_NOIO | __GFP_HIGH,
11242b3daf08SJuergen Gross 				(void **)&conn_sring, 1, &info->conn_ring_ref);
11252b3daf08SJuergen Gross 	if (err) {
11262b3daf08SJuergen Gross 		xenbus_dev_fatal(dev, err, "allocating conn ring");
1127494ed399SJuergen Gross 		goto fail;
1128494ed399SJuergen Gross 	}
11292b3daf08SJuergen Gross 	XEN_FRONT_RING_INIT(&info->conn_ring, conn_sring, PAGE_SIZE);
1130494ed399SJuergen Gross 
1131494ed399SJuergen Gross 	err = xenbus_alloc_evtchn(dev, &info->evtchn);
1132494ed399SJuergen Gross 	if (err) {
1133494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "xenbus_alloc_evtchn");
1134494ed399SJuergen Gross 		goto fail;
1135494ed399SJuergen Gross 	}
1136494ed399SJuergen Gross 
1137aff477cbSJuergen Gross 	err = bind_evtchn_to_irq_lateeoi(info->evtchn);
1138494ed399SJuergen Gross 	if (err <= 0) {
1139aff477cbSJuergen Gross 		xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq_lateeoi");
1140494ed399SJuergen Gross 		goto fail;
1141494ed399SJuergen Gross 	}
1142494ed399SJuergen Gross 
1143494ed399SJuergen Gross 	info->irq = err;
1144494ed399SJuergen Gross 
1145494ed399SJuergen Gross 	err = request_threaded_irq(info->irq, NULL, xenhcd_int,
1146494ed399SJuergen Gross 				   IRQF_ONESHOT, "xenhcd", info);
1147494ed399SJuergen Gross 	if (err) {
1148494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "request_threaded_irq");
1149494ed399SJuergen Gross 		goto free_irq;
1150494ed399SJuergen Gross 	}
1151494ed399SJuergen Gross 
1152494ed399SJuergen Gross 	return 0;
1153494ed399SJuergen Gross 
1154494ed399SJuergen Gross free_irq:
1155494ed399SJuergen Gross 	unbind_from_irqhandler(info->irq, info);
1156494ed399SJuergen Gross fail:
1157494ed399SJuergen Gross 	xenhcd_destroy_rings(info);
1158494ed399SJuergen Gross 	return err;
1159494ed399SJuergen Gross }
1160494ed399SJuergen Gross 
xenhcd_talk_to_backend(struct xenbus_device * dev,struct xenhcd_info * info)1161494ed399SJuergen Gross static int xenhcd_talk_to_backend(struct xenbus_device *dev,
1162494ed399SJuergen Gross 				  struct xenhcd_info *info)
1163494ed399SJuergen Gross {
1164494ed399SJuergen Gross 	const char *message;
1165494ed399SJuergen Gross 	struct xenbus_transaction xbt;
1166494ed399SJuergen Gross 	int err;
1167494ed399SJuergen Gross 
1168494ed399SJuergen Gross 	err = xenhcd_setup_rings(dev, info);
1169494ed399SJuergen Gross 	if (err)
1170494ed399SJuergen Gross 		return err;
1171494ed399SJuergen Gross 
1172494ed399SJuergen Gross again:
1173494ed399SJuergen Gross 	err = xenbus_transaction_start(&xbt);
1174494ed399SJuergen Gross 	if (err) {
1175494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "starting transaction");
1176494ed399SJuergen Gross 		goto destroy_ring;
1177494ed399SJuergen Gross 	}
1178494ed399SJuergen Gross 
1179494ed399SJuergen Gross 	err = xenbus_printf(xbt, dev->nodename, "urb-ring-ref", "%u",
1180494ed399SJuergen Gross 			    info->urb_ring_ref);
1181494ed399SJuergen Gross 	if (err) {
1182494ed399SJuergen Gross 		message = "writing urb-ring-ref";
1183494ed399SJuergen Gross 		goto abort_transaction;
1184494ed399SJuergen Gross 	}
1185494ed399SJuergen Gross 
1186494ed399SJuergen Gross 	err = xenbus_printf(xbt, dev->nodename, "conn-ring-ref", "%u",
1187494ed399SJuergen Gross 			    info->conn_ring_ref);
1188494ed399SJuergen Gross 	if (err) {
1189494ed399SJuergen Gross 		message = "writing conn-ring-ref";
1190494ed399SJuergen Gross 		goto abort_transaction;
1191494ed399SJuergen Gross 	}
1192494ed399SJuergen Gross 
1193494ed399SJuergen Gross 	err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
1194494ed399SJuergen Gross 			    info->evtchn);
1195494ed399SJuergen Gross 	if (err) {
1196494ed399SJuergen Gross 		message = "writing event-channel";
1197494ed399SJuergen Gross 		goto abort_transaction;
1198494ed399SJuergen Gross 	}
1199494ed399SJuergen Gross 
1200494ed399SJuergen Gross 	err = xenbus_transaction_end(xbt, 0);
1201494ed399SJuergen Gross 	if (err) {
1202494ed399SJuergen Gross 		if (err == -EAGAIN)
1203494ed399SJuergen Gross 			goto again;
1204494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "completing transaction");
1205494ed399SJuergen Gross 		goto destroy_ring;
1206494ed399SJuergen Gross 	}
1207494ed399SJuergen Gross 
1208494ed399SJuergen Gross 	return 0;
1209494ed399SJuergen Gross 
1210494ed399SJuergen Gross abort_transaction:
1211494ed399SJuergen Gross 	xenbus_transaction_end(xbt, 1);
1212494ed399SJuergen Gross 	xenbus_dev_fatal(dev, err, "%s", message);
1213494ed399SJuergen Gross 
1214494ed399SJuergen Gross destroy_ring:
1215494ed399SJuergen Gross 	xenhcd_destroy_rings(info);
1216494ed399SJuergen Gross 
1217494ed399SJuergen Gross 	return err;
1218494ed399SJuergen Gross }
1219494ed399SJuergen Gross 
xenhcd_connect(struct xenbus_device * dev)1220494ed399SJuergen Gross static int xenhcd_connect(struct xenbus_device *dev)
1221494ed399SJuergen Gross {
1222494ed399SJuergen Gross 	struct xenhcd_info *info = dev_get_drvdata(&dev->dev);
1223494ed399SJuergen Gross 	struct xenusb_conn_request *req;
1224494ed399SJuergen Gross 	int idx, err;
1225494ed399SJuergen Gross 	int notify;
1226494ed399SJuergen Gross 	char name[TASK_COMM_LEN];
1227494ed399SJuergen Gross 	struct usb_hcd *hcd;
1228494ed399SJuergen Gross 
1229494ed399SJuergen Gross 	hcd = xenhcd_info_to_hcd(info);
1230494ed399SJuergen Gross 	snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum);
1231494ed399SJuergen Gross 
1232494ed399SJuergen Gross 	err = xenhcd_talk_to_backend(dev, info);
1233494ed399SJuergen Gross 	if (err)
1234494ed399SJuergen Gross 		return err;
1235494ed399SJuergen Gross 
1236494ed399SJuergen Gross 	/* prepare ring for hotplug notification */
1237494ed399SJuergen Gross 	for (idx = 0; idx < XENUSB_CONN_RING_SIZE; idx++) {
1238494ed399SJuergen Gross 		req = RING_GET_REQUEST(&info->conn_ring, idx);
1239494ed399SJuergen Gross 		req->id = idx;
1240494ed399SJuergen Gross 	}
1241494ed399SJuergen Gross 	info->conn_ring.req_prod_pvt = idx;
1242494ed399SJuergen Gross 
1243494ed399SJuergen Gross 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify);
1244494ed399SJuergen Gross 	if (notify)
1245494ed399SJuergen Gross 		notify_remote_via_irq(info->irq);
1246494ed399SJuergen Gross 
1247494ed399SJuergen Gross 	return 0;
1248494ed399SJuergen Gross }
1249494ed399SJuergen Gross 
xenhcd_disconnect(struct xenbus_device * dev)1250494ed399SJuergen Gross static void xenhcd_disconnect(struct xenbus_device *dev)
1251494ed399SJuergen Gross {
1252494ed399SJuergen Gross 	struct xenhcd_info *info = dev_get_drvdata(&dev->dev);
1253494ed399SJuergen Gross 	struct usb_hcd *hcd = xenhcd_info_to_hcd(info);
1254494ed399SJuergen Gross 
1255494ed399SJuergen Gross 	usb_remove_hcd(hcd);
1256494ed399SJuergen Gross 	xenbus_frontend_closed(dev);
1257494ed399SJuergen Gross }
1258494ed399SJuergen Gross 
xenhcd_watchdog(struct timer_list * timer)1259494ed399SJuergen Gross static void xenhcd_watchdog(struct timer_list *timer)
1260494ed399SJuergen Gross {
1261494ed399SJuergen Gross 	struct xenhcd_info *info = from_timer(info, timer, watchdog);
1262494ed399SJuergen Gross 	unsigned long flags;
1263494ed399SJuergen Gross 
1264494ed399SJuergen Gross 	spin_lock_irqsave(&info->lock, flags);
1265494ed399SJuergen Gross 	if (likely(HC_IS_RUNNING(xenhcd_info_to_hcd(info)->state))) {
1266494ed399SJuergen Gross 		xenhcd_timer_action_done(info, TIMER_RING_WATCHDOG);
1267494ed399SJuergen Gross 		xenhcd_giveback_unlinked_urbs(info);
1268494ed399SJuergen Gross 		xenhcd_kick_pending_urbs(info);
1269494ed399SJuergen Gross 	}
1270494ed399SJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
1271494ed399SJuergen Gross }
1272494ed399SJuergen Gross 
1273494ed399SJuergen Gross /*
1274494ed399SJuergen Gross  * one-time HC init
1275494ed399SJuergen Gross  */
xenhcd_setup(struct usb_hcd * hcd)1276494ed399SJuergen Gross static int xenhcd_setup(struct usb_hcd *hcd)
1277494ed399SJuergen Gross {
1278494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
1279494ed399SJuergen Gross 
1280494ed399SJuergen Gross 	spin_lock_init(&info->lock);
1281494ed399SJuergen Gross 	INIT_LIST_HEAD(&info->pending_submit_list);
1282494ed399SJuergen Gross 	INIT_LIST_HEAD(&info->pending_unlink_list);
1283494ed399SJuergen Gross 	INIT_LIST_HEAD(&info->in_progress_list);
1284494ed399SJuergen Gross 	INIT_LIST_HEAD(&info->giveback_waiting_list);
1285494ed399SJuergen Gross 	timer_setup(&info->watchdog, xenhcd_watchdog, 0);
1286494ed399SJuergen Gross 
1287494ed399SJuergen Gross 	hcd->has_tt = (hcd->driver->flags & HCD_MASK) != HCD_USB11;
1288494ed399SJuergen Gross 
1289494ed399SJuergen Gross 	return 0;
1290494ed399SJuergen Gross }
1291494ed399SJuergen Gross 
1292494ed399SJuergen Gross /*
1293494ed399SJuergen Gross  * start HC running
1294494ed399SJuergen Gross  */
xenhcd_run(struct usb_hcd * hcd)1295494ed399SJuergen Gross static int xenhcd_run(struct usb_hcd *hcd)
1296494ed399SJuergen Gross {
1297494ed399SJuergen Gross 	hcd->uses_new_polling = 1;
1298494ed399SJuergen Gross 	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
1299494ed399SJuergen Gross 	hcd->state = HC_STATE_RUNNING;
1300494ed399SJuergen Gross 	return 0;
1301494ed399SJuergen Gross }
1302494ed399SJuergen Gross 
1303494ed399SJuergen Gross /*
1304494ed399SJuergen Gross  * stop running HC
1305494ed399SJuergen Gross  */
xenhcd_stop(struct usb_hcd * hcd)1306494ed399SJuergen Gross static void xenhcd_stop(struct usb_hcd *hcd)
1307494ed399SJuergen Gross {
1308494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
1309494ed399SJuergen Gross 
1310494ed399SJuergen Gross 	del_timer_sync(&info->watchdog);
1311494ed399SJuergen Gross 	spin_lock_irq(&info->lock);
1312494ed399SJuergen Gross 	/* cancel all urbs */
1313494ed399SJuergen Gross 	hcd->state = HC_STATE_HALT;
1314494ed399SJuergen Gross 	xenhcd_cancel_all_enqueued_urbs(info);
1315494ed399SJuergen Gross 	xenhcd_giveback_unlinked_urbs(info);
1316494ed399SJuergen Gross 	spin_unlock_irq(&info->lock);
1317494ed399SJuergen Gross }
1318494ed399SJuergen Gross 
1319494ed399SJuergen Gross /*
1320494ed399SJuergen Gross  * called as .urb_enqueue()
1321494ed399SJuergen Gross  * non-error returns are promise to giveback the urb later
1322494ed399SJuergen Gross  */
xenhcd_urb_enqueue(struct usb_hcd * hcd,struct urb * urb,gfp_t mem_flags)1323494ed399SJuergen Gross static int xenhcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
1324494ed399SJuergen Gross 			      gfp_t mem_flags)
1325494ed399SJuergen Gross {
1326494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
1327494ed399SJuergen Gross 	struct urb_priv *urbp;
1328494ed399SJuergen Gross 	unsigned long flags;
1329494ed399SJuergen Gross 	int ret;
1330494ed399SJuergen Gross 
1331494ed399SJuergen Gross 	if (unlikely(info->error))
1332494ed399SJuergen Gross 		return -ESHUTDOWN;
1333494ed399SJuergen Gross 
1334494ed399SJuergen Gross 	urbp = kmem_cache_zalloc(xenhcd_urbp_cachep, mem_flags);
1335494ed399SJuergen Gross 	if (!urbp)
1336494ed399SJuergen Gross 		return -ENOMEM;
1337494ed399SJuergen Gross 
1338494ed399SJuergen Gross 	spin_lock_irqsave(&info->lock, flags);
1339494ed399SJuergen Gross 
1340494ed399SJuergen Gross 	urbp->urb = urb;
1341494ed399SJuergen Gross 	urb->hcpriv = urbp;
1342494ed399SJuergen Gross 	urbp->req_id = ~0;
1343494ed399SJuergen Gross 	urbp->unlink_req_id = ~0;
1344494ed399SJuergen Gross 	INIT_LIST_HEAD(&urbp->list);
1345494ed399SJuergen Gross 	urbp->status = 1;
1346494ed399SJuergen Gross 	urb->unlinked = false;
1347494ed399SJuergen Gross 
1348494ed399SJuergen Gross 	ret = xenhcd_submit_urb(info, urbp);
1349494ed399SJuergen Gross 
1350494ed399SJuergen Gross 	if (ret)
1351494ed399SJuergen Gross 		xenhcd_free_urb_priv(urbp);
1352494ed399SJuergen Gross 
1353494ed399SJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
1354494ed399SJuergen Gross 
1355494ed399SJuergen Gross 	return ret;
1356494ed399SJuergen Gross }
1357494ed399SJuergen Gross 
1358494ed399SJuergen Gross /*
1359494ed399SJuergen Gross  * called as .urb_dequeue()
1360494ed399SJuergen Gross  */
xenhcd_urb_dequeue(struct usb_hcd * hcd,struct urb * urb,int status)1361494ed399SJuergen Gross static int xenhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
1362494ed399SJuergen Gross {
1363494ed399SJuergen Gross 	struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
1364494ed399SJuergen Gross 	struct urb_priv *urbp;
1365494ed399SJuergen Gross 	unsigned long flags;
1366494ed399SJuergen Gross 	int ret = 0;
1367494ed399SJuergen Gross 
1368494ed399SJuergen Gross 	spin_lock_irqsave(&info->lock, flags);
1369494ed399SJuergen Gross 
1370494ed399SJuergen Gross 	urbp = urb->hcpriv;
1371494ed399SJuergen Gross 	if (urbp) {
1372494ed399SJuergen Gross 		urbp->status = status;
1373494ed399SJuergen Gross 		ret = xenhcd_unlink_urb(info, urbp);
1374494ed399SJuergen Gross 	}
1375494ed399SJuergen Gross 
1376494ed399SJuergen Gross 	spin_unlock_irqrestore(&info->lock, flags);
1377494ed399SJuergen Gross 
1378494ed399SJuergen Gross 	return ret;
1379494ed399SJuergen Gross }
1380494ed399SJuergen Gross 
1381494ed399SJuergen Gross /*
1382494ed399SJuergen Gross  * called from usb_get_current_frame_number(),
1383494ed399SJuergen Gross  * but, almost all drivers not use such function.
1384494ed399SJuergen Gross  */
xenhcd_get_frame(struct usb_hcd * hcd)1385494ed399SJuergen Gross static int xenhcd_get_frame(struct usb_hcd *hcd)
1386494ed399SJuergen Gross {
1387494ed399SJuergen Gross 	/* it means error, but probably no problem :-) */
1388494ed399SJuergen Gross 	return 0;
1389494ed399SJuergen Gross }
1390494ed399SJuergen Gross 
1391494ed399SJuergen Gross static struct hc_driver xenhcd_usb20_hc_driver = {
1392494ed399SJuergen Gross 	.description = "xen-hcd",
1393494ed399SJuergen Gross 	.product_desc = "Xen USB2.0 Virtual Host Controller",
1394494ed399SJuergen Gross 	.hcd_priv_size = sizeof(struct xenhcd_info),
1395494ed399SJuergen Gross 	.flags = HCD_USB2,
1396494ed399SJuergen Gross 
1397494ed399SJuergen Gross 	/* basic HC lifecycle operations */
1398494ed399SJuergen Gross 	.reset = xenhcd_setup,
1399494ed399SJuergen Gross 	.start = xenhcd_run,
1400494ed399SJuergen Gross 	.stop = xenhcd_stop,
1401494ed399SJuergen Gross 
1402494ed399SJuergen Gross 	/* managing urb I/O */
1403494ed399SJuergen Gross 	.urb_enqueue = xenhcd_urb_enqueue,
1404494ed399SJuergen Gross 	.urb_dequeue = xenhcd_urb_dequeue,
1405494ed399SJuergen Gross 	.get_frame_number = xenhcd_get_frame,
1406494ed399SJuergen Gross 
1407494ed399SJuergen Gross 	/* root hub operations */
1408494ed399SJuergen Gross 	.hub_status_data = xenhcd_hub_status_data,
1409494ed399SJuergen Gross 	.hub_control = xenhcd_hub_control,
1410494ed399SJuergen Gross #ifdef CONFIG_PM
1411494ed399SJuergen Gross 	.bus_suspend = xenhcd_bus_suspend,
1412494ed399SJuergen Gross 	.bus_resume = xenhcd_bus_resume,
1413494ed399SJuergen Gross #endif
1414494ed399SJuergen Gross };
1415494ed399SJuergen Gross 
1416494ed399SJuergen Gross static struct hc_driver xenhcd_usb11_hc_driver = {
1417494ed399SJuergen Gross 	.description = "xen-hcd",
1418494ed399SJuergen Gross 	.product_desc = "Xen USB1.1 Virtual Host Controller",
1419494ed399SJuergen Gross 	.hcd_priv_size = sizeof(struct xenhcd_info),
1420494ed399SJuergen Gross 	.flags = HCD_USB11,
1421494ed399SJuergen Gross 
1422494ed399SJuergen Gross 	/* basic HC lifecycle operations */
1423494ed399SJuergen Gross 	.reset = xenhcd_setup,
1424494ed399SJuergen Gross 	.start = xenhcd_run,
1425494ed399SJuergen Gross 	.stop = xenhcd_stop,
1426494ed399SJuergen Gross 
1427494ed399SJuergen Gross 	/* managing urb I/O */
1428494ed399SJuergen Gross 	.urb_enqueue = xenhcd_urb_enqueue,
1429494ed399SJuergen Gross 	.urb_dequeue = xenhcd_urb_dequeue,
1430494ed399SJuergen Gross 	.get_frame_number = xenhcd_get_frame,
1431494ed399SJuergen Gross 
1432494ed399SJuergen Gross 	/* root hub operations */
1433494ed399SJuergen Gross 	.hub_status_data = xenhcd_hub_status_data,
1434494ed399SJuergen Gross 	.hub_control = xenhcd_hub_control,
1435494ed399SJuergen Gross #ifdef CONFIG_PM
1436494ed399SJuergen Gross 	.bus_suspend = xenhcd_bus_suspend,
1437494ed399SJuergen Gross 	.bus_resume = xenhcd_bus_resume,
1438494ed399SJuergen Gross #endif
1439494ed399SJuergen Gross };
1440494ed399SJuergen Gross 
xenhcd_create_hcd(struct xenbus_device * dev)1441494ed399SJuergen Gross static struct usb_hcd *xenhcd_create_hcd(struct xenbus_device *dev)
1442494ed399SJuergen Gross {
1443494ed399SJuergen Gross 	int i;
1444494ed399SJuergen Gross 	int err = 0;
1445494ed399SJuergen Gross 	int num_ports;
1446494ed399SJuergen Gross 	int usb_ver;
1447494ed399SJuergen Gross 	struct usb_hcd *hcd = NULL;
1448494ed399SJuergen Gross 	struct xenhcd_info *info;
1449494ed399SJuergen Gross 
1450494ed399SJuergen Gross 	err = xenbus_scanf(XBT_NIL, dev->otherend, "num-ports", "%d",
1451494ed399SJuergen Gross 			   &num_ports);
1452494ed399SJuergen Gross 	if (err != 1) {
1453494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "reading num-ports");
1454494ed399SJuergen Gross 		return ERR_PTR(-EINVAL);
1455494ed399SJuergen Gross 	}
1456494ed399SJuergen Gross 	if (num_ports < 1 || num_ports > XENUSB_MAX_PORTNR) {
1457494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "invalid num-ports");
1458494ed399SJuergen Gross 		return ERR_PTR(-EINVAL);
1459494ed399SJuergen Gross 	}
1460494ed399SJuergen Gross 
1461494ed399SJuergen Gross 	err = xenbus_scanf(XBT_NIL, dev->otherend, "usb-ver", "%d", &usb_ver);
1462494ed399SJuergen Gross 	if (err != 1) {
1463494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "reading usb-ver");
1464494ed399SJuergen Gross 		return ERR_PTR(-EINVAL);
1465494ed399SJuergen Gross 	}
1466494ed399SJuergen Gross 	switch (usb_ver) {
1467494ed399SJuergen Gross 	case XENUSB_VER_USB11:
1468494ed399SJuergen Gross 		hcd = usb_create_hcd(&xenhcd_usb11_hc_driver, &dev->dev,
1469494ed399SJuergen Gross 				     dev_name(&dev->dev));
1470494ed399SJuergen Gross 		break;
1471494ed399SJuergen Gross 	case XENUSB_VER_USB20:
1472494ed399SJuergen Gross 		hcd = usb_create_hcd(&xenhcd_usb20_hc_driver, &dev->dev,
1473494ed399SJuergen Gross 				     dev_name(&dev->dev));
1474494ed399SJuergen Gross 		break;
1475494ed399SJuergen Gross 	default:
1476494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "invalid usb-ver");
1477494ed399SJuergen Gross 		return ERR_PTR(-EINVAL);
1478494ed399SJuergen Gross 	}
1479494ed399SJuergen Gross 	if (!hcd) {
1480494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err,
1481494ed399SJuergen Gross 				 "fail to allocate USB host controller");
1482494ed399SJuergen Gross 		return ERR_PTR(-ENOMEM);
1483494ed399SJuergen Gross 	}
1484494ed399SJuergen Gross 
1485494ed399SJuergen Gross 	info = xenhcd_hcd_to_info(hcd);
1486494ed399SJuergen Gross 	info->xbdev = dev;
1487494ed399SJuergen Gross 	info->rh_numports = num_ports;
1488494ed399SJuergen Gross 
1489494ed399SJuergen Gross 	for (i = 0; i < XENUSB_URB_RING_SIZE; i++) {
1490494ed399SJuergen Gross 		info->shadow[i].req.id = i + 1;
1491494ed399SJuergen Gross 		info->shadow[i].urb = NULL;
1492aff477cbSJuergen Gross 		info->shadow[i].in_flight = false;
1493494ed399SJuergen Gross 	}
1494494ed399SJuergen Gross 	info->shadow[XENUSB_URB_RING_SIZE - 1].req.id = 0x0fff;
1495494ed399SJuergen Gross 
1496494ed399SJuergen Gross 	return hcd;
1497494ed399SJuergen Gross }
1498494ed399SJuergen Gross 
xenhcd_backend_changed(struct xenbus_device * dev,enum xenbus_state backend_state)1499494ed399SJuergen Gross static void xenhcd_backend_changed(struct xenbus_device *dev,
1500494ed399SJuergen Gross 				   enum xenbus_state backend_state)
1501494ed399SJuergen Gross {
1502494ed399SJuergen Gross 	switch (backend_state) {
1503494ed399SJuergen Gross 	case XenbusStateInitialising:
1504494ed399SJuergen Gross 	case XenbusStateReconfiguring:
1505494ed399SJuergen Gross 	case XenbusStateReconfigured:
1506494ed399SJuergen Gross 	case XenbusStateUnknown:
1507494ed399SJuergen Gross 		break;
1508494ed399SJuergen Gross 
1509494ed399SJuergen Gross 	case XenbusStateInitWait:
1510494ed399SJuergen Gross 	case XenbusStateInitialised:
1511494ed399SJuergen Gross 	case XenbusStateConnected:
1512494ed399SJuergen Gross 		if (dev->state != XenbusStateInitialising)
1513494ed399SJuergen Gross 			break;
1514494ed399SJuergen Gross 		if (!xenhcd_connect(dev))
1515494ed399SJuergen Gross 			xenbus_switch_state(dev, XenbusStateConnected);
1516494ed399SJuergen Gross 		break;
1517494ed399SJuergen Gross 
1518494ed399SJuergen Gross 	case XenbusStateClosed:
1519494ed399SJuergen Gross 		if (dev->state == XenbusStateClosed)
1520494ed399SJuergen Gross 			break;
1521494ed399SJuergen Gross 		fallthrough;	/* Missed the backend's Closing state. */
1522494ed399SJuergen Gross 	case XenbusStateClosing:
1523494ed399SJuergen Gross 		xenhcd_disconnect(dev);
1524494ed399SJuergen Gross 		break;
1525494ed399SJuergen Gross 
1526494ed399SJuergen Gross 	default:
1527494ed399SJuergen Gross 		xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
1528494ed399SJuergen Gross 				 backend_state);
1529494ed399SJuergen Gross 		break;
1530494ed399SJuergen Gross 	}
1531494ed399SJuergen Gross }
1532494ed399SJuergen Gross 
xenhcd_remove(struct xenbus_device * dev)1533*7cffcadeSDawei Li static void xenhcd_remove(struct xenbus_device *dev)
1534494ed399SJuergen Gross {
1535494ed399SJuergen Gross 	struct xenhcd_info *info = dev_get_drvdata(&dev->dev);
1536494ed399SJuergen Gross 	struct usb_hcd *hcd = xenhcd_info_to_hcd(info);
1537494ed399SJuergen Gross 
1538494ed399SJuergen Gross 	xenhcd_destroy_rings(info);
1539494ed399SJuergen Gross 	usb_put_hcd(hcd);
1540494ed399SJuergen Gross }
1541494ed399SJuergen Gross 
xenhcd_probe(struct xenbus_device * dev,const struct xenbus_device_id * id)1542494ed399SJuergen Gross static int xenhcd_probe(struct xenbus_device *dev,
1543494ed399SJuergen Gross 			const struct xenbus_device_id *id)
1544494ed399SJuergen Gross {
1545494ed399SJuergen Gross 	int err;
1546494ed399SJuergen Gross 	struct usb_hcd *hcd;
1547494ed399SJuergen Gross 	struct xenhcd_info *info;
1548494ed399SJuergen Gross 
1549494ed399SJuergen Gross 	if (usb_disabled())
1550494ed399SJuergen Gross 		return -ENODEV;
1551494ed399SJuergen Gross 
1552494ed399SJuergen Gross 	hcd = xenhcd_create_hcd(dev);
1553494ed399SJuergen Gross 	if (IS_ERR(hcd)) {
1554494ed399SJuergen Gross 		err = PTR_ERR(hcd);
1555494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err,
1556494ed399SJuergen Gross 				 "fail to create usb host controller");
1557494ed399SJuergen Gross 		return err;
1558494ed399SJuergen Gross 	}
1559494ed399SJuergen Gross 
1560494ed399SJuergen Gross 	info = xenhcd_hcd_to_info(hcd);
1561494ed399SJuergen Gross 	dev_set_drvdata(&dev->dev, info);
1562494ed399SJuergen Gross 
1563494ed399SJuergen Gross 	err = usb_add_hcd(hcd, 0, 0);
1564494ed399SJuergen Gross 	if (err) {
1565494ed399SJuergen Gross 		xenbus_dev_fatal(dev, err, "fail to add USB host controller");
1566494ed399SJuergen Gross 		usb_put_hcd(hcd);
1567494ed399SJuergen Gross 		dev_set_drvdata(&dev->dev, NULL);
1568494ed399SJuergen Gross 	}
1569494ed399SJuergen Gross 
1570494ed399SJuergen Gross 	return err;
1571494ed399SJuergen Gross }
1572494ed399SJuergen Gross 
1573494ed399SJuergen Gross static const struct xenbus_device_id xenhcd_ids[] = {
1574494ed399SJuergen Gross 	{ "vusb" },
1575494ed399SJuergen Gross 	{ "" },
1576494ed399SJuergen Gross };
1577494ed399SJuergen Gross 
1578494ed399SJuergen Gross static struct xenbus_driver xenhcd_driver = {
1579494ed399SJuergen Gross 	.ids			= xenhcd_ids,
1580494ed399SJuergen Gross 	.probe			= xenhcd_probe,
1581494ed399SJuergen Gross 	.otherend_changed	= xenhcd_backend_changed,
1582494ed399SJuergen Gross 	.remove			= xenhcd_remove,
1583494ed399SJuergen Gross };
1584494ed399SJuergen Gross 
xenhcd_init(void)1585494ed399SJuergen Gross static int __init xenhcd_init(void)
1586494ed399SJuergen Gross {
1587494ed399SJuergen Gross 	if (!xen_domain())
1588494ed399SJuergen Gross 		return -ENODEV;
1589494ed399SJuergen Gross 
1590494ed399SJuergen Gross 	xenhcd_urbp_cachep = kmem_cache_create("xenhcd_urb_priv",
1591494ed399SJuergen Gross 					sizeof(struct urb_priv), 0, 0, NULL);
1592494ed399SJuergen Gross 	if (!xenhcd_urbp_cachep) {
1593494ed399SJuergen Gross 		pr_err("xenhcd failed to create kmem cache\n");
1594494ed399SJuergen Gross 		return -ENOMEM;
1595494ed399SJuergen Gross 	}
1596494ed399SJuergen Gross 
1597494ed399SJuergen Gross 	return xenbus_register_frontend(&xenhcd_driver);
1598494ed399SJuergen Gross }
1599494ed399SJuergen Gross module_init(xenhcd_init);
1600494ed399SJuergen Gross 
xenhcd_exit(void)1601494ed399SJuergen Gross static void __exit xenhcd_exit(void)
1602494ed399SJuergen Gross {
1603494ed399SJuergen Gross 	kmem_cache_destroy(xenhcd_urbp_cachep);
1604494ed399SJuergen Gross 	xenbus_unregister_driver(&xenhcd_driver);
1605494ed399SJuergen Gross }
1606494ed399SJuergen Gross module_exit(xenhcd_exit);
1607494ed399SJuergen Gross 
1608494ed399SJuergen Gross MODULE_ALIAS("xen:vusb");
1609494ed399SJuergen Gross MODULE_AUTHOR("Juergen Gross <jgross@suse.com>");
1610494ed399SJuergen Gross MODULE_DESCRIPTION("Xen USB Virtual Host Controller driver (xen-hcd)");
1611494ed399SJuergen Gross MODULE_LICENSE("Dual BSD/GPL");
1612