/* Open device, return the device handle */ SANE_Status sane_open (SANE_String_Const devname, SANE_Handle * handle) { unsigned i, j, id = 0; struct scanner *s; SANE_Int h, bus; SANE_Status st = SANE_STATUS_GOOD; if (!devlist) { st = sane_get_devices (NULL, 0); if (st) return st; } for (i = 0; devlist[i]; i++) { if (!strcmp (devlist[i]->name, devname)) break; } if (!devlist[i]) return SANE_STATUS_INVAL; for (j = 0; j < sizeof (known_devices) / sizeof (known_devices[0]); j++) { if (!strcmp (devlist[i]->model, known_devices[j].scanner.model)) { id = known_devices[j].id; break; } } st = sanei_usb_open (devname, &h); if (st == SANE_STATUS_ACCESS_DENIED) return st; if (st) { st = sanei_scsi_open (devname, &h, kvs40xx_sense_handler, NULL); if (st) { return st; } bus = SCSI; } else { bus = USB; st = sanei_usb_claim_interface (h, 0); if (st) { sanei_usb_close (h); return st; } } s = malloc (sizeof (struct scanner)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (struct scanner)); s->buffer = malloc (MAX_READ_DATA_SIZE + BULK_HEADER_SIZE); if (!s->buffer) return SANE_STATUS_NO_MEM; s->file = h; s->bus = bus; s->id = id; strcpy (s->name, devname); *handle = s; for (i = 0; i < 3; i++) { st = kvs40xx_test_unit_ready (s); if (st) { if (s->bus == SCSI) { sanei_scsi_close (s->file); st = sanei_scsi_open (devname, &h, kvs40xx_sense_handler, NULL); if (st) return st; } else { sanei_usb_release_interface (s->file, 0); sanei_usb_close (s->file); st = sanei_usb_open (devname, &h); if (st) return st; st = sanei_usb_claim_interface (h, 0); if (st) { sanei_usb_close (h); return st; } } s->file = h; } else break; } if (i == 3) return SANE_STATUS_DEVICE_BUSY; if (id == KV_S4085C || id == KV_S4065C) { char str[16]; st = inquiry (s, str); if (st) goto err; if (id == KV_S4085C) s->id = !strcmp (str, "KV-S4085CL") ? KV_S4085CL : KV_S4085CW; else s->id = !strcmp (str, "KV-S4065CL") ? KV_S4065CL : KV_S4065CW; } kvs40xx_init_options (s); st = kvs40xx_set_timeout (s, s->val[FEED_TIMEOUT].w); if (st) goto err; return SANE_STATUS_GOOD; err: sane_close (s); return st; }
/* Control option */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { int i; SANE_Status status; SANE_Word cap; struct scanner *s = (struct scanner *) handle; if (info) *info = 0; if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_UNSUPPORTED; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_UNSUPPORTED; if (action == SANE_ACTION_GET_VALUE) { if (s->opt[option].type == SANE_TYPE_STRING) { DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %s\n", option, s->val[option].s); strcpy (val, s->val[option].s); } else { *(SANE_Word *) val = s->val[option].w; DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %d\n", option, s->val[option].w); } return SANE_STATUS_GOOD; } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; if (s->opt[option].type == SANE_TYPE_STRING) { if (!strcmp (val, s->val[option].s)) return SANE_STATUS_GOOD; DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %s\n", option, (SANE_String_Const) val); } else { if (*(SANE_Word *) val == s->val[option].w) return SANE_STATUS_GOOD; DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %d\n", option, *(SANE_Word *) val); } switch (option) { /* Side-effect options */ case RESOLUTION: s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case TL_Y: if ((*(SANE_Word *) val) + MIN_LENGTH <= s->val[BR_Y].w && !check_area (s, s->val[TL_X].w, *(SANE_Word *) val, s->val[BR_X].w, s->val[BR_Y].w)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case BR_Y: if ((*(SANE_Word *) val) >= s->val[TL_Y].w + MIN_LENGTH && !check_area (s, s->val[TL_X].w, s->val[TL_Y].w, s->val[BR_X].w, *(SANE_Word *) val)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case TL_X: if ((*(SANE_Word *) val) + MIN_WIDTH <= s->val[BR_X].w && !check_area (s, *(SANE_Word *) val, s->val[TL_Y].w, s->val[BR_X].w, s->val[BR_Y].w)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case BR_X: if (*(SANE_Word *) val >= s->val[TL_X].w + MIN_WIDTH && !check_area (s, s->val[TL_X].w, s->val[TL_Y].w, *(SANE_Word *) val, s->val[BR_Y].w)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case LANDSCAPE: s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* Side-effect free options */ case CONTRAST: case BRIGHTNESS: case DUPLEX: case LENGTHCTL: case LONG_PAPER: case FIT_TO_PAGE: case THRESHOLD: case INVERSE: case COMPRESSION_PAR: case DFSTOP: case DFEED_L: case DFEED_C: case DFEED_R: case STOP_SKEW: case DESKEW: case MIRROR: case CROP: case TOPPOS: case BTMPOS: case RED_CHROMA: case BLUE_CHROMA: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; case FEED_TIMEOUT: s->val[option].w = *(SANE_Word *) val; return kvs40xx_set_timeout (s, s->val[option].w); /* String mode */ case IMAGE_EMPHASIS: case GAMMA_CORRECTION: case LAMP: case HALFTONE_PATTERN: case DFEED_SENCE: case AUTOMATIC_THRESHOLD: case WHITE_LEVEL: case NOISE_REDUCTION: strcpy (s->val[option].s, val); return SANE_STATUS_GOOD; case SOURCE: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, SANE_I18N ("adf"))) { strcpy (s->val[FEEDER_MODE].s, feeder_mode_list[0]); strcpy (s->val[MANUALFEED].s, manual_feed_list[0]); s->val[DUPLEX].w = SANE_FALSE; s->val[DBLFEED].w = SANE_FALSE; s->val[BTMPOS].w = SANE_FALSE; s->val[TOPPOS].w = SANE_FALSE; s->val[STOP_SKEW].w = SANE_FALSE; s->val[LENGTHCTL].w = SANE_FALSE; s->val[LONG_PAPER].w = SANE_FALSE; s->opt[FEEDER_MODE].cap |= SANE_CAP_INACTIVE; s->opt[MANUALFEED].cap |= SANE_CAP_INACTIVE; s->opt[DUPLEX].cap |= SANE_CAP_INACTIVE; s->opt[DBLFEED].cap |= SANE_CAP_INACTIVE; s->opt[BTMPOS].cap |= SANE_CAP_INACTIVE; s->opt[TOPPOS].cap |= SANE_CAP_INACTIVE; s->opt[STOP_SKEW].cap |= SANE_CAP_INACTIVE; s->opt[LENGTHCTL].cap |= SANE_CAP_INACTIVE; s->opt[LONG_PAPER].cap |= SANE_CAP_INACTIVE; } else { s->opt[FEEDER_MODE].cap &= ~SANE_CAP_INACTIVE; s->opt[MANUALFEED].cap &= ~SANE_CAP_INACTIVE; s->opt[DUPLEX].cap &= ~SANE_CAP_INACTIVE; s->opt[DBLFEED].cap &= ~SANE_CAP_INACTIVE; s->opt[BTMPOS].cap &= ~SANE_CAP_INACTIVE; s->opt[TOPPOS].cap &= ~SANE_CAP_INACTIVE; s->opt[STOP_SKEW].cap &= ~SANE_CAP_INACTIVE; s->opt[LENGTHCTL].cap &= ~SANE_CAP_INACTIVE; s->opt[LONG_PAPER].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case FEEDER_MODE: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, SANE_I18N ("continuous"))) { s->opt[LONG_PAPER].cap |= SANE_CAP_INACTIVE; } else { s->opt[LONG_PAPER].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case MODE: strcpy (s->val[option].s, val); if (!strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART)) { s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; s->opt[COMPRESSION].cap |= SANE_CAP_INACTIVE; s->opt[COMPRESSION_PAR].cap |= SANE_CAP_INACTIVE; s->opt[THRESHOLD].cap &= ~SANE_CAP_INACTIVE; s->opt[HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; s->opt[AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; s->opt[WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE; s->opt[NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE; s->opt[INVERSE].cap &= ~SANE_CAP_INACTIVE; s->opt[RED_CHROMA].cap |= SANE_CAP_INACTIVE; s->opt[BLUE_CHROMA].cap |= SANE_CAP_INACTIVE; } else { s->opt[COMPRESSION].cap &= ~SANE_CAP_INACTIVE; s->opt[COMPRESSION_PAR].cap &= ~SANE_CAP_INACTIVE; s->opt[THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[INVERSE].cap |= SANE_CAP_INACTIVE; s->opt[HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; s->opt[AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[WHITE_LEVEL].cap |= SANE_CAP_INACTIVE; s->opt[NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE; s->opt[RED_CHROMA].cap &= ~SANE_CAP_INACTIVE; s->opt[BLUE_CHROMA].cap &= ~SANE_CAP_INACTIVE; } if (!strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY)) { s->opt[INVERSE].cap &= ~SANE_CAP_INACTIVE; s->opt[GAMMA_CORRECTION].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case MANUALFEED: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, manual_feed_list[0]) == 0) /* off */ s->opt[FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE; else s->opt[FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case STAPELED_DOC: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, stapeled_list[0]) == 0) { s->opt[DBLFEED].cap &= ~SANE_CAP_INACTIVE; s->opt[DFSTOP].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_L].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_R].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[DBLFEED].cap |= SANE_CAP_INACTIVE; s->opt[DFSTOP].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_L].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_C].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_R].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap |= SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case DBLFEED: s->val[option].w = *(SANE_Word *) val; if (!s->val[option].b) { s->opt[DFSTOP].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_L].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_C].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_R].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap |= SANE_CAP_INACTIVE; } else { s->opt[DFSTOP].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_L].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_R].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case PAPER_SIZE: strcpy (s->val[option].s, val); i = str_index (paper_list, s->val[option].s); if (i == 0) { /*user def */ s->opt[TL_X].cap &= s->opt[TL_Y].cap &= s->opt[BR_X].cap &= s->opt[BR_Y].cap &= ~SANE_CAP_INACTIVE; s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } else { s->opt[TL_X].cap |= s->opt[TL_Y].cap |= s->opt[BR_X].cap |= s->opt[BR_Y].cap |= SANE_CAP_INACTIVE; if ( /*i == 4 || */ i == 5 || i == 6 /*XXX*/ || i == 10 || i == 11) { /*A4, A5, A6, B5, B6 */ if ((s->id == KV_S4085CL || s->id == KV_S4065CL) && i == 4 && i == 10) { /*A4, B5 */ s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } else s->opt[LANDSCAPE].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case COMPRESSION: s->val[option].w = *(SANE_Word *) val; if (!s->val[option].b) { s->opt[COMPRESSION_PAR].cap |= SANE_CAP_INACTIVE; } else { s->opt[COMPRESSION_PAR].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } } return SANE_STATUS_UNSUPPORTED; }