Linux から読み解く光学ドライブの自動開閉機能有無の判別技法

# 概要

(☝ ՞ਊ ՞)☝ウイーン（光学ドライブを eject）するコードを書くがために調べ物をしていて思ったことのメモである。

Linux のソースや、Multi-Media Commands (MMC) の仕様書などを読み、ざっくりした説明を与えることで、Linux はどのように光学ドライブの自動開閉機能の有無を判別しているのかをざっくりと理解し、その問題点を見出すとともに、Linux 以外の OS にも応用できる新たな手法を提案するものである。

Linux の内部については詳しくないので誤りが含まれるかもしれない。コードの書き方は本質的ではないので触れない。

# はじめに

Linux において、

```c
r = ioctl(fd, CDROM_GET_CAPABILITY, 0);
```

というようなコードで光学ドライブの機能（自動開閉機能の有無を含む）を取得できる。一般にこれは MMC というコマンドセットを通じて行われるようである。

- https://en.wikipedia.org/wiki/SCSI_Multimedia_Commands
- http://www.13thmonkey.org/documentation/SCSI/

# `MODE SENSE (10)`

## 仕様

この `ioctl` の戻り値に `CDC_CLOSE_TRAY` や `CDC_OPEN_TRAY` などがセットされているわけだが、実際にどの関数でセットされているのかを調べると、[pcd.c](https://github.com/torvalds/linux/blob/master/drivers/block/paride/pcd.c) の `pcd_probe_capabilities` 関数あるいは [sr.c](https://github.com/torvalds/linux/blob/master/drivers/scsi/sr.c) の `get_capabilities` 関数が見つかる。

これらはともに `MODE SENSE (10)` コマンドの `Page Code` が `2A` (hex) であるものを使っているようで、これはデバイスの機能などを取得するコマンドであるが、`CDC_OPEN_TRAY` に関してはともにコマンド実行結果の同じビットを調べている。

たとえば [MMC-3](http://www.13thmonkey.org/documentation/SCSI/mmc3r10g.pdf) (Revision 10g / November 12, 2001)  の 6.3.11 にある Table 361 – CD/DVD Capabilities and Mechanical Status Page における、6 バイト目の下から 3 ビット目にあたる（0 オリジン）、`Eject (Individual or Magazine)` ビットである。

MMC-3 によれば、このビットが立っていれば `START STOP UNIT` コマンドの `LoEj` ビット経由で Eject 可能であることが期待できる。[MMC-2](http://www.13thmonkey.org/documentation/SCSI/mmc2r11a.pdf) (Revision 11a / August 30, 1999) の 5.5.10 における Table 137 – CD Capabilities and Mechanical Status Page でも同様である。

それらの表から抜粋するとこうである。

<table><tbody>
  <tr>
    <th>Byte | Bit</th>
    <th>7</th>
    <th>6</th>
    <th>5</th>
    <th>4</th>
    <th>3</th>
    <th>2</th>
    <th>1</th>
    <th>0</th>
  </tr>
  <tr>
    <th>6</th>
    <td colspan="3"><strong>Loading Mechanism Type</strong></td>
    <td>Reserved</td>
    <td><strong>Eject (Individual or Magazine)</strong></td>
    <td>Prevent Jumper</td>
    <td>Lock State</td>
    <td>Lock</td>
  </tr>
</tbody></table>

また、この上位 3 ビットで占められる `Loading Mechanism Type` フィールドについては次のようになる。

<table><tbody>
  <tr>
    <th>7 6 5</th><th>Loading Mechanism Type</th>
  </tr>
  <tr>
    <td>0 0 0</td><td>Caddy type loading mechanism</td>
  </tr>
  <tr>
    <td>0 0 1</td><td>Tray type loading mechanism</td>
  </tr>
  <tr>
    <td>0 1 0</td><td>Popup type loading mechanism</td>
  </tr>
  <tr>
    <td>0 1 1</td><td>Reserved</td>
  </tr>
  <tr>
    <td>1 0 0</td><td>Changer with individually changeable discs</td>
  </tr>
  <tr>
    <td>1 0 1</td><td>Changer using a Magazine mechanism</td>
  </tr>
  <tr>
    <td>1 1 0</td><td>Reserved</td>
  </tr>
  <tr>
    <td>1 1 1</td><td>Reserved</td>
  </tr>
</tbody></table>

`CDC_CLOSE_TRAY` に関しては両関数でやや異なる。

`pcd_probe_capabilities` 関数では符号付き `char` 型である 6 バイト目を <strong>6 ビット</strong>だけ右シフトした結果が `0` でなければ `ioctl` の結果において `CDC_CLOSE_TRAY` ビットが立つ（関数内ではできない機能にビットを立てる処理であることに注意）。つまり、上表によれば、上位 2 ビットのどちらかが非 `0` である場合である。よくある旧来の光学ドライブは Tray type であろうが、その場合はクローズ不可ということになるのだが。

一方、`get_capabilities` 関数では、符号なし `char` 型である 6 バイト目を <strong>5 ビット</strong>だけ右シフトした結果が `0` でなければ、結果として `CDC_CLOSE_TRAY` ビットが立つ（関数内においては先述の処理と同様）。当該ソースコードのコメントにも記載されているが、Caddy type 以外はクローズ可能という結果になる。

前者の関数の判定はバグのように見えるがどうなのか。ただ、疑問は、これらの処理でクローズ可能性を判断するのは妥当かどうかということである。

## 実験

実際に調べてみると、PIONEER BD-RW BDR-206 1.56 における 当該バイト部分は <code><strong>001</strong>0<strong>1</strong>001</code> (bin) であって、Tray type であり `Eject` ビットが立っており、かつ `ioctl` の結果は `CDC_CLOSE_TRAY` ビット、`CDC_OPEN_TRAY` ビットがともに立っている。後者の実装によるものと思われる。普通のよくあるタイプなので自動開閉可能である。

また、古いノートに付けている MATSHITA UJDA740 DVD/CDRW 1.20 という古い DVD ドライブでも同様に <code><strong>001</strong>0<strong>1</strong>001</code> であるが、Linux が入っていないので `ioctl` がどういう結果になるかはわからない。よくある手動でクローズするタイプである。

# `GET CONFIGURATION`

## 仕様

しかしながら、この `MODE SENSE (10)` コマンドによる機能の取得は [MMC-4](http://www.13thmonkey.org/documentation/SCSI/mmc4r05a.pdf) (Revision 5a / 3 May 2004) では削除されているのである。

そこで有効なのが `GET CONFIGURATION` コマンドである。これもデバイスの機能などを取得するコマンドである。このコマンドは MMC-2 Revision 7.0E で追加された模様であるので、大昔のデバイス相手には使えない。DVD の登場により、`MODE SENSE (10)` による手法では今後の拡張が見込めないための登場と思われる。

`GET CONFIGURATION` コマンドが使えるのであれば、実行結果のうち、`Feature Code` が `0003` (hex) であるものを見ればよい。

### MMC-4, MMC-5

MMC-4 の Table 75 - Feature Codes から抜粋する。

<table><tbody>
  <tr>
    <th>Feature Code</th>
    <th>Feature Name</th>
    <th>Description</th>
  </tr>
  <tr>
    <td>0003h</td>
    <td>Removable Medium</td>
    <td>The medium may be removed from the device</td>
  </tr>
</tbody></table>

さて、MMC-4 の 5.3.4 Removable Medium Feature (0003h) を見ると、4 バイト目に先ほど見たものに似たものがある。Table 84 - Removable Medium Feature Descriptor Format より抜粋する。

<table><tbody>
  <tr>
    <th>Byte | Bit</th>
    <th>7</th>
    <th>6</th>
    <th>5</th>
    <th>4</th>
    <th>3</th>
    <th>2</th>
    <th>1</th>
    <th>0</th>
  </tr>
  <tr>
    <th>2</th>
    <td colspan="2">Reserved</td>
    <td colspan="4"><strong>Version</strong></td>
    <td>Persistent</td>
    <td>Current</td>
  </tr>
  <tr>
    <th>4</th>
    <td colspan="3"><strong>Loading Mechanism Type</strong></td>
    <td>Reserved</td>
    <td><strong>Eject</strong></td>
    <td>Pvnt Jmpr</td>
    <td>Reserved</td>
    <td>Lock</td>
  </tr>
</tbody></table>

2 バイト目の `Version` フィールドは `0000` (bin) とすることになっており、`Eject` ビットについては先ほどと同様の説明である。
また、Table 85 - Loading Mechanism Type を載せるが、書き方の違いはあれど先述のものと同様の内容である。

<table><tbody>
  <tr>
    <th>Loading Mechanism Type</th><th>Description</th>
  </tr>
  <tr>
    <td>000b</td><td>Caddy/Slot type loading mechanism</td>
  </tr>
  <tr>
    <td>001b</td><td>Tray type loading mechanism</td>
  </tr>
  <tr>
    <td>010b</td><td>Pop-up type loading mechanism</td>
  </tr>
  <tr>
    <td>011b</td><td>Reserved</td>
  </tr>
  <tr>
    <td>100b</td><td>Embedded changer with individually changeable discs</td>
  </tr>
  <tr>
    <td>101b</td><td>Embedded changer using a magazine mechanism</td>
  </tr>
  <tr>
    <td>110b - 111b</td><td>Reserved</td>
  </tr>
</tbody></table>

これらは [MMC-5](http://www.13thmonkey.org/documentation/SCSI/mmc5r04.pdf) (Revision 4 / 24 October 2006) においても同じである。

### MMC-6

では、[MMC-6](http://www.13thmonkey.org/documentation/SCSI/mmc6r02g.pdf) (Revision 2g / 11 December 2009) を見てみると、5.3.4 Removable Medium Feature (0003h) にある Table 98 - Removable Medium Feature Descriptor Format に変更点がある。

<table><tbody>
  <tr>
    <th>Byte | Bit</th>
    <th>7</th>
    <th>6</th>
    <th>5</th>
    <th>4</th>
    <th>3</th>
    <th>2</th>
    <th>1</th>
    <th>0</th>
  </tr>
  <tr>
    <th>2</th>
    <td colspan="2">Reserved</td>
    <td colspan="4"><strong><em>Version = 0010b</em></strong></td>
    <td>Persistent</td>
    <td>Current</td>
  </tr>
  <tr>
    <th>4</th>
    <td colspan="3"><strong>Loading Mechanism Type</strong></td>
    <td><strong><em>Load</em></strong></td>
    <td><strong>Eject</strong></td>
    <td>Pvnt Jmpr</td>
    <td><em>DBML</em></td>
    <td>Lock</td>
  </tr>
</tbody></table>

`Version` が `0010` (bin) になり、さらに 4 バイト目の下から 4 ビット目に `Load` ビットが追加されている。また、ここでは深掘りしないが、`DBML` ビットというものもある。説明によると、`Load` ビットが立っていれば `START STOP UNIT` コマンドの `LoEj` ビット経由でロードつまりクローズ可能であることが期待できるというわけである。`Eject` ビットと対をなすものが現れたのだ。

## 実験

PIONEER BD-RW BDR-206 1.56 の場合、`Version` は <strong><code>0001</code></strong> であり、さらに `Load` ビットが立っている。同様に Tray type であり、`Eject` ビットを見ればオープン可能とのことである。また、MATSHITA UJDA740 DVD/CDRW 1.20 では、`Version` は `0000` で `Load` ビット部分（このバージョンにおいては `Reserved`）は `0` である。これまた同様に Tray type であり、オープン可能とのことである。

## 疑問

前者のドライブにおける `Version` が <strong><code>0001</code></strong> であるとは何事か。

# Mt. Fuji

Mt. Fuji Commands for Multimedia Devices という、MMC のような何かがある。

- ftp://ftp.avc-pioneer.com/

詳しいことはわからないが、どうやら MMC とは切っても切り離せない関係らしい。

## Version 6

[Mt. Fuji Commands for Multimedia Devices Version 6 (Revision 1.0 / Final /
February 8, 2006)](ftp://ftp.avc-pioneer.com/Mtfuji_6/Spec/Fuji6r100.pdf) を見てみる。これにおける 17.4.2.4 Feature 0003h: Removable Medium を見ると、当該 descriptor 部分は MMC-5 と同様の構成である。

## Version 7

### Revision 0.6

[Mt. Fuji Commands for Multimedia Devices Version 7 (Revision 0.6 / Draft for Review / August 1、2006)](ftp://ftp.avc-pioneer.com/Mtfuji_7/Spec/Fuji7r06.zip) を見てみる。公開されている最初のリビジョンであろうが、特に変化がない。

### Revision 0.7

次のリビジョンであろう、[Mt. Fuji Commands for Multimedia Devices Version 7 (Revision 0.7 / Draft for Review / May 7, 2007)](ftp://ftp.avc-pioneer.com/Mtfuji_7/Spec/Fuji7r07.zip) の 17.4.2.4 Feature 0003h: Removable Medium を見る。

Table 324 - Removable Medium Feature Descriptor より抜粋してみる。

<table><tbody>
  <tr>
    <th>Byte | Bit</th>
    <th>7</th>
    <th>6</th>
    <th>5</th>
    <th>4</th>
    <th>3</th>
    <th>2</th>
    <th>1</th>
    <th>0</th>
  </tr>
  <tr>
    <th>2</th>
    <td colspan="2">Reserved</td>
    <td colspan="4"><strong><em>Version = 1h</em></strong></td>
    <td>Persistent</td>
    <td>Current</td>
  </tr>
  <tr>
    <th>4</th>
    <td colspan="3"><strong>Loading Mechanism Type</strong></td>
    <td><strong><em>Load</em></strong></td>
    <td><strong>Eject<strong></td>
    <td>Pvnt Jmpr</td>
    <td>Reserved</td>
    <td>Lock</td>
  </tr>
</tbody></table>

ここで `Load` ビットが追加されるとともに、`Version` が `1` となったのだ。`Load` ビットについては MMC-6 と同様の説明である。

### Revision 1.20

やや飛ばして、[Mt. Fuji Commands for Multimedia Devices Version 7 (Revision 1.20 / Final / September 24, 2009)](ftp://ftp.avc-pioneer.com/Mtfuji_7/Spec/FUJI7r120_final.pdf) を見る。17.4.2.4 Feature 0003h: Removable Medium において変更点がある。`DBML` ビットが追加され、`Version` が `2` となった。

Table 350 - Removable Medium Feature Descriptor より抜粋してみる。

<table><tbody>
  <tr>
    <th>Byte | Bit</th>
    <th>7</th>
    <th>6</th>
    <th>5</th>
    <th>4</th>
    <th>3</th>
    <th>2</th>
    <th>1</th>
    <th>0</th>
  </tr>
  <tr>
    <th>2</th>
    <td colspan="2">Reserved</td>
    <td colspan="4"><strong><em>Version = 2h</em></strong></td>
    <td>Persistent</td>
    <td>Current</td>
  </tr>
  <tr>
    <th>4</th>
    <td colspan="3"><strong>Loading Mechanism Type</strong></td>
    <td><strong>Load</strong></td>
    <td><strong>Eject</strong></td>
    <td>Pvnt Jmpr</td>
    <td><em>DBML</em></td>
    <td>Lock</td>
  </tr>
</tbody></table>

ここで MMC-6 同様の構成になっており、現時点で最新の [Mt. Fuji Commands for Multimedia Devices Version 9 (Revision 1.00 / Final / October 13, 2015)](ftp://ftp.avc-pioneer.com/Mtfuji_9/Spec/FUJI9R100.pdf) まで変わりはない。

## 考察
以上のことから、謎の `Version = 1` とは、Mt. Fuji Commands for Multimedia Devices Version 7 完成過程で登場したバージョンであり、MMC-6 においては Revision 2g が制定されるまでのどこかのリビジョンに存在しているかもしれないものであろうと考えられる。

# まとめ

## Removable Medium Feature Descriptor 年表

以上の出来事を時系列順に並べてみる。

<table><tbody>
  <tr>
    <th>年月日</th>
    <th>仕様, リビジョン</th>
    <th><code>Version</code> フィールド (dec)</th>
    <th>機能</th>
  </tr>
  <tr>
    <td>1997/5/6</td>
    <td>MMC-1, 10A</td>
    <td>N/A</td>
    <td>N/A</td>
  </tr>
  <tr>
    <td>1999/8/30</td>
    <td>MMC-2, 11a</td>
    <td>0</td>
    <td>-</td>
  </tr>
  <tr>
    <td>2001/11/12</td>
    <td>MMC-3, 10g</td>
    <td>0</td>
    <td>-</td>
  </tr>
  <tr>
    <td>2004/5/3</td>
    <td>MMC-4, 5a</td>
    <td>0</td>
    <td>-</td>
  </tr>
  <tr>
  <tr>
    <td>2006/8/1</td>
    <td>Mt. Fuji 7, 0.6</td>
    <td>0</td>
    <td>-</td>
  </tr
  <tr>
    <td>2006/10/24</td>
    <td>MMC-5, 4</td>
    <td>0</td>
    <td>-</td>
  </tr>
  <tr>
    <td>2007/5/7</td>
    <td>Mt. Fuji 7, 0.7</td>
    <td>1</td>
    <td>Load</td>
  </tr>
  <tr>
    <td>2009/9/24</td>
    <td>Mt. Fuji 7, 1.20</td>
    <td>2</td>
    <td>Load, DBML</td>
  </tr>
  <tr>
    <td>2009/12/11</td>
    <td>MMC-6, 2g</td>
    <td>2</td>
    <td>Load, DBML</td>
  </tr>
  <tr>
    <td>2015/10/13</td>
    <td>Mt. Fuji 9, 1.00</td>
    <td>2</td>
    <td>Load, DBML</td>
  </tr>
</tbody></table>

## 問題点と提案

これまでの調査から、次のことがいえよう。

1. 廃止された `MODE SENSE (10)` のみに頼る現状の Linux の実装は古い。
2. さらにクローズ可能性については信憑性に欠く。
3. **古すぎないデバイス相手では** `GET CONFIGURATION` で代用できそうである。
4. それを用いて Removable Medium Feature を取得すれば、`Eject` ビットを利用できる。
5. 同様に、`Load` ビットが利用可能な**場合がある**。
6. それは `Version` が `1` 以上（現状では `1` あるいは `2`）の場合である。（後に `3` などになっても後方互換性を期待してもよさそう）

このことから、光学ドライブの自動開閉機能の有無を調べるコードを改めて書く際には、次のような手法を提案したい。

1. `GET CONFIGURATION` で `Feature Code = 0003h` を取得する。
2. `Eject` ビットが `0` であればオープン不可能、`1` であればオープン可能とする。
3. `Version` が `0` であれば、`Loading Mechanism Type` によって推測する。（Linux の実装を参考に）
4. `Version`が `1` 以上（`1` あるいは `2`）であれば、`Load` ビットを調べ、`0` であればクローズ不可能、`1` であればクローズ可能とする。
5. 対象のデバイスが古すぎるなどのため、`GET CONFIGURATION` 失敗の場合は、必要に応じて `MODE SENSE (10)` の `Page Code = 2Ah` による手法に切り替え、`Loading Mechanism Type` により推測、および `Eject` ビットの確認を行う。（あるいはそれを使用している従来の Linux の `ioctl`）