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: src/sys/i386/isa/ctx.c,v 1.20.2.2 1999/09/05 08:12:29 peter Exp $
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 <sys/param.h>
115 #include <sys/systm.h>
116 #include <sys/conf.h>
117 #include <sys/ioctl.h>
118 #include <sys/proc.h>
119 #include <sys/uio.h>
120 #include <sys/kernel.h>
121 #include <sys/malloc.h>
122 #include <sys/kernel.h>
123 #ifdef DEVFS
124 #include <sys/devfsext.h>
125 #endif /*DEVFS*/
126
127 #include <i386/isa/isa_device.h>
128 #include <i386/isa/ctxreg.h>
129 #include <machine/ioctl_ctx.h>
130 #include <machine/md_var.h>
131
132 static int waitvb(int port);
133
134 /* state flags */
135 #define OPEN (0x01) /* device is open */
136
137 #define UNIT(x) ((x) & 0x07)
138
139 static int ctxprobe __P((struct isa_device *devp));
140 static int ctxattach __P((struct isa_device *devp));
141 struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
142
143 static d_open_t ctxopen;
144 static d_close_t ctxclose;
145 static d_read_t ctxread;
146 static d_write_t ctxwrite;
147 static d_ioctl_t ctxioctl;
148 #define CDEV_MAJOR 40
149
150 static struct cdevsw ctx_cdevsw =
151 { ctxopen, ctxclose, ctxread, ctxwrite, /*40*/
152 ctxioctl, nostop, nullreset, nodevtotty,/* cortex */
153 seltrue, nommap, NULL, "ctx", NULL, -1 };
154
155
156 #define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */
157 #define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */
158
159 /*
160 * Per unit shadow registers (because the dumb hardware is RO)
161 */
162
163 static struct ctx_soft_registers {
164 u_char *lutp;
165 u_char cp0;
166 u_char cp1;
167 u_char flag;
168 int iobase;
169 caddr_t maddr;
170 int msize;
171 void *devfs_token;
172 } ctx_sr[NCTX];
173
174
175 static int
176 ctxprobe(struct isa_device * devp)
177 {
178 int status;
179
180 if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */
181 status = 0;
182 else {
183 status = 1; /*XXX uses only one port? */
184 }
185 return (status);
186 }
187
188 static int
189 ctxattach(struct isa_device * devp)
190 {
191 struct ctx_soft_registers *sr;
192
193 sr = &(ctx_sr[devp->id_unit]);
194 sr->cp0 = 0; /* zero out the shadow registers */
195 sr->cp1 = 0; /* and the open flag. wait for */
196 sr->flag = 0; /* open to malloc the LUT space */
197 sr->iobase = devp->id_iobase;
198 sr->maddr = devp->id_maddr;
199 sr->msize = devp->id_msize;
200 return (1);
201 #ifdef DEVFS
202 sr->devfs_token =
203 devfs_add_devswf(&ctx_cdevsw, 0, DV_CHR, 0, 0, 0600,
204 "ctx%d", devp->id_unit);
205 #endif /* DEVFS */
206 }
207
208 static int
209 ctxopen(dev_t dev, int flags, int fmt, struct proc *p)
210 {
211 struct ctx_soft_registers *sr;
212 u_char unit;
213 int i;
214
215 unit = UNIT(minor(dev));
216
217 /* minor number out of range? */
218
219 if (unit >= NCTX)
220 return (ENXIO);
221 sr = &(ctx_sr[unit]);
222
223 if (sr->flag != 0) /* someone has already opened us */
224 return (EBUSY);
225
226 /* get space for the LUT buffer */
227
228 sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK);
229 if (sr->lutp == NULL)
230 return (ENOMEM);
231
232 sr->flag = OPEN;
233
234 /*
235 Set up the shadow registers. We don't actually write these
236 values to the control ports until after we finish loading the
237 lookup table.
238 */
239 sr->cp0 |= SEE_STORED_VIDEO;
240 if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
241 sr->cp1 |= AB_SELECT; /* map to B or E if necessary */
242 /* but don't enable RAM */
243 /*
244 Set up the lookup table initially so that it is transparent.
245 */
246
247 outb(sr->iobase + ctx_cp0, (u_char) 0);
248 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
249 for (i = 0; i < LUTSIZE; i++) {
250 outb(sr->iobase + ctx_lutaddr, (u_char) i);
251 sr->lutp[i] = (u_char) i;
252 outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
253 }
254 /*
255 Disable LUT loading, and push the data in the shadow
256 registers into the control ports.
257 */
258 outb(sr->iobase + ctx_cp0, sr->cp0);
259 outb(sr->iobase + ctx_cp1, sr->cp1);
260 return (0); /* successful open. All ready to go. */
261 }
262
263 static int
264 ctxclose(dev_t dev, int flags, int fmt, struct proc *p)
265 {
266 int unit;
267
268 unit = UNIT(minor(dev));
269 ctx_sr[unit].flag = 0;
270 free(ctx_sr[unit].lutp, M_DEVBUF);
271 ctx_sr[unit].lutp = NULL;
272 return (0);
273 }
274
275 static int
276 ctxwrite(dev_t dev, struct uio * uio, int ioflag)
277 {
278 int unit, status = 0;
279 int page, count, offset;
280 struct ctx_soft_registers *sr;
281
282 unit = UNIT(minor(dev));
283 sr = &(ctx_sr[unit]);
284
285 page = uio->uio_offset / PAGESIZE;
286 offset = uio->uio_offset % PAGESIZE;
287 count = min(uio->uio_resid, PAGESIZE - offset);
288 while ((page >= 0) && (page <= 3) && (count > 0)) {
289 sr->cp0 &= ~3;
290 sr->cp0 |= page;
291 outb(sr->iobase + ctx_cp0, sr->cp0);
292
293 /*
294 Before doing the uiomove, we need to "connect" the frame buffer
295 ram to the machine bus. This is done here so that we can have
296 several different boards installed, all sharing the same memory
297 space... each board is only "connected" to the bus when its memory
298 is actually being read or written. All my instincts tell me that
299 I should disable interrupts here, so I have done so.
300 */
301
302 disable_intr();
303 sr->cp1 |= RAM_ENABLE;
304 outb(sr->iobase + ctx_cp1, sr->cp1);
305 status = uiomove(sr->maddr + offset, count, uio);
306 sr->cp1 &= ~RAM_ENABLE;
307 outb(sr->iobase + ctx_cp1, sr->cp1);
308 enable_intr();
309
310 page = uio->uio_offset / PAGESIZE;
311 offset = uio->uio_offset % PAGESIZE;
312 count = min(uio->uio_resid, PAGESIZE - offset);
313 }
314 if (uio->uio_resid > 0)
315 return (ENOSPC);
316 else
317 return (status);
318 }
319
320 static int
321 ctxread(dev_t dev, struct uio * uio, int ioflag)
322 {
323 int unit, status = 0;
324 int page, count, offset;
325 struct ctx_soft_registers *sr;
326
327 unit = UNIT(minor(dev));
328 sr = &(ctx_sr[unit]);
329
330 page = uio->uio_offset / PAGESIZE;
331 offset = uio->uio_offset % PAGESIZE;
332 count = min(uio->uio_resid, PAGESIZE - offset);
333 while ((page >= 0) && (page <= 3) && (count > 0)) {
334 sr->cp0 &= ~3;
335 sr->cp0 |= page;
336 outb(sr->iobase + ctx_cp0, sr->cp0);
337 /*
338 Before doing the uiomove, we need to "connect" the frame buffer
339 ram to the machine bus. This is done here so that we can have
340 several different boards installed, all sharing the same memory
341 space... each board is only "connected" to the bus when its memory
342 is actually being read or written. All my instincts tell me that
343 I should disable interrupts here, so I have done so.
344 */
345 disable_intr();
346 sr->cp1 |= RAM_ENABLE;
347 outb(sr->iobase + ctx_cp1, sr->cp1);
348 status = uiomove(sr->maddr + offset, count, uio);
349 sr->cp1 &= ~RAM_ENABLE;
350 outb(sr->iobase + ctx_cp1, sr->cp1);
351 enable_intr();
352
353 page = uio->uio_offset / PAGESIZE;
354 offset = uio->uio_offset % PAGESIZE;
355 count = min(uio->uio_resid, PAGESIZE - offset);
356 }
357 if (uio->uio_resid > 0)
358 return (ENOSPC);
359 else
360 return (status);
361 }
362
363 static int
364 ctxioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *p)
365 {
366 int error;
367 int unit, i;
368 struct ctx_soft_registers *sr;
369
370 error = 0;
371 unit = UNIT(minor(dev));
372 sr = &(ctx_sr[unit]);
373
374 switch (cmd) {
375 case CTX_LIVE:
376 sr->cp0 &= ~SEE_STORED_VIDEO;
377 outb(sr->iobase + ctx_cp0, sr->cp0);
378 break;
379 case CTX_GRAB:
380 sr->cp0 &= ~SEE_STORED_VIDEO;
381 outb(sr->iobase + ctx_cp0, sr->cp0);
382 sr->cp0 |= ACQUIRE;
383 if (waitvb(sr->iobase)) /* wait for vert blank to start
384 * acquire */
385 error = ENODEV;
386 outb(sr->iobase + ctx_cp0, sr->cp0);
387 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
388 error = ENODEV;
389 if (waitvb(sr->iobase))
390 error = ENODEV;
391 sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on
392 * display */
393 sr->cp0 |= SEE_STORED_VIDEO;
394 outb(sr->iobase + ctx_cp0, sr->cp0);
395 break;
396 case CTX_H_ORGANIZE:
397 sr->cp0 &= ~PAGE_ROTATE;
398 outb(sr->iobase + ctx_cp0, sr->cp0);
399 break;
400 case CTX_V_ORGANIZE:
401 sr->cp0 |= PAGE_ROTATE;
402 outb(sr->iobase + ctx_cp0, sr->cp0);
403 break;
404 case CTX_SET_LUT:
405 bcopy((u_char *) data, sr->lutp, LUTSIZE);
406 outb(sr->iobase + ctx_cp0, (u_char) 0);
407 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
408 for (i = 0; i < LUTSIZE; i++) {
409 outb(sr->iobase + ctx_lutaddr, i);
410 outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
411 }
412 outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control
413 * registers */
414 outb(sr->iobase + ctx_cp1, sr->cp1);
415 break;
416 case CTX_GET_LUT:
417 bcopy(sr->lutp, (u_char *) data, LUTSIZE);
418 break;
419 default:
420 error = ENODEV;
421 }
422
423 return (error);
424 }
425
426 static int
427 waitvb(int port)
428 { /* wait for a vertical blank, */
429 if (inb(port) == 0xff) /* 0xff means no board present */
430 return (1);
431
432 while ((inb(port) & VERTICAL_BLANK) != 0) {
433 }
434 while ((inb(port) & VERTICAL_BLANK) == 0) {
435 }
436
437 return (0);
438 }
439
440
441
442 static ctx_devsw_installed = 0;
443
444 static void
445 ctx_drvinit(void *unused)
446 {
447 dev_t dev;
448
449 if( ! ctx_devsw_installed ) {
450 dev = makedev(CDEV_MAJOR,0);
451 cdevsw_add(&dev,&ctx_cdevsw,NULL);
452 ctx_devsw_installed = 1;
453 }
454 }
455
456 SYSINIT(ctxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctx_drvinit,NULL)
457
458
459 #endif /* NCTX > 0 */
Cache object: fca88c5162c5b06fe97e81f3f6ab55e0
|