xref: /illumos-gate/usr/src/uts/common/io/usb/usba/parser.c (revision 6a604193b70017bd933cd973200b3f13803674b2)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * Descriptor parsing functions
29  */
30 #define	USBA_FRAMEWORK
31 #include <sys/usb/usba/usba_impl.h>
32 #include <sys/strsun.h>
33 
34 #define	INCREMENT_BUF(buf) \
35 		if ((buf)[0] == 0) { \
36 			break; \
37 		} else { \
38 			(buf) += (buf)[0]; \
39 		}
40 #define	isdigit(ch) ((ch >= '0') && (ch <= '9'))
41 
42 extern usba_cfg_pwr_descr_t default_cfg_power;
43 extern usba_if_pwr_descr_t default_if_power;
44 
45 size_t
46 usb_parse_data(char	*format,
47 	uchar_t 	*data,
48 	size_t		datalen,
49 	void		*structure,
50 	size_t		structlen)
51 {
52 	int	fmt;
53 	int	counter = 1;
54 	int	multiplier = 0;
55 	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 
170 size_t
171 usb_parse_CV_descr(char *format,
172 	uchar_t *data,
173 	size_t	datalen,
174 	void	*structure,
175 	size_t	structlen)
176 {
177 	return (usb_parse_data(format, data, datalen, structure,
178 	    structlen));
179 }
180 
181 
182 /*
183  *	Helper function: returns pointer to n-th descriptor of
184  *	type descr_type, unless the end of the buffer or a descriptor
185  *	of type	stop_descr_type1 or stop_descr_type2 is encountered first.
186  */
187 static uchar_t *
188 usb_nth_descr(uchar_t	*buf,
189 	size_t		buflen,
190 	int		descr_type,
191 	uint_t		n,
192 	int		stop_descr_type1,
193 	int		stop_descr_type2)
194 {
195 	uchar_t	*bufstart = buf;
196 	uchar_t *bufend = buf + buflen;
197 
198 	if (buf == NULL) {
199 
200 		return (NULL);
201 	}
202 
203 	while (buf + 2 <= bufend) {
204 		if ((buf != bufstart) && ((buf[1] == stop_descr_type1) ||
205 		    (buf[1] == stop_descr_type2))) {
206 
207 			return (NULL);
208 		}
209 
210 		if ((descr_type == USB_DESCR_TYPE_ANY) ||
211 		    (buf[1] == descr_type)) {
212 			if (n-- == 0) {
213 
214 				return (buf);
215 			}
216 		}
217 
218 		/*
219 		 * Check for a bad buffer.
220 		 * If buf[0] is 0, then this will be an infite loop
221 		 */
222 		INCREMENT_BUF(buf);
223 	}
224 
225 	return (NULL);
226 }
227 
228 
229 size_t
230 usb_parse_dev_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(DEVICE) */
231 	size_t			buflen,
232 	usb_dev_descr_t		*ret_descr,
233 	size_t			ret_buf_len)
234 {
235 	if ((buf == NULL) || (ret_descr == NULL) ||
236 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_DEV)) {
237 
238 		return (USB_PARSE_ERROR);
239 	}
240 
241 	return (usb_parse_data("ccsccccssscccc",
242 	    buf, buflen, ret_descr, ret_buf_len));
243 }
244 
245 
246 size_t
247 usb_parse_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
248 	size_t			buflen,
249 	usb_cfg_descr_t		*ret_descr,
250 	size_t			ret_buf_len)
251 {
252 	if ((buf == NULL) || (ret_descr == NULL) ||
253 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_CFG)) {
254 
255 		return (USB_PARSE_ERROR);
256 	}
257 
258 	return (usb_parse_data("ccsccccc",
259 	    buf, buflen, ret_descr, ret_buf_len));
260 }
261 
262 
263 size_t
264 usba_parse_cfg_pwr_descr(
265 	uchar_t			*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
266 	size_t			buflen,
267 	usba_cfg_pwr_descr_t	*ret_descr,
268 	size_t			ret_buf_len)
269 {
270 	uchar_t *bufend = buf + buflen;
271 
272 	if ((buf == NULL) || (ret_descr == NULL)) {
273 
274 		return (USB_PARSE_ERROR);
275 	}
276 	while (buf + 2 <= bufend) {
277 
278 		if (buf[1] == USBA_DESCR_TYPE_CFG_PWR_1_1) {
279 			return (usb_parse_data("ccsccccccccsss",
280 			    buf, buflen, ret_descr, ret_buf_len));
281 		}
282 
283 		/*
284 		 * Check for a bad buffer.
285 		 * If buf[0] is 0, then this will be an infinite loop
286 		 */
287 		INCREMENT_BUF(buf);
288 	}
289 
290 	/* return the default configuration power descriptor */
291 	bcopy(&default_cfg_power, ret_descr, USBA_CFG_PWR_DESCR_SIZE);
292 
293 	return (ret_descr->bLength);
294 
295 }
296 
297 
298 size_t
299 usb_parse_ia_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
300 	size_t			buflen,
301 	size_t			first_if,
302 	usb_ia_descr_t		*ret_descr,
303 	size_t			ret_buf_len)
304 {
305 	uchar_t *bufend = buf + buflen;
306 
307 	if ((buf == NULL) || (ret_descr == NULL)) {
308 
309 		return (USB_PARSE_ERROR);
310 	}
311 
312 	while (buf + USB_IA_DESCR_SIZE <= bufend) {
313 		if ((buf[1] == USB_DESCR_TYPE_IA) &&
314 		    (buf[2] == first_if)) {
315 
316 			return (usb_parse_data("cccccccc",
317 			    buf, _PTRDIFF(bufend, buf),
318 			    ret_descr, ret_buf_len));
319 		}
320 
321 		/*
322 		 * Check for a bad buffer.
323 		 * If buf[0] is 0, then this will be an infinite loop
324 		 */
325 		INCREMENT_BUF(buf);
326 	}
327 
328 	return (USB_PARSE_ERROR);
329 }
330 
331 
332 size_t
333 usb_parse_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
334 	size_t			buflen,
335 	uint_t			if_number,
336 	uint_t			alt_if_setting,
337 	usb_if_descr_t		*ret_descr,
338 	size_t			ret_buf_len)
339 {
340 	uchar_t *bufend = buf + buflen;
341 
342 	if ((buf == NULL) || (ret_descr == NULL)) {
343 
344 		return (USB_PARSE_ERROR);
345 	}
346 
347 	while (buf + 4 <= bufend) {
348 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
349 		    (buf[2] == if_number) &&
350 		    (buf[3] == alt_if_setting)) {
351 
352 			return (usb_parse_data("ccccccccc",
353 			    buf, _PTRDIFF(bufend, buf),
354 			    ret_descr, ret_buf_len));
355 		}
356 
357 		/*
358 		 * Check for a bad buffer.
359 		 * If buf[0] is 0, then this will be an infinite loop
360 		 */
361 		INCREMENT_BUF(buf);
362 	}
363 
364 	return (USB_PARSE_ERROR);
365 }
366 
367 size_t
368 usba_parse_if_pwr_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
369 	size_t			buflen,
370 	uint_t			if_number,
371 	uint_t			alt_if_setting,
372 	usba_if_pwr_descr_t	*ret_descr,
373 	size_t			ret_buf_len)
374 {
375 	uchar_t *bufend = buf + buflen;
376 
377 	if ((buf == NULL) || (ret_descr == NULL)) {
378 
379 		return (USB_PARSE_ERROR);
380 	}
381 
382 	while (buf + 4 <= bufend) {
383 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
384 		    (buf[2] == if_number) &&
385 		    (buf[3] == alt_if_setting)) {
386 
387 			buf += buf[0];
388 
389 			if (buf + 2 <= bufend) {
390 				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
391 
392 					return (
393 					    usb_parse_data("cccccccccsss", buf,
394 					    _PTRDIFF(bufend, buf), ret_descr,
395 					    ret_buf_len));
396 				} else {
397 					break;
398 				}
399 			} else {
400 				break;
401 			}
402 		}
403 
404 		/*
405 		 * Check for a bad buffer.
406 		 * If buf[0] is 0, then this will be an infinite loop
407 		 */
408 		INCREMENT_BUF(buf);
409 	}
410 
411 	/* return the default interface power descriptor */
412 	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
413 
414 	return (ret_descr->bLength);
415 }
416 
417 
418 /*
419  * the endpoint index is relative to the interface. index 0 is
420  * the first endpoint
421  */
422 size_t
423 usb_parse_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
424 	size_t			buflen,
425 	uint_t			if_number,
426 	uint_t			alt_if_setting,
427 	uint_t			ep_index,
428 	usb_ep_descr_t		*ret_descr,
429 	size_t			ret_buf_len)
430 {
431 	uchar_t *bufend = buf + buflen;
432 
433 	if ((buf == NULL) || (ret_descr == NULL)) {
434 
435 		return (USB_PARSE_ERROR);
436 	}
437 
438 	while ((buf + 4) <= bufend) {
439 		if (buf[1] == USB_DESCR_TYPE_IF &&
440 		    buf[2] == if_number &&
441 		    buf[3] == alt_if_setting) {
442 			if ((buf = usb_nth_descr(buf,
443 			    _PTRDIFF(bufend, buf),
444 			    USB_DESCR_TYPE_EP, ep_index,
445 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
446 
447 				break;
448 			}
449 
450 			return (usb_parse_data("ccccsc",
451 			    buf, _PTRDIFF(bufend, buf),
452 			    ret_descr, ret_buf_len));
453 		}
454 
455 		/*
456 		 * Check for a bad buffer.
457 		 * If buf[0] is 0, then this will be an infinite loop
458 		 */
459 		INCREMENT_BUF(buf);
460 	}
461 
462 	return (USB_PARSE_ERROR);
463 }
464 
465 
466 /*
467  * Returns (at ret_descr) a null-terminated string.  Null termination is
468  * guaranteed, even if the string is longer than the buffer.  Thus, a
469  * maximum of (ret_buf_len - 1) characters are returned.
470  * Stops silently on first character not in UNICODE format.
471  */
472 /*ARGSUSED*/
473 size_t
474 usba_ascii_string_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(STRING) */
475 	size_t			buflen,
476 	char			*ret_descr,
477 	size_t			ret_buf_len)
478 {
479 	int	i = 1;
480 	char	*retstart = ret_descr;
481 	uchar_t *bufend = buf + buflen;
482 
483 	if ((buf == NULL) || (ret_descr == NULL) ||
484 	    (ret_buf_len == 0) || (buflen < 2) ||
485 	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
486 
487 		return (USB_PARSE_ERROR);
488 	}
489 
490 	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
491 	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
492 		*ret_descr++ = buf[0];
493 	}
494 
495 	*ret_descr++ = 0;
496 
497 	return (_PTRDIFF(ret_descr, retstart));
498 }
499 
500 
501 size_t
502 usb_parse_CV_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
503 	size_t			buflen,
504 	char			*fmt,
505 	uint_t			descr_type,
506 	uint_t			descr_index,
507 	void			*ret_descr,
508 	size_t			ret_buf_len)
509 {
510 	uchar_t *bufend = buf + buflen;
511 
512 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL) ||
513 	    (buflen < 2) || ((buf = usb_nth_descr(buf, buflen, descr_type,
514 	    descr_index, -1, -1)) == NULL)) {
515 
516 		return (USB_PARSE_ERROR);
517 	}
518 
519 	return (usb_parse_data(fmt, buf,
520 	    _PTRDIFF(bufend, buf), ret_descr,
521 	    ret_buf_len));
522 }
523 
524 
525 size_t
526 usb_parse_CV_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
527 	size_t			buflen,
528 	char			*fmt,
529 	uint_t			if_number,
530 	uint_t			alt_if_setting,
531 	uint_t			descr_type,
532 	uint_t			descr_index,
533 	void			*ret_descr,
534 	size_t			ret_buf_len)
535 {
536 	uchar_t *bufend = buf + buflen;
537 
538 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
539 
540 		return (USB_PARSE_ERROR);
541 	}
542 
543 	while (buf + 4 <= bufend) {
544 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
545 		    (buf[2] == if_number) &&
546 		    (buf[3] == alt_if_setting)) {
547 			if ((buf = usb_nth_descr(buf,
548 			    _PTRDIFF(bufend, buf), descr_type,
549 			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
550 			    NULL) {
551 				break;
552 			}
553 
554 			return (usb_parse_data(fmt, buf,
555 			    _PTRDIFF(bufend, buf),
556 			    ret_descr, ret_buf_len));
557 		}
558 
559 		/*
560 		 * Check for a bad buffer.
561 		 * If buf[0] is 0, then this will be an infinite loop
562 		 */
563 		INCREMENT_BUF(buf);
564 	}
565 
566 	return (USB_PARSE_ERROR);
567 }
568 
569 
570 size_t
571 usb_parse_CV_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
572 	size_t			buflen,
573 	char			*fmt,
574 	uint_t			if_number,
575 	uint_t			alt_if_setting,
576 	uint_t			ep_index,
577 	uint_t			descr_type,
578 	uint_t			descr_index,
579 	void			*ret_descr,
580 	size_t			ret_buf_len)
581 {
582 	uchar_t *bufend = buf + buflen;
583 
584 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
585 
586 		return (USB_PARSE_ERROR);
587 	}
588 
589 	while (buf + 4 <= bufend) {
590 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
591 		    (buf[2] == if_number) &&
592 		    (buf[3] == alt_if_setting)) {
593 			if ((buf = usb_nth_descr(buf,
594 			    _PTRDIFF(bufend, buf),
595 			    USB_DESCR_TYPE_EP, ep_index,
596 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
597 
598 				break;
599 			}
600 
601 			if ((buf = usb_nth_descr(buf,
602 			    _PTRDIFF(bufend, buf),
603 			    descr_type, descr_index,
604 			    USB_DESCR_TYPE_EP,
605 			    USB_DESCR_TYPE_IF)) == NULL) {
606 
607 				break;
608 			}
609 
610 			return (usb_parse_data(fmt, buf,
611 			    _PTRDIFF(bufend, buf),
612 			    ret_descr, ret_buf_len));
613 		}
614 
615 		/*
616 		 * Check for a bad buffer.
617 		 * If buf[0] is 0, then this will be an infite loop
618 		 */
619 		INCREMENT_BUF(buf);
620 	}
621 
622 	return (USB_PARSE_ERROR);
623 }
624