/*
 * Post-Load command for Mux.   Checks and installs /dev/tty*.
 *
 *	Based on the code in the Example Parallel and Serial Port post loaders
 *	supplied by NeXT.
 *	Author: Mark Salyzyn  mark@ve6mgs.ampr.ab.ca  January 15 1994
 */

#import <driverkit/IODeviceMaster.h>
#import <driverkit/IODevice.h>
#import <driverkit/IOConfigTable.h>
#import <errno.h>
#import <libc.h>
#import <stdio.h>
#import "../MuxParm.h"
#ifdef Use_syslog
# import <syslog.h>
#else
# undef LOG_CRIT
# define LOG_CRIT	stderr
# undef LOG_WARNING
# define LOG_WARNING	stderr
# undef LOG_DEBUG
# define LOG_DEBUG	stderr
# undef syslog
# define syslog		fprintf
#endif

#define DEBUG_LOG	0

/*
 * A list of nodes to create. One per minor number, with a letter appended
 * to eaach indicating minor number. 
 */
typedef struct {
	const char *name;
	int minorNum;		/* in addition to 0..(NTTY - 1) */
	int mode;
	const char *key;	/* Key in Instance Table */
} ttyNode;

ttyNode nodeList[] = {	/* Minor  chmod			Override */
	{ "tty",	0,	(0666 | S_IFCHR),	"Path %d"	},
	{ "ttyf",	32,	(0666 | S_IFCHR),	"Path %d CTS"	}, 
	{ "ttyd",	64,	(0666 | S_IFCHR),	"Path %d IN"	},
	{ "ttydf",	96,	(0666 | S_IFCHR),	"Path %d IN CTS"},
	{ "cu",		192,	(0666 | S_IFCHR),	"Path %d OUT"	},
	{ "cuf",	224,	(0666 | S_IFCHR),	"Path %d OUT CTS"},
	/* Two new devices that open LOCAL, but detect DCD drop */
	{ "cud",	128,	(0666 | S_IFCHR),	"Path %d OUT DCD"},
	{ "cudf",	160,	(0666 | S_IFCHR),	"Path %d OUT DCD CTS"},
	{ NULL,		0,	0,			""		},	
};
		
int main(int argc, char **argv)
{	id 		deviceMaster = [IODeviceMaster new];
	IOObjectNumber 	objectNum;
	IOString 	kind;
	IOReturn 	rtn;
	id		config;
	char		name[sizeof(MuxDeviceName)+1];
	unsigned 	array[1], ntty, minorNum, firstMinorNum, majorNum;
	unsigned	count = 1;
	const char *	instance;
	extern char *	strcpy();

	/*
	 *	Parse out the Instance information from the passed arguments
	 */
	instance = "0";
	if ((argc > 1) && (strncmp (argv[1], "Instance=", 9) == 0))
		instance = argv[1] + 9;
	(void)strcpy (name, MuxDeviceName);
	(void)strcpy (kind, MuxDeviceKind);
	name[sizeof(name)-1] = '\0';
	name[sizeof(name)-2] = *instance;
	rtn = [deviceMaster lookUpByDeviceName : name
				  objectNumber : &objectNum
				    deviceKind : &kind];
	if (rtn) {
		syslog(LOG_CRIT,"Error contacting %s (%s)\n",
			name, [IODevice stringFromReturn : rtn]);
		exit(1);	
	}
	rtn = [deviceMaster getIntValues : array
			    forParameter : "IONumberDevice"
			    objectNumber : objectNum
			           count : &count];	// in/out
	if(rtn || (count != 1)) {
		syslog(LOG_WARNING,"Error performing NTTY (%s), Using %d\n",
			[IODevice stringFromReturn : rtn], NTTY);
		ntty = NTTY;
	} else	ntty = array[0] ;
	rtn = [deviceMaster getIntValues : array
			    forParameter : "IOMinorDevice"
			    objectNumber : objectNum
			           count : &count];	// in/out
	if(rtn || (count != 1)) {
		syslog(LOG_CRIT,"Error performing IOMinorDevice (%s)\n",
			[IODevice stringFromReturn : rtn]);
		firstMinorNum = (NTTY * (name[sizeof(name)-2] - '0'));
	} else	firstMinorNum = array[0] ;
	rtn = [deviceMaster getIntValues : array
			    forParameter : "IOMajorDevice"
			    objectNumber : objectNum
			           count : &count];	// in/out
	if(rtn || (count != 1)) {
		syslog(LOG_CRIT,"Error performing IOMajorDevice (%s)\n",
			[IODevice stringFromReturn : rtn]);
		exit(1);	
	}
	majorNum = array[0];
	/*
	 *	Open Instance?.table/Default.table for this system to
	 *	get the information from them about the paths to use
	 *	for the serial ports
	 */
	config = [IOConfigTable newForDriver : name
					unit : 0];
	if (config == nil) {
		config = [IOConfigTable newDefaultTableForDriver : name];
		if (config == nil) {
			List *		ids;
			int		index;
			const char *	cp;

			/*
			 *	Plan B (only picks up Instance? Tables), not
			 *	a problem since our default values match
			 *	somewhat to the default values in the tables.
			 */
			ids = [IOConfigTable tablesForInstalledDrivers];
			for (index = 0; index < [ids count]; ++index) {
				if ((cp = [[ids objectAt : index]
				    valueForStringKey : "Driver Name"])
				 && (strcmp (cp, MuxDeviceName) == 0)
				 && (cp = [[ids objectAt : index]
				    valueForStringKey : "Instance"])
				 && (strcmp (cp, instance) == 0)) {
					config = [ids objectAt : index];
					break ;
				}
			}

			if (config == nil)
				syslog(LOG_WARNING,
				    "Error getting either Instance%s.table or Default.table for %s\n\tWill use built in default for device naming\n",
				    instance, MuxDeviceName);
		}
	}
	for(minorNum=0; minorNum<ntty; minorNum++) {
		IOString 	nodeName;
		ttyNode 	*node;
		int		dev;

		/*
		 *	The starting point should be programmable from the
		 *	instance ... this is the default set, we start at
		 *	`c' since we assume the user has SerialPorts set up.
		 */
		name[sizeof(name)-2] = '0';
		for(node=nodeList; node->name; node++) {
			count = 0;
			if (config) {
				const char *	cp ;

				sprintf(nodeName, node->key, minorNum);
				cp = [config valueForStringKey : nodeName];
				if (cp) {
					(void)strcpy (nodeName, cp);
					++count;
				}
			}
			if (count == 0) {
				/*
				 * Default assumes first two NeXT serial ports
				 * are *not* installed (start at ttya).
				 */
				sprintf(nodeName, "/dev/%s%c", 
				    node->name, 'a' + firstMinorNum + minorNum);
			}
			/*
			 * First delete existing node, then create a new one. 
			 */
			unlink(nodeName);
			dev = makedev(majorNum,
			    (node->minorNum + minorNum + firstMinorNum));
			if(mknod(nodeName, node->mode, dev))
				syslog(LOG_WARNING,"Can\'t create %s\r\n", nodeName);
			/* Lets just rub it in ... */
			if(chmod(nodeName,node->mode&~S_IFCHR))
				syslog(LOG_WARNING,"Can\'t ensure %s is +rw\r\n", nodeName);
#if defined(DEBUG_LOG) && (DEBUG_LOG > 0)
			syslog(LOG_DEBUG,"Created %s\n", nodeName);
#endif
		}
	}
	return 0;
}
