Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
/* usb_descriptor.c - USB common device descriptor definition */

/*
 * Copyright (c) 2017 PHYTEC Messtechnik GmbH
 * Copyright (c) 2017, 2018 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <string.h>
#include <misc/byteorder.h>
#include <misc/__assert.h>
#include <usb/usbstruct.h>
#include <usb/usb_device.h>
#include <usb/usb_common.h>
#include "usb_descriptor.h"

#define SYS_LOG_LEVEL CONFIG_SYS_LOG_USB_DEVICE_LEVEL
#include <logging/sys_log.h>

/*
 * The last index of the initializer_string without null character is:
 *   ascii_idx_max = bLength / 2 - 2
 * Use this macro to determine the last index of ASCII7 string.
 */
#define USB_BSTRING_ASCII_IDX_MAX(n)	(n / 2 - 2)

/*
 * The last index of the bString is:
 *   utf16le_idx_max = sizeof(initializer_string) * 2 - 2 - 1
 *   utf16le_idx_max = bLength - 2 - 1
 * Use this macro to determine the last index of UTF16LE string.
 */
#define USB_BSTRING_UTF16LE_IDX_MAX(n)	(n - 3)

/* Linker-defined symbols bound the USB descriptor structs */
extern struct usb_desc_header __usb_descriptor_start[];
extern struct usb_desc_header __usb_descriptor_end[];
extern struct usb_cfg_data __usb_data_start[];
extern struct usb_cfg_data __usb_data_end[];

/* Structure representing the global USB description */
struct common_descriptor {
	struct usb_device_descriptor device_descriptor;
	struct usb_cfg_descriptor cfg_descr;
} __packed;

#define USB_DESC_MANUFACTURER_IDX			1
#define USB_DESC_PRODUCT_IDX				2
#define USB_DESC_SERIAL_NUMBER_IDX			3

/*
 * Device and configuration descriptor placed in the device section,
 * no additional descriptor may be placed there.
 */
USBD_DEVICE_DESCR_DEFINE(primary) struct common_descriptor common_desc = {
	/* Device descriptor */
	.device_descriptor = {
		.bLength = sizeof(struct usb_device_descriptor),
		.bDescriptorType = USB_DEVICE_DESC,
		.bcdUSB = sys_cpu_to_le16(USB_2_0),
#ifdef CONFIG_USB_COMPOSITE_DEVICE
		.bDeviceClass = MISC_CLASS,
		.bDeviceSubClass = 0x02,
		.bDeviceProtocol = 0x01,
#else
		.bDeviceClass = 0,
		.bDeviceSubClass = 0,
		.bDeviceProtocol = 0,
#endif
		.bMaxPacketSize0 = MAX_PACKET_SIZE0,
		.idVendor = sys_cpu_to_le16((u16_t)CONFIG_USB_DEVICE_VID),
		.idProduct = sys_cpu_to_le16((u16_t)CONFIG_USB_DEVICE_PID),
		.bcdDevice = sys_cpu_to_le16(BCDDEVICE_RELNUM),
		.iManufacturer = USB_DESC_MANUFACTURER_IDX,
		.iProduct = USB_DESC_PRODUCT_IDX,
		.iSerialNumber = USB_DESC_SERIAL_NUMBER_IDX,
		.bNumConfigurations = 1,
	},
	/* Configuration descriptor */
	.cfg_descr = {
		.bLength = sizeof(struct usb_cfg_descriptor),
		.bDescriptorType = USB_CONFIGURATION_DESC,
		/*wTotalLength will be fixed in usb_fix_descriptor() */
		.wTotalLength = 0,
		.bNumInterfaces = 0,
		.bConfigurationValue = 1,
		.iConfiguration = 0,
		.bmAttributes = USB_CONFIGURATION_ATTRIBUTES,
		.bMaxPower = MAX_LOW_POWER,
	},
};

struct usb_string_desription {
	struct usb_string_descriptor lang_descr;
	struct usb_mfr_descriptor {
		u8_t bLength;
		u8_t bDescriptorType;
		u8_t bString[USB_BSTRING_LENGTH(
				CONFIG_USB_DEVICE_MANUFACTURER)];
	} __packed utf16le_mfr;

	struct usb_product_descriptor {
		u8_t bLength;
		u8_t bDescriptorType;
		u8_t bString[USB_BSTRING_LENGTH(CONFIG_USB_DEVICE_PRODUCT)];
	} __packed utf16le_product;

	struct usb_sn_descriptor {
		u8_t bLength;
		u8_t bDescriptorType;
		u8_t bString[USB_BSTRING_LENGTH(CONFIG_USB_DEVICE_SN)];
	} __packed utf16le_sn;
} __packed;

/*
 * Language, Manufacturer, Product and Serial string descriptors,
 * placed in the string section.
 * FIXME: These should be sorted additionally.
 */
USBD_STRING_DESCR_DEFINE(primary) struct usb_string_desription string_descr = {
	.lang_descr = {
		.bLength = sizeof(struct usb_string_descriptor),
		.bDescriptorType = USB_STRING_DESC,
		.bString = sys_cpu_to_le16(0x0409),
	},
	/* Manufacturer String Descriptor */
	.utf16le_mfr = {
		.bLength = USB_STRING_DESCRIPTOR_LENGTH(
				CONFIG_USB_DEVICE_MANUFACTURER),
		.bDescriptorType = USB_STRING_DESC,
		.bString = CONFIG_USB_DEVICE_MANUFACTURER,
	},
	/* Product String Descriptor */
	.utf16le_product = {
		.bLength = USB_STRING_DESCRIPTOR_LENGTH(
				CONFIG_USB_DEVICE_PRODUCT),
		.bDescriptorType = USB_STRING_DESC,
		.bString = CONFIG_USB_DEVICE_PRODUCT,
	},
	/* Serial Number String Descriptor */
	.utf16le_sn = {
		.bLength = USB_STRING_DESCRIPTOR_LENGTH(CONFIG_USB_DEVICE_SN),
		.bDescriptorType = USB_STRING_DESC,
		.bString = CONFIG_USB_DEVICE_SN,
	},
};

/* This element marks the end of the entire descriptor. */
USBD_TERM_DESCR_DEFINE(primary) struct usb_desc_header term_descr = {
	.bLength = 0,
	.bDescriptorType = 0,
};

/*
 * This function fixes bString by transforming the ASCII-7 string
 * into a UTF16-LE during runtime.
 */
static void ascii7_to_utf16le(void *descriptor)
{
	struct usb_string_descriptor *str_descr = descriptor;
	int idx_max = USB_BSTRING_UTF16LE_IDX_MAX(str_descr->bLength);
	int ascii_idx_max = USB_BSTRING_ASCII_IDX_MAX(str_descr->bLength);
	u8_t *buf = (u8_t *)&str_descr->bString;

	SYS_LOG_DBG("idx_max %d, ascii_idx_max %d, buf %x",
		    idx_max, ascii_idx_max, (u32_t)buf);

	for (int i = idx_max; i >= 0; i -= 2) {
		SYS_LOG_DBG("char %c : %x, idx %d -> %d",
			    buf[ascii_idx_max],
			    buf[ascii_idx_max],
			    ascii_idx_max, i);
		__ASSERT(buf[ascii_idx_max] > 0x1F && buf[ascii_idx_max] < 0x7F,
			 "Only printable ascii-7 characters are allowed in USB "
			 "string descriptors");
		buf[i] = 0;
		buf[i - 1] = buf[ascii_idx_max--];
	}
}

/*
 * Look for the bString that has the address equal to the ptr and
 * return its index. Use it to determine the index of the bString and
 * assign it to the interfaces iInterface variable.
 */
int usb_get_str_descriptor_idx(void *ptr)
{
	struct usb_desc_header *head = __usb_descriptor_start;
	struct usb_string_descriptor *str = ptr;
	int str_descr_idx = 0;

	while (head->bLength != 0) {
		switch (head->bDescriptorType) {
		case USB_STRING_DESC:
			if (head == (struct usb_desc_header *)str) {
				return str_descr_idx;
			}

			str_descr_idx += 1;
			break;
		default:
			break;
		}

		/* move to next descriptor */
		head = (struct usb_desc_header *)((u8_t *)head + head->bLength);
	}

	return 0;
}

/*
 * Validate endpoint address and Update the endpoint descriptors at runtime,
 * the result depends on the capabilities of the driver and the number and
 * type of endpoints.
 * The default endpoint address is stored in endpoint descriptor and
 * usb_ep_cfg_data, so both variables bEndpointAddress and ep_addr need
 * to be updated.
 */
static int usb_validate_ep_cfg_data(struct usb_ep_descriptor * const ep_descr,
				    struct usb_cfg_data * const cfg_data,
				    u32_t *requested_ep)
{
	for (int i = 0; i < cfg_data->num_endpoints; i++) {
		struct usb_ep_cfg_data *ep_data = cfg_data->endpoint;

		/*
		 * Trying to find the right entry in the usb_ep_cfg_data.
		 */
		if (ep_descr->bEndpointAddress != ep_data[i].ep_addr) {
			continue;
		}

		for (u8_t idx = 1; idx < 16; idx++) {
			struct usb_dc_ep_cfg_data ep_cfg;

			ep_cfg.ep_type = ep_descr->bmAttributes;
			ep_cfg.ep_mps = ep_descr->wMaxPacketSize;
			ep_cfg.ep_addr = ep_descr->bEndpointAddress;
			if (ep_cfg.ep_addr & USB_EP_DIR_IN) {
				if ((*requested_ep & (1 << (idx + 16)))) {
					continue;
				}

				ep_cfg.ep_addr = (USB_EP_DIR_IN | idx);
			} else {
				if ((*requested_ep & (1 << (idx)))) {
					continue;
				}

				ep_cfg.ep_addr = idx;
			}
			if (!usb_dc_ep_check_cap(&ep_cfg)) {
				ep_descr->bEndpointAddress = ep_cfg.ep_addr;
				ep_data[i].ep_addr = ep_cfg.ep_addr;
				if (ep_cfg.ep_addr & USB_EP_DIR_IN) {
					*requested_ep |= (1 << (idx + 16));
				} else {
					*requested_ep |= (1 << idx);
				}
				SYS_LOG_DBG("endpoint 0x%x",
					    ep_data[i].ep_addr);
				return 0;
			}
		}
	}
	return -1;
}

/*
 * The interface descriptor of a USB function must be assigned to the
 * usb_cfg_data so that usb_ep_cfg_data and matching endpoint descriptor
 * can be found.
 */
static struct usb_cfg_data *usb_get_cfg_data(struct usb_if_descriptor *iface)
{
	size_t length = (__usb_data_end - __usb_data_start);

	for (size_t i = 0; i < length; i++) {
		if (__usb_data_start[i].interface_descriptor == iface) {
			return &__usb_data_start[i];
		}
	}

	return NULL;
}

/*
 * Default USB SN string descriptor is CONFIG_USB_DEVICE_SN, but sometimes
 * we want use another string as SN descriptor such as the chip's unique
 * ID. So user can implement this function return a string thats will be
 * replaced the default SN.
 * Please note that the new SN descriptor you changed must has same length
 * as CONFIG_USB_DEVICE_SN.
 */
__weak u8_t *usb_update_sn_string_descriptor(void)
{
	return NULL;
}

static void
usb_fix_ascii_sn_string_descriptor(struct usb_sn_descriptor *sn)
{
	u8_t *runtime_sn =  usb_update_sn_string_descriptor();
	int runtime_sn_len, default_sn_len;

	if (!runtime_sn) {
		return;
	}

	runtime_sn_len = strlen(runtime_sn);
	default_sn_len = strlen(CONFIG_USB_DEVICE_SN);

	if (runtime_sn_len != default_sn_len) {
		SYS_LOG_ERR("the new SN descriptor doesn't has the same "
			    "length as CONFIG_USB_DEVICE_SN");
		return;
	}

	memcpy(sn->bString, runtime_sn, runtime_sn_len);
}

/*
 * The entire descriptor, placed in the .usb.descriptor section,
 * needs to be fixed before use. Currently, only the length of the
 * entire device configuration (with all interfaces and endpoints)
 * and the string descriptors will be corrected.
 *
 * Restrictions:
 * - just one device configuration (there is only one)
 * - string descriptor must be present
 */
static int usb_fix_descriptor(struct usb_desc_header *head)
{
	struct usb_cfg_descriptor *cfg_descr = NULL;
	struct usb_if_descriptor *if_descr = NULL;
	struct usb_cfg_data *cfg_data = NULL;
	struct usb_ep_descriptor *ep_descr = NULL;
	u8_t numof_ifaces = 0;
	u8_t str_descr_idx = 0;
	u32_t requested_ep = BIT(16) | BIT(0);

	while (head->bLength != 0) {
		switch (head->bDescriptorType) {
		case USB_CONFIGURATION_DESC:
			cfg_descr = (struct usb_cfg_descriptor *)head;
			SYS_LOG_DBG("Configuration descriptor %p", head);
			break;
		case USB_ASSOCIATION_DESC:
			SYS_LOG_DBG("Association descriptor %p", head);
			break;
		case USB_INTERFACE_DESC:
			if_descr = (struct usb_if_descriptor *)head;
			SYS_LOG_DBG("Interface descriptor %p", head);
			if (if_descr->bAlternateSetting) {
				SYS_LOG_DBG("Skip alternate interface");
				break;
			}

			if (if_descr->bInterfaceNumber == 0) {
				cfg_data = usb_get_cfg_data(if_descr);
				if (!cfg_data) {
					SYS_LOG_ERR("There is no usb_cfg_data "
						    "for %p", head);
					return -1;
				}

				if (cfg_data->interface_config) {
					cfg_data->interface_config(
							numof_ifaces);
				}
			}

			numof_ifaces++;
			break;
		case USB_ENDPOINT_DESC:
			SYS_LOG_DBG("Endpoint descriptor %p", head);
			ep_descr = (struct usb_ep_descriptor *)head;
			if (usb_validate_ep_cfg_data(ep_descr,
						     cfg_data,
						     &requested_ep)) {
				SYS_LOG_ERR("Failed to validate endpoints");
				return -1;
			}

			break;
		case 0:
		case USB_STRING_DESC:
			/*
			 * Copy runtime SN string descriptor first, if has
			 */
			if (str_descr_idx == USB_DESC_SERIAL_NUMBER_IDX) {
				struct usb_sn_descriptor *sn =
					(struct usb_sn_descriptor *)head;
				usb_fix_ascii_sn_string_descriptor(sn);
			}
			/*
			 * Skip language descriptor but correct
			 * wTotalLength and bNumInterfaces once.
			 */
			if (str_descr_idx) {
				ascii7_to_utf16le(head);
			} else {
				if (!cfg_descr) {
					SYS_LOG_ERR("Incomplete device "
						    "descriptor");
					return -1;
				}

				SYS_LOG_DBG("Now the wTotalLength is %d",
					    (u8_t *)head - (u8_t *)cfg_descr);
				cfg_descr->wTotalLength =
					sys_cpu_to_le16((u8_t *)head -
							(u8_t *)cfg_descr);
				cfg_descr->bNumInterfaces = numof_ifaces;
			}

			str_descr_idx += 1;

			break;
		default:
			break;
		}

		/* Move to next descriptor */
		head = (struct usb_desc_header *)((u8_t *)head + head->bLength);
	}

	if ((head + 1) != __usb_descriptor_end) {
		SYS_LOG_DBG("try to fix next descriptor at %p", head + 1);
		return usb_fix_descriptor(head + 1);
	}

	return 0;
}


u8_t *usb_get_device_descriptor(void)
{
	SYS_LOG_DBG("__usb_descriptor_start %p", __usb_descriptor_start);
	SYS_LOG_DBG("__usb_descriptor_end %p", __usb_descriptor_end);

	if (usb_fix_descriptor(__usb_descriptor_start)) {
		SYS_LOG_ERR("Failed to fixup USB descriptor");
		return NULL;
	}

	return (u8_t *) __usb_descriptor_start;
}