kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			868 wiersze
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			868 wiersze
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
/* SANE - Scanner Access Now Easy.
 | 
						|
 | 
						|
   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 <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <math.h>		/* pow(C90) */
 | 
						|
 | 
						|
#include <sys/time.h>		/* gettimeofday(4.3BSD) */
 | 
						|
#include <unistd.h>		/* usleep */
 | 
						|
 | 
						|
#include "pixma_rename.h"
 | 
						|
#include "pixma_common.h"
 | 
						|
#include "pixma_io.h"
 | 
						|
 | 
						|
 | 
						|
#ifdef __GNUC__
 | 
						|
# define UNUSED(v) (void) v
 | 
						|
#else
 | 
						|
# define UNUSED(v)
 | 
						|
#endif
 | 
						|
 | 
						|
extern const pixma_config_t pixma_mp150_devices[];
 | 
						|
extern const pixma_config_t pixma_mp750_devices[];
 | 
						|
extern const pixma_config_t pixma_mp730_devices[];
 | 
						|
 | 
						|
static const pixma_config_t *const pixma_devices[] = {
 | 
						|
  pixma_mp150_devices,
 | 
						|
  pixma_mp750_devices,
 | 
						|
  pixma_mp730_devices,
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
static pixma_t *first_pixma = NULL;
 | 
						|
static time_t tstart_sec = 0;
 | 
						|
static uint32_t tstart_usec = 0;
 | 
						|
static int debug_level = 1;
 | 
						|
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
 | 
						|
static void
 | 
						|
u8tohex (uint8_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 >> 4) & 0xf];
 | 
						|
  str[1] = hdigit[x & 0xf];
 | 
						|
  str[2] = '\0';
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
u32tohex (uint32_t x, char *str)
 | 
						|
{
 | 
						|
  u8tohex (x >> 24, str);
 | 
						|
  u8tohex (x >> 16, str + 2);
 | 
						|
  u8tohex (x >> 8, str + 4);
 | 
						|
  u8tohex (x, str + 6);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_hexdump (int level, const void *d_, unsigned len)
 | 
						|
{
 | 
						|
  const uint8_t *d = (const uint8_t *) (d_);
 | 
						|
  unsigned ofs, c;
 | 
						|
  char line[100];		/* actually only 1+8+1+8*3+1+8*3+1 = 61 bytes needed */
 | 
						|
 | 
						|
  if (level > debug_level)
 | 
						|
    return;
 | 
						|
  ofs = 0;
 | 
						|
  while (ofs < len)
 | 
						|
    {
 | 
						|
      char *p;
 | 
						|
      line[0] = ' ';
 | 
						|
      u32tohex (ofs, line + 1);
 | 
						|
      line[9] = ':';
 | 
						|
      p = line + 10;
 | 
						|
      for (c = 0; c != 16 && (ofs + c) < len; c++)
 | 
						|
	{
 | 
						|
	  u8tohex (d[ofs + c], p);
 | 
						|
	  p[2] = ' ';
 | 
						|
	  p += 3;
 | 
						|
	  if (c == 7)
 | 
						|
	    {
 | 
						|
	      p[0] = ' ';
 | 
						|
	      p++;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      p[0] = '\0';
 | 
						|
      pixma_dbg (level, "%s\n", line);
 | 
						|
      ofs += c;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
time2str (char *buf, unsigned size)
 | 
						|
{
 | 
						|
  time_t sec;
 | 
						|
  uint32_t usec;
 | 
						|
 | 
						|
  pixma_get_time (&sec, &usec);
 | 
						|
  sec -= tstart_sec;
 | 
						|
  if (usec >= tstart_usec)
 | 
						|
    {
 | 
						|
      usec -= tstart_usec;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      usec = 1000000 + usec - tstart_usec;
 | 
						|
      sec--;
 | 
						|
    }
 | 
						|
  snprintf (buf, size, "%lu.%03u", (unsigned long) sec,
 | 
						|
	    (unsigned) (usec / 1000));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_dump (int level, const char *type, const void *data, int len,
 | 
						|
	    int size, int max)
 | 
						|
{
 | 
						|
  int actual_len, print_len;
 | 
						|
  char buf[20];
 | 
						|
 | 
						|
  if (level > debug_level)
 | 
						|
    return;
 | 
						|
  if (debug_level >= 20)
 | 
						|
    max = -1;			/* dump every bytes */
 | 
						|
 | 
						|
  time2str (buf, sizeof (buf));
 | 
						|
  pixma_dbg (level, "%s T=%s len=%d\n", type, buf, len);
 | 
						|
 | 
						|
  actual_len = (size >= 0) ? size : len;
 | 
						|
  print_len = (max >= 0 && max < actual_len) ? max : actual_len;
 | 
						|
  if (print_len >= 0)
 | 
						|
    {
 | 
						|
      pixma_hexdump (level, data, print_len);
 | 
						|
      if (print_len < actual_len)
 | 
						|
	pixma_dbg (level, " ...\n");
 | 
						|
    }
 | 
						|
  if (len < 0)
 | 
						|
    pixma_dbg (level, "  ERROR: %s\n", pixma_strerror (len));
 | 
						|
  pixma_dbg (level, "\n");
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
/* NOTE: non-reentrant */
 | 
						|
const char *
 | 
						|
pixma_strerror (int error)
 | 
						|
{
 | 
						|
  static char buf[50];
 | 
						|
 | 
						|
  /* TODO: more human friendly messages */
 | 
						|
  switch (error)
 | 
						|
    {
 | 
						|
    case PIXMA_EIO:
 | 
						|
      return "EIO";
 | 
						|
    case PIXMA_ENODEV:
 | 
						|
      return "ENODEV";
 | 
						|
    case PIXMA_EACCES:
 | 
						|
      return "EACCES";
 | 
						|
    case PIXMA_ENOMEM:
 | 
						|
      return "ENOMEM";
 | 
						|
    case PIXMA_EINVAL:
 | 
						|
      return "EINVAL";
 | 
						|
    case PIXMA_EBUSY:
 | 
						|
      return "EBUSY";
 | 
						|
    case PIXMA_ECANCELED:
 | 
						|
      return "ECANCELED";
 | 
						|
    case PIXMA_ENOTSUP:
 | 
						|
      return "ENOTSUP";
 | 
						|
    case PIXMA_ETIMEDOUT:
 | 
						|
      return "ETIMEDOUT";
 | 
						|
    case PIXMA_EPROTO:
 | 
						|
      return "EPROTO";
 | 
						|
    case PIXMA_EPAPER_JAMMED:
 | 
						|
      return "EPAPAR_JAMMED";
 | 
						|
    case PIXMA_ECOVER_OPEN:
 | 
						|
      return "ECOVER_OPEN";
 | 
						|
    case PIXMA_ENO_PAPER:
 | 
						|
      return "ENO_PAPER";
 | 
						|
    }
 | 
						|
  snprintf (buf, sizeof (buf), "EUNKNOWN:%d", error);
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_set_debug_level (int level)
 | 
						|
{
 | 
						|
  debug_level = level;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_set_be16 (uint16_t x, uint8_t * buf)
 | 
						|
{
 | 
						|
  buf[0] = x >> 8;
 | 
						|
  buf[1] = x;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_set_be32 (uint32_t x, uint8_t * buf)
 | 
						|
{
 | 
						|
  buf[0] = x >> 24;
 | 
						|
  buf[1] = x >> 16;
 | 
						|
  buf[2] = x >> 8;
 | 
						|
  buf[3] = x;
 | 
						|
}
 | 
						|
 | 
						|
uint16_t
 | 
						|
pixma_get_be16 (const uint8_t * buf)
 | 
						|
{
 | 
						|
  return ((uint16_t) buf[0] << 8) | buf[1];
 | 
						|
}
 | 
						|
 | 
						|
uint32_t
 | 
						|
pixma_get_be32 (const uint8_t * buf)
 | 
						|
{
 | 
						|
  return ((uint32_t) buf[0] << 24) + ((uint32_t) buf[1] << 16) +
 | 
						|
    ((uint32_t) buf[2] << 8) + buf[3];
 | 
						|
}
 | 
						|
 | 
						|
uint8_t
 | 
						|
pixma_sum_bytes (const void *data, unsigned len)
 | 
						|
{
 | 
						|
  const uint8_t *d = (const uint8_t *) data;
 | 
						|
  unsigned i, sum = 0;
 | 
						|
  for (i = 0; i != len; i++)
 | 
						|
    sum += d[i];
 | 
						|
  return sum;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_sleep (unsigned long usec)
 | 
						|
{
 | 
						|
  usleep (usec);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_get_time (time_t * sec, uint32_t * usec)
 | 
						|
{
 | 
						|
  struct timeval tv;
 | 
						|
  gettimeofday (&tv, NULL);
 | 
						|
  if (sec)
 | 
						|
    *sec = tv.tv_sec;
 | 
						|
  if (usec)
 | 
						|
    *usec = tv.tv_usec;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_map_status_errno (unsigned status)
 | 
						|
{
 | 
						|
  switch (status)
 | 
						|
    {
 | 
						|
    case PIXMA_STATUS_OK:
 | 
						|
      return 0;
 | 
						|
    case PIXMA_STATUS_FAILED:
 | 
						|
      return PIXMA_ECANCELED;
 | 
						|
    case PIXMA_STATUS_BUSY:
 | 
						|
      return PIXMA_EBUSY;
 | 
						|
    default:
 | 
						|
      return PIXMA_EPROTO;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_check_result (pixma_cmdbuf_t * cb)
 | 
						|
{
 | 
						|
  const uint8_t *r = cb->buf;
 | 
						|
  unsigned header_len = cb->res_header_len;
 | 
						|
  unsigned expected_reslen = cb->expected_reslen;
 | 
						|
  int error;
 | 
						|
  unsigned len;
 | 
						|
 | 
						|
  if (cb->reslen < 0)
 | 
						|
    return cb->reslen;
 | 
						|
 | 
						|
  len = (unsigned) cb->reslen;
 | 
						|
  if (len >= header_len)
 | 
						|
    {
 | 
						|
      error = pixma_map_status_errno (pixma_get_be16 (r));
 | 
						|
      if (expected_reslen != 0)
 | 
						|
	{
 | 
						|
	  if (len == expected_reslen)
 | 
						|
	    {
 | 
						|
	      if (pixma_sum_bytes (r + header_len, len - header_len) != 0)
 | 
						|
		error = PIXMA_EPROTO;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      /* This case will happen when a command cannot be completely
 | 
						|
	         executed, e.g. because you press the cancel button. The
 | 
						|
	         device will return only a header with PIXMA_STATUS_FAILED. */
 | 
						|
	      if (len != header_len)
 | 
						|
		error = PIXMA_EPROTO;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    error = PIXMA_EPROTO;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
  if (error == PIXMA_EPROTO)
 | 
						|
    {
 | 
						|
      pixma_dbg (1, "WARNING: result len=%d expected %d\n",
 | 
						|
		 len, cb->expected_reslen);
 | 
						|
      pixma_hexdump (1, r, MIN (len, 64));
 | 
						|
    }
 | 
						|
#endif
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_cmd_transaction (pixma_t * s, const void *cmd, unsigned cmdlen,
 | 
						|
		       void *data, unsigned expected_len)
 | 
						|
{
 | 
						|
  int error, tmo;
 | 
						|
 | 
						|
  error = pixma_write (s->io, cmd, cmdlen);
 | 
						|
  if (error != (int) cmdlen)
 | 
						|
    {
 | 
						|
      if (error >= 0)
 | 
						|
	{
 | 
						|
	  /* Write timeout is too low? */
 | 
						|
	  PDBG (pixma_dbg
 | 
						|
		(1, "ERROR:incomplete write, %u out of %u written\n",
 | 
						|
		 (unsigned) error, cmdlen));
 | 
						|
	  error = PIXMA_ETIMEDOUT;
 | 
						|
	}
 | 
						|
      return error;
 | 
						|
    }
 | 
						|
 | 
						|
  /* When you send the start_session command while the scanner optic is
 | 
						|
     going back to the home position after the last scan session has been
 | 
						|
     cancelled, you won't get the response before it arrives home. This takes
 | 
						|
     about 5 seconds. If the last session was succeeded, the scanner will
 | 
						|
     immediatly answer with PIXMA_STATUS_BUSY.
 | 
						|
 | 
						|
     Is 8 seconds timeout enough? This affects ALL commands that use
 | 
						|
     pixma_cmd_transaction(). */
 | 
						|
  tmo = 8;
 | 
						|
  do
 | 
						|
    {
 | 
						|
      error = pixma_read (s->io, data, expected_len);
 | 
						|
      if (error == PIXMA_ETIMEDOUT)
 | 
						|
	PDBG (pixma_dbg (2, "No response yet. Timed out in %d sec.\n", tmo));
 | 
						|
    }
 | 
						|
  while (error == PIXMA_ETIMEDOUT && --tmo != 0);
 | 
						|
  if (error < 0)
 | 
						|
    {
 | 
						|
      PDBG (pixma_dbg (1, "WARNING:Error in response phase. cmd:%02x%02x\n",
 | 
						|
		       ((const uint8_t *) cmd)[0],
 | 
						|
		       ((const uint8_t *) cmd)[1]));
 | 
						|
      PDBG (pixma_dbg
 | 
						|
	    (1,
 | 
						|
	     "   If the scanner hangs, reset it and/or unplug the "
 | 
						|
	     "USB cable.\n"));
 | 
						|
    }
 | 
						|
  return error;			/* length of the result packet or error */
 | 
						|
}
 | 
						|
 | 
						|
uint8_t *
 | 
						|
pixma_newcmd (pixma_cmdbuf_t * cb, unsigned cmd,
 | 
						|
	      unsigned dataout, unsigned datain)
 | 
						|
{
 | 
						|
  unsigned cmdlen = cb->cmd_header_len + dataout;
 | 
						|
  unsigned reslen = cb->res_header_len + datain;
 | 
						|
 | 
						|
  if (cmdlen > cb->size || reslen > cb->size)
 | 
						|
    return NULL;
 | 
						|
  memset (cb->buf, 0, cmdlen);
 | 
						|
  cb->cmdlen = cmdlen;
 | 
						|
  cb->expected_reslen = reslen;
 | 
						|
  pixma_set_be16 (cmd, cb->buf);
 | 
						|
  pixma_set_be16 (dataout + datain, cb->buf + cb->cmd_len_field_ofs);
 | 
						|
  if (dataout != 0)
 | 
						|
    return cb->buf + cb->cmd_header_len;
 | 
						|
  else
 | 
						|
    return cb->buf + cb->res_header_len;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_exec (pixma_t * s, pixma_cmdbuf_t * cb)
 | 
						|
{
 | 
						|
  if (cb->cmdlen > cb->cmd_header_len)
 | 
						|
    pixma_fill_checksum (cb->buf + cb->cmd_header_len,
 | 
						|
			 cb->buf + cb->cmdlen - 1);
 | 
						|
  cb->reslen =
 | 
						|
    pixma_cmd_transaction (s, cb->buf, cb->cmdlen, cb->buf,
 | 
						|
			   cb->expected_reslen);
 | 
						|
  return pixma_check_result (cb);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_exec_short_cmd (pixma_t * s, pixma_cmdbuf_t * cb, unsigned cmd)
 | 
						|
{
 | 
						|
  pixma_newcmd (cb, cmd, 0, 0);
 | 
						|
  return pixma_exec (s, cb);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_check_dpi (unsigned dpi, unsigned max)
 | 
						|
{
 | 
						|
  /* valid dpi = 75 * 2^n */
 | 
						|
  unsigned temp = dpi / 75;
 | 
						|
  if (dpi > max || dpi < 75 || 75 * temp != dpi || (temp & (temp - 1)) != 0)
 | 
						|
    return PIXMA_EINVAL;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
pixma_init (void)
 | 
						|
{
 | 
						|
  PDBG (pixma_dbg (2, "pixma version %d.%d.%d\n", PIXMA_VERSION_MAJOR,
 | 
						|
		   PIXMA_VERSION_MINOR, PIXMA_VERSION_BUILD));
 | 
						|
  PASSERT (first_pixma == NULL);
 | 
						|
  if (tstart_sec == 0)
 | 
						|
    pixma_get_time (&tstart_sec, &tstart_usec);
 | 
						|
  return pixma_io_init ();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_cleanup (void)
 | 
						|
{
 | 
						|
  while (first_pixma)
 | 
						|
    pixma_close (first_pixma);
 | 
						|
  pixma_io_cleanup ();
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_open (unsigned devnr, pixma_t ** handle)
 | 
						|
{
 | 
						|
  int error;
 | 
						|
  pixma_t *s;
 | 
						|
  const pixma_config_t *cfg;
 | 
						|
 | 
						|
  *handle = NULL;
 | 
						|
  cfg = pixma_get_device_config (devnr);
 | 
						|
  if (!cfg)
 | 
						|
    return PIXMA_EINVAL;	/* invalid devnr */
 | 
						|
  PDBG (pixma_dbg (2, "pixma_open(): %s\n", cfg->name));
 | 
						|
 | 
						|
  s = (pixma_t *) calloc (1, sizeof (s[0]));
 | 
						|
  if (!s)
 | 
						|
    return PIXMA_ENOMEM;
 | 
						|
  s->next = first_pixma;
 | 
						|
  first_pixma = s;
 | 
						|
 | 
						|
  s->cfg = cfg;
 | 
						|
  error = pixma_connect (devnr, &s->io);
 | 
						|
  if (error < 0)
 | 
						|
    {
 | 
						|
      PDBG (pixma_dbg
 | 
						|
	    (2, "pixma_connect() failed %s\n", pixma_strerror (error)));
 | 
						|
      goto rollback;
 | 
						|
    }
 | 
						|
  strncpy (s->id, pixma_get_device_id (devnr), sizeof (s->id) - 1);
 | 
						|
  s->ops = s->cfg->ops;
 | 
						|
  s->scanning = 0;
 | 
						|
  error = s->ops->open (s);
 | 
						|
  if (error < 0)
 | 
						|
    goto rollback;
 | 
						|
  *handle = s;
 | 
						|
  return 0;
 | 
						|
 | 
						|
rollback:
 | 
						|
  PDBG (pixma_dbg (2, "pixma_open() failed %s\n", pixma_strerror (error)));
 | 
						|
  pixma_close (s);
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_close (pixma_t * s)
 | 
						|
{
 | 
						|
  pixma_t **p;
 | 
						|
 | 
						|
  if (!s)
 | 
						|
    return;
 | 
						|
  for (p = &first_pixma; *p && *p != s; p = &((*p)->next))
 | 
						|
    {
 | 
						|
    }
 | 
						|
  PASSERT (*p);
 | 
						|
  if (!(*p))
 | 
						|
    return;
 | 
						|
  PDBG (pixma_dbg (2, "pixma_close(): %s\n", s->cfg->name));
 | 
						|
  if (s->io)
 | 
						|
    {
 | 
						|
      if (s->scanning)
 | 
						|
	{
 | 
						|
	  PDBG (pixma_dbg (3, "pixma_close():scanning in progress, call"
 | 
						|
			   " finish_scan()\n"));
 | 
						|
	  s->ops->finish_scan (s);
 | 
						|
	}
 | 
						|
      s->ops->close (s);
 | 
						|
      pixma_disconnect (s->io);
 | 
						|
    }
 | 
						|
  *p = s->next;
 | 
						|
  free (s);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_scan (pixma_t * s, pixma_scan_param_t * sp)
 | 
						|
{
 | 
						|
  int error;
 | 
						|
 | 
						|
  error = pixma_check_scan_param (s, sp);
 | 
						|
  if (error < 0)
 | 
						|
    return error;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
  pixma_dbg (3, "\n");
 | 
						|
  pixma_dbg (3, "pixma_scan(): start\n");
 | 
						|
  pixma_dbg (3, "  line_size=%u image_size=%u channels=%u depth=%u\n",
 | 
						|
	     sp->line_size, sp->image_size, sp->channels, sp->depth);
 | 
						|
  pixma_dbg (3, "  dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n",
 | 
						|
	     sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h);
 | 
						|
  pixma_dbg (3, "  gamma_table=%p source=%d\n", sp->gamma_table, sp->source);
 | 
						|
#endif
 | 
						|
 | 
						|
  s->param = sp;
 | 
						|
  s->cancel = 0;
 | 
						|
  s->cur_image_size = 0;
 | 
						|
  s->imagebuf.wptr = NULL;
 | 
						|
  s->imagebuf.wend = NULL;
 | 
						|
  s->imagebuf.rptr = NULL;
 | 
						|
  s->imagebuf.rend = NULL;
 | 
						|
  s->underrun = 0;
 | 
						|
  error = s->ops->scan (s);
 | 
						|
  if (error >= 0)
 | 
						|
    {
 | 
						|
      s->scanning = 1;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      PDBG (pixma_dbg
 | 
						|
	    (3, "pixma_scan() failed %s\n", pixma_strerror (error)));
 | 
						|
    }
 | 
						|
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
static uint8_t *
 | 
						|
fill_pixels (pixma_t * s, uint8_t * ptr, uint8_t * end, uint8_t value)
 | 
						|
{
 | 
						|
  if (s->cur_image_size < s->param->image_size)
 | 
						|
    {
 | 
						|
      int n = s->param->image_size - s->cur_image_size;
 | 
						|
      if (n > (end - ptr))
 | 
						|
	n = end - ptr;
 | 
						|
      memset (ptr, value, n);
 | 
						|
      s->cur_image_size += n;
 | 
						|
      ptr += n;
 | 
						|
    }
 | 
						|
  return ptr;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_read_image (pixma_t * s, void *buf, unsigned len)
 | 
						|
{
 | 
						|
  int result;
 | 
						|
  pixma_imagebuf_t ib;
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    return 0;
 | 
						|
  if (s->cancel)
 | 
						|
    {
 | 
						|
      result = PIXMA_ECANCELED;
 | 
						|
      goto cancel;
 | 
						|
    }
 | 
						|
 | 
						|
  ib = s->imagebuf;		/* get rptr and rend */
 | 
						|
  ib.wptr = (uint8_t *) buf;
 | 
						|
  ib.wend = ib.wptr + len;
 | 
						|
 | 
						|
  if (s->underrun)
 | 
						|
    {
 | 
						|
      if (s->cur_image_size < s->param->image_size)
 | 
						|
	{
 | 
						|
	  ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  PDBG (pixma_dbg
 | 
						|
		(3, "pixma_read_image():completed (underrun detected)\n"));
 | 
						|
	  s->scanning = 0;
 | 
						|
	}
 | 
						|
      return ib.wptr - (uint8_t *) buf;
 | 
						|
    }
 | 
						|
 | 
						|
  while (ib.wptr != ib.wend)
 | 
						|
    {
 | 
						|
      if (ib.rptr == ib.rend)
 | 
						|
	{
 | 
						|
	  ib.rptr = ib.rend = NULL;
 | 
						|
	  result = s->ops->fill_buffer (s, &ib);
 | 
						|
	  if (result < 0)
 | 
						|
	    goto cancel;
 | 
						|
	  if (result == 0)
 | 
						|
	    {			/* end of image? */
 | 
						|
	      s->ops->finish_scan (s);
 | 
						|
#ifndef NDEBUG
 | 
						|
	      if (s->cur_image_size != s->param->image_size)
 | 
						|
		{
 | 
						|
		  pixma_dbg (1, "WARNING:image size mismatches\n");
 | 
						|
		  pixma_dbg (1,
 | 
						|
			     "    %u expected (%d lines) but %u received (%d lines)\n",
 | 
						|
			     s->param->image_size, s->param->h,
 | 
						|
			     s->cur_image_size,
 | 
						|
			     s->cur_image_size / s->param->line_size);
 | 
						|
		  if ((s->cur_image_size % s->param->line_size) != 0)
 | 
						|
		    {
 | 
						|
		      pixma_dbg (1,
 | 
						|
				 "BUG:received data not multiple of line_size\n");
 | 
						|
		    }
 | 
						|
		}
 | 
						|
#endif /* !NDEBUG */
 | 
						|
	      if (s->cur_image_size < s->param->image_size)
 | 
						|
		{
 | 
						|
		  s->underrun = 1;
 | 
						|
		  ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff);
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  PDBG (pixma_dbg (3, "pixma_read_image():completed\n"));
 | 
						|
		  s->scanning = 0;
 | 
						|
		}
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	  s->cur_image_size += result;
 | 
						|
	  PASSERT (s->cur_image_size <= s->param->image_size);
 | 
						|
	}
 | 
						|
      if (ib.rptr)
 | 
						|
	{
 | 
						|
	  unsigned count = MIN (ib.rend - ib.rptr, ib.wend - ib.wptr);
 | 
						|
	  memcpy (ib.wptr, ib.rptr, count);
 | 
						|
	  ib.rptr += count;
 | 
						|
	  ib.wptr += count;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  s->imagebuf = ib;		/* store rptr and rend */
 | 
						|
  return ib.wptr - (uint8_t *) buf;
 | 
						|
 | 
						|
cancel:
 | 
						|
  s->ops->finish_scan (s);
 | 
						|
  s->scanning = 0;
 | 
						|
  if (result == PIXMA_ECANCELED)
 | 
						|
    {
 | 
						|
      PDBG (pixma_dbg (3, "pixma_read_image():cancelled by %sware\n",
 | 
						|
		       (s->cancel) ? "soft" : "hard"));
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      PDBG (pixma_dbg (3, "pixma_read_image() failed %s\n",
 | 
						|
		       pixma_strerror (result)));
 | 
						|
    }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_cancel (pixma_t * s)
 | 
						|
{
 | 
						|
  s->cancel = 1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_enable_background (pixma_t * s, int enabled)
 | 
						|
{
 | 
						|
  return pixma_set_interrupt_mode (s->io, enabled);
 | 
						|
}
 | 
						|
 | 
						|
unsigned
 | 
						|
pixma_wait_event (pixma_t * s, int timeout /*ms */ )
 | 
						|
{
 | 
						|
  unsigned events;
 | 
						|
 | 
						|
  if (s->events == PIXMA_EV_NONE && s->ops->wait_event)
 | 
						|
    s->ops->wait_event (s, timeout);
 | 
						|
  events = s->events;
 | 
						|
  s->events = PIXMA_EV_NONE;
 | 
						|
  return events;
 | 
						|
}
 | 
						|
 | 
						|
#define CLAMP2(x,w,min,max,dpi) do {		\
 | 
						|
    unsigned m = (max) * (dpi) / 75;		\
 | 
						|
    x = MIN(x, m - min);			\
 | 
						|
    w = MIN(w, m - x);				\
 | 
						|
    if (w < min)  w = min;			\
 | 
						|
} while(0)
 | 
						|
 | 
						|
int
 | 
						|
pixma_check_scan_param (pixma_t * s, pixma_scan_param_t * sp)
 | 
						|
{
 | 
						|
  if (!(sp->channels == 3 ||
 | 
						|
	(sp->channels == 1 && (s->cfg->cap & PIXMA_CAP_GRAY) != 0)))
 | 
						|
    return PIXMA_EINVAL;
 | 
						|
 | 
						|
  if (pixma_check_dpi (sp->xdpi, s->cfg->xdpi) < 0 ||
 | 
						|
      pixma_check_dpi (sp->ydpi, s->cfg->ydpi) < 0)
 | 
						|
    return PIXMA_EINVAL;
 | 
						|
 | 
						|
  /* xdpi must be equal to ydpi except that
 | 
						|
     xdpi = max_xdpi and ydpi = max_ydpi. */
 | 
						|
  if (!(sp->xdpi == sp->ydpi ||
 | 
						|
	(sp->xdpi == s->cfg->xdpi && sp->ydpi == s->cfg->ydpi)))
 | 
						|
    return PIXMA_EINVAL;
 | 
						|
 | 
						|
  /* FIXME: I assume the same minimum width and height for every model. */
 | 
						|
  CLAMP2 (sp->x, sp->w, 13, s->cfg->width, sp->xdpi);
 | 
						|
  CLAMP2 (sp->y, sp->h, 8, s->cfg->height, sp->ydpi);
 | 
						|
 | 
						|
  switch (sp->source)
 | 
						|
    {
 | 
						|
    case PIXMA_SOURCE_FLATBED:
 | 
						|
      break;
 | 
						|
 | 
						|
    case PIXMA_SOURCE_TPU:
 | 
						|
      if ((s->cfg->cap & PIXMA_CAP_TPU) != PIXMA_CAP_TPU)
 | 
						|
	{
 | 
						|
	  sp->source = PIXMA_SOURCE_FLATBED;
 | 
						|
	  PDBG (pixma_dbg
 | 
						|
		(1, "WARNING: TPU unsupported, fallback to flatbed.\n"));
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    case PIXMA_SOURCE_ADF:
 | 
						|
      if ((s->cfg->cap & PIXMA_CAP_ADF) != PIXMA_CAP_ADF)
 | 
						|
	{
 | 
						|
	  sp->source = PIXMA_SOURCE_FLATBED;
 | 
						|
	  PDBG (pixma_dbg
 | 
						|
		(1, "WARNING: ADF unsupported, fallback to flatbed.\n"));
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    case PIXMA_SOURCE_ADFDUP:
 | 
						|
      if ((s->cfg->cap & PIXMA_CAP_ADFDUP) != PIXMA_CAP_ADFDUP)
 | 
						|
	{
 | 
						|
	  if (s->cfg->cap & PIXMA_CAP_ADF)
 | 
						|
	    {
 | 
						|
	      sp->source = PIXMA_SOURCE_ADF;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      sp->source = PIXMA_SOURCE_FLATBED;
 | 
						|
	    }
 | 
						|
	  PDBG (pixma_dbg
 | 
						|
		(1, "WARNING: ADF duplex unsupported, fallback to %d.\n",
 | 
						|
		 sp->source));
 | 
						|
	}
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  if (sp->depth == 0)
 | 
						|
    sp->depth = 8;
 | 
						|
  if ((sp->depth % 8) != 0)
 | 
						|
    return PIXMA_EINVAL;
 | 
						|
 | 
						|
  sp->line_size = 0;
 | 
						|
 | 
						|
  if (s->ops->check_param (s, sp) < 0)
 | 
						|
    return PIXMA_EINVAL;
 | 
						|
 | 
						|
  if (sp->line_size == 0)
 | 
						|
    sp->line_size = sp->depth / 8 * sp->channels * sp->w;
 | 
						|
  sp->image_size = sp->line_size * sp->h;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
pixma_get_string (pixma_t * s, pixma_string_index_t i)
 | 
						|
{
 | 
						|
  switch (i)
 | 
						|
    {
 | 
						|
    case PIXMA_STRING_MODEL:
 | 
						|
      return s->cfg->name;
 | 
						|
    case PIXMA_STRING_ID:
 | 
						|
      return s->id;
 | 
						|
    case PIXMA_STRING_LAST:
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
const pixma_config_t *
 | 
						|
pixma_get_config (pixma_t * s)
 | 
						|
{
 | 
						|
  return s->cfg;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  double r_gamma = 1.0 / gamma;
 | 
						|
  double out_scale = 255.0;
 | 
						|
  double in_scale = 1.0 / (n - 1);
 | 
						|
 | 
						|
  for (i = 0; (unsigned) i != n; i++)
 | 
						|
    {
 | 
						|
      table[i] = (int) (out_scale * pow (i * in_scale, r_gamma) + 0.5);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pixma_find_scanners (void)
 | 
						|
{
 | 
						|
  return pixma_collect_devices (pixma_devices);
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
pixma_get_device_model (unsigned devnr)
 | 
						|
{
 | 
						|
  const pixma_config_t *cfg = pixma_get_device_config (devnr);
 | 
						|
  return (cfg) ? cfg->name : NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
pixma_get_device_status (pixma_t * s, pixma_device_status_t * status)
 | 
						|
{
 | 
						|
  if (!status)
 | 
						|
    return PIXMA_EINVAL;
 | 
						|
  memset (status, 0, sizeof (*status));
 | 
						|
  return s->ops->get_status (s, status);
 | 
						|
}
 |