トップ 差分 一覧 Farm ソース 検索 ヘルプ PDF RSS ログイン

Diary/2014-4-23

Xillybusのドライバを読む

xillybus_core.c から.まずは,ざっと,どんな感じのものがあるのか見てみる.
これはFPGA内のIPコアのFIFOとデバイスファイルのパイプを作る.
キャラクタデバイスで,構造体は↓な感じ.

static const struct file_operations xillybus_fops = {
        .owner      = THIS_MODULE,
        .read       = xillybus_read,
        .write      = xillybus_write,
        .open       = xillybus_open,
        .flush      = xillybus_flush,
        .release    = xillybus_release,
        .llseek     = xillybus_llseek,
        .poll       = xillybus_poll,
};

初期化関連

xillybus_init/xillybus_exit

ドライバロード時にやってるのは,

  • class_create
  • alloc_workqueue

アンロード時には,

  • destroy_workqueue
  • class_destroy

xillybus_init_endpoint
EXPORT_SYMBOL(xillybus_init_endpoint);

でエクスポートされている関数.
xillybus_pcie.cのxilly_probe(←pciドライバの.porbeにマッピングされてる)か,
xillybus_of.cのxilly_drv_probe(platform_driverの.probeにマッピングされてる)から呼ばれる.
デバドラ内部で使うデータの初期化

xillybus_endpoint_discovery
EXPORT_SYMBOL(xillybus_endpoint_discovery);

でエクスポートされている関数.
xillybus_pcie.cのxilly_probe(←pciドライバの.porbeにマッピングされてる)か,
xillybus_of.cのxilly_drv_probe(platform_driverの.probeにマッピングされてる)から呼ばれる.
ハードウェアから情報をスキャンしてハンドラを作る.ハンドラの定義は,xillybus.hにある

struct xilly_idt_handle {
        unsigned char *chandesc;
        unsigned char *idt;
        int entries;
};
  • xilly_setupchannels チャネルのセットアップ.
  • xilly_obtain_idt ハードウェアのバージョンチェックなど
  • xilly_scan_idt

ハンドラが用意できたら,xillybus_init_chrdevを呼ぶ

xillybus_init_chrdev

キャラクタデバイスとしての初期化,デバイスファイルを作るのも,ここ.

アクセス関連

xillybus_open

モードに応じたチャネルやロックの設定など

  • mutex_lock_interruptible
  • iowrite32でデバイスにオープンされたことを通知

xillybus_read

データの読み出し.こんな感じ.

while(1)
  bytes_to_do = count - bytes_done; 残りの読むべきバイト数をセット

  if バッファがemptyでない
    if バッファにたまっている個数(howmany)がbyte_to_doより多い
      bufferdone = 0;
    else
      bufferdone = 1;
    end
  end

  if emptyでない
    if (bufops == 0) // バッファが空
      channel->endpoint->ephw->hw_sync_sgl_for_cpuでDMAする(DMA_FROM_DEVICE)
    end
    copy_to_userでバッファのデータをユーザ領域にデータをコピー
    userbuf += howmany
    bytes_done += howmany
    if bufferdoneが0でない then
      channel->endpoint->ephw->hw_sync_sgl_for_cpuでDMAする(DMA_FROM_DEVICE)
      // FPGAにbufferdoneを通知
      iowrite32(1 | (channel->chan_num << 1) | (bufidx << 12),
                &channel->endpoint->registers[fpga_buf_ctrl_reg]);
    end
  end

  // ループの終了判定
  bytes_doneがcount以上かeofに到達 => ループから抜ける
  exhaustedでない => ループ頭に戻る
  bytes_doneが0より大きい
   かつ
  no_time_left か (channel->wr_synchronous && channel->wr_allow_partial) => ループから抜ける
  no_time_leftでない かつ O_NONBLOCKが指定されてた
   bytes_done が 0 より大きい => ループから抜ける
   readyが0でない => desperateへ
   それ以外 => bytes_doneを-EAGAIN(エラー)にしてループから抜ける

  !no_time_left または bytes_done > 0
   アライメント調整

  if !channel->wr_allow_partial || (no_time_left && (bytes_done == 0))
    do{
      if (wait_event_interruptible(channel->wr_wait, (!channel->wr_sleepy)))
        => interruptedへ
      if (mutex_lock_interruptible(&channel->wr_mutex))
        => interruptedへ
    }while(channel->wr_sleep);
    ループの先頭に戻る

    interrupted:
      bytes_doneが0でなければ,return bytes_done
      あるいは エラー
  end

  if left_to_sleep > 0 then
     left_to_sleep = wait_event_interruptible_timeout(channel->wr_wait,
                                                      (!channel->wr_sleepy),
                                                      left_to_sleep);
     (!channel->wr_sleepy) => ループの先頭に戻る
     if left_to_sleep < 0 then
       bytes_doneが0でなければ,return bytes_done
       あるいは エラー
     end
  end

  desparate:
    no_time_left = 1
end

続き

xillybus_write,xillybus_flush,xillybus_release,xillybus_llseek,xillybus_poll
は,また今度.