1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <dirent.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <lib/fdio/util.h>
12 #include <ddk/protocol/usb/modeswitch.h>
13 #include <zircon/device/usb-peripheral.h>
14 #include <zircon/hw/usb.h>
15 #include <zircon/hw/usb/cdc.h>
16 #include <zircon/usb/peripheral/c/fidl.h>
17 #include <zircon/syscalls.h>
18 #include <zircon/types.h>
19
20
21 #include <zircon/types.h>
22
23 #define DEV_USB_PERIPHERAL_DIR "/dev/class/usb-peripheral"
24
25 #define MANUFACTURER_STRING "Zircon"
26 #define CDC_PRODUCT_STRING "CDC Ethernet"
27 #define UMS_PRODUCT_STRING "USB Mass Storage"
28 #define TEST_PRODUCT_STRING "USB Function Test"
29 #define SERIAL_STRING "12345678"
30
31 typedef zircon_usb_peripheral_FunctionDescriptor usb_function_descriptor_t;
32
33 const usb_function_descriptor_t cdc_function_desc = {
34 .interface_class = USB_CLASS_COMM,
35 .interface_subclass = USB_CDC_SUBCLASS_ETHERNET,
36 .interface_protocol = 0,
37 };
38
39 const usb_function_descriptor_t ums_function_desc = {
40 .interface_class = USB_CLASS_MSC,
41 .interface_subclass = USB_SUBCLASS_MSC_SCSI,
42 .interface_protocol = USB_PROTOCOL_MSC_BULK_ONLY,
43 };
44
45 const usb_function_descriptor_t test_function_desc = {
46 .interface_class = USB_CLASS_VENDOR,
47 .interface_subclass = 0,
48 .interface_protocol = 0,
49 };
50
51
52 typedef struct {
53 const usb_function_descriptor_t* desc;
54 const char* product_string;
55 uint16_t vid;
56 uint16_t pid;
57 } usb_function_t;
58
59 static const usb_function_t cdc_function = {
60 .desc = &cdc_function_desc,
61 .product_string = CDC_PRODUCT_STRING,
62 .vid = GOOGLE_USB_VID,
63 .pid = GOOGLE_USB_CDC_PID,
64 };
65
66 static const usb_function_t ums_function = {
67 .desc = &ums_function_desc,
68 .product_string = UMS_PRODUCT_STRING,
69 .vid = GOOGLE_USB_VID,
70 .pid = GOOGLE_USB_UMS_PID,
71 };
72
73 static const usb_function_t test_function = {
74 .desc = &test_function_desc,
75 .product_string = TEST_PRODUCT_STRING,
76 .vid = GOOGLE_USB_VID,
77 .pid = GOOGLE_USB_PERIPHERAL_TEST_PID,
78 };
79
80 static zircon_usb_peripheral_DeviceDescriptor device_desc = {
81 .bcdUSB = htole16(0x0200),
82 .bDeviceClass = 0,
83 .bDeviceSubClass = 0,
84 .bDeviceProtocol = 0,
85 .bMaxPacketSize0 = 64,
86 // idVendor and idProduct are filled in later
87 .bcdDevice = htole16(0x0100),
88 // iManufacturer, iProduct and iSerialNumber are filled in later
89 .bNumConfigurations = 1,
90 };
91
open_usb_device(void)92 static int open_usb_device(void) {
93 struct dirent* de;
94 DIR* dir = opendir(DEV_USB_PERIPHERAL_DIR);
95 if (!dir) {
96 printf("Error opening %s\n", DEV_USB_PERIPHERAL_DIR);
97 return -1;
98 }
99
100 while ((de = readdir(dir)) != NULL) {
101 char devname[128];
102
103 snprintf(devname, sizeof(devname), "%s/%s", DEV_USB_PERIPHERAL_DIR, de->d_name);
104 int fd = open(devname, O_RDWR);
105 if (fd < 0) {
106 printf("Error opening %s\n", devname);
107 continue;
108 }
109
110 closedir(dir);
111 return fd;
112 }
113
114 closedir(dir);
115 return -1;
116 }
117
device_init(zx_handle_t svc,const usb_function_t * function)118 static zx_status_t device_init(zx_handle_t svc, const usb_function_t* function) {
119 zx_status_t status2;
120
121 device_desc.idVendor = htole16(function->vid);
122 device_desc.idProduct = htole16(function->pid);
123
124 // allocate string descriptors
125 zx_status_t status = zircon_usb_peripheral_DeviceAllocStringDesc(svc, MANUFACTURER_STRING,
126 strlen(MANUFACTURER_STRING) + 1,
127 &status2, &device_desc.iManufacturer);
128 if (status == ZX_OK) status = status2;
129 if (status != ZX_OK) {
130 fprintf(stderr, "zircon_usb_peripheral_DeviceAllocStringDesc failed: %d\n", status);
131 return status;
132 }
133 status = zircon_usb_peripheral_DeviceAllocStringDesc(svc, function->product_string,
134 strlen(function->product_string) + 1,
135 &status2, &device_desc.iProduct);
136 if (status == ZX_OK) status = status2;
137 if (status != ZX_OK) {
138 fprintf(stderr, "zircon_usb_peripheral_DeviceAllocStringDesc failed: %d\n", status);
139 return status;
140 }
141 status = zircon_usb_peripheral_DeviceAllocStringDesc(svc, SERIAL_STRING, strlen(SERIAL_STRING) + 1,
142 &status2, &device_desc.iSerialNumber);
143 if (status == ZX_OK) status = status2;
144 if (status != ZX_OK) {
145 fprintf(stderr, "zircon_usb_peripheral_DeviceAllocStringDesc failed: %d\n", status);
146 return status;
147 }
148
149 // set device descriptor
150 status = zircon_usb_peripheral_DeviceSetDeviceDescriptor(svc, &device_desc, &status2);
151 if (status == ZX_OK) status = status2;
152 if (status != ZX_OK) {
153 fprintf(stderr, "zircon_usb_peripheral_DeviceSetDeviceDescriptor failed: %d\n", status);
154 return status;
155 }
156
157 status = zircon_usb_peripheral_DeviceAddFunction(svc, function->desc, &status2);
158 if (status == ZX_OK) status = status2;
159 if (status != ZX_OK) {
160 fprintf(stderr, "zircon_usb_peripheral_DeviceAddFunction failed: %d\n", status);
161 return status;
162 }
163
164 status = zircon_usb_peripheral_DeviceBindFunctions(svc, &status2);
165 if (status == ZX_OK) status = status2;
166 if (status != ZX_OK) {
167 fprintf(stderr, "zircon_usb_peripheral_DeviceBindFunctions failed: %d\n", status);
168 }
169
170 return status;
171 }
172
ums_command(zx_handle_t svc,int argc,const char * argv[])173 static int ums_command(zx_handle_t svc, int argc, const char* argv[]) {
174 zx_status_t status, status2;
175
176 status = zircon_usb_peripheral_DeviceClearFunctions(svc, &status2);
177 if (status == ZX_OK) status = status2;
178 if (status == ZX_OK) {
179 status = device_init(svc, &ums_function);
180 }
181
182 return status == ZX_OK ? 0 : -1;
183 }
184
cdc_command(zx_handle_t svc,int argc,const char * argv[])185 static int cdc_command(zx_handle_t svc, int argc, const char* argv[]) {
186 zx_status_t status, status2;
187
188 status = zircon_usb_peripheral_DeviceClearFunctions(svc, &status2);
189 if (status == ZX_OK) status = status2;
190 if (status == ZX_OK) {
191 status = device_init(svc, &cdc_function);
192 }
193
194 return status == ZX_OK ? 0 : -1;
195 }
196
test_command(zx_handle_t svc,int argc,const char * argv[])197 static int test_command(zx_handle_t svc, int argc, const char* argv[]) {
198 zx_status_t status, status2;
199
200 status = zircon_usb_peripheral_DeviceClearFunctions(svc, &status2);
201 if (status == ZX_OK) status = status2;
202 if (status == ZX_OK) {
203 status = device_init(svc, &test_function);
204 }
205
206 return status == ZX_OK ? 0 : -1;
207 }
208
mode_command(zx_handle_t svc,int argc,const char * argv[])209 static int mode_command(zx_handle_t svc, int argc, const char* argv[]) {
210 zx_status_t status = ZX_OK;
211 zx_status_t status2;
212
213 if (argc == 1) {
214 // print current mode
215 usb_mode_t mode;
216 status = zircon_usb_peripheral_DeviceGetMode(svc, &status2, &mode);
217 if (status == ZX_OK) status = status2;
218 if (status != ZX_OK) {
219 fprintf(stderr, "zircon_usb_peripheral_DeviceGetMode failed: %d\n", status);
220 } else {
221 switch (mode) {
222 case USB_MODE_NONE:
223 printf("NONE\n");
224 break;
225 case USB_MODE_HOST:
226 printf("HOST\n");
227 break;
228 case USB_MODE_PERIPHERAL:
229 printf("PERIPHERAL\n");
230 break;
231 case USB_MODE_OTG:
232 printf("OTG\n");
233 break;
234 default:
235 printf("unknown mode %d\n", mode);
236 break;
237 }
238 }
239 } else {
240 usb_mode_t mode;
241 if (strcasecmp(argv[1], "none") == 0) {
242 mode = USB_MODE_NONE;
243 } else if (strcasecmp(argv[1], "host") == 0) {
244 mode = USB_MODE_HOST;
245 } else if (strcasecmp(argv[1], "peripheral") == 0) {
246 mode = USB_MODE_PERIPHERAL;
247 } else if (strcasecmp(argv[1], "otg") == 0) {
248 mode = USB_MODE_OTG;
249 } else {
250 fprintf(stderr, "unknown USB mode %s\n", argv[1]);
251 status = ZX_ERR_INVALID_ARGS;
252 }
253
254 if (status == ZX_OK) {
255 status = zircon_usb_peripheral_DeviceSetMode(svc, mode, &status2);
256 if (status == ZX_OK) status = status2;
257 if (status != ZX_OK) {
258 fprintf(stderr, "zircon_usb_peripheral_DeviceSetMode failed: %d\n", status);
259 }
260 }
261 }
262
263 return status;
264 }
265
266 typedef struct {
267 const char* name;
268 int (*command)(zx_handle_t svc, int argc, const char* argv[]);
269 const char* description;
270 } usbctl_command_t;
271
272 static usbctl_command_t commands[] = {
273 {
274 "init-ums",
275 ums_command,
276 "init-ums - initializes the USB Mass Storage function"
277 },
278 {
279 "init-cdc",
280 cdc_command,
281 "init-cdc - initializes the CDC Ethernet function"
282 },
283 {
284 "init-test",
285 test_command,
286 "init-test - initializes the USB Peripheral Test function"
287 },
288 {
289 "mode",
290 mode_command,
291 "mode [none|host|peripheral|otg] - sets the current USB mode. "
292 "Returns the current mode if no additional arugment is provided."
293 },
294 { NULL, NULL, NULL },
295 };
296
usage(void)297 static void usage(void) {
298 fprintf(stderr, "usage: \"usbctl <command>\", where command is one of:\n");
299
300 usbctl_command_t* command = commands;
301 while (command->name) {
302 fprintf(stderr, " %s\n", command->description);
303 command++;
304 }
305 }
306
main(int argc,const char ** argv)307 int main(int argc, const char** argv) {
308 if (argc < 2) {
309 usage();
310 return -1;
311 }
312
313 int fd = open_usb_device();
314 if (fd < 0) {
315 fprintf(stderr, "could not find a device in %s\n", DEV_USB_PERIPHERAL_DIR);
316 return fd;
317 }
318
319 zx_handle_t svc;
320 zx_status_t status = fdio_get_service_handle(fd, &svc);
321 if (status != ZX_OK) {
322 close(fd);
323 return status;
324 }
325
326 const char* command_name = argv[1];
327 usbctl_command_t* command = commands;
328 while (command->name) {
329 if (!strcmp(command_name, command->name)) {
330 status = command->command(svc, argc - 1, argv + 1);
331 goto done;
332 }
333 command++;
334 }
335 // if we fall through, print usage
336 usage();
337 status = ZX_ERR_INVALID_ARGS;
338
339 done:
340 zx_handle_close(svc);
341 close(fd);
342 return status;
343 }
344