/* * ppscanimage.c -- AS6E parallel port scanner command line tool * for linux * based on ppscan.c for FreeBSD!! * Documentation on the * scanner is available at * * http://www4.infi.net/~cpinkham/sane/artec_as6e_parallel.ps.gz * */ #include #include #define LPTDEV "/dev/lp1" #include #include #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 /* linux and FreeBSD differ in the argument order for outb!! */ #define PP_SDATA(fd, val) outb(*(val),DPORT) #define PP_GDATA(fd, val) *(val) = inb(DPORT) #define PP_GCTRL(fd, ctrl) \ *(ctrl) = (inb(CPORT) ^ 0x0 ) #define PP_SCTRL(fd, ctrl) outb(*(ctrl),CPORT) #define PP_GSTATUS(fd, ctrl) *(ctrl) = inb(SPORT) #define PP_GEPPA(fd, addrp) *((u_char *)addrp) = inb(APORT) #define PP_SEPPA(fd, addrp) outb(*(addrp),APORT) #define PP_GEPPD(fd, datap) *((u_char *)datap) = inb(EDPORT) #define PP_SEPPD(fd, datap) outb(*(datap),EDPORT) typedef enum { NIBBLE, BIDIR, EPP, ECP } SlowMode ; SlowMode slow_mode = NIBBLE ; int RTIME,BTIME,GTIME; int rc[4096][4],gc[4096][4],bc[4096][4]; /* calibration for the for resolutions 50dpi,100dpi,200dpi,300dpi */ /* * 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 ; ioperm(0x378,5,1); fprintf(stderr, "send key, length %d : ", keylen); for (i = 0 ; i < keylen ; i++ ) fprintf(stderr, " 0x%02x", key[i]); fprintf(stderr, "\n"); for (i = 0 ; i < keylen ; i++ ) PP_SDATA(fd, &(key[i]) ); fprintf(stderr, "send_key %d chars: success\n", keylen); } /* * 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 */ 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] ; } /* * this routine writes to the EPP address register, using either * bit banging or the actual EPP command (does not work on some * interfaces because of timing issues). */ 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 ; } } /* * this routine writes to the EPP data register. again, using * bit banging or the direct ioctl */ 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 ; } } 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); #if 0 fprintf(stderr, " ctrl: 0x%x -> 0x%x -> 0x%x\n", val, val | PCD, val |PCD | AUTOFEED); #endif 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 ; } /* * send artec key sequence */ 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) ; } /* * main scan procedures. */ typedef enum { GREY300, COLOR300, GREY200, COLOR200,GREY100,COLOR100,GREY50,COLOR50 } ScanMode; /*#define SCAN_BUF_LINES 8*/ #define SCAN_BUF_LINES 4 /* 8 is too large a value if x1-x0 >= 1400 since there are only 32kByte of Buffer ! However if the x1-x0 is too small this value also gives problems for unknown reasons! therefore this must be adjusted according to the value of x1-x0*/ #define BLACK_PIXEL 25 #define GAMMA_RED 1.0 #define GAMMA_GREEN 1.0 #define GAMMA_BLUE 1.0 int artec_scan_io(int fd, int lines, int len, int color, int max_lines, int res_ind, int x0) { #define LINELEN 0x4000 unsigned char bufr[LINELEN], bufg[LINELEN], bufb[LINELEN]; char totbuf[LINELEN*3]; float x; int have_lines,j ; /* * write pnm header */ fprintf(stdout,"P%d\n%d %d\n255\n", color ? 6 : 5, len, lines); slow_mode = BIDIR ; ppi_w8(fd, 0x1, 0x90); /* start scan */ for (;lines > 0 ;) { int i, r ; for (i=0 ; i < 1000 ; i++ ) { r = ppi_r8(fd, 0x22) ; /*fprintf(stderr, "reg 22 lines %d\n", r);*/ if (r < max_lines/2 && r < lines) { if (i > 100 ) usleep(50000); } else break ; } have_lines = r ; fprintf(stderr, "got %d lines, miss %d\n", have_lines, lines); for (i = 0 ; i < have_lines ; i++ ) { u_char *p = totbuf ; ppi_rbuf(fd, 0x1e, bufr, len) ; /* read r */ if (color) { ppi_rbuf(fd, 0x1e, bufg, len) ; /* read g */ ppi_rbuf(fd, 0x1e, bufb, len) ; /* read b */ } for (j = 0 ; j < len ; j++) { x=(1.0*bufr[j])/rc[j+x0][res_ind]; x=(255*x-BLACK_PIXEL)/(255-BLACK_PIXEL); if (x > 1 ) x=1; *p++ = 255*pow(x,1.0/GAMMA_RED); if (color) { x=(1.0*bufg[j])/gc[j+x0][res_ind]; x=(255*x-BLACK_PIXEL)/(255-BLACK_PIXEL); if (x > 1 ) x=1; *p++ = 255*pow(x,1.0/GAMMA_GREEN); x=(1.0*bufb[j])/bc[j+x0][res_ind]; x=(255*x-BLACK_PIXEL)/(255-BLACK_PIXEL); if (x > 1 ) x=1; *p++ = 255*pow(x,1.0/GAMMA_BLUE); } } if (color) fwrite(totbuf, len, 3, stdout); else fwrite(totbuf, len, 1, stdout); } lines -= have_lines ; } ppi_w8(fd, 0x1, 0x0); /* end scan */ slow_mode = NIBBLE ; fprintf(stderr, "scan done...\n"); exit(0); } int artec_scan(int fd, ScanMode mode, int x0, int y0, int x1, int y1) { u_char m ; int color ; u_short bytes_per_line; u_short line_time ; u_char r, led ; int rtime,gtime,btime; int max_lines; int res_ind ; /* index of the resolution */ float scale; rtime = RTIME; gtime = GTIME; btime = BTIME; fprintf(stderr,"using rtime=%i gtime=%i btime=%i\n",rtime,gtime,btime); r = 0xc4 ; PP_SCTRL(fd, &r); ppi_w8(fd, 0x1, 0x0) ; artec_key(fd); line_time = 6001; switch (mode) { case COLOR300: led = 0 ; m = 0xe0 ; bytes_per_line = 3 * (x1 - x0) ; color = 1 ; scale= 1; res_ind=3; break ; case COLOR200 : m = 0xa0 ; bytes_per_line = 3*(x1 - x0) ; color = 1 ; scale = 1.5; res_ind=2; break ; case COLOR100: led = 0 ; m = 0x60 ; bytes_per_line = 3 * (x1 - x0) ; color = 1 ; scale = 3 ; res_ind=1; break ; case COLOR50: led = 0 ; m = 0x20 ; bytes_per_line = 3 * (x1 - x0) ; color = 1 ; scale = 6 ; res_ind =0; break ; case GREY300 : led = 0 ; m = 0xf0 ; bytes_per_line = x1 - x0 ; line_time = 6001; color = 0 ; scale =1; res_ind=3; break ; case GREY200 : led = 0; m = 0xb0 ; bytes_per_line = x1 - x0 ; line_time = 6001; color = 0 ; scale = 1.5; res_ind=2; break ; case GREY100 : led =0 ; m = 0x70 ; bytes_per_line = x1 - x0 ; line_time = 6001; color = 0 ; scale = 3 ; res_ind =1; break ; case GREY50 : led = 0; m = 0x30 ; bytes_per_line = x1 - x0 ; line_time = 6001; color = 0 ; scale = 6 ; res_ind=0; break ; } r = 0xc4 ; 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, line_time); /* line time register */ /* scale is the factor between pixels and inch/300! */ ppi_w16(fd, 0x7, scale*x0); /* start image */ ppi_w16(fd, 0x9, scale*x1); /* img stop */ ppi_w16(fd, 0xc, 124+scale*y0); /* y empty steps */ /* 124 lines is the offset to skip over calibration image! */ ppi_w16(fd, 0xe, 124+scale*y1); /* y total steps */ ppi_w16(fd, 0x10, 0xc350); /* clock per motor step */ ppi_w8(fd, 0x4, 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; ppi_w8(fd, 0x19, max_lines-1 ); /* max lines to store... */ /* often uses 3... */ 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, 0xff); /* ignore pixels */ ppi_w16(fd, 0x1c, bytes_per_line-1); /* bytes per scanline 0x9f6*3-1 */ /* this is in actual pixels, not always 300dpi measures... */ artec_scan_io(fd, y1-y0, x1 - x0, color, max_lines,res_ind, x0); } /* * runs the recalibrate sequence on the artec scanner */ int artec_recal(int fd, u_short rtime, u_short gtime,u_short btime, u_short y_0,u_short y_1, u_short len, u_char m, u_char max_lines, u_short bytes_per_line) { ppi_w8(fd, 0x1, 0x0); /* reset register 0 */ ppi_w16(fd, 0x5, 0x1771); /* line time register */ ppi_w16(fd, 0x7, 0x0); /* start image */ ppi_w16(fd, 0x9, len); /* img len (default 2550) */ ppi_w16(fd, 0xc, y_0); /* skip pixels (default 0) */ ppi_w16(fd, 0xe, y_1); /* sw motor steps (32:white area)*/ ppi_w16(fd, 0x10, 0xc350); /* clock per motor step */ ppi_w8(fd, 0x4, m); ppi_w8(fd, 0x12, 0x0); /* hw led control */ ppi_w8(fd, 0x3, 0x10); /* motor off */ ppi_w16(fd, 0x13, rtime ); ppi_w16(fd, 0x15, gtime ); ppi_w16(fd, 0x17, btime ); ppi_w8(fd, 0x19, max_lines-1); /* max lines to store... */ ppi_w16(fd, 0x1c, bytes_per_line-1); /* bytes per scanline */ ppi_w8(fd, 0x27, 0xff); /* ignore pixels */ } /* * scans the area with y_0<=y<=y_1 on the artec scanner * and calculates the averages of the red, blue and green * pixel information */ int artec_scan_average(int fd, u_short y_0 ,u_short y_1,int rtime, int gtime,int btime,int *avg_r, int *avg_g, int *avg_b) { int i,j,k; int n_lines; u_short len; u_char r, buf[1048576],line[65536]; slow_mode = NIBBLE ; r = 0xc4 ; PP_SCTRL(fd, &r); ppi_w8(fd, 0x1, 0x0) ; len = 2550 ; *avg_r=0; *avg_g=0; *avg_b=0; n_lines=0; artec_recal(fd, rtime, gtime, btime, y_0,y_1,len,0xe0,3,3*len); slow_mode = BIDIR ; ppi_w8(fd, 0x1, 0x90); /* start scan */ for ( ;n_lines < y_1-y_0; ) { for (k=0 ; k < 1000 ; k++ ) { r = ppi_r8(fd, 0x22) ; /*fprintf(stderr, "reg 22 lines %d\n", r);*/ if (r< SCAN_BUF_LINES/2 ) { if (k>100) usleep(50000); } else break; } ppi_rbuf(fd, 0x1e, line, r*len*3) ; for (i=0;i100) usleep(50000); } else break; } ppi_rbuf(fd, 0x1e, line, r*length*3) ; for (i=0;i