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