3.13 Custom HAMs

In most respects, what the NWPA specification outlines for a standard HAM is identical to that for a custom HAM. However, because the HAM is acting as both the CDM and the HAM, a number of modifications are necessary. These changes are outlined below in the following topics:

3.13.1 Type Zero HACB Functions

When the custom HAM receives type zero HACBs, it behaves just like a normal SCSI or IDE HAM; the HAM receives requests to do scans, return device info, etc. Also, for type zero HACBs, the HAM sets the hacbCompletion field to the standard HACB completions defined in NPA.H.

All type zero functions that map to functions for the developer's specific bus should be implemented. The ones that do not map should return the standard NPA_UNSUPPORTED_HACB_TYPE_0_FUNCTION as the hacbCompletion.

The structure DeviceInfoStruct has a slightly new appearance that allows the HAM to pass back information about each device. This information is necessary, because in many cases, there is no CDM to pass back device information to the Media Manager.

The inquiryInfo field in the DeviceInfoStruct is replaced by an UpdateInfoDef. This modification allows the driver to pass back information which is normally provided by a CDM. In this way the HAM provides the name, functionMask, controlMask, etc., for each device it supports.

    typedef struct DeviceInfoStruct 
    { 
       . 
       . 
       . 
       } INFO; 
       struct UpdateInfoStruct updateInfo; 
    } deviceInfoDef;
    

Thus, the HAM can pass back the information needed by the Media Manager to control each device without needing an intervening CDM. If the controlMask is set to zero, NWPA defaults to a controlMask of 0x18 for the device (Activate/Deactivate and Mount/Dismount). If the functionMask is set to zero, NWPA defaults to 0x3 for the device (Random Read / Random Write).

All unused fields in the UpdateInfoStruct should be filled with 0xff.

The following code fragment shows how this structure could be filled for a hypothetical device. In this example, the fields are filled statically. Obviously, for a real driver, the fields would be filled according to the characteristics of the scanned devices.

    void SetDeviceInfoStruct(struct deviceInfoStruct *device) 
    { 
       char *deviceName = “\x13" “HYPOTHETICAL_DEVICE”; 
     
       device->deviceHandle                 = 0xff000001; 
          /* Unique HAM-generated handle for device*/ 
       device->deviceType                   = DIRECT_ACCESS_DEVICE; 
          /* This will be a random access device*/ 
       device->unitNumber                   = 1; 
          /* Unit Index for this device*/ 
       device->busId                        = 0; 
          /* HAM-generated ID for the I/O bus*/ 
       device->cardNo                       = 0; 
          /* HAM-generated card index*/ 
       device->attributeFlags               = PRIVATE_PUBLIC_BIT; 
          /* Make this a public device*/ 
       device->haType                       = 3; 
          /* This device accepts type 3 HACBs*/ 
          /* Fill in the fields unique to the custom HAM device*/ 
       CSetB(0xff, &(device->updateInfo), sizeof(struct updateInfoStruct)); 
          /* Fill updateInfo with 0xff.*/ 
       CMovB(deviceName,device->updateInfo.name, deviceName[0]+2); 
          /* Copy the name of the device in*/ 
       device->updateInfo.cartridgeType     = 0; 
          /* Fixed Media*/ 
       device->updateInfo.mediaType         = 0; 
          /* Disk*/ 
       device->updateInfo.removableFlag     = 0; 
          /* Media is non-removable*/ 
       device->updateInfo.readOnlyFlag      = 0; 
          /* Media is not read-only*/ 
       device->updateInfo.activateFlag      = 1; 
          /* The device is active*/ 
       device->updateInfo.controlMask       = 0x18; 
          /* Support mount and activate*/ 
       device->updateInfo.functionMask      = 0x3; 
          /* Support random read and write*/ 
       device->updateInfo.capacity          = 0x8000; 
          /* Device has 0x8000 sectors*/ 
       device->updateInfo.unitSize          = 0x200; 
          /* Use standard sector size*/ 
       device->updateInfo.blockSize         = 0x1ff; 
          /* Define the standard request size*/ 
       device->updateInfo.preferredUnitSize = 0x200; 
    }
    

In this example, the information could be passed back for any of the type zero HACB replies that require a DeviceInfoStruct.

3.13.2 Type 3 HACB Functions

The custom HAM receives type three as well as type zero HACBs. The structure HACBStruct contains an encapsulated CDM message that the HAM can interpret according to the specific I/O bus. For I/O functions, the custom HAM has access to both the vDataBufferPtr and the pDataBufferPtr, allowing either PIO or DMA to be used. The CDM message that the HAM receives is contained in the custom member of the Command Block Overlay area in the HACB. The function, parameter0, parameter1, and parameter2 fields are identical to the fields in the CDMMessageStruct.

Only for type three HACBs: When the custom HAM completes a type three HACB, the hacbCompletion parameter should contain the same completion codes returned by the npaCompletionCode field in the CDI_Complete_Message. Thus, any type three HACB that is completed or aborted must have its completion mapped to one of these values. Two very important completion codes are the 0x3 and 0xa, which correspond to dirty and clean aborts respectively. These completion codes tell the Media Manager whether or not an I/O function was sent to the adapter before the abort was processed. A type three HACB that completes normally should return 0 for the hacbCompletion (ERROR_NO_ERROR_FOUND).

NOTE:Type zero HACB messages must be completed with normal completion codes, as described in HACB Completion Codes.

A HAM_Execute_HACB function for a custom HAM might look like the following code fragment:

    /* HAM_Execute_HACB() – I/O entry point for receiving HACBs and channeling them to the appropriate "devices". */ 
     
    LONG   HAM_Execute_HACB( 
           LONG HAMBusHandle, 
           struct HACBStruct *HACB) 
    { 
       switch(HACB->hacbType) 
       { 
    /* Our ham will only support type 0 and 3 HACBs */ 
     
          case 0: /* Perform a type 0 HACB */ 
                 switch(HACB->command.host.function) 
                 { 
                    case 0: if ( 
                            (HACB->command.host.parameter0 == 0) && 
                            (HACB->command.host.parameter1 == 0) && 
                 (HACB->command.host.parameter2 == 0) ) 
    { 
          /* The caller is requesting HAM info to be returned */ 
    HAM_Return_Bus_Info(HAMBusHandle, HACB); 
    return(0); 
    } 
    else 
    { 
          /* We don’t know what request this is. */ 
                 HACB->hacbCompletion = SET_HACB_COMPLETION( 
                    NPA_MALFORMED_HACB, 
                 NPA_UNSUPPORTED_HACB_TYPE_0_FUNCTION); 
                    HAI_Complete_HACB(HACB->hacbPutHandle); 
                    return(0); 
                 } 
                 break; 
          case 1: HAM_Scan_For_Devices(HAMBusHandle, HACB); 
    break; 
          case 2: HAM_Return_Device_Info(HAMBusHandle, HACB); 
                 break; 
       /* Queuing is arbitrated by the HAM alone. Thus, we do  
          not intercept queuing functions */ 
       /* The following also implemented if needed 
          case 7:   HAM_Recovery_Reset(HAMBusHandle, HACB); 
                    break; 
          case 8:   HAM_Deactivation_Notification(HAMBusHandle, HACB); 
                    break; 
       */ 
     
          default: /*   In the default case, inform the caller that the function 
                       is not supported */ 
             HACB->hacbCompletion = 
                SET_HACB_COMPLETION 
                (NPA_MALFORMED_HACB, 
                NPA_UNSUPPORTED_HACB_TYPE_0_FUNCTION); 
             HAI_Complete_HACB(HACB->hacbPutHandle); 
             return(0); 
             break; 
         } 
         break; 
          Case 3:/* Execute a raw message which has been passed to the HAM */ 
             HAM_Raw_Function(HAMBusHandle, HACB); 
                break; 
          default: /* Inform the caller that the HACB type was invalid */ 
                 HACB->hacbCompletion = 
             SET_HACB_COMPLETION(NPA_MALFORMED_HACB, 
                    NPA_UNSUPPORTED_INTERFACE_TYPE); 
                 HAI_Complete_HACB(HACB->hacbPutHandle); 
                 return(0);
       } 
          return(0); 
       }
    

The HAM_Raw_Function could handle the hacbType = 3 as follows:

    void HAM_Raw_Function( 
       LONG HAMBusHandle, 
       struct HACBStruct *HACB) 
    { 
       void   *bufferPtr; 
       LONG    function, parm0, parm1, parm2, bufferSize; 
       struct  RequestInfoDef *request; 
     
       function = HACB->command.custom.function; 
       parm0 = HACB->command.custom.parameter0; 
       parm1 = HACB->command.custom.parameter1; 
       parm2 = HACB->command.custom.parameter2; 
       bufferPtr = HACB->vDataBufferPtr; 
       bufferSize = HACB->dataBufferLen; 
     
       switch (function) 
       { 
          case MOUNT_DISMOUNT: 
             If ( DoMountDismount(HACB->deviceHandle, parm0) != ERROR) 
                HACB->hacbCompletion = ERROR_NO_ERROR_FOUND; 
             else 
                HACB->hacbCompletion = ERROR_DEVICE_ERROR; 
             break; 
          case RANDOM_READ: 
          . 
          . 
          . 
          default: HACB->hacbCompletion = ERROR_FUNCTION_NOT_SUPPORTED; 
             break; 
       } 
       HAI_Complete_HACB(HACB->hacbPutHandle); 
    }
    

Note that this function handles both type zero and type three HACBs. The type zero HACBs are completed using standard HACB completion codes. The type three HACBs are completed using the standard CDM message completion codes.