QEMUに仮想PCIデバイスを追加する(6) : メモリマップドI/O / ioctl の使用

メモリマップドI/Oの利用 を ioctl システムコールを用いてテストしました。

# QEMU仮想デバイス
I/Oポートの場合と同様に、
メモリマップドI/O のread/writeに対応するMemoryRegionOpsの関数を書けば良いです。
なお、ここではアクセスは4byteずつとしているため、addrの値を2bit右シフトして
配列を参照しています。

I/O ポートの場合と同じく使用するメモリ領域は予め初期化しておきます。

```c
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 関数中で行っています。

```c
// 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用のコマンドは間違いを防ぐために単純な数字ではなく
以下のようにマクロを利用して作成すると良いようです。

```c
// 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 のマジックナンバーで、第二引数はコマンドの
連番となります。第三引数はやり取りするデータ型です。
これは以下の様にしました。

```c
// 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 を行っています。

```c
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)