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