The header structure struct sg_header
serves as a controlling layer between the application and the kernel driver. We now discuss its components in detail.
defines the size of the block written to the driver. This is defined within the kernel for internal use.
defines the size of the block to be accepted at reply. This is defined from the application side.
This field helps to assign replies to requests. The application can supply a unique id for each request. Suppose you have written several commands (say 4) to one device. They may work in parallel, one being the fastest. When getting replies via 4 reads, the replies do not have to have the order of the requests. To identify the correct reply for a given request one can use the pack_id
field. Typically its value is incremented after each request (and wraps eventually). The maximum amount of outstanding requests is limited by the kernel to SG_MAX_QUEUE (eg 4).
the result code of a read
or write
call. This is (sometimes) defined from the generic driver (kernel) side. It is safe to set it to null before the write
call. These codes are defined in errno.h
(0 meaning no error).
This field is necessary only when using non-standard vendor specific commands (in the range 0xc0 - 0xff). When these commands have a command length of 12 bytes instead of 10, this field has to be set to one before the write call. Other command lengths are not supported. This is defined from the application side.
This buffer is set after a command is completed (after a read()
call) and contains the SCSI sense code. Some command results have to be read from here (e.g. for TESTUNITREADY
). Usually it contains just zero bytes. The value in this field is set by the generic driver (kernel) side.
The following example function interfaces directly with the generic kernel driver. It defines the header structure, sends the command via write
, gets the result via read
and does some (limited) error checking. The sense buffer data is available in the output buffer (unless a NULL pointer has been given, in which case it's in the input buffer). We will use it in the examples which follow.
Note: Set the value of DEVICE
to your device descriptor.
#define DEVICE "/dev/sgc" /* Example program to demonstrate the generic SCSI interface */ #include <stdio.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <scsi/sg.h> #define SCSI_OFF sizeof(struct sg_header) static unsigned char cmd[SCSI_OFF + 18]; /* SCSI command buffer */ int fd; /* SCSI device/file descriptor */ /* process a complete SCSI cmd. Use the generic SCSI interface. */ static int handle_SCSI_cmd(unsigned cmd_len, /* command length */ unsigned in_size, /* input data size */ unsigned char *i_buff, /* input buffer */ unsigned out_size, /* output data size */ unsigned char *o_buff /* output buffer */ ) { int status = 0; struct sg_header *sg_hd; /* safety checks */ if (!cmd_len) return -1; /* need a cmd_len != 0 */ if (!i_buff) return -1; /* need an input buffer != NULL */ #ifdef SG_BIG_BUFF if (SCSI_OFF + cmd_len + in_size > SG_BIG_BUFF) return -1; if (SCSI_OFF + out_size > SG_BIG_BUFF) return -1; #else if (SCSI_OFF + cmd_len + in_size > 4096) return -1; if (SCSI_OFF + out_size > 4096) return -1; #endif if (!o_buff) out_size = 0; /* no output buffer, no output size */ /* generic SCSI device header construction */ sg_hd = (struct sg_header *) i_buff; sg_hd->reply_len = SCSI_OFF + out_size; sg_hd->twelve_byte = cmd_len == 12; sg_hd->result = 0; #if 0 sg_hd->pack_len = SCSI_OFF + cmd_len + in_size; /* not necessary */ sg_hd->pack_id; /* not used */ sg_hd->other_flags; /* not used */ #endif /* send command */ status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size ); if ( status < 0 || status != SCSI_OFF + cmd_len + in_size || sg_hd->result ) { /* some error happened */ fprintf( stderr, "write(generic) result = 0x%x cmd = 0x%x\n", sg_hd->result, i_buff[SCSI_OFF] ); perror(""); return status; } if (!o_buff) o_buff = i_buff; /* buffer pointer check */ /* retrieve result */ status = read( fd, o_buff, SCSI_OFF + out_size); if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) { /* some error happened */ fprintf( stderr, "read(generic) status = 0x%x, result = 0x%x, " "cmd = 0x%x\n", status, sg_hd->result, o_buff[SCSI_OFF] ); fprintf( stderr, "read(generic) sense " "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", sg_hd->sense_buffer[0], sg_hd->sense_buffer[1], sg_hd->sense_buffer[2], sg_hd->sense_buffer[3], sg_hd->sense_buffer[4], sg_hd->sense_buffer[5], sg_hd->sense_buffer[6], sg_hd->sense_buffer[7], sg_hd->sense_buffer[8], sg_hd->sense_buffer[9], sg_hd->sense_buffer[10], sg_hd->sense_buffer[11], sg_hd->sense_buffer[12], sg_hd->sense_buffer[13], sg_hd->sense_buffer[14], sg_hd->sense_buffer[15]); if (status < 0) perror(""); } /* Look if we got what we expected to get */ if (status == SCSI_OFF + out_size) status = 0; /* got them all */ return status; /* 0 means no error */ }
While this may look somewhat complex at first appearance, most of the code is for error checking and reporting (which is useful even after the code is working).
Handle_SCSI_cmd
has a generalized form for all SCSI commands types, falling into each of these categories:
Data Mode | Example Command =============================================== neither input nor output data | test unit ready no input data, output data | inquiry, read input data, no output data | mode select, write input data, output data | mode sense