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