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(&pi