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 22*de4e4edaSSam Protsenko #include <env_callback.h> 23*de4e4edaSSam Protsenko 241d4a0b6cSLukasz Majewski #include "gadget_chips.h" 251d4a0b6cSLukasz Majewski #include "composite.c" 261d4a0b6cSLukasz Majewski 271d4a0b6cSLukasz Majewski /* 281d4a0b6cSLukasz Majewski * One needs to define the following: 291d4a0b6cSLukasz Majewski * CONFIG_G_DNL_VENDOR_NUM 301d4a0b6cSLukasz Majewski * CONFIG_G_DNL_PRODUCT_NUM 311d4a0b6cSLukasz Majewski * CONFIG_G_DNL_MANUFACTURER 32e6c0bc06SSam Protsenko * at e.g. ./configs/<board>_defconfig 331d4a0b6cSLukasz Majewski */ 341d4a0b6cSLukasz Majewski 351d4a0b6cSLukasz Majewski #define STRING_MANUFACTURER 25 361d4a0b6cSLukasz Majewski #define STRING_PRODUCT 2 37cfc2d0d6SLukasz Majewski /* Index of String Descriptor describing this configuration */ 381d4a0b6cSLukasz Majewski #define STRING_USBDOWN 2 39ec9002e4SHeiko Schocher /* Index of String serial */ 40ec9002e4SHeiko Schocher #define STRING_SERIAL 3 4112d0b8f5SFelipe Balbi #define MAX_STRING_SERIAL 256 42cfc2d0d6SLukasz Majewski /* Number of supported configurations */ 43cfc2d0d6SLukasz Majewski #define CONFIGURATION_NUMBER 1 441d4a0b6cSLukasz Majewski 451d4a0b6cSLukasz Majewski #define DRIVER_VERSION "usb_dnl 2.0" 461d4a0b6cSLukasz Majewski 471d4a0b6cSLukasz Majewski static const char product[] = "USB download gadget"; 48ec9002e4SHeiko Schocher static char g_dnl_serial[MAX_STRING_SERIAL]; 491d4a0b6cSLukasz Majewski static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; 501d4a0b6cSLukasz Majewski 51ec9002e4SHeiko Schocher void g_dnl_set_serialnumber(char *s) 52ec9002e4SHeiko Schocher { 53ec9002e4SHeiko Schocher memset(g_dnl_serial, 0, MAX_STRING_SERIAL); 54949bf79eSFelipe Balbi strncpy(g_dnl_serial, s, MAX_STRING_SERIAL - 1); 55ec9002e4SHeiko Schocher } 56ec9002e4SHeiko Schocher 571d4a0b6cSLukasz Majewski static struct usb_device_descriptor device_desc = { 581d4a0b6cSLukasz Majewski .bLength = sizeof device_desc, 591d4a0b6cSLukasz Majewski .bDescriptorType = USB_DT_DEVICE, 601d4a0b6cSLukasz Majewski 611d4a0b6cSLukasz Majewski .bcdUSB = __constant_cpu_to_le16(0x0200), 625b718407SJohn Tobias .bDeviceClass = USB_CLASS_PER_INTERFACE, 635b718407SJohn Tobias .bDeviceSubClass = 0, /*0x02:CDC-modem , 0x00:CDC-serial*/ 641d4a0b6cSLukasz Majewski 651d4a0b6cSLukasz Majewski .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), 661d4a0b6cSLukasz Majewski .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), 67207835b1SFelipe Balbi /* .iProduct = DYNAMIC */ 68207835b1SFelipe Balbi /* .iSerialNumber = DYNAMIC */ 691d4a0b6cSLukasz Majewski .bNumConfigurations = 1, 701d4a0b6cSLukasz Majewski }; 711d4a0b6cSLukasz Majewski 72c4219a82SLukasz Majewski /* 73c4219a82SLukasz Majewski * static strings, in UTF-8 74c4219a82SLukasz Majewski * IDs for those strings are assigned dynamically at g_dnl_bind() 75c4219a82SLukasz Majewski */ 761d4a0b6cSLukasz Majewski static struct usb_string g_dnl_string_defs[] = { 77c4219a82SLukasz Majewski {.s = manufacturer}, 78c4219a82SLukasz Majewski {.s = product}, 79ec9002e4SHeiko Schocher {.s = g_dnl_serial}, 80598cf606SPantelis Antoniou { } /* end of list */ 811d4a0b6cSLukasz Majewski }; 821d4a0b6cSLukasz Majewski 831d4a0b6cSLukasz Majewski static struct usb_gadget_strings g_dnl_string_tab = { 841d4a0b6cSLukasz Majewski .language = 0x0409, /* en-us */ 851d4a0b6cSLukasz Majewski .strings = g_dnl_string_defs, 861d4a0b6cSLukasz Majewski }; 871d4a0b6cSLukasz Majewski 881d4a0b6cSLukasz Majewski static struct usb_gadget_strings *g_dnl_composite_strings[] = { 891d4a0b6cSLukasz Majewski &g_dnl_string_tab, 901d4a0b6cSLukasz Majewski NULL, 911d4a0b6cSLukasz Majewski }; 921d4a0b6cSLukasz Majewski 931d4a0b6cSLukasz Majewski static int g_dnl_unbind(struct usb_composite_dev *cdev) 941d4a0b6cSLukasz Majewski { 955a413caeSPantelis Antoniou struct usb_gadget *gadget = cdev->gadget; 965a413caeSPantelis Antoniou 975a413caeSPantelis Antoniou debug("%s: calling usb_gadget_disconnect for " 98c4d0e856SMateusz Zalega "controller '%s'\n", __func__, gadget->name); 995a413caeSPantelis Antoniou usb_gadget_disconnect(gadget); 1005a413caeSPantelis Antoniou 1011d4a0b6cSLukasz Majewski return 0; 1021d4a0b6cSLukasz Majewski } 1031d4a0b6cSLukasz Majewski 104c4d0e856SMateusz Zalega static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void) 105c4d0e856SMateusz Zalega { 106c4d0e856SMateusz Zalega return ll_entry_start(struct g_dnl_bind_callback, 107c4d0e856SMateusz Zalega g_dnl_bind_callbacks); 108c4d0e856SMateusz Zalega } 109c4d0e856SMateusz Zalega 110c4d0e856SMateusz Zalega static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void) 111c4d0e856SMateusz Zalega { 112c4d0e856SMateusz Zalega return ll_entry_end(struct g_dnl_bind_callback, 113c4d0e856SMateusz Zalega g_dnl_bind_callbacks); 114c4d0e856SMateusz Zalega } 115c4d0e856SMateusz Zalega 1161d4a0b6cSLukasz Majewski static int g_dnl_do_config(struct usb_configuration *c) 1171d4a0b6cSLukasz Majewski { 1181d4a0b6cSLukasz Majewski const char *s = c->cdev->driver->name; 119c4d0e856SMateusz Zalega struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first(); 1201d4a0b6cSLukasz Majewski 1211d4a0b6cSLukasz Majewski debug("%s: configuration: 0x%p composite dev: 0x%p\n", 1221d4a0b6cSLukasz Majewski __func__, c, c->cdev); 1231d4a0b6cSLukasz Majewski 124c4d0e856SMateusz Zalega for (; callback != g_dnl_bind_callback_end(); callback++) 125c4d0e856SMateusz Zalega if (!strcmp(s, callback->usb_function_name)) 126c4d0e856SMateusz Zalega return callback->fptr(c); 127c4d0e856SMateusz Zalega return -ENODEV; 1281d4a0b6cSLukasz Majewski } 1291d4a0b6cSLukasz Majewski 1301d4a0b6cSLukasz Majewski static int g_dnl_config_register(struct usb_composite_dev *cdev) 1311d4a0b6cSLukasz Majewski { 1327b412ab3SLukasz Majewski struct usb_configuration *config; 1337b412ab3SLukasz Majewski const char *name = "usb_dnload"; 1341d4a0b6cSLukasz Majewski 1357b412ab3SLukasz Majewski config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); 1367b412ab3SLukasz Majewski if (!config) 1377b412ab3SLukasz Majewski return -ENOMEM; 1381d4a0b6cSLukasz Majewski 1397b412ab3SLukasz Majewski memset(config, 0, sizeof(*config)); 1407b412ab3SLukasz Majewski 1417b412ab3SLukasz Majewski config->label = name; 1427b412ab3SLukasz Majewski config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; 1437b412ab3SLukasz Majewski config->bConfigurationValue = CONFIGURATION_NUMBER; 1447b412ab3SLukasz Majewski config->iConfiguration = STRING_USBDOWN; 1457b412ab3SLukasz Majewski config->bind = g_dnl_do_config; 1467b412ab3SLukasz Majewski 1477b412ab3SLukasz Majewski return usb_add_config(cdev, config); 1481d4a0b6cSLukasz Majewski } 1491d4a0b6cSLukasz Majewski 150c5398cc9SHeiko Schocher __weak 15199fc2221SPaul Kocialkowski int board_usb_init(int index, enum usb_init_type init) 15299fc2221SPaul Kocialkowski { 15399fc2221SPaul Kocialkowski return 0; 15499fc2221SPaul Kocialkowski } 15599fc2221SPaul Kocialkowski 15699fc2221SPaul Kocialkowski __weak 15799fc2221SPaul Kocialkowski int board_usb_cleanup(int index, enum usb_init_type init) 15899fc2221SPaul Kocialkowski { 15999fc2221SPaul Kocialkowski return 0; 16099fc2221SPaul Kocialkowski } 16199fc2221SPaul Kocialkowski 16299fc2221SPaul Kocialkowski __weak 163d6eae7b0SLukasz Majewski int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) 164c5398cc9SHeiko Schocher { 165c5398cc9SHeiko Schocher return 0; 166c5398cc9SHeiko Schocher } 167c5398cc9SHeiko Schocher 1687a0d463fSHeiko Schocher __weak int g_dnl_get_board_bcd_device_number(int gcnum) 1697a0d463fSHeiko Schocher { 1707a0d463fSHeiko Schocher return gcnum; 1717a0d463fSHeiko Schocher } 1727a0d463fSHeiko Schocher 17375504e95SMateusz Zalega __weak int g_dnl_board_usb_cable_connected(void) 17475504e95SMateusz Zalega { 17575504e95SMateusz Zalega return -EOPNOTSUPP; 17675504e95SMateusz Zalega } 17775504e95SMateusz Zalega 178fe1b28c9SRob Herring static bool g_dnl_detach_request; 179fe1b28c9SRob Herring 180fe1b28c9SRob Herring bool g_dnl_detach(void) 181fe1b28c9SRob Herring { 182fe1b28c9SRob Herring return g_dnl_detach_request; 183fe1b28c9SRob Herring } 184fe1b28c9SRob Herring 185fe1b28c9SRob Herring void g_dnl_trigger_detach(void) 186fe1b28c9SRob Herring { 187fe1b28c9SRob Herring g_dnl_detach_request = true; 188fe1b28c9SRob Herring } 189fe1b28c9SRob Herring 190fe1b28c9SRob Herring void g_dnl_clear_detach(void) 191fe1b28c9SRob Herring { 192fe1b28c9SRob Herring g_dnl_detach_request = false; 193fe1b28c9SRob Herring } 194fe1b28c9SRob Herring 1957a0d463fSHeiko Schocher static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev) 1967a0d463fSHeiko Schocher { 1977a0d463fSHeiko Schocher struct usb_gadget *gadget = cdev->gadget; 1987a0d463fSHeiko Schocher int gcnum; 1997a0d463fSHeiko Schocher 2007a0d463fSHeiko Schocher gcnum = usb_gadget_controller_number(gadget); 2017a0d463fSHeiko Schocher if (gcnum > 0) 2027a0d463fSHeiko Schocher gcnum += 0x200; 2037a0d463fSHeiko Schocher 2047a0d463fSHeiko Schocher return g_dnl_get_board_bcd_device_number(gcnum); 2057a0d463fSHeiko Schocher } 2067a0d463fSHeiko Schocher 207*de4e4edaSSam Protsenko /** 208*de4e4edaSSam Protsenko * Update internal serial number variable when the "serial#" env var changes. 209*de4e4edaSSam Protsenko * 210*de4e4edaSSam Protsenko * Handle all cases, even when flags == H_PROGRAMMATIC or op == env_op_delete. 211*de4e4edaSSam Protsenko */ 212*de4e4edaSSam Protsenko static int on_serialno(const char *name, const char *value, enum env_op op, 213*de4e4edaSSam Protsenko int flags) 214*de4e4edaSSam Protsenko { 215*de4e4edaSSam Protsenko g_dnl_set_serialnumber((char *)value); 216*de4e4edaSSam Protsenko return 0; 217*de4e4edaSSam Protsenko } 218*de4e4edaSSam Protsenko U_BOOT_ENV_CALLBACK(serialno, on_serialno); 219*de4e4edaSSam Protsenko 2201d4a0b6cSLukasz Majewski static int g_dnl_bind(struct usb_composite_dev *cdev) 2211d4a0b6cSLukasz Majewski { 2221d4a0b6cSLukasz Majewski struct usb_gadget *gadget = cdev->gadget; 2231d4a0b6cSLukasz Majewski int id, ret; 2241d4a0b6cSLukasz Majewski int gcnum; 2251d4a0b6cSLukasz Majewski 2261d4a0b6cSLukasz Majewski debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); 2271d4a0b6cSLukasz Majewski 2281d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 2291d4a0b6cSLukasz Majewski 2301d4a0b6cSLukasz Majewski if (id < 0) 2311d4a0b6cSLukasz Majewski return id; 2321d4a0b6cSLukasz Majewski g_dnl_string_defs[0].id = id; 2331d4a0b6cSLukasz Majewski device_desc.iManufacturer = id; 2341d4a0b6cSLukasz Majewski 2351d4a0b6cSLukasz Majewski id = usb_string_id(cdev); 2361d4a0b6cSLukasz Majewski if (id < 0) 2371d4a0b6cSLukasz Majewski return id; 2381d4a0b6cSLukasz Majewski 2391d4a0b6cSLukasz Majewski g_dnl_string_defs[1].id = id; 2401d4a0b6cSLukasz Majewski device_desc.iProduct = id; 2411d4a0b6cSLukasz Majewski 2422a0583e3SLukasz Majewski g_dnl_bind_fixup(&device_desc, cdev->driver->name); 2432a0583e3SLukasz Majewski 244842778a0SFelipe Balbi if (strlen(g_dnl_serial)) { 245ec9002e4SHeiko Schocher id = usb_string_id(cdev); 246ec9002e4SHeiko Schocher if (id < 0) 247ec9002e4SHeiko Schocher return id; 248ec9002e4SHeiko Schocher 249ec9002e4SHeiko Schocher g_dnl_string_defs[2].id = id; 250ec9002e4SHeiko Schocher device_desc.iSerialNumber = id; 251842778a0SFelipe Balbi } 252ec9002e4SHeiko Schocher 2531d4a0b6cSLukasz Majewski ret = g_dnl_config_register(cdev); 2541d4a0b6cSLukasz Majewski if (ret) 2551d4a0b6cSLukasz Majewski goto error; 2561d4a0b6cSLukasz Majewski 2577a0d463fSHeiko Schocher gcnum = g_dnl_get_bcd_device_number(cdev); 2581d4a0b6cSLukasz Majewski if (gcnum >= 0) 2597a0d463fSHeiko Schocher device_desc.bcdDevice = cpu_to_le16(gcnum); 2601d4a0b6cSLukasz Majewski else { 2611d4a0b6cSLukasz Majewski debug("%s: controller '%s' not recognized\n", 262c4d0e856SMateusz Zalega __func__, gadget->name); 2631d4a0b6cSLukasz Majewski device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); 2641d4a0b6cSLukasz Majewski } 2651d4a0b6cSLukasz Majewski 2665a413caeSPantelis Antoniou debug("%s: calling usb_gadget_connect for " 267c4d0e856SMateusz Zalega "controller '%s'\n", __func__, gadget->name); 2685a413caeSPantelis Antoniou usb_gadget_connect(gadget); 2695a413caeSPantelis Antoniou 2701d4a0b6cSLukasz Majewski return 0; 2711d4a0b6cSLukasz Majewski 2721d4a0b6cSLukasz Majewski error: 2731d4a0b6cSLukasz Majewski g_dnl_unbind(cdev); 2741d4a0b6cSLukasz Majewski return -ENOMEM; 2751d4a0b6cSLukasz Majewski } 2761d4a0b6cSLukasz Majewski 2771d4a0b6cSLukasz Majewski static struct usb_composite_driver g_dnl_driver = { 2781d4a0b6cSLukasz Majewski .name = NULL, 2791d4a0b6cSLukasz Majewski .dev = &device_desc, 2801d4a0b6cSLukasz Majewski .strings = g_dnl_composite_strings, 2811d4a0b6cSLukasz Majewski 2821d4a0b6cSLukasz Majewski .bind = g_dnl_bind, 2831d4a0b6cSLukasz Majewski .unbind = g_dnl_unbind, 2841d4a0b6cSLukasz Majewski }; 2851d4a0b6cSLukasz Majewski 286c4d0e856SMateusz Zalega /* 287c4d0e856SMateusz Zalega * NOTICE: 288c4d0e856SMateusz Zalega * Registering via USB function name won't be necessary after rewriting 289c4d0e856SMateusz Zalega * g_dnl to support multiple USB functions. 290c4d0e856SMateusz Zalega */ 291c4d0e856SMateusz Zalega int g_dnl_register(const char *name) 2921d4a0b6cSLukasz Majewski { 29325fbf96bSStephen Warren int ret; 2941d4a0b6cSLukasz Majewski 295c4d0e856SMateusz Zalega debug("%s: g_dnl_driver.name = %s\n", __func__, name); 2961d4a0b6cSLukasz Majewski g_dnl_driver.name = name; 2971d4a0b6cSLukasz Majewski 29825fbf96bSStephen Warren ret = usb_composite_register(&g_dnl_driver); 2991d4a0b6cSLukasz Majewski if (ret) { 3001d4a0b6cSLukasz Majewski printf("%s: failed!, error: %d\n", __func__, ret); 3011d4a0b6cSLukasz Majewski return ret; 3021d4a0b6cSLukasz Majewski } 3031d4a0b6cSLukasz Majewski return 0; 3041d4a0b6cSLukasz Majewski } 3051d4a0b6cSLukasz Majewski 3061d4a0b6cSLukasz Majewski void g_dnl_unregister(void) 3071d4a0b6cSLukasz Majewski { 3081d4a0b6cSLukasz Majewski usb_composite_unregister(&g_dnl_driver); 3091d4a0b6cSLukasz Majewski } 310