xref: /illumos-gate/usr/src/uts/common/io/usb/usba/parser.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Descriptor parsing functions
31  */
32 #define	USBA_FRAMEWORK
33 #include <sys/usb/usba/usba_impl.h>
34 
35 #define	INCREMENT_BUF(buf) \
36 		if ((buf)[0] == 0) { \
37 			break; \
38 		} else { \
39 			(buf) += (buf)[0]; \
40 		}
41 #define	isdigit(ch) ((ch >= '0') && (ch <= '9'))
42 
43 extern usba_cfg_pwr_descr_t default_cfg_power;
44 extern usba_if_pwr_descr_t default_if_power;
45 
46 size_t
47 usb_parse_data(char	*format,
48 	uchar_t 	*data,
49 	size_t		datalen,
50 	void		*structure,
51 	size_t		structlen)
52 {
53 	int	fmt;
54 	int	counter = 1;
55 	int	multiplier = 0;
56 	uchar_t	*dataend = data + datalen;
57 	char	*structstart = (char *)structure;
58 	void	*structend = (void *)((intptr_t)structstart + structlen);
59 
60 	if ((format == NULL) || (data == NULL) || (structure == NULL)) {
61 
62 		return (USB_PARSE_ERROR);
63 	}
64 
65 	while ((fmt = *format) != '\0') {
66 
67 		/*
68 		 * Could some one pass a "format" that is greater than
69 		 * the structlen? Conversely, one could pass a ret_buf_len
70 		 * that is less than the "format" length.
71 		 * If so, we need to protect against writing over memory.
72 		 */
73 		if (counter++ > structlen) {
74 			break;
75 		}
76 
77 		if (fmt == 'c') {
78 			uint8_t	*cp = (uint8_t *)structure;
79 
80 			cp = (uint8_t *)(((uintptr_t)cp + _CHAR_ALIGNMENT - 1) &
81 							~(_CHAR_ALIGNMENT - 1));
82 			if (((data + 1) > dataend) ||
83 			    ((cp + 1) > (uint8_t *)structend))
84 				break;
85 
86 			*cp++ = *data++;
87 			structure = (void *)cp;
88 			if (multiplier) {
89 				multiplier--;
90 			}
91 			if (multiplier == 0) {
92 				format++;
93 			}
94 		} else if (fmt == 's') {
95 			uint16_t	*sp = (uint16_t *)structure;
96 
97 			sp = (uint16_t *)
98 				(((uintptr_t)sp + _SHORT_ALIGNMENT - 1) &
99 						~(_SHORT_ALIGNMENT - 1));
100 			if (((data + 2) > dataend) ||
101 			    ((sp + 1) > (uint16_t *)structend))
102 				break;
103 
104 			*sp++ = (data[1] << 8) + data[0];
105 			data += 2;
106 			structure = (void *)sp;
107 			if (multiplier) {
108 				multiplier--;
109 			}
110 			if (multiplier == 0) {
111 				format++;
112 			}
113 		} else if (fmt == 'l') {
114 			uint32_t	*lp = (uint32_t *)structure;
115 
116 			lp = (uint32_t *)
117 				(((uintptr_t)lp + _INT_ALIGNMENT - 1) &
118 							~(_INT_ALIGNMENT - 1));
119 			if (((data + 4) > dataend) ||
120 			    ((lp + 1) > (uint32_t *)structend))
121 				break;
122 
123 			*lp++ = (((((
124 				(uint32_t)data[3] << 8) | data[2]) << 8) |
125 						data[1]) << 8) | data[0];
126 			data += 4;
127 			structure = (void *)lp;
128 			if (multiplier) {
129 				multiplier--;
130 			}
131 			if (multiplier == 0) {
132 				format++;
133 			}
134 		} else if (fmt == 'L') {
135 			uint64_t	*llp = (uint64_t *)structure;
136 
137 			llp = (uint64_t *)
138 				(((uintptr_t)llp + _LONG_LONG_ALIGNMENT - 1) &
139 						~(_LONG_LONG_ALIGNMENT - 1));
140 			if (((data + 8) > dataend) ||
141 			    ((llp + 1) >= (uint64_t *)structend))
142 				break;
143 
144 			*llp++ = (((((((((((((data[7] << 8) |
145 					data[6]) << 8) | data[5]) << 8) |
146 					data[4]) << 8) | data[3]) << 8) |
147 					data[2]) << 8) | data[1]) << 8) |
148 					data[0];
149 			data += 8;
150 			structure = (void *)llp;
151 			if (multiplier) {
152 				multiplier--;
153 			}
154 			if (multiplier == 0) {
155 				format++;
156 			}
157 		} else if (isdigit(fmt)) {
158 			multiplier = (multiplier * 10) + (fmt - '0');
159 			format++;
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_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
300 	size_t			buflen,
301 	uint_t			if_number,
302 	uint_t			alt_if_setting,
303 	usb_if_descr_t		*ret_descr,
304 	size_t			ret_buf_len)
305 {
306 	uchar_t *bufend = buf + buflen;
307 
308 	if ((buf == NULL) || (ret_descr == NULL)) {
309 
310 		return (USB_PARSE_ERROR);
311 	}
312 
313 	while (buf + 4 <= bufend) {
314 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
315 		    (buf[2] == if_number) &&
316 		    (buf[3] == alt_if_setting)) {
317 
318 			return (usb_parse_data("ccccccccc",
319 			    buf, bufend - buf, ret_descr, ret_buf_len));
320 		}
321 
322 		/*
323 		 * Check for a bad buffer.
324 		 * If buf[0] is 0, then this will be an infinite loop
325 		 */
326 		INCREMENT_BUF(buf);
327 	}
328 
329 	return (USB_PARSE_ERROR);
330 }
331 
332 size_t
333 usba_parse_if_pwr_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
334 	size_t			buflen,
335 	uint_t			if_number,
336 	uint_t			alt_if_setting,
337 	usba_if_pwr_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 			buf += buf[0];
353 
354 			if (buf + 2 <= bufend) {
355 				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
356 
357 					return (
358 					    usb_parse_data("cccccccccsss",
359 						buf, bufend - buf, ret_descr,
360 						ret_buf_len));
361 				} else {
362 					break;
363 				}
364 			} else {
365 				break;
366 			}
367 		}
368 
369 		/*
370 		 * Check for a bad buffer.
371 		 * If buf[0] is 0, then this will be an infinite loop
372 		 */
373 		INCREMENT_BUF(buf);
374 	}
375 
376 	/* return the default interface power descriptor */
377 	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
378 
379 	return (ret_descr->bLength);
380 }
381 
382 
383 /*
384  * the endpoint index is relative to the interface. index 0 is
385  * the first endpoint
386  */
387 size_t
388 usb_parse_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
389 	size_t			buflen,
390 	uint_t			if_number,
391 	uint_t			alt_if_setting,
392 	uint_t			ep_index,
393 	usb_ep_descr_t		*ret_descr,
394 	size_t			ret_buf_len)
395 {
396 	uchar_t *bufend = buf + buflen;
397 
398 	if ((buf == NULL) || (ret_descr == NULL)) {
399 
400 		return (USB_PARSE_ERROR);
401 	}
402 
403 	while ((buf + 4) <= bufend) {
404 		if (buf[1] == USB_DESCR_TYPE_IF &&
405 			buf[2] == if_number &&
406 			buf[3] == alt_if_setting) {
407 			if ((buf = usb_nth_descr(buf, bufend - buf,
408 			    USB_DESCR_TYPE_EP, ep_index,
409 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
410 
411 				break;
412 			}
413 
414 			return (usb_parse_data("ccccsc",
415 						buf, bufend - buf,
416 						ret_descr, ret_buf_len));
417 		}
418 
419 		/*
420 		 * Check for a bad buffer.
421 		 * If buf[0] is 0, then this will be an infinite loop
422 		 */
423 		INCREMENT_BUF(buf);
424 	}
425 
426 	return (USB_PARSE_ERROR);
427 }
428 
429 
430 /*
431  * Returns (at ret_descr) a null-terminated string.  Null termination is
432  * guaranteed, even if the string is longer than the buffer.  Thus, a
433  * maximum of (ret_buf_len - 1) characters are returned.
434  * Stops silently on first character not in UNICODE format.
435  */
436 /*ARGSUSED*/
437 size_t
438 usba_ascii_string_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(STRING) */
439 	size_t			buflen,
440 	char			*ret_descr,
441 	size_t			ret_buf_len)
442 {
443 	int	i = 1;
444 	char	*retstart = ret_descr;
445 	uchar_t *bufend = buf + buflen;
446 
447 	if ((buf == NULL) || (ret_descr == NULL) ||
448 	    (ret_buf_len == 0) || (buflen < 2) ||
449 	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
450 
451 		return (USB_PARSE_ERROR);
452 	}
453 
454 	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
455 	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
456 		*ret_descr++ = buf[0];
457 	}
458 
459 	*ret_descr++ = 0;
460 
461 	return (ret_descr - retstart);
462 }
463 
464 
465 size_t
466 usb_parse_CV_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
467 	size_t			buflen,
468 	char			*fmt,
469 	uint_t			descr_type,
470 	uint_t			descr_index,
471 	void			*ret_descr,
472 	size_t			ret_buf_len)
473 {
474 	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, bufend - buf, ret_descr,
484 			ret_buf_len));
485 }
486 
487 
488 size_t
489 usb_parse_CV_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
490 	size_t			buflen,
491 	char			*fmt,
492 	uint_t			if_number,
493 	uint_t			alt_if_setting,
494 	uint_t			descr_type,
495 	uint_t			descr_index,
496 	void			*ret_descr,
497 	size_t			ret_buf_len)
498 {
499 	uchar_t *bufend = buf + buflen;
500 
501 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
502 
503 		return (USB_PARSE_ERROR);
504 	}
505 
506 	while (buf + 4 <= bufend) {
507 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
508 		    (buf[2] == if_number) &&
509 		    (buf[3] == alt_if_setting)) {
510 			if ((buf = usb_nth_descr(buf, bufend - buf, descr_type,
511 			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
512 			    NULL) {
513 				break;
514 			}
515 
516 			return (usb_parse_data(fmt,
517 				buf, bufend - buf, ret_descr, ret_buf_len));
518 		}
519 
520 		/*
521 		 * Check for a bad buffer.
522 		 * If buf[0] is 0, then this will be an infinite loop
523 		 */
524 		INCREMENT_BUF(buf);
525 	}
526 
527 	return (USB_PARSE_ERROR);
528 }
529 
530 
531 size_t
532 usb_parse_CV_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
533 	size_t			buflen,
534 	char			*fmt,
535 	uint_t			if_number,
536 	uint_t			alt_if_setting,
537 	uint_t			ep_index,
538 	uint_t			descr_type,
539 	uint_t			descr_index,
540 	void			*ret_descr,
541 	size_t			ret_buf_len)
542 {
543 	uchar_t *bufend = buf + buflen;
544 
545 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
546 
547 		return (USB_PARSE_ERROR);
548 	}
549 
550 	while (buf + 4 <= bufend) {
551 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
552 		    (buf[2] == if_number) &&
553 		    (buf[3] == alt_if_setting)) {
554 			if ((buf = usb_nth_descr(buf, bufend - buf,
555 			    USB_DESCR_TYPE_EP, ep_index,
556 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
557 
558 				break;
559 			}
560 
561 			if ((buf = usb_nth_descr(buf, bufend - buf,
562 			    descr_type, descr_index,
563 			    USB_DESCR_TYPE_EP,
564 			    USB_DESCR_TYPE_IF)) == NULL) {
565 
566 				break;
567 			}
568 
569 			return (usb_parse_data(fmt, buf, bufend - buf,
570 						ret_descr, ret_buf_len));
571 		}
572 
573 		/*
574 		 * Check for a bad buffer.
575 		 * If buf[0] is 0, then this will be an infite loop
576 		 */
577 		INCREMENT_BUF(buf);
578 	}
579 
580 	return (USB_PARSE_ERROR);
581 }
582