spin_lock_irqsave(&dev->lock, irq_flags);
if ((ret = radix_tree_insert(&dev->pipes, (unsigned long)pipe, pipe))) {
spin_unlock_irqrestore(&dev->lock, irq_flags);
PIPE_E(“opening pipe failed due to radix tree insertion failure\n”);
kfree(pipe);
return ret;
}
writel((unsigned long)pipe, dev->base + PIPE_REG_CHANNEL);
writel(CMD_OPEN, dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);
spin_unlock_irqrestore(&dev->lock, irq_flags);
if (status < 0) {
PIPE_E(“Could not open pipe channel, error=%d\n”, status);
kfree(pipe);
return status;
}
/* All is done, save the pipe into the file’s private data field */
file->private_data = pipe;
return 0;
}
qemu_pipe_read和qemu_pipe_write都是使用qemu_pipe_read_write来实现的,注意access_ok和__get_user/__put_user对于用户空间指针的检测。具体的读写比较简单,就是操作IO寄存器而已,需要注意的是,如果是非阻塞方式,需要进行阻塞等待。
具体的方法就是往PIPE_REG_COMMAND里面写CMD_WAKE_ON_WRITE或者CMD_WAKE_ON_READ,然后调用wait_event_interruptible去等待!test_bit(wakeBit, &pipe->flags)。
当中断来临时,会检查每一个CHANNEL的PIPE_REG_WAKES寄存器,如果可读 or 可写 or 已关闭,中断函数中会清除pipe->flags中的对应的等待标志位,然后wait_event_interruptible等待结束。如果是qemu_pipe被关闭的情况,wait_event_interruptible等待结束之后,检查到错误状态并退出。
/* This function is used for both reading from and writing to a given
* pipe.
*/
static ssize_t qemu_pipe_read_write(struct file *filp, char __user *buffer,
size_t bufflen, int is_write)
{
unsigned long irq_flags;
struct qemu_pipe *pipe = filp->private_data;
struct qemu_pipe_dev *dev = pipe->dev;
-
const int cmd_offset = is_write ? 0
- (CMD_READ_BUFFER - CMD_WRITE_BUFFER);
unsigned long address, address_end;
int ret = 0;
/* If the emulator already closed the pipe, no need to go further */
if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) {
PIPE_W(“(write=%d) already closed!\n”, is_write);
ret = -EIO;
goto out;
}
/* Null reads or writes succeeds */
if (unlikely(bufflen) == 0)
goto out;
/* Check the buffer range for access */
if (!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ,
buffer, bufflen)) {
ret = -EFAULT;
PIPE_W(“rw access_ok failed\n”);
goto out;
}
/* Serialize access to the pipe */
if (mutex_lock_interruptible(&pipe->lock)) {
PIPE_W(“(write=%d) interrupted!\n”, is_write);
return -ERESTARTSYS;
}
address = (unsigned long)(void *)buffer;
address_end = address + bufflen;
while (address < address_end) {
unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
-
unsigned long next = page_end < address_end ? page_end
- address_end;
unsigned long avail = next - address;
int status, wakeBit;
/* Ensure that the corresponding page is properly mapped */
if (is_write) {
char c;
/* Ensure that the page is mapped and readable */
if (__get_user(c, (char __user *)address)) {
PIPE_E(“read fault at address 0x%08x\n”,
(unsigned int)address);
if (!ret)
ret = -EFAULT;
break;
}
} else {
/* Ensure that the page is mapped and writable */
if (__put_user(0, (char __user *)address)) {
PIPE_E(“write fault at address 0x%08x\n”,
(unsigned int)address);
if (!ret)
ret = -EFAULT;
break;
}
}
/* Now, try to transfer the bytes in the current page */
spin_lock_irqsave(&dev->lock, irq_flags);
if (dev->aps == NULL || access_with_param(
dev, CMD_WRITE_BUFFER + cmd_offset, address, avail,
pipe, &status) < 0)
{
writel((unsigned long)pipe,
dev->base + PIPE_REG_CHANNEL);
writel(avail, dev->base + PIPE_REG_SIZE);
writel(address, dev->base + PIPE_REG_ADDRESS);
writel(CMD_WRITE_BUFFER + cmd_offset,
dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);
}
spin_unlock_irqrestore(&dev->lock, irq_flags);
if (status > 0) { /* Correct transfer */
ret += status;
address += status;
continue;
}
if (status == 0) /* EOF */
break;
/* An error occured. If we already transfered stuff, just
* return with its count. We expect the next call to return
* an error code */
if (ret > 0)
break;
/* If the error is not PIPE_ERROR_AGAIN, or if we are not in
* non-blocking mode, just return the error code.
*/
if (status != PIPE_ERROR_AGAIN ||
(filp->f_flags & O_NONBLOCK) != 0) {
ret = qemu_pipe_error_convert(status);
break;
}
/* We will have to wait until more data/space is available.
* First, mark the pipe as waiting for a specific wake signal.
*/
wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;
set_bit(wakeBit, &pipe->flags);
/* Tell the emulator we’re going to wait for a wake event */
spin_lock_irqsave(&dev->lock, irq_flags);
writel((unsigned long)pipe, dev->base + PIPE_REG_CHANNEL);
writel(CMD_WAKE_ON_WRITE + cmd_offset,
dev->base + PIPE_REG_COMMAND);
spin_unlock_irqrestore(&dev->lock, irq_flags);
/* Unlock the pipe, then wait for the wake signal */
mutex_unlock(&pipe->lock);
while (test_bit(wakeBit, &pipe->flags)) {
if (wait_event_interruptible(
pipe->wake_queue,
!test_bit(wakeBit, &pipe->flags))) {
ret = -ERESTARTSYS;
PIPE_W(“rw, wait_event error\n”);
goto out;
}
if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) {
ret = -EIO;
PIPE_W(“rw, pipe already closed\n”);
goto out;
}
}
/* Try to re-acquire the lock */
if (mutex_lock_interruptible(&pipe->lock)) {
ret = -ERESTARTSYS;
goto out;
}
/* Try the transfer again */
continue;
}
mutex_unlock(&pipe->lock);
out:
return ret;
}
static ssize_t qemu_pipe_read(struct file *filp, char __user *buffer,
size_t bufflen, loff_t *ppos)
{
return qemu_pipe_read_write(filp, buffer, bufflen, 0);
}
static ssize_t qemu_pipe_write(struct file *filp,
const char __user *buffer, size_t bufflen,
loff_t *ppos)
{
return qemu_pipe_read_write(filp, (char __user *)buffer, bufflen, 1);
}
qemu_pipe_poll,实现poll,select,epoll接口用的,没什么特殊的,标准实现方式
static unsigned int qemu_pipe_poll(struct file *filp, poll_table *wait)
{
struct qemu_pipe *pipe = filp->private_data;
struct qemu_pipe_dev *dev = pipe->dev;
unsigned long irq_flags;
unsigned int mask = 0;
int status;
mutex_lock(&pipe->lock);
poll_wait(filp, &pipe->wake_queue, wait);
spin_lock_irqsave(&dev->lock, irq_flags);
writel((unsigned long)pipe, dev->base + PIPE_REG_CHANNEL);
writel(CMD_POLL, dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);
spin_unlock_irqrestore(&dev->lock, irq_flags);
mutex_unlock(&pipe->lock);
if (status & PIPE_POLL_IN)
mask |= POLLIN | POLLRDNORM;
if (status & PIPE_POLL_OUT)
mask |= POLLOUT | POLLWRNORM;
if (status & PIPE_POLL_HUP)
mask |= POLLHUP;
if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
mask |= POLLERR;
return mask;
}
qemu_pipe_interrupt,中断处理函数,循环处理每一个qemu_pipe,看看是否可读 or 可写 or 关闭了,然后唤醒对应的线程
static irqreturn_t qemu_pipe_interrupt(int irq, void *dev_id)
{
struct qemu_pipe_dev *dev = dev_id;
unsigned long irq_flags;
int count = 0;
/* We’re going to read from the emulator a list of (channel,flags)
* pairs corresponding to the wake events that occured on each
* blocked pipe (i.e. channel).
*/
spin_lock_irqsave(&dev->lock, irq_flags);
for (;😉 {
/* First read the channel, 0 means the end of the list */
struct qemu_pipe *pipe;
unsigned long wakes;
unsigned long channel = readl(dev->base + PIPE_REG_CHANNEL);
if (channel == 0)
break;
/* Convert channel to struct pipe pointer + read wake flags */
wakes = readl(dev->base + PIPE_REG_WAKES);
pipe = (struct qemu_pipe *)(ptrdiff_t)channel;
/* check if pipe is still valid */
if ((pipe = radix_tree_lookup(&dev->pipes,
(unsigned long)pipe)) == NULL) {
PIPE_W(“interrupt for already closed pipe\n”);
break;
}
/* Did the emulator just closed a pipe? */
if (wakes & PIPE_WAKE_CLOSED) {
set_bit(BIT_CLOSED_ON_HOST, &pipe->flags);
wakes |= PIPE_WAKE_READ | PIPE_WAKE_WRITE;
}
if (wakes & PIPE_WAKE_READ)
clear_bit(BIT_WAKE_ON_READ, &pipe->flags);
if (wakes & PIPE_WAKE_WRITE)
clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags);
wake_up_interruptible(&pipe->wake_queue);
count++;
}
spin_unlock_irqrestore(&dev->lock, irq_flags);
return (count == 0) ? IRQ_NONE : IRQ_HANDLED;
}
setup_access_params_addr和access_with_param用于快速读写的,看不懂的可以跳过:
/* 0 on success */
static int setup_access_params_addr(struct qemu_pipe_dev *dev)
{
uint64_t paddr;
struct access_params *aps;
aps = kmalloc(sizeof(struct access_params), GFP_KERNEL);
if (!aps)
return -1;
paddr = __pa(aps);
writel((uint32_t)(paddr >> 32), dev->base + PIPE_REG_PARAMS_ADDR_HIGH);
writel((uint32_t)paddr, dev->base + PIPE_REG_PARAMS_ADDR_LOW);
if (!valid_batchbuffer_addr(dev, aps))
return -1;
dev->aps = aps;
return 0;
}
/* A value that will not be set by qemu emulator */
#define IMPOSSIBLE_BATCH_RESULT (0xdeadbeaf)
static int access_with_param(struct qemu_pipe_dev *dev, const int cmd,
unsigned long address, unsigned long avail,
struct qemu_pipe *pipe, int *status)
{
struct access_params *aps = dev->aps;
aps->result = IMPOSSIBLE_BATCH_RESULT;
aps->channel = (unsigned long)pipe;
aps->size = avail;
aps->address = address;
aps->cmd = cmd;
writel(cmd, dev->base + PIPE_REG_ACCESS_PARAMS);
/* If aps->result unchanged, then batch command failed */
if (aps->result == IMPOSSIBLE_BATCH_RESULT)
return -1;
*status = aps->result;
return 0;
}
**另外需要说明的是几种不同的地址:
1、guest os进程虚拟地址,用户空间的地址,内核想使用这种地址时,需要调用copy_from_user与copy_to_user去验证是否正确然后才能读写
2、guest os内核虚拟地址,3GB~4GB
3、guest os内核物理地址,经典情况下,就是内核虚拟地址减去一个偏移量(3GB),物理内存较大时,情况不同。在qemu中通过safe_get_phys_page_debug可以把guest os内核虚拟地址转为guest os内核物理地址
4、emulator所在虚拟空间地址,我们的host os中的用户空间地址,qemu可以操作的内存地址。guest os内核物理地址通过cpu_physical_memory_map后可以map为qemu所在的虚拟空间的地址,然后qemu可以去使用内核传递过来的内存。**
**三、虚拟设备
pipe虚拟设备的代码为:http://androidxref.com/5.1.0_r1/xref/external/qemu/hw/android/goldfish/pipe.c**
初始化代码为pipe_dev_init,没啥好说的,比battery的简单多了。最后有三个调试用的东西,可以不看:
/* initialize the trace device */
void pipe_dev_init(bool newDeviceNaming)
{
PipeDevice *s;
s = (PipeDevice *) g_malloc0(sizeof(*s));
s->dev.name = newDeviceNaming ? “goldfish_pipe” : “qemu_pipe”;
s->dev.id = -1;
s->dev.base = 0; // will be allocated dynamically
s->dev.size = 0x2000;
s->dev.irq = 0;
s->dev.irq_count = 1;
goldfish_device_add(&s->dev, pipe_dev_readfn, pipe_dev_writefn, s);
register_savevm(NULL,
“goldfish_pipe”,
0,
GOLDFISH_PIPE_SAVE_VERSION,
goldfish_pipe_save,
goldfish_pipe_load,
s);
#if DEBUG_ZERO_PIPE
goldfish_pipe_add_type(“zero”, NULL, &zeroPipe_funcs);
#endif
#if DEBUG_PINGPONG_PIPE
goldfish_pipe_add_type(“pingpong”, NULL, &pingPongPipe_funcs);
#endif
#if DEBUG_THROTTLE_PIPE
goldfish_pipe_add_type(“throttle”, NULL, &throttlePipe_funcs);
#endif
}
读函数为pipe_dev_read,需要注意的是PIPE_REG_CHANNEL。
kernel中的中断处理函数每次读取PIPE_REG_CHANNEL时,模拟设备都会将dev->signaled_pipes链表上的一个CHANNEL返回,并设置PIPE_REG_WAKES寄存器,告知kernel中pipe的驱动程序可以唤醒哪一个CHANNEL上的读等待 or 写等待的线程。
dev->signaled_pipes时满足条件,等待被唤醒的pipe列表,里面的节点是在goldfish_pipe_wake函数中添加的。
当dev->signaled_pipes为NULL时,通过goldfish_device_set_irq(&dev->dev, 0, 0)清除中断请求位。
/* I/O read */
static uint32_t pipe_dev_read(void *opaque, hwaddr offset)
{
PipeDevice *dev = (PipeDevice *)opaque;
switch (offset) {
case PIPE_REG_STATUS:
DR(“%s: REG_STATUS status=%d (0x%x)”, __FUNCTION__, dev->status, dev->status);
return dev->status;
case PIPE_REG_CHANNEL:
if (dev->signaled_pipes != NULL) {
Pipe* pipe = dev->signaled_pipes;
DR(“%s: channel=0x%llx wanted=%d”, __FUNCTION__,
(unsigned long long)pipe->channel, pipe->wanted);
dev->wakes = pipe->wanted;
pipe->wanted = 0;
dev->signaled_pipes = pipe->next_waked;
pipe->next_waked = NULL;
if (dev->signaled_pipes == NULL) {
goldfish_device_set_irq(&dev->dev, 0, 0);
DD(“%s: lowering IRQ”, __FUNCTION__);
}
return (uint32_t)(pipe->channel & 0xFFFFFFFFUL);
}
DR(“%s: no signaled channels”, __FUNCTION__);
return 0;
case PIPE_REG_CHANNEL_HIGH:
if (dev->signaled_pipes != NULL) {
Pipe* pipe = dev->signaled_pipes;
DR(“%s: channel_high=0x%llx wanted=%d”, __FUNCTION__,
(unsigned long long)pipe->channel, pipe->wanted);
return (uint32_t)(pipe->channel >> 32);
}
DR(“%s: no signaled channels”, __FUNCTION__);
return 0;
case PIPE_REG_WAKES:
DR(“%s: wakes %d”, __FUNCTION__, dev->wakes);
return dev->wakes;
case PIPE_REG_PARAMS_ADDR_HIGH:
return (uint32_t)(dev->params_addr >> 32);
case PIPE_REG_PARAMS_ADDR_LOW:
return (uint32_t)(dev->params_addr & 0xFFFFFFFFUL);
default:
D(“%s: offset=%d (0x%x)\n”, __FUNCTION__, offset, offset);
}
return 0;
}
写函数为pipe_dev_write,如果是写PIPE_REG_COMMAND,有专门的子函数pipeDevice_doCommand处理,如果是写PIPE_REG_ACCESS_PARAMS,相当于batch操作,传递了多个寄存器的值,然后去执行读写操作。
static void pipe_dev_write(void *opaque, hwaddr offset, uint32_t value)
{
PipeDevice *s = (PipeDevice *)opaque;
switch (offset) {
case PIPE_REG_COMMAND:
DR(“%s: command=%d (0x%x)”, __FUNCTION__, value, value);
pipeDevice_doCommand(s, value);
break;
case PIPE_REG_SIZE:
DR(“%s: size=%d (0x%x)”, __FUNCTION__, value, value);
s->size = value;
break;
case PIPE_REG_ADDRESS:
DR(“%s: address=%d (0x%x)”, __FUNCTION__, value, value);
uint64_set_low(&s->address, value);
break;
case PIPE_REG_ADDRESS_HIGH:
DR(“%s: address_high=%d (0x%x)”, __FUNCTION__, value, value);
uint64_set_high(&s->address, value);
break;
case PIPE_REG_CHANNEL:
DR(“%s: channel=%d (0x%x)”, __FUNCTION__, value, value);
uint64_set_low(&s->channel, value);
break;
case PIPE_REG_CHANNEL_HIGH:
DR(“%s: channel_high=%d (0x%x)”, __FUNCTION__, value, value);
uint64_set_high(&s->channel, value);
break;
case PIPE_REG_PARAMS_ADDR_HIGH:
s->params_addr = (s->params_addr & ~(0xFFFFFFFFULL << 32) ) |
((uint64_t)value << 32);
break;
case PIPE_REG_PARAMS_ADDR_LOW:
s->params_addr = (s->params_addr & ~(0xFFFFFFFFULL) ) | value;
break;
case PIPE_REG_ACCESS_PARAMS:
{
struct access_params aps;
struct access_params_64 aps64;
uint32_t cmd;
/* Don’t touch aps.result if anything wrong */
if (s->params_addr == 0)
break;
if (goldfish_guest_is_64bit()) {
cpu_physical_memory_read(s->params_addr, (void*)&aps64,
sizeof(aps64));
} else {
cpu_physical_memory_read(s->params_addr, (void*)&aps,
sizeof(aps));
}
/* sync pipe device state from batch buffer */
if (goldfish_guest_is_64bit()) {
s->channel = aps64.channel;
s->size = aps64.size;
s->address = aps64.address;
cmd = aps64.cmd;
} else {
s->channel = aps.channel;
s->size = aps.size;
s->address = aps.address;
cmd = aps.cmd;
}
if ((cmd != PIPE_CMD_READ_BUFFER) && (cmd != PIPE_CMD_WRITE_BUFFER))
break;
pipeDevice_doCommand(s, cmd);
if (goldfish_guest_is_64bit()) {
aps64.result = s->status;
cpu_physical_memory_write(s->params_addr, (void*)&aps64,
sizeof(aps64));
} else {
aps.result = s->status;
cpu_physical_memory_write(s->params_addr, (void*)&aps,
sizeof(aps));
}
}
break;
default:
D(“%s: offset=%d (0x%x) value=%d (0x%x)\n”, __FUNCTION__, offset,
offset, value, value);
break;
}
}
pipeDevice_doCommand,打开,关闭,读,写,可读时唤醒,可写时唤醒。
需要注意的是:
1、在刚打开CHANNEL时,pipe->funcs函数指针指向pipeConnector_funcs,根据guest os第一次写入到/dev/qemu_pipe的内容,得到pipe service的名字以及args。
之后,pipe->funcs都将指向对应的pipe service中实现的函数。
2、使用safe_get_phys_page_debug将传递过来的guest os内核虚拟地址转为guest os内核物理地址,然后使用qemu_get_ram_ptr转为emulator进程的虚拟空间地址。
static void
pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
{
Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);
Pipe* pipe = *lookup;
CPUOldState* env = cpu_single_env;
/* Check that we’re referring a known pipe channel */
if (command != PIPE_CMD_OPEN && pipe == NULL) {
dev->status = PIPE_ERROR_INVAL;
return;
}
/* If the pipe is closed by the host, return an error */
if (pipe != NULL && pipe->closed && command != PIPE_CMD_CLOSE) {
dev->status = PIPE_ERROR_IO;
return;
}
switch (command) {
case PIPE_CMD_OPEN:
DD(“%s: CMD_OPEN channel=0x%llx”, __FUNCTION__, (unsigned long long)dev->channel);
if (pipe != NULL) {
dev->status = PIPE_ERROR_INVAL;
break;
}
pipe = pipe_new(dev->channel, dev);
pipe->next = dev->pipes;
dev->pipes = pipe;
dev->status = 0;
break;
case PIPE_CMD_CLOSE:
DD(“%s: CMD_CLOSE channel=0x%llx”, __FUNCTION__, (unsigned long long)dev->channel);
/* Remove from device’s lists */
*lookup = pipe->next;
pipe->next = NULL;
pipe_list_remove_waked(&dev->signaled_pipes, pipe);
pipe_free(pipe);
break;
case PIPE_CMD_POLL:
dev->status = pipe->funcs->poll(pipe->opaque);
DD(“%s: CMD_POLL > status=%d”, __FUNCTION__, dev->status);
break;
case PIPE_CMD_READ_BUFFER: {
/* Translate virtual address into physical one, into emulator memory. */
GoldfishPipeBuffer buffer;
target_ulong address = dev->address;
target_ulong page = address & TARGET_PAGE_MASK;
hwaddr phys;
phys = safe_get_phys_page_debug(ENV_GET_CPU(env), page);
#ifdef TARGET_X86_64
phys = phys & TARGET_PTE_MASK;
#endif
buffer.data = qemu_get_ram_ptr(phys) + (address - page);
buffer.size = dev->size;
dev->status = pipe->funcs->recvBuffers(pipe->opaque, &buffer, 1);
DD(“%s: CMD_READ_BUFFER channel=0x%llx address=0x%16llx size=%d > status=%d”,
__FUNCTION__, (unsigned long long)dev->channel, (unsigned long long)dev->address,
dev->size, dev->status);
break;
}
case PIPE_CMD_WRITE_BUFFER: {
/* Translate virtual address into physical one, into emulator memory. */
GoldfishPipeBuffer buffer;
target_ulong address = dev->address;
target_ulong page = address & TARGET_PAGE_MASK;
hwaddr phys;
phys = safe_get_phys_page_debug(ENV_GET_CPU(env), page);
#ifdef TARGET_X86_64
phys = phys & TARGET_PTE_MASK;
#endif
buffer.data = qemu_get_ram_ptr(phys) + (address - page);
buffer.size = dev->size;
dev->status = pipe->funcs->sendBuffers(pipe->opaque, &buffer, 1);
DD(“%s: CMD_WRITE_BUFFER channel=0x%llx address=0x%16llx size=%d > status=%d”,
__FUNCTION__, (unsigned long long)dev->channel, (unsigned long long)dev->address,
dev->size, dev->status);
break;
}
case PIPE_CMD_WAKE_ON_READ:
DD(“%s: CMD_WAKE_ON_READ channel=0x%llx”, __FUNCTION__, (unsigned long long)dev->channel);
if ((pipe->wanted & PIPE_WAKE_READ) == 0) {
pipe->wanted |= PIPE_WAKE_READ;
pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
}
dev->status = 0;
break;
case PIPE_CMD_WAKE_ON_WRITE:
DD(“%s: CMD_WAKE_ON_WRITE channel=0x%llx”, __FUNCTION__, (unsigned long long)dev->channel);
if ((pipe->wanted & PIPE_WAKE_WRITE) == 0) {
pipe->wanted |= PIPE_WAKE_WRITE;
pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
}
dev->status = 0;
break;
default:
D(“%s: command=%d (0x%x)\n”, __FUNCTION__, command, command);
}
}
pipeDevice_doCommand中提到的pipeConnector_funcs函数数组,只有一个pipeConnector_sendBuffers有效,其他都是空壳
pipeConnector_sendBuffers用于guest os第一次往/dev/qemu_pipe中写数据,数据内容为pipe::,去寻找匹配的pipe service,然后调用其初始化函数,得到peer(第三篇中的QemudPipe,也是pipe service funcs中的参数opaque),然后设置pipe->funcs指向pipe service提供的funcs。
static int
pipeConnector_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
{
PipeConnector* pcon = opaque;
const GoldfishPipeBuffer* buffers_limit = buffers + numBuffers;
int ret = 0;
DD(“%s: channel=0x%llx numBuffers=%d”, __FUNCTION__,
(unsigned long long)pcon->pipe->channel,
numBuffers);
while (buffers < buffers_limit) {
int avail;
DD(“%s: buffer data (%3d bytes): ‘%.*s’”, __FUNCTION__,
buffers[0].size, buffers[0].size, buffers[0].data);
if (buffers[0].size == 0) {
buffers++;
continue;
}
avail = sizeof(pcon->buffer) - pcon->buffpos;
if (avail > buffers[0].size)
avail = buffers[0].size;
if (avail > 0) {
memcpy(pcon->buffer + pcon->buffpos, buffers[0].data, avail);
pcon->buffpos += avail;
ret += avail;
}
buffers++;
}
/* Now check that our buffer contains a zero-terminated string */
if (memchr(pcon->buffer, ‘\0’, pcon->buffpos) != NULL) {
/* Acceptable formats for the connection string are:
*
* pipe:
* pipe::
*/
char* pipeName;
char* pipeArgs;
D(“%s: connector: ‘%s’”, __FUNCTION__, pcon->buffer);
if (memcmp(pcon->buffer, “pipe:”, 5) != 0) {
/* Nope, we don’t handle these for now. */
D(“%s: Unknown pipe connection: ‘%s’”, __FUNCTION__, pcon->buffer);
return PIPE_ERROR_INVAL;
}
pipeName = pcon->buffer + 5;
pipeArgs = strchr(pipeName, ‘:’);
if (pipeArgs != NULL) {
*pipeArgs++ = ‘\0’;
if (!*pipeArgs)
pipeArgs = NULL;
}
Pipe* pipe = pcon->pipe;
const PipeService* svc = goldfish_pipe_find_type(pipeName);
if (svc == NULL) {
D(“%s: Unknown server!”, __FUNCTION__);
return PIPE_ERROR_INVAL;
}
void* peer = svc->funcs.init(pipe, svc->opaque, pipeArgs);
if (peer == NULL) {
D(“%s: Initialization failed!”, __FUNCTION__);
return PIPE_ERROR_INVAL;
}
/* Do the evil switch now */
pipe->opaque = peer;
pipe->service = svc;
pipe->funcs = &svc->funcs;
pipe->args = ASTRDUP(pipeArgs);
AFREE(pcon);
}
return ret;
}
goldfish_pipe_add_type用于注册pipe service
void
goldfish_pipe_add_type(const char* pipeName,
void* pipeOpaque,
const GoldfishPipeFuncs* pipeFuncs )
{
PipeServices* list = _pipeServices;
int count = list->count;
if (count >= MAX_PIPE_SERVICES) {
APANIC(“Too many goldfish pipe services (%d)”, count);
}
if (strlen(pipeName) > MAX_PIPE_SERVICE_NAME_SIZE) {
APANIC(“Pipe service name too long: ‘%s’”, pipeName);
}
list->services[count].name = pipeName;
list->services[count].opaque = pipeOpaque;
list->services[count].funcs = pipeFuncs[0];
list->count++;
}
goldfish_pipe_find_type用于按pipe service name查找pipe service
static const PipeService*
goldfish_pipe_find_type(const char* pipeName)
{
PipeServices* list = _pipeServices;
int count = list->count;
int nn;
for (nn = 0; nn < count; nn++) {
if (!strcmp(list->services[nn].name, pipeName)) {
return &list->services[nn];
}
}
return NULL;
}
pipe_list_findp_channel,pipe_list_findp_waked,pipe_list_remove_waked是一些链表操作
static Pipe**
pipe_list_findp_channel( Pipe** list, uint64_t channel )
{
Pipe** pnode = list;
for (;😉 {
Pipe* node = *pnode;
if (node == NULL || node->channel == channel) {
break;
}
pnode = &node->next;
}
return pnode;
}
static Pipe**
pipe_list_findp_waked( Pipe** list, Pipe* pipe )
{
Pipe** pnode = list;
for (;😉 {
Pipe* node = *pnode;
if (node == NULL || node == pipe) {
break;
}
pnode = &node->next_waked;
}
return pnode;
}
static void
pipe_list_remove_waked( Pipe** list, Pipe* pipe )
{
Pipe** lookup = pipe_list_findp_waked(list, pipe);
Pipe* node = *lookup;
if (node != NULL) {
(*lookup) = node->next_waked;
node->next_waked = NULL;
}
}
goldfish_pipe_wake主要是在具体的pipe service中使用的,当pipe service可以接收数据或者被写入数据时,去唤醒等待的线程
void
goldfish_pipe_wake( void* hwpipe, unsigned flags )
{
Pipe* pipe = hwpipe;
Pipe** lookup;
PipeDevice* dev = pipe->device;
DD(“%s: channel=0x%llx flags=%d”, __FUNCTION__, (unsigned long long)pipe->channel, flags);
/* If not already there, add to the list of signaled pipes */
lookup = pipe_list_findp_waked(&dev->signaled_pipes, pipe);
if (!*lookup) {
pipe->next_waked = dev->signaled_pipes;
dev->signaled_pipes = pipe;
}
pipe->wanted |= (unsigned)flags;
/* Raise IRQ to indicate there are items on our list ! */
goldfish_device_set_irq(&dev->dev, 0, 1);
DD(“%s: raising IRQ”, __FUNCTION__);
}
goldfish_pipe_close关闭时,需要唤醒等待的线程
void
goldfish_pipe_close( void* hwpipe )
{