linux.oreilly.com --
An Introduction to SCSI Drivers
Figure 1: The myscsi_detect Function
int myscsi_detect(Scsi_Host_Template *tpnt)
{
struct Scsi_Host *shpnt;
int io = 0x320, 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;
}
Figure 2: The myscsi_queuecommand Function
int myscsi_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
int io, i;
unsigned long flags;
io = SCpnt-host-io_port; /* Dig out our I/O port */
current_command = SCpnt;
current_command-scsi_done = done;
current_command-SCp.Status = 0;
save_flags(flags);
cli();
outb(SCpnt-target, io + TARGET_PORT);
for(i = 0;i SCpnt-cmd_len;i++)
outb(SCpnt-cmnd[i], io + BUF + i);
outb(COMMAND_BEGIN, io + COMMAND);
restore_flags(flags);
return 0;
}
Figure 3: The myscsi_abort Function
int myscsi_abort(Scsi_Cmnd *SCpnt)
{
return SCSI_ABORT_SNOOZE;
}
Figure 4: The myscsi_reset Function
int myscsi_reset(Scsi_Cmnd *SCpnt, unsigned int flags)
{
myhardware_reset(SCpnt-host);
return SCSI_RESET_PENDING;
}
Figure 5: The my_irq_handler Function
int my_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
struct Scsi_Host *shpnt = dev_id;
int io = shpnt-io_port;
u16 data;
data = inw(io + READ_STATUS);
if(data & RESET_DONE)
{
current_command-result = DID_RESET 16;
current_command-scsi_done(current_command);
return;
}
if(data & PARITY_ERROR)
{
current_command-result = DID_PARITY 16;
current_command-scsi_done(current_command);
return;
}
if(data & GENERAL_ERROR)
{
current_command-result = DID_ERROR 16;
current_command-scsi_done(current_command);
return;
}
if(data & DATA_OUT)
{
outsw(port + DATA_FIFO,
current_command-request_buffer,
current_command-request_bufflen);
}
if(data & DATA_IN)
{
int len = inw(port + DATA_LEN);
if(len current_command-request_bufflen)
len = current_command-request_bufflen;
insw(port + DATA_FIFO, current_command-request_buffer,
current_command-request_bufflen);
}
if(data & COMMAND_DONE)
{
current_command-status = inb(port + CMD_STATUS);
current_command-scsi_done(current_command);
}
}
Figure 6: The myscsi_command Function
static void it_finished(Scsi_Cmnd *SCpnt)
{
SCpnt-SCp.Status++;
}
int myscsi_command(Scsi_Cmnd *SCpnt)
{
myscsi_queuecommand(SCpnt, it_finished);
/* Wait for the command to complete */
while(!SCpnt-SCp.Status)
barrier();
return SCpnt-result;
}
Figure 7: The myscsi_info Function
const char *myscsi_info(struct Scsi_Host *SChost)
{
return("My SCSI device");
}
Figure 8: The sym53c416_bios_param Function
int sym53c416_bios_param(Disk *disk, kdev_t dev, int *ip)
{
int size;
size = disk-capacity;
ip[0] = 64; /* heads */
ip[1] = 32; /* sectors */
if((ip[2] = size 11) 1024)
/* cylinders, test for
big disk */
{
ip[0] = 255; /* heads */
ip[1] = 63; /* sectors */
ip[2] = size / (255 * 63); /* cylinders */
}
return 0;
}
Figure 9: The myscsi_release Function
int myscsi_release(struct Scsi_Host *SChost)
{
free_irq(SChost-irq, SChost);
return 0;
}
Figure 10: The myscsi_release Function
extern int myscsi_detect(Scsi_Host_Template *);
extern const char *myscsi_info(struct Scsi_Host *)
/* ... other function definitions ... */
#define MYSCSI { \
name: "My SCSI Demo", \
detect: myscsi_detect, \
info: myscsi_info, \
command: myscsi_command, \
queuecommand:myscsi_queuecommand, \
abort: myscsi_abort, \
reset: myscsi_reset, \
bios_param: myscsi_bios_param, \
can_queue: 1, \
this_id: MY_SCSI_ID, \
sg_tablesize: SG_NONE, \
cmd_per_lun: 1, \
unchecked_isa_dma: 1, \
use_clustering: ENABLE_CLUSTERING, \
proc_dir: &myscsi_proc \
}
Figure 11: The myscsi_proc Structure
struct proc_dir myscsi_proc =
{
PROC_SCSI_MYSCSI,
"myscsi",
6, /* Length of name */
S _ IFDIR|S _ IRUGO|S _ IXUGO,
2
};