/* HP Scanjet 3900 series - SANE Backend controller Copyright (C) 2005-2009 Jonathan Bravo Lopez 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 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. */ /* Backend Code for SANE*/ #define HP3900_CONFIG_FILE "hp3900.conf" #define GAMMA_DEFAULT 1.0 #include "../include/sane/config.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_debug.h" #include "hp3900_rts8822.c" struct st_convert { SANE_Int colormode; SANE_Int depth; SANE_Int threshold; SANE_Int negative; SANE_Int real_depth; }; /* options enumerator */ typedef enum { opt_begin = 0, grp_geometry, opt_tlx, opt_tly, opt_brx, opt_bry, opt_resolution, /* gamma tables */ opt_gamma_red, opt_gamma_green, opt_gamma_blue, opt_scantype, opt_colormode, opt_depth, opt_threshold, /* debugging options */ grp_debug, opt_model, opt_negative, opt_nogamma, opt_nowshading, opt_realdepth, opt_emulategray, opt_nowarmup, opt_dbgimages, opt_reset, /* device information */ grp_info, opt_chipname, opt_chipid, opt_scancount, opt_infoupdate, /* supported buttons. RTS8822 supports up to 6 buttons */ grp_sensors, opt_button_0, opt_button_1, opt_button_2, opt_button_3, opt_button_4, opt_button_5, opt_count } EOptionIndex; /* linked list of SANE_Device structures */ typedef struct TDevListEntry { struct TDevListEntry *pNext; SANE_Device dev; char *devname; } TDevListEntry; typedef struct { char *pszVendor; char *pszName; } TScannerModel; typedef union { SANE_Word w; SANE_Word *wa; /* word array */ SANE_String s; } TOptionValue; typedef struct { SANE_Int model; SANE_Option_Descriptor aOptions[opt_count]; TOptionValue aValues[opt_count]; struct params ScanParams; /* lists */ SANE_String_Const *list_colormodes; SANE_Int *list_depths; SANE_String_Const *list_models; SANE_Int *list_resolutions; SANE_String_Const *list_sources; SANE_Word *aGammaTable[3]; /* a 16-to-16 bit color lookup table */ SANE_Range rng_gamma; /* reading image */ SANE_Byte *image; SANE_Byte *rest; SANE_Int rest_amount; SANE_Int mylin; /* convertion settings */ struct st_convert cnv; /* ranges */ SANE_Range rng_threshold; SANE_Range rng_horizontal; SANE_Range rng_vertical; SANE_Int scan_count; SANE_Int fScanning; /* TRUE if actively scanning */ } TScanner; /* functions to manage backend's options */ static void options_init (TScanner * scanner); static void options_free (TScanner * scanner); /* devices listing */ static SANE_Int _ReportDevice (TScannerModel * pModel, const char *pszDeviceName); static SANE_Status attach_one_device (SANE_String_Const devname); /* capabilities */ static SANE_Status bknd_colormodes (TScanner * scanner, SANE_Int model); static void bknd_constrains (TScanner * scanner, SANE_Int source, SANE_Int type); static SANE_Status bknd_depths (TScanner * scanner, SANE_Int model); static SANE_Status bknd_info (TScanner * scanner); static SANE_Status bknd_models (TScanner * scanner); static SANE_Status bknd_resolutions (TScanner * scanner, SANE_Int model); static SANE_Status bknd_sources (TScanner * scanner, SANE_Int model); /* convertions */ static void Color_Negative (SANE_Byte * buffer, SANE_Int size, SANE_Int depth); static void Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth); static void Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size, SANE_Int threshold); static void Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size, SANE_Byte * to_buffer); /* gamma functions */ static void gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size, SANE_Int depth); static SANE_Int gamma_create (TScanner * s, double gamma); static void gamma_free (TScanner * s); static SANE_Int Get_Colormode (SANE_String colormode); static SANE_Int Get_Model (SANE_String model); static SANE_Int Get_Source (SANE_String source); static SANE_Int GetUSB_device_model (SANE_String_Const name); static size_t max_string_size (const SANE_String_Const strings[]); static SANE_Status get_button_status (TScanner * s); /* reading buffers */ static SANE_Status img_buffers_alloc (TScanner * scanner, SANE_Int size); static SANE_Status img_buffers_free (TScanner * scanner); static SANE_Status option_get (TScanner * scanner, SANE_Int optid, void *result); static SANE_Status option_set (TScanner * scanner, SANE_Int optid, void *value, SANE_Int * pInfo); static void Set_Coordinates (SANE_Int scantype, SANE_Int resolution, struct st_coords *coords); static SANE_Int set_ScannerModel (SANE_Int proposed, SANE_Int product, SANE_Int vendor); static void Silent_Compile (void); static SANE_Status Translate_coords (struct st_coords *coords); /* SANE functions */ void sane_cancel (SANE_Handle h); void sane_close (SANE_Handle h); SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, void *pVal, SANE_Int * pInfo); void sane_exit (void); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only); const SANE_Option_Descriptor *sane_get_option_descriptor (SANE_Handle h, SANE_Int n); SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p); SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd); SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h); SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len); SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking); SANE_Status sane_start (SANE_Handle h); /* variables */ static struct st_device *device = NULL; static TDevListEntry *_pFirstSaneDev = 0; static SANE_Int iNumSaneDev = 0; static const SANE_Device **_pSaneDevList = 0; /* Own functions */ static SANE_Status bknd_resolutions (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_resolutions(*scanner, model=%i)\n", model); if (scanner != NULL) { SANE_Int *res = NULL; switch (model) { case BQ5550: case UA4900: { SANE_Int myres[] = { 8, 50, 75, 100, 150, 200, 300, 600, 1200 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; case HPG2710: case HP3800: { /* 1200 and 2400 dpi are disabled until problems are solved */ SANE_Int myres[] = { 7, 50, 75, 100, 150, 200, 300, 600 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; case HP4370: case HPG3010: case HPG3110: { SANE_Int myres[] = { 10, 50, 75, 100, 150, 200, 300, 600, 1200, 2400, 4800 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; default: /* HP3970 & HP4070 & UA4900 */ { SANE_Int myres[] = { 9, 50, 75, 100, 150, 200, 300, 600, 1200, 2400 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; } if (res != NULL) { if (scanner->list_resolutions != NULL) free (scanner->list_resolutions); scanner->list_resolutions = res; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_models (TScanner * scanner) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_models:\n"); if (scanner != NULL) { SANE_String_Const *model = NULL; /* at this moment all devices use the same list */ SANE_String_Const mymodel[] = { "HP3800", "HP3970", "HP4070", "HP4370", "UA4900", "HPG3010", "BQ5550", "HPG2710", "HPG3110", 0 }; /* allocate space to save list */ model = (SANE_String_Const *) malloc (sizeof (mymodel)); if (model != NULL) memcpy (model, &mymodel, sizeof (mymodel)); if (model != NULL) { /* free previous list */ if (scanner->list_models != NULL) free (scanner->list_models); /* set new list */ scanner->list_models = model; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_colormodes (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_colormodes(*scanner, model=%i)\n", model); if (scanner != NULL) { SANE_String_Const *colormode = NULL; /* at this moment all devices use the same list */ SANE_String_Const mycolormode[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, 0 }; /* silence gcc */ model = model; colormode = (SANE_String_Const *) malloc (sizeof (mycolormode)); if (colormode != NULL) memcpy (colormode, &mycolormode, sizeof (mycolormode)); if (colormode != NULL) { if (scanner->list_colormodes != NULL) free (scanner->list_colormodes); scanner->list_colormodes = colormode; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_sources (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_sources(*scanner, model=%i)\n", model); if (scanner != NULL) { SANE_String_Const *source = NULL; switch (model) { case UA4900: { SANE_String_Const mysource[] = { SANE_I18N ("Flatbed"), 0 }; source = (SANE_String_Const *) malloc (sizeof (mysource)); if (source != NULL) memcpy (source, &mysource, sizeof (mysource)); } break; default: /* hp3970, hp4070, hp4370 and others */ { SANE_String_Const mysource[] = { SANE_I18N ("Flatbed"), SANE_I18N ("Slide"), SANE_I18N ("Negative"), 0 }; source = (SANE_String_Const *) malloc (sizeof (mysource)); if (source != NULL) memcpy (source, &mysource, sizeof (mysource)); } break; } if (source != NULL) { if (scanner->list_sources != NULL) free (scanner->list_sources); scanner->list_sources = source; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_depths (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_depths(*scanner, model=%i\n", model); if (scanner != NULL) { SANE_Int *depth = NULL; /* at this moment all devices use the same list */ SANE_Int mydepth[] = { 2, 8, 16 }; /*{3, 8, 12, 16}; */ /* silence gcc */ model = model; depth = (SANE_Int *) malloc (sizeof (mydepth)); if (depth != NULL) memcpy (depth, &mydepth, sizeof (mydepth)); if (depth != NULL) { if (scanner->list_depths != NULL) free (scanner->list_depths); scanner->list_depths = depth; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_info (TScanner * scanner) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_info(*scanner)"); if (scanner != NULL) { char data[256]; /* update chipset name */ Chipset_Name (device, data, 255); if (scanner->aValues[opt_chipname].s != NULL) { free (scanner->aValues[opt_chipname].s); scanner->aValues[opt_chipname].s = NULL; } scanner->aValues[opt_chipname].s = strdup (data); scanner->aOptions[opt_chipname].size = strlen (data) + 1; /* update chipset id */ scanner->aValues[opt_chipid].w = Chipset_ID (device); /* update scans counter */ scanner->aValues[opt_scancount].w = RTS_ScanCounter_Get (device); rst = SANE_STATUS_GOOD; } return rst; } static SANE_Int GetUSB_device_model (SANE_String_Const name) { SANE_Int usbid, model; /* default model is unknown */ model = -1; /* open usb device */ if (sanei_usb_open (name, &usbid) == SANE_STATUS_GOOD) { SANE_Int vendor, product; if (sanei_usb_get_vendor_product (usbid, &vendor, &product) == SANE_STATUS_GOOD) model = Device_get (product, vendor); sanei_usb_close (usbid); } return model; } static void Silent_Compile (void) { /* There are some functions in hp3900_rts8822.c that aren't used yet. To avoid compilation warnings we will use them here */ SANE_Byte a = 1; if (a == 0) { Buttons_Status (device); Calib_WriteTable (device, NULL, 0, 0); Gamma_GetTables (device, NULL); } } static void bknd_constrains (TScanner * scanner, SANE_Int source, SANE_Int type) { struct st_coords *coords = Constrains_Get (device, source); if ((coords != NULL) && (scanner != NULL)) { switch (type) { case 1: /* Y */ scanner->rng_vertical.max = coords->height; break; default: /* X */ scanner->rng_horizontal.max = coords->width; break; } } } static SANE_Status img_buffers_free (TScanner * scanner) { if (scanner != NULL) { if (scanner->image != NULL) { free (scanner->image); scanner->image = NULL; } if (scanner->rest != NULL) { free (scanner->rest); scanner->rest = NULL; } scanner->rest_amount = 0; } return SANE_STATUS_GOOD; } static SANE_Status img_buffers_alloc (TScanner * scanner, SANE_Int size) { SANE_Status rst; /* default result at this point */ rst = SANE_STATUS_INVAL; if (scanner != NULL) { /* default result at this point */ rst = SANE_STATUS_NO_MEM; /* free previous allocs */ img_buffers_free (scanner); scanner->image = (SANE_Byte *) malloc (size * sizeof (SANE_Byte)); if (scanner->image != NULL) { scanner->rest = (SANE_Byte *) malloc (size * sizeof (SANE_Byte)); if (scanner->rest != NULL) rst = SANE_STATUS_GOOD; /* ok !! */ } if (rst != SANE_STATUS_GOOD) img_buffers_free (scanner); } return rst; } static SANE_Int set_ScannerModel (SANE_Int proposed, SANE_Int product, SANE_Int vendor) { /* This function will set the device behaviour */ SANE_Int current = Device_get (product, vendor); char *sdevname[10] = { "Unknown", "HP3970", "HP4070", "HP4370", "UA4900", "HP3800", "HPG3010", "BQ5550", "HPG2710", "HPG3110" }; DBG (DBG_FNC, "> set_ScannerModel(proposed=%i, product=%04x, vendor=%04x)\n", proposed, product, vendor); if (proposed < 0) { if ((current < 0) || (current >= DEVSCOUNT)) { DBG (DBG_VRB, " -> Unknown device. Defaulting to HP3970...\n"); RTS_Debug->dev_model = HP3970; } else { RTS_Debug->dev_model = current; DBG (DBG_VRB, " -> Device model is %s\n", sdevname[current + 1]); } } else { if (proposed < DEVSCOUNT) { RTS_Debug->dev_model = proposed; DBG (DBG_VRB, " -> Device %s , treating as %s ...\n", sdevname[current + 1], sdevname[proposed + 1]); } else { if ((current >= 0) && (current < DEVSCOUNT)) { RTS_Debug->dev_model = current; DBG (DBG_VRB, " -> Device not supported. Defaulting to %s ...\n", sdevname[current + 1]); } else { RTS_Debug->dev_model = HP3970; DBG (DBG_VRB, "-> Device not supported. Defaulting to HP3970...\n"); } } } return OK; } static void Set_Coordinates (SANE_Int scantype, SANE_Int resolution, struct st_coords *coords) { struct st_coords *limits = Constrains_Get (device, scantype); DBG (DBG_FNC, "> Set_Coordinates(res=%i, *coords):\n", resolution); if (coords->left == -1) coords->left = 0; if (coords->width == -1) coords->width = limits->width; if (coords->top == -1) coords->top = 0; if (coords->height == -1) coords->height = limits->height; DBG (DBG_FNC, " -> Coords [MM] : xy(%i, %i) wh(%i, %i)\n", coords->left, coords->top, coords->width, coords->height); coords->left = MM_TO_PIXEL (coords->left, resolution); coords->width = MM_TO_PIXEL (coords->width, resolution); coords->top = MM_TO_PIXEL (coords->top, resolution); coords->height = MM_TO_PIXEL (coords->height, resolution); DBG (DBG_FNC, " -> Coords [px] : xy(%i, %i) wh(%i, %i)\n", coords->left, coords->top, coords->width, coords->height); Constrains_Check (device, resolution, scantype, coords); DBG (DBG_FNC, " -> Coords [check]: xy(%i, %i) wh(%i, %i)\n", coords->left, coords->top, coords->width, coords->height); } static void Color_Negative (SANE_Byte * buffer, SANE_Int size, SANE_Int depth) { if (buffer != NULL) { SANE_Int a; SANE_Int max_value = (1 << depth) - 1; if (depth > 8) { USHORT *sColor = (void *) buffer; for (a = 0; a < size / 2; a++) { *sColor = max_value - *sColor; sColor++; } } else { for (a = 0; a < size; a++) *(buffer + a) = max_value - *(buffer + a); } } } static SANE_Status get_button_status (TScanner * s) { if (s != NULL) { SANE_Int a, b, status, btn; b = 1; status = Buttons_Released (device) & 63; for (a = 0; a < 6; a++) { if ((status & b) != 0) { btn = Buttons_Order (device, b); if (btn != -1) s->aValues[opt_button_0 + btn].w = SANE_TRUE; } b <<= 1; } } return SANE_STATUS_GOOD; } static void Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size, SANE_Byte * to_buffer) { if ((from_buffer != NULL) && (to_buffer != NULL)) { SANE_Int a, b; a = 1; b = 0; while (a < size) { *(to_buffer + b) = *(from_buffer + a); a += 2; b++; } } } static void Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size, SANE_Int threshold) { /* code provided by tobias leutwein */ if (buffer != NULL) { SANE_Byte toBufferByte; SANE_Int fromBufferPos_i = 0; SANE_Int toBufferPos_i = 0; SANE_Int bitPos_i; while (fromBufferPos_i < size) { toBufferByte = 0; for (bitPos_i = 7; bitPos_i != (-1); bitPos_i--) { if ((fromBufferPos_i < size) && (buffer[fromBufferPos_i] < threshold)) toBufferByte |= (1u << bitPos_i); fromBufferPos_i++; } buffer[toBufferPos_i] = toBufferByte; toBufferPos_i++; } } } static void Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth) { /* converts 3 color channel into 1 gray channel of specified bit depth */ if (buffer != NULL) { SANE_Int c, chn, chn_size; SANE_Byte *ptr_src = NULL; SANE_Byte *ptr_dst = NULL; float data, chn_data; float coef[3] = { 0.299, 0.587, 0.114 }; /* coefficients per channel */ chn_size = (depth > 8) ? 2 : 1; ptr_src = (void *) buffer; ptr_dst = (void *) buffer; for (c = 0; c < size / (3 * chn_size); c++) { data = 0.; /* get, apply coeffs and sum channels */ for (chn = 0; chn < 3; chn++) { chn_data = data_lsb_get (ptr_src + (chn * chn_size), chn_size); data += (chn_data * coef[chn]); } /* save result */ data_lsb_set (ptr_dst, (SANE_Int) data, chn_size); ptr_src += 3 * chn_size; /* next triplet */ ptr_dst += chn_size; } } } static void gamma_free (TScanner * s) { DBG (DBG_FNC, "> gamma_free()\n"); if (s != NULL) { /* Destroy gamma tables */ SANE_Int a; for (a = CL_RED; a <= CL_BLUE; a++) { if (s->aGammaTable[a] != NULL) { free (s->aGammaTable[a]); s->aGammaTable[a] = NULL; } } } } static SANE_Int gamma_create (TScanner * s, double gamma) { SANE_Int rst = ERROR; /* by default */ DBG (DBG_FNC, "> gamma_create(*s)\n"); if (s != NULL) { SANE_Int a; double value, c; /* default result */ rst = OK; /* destroy previus gamma tables */ gamma_free (s); /* check gamma value */ if (gamma < 0) gamma = GAMMA_DEFAULT; /* allocate space for 16 bit gamma tables */ for (a = CL_RED; a <= CL_BLUE; a++) { s->aGammaTable[a] = malloc (65536 * sizeof (SANE_Word)); if (s->aGammaTable[a] == NULL) { rst = ERROR; break; } } if (rst == OK) { /* fill tables */ for (a = 0; a < 65536; a++) { value = (a / (65536. - 1)); value = pow (value, (1. / gamma)); value = value * (65536. - 1); c = (SANE_Int) value; if (c > (65536. - 1)) c = (65536. - 1); else if (c < 0) c = 0; s->aGammaTable[CL_RED][a] = c; s->aGammaTable[CL_GREEN][a] = c; s->aGammaTable[CL_BLUE][a] = c; } } else gamma_free (s); } return rst; } static void gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size, SANE_Int depth) { if ((s != NULL) && (buffer != NULL)) { SANE_Int c; SANE_Int dot_size = 3 * ((depth > 8) ? 2 : 1); SANE_Byte *pColor = buffer; USHORT *sColor = (void *) buffer; if ((s->aGammaTable[CL_RED] != NULL) && (s->aGammaTable[CL_GREEN] != NULL) && (s->aGammaTable[CL_BLUE] != NULL)) { for (c = 0; c < size / dot_size; c++) { if (depth > 8) { *sColor = s->aGammaTable[CL_RED][*sColor]; *(sColor + 1) = s->aGammaTable[CL_GREEN][*(sColor + 1)]; *(sColor + 2) = s->aGammaTable[CL_BLUE][*(sColor + 2)]; sColor += 3; } else { /* 8 bits gamma */ *pColor = (s->aGammaTable[CL_RED][*pColor * 256] >> 8) & 0xff; *(pColor + 1) = (s-> aGammaTable[CL_GREEN][*(pColor + 1) * 256] >> 8) & 0xff; *(pColor + 2) = (s-> aGammaTable[CL_BLUE][*(pColor + 2) * 256] >> 8) & 0xff; pColor += 3; } } } } } static SANE_Int Get_Model (SANE_String model) { SANE_Int rst; if (strcmp (model, "HP3800") == 0) rst = HP3800; else if (strcmp (model, "HPG2710") == 0) rst = HPG2710; else if (strcmp (model, "HP3970") == 0) rst = HP3970; else if (strcmp (model, "HP4070") == 0) rst = HP4070; else if (strcmp (model, "HP4370") == 0) rst = HP4370; else if (strcmp (model, "HPG3010") == 0) rst = HPG3010; else if (strcmp (model, "HPG3110") == 0) rst = HPG3110; else if (strcmp (model, "UA4900") == 0) rst = UA4900; else if (strcmp (model, "BQ5550") == 0) rst = BQ5550; else rst = HP3970; /* default */ return rst; } static SANE_Int Get_Source (SANE_String source) { SANE_Int rst; if (strcmp (source, SANE_I18N ("Flatbed")) == 0) rst = ST_NORMAL; else if (strcmp (source, SANE_I18N ("Slide")) == 0) rst = ST_TA; else if (strcmp (source, SANE_I18N ("Negative")) == 0) rst = ST_NEG; else rst = ST_NORMAL; /* default */ return rst; } static SANE_Int Get_Colormode (SANE_String colormode) { SANE_Int rst; if (strcmp (colormode, SANE_VALUE_SCAN_MODE_COLOR) == 0) rst = CM_COLOR; else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_GRAY) == 0) rst = CM_GRAY; else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_LINEART) == 0) rst = CM_LINEART; else rst = CM_COLOR; /* default */ return rst; } static SANE_Status Translate_coords (struct st_coords *coords) { SANE_Int data; DBG (DBG_FNC, "> Translate_coords(*coords)\n"); if ((coords->left < 0) || (coords->top < 0) || (coords->width < 0) || (coords->height < 0)) return SANE_STATUS_INVAL; if (coords->width < coords->left) { data = coords->left; coords->left = coords->width; coords->width = data; } if (coords->height < coords->top) { data = coords->top; coords->top = coords->height; coords->height = data; } coords->width -= coords->left; coords->height -= coords->top; if (coords->width == 0) coords->width++; if (coords->height == 0) coords->height++; return SANE_STATUS_GOOD; } static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; SANE_Int i; DBG (DBG_FNC, "> max_string_size:\n"); for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static void options_free (TScanner * scanner) { /* frees all information contained in controls */ DBG (DBG_FNC, "> options_free\n"); if (scanner != NULL) { SANE_Int i; SANE_Option_Descriptor *pDesc; TOptionValue *pVal; /* free gamma tables */ gamma_free (scanner); /* free lists */ if (scanner->list_resolutions != NULL) free (scanner->list_resolutions); if (scanner->list_depths != NULL) free (scanner->list_depths); if (scanner->list_sources != NULL) free (scanner->list_sources); if (scanner->list_colormodes != NULL) free (scanner->list_colormodes); if (scanner->list_models != NULL) free (scanner->list_models); /* free values in certain controls */ for (i = opt_begin; i < opt_count; i++) { pDesc = &scanner->aOptions[i]; pVal = &scanner->aValues[i]; if (pDesc->type == SANE_TYPE_STRING) { if (pVal->s != NULL) free (pVal->s); } } } } static void options_init (TScanner * scanner) { /* initializes all controls */ DBG (DBG_FNC, "> options_init\n"); if (scanner != NULL) { SANE_Int i; SANE_Option_Descriptor *pDesc; TOptionValue *pVal; /* set gamma */ gamma_create (scanner, 1.0); /* color convertion */ scanner->cnv.colormode = -1; scanner->cnv.negative = FALSE; scanner->cnv.threshold = 40; scanner->cnv.real_depth = FALSE; scanner->cnv.depth = -1; /* setting threshold */ scanner->rng_threshold.min = 0; scanner->rng_threshold.max = 255; scanner->rng_threshold.quant = 0; /* setting gamma range (16 bits depth) */ scanner->rng_gamma.min = 0; scanner->rng_gamma.max = 65535; scanner->rng_gamma.quant = 0; /* setting default horizontal constrain in milimeters */ scanner->rng_horizontal.min = 0; scanner->rng_horizontal.max = 220; scanner->rng_horizontal.quant = 1; /* setting default vertical constrain in milimeters */ scanner->rng_vertical.min = 0; scanner->rng_vertical.max = 300; scanner->rng_vertical.quant = 1; /* allocate option lists */ bknd_info (scanner); bknd_colormodes (scanner, RTS_Debug->dev_model); bknd_depths (scanner, RTS_Debug->dev_model); bknd_models (scanner); bknd_resolutions (scanner, RTS_Debug->dev_model); bknd_sources (scanner, RTS_Debug->dev_model); /* By default preview scan */ scanner->ScanParams.scantype = ST_NORMAL; scanner->ScanParams.colormode = CM_COLOR; scanner->ScanParams.resolution_x = 75; scanner->ScanParams.resolution_y = 75; scanner->ScanParams.coords.left = 0; scanner->ScanParams.coords.top = 0; scanner->ScanParams.coords.width = 220; scanner->ScanParams.coords.height = 300; scanner->ScanParams.depth = 8; scanner->ScanParams.channel = 0; for (i = opt_begin; i < opt_count; i++) { pDesc = &scanner->aOptions[i]; pVal = &scanner->aValues[i]; /* defaults */ pDesc->name = ""; pDesc->title = ""; pDesc->desc = ""; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = 0; switch (i) { case opt_begin: pDesc->title = SANE_TITLE_NUM_OPTIONS; pDesc->desc = SANE_DESC_NUM_OPTIONS; pDesc->cap = SANE_CAP_SOFT_DETECT; pVal->w = (SANE_Word) opt_count; break; case grp_geometry: pDesc->name = SANE_NAME_GEOMETRY; pDesc->title = SANE_TITLE_GEOMETRY; pDesc->desc = SANE_DESC_GEOMETRY; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_tlx: pDesc->name = SANE_NAME_SCAN_TL_X; pDesc->title = SANE_TITLE_SCAN_TL_X; pDesc->desc = SANE_DESC_SCAN_TL_X; pDesc->unit = SANE_UNIT_MM; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_horizontal; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = 0; break; case opt_tly: pDesc->name = SANE_NAME_SCAN_TL_Y; pDesc->title = SANE_TITLE_SCAN_TL_Y; pDesc->desc = SANE_DESC_SCAN_TL_Y; pDesc->unit = SANE_UNIT_MM; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_vertical; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = 0; break; case opt_brx: pDesc->name = SANE_NAME_SCAN_BR_X; pDesc->title = SANE_TITLE_SCAN_BR_X; pDesc->desc = SANE_DESC_SCAN_BR_X; pDesc->unit = SANE_UNIT_MM; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_horizontal; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->rng_horizontal.max; break; case opt_bry: pDesc->name = SANE_NAME_SCAN_BR_Y; pDesc->title = SANE_TITLE_SCAN_BR_Y; pDesc->desc = SANE_DESC_SCAN_BR_Y; pDesc->unit = SANE_UNIT_MM; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_vertical; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->rng_vertical.max; break; case opt_resolution: pDesc->name = SANE_NAME_SCAN_RESOLUTION; pDesc->title = SANE_TITLE_SCAN_RESOLUTION; pDesc->desc = SANE_DESC_SCAN_RESOLUTION; pDesc->unit = SANE_UNIT_DPI; pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST; pDesc->constraint.word_list = scanner->list_resolutions; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->list_resolutions[1]; break; case opt_gamma_red: pDesc->name = SANE_NAME_GAMMA_VECTOR_R; pDesc->title = SANE_TITLE_GAMMA_VECTOR_R; pDesc->desc = SANE_DESC_GAMMA_VECTOR_R; pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word); pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_gamma; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = scanner->aGammaTable[CL_RED]; break; case opt_gamma_green: pDesc->name = SANE_NAME_GAMMA_VECTOR_G; pDesc->title = SANE_TITLE_GAMMA_VECTOR_G; pDesc->desc = SANE_DESC_GAMMA_VECTOR_G; pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word); pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_gamma; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = scanner->aGammaTable[CL_GREEN]; break; case opt_gamma_blue: pDesc->name = SANE_NAME_GAMMA_VECTOR_B; pDesc->title = SANE_TITLE_GAMMA_VECTOR_B; pDesc->desc = SANE_DESC_GAMMA_VECTOR_B; pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word); pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_gamma; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = scanner->aGammaTable[CL_BLUE]; break; case opt_scantype: pDesc->name = SANE_NAME_SCAN_SOURCE; pDesc->title = SANE_TITLE_SCAN_SOURCE; pDesc->desc = SANE_DESC_SCAN_SOURCE; pDesc->type = SANE_TYPE_STRING; pDesc->size = max_string_size (scanner->list_sources); pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; pDesc->constraint.string_list = scanner->list_sources; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->s = strdup (scanner->list_sources[0]); break; case opt_colormode: pDesc->name = SANE_NAME_SCAN_MODE; pDesc->title = SANE_TITLE_SCAN_MODE; pDesc->desc = SANE_DESC_SCAN_MODE; pDesc->type = SANE_TYPE_STRING; pDesc->size = max_string_size (scanner->list_colormodes); pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; pDesc->constraint.string_list = scanner->list_colormodes; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->s = strdup (scanner->list_colormodes[0]); break; case opt_depth: pDesc->name = SANE_NAME_BIT_DEPTH; pDesc->title = SANE_TITLE_BIT_DEPTH; pDesc->desc = SANE_DESC_BIT_DEPTH; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_BIT; pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST; pDesc->constraint.word_list = scanner->list_depths; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->list_depths[1]; break; case opt_threshold: pDesc->name = SANE_NAME_THRESHOLD; pDesc->title = SANE_TITLE_THRESHOLD; pDesc->desc = SANE_DESC_THRESHOLD; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_threshold; pDesc->cap |= SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; pVal->w = 0x80; break; /* debugging options */ case grp_debug: pDesc->name = "grp_debug"; pDesc->title = SANE_I18N ("Debugging Options"); pDesc->desc = ""; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = SANE_CAP_ADVANCED; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_model: pDesc->name = "opt_model"; pDesc->title = SANE_I18N ("Scanner model"); pDesc->desc = SANE_I18N ("Allows one to test device behaviour with other supported models"); pDesc->type = SANE_TYPE_STRING; pDesc->size = max_string_size (scanner->list_models); pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; pDesc->constraint.string_list = scanner->list_models; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->s = strdup (scanner->list_models[0]); break; case opt_negative: pDesc->name = "opt_negative"; pDesc->title = SANE_I18N ("Negative"); pDesc->desc = SANE_I18N ("Image colours will be inverted"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_nogamma: pDesc->name = "opt_nogamma"; pDesc->title = SANE_I18N ("Disable gamma correction"); pDesc->desc = SANE_I18N ("Gamma correction will be disabled"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_nowshading: pDesc->name = "opt_nowshading"; pDesc->title = SANE_I18N ("Disable white shading correction"); pDesc->desc = SANE_I18N ("White shading correction will be disabled"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_nowarmup: pDesc->name = "opt_nowarmup"; pDesc->title = SANE_I18N ("Skip warmup process"); pDesc->desc = SANE_I18N ("Warmup process will be disabled"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_realdepth: pDesc->name = "opt_realdepth"; pDesc->title = SANE_I18N ("Force real depth"); pDesc->desc = SANE_I18N ("If gamma is enabled, scans are always made in 16 bits depth to improve image quality and then converted to the selected depth. This option avoids depth emulation."); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_emulategray: pDesc->name = "opt_emulategray"; pDesc->title = SANE_I18N ("Emulate Grayscale"); pDesc->desc = SANE_I18N ("If enabled, image will be scanned in color mode and then converted to grayscale by software. This may improve image quality in some circumstances."); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_dbgimages: pDesc->name = "opt_dbgimages"; pDesc->title = SANE_I18N ("Save debugging images"); pDesc->desc = SANE_I18N ("If enabled, some images involved in scanner processing are saved to analyze them."); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_reset: pDesc->name = "opt_reset"; pDesc->title = SANE_I18N ("Reset chipset"); pDesc->desc = SANE_I18N ("Resets chipset data"); pDesc->type = SANE_TYPE_BUTTON; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.string_list = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT; pVal->w = 0; break; /* device information */ case grp_info: pDesc->name = "grp_info"; pDesc->title = SANE_I18N ("Information"); pDesc->desc = ""; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_chipname: pDesc->name = "opt_chipname"; pDesc->title = SANE_I18N ("Chipset name"); pDesc->desc = SANE_I18N ("Shows chipset name used in device."); pDesc->type = SANE_TYPE_STRING; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT; pVal->s = strdup (SANE_I18N ("Unknown")); pDesc->size = strlen(pVal->s) + 1; break; case opt_chipid: pDesc->name = "opt_chipid"; pDesc->title = SANE_I18N ("Chipset ID"); pDesc->desc = SANE_I18N ("Shows the chipset ID"); pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT; pVal->w = -1; break; case opt_scancount: pDesc->name = "opt_scancount"; pDesc->title = SANE_I18N ("Scan counter"); pDesc->desc = SANE_I18N ("Shows the number of scans made by scanner"); pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT; pVal->w = -1; break; case opt_infoupdate: pDesc->name = "opt_infoupdate"; pDesc->title = SANE_I18N ("Update information"); pDesc->desc = SANE_I18N ("Updates information about device"); pDesc->type = SANE_TYPE_BUTTON; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.string_list = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT; pVal->w = 0; break; /* buttons support */ case grp_sensors: pDesc->name = SANE_NAME_SENSORS; pDesc->title = SANE_TITLE_SENSORS; pDesc->desc = SANE_DESC_SENSORS; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_button_0: case opt_button_1: case opt_button_2: case opt_button_3: case opt_button_4: case opt_button_5: { char name[12]; char title[128]; sprintf (name, "button %d", i - opt_button_0); sprintf (title, "Scanner button %d", i - opt_button_0); pDesc->name = strdup (name); pDesc->title = strdup (title); pDesc->desc = SANE_I18N ("This option reflects a front panel scanner button"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if (i - opt_button_0 >= Buttons_Count (device)) pDesc->cap |= SANE_CAP_INACTIVE; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pVal->w = SANE_FALSE; } break; } } } } static SANE_Int _ReportDevice (TScannerModel * pModel, const char *pszDeviceName) { SANE_Int rst = ERROR; TDevListEntry *pNew, *pDev; DBG (DBG_FNC, "> _ReportDevice:\n"); pNew = malloc (sizeof (TDevListEntry)); if (pNew != NULL) { rst = OK; /* add new element to the end of the list */ if (_pFirstSaneDev != NULL) { /* Add at the end of existing list */ for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext); pDev->pNext = pNew; } else _pFirstSaneDev = pNew; /* fill in new element */ pNew->pNext = NULL; pNew->devname = (char *) strdup (pszDeviceName); pNew->dev.name = pNew->devname; pNew->dev.vendor = pModel->pszVendor; pNew->dev.model = pModel->pszName; pNew->dev.type = SANE_I18N ("flatbed scanner"); iNumSaneDev++; } return rst; } static SANE_Status attach_one_device (SANE_String_Const devname) { static TScannerModel sModel; DBG (DBG_FNC, "> attach_one_device(devname=%s)\n", devname); switch (GetUSB_device_model (devname)) { case HP3800: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 3800"); break; case HPG2710: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet G2710"); break; case HP3970: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 3970"); break; case HP4070: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 4070 Photosmart"); break; case HP4370: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 4370"); break; case HPG3010: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet G3010"); break; case HPG3110: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet G3110"); break; case UA4900: sModel.pszVendor = (char *) strdup ("UMAX"); sModel.pszName = (char *) strdup ("Astra 4900"); break; case BQ5550: sModel.pszVendor = (char *) strdup ("BenQ"); sModel.pszName = (char *) strdup ("5550"); break; default: sModel.pszVendor = (char *) strdup ("Unknown"); sModel.pszName = (char *) strdup ("RTS8822 chipset based"); break; } _ReportDevice (&sModel, devname); return SANE_STATUS_GOOD; } /* Sane default functions */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { FILE *conf_fp; /* Config file stream */ SANE_Char line[PATH_MAX]; SANE_Char *str = NULL; SANE_String_Const proper_str; SANE_Int nline = 0; /* Initialize debug */ DBG_INIT (); DBG (DBG_FNC, "> sane_init\n"); /* silence gcc */ authorize = authorize; /* Initialize usb */ sanei_usb_init (); /* Parse config file */ conf_fp = sanei_config_open (HP3900_CONFIG_FILE); if (conf_fp) { while (sanei_config_read (line, sizeof (line), conf_fp)) { nline++; if (str) free (str); proper_str = sanei_config_get_string (line, &str); /* Discards white lines and comments */ if ((str != NULL) && (proper_str != line) && (str[0] != '#')) { /* If line's not blank or a comment, then it's the device * filename or a usb directive. */ sanei_usb_attach_matching_devices (line, attach_one_device); } } fclose (conf_fp); } else { /* default */ DBG (DBG_VRB, "- %s not found. Looking for hardcoded usb ids ...\n", HP3900_CONFIG_FILE); sanei_usb_attach_matching_devices ("usb 0x03f0 0x2605", attach_one_device); /* HP3800 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2805", attach_one_device); /* HPG2710 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2305", attach_one_device); /* HP3970 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2405", attach_one_device); /* HP4070 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4105", attach_one_device); /* HP4370 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4205", attach_one_device); /* HPG3010 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4305", attach_one_device); /* HPG3110 */ sanei_usb_attach_matching_devices ("usb 0x06dc 0x0020", attach_one_device); /* UA4900 */ sanei_usb_attach_matching_devices ("usb 0x04a5 0x2211", attach_one_device); /* BQ5550 */ } /* Return backend version */ if (version_code != NULL) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0); return SANE_STATUS_GOOD; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { SANE_Status rst = SANE_STATUS_GOOD; local_only = local_only; if (_pSaneDevList) free (_pSaneDevList); _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1)); if (_pSaneDevList != NULL) { TDevListEntry *pDev; SANE_Int i = 0; for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext) _pSaneDevList[i++] = &pDev->dev; _pSaneDevList[i++] = 0; /* last entry is 0 */ *device_list = _pSaneDevList; } else rst = SANE_STATUS_NO_MEM; DBG (DBG_FNC, "> sane_get_devices: %i\n", rst); return rst; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { TScanner *s; SANE_Status rst; /* check the name */ if (strlen (name) == 0) /* default to first available device */ name = _pFirstSaneDev->dev.name; /* allocate space for RTS environment */ device = RTS_Alloc (); if (device != NULL) { /* Open device */ rst = sanei_usb_open (name, &device->usb_handle); if (rst == SANE_STATUS_GOOD) { /* Allocating memory for device */ s = malloc (sizeof (TScanner)); if (s != NULL) { memset (s, 0, sizeof (TScanner)); /* Initializing RTS */ if (Init_Vars () == OK) { SANE_Int vendor, product; /* Setting device model */ if (sanei_usb_get_vendor_product (device->usb_handle, &vendor, &product) == SANE_STATUS_GOOD) s->model = Device_get (product, vendor); else s->model = HP3970; set_ScannerModel (s->model, product, vendor); /* Initialize device */ if (RTS_Scanner_Init (device) == OK) { /* silencing unused functions */ Silent_Compile (); /* initialize backend options */ options_init (s); *h = s; /* everything went ok */ rst = SANE_STATUS_GOOD; } else { free ((void *) s); rst = SANE_STATUS_INVAL; } } else rst = SANE_STATUS_NO_MEM; } else rst = SANE_STATUS_NO_MEM; } } else rst = SANE_STATUS_NO_MEM; DBG (DBG_FNC, "> sane_open(name=%s): %i\n", name, rst); return rst; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int n) { SANE_Option_Descriptor *rst = NULL; if ((n >= opt_begin) && (n < opt_count)) { TScanner *s = (TScanner *) h; rst = &s->aOptions[n]; } DBG (DBG_FNC, "> SANE_Option_Descriptor(handle, n=%i): %i\n", n, (rst == NULL) ? -1 : 0); return rst; } static SANE_Status option_get (TScanner * scanner, SANE_Int optid, void *result) { /* This function returns value contained in selected option */ DBG (DBG_FNC, "> option_get(optid=%i)\n", optid); if ((scanner != NULL) && (result != NULL)) { switch (optid) { /* SANE_Word */ case opt_begin: /* null */ case opt_reset: /* null */ case opt_negative: case opt_nogamma: case opt_nowshading: case opt_emulategray: case opt_dbgimages: case opt_nowarmup: case opt_realdepth: case opt_depth: case opt_resolution: case opt_threshold: case opt_brx: case opt_tlx: case opt_bry: case opt_tly: *(SANE_Word *) result = scanner->aValues[optid].w; break; /* SANE_Int */ case opt_chipid: case opt_scancount: *(SANE_Int *) result = scanner->aValues[optid].w; break; /* SANE_Word array */ case opt_gamma_red: case opt_gamma_green: case opt_gamma_blue: memcpy (result, scanner->aValues[optid].wa, scanner->aOptions[optid].size); break; /* String */ case opt_colormode: case opt_scantype: case opt_model: case opt_chipname: strncpy (result, scanner->aValues[optid].s, scanner->aOptions[optid].size); ((char*)result)[scanner->aOptions[optid].size-1] = '\0'; break; /* scanner buttons */ case opt_button_0: get_button_status (scanner); case opt_button_1: case opt_button_2: case opt_button_3: case opt_button_4: case opt_button_5: /* copy the button state */ *(SANE_Word *) result = scanner->aValues[optid].w; /* clear the button state */ scanner->aValues[optid].w = SANE_FALSE; break; } } return SANE_STATUS_GOOD; } static SANE_Status option_set (TScanner * scanner, SANE_Int optid, void *value, SANE_Int * pInfo) { SANE_Status rst; DBG (DBG_FNC, "> option_set(optid=%i)\n", optid); rst = SANE_STATUS_INVAL; if (scanner != NULL) { if (scanner->fScanning == FALSE) { SANE_Int info = 0; rst = SANE_STATUS_GOOD; switch (optid) { case opt_brx: case opt_tlx: case opt_bry: case opt_tly: case opt_depth: case opt_nogamma: case opt_nowshading: case opt_nowarmup: case opt_negative: case opt_emulategray: case opt_dbgimages: case opt_threshold: case opt_resolution: info |= SANE_INFO_RELOAD_PARAMS; scanner->aValues[optid].w = *(SANE_Word *) value; break; case opt_gamma_red: case opt_gamma_green: case opt_gamma_blue: memcpy (scanner->aValues[optid].wa, value, scanner->aOptions[optid].size); break; case opt_scantype: if (strcmp (scanner->aValues[optid].s, value) != 0) { struct st_coords *coords; SANE_Int source; if (scanner->aValues[optid].s) free (scanner->aValues[optid].s); scanner->aValues[optid].s = strdup (value); source = Get_Source (scanner->aValues[opt_scantype].s); coords = Constrains_Get (device, source); if (coords != NULL) { bknd_constrains (scanner, source, 0); bknd_constrains (scanner, source, 1); scanner->aValues[opt_tlx].w = 0; scanner->aValues[opt_tly].w = 0; scanner->aValues[opt_brx].w = coords->width; scanner->aValues[opt_bry].w = coords->height; } info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } break; case opt_colormode: if (strcmp (scanner->aValues[optid].s, value) != 0) { if (scanner->aValues[optid].s) free (scanner->aValues[optid].s); scanner->aValues[optid].s = strdup (value); if (Get_Colormode (scanner->aValues[optid].s) == CM_LINEART) scanner->aOptions[opt_threshold].cap &= ~SANE_CAP_INACTIVE; else scanner->aOptions[opt_threshold].cap |= SANE_CAP_INACTIVE; info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } break; case opt_model: if (strcmp (scanner->aValues[optid].s, value) != 0) { SANE_Int model; if (scanner->aValues[optid].s) free (scanner->aValues[optid].s); scanner->aValues[optid].s = strdup (value); model = Get_Model (scanner->aValues[optid].s); if (model != RTS_Debug->dev_model) { SANE_Int source; struct st_coords *coords; /* free configuration of last model */ Free_Config (device); /* set new model */ RTS_Debug->dev_model = model; /* and load configuration of current model */ Load_Config (device); /* update options according to selected device */ bknd_info (scanner); bknd_colormodes (scanner, model); bknd_depths (scanner, model); bknd_resolutions (scanner, model); bknd_sources (scanner, model); /* updating lists */ scanner->aOptions[opt_colormode].size = max_string_size (scanner->list_colormodes); scanner->aOptions[opt_colormode].constraint. string_list = scanner->list_colormodes; scanner->aOptions[opt_depth].constraint.word_list = scanner->list_depths; scanner->aOptions[opt_resolution].constraint.word_list = scanner->list_resolutions; scanner->aOptions[opt_scantype].size = max_string_size (scanner->list_sources); scanner->aOptions[opt_scantype].constraint.string_list = scanner->list_sources; /* default values */ if (scanner->aValues[opt_colormode].s != NULL) free (scanner->aValues[opt_colormode].s); if (scanner->aValues[opt_scantype].s != NULL) free (scanner->aValues[opt_scantype].s); scanner->aValues[opt_colormode].s = strdup (scanner->list_colormodes[0]); scanner->aValues[opt_scantype].s = strdup (scanner->list_sources[0]); scanner->aValues[opt_resolution].w = scanner->list_resolutions[1]; scanner->aValues[opt_depth].w = scanner->list_depths[1]; source = Get_Source (scanner->aValues[opt_scantype].s); coords = Constrains_Get (device, source); if (coords != NULL) { bknd_constrains (scanner, source, 0); bknd_constrains (scanner, source, 1); scanner->aValues[opt_tlx].w = 0; scanner->aValues[opt_tly].w = 0; scanner->aValues[opt_brx].w = coords->width; scanner->aValues[opt_bry].w = coords->height; } } info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } break; case opt_reset: Chipset_Reset (device); break; case opt_realdepth: scanner->aValues[optid].w = (scanner->cnv.real_depth == TRUE) ? SANE_TRUE : SANE_FALSE; break; case opt_infoupdate: if (bknd_info (scanner) == SANE_STATUS_GOOD) info |= SANE_INFO_RELOAD_OPTIONS; break; default: rst = SANE_STATUS_INVAL; break; } if (pInfo != NULL) *pInfo = info; } } return rst; } SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, void *pVal, SANE_Int * pInfo) { TScanner *scanner; SANE_Status rst; DBG (DBG_FNC, "> sane_control_option\n"); scanner = (TScanner *) h; switch (Action) { case SANE_ACTION_GET_VALUE: rst = option_get (scanner, n, pVal); break; case SANE_ACTION_SET_VALUE: rst = option_set (scanner, n, pVal, pInfo); break; case SANE_ACTION_SET_AUTO: rst = SANE_STATUS_UNSUPPORTED; break; default: rst = SANE_STATUS_INVAL; break; } return rst; } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { SANE_Status rst = SANE_STATUS_INVAL; TScanner *s = (TScanner *) h; DBG (DBG_FNC, "+ sane_get_parameters:"); if (s != NULL) { struct st_coords coords; SANE_Int res, source, depth, colormode, frameformat, bpl; /* first do some checks */ /* colormode */ colormode = Get_Colormode (s->aValues[opt_colormode].s); /* frameformat */ frameformat = (colormode == CM_COLOR) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; /* depth */ depth = (colormode == CM_LINEART) ? 1 : s->aValues[opt_depth].w; /* scan type */ source = Get_Source (s->aValues[opt_scantype].s); /* resolution */ res = s->aValues[opt_resolution].w; /* image coordinates in milimeters */ coords.left = s->aValues[opt_tlx].w; coords.top = s->aValues[opt_tly].w; coords.width = s->aValues[opt_brx].w; coords.height = s->aValues[opt_bry].w; /* validate coords */ if (Translate_coords (&coords) == SANE_STATUS_GOOD) { Set_Coordinates (source, res, &coords); if (colormode != CM_LINEART) { bpl = coords.width * ((depth > 8) ? 2 : 1); if (colormode == CM_COLOR) bpl *= 3; /* three channels */ } else bpl = (coords.width + 7) / 8; /* return the data */ p->format = frameformat; p->last_frame = SANE_TRUE; p->depth = depth; p->lines = coords.height; p->pixels_per_line = coords.width; p->bytes_per_line = bpl; DBG (DBG_FNC, " -> Depth : %i\n", depth); DBG (DBG_FNC, " -> Height: %i\n", coords.height); DBG (DBG_FNC, " -> Width : %i\n", coords.width); DBG (DBG_FNC, " -> BPL : %i\n", bpl); rst = SANE_STATUS_GOOD; } } DBG (DBG_FNC, "- sane_get_parameters: %i\n", rst); return rst; } SANE_Status sane_start (SANE_Handle h) { SANE_Status rst = SANE_STATUS_INVAL; TScanner *s; DBG (DBG_FNC, "+ sane_start\n"); s = (TScanner *) h; if (s != NULL) { struct st_coords coords; SANE_Int res, source, colormode, depth, channel; /* first do some checks */ /* Get Scan type */ source = Get_Source (s->aValues[opt_scantype].s); /* Check if scanner supports slides and negatives in case selected source is tma */ if (!((source != ST_NORMAL) && (RTS_isTmaAttached (device) == FALSE))) { /* Get depth */ depth = s->aValues[opt_depth].w; /* Get color mode */ colormode = Get_Colormode (s->aValues[opt_colormode].s); /* Emulating certain color modes */ if (colormode == CM_LINEART) { /* emulate lineart */ s->cnv.colormode = CM_LINEART; colormode = CM_GRAY; depth = 8; } else if ((colormode == CM_GRAY) && (s->aValues[opt_emulategray].w == SANE_TRUE)) { /* emulate grayscale */ s->cnv.colormode = CM_GRAY; colormode = CM_COLOR; } else s->cnv.colormode = -1; /* setting channel for colormodes different than CM_COLOR */ channel = (colormode != CM_COLOR) ? 1 : 0; /* negative colors */ s->cnv.negative = (s->aValues[opt_negative].w == SANE_TRUE) ? TRUE : FALSE; /* Get threshold */ s->cnv.threshold = s->aValues[opt_threshold].w; /* Get resolution */ res = s->aValues[opt_resolution].w; /* set depth emulation */ if (s->cnv.colormode == CM_LINEART) s->cnv.real_depth = TRUE; else s->cnv.real_depth = (s->aValues[opt_realdepth].w == SANE_TRUE) ? TRUE : FALSE; /* use gamma? */ RTS_Debug->EnableGamma = (s->aValues[opt_nogamma].w == SANE_TRUE) ? FALSE : TRUE; /* disable white shading correction? */ RTS_Debug->wshading = (s->aValues[opt_nowshading].w == SANE_TRUE) ? FALSE : TRUE; /* skip warmup process? */ RTS_Debug->warmup = (s->aValues[opt_nowarmup].w == SANE_TRUE) ? FALSE : TRUE; /* save debugging images? */ RTS_Debug->SaveCalibFile = (s->aValues[opt_dbgimages].w == SANE_TRUE) ? TRUE : FALSE; /* Get image coordinates in milimeters */ coords.left = s->aValues[opt_tlx].w; coords.top = s->aValues[opt_tly].w; coords.width = s->aValues[opt_brx].w; coords.height = s->aValues[opt_bry].w; /* Validate coords */ if (Translate_coords (&coords) == SANE_STATUS_GOOD) { /* Stop previusly started scan */ RTS_Scanner_StopScan (device, TRUE); s->ScanParams.scantype = source; s->ScanParams.colormode = colormode; s->ScanParams.resolution_x = res; s->ScanParams.resolution_y = res; s->ScanParams.channel = channel; memcpy (&s->ScanParams.coords, &coords, sizeof (struct st_coords)); Set_Coordinates (source, res, &s->ScanParams.coords); /* emulating depth? */ if ((s->cnv.real_depth == FALSE) && (depth < 16) && (RTS_Debug->EnableGamma == TRUE)) { /* In order to improve image quality, we will scan at 16bits if we are using gamma correction */ s->cnv.depth = depth; s->ScanParams.depth = 16; } else { s->ScanParams.depth = depth; s->cnv.depth = -1; } /* set scanning parameters */ if (RTS_Scanner_SetParams (device, &s->ScanParams) == OK) { /* Start scanning process */ if (RTS_Scanner_StartScan (device) == OK) { /* Allocate buffer to read one line */ s->mylin = 0; rst = img_buffers_alloc (s, bytesperline); } } } } else rst = SANE_STATUS_COVER_OPEN; } DBG (DBG_FNC, "- sane_start: %i\n", rst); return rst; } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { SANE_Status rst = SANE_STATUS_GOOD; TScanner *s = (TScanner *) h; DBG (DBG_FNC, "+ sane_read\n"); if ((s != NULL) && (buf != NULL) && (len != NULL)) { /* nothing has been read at the moment */ *len = 0; /* if we read all the lines return EOF */ if ((s->mylin == s->ScanParams.coords.height) || (device->status->cancel == TRUE)) { rst = (device->status->cancel == TRUE) ? SANE_STATUS_CANCELLED : SANE_STATUS_EOF; RTS_Scanner_StopScan (device, FALSE); img_buffers_free (s); } else { SANE_Int emul_len, emul_maxlen; SANE_Int thwidth, transferred, bufflength; SANE_Byte *buffer, *pbuffer; emul_len = 0; if (s->cnv.depth != -1) emul_maxlen = maxlen * (s->ScanParams.depth / s->cnv.depth); else emul_maxlen = maxlen; /* if grayscale emulation is enabled check that retrieved data is multiple of three */ if (s->cnv.colormode == CM_GRAY) { SANE_Int chn_size, rest; chn_size = (s->ScanParams.depth > 8) ? 2 : 1; rest = emul_maxlen % (3 * chn_size); if (rest != 0) emul_maxlen -= rest; } /* this is important to keep lines alignment in lineart mode */ if (s->cnv.colormode == CM_LINEART) emul_maxlen = s->ScanParams.coords.width; /* if we are emulating depth, we scan at 16bit when frontend waits for 8bit data. Next buffer will be used to retrieve data from scanner prior to convert to 8 bits depth */ buffer = (SANE_Byte *) malloc (emul_maxlen * sizeof (SANE_Byte)); if (buffer != NULL) { pbuffer = buffer; /* get bytes per line */ if (s->ScanParams.colormode != CM_LINEART) { thwidth = s->ScanParams.coords.width * ((s->ScanParams.depth > 8) ? 2 : 1); if (s->ScanParams.colormode == CM_COLOR) thwidth *= 3; /* three channels */ } else thwidth = (s->ScanParams.coords.width + 7) / 8; /* read as many lines the buffer may contain and while there are lines to be read */ while ((emul_len < emul_maxlen) && (s->mylin < s->ScanParams.coords.height)) { /* Is there any data waiting for being passed ? */ if (s->rest_amount != 0) { /* copy to buffer as many bytes as we can */ bufflength = min (emul_maxlen - emul_len, s->rest_amount); memcpy (pbuffer, s->rest, bufflength); emul_len += bufflength; pbuffer += bufflength; s->rest_amount -= bufflength; if (s->rest_amount == 0) s->mylin++; } else { /* read from scanner up to one line */ if (Read_Image (device, bytesperline, s->image, &transferred) != OK) { /* error, exit function */ rst = SANE_STATUS_EOF; break; } /* is there any data? */ if (transferred != 0) { /* copy to buffer as many bytes as we can */ bufflength = min (emul_maxlen - emul_len, thwidth); memcpy (pbuffer, s->image, bufflength); emul_len += bufflength; pbuffer += bufflength; /* the rest will be copied to s->rest buffer */ if (bufflength < thwidth) { s->rest_amount = thwidth - bufflength; memcpy (s->rest, s->image + bufflength, s->rest_amount); } else s->mylin++; } else break; } } /* while */ /* process buffer before sending to frontend */ if ((emul_len > 0) && (rst != SANE_STATUS_EOF)) { /* at this point ... buffer : contains retrieved image emul_len: contains size in bytes of retrieved image after this code ... buf : will contain postprocessed image len : will contain size in bytes of postprocessed image */ /* apply gamma if neccesary */ if (RTS_Debug->EnableGamma == TRUE) gamma_apply (s, buffer, emul_len, s->ScanParams.depth); /* if we are scanning negatives, let's invert colors */ if (s->ScanParams.scantype == ST_NEG) { if (s->cnv.negative == FALSE) Color_Negative (buffer, emul_len, s->ScanParams.depth); } else if (s->cnv.negative != FALSE) Color_Negative (buffer, emul_len, s->ScanParams.depth); /* emulating grayscale ? */ if (s->cnv.colormode == CM_GRAY) { Color_to_Gray (buffer, emul_len, s->ScanParams.depth); emul_len /= 3; } /* emulating depth */ if (s->cnv.depth != -1) { switch (s->cnv.depth) { /* case 1: treated separately as lineart */ /*case 12: in the future */ case 8: Depth_16_to_8 (buffer, emul_len, buffer); emul_len /= 2; break; } } /* lineart mode ? */ if (s->cnv.colormode == CM_LINEART) { /* I didn't see any scanner supporting lineart mode. Windows drivers scan in grayscale and then convert image to lineart so let's perform convertion */ SANE_Int rest = emul_len % 8; Gray_to_Lineart (buffer, emul_len, s->cnv.threshold); emul_len /= 8; if (rest > 0) emul_len++; } /* copy postprocessed image */ *len = emul_len; memcpy (buf, buffer, *len); } free (buffer); } } } else rst = SANE_STATUS_EOF; DBG (DBG_FNC, "- sane_read: %s\n", sane_strstatus (rst)); return rst; } void sane_cancel (SANE_Handle h) { DBG (DBG_FNC, "> sane_cancel\n"); /* silence gcc */ h = h; device->status->cancel = TRUE; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (DBG_FNC, "> sane_set_io_mode\n"); /* silence gcc */ handle = handle; non_blocking = non_blocking; return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (DBG_FNC, "> sane_get_select_fd\n"); /* silence gcc */ handle = handle; fd = fd; return SANE_STATUS_UNSUPPORTED; } void sane_close (SANE_Handle h) { TScanner *scanner = (TScanner *) h; DBG (DBG_FNC, "- sane_close...\n"); /* stop previus scans */ RTS_Scanner_StopScan (device, TRUE); /* close usb */ sanei_usb_close (device->usb_handle); /* free scanner internal variables */ RTS_Scanner_End (device); /* free RTS enviroment */ RTS_Free (device); /* free backend variables */ if (scanner != NULL) { options_free (scanner); img_buffers_free (scanner); } } void sane_exit (void) { /* free device list memory */ if (_pSaneDevList) { TDevListEntry *pDev, *pNext; for (pDev = _pFirstSaneDev; pDev; pDev = pNext) { pNext = pDev->pNext; /* pDev->dev.name is the same pointer that pDev->devname */ free (pDev->devname); free (pDev); } _pFirstSaneDev = NULL; free (_pSaneDevList); _pSaneDevList = NULL; } }