From 908cd1b10a268ce64f04bf7ef6ac7adb0e222312 Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Mon, 5 Jun 2023 14:47:50 +0800 Subject: [PATCH] misc/goldfish_pipe: Refine the implementation 1.Merge goldfish_pipe_qemu.h into goldfish_pipe.c 2.Change all enum to macro Signed-off-by: Xiang Xiao --- drivers/misc/goldfish_pipe.c | 1259 ++++++++++--------------- drivers/misc/goldfish_pipe_qemu.h | 135 --- include/nuttx/drivers/goldfish_pipe.h | 2 +- 3 files changed, 478 insertions(+), 918 deletions(-) delete mode 100644 drivers/misc/goldfish_pipe_qemu.h diff --git a/drivers/misc/goldfish_pipe.c b/drivers/misc/goldfish_pipe.c index 5028bccad6..d975d5651c 100644 --- a/drivers/misc/goldfish_pipe.c +++ b/drivers/misc/goldfish_pipe.c @@ -18,33 +18,107 @@ * ****************************************************************************/ +/* Usage from the guest is simple: + * + * int fd = open("/dev/goldfish_pipe", O_RDWR); + * .... write() or read() through the pipe. + * + * Connect to a specific qemu service: + * + * // Do this immediately after opening the fd + * const char* msg = ""; + * if (write(fd, msg, strlen(msg) + 1) < 0) { + * ... could not connect to service + * close(fd); + * } + * + * After this, simply read() and write() to communicate with the service. + */ + /**************************************************************************** * Included Files ****************************************************************************/ -#include -#include -#include -#include -#include -#include #include -#include +#include +#include -#include -#include #include -#include #include #include -#include -#include "goldfish_pipe_qemu.h" +#include +#include +#include -#define putreg32(v, x) (*(volatile uint32_t*)(x) = (v)) -#define getreg32(x) (*(uint32_t *)(x)) +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ -#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16)) -#define lower_32_bits(n) ((uint32_t)(n)) +/* List of bitflags returned in status of GOLDFISH_PIPE_CMD_POLL command */ + +#define GOLDFISH_PIPE_POLL_IN (1 << 0) +#define GOLDFISH_PIPE_POLL_OUT (1 << 1) +#define GOLDFISH_PIPE_POLL_HUP (1 << 2) + +/* Possible status values used to signal errors */ + +#define GOLDFISH_PIPE_ERROR_INVAL -1 +#define GOLDFISH_PIPE_ERROR_AGAIN -2 +#define GOLDFISH_PIPE_ERROR_NOMEM -3 +#define GOLDFISH_PIPE_ERROR_IO -4 + +/* Bit-flags used to signal events from the emulator */ + +#define GOLDFISH_PIPE_WAKE_CLOSED (1 << 0) /* Emulator closed pipe */ +#define GOLDFISH_PIPE_WAKE_READ (1 << 1) /* Pipe can now be read from */ +#define GOLDFISH_PIPE_WAKE_WRITE (1 << 2) /* Pipe can now be written to */ +#define GOLDFISH_PIPE_WAKE_UNLOCK_DMA (1 << 3) /* Unlock this pipe's DMA buffer */ +#define GOLDFISH_PIPE_WAKE_UNLOCK_DMA_SHARED (1 << 4) /* Unlock DMA buffer of the pipe shared to this pipe */ + +/* Possible pipe closing reasons */ + +#define GOLDFISH_PIPE_CLOSE_GRACEFUL 0 /* Guest sent a close command */ +#define GOLDFISH_PIPE_CLOSE_REBOOT 1 /* Guest rebooted, we're closing the pipes */ +#define GOLDFISH_PIPE_CLOSE_LOAD_SNAPSHOT 2 /* Close old pipes on snapshot load */ +#define GOLDFISH_PIPE_CLOSE_ERROR 3 /* Some unrecoverable error on the pipe */ + +/* Register offset */ + +#define GOLDFISH_PIPE_REG_CMD 0 +#define GOLDFISH_PIPE_REG_SIGNAL_BUFFER_HIGH 4 +#define GOLDFISH_PIPE_REG_SIGNAL_BUFFER 8 +#define GOLDFISH_PIPE_REG_SIGNAL_BUFFER_COUNT 12 +#define GOLDFISH_PIPE_REG_OPEN_BUFFER_HIGH 20 +#define GOLDFISH_PIPE_REG_OPEN_BUFFER 24 +#define GOLDFISH_PIPE_REG_VERSION 36 +#define GOLDFISH_PIPE_REG_GET_SIGNALLED 48 + +/* Possible pipe command */ + +#define GOLDFISH_PIPE_CMD_OPEN 1 +#define GOLDFISH_PIPE_CMD_CLOSE 2 +#define GOLDFISH_PIPE_CMD_POLL 3 +#define GOLDFISH_PIPE_CMD_WRITE 4 +#define GOLDFISH_PIPE_CMD_WAKE_ON_WRITE 5 +#define GOLDFISH_PIPE_CMD_READ 6 +#define GOLDFISH_PIPE_CMD_WAKE_ON_READ 7 +#define GOLDFISH_PIPE_CMD_WAKE_ON_DONE_IO 8 + +/* Version number */ + +#define GOLDFISH_PIPE_DRIVER_VERSION 4 +#define GOLDFISH_PIPE_CURRENT_DEVICE_VERSION 2 + +#define GOLDFISH_PIPE_MAX_POLL_WAITERS 2 +#define GOLDFISH_PIPE_MAX_COMMAND_BUFFERS 1 +#define GOLDFISH_PIPE_MAX_SIGNALLED 16 +#define GOLDFISH_PIPE_MAX_PIPES 32 + +#define goldfish_pipe_putreg32(v, x) (*(FAR volatile uint32_t *)(x) = (v)) +#define goldfish_pipe_getreg32(x) (*(FAR volatile uint32_t *)(x)) + +#define goldfish_pipe_getupper32(n) ((uint32_t)(((n) >> 16) >> 16)) +#define goldfish_pipe_getlower32(n) ((uint32_t)(n)) #define BIT(nr) (1 << (nr)) @@ -52,846 +126,494 @@ * Private Types ****************************************************************************/ -/**************************************************************************** - * Update this when something changes in the driver's behavior so the host - * can benefit from knowing it - * - ****************************************************************************/ - -enum -{ - PIPE_DRIVER_VERSION = 4, - PIPE_CURRENT_DEVICE_VERSION = 2 -}; - -enum -{ - MAX_BUFFERS_PER_COMMAND = 336, - MAX_SIGNALLED_PIPES = 64, - INITIAL_PIPES_CAPACITY = 64 -}; - -struct goldfish_pipe_dev; - /* A per-pipe command structure, shared with the host */ -struct goldfish_pipe_command +struct goldfish_pipe_command_s { - int cmd; /* PipeCmdCode, guest -> host */ - int id; /* pipe id, guest -> host */ - int status; /* command execution status, host -> guest */ - int reserved; /* to pad to 64-bit boundary */ - union + int32_t cmd; /* PipeCmdCode, guest -> host */ + int32_t id; /* Pipe id, guest -> host */ + int32_t status; /* Command execution status, host -> guest */ + int32_t reserved; /* To pad to 64-bit boundary */ + + struct /* Parameters for GOLDFISH_PIPE_CMD_[READ|WRITE] */ { - /* Parameters for PIPE_CMD_{READ,WRITE} */ + uint32_t buffers_count; /* Number of buffers, guest -> host */ + int32_t consumed_size; /* Number of consumed bytes, host -> guest */ - struct - { - /* number of buffers, guest -> host */ - - uint32_t buffers_count; - - /* number of consumed bytes, host -> guest */ - - int consumed_size; - - /* buffer pointers, guest -> host */ - - uint64_t ptrs[MAX_BUFFERS_PER_COMMAND]; - - /* buffer sizes, guest -> host */ - - uint32_t sizes[MAX_BUFFERS_PER_COMMAND]; - } rw_params; - }; + uint64_t ptrs[GOLDFISH_PIPE_MAX_COMMAND_BUFFERS]; /* Buffer pointers, guest -> host */ + uint32_t sizes[GOLDFISH_PIPE_MAX_COMMAND_BUFFERS]; /* Buffer sizes, guest -> host */ + } + rw_params; }; /* A single signalled pipe information */ -struct signalled_pipe_buffer +struct goldfish_pipe_signalled_s { uint32_t id; uint32_t flags; }; -/* Parameters for the PIPE_CMD_OPEN command */ +/* Parameters for the GOLDFISH_PIPE_CMD_OPEN command */ -struct open_command_param +struct goldfish_pipe_open_param_s { uint64_t command_buffer_ptr; uint32_t rw_params_max_count; }; -/* Device-level set of buffers shared with the host */ - -struct goldfish_pipe_dev_buffers -{ - struct open_command_param open_command_params; - struct signalled_pipe_buffer - signalled_pipe_buffers[MAX_SIGNALLED_PIPES]; -}; - /* This data type models a given pipe instance */ -struct goldfish_pipe +struct goldfish_pipe_dev_s; + +struct goldfish_pipe_s { - /* pipe ID - index into goldfish_pipe_dev::pipes array */ - uint32_t id; - - /* The wake flags pipe is waiting for - * Note: not protected with any lock, uses atomic operations - * and barriers to make it thread-safe. - */ - - unsigned long flags; - - /* wake flags host have signalled, - * - protected by goldfish_pipe_dev::lock - */ - - unsigned long signalled_flags; - - /* A pointer to command buffer */ - - struct goldfish_pipe_command *command_buffer; - - /* doubly linked list of signalled pipes, protected by - * goldfish_pipe_dev::lock - */ - - struct goldfish_pipe *prev_signalled; - struct goldfish_pipe *next_signalled; - - /* A pipe's own lock. Protects the following: - * - *command_buffer - makes sure a command can safely write its - * parameters to the host and read the results back. - */ - + bool closed; + struct goldfish_pipe_command_s command; mutex_t lock; + sem_t wait_for_write; + sem_t wait_for_read; + FAR struct goldfish_pipe_dev_s *dev; + FAR struct pollfd *fds[GOLDFISH_PIPE_MAX_POLL_WAITERS]; +}; - /* A wake queue for sleeping until host signals an event */ - - sem_t wake_queue; - - /* Pointer to the parent goldfish_pipe_dev instance */ - - struct goldfish_pipe_dev *dev; +struct goldfish_pipe_dev_s +{ + spinlock_t lock; + FAR struct goldfish_pipe_s *pipes[GOLDFISH_PIPE_MAX_PIPES]; + struct goldfish_pipe_open_param_s open_params; + struct goldfish_pipe_signalled_s + pipes_signalled[GOLDFISH_PIPE_MAX_SIGNALLED]; + FAR uint8_t *base; }; /**************************************************************************** - * The global driver data. Holds a reference to the i/o page used to - * communicate with the emulator, and a wake queue for blocked tasks - * waiting to be awoken. - * + * Private Function Prototypes ****************************************************************************/ -struct goldfish_pipe_dev +static ssize_t goldfish_pipe_read(FAR struct file *filp, FAR char *buffer, + size_t bufflen); +static ssize_t goldfish_pipe_write(FAR struct file *filp, + FAR const char *buffer, size_t bufflen); +static int goldfish_pipe_poll(FAR struct file *filp, + FAR struct pollfd *fds, bool setup); +static int goldfish_pipe_open(FAR struct file *filep); +static int goldfish_pipe_close(FAR struct file *filep); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_goldfish_pipe_fops = { - /* Global device spinlock. Protects the following members: - * - pipes, pipes_capacity - * - [*pipes, *pipes + pipes_capacity) - array data - * - first_signalled_pipe, - * goldfish_pipe::prev_signalled, - * goldfish_pipe::next_signalled, - * goldfish_pipe::signalled_flags - all singnalled-related fields, - * in all allocated pipes - * - open_command_params - PIPE_CMD_OPEN-related buffers - * - * It looks like a lot of different fields, but the trick is that - * the only operation that happens often is the signalled pipes array - * manipulation. That's why it's OK for now to keep the rest of the - * fields under the same lock. If we notice too much contention because - * of PIPE_CMD_OPEN, then we should add a separate lock there. - */ - - spinlock_t lock; - mutex_t polllock; - - /* Array of the pipes of |pipes_capacity| elements, - * indexed by goldfish_pipe::id - */ - - struct goldfish_pipe **pipes; - uint32_t pipes_capacity; - - /* Pointers to the buffers host uses for interaction with this driver */ - - struct goldfish_pipe_dev_buffers *buffers; - - /* Head of a doubly linked list of signalled pipes */ - - struct goldfish_pipe *first_signalled_pipe; - - /* ptr to platform device's device struct */ - - struct device *pdev_dev; - - /* Some device-specific data */ - - int irq; - int version; - unsigned char *base; - - struct work_s work; - - struct pollfd **fds; + .read = goldfish_pipe_read, + .write = goldfish_pipe_write, + .poll = goldfish_pipe_poll, + .open = goldfish_pipe_open, + .close = goldfish_pipe_close, }; /**************************************************************************** * Private Functions ****************************************************************************/ -static int goldfish_pipe_cmd_locked(struct goldfish_pipe *pipe, - enum pipecmdcode cmd) -{ - pipe->command_buffer->cmd = cmd; - - /* failure by default */ - - pipe->command_buffer->status = PIPE_ERROR_INVAL; - putreg32(pipe->id, pipe->dev->base + PIPE_REG_CMD); - return pipe->command_buffer->status; -} - -static int goldfish_pipe_cmd(struct goldfish_pipe *pipe, - enum pipecmdcode cmd) -{ - int status; - - if (nxmutex_lock(&pipe->lock)) - { - return PIPE_ERROR_IO; - } - - status = goldfish_pipe_cmd_locked(pipe, cmd); - nxmutex_unlock(&pipe->lock); - - return status; -} - -/**************************************************************************** - * - * This function converts an error code returned by the emulator through - * the PIPE_REG_STATUS i/o register into a valid negative errno value. - * - ****************************************************************************/ - -static int goldfish_pipe_error_convert(int status) +static int goldfish_pipe_convert_status(int status) { switch (status) { - case PIPE_ERROR_AGAIN: + case GOLDFISH_PIPE_ERROR_INVAL: + return -EINVAL; + case GOLDFISH_PIPE_ERROR_AGAIN: return -EAGAIN; - case PIPE_ERROR_NOMEM: + case GOLDFISH_PIPE_ERROR_NOMEM: return -ENOMEM; - case PIPE_ERROR_IO: + case GOLDFISH_PIPE_ERROR_IO: return -EIO; default: - return -EINVAL; + return status; } } -/* Populate the call parameters, merging adjacent pages together */ - -static void populate_rw_params(unsigned long address, - unsigned long address_end, - struct goldfish_pipe_command *command) +static int goldfish_pipe_command_locked(FAR struct goldfish_pipe_s *pipe, + int cmd) { - command->rw_params.ptrs[0] = (uint64_t)address; - command->rw_params.sizes[0] = address_end - address; - command->rw_params.buffers_count = 1; + pipe->command.cmd = cmd; + pipe->command.status = GOLDFISH_PIPE_ERROR_INVAL; + goldfish_pipe_putreg32(pipe->id, pipe->dev->base + GOLDFISH_PIPE_REG_CMD); + return goldfish_pipe_convert_status(pipe->command.status); } -static int transfer_max_buffers(struct goldfish_pipe *pipe, - unsigned long address, - unsigned long address_end, - int is_write, - int *consumed_size, - int *status) +static int goldfish_pipe_command(FAR struct goldfish_pipe_s *pipe, int cmd) { - /* Serialize access to the pipe command buffers */ - - if (nxmutex_lock(&pipe->lock)) - { - return -ERESTART; - } - - populate_rw_params(address, address_end, - pipe->command_buffer); - - /* Transfer the data */ - - *status = goldfish_pipe_cmd_locked(pipe, - is_write ? - PIPE_CMD_WRITE : - PIPE_CMD_READ); - - *consumed_size = pipe->command_buffer->rw_params.consumed_size; - - nxmutex_unlock(&pipe->lock); - - return 0; -} - -static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) -{ - uint32_t wake_bit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; - - pipe->flags |= BIT(wake_bit); - - /* Tell the emulator we're going to wait for a wake event */ - - goldfish_pipe_cmd(pipe, - is_write ? - PIPE_CMD_WAKE_ON_WRITE : - PIPE_CMD_WAKE_ON_READ); - - while (BIT(wake_bit) & pipe->flags) - { - if (nxsem_wait(&pipe->wake_queue)) - { - return -ERESTART; - } - - if (BIT(BIT_CLOSED_ON_HOST) & pipe->flags) - { - return -EIO; - } - } - - return 0; -} - -static ssize_t goldfish_pipe_read_write(struct file *filp, - char *buffer, - size_t bufflen, - int is_write) -{ - struct goldfish_pipe *pipe = filp->f_priv; - int count = 0; - int ret = -EINVAL; - unsigned long address; - unsigned long address_end; - - /* If the emulator already closed the pipe, no need to go further */ - - if (BIT(BIT_CLOSED_ON_HOST) & pipe->flags) - { - return -EIO; - } - - /* Null reads or writes succeeds */ - - if (bufflen == 0) - { - return 0; - } - - address = (unsigned long)buffer; - address_end = address + bufflen; - - while (address < address_end) - { - int consumed_size; - int status; - - ret = transfer_max_buffers(pipe, address, address_end, is_write, - &consumed_size, &status); - if (ret < 0) - break; - - if (consumed_size > 0) - { - /* No matter what's the status, we've transferred something. */ - - count += consumed_size; - address += consumed_size; - } - - if (status > 0) - continue; - - if (status == 0) - { - /* EOF */ - - ret = 0; - break; - } - - if (count > 0) - { - /* An error occurred, but we already transferred - * something on one of the previous iterations. - * Just return what we already copied and log this - * err. - */ - - if (status != PIPE_ERROR_AGAIN) - syslog(LOG_INFO, "backend error %d on %s\n", - status, is_write ? "write" : "read"); - break; - } - - /* If the error is not PIPE_ERROR_AGAIN, or if we are in - * non-blocking mode, just return the error code. - */ - - if (status != PIPE_ERROR_AGAIN || (filp->f_oflags & O_NONBLOCK) != 0) - { - ret = goldfish_pipe_error_convert(status); - break; - } - - status = wait_for_host_signal(pipe, is_write); - - if (status < 0) - { - return status; - } - } - - if (count > 0) - { - return count; - } - - return ret; -} - -static ssize_t goldfish_pipe_read(FAR struct file *filp, FAR char *buffer, - size_t bufflen) -{ - return goldfish_pipe_read_write(filp, buffer, bufflen, - /* is_write */ 0); -} - -static ssize_t goldfish_pipe_write(FAR struct file *filp, - FAR const char *buffer, size_t bufflen) -{ - /* cast away the const */ - - char *no_const_buffer = (char *)buffer; - - return goldfish_pipe_read_write(filp, no_const_buffer, bufflen, - /* is_write */ 1); -} - -static int goldfish_pipe_poll(FAR struct file *filp, - FAR struct pollfd *fds, bool setup) -{ - FAR struct goldfish_pipe *pipe = filp->f_priv; - FAR struct inode *inode = filp->f_inode; - FAR struct goldfish_pipe_dev *dev = inode->i_private; - - pollevent_t mask = 0; - int status; int ret; - ret = nxmutex_lock(&dev->polllock); + ret = nxmutex_lock(&pipe->lock); if (ret < 0) { return ret; } + ret = goldfish_pipe_command_locked(pipe, cmd); + nxmutex_unlock(&pipe->lock); + + return ret; +} + +static int goldfish_pipe_wait(FAR struct goldfish_pipe_s *pipe, + bool is_write) +{ + int ret; + + ret = goldfish_pipe_command(pipe, is_write ? + GOLDFISH_PIPE_CMD_WAKE_ON_WRITE : + GOLDFISH_PIPE_CMD_WAKE_ON_READ); + if (ret < 0) + { + return ret; + } + + return nxsem_wait(is_write ? &pipe->wait_for_write : &pipe->wait_for_read); +} + +static ssize_t goldfish_pipe_transfer_one(FAR struct goldfish_pipe_s *pipe, + FAR char *buffer, size_t buflen, + bool is_write) +{ + ssize_t ret; + + ret = nxmutex_lock(&pipe->lock); + if (ret < 0) + { + return ret; + } + + pipe->command.rw_params.ptrs[0] = (uintptr_t)buffer; + pipe->command.rw_params.sizes[0] = buflen; + pipe->command.rw_params.buffers_count = 1; + + ret = goldfish_pipe_command_locked(pipe, + is_write ? GOLDFISH_PIPE_CMD_WRITE : GOLDFISH_PIPE_CMD_READ); + if (ret >= 0) + { + ret = pipe->command.rw_params.consumed_size; + } + + nxmutex_unlock(&pipe->lock); + return ret; +} + +static ssize_t goldfish_pipe_transfer(FAR struct file *filp, + FAR char *buffer, size_t bufflen, + bool is_write) +{ + FAR struct goldfish_pipe_s *pipe = filp->f_priv; + size_t count = 0; + int ret = 0; + + while (bufflen > 0) + { + /* If the emulator already closed the pipe, no need to go further */ + + if (pipe->closed) + { + ret = -EIO; + break; + } + + ret = goldfish_pipe_transfer_one(pipe, buffer, bufflen, is_write); + if (ret > 0) + { + buffer += ret; + bufflen -= ret; + count += ret; + continue; + } + + if (ret != -EAGAIN || count || (filp->f_oflags & O_NONBLOCK)) + { + break; + } + + ret = goldfish_pipe_wait(pipe, is_write); + if (ret < 0) + { + break; + } + } + + return count > 0 ? count : ret; +} + +static ssize_t goldfish_pipe_read(FAR struct file *filp, FAR char *buffer, + size_t bufflen) +{ + return goldfish_pipe_transfer(filp, buffer, bufflen, false); +} + +static ssize_t goldfish_pipe_write(FAR struct file *filp, + FAR const char *buffer, size_t bufflen) +{ + return goldfish_pipe_transfer(filp, (FAR char *)buffer, bufflen, true); +} + +static int goldfish_pipe_poll(FAR struct file *filp, + FAR struct pollfd *fds, bool setup) +{ if (setup) { - if (dev->fds[pipe->id] != NULL) + FAR struct goldfish_pipe_s *pipe = filp->f_priv; + FAR struct goldfish_pipe_dev_s *dev = pipe->dev; + pollevent_t eventset = 0; + irqstate_t flags; + int ret; + int i; + + ret = goldfish_pipe_command(pipe, GOLDFISH_PIPE_CMD_POLL); + if (ret < 0) { - fds->priv = NULL; - ret = -EBUSY; - goto errout; + return ret; } - status = goldfish_pipe_cmd(pipe, PIPE_CMD_POLL); - if (status < 0) + flags = spin_lock_irqsave(&dev->lock); + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < GOLDFISH_PIPE_MAX_POLL_WAITERS; i++) { - return -ERESTART; + /* Find an available slot */ + + if (pipe->fds[i] == NULL) + { + /* Bind the poll structure and this slot */ + + pipe->fds[i] = fds; + fds->priv = &pipe->fds[i]; + break; + } } - dev->fds[pipe->id] = fds; - fds->priv = &dev->fds[pipe->id]; + spin_unlock_irqrestore(&dev->lock, flags); + if (i >= GOLDFISH_PIPE_MAX_POLL_WAITERS) + { + return -EBUSY; + } - if (status & PIPE_POLL_IN) - mask |= EPOLLIN | EPOLLRDNORM; - if (status & PIPE_POLL_OUT) - mask |= EPOLLOUT | EPOLLWRNORM; - if (status & PIPE_POLL_HUP) - mask |= EPOLLHUP; - if (BIT(BIT_CLOSED_ON_HOST) & pipe->flags) - mask |= EPOLLERR; + if (ret & GOLDFISH_PIPE_POLL_IN) + { + eventset |= POLLIN; + } - if (mask) - poll_notify(&fds, 1, mask); + if (ret & GOLDFISH_PIPE_POLL_OUT) + { + eventset |= POLLOUT; + } + + if (ret & GOLDFISH_PIPE_POLL_HUP) + { + eventset |= POLLHUP; + } + + if (pipe->closed) + { + eventset |= POLLERR; + } + + if (eventset) + { + poll_notify(&fds, 1, eventset); + } + else + { + goldfish_pipe_command(pipe, GOLDFISH_PIPE_CMD_WAKE_ON_READ); + } } - else if (fds->priv != NULL) + else { /* This is a request to tear down the poll. */ FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; - /* Remove all memory of the poll setup */ + if (slot == NULL) + { + return -EINVAL; + } *slot = NULL; fds->priv = NULL; } -errout: - nxmutex_unlock(&dev->polllock); - return ret; + return 0; } -static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, - uint32_t id, uint32_t flags) -{ - struct goldfish_pipe *pipe; - - if (id >= dev->pipes_capacity) - { - return; - } - - pipe = dev->pipes[id]; - if (!pipe) - { - return; - } - - pipe->signalled_flags |= flags; - - if (pipe->prev_signalled || pipe->next_signalled || - dev->first_signalled_pipe == pipe) - { - /* already in the list */ - - return; - } - - pipe->next_signalled = dev->first_signalled_pipe; - - if (dev->first_signalled_pipe) - dev->first_signalled_pipe->prev_signalled = pipe; - - dev->first_signalled_pipe = pipe; -} - -static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev, - struct goldfish_pipe *pipe) -{ - if (pipe->prev_signalled) - pipe->prev_signalled->next_signalled = pipe->next_signalled; - - if (pipe->next_signalled) - pipe->next_signalled->prev_signalled = pipe->prev_signalled; - - if (pipe == dev->first_signalled_pipe) - dev->first_signalled_pipe = pipe->next_signalled; - - pipe->prev_signalled = NULL; - pipe->next_signalled = NULL; -} - -static struct goldfish_pipe -*signalled_pipes_pop_front(struct goldfish_pipe_dev *dev, int *wakes) -{ - struct goldfish_pipe *pipe; - irqstate_t flags; - - flags = spin_lock_irqsave(&dev->lock); - - pipe = dev->first_signalled_pipe; - - if (pipe) - { - *wakes = pipe->signalled_flags; - pipe->signalled_flags = 0; - - /* This is an optimized version of - * signalled_pipes_remove_locked() - * - We want to make it as fast as possible to - * wake the sleeping pipe operations faster. - */ - - dev->first_signalled_pipe = pipe->next_signalled; - if (dev->first_signalled_pipe) - dev->first_signalled_pipe->prev_signalled = NULL; - pipe->next_signalled = NULL; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - return pipe; -} - -static void goldfish_interrupt_task(FAR void *arg) -{ - /* Iterate over the signalled pipes and wake them one by one */ - - struct goldfish_pipe_dev *dev = arg; - struct goldfish_pipe *pipe; - int wakes; - - while ((pipe = signalled_pipes_pop_front(dev, &wakes)) != NULL) - { - if (wakes & PIPE_WAKE_CLOSED) - { - pipe->flags = 1 << BIT_CLOSED_ON_HOST; - } - else - { - if (wakes & PIPE_WAKE_READ) - pipe->flags &= ~BIT(BIT_WAKE_ON_READ); - if (wakes & PIPE_WAKE_WRITE) - pipe->flags &= ~BIT(BIT_WAKE_ON_WRITE); - } - - /* wake_up_interruptible() implies a write barrier, so don't - * explicitly add another one here. - */ - - nxsem_post(&pipe->wake_queue); - - if (nxmutex_lock(&dev->polllock) == OK) - { - if (wakes & PIPE_WAKE_READ) - poll_notify(&dev->fds[pipe->id], 1, EPOLLIN); - - if (wakes & PIPE_WAKE_WRITE) - poll_notify(&dev->fds[pipe->id], 1, EPOLLOUT); - - if (wakes & PIPE_WAKE_CLOSED) - poll_notify(&dev->fds[pipe->id], 1, EPOLLHUP); - - nxmutex_unlock(&dev->polllock); - } - } -} - -/**************************************************************************** - * The general idea of the (threaded) interrupt handling: - * - * 1.device raises an interrupt if there's at least one signalled pipe - * 2.IRQ handler reads the signalled pipes and their count from the device - * 3.device writes them into a shared buffer and returns the count - * it only resets the IRQ if it has returned all signalled pipes, - * otherwise it leaves it raised, so IRQ handler will be called - * again for the next chunk - * 4.IRQ handler adds all returned pipes to the device's signalled pipes list - * 5.IRQ handler defers processing the signalled pipes from the list in a - * separate context - * - ****************************************************************************/ - -static int goldfish_pipe_interrupt(int irq, void *dev_id, void *arg) -{ - uint32_t count; - uint32_t i; - irqstate_t flags; - uint32_t signalled_id; - uint32_t signalled_flags; - struct goldfish_pipe_dev *dev = arg; - - /* Request the signalled pipes from the device */ - - flags = spin_lock_irqsave(&dev->lock); - - count = getreg32(dev->base + PIPE_REG_GET_SIGNALLED); - - if (count == 0) - { - spin_unlock_irqrestore(&dev->lock, flags); - return OK; - } - - if (count > MAX_SIGNALLED_PIPES) - count = MAX_SIGNALLED_PIPES; - - for (i = 0; i < count; ++i) - { - signalled_id = dev->buffers->signalled_pipe_buffers[i].id; - signalled_flags = dev->buffers->signalled_pipe_buffers[i].flags; - signalled_pipes_add_locked(dev, signalled_id, signalled_flags); - } - - spin_unlock_irqrestore(&dev->lock, flags); - - work_queue(HPWORK, &dev->work, goldfish_interrupt_task, - dev, 0); - - return OK; -} - -static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) -{ - int id; - - for (id = 0; id < dev->pipes_capacity; ++id) - { - if (!dev->pipes[id]) - { - return id; - } - } - - /* Reallocate the array. - * Since get_free_pipe_id_locked runs with interrupts disabled, - * we don't want to make calls that could lead to sleep. - */ - - uint32_t new_capacity = 2 * dev->pipes_capacity; - struct goldfish_pipe **pipes = kmm_calloc(new_capacity, sizeof(*pipes)); - struct pollfd **fds = kmm_calloc(new_capacity, sizeof(*fds)); - if (!pipes || !fds) - { - return -ENOMEM; - } - - memcpy(pipes, dev->pipes, sizeof(*pipes) * dev->pipes_capacity); - kmm_free(dev->pipes); - dev->pipes = pipes; - - memcpy(fds, dev->fds, sizeof(*fds) * dev->pipes_capacity); - kmm_free(dev->fds); - dev->fds = fds; - - id = dev->pipes_capacity; - dev->pipes_capacity = new_capacity; - - return id; -} - -/** - * goldfish_pipe_open - open a channel to the AVD - * @inode: inode of device - * @file: file struct of opener - * - * Create a new pipe link between the emulator and the use application. - * Each new request produces a new pipe. - * - * Note: we use the pipe ID as a mux. All goldfish emulations are 32bit - * right now so this is fine. A move to 64bit will need this addressing - */ - static int goldfish_pipe_open(FAR struct file *filep) { - FAR struct inode *inode = filep->f_inode; - FAR struct goldfish_pipe_dev *dev = inode->i_private; + FAR struct goldfish_pipe_dev_s *dev = filep->f_inode->i_private; + FAR struct goldfish_pipe_s *pipe; irqstate_t flags; + int ret = -ENFILE; int id; - int status; - uint64_t *ptr; - uint32_t *count; - - /* Allocate new pipe kernel object */ - - struct goldfish_pipe *pipe = kmm_zalloc(sizeof(*pipe)); - - if (!pipe) + pipe = kmm_zalloc(sizeof(*pipe)); + if (pipe == NULL) { return -ENOMEM; } pipe->dev = dev; nxmutex_init(&pipe->lock); - nxsem_init(&pipe->wake_queue, 0, 0); - - /* Command buffer needs to be allocated on its own page to make sure - * it is physically contiguous in host's address space. - */ - - pipe->command_buffer = (struct goldfish_pipe_command *) - kmm_zalloc(sizeof(struct goldfish_pipe_command)); - if (!pipe->command_buffer) - { - status = -ENOMEM; - goto err_pipe; - } + nxsem_init(&pipe->wait_for_read, 0, 0); + nxsem_init(&pipe->wait_for_write, 0, 0); flags = spin_lock_irqsave(&dev->lock); - id = get_free_pipe_id_locked(dev); - if (id < 0) + for (id = 0; id < GOLDFISH_PIPE_MAX_PIPES; id++) { - status = id; - goto err_id_locked; + if (dev->pipes[id] == NULL) + { + break; + } + } + + if (id >= GOLDFISH_PIPE_MAX_PIPES) + { + goto out; } - dev->pipes[id] = pipe; pipe->id = id; - pipe->command_buffer->id = id; + pipe->command.id = id; /* Now tell the emulator we're opening a new pipe. */ - count = &dev->buffers->open_command_params.rw_params_max_count; - ptr = &dev->buffers->open_command_params.command_buffer_ptr; - *count = MAX_BUFFERS_PER_COMMAND; - *ptr = (uintptr_t)pipe->command_buffer; + dev->open_params.command_buffer_ptr = (uintptr_t)&pipe->command; + dev->open_params.rw_params_max_count = GOLDFISH_PIPE_MAX_COMMAND_BUFFERS; - status = goldfish_pipe_cmd_locked(pipe, PIPE_CMD_OPEN); - spin_unlock_irqrestore(&dev->lock, flags); - - if (status < 0) + ret = goldfish_pipe_command_locked(pipe, GOLDFISH_PIPE_CMD_OPEN); + if (ret < 0) { - goto err_cmd; + goto out; } - /* All is done, save the pipe into the file's private data field */ + dev->pipes[id] = pipe; + spin_unlock_irqrestore(&dev->lock, flags); filep->f_priv = pipe; - return 0; -err_cmd: - flags = spin_lock_irqsave(&dev->lock); - dev->pipes[id] = NULL; - -err_id_locked: +out: spin_unlock_irqrestore(&dev->lock, flags); - kmm_free(pipe->command_buffer); - -err_pipe: + nxsem_destroy(&pipe->wait_for_write); + nxsem_destroy(&pipe->wait_for_read); + nxmutex_destroy(&pipe->lock); kmm_free(pipe); - return status; + return ret; } -static int goldfish_pipe_release(FAR struct file *filp) +static int goldfish_pipe_close(FAR struct file *filp) { + FAR struct goldfish_pipe_s *pipe = filp->f_priv; + FAR struct goldfish_pipe_dev_s *dev = pipe->dev; irqstate_t flags; - struct goldfish_pipe *pipe = filp->f_priv; - struct goldfish_pipe_dev *dev = pipe->dev; - /* The guest is closing the channel, so tell the emulator right now */ - - goldfish_pipe_cmd(pipe, PIPE_CMD_CLOSE); + goldfish_pipe_command(pipe, GOLDFISH_PIPE_CMD_CLOSE); flags = spin_lock_irqsave(&dev->lock); dev->pipes[pipe->id] = NULL; - dev->fds[pipe->id] = NULL; - signalled_pipes_remove_locked(dev, pipe); spin_unlock_irqrestore(&dev->lock, flags); filp->f_priv = NULL; - kmm_free(pipe->command_buffer); + + nxsem_destroy(&pipe->wait_for_write); + nxsem_destroy(&pipe->wait_for_read); + nxmutex_destroy(&pipe->lock); kmm_free(pipe); return 0; } -static const struct file_operations g_goldfishpipe_fops = +static void goldfish_pipe_wake(FAR struct goldfish_pipe_s *pipe, + bool is_write) { - .read = goldfish_pipe_read, - .write = goldfish_pipe_write, - .poll = goldfish_pipe_poll, - .open = goldfish_pipe_open, - .close = goldfish_pipe_release, -}; + FAR sem_t *sem = is_write ? &pipe->wait_for_write : &pipe->wait_for_read; + int sval; -static void write_pa_addr(void *addr, void *portl, void *porth) + while (nxsem_get_value(sem, &sval) >= 0 && sval <= 0) + { + nxsem_post(sem); + } +} + +static int goldfish_pipe_interrupt(int irq, FAR void *context, FAR void *arg) { - putreg32(upper_32_bits((uintptr_t)addr), porth); - putreg32(lower_32_bits((uintptr_t)addr), portl); + FAR struct goldfish_pipe_dev_s *dev = arg; + irqstate_t irqflags; + uint32_t count; + uint32_t i; + + irqflags = spin_lock_irqsave(&dev->lock); + + count = goldfish_pipe_getreg32(dev->base + + GOLDFISH_PIPE_REG_GET_SIGNALLED); + if (count > GOLDFISH_PIPE_MAX_SIGNALLED) + { + count = GOLDFISH_PIPE_MAX_SIGNALLED; + } + + for (i = 0; i < count; i++) + { + uint32_t id = dev->pipes_signalled[i].id; + uint32_t flags = dev->pipes_signalled[i].flags; + FAR struct goldfish_pipe_s *pipe = dev->pipes[id]; + pollevent_t eventset = 0; + + if (pipe == NULL) + { + continue; + } + + if (flags & GOLDFISH_PIPE_WAKE_READ) + { + eventset |= POLLIN; + } + + if (flags & GOLDFISH_PIPE_WAKE_WRITE) + { + eventset |= POLLOUT; + } + + if (flags & GOLDFISH_PIPE_WAKE_CLOSED) + { + eventset |= POLLERR; + dev->pipes[id]->closed = true; + } + + if (eventset & (POLLIN | POLLERR)) + { + goldfish_pipe_wake(pipe, false); + } + + if (eventset & (POLLOUT | POLLERR)) + { + goldfish_pipe_wake(pipe, true); + } + + if (eventset) + { + poll_notify(pipe->fds, GOLDFISH_PIPE_MAX_POLL_WAITERS, eventset); + } + } + + spin_unlock_irqrestore(&dev->lock, irqflags); + return 0; +} + +static void goldfish_pipe_write_addr(FAR void *addr, + FAR void *portl, FAR void *porth) +{ + goldfish_pipe_putreg32(goldfish_pipe_getupper32((uintptr_t)addr), porth); + goldfish_pipe_putreg32(goldfish_pipe_getlower32((uintptr_t)addr), portl); } /**************************************************************************** @@ -899,94 +621,67 @@ static void write_pa_addr(void *addr, void *portl, void *porth) ****************************************************************************/ /**************************************************************************** - * Name: goldfishpipe_register + * Name: goldfish_pipe_register * * Description: * register /dev/goldfish_pipe device * ****************************************************************************/ -int goldfish_pipe_register(void *base, int irq) +int goldfish_pipe_register(FAR void *base, int irq) { - FAR struct goldfish_pipe_dev *dev; - int ret; + FAR struct goldfish_pipe_dev_s *dev; + uint32_t version; + int ret = -ENOTSUP; /* Allocate and initialize a new device structure instance */ - dev = (FAR struct goldfish_pipe_dev *)kmm_zalloc(sizeof(*dev)); + dev = (FAR struct goldfish_pipe_dev_s *)kmm_zalloc(sizeof(*dev)); if (dev == NULL) { return -ENOMEM; } - dev->base = (unsigned char *)base; - dev->irq = irq; + spin_lock_init(&dev->lock); + dev->base = (FAR uint8_t *)base; - nxmutex_init(&dev->polllock); - spin_initialize(&dev->lock, 0); + /* Exchange the versions with the host device */ - putreg32(PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION); - dev->version = getreg32(dev->base + PIPE_REG_VERSION); - if (dev->version < PIPE_CURRENT_DEVICE_VERSION) + goldfish_pipe_putreg32(GOLDFISH_PIPE_DRIVER_VERSION, + dev->base + GOLDFISH_PIPE_REG_VERSION); + version = goldfish_pipe_getreg32(dev->base + GOLDFISH_PIPE_REG_VERSION); + if (version < GOLDFISH_PIPE_CURRENT_DEVICE_VERSION) { - return -EINVAL; + goto out; } - ret = irq_attach(dev->irq, goldfish_pipe_interrupt, dev); + ret = irq_attach(irq, goldfish_pipe_interrupt, dev); if (ret < 0) { - syslog(LOG_INFO, "attach irq failed\n"); - return ret; + goto out; } - up_enable_irq(dev->irq); - - dev->first_signalled_pipe = NULL; - dev->pipes_capacity = INITIAL_PIPES_CAPACITY; - dev->pipes = kmm_calloc(dev->pipes_capacity, sizeof(*dev->pipes)); - - if (!dev->pipes) - { - return -ENOMEM; - } - - dev->fds = kmm_calloc(dev->pipes_capacity, sizeof(*dev->fds)); - - if (!dev->fds) - { - return -ENOMEM; - } - - /* We're going to pass two buffers, open_command_params and - * signalled_pipe_buffers, to the host. This means each of those buffers - * needs to be contained in a single physical page. The easiest choice - * is to just allocate a page and place the buffers in it. - */ - - dev->buffers = (struct goldfish_pipe_dev_buffers *) - kmm_zalloc(sizeof(struct goldfish_pipe_dev_buffers)); - - if (!dev->buffers) - { - return -ENOMEM; - } + up_enable_irq(irq); /* Send the buffer addresses to the host */ - write_pa_addr(&dev->buffers->signalled_pipe_buffers, - dev->base + PIPE_REG_SIGNAL_BUFFER, - dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); + goldfish_pipe_write_addr(&dev->pipes_signalled, + dev->base + GOLDFISH_PIPE_REG_SIGNAL_BUFFER, + dev->base + GOLDFISH_PIPE_REG_SIGNAL_BUFFER_HIGH); - putreg32(MAX_SIGNALLED_PIPES, - dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); + goldfish_pipe_putreg32(GOLDFISH_PIPE_MAX_SIGNALLED, + dev->base + GOLDFISH_PIPE_REG_SIGNAL_BUFFER_COUNT); - write_pa_addr(&dev->buffers->open_command_params, - dev->base + PIPE_REG_OPEN_BUFFER, - dev->base + PIPE_REG_OPEN_BUFFER_HIGH); + goldfish_pipe_write_addr(&dev->open_params, + dev->base + GOLDFISH_PIPE_REG_OPEN_BUFFER, + dev->base + GOLDFISH_PIPE_REG_OPEN_BUFFER_HIGH); /* Register the pipe device */ - return register_driver("/dev/goldfish_pipe", &g_goldfishpipe_fops, - 0666, - (FAR void *)dev); + return register_driver("/dev/goldfish_pipe", + &g_goldfish_pipe_fops, 0666, dev); + +out: + kmm_free(dev); + return ret; } diff --git a/drivers/misc/goldfish_pipe_qemu.h b/drivers/misc/goldfish_pipe_qemu.h deleted file mode 100644 index 8e786bff4e..0000000000 --- a/drivers/misc/goldfish_pipe_qemu.h +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** - * drivers/misc/goldfish_pipe_qemu.h - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -#ifndef __GOLDFISH_PIPE_QEMU_H -#define __GOLDFISH_PIPE_QEMU_H - -/* List of bitflags returned in status of CMD_POLL command */ - -enum pipepollflags -{ - PIPE_POLL_IN = 1 << 0, - PIPE_POLL_OUT = 1 << 1, - PIPE_POLL_HUP = 1 << 2 -}; - -/* Possible status values used to signal errors */ - -enum pipeerrors -{ - PIPE_ERROR_INVAL = -1, - PIPE_ERROR_AGAIN = -2, - PIPE_ERROR_NOMEM = -3, - PIPE_ERROR_IO = -4 -}; - -/* Bit-flags used to signal events from the emulator */ - -enum pipewakeflags -{ - /* emulator closed pipe */ - - PIPE_WAKE_CLOSED = 1 << 0, - - /* pipe can now be read from */ - - PIPE_WAKE_READ = 1 << 1, - - /* pipe can now be written to */ - - PIPE_WAKE_WRITE = 1 << 2, - - /* unlock this pipe's DMA buffer */ - - PIPE_WAKE_UNLOCK_DMA = 1 << 3, - - /* unlock DMA buffer of the pipe shared to this pipe */ - - PIPE_WAKE_UNLOCK_DMA_SHARED = 1 << 4, -}; - -/* Possible pipe closing reasons */ - -enum pipeclosereason -{ - /* guest sent a close command */ - - PIPE_CLOSE_GRACEFUL = 0, - - /* guest rebooted, we're closing the pipes */ - - PIPE_CLOSE_REBOOT = 1, - - /* close old pipes on snapshot load */ - - PIPE_CLOSE_LOAD_SNAPSHOT = 2, - - /* some unrecoverable error on the pipe */ - - PIPE_CLOSE_ERROR = 3, -}; - -/* Bit flags for the 'flags' field */ - -enum pipeflagsbits -{ - BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */ - BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */ - BIT_WAKE_ON_READ = 2, /* want to be woken on reads */ -}; - -enum piperegs -{ - PIPE_REG_CMD = 0, - - PIPE_REG_SIGNAL_BUFFER_HIGH = 4, - PIPE_REG_SIGNAL_BUFFER = 8, - PIPE_REG_SIGNAL_BUFFER_COUNT = 12, - - PIPE_REG_OPEN_BUFFER_HIGH = 20, - PIPE_REG_OPEN_BUFFER = 24, - - PIPE_REG_VERSION = 36, - - PIPE_REG_GET_SIGNALLED = 48, -}; - -enum pipecmdcode -{ - /* to be used by the pipe device itself */ - - PIPE_CMD_OPEN = 1, - - PIPE_CMD_CLOSE, - PIPE_CMD_POLL, - PIPE_CMD_WRITE, - PIPE_CMD_WAKE_ON_WRITE, - PIPE_CMD_READ, - PIPE_CMD_WAKE_ON_READ, - - /** - * TODO(zyy): implement a deferred read/write execution to allow - * parallel processing of pipe operations on the host. - */ - - PIPE_CMD_WAKE_ON_DONE_IO, -}; - -#endif /* __GOLDFISH_PIPE_QEMU_H */ diff --git a/include/nuttx/drivers/goldfish_pipe.h b/include/nuttx/drivers/goldfish_pipe.h index 73f146d184..90120c5757 100644 --- a/include/nuttx/drivers/goldfish_pipe.h +++ b/include/nuttx/drivers/goldfish_pipe.h @@ -51,7 +51,7 @@ extern "C" ****************************************************************************/ #ifdef CONFIG_GOLDFISH_PIPE -int goldfish_pipe_register(void *base, int irq); +int goldfish_pipe_register(FAR void *base, int irq); #endif #undef EXTERN