Index: etc/defaults/rc.conf =================================================================== --- etc/defaults/rc.conf (revision 329193) +++ etc/defaults/rc.conf (working copy) @@ -41,7 +41,8 @@ ddb_config="/etc/ddb.conf" # ddb(8) config file. devd_enable="YES" # Run devd, to trigger programs on device tree changes. devd_flags="" # Additional flags for devd(8). -devmatch_enable="YES" # Demand load kernel modules based on device ids. +devmatch_enable="YES" # Demand load kernel modules based on device IDs. +devmatch_flags="" # Additional flags for devmatch(8). #kld_list="" # Kernel modules to load after local disks are mounted kldxref_enable="NO" # Build linker.hints files with kldxref(8). kldxref_clobber="NO" # Overwrite old linker.hints at boot. Index: etc/devd/devmatch.conf =================================================================== --- etc/devd/devmatch.conf (revision 329194) +++ etc/devd/devmatch.conf (working copy) @@ -7,14 +7,10 @@ # loading what modules we can based on nomatch # events. # -# Generic NOMATCH event + +# USB NOMATCH event nomatch 100 { - action "service devmatch start"; + match "bus" "uhub[0-9]+"; + action "/etc/rc.d/devmatch start 'bus=usb device=$ugen vendor=$vendor product=$product devclass=$devclass devsubclass=$devsubclass devprotocol=$devproto release=$release intclass=$intclass intsubclass=$intsubclass intprotocol=$intprotocol mode=$mode'"; }; -# Add the following to devd.conf to prevent this from running: -# nomatch 1000 { -# action "true"; -# }; -# it replaces the generic event with one of higher priority that -# does nothing. You can also set 'devmatch_enable=NO' in /etc/rc.conf Index: etc/rc.d/devmatch =================================================================== --- etc/rc.d/devmatch (revision 329193) +++ etc/rc.d/devmatch (working copy) @@ -38,17 +38,54 @@ start_cmd="${name}_start" stop_cmd=':' -devmatch_start() +fixed_pnpinfo=${2} + +devmatch_start_default() { local x + local m - x=$(devmatch | sort -u) + x=$(devmatch $devmatch_flags | sort -u) - [ -n "$x" ] || return + # + # Load modules one by one, because + # kldload will bail out on the first + # failure: + # + for m in ${x} + do + echo "Autoloading module: ${m}" + kldload -n ${m} + done +} - echo "Autoloading modules: ${x}" - kldload ${x} +devmatch_start_pnpinfo() +{ + local x + local m + + x=$(devmatch -p "$fixed_pnpinfo" | sort -u) + + # + # Load modules one by one, because + # kldload will bail out on the first + # failure: + # + for m in ${x} + do + echo "Autoloading module: ${m}" + kldload -n ${m} + done } +devmatch_start() +{ + if [ "$fixed_pnpinfo" ]; then + devmatch_start_pnpinfo + else + devmatch_start_default + fi +} + load_rc_config $name run_rc_command "$1" Index: sbin/devmatch/devmatch.8 =================================================================== --- sbin/devmatch/devmatch.8 (revision 329193) +++ sbin/devmatch/devmatch.8 (working copy) @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 12, 2018 +.Dd February 13, 2018 .Dt DEVMATCH 8 .Os .Sh NAME @@ -33,9 +33,10 @@ .Nd print information about unattached devices .Sh SYNOPSIS .Nm -.Op Fl aduv +.Op Fl adpuv .Op Fl -all .Op Fl -dump +.Op Fl -pnpinfo .Op Fl -unbound .Op Fl -verbose .Sh DESCRIPTION @@ -50,6 +51,8 @@ Produce a human readable dump of the .Pa linker.hints file. +.It Fl p Fl -pnpinfo +Specify plug and play information to be used when matching a driver. .It Fl u Fl -unbound Attempt to produce a list of those drivers with PNP info whose driver tables with that PNP info can't be found. Index: sbin/devmatch/devmatch.c =================================================================== --- sbin/devmatch/devmatch.c (revision 329193) +++ sbin/devmatch/devmatch.c (working copy) @@ -47,6 +47,7 @@ static struct option longopts[] = { { "all", no_argument, NULL, 'a' }, { "dump", no_argument, NULL, 'd' }, + { "pnpinfo", required_argument, NULL, 'p' }, { "unbound", no_argument, NULL, 'u' }, { "verbose", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } @@ -59,6 +60,7 @@ static void *hints; static void *hints_end; +static const char *fixed_pnpinfo; static void read_linker_hints(void) @@ -137,36 +139,83 @@ } static int +extract_key(char *key, const char *val, int size, char end) +{ + char *old = key; + + /* prepend a space character */ + *key++ = ' '; + size--; + + /* extract key */ + while (1) { + if (*val == '\0' || *val == ';') + break; + if (size < 3) { + warnx("Key is too long."); + return (-1); + } + *key++ = *val++; + size--; + } + + /* append end character, if any */ + if (end != '\0') + *key++ = end; + + /* zero terminate */ + *key = '\0'; + + /* return string length */ + return (key - old); +} + +static int pnpval_as_int(const char *val, const char *pnpinfo) { + char key[256]; + int len; int rv; - char key[256]; - char *cp; if (pnpinfo == NULL) - return -1; + return (-1); - cp = strchr(val, ';'); - key[0] = ' '; - if (cp == NULL) - strlcpy(key + 1, val, sizeof(key) - 1); - else { - memcpy(key + 1, val, cp - val); - key[cp - val + 1] = '\0'; - } - strlcat(key, "=", sizeof(key)); - if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) - rv = strtol(pnpinfo + strlen(key + 1), NULL, 0); - else { - cp = strstr(pnpinfo, key); + len = extract_key(key, val, sizeof(key), '='); + if (len < 1) + return (-1); + + /* check for match at beginning, skipping first space */ + if (strncmp(key + 1, pnpinfo, len - 1) == 0) { + rv = strtol(pnpinfo + len - 1, NULL, 0); + } else { + /* check for match inside */ + const char *cp = strstr(pnpinfo, key); if (cp == NULL) rv = -1; else - rv = strtol(cp + strlen(key), NULL, 0); + rv = strtol(cp + len, NULL, 0); } - return rv; + return (rv); } +static int +pnpval_as_type_match(const char *val, const char *pnpinfo) +{ + char key[256]; + int len; + + if (pnpinfo == NULL) + return (0); + + len = extract_key(key, val, sizeof(key), '\0'); + if (len < 1) + return (0); + + /* check for match */ + return (strncmp(key + 1, pnpinfo, len - 1) == 0 || + strstr(pnpinfo, key) != NULL); +} + static void quoted_strcpy(char *dst, const char *src) { @@ -180,37 +229,35 @@ // XXX overflow } -static char * +static const char * pnpval_as_str(const char *val, const char *pnpinfo) { static char retval[256]; char key[256]; - char *cp; + int len; if (pnpinfo == NULL) { - *retval = '\0'; - return retval; + retval[0] = '\0'; + return (retval); } - cp = strchr(val, ';'); - key[0] = ' '; - if (cp == NULL) - strlcpy(key + 1, val, sizeof(key) - 1); - else { - memcpy(key + 1, val, cp - val); - key[cp - val + 1] = '\0'; + len = extract_key(key, val, sizeof(key), '='); + if (len < 1) { + retval[0] = '\0'; + return (retval); } - strlcat(key, "=", sizeof(key)); - if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) - quoted_strcpy(retval, pnpinfo + strlen(key + 1)); - else { - cp = strstr(pnpinfo, key); + + /* check for match at beginning, skipping first space */ + if (strncmp(key + 1, pnpinfo, len - 1) == 0) { + quoted_strcpy(retval, pnpinfo + len - 1); + } else { + const char *cp = strstr(pnpinfo, key); if (cp == NULL) strcpy(retval, "MISSING"); else - quoted_strcpy(retval, cp + strlen(key)); + quoted_strcpy(retval, cp + len); } - return retval; + return (retval); } static void @@ -219,7 +266,8 @@ char val1[256], val2[256]; int ival, len, ents, i, notme, mask, bit, v, found; void *ptr, *walker; - char *lastmod = NULL, *cp, *s; + char *lastmod = NULL; + const char *cp, *s; walker = hints; getint(&walker); @@ -284,7 +332,7 @@ break; /*FALLTHROUGH*/ case 'I': - if (v != ival && ival != 0) + if (v != ival) notme++; break; case 'G': @@ -313,6 +361,16 @@ if (strcmp(s, val1) != 0) notme++; break; + case 'T': + if (dump_flag) { + int x; + for (x = 2; cp[x] != '\0' && cp[x] != ';'; x++) + putchar(cp[x]); + break; + } + if (!pnpval_as_type_match(cp + 2, pnpinfo)) + notme++; + break; default: break; } @@ -382,7 +440,7 @@ usage(void) { - errx(1, "devmatch [-adv]"); + errx(1, "devmatch [-adv] [-p ]"); } int @@ -391,7 +449,7 @@ struct devinfo_dev *root; int ch; - while ((ch = getopt_long(argc, argv, "aduv", + while ((ch = getopt_long(argc, argv, "aduvp:", longopts, NULL)) != -1) { switch (ch) { case 'a': @@ -403,6 +461,9 @@ case 'u': unbound_flag++; break; + case 'p': + fixed_pnpinfo = optarg; + break; case 'v': verbose_flag++; break; @@ -422,6 +483,13 @@ exit(0); } + if (fixed_pnpinfo != NULL) { + const char *bus = strdup(pnpval_as_str("bus", fixed_pnpinfo)); + const char *dev = strdup(pnpval_as_str("device", fixed_pnpinfo)); + search_hints(bus, dev, fixed_pnpinfo); + exit(0); + } + if (devinfo_init()) err(1, "devinfo_init"); if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL)