Copyright (c) Hyperion Entertainment and contributors.

Difference between revisions of "Supporting Disk Change Events"

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
 
Line 83: Line 83:
 
IExec->Forbid();
 
IExec->Forbid();
   
for(req = (struct IOStdReq *)req->lh_Head ;
+
for(req = (struct IOStdReq *)IExec->GetHead(change_request_list) ;
req->io_Message.mn_Node.ln_Succ != NULL ;
+
req != NULL ;
req = (struct IOStdReq *)req->io_Message.mn_Node.ln_Succ)
+
req = (struct IOStdReq *)IExec->GetSucc(&req->io_Message.mn_Node))
 
{
 
{
 
IExec->Cause((struct Interrupt *)req->io_Data);
 
IExec->Cause((struct Interrupt *)req->io_Data);
Line 109: Line 109:
 
IExec->Forbid();
 
IExec->Forbid();
   
for(req = (struct IOStdReq *)req->lh_Head ;
+
for(req = (struct IOStdReq *)IExec->GetHead(change_request_list) ;
req->io_Message.mn_Node.ln_Succ != NULL ;
+
req != NULL ;
req = (struct IOStdReq *)req->io_Message.mn_Node.ln_Succ)
+
req = (struct IOStdReq *)IExec->GetSucc(&req->io_Message.mn_Node))
 
{
 
{
 
if(req == change_request)
 
if(req == change_request)

Latest revision as of 05:04, 11 November 2015

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 *)IExec->GetHead(change_request_list) ;
    req != NULL ;
    req = (struct IOStdReq *)IExec->GetSucc(&req->io_Message.mn_Node))
{
    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 *)IExec->GetHead(change_request_list) ;
    req != NULL ;
    req = (struct IOStdReq *)IExec->GetSucc(&req->io_Message.mn_Node))
{
    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.