[comp.sys.mac.programmer] Serial drivers and interrupt trapping

jeffsi@tekecs.TEK.COM (Jeff Siegel) (09/28/88)

Help!!  I am trying desperately to write a serial driver which will
allow me to: (1) trap the interrupt
             (2) load the data pending
             (3) do some special processing whenever a specific sized
                 packet has been completely grabbed
 
I've done this type of thing before on other machines (breaking the
rules though).  I'd like to do it on the Mac in a standard way, but
Inside Macintosh V1/2 just doesn't give enough information...

Is there anyone out there with some example code or can you offer any
advice?  I'd be eternally grateful...

Jeffrey Siegel
(301) 948-7151

paul@unisoft.UUCP (n) (09/28/88)

In article <10407@tekecs.TEK.COM> jeffsi@tekecs.TEK.COM (Jeff Siegel) writes:
] Help!!  I am trying desperately to write a serial driver which will
] allow me to: (1) trap the interrupt
]              (2) load the data pending
]              (3) do some special processing whenever a specific sized
]                  packet has been completely grabbed
]  
] I've done this type of thing before on other machines (breaking the
] rules though).  I'd like to do it on the Mac in a standard way, but
] Inside Macintosh V1/2 just doesn't give enough information...
] 

	Be warned! Doing something like this will NOT work with a 3rd party
serial card (yes they do exist), (who knows maybe it wont work on future Macs,
they may change the hardware, that's why there is a driver interface), if you
want to write portable code make sure you use the interface in IM2/4.

	The only problem with connecting to 3rd party cards is the original
'appliance' idea (its a closed box, it only has two serial ports ..... etc
etc). At the moment the only 'known' ports have names '.AIn'/'.Aout',
'.Bin'/'.Bout', if you want to connect to 3rd party boards you have to 
know their 'real' names. Apple have publicly said that this will all be fixed
in the medium term (ie probably sometime next year), in the short term
I have code which I have put in the public domain (mail me if you want a copy)
that searches the driver name space (the unit table) for driver pairs that
have names of the form <PREFIX>In<SUFFIX> and <PREFIX>Out<SUFFIX> ('In'/'Out'
are case insensitive) and returns a list to the caller, this seems to
identify all the drivers of all the boards I know of.


		Paul Campbell

-- 
Paul Campbell, UniSoft Corp. 6121 Hollis, Emeryville, Ca
	E-mail:		..!{ucbvax,hoptoad}!unisoft!paul  
Nothing here represents the opinions of UniSoft or its employees (except me)
"Nuclear war doesn't prove who's Right, just who's Left" (ABC news 10/13/87)

han@Apple.COM (Byron Han, Architect) (09/30/88)

Direct manipulation of the unit table or traversal thereof is strictly
forbidden.  Use the of code Paul posted is **strictly at your own risk**
You will probably break in the future.

The Macintosh Communications Toolbox will contain a Communications Resource
Manager which will provide a directory services for the registration and
lookup of communications resources.

------------------------------------------------------------------------------
Byron Han, Communications Architect                   "Just say NO to MS-DOS."
Apple Computer, Inc.                     -------------------------------------
20525 Mariani Ave, MS27Y                 domain: han@apple.COM
Cupertino, CA 95014                      UUCP:{sun,voder,nsc,decwrl}!apple!han
--------------------------------------   GENIE: BYRONHAN
ATTnet: 408-973-6450   Applelink: HAN1   CompuServe: 72167,1664
------------------------------------------------------------------------------

imp@crayview.msi.umn.edu (Chuck Lukaszewski) (09/30/88)

In article <10407@tekecs.TEK.COM>, jeffsi@tekecs.TEK.COM (Jeff Siegel) writes:
> Help!!  I am trying desperately to write a serial driver which will
> allow me to: (1) trap the interrupt
>              (2) load the data pending
>              (3) do some special processing whenever a specific sized
>                  packet has been completely grabbed

I'm not certain whether you want to replace the SERD driver or just do some
special processing above and beyond it, but here are some things to consider:

The Zilog 8530 is configured entirely from software, and its two ports may be
manipulated basically independently.  I don't think that Apple would be too
upset if you were to trap the level-2 interrupts for a given port, as there is
no real provision anywhere in IM for what you want to do, and the address of
the level-2 interrupt vector is constant in the 680x0 arch..  HOWEVER, I would
STRONGLY caution you to be multifinder friendly as a test of your interface
to the rest of the operating system.  That implies that I ought to
be able to run Red Ryder out the other port while using your program.

To this end, I believe that you could simply vector the level-2 interrupt
to you and then check the IFR register in the 8530 to see if it is for you.
If not, you must jump to the routine that was there before.  In this situation,
it is CRITICAL that you leave the stack alone (i.e. don't do the initial
processing that the level-2 routine does now) and jump to the TOP of the level-2
routine and not to some offset inside of it.  I say this for three reasons:
(1) To minize the rule-bending/breaking affect as little as possible, and
it seems to me that changing the level-2 interrupt address is plenty;
(2) You can't make assumptions about which machine you are running on so you
can't assume the header code in the interrupt handler will be the same and (3)
that code may be totally different anyway as someone else may have already
trapped it.

You can do all of those things that you want by paying attention to appropriate
registers and enables in the 8530.

As far as machine compatibility goes, be SURE to use the global SCCBase rather
than hardcoding addresses.  (I know, I know - globals are off-limits but there
is no OS routine to give that value).  This is especially true if you are
running on the Lisa.  BTW, if you want to run on the Lisa, note that the 8530
interrupts are level 6 (!!) rather than level 2.  The 8530 is mapped into mem-
ory just as on the Macintosh Plus/SE/II, however, so SCCBase will work properly.
There is also a baud rate compensation you must do because of a 2% difference
in speeds between the machines.  Write to me if you need more information.

---===---===---===---===--/* Chuck Lukaszewski */--===---===---===---===---
ARPAnet/NSFnet/MRnet:      AppleLink:  SnailMail:              Ma Bell:
imp@crayview.msi.umn.edu   UG0138      Minneapolis MN 55418    612/789-0931

tim@hoptoad.uucp (Tim Maroney) (09/30/88)

In article <17974@apple.Apple.COM> han@apple.com.UUCP (Byron Han, Architect)
writes:
>Direct manipulation of the unit table or traversal thereof is strictly
>forbidden.  Use the of code Paul posted is **strictly at your own risk**
>You will probably break in the future.

Oh?  I am not completely up to date on technical notes (the last one I have is
#162) but Tech Note #71, "Finding Drivers in the Unit Table", gives sample
code that iterates over the unit table.  I don't recall seeing anything
about this in the Compatibility Guidelines, either.

It's really irritating to be told over and over that everything will be
fine if we just follow the rules, only to have Apple repeatedly change
the rules, and get hostile if we point out ways the rules can't be
followed (e.g., socket listeners accessing globals under Multifinder).
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"I wrapped a newspaper round my head, so I'd look like I was deep.
 I said some mumbo-jumbos then: I told him he was going to sleep.
 I robbed his ring, his pocket watch, and everything else I found.
 I had that sucker hypnotized; he didn't even make a sound!"
    - Frank Zappa, "Kozmik Debris"

paul@unisoft.UUCP (n) (09/30/88)

Lots of people have asked for copies of the code to find 3rd party
serial boards so I'm posting it here (we don't get comp.sources.mac
here). Please remember that this is only a temporary solution, Apple
have announced that they are producing a much more elegant solution
to this problem - this is probably the best we can use in the 
mean time


	Paul Campbell

PS: sorry to those who got the version I posted earlier today, someone
reported a silly bug just after I posted it (Murphy strikes again). This
is the correct one.

			     Cut between the dotted lines
--------------------------------------------------------------------------------
/*
 *
 *		Copyright Paul Campbell, September 1988, All Rights Reserved.
 *				Taniwha Systems Design
 *
 *		The material contained in this document is copyright, as described
 *		above. Permission is granted for you to use it provided you follow
 *		the following restrictions:
 *
 *		- this document may not be reproduced without this copyright message
 *			included
 *
 *		- you may not sell this source file
 *
 *		- you may use object code derived from this document in a commercial
 *			product provided you do not charge any additional fee because of
 *			its inclusion
 *
 *		Paul Campbell
 *		Taniwha Systems Design
 *		4368 Montgomery St
 *		Oakland
 *		CA 94611
 *		(415) 420-8179
 *
 *		Free plug .....
 *
 *			This code was developed for use with SuperMac's CommCard an intelligent
 *			4 port asynchronous serial (or 1 port LocalTalk) card which runs with
 *			the Mac Operating System or A/UX. Specifications are:
 *
 *			Serial:
 *							- baud rates 50-38400 baud
 *							- 6k output/200 byte input buffers
 *							- onboard software/hardware flow controll
 *
 *				MacOS		- built-in serial port like programming interface
 *							- built-in protocol support for X/Y/Z-modem, Kermit,
 *							  Quick-B and CompuServe DL protocols
 *
 *				A/UX		- streams/tty programming interface
 *							- built-in protocol support for UUCP 'G' protocol
 *
 *			LocalTalk:
 *							- onboard LAP support (offloads most network overhead
 *							  to the card
 *							- 25 packets onboard buffering
 *
 *				MacOS		- single card support under Apple's LAP manager
 *
 *				A/UX		- full kernel support for DDP/NBP/ATP/PAP/ZIP/RTMP
 *							  protocols
 *							- TransScript backend for PostScript conversion and
 *							  printing to LaserWriters from A/UX
 *							- multiple cards can be used to implement bridges
 *							  running in the background
 *							- AppleShare Server included
 *							- TOPS Server and mailbridge available from StarNine
 *							  Technologies, Berkeley
 *
 *			For more info contact Scott Meltzer at SuperMac (415)962-2491
 */
 
#include <Resources.h>
#include <Memory.h>
#include <Devices.h>
#include <Files.h>

#define UTableBase		(*(DCtlHandle **)0x11C)	/* unit I/O table [pointer] */
#define	UnitNtryCnt 	(*(short *)0x1D2)		/* count of entries in unit table [word] */
#define RamFlag			0x0040					/* driver is Ram Based */

struct driver {
	short	drvrFlags;
	short	drvrDelay;
	short	drvrEMask;
	short	drvrMenu;
	short	drvrOpen;
	short	drvrPrime;
	short	drvrCtl;
	short	drvrStatus;
	short	drvrClose;
	char	drvrName[64];
};

/*
 *		This file contains code designed to search the Mac Unit Table for
 *		devices that are PROBABLY serial lines. This is intended as a
 *		temporary solution of how to find extra serial lines in a Macintosh
 *		until Apple comes up with its promised Communications Manager which
 *		will be a much better (and elegant) solution to this problem.
 *
 *		The code is written in MPW C.
 *
 *		The basic technique used here is search for two drivers with names that
 *
 *			- start with '.'
 *
 *			- One has the form of <PREFIX>In<SUFFIX> ('In' can ne in any case)
 *
 *			- The other has the form <PREFIX>Out<SUFFIX>
 *
 *			- the strings <PREFIX> and <SUFFIX) for the two devices must resp.
 *			  match. Either (but not both) can be of zero lengh.
 *
 *		Of course we can't make absolutely certain that these are serial drivers
 *		but for the moment it should suffice.
 *
 *		The results are returned in the data structure declared below, it should be
 *		callable from Pascal without change (in C dont forget to declare it
 *		'pascal ... extern'). 
 *
 *		The last routine in this file is used for my testing and is also an example
 *		of how to call find_serial(). One can obtain a list of all the serial devices
 *		in a system by code like:
 *
 *				for (ind = 0; ;) {
 *					ind = find_serial(ind, &result);
 *					if (ind < 0)
 *						break;
 *
 *					.... do something with result ....
 *	
 *				}
 *
 *		A name is returned in 'real_name' which is recommend for use when providing
 *		menus to users.
 */


/*
 *	returned structure - all strings are 'pascal' counted strings
 */
 
struct serial_entry {
	char		real_name[64];		/* The <PREFIX><SUFFIX> name */
	short		in_refnum;			/* input refnum */
	char		in_name[64];		/* input driver name to pass to PBOpen() */
	short		out_refnum;			/* output refnum */
	char		out_name[64];		/* output driver name to pass to PBOpen() */
};

pascal short
find_serial(s, sp)
short s;
struct serial_entry *sp;
{
	DCtlHandle *dpp, dp;			/* pointers into unit table */
	short count, len;
	struct driver *dvp, **dvpp;		/* pointers to drivers */
	char prefix[64], suffix[64];	/* prefix/suffix values */	
	short plen, slen;				/* their lengths */
	int i, j, out;
	
	count = UnitNtryCnt;					/* unit table length */
	dpp = UTableBase;						/* unit table address */
	for (; s < count; s++) {				/* loop thru the unit table */
		if ((dp = dpp[s]) == 0)				/* ignore empty entries */
			continue;
		if ((*dp)->dCtlFlags&RamFlag) {		/* must do indirection for ram drivers */
			dvpp = (struct driver **)(*dp)->dCtlDriver;
			len = (*dvpp)->drvrName[0];						/* get driver name length */
			if (len <= 4 || (*dvpp)->drvrName[1] != '.')	/* sanity check */
				continue;
			out = -1;
			for (i = 2; i <= (len-1); i++) {				/* look for 'In' in the name */
				if (((*dvpp)->drvrName[i] == 'I' ||
					 (*dvpp)->drvrName[i] == 'i') &&
					((*dvpp)->drvrName[i+1] == 'n' ||
					 (*dvpp)->drvrName[i+1] == 'N')) {
					plen = i - 2;							/* isolate the prefix/suffix */
					for (j = 0; j < plen; j++)
						prefix[j] = (*dvpp)->drvrName[j+2];
					slen = len - i - 1;
					for (j = 0; j < slen; j++)
						suffix[j] = (*dvpp)->drvrName[i+j+2];
															/* search for a matching 'Out' */
					if ((out = find_serial_out(sp, len, plen, prefix, slen, suffix)) >= 0) {
						for (i = 0; i <= len; i++)
							sp->in_name[i] = (*dvpp)->drvrName[i];
					}
					break;
				}
			}
		} else {
			dvp = (struct driver *)(*dp)->dCtlDriver;		/* same as above but with no */
			len = dvp->drvrName[0];							/*		indirection */
			if (len < 4 || dvp->drvrName[1] != '.')
				continue;
			out = -1;
			for (i = 2; i <= (len-1); i++) {
				if ((dvp->drvrName[i] == 'I' ||
					 dvp->drvrName[i] == 'i') &&
					(dvp->drvrName[i+1] == 'n' ||
					 dvp->drvrName[i+1] == 'N')) {
					plen = i - 2;
					for (j = 0; j < plen; j++)
						prefix[j] = dvp->drvrName[j+2];
					slen = len - i - 1;
					for (j = 0; j < slen; j++)
						suffix[j] = dvp->drvrName[j+i+2];
					if ((out = find_serial_out(sp, len, plen, prefix, slen, suffix)) >= 0) {
						for (i = 0; i <= len; i++)
							sp->in_name[i] = dvp->drvrName[i];
					}
					break;
				}
			}
		}
		if (out < 0)
			continue;
		sp->real_name[0] = len = plen + slen;				/* build a real_name */
		for (i = 1; i <= plen; i++)
			sp->real_name[i] = prefix[i-1];
		if (slen > 0 && plen > 0) {							/* add '-' to complex names */
			len++;
			sp->real_name[0]++;
			sp->real_name[i++] = '-';
			for (; i <= len; i++)
				sp->real_name[i] = suffix[i-plen-2];
		} else {
			for (; i <= len; i++)
				sp->real_name[i] = suffix[i-plen-1];
		}
		if (len == 1) {										/* treat the built-in ports specially */
			if (sp->real_name[1] == 'A') {
				sp->real_name[0] = 5;
				sp->real_name[1] = 'M';
				sp->real_name[2] = 'o';
				sp->real_name[3] = 'd';
				sp->real_name[4] = 'e';
				sp->real_name[5] = 'm';
			} else
			if (sp->real_name[1] == 'B') {
				sp->real_name[0] = 7;
				sp->real_name[1] = 'P';
				sp->real_name[2] = 'r';
				sp->real_name[3] = 'i';
				sp->real_name[4] = 'n';
				sp->real_name[5] = 't';
				sp->real_name[6] = 'e';
				sp->real_name[7] = 'r';
			} else {
				sp->real_name[0] = 8;						/* do something for those who */
				sp->real_name[8] = sp->real_name[1];		/* follow a different tune */
				sp->real_name[1] = 'S';
				sp->real_name[2] = 'e';
				sp->real_name[3] = 'r';
				sp->real_name[4] = 'i';
				sp->real_name[5] = 'a';
				sp->real_name[6] = 'l';
				sp->real_name[7] = '-';
			}
		}
		sp->in_refnum = -(s+1);
		return(s+1);
	}
	return(-1);
}


/*
 *		This routine searches the unit table for a matching 'Out' 
 *		entry.
 */
 
static int
find_serial_out(sp, length, plen, prefix, slen, suffix)
struct serial_entry *sp;
short plen, slen;
char *prefix, *suffix;
{
	DCtlHandle *dpp, dp;
	short count, len;
	struct driver *dvp, **dvpp;
	int i, j, out, s;
	
	count = UnitNtryCnt;
	dpp = UTableBase;
	for (s = 0; s < count; s++) {							/* search the table */
		if ((dp = dpp[s]) == 0)
			continue;
		if ((*dp)->dCtlFlags&RamFlag) {						/* do indirections */
			dvpp = (struct driver **)(*dp)->dCtlDriver;
			len = (*dvpp)->drvrName[0];
			if (len != (length+1) || (*dvpp)->drvrName[1] != '.') /* simple screening */
				continue;
			if (((*dvpp)->drvrName[plen+2] != 'O' &&		/* look for 'Out' in the right place */
				 (*dvpp)->drvrName[plen+2] != 'o') ||
				((*dvpp)->drvrName[plen+3] != 'u' &&
				 (*dvpp)->drvrName[plen+3] != 'U') ||
				((*dvpp)->drvrName[plen+4] != 't' &&
				 (*dvpp)->drvrName[plen+4] != 'T'))
				 	continue;
			for (i = 0; i < plen; i++)						/* compare prefixes */
			if (prefix[i] != (*dvpp)->drvrName[i+2])
				break;
			if (i < plen)
				continue;
			for (i = 0; i < slen; i++)						/* compare suffixes */
			if (suffix[i] != (*dvpp)->drvrName[i+plen+2+3])
				break;
			if (i < slen)
				continue;
			for (i = 0; i <= len; i++)						/* copy out the name to the result */
				sp->out_name[i] = (*dvpp)->drvrName[i];
		} else {
			dvp = (struct driver *)(*dp)->dCtlDriver;		/* do the same for ROM drivers */
			len = dvp->drvrName[0];
			if (len != (length+1) || dvp->drvrName[1] != '.')
				continue;
			if ((dvp->drvrName[plen+2] != 'O' &&
				 dvp->drvrName[plen+2] != 'o') ||
				(dvp->drvrName[plen+3] != 'u' &&
				 dvp->drvrName[plen+3] != 'U') ||
				(dvp->drvrName[plen+4] != 't' &&
				 dvp->drvrName[plen+4] != 'T'))
				 	continue;
			for (i = 0; i < plen; i++)
			if (prefix[i] != dvp->drvrName[i+2])
				break;
			if (i < plen)
				continue;
			for (i = 0; i < slen; i++)
			if (suffix[i] != dvp->drvrName[i+plen+2+3])
				break;
			if (i < slen)
				continue;
			for (i = 0; i <= len; i++)
				sp->out_name[i] = dvp->drvrName[i];
		}
		sp->out_refnum = -(s+1);							/* return the refnum and success */
		return(s);
	}
	return(-1);
}


#ifdef TEST
list_serial()
{
	register short i, j, k;
	register short count = 0;
	struct serial_entry s[50];
	struct serial_entry *sp[50], *x, **spp;
	char cc[200];
	
	/*
	 *	build a list of devices
	 */
	 
	j = 0;
	for (i = 0; ;i++) {
		j = find_serial(j, &s[i]);
		if (j < 0)
			break;
		sp[i] = &s[i];
		count++;
	}
	
	/*
	 *	sort the list
	 */
	 
	for (i = 0; i < (count-1); i++) {
		spp = &sp[i];
		for (j = i+1; j < count; j++) {
			for (k = 0; ;k++) {
				if (sp[j]->real_name[0] == k) {
					if ((*spp)->real_name[0] != k)
						spp = &sp[j];
					break;
				}
				if ((*spp)->real_name[0] == k) 
					break;
				if (sp[j]->real_name[k+1] < (*spp)->real_name[k+1])
					spp = &sp[j];
				if (sp[j]->real_name[k+1] != (*spp)->real_name[k+1])
					break;
			}
		}
		if (spp != &sp[i]) {
			x = sp[i];
			sp[i] = *spp;
			*spp = x;
		}
	}
	 
	/*
	 *	print the list
	 */
	 
	for (i = 0; i < count; i++) {
		sprintf(cc, "%d: '", i);
		logit(cc);
		sp[i]->real_name[1+sp[i]->real_name[0]] = '\0';
		logit(&sp[i]->real_name[1]);
		sprintf(cc, "' [%d]='", sp[i]->in_refnum);
		logit(cc);
		sp[i]->in_name[1+sp[i]->in_name[0]] = '\0';
		logit(&sp[i]->in_name[1]);
		sprintf(cc, "' [%d]='", sp[i]->out_refnum);
		logit(cc);
		sp[i]->out_name[1+sp[i]->out_name[0]] = '\0';
		logit(&sp[i]->out_name[1]);
		logit("\n");
	}
	sprintf(cc, "%d entries found\n", count);
	logit(cc);
}

#endif /* TEST */
--------------------------------------------------------------------------------
-- 
Paul Campbell, UniSoft Corp. 6121 Hollis, Emeryville, Ca
	E-mail:		..!{ucbvax,hoptoad}!unisoft!paul  
Nothing here represents the opinions of UniSoft or its employees (except me)
"Nuclear war doesn't prove who's Right, just who's Left" (ABC news 10/13/87)