/* * ppscan.c -- parallel port scanner stuff * * This code is used to test the behaviour of various parallel port * devices including the artec scanner AS6E. Documentation on the * scanner is available at * * http://www4.infi.net/~cpinkham/sane/artec_as6e_parallel.ps.gz * * It has been tried on a FreeBSD 3.0 system using the "ppi" device, * and on 2.2.x using direct access to io ports (which makes this * a better candidate for a Linux port). */ #define DEB(x) #define DDB(x) x #define STRIPLEN 2550 /* max length of CCD strip */ #define SKIP_PIXELS 0 #define MAXBRIGHT 220 /* max bright during calibration */ #define ZMAX 255 /* correction factor */ #define CYCLES 1 /* how many cycles to calibrate */ #define LIM (MAXBRIGHT*CYCLES) #define CALIBRATE #if 0 /* DOCUMENTATION, really... */ HOW TO USE THIS CODE run the program with an optional parameter, it will run the proper init sequence. after this you can issue commands manually e.g. scan width height scans a color 300dpi image and send it to stdout in pnm format DOCUMENTATION INCONSISTENCIES... Main (initial) problems are: * using the EPP address and data ports (with an EPP-capable parallel port) seems not to work. Analysing the timings, it looks like the scanner expects /WR signal reasonably before the /AS or /DS strobes. As a consequence i had to simulate these instructions in software with direct access to the control lines. * reading from the scanner using EPP again seems not to work. * reading using "nibble mode" seems to work but i believe the scanner does not implement fully the IEEE1284 nibble mode protocol, in particular there appears to be no handshake from the scanner. In any case the nibble_in() routine below seems to read reliably from the scanner. Overall, the scanner behaves more or less as documented. The key sequence of the as6e is the same as documented. After sending the key sequence, the scanner is accessible using the parallel port. Right now i am only able to read from the scanner using nibble mode. Registers etc. Addr Width R/W Description 01 byte R/W Configuration register b7: enable scan (front panel led flashes) b6: passthrough mode (after this you need to issue the key sequence again to talk to the scanner). b5-b4: reverse transfer mode 00 nibble (tested) 01 byte 10 EPP 11 ECP 02 byte R STATUS 03 byte R/W CONFIGURE SETTING 04 byte R/W LINE CONTROL REGISTER sets scan mode, format, resolution 05 word R/W LINE TIME REGISTER system clocks per scan line / 8 07 word R/W start of scan line (left pixel) 09 word R/W end of scan line (right pixel (-1 ?)) 0B byte R/W THRESHOLD HI 2A byte R/W THRESHOLD LO 0C word R/W MOTOR CONTROL steps the motor should move before scan start (i.e. y start of image) 0E word R/W MOTOR STEPS steps the motor will move (i.e. end of scan) 10h word R/W MOTOR SPEED clocks per motor step. Default 0xc350, no less than 0x5000 12h byte R/W CIS led control 13h word R/W RED led on time 15h word R/W GREEN led on time 17h word R/W BLUE led on time 1eh byte R/W data port (to use with ppi_rbuf) ... I could not manage to turn the led on, despite writing to the appropriate register. INIT SEQUENCE ETC (from Jamie Honan) calibration: w8 0x01 0x10 byte mode w16 0x05 0x1771 line time register (6001 dec) w16 0x07 0x0000 start pixel w16 0x09 0x09f6 stop pixel (2550 dec.) w16 0x0c 0x0000 empty steps w16 0x0e 0x0004 motor steps w16 0x10 0xc350 motor speed (duration of one step) w8 0x04 0xe0 mode 300dpi, 24bpp w8 0x12 0x00 leds off, hw control ? w8 0x03 0x10 motor off w16 0x13 0x3000 red led on time w16 0x15 0x3000 green led on time w16 0x17 0x3600 blue led on time w8 0x19 0x03 max scan lines to store before stopping w16 0x1c 0x1de1 bytes for one scan line (+1) w8 0x27 0xff CIS ignored pixels w8 0x01 0x90 enable scan, byte mode poll reg.22 to see when there are lines in ram -- when it is not zero, then i can read data from register 0x1e The sequence for reading data seems to be the following: * select register 0x1e * read data in parallel mode (?) r8 22 #endif /* end of documentation */ #include #include /* for close() */ #include #if __FreeBSD__ > 2 #define LPTDEV "/dev/ppi0" #include #include #define PP_SDATA(fd, val) ioctl(fd, PPISDATA, val) #define PP_GDATA(fd, val) ioctl(fd, PPIGDATA, val) #define PP_GCTRL(fd, ctrl) ioctl(fd, PPIGCTRL, ctrl) #define PP_SCTRL(fd, ctrl) ioctl(fd, PPISCTRL, ctrl) #define PP_GSTATUS(fd, ctrl) ioctl(fd, PPIGSTATUS, ctrl) #define PP_GEPPA(fd, addr) ioctl(fd, PPIGEPPA, addr) #define PP_SEPPA(fd, addr) ioctl(fd, PPISEPPA, addr) #define PP_GEPPD(fd, addr) ioctl(fd, PPIGEPPD, addr) #define PP_SEPPD(fd, addr) ioctl(fd, PPISEPPD, addr) #else /* FreeBSD 2.x */ #define LPTDEV "/dev/io" #include #define DPORT 0x378 /* data port */ #define SPORT 0x379 /* status port */ #define CPORT 0x37A /* control port */ #define APORT 0x37B /* EPP address port */ #define EDPORT 0x37C /* EPP data port */ /* control register fields... */ #define STROBE 0x01 #define AUTOFEED 0x02 #define nINIT 0x04 #define SELECTIN 0x08 #define PCD 0x20 /* status register fields... */ #define nBUSY 0x80 #define nACK 0x40 #define PERROR 0x20 #define SELECT 0x10 #define nFAULT 0x08 #define TIMEOUT 0x01 #define PP_SDATA(fd, val) outb(DPORT, *(val)) #define PP_GDATA(fd, val) *(val) = inb(DPORT) #define PP_GCTRL(fd, ctrl) \ *(ctrl) = (inb(CPORT) ^ 0x0 ) #define PP_SCTRL(fd, ctrl) outb(CPORT, *(ctrl)) #define PP_GSTATUS(fd, ctrl) *(ctrl) = inb(SPORT) #define PP_GEPPA(fd, addrp) *((u_char *)addrp) = inb(APORT) #define PP_SEPPA(fd, addrp) outb(APORT, *(addrp)) #define PP_GEPPD(fd, datap) *((u_char *)datap) = inb(EDPORT) #define PP_SEPPD(fd, datap) outb(EDPORT, *(datap)) #endif typedef enum { NIBBLE, BIDIR, EPP, ECP } SlowMode ; SlowMode slow_mode = NIBBLE ; /* * this routine sends the activation key over the data port. * The actual key is supplied externally */ void send_key(int fd, int keylen, u_char *key) { int i ; for (i = 0 ; i < keylen ; i++ ) PP_SDATA(fd, &(key[i]) ) ; } /* * This routine is used to read data from the parallel port in * nibble mode. */ /* nACK is 0x40, nBUSY is 0x80 */ #define nibble2char(s) (0xf & (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4) ) u_char nibble_in(int fd) { u_char buf[2], c, ctrl; int i, ix ; i = PP_GCTRL(fd, &ctrl); c = (ctrl | nINIT) & ~(AUTOFEED | STROBE | SELECTIN) ; if (c != ctrl) fprintf(stderr, "nibble_in bad status 0x%x -- should be 0x%x\n", ctrl, c); for (ix = 0 ; ix < 2 ; ix++) { c |= AUTOFEED ; /* /DS=0 */ if (ix == 0) c &= ~nINIT ; /* first nibble ? */ PP_SCTRL(fd, &c); /* request nibble */ PP_GSTATUS(fd, &buf[ix]); c &= ~AUTOFEED ; c |= nINIT ; PP_SCTRL(fd, &c); /* ack data */ DEB(fprintf(stderr, "buf[%d]: 0x%x -> 0x%x\n", ix, buf[ix], nibble2char(buf[ix]) ); ) buf[ix] = nibble2char(buf[ix]); } buf[0] = buf[0] | (buf[1] << 4) ; return buf[0] ; } /* * These routines write to the EPP address and data registers, * respectively, using bit banging or the actual EPP command. * Unfortunately EPP writes do not have the desired effect on all * of my hardware because of timing issues -- * apparently the strobes come before the data! */ void ppi_wa(int fd, u_char val) { u_char r ; switch (slow_mode) { case NIBBLE: default : PP_SDATA(fd, &val); /* write value */ PP_GCTRL(fd, &r); r |= STROBE ; /* /WR=0 */ PP_SCTRL(fd, &r); r |= SELECTIN ; /* /AS=0 */ PP_SCTRL(fd, &r); r &= ~SELECTIN ; /* /AS=1 */ PP_SCTRL(fd, &r); r &= ~STROBE ; /* /WR=1 */ PP_SCTRL(fd, &r); break ; case EPP : PP_SEPPA(fd, &val); break ; } } void ppi_wd(int fd, u_char val) { u_char r ; switch (slow_mode) { case NIBBLE : default : PP_SDATA(fd, &val); /* write value */ PP_GCTRL(fd, &r); r |= STROBE ; /* /WR=0 */ PP_SCTRL(fd, &r); r |= AUTOFEED ; /* /DS=0 */ PP_SCTRL(fd, &r); r &= ~AUTOFEED ; /* /DS=1 */ PP_SCTRL(fd, &r); r &= ~STROBE ; /* /WR=1 */ PP_SCTRL(fd, &r); break ; case EPP: PP_SEPPD(fd, &val); break ; } } /* * read from the parallel port data registers using NIBBLE mode, * BIDIR (SPP) or EPP. As usual, EPP seems to have timing problems. */ inline u_char ppi_rd(int fd) { u_char val, r, oldr ; int i; switch (slow_mode) { default : case NIBBLE : val = nibble_in(fd); break ; case BIDIR : PP_GCTRL(fd, &r); oldr = r ; r |= (PCD | AUTOFEED) ; PP_SCTRL(fd, &r); PP_GDATA(fd, &val); PP_SCTRL(fd, &oldr); break ; case EPP : i = PP_GEPPD(fd, &val); break ; } return val; } /* * routines to read/write 8/16 bit words at a given address * on the parallel port bus */ void ppi_w16(int fd, u_char addr, u_short data) { ppi_wa(fd, addr); ppi_wd(fd, (data & 0xff) ); ppi_wa(fd, addr+1); ppi_wd(fd, ( (data>>8) & 0xff) ); } void ppi_w8(int fd, u_char addr, u_char data) { ppi_wa(fd, addr); ppi_wd(fd, (data & 0xff) ); } u_short ppi_r16(int fd, u_char addr) { u_char low, high; ppi_wa(fd, addr); low = ppi_rd(fd); ppi_wa(fd, addr+1); high = ppi_rd(fd); return (high << 8) | low ; } u_short ppi_r8(int fd, u_char addr) { ppi_wa(fd, addr); return ppi_rd(fd); } /* * ppi_rbuf reads a buffer in bidir mode. */ u_short ppi_rbuf(int fd, u_char addr, u_char *buf, int len) { int i; u_char val ; ppi_wa(fd, addr); PP_GCTRL(fd, &val); DEB( fprintf(stderr, " ctrl: 0x%x -> 0x%x -> 0x%x\n", val, val | PCD, val |PCD | AUTOFEED); ) val |= PCD ; /* turn on bidir */ PP_SCTRL(fd, &val); for (i=0 ; i < len ; i++) { buf[i] = ppi_rd(fd) ; } val &= ~PCD ; /* turn off bidir */ PP_SCTRL(fd, &val); return len ; } /* * ARTEC (AS6E) specific stuff. * * These global variables are used to define various operating modes * and parameters for the scanner. */ int rtime = 0 ; int gtime = 0 ; int btime = 0 ; int scanfd ; /* descriptor used for I/O */ /* * These arrays are used to compensate he brightness on each pixel * because apparently the sensor does not have a uniform response. */ double rcorr[STRIPLEN], gcorr[STRIPLEN], bcorr[STRIPLEN] ; u_char rdark[STRIPLEN], gdark[STRIPLEN], bdark[STRIPLEN] ; void artec_do_recal(int fd, char *mode); void artec_key(int fd); void artec_scan(int fd, char * mode, int x0, int y0, int x1, int y1); /* * send artec key sequence. The basic sequence is the forward one, * but some drivers appear to use the reverse one. */ void artec_key(int fd) { /* send artec key */ static u_char key[] = { /* forward sequence: d3 is a positive clock */ 0x60, 0x68, 0xc0, 0xc8, 0xf0, 0xf8, 0x20, 0x28, 0x10, 0x18, #if 0 /* reverse sequence: d3 is a negative clock */ 0x68, 0x60, 0xc8, 0xc0, 0xf8, 0xf0, 0x28, 0x20, 0x18, 0x10, #endif 0x00, /* end of sequence */ }; fprintf(stderr, "-- sending artec key sequence...\n"); send_key(fd, sizeof(key), key) ; } /* * artec_enable() sends the magic sequence and makes the * scanner able to listen to commands. * Use artec_disable() to go back to passthrough mode. */ void artec_enable(int fd) /* enable the scanner to receive commands */ { u_char r = 0xc4 ; /* set port in the right mode */ PP_SCTRL(fd, &r); slow_mode = NIBBLE ; artec_key(fd); /* out of passthrough mode */ ppi_w8(fd, 0x1, 0x0) ; /* set nibble mode */ } void artec_disable(int fd) { ppi_w8(fd, 0x1, 0x40); /* out of passthru mode */ } /* * start scanning in EPP mode */ void start_scan(int fd) { slow_mode = BIDIR ; /* this works */ ppi_w8(fd, 0x1, 0x90); /* start scan, bidir mode */ // slow_mode = NIBBLE ; /* this works */ //ppi_w8(fd, 0x1, 0x80); /* start scan, nibble mode */ // slow_mode = EPP ; /* this DOES NOT work */ // ppi_w8(fd, 0x1, 0xA0); /* start scan, EPP mode */ } void end_scan(int fd) { ppi_w8(fd, 0x1, 0x0); /* end scan */ slow_mode = NIBBLE ; } /* * procedure called at exit. */ void done(char *msg) { fprintf(stderr, msg); artec_disable(scanfd); close(scanfd); exit(0); } /* * wait for some lines to be ready from the scanner. * This is done reading register 22. */ int wait_lines(int fd, int needed, int delay) { int i, r; again: for (i=0 ; i < 120 ; i++ ) { r = ppi_r8(fd, 0x22) ; if (r >= needed) return r ; else if (i > 100) usleep(50000); } fprintf(stderr, "wait %d lines, have %d, retry %d\n", needed, r, delay); if (delay-- > 0) goto again ; return 0 ; /* failure */ } /* * wait home position */ void wait_home(int fd) { for (;;) { u_char r = ppi_r8(fd, 0x02) ; fprintf(stderr, "reg 2 0x%02x\r", r); if ( (r & 0x20) == 0) { fprintf(stderr, "not home...\n"); sleep(1); } else break ; } } /* * main scan procedures. */ #define SCAN_BUF_LINES 4 /* * color = 1 for color, 0 for grey, -1 for bw * to speed up things, allow reading multiple buffers if * available. */ void artec_scan_io(int fd, int lines, int len, int color, int x0) { #define MAXLINES 10 u_char red[MAXLINES][STRIPLEN], green[MAXLINES][STRIPLEN], blue[MAXLINES][STRIPLEN]; u_char *s, *p, totbuf[STRIPLEN*3]; int wlen = len ; int line ; DDB(fprintf(stderr, "++ scan %d lines, width %d, offset %d color %d\n", lines, len, x0, color); ) /* * write pnm header */ if (color < 0) /* fake PBM mode... */ fprintf(stdout,"P4\n%d %d\n", len, lines); else fprintf(stdout,"P%d\n%d %d\n255\n", (color>0) ? 6 : 5, len, lines); start_scan(fd); for (;lines > 0 ;) { int j, r=3 ; /* wait for 3 lines in buffer (?) */ if (lines < r) r = lines ; r = wait_lines(fd, r, 10) ; if (r == 0 ) done("data not ready"); DEB( fprintf(stderr, "-- got %d lines, miss %d\n", have_lines, lines);) if (r>=2) r-- ; if (r > MAXLINES) r = MAXLINES ; if (r > lines) r = lines ; for (line = 0 ; line < r ; line++ ) { ppi_rbuf(fd, 0x1e, red[line], len) ; if (color>0) { ppi_rbuf(fd, 0x1e, green[line], len) ; /* read g */ ppi_rbuf(fd, 0x1e, blue[line], len) ; /* read g */ } } for (line = 0 ; line < r ; line++ ) { p = totbuf ; if (color > 0) { wlen = 3 * len ; #define STORE(x) { int c = x ; *p++ = (c <0) ? 0 : (c > 255 ? 255 : c ) ; } for (j = 0 ; j < len ; j++) { STORE( (red[line][j] - rdark[j+x0]) * rcorr[j + x0] ) ; STORE( (green[line][j] - gdark[j+x0]) * gcorr[j + x0] ) ; STORE( (blue[line][j] - bdark[j+x0]) * bcorr[j + x0] ) ; } } else { wlen = len ; for (j = 0 ; j < len ; j++) STORE( (red[line][j] - rdark[j+x0]) * rcorr[j + x0] ) ; if (color < 0) { wlen = (len + 7) / 8 ; p = s = totbuf ; for (j = 0 ; j < len ; j++ ) { u_char c = 0 ; if (s[0] < 128) c |= 0x80 ; if (s[1] < 128) c |= 0x40 ; if (s[2] < 128) c |= 0x20 ; if (s[3] < 128) c |= 0x10 ; if (s[4] < 128) c |= 0x08 ; if (s[5] < 128) c |= 0x04 ; if (s[6] < 128) c |= 0x02 ; if (s[7] < 128) c |= 0x01 ; *p++ = c ; s += 8 ; } } } fwrite(totbuf, wlen, 1, stdout); lines -- ; } } end_scan(fd); done("scan done...\n"); } /* * parameters for the various modes are in struct scan_parms, * identified by name. find_mode() finds the matching entry, * or aborts if not found. */ struct scan_parms { char *mode; int m ; #define M_DPI300 0xc0 #define M_DPI200 0x80 #define M_DPI100 0x40 #define M_DPI50 0x00 #define M_BW 0x10 #define M_COLOR 0x20 #define M_GREY 0x30 #define M_AGVON 0x08 #define M_AGVOFF 0x00 int dpi ; int color ; int linetime ; int speed ; } ; struct scan_parms scan_parms[] = { /* mode m dpi color linetime,speed */ { "color300", 0xe0, 300, 1, 6001, 50000 } , { "col300", 0xe0, 300, 1, 6001, 50000 } , { "grey300", 0xf0, 300, 0, 6001, 50000 } , { "bw300", 0xf0, 300, -1, 6001, 50000 } , { "color200", 0xa0, 200, 1, 6001, 33300 } , { "grey200", 0xb0, 200, 0, 6001, 33300 } , { "bw200", 0xb0, 200, -1, 6001, 33300 } , { "color100", 0x60, 100, 1, 6001, 33300 } , { "grey100", 0x70, 100, 0, 6001, 33300 } , { "bw100", 0x70, 100, -1, 6001, 33300 } , { "color50", 0x28, 50, 1, 6001, 33300 } , { "grey50", 0x38, 50, 0, 6001, 33300 } , { NULL } } ; struct scan_parms * find_mode(char *mode) { struct scan_parms *p ; for (p = scan_parms ; p->mode != NULL ; p++) { if ( !strcmp(p->mode, mode) ) return p ; } fprintf(stderr, "FATAL: scan mode not found\n"); exit(0); } /* * this is the main scanning routine. */ void artec_scan(int fd, char * mode, int x0, int y0, int x1, int y1) { struct scan_parms *p ; u_short max_lines, bytes_per_line ; u_char led ; artec_do_recal(fd, mode); /* could become a nop */ artec_enable(fd); p = find_mode(mode); led = 0 ; /* XXX */ bytes_per_line = (p->color>0) ? 3 * (x1 - x0) : x1 - x0 ; ppi_w8(fd, 0x1, 0x0); /* reset register 0 */ ppi_w8(fd, 0x0b, 0x0); /* lineart threshold high */ ppi_w8(fd, 0x2a, 0x0); /* lineart threshold low */ ppi_w16(fd, 0x5, p->linetime); /* line time register */ /* use 0x1771 == 6001 for 300dpi */ /* use 0x1f41 == 8001 for 200dpi */ #define SCALED(x) ( ( (x) * 300 ) / p->dpi ) /* dimension are always in 1/300 in */ ppi_w16(fd, 0x7, SCALED(x0) ); /* start image */ ppi_w16(fd, 0x9, SCALED(x1) ); /* img stop */ #define YOFS 180 ppi_w16(fd, 0xc, YOFS + SCALED(y0) ); /* y empty steps */ ppi_w16(fd, 0xe, YOFS + SCALED(y1) ); /* y total steps */ ppi_w16(fd, 0x10, p->speed); /* clock per motor step */ /* 0x8000 for 200dpi (?) */ ppi_w8(fd, 0x4, p->m); /* scan mode */ ppi_w8(fd, 0x12, led); /* hw led control */ ppi_w8(fd, 0x3, 0x0); /* motor on */ ppi_w16(fd, 0x13, rtime ); ppi_w16(fd, 0x15, gtime ); ppi_w16(fd, 0x17, btime ); max_lines = 32768 / bytes_per_line; //max_lines = SCAN_BUF_LINES ; /* XXX */ ppi_w8(fd, 0x19, SCAN_BUF_LINES /* - 1*/); /* max lines to store... */ ppi_w8(fd, 0x2b, 0x32) ; /* watchdog.. 1.6s times this */ /* will stop if data not read for this long */ ppi_w8(fd, 0x25, 0xe8); /* motor hold time ??? */ ppi_w8(fd, 0x26, 0x00); /* motor back time ??? */ ppi_w8(fd, 0x27, SKIP_PIXELS); /* ignore pixels */ ppi_w16(fd, 0x1c, bytes_per_line ); /* bytes per scanline */ /* this is in actual pixels */ artec_scan_io(fd, y1-y0, x1 - x0, p->color, x0); } /* * runs the recalibrate sequence on the artec scanner */ int init_recal(int fd, struct scan_parms *p, u_short rtime, u_short gtime,u_short btime) { int len; len = ( STRIPLEN * p->dpi ) / 300 ; ppi_w8(fd, 0x1, 0x0); /* reset register 0 */ ppi_w16(fd, 0x5, p->linetime); /* line time register */ ppi_w16(fd, 0x7, 0x0); /* start image */ ppi_w16(fd, 0x9, STRIPLEN ); /* img len (default 2550) */ ppi_w16(fd, 0x10, p->speed); /* clock per motor step */ ppi_w8(fd, 0x4, p->m); /* 300dpi,color24bpp */ ppi_w8(fd, 0x12, 0x0); /* hw led control */ ppi_w8(fd, 0x3, 0x10); /* motor off */ ppi_w16(fd, 0xc, 0); /* skip y lines (default 0) */ ppi_w16(fd, 0xe, CYCLES+100 /* 4 */); /* total motor lines (???) */ ppi_w16(fd, 0x13, rtime ); ppi_w16(fd, 0x15, gtime ); ppi_w16(fd, 0x17, btime ); ppi_w8(fd, 0x19, 0x3); /* max lines to store... */ ppi_w16(fd, 0x1c, (p->color>0) ? len*3 : len ); /* bytes per scanline */ ppi_w8(fd, 0x27, SKIP_PIXELS); /* ignore pixels */ ppi_w8(fd, 0x0b, 0x80); /* lineart threshold high */ ppi_w8(fd, 0x2a, 0x70); /* lineart threshold low */ return len ; } /* * runs the recalibration sequence on the artec scanner, using * the "white" strip in the inside as a reference. * * NOTE: unfortunately, the strip is not really white, and so * your calibration parameters must be hand adjusted anyways... * */ void artec_do_recal(int fd, char *mode) { int i ; int rmin, rmax, gmin, gmax, bmin, bmax ; int got_lines = 0 ; int good, len ; int srbuf[STRIPLEN], sgbuf[STRIPLEN], sbbuf[STRIPLEN] ; struct scan_parms *p = find_mode(mode) ; if (rtime != 0) return ; fprintf(stderr, "starting recal sequence\n"); artec_enable(fd); rtime = gtime = btime = 0 ; fprintf(stderr, "-- calibrating, %d lines, target %d\n", CYCLES, LIM); wait_home(fd); for (;;) { bzero(srbuf, sizeof(srbuf) ); bzero(sgbuf, sizeof(sgbuf) ); bzero(sbbuf, sizeof(sbbuf) ); got_lines = 0 ; len = init_recal(fd, p, rtime, gtime, btime) ; start_scan(fd); for (got_lines = 0 ; got_lines < CYCLES ; got_lines++ ) { u_char rbuf[STRIPLEN], gbuf[STRIPLEN], bbuf[STRIPLEN] ; wait_lines(fd, 3, 10 ) ; ppi_rbuf(fd, 0x1e, rbuf, len) ; if (p->color>0) { ppi_rbuf(fd, 0x1e, gbuf, len) ; ppi_rbuf(fd, 0x1e, bbuf, len) ; } for (i = 0 ; i < len ; i++) { srbuf[i] += rbuf[i] ; sgbuf[i] += gbuf[i] ; sbbuf[i] += bbuf[i] ; } if (rtime == 0) { /* set the dark values */ for (i = 0 ; i < len ; i++) { rdark[i] = rbuf[i] ; rdark[i] = gbuf[i] ; rdark[i] = bbuf[i] ; } } } end_scan(fd); rmin = gmin = bmin = 255*CYCLES ; rmax = gmax = bmax = 0 ; #define MINMAX(buf, min, max) \ { if (buf < min) min = buf ; else if (buf > max) max = buf ; } for (i = 0 ; i < len ; i++) { int c = srbuf[i] - CYCLES*rdark[i] ; MINMAX(c, rmin, rmax); if (p->color > 0) { c = sgbuf[i] - CYCLES*gdark[i] ; MINMAX(c, gmin, gmax); c = sbbuf[i] - CYCLES*bdark[i] ; MINMAX(c, bmin, bmax); } } fprintf(stderr, "int/avg: r %5d(%3d..%3d) " "g %5d (%3d..%3d) b %5d (%3d..%3d)\r", rtime, rmin, rmax, gtime, gmin, gmax, btime, bmin, bmax ); /* * now see if the values need fixing */ #define FIX(var, max) { \ if (max < LIM-CYCLES) var += 2 + (LIM - max)/CYCLES ; \ else if (max > LIM+CYCLES) var -= 4 ; \ else good++ ; } good = 0 ; FIX(rtime, rmax) ; if (p->color > 0) { FIX(gtime, gmax) ; FIX(btime, bmax) ; } else { gtime = btime = rtime ; good += 2 ; } if (good == 3) break ; } fprintf(stderr, "\ncalibration done\n"); for (i = 0 ; i < STRIPLEN ; i++) rcorr[i] = gcorr[i] = bcorr[i] = 1.0 ; #ifdef CALIBRATE for (i = 0 ; i < len ; i++) { rcorr[i] = ( 1.0 * ZMAX * CYCLES ) / ( 1.0 * srbuf[i] ) ; if (p->color > 0 ) { gcorr[i] = ( 1.0 * ZMAX * CYCLES ) / ( 1.0 * sgbuf[i] ) ; bcorr[i] = ( 1.0 * ZMAX * CYCLES ) / ( 1.0 * sbbuf[i] ) ; } } #endif /* * at least on my scanner, the calibration strip is not really * white -- the red component is a bit weaker */ gtime = (int)(0.9 * gtime); btime = (int)(0.9 * btime); artec_disable(fd); } int main(int argc, char *argv[]) { int fd ; scanfd = fd = open(LPTDEV, O_RDWR); if (fd < 0) { fprintf(stderr, "sorry, cannot open lpt device %s\n", LPTDEV); exit(0); } switch(argc) { case 2: artec_scan(fd, argv[1], 0,0, 1000, 1000) ; break ; case 4: artec_scan(fd, argv[1], 0, 0, atoi(argv[2]), atoi(argv[3]) ); break ; case 6: artec_scan(fd, argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]) ); break ; default: fprintf(stderr, "unknown cmd <%s>\n", argv[1]); break ; } return 0 ; }