blob: 98560b8abd4ef5dc94bbcddb331c331093b3353d [file] [log] [blame]
Lukasz Majewski1d4a0b62012-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 Denk1a459662013-07-08 09:37:19 +02007 * SPDX-License-Identifier: GPL-2.0+
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +02008 */
9
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +020010#include <common.h>
11#include <malloc.h>
12
13#include <mmc.h>
14#include <part.h>
15
16#include <g_dnl.h>
Lukasz Majewskiba4e95c2013-09-17 15:58:21 +020017#include <usb_mass_storage.h>
Lukasz Majewskia6921ad2013-09-17 15:58:23 +020018#include <dfu.h>
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +020019
20#include "gadget_chips.h"
21#include "composite.c"
22
23/*
24 * One needs to define the following:
25 * CONFIG_G_DNL_VENDOR_NUM
26 * CONFIG_G_DNL_PRODUCT_NUM
27 * CONFIG_G_DNL_MANUFACTURER
28 * at e.g. ./include/configs/<board>.h
29 */
30
31#define STRING_MANUFACTURER 25
32#define STRING_PRODUCT 2
Lukasz Majewskicfc2d0d2013-08-19 17:17:18 +020033/* Index of String Descriptor describing this configuration */
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +020034#define STRING_USBDOWN 2
Lukasz Majewskicfc2d0d2013-08-19 17:17:18 +020035/* Number of supported configurations */
36#define CONFIGURATION_NUMBER 1
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +020037
38#define DRIVER_VERSION "usb_dnl 2.0"
39
40static const char shortname[] = "usb_dnl_";
41static const char product[] = "USB download gadget";
42static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER;
43
44static struct usb_device_descriptor device_desc = {
45 .bLength = sizeof device_desc,
46 .bDescriptorType = USB_DT_DEVICE,
47
48 .bcdUSB = __constant_cpu_to_le16(0x0200),
49 .bDeviceClass = USB_CLASS_COMM,
50 .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/
51
52 .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM),
53 .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM),
54 .iProduct = STRING_PRODUCT,
55 .bNumConfigurations = 1,
56};
57
Lukasz Majewskic4219a82013-08-19 17:17:19 +020058/*
59 * static strings, in UTF-8
60 * IDs for those strings are assigned dynamically at g_dnl_bind()
61 */
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +020062static struct usb_string g_dnl_string_defs[] = {
Lukasz Majewskic4219a82013-08-19 17:17:19 +020063 {.s = manufacturer},
64 {.s = product},
65 { } /* end of list */
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +020066};
67
68static struct usb_gadget_strings g_dnl_string_tab = {
69 .language = 0x0409, /* en-us */
70 .strings = g_dnl_string_defs,
71};
72
73static struct usb_gadget_strings *g_dnl_composite_strings[] = {
74 &g_dnl_string_tab,
75 NULL,
76};
77
78static int g_dnl_unbind(struct usb_composite_dev *cdev)
79{
Pantelis Antoniou5a413ca2012-11-30 08:01:05 +000080 struct usb_gadget *gadget = cdev->gadget;
81
Lukasz Majewski7b412ab2013-10-08 14:30:40 +020082 free(cdev->config);
83 cdev->config = NULL;
Pantelis Antoniou5a413ca2012-11-30 08:01:05 +000084 debug("%s: calling usb_gadget_disconnect for "
85 "controller '%s'\n", shortname, gadget->name);
86 usb_gadget_disconnect(gadget);
87
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +020088 return 0;
89}
90
91static int g_dnl_do_config(struct usb_configuration *c)
92{
93 const char *s = c->cdev->driver->name;
94 int ret = -1;
95
96 debug("%s: configuration: 0x%p composite dev: 0x%p\n",
97 __func__, c, c->cdev);
98
99 printf("GADGET DRIVER: %s\n", s);
100 if (!strcmp(s, "usb_dnl_dfu"))
101 ret = dfu_add(c);
Lukasz Majewskib528f712013-03-05 12:10:17 +0100102 else if (!strcmp(s, "usb_dnl_ums"))
103 ret = fsg_add(c);
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200104
105 return ret;
106}
107
108static int g_dnl_config_register(struct usb_composite_dev *cdev)
109{
Lukasz Majewski7b412ab2013-10-08 14:30:40 +0200110 struct usb_configuration *config;
111 const char *name = "usb_dnload";
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200112
Lukasz Majewski7b412ab2013-10-08 14:30:40 +0200113 config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config));
114 if (!config)
115 return -ENOMEM;
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200116
Lukasz Majewski7b412ab2013-10-08 14:30:40 +0200117 memset(config, 0, sizeof(*config));
118
119 config->label = name;
120 config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
121 config->bConfigurationValue = CONFIGURATION_NUMBER;
122 config->iConfiguration = STRING_USBDOWN;
123 config->bind = g_dnl_do_config;
124
125 return usb_add_config(cdev, config);
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200126}
127
Heiko Schocherc5398cc2013-06-04 11:19:50 +0200128__weak
Lukasz Majewskid6eae7b2013-10-08 14:30:41 +0200129int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
Heiko Schocherc5398cc2013-06-04 11:19:50 +0200130{
131 return 0;
132}
133
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200134static int g_dnl_bind(struct usb_composite_dev *cdev)
135{
136 struct usb_gadget *gadget = cdev->gadget;
137 int id, ret;
138 int gcnum;
139
140 debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
141
142 id = usb_string_id(cdev);
143
144 if (id < 0)
145 return id;
146 g_dnl_string_defs[0].id = id;
147 device_desc.iManufacturer = id;
148
149 id = usb_string_id(cdev);
150 if (id < 0)
151 return id;
152
153 g_dnl_string_defs[1].id = id;
154 device_desc.iProduct = id;
155
Lukasz Majewskid6eae7b2013-10-08 14:30:41 +0200156 g_dnl_bind_fixup(&device_desc, cdev->driver->name);
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200157 ret = g_dnl_config_register(cdev);
158 if (ret)
159 goto error;
160
161 gcnum = usb_gadget_controller_number(gadget);
162
163 debug("gcnum: %d\n", gcnum);
164 if (gcnum >= 0)
165 device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
166 else {
167 debug("%s: controller '%s' not recognized\n",
168 shortname, gadget->name);
169 device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
170 }
171
Pantelis Antoniou5a413ca2012-11-30 08:01:05 +0000172 debug("%s: calling usb_gadget_connect for "
173 "controller '%s'\n", shortname, gadget->name);
174 usb_gadget_connect(gadget);
175
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200176 return 0;
177
178 error:
179 g_dnl_unbind(cdev);
180 return -ENOMEM;
181}
182
183static struct usb_composite_driver g_dnl_driver = {
184 .name = NULL,
185 .dev = &device_desc,
186 .strings = g_dnl_composite_strings,
187
188 .bind = g_dnl_bind,
189 .unbind = g_dnl_unbind,
190};
191
192int g_dnl_register(const char *type)
193{
194 /* We only allow "dfu" atm, so 3 should be enough */
195 static char name[sizeof(shortname) + 3];
196 int ret;
197
198 if (!strcmp(type, "dfu")) {
199 strcpy(name, shortname);
200 strcat(name, type);
Lukasz Majewskib528f712013-03-05 12:10:17 +0100201 } else if (!strcmp(type, "ums")) {
202 strcpy(name, shortname);
203 strcat(name, type);
Lukasz Majewski1d4a0b62012-08-06 14:41:05 +0200204 } else {
205 printf("%s: unknown command: %s\n", __func__, type);
206 return -EINVAL;
207 }
208
209 g_dnl_driver.name = name;
210
211 debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name);
212 ret = usb_composite_register(&g_dnl_driver);
213
214 if (ret) {
215 printf("%s: failed!, error: %d\n", __func__, ret);
216 return ret;
217 }
218
219 return 0;
220}
221
222void g_dnl_unregister(void)
223{
224 usb_composite_unregister(&g_dnl_driver);
225}