3.2 Operational Overview

The information in this section describes a HAM's major functional procedures and their main flow of events. The information provided in this section helps to add functionality to a HAM program shell and covers the following topics:

3.2.1 Load-Time Initialization and Registration

The following figure outlines the HAM registration process:

Figure 3-1 HAM Initialization

Loading of the HAM can be initiated in multiple ways:

  • By the systems operator at the server console
  • By a startup file
  • By INSTALL

Figure 3-1 shows the sequence of events for initializing and registering a HAM at load-time.

When a HAM is loaded, the OS calls the HAM's HAM_Load entry point passing it loadHandle, screenID, and commandLine as input parameters. HAM_Load is responsible to perform the following (see Figure 3-1):

  1. Registers the HAM module.

    The HAM registers its module by calling NPA_Register_HAM_Module. This API sets up the general environment necessary for the HAM to become operational and makes it possible for the HAM to allocate and register any resources it may need.

    Within the context of NPA_Register_HAM_Module, HAM's npaHandle is assigned a value, and that the following HAM entry points get registered with NWPA:

    NOTE:If the HAM supports multiple adapters, it should call NPA_Register_HAM_Module for each instance it supports. This API accepts a HAM-generated instance number as an input parameter. This instance number should correspond to the adapter card instance being supported by the HAM. A separate instance number is necessary to register different hardware options for each adapter.

  2. Verifies bus compatibility.

    The HAM can check the host bus type by calling NPA_Return_Bus_Type. The HAM can then verify that the bus type is compatible with the type it supports.

    If the HAM is NBI-aware and is supporting an adapter designed for a bus architecture that provides configuration information on a per-slot basis, (such as EISA, MCA, PCI, PnPISA), do the following:

    1. Build the Product_ID (type 0x0008) option structure. (For details, see NPAOptionStruct.)

      Option

      Description

      name

      A length-preceded and null-terminated string

      type

      Name of the option (PRODUCT ID type 0x0008)

      parameter0

      BusType

      parameter1

      Pointer to an array of bytes that contains the architecture-specific Product ID information

      parameter2

      The size of the array pointed to by parameter1

      flags

      USE_THIS_OPTION

      string

      Null

    2. Add the option by calling NPA_Add_Option.
    3. Parse the option by calling NPA_Parse_Options.

      Repeat Steps b and c for each product ID whose hardware exists in your system.

      During the context of NPA_Parse_Options, HAM_Check_Option is repeatedly called (for each product ID), passing it the option structure with output parameters as follows:

      Parameter

      Description

      parameter0

      BusTag

      parameter1

      Slot or HIN

      parameter2

      Unique ID

      For each adapter instance found, HAM_Check_Option stores the return information in a configuration table for use later. The Product_ID option doesn't need to be registered. Parsing of this option is done only to obtain the slot value for the next step.

    4. Register the slot received in part c, by doing the following:

      i. Build a Slot Option (type 0x0005) structure.

      ii. Add the Slot Option using NPA_Add_Option

      iii. Parse the option using NPA_Parse_Options.

      NPA_Parse_Options calls the HAM's HAM_Check_Option function. During the context of this HAM function, NWPA passes the slot that was selected by the user in the NPAOptionStruct structure.

      iv. Register the slot option using NPA_Register_Options.

      The slot that was specified during the proceeding Step iii is then registered (if HAM_Check_Option returned 0) during this function.

      If a SLOT= option matching one of the elements in the HAM's configuration table is present on the command line, the HAM calls NPAB_Get_Card_Config_Info passing it the bus tag and unique ID given during the parse of the Product ID option. This routine returns the bus-specific configuration information associated with the target adapter. Use these to create the list of desired options.

      HIN awareness is required for PCI, EISA, PnPISA, MCA, and PCMCIA adapters.

      iv. Register the Slot Option by calling NPA_Register_Options.

  3. Creates a select-list of desired options.

    Options are command line keywords that set operational states, or specify hardware resources such as interrupts, DMA channels, ports, memory decoding, and custom parameters.

    For each of these applicable resources, the HAM creates a select-list by filling out an instance of an NPAOptionStruct with flags set to USE_THIS_OPTION and parameter2 = busTag (if NBI aware) and calling NPA_Add_Option.

    During the context of NPA_Add_Option, the NPA copies the option information and constructs a "select-list" of valid options for the HAM and adapter.

    To completely build the option list, the HAM should iteratively fill out the NPAOptionStruct instance and call NPA_Add_Option for each option type it desires. Since NWPA maintains its own copy of option information in constructing the select-list, the HAM can reuse the same NPAOptionStruct instance for each call to NPA_Add_Option.

    For hardware resource options, if NPA_Add_Option returns a non-zero value, it indicates that the option is already reserved and NWPA does not add the option to the HAM's select-list.

  4. Parses the load command line for specified options.

    The HAM calls NPA_Parse_Options to cause NWPA to match options specified on the command line with those in the HAM's select-list. In turn, NPA_Parse_Options iteratively calls the HAM's HAM_Check_Option entry point for each match it finds. HAM_Check_Option either accepts or rejects the selected option. Each time HAM_Check_Option accepts an option, NWPA places it on a use-list.

    If there is an option on the command line that does not match anything in the HAM's select-list, it is ignored. However, if after parsing the command line NWPA finds residual options in the HAM's select-list, it either prompts the user for the options or discards them depending on the bits set in the flags field of each option's NPAOptionStruct.

    Hardware options are not physically registered during the context of NPA_Parse_Options. Therefore, the HAM should not try to physically access a resource when its HAM_Check_Option entry point is called during this context.

  5. Registers the options in the HAM's use-list.

    The HAM registers the parsed options (options specified in its use-list) by calling NPA_Register_Options. This API accepts the instance number introduced in the note after step 1. NPA_Register_Options uses this number to associate the group of options being registered with a particular instance of an adapter managed by the HAM.

    NPA_Register_Options physically registers the hardware resources in the use-list, making them available to the HAM. Also, similar to the parse phase in step 4, NPA_Register_Options iteratively calls the HAM's HAM_Check_Option entry point for each registered option, this time allowing the HAM to physically verify the resource or set internal flags to set an operational mode.

    Another reason why HAM_Check_Option gets called during option registration is to provide the HAM with return information pertinent to the option. For example, the memory decode option (type 0x0004) that pages in memory-mapped I/O space to the system returns a logical address to the HAM. This type of return information is given to the HAM through HAM_Check_Option when it is called under the context of NPA_Register_Options. The actual information is found in the parameter2 field of the NPAOptionStruct pointed at by the entry point's option input parameter.

    NOTE:Steps 2 - 5 describe the general paradigm for registering hardware and configuration options. For more detailed information and actual registration examples, refer to NPAOptionStruct

  6. Allocates memory resources.

    The HAM allocates any memory buffers it may need by calling NPA_Allocate_Memory.

  7. Schedules the HAM's timeout routine.

    The HAM schedules its timeout routine, HAM_Timeout, by calling NPA_Spawn_Thread. The HAM will use this routine to recover from a hung-device condition. HAM_Timeout monitors the elapsed time of a HACB request as specified in the HACB's timeoutAmount field.

    NPA_Spawn_Thread creates a thread that is executed once. If the thread is to be executed again, as in a timeout, it must be spawned again within the routine by calling NPA_Spawn_Thread.

  8. Registers for any desired event notification.
  9. Resets and makes the adapter ready.

    The HAM must ensure that it is operational and ready to accept HACB requests before going to step 10.

  10. Activates the host bus.

    The HAM calls HAI_Activate_Bus to activate an instance of a host bus. This API requires an exchange of handles that identify the bus instance. The HAM passes a unique handle (hamBusHandle) it generates to identify the bus instance as an input parameter. Then, NWPA returns its own unique handle (npaBusHandle) it will use to identify the bus instance as an output parameter. The HAM must call HAI_Activate_Bus for each bus instance it will manage.

  11. Return load status.

    If the HAM loaded successfully, HAM_Load should return zero. If the load was unsuccessful, it should do the following:

    1. Cancel HAM_Timeout by calling NPA_Cancel_Thread passing it the same arguments used in setting up the timeout routine.
    2. Return all allocated memory by calling NPA_Return_Memory.
    3. Unregister all hardware options by calling NPA_Unregister_Options.
    4. Unregister the module if the HAM is to be unloaded by calling NPA_Unregister_Module.

      WARNING:NPA_Unregister_Module will unregister all instances of this module and abort all pending I/O for this HAM. If other instances are to remain active, do not make this call. Use Instance Unload to unload a single instance (see Section 5.2, Unloading a Specific Instance of a HAM).

    5. Return -1.

If at any time during initialization and registration an uncorrectable error occurs, the HAM must return its resources and back out from the point it reached. For example, if the bus type returned in 2 is not compatible with what the HAM supports, the HAM only needs to call NPA_Unregister_Module to error out. If the HAM progressed as far as step 6 in the sequence, then the HAM would need to return memory, unregister options, and then unregister the module.

After the HAM is loaded and registered with the OS, it must be ready to receive the following sequence of hacbType = 0 requests:

The first request, HAM_Return_Bus_Info (Function 0x00), is initiated by NWPA so that it can get HAM-specific information and add the HAM to its object database.

The second request, HAM_Scan_For_Devices (Function 0x01), is either initiated from NWPA and OS or by the systems operator at the console. This HAM function spawns a blocking thread, using NPA_Spawn_Thread, that performs a host bus scan for attached devices. The spawned thread builds the HAM's device list, creates a unique deviceHandle for each device, and fills out an instance of a DeviceInfoStruct for each device.

The third request, HAM_Return_Device_Info (Function 0x02), is initiated by NWPA so that it can get device-specific information and add an object for each device to its database. NWPA initiates a find-first-find-next sequence of these requests until information about each device is returned. The return information for each request is in a form defined by the DeviceInfoStruct.

The HAM receives the above sequence of requests for each bus instance it registered at load-time using HAI_Activate_Bus.

3.2.2 HACB I/O Requests

The following figure outlines the process for HACB I/O requests:

Figure 3-2 HACB I/O Request Flow

HAM_Execute_HACB is the HAM's entry point for receiving and executing HACB I/O requests, and it has non-blocking context except for HAM_Instance_Unload (Function 0x09)). This entry point is registered with NWPA during NPA_Register_HAM_Module. The following steps show the sequence of events for processing a HACB I/O request:

  1. NWPA calls HAM_Execute_HACB passing it hamBusHandle and a pointer to a HACB as input parameters. HAM_Execute_HACB does the following:
    1. Identifies the host bus instance.

      The HAM identifies the target bus instance based on the value contained in the hamBusHandle input parameter. The HAM originally generated this hamBusHandle value and registered it for the bus instance using HAI_Activate_Bus at load-time.

      If the HAM is managing only one host bus, the value in the hamBusHandle input parameter is the same for all requests.

      If the HAM is managing multiple buses (when the adapter supports more than one bus or the HAM is managing multiple buses spanned over multiple adapters), the hamBusHandle value is unique to each bus instance.

    2. Identifies the target device.

      The HAM identifies the target device based on the value contained in the deviceHandle field of the HACBStruct instance pointed at by the HACB input parameter.

      The HAM originally generated this deviceHandle value during the scan thread scheduled by HAM_Scan_For_Devices (Function 0x01) and reported it to NWPA during HAM_Return_Device_Info (Function 0x02).

    3. Executes or queues the request.

      If the adapter can immediately accept the request, the HAM should translate the HACB request information into a protocol-specific command block, issue the request to the adapter, and then return to the calling process.

      NWPA expects the HAM to provide a queue for each device it manages.

      If a request cannot be immediately issued to the adapter during the context of HAM_Execute_HACB, the HAM must place the request in the target device's queue and return to the calling process. The HAM must pull requests from the queue and execute them at another time during another thread.

      For a detailed specification on device queue behavior and how it affects HAM_Execute_HACB, see Queue State.

    4. Return 0 from HAM_Execute_HACB.
  2. The target device services the request and at completion, the adapter generates a hardware interrupt. The NetWare OS fields the interrupt and routes servicing to the HAM's ISR entry point, HAM_ISR, passing it irqHandle as an input parameter. HAM_ISR has non-blocking context and does the following:
    1. Determines which device to service.

      The HAM must provide the logic to determine which of the adapters it is managing caused the interrupt.

    2. Ensures that data is transferred correctly.

      If the HAM's adapter does DMA or bus-mastering, the ISR is not concerned with physical data transfer because the transfer buffer was specified when the request was issued to the adapter. However, for host buses that rely on programmed I/O, the ISR needs to perform the transfer. The HACB provides both the virtual (logical) and physical (absolute) addresses of the request's I/O buffer. These addresses are found in the HACB's vDataBufferPtr and pDataBufferPtr fields, respectively.

    3. Posts completion status to the HACB.

      After the request is complete, HAM_ISR must post the HACB's completion status to its hacbCompletion field. Valid completion status values are listed in Section 11.1, HACB Completion Codes under the description of the hacbCompletion field. These status codes can reflect successful completion of the request, or they can reflect HACB and/or device errors. For processor-independence reasons, this field must be processed as a LONG. Manipulation of its contents can be done arithmetically using macros such as SET_STATUS. The HAM can post HACB completion using the SET_STATUS macro, defined below:

          #define SET_STATUS(UpperWord, LowerWord)((UpperWord)<< 16) | 
          ((LowerWord)& 0xFFFF))
          
    4. Completes the HACB as follows:
      • If the request completes successfully and the Freeze_Queue_Bit in the HACB's controlInfo field is cleared (zero), HAM_ISR completes the HACB by calling HAI_Complete_HACB, initiates the execution of the next HACB in the device's queue, and returns to the calling process.
      • If the request completes successfully and the Freeze_Queue_Bit in the HACB's controlInfo field is set (one), HAM_ISR completes the HACB by freezing the device queue, setting the most-significant-bit in the HACB's hacbCompletion field so that the callback CDM can know the device's post-completion queue state, calling HAI_Complete_HACB, and returning to the calling process. The device's queue must remain frozen until either the HAM receives a HAM_Unfreeze_Queue (Function 0x03) for that device, or it receives a priority HACB request for that device.
      • If there is an error, HAM_ISR freezes the device queue, sets the most-significant-bit in the HACB's hacbCompletion field so that the callback CDM can know the device's post-completion queue state, completes the HACB by calling HAI_Complete_HACB, and returns to the calling process.

      The device's queue must remain frozen until either the HAM receives a HAM_Unfreeze_Queue (Function 0x03) for that device, or it receives a priority HACB request for that device. Additionally, the low-order 31 bits of the hacbCompletion field must remain intact to the value set in the third bullet in step d above. This value indicates the type of error that occurred.

      HAM_ISR has other responsibilities regarding the device queue. For a detailed specification on device queue behavior and how it affects HAM_ISR, see Queue State.

3.2.3 HACB Request Aborts

The following figure outlines the process of HACB abort requests:

Figure 3-3 Aborting a HACB Request

HAM_Abort_HACB is the HAM's entry point for aborting I/O requests, and it has non-blocking context. This entry point is registered with NWPA during NPA_Register_HAM_Module. The following shows the sequence of events for aborting a HACB request.

NWPA calls HAM_Abort_HACB passing it hamBusHandle, a pointer to a HACB, and flag as input parameters.

HAM_Abort_HACB does the following:

  1. Identifies the host bus instance.

    The HAM identifies the target bus instance based on the value contained in the hamBusHandle input parameter. The HAM originally generated this hamBusHandle value and registered it for the bus instance using HAI_Activate_Bus at load-time. From this hamBusHandle, the HAM should be able to access its device list for the target bus instance.

  2. Locates the HACB to be aborted.

    NWPA passes a pointer to the HACB that is to be aborted, which the HAM uses to locate the associated request.

  3. Determines the appropriate abort action.

    HAM_Abort_HACB has three possible actions depending on the value of the flag input parameter passed by NWPA.

    • If flag = 0 (unconditional abort case), HAM_Abort_HACB does one of the following:

      If the HACB is still in the device queue (clean abort case):

      • Unlinks the HACB from the device queue.
      • Places the ABORT code (0x0004) in the upper WORD of the HACB's hacbCompletion field and 0x0000 in the lower WORD, using the SET_STATUS macro as follows: SET_STATUS(0x0004, 0x0000);
      • Completes the HACB by calling HAI_Complete_HACB, passing it the HACB's hacbPutHandle as an input parameter.
      • Returns 0 to notify NWPA that this was a clean abort, meaning that the HACB was aborted prior to being physically processed by the device.

      If the HACB is currently being processed by the device (dirty abort case):

      • Tags the HACB for abortion at a later time by placing the ABORT code (0x0004) in the upper WORD of the HACB's hacbCompletion field and 0x0000 in the lower WORD using the SET_STATUS macro, follows: SET_STATUS(0x0004, 0x0000);
      • Under the dirty abort case, HAM_Abort_HACB must not complete the HACB. The HAM will complete the HACB at a later time during its ISR.
      • Returns -1 to notify NWPA that the HACB could not be cleanly aborted.
      • HAM_ISR must intercept the aborting HACB and do the following: Place the ABORT code (0x0004) in the upper WORD of the HACB's hacbCompletion field and 0x0000 in the lower WORD using the SET_STATUS macro, follows: SET_STATUS(0x0004, 0x0000);
      • Complete the HACB by calling HAI_Complete_HACB passing it the HACB's hacbPutHandle as an input parameter.
    • If flag = 1 (conditional abort case), HAM_Abort_HACB does one of the following:

      If the HACB is still in the device queue (clean abort):

      • Unlinks the HACB from the device queue.
      • Places the ABORT code (0x0004) in the upper WORD of the HACB's hacbCompletion field and 0x0000 in the lower WORD using the SET_STATUS macro, follows: SET_STATUS(0x0004, 0x0000);
      • Completes the HACB by calling HAI_Complete_HACBpassing it the HACB's hacbPutHandle as an input parameter.
      • Returns 0 to notify NWPA that this was a clean abort, meaning that the HACB was aborted prior to being physically processed by the device.

      If the HACB has already been sent to the device, returns -1 to notify NWPA that the HACB could not be cleanly aborted. The device queue continues to operate normally.

    • If flag = 2 (check for clean abort case), HAM_Abort_HACB does the following:

      If the HACB is still in the device queue (clean abort), returns 0 to notify NWPA that the indicated HACB can be cleanly aborted. The device queue continues to operate normally.

      If the HACB has already been sent to the device, returns -1 to notify NWPA that the indicated HACB cannot be cleanly aborted. The device queue continues to operate normally.

  4. If the HAM cannot find the HACB that is to be aborted in any of its lists, it has lost the HACB. The HAM should then return -2 to notify NWPA of this condition.

    The results of step 4 will abend the server. The HAM must keep track of HACB requests it receives.

3.2.4 Unload-Time Deregistration

The following figure shows the process for deregistering at unload time:

Figure 3-4 Deregistering Unload-Time

Unloading of the HAM is initiated by the systems operator at the server console. The following steps show the sequence of events at unload-time.

  1. When a HAM is unloaded, the OS first calls the HAM's HAM_Unload_Check entry point passing it screenID as an input parameter. HAM_Unload_Check has blocking context, and it does the following:
    1. Determines if any NLM applications are using any of the devices managed by the HAM.

      HAM_Unload_Check calls NPA_Unload_Module_Check, which checks NWPA's database and returns the status of each device attached to the adapter. HAM_Unload_Check returns the use-status from NPA_Unload_Module_Check.

      NPA_Unload_Module_Check issues a warning message to the console for each device that is locked. Current I/O to these devices will halt if the HAM is unloaded, and the devices will be deactivated.

    2. Returns the composite device status to the calling process.

      A return value of zero indicates that none of the HAM's devices are in use. A return value greater than zero indicates that one or more of the HAM's devices are in use.

  2. If HAM_Unload_Check returns zero, the OS calls the HAM's HAM_Unload entry point. If HAM_Unload_Check returns non-zero, the OS issues a message to the console, giving the operator a chance to either cancel or continue the unload. Only if the operator chooses to continue the unload does the OS call the HAM's HAM_Unload entry point.

    The OS calls the HAM's HAM_Unload entry point with blocking context, and it does the following:

    1. Causes NWPA to terminate I/O to the HAM.

      HAM_Unload terminates I/O to the HAM by calling HAI_Deactivate_Bus immediately upon entry. It is during the context of this API that the NLM application is notified that its link to the device is about to be severed. Therefore, the HAM must remain operational and process requests until HAI_Deactivate_Bus returns control. Once this happens, the HAM is guaranteed not to receive any more HACB requests for that bus instance. HAM_Unload must call HAI_Deactivate_Bus for each bus instance being managed by the HAM.

    2. Returns resources back to the system.

      Verifies that all outstanding HACBs, if any exist, are canceled with the appropriate completion code described in Section 11.1, HACB Completion Codes. This action is really a preventative measure. All of these outstanding HACBs have been aborted during the context of HAI_Deactivate_Bus.

      Cancels all asynchronous events, such as timeout handlers, timers, etc., by calling NPA_Cancel_Thread on each event.

      Returns memory to the system pool by calling NPA_Return_Memory.

      Do not call NPA_Unregister_Options at unload time. NWPA resolves hardware unregistration issues within the call NPA_Unregister_Module. If called during unload, resources are unregistered even in a software hot replace condition. The driver then fails.

      Unregisters the module using NPA_Unregister_Module.

    3. Returns 0 if successful, or returns -1 if unsuccessful.