Index: head/etc/usbd.conf =================================================================== --- head/etc/usbd.conf (nonexistent) +++ head/etc/usbd.conf (revision 53517) @@ -0,0 +1,35 @@ +# Configuration file the USB daemon. +# +# See usbd.conf(5) for the description of the format of the file. + +# Firmware download into the ActiveWire board. After the firmware download is done +# the device detaches and reappears as something new and shiny. +# +device "ActiveWire board, firmware download" + product 0x0100 + vendor 0x0854 + release 0x0000 + attach "/usr/local/bin/ezdownload -f /usr/local/share/usb/firmware/0854.0100.0_01.hex" + +# The piece below has to be copied for every drive. It does not work for the +# generic case, the umass storage class uses interface drivers. The info for +# the interfaces is not yet exported. +# +device "USB Zip drive" + product 0x0001 + vendor 0x059b + release 0x0100 + attach "/usr/bin/camcontrol rescan bus 0" + +# The entry below is for the Logitech mouse. Replace the product and vendor +# id (and the device name of course) with the data for your mouse. +# +device "Logitech N48 USB mouse" + product 0xc001 + vendor 0x046d + attach "/usr/sbin/moused -p /dev/ums0 -I /var/run/moused.ums0.pid" + +# The fallthrough entry: Nothing is specified, nothing is done. And it isn't +# necessary at all :-). Just for pretty printing in debugging mode. +# +device "USB device" Property changes on: head/etc/usbd.conf ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/usr.sbin/usbd/Makefile =================================================================== --- head/usr.sbin/usbd/Makefile (revision 53516) +++ head/usr.sbin/usbd/Makefile (revision 53517) @@ -1,7 +1,8 @@ # $FreeBSD$ PROG= usbd MAN8= usbd.8 +MAN5= usbd.conf.5 CFLAGS+=-I${.CURDIR}/../../sys .include Index: head/usr.sbin/usbd/usbd.8 =================================================================== --- head/usr.sbin/usbd/usbd.8 (revision 53516) +++ head/usr.sbin/usbd/usbd.8 (revision 53517) @@ -1,83 +1,110 @@ .\" $NetBSD: usbd.8,v 1.2 1998/07/13 11:01:50 augustss Exp $ -.\" $FreeBSD$ .\" Copyright (c) 1998 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" Author: Lennart Augustsson .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the NetBSD .\" Foundation, Inc. and its contributors. .\" 4. Neither the name of The NetBSD Foundation nor the names of its .\" contributors may be used to endorse or promote products derived .\" from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED .\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR .\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS .\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd July 12, 1998 .Dt USBD 8 .Os .Sh NAME .Nm usbd .Nd supervise USB attach/detach .Sh SYNOPSIS .Nm .Op Fl d .Op Fl f Ar device .Op Fl t Ar timeout .Op Fl v .Sh DESCRIPTION .Nm handles the USB device attachment and detachment. .Pp The options are as follows: .Bl -tag -width Ds +.It Fl c Ar filename +Name of configuration file. The default is +.Pa /etc/usbd.conf. .It Fl d Enable debugging to the standard output, and do not disassociate from the controlling terminal. .It Fl e Do one device tree exploration and then exit. .It Fl f Ar device Specify the pathname of a USB controller device file. The flag may be repeated to watch more than one USB controller. The default is -.Pa /dev/usb0 , -.Pa /dev/usb1 , -.Pa /dev/usb2 , -and +.Pa /dev/usb0 +through .Pa /dev/usb3 . +Do not specify the device +.Pa /dev/usb +here. It is used for events only. +.It Fl n +Do not handle the event queue on /dev/usb. .It Fl t Ar timeout Set the timeout interval (in seconds) before an exploration happens without being triggered by a connect or disconnect. A timeout of 0 means that there is no timeout. The default is 30. .It Fl v -Be verbose. +Be verbose. Repeating the flag makes +.Nm usbd +more verbose. .El +.Sh FILES +.Bl -tag -width /etc/usbd.conf -compact +.It Pa /etc/usbd.conf +.It /dev/usb +.It /dev/usb0 +.It /dev/usb1 +.It etc. .Sh SEE ALSO -.Xr usb 4 +.Xr usb 4 , +.Xr usbd.conf 8 .Sh HISTORY The .Nm command appeared in -.Nx 1.4 . +.Nx 4.0 . +.Sh AUTHORS +The +.Nm +driver was written by +.An Lennart Augustsson Aq augustss@carlstedt.se +for the +.Nx +project. The event queue handling in +.Nm usbd +was added by +.An Nick Hibma Aq n_hibma@freebsd.org . + Index: head/usr.sbin/usbd/usbd.c =================================================================== --- head/usr.sbin/usbd/usbd.c (revision 53516) +++ head/usr.sbin/usbd/usbd.c (revision 53517) @@ -1,199 +1,933 @@ /* $NetBSD: usbd.c,v 1.4 1998/12/09 00:57:19 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@netbsd.org). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include +/* USBD creates 'threads' in the kernel, used for doing discovery when a + * device has attached or detached. This functionality should be removed + * once kernel threads have been added to the kernel. + * It also handles the event queue, and executing commands based on those + * events. + * + * See usbd(8). + */ + #include #include #include -#include #include #include +#include +#include +#include +#include #include -#if defined(__FreeBSD__) #include -#endif +#include +#include +#include + #include +/* default name of configuration file + */ + +#define CONFIGFILE "/etc/usbd.conf" + /* the name of the device spitting out usb attach/detach events as well as * the prefix for the individual busses (used as a semi kernel thread). */ -#define USBDEV "/dev/usb" +#define USBDEV "/dev/usb" /* Maximum number of USB busses expected to be in a system + * XXX should be replaced by dynamic allocation. */ -#define MAXUSBDEV 4 +#define MAXUSBDEV 4 -/* - * Sometimes a device does not respond in time for interrupt +/* Sometimes a device does not respond in time for interrupt * driven explore to find it. Therefore we run an exploration * at regular intervals to catch those. */ -#define TIMEOUT 30 +#define TIMEOUT 30 +/* The wildcard used in actions for strings and integers + */ +#define WILDCARD_STRING NULL +#define WILDCARD_INT -1 -extern char *__progname; -void usage(void); +extern char *__progname; /* name of program */ +char *configfile = CONFIGFILE; /* name of configuration file */ + +char *devs[MAXUSBDEV]; /* device names */ +int fds[MAXUSBDEV]; /* file descriptors for USBDEV\d+ */ +int ndevs = 0; /* number of entries in fds / devs */ +int fd = -1; /* file descriptor for USBDEV */ + +int lineno; +int verbose = 0; /* print message on what it is doing */ + +typedef struct event_name_s { + int type; /* event number (from usb.h) */ + char *name; +} event_name_t; + +event_name_t event_names[] = { + {USB_EVENT_ATTACH, "attach"}, + {USB_EVENT_DETACH, "detach"}, + {0, NULL} /* NULL indicates end of list, not 0 */ +}; + +#define DEVICE_FIELD 0 /* descriptive field */ + +#define PRODUCT_FIELD 1 /* selective fields */ +#define VENDOR_FIELD 2 +#define RELEASE_FIELD 3 +#define CLASS_FIELD 4 +#define SUBCLASS_FIELD 5 +#define PROTOCOL_FIELD 6 + +#define ATTACH_FIELD 8 /* command fields */ +#define DETACH_FIELD 9 + + +typedef struct action_s { + char *name; /* descriptive string */ + + int product; /* selection criteria */ + int vendor; + int release; + int class; + int subclass; + int protocol; + + char *attach; /* commands to execute */ + char *detach; + + STAILQ_ENTRY(action_s) next; +} action_t; + +STAILQ_HEAD(action_list, action_s) actions = STAILQ_HEAD_INITIALIZER(actions); + + +/* the function returns 0 for failure, 1 for all arguments found and 2 for + * arguments left over in trail. + */ +typedef int (*config_field_fn) __P((action_t *action, char *args, + char **trail)); + +int set_device_field(action_t *action, char *args, char **trail); +int set_product_field(action_t *action, char *args, char **trail); +int set_vendor_field(action_t *action, char *args, char **trail); +int set_release_field(action_t *action, char *args, char **trail); +int set_class_field(action_t *action, char *args, char **trail); +int set_subclass_field(action_t *action, char *args, char **trail); +int set_protocol_field(action_t *action, char *args, char **trail); +int set_attach_field(action_t *action, char *args, char **trail); +int set_detach_field(action_t *action, char *args, char **trail); + +/* the list of fields supported in an entry */ +typedef struct config_field_s { + int event; + char *name; + config_field_fn function; +} config_field_t; + +config_field_t config_fields[] = { + {DEVICE_FIELD, "device", set_device_field}, + + {PRODUCT_FIELD, "product", set_product_field}, + {VENDOR_FIELD, "vendor", set_vendor_field}, + {RELEASE_FIELD, "release", set_release_field}, + {CLASS_FIELD, "class", set_class_field}, + {SUBCLASS_FIELD, "subclass", set_subclass_field}, + {PROTOCOL_FIELD, "protocol", set_protocol_field}, + + {ATTACH_FIELD, "attach", set_attach_field}, + {DETACH_FIELD, "detach", set_detach_field}, + + {0, NULL, NULL} /* NULL is EOL marker, not the 0 */ +}; + + +/* prototypes for some functions */ +void print_event __P((struct usb_event *event)); +void print_action __P((action_t *action, int i)); +void print_actions __P((void)); +action_t *find_action __P((struct usb_device_info *devinfo)); + + void usage(void) { - fprintf(stderr, "Usage: %s [-d] [-e] [-f dev] [-t timeout] [-v]\n", + fprintf(stderr, "Usage: %s [-d] [-v] [-t timeout] [-e] [-f dev]\n" + " [-n] [-c config]\n", __progname); - fprintf(stderr, " -d for debugging\n"); - fprintf(stderr, " -e only do 1 explore\n"); - fprintf(stderr, " -f dev for example /dev/usb0, " - "and can be specified multiple times."); - fprintf(stderr, " -t timeout timeout between explores\n"); - fprintf(stderr, " -v verbose output\n"); - exit(1); } + +/* generic helper functions for the functions to set the fields of actions */ int +get_string(char *src, char **rdst, char **rsrc) +{ + /* Takes the first string from src, taking quoting into account. + * rsrc (if not NULL) is set to the first byte not included in the + * string returned in rdst. + * + * Input is: + * src = 'fir"st \'par"t second part'; + * Returned is: + * *dst = 'hello \'world'; + * if (rsrc != NULL) + * *rsrc = 'second part'; + * + * Notice the fact that the single quote enclosed in double quotes is + * returned. Also notice that before second part there is more than + * one space, which is removed in rsrc. + * + * The string in src is not modified. + */ + + char *dst; /* destination string */ + int i; /* index into src */ + int j; /* index into dst */ + int quoted = 0; /* 1 for single, 2 for double quoted */ + + dst = malloc(strlen(src)); /* XXX allocation is too big, realloc?*/ + if (dst == NULL) { /* should not happen, really */ + fprintf(stderr, "%s:%d: Out of memory\n", configfile, lineno); + exit(2); + } + + /* find the end of the current string. If quotes are found the search + * continues until the corresponding quote is found. + * So, + * hel'lo" "wor'ld + * represents the string + * hello" "world + * and not (hello world). + */ + for (i = 0, j = 0; i < strlen(src); i++) { + if (src[i] == '\'' && (quoted == 0 || quoted == 1)) { + quoted = (quoted? 0:1); + } else if (src[i] == '"' && (quoted == 0 || quoted == 2)) { + quoted = (quoted? 0:2); + } else if (isspace(src[i]) && !quoted) { + /* found a space outside quotes -> terminates src */ + break; + } else { + dst[j++] = src[i]; /* copy character */ + } + } + + /* quotes being left open? */ + if (quoted) { + fprintf(stderr, "%s:%d: Missing %s quote at end of '%s'\n", + configfile, lineno, + (quoted == 1? "single":"double"), src); + exit(2); + } + + /* skip whitespace for second part */ + for (/*i is set*/; i < strlen(src) && isspace(src[i]); i++) + ; /* nop */ + + dst[j] = '\0'; /* make sure it's NULL terminated */ + + *rdst = dst; /* and return the pointers */ + if (rsrc != NULL) /* if info wanted */ + *rsrc = &src[i]; + + if (*dst == '\0') { /* empty string */ + return 0; + } else if (src[i] == '\0') { /* completely used (1 argument) */ + return 1; + } else { /* 2 or more args, *rsrc is rest */ + return 2; + } +} + +int +get_integer(char *src, int *dst, char **rsrc) +{ + char *endptr; + + /* Converts str to a number. If one argument was found in + * str, 1 is returned and *dst is set to the value of the integer. + * If 2 or more arguments were presented, 2 is returned, + * *dst is set to the converted value and rsrc, if not null, points + * at the start of the next argument (whitespace skipped). + * Else 0 is returned and nothing else is valid. + */ + + if (src == NULL || *src == '\0') /* empty src */ + return(0); + + *dst = (int) strtol(src, &endptr, 0); + + /* skip over whitespace of second argument */ + while (isspace(*endptr)) + endptr++; + + if (rsrc) + *rsrc = endptr; + + if (isspace(*endptr)) { /* partial match, 2 or more arguments */ + return(2); + } else if (*endptr == '\0') { /* full match, 1 argument */ + return(1); + } else { /* invalid src, no match */ + return(0); + } +} + +/* functions to set the fields of the actions appropriately */ +int +set_device_field(action_t *action, char *args, char **trail) +{ + return(get_string(args, &action->name, trail)); +} +int +set_product_field(action_t *action, char *args, char **trail) +{ + return(get_integer(args, &action->product, trail)); +} +int +set_vendor_field(action_t *action, char *args, char **trail) +{ + return(get_integer(args, &action->vendor, trail)); +} +int +set_release_field(action_t *action, char *args, char **trail) +{ + return(get_integer(args, &action->release, trail)); +} +int +set_class_field(action_t *action, char *args, char **trail) +{ + return(get_integer(args, &action->class, trail)); +} +int +set_subclass_field(action_t *action, char *args, char **trail) +{ + return(get_integer(args, &action->subclass, trail)); +} +int +set_protocol_field(action_t *action, char *args, char **trail) +{ + return(get_integer(args, &action->protocol, trail)); +} +int +set_attach_field(action_t *action, char *args, char **trail) +{ + return(get_string(args, &action->attach, trail)); +} +int +set_detach_field(action_t *action, char *args, char **trail) +{ + return(get_string(args, &action->detach, trail)); +} + + +void +read_configuration(void) +{ + FILE *file; /* file descriptor */ + char *line; /* current line */ + char *linez; /* current line, NULL terminated */ + char *field; /* first part, the field name */ + char *args; /* second part, arguments */ + char *trail; /* remaining part after parsing, should be '' */ + int len; /* length of current line */ + int i,j; /* loop counters */ + action_t *action = NULL; /* current action */ + + file = fopen(configfile, "r"); + if (file == NULL) { + fprintf(stderr, "%s: Could not open for reading, %s\n", + configfile, strerror(errno)); + exit(2); + } + + for (lineno = 1; /* nop */;lineno++) { + + line = fgetln(file, &len); + if (line == NULL) { + if (feof(file)) /* EOF */ + break; + if (ferror(file)) { + fprintf(stderr, "%s:%d: Could not read, %s\n", + configfile, lineno, strerror(errno)); + exit(2); + } + } + + /* skip initial spaces */ + while (*line != '\0' && isspace(*line)) { + line++; + len--; + } + + if (len == 0) /* empty line */ + continue; + if (line[0] == '#') /* comment line */ + continue; + + /* make a NULL terminated copy of the string */ + linez = malloc(len+1); + if (linez == NULL) { + fprintf(stderr, "%s:%d: Out of memory\n", + configfile, lineno); + exit(2); + } + strncpy(linez, line, len); + linez[len+1] = '\0'; + + /* find the end of the current word (is field), that's the + * start of the arguments + */ + field = linez; + args = linez; + while (*args != '\0' && !isspace(*args)) + args++; + + /* If arguments is not the empty string, NULL terminate the + * field and move the argument pointer to the first character + * of the arguments. + * If arguments is the empty string field and arguments both + * are terminated (strlen(field) >= 0, strlen(arguments) == 0). + */ + if (*args != '\0') { + *args = '\0'; + args++; + } + + /* Skip initial spaces */ + while (*args != '\0' && isspace(*args)) + args++; + + /* Cut off trailing whitespace */ + for (i = 0, j = 0; args[i] != '\0'; i++) + if (!isspace(args[i])) + j = i+1; + args[j] = '\0'; + + /* We now have the field and the argument separated into + * two strings that are NULL terminated + */ + + /* If the field is 'device' we have to start a new action. */ + if (strcmp(field, "device") == 0) { + /* Allocate a new action and set defaults */ + action = malloc(sizeof(*action)); + if (action == NULL) { + fprintf(stderr, "%s:%d: Out of memory\n", + configfile, lineno); + exit(2); + } + memset(action, 0, sizeof(*action)); + action->product = WILDCARD_INT; + action->vendor = WILDCARD_INT; + action->release = WILDCARD_INT; + action->class = WILDCARD_INT; + action->subclass = WILDCARD_INT; + action->protocol = WILDCARD_INT; + + /* Add it to the end of the list to preserve order */ + STAILQ_INSERT_TAIL(&actions, action, next); + } + + if (action == NULL) { + line[len] = '\0'; /* XXX zero terminate */ + fprintf(stderr, "%s:%d: Doesn't start with 'device' " + "but '%s'\n", configfile, lineno, field); + exit(2); + } + + for (i = 0; config_fields[i].name ; i++) { + /* does the field name match? */ + if (strcmp(config_fields[i].name, field) == 0) { + /* execute corresponding set-field function */ + if ((config_fields[i].function)(action, args, + &trail) + != 1) { + fprintf(stderr,"%s:%d: " + "Syntax error in '%s'\n", + configfile, lineno, linez); + exit(2); + } + break; + } + } + if (config_fields[i].name == NULL) { /* Reached end of list*/ + fprintf(stderr, "%s:%d: Unknown field '%s'\n", + configfile, lineno, field); + exit(2); + } + } + + fclose(file); + + if (verbose >= 2) + print_actions(); +} + + +void +print_event(struct usb_event *event) +{ + int i; + struct timespec *timespec = &event->ue_time; + struct usb_device_info *devinfo = &event->ue_device; + + printf("%s: ", __progname); + for (i = 0; event_names[i].name != NULL; i++) { + if (event->ue_type == event_names[i].type) { + printf("%s event", event_names[i].name); + break; + } + } + if (event_names[i].name == NULL) + printf("unknown event %d", event->ue_type); + + printf(" at %ld.%09ld, %s, %s:\n", + timespec->tv_sec, timespec->tv_nsec, + devinfo->product, devinfo->vendor); + + printf(" prdct=0x%04x vndr=0x%04x rlse=0x%04x " + "clss=0x%04x subclss=0x%04x prtcl=0x%04x\n", + devinfo->productNo, devinfo->vendorNo, devinfo->releaseNo, + devinfo->class, devinfo->subclass, devinfo->protocol); +} + +void +print_action(action_t *action, int i) +{ + if (action == NULL) + return; + + printf("%s: action %d: %s\n", + __progname, i, + (action->name? action->name:"")); + if (action->product != WILDCARD_INT || + action->vendor != WILDCARD_INT || + action->release != WILDCARD_INT || + action->class != WILDCARD_INT || + action->subclass != WILDCARD_INT || + action->protocol != WILDCARD_INT) + printf(" "); + if (action->product != WILDCARD_INT) + printf(" prdct=0x%04x", action->product); + if (action->vendor != WILDCARD_INT) + printf(" vndr=0x%04x", action->vendor); + if (action->release != WILDCARD_INT) + printf(" rlse=0x%04x", action->release); + if (action->class != WILDCARD_INT) + printf(" clss=0x%04x", action->class); + if (action->subclass != WILDCARD_INT) + printf(" subclss=0x%04x", action->subclass); + if (action->protocol != WILDCARD_INT) + printf(" prtcl=0x%04x", action->protocol); + if (action->product != WILDCARD_INT || + action->vendor != WILDCARD_INT || + action->release != WILDCARD_INT || + action->class != WILDCARD_INT || + action->subclass != WILDCARD_INT || + action->protocol != WILDCARD_INT) + printf("\n"); + + if (action->attach != NULL) + printf("%s: attach='%s'\n", + __progname, action->attach); + if (action->detach != NULL) + printf("%s: detach='%s'\n", + __progname, action->detach); +} + +void +print_actions() +{ + int i = 0; + action_t *action; + + STAILQ_FOREACH(action, &actions, next) + print_action(action, ++i); + + printf("%s: %d action%s\n", __progname, i, (i == 1? "":"s")); +} + +action_t * +find_action(struct usb_device_info *devinfo) +{ + action_t *action; + + STAILQ_FOREACH(action, &actions, next) { + if ((action->product == WILDCARD_INT || + action->product == devinfo->productNo) && + (action->vendor == WILDCARD_INT || + action->vendor == devinfo->vendorNo) && + (action->release == WILDCARD_INT || + action->release == devinfo->releaseNo) && + (action->class == WILDCARD_INT || + action->class == devinfo->class) && + (action->subclass == WILDCARD_INT || + action->subclass == devinfo->subclass) && + (action->protocol == WILDCARD_INT || + action->protocol == devinfo->protocol)) { + /* found match !*/ + break; + } + } + + if (verbose) + printf("%s: Found action '%s' for %s, %s\n", + __progname, action->name, + devinfo->product, devinfo->vendor); + return(action); +} + +void +execute_command(char *cmd) +{ + pid_t pid; + int pstat; + struct sigaction ign, intact, quitact; + sigset_t newsigblock, oldsigblock; + int status; + int i; + + if (verbose) + printf("%s: Executing '%s'\n", __progname, cmd); + if (cmd == NULL) + return; + + /* The code below is directly taken from the system(3) call. + * Added to it is the closing of open file descriptors. + */ + /* + * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save + * existing signal dispositions. + */ + ign.sa_handler = SIG_IGN; + (void) sigemptyset(&ign.sa_mask); + ign.sa_flags = 0; + (void) sigaction(SIGINT, &ign, &intact); + (void) sigaction(SIGQUIT, &ign, &quitact); + (void) sigemptyset(&newsigblock); + (void) sigaddset(&newsigblock, SIGCHLD); + (void) sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); + pid = fork(); + if (pid == -1) { + fprintf(stderr, "%s: fork failed, %s\n", + __progname, strerror(errno)); + } else if (pid == 0) { + /* child here */ + + /* close all open file handles for USBDEV\d* devices */ + for (i = 0; i < ndevs; i++) + close(fds[i]); /* USBDEV\d+ */ + close(fd); /* USBDEV */ + + /* Restore original signal dispositions and exec the command. */ + (void) sigaction(SIGINT, &intact, NULL); + (void) sigaction(SIGQUIT, &quitact, NULL); + (void) sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + + execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL); + + exit(127); + } else { + /* parent here */ + do { + pid = waitpid(pid, &pstat, 0); + } while (pid == -1 && errno == EINTR); + } + (void) sigaction(SIGINT, &intact, NULL); + (void) sigaction(SIGQUIT, &quitact, NULL); + (void) sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + + if (pid == -1) { + fprintf(stderr, "%s: waitpid returned: %s\n", + __progname, strerror(errno)); + } else if (pid == 0) { + fprintf(stderr, "%s: waitpid returned 0 ?!\n", + __progname); + } else { + if (status == -1) { + fprintf(stderr, "%s: Could not start '%s'\n", + __progname, cmd); + } else if (status == 127) { + fprintf(stderr, "%s: Shell failed for '%s'\n", + __progname, cmd); + } else if (WIFEXITED(status)) { + fprintf(stderr, "%s: '%s' returned %d\n", + __progname, cmd, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + fprintf(stderr, "%s: '%s' caught signal %d\n", + __progname, cmd, WTERMSIG(status)); + } else if (verbose) { + printf("%s: '%s' is done\n", __progname, cmd); + } + } +} + +void +process_event_queue(int fd) +{ + struct usb_event event; + int len; + action_t *action; + + for (;;) { + len = read(fd, &event, sizeof(event)); + if (len == -1) { + if (errno == EWOULDBLOCK) { + /* no more events */ + break; + } else { + fprintf(stderr,"%s: Could not read event, %s\n", + __progname, strerror(errno)); + exit(1); + } + } + if (len == 0) + break; + if (len != sizeof(event)) { + fprintf(stderr, "partial read on %s\n", USBDEV); + exit(1); + } + + /* we seem to have gotten a valid event */ + + if (verbose) + print_event(&event); + + /* handle the event appropriately */ + switch (event.ue_type) { + case USB_EVENT_ATTACH: + case USB_EVENT_DETACH: + action = find_action(&event.ue_device); + if (action == NULL) + break; + else if (verbose >= 2) + print_action(action, 0); + + if (event.ue_type == USB_EVENT_ATTACH && action->attach) + execute_command(action->attach); + if (event.ue_type == USB_EVENT_DETACH && action->detach) + execute_command(action->detach); + + break; + default: + printf("Unknown USB event %d\n", event.ue_type); + } + } +} + + +int main(int argc, char **argv) { int error, i; int ch; /* getopt option */ extern char *optarg; /* from getopt */ extern int optind; /* from getopt */ - char *devs[MAXUSBDEV]; /* device names */ - int ndevs = 0; /* number of devices found */ - int verbose = 0; /* print message on what it is doing */ int debug = 0; /* print debugging output */ - int explore = 0; /* don't do only explore */ + int explore_once = 0; /* don't do only explore */ + int handle_events = 1; /* do handle the event queue */ int maxfd; /* maximum fd in use */ char buf[50]; /* for creation of the filename */ - int fds[MAXUSBDEV]; /* open filedescriptors */ - fd_set fdset; - int itimo = TIMEOUT; /* timeout for select */ - struct timeval timo; + fd_set r,w; + int itimeout = TIMEOUT; /* timeout for select */ + struct timeval tv; - while ((ch = getopt(argc, argv, "def:t:v")) != -1) { + while ((ch = getopt(argc, argv, "c:def:nt:v")) != -1) { switch(ch) { + case 'c': + configfile = strdup(optarg); + break; case 'd': debug++; break; case 'e': - explore++; + explore_once = 1; break; case 'f': if (ndevs < MAXUSBDEV) devs[ndevs++] = optarg; break; + case 'n': + handle_events = 0; + break; case 't': - itimo = atoi(optarg); + itimeout = atoi(optarg); break; case 'v': verbose++; break; case '?': default: usage(); } } argc -= optind; argv += optind; - /* open all the files /dev/usb\d+ or specified with -f */ maxfd = 0; if (ndevs == 0) { + /* open all the USBDEVS\d+ devices */ for (i = 0; i < MAXUSBDEV; i++) { sprintf(buf, "%s%d", USBDEV, i); fds[ndevs] = open(buf, O_RDWR); if (fds[ndevs] >= 0) { devs[ndevs] = strdup(buf); if (verbose) - printf("%s: opening %s\n", + printf("%s: opened %s\n", __progname, devs[ndevs]); if (fds[ndevs] > maxfd) maxfd = fds[ndevs]; ndevs++; } } } else { + /* open all the files specified with -f */ for (i = 0; i < ndevs; i++) { fds[i] = open(devs[i], O_RDWR); - if (fds[i] < 0) - err(1, "%s", devs[i]); - else if (fds[i] > maxfd) - maxfd = fds[i]; + if (fds[i] < 0) { + fprintf(stderr, "%s: Could not open %s, %s\n", + __progname, devs[i], strerror(errno)); + exit(1); + } else { + if (verbose) + printf("%s: opened %s\n", + __progname, devs[i]); + if (fds[i] > maxfd) + maxfd = fds[i]; + } } } + if (ndevs == 0) { fprintf(stderr, "No USB host controllers found\n"); exit(1); } /* Do the explore once and exit */ - if (explore) { + if (explore_once) { for (i = 0; i < ndevs; i++) { error = ioctl(fds[i], USB_DISCOVER); - if (error < 0) - err(1, "USB_DISCOVER"); + if (error < 0) { + fprintf(stderr, "%s: ioctl(%s, USB_DISCOVER) " + "failed, %s\n", + __progname, devs[i], strerror(errno)); + exit(1); + } } exit(0); } + if (handle_events) { + if (verbose) + printf("%s: reading configuration file %s\n", + __progname, configfile); + read_configuration(); + + fd = open(USBDEV, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "%s: Could not open %s, %s\n", + __progname, USBDEV, strerror(errno)); + exit(1); + } + if (verbose) + printf("%s: opened %s\n", __progname, USBDEV); + if (fd > maxfd) + maxfd = fd; + + process_event_queue(fd); /* dequeue the initial events */ + } + /* move to the background */ if (!debug) daemon(0, 0); /* start select on all the open file descriptors */ for (;;) { - FD_ZERO(&fdset); + FD_ZERO(&r); + FD_ZERO(&w); + if (handle_events) + FD_SET(fd, &r); /* device USBDEV */ for (i = 0; i < ndevs; i++) - FD_SET(fds[i], &fdset); - timo.tv_usec = 0; - timo.tv_sec = itimo; - error = select(maxfd+1, 0, &fdset, 0, itimo ? &timo : 0); - if (error < 0) - warn("select failed\n"); - for (i = 0; i < ndevs; i++) - if (error == 0 || FD_ISSET(fds[i], &fdset)) { - if (verbose) + FD_SET(fds[i], &w); /* device USBDEV\d+ */ + tv.tv_usec = 0; + tv.tv_sec = itimeout; + error = select(maxfd+1, &r, &w, 0, itimeout ? &tv : 0); + if (error < 0) { + fprintf(stderr, "%s: Select failed, %s\n", + __progname, strerror(errno)); + exit(1); + } + + /* USBDEV\d+ devices have signaled change, do a usb_discover */ + for (i = 0; i < ndevs; i++) { + if (error == 0 || FD_ISSET(fds[i], &w)) { + if (verbose >= 2) printf("%s: doing %sdiscovery on %s\n", __progname, (error? "":"timeout "), devs[i]); - if (ioctl(fds[i], USB_DISCOVER) < 0) - err(1, "USB_DISCOVER"); + if (ioctl(fds[i], USB_DISCOVER) < 0) { + fprintf(stderr, "%s: ioctl(%s, " + "USB_DISCOVER) failed, %s\n", + __progname, devs[i], + strerror(errno)); + exit(1); + } } + } + + /* check the event queue */ + if (handle_events && (FD_ISSET(fd, &r) || error == 0)) { + if (verbose >= 2) + printf("%s: processing event queue %son %s\n", + __progname, + (error? "":"due to timeout "), USBDEV); + process_event_queue(fd); + } } } Index: head/usr.sbin/usbd/usbd.conf.5 =================================================================== --- head/usr.sbin/usbd/usbd.conf.5 (nonexistent) +++ head/usr.sbin/usbd/usbd.conf.5 (revision 53517) @@ -0,0 +1,128 @@ +.\" +.\" Copyright (c) 1999 Nick Hibma. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.\" Many parts of this manual have been snarfed from the pccard.conf (5) man +.\" page, copyright by Andrew McRae. +.\" +.Dd November 19, 1999 +.Dt USBD.CONF 5 +.Os FreeBSD +.Sh NAME +.Nm usbd.conf +.Nd +.Xr usbd 8 +configuration file +.Sh DESCRIPTION +The +.Nm +file is the configuration file for the +.Xr usbd 8 +daemon. It provides information to allow execution of userland commands +on events reported by the +.Xr usb 4 +subsystem in the kernel. Currently the only events are device attach and +detach, but could in the future be extended to include power management +functions. +.Pp +The configuration file consists of a sorted list of entries. Each entry +describes a set of criteria commands. When an event occurs, the criteria +are checked and if met, the commands for that event are executed through +a shell. The list is sorted and scanned from top to bottom. The first +matching entry is used for an event. +.Pp +Each entry contains a number of fields. There are 3 types of fields: +descriptive fields, selection criteria and commands to execute on +events. The field name is case sensitive and should be all lower case. +Each field can have one or more arguments. +.Pp +The following fields are available: +.Bl -tag -width devicename\ +.It device Ar string +Start a new entry. +.Ar string +is an arbitrary string used for pretty printing. +.It product Ar id +Product Id +.It vendor Ar id +Vendor Id +.It release Ar id +Release Id, also called revision Id sometimes. +.It class Ar id +Device Class +.It subclass Ar id +Device Subclass +.It protocol Ar id +Device Protocol +.It devicenames Ar string +Device name, for example umass2, or ums0. +.El +.Pp +String arguments may be quoted. If a string argument contains a space or +tab character it needs to be enclosed in single or double quotes. If an +argument contains a single or double quote, that quote needs to be +enclosed in double or single quotes respectively. See below for +examples. +.Pp +Numeric arguments can either be specified in decimal (42), octal (052) or +hexadeximal (0x2a). +.Pp +The values for the fields +.Li product , vendor , release, class , subclass +and +.Li protocol +can be retrieved by killing the +.Nm usbd +daemon and running it with the +.Fl -d +and +.Fl -v +flags. +.Pp +.Sh EXAMPLES +A sample entry to rescan the SCSI bus on connection of a +.Tn "Iomega USB Zip Drive" . +.Bd -literal + device "USB Zip drive" + product 0x0001 + vendor 0x059b + release 0x0100 + attach "/usr/bin/camcontrol rescan bus 0" +.Ed +.Sh FILES +.Bl -tag -width /etc/pccard.conf -compact +.It Pa /etc/usbd.conf +The +.Nm usbd +configuration file. +.El +.Sh SEE ALSO +.Xr usb 4 , +.Xr usbd 8 , +.Xr usbdevs 8 +.Sh AUTHORS +The man page for the usbd configuration file was written by +.An Nick Hibma Aq n_hibma@freebsd.org . Property changes on: head/usr.sbin/usbd/usbd.conf.5 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property