android emulator虚拟设备分析第二篇之pipe

一、概述

qemu pipe也是一个虚拟设备,是一个通用的虚拟设备,用于提供guest os和emulator通信的功能,类似于一个抽象的通信层,这样就不用写很多虚拟设备了。

之前在guest os中有个qemud进程,也是干这个事的,使用虚拟设备ttyS1提供guest os和emulator通信的功能,速度比较慢,已被pipe所替代。

看本篇之前,必须看完第一篇;看完本篇,然后看第三篇,这两个是结合在一起的,都看完后建议回顾一下本篇。

基于通用的数据通信pipe,emulator提供了四种服务

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.
其中qemud又提供了一些子服务比如
"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
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值