La structure d'en-tête struct sg_header
est utilisée comme couche de
contrôle entre l'application et le pilote noyau.
Abordons maintenant le détail de ses composants.
définit la taille de bloc en écriture. Cette valeur est définie dans le noyau pour une utilisation interne.
définit la taille de bloc acceptée en réponse. Cette valeur est définie du côté application.
ce champ facilite l'appariement des réponses aux requêtes.
L'application peut fournir un identifiant unique à chaque requête. Supposons
que vous ayez écrit un certain nombre de commandes (mettons quatre) pour un
périphérique. Celles-ci peuvent fonctionner en parallèle, l'une d'entre elles
étant la plus rapide. Lors de la lecture des réponses par quatre "read",
celles-ci ne sont pas forcément dans l'ordre des requêtes. Pour identifier la
réponse correcte pour une requête, on peut utiliser le champ pack_id
.
Habituellement, cette valeur est incrémentée après chaque requête (et boucle
éventuellement). Le nombre maximum de requêtes émises simultanément est limité
par le noyau à SG_MAX_QUEUE (en général, quatre).
c'est la valeur du résultat d'un appel à write
ou à
read
. Elle est définie par le pilote générique (noyau). Ces codes sont
définis dans errno.h
(0 indique un résultat correct).
ce champ n'est nécessaire que lors de
l'utilisation de commandes spécifiques non standard (dans la plage 0xc0 à
0xff). Lorsque la longueur de ces commandes est de 12 octets au lieu de 10,
il faut positionner ce champ à 1 avant l'appel à write
. D'autres
longueurs de commandes ne peuvent être utilisées. Ce champ est positionné par
l'application.
Ce tampon est positionné après
l'exécution d'une commande (après un appel à read()
) et contient le code
de "sensation" SCSI (NdT.~: dans le reste du document, on utilisera
simplement la formule "tampon SCSI"). Certains résultats de commandes
doivent être lus à cet emplacement (par exemple pour TESTUNITREADY
).
Il ne contient habituellement que des octets nuls. La valeur de ce champ est
positionnée par le pilote générique (noyau).
L'exemple de fonction qui suit s'interface directement avec le pilote générique
du noyau. Il définit la structure d'en-tête, envoie la commande par
write
, lit le résultat par read
et effectue un certain contrôle
d'erreur (limité). Les données du tampon SCSI sont disponibles dans le tampon
de sortie (sauf si un pointeur nul a été fourni, auquel cas elles se trouvent
dans le tampon d'émission). Nous l'utiliserons dans les exemples qui suivent.
Note~: positionnez la valeur de DEVICE
à celle qui correspond à votre
matériel.
#define DEVICE "/dev/sgc"
/* Programme d'exemple utilisant l'interface SCSI generique */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/../../drivers/scsi/sg.h>
#define SCSI_OFF sizeof(struct sg_header)
static unsigned char cmd[SCSI_OFF + 18]; /* tampon de commande SCSI */
int fd; /* descripteur de peripherique/fichier SCSI */
/* traite une commande SCSI complete avec l'interface generique */
static int handle_SCSI_cmd(unsigned cmd_len, /* longueur de commande */
unsigned in_size, /* taille data en entree */
unsigned char *i_buff,/* tampon d'entree */
unsigned out_size, /* taille data en sortie */
unsigned char *o_buff /* tampon de sortie */
)
{
int status = 0;
struct sg_header *sg_hd;
/* safety checks */
if (!cmd_len) return -1; /* necessite que cmd_len != 0 */
if (!i_buff) return -1; /* necessite que i_buff != 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; /* pas de tampon de sortie, pas de taille */
/* construction de l'entete generique de peripherique */
sg_hd = (struct sg_header *) i_buff;
sg_hd->reply_len = SCSI_OFF + out_size;
sg_hd->twelve_byte = cmd_len == 12;
#if 0
sg_hd->pack_len = SCSI_OFF + cmd_len + in_size; /* non indispensable */
sg_hd->pack_id; /* inutilise */
sg_hd->other_flags; /* inutilise */
#endif
/* envoi de la commande */
status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size );
if ( status < 0 || status != SCSI_OFF + cmd_len + in_size ||
sg_hd->result ) {
/* condition d'erreur */
fprintf( stderr, "write(generic) resultat = 0x%x cmd = 0x%x\n",
sg_hd->result, i_buff[SCSI_OFF] );
perror("");
return status;
}
if (!o_buff) o_buff = i_buff; /* controle du pointeur du tampon */
/* recuperation du resultat */
status = read( fd, o_buff, SCSI_OFF + out_size);
if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) {
/* condition d'erreur */
fprintf( stderr, "read(generic) statut = 0x%x, resultat = 0x%x, "
"cmd = 0x%x\n",
status, sg_hd->result, o_buff[SCSI_OFF] );
fprintf( stderr, "read(generic) tampon SCSI "
"%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]);
}
/* A-t-on ce qu'on attendait ? */
if (status == SCSI_OFF + out_size) status = 0; /* on a tout */
return status; /* 0 indique que tout est bon */
}
Bien que cela puisse sembler quelque peu complexe au premier abord, une grande partie du code est dédiée aux contrôle et détection d'erreurs (ce qui est utile même après la fin de l'exécution du code).
handle_SCSI_cmd
présente une forme généralisée pour tous les types de
commandes SCSI, qui correspondent à l'une des catégories qui suivent~:
Mode de donnees | Exemple de commande
============================================================
ni entree ni sortie de donnees | test d'unite prete
pas d'entree, sortie de donnees| requete, lecture
entree de donnes, pas de sortie| selection de mode, ecriture
entree et sortie de donnees | detection de mode
Chapitre suivant, Chapitre Précédent
Table des matières de ce chapitre, Table des matières générale
Début du document, Début de ce chapitre