diff options
Diffstat (limited to 'backend/kvs20xx_cmd.c')
-rw-r--r-- | backend/kvs20xx_cmd.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/backend/kvs20xx_cmd.c b/backend/kvs20xx_cmd.c new file mode 100644 index 0000000..7579701 --- /dev/null +++ b/backend/kvs20xx_cmd.c @@ -0,0 +1,379 @@ +/* + Copyright (C) 2008, Panasonic Russia Ltd. + Copyright (C) 2010, m. allan noah +*/ +/* + Panasonic KV-S20xx USB-SCSI scanners. +*/ + +#include "../include/sane/config.h" + +#include <string.h> +/*#include <unistd.h>*/ + +#define DEBUG_DECLARE_ONLY +#define BACKEND_NAME kvs20xx + +#include "../include/sane/sanei_backend.h" +#include "../include/sane/sanei_scsi.h" +#include "../include/sane/sanei_usb.h" + +#include "kvs20xx.h" +#include "kvs20xx_cmd.h" + +static SANE_Status +usb_send_command (struct scanner *s, struct cmd *c, struct response *r, + void *buf) +{ + SANE_Status st; + struct bulk_header *h = (struct bulk_header *) buf; + u8 resp[sizeof (*h) + STATUS_SIZE]; + size_t sz = sizeof (*h) + MAX_CMD_SIZE; + memset (h, 0, sz); + h->length = cpu2be32 (sz); + h->type = cpu2be16 (COMMAND_BLOCK); + h->code = cpu2be16 (COMMAND_CODE); + memcpy (h + 1, c->cmd, c->cmd_size); + + st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz); + if (st) + return st; + if (sz != sizeof (*h) + MAX_CMD_SIZE) + return SANE_STATUS_IO_ERROR; + if (c->dir == CMD_IN) + { + sz = sizeof (*h) + c->data_size; + st = sanei_usb_read_bulk (s->file, (SANE_Byte *) h, &sz); + c->data = h + 1; + c->data_size = sz - sizeof (*h); + + if (st || sz < sizeof (*h)) + { + st = sanei_usb_release_interface (s->file, 0); + if (st) + return st; + st = sanei_usb_claim_interface (s->file, 0); + if (st) + return st; + r->status = CHECK_CONDITION; + return SANE_STATUS_GOOD; + } + + } + else if (c->dir == CMD_OUT) + { + sz = sizeof (*h) + c->data_size; + memset (h, 0, sizeof (*h)); + h->length = cpu2be32 (sizeof (*h) + c->data_size); + h->type = cpu2be16 (DATA_BLOCK); + h->code = cpu2be16 (DATA_CODE); + memcpy (h + 1, c->data, c->data_size); + st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz); + if (st) + return st; + } + sz = sizeof (resp); + st = sanei_usb_read_bulk (s->file, resp, &sz); + if (st || sz != sizeof (resp)) + return SANE_STATUS_IO_ERROR; + r->status = be2cpu32 (*((u32 *) (resp + sizeof (*h)))); + return st; +} + +SANE_Status +kvs20xx_sense_handler (int __sane_unused__ fd, + u_char * sense_buffer, void __sane_unused__ * arg) +{ + unsigned i; + SANE_Status st = SANE_STATUS_GOOD; + for (i = 0; i < sizeof (s_errors) / sizeof (s_errors[0]); i++) + if ((sense_buffer[2] & 0xf) == s_errors[i].sense + && sense_buffer[12] == s_errors[i].asc + && sense_buffer[13] == s_errors[i].ascq) + { + st = s_errors[i].st; + break; + } + if (st == SANE_STATUS_GOOD && sense_buffer[2] & END_OF_MEDIUM) + st = SANE_STATUS_EOF; + if (i == sizeof (s_errors) / sizeof (s_errors[0])) + st = SANE_STATUS_IO_ERROR; + DBG (DBG_ERR, + "send_command: CHECK_CONDITION: sence:0x%x ASC:0x%x ASCQ:0x%x\n", + sense_buffer[2], sense_buffer[12], sense_buffer[13]); + + return st; +} + +static SANE_Status +send_command (struct scanner * s, struct cmd * c) +{ + SANE_Status st = SANE_STATUS_GOOD; + if (s->bus == USB) + { + struct response r; + memset (&r, 0, sizeof (r)); + st = usb_send_command (s, c, &r, s->buffer); + if (st) + return st; + if (r.status) + { + u8 b[sizeof (struct bulk_header) + RESPONSE_SIZE]; + struct cmd c2 = { + {0}, + 6, + 0, + RESPONSE_SIZE, + CMD_IN + }; + c2.cmd[0] = REQUEST_SENSE; + c2.cmd[4] = RESPONSE_SIZE; + st = usb_send_command (s, &c2, &r, b); + if (st) + return st; + st = kvs20xx_sense_handler (0, b + sizeof (struct bulk_header), NULL); + } + } + else + { + if (c->dir == CMD_OUT) + { + memcpy (s->buffer, c->cmd, c->cmd_size); + memcpy (s->buffer + c->cmd_size, c->data, c->data_size); + st = sanei_scsi_cmd (s->file, s->buffer, c->cmd_size + c->data_size, + NULL, NULL); + } + else if (c->dir == CMD_IN) + { + c->data = s->buffer; + st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, + c->data, (size_t *) & c->data_size); + } + else + { + st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, NULL, NULL); + } + } + return st; +} + +SANE_Status +kvs20xx_test_unit_ready (struct scanner * s) +{ + struct cmd c = { + {0}, + 6, + 0, + 0, + CMD_NONE + }; + c.cmd[0] = TEST_UNIT_READY; + if (send_command (s, &c)) + return SANE_STATUS_DEVICE_BUSY; + + return SANE_STATUS_GOOD; +} + +SANE_Status +kvs20xx_set_timeout (struct scanner * s, int timeout) +{ + u16 t = cpu2be16 ((u16) timeout); + struct cmd c = { + {0}, + 10, + 0, + 0, + CMD_OUT + }; + c.cmd[0] = SET_TIMEOUT; + c.cmd[2] = 0x8d; + *((u16 *) (c.cmd + 7)) = cpu2be16 (sizeof (t)); + + c.data = &t; + c.data_size = sizeof (t); + + if (s->bus == USB) + sanei_usb_set_timeout (timeout * 1000); + + return send_command (s, &c); +} + +SANE_Status +kvs20xx_set_window (struct scanner * s, int wnd_id) +{ + struct window wnd; + struct cmd c = { + {0}, + 10, + 0, + 0, + CMD_OUT + }; + c.cmd[0] = SET_WINDOW; + *((u16 *) (c.cmd + 7)) = cpu2be16 (sizeof (wnd)); + + c.data = &wnd; + c.data_size = sizeof (wnd); + + kvs20xx_init_window (s, &wnd, wnd_id); + + return send_command (s, &c); +} + +SANE_Status +kvs20xx_reset_window (struct scanner * s) +{ + struct cmd c = { + {0}, + 10, + 0, + 0, + CMD_NONE + }; + c.cmd[0] = SET_WINDOW; + + return send_command (s, &c); +} + +SANE_Status +kvs20xx_scan (struct scanner * s) +{ + struct cmd c = { + {0}, + 6, + 0, + 0, + CMD_NONE + }; + c.cmd[0] = SCAN; + return send_command (s, &c); +} + +SANE_Status +kvs20xx_document_exist (struct scanner * s) +{ + SANE_Status status; + struct cmd c = { + {0}, + 10, + 0, + 6, + CMD_IN, + }; + u8 *d; + c.cmd[0] = READ_10; + c.cmd[2] = 0x81; + set24 (c.cmd + 6, c.data_size); + status = send_command (s, &c); + if (status) + return status; + d = c.data; + if (d[0] & 0x20) + return SANE_STATUS_GOOD; + + return SANE_STATUS_NO_DOCS; +} + +SANE_Status +kvs20xx_read_picture_element (struct scanner * s, unsigned side, + SANE_Parameters * p) +{ + SANE_Status status; + struct cmd c = { + {0}, + 10, + 0, + 16, + CMD_IN + }; + u32 *data; + c.cmd[0] = READ_10; + c.cmd[2] = 0x80; + c.cmd[5] = side; + set24 (c.cmd + 6, c.data_size); + + status = send_command (s, &c); + if (status) + return status; + data = (u32 *) c.data; + p->pixels_per_line = be2cpu32 (data[0]); + p->lines = be2cpu32 (data[1]); + return SANE_STATUS_GOOD; +} + +static SANE_Status +get_buffer_status (struct scanner * s, unsigned *data_avalible) +{ + SANE_Status status; + struct cmd c = { + {0}, + 10, + 0, + 12, + CMD_IN + }; + u32 *data; + c.cmd[0] = GET_BUFFER_STATUS; + c.cmd[7] = 12; + + status = send_command (s, &c); + if (status) + return status; + data = (u32 *) c.data; + *data_avalible = be2cpu32 (data[3]); + return SANE_STATUS_GOOD; +} + +SANE_Status +kvs20xx_read_image_data (struct scanner * s, unsigned page, unsigned side, + void *buf, unsigned max_size, unsigned *size) +{ + SANE_Status status; + struct cmd c = { + {0}, + 10, + 0, + 0, + CMD_IN + }; + c.cmd[0] = READ_10; + c.cmd[4] = page; + c.cmd[5] = side; + + c.data_size = max_size < MAX_READ_DATA_SIZE ? max_size : MAX_READ_DATA_SIZE; + + set24 (c.cmd + 6, c.data_size); + status = send_command (s, &c); + + if (status && status != SANE_STATUS_EOF) + return status; + + *size = c.data_size; + DBG (DBG_INFO, "kvs20xx_read_image_data: read %d, status %d\n", *size, status); + memcpy (buf, c.data, *size); + return status; +} + +SANE_Status +get_adjust_data (struct scanner * s, unsigned *dummy_length) +{ + SANE_Status status; + struct cmd c = { + {0}, + 10, + 0, + 40, + CMD_IN + }; + u16 *data; + + c.cmd[0] = GET_ADJUST_DATA; + c.cmd[2] = 0x9b; + c.cmd[8] = 40; + status = send_command (s, &c); + if (status) + return status; + data = (u16 *) c.data; + *dummy_length = be2cpu16 (data[0]); + return SANE_STATUS_GOOD; +} |