以上、対応すべきSCSIコマンドと設定内容が分かりました。これを踏まえて、Androidのドライバコードを改造していきましょう。
まず、USBマスストレージクラスのドライバコードは、Linuxカーネルのルートディレクトリ配下にある以下の「f_mass_storage.c」というファイルです。
drivers/usb/gadget/f_mass_storage.c
本ファイルの処理内容をデータフローの観点で分析した結果を以下に示します(図1、表7)。
| 処理順番 | 処理内容 |
|---|---|
| (1)USBデータ受信 | ホストPCから受信したUSBデータは、下位ドライバで受信し、USBマスストレージクラスドライバに受信通知します |
| (2)コマンドデータ作成 | 受信したデータは、get_next_command()で取得し、内部バッファ(SCSIコマンドデータ)にコピーします |
| (3)コマンドデータチェック | SCSIコマンドデータは、共通関数であるcheck_command()にて、データの妥当性チェックが行われます |
| (4)コマンド実行 | データの妥当性チェック後、SCSIコマンドの種別に応じて対応するSCSIコマンド実行関数(do_<SCSIコマンド>())が呼び出されます(※例えば、INQUIRYの場合は、do_inquiry()など) |
| (5)応答データ作成 | コマンド実行終了後、応答が必要な場合は、SCSI応答データを作成します |
| (6)SCSIコマンド応答 | 作成されたSCSI応答データは、finish_reply()にて下位ドライバに渡されます |
| (7)USBデータ送信 | 下位ドライバは、応答データをUSBデバイスに渡し、ホストPCにUSBデータ送信します |
| 表7「f_mass_storage.c」の処理内容をデータフローの観点で分析した結果 | |
それでは、USBマスストレージクラスドライバ(f_mass_storage.c)の改造方法について説明していきます。
(1)インタフェースディスクリプタ
USBマスストレージクラスドライバのインタフェースディスクリプタは、f_mass_storage.cで以下のように定義されています。
この実装を見ると、bInterfaceSubClassは、「US_SC_SCSI(=06h)」と設定されており、期待したものであるため改造は不要です。
(2)INQUIRYコマンド応答
INQUIRYコマンドの実行関数は以下のように改造します。
本関数の改造箇所は赤枠箇所であり、PDTをCDデバイスに変更します。
(3)READ CAPACITYコマンド応答
READ CAPACITYコマンドの実装関数は、以下のように改造します。
本関数の改造箇所は赤枠箇所であり、ブロックサイズを512バイトから2048バイトに変更します。なお、この変更に伴い、f_mass_storage.c内でブロックサイズに依存する処理は全て同様な修正が必要となりますので、一括修正しています。
(4)READ TOCコマンド応答
READ TOCコマンドは、既存のコードに存在しないため、以下の関数の修正・追加が必要となります。1つ目は、SCSIコマンドの実行関数呼び出し関数であるdo_scsi_command()です。
2つ目は、READ TOCコマンドの実行関数であり、以下のように定義しました。
int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
int format, toclen;
struct lun *curlun = fsg->curlun;
int msf = fsg->cmnd[1] & 0x02;
int start_track = fsg->cmnd[6];
u8 *buf = (u8 *) bh->buf;
uint64_t nb_sectors = fsg->curlun->num_sectors;
int ret;
msf = fsg->cmnd[1] & 0x2;
format = fsg->cmnd[2] & 0xf;
start_track = fsg->cmnd[6];
return cdrom_read_toc(nb_sectors, buf, msf, start_track);
}
cdrom_read_toc()関数は、lba_to_msf()関数を使用して、msfの値に応じて適切な応答データを作成します。
static int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
{
uint8_t *q;
int len;
if (start_track > 1 && start_track != 0xaa)
return -1;
q = buf + 2;
*q++ = 1; /* first session */
*q++ = 1; /* last session */
if (start_track <= 1) {
*q++ = 0; /* reserved */
*q++ = 0x14; /* ADR, control */
*q++ = 1; /* track number */
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, 0);
q += 3;
} else {
/* sector 0 */
put_be32((uint32_t *)q, 0);
q += 4;
}
}
/* lead out track */
*q++ = 0; /* reserved */
*q++ = 0x14; /* ADR, control */
*q++ = 0xaa; /* track number */
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, nb_sectors);
q += 3;
} else {
put_be32((uint32_t *)q, nb_sectors);
q += 4;
}
len = q - buf;
put_be16((uint16_t *)buf, len - 2);
return len;
}
static void lba_to_msf(uint8_t *buf, int lba)
{
buf[2] = lba % 75; /* F */
lba /= 75;
lba += 2;
buf[1] = lba % 60; /* S */
buf[0] = lba / 60; /* M */
}
Copyright © ITmedia, Inc. All Rights Reserved.
組み込み開発の記事ランキング
コーナーリンク
よく読まれている編集記者コラム