/* * ESC/I commands for Epson scanners * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for original copyrights. * * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include #include #include #include #include #include "epson2.h" #include "epson2-io.h" #include "epson2-commands.h" /* ESC H, set zoom */ SANE_Status esci_set_zoom(Epson_Scanner * s, unsigned char x, unsigned char y) { SANE_Status status; unsigned char params[2]; DBG(8, "%s: x = %d, y = %d\n", __func__, x, y); if (!s->hw->cmd->set_zoom) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_GOOD; } params[0] = ESC; params[1] = s->hw->cmd->set_zoom; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; params[0] = x; params[1] = y; return e2_cmd_simple(s, params, 2); } /* ESC R */ SANE_Status esci_set_resolution(Epson_Scanner * s, int x, int y) { SANE_Status status; unsigned char params[4]; DBG(8, "%s: x = %d, y = %d\n", __func__, x, y); if (!s->hw->cmd->set_resolution) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_GOOD; } params[0] = ESC; params[1] = s->hw->cmd->set_resolution; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; params[0] = x; params[1] = x >> 8; params[2] = y; params[3] = y >> 8; return e2_cmd_simple(s, params, 4); } /* * Sends the "set scan area" command to the scanner with the currently selected * scan area. This scan area must be already corrected for "color shuffling" if * necessary. */ SANE_Status esci_set_scan_area(Epson_Scanner * s, int x, int y, int width, int height) { SANE_Status status; unsigned char params[8]; DBG(8, "%s: x = %d, y = %d, w = %d, h = %d\n", __func__, x, y, width, height); if (!s->hw->cmd->set_scan_area) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_UNSUPPORTED; } /* verify the scan area */ if (x < 0 || y < 0 || width <= 0 || height <= 0) return SANE_STATUS_INVAL; params[0] = ESC; params[1] = s->hw->cmd->set_scan_area; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; params[0] = x; params[1] = x >> 8; params[2] = y; params[3] = y >> 8; params[4] = width; params[5] = width >> 8; params[6] = height; params[7] = height >> 8; return e2_cmd_simple(s, params, 8); } /* * Sends the "set color correction coefficients" command with the * currently selected parameters to the scanner. */ SANE_Status esci_set_color_correction_coefficients(Epson_Scanner * s) { SANE_Status status; unsigned char params[2]; signed char cct[9]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->set_color_correction_coefficients) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_UNSUPPORTED; } params[0] = ESC; params[1] = s->hw->cmd->set_color_correction_coefficients; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; cct[0] = s->val[OPT_CCT_1].w; cct[1] = s->val[OPT_CCT_2].w; cct[2] = s->val[OPT_CCT_3].w; cct[3] = s->val[OPT_CCT_4].w; cct[4] = s->val[OPT_CCT_5].w; cct[5] = s->val[OPT_CCT_6].w; cct[6] = s->val[OPT_CCT_7].w; cct[7] = s->val[OPT_CCT_8].w; cct[8] = s->val[OPT_CCT_9].w; DBG(11, "%s: %d,%d,%d %d,%d,%d %d,%d,%d\n", __func__, cct[0], cct[1], cct[2], cct[3], cct[4], cct[5], cct[6], cct[7], cct[8]); return e2_cmd_simple(s, params, 9); } SANE_Status esci_set_gamma_table(Epson_Scanner * s) { SANE_Status status; unsigned char params[2]; unsigned char gamma[257]; int n; int table; /* static const char gamma_cmds[] = { 'M', 'R', 'G', 'B' }; */ static const char gamma_cmds[] = { 'R', 'G', 'B' }; DBG(8, "%s\n", __func__); if (!s->hw->cmd->set_gamma_table) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->set_gamma_table; /* Print the gamma tables before sending them to the scanner */ if (DBG_LEVEL >= 10) { int c, i, j; for (c = 0; c < 3; c++) { for (i = 0; i < 256; i += 16) { char gammaValues[16 * 3 + 1], newValue[4]; gammaValues[0] = '\0'; for (j = 0; j < 16; j++) { sprintf(newValue, " %02x", s->gamma_table[c][i + j]); strcat(gammaValues, newValue); } DBG(11, "gamma table[%d][%d] %s\n", c, i, gammaValues); } } } /* * When handling inverted images, we must also invert the user * supplied gamma function. This is *not* just 255-gamma - * this gives a negative image. */ for (table = 0; table < 3; table++) { gamma[0] = gamma_cmds[table]; if (s->invert_image) { for (n = 0; n < 256; ++n) { gamma[n + 1] = 255 - s->gamma_table[table][255 - n]; } } else { for (n = 0; n < 256; ++n) { gamma[n + 1] = s->gamma_table[table][n]; } } status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; status = e2_cmd_simple(s, gamma, 257); if (status != SANE_STATUS_GOOD) return status; } return status; } /* ESC F - Request Status * -> ESC f * <- Information block */ SANE_Status esci_request_status(SANE_Handle handle, unsigned char *scanner_status) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_status == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_status; e2_send(s, params, 2, 4, &status); if (status != SANE_STATUS_GOOD) return status; status = e2_recv_info_block(s, params, 4, NULL); if (status != SANE_STATUS_GOOD) return status; if (scanner_status) *scanner_status = params[0]; DBG(1, "status: %02x\n", params[0]); if (params[0] & STATUS_NOT_READY) DBG(1, " scanner in use on another interface\n"); else DBG(1, " ready\n"); if (params[0] & STATUS_FER) DBG(1, " system error\n"); if (params[0] & STATUS_OPTION) DBG(1, " option equipment is installed\n"); else DBG(1, " no option equipment installed\n"); if (params[0] & STATUS_EXT_COMMANDS) DBG(1, " support extended commands\n"); else DBG(1, " does NOT support extended commands\n"); if (params[0] & STATUS_RESERVED) DBG(0, " a reserved bit is set, please contact the author.\n"); return status; } /* extended commands */ /* FS I, Request Extended Identity * -> FS I * <- Extended identity data (80) * * Request the properties of the scanner. */ SANE_Status esci_request_extended_identity(SANE_Handle handle, unsigned char *buf) { unsigned char model[17]; Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (buf == NULL) return SANE_STATUS_INVAL; if (s->hw->cmd->request_extended_identity == 0) return SANE_STATUS_UNSUPPORTED; params[0] = FS; params[1] = s->hw->cmd->request_extended_identity; status = e2_txrx(s, params, 2, buf, 80); if (status != SANE_STATUS_GOOD) return status; DBG(1, " command level : %c%c\n", buf[0], buf[1]); DBG(1, " basic resolution: %lu\n", (u_long) le32atoh(&buf[4])); DBG(1, " min resolution : %lu\n", (u_long) le32atoh(&buf[8])); DBG(1, " max resolution : %lu\n", (u_long) le32atoh(&buf[12])); DBG(1, " max pixel num : %lu\n", (u_long) le32atoh(&buf[16])); DBG(1, " scan area : %lux%lu\n", (u_long) le32atoh(&buf[20]), (u_long) le32atoh(&buf[24])); DBG(1, " adf area : %lux%lu\n", (u_long) le32atoh(&buf[28]), (u_long) le32atoh(&buf[32])); DBG(1, " tpu area : %lux%lu\n", (u_long) le32atoh(&buf[36]), (u_long) le32atoh(&buf[40])); DBG(1, " main status : 0x%02x\n", buf[44]); DBG(1, " input depth : %d\n", buf[66]); DBG(1, " max output depth: %d\n", buf[67]); DBG(1, " rom version : %c%c%c%c\n", buf[62], buf[63], buf[64], buf[65]); memcpy(model, &buf[46], 16); model[16] = '\0'; DBG(1, " model name : %s\n", model); DBG(1, "options:\n"); if (le32atoh(&buf[28]) > 0) DBG(1, " ADF detected\n"); if (le32atoh(&buf[36]) > 0) DBG(1, " TPU detected\n"); DBG(1, "status:\n"); if (buf[44] & EXT_IDTY_STATUS_DLF) DBG(1, " main lamp change is supported\n"); if (buf[44] & EXT_IDTY_STATUS_NOTFBF) DBG(1, " the device is NOT flatbed\n"); if (buf[44] & EXT_IDTY_STATUS_ADFT) DBG(1, " page type ADF is installed\n"); if (buf[44] & EXT_IDTY_STATUS_ADFS) DBG(1, " ADF is duplex capable\n"); if (buf[44] & EXT_IDTY_STATUS_ADFO) DBG(1, " page type ADF loads from the first sheet\n"); if (buf[44] & EXT_IDTY_STATUS_LID) DBG(1, " lid type option is installed\n"); if (buf[44] & EXT_IDTY_STATUS_TPIR) DBG(1, " infrared scanning is supported\n"); if (buf[44] & EXT_IDTY_STATUS_PB) DBG(1, " push button is supported\n"); return SANE_STATUS_GOOD; } /* FS F, request scanner status */ SANE_Status esci_request_scanner_status(SANE_Handle handle, unsigned char *buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (!s->hw->extended_commands) return SANE_STATUS_UNSUPPORTED; if (buf == NULL) return SANE_STATUS_INVAL; params[0] = FS; params[1] = 'F'; status = e2_txrx(s, params, 2, buf, 16); if (status != SANE_STATUS_GOOD) return status; DBG(1, "global status : 0x%02x\n", buf[0]); if (buf[0] & FSF_STATUS_MAIN_FER) DBG(1, " system error\n"); if (buf[0] & FSF_STATUS_MAIN_NR) DBG(1, " not ready\n"); if (buf[0] & FSF_STATUS_MAIN_WU) DBG(1, " scanner is warming up\n"); DBG(1, "adf status : 0x%02x\n", buf[1]); if (buf[1] & FSF_STATUS_ADF_IST) DBG(11, " installed\n"); else DBG(11, " not installed\n"); if (buf[1] & FSF_STATUS_ADF_EN) DBG(11, " enabled\n"); else DBG(11, " not enabled\n"); if (buf[1] & FSF_STATUS_ADF_ERR) DBG(1, " error\n"); if (buf[1] & FSF_STATUS_ADF_PE) DBG(1, " paper empty\n"); if (buf[1] & FSF_STATUS_ADF_PJ) DBG(1, " paper jam\n"); if (buf[1] & FSF_STATUS_ADF_OPN) DBG(1, " cover open\n"); if (buf[1] & FSF_STATUS_ADF_PAG) DBG(1, " duplex capable\n"); DBG(1, "tpu status : 0x%02x\n", buf[2]); if (buf[2] & FSF_STATUS_TPU_IST) DBG(11, " installed\n"); else DBG(11, " not installed\n"); if (buf[2] & FSF_STATUS_TPU_EN) DBG(11, " enabled\n"); else DBG(11, " not enabled\n"); if (buf[2] & FSF_STATUS_TPU_ERR) DBG(1, " error\n"); if (buf[1] & FSF_STATUS_TPU_OPN) DBG(1, " cover open\n"); DBG(1, "device type : 0x%02x\n", buf[3] & 0xC0); DBG(1, "main body status: 0x%02x\n", buf[3] & 0x3F); if (buf[3] & FSF_STATUS_MAIN2_PE) DBG(1, " paper empty\n"); if (buf[3] & FSF_STATUS_MAIN2_PJ) DBG(1, " paper jam\n"); if (buf[3] & FSF_STATUS_MAIN2_OPN) DBG(1, " cover open\n"); return SANE_STATUS_GOOD; } SANE_Status esci_set_scanning_parameter(SANE_Handle handle, unsigned char *buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (buf == NULL) return SANE_STATUS_INVAL; params[0] = FS; params[1] = 'W'; DBG(10, "resolution of main scan : %lu\n", (u_long) le32atoh(&buf[0])); DBG(10, "resolution of sub scan : %lu\n", (u_long) le32atoh(&buf[4])); DBG(10, "offset length of main scan : %lu\n", (u_long) le32atoh(&buf[8])); DBG(10, "offset length of sub scan : %lu\n", (u_long) le32atoh(&buf[12])); DBG(10, "scanning length of main scan: %lu\n", (u_long) le32atoh(&buf[16])); DBG(10, "scanning length of sub scan : %lu\n", (u_long) le32atoh(&buf[20])); DBG(10, "scanning color : %d\n", buf[24]); DBG(10, "data format : %d\n", buf[25]); DBG(10, "option control : %d\n", buf[26]); DBG(10, "scanning mode : %d\n", buf[27]); DBG(10, "block line number : %d\n", buf[28]); DBG(10, "gamma correction : %d\n", buf[29]); DBG(10, "brightness : %d\n", buf[30]); DBG(10, "color correction : %d\n", buf[31]); DBG(10, "halftone processing : %d\n", buf[32]); DBG(10, "threshold : %d\n", buf[33]); DBG(10, "auto area segmentation : %d\n", buf[34]); DBG(10, "sharpness control : %d\n", buf[35]); DBG(10, "mirroring : %d\n", buf[36]); DBG(10, "film type : %d\n", buf[37]); DBG(10, "main lamp lighting mode : %d\n", buf[38]); status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; status = e2_cmd_simple(s, buf, 64); if (status != SANE_STATUS_GOOD) return status; return SANE_STATUS_GOOD; } SANE_Status esci_request_command_parameter(SANE_Handle handle, unsigned char *buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_condition == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_condition; status = e2_cmd_info_block(s, params, 2, 45, &buf, NULL); if (status != SANE_STATUS_GOOD) return status; DBG(1, "scanning parameters:\n"); DBG(1, "color : %d\n", buf[1]); DBG(1, "resolution : %dx%d\n", buf[4] << 8 | buf[3], buf[6] << 8 | buf[5]); DBG(1, "halftone : %d\n", buf[19]); DBG(1, "brightness : %d\n", buf[21]); DBG(1, "color correction : %d\n", buf[28]); DBG(1, "gamma : %d\n", buf[23]); DBG(1, "sharpness : %d\n", buf[30]); DBG(1, "threshold : %d\n", buf[38]); DBG(1, "data format : %d\n", buf[17]); DBG(1, "mirroring : %d\n", buf[34]); DBG(1, "option unit control : %d\n", buf[42]); DBG(1, "film type : %d\n", buf[44]); DBG(1, "auto area segmentation : %d\n", buf[36]); DBG(1, "line counter : %d\n", buf[40]); DBG(1, "scanning mode : %d\n", buf[32]); DBG(1, "zoom : %d,%d\n", buf[26], buf[25]); DBG(1, "scan area : %d,%d %d,%d\n", buf[9] << 8 | buf[8], buf[11] << 8 | buf[10], buf[13] << 8 | buf[12], buf[15] << 8 | buf[14]); return status; } /* ESC q - Request Focus Position * -> ESC q * <- Information block * <- Focus position status (2) * 0 - Error status * 1 - Focus position */ SANE_Status esci_request_focus_position(SANE_Handle handle, unsigned char *position) { SANE_Status status; unsigned char *buf; Epson_Scanner *s = (Epson_Scanner *) handle; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_focus_position == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_focus_position; status = e2_cmd_info_block(s, params, 2, 2, &buf, NULL); if (status != SANE_STATUS_GOOD) return status; if (buf[0] & 0x01) DBG(1, "autofocus error\n"); *position = buf[1]; DBG(8, " focus position = 0x%x\n", buf[1]); free(buf); return status; } /* ESC ! - Request Push Button Status * -> ESC ! * <- Information block * <- Push button status (1) */ SANE_Status esci_request_push_button_status(SANE_Handle handle, unsigned char *bstatus) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; unsigned char *buf; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_push_button_status == 0) { DBG(1, "push button status unsupported\n"); return SANE_STATUS_UNSUPPORTED; } params[0] = ESC; params[1] = s->hw->cmd->request_push_button_status; status = e2_cmd_info_block(s, params, 2, 1, &buf, NULL); if (status != SANE_STATUS_GOOD) return status; DBG(1, "push button status = %d\n", buf[0]); *bstatus = buf[0]; free(buf); return status; } /* * Request Identity information from scanner and fill in information * into dev and/or scanner structures. * XXX information should be parsed separately. */ SANE_Status esci_request_identity(SANE_Handle handle, unsigned char **buf, size_t *len) { Epson_Scanner *s = (Epson_Scanner *) handle; unsigned char params[2]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->request_identity) return SANE_STATUS_INVAL; params[0] = ESC; params[1] = s->hw->cmd->request_identity; return e2_cmd_info_block(s, params, 2, 0, buf, len); } /* * Request information from scanner */ SANE_Status esci_request_identity2(SANE_Handle handle, unsigned char **buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; size_t len; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_identity2 == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_identity2; status = e2_cmd_info_block(s, params, 2, 0, buf, &len); if (status != SANE_STATUS_GOOD) return status; return status; } /* Send the "initialize scanner" command to the device and reset it */ SANE_Status esci_reset(Epson_Scanner * s) { SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->initialize_scanner) return SANE_STATUS_GOOD; params[0] = ESC; params[1] = s->hw->cmd->initialize_scanner; if (s->fd == -1) return SANE_STATUS_GOOD; status = e2_cmd_simple(s, params, 2); return status; } SANE_Status esci_feed(Epson_Scanner * s) { unsigned char params[1]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->feed) return SANE_STATUS_UNSUPPORTED; params[0] = s->hw->cmd->feed; return e2_cmd_simple(s, params, 1); } /* * Eject the current page from the ADF. The scanner is opened prior to * sending the command and closed afterwards. */ SANE_Status esci_eject(Epson_Scanner * s) { unsigned char params[1]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->eject) return SANE_STATUS_UNSUPPORTED; if (s->fd == -1) return SANE_STATUS_GOOD; params[0] = s->hw->cmd->eject; return e2_cmd_simple(s, params, 1); } SANE_Status esci_request_extended_status(SANE_Handle handle, unsigned char **data, size_t * data_len) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status = SANE_STATUS_GOOD; unsigned char params[2]; unsigned char *buf; size_t buf_len; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_extended_status == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_extended_status; /* This command returns 33 bytes of data on old scanners * and 42 (CMD_SIZE_EXT_STATUS) on new ones. */ status = e2_cmd_info_block(s, params, 2, CMD_SIZE_EXT_STATUS, &buf, &buf_len); if (status != SANE_STATUS_GOOD) return status; switch (buf_len) { case 33: case 42: break; default: DBG(1, "%s: unknown reply length (%lu)\n", __func__, (u_long) buf_len); break; } DBG(4, "main = %02x, ADF = %02x, TPU = %02x, main 2 = %02x\n", buf[0], buf[1], buf[6], buf[11]); if (buf[0] & EXT_STATUS_FER) DBG(1, "system error\n"); if (buf[0] & EXT_STATUS_WU) DBG(1, "scanner is warming up\n"); if (buf[1] & EXT_STATUS_ERR) DBG(1, "ADF: other error\n"); if (buf[1] & EXT_STATUS_PE) DBG(1, "ADF: no paper\n"); if (buf[1] & EXT_STATUS_PJ) DBG(1, "ADF: paper jam\n"); if (buf[1] & EXT_STATUS_OPN) DBG(1, "ADF: cover open\n"); if (buf[6] & EXT_STATUS_ERR) DBG(1, "TPU: other error\n"); /* give back a pointer to the payload * if the user requested it, otherwise * free it. */ if (data) *data = buf; else free(buf); if (data_len) *data_len = buf_len; return status; }