xref: /minix/minix/servers/vfs/cdev.c (revision c5da0dff)
189a4204bSDavid van Moolenbroek /*
289a4204bSDavid van Moolenbroek  * This file contains routines to perform character device operations.
389a4204bSDavid van Moolenbroek  * Character drivers may suspend I/O requests on their devices (read, write,
489a4204bSDavid van Moolenbroek  * ioctl), as well as select requests.  These requests will therefore suspend
589a4204bSDavid van Moolenbroek  * their calling process, freeing up the associated VFS worker thread for other
689a4204bSDavid van Moolenbroek  * tasks.  The I/O requests may later be cancelled as a result of the suspended
789a4204bSDavid van Moolenbroek  * process receiving a signal (which it either catches or dies from), in which
889a4204bSDavid van Moolenbroek  * case there will be a worker thread associated with the cancellation.  Open
989a4204bSDavid van Moolenbroek  * and close requests may not suspend and will thus block the calling thread.
1089a4204bSDavid van Moolenbroek  *
1189a4204bSDavid van Moolenbroek  * The entry points in this file are:
1289a4204bSDavid van Moolenbroek  *   cdev_map:    map a character device to its actual device number
1389a4204bSDavid van Moolenbroek  *   cdev_open:   open a character device
1489a4204bSDavid van Moolenbroek  *   cdev_close:  close a character device
1589a4204bSDavid van Moolenbroek  *   cdev_io:     initiate a read, write, or ioctl to a character device
1689a4204bSDavid van Moolenbroek  *   cdev_select: initiate a select call on a device
1789a4204bSDavid van Moolenbroek  *   cdev_cancel: cancel an I/O request, blocking until it has been cancelled
1889a4204bSDavid van Moolenbroek  *   cdev_reply:  process the result of a character driver request
1989a4204bSDavid van Moolenbroek  */
2089a4204bSDavid van Moolenbroek 
2189a4204bSDavid van Moolenbroek #include "fs.h"
2289a4204bSDavid van Moolenbroek #include "vnode.h"
2389a4204bSDavid van Moolenbroek #include "file.h"
2489a4204bSDavid van Moolenbroek #include <string.h>
2589a4204bSDavid van Moolenbroek #include <fcntl.h>
2689a4204bSDavid van Moolenbroek #include <sys/ttycom.h>
2789a4204bSDavid van Moolenbroek #include <assert.h>
2889a4204bSDavid van Moolenbroek 
2989a4204bSDavid van Moolenbroek /*
3089a4204bSDavid van Moolenbroek  * Map the given device number to a real device number, remapping /dev/tty to
3189a4204bSDavid van Moolenbroek  * the given process's controlling terminal if it has one.  Perform a bounds
3289a4204bSDavid van Moolenbroek  * check on the resulting device's major number, and return NO_DEV on failure.
3389a4204bSDavid van Moolenbroek  * This function is idempotent but not used that way.
3489a4204bSDavid van Moolenbroek  */
3589a4204bSDavid van Moolenbroek dev_t
cdev_map(dev_t dev,struct fproc * rfp)3689a4204bSDavid van Moolenbroek cdev_map(dev_t dev, struct fproc * rfp)
3789a4204bSDavid van Moolenbroek {
3889a4204bSDavid van Moolenbroek 	devmajor_t major;
3989a4204bSDavid van Moolenbroek 
4089a4204bSDavid van Moolenbroek 	/*
4189a4204bSDavid van Moolenbroek 	 * First cover one special case: /dev/tty, the magic device that
4289a4204bSDavid van Moolenbroek 	 * translates to the controlling TTY.
4389a4204bSDavid van Moolenbroek 	 */
4489a4204bSDavid van Moolenbroek 	if ((major = major(dev)) == CTTY_MAJOR) {
4589a4204bSDavid van Moolenbroek 		/* No controlling terminal?  Fail the request. */
4689a4204bSDavid van Moolenbroek 		if (rfp->fp_tty == NO_DEV) return NO_DEV;
4789a4204bSDavid van Moolenbroek 
4889a4204bSDavid van Moolenbroek 		/* Substitute the controlling terminal device. */
4989a4204bSDavid van Moolenbroek 		dev = rfp->fp_tty;
5089a4204bSDavid van Moolenbroek 		major = major(dev);
5189a4204bSDavid van Moolenbroek 	}
5289a4204bSDavid van Moolenbroek 
5389a4204bSDavid van Moolenbroek 	if (major < 0 || major >= NR_DEVICES) return NO_DEV;
5489a4204bSDavid van Moolenbroek 
5589a4204bSDavid van Moolenbroek 	return dev;
5689a4204bSDavid van Moolenbroek }
5789a4204bSDavid van Moolenbroek 
5889a4204bSDavid van Moolenbroek /*
5989a4204bSDavid van Moolenbroek  * Obtain the dmap structure for the given device, if a valid driver exists for
6089a4204bSDavid van Moolenbroek  * the major device.  Perform redirection for CTTY_MAJOR.
6189a4204bSDavid van Moolenbroek  */
6289a4204bSDavid van Moolenbroek static struct dmap *
cdev_get(dev_t dev,devminor_t * minor_dev)6389a4204bSDavid van Moolenbroek cdev_get(dev_t dev, devminor_t * minor_dev)
6489a4204bSDavid van Moolenbroek {
6589a4204bSDavid van Moolenbroek 	struct dmap *dp;
6689a4204bSDavid van Moolenbroek 	int slot;
6789a4204bSDavid van Moolenbroek 
6889a4204bSDavid van Moolenbroek 	/*
6989a4204bSDavid van Moolenbroek 	 * Remap /dev/tty as needed.  Perform a bounds check on the major
7089a4204bSDavid van Moolenbroek 	 * number.
7189a4204bSDavid van Moolenbroek 	 */
7289a4204bSDavid van Moolenbroek 	if ((dev = cdev_map(dev, fp)) == NO_DEV)
7389a4204bSDavid van Moolenbroek 		return NULL;
7489a4204bSDavid van Moolenbroek 
7589a4204bSDavid van Moolenbroek 	/* Determine the driver endpoint. */
7689a4204bSDavid van Moolenbroek 	dp = &dmap[major(dev)];
7789a4204bSDavid van Moolenbroek 
7889a4204bSDavid van Moolenbroek 	/* See if driver is roughly valid. */
7989a4204bSDavid van Moolenbroek 	if (dp->dmap_driver == NONE) return NULL;
8089a4204bSDavid van Moolenbroek 
8189a4204bSDavid van Moolenbroek 	if (isokendpt(dp->dmap_driver, &slot) != OK) {
8289a4204bSDavid van Moolenbroek 		printf("VFS: cdev_get: old driver for major %x (%d)\n",
8389a4204bSDavid van Moolenbroek 		    major(dev), dp->dmap_driver);
8489a4204bSDavid van Moolenbroek 		return NULL;
8589a4204bSDavid van Moolenbroek 	}
8689a4204bSDavid van Moolenbroek 
8789a4204bSDavid van Moolenbroek 	/* Also return the (possibly redirected) minor number. */
8889a4204bSDavid van Moolenbroek 	*minor_dev = minor(dev);
8989a4204bSDavid van Moolenbroek 	return dp;
9089a4204bSDavid van Moolenbroek }
9189a4204bSDavid van Moolenbroek 
9289a4204bSDavid van Moolenbroek /*
9389a4204bSDavid van Moolenbroek  * A new minor device number has been returned.  Request PFS to create a
9489a4204bSDavid van Moolenbroek  * temporary device file to hold it.
9589a4204bSDavid van Moolenbroek  */
9689a4204bSDavid van Moolenbroek static int
cdev_clone(int fd,dev_t dev,devminor_t new_minor)9789a4204bSDavid van Moolenbroek cdev_clone(int fd, dev_t dev, devminor_t new_minor)
9889a4204bSDavid van Moolenbroek {
9989a4204bSDavid van Moolenbroek 	struct vnode *vp;
10089a4204bSDavid van Moolenbroek 	struct node_details res;
10189a4204bSDavid van Moolenbroek 	int r;
10289a4204bSDavid van Moolenbroek 
10389a4204bSDavid van Moolenbroek 	assert(fd != -1);
10489a4204bSDavid van Moolenbroek 
10589a4204bSDavid van Moolenbroek 	/* Device number of the new device. */
10689a4204bSDavid van Moolenbroek 	dev = makedev(major(dev), new_minor);
10789a4204bSDavid van Moolenbroek 
10889a4204bSDavid van Moolenbroek 	/* Create a new file system node on PFS for the cloned device. */
10989a4204bSDavid van Moolenbroek 	r = req_newnode(PFS_PROC_NR, fp->fp_effuid, fp->fp_effgid,
11089a4204bSDavid van Moolenbroek 	    RWX_MODES | I_CHAR_SPECIAL, dev, &res);
11189a4204bSDavid van Moolenbroek 	if (r != OK) {
11289a4204bSDavid van Moolenbroek 		(void)cdev_close(dev);
11389a4204bSDavid van Moolenbroek 		return r;
11489a4204bSDavid van Moolenbroek 	}
11589a4204bSDavid van Moolenbroek 
11689a4204bSDavid van Moolenbroek 	/* Drop the old node and use the new values. */
11789a4204bSDavid van Moolenbroek 	if ((vp = get_free_vnode()) == NULL) {
11889a4204bSDavid van Moolenbroek 		req_putnode(PFS_PROC_NR, res.inode_nr, 1); /* is this right? */
11989a4204bSDavid van Moolenbroek 		(void)cdev_close(dev);
12089a4204bSDavid van Moolenbroek 		return err_code;
12189a4204bSDavid van Moolenbroek 	}
12289a4204bSDavid van Moolenbroek 	lock_vnode(vp, VNODE_OPCL);
12389a4204bSDavid van Moolenbroek 
12489a4204bSDavid van Moolenbroek 	assert(fp->fp_filp[fd] != NULL);
12589a4204bSDavid van Moolenbroek 	unlock_vnode(fp->fp_filp[fd]->filp_vno);
12689a4204bSDavid van Moolenbroek 	put_vnode(fp->fp_filp[fd]->filp_vno);
12789a4204bSDavid van Moolenbroek 
12889a4204bSDavid van Moolenbroek 	vp->v_fs_e = res.fs_e;
12989a4204bSDavid van Moolenbroek 	vp->v_vmnt = NULL;
13089a4204bSDavid van Moolenbroek 	vp->v_dev = NO_DEV;
13189a4204bSDavid van Moolenbroek 	vp->v_inode_nr = res.inode_nr;
13289a4204bSDavid van Moolenbroek 	vp->v_mode = res.fmode;
13389a4204bSDavid van Moolenbroek 	vp->v_sdev = dev;
13489a4204bSDavid van Moolenbroek 	vp->v_fs_count = 1;
13589a4204bSDavid van Moolenbroek 	vp->v_ref_count = 1;
13689a4204bSDavid van Moolenbroek 	fp->fp_filp[fd]->filp_vno = vp;
13789a4204bSDavid van Moolenbroek 
13889a4204bSDavid van Moolenbroek 	return OK;
13989a4204bSDavid van Moolenbroek }
14089a4204bSDavid van Moolenbroek 
14189a4204bSDavid van Moolenbroek /*
14289a4204bSDavid van Moolenbroek  * Open or close a character device.  The given operation must be either
14389a4204bSDavid van Moolenbroek  * CDEV_OPEN or CDEV_CLOSE.  For CDEV_OPEN, 'fd' must be the file descriptor
14489a4204bSDavid van Moolenbroek  * for the file being opened; for CDEV_CLOSE, it is ignored.  For CDEV_OPEN,
14589a4204bSDavid van Moolenbroek  * 'flags' identifies a bitwise combination of R_BIT, W_BIT, and/or O_NOCTTY;
14689a4204bSDavid van Moolenbroek  * for CDEV_CLOSE, it too is ignored.
14789a4204bSDavid van Moolenbroek  */
14889a4204bSDavid van Moolenbroek static int
cdev_opcl(int op,dev_t dev,int fd,int flags)14989a4204bSDavid van Moolenbroek cdev_opcl(int op, dev_t dev, int fd, int flags)
15089a4204bSDavid van Moolenbroek {
15189a4204bSDavid van Moolenbroek 	devminor_t minor_dev, new_minor;
15289a4204bSDavid van Moolenbroek 	struct dmap *dp;
15389a4204bSDavid van Moolenbroek 	struct fproc *rfp;
15489a4204bSDavid van Moolenbroek 	message dev_mess;
15589a4204bSDavid van Moolenbroek 	int r, r2, acc;
15689a4204bSDavid van Moolenbroek 
15789a4204bSDavid van Moolenbroek 	/*
15889a4204bSDavid van Moolenbroek 	 * We need the a descriptor for CDEV_OPEN, because if the driver
15989a4204bSDavid van Moolenbroek 	 * returns a cloned device, we need to replace what the fd points to.
16089a4204bSDavid van Moolenbroek 	 * For CDEV_CLOSE however, we may be closing a device for which the
16189a4204bSDavid van Moolenbroek 	 * calling process has no file descriptor, and thus we expect no
16289a4204bSDavid van Moolenbroek 	 * meaningful fd value in that case.
16389a4204bSDavid van Moolenbroek 	 */
16489a4204bSDavid van Moolenbroek 	assert(op == CDEV_OPEN || op == CDEV_CLOSE);
16589a4204bSDavid van Moolenbroek 	assert(fd != -1 || op == CDEV_CLOSE);
16689a4204bSDavid van Moolenbroek 
16789a4204bSDavid van Moolenbroek 	/* Determine task dmap. */
16889a4204bSDavid van Moolenbroek 	if ((dp = cdev_get(dev, &minor_dev)) == NULL)
16989a4204bSDavid van Moolenbroek 		return ENXIO;
17089a4204bSDavid van Moolenbroek 
17189a4204bSDavid van Moolenbroek 	/*
17289a4204bSDavid van Moolenbroek 	 * CTTY exception: do not actually send the open/close request for
17389a4204bSDavid van Moolenbroek 	 * /dev/tty to the driver.  This avoids the case that the actual device
17489a4204bSDavid van Moolenbroek 	 * will remain open forever if the process calls setsid() after opening
17589a4204bSDavid van Moolenbroek 	 * /dev/tty.
17689a4204bSDavid van Moolenbroek 	 */
17789a4204bSDavid van Moolenbroek 	if (major(dev) == CTTY_MAJOR) return OK;
17889a4204bSDavid van Moolenbroek 
17989a4204bSDavid van Moolenbroek 	/*
18089a4204bSDavid van Moolenbroek 	 * Add O_NOCTTY to the access flags if this process is not a session
18189a4204bSDavid van Moolenbroek 	 * leader, or if it already has a controlling tty, or if it is someone
18289a4204bSDavid van Moolenbroek 	 * else's controlling tty.  For performance reasons, only search the
18389a4204bSDavid van Moolenbroek 	 * full process table if this driver has set controlling TTYs before.
18489a4204bSDavid van Moolenbroek 	 */
18589a4204bSDavid van Moolenbroek 	if (!(fp->fp_flags & FP_SESLDR) || fp->fp_tty != 0) {
18689a4204bSDavid van Moolenbroek 		flags |= O_NOCTTY;
18789a4204bSDavid van Moolenbroek 	} else if (!(flags & O_NOCTTY) && dp->dmap_seen_tty) {
18889a4204bSDavid van Moolenbroek 		for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++)
18989a4204bSDavid van Moolenbroek 			if (rfp->fp_pid != PID_FREE && rfp->fp_tty == dev)
19089a4204bSDavid van Moolenbroek 				flags |= O_NOCTTY;
19189a4204bSDavid van Moolenbroek 	}
19289a4204bSDavid van Moolenbroek 
19389a4204bSDavid van Moolenbroek 	/* Prepare the request message. */
19489a4204bSDavid van Moolenbroek 	memset(&dev_mess, 0, sizeof(dev_mess));
19589a4204bSDavid van Moolenbroek 	dev_mess.m_type = op;
19689a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_openclose.minor = minor_dev;
19789a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_openclose.id = who_e;
19889a4204bSDavid van Moolenbroek 	if (op == CDEV_OPEN) {
19989a4204bSDavid van Moolenbroek 		acc = 0;
20089a4204bSDavid van Moolenbroek 		if (flags & R_BIT) acc |= CDEV_R_BIT;
20189a4204bSDavid van Moolenbroek 		if (flags & W_BIT) acc |= CDEV_W_BIT;
20289a4204bSDavid van Moolenbroek 		if (flags & O_NOCTTY) acc |= CDEV_NOCTTY;
20389a4204bSDavid van Moolenbroek 		dev_mess.m_vfs_lchardriver_openclose.user = who_e;
20489a4204bSDavid van Moolenbroek 		dev_mess.m_vfs_lchardriver_openclose.access = acc;
20589a4204bSDavid van Moolenbroek 	}
20689a4204bSDavid van Moolenbroek 
20789a4204bSDavid van Moolenbroek 	/* Send the request to the driver. */
20889a4204bSDavid van Moolenbroek 	if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
20989a4204bSDavid van Moolenbroek 		panic("VFS: asynsend in cdev_opcl failed: %d", r);
21089a4204bSDavid van Moolenbroek 
21189a4204bSDavid van Moolenbroek 	/* Block the thread waiting for a reply. */
21289a4204bSDavid van Moolenbroek 	self->w_task = dp->dmap_driver;
21389a4204bSDavid van Moolenbroek 	self->w_drv_sendrec = &dev_mess;
21489a4204bSDavid van Moolenbroek 
21589a4204bSDavid van Moolenbroek 	worker_wait();
21689a4204bSDavid van Moolenbroek 
21789a4204bSDavid van Moolenbroek 	self->w_task = NONE;
21889a4204bSDavid van Moolenbroek 	assert(self->w_drv_sendrec == NULL);
21989a4204bSDavid van Moolenbroek 
22089a4204bSDavid van Moolenbroek 	/* Process the reply. */
22189a4204bSDavid van Moolenbroek 	r = dev_mess.m_lchardriver_vfs_reply.status;
22289a4204bSDavid van Moolenbroek 
22389a4204bSDavid van Moolenbroek 	if (op == CDEV_OPEN && r >= 0) {
22489a4204bSDavid van Moolenbroek 		/*
22589a4204bSDavid van Moolenbroek 		 * Some devices need special processing upon open.  Such a
22689a4204bSDavid van Moolenbroek 		 * device is "cloned", i.e., on a succesful open it is replaced
22789a4204bSDavid van Moolenbroek 		 * by a new device with a new unique minor device number.  This
22889a4204bSDavid van Moolenbroek 		 * new device number identifies a new object that has been
22989a4204bSDavid van Moolenbroek 		 * allocated within a driver.
23089a4204bSDavid van Moolenbroek 		 */
23189a4204bSDavid van Moolenbroek 		if (r & CDEV_CLONED) {
23289a4204bSDavid van Moolenbroek 			new_minor = r & ~(CDEV_CLONED | CDEV_CTTY);
23389a4204bSDavid van Moolenbroek 			if ((r2 = cdev_clone(fd, dev, new_minor)) < 0)
23489a4204bSDavid van Moolenbroek 				return r2;
23589a4204bSDavid van Moolenbroek 		}
23689a4204bSDavid van Moolenbroek 
23789a4204bSDavid van Moolenbroek 		/* Did this call make the TTY the controlling TTY? */
23889a4204bSDavid van Moolenbroek 		if (r & CDEV_CTTY) {
23989a4204bSDavid van Moolenbroek 			fp->fp_tty = dev;
24089a4204bSDavid van Moolenbroek 			dp->dmap_seen_tty = TRUE;
24189a4204bSDavid van Moolenbroek 		}
24289a4204bSDavid van Moolenbroek 
24389a4204bSDavid van Moolenbroek 		r = OK;
24489a4204bSDavid van Moolenbroek 	}
24589a4204bSDavid van Moolenbroek 
24689a4204bSDavid van Moolenbroek 	/* Return the result from the driver. */
24789a4204bSDavid van Moolenbroek 	return r;
24889a4204bSDavid van Moolenbroek }
24989a4204bSDavid van Moolenbroek 
25089a4204bSDavid van Moolenbroek /*
25189a4204bSDavid van Moolenbroek  * Open a character device.
25289a4204bSDavid van Moolenbroek  */
25389a4204bSDavid van Moolenbroek int
cdev_open(int fd,dev_t dev,int flags)25489a4204bSDavid van Moolenbroek cdev_open(int fd, dev_t dev, int flags)
25589a4204bSDavid van Moolenbroek {
25689a4204bSDavid van Moolenbroek 
25789a4204bSDavid van Moolenbroek 	return cdev_opcl(CDEV_OPEN, dev, fd, flags);
25889a4204bSDavid van Moolenbroek }
25989a4204bSDavid van Moolenbroek 
26089a4204bSDavid van Moolenbroek /*
26189a4204bSDavid van Moolenbroek  * Close a character device.
26289a4204bSDavid van Moolenbroek  */
26389a4204bSDavid van Moolenbroek int
cdev_close(dev_t dev)26489a4204bSDavid van Moolenbroek cdev_close(dev_t dev)
26589a4204bSDavid van Moolenbroek {
26689a4204bSDavid van Moolenbroek 
26789a4204bSDavid van Moolenbroek 	return cdev_opcl(CDEV_CLOSE, dev, -1, 0);
26889a4204bSDavid van Moolenbroek }
26989a4204bSDavid van Moolenbroek 
27089a4204bSDavid van Moolenbroek /*
27189a4204bSDavid van Moolenbroek  * Initiate a read, write, or ioctl to a character device.  The given operation
27289a4204bSDavid van Moolenbroek  * must be CDEV_READ, CDEV_WRITE, or CDEV_IOCTL.  The call is made on behalf of
27389a4204bSDavid van Moolenbroek  * user process 'proc_e'.  For read/write requests, 'bytes' is the number of
27489a4204bSDavid van Moolenbroek  * bytes to read into 'buf' at file position 'pos'.  For ioctl requests,
27589a4204bSDavid van Moolenbroek  * 'bytes' is actually an IOCTL request code, which implies the size of the
27689a4204bSDavid van Moolenbroek  * buffer 'buf' if needed for the request at all ('pos' is ignored here).  The
27789a4204bSDavid van Moolenbroek  * 'flags' field contains file pointer flags, from which O_NONBLOCK is tested.
27889a4204bSDavid van Moolenbroek  */
27989a4204bSDavid van Moolenbroek int
cdev_io(int op,dev_t dev,endpoint_t proc_e,vir_bytes buf,off_t pos,unsigned long bytes,int flags)28089a4204bSDavid van Moolenbroek cdev_io(int op, dev_t dev, endpoint_t proc_e, vir_bytes buf, off_t pos,
28189a4204bSDavid van Moolenbroek 	unsigned long bytes, int flags)
28289a4204bSDavid van Moolenbroek {
28389a4204bSDavid van Moolenbroek 	devminor_t minor_dev;
28489a4204bSDavid van Moolenbroek 	struct dmap *dp;
28589a4204bSDavid van Moolenbroek 	message dev_mess;
28689a4204bSDavid van Moolenbroek 	cp_grant_id_t gid;
28789a4204bSDavid van Moolenbroek 	int r;
28889a4204bSDavid van Moolenbroek 
28989a4204bSDavid van Moolenbroek 	assert(op == CDEV_READ || op == CDEV_WRITE || op == CDEV_IOCTL);
29089a4204bSDavid van Moolenbroek 
29189a4204bSDavid van Moolenbroek 	/* Determine task map. */
29289a4204bSDavid van Moolenbroek 	if ((dp = cdev_get(dev, &minor_dev)) == NULL)
29389a4204bSDavid van Moolenbroek 		return EIO;
29489a4204bSDavid van Moolenbroek 
29589a4204bSDavid van Moolenbroek 	/*
29689a4204bSDavid van Moolenbroek 	 * Handle TIOCSCTTY ioctl: set controlling TTY.  FIXME: this should not
29789a4204bSDavid van Moolenbroek 	 * hardcode major device numbers, and not assume that the IOCTL request
29889a4204bSDavid van Moolenbroek 	 * succeeds!
29989a4204bSDavid van Moolenbroek 	 */
30089a4204bSDavid van Moolenbroek 	if (op == CDEV_IOCTL && bytes == TIOCSCTTY &&
30189a4204bSDavid van Moolenbroek 	    (major(dev) == TTY_MAJOR || major(dev) == PTY_MAJOR)) {
30289a4204bSDavid van Moolenbroek 		fp->fp_tty = dev;
30389a4204bSDavid van Moolenbroek 	}
30489a4204bSDavid van Moolenbroek 
30589a4204bSDavid van Moolenbroek 	/* Create a grant for the buffer provided by the user process. */
30689a4204bSDavid van Moolenbroek 	if (op != CDEV_IOCTL) {
30789a4204bSDavid van Moolenbroek 		gid = cpf_grant_magic(dp->dmap_driver, proc_e, buf,
30889a4204bSDavid van Moolenbroek 		    (size_t)bytes, (op == CDEV_READ) ? CPF_WRITE : CPF_READ);
30989a4204bSDavid van Moolenbroek 		if (!GRANT_VALID(gid))
31089a4204bSDavid van Moolenbroek 			panic("VFS: cpf_grant_magic failed");
31189a4204bSDavid van Moolenbroek 	} else
31289a4204bSDavid van Moolenbroek 		gid = make_ioctl_grant(dp->dmap_driver, proc_e, buf, bytes);
31389a4204bSDavid van Moolenbroek 
31489a4204bSDavid van Moolenbroek 	/* Set up the message that will be sent to the driver. */
31589a4204bSDavid van Moolenbroek 	memset(&dev_mess, 0, sizeof(dev_mess));
31689a4204bSDavid van Moolenbroek 	dev_mess.m_type = op;
31789a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_readwrite.minor = minor_dev;
31889a4204bSDavid van Moolenbroek 	if (op == CDEV_IOCTL) {
31989a4204bSDavid van Moolenbroek 		dev_mess.m_vfs_lchardriver_readwrite.request = bytes;
32089a4204bSDavid van Moolenbroek 		dev_mess.m_vfs_lchardriver_readwrite.user = proc_e;
32189a4204bSDavid van Moolenbroek 	} else {
32289a4204bSDavid van Moolenbroek 		dev_mess.m_vfs_lchardriver_readwrite.pos = pos;
32389a4204bSDavid van Moolenbroek 		dev_mess.m_vfs_lchardriver_readwrite.count = bytes;
32489a4204bSDavid van Moolenbroek 	}
32589a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_readwrite.id = proc_e;
32689a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_readwrite.grant = gid;
32789a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_readwrite.flags = 0;
32889a4204bSDavid van Moolenbroek 	if (flags & O_NONBLOCK)
32989a4204bSDavid van Moolenbroek 		  dev_mess.m_vfs_lchardriver_readwrite.flags |= CDEV_NONBLOCK;
33089a4204bSDavid van Moolenbroek 
33189a4204bSDavid van Moolenbroek 	/* Send the request to the driver. */
33289a4204bSDavid van Moolenbroek 	if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
33389a4204bSDavid van Moolenbroek 		panic("VFS: asynsend in cdev_io failed: %d", r);
33489a4204bSDavid van Moolenbroek 
33589a4204bSDavid van Moolenbroek 	/* Suspend the calling process until a reply arrives. */
33689a4204bSDavid van Moolenbroek 	fp->fp_cdev.dev = dev;
33789a4204bSDavid van Moolenbroek 	fp->fp_cdev.endpt = dp->dmap_driver;
33889a4204bSDavid van Moolenbroek 	fp->fp_cdev.grant = gid;	/* revoke this when unsuspended */
33989a4204bSDavid van Moolenbroek 	suspend(FP_BLOCKED_ON_CDEV);
34089a4204bSDavid van Moolenbroek 
34189a4204bSDavid van Moolenbroek 	return SUSPEND;
34289a4204bSDavid van Moolenbroek }
34389a4204bSDavid van Moolenbroek 
34489a4204bSDavid van Moolenbroek /*
34589a4204bSDavid van Moolenbroek  * Initiate a select call on a device.  Return OK iff the request was sent.
34689a4204bSDavid van Moolenbroek  * This function explicitly bypasses cdev_get() since it must not do CTTY
34789a4204bSDavid van Moolenbroek  * mapping, because a) the caller already has done that, b) "fp" may be wrong.
34889a4204bSDavid van Moolenbroek  */
34989a4204bSDavid van Moolenbroek int
cdev_select(dev_t dev,int ops)35089a4204bSDavid van Moolenbroek cdev_select(dev_t dev, int ops)
35189a4204bSDavid van Moolenbroek {
35289a4204bSDavid van Moolenbroek 	devmajor_t major;
35389a4204bSDavid van Moolenbroek 	message dev_mess;
35489a4204bSDavid van Moolenbroek 	struct dmap *dp;
35589a4204bSDavid van Moolenbroek 	int r;
35689a4204bSDavid van Moolenbroek 
35789a4204bSDavid van Moolenbroek 	/* Determine task dmap, without CTTY mapping. */
35889a4204bSDavid van Moolenbroek 	assert(dev != NO_DEV);
35989a4204bSDavid van Moolenbroek 	major = major(dev);
36089a4204bSDavid van Moolenbroek 	assert(major >= 0 && major < NR_DEVICES);
36189a4204bSDavid van Moolenbroek 	assert(major != CTTY_MAJOR);
36289a4204bSDavid van Moolenbroek 	dp = &dmap[major];
36389a4204bSDavid van Moolenbroek 
36489a4204bSDavid van Moolenbroek 	/* Prepare the request message. */
36589a4204bSDavid van Moolenbroek 	memset(&dev_mess, 0, sizeof(dev_mess));
36689a4204bSDavid van Moolenbroek 	dev_mess.m_type = CDEV_SELECT;
36789a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_select.minor = minor(dev);
36889a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_select.ops = ops;
36989a4204bSDavid van Moolenbroek 
37089a4204bSDavid van Moolenbroek 	/* Send the request to the driver. */
37189a4204bSDavid van Moolenbroek 	if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
37289a4204bSDavid van Moolenbroek 		panic("VFS: asynsend in cdev_select failed: %d", r);
37389a4204bSDavid van Moolenbroek 
37489a4204bSDavid van Moolenbroek 	return OK;
37589a4204bSDavid van Moolenbroek }
37689a4204bSDavid van Moolenbroek 
37789a4204bSDavid van Moolenbroek /*
37889a4204bSDavid van Moolenbroek  * Cancel an I/O request, blocking until it has been cancelled.
37989a4204bSDavid van Moolenbroek  */
38089a4204bSDavid van Moolenbroek int
cdev_cancel(dev_t dev,endpoint_t endpt __unused,cp_grant_id_t grant)38189a4204bSDavid van Moolenbroek cdev_cancel(dev_t dev, endpoint_t endpt __unused, cp_grant_id_t grant)
38289a4204bSDavid van Moolenbroek {
38389a4204bSDavid van Moolenbroek 	devminor_t minor_dev;
38489a4204bSDavid van Moolenbroek 	message dev_mess;
38589a4204bSDavid van Moolenbroek 	struct dmap *dp;
38689a4204bSDavid van Moolenbroek 	int r;
38789a4204bSDavid van Moolenbroek 
38889a4204bSDavid van Moolenbroek 	/* Determine task dmap. */
38989a4204bSDavid van Moolenbroek 	if ((dp = cdev_get(dev, &minor_dev)) == NULL)
39089a4204bSDavid van Moolenbroek 		return EIO;
39189a4204bSDavid van Moolenbroek 
39289a4204bSDavid van Moolenbroek 	/* Prepare the request message. */
39389a4204bSDavid van Moolenbroek 	memset(&dev_mess, 0, sizeof(dev_mess));
39489a4204bSDavid van Moolenbroek 	dev_mess.m_type = CDEV_CANCEL;
39589a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_cancel.minor = minor_dev;
39689a4204bSDavid van Moolenbroek 	dev_mess.m_vfs_lchardriver_cancel.id = fp->fp_endpoint;
39789a4204bSDavid van Moolenbroek 
39889a4204bSDavid van Moolenbroek 	/* Send the request to the driver. */
39989a4204bSDavid van Moolenbroek 	if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK)
40089a4204bSDavid van Moolenbroek 		panic("VFS: asynsend in cdev_cancel failed: %d", r);
40189a4204bSDavid van Moolenbroek 
40289a4204bSDavid van Moolenbroek 	/* Suspend this thread until we have received the response. */
40389a4204bSDavid van Moolenbroek 	self->w_task = dp->dmap_driver;
40489a4204bSDavid van Moolenbroek 	self->w_drv_sendrec = &dev_mess;
40589a4204bSDavid van Moolenbroek 
40689a4204bSDavid van Moolenbroek 	worker_wait();
40789a4204bSDavid van Moolenbroek 
40889a4204bSDavid van Moolenbroek 	self->w_task = NONE;
40989a4204bSDavid van Moolenbroek 	assert(self->w_drv_sendrec == NULL);
41089a4204bSDavid van Moolenbroek 
41189a4204bSDavid van Moolenbroek 	/* Clean up. */
41289a4204bSDavid van Moolenbroek 	if (GRANT_VALID(grant))
41389a4204bSDavid van Moolenbroek 		(void)cpf_revoke(grant);
41489a4204bSDavid van Moolenbroek 
41589a4204bSDavid van Moolenbroek 	/* Return the result.  Note that the request may have completed. */
41689a4204bSDavid van Moolenbroek 	r = dev_mess.m_lchardriver_vfs_reply.status;
41789a4204bSDavid van Moolenbroek 
41889a4204bSDavid van Moolenbroek 	return (r == EAGAIN) ? EINTR : r; /* see below regarding error codes */
41989a4204bSDavid van Moolenbroek }
42089a4204bSDavid van Moolenbroek 
42189a4204bSDavid van Moolenbroek /*
42289a4204bSDavid van Moolenbroek  * A character driver has results for an open, close, read, write, or ioctl
42389a4204bSDavid van Moolenbroek  * call (i.e., everything except select).  There may be a thread waiting for
42489a4204bSDavid van Moolenbroek  * these results as part of an ongoing open, close, or (for read/write/ioctl)
42589a4204bSDavid van Moolenbroek  * cancel call.  If so, wake up that thread; if not, send a reply to the
42689a4204bSDavid van Moolenbroek  * requesting process. This function MUST NOT block its calling thread.
42789a4204bSDavid van Moolenbroek  */
42889a4204bSDavid van Moolenbroek static void
cdev_generic_reply(message * m_ptr)42989a4204bSDavid van Moolenbroek cdev_generic_reply(message * m_ptr)
43089a4204bSDavid van Moolenbroek {
43189a4204bSDavid van Moolenbroek 	struct fproc *rfp;
43289a4204bSDavid van Moolenbroek 	struct worker_thread *wp;
43389a4204bSDavid van Moolenbroek 	endpoint_t proc_e;
43489a4204bSDavid van Moolenbroek 	int r, slot;
43589a4204bSDavid van Moolenbroek 
43689a4204bSDavid van Moolenbroek 	proc_e = m_ptr->m_lchardriver_vfs_reply.id;
43789a4204bSDavid van Moolenbroek 
43889a4204bSDavid van Moolenbroek 	if (m_ptr->m_lchardriver_vfs_reply.status == SUSPEND) {
43989a4204bSDavid van Moolenbroek 		printf("VFS: ignoring SUSPEND status from %d\n",
44089a4204bSDavid van Moolenbroek 		    m_ptr->m_source);
44189a4204bSDavid van Moolenbroek 		return;
44289a4204bSDavid van Moolenbroek 	}
44389a4204bSDavid van Moolenbroek 
44489a4204bSDavid van Moolenbroek 	if (isokendpt(proc_e, &slot) != OK) {
44589a4204bSDavid van Moolenbroek 		printf("VFS: proc %d from %d not found\n",
44689a4204bSDavid van Moolenbroek 		    proc_e, m_ptr->m_source);
44789a4204bSDavid van Moolenbroek 		return;
44889a4204bSDavid van Moolenbroek 	}
44989a4204bSDavid van Moolenbroek 	rfp = &fproc[slot];
45089a4204bSDavid van Moolenbroek 	wp = rfp->fp_worker;
45189a4204bSDavid van Moolenbroek 	if (wp != NULL && wp->w_task == who_e && wp->w_drv_sendrec != NULL) {
45289a4204bSDavid van Moolenbroek 		assert(!fp_is_blocked(rfp));
45389a4204bSDavid van Moolenbroek 		*wp->w_drv_sendrec = *m_ptr;
45489a4204bSDavid van Moolenbroek 		wp->w_drv_sendrec = NULL;
45589a4204bSDavid van Moolenbroek 		worker_signal(wp);	/* continue open/close/cancel */
45689a4204bSDavid van Moolenbroek 	} else if (rfp->fp_blocked_on != FP_BLOCKED_ON_CDEV ||
45789a4204bSDavid van Moolenbroek 	    rfp->fp_cdev.endpt != m_ptr->m_source) {
45889a4204bSDavid van Moolenbroek 		/*
45989a4204bSDavid van Moolenbroek 		 * This would typically be caused by a protocol error, i.e., a
46089a4204bSDavid van Moolenbroek 		 * driver not properly following the character driver protocol.
46189a4204bSDavid van Moolenbroek 		 */
46289a4204bSDavid van Moolenbroek 		printf("VFS: proc %d not blocked on %d\n",
46389a4204bSDavid van Moolenbroek 		    proc_e, m_ptr->m_source);
46489a4204bSDavid van Moolenbroek 	} else {
46589a4204bSDavid van Moolenbroek 		/*
466*c5da0dffSDavid van Moolenbroek 		 * Some services use the same infrastructure for nonblocking
467*c5da0dffSDavid van Moolenbroek 		 * and cancelled requests, resulting in one of EINTR or EAGAIN
468*c5da0dffSDavid van Moolenbroek 		 * when the other is really the appropriate code.  Thus,
469*c5da0dffSDavid van Moolenbroek 		 * cdev_cancel converts EAGAIN into EINTR, and we convert EINTR
470*c5da0dffSDavid van Moolenbroek 		 * into EAGAIN here.  TODO: this may be obsolete by now..?
47189a4204bSDavid van Moolenbroek 		 */
47289a4204bSDavid van Moolenbroek 		r = m_ptr->m_lchardriver_vfs_reply.status;
47389a4204bSDavid van Moolenbroek 		revive(proc_e, (r == EINTR) ? EAGAIN : r);
47489a4204bSDavid van Moolenbroek 	}
47589a4204bSDavid van Moolenbroek }
47689a4204bSDavid van Moolenbroek 
47789a4204bSDavid van Moolenbroek /*
47889a4204bSDavid van Moolenbroek  * A character driver has results for us.
47989a4204bSDavid van Moolenbroek  */
48089a4204bSDavid van Moolenbroek void
cdev_reply(void)48189a4204bSDavid van Moolenbroek cdev_reply(void)
48289a4204bSDavid van Moolenbroek {
48389a4204bSDavid van Moolenbroek 
484e3b8d4bbSDavid van Moolenbroek 	if (get_dmap_by_endpt(who_e) == NULL) {
48589a4204bSDavid van Moolenbroek 		printf("VFS: ignoring char dev reply from unknown driver %d\n",
48689a4204bSDavid van Moolenbroek 		    who_e);
48789a4204bSDavid van Moolenbroek 		return;
48889a4204bSDavid van Moolenbroek 	}
48989a4204bSDavid van Moolenbroek 
49089a4204bSDavid van Moolenbroek 	switch (call_nr) {
49189a4204bSDavid van Moolenbroek 	case CDEV_REPLY:
49289a4204bSDavid van Moolenbroek 		cdev_generic_reply(&m_in);
49389a4204bSDavid van Moolenbroek 		break;
49489a4204bSDavid van Moolenbroek 	case CDEV_SEL1_REPLY:
495e3b8d4bbSDavid van Moolenbroek 		select_cdev_reply1(m_in.m_source,
496e3b8d4bbSDavid van Moolenbroek 		    m_in.m_lchardriver_vfs_sel1.minor,
49789a4204bSDavid van Moolenbroek 		    m_in.m_lchardriver_vfs_sel1.status);
49889a4204bSDavid van Moolenbroek 		break;
49989a4204bSDavid van Moolenbroek 	case CDEV_SEL2_REPLY:
500e3b8d4bbSDavid van Moolenbroek 		select_cdev_reply2(m_in.m_source,
501e3b8d4bbSDavid van Moolenbroek 		    m_in.m_lchardriver_vfs_sel2.minor,
50289a4204bSDavid van Moolenbroek 		    m_in.m_lchardriver_vfs_sel2.status);
50389a4204bSDavid van Moolenbroek 		break;
50489a4204bSDavid van Moolenbroek 	default:
50589a4204bSDavid van Moolenbroek 		printf("VFS: char driver %u sent unknown reply %x\n",
50689a4204bSDavid van Moolenbroek 		    who_e, call_nr);
50789a4204bSDavid van Moolenbroek 	}
50889a4204bSDavid van Moolenbroek }
509