/** @file u12-if.c * @brief The interface functions to the U12 backend stuff. * * Copyright (c) 2003-2004 Gerhard Jaeger * * History: * - 0.01 - initial version * - 0.02 - added model tweaking * - fixed autodetection bug * - added line-scaling stuff * - removed u12if_setScanEnv() * . *
* This file is part of the SANE package. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * As a special exception, the authors of SANE give permission for * additional uses of the libraries contained in this release of SANE. * * The exception is that, if you link a SANE library with other files * to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public * License. Your use of that executable is in no way restricted on * account of linking the SANE library code into it. * * This exception does not, however, invalidate any other reasons why * the executable file might be covered by the GNU General Public * License. * * If you submit changes to SANE to the maintainers to be included in * a subsequent release, you agree by submitting the changes that * those changes may be distributed with this exception intact. * * If you write modifications of your own for SANE, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. *
*/ #define _DEF_BRIGHTEST_SKIP 3 #define _DEF_DARKEST_SKIP 5 /** useful for description tables */ typedef struct { int id; char *desc; } TabDef; typedef struct { char *vp; char *name; } DevDesc; #define _PLUSTEK_VENID 0x07B3 #define _KYE_VENID 0x0458 /** to allow different vendors... */ static TabDef u12Vendors[] = { { _PLUSTEK_VENID, "Plustek" }, { _KYE_VENID, "KYE/Genius" }, { 0xFFFF, NULL } }; /** list of supported devices */ static DevDesc u12Devices[] = { { "0x07B3-0x0001", "1212U/U12" }, { "0x0458-0x2004", "Colorpage HR6" }, { NULL, NULL } }; /** for autodetection */ static SANE_Char USB_devname[1024]; /********************** the USB scanner interface ****************************/ /** */ static SANE_Status u12_initDev( U12_Device *dev, int handle, int vendor ) { int i; SANE_Status res; TimerDef timer; /* well now we patch the vendor string... * if not found, the default vendor will be Plustek */ for( i = 0; u12Vendors[i].desc != NULL; i++ ) { if( u12Vendors[i].id == vendor ) { dev->sane.vendor = u12Vendors[i].desc; DBG( _DBG_INFO, "Vendor adjusted to: >%s<\n", dev->sane.vendor ); break; } } dev->fd = handle; dev->adj.upNormal = 0; dev->adj.upNegative = 20; dev->adj.upPositive = -30; dev->adj.leftNormal = 51; res = SANE_STATUS_IO_ERROR; if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) { u12motor_PositionModuleToHome( dev ); u12io_StartTimer( &timer, _SECOND * 20); do { if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER) { res = SANE_STATUS_GOOD; break; } } while( !u12io_CheckTimer( &timer )); } else { res = u12hw_InitAsic( dev, SANE_FALSE ); } if( res == SANE_STATUS_GOOD ) u12hw_PutToIdleMode( dev ); return res; } /** will be called upon sane_exit */ static void u12if_shutdown( U12_Device *dev ) { SANE_Int handle; TimerDef timer; DBG( _DBG_INFO, "Shutdown called (dev->fd=%d, %s)\n", dev->fd, dev->sane.name ); if( SANE_STATUS_GOOD == sanei_usb_open( dev->sane.name, &handle )) { dev->fd = handle; u12io_OpenScanPath( dev ); u12hw_PutToIdleMode( dev ); if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) { u12motor_PositionModuleToHome( dev ); u12io_StartTimer( &timer, _SECOND * 20); do { if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER) { break; } } while( !u12io_CheckTimer( &timer )); } DBG( _DBG_INFO, "* Home position reached.\n" ); if( 0 != dev->adj.lampOffOnEnd ) { DBG( _DBG_INFO, "* Switching lamp off...\n" ); dev->regs.RD_ScanControl &= ~_SCAN_LAMPS_ON; u12io_DataToRegister(dev,REG_SCANCONTROL, dev->regs.RD_ScanControl ); } u12io_CloseScanPath( dev ); dev->fd = -1; sanei_usb_close( handle ); } #if 0 usb_StopLampTimer( dev ); #endif DBG( _DBG_INFO, "Shutdown done.\n" ); } /** This function checks whether a device, described by a given * string(vendor and product ID), is supported by this backend or not * * @param usbIdStr - string consisting out of product and vendor ID * format: "0xVVVV-0xPPPP" VVVV = Vendor ID, PPP = Product ID * @returns; SANE_TRUE if supported, SANE_FALSE if not */ static SANE_Bool u12if_IsDeviceSupported( U12_Device *dev ) { int i; for( i = 0; NULL != u12Devices[i].name; i++ ) { if( !strcmp( dev->usbId, u12Devices[i].vp )) { dev->sane.model = u12Devices[i].name; return SANE_TRUE; } } return SANE_FALSE; } /** */ static SANE_Status u12if_usbattach( SANE_String_Const dev_name ) { if( USB_devname[0] == '\0' ) { DBG( _DBG_INFO, "Found device at >%s<\n", dev_name ); strncpy( USB_devname, dev_name, 1023 ); USB_devname[1023] = '\0'; } else { DBG( _DBG_INFO, "Device >%s< ignoring\n", dev_name ); } return SANE_STATUS_GOOD; } #ifndef _FAKE_DEVICE /** here we roam through our list of supported devices and * cross check with the ones the system reports... * @param vendor - pointer to receive vendor ID * @param product - pointer to receive product ID * @return SANE_TRUE if a matching device has been found or * SANE_FALSE if nothing supported found... */ static SANE_Bool usbDev_autodetect( SANE_Word *vendor, SANE_Word *product ) { int i; SANE_Word p, v; DBG( _DBG_INFO, "Autodetection...\n" ); for( i = 0; NULL != u12Devices[i].name; i++ ) { v = strtol( &(u12Devices[i].vp)[0], 0, 0 ); p = strtol( &(u12Devices[i].vp)[7], 0, 0 ); DBG( _DBG_INFO, "* checking for 0x%04x-0x%04x\n", v, p ); sanei_usb_find_devices( v, p, u12if_usbattach ); if( USB_devname[0] != '\0' ) { *vendor = v; *product = p; DBG( _DBG_INFO, "* using device >%s<\n", USB_devname ); return SANE_TRUE; } } return SANE_FALSE; } #endif /** */ static int u12if_open( U12_Device *dev ) { char devStr[50]; int result; SANE_Int handle; SANE_Word vendor, product; SANE_Bool was_empty; DBG( _DBG_INFO, "u12if_open(%s,%s)\n", dev->name, dev->usbId ); USB_devname[0] = '\0'; #ifdef _FAKE_DEVICE dev->name = strdup( "auto" ); dev->sane.name = dev->name; was_empty = SANE_FALSE; result = SANE_STATUS_UNSUPPORTED; #else if( !strcmp( dev->name, "auto" )) { if( dev->usbId[0] == '\0' ) { if( !usbDev_autodetect( &vendor, &product )) { DBG( _DBG_ERROR, "No supported device found!\n" ); return -1; } } else { vendor = strtol( &dev->usbId[0], 0, 0 ); product = strtol( &dev->usbId[7], 0, 0 ); sanei_usb_find_devices( vendor, product, u12if_usbattach ); if( USB_devname[0] == '\0' ) { DBG( _DBG_ERROR, "No matching device found!\n" ); return -1; } } if( SANE_STATUS_GOOD != sanei_usb_open( USB_devname, &handle )) { return -1; } /* replace the old devname, so we are able to have multiple * auto-detected devices */ free( dev->name ); dev->name = strdup( USB_devname ); dev->sane.name = dev->name; } else { if( SANE_STATUS_GOOD != sanei_usb_open( dev->name, &handle )) return -1; } was_empty = SANE_FALSE; result = sanei_usb_get_vendor_product( handle, &vendor, &product ); #endif if( SANE_STATUS_GOOD == result ) { sprintf( devStr, "0x%04X-0x%04X", vendor, product ); DBG(_DBG_INFO,"Vendor ID=0x%04X, Product ID=0x%04X\n",vendor,product); if( dev->usbId[0] != '\0' ) { if( 0 != strcmp( dev->usbId, devStr )) { DBG( _DBG_ERROR, "Specified Vendor and Product ID " "doesn't match with the ones\n" "in the config file\n" ); sanei_usb_close( handle ); return -1; } } else { sprintf( dev->usbId, "0x%04X-0x%04X", vendor, product ); was_empty = SANE_TRUE; } } else { DBG( _DBG_INFO, "Can't get vendor & product ID from driver...\n" ); /* if the ioctl stuff is not supported by the kernel and we have * nothing specified, we have to give up... */ if( dev->usbId[0] == '\0' ) { DBG( _DBG_ERROR, "Cannot autodetect Vendor an Product ID, " "please specify in config file.\n" ); sanei_usb_close( handle ); return -1; } vendor = strtol( &dev->usbId[0], 0, 0 ); product = strtol( &dev->usbId[7], 0, 0 ); DBG( _DBG_INFO, "... using the specified: " "0x%04X-0x%04X\n", vendor, product ); } /* before accessing the scanner, check if supported! */ if( !u12if_IsDeviceSupported( dev )) { DBG( _DBG_ERROR, "Device >%s<, is not supported!\n", dev->usbId ); sanei_usb_close( handle ); return -1; } dev->mode = _PP_MODE_SPP; dev->fd = handle; /* is it accessible ? */ if( SANE_STATUS_GOOD != u12hw_CheckDevice( dev )) { dev->fd = -1; sanei_usb_close( handle ); return -1; } DBG( _DBG_INFO, "Detected vendor & product ID: " "0x%04X-0x%04X\n", vendor, product ); if( was_empty ) dev->usbId[0] = '\0'; /* now initialize the device */ if( SANE_STATUS_GOOD != u12_initDev( dev, handle, vendor )) { dev->fd = -1; sanei_usb_close( handle ); return -1; } if( _PLUSTEK_VENID == vendor ) { if( dev->Tpa ) dev->sane.model = "UT12"; } dev->initialized = SANE_TRUE; return handle; } /** */ static int u12if_close( U12_Device *dev ) { DBG( _DBG_INFO, "u12if_close()\n" ); u12io_CloseScanPath( dev ); sanei_usb_close( dev->fd ); dev->fd = -1; return 0; } /** */ static SANE_Status u12if_getCaps( U12_Device *dev ) { int cntr; int res_x = 600 ; /*dev->caps.OpticDpi.x */ DBG( _DBG_INFO, "u12if_getCaps()\n" ); /* FIXME: set dpi_range.max, max_x & max_y on a per model base */ dev->dpi_max_x = 600; dev->dpi_max_y = 1200; /* A4 devices */ dev->max_x = 8.5 * (double)_MM_PER_INCH; dev->max_y = 11.6934 * (double)_MM_PER_INCH; /* limit the range */ dev->dpi_range.min = _DEF_DPI; dev->dpi_range.max = dev->dpi_max_y; dev->dpi_range.quant = 0; dev->x_range.min = 0; dev->x_range.max = SANE_FIX(dev->max_x); dev->x_range.quant = 0; dev->y_range.min = 0; dev->y_range.max = SANE_FIX(dev->max_y); dev->y_range.quant = 0; /* calculate the size of the resolution list + * one more to avoid a buffer overflow, then allocate it... */ dev->res_list = (SANE_Int *) calloc((((res_x * 16)-_DEF_DPI)/25+1), sizeof (SANE_Int)); if (NULL == dev->res_list) { DBG( _DBG_ERROR, "alloc fail, resolution problem\n" ); u12if_close(dev); return SANE_STATUS_INVAL; } /* build up the resolution table */ dev->res_list_size = 0; for( cntr = _DEF_DPI; cntr <= (res_x*16); cntr += 25 ) { dev->res_list_size++; dev->res_list[dev->res_list_size - 1] = (SANE_Int)cntr; } return SANE_STATUS_GOOD; } /** */ static SANE_Status u12if_startScan( U12_Device *dev ) { DBG( _DBG_INFO, "u12if_startScan()\n" ); u12hw_StopLampTimer( dev ); u12hw_SetGeneralRegister( dev ); u12hw_ControlLampOnOff( dev ); return SANE_STATUS_GOOD; } /** */ static SANE_Status u12if_stopScan( U12_Device *dev ) { DBG( _DBG_INFO, "u12if_stopScan()\n" ); #if 0 u12motor_ToHomePosition( dev, SANE_FALSE ); #else #if 0 u12motor_ToHomePosition( dev, SANE_TRUE ); u12io_SoftwareReset( dev ); #endif u12hw_CancelSequence( dev ); #endif u12hw_StartLampTimer( dev ); dev->DataInf.dwAppLinesPerArea = 0; dev->DataInf.dwScanFlag &= ~_SCANDEF_SCANNING; return SANE_STATUS_GOOD; } /** */ static SANE_Status u12if_prepare( U12_Device *dev ) { SANE_Status res; DBG( _DBG_INFO, "u12if_prepare()\n" ); u12motor_ToHomePosition( dev, SANE_TRUE ); res = u12hw_WarmupLamp( dev ); if( res != SANE_STATUS_GOOD ) return res; res = u12shading_DoCalibration( dev ); if( res != SANE_STATUS_GOOD ) return res; u12image_PrepareScaling( dev ); u12motor_ForceToLeaveHomePos( dev ); if( dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW ) u12hw_SetupPreviewCondition( dev ); else u12hw_SetupScanningCondition( dev ); res = u12motor_WaitForPositionY( dev ); _DODELAY(100); u12io_ResetFifoLen(); u12io_GetFifoLength(dev); dev->scan.bModuleState = _MotorAdvancing; dev->scan.oldScanState = u12io_GetScanState( dev ); dev->scan.oldScanState &= _SCANSTATE_MASK; dev->DataInf.dwScanFlag |= _SCANDEF_SCANNING; DBG( _DBG_INFO, "* oldScanState = %u\n", dev->scan.oldScanState ); DBG( _DBG_INFO, "u12if_prepare() done.\n" ); return res; } /** */ static SANE_Status u12if_readLine( U12_Device *dev, SANE_Byte *line_buffer ) { SANE_Status res; DBG( _DBG_READ, "u12if_readLine()\n" ); if( u12io_IsEscPressed()) { DBG( _DBG_INFO, "u12if_readLine() - cancel detected!\n" ); return SANE_STATUS_CANCELLED; } if( dev->scaleBuf != NULL ) { res = u12image_ReadOneImageLine( dev, dev->scaleBuf ); if( SANE_STATUS_GOOD != res ) return res; u12image_ScaleX( dev, dev->scaleBuf, line_buffer ); } else { res = u12image_ReadOneImageLine( dev, line_buffer ); if( SANE_STATUS_GOOD != res ) return res; } return SANE_STATUS_GOOD; } /** */ static SANE_Status u12if_SetupBuffer( U12_Device *dev ) { void *buffer; DBG( _DBG_INFO, "u12if_SetupBuffer()\n" ); buffer = malloc(_SIZE_TOTAL_BUF_TPA); if( buffer == NULL ) return SANE_STATUS_NO_MEM; dev->shade.pHilight = NULL; dev->bufs.b1.pReadBuf = buffer; dev->bufs.b2.pSumBuf = dev->bufs.b1.pReadBuf + _SIZE_DATA_BUF; dev->bufs.TpaBuf.pb = &((SANE_Byte*)dev->bufs.b2.pSumBuf)[_SIZE_SHADING_SUM_BUF]; /* CHECK: We might should play around with these values... */ dev->shade.skipHilight = _DEF_BRIGHTEST_SKIP; dev->shade.skipShadow = _DEF_DARKEST_SKIP; if( dev->shade.skipHilight && dev->shade.skipShadow ) { u_long skipSize; skipSize = (u_long)((dev->shade.skipHilight + dev->shade.skipShadow) * _SIZE_DATA_BUF * 3); dev->shade.pHilight = (RGBUShortDef*)malloc( skipSize ); if( NULL != dev->shade.pHilight ) { dev->shade.dwDiv = (u_long)(32UL - dev->shade.skipHilight - dev->shade.skipShadow); } } return SANE_STATUS_GOOD; } /* END U12-IF.C .............................................................*/