uboot/drivers/usb/gadget/g_dnl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * g_dnl.c -- USB Downloader Gadget
   4 *
   5 * Copyright (C) 2012 Samsung Electronics
   6 * Lukasz Majewski  <l.majewski@samsung.com>
   7 */
   8
   9#include <common.h>
  10#include <log.h>
  11#include <malloc.h>
  12
  13#include <mmc.h>
  14#include <part.h>
  15#include <usb.h>
  16
  17#include <g_dnl.h>
  18#include <usb_mass_storage.h>
  19#include <dfu.h>
  20#include <thor.h>
  21
  22#include <env_callback.h>
  23
  24#include "gadget_chips.h"
  25#include "composite.c"
  26
  27/*
  28 * One needs to define the following:
  29 * CONFIG_USB_GADGET_VENDOR_NUM
  30 * CONFIG_USB_GADGET_PRODUCT_NUM
  31 * CONFIG_USB_GADGET_MANUFACTURER
  32 * at e.g. ./configs/<board>_defconfig
  33 */
  34
  35#define STRING_MANUFACTURER 25
  36#define STRING_PRODUCT 2
  37/* Index of String Descriptor describing this configuration */
  38#define STRING_USBDOWN 2
  39/* Index of String serial */
  40#define STRING_SERIAL  3
  41#define MAX_STRING_SERIAL       256
  42/* Number of supported configurations */
  43#define CONFIGURATION_NUMBER 1
  44
  45#define DRIVER_VERSION          "usb_dnl 2.0"
  46
  47static const char product[] = "USB download gadget";
  48static char g_dnl_serial[MAX_STRING_SERIAL];
  49static const char manufacturer[] = CONFIG_USB_GADGET_MANUFACTURER;
  50
  51void g_dnl_set_serialnumber(char *s)
  52{
  53        memset(g_dnl_serial, 0, MAX_STRING_SERIAL);
  54        strncpy(g_dnl_serial, s, MAX_STRING_SERIAL - 1);
  55}
  56
  57static struct usb_device_descriptor device_desc = {
  58        .bLength = sizeof device_desc,
  59        .bDescriptorType = USB_DT_DEVICE,
  60
  61        .bcdUSB = __constant_cpu_to_le16(0x0200),
  62        .bDeviceClass = USB_CLASS_PER_INTERFACE,
  63        .bDeviceSubClass = 0, /*0x02:CDC-modem , 0x00:CDC-serial*/
  64
  65        .idVendor = __constant_cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM),
  66        .idProduct = __constant_cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM),
  67        /* .iProduct = DYNAMIC */
  68        /* .iSerialNumber = DYNAMIC */
  69        .bNumConfigurations = 1,
  70};
  71
  72/*
  73 * static strings, in UTF-8
  74 * IDs for those strings are assigned dynamically at g_dnl_bind()
  75 */
  76static struct usb_string g_dnl_string_defs[] = {
  77        {.s = manufacturer},
  78        {.s = product},
  79        {.s = g_dnl_serial},
  80        { }             /* end of list */
  81};
  82
  83static struct usb_gadget_strings g_dnl_string_tab = {
  84        .language = 0x0409, /* en-us */
  85        .strings = g_dnl_string_defs,
  86};
  87
  88static struct usb_gadget_strings *g_dnl_composite_strings[] = {
  89        &g_dnl_string_tab,
  90        NULL,
  91};
  92
  93void g_dnl_set_product(const char *s)
  94{
  95        if (s)
  96                g_dnl_string_defs[1].s = s;
  97        else
  98                g_dnl_string_defs[1].s = product;
  99}
 100
 101static int g_dnl_unbind(struct usb_composite_dev *cdev)
 102{
 103        struct usb_gadget *gadget = cdev->gadget;
 104
 105        debug("%s: calling usb_gadget_disconnect for "
 106                        "controller '%s'\n", __func__, gadget->name);
 107        usb_gadget_disconnect(gadget);
 108
 109        return 0;
 110}
 111
 112static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void)
 113{
 114        return ll_entry_start(struct g_dnl_bind_callback,
 115                                g_dnl_bind_callbacks);
 116}
 117
 118static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void)
 119{
 120        return ll_entry_end(struct g_dnl_bind_callback,
 121                                g_dnl_bind_callbacks);
 122}
 123
 124static int g_dnl_do_config(struct usb_configuration *c)
 125{
 126        const char *s = c->cdev->driver->name;
 127        struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first();
 128
 129        debug("%s: configuration: 0x%p composite dev: 0x%p\n",
 130              __func__, c, c->cdev);
 131
 132        for (; callback != g_dnl_bind_callback_end(); callback++)
 133                if (!strcmp(s, callback->usb_function_name))
 134                        return callback->fptr(c);
 135        return -ENODEV;
 136}
 137
 138static int g_dnl_config_register(struct usb_composite_dev *cdev)
 139{
 140        struct usb_configuration *config;
 141        const char *name = "usb_dnload";
 142
 143        config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config));
 144        if (!config)
 145                return -ENOMEM;
 146
 147        memset(config, 0, sizeof(*config));
 148
 149        config->label = name;
 150        config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
 151        config->bConfigurationValue = CONFIGURATION_NUMBER;
 152        config->iConfiguration = STRING_USBDOWN;
 153        config->bind = g_dnl_do_config;
 154
 155        return usb_add_config(cdev, config);
 156}
 157
 158__weak
 159int board_usb_init(int index, enum usb_init_type init)
 160{
 161        return 0;
 162}
 163
 164__weak
 165int board_usb_cleanup(int index, enum usb_init_type init)
 166{
 167        return 0;
 168}
 169
 170__weak
 171int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
 172{
 173        return 0;
 174}
 175
 176__weak int g_dnl_get_board_bcd_device_number(int gcnum)
 177{
 178        return gcnum;
 179}
 180
 181__weak int g_dnl_board_usb_cable_connected(void)
 182{
 183        return -EOPNOTSUPP;
 184}
 185
 186static bool g_dnl_detach_request;
 187
 188bool g_dnl_detach(void)
 189{
 190        return g_dnl_detach_request;
 191}
 192
 193void g_dnl_trigger_detach(void)
 194{
 195        g_dnl_detach_request = true;
 196}
 197
 198void g_dnl_clear_detach(void)
 199{
 200        g_dnl_detach_request = false;
 201}
 202
 203static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev)
 204{
 205        struct usb_gadget *gadget = cdev->gadget;
 206        int gcnum;
 207
 208        gcnum = usb_gadget_controller_number(gadget);
 209        if (gcnum > 0)
 210                gcnum += 0x200;
 211
 212        return g_dnl_get_board_bcd_device_number(gcnum);
 213}
 214
 215/**
 216 * Update internal serial number variable when the "serial#" env var changes.
 217 *
 218 * Handle all cases, even when flags == H_PROGRAMMATIC or op == env_op_delete.
 219 */
 220static int on_serialno(const char *name, const char *value, enum env_op op,
 221                int flags)
 222{
 223        g_dnl_set_serialnumber((char *)value);
 224        return 0;
 225}
 226U_BOOT_ENV_CALLBACK(serialno, on_serialno);
 227
 228static int g_dnl_bind(struct usb_composite_dev *cdev)
 229{
 230        struct usb_gadget *gadget = cdev->gadget;
 231        int id, ret;
 232        int gcnum;
 233
 234        debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
 235
 236        id = usb_string_id(cdev);
 237
 238        if (id < 0)
 239                return id;
 240        g_dnl_string_defs[0].id = id;
 241        device_desc.iManufacturer = id;
 242
 243        id = usb_string_id(cdev);
 244        if (id < 0)
 245                return id;
 246
 247        g_dnl_string_defs[1].id = id;
 248        device_desc.iProduct = id;
 249
 250        g_dnl_bind_fixup(&device_desc, cdev->driver->name);
 251
 252        if (strlen(g_dnl_serial)) {
 253                id = usb_string_id(cdev);
 254                if (id < 0)
 255                        return id;
 256
 257                g_dnl_string_defs[2].id = id;
 258                device_desc.iSerialNumber = id;
 259        }
 260
 261        ret = g_dnl_config_register(cdev);
 262        if (ret)
 263                goto error;
 264
 265        gcnum = g_dnl_get_bcd_device_number(cdev);
 266        if (gcnum >= 0)
 267                device_desc.bcdDevice = cpu_to_le16(gcnum);
 268        else {
 269                debug("%s: controller '%s' not recognized\n",
 270                        __func__, gadget->name);
 271                device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
 272        }
 273
 274        debug("%s: calling usb_gadget_connect for "
 275                        "controller '%s'\n", __func__, gadget->name);
 276        usb_gadget_connect(gadget);
 277
 278        return 0;
 279
 280 error:
 281        g_dnl_unbind(cdev);
 282        return -ENOMEM;
 283}
 284
 285static struct usb_composite_driver g_dnl_driver = {
 286        .name = NULL,
 287        .dev = &device_desc,
 288        .strings = g_dnl_composite_strings,
 289        .max_speed = USB_SPEED_SUPER,
 290
 291        .bind = g_dnl_bind,
 292        .unbind = g_dnl_unbind,
 293};
 294
 295/*
 296 * NOTICE:
 297 * Registering via USB function name won't be necessary after rewriting
 298 * g_dnl to support multiple USB functions.
 299 */
 300int g_dnl_register(const char *name)
 301{
 302        int ret;
 303
 304        debug("%s: g_dnl_driver.name = %s\n", __func__, name);
 305        g_dnl_driver.name = name;
 306
 307        ret = usb_composite_register(&g_dnl_driver);
 308        if (ret) {
 309                printf("%s: failed!, error: %d\n", __func__, ret);
 310                return ret;
 311        }
 312        return 0;
 313}
 314
 315void g_dnl_unregister(void)
 316{
 317        usb_composite_unregister(&g_dnl_driver);
 318}
 319