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