Minix1.5/fs/device.c

Compare this file to the similar file:
Show the results in this format:

/* When a needed block is not in the cache, it must be fetched from the disk.
 * Special character files also require I/O.  The routines for these are here.
 *
 * The entry points in this file are:
 *   dev_open:	 called when a special file is opened
 *   dev_close:  called when a special file is closed
 *   dev_io:	 perform a read or write on a block or character device
 *   do_ioctl:	 perform the IOCTL system call
 *   rw_dev:	 procedure that actually calls the kernel tasks
 *   rw_dev2:	 procedure that actually calls task for /dev/tty
 *   no_call:	 dummy procedure (e.g., used when device need not be opened)
 *   tty_open:   a tty has been opened
 *   tty_exit:   a process with pid=pgrp has exited.
 */

#include "fs.h"
#include <minix/com.h>
#include "dev.h"
#include "file.h"
#include "fproc.h"
#include "inode.h"
#include "param.h"

PRIVATE message dev_mess;
PRIVATE major, minor, task;

FORWARD void find_dev();

/*===========================================================================*
 *				dev_open				     *
 *===========================================================================*/
PUBLIC int dev_open(rip, mod, nonblock)
struct inode *rip;		/* pointer to the inode */
int mod;			/* how to open it */
int nonblock;			/* TRUE if nonblocking open */
{
/* Special files may need special processing upon open. */

  dev_t dev;

  if (rip->i_count > 1) return(OK);
  dev = (dev_t) rip->i_zone[0];	/* device type */
  find_dev(dev);
  dev_mess.DEVICE = dev;
  (*dmap[major].dmap_open)(task, &dev_mess);
  return(dev_mess.REP_STATUS);
}


/*===========================================================================*
 *				dev_close				     *
 *===========================================================================*/
PUBLIC void dev_close(rip)
struct inode *rip;		/* ptr to the inode */
{
/* This procedure can be used when a special file needs to be closed. */

  dev_t dev;			/* which device to close */

  if (rip->i_count > 1) return;
  dev = (dev_t) rip->i_zone[0];
  find_dev(dev);
  (*dmap[major].dmap_close)(task, &dev_mess);
}


/*===========================================================================*
 *				dev_io					     *
 *===========================================================================*/
PUBLIC int dev_io(rw_flag, nonblock, dev, pos, bytes, proc, buff)
int rw_flag;			/* READING or WRITING */
int nonblock;			/* TRUE if nonblocking op */
dev_t dev;			/* major-minor device number */
off_t pos;			/* byte position */
int bytes;			/* how many bytes to transfer */
int proc;			/* in whose address space is buff? */
char *buff;			/* virtual address of the buffer */
{
/* Read or write from a device.  The parameter 'dev' tells which one. */

  find_dev(dev);

  /* Set up the message passed to task. */
  dev_mess.m_type   = (rw_flag == READING ? DISK_READ :
		       rw_flag == WRITING ? DISK_WRITE : rw_flag);
  dev_mess.DEVICE   = (dev >> MINOR) & BYTE;
  dev_mess.POSITION = pos;
  dev_mess.PROC_NR  = proc;
  dev_mess.ADDRESS  = buff;
  dev_mess.COUNT    = bytes;
  dev_mess.TTY_FLAGS = nonblock; /* temporary kludge */

  /* Call the task. */
  (*dmap[major].dmap_rw)(task, &dev_mess);

  /* Task has completed.  See if call completed. */
  if (dev_mess.REP_STATUS == SUSPEND) suspend(task);	/* suspend user */

  return(dev_mess.REP_STATUS);
}


/*===========================================================================*
 *				do_ioctl				     *
 *===========================================================================*/
PUBLIC int do_ioctl()
{
/* Perform the ioctl(ls_fd, request, argx) system call (uses m2 fmt). */

  struct filp *f;
  register struct inode *rip;

  if ( (f = get_filp(ls_fd)) == NIL_FILP) return(err_code);
  rip = f->filp_ino;		/* get inode pointer */
  if ( (rip->i_mode & I_TYPE) != I_CHAR_SPECIAL) return(ENOTTY);
  find_dev(rip->i_zone[0]);

  dev_mess.m_type  = TTY_IOCTL;
  dev_mess.PROC_NR = who;
  dev_mess.TTY_LINE = minor;	
  dev_mess.TTY_REQUEST = m.TTY_REQUEST;
  dev_mess.TTY_SPEK = m.TTY_SPEK;
  dev_mess.TTY_FLAGS = m.TTY_FLAGS;
  /* Call the task. */
  (*dmap[major].dmap_rw)(task, &dev_mess);

  /* Task has completed.  See if call completed. */
  if (dev_mess.m_type == SUSPEND) suspend(task);  /* User must be suspended. */
  m1.TTY_SPEK = dev_mess.TTY_SPEK;	/* erase and kill */
  m1.TTY_FLAGS = dev_mess.TTY_FLAGS;	/* flags */
  return(dev_mess.REP_STATUS);
}


/*===========================================================================*
 *				find_dev				     *
 *===========================================================================*/
PRIVATE void find_dev(dev)
dev_t dev;			/* device */
{
/* Extract the major and minor device number from the parameter. */

  major = (dev >> MAJOR) & BYTE;	/* major device number */
  minor = (dev >> MINOR) & BYTE;	/* minor device number */
  if (major == 0 || major >= max_major) panic("bad major dev", major);
  task = dmap[major].dmap_task;	/* which task services the device */
}


/*===========================================================================*
 *				rw_dev					     *
 *===========================================================================*/
PUBLIC void rw_dev(task_nr, mess_ptr)
int task_nr;			/* which task to call */
message *mess_ptr;		/* pointer to message for task */
{
/* All file system I/O ultimately comes down to I/O on major/minor device
 * pairs.  These lead to calls on the following routines via the dmap table.
 */

  int r;
  message m;

  while ((r = sendrec(task_nr, mess_ptr)) == ELOCKED) {
	/* sendrec() failed to avoid deadlock. The task 'task_nr' is
	 * trying to send a REVIVE message for an earlier request.
	 * Handle it and go try again.
	 */
	if (receive(task_nr, &m) != OK) panic("rw_dev: can't receive", NO_NUM);

	/* If we're trying to send a cancel message to a task which has just
	 * sent a completion reply, ignore the reply and abort the cancel
	 * request. The caller will do the revive for the process. 
	 */
	if (mess_ptr->m_type == CANCEL && m.REP_PROC_NR == mess_ptr->PROC_NR)
		return;
	revive(m.REP_PROC_NR, m.REP_STATUS);
  }
  if (r != OK) panic("rw_dev: can't send", NO_NUM);
}


/*===========================================================================*
 *				rw_dev2					     *
 *===========================================================================*/
PUBLIC void rw_dev2(dummy, mess_ptr)
int dummy;			/* not used - for compatibility with rw_dev() */
message *mess_ptr;		/* pointer to message for task */
{
/* This routine is only called for one device, namely /dev/tty.  Its job
 * is to change the message to use the controlling terminal, instead of the
 * major/minor pair for /dev/tty itself.
 */

  int task_nr, major_device;

  if (fp->fs_tty == 0) {
	mess_ptr->DEVICE = NULL_DEV;
	rw_dev(MEM, mess_ptr);
	return;
  }
  major_device = (fp->fs_tty >> MAJOR) & BYTE;
  task_nr = dmap[major_device].dmap_task;	/* task for controlling tty */
  mess_ptr->DEVICE = (fp->fs_tty >> MINOR) & BYTE;
  rw_dev(task_nr, mess_ptr);
}


/*===========================================================================*
 *				no_call					     *
 *===========================================================================*/
PUBLIC void no_call(task_nr, m_ptr)
int task_nr;			/* which task */
message *m_ptr;			/* message pointer */
{
/* Null operation always succeeds. */

  m_ptr->REP_STATUS = OK;
}

/*===========================================================================*
 *				tty_open				     *
 *===========================================================================*/
PUBLIC void tty_open(task_nr, mess_ptr)
int task_nr;
message *mess_ptr;
{
  register struct fproc *rfp;
  int maj;

  mess_ptr->REP_STATUS = OK;

  /* Is this a process group leader? */
  if (fp->fp_pid != fp->fp_pgrp) return;

  /* Is there a current control terminal? */
  if (fp->fs_tty != 0) return;

  /* Is this one already allocated to another process? */
  for (rfp = &fproc[INIT_PROC_NR + 1]; rfp < &fproc[NR_PROCS]; rfp++)
	if (rfp->fs_tty == mess_ptr->DEVICE) return;

  /* All conditions satisfied.  Make this a control terminal. */
  fp->fs_tty = mess_ptr->DEVICE;
  maj = (mess_ptr->DEVICE >> MAJOR) & BYTE;
  mess_ptr->DEVICE = (mess_ptr->DEVICE >> MINOR) & BYTE;
  mess_ptr->m_type = TTY_SETPGRP;
  mess_ptr->PROC_NR = who;
  mess_ptr->TTY_PGRP = who;
  (*dmap[maj].dmap_rw)(task_nr, mess_ptr);
}

/*===========================================================================*
 *				tty_exit				     *
 *===========================================================================*/
PUBLIC int tty_exit()
{
/* Process group leader exits. Remove its control terminal
 * from any processes currently running.
 */

  register struct fproc *rfp;
  register dev_t ttydev;

  ttydev = fp->fs_tty;
  for (rfp = &fproc[INIT_PROC_NR + 1]; rfp < &fproc[NR_PROCS]; rfp++)
	if (rfp->fs_tty == ttydev)
		rfp->fs_tty = 0;
  /* Inform the terminal driver. */
  find_dev(ttydev);
  dev_mess.m_type = TTY_SETPGRP;
  dev_mess.DEVICE = (ttydev >> MINOR) & BYTE;
  dev_mess.PROC_NR = who;
  dev_mess.TTY_PGRP = 0;
  (*dmap[major].dmap_rw)(task, &dev_mess);
  return(OK);
}