|
|
The _load(D2) entry point routine is called when the driver is first loaded into the kernel, which could be the first time it is called or, if it is statically configured (marked by $static in the Master file), in the final stages of system configuration. For DDI 8 drivers, the _load( ) entry point must not be prefixed, and must initialize the driver and resources that are required by the driver as a whole, but not resources that are allocated for specific devices.
For the samp driver, the
_load( )
entry point routine populates and initializes a
bcb(D4)
(breakup control) structure
that will be used for actual I/O operations
by the
biostart(D2)
entry point routine.
Notice that the structure is named samp_bcb,
using the driver prefix to avoid collision
with a bcb structure for another driver in the kernel.
The code calls the
bcb_alloc(D3)
function to allocate the bcb
(the KM_SLEEP flag indicates that
it will block if necessary to wait for resources
required for the allocation).
It then populates the bcb_addrtypes
member
with BA_UIO to indicate
that the I/O operations will use the
uio(D4)
structure.
The bcb_granularity
member is set to 1,
to indicate the minimum acceptable offset
or size of a transfer.
The bcb_physreqp
member points to a
physreq(D4)
structure that defines the physical requirements
for the memory being allocated.
This is allocated with the
physreq_alloc(D3)
function, and set up to block if necessary
to wait for resources to be available.
Once the bcb is allocated and populated, the driver calls the bcb_prep(D3) to prepare the bcb and the associated physreq structure for use. Note that the physreq is not explicitly populated; physreq_alloc( ) allocates a physreq structure with default values set for the required members. The ddi8_sample driver has minimal hardware requirements so the default values are adequate. Most true hardware drivers will need to explicitly populate members of the physreq structure.
The code tests that this can happen and, if it does not, calls the samp_free_globals( ) subordinate routine to free up any resources that might have been allocated and exits gracefully by returning the ENOSYS error code discussed on the errnos(D5) manual page. This illustrates a primary principle of driver writing: always check for error conditions and handle them, even if it is unimaginable that the error could actually occur.
No other resources are required for the driver itself, so it calls the drv_attach(D3) function to register the driver in the kernel, based on information populated in the driver's drvinfo(D4) structure in the "Header files and declarations" part of the driver. Again, notice that the driver is coded to handle the condition where the driver cannot be registered so it fails gracefully and frees up any resources that may have been allocated.
This also illustrates one of the uses of a subordinate driver routine. If the bcb structure cannot be prepped or if the drv_attach( ) function fails, the same clean up is required. So coding that clean up functionality in a subordinate routine that is called in both places makes for cleaner code that is easier to maintain.