pkg://SCSI-Programming-HOWTO.tar.gz:32672/SCSI-Programming-HOWTO-8.html
downloads
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
<TITLE>The Linux SCSI programming HOWTO: ヘッダ構造体</TITLE>
<LINK HREF="SCSI-Programming-HOWTO-9.html" REL=next>
<LINK HREF="SCSI-Programming-HOWTO-7.html" REL=previous>
<LINK HREF="SCSI-Programming-HOWTO.html#toc8" REL=contents>
</HEAD>
<BODY>
<A HREF="SCSI-Programming-HOWTO-9.html">次のページ</A>
<A HREF="SCSI-Programming-HOWTO-7.html">前のページ</A>
<A HREF="SCSI-Programming-HOWTO.html#toc8">目次へ</A>
<HR>
<H2><A NAME="s8">8. ヘッダ構造体</A></H2>
<P>
<P>
<A NAME="sec-header"></A>
ヘッダ構造体 <CODE>struct sg_header</CODE> はアプリケーションとカーネルドライバの
間の調整層をつとめます。
では構成要素について詳しく論じることにしましょう。
<P>
<DL>
<DT><B>int pack_len</B><DD><P>はドライバに書かれるブロックのサイズを定義します。
これはカーネルにおいて内部での使用のために定義されます。
<DT><B>int reply_len</B><DD><P>は応答で受け取られるブロックのサイズを定義します。
これはアプリケーションの側から定義されます。
<P>
<DT><B>int pack_id</B><DD><P>このフィールドは要求に対する回答を割当てるのに役立ちます。
アプリケーションは各要求に対して一意のidを与えることができます。
数個のコマンド(例えば 4つ)を一つのデバイスに write したとしましょう。
それらのコマンドは並列に動作し、どれか一つが最も速いでしょう。
4回の read によって回答を受け取る際、回答は要求した通り順番である必要はありません。
与えた要求に対する正しい回答を特定するために <CODE>pack_id</CODE> フィールドを使用することが
できます。典型的にはその値は各要求ごとに加増されます(そしてついには一周します)。
未解決の要求の最大量はカーネルによって SG_MAX_QUEUE (例えば 4)に制限されています。
<P>
<DT><B>int result</B><DD><P><CODE>read</CODE> あるいは <CODE>write</CODE> 呼び出しの結果コード。
これは(たまに)汎用ドライバ(カーネル)側から定義されます。
<CODE>write</CODE> を呼出す前には NULL にしておくのが安全です。
これらのコードは <CODE>errno.h</CODE> において定義されています(0 はエラーなしを意味)。
<P>
<DT><B>unsigned int twelve_byte:1</B><DD><P>このフィールドは非標準のベンダ固有のコマンドを使用する時にだけ必要です
(0xf0 - 0xff の範囲)。これらのコマンドのコマンド長が 10 ではなく 12 の
ときは、このフィールドは write の呼び出しの前に 1 にセットされねばなりません。
それ以外コマンド長はサポートされていません。これはアプリケーション側から
定義されます。
<P>
<DT><B>unsigned char sense_buffer[16]</B><DD><P>このバッファはコマンドが完遂した後(<CODE>read()</CODE> の呼び出しの後)でセットされ
SCSI のセンスコードを含んでいます。コマンドの結果によってはここから
読み出さねばならないものもあります(例えば <CODE>TESTUNITREADY</CODE>)。
通常は 0 バイトしかありません。このフィールドの値は汎用ドライバ
(カーネル)側からセットされます。
</DL>
<P>
<P>
<P>次の見本の関数は直接汎用カーネルドライバに作用します。ヘッダ構造体を定義し、
<CODE>write</CODE> を用いてコマンドを送出し、<CODE>read</CODE> によって結果を受け取り、
多少の(限られた)エラーチェックを行います。センスバッファデータは
出力バッファの中で手に入れることができます(ヌルポインタが与えられていない
限り、その場合は入力バッファ内にあります)。これを以下の例の中で用いる
ことにします。
<P>
<P>注意:<CODE>DEVICE</CODE>の値をあなたのデバイスを記述するものに設定してください。
<BLOCKQUOTE><CODE>
<PRE>
#define DEVICE "/dev/sgc"
/* 汎用 SCSI インターフェースを実際に動かしてみる見本プログラム */
#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 コマンド バッファ */
int fd; /* SCSI デバイス/ファイル ディスクリプタ */
/* 完結した SCSI コマンドを処理。 汎用 SCSI インターフェースを使用 */
static int handle_SCSI_cmd(unsigned cmd_len, /* コマンド長 */
unsigned in_size, /* 入力データサイズ */
unsigned char *i_buff, /* 入力バッファ */
unsigned out_size, /* 出力データサイズ */
unsigned char *o_buff /* 出力バッファ */
)
{
int status = 0;
struct sg_header *sg_hd;
/* 安全性検査 */
if (!cmd_len) return -1; /* cmd_len != 0 が必要 */
if (!i_buff) return -1; /* 入力バッファが 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; /* 出力バッファなし、出力サイズなし */
/* 汎用 SCSI デバイスヘッダの構築 */
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; /* 不要 */
sg_hd->pack_id; /* 未使用 */
sg_hd->other_flags; /* 未使用 */
#endif
/* コマンド送出 */
status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size );
if ( status < 0 || status != SCSI_OFF + cmd_len + in_size ||
sg_hd->result ) {
/* なんらかのエラーが発生 */
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; /* バッファのポインタをチェック */
/* 結果を回収 */
status = read( fd, o_buff, SCSI_OFF + out_size);
if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) {
/* なんらかのエラーが発生 */
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("");
}
/* 受け取るべきものを受け取ったかどうかをみる */
if (status == SCSI_OFF + out_size) status = 0; /* 全部もらった */
return status; /* 0 はエラーなしを意味 */
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>これは最初はいくぶんか複雑にみえるかもしれませんが、コードのほとんど
はエラーのチェックと報告です(これはコードが動作するようになったあとでも
役に立ちます)。
<P>
<P><CODE>Handle_SCSI_cmd</CODE> は全ての SCSI コマンドの種類に対しての一般的な
形式を備えていて、以下のように分類されます:
<P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
データモード | コマンドの例
===============================================
入力・出力データともになし | test unit ready
入力データなし、出力データあり| inquiry, read
入力データあり、出力データなし| mode select, write
入力・出力データともにあり | mode sense
</PRE>
</CODE></BLOCKQUOTE>
<P>
<HR>
<A HREF="SCSI-Programming-HOWTO-9.html">次のページ</A>
<A HREF="SCSI-Programming-HOWTO-7.html">前のページ</A>
<A HREF="SCSI-Programming-HOWTO.html#toc8">目次へ</A>
</BODY>
</HTML>