711 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			711 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* This file contains device independent device driver interface.
 | 
						|
 *
 | 
						|
 * Changes:
 | 
						|
 *   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)
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * The drivers support the following operations (using message format m2):
 | 
						|
 *
 | 
						|
 *    m_type         DEVICE   IO_ENDPT   COUNT   POSITION  HIGHPOS   IO_GRANT
 | 
						|
 * ----------------------------------------------------------------------------
 | 
						|
 * | DEV_OPEN      | device | proc nr |         |        |        |           |
 | 
						|
 * |---------------+--------+---------+---------+--------+--------+-----------|
 | 
						|
 * | DEV_CLOSE     | device | proc nr |         |        |        |           |
 | 
						|
 * |---------------+--------+---------+---------+--------+--------+-----------|
 | 
						|
 * | DEV_READ_S    | device | proc nr |  bytes  | off lo | off hi i buf grant |
 | 
						|
 * |---------------+--------+---------+---------+--------+--------+-----------|
 | 
						|
 * | DEV_WRITE_S   | device | proc nr |  bytes  | off lo | off hi | buf grant |
 | 
						|
 * |---------------+--------+---------+---------+--------+--------+-----------|
 | 
						|
 * | DEV_GATHER_S  | device | proc nr | iov len | off lo | off hi | iov grant |
 | 
						|
 * |---------------+--------+---------+---------+--------+--------+-----------|
 | 
						|
 * | DEV_SCATTER_S | device | proc nr | iov len | off lo | off hi | iov grant |
 | 
						|
 * |---------------+--------+---------+---------+--------+--------+-----------|
 | 
						|
 * | DEV_IOCTL_S   | device | proc nr | request |        |        | buf grant |
 | 
						|
 * |---------------+--------+---------+---------+--------+--------+-----------|
 | 
						|
 * | CANCEL        | device | proc nr |   r/w   |        |        |           |
 | 
						|
 * ----------------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * The file contains the following entry points:
 | 
						|
 *
 | 
						|
 *   driver_announce:	called by a device driver to announce it is up
 | 
						|
 *   driver_receive:	receive() interface for drivers
 | 
						|
 *   driver_receive_mq:	receive() interface for drivers with message queueing
 | 
						|
 *   driver_task:	called by the device dependent task entry
 | 
						|
 *   driver_init_buffer: initialize a DMA buffer
 | 
						|
 *   driver_mq_queue:	queue an incoming message for later processing
 | 
						|
 */
 | 
						|
 | 
						|
#include <minix/drivers.h>
 | 
						|
#include <sys/ioc_disk.h>
 | 
						|
#include <minix/mq.h>
 | 
						|
#include <minix/endpoint.h>
 | 
						|
#include <minix/driver.h>
 | 
						|
#include <minix/ds.h>
 | 
						|
 | 
						|
/* Claim space for variables. */
 | 
						|
u8_t *tmp_buf = NULL;		/* the DMA buffer eventually */
 | 
						|
phys_bytes tmp_phys;		/* phys address of DMA buffer */
 | 
						|
 | 
						|
FORWARD _PROTOTYPE( void clear_open_devs, (void) );
 | 
						|
FORWARD _PROTOTYPE( int is_open_dev, (int device) );
 | 
						|
FORWARD _PROTOTYPE( void set_open_dev, (int device) );
 | 
						|
 | 
						|
FORWARD _PROTOTYPE( void asyn_reply, (message *mess, int proc_nr, int r) );
 | 
						|
FORWARD _PROTOTYPE( int driver_reply, (endpoint_t caller_e, int caller_status,
 | 
						|
	message *m_ptr) );
 | 
						|
FORWARD _PROTOTYPE( int driver_spurious_reply, (endpoint_t caller_e,
 | 
						|
	int caller_status, message *m_ptr) );
 | 
						|
FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp) );
 | 
						|
FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp) );
 | 
						|
 | 
						|
int device_caller;
 | 
						|
PRIVATE mq_t *queue_head = NULL;
 | 
						|
PRIVATE int open_devs[MAX_NR_OPEN_DEVICES];
 | 
						|
PRIVATE int next_open_devs_slot = 0;
 | 
						|
PRIVATE int driver_running;
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			     clear_open_devs				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void clear_open_devs()
 | 
						|
{
 | 
						|
  next_open_devs_slot = 0;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			       is_open_dev				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int is_open_dev(int device)
 | 
						|
{
 | 
						|
  int i, open_dev_found;
 | 
						|
 | 
						|
  open_dev_found = FALSE;
 | 
						|
  for(i=0;i<next_open_devs_slot;i++) {
 | 
						|
	if(open_devs[i] == device) {
 | 
						|
		open_dev_found = TRUE;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
  }
 | 
						|
 | 
						|
  return open_dev_found;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			       set_open_dev				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void set_open_dev(int device)
 | 
						|
{
 | 
						|
  if(next_open_devs_slot >= MAX_NR_OPEN_DEVICES) {
 | 
						|
      panic("out of slots for open devices");
 | 
						|
  }
 | 
						|
  open_devs[next_open_devs_slot] = device;
 | 
						|
  next_open_devs_slot++;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				asyn_reply				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void asyn_reply(mess, proc_nr, r)
 | 
						|
message *mess;
 | 
						|
int proc_nr;
 | 
						|
int r;
 | 
						|
{
 | 
						|
/* Send a reply using the new asynchronous character device protocol.
 | 
						|
 */
 | 
						|
  message reply_mess;
 | 
						|
 | 
						|
  switch (mess->m_type) {
 | 
						|
  case DEV_OPEN:
 | 
						|
	reply_mess.m_type = DEV_REVIVE;
 | 
						|
	reply_mess.REP_ENDPT = proc_nr;
 | 
						|
	reply_mess.REP_STATUS = r;
 | 
						|
	break;
 | 
						|
 | 
						|
  case DEV_CLOSE:
 | 
						|
	reply_mess.m_type = DEV_CLOSE_REPL;
 | 
						|
	reply_mess.REP_ENDPT = proc_nr;
 | 
						|
	reply_mess.REP_STATUS = r;
 | 
						|
	break;
 | 
						|
 | 
						|
  case DEV_READ_S:
 | 
						|
  case DEV_WRITE_S:
 | 
						|
  case DEV_IOCTL_S:
 | 
						|
	if (r == SUSPEND)
 | 
						|
		printf("driver_task: reviving %d with SUSPEND\n", proc_nr);
 | 
						|
 | 
						|
	reply_mess.m_type = DEV_REVIVE;
 | 
						|
	reply_mess.REP_ENDPT = proc_nr;
 | 
						|
	reply_mess.REP_IO_GRANT = (cp_grant_id_t) mess->IO_GRANT;
 | 
						|
	reply_mess.REP_STATUS = r;
 | 
						|
	break;
 | 
						|
 | 
						|
  case CANCEL:
 | 
						|
	/* The original request should send a reply. */
 | 
						|
	return;
 | 
						|
 | 
						|
  case DEV_SELECT:
 | 
						|
	reply_mess.m_type = DEV_SEL_REPL1;
 | 
						|
	reply_mess.DEV_MINOR = mess->DEVICE;
 | 
						|
	reply_mess.DEV_SEL_OPS = r;
 | 
						|
	break;
 | 
						|
 | 
						|
  default:
 | 
						|
	reply_mess.m_type = TASK_REPLY;
 | 
						|
	reply_mess.REP_ENDPT = proc_nr;
 | 
						|
	/* Status is # of bytes transferred or error code. */
 | 
						|
	reply_mess.REP_STATUS = r;
 | 
						|
	break;
 | 
						|
  }
 | 
						|
 | 
						|
  r= asynsend(device_caller, &reply_mess);
 | 
						|
  if (r != OK)
 | 
						|
  {
 | 
						|
	printf("driver_task: unable to asynsend to %d: %d\n",
 | 
						|
		device_caller, r);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			       driver_reply				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int driver_reply(caller_e, caller_status, m_ptr)
 | 
						|
endpoint_t caller_e;
 | 
						|
int caller_status;
 | 
						|
message *m_ptr;
 | 
						|
{
 | 
						|
/* Reply to a message sent to the driver. */
 | 
						|
  int r;
 | 
						|
 | 
						|
  /* Use sendnb if caller is guaranteed to be blocked, asynsend otherwise. */
 | 
						|
  if(IPC_STATUS_CALL(caller_status) == SENDREC) {
 | 
						|
      r = sendnb(caller_e, m_ptr);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
      r = asynsend(caller_e, m_ptr);
 | 
						|
  }
 | 
						|
 | 
						|
  return r;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			    driver_spurious_reply			     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int driver_spurious_reply(caller_e, caller_status, m_ptr)
 | 
						|
endpoint_t caller_e;
 | 
						|
int caller_status;
 | 
						|
message *m_ptr;
 | 
						|
{
 | 
						|
/* Reply to a spurious message pretending to be dead. */
 | 
						|
  int r;
 | 
						|
 | 
						|
  m_ptr->m_type = TASK_REPLY;
 | 
						|
  m_ptr->REP_ENDPT = m_ptr->IO_ENDPT;
 | 
						|
  m_ptr->REP_STATUS = ERESTART;
 | 
						|
 | 
						|
  r = driver_reply(caller_e, caller_status, m_ptr);
 | 
						|
  if(r != OK) {
 | 
						|
	printf("unable to reply to spurious message from %d\n",
 | 
						|
		caller_e);
 | 
						|
  }
 | 
						|
 | 
						|
  return r;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			      driver_announce				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void driver_announce()
 | 
						|
{
 | 
						|
/* 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.vfs.";
 | 
						|
 | 
						|
  /* Callers are allowed to use sendrec to communicate with drivers.
 | 
						|
   * For this reason, there may blocked callers when a driver restarts.
 | 
						|
   * Ask the kernel to unblock them (if any).
 | 
						|
   */
 | 
						|
  r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS);
 | 
						|
  if (r != OK) {
 | 
						|
	panic("driver_announce: sys_statectl failed: %d\n", r);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Publish a driver up event. */
 | 
						|
  r = ds_retrieve_label_name(label, getprocnr());
 | 
						|
  if (r != OK) {
 | 
						|
	panic("driver_announce: unable to get own label: %d\n", r);
 | 
						|
  }
 | 
						|
  snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
 | 
						|
  r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE);
 | 
						|
  if (r != OK) {
 | 
						|
	panic("driver_announce: unable to publish driver up event: %d\n", r);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Expect a DEV_OPEN for any device before serving regular driver requests. */
 | 
						|
  clear_open_devs();
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				driver_receive				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int driver_receive(src, m_ptr, status_ptr)
 | 
						|
endpoint_t src;
 | 
						|
message *m_ptr;
 | 
						|
int *status_ptr;
 | 
						|
{
 | 
						|
/* receive() interface for drivers. */
 | 
						|
  int r;
 | 
						|
  int ipc_status;
 | 
						|
 | 
						|
  while (TRUE) {
 | 
						|
	/* Wait for a request. */
 | 
						|
	r = sef_receive_status(src, m_ptr, &ipc_status);
 | 
						|
	*status_ptr = ipc_status;
 | 
						|
	if (r != OK) {
 | 
						|
		return r;
 | 
						|
	}
 | 
						|
 | 
						|
	/* See if only DEV_OPEN is to be expected for this device. */
 | 
						|
	if(IS_DEV_MINOR_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->DEVICE)) {
 | 
						|
		if(m_ptr->m_type != DEV_OPEN) {
 | 
						|
			if(!is_ipc_asynch(ipc_status)) {
 | 
						|
				driver_spurious_reply(m_ptr->m_source,
 | 
						|
					ipc_status, m_ptr);
 | 
						|
			}
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		set_open_dev(m_ptr->DEVICE);
 | 
						|
	}
 | 
						|
 | 
						|
	break;
 | 
						|
  }
 | 
						|
 | 
						|
  return OK;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			       driver_receive_mq			     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int driver_receive_mq(m_ptr, status_ptr)
 | 
						|
message *m_ptr;
 | 
						|
int *status_ptr;
 | 
						|
{
 | 
						|
/* receive() interface for drivers with message queueing. */
 | 
						|
  int ipc_status;
 | 
						|
 | 
						|
  /* Any queued messages? Oldest are at the head. */
 | 
						|
  while(queue_head) {
 | 
						|
  	mq_t *mq;
 | 
						|
  	mq = queue_head;
 | 
						|
  	memcpy(m_ptr, &mq->mq_mess, sizeof(mq->mq_mess));
 | 
						|
  	ipc_status = mq->mq_mess_status;
 | 
						|
  	*status_ptr = ipc_status;
 | 
						|
  	queue_head = queue_head->mq_next;
 | 
						|
  	mq_free(mq);
 | 
						|
 | 
						|
  	/* See if only DEV_OPEN is to be expected for this device. */
 | 
						|
  	if(IS_DEV_MINOR_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->DEVICE)) {
 | 
						|
  		if(m_ptr->m_type != DEV_OPEN) {
 | 
						|
  			if(!is_ipc_asynch(ipc_status)) {
 | 
						|
				driver_spurious_reply(m_ptr->m_source,
 | 
						|
					ipc_status, m_ptr);
 | 
						|
  			}
 | 
						|
  			continue;
 | 
						|
  		}
 | 
						|
  		set_open_dev(m_ptr->DEVICE);
 | 
						|
  	}
 | 
						|
 | 
						|
  	return OK;
 | 
						|
  }
 | 
						|
 | 
						|
	/* Fall back to standard receive() interface for drivers. */
 | 
						|
	return driver_receive(ANY, m_ptr, status_ptr);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				driver_terminate			     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void driver_terminate(void)
 | 
						|
{
 | 
						|
/* Break out of the main driver loop after finishing the current request.
 | 
						|
 */
 | 
						|
 | 
						|
  driver_running = FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				driver_task				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void driver_task(dp, type)
 | 
						|
struct driver *dp;	/* Device dependent entry points. */
 | 
						|
int type;		/* Driver type (DRIVER_STD or DRIVER_ASYN) */
 | 
						|
{
 | 
						|
/* Main program of any device driver task. */
 | 
						|
 | 
						|
  int r, proc_nr, ipc_status;
 | 
						|
  message mess;
 | 
						|
 | 
						|
  driver_running = TRUE;
 | 
						|
 | 
						|
  /* Here is the main loop of the disk task.  It waits for a message, carries
 | 
						|
   * it out, and sends a reply.
 | 
						|
   */
 | 
						|
  while (driver_running) {
 | 
						|
	if ((r=driver_receive_mq(&mess, &ipc_status)) != OK) {
 | 
						|
		panic("driver_receive_mq failed: %d", r);
 | 
						|
	}
 | 
						|
	driver_handle_msg(dp, type, &mess, ipc_status);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                driver_handle_msg                                          *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int driver_handle_msg(dp, type, m_ptr, ipc_status)
 | 
						|
struct driver *dp;	/* Device dependent entry points. */
 | 
						|
int type; /* Driver type (DRIVER_STD or DRIVER_ASYN) */
 | 
						|
message *m_ptr; /* Pointer to message to handle */
 | 
						|
int ipc_status; 
 | 
						|
{
 | 
						|
  int r, proc_nr;
 | 
						|
 | 
						|
  device_caller = m_ptr->m_source;
 | 
						|
  proc_nr = m_ptr->IO_ENDPT;
 | 
						|
  if (is_ipc_notify(ipc_status)) {
 | 
						|
	switch (_ENDPOINT_P(m_ptr->m_source)) {
 | 
						|
		case HARDWARE:
 | 
						|
			/* leftover interrupt or expired timer. */
 | 
						|
			if(dp->dr_hw_int) {
 | 
						|
				(*dp->dr_hw_int)(dp, m_ptr);
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case CLOCK:
 | 
						|
			(*dp->dr_alarm)(dp, m_ptr);	
 | 
						|
			break;
 | 
						|
		default:		
 | 
						|
			if(dp->dr_other)
 | 
						|
				r = (*dp->dr_other)(dp, m_ptr);
 | 
						|
			else	
 | 
						|
				r = EINVAL;
 | 
						|
		    goto send_reply;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  switch(m_ptr->m_type) {
 | 
						|
	case DEV_OPEN:		r = (*dp->dr_open)(dp, m_ptr);	break;	
 | 
						|
	case DEV_CLOSE:		r = (*dp->dr_close)(dp, m_ptr);	break;
 | 
						|
	case DEV_IOCTL_S:	r = (*dp->dr_ioctl)(dp, m_ptr); break;
 | 
						|
	case CANCEL:		r = (*dp->dr_cancel)(dp, m_ptr);break;
 | 
						|
	case DEV_SELECT:	r = (*dp->dr_select)(dp, m_ptr);break;
 | 
						|
	case DEV_READ_S:	
 | 
						|
	case DEV_WRITE_S:  	r = do_rdwt(dp, m_ptr); break;
 | 
						|
	case DEV_GATHER_S: 
 | 
						|
	case DEV_SCATTER_S: 	r = do_vrdwt(dp, m_ptr); break;
 | 
						|
	default:		
 | 
						|
		if(dp->dr_other)
 | 
						|
			r = (*dp->dr_other)(dp, m_ptr);
 | 
						|
		else	
 | 
						|
			r = EINVAL;
 | 
						|
		break;
 | 
						|
  }
 | 
						|
 
 | 
						|
send_reply:
 | 
						|
  /* Clean up leftover state. */
 | 
						|
  (*dp->dr_cleanup)();
 | 
						|
  
 | 
						|
  /* Finally, prepare and send the reply m_ptrage. */
 | 
						|
  if (r == EDONTREPLY)
 | 
						|
	return 0;
 | 
						|
 | 
						|
  switch (type) {
 | 
						|
	case DRIVER_STD:
 | 
						|
		m_ptr->m_type = TASK_REPLY;
 | 
						|
		m_ptr->REP_ENDPT = proc_nr;
 | 
						|
		/* Status is # of bytes transferred or error code. */
 | 
						|
		m_ptr->REP_STATUS = r;
 | 
						|
 | 
						|
		r = driver_reply(device_caller, ipc_status, m_ptr);
 | 
						|
		
 | 
						|
		if (r != OK) {
 | 
						|
			printf("driver_task: unable to send reply to %d: %d\n",
 | 
						|
			device_caller, r);
 | 
						|
		}
 | 
						|
		
 | 
						|
		break;
 | 
						|
 | 
						|
	case DRIVER_ASYN:
 | 
						|
		asyn_reply(m_ptr, proc_nr, r);
 | 
						|
 | 
						|
		break;
 | 
						|
		
 | 
						|
	default:
 | 
						|
		panic("unknown driver type: %d", type);
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			     driver_init_buffer				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void driver_init_buffer(void)
 | 
						|
{
 | 
						|
/* Select a buffer that can safely be used for DMA transfers.  It may also
 | 
						|
 * be used to read partition tables and such.  Its absolute address is
 | 
						|
 * 'tmp_phys', the normal address is 'tmp_buf'.
 | 
						|
 */
 | 
						|
  vir_bytes size;
 | 
						|
 | 
						|
  if (tmp_buf == NULL) {
 | 
						|
  	size = MAX(2*DMA_BUF_SIZE, CD_SECTOR_SIZE);
 | 
						|
 | 
						|
	if(!(tmp_buf = alloc_contig(size, AC_ALIGN4K, &tmp_phys)))
 | 
						|
		panic("can't allocate tmp_buf: %lu", size);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				do_rdwt					     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int do_rdwt(dp, mp)
 | 
						|
struct driver *dp;		/* device dependent entry points */
 | 
						|
message *mp;			/* pointer to read or write message */
 | 
						|
{
 | 
						|
/* Carry out a single read or write request. */
 | 
						|
  iovec_t iovec1;
 | 
						|
  int r, opcode;
 | 
						|
  u64_t position;
 | 
						|
 | 
						|
  /* Disk address?  Address and length of the user buffer? */
 | 
						|
  if (mp->COUNT < 0) return(EINVAL);
 | 
						|
 | 
						|
  /* Prepare for I/O. */
 | 
						|
  if ((*dp->dr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
 | 
						|
 | 
						|
  /* Create a one element scatter/gather vector for the buffer. */
 | 
						|
  if(mp->m_type == DEV_READ_S) opcode = DEV_GATHER_S;
 | 
						|
  else	opcode =  DEV_SCATTER_S;
 | 
						|
 | 
						|
  iovec1.iov_addr = (vir_bytes) mp->IO_GRANT;
 | 
						|
  iovec1.iov_size = mp->COUNT;
 | 
						|
 | 
						|
  /* Transfer bytes from/to the device. */
 | 
						|
  position= make64(mp->POSITION, mp->HIGHPOS);
 | 
						|
  r = (*dp->dr_transfer)(mp->IO_ENDPT, opcode, position, &iovec1, 1);
 | 
						|
 | 
						|
  /* Return the number of bytes transferred or an error code. */
 | 
						|
  return(r == OK ? (mp->COUNT - iovec1.iov_size) : r);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 *				do_vrdwt				    *
 | 
						|
 *==========================================================================*/
 | 
						|
PRIVATE int do_vrdwt(dp, mp)
 | 
						|
struct driver *dp;	/* device dependent entry points */
 | 
						|
message *mp;		/* pointer to read or write message */
 | 
						|
{
 | 
						|
/* Carry out an device read or write to/from a vector of user addresses.
 | 
						|
 * The "user addresses" are assumed to be safe, i.e. FS transferring to/from
 | 
						|
 * its own buffers, so they are not checked.
 | 
						|
 */
 | 
						|
  static iovec_t iovec[NR_IOREQS];
 | 
						|
  phys_bytes iovec_size;
 | 
						|
  unsigned nr_req;
 | 
						|
  int r, opcode;
 | 
						|
  u64_t position;
 | 
						|
 | 
						|
  nr_req = mp->COUNT;	/* Length of I/O vector */
 | 
						|
 | 
						|
  /* Copy the vector from the caller to kernel space. */
 | 
						|
  if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
 | 
						|
  iovec_size = (phys_bytes) (nr_req * sizeof(iovec[0]));
 | 
						|
 | 
						|
  if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->IO_GRANT, 
 | 
						|
		0, (vir_bytes) iovec, iovec_size, D)) {
 | 
						|
	printf("bad I/O vector by: %d\n", mp->m_source);
 | 
						|
	return(EINVAL);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Prepare for I/O. */
 | 
						|
  if ((*dp->dr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
 | 
						|
 | 
						|
  /* Transfer bytes from/to the device. */
 | 
						|
  opcode = mp->m_type;
 | 
						|
  position= make64(mp->POSITION, mp->HIGHPOS);
 | 
						|
  r = (*dp->dr_transfer)(mp->IO_ENDPT, opcode, position, iovec, nr_req);
 | 
						|
 | 
						|
  /* Copy the I/O vector back to the caller. */
 | 
						|
  if (OK != sys_safecopyto(mp->m_source, (vir_bytes) mp->IO_GRANT, 
 | 
						|
		0, (vir_bytes) iovec, iovec_size, D)) {
 | 
						|
	printf("couldn't return I/O vector: %d\n", mp->m_source);
 | 
						|
	return(EINVAL);
 | 
						|
  }
 | 
						|
 | 
						|
  return(r);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				no_name					     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC char *no_name()
 | 
						|
{
 | 
						|
/* Use this default name if there is no specific name for the device. This was
 | 
						|
 * originally done by fetching the name from the task table for this process: 
 | 
						|
 * "return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);", but currently a
 | 
						|
 * real "noname" is returned. Perhaps, some system information service can be
 | 
						|
 * queried for a name at a later time.
 | 
						|
 */
 | 
						|
  static char name[] = "noname";
 | 
						|
  return name;
 | 
						|
}
 | 
						|
 | 
						|
/*============================================================================*
 | 
						|
 *				do_nop					      *
 | 
						|
 *============================================================================*/
 | 
						|
PUBLIC int do_nop(dp, mp)
 | 
						|
struct driver *dp;
 | 
						|
message *mp;
 | 
						|
{
 | 
						|
/* Nothing there, or nothing to do. */
 | 
						|
 | 
						|
  switch (mp->m_type) {
 | 
						|
  case DEV_OPEN:	return(ENODEV);
 | 
						|
  case DEV_CLOSE:	return(OK);
 | 
						|
  case DEV_IOCTL_S:	
 | 
						|
  default:		printf("nop: ignoring code %d\n", mp->m_type);
 | 
						|
			return(EIO);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*============================================================================*
 | 
						|
 *				nop_ioctl				      *
 | 
						|
 *============================================================================*/
 | 
						|
PUBLIC int nop_ioctl(dp, mp)
 | 
						|
struct driver *dp;
 | 
						|
message *mp;
 | 
						|
{
 | 
						|
  return(ENOTTY);
 | 
						|
}
 | 
						|
 | 
						|
/*============================================================================*
 | 
						|
 *				nop_alarm				      *
 | 
						|
 *============================================================================*/
 | 
						|
PUBLIC void nop_alarm(dp, mp)
 | 
						|
struct driver *dp;
 | 
						|
message *mp;
 | 
						|
{
 | 
						|
/* Ignore the leftover alarm. */
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				nop_prepare				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC struct device *nop_prepare(int device)
 | 
						|
{
 | 
						|
/* Nothing to prepare for. */
 | 
						|
  return(NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				nop_cleanup				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void nop_cleanup()
 | 
						|
{
 | 
						|
/* Nothing to clean up. */
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				nop_cancel				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int nop_cancel(struct driver *dr, message *m)
 | 
						|
{
 | 
						|
/* Nothing to do for cancel. */
 | 
						|
   return(OK);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				nop_select				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int nop_select(struct driver *dr, message *m)
 | 
						|
{
 | 
						|
/* Nothing to do for select. */
 | 
						|
   return(OK);
 | 
						|
}
 | 
						|
 | 
						|
/*============================================================================*
 | 
						|
 *				do_diocntl				      *
 | 
						|
 *============================================================================*/
 | 
						|
PUBLIC int do_diocntl(dp, mp)
 | 
						|
struct driver *dp;
 | 
						|
message *mp;			/* pointer to ioctl request */
 | 
						|
{
 | 
						|
/* Carry out a partition setting/getting request. */
 | 
						|
  struct device *dv;
 | 
						|
  struct partition entry;
 | 
						|
  int s;
 | 
						|
 | 
						|
  if (mp->REQUEST != DIOCSETP && mp->REQUEST != DIOCGETP) {
 | 
						|
  	if(dp->dr_other) {
 | 
						|
  		return dp->dr_other(dp, mp);
 | 
						|
  	} else return(ENOTTY);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Decode the message parameters. */
 | 
						|
  if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NULL) return(ENXIO);
 | 
						|
 | 
						|
  if (mp->REQUEST == DIOCSETP) {
 | 
						|
	/* Copy just this one partition table entry. */
 | 
						|
	s=sys_safecopyfrom(mp->IO_ENDPT, (vir_bytes) mp->IO_GRANT, 
 | 
						|
		0, (vir_bytes) &entry, sizeof(entry), D);
 | 
						|
	if(s != OK)
 | 
						|
	    return s;
 | 
						|
	dv->dv_base = entry.base;
 | 
						|
	dv->dv_size = entry.size;
 | 
						|
  } else {
 | 
						|
	/* Return a partition table entry and the geometry of the drive. */
 | 
						|
	entry.base = dv->dv_base;
 | 
						|
	entry.size = dv->dv_size;
 | 
						|
	(*dp->dr_geometry)(&entry);
 | 
						|
	s=sys_safecopyto(mp->IO_ENDPT, (vir_bytes) mp->IO_GRANT, 
 | 
						|
		0, (vir_bytes) &entry, sizeof(entry), D);
 | 
						|
        if (OK != s) 
 | 
						|
	    return s;
 | 
						|
  }
 | 
						|
  return(OK);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			      driver_mq_queue				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int driver_mq_queue(message *m, int status)
 | 
						|
{
 | 
						|
	mq_t *mq, *mi;
 | 
						|
	static int mq_initialized = FALSE;
 | 
						|
 | 
						|
	if(!mq_initialized) {
 | 
						|
        	/* Init MQ library. */
 | 
						|
        	mq_init();
 | 
						|
        	mq_initialized = TRUE;
 | 
						|
        }
 | 
						|
 | 
						|
	if(!(mq = mq_get()))
 | 
						|
        	panic("driver_mq_queue: mq_get failed");
 | 
						|
	memcpy(&mq->mq_mess, m, sizeof(mq->mq_mess));
 | 
						|
	mq->mq_mess_status = status;
 | 
						|
	mq->mq_next = NULL;
 | 
						|
	if(!queue_head) {
 | 
						|
		queue_head = mq;
 | 
						|
	} else {
 | 
						|
		for(mi = queue_head; mi->mq_next; mi = mi->mq_next)
 | 
						|
			;
 | 
						|
		mi->mq_next = mq;
 | 
						|
	}
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 | 
						|
 |