1 /** @file u12-if.c
2  *  @brief The interface functions to the U12 backend stuff.
3  *
4  * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
5  *
6  * History:
7  * - 0.01 - initial version
8  * - 0.02 - added model tweaking
9  *        - fixed autodetection bug
10  *        - added line-scaling stuff
11  *        - removed u12if_setScanEnv()
12  * .
13  * <hr>
14  * This file is part of the SANE package.
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License as
18  * published by the Free Software Foundation; either version 2 of the
19  * License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful, but
22  * WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
28  *
29  * As a special exception, the authors of SANE give permission for
30  * additional uses of the libraries contained in this release of SANE.
31  *
32  * The exception is that, if you link a SANE library with other files
33  * to produce an executable, this does not by itself cause the
34  * resulting executable to be covered by the GNU General Public
35  * License.  Your use of that executable is in no way restricted on
36  * account of linking the SANE library code into it.
37  *
38  * This exception does not, however, invalidate any other reasons why
39  * the executable file might be covered by the GNU General Public
40  * License.
41  *
42  * If you submit changes to SANE to the maintainers to be included in
43  * a subsequent release, you agree by submitting the changes that
44  * those changes may be distributed with this exception intact.
45  *
46  * If you write modifications of your own for SANE, it is your choice
47  * whether to permit this exception to apply to your modifications.
48  * If you do not wish that, delete this exception notice.
49  * <hr>
50  */
51 
52 #define _DEF_BRIGHTEST_SKIP 3
53 #define _DEF_DARKEST_SKIP   5
54 
55 /** useful for description tables
56  */
57 typedef struct {
58 	int	  id;
59 	char *desc;
60 } TabDef;
61 
62 typedef struct {
63 	char *vp;
64 	char *name;
65 } DevDesc;
66 
67 #define _PLUSTEK_VENID 0x07B3
68 #define _KYE_VENID     0x0458
69 
70 /** to allow different vendors...
71  */
72 static TabDef u12Vendors[] = {
73 
74 	{ _PLUSTEK_VENID, "Plustek"    },
75 	{ _KYE_VENID,     "KYE/Genius" },
76 	{ 0xFFFF,         NULL         }
77 };
78 
79 /** list of supported devices
80  */
81 static DevDesc u12Devices[] = {
82 	{ "0x07B3-0x0001", "1212U/U12"     },
83 	{ "0x0458-0x2004", "Colorpage HR6" },
84 	{ NULL,            NULL }
85 };
86 
87 /** for autodetection */
88 static SANE_Char USB_devname[1024];
89 
90 /********************** the USB scanner interface ****************************/
91 
92 /**
93  */
u12_initDev( U12_Device *dev, int handle, int vendor )94 static SANE_Status u12_initDev( U12_Device *dev, int handle, int vendor )
95 {
96 	int         i;
97 	SANE_Status res;
98 	TimerDef    timer;
99 
100 	/* well now we patch the vendor string...
101 	 * if not found, the default vendor will be Plustek
102 	 */
103 	for( i = 0; u12Vendors[i].desc != NULL; i++ ) {
104 
105 		if( u12Vendors[i].id == vendor ) {
106 			dev->sane.vendor = u12Vendors[i].desc;
107 			DBG( _DBG_INFO, "Vendor adjusted to: >%s<\n", dev->sane.vendor );
108 			break;
109 		}
110 	}
111 	dev->fd = handle;
112 
113 	dev->adj.upNormal   = 0;
114 	dev->adj.upNegative = 20;
115 	dev->adj.upPositive = -30;
116 	dev->adj.leftNormal = 51;
117 
118 	res = SANE_STATUS_IO_ERROR;
119 	if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {
120 
121 		u12motor_PositionModuleToHome( dev );
122 
123 		u12io_StartTimer( &timer, _SECOND * 20);
124 		do {
125 			if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER) {
126 				res = SANE_STATUS_GOOD;
127 				break;
128 			}
129 		} while( !u12io_CheckTimer( &timer ));
130 	} else {
131 		res = u12hw_InitAsic( dev, SANE_FALSE );
132 	}
133 
134 	if( res == SANE_STATUS_GOOD )
135 		u12hw_PutToIdleMode( dev );
136 	return res;
137 }
138 
139 /** will be called upon sane_exit
140  */
u12if_shutdown( U12_Device *dev )141 static void u12if_shutdown( U12_Device *dev  )
142 {
143 	SANE_Int handle;
144 	TimerDef timer;
145 
146 	DBG( _DBG_INFO, "Shutdown called (dev->fd=%d, %s)\n",
147 													dev->fd, dev->sane.name );
148 	if( SANE_STATUS_GOOD == sanei_usb_open( dev->sane.name, &handle )) {
149 
150     	dev->fd = handle;
151 		u12io_OpenScanPath( dev );
152 
153 		u12hw_PutToIdleMode( dev );
154 
155 		if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {
156 
157 			u12motor_PositionModuleToHome( dev );
158 
159 			u12io_StartTimer( &timer, _SECOND * 20);
160 			do {
161 				if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER) {
162 					break;
163 				}
164 			} while( !u12io_CheckTimer( &timer ));
165 		}
166 		DBG( _DBG_INFO, "* Home position reached.\n" );
167 
168 		if( 0 != dev->adj.lampOffOnEnd ) {
169 
170 			DBG( _DBG_INFO, "* Switching lamp off...\n" );
171 			dev->regs.RD_ScanControl &= ~_SCAN_LAMPS_ON;
172 			u12io_DataToRegister(dev,REG_SCANCONTROL, dev->regs.RD_ScanControl );
173 		}
174 
175 		u12io_CloseScanPath( dev );
176 		dev->fd = -1;
177 		sanei_usb_close( handle );
178 	}
179 
180 #if 0
181 	usb_StopLampTimer( dev );
182 #endif
183 	DBG( _DBG_INFO, "Shutdown done.\n" );
184 }
185 
186 /** This function checks whether a device, described by a given
187  * string(vendor and product ID), is supported by this backend or not
188  *
189  * @param usbIdStr - string consisting out of product and vendor ID
190  *                   format: "0xVVVV-0xPPPP" VVVV = Vendor ID, PPP = Product ID
191  * @returns; SANE_TRUE if supported, SANE_FALSE if not
192  */
u12if_IsDeviceSupported( U12_Device *dev )193 static SANE_Bool u12if_IsDeviceSupported( U12_Device *dev )
194 {
195 	int i;
196 
197 	for( i = 0; NULL != u12Devices[i].name; i++ ) {
198 
199 		if( !strcmp( dev->usbId, u12Devices[i].vp )) {
200 			dev->sane.model = u12Devices[i].name;
201 			return SANE_TRUE;
202 		}
203 	}
204 
205 	return SANE_FALSE;
206 }
207 
208 /**
209  */
u12if_usbattach( SANE_String_Const dev_name )210 static SANE_Status u12if_usbattach( SANE_String_Const dev_name )
211 {
212 	if( USB_devname[0] == '\0' ) {
213 		DBG( _DBG_INFO, "Found device at >%s<\n", dev_name );
214 		strncpy( USB_devname, dev_name, 1023 );
215 		USB_devname[1023] = '\0';
216 	} else {
217 		DBG( _DBG_INFO, "Device >%s< ignoring\n", dev_name );
218 	}
219 
220 	return SANE_STATUS_GOOD;
221 }
222 
223 #ifndef _FAKE_DEVICE
224 /** here we roam through our list of supported devices and
225  * cross check with the ones the system reports...
226  * @param vendor  - pointer to receive vendor ID
227  * @param product - pointer to receive product ID
228  * @return SANE_TRUE if a matching device has been found or
229  *         SANE_FALSE if nothing supported found...
230  */
usbDev_autodetect( SANE_Word *vendor, SANE_Word *product )231 static SANE_Bool usbDev_autodetect( SANE_Word *vendor, SANE_Word *product )
232 {
233 	int       i;
234 	SANE_Word p, v;
235 
236 	DBG( _DBG_INFO, "Autodetection...\n" );
237 
238 	for( i = 0; NULL != u12Devices[i].name; i++ ) {
239 
240 		v = strtol( &(u12Devices[i].vp)[0], 0, 0 );
241 		p = strtol( &(u12Devices[i].vp)[7], 0, 0 );
242 		DBG( _DBG_INFO, "* checking for 0x%04x-0x%04x\n", v, p );
243 
244 		sanei_usb_find_devices( v, p, u12if_usbattach );
245 
246 		if( USB_devname[0] != '\0' ) {
247 
248 			*vendor  = v;
249 			*product = p;
250 			DBG( _DBG_INFO, "* using device >%s<\n", USB_devname );
251 			return SANE_TRUE;
252 		}
253 	}
254 
255 	return SANE_FALSE;
256 }
257 #endif
258 
259 /**
260  */
u12if_open( U12_Device *dev )261 static int u12if_open( U12_Device *dev )
262 {
263 	char      devStr[50];
264 	int       result;
265 	SANE_Int  handle;
266 	SANE_Word vendor, product;
267 	SANE_Bool was_empty;
268 
269 	DBG( _DBG_INFO, "u12if_open(%s,%s)\n", dev->name, dev->usbId );
270 
271 	USB_devname[0] = '\0';
272 
273 #ifdef _FAKE_DEVICE
274 	dev->name      = strdup( "auto" );
275 	dev->sane.name = dev->name;
276 	was_empty      = SANE_FALSE;
277 
278 	result = SANE_STATUS_UNSUPPORTED;
279 #else
280 	if( !strcmp( dev->name, "auto" )) {
281 
282 		if( dev->usbId[0] == '\0' ) {
283 
284 			if( !usbDev_autodetect( &vendor, &product )) {
285 				DBG( _DBG_ERROR, "No supported device found!\n" );
286 				return -1;
287 			}
288 
289 		} else {
290 
291 			vendor  = strtol( &dev->usbId[0], 0, 0 );
292 			product = strtol( &dev->usbId[7], 0, 0 );
293 
294 			sanei_usb_find_devices( vendor, product, u12if_usbattach );
295 
296 			if( USB_devname[0] == '\0' ) {
297 				DBG( _DBG_ERROR, "No matching device found!\n" );
298         		return -1;
299 			}
300 		}
301 
302 		if( SANE_STATUS_GOOD != sanei_usb_open( USB_devname, &handle )) {
303 			return -1;
304 		}
305 
306 		/* replace the old devname, so we are able to have multiple
307          * auto-detected devices
308          */
309 		free( dev->name );
310 		dev->name      = strdup( USB_devname );
311 		dev->sane.name = dev->name;
312 
313 	} else {
314 
315 		if( SANE_STATUS_GOOD != sanei_usb_open( dev->name, &handle ))
316     		return -1;
317 	}
318 	was_empty = SANE_FALSE;
319 
320 	result = sanei_usb_get_vendor_product( handle, &vendor, &product );
321 #endif
322 
323 	if( SANE_STATUS_GOOD == result ) {
324 
325 		sprintf( devStr, "0x%04X-0x%04X", vendor, product );
326 
327 		DBG(_DBG_INFO,"Vendor ID=0x%04X, Product ID=0x%04X\n",vendor,product);
328 
329 		if( dev->usbId[0] != '\0' ) {
330 
331 			if( 0 != strcmp( dev->usbId, devStr )) {
332 				DBG( _DBG_ERROR, "Specified Vendor and Product ID "
333 								 "doesn't match with the ones\n"
334 								 "in the config file\n" );
335 				sanei_usb_close( handle );
336 		        return -1;
337 			}
338 		} else {
339 			sprintf( dev->usbId, "0x%04X-0x%04X", vendor, product );
340 			was_empty = SANE_TRUE;
341 		}
342 
343 	} else {
344 
345 		DBG( _DBG_INFO, "Can't get vendor & product ID from driver...\n" );
346 
347 		/* if the ioctl stuff is not supported by the kernel and we have
348 		 * nothing specified, we have to give up...
349 		*/
350 		if( dev->usbId[0] == '\0' ) {
351 			DBG( _DBG_ERROR, "Cannot autodetect Vendor an Product ID, "
352 							 "please specify in config file.\n" );
353 			sanei_usb_close( handle );
354 			return -1;
355 		}
356 
357 		vendor  = strtol( &dev->usbId[0], 0, 0 );
358 		product = strtol( &dev->usbId[7], 0, 0 );
359 		DBG( _DBG_INFO, "... using the specified: "
360 		                "0x%04X-0x%04X\n", vendor, product );
361 	}
362 
363 	/* before accessing the scanner, check if supported!
364 	 */
365 	if( !u12if_IsDeviceSupported( dev )) {
366 		DBG( _DBG_ERROR, "Device >%s<, is not supported!\n", dev->usbId );
367 		sanei_usb_close( handle );
368 		return -1;
369 	}
370 
371 	dev->mode = _PP_MODE_SPP;
372 	dev->fd   = handle;
373 
374 	/* is it accessible ? */
375 	if( SANE_STATUS_GOOD != u12hw_CheckDevice( dev )) {
376 		dev->fd = -1;
377 		sanei_usb_close( handle );
378 		return -1;
379 	}
380 
381 	DBG( _DBG_INFO, "Detected vendor & product ID: "
382 		                "0x%04X-0x%04X\n", vendor, product );
383 
384 	if( was_empty )
385 		dev->usbId[0] = '\0';
386 
387 	/* now initialize the device */
388 	if( SANE_STATUS_GOOD != u12_initDev( dev, handle, vendor )) {
389 		dev->fd = -1;
390 		sanei_usb_close( handle );
391 		return -1;
392 	}
393 
394 	if( _PLUSTEK_VENID == vendor ) {
395 		if( dev->Tpa )
396 			dev->sane.model = "UT12";
397 	}
398 
399 	dev->initialized = SANE_TRUE;
400 	return handle;
401 }
402 
403 /**
404  */
u12if_close( U12_Device *dev )405 static int u12if_close( U12_Device *dev )
406 {
407 	DBG( _DBG_INFO, "u12if_close()\n" );
408 	u12io_CloseScanPath( dev );
409 	sanei_usb_close( dev->fd );
410 	dev->fd = -1;
411     return 0;
412 }
413 
414 /**
415  */
u12if_getCaps( U12_Device *dev )416 static SANE_Status u12if_getCaps( U12_Device *dev )
417 {
418 	int cntr;
419 	int res_x = 600 ; /*dev->caps.OpticDpi.x */
420 	DBG( _DBG_INFO, "u12if_getCaps()\n" );
421 
422 /* FIXME: set dpi_range.max, max_x & max_y on a per model base */
423 	dev->dpi_max_x       = 600;
424 	dev->dpi_max_y       = 1200;
425 
426 	/* A4 devices */
427 	dev->max_x = 8.5     * (double)_MM_PER_INCH;
428 	dev->max_y = 11.6934 * (double)_MM_PER_INCH;
429 
430 	/* limit the range */
431 	dev->dpi_range.min   = _DEF_DPI;
432 	dev->dpi_range.max   = dev->dpi_max_y;
433 	dev->dpi_range.quant = 0;
434 	dev->x_range.min 	 = 0;
435 	dev->x_range.max 	 = SANE_FIX(dev->max_x);
436 	dev->x_range.quant 	 = 0;
437 	dev->y_range.min 	 = 0;
438 	dev->y_range.max 	 = SANE_FIX(dev->max_y);
439 	dev->y_range.quant 	 = 0;
440 
441 	/* calculate the size of the resolution list +
442 	 * one more to avoid a buffer overflow, then allocate it...
443 	 */
444 	dev->res_list = (SANE_Int *)
445 					calloc((((res_x * 16)-_DEF_DPI)/25+1),
446 						sizeof (SANE_Int));
447 
448 	if (NULL == dev->res_list) {
449 		DBG( _DBG_ERROR, "alloc fail, resolution problem\n" );
450 		u12if_close(dev);
451 		return SANE_STATUS_INVAL;
452 	}
453 
454     /* build up the resolution table */
455 	dev->res_list_size = 0;
456 	for( cntr = _DEF_DPI; cntr <= (res_x*16); cntr += 25 ) {
457 		dev->res_list_size++;
458 		dev->res_list[dev->res_list_size - 1] = (SANE_Int)cntr;
459 	}
460 
461 	return SANE_STATUS_GOOD;
462 }
463 
464 /**
465  */
u12if_startScan( U12_Device *dev )466 static SANE_Status u12if_startScan( U12_Device *dev )
467 {
468 	DBG( _DBG_INFO, "u12if_startScan()\n" );
469 	u12hw_StopLampTimer( dev );
470 	u12hw_SetGeneralRegister( dev );
471 	u12hw_ControlLampOnOff( dev );
472 	return SANE_STATUS_GOOD;
473 }
474 
475 /**
476  */
u12if_stopScan( U12_Device *dev )477 static SANE_Status u12if_stopScan( U12_Device *dev )
478 {
479 	DBG( _DBG_INFO, "u12if_stopScan()\n" );
480 
481 #if 0
482 	u12motor_ToHomePosition( dev, SANE_FALSE );
483 #else
484 #if 0
485 	u12motor_ToHomePosition( dev, SANE_TRUE );
486 	u12io_SoftwareReset( dev );
487 #endif
488 	u12hw_CancelSequence( dev );
489 #endif
490 	u12hw_StartLampTimer( dev );
491 	dev->DataInf.dwAppLinesPerArea = 0;
492 	dev->DataInf.dwScanFlag &= ~_SCANDEF_SCANNING;
493 	return SANE_STATUS_GOOD;
494 }
495 
496 /**
497  */
u12if_prepare( U12_Device *dev )498 static SANE_Status u12if_prepare( U12_Device *dev )
499 {
500 	SANE_Status res;
501 
502 	DBG( _DBG_INFO, "u12if_prepare()\n" );
503 
504 	u12motor_ToHomePosition( dev, SANE_TRUE );
505 
506 	res = u12hw_WarmupLamp( dev );
507 	if( res != SANE_STATUS_GOOD )
508 		return res;
509 
510 	res = u12shading_DoCalibration( dev );
511 	if( res != SANE_STATUS_GOOD )
512 		return res;
513 
514 	u12image_PrepareScaling( dev );
515 
516 	u12motor_ForceToLeaveHomePos( dev );
517 	if( dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW )
518 		u12hw_SetupPreviewCondition( dev );
519 	else
520 		u12hw_SetupScanningCondition( dev );
521 
522 	res = u12motor_WaitForPositionY( dev );
523 
524 	_DODELAY(100);
525 	u12io_ResetFifoLen();
526 	u12io_GetFifoLength(dev);
527 
528 	dev->scan.bModuleState   = _MotorAdvancing;
529 	dev->scan.oldScanState   = u12io_GetScanState( dev );
530 	dev->scan.oldScanState  &= _SCANSTATE_MASK;
531 	dev->DataInf.dwScanFlag |= _SCANDEF_SCANNING;
532 	DBG( _DBG_INFO, "* oldScanState = %u\n", dev->scan.oldScanState );
533 	DBG( _DBG_INFO, "u12if_prepare() done.\n" );
534 	return res;
535 }
536 
537 /**
538  */
u12if_readLine( U12_Device *dev, SANE_Byte *line_buffer )539 static SANE_Status u12if_readLine( U12_Device *dev, SANE_Byte *line_buffer )
540 {
541 	SANE_Status res;
542 
543 	DBG( _DBG_READ, "u12if_readLine()\n" );
544 
545 	if( u12io_IsEscPressed()) {
546 		DBG( _DBG_INFO, "u12if_readLine() - cancel detected!\n" );
547 		return SANE_STATUS_CANCELLED;
548 	}
549 
550 	if( dev->scaleBuf != NULL ) {
551 		res = u12image_ReadOneImageLine( dev, dev->scaleBuf );
552 		if( SANE_STATUS_GOOD != res )
553 			return res;
554 
555 		u12image_ScaleX( dev, dev->scaleBuf, line_buffer );
556 
557 	} else {
558 		res = u12image_ReadOneImageLine( dev, line_buffer );
559 		if( SANE_STATUS_GOOD != res )
560 			return res;
561 	}
562 	return SANE_STATUS_GOOD;
563 }
564 
565 /**
566  */
u12if_SetupBuffer( U12_Device *dev )567 static SANE_Status u12if_SetupBuffer( U12_Device *dev )
568 {
569 	void *buffer;
570 
571 	DBG( _DBG_INFO, "u12if_SetupBuffer()\n" );
572 	buffer = malloc(_SIZE_TOTAL_BUF_TPA);
573 	if( buffer == NULL )
574 		return SANE_STATUS_NO_MEM;
575 
576 	dev->shade.pHilight   = NULL;
577 	dev->bufs.b1.pReadBuf = buffer;
578 	dev->bufs.b2.pSumBuf  = dev->bufs.b1.pReadBuf + _SIZE_DATA_BUF;
579 	dev->bufs.TpaBuf.pb   = &((SANE_Byte*)dev->bufs.b2.pSumBuf)[_SIZE_SHADING_SUM_BUF];
580 
581 /* CHECK: We might should play around with these values... */
582 	dev->shade.skipHilight = _DEF_BRIGHTEST_SKIP;
583 	dev->shade.skipShadow  = _DEF_DARKEST_SKIP;
584 
585 	if( dev->shade.skipHilight && dev->shade.skipShadow ) {
586 
587 		u_long skipSize;
588 
589 		skipSize = (u_long)((dev->shade.skipHilight + dev->shade.skipShadow)
590 		                                                 * _SIZE_DATA_BUF * 3);
591 		dev->shade.pHilight = (RGBUShortDef*)malloc( skipSize );
592 		if( NULL != dev->shade.pHilight ) {
593 			dev->shade.dwDiv = (u_long)(32UL - dev->shade.skipHilight -
594 			                                            dev->shade.skipShadow);
595 		}
596 	}
597 	return SANE_STATUS_GOOD;
598 }
599 
600 /* END U12-IF.C .............................................................*/
601