1 /*
2 Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 /*
19 Core NIASH chip functions.
20 */
21
22 #include <stdio.h> /* fopen, fread, fwrite, fclose etc */
23 #include <stdarg.h> /* va_list for vfprintf */
24 #include <string.h> /* memcpy, memset */
25 #include <unistd.h> /* unlink */
26 #include <stdlib.h> /* malloc, free */
27 #include <math.h> /* exp, pow */
28
29 #include "niash_xfer.h"
30 #include "niash_core.h"
31
32
33 #ifndef MIN
34 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
35 #endif
36
37 #ifndef MAX
38 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
39 #endif
40
41
42 #define XFER_BUF_SIZE 0xF000
43
44
45 /* HP3400 firmware data */
46 static unsigned char abData0000[] = {
47 0xfe, 0x9f, 0x58, 0x1b, 0x00, 0x03, 0xa4, 0x02, 0x63, 0x02, 0x33, 0x02,
48 0x0d, 0x02, 0xf0, 0x01,
49 0xd8, 0x01, 0xc5, 0x01, 0xb5, 0x01, 0xa8, 0x01, 0x9d, 0x01, 0x93, 0x01,
50 0x8b, 0x01, 0x84, 0x01,
51 0x7e, 0x01, 0x79, 0x01, 0x74, 0x01, 0x70, 0x01, 0x6d, 0x01, 0x69, 0x01,
52 0x67, 0x01, 0x64, 0x01,
53 0x62, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x5d, 0x01, 0x5c, 0x01, 0x5b, 0x01,
54 0x5a, 0x01, 0x59, 0x01,
55 0x58, 0x01, 0x57, 0x01, 0x57, 0x01, 0x56, 0x01, 0x56, 0x01, 0x55, 0x01,
56 0x55, 0x01, 0x54, 0x01,
57 0x54, 0x01, 0x54, 0x01, 0x54, 0x01, 0x53, 0x01, 0x53, 0x01, 0x53, 0x01,
58 0x53, 0x01, 0x52, 0x81
59 };
60
61 /* 1st word : 0x9ffe = 40958, strip 15th bit: 0x1ffe = 8190
62 2nd word : 0x1b58 = 7000 -> coincidence ?
63 other words: formula: y = 676 / (2 - exp(0.113 * (1-x)) ), where x = 0 for first entry
64 */
65
66 /* more HP3400 firmware data */
67 static unsigned char abData0400[] = {
68 0xa4, 0x82, 0x00, 0x80, 0xa4, 0x82, 0xaa, 0x02, 0xc0, 0x02, 0xe8, 0x02,
69 0x3e, 0x03, 0xc8, 0x03,
70 0x58, 0x1b, 0xfe, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00
73 };
74
75
76
77 static void
_ConvertMotorTable(unsigned char *pabOld, unsigned char *pabNew, int iSize, int iLpi)78 _ConvertMotorTable (unsigned char *pabOld, unsigned char *pabNew, int iSize,
79 int iLpi)
80 {
81 int iData, i, iBit15;
82
83 for (i = 0; i < (iSize / 2); i++)
84 {
85 iData = pabOld[2 * i + 0] + (pabOld[2 * i + 1] << 8);
86 iBit15 = (iData & 0x8000);
87 iData = (iData & 0x7FFF);
88 if (iData <= 0x400)
89 {
90 iData = iData * iLpi / 300;
91 }
92 if (iBit15 != 0)
93 {
94 iData |= 0x8000;
95 }
96 pabNew[2 * i + 0] = iData & 255;
97 pabNew[2 * i + 1] = (iData >> 8) & 255;
98 }
99 }
100
101
102 /*************************************************************************
103 _ProbeRegisters
104 ===============
105 Tries to determine certain hardware properties.
106
107 This is done by checking the writeability of some scanner registers.
108 We cannot rely simply on the scanner model to contain a specific
109 chip. The HP3300c for example uses one of at least three slightly
110 different scanner ASICs (NIASH00012, NIASH00013 and NIASH00014).
111
112 OUT pHWParams Hardware parameters, updated fields:
113 fGamma16 TRUE if 16 bit gamma tables can be used
114 fReg07 TRUE if reg07 is writeable
115 iBufferSize Size of scanner's internal buffer
116
117 Returns TRUE if a NIASH chipset was found.
118 *************************************************************************/
119 static SANE_Bool
_ProbeRegisters(THWParams * pHWParams)120 _ProbeRegisters (THWParams * pHWParams)
121 {
122 unsigned char bData1, bData2;
123 int iHandle;
124
125 iHandle = pHWParams->iXferHandle;
126
127 DBG (DBG_MSG, "Probing scanner...\n");
128
129 /* check register 0x04 */
130 NiashWriteReg (iHandle, 0x04, 0x55);
131 NiashReadReg (iHandle, 0x04, &bData1);
132 NiashWriteReg (iHandle, 0x04, 0xAA);
133 NiashReadReg (iHandle, 0x04, &bData2);
134 NiashWriteReg (iHandle, 0x04, 0x07);
135 if ((bData1 != 0x55) || (bData2 != 0xAA))
136 {
137 DBG (DBG_ERR, " No NIASH chipset found!\n");
138 return SANE_FALSE;
139 }
140
141 /* check writeability of register 3 bit 1 */
142 NiashReadReg (iHandle, 0x03, &bData1);
143 NiashWriteReg (iHandle, 0x03, bData1 | 0x02);
144 NiashReadReg (iHandle, 0x03, &bData2);
145 NiashWriteReg (iHandle, 0x03, bData1);
146 pHWParams->fGamma16 = ((bData2 & 0x02) != 0);
147 DBG (DBG_MSG, " Gamma table entries are %d bit\n",
148 pHWParams->fGamma16 ? 16 : 8);
149
150 /* check register 0x07 */
151 NiashReadReg (iHandle, 0x07, &bData1);
152 NiashWriteReg (iHandle, 0x07, 0x1C);
153 NiashReadReg (iHandle, 0x07, &bData2);
154 NiashWriteReg (iHandle, 0x07, bData1);
155 pHWParams->fReg07 = (bData2 == 0x1C);
156
157 if (!pHWParams->fGamma16)
158 {
159 /* internal scan buffer size is an educated guess, but seems to correlate
160 well with the size calculated from several windows driver log files
161 size = 128kB - 44088 unsigned chars (space required for gamma/calibration table)
162 */
163 pHWParams->iBufferSize = 86984L;
164 DBG (DBG_MSG, " NIASH version < 00014\n");
165 }
166 else
167 {
168 pHWParams->iBufferSize = 0x60000L;
169 if (!pHWParams->fReg07)
170 {
171 DBG (DBG_MSG, " NIASH version = 00014\n");
172 }
173 else
174 {
175 DBG (DBG_MSG, " NIASH version > 00014\n");
176 }
177 }
178
179 return SANE_TRUE;
180 }
181
182
183 /* returns 0 on success, < 0 otherwise */
184 STATIC int
NiashOpen(THWParams * pHWParams, const char *pszName)185 NiashOpen (THWParams * pHWParams, const char *pszName)
186 {
187 int iXferHandle;
188
189 iXferHandle = NiashXferOpen (pszName, &pHWParams->eModel);
190 if (iXferHandle < 0)
191 {
192 DBG (DBG_ERR, "NiashXferOpen failed for '%s'\n", pszName);
193 return -1;
194 }
195
196 pHWParams->iXferHandle = iXferHandle;
197
198 NiashWakeup (pHWParams->iXferHandle);
199
200 /* default HW params */
201 pHWParams->iSensorSkew = 8;
202 pHWParams->iTopLeftX = 0;
203 pHWParams->iTopLeftY = 3;
204 pHWParams->fReg07 = SANE_FALSE;
205 pHWParams->iSkipLines = 0;
206 pHWParams->iExpTime = 5408;
207 pHWParams->iReversedHead = SANE_TRUE;
208
209 switch (pHWParams->eModel)
210 {
211
212 case eHp3300c:
213 DBG (DBG_MSG, "Setting params for Hp3300\n");
214 pHWParams->iTopLeftX = 4;
215 pHWParams->iTopLeftY = 11;
216 pHWParams->iSkipLines = 14;
217 break;
218
219 case eHp3400c:
220 case eHp4300c:
221 DBG (DBG_MSG, "Setting params for Hp3400c/Hp4300c\n");
222 pHWParams->iTopLeftX = 3;
223 pHWParams->iTopLeftY = 14;
224 pHWParams->fReg07 = SANE_TRUE;
225 break;
226
227 case eAgfaTouch:
228 DBG (DBG_MSG, "Setting params for AgfaTouch\n");
229 pHWParams->iReversedHead = SANE_FALSE; /* head not reversed on Agfa Touch */
230 pHWParams->iTopLeftX = 3;
231 pHWParams->iTopLeftY = 10;
232 pHWParams->iSkipLines = 7;
233 break;
234
235 case eUnknownModel:
236 DBG (DBG_MSG, "Setting params for UnknownModel\n");
237 break;
238
239 default:
240 DBG (DBG_ERR, "ERROR: internal error! (%d)\n", (int) pHWParams->eModel);
241 return -1;
242 }
243
244 /* autodetect some hardware properties */
245 if (!_ProbeRegisters (pHWParams))
246 {
247 DBG (DBG_ERR, "_ProbeRegisters failed!\n");
248 return -1;
249 }
250
251 return 0;
252 }
253
254
255 STATIC void
NiashClose(THWParams * pHWPar)256 NiashClose (THWParams * pHWPar)
257 {
258 NiashXferClose (pHWPar->iXferHandle);
259 pHWPar->iXferHandle = 0;
260 }
261
262
263 static void
WriteRegWord(int iHandle, unsigned char bReg, SANE_Word wData)264 WriteRegWord (int iHandle, unsigned char bReg, SANE_Word wData)
265 {
266 NiashWriteReg (iHandle, bReg, wData & 0xFF);
267 NiashWriteReg (iHandle, bReg + 1, (wData >> 8) & 0xFF);
268 }
269
270
271 /* calculate a 4096 unsigned char gamma table */
272 STATIC void
CalcGamma(unsigned char *pabTable, double Gamma)273 CalcGamma (unsigned char *pabTable, double Gamma)
274 {
275 int i, iData;
276
277 /* fill gamma table */
278 for (i = 0; i < 4096; i++)
279 {
280 iData = floor (256.0 * pow (((double) i / 4096.0), 1.0 / Gamma));
281 pabTable[i] = iData;
282 }
283 }
284
285
286 /*
287 Hp3400WriteFw
288 =============
289 Writes data to scanners with a NIASH00019 chipset, e.g.
290 gamma, calibration and motor control data.
291
292 IN pabData pointer to firmware data
293 iLen Size of firmware date (unsigned chars)
294 iAddr Scanner address to write to
295 */
296 static void
Hp3400cWriteFW(int iXferHandle, unsigned char *pabData, int iLen, int iAddr)297 Hp3400cWriteFW (int iXferHandle, unsigned char *pabData, int iLen, int iAddr)
298 {
299 iAddr--;
300 NiashWriteReg (iXferHandle, 0x21, iAddr & 0xFF);
301 NiashWriteReg (iXferHandle, 0x22, (iAddr >> 8) & 0xFF);
302 NiashWriteReg (iXferHandle, 0x23, (iAddr >> 16) & 0xFF);
303 NiashWriteBulk (iXferHandle, pabData, iLen);
304 }
305
306
307 /* Writes the gamma and offset/gain tables to the scanner.
308 In case a calibration file exist, it will be used for offset/gain */
309 STATIC void
WriteGammaCalibTable(unsigned char *pabGammaR, unsigned char *pabGammaG, unsigned char *pabGammaB, unsigned char *pabCalibTable, int iGain, int iOffset, THWParams * pHWPar)310 WriteGammaCalibTable (unsigned char *pabGammaR, unsigned char *pabGammaG,
311 unsigned char *pabGammaB, unsigned char *pabCalibTable,
312 int iGain, int iOffset, THWParams * pHWPar)
313 {
314 int i, j, k;
315 static unsigned char abGamma[60000];
316 int iData;
317 int iHandle;
318
319 iHandle = pHWPar->iXferHandle;
320
321 j = 0;
322 /* fill gamma table for red component */
323 /* pad entries with 0 for 16-bit gamma table */
324 for (i = 0; i < 4096; i++)
325 {
326 if (pHWPar->fGamma16)
327 {
328 abGamma[j++] = 0;
329 }
330 abGamma[j++] = pabGammaR[i];
331 }
332 /* fill gamma table for green component */
333 for (i = 0; i < 4096; i++)
334 {
335 if (pHWPar->fGamma16)
336 {
337 abGamma[j++] = 0;
338 }
339 abGamma[j++] = pabGammaG[i];
340 }
341 /* fill gamma table for blue component */
342 for (i = 0; i < 4096; i++)
343 {
344 if (pHWPar->fGamma16)
345 {
346 abGamma[j++] = 0;
347 }
348 abGamma[j++] = pabGammaB[i];
349 }
350
351 if (pabCalibTable == NULL)
352 {
353 iData = (iGain << 6) + iOffset;
354 for (i = 0; i < HW_PIXELS; i++)
355 {
356 for (k = 0; k < 3; k++)
357 {
358 abGamma[j++] = (iData) & 255;
359 abGamma[j++] = (iData >> 8) & 255;
360 }
361 }
362 }
363 else
364 {
365 memcpy (&abGamma[j], pabCalibTable, HW_PIXELS * 6);
366 j += HW_PIXELS * 6;
367 }
368
369 NiashWriteReg (iHandle, 0x02, 0x80);
370 NiashWriteReg (iHandle, 0x03, 0x01);
371 NiashWriteReg (iHandle, 0x03, 0x11);
372 NiashWriteReg (iHandle, 0x02, 0x84);
373
374 if (pHWPar->fReg07)
375 {
376 Hp3400cWriteFW (iHandle, abGamma, j, 0x2000);
377 }
378 else
379 {
380 NiashWriteBulk (iHandle, abGamma, j);
381 }
382
383 NiashWriteReg (iHandle, 0x02, 0x80);
384 }
385
386
387 static void
WriteAFEReg(int iHandle, int iReg, int iData)388 WriteAFEReg (int iHandle, int iReg, int iData)
389 {
390 NiashWriteReg (iHandle, 0x25, iReg);
391 NiashWriteReg (iHandle, 0x26, iData);
392 }
393
394
395 /* setup the analog front-end -> coarse calibration */
396 static void
WriteAFE(int iHandle)397 WriteAFE (int iHandle)
398 {
399 /* see WM8143 datasheet */
400
401 WriteAFEReg (iHandle, 0x04, 0x00);
402 WriteAFEReg (iHandle, 0x03, 0x12);
403 WriteAFEReg (iHandle, 0x02, 0x04);
404 WriteAFEReg (iHandle, 0x05, 0x10);
405 WriteAFEReg (iHandle, 0x01, 0x03);
406
407 WriteAFEReg (iHandle, 0x20, 0xc0); /*c8 *//* red offset */
408 WriteAFEReg (iHandle, 0x21, 0xc0); /*c8 *//* green offset */
409 WriteAFEReg (iHandle, 0x22, 0xc0); /*d0 *//* blue offset */
410
411 WriteAFEReg (iHandle, 0x28, 0x05); /*5 *//* red gain */
412 WriteAFEReg (iHandle, 0x29, 0x03); /*3 *//* green gain */
413 WriteAFEReg (iHandle, 0x2A, 0x04); /*4 *//* blue gain */
414 }
415
416
417 /* wait for the carriage to return */
418 static void
WaitReadyBit(int iHandle)419 WaitReadyBit (int iHandle)
420 {
421 unsigned char bData;
422
423 do
424 {
425 NiashReadReg (iHandle, 0x03, &bData);
426 }
427 while ((bData & 8) == 0);
428 }
429
430
431 /*
432 Initialisation specific for NIASH00014 and lower chips
433 */
434 static void
InitNiash00014(TScanParams * pParams, THWParams * pHWParams)435 InitNiash00014 (TScanParams * pParams, THWParams * pHWParams)
436 {
437 int iHandle, iLpiCode;
438
439 iHandle = pHWParams->iXferHandle;
440
441 /* exposure time (in units 24/Fcrystal)? */
442 WriteRegWord (iHandle, 0x08, pHWParams->iExpTime - 1);
443
444 /* width in pixels */
445 WriteRegWord (iHandle, 0x12, pParams->iWidth - 1);
446
447 /* top */
448 WriteRegWord (iHandle, 0x17, pParams->iTop);
449 WriteRegWord (iHandle, 0x19, pParams->iTop);
450
451 /* time between stepper motor steps (in units of 24/Fcrystal)? */
452 iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L;
453
454 if (!pHWParams->fGamma16)
455 {
456 /* NIASH 00012 / 00013 init */
457
458 /* LPI specific settings */
459 if (pParams->iLpi < 600)
460 {
461 /* set halfres bit */
462 NiashWriteReg (iHandle, 0x06, 0x01);
463 /* double lpi code because of halfres bit */
464 iLpiCode *= 2;
465 }
466 else
467 {
468 /* clear halfres bit */
469 NiashWriteReg (iHandle, 0x06, 0x00);
470 /* add exptime to make it scan slower */
471 iLpiCode += pHWParams->iExpTime;
472 }
473
474 /* unknown setting */
475 WriteRegWord (iHandle, 0x27, 0x7FD2);
476 WriteRegWord (iHandle, 0x29, 0x6421);
477
478 }
479 else
480 {
481 /* NIASH 00014 init */
482
483 /* halfres bit always cleared */
484 NiashWriteReg (iHandle, 0x06, 0x00);
485
486 /* LPI specific settings */
487 if (pParams->iLpi >= 600)
488 {
489 /* add exptime to make it scan slower */
490 iLpiCode += pHWParams->iExpTime;
491 }
492
493 /* unknown setting */
494 WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */
495 WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */
496 }
497
498 /* LPI code */
499 WriteRegWord (iHandle, 0x0A, iLpiCode - 1);
500
501 /* backtrack reversing speed */
502 NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32);
503 }
504
505
506 /*
507 Initialisation specific for NIASH00019 chips
508 */
509 static void
InitNiash00019(TScanParams * pParams, THWParams * pHWParams)510 InitNiash00019 (TScanParams * pParams, THWParams * pHWParams)
511 {
512 int iHandle, iLpiCode;
513 static unsigned char abMotor[512];
514
515
516 iHandle = pHWParams->iXferHandle;
517
518 /* exposure time (in units 24/Fcrystal)? */
519 WriteRegWord (iHandle, 0x08, pHWParams->iExpTime);
520
521 /* width in pixels */
522 WriteRegWord (iHandle, 0x12, pParams->iWidth);
523
524 /* ? */
525 WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */
526 WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */
527
528 /* specific handling of 150 dpi resolution */
529 if (pParams->iLpi == 150)
530 {
531 /* use 300 LPI but skip every other line */
532 pParams->iLpi = 300;
533 NiashWriteReg (iHandle, 0x06, 0x01);
534 }
535 else
536 {
537 NiashWriteReg (iHandle, 0x06, 0x00);
538 }
539
540 /* DPI and position table */
541 NiashWriteReg (iHandle, 0x07, 0x02);
542 _ConvertMotorTable (abData0000, abMotor, sizeof (abData0000),
543 pParams->iLpi);
544 Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0000), 0x000);
545 _ConvertMotorTable (abData0400, abMotor, sizeof (abData0400),
546 pParams->iLpi);
547 Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0400), 0x400);
548
549 /* backtrack reversing speed */
550 iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L;
551 NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32);
552 }
553
554
555 /*
556 Scanner initialisation common to all NIASH chips
557 */
558 static void
InitNiashCommon(TScanParams * pParams, THWParams * pHWParams)559 InitNiashCommon (TScanParams * pParams, THWParams * pHWParams)
560 {
561 int iWidthHW, iHandle, iMaxLevel;
562
563
564 iHandle = pHWParams->iXferHandle;
565
566 NiashWriteReg (iHandle, 0x02, 0x80);
567 NiashWriteReg (iHandle, 0x03, 0x11);
568 NiashWriteReg (iHandle, 0x01, 0x8B);
569 NiashWriteReg (iHandle, 0x05, 0x01);
570
571 /* dpi */
572 WriteRegWord (iHandle, 0x0C, pParams->iDpi);
573
574 /* calculate width in units of HW resolution */
575 iWidthHW = pParams->iWidth * (HW_DPI / pParams->iDpi);
576
577 /* set left and right limits */
578 if (pHWParams->iReversedHead)
579 {
580 /* head is reversed */
581 /* right */
582 WriteRegWord (iHandle, 0x0E,
583 3 * (HW_PIXELS - (pParams->iLeft + iWidthHW)));
584
585 /* left */
586 WriteRegWord (iHandle, 0x10, 3 * (HW_PIXELS - pParams->iLeft) - 1);
587 }
588 else
589 {
590 /* head is not reversed */
591 /*left */
592 WriteRegWord (iHandle, 0x0E, 3 * pParams->iLeft);
593
594 /* right */
595 WriteRegWord (iHandle, 0x10, 3 * (pParams->iLeft + iWidthHW) - 1);
596 }
597
598 /* bottom */
599 WriteRegWord (iHandle, 0x1B, pParams->iBottom); /* 0x393C); */
600
601 /* forward jogging speed */
602 NiashWriteReg (iHandle, 0x1D, 0x60);
603
604 /* backtrack reversing speed? */
605 NiashWriteReg (iHandle, 0x2B, 0x15);
606
607 /* backtrack distance */
608 if (pParams->iLpi < 600)
609 {
610 NiashWriteReg (iHandle, 0x1F, 0x30);
611 }
612 else
613 {
614 NiashWriteReg (iHandle, 0x1F, 0x18);
615 }
616
617 /* max buffer level before backtrace */
618 iMaxLevel = MIN (pHWParams->iBufferSize / pParams->iWidth, 250);
619 NiashWriteReg (iHandle, 0x14, iMaxLevel - 1);
620
621 /* lamp PWM, max = 0x1ff? */
622 WriteRegWord (iHandle, 0x2C, 0x01FF);
623
624 /* not needed? */
625 NiashWriteReg (iHandle, 0x15, 0x90); /* 90 */
626 NiashWriteReg (iHandle, 0x16, 0x70); /* 70 */
627
628 WriteAFE (iHandle);
629
630 WaitReadyBit (iHandle);
631
632 NiashWriteReg (iHandle, 0x03, 0x05);
633
634 NiashWriteReg (iHandle, 0x02, pParams->fCalib ? 0x88 : 0xA8);
635 }
636
637
638 /* write registers */
639 STATIC SANE_Bool
InitScan(TScanParams * pParams, THWParams * pHWParams)640 InitScan (TScanParams * pParams, THWParams * pHWParams)
641 {
642 int iHeight;
643 int iExpTime;
644 TScanParams Params;
645
646 /* check validity of scanparameters */
647 switch (pParams->iDpi)
648 {
649 case 150:
650 case 300:
651 case 600:
652 break;
653 default:
654 DBG (DBG_ERR, "Invalid dpi (%d)\n", pParams->iDpi);
655 return SANE_FALSE;
656 }
657
658 iHeight = (pParams->iBottom - pParams->iTop + 1);
659 if (iHeight <= 0)
660 {
661 DBG (DBG_ERR, "Invalid height (%d)\n", iHeight);
662 return SANE_FALSE;
663 }
664
665 if (pParams->iWidth <= 0)
666 {
667 DBG (DBG_ERR, "Invalid width (%d)\n", pParams->iWidth);
668 return SANE_FALSE;
669 }
670
671 switch (pParams->iLpi)
672 {
673 case 150:
674 case 300:
675 case 600:
676 break;
677 default:
678 DBG (DBG_ERR, "Invalid lpi (%d)\n", pParams->iLpi);
679 return SANE_FALSE;
680 }
681
682 /* exposure time (in units of 24/Fcrystal?), must be divisible by 8 !!! */
683 iExpTime = 5408;
684 if ((iExpTime % 8) != 0)
685 {
686 DBG (DBG_ERR, "Invalid exposure time (%d)\n", iExpTime);
687 return SANE_FALSE;
688 }
689
690 /*
691 *** Done checking scan parameters validity ***
692 */
693
694 /*
695 copy the parameters locally and make pParams point to the local copy
696 */
697 memcpy (&Params, pParams, sizeof (Params));
698 pParams = &Params;
699
700 if (!pHWParams->fReg07)
701 {
702 /* init NIASH00014 and lower */
703 InitNiash00014 (pParams, pHWParams);
704 }
705 else
706 {
707 /* init NIASH00019 */
708 InitNiash00019 (pParams, pHWParams);
709 }
710
711 /* common NIASH init */
712 InitNiashCommon (pParams, pHWParams);
713
714 return SANE_TRUE;
715 }
716
717
718 /************************************************************************/
719
720 static SANE_Bool
XferBufferGetLine(int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool fReturn)721 XferBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine,
722 SANE_Bool fReturn)
723 {
724 unsigned char bData, bData2;
725 SANE_Bool fJustDone = SANE_FALSE;
726 /* all calculated transfers done ? */
727 if (p->iLinesLeft == 0)
728 return SANE_FALSE;
729
730 /* time for a fresh read? */
731 if (p->iCurLine == 0)
732 {
733 int iLines;
734 iLines = p->iLinesPerXferBuf;
735 /* read only as many lines as needed */
736 if (p->iLinesLeft > 0 && p->iLinesLeft <= iLines)
737 {
738 iLines = p->iLinesLeft;
739 DBG (DBG_MSG, "\n");
740 DBG (DBG_MSG, "last bulk read\n");
741 if (iLines < p->iLinesPerXferBuf)
742 {
743 DBG (DBG_MSG,
744 "reading reduced number of lines: %d instead of %d\n",
745 iLines, p->iLinesPerXferBuf);
746 }
747 fJustDone = SANE_TRUE;
748 }
749 /* reading old buffer level */
750 NiashReadReg (iHandle, 0x20, &bData);
751 NiashReadBulk (iHandle, p->pabXferBuf, iLines * p->iBytesPerLine);
752 /* reding new buffer level */
753 NiashReadReg (iHandle, 0x20, &bData2);
754 if (fJustDone && fReturn)
755 {
756 NiashWriteReg (iHandle, 0x02, 0x80);
757 DBG (DBG_MSG, "returning scanner head\n");
758 }
759 DBG (DBG_MSG,
760 "buffer level = %3d, <reading %5d unsigned chars>, buffer level = %3d\r",
761 (int) bData, iLines * p->iBytesPerLine, (int) bData2);
762 fflush (stdout);
763 }
764 /* copy one line */
765 if (pabLine != NULL)
766 {
767 memcpy (pabLine, &p->pabXferBuf[p->iCurLine * p->iBytesPerLine],
768 p->iBytesPerLine);
769 }
770 /* advance pointer */
771 p->iCurLine = (p->iCurLine + 1) % p->iLinesPerXferBuf;
772
773 /* one transfer line less to the XFerBuffer */
774 if (p->iLinesLeft > 0)
775 --(p->iLinesLeft);
776 return SANE_TRUE;
777 }
778
779
780 static void
XferBufferInit(int iHandle, TDataPipe * p)781 XferBufferInit (int iHandle, TDataPipe * p)
782 {
783 int i;
784
785 p->pabXferBuf = (unsigned char *) malloc (XFER_BUF_SIZE);
786 p->iCurLine = 0;
787
788 /* skip garbage lines */
789 for (i = 0; i < p->iSkipLines; i++)
790 {
791 XferBufferGetLine (iHandle, p, NULL, SANE_FALSE);
792 }
793 }
794
795 /* static procedure that fills the circular buffer in advance to any
796 circular buffer data retrieval */
797 static void
CircBufferFill(int iHandle, TDataPipe * p, SANE_Bool iReversedHead)798 CircBufferFill (int iHandle, TDataPipe * p, SANE_Bool iReversedHead)
799 {
800 int i;
801 for (i = 0; i < p->iLinesPerCircBuf; i++)
802 {
803 if (iReversedHead)
804 {
805 XferBufferGetLine (iHandle, p,
806 &p->pabCircBuf[p->iRedLine * p->iBytesPerLine],
807 SANE_FALSE);
808 }
809 else
810 {
811 XferBufferGetLine (iHandle, p,
812 &p->pabCircBuf[p->iBluLine * p->iBytesPerLine],
813 SANE_FALSE);
814 }
815 /* advance pointers */
816 p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf;
817 p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf;
818 p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf;
819 }
820 }
821
822 static void
XferBufferExit(TDataPipe * p)823 XferBufferExit (TDataPipe * p)
824 {
825 if (p->pabXferBuf != NULL)
826 {
827 free (p->pabXferBuf);
828 p->pabXferBuf = NULL;
829 }
830 else
831 {
832 DBG (DBG_ERR, "XferBufExit: Xfer buffer not initialised!\n");
833 }
834 }
835
836
837 /* unscrambles a line:
838 - combining the proper R, G and B lines and converting them to interpixel RGB
839 - mirroring left to right
840 */
841 static void
_UnscrambleLine(unsigned char *pabLine, unsigned char *pabRed, unsigned char *pabGrn, unsigned char *pabBlu, int iWidth, SANE_Bool iReversedHead, int iScaleDownDpi, int iBufWeight)842 _UnscrambleLine (unsigned char *pabLine,
843 unsigned char *pabRed, unsigned char *pabGrn,
844 unsigned char *pabBlu, int iWidth, SANE_Bool iReversedHead,
845 int iScaleDownDpi, int iBufWeight)
846 {
847 /* never change an approved algorithm ...
848 so take Bertriks original source for this special case */
849 if (iScaleDownDpi == 1 && iBufWeight == 0)
850 {
851 int i, j;
852 if (iReversedHead)
853 {
854 /* reversed */
855 for (i = 0; i < iWidth; i++)
856 {
857 j = (iWidth - i) * 3;
858 pabLine[j - 3] = pabRed[i];
859 pabLine[j - 2] = pabGrn[i + iWidth];
860 pabLine[j - 1] = pabBlu[i + iWidth * 2];
861 }
862 }
863 else
864 {
865 /* not reversed */
866 for (i = 0; i < iWidth; i++)
867 {
868 pabLine[3 * i] = pabRed[i];
869 pabLine[3 * i + 1] = pabGrn[i + iWidth];
870 pabLine[3 * i + 2] = pabBlu[i + iWidth * 2];
871 }
872 }
873 }
874 else
875 {
876 int i, j; /* loop variables */
877 int c; /* color buffer accumulator for horizontal average */
878
879 /* initialize for incremental color buffer access */
880 int iInc = 1;
881 int iStart = 0;
882
883 /* set for "from the end to the front" of the circular color buffers */
884 if (iReversedHead)
885 {
886 iStart = iWidth - iScaleDownDpi;
887 iInc = -1;
888 }
889
890 /* each pixel is the mean of iScaleDownDpi
891 so set the skip width accordingly */
892 iInc *= iScaleDownDpi;
893
894 for (i = iStart; i >= 0 && i < iWidth; i += iInc)
895 {
896 /* collect the red pixels */
897 for (c = j = 0; j < iScaleDownDpi; ++j)
898 c += pabRed[i + j];
899 *pabLine =
900 (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
901 pabLine++;
902
903 /* collect the green pixels */
904 for (c = j = 0; j < iScaleDownDpi; ++j)
905 c += pabGrn[i + iWidth + j];
906 *pabLine =
907 (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
908 pabLine++;
909
910 /* collect the blue pixels */
911 for (c = j = 0; j < iScaleDownDpi; ++j)
912 c += pabBlu[i + 2 * iWidth + j];
913 *pabLine =
914 (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
915 pabLine++;
916 }
917 }
918
919 }
920
921
922 /* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage,
923 if fReturn==SANE_TRUE, the head will return automatically on an end of scan */
924 STATIC SANE_Bool
CircBufferGetLineEx(int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool iReversedHead, SANE_Bool fReturn)925 CircBufferGetLineEx (int iHandle, TDataPipe * p, unsigned char *pabLine,
926 SANE_Bool iReversedHead, SANE_Bool fReturn)
927 {
928 int iLineCount;
929 for (iLineCount = 0; iLineCount < p->iScaleDownLpi; ++iLineCount)
930 {
931 if (iReversedHead)
932 {
933 if (!XferBufferGetLine (iHandle, p,
934 &p->pabCircBuf[p->iRedLine *
935 p->iBytesPerLine], fReturn))
936 return SANE_FALSE;
937 }
938 else
939 {
940 if (!XferBufferGetLine (iHandle, p,
941 &p->pabCircBuf[p->iBluLine *
942 p->iBytesPerLine], fReturn))
943 return SANE_FALSE;
944 }
945 if (pabLine != NULL)
946 {
947 _UnscrambleLine (pabLine,
948 &p->pabCircBuf[p->iRedLine * p->iBytesPerLine],
949 &p->pabCircBuf[p->iGrnLine * p->iBytesPerLine],
950 &p->pabCircBuf[p->iBluLine * p->iBytesPerLine],
951 p->iWidth * p->iScaleDownDpi, iReversedHead,
952 p->iScaleDownDpi, iLineCount);
953 }
954
955 /* advance pointers */
956 p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf;
957 p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf;
958 p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf;
959 }
960 return SANE_TRUE;
961 }
962
963
964 /* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage */
965 STATIC SANE_Bool
CircBufferGetLine(int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool iReversedHead)966 CircBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine,
967 SANE_Bool iReversedHead)
968 {
969 return CircBufferGetLineEx (iHandle, p, pabLine, iReversedHead, SANE_FALSE);
970 }
971
972
973 /* try to keep the number of transfers the same, but make them all
974 as good as possible the same size to avoid cranking in critical
975 situations
976 */
977 static int
_OptimizeXferSize(int iLines, int iLinesPerXfer)978 _OptimizeXferSize (int iLines, int iLinesPerXfer)
979 {
980 int iXfers;
981 iXfers = (iLines + iLinesPerXfer - 1) / iLinesPerXfer;
982 while (--iLinesPerXfer > 0
983 && (iLines + iLinesPerXfer - 1) / iLinesPerXfer == iXfers);
984 return iLinesPerXfer + 1;
985 }
986
987 STATIC void
CircBufferInit(int iHandle, TDataPipe * p, int iWidth, int iHeight, int iMisAlignment, SANE_Bool iReversedHead, int iScaleDownDpi, int iScaleDownLpi)988 CircBufferInit (int iHandle, TDataPipe * p,
989 int iWidth, int iHeight,
990 int iMisAlignment, SANE_Bool iReversedHead,
991 int iScaleDownDpi, int iScaleDownLpi)
992 {
993
994 /* relevant for internal read and write functions */
995 p->iScaleDownLpi = iScaleDownLpi;
996 p->iScaleDownDpi = iScaleDownDpi;
997 p->iWidth = iWidth;
998 p->iBytesPerLine = iWidth * iScaleDownDpi * BYTES_PER_PIXEL;
999 p->iSaneBytesPerLine = iWidth * BYTES_PER_PIXEL;
1000 if (iMisAlignment == 0)
1001 {
1002 p->iLinesPerCircBuf = 1;
1003 }
1004 else
1005 {
1006 p->iLinesPerCircBuf = 3 * iMisAlignment;
1007 }
1008
1009 DBG (DBG_MSG, "_iScaleDown (Dpi,Lpi) = (%d,%d)\n", p->iScaleDownDpi,
1010 p->iScaleDownLpi);
1011 DBG (DBG_MSG, "_iBytesPerLine = %d\n", p->iBytesPerLine);
1012 DBG (DBG_MSG, "_iLinesPerCircBuf = %d\n", p->iLinesPerCircBuf);
1013 p->pabCircBuf =
1014 (unsigned char *) malloc (p->iBytesPerLine * p->iLinesPerCircBuf);
1015 if (p->pabCircBuf == NULL)
1016 {
1017 DBG (DBG_ERR,
1018 "Unable to allocate %d unsigned chars for circular buffer\n",
1019 (int) (p->iBytesPerLine * p->iLinesPerCircBuf));
1020 return;
1021 }
1022 DBG (DBG_MSG, "Allocated %d unsigned chars for circular buffer\n",
1023 p->iBytesPerLine * p->iLinesPerCircBuf);
1024
1025 if (iReversedHead)
1026 {
1027 p->iBluLine = 0;
1028 p->iGrnLine = iMisAlignment;
1029 p->iRedLine = iMisAlignment * 2;
1030 }
1031 else
1032 {
1033 p->iRedLine = 0;
1034 p->iGrnLine = iMisAlignment;
1035 p->iBluLine = iMisAlignment * 2;
1036 }
1037
1038 /* negative height is an indication for "no Check" */
1039 if (iHeight < 0)
1040 {
1041 p->iLinesLeft = -1;
1042 p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine;
1043 DBG (DBG_MSG, "using unchecked XFER_BUF_SIZE\n");
1044 DBG (DBG_MSG, "_iXFerSize = %d\n",
1045 p->iBytesPerLine * p->iLinesPerXferBuf);
1046 }
1047 else
1048 {
1049 #define SAFETY_LINES 0
1050 #define MAX_LINES_PER_XFERBUF 800
1051 /* estimate of number of unsigned chars to transfer at all via the USB */
1052 /* add some lines for security */
1053
1054 p->iLinesLeft =
1055 iHeight + p->iSkipLines + p->iLinesPerCircBuf + SAFETY_LINES;
1056 p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine;
1057 /* with more than 800 lines the timing is spoiled */
1058 if (p->iLinesPerXferBuf > MAX_LINES_PER_XFERBUF)
1059 {
1060 p->iLinesPerXferBuf = MAX_LINES_PER_XFERBUF;
1061 }
1062 /* final optimization to keep critical scans smooth */
1063 p->iLinesPerXferBuf =
1064 _OptimizeXferSize (p->iLinesLeft, p->iLinesPerXferBuf);
1065
1066 DBG (DBG_MSG, "_iXFerSize = %d for %d transfer(s)\n",
1067 (int) p->iLinesPerXferBuf * p->iBytesPerLine,
1068 (p->iLinesLeft + p->iLinesPerXferBuf - 1) / p->iLinesPerXferBuf);
1069 }
1070 DBG (DBG_MSG, "_iLinesPerXferBuf = %d\n", p->iLinesPerXferBuf);
1071
1072 /* init transfer buffer */
1073 XferBufferInit (iHandle, p);
1074
1075 /* fill circular buffer */
1076 CircBufferFill (iHandle, p, iReversedHead);
1077 }
1078
1079
1080 STATIC void
CircBufferExit(TDataPipe * p)1081 CircBufferExit (TDataPipe * p)
1082 {
1083 XferBufferExit (p);
1084 if (p->pabCircBuf != NULL)
1085 {
1086 DBG (DBG_MSG, "\n");
1087 free (p->pabCircBuf);
1088 p->pabCircBuf = NULL;
1089 }
1090 else
1091 {
1092 DBG (DBG_ERR, "CircBufferExit: Circular buffer not initialised!\n");
1093 }
1094 }
1095
1096
1097 /************************************************************************/
1098
1099
1100
1101 static int
_CalcAvg(unsigned char *pabBuf, int n, int iStep)1102 _CalcAvg (unsigned char *pabBuf, int n, int iStep)
1103 {
1104 int i, j, x;
1105
1106 for (i = j = x = 0; i < n; i++)
1107 {
1108 x += pabBuf[j];
1109 j += iStep;
1110 }
1111 return (x / n);
1112 }
1113
1114
1115 /* converts white line data and black point data into a calibration table */
1116 static void
CreateCalibTable(unsigned char *abWhite, unsigned char bBlackR, unsigned char bBlackG, unsigned char bBlackB, int iReversedHead, unsigned char *pabCalibTable)1117 CreateCalibTable (unsigned char *abWhite, unsigned char bBlackR,
1118 unsigned char bBlackG, unsigned char bBlackB,
1119 int iReversedHead, unsigned char *pabCalibTable)
1120 {
1121 int i, j, iGain, iOffset, iData;
1122 unsigned char *pabPixel;
1123
1124 j = 0;
1125 for (i = 0; i < HW_PIXELS; i++)
1126 {
1127 if (iReversedHead)
1128 {
1129 pabPixel = &abWhite[(HW_PIXELS - i - 1) * 3];
1130 }
1131 else
1132 {
1133 pabPixel = &abWhite[i * 3];
1134 }
1135 /* red */
1136 if (bBlackR > 16)
1137 bBlackR = 16;
1138 iGain = 65536 / MAX (1, pabPixel[0] - bBlackR);
1139 iOffset = bBlackR * 4;
1140 if (iOffset > 63)
1141 iOffset = 63;
1142 iData = (iGain << 6) + iOffset;
1143 pabCalibTable[j++] = (iData) & 255;
1144 pabCalibTable[j++] = (iData >> 8) & 255;
1145 /* green */
1146 if (bBlackG > 16)
1147 bBlackG = 16;
1148 iGain = 65536 / MAX (1, pabPixel[1] - bBlackG);
1149 iOffset = bBlackG * 4;
1150 if (iOffset > 63)
1151 iOffset = 63;
1152 iData = (iGain << 6) + iOffset;
1153 pabCalibTable[j++] = (iData) & 255;
1154 pabCalibTable[j++] = (iData >> 8) & 255;
1155 /* blue */
1156 if (bBlackB > 16)
1157 bBlackB = 16;
1158 iGain = 65536 / MAX (1, pabPixel[2] - bBlackB);
1159 iOffset = bBlackB * 4;
1160 if (iOffset > 63)
1161 iOffset = 63;
1162 iData = (iGain << 6) + iOffset;
1163 pabCalibTable[j++] = (iData) & 255;
1164 pabCalibTable[j++] = (iData >> 8) & 255;
1165 }
1166 }
1167
1168
1169 /*************************************************************************
1170 Lamp control functions
1171 *************************************************************************/
1172 STATIC SANE_Bool
GetLamp(THWParams * pHWParams, SANE_Bool * pfLampIsOn)1173 GetLamp (THWParams * pHWParams, SANE_Bool * pfLampIsOn)
1174 {
1175 unsigned char bData;
1176
1177 NiashReadReg (pHWParams->iXferHandle, 0x03, &bData);
1178 *pfLampIsOn = ((bData & 0x01) != 0);
1179 return SANE_TRUE;
1180 }
1181
1182
1183 STATIC SANE_Bool
SetLamp(THWParams * pHWParams, SANE_Bool fLampOn)1184 SetLamp (THWParams * pHWParams, SANE_Bool fLampOn)
1185 {
1186 unsigned char bData;
1187 int iHandle;
1188
1189 iHandle = pHWParams->iXferHandle;
1190
1191 NiashReadReg (iHandle, 0x03, &bData);
1192 if (fLampOn)
1193 {
1194 NiashWriteReg (iHandle, 0x03, bData | 0x01);
1195 }
1196 else
1197 {
1198 NiashWriteReg (iHandle, 0x03, bData & ~0x01);
1199 }
1200 return SANE_TRUE;
1201 }
1202
1203
1204 /*************************************************************************
1205 Experimental simple calibration, but also returning the white levels
1206 *************************************************************************/
1207 STATIC SANE_Bool
SimpleCalibExt(THWParams * pHWPar, unsigned char *pabCalibTable, unsigned char *pabCalWhite)1208 SimpleCalibExt (THWParams * pHWPar, unsigned char *pabCalibTable,
1209 unsigned char *pabCalWhite)
1210 {
1211 unsigned char bMinR, bMinG, bMinB;
1212 TDataPipe DataPipe;
1213 TScanParams Params;
1214 unsigned char abGamma[4096];
1215 int i, j;
1216 static unsigned char abBuf[HW_PIXELS * 3 * 71]; /* Careful : see startWhite and endWhite below */
1217 static unsigned char abLine[HW_PIXELS * 3];
1218 static unsigned char abWhite[HW_PIXELS * 3];
1219 unsigned char *pabWhite;
1220 int iWhiteR, iWhiteG, iWhiteB;
1221 int iHandle;
1222 SANE_Bool iReversedHead;
1223 int startWhiteY, endWhiteY;
1224 int startBlackY, endBlackY;
1225 int endBlackX;
1226
1227 iHandle = pHWPar->iXferHandle;
1228 iReversedHead = pHWPar->iReversedHead;
1229
1230 DataPipe.iSkipLines = pHWPar->iSkipLines;
1231
1232 Params.iDpi = HW_DPI;
1233 Params.iLpi = HW_DPI;
1234 if (iReversedHead) /* hp scanners */
1235 Params.iTop = 60;
1236 else /* agfa scanners */
1237 Params.iTop = 30;
1238 Params.iBottom = HP3300C_BOTTOM;
1239 Params.iLeft = 0;
1240 Params.iWidth = HW_PIXELS;
1241 Params.iHeight = 54;
1242 Params.fCalib = SANE_TRUE;
1243
1244 /* write gamma table with neutral gain / offset */
1245 CalcGamma (abGamma, 1.0);
1246 WriteGammaCalibTable (abGamma, abGamma, abGamma, NULL, 256, 0, pHWPar);
1247
1248 if (!InitScan (&Params, pHWPar))
1249 {
1250 if (pabCalWhite)
1251 pabCalWhite[0] = pabCalWhite[1] = pabCalWhite[2] = 0;
1252 return SANE_FALSE;
1253 }
1254
1255 /* Definition of white and black areas */
1256 if (iReversedHead)
1257 { /* hp scanners */
1258 startWhiteY = 0;
1259 endWhiteY = 15;
1260 startBlackY = 16;
1261 endBlackY = 135;
1262 endBlackX = HW_PIXELS;
1263 }
1264 else
1265 { /* agfa scanners */
1266 startWhiteY = 0;
1267 endWhiteY = 70;
1268 startBlackY = 86;
1269 endBlackY = 135;
1270 endBlackX = 3374;
1271 }
1272
1273 CircBufferInit (iHandle, &DataPipe, HW_PIXELS, -1, Params.iLpi / 150,
1274 iReversedHead, 1, 1);
1275 /* white level */
1276 /* skip some lines */
1277 for (i = 0; i < startWhiteY; i++)
1278 {
1279 CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
1280 }
1281 /* Get white lines */
1282 for (i = 0; i < endWhiteY - startWhiteY + 1; i++)
1283 {
1284 CircBufferGetLine (iHandle, &DataPipe, &abBuf[i * HW_PIXELS * 3],
1285 iReversedHead);
1286 }
1287 /* black level */
1288 bMinR = 255;
1289 bMinG = 255;
1290 bMinB = 255;
1291 /* Skip some lines */
1292 for (i = 0; i < startBlackY; i++)
1293 {
1294 CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
1295 }
1296 for (i = 0; i < endBlackY - startBlackY + 1; i++)
1297 {
1298 CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
1299 for (j = 0; j < endBlackX; j++)
1300 {
1301 bMinR = MIN (abLine[j * 3 + 0], bMinR);
1302 bMinG = MIN (abLine[j * 3 + 1], bMinG);
1303 bMinB = MIN (abLine[j * 3 + 2], bMinB);
1304 }
1305 }
1306 CircBufferExit (&DataPipe);
1307 FinishScan (pHWPar);
1308
1309 /* calc average white level */
1310 pabWhite = abBuf;
1311 for (i = 0; i < HW_PIXELS; i++)
1312 {
1313 abWhite[i * 3 + 0] =
1314 _CalcAvg (&pabWhite[i * 3 + 0], endWhiteY - startWhiteY + 1,
1315 HW_PIXELS * 3);
1316 abWhite[i * 3 + 1] =
1317 _CalcAvg (&pabWhite[i * 3 + 1], endWhiteY - startWhiteY + 1,
1318 HW_PIXELS * 3);
1319 abWhite[i * 3 + 2] =
1320 _CalcAvg (&pabWhite[i * 3 + 2], endWhiteY - startWhiteY + 1,
1321 HW_PIXELS * 3);
1322 }
1323 iWhiteR = _CalcAvg (&abWhite[0], HW_PIXELS, 3);
1324 iWhiteG = _CalcAvg (&abWhite[1], HW_PIXELS, 3);
1325 iWhiteB = _CalcAvg (&abWhite[2], HW_PIXELS, 3);
1326
1327 DBG (DBG_MSG, "Black level (%d,%d,%d), White level (%d,%d,%d)\n",
1328 (int) bMinR, (int) bMinG, (int) bMinB, iWhiteR, iWhiteG, iWhiteB);
1329
1330 /* convert the white line and black point into a calibration table */
1331 CreateCalibTable (abWhite, bMinR, bMinG, bMinB, iReversedHead,
1332 pabCalibTable);
1333 /* assign the White Levels */
1334 if (pabCalWhite)
1335 {
1336 pabCalWhite[0] = iWhiteR;
1337 pabCalWhite[1] = iWhiteG;
1338 pabCalWhite[2] = iWhiteB;
1339 }
1340 return SANE_TRUE;
1341 }
1342
1343
1344 /*************************************************************************
1345 FinishScan
1346 ==========
1347 Finishes the scan. Makes the scanner head move back to the home position.
1348
1349 *************************************************************************/
1350 STATIC void
FinishScan(THWParams * pHWParams)1351 FinishScan (THWParams * pHWParams)
1352 {
1353 NiashWriteReg (pHWParams->iXferHandle, 0x02, 0x80);
1354 }
1355