kopia lustrzana https://gitlab.com/sane-project/backends
2062 wiersze
43 KiB
C
2062 wiersze
43 KiB
C
/* sane - Scanner Access Now Easy.
|
|
Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
|
|
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.
|
|
|
|
This file implements the hardware driver scanners using a 300dpi CCD */
|
|
|
|
#include "mustek_pp_ccd300.h"
|
|
|
|
#define MUSTEK_PP_CCD300 4
|
|
|
|
static void config_ccd_101x (Mustek_pp_Handle * dev);
|
|
static void config_ccd (Mustek_pp_Handle * dev);
|
|
static void lamp (Mustek_pp_Handle * dev, int lamp_on);
|
|
|
|
#define CCD300_ASIC1013 0xa8
|
|
#define CCD300_ASIC1015 0xa5
|
|
|
|
#define CCD300_CHANNEL_RED 0
|
|
#define CCD300_CHANNEL_GREEN 1
|
|
#define CCD300_CHANNEL_BLUE 2
|
|
#define CCD300_CHANNEL_GRAY 1
|
|
|
|
#define CCD300_MAXHSIZE 2600
|
|
#define CCD300_MAXVSIZE 3500
|
|
|
|
/*
|
|
* Here starts the driver code for the different chipsets
|
|
*
|
|
* The 1013 & 1015 chipsets share large portions of the code. This
|
|
* shared functions end with _101x.
|
|
*/
|
|
|
|
static const u_char chan_codes_1013[] = { 0x82, 0x42, 0xC2 };
|
|
static const u_char chan_codes_1015[] = { 0x80, 0x40, 0xC0 };
|
|
static const u_char fullstep[] = { 0x09, 0x0C, 0x06, 0x03 };
|
|
static const u_char halfstep[] = { 0x02, 0x03, 0x01, 0x09,
|
|
0x08, 0x0C, 0x04, 0x06
|
|
};
|
|
static const u_char voltages[4][3] = { {0x5C, 0x5A, 0x63},
|
|
{0xE6, 0xB4, 0xBE},
|
|
{0xB4, 0xB4, 0xB4},
|
|
{0x64, 0x50, 0x64}
|
|
};
|
|
|
|
/* Forward declarations of 1013/1015 functions */
|
|
static void set_ccd_channel_1013 (Mustek_pp_Handle * dev, int channel);
|
|
static void motor_backward_1013 (Mustek_pp_Handle * dev);
|
|
static void return_home_1013 (Mustek_pp_Handle * dev);
|
|
static void motor_forward_1013 (Mustek_pp_Handle * dev);
|
|
static void config_ccd_1013 (Mustek_pp_Handle * dev);
|
|
|
|
static void set_ccd_channel_1015 (Mustek_pp_Handle * dev, int channel);
|
|
/* static void motor_backward_1015 (Mustek_pp_Handle * dev); */
|
|
static void return_home_1015 (Mustek_pp_Handle * dev, SANE_Bool nowait);
|
|
static void motor_forward_1015 (Mustek_pp_Handle * dev);
|
|
static void config_ccd_1015 (Mustek_pp_Handle * dev);
|
|
|
|
|
|
/* These functions are common to all 1013/1015 chipsets */
|
|
|
|
static void
|
|
set_led (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6,
|
|
(priv->motor_step % 5 == 0 ? 0x03 : 0x13));
|
|
|
|
}
|
|
|
|
static void
|
|
set_sti (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 3, 0);
|
|
priv->bank_count++;
|
|
priv->bank_count &= 7;
|
|
|
|
}
|
|
|
|
static void
|
|
get_bank_count (Mustek_pp_Handle * dev)
|
|
{
|
|
u_char val;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
sanei_pa4s2_readbegin (dev->fd, 3);
|
|
sanei_pa4s2_readbyte (dev->fd, &val);
|
|
sanei_pa4s2_readend (dev->fd);
|
|
|
|
priv->bank_count = (val & 0x07);
|
|
|
|
}
|
|
|
|
static void
|
|
reset_bank_count (Mustek_pp_Handle * dev)
|
|
{
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 7);
|
|
}
|
|
|
|
static void
|
|
wait_bank_change (Mustek_pp_Handle * dev, int bankcount, int niceload)
|
|
{
|
|
struct timeval start, end;
|
|
unsigned long diff;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
int first_time = 1;
|
|
|
|
gettimeofday (&start, NULL);
|
|
|
|
do
|
|
{
|
|
if ((niceload == 0) && (first_time == 0))
|
|
{
|
|
usleep (1); /* could be as well sched_yield */
|
|
first_time = 0;
|
|
}
|
|
get_bank_count (dev);
|
|
|
|
gettimeofday (&end, NULL);
|
|
diff = (end.tv_sec * 1000 + end.tv_usec / 1000) -
|
|
(start.tv_sec * 1000 + start.tv_usec / 1000);
|
|
|
|
}
|
|
while ((priv->bank_count != bankcount) && (diff < priv->wait_bank));
|
|
|
|
}
|
|
|
|
static void
|
|
set_dpi_value (Mustek_pp_Handle * dev)
|
|
{
|
|
u_char val = 0;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x80);
|
|
|
|
switch (priv->hwres)
|
|
{
|
|
case 100:
|
|
val = 0x00;
|
|
break;
|
|
case 200:
|
|
val = 0x10;
|
|
break;
|
|
case 300:
|
|
val = 0x20;
|
|
break;
|
|
}
|
|
|
|
|
|
if (priv->ccd_type == 1)
|
|
val |= 0x01;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 5, val);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x00);
|
|
|
|
DBG (5, "set_dpi_value: value 0x%02x\n", val);
|
|
|
|
}
|
|
|
|
static void
|
|
set_line_adjust (Mustek_pp_Handle * dev)
|
|
{
|
|
int adjustline;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
adjustline = (dev->bottomX - dev->topX) * priv->hwres / 300;
|
|
priv->adjustskip = priv->adjustskip * priv->hwres / 300;
|
|
|
|
DBG (5, "set_line_adjust: ppl %u (%u), adjust %u, skip %u\n",
|
|
dev->params.pixels_per_line, (dev->bottomX - dev->topX), adjustline,
|
|
priv->adjustskip);
|
|
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x11);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, (adjustline + priv->adjustskip) >> 8);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x21);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, (adjustline + priv->adjustskip) & 0xFF);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x01);
|
|
|
|
}
|
|
|
|
static void
|
|
set_lamp (Mustek_pp_Handle * dev, int lamp_on)
|
|
{
|
|
|
|
int ctr;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0xC3);
|
|
|
|
for (ctr = 0; ctr < 3; ctr++)
|
|
{
|
|
sanei_pa4s2_writebyte (dev->fd, 6, (lamp_on ? 0x47 : 0x57));
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x77);
|
|
}
|
|
|
|
priv->motor_step = lamp_on;
|
|
|
|
set_led (dev);
|
|
|
|
}
|
|
|
|
static void
|
|
send_voltages (Mustek_pp_Handle * dev)
|
|
{
|
|
|
|
int voltage, sel = 8, ctr;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
switch (priv->ccd_type)
|
|
{
|
|
case 0:
|
|
voltage = 0;
|
|
break;
|
|
case 1:
|
|
voltage = 1;
|
|
break;
|
|
default:
|
|
voltage = 2;
|
|
break;
|
|
}
|
|
|
|
for (ctr = 0; ctr < 3; ctr++)
|
|
{
|
|
|
|
sel <<= 1;
|
|
sanei_pa4s2_writebyte (dev->fd, 6, sel);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, voltages[voltage][ctr]);
|
|
|
|
}
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x00);
|
|
|
|
}
|
|
|
|
static int
|
|
compar (const void *a, const void *b)
|
|
{
|
|
return (signed int) (*(const SANE_Byte *) a) -
|
|
(signed int) (*(const SANE_Byte *) b);
|
|
}
|
|
|
|
static void
|
|
set_ccd_channel_101x (Mustek_pp_Handle * dev, int channel)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
switch (priv->asic)
|
|
{
|
|
case CCD300_ASIC1013:
|
|
set_ccd_channel_1013 (dev, channel);
|
|
break;
|
|
|
|
case CCD300_ASIC1015:
|
|
set_ccd_channel_1015 (dev, channel);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
motor_forward_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
switch (priv->asic)
|
|
{
|
|
case CCD300_ASIC1013:
|
|
motor_forward_1013 (dev);
|
|
break;
|
|
|
|
case CCD300_ASIC1015:
|
|
motor_forward_1015 (dev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
motor_backward_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
switch (priv->asic)
|
|
{
|
|
case CCD300_ASIC1013:
|
|
motor_backward_1013 (dev);
|
|
break;
|
|
|
|
case CCD300_ASIC1015:
|
|
/* motor_backward_1015 (dev); */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
move_motor_101x (Mustek_pp_Handle * dev, int forward)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
if (forward == SANE_TRUE)
|
|
motor_forward_101x (dev);
|
|
else
|
|
motor_backward_101x (dev);
|
|
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
reset_bank_count (dev);
|
|
}
|
|
|
|
|
|
static void
|
|
config_ccd_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
switch (priv->asic)
|
|
{
|
|
case CCD300_ASIC1013:
|
|
config_ccd_1013 (dev);
|
|
break;
|
|
|
|
case CCD300_ASIC1015:
|
|
config_ccd_1015 (dev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
read_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf, SANE_Int pixel,
|
|
SANE_Int RefBlack, SANE_Byte * calib, SANE_Int * gamma)
|
|
{
|
|
|
|
SANE_Byte *cal = calib;
|
|
u_char color;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
int ctr, skips = priv->adjustskip + 1, cval;
|
|
|
|
if (pixel <= 0)
|
|
return;
|
|
|
|
sanei_pa4s2_readbegin (dev->fd, 1);
|
|
|
|
|
|
if (priv->hwres == dev->res)
|
|
{
|
|
|
|
while (skips--)
|
|
sanei_pa4s2_readbyte (dev->fd, &color);
|
|
|
|
for (ctr = 0; ctr < pixel; ctr++)
|
|
{
|
|
|
|
sanei_pa4s2_readbyte (dev->fd, &color);
|
|
|
|
cval = color;
|
|
|
|
if (cval < RefBlack)
|
|
cval = 0;
|
|
else
|
|
cval -= RefBlack;
|
|
|
|
if (cal)
|
|
{
|
|
if (cval >= cal[ctr])
|
|
cval = 0xFF;
|
|
else
|
|
{
|
|
cval <<= 8;
|
|
cval /= (int) cal[ctr];
|
|
}
|
|
}
|
|
|
|
if (gamma)
|
|
cval = gamma[cval];
|
|
|
|
buf[ctr] = cval;
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
int pos = 0, bpos = 0;
|
|
|
|
while (skips--)
|
|
sanei_pa4s2_readbyte (dev->fd, &color);
|
|
|
|
ctr = 0;
|
|
|
|
do
|
|
{
|
|
|
|
sanei_pa4s2_readbyte (dev->fd, &color);
|
|
|
|
cval = color;
|
|
|
|
if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT))
|
|
{
|
|
ctr++;
|
|
continue;
|
|
}
|
|
|
|
ctr++;
|
|
pos += priv->res_step;
|
|
|
|
|
|
if (cval < RefBlack)
|
|
cval = 0;
|
|
else
|
|
cval -= RefBlack;
|
|
|
|
if (cal)
|
|
{
|
|
if (cval >= cal[bpos])
|
|
cval = 0xFF;
|
|
else
|
|
{
|
|
cval <<= 8;
|
|
cval /= (int) cal[bpos];
|
|
}
|
|
}
|
|
|
|
if (gamma)
|
|
cval = gamma[cval];
|
|
|
|
buf[bpos++] = cval;
|
|
|
|
}
|
|
while (bpos < pixel);
|
|
|
|
}
|
|
|
|
sanei_pa4s2_readend (dev->fd);
|
|
|
|
}
|
|
|
|
static void
|
|
read_average_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf, int pixel,
|
|
int RefBlack)
|
|
{
|
|
|
|
SANE_Byte lbuf[4][CCD300_MAXHSIZE * 2];
|
|
int ctr, sum;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
for (ctr = 0; ctr < 4; ctr++)
|
|
{
|
|
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
read_line_101x (dev, lbuf[ctr], pixel, RefBlack, NULL, NULL);
|
|
reset_bank_count (dev);
|
|
if (ctr < 3)
|
|
set_sti (dev);
|
|
|
|
}
|
|
|
|
for (ctr = 0; ctr < pixel; ctr++)
|
|
{
|
|
|
|
sum = lbuf[0][ctr] + lbuf[1][ctr] + lbuf[2][ctr] + lbuf[3][ctr];
|
|
|
|
buf[ctr] = (sum / 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
find_black_side_edge_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
SANE_Byte buf[CCD300_MAXHSIZE * 2];
|
|
SANE_Byte blackposition[5];
|
|
int pos = 0, ctr, blackpos;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
|
|
for (ctr = 0; ctr < 20; ctr++)
|
|
{
|
|
|
|
motor_forward_101x (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
|
|
reset_bank_count (dev);
|
|
|
|
priv->ref_black = priv->ref_red = priv->ref_green = priv->ref_blue =
|
|
buf[0];
|
|
|
|
blackpos = CCD300_MAXHSIZE / 4;
|
|
|
|
while ((abs (buf[blackpos] - buf[0]) >= 15) && (blackpos > 0))
|
|
blackpos--;
|
|
|
|
if (blackpos > 1)
|
|
blackposition[pos++] = blackpos;
|
|
|
|
if (pos == 5)
|
|
break;
|
|
|
|
}
|
|
|
|
blackpos = 0;
|
|
|
|
for (ctr = 0; ctr < pos; ctr++)
|
|
if (blackposition[ctr] > blackpos)
|
|
blackpos = blackposition[ctr];
|
|
|
|
if (blackpos < 0x66)
|
|
blackpos = 0x6A;
|
|
|
|
priv->blackpos = blackpos;
|
|
priv->saved_skipcount = (blackpos + 12) & 0xFF;
|
|
|
|
}
|
|
|
|
static void
|
|
min_color_levels_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
|
|
SANE_Byte buf[CCD300_MAXHSIZE * 2];
|
|
int ctr, sum = 0;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
for (ctr = 0; ctr < 8; ctr++)
|
|
{
|
|
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_RED);
|
|
set_sti (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
|
|
read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
sum += buf[3];
|
|
|
|
}
|
|
|
|
priv->ref_red = sum / 8;
|
|
|
|
sum = 0;
|
|
|
|
for (ctr = 0; ctr < 8; ctr++)
|
|
{
|
|
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN);
|
|
set_sti (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
|
|
read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
sum += buf[3];
|
|
|
|
}
|
|
|
|
priv->ref_green = sum / 8;
|
|
|
|
sum = 0;
|
|
|
|
for (ctr = 0; ctr < 8; ctr++)
|
|
{
|
|
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE);
|
|
set_sti (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
|
|
read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
sum += buf[3];
|
|
|
|
}
|
|
|
|
priv->ref_blue = sum / 8;
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
max_color_levels_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
|
|
int ctr, line, sum;
|
|
SANE_Byte rbuf[32][CCD300_MAXHSIZE * 2];
|
|
SANE_Byte gbuf[32][CCD300_MAXHSIZE * 2];
|
|
SANE_Byte bbuf[32][CCD300_MAXHSIZE * 2];
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
SANE_Byte maxbuf[32];
|
|
|
|
for (ctr = 0; ctr < 32; ctr++)
|
|
{
|
|
|
|
if (dev->mode == MODE_COLOR)
|
|
{
|
|
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_RED);
|
|
motor_forward_101x (dev);
|
|
|
|
read_average_line_101x (dev, rbuf[ctr], dev->params.pixels_per_line,
|
|
priv->ref_red);
|
|
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN);
|
|
set_sti (dev);
|
|
|
|
read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line,
|
|
priv->ref_green);
|
|
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE);
|
|
set_sti (dev);
|
|
|
|
read_average_line_101x (dev, bbuf[ctr], dev->params.pixels_per_line,
|
|
priv->ref_blue);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
priv->channel = CCD300_CHANNEL_GRAY;
|
|
|
|
motor_forward_101x (dev);
|
|
|
|
read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line,
|
|
priv->ref_black);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
|
|
{
|
|
for (line = 0; line < 32; line++)
|
|
maxbuf[line] = gbuf[line][ctr];
|
|
|
|
qsort (maxbuf, 32, sizeof (maxbuf[0]), compar);
|
|
|
|
sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7];
|
|
|
|
priv->calib_g[ctr] = sum / 4;
|
|
|
|
}
|
|
|
|
if (dev->mode == MODE_COLOR)
|
|
{
|
|
|
|
for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
|
|
{
|
|
for (line = 0; line < 32; line++)
|
|
maxbuf[line] = rbuf[line][ctr];
|
|
|
|
qsort (maxbuf, 32, sizeof (maxbuf[0]), compar);
|
|
|
|
sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7];
|
|
|
|
priv->calib_r[ctr] = sum / 4;
|
|
|
|
}
|
|
|
|
for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
|
|
{
|
|
for (line = 0; line < 32; line++)
|
|
maxbuf[line] = bbuf[line][ctr];
|
|
|
|
qsort (maxbuf, 32, sizeof (maxbuf[0]), compar);
|
|
|
|
sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7];
|
|
|
|
priv->calib_b[ctr] = sum / 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
find_black_top_edge_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
|
|
int lines = 0, ctr, pos;
|
|
SANE_Byte buf[CCD300_MAXHSIZE * 2];
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->channel = CCD300_CHANNEL_GRAY;
|
|
|
|
do
|
|
{
|
|
|
|
motor_forward_101x (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
|
|
read_line_101x (dev, buf, CCD300_MAXHSIZE, priv->ref_black, NULL, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
pos = 0;
|
|
|
|
for (ctr = priv->blackpos; ctr > priv->blackpos - 10; ctr--)
|
|
if (buf[ctr] <= 15)
|
|
pos++;
|
|
|
|
}
|
|
while ((pos >= 8) && (lines++ < 67));
|
|
|
|
}
|
|
|
|
static void
|
|
calibrate_device_101x (Mustek_pp_Handle * dev)
|
|
{
|
|
|
|
int saved_ppl = dev->params.pixels_per_line, ctr;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->saved_mode = dev->mode;
|
|
priv->saved_invert = dev->invert;
|
|
priv->saved_skipcount = priv->skipcount;
|
|
priv->saved_skipimagebyte = priv->skipimagebytes;
|
|
priv->saved_adjustskip = priv->adjustskip;
|
|
priv->saved_res = dev->res;
|
|
priv->saved_hwres = priv->hwres;
|
|
priv->saved_res_step = priv->res_step;
|
|
priv->saved_line_step = priv->line_step;
|
|
priv->saved_channel = priv->channel;
|
|
|
|
dev->params.pixels_per_line = CCD300_MAXHSIZE;
|
|
priv->hwres = dev->res = 300;
|
|
dev->mode = MODE_GRAYSCALE;
|
|
priv->skipcount = priv->skipimagebytes = 0;
|
|
dev->invert = SANE_FALSE;
|
|
priv->channel = CCD300_CHANNEL_GRAY;
|
|
|
|
config_ccd_101x (dev);
|
|
get_bank_count (dev);
|
|
|
|
find_black_side_edge_101x (dev);
|
|
|
|
for (ctr = 0; ctr < 4; ctr++)
|
|
move_motor_101x (dev, SANE_TRUE);
|
|
|
|
dev->mode = priv->saved_mode;
|
|
dev->invert = priv->saved_invert;
|
|
priv->skipcount = priv->saved_skipcount;
|
|
priv->skipimagebytes = priv->saved_skipimagebyte;
|
|
priv->adjustskip = priv->saved_adjustskip;
|
|
dev->res = priv->saved_res;
|
|
priv->hwres = priv->saved_hwres;
|
|
priv->res_step = priv->saved_res_step;
|
|
priv->line_step = priv->saved_line_step;
|
|
priv->channel = priv->saved_channel;
|
|
|
|
priv->hwres = dev->res = 300;
|
|
priv->skipcount = priv->skipimagebytes = 0;
|
|
dev->invert = SANE_FALSE;
|
|
|
|
config_ccd_101x (dev);
|
|
get_bank_count (dev);
|
|
|
|
if ((dev->mode == MODE_COLOR) && (priv->ccd_type != 0))
|
|
min_color_levels_101x (dev);
|
|
|
|
dev->mode = priv->saved_mode;
|
|
dev->invert = priv->saved_invert;
|
|
priv->skipcount = priv->saved_skipcount;
|
|
priv->skipimagebytes = priv->saved_skipimagebyte;
|
|
priv->adjustskip = priv->saved_adjustskip;
|
|
dev->res = priv->saved_res;
|
|
priv->hwres = priv->saved_hwres;
|
|
priv->res_step = priv->saved_res_step;
|
|
priv->line_step = priv->saved_line_step;
|
|
priv->channel = priv->saved_channel;
|
|
|
|
dev->params.pixels_per_line = saved_ppl;
|
|
dev->invert = SANE_FALSE;
|
|
|
|
config_ccd_101x (dev);
|
|
get_bank_count (dev);
|
|
|
|
max_color_levels_101x (dev);
|
|
|
|
dev->params.pixels_per_line = CCD300_MAXHSIZE;
|
|
dev->mode = MODE_GRAYSCALE;
|
|
priv->hwres = dev->res = 300;
|
|
priv->skipcount = priv->skipimagebytes = 0;
|
|
dev->invert = SANE_FALSE;
|
|
|
|
config_ccd_101x (dev);
|
|
get_bank_count (dev);
|
|
|
|
find_black_top_edge_101x (dev);
|
|
|
|
dev->mode = priv->saved_mode;
|
|
dev->invert = priv->saved_invert;
|
|
priv->skipcount = priv->saved_skipcount;
|
|
priv->skipimagebytes = priv->saved_skipimagebyte;
|
|
priv->adjustskip = priv->saved_adjustskip;
|
|
dev->res = priv->saved_res;
|
|
priv->hwres = priv->saved_hwres;
|
|
priv->res_step = priv->saved_res_step;
|
|
priv->line_step = priv->saved_line_step;
|
|
priv->channel = priv->saved_channel;
|
|
|
|
dev->params.pixels_per_line = saved_ppl;
|
|
|
|
config_ccd_101x (dev);
|
|
get_bank_count (dev);
|
|
|
|
}
|
|
|
|
static void
|
|
get_grayscale_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf)
|
|
{
|
|
|
|
int skips;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->line_diff += SANE_FIX (300.0 / (float) dev->res);
|
|
|
|
skips = (priv->line_diff >> SANE_FIXED_SCALE_SHIFT);
|
|
|
|
while (--skips)
|
|
{
|
|
motor_forward_101x (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
reset_bank_count (dev);
|
|
}
|
|
|
|
priv->line_diff &= 0xFFFF;
|
|
|
|
motor_forward_101x (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
|
|
read_line_101x (dev, buf, dev->params.pixels_per_line, priv->ref_black,
|
|
priv->calib_g, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
}
|
|
|
|
static void
|
|
get_lineart_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf)
|
|
{
|
|
|
|
int ctr;
|
|
SANE_Byte gbuf[CCD300_MAXHSIZE * 2];
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
get_grayscale_line_101x (dev, gbuf);
|
|
|
|
memset (buf, 0xFF, dev->params.bytes_per_line);
|
|
|
|
for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
|
|
buf[ctr >> 3] ^= ((gbuf[ctr] > priv->bw) ? (1 << (7 - ctr % 8)) : 0);
|
|
|
|
}
|
|
|
|
static void
|
|
get_color_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf)
|
|
{
|
|
|
|
SANE_Byte *red, *blue, *src, *dest;
|
|
int gotline = 0, ctr;
|
|
int gored, goblue, gogreen;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
int step = priv->line_step;
|
|
|
|
do
|
|
{
|
|
|
|
red = priv->red[priv->redline];
|
|
blue = priv->blue[priv->blueline];
|
|
|
|
priv->ccd_line++;
|
|
|
|
if ((priv->rdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line)
|
|
{
|
|
gored = 1;
|
|
priv->rdiff += step;
|
|
}
|
|
else
|
|
gored = 0;
|
|
|
|
if ((priv->bdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line)
|
|
{
|
|
goblue = 1;
|
|
priv->bdiff += step;
|
|
}
|
|
else
|
|
goblue = 0;
|
|
|
|
if ((priv->gdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line)
|
|
{
|
|
gogreen = 1;
|
|
priv->gdiff += step;
|
|
}
|
|
else
|
|
gogreen = 0;
|
|
|
|
if (!gored && !goblue && !gogreen)
|
|
{
|
|
motor_forward_101x (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
reset_bank_count (dev);
|
|
if (priv->ccd_line >= (priv->line_step >> SANE_FIXED_SCALE_SHIFT))
|
|
priv->redline = ++priv->redline % priv->green_offs;
|
|
if (priv->ccd_line >=
|
|
priv->blue_offs + (priv->line_step >> SANE_FIXED_SCALE_SHIFT))
|
|
priv->blueline = ++priv->blueline % priv->blue_offs;
|
|
continue;
|
|
}
|
|
|
|
if (gored)
|
|
priv->channel = CCD300_CHANNEL_RED;
|
|
else if (goblue)
|
|
priv->channel = CCD300_CHANNEL_BLUE;
|
|
else
|
|
priv->channel = CCD300_CHANNEL_GREEN;
|
|
|
|
motor_forward_101x (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
|
|
if (priv->ccd_line >= priv->green_offs && gogreen)
|
|
{
|
|
src = red;
|
|
dest = buf;
|
|
|
|
for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
|
|
{
|
|
*dest = *src++;
|
|
dest += 3;
|
|
}
|
|
}
|
|
|
|
if (gored)
|
|
{
|
|
|
|
read_line_101x (dev, red, dev->params.pixels_per_line,
|
|
priv->ref_red, priv->calib_r, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
}
|
|
|
|
priv->redline = ++priv->redline % priv->green_offs;
|
|
|
|
if (priv->ccd_line >= priv->green_offs && gogreen)
|
|
{
|
|
src = blue;
|
|
dest = buf + 2;
|
|
|
|
|
|
for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
|
|
{
|
|
*dest = *src++;
|
|
dest += 3;
|
|
}
|
|
|
|
}
|
|
|
|
if (goblue)
|
|
{
|
|
if (gored)
|
|
{
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE);
|
|
set_sti (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
}
|
|
|
|
read_line_101x (dev, blue, dev->params.pixels_per_line,
|
|
priv->ref_blue, priv->calib_b, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
}
|
|
|
|
if (priv->ccd_line >=
|
|
priv->blue_offs + (priv->line_step >> SANE_FIXED_SCALE_SHIFT))
|
|
priv->blueline = ++priv->blueline % priv->blue_offs;
|
|
|
|
if (gogreen)
|
|
{
|
|
|
|
if (gored || goblue)
|
|
{
|
|
set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN);
|
|
set_sti (dev);
|
|
wait_bank_change (dev, priv->bank_count, 1);
|
|
}
|
|
|
|
read_line_101x (dev, priv->green, dev->params.pixels_per_line,
|
|
priv->ref_green, priv->calib_g, NULL);
|
|
|
|
reset_bank_count (dev);
|
|
|
|
src = priv->green;
|
|
dest = buf + 1;
|
|
|
|
for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
|
|
{
|
|
*dest = *src++;
|
|
dest += 3;
|
|
}
|
|
|
|
gotline = 1;
|
|
}
|
|
|
|
}
|
|
while (!gotline);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* these functions are for the 1013 chipset */
|
|
|
|
static void
|
|
set_ccd_channel_1013 (Mustek_pp_Handle * dev, int channel)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
priv->channel = channel;
|
|
sanei_pa4s2_writebyte (dev->fd, 6, chan_codes_1013[channel]);
|
|
}
|
|
|
|
static void
|
|
motor_backward_1013 (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->motor_step++;
|
|
set_led (dev);
|
|
|
|
if (priv->motor_phase > 3)
|
|
priv->motor_phase = 3;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x62);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, fullstep[priv->motor_phase]);
|
|
|
|
priv->motor_phase = (priv->motor_phase == 0 ? 3 : priv->motor_phase - 1);
|
|
|
|
set_ccd_channel_1013 (dev, priv->channel);
|
|
set_sti (dev);
|
|
|
|
}
|
|
|
|
static void
|
|
return_home_1013 (Mustek_pp_Handle * dev)
|
|
{
|
|
u_char ishome;
|
|
int ctr;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
/* 1013 can't return home all alone, nowait ignored */
|
|
|
|
for (ctr = 0; ctr < 4500; ctr++)
|
|
{
|
|
|
|
/* check_is_home_1013 */
|
|
sanei_pa4s2_readbegin (dev->fd, 2);
|
|
sanei_pa4s2_readbyte (dev->fd, &ishome);
|
|
sanei_pa4s2_readend (dev->fd);
|
|
|
|
/* yes, it should be is_not_home */
|
|
if ((ishome & 1) == 0)
|
|
break;
|
|
|
|
motor_backward_1013 (dev);
|
|
wait_bank_change (dev, priv->bank_count, 0);
|
|
reset_bank_count (dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
motor_forward_1013 (Mustek_pp_Handle * dev)
|
|
{
|
|
|
|
int ctr;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->motor_step++;
|
|
set_led (dev);
|
|
|
|
for (ctr = 0; ctr < 2; ctr++)
|
|
{
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x62);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, halfstep[priv->motor_phase]);
|
|
|
|
priv->motor_phase =
|
|
(priv->motor_phase == 7 ? 0 : priv->motor_phase + 1);
|
|
|
|
}
|
|
|
|
set_ccd_channel_1013 (dev, priv->channel);
|
|
set_sti (dev);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
config_ccd_1013 (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
if (dev->res != 0)
|
|
priv->res_step = SANE_FIX ((float) priv->hwres / (float) dev->res);
|
|
|
|
set_dpi_value (dev);
|
|
|
|
/* set_start_channel_1013 (dev); */
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x05);
|
|
|
|
switch (dev->mode)
|
|
{
|
|
case MODE_BW:
|
|
case MODE_GRAYSCALE:
|
|
priv->channel = CCD300_CHANNEL_GRAY;
|
|
break;
|
|
|
|
case MODE_COLOR:
|
|
priv->channel = CCD300_CHANNEL_RED;
|
|
break;
|
|
|
|
}
|
|
|
|
set_ccd_channel_1013 (dev, priv->channel);
|
|
|
|
/* set_invert_1013 (dev); */
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6,
|
|
(dev->invert == SANE_TRUE ? 0x04 : 0x14));
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x37);
|
|
reset_bank_count (dev);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x27);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x67);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x17);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x77);
|
|
|
|
/* set_initial_skip_1013 (dev); */
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x41);
|
|
|
|
priv->adjustskip = priv->skipcount + priv->skipimagebytes;
|
|
|
|
DBG (5, "config_ccd_1013: adjustskip %u\n", priv->adjustskip);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 5, priv->adjustskip / 16 + 2);
|
|
|
|
priv->adjustskip %= 16;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x81);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, 0x70);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x01);
|
|
|
|
|
|
set_line_adjust (dev);
|
|
|
|
get_bank_count (dev);
|
|
|
|
}
|
|
|
|
/* these functions are for the 1015 chipset */
|
|
|
|
|
|
static void
|
|
motor_control_1015 (Mustek_pp_Handle * dev, u_char control)
|
|
{
|
|
u_char val;
|
|
|
|
DBG (5, "motor_controll_1015: control code 0x%02x\n",
|
|
(unsigned int) control);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0xF6);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x22);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, control);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x02);
|
|
|
|
do
|
|
{
|
|
|
|
sanei_pa4s2_readbegin (dev->fd, 2);
|
|
sanei_pa4s2_readbyte (dev->fd, &val);
|
|
sanei_pa4s2_readend (dev->fd);
|
|
|
|
}
|
|
while ((val & 0x08) != 0);
|
|
|
|
}
|
|
|
|
static void
|
|
return_home_1015 (Mustek_pp_Handle * dev, SANE_Bool nowait)
|
|
{
|
|
|
|
u_char ishome, control = 0xC3;
|
|
|
|
motor_control_1015 (dev, control);
|
|
|
|
do
|
|
{
|
|
|
|
/* check_is_home_1015 */
|
|
sanei_pa4s2_readbegin (dev->fd, 2);
|
|
sanei_pa4s2_readbyte (dev->fd, &ishome);
|
|
sanei_pa4s2_readend (dev->fd);
|
|
|
|
if (nowait)
|
|
break;
|
|
|
|
usleep (1000); /* much nicer load */
|
|
|
|
}
|
|
while ((ishome & 2) == 0);
|
|
|
|
}
|
|
|
|
static void
|
|
motor_forward_1015 (Mustek_pp_Handle * dev)
|
|
{
|
|
u_char control = 0x1B;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->motor_step++;
|
|
set_led (dev);
|
|
|
|
|
|
motor_control_1015 (dev, control);
|
|
|
|
set_ccd_channel_1015 (dev, priv->channel);
|
|
set_sti (dev);
|
|
|
|
}
|
|
|
|
/*
|
|
static void
|
|
motor_backward_1015 (Mustek_pp_Handle * dev)
|
|
{
|
|
u_char control = 0x43;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->motor_step++;
|
|
|
|
set_led (dev);
|
|
|
|
switch (priv->ccd_type)
|
|
{
|
|
case 1:
|
|
control = 0x1B;
|
|
break;
|
|
|
|
default:
|
|
control = 0x43;
|
|
break;
|
|
}
|
|
|
|
motor_control_1015 (dev, control);
|
|
|
|
set_ccd_channel_1015 (dev, priv->channel);
|
|
set_sti (dev);
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
static void
|
|
set_ccd_channel_1015 (Mustek_pp_Handle * dev, int channel)
|
|
{
|
|
|
|
u_char chancode = chan_codes_1015[channel];
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->channel = channel;
|
|
|
|
priv->image_control &= 0x34;
|
|
chancode |= priv->image_control;
|
|
|
|
|
|
priv->image_control = chancode;
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, chancode);
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
config_ccd_1015 (Mustek_pp_Handle * dev)
|
|
{
|
|
|
|
u_char val;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
if (dev->res != 0)
|
|
priv->res_step = SANE_FIX ((float) priv->hwres / (float) dev->res);
|
|
|
|
|
|
set_dpi_value (dev);
|
|
|
|
priv->image_control = 4;
|
|
|
|
/* set_start_channel_1015 (dev); */
|
|
|
|
switch (dev->mode)
|
|
{
|
|
case MODE_BW:
|
|
case MODE_GRAYSCALE:
|
|
priv->channel = CCD300_CHANNEL_GRAY;
|
|
break;
|
|
|
|
case MODE_COLOR:
|
|
priv->channel = CCD300_CHANNEL_RED;
|
|
break;
|
|
|
|
}
|
|
|
|
set_ccd_channel_1015 (dev, priv->channel);
|
|
|
|
|
|
/* set_invert_1015 (dev); */
|
|
|
|
priv->image_control &= 0xE4;
|
|
|
|
if (dev->invert == SANE_FALSE)
|
|
priv->image_control |= 0x10;
|
|
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, priv->image_control);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x23);
|
|
sanei_pa4s2_writebyte (dev->fd, 5, 0x00);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x43);
|
|
|
|
switch (priv->ccd_type)
|
|
{
|
|
case 1:
|
|
val = 0x6B;
|
|
break;
|
|
case 4:
|
|
val = 0x9F;
|
|
break;
|
|
default:
|
|
val = 0x92;
|
|
break;
|
|
}
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 5, val);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x03);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x37);
|
|
reset_bank_count (dev);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x27);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x67);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x17);
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x77);
|
|
|
|
/* set_initial_skip_1015 (dev); */
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x41);
|
|
|
|
priv->adjustskip = priv->skipcount + priv->skipimagebytes;
|
|
|
|
/* if (dev->CCD.mode == MODE_COLOR)
|
|
dev->CCD.adjustskip <<= 3; */
|
|
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 5, priv->adjustskip / 32 + 1);
|
|
|
|
priv->adjustskip %= 32;
|
|
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x81);
|
|
|
|
/* expose time */
|
|
switch (priv->ccd_type)
|
|
{
|
|
case 1:
|
|
|
|
val = 0xA8;
|
|
break;
|
|
case 0:
|
|
val = 0x8A;
|
|
break;
|
|
default:
|
|
val = 0xA8;
|
|
break;
|
|
}
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 5, val);
|
|
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x01);
|
|
|
|
|
|
set_line_adjust (dev);
|
|
|
|
get_bank_count (dev);
|
|
|
|
}
|
|
|
|
|
|
/* these functions are interfaces only */
|
|
static void
|
|
config_ccd (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
DBG (5, "config_ccd: %d dpi, mode %d, invert %d, size %d\n",
|
|
priv->hwres, dev->mode, dev->invert, dev->params.pixels_per_line);
|
|
|
|
switch (priv->asic)
|
|
{
|
|
case CCD300_ASIC1013:
|
|
config_ccd_1013 (dev);
|
|
break;
|
|
|
|
case CCD300_ASIC1015:
|
|
config_ccd_1015 (dev);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
return_home (Mustek_pp_Handle * dev, SANE_Bool nowait)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
priv->saved_mode = dev->mode;
|
|
priv->saved_invert = dev->invert;
|
|
priv->saved_skipcount = priv->skipcount;
|
|
priv->saved_skipimagebyte = priv->skipimagebytes;
|
|
priv->saved_adjustskip = priv->adjustskip;
|
|
priv->saved_res = dev->res;
|
|
priv->saved_hwres = priv->hwres;
|
|
priv->saved_res_step = priv->res_step;
|
|
priv->saved_line_step = priv->line_step;
|
|
priv->saved_channel = priv->channel;
|
|
|
|
|
|
priv->hwres = dev->res = 100;
|
|
dev->mode = MODE_GRAYSCALE;
|
|
|
|
priv->skipcount = priv->skipimagebytes = 0;
|
|
|
|
config_ccd (dev);
|
|
|
|
switch (priv->asic)
|
|
{
|
|
case CCD300_ASIC1013:
|
|
return_home_1013 (dev);
|
|
break;
|
|
|
|
case CCD300_ASIC1015:
|
|
return_home_1015 (dev, nowait);
|
|
break;
|
|
}
|
|
|
|
|
|
dev->mode = priv->saved_mode;
|
|
dev->invert = priv->saved_invert;
|
|
priv->skipcount = priv->saved_skipcount;
|
|
priv->skipimagebytes = priv->saved_skipimagebyte;
|
|
priv->adjustskip = priv->saved_adjustskip;
|
|
dev->res = priv->saved_res;
|
|
priv->hwres = priv->saved_hwres;
|
|
priv->res_step = priv->saved_res_step;
|
|
priv->line_step = priv->saved_line_step;
|
|
priv->channel = priv->saved_channel;
|
|
priv->motor_step = 0;
|
|
|
|
config_ccd (dev);
|
|
}
|
|
|
|
static void
|
|
lamp (Mustek_pp_Handle * dev, int lamp_on)
|
|
{
|
|
|
|
set_lamp (dev, lamp_on);
|
|
|
|
}
|
|
|
|
static void
|
|
set_voltages (Mustek_pp_Handle * dev)
|
|
{
|
|
send_voltages (dev);
|
|
}
|
|
|
|
static void
|
|
move_motor (Mustek_pp_Handle * dev, int count, int forward)
|
|
{
|
|
|
|
int ctr;
|
|
|
|
DBG (5, "move_motor: %u steps (%s)\n", count,
|
|
(forward == SANE_TRUE ? "forward" : "backward"));
|
|
|
|
|
|
for (ctr = 0; ctr < count; ctr++)
|
|
{
|
|
|
|
move_motor_101x (dev, forward);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
static void
|
|
calibrate (Mustek_pp_Handle * dev)
|
|
{
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
DBG (5, "calibrate entered (asic = 0x%02x)\n", priv->asic);
|
|
|
|
calibrate_device_101x (dev);
|
|
|
|
DBG (5, "calibrate: ref_black %d, blackpos %d\n",
|
|
priv->ref_black, priv->blackpos);
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
get_lineart_line (Mustek_pp_Handle * dev, SANE_Byte * buf)
|
|
{
|
|
get_lineart_line_101x (dev, buf);
|
|
}
|
|
|
|
static void
|
|
get_grayscale_line (Mustek_pp_Handle * dev, SANE_Byte * buf)
|
|
{
|
|
|
|
get_grayscale_line_101x (dev, buf);
|
|
}
|
|
|
|
static void
|
|
get_color_line (Mustek_pp_Handle * dev, SANE_Byte * buf)
|
|
{
|
|
|
|
get_color_line_101x (dev, buf);
|
|
|
|
}
|
|
|
|
|
|
static SANE_Status
|
|
ccd300_init (SANE_Int options, SANE_String_Const port,
|
|
SANE_String_Const name, SANE_Attach_Callback attach)
|
|
{
|
|
SANE_Status status;
|
|
unsigned char asic, ccd;
|
|
int fd;
|
|
|
|
if (options != CAP_NOTHING)
|
|
{
|
|
DBG (1, "ccd300_init: called with unknown options (%#02x)\n", options);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* try to attach to he supplied port */
|
|
status = sanei_pa4s2_open (port, &fd);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (2, "ccd300_init: couldn't attach to port ``%s'' (%s)\n",
|
|
port, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
sanei_pa4s2_enable (fd, SANE_TRUE);
|
|
sanei_pa4s2_readbegin (fd, 0);
|
|
sanei_pa4s2_readbyte (fd, &asic);
|
|
sanei_pa4s2_readend (fd);
|
|
sanei_pa4s2_readbegin (fd, 2);
|
|
sanei_pa4s2_readbyte (fd, &ccd);
|
|
sanei_pa4s2_readend (fd);
|
|
sanei_pa4s2_enable (fd, SANE_FALSE);
|
|
sanei_pa4s2_close (fd);
|
|
|
|
if (asic != CCD300_ASIC1013 && asic != CCD300_ASIC1015)
|
|
{
|
|
DBG (2, "ccd300_init: scanner not recognized (unknown ASIC id %#02x)\n",
|
|
asic);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
ccd &= (asic == CCD300_ASIC1013 ? 0x04 : 0x05);
|
|
|
|
DBG (3, "ccd_init: found scanner on port ``%s'' (ASIC id %#02x, CCD %d)\n",
|
|
port, asic, ccd);
|
|
|
|
return attach (port, name, MUSTEK_PP_CCD300, options);
|
|
|
|
}
|
|
|
|
static void
|
|
ccd300_capabilities (SANE_Int info, SANE_String * model,
|
|
SANE_String * vendor, SANE_String * type,
|
|
SANE_Int * maxres, SANE_Int * minres,
|
|
SANE_Int * maxhsize, SANE_Int * maxvsize,
|
|
SANE_Int * caps)
|
|
{
|
|
*model = strdup ("600 III EP Plus");
|
|
*vendor = strdup ("Mustek");
|
|
*type = strdup ("flatbed (CCD 300 dpi)");
|
|
DBG (3,
|
|
"ccd300_capabilities: 600 III EP Plus flatbed CCD (300 dpi) scanner\n");
|
|
|
|
*maxres = 300;
|
|
*minres = 50;
|
|
*maxhsize = CCD300_MAXHSIZE;
|
|
*maxvsize = CCD300_MAXVSIZE;
|
|
*caps = info | CAP_INVERT | CAP_LAMP_OFF;
|
|
}
|
|
|
|
static SANE_Status
|
|
ccd300_open (SANE_String port, SANE_Int caps, SANE_Int * fd)
|
|
{
|
|
SANE_Status status;
|
|
|
|
if (caps & ~(CAP_NOTHING | CAP_INVERT | CAP_LAMP_OFF))
|
|
{
|
|
DBG (1, "ccd300_open: called with unknonw capabilities (%#02x)\n",
|
|
caps);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
DBG (3, "ccd300_open: called for port ``%s''\n", port);
|
|
|
|
status = sanei_pa4s2_open (port, fd);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
DBG (2, "ccd300_open: open failed (%s)\n", sane_strstatus (status));
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
ccd300_setup (SANE_Handle handle)
|
|
{
|
|
Mustek_pp_Handle *dev = handle;
|
|
mustek_pp_ccd300_priv *priv;
|
|
unsigned char asic, ccd;
|
|
|
|
DBG (3, "ccd300_setup: called for port ``%s''\n", dev->dev->port);
|
|
|
|
if ((priv = malloc (sizeof (mustek_pp_ccd300_priv))) == NULL)
|
|
{
|
|
DBG (1, "ccd300_setup: not enough memory\n");
|
|
return; /* can you here the shit hitting the fan? */
|
|
}
|
|
|
|
dev->priv = priv;
|
|
memset (priv, 0, sizeof (mustek_pp_ccd300_priv));
|
|
|
|
priv->bw = 128;
|
|
priv->wait_bank = 700;
|
|
priv->top = 47;
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_TRUE);
|
|
|
|
sanei_pa4s2_readbegin (dev->fd, 0);
|
|
sanei_pa4s2_readbyte (dev->fd, &asic);
|
|
sanei_pa4s2_readend (dev->fd);
|
|
sanei_pa4s2_readbegin (dev->fd, 2);
|
|
sanei_pa4s2_readbyte (dev->fd, &ccd);
|
|
sanei_pa4s2_readend (dev->fd);
|
|
ccd &= (asic == CCD300_ASIC1013 ? 0x04 : 0x05);
|
|
priv->asic = asic;
|
|
priv->ccd_type = ccd;
|
|
|
|
return_home (dev, SANE_TRUE);
|
|
lamp (dev, SANE_TRUE);
|
|
sanei_pa4s2_enable (dev->fd, SANE_FALSE);
|
|
dev->lamp_on = time (NULL);
|
|
dev->res = priv->hwres = 300;
|
|
dev->mode = MODE_COLOR;
|
|
}
|
|
|
|
static void
|
|
ccd300_close (SANE_Handle handle)
|
|
{
|
|
|
|
Mustek_pp_Handle *dev = handle;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
DBG (3, "ccd300_close: called for port ``%s''\n", dev->dev->port);
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_TRUE);
|
|
lamp (dev, SANE_FALSE);
|
|
return_home (dev, SANE_FALSE);
|
|
sanei_pa4s2_enable (dev->fd, SANE_FALSE);
|
|
|
|
sanei_pa4s2_close (dev->fd);
|
|
free (priv);
|
|
|
|
DBG (3, "ccd300_close: device shut down and all buffers freed\n");
|
|
}
|
|
|
|
static SANE_Status
|
|
ccd300_config (SANE_Handle handle, SANE_String_Const optname,
|
|
SANE_String_Const optval)
|
|
{
|
|
Mustek_pp_Handle *dev = handle;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
int value = -1;
|
|
|
|
DBG (3, "ccd300_config: called for port ``%s'' (%s%s%s)\n",
|
|
dev->dev->port,
|
|
optname, (optval ? " = " : ""), (optval ? optval : ""));
|
|
|
|
if (!strcmp (optname, "bw"))
|
|
{
|
|
|
|
if (!optval)
|
|
{
|
|
DBG (1, "ccd300_config: missing value for option ``bw''\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* ok, ok, should be strtol... know what? send me a patch. */
|
|
value = atoi (optval);
|
|
|
|
if ((value < 0) || (value > 255))
|
|
{
|
|
DBG (1,
|
|
"ccd300_config: value ``%s'' for option ``bw'' is out of range (0 <= bw <= 255)\n",
|
|
optval);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
priv->bw = value;
|
|
|
|
}
|
|
else if (!strcmp (optname, "waitbank"))
|
|
{
|
|
|
|
if (!optval)
|
|
{
|
|
DBG (1, "ccd300_config: missing value for option ``waitbank''\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
value = atoi (optval);
|
|
|
|
if (value < 0)
|
|
{
|
|
DBG (1,
|
|
"ccd300_config: value ``%s'' for option ``waitbank'' is out of range (>= 0)\n",
|
|
optval);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
priv->wait_bank = value;
|
|
}
|
|
else if (!strcmp (optname, "top"))
|
|
{
|
|
|
|
if (!optval)
|
|
{
|
|
DBG (1, "ccd300_config: missing value for option ``top''\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
value = atoi (optval);
|
|
|
|
if (value < 0)
|
|
{
|
|
DBG (1,
|
|
"ccd300_config: value ``%s'' for option ``top'' is out of range (>= 0)\n",
|
|
optval);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
priv->top = value;
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "ccd300_config: unkown option ``%s''", optname);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
static void
|
|
ccd300_stop (SANE_Handle handle)
|
|
{
|
|
Mustek_pp_Handle *dev = handle;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
int cnt;
|
|
|
|
DBG (3, "ccd300_stop: stopping scan operating on port ``%s''\n",
|
|
dev->dev->port);
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_TRUE);
|
|
return_home (dev, SANE_TRUE);
|
|
sanei_pa4s2_enable (dev->fd, SANE_FALSE);
|
|
|
|
free (priv->calib_r);
|
|
free (priv->calib_g);
|
|
free (priv->calib_b);
|
|
|
|
if (priv->red)
|
|
{
|
|
for (cnt = 0; cnt < priv->green_offs; cnt++)
|
|
free (priv->red[cnt]);
|
|
free (priv->red);
|
|
}
|
|
if (priv->blue)
|
|
{
|
|
for (cnt = 0; cnt < priv->blue_offs; cnt++)
|
|
free (priv->blue[cnt]);
|
|
free (priv->blue);
|
|
}
|
|
free (priv->green);
|
|
|
|
priv->calib_r = priv->calib_g = priv->calib_b = NULL;
|
|
priv->red = priv->blue = NULL;
|
|
priv->green = NULL;
|
|
|
|
}
|
|
|
|
static SANE_Status
|
|
ccd300_start (SANE_Handle handle)
|
|
{
|
|
Mustek_pp_Handle *dev = handle;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
DBG (3, "ccd300_start: called for port ``%s''\n", dev->dev->port);
|
|
|
|
if (dev->res <= 100)
|
|
priv->hwres = 100;
|
|
else if (dev->res <= 200)
|
|
priv->hwres = 200;
|
|
else if (dev->res <= 300)
|
|
priv->hwres = 300;
|
|
|
|
DBG (4, "ccd300_start: setting hardware resolution to %d dpi\n",
|
|
priv->hwres);
|
|
|
|
priv->skipimagebytes = dev->topX;
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_TRUE);
|
|
config_ccd (dev);
|
|
set_voltages (dev);
|
|
get_bank_count (dev);
|
|
|
|
if (priv->bank_count != 0)
|
|
{
|
|
DBG (2, "ccd300_start: bank count is not zero...\n");
|
|
}
|
|
|
|
return_home (dev, SANE_FALSE);
|
|
|
|
priv->motor_step = 0;
|
|
|
|
/* allocate memory for calibration */
|
|
if ((priv->calib_g = malloc (dev->params.pixels_per_line)) == NULL)
|
|
{
|
|
sanei_pa4s2_enable (dev->fd, SANE_FALSE);
|
|
DBG (1, "ccd300_start: not enough memory\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
if (dev->mode == MODE_COLOR)
|
|
{
|
|
priv->calib_r = malloc (dev->params.pixels_per_line);
|
|
priv->calib_b = malloc (dev->params.pixels_per_line);
|
|
|
|
if ((priv->calib_r == NULL) || (priv->calib_b == NULL))
|
|
{
|
|
free (priv->calib_g);
|
|
free (priv->calib_r);
|
|
free (priv->calib_b);
|
|
priv->calib_r = priv->calib_g = priv->calib_b = NULL;
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_FALSE);
|
|
DBG (1, "ccd300_start: not enough memory\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
}
|
|
|
|
calibrate (dev);
|
|
|
|
if (priv->ccd_type == 1)
|
|
{
|
|
priv->blue_offs = 4;
|
|
priv->green_offs = 8;
|
|
}
|
|
else
|
|
{
|
|
priv->blue_offs = 8;
|
|
priv->green_offs = 16;
|
|
}
|
|
|
|
move_motor (dev, priv->top + dev->topY -
|
|
(dev->mode == MODE_COLOR ? priv->green_offs : 0), SANE_TRUE);
|
|
|
|
if (priv->ccd_type == 1)
|
|
sanei_pa4s2_writebyte (dev->fd, 6, 0x15);
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_FALSE);
|
|
|
|
if (dev->mode == MODE_COLOR)
|
|
{
|
|
int failed = SANE_FALSE, cnt;
|
|
|
|
priv->line_step = SANE_FIX (300.0 / (float) dev->res);
|
|
priv->rdiff = priv->line_step;
|
|
priv->bdiff = priv->rdiff + (priv->blue_offs << SANE_FIXED_SCALE_SHIFT);
|
|
priv->gdiff =
|
|
priv->rdiff + (priv->green_offs << SANE_FIXED_SCALE_SHIFT);
|
|
|
|
priv->red = malloc (sizeof (SANE_Byte *) * priv->green_offs);
|
|
priv->blue = malloc (sizeof (SANE_Byte *) * priv->blue_offs);
|
|
priv->green = malloc (dev->params.pixels_per_line);
|
|
|
|
if ((priv->red == NULL) || (priv->blue == NULL)
|
|
|| (priv->green == NULL))
|
|
{
|
|
free (priv->calib_r);
|
|
free (priv->calib_g);
|
|
free (priv->calib_b);
|
|
priv->calib_r = priv->calib_g = priv->calib_b = NULL;
|
|
|
|
free (priv->red);
|
|
free (priv->green);
|
|
free (priv->blue);
|
|
priv->red = priv->blue = NULL;
|
|
priv->green = NULL;
|
|
|
|
DBG (1, "ccd300_start: not enough memory for ld buffers\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
/* note to myself: better allocate one huge chunk of memory and set
|
|
pointers */
|
|
for (cnt = 0; cnt < priv->green_offs; cnt++)
|
|
if ((priv->red[cnt] = malloc (dev->params.pixels_per_line)) == NULL)
|
|
failed = SANE_TRUE;
|
|
|
|
for (cnt = 0; cnt < priv->blue_offs; cnt++)
|
|
if ((priv->blue[cnt] = malloc (dev->params.pixels_per_line)) == NULL)
|
|
failed = SANE_TRUE;
|
|
|
|
if (failed == SANE_TRUE)
|
|
{
|
|
free (priv->calib_r);
|
|
free (priv->calib_g);
|
|
free (priv->calib_b);
|
|
priv->calib_r = priv->calib_g = priv->calib_b = NULL;
|
|
|
|
for (cnt = 0; cnt < priv->green_offs; cnt++)
|
|
free (priv->red[cnt]);
|
|
for (cnt = 0; cnt < priv->blue_offs; cnt++)
|
|
free (priv->blue[cnt]);
|
|
|
|
free (priv->red);
|
|
free (priv->green);
|
|
free (priv->blue);
|
|
priv->red = priv->blue = NULL;
|
|
priv->green = NULL;
|
|
|
|
DBG (1, "ccd300_start: not enough memory for ld buffers\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
priv->redline = priv->blueline = priv->ccd_line = 0;
|
|
}
|
|
|
|
priv->lines = 0;
|
|
priv->lines_left = dev->params.lines;
|
|
|
|
DBG (3, "ccd300_start: device ready for scanning\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
static void
|
|
ccd300_read (SANE_Handle handle, SANE_Byte * buffer)
|
|
{
|
|
Mustek_pp_Handle *dev = handle;
|
|
mustek_pp_ccd300_priv *priv = dev->priv;
|
|
|
|
DBG (3, "ccd300_read: receiving one line from port ``%s''\n",
|
|
dev->dev->port);
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_TRUE);
|
|
|
|
switch (dev->mode)
|
|
{
|
|
case MODE_BW:
|
|
get_lineart_line (dev, buffer);
|
|
break;
|
|
|
|
case MODE_GRAYSCALE:
|
|
get_grayscale_line (dev, buffer);
|
|
break;
|
|
|
|
case MODE_COLOR:
|
|
get_color_line (dev, buffer);
|
|
break;
|
|
}
|
|
|
|
priv->lines_left--;
|
|
priv->lines++;
|
|
|
|
DBG (4, "ccd300_read: %d lines read (%d to go)\n", priv->lines,
|
|
priv->lines_left);
|
|
|
|
if (priv->lines_left == 0)
|
|
{
|
|
DBG (3, "ccd300_read: scan finished\n");
|
|
return_home (dev, SANE_TRUE);
|
|
}
|
|
|
|
sanei_pa4s2_enable (dev->fd, SANE_FALSE);
|
|
|
|
}
|