Inhalt

7. Die Kopfstruktur

Die Kopfstruktur struct sg_header dient als Übergabeschnittstelle zwischen Applikation und Kerneltreiber. Jetzt kommen wir zu den einzelnen Komponenten dieser Struktur.

int pack_len

definiert die Größe des an den Treiber gesendeten Blocks. Das Feld wird vom Kernel zur internen Verwaltung gefüllt.

int reply_len

definiert die Größe des erwarteten Antwortblocks. Das Feld wird von der Applikation bestimmt.

int pack_id

Dieses Feld dient zum Wiederfinden einer Antwort zur gestarteten Anfrage. Die Applikation kann einen neuen ID für jedes Kommando angeben. Die Antworten sind dann den Kommandos zuzuordnen, auch wenn sie in anderer Reihenfolge erscheinen. Bis zu SG_MAX_QUEUE (z.B. 4) parallele Kommandos sind möglich.

int result

Der Rückgabewert eines read oder write Aufrufs. Dieses Feld sollte vom Kernel definiert werden (leider nicht immer). Daher ist es am besten, das Feld vor den write Aufrufen explizit auf Null zu setzen. Die Codes sind in errno.h aufgeführt (0 bedeutet kein Fehler).

unsigned int twelve_byte:1

Das Feld wird nur bei Nichtstandardbefehlen (vendor specific Kommandos) im Bereich 0xc0 - 0xff ausgewertet. Wenn Kommandos aus diesem Bereich eine Länge von 12 Bytes statt 10 Bytes (ohne eventuelle Daten) haben, muß dieses Feld vor dem write Aufruf auf Eins gesetzt werden. Andere Längen werden nicht unterstützt. Dieses Feld wird von der Applikation gefüllt.

unsigned char sense_buffer[16]

Dieser Puffer wird nach Abarbeitung eines Kommandos (read() Aufruf) vom Kernel gefüllt und enthält den sogenannten SCSI sense code. Manche SCSI Kommandoergebnisse stehen hier (z.B. die des TESTUNITREADY Kommandos). Ansonsten enthält der Puffer Nullen.

Die folgende Beispielfunktion stellt den Kontakt mit dem Interface her. Erst wird die Kopfstruktur definiert, dann der Befehl per write geschickt, das Ergebnis per read abgeholt und Fehlerstati gecheckt. Der Sensepuffer ist im Ausgabepuffer untergebracht, es sei denn ein NULL-Zeiger wurde angegeben, dann wird der Eingabepuffer verwendet. Wir werden ihn in einem der Beispiele verwenden.

Hinweis: Setze den Wert für DEVICE auf einen Deiner Gerätedeskriptoren.

#define DEVICE "/dev/sgc"

/* Beispielprogramm zur Demonstration des generischen 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 Kommandopuffer */
int fd;                               /* SCSI device/file-Deskriptor */

/* einen kompletten SCSI Befehl abarbeiten. Verwende das generische 
SCSI-Interface. */
static int handle_SCSI_cmd(unsigned cmd_len,         /* Kommandolänge */
                           unsigned in_size,         /* Eingabedatengröße */
                           unsigned char *i_buff,    /* Eingabepuffer */
                           unsigned out_size,        /* Ausgabedatengröße */
                           unsigned char *o_buff     /* Ausgabepuffer */
                           )
{
    int status = 0;
    struct sg_header *sg_hd;

    /* Sicherheitsüberprüfungen */
    if (!cmd_len) return -1;            /* erfordert cmd_len != 0 */
    if (!i_buff) return -1;             /* erfordert Eingabepuffer != 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;      /* kein Ausgabepuffer, keine 
Ausgabegröße */

    /* Aufbau der generischen SCSI-Device Kopfstruktur */
    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; /* nicht notwendig */
    sg_hd->pack_id;     /* unbenutzt */
    sg_hd->other_flags; /* unbenutzt */
#endif

    /* Befehl schicken */
    status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size );
    if ( status < 0 || status != SCSI_OFF + cmd_len + in_size || 
                       sg_hd->result ) {
        /* ein Fehler ist aufgetreten */
        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;       /* Pufferpointer checken */
    sg_hd = (struct sg_header *) o_buff;

    /* Ergebnis abholen */
    status = read( fd, o_buff, SCSI_OFF + out_size);
    if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) {
        /* ein Fehler ist aufgetreten */
        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("");
    }
    /* Nachsehen, ob wir bekommen haben, was wir wollten */
    if (status == SCSI_OFF + out_size) status = 0; /* alles bekommen */

    return status;  /* 0 bedeutet kein Fehler */
}

Auf den ersten Blick mag dies abschreckend wirken, jedoch dient der meiste Code zur Überprüfung und zum Melden von Fehlern. Das ist nützlich, selbst wenn der Code fehlerfrei gemacht wurde.

Handle_SCSI_cmd hat eine generalisierte Form für alle Arten von SCSI Kommandos, was den Transfer von Daten angeht. Es gibt diese Kategorien:

       Datenmodus                     | Beispiel Kommando
=========================================================
weder Eingabe- noch Ausgabe-Daten     | test unit ready
keine Eingabedaten, aber Ausgabedaten | inquiry, read
Eingabedaten, aber keine Ausgabedaten | mode select, write
Eingabedaten und Ausgabedaten         | mode sense


Inhalt