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 <errno.h> 111d4a0b6cSLukasz Majewski #include <common.h> 121d4a0b6cSLukasz Majewski #include <malloc.h> 131d4a0b6cSLukasz Majewski 141d4a0b6cSLukasz Majewski #include <mmc.h> 151d4a0b6cSLukasz Majewski #include <part.h> 161d4a0b6cSLukasz Majewski 171d4a0b6cSLukasz Majewski #include <g_dnl.h> 18*ba4e95c9SLukasz Majewski #include <usb_mass_storage.h> 191d4a0b6cSLukasz Majewski #include "f_dfu.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 36cfc2d0d6SLukasz Majewski /* Number of supported configurations */ 37cfc2d0d6SLukasz Majewski #define CONFIGURATION_NUMBER 1 381d4a0b6cSLukasz Majewski 391d4a0b6cSLukasz Majewski #define DRIVER_VERSION "usb_dnl 2.0" 401d4a0b6cSLukasz Majewski 411d4a0b6cSLukasz Majewski static const char shortname[] = "usb_dnl_"; 421d4a0b6cSLukasz Majewski static const char product[] = "USB download gadget"; 431d4a0b6cSLukasz Majewski static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; 441d4a0b6cSLukasz Majewski 451d4a0b6cSLukasz Majewski static struct usb_device_descriptor device_desc = { 461d4a0b6cSLukasz Majewski .bLength = sizeof device_desc, 471d4a0b6cSLukasz Majewski .bDescriptorType = USB_DT_DEVICE, 481d4a0b6cSLukasz Majewski 491d4a0b6cSLukasz Majewski .bcdUSB = __constant_cpu_to_le16(0x0200), 501d4a0b6cSLukasz Majewski .bDeviceClass = USB_CLASS_COMM, 511d4a0b6cSLukasz Majewski .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ 521d4a0b6cSLukasz Majewski 531d4a0b6cSLukasz Majewski .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), 541d4a0b6cSLukasz Majewski .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), 551d4a0b6cSLukasz Majewski .iProduct = STRING_PRODUCT, 561d4a0b6cSLukasz Majewski .bNumConfigurations = 1, 571d4a0b6cSLukasz Majewski }; 581d4a0b6cSLukasz Majewski 59c4219a82SLukasz Majewski /* 60c4219a82SLukasz Majewski * static strings, in UTF-8 61c4219a82SLukasz Majewski * IDs for those strings are assigned dynamically at g_dnl_bind() 62c4219a82SLukasz Majewski */ 631d4a0b6cSLukasz Majewski static struct usb_string g_dnl_string_defs[] = { 64c4219a82SLukasz Majewski {.s = manufacturer}, 65c4219a82SLukasz Majewski {.s = product}, 66598cf606SPantelis Antoniou { } /* end of list */ 671d4a0b6cSLukasz Majewski }; 681d4a0b6cSLukasz Majewski 691d4a0b6cSLukasz Majewski static struct usb_gadget_strings g_dnl_string_tab = { 701d4a0b6cSLukasz Majewski .language = 0x0409, /* en-us */ 711d4a0b6cSLukasz Majewski .strings = g_dnl_string_defs, 721d4a0b6cSLukasz Majewski }; 731d4a0b6cSLukasz Majewski 741d4a0b6cSLukasz Majewski static struct usb_gadget_strings *g_dnl_composite_strings[] = { 751d4a0b6cSLukasz Majewski &g_dnl_string_tab, 761d4a0b6cSLukasz Majewski NULL, 771d4a0b6cSLukasz Majewski }; 781d4a0b6cSLukasz Majewski 791d4a0b6cSLukasz Majewski static int g_dnl_unbind(struct usb_composite_dev *cdev) 801d4a0b6cSLukasz Majewski { 815a413caeSPantelis Antoniou struct usb_gadget *gadget = cdev->gadget; 825a413caeSPantelis Antoniou 835a413caeSPantelis Antoniou debug("%s: calling usb_gadget_disconnect for " 845a413caeSPantelis Antoniou "controller '%s'\n", shortname, gadget->name); 855a413caeSPantelis Antoniou usb_gadget_disconnect(gadget); 865a413caeSPantelis Antoniou 871d4a0b6cSLukasz Majewski return 0; 881d4a0b6cSLukasz Majewski } 891d4a0b6cSLukasz Majewski 901d4a0b6cSLukasz Majewski static int g_dnl_do_config(struct usb_configuration *c) 911d4a0b6cSLukasz Majewski { 921d4a0b6cSLukasz Majewski const char *s = c->cdev->driver->name; 931d4a0b6cSLukasz Majewski int ret = -1; 941d4a0b6cSLukasz Majewski 951d4a0b6cSLukasz Majewski debug("%s: configuration: 0x%p composite dev: 0x%p\n", 961d4a0b6cSLukasz Majewski __func__, c, c->cdev); 971d4a0b6cSLukasz Majewski 981d4a0b6cSLukasz Majewski printf("GADGET DRIVER: %s\n", s); 991d4a0b6cSLukasz Majewski if (!strcmp(s, "usb_dnl_dfu")) 1001d4a0b6cSLukasz Majewski ret = dfu_add(c); 101b528f713SLukasz Majewski else if (!strcmp(s, "usb_dnl_ums")) 102b528f713SLukasz Majewski ret = fsg_add(c); 1031d4a0b6cSLukasz Majewski 1041d4a0b6cSLukasz Majewski return ret; 1051d4a0b6cSLukasz Majewski } 1061d4a0b6cSLukasz Majewski 1071d4a0b6cSLukasz Majewski static int g_dnl_config_register(struct usb_composite_dev *cdev) 1081d4a0b6cSLukasz Majewski { 1091d4a0b6cSLukasz Majewski static struct usb_configuration config = { 1101d4a0b6cSLukasz Majewski .label = "usb_dnload", 1111d4a0b6cSLukasz Majewski .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, 112cfc2d0d6SLukasz Majewski .bConfigurationValue = CONFIGURATION_NUMBER, 1131d4a0b6cSLukasz Majewski .iConfiguration = STRING_USBDOWN, 1141d4a0b6cSLukasz Majewski 1151d4a0b6cSLukasz Majewski .bind = g_dnl_do_config, 1161d4a0b6cSLukasz Majewski }; 1171d4a0b6cSLukasz Majewski 1181d4a0b6cSLukasz Majewski return usb_add_config(cdev, &config); 1191d4a0b6cSLukasz Majewski } 1201d4a0b6cSLukasz Majewski 121c5398cc9SHeiko Schocher __weak 122c5398cc9SHeiko Schocher int g_dnl_bind_fixup(struct usb_device_descriptor *dev) 123c5398cc9SHeiko Schocher { 124c5398cc9SHeiko Schocher return 0; 125c5398cc9SHeiko Schocher } 126c5398cc9SHeiko Schocher 1271d4a0b6cSLukasz Majewski static int g_dnl_bind(struct usb_composite_dev *cdev) 1281d4a0b6cSLukasz Majewski { 1291d4a0b6cSLukasz Majewski struct usb_gadget *gadget = cdev->gadget; 1301d4a0b6cSLukasz Majewski int id, ret; 1311d4a0b6cSLukasz Majewski int gcnum; 1321d4a0b6cSLukasz Majewski 1331d4a0b6cSLukasz Majewski debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); 1341d4a0b6cSLukasz Majewski 1351d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 1361d4a0b6cSLukasz Majewski 1371d4a0b6cSLukasz Majewski if (id < 0) 1381d4a0b6cSLukasz Majewski return id; 1391d4a0b6cSLukasz Majewski g_dnl_string_defs[0].id = id; 1401d4a0b6cSLukasz Majewski device_desc.iManufacturer = id; 1411d4a0b6cSLukasz Majewski 1421d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 1431d4a0b6cSLukasz Majewski if (id < 0) 1441d4a0b6cSLukasz Majewski return id; 1451d4a0b6cSLukasz Majewski 1461d4a0b6cSLukasz Majewski g_dnl_string_defs[1].id = id; 1471d4a0b6cSLukasz Majewski device_desc.iProduct = id; 1481d4a0b6cSLukasz Majewski 149c5398cc9SHeiko Schocher g_dnl_bind_fixup(&device_desc); 1501d4a0b6cSLukasz Majewski ret = g_dnl_config_register(cdev); 1511d4a0b6cSLukasz Majewski if (ret) 1521d4a0b6cSLukasz Majewski goto error; 1531d4a0b6cSLukasz Majewski 1541d4a0b6cSLukasz Majewski gcnum = usb_gadget_controller_number(gadget); 1551d4a0b6cSLukasz Majewski 1561d4a0b6cSLukasz Majewski debug("gcnum: %d\n", gcnum); 1571d4a0b6cSLukasz Majewski if (gcnum >= 0) 1581d4a0b6cSLukasz Majewski device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); 1591d4a0b6cSLukasz Majewski else { 1601d4a0b6cSLukasz Majewski debug("%s: controller '%s' not recognized\n", 1611d4a0b6cSLukasz Majewski shortname, gadget->name); 1621d4a0b6cSLukasz Majewski device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); 1631d4a0b6cSLukasz Majewski } 1641d4a0b6cSLukasz Majewski 1655a413caeSPantelis Antoniou debug("%s: calling usb_gadget_connect for " 1665a413caeSPantelis Antoniou "controller '%s'\n", shortname, gadget->name); 1675a413caeSPantelis Antoniou usb_gadget_connect(gadget); 1685a413caeSPantelis Antoniou 1691d4a0b6cSLukasz Majewski return 0; 1701d4a0b6cSLukasz Majewski 1711d4a0b6cSLukasz Majewski error: 1721d4a0b6cSLukasz Majewski g_dnl_unbind(cdev); 1731d4a0b6cSLukasz Majewski return -ENOMEM; 1741d4a0b6cSLukasz Majewski } 1751d4a0b6cSLukasz Majewski 1761d4a0b6cSLukasz Majewski static struct usb_composite_driver g_dnl_driver = { 1771d4a0b6cSLukasz Majewski .name = NULL, 1781d4a0b6cSLukasz Majewski .dev = &device_desc, 1791d4a0b6cSLukasz Majewski .strings = g_dnl_composite_strings, 1801d4a0b6cSLukasz Majewski 1811d4a0b6cSLukasz Majewski .bind = g_dnl_bind, 1821d4a0b6cSLukasz Majewski .unbind = g_dnl_unbind, 1831d4a0b6cSLukasz Majewski }; 1841d4a0b6cSLukasz Majewski 1851d4a0b6cSLukasz Majewski int g_dnl_register(const char *type) 1861d4a0b6cSLukasz Majewski { 1871d4a0b6cSLukasz Majewski /* We only allow "dfu" atm, so 3 should be enough */ 1881d4a0b6cSLukasz Majewski static char name[sizeof(shortname) + 3]; 1891d4a0b6cSLukasz Majewski int ret; 1901d4a0b6cSLukasz Majewski 1911d4a0b6cSLukasz Majewski if (!strcmp(type, "dfu")) { 1921d4a0b6cSLukasz Majewski strcpy(name, shortname); 1931d4a0b6cSLukasz Majewski strcat(name, type); 194b528f713SLukasz Majewski } else if (!strcmp(type, "ums")) { 195b528f713SLukasz Majewski strcpy(name, shortname); 196b528f713SLukasz Majewski strcat(name, type); 1971d4a0b6cSLukasz Majewski } else { 1981d4a0b6cSLukasz Majewski printf("%s: unknown command: %s\n", __func__, type); 1991d4a0b6cSLukasz Majewski return -EINVAL; 2001d4a0b6cSLukasz Majewski } 2011d4a0b6cSLukasz Majewski 2021d4a0b6cSLukasz Majewski g_dnl_driver.name = name; 2031d4a0b6cSLukasz Majewski 2041d4a0b6cSLukasz Majewski debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); 2051d4a0b6cSLukasz Majewski ret = usb_composite_register(&g_dnl_driver); 2061d4a0b6cSLukasz Majewski 2071d4a0b6cSLukasz Majewski if (ret) { 2081d4a0b6cSLukasz Majewski printf("%s: failed!, error: %d\n", __func__, ret); 2091d4a0b6cSLukasz Majewski return ret; 2101d4a0b6cSLukasz Majewski } 2111d4a0b6cSLukasz Majewski 2121d4a0b6cSLukasz Majewski return 0; 2131d4a0b6cSLukasz Majewski } 2141d4a0b6cSLukasz Majewski 2151d4a0b6cSLukasz Majewski void g_dnl_unregister(void) 2161d4a0b6cSLukasz Majewski { 2171d4a0b6cSLukasz Majewski usb_composite_unregister(&g_dnl_driver); 2181d4a0b6cSLukasz Majewski } 219