599 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			599 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* This file contains the device independent character driver interface.
 | |
|  *
 | |
|  * Character drivers support the following requests. Message format m10 is
 | |
|  * used. Field names are prefixed with CDEV_. Separate field names are used for
 | |
|  * the "access", "ops", "user", and "request" fields.
 | |
|  *
 | |
|  *     m_type      MINOR    GRANT   COUNT   FLAGS   ID     POS_LO    POS_HI
 | |
|  * +-------------+-------+--------+-------+-------+------+---------+--------+
 | |
|  * | CDEV_OPEN   | minor | access | user  |       |  id  |         |        |
 | |
|  * |-------------+-------+--------+-------+-------+------+---------+--------|
 | |
|  * | CDEV_CLOSE  | minor |        |       |       |  id  |         |        |
 | |
|  * |-------------+-------+--------+-------+-------+------+---------+--------|
 | |
|  * | CDEV_READ   | minor | grant  | bytes | flags |  id  |     position     |
 | |
|  * |-------------+-------+--------+-------+-------+------+---------+--------|
 | |
|  * | CDEV_WRITE  | minor | grant  | bytes | flags |  id  |     position     |
 | |
|  * |-------------+-------+--------+-------+-------+------+---------+--------|
 | |
|  * | CDEV_IOCTL  | minor | grant  | user  | flags |  id  | request |        |
 | |
|  * |-------------+-------+--------+-------+-------+------+---------+--------|
 | |
|  * | CDEV_CANCEL | minor |        |       |       |  id  |         |        |
 | |
|  * |-------------+-------+--------+-------+-------+------+---------+--------|
 | |
|  * | CDEV_SELECT | minor |  ops   |       |       |      |         |        |
 | |
|  * --------------------------------------------------------------------------
 | |
|  *
 | |
|  * The following reply messages are used.
 | |
|  *
 | |
|  *       m_type        MINOR   STATUS                ID
 | |
|  * +-----------------+-------+--------+-----+-----+------+---------+--------+
 | |
|  * | CDEV_REPLY      |       | status |     |     |  id  |         |        |
 | |
|  * |-----------------+-------+--------+-----+-----+------+---------+--------|
 | |
|  * | CDEV_SEL1_REPLY | minor | status |     |     |      |         |        |
 | |
|  * |-----------------+-------+--------+-----+-----+------+---------+--------|
 | |
|  * | CDEV_SEL2_REPLY | minor | status |     |     |      |         |        |
 | |
|  * --------------------------------------------------------------------------
 | |
|  *
 | |
|  * Changes:
 | |
|  *   Sep 01, 2013   complete rewrite of the API  (D.C. van Moolenboek)
 | |
|  *   Aug 20, 2013   retire synchronous protocol  (D.C. van Moolenbroek)
 | |
|  *   Oct 16, 2011   split character and block protocol  (D.C. van Moolenbroek)
 | |
|  *   Aug 27, 2011   move common functions into driver.c  (A. Welzel)
 | |
|  *   Jul 25, 2005   added SYS_SIG type for signals  (Jorrit N. Herder)
 | |
|  *   Sep 15, 2004   added SYN_ALARM type for timeouts  (Jorrit N. Herder)
 | |
|  *   Jul 23, 2004   removed kernel dependencies  (Jorrit N. Herder)
 | |
|  *   Apr 02, 1992   constructed from AT wini and floppy driver  (Kees J. Bot)
 | |
|  */
 | |
| 
 | |
| #include <assert.h>
 | |
| 
 | |
| #include <minix/drivers.h>
 | |
| #include <minix/chardriver.h>
 | |
| #include <minix/ds.h>
 | |
| 
 | |
| static int running;
 | |
| 
 | |
| /* Management data for opened devices. */
 | |
| static devminor_t open_devs[MAX_NR_OPEN_DEVICES];
 | |
| static int next_open_devs_slot = 0;
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				clear_open_devs				     *
 | |
|  *===========================================================================*/
 | |
| static void clear_open_devs(void)
 | |
| {
 | |
| /* Reset the set of previously opened minor devices. */
 | |
|   next_open_devs_slot = 0;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				is_open_dev				     *
 | |
|  *===========================================================================*/
 | |
| static int is_open_dev(devminor_t minor)
 | |
| {
 | |
| /* Check whether the given minor device has previously been opened. */
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < next_open_devs_slot; i++)
 | |
| 	if (open_devs[i] == minor)
 | |
| 		return TRUE;
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				set_open_dev				     *
 | |
|  *===========================================================================*/
 | |
| static void set_open_dev(devminor_t minor)
 | |
| {
 | |
| /* Mark the given minor device as having been opened. */
 | |
| 
 | |
|   if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES)
 | |
| 	panic("out of slots for open devices");
 | |
| 
 | |
|   open_devs[next_open_devs_slot] = minor;
 | |
|   next_open_devs_slot++;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_announce			     *
 | |
|  *===========================================================================*/
 | |
| void chardriver_announce(void)
 | |
| {
 | |
| /* Announce we are up after a fresh start or restart. */
 | |
|   int r;
 | |
|   char key[DS_MAX_KEYLEN];
 | |
|   char label[DS_MAX_KEYLEN];
 | |
|   char *driver_prefix = "drv.chr.";
 | |
| 
 | |
|   /* Callers are allowed to use ipc_sendrec to communicate with drivers.
 | |
|    * For this reason, there may blocked callers when a driver restarts.
 | |
|    * Ask the kernel to unblock them (if any).
 | |
|    */
 | |
|   if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK)
 | |
| 	panic("chardriver_announce: sys_statectl failed: %d", r);
 | |
| 
 | |
|   /* Publish a driver up event. */
 | |
|   if ((r = ds_retrieve_label_name(label, sef_self())) != OK)
 | |
| 	panic("chardriver_announce: unable to get own label: %d", r);
 | |
| 
 | |
|   snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
 | |
|   if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
 | |
| 	panic("chardriver_announce: unable to publish driver up event: %d", r);
 | |
| 
 | |
|   /* Expect an open for any device before serving regular driver requests. */
 | |
|   clear_open_devs();
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_reply_task			     *
 | |
|  *===========================================================================*/
 | |
| void chardriver_reply_task(endpoint_t endpt, cdev_id_t id, int r)
 | |
| {
 | |
| /* Reply to a (read, write, ioctl) task request that was suspended earlier.
 | |
|  * Not-so-well-written drivers may use this function to send a reply to a
 | |
|  * request that is being processed right now, and then return EDONTREPLY later.
 | |
|  */
 | |
|   message m_reply;
 | |
| 
 | |
|   if (r == EDONTREPLY || r == SUSPEND)
 | |
| 	panic("chardriver: bad task reply: %d", r);
 | |
| 
 | |
|   memset(&m_reply, 0, sizeof(m_reply));
 | |
| 
 | |
|   m_reply.m_type = CDEV_REPLY;
 | |
|   m_reply.m_lchardriver_vfs_reply.status = r;
 | |
|   m_reply.m_lchardriver_vfs_reply.id = id;
 | |
| 
 | |
|   if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
 | |
| 	printf("chardriver_reply_task: send to %d failed: %d\n", endpt, r);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_reply_select			     *
 | |
|  *===========================================================================*/
 | |
| void chardriver_reply_select(endpoint_t endpt, devminor_t minor, int r)
 | |
| {
 | |
| /* Reply to a select request with a status update. This must not be used to
 | |
|  * reply to a select request that is being processed right now.
 | |
|  */
 | |
|   message m_reply;
 | |
| 
 | |
|   /* Replying with an error is allowed (if unusual). */
 | |
|   if (r == EDONTREPLY || r == SUSPEND)
 | |
| 	panic("chardriver: bad select reply: %d", r);
 | |
| 
 | |
|   memset(&m_reply, 0, sizeof(m_reply));
 | |
| 
 | |
|   m_reply.m_type = CDEV_SEL2_REPLY;
 | |
|   m_reply.m_lchardriver_vfs_sel2.minor = minor;
 | |
|   m_reply.m_lchardriver_vfs_sel2.status = r;
 | |
| 
 | |
|   if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
 | |
| 	printf("chardriver_reply_select: send to %d failed: %d\n", endpt, r);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				send_reply				     *
 | |
|  *===========================================================================*/
 | |
| static void send_reply(endpoint_t endpt, message *m_ptr, int ipc_status)
 | |
| {
 | |
| /* Send a reply message to a request. */
 | |
|   int r;
 | |
| 
 | |
|   /* If we would block sending the message, send it asynchronously. */
 | |
|   if (IPC_STATUS_CALL(ipc_status) == SENDREC)
 | |
| 	r = ipc_sendnb(endpt, m_ptr);
 | |
|   else
 | |
| 	r = asynsend3(endpt, m_ptr, AMF_NOREPLY);
 | |
| 
 | |
|   if (r != OK)
 | |
| 	printf("chardriver: unable to send reply to %d: %d\n", endpt, r);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_reply			     *
 | |
|  *===========================================================================*/
 | |
| static void chardriver_reply(message *mess, int ipc_status, int r)
 | |
| {
 | |
| /* Prepare and send a reply message. */
 | |
|   message reply_mess;
 | |
| 
 | |
|   /* If the EDONTREPLY pseudo-reply is given, we do not reply. This is however
 | |
|    * allowed only for blocking task calls. Perform a sanity check.
 | |
|    */
 | |
|   if (r == EDONTREPLY) {
 | |
| 	switch (mess->m_type) {
 | |
| 	case CDEV_READ:
 | |
| 	case CDEV_WRITE:
 | |
| 	case CDEV_IOCTL:
 | |
| 		/* FIXME: we should be able to check FLAGS against
 | |
| 		 * CDEV_NONBLOCK here, but in practice, several drivers do not
 | |
| 		 * send a reply through this path (eg TTY) or simply do not
 | |
| 		 * implement nonblocking calls properly (eg audio, LWIP).
 | |
| 		 */
 | |
| #if 0
 | |
| 		if (mess->m_vfs_lchardriver_readwrite.flags & CDEV_NONBLOCK)
 | |
| 			panic("chardriver: cannot suspend nonblocking I/O");
 | |
| #endif
 | |
| 		/*fall-through*/
 | |
| 	case CDEV_CANCEL:
 | |
| 		return;	/* alright */
 | |
| 	default:
 | |
| 		panic("chardriver: cannot suspend request %d", mess->m_type);
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   if (r == SUSPEND)
 | |
| 	panic("chardriver: SUSPEND should not be used anymore");
 | |
| 
 | |
|   /* Do not reply with ERESTART. The only possible caller, VFS, will find out
 | |
|    * through other means when we have restarted, and is not (fully) ready to
 | |
|    * deal with ERESTART errors.
 | |
|    */
 | |
|   if (r == ERESTART)
 | |
| 	return;
 | |
| 
 | |
|   memset(&reply_mess, 0, sizeof(reply_mess));
 | |
| 
 | |
|   switch (mess->m_type) {
 | |
|   case CDEV_OPEN:
 | |
|   case CDEV_CLOSE:
 | |
| 	reply_mess.m_type = CDEV_REPLY;
 | |
| 	reply_mess.m_lchardriver_vfs_reply.status = r;
 | |
| 	reply_mess.m_lchardriver_vfs_reply.id =
 | |
| 		mess->m_vfs_lchardriver_openclose.id;
 | |
| 	break;
 | |
| 
 | |
|   case CDEV_READ:
 | |
|   case CDEV_WRITE:
 | |
|   case CDEV_IOCTL:
 | |
| 	reply_mess.m_type = CDEV_REPLY;
 | |
| 	reply_mess.m_lchardriver_vfs_reply.status = r;
 | |
| 	reply_mess.m_lchardriver_vfs_reply.id =
 | |
| 		mess->m_vfs_lchardriver_readwrite.id;
 | |
| 	break;
 | |
| 
 | |
|   case CDEV_CANCEL: /* For cancel, this is a reply to the original request! */
 | |
| 	reply_mess.m_type = CDEV_REPLY;
 | |
| 	reply_mess.m_lchardriver_vfs_reply.status = r;
 | |
| 	reply_mess.m_lchardriver_vfs_reply.id =
 | |
| 		mess->m_vfs_lchardriver_cancel.id;
 | |
| 	break;
 | |
| 
 | |
|   case CDEV_SELECT:
 | |
| 	reply_mess.m_type = CDEV_SEL1_REPLY;
 | |
| 	reply_mess.m_lchardriver_vfs_sel1.status = r;
 | |
| 	reply_mess.m_lchardriver_vfs_sel1.minor =
 | |
| 		mess->m_vfs_lchardriver_select.minor;
 | |
| 	break;
 | |
| 
 | |
|   default:
 | |
| 	panic("chardriver: unknown request %d", mess->m_type);
 | |
|   }
 | |
| 
 | |
|   send_reply(mess->m_source, &reply_mess, ipc_status);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_open					     *
 | |
|  *===========================================================================*/
 | |
| static int do_open(struct chardriver *cdp, message *m_ptr)
 | |
| {
 | |
| /* Open a minor device. */
 | |
|   endpoint_t user_endpt;
 | |
|   devminor_t minor;
 | |
|   int r, access;
 | |
| 
 | |
|   /* Default action if no open hook is in place. */
 | |
|   if (cdp->cdr_open == NULL)
 | |
| 	return OK;
 | |
| 
 | |
|   /* Call the open hook. */
 | |
|   minor = m_ptr->m_vfs_lchardriver_openclose.minor;
 | |
|   access = m_ptr->m_vfs_lchardriver_openclose.access;
 | |
|   user_endpt = m_ptr->m_vfs_lchardriver_openclose.user;
 | |
| 
 | |
|   r = cdp->cdr_open(minor, access, user_endpt);
 | |
| 
 | |
|   /* If the device has been cloned, mark the new minor as open too. */
 | |
|   if (r >= 0 && (r & CDEV_CLONED)) {
 | |
| 	minor = r & ~(CDEV_CLONED | CDEV_CTTY);
 | |
| 	if (!is_open_dev(minor))
 | |
| 		set_open_dev(minor);
 | |
|   }
 | |
| 
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_close				     *
 | |
|  *===========================================================================*/
 | |
| static int do_close(struct chardriver *cdp, message *m_ptr)
 | |
| {
 | |
| /* Close a minor device. */
 | |
|   devminor_t minor;
 | |
| 
 | |
|   /* Default action if no close hook is in place. */
 | |
|   if (cdp->cdr_close == NULL)
 | |
| 	return OK;
 | |
| 
 | |
|   /* Call the close hook. */
 | |
|   minor = m_ptr->m_vfs_lchardriver_openclose.minor;
 | |
| 
 | |
|   return cdp->cdr_close(minor);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_trasnfer				     *
 | |
|  *===========================================================================*/
 | |
| static int do_transfer(struct chardriver *cdp, message *m_ptr, int do_write)
 | |
| {
 | |
| /* Carry out a read or write task request. */
 | |
|   devminor_t minor;
 | |
|   off_t position;
 | |
|   endpoint_t endpt;
 | |
|   cp_grant_id_t grant;
 | |
|   size_t size;
 | |
|   int flags;
 | |
|   cdev_id_t id;
 | |
|   ssize_t r;
 | |
| 
 | |
|   minor = m_ptr->m_vfs_lchardriver_readwrite.minor;
 | |
|   position = m_ptr->m_vfs_lchardriver_readwrite.pos;
 | |
|   endpt = m_ptr->m_source;
 | |
|   grant = m_ptr->m_vfs_lchardriver_readwrite.grant;
 | |
|   size = m_ptr->m_vfs_lchardriver_readwrite.count;
 | |
|   flags = m_ptr->m_vfs_lchardriver_readwrite.flags;
 | |
|   id = m_ptr->m_vfs_lchardriver_readwrite.id;
 | |
| 
 | |
|   /* Call the read/write hook, if the appropriate one is in place. */
 | |
|   if (!do_write && cdp->cdr_read != NULL)
 | |
| 	r = cdp->cdr_read(minor, position, endpt, grant, size, flags, id);
 | |
|   else if (do_write && cdp->cdr_write != NULL)
 | |
| 	r = cdp->cdr_write(minor, position, endpt, grant, size, flags, id);
 | |
|   else
 | |
| 	r = EIO; /* Default action if no read/write hook is in place. */
 | |
| 
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_ioctl				     *
 | |
|  *===========================================================================*/
 | |
| static int do_ioctl(struct chardriver *cdp, message *m_ptr)
 | |
| {
 | |
| /* Carry out an I/O control task request. */
 | |
|   devminor_t minor;
 | |
|   unsigned long request;
 | |
|   cp_grant_id_t grant;
 | |
|   endpoint_t endpt, user_endpt;
 | |
|   int flags;
 | |
|   cdev_id_t id;
 | |
| 
 | |
|   /* Default action if no ioctl hook is in place. */
 | |
|   if (cdp->cdr_ioctl == NULL)
 | |
| 	return ENOTTY;
 | |
| 
 | |
|   /* Call the ioctl hook. */
 | |
|   minor = m_ptr->m_vfs_lchardriver_readwrite.minor;
 | |
|   request = m_ptr->m_vfs_lchardriver_readwrite.request;
 | |
|   endpt = m_ptr->m_source;
 | |
|   grant = m_ptr->m_vfs_lchardriver_readwrite.grant;
 | |
|   flags = m_ptr->m_vfs_lchardriver_readwrite.flags;
 | |
|   user_endpt = m_ptr->m_vfs_lchardriver_readwrite.user;
 | |
|   id = m_ptr->m_vfs_lchardriver_readwrite.id;
 | |
| 
 | |
|   return cdp->cdr_ioctl(minor, request, endpt, grant, flags, user_endpt, id);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_cancel				     *
 | |
|  *===========================================================================*/
 | |
| static int do_cancel(struct chardriver *cdp, message *m_ptr)
 | |
| {
 | |
| /* Cancel a suspended (read, write, ioctl) task request. The original request
 | |
|  * may already have finished, in which case no reply should be sent.
 | |
|  */
 | |
|   devminor_t minor;
 | |
|   endpoint_t endpt;
 | |
|   cdev_id_t id;
 | |
| 
 | |
|   /* Default action if no cancel hook is in place: let the request finish. */
 | |
|   if (cdp->cdr_cancel == NULL)
 | |
| 	return EDONTREPLY;
 | |
| 
 | |
|   /* Call the cancel hook. */
 | |
|   minor = m_ptr->m_vfs_lchardriver_cancel.minor;
 | |
|   endpt = m_ptr->m_source;
 | |
|   id = m_ptr->m_vfs_lchardriver_cancel.id;
 | |
| 
 | |
|   return cdp->cdr_cancel(minor, endpt, id);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_select				     *
 | |
|  *===========================================================================*/
 | |
| static int do_select(struct chardriver *cdp, message *m_ptr)
 | |
| {
 | |
| /* Perform a select query on a minor device. */
 | |
|   devminor_t minor;
 | |
|   unsigned int ops;
 | |
|   endpoint_t endpt;
 | |
| 
 | |
|   /* Default action if no select hook is in place. */
 | |
|   if (cdp->cdr_select == NULL)
 | |
| 	return EBADF;
 | |
| 
 | |
|   /* Call the select hook. */
 | |
|   minor = m_ptr->m_vfs_lchardriver_select.minor;
 | |
|   ops = m_ptr->m_vfs_lchardriver_select.ops;
 | |
|   endpt = m_ptr->m_source;
 | |
| 
 | |
|   return cdp->cdr_select(minor, ops, endpt);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_block_open				     *
 | |
|  *===========================================================================*/
 | |
| static void do_block_open(message *m_ptr, int ipc_status)
 | |
| {
 | |
| /* Reply to a block driver open request stating there is no such device. */
 | |
|   message m_reply;
 | |
| 
 | |
|   memset(&m_reply, 0, sizeof(m_reply));
 | |
| 
 | |
|   m_reply.m_type = BDEV_REPLY;
 | |
|   m_reply.m_lblockdriver_lbdev_reply.status = ENXIO;
 | |
|   m_reply.m_lblockdriver_lbdev_reply.id = m_ptr->m_lbdev_lblockdriver_msg.id;
 | |
| 
 | |
|   send_reply(m_ptr->m_source, &m_reply, ipc_status);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_process			     *
 | |
|  *===========================================================================*/
 | |
| void chardriver_process(struct chardriver *cdp, message *m_ptr, int ipc_status)
 | |
| {
 | |
| /* Call the appropiate driver function, based on the type of request. Send a
 | |
|  * reply to the caller if necessary.
 | |
|  */
 | |
|   int r, reply;
 | |
| 
 | |
|   /* Check for notifications first. We never reply to notifications. */
 | |
|   if (is_ipc_notify(ipc_status)) {
 | |
| 	switch (_ENDPOINT_P(m_ptr->m_source)) {
 | |
| 	case HARDWARE:
 | |
| 		if (cdp->cdr_intr)
 | |
| 			cdp->cdr_intr(m_ptr->m_notify.interrupts);
 | |
| 		break;
 | |
| 
 | |
| 	case CLOCK:
 | |
| 		if (cdp->cdr_alarm)
 | |
| 			cdp->cdr_alarm(m_ptr->m_notify.timestamp);
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		if (cdp->cdr_other)
 | |
| 			cdp->cdr_other(m_ptr, ipc_status);
 | |
| 	}
 | |
| 
 | |
| 	return; /* do not send a reply */
 | |
|   }
 | |
| 
 | |
|   /* Reply to block driver open requests with an error code. Otherwise, if
 | |
|    * someone creates a block device node for a character driver, opening that
 | |
|    * device node will cause the corresponding VFS thread to block forever.
 | |
|    */
 | |
|   if (m_ptr->m_type == BDEV_OPEN) {
 | |
| 	do_block_open(m_ptr, ipc_status);
 | |
| 
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   if (IS_CDEV_RQ(m_ptr->m_type)) {
 | |
| 	int minor;
 | |
| 
 | |
| 	/* Try to retrieve minor device number */
 | |
| 	r = chardriver_get_minor(m_ptr, &minor);
 | |
| 
 | |
| 	if (OK != r)
 | |
| 		return;
 | |
| 
 | |
| 	/* We might get spurious requests if the driver has been restarted.
 | |
| 	 * Deny any requests on devices that have not previously been opened.
 | |
| 	 */
 | |
| 	if (!is_open_dev(minor)) {
 | |
| 		/* Ignore spurious requests for unopened devices. */
 | |
| 		if (m_ptr->m_type != CDEV_OPEN)
 | |
| 			return; /* do not send a reply */
 | |
| 
 | |
| 		/* Mark the device as opened otherwise. */
 | |
| 		set_open_dev(minor);
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   /* Call the appropriate function(s) for this request. */
 | |
|   switch (m_ptr->m_type) {
 | |
|   case CDEV_OPEN:	r = do_open(cdp, m_ptr);		break;
 | |
|   case CDEV_CLOSE:	r = do_close(cdp, m_ptr);		break;
 | |
|   case CDEV_READ:	r = do_transfer(cdp, m_ptr, FALSE);	break;
 | |
|   case CDEV_WRITE:	r = do_transfer(cdp, m_ptr, TRUE);	break;
 | |
|   case CDEV_IOCTL:	r = do_ioctl(cdp, m_ptr);		break;
 | |
|   case CDEV_CANCEL:	r = do_cancel(cdp, m_ptr);		break;
 | |
|   case CDEV_SELECT:	r = do_select(cdp, m_ptr);		break;
 | |
|   default:
 | |
| 	if (cdp->cdr_other)
 | |
| 		cdp->cdr_other(m_ptr, ipc_status);
 | |
| 	return; /* do not send a reply */
 | |
|   }
 | |
| 
 | |
|   chardriver_reply(m_ptr, ipc_status, r);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_terminate			     *
 | |
|  *===========================================================================*/
 | |
| void chardriver_terminate(void)
 | |
| {
 | |
| /* Break out of the main loop after finishing the current request. */
 | |
| 
 | |
|   running = FALSE;
 | |
| 
 | |
|   sef_cancel();
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_task				     *
 | |
|  *===========================================================================*/
 | |
| void chardriver_task(struct chardriver *cdp)
 | |
| {
 | |
| /* Main program of any character device driver task. */
 | |
|   int r, ipc_status;
 | |
|   message mess;
 | |
| 
 | |
|   running = TRUE;
 | |
| 
 | |
|   /* Here is the main loop of the character driver task.  It waits for a
 | |
|    * message, carries it out, and sends a reply.
 | |
|    */
 | |
|   while (running) {
 | |
| 	if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
 | |
| 		if (r == EINTR && !running)
 | |
| 			break;
 | |
| 
 | |
| 		panic("chardriver: sef_receive_status failed: %d", r);
 | |
| 	}
 | |
| 
 | |
| 	chardriver_process(cdp, &mess, ipc_status);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				chardriver_get_minor			     *
 | |
|  *===========================================================================*/
 | |
| int chardriver_get_minor(message *m, devminor_t *minor)
 | |
| {
 | |
|   assert(NULL != m);
 | |
|   assert(NULL != minor);
 | |
| 
 | |
|   switch(m->m_type)
 | |
|   {
 | |
| 	case CDEV_OPEN:
 | |
| 	case CDEV_CLOSE:
 | |
| 	    *minor = m->m_vfs_lchardriver_openclose.minor;
 | |
| 	    return OK;
 | |
| 	case CDEV_CANCEL:
 | |
| 	    *minor = m->m_vfs_lchardriver_cancel.minor;
 | |
| 	    return OK;
 | |
| 	case CDEV_SELECT:
 | |
| 	    *minor = m->m_vfs_lchardriver_select.minor;
 | |
| 	    return OK;
 | |
| 	case CDEV_READ:
 | |
| 	case CDEV_WRITE:
 | |
| 	case CDEV_IOCTL:
 | |
| 	    *minor = m->m_vfs_lchardriver_readwrite.minor;
 | |
| 	    return OK;
 | |
| 	default:
 | |
| 	    return EINVAL;
 | |
|   }
 | |
| }
 | 
