xref: /illumos-gate/usr/src/uts/common/io/usb/usba/parser.c (revision 3db86aab)
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 2005 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 			counter--;
161 		} else {
162 			multiplier = 0;
163 			break;
164 		}
165 	}
166 
167 	return ((intptr_t)structure - (intptr_t)structstart);
168 }
169 
170 
171 size_t
172 usb_parse_CV_descr(char *format,
173 	uchar_t *data,
174 	size_t	datalen,
175 	void	*structure,
176 	size_t	structlen)
177 {
178 	return (usb_parse_data(format, data, datalen, structure,
179 		structlen));
180 }
181 
182 
183 /*
184  *	Helper function: returns pointer to n-th descriptor of
185  *	type descr_type, unless the end of the buffer or a descriptor
186  *	of type	stop_descr_type1 or stop_descr_type2 is encountered first.
187  */
188 static uchar_t *
189 usb_nth_descr(uchar_t	*buf,
190 	size_t		buflen,
191 	int		descr_type,
192 	uint_t		n,
193 	int		stop_descr_type1,
194 	int		stop_descr_type2)
195 {
196 	uchar_t	*bufstart = buf;
197 	uchar_t *bufend = buf + buflen;
198 
199 	if (buf == NULL) {
200 
201 		return (NULL);
202 	}
203 
204 	while (buf + 2 <= bufend) {
205 		if ((buf != bufstart) && ((buf[1] == stop_descr_type1) ||
206 		    (buf[1] == stop_descr_type2))) {
207 
208 			return (NULL);
209 		}
210 
211 		if ((descr_type == USB_DESCR_TYPE_ANY) ||
212 		    (buf[1] == descr_type)) {
213 			if (n-- == 0) {
214 
215 				return (buf);
216 			}
217 		}
218 
219 		/*
220 		 * Check for a bad buffer.
221 		 * If buf[0] is 0, then this will be an infite loop
222 		 */
223 		INCREMENT_BUF(buf);
224 	}
225 
226 	return (NULL);
227 }
228 
229 
230 size_t
231 usb_parse_dev_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(DEVICE) */
232 	size_t			buflen,
233 	usb_dev_descr_t		*ret_descr,
234 	size_t			ret_buf_len)
235 {
236 	if ((buf == NULL) || (ret_descr == NULL) ||
237 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_DEV)) {
238 
239 		return (USB_PARSE_ERROR);
240 	}
241 
242 	return (usb_parse_data("ccsccccssscccc",
243 		buf, buflen, ret_descr, ret_buf_len));
244 }
245 
246 
247 size_t
248 usb_parse_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
249 	size_t			buflen,
250 	usb_cfg_descr_t		*ret_descr,
251 	size_t			ret_buf_len)
252 {
253 	if ((buf == NULL) || (ret_descr == NULL) ||
254 	    (buflen < 2) || (buf[1] != USB_DESCR_TYPE_CFG)) {
255 
256 		return (USB_PARSE_ERROR);
257 	}
258 
259 	return (usb_parse_data("ccsccccc",
260 		buf, buflen, ret_descr, ret_buf_len));
261 }
262 
263 
264 size_t
265 usba_parse_cfg_pwr_descr(
266 	uchar_t			*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
267 	size_t			buflen,
268 	usba_cfg_pwr_descr_t	*ret_descr,
269 	size_t			ret_buf_len)
270 {
271 	uchar_t *bufend = buf + buflen;
272 
273 	if ((buf == NULL) || (ret_descr == NULL)) {
274 
275 		return (USB_PARSE_ERROR);
276 	}
277 	while (buf + 2 <= bufend) {
278 
279 		if (buf[1] == USBA_DESCR_TYPE_CFG_PWR_1_1) {
280 			return (usb_parse_data("ccsccccccccsss",
281 				buf, buflen, ret_descr, ret_buf_len));
282 		}
283 
284 		/*
285 		 * Check for a bad buffer.
286 		 * If buf[0] is 0, then this will be an infinite loop
287 		 */
288 		INCREMENT_BUF(buf);
289 	}
290 
291 	/* return the default configuration power descriptor */
292 	bcopy(&default_cfg_power, ret_descr, USBA_CFG_PWR_DESCR_SIZE);
293 
294 	return (ret_descr->bLength);
295 
296 }
297 
298 
299 size_t
300 usb_parse_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
301 	size_t			buflen,
302 	uint_t			if_number,
303 	uint_t			alt_if_setting,
304 	usb_if_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 + 4 <= bufend) {
315 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
316 		    (buf[2] == if_number) &&
317 		    (buf[3] == alt_if_setting)) {
318 
319 			return (usb_parse_data("ccccccccc",
320 			    buf, bufend - buf, 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 size_t
334 usba_parse_if_pwr_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
335 	size_t			buflen,
336 	uint_t			if_number,
337 	uint_t			alt_if_setting,
338 	usba_if_pwr_descr_t	*ret_descr,
339 	size_t			ret_buf_len)
340 {
341 	uchar_t *bufend = buf + buflen;
342 
343 	if ((buf == NULL) || (ret_descr == NULL)) {
344 
345 		return (USB_PARSE_ERROR);
346 	}
347 
348 	while (buf + 4 <= bufend) {
349 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
350 		    (buf[2] == if_number) &&
351 		    (buf[3] == alt_if_setting)) {
352 
353 			buf += buf[0];
354 
355 			if (buf + 2 <= bufend) {
356 				if (buf[1] == USBA_DESCR_TYPE_IF_PWR_1_1) {
357 
358 					return (
359 					    usb_parse_data("cccccccccsss",
360 						buf, bufend - buf, ret_descr,
361 						ret_buf_len));
362 				} else {
363 					break;
364 				}
365 			} else {
366 				break;
367 			}
368 		}
369 
370 		/*
371 		 * Check for a bad buffer.
372 		 * If buf[0] is 0, then this will be an infinite loop
373 		 */
374 		INCREMENT_BUF(buf);
375 	}
376 
377 	/* return the default interface power descriptor */
378 	bcopy(&default_if_power, ret_descr, USBA_IF_PWR_DESCR_SIZE);
379 
380 	return (ret_descr->bLength);
381 }
382 
383 
384 /*
385  * the endpoint index is relative to the interface. index 0 is
386  * the first endpoint
387  */
388 size_t
389 usb_parse_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
390 	size_t			buflen,
391 	uint_t			if_number,
392 	uint_t			alt_if_setting,
393 	uint_t			ep_index,
394 	usb_ep_descr_t		*ret_descr,
395 	size_t			ret_buf_len)
396 {
397 	uchar_t *bufend = buf + buflen;
398 
399 	if ((buf == NULL) || (ret_descr == NULL)) {
400 
401 		return (USB_PARSE_ERROR);
402 	}
403 
404 	while ((buf + 4) <= bufend) {
405 		if (buf[1] == USB_DESCR_TYPE_IF &&
406 			buf[2] == if_number &&
407 			buf[3] == alt_if_setting) {
408 			if ((buf = usb_nth_descr(buf, bufend - buf,
409 			    USB_DESCR_TYPE_EP, ep_index,
410 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
411 
412 				break;
413 			}
414 
415 			return (usb_parse_data("ccccsc",
416 						buf, bufend - buf,
417 						ret_descr, ret_buf_len));
418 		}
419 
420 		/*
421 		 * Check for a bad buffer.
422 		 * If buf[0] is 0, then this will be an infinite loop
423 		 */
424 		INCREMENT_BUF(buf);
425 	}
426 
427 	return (USB_PARSE_ERROR);
428 }
429 
430 
431 /*
432  * Returns (at ret_descr) a null-terminated string.  Null termination is
433  * guaranteed, even if the string is longer than the buffer.  Thus, a
434  * maximum of (ret_buf_len - 1) characters are returned.
435  * Stops silently on first character not in UNICODE format.
436  */
437 /*ARGSUSED*/
438 size_t
439 usba_ascii_string_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(STRING) */
440 	size_t			buflen,
441 	char			*ret_descr,
442 	size_t			ret_buf_len)
443 {
444 	int	i = 1;
445 	char	*retstart = ret_descr;
446 	uchar_t *bufend = buf + buflen;
447 
448 	if ((buf == NULL) || (ret_descr == NULL) ||
449 	    (ret_buf_len == 0) || (buflen < 2) ||
450 	    (buf[0] < 2) || (buf[1] != USB_DESCR_TYPE_STRING)) {
451 
452 		return (USB_PARSE_ERROR);
453 	}
454 
455 	for (buf = buf + 2; buf+1 < bufend && ret_buf_len > 1 &&
456 	    buf[0] != 0 && buf[1] == 0 && (i < ret_buf_len); buf += 2, i++) {
457 		*ret_descr++ = buf[0];
458 	}
459 
460 	*ret_descr++ = 0;
461 
462 	return (ret_descr - retstart);
463 }
464 
465 
466 size_t
467 usb_parse_CV_cfg_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
468 	size_t			buflen,
469 	char			*fmt,
470 	uint_t			descr_type,
471 	uint_t			descr_index,
472 	void			*ret_descr,
473 	size_t			ret_buf_len)
474 {
475 	uchar_t *bufend = buf + buflen;
476 
477 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL) ||
478 	    (buflen < 2) || ((buf = usb_nth_descr(buf, buflen, descr_type,
479 				descr_index, -1, -1)) == NULL)) {
480 
481 		return (USB_PARSE_ERROR);
482 	}
483 
484 	return (usb_parse_data(fmt, buf, bufend - buf, ret_descr,
485 			ret_buf_len));
486 }
487 
488 
489 size_t
490 usb_parse_CV_if_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
491 	size_t			buflen,
492 	char			*fmt,
493 	uint_t			if_number,
494 	uint_t			alt_if_setting,
495 	uint_t			descr_type,
496 	uint_t			descr_index,
497 	void			*ret_descr,
498 	size_t			ret_buf_len)
499 {
500 	uchar_t *bufend = buf + buflen;
501 
502 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
503 
504 		return (USB_PARSE_ERROR);
505 	}
506 
507 	while (buf + 4 <= bufend) {
508 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
509 		    (buf[2] == if_number) &&
510 		    (buf[3] == alt_if_setting)) {
511 			if ((buf = usb_nth_descr(buf, bufend - buf, descr_type,
512 			    descr_index, USB_DESCR_TYPE_IF, -1)) ==
513 			    NULL) {
514 				break;
515 			}
516 
517 			return (usb_parse_data(fmt,
518 				buf, bufend - buf, ret_descr, ret_buf_len));
519 		}
520 
521 		/*
522 		 * Check for a bad buffer.
523 		 * If buf[0] is 0, then this will be an infinite loop
524 		 */
525 		INCREMENT_BUF(buf);
526 	}
527 
528 	return (USB_PARSE_ERROR);
529 }
530 
531 
532 size_t
533 usb_parse_CV_ep_descr(uchar_t	*buf,	/* from GET_DESCRIPTOR(CONFIGURATION) */
534 	size_t			buflen,
535 	char			*fmt,
536 	uint_t			if_number,
537 	uint_t			alt_if_setting,
538 	uint_t			ep_index,
539 	uint_t			descr_type,
540 	uint_t			descr_index,
541 	void			*ret_descr,
542 	size_t			ret_buf_len)
543 {
544 	uchar_t *bufend = buf + buflen;
545 
546 	if ((buf == NULL) || (ret_descr == NULL) || (fmt == NULL)) {
547 
548 		return (USB_PARSE_ERROR);
549 	}
550 
551 	while (buf + 4 <= bufend) {
552 		if ((buf[1] == USB_DESCR_TYPE_IF) &&
553 		    (buf[2] == if_number) &&
554 		    (buf[3] == alt_if_setting)) {
555 			if ((buf = usb_nth_descr(buf, bufend - buf,
556 			    USB_DESCR_TYPE_EP, ep_index,
557 			    USB_DESCR_TYPE_IF, -1)) == NULL) {
558 
559 				break;
560 			}
561 
562 			if ((buf = usb_nth_descr(buf, bufend - buf,
563 			    descr_type, descr_index,
564 			    USB_DESCR_TYPE_EP,
565 			    USB_DESCR_TYPE_IF)) == NULL) {
566 
567 				break;
568 			}
569 
570 			return (usb_parse_data(fmt, buf, bufend - buf,
571 						ret_descr, ret_buf_len));
572 		}
573 
574 		/*
575 		 * Check for a bad buffer.
576 		 * If buf[0] is 0, then this will be an infite loop
577 		 */
578 		INCREMENT_BUF(buf);
579 	}
580 
581 	return (USB_PARSE_ERROR);
582 }
583