Copyright (c) Hyperion Entertainment and contributors.

Supporting Disk Change Events

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Notes for file systems and other clients

Device drivers either do not implement the TD_ADDCHANGEINT/TD_REMCHANGEINT commands at all, or they implement them in a manner which is inconsistent with how device I/O commands are normally used. If you use these commands in your own software, then you should follow a specific protocol so as to minimize the risk of causing your software to hang or to crash the device driver.

  • You must use the same 'struct IOStdReq' both for installing the change interrupt handler with TD_ADDCHANGEINT and for removing it later using TD_REMCHANGEINT. The IOStdReq->io_Data and IOStdReq->io_Length fields should not change between the use of the TD_ADDCHANGEINT/TD_REMCHANGEINT commands.
  • You must not free the IOStdReq you used with the TD_ADDCHANGEINT command until after you have removed the change interrupt handler with the TD_REMCHANGEINT command.
  • After you have sent the TD_ADDCHANGEINT command you must not use the IOStdReq for any other purpose except for eventually performing the TD_REMCHANGEINT command, which removes the change interrupt handler.
  • Never use DoIO() with the TD_ADDCHANGEINT command. Either use SendIO(), or if you must use BeginIO() then you must make sure that the IOB_QUICK flag in the IOStdReq->io_Flags field is not set, like so:
change_request->io_Flags &= ~IOF_QUICK;
  • When you remove the change interrupt handler with the TD_REMCHANGEINT command, you must make sure that the TD_ADDCHANGEINT command was successful, and if not, then you will have to remove the IOStdReq, like so:
/* Was the TD_ADDCHANGEINT command successful? */
if(IExec->CheckIO((struct IORequest *)change_request) == NULL)
{
   change_request->io_Command = TD_REMCHANGEINT;
   IExec->DoIO((struct IORequest *)change_request);
}
/* The TD_ADDCHANGEINT command was not successful and has been rejected. */
else
{
   IExec->WaitIO((struct IORequest *)change_request);
}
  • Never use SendIO() with the TD_REMCHANGEINT command. Always use DoIO(), and if you must use BeginIO() then you must make sure that the IOB_QUICK flag in the IOStdReq->io_Flags field is set, like so:
change_request->io_Flags |= IOF_QUICK;
  • Never use the TD_REMCHANGEINT command unless you have used the TD_ADDCHANGEINT command first.

Notes for device driver implementors

Client software which uses the TD_ADDCHANGEINT/TD_REMCHANGEINT makes assumptions about how these commands act. The basic assumption is that none of these commands can fail, which is why your device driver should implement these commands in a specific fashion.

  • The TD_ADDCHANGEINT command may be received with the IOB_QUICK flag set in the IOStdReq->io_Flags field. You must initialize the IOStdReq fields as follows and then store the IOStdReq in a list for later use:
/* All IOStdReqs which were installed with the TD_ADDCHANGEINT
   command are expected to be stored in a list. */
struct List * change_request_list;
 
change_request->io_Message.mn_Node.ln_Type = NT_MESSAGE;
change_request->io_Flags &= ~IOF_QUICK;
change_request->io_Error = 0;
 
/* Before you start accessing the IOStdReq list you will want to
   protect yourself against list modification. In this example
   we use Forbid() and Permit(); you might want to use a different
   method. Make sure to protect adding/removing IOStdReqs in the
   same manner. */
IExec->Forbid();
 
IExec->AddTail(change_request_list,&change_request->io_Message.mn_Node);
 
IExec->Permit();
  • You may want to verify that the contents of the IOStdReq->io_Data field are sound, i.e. they refer to a valid address space and the 'struct Interrupt' pointed to contains a valid Interrupt->is_Code field.
  • When a medium change is detected, you should invoke each interrupt change handlers which were installed through the TD_ADDCHANGEINT command in the following fashion:
/* All IOStdReqs which were installed with the TD_ADDCHANGEINT
   command are expected to be stored in a list. */
struct List * change_request_list;
struct IOStdReq * req;
 
/* Before you start accessing the IOStdReq list you will want to
   protect yourself against list modification. In this example
   we use Forbid() and Permit(); you might want to use a different
   method. Make sure to protect adding/removing IOStdReqs in the
   same manner. */
IExec->Forbid();
 
for(req = (struct IOStdReq *)req->lh_Head ;
    req->io_Message.mn_Node.ln_Succ != NULL ;
    req = (struct IOStdReq *)req->io_Message.mn_Node.ln_Succ)
{
    IExec->Cause((struct Interrupt *)req->io_Data);
}
 
IExec->Permit();
  • The TD_REMCHANGEINT command does not conform to the rules of how device commands are normally handled. The TD_REMCHANGEINT command must always be processed as if the IOB_QUICK flag were set in the IOStdReq->io_Flags field, regardless of whether this is actually the case. You must never store the IOStdReq in a list for later processing, you must always process it immediately, like so:
/* All IOStdReqs which were installed with the TD_ADDCHANGEINT
   command are expected to be stored in a list. */
struct List * change_request_list;
struct IOStdReq * req;
 
/* This is the IOStdReq which refers to the TD_REMCHANGEINT command
   which we need to perform now. */
struct IOStdReq * change_request;
 
/* Make sure that the TD_REMCHANGEINT command refers to an
   IOStdReq which has been used. */
IExec->Forbid();
 
for(req = (struct IOStdReq *)req->lh_Head ;
    req->io_Message.mn_Node.ln_Succ != NULL ;
    req = (struct IOStdReq *)req->io_Message.mn_Node.ln_Succ)
{
    if(req == change_request)
    {
       IExec->Remove(&change_request->io_Message.mn_Node);
 
       if((change_request->io_Flags & IOF_QUICK) == 0)
          IExec->ReplyMsg(&change_request->io_Message);
 
       break;
    }
}
 
IExec->Permit();
  • It is not strictly necessary for all storage device drivers to implement the TD_ADDCHANGEINT/TD_REMCHANGEINT commands. Because existing client software assumes that TD_ADDCHANGEINT cannot fail, it may fail to clean up after a failed TD_ADDCHANGEINT command and hang/crash. For this reason you may want to consider implementing both TD_ADDCHANGEINT and TD_REMCHANGEINT commands even if your device driver does not support media change operations.