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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
32 #define MAXLINESIZE 512
35 #define isunary(ch) ((ch) == '~' || (ch) == '-')
36 #define iswhite(ch) ((ch) == ' ' || (ch) == '\t')
37 #define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f')
38 #define isalphanum(ch) (isalpha(ch) || isdigit(ch))
39 #define isnamechar(ch) (isalphanum(ch) || (ch) == '_' || (ch) == '-')
41 #define MAX(a, b) ((a) < (b) ? (b) : (a))
42 #define GETC(a, cntr) a[cntr++]
43 #define UNGETC(cntr) cntr--
46 typedef struct usb_configrec
{
48 int idVendor
, idProduct
, cfgndx
;
55 USB_SELECTION
, USB_VENDOR
, USB_PRODUCT
, USB_CFGNDX
, USB_SRNO
,
56 USB_PATH
, USB_DRIVER
, USB_NONE
59 typedef struct usbcfg_var
{
64 static usbcfg_var_t usbcfg_varlist
[] = {
65 { "selection", USB_SELECTION
},
66 { "idVendor", USB_VENDOR
},
67 { "idProduct", USB_PRODUCT
},
68 { "cfgndx", USB_CFGNDX
},
70 { "pathname", USB_PATH
},
71 { "driver", USB_DRIVER
},
95 static char usbconf_file
[] = USBCONF_FILE
;
96 static int linenum
= 1;
100 static int btoken
= 0;
101 mutex_t file_lock
= DEFAULTMUTEX
;
107 static int get_string(u_longlong_t
*llptr
, char *tchar
);
108 static int getvalue(char *token
, u_longlong_t
*valuep
);
112 * The next item on the line is a string value. Allocate memory for
113 * it and copy the string. Return 1, and set arg ptr to newly allocated
114 * and initialized buffer, or NULL if an error occurs.
117 get_string(u_longlong_t
*llptr
, char *tchar
)
120 register char *start
= (char *)0;
121 register int len
= 0;
126 cp
= (char *)calloc(len
+ 1, sizeof (char));
127 if (cp
== (char *)NULL
) {
133 *llptr
= (u_longlong_t
)(uintptr_t)cp
;
134 for (; len
> 0; len
--) {
135 /* convert some common escape sequences */
136 if (*start
== '\\') {
137 switch (*(start
+ 1)) {
171 * get a decimal octal or hex number. Handle '~' for one's complement.
174 getvalue(char *token
, u_longlong_t
*valuep
)
177 register u_longlong_t retval
= 0;
178 register int onescompl
= 0;
179 register int negate
= 0;
183 onescompl
++; /* perform one's complement on result */
185 } else if (*token
== '-') {
194 *valuep
= 0; /* value is 0 */
198 if (c
== 'x' || c
== 'X') {
208 while ((c
= *token
++)) {
211 if (c
>= '0' && c
<= '7') {
214 return (-1); /* invalid number */
216 retval
= (retval
<< 3) + c
;
219 if (c
>= '0' && c
<= '9') {
222 return (-1); /* invalid number */
224 retval
= (retval
* 10) + c
;
227 if (c
>= 'a' && c
<= 'f') {
229 } else if (c
>= 'A' && c
<= 'F') {
231 } else if (c
>= '0' && c
<= '9') {
234 return (-1); /* invalid number */
236 retval
= (retval
<< 4) + c
;
250 * returns the field from the token
252 static config_field_t
253 usb_get_var_type(char *str
)
255 usbcfg_var_t
*cfgvar
;
257 cfgvar
= &usbcfg_varlist
[0];
258 while (cfgvar
->field
!= USB_NONE
) {
259 if (strcasecmp(cfgvar
->name
, str
) == NULL
) {
266 return (cfgvar
->field
);
272 lex(char *buf
, char *val
, char **errmsg
)
274 int ch
, oval
, badquote
;
279 while ((ch
= GETC(buf
, cntr
)) == ' ' || ch
== '\t');
282 * Note the beginning of a token
318 while ((ch
= GETC(buf
, cntr
)) == ' ' ||
319 ch
== '\t' || ch
== '\f')
331 while (!badquote
&& (ch
= GETC(buf
, cntr
)) != '"') {
335 (void) snprintf(*errmsg
, MAXPATHLEN
,
340 /* since we consumed the newline/EOF */
345 ch
= (char)GETC(buf
, cntr
);
347 /* escape the character */
352 while (ch
>= '0' && ch
<= '7') {
354 oval
= (oval
<< 3) + ch
;
355 ch
= (char)GETC(buf
, cntr
);
358 /* check for character overflow? */
360 (void) snprintf(*errmsg
, MAXPATHLEN
,
361 "Character overflow detected.\n");
379 * detect a lone '-' (including at the end of a line), and
380 * identify it as a 'name'
383 *cp
++ = (char)(ch
= GETC(buf
, cntr
));
384 if (iswhite(ch
) || (ch
== '\n')) {
390 } else if (isunary(ch
)) {
391 *cp
++ = (char)(ch
= GETC(buf
, cntr
));
396 if ((ch
= GETC(buf
, cntr
)) == 'x') {
398 ch
= GETC(buf
, cntr
);
399 while (isxdigit(ch
)) {
401 ch
= GETC(buf
, cntr
);
409 ch
= GETC(buf
, cntr
);
411 while (isdigit(ch
)) {
413 ch
= GETC(buf
, cntr
);
418 } else if (isalpha(ch
) || ch
== '\\') {
420 ch
= GETC(buf
, cntr
);
423 * if the character was a backslash,
424 * back up so we can overwrite it with
425 * the next (i.e. escaped) character.
430 while (isnamechar(ch
) || ch
== '\\') {
432 ch
= GETC(buf
, cntr
);
434 ch
= GETC(buf
, cntr
);
451 * Leave NEWLINE as the next character.
458 while ((ch
= GETC(buf
, cntr
)) != -1) {
468 * Fetch one record from the USBCONF_FILE
471 usb_get_conf_rec(char *buf
, usb_configrec_t
**rec
, char **errmsg
)
474 char tokval
[MAXLINESIZE
];
475 usb_configrec_t
*user_rec
;
476 config_field_t cfgvar
;
479 boolean_t sor
= B_TRUE
;
482 USB_NEWVAR
, USB_CONFIG_VAR
, USB_VAR_EQUAL
, USB_VAR_VALUE
,
484 } parse_state
= USB_NEWVAR
;
486 DPRINTF("usb_get_conf_rec:\n");
488 user_rec
= (usb_configrec_t
*)calloc(1, sizeof (usb_configrec_t
));
489 if (user_rec
== (usb_configrec_t
*)NULL
) {
493 user_rec
->idVendor
= user_rec
->idProduct
= user_rec
->cfgndx
= -1;
495 token
= lex(buf
, tokval
, errmsg
);
496 while ((token
!= EOF
) && (token
!= SEMICOLON
)) {
508 switch (parse_state
) {
510 cfgvar
= usb_get_var_type(tokval
);
511 if (cfgvar
== USB_NONE
) {
512 parse_state
= USB_ERROR
;
513 (void) snprintf(*errmsg
, MAXPATHLEN
,
514 "Syntax Error: Invalid field %s",
518 * Note the beginning of a record
522 if (frec
== 0) frec
= brec
;
525 parse_state
= USB_CONFIG_VAR
;
529 if ((cfgvar
== USB_VENDOR
) ||
530 (cfgvar
== USB_PRODUCT
) ||
531 (cfgvar
== USB_CFGNDX
)) {
532 parse_state
= USB_ERROR
;
533 (void) snprintf(*errmsg
, MAXPATHLEN
,
534 "Syntax Error: Invalid value %s "
535 "for field: %s\n", tokval
,
536 usbcfg_varlist
[cfgvar
].name
);
537 } else if (get_string(&llptr
, tokval
)) {
540 user_rec
->selection
=
541 (char *)(uintptr_t)llptr
;
542 parse_state
= USB_NEWVAR
;
546 (char *)(uintptr_t)llptr
;
547 parse_state
= USB_NEWVAR
;
551 (char *)(uintptr_t)llptr
;
552 parse_state
= USB_NEWVAR
;
556 (char *)(uintptr_t)llptr
;
557 parse_state
= USB_NEWVAR
;
560 parse_state
= USB_ERROR
;
561 free((void *)(uintptr_t)llptr
);
564 parse_state
= USB_ERROR
;
565 (void) snprintf(*errmsg
, MAXPATHLEN
,
566 "Syntax Error: Invalid value %s "
567 "for field: %s\n", tokval
,
568 usbcfg_varlist
[cfgvar
].name
);
575 parse_state
= USB_ERROR
;
576 (void) snprintf(*errmsg
, MAXPATHLEN
,
577 "Syntax Error: at %s", tokval
);
582 if (parse_state
== USB_CONFIG_VAR
) {
583 if (cfgvar
== USB_NONE
) {
584 parse_state
= USB_ERROR
;
585 (void) snprintf(*errmsg
, MAXPATHLEN
,
586 "Syntax Error: unexpected '='");
588 parse_state
= USB_VAR_VALUE
;
590 } else if (parse_state
!= USB_ERROR
) {
591 (void) snprintf(*errmsg
, MAXPATHLEN
,
592 "Syntax Error: unexpected '='");
593 parse_state
= USB_ERROR
;
598 if ((parse_state
== USB_VAR_VALUE
) && (cfgvar
!=
600 (void) getvalue(tokval
, &value
);
603 user_rec
->idVendor
= (int)value
;
604 parse_state
= USB_NEWVAR
;
607 user_rec
->idProduct
= (int)value
;
608 parse_state
= USB_NEWVAR
;
611 user_rec
->cfgndx
= (int)value
;
612 parse_state
= USB_NEWVAR
;
615 (void) snprintf(*errmsg
, MAXPATHLEN
,
616 "Syntax Error: Invalid value for "
617 "%s", usbcfg_varlist
[cfgvar
].name
);
619 } else if (parse_state
!= USB_ERROR
) {
620 parse_state
= USB_ERROR
;
621 (void) snprintf(*errmsg
, MAXPATHLEN
,
622 "Syntax Error: unexpected hex/decimal: %s",
627 (void) snprintf(*errmsg
, MAXPATHLEN
,
628 "Syntax Error: at: %s", tokval
);
629 parse_state
= USB_ERROR
;
632 token
= lex(buf
, tokval
, errmsg
);
641 * Here we compare the two records and determine if they are the same
644 usb_cmp_rec(usb_configrec_t
*cfg_rec
, usb_configrec_t
*user_rec
)
647 boolean_t srno
= B_FALSE
, path
= B_FALSE
;
649 DPRINTF("usb_cmp_rec:\n");
651 if ((cfg_rec
->idVendor
== user_rec
->idVendor
) &&
652 (cfg_rec
->idProduct
== user_rec
->idProduct
)) {
653 if (user_rec
->serialno
) {
654 if (cfg_rec
->serialno
) {
655 srno
= (strcmp(cfg_rec
->serialno
,
656 user_rec
->serialno
) == 0);
662 } else if (user_rec
->pathname
) {
663 if (cfg_rec
->pathname
) {
665 * Comparing on this is tricky. At this point
666 * hubd knows: ../hubd@P/device@P while user
667 * will specify ..../hubd@P/keyboard@P
668 * First compare till .../hubd@P
669 * Second compare is just P in "device@P"
671 * XXX: note that we assume P as one character
672 * as there are no 2 digit hubs in the market.
674 ustr
= strrchr(user_rec
->pathname
, '/');
675 cstr
= strrchr(cfg_rec
->pathname
, '/');
676 path
= (strncmp(cfg_rec
->pathname
,
678 MAX(ustr
- user_rec
->pathname
,
679 cstr
- cfg_rec
->pathname
)) == 0);
680 path
= path
&& (*(user_rec
->pathname
+
681 strlen(user_rec
->pathname
) -1) ==
682 *(cfg_rec
->pathname
+
683 strlen(cfg_rec
->pathname
) - 1));
689 } else if (cfg_rec
->serialno
|| cfg_rec
->pathname
) {
697 return (srno
|| path
);
706 * free the record allocated in usb_get_conf_rec
709 usb_free_rec(usb_configrec_t
*rec
)
711 if (rec
== (usb_configrec_t
*)NULL
) {
716 free(rec
->selection
);
725 add_entry(char *selection
, int vid
, int pid
, int cfgndx
, char *srno
,
726 char *path
, char *driver
, char **errmsg
)
729 int rval
= CFGA_USB_OK
;
730 char *buf
= (char *)NULL
;
731 char str
[MAXLINESIZE
];
732 token_t token
= NEWLINE
;
733 boolean_t found
= B_FALSE
;
735 usb_configrec_t cfgrec
, *user_rec
= NULL
;
737 DPRINTF("add_entry: driver=%s, path=%s\n",
738 driver
? driver
: "", path
? path
: "");
740 if (*errmsg
== (char *)NULL
) {
741 if ((*errmsg
= calloc(MAXPATHLEN
, 1)) == (char *)NULL
) {
743 return (CFGA_USB_CONFIG_FILE
);
747 (void) mutex_lock(&file_lock
);
749 /* Initialize the cfgrec */
750 cfgrec
.selection
= selection
;
751 cfgrec
.idVendor
= vid
;
752 cfgrec
.idProduct
= pid
;
753 cfgrec
.cfgndx
= cfgndx
;
754 cfgrec
.serialno
= srno
;
755 cfgrec
.pathname
= path
;
756 cfgrec
.driver
= driver
;
758 /* open config_map.conf file */
759 file
= open(usbconf_file
, O_RDWR
, 0666);
761 (void) snprintf(*errmsg
, MAXPATHLEN
,
762 "failed to open config file\n");
763 (void) mutex_unlock(&file_lock
);
765 return (CFGA_USB_CONFIG_FILE
);
768 if (lockf(file
, F_TLOCK
, 0) == -1) {
769 (void) snprintf(*errmsg
, MAXPATHLEN
,
770 "failed to lock config file\n");
772 (void) mutex_unlock(&file_lock
);
774 return (CFGA_USB_LOCK_FILE
);
778 * These variables need to be reinitialized here as they may
779 * have been modified by a previous thread that called this
788 if (fstat(file
, &st
) != 0) {
789 DPRINTF("add_entry: failed to fstat config file\n");
790 rval
= CFGA_USB_CONFIG_FILE
;
794 if ((buf
= (char *)malloc(st
.st_size
)) == NULL
) {
795 DPRINTF("add_entry: failed to fstat config file\n");
796 rval
= CFGA_USB_ALLOC_FAIL
;
800 if (st
.st_size
!= read(file
, buf
, st
.st_size
)) {
801 DPRINTF("add_entry: failed to read config file\n");
802 rval
= CFGA_USB_CONFIG_FILE
;
806 /* set up for reading the file */
808 while ((token
!= EOF
) && !found
) {
810 usb_free_rec(user_rec
);
813 token
= usb_get_conf_rec(buf
, &user_rec
, errmsg
);
814 found
= usb_cmp_rec(&cfgrec
, user_rec
);
815 DPRINTF("add_entry: token=%x, found=%x\n", token
, found
);
818 bzero(str
, MAXLINESIZE
);
822 (void) snprintf(str
, MAXLINESIZE
, "selection=%s idVendor=0x%x "
824 (cfgrec
.selection
) ? cfgrec
.selection
: user_rec
->selection
,
825 user_rec
->idVendor
, user_rec
->idProduct
);
827 if ((user_rec
->cfgndx
!= -1) || (cfgrec
.cfgndx
!= -1)) {
828 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
829 "cfgndx=0x%x ", (cfgrec
.cfgndx
!= -1) ?
830 cfgrec
.cfgndx
: user_rec
->cfgndx
);
833 if (user_rec
->serialno
) {
834 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
835 "srno=\"%s\" ", user_rec
->serialno
);
838 if (user_rec
->pathname
) {
839 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
840 "pathname=\"%s\" ", user_rec
->pathname
);
843 if (user_rec
->driver
) {
844 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
845 "driver=\"%s\" ", user_rec
->driver
);
846 } else if (cfgrec
.driver
!= NULL
) {
847 if (strlen(cfgrec
.driver
)) {
848 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
849 "driver=\"%s\" ", cfgrec
.driver
);
853 (void) strlcat(str
, ";", sizeof (str
));
856 * Seek to the beginning of the record
858 if (lseek(file
, brec
, SEEK_SET
) == -1) {
859 DPRINTF("add_entry: failed to lseek config file\n");
860 rval
= CFGA_USB_CONFIG_FILE
;
865 * Write the modified record
867 if (write(file
, str
, strlen(str
)) == -1) {
868 DPRINTF("add_entry: failed to write config file\n");
869 rval
= CFGA_USB_CONFIG_FILE
;
874 * Write the rest of the file as it was
876 if (write(file
, buf
+cntr
, st
.st_size
- cntr
) == -1) {
877 DPRINTF("add_entry: failed to write config file\n");
878 rval
= CFGA_USB_CONFIG_FILE
;
884 (void) snprintf(str
, MAXLINESIZE
,
885 "selection=%s idVendor=0x%x idProduct=0x%x ",
886 (cfgrec
.selection
) ? cfgrec
.selection
: "enable",
887 cfgrec
.idVendor
, cfgrec
.idProduct
);
889 if (cfgrec
.cfgndx
!= -1) {
890 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
891 "cfgndx=0x%x ", cfgrec
.cfgndx
);
894 if (cfgrec
.serialno
) {
895 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
896 "srno=\"%s\" ", cfgrec
.serialno
);
899 if (cfgrec
.pathname
!= NULL
) {
900 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
901 "pathname=\"%s\" ", cfgrec
.pathname
);
904 if (cfgrec
.driver
!= NULL
) {
905 if (strlen(cfgrec
.driver
)) {
906 (void) snprintf(&str
[strlen(str
)], MAXLINESIZE
,
907 "driver=\"%s\" ", cfgrec
.driver
);
911 (void) strlcat(str
, ";\n", sizeof (str
));
914 * Incase this is the first entry, add it after the comments
921 * Go to the beginning of the records
923 if (lseek(file
, frec
, SEEK_SET
) == -1) {
924 DPRINTF("add_entry: failed to lseek config file\n");
925 rval
= CFGA_USB_CONFIG_FILE
;
932 if (write(file
, str
, strlen(str
)) == -1) {
933 DPRINTF("add_entry: failed to write config file\n");
934 rval
= CFGA_USB_CONFIG_FILE
;
939 * write the remaining file as it was
941 if (write(file
, buf
+frec
, st
.st_size
- frec
) == -1) {
942 DPRINTF("add_entry: failed to write config file\n");
943 rval
= CFGA_USB_CONFIG_FILE
;
948 /* no error encountered */
949 if (rval
== CFGA_USB_OK
) {
958 if (lockf(file
, F_ULOCK
, 0) == -1) {
959 DPRINTF("add_entry: failed to unlock config file\n");
961 rval
= CFGA_USB_LOCK_FILE
;
966 (void) mutex_unlock(&file_lock
);