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