314 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* This file contains the table with device <-> driver mappings. It also
 | |
|  * contains some routines to dynamically add and/ or remove device drivers
 | |
|  * or change mappings.
 | |
|  */
 | |
| 
 | |
| #include "fs.h"
 | |
| #include <assert.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| #include <unistd.h>
 | |
| #include <minix/callnr.h>
 | |
| #include <minix/ds.h>
 | |
| 
 | |
| /* The order of the entries in the table determines the mapping between major
 | |
|  * device numbers and device drivers. Character and block devices
 | |
|  * can be intermixed at random.  The ordering determines the device numbers in
 | |
|  * /dev. Note that the major device numbers used in /dev are NOT the same as
 | |
|  * the process numbers of the device drivers. See <minix/dmap.h> for mappings.
 | |
|  */
 | |
| 
 | |
| struct dmap dmap[NR_DEVICES];
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				lock_dmap		 		     *
 | |
|  *===========================================================================*/
 | |
| void lock_dmap(struct dmap *dp)
 | |
| {
 | |
| /* Lock a driver */
 | |
| 	struct worker_thread *org_self;
 | |
| 	int r;
 | |
| 
 | |
| 	assert(dp != NULL);
 | |
| 	assert(dp->dmap_driver != NONE);
 | |
| 
 | |
| 	org_self = worker_suspend();
 | |
| 
 | |
| 	if ((r = mutex_lock(&dp->dmap_lock)) != 0)
 | |
| 		panic("unable to get a lock on dmap: %d\n", r);
 | |
| 
 | |
| 	worker_resume(org_self);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				unlock_dmap		 		     *
 | |
|  *===========================================================================*/
 | |
| void unlock_dmap(struct dmap *dp)
 | |
| {
 | |
| /* Unlock a driver */
 | |
| 	int r;
 | |
| 
 | |
| 	assert(dp != NULL);
 | |
| 
 | |
| 	if ((r = mutex_unlock(&dp->dmap_lock)) != 0)
 | |
| 		panic("unable to unlock dmap lock: %d\n", r);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				map_driver		 		     *
 | |
|  *===========================================================================*/
 | |
| static int map_driver(const char label[LABEL_MAX], devmajor_t major,
 | |
| 	endpoint_t proc_nr_e)
 | |
| {
 | |
| /* Add a new device driver mapping in the dmap table. If the proc_nr is set to
 | |
|  * NONE, we're supposed to unmap it.
 | |
|  */
 | |
|   size_t len;
 | |
|   struct dmap *dp;
 | |
| 
 | |
|   /* Get pointer to device entry in the dmap table. */
 | |
|   if (major < 0 || major >= NR_DEVICES) return(ENODEV);
 | |
|   dp = &dmap[major];
 | |
| 
 | |
|   /* Check if we're supposed to unmap it. */
 | |
|  if (proc_nr_e == NONE) {
 | |
| 	/* Even when a driver is now unmapped and is shortly to be mapped in
 | |
| 	 * due to recovery, invalidate associated filps if they're character
 | |
| 	 * special files. More sophisticated recovery mechanisms which would
 | |
| 	 * reduce the need to invalidate files are possible, but would require
 | |
| 	 * cooperation of the driver and more recovery framework between RS,
 | |
| 	 * VFS, and DS.
 | |
| 	 */
 | |
| 	invalidate_filp_by_char_major(major);
 | |
| 	dp->dmap_driver = NONE;
 | |
| 	return(OK);
 | |
|   }
 | |
| 
 | |
|   if (label != NULL) {
 | |
| 	len = strlen(label);
 | |
| 	if (len+1 > sizeof(dp->dmap_label)) {
 | |
| 		printf("VFS: map_driver: label too long: %d\n", len);
 | |
| 		return(EINVAL);
 | |
| 	}
 | |
| 	strlcpy(dp->dmap_label, label, sizeof(dp->dmap_label));
 | |
|   }
 | |
| 
 | |
|   /* Store driver I/O routines based on type of device */
 | |
|   dp->dmap_driver = proc_nr_e;
 | |
| 
 | |
|   return(OK);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_mapdriver		 		     *
 | |
|  *===========================================================================*/
 | |
| int do_mapdriver(void)
 | |
| {
 | |
| /* Create a device->driver mapping. RS will tell us which major is driven by
 | |
|  * this driver, what type of device it is (regular, TTY, asynchronous, clone,
 | |
|  * etc), and its label. This label is registered with DS, and allows us to
 | |
|  * retrieve the driver's endpoint.
 | |
|  */
 | |
|   int r, major, slot;
 | |
|   endpoint_t endpoint;
 | |
|   vir_bytes label_vir;
 | |
|   size_t label_len;
 | |
|   char label[LABEL_MAX];
 | |
|   struct fproc *rfp;
 | |
| 
 | |
|   /* Only RS can map drivers. */
 | |
|   if (who_e != RS_PROC_NR) return(EPERM);
 | |
| 
 | |
|   label_vir = job_m_in.m_lsys_vfs_mapdriver.label;
 | |
|   label_len = job_m_in.m_lsys_vfs_mapdriver.labellen;
 | |
|   major = job_m_in.m_lsys_vfs_mapdriver.major;
 | |
| 
 | |
|   /* Get the label */
 | |
|   if (label_len > sizeof(label)) { /* Can we store this label? */
 | |
| 	printf("VFS: do_mapdriver: label too long\n");
 | |
| 	return(EINVAL);
 | |
|   }
 | |
|   r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len,
 | |
| 	CP_FLAG_TRY);
 | |
|   if (r != OK) {
 | |
| 	printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r);
 | |
| 	return(EINVAL);
 | |
|   }
 | |
|   if (label[label_len-1] != '\0') {
 | |
| 	printf("VFS: do_mapdriver: label not null-terminated\n");
 | |
| 	return(EINVAL);
 | |
|   }
 | |
| 
 | |
|   /* Now we know how the driver is called, fetch its endpoint */
 | |
|   r = ds_retrieve_label_endpt(label, &endpoint);
 | |
|   if (r != OK) {
 | |
| 	printf("VFS: do_mapdriver: label '%s' unknown\n", label);
 | |
| 	return(EINVAL);
 | |
|   }
 | |
| 
 | |
|   /* Process is a service */
 | |
|   if (isokendpt(endpoint, &slot) != OK) {
 | |
| 	printf("VFS: can't map driver to unknown endpoint %d\n", endpoint);
 | |
| 	return(EINVAL);
 | |
|   }
 | |
|   rfp = &fproc[slot];
 | |
|   rfp->fp_flags |= FP_SRV_PROC;
 | |
| 
 | |
|   /* Try to update device mapping. */
 | |
|   return map_driver(label, major, endpoint);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				dmap_unmap_by_endpt	 		     *
 | |
|  *===========================================================================*/
 | |
| void dmap_unmap_by_endpt(endpoint_t proc_e)
 | |
| {
 | |
| /* Lookup driver in dmap table by endpoint and unmap it */
 | |
|   int major, r;
 | |
| 
 | |
|   for (major = 0; major < NR_DEVICES; major++) {
 | |
| 	if (dmap_driver_match(proc_e, major)) {
 | |
| 		/* Found driver; overwrite it with a NULL entry */
 | |
| 		if ((r = map_driver(NULL, major, NONE)) != OK) {
 | |
| 			printf("VFS: unmapping driver %d for major %d failed:"
 | |
| 				" %d\n", proc_e, major, r);
 | |
| 		}
 | |
| 	}
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *		               map_service				     *
 | |
|  *===========================================================================*/
 | |
| int map_service(struct rprocpub *rpub)
 | |
| {
 | |
| /* Map a new service by storing its device driver properties. */
 | |
|   int r, slot;
 | |
|   struct fproc *rfp;
 | |
| 
 | |
|   if (IS_RPUB_BOOT_USR(rpub)) return(OK);
 | |
| 
 | |
|   /* Process is a service */
 | |
|   if (isokendpt(rpub->endpoint, &slot) != OK) {
 | |
| 	printf("VFS: can't map service with unknown endpoint %d\n",
 | |
| 		rpub->endpoint);
 | |
| 	return(EINVAL);
 | |
|   }
 | |
|   rfp = &fproc[slot];
 | |
|   rfp->fp_flags |= FP_SRV_PROC;
 | |
| 
 | |
|   /* Not a driver, nothing more to do. */
 | |
|   if (rpub->dev_nr == NO_DEV) return(OK);
 | |
| 
 | |
|   /* Map driver. */
 | |
|   r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint);
 | |
|   if(r != OK) return(r);
 | |
| 
 | |
|   return(OK);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				init_dmap		 		     *
 | |
|  *===========================================================================*/
 | |
| void init_dmap(void)
 | |
| {
 | |
| /* Initialize the device mapping table. */
 | |
|   int i;
 | |
| 
 | |
|   memset(dmap, 0, sizeof(dmap));
 | |
| 
 | |
|   for (i = 0; i < NR_DEVICES; i++) {
 | |
| 	dmap[i].dmap_driver = NONE;
 | |
| 	dmap[i].dmap_servicing = INVALID_THREAD;
 | |
| 	if (mutex_init(&dmap[i].dmap_lock, NULL) != 0)
 | |
| 		panic("unable to initialize dmap lock");
 | |
|   }
 | |
| 
 | |
|   /* CTTY_MAJOR is a special case, which is handled by VFS itself. */
 | |
|   if (map_driver("vfs", CTTY_MAJOR, CTTY_ENDPT) != OK)
 | |
| 	panic("map_driver(CTTY_MAJOR) failed");
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				dmap_driver_match	 		     *
 | |
|  *===========================================================================*/
 | |
| int dmap_driver_match(endpoint_t proc, int major)
 | |
| {
 | |
|   if (major < 0 || major >= NR_DEVICES) return(0);
 | |
|   if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc)
 | |
| 	return(1);
 | |
| 
 | |
|   return(0);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				dmap_by_major		 		     *
 | |
|  *===========================================================================*/
 | |
| struct dmap *
 | |
| get_dmap_by_major(int major)
 | |
| {
 | |
| 	if (major < 0 || major >= NR_DEVICES) return(NULL);
 | |
| 	if (dmap[major].dmap_driver == NONE) return(NULL);
 | |
| 	return(&dmap[major]);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				dmap_endpt_up		 		     *
 | |
|  *===========================================================================*/
 | |
| void dmap_endpt_up(endpoint_t proc_e, int is_blk)
 | |
| {
 | |
| /* A device driver with endpoint proc_e has been restarted. Go tell everyone
 | |
|  * that might be blocking on it that this device is 'up'.
 | |
|  */
 | |
| 
 | |
|   int major;
 | |
|   struct dmap *dp;
 | |
|   struct worker_thread *worker;
 | |
| 
 | |
|   if (proc_e == NONE) return;
 | |
| 
 | |
|   for (major = 0; major < NR_DEVICES; major++) {
 | |
| 	if ((dp = get_dmap_by_major(major)) == NULL) continue;
 | |
| 	if (dp->dmap_driver == proc_e) {
 | |
| 		if (is_blk) {
 | |
| 			if (dp->dmap_recovering) {
 | |
| 				printf("VFS: driver recovery failure for"
 | |
| 					" major %d\n", major);
 | |
| 				if (dp->dmap_servicing != INVALID_THREAD) {
 | |
| 					worker = worker_get(dp->dmap_servicing);
 | |
| 					worker_stop(worker);
 | |
| 				}
 | |
| 				dp->dmap_recovering = 0;
 | |
| 				continue;
 | |
| 			}
 | |
| 			dp->dmap_recovering = 1;
 | |
| 			bdev_up(major);
 | |
| 			dp->dmap_recovering = 0;
 | |
| 		} else {
 | |
| 			if (dp->dmap_servicing != INVALID_THREAD) {
 | |
| 				worker = worker_get(dp->dmap_servicing);
 | |
| 				worker_stop(worker);
 | |
| 			}
 | |
| 			invalidate_filp_by_char_major(major);
 | |
| 		}
 | |
| 	}
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				get_dmap		 		     *
 | |
|  *===========================================================================*/
 | |
| struct dmap *get_dmap(endpoint_t proc_e)
 | |
| {
 | |
| /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a
 | |
|  * pointer */
 | |
| 
 | |
|   int major;
 | |
|   for (major = 0; major < NR_DEVICES; major++)
 | |
| 	if (dmap_driver_match(proc_e, major))
 | |
| 		return(&dmap[major]);
 | |
| 
 | |
|   return(NULL);
 | |
| }
 | 
