1141cc406Sopenharmony_ci/* 2141cc406Sopenharmony_ci Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 3141cc406Sopenharmony_ci 4141cc406Sopenharmony_ci This program is free software; you can redistribute it and/or 5141cc406Sopenharmony_ci modify it under the terms of the GNU General Public License 6141cc406Sopenharmony_ci as published by the Free Software Foundation; either version 2 7141cc406Sopenharmony_ci of the License, or (at your option) any later version. 8141cc406Sopenharmony_ci 9141cc406Sopenharmony_ci This program is distributed in the hope that it will be useful, 10141cc406Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 11141cc406Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12141cc406Sopenharmony_ci GNU General Public License for more details. 13141cc406Sopenharmony_ci 14141cc406Sopenharmony_ci You should have received a copy of the GNU General Public License 15141cc406Sopenharmony_ci along with this program. If not, see <https://www.gnu.org/licenses/>. 16141cc406Sopenharmony_ci*/ 17141cc406Sopenharmony_ci 18141cc406Sopenharmony_ci/* 19141cc406Sopenharmony_ci Core NIASH chip functions. 20141cc406Sopenharmony_ci*/ 21141cc406Sopenharmony_ci 22141cc406Sopenharmony_ci#include <stdio.h> /* fopen, fread, fwrite, fclose etc */ 23141cc406Sopenharmony_ci#include <stdarg.h> /* va_list for vfprintf */ 24141cc406Sopenharmony_ci#include <string.h> /* memcpy, memset */ 25141cc406Sopenharmony_ci#include <unistd.h> /* unlink */ 26141cc406Sopenharmony_ci#include <stdlib.h> /* malloc, free */ 27141cc406Sopenharmony_ci#include <math.h> /* exp, pow */ 28141cc406Sopenharmony_ci 29141cc406Sopenharmony_ci#include "niash_xfer.h" 30141cc406Sopenharmony_ci#include "niash_core.h" 31141cc406Sopenharmony_ci 32141cc406Sopenharmony_ci 33141cc406Sopenharmony_ci#ifndef MIN 34141cc406Sopenharmony_ci#define MIN(a,b) (((a) < (b)) ? (a) : (b)) 35141cc406Sopenharmony_ci#endif 36141cc406Sopenharmony_ci 37141cc406Sopenharmony_ci#ifndef MAX 38141cc406Sopenharmony_ci#define MAX(a,b) (((a) > (b)) ? (a) : (b)) 39141cc406Sopenharmony_ci#endif 40141cc406Sopenharmony_ci 41141cc406Sopenharmony_ci 42141cc406Sopenharmony_ci#define XFER_BUF_SIZE 0xF000 43141cc406Sopenharmony_ci 44141cc406Sopenharmony_ci 45141cc406Sopenharmony_ci/* HP3400 firmware data */ 46141cc406Sopenharmony_cistatic unsigned char abData0000[] = { 47141cc406Sopenharmony_ci 0xfe, 0x9f, 0x58, 0x1b, 0x00, 0x03, 0xa4, 0x02, 0x63, 0x02, 0x33, 0x02, 48141cc406Sopenharmony_ci 0x0d, 0x02, 0xf0, 0x01, 49141cc406Sopenharmony_ci 0xd8, 0x01, 0xc5, 0x01, 0xb5, 0x01, 0xa8, 0x01, 0x9d, 0x01, 0x93, 0x01, 50141cc406Sopenharmony_ci 0x8b, 0x01, 0x84, 0x01, 51141cc406Sopenharmony_ci 0x7e, 0x01, 0x79, 0x01, 0x74, 0x01, 0x70, 0x01, 0x6d, 0x01, 0x69, 0x01, 52141cc406Sopenharmony_ci 0x67, 0x01, 0x64, 0x01, 53141cc406Sopenharmony_ci 0x62, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x5d, 0x01, 0x5c, 0x01, 0x5b, 0x01, 54141cc406Sopenharmony_ci 0x5a, 0x01, 0x59, 0x01, 55141cc406Sopenharmony_ci 0x58, 0x01, 0x57, 0x01, 0x57, 0x01, 0x56, 0x01, 0x56, 0x01, 0x55, 0x01, 56141cc406Sopenharmony_ci 0x55, 0x01, 0x54, 0x01, 57141cc406Sopenharmony_ci 0x54, 0x01, 0x54, 0x01, 0x54, 0x01, 0x53, 0x01, 0x53, 0x01, 0x53, 0x01, 58141cc406Sopenharmony_ci 0x53, 0x01, 0x52, 0x81 59141cc406Sopenharmony_ci}; 60141cc406Sopenharmony_ci 61141cc406Sopenharmony_ci/* 1st word : 0x9ffe = 40958, strip 15th bit: 0x1ffe = 8190 62141cc406Sopenharmony_ci 2nd word : 0x1b58 = 7000 -> coincidence ? 63141cc406Sopenharmony_ci other words: formula: y = 676 / (2 - exp(0.113 * (1-x)) ), where x = 0 for first entry 64141cc406Sopenharmony_ci*/ 65141cc406Sopenharmony_ci 66141cc406Sopenharmony_ci/* more HP3400 firmware data */ 67141cc406Sopenharmony_cistatic unsigned char abData0400[] = { 68141cc406Sopenharmony_ci 0xa4, 0x82, 0x00, 0x80, 0xa4, 0x82, 0xaa, 0x02, 0xc0, 0x02, 0xe8, 0x02, 69141cc406Sopenharmony_ci 0x3e, 0x03, 0xc8, 0x03, 70141cc406Sopenharmony_ci 0x58, 0x1b, 0xfe, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71141cc406Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 72141cc406Sopenharmony_ci 0x00, 0x00, 0x00, 0x00 73141cc406Sopenharmony_ci}; 74141cc406Sopenharmony_ci 75141cc406Sopenharmony_ci 76141cc406Sopenharmony_ci 77141cc406Sopenharmony_cistatic void 78141cc406Sopenharmony_ci_ConvertMotorTable (unsigned char *pabOld, unsigned char *pabNew, int iSize, 79141cc406Sopenharmony_ci int iLpi) 80141cc406Sopenharmony_ci{ 81141cc406Sopenharmony_ci int iData, i, iBit15; 82141cc406Sopenharmony_ci 83141cc406Sopenharmony_ci for (i = 0; i < (iSize / 2); i++) 84141cc406Sopenharmony_ci { 85141cc406Sopenharmony_ci iData = pabOld[2 * i + 0] + (pabOld[2 * i + 1] << 8); 86141cc406Sopenharmony_ci iBit15 = (iData & 0x8000); 87141cc406Sopenharmony_ci iData = (iData & 0x7FFF); 88141cc406Sopenharmony_ci if (iData <= 0x400) 89141cc406Sopenharmony_ci { 90141cc406Sopenharmony_ci iData = iData * iLpi / 300; 91141cc406Sopenharmony_ci } 92141cc406Sopenharmony_ci if (iBit15 != 0) 93141cc406Sopenharmony_ci { 94141cc406Sopenharmony_ci iData |= 0x8000; 95141cc406Sopenharmony_ci } 96141cc406Sopenharmony_ci pabNew[2 * i + 0] = iData & 255; 97141cc406Sopenharmony_ci pabNew[2 * i + 1] = (iData >> 8) & 255; 98141cc406Sopenharmony_ci } 99141cc406Sopenharmony_ci} 100141cc406Sopenharmony_ci 101141cc406Sopenharmony_ci 102141cc406Sopenharmony_ci/************************************************************************* 103141cc406Sopenharmony_ci _ProbeRegisters 104141cc406Sopenharmony_ci =============== 105141cc406Sopenharmony_ci Tries to determine certain hardware properties. 106141cc406Sopenharmony_ci 107141cc406Sopenharmony_ci This is done by checking the writeability of some scanner registers. 108141cc406Sopenharmony_ci We cannot rely simply on the scanner model to contain a specific 109141cc406Sopenharmony_ci chip. The HP3300c for example uses one of at least three slightly 110141cc406Sopenharmony_ci different scanner ASICs (NIASH00012, NIASH00013 and NIASH00014). 111141cc406Sopenharmony_ci 112141cc406Sopenharmony_ci OUT pHWParams Hardware parameters, updated fields: 113141cc406Sopenharmony_ci fGamma16 TRUE if 16 bit gamma tables can be used 114141cc406Sopenharmony_ci fReg07 TRUE if reg07 is writeable 115141cc406Sopenharmony_ci iBufferSize Size of scanner's internal buffer 116141cc406Sopenharmony_ci 117141cc406Sopenharmony_ci Returns TRUE if a NIASH chipset was found. 118141cc406Sopenharmony_ci*************************************************************************/ 119141cc406Sopenharmony_cistatic SANE_Bool 120141cc406Sopenharmony_ci_ProbeRegisters (THWParams * pHWParams) 121141cc406Sopenharmony_ci{ 122141cc406Sopenharmony_ci unsigned char bData1, bData2; 123141cc406Sopenharmony_ci int iHandle; 124141cc406Sopenharmony_ci 125141cc406Sopenharmony_ci iHandle = pHWParams->iXferHandle; 126141cc406Sopenharmony_ci 127141cc406Sopenharmony_ci DBG (DBG_MSG, "Probing scanner...\n"); 128141cc406Sopenharmony_ci 129141cc406Sopenharmony_ci /* check register 0x04 */ 130141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x04, 0x55); 131141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x04, &bData1); 132141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x04, 0xAA); 133141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x04, &bData2); 134141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x04, 0x07); 135141cc406Sopenharmony_ci if ((bData1 != 0x55) || (bData2 != 0xAA)) 136141cc406Sopenharmony_ci { 137141cc406Sopenharmony_ci DBG (DBG_ERR, " No NIASH chipset found!\n"); 138141cc406Sopenharmony_ci return SANE_FALSE; 139141cc406Sopenharmony_ci } 140141cc406Sopenharmony_ci 141141cc406Sopenharmony_ci /* check writeability of register 3 bit 1 */ 142141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x03, &bData1); 143141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, bData1 | 0x02); 144141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x03, &bData2); 145141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, bData1); 146141cc406Sopenharmony_ci pHWParams->fGamma16 = ((bData2 & 0x02) != 0); 147141cc406Sopenharmony_ci DBG (DBG_MSG, " Gamma table entries are %d bit\n", 148141cc406Sopenharmony_ci pHWParams->fGamma16 ? 16 : 8); 149141cc406Sopenharmony_ci 150141cc406Sopenharmony_ci /* check register 0x07 */ 151141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x07, &bData1); 152141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x07, 0x1C); 153141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x07, &bData2); 154141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x07, bData1); 155141cc406Sopenharmony_ci pHWParams->fReg07 = (bData2 == 0x1C); 156141cc406Sopenharmony_ci 157141cc406Sopenharmony_ci if (!pHWParams->fGamma16) 158141cc406Sopenharmony_ci { 159141cc406Sopenharmony_ci /* internal scan buffer size is an educated guess, but seems to correlate 160141cc406Sopenharmony_ci well with the size calculated from several windows driver log files 161141cc406Sopenharmony_ci size = 128kB - 44088 unsigned chars (space required for gamma/calibration table) 162141cc406Sopenharmony_ci */ 163141cc406Sopenharmony_ci pHWParams->iBufferSize = 86984L; 164141cc406Sopenharmony_ci DBG (DBG_MSG, " NIASH version < 00014\n"); 165141cc406Sopenharmony_ci } 166141cc406Sopenharmony_ci else 167141cc406Sopenharmony_ci { 168141cc406Sopenharmony_ci pHWParams->iBufferSize = 0x60000L; 169141cc406Sopenharmony_ci if (!pHWParams->fReg07) 170141cc406Sopenharmony_ci { 171141cc406Sopenharmony_ci DBG (DBG_MSG, " NIASH version = 00014\n"); 172141cc406Sopenharmony_ci } 173141cc406Sopenharmony_ci else 174141cc406Sopenharmony_ci { 175141cc406Sopenharmony_ci DBG (DBG_MSG, " NIASH version > 00014\n"); 176141cc406Sopenharmony_ci } 177141cc406Sopenharmony_ci } 178141cc406Sopenharmony_ci 179141cc406Sopenharmony_ci return SANE_TRUE; 180141cc406Sopenharmony_ci} 181141cc406Sopenharmony_ci 182141cc406Sopenharmony_ci 183141cc406Sopenharmony_ci/* returns 0 on success, < 0 otherwise */ 184141cc406Sopenharmony_ciSTATIC int 185141cc406Sopenharmony_ciNiashOpen (THWParams * pHWParams, const char *pszName) 186141cc406Sopenharmony_ci{ 187141cc406Sopenharmony_ci int iXferHandle; 188141cc406Sopenharmony_ci 189141cc406Sopenharmony_ci iXferHandle = NiashXferOpen (pszName, &pHWParams->eModel); 190141cc406Sopenharmony_ci if (iXferHandle < 0) 191141cc406Sopenharmony_ci { 192141cc406Sopenharmony_ci DBG (DBG_ERR, "NiashXferOpen failed for '%s'\n", pszName); 193141cc406Sopenharmony_ci return -1; 194141cc406Sopenharmony_ci } 195141cc406Sopenharmony_ci 196141cc406Sopenharmony_ci pHWParams->iXferHandle = iXferHandle; 197141cc406Sopenharmony_ci 198141cc406Sopenharmony_ci NiashWakeup (pHWParams->iXferHandle); 199141cc406Sopenharmony_ci 200141cc406Sopenharmony_ci /* default HW params */ 201141cc406Sopenharmony_ci pHWParams->iSensorSkew = 8; 202141cc406Sopenharmony_ci pHWParams->iTopLeftX = 0; 203141cc406Sopenharmony_ci pHWParams->iTopLeftY = 3; 204141cc406Sopenharmony_ci pHWParams->fReg07 = SANE_FALSE; 205141cc406Sopenharmony_ci pHWParams->iSkipLines = 0; 206141cc406Sopenharmony_ci pHWParams->iExpTime = 5408; 207141cc406Sopenharmony_ci pHWParams->iReversedHead = SANE_TRUE; 208141cc406Sopenharmony_ci 209141cc406Sopenharmony_ci switch (pHWParams->eModel) 210141cc406Sopenharmony_ci { 211141cc406Sopenharmony_ci 212141cc406Sopenharmony_ci case eHp3300c: 213141cc406Sopenharmony_ci DBG (DBG_MSG, "Setting params for Hp3300\n"); 214141cc406Sopenharmony_ci pHWParams->iTopLeftX = 4; 215141cc406Sopenharmony_ci pHWParams->iTopLeftY = 11; 216141cc406Sopenharmony_ci pHWParams->iSkipLines = 14; 217141cc406Sopenharmony_ci break; 218141cc406Sopenharmony_ci 219141cc406Sopenharmony_ci case eHp3400c: 220141cc406Sopenharmony_ci case eHp4300c: 221141cc406Sopenharmony_ci DBG (DBG_MSG, "Setting params for Hp3400c/Hp4300c\n"); 222141cc406Sopenharmony_ci pHWParams->iTopLeftX = 3; 223141cc406Sopenharmony_ci pHWParams->iTopLeftY = 14; 224141cc406Sopenharmony_ci pHWParams->fReg07 = SANE_TRUE; 225141cc406Sopenharmony_ci break; 226141cc406Sopenharmony_ci 227141cc406Sopenharmony_ci case eAgfaTouch: 228141cc406Sopenharmony_ci DBG (DBG_MSG, "Setting params for AgfaTouch\n"); 229141cc406Sopenharmony_ci pHWParams->iReversedHead = SANE_FALSE; /* head not reversed on Agfa Touch */ 230141cc406Sopenharmony_ci pHWParams->iTopLeftX = 3; 231141cc406Sopenharmony_ci pHWParams->iTopLeftY = 10; 232141cc406Sopenharmony_ci pHWParams->iSkipLines = 7; 233141cc406Sopenharmony_ci break; 234141cc406Sopenharmony_ci 235141cc406Sopenharmony_ci case eUnknownModel: 236141cc406Sopenharmony_ci DBG (DBG_MSG, "Setting params for UnknownModel\n"); 237141cc406Sopenharmony_ci break; 238141cc406Sopenharmony_ci 239141cc406Sopenharmony_ci default: 240141cc406Sopenharmony_ci DBG (DBG_ERR, "ERROR: internal error! (%d)\n", (int) pHWParams->eModel); 241141cc406Sopenharmony_ci return -1; 242141cc406Sopenharmony_ci } 243141cc406Sopenharmony_ci 244141cc406Sopenharmony_ci /* autodetect some hardware properties */ 245141cc406Sopenharmony_ci if (!_ProbeRegisters (pHWParams)) 246141cc406Sopenharmony_ci { 247141cc406Sopenharmony_ci DBG (DBG_ERR, "_ProbeRegisters failed!\n"); 248141cc406Sopenharmony_ci return -1; 249141cc406Sopenharmony_ci } 250141cc406Sopenharmony_ci 251141cc406Sopenharmony_ci return 0; 252141cc406Sopenharmony_ci} 253141cc406Sopenharmony_ci 254141cc406Sopenharmony_ci 255141cc406Sopenharmony_ciSTATIC void 256141cc406Sopenharmony_ciNiashClose (THWParams * pHWPar) 257141cc406Sopenharmony_ci{ 258141cc406Sopenharmony_ci NiashXferClose (pHWPar->iXferHandle); 259141cc406Sopenharmony_ci pHWPar->iXferHandle = 0; 260141cc406Sopenharmony_ci} 261141cc406Sopenharmony_ci 262141cc406Sopenharmony_ci 263141cc406Sopenharmony_cistatic void 264141cc406Sopenharmony_ciWriteRegWord (int iHandle, unsigned char bReg, SANE_Word wData) 265141cc406Sopenharmony_ci{ 266141cc406Sopenharmony_ci NiashWriteReg (iHandle, bReg, wData & 0xFF); 267141cc406Sopenharmony_ci NiashWriteReg (iHandle, bReg + 1, (wData >> 8) & 0xFF); 268141cc406Sopenharmony_ci} 269141cc406Sopenharmony_ci 270141cc406Sopenharmony_ci 271141cc406Sopenharmony_ci/* calculate a 4096 unsigned char gamma table */ 272141cc406Sopenharmony_ciSTATIC void 273141cc406Sopenharmony_ciCalcGamma (unsigned char *pabTable, double Gamma) 274141cc406Sopenharmony_ci{ 275141cc406Sopenharmony_ci int i, iData; 276141cc406Sopenharmony_ci 277141cc406Sopenharmony_ci /* fill gamma table */ 278141cc406Sopenharmony_ci for (i = 0; i < 4096; i++) 279141cc406Sopenharmony_ci { 280141cc406Sopenharmony_ci iData = floor (256.0 * pow (((double) i / 4096.0), 1.0 / Gamma)); 281141cc406Sopenharmony_ci pabTable[i] = iData; 282141cc406Sopenharmony_ci } 283141cc406Sopenharmony_ci} 284141cc406Sopenharmony_ci 285141cc406Sopenharmony_ci 286141cc406Sopenharmony_ci/* 287141cc406Sopenharmony_ci Hp3400WriteFw 288141cc406Sopenharmony_ci ============= 289141cc406Sopenharmony_ci Writes data to scanners with a NIASH00019 chipset, e.g. 290141cc406Sopenharmony_ci gamma, calibration and motor control data. 291141cc406Sopenharmony_ci 292141cc406Sopenharmony_ci IN pabData pointer to firmware data 293141cc406Sopenharmony_ci iLen Size of firmware date (unsigned chars) 294141cc406Sopenharmony_ci iAddr Scanner address to write to 295141cc406Sopenharmony_ci*/ 296141cc406Sopenharmony_cistatic void 297141cc406Sopenharmony_ciHp3400cWriteFW (int iXferHandle, unsigned char *pabData, int iLen, int iAddr) 298141cc406Sopenharmony_ci{ 299141cc406Sopenharmony_ci iAddr--; 300141cc406Sopenharmony_ci NiashWriteReg (iXferHandle, 0x21, iAddr & 0xFF); 301141cc406Sopenharmony_ci NiashWriteReg (iXferHandle, 0x22, (iAddr >> 8) & 0xFF); 302141cc406Sopenharmony_ci NiashWriteReg (iXferHandle, 0x23, (iAddr >> 16) & 0xFF); 303141cc406Sopenharmony_ci NiashWriteBulk (iXferHandle, pabData, iLen); 304141cc406Sopenharmony_ci} 305141cc406Sopenharmony_ci 306141cc406Sopenharmony_ci 307141cc406Sopenharmony_ci/* Writes the gamma and offset/gain tables to the scanner. 308141cc406Sopenharmony_ci In case a calibration file exist, it will be used for offset/gain */ 309141cc406Sopenharmony_ciSTATIC void 310141cc406Sopenharmony_ciWriteGammaCalibTable (unsigned char *pabGammaR, unsigned char *pabGammaG, 311141cc406Sopenharmony_ci unsigned char *pabGammaB, unsigned char *pabCalibTable, 312141cc406Sopenharmony_ci int iGain, int iOffset, THWParams * pHWPar) 313141cc406Sopenharmony_ci{ 314141cc406Sopenharmony_ci int i, j, k; 315141cc406Sopenharmony_ci static unsigned char abGamma[60000]; 316141cc406Sopenharmony_ci int iData; 317141cc406Sopenharmony_ci int iHandle; 318141cc406Sopenharmony_ci 319141cc406Sopenharmony_ci iHandle = pHWPar->iXferHandle; 320141cc406Sopenharmony_ci 321141cc406Sopenharmony_ci j = 0; 322141cc406Sopenharmony_ci /* fill gamma table for red component */ 323141cc406Sopenharmony_ci /* pad entries with 0 for 16-bit gamma table */ 324141cc406Sopenharmony_ci for (i = 0; i < 4096; i++) 325141cc406Sopenharmony_ci { 326141cc406Sopenharmony_ci if (pHWPar->fGamma16) 327141cc406Sopenharmony_ci { 328141cc406Sopenharmony_ci abGamma[j++] = 0; 329141cc406Sopenharmony_ci } 330141cc406Sopenharmony_ci abGamma[j++] = pabGammaR[i]; 331141cc406Sopenharmony_ci } 332141cc406Sopenharmony_ci /* fill gamma table for green component */ 333141cc406Sopenharmony_ci for (i = 0; i < 4096; i++) 334141cc406Sopenharmony_ci { 335141cc406Sopenharmony_ci if (pHWPar->fGamma16) 336141cc406Sopenharmony_ci { 337141cc406Sopenharmony_ci abGamma[j++] = 0; 338141cc406Sopenharmony_ci } 339141cc406Sopenharmony_ci abGamma[j++] = pabGammaG[i]; 340141cc406Sopenharmony_ci } 341141cc406Sopenharmony_ci /* fill gamma table for blue component */ 342141cc406Sopenharmony_ci for (i = 0; i < 4096; i++) 343141cc406Sopenharmony_ci { 344141cc406Sopenharmony_ci if (pHWPar->fGamma16) 345141cc406Sopenharmony_ci { 346141cc406Sopenharmony_ci abGamma[j++] = 0; 347141cc406Sopenharmony_ci } 348141cc406Sopenharmony_ci abGamma[j++] = pabGammaB[i]; 349141cc406Sopenharmony_ci } 350141cc406Sopenharmony_ci 351141cc406Sopenharmony_ci if (pabCalibTable == NULL) 352141cc406Sopenharmony_ci { 353141cc406Sopenharmony_ci iData = (iGain << 6) + iOffset; 354141cc406Sopenharmony_ci for (i = 0; i < HW_PIXELS; i++) 355141cc406Sopenharmony_ci { 356141cc406Sopenharmony_ci for (k = 0; k < 3; k++) 357141cc406Sopenharmony_ci { 358141cc406Sopenharmony_ci abGamma[j++] = (iData) & 255; 359141cc406Sopenharmony_ci abGamma[j++] = (iData >> 8) & 255; 360141cc406Sopenharmony_ci } 361141cc406Sopenharmony_ci } 362141cc406Sopenharmony_ci } 363141cc406Sopenharmony_ci else 364141cc406Sopenharmony_ci { 365141cc406Sopenharmony_ci memcpy (&abGamma[j], pabCalibTable, HW_PIXELS * 6); 366141cc406Sopenharmony_ci j += HW_PIXELS * 6; 367141cc406Sopenharmony_ci } 368141cc406Sopenharmony_ci 369141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x02, 0x80); 370141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, 0x01); 371141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, 0x11); 372141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x02, 0x84); 373141cc406Sopenharmony_ci 374141cc406Sopenharmony_ci if (pHWPar->fReg07) 375141cc406Sopenharmony_ci { 376141cc406Sopenharmony_ci Hp3400cWriteFW (iHandle, abGamma, j, 0x2000); 377141cc406Sopenharmony_ci } 378141cc406Sopenharmony_ci else 379141cc406Sopenharmony_ci { 380141cc406Sopenharmony_ci NiashWriteBulk (iHandle, abGamma, j); 381141cc406Sopenharmony_ci } 382141cc406Sopenharmony_ci 383141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x02, 0x80); 384141cc406Sopenharmony_ci} 385141cc406Sopenharmony_ci 386141cc406Sopenharmony_ci 387141cc406Sopenharmony_cistatic void 388141cc406Sopenharmony_ciWriteAFEReg (int iHandle, int iReg, int iData) 389141cc406Sopenharmony_ci{ 390141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x25, iReg); 391141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x26, iData); 392141cc406Sopenharmony_ci} 393141cc406Sopenharmony_ci 394141cc406Sopenharmony_ci 395141cc406Sopenharmony_ci/* setup the analog front-end -> coarse calibration */ 396141cc406Sopenharmony_cistatic void 397141cc406Sopenharmony_ciWriteAFE (int iHandle) 398141cc406Sopenharmony_ci{ 399141cc406Sopenharmony_ci /* see WM8143 datasheet */ 400141cc406Sopenharmony_ci 401141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x04, 0x00); 402141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x03, 0x12); 403141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x02, 0x04); 404141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x05, 0x10); 405141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x01, 0x03); 406141cc406Sopenharmony_ci 407141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x20, 0xc0); /*c8 *//* red offset */ 408141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x21, 0xc0); /*c8 *//* green offset */ 409141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x22, 0xc0); /*d0 *//* blue offset */ 410141cc406Sopenharmony_ci 411141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x28, 0x05); /*5 *//* red gain */ 412141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x29, 0x03); /*3 *//* green gain */ 413141cc406Sopenharmony_ci WriteAFEReg (iHandle, 0x2A, 0x04); /*4 *//* blue gain */ 414141cc406Sopenharmony_ci} 415141cc406Sopenharmony_ci 416141cc406Sopenharmony_ci 417141cc406Sopenharmony_ci/* wait for the carriage to return */ 418141cc406Sopenharmony_cistatic void 419141cc406Sopenharmony_ciWaitReadyBit (int iHandle) 420141cc406Sopenharmony_ci{ 421141cc406Sopenharmony_ci unsigned char bData; 422141cc406Sopenharmony_ci 423141cc406Sopenharmony_ci do 424141cc406Sopenharmony_ci { 425141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x03, &bData); 426141cc406Sopenharmony_ci } 427141cc406Sopenharmony_ci while ((bData & 8) == 0); 428141cc406Sopenharmony_ci} 429141cc406Sopenharmony_ci 430141cc406Sopenharmony_ci 431141cc406Sopenharmony_ci/* 432141cc406Sopenharmony_ci Initialisation specific for NIASH00014 and lower chips 433141cc406Sopenharmony_ci*/ 434141cc406Sopenharmony_cistatic void 435141cc406Sopenharmony_ciInitNiash00014 (TScanParams * pParams, THWParams * pHWParams) 436141cc406Sopenharmony_ci{ 437141cc406Sopenharmony_ci int iHandle, iLpiCode; 438141cc406Sopenharmony_ci 439141cc406Sopenharmony_ci iHandle = pHWParams->iXferHandle; 440141cc406Sopenharmony_ci 441141cc406Sopenharmony_ci /* exposure time (in units 24/Fcrystal)? */ 442141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x08, pHWParams->iExpTime - 1); 443141cc406Sopenharmony_ci 444141cc406Sopenharmony_ci /* width in pixels */ 445141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x12, pParams->iWidth - 1); 446141cc406Sopenharmony_ci 447141cc406Sopenharmony_ci /* top */ 448141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x17, pParams->iTop); 449141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x19, pParams->iTop); 450141cc406Sopenharmony_ci 451141cc406Sopenharmony_ci /* time between stepper motor steps (in units of 24/Fcrystal)? */ 452141cc406Sopenharmony_ci iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L; 453141cc406Sopenharmony_ci 454141cc406Sopenharmony_ci if (!pHWParams->fGamma16) 455141cc406Sopenharmony_ci { 456141cc406Sopenharmony_ci /* NIASH 00012 / 00013 init */ 457141cc406Sopenharmony_ci 458141cc406Sopenharmony_ci /* LPI specific settings */ 459141cc406Sopenharmony_ci if (pParams->iLpi < 600) 460141cc406Sopenharmony_ci { 461141cc406Sopenharmony_ci /* set halfres bit */ 462141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x06, 0x01); 463141cc406Sopenharmony_ci /* double lpi code because of halfres bit */ 464141cc406Sopenharmony_ci iLpiCode *= 2; 465141cc406Sopenharmony_ci } 466141cc406Sopenharmony_ci else 467141cc406Sopenharmony_ci { 468141cc406Sopenharmony_ci /* clear halfres bit */ 469141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x06, 0x00); 470141cc406Sopenharmony_ci /* add exptime to make it scan slower */ 471141cc406Sopenharmony_ci iLpiCode += pHWParams->iExpTime; 472141cc406Sopenharmony_ci } 473141cc406Sopenharmony_ci 474141cc406Sopenharmony_ci /* unknown setting */ 475141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x27, 0x7FD2); 476141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x29, 0x6421); 477141cc406Sopenharmony_ci 478141cc406Sopenharmony_ci } 479141cc406Sopenharmony_ci else 480141cc406Sopenharmony_ci { 481141cc406Sopenharmony_ci /* NIASH 00014 init */ 482141cc406Sopenharmony_ci 483141cc406Sopenharmony_ci /* halfres bit always cleared */ 484141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x06, 0x00); 485141cc406Sopenharmony_ci 486141cc406Sopenharmony_ci /* LPI specific settings */ 487141cc406Sopenharmony_ci if (pParams->iLpi >= 600) 488141cc406Sopenharmony_ci { 489141cc406Sopenharmony_ci /* add exptime to make it scan slower */ 490141cc406Sopenharmony_ci iLpiCode += pHWParams->iExpTime; 491141cc406Sopenharmony_ci } 492141cc406Sopenharmony_ci 493141cc406Sopenharmony_ci /* unknown setting */ 494141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */ 495141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */ 496141cc406Sopenharmony_ci } 497141cc406Sopenharmony_ci 498141cc406Sopenharmony_ci /* LPI code */ 499141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x0A, iLpiCode - 1); 500141cc406Sopenharmony_ci 501141cc406Sopenharmony_ci /* backtrack reversing speed */ 502141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32); 503141cc406Sopenharmony_ci} 504141cc406Sopenharmony_ci 505141cc406Sopenharmony_ci 506141cc406Sopenharmony_ci/* 507141cc406Sopenharmony_ci Initialisation specific for NIASH00019 chips 508141cc406Sopenharmony_ci*/ 509141cc406Sopenharmony_cistatic void 510141cc406Sopenharmony_ciInitNiash00019 (TScanParams * pParams, THWParams * pHWParams) 511141cc406Sopenharmony_ci{ 512141cc406Sopenharmony_ci int iHandle, iLpiCode; 513141cc406Sopenharmony_ci static unsigned char abMotor[512]; 514141cc406Sopenharmony_ci 515141cc406Sopenharmony_ci 516141cc406Sopenharmony_ci iHandle = pHWParams->iXferHandle; 517141cc406Sopenharmony_ci 518141cc406Sopenharmony_ci /* exposure time (in units 24/Fcrystal)? */ 519141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x08, pHWParams->iExpTime); 520141cc406Sopenharmony_ci 521141cc406Sopenharmony_ci /* width in pixels */ 522141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x12, pParams->iWidth); 523141cc406Sopenharmony_ci 524141cc406Sopenharmony_ci /* ? */ 525141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */ 526141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */ 527141cc406Sopenharmony_ci 528141cc406Sopenharmony_ci /* specific handling of 150 dpi resolution */ 529141cc406Sopenharmony_ci if (pParams->iLpi == 150) 530141cc406Sopenharmony_ci { 531141cc406Sopenharmony_ci /* use 300 LPI but skip every other line */ 532141cc406Sopenharmony_ci pParams->iLpi = 300; 533141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x06, 0x01); 534141cc406Sopenharmony_ci } 535141cc406Sopenharmony_ci else 536141cc406Sopenharmony_ci { 537141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x06, 0x00); 538141cc406Sopenharmony_ci } 539141cc406Sopenharmony_ci 540141cc406Sopenharmony_ci /* DPI and position table */ 541141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x07, 0x02); 542141cc406Sopenharmony_ci _ConvertMotorTable (abData0000, abMotor, sizeof (abData0000), 543141cc406Sopenharmony_ci pParams->iLpi); 544141cc406Sopenharmony_ci Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0000), 0x000); 545141cc406Sopenharmony_ci _ConvertMotorTable (abData0400, abMotor, sizeof (abData0400), 546141cc406Sopenharmony_ci pParams->iLpi); 547141cc406Sopenharmony_ci Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0400), 0x400); 548141cc406Sopenharmony_ci 549141cc406Sopenharmony_ci /* backtrack reversing speed */ 550141cc406Sopenharmony_ci iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L; 551141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32); 552141cc406Sopenharmony_ci} 553141cc406Sopenharmony_ci 554141cc406Sopenharmony_ci 555141cc406Sopenharmony_ci/* 556141cc406Sopenharmony_ci Scanner initialisation common to all NIASH chips 557141cc406Sopenharmony_ci*/ 558141cc406Sopenharmony_cistatic void 559141cc406Sopenharmony_ciInitNiashCommon (TScanParams * pParams, THWParams * pHWParams) 560141cc406Sopenharmony_ci{ 561141cc406Sopenharmony_ci int iWidthHW, iHandle, iMaxLevel; 562141cc406Sopenharmony_ci 563141cc406Sopenharmony_ci 564141cc406Sopenharmony_ci iHandle = pHWParams->iXferHandle; 565141cc406Sopenharmony_ci 566141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x02, 0x80); 567141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, 0x11); 568141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x01, 0x8B); 569141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x05, 0x01); 570141cc406Sopenharmony_ci 571141cc406Sopenharmony_ci /* dpi */ 572141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x0C, pParams->iDpi); 573141cc406Sopenharmony_ci 574141cc406Sopenharmony_ci /* calculate width in units of HW resolution */ 575141cc406Sopenharmony_ci iWidthHW = pParams->iWidth * (HW_DPI / pParams->iDpi); 576141cc406Sopenharmony_ci 577141cc406Sopenharmony_ci /* set left and right limits */ 578141cc406Sopenharmony_ci if (pHWParams->iReversedHead) 579141cc406Sopenharmony_ci { 580141cc406Sopenharmony_ci /* head is reversed */ 581141cc406Sopenharmony_ci /* right */ 582141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x0E, 583141cc406Sopenharmony_ci 3 * (HW_PIXELS - (pParams->iLeft + iWidthHW))); 584141cc406Sopenharmony_ci 585141cc406Sopenharmony_ci /* left */ 586141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x10, 3 * (HW_PIXELS - pParams->iLeft) - 1); 587141cc406Sopenharmony_ci } 588141cc406Sopenharmony_ci else 589141cc406Sopenharmony_ci { 590141cc406Sopenharmony_ci /* head is not reversed */ 591141cc406Sopenharmony_ci /*left */ 592141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x0E, 3 * pParams->iLeft); 593141cc406Sopenharmony_ci 594141cc406Sopenharmony_ci /* right */ 595141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x10, 3 * (pParams->iLeft + iWidthHW) - 1); 596141cc406Sopenharmony_ci } 597141cc406Sopenharmony_ci 598141cc406Sopenharmony_ci /* bottom */ 599141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x1B, pParams->iBottom); /* 0x393C); */ 600141cc406Sopenharmony_ci 601141cc406Sopenharmony_ci /* forward jogging speed */ 602141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x1D, 0x60); 603141cc406Sopenharmony_ci 604141cc406Sopenharmony_ci /* backtrack reversing speed? */ 605141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x2B, 0x15); 606141cc406Sopenharmony_ci 607141cc406Sopenharmony_ci /* backtrack distance */ 608141cc406Sopenharmony_ci if (pParams->iLpi < 600) 609141cc406Sopenharmony_ci { 610141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x1F, 0x30); 611141cc406Sopenharmony_ci } 612141cc406Sopenharmony_ci else 613141cc406Sopenharmony_ci { 614141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x1F, 0x18); 615141cc406Sopenharmony_ci } 616141cc406Sopenharmony_ci 617141cc406Sopenharmony_ci /* max buffer level before backtrace */ 618141cc406Sopenharmony_ci iMaxLevel = MIN (pHWParams->iBufferSize / pParams->iWidth, 250); 619141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x14, iMaxLevel - 1); 620141cc406Sopenharmony_ci 621141cc406Sopenharmony_ci /* lamp PWM, max = 0x1ff? */ 622141cc406Sopenharmony_ci WriteRegWord (iHandle, 0x2C, 0x01FF); 623141cc406Sopenharmony_ci 624141cc406Sopenharmony_ci /* not needed? */ 625141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x15, 0x90); /* 90 */ 626141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x16, 0x70); /* 70 */ 627141cc406Sopenharmony_ci 628141cc406Sopenharmony_ci WriteAFE (iHandle); 629141cc406Sopenharmony_ci 630141cc406Sopenharmony_ci WaitReadyBit (iHandle); 631141cc406Sopenharmony_ci 632141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, 0x05); 633141cc406Sopenharmony_ci 634141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x02, pParams->fCalib ? 0x88 : 0xA8); 635141cc406Sopenharmony_ci} 636141cc406Sopenharmony_ci 637141cc406Sopenharmony_ci 638141cc406Sopenharmony_ci/* write registers */ 639141cc406Sopenharmony_ciSTATIC SANE_Bool 640141cc406Sopenharmony_ciInitScan (TScanParams * pParams, THWParams * pHWParams) 641141cc406Sopenharmony_ci{ 642141cc406Sopenharmony_ci int iHeight; 643141cc406Sopenharmony_ci int iExpTime; 644141cc406Sopenharmony_ci TScanParams Params; 645141cc406Sopenharmony_ci 646141cc406Sopenharmony_ci /* check validity of scanparameters */ 647141cc406Sopenharmony_ci switch (pParams->iDpi) 648141cc406Sopenharmony_ci { 649141cc406Sopenharmony_ci case 150: 650141cc406Sopenharmony_ci case 300: 651141cc406Sopenharmony_ci case 600: 652141cc406Sopenharmony_ci break; 653141cc406Sopenharmony_ci default: 654141cc406Sopenharmony_ci DBG (DBG_ERR, "Invalid dpi (%d)\n", pParams->iDpi); 655141cc406Sopenharmony_ci return SANE_FALSE; 656141cc406Sopenharmony_ci } 657141cc406Sopenharmony_ci 658141cc406Sopenharmony_ci iHeight = (pParams->iBottom - pParams->iTop + 1); 659141cc406Sopenharmony_ci if (iHeight <= 0) 660141cc406Sopenharmony_ci { 661141cc406Sopenharmony_ci DBG (DBG_ERR, "Invalid height (%d)\n", iHeight); 662141cc406Sopenharmony_ci return SANE_FALSE; 663141cc406Sopenharmony_ci } 664141cc406Sopenharmony_ci 665141cc406Sopenharmony_ci if (pParams->iWidth <= 0) 666141cc406Sopenharmony_ci { 667141cc406Sopenharmony_ci DBG (DBG_ERR, "Invalid width (%d)\n", pParams->iWidth); 668141cc406Sopenharmony_ci return SANE_FALSE; 669141cc406Sopenharmony_ci } 670141cc406Sopenharmony_ci 671141cc406Sopenharmony_ci switch (pParams->iLpi) 672141cc406Sopenharmony_ci { 673141cc406Sopenharmony_ci case 150: 674141cc406Sopenharmony_ci case 300: 675141cc406Sopenharmony_ci case 600: 676141cc406Sopenharmony_ci break; 677141cc406Sopenharmony_ci default: 678141cc406Sopenharmony_ci DBG (DBG_ERR, "Invalid lpi (%d)\n", pParams->iLpi); 679141cc406Sopenharmony_ci return SANE_FALSE; 680141cc406Sopenharmony_ci } 681141cc406Sopenharmony_ci 682141cc406Sopenharmony_ci /* exposure time (in units of 24/Fcrystal?), must be divisible by 8 !!! */ 683141cc406Sopenharmony_ci iExpTime = 5408; 684141cc406Sopenharmony_ci if ((iExpTime % 8) != 0) 685141cc406Sopenharmony_ci { 686141cc406Sopenharmony_ci DBG (DBG_ERR, "Invalid exposure time (%d)\n", iExpTime); 687141cc406Sopenharmony_ci return SANE_FALSE; 688141cc406Sopenharmony_ci } 689141cc406Sopenharmony_ci 690141cc406Sopenharmony_ci /* 691141cc406Sopenharmony_ci *** Done checking scan parameters validity *** 692141cc406Sopenharmony_ci */ 693141cc406Sopenharmony_ci 694141cc406Sopenharmony_ci /* 695141cc406Sopenharmony_ci copy the parameters locally and make pParams point to the local copy 696141cc406Sopenharmony_ci */ 697141cc406Sopenharmony_ci memcpy (&Params, pParams, sizeof (Params)); 698141cc406Sopenharmony_ci pParams = &Params; 699141cc406Sopenharmony_ci 700141cc406Sopenharmony_ci if (!pHWParams->fReg07) 701141cc406Sopenharmony_ci { 702141cc406Sopenharmony_ci /* init NIASH00014 and lower */ 703141cc406Sopenharmony_ci InitNiash00014 (pParams, pHWParams); 704141cc406Sopenharmony_ci } 705141cc406Sopenharmony_ci else 706141cc406Sopenharmony_ci { 707141cc406Sopenharmony_ci /* init NIASH00019 */ 708141cc406Sopenharmony_ci InitNiash00019 (pParams, pHWParams); 709141cc406Sopenharmony_ci } 710141cc406Sopenharmony_ci 711141cc406Sopenharmony_ci /* common NIASH init */ 712141cc406Sopenharmony_ci InitNiashCommon (pParams, pHWParams); 713141cc406Sopenharmony_ci 714141cc406Sopenharmony_ci return SANE_TRUE; 715141cc406Sopenharmony_ci} 716141cc406Sopenharmony_ci 717141cc406Sopenharmony_ci 718141cc406Sopenharmony_ci/************************************************************************/ 719141cc406Sopenharmony_ci 720141cc406Sopenharmony_cistatic SANE_Bool 721141cc406Sopenharmony_ciXferBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine, 722141cc406Sopenharmony_ci SANE_Bool fReturn) 723141cc406Sopenharmony_ci{ 724141cc406Sopenharmony_ci unsigned char bData, bData2; 725141cc406Sopenharmony_ci SANE_Bool fJustDone = SANE_FALSE; 726141cc406Sopenharmony_ci /* all calculated transfers done ? */ 727141cc406Sopenharmony_ci if (p->iLinesLeft == 0) 728141cc406Sopenharmony_ci return SANE_FALSE; 729141cc406Sopenharmony_ci 730141cc406Sopenharmony_ci /* time for a fresh read? */ 731141cc406Sopenharmony_ci if (p->iCurLine == 0) 732141cc406Sopenharmony_ci { 733141cc406Sopenharmony_ci int iLines; 734141cc406Sopenharmony_ci iLines = p->iLinesPerXferBuf; 735141cc406Sopenharmony_ci /* read only as many lines as needed */ 736141cc406Sopenharmony_ci if (p->iLinesLeft > 0 && p->iLinesLeft <= iLines) 737141cc406Sopenharmony_ci { 738141cc406Sopenharmony_ci iLines = p->iLinesLeft; 739141cc406Sopenharmony_ci DBG (DBG_MSG, "\n"); 740141cc406Sopenharmony_ci DBG (DBG_MSG, "last bulk read\n"); 741141cc406Sopenharmony_ci if (iLines < p->iLinesPerXferBuf) 742141cc406Sopenharmony_ci { 743141cc406Sopenharmony_ci DBG (DBG_MSG, 744141cc406Sopenharmony_ci "reading reduced number of lines: %d instead of %d\n", 745141cc406Sopenharmony_ci iLines, p->iLinesPerXferBuf); 746141cc406Sopenharmony_ci } 747141cc406Sopenharmony_ci fJustDone = SANE_TRUE; 748141cc406Sopenharmony_ci } 749141cc406Sopenharmony_ci /* reading old buffer level */ 750141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x20, &bData); 751141cc406Sopenharmony_ci NiashReadBulk (iHandle, p->pabXferBuf, iLines * p->iBytesPerLine); 752141cc406Sopenharmony_ci /* reding new buffer level */ 753141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x20, &bData2); 754141cc406Sopenharmony_ci if (fJustDone && fReturn) 755141cc406Sopenharmony_ci { 756141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x02, 0x80); 757141cc406Sopenharmony_ci DBG (DBG_MSG, "returning scanner head\n"); 758141cc406Sopenharmony_ci } 759141cc406Sopenharmony_ci DBG (DBG_MSG, 760141cc406Sopenharmony_ci "buffer level = %3d, <reading %5d unsigned chars>, buffer level = %3d\r", 761141cc406Sopenharmony_ci (int) bData, iLines * p->iBytesPerLine, (int) bData2); 762141cc406Sopenharmony_ci fflush (stdout); 763141cc406Sopenharmony_ci } 764141cc406Sopenharmony_ci /* copy one line */ 765141cc406Sopenharmony_ci if (pabLine != NULL) 766141cc406Sopenharmony_ci { 767141cc406Sopenharmony_ci memcpy (pabLine, &p->pabXferBuf[p->iCurLine * p->iBytesPerLine], 768141cc406Sopenharmony_ci p->iBytesPerLine); 769141cc406Sopenharmony_ci } 770141cc406Sopenharmony_ci /* advance pointer */ 771141cc406Sopenharmony_ci p->iCurLine = (p->iCurLine + 1) % p->iLinesPerXferBuf; 772141cc406Sopenharmony_ci 773141cc406Sopenharmony_ci /* one transfer line less to the XFerBuffer */ 774141cc406Sopenharmony_ci if (p->iLinesLeft > 0) 775141cc406Sopenharmony_ci --(p->iLinesLeft); 776141cc406Sopenharmony_ci return SANE_TRUE; 777141cc406Sopenharmony_ci} 778141cc406Sopenharmony_ci 779141cc406Sopenharmony_ci 780141cc406Sopenharmony_cistatic void 781141cc406Sopenharmony_ciXferBufferInit (int iHandle, TDataPipe * p) 782141cc406Sopenharmony_ci{ 783141cc406Sopenharmony_ci int i; 784141cc406Sopenharmony_ci 785141cc406Sopenharmony_ci p->pabXferBuf = (unsigned char *) malloc (XFER_BUF_SIZE); 786141cc406Sopenharmony_ci p->iCurLine = 0; 787141cc406Sopenharmony_ci 788141cc406Sopenharmony_ci /* skip garbage lines */ 789141cc406Sopenharmony_ci for (i = 0; i < p->iSkipLines; i++) 790141cc406Sopenharmony_ci { 791141cc406Sopenharmony_ci XferBufferGetLine (iHandle, p, NULL, SANE_FALSE); 792141cc406Sopenharmony_ci } 793141cc406Sopenharmony_ci} 794141cc406Sopenharmony_ci 795141cc406Sopenharmony_ci/* static procedure that fills the circular buffer in advance to any 796141cc406Sopenharmony_ci circular buffer data retrieval */ 797141cc406Sopenharmony_cistatic void 798141cc406Sopenharmony_ciCircBufferFill (int iHandle, TDataPipe * p, SANE_Bool iReversedHead) 799141cc406Sopenharmony_ci{ 800141cc406Sopenharmony_ci int i; 801141cc406Sopenharmony_ci for (i = 0; i < p->iLinesPerCircBuf; i++) 802141cc406Sopenharmony_ci { 803141cc406Sopenharmony_ci if (iReversedHead) 804141cc406Sopenharmony_ci { 805141cc406Sopenharmony_ci XferBufferGetLine (iHandle, p, 806141cc406Sopenharmony_ci &p->pabCircBuf[p->iRedLine * p->iBytesPerLine], 807141cc406Sopenharmony_ci SANE_FALSE); 808141cc406Sopenharmony_ci } 809141cc406Sopenharmony_ci else 810141cc406Sopenharmony_ci { 811141cc406Sopenharmony_ci XferBufferGetLine (iHandle, p, 812141cc406Sopenharmony_ci &p->pabCircBuf[p->iBluLine * p->iBytesPerLine], 813141cc406Sopenharmony_ci SANE_FALSE); 814141cc406Sopenharmony_ci } 815141cc406Sopenharmony_ci /* advance pointers */ 816141cc406Sopenharmony_ci p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf; 817141cc406Sopenharmony_ci p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf; 818141cc406Sopenharmony_ci p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf; 819141cc406Sopenharmony_ci } 820141cc406Sopenharmony_ci} 821141cc406Sopenharmony_ci 822141cc406Sopenharmony_cistatic void 823141cc406Sopenharmony_ciXferBufferExit (TDataPipe * p) 824141cc406Sopenharmony_ci{ 825141cc406Sopenharmony_ci if (p->pabXferBuf != NULL) 826141cc406Sopenharmony_ci { 827141cc406Sopenharmony_ci free (p->pabXferBuf); 828141cc406Sopenharmony_ci p->pabXferBuf = NULL; 829141cc406Sopenharmony_ci } 830141cc406Sopenharmony_ci else 831141cc406Sopenharmony_ci { 832141cc406Sopenharmony_ci DBG (DBG_ERR, "XferBufExit: Xfer buffer not initialised!\n"); 833141cc406Sopenharmony_ci } 834141cc406Sopenharmony_ci} 835141cc406Sopenharmony_ci 836141cc406Sopenharmony_ci 837141cc406Sopenharmony_ci/* unscrambles a line: 838141cc406Sopenharmony_ci - combining the proper R, G and B lines and converting them to interpixel RGB 839141cc406Sopenharmony_ci - mirroring left to right 840141cc406Sopenharmony_ci*/ 841141cc406Sopenharmony_cistatic void 842141cc406Sopenharmony_ci_UnscrambleLine (unsigned char *pabLine, 843141cc406Sopenharmony_ci unsigned char *pabRed, unsigned char *pabGrn, 844141cc406Sopenharmony_ci unsigned char *pabBlu, int iWidth, SANE_Bool iReversedHead, 845141cc406Sopenharmony_ci int iScaleDownDpi, int iBufWeight) 846141cc406Sopenharmony_ci{ 847141cc406Sopenharmony_ci /* never change an approved algorithm ... 848141cc406Sopenharmony_ci so take Bertriks original source for this special case */ 849141cc406Sopenharmony_ci if (iScaleDownDpi == 1 && iBufWeight == 0) 850141cc406Sopenharmony_ci { 851141cc406Sopenharmony_ci int i, j; 852141cc406Sopenharmony_ci if (iReversedHead) 853141cc406Sopenharmony_ci { 854141cc406Sopenharmony_ci /* reversed */ 855141cc406Sopenharmony_ci for (i = 0; i < iWidth; i++) 856141cc406Sopenharmony_ci { 857141cc406Sopenharmony_ci j = (iWidth - i) * 3; 858141cc406Sopenharmony_ci pabLine[j - 3] = pabRed[i]; 859141cc406Sopenharmony_ci pabLine[j - 2] = pabGrn[i + iWidth]; 860141cc406Sopenharmony_ci pabLine[j - 1] = pabBlu[i + iWidth * 2]; 861141cc406Sopenharmony_ci } 862141cc406Sopenharmony_ci } 863141cc406Sopenharmony_ci else 864141cc406Sopenharmony_ci { 865141cc406Sopenharmony_ci /* not reversed */ 866141cc406Sopenharmony_ci for (i = 0; i < iWidth; i++) 867141cc406Sopenharmony_ci { 868141cc406Sopenharmony_ci pabLine[3 * i] = pabRed[i]; 869141cc406Sopenharmony_ci pabLine[3 * i + 1] = pabGrn[i + iWidth]; 870141cc406Sopenharmony_ci pabLine[3 * i + 2] = pabBlu[i + iWidth * 2]; 871141cc406Sopenharmony_ci } 872141cc406Sopenharmony_ci } 873141cc406Sopenharmony_ci } 874141cc406Sopenharmony_ci else 875141cc406Sopenharmony_ci { 876141cc406Sopenharmony_ci int i, j; /* loop variables */ 877141cc406Sopenharmony_ci int c; /* color buffer accumulator for horizontal average */ 878141cc406Sopenharmony_ci 879141cc406Sopenharmony_ci /* initialize for incremental color buffer access */ 880141cc406Sopenharmony_ci int iInc = 1; 881141cc406Sopenharmony_ci int iStart = 0; 882141cc406Sopenharmony_ci 883141cc406Sopenharmony_ci /* set for "from the end to the front" of the circular color buffers */ 884141cc406Sopenharmony_ci if (iReversedHead) 885141cc406Sopenharmony_ci { 886141cc406Sopenharmony_ci iStart = iWidth - iScaleDownDpi; 887141cc406Sopenharmony_ci iInc = -1; 888141cc406Sopenharmony_ci } 889141cc406Sopenharmony_ci 890141cc406Sopenharmony_ci /* each pixel is the mean of iScaleDownDpi 891141cc406Sopenharmony_ci so set the skip width accordingly */ 892141cc406Sopenharmony_ci iInc *= iScaleDownDpi; 893141cc406Sopenharmony_ci 894141cc406Sopenharmony_ci for (i = iStart; i >= 0 && i < iWidth; i += iInc) 895141cc406Sopenharmony_ci { 896141cc406Sopenharmony_ci /* collect the red pixels */ 897141cc406Sopenharmony_ci for (c = j = 0; j < iScaleDownDpi; ++j) 898141cc406Sopenharmony_ci c += pabRed[i + j]; 899141cc406Sopenharmony_ci *pabLine = 900141cc406Sopenharmony_ci (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1); 901141cc406Sopenharmony_ci pabLine++; 902141cc406Sopenharmony_ci 903141cc406Sopenharmony_ci /* collect the green pixels */ 904141cc406Sopenharmony_ci for (c = j = 0; j < iScaleDownDpi; ++j) 905141cc406Sopenharmony_ci c += pabGrn[i + iWidth + j]; 906141cc406Sopenharmony_ci *pabLine = 907141cc406Sopenharmony_ci (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1); 908141cc406Sopenharmony_ci pabLine++; 909141cc406Sopenharmony_ci 910141cc406Sopenharmony_ci /* collect the blue pixels */ 911141cc406Sopenharmony_ci for (c = j = 0; j < iScaleDownDpi; ++j) 912141cc406Sopenharmony_ci c += pabBlu[i + 2 * iWidth + j]; 913141cc406Sopenharmony_ci *pabLine = 914141cc406Sopenharmony_ci (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1); 915141cc406Sopenharmony_ci pabLine++; 916141cc406Sopenharmony_ci } 917141cc406Sopenharmony_ci } 918141cc406Sopenharmony_ci 919141cc406Sopenharmony_ci} 920141cc406Sopenharmony_ci 921141cc406Sopenharmony_ci 922141cc406Sopenharmony_ci/* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage, 923141cc406Sopenharmony_ci if fReturn==SANE_TRUE, the head will return automatically on an end of scan */ 924141cc406Sopenharmony_ciSTATIC SANE_Bool 925141cc406Sopenharmony_ciCircBufferGetLineEx (int iHandle, TDataPipe * p, unsigned char *pabLine, 926141cc406Sopenharmony_ci SANE_Bool iReversedHead, SANE_Bool fReturn) 927141cc406Sopenharmony_ci{ 928141cc406Sopenharmony_ci int iLineCount; 929141cc406Sopenharmony_ci for (iLineCount = 0; iLineCount < p->iScaleDownLpi; ++iLineCount) 930141cc406Sopenharmony_ci { 931141cc406Sopenharmony_ci if (iReversedHead) 932141cc406Sopenharmony_ci { 933141cc406Sopenharmony_ci if (!XferBufferGetLine (iHandle, p, 934141cc406Sopenharmony_ci &p->pabCircBuf[p->iRedLine * 935141cc406Sopenharmony_ci p->iBytesPerLine], fReturn)) 936141cc406Sopenharmony_ci return SANE_FALSE; 937141cc406Sopenharmony_ci } 938141cc406Sopenharmony_ci else 939141cc406Sopenharmony_ci { 940141cc406Sopenharmony_ci if (!XferBufferGetLine (iHandle, p, 941141cc406Sopenharmony_ci &p->pabCircBuf[p->iBluLine * 942141cc406Sopenharmony_ci p->iBytesPerLine], fReturn)) 943141cc406Sopenharmony_ci return SANE_FALSE; 944141cc406Sopenharmony_ci } 945141cc406Sopenharmony_ci if (pabLine != NULL) 946141cc406Sopenharmony_ci { 947141cc406Sopenharmony_ci _UnscrambleLine (pabLine, 948141cc406Sopenharmony_ci &p->pabCircBuf[p->iRedLine * p->iBytesPerLine], 949141cc406Sopenharmony_ci &p->pabCircBuf[p->iGrnLine * p->iBytesPerLine], 950141cc406Sopenharmony_ci &p->pabCircBuf[p->iBluLine * p->iBytesPerLine], 951141cc406Sopenharmony_ci p->iWidth * p->iScaleDownDpi, iReversedHead, 952141cc406Sopenharmony_ci p->iScaleDownDpi, iLineCount); 953141cc406Sopenharmony_ci } 954141cc406Sopenharmony_ci 955141cc406Sopenharmony_ci /* advance pointers */ 956141cc406Sopenharmony_ci p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf; 957141cc406Sopenharmony_ci p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf; 958141cc406Sopenharmony_ci p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf; 959141cc406Sopenharmony_ci } 960141cc406Sopenharmony_ci return SANE_TRUE; 961141cc406Sopenharmony_ci} 962141cc406Sopenharmony_ci 963141cc406Sopenharmony_ci 964141cc406Sopenharmony_ci/* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage */ 965141cc406Sopenharmony_ciSTATIC SANE_Bool 966141cc406Sopenharmony_ciCircBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine, 967141cc406Sopenharmony_ci SANE_Bool iReversedHead) 968141cc406Sopenharmony_ci{ 969141cc406Sopenharmony_ci return CircBufferGetLineEx (iHandle, p, pabLine, iReversedHead, SANE_FALSE); 970141cc406Sopenharmony_ci} 971141cc406Sopenharmony_ci 972141cc406Sopenharmony_ci 973141cc406Sopenharmony_ci/* try to keep the number of transfers the same, but make them all 974141cc406Sopenharmony_ci as good as possible the same size to avoid cranking in critical 975141cc406Sopenharmony_ci situations 976141cc406Sopenharmony_ci*/ 977141cc406Sopenharmony_cistatic int 978141cc406Sopenharmony_ci_OptimizeXferSize (int iLines, int iLinesPerXfer) 979141cc406Sopenharmony_ci{ 980141cc406Sopenharmony_ci int iXfers; 981141cc406Sopenharmony_ci iXfers = (iLines + iLinesPerXfer - 1) / iLinesPerXfer; 982141cc406Sopenharmony_ci while (--iLinesPerXfer > 0 983141cc406Sopenharmony_ci && (iLines + iLinesPerXfer - 1) / iLinesPerXfer == iXfers); 984141cc406Sopenharmony_ci return iLinesPerXfer + 1; 985141cc406Sopenharmony_ci} 986141cc406Sopenharmony_ci 987141cc406Sopenharmony_ciSTATIC void 988141cc406Sopenharmony_ciCircBufferInit (int iHandle, TDataPipe * p, 989141cc406Sopenharmony_ci int iWidth, int iHeight, 990141cc406Sopenharmony_ci int iMisAlignment, SANE_Bool iReversedHead, 991141cc406Sopenharmony_ci int iScaleDownDpi, int iScaleDownLpi) 992141cc406Sopenharmony_ci{ 993141cc406Sopenharmony_ci 994141cc406Sopenharmony_ci /* relevant for internal read and write functions */ 995141cc406Sopenharmony_ci p->iScaleDownLpi = iScaleDownLpi; 996141cc406Sopenharmony_ci p->iScaleDownDpi = iScaleDownDpi; 997141cc406Sopenharmony_ci p->iWidth = iWidth; 998141cc406Sopenharmony_ci p->iBytesPerLine = iWidth * iScaleDownDpi * BYTES_PER_PIXEL; 999141cc406Sopenharmony_ci p->iSaneBytesPerLine = iWidth * BYTES_PER_PIXEL; 1000141cc406Sopenharmony_ci if (iMisAlignment == 0) 1001141cc406Sopenharmony_ci { 1002141cc406Sopenharmony_ci p->iLinesPerCircBuf = 1; 1003141cc406Sopenharmony_ci } 1004141cc406Sopenharmony_ci else 1005141cc406Sopenharmony_ci { 1006141cc406Sopenharmony_ci p->iLinesPerCircBuf = 3 * iMisAlignment; 1007141cc406Sopenharmony_ci } 1008141cc406Sopenharmony_ci 1009141cc406Sopenharmony_ci DBG (DBG_MSG, "_iScaleDown (Dpi,Lpi) = (%d,%d)\n", p->iScaleDownDpi, 1010141cc406Sopenharmony_ci p->iScaleDownLpi); 1011141cc406Sopenharmony_ci DBG (DBG_MSG, "_iBytesPerLine = %d\n", p->iBytesPerLine); 1012141cc406Sopenharmony_ci DBG (DBG_MSG, "_iLinesPerCircBuf = %d\n", p->iLinesPerCircBuf); 1013141cc406Sopenharmony_ci p->pabCircBuf = 1014141cc406Sopenharmony_ci (unsigned char *) malloc (p->iBytesPerLine * p->iLinesPerCircBuf); 1015141cc406Sopenharmony_ci if (p->pabCircBuf == NULL) 1016141cc406Sopenharmony_ci { 1017141cc406Sopenharmony_ci DBG (DBG_ERR, 1018141cc406Sopenharmony_ci "Unable to allocate %d unsigned chars for circular buffer\n", 1019141cc406Sopenharmony_ci (int) (p->iBytesPerLine * p->iLinesPerCircBuf)); 1020141cc406Sopenharmony_ci return; 1021141cc406Sopenharmony_ci } 1022141cc406Sopenharmony_ci DBG (DBG_MSG, "Allocated %d unsigned chars for circular buffer\n", 1023141cc406Sopenharmony_ci p->iBytesPerLine * p->iLinesPerCircBuf); 1024141cc406Sopenharmony_ci 1025141cc406Sopenharmony_ci if (iReversedHead) 1026141cc406Sopenharmony_ci { 1027141cc406Sopenharmony_ci p->iBluLine = 0; 1028141cc406Sopenharmony_ci p->iGrnLine = iMisAlignment; 1029141cc406Sopenharmony_ci p->iRedLine = iMisAlignment * 2; 1030141cc406Sopenharmony_ci } 1031141cc406Sopenharmony_ci else 1032141cc406Sopenharmony_ci { 1033141cc406Sopenharmony_ci p->iRedLine = 0; 1034141cc406Sopenharmony_ci p->iGrnLine = iMisAlignment; 1035141cc406Sopenharmony_ci p->iBluLine = iMisAlignment * 2; 1036141cc406Sopenharmony_ci } 1037141cc406Sopenharmony_ci 1038141cc406Sopenharmony_ci /* negative height is an indication for "no Check" */ 1039141cc406Sopenharmony_ci if (iHeight < 0) 1040141cc406Sopenharmony_ci { 1041141cc406Sopenharmony_ci p->iLinesLeft = -1; 1042141cc406Sopenharmony_ci p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine; 1043141cc406Sopenharmony_ci DBG (DBG_MSG, "using unchecked XFER_BUF_SIZE\n"); 1044141cc406Sopenharmony_ci DBG (DBG_MSG, "_iXFerSize = %d\n", 1045141cc406Sopenharmony_ci p->iBytesPerLine * p->iLinesPerXferBuf); 1046141cc406Sopenharmony_ci } 1047141cc406Sopenharmony_ci else 1048141cc406Sopenharmony_ci { 1049141cc406Sopenharmony_ci#define SAFETY_LINES 0 1050141cc406Sopenharmony_ci#define MAX_LINES_PER_XFERBUF 800 1051141cc406Sopenharmony_ci /* estimate of number of unsigned chars to transfer at all via the USB */ 1052141cc406Sopenharmony_ci /* add some lines for security */ 1053141cc406Sopenharmony_ci 1054141cc406Sopenharmony_ci p->iLinesLeft = 1055141cc406Sopenharmony_ci iHeight + p->iSkipLines + p->iLinesPerCircBuf + SAFETY_LINES; 1056141cc406Sopenharmony_ci p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine; 1057141cc406Sopenharmony_ci /* with more than 800 lines the timing is spoiled */ 1058141cc406Sopenharmony_ci if (p->iLinesPerXferBuf > MAX_LINES_PER_XFERBUF) 1059141cc406Sopenharmony_ci { 1060141cc406Sopenharmony_ci p->iLinesPerXferBuf = MAX_LINES_PER_XFERBUF; 1061141cc406Sopenharmony_ci } 1062141cc406Sopenharmony_ci /* final optimization to keep critical scans smooth */ 1063141cc406Sopenharmony_ci p->iLinesPerXferBuf = 1064141cc406Sopenharmony_ci _OptimizeXferSize (p->iLinesLeft, p->iLinesPerXferBuf); 1065141cc406Sopenharmony_ci 1066141cc406Sopenharmony_ci DBG (DBG_MSG, "_iXFerSize = %d for %d transfer(s)\n", 1067141cc406Sopenharmony_ci (int) p->iLinesPerXferBuf * p->iBytesPerLine, 1068141cc406Sopenharmony_ci (p->iLinesLeft + p->iLinesPerXferBuf - 1) / p->iLinesPerXferBuf); 1069141cc406Sopenharmony_ci } 1070141cc406Sopenharmony_ci DBG (DBG_MSG, "_iLinesPerXferBuf = %d\n", p->iLinesPerXferBuf); 1071141cc406Sopenharmony_ci 1072141cc406Sopenharmony_ci /* init transfer buffer */ 1073141cc406Sopenharmony_ci XferBufferInit (iHandle, p); 1074141cc406Sopenharmony_ci 1075141cc406Sopenharmony_ci /* fill circular buffer */ 1076141cc406Sopenharmony_ci CircBufferFill (iHandle, p, iReversedHead); 1077141cc406Sopenharmony_ci} 1078141cc406Sopenharmony_ci 1079141cc406Sopenharmony_ci 1080141cc406Sopenharmony_ciSTATIC void 1081141cc406Sopenharmony_ciCircBufferExit (TDataPipe * p) 1082141cc406Sopenharmony_ci{ 1083141cc406Sopenharmony_ci XferBufferExit (p); 1084141cc406Sopenharmony_ci if (p->pabCircBuf != NULL) 1085141cc406Sopenharmony_ci { 1086141cc406Sopenharmony_ci DBG (DBG_MSG, "\n"); 1087141cc406Sopenharmony_ci free (p->pabCircBuf); 1088141cc406Sopenharmony_ci p->pabCircBuf = NULL; 1089141cc406Sopenharmony_ci } 1090141cc406Sopenharmony_ci else 1091141cc406Sopenharmony_ci { 1092141cc406Sopenharmony_ci DBG (DBG_ERR, "CircBufferExit: Circular buffer not initialised!\n"); 1093141cc406Sopenharmony_ci } 1094141cc406Sopenharmony_ci} 1095141cc406Sopenharmony_ci 1096141cc406Sopenharmony_ci 1097141cc406Sopenharmony_ci/************************************************************************/ 1098141cc406Sopenharmony_ci 1099141cc406Sopenharmony_ci 1100141cc406Sopenharmony_ci 1101141cc406Sopenharmony_cistatic int 1102141cc406Sopenharmony_ci_CalcAvg (unsigned char *pabBuf, int n, int iStep) 1103141cc406Sopenharmony_ci{ 1104141cc406Sopenharmony_ci int i, j, x; 1105141cc406Sopenharmony_ci 1106141cc406Sopenharmony_ci for (i = j = x = 0; i < n; i++) 1107141cc406Sopenharmony_ci { 1108141cc406Sopenharmony_ci x += pabBuf[j]; 1109141cc406Sopenharmony_ci j += iStep; 1110141cc406Sopenharmony_ci } 1111141cc406Sopenharmony_ci return (x / n); 1112141cc406Sopenharmony_ci} 1113141cc406Sopenharmony_ci 1114141cc406Sopenharmony_ci 1115141cc406Sopenharmony_ci/* converts white line data and black point data into a calibration table */ 1116141cc406Sopenharmony_cistatic void 1117141cc406Sopenharmony_ciCreateCalibTable (unsigned char *abWhite, unsigned char bBlackR, 1118141cc406Sopenharmony_ci unsigned char bBlackG, unsigned char bBlackB, 1119141cc406Sopenharmony_ci int iReversedHead, unsigned char *pabCalibTable) 1120141cc406Sopenharmony_ci{ 1121141cc406Sopenharmony_ci int i, j, iGain, iOffset, iData; 1122141cc406Sopenharmony_ci unsigned char *pabPixel; 1123141cc406Sopenharmony_ci 1124141cc406Sopenharmony_ci j = 0; 1125141cc406Sopenharmony_ci for (i = 0; i < HW_PIXELS; i++) 1126141cc406Sopenharmony_ci { 1127141cc406Sopenharmony_ci if (iReversedHead) 1128141cc406Sopenharmony_ci { 1129141cc406Sopenharmony_ci pabPixel = &abWhite[(HW_PIXELS - i - 1) * 3]; 1130141cc406Sopenharmony_ci } 1131141cc406Sopenharmony_ci else 1132141cc406Sopenharmony_ci { 1133141cc406Sopenharmony_ci pabPixel = &abWhite[i * 3]; 1134141cc406Sopenharmony_ci } 1135141cc406Sopenharmony_ci /* red */ 1136141cc406Sopenharmony_ci if (bBlackR > 16) 1137141cc406Sopenharmony_ci bBlackR = 16; 1138141cc406Sopenharmony_ci iGain = 65536 / MAX (1, pabPixel[0] - bBlackR); 1139141cc406Sopenharmony_ci iOffset = bBlackR * 4; 1140141cc406Sopenharmony_ci if (iOffset > 63) 1141141cc406Sopenharmony_ci iOffset = 63; 1142141cc406Sopenharmony_ci iData = (iGain << 6) + iOffset; 1143141cc406Sopenharmony_ci pabCalibTable[j++] = (iData) & 255; 1144141cc406Sopenharmony_ci pabCalibTable[j++] = (iData >> 8) & 255; 1145141cc406Sopenharmony_ci /* green */ 1146141cc406Sopenharmony_ci if (bBlackG > 16) 1147141cc406Sopenharmony_ci bBlackG = 16; 1148141cc406Sopenharmony_ci iGain = 65536 / MAX (1, pabPixel[1] - bBlackG); 1149141cc406Sopenharmony_ci iOffset = bBlackG * 4; 1150141cc406Sopenharmony_ci if (iOffset > 63) 1151141cc406Sopenharmony_ci iOffset = 63; 1152141cc406Sopenharmony_ci iData = (iGain << 6) + iOffset; 1153141cc406Sopenharmony_ci pabCalibTable[j++] = (iData) & 255; 1154141cc406Sopenharmony_ci pabCalibTable[j++] = (iData >> 8) & 255; 1155141cc406Sopenharmony_ci /* blue */ 1156141cc406Sopenharmony_ci if (bBlackB > 16) 1157141cc406Sopenharmony_ci bBlackB = 16; 1158141cc406Sopenharmony_ci iGain = 65536 / MAX (1, pabPixel[2] - bBlackB); 1159141cc406Sopenharmony_ci iOffset = bBlackB * 4; 1160141cc406Sopenharmony_ci if (iOffset > 63) 1161141cc406Sopenharmony_ci iOffset = 63; 1162141cc406Sopenharmony_ci iData = (iGain << 6) + iOffset; 1163141cc406Sopenharmony_ci pabCalibTable[j++] = (iData) & 255; 1164141cc406Sopenharmony_ci pabCalibTable[j++] = (iData >> 8) & 255; 1165141cc406Sopenharmony_ci } 1166141cc406Sopenharmony_ci} 1167141cc406Sopenharmony_ci 1168141cc406Sopenharmony_ci 1169141cc406Sopenharmony_ci/************************************************************************* 1170141cc406Sopenharmony_ci Lamp control functions 1171141cc406Sopenharmony_ci*************************************************************************/ 1172141cc406Sopenharmony_ciSTATIC SANE_Bool 1173141cc406Sopenharmony_ciGetLamp (THWParams * pHWParams, SANE_Bool * pfLampIsOn) 1174141cc406Sopenharmony_ci{ 1175141cc406Sopenharmony_ci unsigned char bData; 1176141cc406Sopenharmony_ci 1177141cc406Sopenharmony_ci NiashReadReg (pHWParams->iXferHandle, 0x03, &bData); 1178141cc406Sopenharmony_ci *pfLampIsOn = ((bData & 0x01) != 0); 1179141cc406Sopenharmony_ci return SANE_TRUE; 1180141cc406Sopenharmony_ci} 1181141cc406Sopenharmony_ci 1182141cc406Sopenharmony_ci 1183141cc406Sopenharmony_ciSTATIC SANE_Bool 1184141cc406Sopenharmony_ciSetLamp (THWParams * pHWParams, SANE_Bool fLampOn) 1185141cc406Sopenharmony_ci{ 1186141cc406Sopenharmony_ci unsigned char bData; 1187141cc406Sopenharmony_ci int iHandle; 1188141cc406Sopenharmony_ci 1189141cc406Sopenharmony_ci iHandle = pHWParams->iXferHandle; 1190141cc406Sopenharmony_ci 1191141cc406Sopenharmony_ci NiashReadReg (iHandle, 0x03, &bData); 1192141cc406Sopenharmony_ci if (fLampOn) 1193141cc406Sopenharmony_ci { 1194141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, bData | 0x01); 1195141cc406Sopenharmony_ci } 1196141cc406Sopenharmony_ci else 1197141cc406Sopenharmony_ci { 1198141cc406Sopenharmony_ci NiashWriteReg (iHandle, 0x03, bData & ~0x01); 1199141cc406Sopenharmony_ci } 1200141cc406Sopenharmony_ci return SANE_TRUE; 1201141cc406Sopenharmony_ci} 1202141cc406Sopenharmony_ci 1203141cc406Sopenharmony_ci 1204141cc406Sopenharmony_ci/************************************************************************* 1205141cc406Sopenharmony_ci Experimental simple calibration, but also returning the white levels 1206141cc406Sopenharmony_ci*************************************************************************/ 1207141cc406Sopenharmony_ciSTATIC SANE_Bool 1208141cc406Sopenharmony_ciSimpleCalibExt (THWParams * pHWPar, unsigned char *pabCalibTable, 1209141cc406Sopenharmony_ci unsigned char *pabCalWhite) 1210141cc406Sopenharmony_ci{ 1211141cc406Sopenharmony_ci unsigned char bMinR, bMinG, bMinB; 1212141cc406Sopenharmony_ci TDataPipe DataPipe; 1213141cc406Sopenharmony_ci TScanParams Params; 1214141cc406Sopenharmony_ci unsigned char abGamma[4096]; 1215141cc406Sopenharmony_ci int i, j; 1216141cc406Sopenharmony_ci static unsigned char abBuf[HW_PIXELS * 3 * 71]; /* Careful : see startWhite and endWhite below */ 1217141cc406Sopenharmony_ci static unsigned char abLine[HW_PIXELS * 3]; 1218141cc406Sopenharmony_ci static unsigned char abWhite[HW_PIXELS * 3]; 1219141cc406Sopenharmony_ci unsigned char *pabWhite; 1220141cc406Sopenharmony_ci int iWhiteR, iWhiteG, iWhiteB; 1221141cc406Sopenharmony_ci int iHandle; 1222141cc406Sopenharmony_ci SANE_Bool iReversedHead; 1223141cc406Sopenharmony_ci int startWhiteY, endWhiteY; 1224141cc406Sopenharmony_ci int startBlackY, endBlackY; 1225141cc406Sopenharmony_ci int endBlackX; 1226141cc406Sopenharmony_ci 1227141cc406Sopenharmony_ci iHandle = pHWPar->iXferHandle; 1228141cc406Sopenharmony_ci iReversedHead = pHWPar->iReversedHead; 1229141cc406Sopenharmony_ci 1230141cc406Sopenharmony_ci DataPipe.iSkipLines = pHWPar->iSkipLines; 1231141cc406Sopenharmony_ci 1232141cc406Sopenharmony_ci Params.iDpi = HW_DPI; 1233141cc406Sopenharmony_ci Params.iLpi = HW_DPI; 1234141cc406Sopenharmony_ci if (iReversedHead) /* hp scanners */ 1235141cc406Sopenharmony_ci Params.iTop = 60; 1236141cc406Sopenharmony_ci else /* agfa scanners */ 1237141cc406Sopenharmony_ci Params.iTop = 30; 1238141cc406Sopenharmony_ci Params.iBottom = HP3300C_BOTTOM; 1239141cc406Sopenharmony_ci Params.iLeft = 0; 1240141cc406Sopenharmony_ci Params.iWidth = HW_PIXELS; 1241141cc406Sopenharmony_ci Params.iHeight = 54; 1242141cc406Sopenharmony_ci Params.fCalib = SANE_TRUE; 1243141cc406Sopenharmony_ci 1244141cc406Sopenharmony_ci /* write gamma table with neutral gain / offset */ 1245141cc406Sopenharmony_ci CalcGamma (abGamma, 1.0); 1246141cc406Sopenharmony_ci WriteGammaCalibTable (abGamma, abGamma, abGamma, NULL, 256, 0, pHWPar); 1247141cc406Sopenharmony_ci 1248141cc406Sopenharmony_ci if (!InitScan (&Params, pHWPar)) 1249141cc406Sopenharmony_ci { 1250141cc406Sopenharmony_ci if (pabCalWhite) 1251141cc406Sopenharmony_ci pabCalWhite[0] = pabCalWhite[1] = pabCalWhite[2] = 0; 1252141cc406Sopenharmony_ci return SANE_FALSE; 1253141cc406Sopenharmony_ci } 1254141cc406Sopenharmony_ci 1255141cc406Sopenharmony_ci /* Definition of white and black areas */ 1256141cc406Sopenharmony_ci if (iReversedHead) 1257141cc406Sopenharmony_ci { /* hp scanners */ 1258141cc406Sopenharmony_ci startWhiteY = 0; 1259141cc406Sopenharmony_ci endWhiteY = 15; 1260141cc406Sopenharmony_ci startBlackY = 16; 1261141cc406Sopenharmony_ci endBlackY = 135; 1262141cc406Sopenharmony_ci endBlackX = HW_PIXELS; 1263141cc406Sopenharmony_ci } 1264141cc406Sopenharmony_ci else 1265141cc406Sopenharmony_ci { /* agfa scanners */ 1266141cc406Sopenharmony_ci startWhiteY = 0; 1267141cc406Sopenharmony_ci endWhiteY = 70; 1268141cc406Sopenharmony_ci startBlackY = 86; 1269141cc406Sopenharmony_ci endBlackY = 135; 1270141cc406Sopenharmony_ci endBlackX = 3374; 1271141cc406Sopenharmony_ci } 1272141cc406Sopenharmony_ci 1273141cc406Sopenharmony_ci CircBufferInit (iHandle, &DataPipe, HW_PIXELS, -1, Params.iLpi / 150, 1274141cc406Sopenharmony_ci iReversedHead, 1, 1); 1275141cc406Sopenharmony_ci /* white level */ 1276141cc406Sopenharmony_ci /* skip some lines */ 1277141cc406Sopenharmony_ci for (i = 0; i < startWhiteY; i++) 1278141cc406Sopenharmony_ci { 1279141cc406Sopenharmony_ci CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead); 1280141cc406Sopenharmony_ci } 1281141cc406Sopenharmony_ci /* Get white lines */ 1282141cc406Sopenharmony_ci for (i = 0; i < endWhiteY - startWhiteY + 1; i++) 1283141cc406Sopenharmony_ci { 1284141cc406Sopenharmony_ci CircBufferGetLine (iHandle, &DataPipe, &abBuf[i * HW_PIXELS * 3], 1285141cc406Sopenharmony_ci iReversedHead); 1286141cc406Sopenharmony_ci } 1287141cc406Sopenharmony_ci /* black level */ 1288141cc406Sopenharmony_ci bMinR = 255; 1289141cc406Sopenharmony_ci bMinG = 255; 1290141cc406Sopenharmony_ci bMinB = 255; 1291141cc406Sopenharmony_ci /* Skip some lines */ 1292141cc406Sopenharmony_ci for (i = 0; i < startBlackY; i++) 1293141cc406Sopenharmony_ci { 1294141cc406Sopenharmony_ci CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead); 1295141cc406Sopenharmony_ci } 1296141cc406Sopenharmony_ci for (i = 0; i < endBlackY - startBlackY + 1; i++) 1297141cc406Sopenharmony_ci { 1298141cc406Sopenharmony_ci CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead); 1299141cc406Sopenharmony_ci for (j = 0; j < endBlackX; j++) 1300141cc406Sopenharmony_ci { 1301141cc406Sopenharmony_ci bMinR = MIN (abLine[j * 3 + 0], bMinR); 1302141cc406Sopenharmony_ci bMinG = MIN (abLine[j * 3 + 1], bMinG); 1303141cc406Sopenharmony_ci bMinB = MIN (abLine[j * 3 + 2], bMinB); 1304141cc406Sopenharmony_ci } 1305141cc406Sopenharmony_ci } 1306141cc406Sopenharmony_ci CircBufferExit (&DataPipe); 1307141cc406Sopenharmony_ci FinishScan (pHWPar); 1308141cc406Sopenharmony_ci 1309141cc406Sopenharmony_ci /* calc average white level */ 1310141cc406Sopenharmony_ci pabWhite = abBuf; 1311141cc406Sopenharmony_ci for (i = 0; i < HW_PIXELS; i++) 1312141cc406Sopenharmony_ci { 1313141cc406Sopenharmony_ci abWhite[i * 3 + 0] = 1314141cc406Sopenharmony_ci _CalcAvg (&pabWhite[i * 3 + 0], endWhiteY - startWhiteY + 1, 1315141cc406Sopenharmony_ci HW_PIXELS * 3); 1316141cc406Sopenharmony_ci abWhite[i * 3 + 1] = 1317141cc406Sopenharmony_ci _CalcAvg (&pabWhite[i * 3 + 1], endWhiteY - startWhiteY + 1, 1318141cc406Sopenharmony_ci HW_PIXELS * 3); 1319141cc406Sopenharmony_ci abWhite[i * 3 + 2] = 1320141cc406Sopenharmony_ci _CalcAvg (&pabWhite[i * 3 + 2], endWhiteY - startWhiteY + 1, 1321141cc406Sopenharmony_ci HW_PIXELS * 3); 1322141cc406Sopenharmony_ci } 1323141cc406Sopenharmony_ci iWhiteR = _CalcAvg (&abWhite[0], HW_PIXELS, 3); 1324141cc406Sopenharmony_ci iWhiteG = _CalcAvg (&abWhite[1], HW_PIXELS, 3); 1325141cc406Sopenharmony_ci iWhiteB = _CalcAvg (&abWhite[2], HW_PIXELS, 3); 1326141cc406Sopenharmony_ci 1327141cc406Sopenharmony_ci DBG (DBG_MSG, "Black level (%d,%d,%d), White level (%d,%d,%d)\n", 1328141cc406Sopenharmony_ci (int) bMinR, (int) bMinG, (int) bMinB, iWhiteR, iWhiteG, iWhiteB); 1329141cc406Sopenharmony_ci 1330141cc406Sopenharmony_ci /* convert the white line and black point into a calibration table */ 1331141cc406Sopenharmony_ci CreateCalibTable (abWhite, bMinR, bMinG, bMinB, iReversedHead, 1332141cc406Sopenharmony_ci pabCalibTable); 1333141cc406Sopenharmony_ci /* assign the White Levels */ 1334141cc406Sopenharmony_ci if (pabCalWhite) 1335141cc406Sopenharmony_ci { 1336141cc406Sopenharmony_ci pabCalWhite[0] = iWhiteR; 1337141cc406Sopenharmony_ci pabCalWhite[1] = iWhiteG; 1338141cc406Sopenharmony_ci pabCalWhite[2] = iWhiteB; 1339141cc406Sopenharmony_ci } 1340141cc406Sopenharmony_ci return SANE_TRUE; 1341141cc406Sopenharmony_ci} 1342141cc406Sopenharmony_ci 1343141cc406Sopenharmony_ci 1344141cc406Sopenharmony_ci/************************************************************************* 1345141cc406Sopenharmony_ci FinishScan 1346141cc406Sopenharmony_ci ========== 1347141cc406Sopenharmony_ci Finishes the scan. Makes the scanner head move back to the home position. 1348141cc406Sopenharmony_ci 1349141cc406Sopenharmony_ci*************************************************************************/ 1350141cc406Sopenharmony_ciSTATIC void 1351141cc406Sopenharmony_ciFinishScan (THWParams * pHWParams) 1352141cc406Sopenharmony_ci{ 1353141cc406Sopenharmony_ci NiashWriteReg (pHWParams->iXferHandle, 0x02, 0x80); 1354141cc406Sopenharmony_ci} 1355