945 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			945 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <getopt.h>
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| #include <lib.h>
 | |
| #include <sys/stat.h>
 | |
| #include <dirent.h>
 | |
| #include <assert.h>
 | |
| #include <signal.h>
 | |
| #include <minix/dmap.h>
 | |
| #include "usb_driver.h"
 | |
| #include "proto.h"
 | |
| 
 | |
| #define SERVICE_BINARY "/bin/service"
 | |
| 
 | |
| 
 | |
| #define DEVMAN_TYPE_NAME "dev_type"
 | |
| #define PATH_LEN 256
 | |
| #define INVAL_MAJOR -1
 | |
| #define MAX_CONFIG_DIRS 4
 | |
| 
 | |
| static void main_loop();
 | |
| static void handle_event();
 | |
| static void cleanup();
 | |
| static void parse_config();
 | |
| static void display_usage();
 | |
| static enum dev_type determine_type(char *path);
 | |
| static int get_major();
 | |
| static void create_pid_file();
 | |
| static void put_major(int major);
 | |
| static struct devmand_usb_driver* match_usb_driver(struct usb_device_id *id);
 | |
| static struct devmand_driver_instance *find_instance(int dev_id);
 | |
| 
 | |
| #define dbg(fmt, ... ) \
 | |
| 	if (args.verbose) \
 | |
| 	printf("%8s:%4d: %13s()| "fmt"\n", __FILE__, __LINE__, __func__,  ##__VA_ARGS__ )
 | |
| 
 | |
| static LIST_HEAD(usb_driver_head, devmand_usb_driver) drivers =
 | |
|     LIST_HEAD_INITIALIZER(drivers);
 | |
| static LIST_HEAD(usb_driver_inst_head, devmand_driver_instance) instances =
 | |
|     LIST_HEAD_INITIALIZER(instances);
 | |
| 
 | |
| 
 | |
| static int _run = 1;
 | |
| struct global_args {
 | |
| 	char *path;
 | |
| 	char *config_dirs[MAX_CONFIG_DIRS];
 | |
| 	int config_dir_count ;
 | |
| 	int major_offset;
 | |
| 	int verbose;
 | |
| 	int check_config;
 | |
| };
 | |
| 
 | |
| enum dev_type {
 | |
| 	DEV_TYPE_USB_DEVICE,
 | |
| 	DEV_TYPE_USB_INTF,
 | |
| 	DEV_TYPE_UNKOWN
 | |
| };
 | |
| 
 | |
| extern FILE *yyin;
 | |
| 
 | |
| static struct global_args args = {
 | |
| 	.path = NULL,
 | |
| 	.config_dirs = {NULL,NULL,NULL,NULL},
 | |
| 	.config_dir_count = 0,
 | |
| 	.major_offset = USB_BASE_MAJOR,
 | |
| 	.verbose = 0,
 | |
| 	.check_config = 0};
 | |
| 
 | |
| static struct option options[] =
 | |
| {
 | |
| 	{"dir"   ,    required_argument, NULL, 'd'},
 | |
| 	{"path",      required_argument, NULL, 'p'},
 | |
| 	{"verbose",    required_argument, NULL, 'v'},
 | |
| 	{"check-config", no_argument,       NULL, 'x'},
 | |
| 	{0,0,0,0} /* terminating entry */
 | |
| };
 | |
| 
 | |
| static char major_bitmap[16]; /* can store up to 128 major number states */
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *             run_upscript                                                  *
 | |
|  *===========================================================================*/
 | |
| int run_upscript(struct devmand_driver_instance *inst)
 | |
| {
 | |
| 	char cmdl[1024];
 | |
| 	cmdl[0] = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	assert(inst->drv->upscript);
 | |
| 	assert(inst->label);
 | |
| 
 | |
| 	snprintf(cmdl, 1024, "%s up %s %d %d",
 | |
| 	    inst->drv->upscript, inst->label, inst->major, inst->dev_id);
 | |
| 	dbg("Running Upscript:  \"%s\"", cmdl);
 | |
| 	ret = system(cmdl);
 | |
| 	if (ret != 0) {
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *             run_cleanscript                                               *
 | |
|  *===========================================================================*/
 | |
| int run_cleanscript(struct devmand_usb_driver *drv)
 | |
| {
 | |
| 	char cmdl[1024];
 | |
| 	cmdl[0] = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	assert(drv->upscript);
 | |
| 	assert(drv->devprefix);
 | |
| 
 | |
| 	snprintf(cmdl, 1024, "%s clean %s ",
 | |
| 		drv->upscript, drv->devprefix);
 | |
| 	dbg("Running Upscript:  \"%s\"", cmdl);
 | |
| 	ret = system(cmdl);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *             run_downscript                                                *
 | |
|  *===========================================================================*/
 | |
| int run_downscript(struct devmand_driver_instance *inst)
 | |
| {
 | |
| 	char cmdl[1024];
 | |
| 	cmdl[0] = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	assert(inst->drv->downscript);
 | |
| 	assert(inst->label);
 | |
| 
 | |
| 	snprintf(cmdl, 1024, "%s down %s %d",
 | |
| 	    inst->drv->downscript, inst->label, inst->major);
 | |
| 
 | |
| 	dbg("Running Upscript:  \"%s\"", cmdl);
 | |
| 
 | |
| 	ret = system(cmdl);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *             stop_driver                                                   *
 | |
|  *===========================================================================*/
 | |
| int stop_driver(struct devmand_driver_instance *inst)
 | |
| {
 | |
| 	char cmdl[1024];
 | |
| 	cmdl[0] = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	assert(inst->label);
 | |
| 
 | |
| 	snprintf(cmdl, 1024, "%s down %s %d",
 | |
| 	    SERVICE_BINARY, inst->label, inst->dev_id);
 | |
| 	dbg("executing service: \"%s\"", cmdl);
 | |
| 	ret = system(cmdl);
 | |
| 	if (ret != 0)
 | |
| 	{
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 	printf("Stopped driver %s with label %s for device %d.\n",
 | |
| 		inst->drv->binary, inst->label, inst->dev_id);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *             start_driver                                                  *
 | |
|  *===========================================================================*/
 | |
| int start_driver(struct devmand_driver_instance *inst)
 | |
| {
 | |
| 	char cmdl[1024];
 | |
| 	cmdl[0] = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	/* generate label */
 | |
| 	ret = snprintf(inst->label, 32,  "%s%d", inst->drv->devprefix,
 | |
| 		inst->dev_id);
 | |
| 	if (ret < 0 || ret > DEVMAND_DRIVER_LABEL_LEN) {
 | |
| 		dbg("label too long");
 | |
| 		return ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	assert(inst->drv->binary);
 | |
| 	assert(inst->label);
 | |
| 
 | |
| 	snprintf(cmdl, 1024, "%s up %s  -major %d -devid %d -label %s",
 | |
| 	    SERVICE_BINARY, inst->drv->binary, inst->major, inst->dev_id,
 | |
| 		inst->label);
 | |
| 	dbg("executing service: \"%s\"", cmdl);
 | |
| 
 | |
| 	ret = system(cmdl);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	printf("Started driver %s with label %s for device %d.\n",
 | |
| 		inst->drv->binary, inst->label, inst->dev_id);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *             find_instance                                                 *
 | |
|  *===========================================================================*/
 | |
| static struct devmand_driver_instance *
 | |
| find_instance(int dev_id)
 | |
| {
 | |
| 	struct devmand_driver_instance *inst;
 | |
| 
 | |
| 	LIST_FOREACH(inst, &instances, list) {
 | |
| 		if (inst->dev_id == dev_id) {
 | |
| 			return inst;
 | |
| 		}
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *              match_usb_driver                                             *
 | |
|  *===========================================================================*/
 | |
| static int
 | |
| match_usb_id(struct devmand_usb_match_id *mid, struct usb_device_id *id)
 | |
| {
 | |
| 	int res = 1;
 | |
| 	unsigned long match = mid->match_flags;
 | |
| 	struct usb_device_id *_id = &mid->match_id;
 | |
| 
 | |
| 	if (match & USB_MATCH_ID_VENDOR)
 | |
| 		if (id->idVendor != _id->idVendor) res = 0;
 | |
| 	if (match & USB_MATCH_ID_PRODUCT)
 | |
| 		if (id->idProduct != _id->idProduct) res = 0;
 | |
| 	if (match & USB_MATCH_BCD_DEVICE)
 | |
| 		if (id->bcdDevice != _id->bcdDevice) res = 0;
 | |
| 	if (match & USB_MATCH_DEVICE_PROTOCOL)
 | |
| 		if (id->bDeviceProtocol != _id->bDeviceProtocol) res = 0;
 | |
| 	if (match & USB_MATCH_DEVICE_SUBCLASS)
 | |
| 		if (id->bDeviceSubClass != _id->bDeviceSubClass) res = 0;
 | |
| 	if (match & USB_MATCH_DEVICE_PROTOCOL)
 | |
| 		if (id->bDeviceProtocol != _id->bDeviceProtocol) res = 0;
 | |
| 	if (match & USB_MATCH_INTERFACE_CLASS)
 | |
| 		if (id->bInterfaceClass != _id->bInterfaceClass) res = 0;
 | |
| 	if (match & USB_MATCH_INTERFACE_SUBCLASS)
 | |
| 		if (id->bInterfaceSubClass != _id->bInterfaceSubClass) res = 0;
 | |
| 	if (match & USB_MATCH_INTERFACE_PROTOCOL)
 | |
| 		if (id->bInterfaceProtocol != _id->bInterfaceProtocol) res = 0;
 | |
| 
 | |
| 	if (match == 0UL) {
 | |
| 		res = 0;
 | |
| 	}
 | |
| 
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *              match_usb_driver                                             *
 | |
|  *===========================================================================*/
 | |
| static struct devmand_usb_driver*
 | |
| match_usb_driver(struct usb_device_id *id)
 | |
| {
 | |
| 	struct devmand_usb_driver *driver;
 | |
| 	struct devmand_usb_match_id *mid;
 | |
| 
 | |
| 	LIST_FOREACH(driver, &drivers, list) {
 | |
| 		LIST_FOREACH(mid, &driver->ids, list) {
 | |
| 			if (match_usb_id(mid, id)) {
 | |
| 				return driver;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *              add_usb_match_id                                             *
 | |
|  *===========================================================================*/
 | |
| struct devmand_usb_driver * add_usb_driver(char *name)
 | |
| {
 | |
| 	struct devmand_usb_driver *udrv = (struct devmand_usb_driver*)
 | |
| 	    malloc(sizeof(struct devmand_usb_driver));
 | |
| 
 | |
| 	LIST_INSERT_HEAD(&drivers, udrv, list);
 | |
| 	LIST_INIT(&udrv->ids);
 | |
| 
 | |
| 	udrv->name = name;
 | |
| 	return udrv;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *              add_usb_match_id                                             *
 | |
|  *===========================================================================*/
 | |
| struct devmand_usb_match_id *
 | |
| add_usb_match_id
 | |
| (struct devmand_usb_driver *drv)
 | |
| {
 | |
| 	struct devmand_usb_match_id *id = (struct devmand_usb_match_id*)
 | |
| 	    malloc(sizeof(struct devmand_usb_match_id));
 | |
| 
 | |
| 	memset(id, 0, sizeof(struct devmand_usb_match_id));
 | |
| 
 | |
| 	LIST_INSERT_HEAD(&drv->ids, id, list);
 | |
| 
 | |
| 	return id;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           parse_config                                                    *
 | |
|  *===========================================================================*/
 | |
| static void parse_config()
 | |
| {
 | |
| 	int i, status, error;
 | |
| 	struct stat stats;
 | |
| 	char * dirname;
 | |
| 
 | |
| 	DIR * dir;
 | |
| 	struct dirent entry;
 | |
| 	struct dirent *result;
 | |
| 	char config_file[PATH_MAX];
 | |
| 
 | |
| 	dbg("Parsing configuration directories... ");
 | |
| 	/* Next parse the configuration directories */
 | |
| 	for(i=0; i < args.config_dir_count; i++){
 | |
| 		dirname = args.config_dirs[i];
 | |
| 		dbg("Parsing config dir %s ", dirname);
 | |
| 		status = stat(dirname,&stats);
 | |
| 		if (status == -1){
 | |
| 			error = errno;
 | |
| 			dbg("Failed to read directory '%s':%s (skipping) \n", 
 | |
| 			    dirname,strerror(error));
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!S_ISDIR(stats.st_mode)){
 | |
| 			dbg("Parse configuration skipping %s "
 | |
| 			    "(not a directory) \n",dirname);
 | |
| 			continue;
 | |
| 		}
 | |
| 		dir = opendir(dirname);
 | |
| 		if (dir == NULL){
 | |
| 			error = errno;
 | |
| 			dbg("Parse configuration failed to read dir '%s'"
 | |
| 			    "(skipping) :%s\n",dirname, strerror(error));
 | |
| 			continue;
 | |
| 		}
 | |
| 		while( (status = readdir_r(dir,&entry,&result)) == 0 ){
 | |
| 			if (result == NULL){ /* last entry */ 
 | |
| 				closedir(dir);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/* concatenate dir and file name to open it */
 | |
| 			snprintf(config_file,PATH_MAX, "%s/%s",
 | |
| 				 dirname,entry.d_name);
 | |
| 			status = stat(config_file, &stats);
 | |
| 			if (status == -1){ 
 | |
| 				error = errno;
 | |
| 				dbg("Parse configuration Failed to stat file "
 | |
| 				    "'%s': %s (skipping)\n", config_file,
 | |
| 				    strerror(error));
 | |
| 			}
 | |
| 			if (S_ISREG(stats.st_mode)){
 | |
| 				dbg("Parsing file %s",config_file);
 | |
| 				yyin = fopen(config_file, "r");
 | |
| 
 | |
| 				if (yyin == NULL) {
 | |
| 					dbg("Can not open config file:" 
 | |
| 				 	       " %d.\n", errno);
 | |
| 				}
 | |
| 				yyparse();
 | |
| 				dbg("Done.");
 | |
| 				fclose(yyin);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	dbg("Parsing configuration directories done... ");
 | |
| 
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           cleanup                                                        *
 | |
|  *===========================================================================*/
 | |
| static void cleanup() {
 | |
| 	struct devmand_driver_instance *inst;
 | |
| 	/* destroy fifo */
 | |
| 	dbg("cleaning up... ");
 | |
| 	/* quit all running drivers */
 | |
| 	LIST_FOREACH(inst, &instances, list) {
 | |
| 		dbg("stopping driver %s", inst->label);
 | |
| 		if(inst->drv->downscript) {
 | |
| 			run_downscript (inst);
 | |
| 		}
 | |
| 		stop_driver(inst);
 | |
| 	}
 | |
| 	unlink("/var/run/devmand.pid");
 | |
| }
 | |
| 
 | |
| static void sig_int(int sig) {
 | |
| 	dbg("devman: Received SIGINT... cleaning up.");
 | |
| 	_run = 0;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           create_pid_file                                                 *
 | |
|  *===========================================================================*/
 | |
| void create_pid_file()
 | |
| {
 | |
| 	FILE *fd;
 | |
| 
 | |
| 	fd = fopen("/var/run/devmand.pid", "r");
 | |
| 	if(fd) {
 | |
| 		fprintf(stderr, "devmand: /var/run/devmand.pid exists... "
 | |
| 		                "another devmand running?\n");
 | |
| 		fclose(fd);
 | |
| 		exit(1);
 | |
| 	} else {
 | |
| 		fd = fopen("/var/run/devmand.pid","w");
 | |
| 		fprintf(fd, "%d", getpid());
 | |
| 		fclose(fd);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           main                                                            *
 | |
|  *===========================================================================*/
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
| 	int opt, optindex;
 | |
| 	struct devmand_usb_driver *driver;
 | |
| 
 | |
| 
 | |
| 	/* get command line arguments */
 | |
| 	while ((opt = getopt_long(argc, argv, "d:p:vxh?", options, &optindex))
 | |
| 			!= -1) {
 | |
| 		switch (opt) {
 | |
| 			case 'd':/* config directory */
 | |
| 				if (args.config_dir_count >= MAX_CONFIG_DIRS){
 | |
| 				 	fprintf(stderr,"Parse arguments: Maximum" 
 | |
| 					        " of %i configuration directories"
 | |
| 						" reached skipping directory '%s'\n" 
 | |
| 						, MAX_CONFIG_DIRS, optarg);
 | |
| 				 	break;
 | |
| 				}
 | |
| 				args.config_dirs[args.config_dir_count] = optarg;
 | |
| 				args.config_dir_count++;
 | |
| 				break;
 | |
| 			case 'p': /* sysfs path */
 | |
| 				args.path = optarg;
 | |
| 				break;
 | |
| 			case 'v': /* verbose */
 | |
| 				args.verbose = 1;
 | |
| 				break;
 | |
| 			case 'x': /* check config */
 | |
| 				args.check_config = 1;
 | |
| 				break;
 | |
| 			case 'h': /* help */
 | |
| 			case '?': /* help */
 | |
| 			default:
 | |
| 				display_usage(argv[0]);
 | |
| 				return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	/* is path set? */
 | |
| 	if (args.path == NULL) {
 | |
| 		args.path = "/sys/";
 | |
| 	}
 | |
| 
 | |
| 	/* is the configuration directory set? */
 | |
| 	if (args.config_dir_count == 0) {
 | |
| 		dbg("Using default configuration directory");
 | |
| 		args.config_dirs[0] = "/etc/devmand";
 | |
| 		args.config_dir_count = 1;
 | |
| 	}
 | |
| 
 | |
| 	/* If we only check the configuration run and exit imediately */
 | |
| 	if (args.check_config == 1){
 | |
| 		fprintf(stdout, "Only parsing configuration\n");
 | |
| 		parse_config();
 | |
| 		exit(0);
 | |
| 	}
 | |
| 
 | |
| 	create_pid_file();
 | |
| 
 | |
| 	parse_config();
 | |
| 	LIST_FOREACH(driver, &drivers, list) {
 | |
| 		if (driver->upscript) {
 | |
| 			run_cleanscript(driver);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	signal(SIGINT, sig_int);
 | |
| 
 | |
| 	main_loop();
 | |
| 
 | |
| 	cleanup();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           determine_type                                                  *
 | |
|  *===========================================================================*/
 | |
| static enum dev_type determine_type (char *path)
 | |
| {
 | |
| 	FILE * fd;
 | |
| 	char *mypath;
 | |
| 	char buf[256];
 | |
| 	int res;
 | |
| 
 | |
| 	mypath = (char *) calloc(1, strlen(path)+strlen(DEVMAN_TYPE_NAME)+1);
 | |
| 
 | |
| 	if (mypath == NULL) {
 | |
| 		fprintf(stderr, "ERROR: out of mem\n");
 | |
| 		cleanup();
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	strcat(mypath, path);
 | |
| 	strcat(mypath, DEVMAN_TYPE_NAME);
 | |
| 
 | |
| 	fd = fopen(mypath, "r");
 | |
| 	free(mypath);
 | |
| 
 | |
| 	if (fd == NULL) {
 | |
| 		fprintf(stderr, "WARN: could not open %s\n", mypath);
 | |
| 		return DEV_TYPE_UNKOWN;
 | |
| 	}
 | |
| 
 | |
| 	res = fscanf(fd , "%255s\n", buf);
 | |
| 	fclose(fd);
 | |
| 
 | |
| 	if (res != 1) {
 | |
| 		fprintf(stderr, "WARN: could not parse %s\n", mypath);
 | |
| 		return DEV_TYPE_UNKOWN;
 | |
| 	}
 | |
| 
 | |
| 	if (strcmp(buf, "USB_DEV") == 0) {
 | |
| 		return DEV_TYPE_USB_DEVICE;
 | |
| 	} else if (strcmp(buf, "USB_INTF") == 0) {
 | |
| 		return DEV_TYPE_USB_INTF;
 | |
| 	}
 | |
| 
 | |
| 	return  DEV_TYPE_UNKOWN;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           read_hex_uint                                                   *
 | |
|  *===========================================================================*/
 | |
| static int read_hex_uint(char *base_path, char *name, unsigned int* val )
 | |
| {
 | |
| 	char my_path[PATH_LEN];
 | |
| 	FILE *fd;
 | |
| 	memset(my_path,0,PATH_LEN);
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	strcat(my_path, base_path);
 | |
| 	strcat(my_path, name);
 | |
| 
 | |
| 	fd = fopen(my_path, "r");
 | |
| 
 | |
| 	if (fd == NULL) {
 | |
| 		fprintf(stderr, "WARN: could not open %s\n", my_path);
 | |
| 		return EEXIST;
 | |
| 	} else	if (fscanf(fd, "0x%x\n", val ) != 1) {
 | |
| 		fprintf(stderr, "WARN: could not parse %s\n", my_path);
 | |
| 		ret = EINVAL;
 | |
| 	}
 | |
| 	fclose(fd);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *               get_major                                                   *
 | |
|  *===========================================================================*/
 | |
| static int get_major() {
 | |
| 	int i, ret = args.major_offset;
 | |
| 
 | |
| 	for (i=0; i < 16; i++) {
 | |
| 		int j;
 | |
| 		for (j = 0; j < 8; j++ ) {
 | |
| 			if ((major_bitmap[i] & (1 << j))) {
 | |
| 				major_bitmap[i] &= !(1 << j);
 | |
| 				return ret;
 | |
| 			}
 | |
| 			ret++;
 | |
| 		}
 | |
| 	}
 | |
| 	return INVAL_MAJOR;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *               put_major                                                   *
 | |
|  *===========================================================================*/
 | |
| static void put_major(int major) {
 | |
| 	int i;
 | |
| 	major -= args.major_offset;
 | |
| 	assert(major >= 0);
 | |
| 
 | |
| 	for (i=0; i < 16; i++) {
 | |
| 		int j;
 | |
| 		for (j = 0; j < 8; j++ ) {
 | |
| 			if (major==0) {
 | |
| 				assert(!(major_bitmap[i] & (1 <<j)));
 | |
| 				major_bitmap[i] |= (1 << j);
 | |
| 				return;
 | |
| 			}
 | |
| 			major--;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *          generate_usb_device_id                                           *
 | |
|  *===========================================================================*/
 | |
| static struct usb_device_id *
 | |
| generate_usb_device_id(char * path, int is_interface)
 | |
| {
 | |
| 	struct usb_device_id *ret;
 | |
| 	int res;
 | |
| 	unsigned int val;
 | |
| 
 | |
| 	ret = (struct usb_device_id *)
 | |
| 	    calloc(1,sizeof (struct usb_device_id));
 | |
| 
 | |
| 	if (is_interface) {
 | |
| 
 | |
| 		res = read_hex_uint(path, "../idVendor", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->idVendor = val;
 | |
| 
 | |
| 		res = read_hex_uint(path, "../idProduct", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->idProduct = val;
 | |
| #if 0
 | |
| 		res = read_hex_uint(path, "../bcdDevice", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->bcdDevice = val;
 | |
| #endif
 | |
| 		res = read_hex_uint(path, "../bDeviceClass", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->bDeviceClass = val;
 | |
| 
 | |
| 		res = read_hex_uint(path, "../bDeviceSubClass", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->bDeviceSubClass = val;
 | |
| 
 | |
| 		res = read_hex_uint(path, "../bDeviceProtocol", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->bDeviceProtocol = val;
 | |
| 
 | |
| 		res = read_hex_uint(path, "/bInterfaceClass", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->bInterfaceClass = val;
 | |
| 
 | |
| 		res = read_hex_uint(path, "/bInterfaceSubClass", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->bInterfaceSubClass = val;
 | |
| 
 | |
| 		res = read_hex_uint(path, "/bInterfaceProtocol", &val);
 | |
| 		if (res) goto err;
 | |
| 		ret->bInterfaceProtocol = val;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| err:
 | |
| 	free(ret);
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *            usb_intf_add_even                                              *
 | |
|  *===========================================================================*/
 | |
| static void usb_intf_add_event(char *path, int dev_id)
 | |
| {
 | |
| 	struct usb_device_id *id;
 | |
| 	struct devmand_usb_driver *drv;
 | |
| 	struct devmand_driver_instance *drv_inst;
 | |
| 	int major, ret;
 | |
| 
 | |
| 	/* generate usb_match_id */
 | |
| 	id = generate_usb_device_id(path,TRUE);
 | |
| 	if (id == NULL) {
 | |
| 		fprintf(stderr, "WARN: could not create usb_device id...\n"
 | |
| 		                "      ommiting event\n");
 | |
| 		free(id);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* find suitable driver */
 | |
| 	drv = match_usb_driver(id);
 | |
| 	free (id);
 | |
| 
 | |
| 	if (drv == NULL) {
 | |
| 		dbg("INFO: could not find a suitable driver for %s", path);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* create instance */
 | |
| 	drv_inst = (struct devmand_driver_instance *)
 | |
| 	    calloc(1,sizeof(struct devmand_driver_instance));
 | |
| 
 | |
| 	if (drv_inst == NULL) {
 | |
| 		fprintf(stderr, "ERROR: out of memory");
 | |
| 		return; /* maybe better quit here. */
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	/* allocate inode number, if device files needed */
 | |
| 	major = get_major();
 | |
| 	if (major == INVAL_MAJOR) {
 | |
| 		fprintf(stderr, "WARN: ran out of major numbers\n"
 | |
| 		                "      cannot start driver %s for %s\n",
 | |
| 							   drv->name, path);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	drv_inst->major  = major;
 | |
| 	drv_inst->drv    = drv;
 | |
| 	drv_inst->dev_id = dev_id;
 | |
| 
 | |
| 
 | |
| 	/* start driver (invoke service) */
 | |
| 	start_driver(drv_inst);
 | |
| 
 | |
| 	/*
 | |
| 	 * run the up action
 | |
| 	 *
 | |
| 	 * An up action can be any executable. Before running it devmand
 | |
| 	 * will set certain environment variables so the script can configure
 | |
| 	 * the device (or generate device files, etc). See up_action() for that.
 | |
| 	 */
 | |
| 	if (drv->upscript) {
 | |
| 		ret = run_upscript(drv_inst);
 | |
| 		if (ret) {
 | |
| 			stop_driver(drv_inst);
 | |
| 			fprintf(stderr, "devmand: warning, could not run up_action\n");
 | |
| 			free(drv_inst);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	LIST_INSERT_HEAD(&instances,drv_inst,list);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *            usb_intf_remove_event                                          *
 | |
|  *===========================================================================*/
 | |
| static void usb_intf_remove_event(char *path, int dev_id)
 | |
| {
 | |
| 	struct devmand_driver_instance *inst;
 | |
| 	struct devmand_usb_driver *drv;
 | |
| 	int ret;
 | |
| 
 | |
| 	/* find the driver instance */
 | |
| 	inst = find_instance(dev_id);
 | |
| 
 | |
| 	if (inst == NULL) {
 | |
| 		dbg("No driver running for id: %d", dev_id);
 | |
| 		return;
 | |
| 	}
 | |
| 	drv = inst->drv;
 | |
| 
 | |
| 	/* run the down script */
 | |
| 	if (drv->downscript) {
 | |
| 		ret = run_downscript(inst);
 | |
| 		if (ret) {
 | |
| 			fprintf(stderr, "WARN: error running up_action");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* stop the driver */
 | |
| 	stop_driver(inst);
 | |
| 
 | |
| 	/* free major */
 | |
| 	put_major(inst->major);
 | |
| 
 | |
| 	/* free instance */
 | |
| 	LIST_REMOVE(inst,list);
 | |
| 	free(inst);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           handle_event                                                    *
 | |
|  *===========================================================================*/
 | |
| static void handle_event(char *event)
 | |
| {
 | |
| 	enum dev_type type;
 | |
| 	char path[PATH_LEN];
 | |
| 	char tmp_path[PATH_LEN];
 | |
| 	int dev_id, res;
 | |
| 
 | |
| 	path[0]=0;
 | |
| 
 | |
| 	if (strncmp("ADD ", event, 4) == 0) {
 | |
| 
 | |
| 		/* read data from event */
 | |
| 		res = sscanf(event, "ADD %s 0x%x", tmp_path, &dev_id);
 | |
| 
 | |
| 		if (res != 2) {
 | |
| 			fprintf(stderr, "WARN: could not parse event: %s", event);
 | |
| 			fprintf(stderr, "WARN: omitting event: %s", event);
 | |
| 		}
 | |
| 
 | |
| 		strcpy(path, args.path);
 | |
| 		strcat(path, tmp_path);
 | |
| 
 | |
| 		/* what kind of device is added? */
 | |
| 		type = determine_type(path);
 | |
| 
 | |
| 		switch (type) {
 | |
| 			case DEV_TYPE_USB_DEVICE:
 | |
| 				dbg("USB device added: ommited....");
 | |
| 				/* ommit usb devices for now */
 | |
| 				break;
 | |
| 			case DEV_TYPE_USB_INTF:
 | |
| 				dbg("USB interface added: (%s, devid: = %d)",path, dev_id);
 | |
| 				usb_intf_add_event(path, dev_id);
 | |
| 				return;
 | |
| 			default:
 | |
| 				dbg("default");
 | |
| 				fprintf(stderr, "WARN: ommiting event\n");
 | |
| 		}
 | |
| 	} else if (strncmp("REMOVE ", event, 7) == 0) {
 | |
| 
 | |
| 		/* read data from event */
 | |
| 		res = sscanf(event,"REMOVE %s 0x%x", tmp_path, &dev_id);
 | |
| 
 | |
| 		if (res != 2) {
 | |
| 			fprintf(stderr, "WARN: could not parse event: %s", event);
 | |
| 			fprintf(stderr, "WARN: omitting event: %s", event);
 | |
| 		}
 | |
| 
 | |
| 		usb_intf_remove_event(path, dev_id);
 | |
| 
 | |
| #if 0
 | |
| 		strcpy(path, args.path);
 | |
| 		strcat(path, tmp_path);
 | |
| 
 | |
| 		/* what kind of device is added? */
 | |
| 		type = determine_type(path);
 | |
| 
 | |
| 		switch (type) {
 | |
| 			case DEV_TYPE_USB_DEVICE:
 | |
| 				/* ommit usb devices for now */
 | |
| 				break;
 | |
| 			case DEV_TYPE_USB_INTF:
 | |
| 				usb_intf_remove_event(path, dev_id);
 | |
| 				return;
 | |
| 			default:
 | |
| 				fprintf(stderr, "WARN: ommiting event\n");
 | |
| 		}
 | |
| #endif
 | |
| 
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           main_loop                                                       *
 | |
|  *===========================================================================*/
 | |
| static void main_loop()
 | |
| {
 | |
| 	char ev_path[128];
 | |
| 	char buf[256];
 | |
| 	int len;
 | |
| 	FILE* fd;
 | |
| 	len = strlen(args.path);
 | |
| 
 | |
| 	/* init major numbers */
 | |
| 
 | |
| 	memset(&major_bitmap, 0xff, 16);
 | |
| 
 | |
| 	if (len > 128 - 7 /*len of "events" */) {
 | |
| 		fprintf(stderr, "pathname to long\n");
 | |
| 		cleanup();
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	strcpy(ev_path, args.path);
 | |
| 	strcat(ev_path, "events");
 | |
| 
 | |
| 
 | |
| 	while (_run) {
 | |
| 
 | |
| 		char *res;
 | |
| 
 | |
| 		fd = fopen(ev_path, "r");
 | |
| 		if (fd == NULL) {
 | |
| 			/*
 | |
| 			 * ENFILE is a temporary failure, often caused by
 | |
| 			 * running the test set.  Don't die from that..
 | |
| 			 */
 | |
| 			if (errno == ENFILE) {
 | |
| 				usleep(50000);
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			fprintf(stderr,"devmand error: could not open event "
 | |
| 				"file %s bailing out\n", ev_path);
 | |
| 			cleanup();
 | |
| 			exit(1);
 | |
| 		}
 | |
| 
 | |
| 		res = fgets(buf, 256, fd);
 | |
| 		fclose(fd);
 | |
| 
 | |
| 		if (res == NULL) {
 | |
| 			usleep(50000);
 | |
| 			continue;
 | |
| 		}
 | |
| 		dbg("handle_event:  %s", buf);
 | |
| 		handle_event(buf);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *           display_usage                                                   *
 | |
|  *===========================================================================*/
 | |
| static void display_usage(const char *name)
 | |
| {
 | |
| 	printf("Usage: %s [{-p|--pathname} PATH_TO_SYS}"
 | |
| 	       " [{-d|--config-dir} CONFIG_DIR] [-v|--verbose]" 
 | |
| 	       " [[x||--check-config]\n", name);
 | |
| }
 | |
| 
 | 
