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