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 color scan routine
45 
46 (C) Marian Eichholz 2001
47 
48 ====================================================================== */
49 
50 #include "sm3600-scantool.h"
51 
52 #define ORDER_RGB             "012"
53 #define ORDER_BRG             "120"
54 
55 /* **********************************************************************
56 
57 ReadNextColorLine()
58 
59 ********************************************************************** */
60 
ReadNextColorLine(PTInstance this)61 static TState ReadNextColorLine(PTInstance this)
62 {
63   int           iWrite,i;
64   int           iRead; /* read position in raw line */
65   int           nInterpolator;
66   int           iOffsetR,iOffsetG,iOffsetB;
67   short        *pchLineSwap;
68   TBool         bVisible;
69 
70   bVisible=false;
71   do {
72       iWrite=0;
73       while (iWrite<3*this->state.cxMax) /* max 1 time in reality */
74 	{
75 	  while (iWrite<3*this->state.cxMax &&
76 		 this->state.iBulkReadPos<this->state.cchBulk)
77 	    this->state.ppchLines[0][iWrite++] =
78 	      this->state.pchBuf[this->state.iBulkReadPos++];
79 	  if (iWrite<3*this->state.cxMax) /* we need an additional chunk */
80 	    {
81 	      if (this->state.bLastBulk)
82 		return SANE_STATUS_EOF;
83 	      this->state.cchBulk=BulkReadBuffer(this,this->state.pchBuf,
84 						 USB_CHUNK_SIZE);
85 	      dprintf(DEBUG_SCAN,"bulk read: %d byte(s), line #%d\n",
86 		      this->state.cchBulk, this->state.iLine);
87 	      if (this->bWriteRaw)
88 		fwrite(this->state.pchBuf,1,this->state.cchBulk,this->fhScan);
89 	      INST_ASSERT();
90 	      if (this->state.cchBulk!=USB_CHUNK_SIZE)
91 		this->state.bLastBulk=true;
92 	      this->state.iBulkReadPos=0;
93 	    }
94 	} /* while raw line buffer acquiring */
95       this->state.iLine++;
96       if (this->state.iLine>2*this->state.ySensorSkew)
97 	{
98 	  bVisible=true;
99 	  iOffsetR=(this->state.szOrder[0]-'0')*this->state.cxMax;
100 	  iOffsetG=(this->state.szOrder[1]-'0')*this->state.cxMax;
101 	  iOffsetB=(this->state.szOrder[2]-'0')*this->state.cxMax;
102 	  for (nInterpolator=100, iWrite=0, iRead=0;
103 	       iRead<3*this->state.cxMax && iWrite<this->state.cchLineOut;
104 	       iRead++)
105 	    {
106 	      nInterpolator+=this->state.nFixAspect;
107 	      if (nInterpolator<100) continue; /* res. reduction */
108 	      nInterpolator-=100;
109 	      /* dprintf(DEBUG_SCAN," i=%d",iTo); */
110 	      /* the first scan lines only fill the line backlog buffer */
111 	      {
112 		/* dprintf(DEBUG_SCAN,"assembling line %d\n",++this->state.iLine); */
113 		this->state.pchLineOut[iWrite++]=
114 		  this->state.ppchLines[2*this->state.ySensorSkew][iRead+iOffsetR];
115 		this->state.pchLineOut[iWrite++]=
116 		  this->state.ppchLines[1*this->state.ySensorSkew][iRead+iOffsetG];
117 		this->state.pchLineOut[iWrite++]=
118 		  this->state.ppchLines[0*this->state.ySensorSkew][iRead+iOffsetB];
119 	      }
120 	    }
121 	} /* if visible line */
122       /* cycle backlog buffers */
123       pchLineSwap=this->state.ppchLines[this->state.cBacklog-1];
124       for (i=this->state.cBacklog-2; i>=0; i--)
125 	this->state.ppchLines[i+1]=this->state.ppchLines[i];
126       this->state.ppchLines[0]=pchLineSwap;
127   } while (!bVisible);
128   return SANE_STATUS_GOOD;
129 }
130 
131 /* ======================================================================
132 
133 StartScanColor()
134 
135 ====================================================================== */
136 
137 /* Parameter are in resolution units! */
138 __SM3600EXPORT__
StartScanColor(TInstance *this)139 TState StartScanColor(TInstance *this)
140 {
141 
142   /* live could be easy: Simple calculate a window, start the scan,
143      get the data and get off. It dimply does not work.  We have to
144      deal with the fact, that the pixels are reported with color scan
145      line by color scan line, and have to be rearranged to RGB
146      triples. They even ar enot always in RGB order, but mostly in BRG
147      order. And they have a skew of 2/300 inch , given by the slider
148      construction.  Thus, we have to deal with several buffers, some
149      interpolation, and related management stuff. */
150   int             i;
151   if (this->state.bScanning)
152     return SetError(this,SANE_STATUS_DEVICE_BUSY,"scan active");
153   memset(&(this->state),0,sizeof(this->state));
154   this->state.ReadProc  =ReadNextColorLine;
155   this->state.ySensorSkew=0;
156 
157   GetAreaSize(this);
158 
159   switch (this->param.res)
160     {
161     case 200: this->state.ySensorSkew=1; break;
162     case 300: this->state.ySensorSkew=2; break;
163     case 600: this->state.ySensorSkew=4; break;
164     default: break;
165     }
166   /* since we need 2*this->state.ySensorSkew additional scan lines for de-skewing of
167      the sensor lines, we enlarge the window and shorten the initial movement
168      accordingly */
169   this->state.cyTotalPath =
170     this->param.y/2-(2*this->state.ySensorSkew)*600/this->param.res;
171   DoJog(this,this->state.cyTotalPath); INST_ASSERT();
172   this->state.cyTotalPath +=
173     (this->state.cyPixel+2*this->state.ySensorSkew)
174     *600/this->param.res; /* for jogging back */
175 
176   /*
177     regular scan is asynchronously, that is,
178     the scanning is issued, and the driver does bulk reads,
179     until there are no data left.
180     Each line has a full R, G and B subline, 8bit each sample.
181   */
182   {
183     unsigned char uchRegs[]={
184       0xFC /*!!R_SPOS!!*/, 0x00 /*R_SPOSH*/, 0x24 /*!!0x03!!*/,
185       0xB0 /*!!R_SWID!!*/, 0xC4 /*!!R_SWIDH!!*/,
186       1,0,
187       0xFF /*!!0x08!!*/, 0xFF /*!!0x09!!*/,
188       0x22 /*!!R_LEN!!*/, 0x07 /*!!R_LENH!!*/, 0x6D /*0x0C*/,
189       0x70 /*0x0D*/, 0x69 /*0x0E*/, 0xD0 /*0x0F*/,
190       0x00 /*0x10*/, 0x00 /*0x11*/, 0x42 /*!!0x12!!*/,
191       0x15 /*0x13*/, 0x84 /*!!0x14!!*/, 0x2A /*0x15*/,
192       0xC5 /*!!0x16!!*/, 0x40 /*0x17*/, 0xC5 /*!!0x18!!*/,
193       0x40 /*0x19*/, 0xFF /*0x1A*/, 0x01 /*0x1B*/,
194       0x88 /*0x1C*/, 0x40 /*0x1D*/, 0x4C /*0x1E*/,
195       0x50 /*0x1F*/, 0x00 /*0x20*/, 0x0C /*0x21*/,
196       0x21 /*0x22*/, 0xF0 /*0x23*/, 0x40 /*0x24*/,
197       0x00 /*0x25*/, 0x0A /*0x26*/, 0xF0 /*0x27*/,
198       0x00 /*0x28*/, 0x00 /*0x29*/, 0x4E /*0x2A*/,
199       0xF0 /*0x2B*/, 0x00 /*0x2C*/, 0x00 /*0x2D*/,
200       0x4E /*0x2E*/, 0x80 /*R_CCAL*/, 0x80 /*R_CCAL2*/,
201       0x80 /*R_CCAL3*/, 0x0B /*0x32*/, 0x2D /*0x33*/,
202       0x43 /*!!0x34!!*/, 0x29 /*0x35*/, 0x00 /*0x36*/,
203       0x00 /*0x37*/, 0x00 /*0x38*/, 0x00 /*0x39*/,
204       0x00 /*0x3A*/, 0x00 /*0x3B*/, 0xFF /*0x3C*/,
205       0x0F /*0x3D*/, 0x00 /*0x3E*/, 0x00 /*0x3F*/,
206       0x01 /*0x40*/, 0x00 /*0x41*/, 0x80 /*R_CSTAT*/,
207       0x03 /*0x43*/, 0x01 /*R_LMP*/, 0x00 /*0x45*/,
208       0x39 /*R_CTL*/, 0xC5 /*!!0x47!!*/, 0x40 /*0x48*/,
209       0x9E /*0x49*/, 0x8C /*0x4A*/ };
210     RegWriteArray(this,R_ALL, NUM_SCANREGS, uchRegs);
211     RegWrite(this,R_SPOS, 2, this->param.x/2 + this->calibration.xMargin);
212     RegWrite(this,R_SLEN, 2,
213 	     this->state.cyWindow+
214 	     (2*this->state.ySensorSkew)*600/this->param.res);
215     this->state.szOrder=ORDER_BRG;
216     RegWrite(this,R_CCAL, 3, this->calibration.rgbBias); INST_ASSERT(); /* 0xBBGGRR */
217     switch (this->param.res)
218       {
219       case 75:
220 	RegWrite(this,R_XRES,1, 0x20); /* ups, can  do only 100 dpi horizontal */
221 	RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
222 	RegWrite(this,0x34, 1, 0x83); /* halves the vertical resolution */
223 	RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
224 	break;
225       case 100:
226 	RegWrite(this,R_XRES,1, 0x20);
227 	RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
228 	RegWrite(this,0x34, 1, 0x63); /* halves the vertical resolution */
229 	RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
230 	/* I have no idea, what these differences are good for. The seem to produce
231 	   a slight blue presence.
232 	   RegWrite(this,0x16, 1, 0xC0);  RegWrite(this,0x18, 1, 0xC0);
233 	   RegWrite(this,0x12, 1, 0x40);  RegWrite(this,0x10, 2, 0x0728);
234 	   RegWrite(this,0x14, 1, 0x80); */
235 	break;
236       case 200:
237 	RegWrite(this,R_XRES,1, 0x24);
238 	RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
239 	break;
240       case 300:
241 	RegWrite(this,0x08,2, 0x6A6A);
242 	RegWrite(this,R_XRES,1, 0x2A);
243 	RegWrite(this,R_SWID, 2, 0x4000 | this->state.cxWindow);
244 	RegWrite(this,0x34, 1, 0x03); /* halves the vertical resolution */
245 	RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
246 	this->state.szOrder=ORDER_RGB;
247 	break;
248       case 600:
249 	RegWrite(this,R_XRES,1, 0x3F);
250 	RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
251 	RegWrite(this,0x34, 1, 0x03); /* halves the vertical resolution */
252 	RegWrite(this,0x47,1,0xC2); /* reduces the speed a bit */
253 	break;
254       case 1200:
255 	/* not supported, since the driver supports only 600 dpi in color */
256 	break;
257       }
258   }
259 
260   /* setup gamma tables */
261   RegWrite(this,0x41,1,0x03); /* gamma, RGB */
262   RegWrite(this,0x40,1,0x28); /* offset FIFO 8*3 (GAMMA)+16 KB(gain) spared */
263   /*
264     hey, surprise: Although the color lines are sent in a strange order,
265     the gamma tables are mapped constantly to the sensors (i.e. RGB)
266   */
267   UploadGammaTable(this,0x0000,this->agammaR);
268   UploadGammaTable(this,0x2000,this->agammaG);
269   UploadGammaTable(this,0x4000,this->agammaB);
270   INST_ASSERT();
271 
272   UploadGainCorrection(this,0x6000);
273   INST_ASSERT();
274 
275   /* enough for 1/100 inch sensor distance */
276   this->state.cBacklog=1+2*this->state.ySensorSkew;
277 
278   /* allocate raw line buffers */
279   this->state.ppchLines=calloc(this->state.cBacklog,sizeof(short*));
280   this->state.pchBuf=malloc(0x8000);
281   if (!this->state.ppchLines || !this->state.pchBuf)
282     return FreeState(this,SetError(this,
283 				   SANE_STATUS_NO_MEM,"no buffers available"));
284 
285   for (i=0; i<this->state.cBacklog; i++)
286   {
287     this->state.ppchLines[i]=calloc(1,3*this->state.cxMax*sizeof(short)); /* must be less than 0x8000 */
288     if (!this->state.ppchLines[i])
289       return FreeState(this,
290 		       SetError(this,SANE_STATUS_NO_MEM,
291 				"no line buffer available"));
292   }
293 
294   /* calculate and prepare intermediate line transfer buffer */
295 
296   this->state.cchLineOut=3*this->state.cxPixel;
297   this->state.pchLineOut = malloc(this->state.cchLineOut);
298   if (!this->state.pchLineOut)
299     return FreeState(this,SetError(this,
300 				   SANE_STATUS_NO_MEM,
301 				   "no buffers available"));
302 
303   RegWrite(this,R_CTL, 1, 0x39);    /* #1532[005.0] */
304   RegWrite(this,R_CTL, 1, 0x79);    /* #1533[005.0] */
305   RegWrite(this,R_CTL, 1, 0xF9);    /* #1534[005.0] */
306   INST_ASSERT();
307 
308   this->state.bScanning = true;
309   return SANE_STATUS_GOOD;
310 }
311