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