kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1612 wiersze
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1612 wiersze
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
/**
 | 
						|
 * Description of the Primax PagePartner model
 | 
						|
 */
 | 
						|
static P5_Model pagepartner_model = {
 | 
						|
  "Primax PagePartner",
 | 
						|
  "Primax",
 | 
						|
  "PagePartner",
 | 
						|
  SANE_I18N ("sheetfed scanner"),
 | 
						|
 | 
						|
  {300, 200, 150, 100, 0},
 | 
						|
  /* 500 seems also possible */
 | 
						|
  {600, 400, 300, 200, 150, 100, 0},
 | 
						|
 | 
						|
  300,
 | 
						|
  600,
 | 
						|
  100,
 | 
						|
  100,
 | 
						|
  16,
 | 
						|
 | 
						|
  SANE_FIX (0.0),
 | 
						|
  SANE_FIX (0.0),
 | 
						|
  SANE_FIX (215.9),
 | 
						|
  SANE_FIX (300.0),
 | 
						|
};
 | 
						|
 | 
						|
#ifdef HAVE_LINUX_PPDEV_H
 | 
						|
static char *
 | 
						|
addr_name (uint16_t addr)
 | 
						|
{
 | 
						|
  switch (addr)
 | 
						|
    {
 | 
						|
    case DATA:
 | 
						|
      return "DATA";
 | 
						|
      break;
 | 
						|
    case STATUS:
 | 
						|
      return "STATUS";
 | 
						|
      break;
 | 
						|
    case CONTROL:
 | 
						|
      return "CONTROL";
 | 
						|
      break;
 | 
						|
    case EPPADR:
 | 
						|
      return "EPPADR";
 | 
						|
      break;
 | 
						|
    case EPPDATA:
 | 
						|
      return "EPPDATA";
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return "*ERROR*";
 | 
						|
    }
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/** @brief low level hardware access functions
 | 
						|
 * @{
 | 
						|
 */
 | 
						|
 | 
						|
static uint8_t
 | 
						|
inb (int fd, uint16_t addr)
 | 
						|
{
 | 
						|
#ifdef HAVE_LINUX_PPDEV_H
 | 
						|
  uint8_t val = 0xff;
 | 
						|
  int rc, mode = 0xff;
 | 
						|
 | 
						|
  switch (addr)
 | 
						|
    {
 | 
						|
    case DATA:
 | 
						|
      rc = ioctl (fd, PPRDATA, &val);
 | 
						|
      break;
 | 
						|
    case STATUS:
 | 
						|
      rc = ioctl (fd, PPRSTATUS, &val);
 | 
						|
      break;
 | 
						|
    case CONTROL:
 | 
						|
      rc = ioctl (fd, PPRCONTROL, &val);
 | 
						|
      break;
 | 
						|
    case EPPDATA:
 | 
						|
      mode = 1;			/* data_reverse */
 | 
						|
      rc = ioctl (fd, PPDATADIR, &mode);
 | 
						|
      mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
 | 
						|
      rc = ioctl (fd, PPSETMODE, &mode);
 | 
						|
#ifdef PPSETFLAGS
 | 
						|
      mode = PP_FASTREAD;
 | 
						|
      rc = ioctl (fd, PPSETFLAGS, &mode);
 | 
						|
#endif
 | 
						|
      rc = read (fd, &val, 1);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      DBG (DBG_error, "inb(%s) escaped ppdev\n", addr_name (addr));
 | 
						|
      return 0xFF;
 | 
						|
    }
 | 
						|
  if (rc < 0)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
 | 
						|
    }
 | 
						|
  return val;
 | 
						|
#else
 | 
						|
  if(fd && addr)
 | 
						|
    return 0;
 | 
						|
  return 0;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
outb (int fd, uint16_t addr, uint8_t value)
 | 
						|
{
 | 
						|
#ifdef HAVE_LINUX_PPDEV_H
 | 
						|
  int rc = 0, mode = 0xff;
 | 
						|
 | 
						|
  switch (addr)
 | 
						|
    {
 | 
						|
    case DATA:
 | 
						|
      rc = ioctl (fd, PPWDATA, &value);
 | 
						|
      break;
 | 
						|
    case CONTROL:
 | 
						|
      mode = value & 0x20;
 | 
						|
      rc = ioctl (fd, PPDATADIR, &mode);
 | 
						|
      if (!rc)
 | 
						|
	{
 | 
						|
	  value = value & 0xDF;
 | 
						|
	  rc = ioctl (fd, PPWCONTROL, &value);
 | 
						|
	}
 | 
						|
      break;
 | 
						|
    case EPPDATA:
 | 
						|
      mode = 0;			/* data forward */
 | 
						|
      rc = ioctl (fd, PPDATADIR, &mode);
 | 
						|
      mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
 | 
						|
      rc = ioctl (fd, PPSETMODE, &mode);
 | 
						|
      rc = write (fd, &value, 1);
 | 
						|
      break;
 | 
						|
    case EPPADR:
 | 
						|
      mode = 0;			/* data forward */
 | 
						|
      rc = ioctl (fd, PPDATADIR, &mode);
 | 
						|
      mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
 | 
						|
      rc = ioctl (fd, PPSETMODE, &mode);
 | 
						|
      rc = write (fd, &value, 1);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      DBG (DBG_error, "outb(%s,0x%02x) escaped ppdev\n", addr_name (addr),
 | 
						|
	   value);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  if (rc < 0)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
 | 
						|
    }
 | 
						|
#else
 | 
						|
  if(fd && addr && value)
 | 
						|
    return;
 | 
						|
#endif /* HAVE_LINUX_PPDEV_H */
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
write_reg (int fd, uint8_t index, uint8_t value)
 | 
						|
{
 | 
						|
  uint8_t idx;
 | 
						|
 | 
						|
  /* both nibbles hold the same value */
 | 
						|
  idx = index & 0x0F;
 | 
						|
  DBG (DBG_io2, "write_reg(REG%X,0x%x)\n", idx, value);
 | 
						|
  idx = idx << 4 | idx;
 | 
						|
  outb (fd, EPPADR, idx);
 | 
						|
  outb (fd, EPPDATA, value);
 | 
						|
}
 | 
						|
 | 
						|
static uint8_t
 | 
						|
read_reg (int fd, uint8_t index)
 | 
						|
{
 | 
						|
  uint8_t idx;
 | 
						|
 | 
						|
  /* both nibbles hold the same value */
 | 
						|
  idx = index & 0x0F;
 | 
						|
  idx = idx << 4 | idx;
 | 
						|
  outb (fd, EPPADR, idx);
 | 
						|
  return inb (fd, EPPDATA);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_LINUX_PPDEV_H
 | 
						|
static int
 | 
						|
read_data (int fd, uint8_t * data, int length)
 | 
						|
{
 | 
						|
  int mode, rc, nb;
 | 
						|
  unsigned char bval;
 | 
						|
 | 
						|
  bval = REG8;
 | 
						|
  mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
 | 
						|
  rc = ioctl (fd, PPSETMODE, &mode);
 | 
						|
  rc = write (fd, &bval, 1);
 | 
						|
 | 
						|
  mode = 1;			/* data_reverse */
 | 
						|
  rc = ioctl (fd, PPDATADIR, &mode);
 | 
						|
#ifdef PPSETFLAGS
 | 
						|
  mode = PP_FASTREAD;
 | 
						|
  rc = ioctl (fd, PPSETFLAGS, &mode);
 | 
						|
#endif
 | 
						|
  mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
 | 
						|
  rc = ioctl (fd, PPSETMODE, &mode);
 | 
						|
  nb = 0;
 | 
						|
  while (nb < length)
 | 
						|
    {
 | 
						|
      rc = read (fd, data + nb, length - nb);
 | 
						|
      if (rc < 0)
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "memtest: error reading data back!\n");
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  nb += rc;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
index_write_data (int fd, uint8_t index, uint8_t * data, int length)
 | 
						|
{
 | 
						|
  int mode;
 | 
						|
  unsigned char bval;
 | 
						|
 | 
						|
  bval = index;
 | 
						|
  mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
 | 
						|
  ioctl (fd, PPSETMODE, &mode);
 | 
						|
  write (fd, &bval, 1);
 | 
						|
 | 
						|
  mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
 | 
						|
  ioctl (fd, PPSETMODE, &mode);
 | 
						|
  mode = 0;			/* data forward */
 | 
						|
  ioctl (fd, PPDATADIR, &mode);
 | 
						|
  write (fd, data, length);
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
write_data (int fd, uint8_t * data, int length)
 | 
						|
{
 | 
						|
  index_write_data (fd, REG8, data, length);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
write_reg2 (int fd, uint8_t index, uint16_t value)
 | 
						|
{
 | 
						|
  uint8_t data2[2];
 | 
						|
 | 
						|
  data2[0] = value & 0xff;
 | 
						|
  data2[1] = value >> 8;
 | 
						|
  index_write_data (fd, index, data2, 2);
 | 
						|
}
 | 
						|
#else
 | 
						|
 | 
						|
static int
 | 
						|
read_data (int fd, uint8_t * data, int length)
 | 
						|
{
 | 
						|
  if(fd && data && length)
 | 
						|
    return -1;
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
write_data (int fd, uint8_t * data, int length)
 | 
						|
{
 | 
						|
  if(fd && data && length)
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
write_reg2 (int fd, uint8_t index, uint16_t value)
 | 
						|
{
 | 
						|
  if(fd && index && value)
 | 
						|
    return;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
 * @}
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
/** @brief This function checks a memory buffer.
 | 
						|
 * This function writes at the given memory address then read it back
 | 
						|
 * to check the scanner is correctly working.
 | 
						|
 * @param fd file descriptor used to access hardware
 | 
						|
 * @param addr address where to write and read
 | 
						|
 * @return SANE_TRUE on succes, SANE_FALSE otherwise
 | 
						|
 */
 | 
						|
static int
 | 
						|
memtest (int fd, uint16_t addr)
 | 
						|
{
 | 
						|
  uint8_t sent[256];
 | 
						|
  uint8_t back[256];
 | 
						|
  int i;
 | 
						|
 | 
						|
  write_reg2 (fd, REG1, addr);
 | 
						|
  for (i = 0; i < 256; i++)
 | 
						|
    {
 | 
						|
      sent[i] = (uint8_t) i;
 | 
						|
      back[i] = 0;
 | 
						|
    }
 | 
						|
  write_data (fd, sent, 256);
 | 
						|
  read_data (fd, back, 256);
 | 
						|
 | 
						|
  /* check if data read back is the same that the one sent */
 | 
						|
  for (i = 0; i < 256; i++)
 | 
						|
    {
 | 
						|
      if (back[i] != sent[i])
 | 
						|
	{
 | 
						|
	  return SANE_FALSE;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#define INB(k,y,z) val=inb(k,y); if(val!=z) { DBG(DBG_error,"expected 0x%02x, got 0x%02x\n",z, val); return SANE_FALSE; }
 | 
						|
 | 
						|
/** @brief connect to scanner
 | 
						|
 * This function sends the connect sequence for the scanner.
 | 
						|
 * @param fd filedescriptor of the parallel port communication channel
 | 
						|
 * @return SANE_TRUE in case of success, SANE_FALSE otherwise
 | 
						|
 */
 | 
						|
static int
 | 
						|
connect (int fd)
 | 
						|
{
 | 
						|
  uint8_t val;
 | 
						|
 | 
						|
  inb (fd, CONTROL);
 | 
						|
  outb (fd, CONTROL, 0x04);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  INB (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  INB (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  INB (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  INB (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  INB (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  INB (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  INB (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  INB (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  INB (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  INB (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  INB (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  INB (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  INB (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  INB (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x03);
 | 
						|
  outb (fd, DATA, 0x83);
 | 
						|
  INB (fd, DATA, 0x83);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  INB (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0x02);
 | 
						|
  outb (fd, DATA, 0x82);
 | 
						|
  outb (fd, DATA, 0xFF);
 | 
						|
  DBG (DBG_info, "connect() OK...\n");
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
disconnect (int fd)
 | 
						|
{
 | 
						|
  uint8_t val;
 | 
						|
 | 
						|
  outb (fd, CONTROL, 0x04);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  INB (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x01);
 | 
						|
  INB (fd, DATA, 0x01);
 | 
						|
  outb (fd, DATA, 0x01);
 | 
						|
  outb (fd, DATA, 0x81);
 | 
						|
  outb (fd, DATA, 0x01);
 | 
						|
  outb (fd, DATA, 0x81);
 | 
						|
  INB (fd, DATA, 0x81);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x81);
 | 
						|
  INB (fd, DATA, 0x81);
 | 
						|
  outb (fd, DATA, 0x01);
 | 
						|
  outb (fd, DATA, 0x81);
 | 
						|
  outb (fd, DATA, 0x01);
 | 
						|
  outb (fd, DATA, 0x81);
 | 
						|
  INB (fd, DATA, 0x81);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  INB (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  outb (fd, DATA, 0x00);
 | 
						|
  outb (fd, DATA, 0x80);
 | 
						|
  inb (fd, CONTROL);
 | 
						|
  outb (fd, CONTROL, 0x0C);
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
setadresses (int fd, uint16_t start, uint16_t end)
 | 
						|
{
 | 
						|
  write_reg (fd, REG3, start & 0xff);
 | 
						|
  write_reg (fd, REG4, start >> 8);
 | 
						|
  write_reg (fd, REG5, end & 0xff);
 | 
						|
  write_reg (fd, REG6, end >> 8);
 | 
						|
  DBG (DBG_io, "setadresses(0x%x,0x%x); OK...\n", start, end);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_LINUX_PPDEV_H
 | 
						|
/** @brief open parallel port device
 | 
						|
 * opens parallel port's low level device in EPP mode
 | 
						|
 * @param devicename nam of the real device or the special value 'auto'
 | 
						|
 * @return file descriptor in cas of successn -1 otherwise
 | 
						|
 */
 | 
						|
static int
 | 
						|
open_pp (const char *devicename)
 | 
						|
{
 | 
						|
  int fd, mode = 0;
 | 
						|
  char *name;
 | 
						|
 | 
						|
  DBG (DBG_proc, "open_pp: start, devicename=%s\n", devicename);
 | 
						|
  /* TODO improve auto device finding */
 | 
						|
  if (strncmp (devicename, "auto", 4) == 0)
 | 
						|
    {
 | 
						|
      name = strdup("/dev/parport0");
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      name = strdup(devicename);
 | 
						|
    }
 | 
						|
 | 
						|
  /* open device */
 | 
						|
  fd = open (name, O_RDWR);
 | 
						|
  if (fd < 0)
 | 
						|
    {
 | 
						|
      switch (errno)
 | 
						|
	{
 | 
						|
	case ENOENT:
 | 
						|
#ifdef ENIO
 | 
						|
	case ENXIO:
 | 
						|
#endif
 | 
						|
#ifdef ENODEV
 | 
						|
	case ENODEV:
 | 
						|
#endif
 | 
						|
	  DBG (DBG_error, "open_pp: no %s device ...\n", name);
 | 
						|
	  break;
 | 
						|
	case EACCES:
 | 
						|
	  DBG (DBG_error,
 | 
						|
	       "open_pp: current user cannot use existing %s device ...\n",
 | 
						|
	       name);
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  DBG (DBG_error, "open_pp: %s while opening %s\n", strerror (errno),
 | 
						|
	       name);
 | 
						|
	}
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  free(name);
 | 
						|
 | 
						|
  /* claim device and set it to EPP */
 | 
						|
  ioctl (fd, PPCLAIM);
 | 
						|
  ioctl (fd, PPGETMODES, &mode);
 | 
						|
  if (mode & PARPORT_MODE_PCSPP)
 | 
						|
    DBG (DBG_io, "PARPORT_MODE_PCSPP\n");
 | 
						|
  if (mode & PARPORT_MODE_TRISTATE)
 | 
						|
    DBG (DBG_io, "PARPORT_MODE_TRISTATE\n");
 | 
						|
  if (mode & PARPORT_MODE_EPP)
 | 
						|
    DBG (DBG_io, "PARPORT_MODE_EPP\n");
 | 
						|
  if (mode & PARPORT_MODE_ECP)
 | 
						|
    DBG (DBG_io, "PARPORT_MODE_ECP\n");
 | 
						|
  if (mode & PARPORT_MODE_COMPAT)
 | 
						|
    DBG (DBG_io, "PARPORT_MODE_COMPAT\n");
 | 
						|
  if (mode & PARPORT_MODE_DMA)
 | 
						|
    DBG (DBG_io, "PARPORT_MODE_DMA\n");
 | 
						|
  if (mode & PARPORT_MODE_EPP)
 | 
						|
    {
 | 
						|
      mode = IEEE1284_MODE_EPP;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
         if (mode & PARPORT_MODE_ECP)
 | 
						|
         {
 | 
						|
         mode = IEEE1284_MODE_ECP;
 | 
						|
         }
 | 
						|
         else
 | 
						|
       */
 | 
						|
      {
 | 
						|
	mode = -1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  if (mode == -1)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "open_pp: no EPP mode, giving up ...\n");
 | 
						|
      ioctl (fd, PPRELEASE);
 | 
						|
      close (fd);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  ioctl (fd, PPNEGOT, &mode);
 | 
						|
  ioctl (fd, PPSETMODE, &mode);
 | 
						|
  DBG (DBG_proc, "open_pp: exit\n");
 | 
						|
  return fd;
 | 
						|
}
 | 
						|
 | 
						|
/** close low level device
 | 
						|
 * release and close low level hardware device
 | 
						|
 */
 | 
						|
static void
 | 
						|
close_pp (int fd)
 | 
						|
{
 | 
						|
  int mode = IEEE1284_MODE_COMPAT;
 | 
						|
 | 
						|
  if (fd > 2)
 | 
						|
    {
 | 
						|
      ioctl (fd, PPNEGOT, &mode);
 | 
						|
      ioctl (fd, PPRELEASE);
 | 
						|
      close (fd);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#else /*  HAVE_LINUX_PPDEV_H */
 | 
						|
 | 
						|
static int
 | 
						|
open_pp (const char *devicename)
 | 
						|
{
 | 
						|
  if(devicename)
 | 
						|
    return -1;
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
close_pp (int fd)
 | 
						|
{
 | 
						|
  if(fd)
 | 
						|
    return;
 | 
						|
}
 | 
						|
#endif /*  HAVE_LINUX_PPDEV_H */
 | 
						|
 | 
						|
/** @brief test if a document is inserted
 | 
						|
 * Test if a document is inserted by reading register E
 | 
						|
 * @param fd file descriptor to access scanner
 | 
						|
 * @return SANE_STATUS_NO_DOCS if no document or SANE_STATUS_GOOD
 | 
						|
 * if something is present.
 | 
						|
 */
 | 
						|
static SANE_Status
 | 
						|
test_document (int fd)
 | 
						|
{
 | 
						|
  int detector;
 | 
						|
 | 
						|
  /* check for document presence 0xC6: present, 0xC3 no document */
 | 
						|
  detector = read_reg (fd, REGE);
 | 
						|
  DBG (DBG_io, "test_document: detector=0x%02X\n", detector);
 | 
						|
 | 
						|
  /* document inserted */
 | 
						|
  if (detector & 0x04)
 | 
						|
    return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
  return SANE_STATUS_NO_DOCS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * return the amount of scanned data available
 | 
						|
 * @param fd file descriptor to access scanner
 | 
						|
 * @return avaible byte number
 | 
						|
 */
 | 
						|
static int
 | 
						|
available_bytes (int fd)
 | 
						|
{
 | 
						|
  int counter;
 | 
						|
 | 
						|
  /* read the number of 256 bytes block of scanned data */
 | 
						|
  counter = read_reg (fd, REG9);
 | 
						|
  DBG (DBG_io, "available_bytes: available_bytes=0x%02X\n", counter);
 | 
						|
  return 256 * counter;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
build_correction (P5_Device * dev, unsigned int dpi, unsigned int mode,
 | 
						|
		  unsigned int start, unsigned int width)
 | 
						|
{
 | 
						|
  unsigned int i, j, shift, step;
 | 
						|
 | 
						|
  DBG (DBG_proc, "build_correction: start=%d, width=%d\n", start, width);
 | 
						|
  DBG (DBG_trace, "build_correction: dpi=%d, mode=%d\n", dpi, mode);
 | 
						|
 | 
						|
  /* loop on calibration data to find the matching one */
 | 
						|
  j = 0;
 | 
						|
  while (dev->calibration_data[j]->dpi != dpi)
 | 
						|
    {
 | 
						|
      j++;
 | 
						|
      if (j > MAX_RESOLUTIONS)
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "build_correction: couldn't find calibration!\n");
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (dev->gain != NULL)
 | 
						|
    {
 | 
						|
      free (dev->gain);
 | 
						|
      dev->gain = NULL;
 | 
						|
    }
 | 
						|
  if (dev->offset != NULL)
 | 
						|
    {
 | 
						|
      free (dev->offset);
 | 
						|
      dev->offset = NULL;
 | 
						|
    }
 | 
						|
  dev->gain = (float *) malloc (width * sizeof (float));
 | 
						|
  if (dev->gain == NULL)
 | 
						|
    {
 | 
						|
      DBG (DBG_error,
 | 
						|
	   "build_correction: failed to allocate memory for gain!\n");
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  dev->offset = (uint8_t *) malloc (width);
 | 
						|
  if (dev->offset == NULL)
 | 
						|
    {
 | 
						|
      DBG (DBG_error,
 | 
						|
	   "build_correction: failed to allocate memory for offset!\n");
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  /* compute starting point of calibration data to use */
 | 
						|
  shift = start;
 | 
						|
  step = 1;
 | 
						|
  if (mode == MODE_GRAY)
 | 
						|
    {
 | 
						|
      /* we use green data */
 | 
						|
      shift += 1;
 | 
						|
      step = 3;
 | 
						|
    }
 | 
						|
  for (i = 0; i < width; i += step)
 | 
						|
    {
 | 
						|
      if (dev->calibration_data[j]->white_data[shift + i] -
 | 
						|
	  dev->calibration_data[0]->black_data[shift + i] > BLACK_LEVEL)
 | 
						|
	{
 | 
						|
	  dev->gain[i] =
 | 
						|
	    WHITE_TARGET /
 | 
						|
	    ((float)
 | 
						|
	     (dev->calibration_data[j]->white_data[shift + i] -
 | 
						|
	      dev->calibration_data[j]->black_data[shift + i]));
 | 
						|
	  dev->offset[i] = dev->calibration_data[j]->black_data[shift + i];
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  dev->gain[i] = 1.0;
 | 
						|
	  dev->offset[i] = 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
  DBG (DBG_proc, "build_correction: end\n");
 | 
						|
}
 | 
						|
 | 
						|
/** @brief start up a real scan
 | 
						|
 * This function starts the scan with the given parameters.
 | 
						|
 * @param dev device describing hardware
 | 
						|
 * @param mode color, gray level or lineart.
 | 
						|
 * @param dpi desired scan resolution.
 | 
						|
 * @param startx coordinate of the first pixel to scan in
 | 
						|
 * scan's resolution coordinate
 | 
						|
 * @param width width of the scanned area
 | 
						|
 * scanner's physical scan aread.
 | 
						|
 * @return SANE_STATUS_GOOD if scan is successfully started
 | 
						|
 */
 | 
						|
static SANE_Status
 | 
						|
start_scan (P5_Device * dev, int mode, unsigned int dpi, unsigned int startx,
 | 
						|
	    unsigned int width)
 | 
						|
{
 | 
						|
  uint8_t reg0=0;
 | 
						|
  uint8_t reg2=0;
 | 
						|
  uint8_t regF=0;
 | 
						|
  uint16_t addr=0;
 | 
						|
  uint16_t start, end;
 | 
						|
  unsigned int xdpi;
 | 
						|
 | 
						|
  DBG (DBG_proc, "start_scan: start \n");
 | 
						|
  DBG (DBG_io, "start_scan: startx=%d, width=%d, dpi=%d\n", startx, width,
 | 
						|
       dpi);
 | 
						|
 | 
						|
	/** @brief register values
 | 
						|
	 * - reg2 : reg2 seems related to x dpi and provides only 100,
 | 
						|
	 *   150, 200 and 300 resolutions.
 | 
						|
	 * - regF : lower nibble gives y dpi resolution ranging from 150
 | 
						|
	 *   to 1200 dpi.
 | 
						|
	 */
 | 
						|
  xdpi = dpi;
 | 
						|
  switch (dpi)
 | 
						|
    {
 | 
						|
    case 100:
 | 
						|
      reg2 = 0x90;
 | 
						|
      regF = 0xA2;
 | 
						|
      break;
 | 
						|
    case 150:
 | 
						|
      reg2 = 0x10;
 | 
						|
      regF = 0xA4;
 | 
						|
      break;
 | 
						|
    case 200:
 | 
						|
      reg2 = 0x80;
 | 
						|
      regF = 0xA6;
 | 
						|
      break;
 | 
						|
    case 300:
 | 
						|
      reg2 = 0x00;
 | 
						|
      regF = 0xA8;
 | 
						|
      break;
 | 
						|
    case 400:
 | 
						|
      reg2 = 0x80;		/* xdpi=200 */
 | 
						|
      regF = 0xAA;
 | 
						|
      xdpi = 200;
 | 
						|
      break;
 | 
						|
    case 500:
 | 
						|
      reg2 = 0x00;
 | 
						|
      regF = 0xAC;
 | 
						|
      xdpi = 300;
 | 
						|
      break;
 | 
						|
    case 600:
 | 
						|
      reg2 = 0x00;
 | 
						|
      regF = 0xAE;
 | 
						|
      xdpi = 300;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  switch (mode)
 | 
						|
    {
 | 
						|
    case MODE_COLOR:
 | 
						|
      reg0 = 0x00;
 | 
						|
      addr = 0x0100;
 | 
						|
      break;
 | 
						|
    case MODE_GRAY:
 | 
						|
      /* green channel only */
 | 
						|
      reg0 = 0x20;
 | 
						|
      addr = 0x0100;
 | 
						|
      break;
 | 
						|
    case MODE_LINEART:
 | 
						|
      reg0 = 0x40;
 | 
						|
      addr = 0x0908;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  write_reg (dev->fd, REG1, 0x01);
 | 
						|
  write_reg (dev->fd, REG7, 0x00);
 | 
						|
  write_reg (dev->fd, REG0, reg0);
 | 
						|
  write_reg (dev->fd, REG1, 0x00);
 | 
						|
  write_reg (dev->fd, REGF, regF);
 | 
						|
  /* the memory addr used to test need not to be related
 | 
						|
   * to resolution, 0x0100 could be always used */
 | 
						|
  /* TODO get rid of it */
 | 
						|
  memtest (dev->fd, addr);
 | 
						|
 | 
						|
  /* handle case where dpi>xdpi */
 | 
						|
  start = startx;
 | 
						|
  if (dpi > xdpi)
 | 
						|
    {
 | 
						|
      width = (width * xdpi) / dpi;
 | 
						|
      start = (startx * xdpi) / dpi;
 | 
						|
    }
 | 
						|
 | 
						|
  /* compute and set start addr */
 | 
						|
  if (mode == MODE_COLOR)
 | 
						|
    {
 | 
						|
      start = start * 3;
 | 
						|
      width = width * 3;
 | 
						|
    }
 | 
						|
  end = start + width + 1;
 | 
						|
 | 
						|
  /* build calibration data for the scan */
 | 
						|
  if (dev->calibrated)
 | 
						|
    {
 | 
						|
      build_correction (dev, xdpi, mode, start, width);
 | 
						|
    }
 | 
						|
 | 
						|
  setadresses (dev->fd, start, end);
 | 
						|
 | 
						|
  write_reg (dev->fd, REG1, addr >> 8);
 | 
						|
  write_reg (dev->fd, REG2, reg2);
 | 
						|
  regF = (regF & 0x0F) | 0x80;
 | 
						|
  write_reg (dev->fd, REGF, regF);
 | 
						|
  write_reg (dev->fd, REG0, reg0);
 | 
						|
  if (mode == MODE_LINEART)
 | 
						|
    {
 | 
						|
      write_reg (dev->fd, 0x07, 0x04);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      write_reg (dev->fd, 0x07, 0x00);
 | 
						|
    }
 | 
						|
  write_reg (dev->fd, REG1, addr >> 8);
 | 
						|
  write_reg2 (dev->fd, REG1, addr);
 | 
						|
  write_reg (dev->fd, REGF, regF | 0x01);
 | 
						|
  write_reg (dev->fd, REG0, reg0 | 0x0C);
 | 
						|
  if (mode == MODE_LINEART)
 | 
						|
    {
 | 
						|
      write_reg (dev->fd, REG1, 0x19);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      write_reg (dev->fd, REG1, 0x11);
 | 
						|
    }
 | 
						|
  DBG (DBG_proc, "start_scan: exit\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/** read a line of scan data
 | 
						|
 * @param dev device to read
 | 
						|
 * @param data pointer where to store data
 | 
						|
 * @param length total bytes to read on one line
 | 
						|
 * @param ltr total number of lines to read
 | 
						|
 * @param retry signals that the function must read as much lines it can
 | 
						|
 * @param x2 tells that lines must be enlarged by a 2 factor
 | 
						|
 * @param mode COLOR_MODE if color mode
 | 
						|
 * @returns number of data lines read, -1 in case of error
 | 
						|
 */
 | 
						|
static int
 | 
						|
read_line (P5_Device * dev, uint8_t * data, size_t length, int ltr,
 | 
						|
	   SANE_Bool retry, SANE_Bool x2, int mode, SANE_Bool correction)
 | 
						|
{
 | 
						|
  uint8_t counter, read, cnt;
 | 
						|
  uint8_t inbuffer[MAX_SENSOR_PIXELS * 2 * 3 + 2];
 | 
						|
  unsigned int i, factor;
 | 
						|
  float val;
 | 
						|
 | 
						|
  DBG (DBG_proc, "read_line: trying to read %d lines of %lu bytes\n", ltr,
 | 
						|
       (unsigned long)length);
 | 
						|
 | 
						|
  counter = read_reg (dev->fd, REG9);
 | 
						|
  DBG (DBG_io, "read_line: %d bytes available\n", counter * 256);
 | 
						|
  read = 0;
 | 
						|
  if (x2 == SANE_FALSE)
 | 
						|
    {
 | 
						|
      factor = 1;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      factor = 2;
 | 
						|
    }
 | 
						|
 | 
						|
  /* in retry mode we read until not enough data, but in no retry
 | 
						|
   * read only one line , counter give us 256 bytes block available
 | 
						|
   * and we want an number multiple of color channels */
 | 
						|
  cnt = (255 + length / factor) / 256;
 | 
						|
  while ((counter > cnt && retry == 1) || (counter > cnt && read == 0))
 | 
						|
    {
 | 
						|
      /* read data from scanner, first and last byte aren't picture data */
 | 
						|
      read_data (dev->fd, inbuffer, length / factor + 2);
 | 
						|
 | 
						|
      /* image correction */
 | 
						|
      if (correction == SANE_TRUE)
 | 
						|
	{
 | 
						|
	  for (i = 0; i < length / factor; i++)
 | 
						|
	    {
 | 
						|
	      val = inbuffer[i + 1] - dev->offset[i];
 | 
						|
	      if (val > 0)
 | 
						|
		{
 | 
						|
		  val = val * dev->gain[i];
 | 
						|
		  if (val < 255)
 | 
						|
		    inbuffer[i + 1] = val;
 | 
						|
		  else
 | 
						|
		    inbuffer[i + 1] = 255;
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  inbuffer[i + 1] = 0;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      /* handle horizontal data doubling */
 | 
						|
      if (x2 == SANE_FALSE)
 | 
						|
	{
 | 
						|
	  memcpy (data + read * length, inbuffer + 1, length);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (mode == MODE_COLOR)
 | 
						|
	    {
 | 
						|
	      for (i = 0; i < length / factor; i += 3)
 | 
						|
		{
 | 
						|
		  data[read * length + i * factor] = inbuffer[i + 1];
 | 
						|
		  data[read * length + i * factor + 1] = inbuffer[i + 2];
 | 
						|
		  data[read * length + i * factor + 2] = inbuffer[i + 3];
 | 
						|
		  data[read * length + i * factor + 3] = inbuffer[i + 1];
 | 
						|
		  data[read * length + i * factor + 4] = inbuffer[i + 2];
 | 
						|
		  data[read * length + i * factor + 5] = inbuffer[i + 3];
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      for (i = 0; i < length / factor; i++)
 | 
						|
		{
 | 
						|
		  data[read * length + i * factor] = inbuffer[i + 1];
 | 
						|
		  data[read * length + i * factor + 1] = inbuffer[i + 1];
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      read++;
 | 
						|
      if (retry == SANE_TRUE)
 | 
						|
	{
 | 
						|
	  read_reg (dev->fd, REGF);
 | 
						|
	  read_reg (dev->fd, REGA);
 | 
						|
	  read_reg (dev->fd, REG9);
 | 
						|
	  counter = read_reg (dev->fd, REG9);
 | 
						|
	  read_reg (dev->fd, REGA);
 | 
						|
	  if (read >= ltr)
 | 
						|
	    {
 | 
						|
	      DBG (DBG_io, "read_line returning %d lines\n", read);
 | 
						|
	      return read;
 | 
						|
	    }
 | 
						|
	  counter = read_reg (dev->fd, REG9);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  read_reg (dev->fd, REGF);
 | 
						|
  read_reg (dev->fd, REGA);
 | 
						|
  read_reg (dev->fd, REG9);
 | 
						|
  counter = read_reg (dev->fd, REG9);
 | 
						|
  read_reg (dev->fd, REGA);
 | 
						|
  DBG (DBG_io, "read_line returning %d lines\n", read);
 | 
						|
  return read;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
eject (int fd)
 | 
						|
{
 | 
						|
  int detector;
 | 
						|
 | 
						|
  DBG (DBG_proc, "eject: start ...\n");
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
      write_reg2 (fd, REG1, 0x1110);
 | 
						|
      detector = read_reg (fd, REGE);
 | 
						|
      detector = read_reg (fd, REGE);
 | 
						|
    }
 | 
						|
  while ((detector & 0x04) != 0);
 | 
						|
  write_reg (fd, REG0, 0x00);
 | 
						|
  write_reg (fd, REG1, 0x00);
 | 
						|
  write_reg (fd, REGF, 0x82);
 | 
						|
  write_reg (fd, REG7, 0x00);
 | 
						|
 | 
						|
  DBG (DBG_proc, "eject: end.\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/** @brief wait for document to be present in feeder
 | 
						|
 * Polls document sensor until something is present. Give up after 20 seconds
 | 
						|
 * @param fd file descriptor of the physical device
 | 
						|
 */
 | 
						|
/* static int
 | 
						|
wait_document (int fd, uint8_t detector)
 | 
						|
{
 | 
						|
  int count = 0;
 | 
						|
  uint8_t val;
 | 
						|
 | 
						|
  write_reg (fd, REG1, 0x00);
 | 
						|
  write_reg (fd, REG7, 0x00);
 | 
						|
  detector = read_reg (fd, REGE);
 | 
						|
  while (detector == 0xc3 && count < 20)
 | 
						|
    {
 | 
						|
      sleep (1);
 | 
						|
      count++;
 | 
						|
      detector = read_reg (fd, REGE);
 | 
						|
    }
 | 
						|
  setadresses (fd, 0x002d, 0x09c7);
 | 
						|
  write_reg (fd, REG1, 0x00);
 | 
						|
  write_reg (fd, REG2, 0x90);
 | 
						|
  write_reg (fd, REGF, 0x82);
 | 
						|
  write_reg (fd, REG0, 0x00);
 | 
						|
  val = inb (fd, STATUS) & 0xf8;
 | 
						|
  if (val != 0xf8)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "wait_document: unexpected STATUS value 0x%02x instead of 0xf8", val);
 | 
						|
    }
 | 
						|
  if (count >= 20)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "wait_document: failed to detect document!\n");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  return 1;
 | 
						|
} */
 | 
						|
 | 
						|
/** @brief move at 150 dpi
 | 
						|
 * move the paper at 150 dpi motor speed by the amount specified
 | 
						|
 * @params dev pointer to the device structure
 | 
						|
 */
 | 
						|
static SANE_Status
 | 
						|
move (P5_Device * dev)
 | 
						|
{
 | 
						|
  int skip, done, read, count;
 | 
						|
  SANE_Status status = SANE_STATUS_GOOD;
 | 
						|
  unsigned char buffer[256];
 | 
						|
 | 
						|
  DBG (DBG_proc, "move: start\n");
 | 
						|
 | 
						|
  /* compute number of lines to skip */
 | 
						|
  skip = dev->ystart;
 | 
						|
 | 
						|
  /* works, but remains to be explained ... */
 | 
						|
  if (dev->ydpi > 300)
 | 
						|
    skip = skip / 2;
 | 
						|
 | 
						|
  DBG (DBG_io, "move: skipping %d lines at %d dpi\n", skip, dev->ydpi);
 | 
						|
 | 
						|
  /* we do a real scan of small width, discarding data */
 | 
						|
  done = 0;
 | 
						|
  status = start_scan (dev, MODE_GRAY, dev->ydpi, 0, 256);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "move: failed to start scan\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
      /* test if document left the feeder */
 | 
						|
      status = test_document (dev->fd);
 | 
						|
      if (status == SANE_STATUS_NO_DOCS)
 | 
						|
	{
 | 
						|
	  DBG (DBG_info,
 | 
						|
	       "move: document was shorter than the required move\n");
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
      /* test if data is available */
 | 
						|
      count = available_bytes (dev->fd);
 | 
						|
      if (count)
 | 
						|
	{
 | 
						|
	  read =
 | 
						|
	    read_line (dev, buffer, 256, 1, SANE_FALSE, SANE_FALSE,
 | 
						|
		       MODE_GRAY, SANE_FALSE);
 | 
						|
	  if (read == -1)
 | 
						|
	    {
 | 
						|
	      DBG (DBG_error, "move: failed to read data\n");
 | 
						|
	      return SANE_STATUS_INVAL;
 | 
						|
	    }
 | 
						|
	  done += read;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  while (done < skip);
 | 
						|
 | 
						|
  /* reset scanner */
 | 
						|
  write_reg2 (dev->fd, REG1, 0x1110);
 | 
						|
  count = read_reg (dev->fd, REGE);
 | 
						|
  count = read_reg (dev->fd, REGE);
 | 
						|
  write_reg (dev->fd, REG0, 0x00);
 | 
						|
  write_reg (dev->fd, REG1, 0x00);
 | 
						|
  write_reg (dev->fd, REGF, 0x82);
 | 
						|
  write_reg (dev->fd, REG7, 0x00);
 | 
						|
 | 
						|
  DBG (DBG_proc, "move: exit\n");
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
/** clean up calibration data
 | 
						|
 * @param dev device to clean up
 | 
						|
 */
 | 
						|
static void
 | 
						|
cleanup_calibration (P5_Device * dev)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  for (i = 0; i < MAX_RESOLUTIONS * 2; i++)
 | 
						|
    {
 | 
						|
      if (dev->calibration_data[i] != NULL)
 | 
						|
	{
 | 
						|
	  free (dev->calibration_data[i]);
 | 
						|
	  dev->calibration_data[i] = NULL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  dev->calibrated = SANE_FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/** detect a black scan line
 | 
						|
 * parses the given buffer and retrun SANE_TRUE if the line is an
 | 
						|
 * acceptable black line for calibration
 | 
						|
 * @param buffer data line to parse
 | 
						|
 * @param pixels number of pixels
 | 
						|
 * @param mode MODE_COLOR or MODE_GRAY
 | 
						|
 * @returns SANE_TRUE if it is considered as a white line
 | 
						|
 */
 | 
						|
static SANE_Bool
 | 
						|
is_black_line (uint8_t * buffer, unsigned int pixels, int mode)
 | 
						|
{
 | 
						|
  unsigned int i, start, end, count, width;
 | 
						|
 | 
						|
  /* compute width in bytes */
 | 
						|
  if (mode == MODE_COLOR)
 | 
						|
    {
 | 
						|
      width = pixels * 3;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      width = pixels;
 | 
						|
    }
 | 
						|
 | 
						|
  /* we allow the calibration target to be narrower than full width, ie
 | 
						|
   * black margin at both ends of the line */
 | 
						|
  start = (5 * width) / 100;
 | 
						|
  end = (95 * width) / 100;
 | 
						|
  count = 0;
 | 
						|
 | 
						|
  /* count number of black bytes */
 | 
						|
  for (i = start; i < end; i++)
 | 
						|
    {
 | 
						|
      if (buffer[i] > BLACK_LEVEL)
 | 
						|
	{
 | 
						|
	  count++;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* we allow 3% black pixels maximum */
 | 
						|
  if (count > (3 * width) / 100)
 | 
						|
    {
 | 
						|
      DBG (DBG_io, "is_black_line=SANE_FALSE\n");
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (DBG_io, "is_black_line=SANE_TRUE\n");
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/** detect a white scan line
 | 
						|
 * parses the given buffer and retrun SANE_TRUE if the line is an
 | 
						|
 * acceptable white line for calibration
 | 
						|
 * @param buffer data line to parse
 | 
						|
 * @param pixels number of pixels
 | 
						|
 * @param mode MODE_COLOR or MODE_GRAY
 | 
						|
 * @returns SANE_TRUE if it is considered as a white line
 | 
						|
 */
 | 
						|
static SANE_Bool
 | 
						|
is_white_line (uint8_t * buffer, unsigned int pixels, int mode)
 | 
						|
{
 | 
						|
  unsigned int i, start, end, count, width;
 | 
						|
 | 
						|
  /* compute width in bytes */
 | 
						|
  if (mode == MODE_COLOR)
 | 
						|
    {
 | 
						|
      width = pixels * 3;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      width = pixels;
 | 
						|
    }
 | 
						|
 | 
						|
  /* we allow the calibration target to be narrower than full width, ie
 | 
						|
   * black margin at both ends of the line */
 | 
						|
  start = (5 * width) / 100;
 | 
						|
  end = (95 * width) / 100;
 | 
						|
  count = 0;
 | 
						|
 | 
						|
  /* count number of black bytes */
 | 
						|
  for (i = start; i < end; i++)
 | 
						|
    {
 | 
						|
      if (buffer[i] < BLACK_LEVEL)
 | 
						|
	{
 | 
						|
	  count++;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* we allow 3% black pixels maximum */
 | 
						|
  if (count > (3 * width) / 100)
 | 
						|
    {
 | 
						|
      DBG (DBG_io, "is_white_line=SANE_FALSE\n");
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (DBG_io, "is_white_line=SANE_TRUE\n");
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/* ------------------------------------------------------------------------- */
 | 
						|
/* writes gray data to a pnm file */
 | 
						|
/*
 | 
						|
static void
 | 
						|
write_gray_data (unsigned char *image, char *name, SANE_Int width,
 | 
						|
		 SANE_Int height)
 | 
						|
{
 | 
						|
  FILE *fdbg = NULL;
 | 
						|
 | 
						|
  fdbg = fopen (name, "wb");
 | 
						|
  if (fdbg == NULL)
 | 
						|
    return;
 | 
						|
  fprintf (fdbg, "P5\n%d %d\n255\n", width, height);
 | 
						|
  fwrite (image, width, height, fdbg);
 | 
						|
  fclose (fdbg);
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
/* ------------------------------------------------------------------------- */
 | 
						|
/* writes rgb data to a pnm file */
 | 
						|
static void
 | 
						|
write_rgb_data (char *name, unsigned char *image, SANE_Int width,
 | 
						|
		SANE_Int height)
 | 
						|
{
 | 
						|
  FILE *fdbg = NULL;
 | 
						|
 | 
						|
  fdbg = fopen (name, "wb");
 | 
						|
  if (fdbg == NULL)
 | 
						|
    return;
 | 
						|
  fprintf (fdbg, "P6\n%d %d\n255\n", width, height);
 | 
						|
  fwrite (image, width * 3, height, fdbg);
 | 
						|
  fclose (fdbg);
 | 
						|
}
 | 
						|
 | 
						|
/** give calibration file name
 | 
						|
 * computes the calibration file name to use based on the
 | 
						|
 * backend name and device
 | 
						|
 */
 | 
						|
static char *
 | 
						|
calibration_file (const char *devicename)
 | 
						|
{
 | 
						|
  char *ptr = NULL;
 | 
						|
  char tmp_str[PATH_MAX];
 | 
						|
 | 
						|
  ptr = getenv ("HOME");
 | 
						|
  if (ptr != NULL)
 | 
						|
    {
 | 
						|
      sprintf (tmp_str, "%s/.sane/p5-%s.cal", ptr, devicename);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      ptr = getenv ("TMPDIR");
 | 
						|
      if (ptr != NULL)
 | 
						|
	{
 | 
						|
	  sprintf (tmp_str, "%s/p5-%s.cal", ptr, devicename);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  sprintf (tmp_str, "/tmp/p5-%s.cal", devicename);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (DBG_trace, "calibration_file: using >%s< for calibration file name\n",
 | 
						|
       tmp_str);
 | 
						|
  return strdup (tmp_str);
 | 
						|
}
 | 
						|
 | 
						|
/** restore calibration data
 | 
						|
 * restore calibration data by loading previously saved calibration data
 | 
						|
 * @param dev device to restore
 | 
						|
 * @return SANE_STATUS_GOOD on success, otherwise error code
 | 
						|
 */
 | 
						|
static SANE_Status
 | 
						|
restore_calibration (P5_Device * dev)
 | 
						|
{
 | 
						|
  SANE_Status status = SANE_STATUS_GOOD;
 | 
						|
  char *fname = NULL;
 | 
						|
  FILE *fcalib = NULL;
 | 
						|
  size_t size;
 | 
						|
  int i;
 | 
						|
 | 
						|
  DBG (DBG_proc, "restore_calibration: start\n");
 | 
						|
  cleanup_calibration (dev);
 | 
						|
  fname = calibration_file (dev->model->name);
 | 
						|
  fcalib = fopen (fname, "rb");
 | 
						|
  if (fcalib == NULL)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "restore_calibration: failed to open %s!\n", fname);
 | 
						|
      free (fname);
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  /* loop filling calibration data until EOF reached */
 | 
						|
  i = 0;
 | 
						|
  while (!feof (fcalib) && (i < 2 * MAX_RESOLUTIONS))
 | 
						|
    {
 | 
						|
      dev->calibration_data[i] = malloc (sizeof (P5_Calibration_Data));
 | 
						|
      if (dev->calibration_data[i] == NULL)
 | 
						|
	{
 | 
						|
	  cleanup_calibration (dev);
 | 
						|
	  free (fname);
 | 
						|
	  fclose (fcalib);
 | 
						|
	  DBG (DBG_error,
 | 
						|
	       "restore_calibration: failed to allocate memory for calibration\n");
 | 
						|
	  return SANE_STATUS_NO_MEM;
 | 
						|
	}
 | 
						|
      size =
 | 
						|
	fread (dev->calibration_data[i], 1, sizeof (P5_Calibration_Data),
 | 
						|
	       fcalib);
 | 
						|
      if (feof (fcalib))
 | 
						|
	{
 | 
						|
	  free (dev->calibration_data[i]);
 | 
						|
	  dev->calibration_data[i] = NULL;
 | 
						|
	}
 | 
						|
      else if (size != sizeof (P5_Calibration_Data))
 | 
						|
	{
 | 
						|
	  cleanup_calibration (dev);
 | 
						|
	  free (fname);
 | 
						|
	  fclose (fcalib);
 | 
						|
	  DBG (DBG_error, "restore_calibration: failed to read from file\n");
 | 
						|
	  return SANE_STATUS_IO_ERROR;
 | 
						|
	}
 | 
						|
      DBG (DBG_trace,
 | 
						|
	   "restore_calibration: read 1 calibration structure from file\n");
 | 
						|
      i++;
 | 
						|
    }
 | 
						|
 | 
						|
  dev->calibrated = SANE_TRUE;
 | 
						|
  fclose (fcalib);
 | 
						|
  free (fname);
 | 
						|
 | 
						|
  DBG (DBG_proc, "restore_calibration: end\n");
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
/** save calibration data
 | 
						|
 * save calibration data from memory to file
 | 
						|
 * @param dev device calibration to save
 | 
						|
 * @return SANE_STATUS_GOOD on success, otherwise error code
 | 
						|
 */
 | 
						|
static SANE_Status
 | 
						|
save_calibration (P5_Device * dev)
 | 
						|
{
 | 
						|
  SANE_Status status = SANE_STATUS_GOOD;
 | 
						|
  char *fname = NULL;
 | 
						|
  FILE *fcalib = NULL;
 | 
						|
  int i;
 | 
						|
  size_t size;
 | 
						|
 | 
						|
  DBG (DBG_proc, "save_calibration: start\n");
 | 
						|
  fname = calibration_file (dev->model->name);
 | 
						|
  fcalib = fopen (fname, "wb");
 | 
						|
  if (fcalib == NULL)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "save_calibration: failed to open %s!\n", fname);
 | 
						|
      free (fname);
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  /* loop filling calibration data until EOF reached */
 | 
						|
  i = 0;
 | 
						|
  while (dev->calibration_data[i] != NULL && (i < 2 * MAX_RESOLUTIONS))
 | 
						|
    {
 | 
						|
      size =
 | 
						|
	fwrite (dev->calibration_data[i], sizeof (P5_Calibration_Data), 1,
 | 
						|
		fcalib);
 | 
						|
      if (size != sizeof (P5_Calibration_Data))
 | 
						|
	{
 | 
						|
	  free (fname);
 | 
						|
	  fclose (fcalib);
 | 
						|
	  DBG (DBG_error, "save_calibration: failed to write to file\n");
 | 
						|
	  return SANE_STATUS_IO_ERROR;
 | 
						|
	}
 | 
						|
      DBG (DBG_trace,
 | 
						|
	   "save_calibration: wrote 1 calibration structure to file\n");
 | 
						|
      i++;
 | 
						|
    }
 | 
						|
 | 
						|
  fclose (fcalib);
 | 
						|
  free (fname);
 | 
						|
 | 
						|
  DBG (DBG_proc, "save_calibration: end\n");
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
/** calibrate scanner
 | 
						|
 * calibrates scanner by scanning a white sheet to get
 | 
						|
 * reference data. The black reference data is extracted from the lines
 | 
						|
 * that precede the physical document.
 | 
						|
 * Calibration is done at 300 color, then data is built for other modes
 | 
						|
 * and resolutions.
 | 
						|
 * @param dev device to calibrate
 | 
						|
 */
 | 
						|
static SANE_Status
 | 
						|
sheetfed_calibration (P5_Device * dev)
 | 
						|
{
 | 
						|
  uint8_t buffer[MAX_SENSOR_PIXELS * 3];
 | 
						|
  uint16_t white_data[MAX_SENSOR_PIXELS * 3];
 | 
						|
  uint16_t black_data[MAX_SENSOR_PIXELS * 3];
 | 
						|
  unsigned int i, j, k, dpi, pixels, read, black, white;
 | 
						|
  float coeff;
 | 
						|
  unsigned int red, green, blue;
 | 
						|
  int line;
 | 
						|
  SANE_Status status;
 | 
						|
  char title[40];
 | 
						|
 | 
						|
  FILE *dbg = fopen ("debug.pnm", "wb");
 | 
						|
  fprintf (dbg, "P6\n%d %d\n255\n", MAX_SENSOR_PIXELS,
 | 
						|
	   CALIBRATION_SKIP_LINES * 4);
 | 
						|
 | 
						|
  DBG (DBG_proc, "sheetfed_calibration: start\n");
 | 
						|
 | 
						|
  /* check calibration target has been loaded in ADF */
 | 
						|
  status = test_document (dev->fd);
 | 
						|
  if (status == SANE_STATUS_NO_DOCS)
 | 
						|
    {
 | 
						|
      DBG (DBG_error,
 | 
						|
	   "sheetfed_calibration: no calibration target present!\n");
 | 
						|
      return SANE_STATUS_NO_DOCS;
 | 
						|
    }
 | 
						|
 | 
						|
  /* clean up calibration data */
 | 
						|
  cleanup_calibration (dev);
 | 
						|
 | 
						|
  /*  a RGB scan to get reference data */
 | 
						|
  /* initialize calibration slot for the resolution */
 | 
						|
  i = 0;
 | 
						|
  dpi = dev->model->max_xdpi;
 | 
						|
  pixels = MAX_SENSOR_PIXELS;
 | 
						|
  dev->calibration_data[i] =
 | 
						|
    (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
 | 
						|
  if (dev->calibration_data[i] == NULL)
 | 
						|
    {
 | 
						|
      cleanup_calibration (dev);
 | 
						|
      DBG (DBG_error,
 | 
						|
	   "sheetfed_calibration: failed to allocate memory for calibration\n");
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  dev->calibration_data[i]->dpi = dpi;
 | 
						|
 | 
						|
  /* start scan */
 | 
						|
  status = start_scan (dev, MODE_COLOR, dpi, 0, pixels);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      cleanup_calibration (dev);
 | 
						|
      DBG (DBG_error,
 | 
						|
	   "sheetfed_calibration: failed to start scan at %d dpi\n", dpi);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  white = 0;
 | 
						|
  black = 0;
 | 
						|
  read = 0;
 | 
						|
  for (j = 0; j < pixels * 3; j++)
 | 
						|
    {
 | 
						|
      black_data[j] = 0;
 | 
						|
      white_data[j] = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  /* read lines and gather black and white ones until enough for sensor's
 | 
						|
   * native resolution */
 | 
						|
  do
 | 
						|
    {
 | 
						|
      status = test_document (dev->fd);
 | 
						|
      if (status == SANE_STATUS_NO_DOCS && (white < 10 || black < 10))
 | 
						|
	{
 | 
						|
	  cleanup_calibration (dev);
 | 
						|
	  DBG (DBG_error,
 | 
						|
	       "sheetfed_calibration: calibration sheet too short!\n");
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
      memset (buffer, 0x00, MAX_SENSOR_PIXELS * 3);
 | 
						|
      line =
 | 
						|
	read_line (dev, buffer, pixels * 3, 1, SANE_FALSE, SANE_FALSE,
 | 
						|
		   MODE_COLOR, SANE_FALSE);
 | 
						|
      if (line == -1)
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "sheetfed_calibration: failed to read data\n");
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
 | 
						|
      /* if a data line has been read, add it to reference data */
 | 
						|
      if (line)
 | 
						|
	{
 | 
						|
	  read++;
 | 
						|
	  fwrite (buffer, pixels * 3, 1, dbg);
 | 
						|
	  if (is_white_line (buffer, pixels, MODE_COLOR) && white < 256)
 | 
						|
	    {
 | 
						|
	      white++;
 | 
						|
	      /* first calibration lines are skipped */
 | 
						|
	      for (j = 0; j < pixels * 3 && read > CALIBRATION_SKIP_LINES;
 | 
						|
		   j++)
 | 
						|
		{
 | 
						|
		  white_data[j] += buffer[j];
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  if (is_black_line (buffer, pixels, MODE_COLOR) && black < 256)
 | 
						|
	    {
 | 
						|
	      black++;
 | 
						|
	      for (j = 0; j < pixels * 3; j++)
 | 
						|
		{
 | 
						|
		  black_data[j] += buffer[j];
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  while (test_document (dev->fd) != SANE_STATUS_NO_DOCS);
 | 
						|
  DBG (DBG_trace, "sheetfed_calibration: white lines=%d, black lines=%d\n",
 | 
						|
       white, black);
 | 
						|
 | 
						|
  /* average pixels and store in per dpi calibration data */
 | 
						|
  for (j = 0; j < pixels * 3; j++)
 | 
						|
    {
 | 
						|
      dev->calibration_data[i]->white_data[j] = white_data[j] / white;
 | 
						|
      dev->calibration_data[i]->black_data[j] = black_data[j] / black;
 | 
						|
    }
 | 
						|
 | 
						|
  /* we average red, green and blue offset on the full sensor */
 | 
						|
  red = 0;
 | 
						|
  green = 0;
 | 
						|
  blue = 0;
 | 
						|
  for (j = 0; j < pixels * 3; j += 3)
 | 
						|
    {
 | 
						|
      red += dev->calibration_data[i]->black_data[j];
 | 
						|
      green += dev->calibration_data[i]->black_data[j + 1];
 | 
						|
      blue += dev->calibration_data[i]->black_data[j + 2];
 | 
						|
    }
 | 
						|
  for (j = 0; j < pixels * 3; j += 3)
 | 
						|
    {
 | 
						|
      dev->calibration_data[i]->black_data[j] = red / pixels;
 | 
						|
      dev->calibration_data[i]->black_data[j + 1] = green / pixels;
 | 
						|
      dev->calibration_data[i]->black_data[j + 2] = blue / pixels;
 | 
						|
    }
 | 
						|
 | 
						|
  /* trace calibration data for debug */
 | 
						|
  if (DBG_LEVEL > DBG_data)
 | 
						|
    {
 | 
						|
      sprintf (title, "calibration-white-%d.pnm",
 | 
						|
	       dev->calibration_data[i]->dpi);
 | 
						|
      write_rgb_data (title, dev->calibration_data[i]->white_data, pixels, 1);
 | 
						|
      sprintf (title, "calibration-black-%d.pnm",
 | 
						|
	       dev->calibration_data[i]->dpi);
 | 
						|
      write_rgb_data (title, dev->calibration_data[i]->black_data, pixels, 1);
 | 
						|
    }
 | 
						|
 | 
						|
  /* loop on all remaining resolution and compute calibration data from it */
 | 
						|
  for (i = 1; i < MAX_RESOLUTIONS && dev->model->xdpi_values[i] > 0; i++)
 | 
						|
    {
 | 
						|
      dev->calibration_data[i] =
 | 
						|
	(P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
 | 
						|
      if (dev->calibration_data[i] == NULL)
 | 
						|
	{
 | 
						|
	  cleanup_calibration (dev);
 | 
						|
	  DBG (DBG_error,
 | 
						|
	       "sheetfed_calibration: failed to allocate memory for calibration\n");
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
      dev->calibration_data[i]->dpi = dev->model->xdpi_values[i];
 | 
						|
 | 
						|
      coeff = ((float) dev->model->xdpi_values[i]) / (float) dpi;
 | 
						|
 | 
						|
      /* generate data by decimation */
 | 
						|
      for (j = 0; j < pixels / coeff; j++)
 | 
						|
	{
 | 
						|
	  k = j * coeff;
 | 
						|
	  dev->calibration_data[i]->white_data[j] =
 | 
						|
	    dev->calibration_data[0]->white_data[k];
 | 
						|
	  dev->calibration_data[i]->white_data[j + 1] =
 | 
						|
	    dev->calibration_data[0]->white_data[k + 1];
 | 
						|
	  dev->calibration_data[i]->white_data[j + 2] =
 | 
						|
	    dev->calibration_data[0]->white_data[k + 2];
 | 
						|
	  dev->calibration_data[i]->black_data[j] =
 | 
						|
	    dev->calibration_data[0]->black_data[k];
 | 
						|
	  dev->calibration_data[i]->black_data[j + 1] =
 | 
						|
	    dev->calibration_data[0]->black_data[k + 1];
 | 
						|
	  dev->calibration_data[i]->black_data[j + 2] =
 | 
						|
	    dev->calibration_data[0]->black_data[k + 2];
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  fclose (dbg);
 | 
						|
  dev->calibrated = SANE_TRUE;
 | 
						|
 | 
						|
  /* eject calibration target */
 | 
						|
  eject (dev->fd);
 | 
						|
 | 
						|
  DBG (DBG_proc, "sheetfed_calibration: end\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
 |