root/drivers/staging/wusbcore/rh.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wusbhc_rh_port_reset
  2. wusbhc_rh_status_data
  3. wusbhc_rh_get_hub_descr
  4. wusbhc_rh_clear_hub_feat
  5. wusbhc_rh_get_hub_status
  6. wusbhc_rh_set_port_feat
  7. wusbhc_rh_clear_port_feat
  8. wusbhc_rh_get_port_status
  9. wusbhc_rh_control
  10. wusbhc_rh_start_port_reset
  11. wusb_port_init
  12. wusbhc_rh_create
  13. wusbhc_rh_destroy

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

/* [<][>][^][v][top][bottom][index][help] */