diff options
Diffstat (limited to 'backend/pixma/pixma_io_sanei.c')
-rw-r--r-- | backend/pixma/pixma_io_sanei.c | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/backend/pixma/pixma_io_sanei.c b/backend/pixma/pixma_io_sanei.c new file mode 100644 index 0000000..c30b404 --- /dev/null +++ b/backend/pixma/pixma_io_sanei.c @@ -0,0 +1,556 @@ +/* SANE - Scanner Access Now Easy. + * For limitations, see function sanei_usb_get_vendor_product(). + + Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> + + 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 <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> /* INT_MAX */ + +#include "pixma_rename.h" +#include "pixma_common.h" +#include "pixma_io.h" +#include "pixma_bjnp.h" + +#include "../include/sane/sanei_usb.h" +#include "../include/sane/sane.h" + + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +/* MAC OS X does not support timeouts in darwin/libusb interrupt reads + * This is a very basic turnaround for MAC OS X + * Button scan will not work with this wrapper */ +#ifdef __APPLE__ +# define sanei_usb_read_int sanei_usb_read_bulk +#endif + + +struct pixma_io_t +{ + pixma_io_t *next; + int interface; + SANE_Int dev; +}; + +typedef struct scanner_info_t +{ + struct scanner_info_t *next; + char *devname; + int interface; + const pixma_config_t *cfg; + char serial[PIXMA_MAX_ID_LEN + 1]; /* "xxxxyyyy_zzzzzzz..." + x = vid, y = pid, z = serial */ +} scanner_info_t; + +#define INT_USB 0 +#define INT_BJNP 1 + +static scanner_info_t *first_scanner = NULL; +static pixma_io_t *first_io = NULL; +static unsigned nscanners; + + +static scanner_info_t * +get_scanner_info (unsigned devnr) +{ + scanner_info_t *si; + for (si = first_scanner; si && devnr != 0; --devnr, si = si->next) + { + } + return si; +} + +static SANE_Status +attach (SANE_String_Const devname) +{ + scanner_info_t *si; + + si = (scanner_info_t *) calloc (1, sizeof (*si)); + if (!si) + return SANE_STATUS_NO_MEM; + si->devname = strdup (devname); + if (!si->devname) + return SANE_STATUS_NO_MEM; + si -> interface = INT_USB; + si->next = first_scanner; + first_scanner = si; + nscanners++; + return SANE_STATUS_GOOD; +} + + +static SANE_Status +attach_bjnp (SANE_String_Const devname, + SANE_String_Const serial, + const struct pixma_config_t *cfg) +{ + scanner_info_t *si; + + si = (scanner_info_t *) calloc (1, sizeof (*si)); + if (!si) + return SANE_STATUS_NO_MEM; + si->devname = strdup (devname); + if (!si->devname) + return SANE_STATUS_NO_MEM; + + si->cfg = cfg; + sprintf(si->serial, "%s_%s", cfg->model, serial); + si -> interface = INT_BJNP; + si->next = first_scanner; + first_scanner = si; + nscanners++; + return SANE_STATUS_GOOD; +} + +static void +clear_scanner_list (void) +{ + scanner_info_t *si = first_scanner; + while (si) + { + scanner_info_t *temp = si; + free (si->devname); + si = si->next; + free (temp); + } + nscanners = 0; + first_scanner = NULL; +} + +static SANE_Status +get_descriptor (SANE_Int dn, SANE_Int type, SANE_Int descidx, + SANE_Int index, SANE_Int length, SANE_Byte * data) +{ + return sanei_usb_control_msg (dn, 0x80, USB_REQ_GET_DESCRIPTOR, + ((type & 0xff) << 8) | (descidx & 0xff), + index, length, data); +} + +static SANE_Status +get_string_descriptor (SANE_Int dn, SANE_Int index, SANE_Int lang, + SANE_Int length, SANE_Byte * data) +{ + return get_descriptor (dn, USB_DT_STRING, index, lang, length, data); +} + +static void +u16tohex (uint16_t x, char *str) +{ + static const char hdigit[16] = + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F' + }; + str[0] = hdigit[(x >> 12) & 0xf]; + str[1] = hdigit[(x >> 8) & 0xf]; + str[2] = hdigit[(x >> 4) & 0xf]; + str[3] = hdigit[x & 0xf]; + str[4] = '\0'; +} + +static void +read_serial_number (scanner_info_t * si) +{ + uint8_t unicode[2 * (PIXMA_MAX_ID_LEN - 9) + 2]; + uint8_t ddesc[18]; + int iSerialNumber; + SANE_Int usb; + char *serial = si->serial; + + u16tohex (si->cfg->vid, serial); + u16tohex (si->cfg->pid, serial + 4); + + if (SANE_STATUS_GOOD != sanei_usb_open (si->devname, &usb)) + return; + if (get_descriptor (usb, USB_DT_DEVICE, 0, 0, 18, ddesc) + != SANE_STATUS_GOOD) + goto done; + iSerialNumber = ddesc[16]; + if (iSerialNumber != 0) + { + int i, len; + SANE_Status status; + + /*int iSerialNumber = ddesc[16];*/ + /* Read the first language code. Assumed that there is at least one. */ + if (get_string_descriptor (usb, 0, 0, 4, unicode) != SANE_STATUS_GOOD) + goto done; + /* Read the serial number string. */ + status = get_string_descriptor (usb, iSerialNumber, + unicode[3] * 256 + unicode[2], + sizeof (unicode), unicode); + if (status != SANE_STATUS_GOOD) + goto done; + /* Assumed charset: Latin1 */ + len = unicode[0]; + if (len > (int) sizeof (unicode)) + { + len = sizeof (unicode); + PDBG (pixma_dbg (1, "WARNING:Truncated serial number\n")); + } + serial[8] = '_'; + for (i = 2; i < len; i += 2) + { + serial[9 + i / 2 - 1] = unicode[i]; + } + serial[9 + i / 2 - 1] = '\0'; + } + else + { + PDBG (pixma_dbg (1, "WARNING:No serial number\n")); + } +done: + sanei_usb_close (usb); +} + +static int +map_error (SANE_Status ss) +{ + switch (ss) + { + case SANE_STATUS_GOOD: + return 0; + case SANE_STATUS_UNSUPPORTED: + return PIXMA_ENODEV; + case SANE_STATUS_DEVICE_BUSY: + return PIXMA_EBUSY; + case SANE_STATUS_INVAL: + return PIXMA_EINVAL; + case SANE_STATUS_IO_ERROR: + return PIXMA_EIO; + case SANE_STATUS_NO_MEM: + return PIXMA_ENOMEM; + case SANE_STATUS_ACCESS_DENIED: + return PIXMA_EACCES; + case SANE_STATUS_CANCELLED: + return PIXMA_ECANCELED; + case SANE_STATUS_JAMMED: + return PIXMA_EPAPER_JAMMED; + case SANE_STATUS_COVER_OPEN: + return PIXMA_ECOVER_OPEN; + case SANE_STATUS_NO_DOCS: + return PIXMA_ENO_PAPER; + case SANE_STATUS_EOF: + return PIXMA_EOF; +#ifdef SANE_STATUS_HW_LOCKED + case SANE_STATUS_HW_LOCKED: /* unused by pixma */ +#endif +#ifdef SANE_STATUS_WARMING_UP + case SANE_STATUS_WARMING_UP: /* unused by pixma */ +#endif + break; + } + PDBG (pixma_dbg (1, "BUG:Unmapped SANE Status code %d\n", ss)); + return PIXMA_EIO; /* should not happen */ +} + + +int +pixma_io_init (void) +{ + sanei_usb_init (); + sanei_bjnp_init(); + nscanners = 0; + return 0; +} + +void +pixma_io_cleanup (void) +{ + while (first_io) + pixma_disconnect (first_io); + clear_scanner_list (); +} + +unsigned +pixma_collect_devices (const char **conf_devices, + const struct pixma_config_t *const pixma_devices[], SANE_Bool local_only) +{ + unsigned i, j; + struct scanner_info_t *si; + const struct pixma_config_t *cfg; + + clear_scanner_list (); + j = 0; + for (i = 0; pixma_devices[i]; i++) + { + for (cfg = pixma_devices[i]; cfg->name; cfg++) + { + sanei_usb_find_devices (cfg->vid, cfg->pid, attach); + si = first_scanner; + while (j < nscanners) + { + PDBG (pixma_dbg (3, "pixma_collect_devices() found %s at %s\n", + cfg->name, si->devname)); + si->cfg = cfg; + read_serial_number (si); + si = si->next; + j++; + } + } + } + if (! local_only) + sanei_bjnp_find_devices(conf_devices, attach_bjnp, pixma_devices); + + si = first_scanner; + while (j < nscanners) + { + PDBG (pixma_dbg (3, "pixma_collect_devices() found %s at %s\n", + si->cfg->name, si->devname)); + si = si->next; + j++; + + } + return nscanners; +} + +const pixma_config_t * +pixma_get_device_config (unsigned devnr) +{ + const scanner_info_t *si = get_scanner_info (devnr); + return (si) ? si->cfg : NULL; +} + +const char * +pixma_get_device_id (unsigned devnr) +{ + const scanner_info_t *si = get_scanner_info (devnr); + return (si) ? si->serial : NULL; +} + +int +pixma_connect (unsigned devnr, pixma_io_t ** handle) +{ + pixma_io_t *io; + SANE_Int dev; + const scanner_info_t *si; + int error; + + *handle = NULL; + si = get_scanner_info (devnr); + if (!si) + return PIXMA_EINVAL; + if (si-> interface == INT_BJNP) + error = map_error (sanei_bjnp_open (si->devname, &dev)); + else + error = map_error (sanei_usb_open (si->devname, &dev)); + + if (error < 0) + return error; + io = (pixma_io_t *) calloc (1, sizeof (*io)); + if (!io) + { + if (si -> interface == INT_BJNP) + sanei_bjnp_close (dev); + else + sanei_usb_close (dev); + return PIXMA_ENOMEM; + } + io->next = first_io; + first_io = io; + io->dev = dev; + io->interface = si->interface; + *handle = io; + return 0; +} + + +void +pixma_disconnect (pixma_io_t * io) +{ + pixma_io_t **p; + + if (!io) + return; + for (p = &first_io; *p && *p != io; p = &((*p)->next)) + { + } + PASSERT (*p); + if (!(*p)) + return; + if (io-> interface == INT_BJNP) + sanei_bjnp_close (io->dev); + else + sanei_usb_close (io->dev); + *p = io->next; + free (io); +} + +int pixma_activate (pixma_io_t * io) +{ + int error; + if (io->interface == INT_BJNP) + { + error = map_error(sanei_bjnp_activate (io->dev)); + } + else + /* noop for USB interface */ + error = 0; + return error; +} + +int pixma_deactivate (pixma_io_t * io) +{ + int error; + if (io->interface == INT_BJNP) + { + error = map_error(sanei_bjnp_deactivate (io->dev)); + } + else + /* noop for USB interface */ + error = 0; + return error; + +} + +int +pixma_reset_device (pixma_io_t * io) +{ + UNUSED (io); + return PIXMA_ENOTSUP; +} + +int +pixma_write (pixma_io_t * io, const void *cmd, unsigned len) +{ + size_t count = len; + int error; + + if (io->interface == INT_BJNP) + { + sanei_bjnp_set_timeout (io->dev, PIXMA_BULKOUT_TIMEOUT); + error = map_error (sanei_bjnp_write_bulk (io->dev, cmd, &count)); + } + else + { +#ifdef HAVE_SANEI_USB_SET_TIMEOUT + sanei_usb_set_timeout (PIXMA_BULKOUT_TIMEOUT); +#endif + error = map_error (sanei_usb_write_bulk (io->dev, cmd, &count)); + } + if (error == PIXMA_EIO) + error = PIXMA_ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */ + if (count != len) + { + PDBG (pixma_dbg (1, "WARNING:pixma_write(): count(%u) != len(%u)\n", + (unsigned) count, len)); + error = PIXMA_EIO; + } + if (error >= 0) + error = count; + PDBG (pixma_dump (10, "OUT ", cmd, error, len, 128)); + return error; +} + +int +pixma_read (pixma_io_t * io, void *buf, unsigned size) +{ + size_t count = size; + int error; + + if (io-> interface == INT_BJNP) + { + sanei_bjnp_set_timeout (io->dev, PIXMA_BULKIN_TIMEOUT); + error = map_error (sanei_bjnp_read_bulk (io->dev, buf, &count)); + } + else + { +#ifdef HAVE_SANEI_USB_SET_TIMEOUT + sanei_usb_set_timeout (PIXMA_BULKIN_TIMEOUT); +#endif + error = map_error (sanei_usb_read_bulk (io->dev, buf, &count)); + } + + if (error == PIXMA_EIO) + error = PIXMA_ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */ + if (error >= 0) + error = count; + PDBG (pixma_dump (10, "IN ", buf, error, -1, 128)); + return error; +} + +int +pixma_wait_interrupt (pixma_io_t * io, void *buf, unsigned size, int timeout) +{ + size_t count = size; + int error; + + /* FIXME: What is the meaning of "timeout" in sanei_usb? */ + if (timeout < 0) + timeout = INT_MAX; + else if (timeout < 100) + timeout = 100; + if (io-> interface == INT_BJNP) + { + sanei_bjnp_set_timeout (io->dev, timeout); + error = map_error (sanei_bjnp_read_int (io->dev, buf, &count)); + } + else + { +#ifdef HAVE_SANEI_USB_SET_TIMEOUT + sanei_usb_set_timeout (timeout); +#endif + error = map_error (sanei_usb_read_int (io->dev, buf, &count)); + } + if (error == PIXMA_EIO || + (io->interface == INT_BJNP && error == PIXMA_EOF)) /* EOF is a bjnp timeout error! */ + error = PIXMA_ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */ + if (error == 0) + error = count; + if (error != PIXMA_ETIMEDOUT) + PDBG (pixma_dump (10, "INTR", buf, error, -1, -1)); + return error; +} + +int +pixma_set_interrupt_mode (pixma_io_t * s, int background) +{ + UNUSED (s); + return (background) ? PIXMA_ENOTSUP : 0; +} |