summaryrefslogtreecommitdiff
path: root/backend/mustek_pp_ccd300.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/mustek_pp_ccd300.c')
-rw-r--r--backend/mustek_pp_ccd300.c2061
1 files changed, 2061 insertions, 0 deletions
diff --git a/backend/mustek_pp_ccd300.c b/backend/mustek_pp_ccd300.c
new file mode 100644
index 0000000..c5351a4
--- /dev/null
+++ b/backend/mustek_pp_ccd300.c
@@ -0,0 +1,2061 @@
+/* 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);
+
+}