3.17 SDカードドライバを移植
3.17.1 カーネルにSDデバイスドライバを登録
Linux-2.6.32.2は既にS3C2440チップのSDカードドライバがあり、初期化コードにSDプラットフォームデバイス構造を追加するだけ、arch/arm/mach-s3c2440/mach-mini2440.cをオープンし、nand flashプラットフォーム構造に、次の赤いコードを追加する。
SDカード構造デバイスをターゲットプラットフォームデバイスセットに追加され、下図を参照する
SDカードのドライバプログラム最下層操作は実際にソースコードlinux-2.6.32.2/drivers/mmc/host/s3cmci.cを対応し、テストによると、カーネルプリントアウト情報を含まれる場合、SDカードは正常に認識されて使える。プリントアウト情報がない場合、不安定になり、該当プログラムに遅延コードを追加する、図を参照する
SDカードの移植は完了。
3.17.2 SDカードをテスト
上記の手順に続き、カーネルソースコードディレクトリにmake zImageを実行し、生成されたカーネルは開発ボードにプログラミングし、まず、SDカードを差し込まず(差し込む時のプリントアウト情報を確認するため)、システムが起動してから、コマンドラインコンソールに入り、この時SDカードを差し込むと、次の情報を見える
開発ボードの/sdcardディレクトリは自動的にマウントされ、Qtopiaシステムにタスクバーでアイコンを確認できる、下図を参照する
SDカードまたUSBメモリードライブにあるすべてファイルは文書グループにすべて表示され、しかし、ディレクトリ名前を表示しない、ファイルは多すぎると、リスト数はかなり多くとなる
説明:Qtopia 2.2.0プラグインを通じてSDカードまたUSBメモリードライブ自動マウントをサポートする、MMC/SDカードまたはUSBメモリードライブの第一のパーティションとして認識され、またフォーマットはVFAT/FAT32/FAT16で、USBメモリードライブやSDカードは認識できない場合、VFAT/FAT32/FAT16フォーマットを確認ください
3.17.3 mini2440のSDカードドライバ分析
参考資料
1.SD Memory Card Specifications / Part 1. Physical Layer Specification; Version 1.0
2.LDD3 CHAPTER-16 BLOCK DEVICE
3. http://www.sdcard.org
1. ハードウェア基礎
SD(セキュリティデジタル)とMMC(マルチメディアカード)
SDは、一般に、共通のSDメモリカードであり、MMCは、メモリカード規格の以前の一種であり、現在SD規格によって置換されているフラッシュメモリカード規格である。
SDIO(セキュリティ•デジタルI / O)
SDIOは、SDIOの名前通り、SD I / Oインタフェースの意味、
これは少し抽象的な解釈かもしれない。具体的に説明するように、SDメモリーカードの規格はもともとあったが、今ではSDにいくつかのペリフェラル•インターフェースを挿入するために使用することができ、そのような技術がSDIO。
SDIO自体は外部の周辺機器を接続するためのSD I / Oピンを介して、非常に単純な技術であり、SD I / Oを経由で外部と接続し、それはまた、SD上のI/Oが外部とデータを転送します。そして、SDアソシエーションのメンバーは完全なSDIOスタックドライバを出し、SDIO周辺機器(我々はSDIOカードと呼ばれる)の開発と応用は非常に普及している。
そこに携帯電話やハンドヘルドデバイスからサポートするSDIO機能(SD標準は、もともとモバイル機器に対して開発されたもの)が多くなり、そして、数多くのSDIO周辺機器も開発されている、携帯に外部周辺機器をより簡単に接続され、開発上にも拡張性がある。(内蔵ペリフェラル不要)。
現在の一般的なSDIO周辺機器(SDIOカード)次のとおり。
Wi-Fiカード(無線ネットワークカード)
CMOSセンサーカード(カメラモジュール)
GPSカード
GSM / GPRSモデムカード
Bluetoothカード
ラジオ/ TVカード
SDIOアプリケーションは、組込みシステムの将来になる最も重要なインターフェース技術の一つであり、現在のGPIOタイプSPIインタフェースに置き換えられる。
SD / SDIO転送モードは、次の三種類がある。
SPIモード(必須)
1ビットモード
4ビットモード
SDのMMCモード
SDはまたMMCメモリを読むことができ、MMC規格に述べた通り、MMCメモリは必ずしもSPIモード(ただし、1ビットモードをサポートするようにしてください)をサポートしたいのですが、市販のMMCカードは殆どSPIモードをサポートする。そこで、MMCメモリカードを読み取るためにSDカードをSPIモードに設定する。
SDのMMCモードがMMCカードを読み取るために使用されるもの、SDのSPIモードはMMCモードを使用されるが、物理特性は異なる。
MMC SPIモードの最大伝送速度:20メガビット/秒
SD SPIモードの最大伝送速度:25メガビット/秒
混乱を避けるため、SPI/MMCモードとSPI/SDモードを分けて書く。
2. MMCサプシステムの基本フレームワーク仕組み
残念ながら、カーネルは私たちにMMCサプシステムに関するドキュメントを提供されなく、googleに検索しても、関連の文章を見つけないから、自分でコード分析を勉強する。MMCサプシステムのコードはkernel/driver/MMCのもとにあり、現在のMMCサプシステムはあるフォーマットのメモリカード:SD、SDIO、MMCをサポートします。MMCサプシステムは三つ部分に分けられる
HOST部分は異なるホストに対するプログラムで、ドライバプログラムエンジニアがプラットフォームの特性よりこの部分を完成する
CORE部分は全体MMCのコアメモリで、異なるプロトコルと規格の実現を完成し、HOST層のドライバにインタフェース関数を提供する
CARD部分:これらのメモリカードは、ブロック•デバイスであるから、SDカードはどのようにブロック•デバイスになるかを実現する
3.HOST層分析
HOST層は特定のホストに対するドライバプログラムを実現し、ここで、mini2440のs3cmci.cを例として分析する。platform_driver_register(&s3cmci_2440_driver)を使用し、プラットフォームデバイスを登録し、次にprobe関数をメイン説明する。この関数に、COREとの関係は次の三つことを通じて実現する、まず、mmc_host構造を割り当てられ(sizeof(struct s3cmci_host)にご注意)、このようにmmc_hostからs3cmci_hostを見つけられる、組み込みと組み込まれる構造は相手を見つけると言う技術はLinuxカーネルによく使われる。これから、MMC-> posに値を与える、s3cmci_ops構造はいくつかの重要な関数を実現し、後は説明する、mmc構造の多くのメンバーに値を与える、最後にmmc構造はMMCサブシステムに追加し、mmc_alloc_hostと mmc_add_hostの具体てきな役割は次の章節に説明する、この三つのコマンドはMMCドライバ層を書くには必要である
mmc = mmc_alloc_host(sizeof(struct s3cmci_host)、 &pdev->dev);
mmc->ops = &s3cmci_ops;
……………
s3cmci_opsに四つ関数を含める:
static struct mmc_host_ops s3cmci_ops = {
.request = s3cmci_request、
.set_ios = s3cmci_set_ios、
.get_ro = s3cmci_get_ro、
.get_cd = s3cmci_card_present、
};
簡単のものから分析し、これらの関数はcore部分に呼び出される
s3cmci_get_ro:この関数は GPIOから読み取ってカードは保護されるかどうか判断する
s3cmci_card_present:この関数は GPIOから読み取ってカードが存在するかどうか判断する
s3cmci_set_ios:s3cmci_set_ios(struct mmc_host *mmc、 struct mmc_ios *ios)
コア層から受信したiosよりハードウェアのIOを設定し、ピン・コンフィグ、クロック・有効、バス帯域幅設定などを含まれる。
s3cmci_request:この関数は最も重要で複雑な関数で、コマンドとデータの送信と受信を実現し、CORE部分はコマンドやデータを送信する時、この関数を呼び出し、mrqリクエストを伝送する
static void s3cmci_request(struct mmc_host *mmc、 struct mmc_request *mrq)
{
struct s3cmci_host *host = mmc_priv(mmc);
host->status = "mmc request";
host->cmd_is_stop = 0;
host->mrq = mrq;
if (s3cmci_card_present(mmc) == 0) {
dbg(host、 dbg_err、 "%s: no medium present\n"、 __func__);
host->mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc、 mrq);//カードがない場合、リクエストを終了する
} else
s3cmci_send_request(mmc);
}
次にはs3cmci_send_request(mmc)を見て:
この関数はリクエストの時、データを伝送するか、またはコマンドを伝送するかと判断し、データの場合
s3cmci_setup_dataを呼び出し、S3C2410_SDIDCONレジスタを設定し、その後、SDITIMERレジスタを設定し、このようにバス幅を設定した、DMAを使用するかどうか、データ伝送モードを起動するかどうか、次の割り込みを有効になる:
imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
DMAでデータを送信するか、またはFIFOでデータを送信するかと判断する
if (host->dodma)
/ because host->dodma = 0、so we don't use it
res = s3cmci_prepare_dma(host、 cmd->data);//DMA送信を準備する、
else
res = s3cmci_prepare_pio(host、 cmd->data);.//FIFO送信を準備する
コマンドの場合:s3cmci_send_command()というコマンド送信関数を呼び出し、datesheetに説明するプロセスは大体同じで、SD規格のコマンドフォーマットについて参考資料1を参照する
writel(cmd->arg、 host->base + S3C2410_SDICMDARG);/*先にパラメータレジスタを書き込む
ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;//コマンド種類を設定
ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
/*with start 2bits*/
if (cmd->flags & MMC_RSP_PRESENT)
ccon |= S3C2410_SDICMDCON_WAITRSP;
/*wait rsp*/
if (cmd->flags & MMC_RSP_136)
ccon |= S3C2410_SDICMDCON_LONGRSP;
//resposeのタイプを確認
writel(ccon、 host->base + S3C2410_SDICMDCON);
コマンドチャンネルを分析してから、データチャンネルを分析し、まずFIFO方式でどのように伝送を実現するかを分析する
s3cmci_prepare_pio(host、 cmd->data)を分析し
rwより読み取り/書き込みを判断し
if (rw) {
do_pio_write(host);
/* Determines SDI generate an interrupt if Tx FIFO fills half*/
enable_imask(host、 S3C2410_SDIIMSK_TXFIFOHALF);
} else {
enable_imask(host、 S3C2410_SDIIMSK_RXFIFOHALF
| S3C2410_SDIIMSK_RXFIFOLAST);
}
データはSDに書き込まれる場合、do_pio_writeを呼び出し、FIFOにデータを充填し、64ビットのFIFOは33ビットより少ない場合、割り込みが生じる、SDからデータを読み取る場合、まず割り込みを有効にする、FIFOは31ビットより多くになる場合、割り込みサービスプログラムを呼び出し、割り込みサービスプログラムはdo_pio_read FIFOのデータを呼び出し、読み取る
次にdo_pio_writeを分析し
to_ptr = host->base + host->sdidata;
fifo_free(host)は fifoの残り容量を検出するために使用される
while ((fifo = fifo_free(host)) > 3) {
if (!host->pio_bytes) {
res = get_data_buffer(host、 &host->pio_bytes、
/* If we have reached the end of the block、 we have to
* write exactly the remaining number of bytes. If we
* in the middle of the block、 we have to write full
* words、 so round down to an even multiple of 4. */
if (fifo >= host->pio_bytes)//fifoの容量は pio_bytesより大きい場合、最後にこのブロックを読み取ることを明らかにする
fifo = host->pio_bytes;
/* because the volume of FIFO can contain the remaning block*/
else
fifo -= fifo & 3;/*round down to an even multiple of 4*/
host->pio_bytes -= fifo;//余った文字を更新する
host->pio_count += fifo;/*chang the value of pio_bytes*/
fifo = (fifo + 3) >> 2;//ビット数は文字数に変換される
/*how many words fifo contain、every time we just writ one word*/
ptr = host->pio_ptr;
while (fifo--)
writel(*ptr++、 to_ptr);// FIFOへ書き込む.
host->pio_ptr = ptr;
}
注釈一:MMCコアはmrq->dataメンバーにstruct scatterlist表を割り当てられ、スキャッター•ギャザーをサポートするために使用され、この方法を通じて、物理における一致しないメモリページは連続的な配列に組み立てられる、大きなバッファを割り当てることを避ける
コードを見て
if (host->pio_sgptr >= host->mrq->data->sg_len) {
dbg(host、 dbg_debug、 "no more buffers (%i/%i)\n"、
host->pio_sgptr、 host->mrq->data->sg_len);
return -EBUSY;
}
sg = &host->mrq->data->sg[host->pio_sgptr];
*bytes = sg->length;// ページバッファの長さ
*pointer = sg_virt(sg); ページアドレスは仮想アドレスにマッピングされる
host->pio_sgptr++;ここで、プログラムは一回マッピングを完了することを現れる
このようにmmcリクエスト毎に対してscatterlist表の一つページ(ブロック)を処理するだけ、フルリクエストを完了するのはsg_lenをマッピングする必要がある
Mmc書きデバイスのリクエストプロセスをまとめて
s3cmci_prepare_pioの中、始めにdo_pio_writeを呼び出し、FIFO空間は3より大きくなって、また、scatterlistを取得できる場合、FIFOへデータを書き始め、FIFO容量は3より小さくなる場合、TXFIFOHALF割り込み・オンする、サービスプログラムを割り込みする時、TFDETを検出すると、FIFOでスペースがあると表示し、この時、TXFIFOHALF割り込みをオフし、do_pio_writeを呼び出し、書き込む
データの流れは次の通りであり:scatterlist-------->fifo---------->sdcard
Mmc読み取りデバイスのリクエストのプロセスデータの流れは次の通りであり:sdcard --------> fifo ---------->scatterlist、データの読み取りプロセスと割り込みのトリガについて、s3cmci_prepare_pioの中のenable_imask(host、
S3C2410_SDIIMSK_RXFIFOHALF、S3C2410_SDIIMSK_RXFIFOLAST); SDカードからデータを読み取らない場合、割り込みを引き起こす、S3C2410_SDIIMSK_RXFIFOLASTから引き起こすプロセスは
次に割り込みサービスプログラムを分析し
static irqreturn_t s3cmci_irq(int irq、 void *dev_id)
該当プログラムはすべてステータスレジスタを取得する
mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
mci_dsta = readl(host->base + S3C2410_SDIDSTA);
mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
mci_fsta = readl(host->base + S3C2410_SDIFSTA);
mci_imsk = readl(host->base + host->sdiimsk);
これらは割り込み処理の根拠とする
DMAモードではない場合、データの受送信を処理する
if (!host->dodma) {
if ((host->pio_active == XFER_WRITE) &&
(mci_fsta & S3C2410_SDIFSTA_TFDET)) {
/*This bit indicates that FIFO data is available for transmit when
DatMode is data transmit mode. If DMA mode is enable、 sd
host requests DMA operation.*/
disable_imask(host、 S3C2410_SDIIMSK_TXFIFOHALF);
tasklet_schedule(&host->pio_tasklet);
注: taskletの遅延機能を使用し、割り込みサービス時間を短縮する、遅延関数pio_taskletにdo_pio_writeとdo_pio_readを呼び出す
host->status = "pio tx";
}
if ((host->pio_active == XFER_READ) &&
(mci_fsta & S3C2410_SDIFSTA_RFDET)) {
disable_imask(host、
S3C2410_SDIIMSK_RXFIFOHALF |
S3C2410_SDIIMSK_RXFIFOLAST);
tasklet_schedule(&host->pio_tasklet);
host->status = "pio rx";
}
次の多くkのコードは他のタイプの割り込みに対する処理である
最後にDMAモードを分析し、このモードでCPUの介入が必要ではない、S3C2440の DMAは四つチャンネルがあり、チャンネル0を選択する
static int s3cmci_prepare_dma(struct s3cmci_host *host、 struct mmc_data *data)
{
int dma_len、 i;
int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
s3cmci_dma_setup(host、 rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);//注一
s3c2410_dma_ctrl(host->dma、 S3C2410_DMAOP_FLUSH);
dma_len = dma_map_sg(mmc_dev(host->mmc)、 data->sg、 data->sg_len、
(rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);//注二
if (dma_len == 0)
return -ENOMEM;
host->dma_complete = 0;
host->dmatogo = dma_len;
for (i = 0; i < dma_len; i++) {
int res;
dbg(host、 dbg_dma、 "enqueue %i:%u@%u\n"、 i、
sg_dma_address(&data->sg)、
sg_dma_len(&data->sg));
res = s3c2410_dma_enqueue(host->dma、 (void *) host、
sg_dma_address(&data->sg)、
sg_dma_len(&data->sg));
if (res) {
s3c2410_dma_ctrl(host->dma、 S3C2410_DMAOP_FLUSH);
return -EBUSY;
}
}
s3c2410_dma_ctrl(host->dma、 S3C2410_DMAOP_START);
return 0;
}
注一:この関数はまずs3c2410_dma_devconfigを呼び出し、DMA送信元/ターゲットのタイプとアドレスを設定する。メモ:ここのデバイスhost->mem->start + host->sdidataは実はSDIDATAレジスタのアドレス値であり、SDカードを書き込むと、ターゲットアドレスであり、ではなければ、ソースアドレスである、これからs3c2410_dma_set_buffdone_fn(host->dma、 s3cmci_dma_done_callback)を呼び出す
DMAチャンネル0のコールバック関数を設定する
注二:
dma_len = dma_map_sg(mmc_dev(host->mmc)、 data->sg、 data->sg_len、
(rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
ここで、スキャッタ/ギャザーマッピング(P444、LDD3)を行い、戻り値は伝送されるDMAバッファの数であり、sg_lenより小さいな場合はある、即ちsg_len とdma_lenは違うのである
sg_dma_address(&data->sg)、戻り値はバス(DMA)アドレス
sg_dma_len(&data->sg)); 戻り値はバッファの長さである。
最後に呼び出すのはs3c2410_dma_enqueue(host->dma、 (void *) host、
sg_dma_address(&data->sg)、
sg_dma_len(&data->sg));
各DMAバッファに行列に并んで、処理を待つ
s3c2410_dma_ctrl(host->dma、 S3C2410_DMAOP_START); DMAを起動する
これによってDMAバッファはscatterlistと繋がる。データを書き込む時、scatterlistのデータは上記のマッピング関係のため、DMAバッファに直接にコピーされる、データを読み取る時、これとは逆に。すべて処理はCPUの介入が必要ではない、自動的に完成する
4. CORE層分析
CORE層は異なるプロトコルと規格を実現させ、そして、HOST層のドライバにインタフェース関数を提供する、HOST層に呼び出された二つ関数は次の通り
mmc_alloc_host(sizeof(struct s3cmci_host)、 &pdev->dev);
mmc_add_host(mmc);
この二つ関数から始め、CORE層とHOST層はどのようにお互いににやり取りすることを分析する
まず、mmc_alloc_host関数を見て
dev_set_name(&host->class_dev、 "mmc%d"、 host->index);
host->parent = dev;
host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
上記関数は/SYS/CLASS/mmc_host下にmmc0ディレクトリを生成する、クラス・デバイスを追加し、2.6.21以降のバージョンにクラス・デバイスのclass_deviceはdeviceに取り替えられ、LDD3P387の内容は古い。
INIT_DELAYED_WORK(&host->detect、 mmc_rescan);
動作キューを初期化、遅延関数はmmc_rescanで、最後にhostに対してデフォルト設定を行う、probe関数の後で再設定する
mmc_add_host(mmc)を分析する;
device_add(&host->class_dev);クラス・デバイスを追加する。
その中でmmc_start_hostを呼び出す
void mmc_start_host(struct mmc_host *host)
{
mmc_power_off(host);
mmc_detect_change(host、 0);
}
mmc_power_offに iosに対して設定を行い、その後mmc_set_ios(host)を呼び出す;
host->ios.power_mode = MMC_POWER_OFF;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host);
mmc_set_ios(host)のキ―関数host->ops->set_ios(host、 ios);set_iosは前のset_ios = s3cmci_set_iosである
次にmmc_detect_change(host、 0)を見て;最後関数
mmc_schedule_delayed_work(&host->detect、 delay)は;
前の遅延関数mmc_rescanを呼び出す
mmc_power_up(host);//この関数は前のmmc_power_offと類似、起動時必要なiosを設定する
mmc_go_idle(host);
//CMD0 、from inactive to idle
mmc_send_if_cond(host、 host->ocr_avail);// SD_SEND_IF_CONDを送信し、SD2.0カードを使用する時、設定コマンド
/*suppot for 2.0 card*/
* ...then normal SD...
*/
err = mmc_send_app_op_cond(host、 0、 &ocr);
if (!err) {
if (mmc_attach_sd(host、 ocr))
mmc_power_off(host);
goto out;
}
上記は、SDカードプロトコルのSDカード起動流れに従い、非アクティブモード、カード認識モードとデータ伝送モードの三つモードで合計9つの状態変換を含む。関連規格を参照し、次の三章図を参照し、モードと状態、状態変換を理解する
SDカードの初期状態はinactive状態でmmc_go_idle(host)を呼び出してからコマンドCMD0を送信し、IDLE状態にさせるため
mmc_go_idleを分析し
memset(&cmd、 0、 sizeof(struct mmc_command));
cmd.opcode = MMC_GO_IDLE_STATE; //MMC_GO_IDLE_STATEは CMD0コマンドである
cmd.arg = 0;//このコマンドはパラメ—タ—がない
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
err = mmc_wait_for_cmd(host、 &cmd、 0);//注1を参照
mmc_delay(1);
注 1:mmc_wait_for_cmd(host、 &cmd、 0)はコマンド送信用。
memset(&mrq、 0、 sizeof(struct mmc_request));
memset(cmd->resp、 0、 sizeof(cmd->resp));
cmd->retries = retries;
mrq.cmd = cmd;コマンドはmmcリクエストに組み込まれる
cmd->data = NULL;mmcコマンドの data部分は NULLに設定され、伝送するのはコマンドであり、データではないことを現れる
mmc_wait_for_req(host、 &mrq);// 肝心な部分
該当関数にはmmc_start_requestを呼び出し、この関数はhost->ops->request(host、 mrq)を呼び出し、このrequest関数はこの前の部分に分析するs3cmci_requestである。次のことを見て
err = mmc_send_app_op_cond(host、 0、 &ocr);//注一
if (!err) {
if (mmc_attach_sd(host、 ocr))//注二
mmc_power_off(host);
goto out;
注一:実際にはACMD41コマンドを送信し、このコマンドはSDcardの許可する電圧範囲値を取得するに使用され、これは応用コマンドのため、送信する前にCMD_55コマンドを送信する必要がり、実行してからcard状態は READYが取得した電圧範囲値になって、ocrに保存される、mmc_attach_sd(host、 ocr)を呼び出し、この電圧範囲はホストの要求を確認し、満たすことできない場合、power_offホストである
注二:mmc_attach_sdはマッチ、カード初期化機能を実現する
host->ocr = mmc_select_voltage(host、 ocr);マッチかどうかを確認し、マッチの場合、次の初期化操作を行い
mmc_sd_init_card(host、 host->ocr、 NULL);この関数を分析し
(1)mmc_all_send_cid()この関数はCMD2を発生し、カードのID情報を取得し、ID状態に入る
(2)card = mmc_alloc_card(host、 &sd_type);一枚SDタイプの card構造を割り当てられる
(3)mmc_send_relative_addを呼び出し、カード対応アドレスを取得し、注:前のカードとホスト通信はデフォルトアドレスを使用し、stand_by状態に入る
(4)SEND_CSD (CMD9)の送信を通じて CSDレジスタの情報を取得し、 block長さ、カード容量を含まれる
(5) mmc_select_card(card)は CMD7を送信し、現在の RADDアドレスのカードを選択し、どんな時にバスにただ一枚カードは選択される、伝送状態に入る、
(6)mmc_app_send_scrを呼び出し、コマンドACMD51を送信し、SRCレジスタの内容を取得し、SENDING-DATA 状態に入る
関数に取得した各カードレジスタの内容をディコードし、cmd構造の該当のメンバーに保存される。
(7)if (host->ops->get_ro(host) > 0)
mmc_card_set_readonly(card);
get_ro(host)関数を呼び出すのを通じて、実際にはs3cmci_get_ro関数である、書き込み保護を判断し、card状態は読み取り専用ステータス状態を設定される
最後にmmc_attach_sdにcard構造を追加する
mmc_add_card(host->card);
dev_set_name(&card->dev、 "%s:%04x"、 mmc_hostname(card->host)、 card->rca);ここで、host名前+rcaアドレスで、カードを名づける、/sys/devices/platform/s3c2440-sdi/mmc_host:mmc0/の中に mmc0:0002ディレクトリ、0002は rcaアドレスである
5. CARD層分析
これらのメモリカードは、ブロック•デバイスであるため、ブロック•デバイスのドライバプログラムを提供するで、この部分はSDカードはどのようにブロック•デバイスを実現するかのもので、まず、block.Cのprobe関数を見て
MMCブロック•デバイスは次の構造で表示される
struct mmc_blk_data {
spinlock_t lock;
struct gendisk *disk;
struct mmc_queue queue;
unsigned int usage;
unsigned int read_only;
};
先まずmmc_blk_alloc( )を見て
devidx = find_first_zero_bit(dev_use、 MMC_NUM_MINORS);
if (devidx >= MMC_NUM_MINORS)// mmc層は一番多くの16個カードをサポートするを現れる、card毎には8パーティションを占める
return ERR_PTR(-ENOSPC);
__set_bit(devidx、 dev_use);
md->disk = alloc_disk(1 << MMC_SHIFT);// 一つディスク、8つパーティションを割り当てる
//8 partion
if (md->disk == NULL) {
ret = -ENOMEM;
goto err_kfree;
}
spin_lock_init(&md->lock);
md->usage = 1;
ret = mmc_init_queue(&md->queue、 card、 &md->lock);//注一
if (ret)
goto err_putdisk;
md->queue.issue_fn = mmc_blk_issue_rq;//
md->queue.data = md;
md->disk->major = MMC_BLOCK_MAJOR;
md->disk->first_minor = devidx << MMC_SHIFT;
md->disk->fops = &mmc_bdops; ディスクの操作関数
md->disk->private_data = md;
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = &card->dev;
/*
* As discussed on lkml、 GENHD_FL_REMOVABLE should:
*
* - be set for removable media with permanent block devices
* - be unset for removable block devices with permanent media
*
* Since MMC block devices clearly fall under the second
* case、 we do not set GENHD_FL_REMOVABLE. Userspace
* should use the block device creation/destruction hotplug
* messages to tell when the card is present.
*/
sprintf(md->disk->disk_name、 "mmcblk%d"、 devidx);//名前は/proc/device の中に出る
/sys/block の中に"mmcblk0がある
blk_queue_hardsect_size(md->queue.queue、 512);// ハードウェアセクタの容量を設定する
}
注一:
mq->queue = blk_init_queue(mmc_request、 lock);初期化、request関数はキューとバインドされる
if (!mq->queue)
return -ENOMEM;
mq->queue->queuedata = mq;
mq->req = NULL;
blk_queue_prep_rq(mq->queue、 mmc_prep_request);
//コマンドの前処理、ドライバプログラムはevl_next_requestに戻る前に、チェックと前処理リクエストのメカニズムを提供し、LDD3 P485を参照する
//command prepare process
blk_queue_ordered(mq->queue、 QUEUE_ORDERED_DRAIN、 NULL);//
//barrier requestバリアリクエスト、再度組み合わせることより発生するエラーを防ぐ、設定は標準に達してからリクエストデータはタイムリーにメディアに書き込まれることを保証する。
mq->sg = kmalloc(sizeof(struct scatterlist) *
host->max_phys_segs、 GFP_KERNEL);
if (!mq->sg) {
ret = -ENOMEM;
goto cleanup_queue;
}
sg_init_table(mq->sg、 host->max_phys_segs);
}
//scatterlist構造体を割り当てる
mq->thread = kthread_run(mmc_queue_thread、 mq、 "mmcqd");最後にカーネルスレッドを設定し、スレッドの関連関数はmmc_queue_threadで、これは重要で後程分析する。
これから mmc_blk_set_blksizeを呼び出し、blockの長さは512に設定される。
すべて準備してからディスクadd_disk(md->disk) をアクティベーションする;
最後にrequest関数を分析する:
*
* Generic MMC request handler. This is called for any queue on a
* particular host. When the host is not busy、 we look for a request
* on any queue on this host、 and attempt to issue it. This may
* not be the queue we were asked to process.即ち、elv_next_requestから戻られたreqは必ずしもmq->reqではない
*/
static void mmc_request(struct request_queue *q)
{
struct mmc_queue *mq = q->queuedata;
struct request *req;
int ret;
if (!mq) {
printk(KERN_ERR "MMC: killing requests for dead queue\n");
while ((req = elv_next_request(q)) != NULL) {
do {
ret = __blk_end_request(req、 -EIO、
blk_rq_cur_bytes(req));//処理できるリクエストはない場合、このリクエストを使う
} while (ret);
}
return;
}
if (!mq->req)
wake_up_process(mq->thread);//注一
}
注一:LDD3で紹介するブロックデバイス方法と異なって、bio構造に関連するデバイスはない、リクエストが取得する時、データブロックの送信はmq->threadスレッドを呼び出すことにより実現できる、スレッドは実際にはmmc_queue_thread関数である
static int mmc_queue_thread(void *d)
{
struct mmc_queue *mq = d;
struct request_queue *q = mq->queue;
current->flags |= PF_MEMALLOC;
down(&mq->thread_sem);
do {
struct request *req = NULL;
spin_lock_irq(q->queue_lock);
set_current_state(TASK_INTERRUPTIBLE);
if (!blk_queue_plugged(q))
req = elv_next_request(q);
mq->req = req;
spin_unlock_irq(q->queue_lock);
if (!req) {
if (kthread_should_stop()) {
set_current_state(TASK_RUNNING);
break;
}
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
continue;
}
set_current_state(TASK_RUNNING);
//
mq->issue_fn(mq、 req);//注一
} while (1);
up(&mq->thread_sem);
return 0;
}
注一:issue_fn関数:
brq.data.sg = mq->sg;
brq.data.sg_len = mmc_queue_map_sg(mq);
/*
* Adjust the sg list so it is the same size as the
* request.
*/
if (brq.data.blocks != req->nr_sectors) {
int i、 data_size = brq.data.blocks << 9;
struct scatterlist *sg;
for_each_sg(brq.data.sg、 sg、 brq.data.sg_len、 i) {
data_size -= sg->length;
if (data_size <= 0) {
sg->length += data_size;
i++;
break;
}
}
brq.data.sg_len = i;
}
上記のコードはscatterlistを準備するため、データ伝送のバッファ
mmc_wait_for_req(card->host、 &brq.mrq);次にhostへリクエストを送信し、この関数はよく分かるはずで、この最後はhost->ops->request(host、 mrq)である、このようにドライバプログラムのrequestと繋がり、今回のcmd—>dataデータメンバは空になくなったのため、起動するのはデータ伝送である。
6. 実験
デフォルトプラットフォームの情報が変更され
.get_ro = s3cmci_get_ro、
.get_cd = s3cmci_card_present、
static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
/* This is currently here to avoid a number of if (host->pdata)
* checks. Any zero fields to ensure reaonable defaults are picked. */
.detect_invert=0、
.wprotect_invert=1、
.gpio_detect=1、
.gpio_wprotect = 1 、
};
問題:
host_dodmaは1を設定する場合、/sdcardで内容がない、/proc/devicesにも対応デバイスがない
プリントアウト情報から見て:
7>mmc0: clock 0Hz busmode 1 powermode 1 cs 0 Vdd 21 width 0 timing 0
<6>s3c2440-sdi s3c2440-sdi: running at 0kHz (requested: 0kHz).
<7>mmc0: clock 197753Hz busmode 1 powermode 2 cs 0 Vdd 21 width 0 timing 0
<6>s3c2440-sdi s3c2440-sdi: running at 198kHz (requested: 197kHz).
<7>mmc0: clock 197753Hz busmode 1 powermode 2 cs 1 Vdd 21 width 0 timing 0
<6>s3c2440-sdi s3c2440-sdi: running at 198kHz (requested: 197kHz).
<7>mmc0: starting CMD0 arg 00000000 flags 000000c0
<7>s3c2440-sdi s3c2440-sdi: CMD[OK] #1 op:0 arg:0x00000000 flags:0x08c0 retries:0 R0:0x00000000
<7>mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
コマンドは成功に送信された、こうなる原因は?
ここまで、データとコマンドフローに従い、MMCサブシステムの基本構造を分析した。
----------続く