/* backend.c -- implements the SANE epkowa backend * Copyright (C) 2008--2009 SEIKO EPSON CORPORATION * * License: GPLv2+|iscan * Authors: AVASYS CORPORATION * * This file is part of the SANE backend distributed with Image Scan! * * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License * along with this package. If not, see . * * * Linking Image Scan!'s SANE backend statically or dynamically with * other modules is making a combined work based on this SANE backend. * Thus, the terms and conditions of the GNU General Public License * cover the whole combination. * * As a special exception, the copyright holders of Image Scan!'s SANE * backend give you permission to link Image Scan!'s SANE backend with * SANE frontends that communicate with Image Scan!'s SANE backend * solely through the SANE Application Programming Interface, * regardless of the license terms of these SANE frontends, and to * copy and distribute the resulting combined work under terms of your * choice, provided that every copy of the combined work is * accompanied by a complete copy of the source code of Image Scan!'s * SANE backend (the version of Image Scan!'s SANE backend used to * produce the combined work), being distributed under the terms of * the GNU General Public License plus this exception. An independent * module is a module which is not derived from or based on Image * Scan!'s SANE backend. * * As a special exception, the copyright holders of Image Scan!'s SANE * backend give you permission to link Image Scan!'s SANE backend with * independent modules that communicate with Image Scan!'s SANE * backend solely through the "Interpreter" interface, regardless of * the license terms of these independent modules, and to copy and * distribute the resulting combined work under terms of your choice, * provided that every copy of the combined work is accompanied by a * complete copy of the source code of Image Scan!'s SANE backend (the * version of Image Scan!'s SANE backend used to produce the combined * work), being distributed under the terms of the GNU General Public * License plus this exception. An independent module is a module * which is not derived from or based on Image Scan!'s SANE backend. * * Note that people who make modified versions of Image Scan!'s SANE * backend are not obligated to grant special exceptions for their * modified versions; it is their choice whether to do so. The GNU * General Public License gives permission to release a modified * version without this exception; this exception also makes it * possible to release a modified version which carries forward this * exception. */ #ifdef HAVE_CONFIG_H #include #endif #define BACKEND_CREATE_FORWARDERS #include "backend.h" #define BACKEND_BUILD 213 #define BACKEND_SOURCE PACKAGE_STRING #include #include #include #include "cfg-obj.h" #include "dip-obj.h" #include "net-obj.h" #include "model-info.h" #include "utils.h" /* Deprecated includes */ #include "include/sane/sanei_usb.h" #include "epkowa.h" /* Deprecated API calls */ extern SANE_Status epkowa_open (const char *, SANE_Handle *, const void *); typedef struct { void *cfg; void *net; void *dip; list *sane_dev; void **dev_list; void *model_info_cache; } backend_type; static backend_type *be = NULL; static void be_sane_dev_dtor (void *); /*! \defgroup SANE_API SANE API Entry Points * * The SANE API entry points make up the \e full API available to the * SANE frontend application programmer. Users of this API should be * careful \e never to assume \e anything about a backend's behaviour * beyond what is required by the SANE standard. The standard can be * retrieved via http://www.sane-project.org/docs.html. * * Whatever documentation may be provided here serves to document the * implementation, if anything. In case of discrepancy with the SANE * specification, the SANE specification is correct. * * @{ */ /*! \brief Prepares the backend for use by a SANE frontend. * * \remarks * This function \e must be called before any other SANE API entry is * called. It is the only SANE function that may be called after the * sane_exit() function has been called. * * \todo * Deal properly with the \c SANE_Auth_Callback. * It is completely ignored in the current implementation. */ SANE_Status sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize) { SANE_Status status = SANE_STATUS_GOOD; if (be) { log_call ("(%p, %p)", version_code, authorize); err_minor ("backend already initialised"); return status; } msg_init (); log_call ("(%p, %p)", version_code, authorize); log_info ("%s", BACKEND_SOURCE); log_info ("version %d.%d.%d", SANE_MAJOR, SANE_MINOR, BACKEND_BUILD); if (version_code) { *version_code = SANE_VERSION_CODE (SANE_MAJOR, SANE_MINOR, BACKEND_BUILD); } if (authorize) { err_minor ("authorisation not supported"); } be = t_calloc (1, backend_type); if (!be) { return SANE_STATUS_NO_MEM; } /* Needs to be done _before_ cfg_init() because that refers to the * model_info_cache somewhere deep down in its bowels. */ be->model_info_cache = model_info_cache_init (MODELDATADIR, &status); if (!be->model_info_cache) { sane_exit (); return status; } be->cfg = cfg_init (PKGDATADIR, &status); if (!be->cfg) { sane_exit (); return status; } if (cfg_has (be->cfg, CFG_KEY_NET)) { be->net = net_init (PKGLIBDIR, &status); if (!be->net) { if (SANE_STATUS_GOOD != status) err_fatal ("%s", sane_strstatus (status)); err_major ("disabling network device support"); cfg_set (be->cfg, CFG_KEY_NET, false); status = SANE_STATUS_GOOD; } } if (cfg_has (be->cfg, CFG_KEY_PIO)) { /* place holder */ } if (cfg_has (be->cfg, CFG_KEY_SCSI)) { /* place holder */ } if (cfg_has (be->cfg, CFG_KEY_USB)) { sanei_usb_init (); } if (cfg_has (be->cfg, CFG_KEY_INTERPRETER)) { if (0 != lt_dlinit ()) { err_fatal ("%s", lt_dlerror ()); err_major ("disabling interpreter support"); cfg_set (be->cfg, CFG_KEY_INTERPRETER, false); } } be->dip = dip_init (PKGLIBDIR, &status); if (!be->dip) { sane_exit (); return status; } return status; } /*! \brief Releases all resources held by the backend. * * \remarks * Applications \e must call this function to terminate use of the * backend. After it has been called, sane_init() has to be called * before other SANE API can be used. The function needs to close * any open handles. * * \remarks * The implementation must be able to deal properly with a partially * initialised backend so that sane_init() can use this function for * its error recovery. */ void sane_exit (void) { log_call ("()"); if (!be) { msg_init (); err_minor ("backend is not initialized"); return; } be->dip = dip_exit (be->dip); if (cfg_has (be->cfg, CFG_KEY_INTERPRETER)) { lt_dlexit (); } if (cfg_has (be->cfg, CFG_KEY_USB)) { /* sanei_usb_exit, if only there was one! */ } if (cfg_has (be->cfg, CFG_KEY_SCSI)) { /* place holder */ } if (cfg_has (be->cfg, CFG_KEY_PIO)) { /* place holder */ } if (be->net) { be->net = net_exit (be->net); } be->cfg = cfg_exit (be->cfg); delete (be->dev_list); list_destroy (be->sane_dev, be_sane_dev_dtor); be->model_info_cache = model_info_cache_exit (be->model_info_cache); delete (be); } /*! \brief Creates a list of devices available through the backend. * * \remarks * The returned \a device_list \e must remain unchanged and valid * until this function is called again or sane_exit() is called. * * \remarks * Applications are \e not required to call this function before * they call sane_open(). */ SANE_Status sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only) { SANE_Status status = SANE_STATUS_GOOD; list *sane_dev = NULL; log_call ("(%p, %d)", device_list, local_only); if (!be) { msg_init (); err_fatal ("backend is not initialized"); return SANE_STATUS_ACCESS_DENIED; } if (!device_list) { err_fatal ("%s", strerror (EINVAL)); return SANE_STATUS_INVAL; } sane_dev = list_create (); if (sane_dev) { if (!local_only && cfg_has (be->cfg, CFG_KEY_NET)) { cfg_find (be->cfg, CFG_KEY_NET, sane_dev); } if (cfg_has (be->cfg, CFG_KEY_PIO)) { cfg_find (be->cfg, CFG_KEY_PIO, sane_dev); } if (cfg_has (be->cfg, CFG_KEY_SCSI)) { cfg_find (be->cfg, CFG_KEY_SCSI, sane_dev); } if (cfg_has (be->cfg, CFG_KEY_USB)) { cfg_find (be->cfg, CFG_KEY_USB, sane_dev); } if (cfg_has (be->cfg, CFG_KEY_INTERPRETER)) { cfg_find (be->cfg, CFG_KEY_INTERPRETER, sane_dev); } if (be->sane_dev) { delete (be->dev_list); list_destroy (be->sane_dev, be_sane_dev_dtor); } be->sane_dev = sane_dev; } be->dev_list = list_normalize (be->sane_dev); *device_list = (const SANE_Device **) be->dev_list; if (!*device_list) { status = SANE_STATUS_NO_MEM; } return status; } /*! \brief Establishes a connection to a named device. * * \remarks * Applications are allowed to call this function directly, without a * call to sane_get_devices() first. An empty string may be used for * the \a device_name to request the first available device. * * \todo Register the handle with \c be before \c SANE_STATUS_GOOD is * returned so we can check in the other API entries whether we * got a known handle passed in as well as call sane_close() on * all open handles in sane_exit(). */ SANE_Status sane_open (SANE_String_Const device_name, SANE_Handle *handle) { const SANE_Device *sane_dev = NULL; log_call ("(%s, %p)", device_name, handle); if (!be) { msg_init (); err_fatal ("backend is not initialized"); return SANE_STATUS_ACCESS_DENIED; } if (!handle) { err_fatal ("%s", strerror (EINVAL)); return SANE_STATUS_INVAL; } if (!device_name) { /* The SANE API specification explicitly talks about a zero * length string. There is no mention about what should be * done in the case where there is NO string at all. * We degrade gracefully. */ err_minor ("assuming frontend meant to pass an empty string"); } if (!be->sane_dev) { /* FIXME: does more than necessary */ const SANE_Device **dev = NULL; sane_get_devices (&dev, false); } if (0 == list_size (be->sane_dev)) { err_major ("no supported devices available"); return SANE_STATUS_ACCESS_DENIED; } if (!device_name || 0 == strlen (device_name)) { sane_dev = be->sane_dev->head->data; } else { list_reset (be->sane_dev); while ((sane_dev = list_next (be->sane_dev)) && 0 != strcmp_c (sane_dev->name, device_name)) { /* nothing to do, condition does all the processing */ } } if (!sane_dev) { err_major ("no such device"); return SANE_STATUS_INVAL; } return epkowa_open (sane_dev->name, handle, be->dip); } //! Obtains the current scan parameters for a device /*! \remarks * The parameters are only guaranteed to be accurate between a call * to sane_start() and the completion of that request. Outside of * that scope the parameters are a best effort only and the backend * is at liberty to change them. */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters *parameters) { SANE_Status status = SANE_STATUS_GOOD; Epson_Scanner *s; log_call ("(%p, %p)", handle, parameters); if (!handle || !parameters) { err_fatal ("%s", strerror (EINVAL)); return SANE_STATUS_INVAL; } s = (Epson_Scanner *) handle; if (s->src->transfer_started && !s->src->transfer_stopped) { static const char *const color_space[] = { "GRAY", "RGB", "RED", "GREEN", "BLUE" }; const SANE_Parameters *p = &s->src->ctx; log_info ("Scan area : %.2f x %.2f [mm^2]", SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w)); log_info ("Offset : (%.2f, %.2f) [mm]", SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_TL_Y].w)); log_info ("Color space : %s-%d", color_space[p->format], p->depth); log_info ("Image size : %d x %d [pixels^2] (%.2f x %.2f [mm^2])", p->pixels_per_line, p->lines, p->pixels_per_line * MM_PER_INCH / s->val[OPT_X_RESOLUTION].w, p->lines * MM_PER_INCH / s->val[OPT_Y_RESOLUTION].w); log_info ("X Resolution: %d [dpi]", s->val[OPT_X_RESOLUTION].w); log_info ("Y Resolution: %d [dpi]", s->val[OPT_Y_RESOLUTION].w); memcpy (parameters, p, sizeof (*p)); } else { status = estimate_parameters (s, parameters); } return status; } /*! \brief Acquires up to \a max_length bytes of new image data. * * \remarks * The \a length is guaranteed to be zero in case of an unsuccessful * request. * * \remarks * The implementation allows for \c NULL as a \a buffer value. This * caters to a frontends that do not prepare buffer space when they * expect a \c SANE_STATUS_EOF return value. */ SANE_Status sane_read (SANE_Handle handle, SANE_Byte *buffer, SANE_Int max_length, SANE_Int *length) { SANE_Status status = SANE_STATUS_GOOD; Epson_Scanner *s; log_call ("(%p, %p, %i, %p)", handle, buffer, max_length, length); if (length) *length = 0; if (!handle) { err_fatal ("%s", strerror (EINVAL)); return SANE_STATUS_INVAL; } s = (Epson_Scanner *) handle; require (s->src == &s->raw || s->src == &s->img); if (s->src == &s->raw) { status = fetch_image_data (s, buffer, max_length, length); } else if (s->src == &s->img) { /**/ if (!s->img.ptr) { err_major ("%s", strerror (ENOMEM)); status = SANE_STATUS_NO_MEM; } else if (s->img.ptr == s->img.end) { status = SANE_STATUS_EOF; } else if (s->img.cancel_requested) { s->img.transfer_stopped = true; status = SANE_STATUS_CANCELLED; } else if (buffer && 0 < max_length) { SANE_Int len = s->img.end - s->img.ptr; if (len > max_length) len = max_length; memcpy (buffer, s->img.ptr, len); s->img.ptr += len; if (length) *length = len; } else { status = SANE_STATUS_NO_MEM; } } if (SANE_STATUS_EOF == status) { s->src->transfer_stopped = true; } return status; } /*! @} */ /*! Releases the resources held by a SANE_Device. This function is primarily useful to maintain the \c sane_dev member of a backend_type object. */ static void be_sane_dev_dtor (void *p) { SANE_Device *sd = (SANE_Device *) p; if (!sd) return; const_delete (sd->name , SANE_String); const_delete (sd->vendor, SANE_String); const_delete (sd->model , SANE_String); const_delete (sd->type , SANE_String); delete (sd); }