868b17bc5a
x86_64 uses 4-5G virtual addresses, we need to convert them into physical addresses and pass them to qemu, otherwise qemu will fail to map Signed-off-by: liwenxiang1 <liwenxiang1@xiaomi.com>
684 lines
19 KiB
C
684 lines
19 KiB
C
/****************************************************************************
|
|
* drivers/misc/goldfish_pipe.c
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* 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 = "<pipename>";
|
|
* if (write(fd, msg, strlen(msg) + 1) < 0) {
|
|
* ... could not connect to <pipename> service
|
|
* close(fd);
|
|
* }
|
|
*
|
|
* After this, simply read() and write() to communicate with the service.
|
|
*/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/spinlock.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* 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))
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* A per-pipe command structure, shared with the host */
|
|
|
|
struct goldfish_pipe_command_s
|
|
{
|
|
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] */
|
|
{
|
|
uint32_t buffers_count; /* Number of buffers, guest -> host */
|
|
int32_t consumed_size; /* Number of consumed bytes, host -> guest */
|
|
|
|
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 goldfish_pipe_signalled_s
|
|
{
|
|
uint32_t id;
|
|
uint32_t flags;
|
|
};
|
|
|
|
/* Parameters for the GOLDFISH_PIPE_CMD_OPEN command */
|
|
|
|
struct goldfish_pipe_open_param_s
|
|
{
|
|
uint64_t command_buffer_ptr;
|
|
uint32_t rw_params_max_count;
|
|
};
|
|
|
|
/* This data type models a given pipe instance */
|
|
|
|
struct goldfish_pipe_dev_s;
|
|
|
|
struct goldfish_pipe_s
|
|
{
|
|
uint32_t id;
|
|
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];
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
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 =
|
|
{
|
|
.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_convert_status(int status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case GOLDFISH_PIPE_ERROR_INVAL:
|
|
return -EINVAL;
|
|
case GOLDFISH_PIPE_ERROR_AGAIN:
|
|
return -EAGAIN;
|
|
case GOLDFISH_PIPE_ERROR_NOMEM:
|
|
return -ENOMEM;
|
|
case GOLDFISH_PIPE_ERROR_IO:
|
|
return -EIO;
|
|
default:
|
|
return status;
|
|
}
|
|
}
|
|
|
|
static int goldfish_pipe_command_locked(FAR struct goldfish_pipe_s *pipe,
|
|
int cmd)
|
|
{
|
|
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 goldfish_pipe_command(FAR struct goldfish_pipe_s *pipe, int cmd)
|
|
{
|
|
int ret;
|
|
|
|
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] = up_addrenv_va_to_pa(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)
|
|
{
|
|
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)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
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++)
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
if (i >= GOLDFISH_PIPE_MAX_POLL_WAITERS)
|
|
{
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (ret & GOLDFISH_PIPE_POLL_IN)
|
|
{
|
|
eventset |= POLLIN;
|
|
}
|
|
|
|
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
|
|
{
|
|
/* This is a request to tear down the poll. */
|
|
|
|
FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv;
|
|
|
|
if (slot == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
*slot = NULL;
|
|
fds->priv = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int goldfish_pipe_open(FAR struct file *filep)
|
|
{
|
|
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;
|
|
|
|
pipe = kmm_zalloc(sizeof(*pipe));
|
|
if (pipe == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pipe->dev = dev;
|
|
nxmutex_init(&pipe->lock);
|
|
nxsem_init(&pipe->wait_for_read, 0, 0);
|
|
nxsem_init(&pipe->wait_for_write, 0, 0);
|
|
|
|
flags = spin_lock_irqsave(&dev->lock);
|
|
|
|
for (id = 0; id < GOLDFISH_PIPE_MAX_PIPES; id++)
|
|
{
|
|
if (dev->pipes[id] == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (id >= GOLDFISH_PIPE_MAX_PIPES)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
pipe->id = id;
|
|
pipe->command.id = id;
|
|
|
|
/* Now tell the emulator we're opening a new pipe. */
|
|
|
|
dev->open_params.command_buffer_ptr = up_addrenv_va_to_pa(&pipe->command);
|
|
dev->open_params.rw_params_max_count = GOLDFISH_PIPE_MAX_COMMAND_BUFFERS;
|
|
|
|
ret = goldfish_pipe_command_locked(pipe, GOLDFISH_PIPE_CMD_OPEN);
|
|
if (ret < 0)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
dev->pipes[id] = pipe;
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
filep->f_priv = pipe;
|
|
return 0;
|
|
|
|
out:
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
nxsem_destroy(&pipe->wait_for_write);
|
|
nxsem_destroy(&pipe->wait_for_read);
|
|
nxmutex_destroy(&pipe->lock);
|
|
kmm_free(pipe);
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
goldfish_pipe_command(pipe, GOLDFISH_PIPE_CMD_CLOSE);
|
|
|
|
flags = spin_lock_irqsave(&dev->lock);
|
|
dev->pipes[pipe->id] = NULL;
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
filp->f_priv = NULL;
|
|
|
|
nxsem_destroy(&pipe->wait_for_write);
|
|
nxsem_destroy(&pipe->wait_for_read);
|
|
nxmutex_destroy(&pipe->lock);
|
|
kmm_free(pipe);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void goldfish_pipe_wake(FAR struct goldfish_pipe_s *pipe,
|
|
bool is_write)
|
|
{
|
|
FAR sem_t *sem = is_write ? &pipe->wait_for_write : &pipe->wait_for_read;
|
|
int sval;
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
uintptr_t paddr = up_addrenv_va_to_pa(addr);
|
|
goldfish_pipe_putreg32((paddr >> 16) >> 16, porth);
|
|
goldfish_pipe_putreg32(paddr, portl);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: goldfish_pipe_register
|
|
*
|
|
* Description:
|
|
* register /dev/goldfish_pipe device
|
|
*
|
|
****************************************************************************/
|
|
|
|
int goldfish_pipe_register(FAR void *base, int irq)
|
|
{
|
|
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_s *)kmm_zalloc(sizeof(*dev));
|
|
if (dev == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
spin_lock_init(&dev->lock);
|
|
dev->base = (FAR uint8_t *)base;
|
|
|
|
/* Exchange the versions with the host device */
|
|
|
|
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)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
ret = irq_attach(irq, goldfish_pipe_interrupt, dev);
|
|
if (ret < 0)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
up_enable_irq(irq);
|
|
|
|
/* Send the buffer addresses to the host */
|
|
|
|
goldfish_pipe_write_addr(&dev->pipes_signalled,
|
|
dev->base + GOLDFISH_PIPE_REG_SIGNAL_BUFFER,
|
|
dev->base + GOLDFISH_PIPE_REG_SIGNAL_BUFFER_HIGH);
|
|
|
|
goldfish_pipe_putreg32(GOLDFISH_PIPE_MAX_SIGNALLED,
|
|
dev->base + GOLDFISH_PIPE_REG_SIGNAL_BUFFER_COUNT);
|
|
|
|
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_goldfish_pipe_fops, 0666, dev);
|
|
|
|
out:
|
|
kmm_free(dev);
|
|
return ret;
|
|
}
|