Skip to contentSkip to conversation

Image capturing driver for Basler eXcite smart camera

PATCH In review Thomas Koeller 9 participants 12 messages Aug 11, 2006 – Aug 22, 2006 MIPS

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 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?).

---
 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(-)
drivers/Kconfig +2 -0
index 8b11ceb..5b4d329 100644
@@ -74,4 +74,6 @@ source "drivers/rtc/Kconfig"
 
 source "drivers/dma/Kconfig"
 
+source "drivers/xicap/Kconfig"
+
 endmenu
drivers/Makefile +1 -0
index fc2d744..af1b1ee 100644
@@ -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/
drivers/xicap/Kconfig new file +30 -0
new file mode 100644
index 0000000..da17d82
@@ -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
+
drivers/xicap/Makefile new file +6 -0
new file mode 100644
index 0000000..12e1a3b
@@ -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
drivers/xicap/xicap_core.c new file +46 -0

46 lines not on this page · raw message

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");

drivers/xicap/xicap_gpi.c new file +113 -0

113 lines not on this page · raw message

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");

drivers/xicap/xicap_priv.h new file +34 -0
new file mode 100644
index 0000000..7c6b158
@@ -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) */

include/xicap/xicap.h new file +40 -0
new file mode 100644
index 0000000..6614bc4
@@ -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) */
Signature · 16 lines
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
#1 lore raw
Quoted 5 lines · 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 ?

Quoted 65 lines
---
 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(-)
drivers/Kconfig +2 -0
index 8b11ceb..5b4d329 100644
@@ -74,4 +74,6 @@ source "drivers/rtc/Kconfig"
 
 source "drivers/dma/Kconfig"
 
+source "drivers/xicap/Kconfig"
+
 endmenu
drivers/Makefile +1 -0
index fc2d744..af1b1ee 100644
@@ -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/
drivers/xicap/Kconfig new file +9 -0
new file mode 100644
index 0000000..da17d82
@@ -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)"

drivers/xicap/Kconfig new file +2 -0
+	---help---
+	 Enable basic support for frame capture devices on the

BASLER eXcite

drivers/xicap/Kconfig new file +12 -0
+	 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

drivers/xicap/Kconfig line 29

Quoted 6 lines
drivers/xicap/Kconfig new file +6 -0
+	 by PMC-Sierra, Inc.
+	 
+	 This driver can be built as a module, which will be named
+	 xicap_gpi.
+
+endmenu

---
~Randy

#2 lore raw
In reply to Thomas Koeller

Ar Iau, 2006-08-10 am 23:18 +0200, ysgrifennodd Thomas Koeller:

Quoted 2 lines

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

#3 lore raw
Quoted 12 lines · Alan Cox <alan <at> lxorguk.ukuu.org.uk> writes:

Ar Iau, 2006-08-10 am 23:18 +0200, ysgrifennodd Thomas Koeller:

Quoted 2 lines

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

#4 lore raw
Quoted 9 lines · Thomas Koeller wrote/
Quoted 7 lines · Alan Cox <alan <at> lxorguk.ukuu.org.uk> writes:

Ar Iau, 2006-08-10 am 23:18 +0200, ysgrifennodd Thomas Koeller:

Quoted 2 lines

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.

Quoted 7 lines
Quoted 3 lines

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

Quoted 1 line

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.

#5 lore raw
Quoted 9 lines · Éric Piel

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.

Quoted 10 lines

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.

Quoted 10 lines

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] :-)

Quoted 7 lines

See you,
Eric

Quoted 2 lines

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

Signature · 12 lines
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
#6 lore raw

Hi!

Quoted 10 lines
Quoted 9 lines

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!

Quoted 11 lines

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...

Signature · 1 line
Thanks for all the (sleeping) penguins.
#7 lore raw
Quoted 2 lines · Pavel Machek

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

Signature · 12 lines
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
#8 lore raw
Quoted 9 lines · Thomas Koeller
Quoted 2 lines · Pavel Machek

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?

Signature · 1 line
Thanks for all the (sleeping) penguins.
#9 lore raw
In reply to Thomas Koeller
Quoted 12 lines · Thomas Koeller
Quoted 2 lines · Pavel Machek

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'.

Quoted 2 lines

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.

Signature · 5 lines
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.
#10 lore raw
Quoted 7 lines · Bill Davidsen

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

Signature · 12 lines
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
#11 lore raw
In reply to Thomas Koeller
Quoted 4 lines · Thomas Koeller

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.

Review

Quoted 4 lines

+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */

(quoted diff) +2 -0
+
+#include <linux/config.h>

Unnecessary include (kbuild does this for you now)

Quoted 1 line

+static unsigned long devnum_bitmap = 0;

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

Review

Quoted 9 lines
(quoted diff) +9 -0
+/* 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.

Review

Quoted 3 lines
(quoted diff) +3 -0
+/* A class for xicap devices */
+static struct class xicap_class = {
+	.name		= (char *) xicap_name,

Is that cast necessary ?

Review

Quoted 3 lines
(quoted diff) +3 -0
+/* 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

Review

Quoted 9 lines
(quoted diff) +9 -0
+	/* 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.

Quoted 1 line

+MODULE_VERSION("0.0");

Heh, early days ? :-)

Review

Quoted 4 lines

+++ b/drivers/xicap/xicap_gpi.c
...

(quoted diff) +2 -0
+
+#include <linux/config.h>

Same as above. Unneeded.

Quoted 1 line

+#define VMAP_WORKAROUND 1

This needs a comment to explain what its doing.

drivers/xicap/xicap_gpi.c line 99

Quoted 11 lines
drivers/xicap/xicap_gpi.c new file +11 -0
+/*
+ * 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.

Review

Quoted 8 lines
(quoted diff) +8 -0
+	/* 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.

Review

Quoted 2 lines
(quoted diff) +2 -0
+	rsrc = xicap_gpi_get_resource(pdv, 0, rsrcname_gpi_slice);
+	if (unlikely(!rsrc)) goto errex;
	if (unlikely(!rsrc))
		goto errex;

Review

Quoted 1 line
(quoted diff) +1 -0
+	if (unlikely(!rsrc)) goto errex;
	if (unlikely(!rsrc))
		goto errex;

Review

Quoted 1 line
(quoted diff) +1 -0
+	if (unlikely(!rsrc)) goto errex;
	if (unlikely(!rsrc))
		goto errex;

Review

Quoted 1 line
(quoted diff) +1 -0
+	if (unlikely(!rsrc)) goto errex;

etc.

Review

Quoted 8 lines
(quoted diff) +8 -0
+	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

Review

Quoted 5 lines
(quoted diff) +5 -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();

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 ?

Review

Quoted 8 lines
(quoted diff) +8 -0
+	/*
+	 * 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.

Review

Quoted 2 lines
(quoted diff) +2 -0
+	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

Signature · 1 line
http://www.codemonkey.org.uk