xref: /openbmc/u-boot/drivers/usb/gadget/g_dnl.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
21d4a0b6cSLukasz Majewski /*
31d4a0b6cSLukasz Majewski  * g_dnl.c -- USB Downloader Gadget
41d4a0b6cSLukasz Majewski  *
51d4a0b6cSLukasz Majewski  * Copyright (C) 2012 Samsung Electronics
61d4a0b6cSLukasz Majewski  * Lukasz Majewski  <l.majewski@samsung.com>
71d4a0b6cSLukasz Majewski  */
81d4a0b6cSLukasz Majewski 
91d4a0b6cSLukasz Majewski #include <common.h>
101d4a0b6cSLukasz Majewski #include <malloc.h>
111d4a0b6cSLukasz Majewski 
121d4a0b6cSLukasz Majewski #include <mmc.h>
131d4a0b6cSLukasz Majewski #include <part.h>
1499fc2221SPaul Kocialkowski #include <usb.h>
151d4a0b6cSLukasz Majewski 
161d4a0b6cSLukasz Majewski #include <g_dnl.h>
17ba4e95c9SLukasz Majewski #include <usb_mass_storage.h>
18a6921adcSLukasz Majewski #include <dfu.h>
19b958fb91SLukasz Majewski #include <thor.h>
201d4a0b6cSLukasz Majewski 
21de4e4edaSSam Protsenko #include <env_callback.h>
22de4e4edaSSam Protsenko 
231d4a0b6cSLukasz Majewski #include "gadget_chips.h"
241d4a0b6cSLukasz Majewski #include "composite.c"
251d4a0b6cSLukasz Majewski 
261d4a0b6cSLukasz Majewski /*
271d4a0b6cSLukasz Majewski  * One needs to define the following:
28a95aee6aSMaxime Ripard  * CONFIG_USB_GADGET_VENDOR_NUM
29a95aee6aSMaxime Ripard  * CONFIG_USB_GADGET_PRODUCT_NUM
30a95aee6aSMaxime Ripard  * CONFIG_USB_GADGET_MANUFACTURER
31e6c0bc06SSam Protsenko  * at e.g. ./configs/<board>_defconfig
321d4a0b6cSLukasz Majewski  */
331d4a0b6cSLukasz Majewski 
341d4a0b6cSLukasz Majewski #define STRING_MANUFACTURER 25
351d4a0b6cSLukasz Majewski #define STRING_PRODUCT 2
36cfc2d0d6SLukasz Majewski /* Index of String Descriptor describing this configuration */
371d4a0b6cSLukasz Majewski #define STRING_USBDOWN 2
38ec9002e4SHeiko Schocher /* Index of String serial */
39ec9002e4SHeiko Schocher #define STRING_SERIAL  3
4012d0b8f5SFelipe Balbi #define MAX_STRING_SERIAL	256
41cfc2d0d6SLukasz Majewski /* Number of supported configurations */
42cfc2d0d6SLukasz Majewski #define CONFIGURATION_NUMBER 1
431d4a0b6cSLukasz Majewski 
441d4a0b6cSLukasz Majewski #define DRIVER_VERSION		"usb_dnl 2.0"
451d4a0b6cSLukasz Majewski 
461d4a0b6cSLukasz Majewski static const char product[] = "USB download gadget";
47ec9002e4SHeiko Schocher static char g_dnl_serial[MAX_STRING_SERIAL];
48a95aee6aSMaxime Ripard static const char manufacturer[] = CONFIG_USB_GADGET_MANUFACTURER;
491d4a0b6cSLukasz Majewski 
g_dnl_set_serialnumber(char * s)50ec9002e4SHeiko Schocher void g_dnl_set_serialnumber(char *s)
51ec9002e4SHeiko Schocher {
52ec9002e4SHeiko Schocher 	memset(g_dnl_serial, 0, MAX_STRING_SERIAL);
53949bf79eSFelipe Balbi 	strncpy(g_dnl_serial, s, MAX_STRING_SERIAL - 1);
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 
64a95aee6aSMaxime Ripard 	.idVendor = __constant_cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM),
65a95aee6aSMaxime Ripard 	.idProduct = __constant_cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM),
66207835b1SFelipe Balbi 	/* .iProduct = DYNAMIC */
67207835b1SFelipe Balbi 	/* .iSerialNumber = DYNAMIC */
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 
g_dnl_unbind(struct usb_composite_dev * cdev)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 
g_dnl_bind_callback_first(void)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 
g_dnl_bind_callback_end(void)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 
g_dnl_do_config(struct usb_configuration * c)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 
g_dnl_config_register(struct usb_composite_dev * cdev)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
board_usb_init(int index,enum usb_init_type init)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
board_usb_cleanup(int index,enum usb_init_type init)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
g_dnl_bind_fixup(struct usb_device_descriptor * dev,const char * name)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 
g_dnl_get_board_bcd_device_number(int gcnum)1677a0d463fSHeiko Schocher __weak int g_dnl_get_board_bcd_device_number(int gcnum)
1687a0d463fSHeiko Schocher {
1697a0d463fSHeiko Schocher 	return gcnum;
1707a0d463fSHeiko Schocher }
1717a0d463fSHeiko Schocher 
g_dnl_board_usb_cable_connected(void)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 
g_dnl_detach(void)179fe1b28c9SRob Herring bool g_dnl_detach(void)
180fe1b28c9SRob Herring {
181fe1b28c9SRob Herring 	return g_dnl_detach_request;
182fe1b28c9SRob Herring }
183fe1b28c9SRob Herring 
g_dnl_trigger_detach(void)184fe1b28c9SRob Herring void g_dnl_trigger_detach(void)
185fe1b28c9SRob Herring {
186fe1b28c9SRob Herring 	g_dnl_detach_request = true;
187fe1b28c9SRob Herring }
188fe1b28c9SRob Herring 
g_dnl_clear_detach(void)189fe1b28c9SRob Herring void g_dnl_clear_detach(void)
190fe1b28c9SRob Herring {
191fe1b28c9SRob Herring 	g_dnl_detach_request = false;
192fe1b28c9SRob Herring }
193fe1b28c9SRob Herring 
g_dnl_get_bcd_device_number(struct usb_composite_dev * cdev)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 
206de4e4edaSSam Protsenko /**
207de4e4edaSSam Protsenko  * Update internal serial number variable when the "serial#" env var changes.
208de4e4edaSSam Protsenko  *
209de4e4edaSSam Protsenko  * Handle all cases, even when flags == H_PROGRAMMATIC or op == env_op_delete.
210de4e4edaSSam Protsenko  */
on_serialno(const char * name,const char * value,enum env_op op,int flags)211de4e4edaSSam Protsenko static int on_serialno(const char *name, const char *value, enum env_op op,
212de4e4edaSSam Protsenko 		int flags)
213de4e4edaSSam Protsenko {
214de4e4edaSSam Protsenko 	g_dnl_set_serialnumber((char *)value);
215de4e4edaSSam Protsenko 	return 0;
216de4e4edaSSam Protsenko }
217de4e4edaSSam Protsenko U_BOOT_ENV_CALLBACK(serialno, on_serialno);
218de4e4edaSSam Protsenko 
g_dnl_bind(struct usb_composite_dev * cdev)2191d4a0b6cSLukasz Majewski static int g_dnl_bind(struct usb_composite_dev *cdev)
2201d4a0b6cSLukasz Majewski {
2211d4a0b6cSLukasz Majewski 	struct usb_gadget *gadget = cdev->gadget;
2221d4a0b6cSLukasz Majewski 	int id, ret;
2231d4a0b6cSLukasz Majewski 	int gcnum;
2241d4a0b6cSLukasz Majewski 
2251d4a0b6cSLukasz Majewski 	debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
2261d4a0b6cSLukasz Majewski 
2271d4a0b6cSLukasz Majewski 	id = usb_string_id(cdev);
2281d4a0b6cSLukasz Majewski 
2291d4a0b6cSLukasz Majewski 	if (id < 0)
2301d4a0b6cSLukasz Majewski 		return id;
2311d4a0b6cSLukasz Majewski 	g_dnl_string_defs[0].id = id;
2321d4a0b6cSLukasz Majewski 	device_desc.iManufacturer = id;
2331d4a0b6cSLukasz Majewski 
2341d4a0b6cSLukasz Majewski 	id = usb_string_id(cdev);
2351d4a0b6cSLukasz Majewski 	if (id < 0)
2361d4a0b6cSLukasz Majewski 		return id;
2371d4a0b6cSLukasz Majewski 
2381d4a0b6cSLukasz Majewski 	g_dnl_string_defs[1].id = id;
2391d4a0b6cSLukasz Majewski 	device_desc.iProduct = id;
2401d4a0b6cSLukasz Majewski 
2412a0583e3SLukasz Majewski 	g_dnl_bind_fixup(&device_desc, cdev->driver->name);
2422a0583e3SLukasz Majewski 
243842778a0SFelipe Balbi 	if (strlen(g_dnl_serial)) {
244ec9002e4SHeiko Schocher 		id = usb_string_id(cdev);
245ec9002e4SHeiko Schocher 		if (id < 0)
246ec9002e4SHeiko Schocher 			return id;
247ec9002e4SHeiko Schocher 
248ec9002e4SHeiko Schocher 		g_dnl_string_defs[2].id = id;
249ec9002e4SHeiko Schocher 		device_desc.iSerialNumber = id;
250842778a0SFelipe Balbi 	}
251ec9002e4SHeiko Schocher 
2521d4a0b6cSLukasz Majewski 	ret = g_dnl_config_register(cdev);
2531d4a0b6cSLukasz Majewski 	if (ret)
2541d4a0b6cSLukasz Majewski 		goto error;
2551d4a0b6cSLukasz Majewski 
2567a0d463fSHeiko Schocher 	gcnum = g_dnl_get_bcd_device_number(cdev);
2571d4a0b6cSLukasz Majewski 	if (gcnum >= 0)
2587a0d463fSHeiko Schocher 		device_desc.bcdDevice = cpu_to_le16(gcnum);
2591d4a0b6cSLukasz Majewski 	else {
2601d4a0b6cSLukasz Majewski 		debug("%s: controller '%s' not recognized\n",
261c4d0e856SMateusz Zalega 			__func__, gadget->name);
2621d4a0b6cSLukasz Majewski 		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
2631d4a0b6cSLukasz Majewski 	}
2641d4a0b6cSLukasz Majewski 
2655a413caeSPantelis Antoniou 	debug("%s: calling usb_gadget_connect for "
266c4d0e856SMateusz Zalega 			"controller '%s'\n", __func__, gadget->name);
2675a413caeSPantelis Antoniou 	usb_gadget_connect(gadget);
2685a413caeSPantelis Antoniou 
2691d4a0b6cSLukasz Majewski 	return 0;
2701d4a0b6cSLukasz Majewski 
2711d4a0b6cSLukasz Majewski  error:
2721d4a0b6cSLukasz Majewski 	g_dnl_unbind(cdev);
2731d4a0b6cSLukasz Majewski 	return -ENOMEM;
2741d4a0b6cSLukasz Majewski }
2751d4a0b6cSLukasz Majewski 
2761d4a0b6cSLukasz Majewski static struct usb_composite_driver g_dnl_driver = {
2771d4a0b6cSLukasz Majewski 	.name = NULL,
2781d4a0b6cSLukasz Majewski 	.dev = &device_desc,
2791d4a0b6cSLukasz Majewski 	.strings = g_dnl_composite_strings,
2801d4a0b6cSLukasz Majewski 
2811d4a0b6cSLukasz Majewski 	.bind = g_dnl_bind,
2821d4a0b6cSLukasz Majewski 	.unbind = g_dnl_unbind,
2831d4a0b6cSLukasz Majewski };
2841d4a0b6cSLukasz Majewski 
285c4d0e856SMateusz Zalega /*
286c4d0e856SMateusz Zalega  * NOTICE:
287c4d0e856SMateusz Zalega  * Registering via USB function name won't be necessary after rewriting
288c4d0e856SMateusz Zalega  * g_dnl to support multiple USB functions.
289c4d0e856SMateusz Zalega  */
g_dnl_register(const char * name)290c4d0e856SMateusz Zalega int g_dnl_register(const char *name)
2911d4a0b6cSLukasz Majewski {
29225fbf96bSStephen Warren 	int ret;
2931d4a0b6cSLukasz Majewski 
294c4d0e856SMateusz Zalega 	debug("%s: g_dnl_driver.name = %s\n", __func__, name);
2951d4a0b6cSLukasz Majewski 	g_dnl_driver.name = name;
2961d4a0b6cSLukasz Majewski 
29725fbf96bSStephen Warren 	ret = usb_composite_register(&g_dnl_driver);
2981d4a0b6cSLukasz Majewski 	if (ret) {
2991d4a0b6cSLukasz Majewski 		printf("%s: failed!, error: %d\n", __func__, ret);
3001d4a0b6cSLukasz Majewski 		return ret;
3011d4a0b6cSLukasz Majewski 	}
3021d4a0b6cSLukasz Majewski 	return 0;
3031d4a0b6cSLukasz Majewski }
3041d4a0b6cSLukasz Majewski 
g_dnl_unregister(void)3051d4a0b6cSLukasz Majewski void g_dnl_unregister(void)
3061d4a0b6cSLukasz Majewski {
3071d4a0b6cSLukasz Majewski 	usb_composite_unregister(&g_dnl_driver);
3081d4a0b6cSLukasz Majewski }
309