This month, I’m going to talk about writing a driver for a simple SCSI controller under Linux. The Linux kernel SCSI layer does most of the work for SCSI device handling, so a simple SCSI driver is relatively painless to write. For more advanced devices, however, the kernel’s SCSI code is actually too clever — there are plans afoot to streamline it and solve these problems.
The job of a SCSI driver is different than that of a block device driver. The upper layers of the SCSI code handle CD-ROMs, disks and other devices. Requests from these upper layers are turned into SCSI command blocks before they are fed to your driver. This means your SCSI driver need only worry about low-level SCSI requests and not about other aspects of the kernel device structure.
In order to illustrate SCSI drivers I’m going to invent a SCSI controller that has a simple command interface. Sadly, this is rarely the case for real devices. However, our imaginary SCSI controller will make the examples much easier to follow. This should be enough to get you started — dealing with the complexities of a particular piece of hardware would take up more space than this column allows.
A Linux SCSI driver contains seven main functions:
* The detect function is called by the SCSI layer when the driver is initialized. It is responsible for scanning for the controller and registering whatever it finds with the SCSI layer. Oncecontrollers are registered, the SCSI layer will issue commands to the controller to probe the SCSI devices on the chain.
* The queuecommand function issues a SCSI command and does not wait for it to finish. This is used by almost all
operations. When the command completes, the driver calls back up to the SCSI layer to inform it of the completion, passing back any error information it may find.
* The command function issues a SCSI command synchronously and then waits for it to complete. Most drivers do this by calling their own queuecommand function.
* The abort and reset functions are used to handle error situations orcases where the SCSI layer thinks a command has gone missing. The SCSI layer will first attempt to abort a command which has gone astray; if this isn’t effective, then the SCSI layer may need to reset the entire controller. Hopefully, this will never happen.
* The info function returns a description of the actual controller itself. This descriptor tends to be a very short piece of code.
* Finally, the bios_param function is called by the SCSI layer to ask the controller to query its BIOS for a faked disk geometry, or to have the driver compute a geometry itself. This is necessary as the mapping of SCSI blocks to the PC disk geometry has never been properly standardized; different controllers implement this in different ways.
The detect function [ see Figure 1 ] is called at boot time or when a SCSI module is loaded. For our example we are going to assume that there can be only one card and that it behaves sanely.
Figure 1: The myscsi_detect Function
int myscsi_detect(Scsi_Host_Template *tpnt)
{
struct Scsi_Host *shpnt;
int io = 0×320, irq = 11; /* Assume fixed for example */
if(myscsi_probe(io, irq) == 0)
{
/* Found - create an instance of this controller */
shpnt = scsi_register(tpnt, 0);
if(shpnt == NULL)
return 0;
shpnt->unique_id = io;
shpnt->io_port = io;
shpnt->n_io_port = MY_PORT_RANGE;
shpnt->irq = irq;
shpnt->this_id = MY_SCSI_ID;
my_hardware_init(shpnt);
if(request_irq(irq, my_irq_handler, 0, “myscsi”, shpnt))
{
scsi_unregister(shpnt);
printk(”my_scsi: IRQ %d is busy.n”, irq);
return 0;
}
}
return 1;
}
|
In our example we will use a fixed IO and IRQ. A real controller would either read PCI space or would probe a list of common addresses. The logic to probe for a particular device is hidden here in the function myscsi_probe .
After probing for the controller, we create a device structure using scsi_register . tpnt is the template passed into the detect function, described later in the article. The returned structure, shpnt , represents the particular controller which we have registered. We pass 0 for the second argument of scsi_register , as no driver-private memory area is needed. Passing a size arranges for a private block to be allocated as shpnt->hostdata , for convenience.
Now we start to fill in the shpnt structure. unique_id is for distinguishing multiple cards. In our case, the I/O portis a convenient choice for this. this_id holds the ID of the controller itself. Each SCSI device has an identity, including the controller ID, which the kernel SCSI layer needs to know about. We assume for this example that it is fixed.
We then call the my_hardware_init routine, which initializes the actual hardware. You get to write this routine to do whatever initialization the hardware requires.
Linux Magazine /
August 1998 /
Gearheads /
Page
1
2
3
Linux Magazine /
August 1999 / GEARHEADS ONLY
An Introduction to SCSI Drivers