;*********************************************************************
;*                       Origin and Purpose                          *
;*                                                                   *
;* This device represents things I would liked to have known when    *
;* starting out writing devices.  I found it hard to get firm data   *
;* on device design, and collected everything I could find into this *
;* file.  Sure it has lots of comments, but it makes a great basic   *
;* skeleton or classroom presentation.                               *
;*                                                                   *
;* I apologize in advance for any bugs, oversights or errors.  As    *
;* always, corrections and design tips are welcome.                  *
;*                                                                   *
;* Send comments, flames, etc. to the following:                     *
;*                                                                   *
;*                             Tau Productions                       *
;*                             Jeff Rush                             *
;*                             BIX: jrush                            *
;*                             BBS: (214) 231-1372 Rising Star       *
;*                             Fidonet: 1:124/4206 (Dallas, Tx)      *
;*                                                                   *
;* All changes I made are hereby in the Public Domain.  Commodore    *
;* retains its copyright, if any, of the original code in the Rom    *
;* Kernel manuals since this is a derivative work.                   *
;*                                                                   *
;*********************************************************************
;*                           History                                 *
;*                                                                   *
;* 20Sep89 jrr Original crib from Amiga Rom Kernel Manual.           *
;* 23Jul90 jrr Extensive descriptive prose added.                    *
;* 10Sep90 jrr Uploaded to BIX and made available on my BBS.         *
;*                                                                   *
;*********************************************************************
;*          Whats Different from the Rom Kernel Example              *
;*                                                                   *
;* The following Exec device represents a very simple skeleton for   *
;* use as a starting point for authors.  It was derived from the     *
;* sample device in the Amiga ROM Kernel: Includes & Autodocs, but   *
;* has had documentation and folklore from various places folded     *
;* into the comments in order to explain some of the 'magic'.        *
;*                                                                   *
;* For simplicity, the following components of the original example  *
;* have been stripped out:                                           *
;*   o Automount at Autoconfig time when a matching board is found   *
;*   o Hardware interrupt fielding support                           *
;*   o AmigaDOS volume/device support                                *
;*                                                                   *
;* I also stripped out all code that make this device implement a    *
;* ram disk.  I found the assumption that the device was a disk      *
;* tended to bias the structure away from a character device a bit.  *
;* I hope I made it more generic, albeit less practical.  This code  *
;* implements a null device that does nothing except go through the  *
;* motions.                                                          *
;*                                                                   *
;* I added code to implement a signal semaphore to arbitrate access  *
;* to the device's internal global data.  I would like to add code   *
;* to make the unit task a full process so that it can perform DOS   *
;* operations if needed in a real design, but have not yet done so.  *
;*                                                                   *
;*********************************************************************
;*                   Exec-Style Device Skeleton                      *
;*                                                                   *
;* What is a Device?                                                 *
;*                                                                   *
;* An Exec-style device is a logical abstraction of standard and     *
;* custom functions used to manage a piece of either real or         *
;* simulated hardware.  The term 'device' in this file refers to the *
;* abstraction as represented by this software, not the hardware     *
;* itself.  A device is an Exec shared library that by convention    *
;* a few extra function vectors that caller's can utilize for        *
;* control.  Except for this and the fact that the code resides in a *
;* different directory, it -is- a shared library.  By convention a   *
;* device can spin off background tasks to handle work, but shared   *
;* libraries are capable of this too, they just tend not to.  So a   *
;* 'static' library or passive piece of code is what we call a       *
;* library, and a 'dynamic' library or active piece of code with a   *
;* life of its own we call a device.                                 *
;*                                                                   *
;* For those with IBM-PC backgrounds, an Exec-style device can       *
;* functionally be thought of as a BIOS rom.  Although BIOS roms do  *
;* not act as independent tasks, the low-level nature of the BIOS    *
;* interface is similar to a device.  You wouldn't think of using    *
;* the PCDOS Copy command to send a file to the screen using INT 10H *
;* (Video), and AmigaDOS commands cannot necessarily be used to talk *
;* to Exec devices directly.                                         *
;*                                                                   *
;* A device can be activated in one of three ways.                   *
;*                                                                   *
;* a) It can auto-mounted by the BindDrivers AmigaDOS command in     *
;*    response to the presence of a piece of vendor hardware.  It is *
;*    matched up with the hardware by checking the autoconfigure     *
;*    manufacturer ID built into the hardware with the product code  *
;*    within the tooltype of a icon in the expansion directory.  The *
;*    icon is for the file containing the device code.               *
;*                                                                   *
;* b) It can be manually mounted by a user in response to the Mount  *
;*    AmigaDOS command.  This requires that an entry be defined in   *
;*    a 'mountlist' file defining the configuration of the device.   *
;*                                                                   *
;* c) It can be activated by an application calling the Exec routine *
;*    OpenDevice() for use within the application.                   *
;*                                                                   *
;* Method a) and b) above are typically used when AmigaDOS needs to  *
;* be made aware of the device, either as a logical name (PRT:) or   *
;* as a raw media on which a filesystem resides (DF0:).  Method c)   *
;* is used when AmigaDOS does not need to be aware of the device     *
;* and/or the device represents internal or low-level functionality, *
;* like a LAN adapter interface.                                     *
;*                                                                   *
;*********************************************************************
;*                     ! Obscure Bug Alert !                         *
;* You should be aware that there is a bug in the Exec v1.3 function *
;* MakeLibrary().  If the device name within the code is different   *
;* from the name used in the OpenDevice() call, either in case or    *
;* spelling, the device will be loaded into memory, the code for its *
;* initialization will be executed, and then Exec will deallocate    *
;* the device node *** while leaving the node linked into the system *
;* lists ***!!  This results in very strange system behavior and a   *
;* crash during an Expunge.  This is because AmigaDOS is             *
;* case-INsensitive, but Exec is case-sensitive.  Also, the name in  *
;* the OpenDevice() is used to locate the file to load, but the name *
;* inside the code is used to reference the device in memory.        *
;*                                                                   *
;* The moral: Insure device names match *exactly* in all cases.      *
;*                                                                   *
;*********************************************************************
;*     The Role and Importance of Single-Threading (Forbid'ing)      *
;*                                                                   *
;* At Load Initialization Time:                                      *
;*                                                                   *
;* During the setup of an Exec-style device, there is a window of    *
;* vulnerability due to the multitasking nature of the Amiga.  It is *
;* possible for two separate applications to attempt to open a       *
;* device concurrently.                                              *
;*                                                                   *
;* Application A issues an open request, whereupon AmigaDOS loads    *
;* the code into memory and executes the LoadInitRoutine function    *
;* within the device.  This function is single-threaded by Exec who  *
;* does a Forbid() before executing the device's LoadInitRoutine.    *
;* This is because the device node is not yet on the system list of  *
;* known devices and if a request from Application B came along,     *
;* there would now be two copies of the device in memory and trying  *
;* to initialize.                                                    *
;*                                                                   *
;* This means that the LoadInitRoutine should -not- break the        *
;* Forbid() by doing a Wait(), either directly or indirectly by      *
;* calling AmigaDOS or other functions that could do a Wait().  This *
;* means no OpenLibrary(), OpenDevice() or file operations.          *
;*                                                                   *
;* At Device Open Time:                                              *
;*                                                                   *
;* When the DevOpen entry point of the device is called by Exec,     *
;* either right after the code is loaded or on subsequent opens, the *
;* device node is on the system list of known devices.  This means   *
;* that there is no risk of Exec trying to load duplicate copies of  *
;* the device.  However, there is a risk of two tasks trying to      *
;* perform the open setup within DevOpen in the device at the same   *
;* time.  Depending upon the nature of the open setup code in your   *
;* device, this may be fine or dangerous.  For this reason Exec      *
;* single-threads device opens just as it does device initialization *
;* (as detailed above).  If your code does not need this protection, *
;* it may break the Forbid() to perform some special setup you need. *
;*                                                                   *
;* If by chance your device needs to break the Forbid() but still do *
;* its open setup in an exclusive fashion, there is a solution       *
;* (proposed by Bill Hawes on BIX in Amiga.Sw/Exec).  You define a   *
;* signal semaphore in your device global data and obtain a lock at  *
;* the start of your open setup.  This allows other tasks to run and *
;* yet single-threads your open setup code.                          *
;*                                                                   *
;* Also don't forget that if there is some operation that must be    *
;* performed on the first open of a device only and that cannot be   *
;* done at load initialization time, you can use the open count kept *
;* within the device node to detect the first open/last close.  You  *
;* should do this -after- locking your device semaphore to insure    *
;* you remain the first opener/last closer during the operation.     *
;*                                                                   *
;*********************************************************************
;*        The Ramifications of Being a Task Versus a Process         *
;*                                                                   *
;* An Exec-style device must never assume that its caller is a full  *
;* process and not a task.  Remember that tasks are scheduling       *
;* entities and cannot perform any DOS operations such as file opens *
;* or open libraries or devices which may indirectly do a file open. *
;* When a device is invoked via BeginIO(), AbortIO() or              *
;* CloseDevice(), the caller may quite likely be a task which means  *
;* you are restricted to things a task can do.  However, when the    *
;* device is servicing an OpenDevice(), it obviously is running in a *
;* process or the DOS call by Exec to open the device file and load  *
;* it would have failed.  This means that during opens, you may      *
;* perform DOS functions, although this assumption may yet be wrong. *
;*                                                                   *
;* In addition, the unit task created by a device to handle I/O      *
;* requests is typically a task.  This device contains additional    *
;* code to promote the unit task into a full process for maximum     *
;* flexibility.  This adds only a little code at open time and a     *
;* little larger task control block.  A process does -not- consume   *
;* more CPU time during execution than a task.  The Exec scheduler   *
;* treats them equally.                                              *
;*                                                                   *
;*********************************************************************

