xref: /rk3399_rockchip-uboot/drivers/usb/gadget/g_dnl.c (revision de4e4edaffeeb56aeb9354662872cbbcaf46d4cd)
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