summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/ctx.c
diff options
context:
space:
mode:
authordg <dg@FreeBSD.org>1994-10-01 17:59:42 +0000
committerdg <dg@FreeBSD.org>1994-10-01 17:59:42 +0000
commit509369c3009b6f26517876b87f45c894fa7c0519 (patch)
tree6db9e31636333a891b29fc387171a9fad5c374ac /sys/i386/isa/ctx.c
parentc32e69883fe19e5e15cad90f2573376f24cae338 (diff)
downloadFreeBSD-src-509369c3009b6f26517876b87f45c894fa7c0519.zip
FreeBSD-src-509369c3009b6f26517876b87f45c894fa7c0519.tar.gz
Added Cortex-I Frame Grabber by Paul S. LaFollette, Jr.
Submitted by: Paul S. LaFollette, Jr.
Diffstat (limited to 'sys/i386/isa/ctx.c')
-rw-r--r--sys/i386/isa/ctx.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/sys/i386/isa/ctx.c b/sys/i386/isa/ctx.c
new file mode 100644
index 0000000..8ba2cc6
--- /dev/null
+++ b/sys/i386/isa/ctx.c
@@ -0,0 +1,415 @@
+/*
+ * CORTEX-I Frame Grabber driver V1.0
+ *
+ * Copyright (C) 1994, Paul S. LaFollette, Jr. This software may be used,
+ * modified, copied, distributed, and sold, in both source and binary form
+ * provided that the above copyright and these terms are retained. Under
+ * no circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ * $Id$
+ */
+
+/*
+ *
+ *
+ *
+ * Device Driver for CORTEX-I Frame Grabber
+ * Made by ImageNation Corporation
+ * 1200 N.E. Keyues Road
+ * Vancouver, WA 98684 (206) 944-9131
+ * (I have no ties to this company, just thought you might want
+ * to know how to get in touch with them.)
+ *
+ * In order to understand this device, you really need to consult the
+ * manual which ImageNation provides when you buy the board. (And
+ * what a pleasure it is to buy something for a PC and actually get
+ * programming information along with it.) I will limit myself here to
+ * a few comments which are specific to this driver. See also the file
+ * ctxreg.h for definitions of registers and control bits.
+ *
+ * 1. Although the hardware supports low resolution (256 x 256)
+ * acqusition and display, I have not implemented access to
+ * these modes in this driver. There are some fairly quirky
+ * aspects to the way this board works in low resolution mode,
+ * and I don't want to deal with them. Maybe later.
+ *
+ * 2. Choosing the base address for the video memory: This is set
+ * using a combination of hardware and software, using the left
+ * most dip switch on the board, and the AB_SELECT bit of control
+ * port 1, according to the chart below:
+ *
+ * Left DIP switch || DOWN | UP |
+ * =================================================
+ * AB_SELECT = 0 || 0xA0000 | 0xB0000 |
+ * -------------------------------------------------
+ * AB_SELECT = 1 || 0xD0000 | 0xE0000 |
+ * ------------------------------------------------
+ *
+ * When the RAM_ENABLE bit of control port 1 is clear (0), the
+ * video ram is disconnected from the computer bus. This makes
+ * it possible, in principle, to share memory space with other
+ * devices (such as VGA) which can also disconnect themselves
+ * from the bus. It also means that multiple CORTEX-I boards
+ * can share the same video memory space. Disconnecting from the
+ * bus does not affect the video display of the video ram contents,
+ * so that one needs only set the RAM_ENABLE bit when actually
+ * reading or writing to memory. The cost of this is low,
+ * the benefits to me are great (I need more than one board
+ * in my machine, and 0xE0000 is the only address choice that
+ * doesn't conflict with anything) so I adopt this strategy here.
+ *
+ * XXX-Note... this driver has only been tested for the
+ * XXX base = 0xE0000 case!
+ *
+ * 3) There is a deficiency in the documentation from ImageNation, I
+ * think. In order to successfully load the lookup table, it is
+ * necessary to clear SEE_STORED_VIDEO in control port 0 as well as
+ * setting LUT_LOAD_ENABLE in control port 1.
+ *
+ * 4) This driver accesses video memory through read or write operations.
+ * Other functionality is provided through ioctl's, manifest
+ * constants for which are defined in ioctl_ctx.h. The ioctl's
+ * include:
+ * CTX_LIVE Display live video
+ * CTX_GRAB Grab a frame of video data
+ * CTX_H_ORGANIZE Set things up so that sequential read
+ * operations access horizontal lines of
+ * pixels.
+ * CTX_V_ORGANIZE Set things up so that sequential read
+ * operations access vertical lines of
+ * pixels.
+ * CTX_SET_LUT Set the lookup table from an array
+ * of 256 unsigned chars passed as the
+ * third parameter to ioctl.
+ * CTX_GET_LUT Return the current lookup table to
+ * the application as an array of 256
+ * unsigned chars. Again the third
+ * parameter to the ioctl call.
+ *
+ * Thus,
+ * ioctl(fi, CTX_H_ORGANIZE, 0);
+ * lseek(fi, y*512, SEEK_SET);
+ * read(fi, buffer, 512);
+ *
+ * will fill buffer with 512 pixels (unsigned chars) which represent
+ * the y-th horizontal line of the image.
+ * Similarly,
+ * ioctl(fi, CTX_V_ORGANIZE, 0:
+ * lseek(fi, x*512+y, SEEK_SET);
+ * read(fi, buffer, 10);
+ *
+ * will read 10 a vertical line of 10 pixels starting at (x,y).
+ *
+ * Obviously, this sort of ugliness needs to be hidden away from
+ * the casual user, with an appropriate set of higher level
+ * functions.
+ *
+ */
+
+#include "ctx.h"
+#if NCTX > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include "isa.h"
+#include "isa_device.h"
+#include "ctxreg.h"
+#include <machine/ioctl_ctx.h>
+
+int waitvb(short);
+
+/* state flags */
+#define OPEN (0x01) /* device is open */
+
+#define UNIT(x) ((x) & 0x07)
+
+int ctxprobe(), ctxattach();
+struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
+
+#define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */
+#define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */
+
+/*
+ * Per unit shadow registers (because the dumb hardware is RO)
+*/
+
+struct ctx_soft_registers {
+ u_char *lutp;
+ u_char cp0;
+ u_char cp1;
+ u_char flag;
+ short iobase;
+ caddr_t maddr;
+ int msize;
+} ctx_sr[NCTX];
+
+
+
+int
+ctxprobe(struct isa_device * devp)
+{
+ int status;
+
+ if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */
+ status = 0;
+ else
+ status = 1;
+ return (status);
+}
+
+int
+ctxattach(struct isa_device * devp)
+{
+ struct ctx_soft_registers *sr;
+
+ sr = &(ctx_sr[devp->id_unit]);
+ sr->cp0 = 0; /* zero out the shadow registers */
+ sr->cp1 = 0; /* and the open flag. wait for */
+ sr->flag = 0; /* open to malloc the LUT space */
+ sr->iobase = devp->id_iobase;
+ sr->maddr = devp->id_maddr;
+ sr->msize = devp->id_msize;
+ return (1);
+}
+
+int
+ctxopen(dev_t dev, int flag)
+{
+ struct ctx_soft_registers *sr;
+ u_char unit;
+ int i;
+
+ unit = UNIT(minor(dev));
+
+ /* minor number out of range? */
+
+ if (unit >= NCTX)
+ return (ENXIO);
+ sr = &(ctx_sr[unit]);
+
+ if (sr->flag != 0) /* someone has already opened us */
+ return (EBUSY);
+
+ /* get space for the LUT buffer */
+
+ sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK);
+ if (sr->lutp == NULL)
+ return (ENOMEM);
+
+ sr->flag = OPEN;
+
+/*
+ Set up the shadow registers. We don't actually write these
+ values to the control ports until after we finish loading the
+ lookup table.
+*/
+ sr->cp0 |= SEE_STORED_VIDEO;
+ if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
+ sr->cp1 |= AB_SELECT; /* map to B or E if necessary */
+ /* but don't enable RAM */
+/*
+ Set up the lookup table initially so that it is transparent.
+*/
+
+ outb(sr->iobase + ctx_cp0, (u_char) 0);
+ outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
+ for (i = 0; i < LUTSIZE; i++) {
+ outb(sr->iobase + ctx_lutaddr, (u_char) i);
+ sr->lutp[i] = (u_char) i;
+ outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
+ }
+/*
+ Disable LUT loading, and push the data in the shadow
+ registers into the control ports.
+*/
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+ outb(sr->iobase + ctx_cp1, sr->cp1);
+ return (0); /* successful open. All ready to go. */
+}
+
+int
+ctxclose(dev_t dev, int flag)
+{
+ int unit;
+
+ unit = UNIT(minor(dev));
+ ctx_sr[unit].flag = 0;
+ free(ctx_sr[unit].lutp, M_DEVBUF);
+ ctx_sr[unit].lutp = NULL;
+ return (0);
+}
+
+int
+ctxwrite(dev_t dev, struct uio * uio)
+{
+ int unit, status = 0;
+ int page, count, offset;
+ struct ctx_soft_registers *sr;
+
+ unit = UNIT(minor(dev));
+ sr = &(ctx_sr[unit]);
+
+ page = uio->uio_offset / PAGESIZE;
+ offset = uio->uio_offset % PAGESIZE;
+ count = min(uio->uio_resid, PAGESIZE - offset);
+ while ((page >= 0) && (page <= 3) && (count > 0)) {
+ sr->cp0 &= ~3;
+ sr->cp0 |= page;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+
+/*
+ Before doing the uiomove, we need to "connect" the frame buffer
+ ram to the machine bus. This is done here so that we can have
+ several different boards installed, all sharing the same memory
+ space... each board is only "connected" to the bus when its memory
+ is actually being read or written. All my instincts tell me that
+ I should disable interrupts here, so I have done so.
+*/
+
+ disable_intr();
+ sr->cp1 |= RAM_ENABLE;
+ outb(sr->iobase + ctx_cp1, sr->cp1);
+ status = uiomove(sr->maddr + offset, count, uio);
+ sr->cp1 &= ~RAM_ENABLE;
+ outb(sr->iobase + ctx_cp1, sr->cp1);
+ enable_intr();
+
+ page = uio->uio_offset / PAGESIZE;
+ offset = uio->uio_offset % PAGESIZE;
+ count = min(uio->uio_resid, PAGESIZE - offset);
+ }
+ if (uio->uio_resid > 0)
+ return (ENOSPC);
+ else
+ return (status);
+}
+
+int
+ctxread(dev_t dev, struct uio * uio)
+{
+ int unit, status = 0;
+ int page, count, offset;
+ struct ctx_soft_registers *sr;
+
+ unit = UNIT(minor(dev));
+ sr = &(ctx_sr[unit]);
+
+ page = uio->uio_offset / PAGESIZE;
+ offset = uio->uio_offset % PAGESIZE;
+ count = min(uio->uio_resid, PAGESIZE - offset);
+ while ((page >= 0) && (page <= 3) && (count > 0)) {
+ sr->cp0 &= ~3;
+ sr->cp0 |= page;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+/*
+ Before doing the uiomove, we need to "connect" the frame buffer
+ ram to the machine bus. This is done here so that we can have
+ several different boards installed, all sharing the same memory
+ space... each board is only "connected" to the bus when its memory
+ is actually being read or written. All my instincts tell me that
+ I should disable interrupts here, so I have done so.
+*/
+ disable_intr();
+ sr->cp1 |= RAM_ENABLE;
+ outb(sr->iobase + ctx_cp1, sr->cp1);
+ status = uiomove(sr->maddr + offset, count, uio);
+ sr->cp1 &= ~RAM_ENABLE;
+ outb(sr->iobase + ctx_cp1, sr->cp1);
+ enable_intr();
+
+ page = uio->uio_offset / PAGESIZE;
+ offset = uio->uio_offset % PAGESIZE;
+ count = min(uio->uio_resid, PAGESIZE - offset);
+ }
+ if (uio->uio_resid > 0)
+ return (ENOSPC);
+ else
+ return (status);
+}
+
+int
+ctxioctl(dev_t dev, int cmd, caddr_t data, int flag)
+{
+ int error;
+ int unit, i;
+ struct ctx_soft_registers *sr;
+
+ error = 0;
+ unit = UNIT(minor(dev));
+ sr = &(ctx_sr[unit]);
+
+ switch (cmd) {
+ case CTX_LIVE:
+ sr->cp0 &= ~SEE_STORED_VIDEO;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+ break;
+ case CTX_GRAB:
+ sr->cp0 &= ~SEE_STORED_VIDEO;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+ sr->cp0 |= ACQUIRE;
+ if (waitvb(sr->iobase)) /* wait for vert blank to start
+ * acquire */
+ error = ENODEV;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+ if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
+ error = ENODEV;
+ if (waitvb(sr->iobase))
+ error = ENODEV;
+ sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on
+ * display */
+ sr->cp0 |= SEE_STORED_VIDEO;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+ break;
+ case CTX_H_ORGANIZE:
+ sr->cp0 &= ~PAGE_ROTATE;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+ break;
+ case CTX_V_ORGANIZE:
+ sr->cp0 |= PAGE_ROTATE;
+ outb(sr->iobase + ctx_cp0, sr->cp0);
+ break;
+ case CTX_SET_LUT:
+ bcopy((u_char *) data, sr->lutp, LUTSIZE);
+ outb(sr->iobase + ctx_cp0, (u_char) 0);
+ outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
+ for (i = 0; i < LUTSIZE; i++) {
+ outb(sr->iobase + ctx_lutaddr, i);
+ outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
+ }
+ outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control
+ * registers */
+ outb(sr->iobase + ctx_cp1, sr->cp1);
+ break;
+ case CTX_GET_LUT:
+ bcopy(sr->lutp, (u_char *) data, LUTSIZE);
+ break;
+ default:
+ error = ENODEV;
+ }
+
+ return (error);
+}
+
+int
+waitvb(short port)
+{ /* wait for a vertical blank, */
+ if (inb(port) == 0xff) /* 0xff means no board present */
+ return (1);
+
+ while ((inb(port) & VERTICAL_BLANK) != 0) {
+ }
+ while ((inb(port) & VERTICAL_BLANK) == 0) {
+ }
+
+ return (0);
+}
+#endif /* NCTX > 0 */
OpenPOWER on IntegriCloud