summaryrefslogtreecommitdiff
path: root/backend/canon_pp-io.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/canon_pp-io.c')
-rw-r--r--backend/canon_pp-io.c618
1 files changed, 618 insertions, 0 deletions
diff --git a/backend/canon_pp-io.c b/backend/canon_pp-io.c
new file mode 100644
index 0000000..881ac24
--- /dev/null
+++ b/backend/canon_pp-io.c
@@ -0,0 +1,618 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix
+ 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 is part of the canon_pp backend, supporting Canon CanoScan
+ Parallel scanners and also distributed as part of the stand-alone driver.
+
+ canon_pp-io.c: $Revision$
+
+ Low Level Function library for Canon CanoScan Parallel Scanners by
+ Simon Krix <kinsei@users.sourceforge.net>
+ */
+
+#ifndef NOSANE
+#include "../include/sane/config.h"
+#endif
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <ieee1284.h>
+#include "canon_pp-io.h"
+#include "canon_pp-dev.h"
+
+#ifdef NOSANE
+
+/* No SANE, Things that only apply to stand-alone */
+#include <stdio.h>
+#include <stdarg.h>
+
+static void DBG(int level, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ if (level < 50) vfprintf(stderr, format, args);
+ va_end(args);
+}
+#else
+
+/* Definitions which only apply to SANE compiles */
+#ifndef VERSION
+#define VERSION "$Revision$"
+#endif
+
+/* Fix problem with DBG macro definition having a - in the name */
+#define DEBUG_DECLARE_ONLY
+#include "canon_pp.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_config.h"
+
+#endif
+
+/* 0x00 = Nibble Mode (M1284_NIBBLE)
+ 0x10 = ECP Mode (M1284_ECP)
+ The scanner driver seems not to support ECP RLE mode
+ (which is a huge bummer because compression would be
+ ace) nor EPP mode.
+ */
+static int ieee_mode = M1284_NIBBLE;
+
+/* For super-verbose debugging */
+/* #define DUMP_PACKETS 1 */
+
+/* Some sort of initialisation command */
+static unsigned char cmd_init[10] = { 0xec, 0x20, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/************* Local Prototypes ******************/
+
+/* Used by wake_scanner */
+static int scanner_reset(struct parport *port);
+static void scanner_chessboard_control(struct parport *port);
+static void scanner_chessboard_data(struct parport *port, int mode);
+
+/* Used by read_data */
+static int ieee_transfer(struct parport *port, int length,
+ unsigned char *data);
+
+/* Low level functions */
+static int readstatus(struct parport *port);
+static int expect(struct parport *port, const char *step, int s,
+ int mask, unsigned int delay);
+
+/* Port-level functions */
+static void outdata(struct parport *port, int d);
+static void outcont(struct parport *port, int d, int mask);
+static void outboth(struct parport *port, int d, int c);
+
+/************************************/
+
+/*
+ * IEEE 1284 defines many values for m,
+ * but these scanners only support 2: nibble and ECP modes.
+ * And no data compression either (argh!)
+ * 0 = Nibble-mode reverse channel transfer
+ * 16 = ECP-mode
+ */
+void sanei_canon_pp_set_ieee1284_mode(int m)
+{
+ ieee_mode = m;
+}
+
+int sanei_canon_pp_wake_scanner(struct parport *port, int mode)
+{
+ /* The scanner tristates the printer's control lines
+ (essentially disabling the passthrough port) and exits
+ from Transparent Mode ready for communication. */
+ int i = 0;
+ int tmp;
+ int max_cycles = 3;
+
+ tmp = readstatus(port);
+
+ /* Reset only works on 30/40 models */
+ if (mode != INITMODE_20P)
+ {
+ if ((tmp != READY))
+ {
+ DBG(40, "Scanner not ready (0x%x). Attempting to "
+ "reset...\n", tmp);
+ scanner_reset(port);
+ /* give it more of a chance to reset in this case */
+ max_cycles = 5;
+ }
+ } else {
+ DBG(0, "WARNING: Don't know how to reset an FBx20P, you may "
+ "have to power cycle\n");
+ }
+
+ do
+ {
+ i++;
+
+ /* Send the wakeup sequence */
+ scanner_chessboard_control(port);
+ scanner_chessboard_data(port, mode);
+
+ if (expect(port, NULL, 0x03, 0x1f, 800000) &&
+ (mode == INITMODE_AUTO))
+ {
+ /* 630 Style init failed, try 620 style */
+ scanner_chessboard_control(port);
+ scanner_chessboard_data(port, INITMODE_20P);
+ }
+
+ if (expect(port, "Scanner wakeup reply 1", 0x03, 0x1f, 50000))
+ {
+ outboth(port, 0x04, 0x0d);
+ usleep(100000);
+ outcont(port, 0x07, 0x0f);
+ usleep(100000);
+ }
+
+ } while ((i < max_cycles) && (!expect(port,"Scanner wakeup reply 2",
+ 0x03, 0x1f, 100000) == 0));
+
+ /* Block just after chessboarding
+ Reply 1 (S3 and S4 on, S5 and S7 off) */
+ outcont(port, 0, HOSTBUSY); /* C1 off */
+ /* Reply 2 - If it ain't happening by now, it ain't gonna happen. */
+ if (expect(port, "Reply 2", 0xc, 0x1f, 800000))
+ return -1;
+ outcont(port, HOSTBUSY, HOSTBUSY); /* C1 on */
+ if (expect(port, "Reply 3", 0x0b, 0x1f, 800000))
+ return -1;
+ outboth(port, 0, NSELECTIN | NINIT | HOSTCLK); /* Clear D, C3+, C1- */
+
+ /* If we had to try the wakeup cycle more than once, we should wait
+ * here for 10 seconds to let the scanner pull itself together -
+ * it can actually take longer, but I can't wait that long! */
+ if (i > 1)
+ {
+ DBG(10, "Had to reset scanner, waiting for the "
+ "head to get back.\n");
+ usleep(10000000);
+ }
+
+ return 0;
+}
+
+
+int sanei_canon_pp_write(struct parport *port, int length, unsigned char *data)
+{
+
+#ifdef DUMP_PACKETS
+ ssize_t count;
+
+ DBG(10,"Sending: ");
+ for (count = 0; count < length; count++)
+ {
+ DBG(10,"%02x ", data[count]);
+ if (count % 20 == 19)
+ DBG(10,"\n ");
+ }
+ if (count % 20 != 19) DBG(10,"\n");
+#endif
+
+ DBG(100, "NEW Send Command (length %i):\n", length);
+
+ switch (ieee_mode)
+ {
+ case M1284_BECP:
+ case M1284_ECPRLE:
+ case M1284_ECPSWE:
+ case M1284_ECP:
+ ieee1284_negotiate(port, ieee_mode);
+ if (ieee1284_ecp_write_data(port, 0, (char *)data,
+ length) != length)
+ return -1;
+ break;
+ case M1284_NIBBLE:
+ if (ieee1284_compat_write(port, 0, (char *)data,
+ length) != length)
+ return -1;
+ break;
+ default:
+ DBG(0, "Invalid mode in write!\n");
+ }
+
+ DBG(100, "<< write");
+
+ return 0;
+}
+
+int sanei_canon_pp_read(struct parport *port, int length, unsigned char *data)
+{
+ int count, offset;
+
+ DBG(200, "NEW read_data (%i bytes):\n", length);
+ ieee1284_negotiate(port, ieee_mode);
+
+ /* This is special; Nibble mode needs a little
+ extra help from us. */
+
+ if (ieee_mode == M1284_NIBBLE)
+ {
+ /* Interrupt phase */
+ outcont(port, NSELECTIN, HOSTBUSY | NSELECTIN);
+ if (expect(port, "Read Data 1", 0, NDATAAVAIL, 6000000))
+ {
+ DBG(10,"Error 1\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+ outcont(port, HOSTBUSY, HOSTBUSY);
+
+ if (expect(port, "Read Data 2", NACK, NACK, 1000000))
+ {
+ DBG(1,"Error 2\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+ if (expect(port, "Read Data 3 (Ready?)", 0, PERROR, 1000000))
+ {
+ DBG(1,"Error 3\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+
+ /* Host-Busy Data Available phase */
+
+ if ((readstatus(port) & NDATAAVAIL) == NDATAAVAIL)
+ {
+ DBG(1,"No data to read.\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+ }
+
+ offset = 0;
+
+ DBG(100, "-> ieee_transfer(%d) *\n", length);
+ count = ieee_transfer(port, length, data);
+ DBG(100, "<- (%d)\n", count);
+ /* Early-out if it was not implemented */
+ if (count == E1284_NOTIMPL)
+ return 2;
+
+ length -= count;
+ offset+= count;
+ while (length > 0)
+ {
+ /* If 0 bytes were transferred, it's a legal
+ "No data" condition (I think). Otherwise,
+ it may have run out of buffer.. keep reading*/
+
+ if (count < 0) {
+ DBG(10, "Couldn't read enough data (need %d more "
+ "of %d)\n", length+count,length+offset);
+ ieee1284_terminate(port);
+ return 1;
+ }
+
+ DBG(100, "-> ieee_transfer(%d)\n", length);
+ count = ieee_transfer(port, length, data+offset);
+ DBG(100, "<- (%d)\n", count);
+ length-=count;
+ offset+= count;
+
+ }
+
+#ifdef DUMP_PACKETS
+ if (length <= 60)
+ {
+ DBG(10,"Read: ");
+ for (count = 0; count < length; count++)
+ {
+ DBG(10,"%02x ", data[count]);
+ if (count % 20 == 19)
+ DBG(10,"\n ");
+ }
+
+ if (count % 20 != 19) DBG(10,"\n");
+ }
+ else
+ {
+ DBG(10,"Read: %i bytes\n", length);
+ }
+#endif
+
+ if (ieee_mode == M1284_NIBBLE)
+ ieee1284_terminate(port);
+
+ return 0;
+
+}
+
+static int ieee_transfer(struct parport *port, int length, unsigned char *data)
+{
+ int result = 0;
+
+ DBG(100, "IEEE transfer (%i bytes)\n", length);
+
+ switch (ieee_mode)
+ {
+ case M1284_BECP:
+ case M1284_ECP:
+ case M1284_ECPRLE:
+ case M1284_ECPSWE:
+ result = ieee1284_ecp_read_data(port, 0, (char *)data,
+ length);
+ break;
+ case M1284_NIBBLE:
+ result = ieee1284_nibble_read(port, 0, (char *)data,
+ length);
+ break;
+ default:
+ DBG(1, "Internal error: Wrong mode for transfer.\n"
+ "Please email stauff1@users.sourceforge.net\n"
+ "or kinsei@users.sourceforge.net\n");
+ }
+
+ return result;
+}
+
+int sanei_canon_pp_check_status(struct parport *port)
+{
+ int status;
+ unsigned char data[2];
+
+ DBG(200, "* Check Status:\n");
+
+ if (sanei_canon_pp_read(port, 2, data))
+ return -1;
+
+ status = data[0] | (data[1] << 8);
+
+ switch(status)
+ {
+ case 0x0606:
+ DBG(200, "Ready - 0x0606\n");
+ return 0;
+ break;
+ case 0x1414:
+ DBG(200, "Busy - 0x1414\n");
+ return 1;
+ break;
+ case 0x0805:
+ DBG(200, "Resetting - 0x0805\n");
+ return 3;
+ break;
+ case 0x1515:
+ DBG(1, "!! Invalid Command - 0x1515\n");
+ return 2;
+ break;
+ case 0x0000:
+ DBG(200, "Nothing - 0x0000");
+ return 4;
+ break;
+
+ default:
+ DBG(1, "!! Unknown status - %04x\n", status);
+ return 100;
+ }
+}
+
+
+/* Send a raw byte to the printer port */
+static void outdata(struct parport *port, int d)
+{
+ ieee1284_write_data(port, d & 0xff);
+}
+
+/* Send the low nibble of d to the control port.
+ The mask affects which bits are changed. */
+static void outcont(struct parport *port, int d, int mask)
+{
+ static int control_port_status = 0;
+ control_port_status = (control_port_status & ~mask) | (d & mask);
+ ieee1284_write_control(port, (control_port_status & 0x0f));
+}
+
+/* Send a byte to both ports */
+static void outboth(struct parport *port, int d, int c)
+{
+ ieee1284_write_data(port, d & 0xff);
+ outcont(port, c, 0x0f);
+}
+
+/* readstatus():
+ Returns the LOGIC value of the S register (ie: all input lines)
+ shifted right to to make it easier to read. Note: S5 is inverted
+ by ieee1284_read_status so we don't need to */
+static int readstatus(struct parport *port)
+{
+ return (ieee1284_read_status(port) & 0xf8) >> 3;
+}
+
+static void scanner_chessboard_control(struct parport *port)
+{
+ /* Wiggle C1 and C3 (twice) */
+ outboth(port, 0x0, 13);
+ usleep(10);
+ outcont(port, 7, 0xf);
+ usleep(10);
+ outcont(port, 13, 0xf);
+ usleep(10);
+ outcont(port, 7, 0xf);
+ usleep(10);
+}
+
+static void scanner_chessboard_data(struct parport *port, int mode)
+{
+ int count;
+
+ /* initial weirdness here for 620P - seems to go quite fast,
+ * just ignore it! */
+
+ for (count = 0; count < 2; count++)
+ {
+ /* Wiggle data lines (4 times) while strobing C1 */
+ /* 33 here for *30P, 55 for *20P */
+ if (mode == INITMODE_20P)
+ outdata(port, 0x55);
+ else
+ outdata(port, 0x33);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+ outcont(port, 0, HOSTBUSY);
+ usleep(10);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+
+ if (mode == INITMODE_20P)
+ outdata(port, 0xaa);
+ else
+ outdata(port, 0xcc);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+ outcont(port, 0, HOSTBUSY);
+ usleep(10);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+ }
+}
+
+/* Reset the scanner. At least, it works 50% of the time. */
+static int scanner_reset(struct parport *port)
+{
+
+ /* Resetting only works for the *30Ps, sorry */
+ if (readstatus(port) == 0x0b)
+ {
+ /* Init Block 1 - composed of a 0-byte IEEE read */
+ ieee1284_negotiate(port, 0x0);
+ ieee1284_terminate(port);
+ ieee1284_negotiate(port, 0x0);
+ ieee1284_terminate(port);
+ scanner_chessboard_data(port, 1);
+ scanner_chessboard_data(port, 1);
+ scanner_chessboard_data(port, 1);
+ scanner_chessboard_data(port, 1);
+
+ scanner_chessboard_data(port, 0);
+ scanner_chessboard_data(port, 0);
+ scanner_chessboard_data(port, 0);
+ scanner_chessboard_data(port, 0);
+ }
+
+ /* Reset Block 2 =============== */
+ outboth(port, 0x04, 0x0d);
+
+ /* Specifically, we want this: 00111 on S */
+ if (expect(port, "Reset 2 response 1", 0x7, 0x1f, 500000))
+ return 1;
+
+ outcont(port, 0, HOSTCLK);
+ usleep(5);
+ outcont(port, 0x0f, 0xf); /* All lines must be 1. */
+
+ /* All lines 1 */
+ if (expect(port, "Reset 2 response 2 (READY)",
+ 0x1f, 0x1f, 500000))
+ return 1;
+
+ outcont(port, 0, HOSTBUSY);
+ usleep(100000); /* a short pause */
+ outcont(port, HOSTBUSY, HOSTBUSY | NSELECTIN);
+
+ return 0;
+}
+
+/* A timed version of expect, which will wait for delay before erroring
+ This is the one and only one we should be using */
+static int expect(struct parport *port, const char *msg, int s,
+ int mask, unsigned int delay)
+{
+ struct timeval tv;
+
+ tv.tv_sec = delay / 1000000;
+ tv.tv_usec = delay % 1000000;
+
+ if (ieee1284_wait_status(port, mask << 3, s << 3, &tv))
+ {
+ if (msg) DBG(10, "Timeout: %s (0x%02x in 0x%02x) - Status "
+ "= 0x%02x\n", msg, s, mask, readstatus(port));
+ return 1;
+ }
+
+ return 0;
+}
+
+int sanei_canon_pp_scanner_init(struct parport *port)
+{
+
+ int tries = 0;
+ int tmp = 0;
+
+ /* Put the scanner in nibble mode */
+ ieee1284_negotiate(port, 0x0);
+
+ /* No data to read yet - return to idle mode */
+ ieee1284_terminate(port);
+
+ /* In Windows, this is always ECP (or an attempt at it) */
+ if (sanei_canon_pp_write(port, 10, cmd_init))
+ return -1;
+ /* Note that we don't really mind what the status was as long as it
+ * wasn't a read error (returns -1) */
+ /* In fact, the 620P gives an error on that last command, but they
+ * keep going anyway */
+ if (sanei_canon_pp_check_status(port) < 0)
+ return -1;
+
+ /* Try until it's ready */
+ sanei_canon_pp_write(port, 10, cmd_init);
+ while ((tries < 3) && (tmp = sanei_canon_pp_check_status(port)))
+ {
+ if (tmp < 0)
+ return -1;
+ DBG(10, "scanner_init: Giving the scanner a snooze...\n");
+ usleep(500000);
+
+ tries++;
+
+ sanei_canon_pp_write(port, 10, cmd_init);
+ }
+
+ if (tries == 3) return 1;
+
+ return 0;
+}