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> 191d4a0b6cSLukasz Majewski 201d4a0b6cSLukasz Majewski #include "gadget_chips.h" 211d4a0b6cSLukasz Majewski #include "composite.c" 221d4a0b6cSLukasz Majewski 231d4a0b6cSLukasz Majewski /* 241d4a0b6cSLukasz Majewski * One needs to define the following: 251d4a0b6cSLukasz Majewski * CONFIG_G_DNL_VENDOR_NUM 261d4a0b6cSLukasz Majewski * CONFIG_G_DNL_PRODUCT_NUM 271d4a0b6cSLukasz Majewski * CONFIG_G_DNL_MANUFACTURER 281d4a0b6cSLukasz Majewski * at e.g. ./include/configs/<board>.h 291d4a0b6cSLukasz Majewski */ 301d4a0b6cSLukasz Majewski 311d4a0b6cSLukasz Majewski #define STRING_MANUFACTURER 25 321d4a0b6cSLukasz Majewski #define STRING_PRODUCT 2 33cfc2d0d6SLukasz Majewski /* Index of String Descriptor describing this configuration */ 341d4a0b6cSLukasz Majewski #define STRING_USBDOWN 2 35cfc2d0d6SLukasz Majewski /* Number of supported configurations */ 36cfc2d0d6SLukasz Majewski #define CONFIGURATION_NUMBER 1 371d4a0b6cSLukasz Majewski 381d4a0b6cSLukasz Majewski #define DRIVER_VERSION "usb_dnl 2.0" 391d4a0b6cSLukasz Majewski 401d4a0b6cSLukasz Majewski static const char shortname[] = "usb_dnl_"; 411d4a0b6cSLukasz Majewski static const char product[] = "USB download gadget"; 421d4a0b6cSLukasz Majewski static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; 431d4a0b6cSLukasz Majewski 441d4a0b6cSLukasz Majewski static struct usb_device_descriptor device_desc = { 451d4a0b6cSLukasz Majewski .bLength = sizeof device_desc, 461d4a0b6cSLukasz Majewski .bDescriptorType = USB_DT_DEVICE, 471d4a0b6cSLukasz Majewski 481d4a0b6cSLukasz Majewski .bcdUSB = __constant_cpu_to_le16(0x0200), 491d4a0b6cSLukasz Majewski .bDeviceClass = USB_CLASS_COMM, 501d4a0b6cSLukasz Majewski .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ 511d4a0b6cSLukasz Majewski 521d4a0b6cSLukasz Majewski .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), 531d4a0b6cSLukasz Majewski .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), 541d4a0b6cSLukasz Majewski .iProduct = STRING_PRODUCT, 551d4a0b6cSLukasz Majewski .bNumConfigurations = 1, 561d4a0b6cSLukasz Majewski }; 571d4a0b6cSLukasz Majewski 58c4219a82SLukasz Majewski /* 59c4219a82SLukasz Majewski * static strings, in UTF-8 60c4219a82SLukasz Majewski * IDs for those strings are assigned dynamically at g_dnl_bind() 61c4219a82SLukasz Majewski */ 621d4a0b6cSLukasz Majewski static struct usb_string g_dnl_string_defs[] = { 63c4219a82SLukasz Majewski {.s = manufacturer}, 64c4219a82SLukasz Majewski {.s = product}, 65598cf606SPantelis Antoniou { } /* end of list */ 661d4a0b6cSLukasz Majewski }; 671d4a0b6cSLukasz Majewski 681d4a0b6cSLukasz Majewski static struct usb_gadget_strings g_dnl_string_tab = { 691d4a0b6cSLukasz Majewski .language = 0x0409, /* en-us */ 701d4a0b6cSLukasz Majewski .strings = g_dnl_string_defs, 711d4a0b6cSLukasz Majewski }; 721d4a0b6cSLukasz Majewski 731d4a0b6cSLukasz Majewski static struct usb_gadget_strings *g_dnl_composite_strings[] = { 741d4a0b6cSLukasz Majewski &g_dnl_string_tab, 751d4a0b6cSLukasz Majewski NULL, 761d4a0b6cSLukasz Majewski }; 771d4a0b6cSLukasz Majewski 781d4a0b6cSLukasz Majewski static int g_dnl_unbind(struct usb_composite_dev *cdev) 791d4a0b6cSLukasz Majewski { 805a413caeSPantelis Antoniou struct usb_gadget *gadget = cdev->gadget; 815a413caeSPantelis Antoniou 827b412ab3SLukasz Majewski free(cdev->config); 837b412ab3SLukasz Majewski cdev->config = NULL; 845a413caeSPantelis Antoniou debug("%s: calling usb_gadget_disconnect for " 855a413caeSPantelis Antoniou "controller '%s'\n", shortname, gadget->name); 865a413caeSPantelis Antoniou usb_gadget_disconnect(gadget); 875a413caeSPantelis Antoniou 881d4a0b6cSLukasz Majewski return 0; 891d4a0b6cSLukasz Majewski } 901d4a0b6cSLukasz Majewski 911d4a0b6cSLukasz Majewski static int g_dnl_do_config(struct usb_configuration *c) 921d4a0b6cSLukasz Majewski { 931d4a0b6cSLukasz Majewski const char *s = c->cdev->driver->name; 941d4a0b6cSLukasz Majewski int ret = -1; 951d4a0b6cSLukasz Majewski 961d4a0b6cSLukasz Majewski debug("%s: configuration: 0x%p composite dev: 0x%p\n", 971d4a0b6cSLukasz Majewski __func__, c, c->cdev); 981d4a0b6cSLukasz Majewski 991d4a0b6cSLukasz Majewski printf("GADGET DRIVER: %s\n", s); 1001d4a0b6cSLukasz Majewski if (!strcmp(s, "usb_dnl_dfu")) 1011d4a0b6cSLukasz Majewski ret = dfu_add(c); 102b528f713SLukasz Majewski else if (!strcmp(s, "usb_dnl_ums")) 103b528f713SLukasz Majewski ret = fsg_add(c); 1041d4a0b6cSLukasz Majewski 1051d4a0b6cSLukasz Majewski return ret; 1061d4a0b6cSLukasz Majewski } 1071d4a0b6cSLukasz Majewski 1081d4a0b6cSLukasz Majewski static int g_dnl_config_register(struct usb_composite_dev *cdev) 1091d4a0b6cSLukasz Majewski { 1107b412ab3SLukasz Majewski struct usb_configuration *config; 1117b412ab3SLukasz Majewski const char *name = "usb_dnload"; 1121d4a0b6cSLukasz Majewski 1137b412ab3SLukasz Majewski config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); 1147b412ab3SLukasz Majewski if (!config) 1157b412ab3SLukasz Majewski return -ENOMEM; 1161d4a0b6cSLukasz Majewski 1177b412ab3SLukasz Majewski memset(config, 0, sizeof(*config)); 1187b412ab3SLukasz Majewski 1197b412ab3SLukasz Majewski config->label = name; 1207b412ab3SLukasz Majewski config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; 1217b412ab3SLukasz Majewski config->bConfigurationValue = CONFIGURATION_NUMBER; 1227b412ab3SLukasz Majewski config->iConfiguration = STRING_USBDOWN; 1237b412ab3SLukasz Majewski config->bind = g_dnl_do_config; 1247b412ab3SLukasz Majewski 1257b412ab3SLukasz Majewski return usb_add_config(cdev, config); 1261d4a0b6cSLukasz Majewski } 1271d4a0b6cSLukasz Majewski 128c5398cc9SHeiko Schocher __weak 129*d6eae7b0SLukasz Majewski int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) 130c5398cc9SHeiko Schocher { 131c5398cc9SHeiko Schocher return 0; 132c5398cc9SHeiko Schocher } 133c5398cc9SHeiko Schocher 1341d4a0b6cSLukasz Majewski static int g_dnl_bind(struct usb_composite_dev *cdev) 1351d4a0b6cSLukasz Majewski { 1361d4a0b6cSLukasz Majewski struct usb_gadget *gadget = cdev->gadget; 1371d4a0b6cSLukasz Majewski int id, ret; 1381d4a0b6cSLukasz Majewski int gcnum; 1391d4a0b6cSLukasz Majewski 1401d4a0b6cSLukasz Majewski debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); 1411d4a0b6cSLukasz Majewski 1421d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 1431d4a0b6cSLukasz Majewski 1441d4a0b6cSLukasz Majewski if (id < 0) 1451d4a0b6cSLukasz Majewski return id; 1461d4a0b6cSLukasz Majewski g_dnl_string_defs[0].id = id; 1471d4a0b6cSLukasz Majewski device_desc.iManufacturer = id; 1481d4a0b6cSLukasz Majewski 1491d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 1501d4a0b6cSLukasz Majewski if (id < 0) 1511d4a0b6cSLukasz Majewski return id; 1521d4a0b6cSLukasz Majewski 1531d4a0b6cSLukasz Majewski g_dnl_string_defs[1].id = id; 1541d4a0b6cSLukasz Majewski device_desc.iProduct = id; 1551d4a0b6cSLukasz Majewski 156*d6eae7b0SLukasz Majewski g_dnl_bind_fixup(&device_desc, cdev->driver->name); 1571d4a0b6cSLukasz Majewski ret = g_dnl_config_register(cdev); 1581d4a0b6cSLukasz Majewski if (ret) 1591d4a0b6cSLukasz Majewski goto error; 1601d4a0b6cSLukasz Majewski 1611d4a0b6cSLukasz Majewski gcnum = usb_gadget_controller_number(gadget); 1621d4a0b6cSLukasz Majewski 1631d4a0b6cSLukasz Majewski debug("gcnum: %d\n", gcnum); 1641d4a0b6cSLukasz Majewski if (gcnum >= 0) 1651d4a0b6cSLukasz Majewski device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); 1661d4a0b6cSLukasz Majewski else { 1671d4a0b6cSLukasz Majewski debug("%s: controller '%s' not recognized\n", 1681d4a0b6cSLukasz Majewski shortname, gadget->name); 1691d4a0b6cSLukasz Majewski device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); 1701d4a0b6cSLukasz Majewski } 1711d4a0b6cSLukasz Majewski 1725a413caeSPantelis Antoniou debug("%s: calling usb_gadget_connect for " 1735a413caeSPantelis Antoniou "controller '%s'\n", shortname, gadget->name); 1745a413caeSPantelis Antoniou usb_gadget_connect(gadget); 1755a413caeSPantelis Antoniou 1761d4a0b6cSLukasz Majewski return 0; 1771d4a0b6cSLukasz Majewski 1781d4a0b6cSLukasz Majewski error: 1791d4a0b6cSLukasz Majewski g_dnl_unbind(cdev); 1801d4a0b6cSLukasz Majewski return -ENOMEM; 1811d4a0b6cSLukasz Majewski } 1821d4a0b6cSLukasz Majewski 1831d4a0b6cSLukasz Majewski static struct usb_composite_driver g_dnl_driver = { 1841d4a0b6cSLukasz Majewski .name = NULL, 1851d4a0b6cSLukasz Majewski .dev = &device_desc, 1861d4a0b6cSLukasz Majewski .strings = g_dnl_composite_strings, 1871d4a0b6cSLukasz Majewski 1881d4a0b6cSLukasz Majewski .bind = g_dnl_bind, 1891d4a0b6cSLukasz Majewski .unbind = g_dnl_unbind, 1901d4a0b6cSLukasz Majewski }; 1911d4a0b6cSLukasz Majewski 1921d4a0b6cSLukasz Majewski int g_dnl_register(const char *type) 1931d4a0b6cSLukasz Majewski { 1941d4a0b6cSLukasz Majewski /* We only allow "dfu" atm, so 3 should be enough */ 1951d4a0b6cSLukasz Majewski static char name[sizeof(shortname) + 3]; 1961d4a0b6cSLukasz Majewski int ret; 1971d4a0b6cSLukasz Majewski 1981d4a0b6cSLukasz Majewski if (!strcmp(type, "dfu")) { 1991d4a0b6cSLukasz Majewski strcpy(name, shortname); 2001d4a0b6cSLukasz Majewski strcat(name, type); 201b528f713SLukasz Majewski } else if (!strcmp(type, "ums")) { 202b528f713SLukasz Majewski strcpy(name, shortname); 203b528f713SLukasz Majewski strcat(name, type); 2041d4a0b6cSLukasz Majewski } else { 2051d4a0b6cSLukasz Majewski printf("%s: unknown command: %s\n", __func__, type); 2061d4a0b6cSLukasz Majewski return -EINVAL; 2071d4a0b6cSLukasz Majewski } 2081d4a0b6cSLukasz Majewski 2091d4a0b6cSLukasz Majewski g_dnl_driver.name = name; 2101d4a0b6cSLukasz Majewski 2111d4a0b6cSLukasz Majewski debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); 2121d4a0b6cSLukasz Majewski ret = usb_composite_register(&g_dnl_driver); 2131d4a0b6cSLukasz Majewski 2141d4a0b6cSLukasz Majewski if (ret) { 2151d4a0b6cSLukasz Majewski printf("%s: failed!, error: %d\n", __func__, ret); 2161d4a0b6cSLukasz Majewski return ret; 2171d4a0b6cSLukasz Majewski } 2181d4a0b6cSLukasz Majewski 2191d4a0b6cSLukasz Majewski return 0; 2201d4a0b6cSLukasz Majewski } 2211d4a0b6cSLukasz Majewski 2221d4a0b6cSLukasz Majewski void g_dnl_unregister(void) 2231d4a0b6cSLukasz Majewski { 2241d4a0b6cSLukasz Majewski usb_composite_unregister(&g_dnl_driver); 2251d4a0b6cSLukasz Majewski } 226