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