Linuxdoc Linux Questions
Click here to ask our community of linux experts!
Custom Search
Next Previous Contents

8. The Header Structure

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.

int pack_len

defines the size of the block written to the driver. This is defined within the kernel for internal use.

int reply_len

defines the size of the block to be accepted at reply. This is defined from the application side.

int pack_id

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).

int result

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).

unsigned int twelve_byte:1

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.

unsigned char sense_buffer[16]

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


Next Previous Contents