[comp.sys.amiga.tech] What about the serial port IPC discussion?

acs@amdahl.uts.amdahl.com (Tony Sumrall) (05/04/88)

I know that Doug Merritt and I were pretty much the only ones involved in
this but I *did* get some e-mail from others expressing interest.  As I
said in my postings, I will implement this in VT100 Rx.y when the serial
port IPC becomes stable.  Well folks, it's stable alright but it's not
exactly in any shape to be implemented.  So, does this mean that it's no
longer an issue?  Are we all too busy to do anything with it?
-- 
Tony Sumrall acs@amdahl.uts.amdahl.com <=> amdahl!acs

[ Opinions expressed herein are the author's and should not be construed
  to reflect the views of Amdahl Corp. ]

jesup@pawl12.pawl.rpi.edu (Randell E. Jesup) (05/06/88)

In article <30731@amdahl.uts.amdahl.com> you write:
>I know that Doug Merritt and I were pretty much the only ones involved in
>this but I *did* get some e-mail from others expressing interest.  As I
>said in my postings, I will implement this in VT100 Rx.y when the serial
>port IPC becomes stable.  Well folks, it's stable alright but it's not
>exactly in any shape to be implemented.  So, does this mean that it's no
>longer an issue?  Are we all too busy to do anything with it?
>-- 
>Tony Sumrall acs@amdahl.uts.amdahl.com <=> amdahl!acs

	Well, it was a big item of discussion at the Devcon, I was on
the panel (suprise), as was Bryce (suprise).  It seems there will be two
levels:  a low-level mapper in the serial.device that bryce is working on,
that maps unit numbers to other devices, and a name mapping utility, that
allows you to tell it you want a modem (or xyzvax via direct connect,...).

	Essentially, the user tells the system once what's hooked up to his
serial ports (that's what matters, anyhow).  The application says what name
it wants to open (like modem or modem_9600 or vaxline or ...).  The mapper
searchs it's list of names for serial ports, and opens the first one that
responds to an OpenDevice call (i.e. first one not busy).

	The user only needs to tell the system once about hardware, through
a preferences-type of program.

	The result of the Devcon was that I've generalized the mapper so it
can be used for naming software services (like the object oriented stuff
being discussed), and there most certainly will be a "working group" ala
Perry's (of ASDG) proposal for the formation of working groups on various
subjects (he should be posting the proposal soon).  I'll be posting a 
message soon recruiting people who want to be on the working group.

	BTW, I've been hired by Commodore, effective May 16.

	Following are some of the include files and docs I've been working on.
Warning: they may not all be fully modified to reflect the devcon stuff,
though they're close (in fact, I've written 1/2 the code for the mapper.device
already.)

/* user.doc - this file is NOT modified yet, but is mostly correct */
		      What is this mapper.device, anyway?
		      -----------------------------------

      Simply put, it allows you to name your ports (much like assign), and
open ports by name, not an arbitrary number (possibly changing from boot to
boot).  Usually, you only have one piece of hardware attached to a serial
port, and you're more interested in the hardware attached to ports than the
specific port.

      Each unit of a device could have several names, such as a serial
port with the names "modem", "modem_1200", "modem_2400", and "7".  Note
that if you like using numbers, you can name them with numbers.  These names
can be changed at any time by the user, and will survive reboots without
any modifications to your startup-sequence (they're stored in the ToolTypes
of the Expansion drawer icon for the board.)

      The user would be able to edit these names with a setup program, or
could, if they wanted, use the 'info' command of WorkBench.

      With expansion ports known by name or function, it should be simpler
for the user to configure new software.  For example, a terminal program
might default to opening "modem".  If the user has already set the name of
the port with a modem on it to, lets say "modem | modem_1200 | serial_2",
then the user would not have to change anything to run.

      When a program opens a port by name, the mapper.device will try to 
open a port of that name (if any exist).  As an option, you can tell it
to return the first unit with that name (ex: "modem") that can be opened
(i.e. open the first free modem).  This ability is especially useful for
jobs running unattended, like late-night file transfers or uucp.

      A side effect of how they're stored means that if you remove a board,
none of the names for the units on it will show up.  This eases maintenance
chores.  Also, for many programs you won't have to change the defaults
as to what it should open (ex: terminal programs can open "modem", they'll
get a modem regardless of what your port setup is.  Otherwise you'd have
to somehow tell it your modem is on port #3.)

      Another nice thing about the mapper.device is that it can be used
to provide namings for other types of hardware, such as parallel ports, and
hardware yet to be thought of, with no modifications necessary.

	The mapper.device will work under 1.2 or 1.3.  It requires only
installing the mapper.device in the devs drawer, and replacing the
serial.device with something that allows remapping old-style requests to
a name (or even querying the user when the open occurs.)  The old
serial.device would be called oldserial.device or internalserial.device.

	As far as AmigaDos/CLI level access, either the port-handler
would be rev'ed (best), or a new handler (XSER:) could be written (less
good).  With name mapping, you could open XSER:modem/1200 to open any
modem at 1200 baud.  'list xser:' would provide you with a list of all
the current mappings of serial ports (as if they were files).  The same
thing would occur for extra parallel ports.  Perhaps even things like
'assign modem1200: xser:modem/1200' would be workable.


	  What do I as an Applications Writer need to do to use this?
	  -----------------------------------------------------------

      From the application writers point of view, the code to take advantage
of additional devices is small.  The mapper.device can be used for all types
of hardware being mapped.  For a serial open, the IOExtMap looks like this:

	struct IOExtMap {
		struct IORequest *iom_Req;     /* for serial, an IOExtSer */
		UBYTE  *iom_Name;
		UWORD  iom_Type;
		UWORD  iom_Error;
		ULONG  iom_Flags;
		ULONG  iom_Reserved;
	};

	If you are writing a terminal program, I'd advise defaulting to
modem (though please try to make the default a user-settable option.)  For
other things, try to use a descriptive name of the hardware you expect to
be attached to the port.

	You might also might want to consider making IOMF_FIRST_FREE an
option for the user.


	To take advantage of the new functionality, the application writer
uses this to open a serial port:

	#include <device/mapper.h>
	...
	<set up IOExtSer as needed>
	...
	IOM->iom_Req	  = IOS; 		/* the IOExtSer */
	IOM->iom_Name     = "modem";
	IOM->iom_Flags    = IOMF_FIRST_FREE;	/* get first free modem */
	IOM->iom_Reserved = NULL;

	/* no need to set iom_Type, it is set by OpenSerial */

	switch (OpenSerial(IOM)) {
	case IOM_SUCCESS:
		/* the IOExtSer is opened, the IOExtMap can be released */
		break;	/* or return success */

	/* see mapper.h for more error codes */
	default:
		<handle failure>
		<may want to jump out of routine or exit here - careful
		 about falling through!>
	}

Note that you can handle the errors differently by checking for specific
failure codes in the switch (like all busy, or no match).  As you can see,
the mapping ability only requires a few lines to take advantage of.



For your information, this is what OpenSerial looks like:

ULONG
OpenSerial (IOM)
	struct IOExtMap IOM;
{
	if (!IOM || !IOM->iom_Req)
		return IOM_NO_REQUEST;

	IOM->iom_Type = MAP_SERIAL;

	if (OpenDevice("mapper.device", 0L, IOM, MAP_SERIAL))
	{
		/* mapper doesn't exist, for compatiblity try 1.2 open */

		if (OpenDevice("serial.device", 0L, IOM->iom_Req, 0L))
			return IOM_TOTAL_FAILURE;

		return IOM_SUCCESS;
	}
	/* mapper exists, did it manage to open a serial port?   */
	/* note that we can't just use the return of OpenDevice. */
	/* It would be nice if we could, but the error return for */
	/* an unknown device isn't documented.			 */
	/* I could have used the standard io_Error, but I wasn't */
	/* certain that was kosher.  Either way works.		 */

	return IOB->iom_Error;
}

/* mapperbase.h - mainly for people producing expansion hardware */
#ifndef DEVICES_MAPPERBASE_H
#define DEVICES_MAPPERBASE_H

#ifndef EXEC_TYPES_H
#include <exec/types.h>
#endif

#ifndef EXEC_NODES_H
#include <exec/nodes.h>
#endif

#ifndef EXEC_LISTS_H
#include <exec/lists.h>
#endif

#ifndef EXEC_SEMAPHORES_H
#include <exec/semaphores.h>
#endif

/*
	mapperbase.h				Last Update: 1-19-88 REJ

   This is a first cut at the in-memory data structures for the mapping and
   to identify what expansion boards are in the system.  I've tried to make
   sure it is easily expanded in the future.  Comments and suggestions would
   be greatly appreciated, even more so from people doing serial expansion
   boards or using serial.device.  These are by no means graven in silly
   putty. :-)

   	Randell Jesup, Lunge Software, 13 Frear Ave, Troy, NY 12180
   	(518) 272-2942 (until mid-may)
	(Soon to be cbmvax!jesup)

  Change List:
   5- 5-88 REJ	changed some single-linked lists to minnodes & exec lists
   5- 4-88 REJ  pulled all the allocation routines into mapper.device
   5- 3-88 REJ	added support for software naming, MinNodes, all the ETF_*'s
		Thanks to everyone for the input at DevCon!
   4-26-88 REJ	changed flags to number for board types, changed structure
		names, added flags, added #includes, + other.
   4- 1-88 REJ  Reordered things, added more comments
   1-19-88 REJ	Changed from library to named SignalSemaphore
   1-18-88 REJ	Added support for multiple types of boards.
   1-16-88 REJ	Created
*/

/* The structures were designed for easy use with the Exec list-handling */
/* routines.								 */
/* I've tried to make these multiples of 8 bytes long to make most the   */
/* most efficient use of memory.					 */

/* explanation of structures:
	There is one MapperBase.
	The MapperBase may have any number of different types of things.
	Each type may have several 'boards'.
	Each 'board' may have several 'units'.
	Each 'unit' may have several names (mappings).

	There are lists of boards, units, and mappings for each type of
	thing being named.  Currently, it is envisioned that expansion boards,
	serial, parallel, A/D's, etc, as well as software/network services
	will be types, plus things not thought of yet.

	Each mapping is linked to it's unit, each unit is linked to
	it's board.  This allows you to find what unit a mapping is for
	easily.

	In addition, boards are linked to their units, and units are
	linked to their mappings, in singly linked lists, so one may
	traverse, for example, all mappings of a unit, or all the units of
	a board.

	The mapper.device will attempt to open in the manner specified for
	that type when OpenDevice("mapper.device",0,IOM,MAP_xxxx) is called.

	It also has extended entries, ala the console.device, for calling
	to get returned lists of names/units that match a name/pattern.
	(For those 'roll-your-own' ways of handling mapping.)  There are
	extended entries to allocate all structures for you, and to deallocate
	mappings.

	These extended features of the mapper.device aren't important for
	simple hardware devices, like serial ports.  Just use the supplied
	code and examples.
*/

/* board types */
/* MAP_NOTYPE is used to get to the extended entrypoints for the device */

#define MAP_NOTYPE		0
#define MAP_SERIAL		1
#define MAP_PARALLEL		2
#define MAP_ATOD		3

/* for type flag var - methods of opening for mapper.device */
/* note that MAP_SERIAL, MAP_PARALLEL, and MAP_ATOD all use ETF_OPENDEVICE */

#define ETB_UNIT		0	/* just return ptr to unit */
#define ETF_UNIT		(1L << ETB_UNIT)

#define ETB_OPENDEVICE		1	/* use OpenDevice calls */
#define ETF_OPENDEVICE		(1L << ETB_OPENDEVICE)

#define ETB_MESSAGE		2	/* send ior to port - wait for reply */
#define ETF_MESSAGE		(1L << ETB_MESSAGE)
/* port address is stored in et_Open - io_Error indicates success/fail */

#define ETB_SIGNAL		3	/* signal task */
#define ETF_SIGNAL		(1L << ETB_SIGNAL)
/* Task is stored in et_Open - assume success */

#define ETB_CODE		4	/* call code, pass ior & unit as param*/
#define ETF_CODE		(1L << ETB_CODE)
/* Code address is stored in et_Open - called with params ior & unit in */
/* A0 and A1 respectively, AS WELL as on the stack for C programs.	*/
/* (OpenRoutine(ior,unit))						*/
/* the routine should return a boolean TRUE/FALSE for success/fail	*/

/* for unit and board flags vars */
#define UNITB_DISABLED		0
#define UNITF_DISABLED		(1L << UNITB_DISABLED)
#define UNIT_FLAGS_RESERVED	~(UNITF_DISABLED)

#define BOARDB_DISABLED		0
#define BOARDF_DISABLED 	(1L << BOARDB_DISABLED)
#define BOARD_FLAGS_RESERVED	~(BOARDF_DISABLED)


/* this is the name & priority of the signal semaphore in mapperbase */

#define MAPPERBASE_NAME	"mapperbase_lock"
#define MAPPERBASE_PRI	-5


/* this is the structure to pass to the CreateBoard function of the	*/
/* mapper.device.  See below and above for the meanings of the fields.	*/
/* There is an assumption that the Units will be consecutive starting	*/
/* at some number.  You may have to do more setup, the address of	*/
/* your ExpansionBoard structure will be returned (you can get the	*/
/* ExpansionType structure easily).					*/
/* This purpose of this is to make setup very easy.			*/
/* For the simple setup (a serial device for example), this should be   */
/* all you need.							*/

struct MappingSetup {
	ULONG			ms_Flags;	/* null for now */

	/* ExpansionType stuff */
	ULONG			ms_Type;
	ULONG			ms_TypeFlags;
	CPTR			ms_TypeOpen;	/* if flags need it */

	/* mappings are drawn from the DiskObject */
	/* if null, no mappings will be set up for you */
	struct DiskObject	*ms_Obj;

	/* HardwareStuff */
/* if not hardware, set ms_DevName to 0, and no HardwareStuff will be created */
	UBYTE			*ms_Devname;
	UBYTE			*ms_Devpath;
	ULONG			ms_MID;
	ULONG			ms_PIC;
	CPTR			ms_Address;

	/* ExpansionUnit stuff */
	/* n units starting with m */
	ULONG			ms_NumUnits;
	ULONG			ms_FirstUnit;

	CPTR			ms_Reserved;	/* null for now */
};

/* A single name for a unit.						   */
/* note that the Node is used to link all Mapping structures for a given   */
/* board type, while the NextMapping field links the Mappings for a single */
/* unit together.							   */

struct Mapping {
	struct Node	     map_Node;

	/* ln_Name points to null-terminated name			*/
	/* ln_Type should be 0 for now					*/
	/* Note: prioritized node - 0 is default			*/
	/* map_Reserved should be NULL for now!				*/

	struct MinNode 	     map_Mappings;	/* next mapping for same unit */
	struct ExpansionUnit *map_Unit;		/* which unit associated with */
	CPTR		     map_Reserved;	/* set to NULL!!! */
};


/* this is the structure for a 'unit' of a 'board'. */

struct ExpansionUnit {
	struct MinNode eu_Node;
	ULONG  eu_Number;		   /* could be used for other things  */
	struct MinNode eu_Units;	   /* all units of a board are linked */
	struct ExpansionBoard *eu_Board;   /* links back to board */
	struct List eu_Mappings;	   /* mappings for this unit */
	ULONG  eu_Flags;		   /* see defs above */
	CPTR   eu_Data;			   /* for private use by driver */
	CPTR   eu_Reserved;		   /* Future expansion */
};


/* This defines a 'board'.  I use quotes, because for software stuff, it may */
/* not really be hardware at all.					     */

struct ExpansionBoard {
	struct MinNode eb_Node;
	ULONG eb_NumUnits;	/* # of units on this board */
	struct List eb_Units;	/* units for this board */
	ULONG eb_Flags;		/* see above for defs */
	CPTR  eb_Stuff;		/* points to structure that depends on type */
				/* usually, this will be hardware info      */
	CPTR  eb_Data;		/* for driver private use */
	CPTR  eb_Reserved;	/* leave NULL! */
};

/* this is for eb_Stuff above, for hardware types			     */
/* equivalent ones will be worked out for any software types developed       */

struct HardwareStuff {
	UBYTE *eh_DevName;
		/* DevName must be set to device name! */
		/* Note: do not include any path eg: myserial.device */
	UBYTE *eh_DevPath;
		/* file name of device to load */
		/* include path. eg: DEVS:serial/myserial.device */
		/* path needed if not in devs: */
		/* if opendevice on plain name (from ln_Name) fails, use */
		/* this path to attempt to open it */
	ULONG eh_ManufacturerID;
	ULONG eh_ProductID;	/* PIC */
	CPTR  eh_Address;	/* address board was assigned */
	CPTR  eh_Reserved;	/* Null, if you please */
};

/* This is the structure for a type.  It specifies what the mapper.device */
/* should do with opens for the type.					  */

struct ExpansionType {
	struct MinNode et_Node;
	struct List et_BoardList;	/* list of ExpansionBoards */
	struct List et_UnitList;	/* list of ExpansionUnits */
	struct List et_MappingList;	/* list of names (prioritized) */
	ULONG et_Type;			/* MAP_SERIAL for serial ports */
	ULONG et_Flags;			/* see above (ETF_*) */
	CPTR  et_Open;			/* used according to Flags */
	CPTR  et_Reserved[2];		/* set to NULL for now */
};


/* The semaphore is named "mapperbase_lock", priority -5 */
/* user MAPPERBASE_NAME and MAPPERBASE_PRI to get those  */

struct MapperBase {
	struct SignalSemaphore mb_Access;	/* MUST obtain before looking */
						/* at or modifying lists! */
	struct List mb_TypeList;	/* Find the type you want */
					/* points to struct ExpansionType */
	ULONG mb_Flags;			/* NULL for now */
	CPTR mb_Reserved[4]; /* I want a bunch of space for expansion here */
};

/*
 *	To access any of the lists, you MUST obtain the semaphore!!!!
 *
 *	This is designed so the name mapper can be used for other types
 *	of expansion hardware, such as parallel ports (or anything else).
 *	You should set the MAP_SERIAL type in the ExpansionType structures
 *	for a serial port.  Note that it can be used for software, or anything
 *	else, not just hardware.
 *
 * 	The MapperBase, ExpansionBoard and ExpansionUnit structure lists
 *	should remain in memory after creation.  The Mapping structures can
 *	be released; note the name was obtained by AllocMem(strlen(name)+1,
 *	MEMF_PUBLIC).  Do not release a Mapping structure if the
 *	MapReserved field is non-null (for safe expansion).
 *
 *	All these structures should be in public memory.  The private data
 *	areas for the drivers are meant for data storage when the driver
 *	isn't loaded (or hasn't been loaded yet.)  Make NO assumptions about
 *	what they point to, if anything at all.
 *
 *	The code attached to each icon in the expansion drawer should
 *	create a ExpansionBoard structure for each board it is handling.
 *	This is done for you by the CreateBoard call of the mapper.device.
 *	Note: the lists of Mappings are prioritized, default is 0.  All
 *	canned calls are to the extended entries of the mapper.device (ala
 *	console.device).  Link with mapper.lib (or include <proto/mapper.h>
 *	for Lattice 4.0 users).
 *
 *	For non-hardware types, they're on their own to come up with ways
 *	to set up the names.  For example, you might run a program during
 *	startup that queries the network as to which hosts have what
 *	services available.  They may also have to create and set whatever
 *	they want in the eb_Stuff field of their board.
 *
 *	The syntax for the tooltypes in the icons is:
 *		<name>     := <non-whitespace chars excluding ':'>;
 *				Should these be case sensitive?
 *				If so, what about foreign languages?
 *				Should whitespace be allowed?
 *		<mapping>  := <name>[:<priority>];
 *		<tooltype> := Unit_<number>=<mapping>[|<mapping>...]
 *
 *	Note there may be more than one Unit_<number> for the same number in
 *	the tooltypes for a given icon.
 *
 *	General flowchart for expansion drawer code:
 *
 *	    <init - make an struct IOExtMap *IOM>
 *	    if (OpenDevice("mapper.device",0,IOM,MAP_NOTYPE) == 0)
 *	    {
 *		base   = GetMapBase();
 *
 *		<for each board you will handle> {
 *			<do any board init needed>
 *			<mark board as configured>
 *
 *			<GetDiskObject so you can get the tooltypes>
 *			<set up the struct MappingSetup>
 *
 *			board = CreateBoard(myMappingSetup);
 *
 *			<if you need to modify anything, do here>
 *			< it shouldn't be necessary for hardware types >
 *		}
 *		<do anything else you want>
 *
 *		CloseMapBase();
 *		CloseDevice(IOM);
 *	    }
 *
 *	    <clean up, release stuff like libraries, locks, whatever>
 *	    <if driver not loaded now and not in rom>
 *		<return NULL - program will be unloaded -
 *		 driver will be loaded on reference>
 *	    <else do whatever, return non-NULL - program remains in mem>
 *
 *
 */

/* mapper.doc - documents extended device entry points */
Calls available from the mapper.device:
(Put io_Device into the variable MapBase.)

	struct MapperBase *GetMapBase(void)
		  A0

		Returns address of MapperBase, locks structure.
		Must be balanced with a call to CloseMapBase.

	void CloseMapBase(void)

		Unlocks the MapperBase.  

	struct ExpansionBoard *CreateBoard(struct MappingSetup *setup)
		   D0				    A0
	
		Creates an ExpansionBoard structure, a number of Unit
		structures, and a number of Mappings structures for each unit.
		You must call GetMapBase before using this!
		The pointer to the Board structure is returned so you
		can modify things CreateBoard doesn't deal with (see
		mapperbase.h).

	struct ExpansionBoard *FirstBoard(type)
		D0			 A0

		Gets the first board for the type.

	struct ExpansionBoard *NextBoard(board)
		D0			  A0

		Gets the next board of the same type.  Assumes board was
		returned by FirstBoard.

/*
 * Note: for all calls returning units, do NOT look at the eu_FirstMapping
 *	 field unless the MapperBase has been locked via GetMapbase().
 */

	struct ExpansionUnit *FirstUnit(board)
		D0			 A0

		Gets the first unit for the board.

	struct ExpansionUnit *NextUnit(unit)
		D0			A0

		Gets the next unit of the same type.  Assumes unit was returned
		by FirstUnit.

	struct ExpansionUnit *FindFirstUnit(type,name)
		D0			     D1?  A1

		Finds the first unit of the type with the name, and returns
		a pointer to the unit.  Null is returned if there is no
		match.  NOTE:  the method used for this is NOT priority
		based.  It searches unit by unit so FindNextUnit will work!

	struct ExpansionUnit *FindNextUnit(unit,name)
		D0			    A0   A1

		Finds the next unit of the type with the name, and returns
		a pointer to the unit.  Assumes unit was obtained via
		FindFirstUnit.

	struct Mapping *GetFirstMapping(type)
		D0?			 A0

		Gets the first mapping for a type.  NOTE: MapperBase MUST be
		locked by a call to GetMapBase!  Do not look at any Mapping
		structure when the base is not locked!

	void AddMapping(type,unit,name,pri)
			 D0   D1   A1  D2

		Adds a mapping to a specified unit.

	void RemMapping(type,unit,name)
			 D0   A0   A1

		Removes a specific name for a unit.


/* mapper.c - code written so far as part of the mapper.device */
/* mapper.c */

#include "exec/types.h"
#include "exec/memory.h"
#include "exec/devices.h"
#include "exec/io.h"
#include "string.h"
#include "dos.h"

#ifdef LATTICE_V4
#include "proto/exec.h"		/* lattice >= 4.0 */
#endif

#include "mapperbase.h"
#include <devices/mapper.h>
#include "mapdev.h"

extern struct MapBase *MapBase;

/* code for obtaining/creating mapperbase */

struct MapperBase *
GetMapBase()
{
	register struct MapperBase *base;

	Forbid();
	if (MapBase->MapperBase == NULL)
	{
/* this is in forbid so two tasks don't try to make it at the same time. */
/* Note: I'm playing tricks and allocating everything at once to save on */
/* error checking code.							 */
/* note: the semaphore is no longer public!!!  ONLY obtain via this	 */
/* function!								 */

		base = (struct SignalSemaphore *)
			AllocMem(sizeof(struct MapperBase),
				 MEMF_CLEAR | MEMF_PUBLIC);
		if (base)
		{
			NewList(&base->mb_TypeList);
			MapBase->MapperBase = base;
		}
	}

	Permit();

	if (base)				/* may not have gotten it */
		ObtainSemaphore(&base->mb_Access);

	return (base);
}

void
CloseMapbase ()
{
	/* maybe we should make sure we have it locked?? */
	FreeSemaphore(&(MapBase->MapperBase->mb_Access));
}

struct ExpansionType *
GetExpansionType (setup)
	register struct MappingSetup *setup;
{
	register struct ExpansionType *curr;

	for (curr = (struct ExpansionType *)
		    MapBase->MapperBase->mb_TypeList.lh_Head;
	     curr->et_Node.ln_Succ;
	     curr = (struct ExpansionType *) curr->et_Node.ln_Succ)
	{
		if (curr->et_Type == setup->ms_Type)
			return (curr);
	}
	/* we got here, boardtype isn't in list */
	curr = (struct ExpansionType *) AllocMem(sizeof(struct ExpansionType),
						 MEMF_CLEAR | MEMF_PUBLIC);
	if (!curr)
		return NULL;

	NewList(&curr->et_BoardList);
	NewList(&curr->et_UnitList);
	NewList(&curr->et_MappingList);
	curr->et_Type  = setup->ms_Type;
	curr->et_Flags = setup->ms_TypeFlags;
	curr->et_Open  = setup->ms_TypeOpen;

	AddHead(base->mb_TypeList,curr);

	return curr;
}

/* makes main board, doesn't set everything for you	*/
/* Note AddUnit increments NumUnits			*/

struct ExpansionBoard *
MakeExpansionBoard (setup)
	register struct MappingSetup	*setup;
{
	register struct HardwareStuff	*hw;
	struct ExpansionBoard		*board;
	register unsigned short devlen = 0, parthlen = 0, length = 0;

	if (setup->ms_Devname)
		devlen = strlen(setup->ms_Devname);

	if (setup->ms_Devname && setup->ms_Devpath)
		pathlen = strlen(setup->ms_Devpath);

	length = sizeof(struct ExpansionBoard) + devlen + pathlen +
		 (setup->ms_Devname ? sizeof(struct HardwareStuff) + 2 : 0);

	/* the +2 was for nulls for the strings */

	/* note: I'm playing tricks and allocating all at once, to reduce */
	/* cleanup hassles if I got 1 and not the others		  */

	board = (struct ExpansionBoard *) AllocMem(length,
						   MEMF_PUBLIC | MEMF_CLEAR);
	if (!board)
		return NULL;

	if (setup->ms_Devname)
	{
		hw = (struct HardwareStuff *)
		     ((char *) board) + sizeof(struct ExpansionBoard));
		hw->eh_DevName = ((char *) hw) + sizeof(struct HardwareStuff);
		hw->eh_DevPath = hw->eh_DevName + devlen + 1;

		strcpy(hw->eh_DevName,setup->ms_Devname);
		strcpy(hw->eh_DevPath,setup->ms_Devpath);

		board->eb_Stuff = hw;
	}

	NewList(&board->eb_Units);

	AddHead(&type->et_BoardList,(struct Node *) &board->eb_Node);

	return board;
}

struct ExpansionUnit *
AddUnit (type, board, number)
		 struct ExpansionType  *type;
	register struct ExpansionBoard *board;
		 UWORD  number;
{
	register struct ExpansionUnit *unit;

	unit = (struct ExpansionUnit *) AllocMem(sizeof(struct ExpansionUnit),
						 MEMF_CLEAR | MEMF_PUBLIC);
	if (!unit)
		return NULL;

	unit->eu_Number     = number;
	unit->eu_Board	    = board;

	NewList(&unit->eu_Mappings);

	board->eb_NumUnits += 1;

	/* the list of units for a board */
	AddHead(&board->eb_Units,(struct Node *) &unit->eu_Units);

	/* the list of all units for a type */
	AddHead(&type->et_UnitList,(struct Node *) &unit->eu_Node);

	return unit;
}

/* Note: I'm NOT playing tricks with allocations here, as others may	*/
/* deallocate these.							*/

struct Mapping *
AddMapping (type, unit, name, pri)
	struct ExpansionType *type;
	struct ExpansionUnit *unit;
	char   *name;
	BYTE   pri;
{
	register struct Mapping *map;

	map = (struct Mapping *) AllocMem(sizeof(struct Mapping),
					  MEMF_PUBLIC | MEMF_CLEAR);
	if (!map)
		return NULL;

	map->map_Node.ln_Name = AllocMem(strlen(name)+1,MEMF_PUBLIC);
	if (!map->map_Node.ln_Name)
	{
		FreeMem((char *) map, sizeof(struct Mapping));
		return NULL;
	}
	strcpy(map->map_Node.ln_Name,name);

	map->map_Node.ln_Pri  = pri;
	map->map_Unit	      = unit;

	/* add to list of mappings for unit */
	AddHead(&unit->eu_Mappings,(struct Node *) &map->map_Mappings);

	/* the overall list of names for the type */
	Enqueue(&type->et_MappingList,map);	/* prioritized */

	return map;
}

void
FreeMapping (mapping)
	register struct Mapping		*mapping;
{
	Remove(&mapping->map_Node);	/* remove from queue */

	/* now unlink from unit */
	Remove((struct Node *) mapping->map_Mappings);

	FreeMem(mapping->map_Node.ln_Name,strlen(mapping->map_Node.ln_Name)+1);
	FreeMem(mapping,sizeof(struct Mapping));
}

void
FreeUnit (unit)
	register struct ExpansionUnit	*unit;
{
	register struct Mapping *mapping;

	Remove((struct Node *) &unit->eu_Node);

	/* now unlink from board */
	Remove((struct Node *) &unit->eu_Units);

	while ((mapping = unit->eu_Mappings.lh_First) != NULL)
		FreeMapping(mapping);

	FreeMem(unit,sizeof(struct ExpansionUnit));
}

void
FreeBoard (board)
	register struct ExpansionBoard *board;
{
	register struct ExpansionUnit  *unit;

	Remove((struct Node *) &board->eb_Node);

	while ((unit = board->eb_Units.lh_First) != NULL)
		FreeUnit(unit);

	/* since only deallocated on initial error, this can only be Hardware */
	if (board->eb_Stuff)
		FreeMem(board->eb_Stuff,sizeof(struct HardwareStuff));

	FreeMem(board,sizeof(struct ExpansionBoard));
}

/* assumes base is locked! */

struct ExpansionBoard *
CreateBoard (setup)
	register struct MappingSetup *setup;
{
	struct ExpansionBoard *board = NULL;
	struct ExpansionType  *type;
	struct ExpansionUnit  *unit;
	struct Mapping	      *mapping;

	if (!setup)
		goto create_error;
	if ((type = GetExpansionType(setup)) == NULL)
		goto create_error;
	if ((board = MakeExpansionBoard(setup)) == NULL)
		goto create_error;

	/* loop backwards so they go in the list forwards */
	for (i  = setup->FirstUnit + setup->NumUnits - 1;
	     i >= setup->FirstUnit;
	     i--)
	{
		if (AddUnit(type,board,i) == NULL)
			goto create_error;

		/* add MatchToolValue stuff here */
		*********
		/* then set up mappings with AddMapping */
	}

	return board;

create_error:
	/* free all things allocated (except type) */
	if (board)
		FreeBoard(type,board);
	return NULL;
}

**** remove ****

/* This will be the heart of the mapper.device 				   */
/* This takes a name and finds a match	 				   */
/* (Actually, the mapper.device might have a modified version for use in   */
/*  things like "open the first unit of this name that isn't in use".  In  */
/*  that case, it would keep the MapperBase locked while trying to open    */
/*  each unit that matched.)						   */
/* This is more an attempt to give an example of how to find the first     */
/* matching name.							   */

struct Unit *
GetMapping (name,type)
	char  *name;
	UWORD type;
{
	struct Mapping    *map = NULL;
	struct MapperBase *base;

	if ((base = (struct MapperBase *)
	     FindSemaphore(MAPPERBASE_NAME)) == NULL)
		return NULL;

	ObtainSemaphore(&base->em_Access);

	for (curr = (struct ExpansionType *) base->mb_TypeList.lh_Head;
	     curr->et_Node.ln_Succ;
	     curr = (struct ExpansionType *) curr->et_Node.ln_Succ)
	{
		if (curr->et_Type == type)
		{
			map = FindName(&curr->et_MappingList,name);
			break;
		}
	}
	FreeSemaphore(&base->em_Access);

	if (map)
		return map->map_Unit;
	return NULL;
}

/* this is the routine the user uses to open a serial device with mappings */

ULONG
OpenSerial (IOM)
	struct IOExtMap IOM;
{
	if (!IOM || !IOM->iom_Req)
		return IOM_NO_REQUEST;

	IOM->iom_Type = MAP_SERIAL;

	if (OpenDevice("mapper.device", 0L, IOM, MAP_SERIAL))
	{
		/* mapper doesn't exist, for compatiblity try 1.2 open */

		if (OpenDevice("serial.device", 0L, IOM->iom_Req, 0L))
			return IOM_TOTAL_FAILURE;

		return IOM_SUCCESS;
	}
	/* mapper exists, did it manage to open a serial port?   */
	/* note that we can't just use the return of OpenDevice. */
	/* It would be nice if we could, but the error return for */
	/* an unknown device isn't documented.			 */
	/* I could have used the standard io_Error, but I wasn't */
	/* certain that was kosher.  Either way works.		 */

	return IOB->iom_Error;
}

     //	Randell Jesup			      Lunge Software Development
    //	Dedicated Amiga Programmer            13 Frear Ave, Troy, NY 12180
 \\//	beowulf!lunge!jesup@steinmetz.UUCP    (518) 272-2942
  \/    (uunet!steinmetz!beowulf!lunge!jesup) BIX: rjesup
(-: The Few, The Proud, The Architects of the RPM40 40MIPS CMOS Micro :-)

acs@amdahl.uts.amdahl.com (Tony Sumrall) (05/08/88)

In article <856@imagine.PAWL.RPI.EDU> jesup@pawl12.pawl.rpi.edu (Randell E. Jesup) writes:
>	Well, it was a big item of discussion at the Devcon, I was on
>the panel (suprise), as was Bryce (suprise).  It seems there will be two
>levels:  a low-level mapper in the serial.device that bryce is working on,
>that maps unit numbers to other devices, and a name mapping utility, that
>allows you to tell it you want a modem (or xyzvax via direct connect,...).

Well, this is close but not exactly on target.  The discussion to which I
was referring specified that an IPC handler should be created for the
serial port.  Upon reflection now, though, it seems that the serial
device would serve just as well as an IPC server if it provides for what
I called a "listener".  This differs from simply opening the serial
device in shared mode in that everyone that has a read up receives *all*
of the data that comes in across the serial port (instead of a chunk
going to this user and a chunk going to that user).  Does this sound
right to you Doug (Merritt--the other primary "contributor" to the
discussion)?

BTW, Randell, I got your e-mail...thanks and congrats on the new job!

>     //	Randell Jesup			      Lunge Software Development

-- 
Tony Sumrall acs@amdahl.uts.amdahl.com <=> amdahl!acs

[ Opinions expressed herein are the author's and should not be construed
  to reflect the views of Amdahl Corp. ]

jesup@pawl23.pawl.rpi.edu (Randell E. Jesup) (05/08/88)

In article <31046@amdahl.uts.amdahl.com> acs@amdahl.uts.amdahl.com (Tony Sumrall) writes:
>This differs from simply opening the serial
>device in shared mode in that everyone that has a read up receives *all*
>of the data that comes in across the serial port (instead of a chunk
>going to this user and a chunk going to that user).

	Either a) create a second-level handler that is the one that talks
to the serial.device, then clones off copies for everyone, or b) create
a fake serial.device (named something else), that took stuff like a serial
device, and passed them all to/from the serial device, except that it cloned
the shared reads.  You could register this as a serial device with the name-
mapper, so programs that know nothing of this whole concept would then
work with it, whereas option a requires programs to be written to take
advantage of this.

     //	Randell Jesup			      Lunge Software Development
    //	Dedicated Amiga Programmer            13 Frear Ave, Troy, NY 12180
 \\//	beowulf!lunge!jesup@steinmetz.UUCP    (518) 272-2942
  \/    (uunet!steinmetz!beowulf!lunge!jesup) BIX: rjesup
(-: The Few, The Proud, The Architects of the RPM40 40MIPS CMOS Micro :-)

doug-merritt@cup.portal.com (05/10/88)

Tony talks about multiple readers of the serial port. Perhaps I'm
missing a point here. I don't understand how this is a desirable
thing in the first place. It strikes me that you need intelligence
to make multiple readers work correctly, so why not have only
a single open on the device itself, and have multiple readers talk
to the server that has the device open, so that the server can
incorporate the multiple-reader intelligence? Are there any circumstances
where you'd want to "blindly" have multiple readers on a single serial
port?
   I suppose that DNET provides a good example for a nice application
of multiple readers, but it has considerable intelligence in supporting
multiple logical streams. Does that illustrate what I'm talking about
well enough?
   Doug
-----
      Doug Merritt        ucbvax!sun.com!cup.portal.com!doug-merritt
                      or  ucbvax!eris!doug (doug@eris.berkeley.edu)
                      or  ucbvax!unisoft!certes!doug

peter@sugar.UUCP (Peter da Silva) (05/10/88)

Perhaps someone could explain how this "name mapper" is to work?

And why?

Because, after all, the devices already have names. The serial port
is "serial.device, unit 0". Fred's superserial card will have "fred.device,
units 0-3". 

Is it a generic name server, that could be used for other types of devices,
and if so why not use one of the existing name spaces: the file system, for
example. Or Manx's setenv/getenv routines. You can do a fancy frontend for
them just as well as for any other name spaces.
-- 
-- Peter da Silva      `-_-'      ...!hoptoad!academ!uhnix1!sugar!peter
-- "Have you hugged your U wolf today?" ...!bellcore!tness1!sugar!peter
-- Disclaimer: These aren't mere opinions, these are *values*.

doug-merritt@cup.portal.com (05/17/88)

Tony, I understand and agree with your examples about multiple
readers being a good thing (e.g. funneling ascii capture off to
another process).

What I was referring to was "dumb" uncoordinated multiple readers.
In other words, if you tried to start up several tasks which all
tried to read the serial port at the same time, without knowing that
the others exist. This seems obviously undesirable to me both in
theory and from experience.

I was just trying to underscore the importance of having one smart
reader read directly from the serial port, and to funnel what it
sees off to other tasks *as appropriate*.

This is probably an obvious point, sorry to overexplain.
   Doug
---
      Doug Merritt        ucbvax!sun.com!cup.portal.com!doug-merritt
                      or  ucbvax!eris!doug (doug@eris.berkeley.edu)
                      or  ucbvax!unisoft!certes!doug

acs@amdahl.UUCP (05/19/88)

In article <5500@cup.portal.com> doug-merritt@cup.portal.com writes:
>Tony, I understand and agree with your examples about multiple
>readers being a good thing (e.g. funneling ascii capture off to
>another process).

Good deal, Doug.  So, does unanimity of a party of 2 mean that this is a
"good thing"?  Should we (have we) asked C-A to implement this?
-- 
Tony Sumrall acs@amdahl.uts.amdahl.com <=> amdahl!acs

[ Opinions expressed herein are the author's and should not be construed
  to reflect the views of Amdahl Corp. ]