UMSクラスドライバを改造し、Androidを“CD-ROMドライブ”として認識させる!!実践しながら学ぶ Android USBガジェットの仕組み(13)(2/3 ページ)

» 2013年11月08日 09時30分 公開
[村上雅彦、舟元拓斗、森崇(永和システムマネジメント 組込み技術センター),MONOist]

3.Androidのドライバコードを改造

 以上、対応すべきSCSIコマンドと設定内容が分かりました。これを踏まえて、Androidのドライバコードを改造していきましょう。

3.1.Android USBマスストレージクラスドライバの構造

 まず、USBマスストレージクラスのドライバコードは、Linuxカーネルのルートディレクトリ配下にある以下の「f_mass_storage.c」というファイルです。


drivers/usb/gadget/f_mass_storage.c
「f_mass_storage.c」のパス

 本ファイルの処理内容をデータフローの観点で分析した結果を以下に示します(図1表7)。

データフロー 図1 「f_mass_storage.c」の処理内容をデータフローの観点で分析した結果 画像クリックで拡大表示

処理順番 処理内容
(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」の処理内容をデータフローの観点で分析した結果

3.2.Android USBマスストレージクラスドライバの改造方法

 それでは、USBマスストレージクラスドライバ(f_mass_storage.c)の改造方法について説明していきます。

(1)インタフェースディスクリプタ
 USBマスストレージクラスドライバのインタフェースディスクリプタは、f_mass_storage.cで以下のように定義されています。

ソースコード1

 この実装を見ると、bInterfaceSubClassは、「US_SC_SCSI(=06h)」と設定されており、期待したものであるため改造は不要です。

(2)INQUIRYコマンド応答
 INQUIRYコマンドの実行関数は以下のように改造します。

ソースコード2

 本関数の改造箇所は赤枠箇所であり、PDTをCDデバイスに変更します。

(3)READ CAPACITYコマンド応答
 READ CAPACITYコマンドの実装関数は、以下のように改造します。

ソースコード3

 本関数の改造箇所は赤枠箇所であり、ブロックサイズを512バイトから2048バイトに変更します。なお、この変更に伴い、f_mass_storage.c内でブロックサイズに依存する処理は全て同様な修正が必要となりますので、一括修正しています。

(4)READ TOCコマンド応答
 READ TOCコマンドは、既存のコードに存在しないため、以下の関数の修正・追加が必要となります。1つ目は、SCSIコマンドの実行関数呼び出し関数であるdo_scsi_command()です。

ソースコード4

 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.