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