メモリマップドI/Oの利用 を ioctl システムコールを用いてテストしました。
QEMU仮想デバイス
I/Oポートの場合と同様に、
メモリマップドI/O のread/writeに対応するMemoryRegionOpsの関数を書けば良いです。
なお、ここではアクセスは4byteずつとしているため、addrの値を2bit右シフトして
配列を参照しています。
I/O ポートの場合と同じく使用するメモリ領域は予め初期化しておきます。
static uint64_t
test_pci_mmio_read(void *opaque, hwaddr addr, unsigned size)
{
TestPCIState *s = opaque;
tprintf("addr %ld, size %d\n", addr, size);
if(addr >= TEST_MEM && addr < TEST_MEM + TEST_MMIO_DATASIZE){
return s->mmiodata[addr>>2]; // 4byte
}
return 0xaabbccdd; // not used
}
static void
test_pci_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
TestPCIState *s = opaque;
tprintf("addr %ld, size %d\n", addr, size);
if(addr >= TEST_MEM && addr < TEST_MEM + TEST_MMIO_DATASIZE){
s->mmiodata[addr>>2] = val; // 4byte
}
}
// test_pci_init 中
for(i=0; i<TEST_MMIO_DATASIZE; i++){
s->mmiodata[i] = i;
}
デバイスドライバ
ioctl を利用してメモリマップドI/Oをテストしています。
なお、PCIコンフィギュレーション空間から読み取ったメモリマップドI/Oの
開始アドレスは物理アドレスであるため、
アクセスする際には仮想アドレスへの変換が必要です。
これには ioremap 関数を用います。(終了時にiounmapを実行)。
この変換は初期化の probe 関数中で行っています。
// test_pci_probe 関数中
dev_data->mmio_addr = ioremap(dev_data->mmio_base, TEST_PCI_MEMSIZE);
// test_pci_remove 関数中
iounmap(dev_data->mmio_addr);
long test_pci_uioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int data[TEST_MMIO_DATANUM];
test_ioctl_data *d = arg;
int n;
tprintk("_cmd:%d\n", cmd);
switch (cmd) {
case TEST_CMD_MEMREAD :
memcpy_fromio(data, dev_data->mmio_addr, TEST_MMIO_DATASIZE);
copy_to_user(d->mmiodata, data, TEST_MMIO_DATASIZE);
break;
case TEST_CMD_MEMWRITE :
copy_from_user(data, d->mmiodata, TEST_MMIO_DATASIZE);
memcpy_toio(dev_data->mmio_addr, data, TEST_MMIO_DATASIZE);
break;
/* .... */
default:
return -EINVAL; // invalid argument(cmd)
}
return 0;
}
メモリマップドI/O を使用する場合、in/out 命令と同じように
read/write 命令を用いて数バイト単位でのアクセスが行えます
(read8/read16/read32, write8/write16/write32)。
これに加えて、memcpy_fromio/memcpy_toio 関数を用いることで
一定サイズのデータのやりとりを簡単に行うことが出来、今回はそちらの方法を採用しています。
また、ユーザー空間とのデータのやり取りに
copy_to_user/copy_from_user 関数を使用しています。
ioctl はコマンド(第二引数 unsigned int cmd) と
データ(第三引数 unsined long arg, 適当にキャストする)
を受け取り、コマンドに応じて switch 文を書くことでデバイス固有の動作を
実現します。 read/write システムコールにはI/Oポートを使用したので、
メモリマップドI/Oのテストにはioctlを利用しています。
なお、ioctl用のコマンドは間違いを防ぐために単純な数字ではなく
以下のようにマクロを利用して作成すると良いようです。
// test_pci.h 中
#define TEST_PCI_MAGIC 123 // 8bit
/* command for ioctl */
// for mmio
#define TEST_CMD_MEMREAD _IOR(TEST_PCI_MAGIC, 0, test_ioctl_data)
#define TEST_CMD_MEMWRITE _IOW(TEST_PCI_MAGIC, 1, test_ioctl_data)
リードの時は _IOR, ライトの時は _IOW, 両方なら _IOWRマクロを使います。
第一引数は8bit のマジックナンバーで、第二引数はコマンドの
連番となります。第三引数はやり取りするデータ型です。
これは以下の様にしました。
// test_pci.h
// use for ioctl
typedef struct hello_ioctl_data{
int mmiodata[TEST_MMIO_DATANUM];
int cdmabuf[TEST_CDMA_BUFFER_NUM];
int sdmabuf[TEST_SDMA_BUFFER_NUM];
} test_ioctl_data;
ユーザプログラム
ioctlシステムコールを使用します。
I/Oポートのテストと同じくある領域を
read/その値を+10してwrite/再びread を行っています。
void mmio_test(int fd)
{
int i;
test_ioctl_data *d;
d = malloc(sizeof(test_ioctl_data));
if(!d) exit(1);
printf("\n---- start mmio test ----\n");
ioctl(fd, TEST_CMD_MEMREAD, d);
for(i = 0; i < TEST_MMIO_DATANUM; i++) {
printf("%2d ", d->mmiodata[i]);
d->mmiodata[i] += 10;
}
printf("\n");
ioctl(fd, TEST_CMD_MEMWRITE, d);
for(i = 0; i < TEST_MMIO_DATANUM; i++) {
d->mmiodata[i] = 0;
}
ioctl(fd, TEST_CMD_MEMREAD, d);
for(i = 0; i < TEST_MMIO_DATANUM; i++) {
printf("%2d ", d->mmiodata[i]);
}
printf("\n");
printf("\n---- end mmio test ----\n");
free(d);
}
実行結果
mmio_test の実行結果は以下のようになります。
---- start mmio test ----
** (151) test_pci_uioctl : _cmd:-2147190016
## (105) test_pci_mmio_read : addr 0, size 4
## (105) test_pci_mmio_read : addr 4, size 4
## (105) test_pci_mmio_read : addr 8, size 4
## (105) test_pci_mmio_read : addr 12, size 4
## (105) test_pci_mmio_read : addr 16, size 4
## (105) test_pci_mmio_read : addr 20, size 4
## (105) test_pci_mmio_read : addr 24, size 4
## (105) test_pci_mmio_read : addr 28, size 4
## (105) test_pci_mmio_read : addr 32, size 4
## (105) test_pci_mmio_read : addr 36, size 4
## (105) test_pci_mmio_read : addr 40, size 4
## (105) test_pci_mmio_read : addr 44, size 4
## (105) test_pci_mmio_read : addr 48, size 4
## (105) test_pci_mmio_read : addr 52, size 4
## (105) test_pci_mmio_read : addr 56, size 4
## (105) test_pci_mmio_read : addr 60, size 4
## (105) test_pci_mmio_read : addr 64, size 4
## (105) test_pci_mmio_read : addr 68, size 4
## (105) test_pci_mmio_read : addr 72, size 4
## (105) test_pci_mmio_read : addr 76, size 4
## (105) test_pci_mmio_read : addr 80, size 4
## (105) test_pci_mmio_read : addr 84, size 4
## (105) test_pci_mmio_read : addr 88, size 4
## (105) test_pci_mmio_read : addr 92, size 4
## (105) test_pci_mmio_read : addr 96, size 4
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
** (151) test_pci_uioctl : _cmd:1074035457
## (139) test_pci_mmio_write : addr 0, size 4
## (139) test_pci_mmio_write : addr 4, size 4
## (139) test_pci_mmio_write : addr 8, size 4
## (139) test_pci_mmio_write : addr 12, size 4
## (139) test_pci_mmio_write : addr 16, size 4
## (139) test_pci_mmio_write : addr 20, size 4
## (139) test_pci_mmio_write : addr 24, size 4
## (139) test_pci_mmio_write : addr 28, size 4
## (139) test_pci_mmio_write : addr 32, size 4
## (139) test_pci_mmio_write : addr 36, size 4
## (139) test_pci_mmio_write : addr 40, size 4
## (139) test_pci_mmio_write : addr 44, size 4
## (139) test_pci_mmio_write : addr 48, size 4
## (139) test_pci_mmio_write : addr 52, size 4
## (139) test_pci_mmio_write : addr 56, size 4
## (139) test_pci_mmio_write : addr 60, size 4
## (139) test_pci_mmio_write : addr 64, size 4
## (139) test_pci_mmio_write : addr 68, size 4
## (139) test_pci_mmio_write : addr 72, size 4
## (139) test_pci_mmio_write : addr 76, size 4
## (139) test_pci_mmio_write : addr 80, size 4
## (139) test_pci_mmio_write : addr 84, size 4
## (139) test_pci_mmio_write : addr 88, size 4
## (139) test_pci_mmio_write : addr 92, size 4
## (139) test_pci_mmio_write : addr 96, size 4
** (151) test_pci_uioctl : _cmd:-2147190016
## (105) test_pci_mmio_read : addr 0, size 4
## (105) test_pci_mmio_read : addr 4, size 4
## (105) test_pci_mmio_read : addr 8, size 4
## (105) test_pci_mmio_read : addr 12, size 4
## (105) test_pci_mmio_read : addr 16, size 4
## (105) test_pci_mmio_read : addr 20, size 4
## (105) test_pci_mmio_read : addr 24, size 4
## (105) test_pci_mmio_read : addr 28, size 4
## (105) test_pci_mmio_read : addr 32, size 4
## (105) test_pci_mmio_read : addr 36, size 4
## (105) test_pci_mmio_read : addr 40, size 4
## (105) test_pci_mmio_read : addr 44, size 4
## (105) test_pci_mmio_read : addr 48, size 4
## (105) test_pci_mmio_read : addr 52, size 4
## (105) test_pci_mmio_read : addr 56, size 4
## (105) test_pci_mmio_read : addr 60, size 4
## (105) test_pci_mmio_read : addr 64, size 4
## (105) test_pci_mmio_read : addr 68, size 4
## (105) test_pci_mmio_read : addr 72, size 4
## (105) test_pci_mmio_read : addr 76, size 4
## (105) test_pci_mmio_read : addr 80, size 4
## (105) test_pci_mmio_read : addr 84, size 4
## (105) test_pci_mmio_read : addr 88, size 4
## (105) test_pci_mmio_read : addr 92, size 4
## (105) test_pci_mmio_read : addr 96, size 4
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
はじめ: [(1) 作ったもの]
(http://qiita.com/rafilia/items/f7646d12212da2a85bd8)
前: [(5) I/Oポートの使用]
(http://qiita.com/rafilia/items/9e8d62444be472e573c2)
次: [(7) 割り込みの設定]
(http://qiita.com/rafilia/items/b719d8adf1f39d956670)