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