uboot/drivers/usb/emul/sandbox_hub.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2015 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <usb.h>
  10#include <dm/device-internal.h>
  11
  12/* We only support up to 8 */
  13#define SANDBOX_NUM_PORTS       4
  14
  15struct sandbox_hub_platdata {
  16        struct usb_dev_platdata plat;
  17        int port;       /* Port number (numbered from 0) */
  18};
  19
  20enum {
  21        STRING_MANUFACTURER = 1,
  22        STRING_PRODUCT,
  23        STRING_SERIAL,
  24
  25        STRING_count,
  26};
  27
  28static struct usb_string hub_strings[] = {
  29        {STRING_MANUFACTURER,   "sandbox"},
  30        {STRING_PRODUCT,        "hub"},
  31        {STRING_SERIAL,         "2345"},
  32        {},
  33};
  34
  35static struct usb_device_descriptor hub_device_desc = {
  36        .bLength =              sizeof(hub_device_desc),
  37        .bDescriptorType =      USB_DT_DEVICE,
  38
  39        .bcdUSB =               __constant_cpu_to_le16(0x0200),
  40
  41        .bDeviceClass =         USB_CLASS_HUB,
  42        .bDeviceSubClass =      0,
  43        .bDeviceProtocol =      0,
  44
  45        .idVendor =             __constant_cpu_to_le16(0x1234),
  46        .idProduct =            __constant_cpu_to_le16(0x5678),
  47        .iManufacturer =        STRING_MANUFACTURER,
  48        .iProduct =             STRING_PRODUCT,
  49        .iSerialNumber =        STRING_SERIAL,
  50        .bNumConfigurations =   1,
  51};
  52
  53static struct usb_config_descriptor hub_config1 = {
  54        .bLength                = sizeof(hub_config1),
  55        .bDescriptorType        = USB_DT_CONFIG,
  56
  57        /* wTotalLength is set up by usb-emul-uclass */
  58        .bNumInterfaces         = 1,
  59        .bConfigurationValue    = 0,
  60        .iConfiguration         = 0,
  61        .bmAttributes           = 1 << 7,
  62        .bMaxPower              = 50,
  63};
  64
  65static struct usb_interface_descriptor hub_interface0 = {
  66        .bLength                = sizeof(hub_interface0),
  67        .bDescriptorType        = USB_DT_INTERFACE,
  68
  69        .bInterfaceNumber       = 0,
  70        .bAlternateSetting      = 0,
  71        .bNumEndpoints          = 1,
  72        .bInterfaceClass        = USB_CLASS_HUB,
  73        .bInterfaceSubClass     = 0,
  74        .bInterfaceProtocol     = US_PR_CB,
  75        .iInterface             = 0,
  76};
  77
  78static struct usb_endpoint_descriptor hub_endpoint0_in = {
  79        .bLength                = USB_DT_ENDPOINT_SIZE,
  80        .bDescriptorType        = USB_DT_ENDPOINT,
  81
  82        .bEndpointAddress       = 1 | USB_DIR_IN,
  83        .bmAttributes           = USB_ENDPOINT_XFER_INT,
  84        .wMaxPacketSize         = __constant_cpu_to_le16(1024),
  85        .bInterval              = 0,
  86};
  87
  88static struct usb_hub_descriptor hub_desc = {
  89        .bLength                = sizeof(hub_desc),
  90        .bDescriptorType        = USB_DT_HUB,
  91        .bNbrPorts              = SANDBOX_NUM_PORTS,
  92        .wHubCharacteristics    = __constant_cpu_to_le16(1 << 0 | 1 << 3 |
  93                                                                1 << 7),
  94        .bPwrOn2PwrGood         = 2,
  95        .bHubContrCurrent       = 5,
  96        {
  97                {
  98                        /* all ports removeable */
  99                        .DeviceRemovable        = {0, 0xff}
 100                }
 101        }
 102#if SANDBOX_NUM_PORTS > 8
 103#error "This code sets up an incorrect mask"
 104#endif
 105};
 106
 107static void *hub_desc_list[] = {
 108        &hub_device_desc,
 109        &hub_config1,
 110        &hub_interface0,
 111        &hub_endpoint0_in,
 112        &hub_desc,
 113        NULL,
 114};
 115
 116struct sandbox_hub_priv {
 117        int status[SANDBOX_NUM_PORTS];
 118        int change[SANDBOX_NUM_PORTS];
 119};
 120
 121static struct udevice *hub_find_device(struct udevice *hub, int port,
 122                                       enum usb_device_speed *speed)
 123{
 124        struct udevice *dev;
 125        struct usb_generic_descriptor **gen_desc;
 126        struct usb_device_descriptor **dev_desc;
 127
 128        for (device_find_first_child(hub, &dev);
 129             dev;
 130             device_find_next_child(&dev)) {
 131                struct sandbox_hub_platdata *plat;
 132
 133                plat = dev_get_parent_platdata(dev);
 134                if (plat->port == port) {
 135                        gen_desc = plat->plat.desc_list;
 136                        gen_desc = usb_emul_find_descriptor(gen_desc,
 137                                                            USB_DT_DEVICE, 0);
 138                        dev_desc = (struct usb_device_descriptor **)gen_desc;
 139
 140                        switch (le16_to_cpu((*dev_desc)->bcdUSB)) {
 141                        case 0x0100:
 142                                *speed = USB_SPEED_LOW;
 143                                break;
 144                        case 0x0101:
 145                                *speed = USB_SPEED_FULL;
 146                                break;
 147                        case 0x0200:
 148                        default:
 149                                *speed = USB_SPEED_HIGH;
 150                                break;
 151                        }
 152
 153                        return dev;
 154                }
 155        }
 156
 157        return NULL;
 158}
 159
 160static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
 161{
 162        struct sandbox_hub_priv *priv = dev_get_priv(hub);
 163        int *status = &priv->status[port];
 164        int *change = &priv->change[port];
 165        int ret = 0;
 166
 167        if ((clear | set) & USB_PORT_STAT_POWER) {
 168                enum usb_device_speed speed;
 169                struct udevice *dev = hub_find_device(hub, port, &speed);
 170
 171                if (dev) {
 172                        if (set & USB_PORT_STAT_POWER) {
 173                                ret = device_probe(dev);
 174                                debug("%s: %s: power on, probed, ret=%d\n",
 175                                      __func__, dev->name, ret);
 176                                if (!ret) {
 177                                        set |= USB_PORT_STAT_CONNECTION |
 178                                                USB_PORT_STAT_ENABLE;
 179                                        if (speed == USB_SPEED_LOW)
 180                                                set |= USB_PORT_STAT_LOW_SPEED;
 181                                        else if (speed == USB_SPEED_HIGH)
 182                                                set |= USB_PORT_STAT_HIGH_SPEED;
 183                                }
 184
 185                        } else if (clear & USB_PORT_STAT_POWER) {
 186                                debug("%s: %s: power off, removed, ret=%d\n",
 187                                      __func__, dev->name, ret);
 188                                ret = device_remove(dev, DM_REMOVE_NORMAL);
 189                                clear |= USB_PORT_STAT_CONNECTION;
 190                        }
 191                }
 192        }
 193        *change |= *status & clear;
 194        *change |= ~*status & set;
 195        *change &= 0x1f;
 196        *status = (*status & ~clear) | set;
 197
 198        return ret;
 199}
 200
 201static int sandbox_hub_submit_control_msg(struct udevice *bus,
 202                                          struct usb_device *udev,
 203                                          unsigned long pipe,
 204                                          void *buffer, int length,
 205                                          struct devrequest *setup)
 206{
 207        struct sandbox_hub_priv *priv = dev_get_priv(bus);
 208        int ret = 0;
 209
 210        if (pipe == usb_rcvctrlpipe(udev, 0)) {
 211                switch (setup->requesttype) {
 212                case USB_RT_HUB | USB_DIR_IN:
 213                        switch (setup->request) {
 214                        case USB_REQ_GET_STATUS: {
 215                                struct usb_hub_status *hubsts = buffer;
 216
 217                                hubsts->wHubStatus = 0;
 218                                hubsts->wHubChange = 0;
 219                                udev->status = 0;
 220                                udev->act_len = sizeof(*hubsts);
 221                                return 0;
 222                        }
 223                        default:
 224                                debug("%s: rx ctl requesttype=%x, request=%x\n",
 225                                      __func__, setup->requesttype,
 226                                      setup->request);
 227                                break;
 228                        }
 229                case USB_RT_PORT | USB_DIR_IN:
 230                        switch (setup->request) {
 231                        case USB_REQ_GET_STATUS: {
 232                                struct usb_port_status *portsts = buffer;
 233                                int port;
 234
 235                                port = (setup->index & USB_HUB_PORT_MASK) - 1;
 236                                portsts->wPortStatus = priv->status[port];
 237                                portsts->wPortChange = priv->change[port];
 238                                udev->status = 0;
 239                                udev->act_len = sizeof(*portsts);
 240                                return 0;
 241                        }
 242                        }
 243                default:
 244                        debug("%s: rx ctl requesttype=%x, request=%x\n",
 245                              __func__, setup->requesttype, setup->request);
 246                        break;
 247                }
 248        } else if (pipe == usb_sndctrlpipe(udev, 0)) {
 249                switch (setup->requesttype) {
 250                case USB_RT_PORT:
 251                        switch (setup->request) {
 252                        case USB_REQ_SET_FEATURE: {
 253                                int port;
 254
 255                                port = (setup->index & USB_HUB_PORT_MASK) - 1;
 256                                debug("set feature port=%x, feature=%x\n",
 257                                      port, setup->value);
 258                                if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
 259                                        ret = clrset_post_state(bus, port, 0,
 260                                                        1 << setup->value);
 261                                } else {
 262                                        debug("  ** Invalid feature\n");
 263                                }
 264                                return ret;
 265                        }
 266                        case USB_REQ_CLEAR_FEATURE: {
 267                                int port;
 268
 269                                port = (setup->index & USB_HUB_PORT_MASK) - 1;
 270                                debug("clear feature port=%x, feature=%x\n",
 271                                      port, setup->value);
 272                                if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
 273                                        ret = clrset_post_state(bus, port,
 274                                                        1 << setup->value, 0);
 275                                } else {
 276                                        priv->change[port] &= 1 <<
 277                                                (setup->value - 16);
 278                                }
 279                                udev->status = 0;
 280                                return 0;
 281                        }
 282                        default:
 283                                debug("%s: tx ctl requesttype=%x, request=%x\n",
 284                                      __func__, setup->requesttype,
 285                                      setup->request);
 286                                break;
 287                        }
 288                default:
 289                        debug("%s: tx ctl requesttype=%x, request=%x\n",
 290                              __func__, setup->requesttype, setup->request);
 291                        break;
 292                }
 293        }
 294        debug("pipe=%lx\n", pipe);
 295
 296        return -EIO;
 297}
 298
 299static int sandbox_hub_bind(struct udevice *dev)
 300{
 301        return usb_emul_setup_device(dev, hub_strings, hub_desc_list);
 302}
 303
 304static int sandbox_child_post_bind(struct udevice *dev)
 305{
 306        struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev);
 307        struct usb_emul_platdata *emul = dev_get_uclass_platdata(dev);
 308
 309        plat->port = dev_read_u32_default(dev, "reg", -1);
 310        emul->port1 = plat->port + 1;
 311
 312        return 0;
 313}
 314
 315static const struct dm_usb_ops sandbox_usb_hub_ops = {
 316        .control        = sandbox_hub_submit_control_msg,
 317};
 318
 319static const struct udevice_id sandbox_usb_hub_ids[] = {
 320        { .compatible = "sandbox,usb-hub" },
 321        { }
 322};
 323
 324U_BOOT_DRIVER(usb_sandbox_hub) = {
 325        .name   = "usb_sandbox_hub",
 326        .id     = UCLASS_USB_EMUL,
 327        .of_match = sandbox_usb_hub_ids,
 328        .bind   = sandbox_hub_bind,
 329        .ops    = &sandbox_usb_hub_ops,
 330        .priv_auto_alloc_size = sizeof(struct sandbox_hub_priv),
 331        .per_child_platdata_auto_alloc_size =
 332                        sizeof(struct sandbox_hub_platdata),
 333        .child_post_bind = sandbox_child_post_bind,
 334};
 335