[comp.sys.amiga.tech] help with disk/tape device driver

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!"