一、概述
qemu pipe也是一个虚拟设备,是一个通用的虚拟设备,用于提供guest os和emulator通信的功能,类似于一个抽象的通信层,这样就不用写很多虚拟设备了。
之前在guest os中有个qemud进程,也是干这个事的,使用虚拟设备ttyS1提供guest os和emulator通信的功能,速度比较慢,已被pipe所替代。
看本篇之前,必须看完第一篇;看完本篇,然后看第三篇,这两个是结合在一起的,都看完后建议回顾一下本篇。
基于通用的数据通信pipe,emulator提供了四种服务:
其中qemud又提供了一些子服务,比如Available services: ------------------- tcp:<port> Open a TCP socket to a given localhost port. This provides a very fast pass-through that doesn't depend on the very slow internal emulator NAT router. Note that you can only use the file descriptor with read() and write() though, send() and recv() will return an ENOTSOCK error, as well as any socket ioctl(). For security reasons, it is not possible to connect to non-localhost ports. unix:<path> Open a Unix-domain socket on the host. opengles Connects to the OpenGL ES emulation process. For now, the implementation is equivalent to tcp:22468, but this may change in the future. qemud Connects to the QEMUD service inside the emulator. This replaces the connection that was performed through /dev/ttyS1 in older Android platform releases. See $QEMU/docs/ANDROID-QEMUD.TXT for details.
"gsm" service"gps" service"hw-control" / "control" service"sensors" service"boot-properties" service
如何使用qemu_pipe去通信,将在第三篇中以qemud service中的"boot-properties" service为例去介绍,本篇仅关心虚拟设备以及驱动
二、驱动
先看文档:
XIV. QEMU Pipe device:
======================
Relevant files:
$QEMU/hw/android/goldfish/pipe.c
$KERNEL/drivers/misc/qemupipe/qemu_pipe.c
Device properties:
Name: qemu_pipe
Id: -1
IrqCount: 1
I/O Registers:
0x00 COMMAND W: Write to perform command (see below).
0x04 STATUS R: Read status
0x08 CHANNEL RW: Read or set current channel id.
0x0c SIZE RW: Read or set current buffer size.
0x10 ADDRESS RW: Read or set current buffer physical address.
0x14 WAKES R: Read wake flags.
0x18 PARAMS_ADDR_LOW RW: Read/set low bytes of parameters block address.
0x1c PARAMS_ADDR_HIGH RW: Read/set high bytes of parameters block address.
0x20 ACCESS_PARAMS W: Perform access with parameter block.
This is a special device that is totally specific to QEMU, but allows guest
processes to communicate directly with the emulator with extremely high
performance. This is achieved by avoiding any in-kernel memory copies, relying
on the fact that QEMU can access guest memory at runtime (under proper
conditions controlled by the kernel).
Please refer to $QEMU/docs/ANDROID-QEMU-PIPE.TXT for full details on the
device's operations.
1、COMMAND包括CMD_OPEN,CMD_CLOSE,CMD_POLL,CMD_WRITE_BUFFER,CMD_WAKE_ON_WRITE(可写时唤醒),CMD_READ_BUFFER,CMD_WAKE_ON_READ(可读时唤醒)
2、CHANNEL,每次打开/dev/qemu_pipe,都将新建一个struct qemu_pipe* pipe,相当于在/dev/qemu_pipe上面新开了一个通道,通道号CHANNEL=(unsigned long)pipe
3、WAKES,是否应该将读等待/写等待的线程唤醒
4、PARAMS_ADDR_LOW,PARAMS_ADDR_HIGH,ACCESS_PARAMS用于快速读写访问,这个看不懂的话不影响理解qemu_pipe,可以跳过。
struct access_params{
uint32_t channel;
uint32_t size;
uint32_t address;
uint32_t cmd;
uint32_t result;
/* reserved for future extension */
uint32_t flags;
};
kernel代码中qemu_pipe_dev在probe时,会申请一个access_params结构体,并将它在guest os的内核物理地址写入PARAMS_ADDR_LOW和PARAMS_ADDR_HIGH。
kernel代码在需要进行快速读写访问时,设置access_params结构体的内容,然后使用ACCESS_PARAMS启动快速读写。
emulator代码中虚拟设备将PARAMS_ADDR_LOW和PARAMS_ADDR_HIGH所表示的地址映射到emulator虚拟空间地址中,然后去获取channel, size, address, cmd等数据然后去操作,相同于一次IO访问,得到多个IO数据,所以叫做batch,快速访问。
注意PARAMS_ADDR_LOW和PARAMS_ADDR_HIGH写的是guest os的内核物理地址,access_params结构体里面的buffer还是guest os内核虚拟地址。
驱动程序为goldfish代码中的drivers/misc/qemupipe/qemu_pipe.c
初始化代码为:
static struct platform_driver qemu_pipe = {
.probe = qemu_pipe_probe,
.remove = qemu_pipe_remove,
.driver = {
.name = "qemu_pipe"
}
};
static int __init qemu_pipe_dev_init(void)
{
return platform_driver_register(&qemu_pipe);
}
static void qemu_pipe_dev_exit(void)
{
platform_driver_unregister(&qemu_pipe);
}
qemu_pipe_probe干的还是那些事,得到IO内存资源,进行ioremap,得到中断号,设置中断函数。最后使用misc_register注册了一个杂项字符设备,设备文件为/dev/qemu_pipe:
static const struct file_operations qemu_pipe_fops = {
.owner = THIS_MODULE,
.read = qemu_pipe_read,
.write = qemu_pipe_write,
.poll = qemu_pipe_poll,
.open = qemu_pipe_open,
.release = qemu_pipe_release,
};
static struct miscdevice qemu_pipe_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "qemu_pipe",
.fops = &qemu_pipe_fops,
};
static int qemu_pipe_probe(struct platform_device *pdev)
{
int err;
struct resource *r;
struct qemu_pipe_dev *dev = pipe_dev;
PIPE_D("Creating device\n");
INIT_RADIX_TREE(&dev->pipes, GFP_ATOMIC);
/* not thread safe, but this should not happen */
if (dev->base != NULL) {
printk(KERN_ERR "QEMU PIPE Device: already mapped at %p\n",
dev->base);
return -ENODEV;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL || r->end - r->start < PAGE_SIZE - 1) {
printk(KERN_ERR "QEMU PIPE Device: can't allocate i/o page\n");
return -EINVAL;
}
dev->base = ioremap(r->start, PAGE_SIZE);
PIPE_D("The mapped IO base is %p\n", dev->base);
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (r == NULL) {
printk(KERN_ERR "QEMU PIPE Device: failure to allocate IRQ\n");
err = -EINVAL;
goto err_alloc_irq;
}
dev->irq = r->start;
PIPE_D("The IRQ is %d\n", dev->irq);
err = request_irq(dev->irq, qemu_pipe_interrupt, IRQF_SHARED,
"goldfish_pipe", dev);
if (err)
goto err_alloc_irq;
spin_lock_init(&dev->lock);
err = misc_register(&am