/*
 * Sample QNX4 Device Driver
 *
 * Far call interface routines.
 *
 * These routines are far called by Dev which requires
 * them to be compiled specially. In particular, it is very important
 * that they be compiled with -zu (SS != DS) since this will be
 * the case. Also stack checking should be disabled
 *
 * Kick() is called by Dev whenever "something" has changed in the
 *        output buffer (new data, change in flow control, etc.)
 *
 * Stty() is called by Dev when something "significant" has changed
 *        in the termios control structure which requires hardware
 *        attention (eg: baud rate, parity).
 *
 * Ctrl() is called to perform various "houskeeping" functions
 *        (like dropline by rasining/lowering DTR, sendbreak, etc.)
 * 
 * Inside these routines, teh following limitations apply:
 *
 * 1) No system calls other than Trigger() can be used. Since most
 *    C library routines use system calls internally (eg: read
 *    will use Sendfd), the "safe" rule is to avoid ALL library
 *    routines completetly. The best policy is to only access/change
 *    global memory variables and Trigger() a proxy to wake up your
 *    process if more work needs to be done.
 *
 * 2) No mapped or segmented memory can be accesses (ie: no far pointers)
 *    since Dev's process context will not have access to them. Only
 *    variables in DS can be accessed.
 *
 * 3) Try to avoid much processing since Dev (at high priority) will be
 *    effectively blocked until your routine finishes.
 *
 * 4) Don't use much stack (keep it under a few hundred bytes).
 */


#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/dev.h>
#include <sys/_devdrvr.h>

/*
 *	declare the calls 'dev' will make into driver.
 */
int far	Kick  (struct output_buffer far *obuf);
int far	Stty  (struct termios far *tios); 
int far	Ctrl  (union ioctl_entry far *ioc); 

#pragma aux (_dev_call) Kick;
#pragma aux (_dev_call) Stty;
#pragma aux (_dev_call) Ctrl;

#pragma off (check_stack);

/*
 * Kick is called by DEV everytime something "new" happens
 * to the output buffer
 *
 * It will set 'obuf' to be the output buffer
 * It will also set ds to be the data segment of the driver being called
 *
 * Since this routine is "called" by DEV, it will ALWAYS run at higher
 * priority than the driver task, so cannot be interrupted.
 */

int far Kick (struct output_buffer far *obuf)	{
	extern int idle;
	extern int more;
	extern pid_t out_proxy;

	if((obuf->size > 0
			&&  (obuf->mode & (OUTPUT_STOPPED|OUTPUT_STOPPED_HW)) == 0)
			||  (obuf->mode & (OUTPUT_HFLOW_OFF|OUTPUT_HFLOW_ON|OUTPUT_XON|OUTPUT_XOFF)) ) {
		if(idle) {
			idle = 0;
			Trigger(out_proxy);
			}
		else
			more = 1;
		return(1);
		}
	return(0);
	}


/*
 * Stty is called by DEV to indicate a significant change in the
 * termios control structure (baud rate, modem control)
 * It can optionally be called for EVERY call to tcsetattr() if the
 * DRVR_STTY option os passed to dev_drvr_register().
 *
 * It will set 'tios' to be a far pointer to the termios entry
 * It will also set ds to be the data segment of the driver being called
 *
 * Since this routine is "called" by DEV, it will ALWAYS run at higher
 * priority than the driver task, so cannot be interrupted.
 */

int far Stty (struct termios far *tios)
	{
	tios = tios; /* keep compiler happy */
	return(0);
	}


/*
 * Ctrl is called by DEV to indicate a high level
 * control function
 *
 * It will set 'ioc' to be an ioctl control block
 * It will also set ds to be the data segment of the driver being called
 *
 * Since this routine is "called" by DEV, it will ALWAYS run at higher
 * priority than the driver task, so cannot be interrupted.
 */

int far Ctrl (union ioctl_entry far *ioc)
	{
	int type;

	type = ioc->type;
	ioc->status = IOCTL_ERROR;

	switch (type) {

	case IOCTL_OPEN:
		/*
		 * First open on device
		 */
		ioc->status = IOCTL_OK;
		return(0);

	case IOCTL_CLOSE:
		/*
		 * Last close on device
		 */
		ioc->status = IOCTL_OK;
		return(0);

	case IOCTL_CTL:	
		ioc->user_return.status = IOCTL_OK;
		return(0);
		}

	return(-1);
	}

#pragma on (check_stack);
