enno@olympic.oz (Enno Davids) (06/25/89)
A couple of quick questions for the net about device drivers. I am building a set of device drivers based around a piece of SCSI hardware. To access the hardware I have built a server process which opens a public message port and accepts messages which contain a pointer to a SCSI command block pointer, a data buffer pointer and some housekeeping and puts them out on the SCSI bus for any device drivers which send it such messages. The first problem I have is I would like to kick off the SCSI server process from inside the a device driver if it starts but cannot find the server process' port (and presumably thus the server isn't running). As I understand it I cannot make any requests that result in use of AmigaDOS either directly or indirectly. So precisely, what can I do and what can't I, and is there a good way to start my server process (presumably from the Init code of device driver). The second question I have is a little more complicated. In order to PutMsg the message to the server process I need to supply a reply port so that the server process can ReplyMsg the message sucessfully on completion of the SCSI command. The reply port has in it a task and signal to that task to use to complete the WaitPort that the device driver will do after the PutMsg. The first version of the code I wrote allocated a reply port in the device init code. This inherited the task and task signal of the client process that caused the OpenDevice to load the driver. This code broke when the driver first caused an I/O request to queue internally and hence be executed in the device drivers own task rather than that of the client process indicated in the replyport's task field. The system hangs at that point as the server process ReplyMsg's the message it is sent, but the wrong task is signalled and presumably woken while the device driver continues to WaitPort forever. I could only see two possible solutions to this problem. I could somehow open a separate reply port for each client that opens the device. It is worth noting that this then becomes a problem as multiple clients can quite happily open the same device and/or unit and tracking ports for each client can become quite complex. The second (simpler) solution that occurred was to write my own version of the standard CreatePort routine to ask for a specific signal rather than any available signal. This, together with judicious use of FindTask(0) should allow the server process to at least reply sucessfully. (i.e. All clients of any particular device driver use the same signal, recorded in the reply port structure, and can set their task at the time they send their message to the server process) A question here becomes what should I do if I cannot allocate the requested signal when a client opens a device/unit? I have a version employing the second solution running although I have not as yet 'torture test'ed it or otherwise satisfied myself of its safety. My real question here is whether or not there is a better answer to the problem or even a 'correct' solution. In anticipation, Enno. ------- Enno Davids Olympic Video Gaming P/L, 1562-68 Centre Rd, Springvale 3171 enno@olympic.oz Phone: (03) 562 4255 Fax: (03) 562 4889
phils@tekigm2.MEN.TEK.COM (Philip E Staub) (06/29/89)
In article <1006@olympic.oz> enno@olympic.oz (Enno Davids) writes:
[questions about device drivers]
G'day Enno:
Our machine apparently doesn't know how to get to oz, and I
don't know the correct routing to do it manually, hence this posting.
This is a brief description of a hard disk device driver which I wrote (and
am continuing to tinker with). I'm sure it isn't quite the same sort of thing
that you're working with, but maybe the structure of the driver will give you
some ideas.
Basically, I have a module called 'harddisk.device' and partition entries for
the hard disk in the mountlist refer to it as their controlling driver. I
have split the driver into two logical pieces, for which I have coined the
terms 'driver' and 'handler', although they both physically reside in the
same file.
(I'm not sure that my use of the terms 'driver' and 'handler' are consistent
with accepted terminology, so I'll describe the contents and function of each.)
The driver consists primarily of a romtag structure, the initialization
code and the required entry points for Open, Close, Expunge, Null, BeginIO
and AbortIO. It also contains routines to handle functions which don't directly
access the disk. These functions are typically things like status
reporting or things which are provided for trackdisk.device compatibility,
but are really just stubs which return values which won't create errors (or
maybe *do* create errors, if that's appropriate).
The handler is spun off as a separate process (via CreateProcess())
and a message port is created through which the driver sends requests
to the handler. Now, the magic here is that when the driver sends a message
to the handler, the message it sends is actually the very same message which
was sent to it via the BeginIO call. This being the case, the reply port is
the user's reply port, and the handler replies directly to it, instead of to
the driver. The bulk of the handler consists of the routines which perform
the actual disk I/O operations.
Now for a description of the general operation.
The first access to one of the hard disk partitions causes 'harddisk.device'
to be loaded and the initialization sequence to take place.
Note: there are several pre-allocated fields in the 'device' structure which
would normally be allocated via AllocMem(), including the MsgPort structure
for the handler's message port (which of course includes the list header
for the message list), the Task structure for the handler process, the
task stack for the handler process, and the Interrupt structure to point to
the interrupt service routine. I use the RTF_AUTOINIT flag in the romtag
structure to cause automatic creation of the device (alias library) and
allocation and initialization of the 'device' structure.
The driver initialization sequence looks basically like this in pseudo code:
Allocate a signal bit to use for the handler's message port
and mark the bit in the MsgPort structure.
Initialize the handler's message list to empty
Add the handler's message port to the system pool
Create the handler process, which causes it to start running,
with its own initialization procedure.
The handler initialization sets up some registers and the controller
hardware, then starts waiting for messages at it's message port.
When the driver receives a request via its BeginIO entry point, it
determines whether it needs to relay the message on to the handler or to
process the request itself.
For Quick functions (any function performed by the driver), the driver
calls an appropriate routine to perform the requested action, then returns.
A ReplyMsg() is not necessary, because the message will be returned with
the IO_QUICK flag set. (I suppose I should really be checking the QUICK flag
upon entry and doing a ReplyMsg() if it was clear, to support users who
might want to call BeginIO() directly with the QUICK flag clear, but I
haven't found that to be a problem. Yet.)
For Queued functions (any function performed by the handler), the driver
clears the QUICK bit in the message flags and calls PutMsg() to send the
message to the handler. When this message is received by the handler
(remember it may be queued behind other requests), the handler performs the
requested disk I/O operation and calls ReplyMsg() to return the request to
the user (not the driver).
There is a significant difference between the driver process and the
handler process. The driver code must be re-entrant. The handler process
must *not* be re-entrant. This is because my disk controller hardware can
only work on one command at once, even though there may be multiple user
processes wishing to perform disk I/O simultaneously (and thus multiple
processes may be executing at various points in the driver code). I rely
upon the message port to ensure the single-threaded nature of operation of
the handler, because it can only be invoked via the message port, and it
never asks for a message until all processing from the last command is
complete.
Well, I hope this information is of some use to you. If there is anything
further you'd like to know about my driver, let me know.
Regards,
Phil
--
------------------------------------------------------------------------------
Phil Staub
Tektronix, Inc., Vancouver, Washington 98668
phils@tekigm2.MEN.TEK.COM
jesup@cbmvax.UUCP (Randell Jesup) (07/01/89)
In article <5107@tekigm2.MEN.TEK.COM> phils@tekigm2.MEN.TEK.COM (Philip E Staub) writes: >This is a brief description of a hard disk device driver which I wrote (and >am continuing to tinker with). I'm sure it isn't quite the same sort of thing >that you're working with, but maybe the structure of the driver will give you >some ideas. [ a good description of a well-designed driver follows ] >The driver initialization sequence looks basically like this in pseudo code: > > Allocate a signal bit to use for the handler's message port > and mark the bit in the MsgPort structure. > Initialize the handler's message list to empty > Add the handler's message port to the system pool > Create the handler process, which causes it to start running, > with its own initialization procedure. Please, don't add private ports to the public port pool. It increases name collisions, and slows down searches for real public ports. -- Randell Jesup, Commodore Engineering {uunet|rutgers|allegra}!cbmvax!jesup
enno@olympic.oz (Enno Davids) (07/13/89)
In article <5107@tekigm2.MEN.TEK.COM>, phils@tekigm2.MEN.TEK.COM (Philip E Staub) writes: > In article <1006@olympic.oz> enno@olympic.oz (Enno Davids) writes: > [questions about device drivers] > > G'day Enno: > > Our machine apparently doesn't know how to get to oz, and I > don't know the correct routing to do it manually, hence this posting. > > This is a brief description of a hard disk device driver which I wrote (and > am continuing to tinker with). I'm sure it isn't quite the same sort of thing > that you're working with, but maybe the structure of the driver will give you > some ideas. > > [more good driver related stuff] > > Well, I hope this information is of some use to you. If there is anything > further you'd like to know about my driver, let me know. > > Regards, > Phil > -- G'Day Phil, Thanks for the (only!) reply. As you noted I am not doing exactly what you outlined, although I was in the past. To explain in more detail I have tried some disgusting ASCII pseudo-diagrams to help out. The problem I have is a fairly simple one. I have a SCSI adapter card and a hard disk driver that uses it. Nothing special here. This driver, much like the one you describe, and CBM documents in the RKM's, has a number of interface routines that various client tasks (most often filesystem tasks of course) use to interact with the driver. The driver either executes the appropriate code directly (e.g. open/close/beginIO to/with unit), or if the bus/unit is in use, may choose to queue the request for later completion by a task that has (as in your driver) been started for the unit. +--------+ | Client | DoIO/SendIO | task 1 | -------\ +--------+ \ \----> +------------------+ +---------+ | Driver interface | | | +--------+ /----> | routines 3 | --------------> | Common | | Client | / +------------------+ | SCSI | | task 2 | -------/ | |interface| +--------+ DoIO/SendIO | Queued I/O requests | code | v | | +--------------------+ | 5 | | Unit queue service | | | | task started by | ------------> | | | driver 4 | | | +--------------------+ +---------+ This is as I said much like the driver you described. The important point to note in this is which task is doing the work. For I/O requests that are immediately executed the 'current task' or context is that of the client task which originated the request. For queued I/O the context is that of the unit task that acts on the clients behalf sometime later. A final point of note is that non 'quick' I/O requests are replyed back to the calling client as recorded in the client's original I/O request (which was passed to the unit task whole) by whatever executes them (that is from box 3 or 4 as appropriate). Now I complicate the matter by acquiring a SCSI tape drive and adding it to the system. To simplify the software I want to keep the tape and disk driver code separate. To do this I must provide some alternate way for the tape driver code to access the SCSI bus independantly of the disk driver. The simplest way to do this is to provide a separate task which opens a public message port with a pre-defined name ('scsi_server' in this case) and accepts messages containing SCSI commands and data. The server puts these onto the bus and returns the SCSI completion status to the device driver. To visualise this pretend that I have added another task (6) to the diagram above, (off the right end of your terminal) which the common SCSI interface code (5) now interacts with (i.e. instead of driving the SCSI bus the interface code now sends a message to the server code which drives the bus for it.) The difficulty now arises when we consider contexts that the device driver code runs in. Specifically, the driver at some point must allocate a private reply port so that it can wait there for the returning message from the SCSI server task. Unfortunately the reply port has in it, a pointer to the task which sent it(!) and a signal mask used to wake the task (if the port is set as PA_SIGNAL). Unfortunately, these fields are set at the time the reply port is created, and can thus be quite wrong. The ideal situation is to have one private reply port per client task that opens the device driver, plus one for each each unit task that is started. Unfortunately, there is no easy way for the device driver to track which of several clients may currently be using it. Other solutions are to fudge the reply port creation so that it always asks for the same signal (if it is available) hence making all clients of the device driver use this signal and using FindTask(0) to fill in the task pointer immediately prior to sending the message to the scsi_server port. A final solution is to create the reply port at each instance of sending a message to the SCSI server and deleting it when the server completes processing. Each of these last two solutions has been implemented and works quite acceptably. I am however left with the nagging feeling that, 'there must be a better way' that I have missed. Hence my question to the net. The question is really quite generic in that what I am asking is how to allow a device driver to communicate with another task using message ports. Finally, I should note that the driver you described won't exhibit the problems I describe provided that the reply port is created by the unit task (as you appear to be automatically queuing all requests that will cause a real I/O to occur) although you will need to be careful in the unit close code to have the port removed. Other questions I originally asked were about what I could and couldn't do in the device driver. I believe for instance that it is not kosher to use dos.library either directly or otherwise. Is this correct? Interestingly the V1.1 RKM's example device driver shows the Init code using dos.library to CreateProc the unit task. Is dos OK here (e.g. to loadseg the scsi_server if it's port cannot be found and start it up). Could I make the scsi_server a device itself and OpenDevice it (with a possible load from disk, etc). I note that the 1.3 RKM example no longer uses dos but Exec to AddTask in do the same thing. Lastly, where is there any further information about the contents of the 1.3 include files scsidisk.h and hardblocks.h. (That is over and above the files themselves). Is this in the 1.3 Libraries & Devices RKM (not in Oz yet). Anyone from CBM care to comment about any of this? Enno. Just for the record, for those who want to try mail, the trick is to get to uunet and then to munnari. From there olympic.oz is one step (effectively). If you want to post a news reply stay in c.s.a.tech as we don't currently receive c.s.a in this country. Good Luck.
kodiak@amiga.UUCP (Robert R. Burns) (07/18/89)
In articles ... Michael van Elst and Enno Davids write about SCSI disk and tape support at the exec device level. A (standard or fast) DOS file system is associated with a SCSI disk thru an exec device (e.g. hddisk.device) that looks like the trackdisk.device. There is no corresponding DOS handler for tape drives at present. A backup program that I know of that uses SCSI tapes (BRU) does so via SCSI commands issued directly thru the tape drive thru the same exec device using the HD_SCSICMD command described in the 1.3 include file <devices/scsidisk.h>. We encourage all applications requiring access to something other than a magnetic disk on SCSI to use this command, and all writers of SCSI exec-level drivers to support it. - Kodiak -- Bob Burns, amiga!kodiak _ | /_ _|. _ | Commodore __ |_) _ |_ _ )' |<(_)(_)|(_\|< /\ | ||| _` /\ |_)(_\| )(_\ | | \ Software ___/..\|\/|||__|/..\___ Faith
enno@olympic.oz (Enno Davids) (07/26/89)
In article <4172@amiga.UUCP>, kodiak@amiga.UUCP (Robert R. Burns) writes: > In articles ... Michael van Elst and Enno Davids write about SCSI disk and > tape support at the exec device level. A (standard or fast) DOS file > system is associated with a SCSI disk thru an exec device (e.g. hddisk.device) > that looks like the trackdisk.device. There is no corresponding DOS handler > for tape drives at present. A backup program that I know of that uses SCSI As I have been playing with this perhaps you would like some observations. The DC2000 based QIC100 tape drives I have been playing with all support random I/O (i.e they can read/write at arbitrary points on the tape) and could conceivably be used with current filesystems and handlers. Trying this would fairly quickly become tiresome due to the excessive amounts of time winding tape from end to end can take. So, to enhance a current filesystem to work here is a minimal list of what needs to be done. (BTW, I don't claim this list is exhaustive, merely that it hits some of the high points): - An analog of MaxTransfer (MinTransfer?) for mountlist to allow the handler to work optimally with non 512 byte blocked devices. Handlers might still be required to pre-read a block to perform a shorter write operation(?). - Caching of directories in the handler and possibly use of a fixed size (pre-allocated?) directory kept at the start of tape. This more like the inode area of the old **IX disk organisation than current Amiga filesystems. - Some way of telling the handler about the cost of seeking from one (tape) block to the next so that it can attempt to optimise this. > tapes (BRU) does so via SCSI commands issued directly thru the tape drive > thru the same exec device using the HD_SCSICMD command described in the > 1.3 include file <devices/scsidisk.h>. We encourage all applications requiring > access to something other than a magnetic disk on SCSI to use this command, > and all writers of SCSI exec-level drivers to support it. This isn't hard to provide, but I find the choice of HD_SCSICMD (it's value) to be a little kludgy. (What if you already have more commands than this? If you don't you probably fill the command table with a bunch of dummy entries because you have too few.) My solution was to have a completely separate task doing the SCSI bus management. Any device driver could still offer HD_SCSICMD by simply accepting such commands and then passing them on to the SCSI task. This also means that the SCSI access is no longer reliant on some particular device being mounted (e.g tape restore to an unmounted hard drive, etc.). Just some thoughts. Finally, a repeat question. Is there any other documentation that pertains to the information in scsidisk.h/hardblocks.h other than the includes themselves? Nice to talk to you, Enno. ------- Enno Davids Voice: +61 3 562 4255 Olympic Video Gaming P/L. Fax: +61 3 562 4889 1562-68 Centre Rd. Springvale, 3171 ACSnet: enno@olympic.oz Australia "... and in the event of your capture the secretary will disavow any knowledge of your actions or agreement with your opinions. This netnews item will self- destruct in five seconds."
jesup@cbmvax.UUCP (Randell Jesup) (07/29/89)
In article <1072@olympic.oz> enno@olympic.oz (Enno Davids) writes: >In article <4172@amiga.UUCP>, kodiak@amiga.UUCP (Robert R. Burns) writes: >> tapes (BRU) does so via SCSI commands issued directly thru the tape drive >> thru the same exec device using the HD_SCSICMD command described in the >> 1.3 include file <devices/scsidisk.h>. We encourage all applications requiring >> access to something other than a magnetic disk on SCSI to use this command, >> and all writers of SCSI exec-level drivers to support it. > >This isn't hard to provide, but I find the choice of HD_SCSICMD (it's value) >to be a little kludgy. (What if you already have more commands than this? If >you don't you probably fill the command table with a bunch of dummy entries >because you have too few.) It was chosen to be sure no one was using that device command slot on a trackdisk-like driver. If you have a number fewer than that, you either make a specific check for HD_SCSICMD before consulting the table, or add a few null entries to your table. Not an awful lot of bytes. >My solution was to have a completely separate task doing the SCSI bus >management. Any device driver could still offer HD_SCSICMD by simply accepting >such commands and then passing them on to the SCSI task. This also means that >the SCSI access is no longer reliant on some particular device being mounted >(e.g tape restore to an unmounted hard drive, etc.). Just some thoughts. This is a fairly common solution to writing a device driver. Most drivers have a task handling the hardware, which is fed requests by the BeginIO vector of the device. Some have a second task, which is fed low-level "raw" requests (such as raw scsi commands) by the higher-level task. This does cost an extra task switch per command (about 400us). >Finally, a repeat question. Is there any other documentation that pertains to >the information in scsidisk.h/hardblocks.h other than the includes themselves? Well, not really, though if you send private mail with your needs I can probably scrounge you up some documents. There are some more verbose explanations of each of the fields in rigid disk blocks. There is no published documentation other than the includes at this time. Perhaps in an upcoming AmigaMail. -- Randell Jesup, Keeper of AmigaDos, Commodore Engineering. {uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.cbm.commodore.com BIX: rjesup Common phrase heard at Amiga Devcon '89: "It's in there!"