/* -*- Mode:C; Tab-width:4 -*- */
/*                                                                       */
/*                                                                       */
/*                      RESTRICTED RIGHTS LEGEND                         */
/*                                                                       */
/* Use, duplication, or disclosure by the Government is subject to       */
/* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in  */
/* Technical Data and Computer Software clause at 252.227-7013.          */
/*                                                                       */
/*                    TEXAS INSTRUMENTS INCORPORATED.                    */
/*                            P.O. BOX 149149                            */
/*                         AUSTIN, TEXAS 78714-9149                      */
/*                              MS 2151                                  */
/*                                                                       */
/*  Copyright (C)   1987,1988,1989,1990 Texas Instruments Incorporated.  */
/*  All rights reserved.                                                 */
/*                                                                       */

/*  9-26-88  BJ  Add an IOCTL handler for HANDLE_MICRONET_PORTS.              */
/*  9-28-88  BJ  Move Nupi and Crash register defines to micronet-accessors.h */


#include <quickdraw.h>
#include <devices.h>
#include <fonts.h>
#include <events.h>
#include <windows.h>
#include <osutils.h>
#include <menus.h>
#include <textedit.h>
#include <dialogs.h>
#include <toolutils.h>
#include <desk.h>
#include <resources.h>
#include <stdio.h>
#include <AddinComm.h>
#include <utility.h>
#include <Micronet-device.h>
/* #include <micronet-ports.h> */
#include <micronet-accessors.h>
#include <errors.h>
#include <serial.h>
#include <strings.h>
#include <sockets.h>
#include <portmap.h>
#include <read_startup.h>
#include <osevents.h>  /* needed for FlushEvents() - clm 08/30/89 */

extern void tv_command();
extern void Lap_Handler();
extern void misc_command();
extern void tb_command();
extern void rpc_handler();
static Boolean 
(*channel_handlers[])() =
{
  NULL,           /*  0 */
  tv_command,  	  /*  1 */
  Lap_Handler,    /*  2 */
  NULL  		  /*  3 */
  misc_command,   /*  4 */
  NULL,           /*  5 */
  NULL, /*  6 */
  NULL,  		  /*  7 */
  rpc_handler,    /*  8 */
  NULL,           /*  9 */
  NULL,           /* 10 */
  NULL,           /* 11 */
  NULL,           /* 12 */
  NULL,           /* 13 */
  NULL,           /* 14 */
  NULL            /* 15 */
};

extern void nupi_close();
extern void Lap_Close();
extern void tvClose();
/* Channel Close Functions. Each is passed a parm block and DCE.*/
static OSErr
(*channel_close_functions[])() =
{
  NULL,  		  /*  0 */
  tvClose, 		  /*  1 */
  Lap_Close,  	  /*  2 */ 
  NULL,  		  /*  3 */
  NULL,  		  /*  4 */
  nupi_close,  	  /*  5 */
  NULL,  		  /*  6 */
  NULL,  		  /*  7 */
  NULL,           /*  8 */
  NULL,           /*  9 */
  NULL,           /* 10 */
  NULL,           /* 11 */
  NULL,           /* 12 */
  NULL,           /* 13 */
  NULL,           /* 14 */
  NULL            /* 15 */
};

extern void nupi_init();
extern void InitTVStuff();
extern void Lap_Init();
extern short tb_init();
extern void rpc_init();

static OSErr
(*channel_open_functions[])() =
{
  NULL,  		  /*  0 */
  NULL,	 	 	  /*  1 */
  Lap_Init,  	  /*  2 */
  NULL,  		  /*  3 */
  NULL,  		  /*  4 */
  nupi_init,	  /*  5 */
  NULL,		      /*  6 */ 
  NULL,  		  /*  7 */
  tb_init,        /*  8 */
  NULL,           /*  9 */
  NULL,           /* 10 */
  NULL,           /* 11 */
  NULL,           /* 12 */
  NULL,           /* 13 */
  NULL,           /* 14 */
  NULL            /* 15 */
};

#define DRIVER_SLEEP_TIME 0

/*	The number of ticks to process just paging requests  */
short	TicksToParkOnPagingChannel = 1 ;

Boolean dynamicResize = TRUE ;

/*
 1. Send shutdown message to the addin.
 2. Do any cleanup required for NUPI, MicroNet Ports, Control, and Network channels.
*/

static OSErr
drvr_close(pblk, dce)
	 ParamBlockRec *pblk;
	 DCtlEntry *dce;
{
  int x, return_value = noErr;
  extern short mxslot ;

  DriverData->driver_initialized = false;

  /* Run any channel cleanups. */
  for(x=0; x<MAX_NUMBER_OF_CHANNELS; x++)
	if(channel_close_functions[x])
	  (*channel_close_functions[x])(pblk, dce);

  /* Remove the driver globals. */
  dce->dCtlStorage = NULL;

  DriverData->done_flag = true;

  if(AUX_p) map_out_board(mxslot);
  
  restore_KCHR();

  return(return_value);
}

/*
 1. Handle read and write calls. For now these are not handled.
*/
	 
static OSErr
drvr_prime(pblk, dce)
	 ParamBlockRec *pblk;
	 DCtlEntry *dce;
{
#pragma unused(pblk, dce)		/* 11/22/89 sbw */

#ifdef DEBUG
  if(debugFlag)
	printf("Inside prime\n");
#endif
  return readErr;
}

/*
 1. handle control and status calls from user.
 (a) socket calls.
 (b) channel-address (for TV code.)
 (c) 
 
 */
	 
/* Misc macros. */
/*
#define csCode pblk->cntrlParam.csCode
#define acb_size pblk->cntrlParam.csParam[2]
#define acb_ptr  ((char *) (pblk->cntrlParam.csParam[0]))
#define channel pblk->cntrlParam.csparam[2]
*/


/* A short term kludge to avoid changing the microcode for these channels. */
Boolean
poll_mcr_channels()
{
   
   acb *cmd;

   if(cmd = ((acb *) nubus_read(DriverData->mx_slot, nupi_register)))
     {

#ifdef DEBUG
	    if((((long)cmd) & 0x0f000000) != ((unsigned long)DriverData->mx_slot << 24))
	      DebugStr("\ppoll_mcr_channels got a pointer from another slot for the nupi");
#endif
	 	nubus_write(DriverData->mx_slot, nupi_register, 0);
		nupi_command(cmd);
		return true;
	  }
	  
	if(cmd = ((acb *) nubus_read(DriverData->mx_slot, crash_register)))
     {
#ifdef DEBUG
	    if((((long) cmd) & 0x0f000000) != ((unsigned long)DriverData->mx_slot << 24))
	      DebugStr("\ppoll_mcr_channels got a pointer from another slot for the crash ch");
#endif
	 	nubus_write(DriverData->mx_slot, crash_register, 0);
		control_server(cmd);
		return true;
	  }
	return false;
}

#ifdef TRASH
/* A macro to speed up getting tick count. */
/* Watch out if they ever really remove the low memory globals. */
#define TickCount() *((unsigned long *) 0x16a)
#endif

OSErr
drvr_control(pblk, dce)
	 ParamBlockRec *pblk;
	 DCtlEntry *dce;
{	   
#pragma unused(dce)							/* 11/22/89 sbw */

  int return_value = noErr;
  acb *cmd ;
  acb *get_acb_fast() ;


  switch(pblk->cntrlParam.csCode)
	{
	case 0:
	  /* A request to reinstall the driver. Return an error.*/
	  return_value = controlErr;
	  break;
	  
	case GetAcb:
	  cmd = get_acb_fast(pblk->cntrlParam.csParam[2]);     /* Get size from PARM 2 */
	  pblk->cntrlParam.csParam[0] = LoWord((long)cmd) ;
	  pblk->cntrlParam.csParam[1] = HiWord((long)cmd) ;
	  pblk->cntrlParam.csParam[4] = (cmd == NULL);         /* ab 11/7/88.  Parm 4 = Flag saying if ACB = NIL, ie, can't get */
	  return_value = 0;                                    /* ab 11/7/88.  Be sure not to use return_value = 1.  That hangs the PBControl */
	  break;
	  
	case SendCommand:
	  send_command((acb *) ((unsigned short) pblk->cntrlParam.csParam[0] | ((long)pblk->cntrlParam.csParam[1] << 16)),
	  				pblk->cntrlParam.csParam[2]);
	  return_value = 0 ;
	  break;
	  
	case GetCommand:
	  cmd = get_command(pblk->cntrlParam.csParam[2],
	  					pblk->cntrlParam.csParam[3]);
	  pblk->cntrlParam.csParam[0] = LoWord((long)cmd) ;
	  pblk->cntrlParam.csParam[1] = HiWord((long)cmd) ;
	  pblk->cntrlParam.csParam[4] = (cmd == NULL);	
	  return_value = 0;
	  break;

	case ADD_OPEN_FUNCTION:
	  {
		struct accessor_info *parms = ((struct accessor_info *) pblk->cntrlParam.csParam);
		channel_open_functions[parms->ch] =  (OSErr (*) ()) parms->fn;  /* clm 9/29/89 - cast */
		return_value = 0;
	  }
	  break;

	case ADD_CLOSE_FUNCTION:
	  {
		struct accessor_info *parms = ((struct accessor_info *) pblk->cntrlParam.csParam);
		channel_close_functions[parms->ch] = (OSErr (*) ()) parms->fn;   /* clm 9/29/89 - cast */
		return_value = 0;
	  }
	  break;
	case ADD_HANDLER_FUNCTION:
	  {
		struct accessor_info *parms = ((struct accessor_info *) pblk->cntrlParam.csParam);
		channel_handlers[parms->ch] = (Boolean (*) ()) parms->fn;   /* clm 9/29/89 - cast */
		return_value = 0;
	  }
	  break;

	case REMOVE_OPEN_FUNCTION:
	  {
		struct accessor_info *parms = ((struct accessor_info *) pblk->cntrlParam.csParam);
		channel_open_functions[parms->ch] = NULL;
		return_value = 0;
	  }
	  break;

	case REMOVE_CLOSE_FUNCTION:
	  {
		struct accessor_info *parms = ((struct accessor_info *) pblk->cntrlParam.csParam);
		channel_close_functions[parms->ch] = NULL;
		return_value = 0;
	  }
	  break;
	case REMOVE_HANDLER_FUNCTION:
	  {
		struct accessor_info *parms = ((struct accessor_info *) pblk->cntrlParam.csParam);
		channel_handlers[parms->ch] = NULL;
		return_value = 0;
	  }
	  break;

	case SIOCSOCK:
	  {
		  struct sock_info *parms = ((struct sock_info *) (pblk->cntrlParam.csParam));
		  parms->socket = socket(parms->domain, parms->type, parms->protocol);
		  return_value = (parms->socket < 0) ? -1 : 0;
	  }
	  break;

	case SIOCBIND:
	  {
		  struct bind_info *parms = ((struct sock_info *) (pblk->cntrlParam.csParam));
		  return_value = bind(parms->socket, parms->addr, parms->addrlen);
	  }
	  break;

	case SIOCNAME:
	  {
		  struct sockname_info *parms = ((struct sock_info *) (pblk->cntrlParam.csParam));
		  return_value = getsockname(parms->socket, parms->addr, parms->addrlen);
	  }
	  break;

	case SIOCSEND:
	  {
		  struct send_info *parms = ((struct sock_info *) (pblk->cntrlParam.csParam));
		  return_value = sendto(parms->socket, parms->buf, parms->buflen, parms->flags, parms->addr, parms->addrlen);
	  }
	  break;

	case SIOCRECV:
	  {
		  struct recv_info *parms = ((struct sock_info *) (pblk->cntrlParam.csParam));
		  parms->nbytes = recvfrom(parms->socket, parms->buf, parms->buflen, parms->flags, parms->addr, parms->addrlen);
		  return_value = (parms->nbytes == parms->buflen) ? 0 : -1;
	  }
	  break;

	case SIOCCLOSE:
	  {
		  struct close_info *parms = ((struct sock_info *) (pblk->cntrlParam.csParam));
		  return_value = sockclose(parms->socket);
	  }
	  break;
	  
	case SELECT_WORD_LOC:
	  {
		  extern unsigned long select_word;
		  unsigned long **parms = ((unsigned long **) (pblk->cntrlParam.csParam));
		  parms[0] = &select_word;
		  return_value = 0;
	  }
	  break;

	case HANDLE_MICRONET_PORTS:
	  {
		  int limit_ticks = TickCount() + TicksToParkOnPagingChannel ;
		  
		  while(poll_mcr_channels() && (TickCount() < limit_ticks));
	  }
	  return_value = 0;
	  break;

	case IOCTL_PMAP_SET:
	
	  return_value = ioctl_pmap_set( *((Ptr *) &pblk->cntrlParam.csParam[0]));
	  break;

	case IOCTL_PMAP_UNSET:
	  return_value = ioctl_pmap_unset(*((Ptr *) &pblk->cntrlParam.csParam[0]));
	  break;

	case IOCTL_PMAP_GET:
	  return_value = ioctl_pmap_get(*((Ptr *) &pblk->cntrlParam.csParam[0]));
	  break;

	case READ_STARTUP:
	   (startup_tbl **) *((Ptr *) &pblk->cntrlParam.csParam[0]) =  read_startup();
	  /* clm 10/2/89 - added cast to (startup_tbl **); this is the type read_startup returns */
	  return_value = 0;

	default:
	  SysBeep(5);
	  return_value = controlErr;
	  break;
	}

  pblk->cntrlParam.ioResult = return_value;
  return(return_value);
}

/*

 1. Initialize MicroNet structures.
 2. Initialize handlers for NUPI, MicroNet Ports, Control, and Network channels.
 3. Boot the addin using the band found in the open parameters or in the config file.
*/

static OSErr 
drvr_open()
{ 
  int x;
  OSErr error;
  Ptr *parms[6];
  unsigned char mx_slot;
  extern short mxslot ;
  extern int startup_echo;
  
  startup_echo++; 	/* show startup filename on log */
  if(!find_mx(&mx_slot))
	{
	  PrtErrorMsg(200) ;	/* microExplorer not found */
	  return -1;
	}
	
  mxslot = mx_slot ;						/* for pascal references */

  if(AUX_p)	map_in_board(mx_slot); 

  initialize_comm(mx_slot);
  for(x=0; x<MAX_NUMBER_OF_CHANNELS; x++)
	if(channel_open_functions[x])
	  {
	    if((*channel_open_functions[x])(NULL) )
		  return -1;  /* one of the channel_open_functions failed and printed it's error message */
	   }
  if (!(bare_boot())) {
	  return -1;  /* Bareboot failed and already put up an error message */
  }
  
  find_driver_data(mx_slot, &DriverData) ;
  
  DriverData->mx_slot = mx_slot;
  DriverData->prime_internal =  (Ptr) drvr_prime;  /* clm 10/2/89 - added the following three casts */
  DriverData->control_internal = (Ptr) drvr_control; 
  DriverData->close_internal = (Ptr) drvr_close;
  DriverData->a5_value = CurrentA5;
  DriverData->lockedMouse.ScreenID = 0;

  find_host(&DriverData->my_slot) ;
  
  if((error = OpenDriver("\p.MicroNet", &DriverData->refnum)) != noErr) 
                           /* clm 10/02/89 - made into a pascal \p string  */
	{
	  PrtErrorMsg(204) ;	/* MicronetDriver open failed */
	  return error;
	}

  parms[0] = (Ptr *) DriverData;  /* clm 10/02/89 - cast */ 

  in_driver_p = false;
  if((error = r_ioctl(DriverData->refnum, 0, parms)) != noErr)
	{
	  PrtErrorMsg(205) ;	/* MicronetDriver init failed */
	  return error;
	}
   in_driver_p = true;
	
	InitTVStuff();
	
#ifdef TRASH
  if(debugFlag)
	printf("Driver installed, ref = %d, sockets = %x, globals = %x\n", DriverData->refnum, sockets, DriverData);
#endif

  DriverData->done_flag = false;
  DriverData->driver_initialized = true;
  DriverData->portmap_initialized = true;
  DriverData->nfs_initialized = true; 
  
  return noErr;
}

unsigned short poll_mask = 0xff;
unsigned short handled_tv_command = 0;

command_loop()	 
{
  acb 	*pkt;
  int 	x, y ;
  int	stop_time = TickCount() + 5;
  int	stop_paging_time = TickCount() + TicksToParkOnPagingChannel ;

  while(TickCount() < stop_time)
	{
	  
	  while(poll_mcr_channels() && (TickCount() < stop_paging_time));
	  
	  for(x=0; x<MAX_NUMBER_OF_CHANNELS; x++)
		if(channel_handlers[x])
		   {
			 poll_mask &= ~(1<<x);
			 
			 for(y=0; (y < MAX_NUMBER_OF_CHANNELS - x) && (TickCount() < stop_time) && (pkt = get_command(x, true)); y++)
			   {
				   (*channel_handlers[x])(pkt);
			   }
			 
			 /* Turn polling back on for this channel. */
			 poll_mask |= 1<<x;
		   }
	}  
}  


extern Boolean use_real_kchr;
extern Boolean DirectDrawingEnabled;


/*

  ExitToShell Patch. This patch insures that the mouse interrupt handler will be removed 
                     however the microExplorer is exited.

*/

r_ExitToShell()
{
	CloseDriver(DriverData->refnum);
}

short OurResourceRefnum;  			/* ab 11/10/88 */
#define Auxmode() (*((unsigned short *) 0x0B22) & 0x200)
extern Ptr malloc();
main()
{
  int error;
  int AUX_heap_zone_size =  0x60000;
  Ptr startPtr, endPtr;
  KeyMap theKeys ;

  in_driver_p = true;
  
  InitExitToShell();  

  SetApplLimit(GetApplLimit() - 32000) ;

  InitGraf(&qd.thePort); 
  InitFonts();
  FlushEvents(everyEvent, 0);
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);
  InitCursor();

  MaxApplZone();					   	/*Expand heap to its max*/
  MoreMasters();
  MoreMasters();						
  MoreMasters();

  doneFlag = false;
  debugFlag = false;
  
  WWInit() ;
  if(Auxmode())
	{
		printf("Running under AUX\n");
		AUX_p = true;
		use24bitmode = false;
		use_real_kchr = false;
		
		/* This is a work around for memory allocation. NewPtr does not work after we issue the phys call unless we have our own heap zone. */
		if(!(startPtr = malloc(AUX_heap_zone_size)))
		  exit(0);
		endPtr = startPtr + AUX_heap_zone_size;
		InitZone(NULL, 100, endPtr, startPtr);
		
		DirectDrawingEnabled = true;
		/* Always do the menu boot under AUX since it has trouble getting the keystate. */
		DoBootDialog() ;
	}
  else
	{
		printf("Running under the Macintosh OS.\n");
		AUX_p = false;
		use24bitmode = true; /* For now assume that Mac OS runs in 24 bit mode. */
		use_real_kchr = true;
		DirectDrawingEnabled = true;
	}

   GetKeys(theKeys) ;
#ifdef DEBUG
   printf("%08x %08x %08x %08x\n", theKeys[0], theKeys[1], theKeys[2], theKeys[3]) ;
#endif

   if (((1L << 2) & theKeys[1]) && ((1L << 15) & theKeys[1])) {		/* check for command & option key pressed */
		DebugStr("\pHello there."); ;
   }


   if (((1L << 2) & theKeys[1])) {		/* check for option key pressed */
		dynamicResize = FALSE ;
   }
   if (((1L << 15) & theKeys[1])) {		/* check for command key pressed */
		DoBootDialog() ;
   }

  OurResourceRefnum = CurResFile();	/* ab 11/10/88.  Save off refnum of microExplorer's resource file */
 
  load_KCHR();  						/* initialize keyboard resource: lispm.c */

  SetUpMenus();
  
  /* Install MicroNet driver. */
  if((error = drvr_open()) != noErr)
	{
#ifdef DEBUG
	  printf("Error in drvr_open: %d\n", error);
#endif
	  exit(1) ;   /* error message has already been displayed */
	}
	
  rpc_init();
  
  nfs_initialize();

  /* Main loop. */
  do
	{
	  poll_mask = -1;
	  event_handler(everyEvent - keyDownMask - keyUpMask - autoKeyMask, DRIVER_SLEEP_TIME);
	} while(!doneFlag);

  exit(0);
}



/* pascal void ExitToShell()
	extern 0xA9F4;
*/

/* Redefine the exit function to call ExitToShell. */
exit(n)
{
#pragma unused(n)				/* 11/22/89 sbw */

	ExitToShell();
}



