2006-08-11 19:26:24

by Thomas Koeller

[permalink] [raw]
Subject: [PATCH] Image capturing driver for Basler eXcite smart camera

This is a driver used for image capturing by the Basler eXcite smart camera
platform. It utilizes the integrated GPI DMA engine of the MIPS RM9122
processor. Since this driver does not fit into one of the existing categories
I created a new toplevel directory for it (which may not be appropriate?).

Signed-off-by: Thomas Koeller <[email protected]>
---
drivers/Kconfig | 2
drivers/Makefile | 1
drivers/xicap/Kconfig | 30 +
drivers/xicap/Makefile | 6
drivers/xicap/xicap_core.c | 483 ++++++++++++++++++
drivers/xicap/xicap_gpi.c | 1204
++++++++++++++++++++++++++++++++++++++++++++
drivers/xicap/xicap_priv.h | 47 ++
include/xicap/xicap.h | 40 +
8 files changed, 1813 insertions(+), 0 deletions(-)

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 8b11ceb..5b4d329 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -74,4 +74,6 @@ source "drivers/rtc/Kconfig"

source "drivers/dma/Kconfig"

+source "drivers/xicap/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index fc2d744..af1b1ee 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -76,3 +76,4 @@ obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
obj-$(CONFIG_GENERIC_TIME) += clocksource/
obj-$(CONFIG_DMA_ENGINE) += dma/
+obj-$(CONFIG_EXCITE_FCAP) += xicap/
diff --git a/drivers/xicap/Kconfig b/drivers/xicap/Kconfig
new file mode 100644
index 0000000..da17d82
--- /dev/null
+++ b/drivers/xicap/Kconfig
@@ -0,0 +1,30 @@
+#
+# eXcite frame capturing configuration
+#
+
+menu "eXcite frame capture support"
+ depends BASLER_EXCITE
+
+config EXCITE_FCAP
+ tristate "Frame capturing support for eXcite devices (EXPERIMENTAL)"
+ ---help---
+ Enable basic support for frame capture devices on the BASLER eXcite
+ platform. You also have to select a hardware driver.
+
+ This can also be compiled as a module, which will be named
+ xicap_core.
+
+
+config EXCITE_FCAP_GPI
+ depends CPU_RM9000 && EXCITE_FCAP
+ tristate "Frame capturing using MIPS RM9K (EXPERIMENTAL)"
+ ---help---
+ This driver implememnts frame capturing support via the
+ GPI device found on MIPS RM9K embedded processors manufactured
+ by PMC-Sierra, Inc.
+
+ This driver can be built as a module, which will be named
+ xicap_gpi.
+
+endmenu
+
diff --git a/drivers/xicap/Makefile b/drivers/xicap/Makefile
new file mode 100644
index 0000000..12e1a3b
--- /dev/null
+++ b/drivers/xicap/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Basler eXcite frame capturing drivers
+#
+
+obj-$(CONFIG_EXCITE_FCAP) += xicap_core.o
+obj-$(CONFIG_EXCITE_FCAP_GPI) += xicap_gpi.o
diff --git a/drivers/xicap/xicap_core.c b/drivers/xicap/xicap_core.c
new file mode 100644
index 0000000..a72f7b8
--- /dev/null
+++ b/drivers/xicap/xicap_core.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2004, 2005 by Basler Vision Technologies AG
+ * Author: Thomas Koeller <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <xicap/xicap.h>
+
+#include "xicap_priv.h"
+
+
+
+/* Get device context from inode */
+#define cxi(__i__) container_of((__i__)->i_cdev, xicap_device_context_t,
chardev)
+
+/* Get device context from file */
+#define cxf(__f__) ((xicap_device_context_t *) (__f__)->private_data)
+
+
+
+/* String constants */
+static const char xicap_name[] = "xicap";
+
+
+
+/* Data used for device number allocation */
+static dev_t devnum_base;
+static DECLARE_MUTEX(devnum_lock);
+static unsigned long devnum_bitmap = 0;
+
+#define MAX_DEVICES (sizeof devnum_bitmap * 8)
+
+
+
+/* Function prototypes */
+static void xicap_device_release(struct class_device *);
+static long xicap_ioctl(struct file *, unsigned int, unsigned long);
+static unsigned int xicap_poll(struct file *, poll_table *);
+static ssize_t xicap_read(struct file *, char __user *, size_t, loff_t *);
+static int xicap_open(struct inode *, struct file *);
+static int xicap_release(struct inode *, struct file *);
+static int xicap_queue_buffer(xicap_device_context_t *,
+ const xicap_arg_qbuf_t *);
+
+
+
+/* A class for xicap devices */
+static struct class xicap_class = {
+ .name = (char *) xicap_name,
+ .release = xicap_device_release,
+ .class_release = NULL
+};
+
+
+
+/* The file operations vector */
+static struct file_operations xicap_fops = {
+ .unlocked_ioctl = xicap_ioctl,
+ .read = xicap_read,
+ .open = xicap_open,
+ .release = xicap_release,
+ .poll = xicap_poll
+};
+
+
+
+struct xicap_devctxt {
+ struct class_device classdev;
+ struct cdev chardev;
+ const xicap_hw_driver_t * hwdrv;
+ dev_t devnum;
+ spinlock_t compl_lock;
+ struct list_head compl_queue;
+ atomic_t opencnt;
+ wait_queue_head_t wq;
+};
+
+
+
+/* A context for every class device */
+struct xicap_clsdev_ctxt {
+ xicap_hw_driver_t * hwdrv;
+};
+
+
+
+/* Check for completed buffers */
+static inline int xicap_check_completed(xicap_device_context_t *dc)
+{
+ int r;
+ spin_lock(&dc->compl_lock);
+ r = !list_empty(&dc->compl_queue);
+ spin_unlock(&dc->compl_lock);
+ return r;
+}
+
+
+
+/* Retreive a completed buffer from the queue */
+static inline xicap_data_buffer_t *
+xicap_retreive_completed(xicap_device_context_t *dc)
+{
+ xicap_data_buffer_t * p;
+ spin_lock(&dc->compl_lock);
+ p = list_empty(&dc->compl_queue) ?
+ NULL : list_entry(dc->compl_queue.next, xicap_data_buffer_t,
+ link);
+ if (p)
+ list_del(&p->link);
+ spin_unlock(&dc->compl_lock);
+ return p;
+}
+
+
+
+
+static long xicap_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ int res = -ENOTTY;
+ union {
+ xicap_arg_qbuf_t qbuf;
+ } a;
+
+
+ if (unlikely(_IOC_TYPE(cmd) != XICAP_IOC_TYPE))
+ return -ENOTTY;
+
+ if ((_IOC_DIR(cmd) & _IOC_READ)
+ && !access_ok(VERIFY_WRITE, arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ if ((_IOC_DIR(cmd) & _IOC_WRITE)
+ && !access_ok(VERIFY_READ, arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case XICAP_IOC_QBUF:
+ if (_IOC_SIZE(XICAP_IOC_QBUF) != sizeof a.qbuf) {
+ res = -EINVAL;
+ break;
+ }
+ res = __copy_from_user(&a.qbuf, (void *) arg,
+ sizeof a.qbuf) ?
+ -EINVAL : xicap_queue_buffer(cxf(f), &a.qbuf);
+ break;
+
+ case XICAP_IOC_FLUSH:
+ {
+ xicap_device_context_t * const dc = cxf(f);
+ res = dc->hwdrv->flush ?
+ dc->hwdrv->flush(dc->classdev.dev) :
+ -ENOTTY;
+ }
+ break;
+ }
+
+ return res;
+}
+
+
+
+static unsigned int xicap_poll(struct file *f, poll_table *tbl)
+{
+ xicap_device_context_t * const dc = cxf(f);
+ poll_wait(f, &dc->wq, tbl);
+ return POLLOUT | POLLWRNORM
+ | (xicap_check_completed(dc) ? POLLIN | POLLRDNORM : 0);
+}
+
+
+
+static ssize_t xicap_read(struct file *f, char __user *buf, size_t len,
+ loff_t *offs)
+{
+ int res;
+ xicap_device_context_t * const dc = cxf(f);
+ xicap_data_buffer_t *bd;
+ xicap_result_t dat;
+
+ if (unlikely(len != sizeof dat))
+ return -EINVAL;
+
+ if (unlikely(!access_ok(VERIFY_WRITE, buf, len)))
+ return -EFAULT;
+
+ if (f->f_flags & O_NONBLOCK) {
+ /* If nonblocking and no completed ops, return error status */
+ if (bd = xicap_retreive_completed(dc), !bd)
+ return -EAGAIN;
+ } else {
+ res = wait_event_interruptible(
+ dc->wq,
+ (bd = xicap_retreive_completed(dc), bd));
+ if (res)
+ return res;
+ }
+
+ if (dc->hwdrv->finish_buffer) {
+ const int i = dc->hwdrv->finish_buffer(dc->classdev.dev,
+ bd->frmctxt);
+ if (i) {
+ kfree(bd);
+ return i;
+ }
+ }
+
+ dat.data = bd->uaddr;
+ dat.ctxt = bd->uctxt;
+ dat.status = bd->status;
+ __copy_to_user(buf, &dat, sizeof dat);
+ kfree(bd);
+
+ return sizeof dat;
+}
+
+
+
+static int xicap_open(struct inode *i, struct file *f)
+{
+ xicap_device_context_t * const dc = cxi(i);
+
+ /* Only one opener allowed */
+ if (atomic_dec_if_positive(&dc->opencnt) < 0)
+ return -EBUSY;
+
+ spin_lock_init(&dc->compl_lock);
+ INIT_LIST_HEAD(&dc->compl_queue);
+ init_waitqueue_head(&dc->wq);
+ f->private_data = cxi(i);
+ return dc->hwdrv->start(dc->classdev.dev);
+}
+
+
+
+static int xicap_release(struct inode *i, struct file *f)
+{
+ xicap_device_context_t * const dc = cxi(i);
+ dc->hwdrv->stop(dc->classdev.dev);
+
+ while (xicap_check_completed(dc))
+ kfree(xicap_retreive_completed(dc));
+
+ atomic_set(&dc->opencnt, 1);
+ return 0;
+}
+
+
+
+/* Device registration */
+xicap_device_context_t *
+xicap_device_register(struct device *dev, const xicap_hw_driver_t *hwdrv)
+{
+ int res = 0, devi = -1;
+
+ /* Set up a device context */
+ xicap_device_context_t * const dc =
+ (xicap_device_context_t *) kmalloc(sizeof *dc, GFP_KERNEL);
+ if (!dc) {
+ res = -ENOMEM;
+ goto ex;
+ }
+
+ memset(dc, 0, sizeof *dc);
+ cdev_init(&dc->chardev, &xicap_fops);
+ dc->chardev.owner = THIS_MODULE;
+ dc->classdev.dev = get_device(dev);
+ dc->hwdrv = hwdrv;
+ atomic_set(&dc->opencnt, 1);
+
+ /* Allocate a device number */
+ down(&devnum_lock);
+ if (unlikely(devnum_bitmap == ~0x0)) {
+ up(&devnum_lock);
+ res = -ENODEV;
+ goto ex;
+ }
+ devi = ffz(devnum_bitmap);
+ devnum_bitmap |= 0x1 << devi;
+ up(&devnum_lock);
+
+ /* Register the class device with its class */
+ dc->classdev.class = &xicap_class;
+ dc->classdev.devt = devi + devnum_base;
+ res = snprintf(dc->classdev.class_id, sizeof dc->classdev.class_id,
+ "%s%u", xicap_name, devi)
+ < sizeof dc->classdev.class_id ? 0 : -ENAMETOOLONG;
+ if (!res)
+ res = class_device_register(&dc->classdev);
+ if (unlikely(res)) {
+ dc->classdev.class = NULL;
+ goto ex;
+ }
+
+ /* Register the character device */
+ res = cdev_add(&dc->chardev, devi + devnum_base, 1);
+
+ex:
+ if (res) {
+ if (dc->classdev.class)
+ class_device_unregister(&dc->classdev);
+ if (devi >= 0) {
+ down(&devnum_lock);
+ devnum_bitmap &= ~(0x1 << devi);
+ up(&devnum_lock);
+ }
+ if (dc) {
+ put_device(dc->classdev.dev);
+ kfree(dc);
+ }
+ } else {
+ dc->devnum = devi + devnum_base;
+ }
+
+ return res ? (xicap_device_context_t *) ERR_PTR(res) : dc;
+}
+
+
+
+/* Device unregistration */
+void xicap_device_unregister(xicap_device_context_t *dc)
+{
+ cdev_del(&dc->chardev);
+ class_device_unregister(&dc->classdev);
+ down(&devnum_lock);
+ devnum_base &= ~(0x1 << (dc->devnum - devnum_base));
+ up(&devnum_lock);
+}
+
+
+
+void xicap_frame_done(xicap_device_context_t *dc, xicap_data_buffer_t *bd)
+{
+ struct page **p;
+
+ for (p = bd->pages; p < bd->pages + bd->npages; p++)
+ page_cache_release(*p);
+
+ spin_lock(&dc->compl_lock);
+ list_add_tail(&bd->link, &dc->compl_queue);
+ spin_unlock(&dc->compl_lock);
+ wake_up_interruptible(&dc->wq);
+}
+
+
+
+static void xicap_device_release(struct class_device *cldev)
+{
+ xicap_device_context_t * const dc =
+ container_of(cldev, xicap_device_context_t, classdev);
+ put_device(dc->classdev.dev);
+ kfree(dc);
+}
+
+
+
+static int xicap_queue_buffer(xicap_device_context_t *dc,
+ const xicap_arg_qbuf_t *arg)
+{
+ int res, npages;
+ xicap_data_buffer_t *bufdesc;
+
+ /* Check for buffer write permissions */
+ if (!access_ok(VERIFY_WRITE, arg->data, arg->size))
+ return -EFAULT;
+
+ npages = (PAGE_ALIGN((unsigned long ) arg->data + arg->size)
+ - ((unsigned long ) arg->data & PAGE_MASK)) >> PAGE_SHIFT;
+
+ bufdesc = (xicap_data_buffer_t *)
+ kmalloc(sizeof *bufdesc + sizeof (struct page *) * npages,
+ GFP_KERNEL);
+ if (!bufdesc)
+ return -ENOMEM;
+
+ bufdesc->uctxt = arg->ctxt;
+ bufdesc->uaddr = arg->data;
+ bufdesc->size = arg->size;
+ bufdesc->npages = npages;
+
+ /* Get hold of the data buffer pages */
+ res = get_user_pages(current, current->mm, (unsigned long) arg->data,
+ npages, 1, 0, bufdesc->pages, NULL);
+ if (res < 0) {
+ kfree(bufdesc);
+ return res;
+ }
+
+ bufdesc->frmctxt = dc->hwdrv->do_buffer(dc->classdev.dev, bufdesc);
+ if (IS_ERR(bufdesc->frmctxt)) {
+ int i;
+
+ for (i = 0; i < npages; i++)
+ put_page(bufdesc->pages[i]);
+ i = PTR_ERR(bufdesc->frmctxt);
+ kfree(bufdesc);
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+static int __init xicap_init_module(void)
+{
+ int res;
+ static __initdata char
+ errfmt[] = KERN_ERR "%s: %s failed - error = %d\n",
+ clsreg[] = "class registration",
+ devalc[] = "device number allocation";
+
+ /* Register the xicap class */
+ res = class_register(&xicap_class);
+ if (unlikely(res)) {
+ printk(errfmt, xicap_class.name, clsreg, res);
+ return res;
+ }
+
+ /* Allocate a range of device numbers */
+ res = alloc_chrdev_region(&devnum_base, 0, MAX_DEVICES, xicap_name);
+ if (unlikely(res)) {
+ printk(errfmt, xicap_name, devalc, res);
+ class_unregister(&xicap_class);
+ return res;
+ }
+
+ return 0;
+}
+
+
+
+static void __exit xicap_cleanup_module(void)
+{
+ unregister_chrdev_region(devnum_base, MAX_DEVICES);
+ class_unregister(&xicap_class);
+}
+
+
+
+EXPORT_SYMBOL(xicap_device_register);
+EXPORT_SYMBOL(xicap_device_unregister);
+EXPORT_SYMBOL(xicap_frame_done);
+module_init(xicap_init_module);
+module_exit(xicap_cleanup_module);
+
+
+
+MODULE_AUTHOR("Thomas Koeller <[email protected]>");
+MODULE_DESCRIPTION("Basler eXcite frame capturing core driver");
+MODULE_VERSION("0.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/xicap/xicap_gpi.c b/drivers/xicap/xicap_gpi.c
new file mode 100644
index 0000000..d39da37
--- /dev/null
+++ b/drivers/xicap/xicap_gpi.c
@@ -0,0 +1,1204 @@
+/*
+ * Copyright (C) 2004, 2005 by Basler Vision Technologies AG
+ * Author: Thomas Koeller <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/moduleparam.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/rm9k-ocd.h>
+#include <asm/page.h>
+#include <asm/semaphore.h>
+
+#include <rm9k_xicap.h>
+
+#include "xicap_priv.h"
+
+
+/* Debug support */
+#define XICAP_GPI_DBG 0
+
+#if XICAP_GPI_DBG
+# define dbgprint(fmt,arg...) printk(KERN_DEBUG fmt,##arg)
+
+static u32 dbg_readl(unsigned int) __attribute__((used));
+
+static u32 dbg_readl(unsigned int addr)
+{
+ return titan_readl(addr);
+}
+#else
+# define dbgprint(fmt,arg...) do {} while (0)
+#endif
+
+
+
+/* Module arguments */
+unsigned int min_packet_size = (32 * L1_CACHE_BYTES);
+module_param(min_packet_size, uint, 0644);
+
+
+
+#define VMAP_WORKAROUND 1
+
+
+
+#define PAGES_PER_FULL_PACKET 7
+#define MAX_PAGES_PER_PACKET (PAGES_PER_FULL_PACKET + 1)
+#define FULL_PACKET_SIZE (PAGE_SIZE * PAGES_PER_FULL_PACKET)
+#define ABSOLUTE_MIN_PACKET_SIZE 5
+#define MAX_PACKET_SIZE 32767
+#define DUMMY_PACKET_SIZE min_packet_size
+
+
+
+/* DMA decriptor-related definitions */
+#define XDMA_RING_SIZE_CODE 3
+#define XDMA_DESC_RING_SIZE (512 >> XDMA_RING_SIZE_CODE)
+#define XDMA_ENABLE_REGVAL \
+ (0x80000000 | (XDMA_RING_SIZE_CODE << 16) | (3 << 5))
+
+
+/*
+ * I/O register access macros
+ * Do not use __raw_writeq() and __raw_readq(), these do not seem to work!
+ */
+#define io_writeq(__v__, __a__) \
+ *(volatile unsigned long long *) (__a__) = (__v__)
+#define io_readq(__a__) (*(volatile unsigned long long *) (__a__))
+#define io_readl(__a__) __raw_readl((__a__))
+#define io_writel(__v__, __a__) __raw_writel((__v__), (__a__))
+#define io_readb(__a__) __raw_readb((__a__))
+#define io_writeb(__v__, __a__) __raw_writeb((__v__), (__a__))
+
+
+
+typedef struct __pkt packet_t;
+typedef struct __gpi_devctxt xicap_gpi_device_context_t;
+
+
+/* Function prototypes */
+static int __init xicap_gpi_probe(struct device *);
+static int __exit xicap_gpi_remove(struct device *);
+static int xicap_gpi_start(struct device *);
+static void xicap_gpi_stop(struct device *);
+static int xicap_gpi_flush(struct device *);
+static xicap_frame_context_t * xicap_gpi_do_buffer(struct device *,
xicap_data_buffer_t *);
+#if VMAP_WORKAROUND
+static int xicap_gpi_finish_buffer(struct device *, xicap_frame_context_t *);
+#endif
+static void xicap_gpi_run_pkt_queue(xicap_gpi_device_context_t *);
+static void xicap_gpi_start_data(xicap_gpi_device_context_t *);
+static void xicap_gpi_stop_data(xicap_gpi_device_context_t *);
+static void xicap_gpi_pkt_finish(void *);
+static void xicap_gpi_flush_queue(struct list_head *, unsigned int);
+static irqreturn_t xicap_gpi_int_handler(int, void *, struct pt_regs *);
+
+
+
+/* A common name for various objects */
+static const char xicap_gpi_name[] = "xicap_gpi";
+
+
+
+/* The driver struct */
+static struct device_driver xicap_gpi_driver = {
+ .name = (char *) xicap_gpi_name,
+ .bus = &platform_bus_type,
+ .owner = THIS_MODULE,
+ .probe = xicap_gpi_probe,
+ .remove = __exit_p(xicap_gpi_remove),
+ .shutdown = NULL,
+ .suspend = NULL,
+ .resume = NULL
+};
+
+
+
+static const xicap_hw_driver_t xicap_gpi_hwdrv = {
+ .start = xicap_gpi_start,
+ .stop = xicap_gpi_stop,
+ .do_buffer = xicap_gpi_do_buffer,
+#if VMAP_WORKAROUND
+ .finish_buffer = xicap_gpi_finish_buffer,
+#else
+ .finish_buffer = NULL,
+#endif
+ .flush = xicap_gpi_flush
+};
+
+
+
+/* A work queue for cleting packets */
+struct workqueue_struct *wq;
+
+
+
+/* A DMA buffer used to work around the RM9K GPI silicon bug */
+unsigned long dummy_dma_buffer;
+
+
+
+/* XDMA read descriptor */
+typedef struct {
+ u64 cpu_part;
+ u64 xdma_part;
+} xdmadesc_t;
+
+
+
+/* A context struct for every device */
+struct __gpi_devctxt {
+ struct semaphore lock;
+ unsigned int slice;
+ unsigned int irq;
+ unsigned int fifomem_start;
+ unsigned int fifomem_size;
+
+ void __iomem * regaddr_fifo_rx;
+ void __iomem * regaddr_fifo_tx;
+ void __iomem * regaddr_xdma;
+ void __iomem * regaddr_pktproc;
+ void __iomem * regaddr_fpga;
+ void __iomem * dmadesc;
+
+ dma_addr_t dmadesc_p;
+ atomic_t desc_cnt;
+
+ struct list_head frm_queue;
+ unsigned int frm_cnt;
+ unsigned int frm_ready_cnt;
+
+ /* Core driver context pointer */
+ xicap_device_context_t * devctxt;
+
+ /*
+ * The interrupt queue, where packes are queued for
+ * processing at interrupt level.
+ */
+ spinlock_t int_queue_lock;
+ struct list_head int_queue;
+ unsigned int int_errflg;
+
+
+ /* The packet queue & related stuff */
+ struct list_head pkt_queue;
+ unsigned int pkt_page_i;
+
+
+ void __iomem * curdesc;
+};
+
+
+
+struct __pkt {
+ union {
+ struct list_head link;
+ struct work_struct wrk;
+ } link;
+ dma_addr_t pgaddr[MAX_PAGES_PER_PACKET];
+ xicap_frame_context_t * fctxt;
+ volatile const u64 * desc2;
+ u16 copy_size;
+ u16 mapped_size;
+ u16 remain_size;
+ void * copy_src;
+ struct page ** copy_pg;
+ unsigned int copy_offs;
+ struct page ** page;
+ unsigned int ndirty;
+};
+
+
+
+#define PKTFLG_FIFO_OVERFLOW 0x01
+#define PKTFLG_DATA_ERROR 0x02
+#define PKTFLG_XDMA_ERROR 0x04
+#define PKTFLG_DESC_UNDERRUN 0x08
+
+
+
+
+struct xicap_frmctxt {
+ struct list_head link;
+ struct device * dev;
+ unsigned int flags;
+ xicap_gpi_device_context_t * dc;
+ int status;
+ u16 fpga_data[5];
+ xicap_data_buffer_t * buf;
+ atomic_t npkts;
+ unsigned int total_pkts;
+ packet_t pkts[0];
+};
+
+
+
+static inline xicap_data_buffer_t * xicap_gpi_delete_packet(packet_t *pkt)
+{
+ xicap_frame_context_t * const fctxt = pkt->fctxt;
+ xicap_data_buffer_t * res = NULL;
+
+ if (!atomic_dec_return(&fctxt->npkts)) {
+ res = fctxt->buf;
+ res->status = fctxt->status;
+#if !VMAP_WORKAROUND
+ kfree(fctxt);
+#endif
+ }
+
+ return res;
+}
+
+
+
+static inline const struct resource *
+xicap_gpi_get_resource(struct platform_device *d, unsigned long flags,
+ const char *basename)
+{
+ const char fmt[] = "%s_%u";
+ char buf[80];
+
+ if (unlikely(snprintf(buf, sizeof buf, fmt, basename, d->id) >= sizeof buf))
+ return NULL;
+ return platform_get_resource_byname(d, flags, buf);
+}
+
+
+
+static inline void __iomem *
+xicap_gpi_map_regs(struct platform_device *d, const char *basename)
+{
+ void * result = NULL;
+ const struct resource * const r =
+ xicap_gpi_get_resource(d, IORESOURCE_MEM, basename);
+ if (likely(r))
+ result = ioremap_nocache(r->start, r->end + 1 - r->start);
+ return result;
+}
+
+
+
+/* No hotplugging on the platform bus - use __init */
+static int __init xicap_gpi_probe(struct device *dev)
+{
+ int res;
+ xicap_gpi_device_context_t *dc = NULL;
+ struct platform_device * pdv;
+ const struct resource * rsrc;
+
+ static char __initdata
+ rsrcname_gpi_slice[] = XICAP_RESOURCE_GPI_SLICE,
+ rsrcname_fifo_blk[] = XICAP_RESOURCE_FIFO_BLK,
+ rsrcname_irq[] = XICAP_RESOURCE_IRQ,
+ rsrcname_dmadesc[] = XICAP_RESOURCE_DMADESC,
+ rsrcname_fifo_rx[] = XICAP_RESOURCE_FIFO_RX,
+ rsrcname_fifo_tx[] = XICAP_RESOURCE_FIFO_TX,
+ rsrcname_xdma[] = XICAP_RESOURCE_XDMA,
+ rsrcname_pktproc[] = XICAP_RESOURCE_PKTPROC,
+ rsrcname_pkt_stream[] = XICAP_RESOURCE_PKT_STREAM;
+
+ /* Get the platform device. */
+ if (unlikely(dev->bus != &platform_bus_type)) {
+ res = -ENODEV;
+ goto errex;
+ }
+
+ pdv = to_platform_device(dev);
+
+ /* Create and set up the device context */
+ dc = (xicap_gpi_device_context_t *)
+ kmalloc(sizeof (xicap_gpi_device_context_t), GFP_KERNEL);
+ if (!dc) {
+ res = -ENOMEM;
+ goto errex;
+ }
+ memset(dc, 0, sizeof *dc);
+ init_MUTEX(&dc->lock);
+
+ /* Evaluate resources */
+ res = -ENXIO;
+
+ rsrc = xicap_gpi_get_resource(pdv, 0, rsrcname_gpi_slice);
+ if (unlikely(!rsrc)) goto errex;
+ dc->slice = rsrc->start;
+
+ rsrc = xicap_gpi_get_resource(pdv, 0, rsrcname_fifo_blk);
+ if (unlikely(!rsrc)) goto errex;
+ dc->fifomem_start = rsrc->start;
+ dc->fifomem_size = rsrc->end + 1 - rsrc->start;
+
+ rsrc = xicap_gpi_get_resource(pdv, IORESOURCE_IRQ, rsrcname_irq);
+ if (unlikely(!rsrc)) goto errex;
+ dc->irq = rsrc->start;
+
+ rsrc = xicap_gpi_get_resource(pdv, IORESOURCE_MEM, rsrcname_dmadesc);
+ if (unlikely(!rsrc)) goto errex;
+ if (unlikely((rsrc->end + 1 - rsrc->start)
+ < (XDMA_DESC_RING_SIZE * sizeof (xdmadesc_t))))
+ goto errex;
+ dc->dmadesc_p = (dma_addr_t) rsrc->start;
+ dc->dmadesc = ioremap_nocache(rsrc->start, rsrc->end + 1 - rsrc->start);
+
+ dc->regaddr_fifo_rx = xicap_gpi_map_regs(pdv, rsrcname_fifo_rx);
+ dc->regaddr_fifo_tx = xicap_gpi_map_regs(pdv, rsrcname_fifo_tx);
+ dc->regaddr_xdma = xicap_gpi_map_regs(pdv, rsrcname_xdma);
+ dc->regaddr_pktproc = xicap_gpi_map_regs(pdv, rsrcname_pktproc);
+ dc->regaddr_fpga = xicap_gpi_map_regs(pdv, rsrcname_pkt_stream);
+
+ if (unlikely(!dc->regaddr_fifo_rx || !dc->regaddr_fifo_tx
+ || !dc->regaddr_xdma || !dc->regaddr_pktproc || !dc->regaddr_fpga
+ || !dc->dmadesc))
+ goto errex;
+
+ /* Register the device with the core */
+ dc->devctxt = xicap_device_register(dev, &xicap_gpi_hwdrv);
+ res = IS_ERR(dc->devctxt) ? PTR_ERR(dc->devctxt) : 0;
+
+errex:
+ if (res) {
+ if (dc->regaddr_fifo_rx) iounmap(dc->regaddr_fifo_rx);
+ if (dc->regaddr_fifo_tx) iounmap(dc->regaddr_fifo_tx);
+ if (dc->regaddr_xdma) iounmap(dc->regaddr_xdma);
+ if (dc->regaddr_pktproc) iounmap(dc->regaddr_pktproc);
+ if (dc->regaddr_fpga) iounmap(dc->regaddr_fpga);
+ if (dc->dmadesc) iounmap(dc->dmadesc);
+ if (dc) kfree(dc);
+ dbgprint("%s: %s failed, error = %d\n", xicap_gpi_name,
+ __func__, res);
+ } else {
+ dev->driver_data = dc;
+ dbgprint("%s: Context at %p\n", xicap_gpi_name, dc);
+ }
+
+ return res;
+}
+
+
+
+static int __exit xicap_gpi_remove(struct device *dev)
+{
+ xicap_gpi_device_context_t * const dc =
+ (xicap_gpi_device_context_t *) dev->driver_data;
+
+ xicap_device_unregister(dc->devctxt);
+ kfree(dc);
+ dev->driver_data = NULL;
+ return 0;
+}
+
+
+
+static int xicap_gpi_start(struct device *dev)
+{
+ xicap_gpi_device_context_t * const dc =
+ (xicap_gpi_device_context_t *) dev->driver_data;
+ u32 reg;
+ int res;
+
+ /* Lock the device context */
+ down(&dc->lock);
+
+ /* Device context initialization */
+ INIT_LIST_HEAD(&dc->pkt_queue);
+ INIT_LIST_HEAD(&dc->frm_queue);
+ INIT_LIST_HEAD(&dc->int_queue);
+ spin_lock_init(&dc->int_queue_lock);
+ dc->int_errflg = 0;
+
+ lock_titan_regs();
+
+ /* Disable the slice status interrupts */
+ reg = titan_readl(0x0050) & ~(0x1f << (dc->slice * 5));
+ titan_writel(reg, 0x0050);
+
+ /* Disable the XDMA interrupts for this slice */
+ reg = titan_readl(0x0058) & ~(0xff << (dc->slice * 8));
+ titan_writel(reg, 0x0058);
+
+ unlock_titan_regs();
+
+ xicap_gpi_start_data(dc);
+
+ res = request_irq(dc->irq, xicap_gpi_int_handler,
+ SA_SHIRQ, xicap_gpi_name, dc);
+ if (unlikely(res))
+ return res;
+
+ lock_titan_regs();
+
+ /* Enable the slice status interrupts */
+ reg = titan_readl(0x0050) | (0x2 << (dc->slice * 5));
+ titan_writel(reg, 0x0050);
+
+ /* Enable the XDMA data interrupt */
+ reg = 0xff << (dc->slice * 8);
+ titan_writel(reg, 0x0048);
+ titan_writel(reg, 0x004c);
+ reg = titan_readl(0x0058);
+ titan_writel(reg | (0x1 << (dc->slice * 8)), 0x0058);
+
+ unlock_titan_regs();
+
+ /* Release the device context and exit */
+ up(&dc->lock);
+ return 0;
+}
+
+
+
+static void xicap_gpi_start_data(xicap_gpi_device_context_t *dc)
+{
+ unsigned int i;
+
+ /* Reset all XDMA channels for this slice */
+ io_writel(0x80080000, dc->regaddr_xdma + 0x0000);
+ io_writel(0x80080000, dc->regaddr_xdma + 0x0040);
+ io_writel(0x80080000, dc->regaddr_xdma + 0x0080);
+ io_writel(0x80080000, dc->regaddr_xdma + 0x00c0);
+
+ /* Reset & enable the XDMA slice interrupts */
+ io_writel(0x80068002, dc->regaddr_xdma + 0x000c);
+ io_writel(0x00008002, dc->regaddr_xdma + 0x0010);
+
+ dc->pkt_page_i = 0;
+ dc->frm_ready_cnt = dc->frm_cnt = 0;
+
+ /* Set up the XDMA descriptor ring & enable the XDMA */
+ dc->curdesc = dc->dmadesc;
+ atomic_set(&dc->desc_cnt, XDMA_DESC_RING_SIZE);
+ io_writel(dc->dmadesc_p, dc->regaddr_xdma + 0x0018);
+ wmb();
+ memset(dc->dmadesc, 0, XDMA_DESC_RING_SIZE * sizeof (xdmadesc_t));
+ io_writel(XDMA_ENABLE_REGVAL, dc->regaddr_xdma + 0x0000);
+
+ /*
+ * Enable the rx fifo we are going to use. Disable the
+ * unused ones as well as the tx fifo.
+ */
+ io_writel(0x00100000 | ((dc->fifomem_size) << 10)
+ | dc->fifomem_start,
+ dc->regaddr_fifo_rx + 0x0000);
+ wmb();
+ io_writel((10 << 20) | (10 << 10) | 128, dc->regaddr_fifo_rx
+ + 0x0004);
+ io_writel(0x00100400, dc->regaddr_fifo_rx + 0x000c);
+ io_writel(0x00100400, dc->regaddr_fifo_rx + 0x0018);
+ io_writel(0x00100400, dc->regaddr_fifo_rx + 0x0024);
+ io_writel(0x00100400, dc->regaddr_fifo_tx + 0x0000);
+
+ /* Reset any pending interrupt, then enable fifo */
+ titan_writel(0xf << (dc->slice * 4), 0x482c);
+ wmb();
+ io_writel(0x00200000 | ((dc->fifomem_size) << 10)
+ | dc->fifomem_start,
+ dc->regaddr_fifo_rx + 0x0000);
+
+ /* Enable the packet processor */
+ io_writel(0x00000000, dc->regaddr_pktproc + 0x0000);
+ wmb();
+ io_writel(0x0000001f, dc->regaddr_pktproc + 0x0008);
+ io_writel(0x00000e08, dc->regaddr_pktproc + 0x0010);
+ io_writel(0x0000080f, dc->regaddr_pktproc + 0x0014);
+ io_writel(0x000003ff, dc->regaddr_pktproc + 0x0018);
+ io_writel(0x00000100, dc->regaddr_pktproc + 0x0038);
+ wmb();
+ io_writel(0x00000001, dc->regaddr_pktproc + 0x0000);
+
+ /* Disable address filtering */
+ io_writel(0x0, dc->regaddr_pktproc + 0x0120);
+ io_writel(0x2, dc->regaddr_pktproc + 0x0124);
+ for (i = 0; i < 8; i++) {
+ io_writel( i, dc->regaddr_pktproc + 0x0128);
+ wmb();
+ io_writel(0x0, dc->regaddr_pktproc + 0x0100);
+ io_writel(0x0, dc->regaddr_pktproc + 0x0104);
+ io_writel(0x0, dc->regaddr_pktproc + 0x0108);
+ io_writel(0x0, dc->regaddr_pktproc + 0x010c);
+ wmb();
+ }
+
+ io_writel(0x1, dc->regaddr_pktproc + 0x012c);
+
+}
+
+
+
+static void xicap_gpi_stop_data(xicap_gpi_device_context_t *dc)
+{
+ /* Shut down the data transfer */
+ io_writeb(0x01, dc->regaddr_fpga + 0x000b);
+
+ /* Reset the XDMA channel */
+ io_writel(0x80080000, dc->regaddr_xdma + 0x0000);
+
+ /* Disable the FIFO */
+ io_writel(0x00100400, dc->regaddr_fifo_rx + 0x0000);
+
+ /* Disable the packet processor */
+ io_writel(0x00000000, dc->regaddr_pktproc + 0x0000);
+
+ dc->frm_ready_cnt = 0;
+ INIT_LIST_HEAD(&dc->frm_queue);
+}
+
+
+
+static void xicap_gpi_flush_queue(struct list_head *l, unsigned int stat)
+{
+ while (!list_empty(l)) {
+ packet_t * const pkt =
+ list_entry(l->next, packet_t, link.link);
+ xicap_gpi_device_context_t * const dc = pkt->fctxt->dc;
+ const dma_addr_t *pa = pkt->pgaddr;
+ xicap_data_buffer_t * buf;
+
+ list_del(&pkt->link.link);
+
+ while (pkt->mapped_size) {
+ size_t sz = pkt->mapped_size;
+ if (sz > PAGE_SIZE)
+ sz = PAGE_SIZE;
+ pkt->mapped_size -= sz;
+ dma_unmap_page(pkt->fctxt->dev, *pa++, sz,
+ DMA_FROM_DEVICE);
+ }
+
+ if (pkt->copy_size) {
+ free_pages((unsigned long) pkt->copy_src,
+ pkt->copy_size > PAGE_SIZE ? 1 : 0);
+ pkt->copy_size = 0;
+ }
+
+ buf = xicap_gpi_delete_packet(pkt);
+ if (buf) {
+ buf->status = stat;
+ xicap_frame_done(dc->devctxt, buf);
+ }
+ }
+}
+
+
+
+static void xicap_gpi_stop(struct device *dev)
+{
+ u32 reg;
+ LIST_HEAD(l);
+ xicap_gpi_device_context_t * const dc =
+ (xicap_gpi_device_context_t *) dev->driver_data;
+
+ lock_titan_regs();
+
+ /* Disable the slice status interrupts */
+ reg = titan_readl(0x0050) & ~(0x1f << (dc->slice * 5));
+ titan_writel(reg, 0x0050);
+
+ /* Disable the XDMA interrupts for this slice */
+ reg = titan_readl(0x0058) & ~(0xff << (dc->slice * 8));
+ titan_writel(reg, 0x0058);
+
+ unlock_titan_regs();
+ flush_workqueue(wq);
+ down(&dc->lock);
+ xicap_gpi_stop_data(dc);
+
+ /* Now clean up the packet & int queues */
+ spin_lock_irq(&dc->int_queue_lock);
+ list_splice_init(&dc->int_queue, &l);
+ spin_unlock_irq(&dc->int_queue_lock);
+ list_splice_init(&dc->pkt_queue, l.prev);
+ xicap_gpi_flush_queue(&l, ~0x0);
+ up(&dc->lock);
+
+ /* Detach interrupt handler */
+ free_irq(dc->irq, dc);
+}
+
+
+
+static int xicap_gpi_flush(struct device *dev)
+{
+ u32 reg;
+ xicap_gpi_device_context_t * const dc =
+ (xicap_gpi_device_context_t *) dev->driver_data;
+ LIST_HEAD(l);
+
+ lock_titan_regs();
+
+ /* Disable the slice status interrupts */
+ reg = titan_readl(0x0050) & ~(0x1f << (dc->slice * 5));
+ titan_writel(reg, 0x0050);
+
+ /* Disable the XDMA interrupts for this slice */
+ reg = titan_readl(0x0058) & ~(0xff << (dc->slice * 8));
+ titan_writel(reg, 0x0058);
+
+ unlock_titan_regs();
+
+ /* Now clean up the packet & int queues */
+ flush_workqueue(wq);
+ down(&dc->lock);
+ xicap_gpi_stop_data(dc);
+ spin_lock_irq(&dc->int_queue_lock);
+ list_splice_init(&dc->int_queue, &l);
+ spin_unlock_irq(&dc->int_queue_lock);
+ list_splice_init(&dc->pkt_queue, l.prev);
+ xicap_gpi_flush_queue(&l, XICAP_BUFSTAT_ABORTED);
+ xicap_gpi_start_data(dc);
+ up(&dc->lock);
+
+ lock_titan_regs();
+
+ /* Re-enable the slice status interrupts */
+ reg = titan_readl(0x0050) | (0x2 << (dc->slice * 5));
+ titan_writel(reg, 0x0050);
+
+ /* Re-enable the XDMA data interrupt */
+ reg = 0xff << (dc->slice * 8);
+ titan_writel(reg, 0x0048);
+ titan_writel(reg, 0x004c);
+ reg = titan_readl(0x0058);
+ wmb();
+ titan_writel(reg | (0x1 << (dc->slice * 8)), 0x0058);
+
+ unlock_titan_regs();
+
+ return 0;
+}
+
+
+
+static xicap_frame_context_t *
+xicap_gpi_do_buffer(struct device *dev, xicap_data_buffer_t *buf)
+{
+ unsigned long head_buf = 0, tail_buf = 0;
+ u16 head_size = 0, tail_size = 0, full_size = FULL_PACKET_SIZE;
+ u16 total_packets;
+ size_t sz = buf->size;
+ unsigned int full_packets = 0, head_order = 0, i, request_frame = 1;
+ const unsigned int boffs = (unsigned long) buf->uaddr & ~PAGE_MASK;
+ packet_t *pkt;
+ struct page ** pg;
+ LIST_HEAD(packets);
+ xicap_frame_context_t * fctxt;
+ xicap_gpi_device_context_t * const dc =
+ (xicap_gpi_device_context_t *) dev->driver_data;
+
+ if (unlikely(sz < ABSOLUTE_MIN_PACKET_SIZE))
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * If the buffer is not page aligned, the first part of it
+ * (the 'head') is DMA'ed into a temporary buffer and later
+ * copied to the user's buffer. The size of the head is chosen
+ * so that the remaining part of the buffer is page aligned.
+ */
+ if (boffs) {
+ head_size = PAGE_SIZE - boffs;
+ if (head_size < min_packet_size) {
+ head_size += PAGE_SIZE;
+ head_order = 1;
+ }
+ if (head_size > sz) {
+ head_size = sz;
+ head_order = (head_size > PAGE_SIZE) ? 1 : 0;
+ }
+ head_buf = __get_dma_pages(GFP_KERNEL, head_order);
+ if (!head_buf)
+ return ERR_PTR(-ENOMEM);
+
+ /* Compute the residual buffer size */
+ sz -= head_size;
+ }
+
+ /*
+ * Now compute the number of full-sized packets, and the size
+ * of the last ('tail') packet.
+ */
+ if (sz) {
+ full_packets = sz / FULL_PACKET_SIZE;
+ tail_size = sz % FULL_PACKET_SIZE;
+ }
+
+ /*
+ * If the tail packet is less than a page, it can be merged with
+ * the previous one. This also covers the case where the size of
+ * the tail packet is less than min_packet_size.
+ */
+ if ((tail_size < PAGE_SIZE) && full_packets) {
+ full_packets--;
+ tail_size += FULL_PACKET_SIZE;
+ }
+
+ /*
+ * The XDMA will pad the last packet to the next cache line
+ * boundary, so in order to avoid writing beyond the user's
+ * buffer, we need to use a temporary buffer if the tail packet
+ * size is not an integer multiple of the cache line size.
+ */
+ if (tail_size % L1_CACHE_BYTES) {
+ tail_buf = __get_dma_pages(GFP_KERNEL, 0);
+ if (unlikely(!tail_buf)) {
+ if (head_buf)
+ free_pages(head_buf, head_order);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ /*
+ * Now we know how many packets to process for the buffer, so we
+ * can allocate a packet set. Add one extra dummy packet (silicon
+ * bug workaround).
+ */
+ total_packets =
+ full_packets + (head_size ? 1 : 0) + (tail_size ? 1 : 0) + 1;
+ fctxt = kmalloc(
+ sizeof (xicap_frame_context_t) + sizeof (packet_t) * total_packets,
+ GFP_KERNEL);
+ if (unlikely(!fctxt)) {
+ if (tail_buf)
+ free_pages(tail_buf, 0);
+ if (head_buf)
+ free_pages(head_buf, head_order);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ fctxt->buf = buf;
+ fctxt->dev = dev;
+ fctxt->flags = 0;
+ fctxt->status = XICAP_BUFSTAT_OK;
+ atomic_set(&fctxt->npkts, total_packets);
+ fctxt->total_pkts = total_packets;
+ pkt = &fctxt->pkts[0];
+ pg = buf->pages;
+ fctxt->dc = (xicap_gpi_device_context_t *) dev->driver_data;
+
+ /* Set up the head packet descriptor */
+ if (head_size) {
+ struct page * const p = virt_to_page(head_buf);
+
+ pkt->page = pkt->copy_pg = pg;
+
+ if (head_order) {
+ pkt->pgaddr[0] = dma_map_page(dev, p, 0, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ pkt->pgaddr[1] = dma_map_page(dev, p + 1, 0,
+ head_size - PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ pkt->ndirty = 2;
+ pg += 2;
+ } else {
+ pkt->pgaddr[0] = dma_map_page(dev, p, 0, head_size,
+ DMA_FROM_DEVICE);
+ pkt->ndirty = 1;
+ pg++;
+ }
+
+ pkt->copy_src = (void *) head_buf;
+ pkt->copy_offs = ((unsigned long)buf->uaddr & ~PAGE_MASK);
+ pkt->remain_size = pkt->mapped_size = pkt->copy_size = head_size;
+ pkt->fctxt = fctxt;
+ list_add_tail(&pkt->link.link, &packets);
+ pkt++;
+ }
+
+ /* Set up descriptors for all full-sized packets */
+ while (full_packets--) {
+ pkt->remain_size = pkt->mapped_size = FULL_PACKET_SIZE;
+ pkt->copy_size = 0;
+ pkt->page = pg;
+ pkt->ndirty = PAGES_PER_FULL_PACKET;
+
+ for (i = 0; i < PAGES_PER_FULL_PACKET; i++)
+ pkt->pgaddr[i] = dma_map_page(dev, *pg++, 0, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+
+ pkt->fctxt = fctxt;
+ list_add_tail(&pkt->link.link, &packets);
+ pkt++;
+ }
+
+ /* Set up the descriptor for the tail packet */
+ if (tail_size) {
+ const size_t cs = tail_size % PAGE_SIZE;
+
+ pkt->remain_size = pkt->mapped_size = tail_size;
+ pkt->page = pg;
+ pkt->ndirty = tail_size / PAGE_SIZE;
+
+ for (i = 0; i < pkt->ndirty; i++)
+ pkt->pgaddr[i] = dma_map_page(dev, *pg++, 0, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (cs) {
+ if (tail_buf) {
+ struct page * const p = virt_to_page(tail_buf);
+ pkt->pgaddr[i] = dma_map_page(dev, p, 0, cs,
+ DMA_FROM_DEVICE);
+ pkt->copy_src = (void *) tail_buf;
+ pkt->copy_size = cs;
+ pkt->copy_pg = pg;
+ pkt->copy_offs = 0;
+ } else {
+ pkt->pgaddr[i] = dma_map_page(dev, *pg, 0, cs,
+ DMA_FROM_DEVICE);
+ pkt->copy_size = 0;
+ }
+ pkt->ndirty++;
+ } else {
+ pkt->copy_size = 0;
+ }
+
+ pkt->fctxt = fctxt;
+ list_add_tail(&pkt->link.link, &packets);
+ pkt++;
+ }
+
+ /*
+ * Set up the trailing dummy packet (silicon bug workaround).
+ *
+ * The XDMA does not generate an interrupt if the memory
+ * controlled by the last DMA descriptor is filled with data
+ * to the last byte. We work around this by adding a dummy packet
+ * after each frame. This guarantees that there always is an
+ * active DMA descriptor after the last one of the frame, and so
+ * the XDMA will interrupt. The dummy packet size is chosen so that
+ * the dummy buffer is not entirely filled and hence will always
+ * generate an interrupt, too.
+ */
+ pkt->remain_size = pkt->mapped_size = DUMMY_PACKET_SIZE;
+ pkt->page = NULL;
+ pkt->copy_size = 0;
+ pkt->ndirty = 0;
+
+ for (i = 0; i < DUMMY_PACKET_SIZE / PAGE_SIZE; i++)
+ pkt->pgaddr[i] = dma_map_page(dev, virt_to_page(dummy_dma_buffer),
+ 0, PAGE_SIZE, DMA_FROM_DEVICE);
+
+ pkt->pgaddr[i] = dma_map_page(dev, virt_to_page(dummy_dma_buffer),
+ 0, DUMMY_PACKET_SIZE % PAGE_SIZE,
+ DMA_FROM_DEVICE);
+
+ pkt->fctxt = fctxt;
+ list_add_tail(&pkt->link.link, &packets);
+
+ /*
+ * Set up data to send to the FPGA. The total number of packets
+ * requested does _not_ include the dummy packet. If DUMMY_PACKET_SIZE
+ * is not zero, a dummy packet is always sent.
+ */
+ fctxt->fpga_data[0] = cpu_to_le16(fctxt->pkts[0].mapped_size);
+ fctxt->fpga_data[1] = cpu_to_le16(full_size);
+ fctxt->fpga_data[2] = cpu_to_le16((pkt - 1)->mapped_size);
+ fctxt->fpga_data[3] = cpu_to_le16(DUMMY_PACKET_SIZE);
+ fctxt->fpga_data[4] = cpu_to_le16(total_packets - 1);
+
+ down(&dc->lock);
+
+ /* Now enqueue all the packets in one step */
+ list_splice(&packets, dc->pkt_queue.prev);
+ if (!dc->frm_cnt++)
+ xicap_gpi_run_pkt_queue(dc);
+
+ dbgprint("%s: created packet set %p\n"
+ "\thead size = %#06x, full size = %#06x, "
+ "tail size = %#06x, dummy size = %#06x\n"
+ "\ttotal packets = %u, active DMA descriptors = %u\n",
+ xicap_gpi_name, fctxt, head_size, full_size, tail_size,
+ DUMMY_PACKET_SIZE, total_packets,
+ io_readl(dc->regaddr_xdma + 0x0008));
+
+ /*
+ * If less than two frames are currently pending, we can send
+ * the frame parameters to the FPGA right away. Otherwise, we
+ * need to queue the frame.
+ */
+ if (dc->frm_ready_cnt++ > 1) {
+ list_add_tail(&fctxt->link, &dc->frm_queue);
+ request_frame = 0;
+ }
+
+ up(&dc->lock);
+
+ if (request_frame)
+ memcpy_toio(dc->regaddr_fpga, fctxt->fpga_data,
+ sizeof fctxt->fpga_data);
+
+ return fctxt;
+}
+
+
+
+static void xicap_gpi_run_pkt_queue(xicap_gpi_device_context_t *dc)
+{
+ packet_t *pkt = list_empty(&dc->pkt_queue) ? NULL :
+ list_entry(dc->pkt_queue.next, packet_t, link.link);
+
+ while (pkt) {
+ int i;
+ size_t sz;
+
+ /* Stop, if no more free DMA descriptors */
+ if (0 > atomic_dec_if_positive(&dc->desc_cnt))
+ break;
+
+ i = dc->pkt_page_i++;
+
+ sz = pkt->remain_size;
+ if (sz > PAGE_SIZE)
+ sz = PAGE_SIZE;
+ pkt->remain_size -= sz;
+
+ /* Set up the DMA descriptor */
+ io_writeq(cpu_to_be64(pkt->pgaddr[i]), dc->curdesc);
+ if (i) {
+ io_writeq(cpu_to_be64(0x1ULL << 53), dc->curdesc + 8);
+ } else {
+ io_writeq(cpu_to_be64((0x1ULL << 63) | (0x1ULL << 53)),
+ dc->curdesc + 8);
+ pkt->desc2 = dc->curdesc + 8;
+ }
+
+ dbgprint("%s: Desc. %2u = %016Lx, %016Lx\n",
+ xicap_gpi_name,
+ (dc->curdesc - dc->dmadesc) / sizeof (xdmadesc_t),
+ (u64) pkt->pgaddr[i], desc2);
+
+ dc->curdesc += sizeof (xdmadesc_t);
+ if ((dc->curdesc - dc->dmadesc) >=
+ (XDMA_DESC_RING_SIZE * sizeof (xdmadesc_t)))
+ dc->curdesc = dc->dmadesc;
+
+ /* Add the packet to the interrupt queue */
+ if (!pkt->remain_size) {
+ spin_lock_irq(&dc->int_queue_lock);
+ list_move_tail(&pkt->link.link, &dc->int_queue);
+ spin_unlock_irq(&dc->int_queue_lock);
+ dc->pkt_page_i = 0;
+ pkt = list_empty(&dc->pkt_queue) ? NULL :
+ list_entry(dc->pkt_queue.next, packet_t,
+ link.link);
+ }
+
+ io_writel(1, dc->regaddr_xdma + 0x0008);
+ }
+}
+
+
+#if VMAP_WORKAROUND
+static int
+xicap_gpi_finish_buffer(struct device *dev, xicap_frame_context_t *frmctxt)
+{
+ struct __pkt * const pkt_h = frmctxt->pkts,
+ * const pkt_t = frmctxt->pkts + frmctxt->total_pkts - 2;
+
+ if (pkt_h->copy_size) {
+ __copy_to_user(frmctxt->buf->uaddr, pkt_h->copy_src,
+ pkt_h->copy_size);
+ free_pages((unsigned long) pkt_h->copy_src,
+ (pkt_h->copy_size > PAGE_SIZE) ? 1 : 0);
+ }
+
+ if (2 < frmctxt->total_pkts) {
+ if (pkt_t->copy_size) {
+ __copy_to_user(frmctxt->buf->uaddr + frmctxt->buf->size
+ - pkt_t->copy_size,
+ pkt_t->copy_src,
+ pkt_t->copy_size);
+ free_pages((unsigned long) pkt_t->copy_src, 0);
+ }
+ }
+
+ kfree(frmctxt);
+ return 0;
+}
+#endif
+
+
+
+static void xicap_gpi_pkt_finish(void *p)
+{
+ packet_t * const pkt = (packet_t *) p;
+ xicap_gpi_device_context_t * const dc =
+ (xicap_gpi_device_context_t *) pkt->fctxt->dev->driver_data;
+ const dma_addr_t *pa = pkt->pgaddr;
+ xicap_data_buffer_t * buf;
+
+ while (pkt->mapped_size) {
+ size_t sz = pkt->mapped_size;
+ if (sz > PAGE_SIZE)
+ sz = PAGE_SIZE;
+ pkt->mapped_size -= sz;
+ dma_unmap_page(pkt->fctxt->dev, *pa++, sz, DMA_FROM_DEVICE);
+ }
+
+#if !VMAP_WORKAROUND
+ if (pkt->copy_size) {
+ const unsigned int page_order =
+ (pkt->copy_size > PAGE_SIZE) ? 1 : 0;
+ void * const dst = vmap(pkt->copy_pg, 0x1 << page_order,
+ VM_MAP, PAGE_USERIO);
+
+ if (dst) {
+ memcpy(dst + pkt->copy_offs, pkt->copy_src,
+ pkt->copy_size);
+ free_pages((unsigned long) pkt->copy_src, page_order);
+ vunmap(dst);
+ } else {
+ pkt->fctxt->status = XICAP_BUFSTAT_VMERR;
+ }
+ }
+#endif
+
+ while (pkt->ndirty--)
+ set_page_dirty_lock(*pkt->page++);
+
+ down(&dc->lock);
+ buf = xicap_gpi_delete_packet(pkt);
+ if (buf) {
+ xicap_frame_context_t *fctxt = NULL;
+ xicap_frame_done(dc->devctxt, buf);
+ dc->frm_cnt--;
+ if (dc->frm_ready_cnt-- > 2) {
+ fctxt = list_entry(dc->frm_queue.next,
+ xicap_frame_context_t, link);
+ list_del(&fctxt->link);
+ }
+
+ if (fctxt)
+ memcpy_toio(dc->regaddr_fpga, fctxt->fpga_data,
+ sizeof fctxt->fpga_data);
+ }
+
+ if (dc->frm_cnt)
+ xicap_gpi_run_pkt_queue(dc);
+ up(&dc->lock);
+}
+
+
+
+/* The interrupt handler */
+static irqreturn_t xicap_gpi_int_handler(int irq, void *arg, struct pt_regs
*regs)
+{
+ xicap_gpi_device_context_t * const dc =
+ (xicap_gpi_device_context_t *) arg;
+ u32 flg_dmadata, flg_slstat, flg_fofl, reg;
+
+ /* Check, if this interrupt is for us */
+ flg_dmadata = titan_readl(0x0048) & (0x1 << (dc->slice * 8));
+ flg_slstat = titan_readl(0x0040) & (0x1f << (dc->slice * 5));
+ flg_fofl = titan_readl(0x482c) & ((dc->slice * 4));
+ if (!(flg_dmadata | flg_slstat | flg_fofl))
+ return IRQ_NONE; /* not our interrupt */
+
+ if (unlikely(flg_slstat)) {
+ reg = io_readl(dc->regaddr_pktproc + 0x000c) & 0x1f;
+ io_writel(reg, dc->regaddr_pktproc + 0x000c);
+ dc->int_errflg |= PKTFLG_DATA_ERROR;
+ }
+
+ if (unlikely(flg_fofl)) {
+ titan_writel(flg_fofl, 0x482c);
+ dc->int_errflg |= PKTFLG_FIFO_OVERFLOW;
+ }
+
+ reg = io_readl(dc->regaddr_xdma + 0x000c) & 0x00008002;
+ if (unlikely(reg)) {
+ io_writel(reg, dc->regaddr_xdma + 0x000c);
+ dc->int_errflg |= ((reg & 0x00008000) ? PKTFLG_XDMA_ERROR : 0)
+ | ((reg & 0x00000002) ? PKTFLG_DESC_UNDERRUN : 0);
+ }
+
+ if (likely(flg_dmadata)) {
+ titan_writel(flg_dmadata, 0x0048);
+ spin_lock(&dc->int_queue_lock);
+ while (!list_empty(&dc->int_queue)) {
+ packet_t * const pkt = list_entry(dc->int_queue.next,
+ packet_t, link.link);
+
+ /* If the packet is not completed yet, exit */
+ if (be64_to_cpu(*pkt->desc2) & (0x1ULL << 53))
+ break;
+ list_del(&pkt->link.link);
+
+ /* Release the DMA descriptors used by this packet */
+ atomic_add(PAGE_ALIGN(pkt->mapped_size) >> PAGE_SHIFT, &dc->desc_cnt);
+
+ /* All further processing is deferred to a worker thread */
+ INIT_WORK(&pkt->link.wrk, xicap_gpi_pkt_finish, pkt);
+ if(unlikely(!queue_work(wq, &pkt->link.wrk)))
+ panic("%s: worker thread error\n",
+ xicap_gpi_name);
+ }
+ spin_unlock(&dc->int_queue_lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+static int __init xicap_gpi_init_module(void)
+{
+ int res;
+ dummy_dma_buffer = __get_dma_pages(GFP_KERNEL, 0);
+ if (!dummy_dma_buffer)
+ return -ENOMEM;
+ wq = create_workqueue(xicap_gpi_name);
+ if (unlikely(!wq)) {
+ free_pages(dummy_dma_buffer, 0);
+ return -ENOMEM;
+ }
+ res = driver_register(&xicap_gpi_driver);
+ if (unlikely(res)) {
+ free_pages(dummy_dma_buffer, 0);
+ destroy_workqueue(wq);
+ }
+ return res;
+}
+
+
+
+static void __exit xicap_gpi_cleanup_module(void)
+{
+ driver_unregister(&xicap_gpi_driver);
+ destroy_workqueue(wq);
+ free_pages(dummy_dma_buffer, 0);
+}
+
+module_init(xicap_gpi_init_module);
+module_exit(xicap_gpi_cleanup_module);
+
+
+
+MODULE_AUTHOR("Thomas Koeller <[email protected]>");
+MODULE_DESCRIPTION("Basler eXcite frame capturing driver for gpi devices");
+MODULE_VERSION("0.0");
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(min_packet_size, "Minimum data packet size");
diff --git a/drivers/xicap/xicap_priv.h b/drivers/xicap/xicap_priv.h
new file mode 100644
index 0000000..7c6b158
--- /dev/null
+++ b/drivers/xicap/xicap_priv.h
@@ -0,0 +1,47 @@
+#if ! defined(XICAP_PRIV_H)
+#define XICAP_PRIV_H
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <xicap/xicap.h>
+
+
+
+typedef struct xicap_devctxt xicap_device_context_t;
+typedef struct xicap_frmctxt xicap_frame_context_t;
+
+
+
+/* A queue block for a data buffer */
+typedef struct {
+ struct list_head link;
+ void __user * uaddr;
+ size_t size;
+ void * uctxt;
+ xicap_frame_context_t * frmctxt;
+ unsigned int status;
+ unsigned int npages;
+ struct page * pages[0]; /* must be last element! */
+} xicap_data_buffer_t;
+
+
+
+/* Functions invoked by the core */
+typedef struct {
+ int (*start)(struct device *);
+ void (*stop)(struct device *);
+ xicap_frame_context_t * (*do_buffer)(struct device *, xicap_data_buffer_t
*);
+ int (*finish_buffer)(struct device *, xicap_frame_context_t *);
+ int (*flush)(struct device *);
+} xicap_hw_driver_t;
+
+
+
+/* Functions exported by the core */
+xicap_device_context_t *
+ xicap_device_register(struct device *, const xicap_hw_driver_t *);
+void xicap_device_unregister(xicap_device_context_t *);
+void xicap_frame_done(xicap_device_context_t *, xicap_data_buffer_t *);
+
+#endif /* ! defined(XICAP_PRIV_H) */
diff --git a/include/xicap/xicap.h b/include/xicap/xicap.h
new file mode 100644
index 0000000..6614bc4
--- /dev/null
+++ b/include/xicap/xicap.h
@@ -0,0 +1,40 @@
+#if ! defined(XICAP_H)
+#define XICAP_H
+
+#include <linux/ioctl.h>
+
+/* A buffer descriptor. */
+typedef struct {
+ void *data; /* data buffer */
+ size_t size; /* data buffer size */
+ void *ctxt; /* user-defined context pointer */
+} xicap_arg_qbuf_t;
+
+
+/*
+ * Result block passed back to user after operation completed.
+ * Todo: add time stamp field.
+ */
+typedef struct {
+ void *data; /* data buffer pointer */
+ void *ctxt; /* user context */
+ int status; /* buffer status, see below */
+} xicap_result_t;
+
+/* Returned buffer status values */
+#define XICAP_BUFSTAT_OK 0 /* normal return */
+#define XICAP_BUFSTAT_ABORTED 1 /* aborted by flush */
+#define XICAP_BUFSTAT_VMERR 2 /* buffer mapping error */
+
+
+
+/* Definitions for ioctl() */
+#define XICAP_IOC_TYPE 0xbb /* a random choice */
+
+/* Ready to grab next frame */
+#define XICAP_IOC_QBUF _IOW(XICAP_IOC_TYPE, 0, xicap_arg_qbuf_t)
+#define XICAP_IOC_FLUSH _IO(XICAP_IOC_TYPE, 1)
+
+#define XICAP_IOC_MAXNR 1
+
+#endif /* ! defined(XICAP_H) */
--
1.4.0


--
Thomas Koeller, Software Development

Basler Vision Technologies
An der Strusbek 60-62
22926 Ahrensburg
Germany

Tel +49 (4102) 463-390
Fax +49 (4102) 463-46390

mailto:[email protected]
http://www.baslerweb.com


2006-08-11 19:32:39

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

On Thu, 10 Aug 2006 23:18:04 +0200 Thomas Koeller wrote:

> This is a driver used for image capturing by the Basler eXcite
> smart camera platform. It utilizes the integrated GPI DMA engine of
> the MIPS RM9122 processor. Since this driver does not fit into one
> of the existing categories I created a new toplevel directory for
> it (which may not be appropriate?).

Sounds like it could fit into drivers/media/ maybe ?

> Signed-off-by: Thomas Koeller <[email protected]>
> ---
> drivers/Kconfig | 2
> drivers/Makefile | 1
> drivers/xicap/Kconfig | 30 +
> drivers/xicap/Makefile | 6
> drivers/xicap/xicap_core.c | 483 ++++++++++++++++++
> drivers/xicap/xicap_gpi.c | 1204
> ++++++++++++++++++++++++++++++++++++++++++++
> drivers/xicap/xicap_priv.h | 47 ++
> include/xicap/xicap.h | 40 +
> 8 files changed, 1813 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 8b11ceb..5b4d329 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -74,4 +74,6 @@ source "drivers/rtc/Kconfig"
>
> source "drivers/dma/Kconfig"
>
> +source "drivers/xicap/Kconfig"
> +
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index fc2d744..af1b1ee 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -76,3 +76,4 @@ obj-$(CONFIG_CRYPTO) += crypto/
> obj-$(CONFIG_SUPERH) += sh/
> obj-$(CONFIG_GENERIC_TIME) += clocksource/
> obj-$(CONFIG_DMA_ENGINE) += dma/
> +obj-$(CONFIG_EXCITE_FCAP) += xicap/
> diff --git a/drivers/xicap/Kconfig b/drivers/xicap/Kconfig
> new file mode 100644
> index 0000000..da17d82
> --- /dev/null
> +++ b/drivers/xicap/Kconfig
> @@ -0,0 +1,30 @@
> +#
> +# eXcite frame capturing configuration
> +#
> +
> +menu "eXcite frame capture support"
> + depends BASLER_EXCITE
> +
> +config EXCITE_FCAP
> + tristate "Frame capturing support for eXcite devices
> (EXPERIMENTAL)"
> + ---help---
> + Enable basic support for frame capture devices on the
> BASLER eXcite
> + platform. You also have to select a hardware driver.
> +
> + This can also be compiled as a module, which will be named
> + xicap_core.
> +
> +
> +config EXCITE_FCAP_GPI
> + depends CPU_RM9000 && EXCITE_FCAP
> + tristate "Frame capturing using MIPS RM9K (EXPERIMENTAL)"
> + ---help---
> + This driver implememnts frame capturing support via the
> + GPI device found on MIPS RM9K embedded processors
> manufactured
> + by PMC-Sierra, Inc.
> +
> + This driver can be built as a module, which will be named
> + xicap_gpi.
> +
> +endmenu

---
~Randy

2006-08-11 19:49:42

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

Ar Iau, 2006-08-10 am 23:18 +0200, ysgrifennodd Thomas Koeller:
> This is a driver used for image capturing by the Basler eXcite smart camera
> platform.

drivers/media/video and the Video4Linux2 API deal with image capture in
Linux. It provides a common API for video and thus image capture. Any
reason that interface is not suitable.

Alan

2006-08-11 20:48:46

by Dave Jones

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

On Thu, Aug 10, 2006 at 11:18:04PM +0200, Thomas Koeller wrote:
> This is a driver used for image capturing by the Basler eXcite smart camera
> platform. It utilizes the integrated GPI DMA engine of the MIPS RM9122
> processor. Since this driver does not fit into one of the existing categories
> I created a new toplevel directory for it (which may not be appropriate?).

Hi Thomas.

As others have pointed out, drivers/media/video is probably a better home.

Some speedy mostly-nitpicking comments below. I didn't give it an indepth review,
but this is stuff that jumped out at me from a quick skim.

> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/config.h>

Unnecessary include (kbuild does this for you now)

> +static unsigned long devnum_bitmap = 0;

Unneeded initialisation. (static uninitialised vars go in .bss)

> +/* Function prototypes */
> +static void xicap_device_release(struct class_device *);
> +static long xicap_ioctl(struct file *, unsigned int, unsigned long);
> +static unsigned int xicap_poll(struct file *, poll_table *);
> +static ssize_t xicap_read(struct file *, char __user *, size_t, loff_t *);
> +static int xicap_open(struct inode *, struct file *);
> +static int xicap_release(struct inode *, struct file *);
> +static int xicap_queue_buffer(xicap_device_context_t *,
> + const xicap_arg_qbuf_t *);

You could lose all these forward declarations if you move
the xicap_fops after the function declarations.

> +/* A class for xicap devices */
> +static struct class xicap_class = {
> + .name = (char *) xicap_name,

Is that cast necessary ?

> +/* Device registration */
> +xicap_device_context_t *
> +xicap_device_register(struct device *dev, const xicap_hw_driver_t *hwdrv)

The typedef had me dancing around trying to find out what it was a few
times. Can we just replace it with uses of struct xicap_devctx ?
Ditto for xicap_frame_context_t

> + /* Set up a device context */
> + xicap_device_context_t * const dc =
> + (xicap_device_context_t *) kmalloc(sizeof *dc, GFP_KERNEL);
> + if (!dc) {
> + res = -ENOMEM;
> + goto ex;
> + }
> +
> + memset(dc, 0, sizeof *dc);

You could lose the memset, and use kzalloc instead.

> +MODULE_VERSION("0.0");

Heh, early days ? :-)

> +++ b/drivers/xicap/xicap_gpi.c
> ...
> +
> +#include <linux/config.h>

Same as above. Unneeded.

> +#define VMAP_WORKAROUND 1

This needs a comment to explain what its doing.

> +/*
> + * I/O register access macros
> + * Do not use __raw_writeq() and __raw_readq(), these do not seem to work!
> + */
> +#define io_writeq(__v__, __a__) \
> + *(volatile unsigned long long *) (__a__) = (__v__)
> +#define io_readq(__a__) (*(volatile unsigned long long *) (__a__))
> +#define io_readl(__a__) __raw_readl((__a__))
> +#define io_writel(__v__, __a__) __raw_writel((__v__), (__a__))
> +#define io_readb(__a__) __raw_readb((__a__))
> +#define io_writeb(__v__, __a__) __raw_writeb((__v__), (__a__))

If they don't work, it'd be nice to get them fixed instead of reinventing new ones.

> + /* Create and set up the device context */
> + dc = (xicap_gpi_device_context_t *)
> + kmalloc(sizeof (xicap_gpi_device_context_t), GFP_KERNEL);
> + if (!dc) {
> + res = -ENOMEM;
> + goto errex;
> + }
> + memset(dc, 0, sizeof *dc);

kzalloc.

> + rsrc = xicap_gpi_get_resource(pdv, 0, rsrcname_gpi_slice);
> + if (unlikely(!rsrc)) goto errex;

if (unlikely(!rsrc))
goto errex;

> + if (unlikely(!rsrc)) goto errex;

if (unlikely(!rsrc))
goto errex;

> + if (unlikely(!rsrc)) goto errex;

if (unlikely(!rsrc))
goto errex;

> + if (unlikely(!rsrc)) goto errex;

etc.

> + if (res) {
> + if (dc->regaddr_fifo_rx) iounmap(dc->regaddr_fifo_rx);
> + if (dc->regaddr_fifo_tx) iounmap(dc->regaddr_fifo_tx);
> + if (dc->regaddr_xdma) iounmap(dc->regaddr_xdma);
> + if (dc->regaddr_pktproc) iounmap(dc->regaddr_pktproc);
> + if (dc->regaddr_fpga) iounmap(dc->regaddr_fpga);
> + if (dc->dmadesc) iounmap(dc->dmadesc);
> + if (dc) kfree(dc);

etc


> + /* Set up the XDMA descriptor ring & enable the XDMA */
> + dc->curdesc = dc->dmadesc;
> + atomic_set(&dc->desc_cnt, XDMA_DESC_RING_SIZE);
> + io_writel(dc->dmadesc_p, dc->regaddr_xdma + 0x0018);
> + wmb();

Uncommented wmb's are a sin :)
This one may actually need to be a io_readl if its just to flush
the previous io_writel ?

> + /*
> + * Enable the rx fifo we are going to use. Disable the
> + * unused ones as well as the tx fifo.
> + */
> + io_writel(0x00100000 | ((dc->fifomem_size) << 10)
> + | dc->fifomem_start,
> + dc->regaddr_fifo_rx + 0x0000);
> + wmb();

same again.

> + titan_writel(0xf << (dc->slice * 4), 0x482c);
> + wmb();

and again for a whole bunch more writel's, which really make me wonder...

Asides from all these points, the only thing that really makes me nervous
is the amount of access_ok & __copy_*_user()/memcpy() uses we have rather than
just doing a copy_*_user. It's one of those "are we sure we've checked everything"
paranoia's I have..

Dave

--
http://www.codemonkey.org.uk

2006-08-12 17:30:11

by Thomas Koeller

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera


Alan Cox <alan <at> lxorguk.ukuu.org.uk> writes:

>
> Ar Iau, 2006-08-10 am 23:18 +0200, ysgrifennodd Thomas Koeller:
> > This is a driver used for image capturing by the Basler eXcite smart camera
> > platform.
>
> drivers/media/video and the Video4Linux2 API deal with image capture in
> Linux. It provides a common API for video and thus image capture. Any
> reason that interface is not suitable.
>
> Alan
>
>

This is not a driver for grabbing live video streams. The eXcite platform using
it is a smart camera running linux, and the driver is for a very special piece
of hardware designed into this camera. Its purpose is grabbing single image
frames for processing within the camera itself, which is quite different from
what the v4l2 API has been designed for.

For more information about the device, see
http://www.baslerweb.com/beitraege/beitrag_en_18458.html.

I am not subscribed to lkml, so please cc my address thomas at koeller dot
dyndns dot org on all replies.

Thomas



2006-08-14 15:53:39

by Eric Piel

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

08/12/2006 07:27 PM, Thomas Koeller wrote/a écrit:
> Alan Cox <alan <at> lxorguk.ukuu.org.uk> writes:
>
>> Ar Iau, 2006-08-10 am 23:18 +0200, ysgrifennodd Thomas Koeller:
>>> This is a driver used for image capturing by the Basler eXcite smart camera
>>> platform.
>> drivers/media/video and the Video4Linux2 API deal with image capture in
>> Linux. It provides a common API for video and thus image capture. Any
>> reason that interface is not suitable.
>>
>> Alan
>>
>>
>
> This is not a driver for grabbing live video streams. The eXcite platform using
> it is a smart camera running linux, and the driver is for a very special piece
> of hardware designed into this camera. Its purpose is grabbing single image
> frames for processing within the camera itself, which is quite different from
> what the v4l2 API has been designed for.
>
> For more information about the device, see
> http://www.baslerweb.com/beitraege/beitrag_en_18458.html.
Hello,

Maybe I just completely misunderstood you point, in which case I
apologize... However from what it seems, you are proposing a kernel
driver for the hardware which is inside the product described on the
webpage. This driver will run on a processor embedded into this camera.
It will allow some user-space programs which also run on this processor
to acquire pictures generated by the captor. Am I right so far?

If so then the Video4Linux2 API is still the best way to implement the
protocol to pass data between the user-space programs and the driver.
The V4L2 API doesn't says that the camera must be far away from the
processor, it can work for USB webcams, for Firewire video camera, PCI
TV tuners, and probably also for your device. Using the V4L2 not only
has the advantage of being a well tested API for communicating video
related information with the user-space but it also buys you the fact
that any program available on Linux for video should be able to directly
detect and use the captor!

That said, thank you already very much for submitting your driver to the
Linux kernel. Your code seems already good quality and conformant with
the coding style of the kernel. Still, some people will review your
code, and let you know what they think might be problematic. This is the
normal process for driver acceptance, for the good both of your driver
and of the kernel. When you have answered the different suggestion,
please resubmit your new version of the driver with the change. I
suggest also CCing [email protected] :-)

See you,
Eric


> I am not subscribed to lkml, so please cc my address thomas at koeller dot
> dyndns dot org on all replies.
Please, also keep the CC: in your answers.

2006-08-14 19:26:40

by Thomas Koeller

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

On Monday 14 August 2006 17:53, Éric Piel wrote:
> 08/12/2006 07:27 PM, Thomas Koeller wrote/a écrit:
> Hello,
>
> Maybe I just completely misunderstood you point, in which case I
> apologize... However from what it seems, you are proposing a kernel
> driver for the hardware which is inside the product described on the
> webpage. This driver will run on a processor embedded into this camera.
> It will allow some user-space programs which also run on this processor
> to acquire pictures generated by the captor. Am I right so far?

Exactly.

>
> If so then the Video4Linux2 API is still the best way to implement the
> protocol to pass data between the user-space programs and the driver.
> The V4L2 API doesn't says that the camera must be far away from the
> processor, it can work for USB webcams, for Firewire video camera, PCI
> TV tuners, and probably also for your device. Using the V4L2 not only
> has the advantage of being a well tested API for communicating video
> related information with the user-space but it also buys you the fact
> that any program available on Linux for video should be able to directly
> detect and use the captor!

Sorry, but no. The camera has been designed to be used in industrial
control applications, such as quality assurance. Think of an automated
inspection of a certain product, where the inspection is integrated
into the production process. Faulty products are sorted out. For this to
work it is absolutely necessary to get the maximum speed (image frames
per second) out of the hardware, so image acquisition and processing
must be carried out in parallel. The way to achieve this is have the
driver manage a queue of image buffers to fill, so it will continue
grabbing images even if no read operation is currently pending. Also,
the ability to attach user-specific context information to every buffer
is essential.

Another reason to choose this API was to ease customer migration from
PC-based solutions involving IEEE-1394 and Gigabit Ethernet cameras
(of which my employer is a major manufacturer) to the eXcite platform.
These devices ship with driver software that implements a very similar
API, and we provide user-space libraries that allow for writing generic
software that will run on the eXcite platform as well as on a PC with
a dumb camera attached via FireWire or GigE. There is an entire
software framework, of which this driver is just a small component.

Finally, the camera already ships with the software as is, so changing
the API is out of the question, as it would break customer applications.

>
> That said, thank you already very much for submitting your driver to the
> Linux kernel. Your code seems already good quality and conformant with
> the coding style of the kernel. Still, some people will review your
> code, and let you know what they think might be problematic. This is the
> normal process for driver acceptance, for the good both of your driver
> and of the kernel. When you have answered the different suggestion,
> please resubmit your new version of the driver with the change. I
> suggest also CCing [email protected] :-)
>
> See you,
> Eric
>
> > I am not subscribed to lkml, so please cc my address thomas at koeller
> > dot dyndns dot org on all replies.
>
> Please, also keep the CC: in your answers.

Thanks for taking the time to look at the code!

Thomas

--
Thomas Koeller, Software Development

Basler Vision Technologies
An der Strusbek 60-62
22926 Ahrensburg
Germany

Tel +49 (4102) 463-390
Fax +49 (4102) 463-46390

mailto:[email protected]
http://www.baslerweb.com

2006-08-17 15:35:14

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

Hi!

> > If so then the Video4Linux2 API is still the best way to implement the
> > protocol to pass data between the user-space programs and the driver.
> > The V4L2 API doesn't says that the camera must be far away from the
> > processor, it can work for USB webcams, for Firewire video camera, PCI
> > TV tuners, and probably also for your device. Using the V4L2 not only
> > has the advantage of being a well tested API for communicating video
> > related information with the user-space but it also buys you the fact
> > that any program available on Linux for video should be able to directly
> > detect and use the captor!
>
> Sorry, but no. The camera has been designed to be used in industrial
> control applications, such as quality assurance. Think of an automated
> inspection of a certain product, where the inspection is integrated
> into the production process. Faulty products are sorted out. For this to
> work it is absolutely necessary to get the maximum speed (image frames
> per second) out of the hardware, so image acquisition and processing
> must be carried out in parallel. The way to achieve this is have the
> driver manage a queue of image buffers to fill, so it will continue
> grabbing images even if no read operation is currently pending. Also,
> the ability to attach user-specific context information to every buffer
> is essential.

Well, I guess v4l api will need to be improved, then. That is still
not a reason to introduce completely new api...

--
Thanks for all the (sleeping) penguins.

2006-08-17 20:30:48

by Thomas Koeller

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

On Thursday 17 August 2006 17:31, Pavel Machek wrote:
> Well, I guess v4l api will need to be improved, then. That is still
> not a reason to introduce completely new api...

The API as implemented by the driver I submitted is very minimalistic,
because it is just a starting point. There's more to be added in future,
like controlling flashes, interfacing to line-scan cameras clocked by
incremental encodes attached to some conveyor, and other stuff which
is common in industrial image processing applications. You really do
not want to clutter the v4l2 API with these things; that would hardly
be an 'improvement'.

Different interfaces, designed to serve different purposes...

Thomas
--
Thomas Koeller, Software Development

Basler Vision Technologies
An der Strusbek 60-62
22926 Ahrensburg
Germany

Tel +49 (4102) 463-390
Fax +49 (4102) 463-46390

mailto:[email protected]
http://www.baslerweb.com

2006-08-18 08:28:05

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

On Thu 17-08-06 22:30:30, Thomas Koeller wrote:
> On Thursday 17 August 2006 17:31, Pavel Machek wrote:
> > Well, I guess v4l api will need to be improved, then. That is still
> > not a reason to introduce completely new api...
>
> The API as implemented by the driver I submitted is very minimalistic,
> because it is just a starting point. There's more to be added in future,
> like controlling flashes, interfacing to line-scan cameras clocked by
> incremental encodes attached to some conveyor, and other stuff which
> is common in industrial image processing applications. You really do


If it is _common_, we definitely need an API. We do not want the next
driver to reinvent it from scratch, right?
--
Thanks for all the (sleeping) penguins.

2006-08-18 13:16:09

by Bill Davidsen

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

Thomas Koeller wrote:
> On Thursday 17 August 2006 17:31, Pavel Machek wrote:
>> Well, I guess v4l api will need to be improved, then. That is still
>> not a reason to introduce completely new api...
>
> The API as implemented by the driver I submitted is very minimalistic,
> because it is just a starting point. There's more to be added in future,
> like controlling flashes, interfacing to line-scan cameras clocked by
> incremental encodes attached to some conveyor, and other stuff which
> is common in industrial image processing applications. You really do
> not want to clutter the v4l2 API with these things; that would hardly
> be an 'improvement'.
>
> Different interfaces, designed to serve different purposes...
>
If you look at Pavel's posts WRT swsusp2, he has taken this position
before, that lack of functionality in {something} is no justification to
introduce a new solution, and that the limitations of {something} can be
addressed by incremental improvement. Like any good idea, this can be
carried to extremes.

Don't take it personally, just write working code people can patch in.
When your code has the features you mentioned it will be highly useful
and hopefully ported to many devices. I guess security monitoring is an
"industrial image processing application," which interests me. At the
moment I would call it an impressive proof of concept, but you have many
useful ideas for its future.

--
Bill Davidsen <[email protected]>
Obscure bug of 2004: BASH BUFFER OVERFLOW - if bash is being run by a
normal user and is setuid root, with the "vi" line edit mode selected,
and the character set is "big5," an off-by-one errors occurs during
wildcard (glob) expansion.

2006-08-22 21:36:22

by Thomas Koeller

[permalink] [raw]
Subject: Re: [PATCH] Image capturing driver for Basler eXcite smart camera

On Friday 18 August 2006 15:19, Bill Davidsen wrote:
>
> Don't take it personally, just write working code people can patch in.
> When your code has the features you mentioned it will be highly useful
> and hopefully ported to many devices. I guess security monitoring is an
> "industrial image processing application," which interests me. At the
> moment I would call it an impressive proof of concept, but you have many
> useful ideas for its future.

I am not offended at all, I certainly agree with Pavel's opinion of 'do
not invent new interfaces needlessly'. But this is a use case significantly
different from what the v4l2 api is aimed at.

As I wrote earlier, if it were not for other reasons, then changing the
API is not an option because the software already ships. I can place the
driver somewhere else in the kernel tree. I can fix any issues that
someone may find with it. I certainly cannot replace it with something
entirely different. If it is rejected, my only option is to submit the
rest of the code, without the capturing driver (the platform is already
in the kernel tree). Some may feel that a camera that cannot capture
images is somewhat pointless, though...

Of course, everyone is free to write a v4l2 driver, but will likely
find that the hardware is not very suitable as a streaming video
device. At least, it would be a very expensive one...

Thomas

--
Thomas Koeller, Software Development

Basler Vision Technologies
An der Strusbek 60-62
22926 Ahrensburg
Germany

Tel +49 (4102) 463-390
Fax +49 (4102) 463-46390

mailto:[email protected]
http://www.baslerweb.com