/usr/share/doc/HOWTO/de-html/DE-SCSI-Programmierung-HOWTO-7.html is in doc-linux-de 2003.10-5.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.65">
<TITLE>Das Linux SCSI Programmier HOWTO: Die Kopfstruktur</TITLE>
<LINK HREF="DE-SCSI-Programmierung-HOWTO-8.html" REL=next>
<LINK HREF="DE-SCSI-Programmierung-HOWTO-6.html" REL=previous>
<LINK HREF="DE-SCSI-Programmierung-HOWTO.html#toc7" REL=contents>
</HEAD>
<BODY>
<A HREF="DE-SCSI-Programmierung-HOWTO-8.html"><IMG SRC="next.png" ALT="Weiter"></A>
<A HREF="DE-SCSI-Programmierung-HOWTO-6.html"><IMG SRC="prev.png" ALT="Zurück"></A>
<A HREF="DE-SCSI-Programmierung-HOWTO.html#toc7"><IMG SRC="toc.png" ALT="Inhalt"></A>
<HR>
<H2><A NAME="s7">7.</A> <A HREF="DE-SCSI-Programmierung-HOWTO.html#toc7">Die Kopfstruktur</A></H2>
<P>
<A NAME="sec-header"></A>
Die Kopfstruktur <CODE>struct sg_header</CODE> dient als Übergabeschnittstelle
zwischen Applikation und Kerneltreiber.
Jetzt kommen wir zu den einzelnen Komponenten dieser Struktur.</P>
<P>
<DL>
<DT><B>int pack_len</B><DD>
<P>definiert die Größe des an den Treiber gesendeten
Blocks.
Das Feld wird vom Kernel zur internen Verwaltung gefüllt.</P>
<DT><B>int reply_len</B><DD>
<P>definiert die Größe des erwarteten Antwortblocks.
Das Feld wird von der Applikation bestimmt.</P>
<DT><B>int pack_id</B><DD>
<P>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.</P>
<DT><B>int result</B><DD>
<P>Der Rückgabewert eines <CODE>read</CODE> oder <CODE>write</CODE> Aufrufs.
Dieses Feld sollte vom Kernel definiert werden (leider nicht immer).
Daher ist es am besten, das Feld vor den <CODE>write</CODE> Aufrufen explizit
auf Null zu setzen. Die Codes sind in <CODE>errno.h</CODE> aufgeführt (0 bedeutet
kein Fehler).</P>
<DT><B>unsigned int twelve_byte:1</B><DD>
<P>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 <CODE>write</CODE> Aufruf auf Eins gesetzt werden. Andere
Längen werden nicht unterstützt. Dieses Feld wird von der Applikation
gefüllt.</P>
<DT><B>unsigned char sense_buffer[16]</B><DD>
<P>Dieser Puffer wird nach
Abarbeitung eines Kommandos (<CODE>read()</CODE> Aufruf) vom Kernel gefüllt und
enthält den sogenannten SCSI sense code.
Manche SCSI Kommandoergebnisse stehen hier (z.B. die des <CODE>TESTUNITREADY</CODE>
Kommandos). Ansonsten enthält der Puffer Nullen.</P>
</DL>
</P>
<P>Die folgende Beispielfunktion stellt den Kontakt mit dem Interface her.
Erst wird die Kopfstruktur definiert, dann der Befehl per <CODE>write</CODE>
geschickt, das Ergebnis per <CODE>read</CODE> 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.</P>
<P>Hinweis: Setze den Wert für <CODE>DEVICE</CODE> auf einen Deiner
Gerätedeskriptoren.</P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
#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 */
}
</PRE>
</CODE></BLOCKQUOTE>
</P>
<P>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.</P>
<P><CODE>Handle_SCSI_cmd</CODE> hat eine generalisierte Form für alle Arten von
SCSI Kommandos, was den Transfer von Daten angeht. Es gibt diese Kategorien:</P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
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
</PRE>
</CODE></BLOCKQUOTE>
</P>
<HR>
<A HREF="DE-SCSI-Programmierung-HOWTO-8.html"><IMG SRC="next.png" ALT="Weiter"></A>
<A HREF="DE-SCSI-Programmierung-HOWTO-6.html"><IMG SRC="prev.png" ALT="Zurück"></A>
<A HREF="DE-SCSI-Programmierung-HOWTO.html#toc7"><IMG SRC="toc.png" ALT="Inhalt"></A>
</BODY>
</HTML>
|