Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* asc.c - device driver for hand scanners |
2 | * | |
3 | * Current version supports: | |
4 | * | |
5 | * - AmiScan (Mustek) Color and BW hand scanners (GI1904 chipset) | |
6 | * | |
7 | * Copyright (c) 1995 Gunther Schadow. All rights reserved. | |
8 | * Copyright (c) 1995,1996,1997 Luigi Rizzo. All rights reserved. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by Gunther Schadow | |
21 | * and Luigi Rizzo. | |
22 | * 4. The name of the author may not be used to endorse or promote products | |
23 | * derived from this software without specific prior written permission. | |
24 | * | |
25 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
26 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
27 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
28 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
29 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
30 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
32 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
33 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
34 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
35 | */ | |
36 | /* | |
37 | * $FreeBSD: src/sys/i386/isa/asc.c,v 1.42.2.2 2001/03/01 03:22:39 jlemon Exp $ | |
21ce0dfa | 38 | * $DragonFly: src/sys/platform/pc32/isa/asc.c,v 1.21 2008/08/02 01:14:43 dillon Exp $ |
984263bc MD |
39 | */ |
40 | ||
1f2de5d4 | 41 | #include "use_asc.h" |
984263bc MD |
42 | #include <sys/param.h> |
43 | #include <sys/systm.h> | |
44 | #include <sys/conf.h> | |
45 | #include <sys/proc.h> | |
46 | #include <sys/buf.h> | |
47 | #include <sys/malloc.h> | |
48 | #include <sys/kernel.h> | |
49 | #include <sys/poll.h> | |
9183fa5b | 50 | #include <sys/selinfo.h> |
984263bc | 51 | #include <sys/uio.h> |
9acd5bbb | 52 | #include <sys/thread2.h> |
feafdc7d | 53 | #include <sys/bus.h> |
984263bc MD |
54 | |
55 | #include <machine/asc_ioctl.h> | |
56 | ||
21ce0dfa MD |
57 | #include <bus/isa/isa.h> |
58 | #include <bus/isa/isa_device.h> | |
feafdc7d | 59 | #include <bus/isa/isavar.h> |
1f2de5d4 | 60 | #include "ascreg.h" |
984263bc MD |
61 | |
62 | /*** | |
63 | *** CONSTANTS & DEFINES | |
64 | *** | |
65 | ***/ | |
66 | ||
67 | #define PROBE_FAIL 0 | |
68 | #define PROBE_SUCCESS IO_ASCSIZE | |
69 | #define ATTACH_FAIL 0 | |
70 | #define ATTACH_SUCCESS 1 | |
71 | #define SUCCESS 0 | |
72 | #define FAIL -1 | |
73 | #define INVALID FAIL | |
74 | ||
75 | #define DMA1_READY 0x08 | |
76 | #define ASCDEBUG | |
77 | #ifdef ASCDEBUG | |
26be20a0 | 78 | # define lprintf if(scu->flags & FLAG_DEBUG) kprintf |
984263bc MD |
79 | #else |
80 | # define lprintf (void) | |
81 | #endif | |
82 | ||
83 | #define TIMEOUT (hz*15) /* timeout while reading a buffer - default value */ | |
984263bc MD |
84 | |
85 | /*** | |
86 | *** LAYOUT OF THE MINOR NUMBER | |
87 | ***/ | |
88 | ||
89 | #define UNIT_MASK 0xc0 /* unit asc0 .. asc3 */ | |
90 | #define UNIT(x) (x >> 6) | |
91 | #define DBUG_MASK 0x20 | |
92 | #define FRMT_MASK 0x18 /* output format */ | |
93 | #define FRMT_RAW 0x00 /* output bits as read from scanner */ | |
94 | #define FRMT_GRAY 0x1 /* output gray mode for color scanner */ | |
95 | #define FRMT_PBM 0x08 /* output pbm format */ | |
96 | #define FRMT_PGM 0x18 | |
97 | ||
98 | /*** | |
99 | *** THE GEMOMETRY TABLE | |
100 | ***/ | |
101 | ||
102 | #define GREY_LINE 826 /* 825, or 826 , or 550 ??? */ | |
103 | static const struct asc_geom { | |
104 | int dpi; /* dots per inch */ | |
105 | int dpl; /* dots per line */ | |
106 | int bpl; /* bytes per line */ | |
107 | int g_res; /* get resolution value (ASC_STAT) */ | |
108 | } geomtab[] = { | |
109 | { 800, 3312, 414, ASC_RES_800}, | |
110 | { 700, 2896, 362, ASC_RES_700}, | |
111 | { 600, 2480, 310, ASC_RES_600}, | |
112 | { 500, 1656, 258, ASC_RES_500}, | |
113 | { 400, 1656, 207, ASC_RES_400}, | |
114 | { 300, 1240, 155, ASC_RES_300}, | |
115 | { 200, 832, 104, ASC_RES_200}, | |
116 | { 100, 416, 52, ASC_RES_100}, | |
117 | { 200, 3*GREY_LINE, 3*GREY_LINE, 0 /* returned by color scanner */}, | |
118 | { 200, GREY_LINE, GREY_LINE, 0 /* color scanner, grey mode */}, | |
119 | { INVALID, 416, 52, INVALID } /* terminator */ | |
120 | }; | |
121 | ||
122 | /*** | |
123 | *** THE TABLE OF UNITS | |
124 | ***/ | |
125 | ||
126 | struct _sbuf { | |
127 | size_t size; | |
128 | size_t rptr; | |
129 | size_t wptr; /* only changed in ascintr */ | |
130 | size_t count; | |
131 | char *base; | |
132 | }; | |
133 | ||
134 | struct asc_unit { | |
135 | long thedev; /* XXX */ | |
136 | int base; /* base address */ | |
137 | int dma_num; /* dma number */ | |
138 | char dma_byte; /* mask of byte for setting DMA value */ | |
139 | char int_byte; /* mask of byte for setting int value */ | |
140 | char cfg_byte; /* mirror of byte written to config reg (ASC_CFG). */ | |
141 | char cmd_byte; /* mirror of byte written to cmd port (ASC_CMD)*/ | |
142 | char portf_byte; | |
143 | int flags; | |
144 | #define ATTACHED 0x01 | |
145 | #define OPEN 0x02 | |
146 | #define READING 0x04 | |
147 | #define DMA_ACTIVE 0x08 | |
148 | #define SLEEPING 0x10 | |
149 | #define SEL_COLL 0x20 | |
150 | #define PBM_MODE 0x40 | |
151 | #define FLAG_DEBUG 0x80 | |
152 | int geometry; /* resolution as geomtab index */ | |
153 | int linesize; /* length of one scan line (from geom.table) */ | |
154 | int blen; /* length of buffer in lines */ | |
155 | int btime; /* timeout of buffer in seconds/hz */ | |
156 | struct _sbuf sbuf; | |
157 | long icnt; /* interrupt count XXX for debugging */ | |
158 | struct selinfo selp; | |
159 | int height; /* height, for pnm modes */ | |
160 | size_t bcount; /* bytes to read, for pnm modes */ | |
161 | }; | |
162 | ||
163 | static struct asc_unit unittab[NASC]; | |
164 | ||
165 | /*** I could not find a reasonable buffer size limit other than by | |
166 | *** experiments. MAXPHYS is obviously too much, while DEV_BSIZE and | |
167 | *** PAGE_SIZE are really too small. There must be something wrong | |
168 | *** with isa_dmastart/isa_dmarangecheck HELP!!! | |
169 | *** | |
170 | *** Note, must be DEFAULT_BLEN * samples_per_line <= MAX_BUFSIZE | |
171 | ***/ | |
172 | #define MAX_BUFSIZE 0xb000 /* XXX was 0x3000 */ | |
173 | #define DEFAULT_BLEN 16 | |
174 | ||
175 | /*** | |
176 | *** THE PER-DRIVER RECORD FOR ISA.C | |
177 | ***/ | |
178 | static int ascprobe (struct isa_device *isdp); | |
179 | static int ascattach(struct isa_device *isdp); | |
180 | struct isa_driver ascdriver = { ascprobe, ascattach, "asc" }; | |
181 | ||
1b51b0fa | 182 | static void ascintr(void *); |
984263bc MD |
183 | |
184 | static d_open_t ascopen; | |
185 | static d_close_t ascclose; | |
186 | static d_read_t ascread; | |
187 | static d_ioctl_t ascioctl; | |
188 | static d_poll_t ascpoll; | |
189 | ||
190 | #define CDEV_MAJOR 71 | |
191 | ||
fef8985e MD |
192 | static struct dev_ops asc_ops = { |
193 | { "asc", CDEV_MAJOR, 0 }, | |
194 | .d_open = ascopen, | |
195 | .d_close = ascclose, | |
196 | .d_read = ascread, | |
197 | .d_ioctl = ascioctl, | |
198 | .d_poll = ascpoll, | |
984263bc MD |
199 | }; |
200 | ||
201 | #define STATIC static | |
202 | ||
203 | /*** | |
204 | *** LOCALLY USED SUBROUTINES | |
205 | *** | |
206 | ***/ | |
207 | ||
208 | /*** | |
209 | *** get_resolution | |
210 | *** read resolution from the scanner | |
211 | ***/ | |
212 | static void | |
213 | get_resolution(struct asc_unit *scu) | |
214 | { | |
215 | int res, i, delay; | |
216 | ||
217 | res=0; | |
218 | scu->cmd_byte = ASC_STANDBY; | |
219 | outb(ASC_CMD, scu->cmd_byte); | |
377d4740 | 220 | tsleep((caddr_t)scu, PCATCH, "ascres", hz/10); |
984263bc MD |
221 | for(delay= 100; (res=inb(ASC_STAT)) & ASC_RDY_FLAG; delay--) |
222 | { | |
377d4740 | 223 | i = tsleep((caddr_t)scu, PCATCH, "ascres0", 1); |
984263bc MD |
224 | if ( ( i == 0 ) || ( i == EWOULDBLOCK ) ) |
225 | i = SUCCESS; | |
226 | else | |
227 | break; | |
228 | } | |
229 | if (delay==0) { | |
230 | lprintf("asc.get_resolution: timeout completing command\n"); | |
231 | return /* -1 */; | |
232 | } | |
233 | /* ... actual read resolution... */ | |
234 | res &= ASC_RES_MASK; | |
235 | for (i=0; geomtab[i].dpi != INVALID; i++) { | |
236 | if (geomtab[i].g_res == res) break; | |
237 | } | |
238 | if (geomtab[i].dpi==INVALID) { | |
239 | scu->geometry= i; /* INVALID; */ | |
240 | lprintf("asc.get_resolution: wrong resolution\n"); | |
241 | } else { | |
242 | lprintf("asc.get_resolution: %d dpi\n",geomtab[i].dpi); | |
243 | scu->geometry = i; | |
244 | } | |
245 | scu->portf_byte=0; /* default */ | |
246 | if (geomtab[scu->geometry].g_res==0 && !(scu->thedev&FRMT_GRAY)) { | |
247 | /* color scanner seems to require this */ | |
248 | scu->portf_byte=2; | |
249 | /* scu->geometry++; */ | |
250 | } | |
251 | scu->linesize = geomtab[scu->geometry].bpl; | |
252 | scu->height = geomtab[scu->geometry].dpl; /* default... */ | |
253 | } | |
254 | ||
255 | /*** | |
256 | *** buffer_allocate | |
257 | *** allocate/reallocate a buffer | |
258 | *** Now just checks that the preallocated buffer is large enough. | |
259 | ***/ | |
260 | ||
261 | static int | |
262 | buffer_allocate(struct asc_unit *scu) | |
263 | { | |
264 | size_t size, size1; | |
265 | ||
266 | size = scu->blen * scu->linesize; | |
267 | ||
268 | lprintf("asc.buffer_allocate: need 0x%x bytes\n", size); | |
269 | ||
270 | if ( size > MAX_BUFSIZE ) { | |
271 | size1=size; | |
272 | size= ( (MAX_BUFSIZE+scu->linesize-1) / scu->linesize)*scu->linesize; | |
273 | lprintf("asc.buffer_allocate: 0x%x bytes are too much, try 0x%x\n", | |
274 | size1, size); | |
275 | return ENOMEM; | |
276 | } | |
277 | ||
278 | scu->sbuf.size = size; | |
279 | scu->sbuf.rptr = 0; | |
280 | scu->sbuf.wptr = 0; | |
281 | scu->sbuf.count = 0; /* available data for reading */ | |
282 | ||
283 | lprintf("asc.buffer_allocate: ok\n"); | |
284 | ||
285 | return SUCCESS; | |
286 | } | |
287 | ||
288 | /*** dma_restart | |
289 | *** invoked locally to start dma. Must run in a critical section | |
290 | ***/ | |
291 | static void | |
292 | dma_restart(struct asc_unit *scu) | |
293 | { | |
294 | unsigned char al=scu->cmd_byte; | |
295 | ||
296 | if (geomtab[scu->geometry].g_res==0) {/* color */ | |
feafdc7d | 297 | isa_dmastart(BUF_CMD_READ, scu->sbuf.base+scu->sbuf.wptr, |
984263bc MD |
298 | scu->linesize + 90 /* XXX */ , scu->dma_num); |
299 | /* | |
300 | * looks like we have to set and then clear this | |
301 | * bit to enable the scanner to send interrupts | |
302 | */ | |
303 | outb( ASC_CMD, al |= 4 ); /* seems to disable interrupts */ | |
304 | #if 0 | |
305 | outb( ASC_CMD, al |= 8 ); /* ??? seems useless */ | |
306 | #endif | |
307 | outb( ASC_CMD, al &= 0xfb ); | |
308 | scu->cmd_byte = al; | |
309 | } else { /* normal */ | |
feafdc7d | 310 | isa_dmastart(BUF_CMD_READ, scu->sbuf.base+scu->sbuf.wptr, |
984263bc MD |
311 | scu->linesize, scu->dma_num); |
312 | /*** this is done in sub_20, after dmastart ? ***/ | |
313 | #if 0 | |
314 | outb( ASC_CMD, al |= 4 ); | |
315 | outb( ASC_CMD, al |= 8 ); /* ??? seems useless */ | |
316 | outb( ASC_CMD, al &= 0xfb ); | |
317 | scu->cmd_byte = al; | |
318 | #else | |
319 | outb( ASC_CMD, ASC_OPERATE); | |
320 | #endif | |
321 | } | |
322 | scu->flags |= DMA_ACTIVE; | |
323 | } | |
324 | ||
325 | /*** | |
326 | *** the main functions | |
327 | ***/ | |
328 | ||
329 | /*** asc_reset | |
330 | *** resets the scanner and the config bytes... | |
331 | ***/ | |
332 | static void | |
333 | asc_reset(struct asc_unit *scu) | |
334 | { | |
335 | scu->cfg_byte = 0 ; /* clear... */ | |
336 | scu->cmd_byte = 0 ; /* clear... */ | |
337 | ||
338 | outb(ASC_CFG,scu->cfg_byte); /* for safety, do this here */ | |
339 | outb(ASC_CMD,scu->cmd_byte); /* probably not needed */ | |
377d4740 | 340 | tsleep((caddr_t)scu, PCATCH, "ascres", hz/10); /* sleep .1 sec */ |
984263bc MD |
341 | |
342 | scu->blen = DEFAULT_BLEN; | |
343 | scu->btime = TIMEOUT; | |
344 | scu->height = 0 ; /* don't know better... */ | |
345 | } | |
346 | /************************************************************************** | |
347 | *** | |
348 | *** ascprobe | |
349 | *** read status port and check for proper configuration: | |
350 | *** - if address group matches (status byte has reasonable value) | |
351 | *** cannot check interrupt/dma, only clear the config byte. | |
352 | ***/ | |
353 | static int | |
354 | ascprobe (struct isa_device *isdp) | |
355 | { | |
356 | int unit = isdp->id_unit; | |
357 | struct asc_unit *scu = unittab + unit; | |
358 | int stb; | |
984263bc MD |
359 | |
360 | scu->base = isdp->id_iobase; /*** needed by the following macros ***/ | |
361 | scu->flags = FLAG_DEBUG; | |
362 | ||
363 | if ( isdp->id_iobase < 0 ) { | |
364 | lprintf("asc%d.probe: no iobase given\n", unit); | |
365 | return PROBE_FAIL; | |
366 | } | |
367 | ||
368 | if ((stb=inb(ASC_PROBE)) != ASC_PROBE_VALUE) { | |
369 | lprintf("asc%d.probe: failed, got 0x%02x instead of 0x%02x\n", | |
370 | unit, stb, ASC_PROBE_VALUE); | |
371 | return PROBE_FAIL; | |
372 | } | |
373 | ||
374 | /* | |
375 | * NOTE NOTE NOTE | |
376 | * the new AmiScan Color board uses int 10,11,12 instead of 3,5,10 | |
377 | * respectively. This means that the driver must act accordingly. | |
378 | * Unfortunately there is no easy way of telling which board one has, | |
379 | * other than trying to get an interrupt and noticing that it is | |
380 | * missing. use "option ASC_NEW_BOARD" if you have a new board. | |
381 | * | |
382 | */ | |
383 | ||
384 | #if ASC_NEW_BOARD | |
385 | #define ASC_IRQ_A 10 | |
386 | #define ASC_IRQ_B 11 | |
387 | #define ASC_IRQ_C 12 | |
388 | #else | |
389 | #define ASC_IRQ_A 3 | |
390 | #define ASC_IRQ_B 5 | |
391 | #define ASC_IRQ_C 10 | |
392 | #endif | |
393 | ||
394 | switch(ffs(isdp->id_irq) - 1) { | |
395 | case ASC_IRQ_A : | |
396 | scu->int_byte = ASC_CNF_IRQ3; | |
397 | break; | |
398 | case ASC_IRQ_B : | |
399 | scu->int_byte = ASC_CNF_IRQ5; | |
400 | break; | |
401 | case ASC_IRQ_C : | |
402 | scu->int_byte = ASC_CNF_IRQ10; | |
403 | break; | |
404 | #if 0 | |
405 | case -1: | |
406 | scu->int_byte = 0; | |
407 | lprintf("asc%d.probe: warning - going interruptless\n", unit); | |
408 | break; | |
409 | #endif | |
410 | default: | |
411 | lprintf("asc%d.probe: unsupported INT %d (only 3, 5, 10)\n", | |
412 | unit, ffs(isdp->id_irq) - 1 ); | |
413 | return PROBE_FAIL; | |
414 | } | |
415 | scu->dma_num = isdp->id_drq; | |
416 | switch(scu->dma_num) { | |
417 | case 1: | |
418 | scu->dma_byte = ASC_CNF_DMA1; | |
419 | break; | |
420 | case 3: | |
421 | scu->dma_byte = ASC_CNF_DMA3; | |
422 | break; | |
423 | default: | |
424 | lprintf("asc%d.probe: unsupported DMA %d (only 1 or 3)\n", | |
425 | unit, scu->dma_num); | |
426 | return PROBE_FAIL; | |
427 | } | |
428 | asc_reset(scu); | |
429 | /* lprintf("asc%d.probe: ok\n", unit); */ | |
430 | ||
431 | scu->flags &= ~FLAG_DEBUG; | |
432 | scu->icnt = 0; | |
433 | return PROBE_SUCCESS; | |
434 | } | |
435 | ||
436 | /************************************************************************** | |
437 | *** | |
438 | *** ascattach | |
439 | *** finish initialization of unit structure, get geometry value (?) | |
440 | ***/ | |
441 | ||
442 | static int | |
443 | ascattach(struct isa_device *isdp) | |
444 | { | |
445 | int unit = isdp->id_unit; | |
446 | struct asc_unit *scu = unittab + unit; | |
447 | ||
1b51b0fa | 448 | isdp->id_intr = (inthand2_t *)ascintr; |
984263bc | 449 | scu->flags |= FLAG_DEBUG; |
26be20a0 | 450 | kprintf("asc%d: [GI1904/Trust Ami-Scan Grey/Color]\n", unit); |
984263bc MD |
451 | |
452 | /* | |
453 | * Initialize buffer structure. | |
454 | * XXX this must be done early to give a good chance of getting a | |
455 | * contiguous buffer. This wastes memory. | |
456 | */ | |
457 | scu->sbuf.base = contigmalloc((unsigned long)MAX_BUFSIZE, M_DEVBUF, M_NOWAIT, | |
458 | 0ul, 0xfffffful, 1ul, 0x10000ul); | |
459 | if ( scu->sbuf.base == NULL ) | |
460 | { | |
461 | lprintf("asc%d.attach: buffer allocation failed\n", unit); | |
462 | return ATTACH_FAIL; /* XXX attach must not fail */ | |
463 | } | |
464 | scu->sbuf.size = INVALID; | |
465 | scu->sbuf.rptr = INVALID; | |
466 | ||
467 | scu->flags |= ATTACHED; | |
468 | /* lprintf("asc%d.attach: ok\n", unit); */ | |
469 | scu->flags &= ~FLAG_DEBUG; | |
470 | ||
471 | scu->selp.si_flags=0; | |
472 | scu->selp.si_pid=(pid_t)0; | |
473 | #define ASC_UID 0 | |
474 | #define ASC_GID 13 | |
fef8985e MD |
475 | make_dev(&asc_ops, unit<<6, ASC_UID, ASC_GID, 0666, "asc%d", unit); |
476 | make_dev(&asc_ops, ((unit<<6) + FRMT_PBM), | |
3e82b46c | 477 | ASC_UID, ASC_GID, 0666, "asc%dp", unit); |
fef8985e | 478 | make_dev(&asc_ops, ((unit<<6) + DBUG_MASK), |
3e82b46c | 479 | ASC_UID, ASC_GID, 0666, "asc%dd", unit); |
fef8985e | 480 | make_dev(&asc_ops, ((unit<<6) + DBUG_MASK+FRMT_PBM), |
3e82b46c | 481 | ASC_UID, ASC_GID, 0666, "asc%dpd", unit); |
984263bc MD |
482 | return ATTACH_SUCCESS; |
483 | } | |
484 | ||
485 | /************************************************************************** | |
486 | *** | |
487 | *** ascintr | |
488 | *** the interrupt routine, at the end of DMA... | |
489 | ***/ | |
490 | static void | |
477d3c1c | 491 | ascintr(void *arg) |
984263bc | 492 | { |
1b51b0fa | 493 | int unit = (int)arg; |
984263bc MD |
494 | struct asc_unit *scu = unittab + unit; |
495 | int chan_bit = 0x01 << scu->dma_num; | |
496 | ||
497 | scu->icnt++; | |
498 | /* ignore stray interrupts... */ | |
499 | if ((scu->flags & (OPEN |READING)) != (OPEN | READING) ) { | |
500 | /* must be after closing... */ | |
501 | scu->flags &= ~(OPEN | READING | DMA_ACTIVE | SLEEPING | SEL_COLL); | |
502 | return; | |
503 | } | |
504 | if ( (scu->flags & DMA_ACTIVE) && (inb(DMA1_READY) & chan_bit) != 0) { | |
505 | outb( ASC_CMD, ASC_STANDBY); | |
506 | scu->flags &= ~DMA_ACTIVE; | |
507 | /* bounce buffers... */ | |
feafdc7d | 508 | isa_dmadone(BUF_CMD_READ, scu->sbuf.base+scu->sbuf.wptr, |
984263bc MD |
509 | scu->linesize, scu->dma_num); |
510 | scu->sbuf.wptr += scu->linesize; | |
511 | if (scu->sbuf.wptr >= scu->sbuf.size) scu->sbuf.wptr=0; | |
512 | scu->sbuf.count += scu->linesize; | |
513 | if (scu->flags & SLEEPING) { | |
514 | scu->flags &= ~SLEEPING; | |
515 | wakeup((caddr_t)scu); | |
516 | } | |
517 | if (scu->sbuf.size - scu->sbuf.count >= scu->linesize) { | |
518 | dma_restart(scu); | |
519 | } | |
520 | if (scu->selp.si_pid) { | |
521 | selwakeup(&scu->selp); | |
522 | scu->selp.si_pid=(pid_t)0; | |
523 | scu->selp.si_flags = 0; | |
524 | } | |
525 | } | |
526 | } | |
527 | ||
528 | /************************************************************************** | |
529 | *** | |
530 | *** ascopen | |
531 | *** set open flag, set modes according to minor number | |
532 | *** FOR RELEASE: | |
533 | *** don't switch scanner on, wait until first read or ioctls go before | |
534 | ***/ | |
535 | ||
536 | STATIC int | |
fef8985e | 537 | ascopen(struct dev_open_args *ap) |
984263bc | 538 | { |
b13267a5 | 539 | cdev_t dev = ap->a_head.a_dev; |
984263bc MD |
540 | struct asc_unit *scu; |
541 | int unit; | |
542 | ||
543 | unit = UNIT(minor(dev)) & UNIT_MASK; | |
544 | if ( unit >= NASC ) | |
545 | { | |
546 | #ifdef ASCDEBUG | |
547 | /* XXX lprintf isn't valid here since there is no scu. */ | |
26be20a0 | 548 | kprintf("asc%d.open: unconfigured unit number (max %d)\n", unit, NASC); |
984263bc MD |
549 | #endif |
550 | return ENXIO; | |
551 | } | |
552 | scu = unittab + unit; | |
553 | if ( !( scu->flags & ATTACHED ) ) | |
554 | { | |
555 | lprintf("asc%d.open: unit was not attached successfully 0x%04x\n", | |
556 | unit, scu->flags); | |
557 | return ENXIO; | |
558 | } | |
559 | ||
560 | if ( minor(dev) & DBUG_MASK ) | |
561 | scu->flags |= FLAG_DEBUG; | |
562 | else | |
563 | scu->flags &= ~FLAG_DEBUG; | |
564 | ||
565 | switch(minor(dev) & FRMT_MASK) { | |
566 | case FRMT_PBM: | |
567 | scu->flags |= PBM_MODE; | |
568 | lprintf("asc%d.open: pbm mode\n", unit); | |
569 | break; | |
570 | case FRMT_RAW: | |
571 | lprintf("asc%d.open: raw mode\n", unit); | |
572 | scu->flags &= ~PBM_MODE; | |
573 | break; | |
574 | default: | |
575 | lprintf("asc%d.open: gray maps are not yet supported", unit); | |
576 | return ENXIO; | |
577 | } | |
578 | ||
579 | lprintf("asc%d.open: minor %d icnt %ld\n", unit, minor(dev), scu->icnt); | |
580 | ||
581 | if ( scu->flags & OPEN ) { | |
582 | lprintf("asc%d.open: already open", unit); | |
583 | return EBUSY; | |
584 | } | |
585 | if (isa_dma_acquire(scu->dma_num)) | |
586 | return(EBUSY); | |
587 | ||
588 | scu->flags = ATTACHED | OPEN; | |
589 | ||
590 | asc_reset(scu); | |
591 | get_resolution(scu); | |
592 | return SUCCESS; | |
593 | } | |
594 | ||
595 | static int | |
596 | asc_startread(struct asc_unit *scu) | |
597 | { | |
598 | /*** from here on, things can be delayed to the first read/ioctl ***/ | |
599 | /*** this was done in sub_12... ***/ | |
600 | scu->cfg_byte= scu->cmd_byte=0; /* init scanner */ | |
601 | outb(ASC_CMD, scu->cmd_byte); | |
602 | /*** this was done in sub_16, set scan len... ***/ | |
603 | outb(ASC_BOH, scu->portf_byte ); | |
604 | if (geomtab[scu->geometry].g_res==0) { /* color */ | |
605 | scu->cmd_byte = 0x00 ; | |
606 | } else { | |
607 | scu->cmd_byte = 0x90 ; | |
608 | } | |
609 | outb(ASC_CMD, scu->cmd_byte); | |
610 | outb(ASC_LEN_L, scu->linesize & 0xff /* len_low */); | |
611 | outb(ASC_LEN_H, (scu->linesize >>8) & 0xff /* len_high */); | |
612 | /*** this was done in sub_21, config DMA ... ***/ | |
613 | scu->cfg_byte |= scu->dma_byte; | |
614 | outb(ASC_CFG, scu->cfg_byte); | |
615 | /*** sub_22: enable int on the scanner ***/ | |
616 | scu->cfg_byte |= scu->int_byte; | |
617 | outb(ASC_CFG, scu->cfg_byte); | |
618 | /*** sub_28: light on etc...***/ | |
619 | scu->cmd_byte = ASC_STANDBY; | |
620 | outb(ASC_CMD, scu->cmd_byte); | |
377d4740 | 621 | tsleep((caddr_t)scu, PCATCH, "ascstrd", hz/10); /* sleep .1 sec */ |
984263bc MD |
622 | return SUCCESS; |
623 | } | |
624 | ||
625 | /************************************************************************** | |
626 | *** | |
627 | *** ascclose | |
628 | *** turn off scanner, release the buffer | |
629 | *** should probably terminate dma ops, release int and dma. lr 12mar95 | |
630 | ***/ | |
631 | ||
632 | STATIC int | |
fef8985e | 633 | ascclose(struct dev_close_args *ap) |
984263bc | 634 | { |
b13267a5 | 635 | cdev_t dev = ap->a_head.a_dev; |
984263bc MD |
636 | int unit = UNIT(minor(dev)); |
637 | struct asc_unit *scu = unittab + unit; | |
638 | ||
639 | lprintf("asc%d.close: minor %d\n", | |
640 | unit, minor(dev)); | |
641 | ||
642 | if ( unit >= NASC || !( scu->flags & ATTACHED ) ) { | |
643 | lprintf("asc%d.close: unit was not attached successfully 0x%04x\n", | |
644 | unit, scu->flags); | |
645 | return ENXIO; | |
646 | } | |
647 | /* all this is in sub_29... */ | |
648 | /* cli(); */ | |
649 | outb(ASC_CFG, 0 ); /* don't save in CFG byte!!! */ | |
650 | scu->cmd_byte &= ~ASC_LIGHT_ON; | |
651 | outb(ASC_CMD, scu->cmd_byte);/* light off */ | |
377d4740 | 652 | tsleep((caddr_t)scu, PCATCH, "ascclo", hz/2); /* sleep 1/2 sec */ |
984263bc MD |
653 | scu->cfg_byte &= ~ scu->dma_byte ; /* disable scanner dma */ |
654 | scu->cfg_byte &= ~ scu->int_byte ; /* disable scanner int */ | |
655 | outb(ASC_CFG, scu->cfg_byte); | |
656 | /* --- disable dma controller ? --- */ | |
657 | isa_dma_release(scu->dma_num); | |
658 | /* --- disable interrupts on the controller (sub_24) --- */ | |
659 | ||
660 | scu->sbuf.size = INVALID; | |
661 | scu->sbuf.rptr = INVALID; | |
662 | ||
663 | scu->flags &= ~(FLAG_DEBUG | OPEN | READING); | |
664 | ||
665 | return SUCCESS; | |
666 | } | |
667 | ||
668 | static void | |
669 | pbm_init(struct asc_unit *scu) | |
670 | { | |
671 | int width = geomtab[scu->geometry].dpl; | |
f8c7a42d | 672 | int l= ksprintf(scu->sbuf.base,"P4 %d %d\n", width, scu->height); |
984263bc MD |
673 | char *p; |
674 | ||
675 | scu->bcount = scu->height * width / 8 + l; | |
676 | ||
677 | /* move header to end of sbuf */ | |
678 | scu->sbuf.rptr=scu->sbuf.size-l; | |
679 | bcopy(scu->sbuf.base, scu->sbuf.base+scu->sbuf.rptr,l); | |
680 | scu->sbuf.count = l; | |
681 | if (geomtab[scu->geometry].g_res!=0) { /* BW scanner */ | |
682 | for(p = scu->sbuf.base + scu->sbuf.rptr; l; p++, l--) | |
683 | *p = ~*p; | |
684 | } | |
685 | } | |
686 | /************************************************************************** | |
687 | *** | |
688 | *** ascread | |
689 | ***/ | |
690 | ||
691 | STATIC int | |
fef8985e | 692 | ascread(struct dev_read_args *ap) |
984263bc | 693 | { |
b13267a5 | 694 | cdev_t dev = ap->a_head.a_dev; |
fef8985e | 695 | struct uio *uio = ap->a_uio; |
984263bc MD |
696 | int unit = UNIT(minor(dev)); |
697 | struct asc_unit *scu = unittab + unit; | |
698 | size_t nbytes; | |
9acd5bbb | 699 | int res; |
984263bc MD |
700 | unsigned char *p; |
701 | ||
702 | lprintf("asc%d.read: minor %d icnt %ld\n", unit, minor(dev), scu->icnt); | |
703 | ||
704 | if ( unit >= NASC || !( scu->flags & ATTACHED ) ) { | |
705 | lprintf("asc%d.read: unit was not attached successfully 0x%04x\n", | |
706 | unit, scu->flags); | |
707 | return ENXIO; | |
708 | } | |
709 | ||
710 | if ( !(scu->flags & READING) ) { /*** first read... ***/ | |
711 | /* allocate a buffer for reading data and init things */ | |
712 | if ( (res = buffer_allocate(scu)) == SUCCESS ) scu->flags |= READING; | |
713 | else return res; | |
714 | asc_startread(scu); | |
715 | if ( scu->flags & PBM_MODE ) { /* initialize for pbm mode */ | |
716 | pbm_init(scu); | |
717 | } | |
718 | } | |
719 | ||
720 | lprintf("asc%d.read(before): " | |
721 | "sz 0x%x, rptr 0x%x, wptr 0x%x, cnt 0x%x bcnt 0x%x flags 0x%x icnt %ld\n", | |
722 | unit, scu->sbuf.size, scu->sbuf.rptr, | |
723 | scu->sbuf.wptr, scu->sbuf.count, scu->bcount,scu->flags, | |
724 | scu->icnt); | |
725 | ||
9acd5bbb | 726 | crit_enter(); |
984263bc MD |
727 | if ( scu->sbuf.count == 0 ) { /* no data avail., must wait */ |
728 | if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu); | |
729 | scu->flags |= SLEEPING; | |
377d4740 | 730 | res = tsleep((caddr_t)scu, PCATCH, "ascread", 0); |
984263bc MD |
731 | scu->flags &= ~SLEEPING; |
732 | if ( res == 0 ) res = SUCCESS; | |
733 | } | |
9acd5bbb | 734 | crit_exit(); |
984263bc | 735 | if (scu->flags & FLAG_DEBUG) |
377d4740 | 736 | tsleep((caddr_t)scu, PCATCH, "ascdly",hz); |
984263bc MD |
737 | lprintf("asc%d.read(after): " |
738 | "sz 0x%x, rptr 0x%x, wptr 0x%x, cnt 0x%x bcnt 0x%x flags 0x%x icnt %ld\n", | |
739 | unit, scu->sbuf.size, scu->sbuf.rptr, | |
740 | scu->sbuf.wptr, scu->sbuf.count, scu->bcount,scu->flags,scu->icnt); | |
741 | ||
742 | /* first, not more than available... */ | |
743 | nbytes = min( uio->uio_resid, scu->sbuf.count ); | |
744 | /* second, contiguous data... */ | |
745 | nbytes = min( nbytes, (scu->sbuf.size - scu->sbuf.rptr) ); | |
746 | /* third, one line (will remove this later, XXX) */ | |
747 | nbytes = min( nbytes, scu->linesize ); | |
748 | if ( (scu->flags & PBM_MODE) ) | |
749 | nbytes = min( nbytes, scu->bcount ); | |
750 | lprintf("asc%d.read: transferring 0x%x bytes\n", unit, nbytes); | |
751 | if (geomtab[scu->geometry].g_res!=0) { /* BW scanner */ | |
752 | lprintf("asc%d.read: invert buffer\n",unit); | |
753 | for(p = scu->sbuf.base + scu->sbuf.rptr, res=nbytes; res; p++, res--) | |
754 | *p = ~*p; | |
755 | } | |
756 | res = uiomove(scu->sbuf.base + scu->sbuf.rptr, nbytes, uio); | |
757 | if ( res != SUCCESS ) { | |
758 | lprintf("asc%d.read: uiomove failed %d", unit, res); | |
759 | return res; | |
760 | } | |
761 | ||
9acd5bbb | 762 | crit_enter(); |
984263bc MD |
763 | scu->sbuf.rptr += nbytes; |
764 | if (scu->sbuf.rptr >= scu->sbuf.size) scu->sbuf.rptr=0; | |
765 | scu->sbuf.count -= nbytes; | |
766 | /* having moved some data, can read mode */ | |
767 | if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu); | |
9acd5bbb | 768 | crit_exit(); |
984263bc MD |
769 | if ( scu->flags & PBM_MODE ) scu->bcount -= nbytes; |
770 | ||
771 | lprintf("asc%d.read: size 0x%x, pointer 0x%x, bcount 0x%x, ok\n", | |
772 | unit, scu->sbuf.size, scu->sbuf.rptr, scu->bcount); | |
773 | ||
774 | return SUCCESS; | |
775 | } | |
776 | ||
777 | /************************************************************************** | |
778 | *** | |
779 | *** ascioctl | |
780 | ***/ | |
781 | ||
782 | STATIC int | |
fef8985e | 783 | ascioctl(struct dev_ioctl_args *ap) |
984263bc | 784 | { |
b13267a5 | 785 | cdev_t dev = ap->a_head.a_dev; |
fef8985e | 786 | caddr_t data = ap->a_data; |
984263bc MD |
787 | int unit = UNIT(minor(dev)); |
788 | struct asc_unit *scu = unittab + unit; | |
789 | ||
790 | lprintf("asc%d.ioctl: minor %d\n", | |
791 | unit, minor(dev)); | |
792 | ||
793 | if ( unit >= NASC || !( scu->flags & ATTACHED ) ) { | |
794 | lprintf("asc%d.ioctl: unit was not attached successfully 0x%04x\n", | |
795 | unit, scu->flags); | |
796 | return ENXIO; | |
797 | } | |
fef8985e | 798 | switch(ap->a_cmd) { |
984263bc MD |
799 | case ASC_GRES: |
800 | asc_reset(scu); | |
801 | get_resolution(scu); | |
802 | *(int *)data=geomtab[scu->geometry].dpi; | |
803 | lprintf("asc%d.ioctl:ASC_GRES %ddpi\n", unit, *(int *)data); | |
804 | return SUCCESS; | |
805 | case ASC_GWIDTH: | |
806 | *(int *)data=geomtab[scu->geometry].dpl; | |
807 | lprintf("asc%d.ioctl:ASC_GWIDTH %d\n", unit, *(int *)data); | |
808 | return SUCCESS; | |
809 | case ASC_GHEIGHT: | |
810 | *(int *)data=scu->height; | |
811 | lprintf("asc%d.ioctl:ASC_GHEIGHT %d\n", unit, *(int *)data); | |
812 | return SUCCESS; | |
813 | case ASC_SHEIGHT: | |
814 | lprintf("asc%d.ioctl:ASC_SHEIGHT %d\n", unit, *(int *)data); | |
815 | if ( scu->flags & READING ) { | |
816 | lprintf("asc%d:ioctl on already reading unit\n", unit); | |
817 | return EBUSY; | |
818 | } | |
819 | scu->height=*(int *)data; | |
820 | return SUCCESS; | |
821 | #if 0 | |
822 | case ASC_GBLEN: | |
823 | *(int *)data=scu->blen; | |
824 | lprintf("asc%d.ioctl:ASC_GBLEN %d\n", unit, *(int *)data); | |
825 | return SUCCESS; | |
826 | case ASC_SBLEN: | |
827 | lprintf("asc%d.ioctl:ASC_SBLEN %d\n", unit, *(int *)data); | |
828 | if (*(int *)data * geomtab[scu->geometry].dpl / 8 > MAX_BUFSIZE) | |
829 | { | |
830 | lprintf("asc%d:ioctl buffer size too high\n", unit); | |
831 | return ENOMEM; | |
832 | } | |
833 | scu->blen=*(int *)data; | |
834 | return SUCCESS; | |
835 | case ASC_GBTIME: | |
836 | *(int *)data = scu->btime / hz; | |
837 | lprintf("asc%d.ioctl:ASC_GBTIME %d\n", unit, *(int *)data); | |
838 | return SUCCESS; | |
839 | case ASC_SBTIME: | |
840 | scu->btime = *(int *)data * hz; | |
841 | lprintf("asc%d.ioctl:ASC_SBTIME %d\n", unit, *(int *)data); | |
842 | return SUCCESS; | |
843 | #endif | |
844 | default: return ENOTTY; | |
845 | } | |
846 | return SUCCESS; | |
847 | } | |
848 | ||
849 | STATIC int | |
fef8985e | 850 | ascpoll(struct dev_poll_args *ap) |
984263bc | 851 | { |
b13267a5 | 852 | cdev_t dev = ap->a_head.a_dev; |
984263bc MD |
853 | int unit = UNIT(minor(dev)); |
854 | struct asc_unit *scu = unittab + unit; | |
984263bc MD |
855 | int revents = 0; |
856 | ||
9acd5bbb | 857 | crit_enter(); |
984263bc | 858 | |
fef8985e | 859 | if (ap->a_events & (POLLIN | POLLRDNORM)) { |
984263bc | 860 | if (scu->sbuf.count >0) |
fef8985e | 861 | revents |= ap->a_events & (POLLIN | POLLRDNORM); |
984263bc MD |
862 | else { |
863 | if (!(scu->flags & DMA_ACTIVE)) | |
864 | dma_restart(scu); | |
9c3411aa SS |
865 | |
866 | selrecord(curthread, &scu->selp); | |
984263bc MD |
867 | } |
868 | } | |
9acd5bbb | 869 | crit_exit(); |
fef8985e MD |
870 | ap->a_events = revents; |
871 | return (0); | |
984263bc | 872 | } |