xref: /illumos-gate/usr/src/uts/common/io/usb/usba/parser.c (revision ba2be53024c0b999e74ba9adcd7d80fec5df8c57)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Descriptor parsing functions
30  */
31 #define	USBA_FRAMEWORK
32 #include <sys/usb/usba/usba_impl.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, bufend - buf, ret_descr, ret_buf_len));
318 		}
319 
320 		/*
321 		 * Check for a bad buffer.
322 		 * If buf[0] is 0, then this will be an infinite loop
323 		 */
324 		INCREMENT_BUF(buf);
325 	}
326 
327 	return (USB_PARSE_ERROR);
328 }
329 
330 
331 size_t
332 usb_parse_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
333 	size_t			buflen,
334 	uint_t			if_number,
335 	uint_t			alt_if_setting,
336 	usb_if_descr_t		*ret_descr,
337 	size_t			ret_buf_len)
338 {
339 	uchar_t *bufend = buf + buflen;
340 
341 	if ((buf == NULL) || (ret_descr == NULL)) {
342 
343 		return (USB_PARSE_ERROR);
344 	}
345 
346 	while (buf + 4 <= bufend) {
347 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
348 		    (buf[2] == if_number) &&
349 		    (buf[3] == alt_if_setting)) {
350 
351 			return (usb_parse_data("ccccccccc",
352 			    buf, bufend - buf, ret_descr, ret_buf_len));
353 		}
354 
355 		/*
356 		 * Check for a bad buffer.
357 		 * If buf[0] is 0, then this will be an infinite loop
358 		 */
359 		INCREMENT_BUF(buf);
360 	}
361 
362 	return (USB_PARSE_ERROR);
363 }
364 
365 size_t
366 usba_parse_if_pwr_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
367 	size_t			buflen,
368 	uint_t			if_number,
369 	uint_t			alt_if_setting,
370 	usba_if_pwr_descr_t	*ret_descr,
371 	size_t			ret_buf_len)
372 {
373 	uchar_t *bufend = buf + buflen;
374 
375 	if ((buf == NULL) || (ret_descr == NULL)) {
376 
377 		return (USB_PARSE_ERROR);
378 	}
379 
380 	while (buf + 4 <= bufend) {
381 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
382 		    (buf[2] == if_number) &&
383 		    (buf[3] == alt_if_setting)) {
384 
385 			buf += buf[0];
386 
387 			if (buf + 2 <= bufend) {
388 				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
389 
390 					return (
391 					    usb_parse_data("cccccccccsss",
392 						buf, bufend - buf, ret_descr,
393 						ret_buf_len));
394 				} else {
395 					break;
396 				}
397 			} else {
398 				break;
399 			}
400 		}
401 
402 		/*
403 		 * Check for a bad buffer.
404 		 * If buf[0] is 0, then this will be an infinite loop
405 		 */
406 		INCREMENT_BUF(buf);
407 	}
408 
409 	/* return the default interface power descriptor */
410 	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
411 
412 	return (ret_descr->bLength);
413 }
414 
415 
416 /*
417  * the endpoint index is relative to the interface. index 0 is
418  * the first endpoint
419  */
420 size_t
421 usb_parse_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
422 	size_t			buflen,
423 	uint_t			if_number,
424 	uint_t			alt_if_setting,
425 	uint_t			ep_index,
426 	usb_ep_descr_t		*ret_descr,
427 	size_t			ret_buf_len)
428 {
429 	uchar_t *bufend = buf + buflen;
430 
431 	if ((buf == NULL) || (ret_descr == NULL)) {
432 
433 		return (USB_PARSE_ERROR);
434 	}
435 
436 	while ((buf + 4) <= bufend) {
437 		if (buf[1] == USB_DESCR_TYPE_IF &&
438 			buf[2] == if_number &&
439 			buf[3] == alt_if_setting) {
440 			if ((buf = usb_nth_descr(buf, bufend - buf,
441 			    USB_DESCR_TYPE_EP, ep_index,
442 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
443 
444 				break;
445 			}
446 
447 			return (usb_parse_data("ccccsc",
448 						buf, bufend - buf,
449 						ret_descr, ret_buf_len));
450 		}
451 
452 		/*
453 		 * Check for a bad buffer.
454 		 * If buf[0] is 0, then this will be an infinite loop
455 		 */
456 		INCREMENT_BUF(buf);
457 	}
458 
459 	return (USB_PARSE_ERROR);
460 }
461 
462 
463 /*
464  * Returns (at ret_descr) a null-terminated string.  Null termination is
465  * guaranteed, even if the string is longer than the buffer.  Thus, a
466  * maximum of (ret_buf_len - 1) characters are returned.
467  * Stops silently on first character not in UNICODE format.
468  */
469 /*ARGSUSED*/
470 size_t
471 usba_ascii_string_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(STRING) */
472 	size_t			buflen,
473 	char			*ret_descr,
474 	size_t			ret_buf_len)
475 {
476 	int	i = 1;
477 	char	*retstart = ret_descr;
478 	uchar_t *bufend = buf + buflen;
479 
480 	if ((buf == NULL) || (ret_descr == NULL) ||
481 	    (ret_buf_len == 0) || (buflen < 2) ||
482 	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
483 
484 		return (USB_PARSE_ERROR);
485 	}
486 
487 	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
488 	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
489 		*ret_descr++ = buf[0];
490 	}
491 
492 	*ret_descr++ = 0;
493 
494 	return (ret_descr - retstart);
495 }
496 
497 
498 size_t
499 usb_parse_CV_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
500 	size_t			buflen,
501 	char			*fmt,
502 	uint_t			descr_type,
503 	uint_t			descr_index,
504 	void			*ret_descr,
505 	size_t			ret_buf_len)
506 {
507 	uchar_t *bufend = buf + buflen;
508 
509 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL) ||
510 	    (buflen < 2) || ((buf = usb_nth_descr(buf, buflen, descr_type,
511 				descr_index, -1, -1)) == NULL)) {
512 
513 		return (USB_PARSE_ERROR);
514 	}
515 
516 	return (usb_parse_data(fmt, buf, bufend - buf, ret_descr,
517 			ret_buf_len));
518 }
519 
520 
521 size_t
522 usb_parse_CV_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
523 	size_t			buflen,
524 	char			*fmt,
525 	uint_t			if_number,
526 	uint_t			alt_if_setting,
527 	uint_t			descr_type,
528 	uint_t			descr_index,
529 	void			*ret_descr,
530 	size_t			ret_buf_len)
531 {
532 	uchar_t *bufend = buf + buflen;
533 
534 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
535 
536 		return (USB_PARSE_ERROR);
537 	}
538 
539 	while (buf + 4 <= bufend) {
540 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
541 		    (buf[2] == if_number) &&
542 		    (buf[3] == alt_if_setting)) {
543 			if ((buf = usb_nth_descr(buf, bufend - buf, descr_type,
544 			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
545 			    NULL) {
546 				break;
547 			}
548 
549 			return (usb_parse_data(fmt,
550 				buf, bufend - buf, ret_descr, ret_buf_len));
551 		}
552 
553 		/*
554 		 * Check for a bad buffer.
555 		 * If buf[0] is 0, then this will be an infinite loop
556 		 */
557 		INCREMENT_BUF(buf);
558 	}
559 
560 	return (USB_PARSE_ERROR);
561 }
562 
563 
564 size_t
565 usb_parse_CV_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
566 	size_t			buflen,
567 	char			*fmt,
568 	uint_t			if_number,
569 	uint_t			alt_if_setting,
570 	uint_t			ep_index,
571 	uint_t			descr_type,
572 	uint_t			descr_index,
573 	void			*ret_descr,
574 	size_t			ret_buf_len)
575 {
576 	uchar_t *bufend = buf + buflen;
577 
578 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
579 
580 		return (USB_PARSE_ERROR);
581 	}
582 
583 	while (buf + 4 <= bufend) {
584 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
585 		    (buf[2] == if_number) &&
586 		    (buf[3] == alt_if_setting)) {
587 			if ((buf = usb_nth_descr(buf, bufend - buf,
588 			    USB_DESCR_TYPE_EP, ep_index,
589 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
590 
591 				break;
592 			}
593 
594 			if ((buf = usb_nth_descr(buf, bufend - buf,
595 			    descr_type, descr_index,
596 			    USB_DESCR_TYPE_EP,
597 			    USB_DESCR_TYPE_IF)) == NULL) {
598 
599 				break;
600 			}
601 
602 			return (usb_parse_data(fmt, buf, bufend - buf,
603 						ret_descr, ret_buf_len));
604 		}
605 
606 		/*
607 		 * Check for a bad buffer.
608 		 * If buf[0] is 0, then this will be an infite loop
609 		 */
610 		INCREMENT_BUF(buf);
611 	}
612 
613 	return (USB_PARSE_ERROR);
614 }
615