pci/pci_uio_ivshmem: pci uio ivshmem msix interrupt support
1. Change pci uio ivshmem driver from pci bus based tp pci-ivshmem bus based; 2. Add the interrupt support by usin the pci_ivshmem API; Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
This commit is contained in:
parent
733a68002c
commit
7a495fabb6
@ -47,10 +47,22 @@ config PCI_IVSHMEM
|
||||
config PCI_UIO_IVSHMEM
|
||||
bool "Enable uio ivshmem driver support"
|
||||
default n
|
||||
depends on PCI_IVSHMEM
|
||||
---help---
|
||||
When this option is enabled, char ivshmem driver will register
|
||||
char device with name: "/dev/uioX" to the VFS, then
|
||||
application can open this device and use `mmap()` to get the
|
||||
share memory provided by ivshmem device.
|
||||
|
||||
config PCI_UIO_IVSHMEM_IDTABLE
|
||||
string "The id table of uio ivshmem device"
|
||||
depends on PCI_UIO_IVSHMEM
|
||||
---help---
|
||||
"id0;id1;id2", e.g.: "0;1;2"
|
||||
|
||||
config PCI_UIO_IVSHMEM_NPOLLWAITERS
|
||||
int "PCI uio ivshmem poll waiter number"
|
||||
default 2
|
||||
depends on PCI_UIO_IVSHMEM
|
||||
|
||||
endif # PCI
|
||||
|
@ -26,29 +26,61 @@
|
||||
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <nuttx/fs/fs.h>
|
||||
#include <nuttx/mm/map.h>
|
||||
#include <nuttx/pci/pci.h>
|
||||
|
||||
#include "pci_drivers.h"
|
||||
#include <nuttx/pci/pci_ivshmem.h>
|
||||
#include <nuttx/semaphore.h>
|
||||
#include <nuttx/spinlock.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define UIO_IVSHMEM_SHMEM_BAR 2
|
||||
#define dev_to_udev(dev) \
|
||||
((FAR struct uio_ivshmem_dev_s *)ivshmem_get_driver(dev))
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct uio_ivshmem_notify_s;
|
||||
typedef CODE void (*uio_ivshmem_notify_t)(
|
||||
FAR struct uio_ivshmem_notify_s *notify, int32_t newevent);
|
||||
|
||||
struct uio_ivshmem_notify_s
|
||||
{
|
||||
struct list_node node;
|
||||
uio_ivshmem_notify_t cb;
|
||||
};
|
||||
|
||||
struct uio_ivshmem_read_s
|
||||
{
|
||||
struct uio_ivshmem_notify_s notify;
|
||||
sem_t wait;
|
||||
};
|
||||
|
||||
struct uio_ivshmem_poll_s
|
||||
{
|
||||
struct uio_ivshmem_notify_s notify;
|
||||
FAR struct pollfd *fds;
|
||||
FAR void **ppriv;
|
||||
};
|
||||
|
||||
struct uio_ivshmem_dev_s
|
||||
{
|
||||
FAR void *shmem;
|
||||
size_t shmem_size;
|
||||
char name[32];
|
||||
struct ivshmem_driver_s drv;
|
||||
FAR struct ivshmem_device_s *dev;
|
||||
char name[32];
|
||||
|
||||
spinlock_t lock;
|
||||
int32_t event_count;
|
||||
struct list_node notify_list;
|
||||
struct uio_ivshmem_poll_s polls[CONFIG_PCI_UIO_IVSHMEM_NPOLLWAITERS];
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
@ -66,9 +98,13 @@ static int uio_ivshmem_unmap(FAR struct task_group_s *group,
|
||||
FAR void *start, size_t length);
|
||||
static int uio_ivshmem_mmap(FAR struct file *filep,
|
||||
FAR struct mm_map_entry_s *map);
|
||||
static int uio_ivshmem_poll(FAR struct file *filep, FAR struct pollfd *fds,
|
||||
bool setup);
|
||||
|
||||
static int uio_ivshmem_probe(FAR struct pci_device_s *dev);
|
||||
static void uio_ivshmem_remove(FAR struct pci_device_s *dev);
|
||||
static int uio_ivshmem_probe(FAR struct ivshmem_device_s *dev);
|
||||
static void uio_ivshmem_remove(FAR struct ivshmem_device_s *dev);
|
||||
|
||||
static int uio_ivshmem_interrupt(int irq, FAR void *context, FAR void *arg);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
@ -84,38 +120,61 @@ static const struct file_operations g_uio_ivshmem_fops =
|
||||
NULL, /* ioctl */
|
||||
uio_ivshmem_mmap, /* mmap */
|
||||
NULL, /* truncate */
|
||||
NULL /* poll */
|
||||
uio_ivshmem_poll /* poll */
|
||||
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
||||
, NULL /* unlink */
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct pci_device_id_s g_uio_ivshmem_ids[] =
|
||||
{
|
||||
{ PCI_DEVICE(0x1af4, 0x1110) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct pci_driver_s g_uio_ivshmem_drv =
|
||||
{
|
||||
g_uio_ivshmem_ids, /* PCI id_tables */
|
||||
uio_ivshmem_probe, /* Probe function */
|
||||
uio_ivshmem_remove, /* Remove function */
|
||||
};
|
||||
|
||||
static int g_uio_ivshmem_idx = 0;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_add_notify
|
||||
****************************************************************************/
|
||||
|
||||
static inline void
|
||||
uio_ivshmem_add_notify(FAR struct uio_ivshmem_dev_s *dev,
|
||||
FAR struct uio_ivshmem_notify_s *notify)
|
||||
{
|
||||
irqstate_t flags;
|
||||
|
||||
flags = spin_lock_irqsave(&dev->lock);
|
||||
list_add_tail(&dev->notify_list, ¬ify->node);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_remove_notify
|
||||
****************************************************************************/
|
||||
|
||||
static inline void
|
||||
uio_ivshmem_remove_notify(FAR struct uio_ivshmem_dev_s *dev,
|
||||
FAR struct uio_ivshmem_notify_s *notify)
|
||||
{
|
||||
irqstate_t flags;
|
||||
|
||||
flags = spin_lock_irqsave(&dev->lock);
|
||||
list_delete(¬ify->node);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_open
|
||||
****************************************************************************/
|
||||
|
||||
static int uio_ivshmem_open(FAR struct file *filep)
|
||||
{
|
||||
UNUSED(filep);
|
||||
FAR struct uio_ivshmem_dev_s *dev;
|
||||
irqstate_t flags;
|
||||
|
||||
DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL);
|
||||
dev = filep->f_inode->i_private;
|
||||
|
||||
flags = spin_lock_irqsave(&dev->lock);
|
||||
filep->f_priv = (FAR void *)(uintptr_t)dev->event_count;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -129,6 +188,19 @@ static int uio_ivshmem_close(FAR struct file *filep)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_notify_read
|
||||
****************************************************************************/
|
||||
|
||||
static void uio_ivshmem_notify_read(FAR struct uio_ivshmem_notify_s *notify,
|
||||
int32_t newevent)
|
||||
{
|
||||
FAR struct uio_ivshmem_read_s *read =
|
||||
(FAR struct uio_ivshmem_read_s *)notify;
|
||||
|
||||
nxsem_post(&read->wait);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_read
|
||||
****************************************************************************/
|
||||
@ -136,9 +208,49 @@ static int uio_ivshmem_close(FAR struct file *filep)
|
||||
static ssize_t uio_ivshmem_read(FAR struct file *filep, FAR char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
UNUSED(filep);
|
||||
UNUSED(buffer);
|
||||
return buflen;
|
||||
FAR struct uio_ivshmem_dev_s *dev;
|
||||
struct uio_ivshmem_read_s read;
|
||||
irqstate_t flags;
|
||||
int ret = OK;
|
||||
|
||||
if (buflen != sizeof(int32_t))
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL);
|
||||
dev = filep->f_inode->i_private;
|
||||
|
||||
nxsem_init(&read.wait, 0, 0);
|
||||
read.notify.cb = uio_ivshmem_notify_read;
|
||||
uio_ivshmem_add_notify(dev, &read.notify);
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
flags = spin_lock_irqsave(&dev->lock);
|
||||
if (dev->event_count != (int32_t)(uintptr_t)filep->f_priv)
|
||||
{
|
||||
filep->f_priv = (FAR void *)(uintptr_t)dev->event_count;
|
||||
memcpy(buffer, &dev->event_count, sizeof(int32_t));
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
ret = sizeof(int32_t);
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (filep->f_oflags & O_NONBLOCK)
|
||||
{
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
nxsem_wait_uninterruptible(&read.wait);
|
||||
}
|
||||
|
||||
uio_ivshmem_remove_notify(dev, &read.notify);
|
||||
nxsem_destroy(&read.wait);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -148,9 +260,24 @@ static ssize_t uio_ivshmem_read(FAR struct file *filep, FAR char *buffer,
|
||||
static ssize_t uio_ivshmem_write(FAR struct file *filep,
|
||||
FAR const char *buffer, size_t buflen)
|
||||
{
|
||||
UNUSED(filep);
|
||||
UNUSED(buffer);
|
||||
return buflen;
|
||||
FAR struct uio_ivshmem_dev_s *dev;
|
||||
int32_t irq_on;
|
||||
|
||||
DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL);
|
||||
dev = filep->f_inode->i_private;
|
||||
|
||||
if (buflen != sizeof(int32_t))
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_on = *(FAR int32_t *)buffer;
|
||||
if (irq_on != 0 && irq_on != 1)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ivshmem_control_irq(dev->dev, irq_on);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -199,22 +326,23 @@ static int uio_ivshmem_unmap(FAR struct task_group_s *group,
|
||||
static int uio_ivshmem_mmap(FAR struct file *filep,
|
||||
FAR struct mm_map_entry_s *map)
|
||||
{
|
||||
FAR struct uio_ivshmem_dev_s *priv;
|
||||
FAR struct uio_ivshmem_dev_s *dev;
|
||||
size_t shmem_size;
|
||||
FAR void *shmem;
|
||||
|
||||
DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL);
|
||||
dev = filep->f_inode->i_private;
|
||||
|
||||
/* Recover our private data from the struct file instance */
|
||||
shmem = ivshmem_get_shmem(dev->dev, &shmem_size);
|
||||
|
||||
priv = filep->f_inode->i_private;
|
||||
|
||||
if (map->offset < 0 || map->offset >= priv->shmem_size ||
|
||||
map->length == 0 || map->offset + map->length > priv->shmem_size)
|
||||
if (map->offset < 0 || map->offset >= shmem_size ||
|
||||
map->length == 0 || map->offset + map->length > shmem_size)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
map->vaddr = (FAR char *)priv->shmem + map->offset;
|
||||
map->priv.p = priv;
|
||||
map->vaddr = (FAR char *)shmem + map->offset;
|
||||
map->priv.p = dev;
|
||||
map->munmap = uio_ivshmem_unmap;
|
||||
|
||||
/* Not allow mapped memory overlap */
|
||||
@ -227,60 +355,141 @@ static int uio_ivshmem_mmap(FAR struct file *filep,
|
||||
return mm_map_add(get_current_mm(), map);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_notify_poll
|
||||
****************************************************************************/
|
||||
|
||||
void uio_ivshmem_notify_poll(FAR struct uio_ivshmem_notify_s *notify,
|
||||
int32_t newevent)
|
||||
{
|
||||
FAR struct uio_ivshmem_poll_s *poll =
|
||||
(FAR struct uio_ivshmem_poll_s *)notify;
|
||||
|
||||
if (newevent != (int32_t)(uintptr_t)*poll->ppriv)
|
||||
{
|
||||
poll_notify(&poll->fds, 1, POLLIN | POLLRDNORM);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_poll
|
||||
****************************************************************************/
|
||||
|
||||
static int uio_ivshmem_poll(FAR struct file *filep, FAR struct pollfd *fds,
|
||||
bool setup)
|
||||
{
|
||||
FAR struct uio_ivshmem_poll_s *poll;
|
||||
FAR struct uio_ivshmem_dev_s *dev;
|
||||
irqstate_t flags;
|
||||
int i;
|
||||
|
||||
DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL);
|
||||
dev = filep->f_inode->i_private;
|
||||
|
||||
if (setup)
|
||||
{
|
||||
flags = spin_lock_irqsave(&dev->lock);
|
||||
|
||||
for (i = 0; i < CONFIG_PCI_UIO_IVSHMEM_NPOLLWAITERS; i++)
|
||||
{
|
||||
poll = &dev->polls[i];
|
||||
if (poll->fds == NULL)
|
||||
{
|
||||
/* Bind the poll structure and this slot */
|
||||
|
||||
poll->fds = fds;
|
||||
fds->priv = poll;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= CONFIG_PCI_UIO_IVSHMEM_NPOLLWAITERS)
|
||||
{
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (dev->event_count != (int32_t)(uintptr_t)filep->f_priv)
|
||||
{
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
poll_notify(&fds, 1, POLLIN | POLLRDNORM);
|
||||
}
|
||||
else
|
||||
{
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
poll->notify.cb = uio_ivshmem_notify_poll;
|
||||
poll->ppriv = &filep->f_priv;
|
||||
uio_ivshmem_add_notify(dev, &poll->notify);
|
||||
}
|
||||
else if (fds->priv != NULL)
|
||||
{
|
||||
poll = fds->priv;
|
||||
|
||||
uio_ivshmem_remove_notify(dev, &poll->notify);
|
||||
|
||||
flags = spin_lock_irqsave(&dev->lock);
|
||||
poll->fds = NULL;
|
||||
fds->priv = NULL;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_interrupt
|
||||
****************************************************************************/
|
||||
|
||||
static int uio_ivshmem_interrupt(int irq, FAR void *context, FAR void *arg)
|
||||
{
|
||||
FAR struct uio_ivshmem_notify_s *notify;
|
||||
FAR struct uio_ivshmem_dev_s *dev = arg;
|
||||
irqstate_t flags;
|
||||
|
||||
flags = spin_lock_irqsave(&dev->lock);
|
||||
dev->event_count++;
|
||||
list_for_every_entry(&dev->notify_list, notify,
|
||||
struct uio_ivshmem_notify_s, node)
|
||||
{
|
||||
notify->cb(notify, dev->event_count);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: uio_ivshmem_probe
|
||||
****************************************************************************/
|
||||
|
||||
static int uio_ivshmem_probe(FAR struct pci_device_s *dev)
|
||||
static int uio_ivshmem_probe(FAR struct ivshmem_device_s *dev)
|
||||
{
|
||||
FAR struct uio_ivshmem_dev_s *priv;
|
||||
FAR struct uio_ivshmem_dev_s *udev = dev_to_udev(dev);
|
||||
int ret;
|
||||
|
||||
priv = kmm_zalloc(sizeof(*priv));
|
||||
if (priv == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
udev->dev = dev;
|
||||
spin_lock_init(&udev->lock);
|
||||
list_initialize(&udev->notify_list);
|
||||
|
||||
/* Configure the ivshmem device and get share memory address */
|
||||
/* Init the irq and ignore error */
|
||||
|
||||
ret = pci_enable_device(dev);
|
||||
if (ret < 0)
|
||||
{
|
||||
pcierr("ERROR: Enable device failed, ret=%d\n", ret);
|
||||
goto err_priv;
|
||||
}
|
||||
ivshmem_attach_irq(dev, uio_ivshmem_interrupt, udev);
|
||||
ivshmem_control_irq(dev, true);
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
priv->shmem = pci_map_bar(dev, UIO_IVSHMEM_SHMEM_BAR);
|
||||
if (priv->shmem == NULL)
|
||||
{
|
||||
ret = -ENOTSUP;
|
||||
pcierr("ERROR: Device not support share memory bar\n");
|
||||
goto err_master;
|
||||
}
|
||||
|
||||
priv->shmem_size = pci_resource_len(dev, UIO_IVSHMEM_SHMEM_BAR);
|
||||
|
||||
pciinfo("shmem addr=%p size=%zu\n", priv->shmem, priv->shmem_size);
|
||||
|
||||
snprintf(priv->name, sizeof(priv->name), "/dev/uio%d", g_uio_ivshmem_idx);
|
||||
ret = register_driver(priv->name, &g_uio_ivshmem_fops, 0666, priv);
|
||||
snprintf(udev->name, sizeof(udev->name), "/dev/uio%d", udev->drv.id);
|
||||
ret = register_driver(udev->name, &g_uio_ivshmem_fops, 0666, udev);
|
||||
if (ret < 0)
|
||||
{
|
||||
pcierr("ERROR: Ivshmem register_driver failed, ret=%d\n", ret);
|
||||
goto err_master;
|
||||
goto err;
|
||||
}
|
||||
|
||||
g_uio_ivshmem_idx++;
|
||||
return ret;
|
||||
|
||||
err_master:
|
||||
pci_clear_master(dev);
|
||||
pci_disable_device(dev);
|
||||
err_priv:
|
||||
kmm_free(priv);
|
||||
err:
|
||||
ivshmem_unregister_driver(&udev->drv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -288,15 +497,13 @@ err_priv:
|
||||
* Name: uio_ivshmem_remove
|
||||
****************************************************************************/
|
||||
|
||||
static void uio_ivshmem_remove(FAR struct pci_device_s *dev)
|
||||
static void uio_ivshmem_remove(FAR struct ivshmem_device_s *dev)
|
||||
{
|
||||
FAR struct uio_ivshmem_dev_s *priv = dev->priv;
|
||||
FAR struct uio_ivshmem_dev_s *udev = dev_to_udev(dev);
|
||||
|
||||
dev->priv = NULL;
|
||||
unregister_driver(priv->name);
|
||||
pci_clear_master(dev);
|
||||
pci_disable_device(dev);
|
||||
kmm_free(priv);
|
||||
unregister_driver(udev->name);
|
||||
ivshmem_detach_irq(dev);
|
||||
kmm_free(udev);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -305,6 +512,28 @@ static void uio_ivshmem_remove(FAR struct pci_device_s *dev)
|
||||
|
||||
int pci_register_uio_ivshmem_driver(void)
|
||||
{
|
||||
return pci_register_driver(&g_uio_ivshmem_drv);
|
||||
}
|
||||
FAR struct uio_ivshmem_dev_s *dev;
|
||||
FAR char *start = CONFIG_PCI_UIO_IVSHMEM_IDTABLE;
|
||||
|
||||
do
|
||||
{
|
||||
dev = kmm_zalloc(sizeof(*dev));
|
||||
if (dev == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev->drv.id = strtoul(start, &start, 0);
|
||||
dev->drv.probe = uio_ivshmem_probe;
|
||||
dev->drv.remove = uio_ivshmem_remove;
|
||||
if (ivshmem_register_driver(&dev->drv) < 0)
|
||||
{
|
||||
kmm_free(dev);
|
||||
}
|
||||
|
||||
pciinfo("Register ivshmem driver, id=%d\n", dev->drv.id);
|
||||
}
|
||||
while (*start++ != '\0');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user