diff options
Diffstat (limited to 'backend/epkowa_ip.c')
-rw-r--r-- | backend/epkowa_ip.c | 596 |
1 files changed, 596 insertions, 0 deletions
diff --git a/backend/epkowa_ip.c b/backend/epkowa_ip.c new file mode 100644 index 0000000..763a34a --- /dev/null +++ b/backend/epkowa_ip.c @@ -0,0 +1,596 @@ +/* epkowa_ip.c -- for scanners that don't speak EPSON ESC/I natively + Copyright (C) 2005--2008 Olaf Meeuwissen + Copyright (C) 2009 SEIKO EPSON CORPORATION + + This file is part of the EPKOWA SANE backend. + It defines a wrapper around the backend's "Interpreter" interface. + + The EPKOWA 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 FITNESS + FOR A PARTICULAR PURPOSE or MERCHANTABILITY. + See the GNU General Public License for more details. + + You should have received a verbatim copy of the GNU General Public + License along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place, Suite 330 + Boston, MA 02111-1307 USA + + Linking the EPKOWA SANE backend statically or dynamically with + other modules is making a combined work based on the EPKOWA 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 the EPKOWA SANE + backend give you permission to link the EPKOWA SANE backend with + independent modules that communicate with the EPKOWA 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 the EPKOWA SANE backend (the + version of the EPKOWA 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 the EPKOWA SANE backend. + + Note that people who make modified versions of the EPKOWA SANE + backend are not obligated to grant this special exception 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 <config.h> +#endif + +#include <signal.h> +#include <string.h> +#include <unistd.h> + +#include "epkowa_ip.h" +#include "cfg-obj.h" + +#include "sane/sanei_usb.h" + +#define TICK_INTERVAL 60 /* seconds */ +#define TICK_TIME_OUT 15 /* intervals */ + +// FIXME: this ought to be part of the interpreter_type struct so that +// every instance can have its own. +device_type *g_epson = NULL; + + +/* default member function implementors */ + +static SANE_Status _dtor (device_type *); +static int _open (device_type *); +static int _close (device_type *); +static ssize_t _recv (device_type *, void *, size_t, SANE_Status *); +static ssize_t _send (device_type *, const void *, size_t, SANE_Status *); + +static SANE_Status _free (device_type *); +static SANE_Status _ftor0 (device_type *, SANE_Parameters *, + SANE_Byte *, SANE_Byte *); +static SANE_Status _ftor1 (device_type *, SANE_Parameters *, + int, int, int, int); + +/* private functions */ + +static void *_load (const char *, device_type *); + + +/* callback functions */ + +static ssize_t usb_read (void *buffer, size_t size); +static ssize_t usb_write (void *buffer, size_t size); +static ssize_t usb_ctrl (size_t request_type, size_t request, + size_t value, size_t index, + size_t size, void *buffer); + + +/*! Creates an interpreter object for a \a device if both necessary + and available. The object is accessible through the \a device's + \c interpreter field once successfully created. + */ +SANE_Status +create_interpreter (device_type *device, unsigned int usb_product_id) +{ + void *cfg = cfg_init (NULL, NULL); + list *seen = cfg_seen (cfg, CFG_KEY_INTERPRETER); + const cfg_interpreter_info *next = NULL; + + if (!device) /* sanity check */ + { + return SANE_STATUS_INVAL; + } + + if (device->interpreter) /* we've been here before */ + { + if (device != device->interpreter->_device) + { /* internal inconsistency! */ + return SANE_STATUS_INVAL; + } + return SANE_STATUS_GOOD; + } + + if (seen) + { + list_entry *cur = seen->cur; + list_reset (seen); + while ((next = list_next (seen)) + && usb_product_id != next->product) + { + /* condition does all the processing */ + } + seen->cur = cur; + } + if (!next) return SANE_STATUS_GOOD; + + /* If we are still here, we need to set up an interpreter and load + the corresponding module. */ + + device->interpreter = t_malloc (1, struct interpreter_type); + + if (!device->interpreter) + { + return SANE_STATUS_NO_MEM; + } + + device->interpreter->_device = device; + device->interpreter->_module = _load (next->library, device); + + if (!device->interpreter->_module) + { + delete (device->interpreter); + return SANE_STATUS_INVAL; + } + + device->interpreter->_tick_count = -1; + device->interpreter->_table = NULL; + device->interpreter->_buffer = NULL; + + device->interpreter->dtor = _dtor; + device->interpreter->open = _open; + device->interpreter->close = _close; + device->interpreter->recv = _recv; + device->interpreter->send = _send; + + device->interpreter->free = _free; + device->interpreter->ftor0 = _ftor0; + device->interpreter->ftor1 = _ftor1; + + return SANE_STATUS_GOOD; +} + + +/*! Destroys a \a device's interpreter object. + */ +SANE_Status +_dtor (device_type *device) +{ + if (!device || !device->interpreter) + { + return SANE_STATUS_INVAL; + } + + device->interpreter->close (device); + device->interpreter->free (device); + +#ifdef USE_ALARM + alarm (0); +#endif + + lt_dlclose (device->interpreter->_module); + device->interpreter->_module = 0; + + delete (device->interpreter); + + return SANE_STATUS_GOOD; +} + +/*! Frees several temporary buffers. + */ +SANE_Status +_free (device_type *device) +{ + if (!device || !device->interpreter) + { + return SANE_STATUS_INVAL; + } + + delete (device->interpreter->_table); + delete (device->interpreter->_buffer); + + return SANE_STATUS_GOOD; +} + +/*! Performs whatever actions are needed when opening an interpreter + "controlled" device. One of these actions is disarming an alarm + used to control whether the device can go into power saving mode + or not. */ +int +_open (device_type *device) +{ + struct interpreter_type *di; + + if (!device || !device->interpreter) + { + return -1; + } + di = device->interpreter; + +#ifdef USE_ALARM + alarm (0); + log_info ("alarm (%d)", 0); +#endif + di->_tick_count = -1; + + g_epson = device; + + if (device->fd < 0 + || !(di->_init + ? di->_init (device->fd, usb_read, usb_write) + : di->_init_with_ctrl (device->fd, usb_read, + usb_write, usb_ctrl))) + { + err_fatal ("failed to initialize interpreter"); + g_epson = NULL; + return -1; + } + + return device->fd; +} + +int +_close (device_type *device) +{ + if (!device || !device->interpreter) + { + return -1; + } + + device->interpreter->_fini (); + + device->interpreter->_tick_count = 0; +#ifdef USE_ALARM + alarm (TICK_INTERVAL); + log_info ("alarm (%d)", TICK_INTERVAL); +#endif + + g_epson = NULL; + + return device->fd; +} + +ssize_t +_send (device_type *device, const void *buffer, size_t size, + SANE_Status *status) +{ + if (!status) + { + return -1; + } + + if (!device || !device->interpreter) + { + *status = SANE_STATUS_INVAL; + return -1; + } + + /* ASSUMPTION: Interpreter does NOT change buffer's content. + */ + if (device->interpreter->_write ((void *)buffer, size)) + { + *status = SANE_STATUS_GOOD; + } + else + { + *status = SANE_STATUS_INVAL; + } + + return size; +} + +ssize_t +_recv (device_type *device, void *buffer, size_t size, + SANE_Status *status) +{ + if (!status) + { + return -1; + } + + if (!device || !device->interpreter) + { + *status = SANE_STATUS_INVAL; + return -1; + } + + if (device->interpreter->_read (buffer, size)) + { + *status = SANE_STATUS_GOOD; + } + else + { + *status = SANE_STATUS_INVAL; + } + + return size; +} + +/*! Callback for use by the interpreter. */ +ssize_t +usb_read (void *buffer, size_t size) +{ + size_t n = size; + + if (!g_epson || g_epson->fd < 0) + { + return 0; + } + + if (SANE_STATUS_GOOD == sanei_usb_read_bulk (g_epson->fd, buffer, &n)) + { + if (size != n) + err_minor ("Did not read number of bytes requested"); + return n; + } + else + { + return 0; + } +} + +/*! Callback for use by the interpreter. */ +ssize_t +usb_write (void *buffer, size_t size) +{ + size_t n = size; + + if (!g_epson || g_epson->fd < 0) + { + return 0; + } + + if (SANE_STATUS_GOOD == sanei_usb_write_bulk (g_epson->fd, buffer, &n)) + { + if (size != n) + err_minor ("Did not read number of bytes requested"); + return n; + } + else + return 0; +} + +/*! Callback for use by the interpreter. */ +ssize_t +usb_ctrl (size_t request_type, size_t request, size_t value, + size_t index, size_t size, void *buffer) +{ + size_t n = size; + + if (!g_epson || g_epson->fd < 0) + { + return 0; + } + + if (SANE_STATUS_GOOD == sanei_usb_control_msg (g_epson->fd, request_type, + request, value, index, + &n, buffer)) + { + if (size != n) + err_minor ("Did not read number of bytes requested"); + return n; + } + else + return 0; +} + +#ifdef USE_ALARM +void +timer_event (int sig) +{ + sig = sig; + + log_call (); + + if (!g_epson || !g_epson->interpreter) + return; + + if ((g_epson && 0 < g_epson->fd) || g_epson->interpreter->_tick_count == -1) + { + return; + } + + g_epson->interpreter->_tick_count++; + + if (TICK_TIME_OUT - 1 <= g_epson->interpreter->_tick_count) + { + g_epson->interpreter->_power (); + g_epson->interpreter->_tick_count = -1; + return; + } + + alarm (TICK_INTERVAL); + log_info ("alarm (%d)", TICK_INTERVAL); +} +#endif + +/*! Loads the interpreter module. */ +/*! + */ +void * +_load (const char *name, device_type *device) +{ + void *handle = NULL; + struct interpreter_type *di = device->interpreter; + + /* FIXME: should set the search path to a list of directories based + on the regular load path with the PACKAGE attached to each of the + individual directories. Should _not_ look in the regular load + path's directories. */ + { + const char *path = lt_dlgetsearchpath (); + if (!(path && strstr (path, PKGLIBDIR))) + { + lt_dladdsearchdir (PKGLIBDIR); + } + handle = lt_dlopenext (name); + } + if (!handle) + { + err_fatal ("%s", lt_dlerror()); + return NULL; + } + + di->_init_with_ctrl = lt_dlsym (handle, "int_init_with_ctrl"); + if (di->_init_with_ctrl) + { + di->_init = NULL; + } + else + { + di->_init = lt_dlsym (handle, "int_init"); + } + di->_fini = lt_dlsym (handle, "int_fini"); + di->_read = lt_dlsym (handle, "int_read"); + di->_write = lt_dlsym (handle, "int_write"); + di->_power = lt_dlsym (handle, "int_power_saving_mode"); + di->_s_0 = lt_dlsym (handle, "function_s_0"); + di->_s_1 = lt_dlsym (handle, "function_s_1"); + + if (! + ( (di->_init || di->_init_with_ctrl) + && di->_fini + && di->_read + && di->_write + && di->_s_0 + && di->_s_1) + ) + { + err_fatal ("failed to find all required interpreter API"); + di->_init_with_ctrl = NULL; + di->_init = NULL; + di->_fini = NULL; + di->_read = NULL; + di->_write = NULL; + di->_power = NULL; + di->_s_0 = NULL; + di->_s_1 = NULL; + lt_dlclose (handle); + return NULL; + } + + /* FIXME: if we are still here, we should set up the alarm stuff + here because _load is really a sort of ctor. Note that open + should disarm the alarm and close should re-arm it. Our dtor + should take care of disarming the alarm permanently. */ + /* FIXME: add error handling */ + { /* set up alarm for power saving */ +#ifdef USE_ALARM + struct sigaction act; + + /* FIXME: set act.sa_sigaction instead because that can hold a + void (*) (int, siginfo_t *, void *) so we can pass arguments + such as device->interpreter->_device(!) to it, perhaps. */ + act.sa_handler = timer_event; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + + sigaction (SIGALRM, &act, 0); + + alarm (TICK_INTERVAL); + log_info ("alarm (%d)", TICK_INTERVAL); +#endif + di->_tick_count = 0; + } + return handle; +} + +/* + This functor is called by sane_read only. + */ +SANE_Status +_ftor0 (device_type *device, SANE_Parameters *params, + SANE_Byte *ptr, SANE_Byte *end) +{ + if (!device || !device->interpreter || !params) + { + return SANE_STATUS_INVAL; + } + + if (params->depth != 1 + && device->interpreter->_table && device->interpreter->_buffer) + { + int i, row; + + row = (end - ptr) / params->bytes_per_line; + + for (i = 0; i < row; ++i) + { + memcpy (device->interpreter->_buffer, ptr + i * params->bytes_per_line, + params->bytes_per_line); + device->interpreter->_s_1 (device->interpreter->_buffer, + ptr + i * params->bytes_per_line, + params->pixels_per_line, + params->format == SANE_FRAME_RGB, + device->interpreter->_table); + } + } + return SANE_STATUS_GOOD; +} + +/* + This functor is called by sane_start only. + */ +SANE_Status +_ftor1 (device_type *device, SANE_Parameters *params, + int depth, int left, int x_dpi, int optical_res) +{ + if (!device || !device->interpreter || !params) + { + return SANE_STATUS_INVAL; + } + + device->interpreter->free (device); + + if (depth != 1) + { + device->interpreter->_table = + t_malloc (params->pixels_per_line, double); + + if (!device->interpreter->_table) + { + return SANE_STATUS_NO_MEM; + } + + if (device->interpreter->_s_0 (left, params->pixels_per_line, + x_dpi, optical_res, + device->interpreter->_table)) + { + device->interpreter->_buffer + = t_malloc (params->bytes_per_line, SANE_Byte); + + if (!device->interpreter->_buffer) + { + delete (device->interpreter->_table); + return SANE_STATUS_NO_MEM; + } + } + else + { + delete (device->interpreter->_table); + } + } + return SANE_STATUS_GOOD; +} |