wusb: reset WUSB devices with SetAddress(0)
[linux-2.6.git] / drivers / usb / wusbcore / rh.c
1 /*
2  * Wireless USB Host Controller
3  * Root Hub operations
4  *
5  *
6  * Copyright (C) 2005-2006 Intel Corporation
7  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License version
11  * 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301, USA.
22  *
23  *
24  * We fake a root hub that has fake ports (as many as simultaneous
25  * devices the Wireless USB Host Controller can deal with). For each
26  * port we keep an state in @wusbhc->port[index] identical to the one
27  * specified in the USB2.0[ch11] spec and some extra device
28  * information that complements the one in 'struct usb_device' (as
29  * this lacs a hcpriv pointer).
30  *
31  * Note this is common to WHCI and HWA host controllers.
32  *
33  * Through here we enable most of the state changes that the USB stack
34  * will use to connect or disconnect devices. We need to do some
35  * forced adaptation of Wireless USB device states vs. wired:
36  *
37  *        USB:                 WUSB:
38  *
39  * Port   Powered-off          port slot n/a
40  *        Powered-on           port slot available
41  *        Disconnected         port slot available
42  *        Connected            port slot assigned device
43  *                             device sent DN_Connect
44  *                             device was authenticated
45  *        Enabled              device is authenticated, transitioned
46  *                             from unauth -> auth -> default address
47  *                             -> enabled
48  *        Reset                disconnect
49  *        Disable              disconnect
50  *
51  * This maps the standard USB port states with the WUSB device states
52  * so we can fake ports without having to modify the USB stack.
53  *
54  * FIXME: this process will change in the future
55  *
56  *
57  * ENTRY POINTS
58  *
59  * Our entry points into here are, as in hcd.c, the USB stack root hub
60  * ops defined in the usb_hcd struct:
61  *
62  * wusbhc_rh_status_data()      Provide hub and port status data bitmap
63  *
64  * wusbhc_rh_control()          Execution of all the major requests
65  *                              you can do to a hub (Set|Clear
66  *                              features, get descriptors, status, etc).
67  *
68  * wusbhc_rh_[suspend|resume]() That
69  *
70  * wusbhc_rh_start_port_reset() ??? unimplemented
71  */
72 #include "wusbhc.h"
73
74 #define D_LOCAL 0
75 #include <linux/uwb/debug.h>
76
77 /*
78  * Reset a fake port
79  *
80  * Using a Reset Device IE is too heavyweight as it causes the device
81  * to enter the UnConnected state and leave the cluster, this can mean
82  * that when the device reconnects it is connected to a different fake
83  * port.
84  *
85  * Instead, reset authenticated devices with a SetAddress(0), followed
86  * by a SetAddresss(AuthAddr).
87  *
88  * For unauthenticated devices just pretend to reset but do nothing.
89  * If the device initialization continues to fail it will eventually
90  * time out after TrustTimeout and enter the UnConnected state.
91  *
92  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
93  *
94  * Supposedly we are the only thread accesing @wusbhc->port; in any
95  * case, maybe we should move the mutex locking from
96  * wusbhc_devconnect_auth() to here.
97  *
98  * @port_idx refers to the wusbhc's port index, not the USB port number
99  */
100 static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx)
101 {
102         int result = 0;
103         struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
104         struct wusb_dev *wusb_dev = port->wusb_dev;
105
106         port->status |= USB_PORT_STAT_RESET;
107         port->change |= USB_PORT_STAT_C_RESET;
108
109         if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH)
110                 result = 0;
111         else
112                 result = wusb_dev_update_address(wusbhc, wusb_dev);
113
114         port->status &= ~USB_PORT_STAT_RESET;
115         port->status |= USB_PORT_STAT_ENABLE;
116         port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE; 
117
118         return result;
119 }
120
121 /*
122  * Return the hub change status bitmap
123  *
124  * The bits in the change status bitmap are cleared when a
125  * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4].
126  *
127  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
128  *
129  * WARNING!! This gets called from atomic context; we cannot get the
130  *           mutex--the only race condition we can find is some bit
131  *           changing just after we copy it, which shouldn't be too
132  *           big of a problem [and we can't make it an spinlock
133  *           because other parts need to take it and sleep] .
134  *
135  *           @usb_hcd is refcounted, so it won't dissapear under us
136  *           and before killing a host, the polling of the root hub
137  *           would be stopped anyway.
138  */
139 int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf)
140 {
141         struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
142         size_t cnt, size;
143         unsigned long *buf = (unsigned long *) _buf;
144
145         d_fnstart(1, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
146         /* WE DON'T LOCK, see comment */
147         size = wusbhc->ports_max + 1 /* hub bit */;
148         size = (size + 8 - 1) / 8;      /* round to bytes */
149         for (cnt = 0; cnt < wusbhc->ports_max; cnt++)
150                 if (wusb_port_by_idx(wusbhc, cnt)->change)
151                         set_bit(cnt + 1, buf);
152                 else
153                         clear_bit(cnt + 1, buf);
154         d_fnend(1, wusbhc->dev, "(wusbhc %p) %u, buffer:\n", wusbhc, (int)size);
155         d_dump(1, wusbhc->dev, _buf, size);
156         return size;
157 }
158 EXPORT_SYMBOL_GPL(wusbhc_rh_status_data);
159
160 /*
161  * Return the hub's desciptor
162  *
163  * NOTE: almost cut and paste from ehci-hub.c
164  *
165  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked
166  */
167 static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
168                                    u16 wIndex,
169                                    struct usb_hub_descriptor *descr,
170                                    u16 wLength)
171 {
172         u16 temp = 1 + (wusbhc->ports_max / 8);
173         u8 length = 7 + 2 * temp;
174
175         if (wLength < length)
176                 return -ENOSPC;
177         descr->bDescLength = 7 + 2 * temp;
178         descr->bDescriptorType = 0x29;  /* HUB type */
179         descr->bNbrPorts = wusbhc->ports_max;
180         descr->wHubCharacteristics = cpu_to_le16(
181                 0x00                    /* All ports power at once */
182                 | 0x00                  /* not part of compound device */
183                 | 0x10                  /* No overcurrent protection */
184                 | 0x00                  /* 8 FS think time FIXME ?? */
185                 | 0x00);                /* No port indicators */
186         descr->bPwrOn2PwrGood = 0;
187         descr->bHubContrCurrent = 0;
188         /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
189         memset(&descr->bitmap[0], 0, temp);
190         memset(&descr->bitmap[temp], 0xff, temp);
191         return 0;
192 }
193
194 /*
195  * Clear a hub feature
196  *
197  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
198  *
199  * Nothing to do, so no locking needed ;)
200  */
201 static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature)
202 {
203         int result;
204         struct device *dev = wusbhc->dev;
205
206         d_fnstart(4, dev, "(%p, feature 0x%04u)\n", wusbhc, feature);
207         switch (feature) {
208         case C_HUB_LOCAL_POWER:
209                 /* FIXME: maybe plug bit 0 to the power input status,
210                  * if any?
211                  * see wusbhc_rh_get_hub_status() */
212         case C_HUB_OVER_CURRENT:
213                 result = 0;
214                 break;
215         default:
216                 result = -EPIPE;
217         }
218         d_fnend(4, dev, "(%p, feature 0x%04u), %d\n", wusbhc, feature, result);
219         return result;
220 }
221
222 /*
223  * Return hub status (it is always zero...)
224  *
225  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
226  *
227  * Nothing to do, so no locking needed ;)
228  */
229 static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf,
230                                     u16 wLength)
231 {
232         /* FIXME: maybe plug bit 0 to the power input status (if any)? */
233         *buf = 0;
234         return 0;
235 }
236
237 /*
238  * Set a port feature
239  *
240  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
241  */
242 static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature,
243                                    u8 selector, u8 port_idx)
244 {
245         int result = -EINVAL;
246         struct device *dev = wusbhc->dev;
247
248         d_fnstart(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d)\n",
249                   feature, selector, port_idx);
250
251         if (port_idx > wusbhc->ports_max)
252                 goto error;
253
254         switch (feature) {
255                 /* According to USB2.0[11.24.2.13]p2, these features
256                  * are not required to be implemented. */
257         case USB_PORT_FEAT_C_OVER_CURRENT:
258         case USB_PORT_FEAT_C_ENABLE:
259         case USB_PORT_FEAT_C_SUSPEND:
260         case USB_PORT_FEAT_C_CONNECTION:
261         case USB_PORT_FEAT_C_RESET:
262                 result = 0;
263                 break;
264
265         case USB_PORT_FEAT_POWER:
266                 /* No such thing, but we fake it works */
267                 mutex_lock(&wusbhc->mutex);
268                 wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER;
269                 mutex_unlock(&wusbhc->mutex);
270                 result = 0;
271                 break;
272         case USB_PORT_FEAT_RESET:
273                 result = wusbhc_rh_port_reset(wusbhc, port_idx);
274                 break;
275         case USB_PORT_FEAT_ENABLE:
276         case USB_PORT_FEAT_SUSPEND:
277                 dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n",
278                         port_idx, feature, selector);
279                 result = -ENOSYS;
280                 break;
281         default:
282                 dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n",
283                         port_idx, feature, selector);
284                 result = -EPIPE;
285                 break;
286         }
287 error:
288         d_fnend(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d) = %d\n",
289                 feature, selector, port_idx, result);
290         return result;
291 }
292
293 /*
294  * Clear a port feature...
295  *
296  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
297  */
298 static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature,
299                                      u8 selector, u8 port_idx)
300 {
301         int result = -EINVAL;
302         struct device *dev = wusbhc->dev;
303
304         d_fnstart(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d)\n",
305                   wusbhc, feature, selector, port_idx);
306
307         if (port_idx > wusbhc->ports_max)
308                 goto error;
309
310         mutex_lock(&wusbhc->mutex);
311         result = 0;
312         switch (feature) {
313         case USB_PORT_FEAT_POWER:       /* fake port always on */
314                 /* According to USB2.0[11.24.2.7.1.4], no need to implement? */
315         case USB_PORT_FEAT_C_OVER_CURRENT:
316                 break;
317         case USB_PORT_FEAT_C_RESET:
318                 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET;
319                 break;
320         case USB_PORT_FEAT_C_CONNECTION:
321                 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION;
322                 break;
323         case USB_PORT_FEAT_ENABLE:
324                 __wusbhc_dev_disable(wusbhc, port_idx);
325                 break;
326         case USB_PORT_FEAT_C_ENABLE:
327                 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE;
328                 break;
329         case USB_PORT_FEAT_SUSPEND:
330         case USB_PORT_FEAT_C_SUSPEND:
331         case 0xffff:            /* ??? FIXME */
332                 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n",
333                         port_idx, feature, selector);
334                 /* dump_stack(); */
335                 result = -ENOSYS;
336                 break;
337         default:
338                 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n",
339                         port_idx, feature, selector);
340                 result = -EPIPE;
341                 break;
342         }
343         mutex_unlock(&wusbhc->mutex);
344 error:
345         d_fnend(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d) = "
346                 "%d\n", wusbhc, feature, selector, port_idx, result);
347         return result;
348 }
349
350 /*
351  * Return the port's status
352  *
353  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
354  */
355 static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx,
356                                      u32 *_buf, u16 wLength)
357 {
358         int result = -EINVAL;
359         u16 *buf = (u16 *) _buf;
360
361         d_fnstart(1, wusbhc->dev, "(wusbhc %p port_idx %u wLength %u)\n",
362                   wusbhc, port_idx, wLength);
363         if (port_idx > wusbhc->ports_max)
364                 goto error;
365         mutex_lock(&wusbhc->mutex);
366         buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status);
367         buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change);
368         result = 0;
369         mutex_unlock(&wusbhc->mutex);
370 error:
371         d_fnend(1, wusbhc->dev, "(wusbhc %p) = %d, buffer:\n", wusbhc, result);
372         d_dump(1, wusbhc->dev, _buf, wLength);
373         return result;
374 }
375
376 /*
377  * Entry point for Root Hub operations
378  *
379  * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
380  */
381 int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue,
382                       u16 wIndex, char *buf, u16 wLength)
383 {
384         int result = -ENOSYS;
385         struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
386
387         switch (reqntype) {
388         case GetHubDescriptor:
389                 result = wusbhc_rh_get_hub_descr(
390                         wusbhc, wValue, wIndex,
391                         (struct usb_hub_descriptor *) buf, wLength);
392                 break;
393         case ClearHubFeature:
394                 result = wusbhc_rh_clear_hub_feat(wusbhc, wValue);
395                 break;
396         case GetHubStatus:
397                 result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength);
398                 break;
399
400         case SetPortFeature:
401                 result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8,
402                                                  (wIndex & 0xff) - 1);
403                 break;
404         case ClearPortFeature:
405                 result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8,
406                                                    (wIndex & 0xff) - 1);
407                 break;
408         case GetPortStatus:
409                 result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1,
410                                                    (u32 *)buf, wLength);
411                 break;
412
413         case SetHubFeature:
414         default:
415                 dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) "
416                         "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype,
417                         wValue, wIndex, buf, wLength);
418                 /* dump_stack(); */
419                 result = -ENOSYS;
420         }
421         return result;
422 }
423 EXPORT_SYMBOL_GPL(wusbhc_rh_control);
424
425 int wusbhc_rh_suspend(struct usb_hcd *usb_hcd)
426 {
427         struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
428         dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
429                 usb_hcd, wusbhc);
430         /* dump_stack(); */
431         return -ENOSYS;
432 }
433 EXPORT_SYMBOL_GPL(wusbhc_rh_suspend);
434
435 int wusbhc_rh_resume(struct usb_hcd *usb_hcd)
436 {
437         struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
438         dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
439                 usb_hcd, wusbhc);
440         /* dump_stack(); */
441         return -ENOSYS;
442 }
443 EXPORT_SYMBOL_GPL(wusbhc_rh_resume);
444
445 int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx)
446 {
447         struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
448         dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n",
449                 __func__, usb_hcd, wusbhc, port_idx);
450         WARN_ON(1);
451         return -ENOSYS;
452 }
453 EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset);
454
455 static void wusb_port_init(struct wusb_port *port)
456 {
457         port->status |= USB_PORT_STAT_HIGH_SPEED;
458 }
459
460 /*
461  * Alloc fake port specific fields and status.
462  */
463 int wusbhc_rh_create(struct wusbhc *wusbhc)
464 {
465         int result = -ENOMEM;
466         size_t port_size, itr;
467         port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]);
468         wusbhc->port = kzalloc(port_size, GFP_KERNEL);
469         if (wusbhc->port == NULL)
470                 goto error_port_alloc;
471         for (itr = 0; itr < wusbhc->ports_max; itr++)
472                 wusb_port_init(&wusbhc->port[itr]);
473         result = 0;
474 error_port_alloc:
475         return result;
476 }
477
478 void wusbhc_rh_destroy(struct wusbhc *wusbhc)
479 {
480         kfree(wusbhc->port);
481 }