1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
26 * Copyright 2019, Joyent, Inc.
27 */
28
29
30/*
31 * Descriptor parsing functions
32 */
33#define	USBA_FRAMEWORK
34#include <sys/usb/usba/usba_impl.h>
35#include <sys/strsun.h>
36
37#define	INCREMENT_BUF(buf) \
38		if ((buf)[0] == 0) { \
39			break; \
40		} else { \
41			(buf) += (buf)[0]; \
42		}
43#define	isdigit(ch) ((ch >= '0') && (ch <= '9'))
44
45extern usba_cfg_pwr_descr_t default_cfg_power;
46extern usba_if_pwr_descr_t default_if_power;
47
48size_t
49usb_parse_data(char *format, const uchar_t  *data, size_t datalen,
50    void *structure, size_t structlen)
51{
52	int	fmt;
53	int	counter = 1;
54	int	multiplier = 0;
55	const uchar_t	*dataend = data + datalen;
56	char	*structstart = (char *)structure;
57	void	*structend = (void *)((intptr_t)structstart + structlen);
58
59	if ((format == NULL) || (data == NULL) || (structure == NULL)) {
60
61		return (USB_PARSE_ERROR);
62	}
63
64	while ((fmt = *format) != '\0') {
65
66		/*
67		 * Could some one pass a "format" that is greater than
68		 * the structlen? Conversely, one could pass a ret_buf_len
69		 * that is less than the "format" length.
70		 * If so, we need to protect against writing over memory.
71		 */
72		if (counter++ > structlen) {
73			break;
74		}
75
76		if (fmt == 'c') {
77			uint8_t	*cp = (uint8_t *)structure;
78
79			cp = (uint8_t *)(((uintptr_t)cp + _CHAR_ALIGNMENT - 1) &
80			    ~(_CHAR_ALIGNMENT - 1));
81			if (((data + 1) > dataend) ||
82			    ((cp + 1) > (uint8_t *)structend))
83				break;
84
85			*cp++ = *data++;
86			structure = (void *)cp;
87			if (multiplier) {
88				multiplier--;
89			}
90			if (multiplier == 0) {
91				format++;
92			}
93		} else if (fmt == 's') {
94			uint16_t	*sp = (uint16_t *)structure;
95
96			sp = (uint16_t *)
97			    (((uintptr_t)sp + _SHORT_ALIGNMENT - 1) &
98			    ~(_SHORT_ALIGNMENT - 1));
99			if (((data + 2) > dataend) ||
100			    ((sp + 1) > (uint16_t *)structend))
101				break;
102
103			*sp++ = (data[1] << 8) + data[0];
104			data += 2;
105			structure = (void *)sp;
106			if (multiplier) {
107				multiplier--;
108			}
109			if (multiplier == 0) {
110				format++;
111			}
112		} else if (fmt == 'l') {
113			uint32_t	*lp = (uint32_t *)structure;
114
115			lp = (uint32_t *)
116			    (((uintptr_t)lp + _INT_ALIGNMENT - 1) &
117			    ~(_INT_ALIGNMENT - 1));
118			if (((data + 4) > dataend) ||
119			    ((lp + 1) > (uint32_t *)structend))
120				break;
121
122			*lp++ = (((((
123			    (uint32_t)data[3] << 8) | data[2]) << 8) |
124			    data[1]) << 8) | data[0];
125			data += 4;
126			structure = (void *)lp;
127			if (multiplier) {
128				multiplier--;
129			}
130			if (multiplier == 0) {
131				format++;
132			}
133		} else if (fmt == 'L') {
134			uint64_t	*llp = (uint64_t *)structure;
135
136			llp = (uint64_t *)
137			    (((uintptr_t)llp + _LONG_LONG_ALIGNMENT - 1) &
138			    ~(_LONG_LONG_ALIGNMENT - 1));
139			if (((data + 8) > dataend) ||
140			    ((llp + 1) >= (uint64_t *)structend))
141				break;
142
143			*llp++ = (((((((((((((data[7] << 8) |
144			    data[6]) << 8) | data[5]) << 8) |
145			    data[4]) << 8) | data[3]) << 8) |
146			    data[2]) << 8) | data[1]) << 8) |
147			    data[0];
148			data += 8;
149			structure = (void *)llp;
150			if (multiplier) {
151				multiplier--;
152			}
153			if (multiplier == 0) {
154				format++;
155			}
156		} else if (isdigit(fmt)) {
157			multiplier = (multiplier * 10) + (fmt - '0');
158			format++;
159			counter--;
160		} else {
161			multiplier = 0;
162			break;
163		}
164	}
165
166	return ((intptr_t)structure - (intptr_t)structstart);
167}
168
169
170size_t
171usb_parse_CV_descr(char *format, const uchar_t *data, size_t datalen,
172    void *structure, size_t structlen)
173{
174	return (usb_parse_data(format, data, datalen, structure,
175	    structlen));
176}
177
178
179/*
180 *	Helper function: returns pointer to n-th descriptor of
181 *	type descr_type, unless the end of the buffer or a descriptor
182 *	of type	stop_descr_type1 or stop_descr_type2 is encountered first.
183 */
184static const uchar_t *
185usb_nth_descr(const uchar_t *buf, size_t buflen, int descr_type, uint_t n,
186    int stop_descr_type1, int stop_descr_type2)
187{
188	const uchar_t	*bufstart = buf;
189	const uchar_t *bufend = buf + buflen;
190
191	if (buf == NULL) {
192
193		return (NULL);
194	}
195
196	while (buf + 2 <= bufend) {
197		if ((buf != bufstart) && ((buf[1] == stop_descr_type1) ||
198		    (buf[1] == stop_descr_type2))) {
199
200			return (NULL);
201		}
202
203		if ((descr_type == USB_DESCR_TYPE_ANY) ||
204		    (buf[1] == descr_type)) {
205			if (n-- == 0) {
206
207				return (buf);
208			}
209		}
210
211		/*
212		 * Check for a bad buffer.
213		 * If buf[0] is 0, then this will be an infite loop
214		 */
215		INCREMENT_BUF(buf);
216	}
217
218	return (NULL);
219}
220
221
222size_t
223usb_parse_dev_descr(const uchar_t *buf, size_t buflen,
224    usb_dev_descr_t *ret_descr, size_t ret_buf_len)
225{
226	if ((buf == NULL) || (ret_descr == NULL) ||
227	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_DEV)) {
228
229		return (USB_PARSE_ERROR);
230	}
231
232	return (usb_parse_data("ccsccccssscccc",
233	    buf, buflen, ret_descr, ret_buf_len));
234}
235
236
237size_t
238usb_parse_cfg_descr(const uchar_t *buf, size_t buflen,
239    usb_cfg_descr_t *ret_descr, size_t ret_buf_len)
240{
241	if ((buf == NULL) || (ret_descr == NULL) ||
242	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_CFG)) {
243
244		return (USB_PARSE_ERROR);
245	}
246
247	return (usb_parse_data("ccsccccc",
248	    buf, buflen, ret_descr, ret_buf_len));
249}
250
251
252size_t
253usba_parse_cfg_pwr_descr(const uchar_t *buf, size_t buflen,
254    usba_cfg_pwr_descr_t *ret_descr, size_t ret_buf_len)
255{
256	const uchar_t *bufend = buf + buflen;
257
258	if ((buf == NULL) || (ret_descr == NULL)) {
259
260		return (USB_PARSE_ERROR);
261	}
262	while (buf + 2 <= bufend) {
263
264		if (buf[1] == USBA_DESCR_TYPE_CFG_PWR_1_1) {
265			return (usb_parse_data("ccsccccccccsss",
266			    buf, buflen, ret_descr, ret_buf_len));
267		}
268
269		/*
270		 * Check for a bad buffer.
271		 * If buf[0] is 0, then this will be an infinite loop
272		 */
273		INCREMENT_BUF(buf);
274	}
275
276	/* return the default configuration power descriptor */
277	bcopy(&default_cfg_power, ret_descr, USBA_CFG_PWR_DESCR_SIZE);
278
279	return (ret_descr->bLength);
280
281}
282
283
284size_t
285usb_parse_ia_descr(const uchar_t *buf, size_t buflen, size_t first_if,
286    usb_ia_descr_t *ret_descr, size_t ret_buf_len)
287{
288	const uchar_t *bufend = buf + buflen;
289
290	if ((buf == NULL) || (ret_descr == NULL)) {
291
292		return (USB_PARSE_ERROR);
293	}
294
295	while (buf + USB_IA_DESCR_SIZE <= bufend) {
296		if ((buf[1] == USB_DESCR_TYPE_IA) &&
297		    (buf[2] == first_if)) {
298
299			return (usb_parse_data("cccccccc",
300			    buf, _PTRDIFF(bufend, buf),
301			    ret_descr, ret_buf_len));
302		}
303
304		/*
305		 * Check for a bad buffer.
306		 * If buf[0] is 0, then this will be an infinite loop
307		 */
308		INCREMENT_BUF(buf);
309	}
310
311	return (USB_PARSE_ERROR);
312}
313
314
315size_t
316usb_parse_if_descr(const uchar_t *buf, size_t buflen, uint_t if_number,
317    uint_t alt_if_setting, usb_if_descr_t *ret_descr, size_t ret_buf_len)
318{
319	const uchar_t *bufend = buf + buflen;
320
321	if ((buf == NULL) || (ret_descr == NULL)) {
322
323		return (USB_PARSE_ERROR);
324	}
325
326	while (buf + 4 <= bufend) {
327		if ((buf[1] == USB_DESCR_TYPE_IF) &&
328		    (buf[2] == if_number) &&
329		    (buf[3] == alt_if_setting)) {
330
331			return (usb_parse_data("ccccccccc",
332			    buf, _PTRDIFF(bufend, buf),
333			    ret_descr, ret_buf_len));
334		}
335
336		/*
337		 * Check for a bad buffer.
338		 * If buf[0] is 0, then this will be an infinite loop
339		 */
340		INCREMENT_BUF(buf);
341	}
342
343	return (USB_PARSE_ERROR);
344}
345
346size_t
347usba_parse_if_pwr_descr(const uchar_t *buf, size_t buflen, uint_t if_number,
348    uint_t alt_if_setting, usba_if_pwr_descr_t *ret_descr, size_t ret_buf_len)
349{
350	const uchar_t *bufend = buf + buflen;
351
352	if ((buf == NULL) || (ret_descr == NULL)) {
353
354		return (USB_PARSE_ERROR);
355	}
356
357	while (buf + 4 <= bufend) {
358		if ((buf[1] == USB_DESCR_TYPE_IF) &&
359		    (buf[2] == if_number) &&
360		    (buf[3] == alt_if_setting)) {
361
362			buf += buf[0];
363
364			if (buf + 2 <= bufend) {
365				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
366
367					return (
368					    usb_parse_data("cccccccccsss", buf,
369					    _PTRDIFF(bufend, buf), ret_descr,
370					    ret_buf_len));
371				} else {
372					break;
373				}
374			} else {
375				break;
376			}
377		}
378
379		/*
380		 * Check for a bad buffer.
381		 * If buf[0] is 0, then this will be an infinite loop
382		 */
383		INCREMENT_BUF(buf);
384	}
385
386	/* return the default interface power descriptor */
387	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
388
389	return (ret_descr->bLength);
390}
391
392
393/*
394 * the endpoint index is relative to the interface. index 0 is
395 * the first endpoint
396 */
397size_t
398usb_parse_ep_descr(const uchar_t *buf, size_t buflen, uint_t if_number,
399    uint_t alt_if_setting, uint_t ep_index, usb_ep_descr_t *ret_descr,
400    size_t ret_buf_len)
401{
402	const uchar_t *bufend = buf + buflen;
403
404	if ((buf == NULL) || (ret_descr == NULL)) {
405
406		return (USB_PARSE_ERROR);
407	}
408
409	while ((buf + 4) <= bufend) {
410		if (buf[1] == USB_DESCR_TYPE_IF &&
411		    buf[2] == if_number &&
412		    buf[3] == alt_if_setting) {
413			if ((buf = usb_nth_descr(buf,
414			    _PTRDIFF(bufend, buf),
415			    USB_DESCR_TYPE_EP, ep_index,
416			    USB_DESCR_TYPE_IF, -1)) == NULL) {
417
418				break;
419			}
420
421			return (usb_parse_data("ccccsc",
422			    buf, _PTRDIFF(bufend, buf),
423			    ret_descr, ret_buf_len));
424		}
425
426		/*
427		 * Check for a bad buffer.
428		 * If buf[0] is 0, then this will be an infinite loop
429		 */
430		INCREMENT_BUF(buf);
431	}
432
433	return (USB_PARSE_ERROR);
434}
435
436
437/*
438 * Returns (at ret_descr) a null-terminated string.  Null termination is
439 * guaranteed, even if the string is longer than the buffer.  Thus, a
440 * maximum of (ret_buf_len - 1) characters are returned.
441 * Stops silently on first character not in UNICODE format.
442 */
443/*ARGSUSED*/
444size_t
445usba_ascii_string_descr(const uchar_t *buf, size_t buflen, char *ret_descr,
446    size_t ret_buf_len)
447{
448	int		i = 1;
449	char		*retstart = ret_descr;
450	const uchar_t	*bufend = buf + buflen;
451
452	if ((buf == NULL) || (ret_descr == NULL) ||
453	    (ret_buf_len == 0) || (buflen < 2) ||
454	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
455
456		return (USB_PARSE_ERROR);
457	}
458
459	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
460	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
461		*ret_descr++ = buf[0];
462	}
463
464	*ret_descr++ = 0;
465
466	return (_PTRDIFF(ret_descr, retstart));
467}
468
469
470size_t
471usb_parse_CV_cfg_descr(const uchar_t *buf, size_t buflen, char *fmt,
472    uint_t descr_type, uint_t descr_index, void *ret_descr, size_t ret_buf_len)
473{
474	const uchar_t *bufend = buf + buflen;
475
476	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL) ||
477	    (buflen < 2) || ((buf = usb_nth_descr(buf, buflen, descr_type,
478	    descr_index, -1, -1)) == NULL)) {
479
480		return (USB_PARSE_ERROR);
481	}
482
483	return (usb_parse_data(fmt, buf,
484	    _PTRDIFF(bufend, buf), ret_descr,
485	    ret_buf_len));
486}
487
488
489size_t
490usb_parse_CV_if_descr(const uchar_t *buf, size_t buflen, char *fmt,
491    uint_t if_number, uint_t alt_if_setting, uint_t descr_type,
492    uint_t descr_index, void *ret_descr, size_t ret_buf_len)
493{
494	const uchar_t *bufend = buf + buflen;
495
496	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
497
498		return (USB_PARSE_ERROR);
499	}
500
501	while (buf + 4 <= bufend) {
502		if ((buf[1] == USB_DESCR_TYPE_IF) &&
503		    (buf[2] == if_number) &&
504		    (buf[3] == alt_if_setting)) {
505			if ((buf = usb_nth_descr(buf,
506			    _PTRDIFF(bufend, buf), descr_type,
507			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
508			    NULL) {
509				break;
510			}
511
512			return (usb_parse_data(fmt, buf,
513			    _PTRDIFF(bufend, buf),
514			    ret_descr, ret_buf_len));
515		}
516
517		/*
518		 * Check for a bad buffer.
519		 * If buf[0] is 0, then this will be an infinite loop
520		 */
521		INCREMENT_BUF(buf);
522	}
523
524	return (USB_PARSE_ERROR);
525}
526
527
528size_t
529usb_parse_CV_ep_descr(const uchar_t *buf, size_t buflen, char *fmt,
530    uint_t if_number, uint_t alt_if_setting, uint_t ep_index, uint_t descr_type,
531    uint_t descr_index, void *ret_descr, size_t ret_buf_len)
532{
533	const uchar_t *bufend = buf + buflen;
534
535	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
536
537		return (USB_PARSE_ERROR);
538	}
539
540	while (buf + 4 <= bufend) {
541		if ((buf[1] == USB_DESCR_TYPE_IF) &&
542		    (buf[2] == if_number) &&
543		    (buf[3] == alt_if_setting)) {
544			if ((buf = usb_nth_descr(buf,
545			    _PTRDIFF(bufend, buf),
546			    USB_DESCR_TYPE_EP, ep_index,
547			    USB_DESCR_TYPE_IF, -1)) == NULL) {
548
549				break;
550			}
551
552			if ((buf = usb_nth_descr(buf,
553			    _PTRDIFF(bufend, buf),
554			    descr_type, descr_index,
555			    USB_DESCR_TYPE_EP,
556			    USB_DESCR_TYPE_IF)) == NULL) {
557
558				break;
559			}
560
561			return (usb_parse_data(fmt, buf,
562			    _PTRDIFF(bufend, buf),
563			    ret_descr, ret_buf_len));
564		}
565
566		/*
567		 * Check for a bad buffer.
568		 * If buf[0] is 0, then this will be an infite loop
569		 */
570		INCREMENT_BUF(buf);
571	}
572
573	return (USB_PARSE_ERROR);
574}
575