FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/ctx.c
1 /*
2 * CORTEX-I Frame Grabber driver V1.0
3 *
4 * Copyright (C) 1994, Paul S. LaFollette, Jr. This software may be used,
5 * modified, copied, distributed, and sold, in both source and binary form
6 * provided that the above copyright and these terms are retained. Under
7 * no circumstances is the author responsible for the proper functioning
8 * of this software, nor does the author assume any responsibility
9 * for damages incurred with its use.
10 *
11 * $FreeBSD$
12 */
13
14 /*
15 *
16 *
17 *
18 * Device Driver for CORTEX-I Frame Grabber
19 * Made by ImageNation Corporation
20 * 1200 N.E. Keyues Road
21 * Vancouver, WA 98684 (206) 944-9131
22 * (I have no ties to this company, just thought you might want
23 * to know how to get in touch with them.)
24 *
25 * In order to understand this device, you really need to consult the
26 * manual which ImageNation provides when you buy the board. (And
27 * what a pleasure it is to buy something for a PC and actually get
28 * programming information along with it.) I will limit myself here to
29 * a few comments which are specific to this driver. See also the file
30 * ctxreg.h for definitions of registers and control bits.
31 *
32 * 1. Although the hardware supports low resolution (256 x 256)
33 * acqusition and display, I have not implemented access to
34 * these modes in this driver. There are some fairly quirky
35 * aspects to the way this board works in low resolution mode,
36 * and I don't want to deal with them. Maybe later.
37 *
38 * 2. Choosing the base address for the video memory: This is set
39 * using a combination of hardware and software, using the left
40 * most dip switch on the board, and the AB_SELECT bit of control
41 * port 1, according to the chart below:
42 *
43 * Left DIP switch || DOWN | UP |
44 * =================================================
45 * AB_SELECT = 0 || 0xA0000 | 0xB0000 |
46 * -------------------------------------------------
47 * AB_SELECT = 1 || 0xD0000 | 0xE0000 |
48 * ------------------------------------------------
49 *
50 * When the RAM_ENABLE bit of control port 1 is clear (0), the
51 * video ram is disconnected from the computer bus. This makes
52 * it possible, in principle, to share memory space with other
53 * devices (such as VGA) which can also disconnect themselves
54 * from the bus. It also means that multiple CORTEX-I boards
55 * can share the same video memory space. Disconnecting from the
56 * bus does not affect the video display of the video ram contents,
57 * so that one needs only set the RAM_ENABLE bit when actually
58 * reading or writing to memory. The cost of this is low,
59 * the benefits to me are great (I need more than one board
60 * in my machine, and 0xE0000 is the only address choice that
61 * doesn't conflict with anything) so I adopt this strategy here.
62 *
63 * XXX-Note... this driver has only been tested for the
64 * XXX base = 0xE0000 case!
65 *
66 * 3) There is a deficiency in the documentation from ImageNation, I
67 * think. In order to successfully load the lookup table, it is
68 * necessary to clear SEE_STORED_VIDEO in control port 0 as well as
69 * setting LUT_LOAD_ENABLE in control port 1.
70 *
71 * 4) This driver accesses video memory through read or write operations.
72 * Other functionality is provided through ioctl's, manifest
73 * constants for which are defined in ioctl_ctx.h. The ioctl's
74 * include:
75 * CTX_LIVE Display live video
76 * CTX_GRAB Grab a frame of video data
77 * CTX_H_ORGANIZE Set things up so that sequential read
78 * operations access horizontal lines of
79 * pixels.
80 * CTX_V_ORGANIZE Set things up so that sequential read
81 * operations access vertical lines of
82 * pixels.
83 * CTX_SET_LUT Set the lookup table from an array
84 * of 256 unsigned chars passed as the
85 * third parameter to ioctl.
86 * CTX_GET_LUT Return the current lookup table to
87 * the application as an array of 256
88 * unsigned chars. Again the third
89 * parameter to the ioctl call.
90 *
91 * Thus,
92 * ioctl(fi, CTX_H_ORGANIZE, 0);
93 * lseek(fi, y*512, SEEK_SET);
94 * read(fi, buffer, 512);
95 *
96 * will fill buffer with 512 pixels (unsigned chars) which represent
97 * the y-th horizontal line of the image.
98 * Similarly,
99 * ioctl(fi, CTX_V_ORGANIZE, 0:
100 * lseek(fi, x*512+y, SEEK_SET);
101 * read(fi, buffer, 10);
102 *
103 * will read 10 a vertical line of 10 pixels starting at (x,y).
104 *
105 * Obviously, this sort of ugliness needs to be hidden away from
106 * the casual user, with an appropriate set of higher level
107 * functions.
108 *
109 */
110
111 #include "ctx.h"
112 #if NCTX > 0
113
114 #include "opt_devfs.h"
115
116 #include <sys/param.h>
117 #include <sys/systm.h>
118 #include <sys/conf.h>
119 #include <sys/uio.h>
120 #include <sys/kernel.h>
121 #include <sys/malloc.h>
122 #ifdef DEVFS
123 #include <sys/devfsext.h>
124 #endif /*DEVFS*/
125
126 #include <i386/isa/isa_device.h>
127 #include <i386/isa/ctxreg.h>
128 #include <machine/ioctl_ctx.h>
129 #include <machine/md_var.h>
130
131 static int waitvb(int port);
132
133 /* state flags */
134 #define OPEN (0x01) /* device is open */
135
136 #define UNIT(x) ((x) & 0x07)
137
138 static int ctxprobe __P((struct isa_device *devp));
139 static int ctxattach __P((struct isa_device *devp));
140 struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
141
142 static d_open_t ctxopen;
143 static d_close_t ctxclose;
144 static d_read_t ctxread;
145 static d_write_t ctxwrite;
146 static d_ioctl_t ctxioctl;
147 #define CDEV_MAJOR 40
148
149 static struct cdevsw ctx_cdevsw =
150 { ctxopen, ctxclose, ctxread, ctxwrite, /*40*/
151 ctxioctl, nostop, nullreset, nodevtotty,/* cortex */
152 seltrue, nommap, NULL, "ctx", NULL, -1 };
153
154
155 #define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */
156 #define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */
157
158 /*
159 * Per unit shadow registers (because the dumb hardware is RO)
160 */
161
162 static struct ctx_soft_registers {
163 u_char *lutp;
164 u_char cp0;
165 u_char cp1;
166 u_char flag;
167 int iobase;
168 caddr_t maddr;
169 int msize;
170 void *devfs_token;
171 } ctx_sr[NCTX];
172
173
174 static int
175 ctxprobe(struct isa_device * devp)
176 {
177 int status;
178
179 if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */
180 status = 0;
181 else {
182 status = 1; /*XXX uses only one port? */
183 }
184 return (status);
185 }
186
187 static int
188 ctxattach(struct isa_device * devp)
189 {
190 struct ctx_soft_registers *sr;
191
192 sr = &(ctx_sr[devp->id_unit]);
193 sr->cp0 = 0; /* zero out the shadow registers */
194 sr->cp1 = 0; /* and the open flag. wait for */
195 sr->flag = 0; /* open to malloc the LUT space */
196 sr->iobase = devp->id_iobase;
197 sr->maddr = devp->id_maddr;
198 sr->msize = devp->id_msize;
199 #ifdef DEVFS
200 sr->devfs_token =
201 devfs_add_devswf(&ctx_cdevsw, 0, DV_CHR, 0, 0, 0600,
202 "ctx%d", devp->id_unit);
203 #endif /* DEVFS */
204 return (1);
205 }
206
207 static int
208 ctxopen(dev_t dev, int flags, int fmt, struct proc *p)
209 {
210 struct ctx_soft_registers *sr;
211 u_char unit;
212 int i;
213
214 unit = UNIT(minor(dev));
215
216 /* minor number out of range? */
217
218 if (unit >= NCTX)
219 return (ENXIO);
220 sr = &(ctx_sr[unit]);
221
222 if (sr->flag != 0) /* someone has already opened us */
223 return (EBUSY);
224
225 /* get space for the LUT buffer */
226
227 sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK);
228 if (sr->lutp == NULL)
229 return (ENOMEM);
230
231 sr->flag = OPEN;
232
233 /*
234 Set up the shadow registers. We don't actually write these
235 values to the control ports until after we finish loading the
236 lookup table.
237 */
238 sr->cp0 |= SEE_STORED_VIDEO;
239 if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
240 sr->cp1 |= AB_SELECT; /* map to B or E if necessary */
241 /* but don't enable RAM */
242 /*
243 Set up the lookup table initially so that it is transparent.
244 */
245
246 outb(sr->iobase + ctx_cp0, (u_char) 0);
247 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
248 for (i = 0; i < LUTSIZE; i++) {
249 outb(sr->iobase + ctx_lutaddr, (u_char) i);
250 sr->lutp[i] = (u_char) i;
251 outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
252 }
253 /*
254 Disable LUT loading, and push the data in the shadow
255 registers into the control ports.
256 */
257 outb(sr->iobase + ctx_cp0, sr->cp0);
258 outb(sr->iobase + ctx_cp1, sr->cp1);
259 return (0); /* successful open. All ready to go. */
260 }
261
262 static int
263 ctxclose(dev_t dev, int flags, int fmt, struct proc *p)
264 {
265 int unit;
266
267 unit = UNIT(minor(dev));
268 ctx_sr[unit].flag = 0;
269 free(ctx_sr[unit].lutp, M_DEVBUF);
270 ctx_sr[unit].lutp = NULL;
271 return (0);
272 }
273
274 static int
275 ctxwrite(dev_t dev, struct uio * uio, int ioflag)
276 {
277 int unit, status = 0;
278 int page, count, offset;
279 struct ctx_soft_registers *sr;
280
281 unit = UNIT(minor(dev));
282 sr = &(ctx_sr[unit]);
283
284 if (uio->uio_offset < 0)
285 return (EINVAL);
286 if (uio->uio_offset >= 4 * PAGESIZE)
287 page = 4; /* EOF */
288 else
289 page = (u_int)uio->uio_offset / PAGESIZE;
290 offset = (u_int)uio->uio_offset % PAGESIZE;
291 count = min(uio->uio_resid, PAGESIZE - offset);
292 while ((page >= 0) && (page <= 3) && (count > 0)) {
293 sr->cp0 &= ~3;
294 sr->cp0 |= page;
295 outb(sr->iobase + ctx_cp0, sr->cp0);
296
297 /*
298 Before doing the uiomove, we need to "connect" the frame buffer
299 ram to the machine bus. This is done here so that we can have
300 several different boards installed, all sharing the same memory
301 space... each board is only "connected" to the bus when its memory
302 is actually being read or written. All my instincts tell me that
303 I should disable interrupts here, so I have done so.
304 */
305
306 disable_intr();
307 sr->cp1 |= RAM_ENABLE;
308 outb(sr->iobase + ctx_cp1, sr->cp1);
309 status = uiomove(sr->maddr + offset, count, uio);
310 sr->cp1 &= ~RAM_ENABLE;
311 outb(sr->iobase + ctx_cp1, sr->cp1);
312 enable_intr();
313
314 page = (u_int)uio->uio_offset / PAGESIZE;
315 offset = (u_int)uio->uio_offset % PAGESIZE;
316 count = min(uio->uio_resid, PAGESIZE - offset);
317 }
318 if (uio->uio_resid > 0)
319 return (ENOSPC);
320 else
321 return (status);
322 }
323
324 static int
325 ctxread(dev_t dev, struct uio * uio, int ioflag)
326 {
327 int unit, status = 0;
328 int page, count, offset;
329 struct ctx_soft_registers *sr;
330
331 unit = UNIT(minor(dev));
332 sr = &(ctx_sr[unit]);
333
334 if (uio->uio_offset < 0)
335 return (EINVAL);
336 if (uio->uio_offset >= 4 * PAGESIZE)
337 page = 4; /* EOF */
338 else
339 page = (u_int)uio->uio_offset / PAGESIZE;
340 offset = (u_int)uio->uio_offset % PAGESIZE;
341 count = min(uio->uio_resid, PAGESIZE - offset);
342 while ((page >= 0) && (page <= 3) && (count > 0)) {
343 sr->cp0 &= ~3;
344 sr->cp0 |= page;
345 outb(sr->iobase + ctx_cp0, sr->cp0);
346 /*
347 Before doing the uiomove, we need to "connect" the frame buffer
348 ram to the machine bus. This is done here so that we can have
349 several different boards installed, all sharing the same memory
350 space... each board is only "connected" to the bus when its memory
351 is actually being read or written. All my instincts tell me that
352 I should disable interrupts here, so I have done so.
353 */
354 disable_intr();
355 sr->cp1 |= RAM_ENABLE;
356 outb(sr->iobase + ctx_cp1, sr->cp1);
357 status = uiomove(sr->maddr + offset, count, uio);
358 sr->cp1 &= ~RAM_ENABLE;
359 outb(sr->iobase + ctx_cp1, sr->cp1);
360 enable_intr();
361
362 page = (u_int)uio->uio_offset / PAGESIZE;
363 offset = (u_int)uio->uio_offset % PAGESIZE;
364 count = min(uio->uio_resid, PAGESIZE - offset);
365 }
366 if (uio->uio_resid > 0)
367 return (ENOSPC);
368 else
369 return (status);
370 }
371
372 static int
373 ctxioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
374 {
375 int error;
376 int unit, i;
377 struct ctx_soft_registers *sr;
378
379 error = 0;
380 unit = UNIT(minor(dev));
381 sr = &(ctx_sr[unit]);
382
383 switch (cmd) {
384 case CTX_LIVE:
385 sr->cp0 &= ~SEE_STORED_VIDEO;
386 outb(sr->iobase + ctx_cp0, sr->cp0);
387 break;
388 case CTX_GRAB:
389 sr->cp0 &= ~SEE_STORED_VIDEO;
390 outb(sr->iobase + ctx_cp0, sr->cp0);
391 sr->cp0 |= ACQUIRE;
392 if (waitvb(sr->iobase)) /* wait for vert blank to start
393 * acquire */
394 error = ENODEV;
395 outb(sr->iobase + ctx_cp0, sr->cp0);
396 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
397 error = ENODEV;
398 if (waitvb(sr->iobase))
399 error = ENODEV;
400 sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on
401 * display */
402 sr->cp0 |= SEE_STORED_VIDEO;
403 outb(sr->iobase + ctx_cp0, sr->cp0);
404 break;
405 case CTX_H_ORGANIZE:
406 sr->cp0 &= ~PAGE_ROTATE;
407 outb(sr->iobase + ctx_cp0, sr->cp0);
408 break;
409 case CTX_V_ORGANIZE:
410 sr->cp0 |= PAGE_ROTATE;
411 outb(sr->iobase + ctx_cp0, sr->cp0);
412 break;
413 case CTX_SET_LUT:
414 bcopy((u_char *) data, sr->lutp, LUTSIZE);
415 outb(sr->iobase + ctx_cp0, (u_char) 0);
416 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
417 for (i = 0; i < LUTSIZE; i++) {
418 outb(sr->iobase + ctx_lutaddr, i);
419 outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
420 }
421 outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control
422 * registers */
423 outb(sr->iobase + ctx_cp1, sr->cp1);
424 break;
425 case CTX_GET_LUT:
426 bcopy(sr->lutp, (u_char *) data, LUTSIZE);
427 break;
428 default:
429 error = ENODEV;
430 }
431
432 return (error);
433 }
434
435 static int
436 waitvb(int port)
437 { /* wait for a vertical blank, */
438 if (inb(port) == 0xff) /* 0xff means no board present */
439 return (1);
440
441 while ((inb(port) & VERTICAL_BLANK) != 0) {
442 }
443 while ((inb(port) & VERTICAL_BLANK) == 0) {
444 }
445
446 return (0);
447 }
448
449
450
451 static ctx_devsw_installed = 0;
452
453 static void
454 ctx_drvinit(void *unused)
455 {
456 dev_t dev;
457
458 if( ! ctx_devsw_installed ) {
459 dev = makedev(CDEV_MAJOR,0);
460 cdevsw_add(&dev,&ctx_cdevsw,NULL);
461 ctx_devsw_installed = 1;
462 }
463 }
464
465 SYSINIT(ctxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctx_drvinit,NULL)
466
467
468 #endif /* NCTX > 0 */
Cache object: 825d6ebdc05660c673ecf4f85bff142f
|