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> 1599fc2221SPaul Kocialkowski #include <usb.h> 161d4a0b6cSLukasz Majewski 171d4a0b6cSLukasz Majewski #include <g_dnl.h> 18ba4e95c9SLukasz Majewski #include <usb_mass_storage.h> 19a6921adcSLukasz Majewski #include <dfu.h> 20b958fb91SLukasz Majewski #include <thor.h> 211d4a0b6cSLukasz Majewski 221d4a0b6cSLukasz Majewski #include "gadget_chips.h" 231d4a0b6cSLukasz Majewski #include "composite.c" 241d4a0b6cSLukasz Majewski 251d4a0b6cSLukasz Majewski /* 261d4a0b6cSLukasz Majewski * One needs to define the following: 271d4a0b6cSLukasz Majewski * CONFIG_G_DNL_VENDOR_NUM 281d4a0b6cSLukasz Majewski * CONFIG_G_DNL_PRODUCT_NUM 291d4a0b6cSLukasz Majewski * CONFIG_G_DNL_MANUFACTURER 30*e6c0bc06SSam Protsenko * at e.g. ./configs/<board>_defconfig 311d4a0b6cSLukasz Majewski */ 321d4a0b6cSLukasz Majewski 331d4a0b6cSLukasz Majewski #define STRING_MANUFACTURER 25 341d4a0b6cSLukasz Majewski #define STRING_PRODUCT 2 35cfc2d0d6SLukasz Majewski /* Index of String Descriptor describing this configuration */ 361d4a0b6cSLukasz Majewski #define STRING_USBDOWN 2 37ec9002e4SHeiko Schocher /* Index of String serial */ 38ec9002e4SHeiko Schocher #define STRING_SERIAL 3 39ec9002e4SHeiko Schocher #define MAX_STRING_SERIAL 32 40cfc2d0d6SLukasz Majewski /* Number of supported configurations */ 41cfc2d0d6SLukasz Majewski #define CONFIGURATION_NUMBER 1 421d4a0b6cSLukasz Majewski 431d4a0b6cSLukasz Majewski #define DRIVER_VERSION "usb_dnl 2.0" 441d4a0b6cSLukasz Majewski 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), 615b718407SJohn Tobias .bDeviceClass = USB_CLASS_PER_INTERFACE, 625b718407SJohn Tobias .bDeviceSubClass = 0, /*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 965a413caeSPantelis Antoniou debug("%s: calling usb_gadget_disconnect for " 97c4d0e856SMateusz Zalega "controller '%s'\n", __func__, gadget->name); 985a413caeSPantelis Antoniou usb_gadget_disconnect(gadget); 995a413caeSPantelis Antoniou 1001d4a0b6cSLukasz Majewski return 0; 1011d4a0b6cSLukasz Majewski } 1021d4a0b6cSLukasz Majewski 103c4d0e856SMateusz Zalega static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void) 104c4d0e856SMateusz Zalega { 105c4d0e856SMateusz Zalega return ll_entry_start(struct g_dnl_bind_callback, 106c4d0e856SMateusz Zalega g_dnl_bind_callbacks); 107c4d0e856SMateusz Zalega } 108c4d0e856SMateusz Zalega 109c4d0e856SMateusz Zalega static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void) 110c4d0e856SMateusz Zalega { 111c4d0e856SMateusz Zalega return ll_entry_end(struct g_dnl_bind_callback, 112c4d0e856SMateusz Zalega g_dnl_bind_callbacks); 113c4d0e856SMateusz Zalega } 114c4d0e856SMateusz Zalega 1151d4a0b6cSLukasz Majewski static int g_dnl_do_config(struct usb_configuration *c) 1161d4a0b6cSLukasz Majewski { 1171d4a0b6cSLukasz Majewski const char *s = c->cdev->driver->name; 118c4d0e856SMateusz Zalega struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first(); 1191d4a0b6cSLukasz Majewski 1201d4a0b6cSLukasz Majewski debug("%s: configuration: 0x%p composite dev: 0x%p\n", 1211d4a0b6cSLukasz Majewski __func__, c, c->cdev); 1221d4a0b6cSLukasz Majewski 123c4d0e856SMateusz Zalega for (; callback != g_dnl_bind_callback_end(); callback++) 124c4d0e856SMateusz Zalega if (!strcmp(s, callback->usb_function_name)) 125c4d0e856SMateusz Zalega return callback->fptr(c); 126c4d0e856SMateusz Zalega return -ENODEV; 1271d4a0b6cSLukasz Majewski } 1281d4a0b6cSLukasz Majewski 1291d4a0b6cSLukasz Majewski static int g_dnl_config_register(struct usb_composite_dev *cdev) 1301d4a0b6cSLukasz Majewski { 1317b412ab3SLukasz Majewski struct usb_configuration *config; 1327b412ab3SLukasz Majewski const char *name = "usb_dnload"; 1331d4a0b6cSLukasz Majewski 1347b412ab3SLukasz Majewski config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); 1357b412ab3SLukasz Majewski if (!config) 1367b412ab3SLukasz Majewski return -ENOMEM; 1371d4a0b6cSLukasz Majewski 1387b412ab3SLukasz Majewski memset(config, 0, sizeof(*config)); 1397b412ab3SLukasz Majewski 1407b412ab3SLukasz Majewski config->label = name; 1417b412ab3SLukasz Majewski config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; 1427b412ab3SLukasz Majewski config->bConfigurationValue = CONFIGURATION_NUMBER; 1437b412ab3SLukasz Majewski config->iConfiguration = STRING_USBDOWN; 1447b412ab3SLukasz Majewski config->bind = g_dnl_do_config; 1457b412ab3SLukasz Majewski 1467b412ab3SLukasz Majewski return usb_add_config(cdev, config); 1471d4a0b6cSLukasz Majewski } 1481d4a0b6cSLukasz Majewski 149c5398cc9SHeiko Schocher __weak 15099fc2221SPaul Kocialkowski int board_usb_init(int index, enum usb_init_type init) 15199fc2221SPaul Kocialkowski { 15299fc2221SPaul Kocialkowski return 0; 15399fc2221SPaul Kocialkowski } 15499fc2221SPaul Kocialkowski 15599fc2221SPaul Kocialkowski __weak 15699fc2221SPaul Kocialkowski int board_usb_cleanup(int index, enum usb_init_type init) 15799fc2221SPaul Kocialkowski { 15899fc2221SPaul Kocialkowski return 0; 15999fc2221SPaul Kocialkowski } 16099fc2221SPaul Kocialkowski 16199fc2221SPaul Kocialkowski __weak 162d6eae7b0SLukasz Majewski int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) 163c5398cc9SHeiko Schocher { 164c5398cc9SHeiko Schocher return 0; 165c5398cc9SHeiko Schocher } 166c5398cc9SHeiko Schocher 1677a0d463fSHeiko Schocher __weak int g_dnl_get_board_bcd_device_number(int gcnum) 1687a0d463fSHeiko Schocher { 1697a0d463fSHeiko Schocher return gcnum; 1707a0d463fSHeiko Schocher } 1717a0d463fSHeiko Schocher 17275504e95SMateusz Zalega __weak int g_dnl_board_usb_cable_connected(void) 17375504e95SMateusz Zalega { 17475504e95SMateusz Zalega return -EOPNOTSUPP; 17575504e95SMateusz Zalega } 17675504e95SMateusz Zalega 177fe1b28c9SRob Herring static bool g_dnl_detach_request; 178fe1b28c9SRob Herring 179fe1b28c9SRob Herring bool g_dnl_detach(void) 180fe1b28c9SRob Herring { 181fe1b28c9SRob Herring return g_dnl_detach_request; 182fe1b28c9SRob Herring } 183fe1b28c9SRob Herring 184fe1b28c9SRob Herring void g_dnl_trigger_detach(void) 185fe1b28c9SRob Herring { 186fe1b28c9SRob Herring g_dnl_detach_request = true; 187fe1b28c9SRob Herring } 188fe1b28c9SRob Herring 189fe1b28c9SRob Herring void g_dnl_clear_detach(void) 190fe1b28c9SRob Herring { 191fe1b28c9SRob Herring g_dnl_detach_request = false; 192fe1b28c9SRob Herring } 193fe1b28c9SRob Herring 1947a0d463fSHeiko Schocher static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev) 1957a0d463fSHeiko Schocher { 1967a0d463fSHeiko Schocher struct usb_gadget *gadget = cdev->gadget; 1977a0d463fSHeiko Schocher int gcnum; 1987a0d463fSHeiko Schocher 1997a0d463fSHeiko Schocher gcnum = usb_gadget_controller_number(gadget); 2007a0d463fSHeiko Schocher if (gcnum > 0) 2017a0d463fSHeiko Schocher gcnum += 0x200; 2027a0d463fSHeiko Schocher 2037a0d463fSHeiko Schocher return g_dnl_get_board_bcd_device_number(gcnum); 2047a0d463fSHeiko Schocher } 2057a0d463fSHeiko Schocher 2061d4a0b6cSLukasz Majewski static int g_dnl_bind(struct usb_composite_dev *cdev) 2071d4a0b6cSLukasz Majewski { 2081d4a0b6cSLukasz Majewski struct usb_gadget *gadget = cdev->gadget; 2091d4a0b6cSLukasz Majewski int id, ret; 2101d4a0b6cSLukasz Majewski int gcnum; 2111d4a0b6cSLukasz Majewski 2121d4a0b6cSLukasz Majewski debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); 2131d4a0b6cSLukasz Majewski 2141d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 2151d4a0b6cSLukasz Majewski 2161d4a0b6cSLukasz Majewski if (id < 0) 2171d4a0b6cSLukasz Majewski return id; 2181d4a0b6cSLukasz Majewski g_dnl_string_defs[0].id = id; 2191d4a0b6cSLukasz Majewski device_desc.iManufacturer = id; 2201d4a0b6cSLukasz Majewski 2211d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 2221d4a0b6cSLukasz Majewski if (id < 0) 2231d4a0b6cSLukasz Majewski return id; 2241d4a0b6cSLukasz Majewski 2251d4a0b6cSLukasz Majewski g_dnl_string_defs[1].id = id; 2261d4a0b6cSLukasz Majewski device_desc.iProduct = id; 2271d4a0b6cSLukasz Majewski 228ec9002e4SHeiko Schocher id = usb_string_id(cdev); 229ec9002e4SHeiko Schocher if (id < 0) 230ec9002e4SHeiko Schocher return id; 231ec9002e4SHeiko Schocher 232ec9002e4SHeiko Schocher g_dnl_string_defs[2].id = id; 233ec9002e4SHeiko Schocher device_desc.iSerialNumber = id; 234ec9002e4SHeiko Schocher 235d6eae7b0SLukasz Majewski g_dnl_bind_fixup(&device_desc, cdev->driver->name); 2361d4a0b6cSLukasz Majewski ret = g_dnl_config_register(cdev); 2371d4a0b6cSLukasz Majewski if (ret) 2381d4a0b6cSLukasz Majewski goto error; 2391d4a0b6cSLukasz Majewski 2407a0d463fSHeiko Schocher gcnum = g_dnl_get_bcd_device_number(cdev); 2411d4a0b6cSLukasz Majewski if (gcnum >= 0) 2427a0d463fSHeiko Schocher device_desc.bcdDevice = cpu_to_le16(gcnum); 2431d4a0b6cSLukasz Majewski else { 2441d4a0b6cSLukasz Majewski debug("%s: controller '%s' not recognized\n", 245c4d0e856SMateusz Zalega __func__, gadget->name); 2461d4a0b6cSLukasz Majewski device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); 2471d4a0b6cSLukasz Majewski } 2481d4a0b6cSLukasz Majewski 2495a413caeSPantelis Antoniou debug("%s: calling usb_gadget_connect for " 250c4d0e856SMateusz Zalega "controller '%s'\n", __func__, gadget->name); 2515a413caeSPantelis Antoniou usb_gadget_connect(gadget); 2525a413caeSPantelis Antoniou 2531d4a0b6cSLukasz Majewski return 0; 2541d4a0b6cSLukasz Majewski 2551d4a0b6cSLukasz Majewski error: 2561d4a0b6cSLukasz Majewski g_dnl_unbind(cdev); 2571d4a0b6cSLukasz Majewski return -ENOMEM; 2581d4a0b6cSLukasz Majewski } 2591d4a0b6cSLukasz Majewski 2601d4a0b6cSLukasz Majewski static struct usb_composite_driver g_dnl_driver = { 2611d4a0b6cSLukasz Majewski .name = NULL, 2621d4a0b6cSLukasz Majewski .dev = &device_desc, 2631d4a0b6cSLukasz Majewski .strings = g_dnl_composite_strings, 2641d4a0b6cSLukasz Majewski 2651d4a0b6cSLukasz Majewski .bind = g_dnl_bind, 2661d4a0b6cSLukasz Majewski .unbind = g_dnl_unbind, 2671d4a0b6cSLukasz Majewski }; 2681d4a0b6cSLukasz Majewski 269c4d0e856SMateusz Zalega /* 270c4d0e856SMateusz Zalega * NOTICE: 271c4d0e856SMateusz Zalega * Registering via USB function name won't be necessary after rewriting 272c4d0e856SMateusz Zalega * g_dnl to support multiple USB functions. 273c4d0e856SMateusz Zalega */ 274c4d0e856SMateusz Zalega int g_dnl_register(const char *name) 2751d4a0b6cSLukasz Majewski { 27625fbf96bSStephen Warren int ret; 2771d4a0b6cSLukasz Majewski 278c4d0e856SMateusz Zalega debug("%s: g_dnl_driver.name = %s\n", __func__, name); 2791d4a0b6cSLukasz Majewski g_dnl_driver.name = name; 2801d4a0b6cSLukasz Majewski 28125fbf96bSStephen Warren ret = usb_composite_register(&g_dnl_driver); 2821d4a0b6cSLukasz Majewski if (ret) { 2831d4a0b6cSLukasz Majewski printf("%s: failed!, error: %d\n", __func__, ret); 2841d4a0b6cSLukasz Majewski return ret; 2851d4a0b6cSLukasz Majewski } 2861d4a0b6cSLukasz Majewski return 0; 2871d4a0b6cSLukasz Majewski } 2881d4a0b6cSLukasz Majewski 2891d4a0b6cSLukasz Majewski void g_dnl_unregister(void) 2901d4a0b6cSLukasz Majewski { 2911d4a0b6cSLukasz Majewski usb_composite_unregister(&g_dnl_driver); 2921d4a0b6cSLukasz Majewski } 293