4 * Xen USB Virtual Host Controller - Root Hub Emulations
6 * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
7 * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
24 * When distributed separately from the Linux kernel or incorporated into
25 * other software packages, subject to the following license:
27 * Permission is hereby granted, free of charge, to any person obtaining a copy
28 * of this software and associated documentation files (the "Software"), to
29 * deal in the Software without restriction, including without limitation the
30 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
31 * sell copies of the Software, and to permit persons to whom the Software is
32 * furnished to do so, subject to the following conditions:
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
42 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
43 * DEALINGS IN THE SOFTWARE.
47 * set virtual port connection status
49 void set_connect_state(struct usbfront_info *info, int portnum)
54 if (info->ports[port].status & USB_PORT_STAT_POWER) {
55 switch (info->devices[port].speed) {
56 case USB_SPEED_UNKNOWN:
57 info->ports[port].status &=
58 ~(USB_PORT_STAT_CONNECTION |
59 USB_PORT_STAT_ENABLE |
60 USB_PORT_STAT_LOW_SPEED |
61 USB_PORT_STAT_HIGH_SPEED |
62 USB_PORT_STAT_SUSPEND);
65 info->ports[port].status |= USB_PORT_STAT_CONNECTION;
66 info->ports[port].status |= USB_PORT_STAT_LOW_SPEED;
69 info->ports[port].status |= USB_PORT_STAT_CONNECTION;
72 info->ports[port].status |= USB_PORT_STAT_CONNECTION;
73 info->ports[port].status |= USB_PORT_STAT_HIGH_SPEED;
78 info->ports[port].status |= (USB_PORT_STAT_C_CONNECTION << 16);
83 * set virtual device connection status
85 void rhport_connect(struct usbfront_info *info,
86 int portnum, enum usb_device_speed speed)
90 if (portnum < 1 || portnum > info->rh_numports)
91 return; /* invalid port number */
94 if (info->devices[port].speed != speed) {
96 case USB_SPEED_UNKNOWN: /* disconnect */
97 info->devices[port].status = USB_STATE_NOTATTACHED;
102 info->devices[port].status = USB_STATE_ATTACHED;
107 info->devices[port].speed = speed;
108 info->ports[port].c_connection = 1;
110 set_connect_state(info, portnum);
115 * SetPortFeature(PORT_SUSPENDED)
117 void rhport_suspend(struct usbfront_info *info, int portnum)
122 info->ports[port].status |= USB_PORT_STAT_SUSPEND;
123 info->devices[port].status = USB_STATE_SUSPENDED;
127 * ClearPortFeature(PORT_SUSPENDED)
129 void rhport_resume(struct usbfront_info *info, int portnum)
134 if (info->ports[port].status & USB_PORT_STAT_SUSPEND) {
135 info->ports[port].resuming = 1;
136 info->ports[port].timeout = jiffies + msecs_to_jiffies(20);
141 * SetPortFeature(PORT_POWER)
143 void rhport_power_on(struct usbfront_info *info, int portnum)
148 if ((info->ports[port].status & USB_PORT_STAT_POWER) == 0) {
149 info->ports[port].status |= USB_PORT_STAT_POWER;
150 if (info->devices[port].status != USB_STATE_NOTATTACHED)
151 info->devices[port].status = USB_STATE_POWERED;
152 if (info->ports[port].c_connection)
153 set_connect_state(info, portnum);
158 * ClearPortFeature(PORT_POWER)
159 * SetConfiguration(non-zero)
163 void rhport_power_off(struct usbfront_info *info, int portnum)
168 if (info->ports[port].status & USB_PORT_STAT_POWER) {
169 info->ports[port].status = 0;
170 if (info->devices[port].status != USB_STATE_NOTATTACHED)
171 info->devices[port].status = USB_STATE_ATTACHED;
176 * ClearPortFeature(PORT_ENABLE)
178 void rhport_disable(struct usbfront_info *info, int portnum)
183 info->ports[port].status &= ~USB_PORT_STAT_ENABLE;
184 info->ports[port].status &= ~USB_PORT_STAT_SUSPEND;
185 info->ports[port].resuming = 0;
186 if (info->devices[port].status != USB_STATE_NOTATTACHED)
187 info->devices[port].status = USB_STATE_POWERED;
191 * SetPortFeature(PORT_RESET)
193 void rhport_reset(struct usbfront_info *info, int portnum)
198 info->ports[port].status &= ~(USB_PORT_STAT_ENABLE
199 | USB_PORT_STAT_LOW_SPEED
200 | USB_PORT_STAT_HIGH_SPEED);
201 info->ports[port].status |= USB_PORT_STAT_RESET;
203 if (info->devices[port].status != USB_STATE_NOTATTACHED)
204 info->devices[port].status = USB_STATE_ATTACHED;
206 /* 10msec reset signaling */
207 info->ports[port].timeout = jiffies + msecs_to_jiffies(10);
212 static int xenhcd_bus_suspend(struct usb_hcd *hcd)
214 struct usbfront_info *info = hcd_to_info(hcd);
218 ports = info->rh_numports;
220 spin_lock_irq(&info->lock);
221 if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
224 /* suspend any active ports*/
225 for (i = 1; i <= ports; i++)
226 rhport_suspend(info, i);
228 spin_unlock_irq(&info->lock);
230 del_timer_sync(&info->watchdog);
235 static int xenhcd_bus_resume(struct usb_hcd *hcd)
237 struct usbfront_info *info = hcd_to_info(hcd);
241 ports = info->rh_numports;
243 spin_lock_irq(&info->lock);
244 if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
247 /* resume any suspended ports*/
248 for (i = 1; i <= ports; i++)
249 rhport_resume(info, i);
251 spin_unlock_irq(&info->lock);
258 static void xenhcd_hub_descriptor(struct usbfront_info *info,
259 struct usb_hub_descriptor *desc)
262 int ports = info->rh_numports;
264 desc->bDescriptorType = 0x29;
265 desc->bPwrOn2PwrGood = 10; /* EHCI says 20ms max */
266 desc->bHubContrCurrent = 0;
267 desc->bNbrPorts = ports;
269 /* size of DeviceRemovable and PortPwrCtrlMask fields*/
270 temp = 1 + (ports / 8);
271 desc->bDescLength = 7 + 2 * temp;
273 /* bitmaps for DeviceRemovable and PortPwrCtrlMask */
274 memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
275 memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
277 /* per-port over current reporting and no power switching */
279 desc->wHubCharacteristics = cpu_to_le16(temp);
282 /* port status change mask for hub_status_data */
283 #define PORT_C_MASK \
284 ((USB_PORT_STAT_C_CONNECTION \
285 | USB_PORT_STAT_C_ENABLE \
286 | USB_PORT_STAT_C_SUSPEND \
287 | USB_PORT_STAT_C_OVERCURRENT \
288 | USB_PORT_STAT_C_RESET) << 16)
291 * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap.
292 * If port status changed, writes the bitmap to buf and return
293 * that length(number of bytes).
294 * If Nothing changed, return 0.
296 static int xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
298 struct usbfront_info *info = hcd_to_info(hcd);
309 if (!HC_IS_RUNNING(hcd->state))
312 /* initialize the status to no-changes */
313 ports = info->rh_numports;
314 length = 1 + (ports / 8);
315 for (i = 0; i < length; i++) {
320 spin_lock_irqsave(&info->lock, flags);
322 for (i = 0; i < ports; i++) {
323 /* check status for each port */
324 if (info->ports[i].status & PORT_C_MASK) {
326 buf[0] |= 1 << (i + 1);
328 buf[1] |= 1 << (i - 7);
330 buf[2] |= 1 << (i - 15);
332 buf[3] |= 1 << (i - 23);
340 spin_unlock_irqrestore(&info->lock, flags);
345 static int xenhcd_hub_control(struct usb_hcd *hcd,
352 struct usbfront_info *info = hcd_to_info(hcd);
353 int ports = info->rh_numports;
359 spin_lock_irqsave(&info->lock, flags);
361 case ClearHubFeature:
362 /* ignore this request */
364 case ClearPortFeature:
365 if (!wIndex || wIndex > ports)
369 case USB_PORT_FEAT_SUSPEND:
370 rhport_resume(info, wIndex);
372 case USB_PORT_FEAT_POWER:
373 rhport_power_off(info, wIndex);
375 case USB_PORT_FEAT_ENABLE:
376 rhport_disable(info, wIndex);
378 case USB_PORT_FEAT_C_CONNECTION:
379 info->ports[wIndex-1].c_connection = 0;
380 /* falling through */
382 info->ports[wIndex-1].status &= ~(1 << wValue);
386 case GetHubDescriptor:
387 xenhcd_hub_descriptor(info,
388 (struct usb_hub_descriptor *) buf);
391 /* always local power supply good and no over-current exists. */
392 *(__le32 *)buf = cpu_to_le32(0);
395 if (!wIndex || wIndex > ports)
400 /* resume completion */
401 if (info->ports[wIndex].resuming &&
402 time_after_eq(jiffies, info->ports[wIndex].timeout)) {
403 info->ports[wIndex].status |= (USB_PORT_STAT_C_SUSPEND << 16);
404 info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND;
407 /* reset completion */
408 if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 &&
409 time_after_eq(jiffies, info->ports[wIndex].timeout)) {
410 info->ports[wIndex].status |= (USB_PORT_STAT_C_RESET << 16);
411 info->ports[wIndex].status &= ~USB_PORT_STAT_RESET;
413 if (info->devices[wIndex].status != USB_STATE_NOTATTACHED) {
414 info->ports[wIndex].status |= USB_PORT_STAT_ENABLE;
415 info->devices[wIndex].status = USB_STATE_DEFAULT;
418 switch (info->devices[wIndex].speed) {
420 info->ports[wIndex].status |= USB_PORT_STAT_LOW_SPEED;
423 info->ports[wIndex].status |= USB_PORT_STAT_HIGH_SPEED;
430 ((u16 *) buf)[0] = cpu_to_le16 (info->ports[wIndex].status);
431 ((u16 *) buf)[1] = cpu_to_le16 (info->ports[wIndex].status >> 16);
437 if (!wIndex || wIndex > ports)
441 case USB_PORT_FEAT_POWER:
442 rhport_power_on(info, wIndex);
444 case USB_PORT_FEAT_RESET:
445 rhport_reset(info, wIndex);
447 case USB_PORT_FEAT_SUSPEND:
448 rhport_suspend(info, wIndex);
451 if ((info->ports[wIndex-1].status & USB_PORT_STAT_POWER) != 0)
452 info->ports[wIndex-1].status |= (1 << wValue);
460 spin_unlock_irqrestore(&info->lock, flags);
462 /* check status for each port */
463 for (i = 0; i < ports; i++) {
464 if (info->ports[i].status & PORT_C_MASK)
468 usb_hcd_poll_rh_status(hcd);