/* SANE - Scanner Access Now Easy. Copyright (C) 2011-2020 Rolf Bensch Copyright (C) 2007-2008 Nicolas Martin, Copyright (C) 2006-2007 Wittawat Yamwong 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; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. */ #include "../include/sane/config.h" #include #include #include #ifdef USE_PTHREAD # include #endif #include /* sigaction(POSIX) */ #include /* POSIX: write read close pipe */ #ifdef HAVE_FCNTL_H # include #endif #include "pixma_rename.h" #include "pixma.h" # define DEBUG_NOT_STATIC # include "../include/sane/sane.h" # include "../include/sane/sanei.h" # include "../include/sane/saneopts.h" # include "../include/sane/sanei_thread.h" # include "../include/sane/sanei_backend.h" # include "../include/sane/sanei_config.h" # include "../include/sane/sanei_jpeg.h" # include "../include/sane/sanei_usb.h" #ifdef NDEBUG # define PDBG(x) #else # define PDBG(x) IF_DBG(x) #endif /* NDEBUG */ #ifdef __GNUC__ # define UNUSED(v) (void) v #else # define UNUSED(v) #endif #define DECL_CTX pixma_sane_t *ss = check_handle(h) #define OPT_IN_CTX ss->opt #define SOD(opt) OPT_IN_CTX[opt].sod #define OVAL(opt) OPT_IN_CTX[opt].val #define AUTO_GAMMA 2.2 /* pixma_sane_options.h generated by * scripts/pixma_gen_options.py h < pixma.c > pixma_sane_options.h */ #include "pixma_sane_options.h" #define BUTTON_GROUP_SIZE ( opt_adf_orientation - opt_button_1 + 1 ) #define BUTTON_GROUP_INDEX(x) ( x - opt_button_1 ) typedef struct pixma_sane_t { struct pixma_sane_t *next; pixma_t *s; pixma_scan_param_t sp; SANE_Bool cancel; /* valid states: idle, !idle && scanning, !idle && !scanning */ SANE_Bool idle; SANE_Bool scanning; SANE_Status last_read_status; /* valid if !idle && !scanning */ option_descriptor_t opt[opt_last]; char button_option_is_cached[BUTTON_GROUP_SIZE]; SANE_Range xrange, yrange; SANE_Word dpi_list[9]; /* up to 9600 dpi */ SANE_String_Const mode_list[6]; pixma_scan_mode_t mode_map[6]; uint8_t gamma_table[4096]; SANE_String_Const source_list[4]; pixma_paper_source_t source_map[4]; unsigned byte_pos_in_line, output_line_size; uint64_t image_bytes_read; unsigned page_count; /* valid for ADF */ SANE_Pid reader_taskid; int wpipe, rpipe; SANE_Bool reader_stop; /* Valid for JPEG source */ djpeg_dest_ptr jdst; struct jpeg_decompress_struct jpeg_cinfo; struct jpeg_error_mgr jpeg_err; SANE_Bool jpeg_header_seen; } pixma_sane_t; typedef struct { struct jpeg_source_mgr jpeg; pixma_sane_t *s; JOCTET *buffer; SANE_Byte *linebuffer; SANE_Int linebuffer_size; SANE_Int linebuffer_index; } pixma_jpeg_src_mgr; static const char vendor_str[] = "CANON"; static const char type_str[] = "multi-function peripheral"; static pixma_sane_t *first_scanner = NULL; static const SANE_Device **dev_list = NULL; static const char* conf_devices[MAX_CONF_DEVICES]; static void mark_all_button_options_cached ( struct pixma_sane_t * ss ) { int i; for (i = 0; i < (opt__group_5 - opt_button_1); i++ ) ss -> button_option_is_cached[i] = 1; } static SANE_Status config_attach_pixma(SANEI_Config * config, const char *devname) { int i; UNUSED(config); for (i=0; i < (MAX_CONF_DEVICES -1); i++) { if(conf_devices[i] == NULL) { conf_devices[i] = strdup(devname); return SANE_STATUS_GOOD; } } return SANE_STATUS_INVAL; } static SANE_Status map_error (int error) { if (error >= 0) return SANE_STATUS_GOOD; switch (error) { case PIXMA_ENOMEM: return SANE_STATUS_NO_MEM; case PIXMA_ECANCELED: return SANE_STATUS_CANCELLED; case PIXMA_EBUSY: return SANE_STATUS_DEVICE_BUSY; case PIXMA_EINVAL: return SANE_STATUS_INVAL; case PIXMA_EACCES: return SANE_STATUS_ACCESS_DENIED; case PIXMA_EPAPER_JAMMED: return SANE_STATUS_JAMMED; case PIXMA_ENO_PAPER: return SANE_STATUS_NO_DOCS; case PIXMA_ECOVER_OPEN: return SANE_STATUS_COVER_OPEN; case PIXMA_ENOTSUP: return SANE_STATUS_UNSUPPORTED; case PIXMA_EPROTO: case PIXMA_ENODEV: case PIXMA_EIO: case PIXMA_ETIMEDOUT: return SANE_STATUS_IO_ERROR; } PDBG (pixma_dbg (1, "BUG: unmapped error %d\n", error)); return SANE_STATUS_IO_ERROR; } static int getenv_atoi (const char *name, int def) { const char *str = getenv (name); return (str) ? atoi (str) : def; } #define CONST_CAST(t,x) (t)(x) static void free_block (const void * ptr) { free (CONST_CAST (void *, ptr)); } static void cleanup_device_list (void) { if (dev_list) { int i; for (i = 0; dev_list[i]; i++) { free_block ((const void *) dev_list[i]->name); free_block ((const void *) dev_list[i]->model); free_block ((const void *) dev_list[i]); } } free (dev_list); dev_list = NULL; } static void find_scanners (SANE_Bool local_only) { unsigned i, nscanners; cleanup_device_list (); nscanners = pixma_find_scanners (conf_devices, local_only); PDBG (pixma_dbg (3, "pixma_find_scanners() found %u devices\n", nscanners)); dev_list = (const SANE_Device **) calloc (nscanners + 1, sizeof (*dev_list)); if (!dev_list) return; for (i = 0; i != nscanners; i++) { SANE_Device *sdev = (SANE_Device *) calloc (1, sizeof (*sdev)); char *name, *model; if (!sdev) goto nomem; name = strdup (pixma_get_device_id (i)); model = strdup (pixma_get_device_model (i)); if (!name || !model) { free (name); free (model); free (sdev); goto nomem; } sdev->name = name; sdev->model = model; sdev->vendor = vendor_str; sdev->type = type_str; dev_list[i] = sdev; } /* dev_list is already NULL terminated by calloc(). */ return; nomem: PDBG (pixma_dbg (1, "WARNING:not enough memory for device list\n")); return; } static pixma_sane_t * check_handle (SANE_Handle h) { pixma_sane_t *p; for (p = first_scanner; p && (SANE_Handle) p != h; p = p->next) { } return p; } static void update_button_state (pixma_sane_t * ss, SANE_Int * info) { SANE_Int b1 = OVAL (opt_button_1).w; SANE_Int b2 = OVAL (opt_button_2).w; uint32_t ev = pixma_wait_event (ss->s, 300); switch (ev & ~PIXMA_EV_ACTION_MASK) { case PIXMA_EV_BUTTON1: b1 = 1; break; case PIXMA_EV_BUTTON2: b2 = 1; break; } if (b1 != OVAL (opt_button_1).w || b2 != OVAL (opt_button_2).w) { *info |= SANE_INFO_RELOAD_OPTIONS; OVAL (opt_button_1).w = b1; OVAL (opt_button_2).w = b2; OVAL (opt_original).w = GET_EV_ORIGINAL(ev); OVAL (opt_target).w = GET_EV_TARGET(ev); OVAL (opt_scan_resolution).w = GET_EV_DPI(ev); OVAL (opt_document_type).w = GET_EV_DOC(ev); OVAL (opt_adf_status).w = GET_EV_STAT(ev); OVAL (opt_adf_orientation).w = GET_EV_ORIENT(ev); } mark_all_button_options_cached(ss); } static SANE_Bool enable_option (pixma_sane_t * ss, SANE_Int o, SANE_Bool enable) { SANE_Word save = SOD (o).cap; if (enable) SOD (o).cap &= ~SANE_CAP_INACTIVE; else SOD (o).cap |= SANE_CAP_INACTIVE; return (save != SOD (o).cap); } static void clamp_value (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info) { SANE_Option_Descriptor *sod = &SOD (n); SANE_Word *va = (SANE_Word *) v; const SANE_Range *range = sod->constraint.range; int i, nmemb; nmemb = sod->size / sizeof (SANE_Word); for (i = 0; i < nmemb; i++) { SANE_Word value = va[i]; if (value < range->min) { value = range->min; } else if (value > range->max) { value = range->max; } if (range->quant != 0) { value = (value - range->min + range->quant / 2) / range->quant * range->quant; } if (value != va[i]) { va[i] = value; *info |= SANE_INFO_INEXACT; } } } /* create dynamic mode_list * ss: scanner device * tpu = 0: flatbed or ADF mode * 1 bit lineart, 8 bit grayscale and 24 bit color scans * tpu = 1: TPU mode * 16 bit grayscale and 48 bit color scans */ static void create_mode_list (pixma_sane_t * ss) { SANE_Bool tpu; const pixma_config_t *cfg; int i; cfg = pixma_get_config (ss->s); tpu = (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU); /* setup available mode */ i = 0; ss->mode_list[i] = SANE_VALUE_SCAN_MODE_COLOR; ss->mode_map[i] = PIXMA_SCAN_MODE_COLOR; i++; if (cfg->cap & PIXMA_CAP_GRAY) { ss->mode_list[i] = SANE_VALUE_SCAN_MODE_GRAY; ss->mode_map[i] = PIXMA_SCAN_MODE_GRAY; i++; } if (tpu && (cfg->cap & PIXMA_CAP_NEGATIVE)) { ss->mode_list[i] = SANE_I18N ("Negative color"); ss->mode_map[i] = PIXMA_SCAN_MODE_NEGATIVE_COLOR; i++; if (cfg->cap & PIXMA_CAP_GRAY) { ss->mode_list[i] = SANE_I18N ("Negative gray"); ss->mode_map[i] = PIXMA_SCAN_MODE_NEGATIVE_GRAY; i++; } } if (tpu && (cfg->cap & PIXMA_CAP_TPUIR) == PIXMA_CAP_TPUIR) { ss->mode_list[i] = SANE_I18N ("Infrared"); ss->mode_map[i] = PIXMA_SCAN_MODE_TPUIR; i++; } if (!tpu && (cfg->cap & PIXMA_CAP_48BIT)) { ss->mode_list[i] = SANE_I18N ("48 bits color"); ss->mode_map[i] = PIXMA_SCAN_MODE_COLOR_48; i++; if (cfg->cap & PIXMA_CAP_GRAY) { ss->mode_list[i] = SANE_I18N ("16 bits gray"); ss->mode_map[i] = PIXMA_SCAN_MODE_GRAY_16; i++; } } if (!tpu && (cfg->cap & PIXMA_CAP_LINEART)) { ss->mode_list[i] = SANE_VALUE_SCAN_MODE_LINEART; ss->mode_map[i] = PIXMA_SCAN_MODE_LINEART; i++; } /* terminate mode_list and mode_map */ ss->mode_list[i] = 0; ss->mode_map[i] = 0; } /* create dynamic dpi_list * ss: scanner device */ static void create_dpi_list (pixma_sane_t * ss) { const pixma_config_t *cfg; int i, j; int min; unsigned min_dpi; unsigned max_dpi; cfg = pixma_get_config (ss->s); /* get min/max dpi */ max_dpi = cfg->xdpi; min_dpi = 75; if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU && ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_TPUIR) { /* IR mode */ /*PDBG (pixma_dbg (4, "*create_dpi_list***** TPUIR mode\n"));*/ min_dpi = (cfg->tpuir_min_dpi) ? cfg->tpuir_min_dpi : 75; max_dpi = (cfg->tpuir_max_dpi) ? cfg->tpuir_max_dpi : cfg->xdpi; } else if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADF || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADFDUP) { /* ADF / TPU mode */ /*PDBG (pixma_dbg (4, "*create_dpi_list***** ADF/TPU mode\n"));*/ min_dpi = (cfg->adftpu_min_dpi) ? cfg->adftpu_min_dpi : 75; max_dpi = (cfg->adftpu_max_dpi) ? cfg->adftpu_max_dpi : cfg->xdpi; } else if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED && (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_COLOR_48 || ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_GRAY_16)) { /* 48 bits flatbed */ /*PDBG (pixma_dbg (4, "*create_dpi_list***** 48 bits flatbed mode\n"));*/ min_dpi = (cfg->min_xdpi_16) ? cfg->min_xdpi_16 : 75; } /* set j for min. dpi * 75 dpi: j = 0 * 150 dpi: j = 1 \ * 300 dpi: j = 2 |--> from cfg->adftpu_min_dpi or cfg->tpuir_min_dpi * 600 dpi: j = 3 / * */ j = -1; min = min_dpi / 75; do { j++; min >>= 1; } while (min > 0); /* create dpi_list * use j for min. dpi */ i = 0; do { i++; j++; ss->dpi_list[i] = 75 * (1 << (j - 1)); /* 75 x 2^(j-1) */ } while ((unsigned) ss->dpi_list[i] < max_dpi); ss->dpi_list[0] = i; /*PDBG (pixma_dbg (4, "*create_dpi_list***** min_dpi = %d, max_dpi = %d\n", min_dpi, max_dpi));*/ } static void select_value_from_list (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info) { SANE_Option_Descriptor *sod = &SOD (n); SANE_Word *va = (SANE_Word *) v; const SANE_Word *list = sod->constraint.word_list; int i, j, nmemb; nmemb = sod->size / sizeof (SANE_Word); for (i = 0; i < nmemb; i++) { SANE_Word value = va[i]; SANE_Word mindelta = abs (value - list[1]); SANE_Word nearest = list[1]; for (j = 2; j <= list[0]; j++) { SANE_Word delta = abs (value - list[j]); if (delta < mindelta) { mindelta = delta; nearest = list[j]; } if (mindelta == 0) break; } if (va[i] != nearest) { va[i] = nearest; *info |= SANE_INFO_INEXACT; } } } static SANE_Status control_scalar_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, SANE_Int * info) { option_descriptor_t *opt = &(OPT_IN_CTX[n]); SANE_Word val; /* PDBG (pixma_dbg (4, "*control_scalar_option***** n = %u, a = %u\n", n, a)); */ switch (a) { case SANE_ACTION_GET_VALUE: switch (opt->sod.type) { case SANE_TYPE_BOOL: case SANE_TYPE_INT: case SANE_TYPE_FIXED: *(SANE_Word *) v = opt->val.w; break; default: return SANE_STATUS_UNSUPPORTED; } return SANE_STATUS_GOOD; case SANE_ACTION_SET_VALUE: switch (opt->sod.type) { case SANE_TYPE_BOOL: val = *(SANE_Word *) v; if (val != SANE_TRUE && val != SANE_FALSE) return SANE_STATUS_INVAL; opt->val.w = val; break; case SANE_TYPE_INT: case SANE_TYPE_FIXED: if (opt->sod.constraint_type == SANE_CONSTRAINT_RANGE) clamp_value (ss, n, v, info); else if (opt->sod.constraint_type == SANE_CONSTRAINT_WORD_LIST) select_value_from_list (ss, n, v, info); opt->val.w = *(SANE_Word *) v; break; default: return SANE_STATUS_UNSUPPORTED; } *info |= opt->info; return SANE_STATUS_GOOD; case SANE_ACTION_SET_AUTO: switch (opt->sod.type) { case SANE_TYPE_BOOL: case SANE_TYPE_INT: case SANE_TYPE_FIXED: opt->val.w = opt->def.w; break; default: return SANE_STATUS_UNSUPPORTED; } *info |= opt->info; return SANE_STATUS_GOOD; } return SANE_STATUS_UNSUPPORTED; } static SANE_Status control_string_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, SANE_Int * info) { option_descriptor_t *opt = &(OPT_IN_CTX[n]); const SANE_String_Const *slist = opt->sod.constraint.string_list; SANE_String str = (SANE_String) v; /* PDBG (pixma_dbg (4, "*control_string_option***** n = %u, a = %u\n", n, a)); */ if (opt->sod.constraint_type == SANE_CONSTRAINT_NONE) { switch (a) { case SANE_ACTION_GET_VALUE: strcpy (str, opt->val.s); break; case SANE_ACTION_SET_AUTO: str = opt->def.s; /* fall through */ case SANE_ACTION_SET_VALUE: strncpy (opt->val.s, str, opt->sod.size - 1); *info |= opt->info; break; } return SANE_STATUS_GOOD; } else { int i; switch (a) { case SANE_ACTION_GET_VALUE: strcpy (str, slist[opt->val.w]); break; case SANE_ACTION_SET_AUTO: str = opt->def.ptr; /* fall through */ case SANE_ACTION_SET_VALUE: i = 0; while (slist[i] && strcasecmp (str, slist[i]) != 0) i++; if (!slist[i]) return SANE_STATUS_INVAL; if (strcmp (slist[i], str) != 0) { strcpy (str, slist[i]); *info |= SANE_INFO_INEXACT; } opt->val.w = i; *info |= opt->info; break; } return SANE_STATUS_GOOD; } } static SANE_Status control_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, SANE_Int * info) { SANE_Option_Descriptor *sod = &SOD (n); int result, i; const pixma_config_t *cfg; SANE_Int dummy; /* info may be null, better to set a dummy here then test everywhere */ if (info == NULL) info = &dummy; cfg = pixma_get_config (ss->s); /* PDBG (pixma_dbg (4, "*control_option***** n = %u, a = %u\n", n, a)); */ /* first deal with options that require special treatment */ result = SANE_STATUS_UNSUPPORTED; switch (n) { case opt_gamma_table: { int table_size = sod->size / sizeof (SANE_Word); int byte_cnt = table_size == 1024 ? 2 : 1; switch (a) { case SANE_ACTION_SET_VALUE: PDBG (pixma_dbg (4, "*control_option***** opt_gamma_table: SANE_ACTION_SET_VALUE with %d values ***** \n", table_size)); clamp_value (ss, n, v, info); if (byte_cnt == 1) { for (i = 0; i < table_size; i++) ss->gamma_table[i] = *((SANE_Int *) v + i); } else { for (i = 0; i < table_size; i++) { ss->gamma_table[i * 2] = *((SANE_Int *) v + i); ss->gamma_table[i * 2 + 1] = *((uint8_t *)((SANE_Int *) v + i) + 1); } } /* PDBG (pixma_hexdump (4, (uint8_t *)v, table_size * 4)); */ /* PDBG (pixma_hexdump (4, ss->gamma_table, table_size * byte_cnt)); */ break; case SANE_ACTION_GET_VALUE: PDBG (pixma_dbg (4, "*control_option***** opt_gamma_table: SANE_ACTION_GET_VALUE ***** \n")); if (byte_cnt == 1) { for (i = 0; i < table_size; i++) *((SANE_Int *) v + i) = ss->gamma_table[i]; } else { for (i = 0; i < table_size; i++) { *((SANE_Int *) v + i) = ss->gamma_table[i * 2]; *((uint8_t *)((SANE_Int *) v + i) + 1) = ss->gamma_table[i * 2 + 1]; } } break; case SANE_ACTION_SET_AUTO: PDBG (pixma_dbg (4, "*control_option***** opt_gamma_table: SANE_ACTION_SET_AUTO with gamma=%f ***** \n", SANE_UNFIX (OVAL (opt_gamma).w))); pixma_fill_gamma_table (SANE_UNFIX (OVAL (opt_gamma).w), ss->gamma_table, table_size); /* PDBG (pixma_hexdump (4, ss->gamma_table, table_size * byte_cnt)); */ break; default: return SANE_STATUS_UNSUPPORTED; } return SANE_STATUS_GOOD; } case opt_button_update: if (a == SANE_ACTION_SET_VALUE) { update_button_state (ss, info); return SANE_STATUS_GOOD; } else { return SANE_STATUS_INVAL; } break; case opt_button_1: case opt_button_2: case opt_original: case opt_target: case opt_scan_resolution: case opt_document_type: case opt_adf_status: case opt_adf_orientation: /* poll scanner if option is not cached */ if (! ss->button_option_is_cached[ BUTTON_GROUP_INDEX(n) ] ) update_button_state (ss, info); /* mark this option as read */ ss->button_option_is_cached[ BUTTON_GROUP_INDEX(n) ] = 0; } /* now deal with getting and setting of options */ switch (SOD (n).type) { case SANE_TYPE_BOOL: case SANE_TYPE_INT: case SANE_TYPE_FIXED: result = control_scalar_option (ss, n, a, v, info); break; case SANE_TYPE_STRING: result = control_string_option (ss, n, a, v, info); break; case SANE_TYPE_BUTTON: case SANE_TYPE_GROUP: PDBG (pixma_dbg (1, "BUG:control_option():Unhandled option\n")); result = SANE_STATUS_INVAL; break; } if (result != SANE_STATUS_GOOD) return result; /* deal with dependencies between options */ switch (n) { case opt_custom_gamma: if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO) { if (enable_option (ss, opt_gamma_table, OVAL (opt_custom_gamma).b)) *info |= SANE_INFO_RELOAD_OPTIONS; if (OVAL (opt_custom_gamma).b) sane_control_option (ss, opt_gamma_table, SANE_ACTION_SET_AUTO, NULL, NULL); } break; case opt_gamma: if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO) { int table_size = SOD (opt_gamma_table).size / sizeof(SANE_Word); PDBG (pixma_dbg (4, "*control_option***** gamma = %f *\n", SANE_UNFIX (OVAL (opt_gamma).w))); PDBG (pixma_dbg (4, "*control_option***** table size = %d *\n", (int)(SOD (opt_gamma_table).size / sizeof (SANE_Word)))); pixma_fill_gamma_table (SANE_UNFIX (OVAL (opt_gamma).w), ss->gamma_table, table_size); /* PDBG (pixma_hexdump (4, ss->gamma_table, table_size == 1024 ? 2048 : table_size)); */ } break; case opt_mode: if (cfg->cap & (PIXMA_CAP_48BIT|PIXMA_CAP_LINEART|PIXMA_CAP_TPUIR) && (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO)) { /* new mode selected: Color, Gray, ... */ /* PDBG (pixma_dbg (4, "*control_option***** mode = %u *\n", ss->mode_map[OVAL (opt_mode).w])); */ /* recreate dynamic lists */ create_dpi_list (ss); if (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_LINEART) { /* lineart */ enable_option (ss, opt_threshold, SANE_TRUE); enable_option (ss, opt_threshold_curve, SANE_TRUE); } else { /* all other modes */ enable_option (ss, opt_threshold, SANE_FALSE); enable_option (ss, opt_threshold_curve, SANE_FALSE); } *info |= SANE_INFO_RELOAD_OPTIONS; } break; case opt_source: if ((cfg->cap & (PIXMA_CAP_ADF|PIXMA_CAP_ADFDUP|PIXMA_CAP_TPU)) && (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO)) { /* new source selected: flatbed, ADF, TPU, ... */ /* to avoid fatal errors, * select first entry of dynamic mode_list * identifiers are unknown here */ OVAL (opt_mode).w = ss->mode_map[0]; /* recreate dynamic lists */ create_mode_list (ss); create_dpi_list (ss); /* to avoid fatal errors, * select first entry of dynamic dpi_list * identifiers are unknown here */ OVAL (opt_resolution).w = ss->dpi_list[1]; if (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_LINEART) { /* lineart */ enable_option (ss, opt_threshold, SANE_TRUE); enable_option (ss, opt_threshold_curve, SANE_TRUE); } else { /* all other modes */ enable_option (ss, opt_threshold, SANE_FALSE); enable_option (ss, opt_threshold_curve, SANE_FALSE); } if (cfg->cap & (PIXMA_CAP_ADF_WAIT)) { /* adf-wait */ enable_option (ss, opt_adf_wait, SANE_TRUE); } else { /* disable adf-wait */ enable_option (ss, opt_adf_wait, SANE_FALSE); } *info |= SANE_INFO_RELOAD_OPTIONS; } break; } return result; } #ifndef NDEBUG static void print_scan_param (int level, const pixma_scan_param_t * sp) { pixma_dbg (level, "Scan parameters\n"); pixma_dbg (level, " line_size=%"PRIu64" image_size=%"PRIu64" channels=%u depth=%u\n", sp->line_size, sp->image_size, sp->channels, sp->depth); pixma_dbg (level, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n", sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h); pixma_dbg (level, " gamma=%f gamma_table=%p source=%d\n", sp->gamma, sp->gamma_table, sp->source); pixma_dbg (level, " adf-wait=%d\n", sp->adf_wait); } #endif static int calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp) { int x1, y1, x2, y2; int error; memset (sp, 0, sizeof (*sp)); sp->channels = (OVAL (opt_mode).w == 0) ? 3 : 1; sp->depth = (OVAL (opt_mode).w == 2) ? 1 : 8; sp->xdpi = sp->ydpi = OVAL (opt_resolution).w; #define PIXEL(x,dpi) (int)((SANE_UNFIX(x) / 25.4 * (dpi)) + 0.5) x1 = PIXEL (OVAL (opt_tl_x).w, sp->xdpi); x2 = PIXEL (OVAL (opt_br_x).w, sp->xdpi); if (x2 < x1) { int temp = x1; x1 = x2; x2 = temp; } y1 = PIXEL (OVAL (opt_tl_y).w, sp->ydpi); y2 = PIXEL (OVAL (opt_br_y).w, sp->ydpi); if (y2 < y1) { int temp = y1; y1 = y2; y2 = temp; } #undef PIXEL sp->x = x1; sp->y = y1; sp->w = x2 - x1; sp->h = y2 - y1; if (sp->w == 0) sp->w = 1; if (sp->h == 0) sp->h = 1; sp->tpu_offset_added = 0; sp->gamma = SANE_UNFIX (OVAL (opt_gamma).w); sp->gamma_table = ss->gamma_table; sp->source = ss->source_map[OVAL (opt_source).w]; sp->mode = ss->mode_map[OVAL (opt_mode).w]; sp->adf_pageid = ss->page_count; sp->threshold = 2.55 * OVAL (opt_threshold).w; sp->threshold_curve = OVAL (opt_threshold_curve).w; sp->adf_wait = OVAL (opt_adf_wait).w; error = pixma_check_scan_param (ss->s, sp); if (error < 0) { PDBG (pixma_dbg (1, "BUG:calc_scan_param() failed %d\n", error)); PDBG (print_scan_param (1, sp)); } return error; } static void init_option_descriptors (pixma_sane_t * ss) { const pixma_config_t *cfg; int i; cfg = pixma_get_config (ss->s); /* PDBG (pixma_dbg (4, "*init_option_descriptors*****\n")); */ /* setup range for the scan area. */ ss->xrange.min = SANE_FIX (0); ss->xrange.max = SANE_FIX (cfg->width / 75.0 * 25.4); ss->xrange.quant = SANE_FIX (0); ss->yrange.min = SANE_FIX (0); ss->yrange.max = SANE_FIX (cfg->height / 75.0 * 25.4); ss->yrange.quant = SANE_FIX (0); /* mode_list and source_list were already NULL-terminated, * because the whole pixma_sane_t was cleared during allocation. */ /* setup available mode. */ create_mode_list (ss); /* setup dpi up to the value supported by the scanner. */ create_dpi_list (ss); /* setup paper source */ i = 0; ss->source_list[i] = SANE_I18N ("Flatbed"); ss->source_map[i] = PIXMA_SOURCE_FLATBED; i++; if (cfg->cap & PIXMA_CAP_ADF) { ss->source_list[i] = SANE_I18N ("Automatic Document Feeder"); ss->source_map[i] = PIXMA_SOURCE_ADF; i++; } if ((cfg->cap & PIXMA_CAP_ADFDUP) == PIXMA_CAP_ADFDUP) { ss->source_list[i] = SANE_I18N ("ADF Duplex"); ss->source_map[i] = PIXMA_SOURCE_ADFDUP; i++; } if (cfg->cap & PIXMA_CAP_TPU) { ss->source_list[i] = SANE_I18N ("Transparency Unit"); ss->source_map[i] = PIXMA_SOURCE_TPU; i++; } build_option_descriptors (ss); /* Enable options that are available only in some scanners. */ if (cfg->cap & PIXMA_CAP_GAMMA_TABLE) { SANE_Option_Descriptor *sod = &SOD (opt_gamma_table); /* some scanners have a large gamma table with 4096 entries */ if (cfg->cap & PIXMA_CAP_GT_4096) { static const SANE_Range constraint_gamma_table_4096 = { 0,0xff,0 }; sod->desc = SANE_I18N("Gamma-correction table with 4096 entries. In color mode this option equally affects the red, green, and blue channels simultaneously (i.e., it is an intensity gamma table)."); sod->size = 4096 * sizeof(SANE_Word); sod->constraint.range = &constraint_gamma_table_4096; } /* PDBG (pixma_dbg (4, "*%s***** PIXMA_CAP_GAMMA_TABLE ***** \n", __func__)); */ /* PDBG (pixma_dbg (4, "%s: gamma_table_contraint.max = %d\n", __func__, sod->constraint.range->max)); */ /* PDBG (pixma_dbg (4, "%s: gamma_table_size = %d\n", __func__, sod->size / sizeof(SANE_Word))); */ /* activate option gamma */ enable_option (ss, opt_gamma, SANE_TRUE); sane_control_option (ss, opt_gamma, SANE_ACTION_SET_AUTO, NULL, NULL); /* activate option custom gamma table */ enable_option (ss, opt_custom_gamma, SANE_TRUE); sane_control_option (ss, opt_custom_gamma, SANE_ACTION_SET_AUTO, NULL, NULL); } enable_option (ss, opt_button_controlled, ((cfg->cap & PIXMA_CAP_EVENTS) != 0)); } /* Writing to reader_ss outside reader_process() is a BUG! */ static pixma_sane_t *reader_ss = NULL; static void reader_signal_handler (int sig) { if (reader_ss) { reader_ss->reader_stop = SANE_TRUE; /* reader process is ended by SIGTERM, so no cancel in this case */ if (sig != SIGTERM) pixma_cancel (reader_ss->s); } } static int write_all (pixma_sane_t * ss, void *buf_, size_t size) { uint8_t *buf = (uint8_t *) buf_; int count; while (size != 0 && !ss->reader_stop) { count = write (ss->wpipe, buf, size); if (count == -1 && errno != EINTR) break; if (count == -1 && errno == EINTR) continue; buf += count; size -= count; } return buf - (uint8_t *) buf_; } /* NOTE: reader_loop() runs either in a separate thread or process. */ static SANE_Status reader_loop (pixma_sane_t * ss) { void *buf; unsigned bufsize; int count = 0; PDBG (pixma_dbg (3, "Reader task started\n")); /*bufsize = ss->sp.line_size + 1;*/ /* XXX: "odd" bufsize for testing pixma_read_image() */ bufsize = ss->sp.line_size; /* bufsize EVEN needed by Xsane for 48 bits depth */ buf = malloc (bufsize); if (!buf) { count = PIXMA_ENOMEM; goto done; } count = pixma_activate_connection (ss->s); if (count < 0) goto done; pixma_enable_background (ss->s, 1); if (OVAL (opt_button_controlled).b && ss->page_count == 0) { int start = 0; #ifndef NDEBUG pixma_dbg (1, "==== Button-controlled scan mode is enabled.\n"); pixma_dbg (1, "==== To proceed, press 'SCAN' or 'COLOR' button. " "To cancel, press 'GRAY' or 'END' button.\n"); #endif while (pixma_wait_event (ss->s, 10) != 0) { } while (!start) { uint32_t events; if (ss->reader_stop) { count = PIXMA_ECANCELED; goto done; } events = pixma_wait_event (ss->s, 1000); switch (events & ~PIXMA_EV_ACTION_MASK) { case PIXMA_EV_BUTTON1: start = 1; break; case PIXMA_EV_BUTTON2: count = PIXMA_ECANCELED; goto done; } } } count = pixma_scan (ss->s, &ss->sp); if (count >= 0) { while ((count = pixma_read_image (ss->s, buf, bufsize)) > 0) { if (write_all (ss, buf, count) != count) pixma_cancel (ss->s); } } done: pixma_enable_background (ss->s, 0); pixma_deactivate_connection (ss->s); free (buf); close (ss->wpipe); ss->wpipe = -1; if (count >= 0) { PDBG (pixma_dbg (3, "Reader task terminated\n")); } else { PDBG (pixma_dbg (2, "Reader task terminated: %s\n", pixma_strerror (count))); } return map_error (count); } static int reader_process (void *arg) { pixma_sane_t *ss = (pixma_sane_t *) arg; struct SIGACTION sa; reader_ss = ss; memset (&sa, 0, sizeof (sa)); sigemptyset (&sa.sa_mask); sa.sa_handler = reader_signal_handler; /* FIXME: which signal else? */ sigaction (SIGHUP, &sa, NULL); sigaction (SIGINT, &sa, NULL); sigaction (SIGPIPE, &sa, NULL); sigaction (SIGTERM, &sa, NULL); close (ss->rpipe); ss->rpipe = -1; return reader_loop (ss); } static int reader_thread (void *arg) { pixma_sane_t *ss = (pixma_sane_t *) arg; #ifdef USE_PTHREAD /* Block SIGPIPE. We will handle this in reader_loop() by checking ss->reader_stop and the return value from write(). */ sigset_t sigs; sigemptyset (&sigs); sigaddset (&sigs, SIGPIPE); pthread_sigmask (SIG_BLOCK, &sigs, NULL); #endif /* USE_PTHREAD */ return reader_loop (ss); } static SANE_Pid terminate_reader_task (pixma_sane_t * ss, int *exit_code) { SANE_Pid result, pid; int status = 0; pid = ss->reader_taskid; if (!sanei_thread_is_valid (pid)) return pid; if (sanei_thread_is_forked ()) { sanei_thread_kill (pid); } else { ss->reader_stop = SANE_TRUE; /* pixma_cancel (ss->s); What is this for ? Makes end-of-scan buggy => removing */ } result = sanei_thread_waitpid (pid, &status); sanei_thread_invalidate (ss->reader_taskid); if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP) ss->idle = SANE_TRUE; if (result == pid) { if (exit_code) *exit_code = status; return pid; } else { PDBG (pixma_dbg (1, "WARNING:waitpid() failed %s\n", strerror (errno))); sanei_thread_invalidate (pid); return pid; } } static int start_reader_task (pixma_sane_t * ss) { int fds[2]; SANE_Pid pid; int is_forked; if (ss->rpipe != -1 || ss->wpipe != -1) { PDBG (pixma_dbg (1, "BUG:rpipe = %d, wpipe = %d\n", ss->rpipe, ss->wpipe)); close (ss->rpipe); close (ss->wpipe); ss->rpipe = -1; ss->wpipe = -1; } if (sanei_thread_is_valid (ss->reader_taskid)) { PDBG (pixma_dbg (1, "BUG:reader_taskid(%ld) != -1\n", (long) ss->reader_taskid)); terminate_reader_task (ss, NULL); } if (pipe (fds) == -1) { PDBG (pixma_dbg (1, "ERROR:start_reader_task():pipe() failed %s\n", strerror (errno))); return PIXMA_ENOMEM; } ss->rpipe = fds[0]; ss->wpipe = fds[1]; ss->reader_stop = SANE_FALSE; is_forked = sanei_thread_is_forked (); if (is_forked) { pid = sanei_thread_begin (reader_process, ss); if (sanei_thread_is_valid (pid)) { close (ss->wpipe); ss->wpipe = -1; } } else { pid = sanei_thread_begin (reader_thread, ss); } if (!sanei_thread_is_valid (pid)) { close (ss->wpipe); close (ss->rpipe); ss->wpipe = -1; ss->rpipe = -1; PDBG (pixma_dbg (1, "ERROR:unable to start reader task\n")); return PIXMA_ENOMEM; } PDBG (pixma_dbg (3, "Reader task id=%ld (%s)\n", (long) pid, (is_forked) ? "forked" : "threaded")); ss->reader_taskid = pid; return 0; } /* libJPEG API callbacks */ static void jpeg_init_source(j_decompress_ptr __sane_unused__ cinfo) { /* No-op */ } static void jpeg_term_source(j_decompress_ptr __sane_unused__ cinfo) { /* No-op */ } static boolean jpeg_fill_input_buffer(j_decompress_ptr cinfo) { pixma_jpeg_src_mgr *mgr = (pixma_jpeg_src_mgr *)cinfo->src; int size; int retry; for (retry = 0; retry < 30; retry ++ ) { size = read (mgr->s->rpipe, mgr->buffer, 1024); if (size == 0) { return FALSE; } else if (size < 0) { sleep (1); } else { mgr->jpeg.next_input_byte = mgr->buffer; mgr->jpeg.bytes_in_buffer = size; return TRUE; } } return FALSE; } static void jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { pixma_jpeg_src_mgr *mgr = (pixma_jpeg_src_mgr *)cinfo->src; if (num_bytes > 0) { /* Read and throw away extra */ while (num_bytes > (long)mgr->jpeg.bytes_in_buffer) { num_bytes -= (long)mgr->jpeg.bytes_in_buffer; jpeg_fill_input_buffer(cinfo); } /* Update jpeg info structure with leftover */ mgr->jpeg.next_input_byte += (size_t) num_bytes; mgr->jpeg.bytes_in_buffer -= (size_t) num_bytes; } } /* Pixma JPEG reader helpers */ static SANE_Status pixma_jpeg_start(pixma_sane_t *s) { pixma_jpeg_src_mgr *mgr; s->jpeg_cinfo.err = jpeg_std_error(&s->jpeg_err); jpeg_create_decompress(&s->jpeg_cinfo); s->jpeg_cinfo.src = (struct jpeg_source_mgr *)(*s->jpeg_cinfo.mem->alloc_small)((j_common_ptr)&s->jpeg_cinfo, JPOOL_PERMANENT, sizeof(pixma_jpeg_src_mgr)); memset(s->jpeg_cinfo.src, 0, sizeof(pixma_jpeg_src_mgr)); mgr = (pixma_jpeg_src_mgr *)s->jpeg_cinfo.src; mgr->s = s; mgr->buffer = (JOCTET *)(*s->jpeg_cinfo.mem->alloc_small)((j_common_ptr)&s->jpeg_cinfo, JPOOL_PERMANENT, 1024 * sizeof(JOCTET)); mgr->jpeg.init_source = jpeg_init_source; mgr->jpeg.fill_input_buffer = jpeg_fill_input_buffer; mgr->jpeg.skip_input_data = jpeg_skip_input_data; mgr->jpeg.resync_to_restart = jpeg_resync_to_restart; mgr->jpeg.term_source = jpeg_term_source; mgr->jpeg.bytes_in_buffer = 0; mgr->jpeg.next_input_byte = NULL; s->jpeg_header_seen = 0; return SANE_STATUS_GOOD; } static SANE_Status pixma_jpeg_read_header(pixma_sane_t *s) { pixma_jpeg_src_mgr *src = (pixma_jpeg_src_mgr *)s->jpeg_cinfo.src; if (jpeg_read_header(&s->jpeg_cinfo, TRUE)) { s->jdst = sanei_jpeg_jinit_write_ppm(&s->jpeg_cinfo); if (jpeg_start_decompress(&s->jpeg_cinfo)) { int size; DBG(3, "%s: w: %d, h: %d, components: %d\n", __func__, s->jpeg_cinfo.output_width, s->jpeg_cinfo.output_height, s->jpeg_cinfo.output_components); size = s->jpeg_cinfo.output_width * s->jpeg_cinfo.output_components * 1; src->linebuffer = (*s->jpeg_cinfo.mem->alloc_large)((j_common_ptr)&s->jpeg_cinfo, JPOOL_PERMANENT, size); src->linebuffer_size = 0; src->linebuffer_index = 0; s->jpeg_header_seen = 1; return SANE_STATUS_GOOD; } else { DBG(0, "%s: decompression failed\n", __func__); return SANE_STATUS_IO_ERROR; } } else { DBG(0, "%s: cannot read JPEG header\n", __func__); return SANE_STATUS_IO_ERROR; } } static void pixma_jpeg_finish(pixma_sane_t *ss) { jpeg_destroy_decompress(&ss->jpeg_cinfo); } static void pixma_jpeg_read(pixma_sane_t *ss, SANE_Byte *data, SANE_Int max_length, SANE_Int *length) { struct jpeg_decompress_struct cinfo = ss->jpeg_cinfo; pixma_jpeg_src_mgr *src = (pixma_jpeg_src_mgr *)ss->jpeg_cinfo.src; int l; *length = 0; /* copy from line buffer if available */ if (src->linebuffer_size && src->linebuffer_index < src->linebuffer_size) { *length = src->linebuffer_size - src->linebuffer_index; if (*length > max_length) *length = max_length; memcpy(data, src->linebuffer + src->linebuffer_index, *length); src->linebuffer_index += *length; return; } if (cinfo.output_scanline >= cinfo.output_height) { *length = 0; return; } /* scanlines of decompressed data will be in ss->jdst->buffer * only one line at time is supported */ l = jpeg_read_scanlines(&cinfo, ss->jdst->buffer, 1); if (l == 0) return; /* from ss->jdst->buffer to linebuffer * linebuffer holds width * bytesperpixel */ (*ss->jdst->put_pixel_rows)(&cinfo, ss->jdst, 1, (char *)src->linebuffer); *length = ss->sp.w * ss->sp.channels; /* Convert RGB into grayscale */ if (ss->sp.channels == 1) { unsigned int i; unsigned char *d = (unsigned char *)src->linebuffer; unsigned char *s = (unsigned char *)src->linebuffer; for (i = 0; i < ss->sp.w; i++) { /* Using BT.709 luma formula, fixed-point */ int sum = ( s[0]*2126 + s[1]*7152 + s[2]*722 ); *d = sum / 10000; d ++; s += 3; } } /* Maybe pack into lineary binary image */ if (ss->sp.depth == 1) { *length /= 8; unsigned int i; unsigned char *d = (unsigned char *)src->linebuffer; unsigned char *s = (unsigned char *)src->linebuffer; unsigned char b = 0; for (i = 1; i < ss->sp.w + 1; i++) { if (*(s++) > 127) b = (b << 1) | 0; else b = (b << 1) | 1; } if ((i % 8) == 0) *(d++) = b; } src->linebuffer_size = *length; src->linebuffer_index = 0; if (*length > max_length) *length = max_length; memcpy(data, src->linebuffer + src->linebuffer_index, *length); src->linebuffer_index += *length; } static SANE_Status read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen) { int count, status; if (readlen) *readlen = 0; if (ss->image_bytes_read >= ss->sp.image_size) return SANE_STATUS_EOF; do { if (ss->cancel) /* ss->rpipe has already been closed by sane_cancel(). */ return SANE_STATUS_CANCELLED; if (ss->sp.mode_jpeg && !ss->jpeg_header_seen) { status = pixma_jpeg_read_header(ss); if (status != SANE_STATUS_GOOD) { close (ss->rpipe); pixma_jpeg_finish(ss); ss->rpipe = -1; if (sanei_thread_is_valid (terminate_reader_task (ss, &status)) && status != SANE_STATUS_GOOD) { return status; } else { /* either terminate_reader_task failed or rpipe was closed but we expect more data */ return SANE_STATUS_IO_ERROR; } } } if (ss->sp.mode_jpeg) { count = -1; pixma_jpeg_read(ss, buf, size, &count); } else count = read (ss->rpipe, buf, size); } while (count == -1 && errno == EINTR); if (count == -1) { if (errno == EAGAIN) return SANE_STATUS_GOOD; if (!ss->cancel) { PDBG (pixma_dbg (1, "WARNING:read_image():read() failed %s\n", strerror (errno))); } close (ss->rpipe); ss->rpipe = -1; terminate_reader_task (ss, NULL); if (ss->sp.mode_jpeg) pixma_jpeg_finish(ss); return SANE_STATUS_IO_ERROR; } /* here count >= 0 */ ss->image_bytes_read += count; if (ss->image_bytes_read > ss->sp.image_size) { PDBG (pixma_dbg (1, "BUG:ss->image_bytes_read > ss->sp.image_size\n")); } if (ss->image_bytes_read >= ss->sp.image_size) { close (ss->rpipe); ss->rpipe = -1; terminate_reader_task (ss, NULL); if (ss->sp.mode_jpeg) pixma_jpeg_finish(ss); } else if (count == 0) { PDBG (pixma_dbg (3, "read_image():reader task closed the pipe:%" PRIu64" bytes received, %"PRIu64" bytes expected\n", ss->image_bytes_read, ss->sp.image_size)); close (ss->rpipe); if (ss->sp.mode_jpeg) pixma_jpeg_finish(ss); ss->rpipe = -1; if (sanei_thread_is_valid (terminate_reader_task (ss, &status)) && status != SANE_STATUS_GOOD) { return status; } else { /* either terminate_reader_task failed or rpipe was closed but we expect more data */ return SANE_STATUS_IO_ERROR; } } if (readlen) *readlen = count; return SANE_STATUS_GOOD; } /******************************************************************* ** SANE API *******************************************************************/ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { int status, myversion, i; SANEI_Config config; UNUSED (authorize); if (!version_code) return SANE_STATUS_INVAL; myversion = 100 * PIXMA_VERSION_MAJOR + PIXMA_VERSION_MINOR; *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, myversion); DBG_INIT (); sanei_thread_init (); pixma_set_debug_level (DBG_LEVEL); PDBG(pixma_dbg(2, "pixma is compiled %s pthread support.\n", (sanei_thread_is_forked () ? "without" : "with"))); for (i = 0; i < MAX_CONF_DEVICES; i++) conf_devices[i] = NULL; config.count = 0; config.descriptors = NULL; config.values = NULL; if (sanei_configure_attach(PIXMA_CONFIG_FILE, &config, config_attach_pixma) != SANE_STATUS_GOOD) PDBG(pixma_dbg(2, "Could not read pixma configuration file: %s\n", PIXMA_CONFIG_FILE)); status = pixma_init (); if (status < 0) { PDBG (pixma_dbg (2, "pixma_init() failed %s\n", pixma_strerror (status))); } return map_error (status); } void sane_exit (void) { while (first_scanner) sane_close (first_scanner); cleanup_device_list (); pixma_cleanup (); sanei_usb_exit (); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { if (!device_list) return SANE_STATUS_INVAL; find_scanners (local_only); *device_list = dev_list; return (dev_list) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { unsigned i, j, nscanners; int error = 0; pixma_sane_t *ss = NULL; const pixma_config_t *cfg; if (!name || !h) return SANE_STATUS_INVAL; *h = NULL; nscanners = pixma_find_scanners (conf_devices, SANE_FALSE); if (nscanners == 0) return SANE_STATUS_INVAL; /* also get device id if we replay a xml file * otherwise name contains the xml filename * and further replay will fail */ if (name[0] == '\0' || strstr (name, ".xml")) name = pixma_get_device_id (0); /* Have we already opened the scanner? */ for (ss = first_scanner; ss; ss = ss->next) { if (strcmp (pixma_get_string (ss->s, PIXMA_STRING_ID), name) == 0) { /* We have already opened it! */ return SANE_STATUS_DEVICE_BUSY; } } i = 0; while (strcmp (pixma_get_device_id (i), name) != 0) { if (++i >= nscanners) return SANE_STATUS_INVAL; } cfg = pixma_get_device_config (i); if ((cfg->cap & PIXMA_CAP_EXPERIMENT) != 0) { #ifndef NDEBUG pixma_dbg (1, "WARNING:" "Experimental backend CAN DAMAGE your hardware!\n"); if (getenv_atoi ("PIXMA_EXPERIMENT", 0) == 0) { pixma_dbg (1, "Experimental SANE backend for %s is disabled " "by default.\n", pixma_get_device_model (i)); pixma_dbg (1, "To enable it, set the environment variable " "PIXMA_EXPERIMENT to non-zero.\n"); return SANE_STATUS_UNSUPPORTED; } #else return SANE_STATUS_UNSUPPORTED; #endif } ss = (pixma_sane_t *) calloc (1, sizeof (*ss)); if (!ss) return SANE_STATUS_NO_MEM; ss->next = first_scanner; first_scanner = ss; sanei_thread_initialize (ss->reader_taskid); ss->wpipe = -1; ss->rpipe = -1; ss->idle = SANE_TRUE; ss->scanning = SANE_FALSE; ss->sp.frontend_cancel = SANE_FALSE; for (j=0; j < BUTTON_GROUP_SIZE; j++) ss->button_option_is_cached[j] = 0; error = pixma_open (i, &ss->s); if (error < 0) { sane_close (ss); return map_error (error); } pixma_enable_background (ss->s, 0); init_option_descriptors (ss); *h = ss; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle h) { pixma_sane_t **p, *ss; for (p = &first_scanner; *p && *p != (pixma_sane_t *) h; p = &((*p)->next)) { } if (!(*p)) return; ss = *p; sane_cancel (ss); pixma_close (ss->s); *p = ss->next; free (ss); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int n) { DECL_CTX; if (ss && 0 <= n && n < opt_last) return &SOD (n); return NULL; } SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int * i) { DECL_CTX; SANE_Int info = 0; int error; option_descriptor_t *opt; if (i) *i = 0; if (!ss) return SANE_STATUS_INVAL; if (n < 0 || n >= opt_last) return SANE_STATUS_UNSUPPORTED; if (!ss->idle && a != SANE_ACTION_GET_VALUE) { PDBG (pixma_dbg (3, "Warning: !idle && !SANE_ACTION_GET_VALUE\n")); if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP) return SANE_STATUS_INVAL; } opt = &(OPT_IN_CTX[n]); if (!SANE_OPTION_IS_ACTIVE (opt->sod.cap)) return SANE_STATUS_INVAL; switch (a) { case SANE_ACTION_SET_VALUE: if ((opt->sod.type != SANE_TYPE_BUTTON && !v) || !SANE_OPTION_IS_SETTABLE (opt->sod.cap)) return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */ break; case SANE_ACTION_SET_AUTO: if (!(opt->sod.cap & SANE_CAP_AUTOMATIC) || !SANE_OPTION_IS_SETTABLE (opt->sod.cap)) return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */ break; case SANE_ACTION_GET_VALUE: if (!v || !(opt->sod.cap & SANE_CAP_SOFT_DETECT)) return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */ break; default: return SANE_STATUS_UNSUPPORTED; } error = control_option (ss, n, a, v, &info); if (error == SANE_STATUS_GOOD && i) *i = info; return error; } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { DECL_CTX; pixma_scan_param_t temp, *sp; if (!ss || !p) return SANE_STATUS_INVAL; if (!ss->idle) { sp = &ss->sp; /* sp is calculated in sane_start() */ } else { calc_scan_param (ss, &temp); sp = &temp; } p->format = (sp->channels == 3) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; p->last_frame = SANE_TRUE; p->lines = sp->h; p->depth = sp->depth; p->pixels_per_line = sp->w; /* p->bytes_per_line = sp->line_size; NOTE: It should work this way, but it doesn't. No SANE frontend can cope with this. */ p->bytes_per_line = (sp->w * sp->channels * sp->depth) / 8; return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle h) { DECL_CTX; int error = 0; if (!ss) return SANE_STATUS_INVAL; if (!ss->idle && ss->scanning) { PDBG (pixma_dbg (3, "Warning in Sane_start: !idle && scanning. idle=%d, ss->scanning=%d\n", ss->idle, ss->scanning)); if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP) return SANE_STATUS_INVAL; } ss->cancel = SANE_FALSE; if (ss->idle || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU) ss->page_count = 0; /* start from idle state or scan from flatbed or TPU */ else ss->page_count++; if (calc_scan_param (ss, &ss->sp) < 0) return SANE_STATUS_INVAL; /* Prepare the JPEG decompressor, if needed */ if (ss->sp.mode_jpeg) { SANE_Status status; status = pixma_jpeg_start(ss); if (status != SANE_STATUS_GOOD) { PDBG (pixma_dbg(1, "%s: pixma_jpeg_start: %s\n", __func__, sane_strstatus(status)) ); return status; } } ss->image_bytes_read = 0; /* TODO: Check paper here in sane_start(). A function like pixma_get_status() is needed. */ error = start_reader_task (ss); if (error >= 0) { ss->output_line_size = (ss->sp.w * ss->sp.channels * ss->sp.depth) / 8; ss->byte_pos_in_line = 0; ss->last_read_status = SANE_STATUS_GOOD; ss->scanning = SANE_TRUE; ss->idle = SANE_FALSE; if (ss->sp.mode_jpeg && !ss->jpeg_header_seen) { SANE_Status status; status = pixma_jpeg_read_header(ss); if (status != SANE_STATUS_GOOD) { close (ss->rpipe); pixma_jpeg_finish(ss); ss->rpipe = -1; if (sanei_thread_is_valid (terminate_reader_task (ss, &error)) && error != SANE_STATUS_GOOD) { return error; } } } } return map_error (error); } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { DECL_CTX; int sum, n; /* Due to 32 pixels alignment, sizeof(temp) is to be greater than: * max(nchannels) * max (sp.line_size - output_line_size) * so currently: 3 * 32 = 96 for better end line cropping efficiency */ SANE_Byte temp[100]; SANE_Status status; if (len) *len = 0; if (!ss || !buf || !len) return SANE_STATUS_INVAL; if (ss->cancel) return SANE_STATUS_CANCELLED; if ((ss->idle) && (ss->sp.source == PIXMA_SOURCE_ADF || ss->sp.source == PIXMA_SOURCE_ADFDUP)) return SANE_STATUS_INVAL; if (!ss->scanning) return ss->last_read_status; status = SANE_STATUS_GOOD; /* CCD scanners use software lineart * the scanner must scan 24 bit color or 8 bit grayscale for one bit lineart */ if ((ss->sp.line_size - ((ss->sp.software_lineart == 1) ? (ss->output_line_size * 8) : ss->output_line_size)) == 0) { status = read_image (ss, buf, maxlen, &sum); } else { /* FIXME: Because there is no frontend that can cope with padding at the end of line, we've to remove it here in the backend! */ PDBG (pixma_dbg (1, "*sane_read***** Warning: padding may cause incomplete scan results\n")); sum = 0; while (sum < maxlen) { if (ss->byte_pos_in_line < ss->output_line_size) { n = ss->output_line_size - ss->byte_pos_in_line; if ((maxlen - sum) < n) n = maxlen - sum; status = read_image (ss, buf, n, &n); if (n == 0) break; sum += n; buf += n; ss->byte_pos_in_line += n; } else { /* skip padding */ n = ss->sp.line_size - ss->byte_pos_in_line; if (n > (int) sizeof (temp)) { PDBG (pixma_dbg (3, "Inefficient skip buffer. Should be %d\n", n)); n = sizeof (temp); } status = read_image (ss, temp, n, &n); if (n == 0) break; ss->byte_pos_in_line += n; if (ss->byte_pos_in_line == ss->sp.line_size) ss->byte_pos_in_line = 0; } } } if (ss->cancel) status = SANE_STATUS_CANCELLED; else if ((status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF) && sum > 0) { *len = sum; status = SANE_STATUS_GOOD; } ss->scanning = (status == SANE_STATUS_GOOD); ss->last_read_status = status; return status; } void sane_cancel (SANE_Handle h) { DECL_CTX; if (!ss) return; ss->cancel = SANE_TRUE; ss->sp.frontend_cancel = SANE_TRUE; if (ss->idle) return; close (ss->rpipe); if (ss->sp.mode_jpeg) pixma_jpeg_finish(ss); ss->rpipe = -1; terminate_reader_task (ss, NULL); ss->idle = SANE_TRUE; } SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m) { DECL_CTX; if (!ss || ss->idle || ss->rpipe == -1) return SANE_STATUS_INVAL; #ifdef HAVE_FCNTL_H PDBG (pixma_dbg (2, "Setting %sblocking mode\n", (m) ? "non-" : "")); if (fcntl (ss->rpipe, F_SETFL, (m) ? O_NONBLOCK : 0) == -1) { PDBG (pixma_dbg (1, "WARNING:fcntl(F_SETFL) failed %s\n", strerror (errno))); return SANE_STATUS_UNSUPPORTED; } return SANE_STATUS_GOOD; #else return (m) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD; #endif } SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd) { DECL_CTX; *fd = -1; if (!ss || !fd || ss->idle || ss->rpipe == -1) return SANE_STATUS_INVAL; *fd = ss->rpipe; return SANE_STATUS_GOOD; } /* CAUTION! * Remove generated files pixma_sane_options.[ch] after editing SANE option * descriptors below OR do a 'make clean' OR manually generate them as described * below. * However, make drops the circular dependency and the files won't be generated * again (see merge request sane-project/backends!491). BEGIN SANE_Option_Descriptor rem ------------------------------------------- type group title Scan mode type int resolution unit dpi constraint @word_list = ss->dpi_list default 75 title @SANE_TITLE_SCAN_RESOLUTION desc @SANE_DESC_SCAN_RESOLUTION cap soft_select soft_detect automatic info reload_params type string mode[30] constraint @string_list = ss->mode_list default @s = SANE_VALUE_SCAN_MODE_COLOR title @SANE_TITLE_SCAN_MODE desc @SANE_DESC_SCAN_MODE cap soft_select soft_detect automatic info reload_params type string source[30] constraint @string_list = ss->source_list title @SANE_TITLE_SCAN_SOURCE desc Selects the scan source (such as a document-feeder). Set source before mode and resolution. Resets mode and resolution to auto values. default Flatbed cap soft_select soft_detect type bool button-controlled title Button-controlled scan desc When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button. default SANE_FALSE cap soft_select soft_detect inactive rem ------------------------------------------- type group title Gamma type bool custom-gamma default SANE_FALSE title @SANE_TITLE_CUSTOM_GAMMA desc @SANE_DESC_CUSTOM_GAMMA cap soft_select soft_detect automatic inactive type int gamma-table[1024] constraint (0,0xffff,0) title @SANE_TITLE_GAMMA_VECTOR desc Gamma-correction table with 1024 entries. In color mode this option equally affects the red, green, and blue channels simultaneously (i.e., it is an intensity gamma table). cap soft_select soft_detect automatic inactive type fixed gamma default AUTO_GAMMA constraint (0.3,5,0) title Gamma function exponent desc Changes intensity of midtones cap soft_select soft_detect automatic inactive rem ------------------------------------------- type group title Geometry type fixed tl-x unit mm default 0 constraint @range = &ss->xrange title @SANE_TITLE_SCAN_TL_X desc @SANE_DESC_SCAN_TL_X cap soft_select soft_detect automatic info reload_params type fixed tl-y unit mm default 0 constraint @range = &ss->yrange title @SANE_TITLE_SCAN_TL_Y desc @SANE_DESC_SCAN_TL_Y cap soft_select soft_detect automatic info reload_params type fixed br-x unit mm default _MAX constraint @range = &ss->xrange title @SANE_TITLE_SCAN_BR_X desc @SANE_DESC_SCAN_BR_X cap soft_select soft_detect automatic info reload_params type fixed br-y unit mm default _MAX constraint @range = &ss->yrange title @SANE_TITLE_SCAN_BR_Y desc @SANE_DESC_SCAN_BR_Y cap soft_select soft_detect automatic info reload_params rem ------------------------------------------- type group title Buttons type button button-update title Update button state cap soft_select soft_detect advanced type int button-1 default 0 title Button 1 cap soft_detect advanced type int button-2 default 0 title Button 2 cap soft_detect advanced type int original default 0 title Type of original to scan cap soft_detect advanced type int target default 0 title Target operation type cap soft_detect advanced type int scan-resolution default 0 title Scan resolution cap soft_detect advanced type int document-type default 0 title Document type cap soft_detect advanced type int adf-status default 0 title ADF status cap soft_detect advanced type int adf-orientation default 0 title ADF orientation cap soft_detect advanced rem ------------------------------------------- type group title Extras type int threshold unit PERCENT default 50 constraint (0,100,1) title @SANE_TITLE_THRESHOLD desc @SANE_DESC_THRESHOLD cap soft_select soft_detect automatic inactive type int threshold-curve constraint (0,127,1) title Threshold curve desc Dynamic threshold curve, from light to dark, normally 50-65 cap soft_select soft_detect automatic inactive type int adf-wait default 0 constraint (0,3600,1) title ADF Waiting Time desc When set, the scanner waits upto the specified time in seconds for a new document inserted into the automatic document feeder. cap soft_select soft_detect automatic inactive rem ------------------------------------------- END SANE_Option_Descriptor */ /* pixma_sane_options.c generated by * scripts/pixma_gen_options.py < pixma.c > pixma_sane_options.c * * pixma_sane_options.h generated by * scripts/pixma_gen_options.py h < pixma.c > pixma_sane_options.h */ #include "pixma_sane_options.c"