1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * g_dnl.c -- USB Downloader Gadget
4  *
5  * Copyright (C) 2012 Samsung Electronics
6  * Lukasz Majewski  <l.majewski@samsung.com>
7  */
8 
9 #include <log.h>
10 #include <malloc.h>
11 
12 #include <mmc.h>
13 #include <part.h>
14 #include <usb.h>
15 
16 #include <g_dnl.h>
17 #include <usb_mass_storage.h>
18 #include <dfu.h>
19 #include <thor.h>
20 #include <version.h>
21 
22 #include <env_callback.h>
23 
24 #include "composite.c"
25 
26 /*
27  * One needs to define the following:
28  * CONFIG_USB_GADGET_VENDOR_NUM
29  * CONFIG_USB_GADGET_PRODUCT_NUM
30  * CONFIG_USB_GADGET_MANUFACTURER
31  * at e.g. ./configs/<board>_defconfig
32  */
33 
34 #define STRING_MANUFACTURER 25
35 #define STRING_PRODUCT 2
36 /* Index of String Descriptor describing this configuration */
37 #define STRING_USBDOWN 2
38 /* Index of String serial */
39 #define STRING_SERIAL  3
40 #define MAX_STRING_SERIAL	256
41 /* Number of supported configurations */
42 #define CONFIGURATION_NUMBER 1
43 
44 #define DRIVER_VERSION		"usb_dnl 2.0"
45 
46 static const char product[] = "USB download gadget";
47 static char g_dnl_serial[MAX_STRING_SERIAL];
48 static const char manufacturer[] = CONFIG_USB_GADGET_MANUFACTURER;
49 
g_dnl_set_serialnumber(char * s)50 void g_dnl_set_serialnumber(char *s)
51 {
52 	memset(g_dnl_serial, 0, MAX_STRING_SERIAL);
53 	strncpy(g_dnl_serial, s, MAX_STRING_SERIAL - 1);
54 }
55 
56 static struct usb_device_descriptor device_desc = {
57 	.bLength = sizeof device_desc,
58 	.bDescriptorType = USB_DT_DEVICE,
59 
60 	.bcdUSB = __constant_cpu_to_le16(0x0200),
61 	.bDeviceClass = USB_CLASS_PER_INTERFACE,
62 	.bDeviceSubClass = 0, /*0x02:CDC-modem , 0x00:CDC-serial*/
63 
64 	.idVendor = __constant_cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM),
65 	.idProduct = __constant_cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM),
66 	/* .iProduct = DYNAMIC */
67 	/* .iSerialNumber = DYNAMIC */
68 	.bNumConfigurations = 1,
69 };
70 
71 /*
72  * static strings, in UTF-8
73  * IDs for those strings are assigned dynamically at g_dnl_bind()
74  */
75 static struct usb_string g_dnl_string_defs[] = {
76 	{.s = manufacturer},
77 	{.s = product},
78 	{.s = g_dnl_serial},
79 	{ }		/* end of list */
80 };
81 
82 static struct usb_gadget_strings g_dnl_string_tab = {
83 	.language = 0x0409, /* en-us */
84 	.strings = g_dnl_string_defs,
85 };
86 
87 static struct usb_gadget_strings *g_dnl_composite_strings[] = {
88 	&g_dnl_string_tab,
89 	NULL,
90 };
91 
g_dnl_set_product(const char * s)92 void g_dnl_set_product(const char *s)
93 {
94 	if (s)
95 		g_dnl_string_defs[1].s = s;
96 	else
97 		g_dnl_string_defs[1].s = product;
98 }
99 
g_dnl_unbind(struct usb_composite_dev * cdev)100 static int g_dnl_unbind(struct usb_composite_dev *cdev)
101 {
102 	struct usb_gadget *gadget = cdev->gadget;
103 
104 	debug("%s: calling usb_gadget_disconnect for "
105 			"controller '%s'\n", __func__, gadget->name);
106 	usb_gadget_disconnect(gadget);
107 
108 	return 0;
109 }
110 
g_dnl_bind_callback_first(void)111 static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void)
112 {
113 	return ll_entry_start(struct g_dnl_bind_callback,
114 				g_dnl_bind_callbacks);
115 }
116 
g_dnl_bind_callback_end(void)117 static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void)
118 {
119 	return ll_entry_end(struct g_dnl_bind_callback,
120 				g_dnl_bind_callbacks);
121 }
122 
g_dnl_do_config(struct usb_configuration * c)123 static int g_dnl_do_config(struct usb_configuration *c)
124 {
125 	const char *s = c->cdev->driver->name;
126 	struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first();
127 
128 	debug("%s: configuration: 0x%p composite dev: 0x%p\n",
129 	      __func__, c, c->cdev);
130 
131 	for (; callback != g_dnl_bind_callback_end(); callback++)
132 		if (!strcmp(s, callback->usb_function_name))
133 			return callback->fptr(c);
134 	return -ENODEV;
135 }
136 
g_dnl_config_register(struct usb_composite_dev * cdev)137 static int g_dnl_config_register(struct usb_composite_dev *cdev)
138 {
139 	struct usb_configuration *config;
140 	const char *name = "usb_dnload";
141 
142 	config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config));
143 	if (!config)
144 		return -ENOMEM;
145 
146 	memset(config, 0, sizeof(*config));
147 
148 	config->label = name;
149 	config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
150 	config->bConfigurationValue = CONFIGURATION_NUMBER;
151 	config->iConfiguration = STRING_USBDOWN;
152 	config->bind = g_dnl_do_config;
153 
154 	return usb_add_config(cdev, config);
155 }
156 
157 __weak
board_usb_init(int index,enum usb_init_type init)158 int board_usb_init(int index, enum usb_init_type init)
159 {
160 	return 0;
161 }
162 
163 __weak
board_usb_cleanup(int index,enum usb_init_type init)164 int board_usb_cleanup(int index, enum usb_init_type init)
165 {
166 	return 0;
167 }
168 
169 __weak
g_dnl_bind_fixup(struct usb_device_descriptor * dev,const char * name)170 int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
171 {
172 	return 0;
173 }
174 
g_dnl_get_board_bcd_device_number(int gcnum)175 __weak int g_dnl_get_board_bcd_device_number(int gcnum)
176 {
177 	return gcnum;
178 }
179 
g_dnl_board_usb_cable_connected(void)180 __weak int g_dnl_board_usb_cable_connected(void)
181 {
182 	return -EOPNOTSUPP;
183 }
184 
185 static bool g_dnl_detach_request;
186 
g_dnl_detach(void)187 bool g_dnl_detach(void)
188 {
189 	return g_dnl_detach_request;
190 }
191 
g_dnl_trigger_detach(void)192 void g_dnl_trigger_detach(void)
193 {
194 	g_dnl_detach_request = true;
195 }
196 
g_dnl_clear_detach(void)197 void g_dnl_clear_detach(void)
198 {
199 	g_dnl_detach_request = false;
200 }
201 
202 /**
203  * Update internal serial number variable when the "serial#" env var changes.
204  *
205  * Handle all cases, even when flags == H_PROGRAMMATIC or op == env_op_delete.
206  */
on_serialno(const char * name,const char * value,enum env_op op,int flags)207 static int on_serialno(const char *name, const char *value, enum env_op op,
208 		int flags)
209 {
210 	if (value)
211 		g_dnl_set_serialnumber((char *)value);
212 	return 0;
213 }
214 U_BOOT_ENV_CALLBACK(serialno, on_serialno);
215 
g_dnl_bind(struct usb_composite_dev * cdev)216 static int g_dnl_bind(struct usb_composite_dev *cdev)
217 {
218 	struct usb_gadget *gadget = cdev->gadget;
219 	int id, ret;
220 	int gcnum;
221 
222 	debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
223 
224 	id = usb_string_id(cdev);
225 
226 	if (id < 0)
227 		return id;
228 	g_dnl_string_defs[0].id = id;
229 	device_desc.iManufacturer = id;
230 
231 	id = usb_string_id(cdev);
232 	if (id < 0)
233 		return id;
234 
235 	g_dnl_string_defs[1].id = id;
236 	device_desc.iProduct = id;
237 
238 	g_dnl_bind_fixup(&device_desc, cdev->driver->name);
239 
240 	if (strlen(g_dnl_serial)) {
241 		id = usb_string_id(cdev);
242 		if (id < 0)
243 			return id;
244 
245 		g_dnl_string_defs[2].id = id;
246 		device_desc.iSerialNumber = id;
247 	}
248 
249 	ret = g_dnl_config_register(cdev);
250 	if (ret)
251 		goto error;
252 
253 	gcnum = g_dnl_get_board_bcd_device_number((U_BOOT_VERSION_NUM << 4) |
254 						  U_BOOT_VERSION_NUM_PATCH);
255 	if (gcnum >= 0)
256 		device_desc.bcdDevice = cpu_to_le16(gcnum);
257 	else {
258 		debug("%s: controller '%s' not recognized\n",
259 			__func__, gadget->name);
260 		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
261 	}
262 
263 	debug("%s: calling usb_gadget_connect for "
264 			"controller '%s'\n", __func__, gadget->name);
265 	usb_gadget_connect(gadget);
266 
267 	return 0;
268 
269  error:
270 	g_dnl_unbind(cdev);
271 	return -ENOMEM;
272 }
273 
274 static struct usb_composite_driver g_dnl_driver = {
275 	.name = NULL,
276 	.dev = &device_desc,
277 	.strings = g_dnl_composite_strings,
278 	.max_speed = USB_SPEED_SUPER,
279 
280 	.bind = g_dnl_bind,
281 	.unbind = g_dnl_unbind,
282 };
283 
284 /*
285  * NOTICE:
286  * Registering via USB function name won't be necessary after rewriting
287  * g_dnl to support multiple USB functions.
288  */
g_dnl_register(const char * name)289 int g_dnl_register(const char *name)
290 {
291 	int ret;
292 
293 	debug("%s: g_dnl_driver.name = %s\n", __func__, name);
294 	g_dnl_driver.name = name;
295 
296 	ret = usb_composite_register(&g_dnl_driver);
297 	if (ret) {
298 		printf("%s: failed!, error: %d\n", __func__, ret);
299 		return ret;
300 	}
301 	return 0;
302 }
303 
g_dnl_unregister(void)304 void g_dnl_unregister(void)
305 {
306 	usb_composite_unregister(&g_dnl_driver);
307 }
308