1/* sane - Scanner Access Now Easy. 2 3 This file is part of the SANE package. 4 5 This program is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 2 of the 8 License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18 As a special exception, the authors of SANE give permission for 19 additional uses of the libraries contained in this release of SANE. 20 21 The exception is that, if you link a SANE library with other files 22 to produce an executable, this does not by itself cause the 23 resulting executable to be covered by the GNU General Public 24 License. Your use of that executable is in no way restricted on 25 account of linking the SANE library code into it. 26 27 This exception does not, however, invalidate any other reasons why 28 the executable file might be covered by the GNU General Public 29 License. 30 31 If you submit changes to SANE to the maintainers to be included in 32 a subsequent release, you agree by submitting the changes that 33 those changes may be distributed with this exception intact. 34 35 If you write modifications of your own for SANE, it is your choice 36 whether to permit this exception to apply to your modifications. 37 If you do not wish that, delete this exception notice. 38*/ 39 40/* 41 This file implements a SANE backend for the Ibm 2456 flatbed scanner, 42 written by mf <massifr@tiscalinet.it>. It derives from the backend for 43 Ricoh flatbed scanners written by Feico W. Dillema. 44 45 Currently maintained by Henning Meier-Geinitz <henning@meier-geinitz.de>. 46*/ 47 48#define BUILD 5 49 50#include "../include/sane/config.h" 51 52#include <limits.h> 53#include <stdlib.h> 54#include <stdarg.h> 55#include <string.h> 56#include <sys/time.h> 57#include <unistd.h> 58#include <ctype.h> 59 60#include "../include/sane/sane.h" 61#include "../include/sane/saneopts.h" 62#include "../include/sane/sanei_scsi.h" 63 64#define BACKEND_NAME ibm 65#include "../include/sane/sanei_backend.h" 66 67#ifndef PATH_MAX 68# define PATH_MAX 1024 69#endif 70 71#include "../include/sane/sanei_config.h" 72#define IBM_CONFIG_FILE "ibm.conf" 73 74#include "ibm.h" 75#include "ibm-scsi.c" 76 77#define MAX(a,b) ((a) > (b) ? (a) : (b)) 78 79static int num_devices = 0; 80static Ibm_Device *first_dev = NULL; 81static Ibm_Scanner *first_handle = NULL; 82/* static int is50 = 0; */ 83 84 85static size_t 86max_string_size (const SANE_String_Const strings[]) 87{ 88 size_t size, max_size = 0; 89 int i; 90 DBG (11, ">> max_string_size\n"); 91 92 for (i = 0; strings[i]; ++i) 93 { 94 size = strlen (strings[i]) + 1; 95 if (size > max_size) 96 max_size = size; 97 } 98 99 DBG (11, "<< max_string_size\n"); 100 return max_size; 101} 102 103static SANE_Status 104attach (const char *devnam, Ibm_Device ** devp) 105{ 106 SANE_Status status; 107 Ibm_Device *dev; 108 109 int fd; 110 struct inquiry_data ibuf; 111 struct measurements_units_page mup; 112 struct ibm_window_data wbuf; 113 size_t buf_size; 114 char *str; 115 DBG (11, ">> attach\n"); 116 117 for (dev = first_dev; dev; dev = dev->next) 118 { 119 if (strcmp (dev->sane.name, devnam) == 0) 120 { 121 if (devp) 122 *devp = dev; 123 return (SANE_STATUS_GOOD); 124 } 125 } 126 127 DBG (3, "attach: opening %s\n", devnam); 128 status = sanei_scsi_open (devnam, &fd, NULL, NULL); 129 if (status != SANE_STATUS_GOOD) 130 { 131 DBG (1, "attach: open failed: %s\n", sane_strstatus (status)); 132 return (status); 133 } 134 135 DBG (3, "attach: sending INQUIRY\n"); 136 memset (&ibuf, 0, sizeof (ibuf)); 137 buf_size = sizeof(ibuf); 138/* next line by mf */ 139 ibuf.byte2 = 2; 140 status = inquiry (fd, &ibuf, &buf_size); 141 if (status != SANE_STATUS_GOOD) 142 { 143 DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status)); 144 sanei_scsi_close (fd); 145 return (status); 146 } 147 148 if (ibuf.devtype != 6) 149 { 150 DBG (1, "attach: device \"%s\" is not a scanner\n", devnam); 151 sanei_scsi_close (fd); 152 return (SANE_STATUS_INVAL); 153 } 154 155 if (!( 156 (strncmp ((char *)ibuf.vendor, "IBM", 3) ==0 157 && strncmp ((char *)ibuf.product, "2456", 4) == 0) 158 || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0 159 && strncmp ((char *)ibuf.product, "IS420", 5) == 0) 160 || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0 161 && strncmp ((char *)ibuf.product, "IS410", 5) == 0) 162 || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0 163 && strncmp ((char *)ibuf.product, "IS430", 5) == 0) 164 )) 165 { 166 DBG (1, "attach: device \"%s\" doesn't look like a scanner I know\n", 167 devnam); 168 sanei_scsi_close (fd); 169 return (SANE_STATUS_INVAL); 170 } 171 172 DBG (3, "attach: sending TEST_UNIT_READY\n"); 173 status = test_unit_ready (fd); 174 if (status != SANE_STATUS_GOOD) 175 { 176 DBG (1, "attach: test unit ready failed (%s)\n", 177 sane_strstatus (status)); 178 sanei_scsi_close (fd); 179 return (status); 180 } 181 /* 182 * Causes a problem with RICOH IS420 183 * Ignore this function ... seems to work ok 184 * Suggested to George Murphy george@topfloor.ie by henning 185 */ 186 if (strncmp((char *)ibuf.vendor, "RICOH", 5) != 0 187 && strncmp((char *)ibuf.product, "IS420", 5) != 0) 188 { 189 DBG (3, "attach: sending OBJECT POSITION\n"); 190 status = object_position (fd, OBJECT_POSITION_UNLOAD); 191 if (status != SANE_STATUS_GOOD) 192 { 193 DBG (1, "attach: OBJECT POSITION failed\n"); 194 sanei_scsi_close (fd); 195 return (SANE_STATUS_INVAL); 196 } 197 } 198 199 memset (&mup, 0, sizeof (mup)); 200 mup.page_code = MEASUREMENTS_PAGE; 201 mup.parameter_length = 0x06; 202 mup.bmu = INCHES; 203 mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff; 204 mup.mud[1] = (DEFAULT_MUD & 0xff); 205 206#if 0 207 DBG (3, "attach: sending MODE SELECT\n"); 208 status = mode_select (fd, (struct mode_pages *) &mup); 209 if (status != SANE_STATUS_GOOD) 210 { 211 DBG (1, "attach: MODE_SELECT failed\n"); 212 sanei_scsi_close (fd); 213 return (SANE_STATUS_INVAL); 214 } 215#endif 216 217#if 0 218 DBG (3, "attach: sending MODE SENSE\n"); 219 memset (&mup, 0, sizeof (mup)); 220 status = mode_sense (fd, (struct mode_pages *) &mup, PC_CURRENT | MEASUREMENTS_PAGE); 221 if (status != SANE_STATUS_GOOD) 222 { 223 DBG (1, "attach: MODE_SENSE failed\n"); 224 sanei_scsi_close (fd); 225 return (SANE_STATUS_INVAL); 226 } 227#endif 228 229 DBG (3, "attach: sending GET WINDOW\n"); 230 memset (&wbuf, 0, sizeof (wbuf)); 231 status = get_window (fd, &wbuf); 232 if (status != SANE_STATUS_GOOD) 233 { 234 DBG (1, "attach: GET_WINDOW failed %d\n", status); 235 sanei_scsi_close (fd); 236 DBG (11, "<< attach\n"); 237 return (SANE_STATUS_INVAL); 238 } 239 240 sanei_scsi_close (fd); 241 242 dev = malloc (sizeof (*dev)); 243 if (!dev) 244 return (SANE_STATUS_NO_MEM); 245 memset (dev, 0, sizeof (*dev)); 246 247 dev->sane.name = strdup (devnam); 248 dev->sane.vendor = "IBM"; 249 250 size_t prod_rev_size = sizeof(ibuf.product) + sizeof(ibuf.revision) + 1; 251 str = malloc (prod_rev_size); 252 if (str) 253 { 254 snprintf (str, prod_rev_size, "%.*s%.*s", 255 (int) sizeof(ibuf.product), (const char *) ibuf.product, 256 (int) sizeof(ibuf.revision), (const char *) ibuf.revision); 257 } 258 dev->sane.model = str; 259 dev->sane.type = "flatbed scanner"; 260 261 DBG (5, "dev->sane.name = %s\n", dev->sane.name); 262 DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor); 263 DBG (5, "dev->sane.model = %s\n", dev->sane.model); 264 DBG (5, "dev->sane.type = %s\n", dev->sane.type); 265 266 dev->info.xres_default = _2btol(wbuf.x_res); 267 dev->info.yres_default = _2btol(wbuf.y_res); 268 dev->info.image_mode_default = wbuf.image_comp; 269 270 /* if you throw the MRIF bit the brightness control reverses too */ 271 /* so I reverse the reversal in software for symmetry's sake */ 272 /* I should make this into an option */ 273 274 if (wbuf.image_comp == IBM_GRAYSCALE || wbuf.image_comp == IBM_DITHERED_MONOCHROME) 275 { 276 dev->info.brightness_default = 256 - wbuf.brightness; 277/* 278 if (is50) 279 dev->info.contrast_default = wbuf.contrast; 280 else 281*/ 282 dev->info.contrast_default = 256 - wbuf.contrast; 283 } 284 else /* wbuf.image_comp == IBM_BINARY_MONOCHROME */ 285 { 286 dev->info.brightness_default = wbuf.brightness; 287 dev->info.contrast_default = wbuf.contrast; 288 } 289 290/* da rivedere 291 dev->info.adf_default = wbuf.adf_state; 292*/ 293 dev->info.adf_default = ADF_UNUSED; 294 dev->info.adf_default = IBM_PAPER_USER_DEFINED; 295 296#if 1 297 dev->info.bmu = mup.bmu; 298 dev->info.mud = _2btol(mup.mud); 299 if (dev->info.mud == 0) { 300 /* The Ricoh says it uses points as default Basic Measurement Unit */ 301 /* but gives a Measurement Unit Divisor of zero */ 302 /* So, we set it to the default (SCSI-standard) of 1200 */ 303 /* with BMU in inches, i.e. 1200 points equal 1 inch */ 304 dev->info.bmu = INCHES; 305 dev->info.mud = DEFAULT_MUD; 306 } 307#else 308 dev->info.bmu = INCHES; 309 dev->info.mud = DEFAULT_MUD; 310#endif 311 312 DBG (5, "xres_default=%d\n", dev->info.xres_default); 313 DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max); 314 DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min); 315 316 DBG (5, "yres_default=%d\n", dev->info.yres_default); 317 DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max); 318 DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min); 319 320 DBG (5, "x_range.max=%d\n", dev->info.x_range.max); 321 DBG (5, "y_range.max=%d\n", dev->info.y_range.max); 322 323 DBG (5, "image_mode=%d\n", dev->info.image_mode_default); 324 325 DBG (5, "brightness=%d\n", dev->info.brightness_default); 326 DBG (5, "contrast=%d\n", dev->info.contrast_default); 327 328 DBG (5, "adf_state=%d\n", dev->info.adf_default); 329 330 DBG (5, "bmu=%d\n", dev->info.bmu); 331 DBG (5, "mud=%d\n", dev->info.mud); 332 333 ++num_devices; 334 dev->next = first_dev; 335 first_dev = dev; 336 337 if (devp) 338 *devp = dev; 339 340 DBG (11, "<< attach\n"); 341 return (SANE_STATUS_GOOD); 342} 343 344static SANE_Status 345attach_one(const char *devnam) 346{ 347 attach (devnam, NULL); 348 return SANE_STATUS_GOOD; 349} 350 351static SANE_Status 352init_options (Ibm_Scanner * s) 353{ 354 int i; 355 DBG (11, ">> init_options\n"); 356 357 memset (s->opt, 0, sizeof (s->opt)); 358 memset (s->val, 0, sizeof (s->val)); 359 360 for (i = 0; i < NUM_OPTIONS; ++i) 361 { 362 s->opt[i].size = sizeof (SANE_Word); 363 s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; 364 } 365 366 s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; 367 s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; 368 s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; 369 s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; 370 s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; 371 372 /* "Mode" group: */ 373 s->opt[OPT_MODE_GROUP].title = "Scan Mode"; 374 s->opt[OPT_MODE_GROUP].desc = ""; 375 s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; 376 s->opt[OPT_MODE_GROUP].cap = 0; 377 s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; 378 379 /* scan mode */ 380 s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 381 s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; 382 s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; 383 s->opt[OPT_MODE].type = SANE_TYPE_STRING; 384 s->opt[OPT_MODE].size = max_string_size (mode_list); 385 s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; 386 s->opt[OPT_MODE].constraint.string_list = mode_list; 387 s->val[OPT_MODE].s = strdup (mode_list[s->hw->info.image_mode_default]); 388 389 /* x resolution */ 390 s->opt[OPT_X_RESOLUTION].name = "X" SANE_NAME_SCAN_RESOLUTION; 391 s->opt[OPT_X_RESOLUTION].title = "X " SANE_TITLE_SCAN_RESOLUTION; 392 s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; 393 s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; 394 s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; 395 s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; 396 s->opt[OPT_X_RESOLUTION].constraint.range = &ibm2456_res_range; 397 s->val[OPT_X_RESOLUTION].w = s->hw->info.xres_default; 398/* 399 if (is50) 400 s->opt[OPT_X_RESOLUTION].constraint.range = &is50_res_range; 401 else 402*/ 403 s->opt[OPT_X_RESOLUTION].constraint.range = &ibm2456_res_range; 404 405 /* y resolution */ 406 s->opt[OPT_Y_RESOLUTION].name = "Y" SANE_NAME_SCAN_RESOLUTION; 407 s->opt[OPT_Y_RESOLUTION].title = "Y " SANE_TITLE_SCAN_RESOLUTION; 408 s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; 409 s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; 410 s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; 411 s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; 412 s->val[OPT_Y_RESOLUTION].w = s->hw->info.yres_default; 413 s->opt[OPT_Y_RESOLUTION].constraint.range = &ibm2456_res_range; 414 415 /* adf */ 416 s->opt[OPT_ADF].name = "adf"; 417 s->opt[OPT_ADF].title = "Use ADF"; 418 s->opt[OPT_ADF].desc = "Uses the automatic document feeder."; 419 s->opt[OPT_ADF].type = SANE_TYPE_BOOL; 420 s->opt[OPT_ADF].unit = SANE_UNIT_NONE; 421 s->opt[OPT_ADF].constraint_type = SANE_CONSTRAINT_NONE; 422 s->val[OPT_ADF].b = s->hw->info.adf_default; 423 424 /* "Geometry" group: */ 425 s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; 426 s->opt[OPT_GEOMETRY_GROUP].desc = ""; 427 s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; 428 s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; 429 s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; 430 431 /* paper */ 432 s->opt[OPT_PAPER].name = "paper"; 433 s->opt[OPT_PAPER].title = "Paper format"; 434 s->opt[OPT_PAPER].desc = "Sets the paper format."; 435 s->opt[OPT_PAPER].type = SANE_TYPE_STRING; 436 s->opt[OPT_PAPER].size = max_string_size (paper_list); 437 s->opt[OPT_PAPER].constraint_type = SANE_CONSTRAINT_STRING_LIST; 438 s->opt[OPT_PAPER].constraint.string_list = paper_list; 439 s->val[OPT_PAPER].s = strdup (paper_list[s->hw->info.paper_default]); 440 441 /* top-left x */ 442 s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; 443 s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; 444 s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; 445 s->opt[OPT_TL_X].type = SANE_TYPE_INT; 446 s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL; 447 s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; 448 s->opt[OPT_TL_X].constraint.range = &default_x_range; 449 s->val[OPT_TL_X].w = 0; 450 451 /* top-left y */ 452 s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; 453 s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; 454 s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; 455 s->opt[OPT_TL_Y].type = SANE_TYPE_INT; 456 s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL; 457 s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; 458 s->opt[OPT_TL_Y].constraint.range = &default_y_range; 459 s->val[OPT_TL_Y].w = 0; 460 461 /* bottom-right x */ 462 s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; 463 s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; 464 s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; 465 s->opt[OPT_BR_X].type = SANE_TYPE_INT; 466 s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL; 467 s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; 468 s->opt[OPT_BR_X].constraint.range = &default_x_range; 469 s->val[OPT_BR_X].w = default_x_range.max; 470 471 /* bottom-right y */ 472 s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; 473 s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; 474 s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; 475 s->opt[OPT_BR_Y].type = SANE_TYPE_INT; 476 s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL; 477 s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; 478 s->opt[OPT_BR_Y].constraint.range = &default_y_range; 479 s->val[OPT_BR_Y].w = default_y_range.max; 480 481 /* "Enhancement" group: */ 482 s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; 483 s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; 484 s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; 485 s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; 486 s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; 487 488 /* brightness */ 489 s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; 490 s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; 491 s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; 492 s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; 493 s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; 494 s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; 495 s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range; 496 s->val[OPT_BRIGHTNESS].w = s->hw->info.brightness_default; 497 498 /* contrast */ 499 s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; 500 s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; 501 s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; 502 s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; 503 s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; 504 s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; 505 s->opt[OPT_CONTRAST].constraint.range = &u8_range; 506 s->val[OPT_CONTRAST].w = s->hw->info.contrast_default; 507 508 DBG (11, "<< init_options\n"); 509 return SANE_STATUS_GOOD; 510} 511 512static SANE_Status 513do_cancel (Ibm_Scanner * s) 514{ 515 SANE_Status status; 516 DBG (11, ">> do_cancel\n"); 517 518 DBG (3, "cancel: sending OBJECT POSITION\n"); 519 status = object_position (s->fd, OBJECT_POSITION_UNLOAD); 520 if (status != SANE_STATUS_GOOD) 521 { 522 DBG (1, "cancel: OBJECT POSITION failed\n"); 523 } 524 525 s->scanning = SANE_FALSE; 526 527 if (s->fd >= 0) 528 { 529 sanei_scsi_close (s->fd); 530 s->fd = -1; 531 } 532 533 DBG (11, "<< do_cancel\n"); 534 return (SANE_STATUS_CANCELLED); 535} 536 537SANE_Status 538sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) 539{ 540 char devnam[PATH_MAX] = "/dev/scanner"; 541 FILE *fp; 542 543 DBG_INIT (); 544 DBG (11, ">> sane_init (authorize %s null)\n", (authorize) ? "!=" : "=="); 545 546#if defined PACKAGE && defined VERSION 547 DBG (2, "sane_init: ibm backend version %d.%d-%d (" 548 PACKAGE " " VERSION ")\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); 549#endif 550 551 if (version_code) 552 *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); 553 554 fp = sanei_config_open(IBM_CONFIG_FILE); 555 if (fp) 556 { 557 char line[PATH_MAX], *lp; 558 size_t len; 559 560 /* read config file */ 561 while (sanei_config_read (line, sizeof (line), fp)) 562 { 563 if (line[0] == '#') /* ignore line comments */ 564 continue; 565 len = strlen (line); 566 567 if (!len) 568 continue; /* ignore empty lines */ 569 570 /* skip white space: */ 571 for (lp = line; isspace(*lp); ++lp) 572 ; 573 strcpy (devnam, lp); 574 } 575 fclose (fp); 576 } 577 sanei_config_attach_matching_devices (devnam, attach_one); 578 DBG (11, "<< sane_init\n"); 579 return SANE_STATUS_GOOD; 580} 581 582void 583sane_exit (void) 584{ 585 Ibm_Device *dev, *next; 586 DBG (11, ">> sane_exit\n"); 587 588 for (dev = first_dev; dev; dev = next) 589 { 590 next = dev->next; 591 free ((void *) dev->sane.name); 592 free ((void *) dev->sane.model); 593 free (dev); 594 } 595 596 DBG (11, "<< sane_exit\n"); 597} 598 599SANE_Status 600sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) 601{ 602 static const SANE_Device **devlist = 0; 603 Ibm_Device *dev; 604 int i; 605 DBG (11, ">> sane_get_devices (local_only = %d)\n", local_only); 606 607 if (devlist) 608 free (devlist); 609 devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); 610 if (!devlist) 611 return (SANE_STATUS_NO_MEM); 612 613 i = 0; 614 for (dev = first_dev; dev; dev = dev->next) 615 devlist[i++] = &dev->sane; 616 devlist[i++] = 0; 617 618 *device_list = devlist; 619 620 DBG (11, "<< sane_get_devices\n"); 621 return SANE_STATUS_GOOD; 622} 623 624SANE_Status 625sane_open (SANE_String_Const devnam, SANE_Handle * handle) 626{ 627 SANE_Status status; 628 Ibm_Device *dev; 629 Ibm_Scanner *s; 630 DBG (11, ">> sane_open\n"); 631 632 if (devnam[0] == '\0') 633 { 634 for (dev = first_dev; dev; dev = dev->next) 635 { 636 if (strcmp (dev->sane.name, devnam) == 0) 637 break; 638 } 639 640 if (!dev) 641 { 642 status = attach (devnam, &dev); 643 if (status != SANE_STATUS_GOOD) 644 return (status); 645 } 646 } 647 else 648 { 649 dev = first_dev; 650 } 651 652 if (!dev) 653 return (SANE_STATUS_INVAL); 654 655 s = malloc (sizeof (*s)); 656 if (!s) 657 return SANE_STATUS_NO_MEM; 658 memset (s, 0, sizeof (*s)); 659 660 s->fd = -1; 661 s->hw = dev; 662 663 init_options (s); 664 665 s->next = first_handle; 666 first_handle = s; 667 668 *handle = s; 669 670 DBG (11, "<< sane_open\n"); 671 return SANE_STATUS_GOOD; 672} 673 674void 675sane_close (SANE_Handle handle) 676{ 677 Ibm_Scanner *s = (Ibm_Scanner *) handle; 678 DBG (11, ">> sane_close\n"); 679 680 if (s->fd != -1) 681 sanei_scsi_close (s->fd); 682 free (s); 683 684 DBG (11, ">> sane_close\n"); 685} 686 687const SANE_Option_Descriptor * 688sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) 689{ 690 Ibm_Scanner *s = handle; 691 DBG (11, ">> sane_get_option_descriptor\n"); 692 693 if ((unsigned) option >= NUM_OPTIONS) 694 return (0); 695 696 DBG (11, "<< sane_get_option_descriptor\n"); 697 return (s->opt + option); 698} 699 700SANE_Status 701sane_control_option (SANE_Handle handle, SANE_Int option, 702 SANE_Action action, void *val, SANE_Int * info) 703{ 704 Ibm_Scanner *s = handle; 705 SANE_Status status; 706 SANE_Word cap; 707 DBG (11, ">> sane_control_option\n"); 708 709 if (info) 710 *info = 0; 711 712 if (s->scanning) 713 return (SANE_STATUS_DEVICE_BUSY); 714 if (option >= NUM_OPTIONS) 715 return (SANE_STATUS_INVAL); 716 717 cap = s->opt[option].cap; 718 if (!SANE_OPTION_IS_ACTIVE (cap)) 719 return (SANE_STATUS_INVAL); 720 721 if (action == SANE_ACTION_GET_VALUE) 722 { 723 DBG (11, "sane_control_option get_value\n"); 724 switch (option) 725 { 726 /* word options: */ 727 case OPT_X_RESOLUTION: 728 case OPT_Y_RESOLUTION: 729 case OPT_TL_X: 730 case OPT_TL_Y: 731 case OPT_BR_X: 732 case OPT_BR_Y: 733 case OPT_NUM_OPTS: 734 case OPT_BRIGHTNESS: 735 case OPT_CONTRAST: 736 *(SANE_Word *) val = s->val[option].w; 737 return (SANE_STATUS_GOOD); 738 739 /* bool options: */ 740 case OPT_ADF: 741 *(SANE_Bool *) val = s->val[option].b; 742 return (SANE_STATUS_GOOD); 743 744 /* string options: */ 745 case OPT_MODE: 746 case OPT_PAPER: 747 strcpy (val, s->val[option].s); 748 return (SANE_STATUS_GOOD); 749 } 750 } 751 else { 752 DBG (11, "sane_control_option set_value\n"); 753 if (action == SANE_ACTION_SET_VALUE) 754 { 755 if (!SANE_OPTION_IS_SETTABLE (cap)) 756 return (SANE_STATUS_INVAL); 757 758 status = sanei_constrain_value (s->opt + option, val, info); 759 if (status != SANE_STATUS_GOOD) 760 return status; 761 762 switch (option) 763 { 764 /* (mostly) side-effect-free word options: */ 765 case OPT_X_RESOLUTION: 766 case OPT_Y_RESOLUTION: 767 if (info && s->val[option].w != *(SANE_Word *) val) 768 *info |= SANE_INFO_RELOAD_PARAMS; 769 s->val[option].w = *(SANE_Word *) val; 770 return (SANE_STATUS_GOOD); 771 772 case OPT_TL_X: 773 case OPT_TL_Y: 774 case OPT_BR_X: 775 case OPT_BR_Y: 776 if (info && s->val[option].w != *(SANE_Word *) val) 777 *info |= SANE_INFO_RELOAD_PARAMS; 778 s->val[option].w = *(SANE_Word *) val; 779 /* resets the paper format to user defined */ 780 if (strcmp(s->val[OPT_PAPER].s, paper_list[IBM_PAPER_USER_DEFINED]) != 0) 781 { 782 if (info) 783 *info |= SANE_INFO_RELOAD_OPTIONS; 784 if (s->val[OPT_PAPER].s) 785 free (s->val[OPT_PAPER].s); 786 s->val[OPT_PAPER].s = strdup (paper_list[IBM_PAPER_USER_DEFINED]); 787 } 788 return (SANE_STATUS_GOOD); 789 790 case OPT_NUM_OPTS: 791 case OPT_BRIGHTNESS: 792 case OPT_CONTRAST: 793 s->val[option].w = *(SANE_Word *) val; 794 return (SANE_STATUS_GOOD); 795 796 case OPT_MODE: 797 if (info && strcmp (s->val[option].s, (SANE_String) val)) 798 *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; 799 if (s->val[option].s) 800 free (s->val[option].s); 801 s->val[option].s = strdup (val); 802 return (SANE_STATUS_GOOD); 803 804 case OPT_ADF: 805 s->val[option].b = *(SANE_Bool *) val; 806 if (*(SANE_Bool *) val) 807 s->adf_state = ADF_ARMED; 808 else 809 s->adf_state = ADF_UNUSED; 810 return (SANE_STATUS_GOOD); 811 812 case OPT_PAPER: 813 if (info && strcmp (s->val[option].s, (SANE_String) val)) 814 *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; 815 if (s->val[option].s) 816 free (s->val[option].s); 817 s->val[option].s = strdup (val); 818 if (strcmp (s->val[OPT_PAPER].s, "User") != 0) 819 { 820 s->val[OPT_TL_X].w = 0; 821 s->val[OPT_TL_Y].w = 0; 822 if (strcmp (s->val[OPT_PAPER].s, "A3") == 0) 823 { 824 s->val[OPT_BR_X].w = PAPER_A3_W; 825 s->val[OPT_BR_Y].w = PAPER_A3_H; 826 } 827 else if (strcmp (s->val[OPT_PAPER].s, "A4") == 0) 828 { 829 s->val[OPT_BR_X].w = PAPER_A4_W; 830 s->val[OPT_BR_Y].w = PAPER_A4_H; 831 } 832 else if (strcmp (s->val[OPT_PAPER].s, "A4R") == 0) 833 { 834 s->val[OPT_BR_X].w = PAPER_A4R_W; 835 s->val[OPT_BR_Y].w = PAPER_A4R_H; 836 } 837 else if (strcmp (s->val[OPT_PAPER].s, "A5") == 0) 838 { 839 s->val[OPT_BR_X].w = PAPER_A5_W; 840 s->val[OPT_BR_Y].w = PAPER_A5_H; 841 } 842 else if (strcmp (s->val[OPT_PAPER].s, "A5R") == 0) 843 { 844 s->val[OPT_BR_X].w = PAPER_A5R_W; 845 s->val[OPT_BR_Y].w = PAPER_A5R_H; 846 } 847 else if (strcmp (s->val[OPT_PAPER].s, "A6") == 0) 848 { 849 s->val[OPT_BR_X].w = PAPER_A6_W; 850 s->val[OPT_BR_Y].w = PAPER_A6_H; 851 } 852 else if (strcmp (s->val[OPT_PAPER].s, "B4") == 0) 853 { 854 s->val[OPT_BR_X].w = PAPER_B4_W; 855 s->val[OPT_BR_Y].w = PAPER_B4_H; 856 } 857 else if (strcmp (s->val[OPT_PAPER].s, "Legal") == 0) 858 { 859 s->val[OPT_BR_X].w = PAPER_LEGAL_W; 860 s->val[OPT_BR_Y].w = PAPER_LEGAL_H; 861 } 862 else if (strcmp (s->val[OPT_PAPER].s, "Letter") == 0) 863 { 864 s->val[OPT_BR_X].w = PAPER_LETTER_W; 865 s->val[OPT_BR_Y].w = PAPER_LETTER_H; 866 } 867 } 868 return (SANE_STATUS_GOOD); 869 } 870 871 } 872 } 873 874 DBG (11, "<< sane_control_option\n"); 875 return (SANE_STATUS_INVAL); 876} 877 878SANE_Status 879sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) 880{ 881 Ibm_Scanner *s = handle; 882 DBG (11, ">> sane_get_parameters\n"); 883 884 if (!s->scanning) 885 { 886 int width, length, xres, yres; 887 const char *mode; 888 889 memset (&s->params, 0, sizeof (s->params)); 890 891 width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w; 892 length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w; 893 xres = s->val[OPT_X_RESOLUTION].w; 894 yres = s->val[OPT_Y_RESOLUTION].w; 895 896 /* make best-effort guess at what parameters will look like once 897 scanning starts. */ 898 if (xres > 0 && yres > 0 && width > 0 && length > 0) 899 { 900 s->params.pixels_per_line = width * xres / s->hw->info.mud; 901 s->params.lines = length * yres / s->hw->info.mud; 902 } 903 904 mode = s->val[OPT_MODE].s; 905 if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) || 906 (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE)) == 0) 907 { 908 s->params.format = SANE_FRAME_GRAY; 909 s->params.bytes_per_line = s->params.pixels_per_line / 8; 910 /* the Ibm truncates to the byte boundary, so: chop! */ 911 s->params.pixels_per_line = s->params.bytes_per_line * 8; 912 s->params.depth = 1; 913 } 914 else /* if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) */ 915 { 916 s->params.format = SANE_FRAME_GRAY; 917 s->params.bytes_per_line = s->params.pixels_per_line; 918 s->params.depth = 8; 919 } 920 s->params.last_frame = SANE_TRUE; 921 } 922 else 923 DBG (5, "sane_get_parameters: scanning, so can't get params\n"); 924 925 if (params) 926 *params = s->params; 927 928 DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, " 929 "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, 930 s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w); 931 932 DBG (11, "<< sane_get_parameters\n"); 933 return (SANE_STATUS_GOOD); 934} 935 936 937SANE_Status 938sane_start (SANE_Handle handle) 939{ 940 char *mode_str; 941 Ibm_Scanner *s = handle; 942 SANE_Status status; 943 struct ibm_window_data wbuf; 944 struct measurements_units_page mup; 945 946 DBG (11, ">> sane_start\n"); 947 948 /* First make sure we have a current parameter set. Some of the 949 parameters will be overwritten below, but that's OK. */ 950 status = sane_get_parameters (s, 0); 951 if (status != SANE_STATUS_GOOD) 952 return status; 953 954 status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0); 955 if (status != SANE_STATUS_GOOD) 956 { 957 DBG (1, "open of %s failed: %s\n", 958 s->hw->sane.name, sane_strstatus (status)); 959 return (status); 960 } 961 962 mode_str = s->val[OPT_MODE].s; 963 s->xres = s->val[OPT_X_RESOLUTION].w; 964 s->yres = s->val[OPT_Y_RESOLUTION].w; 965 s->ulx = s->val[OPT_TL_X].w; 966 s->uly = s->val[OPT_TL_Y].w; 967 s->width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w; 968 s->length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w; 969 s->brightness = s->val[OPT_BRIGHTNESS].w; 970 s->contrast = s->val[OPT_CONTRAST].w; 971 s->bpp = s->params.depth; 972 if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART) == 0) 973 { 974 s->image_composition = IBM_BINARY_MONOCHROME; 975 } 976 else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) 977 { 978 s->image_composition = IBM_DITHERED_MONOCHROME; 979 } 980 else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY) == 0) 981 { 982 s->image_composition = IBM_GRAYSCALE; 983 } 984 985 memset (&wbuf, 0, sizeof (wbuf)); 986/* next line commented out by mf */ 987/* _lto2b(sizeof(wbuf) - 8, wbuf.len); */ 988/* next line by mf */ 989 _lto2b(IBM_WINDOW_DATA_SIZE, wbuf.len); /* size=320 */ 990 _lto2b(s->xres, wbuf.x_res); 991 _lto2b(s->yres, wbuf.y_res); 992 _lto4b(s->ulx, wbuf.x_org); 993 _lto4b(s->uly, wbuf.y_org); 994 _lto4b(s->width, wbuf.width); 995 _lto4b(s->length, wbuf.length); 996 997 wbuf.image_comp = s->image_composition; 998 /* if you throw the MRIF bit the brightness control reverses too */ 999 /* so I reverse the reversal in software for symmetry's sake */ 1000 if (wbuf.image_comp == IBM_GRAYSCALE || wbuf.image_comp == IBM_DITHERED_MONOCHROME) 1001 { 1002 if (wbuf.image_comp == IBM_GRAYSCALE) 1003 wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x80; /* it was 0x90 */ 1004 if (wbuf.image_comp == IBM_DITHERED_MONOCHROME) 1005 wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x10; 1006 wbuf.brightness = 256 - (SANE_Byte) s->brightness; 1007/* 1008 if (is50) 1009 wbuf.contrast = (SANE_Byte) s->contrast; 1010 else 1011*/ 1012 wbuf.contrast = 256 - (SANE_Byte) s->contrast; 1013 } 1014 else /* wbuf.image_comp == IBM_BINARY_MONOCHROME */ 1015 { 1016 wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x00; 1017 wbuf.brightness = (SANE_Byte) s->brightness; 1018 wbuf.contrast = (SANE_Byte) s->contrast; 1019 } 1020 1021 wbuf.threshold = 0; 1022 wbuf.bits_per_pixel = s->bpp; 1023 1024 wbuf.halftone_code = 2; /* diithering */ 1025 wbuf.halftone_id = 0x0A; /* 8x8 Bayer pattenr */ 1026 wbuf.pad_type = 3; 1027 wbuf.bit_ordering[0] = 0; 1028 wbuf.bit_ordering[1] = 7; /* modified by mf (it was 3) */ 1029 1030 DBG (5, "xres=%d\n", _2btol(wbuf.x_res)); 1031 DBG (5, "yres=%d\n", _2btol(wbuf.y_res)); 1032 DBG (5, "ulx=%d\n", _4btol(wbuf.x_org)); 1033 DBG (5, "uly=%d\n", _4btol(wbuf.y_org)); 1034 DBG (5, "width=%d\n", _4btol(wbuf.width)); 1035 DBG (5, "length=%d\n", _4btol(wbuf.length)); 1036 DBG (5, "image_comp=%d\n", wbuf.image_comp); 1037 1038 DBG (11, "sane_start: sending SET WINDOW\n"); 1039 status = set_window (s->fd, &wbuf); 1040 if (status != SANE_STATUS_GOOD) 1041 { 1042 DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status)); 1043 return (status); 1044 } 1045 1046 DBG (11, "sane_start: sending GET WINDOW\n"); 1047 memset (&wbuf, 0, sizeof (wbuf)); 1048 status = get_window (s->fd, &wbuf); 1049 if (status != SANE_STATUS_GOOD) 1050 { 1051 DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status)); 1052 return (status); 1053 } 1054 DBG (5, "xres=%d\n", _2btol(wbuf.x_res)); 1055 DBG (5, "yres=%d\n", _2btol(wbuf.y_res)); 1056 DBG (5, "ulx=%d\n", _4btol(wbuf.x_org)); 1057 DBG (5, "uly=%d\n", _4btol(wbuf.y_org)); 1058 DBG (5, "width=%d\n", _4btol(wbuf.width)); 1059 DBG (5, "length=%d\n", _4btol(wbuf.length)); 1060 DBG (5, "image_comp=%d\n", wbuf.image_comp); 1061 1062 DBG (11, "sane_start: sending MODE SELECT\n"); 1063 memset (&mup, 0, sizeof (mup)); 1064 mup.page_code = MEASUREMENTS_PAGE; 1065 mup.parameter_length = 0x06; 1066 mup.bmu = INCHES; 1067 mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff; 1068 mup.mud[1] = (DEFAULT_MUD & 0xff); 1069/* next lines by mf */ 1070 mup.adf_page_code = 0x26; 1071 mup.adf_parameter_length = 6; 1072 if (s->adf_state == ADF_ARMED) 1073 mup.adf_control = 1; 1074 else 1075 mup.adf_control = 0; 1076/* end lines by mf */ 1077 1078 status = mode_select (s->fd, (struct mode_pages *) &mup); 1079 if (status != SANE_STATUS_GOOD) 1080 { 1081 DBG (1, "attach: MODE_SELECT failed\n"); 1082 return (SANE_STATUS_INVAL); 1083 } 1084 1085 status = trigger_scan (s->fd); 1086 if (status != SANE_STATUS_GOOD) 1087 { 1088 DBG (1, "start of scan failed: %s\n", sane_strstatus (status)); 1089 /* next line introduced not to freeze xscanimage */ 1090 do_cancel(s); 1091 return status; 1092 } 1093 1094 /* Wait for scanner to become ready to transmit data */ 1095 status = ibm_wait_ready (s); 1096 if (status != SANE_STATUS_GOOD) 1097 { 1098 DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status)); 1099 return (status); 1100 } 1101 1102 s->bytes_to_read = s->params.bytes_per_line * s->params.lines; 1103 1104 DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, " 1105 "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, 1106 s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w); 1107 1108 s->scanning = SANE_TRUE; 1109 1110 DBG (11, "<< sane_start\n"); 1111 return (SANE_STATUS_GOOD); 1112} 1113 1114SANE_Status 1115sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, 1116 SANE_Int * len) 1117{ 1118 Ibm_Scanner *s = handle; 1119 SANE_Status status; 1120 size_t nread; 1121 DBG (11, ">> sane_read\n"); 1122 1123 *len = 0; 1124 1125 DBG (11, "sane_read: bytes left to read: %ld\n", (u_long) s->bytes_to_read); 1126 1127 if (s->bytes_to_read == 0) 1128 { 1129 do_cancel (s); 1130 return (SANE_STATUS_EOF); 1131 } 1132 1133 if (!s->scanning) { 1134 DBG (11, "sane_read: scanning is false!\n"); 1135 return (do_cancel (s)); 1136 } 1137 1138 nread = max_len; 1139 if (nread > s->bytes_to_read) 1140 nread = s->bytes_to_read; 1141 1142 DBG (11, "sane_read: read %ld bytes\n", (u_long) nread); 1143 status = read_data (s->fd, buf, &nread); 1144 if (status != SANE_STATUS_GOOD) 1145 { 1146 DBG (11, "sane_read: read error\n"); 1147 do_cancel (s); 1148 return (SANE_STATUS_IO_ERROR); 1149 } 1150 *len = nread; 1151 s->bytes_to_read -= nread; 1152 1153 DBG (11, "<< sane_read\n"); 1154 return (SANE_STATUS_GOOD); 1155} 1156 1157void 1158sane_cancel (SANE_Handle handle) 1159{ 1160 Ibm_Scanner *s = handle; 1161 DBG (11, ">> sane_cancel\n"); 1162 1163 s->scanning = SANE_FALSE; 1164 1165 DBG (11, "<< sane_cancel\n"); 1166} 1167 1168SANE_Status 1169sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) 1170{ 1171 DBG (5, ">> sane_set_io_mode (handle = %p, non_blocking = %d)\n", 1172 handle, non_blocking); 1173 DBG (5, "<< sane_set_io_mode\n"); 1174 1175 return SANE_STATUS_UNSUPPORTED; 1176} 1177 1178SANE_Status 1179sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) 1180{ 1181 DBG (5, ">> sane_get_select_fd (handle = %p, fd = %p)\n", 1182 handle, (void *) fd); 1183 DBG (5, "<< sane_get_select_fd\n"); 1184 1185 return SANE_STATUS_UNSUPPORTED; 1186} 1187