blob: 2fa6da4091e16b07ca5b2b3322e133b67550b934 [file] [log] [blame]
Lukasz Majewskic574de52012-08-06 14:41:05 +02001/*
2 * g_dnl.c -- USB Downloader Gadget
3 *
4 * Copyright (C) 2012 Samsung Electronics
5 * Lukasz Majewski <l.majewski@samsung.com>
6 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02007 * SPDX-License-Identifier: GPL-2.0+
Lukasz Majewskic574de52012-08-06 14:41:05 +02008 */
9
Lukasz Majewskic574de52012-08-06 14:41:05 +020010#include <common.h>
11#include <malloc.h>
12
13#include <mmc.h>
14#include <part.h>
Paul Kocialkowski46f97292015-06-12 19:57:01 +020015#include <usb.h>
Lukasz Majewskic574de52012-08-06 14:41:05 +020016
17#include <g_dnl.h>
Lukasz Majewski0c635232013-09-17 15:58:21 +020018#include <usb_mass_storage.h>
Lukasz Majewskic4219462013-09-17 15:58:23 +020019#include <dfu.h>
Lukasz Majewski1c1af4f2013-10-08 14:30:43 +020020#include <thor.h>
Lukasz Majewskic574de52012-08-06 14:41:05 +020021
22#include "gadget_chips.h"
23#include "composite.c"
24
25/*
26 * One needs to define the following:
27 * CONFIG_G_DNL_VENDOR_NUM
28 * CONFIG_G_DNL_PRODUCT_NUM
29 * CONFIG_G_DNL_MANUFACTURER
30 * at e.g. ./include/configs/<board>.h
31 */
32
33#define STRING_MANUFACTURER 25
34#define STRING_PRODUCT 2
Lukasz Majewski0d9752e2013-08-19 17:17:18 +020035/* Index of String Descriptor describing this configuration */
Lukasz Majewskic574de52012-08-06 14:41:05 +020036#define STRING_USBDOWN 2
Heiko Schocher1799fa82013-11-04 14:05:01 +010037/* Index of String serial */
38#define STRING_SERIAL 3
39#define MAX_STRING_SERIAL 32
Lukasz Majewski0d9752e2013-08-19 17:17:18 +020040/* Number of supported configurations */
41#define CONFIGURATION_NUMBER 1
Lukasz Majewskic574de52012-08-06 14:41:05 +020042
43#define DRIVER_VERSION "usb_dnl 2.0"
44
Lukasz Majewskic574de52012-08-06 14:41:05 +020045static const char product[] = "USB download gadget";
Heiko Schocher1799fa82013-11-04 14:05:01 +010046static char g_dnl_serial[MAX_STRING_SERIAL];
Lukasz Majewskic574de52012-08-06 14:41:05 +020047static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER;
48
Heiko Schocher1799fa82013-11-04 14:05:01 +010049void g_dnl_set_serialnumber(char *s)
50{
51 memset(g_dnl_serial, 0, MAX_STRING_SERIAL);
52 if (strlen(s) < MAX_STRING_SERIAL)
53 strncpy(g_dnl_serial, s, strlen(s));
54}
55
Lukasz Majewskic574de52012-08-06 14:41:05 +020056static 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_COMM,
62 .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/
63
64 .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM),
65 .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM),
66 .iProduct = STRING_PRODUCT,
Heiko Schocher1799fa82013-11-04 14:05:01 +010067 .iSerialNumber = STRING_SERIAL,
Lukasz Majewskic574de52012-08-06 14:41:05 +020068 .bNumConfigurations = 1,
69};
70
Lukasz Majewskidbbaef72013-08-19 17:17:19 +020071/*
72 * static strings, in UTF-8
73 * IDs for those strings are assigned dynamically at g_dnl_bind()
74 */
Lukasz Majewskic574de52012-08-06 14:41:05 +020075static struct usb_string g_dnl_string_defs[] = {
Lukasz Majewskidbbaef72013-08-19 17:17:19 +020076 {.s = manufacturer},
77 {.s = product},
Heiko Schocher1799fa82013-11-04 14:05:01 +010078 {.s = g_dnl_serial},
Lukasz Majewskidbbaef72013-08-19 17:17:19 +020079 { } /* end of list */
Lukasz Majewskic574de52012-08-06 14:41:05 +020080};
81
82static struct usb_gadget_strings g_dnl_string_tab = {
83 .language = 0x0409, /* en-us */
84 .strings = g_dnl_string_defs,
85};
86
87static struct usb_gadget_strings *g_dnl_composite_strings[] = {
88 &g_dnl_string_tab,
89 NULL,
90};
91
92static int g_dnl_unbind(struct usb_composite_dev *cdev)
93{
Pantelis Antoniouc29d2be2012-11-30 08:01:05 +000094 struct usb_gadget *gadget = cdev->gadget;
95
96 debug("%s: calling usb_gadget_disconnect for "
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +020097 "controller '%s'\n", __func__, gadget->name);
Pantelis Antoniouc29d2be2012-11-30 08:01:05 +000098 usb_gadget_disconnect(gadget);
99
Lukasz Majewskic574de52012-08-06 14:41:05 +0200100 return 0;
101}
102
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +0200103static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void)
104{
105 return ll_entry_start(struct g_dnl_bind_callback,
106 g_dnl_bind_callbacks);
107}
108
109static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void)
110{
111 return ll_entry_end(struct g_dnl_bind_callback,
112 g_dnl_bind_callbacks);
113}
114
Lukasz Majewskic574de52012-08-06 14:41:05 +0200115static int g_dnl_do_config(struct usb_configuration *c)
116{
117 const char *s = c->cdev->driver->name;
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +0200118 struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first();
Lukasz Majewskic574de52012-08-06 14:41:05 +0200119
120 debug("%s: configuration: 0x%p composite dev: 0x%p\n",
121 __func__, c, c->cdev);
122
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +0200123 for (; callback != g_dnl_bind_callback_end(); callback++)
124 if (!strcmp(s, callback->usb_function_name))
125 return callback->fptr(c);
126 return -ENODEV;
Lukasz Majewskic574de52012-08-06 14:41:05 +0200127}
128
129static int g_dnl_config_register(struct usb_composite_dev *cdev)
130{
Lukasz Majewski8143f222013-10-08 14:30:40 +0200131 struct usb_configuration *config;
132 const char *name = "usb_dnload";
Lukasz Majewskic574de52012-08-06 14:41:05 +0200133
Lukasz Majewski8143f222013-10-08 14:30:40 +0200134 config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config));
135 if (!config)
136 return -ENOMEM;
137
138 memset(config, 0, sizeof(*config));
139
140 config->label = name;
141 config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
142 config->bConfigurationValue = CONFIGURATION_NUMBER;
143 config->iConfiguration = STRING_USBDOWN;
144 config->bind = g_dnl_do_config;
Lukasz Majewskic574de52012-08-06 14:41:05 +0200145
Lukasz Majewski8143f222013-10-08 14:30:40 +0200146 return usb_add_config(cdev, config);
Lukasz Majewskic574de52012-08-06 14:41:05 +0200147}
148
Heiko Schocher07a12ea2013-06-04 11:19:50 +0200149__weak
Paul Kocialkowski46f97292015-06-12 19:57:01 +0200150int board_usb_init(int index, enum usb_init_type init)
151{
152 return 0;
153}
154
155__weak
156int board_usb_cleanup(int index, enum usb_init_type init)
157{
158 return 0;
159}
160
161__weak
Lukasz Majewski44b5b382013-10-08 14:30:41 +0200162int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
Heiko Schocher07a12ea2013-06-04 11:19:50 +0200163{
164 return 0;
165}
166
Heiko Schocher6f464a52013-11-04 14:05:02 +0100167__weak int g_dnl_get_board_bcd_device_number(int gcnum)
168{
169 return gcnum;
170}
171
Mateusz Zalega21fe3f72014-04-30 13:07:48 +0200172__weak int g_dnl_board_usb_cable_connected(void)
173{
174 return -EOPNOTSUPP;
175}
176
Rob Herringd38f24e2014-12-10 14:43:03 -0600177static bool g_dnl_detach_request;
178
179bool g_dnl_detach(void)
180{
181 return g_dnl_detach_request;
182}
183
184void g_dnl_trigger_detach(void)
185{
186 g_dnl_detach_request = true;
187}
188
189void g_dnl_clear_detach(void)
190{
191 g_dnl_detach_request = false;
192}
193
Heiko Schocher6f464a52013-11-04 14:05:02 +0100194static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev)
195{
196 struct usb_gadget *gadget = cdev->gadget;
197 int gcnum;
198
199 gcnum = usb_gadget_controller_number(gadget);
200 if (gcnum > 0)
201 gcnum += 0x200;
202
203 return g_dnl_get_board_bcd_device_number(gcnum);
204}
205
Lukasz Majewskic574de52012-08-06 14:41:05 +0200206static int g_dnl_bind(struct usb_composite_dev *cdev)
207{
208 struct usb_gadget *gadget = cdev->gadget;
209 int id, ret;
210 int gcnum;
211
212 debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
213
214 id = usb_string_id(cdev);
215
216 if (id < 0)
217 return id;
218 g_dnl_string_defs[0].id = id;
219 device_desc.iManufacturer = id;
220
221 id = usb_string_id(cdev);
222 if (id < 0)
223 return id;
224
225 g_dnl_string_defs[1].id = id;
226 device_desc.iProduct = id;
227
Heiko Schocher1799fa82013-11-04 14:05:01 +0100228 id = usb_string_id(cdev);
229 if (id < 0)
230 return id;
231
232 g_dnl_string_defs[2].id = id;
233 device_desc.iSerialNumber = id;
234
Lukasz Majewski44b5b382013-10-08 14:30:41 +0200235 g_dnl_bind_fixup(&device_desc, cdev->driver->name);
Lukasz Majewskic574de52012-08-06 14:41:05 +0200236 ret = g_dnl_config_register(cdev);
237 if (ret)
238 goto error;
239
Heiko Schocher6f464a52013-11-04 14:05:02 +0100240 gcnum = g_dnl_get_bcd_device_number(cdev);
Lukasz Majewskic574de52012-08-06 14:41:05 +0200241 if (gcnum >= 0)
Heiko Schocher6f464a52013-11-04 14:05:02 +0100242 device_desc.bcdDevice = cpu_to_le16(gcnum);
Lukasz Majewskic574de52012-08-06 14:41:05 +0200243 else {
244 debug("%s: controller '%s' not recognized\n",
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +0200245 __func__, gadget->name);
Lukasz Majewskic574de52012-08-06 14:41:05 +0200246 device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
247 }
248
Pantelis Antoniouc29d2be2012-11-30 08:01:05 +0000249 debug("%s: calling usb_gadget_connect for "
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +0200250 "controller '%s'\n", __func__, gadget->name);
Pantelis Antoniouc29d2be2012-11-30 08:01:05 +0000251 usb_gadget_connect(gadget);
252
Lukasz Majewskic574de52012-08-06 14:41:05 +0200253 return 0;
254
255 error:
256 g_dnl_unbind(cdev);
257 return -ENOMEM;
258}
259
260static struct usb_composite_driver g_dnl_driver = {
261 .name = NULL,
262 .dev = &device_desc,
263 .strings = g_dnl_composite_strings,
264
265 .bind = g_dnl_bind,
266 .unbind = g_dnl_unbind,
267};
268
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +0200269/*
270 * NOTICE:
271 * Registering via USB function name won't be necessary after rewriting
272 * g_dnl to support multiple USB functions.
273 */
274int g_dnl_register(const char *name)
Lukasz Majewskic574de52012-08-06 14:41:05 +0200275{
Stephen Warrene239fa52014-05-01 15:45:16 -0600276 int ret;
Lukasz Majewskic574de52012-08-06 14:41:05 +0200277
Mateusz Zalega69cb0bb2014-04-28 21:13:28 +0200278 debug("%s: g_dnl_driver.name = %s\n", __func__, name);
Lukasz Majewskic574de52012-08-06 14:41:05 +0200279 g_dnl_driver.name = name;
280
Stephen Warrene239fa52014-05-01 15:45:16 -0600281 ret = usb_composite_register(&g_dnl_driver);
Lukasz Majewskic574de52012-08-06 14:41:05 +0200282 if (ret) {
283 printf("%s: failed!, error: %d\n", __func__, ret);
284 return ret;
285 }
Lukasz Majewskic574de52012-08-06 14:41:05 +0200286 return 0;
287}
288
289void g_dnl_unregister(void)
290{
291 usb_composite_unregister(&g_dnl_driver);
292}