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
22de4e4edaSSam Protsenko #include <env_callback.h>
23de4e4edaSSam Protsenko
241d4a0b6cSLukasz Majewski #include "gadget_chips.h"
251d4a0b6cSLukasz Majewski #include "composite.c"
261d4a0b6cSLukasz Majewski
271d4a0b6cSLukasz Majewski /*
281d4a0b6cSLukasz Majewski * One needs to define the following:
29*473221daSMaxime Ripard * CONFIG_USB_GADGET_VENDOR_NUM
30*473221daSMaxime Ripard * CONFIG_USB_GADGET_PRODUCT_NUM
31*473221daSMaxime Ripard * CONFIG_USB_GADGET_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];
49*473221daSMaxime Ripard static const char manufacturer[] = CONFIG_USB_GADGET_MANUFACTURER;
501d4a0b6cSLukasz Majewski
g_dnl_set_serialnumber(char * s)51ec9002e4SHeiko Schocher void g_dnl_set_serialnumber(char *s)
52ec9002e4SHeiko Schocher {
53ec9002e4SHeiko Schocher memset(g_dnl_serial, 0, MAX_STRING_SERIAL);
54f61a2da6SJoseph Chen if (s)
55949bf79eSFelipe Balbi strncpy(g_dnl_serial, s, MAX_STRING_SERIAL - 1);
56ec9002e4SHeiko Schocher }
57ec9002e4SHeiko Schocher
581d4a0b6cSLukasz Majewski static struct usb_device_descriptor device_desc = {
591d4a0b6cSLukasz Majewski .bLength = sizeof device_desc,
601d4a0b6cSLukasz Majewski .bDescriptorType = USB_DT_DEVICE,
611d4a0b6cSLukasz Majewski
621d4a0b6cSLukasz Majewski .bcdUSB = __constant_cpu_to_le16(0x0200),
635b718407SJohn Tobias .bDeviceClass = USB_CLASS_PER_INTERFACE,
645b718407SJohn Tobias .bDeviceSubClass = 0, /*0x02:CDC-modem , 0x00:CDC-serial*/
651d4a0b6cSLukasz Majewski
66*473221daSMaxime Ripard .idVendor = __constant_cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM),
67*473221daSMaxime Ripard .idProduct = __constant_cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM),
68207835b1SFelipe Balbi /* .iProduct = DYNAMIC */
69207835b1SFelipe Balbi /* .iSerialNumber = DYNAMIC */
701d4a0b6cSLukasz Majewski .bNumConfigurations = 1,
711d4a0b6cSLukasz Majewski };
721d4a0b6cSLukasz Majewski
73c4219a82SLukasz Majewski /*
74c4219a82SLukasz Majewski * static strings, in UTF-8
75c4219a82SLukasz Majewski * IDs for those strings are assigned dynamically at g_dnl_bind()
76c4219a82SLukasz Majewski */
771d4a0b6cSLukasz Majewski static struct usb_string g_dnl_string_defs[] = {
78c4219a82SLukasz Majewski {.s = manufacturer},
79c4219a82SLukasz Majewski {.s = product},
80ec9002e4SHeiko Schocher {.s = g_dnl_serial},
81598cf606SPantelis Antoniou { } /* end of list */
821d4a0b6cSLukasz Majewski };
831d4a0b6cSLukasz Majewski
841d4a0b6cSLukasz Majewski static struct usb_gadget_strings g_dnl_string_tab = {
851d4a0b6cSLukasz Majewski .language = 0x0409, /* en-us */
861d4a0b6cSLukasz Majewski .strings = g_dnl_string_defs,
871d4a0b6cSLukasz Majewski };
881d4a0b6cSLukasz Majewski
891d4a0b6cSLukasz Majewski static struct usb_gadget_strings *g_dnl_composite_strings[] = {
901d4a0b6cSLukasz Majewski &g_dnl_string_tab,
911d4a0b6cSLukasz Majewski NULL,
921d4a0b6cSLukasz Majewski };
931d4a0b6cSLukasz Majewski
g_dnl_unbind(struct usb_composite_dev * cdev)941d4a0b6cSLukasz Majewski static int g_dnl_unbind(struct usb_composite_dev *cdev)
951d4a0b6cSLukasz Majewski {
965a413caeSPantelis Antoniou struct usb_gadget *gadget = cdev->gadget;
975a413caeSPantelis Antoniou
985a413caeSPantelis Antoniou debug("%s: calling usb_gadget_disconnect for "
99c4d0e856SMateusz Zalega "controller '%s'\n", __func__, gadget->name);
1005a413caeSPantelis Antoniou usb_gadget_disconnect(gadget);
1015a413caeSPantelis Antoniou
1021d4a0b6cSLukasz Majewski return 0;
1031d4a0b6cSLukasz Majewski }
1041d4a0b6cSLukasz Majewski
g_dnl_bind_callback_first(void)105c4d0e856SMateusz Zalega static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void)
106c4d0e856SMateusz Zalega {
107c4d0e856SMateusz Zalega return ll_entry_start(struct g_dnl_bind_callback,
108c4d0e856SMateusz Zalega g_dnl_bind_callbacks);
109c4d0e856SMateusz Zalega }
110c4d0e856SMateusz Zalega
g_dnl_bind_callback_end(void)111c4d0e856SMateusz Zalega static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void)
112c4d0e856SMateusz Zalega {
113c4d0e856SMateusz Zalega return ll_entry_end(struct g_dnl_bind_callback,
114c4d0e856SMateusz Zalega g_dnl_bind_callbacks);
115c4d0e856SMateusz Zalega }
116c4d0e856SMateusz Zalega
g_dnl_do_config(struct usb_configuration * c)1171d4a0b6cSLukasz Majewski static int g_dnl_do_config(struct usb_configuration *c)
1181d4a0b6cSLukasz Majewski {
1191d4a0b6cSLukasz Majewski const char *s = c->cdev->driver->name;
120c4d0e856SMateusz Zalega struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first();
1211d4a0b6cSLukasz Majewski
1221d4a0b6cSLukasz Majewski debug("%s: configuration: 0x%p composite dev: 0x%p\n",
1231d4a0b6cSLukasz Majewski __func__, c, c->cdev);
1241d4a0b6cSLukasz Majewski
125c4d0e856SMateusz Zalega for (; callback != g_dnl_bind_callback_end(); callback++)
126c4d0e856SMateusz Zalega if (!strcmp(s, callback->usb_function_name))
127c4d0e856SMateusz Zalega return callback->fptr(c);
128c4d0e856SMateusz Zalega return -ENODEV;
1291d4a0b6cSLukasz Majewski }
1301d4a0b6cSLukasz Majewski
g_dnl_config_register(struct usb_composite_dev * cdev)1311d4a0b6cSLukasz Majewski static int g_dnl_config_register(struct usb_composite_dev *cdev)
1321d4a0b6cSLukasz Majewski {
1337b412ab3SLukasz Majewski struct usb_configuration *config;
1347b412ab3SLukasz Majewski const char *name = "usb_dnload";
1351d4a0b6cSLukasz Majewski
1367b412ab3SLukasz Majewski config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config));
1377b412ab3SLukasz Majewski if (!config)
1387b412ab3SLukasz Majewski return -ENOMEM;
1391d4a0b6cSLukasz Majewski
1407b412ab3SLukasz Majewski memset(config, 0, sizeof(*config));
1417b412ab3SLukasz Majewski
1427b412ab3SLukasz Majewski config->label = name;
1437b412ab3SLukasz Majewski config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
1447b412ab3SLukasz Majewski config->bConfigurationValue = CONFIGURATION_NUMBER;
1457b412ab3SLukasz Majewski config->iConfiguration = STRING_USBDOWN;
1467b412ab3SLukasz Majewski config->bind = g_dnl_do_config;
1477b412ab3SLukasz Majewski
1487b412ab3SLukasz Majewski return usb_add_config(cdev, config);
1491d4a0b6cSLukasz Majewski }
1501d4a0b6cSLukasz Majewski
151c5398cc9SHeiko Schocher __weak
board_usb_init(int index,enum usb_init_type init)15299fc2221SPaul Kocialkowski int board_usb_init(int index, enum usb_init_type init)
15399fc2221SPaul Kocialkowski {
15499fc2221SPaul Kocialkowski return 0;
15599fc2221SPaul Kocialkowski }
15699fc2221SPaul Kocialkowski
15799fc2221SPaul Kocialkowski __weak
board_usb_cleanup(int index,enum usb_init_type init)15899fc2221SPaul Kocialkowski int board_usb_cleanup(int index, enum usb_init_type init)
15999fc2221SPaul Kocialkowski {
16099fc2221SPaul Kocialkowski return 0;
16199fc2221SPaul Kocialkowski }
16299fc2221SPaul Kocialkowski
16399fc2221SPaul Kocialkowski __weak
g_dnl_bind_fixup(struct usb_device_descriptor * dev,const char * name)164d6eae7b0SLukasz Majewski int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
165c5398cc9SHeiko Schocher {
166c5398cc9SHeiko Schocher return 0;
167c5398cc9SHeiko Schocher }
168c5398cc9SHeiko Schocher
g_dnl_get_board_bcd_device_number(int gcnum)1697a0d463fSHeiko Schocher __weak int g_dnl_get_board_bcd_device_number(int gcnum)
1707a0d463fSHeiko Schocher {
1717a0d463fSHeiko Schocher return gcnum;
1727a0d463fSHeiko Schocher }
1737a0d463fSHeiko Schocher
g_dnl_board_usb_cable_connected(void)17475504e95SMateusz Zalega __weak int g_dnl_board_usb_cable_connected(void)
17575504e95SMateusz Zalega {
17675504e95SMateusz Zalega return -EOPNOTSUPP;
17775504e95SMateusz Zalega }
17875504e95SMateusz Zalega
179fe1b28c9SRob Herring static bool g_dnl_detach_request;
180fe1b28c9SRob Herring
g_dnl_detach(void)181fe1b28c9SRob Herring bool g_dnl_detach(void)
182fe1b28c9SRob Herring {
183fe1b28c9SRob Herring return g_dnl_detach_request;
184fe1b28c9SRob Herring }
185fe1b28c9SRob Herring
g_dnl_trigger_detach(void)186fe1b28c9SRob Herring void g_dnl_trigger_detach(void)
187fe1b28c9SRob Herring {
188fe1b28c9SRob Herring g_dnl_detach_request = true;
189fe1b28c9SRob Herring }
190fe1b28c9SRob Herring
g_dnl_clear_detach(void)191fe1b28c9SRob Herring void g_dnl_clear_detach(void)
192fe1b28c9SRob Herring {
193fe1b28c9SRob Herring g_dnl_detach_request = false;
194fe1b28c9SRob Herring }
195fe1b28c9SRob Herring
g_dnl_get_bcd_device_number(struct usb_composite_dev * cdev)1967a0d463fSHeiko Schocher static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev)
1977a0d463fSHeiko Schocher {
1987a0d463fSHeiko Schocher struct usb_gadget *gadget = cdev->gadget;
1997a0d463fSHeiko Schocher int gcnum;
2007a0d463fSHeiko Schocher
2017a0d463fSHeiko Schocher gcnum = usb_gadget_controller_number(gadget);
2027a0d463fSHeiko Schocher if (gcnum > 0)
2037a0d463fSHeiko Schocher gcnum += 0x200;
2047a0d463fSHeiko Schocher
2057a0d463fSHeiko Schocher return g_dnl_get_board_bcd_device_number(gcnum);
2067a0d463fSHeiko Schocher }
2077a0d463fSHeiko Schocher
208de4e4edaSSam Protsenko /**
209de4e4edaSSam Protsenko * Update internal serial number variable when the "serial#" env var changes.
210de4e4edaSSam Protsenko *
211de4e4edaSSam Protsenko * Handle all cases, even when flags == H_PROGRAMMATIC or op == env_op_delete.
212de4e4edaSSam Protsenko */
on_serialno(const char * name,const char * value,enum env_op op,int flags)213de4e4edaSSam Protsenko static int on_serialno(const char *name, const char *value, enum env_op op,
214de4e4edaSSam Protsenko int flags)
215de4e4edaSSam Protsenko {
216de4e4edaSSam Protsenko g_dnl_set_serialnumber((char *)value);
217de4e4edaSSam Protsenko return 0;
218de4e4edaSSam Protsenko }
219de4e4edaSSam Protsenko U_BOOT_ENV_CALLBACK(serialno, on_serialno);
220de4e4edaSSam Protsenko
g_dnl_bind(struct usb_composite_dev * cdev)2211d4a0b6cSLukasz Majewski static int g_dnl_bind(struct usb_composite_dev *cdev)
2221d4a0b6cSLukasz Majewski {
2231d4a0b6cSLukasz Majewski struct usb_gadget *gadget = cdev->gadget;
2241d4a0b6cSLukasz Majewski int id, ret;
2251d4a0b6cSLukasz Majewski int gcnum;
2261d4a0b6cSLukasz Majewski
2271d4a0b6cSLukasz Majewski debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
2281d4a0b6cSLukasz Majewski
2291d4a0b6cSLukasz Majewski id = usb_string_id(cdev);
2301d4a0b6cSLukasz Majewski
2311d4a0b6cSLukasz Majewski if (id < 0)
2321d4a0b6cSLukasz Majewski return id;
2331d4a0b6cSLukasz Majewski g_dnl_string_defs[0].id = id;
2341d4a0b6cSLukasz Majewski device_desc.iManufacturer = id;
2351d4a0b6cSLukasz Majewski
2361d4a0b6cSLukasz Majewski id = usb_string_id(cdev);
2371d4a0b6cSLukasz Majewski if (id < 0)
2381d4a0b6cSLukasz Majewski return id;
2391d4a0b6cSLukasz Majewski
2401d4a0b6cSLukasz Majewski g_dnl_string_defs[1].id = id;
2411d4a0b6cSLukasz Majewski device_desc.iProduct = id;
2421d4a0b6cSLukasz Majewski
2432a0583e3SLukasz Majewski g_dnl_bind_fixup(&device_desc, cdev->driver->name);
2442a0583e3SLukasz Majewski
245842778a0SFelipe Balbi if (strlen(g_dnl_serial)) {
246ec9002e4SHeiko Schocher id = usb_string_id(cdev);
247ec9002e4SHeiko Schocher if (id < 0)
248ec9002e4SHeiko Schocher return id;
249ec9002e4SHeiko Schocher
250ec9002e4SHeiko Schocher g_dnl_string_defs[2].id = id;
251ec9002e4SHeiko Schocher device_desc.iSerialNumber = id;
252842778a0SFelipe Balbi }
253ec9002e4SHeiko Schocher
2541d4a0b6cSLukasz Majewski ret = g_dnl_config_register(cdev);
2551d4a0b6cSLukasz Majewski if (ret)
2561d4a0b6cSLukasz Majewski goto error;
2571d4a0b6cSLukasz Majewski
2587a0d463fSHeiko Schocher gcnum = g_dnl_get_bcd_device_number(cdev);
2591d4a0b6cSLukasz Majewski if (gcnum >= 0)
2607a0d463fSHeiko Schocher device_desc.bcdDevice = cpu_to_le16(gcnum);
2611d4a0b6cSLukasz Majewski else {
2621d4a0b6cSLukasz Majewski debug("%s: controller '%s' not recognized\n",
263c4d0e856SMateusz Zalega __func__, gadget->name);
2641d4a0b6cSLukasz Majewski device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
2651d4a0b6cSLukasz Majewski }
2661d4a0b6cSLukasz Majewski
2675a413caeSPantelis Antoniou debug("%s: calling usb_gadget_connect for "
268c4d0e856SMateusz Zalega "controller '%s'\n", __func__, gadget->name);
2695a413caeSPantelis Antoniou usb_gadget_connect(gadget);
2705a413caeSPantelis Antoniou
2711d4a0b6cSLukasz Majewski return 0;
2721d4a0b6cSLukasz Majewski
2731d4a0b6cSLukasz Majewski error:
2741d4a0b6cSLukasz Majewski g_dnl_unbind(cdev);
2751d4a0b6cSLukasz Majewski return -ENOMEM;
2761d4a0b6cSLukasz Majewski }
2771d4a0b6cSLukasz Majewski
2781d4a0b6cSLukasz Majewski static struct usb_composite_driver g_dnl_driver = {
2791d4a0b6cSLukasz Majewski .name = NULL,
2801d4a0b6cSLukasz Majewski .dev = &device_desc,
2811d4a0b6cSLukasz Majewski .strings = g_dnl_composite_strings,
2821d4a0b6cSLukasz Majewski
2831d4a0b6cSLukasz Majewski .bind = g_dnl_bind,
2841d4a0b6cSLukasz Majewski .unbind = g_dnl_unbind,
2851d4a0b6cSLukasz Majewski };
2861d4a0b6cSLukasz Majewski
287c4d0e856SMateusz Zalega /*
288c4d0e856SMateusz Zalega * NOTICE:
289c4d0e856SMateusz Zalega * Registering via USB function name won't be necessary after rewriting
290c4d0e856SMateusz Zalega * g_dnl to support multiple USB functions.
291c4d0e856SMateusz Zalega */
g_dnl_register(const char * name)292c4d0e856SMateusz Zalega int g_dnl_register(const char *name)
2931d4a0b6cSLukasz Majewski {
29425fbf96bSStephen Warren int ret;
2951d4a0b6cSLukasz Majewski
296c4d0e856SMateusz Zalega debug("%s: g_dnl_driver.name = %s\n", __func__, name);
2971d4a0b6cSLukasz Majewski g_dnl_driver.name = name;
2981d4a0b6cSLukasz Majewski
29925fbf96bSStephen Warren ret = usb_composite_register(&g_dnl_driver);
3001d4a0b6cSLukasz Majewski if (ret) {
3011d4a0b6cSLukasz Majewski printf("%s: failed!, error: %d\n", __func__, ret);
3021d4a0b6cSLukasz Majewski return ret;
3031d4a0b6cSLukasz Majewski }
3041d4a0b6cSLukasz Majewski return 0;
3051d4a0b6cSLukasz Majewski }
3061d4a0b6cSLukasz Majewski
g_dnl_unregister(void)3071d4a0b6cSLukasz Majewski void g_dnl_unregister(void)
3081d4a0b6cSLukasz Majewski {
3091d4a0b6cSLukasz Majewski usb_composite_unregister(&g_dnl_driver);
3101d4a0b6cSLukasz Majewski }
311