The answer is provided by a set of function vector tables that are indexed by adapter number, and that yield the address of the appropriate function for that adapter.
This declaration states that scsi_command is an array of pointers to functions. Each function has the prototypeextern void (*scsi_command[])(struct scsi_request *req);
The four tables scsi_command, scsi_alloc, scsi_free, and scsi_info are similar. Each is an array of pointers to functions. Each array is indexed by the adapter type number. If iAdapT is an integer variable containing the adapter type number for a device, the following statements are valid calls to the four host adapter functions (the function arguments are examined in detail in the following topics):void function(struct scsi_request *req);
#include <sys/scsi.h> pTargInfo = (*scsi_info[iAdapT])(iAdap,iTarg,iLun); iAllocRet = (*scsi_alloc[iAdapT])(iAdap,iTarg,iLun,0,NULL); (void) (*scsi_command[iAdapT])(&request); (void) (*scsi_free[iAdapT])(iAdap,iTarg,iLun,NULL);Each statement is a function call, but in each case, the name of the function is replaced by an expression that indexes the appropriate table.
The adapter type number for each adapter in the system is stored in an array maintained by the scsi driver. The array is declared as follows in sys/scsi.h:
When indexed by the number of the adapter in use, this table returns the adapter type number of the host adapter driver for that adapter.extern u_char scsi_driver_table[];
One method is to get the number in the edt_t structure. When a device is configured using a VECTOR line, the VECTOR should contain an adapter=n parameter. This number is stored in the e_adap field of the edt_t structure that is passed to the pfxedtinit() entry point. Code to retrieve it in a hypothetical driver is shown in Example 15-1.
Example 15-1 : Storing the Adapter Type Number in pfxedtinit()
#include <sys/scsi.h> typedef struct devVital_s { uchar devAdapNum; uchar devAdapType; ...} devVital_t; void hypo_edtinit(edt_t *edt) { devVital_t *pVitals; ... pVitals->devAdapNum = edt->e_adap; pVitals->devAdapType = scsi_driver_table[edt->e_adap]; ... }A second method is to get it from the device minor number. For all Silicon Graphics disk and tape devices, the adapter number is encoded into both the visible name and the minor number of the device special file. You can use the bits of the minor number of any device in a similar way (see "Minor Device Number").
Under the second plan, geteminor() is used to extract the minor number is extracted from the dev_t value passed to each entry point (see "Device Number Functions"). The adapter number is calculated by shifting and masking the minor number. Hypothetical example code is shown in Example 15-2. The code of Example 15-2 can be extended to macros for the logical unit and control unit in obvious ways.
Example 15-2 : Extracting an Adapter Number From a Minor Device Number
/* Hypothetical minor bits: 00 aaaaaaaa ccccuuuu */ #define MINOR_ADAP_SHIFT 8 #define MINOR_ADAP_MASK 0x00ff #define MINOR_ADAP(devt) (MINOR_ADAP_MASK & \ (geteminor(devt) >> MINOR_ADAP_SHIFT))When the adapter number is known, the expression to call a host adapter function can be converted to a macro as well, possibly making the code more readable. The macro in Example 15-3 encapsulates a call to scsi_alloc(). This code takes advantage of the fact that the adapter number is an argument to the function in any case.
Example 15-3 : Macro to Encapsulate a Call to scsi_alloc()
#define SCSI_ALLOC(adap,targ,lun,opt,func) \ (*scsi_alloc[scsi_driver_table[adap]]) \ (adap,targ,lun,opt,func)It could be argued that the double indexing in Example 15-3 imposes needless overhead. An approach with minimum overhead is to reserve space in the device-information structure for four function addresses, and to store the addresses of the host adapter functions with the other unique device information when the device is initialized.