/* Copyright (C) 2009, Panasonic Russia Ltd. Copyright (C) 2010,2011, m. allan noah */ /* Panasonic KV-S40xx USB-SCSI scanner driver. */ #include "../include/sane/config.h" #include /*isspace*/ #include /*tan*/ #include #include #include #define DEBUG_NOT_STATIC #include "../include/sane/sanei_backend.h" #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_scsi.h" #include "lassert.h" #include "kvs40xx.h" #include "sane/sanei_debug.h" #define DATA_TAIL 0x200 struct known_device { const SANE_Int id; const SANE_Device scanner; }; static const struct known_device known_devices[] = { { KV_S4085C, { "MATSHITA", "KV-S4085C", "High Speed Color ADF Scanner", "scanner" }, }, { KV_S4065C, { "MATSHITA", "KV-S4065C", "High Speed Color ADF Scanner", "scanner" }, }, { KV_S7075C, { "MATSHITA", "KV-S7075C", "High Speed Color ADF Scanner", "scanner" }, }, }; static inline SANE_Status buf_init(struct buf *b, SANE_Int sz) { const int num = sz / BUF_SIZE + 1; b->buf = (u8 **) realloc(b->buf, num * sizeof(u8 *)); if (!b->buf) return SANE_STATUS_NO_MEM; memset(b->buf, 0, num * sizeof(void *)); b->size = b->head = b->tail = 0; b->sem = 0; b->st = SANE_STATUS_GOOD; pthread_cond_init(&b->cond, NULL); pthread_mutex_init(&b->mu, NULL); return SANE_STATUS_GOOD; } static inline void buf_deinit(struct buf *b) { int i; if (!b->buf) return; for (i = b->head; i < b->tail; i++) if (b->buf[i]) free(b->buf[i]); free(b->buf); b->buf = NULL; b->head = b->tail = 0; } static inline SANE_Status new_buf(struct buf *b, u8 ** p) { b->buf[b->tail] = (u8 *) malloc(BUF_SIZE); if (!b->buf[b->tail]) return SANE_STATUS_NO_MEM; *p = b->buf[b->tail]; ++b->tail; return SANE_STATUS_GOOD; } static inline SANE_Status buf_get_err(struct buf *b) { return b->size ? SANE_STATUS_GOOD : b->st; } static inline void buf_set_st(struct buf *b, SANE_Status st) { pthread_mutex_lock(&b->mu); b->st = st; if (buf_get_err(b)) pthread_cond_signal(&b->cond); pthread_mutex_unlock(&b->mu); } static inline void buf_cancel(struct buf *b) { buf_set_st(b, SANE_STATUS_CANCELLED); } static inline void push_buf(struct buf *b, SANE_Int sz) { pthread_mutex_lock(&b->mu); b->sem++; b->size += sz; pthread_cond_signal(&b->cond); pthread_mutex_unlock(&b->mu); } static inline u8 *get_buf(struct buf *b, SANE_Int * sz) { SANE_Status err = buf_get_err(b); if (err) return NULL; pthread_mutex_lock(&b->mu); while (!b->sem && !buf_get_err(b)) pthread_cond_wait(&b->cond, &b->mu); b->sem--; err = buf_get_err(b); if (!err) { *sz = b->size < BUF_SIZE ? b->size : BUF_SIZE; b->size -= *sz; } pthread_mutex_unlock(&b->mu); return err ? NULL : b->buf[b->head]; } static inline void pop_buf(struct buf *b) { free(b->buf[b->head]); b->buf[b->head] = NULL; ++b->head; } SANE_Status sane_init (SANE_Int __sane_unused__ * version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT (); DBG (DBG_INFO, "This is panasonic kvs40xx driver\n"); *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 1); /* Initialize USB */ sanei_usb_init (); return SANE_STATUS_GOOD; } /* * List of available devices, allocated by sane_get_devices, released * by sane_exit() */ static SANE_Device **devlist = NULL; static unsigned curr_scan_dev = 0; void sane_exit (void) { if (devlist) { int i; for (i = 0; devlist[i]; i++) { free ((void *) devlist[i]); } free ((void *) devlist); devlist = NULL; } } SANE_Status attach (SANE_String_Const devname); SANE_Status attach (SANE_String_Const devname) { int i = 0; if (devlist) { for (; devlist[i]; i++); devlist = realloc (devlist, sizeof (SANE_Device *) * (i + 1)); if (!devlist) return SANE_STATUS_NO_MEM; } else { devlist = malloc (sizeof (SANE_Device *) * 2); if (!devlist) return SANE_STATUS_NO_MEM; } devlist[i] = malloc (sizeof (SANE_Device)); if (!devlist[i]) return SANE_STATUS_NO_MEM; memcpy (devlist[i], &known_devices[curr_scan_dev].scanner, sizeof (SANE_Device)); devlist[i]->name = strdup (devname); /* terminate device list with NULL entry: */ devlist[i + 1] = 0; DBG (DBG_INFO, "%s device attached\n", devname); return SANE_STATUS_GOOD; } /* Get device list */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { if (devlist) { int i; for (i = 0; devlist[i]; i++) { free ((void *) devlist[i]); } free ((void *) devlist); devlist = NULL; } for (curr_scan_dev = 0; curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++) { sanei_usb_find_devices (PANASONIC_ID, known_devices[curr_scan_dev].id, attach); } for (curr_scan_dev = 0; curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++) { sanei_scsi_find_devices (known_devices[curr_scan_dev]. scanner.vendor, known_devices[curr_scan_dev]. scanner.model, NULL, -1, -1, -1, -1, attach); } if(device_list) *device_list = (const SANE_Device **) devlist; return SANE_STATUS_GOOD; } /* 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; } /* Close device */ void sane_close (SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; unsigned i; hopper_down (s); if (s->bus == USB) { sanei_usb_release_interface (s->file, 0); sanei_usb_close (s->file); } else sanei_scsi_close (s->file); for (i = 1; i < NUM_OPTIONS; i++) { if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s) free (s->val[i].s); } for (i = 0; i < sizeof (s->buf) / sizeof (s->buf[0]); i++) buf_deinit (&s->buf[i]); free (s->buffer); free (s); } /* Get option descriptor */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { struct scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS || option < 0) return NULL; return s->opt + option; } static SANE_Status wait_document (struct scanner *s) { SANE_Status st; int i; if (!strcmp ("fb", s->val[SOURCE].s)) return SANE_STATUS_GOOD; if (!strcmp ("off", s->val[MANUALFEED].s)) return kvs40xx_document_exist (s); for (i = 0; i < s->val[FEED_TIMEOUT].w; i++) { st = kvs40xx_document_exist (s); if (st != SANE_STATUS_NO_DOCS) return st; sleep (1); } return SANE_STATUS_NO_DOCS; } static SANE_Status read_image_duplex(SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; SANE_Status st = SANE_STATUS_GOOD; unsigned read, side; int i; struct side { unsigned mx, eof; u8 *p; struct buf *buf; } a[2], *b; for (i = 0; i < 2; i++) { a[i].mx = BUF_SIZE; a[i].eof = 0; a[i].buf = &s->buf[i]; st = new_buf(&s->buf[i], &a[i].p); if (st) goto err; } for (b = &a[0], side = SIDE_FRONT; (!a[0].eof || !a[1].eof);) { pthread_testcancel(); if (b->mx == 0) { push_buf(b->buf, BUF_SIZE); st = new_buf(b->buf, &b->p); if (st) goto err; b->mx = BUF_SIZE; } st = kvs40xx_read_image_data(s, s->page, side, b->p + BUF_SIZE - b->mx, b->mx, &read); b->mx -= read; if (st) { if (st != INCORRECT_LENGTH && st != SANE_STATUS_EOF) goto err; if (st == SANE_STATUS_EOF) { b->eof = 1; push_buf(b->buf, BUF_SIZE - b->mx); } side ^= SIDE_BACK; b = &a[side == SIDE_FRONT ? 0 : 1]; } } err: for (i = 0; i < 2; i++) buf_set_st(&s->buf[i], st); return st; } static SANE_Status read_image_simplex(SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; SANE_Status st = SANE_STATUS_GOOD; for (; (!st || st == INCORRECT_LENGTH);) { unsigned read, mx; unsigned char *p = NULL; st = new_buf(&s->buf[0], &p); for (read = 0, mx = BUF_SIZE; mx && (!st || st == INCORRECT_LENGTH); mx -= read) { pthread_testcancel(); st = kvs40xx_read_image_data(s, s->page, SIDE_FRONT, p + BUF_SIZE - mx, mx, &read); } push_buf(&s->buf[0], BUF_SIZE - mx); } buf_set_st(&s->buf[0], st); return st; } static SANE_Status read_data(struct scanner *s) { SANE_Status st; int duplex = s->val[DUPLEX].w; s->read = 0; s->side = SIDE_FRONT; st = duplex ? read_image_duplex(s) : read_image_simplex(s); if (st && (st != SANE_STATUS_EOF)) goto err; st = kvs40xx_read_picture_element(s, SIDE_FRONT, &s->params); if (st) goto err; if (!s->params.lines) { st = SANE_STATUS_INVAL; goto err; } sane_get_parameters(s, NULL); s->page++; return SANE_STATUS_GOOD; err: s->scanning = 0; return st; } /* Start scanning */ SANE_Status sane_start (SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; SANE_Status st = SANE_STATUS_GOOD; int duplex = s->val[DUPLEX].w, i; unsigned data_avalible; int start = 0; if (s->thread) { pthread_join (s->thread, NULL); s->thread = 0; } if (!s->scanning) { st = kvs40xx_test_unit_ready (s); if (st) return st; st = wait_document (s); if (st) return st; st = kvs40xx_reset_window (s); if (st) return st; st = kvs40xx_set_window (s, SIDE_FRONT); if (st) return st; if (duplex) { st = kvs40xx_set_window (s, SIDE_BACK); if (st) return st; } st = kvs40xx_scan (s); if (st) return st; if (s->val[CROP].b || s->val[LENGTHCTL].b || s->val[LONG_PAPER].b) { unsigned w, h, res = s->val[RESOLUTION].w; SANE_Parameters *p = &s->params; w = 297; /*A3 */ h = 420; p->pixels_per_line = w * res / 25.4 + .5; p->lines = h * res / 25.4 + .5; } else { st = kvs40xx_read_picture_element (s, SIDE_FRONT, &s->params); if (st) return st; } start = 1; s->scanning = 1; s->page = 0; s->read = 0; s->side = SIDE_FRONT; sane_get_parameters (s, NULL); } if (duplex && s->side == SIDE_FRONT && !start) { s->side = SIDE_BACK; s->read = 0; return SANE_STATUS_GOOD; } do { st = get_buffer_status(s, &data_avalible); if (st) goto err; } while (!data_avalible); for (i = 0; i < (duplex ? 2 : 1); i++) { st = buf_init (&s->buf[i], s->side_size); if (st) goto err; } if (pthread_create (&s->thread, NULL, (void *(*)(void *)) read_data, s)) { st = SANE_STATUS_IO_ERROR; goto err; } if (s->val[CROP].b || s->val[LENGTHCTL].b || s->val[LONG_PAPER].b) { pthread_join (s->thread, NULL); s->thread = 0; } return SANE_STATUS_GOOD; err: s->scanning = 0; return st; } SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { struct scanner *s = (struct scanner *) handle; int duplex = s->val[DUPLEX].w; struct buf *b = s->side == SIDE_FRONT ? &s->buf[0] : &s->buf[1]; SANE_Status err = buf_get_err(b); SANE_Int inbuf = 0; *len = 0; if (!s->scanning) return SANE_STATUS_EOF; if (err) goto out; if (s->read) { *len = max_len < (SANE_Int) s->read ? max_len : (SANE_Int) s->read; memcpy(buf, s->data + BUF_SIZE - s->read, *len); s->read -= *len; if (!s->read) pop_buf(b); goto out; } s->data = get_buf(b, &inbuf); if (!s->data) goto out; *len = max_len < inbuf ? max_len : inbuf; if (*len > BUF_SIZE) *len = BUF_SIZE; memcpy(buf, s->data, *len); s->read = inbuf > BUF_SIZE ? BUF_SIZE - *len : inbuf - *len; if (!s->read) pop_buf(b); out: err = *len ? SANE_STATUS_GOOD : buf_get_err(b); if (err == SANE_STATUS_EOF) { if (strcmp(s->val[FEEDER_MODE].s, SANE_I18N("continuous"))) { if (!duplex || s->side == SIDE_BACK) s->scanning = 0; } buf_deinit(b); } else if (err) { unsigned i; for (i = 0; i < sizeof(s->buf) / sizeof(s->buf[0]); i++) buf_deinit(&s->buf[i]); } return err; } void sane_cancel (SANE_Handle handle) { unsigned i; struct scanner *s = (struct scanner *) handle; if (s->scanning && !strcmp (s->val[FEEDER_MODE].s, SANE_I18N ("continuous"))) { stop_adf (s); } if (s->thread) { pthread_cancel (s->thread); pthread_join (s->thread, NULL); s->thread = 0; } for (i = 0; i < sizeof (s->buf) / sizeof (s->buf[0]); i++) buf_deinit (&s->buf[i]); s->scanning = 0; } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool __sane_unused__ m) { return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; }