11d4a0b6cSLukasz Majewski /* 21d4a0b6cSLukasz Majewski * g_dnl.c -- USB Downloader Gadget 31d4a0b6cSLukasz Majewski * 41d4a0b6cSLukasz Majewski * Copyright (C) 2012 Samsung Electronics 51d4a0b6cSLukasz Majewski * Lukasz Majewski <l.majewski@samsung.com> 61d4a0b6cSLukasz Majewski * 71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 81d4a0b6cSLukasz Majewski */ 91d4a0b6cSLukasz Majewski 101d4a0b6cSLukasz Majewski #include <common.h> 111d4a0b6cSLukasz Majewski #include <malloc.h> 121d4a0b6cSLukasz Majewski 131d4a0b6cSLukasz Majewski #include <mmc.h> 141d4a0b6cSLukasz Majewski #include <part.h> 151d4a0b6cSLukasz Majewski 161d4a0b6cSLukasz Majewski #include <g_dnl.h> 17ba4e95c9SLukasz Majewski #include <usb_mass_storage.h> 18a6921adcSLukasz Majewski #include <dfu.h> 19b958fb91SLukasz Majewski #include <thor.h> 201d4a0b6cSLukasz Majewski 211d4a0b6cSLukasz Majewski #include "gadget_chips.h" 221d4a0b6cSLukasz Majewski #include "composite.c" 231d4a0b6cSLukasz Majewski 241d4a0b6cSLukasz Majewski /* 251d4a0b6cSLukasz Majewski * One needs to define the following: 261d4a0b6cSLukasz Majewski * CONFIG_G_DNL_VENDOR_NUM 271d4a0b6cSLukasz Majewski * CONFIG_G_DNL_PRODUCT_NUM 281d4a0b6cSLukasz Majewski * CONFIG_G_DNL_MANUFACTURER 291d4a0b6cSLukasz Majewski * at e.g. ./include/configs/<board>.h 301d4a0b6cSLukasz Majewski */ 311d4a0b6cSLukasz Majewski 321d4a0b6cSLukasz Majewski #define STRING_MANUFACTURER 25 331d4a0b6cSLukasz Majewski #define STRING_PRODUCT 2 34cfc2d0d6SLukasz Majewski /* Index of String Descriptor describing this configuration */ 351d4a0b6cSLukasz Majewski #define STRING_USBDOWN 2 36ec9002e4SHeiko Schocher /* Index of String serial */ 37ec9002e4SHeiko Schocher #define STRING_SERIAL 3 38ec9002e4SHeiko Schocher #define MAX_STRING_SERIAL 32 39cfc2d0d6SLukasz Majewski /* Number of supported configurations */ 40cfc2d0d6SLukasz Majewski #define CONFIGURATION_NUMBER 1 411d4a0b6cSLukasz Majewski 421d4a0b6cSLukasz Majewski #define DRIVER_VERSION "usb_dnl 2.0" 431d4a0b6cSLukasz Majewski 441d4a0b6cSLukasz Majewski static const char shortname[] = "usb_dnl_"; 451d4a0b6cSLukasz Majewski static const char product[] = "USB download gadget"; 46ec9002e4SHeiko Schocher static char g_dnl_serial[MAX_STRING_SERIAL]; 471d4a0b6cSLukasz Majewski static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; 481d4a0b6cSLukasz Majewski 49ec9002e4SHeiko Schocher void g_dnl_set_serialnumber(char *s) 50ec9002e4SHeiko Schocher { 51ec9002e4SHeiko Schocher memset(g_dnl_serial, 0, MAX_STRING_SERIAL); 52ec9002e4SHeiko Schocher if (strlen(s) < MAX_STRING_SERIAL) 53ec9002e4SHeiko Schocher strncpy(g_dnl_serial, s, strlen(s)); 54ec9002e4SHeiko Schocher } 55ec9002e4SHeiko Schocher 561d4a0b6cSLukasz Majewski static struct usb_device_descriptor device_desc = { 571d4a0b6cSLukasz Majewski .bLength = sizeof device_desc, 581d4a0b6cSLukasz Majewski .bDescriptorType = USB_DT_DEVICE, 591d4a0b6cSLukasz Majewski 601d4a0b6cSLukasz Majewski .bcdUSB = __constant_cpu_to_le16(0x0200), 611d4a0b6cSLukasz Majewski .bDeviceClass = USB_CLASS_COMM, 621d4a0b6cSLukasz Majewski .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ 631d4a0b6cSLukasz Majewski 641d4a0b6cSLukasz Majewski .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), 651d4a0b6cSLukasz Majewski .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), 661d4a0b6cSLukasz Majewski .iProduct = STRING_PRODUCT, 67ec9002e4SHeiko Schocher .iSerialNumber = STRING_SERIAL, 681d4a0b6cSLukasz Majewski .bNumConfigurations = 1, 691d4a0b6cSLukasz Majewski }; 701d4a0b6cSLukasz Majewski 71c4219a82SLukasz Majewski /* 72c4219a82SLukasz Majewski * static strings, in UTF-8 73c4219a82SLukasz Majewski * IDs for those strings are assigned dynamically at g_dnl_bind() 74c4219a82SLukasz Majewski */ 751d4a0b6cSLukasz Majewski static struct usb_string g_dnl_string_defs[] = { 76c4219a82SLukasz Majewski {.s = manufacturer}, 77c4219a82SLukasz Majewski {.s = product}, 78ec9002e4SHeiko Schocher {.s = g_dnl_serial}, 79598cf606SPantelis Antoniou { } /* end of list */ 801d4a0b6cSLukasz Majewski }; 811d4a0b6cSLukasz Majewski 821d4a0b6cSLukasz Majewski static struct usb_gadget_strings g_dnl_string_tab = { 831d4a0b6cSLukasz Majewski .language = 0x0409, /* en-us */ 841d4a0b6cSLukasz Majewski .strings = g_dnl_string_defs, 851d4a0b6cSLukasz Majewski }; 861d4a0b6cSLukasz Majewski 871d4a0b6cSLukasz Majewski static struct usb_gadget_strings *g_dnl_composite_strings[] = { 881d4a0b6cSLukasz Majewski &g_dnl_string_tab, 891d4a0b6cSLukasz Majewski NULL, 901d4a0b6cSLukasz Majewski }; 911d4a0b6cSLukasz Majewski 921d4a0b6cSLukasz Majewski static int g_dnl_unbind(struct usb_composite_dev *cdev) 931d4a0b6cSLukasz Majewski { 945a413caeSPantelis Antoniou struct usb_gadget *gadget = cdev->gadget; 955a413caeSPantelis Antoniou 967b412ab3SLukasz Majewski free(cdev->config); 977b412ab3SLukasz Majewski cdev->config = NULL; 985a413caeSPantelis Antoniou debug("%s: calling usb_gadget_disconnect for " 995a413caeSPantelis Antoniou "controller '%s'\n", shortname, gadget->name); 1005a413caeSPantelis Antoniou usb_gadget_disconnect(gadget); 1015a413caeSPantelis Antoniou 1021d4a0b6cSLukasz Majewski return 0; 1031d4a0b6cSLukasz Majewski } 1041d4a0b6cSLukasz Majewski 1051d4a0b6cSLukasz Majewski static int g_dnl_do_config(struct usb_configuration *c) 1061d4a0b6cSLukasz Majewski { 1071d4a0b6cSLukasz Majewski const char *s = c->cdev->driver->name; 1081d4a0b6cSLukasz Majewski int ret = -1; 1091d4a0b6cSLukasz Majewski 1101d4a0b6cSLukasz Majewski debug("%s: configuration: 0x%p composite dev: 0x%p\n", 1111d4a0b6cSLukasz Majewski __func__, c, c->cdev); 1121d4a0b6cSLukasz Majewski 1131d4a0b6cSLukasz Majewski printf("GADGET DRIVER: %s\n", s); 1141d4a0b6cSLukasz Majewski if (!strcmp(s, "usb_dnl_dfu")) 1151d4a0b6cSLukasz Majewski ret = dfu_add(c); 116b528f713SLukasz Majewski else if (!strcmp(s, "usb_dnl_ums")) 117b528f713SLukasz Majewski ret = fsg_add(c); 118b958fb91SLukasz Majewski else if (!strcmp(s, "usb_dnl_thor")) 119b958fb91SLukasz Majewski ret = thor_add(c); 1201d4a0b6cSLukasz Majewski 1211d4a0b6cSLukasz Majewski return ret; 1221d4a0b6cSLukasz Majewski } 1231d4a0b6cSLukasz Majewski 1241d4a0b6cSLukasz Majewski static int g_dnl_config_register(struct usb_composite_dev *cdev) 1251d4a0b6cSLukasz Majewski { 1267b412ab3SLukasz Majewski struct usb_configuration *config; 1277b412ab3SLukasz Majewski const char *name = "usb_dnload"; 1281d4a0b6cSLukasz Majewski 1297b412ab3SLukasz Majewski config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); 1307b412ab3SLukasz Majewski if (!config) 1317b412ab3SLukasz Majewski return -ENOMEM; 1321d4a0b6cSLukasz Majewski 1337b412ab3SLukasz Majewski memset(config, 0, sizeof(*config)); 1347b412ab3SLukasz Majewski 1357b412ab3SLukasz Majewski config->label = name; 1367b412ab3SLukasz Majewski config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; 1377b412ab3SLukasz Majewski config->bConfigurationValue = CONFIGURATION_NUMBER; 1387b412ab3SLukasz Majewski config->iConfiguration = STRING_USBDOWN; 1397b412ab3SLukasz Majewski config->bind = g_dnl_do_config; 1407b412ab3SLukasz Majewski 1417b412ab3SLukasz Majewski return usb_add_config(cdev, config); 1421d4a0b6cSLukasz Majewski } 1431d4a0b6cSLukasz Majewski 144c5398cc9SHeiko Schocher __weak 145d6eae7b0SLukasz Majewski int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) 146c5398cc9SHeiko Schocher { 147c5398cc9SHeiko Schocher return 0; 148c5398cc9SHeiko Schocher } 149c5398cc9SHeiko Schocher 150*7a0d463fSHeiko Schocher __weak int g_dnl_get_board_bcd_device_number(int gcnum) 151*7a0d463fSHeiko Schocher { 152*7a0d463fSHeiko Schocher return gcnum; 153*7a0d463fSHeiko Schocher } 154*7a0d463fSHeiko Schocher 155*7a0d463fSHeiko Schocher static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev) 156*7a0d463fSHeiko Schocher { 157*7a0d463fSHeiko Schocher struct usb_gadget *gadget = cdev->gadget; 158*7a0d463fSHeiko Schocher int gcnum; 159*7a0d463fSHeiko Schocher 160*7a0d463fSHeiko Schocher gcnum = usb_gadget_controller_number(gadget); 161*7a0d463fSHeiko Schocher if (gcnum > 0) 162*7a0d463fSHeiko Schocher gcnum += 0x200; 163*7a0d463fSHeiko Schocher 164*7a0d463fSHeiko Schocher return g_dnl_get_board_bcd_device_number(gcnum); 165*7a0d463fSHeiko Schocher } 166*7a0d463fSHeiko Schocher 1671d4a0b6cSLukasz Majewski static int g_dnl_bind(struct usb_composite_dev *cdev) 1681d4a0b6cSLukasz Majewski { 1691d4a0b6cSLukasz Majewski struct usb_gadget *gadget = cdev->gadget; 1701d4a0b6cSLukasz Majewski int id, ret; 1711d4a0b6cSLukasz Majewski int gcnum; 1721d4a0b6cSLukasz Majewski 1731d4a0b6cSLukasz Majewski debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); 1741d4a0b6cSLukasz Majewski 1751d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 1761d4a0b6cSLukasz Majewski 1771d4a0b6cSLukasz Majewski if (id < 0) 1781d4a0b6cSLukasz Majewski return id; 1791d4a0b6cSLukasz Majewski g_dnl_string_defs[0].id = id; 1801d4a0b6cSLukasz Majewski device_desc.iManufacturer = id; 1811d4a0b6cSLukasz Majewski 1821d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 1831d4a0b6cSLukasz Majewski if (id < 0) 1841d4a0b6cSLukasz Majewski return id; 1851d4a0b6cSLukasz Majewski 1861d4a0b6cSLukasz Majewski g_dnl_string_defs[1].id = id; 1871d4a0b6cSLukasz Majewski device_desc.iProduct = id; 1881d4a0b6cSLukasz Majewski 189ec9002e4SHeiko Schocher id = usb_string_id(cdev); 190ec9002e4SHeiko Schocher if (id < 0) 191ec9002e4SHeiko Schocher return id; 192ec9002e4SHeiko Schocher 193ec9002e4SHeiko Schocher g_dnl_string_defs[2].id = id; 194ec9002e4SHeiko Schocher device_desc.iSerialNumber = id; 195ec9002e4SHeiko Schocher 196d6eae7b0SLukasz Majewski g_dnl_bind_fixup(&device_desc, cdev->driver->name); 1971d4a0b6cSLukasz Majewski ret = g_dnl_config_register(cdev); 1981d4a0b6cSLukasz Majewski if (ret) 1991d4a0b6cSLukasz Majewski goto error; 2001d4a0b6cSLukasz Majewski 201*7a0d463fSHeiko Schocher gcnum = g_dnl_get_bcd_device_number(cdev); 2021d4a0b6cSLukasz Majewski if (gcnum >= 0) 203*7a0d463fSHeiko Schocher device_desc.bcdDevice = cpu_to_le16(gcnum); 2041d4a0b6cSLukasz Majewski else { 2051d4a0b6cSLukasz Majewski debug("%s: controller '%s' not recognized\n", 2061d4a0b6cSLukasz Majewski shortname, gadget->name); 2071d4a0b6cSLukasz Majewski device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); 2081d4a0b6cSLukasz Majewski } 2091d4a0b6cSLukasz Majewski 2105a413caeSPantelis Antoniou debug("%s: calling usb_gadget_connect for " 2115a413caeSPantelis Antoniou "controller '%s'\n", shortname, gadget->name); 2125a413caeSPantelis Antoniou usb_gadget_connect(gadget); 2135a413caeSPantelis Antoniou 2141d4a0b6cSLukasz Majewski return 0; 2151d4a0b6cSLukasz Majewski 2161d4a0b6cSLukasz Majewski error: 2171d4a0b6cSLukasz Majewski g_dnl_unbind(cdev); 2181d4a0b6cSLukasz Majewski return -ENOMEM; 2191d4a0b6cSLukasz Majewski } 2201d4a0b6cSLukasz Majewski 2211d4a0b6cSLukasz Majewski static struct usb_composite_driver g_dnl_driver = { 2221d4a0b6cSLukasz Majewski .name = NULL, 2231d4a0b6cSLukasz Majewski .dev = &device_desc, 2241d4a0b6cSLukasz Majewski .strings = g_dnl_composite_strings, 2251d4a0b6cSLukasz Majewski 2261d4a0b6cSLukasz Majewski .bind = g_dnl_bind, 2271d4a0b6cSLukasz Majewski .unbind = g_dnl_unbind, 2281d4a0b6cSLukasz Majewski }; 2291d4a0b6cSLukasz Majewski 2301d4a0b6cSLukasz Majewski int g_dnl_register(const char *type) 2311d4a0b6cSLukasz Majewski { 232b958fb91SLukasz Majewski /* The largest function name is 4 */ 233b958fb91SLukasz Majewski static char name[sizeof(shortname) + 4]; 2341d4a0b6cSLukasz Majewski int ret; 2351d4a0b6cSLukasz Majewski 2361d4a0b6cSLukasz Majewski if (!strcmp(type, "dfu")) { 2371d4a0b6cSLukasz Majewski strcpy(name, shortname); 2381d4a0b6cSLukasz Majewski strcat(name, type); 239b528f713SLukasz Majewski } else if (!strcmp(type, "ums")) { 240b528f713SLukasz Majewski strcpy(name, shortname); 241b528f713SLukasz Majewski strcat(name, type); 242b958fb91SLukasz Majewski } else if (!strcmp(type, "thor")) { 243b958fb91SLukasz Majewski strcpy(name, shortname); 244b958fb91SLukasz Majewski strcat(name, type); 2451d4a0b6cSLukasz Majewski } else { 2461d4a0b6cSLukasz Majewski printf("%s: unknown command: %s\n", __func__, type); 2471d4a0b6cSLukasz Majewski return -EINVAL; 2481d4a0b6cSLukasz Majewski } 2491d4a0b6cSLukasz Majewski 2501d4a0b6cSLukasz Majewski g_dnl_driver.name = name; 2511d4a0b6cSLukasz Majewski 2521d4a0b6cSLukasz Majewski debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); 2531d4a0b6cSLukasz Majewski ret = usb_composite_register(&g_dnl_driver); 2541d4a0b6cSLukasz Majewski 2551d4a0b6cSLukasz Majewski if (ret) { 2561d4a0b6cSLukasz Majewski printf("%s: failed!, error: %d\n", __func__, ret); 2571d4a0b6cSLukasz Majewski return ret; 2581d4a0b6cSLukasz Majewski } 2591d4a0b6cSLukasz Majewski 2601d4a0b6cSLukasz Majewski return 0; 2611d4a0b6cSLukasz Majewski } 2621d4a0b6cSLukasz Majewski 2631d4a0b6cSLukasz Majewski void g_dnl_unregister(void) 2641d4a0b6cSLukasz Majewski { 2651d4a0b6cSLukasz Majewski usb_composite_unregister(&g_dnl_driver); 2661d4a0b6cSLukasz Majewski } 267