1 /* sane - Scanner Access Now Easy.
2    Copyright (C) Marian Eichholz 2001
3    This file is part of the SANE package.
4 
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 
18    As a special exception, the authors of SANE give permission for
19    additional uses of the libraries contained in this release of SANE.
20 
21    The exception is that, if you link a SANE library with other files
22    to produce an executable, this does not by itself cause the
23    resulting executable to be covered by the GNU General Public
24    License.  Your use of that executable is in no way restricted on
25    account of linking the SANE library code into it.
26 
27    This exception does not, however, invalidate any other reasons why
28    the executable file might be covered by the GNU General Public
29    License.
30 
31    If you submit changes to SANE to the maintainers to be included in
32    a subsequent release, you agree by submitting the changes that
33    those changes may be distributed with this exception intact.
34 
35    If you write modifications of your own for SANE, it is your choice
36    whether to permit this exception to apply to your modifications.
37    If you do not wish that, delete this exception notice.
38 */
39 
40 /* ======================================================================
41 
42 Userspace scan tool for the Microtek 3600 scanner
43 
44 grayscale scan routine
45 
46 (C) Marian Eichholz 2001
47 
48 ====================================================================== */
49 
50 #include "sm3600-scantool.h"
51 
52 /* **********************************************************************
53 
54 DoScanGray()
55 
56 ********************************************************************** */
57 
58 #define LINE_THRESHOLD   0x800
59 
60 static unsigned char uchRegs075[]={
61    /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x20,
62    /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
63    /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
64    /*R_LEN*/ 0x28, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
65    /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
66    /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x40,
67    /*0x13*/ 0x15, /*0x14*/ 0x80, /*0x15*/ 0x2A,
68    /*0x16*/ 0xC0, /*0x17*/ 0x40, /*0x18*/ 0xC0,
69    /*0x19*/ 0x40, /*0x1A*/ 0xFF, /*0x1B*/ 0x01,
70    /*0x1C*/ 0x88, /*0x1D*/ 0x40, /*0x1E*/ 0x4C,
71    /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
72    /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
73    /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
74    /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
75    /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
76    /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
77    /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
78    /*0x34*/ 0x83, /*0x35*/ 0x29, /*0x36*/ 0x00,
79    /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
80    /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
81    /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
82    /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x00,
83    /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
84    /*R_CTL*/ 0x39, /*0x47*/ 0xC0, /*0x48*/ 0x40,
85    /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
86 
87 static unsigned char uchRegs100[]={
88    /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x20,
89    /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
90    /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
91    /*R_LEN*/ 0x34, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
92    /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
93    /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
94    /*0x13*/ 0x15, /*0x14*/ 0x84, /*0x15*/ 0x2A,
95    /*0x16*/ 0xC2, /*0x17*/ 0x40, /*0x18*/ 0xC2,
96    /*0x19*/ 0x40, /*0x1A*/ 0xFF, /*0x1B*/ 0x01,
97    /*0x1C*/ 0x88, /*0x1D*/ 0x40, /*0x1E*/ 0x4C,
98    /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
99    /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
100    /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
101    /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
102    /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
103    /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
104    /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
105    /*0x34*/ 0x63, /*0x35*/ 0x29, /*0x36*/ 0x00,
106    /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
107    /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
108    /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
109    /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
110    /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
111    /*R_CTL*/ 0x39, /*0x47*/ 0xC2, /*0x48*/ 0x40,
112    /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
113 
114 static unsigned char uchRegs200[]={
115    /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x24,
116    /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
117    /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
118    /*R_LEN*/ 0x22, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
119    /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
120    /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
121    /*0x13*/ 0x15, /*0x14*/ 0x42, /*0x15*/ 0x15,
122    /*0x16*/ 0x42, /*0x17*/ 0x15, /*0x18*/ 0x42,
123    /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
124    /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
125    /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
126    /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
127    /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
128    /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
129    /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
130    /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
131    /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
132    /*0x34*/ 0x43, /*0x35*/ 0x29, /*0x36*/ 0x00,
133    /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
134    /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
135    /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
136    /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
137    /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
138    /*R_CTL*/ 0x39, /*0x47*/ 0x42, /*0x48*/ 0x15,
139    /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
140 
141 static unsigned char uchRegs300[]={
142    /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x2A,
143    /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
144    /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x2A,
145    /*R_LEN*/ 0x16, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
146    /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
147    /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x40,
148    /*0x13*/ 0x15, /*0x14*/ 0x40, /*0x15*/ 0x15,
149    /*0x16*/ 0x40, /*0x17*/ 0x15, /*0x18*/ 0x40,
150    /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
151    /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
152    /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
153    /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
154    /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
155    /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
156    /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
157    /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
158    /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
159    /*0x34*/ 0x03, /*0x35*/ 0x29, /*0x36*/ 0x00,
160    /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
161    /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
162    /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
163    /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
164    /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
165    /*R_CTL*/ 0x39, /*0x47*/ 0x40, /*0x48*/ 0x15,
166    /*0x49*/ 0x96, /*0x4A*/ 0x8C };
167 
168 static unsigned char uchRegs600[]={
169    /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x3F,
170    /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
171    /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
172    /*R_LEN*/ 0x16, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
173    /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
174    /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
175    /*0x13*/ 0x15, /*0x14*/ 0x42, /*0x15*/ 0x15,
176    /*0x16*/ 0x42, /*0x17*/ 0x15, /*0x18*/ 0x42,
177    /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
178    /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
179    /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
180    /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
181    /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
182    /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
183    /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
184    /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
185    /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
186    /*0x34*/ 0x03, /*0x35*/ 0x29, /*0x36*/ 0x00,
187    /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
188    /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
189    /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
190    /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
191    /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
192    /*R_CTL*/ 0x39, /*0x47*/ 0x42, /*0x48*/ 0x15,
193    /*0x49*/ 0x96, /*0x4A*/ 0x8C };
194 
195 /* ======================================================================
196 
197 ReadNextGrayLine()
198 
199 ====================================================================== */
200 
ReadNextGrayLine(PTInstance this)201 static TState ReadNextGrayLine(PTInstance this)
202 {
203   int           iWrite;
204   int           iDot;
205   unsigned char chBits;
206   int           iRead; /* read position in raw line */
207   int           nInterpolator;
208 
209   iWrite=0;
210 
211   while (iWrite<this->state.cxMax) /* max 1 time in reality */
212     {
213       while (iWrite<this->state.cxMax &&
214 	     this->state.iBulkReadPos<this->state.cchBulk)
215 	this->state.ppchLines[0][iWrite++] += /* add! */
216 	  this->state.pchBuf[this->state.iBulkReadPos++]<<4;
217       if (iWrite<this->state.cxMax) /* we need an additional chunk */
218 	{
219 	  if (this->state.bLastBulk)
220 	      return SANE_STATUS_EOF;
221 	  this->state.cchBulk=BulkReadBuffer(this,this->state.pchBuf,
222 					     USB_CHUNK_SIZE);
223 	  dprintf(DEBUG_SCAN,"bulk read: %d byte(s), line #%d\n",
224 		  this->state.cchBulk, this->state.iLine);
225 	  if (this->bWriteRaw)
226 	    fwrite(this->state.pchBuf,1,this->state.cchBulk,this->fhScan);
227 	  INST_ASSERT();
228 	  if (this->state.cchBulk!=USB_CHUNK_SIZE)
229 	    this->state.bLastBulk=true;
230 	  this->state.iBulkReadPos=0;
231 	}
232     } /* while raw line buffer acquiring */
233   this->state.iLine++;
234   iDot=0; chBits=0; /* init pixelbuffer */
235   for (nInterpolator=50, iWrite=0, iRead=0;
236        iRead<this->state.cxMax;
237        iRead++)
238     {
239       nInterpolator+=this->state.nFixAspect;
240       if (nInterpolator<100) continue; /* res. reduction */
241       nInterpolator-=100;
242       if (iWrite>=this->state.cchLineOut) continue;
243       /* dprintf(DEBUG_SCAN," i=%d",iTo); */
244       if (this->mode==gray)
245 	  this->state.pchLineOut[iWrite++]=
246 	    this->state.ppchLines[0][iRead]>>4;
247       else
248 	{
249 	  unsigned char chBit; /* 1=white */
250 	  if (this->mode==line)
251 	    chBit=(this->state.ppchLines[0][iRead]<LINE_THRESHOLD);
252 	  else
253 	    {
254 	      short nError=this->state.ppchLines[0][iRead];
255 	      /* printf("(%d)",nError); */
256 	      if (nError>=0xFF0)
257 		{
258 		  nError-=0xFF0;
259 		  chBit=0;
260 		}
261 	      else
262 		chBit=1;
263 	      /* since I sketched the Floyd-Steinberg
264 		 algorithm from heart, I have no idea, if
265 		 there is room for improvement in the
266 		 coefficients.  If You know, please drop
267 		 me a line (eichholz@computer.org, 1.4.2001) */
268 #define FASTDITHER
269 #ifdef FASTDITHER
270 	      this->state.ppchLines[0][iRead+1]+=(nError>>2); /* 8/16 */
271 	      this->state.ppchLines[1][iRead+1]+=(nError>>1);
272 	      this->state.ppchLines[1][iRead]  +=(nError>>2); /* 8/16 */
273 #else
274 	      this->state.ppchLines[0][iRead+1]+=(nError*5)>>4;
275 	      this->state.ppchLines[1][iRead+1]+=(nError*8)>>4;
276 	      this->state.ppchLines[1][iRead]  +=(nError*3)>>4;
277 #endif
278 	    }
279 	  chBits=(chBits<<1)|chBit;
280 	  iDot++;
281 	  if (iDot==8 && iWrite<this->state.cchLineOut)
282 	    {
283 	      this->state.pchLineOut[iWrite++]=chBits;
284 	      iDot=0; chBits=0;
285 	    }
286 	} /* gray pixel postprocessing */
287     } /* line postprocessing */
288   if (iDot && iWrite<this->state.cchLineOut)
289     this->state.pchLineOut[iWrite++]=chBits;
290   /* cycle the history lines and clear the preread buffer*/
291   {
292     short *p=this->state.ppchLines[0];
293     this->state.ppchLines[0]=this->state.ppchLines[1];
294     this->state.ppchLines[1]=p;
295     memset(this->state.ppchLines[1],0,(this->state.cxMax+1)*sizeof(short));
296   }
297   return SANE_STATUS_GOOD;
298 }
299 
300 /* ======================================================================
301 
302 StartScanGray()
303 
304 ====================================================================== */
305 
306 __SM3600EXPORT__
StartScanGray(TInstance *this)307 TState StartScanGray(TInstance *this)
308 {
309   unsigned char  *puchRegs;
310   int             i;
311   if (this->state.bScanning)
312     return SetError(this,SANE_STATUS_DEVICE_BUSY,"scan active");
313   memset(&(this->state),0,sizeof(TScanState));
314   this->state.ReadProc  =ReadNextGrayLine;
315   puchRegs=NULL;
316   switch (this->param.res)
317   {
318   case 75:  puchRegs=uchRegs075; break;
319   case 100: puchRegs=uchRegs100; break;
320   case 200: puchRegs=uchRegs200; break;
321   case 300: puchRegs=uchRegs300; break;
322   case 600: puchRegs=uchRegs600; break;
323   }
324   GetAreaSize(this);
325   this->state.cyTotalPath = this->param.y/2;
326   DoJog(this,this->state.cyTotalPath);
327   INST_ASSERT();
328   this->state.cyTotalPath += this->param.cy/2; /* for jogging back */
329 
330   /*
331     regular scan is asynchronously, that is,
332     the scanning is issued, and the driver does bulk reads,
333     until there are no data left.
334   */
335   RegWriteArray(this,R_ALL, NUM_SCANREGS, puchRegs); INST_ASSERT();
336   RegWrite(this,R_SPOS, 2,
337 	   this->param.x/2+this->calibration.xMargin); INST_ASSERT();
338   RegWrite(this,R_SLEN, 2, this->state.cyWindow); INST_ASSERT();
339   RegWrite(this,R_SWID, 2, this->state.cxWindow); INST_ASSERT();
340   RegWrite(this,R_STPS, 2, 0); INST_ASSERT();
341 
342   /* upload gamma table */
343   RegWrite(this,0x41,1,0x01); /* gamma, gray */
344   RegWrite(this,0x40,1,0x20); /* FIFO at   0x08000 */
345   UploadGammaTable(this,0,this->agammaY); INST_ASSERT();
346 
347   UploadGainCorrection(this, 0x2000);
348   INST_ASSERT();
349 
350   /* for halftone dithering we need one history line */
351   this->state.pchBuf=malloc(USB_CHUNK_SIZE);
352   this->state.cBacklog=2;
353   this->state.ppchLines=calloc(this->state.cBacklog,sizeof(short *));
354   if (!this->state.pchBuf || !this->state.ppchLines)
355     return FreeState(this,SetError(this,
356 				   SANE_STATUS_NO_MEM,"no buffers available"));
357   for (i=0; i<this->state.cBacklog; i++)
358     {
359       this->state.ppchLines[i]=calloc(this->state.cxMax+1,
360 				      sizeof(short)); /* 1 dummy at right edge */
361       if (!this->state.ppchLines[i])
362 	return FreeState(this,SetError(this,
363 				       SANE_STATUS_NO_MEM,"no buffers available"));
364     }
365 
366   /* calculate and prepare intermediate line transfer buffer */
367 
368   this->state.cchLineOut=(this->mode==gray)
369     ? this->state.cxPixel
370     : (this->state.cxPixel+7)/8;
371 
372   this->state.pchLineOut = malloc(this->state.cchLineOut);
373   if (!this->state.pchLineOut)
374     return FreeState(this,SetError(this,
375 				   SANE_STATUS_NO_MEM,
376 				   "no buffers available"));
377 
378   /* start the unit, when all buffers are available */
379 
380   RegWrite(this,R_CTL, 1, 0x39); INST_ASSERT();
381   RegWrite(this,R_CTL, 1, 0x79); INST_ASSERT();
382   RegWrite(this,R_CTL, 1, 0xF9); INST_ASSERT();
383 
384   this->state.bScanning = true;
385   return SANE_STATUS_GOOD;
386 }
387