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. ]