ia64/linux-2.6.18-xen.hg

view drivers/xen/usbfront/usbfront-hub.c @ 845:4c7eb2e71e9d

pvusb: Fix license headers.

Signed-off-by: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Mar 31 11:11:23 2009 +0100 (2009-03-31)
parents 8ebd33ba19b3
children
line source
1 /*
2 * usbfront-hub.c
3 *
4 * Xen USB Virtual Host Controller - Root Hub Emulations
5 *
6 * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
7 * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
8 *
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.
13 *
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.
18 *
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/>.
21 *
22 * or, by your choice,
23 *
24 * When distributed separately from the Linux kernel or incorporated into
25 * other software packages, subject to the following license:
26 *
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:
33 *
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
36 *
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.
44 */
46 /*
47 * set virtual port connection status
48 */
49 void set_connect_state(struct usbfront_info *info, int portnum)
50 {
51 int port;
53 port = portnum -1;
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 &= ~(USB_PORT_STAT_CONNECTION |
58 USB_PORT_STAT_ENABLE |
59 USB_PORT_STAT_LOW_SPEED |
60 USB_PORT_STAT_HIGH_SPEED |
61 USB_PORT_STAT_SUSPEND);
62 break;
63 case USB_SPEED_LOW:
64 info->ports[port].status |= USB_PORT_STAT_CONNECTION;
65 info->ports[port].status |= USB_PORT_STAT_LOW_SPEED;
66 break;
67 case USB_SPEED_FULL:
68 info->ports[port].status |= USB_PORT_STAT_CONNECTION;
69 break;
70 case USB_SPEED_HIGH:
71 info->ports[port].status |= USB_PORT_STAT_CONNECTION;
72 info->ports[port].status |= USB_PORT_STAT_HIGH_SPEED;
73 break;
74 default: /* error */
75 return;
76 }
77 info->ports[port].status |= (USB_PORT_STAT_C_CONNECTION << 16);
78 }
79 }
81 /*
82 * set virtual device connection status
83 */
84 void rhport_connect(struct usbfront_info *info,
85 int portnum, enum usb_device_speed speed)
86 {
87 int port;
89 port = portnum - 1;
90 if (info->devices[port].speed != speed) {
91 switch (speed) {
92 case USB_SPEED_UNKNOWN: /* disconnect */
93 info->devices[port].status = USB_STATE_NOTATTACHED;
94 break;
95 case USB_SPEED_LOW:
96 case USB_SPEED_FULL:
97 case USB_SPEED_HIGH:
98 info->devices[port].status = USB_STATE_ATTACHED;
99 break;
100 default: /* error */
101 return;
102 }
103 info->devices[port].speed = speed;
104 info->ports[port].c_connection = 1;
106 set_connect_state(info, portnum);
107 }
108 }
110 void rhport_disconnect(struct usbfront_info *info, int portnum)
111 {
112 rhport_connect(info, portnum, USB_SPEED_UNKNOWN);
113 }
115 void xenhcd_rhport_state_change(struct usbfront_info *info,
116 int portnum, enum usb_device_speed speed)
117 {
118 int changed = 0;
119 unsigned long flags;
121 if (portnum < 1 || portnum > info->rh_numports)
122 return; /* invalid port number */
124 spin_lock_irqsave(&info->lock, flags);
125 rhport_connect(info, portnum, speed);
126 if (info->ports[portnum-1].c_connection)
127 changed = 1;
128 spin_unlock_irqrestore(&info->lock, flags);
130 if (changed)
131 usb_hcd_poll_rh_status(info_to_hcd(info));
132 }
134 /*
135 * SetPortFeature(PORT_SUSPENDED)
136 */
137 void rhport_suspend(struct usbfront_info *info, int portnum)
138 {
139 int port;
141 port = portnum - 1;
142 info->ports[port].status |= USB_PORT_STAT_SUSPEND;
143 info->devices[port].status = USB_STATE_SUSPENDED;
144 }
146 /*
147 * ClearPortFeature(PORT_SUSPENDED)
148 */
149 void rhport_resume(struct usbfront_info *info, int portnum)
150 {
151 int port;
153 port = portnum - 1;
154 if (info->ports[port].status & USB_PORT_STAT_SUSPEND) {
155 info->ports[port].resuming = 1;
156 info->ports[port].timeout = jiffies + msecs_to_jiffies(20);
157 }
158 }
160 /*
161 * SetPortFeature(PORT_POWER)
162 */
163 void rhport_power_on(struct usbfront_info *info, int portnum)
164 {
165 int port;
167 port = portnum - 1;
168 if ((info->ports[port].status & USB_PORT_STAT_POWER) == 0) {
169 info->ports[port].status |= USB_PORT_STAT_POWER;
170 if (info->devices[port].status != USB_STATE_NOTATTACHED)
171 info->devices[port].status = USB_STATE_POWERED;
172 if (info->ports[port].c_connection)
173 set_connect_state(info, portnum);
174 }
175 }
177 /*
178 * ClearPortFeature(PORT_POWER)
179 * SetConfiguration(non-zero)
180 * Power_Source_Off
181 * Over-current
182 */
183 void rhport_power_off(struct usbfront_info *info, int portnum)
184 {
185 int port;
187 port = portnum - 1;
188 if (info->ports[port].status & USB_PORT_STAT_POWER) {
189 info->ports[port].status = 0;
190 if (info->devices[port].status != USB_STATE_NOTATTACHED)
191 info->devices[port].status = USB_STATE_ATTACHED;
192 }
193 }
195 /*
196 * ClearPortFeature(PORT_ENABLE)
197 */
198 void rhport_disable(struct usbfront_info *info, int portnum)
199 {
200 int port;
202 port = portnum - 1;
203 info->ports[port].status &= ~USB_PORT_STAT_ENABLE;
204 info->ports[port].status &= ~USB_PORT_STAT_SUSPEND;
205 info->ports[port].resuming = 0;
206 if (info->devices[port].status != USB_STATE_NOTATTACHED)
207 info->devices[port].status = USB_STATE_POWERED;
208 }
210 /*
211 * SetPortFeature(PORT_RESET)
212 */
213 void rhport_reset(struct usbfront_info *info, int portnum)
214 {
215 int port;
217 port = portnum -1;
218 info->ports[port].status &= ~(USB_PORT_STAT_ENABLE
219 | USB_PORT_STAT_LOW_SPEED
220 | USB_PORT_STAT_HIGH_SPEED);
221 info->ports[port].status |= USB_PORT_STAT_RESET;
223 if (info->devices[port].status != USB_STATE_NOTATTACHED)
224 info->devices[port].status = USB_STATE_ATTACHED;
226 /* 10msec reset signaling */
227 info->ports[port].timeout = jiffies + msecs_to_jiffies(10);
228 }
230 #if 0
231 #ifdef CONFIG_PM
232 static int xenhcd_bus_suspend(struct usb_hcd *hcd)
233 {
234 struct usbfront_info *info = hcd_to_info(hcd);
235 int i, ports;
237 ports = info->rh_numports;
239 spin_lock_irq(&info->lock);
241 if (HC_IS_RUNNING(hcd->state)) {
242 /*
243 * TODO:
244 * clean queue,
245 * stop all transfers,
246 * ...
247 */
248 hcd->state = HC_STATE_QUIESCING;
249 }
251 /* suspend any active ports*/
252 for (i = 1; i <= ports; i++) {
253 rhport_suspend(info, i);
254 }
256 del_timer_sync(&info->watchdog);
258 spin_unlock_irq(&info->lock);
260 return 0;
261 }
263 static int xenhcd_bus_resume(struct usb_hcd *hcd)
264 {
265 struct usbfront_info *info = hcd_to_info(hcd);
266 int i, ports;
268 ports = info->rh_numports;
270 spin_lock_irq(&info->lock);
271 /* resume any suspended ports*/
272 for (i = 1; i <= ports; i++) {
273 rhport_resume(info, i);
274 }
275 hcd->state = HC_STATE_RUNNING;
276 spin_unlock_irq(&info->lock);
277 return 0;
278 }
279 #endif
280 #endif
282 static void xenhcd_hub_descriptor(struct usbfront_info *info,
283 struct usb_hub_descriptor *desc)
284 {
285 u16 temp;
286 int ports = info->rh_numports;
288 desc->bDescriptorType = 0x29;
289 desc->bPwrOn2PwrGood = 10; /* EHCI says 20ms max */
290 desc->bHubContrCurrent = 0;
291 desc->bNbrPorts = ports;
293 /* size of DeviceRemovable and PortPwrCtrlMask fields*/
294 temp = 1 + (ports / 8);
295 desc->bDescLength = 7 + 2 * temp;
297 /* bitmaps for DeviceRemovable and PortPwrCtrlMask */
298 memset (&desc->bitmap[0], 0, temp);
299 memset (&desc->bitmap[temp], 0xff, temp);
301 /* per-port over current reporting and no power switching */
302 temp = 0x000a;
303 desc->wHubCharacteristics = cpu_to_le16(temp);
304 }
306 /* port status change mask for hub_status_data */
307 #define PORT_C_MASK \
308 ((USB_PORT_STAT_C_CONNECTION \
309 | USB_PORT_STAT_C_ENABLE \
310 | USB_PORT_STAT_C_SUSPEND \
311 | USB_PORT_STAT_C_OVERCURRENT \
312 | USB_PORT_STAT_C_RESET) << 16)
314 /*
315 * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap.
316 * If port status changed, writes the bitmap to buf and return
317 * that length(number of bytes).
318 * If Nothing changed, return 0.
319 */
320 static int xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
321 {
322 struct usbfront_info *info = hcd_to_info(hcd);
324 int ports;
325 int i;
326 int length;
328 unsigned long flags;
329 int ret = 0;
331 int changed = 0;
333 if (!HC_IS_RUNNING(hcd->state))
334 return 0;
336 /* initialize the status to no-changes */
337 ports = info->rh_numports;
338 length = 1 + (ports / 8);
339 for (i = 0; i < length; i++) {
340 buf[i] = 0;
341 ret++;
342 }
344 spin_lock_irqsave(&info->lock, flags);
346 for (i = 0; i < ports; i++) {
347 /* check status for each port */
348 if (info->ports[i].status & PORT_C_MASK) {
349 if (i < 7)
350 buf[0] |= 1 << (i + 1);
351 else if (i < 15)
352 buf[1] |= 1 << (i - 7);
353 else if (i < 23)
354 buf[2] |= 1 << (i - 15);
355 else
356 buf[3] |= 1 << (i - 23);
357 changed = 1;
358 }
359 }
361 if (!changed)
362 ret = 0;
364 spin_unlock_irqrestore(&info->lock, flags);
366 return ret;
367 }
369 static int xenhcd_hub_control(struct usb_hcd *hcd,
370 u16 typeReq,
371 u16 wValue,
372 u16 wIndex,
373 char *buf,
374 u16 wLength)
375 {
376 struct usbfront_info *info = hcd_to_info(hcd);
377 int ports = info->rh_numports;
378 unsigned long flags;
379 int ret = 0;
380 int i;
381 int changed = 0;
383 #ifdef USBFRONT_DEBUG
384 WPRINTK("xenusb_hub_control(typeReq %x wValue %x wIndex %x)\n",
385 typeReq, wValue, wIndex);
386 #endif
388 spin_lock_irqsave(&info->lock, flags);
389 switch (typeReq) {
390 case ClearHubFeature:
391 /* ignore this request */
392 break;
393 case ClearPortFeature:
394 if (!wIndex || wIndex > ports)
395 goto error;
397 switch(wValue) {
398 case USB_PORT_FEAT_SUSPEND:
399 rhport_resume(info, wIndex);
400 break;
401 case USB_PORT_FEAT_POWER:
402 rhport_power_off(info, wIndex);
403 break;
404 case USB_PORT_FEAT_ENABLE:
405 rhport_disable(info, wIndex);
406 break;
407 case USB_PORT_FEAT_C_CONNECTION:
408 info->ports[wIndex-1].c_connection = 0;
409 /* falling through */
410 default:
411 info->ports[wIndex-1].status &= ~(1 << wValue);
412 break;
413 }
414 break;
415 case GetHubDescriptor:
416 xenhcd_hub_descriptor(info,
417 (struct usb_hub_descriptor*) buf);
418 break;
419 case GetHubStatus:
420 /* always local power supply good and no over-current exists. */
421 *(__le32 *)buf = cpu_to_le32(0);
422 break;
423 case GetPortStatus:
424 if (!wIndex || wIndex > ports)
425 goto error;
427 wIndex--;
429 /* resume completion */
430 if (info->ports[wIndex].resuming &&
431 time_after_eq(jiffies, info->ports[wIndex].timeout)) {
432 info->ports[wIndex].status |= (USB_PORT_STAT_C_SUSPEND << 16);
433 info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND;
434 }
436 /* reset completion */
437 if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 &&
438 time_after_eq(jiffies, info->ports[wIndex].timeout)) {
439 info->ports[wIndex].status |= (USB_PORT_STAT_C_RESET << 16);
440 info->ports[wIndex].status &= ~USB_PORT_STAT_RESET;
442 if (info->devices[wIndex].status != USB_STATE_NOTATTACHED) {
443 info->ports[wIndex].status |= USB_PORT_STAT_ENABLE;
444 info->devices[wIndex].status = USB_STATE_DEFAULT;
445 }
447 switch(info->devices[wIndex].speed) {
448 case USB_SPEED_LOW:
449 info->ports[wIndex].status |= USB_PORT_STAT_LOW_SPEED;
450 break;
451 case USB_SPEED_HIGH:
452 info->ports[wIndex].status |= USB_PORT_STAT_HIGH_SPEED;
453 break;
454 default:
455 break;
456 }
457 }
459 ((u16 *) buf)[0] = cpu_to_le16 (info->ports[wIndex].status);
460 ((u16 *) buf)[1] = cpu_to_le16 (info->ports[wIndex].status >> 16);
461 break;
462 case SetHubFeature:
463 /* not supported */
464 goto error;
465 case SetPortFeature:
466 if (!wIndex || wIndex > ports)
467 goto error;
469 switch(wValue) {
470 case USB_PORT_FEAT_POWER:
471 rhport_power_on(info, wIndex);
472 break;
473 case USB_PORT_FEAT_RESET:
474 rhport_reset(info, wIndex);
475 break;
476 case USB_PORT_FEAT_SUSPEND:
477 rhport_suspend(info, wIndex);
478 break;
479 default:
480 if ((info->ports[wIndex-1].status & USB_PORT_STAT_POWER) != 0) {
481 info->ports[wIndex-1].status |= (1 << wValue);
482 }
483 }
484 break;
486 default:
487 error:
488 ret = -EPIPE;
489 }
490 spin_unlock_irqrestore(&info->lock, flags);
492 /* check status for each port */
493 for (i = 0; i < ports; i++) {
494 if (info->ports[i].status & PORT_C_MASK) {
495 changed = 1;
496 }
497 }
498 if (changed)
499 usb_hcd_poll_rh_status(hcd);
501 return ret;
502 }