diff options
author | Didier Raboud <odyx@debian.org> | 2015-10-19 10:40:17 +0200 |
---|---|---|
committer | Didier Raboud <odyx@debian.org> | 2015-10-19 10:40:17 +0200 |
commit | 82a5e2a21f1cc4ea142514a1a045a63158f10256 (patch) | |
tree | 43bb3f0d058d6f632c273f1073708f2017b6d1bb /src/cups | |
parent | 92976f71a4aa7f84cd8aadf013d2b03873dead7c (diff) |
Imported Upstream version 5.2.11~pre2
Diffstat (limited to 'src/cups')
-rw-r--r-- | src/cups/Makefile.am | 4 | ||||
-rw-r--r-- | src/cups/Makefile.in | 40 | ||||
-rw-r--r-- | src/cups/backend_common.c | 433 | ||||
-rw-r--r-- | src/cups/backend_common.h | 69 | ||||
-rw-r--r-- | src/cups/blacklist | 3 | ||||
-rw-r--r-- | src/cups/citizencw01_print.c | 65 | ||||
-rw-r--r-- | src/cups/dnpds40_print.c | 541 | ||||
-rw-r--r-- | src/cups/genppd.c | 69 | ||||
-rw-r--r-- | src/cups/kodak1400_print.c | 30 | ||||
-rw-r--r-- | src/cups/kodak605_print.c | 548 | ||||
-rw-r--r-- | src/cups/kodak6800_print.c | 1186 | ||||
-rw-r--r-- | src/cups/mitsu70x_print.c | 38 | ||||
-rw-r--r-- | src/cups/mitsu9550_print.c | 88 | ||||
-rw-r--r-- | src/cups/selphy_print.c | 92 | ||||
-rw-r--r-- | src/cups/shinko_s1245_print.c | 1621 | ||||
-rw-r--r-- | src/cups/shinko_s2145_print.c | 344 | ||||
-rw-r--r-- | src/cups/shinko_s6245_print.c | 1863 | ||||
-rw-r--r-- | src/cups/sony_updr150_print.c | 36 | ||||
-rwxr-xr-x[-rw-r--r--] | src/cups/test-ppds | 2 | ||||
-rwxr-xr-x | src/cups/test-rastertogutenprint.in | 31 |
20 files changed, 5362 insertions, 1741 deletions
diff --git a/src/cups/Makefile.am b/src/cups/Makefile.am index 7bd6cd9..d4789ad 100644 --- a/src/cups/Makefile.am +++ b/src/cups/Makefile.am @@ -1,4 +1,4 @@ -## $Id: Makefile.am,v 1.150 2015/01/10 14:27:55 speachy Exp $ +## $Id: Makefile.am,v 1.151 2015/10/05 13:55:40 speachy Exp $ ## Copyright (C) 2000 Roger Leigh ## ## This program is free software; you can redistribute it and/or modify @@ -115,7 +115,7 @@ commandtoepson_SOURCES = commandtoepson.c commandtoepson_LDADD = $(CUPS_LIBS) if BUILD_LIBUSB_BACKENDS -backend_gutenprint_SOURCES = selphy_print.c kodak1400_print.c kodak6800_print.c kodak605_print.c shinko_s2145_print.c sony_updr150_print.c dnpds40_print.c mitsu70x_print.c citizencw01_print.c mitsu9550_print.c backend_common.c backend_common.h +backend_gutenprint_SOURCES = selphy_print.c kodak1400_print.c kodak6800_print.c kodak605_print.c shinko_s2145_print.c sony_updr150_print.c dnpds40_print.c mitsu70x_print.c citizencw01_print.c mitsu9550_print.c backend_common.c backend_common.h shinko_s1245_print.c shinko_s6245_print.c backend_gutenprint_LDADD = $(LIBUSB_LIBS) backend_gutenprint_CPPFLAGS = $(LIBUSB_CFLAGS) -DURI_PREFIX=\"gutenprint$(GUTENPRINT_MAJOR_VERSION)$(GUTENPRINT_MINOR_VERSION)+usb\" -DLIBUSB_PRE_1_0_10 diff --git a/src/cups/Makefile.in b/src/cups/Makefile.in index c5b156b..2074e88 100644 --- a/src/cups/Makefile.in +++ b/src/cups/Makefile.in @@ -129,7 +129,7 @@ am__backend_gutenprint_SOURCES_DIST = selphy_print.c kodak1400_print.c \ kodak6800_print.c kodak605_print.c shinko_s2145_print.c \ sony_updr150_print.c dnpds40_print.c mitsu70x_print.c \ citizencw01_print.c mitsu9550_print.c backend_common.c \ - backend_common.h + backend_common.h shinko_s1245_print.c shinko_s6245_print.c @BUILD_LIBUSB_BACKENDS_TRUE@am_backend_gutenprint_OBJECTS = backend_gutenprint-selphy_print.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-kodak1400_print.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-kodak6800_print.$(OBJEXT) \ @@ -140,7 +140,9 @@ am__backend_gutenprint_SOURCES_DIST = selphy_print.c kodak1400_print.c \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-mitsu70x_print.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-citizencw01_print.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-mitsu9550_print.$(OBJEXT) \ -@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_common.$(OBJEXT) +@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_common.$(OBJEXT) \ +@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-shinko_s1245_print.$(OBJEXT) \ +@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-shinko_s6245_print.$(OBJEXT) backend_gutenprint_OBJECTS = $(am_backend_gutenprint_OBJECTS) am__DEPENDENCIES_1 = @BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_DEPENDENCIES = \ @@ -508,6 +510,8 @@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FIND = @FIND@ @@ -719,7 +723,7 @@ commandtocanon_SOURCES = commandtocanon.c commandtocanon_LDADD = $(CUPS_LIBS) commandtoepson_SOURCES = commandtoepson.c commandtoepson_LDADD = $(CUPS_LIBS) -@BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_SOURCES = selphy_print.c kodak1400_print.c kodak6800_print.c kodak605_print.c shinko_s2145_print.c sony_updr150_print.c dnpds40_print.c mitsu70x_print.c citizencw01_print.c mitsu9550_print.c backend_common.c backend_common.h +@BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_SOURCES = selphy_print.c kodak1400_print.c kodak6800_print.c kodak605_print.c shinko_s2145_print.c sony_updr150_print.c dnpds40_print.c mitsu70x_print.c citizencw01_print.c mitsu9550_print.c backend_common.c backend_common.h shinko_s1245_print.c shinko_s6245_print.c @BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_LDADD = $(LIBUSB_LIBS) @BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_CPPFLAGS = $(LIBUSB_CFLAGS) -DURI_PREFIX=\"gutenprint$(GUTENPRINT_MAJOR_VERSION)$(GUTENPRINT_MINOR_VERSION)+usb\" -DLIBUSB_PRE_1_0_10 cups_genppd_@GUTENPRINT_RELEASE_VERSION@_SOURCES = genppd.c i18n.c i18n.h @@ -1117,7 +1121,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-mitsu70x_print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-mitsu9550_print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-selphy_print.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-shinko_s1245_print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-shinko_s2145_print.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-shinko_s6245_print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-sony_updr150_print.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commandtocanon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commandtoepson.Po@am__quote@ @@ -1304,6 +1310,34 @@ backend_gutenprint-backend_common.obj: backend_common.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o backend_gutenprint-backend_common.obj `if test -f 'backend_common.c'; then $(CYGPATH_W) 'backend_common.c'; else $(CYGPATH_W) '$(srcdir)/backend_common.c'; fi` +backend_gutenprint-shinko_s1245_print.o: shinko_s1245_print.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-shinko_s1245_print.o -MD -MP -MF $(DEPDIR)/backend_gutenprint-shinko_s1245_print.Tpo -c -o backend_gutenprint-shinko_s1245_print.o `test -f 'shinko_s1245_print.c' || echo '$(srcdir)/'`shinko_s1245_print.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-shinko_s1245_print.Tpo $(DEPDIR)/backend_gutenprint-shinko_s1245_print.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shinko_s1245_print.c' object='backend_gutenprint-shinko_s1245_print.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o backend_gutenprint-shinko_s1245_print.o `test -f 'shinko_s1245_print.c' || echo '$(srcdir)/'`shinko_s1245_print.c + +backend_gutenprint-shinko_s1245_print.obj: shinko_s1245_print.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-shinko_s1245_print.obj -MD -MP -MF $(DEPDIR)/backend_gutenprint-shinko_s1245_print.Tpo -c -o backend_gutenprint-shinko_s1245_print.obj `if test -f 'shinko_s1245_print.c'; then $(CYGPATH_W) 'shinko_s1245_print.c'; else $(CYGPATH_W) '$(srcdir)/shinko_s1245_print.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-shinko_s1245_print.Tpo $(DEPDIR)/backend_gutenprint-shinko_s1245_print.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shinko_s1245_print.c' object='backend_gutenprint-shinko_s1245_print.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o backend_gutenprint-shinko_s1245_print.obj `if test -f 'shinko_s1245_print.c'; then $(CYGPATH_W) 'shinko_s1245_print.c'; else $(CYGPATH_W) '$(srcdir)/shinko_s1245_print.c'; fi` + +backend_gutenprint-shinko_s6245_print.o: shinko_s6245_print.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-shinko_s6245_print.o -MD -MP -MF $(DEPDIR)/backend_gutenprint-shinko_s6245_print.Tpo -c -o backend_gutenprint-shinko_s6245_print.o `test -f 'shinko_s6245_print.c' || echo '$(srcdir)/'`shinko_s6245_print.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-shinko_s6245_print.Tpo $(DEPDIR)/backend_gutenprint-shinko_s6245_print.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shinko_s6245_print.c' object='backend_gutenprint-shinko_s6245_print.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o backend_gutenprint-shinko_s6245_print.o `test -f 'shinko_s6245_print.c' || echo '$(srcdir)/'`shinko_s6245_print.c + +backend_gutenprint-shinko_s6245_print.obj: shinko_s6245_print.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-shinko_s6245_print.obj -MD -MP -MF $(DEPDIR)/backend_gutenprint-shinko_s6245_print.Tpo -c -o backend_gutenprint-shinko_s6245_print.obj `if test -f 'shinko_s6245_print.c'; then $(CYGPATH_W) 'shinko_s6245_print.c'; else $(CYGPATH_W) '$(srcdir)/shinko_s6245_print.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-shinko_s6245_print.Tpo $(DEPDIR)/backend_gutenprint-shinko_s6245_print.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shinko_s6245_print.c' object='backend_gutenprint-shinko_s6245_print.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o backend_gutenprint-shinko_s6245_print.obj `if test -f 'shinko_s6245_print.c'; then $(CYGPATH_W) 'shinko_s6245_print.c'; else $(CYGPATH_W) '$(srcdir)/shinko_s6245_print.c'; fi` + cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.o: genppd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cups_genppd_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -MT cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.o -MD -MP -MF $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Tpo -c -o cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.o `test -f 'genppd.c' || echo '$(srcdir)/'`genppd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Tpo $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Po diff --git a/src/cups/backend_common.c b/src/cups/backend_common.c index 0a2aaba..02aa923 100644 --- a/src/cups/backend_common.c +++ b/src/cups/backend_common.c @@ -27,19 +27,41 @@ #include "backend_common.h" -#define BACKEND_VERSION "0.54G" +#define BACKEND_VERSION "0.62G" #ifndef URI_PREFIX #error "Must Define URI_PREFIX" #endif -/* Global variables */ +#define NUM_CLAIM_ATTEMPTS 10 + +/* Global Variables */ int dyesub_debug = 0; +int terminate = 0; +int fast_return = 0; int extra_vid = -1; int extra_pid = -1; int extra_type = -1; +int copies = 1; char *use_serno = NULL; +int current_page = 0; /* Support Functions */ +static int backend_claim_interface(struct libusb_device_handle *dev, int iface) +{ + int attempts = NUM_CLAIM_ATTEMPTS; + int ret; + do { + ret = libusb_claim_interface(dev, iface); + if (!ret) + break; + sleep(1); + } while (--attempts > 0); + + if (ret) + ERROR("Printer open failure (Could not claim printer interface after %d attempts) (%d)\n", NUM_CLAIM_ATTEMPTS, ret); + + return ret; +} #define ID_BUF_SIZE 2048 static char *get_device_id(struct libusb_device_handle *dev) @@ -56,7 +78,8 @@ static char *get_device_id(struct libusb_device_handle *dev) if (libusb_kernel_driver_active(dev, iface)) libusb_detach_kernel_driver(dev, iface); - libusb_claim_interface(dev, iface); + if (backend_claim_interface(dev, iface)) + return NULL; if (libusb_control_transfer(dev, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_IN | @@ -97,7 +120,6 @@ done: } /* Used with the IEEE1284 deviceid string parsing */ - struct deviceid_dict { char *key; char *val; @@ -112,6 +134,9 @@ static int parse1284_data(const char *device_id, struct deviceid_dict* dict) char val[256]; int num = 0; + if (!device_id) + return 0; + //[whitespace]key[whitespace]:[whitespace]value[whitespace]; while (*device_id && num < MAX_DICT) { /* Skip leading spaces */ @@ -165,7 +190,6 @@ static char *dict_find(const char *key, int dlen, struct deviceid_dict* dict) } /* I/O functions */ - int read_data(struct libusb_device_handle *dev, uint8_t endp, uint8_t *buf, int buflen, int *readlen) { @@ -254,8 +278,6 @@ int send_data(struct libusb_device_handle *dev, uint8_t endp, } /* More stuff */ -int terminate = 0; - static void sigterm_handler(int signum) { UNUSED(signum); @@ -339,7 +361,7 @@ static char *url_decode(char *str) { static int print_scan_output(struct libusb_device *device, struct libusb_device_descriptor *desc, char *prefix, char *manuf2, - int found, int match, + int found, int scan_only, char *match_serno, struct dyesub_backend *backend) { @@ -411,7 +433,11 @@ static int print_scan_output(struct libusb_device *device, descr = malloc(256); if (!descr) { ERROR("Memory allocation failure (%d bytes)\n", 256); - return found; + if (manuf3) + free(manuf3); + if (product2) + free(product2); + return -1; } sprintf(descr, "%s %s", manuf3, product2); @@ -434,17 +460,16 @@ static int print_scan_output(struct libusb_device *device, serial = url_encode(buf); } else if (backend->query_serno) { /* Get from backend hook */ int iface = 0; + struct libusb_config_descriptor *config; if (libusb_kernel_driver_active(dev, iface)) libusb_detach_kernel_driver(dev, iface); - /* If we fail to claim the printer, it's already in use - so we should just skip over it... */ - buf[0] = 0; - if (!libusb_claim_interface(dev, iface)) { + /* Try to claim the printer, and handle transient failures */ + if (!backend_claim_interface(dev, iface)) { int i; - uint8_t endp_up, endp_down; + uint8_t endp_up = 0, endp_down = 0; libusb_get_active_config_descriptor(device, &config); for (i = 0 ; i < config->interface[0].altsetting[0].bNumEndpoints ; i++) { if ((config->interface[0].altsetting[0].endpoint[i].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_BULK) { @@ -455,6 +480,7 @@ static int print_scan_output(struct libusb_device *device, } } + buf[0] = 0; /* Ignore result since a failure isn't critical here */ backend->query_serno(dev, endp_up, endp_down, buf, STR_LEN_MAX); libusb_release_interface(dev, iface); @@ -471,8 +497,7 @@ static int print_scan_output(struct libusb_device *device, } if (dyesub_debug) - DEBUG("%sVID: %04X PID: %04X Manuf: '%s' Product: '%s' Serial: '%s'\n", - match ? "MATCH: " : "", + DEBUG("VID: %04X PID: %04X Manuf: '%s' Product: '%s' Serial: '%s'\n", desc->idVendor, desc->idProduct, manuf, product, serial); if (scan_only) { @@ -518,12 +543,29 @@ abort: return found; } +extern struct dyesub_backend updr150_backend; +extern struct dyesub_backend kodak6800_backend; +extern struct dyesub_backend kodak605_backend; +extern struct dyesub_backend kodak1400_backend; +extern struct dyesub_backend shinkos1245_backend; +extern struct dyesub_backend shinkos2145_backend; +extern struct dyesub_backend shinkos6145_backend; +extern struct dyesub_backend shinkos6245_backend; +extern struct dyesub_backend canonselphy_backend; +extern struct dyesub_backend mitsu70x_backend; +extern struct dyesub_backend mitsu9550_backend; +extern struct dyesub_backend dnpds40_backend; +extern struct dyesub_backend cw01_backend; + static struct dyesub_backend *backends[] = { &canonselphy_backend, &kodak6800_backend, &kodak605_backend, &kodak1400_backend, + &shinkos1245_backend, &shinkos2145_backend, +// &shinkos6145_backend, + &shinkos6245_backend, &updr150_backend, &mitsu70x_backend, &mitsu9550_backend, @@ -536,11 +578,10 @@ static int find_and_enumerate(struct libusb_context *ctx, struct libusb_device ***list, struct dyesub_backend *backend, char *match_serno, - int printer_type, int scan_only) { int num; - int i, j, k; + int i, j = 0, k; int found = -1; /* Enumerate and find suitable device */ @@ -548,45 +589,36 @@ static int find_and_enumerate(struct libusb_context *ctx, for (i = 0 ; i < num ; i++) { struct libusb_device_descriptor desc; - int match = 0; libusb_get_device_descriptor((*list)[i], &desc); for (k = 0 ; backends[k] ; k++) { if (backend && backend != backends[k]) continue; for (j = 0 ; backends[k]->devices[j].vid ; j++) { + if (extra_pid != -1 && + extra_vid != -1 && + extra_type != -1) { + if (backends[k]->devices[j].type == extra_type && + extra_vid == desc.idVendor && + extra_pid == desc.idProduct) { + found = i; + goto match; + } + } if (desc.idVendor == backends[k]->devices[j].vid && desc.idProduct == backends[k]->devices[j].pid) { - match = 1; - if (printer_type == P_ANY || - printer_type == backends[k]->devices[j].type) - found = i; + found = i; goto match; } } } - match: - if (!match) { - if (extra_pid != -1 && - extra_vid != -1 && - extra_type != -1) { - if (extra_vid == desc.idVendor && - extra_pid == desc.idProduct) { - match = 1; - if (printer_type == P_ANY || - printer_type == extra_type) - found = i; - } - } - } - - if (!match) - continue; + continue; + match: found = print_scan_output((*list)[i], &desc, URI_PREFIX, backends[k]->devices[j].manuf_str, - found, (found == i), + found, scan_only, match_serno, backends[k]); @@ -615,7 +647,7 @@ static struct dyesub_backend *find_backend(char *uri_prefix) return NULL; } -static void print_license_blurb(void) +void print_license_blurb(void) { const char *license = "\n\ Copyright 2007-2015 Solomon Peachy <pizza AT shaftnet DOT org>\n\ @@ -638,7 +670,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n\ fprintf(stderr, "%s", license); } -static void print_help(char *argv0, struct dyesub_backend *backend) +void print_help(char *argv0, struct dyesub_backend *backend) { struct libusb_context *ctx = NULL; struct libusb_device **list = NULL; @@ -655,21 +687,24 @@ static void print_help(char *argv0, struct dyesub_backend *backend) if (!backend) { int i; + DEBUG("Environment variables:\n"); + DEBUG(" DYESUB_DEBUG EXTRA_PID EXTRA_VID EXTRA_TYPE BACKEND SERIAL\n"); DEBUG("CUPS Usage:\n"); DEBUG("\tDEVICE_URI=someuri %s job user title num-copies options [ filename ]\n", URI_PREFIX); DEBUG("\n"); DEBUG("Standalone Usage:\n"); DEBUG("\t%s\n", URI_PREFIX); - DEBUG(" [ -D ] [ -G ]\n"); - DEBUG(" [ -S serialnum ] [ -B backendname ] \n"); + DEBUG(" [ -D ] [ -G ] [ -f ]\n"); + DEBUG(" [ -S serialnum ] \n"); DEBUG(" [ -V extra_vid ] [ -P extra_pid ] [ -T extra_type ] \n"); DEBUG(" [ backend_specific_args ] \n"); - DEBUG(" [ -d copies ] [ - | infile ] \n"); + DEBUG(" [ -d copies ] \n"); + DEBUG(" [ - | infile ] \n"); for (i = 0; ; i++) { backend = backends[i]; if (!backend) break; - DEBUG(" -B %s\t# %s version %s\n", + DEBUG(" BACKEND=%s\t# %s version %s\n", backend->uri_prefix, backend->name, backend->version); if (backend->cmdline_usage) backend->cmdline_usage(); @@ -685,12 +720,13 @@ static void print_help(char *argv0, struct dyesub_backend *backend) DEBUG("\t[ -d copies ] [ infile | - ]\n"); } + /* Probe for printers */ i = libusb_init(&ctx); if (i) { ERROR("Failed to initialize libusb (%d)\n", i); exit(CUPS_BACKEND_STOP); } - find_and_enumerate(ctx, &list, backend, NULL, P_ANY, 1); + find_and_enumerate(ctx, &list, backend, NULL, 1); libusb_free_device_list(list, 1); libusb_exit(ctx); } @@ -712,18 +748,14 @@ int main (int argc, char **argv) int i; int claimed; - int backend_cmd = 0; int ret = CUPS_BACKEND_OK; int iface = 0; int found = -1; - int copies = 1; int jobid = 0; - int pages = 0; char *uri; char *fname = NULL; - int printer_type = P_ANY; DEBUG("Multi-Call Dye-sublimation CUPS Backend version %s\n", BACKEND_VERSION); @@ -734,7 +766,7 @@ int main (int argc, char **argv) /* First pass at cmdline parsing */ if (getenv("DYESUB_DEBUG")) - dyesub_debug++; + dyesub_debug = atoi(getenv("DYESUB_DEBUG")); if (getenv("EXTRA_PID")) extra_pid = strtol(getenv("EXTRA_PID"), NULL, 16); if (getenv("EXTRA_VID")) @@ -743,114 +775,26 @@ int main (int argc, char **argv) extra_type = atoi(getenv("EXTRA_TYPE")); if (getenv("BACKEND")) backend = find_backend(getenv("BACKEND")); - use_serno = getenv("DEVICE"); - uri = getenv("DEVICE_URI"); /* For CUPS */ - - /* Try to ensure we have a sane backend for standalone mode. - CUPS mode uses 'uri' later on. */ - if (!backend) { - char *ptr = strrchr(argv[0], '/'); - if (ptr) - ptr++; - else - ptr = argv[0]; - backend = find_backend(ptr); - } - - /* Reset arg parsing */ - optind = 1; - opterr = 0; - while ((i = getopt(argc, argv, "B:d:DGhP:S:T:V:")) >= 0) { - switch(i) { - case 'B': - backend = find_backend(optarg); - if (!backend) { - fprintf(stderr, "ERROR: Unknown backend '%s'\n", optarg); - } - break; - case 'd': - copies = atoi(optarg); - break; - case 'D': - dyesub_debug++; - break; - case 'G': - print_license_blurb(); - exit(0); - case 'h': - print_help(argv[0], backend); - exit(0); - break; - case 'P': - extra_pid = strtol(optarg, NULL, 16); - break; - case 'S': - use_serno = optarg; - break; - case 'T': - extra_type = atoi(optarg); - break; - case 'V': - extra_pid = strtol(optarg, NULL, 16); - break; - case '?': - default: { - /* Check to see if it is claimed by the backend */ - if (backend && backend->cmdline_arg) { - int keep = optind; - backend_cmd += backend->cmdline_arg(NULL, argc, argv); - optind = keep; - } - break; - } - } - } + if (getenv("FAST_RETURN")) + fast_return++; + use_serno = getenv("SERIAL"); + uri = getenv("DEVICE_URI"); /* CUPS backend mode? */ -#ifndef LIBUSB_PRE_1_0_10 - if (dyesub_debug) { - const struct libusb_version *ver; - ver = libusb_get_version(); - DEBUG(" ** running with libusb %d.%d.%d%s (%d)\n", - ver->major, ver->minor, ver->micro, (ver->rc? ver->rc : ""), ver->nano ); - } -#endif - - /* Make sure a filename was specified */ - if (!backend_cmd && (optind == argc || !argv[optind])) { - print_help(argv[0], backend); - exit(0); - } - - /* Are we running as a CUPS backend? */ if (uri) { - int base = optind; // XXX aka 1. - fname = argv[base + 5]; + /* CUPS backend mode */ + int base = optind; /* ie 1 */ + if (argc < 6) { + ERROR("Insufficient arguments\n"); + exit(1); + } if (argv[base]) jobid = atoi(argv[base]); if (argv[base + 3]) copies = atoi(argv[base + 3]); - if (fname) { /* IOW, is it specified? */ - data_fd = open(fname, O_RDONLY); - if (data_fd < 0) { - perror("ERROR:Can't open input file"); - exit(1); - } - } else { + if (argc > 6) + fname = argv[base + 5]; + else fname = "-"; - } - - /* Ensure we're using BLOCKING I/O */ - i = fcntl(data_fd, F_GETFL, 0); - if (i < 0) { - perror("ERROR:Can't open input"); - exit(1); - } - i &= ~O_NONBLOCK; - i = fcntl(data_fd, F_SETFL, 0); - if (i < 0) { - perror("ERROR:Can't open input"); - exit(1); - } /* Figure out backend based on URI */ { @@ -884,46 +828,34 @@ int main (int argc, char **argv) if (ptr) *ptr = 0; } - } else { - srand(getpid()); - jobid = rand(); - /* Grab the filename */ - fname = argv[optind]; + /* Always enable fast return in CUPS mode */ + fast_return++; + } else { + /* Standalone mode */ - if (!fname && !backend_cmd) { - perror("ERROR:No input file"); - exit(1); - } - if (fname) { - /* Open Input File */ - if (strcmp("-", fname)) { - data_fd = open(fname, O_RDONLY); - if (data_fd < 0) { - perror("ERROR:Can't open input file"); - exit(1); - } - } + /* Try to guess backend from executable name */ + if (!backend) { + char *ptr = strrchr(argv[0], '/'); + if (ptr) + ptr++; + else + ptr = argv[0]; + backend = find_backend(ptr); } + + srand(getpid()); + jobid = rand(); } - /* Ignore SIGPIPE */ - signal(SIGPIPE, SIG_IGN); - signal(SIGTERM, sigterm_handler); - - /* Initialize backend */ - DEBUG("Initializing '%s' backend (version %s)\n", - backend->name, backend->version); - backend_ctx = backend->init(); - - /* Parse printjob if necessary */ - if (fname && backend->early_parse) { - printer_type = backend->early_parse(backend_ctx, data_fd); - if (printer_type < 0) { - ret = CUPS_BACKEND_CANCEL; - goto done; - } +#ifndef LIBUSB_PRE_1_0_10 + if (dyesub_debug) { + const struct libusb_version *ver; + ver = libusb_get_version(); + DEBUG(" ** running with libusb %d.%d.%d%s (%d)\n", + ver->major, ver->minor, ver->micro, (ver->rc? ver->rc : ""), ver->nano ); } +#endif /* Libusb setup */ ret = libusb_init(&ctx); @@ -933,16 +865,30 @@ int main (int argc, char **argv) goto done; } + /* If we don't have a valid backend, print help and terminate */ + if (!backend) { + print_help(argv[0], NULL); // probes all devices + exit(1); + } + + /* If we're in standalone mode, print help only if no args */ + if (!uri) { + if (argc < 2) { + print_help(argv[0], backend); // probes all devices + exit(1); + } + } + /* Enumerate devices */ - found = find_and_enumerate(ctx, &list, backend, use_serno, printer_type, 0); + found = find_and_enumerate(ctx, &list, backend, use_serno, 0); -#if 1 if (found == -1) { - ERROR("Printer open failure (No suitable printers found!)\n"); + ERROR("Printer open failure (No matching printers found!)\n"); ret = CUPS_BACKEND_HOLD; goto done; } + /* Open an appropriate device */ ret = libusb_open(list[found], &dev); if (ret) { ERROR("Printer open failure (Need to be root?) (%d)\n", ret); @@ -960,9 +906,8 @@ int main (int argc, char **argv) } } - ret = libusb_claim_interface(dev, iface); + ret = backend_claim_interface(dev, iface); if (ret) { - ERROR("Printer open failure (Could not claim printer interface) (%d)\n", ret); ret = CUPS_BACKEND_STOP; goto done_close; } @@ -982,35 +927,69 @@ int main (int argc, char **argv) endp_down = config->interface[0].altsetting[0].endpoint[i].bEndpointAddress; } } -#endif + + /* Initialize backend */ + DEBUG("Initializing '%s' backend (version %s)\n", + backend->name, backend->version); + backend_ctx = backend->init(); + /* Attach backend to device */ backend->attach(backend_ctx, dev, endp_up, endp_down, jobid); - if (backend_cmd && !uri) { - if (backend->cmdline_arg(backend_ctx, argc, argv)) - goto done_claimed; - if (!fname) + if (!uri) { + if (backend->cmdline_arg(backend_ctx, argc, argv) < 0) goto done_claimed; + + /* Grab the filename */ + fname = argv[optind]; // XXX do this a smarter way? } + if (!fname) { + if (uri) + fprintf(stderr, "ERROR: No input file specified\n"); + goto done_claimed; + } + + /* Open file if not STDIN */ + if (strcmp("-", fname)) { + data_fd = open(fname, O_RDONLY); + if (data_fd < 0) { + perror("ERROR:Can't open input file"); + exit(1); + } + } + + /* Ensure we're using BLOCKING I/O */ + i = fcntl(data_fd, F_GETFL, 0); + if (i < 0) { + perror("ERROR:Can't open input"); + exit(1); + } + i &= ~O_NONBLOCK; + i = fcntl(data_fd, F_SETFL, i); + if (i < 0) { + perror("ERROR:Can't open input"); + exit(1); + } + + /* Ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + signal(SIGTERM, sigterm_handler); + /* Time for the main processing loop */ INFO("Printing started (%d copies)\n", copies); newpage: - /* Do early parsing if needed for subsequent pages */ - if (pages && backend->early_parse && - backend->early_parse(backend_ctx, data_fd) < 0) - goto done_multiple; /* Read in data */ if ((ret = backend->read_parse(backend_ctx, data_fd))) { - if (pages) + if (current_page) goto done_multiple; else goto done_claimed; } - INFO("Printing page %d\n", ++pages); + INFO("Printing page %d\n", ++current_page); ret = backend->main_loop(backend_ctx, copies); if (ret) @@ -1025,7 +1004,7 @@ done_multiple: close(data_fd); /* Done printing */ - INFO("All printing done (%d pages * %d copies)\n", pages, copies); + INFO("All printing done (%d pages * %d copies)\n", current_page, copies); ret = CUPS_BACKEND_OK; done_claimed: @@ -1050,3 +1029,47 @@ done: return ret; } +int lookup_printer_type(struct dyesub_backend *backend, uint16_t idVendor, uint16_t idProduct) +{ + int i; + int type = -1; + + for (i = 0 ; backend->devices[i].vid ; i++) { + if (extra_pid != -1 && + extra_vid != -1 && + extra_type != -1) { + if (backend->devices[i].type == extra_type && + extra_vid == idVendor && + extra_pid == idProduct) { + return extra_type; + } + } + if (idVendor == backend->devices[i].vid && + idProduct == backend->devices[i].pid) { + return backend->devices[i].type; + } + } + + return type; +} + +uint16_t uint16_to_packed_bcd(uint16_t val) +{ + uint16_t bcd; + uint16_t i; + + /* Handle from 0-9999 */ + i = val % 10; + bcd = i; + val /= 10; + i = val % 10; + bcd |= (i << 4); + val /= 10; + i = val % 10; + bcd |= (i << 8); + val /= 10; + i = val % 10; + bcd |= (i << 12); + + return bcd; +} diff --git a/src/cups/backend_common.h b/src/cups/backend_common.h index 1a5c9f1..9b32071 100644 --- a/src/cups/backend_common.h +++ b/src/cups/backend_common.h @@ -91,7 +91,6 @@ enum { P_ES1, P_ES2_20, P_ES3_30, - P_ES40_CP790, P_ES40, P_CP790, P_CP_XXX, @@ -102,12 +101,17 @@ enum { P_KODAK_605, P_SHINKO_S2145, P_SHINKO_S1245, + P_SHINKO_S6245, + P_SHINKO_S6145, P_SONY_UPDR150, P_SONY_UPCR10, P_MITSU_D70X, + P_MITSU_K60, P_MITSU_9550, + P_MITSU_9550S, P_DNP_DS40, P_DNP_DS80, + P_DNP_DS80D, P_CITIZEN_CW01, P_DNP_DSRX1, P_DNP_DS620, @@ -132,7 +136,6 @@ struct dyesub_backend { uint8_t endp_up, uint8_t endp_down, uint8_t jobid); void (*teardown)(void *ctx); int (*cmdline_arg)(void *ctx, int argc, char **argv); - int (*early_parse)(void *ctx, int data_fd); int (*read_parse)(void *ctx, int data_fd); int (*main_loop)(void *ctx, int copies); int (*query_serno)(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len); @@ -144,22 +147,27 @@ int send_data(struct libusb_device_handle *dev, uint8_t endp, uint8_t *buf, int len); int read_data(struct libusb_device_handle *dev, uint8_t endp, uint8_t *buf, int buflen, int *readlen); +int lookup_printer_type(struct dyesub_backend *backend, uint16_t idVendor, uint16_t idProduct); -/* Exported data */ +void print_license_blurb(void); +void print_help(char *argv0, struct dyesub_backend *backend); + +uint16_t uint16_to_packed_bcd(uint16_t val); + +/* Global data */ extern int terminate; extern int dyesub_debug; - -/* External data */ -extern struct dyesub_backend updr150_backend; -extern struct dyesub_backend kodak6800_backend; -extern struct dyesub_backend kodak605_backend; -extern struct dyesub_backend kodak1400_backend; -extern struct dyesub_backend shinkos2145_backend; -extern struct dyesub_backend canonselphy_backend; -extern struct dyesub_backend mitsu70x_backend; -extern struct dyesub_backend mitsu9550_backend; -extern struct dyesub_backend dnpds40_backend; -extern struct dyesub_backend cw01_backend; +extern int fast_return; +extern int extra_vid; +extern int extra_pid; +extern int extra_type; +extern int copies; +extern char *use_serno; +extern int current_page; + +#if defined(BACKEND) +extern struct dyesub_backend BACKEND; +#endif /* CUPS compatibility */ #define CUPS_BACKEND_OK 0 /* Sucess */ @@ -171,4 +179,35 @@ extern struct dyesub_backend cw01_backend; #define CUPS_BACKEND_RETRY 6 /* Retry later */ #define CUPS_BACKEND_RETRY_CURRENT 7 /* Retry immediately */ +/* Argument processing */ +#define GETOPT_LIST_GLOBAL "d:DfGhP:S:T:V:" +#define GETOPT_PROCESS_GLOBAL \ + case 'd': \ + copies = atoi(optarg); \ + break; \ + case 'D': \ + dyesub_debug++; \ + break; \ + case 'f': \ + fast_return++; \ + break; \ + case 'G': \ + print_license_blurb(); \ + exit(0); \ + case 'h': \ + print_help(argv[0], &BACKEND); \ + exit(0); \ + case 'P': \ + extra_pid = strtol(optarg, NULL, 16); \ + break; \ + case 'S': \ + use_serno = optarg; \ + break; \ + case 'T': \ + extra_type = atoi(optarg); \ + break; \ + case 'V': \ + extra_pid = strtol(optarg, NULL, 16); \ + break; + #endif /* __BACKEND_COMMON_H */ diff --git a/src/cups/blacklist b/src/cups/blacklist index 420c485..b137624 100644 --- a/src/cups/blacklist +++ b/src/cups/blacklist @@ -132,6 +132,9 @@ # Sony UP-DR200 0x054c 0x035f blacklist +# Sony UP-CR10L +0x054c 0x0226 blacklist + # Mitsubishi CP-D70/CP-D707 0x06d3 0x3b30 blacklist diff --git a/src/cups/citizencw01_print.c b/src/cups/citizencw01_print.c index b99822c..37313b5 100644 --- a/src/cups/citizencw01_print.c +++ b/src/cups/citizencw01_print.c @@ -35,6 +35,8 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND cw01_backend + #include "backend_common.h" #define USB_VID_CITIZEN 0x1343 @@ -68,6 +70,7 @@ struct cw01_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; uint8_t *databuf; struct cw01_spool_hdr hdr; @@ -296,12 +299,20 @@ static void cw01_attach(void *vctx, struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct cw01_ctx *ctx = vctx; + struct libusb_device *device; + struct libusb_device_descriptor desc; UNUSED(jobid); ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; + + device = libusb_get_device(dev); + libusb_get_device_descriptor(device, &desc); + + ctx->type = lookup_printer_type(&cw01_backend, + desc.idVendor, desc.idProduct); } static void cw01_teardown(void *vctx) { @@ -434,7 +445,8 @@ top: free(resp); resp = NULL; - /* Set print quantity */ + /* Set print quantity */ // XXX check against remaining print count + cw01_build_cmd(&cmd, "CNTRL", "QTY", 8); snprintf(buf, sizeof(buf), "%07d\r", copies); ret = cw01_do_cmd(ctx, &cmd, (uint8_t*) buf, 8); @@ -498,7 +510,7 @@ top: ptr + SPOOL_PLANE_HDR_LEN, ctx->hdr.plane_len - SPOOL_PLANE_HDR_LEN))) return CUPS_BACKEND_FAILED; - ptr += ctx->hdr.plane_len; + /* ptr += ctx->hdr.plane_len; */ /* Start print */ cw01_build_cmd(&cmd, "CNTRL", "START", 0); @@ -506,18 +518,7 @@ top: if (ret) return CUPS_BACKEND_FAILED; - /* This printer handles copies internally */ - copies = 1; - - /* Clean up */ - if (terminate) - copies = 1; - - INFO("Print complete (%d copies remaining)\n", copies - 1); - - if (copies && --copies) { - goto top; - } + INFO("Print complete\n"); if (resp) free(resp); @@ -822,38 +823,30 @@ static int cw01_cmdline_arg(void *vctx, int argc, char **argv) struct cw01_ctx *ctx = vctx; int i, j = 0; + if (!ctx) + return -1; + /* Reset arg parsing */ optind = 1; opterr = 0; - while ((i = getopt(argc, argv, "inN:s")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "inN:s")) >= 0) { switch(i) { + GETOPT_PROCESS_GLOBAL case 'i': - if (ctx) { - j = cw01_get_info(ctx); - break; - } - return 1; + j = cw01_get_info(ctx); + break; case 'n': - if (ctx) { - j = cw01_get_counters(ctx); - break; - } - return 1; + j = cw01_get_counters(ctx); + break; case 'N': if (optarg[0] != 'A' && optarg[0] != 'B') return CUPS_BACKEND_FAILED; - if (ctx) { - j = cw01_clear_counter(ctx, optarg[0]); - break; - } - return 1; + j = cw01_clear_counter(ctx, optarg[0]); + break; case 's': - if (ctx) { - j = cw01_get_status(ctx); - break; - } - return 1; + j = cw01_get_status(ctx); + break; default: break; /* Ignore completely */ } @@ -867,7 +860,7 @@ static int cw01_cmdline_arg(void *vctx, int argc, char **argv) /* Exported */ struct dyesub_backend cw01_backend = { .name = "Citizen CW-01", - .version = "0.10", + .version = "0.12", .uri_prefix = "citizencw01", .cmdline_usage = cw01_cmdline, .cmdline_arg = cw01_cmdline_arg, diff --git a/src/cups/dnpds40_print.c b/src/cups/dnpds40_print.c index 16e8ab1..47ff498 100644 --- a/src/cups/dnpds40_print.c +++ b/src/cups/dnpds40_print.c @@ -44,6 +44,8 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND dnpds40_backend + #include "backend_common.h" #define USB_VID_CITIZEN 0x1343 @@ -82,6 +84,7 @@ struct dnpds40_ctx { int cutter; int can_rewind; + int manual_copies; int supports_6x9; int supports_2x6; int supports_3x5x2; @@ -90,6 +93,8 @@ struct dnpds40_ctx { int supports_rewind; int supports_standby; int supports_6x4_5; + int supports_mqty_default; + int supports_iserial; uint8_t *qty_offset; uint8_t *buffctrl_offset; @@ -328,6 +333,9 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, device = libusb_get_device(dev); libusb_get_device_descriptor(device, &desc); + ctx->type = lookup_printer_type(&dnpds40_backend, + desc.idVendor, desc.idProduct); + { /* Get Firmware Version */ struct dnpds40_cmd cmd; @@ -343,7 +351,7 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->version = strdup((char*) resp); /* Parse version */ - ptr = strtok((char*)resp, " ."); + /* ptr = */ strtok((char*)resp, " ."); ptr = strtok(NULL, "."); ctx->ver_major = atoi(ptr); ptr = strtok(NULL, "."); @@ -397,9 +405,8 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, #endif /* Per-printer options */ - switch (desc.idProduct) { - case USB_PID_DNP_DS40: - ctx->type = P_DNP_DS40; + switch (ctx->type) { + case P_DNP_DS40: ctx->supports_6x9 = 1; if (FW_VER_CHECK(1,30)) ctx->supports_matte = 1; @@ -410,31 +417,31 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, if (FW_VER_CHECK(1,51)) ctx->supports_fullcut = 1; break; - case USB_PID_DNP_DS80: - ctx->type = P_DNP_DS80; + case P_DNP_DS80: if (FW_VER_CHECK(1,30)) ctx->supports_matte = 1; break; - case USB_PID_DNP_DSRX1: - ctx->type = P_DNP_DSRX1; + case P_DNP_DSRX1: ctx->supports_matte = 1; + ctx->supports_mqty_default = 1; // 1.10 does. Maybe older too? if (FW_VER_CHECK(1,10)) ctx->supports_2x6 = 1; break; - case USB_PID_DNP_DS620: - ctx->type = P_DNP_DS620; + case P_DNP_DS620: ctx->supports_matte = 1; ctx->supports_2x6 = 1; ctx->supports_fullcut = 1; - ctx->supports_rewind = 1; // XXX DS620 only, 620A does not. + ctx->supports_mqty_default = 1; + ctx->supports_rewind = 1; ctx->supports_standby = 1; + ctx->supports_iserial = 1; if (FW_VER_CHECK(0,30)) ctx->supports_3x5x2 = 1; if (FW_VER_CHECK(1,10)) ctx->supports_6x9 = ctx->supports_6x4_5 = 1; break; default: - ERROR("Unknown USB PID...\n"); + ERROR("Unknown vid/pid %04x/%04x (%d)\n", desc.idVendor, desc.idProduct, ctx->type); return; } } @@ -491,6 +498,7 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { matte = 0; dpi = 0; cutter = 0; + ctx->manual_copies = 0; ctx->multicut = 0; ctx->buffctrl_offset = ctx->qty_offset = ctx->multicut_offset = 0; @@ -603,7 +611,7 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { if (y_ppm != 1920) { ERROR("Incorrect horizontal resolution (%d), aborting!\n", y_ppm); return CUPS_BACKEND_CANCEL; - } + } } } @@ -616,8 +624,8 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { } if (!ctx->datalen) - return CUPS_BACKEND_CANCEL; - + return CUPS_BACKEND_CANCEL; + /* Figure out the number of buffers we need. Most only need one. */ if (ctx->multicut) { ctx->buf_needed = 1; @@ -668,12 +676,14 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { } break; case 210: //"5x7 (2L)" - ctx->can_rewind = 1; if (ctx->multicut != 1 && ctx->multicut != 3 && ctx->multicut != 22 && ctx->multicut != 29) { ERROR("Incorrect media for job loaded (%d vs %d)\n", ctx->media, ctx->multicut); return CUPS_BACKEND_CANCEL; } + /* Only 3.5x5 on 7x5 media can be rewound */ + if (ctx->multicut == 1) + ctx->can_rewind = 1; break; case 300: //"6x4 (PC)" if (ctx->multicut != 2) { @@ -682,21 +692,27 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { } break; case 310: //"6x8 (A5)" - ctx->can_rewind = 1; if (ctx->multicut != 2 && ctx->multicut != 4 && - ctx->multicut != 27) { + ctx->multicut != 12 && + ctx->multicut != 27 && ctx->multicut != 30) { ERROR("Incorrect media for job loaded (%d vs %d)\n", ctx->media, ctx->multicut); return CUPS_BACKEND_CANCEL; } + /* Only 6x4 on 6x8 media can be rewound */ + if (ctx->multicut == 2) + ctx->can_rewind = 1; break; case 400: //"6x9 (A5W)" - ctx->can_rewind = 1; if (ctx->multicut != 2 && ctx->multicut != 4 && - ctx->multicut != 5 && ctx->multicut != 27 && + ctx->multicut != 5 && ctx->multicut != 12 && + ctx->multicut != 27 && ctx->multicut != 30 && ctx->multicut != 31) { ERROR("Incorrect media for job loaded (%d vs %d)\n", ctx->media, ctx->multicut); return CUPS_BACKEND_CANCEL; } + /* Only 6x4 or 6x4.5 on 6x9 media can be rewound */ + if (ctx->multicut == 2 || ctx->multicut == 30) + ctx->can_rewind = 1; break; case 500: //"8x10" if (ctx->multicut < 6 || ctx->multicut == 7 || @@ -749,6 +765,11 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { ERROR("Printer only supports 2-inch cuts on 4x6 or 8x6 jobs!"); return CUPS_BACKEND_CANCEL; } + + /* Work around firmware bug on DS40 where if we run out + of media, we can't resume the job without losing the + cutter setting. XXX add version test? */ + ctx->manual_copies = 1; } if (ctx->matte && !ctx->supports_matte) { @@ -768,65 +789,15 @@ static int dnpds40_main_loop(void *vctx, int copies) { uint8_t *ptr; char buf[9]; int status; + int buf_needed; if (!ctx) return CUPS_BACKEND_FAILED; - /* Verify we have sufficient media for prints */ - { - int i = 0; - - /* See if we can rewind to save media */ - if (ctx->can_rewind && ctx->supports_rewind && - (ctx->multicut == 1 || ctx->multicut == 2)) { - - /* Get Media remaining */ - dnpds40_build_cmd(&cmd, "INFO", "RQTY", 0); - - if (resp) free(resp); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - dnpds40_cleanup_string((char*)resp, len); - i = atoi((char*)resp); - - /* If the count is odd, we can rewind. */ - if (i & 1) { - snprintf(buf, sizeof(buf), "%08d", ctx->multicut + 400); - memcpy(ctx->multicut_offset, buf, 8); - } - } - - /* If we didn't succeed with RQTY, try MQTY */ - if (i == 0) { - dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0); - - if (resp) free(resp); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - dnpds40_cleanup_string((char*)resp, len); - - i = atoi((char*)resp+4); - - /* For some reason all but the DS620 report 50 too high */ - if (ctx->type != P_DNP_DS620 && i > 0) - i -= 50; - } - - if (i < 1) { - ERROR("Printer out of media, please correct!\n"); - return CUPS_BACKEND_STOP; - } - if (i < copies) { - WARNING("Printer does not have sufficient remaining media to complete job..\n"); - } - } - /* Update quantity offset with count */ - if (copies > 1) { + // XXX this breaks if ctx->manual_copies is set, but the job + // has a CNTRL QTY != 1 + if (!ctx->manual_copies && copies > 1) { snprintf(buf, sizeof(buf), "%07d\r", copies); if (ctx->qty_offset) { memcpy(ctx->qty_offset, buf, 8); @@ -843,7 +814,7 @@ static int dnpds40_main_loop(void *vctx, int copies) { if (ctx->supports_matte) { snprintf(buf, sizeof(buf), "%08d", 1); if (ctx->buffctrl_offset) { - memcpy(ctx->qty_offset, buf, 8); + memcpy(ctx->buffctrl_offset, buf, 8); } else { dnpds40_build_cmd(&cmd, "CNTRL", "BUFFCNTRL", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) @@ -866,9 +837,11 @@ static int dnpds40_main_loop(void *vctx, int copies) { } #endif + buf_needed = ctx->buf_needed; + #ifdef MATTE_GLOSSY_2BUF if (ctx->matte != ctx->last_matte) - ctx->buf_needed = 2; /* Switching needs both buffers */ + buf_needed = 2; /* Switching needs both buffers */ #endif ctx->last_matte = ctx->matte; @@ -888,25 +861,23 @@ static int dnpds40_main_loop(void *vctx, int copies) { top: - if (resp) free(resp); - /* Query status */ dnpds40_build_cmd(&cmd, "STATUS", "", 0); + if (resp) free(resp); resp = dnpds40_resp_cmd(ctx, &cmd, &len); if (!resp) return CUPS_BACKEND_FAILED; dnpds40_cleanup_string((char*)resp, len); status = atoi((char*)resp); - free(resp); /* Figure out what's going on */ switch(status) { case 0: /* Idle; we can continue! */ - break; case 1: /* Printing */ { int bufs; + if (resp) free(resp); /* Query buffer state */ dnpds40_build_cmd(&cmd, "INFO", "FREE_PBUFFER", 0); resp = dnpds40_resp_cmd(ctx, &cmd, &len); @@ -917,8 +888,8 @@ top: dnpds40_cleanup_string((char*)resp, len); /* Check to see if we have sufficient buffers */ bufs = atoi(((char*)resp)+3); - if (bufs < ctx->buf_needed) { - INFO("Insufficient printer buffers (%d vs %d), retrying...\n", bufs, ctx->buf_needed); + if (bufs < buf_needed) { + INFO("Insufficient printer buffers (%d vs %d), retrying...\n", bufs, buf_needed); sleep(1); goto top; } @@ -952,7 +923,56 @@ top: ERROR("Fatal Printer Error: %d => %s, halting queue!\n", status, dnpds40_statuses(status)); return CUPS_BACKEND_HOLD; } - + + /* Verify we have sufficient media for prints */ + { + int i = 0; + + /* See if we can rewind to save media */ + if (ctx->can_rewind && ctx->supports_rewind) { + /* Tell the printer we want to rewind, if possible. */ + snprintf(buf, sizeof(buf), "%08d", ctx->multicut + 400); + memcpy(ctx->multicut_offset, buf, 8); + + /* Get Media remaining */ + dnpds40_build_cmd(&cmd, "INFO", "RQTY", 0); + + if (resp) free(resp); + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + i = atoi((char*)resp+4); + } + + /* If we didn't succeed with RQTY, try MQTY */ + if (i == 0) { + dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0); + + if (resp) free(resp); + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + + i = atoi((char*)resp+4); + + /* For some reason all but the DS620 report 50 too high */ + if (ctx->type != P_DNP_DS620 && i > 0) + i -= 50; + } + + if (i < 1) { + ERROR("Printer out of media, please correct!\n"); + return CUPS_BACKEND_STOP; + } + if (i < copies) { + WARNING("Printer does not have sufficient remaining media to complete job..\n"); + } + } + /* Send the stream over as individual data chunks */ ptr = ctx->databuf; @@ -962,7 +982,6 @@ top: memcpy(buf, ptr + 24, 8); i = atoi(buf) + 32; - if ((ret = send_data(ctx->dev, ctx->endp_down, ptr, i))) return CUPS_BACKEND_FAILED; @@ -973,27 +992,26 @@ top: /* Clean up */ if (terminate) copies = 1; - + INFO("Print complete (%d copies remaining)\n", copies - 1); if (copies && --copies) { +#ifdef MATTE_GLOSSY_2BUF + /* No need to wait on buffers due to matte switching */ + buf_needed = ctx->buf_needed; +#endif goto top; } return CUPS_BACKEND_OK; } -static int dnpds40_get_info(struct dnpds40_ctx *ctx) +static int dnpds40_get_sensors(struct dnpds40_ctx *ctx) { struct dnpds40_cmd cmd; uint8_t *resp; int len = 0; - - /* Serial number already queried */ - INFO("Serial Number: '%s'\n", ctx->serno); - - /* Firmware version already queried */ - INFO("Firmware Version: '%s'\n", ctx->version); + char *tok; /* Get Sensor Info */ dnpds40_build_cmd(&cmd, "INFO", "SENSOR", 0); @@ -1003,33 +1021,72 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) return CUPS_BACKEND_FAILED; dnpds40_cleanup_string((char*)resp, len); - INFO("Sensor Info:\n"); - { - char *tmp; - tmp = strtok((char*)resp, "; "); - do { - // XXX parse the components? - INFO(" %s\n", tmp); - } while ((tmp = strtok(NULL, "; ")) != NULL); - } - free(resp); + tok = strtok((char*)resp, "; -"); + do { + char *val = strtok(NULL, "; -"); + + if (!strcmp("HDT", tok)) { + INFO("Head Temperature : %s\n", val); + } else if (!strcmp("MDT", tok)) { + INFO("Media Temperature : %s\n", val); + } else if (!strcmp("PMK", tok)) { + INFO("Paper Mark : %s\n", val); + } else if (!strcmp("RML", tok)) { + INFO("Ribbon Mark Left : %s\n", val); + } else if (!strcmp("RMC", tok)) { + INFO("Ribbon Mark Right : %s\n", val); + } else if (!strcmp("RMR", tok)) { + INFO("Ribbon Mark Center : %s\n", val); + } else if (!strcmp("PSZ", tok)) { + INFO("Paper Size : %s\n", val); + } else if (!strcmp("PNT", tok)) { + INFO("Paper Notch : %s\n", val); + } else if (!strcmp("PJM", tok)) { + INFO("Paper Jam : %s\n", val); + } else if (!strcmp("PED", tok)) { + INFO("Paper End : %s\n", val); + } else if (!strcmp("PET", tok)) { + INFO("Paper Empty : %s\n", val); + } else if (!strcmp("HDV", tok)) { + INFO("Head Voltage : %s\n", val); + } else if (!strcmp("HMD", tok)) { + INFO("Humidity : %s\n", val); + } else if (!strcmp("RP1", tok)) { + INFO("Roll Paper End 1 : %s\n", val); + } else if (!strcmp("RP2", tok)) { + INFO("Roll Paper End 2 : %s\n", val); + } else if (!strcmp("GSR", tok)) { + INFO("Color Sensor Red : %s\n", val); + } else if (!strcmp("GSG", tok)) { + INFO("Color Sensor Green : %s\n", val); + } else if (!strcmp("GSB", tok)) { + INFO("Color Sensor Blue : %s\n", val); + } else { + INFO("Unknown Sensor: '%s' '%s'\n", + tok, val); + } + } while ((tok = strtok(NULL, "; -")) != NULL); - /* Get Horizonal resolution */ - dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_H", 0); + free(resp); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; + return CUPS_BACKEND_OK; +} - dnpds40_cleanup_string((char*)resp, len); +static int dnpds40_get_info(struct dnpds40_ctx *ctx) +{ + struct dnpds40_cmd cmd; + uint8_t *resp; + int len = 0; - INFO("Horizontal Resolution: '%s' dpi\n", (char*)resp + 3); + /* Serial number already queried */ + INFO("Serial Number: '%s'\n", ctx->serno); - free(resp); + /* Firmware version already queried */ + INFO("Firmware Version: '%s'\n", ctx->version); - /* Get Vertical resolution */ - dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0); + /* Get Media Color offset */ + dnpds40_build_cmd(&cmd, "INFO", "MCOLOR", 0); resp = dnpds40_resp_cmd(ctx, &cmd, &len); if (!resp) @@ -1037,12 +1094,13 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) dnpds40_cleanup_string((char*)resp, len); - INFO("Vertical Resolution: '%s' dpi\n", (char*)resp + 3); + INFO("Media Color Offset: '%02x%02x%02x%02x'\n", *(resp+2), *(resp+3), + *(resp+4), *(resp+5)); free(resp); - /* Get Media Color offset */ - dnpds40_build_cmd(&cmd, "INFO", "MCOLOR", 0); + /* Get Media Class */ + dnpds40_build_cmd(&cmd, "INFO", "MEDIA_CLASS", 0); resp = dnpds40_resp_cmd(ctx, &cmd, &len); if (!resp) @@ -1050,8 +1108,7 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) dnpds40_cleanup_string((char*)resp, len); - INFO("Media Color Offset: '%02x%02x%02x%02x'\n", *(resp+2), *(resp+3), - *(resp+4), *(resp+5)); + INFO("Media Class: '%s'\n", (char*)resp); free(resp); @@ -1085,87 +1142,74 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) free(resp); - if (ctx->type == P_DNP_DS620) { - /* Loop through control data versions and checksums */ - - /* 300 DPI */ - dnpds40_build_cmd(&cmd, "TBL_RD", "CWD300_Version", 0); - - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - dnpds40_cleanup_string((char*)resp, len); - - INFO("300 DPI Color Data Version: '%s' ", (char*)resp); - - free(resp); + /* Get Ribbon ID code (?) */ + dnpds40_build_cmd(&cmd, "MNT_RD", "RIBBON_ID_CODE", 0); - dnpds40_build_cmd(&cmd, "TBL_RD", "CWD300_Checksum", 0); + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; + dnpds40_cleanup_string((char*)resp, len); - dnpds40_cleanup_string((char*)resp, len); + INFO("Ribbon ID(?): '%s'\n", (char*)resp+4); - INFO("Checksum: '%s'\n", (char*)resp); + free(resp); - free(resp); + /* Figure out control data and checksums */ - /* 600 DPI */ - dnpds40_build_cmd(&cmd, "TBL_RD", "CWD600_Version", 0); + /* 300 DPI */ + dnpds40_build_cmd(&cmd, "TBL_RD", "CWD300_Version", 0); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; - dnpds40_cleanup_string((char*)resp, len); + dnpds40_cleanup_string((char*)resp, len); - INFO("600 DPI Color Data Version: '%s' ", (char*)resp); + INFO("300 DPI Color Data Version: '%s' ", (char*)resp); - free(resp); + free(resp); - dnpds40_build_cmd(&cmd, "TBL_RD", "CWD600_Checksum", 0); + dnpds40_build_cmd(&cmd, "TBL_RD", "CWD300_Checksum", 0); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; - dnpds40_cleanup_string((char*)resp, len); + dnpds40_cleanup_string((char*)resp, len); - INFO("Checksum: '%s'\n", (char*)resp); + DEBUG2("Checksum: '%s'\n", (char*)resp); - free(resp); + free(resp); - /* "Low Speed" */ - dnpds40_build_cmd(&cmd, "TBL_RD", "CWD610_Version", 0); + /* 600 DPI */ + dnpds40_build_cmd(&cmd, "TBL_RD", "CWD600_Version", 0); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; - dnpds40_cleanup_string((char*)resp, len); + dnpds40_cleanup_string((char*)resp, len); - INFO("Low Speed Color Data Version: '%s' ", (char*)resp); + INFO("600 DPI Color Data Version: '%s' ", (char*)resp); - free(resp); + free(resp); - dnpds40_build_cmd(&cmd, "TBL_RD", "CWD610_Checksum", 0); + dnpds40_build_cmd(&cmd, "TBL_RD", "CWD600_Checksum", 0); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; - dnpds40_cleanup_string((char*)resp, len); + dnpds40_cleanup_string((char*)resp, len); - INFO("Checksum: '%s'\n", (char*)resp); + DEBUG2("Checksum: '%s'\n", (char*)resp); - free(resp); + free(resp); - } else { - /* Get Color Control Data Version */ - dnpds40_build_cmd(&cmd, "TBL_RD", "Version", 0); + if (ctx->type == P_DNP_DS620) { + /* "Low Speed" */ + dnpds40_build_cmd(&cmd, "TBL_RD", "CWD610_Version", 0); resp = dnpds40_resp_cmd(ctx, &cmd, &len); if (!resp) @@ -1173,12 +1217,11 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) dnpds40_cleanup_string((char*)resp, len); - INFO("Color Data Version: '%s'\n", (char*)resp); + INFO("Low Speed Color Data Version: '%s' ", (char*)resp); free(resp); - /* Get Color Control Data Checksum */ - dnpds40_build_cmd(&cmd, "MNT_RD", "CTRLD_CHKSUM", 0); + dnpds40_build_cmd(&cmd, "TBL_RD", "CWD610_Checksum", 0); resp = dnpds40_resp_cmd(ctx, &cmd, &len); if (!resp) @@ -1186,7 +1229,7 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) dnpds40_cleanup_string((char*)resp, len); - INFO("Color Data Checksum: '%s'\n", (char*)resp); + DEBUG2("Checksum: '%s'\n", (char*)resp); free(resp); } @@ -1217,7 +1260,9 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) INFO("Media End kept across power cycles: '%s'\n", (char*)resp); free(resp); + } + if (ctx->supports_iserial) { /* Get USB serial descriptor status */ dnpds40_build_cmd(&cmd, "MNT_RD", "USB_ISERI_SET", 0); @@ -1284,7 +1329,7 @@ static int dnpds40_get_status(struct dnpds40_ctx *ctx) /* Report media */ INFO("Media Type: '%s'\n", dnpds40_media_types(ctx->media)); - if (ctx->supports_rewind) { + if (ctx->supports_mqty_default) { /* Get Media remaining */ dnpds40_build_cmd(&cmd, "INFO", "MQTY_DEFAULT", 0); @@ -1460,6 +1505,38 @@ static int dnpds620_standby_mode(struct dnpds40_ctx *ctx, int delay) return 0; } +static int dnpds620_media_keep_mode(struct dnpds40_ctx *ctx, int delay) +{ + struct dnpds40_cmd cmd; + char msg[9]; + int ret; + + /* Generate command */ + dnpds40_build_cmd(&cmd, "MNT_WT", "END_KEEP_MODE", 4); + snprintf(msg, sizeof(msg), "%02d\r", delay); + + if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, 4))) + return ret; + + return 0; +} + +static int dnpds620_iserial_mode(struct dnpds40_ctx *ctx, int enable) +{ + struct dnpds40_cmd cmd; + char msg[9]; + int ret; + + /* Generate command */ + dnpds40_build_cmd(&cmd, "MNT_WT", "USB_ISERI_SET", 4); + snprintf(msg, sizeof(msg), "%02d\r", enable); + + if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, 8))) + return ret; + + return 0; +} + static int dnpds40_set_counter_p(struct dnpds40_ctx *ctx, char *arg) { struct dnpds40_cmd cmd; @@ -1480,12 +1557,14 @@ static int dnpds40_set_counter_p(struct dnpds40_ctx *ctx, char *arg) static void dnpds40_cmdline(void) { DEBUG("\t\t[ -i ] # Query printer info\n"); + DEBUG("\t\t[ -I ] # Query sensor info\n"); DEBUG("\t\t[ -s ] # Query status\n"); DEBUG("\t\t[ -n ] # Query counters\n"); DEBUG("\t\t[ -N A|B|M ] # Clear counter A/B/M\n"); DEBUG("\t\t[ -p num ] # Set counter P\n"); - DEBUG("\t\t[ -S num ] # Set standby time (1-99 minutes, 0 disables)\n"); - + DEBUG("\t\t[ -k num ] # Set standby time (1-99 minutes, 0 disables)\n"); + DEBUG("\t\t[ -K num ] # Keep Media Status Across Power Cycles (1 on, 0 off)\n"); + DEBUG("\t\t[ -x num ] # Set USB iSerialNumber Reporting (1 on, 0 off)\n"); } static int dnpds40_cmdline_arg(void *vctx, int argc, char **argv) @@ -1493,64 +1572,86 @@ static int dnpds40_cmdline_arg(void *vctx, int argc, char **argv) struct dnpds40_ctx *ctx = vctx; int i, j = 0; + if (!ctx) + return -1; + /* Reset arg parsing */ optind = 1; opterr = 0; - while ((i = getopt(argc, argv, "inN:p:sS:")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "iInN:p:sK:k:")) >= 0) { switch(i) { + GETOPT_PROCESS_GLOBAL case 'i': - if (ctx) { - j = dnpds40_get_info(ctx); - break; - } - return 1; + j = dnpds40_get_info(ctx); + break; + case 'I': + j = dnpds40_get_sensors(ctx); + break; case 'n': - if (ctx) { - j = dnpds40_get_counters(ctx); - break; - } - return 1; + j = dnpds40_get_counters(ctx); + break; case 'N': if (optarg[0] != 'A' && optarg[0] != 'B' && optarg[0] != 'M') return CUPS_BACKEND_FAILED; - if (ctx) { - if (!ctx->supports_matte) { - ERROR("Printer FW does not support matte functions, please update!\n"); - return CUPS_BACKEND_FAILED; - } - j = dnpds40_clear_counter(ctx, optarg[0]); - break; + if (!ctx->supports_matte) { + ERROR("Printer FW does not support matte functions, please update!\n"); + return CUPS_BACKEND_FAILED; } - return 1; + j = dnpds40_clear_counter(ctx, optarg[0]); + break; case 'p': - if (ctx) { - j = dnpds40_set_counter_p(ctx, optarg); + j = dnpds40_set_counter_p(ctx, optarg); + break; + case 's': + j = dnpds40_get_status(ctx); + break; + case 'k': { + int time = atoi(optarg); + if (!ctx->supports_standby) { + ERROR("Printer does not support standby\n"); + j = -1; break; } - return 1; - case 's': - if (ctx) { - j = dnpds40_get_status(ctx); + if (time < 0 || time > 99) { + ERROR("Value out of range (0-99)"); + j = -1; break; } - return 1; - case 'S': - if (ctx) { - int time = atoi(optarg); - if (!ctx->supports_standby) { - ERROR("Printer does not support standby\n"); - j = -1; - break; - } - if (time < 0 || time > 99) { - ERROR("Value out of range (0-99)"); - j = -1; - break; - } - j = dnpds620_standby_mode(ctx, time); + j = dnpds620_standby_mode(ctx, time); + break; + } + case 'K': { + int keep = atoi(optarg); + if (!ctx->supports_standby) { + ERROR("Printer does not support media keep mode\n"); + j = -1; + break; + } + if (keep < 0 || keep > 1) { + ERROR("Value out of range (0-1)"); + j = -1; + break; } + j = dnpds620_media_keep_mode(ctx, keep); + break; + } + case 'x': { + int enable = atoi(optarg); + if (!ctx->supports_iserial) { + ERROR("Printer does not support USB iSerialNumber reporting\n"); + j = -1; + break; + } + if (enable < 0 || enable > 1) { + ERROR("Value out of range (0-1)"); + j = -1; + break; + } + j = dnpds620_iserial_mode(ctx, enable); + break; + } default: break; /* Ignore completely */ } @@ -1564,7 +1665,7 @@ static int dnpds40_cmdline_arg(void *vctx, int argc, char **argv) /* Exported */ struct dyesub_backend dnpds40_backend = { .name = "DNP DS40/DS80/DSRX1/DS620", - .version = "0.51", + .version = "0.61", .uri_prefix = "dnpds40", .cmdline_usage = dnpds40_cmdline, .cmdline_arg = dnpds40_cmdline_arg, diff --git a/src/cups/genppd.c b/src/cups/genppd.c index e868444..8633611 100644 --- a/src/cups/genppd.c +++ b/src/cups/genppd.c @@ -1,5 +1,5 @@ /* - * "$Id: genppd.c,v 1.197 2015/06/25 01:48:02 speachy Exp $" + * "$Id: genppd.c,v 1.204 2015/09/13 14:37:16 speachy Exp $" * * PPD file generation program for the CUPS drivers. * @@ -579,8 +579,6 @@ main(int argc, /* I - Number of command-line arguments */ models[n] = argv[optind+n]; } models[numargs] = (char*)NULL; - - n=0; } /* @@ -1144,12 +1142,13 @@ print_ppd_header(gpFile fp, ppd_type_t ppd_type, int model, const char *driver, } static void -print_ppd_header_3(gpFile fp, ppd_type_t ppd_type, int model, const char *driver, - const char *family, const char *long_name, - const char *manufacturer, const char *device_id, - const char *ppd_location, - const char *language, const stp_string_list_t *po, - char **all_langs) +print_ppd_header_3(gpFile fp, ppd_type_t ppd_type, int model, + const char *driver, + const char *family, const char *long_name, + const char *manufacturer, const char *device_id, + const char *ppd_location, + const char *language, const stp_string_list_t *po, + char **all_langs) { int i; gpputs(fp, "*FileSystem: False\n"); @@ -1157,7 +1156,7 @@ print_ppd_header_3(gpFile fp, ppd_type_t ppd_type, int model, const char *driver gpputs(fp, "*TTRasterizer: Type42\n"); gpputs(fp, "*cupsVersion: 1.2\n"); - gpputs(fp, "*cupsManualCopies: True\n"); + gpprintf(fp, "*cupsFilter: \"application/vnd.cups-raster 100 rastertogutenprint.%s\"\n", GUTENPRINT_RELEASE_VERSION); if (strcasecmp(manufacturer, "EPSON") == 0) gpputs(fp, "*cupsFilter: \"application/vnd.cups-command 33 commandtoepson\"\n"); @@ -1946,6 +1945,7 @@ write_ppd( const char *manufacturer; /* Manufacturer of printer */ const char *device_id; /* IEEE1284 device ID */ const stp_vars_t *printvars; /* Printer option names */ + int nativecopies = 0; /* Printer natively generates copies */ stp_parameter_t desc; stp_parameter_list_t param_list; const stp_param_string_t *opt; @@ -2004,9 +2004,21 @@ write_ppd( } stp_parameter_description_destroy(&desc); - print_ppd_header_3(fp, ppd_type, model, driver, family, long_name, - manufacturer, device_id, ppd_location, language, po, - all_langs); + stp_describe_parameter(v, "NativeCopies", &desc); + if (desc.p_type == STP_PARAMETER_TYPE_BOOLEAN) + nativecopies = stp_get_boolean_parameter(v, "NativeCopies"); + + stp_parameter_description_destroy(&desc); + + if (nativecopies) + gpputs(fp, "*cupsManualCopies: False\n"); + else + gpputs(fp, "*cupsManualCopies: True\n"); + + print_ppd_header_3(fp, ppd_type, model, + driver, family, long_name, + manufacturer, device_id, ppd_location, language, po, + all_langs); /* Macintosh color management */ @@ -2426,6 +2438,33 @@ write_ppd( } stp_parameter_description_destroy(&desc); + /* Constraints */ + stp_describe_parameter(v, "PPDUIConstraints", &desc); + if (desc.is_active && desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) + { + num_opts = stp_string_list_count(desc.bounds.str); + if (num_opts > 0) + { + gpputs(fp, "*% ===== Constraints ===== \n"); + for (i = 0; i < num_opts; i++) + { + char *opt1, *opt2; + opt = stp_string_list_param(desc.bounds.str, i); + opt1 = stp_strdup(opt->text); + opt2 = strrchr(opt1, '*'); + if (opt2) + { + opt2[-1] = 0; + gpprintf(fp, "*%s: %s %s\n", opt->name, opt1, opt2); + gpprintf(fp, "*%s: %s %s\n", opt->name, opt2, opt1); + } + stp_free(opt1); + } + gpputs(fp, "\n"); + } + } + stp_parameter_description_destroy(&desc); + if (!language) { /* @@ -2554,7 +2593,7 @@ write_ppd( } } stp_parameter_description_destroy(&desc); - + /* * Quality settings */ @@ -2708,5 +2747,5 @@ write_ppd( /* - * End of "$Id: genppd.c,v 1.197 2015/06/25 01:48:02 speachy Exp $". + * End of "$Id: genppd.c,v 1.204 2015/09/13 14:37:16 speachy Exp $". */ diff --git a/src/cups/kodak1400_print.c b/src/cups/kodak1400_print.c index 1921bb4..c8e257c 100644 --- a/src/cups/kodak1400_print.c +++ b/src/cups/kodak1400_print.c @@ -35,6 +35,8 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND kodak1400_backend + #include "backend_common.h" /* Program states */ @@ -77,6 +79,7 @@ struct kodak1400_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; struct kodak1400_hdr hdr; uint8_t *plane_r; @@ -258,17 +261,18 @@ int kodak1400_cmdline_arg(void *vctx, int argc, char **argv) struct kodak1400_ctx *ctx = vctx; int i, j = 0; + if (!ctx) + return -1; + /* Reset arg parsing */ optind = 1; opterr = 0; - while ((i = getopt(argc, argv, "C:")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "C:")) >= 0) { switch(i) { + GETOPT_PROCESS_GLOBAL case 'C': - if (ctx) { - j = kodak1400_set_tonecurve(ctx, optarg); - break; - } - return 1; + j = kodak1400_set_tonecurve(ctx, optarg); + break; default: break; /* Ignore completely */ } @@ -295,14 +299,21 @@ static void kodak1400_attach(void *vctx, struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct kodak1400_ctx *ctx = vctx; + struct libusb_device *device; + struct libusb_device_descriptor desc; UNUSED(jobid); ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; -} + device = libusb_get_device(dev); + libusb_get_device_descriptor(device, &desc); + + ctx->type = lookup_printer_type(&kodak1400_backend, + desc.idVendor, desc.idProduct); +} static void kodak1400_teardown(void *vctx) { struct kodak1400_ctx *ctx = vctx; @@ -603,7 +614,7 @@ top: struct dyesub_backend kodak1400_backend = { .name = "Kodak 1400/805", - .version = "0.33", + .version = "0.34", .uri_prefix = "kodak1400", .cmdline_usage = kodak1400_cmdline, .cmdline_arg = kodak1400_cmdline_arg, @@ -739,7 +750,8 @@ struct dyesub_backend kodak1400_backend = { Other readback codes seen: - e4 72 00 00 10 00 50 59 -- ??? + e4 72 00 00 40 00 50 59 -- ?? paper jam? + e4 72 00 00 10 00 50 59 -- media red blink, error red blink, [media mismatch]] e4 72 00 00 10 01 50 59 -- ??? e4 72 00 00 00 04 50 59 -- media red blink, error red [media too small for image ?] e4 72 00 00 02 00 50 59 -- media off, error red. [out of paper] diff --git a/src/cups/kodak605_print.c b/src/cups/kodak605_print.c index 544eb72..ad2e6bf 100644 --- a/src/cups/kodak605_print.c +++ b/src/cups/kodak605_print.c @@ -35,38 +35,116 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND kodak605_backend + #include "backend_common.h" #define USB_VID_KODAK 0x040A #define USB_PID_KODAK_605 0x402E +/* Command Header */ +struct kodak605_cmd { + uint16_t cmd; /* LE */ + uint16_t len; /* LE, not counting this header */ +} __attribute__((packed)); + +struct kodak605_sts_hdr { + uint8_t result; /* RESULT_* */ + uint8_t unk_1[5]; /* 00 00 00 00 00 */ + uint8_t sts_1; /* 01/02 */ + uint8_t sts_2; /* 00/61->6b ?? temperature? */ + uint16_t length; /* LE, not counting this header */ +} __attribute__((packed)); + +#define RESULT_SUCCESS 0x01 +#define RESULT_FAIL 0x02 + /* Media structure */ struct kodak605_medium { uint8_t index; uint16_t cols; /* LE */ uint16_t rows; /* LE */ - uint8_t unk[5]; /* 01 00 00 00 00 */ + uint8_t type; /* MEDIA_TYPE_* */ + uint8_t unk[4]; /* 00 00 00 00 */ } __attribute__((packed)); +#define MEDIA_TYPE_UNKNOWN 0x00 +#define MEDIA_TYPE_PAPER 0x01 + struct kodak605_media_list { - uint8_t unk[12]; /* 01 00 00 00 00 00 02 00 67 00 02 0b */ + struct kodak605_sts_hdr hdr; + uint8_t unk; /* always seen 02 */ + uint8_t type; /* KODAK_MEDIA_* */ uint8_t count; struct kodak605_medium entries[]; } __attribute__((packed)); +#define KODAK_MEDIA_6R 0x0b +#define KODAK_MEDIA_NONE 0x00 + +#define MAX_MEDIA_LEN 128 + +/* Status response */ +struct kodak605_status { + struct kodak605_sts_hdr hdr; +/*@10*/ uint32_t ctr_life; /* Lifetime Prints */ + uint32_t ctr_maint; /* Prints since last maintainence */ + uint32_t ctr_media; /* Prints on current media */ + uint32_t ctr_cut; /* Cutter Actuations */ + uint32_t ctr_head; /* Prints on current head */ +/*@30*/ uint8_t donor; /* Donor Percentage remaining */ +/*@31*/ uint8_t null_1[7]; /* 00 00 00 00 00 00 00 */ +/*@38*/ uint8_t b1_id; /* 00/01/02 */ + uint16_t b1_remain; + uint16_t b1_complete; + uint16_t b1_total; +/*@45*/ uint8_t b1_sts; /* See BANK_STATUS_* */ + uint8_t b2_id; /* 00/01/02 */ + uint16_t b2_remain; + uint16_t b2_complete; + uint16_t b2_total; +/*@53*/ uint8_t b2_sts; /* see BANK_STATUS_* */ +/*@54*/ uint8_t id; /* current job id ( 00/01/02 seen ) */ +/*@55*/ uint16_t remain; /* in current job */ +/*@57*/ uint16_t complete; /* in current job */ +/*@59*/ uint16_t total; /* in current job */ +/*@61*/ uint8_t null_2[9]; /* 00 00 00 00 00 00 00 00 00 */ +/*@70*/ uint8_t unk_12[6]; /* 01 00 00 00 00 00 */ +} __attribute__((packed)); + /* File header */ struct kodak605_hdr { uint8_t hdr[4]; /* 01 40 0a 00 */ - uint8_t unk1; /* 01 or 02 */ - uint8_t copies; /* 01 or more */ - uint8_t unk2; /* always 00 */ - uint16_t columns; /* BE always 0x0734 */ - uint16_t rows; /* BE */ + uint8_t jobid; + uint16_t copies; /* LE, 0x0001 or more */ + uint16_t columns; /* LE, always 0x0734 */ + uint16_t rows; /* LE */ uint8_t media; /* 0x03 for 6x8, 0x01 for 6x4 */ uint8_t laminate; /* 0x02 to laminate, 0x01 for not */ - uint8_t unk3; /* 0x00, 0x01 [may be print mode] */ + uint8_t mode; /* Print mode -- 0x00, 0x01 seen */ } __attribute__((packed)); +#define BANK_STATUS_FREE 0x00 +#define BANK_STATUS_XFER 0x01 +#define BANK_STATUS_FULL 0x02 +#define BANK_STATUS_PRINTING 0x12 + +static char *bank_statuses(uint8_t v) +{ + switch (v) { + case BANK_STATUS_FREE: + return "Free"; + case BANK_STATUS_XFER: + return "Xfer"; + case BANK_STATUS_FULL: + return "Full"; + case BANK_STATUS_PRINTING: + return "Printing"; + default: + return "Unknown"; + } +} + #define CMDBUF_LEN 4 /* Private data stucture */ @@ -74,23 +152,50 @@ struct kodak605_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; + uint8_t jobid; struct kodak605_hdr hdr; + + struct kodak605_media_list *media; + uint8_t *databuf; int datalen; }; -/* Program states */ -enum { - S_IDLE = 0, - S_READY, - S_STARTED, - S_SENT_HDR, - S_SENT_DATA, - S_FINISHED, -}; +static int kodak605_get_media(struct kodak605_ctx *ctx, struct kodak605_media_list *media) +{ + uint8_t cmdbuf[4]; + + int ret, num = 0; + + /* Send Media Query */ + cmdbuf[0] = 0x02; + cmdbuf[1] = 0x00; + cmdbuf[2] = 0x00; + cmdbuf[3] = 0x00; + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmdbuf, sizeof(cmdbuf)))) + return ret; -#define READBACK_LEN 120 + /* Read in the printer status */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) media, MAX_MEDIA_LEN, &num); + if (ret < 0) + return ret; + + if (num < (int)sizeof(*media)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*media)); + return CUPS_BACKEND_FAILED; + } + + if (media->hdr.result != RESULT_SUCCESS) { + ERROR("Unexpected response from media query (%x)!\n", media->hdr.result); + return CUPS_BACKEND_FAILED; + } + + return 0; +} static void *kodak605_init(void) { @@ -101,6 +206,10 @@ static void *kodak605_init(void) } memset(ctx, 0, sizeof(struct kodak605_ctx)); + ctx->media = malloc(MAX_MEDIA_LEN); + + ctx->type = P_ANY; + return ctx; } @@ -111,15 +220,23 @@ static void kodak605_attach(void *vctx, struct libusb_device_handle *dev, struct libusb_device *device; struct libusb_device_descriptor desc; - UNUSED(jobid); - - ctx->dev = dev; + ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; device = libusb_get_device(dev); libusb_get_device_descriptor(device, &desc); + ctx->type = lookup_printer_type(&kodak605_backend, + desc.idVendor, desc.idProduct); + + /* Make sure jobid is sane */ + ctx->jobid = (jobid & 0x7f) + 1; + + /* Query media info */ + if (kodak605_get_media(ctx, ctx->media)) { + ERROR("Can't query media\n"); + } } static void kodak605_teardown(void *vctx) { @@ -190,236 +307,176 @@ static int kodak605_read_parse(void *vctx, int data_fd) { return CUPS_BACKEND_OK; } -static int kodak605_main_loop(void *vctx, int copies) { - struct kodak605_ctx *ctx = vctx; - - uint8_t rdbuf[READBACK_LEN]; - uint8_t rdbuf2[READBACK_LEN]; - uint8_t cmdbuf[CMDBUF_LEN]; - - int last_state = -1, state = S_IDLE; - int num, ret; - int pending = 0; - - if (!ctx) - return CUPS_BACKEND_FAILED; - - /* Printer handles generating copies.. */ - if (ctx->hdr.copies < copies) - ctx->hdr.copies = copies; - copies = 1; - -top: - if (state != last_state) { - if (dyesub_debug) - DEBUG("last_state %d new %d\n", last_state, state); - } +static int kodak605_get_status(struct kodak605_ctx *ctx, struct kodak605_status *sts) +{ + uint8_t cmdbuf[4]; - if (pending) - goto skip_query; + int ret, num = 0; /* Send Status Query */ cmdbuf[0] = 0x01; cmdbuf[1] = 0x00; cmdbuf[2] = 0x00; cmdbuf[3] = 0x00; - if ((ret = send_data(ctx->dev, ctx->endp_down, - cmdbuf, CMDBUF_LEN))) - return CUPS_BACKEND_FAILED; + cmdbuf, sizeof(cmdbuf)))) + return ret; -skip_query: /* Read in the printer status */ ret = read_data(ctx->dev, ctx->endp_up, - rdbuf, READBACK_LEN, &num); + (uint8_t*) sts, sizeof(*sts), &num); if (ret < 0) return ret; - if (num < 10) { - ERROR("Short read! (%d/%d)\n", num, 10); + if (num < (int)sizeof(*sts)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*sts)); return CUPS_BACKEND_FAILED; } - if (num != 10 && num != 76 && num != 113) { - ERROR("Unexpected readback from printer (%d/%d from 0x%02x))\n", - num, READBACK_LEN, ctx->endp_up); + if (sts->hdr.result != RESULT_SUCCESS) { + ERROR("Unexpected response from status query (%x)!\n", sts->hdr.result); return CUPS_BACKEND_FAILED; } - if (memcmp(rdbuf, rdbuf2, READBACK_LEN)) { - memcpy(rdbuf2, rdbuf, READBACK_LEN); - } else if (state == last_state) { - sleep(1); - } - last_state = state; + return 0; +} + +static int kodak605_main_loop(void *vctx, int copies) { + struct kodak605_ctx *ctx = vctx; - fflush(stderr); + struct kodak605_status sts; - pending = 0; + int num, ret; - switch (state) { - case S_IDLE: - INFO("Waiting for printer idle\n"); -#if 0 - if (rdbuf[0] != 0x01 || - rdbuf[1] != 0x02 || - rdbuf[2] != 0x01) { - break; - } -#endif - // XXX detect media type based on readback! + if (!ctx) + return CUPS_BACKEND_FAILED; - INFO("Printing started; Sending init sequence\n"); - state = S_STARTED; + /* Printer handles generating copies.. */ + if (le16_to_cpu(ctx->hdr.copies) < copies) + ctx->hdr.copies = cpu_to_le16(copies); - break; - case S_STARTED: -#if 0 - if (rdbuf[0] != 0x01 || - rdbuf[2] != 0x00) + /* Validate against supported media list */ + for (num = 0 ; num < ctx->media->count; num++) { + if (ctx->media->entries[num].rows == ctx->hdr.rows && + ctx->media->entries[num].cols == ctx->hdr.columns) break; + } + if (num == ctx->media->count) { + ERROR("Print size unsupported by media!\n"); + return CUPS_BACKEND_HOLD; + } - /* Aappears to depend on media */ - if (rdbuf[1] != 0x0b && - rdbuf[1] != 0x03) - break; -#endif + /* Use specified jobid */ + ctx->hdr.jobid = ctx->jobid; - INFO("Sending image header\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*)&ctx->hdr, sizeof(ctx->hdr)))) - return CUPS_BACKEND_FAILED; - pending = 1; - state = S_SENT_HDR; - break; - case S_SENT_HDR: - INFO("Waiting for printer to accept data\n"); - if (rdbuf[0] != 0x01 || - rdbuf[6] == 0x00 || - num != 10) { - break; - } - INFO("Sending image data\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf, ctx->datalen))) + INFO("Waiting for printer idle\n"); + + while(1) { + if ((ret = kodak605_get_status(ctx, &sts))) return CUPS_BACKEND_FAILED; - INFO("Image data sent\n"); - sleep(1); /* An experiment */ - state = S_SENT_DATA; - break; - case S_SENT_DATA: - INFO("Waiting for printer to acknowledge completion\n"); -#if 0 - if (rdbuf[0] != 0x01 || - rdbuf[1] != 0x02 || - rdbuf[2] != 0x01) { + // XXX check for errors + + /* Wait for a free buffer */ + if (sts.b1_sts == BANK_STATUS_FREE || + sts.b2_sts == BANK_STATUS_FREE) { break; } -#endif - state = S_FINISHED; - break; - default: - break; - }; - if (state != S_FINISHED) - goto top; + sleep(1); + } - /* Clean up */ - if (terminate) - copies = 1; + { + INFO("Sending image header\n"); + if ((ret = send_data(ctx->dev, ctx->endp_down, + (uint8_t*)&ctx->hdr, sizeof(ctx->hdr)))) + return CUPS_BACKEND_FAILED; - INFO("Print complete (%d copies remaining)\n", copies - 1); + struct kodak605_sts_hdr resp; + if ((ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &resp, sizeof(resp), &num))) + return CUPS_BACKEND_FAILED; - if (copies && --copies) { - state = S_IDLE; - goto top; + if (resp.result != RESULT_SUCCESS) { + ERROR("Unexpected response from print command (%x)!\n", resp.result); + return CUPS_BACKEND_FAILED; + } + // XXX what about resp.sts1 or resp.sts2? } + sleep(1); - return CUPS_BACKEND_OK; -} + INFO("Sending image data\n"); + if ((ret = send_data(ctx->dev, ctx->endp_down, + ctx->databuf, ctx->datalen))) + return CUPS_BACKEND_FAILED; -static int kodak605_get_status(struct kodak605_ctx *ctx) -{ - uint8_t cmdbuf[4]; - uint8_t rdbuf[76]; + INFO("Image data sent\n"); - int ret, i, num = 0; + INFO("Waiting for printer to acknowledge completion\n"); + do { + sleep(1); + if ((ret = kodak605_get_status(ctx, &sts))) + return CUPS_BACKEND_FAILED; - /* Send Status Query */ - cmdbuf[0] = 0x01; - cmdbuf[1] = 0x00; - cmdbuf[2] = 0x00; - cmdbuf[3] = 0x00; - if ((ret = send_data(ctx->dev, ctx->endp_down, - cmdbuf, sizeof(cmdbuf)))) - return ret; + // XXX check for errors ? - /* Read in the printer status */ - ret = read_data(ctx->dev, ctx->endp_up, - rdbuf, READBACK_LEN, &num); - if (ret < 0) - return ret; + /* Wait for completion */ + if (sts.b1_id == ctx->jobid && sts.b1_complete == sts.b1_total) + break; + if (sts.b2_id == ctx->jobid && sts.b2_complete == sts.b2_total) + break; - if (num < (int)sizeof(rdbuf)) { - ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(rdbuf)); - return 4; - } + if (fast_return) { + INFO("Fast return mode enabled.\n"); + break; + } + } while(1); - DEBUG("status: "); - for (i = 0 ; i < num ; i++) { - DEBUG2("%02x ", rdbuf[i]); - } + INFO("Print complete\n"); - return 0; + return CUPS_BACKEND_OK; +} + +static void kodak605_dump_status(struct kodak605_status *sts) +{ + INFO("Bank 1: %s Job %03u @ %03u/%03u\n", + bank_statuses(sts->b1_sts), sts->b1_id, + le16_to_cpu(sts->b1_complete), le16_to_cpu(sts->b1_complete)); + INFO("Bank 2: %s Job %03u @ %03u/%03u\n", + bank_statuses(sts->b2_sts), sts->b2_id, + le16_to_cpu(sts->b2_complete), le16_to_cpu(sts->b2_complete)); + + INFO("Lifetime prints : %d\n", be32_to_cpu(sts->ctr_life)); + INFO("Cutter actuations : %d\n", be32_to_cpu(sts->ctr_cut)); + INFO("Head prints : %d\n", be32_to_cpu(sts->ctr_head)); + INFO("Media prints : %d\n", be32_to_cpu(sts->ctr_media)); + INFO("Donor : %d%%\n", sts->donor); } static void kodak605_dump_mediainfo(struct kodak605_media_list *media) { int i; + if (media->type == KODAK_MEDIA_NONE) { + DEBUG("No Media Loaded\n"); + return; + } + + if (media->type == KODAK_MEDIA_6R) { + DEBUG("Media type: 6R (Kodak 197-4096 or equivalent)\n"); + } else { + DEBUG("Media type %02x (unknown, please report!)\n", media->type); + } + DEBUG("Legal print sizes:\n"); for (i = 0 ; i < media->count ; i++) { - DEBUG("\t%d: %dx%d\n", i, + DEBUG("\t%d: %dx%d\n", i, le16_to_cpu(media->entries[i].cols), le16_to_cpu(media->entries[i].rows)); } DEBUG("\n"); } -static int kodak605_get_media(struct kodak605_ctx *ctx) -{ - uint8_t cmdbuf[4]; - uint8_t rdbuf[113]; - - int ret, num = 0; - - /* Send Media Query */ - cmdbuf[0] = 0x02; - cmdbuf[1] = 0x00; - cmdbuf[2] = 0x00; - cmdbuf[3] = 0x00; - if ((ret = send_data(ctx->dev, ctx->endp_down, - cmdbuf, sizeof(cmdbuf)))) - return ret; - - /* Read in the printer status */ - ret = read_data(ctx->dev, ctx->endp_up, - rdbuf, READBACK_LEN, &num); - if (ret < 0) - return ret; - - if (num < (int)sizeof(rdbuf)) { - ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(rdbuf)); - return 4; - } - - kodak605_dump_mediainfo((struct kodak605_media_list *)rdbuf); - - return 0; -} - #define UPDATE_SIZE 1536 static int kodak605_set_tonecurve(struct kodak605_ctx *ctx, char *fname) { @@ -507,29 +564,29 @@ static int kodak605_cmdline_arg(void *vctx, int argc, char **argv) struct kodak605_ctx *ctx = vctx; int i, j = 0; + if (!ctx) + return -1; + /* Reset arg parsing */ optind = 1; opterr = 0; - while ((i = getopt(argc, argv, "C:ms")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "C:ms")) >= 0) { switch(i) { + GETOPT_PROCESS_GLOBAL case 'C': - if (ctx) { - j = kodak605_set_tonecurve(ctx, optarg); - break; - } - return 1; + j = kodak605_set_tonecurve(ctx, optarg); + break; case 'm': - if (ctx) { - j = kodak605_get_media(ctx); - break; - } - return 1; - case 's': - if (ctx) { - j = kodak605_get_status(ctx); - break; - } - return 1; + kodak605_dump_mediainfo(ctx->media); + break; + case 's': { + struct kodak605_status sts; + + j = kodak605_get_status(ctx, &sts); + if (!j) + kodak605_dump_status(&sts); + break; + } default: break; /* Ignore completely */ } @@ -543,7 +600,7 @@ static int kodak605_cmdline_arg(void *vctx, int argc, char **argv) /* Exported */ struct dyesub_backend kodak605_backend = { .name = "Kodak 605", - .version = "0.21", + .version = "0.24", .uri_prefix = "kodak605", .cmdline_usage = kodak605_cmdline, .cmdline_arg = kodak605_cmdline_arg, @@ -563,76 +620,23 @@ struct dyesub_backend kodak605_backend = { Spool file consists of 14-byte header followed by plane-interleaved BGR data. Native printer resolution is 1844 pixels per row, and 1240 or 2434 rows. + All fields are LITTLE ENDIAN unless otherwise specified + Header: 01 40 0a 00 Fixed header - XX Unknown, always 01 in file, but 02 seen in sniffs sometimes - CC Number of copies (1-255) - 00 Always 0x00 - WW WW Number of columns, little endian. (Fixed at 1844) - HH HH Number of rows, little endian. (1240 or 2434) + XX Job ID + CC CC Number of copies (1-???) + WW WW Number of columns (Fixed at 1844) + HH HH Number of rows (1240 or 2434) DD 0x01 (4x6) 0x03 (8x6) LL Laminate, 0x01 (off) or 0x02 (on) - 00 + 00 Print Mode (???) ************************************************************************ - Kodak 605 Printer Comms: - - [[file header]] 01 40 0a 00 01 CC 00 WW WW HH HH MT LL 00 - --> 01 00 00 00 -<- [76 bytes -- status ] - - 01 00 00 00 00 00 02 00 42 00 30 00 00 00 30 00 - 00 00 13 00 00 00 75 00 00 00 30 00 00 00 5d 00 - 00 00 00 00 00 00 01 01 00 00 00 01 00 20 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 01 00 00 00 00 00 - --> 01 00 00 00 -<- [76 bytes -- status ] + Note: Kodak 605 is actually a Shinko CHC-S1545-5A - 01 00 00 00 00 00 02 00 42 00 30 00 00 00 30 00 - 00 00 13 00 00 00 75 00 00 00 30 00 00 00 5d 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 01 00 00 00 00 00 - - --> 02 00 00 00 -<- [113 bytes -- supported media/sizes? Always seems to be identical ] - - [ 13-byte header, plus 10 slots for 10-byte media definitions, see above ] - - 01 00 00 00 00 00 02 00 67 00 02 0b 04 01 34 07 - d8 04 01 00 00 00 00 02 dc 05 34 08 01 00 00 00 - 00 03 34 07 82 09 01 00 00 00 00 04 34 07 ba 09 - 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 - --> 01 40 0a 00 01 01 00 34 07 d8 04 01 02 00 [[ unmodified header ]] -<- 01 00 00 00 00 00 XX 00 00 00 [[ Seen 0x01 and 0x02 ] --> image data! --> image data! - --> 01 00 00 00 -<- [76 bytes -- status ?? ] - - 01 00 00 00 00 00 01 00 42 00 31 00 00 00 31 00 - 00 00 14 00 00 00 77 00 00 00 31 00 00 00 5d 00 - 00 00 00 00 00 00 01 00 00 01 00 01 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 01 00 00 00 00 00 - - Write tone curve data: - --> 04 c0 0a 00 03 01 00 00 00 00 LL LL 00 00 [[ LL LL == 0x0600 in LE ]] -<- 01 00 00 00 00 00 XX 00 00 00 [[ Seen 0x01 and 0x02 ] - --> [[ 1536 bytes of LE tone curve data ]] + ************************************************************************ */ diff --git a/src/cups/kodak6800_print.c b/src/cups/kodak6800_print.c index 51093b8..1e77850 100644 --- a/src/cups/kodak6800_print.c +++ b/src/cups/kodak6800_print.c @@ -39,6 +39,8 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND kodak6800_backend + #include "backend_common.h" #define USB_VID_KODAK 0x040A @@ -47,50 +49,155 @@ /* File header */ struct kodak6800_hdr { - uint8_t hdr[9]; /* Always 03 1b 43 48 43 0a 00 04 00 [6850] - 03 1b 43 48 43 0a 00 01 00 [6800] */ - uint8_t copies; + uint8_t hdr[7]; /* Always 03 1b 43 48 43 0a 00 */ + uint8_t jobid; /* Non-zero */ + uint16_t copies; /* BE, in BCD format (1-9999) */ uint16_t columns; /* BE */ uint16_t rows; /* BE */ - uint8_t size; /* 0x06 for 6x8, 0x00 for 6x4, 0x07 for 5x7 */ + uint8_t size; /* 0x06 for 6x8, 0x00 for 6x4, 0x07 for 5x7 */ uint8_t laminate; /* 0x01 to laminate, 0x00 for not */ - uint8_t unk1; /* 0x00 or 0x01 (for 4x6 on 6x8 media) */ + uint8_t mode; /* 0x00 or 0x01 (for 4x6 on 6x8 media) */ } __attribute__((packed)); struct kodak68x0_status_readback { uint8_t hdr; /* Always 01 */ - uint8_t sts1; /* Always 0x02 (idle) or 0x01 (busy) */ - uint8_t sts2; /* 0x01 == ready, 0x02 == no media, 0x03 == not ready */ - uint8_t errtype; /* 0x00 none, 0x80 "control" */ - uint8_t null0[2]; - uint8_t unkA; /* 0x00 or 0x01 or 0x10 */ + uint8_t status; /* STATUS_* */ + uint8_t status1; /* STATUS1_* */ + uint32_t status2; /* STATUS2_* */ uint8_t errcode; /* Error ## */ - uint32_t ctr0; /* Total Prints (BE) */ - uint32_t ctr1; /* Total Prints (BE) */ - uint32_t ctr2; /* Increments by 1 for each print (6850), unk (6800). BE */ - uint32_t ctr3; /* Increments by 2 for each print. BE */ + uint32_t lifetime; /* Lifetime Prints (BE) */ + uint32_t maint; /* Maint Prints (BE) */ + uint32_t media; /* Media Prints (6850), Unknown (6800) (BE) */ + uint32_t cutter; /* Cutter Actuations (BE) */ uint8_t nullB[2]; - uint8_t errtype2; /* 0x00 none, 0xd0 "control" */ - uint8_t donor; /* Percentage, 0-100 */ - uint8_t unkC[2]; /* Always 00 03 */ - uint16_t main_fw; /* seen 652, 656, 670 (6850) and 232 (6800) */ - uint8_t unkD[2]; /* Always 00 01 */ - uint16_t dsp_fw; /* Seen 540, 541, 560 (6850) and 131 (6800) */ - uint8_t unk1; /* Seen 0x00, 0x01, 0x03, 0x04 */ - uint8_t null1[2]; - uint8_t unk2; /* Seen 0x01, 0x00 */ - uint8_t null2; - uint8_t unk3; /* Seen 0x01, 0x00 */ - uint8_t null4; - uint8_t unk4; /* Seen 0x01, 0x00 */ - uint8_t null5[7]; + uint8_t errtype; /* seen 0x00 or 0xd0 */ + uint8_t donor; /* Percentage, 0-100 */ + uint16_t main_boot; /* Always 003 */ + uint16_t main_fw; /* seen 652, 656, 670, 671 (6850) and 232 (6800) */ + uint16_t dsp_boot; /* Always 001 */ + uint16_t dsp_fw; /* Seen 540, 541, 560 (6850) and 131 (6800) */ + uint8_t b1_jobid; + uint8_t b2_jobid; + uint16_t b1_remain; /* Remaining prints in job */ + uint16_t b1_complete; /* Completed prints in job */ + uint16_t b1_total; /* Total prints in job */ + uint16_t b2_remain; /* Remaining prints in job */ + uint16_t b2_complete; /* Completed prints in job */ + uint16_t b2_total; /* Total prints in job */ + uint8_t curve_status; /* Always seems to be 0x00 */ } __attribute__((packed)); +enum { + CMD_CODE_OK = 1, + CMD_CODE_BAD = 2, +}; + +enum { + STATUS_PRINTING = 1, + STATUS_IDLE = 2, +}; + +enum { + STATE_STATUS1_STANDBY = 1, + STATE_STATUS1_ERROR = 2, + STATE_STATUS1_WAIT = 3, +}; + +#define STATE_STANDBY_STATUS2 0x0 + +enum { + WAIT_STATUS2_INIT = 0, + WAIT_STATUS2_RIBBON = 1, + WAIT_STATUS2_THERMAL = 2, + WAIT_STATUS2_OPERATING = 3, + WAIT_STATUS2_BUSY = 4, +}; + +#define ERROR_STATUS2_CTRL_CIRCUIT (1<<31) +#define ERROR_STATUS2_MECHANISM_CTRL (1<<30) +#define ERROR_STATUS2_SENSOR (1<<13) +#define ERROR_STATUS2_COVER_OPEN (1<<12) +#define ERROR_STATUS2_TEMP_SENSOR (1<<9) +#define ERROR_STATUS2_PAPER_JAM (1<<8) +#define ERROR_STATUS2_PAPER_EMPTY (1<<6) +#define ERROR_STATUS2_RIBBON_ERR (1<<4) + +enum { + CTRL_CIR_ERROR_EEPROM1 = 0x01, + CTRL_CIR_ERROR_EEPROM2 = 0x02, + CTRL_CIR_ERROR_DSP = 0x04, + CTRL_CIR_ERROR_CRC_MAIN = 0x06, + CTRL_CIR_ERROR_DL_MAIN = 0x07, + CTRL_CIR_ERROR_CRC_DSP = 0x08, + CTRL_CIR_ERROR_DL_DSP = 0x09, + CTRL_CIR_ERROR_ASIC = 0x0a, + CTRL_CIR_ERROR_DRAM = 0x0b, + CTRL_CIR_ERROR_DSPCOMM = 0x29, +}; + +enum { + MECH_ERROR_HEAD_UP = 0x01, + MECH_ERROR_HEAD_DOWN = 0x02, + MECH_ERROR_MAIN_PINCH_UP = 0x03, + MECH_ERROR_MAIN_PINCH_DOWN = 0x04, + MECH_ERROR_SUB_PINCH_UP = 0x05, + MECH_ERROR_SUB_PINCH_DOWN = 0x06, + MECH_ERROR_FEEDIN_PINCH_UP = 0x07, + MECH_ERROR_FEEDIN_PINCH_DOWN = 0x08, + MECH_ERROR_FEEDOUT_PINCH_UP = 0x09, + MECH_ERROR_FEEDOUT_PINCH_DOWN = 0x0a, + MECH_ERROR_CUTTER_LR = 0x0b, + MECH_ERROR_CUTTER_RL = 0x0c, +}; + +enum { + SENSOR_ERROR_CUTTER = 0x05, + SENSOR_ERROR_HEAD_DOWN = 0x09, + SENSOR_ERROR_HEAD_UP = 0x0a, + SENSOR_ERROR_MAIN_PINCH_DOWN = 0x0b, + SENSOR_ERROR_MAIN_PINCH_UP = 0x0c, + SENSOR_ERROR_FEED_PINCH_DOWN = 0x0d, + SENSOR_ERROR_FEED_PINCH_UP = 0x0e, + SENSOR_ERROR_EXIT_PINCH_DOWN = 0x0f, + SENSOR_ERROR_EXIT_PINCH_UP = 0x10, + SENSOR_ERROR_LEFT_CUTTER = 0x11, + SENSOR_ERROR_RIGHT_CUTTER = 0x12, + SENSOR_ERROR_CENTER_CUTTER = 0x13, + SENSOR_ERROR_UPPER_CUTTER = 0x14, + SENSOR_ERROR_PAPER_FEED_COVER = 0x15, +}; + +enum { + TEMP_SENSOR_ERROR_HEAD_HIGH = 0x01, + TEMP_SENSOR_ERROR_HEAD_LOW = 0x02, + TEMP_SENSOR_ERROR_ENV_HIGH = 0x03, + TEMP_SENSOR_ERROR_ENV_LOW = 0x04, +}; + +enum { + COVER_OPEN_ERROR_UPPER = 0x01, + COVER_OPEN_ERROR_LOWER = 0x02, +}; + +enum { + PAPER_EMPTY_ERROR = 0x00, +}; + +enum { + RIBBON_ERROR = 0x00, +}; + +enum { + CURVE_TABLE_STATUS_INITIAL = 0x00, + CURVE_TABLE_STATUS_USERSET = 0x01, + CURVE_TABLE_STATUS_CURRENT = 0x02, +}; + struct kodak6800_printsize { uint8_t hdr; /* Always 0x06 */ uint16_t width; /* BE */ uint16_t height; /* BE */ - uint8_t hdr2; /* Always 0x01 */ + uint8_t type; /* MEDIA_TYPE_* [ ie paper ] */ uint8_t code; /* 00, 01, 02, 03, 04, 05 seen. An index? */ uint8_t code2; /* 00, 01 seen. Seems to be 1 only after a 4x6 printed. */ uint8_t null[2]; @@ -106,7 +213,9 @@ struct kodak68x0_media_readback { struct kodak6800_printsize sizes[]; } __attribute__((packed)); -#define KODAK68x0_MEDIA_6R 0x0b +#define KODAK68x0_MEDIA_6R 0x0b +#define KODAK68x0_MEDIA_UNK 0x03 +#define KODAK68x0_MEDIA_NONE 0x00 #define CMDBUF_LEN 17 @@ -117,39 +226,60 @@ struct kodak6800_ctx { uint8_t endp_down; int type; - int media; + + uint8_t jobid; + + struct kodak68x0_media_readback *media; struct kodak6800_hdr hdr; uint8_t *databuf; int datalen; }; -#define READBACK_LEN 68 -char *kodak68x0_error_codes(uint8_t code1, uint8_t code2) +/* Baseline commands */ +static int kodak6800_do_cmd(struct kodak6800_ctx *ctx, + void *cmd, int cmd_len, + void *resp, int resp_len, + int *actual_len) { - if (code1 == 0x80 && code2 == 0xd0) - return "Control Error"; + int ret; + + /* Write command */ + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmd, cmd_len))) + return (ret < 0) ? ret : -99; + + /* Read response */ + ret = read_data(ctx->dev, ctx->endp_up, + resp, resp_len, actual_len); + if (ret < 0) + return ret; - return "Unknown Type (please report!)"; + return 0; } static void kodak68x0_dump_mediainfo(struct kodak68x0_media_readback *media) { int i; + if (media->media == KODAK68x0_MEDIA_NONE) { + INFO("No Media Loaded\n"); + return; + } + if (media->media == KODAK68x0_MEDIA_6R) { - DEBUG("Media type: 6R (Kodak 197-4096 or equivalent)\n"); + INFO("Media type: 6R (Kodak 197-4096 or equivalent)\n"); } else { - DEBUG("Media type %02x (unknown, please report!)\n", media->media); + INFO("Media type %02x (unknown, please report!)\n", media->media); } - DEBUG("Legal print sizes:\n"); + INFO("Legal print sizes:\n"); for (i = 0 ; i < media->count ; i++) { - DEBUG("\t%d: %dx%d (%02x) %s\n", i, + INFO("\t%d: %dx%d (%02x) %s\n", i, be16_to_cpu(media->sizes[i].width), be16_to_cpu(media->sizes[i].height), media->sizes[i].code, media->sizes[i].code2? "Disallowed" : ""); } - DEBUG("\n"); + INFO("\n"); } static int kodak6800_get_mediainfo(struct kodak6800_ctx *ctx, struct kodak68x0_media_readback *media) @@ -167,62 +297,270 @@ static int kodak6800_get_mediainfo(struct kodak6800_ctx *ctx, struct kodak68x0_m req[4] = 0x43; req[5] = 0x1a; - /* Send request */ - if ((ret = send_data(ctx->dev, ctx->endp_down, - req, sizeof(req)))) - return ret; - - /* Get response */ - ret = read_data(ctx->dev, ctx->endp_up, - (uint8_t*)media, MAX_MEDIA_LEN, &num); - - if (ret < 0) + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req), + media, MAX_MEDIA_LEN, + &num))) return ret; - if (num < (int)sizeof(*media)) { - ERROR("Short read! (%d/%d)\n", num, (int) sizeof(*media)); - return 4; - } - /* Validate proper response */ - if (media->hdr != 0x01 || + if (media->hdr != CMD_CODE_OK || media->null[0] != 0x00) { ERROR("Unexpected response from media query!\n"); return CUPS_BACKEND_STOP; } - ctx->media = media->media; + return 0; +} + +static int kodak68x0_canceljob(struct kodak6800_ctx *ctx, + int id) +{ + uint8_t req[16]; + int ret, num; + struct kodak68x0_status_readback sts; + + memset(req, 0, sizeof(req)); + + req[0] = 0x03; + req[1] = 0x1b; + req[2] = 0x43; + req[3] = 0x48; + req[4] = 0x43; + req[5] = 0x13; + req[6] = id; + + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req), + &sts, sizeof(sts), + &num))) + return ret; + + /* Validate proper response */ + if (sts.hdr != CMD_CODE_OK) { + ERROR("Unexpected response from job cancel!\n"); + return -99; + } return 0; } +/* Structure dumps */ +static char *kodak68x0_status_str(struct kodak68x0_status_readback *resp) +{ + switch(resp->status1) { + case STATE_STATUS1_STANDBY: + return "Standby (Ready)"; + case STATE_STATUS1_WAIT: + switch (be32_to_cpu(resp->status2)) { + case WAIT_STATUS2_INIT: + return "Wait (Initializing)"; + case WAIT_STATUS2_RIBBON: + return "Wait (Ribbon Winding)"; + case WAIT_STATUS2_THERMAL: + return "Wait (Thermal Protection)"; + case WAIT_STATUS2_OPERATING: + return "Wait (Operating)"; + case WAIT_STATUS2_BUSY: + return "Wait (Busy)"; + default: + return "Wait (Unknown)"; + } + case STATE_STATUS1_ERROR: + switch (be32_to_cpu(resp->status2)) { + case ERROR_STATUS2_CTRL_CIRCUIT: + switch (resp->errcode) { + case CTRL_CIR_ERROR_EEPROM1: + return "Error (EEPROM1)"; + case CTRL_CIR_ERROR_EEPROM2: + return "Error (EEPROM2)"; + case CTRL_CIR_ERROR_DSP: + return "Error (DSP)"; + case CTRL_CIR_ERROR_CRC_MAIN: + return "Error (Main CRC)"; + case CTRL_CIR_ERROR_DL_MAIN: + return "Error (Main Download)"; + case CTRL_CIR_ERROR_CRC_DSP: + return "Error (DSP CRC)"; + case CTRL_CIR_ERROR_DL_DSP: + return "Error (DSP Download)"; + case CTRL_CIR_ERROR_ASIC: + return "Error (ASIC)"; + case CTRL_CIR_ERROR_DRAM: + return "Error (DRAM)"; + case CTRL_CIR_ERROR_DSPCOMM: + return "Error (DSP Communincation)"; + default: + return "Error (Unknown Circuit)"; + } + case ERROR_STATUS2_MECHANISM_CTRL: + switch (resp->errcode) { + case MECH_ERROR_HEAD_UP: + return "Error (Head Up Mechanism)"; + case MECH_ERROR_HEAD_DOWN: + return "Error (Head Down Mechanism)"; + case MECH_ERROR_MAIN_PINCH_UP: + return "Error (Main Pinch Up Mechanism)"; + case MECH_ERROR_MAIN_PINCH_DOWN: + return "Error (Main Pinch Down Mechanism)"; + case MECH_ERROR_SUB_PINCH_UP: + return "Error (Sub Pinch Up Mechanism)"; + case MECH_ERROR_SUB_PINCH_DOWN: + return "Error (Sub Pinch Down Mechanism)"; + case MECH_ERROR_FEEDIN_PINCH_UP: + return "Error (Feed-in Pinch Up Mechanism)"; + case MECH_ERROR_FEEDIN_PINCH_DOWN: + return "Error (Feed-in Pinch Down Mechanism)"; + case MECH_ERROR_FEEDOUT_PINCH_UP: + return "Error (Feed-out Pinch Up Mechanism)"; + case MECH_ERROR_FEEDOUT_PINCH_DOWN: + return "Error (Feed-out Pinch Down Mechanism)"; + case MECH_ERROR_CUTTER_LR: + return "Error (Left->Right Cutter)"; + case MECH_ERROR_CUTTER_RL: + return "Error (Right->Left Cutter)"; + default: + return "Error (Unknown Mechanism)"; + } + case ERROR_STATUS2_SENSOR: + switch (resp->errcode) { + case SENSOR_ERROR_CUTTER: + return "Error (Cutter Sensor)"; + case SENSOR_ERROR_HEAD_DOWN: + return "Error (Head Down Sensor)"; + case SENSOR_ERROR_HEAD_UP: + return "Error (Head Up Sensor)"; + case SENSOR_ERROR_MAIN_PINCH_DOWN: + return "Error (Main Pinch Down Sensor)"; + case SENSOR_ERROR_MAIN_PINCH_UP: + return "Error (Main Pinch Up Sensor)"; + case SENSOR_ERROR_FEED_PINCH_DOWN: + return "Error (Feed Pinch Down Sensor)"; + case SENSOR_ERROR_FEED_PINCH_UP: + return "Error (Feed Pinch Up Sensor)"; + case SENSOR_ERROR_EXIT_PINCH_DOWN: + return "Error (Exit Pinch Up Sensor)"; + case SENSOR_ERROR_EXIT_PINCH_UP: + return "Error (Exit Pinch Up Sensor)"; + case SENSOR_ERROR_LEFT_CUTTER: + return "Error (Left Cutter Sensor)"; + case SENSOR_ERROR_RIGHT_CUTTER: + return "Error (Right Cutter Sensor)"; + case SENSOR_ERROR_CENTER_CUTTER: + return "Error (Center Cutter Sensor)"; + case SENSOR_ERROR_UPPER_CUTTER: + return "Error (Upper Cutter Sensor)"; + case SENSOR_ERROR_PAPER_FEED_COVER: + return "Error (Paper Feed Cover)"; + default: + return "Error (Unknown Sensor)"; + } + case ERROR_STATUS2_COVER_OPEN: + switch (resp->errcode) { + case COVER_OPEN_ERROR_UPPER: + return "Error (Upper Cover Open)"; + case COVER_OPEN_ERROR_LOWER: + return "Error (Lower Cover Open)"; + default: + return "Error (Unknown Cover Open)"; + } + case ERROR_STATUS2_TEMP_SENSOR: + switch (resp->errcode) { + case TEMP_SENSOR_ERROR_HEAD_HIGH: + return "Error (Head Temperature High)"; + case TEMP_SENSOR_ERROR_HEAD_LOW: + return "Error (Head Temperature Low)"; + case TEMP_SENSOR_ERROR_ENV_HIGH: + return "Error (Environmental Temperature High)"; + case TEMP_SENSOR_ERROR_ENV_LOW: + return "Error (Environmental Temperature Low)"; + default: + return "Error (Unknown Temperature)"; + } + case ERROR_STATUS2_PAPER_JAM: + return "Error (Paper Jam)"; + case ERROR_STATUS2_PAPER_EMPTY: + return "Error (Paper Empty)"; + case ERROR_STATUS2_RIBBON_ERR: + return "Error (Ribbon)"; + default: + return "Error (Unknown)"; + } + default: + return "Unknown!"; + } +} + static void kodak68x0_dump_status(struct kodak6800_ctx *ctx, struct kodak68x0_status_readback *status) { - if (status->errtype || status->errtype2 || status->errcode) { - DEBUG("Error code : %s (%d/%d) # %d\n", - kodak68x0_error_codes(status->errtype, status->errtype2), - status->errtype, status->errtype2, status->errcode); + char *detail; + + switch (status->status) { + case STATUS_PRINTING: + detail = "Printing"; + break; + case STATUS_IDLE: + detail = "Idle"; + break; + default: + detail = "Unknown"; + break; + } + INFO("Printer Status : %s\n", detail); + + INFO("Printer State : %s # %02x %08x %02x\n", + kodak68x0_status_str(status), + status->status1, be32_to_cpu(status->status2), status->errcode); + + INFO("Bank 1 ID: %d\n", status->b1_jobid); + INFO("\tPrints: %d/%d complete\n", + be16_to_cpu(status->b1_complete), be16_to_cpu(status->b1_total)); + INFO("Bank 2 ID: %d\n", status->b2_jobid); + INFO("\tPrints: %d/%d complete\n", + be16_to_cpu(status->b2_complete), be16_to_cpu(status->b2_total)); + + switch (status->curve_status) { + case CURVE_TABLE_STATUS_INITIAL: + detail = "Initial/Default"; + break; + case CURVE_TABLE_STATUS_USERSET: + detail = "User Stored"; + break; + case CURVE_TABLE_STATUS_CURRENT: + detail = "Current"; + break; + default: + detail = "Unknown"; + break; } - DEBUG("Total prints : %d\n", be32_to_cpu(status->ctr0)); - DEBUG("Media prints : %d\n", be32_to_cpu(status->ctr2)); + INFO("Tone Curve Status: %s\n", detail); + + INFO("Counters:\n"); + INFO("\tLifetime : %d\n", be32_to_cpu(status->lifetime)); + INFO("\tThermal Head : %d\n", be32_to_cpu(status->maint)); + INFO("\tCutter : %d\n", be32_to_cpu(status->cutter)); + if (ctx->type == P_KODAK_6850) { int max; - if (ctx->media == KODAK68x0_MEDIA_6R) { + + INFO("\tMedia : %d\n", be32_to_cpu(status->media)); + + if (ctx->media->media == KODAK68x0_MEDIA_6R) { max = 375; } else { max = 0; } if (max) { - DEBUG("Remaining prints : %d\n", max - be32_to_cpu(status->ctr2)); + INFO("\t Remaining : %d\n", max - be32_to_cpu(status->media)); } else { - DEBUG("Remaining prints : Unknown media type\n"); + INFO("\t Remaining : Unknown\n"); } } - DEBUG("Main FW version : %d\n", be16_to_cpu(status->main_fw)); - DEBUG("DSP FW version : %d\n", be16_to_cpu(status->dsp_fw)); - DEBUG("Donor : %d%%\n", status->donor); - DEBUG("\n"); + INFO("Main FW version: %d\n", be16_to_cpu(status->main_fw)); + INFO("DSP FW version : %d\n", be16_to_cpu(status->dsp_fw)); + INFO("Donor : %d%%\n", status->donor); + INFO("\n"); } static int kodak6800_get_status(struct kodak6800_ctx *ctx, @@ -241,25 +579,16 @@ static int kodak6800_get_status(struct kodak6800_ctx *ctx, req[4] = 0x43; req[5] = 0x03; - /* Send request */ - if ((ret = send_data(ctx->dev, ctx->endp_down, - req, sizeof(req)))) + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req), + status, sizeof(*status), + &num))) return ret; - /* Get response */ - ret = read_data(ctx->dev, ctx->endp_up, - (uint8_t*)status, sizeof(*status), &num); - - if (ret < 0) - return ret; - if (num < (int)sizeof(*status)) { - ERROR("Short read! (%d/%d)\n", num, (int) sizeof(*status)); - return CUPS_BACKEND_FAILED; - } - - if (status->hdr != 0x01) { + /* Validate proper response */ + if (status->hdr != CMD_CODE_OK) { ERROR("Unexpected response from status query!\n"); - return CUPS_BACKEND_FAILED; + return -99; } return 0; @@ -269,10 +598,6 @@ static int kodak6800_get_status(struct kodak6800_ctx *ctx, #define UPDATE_SIZE 1536 static int kodak6800_get_tonecurve(struct kodak6800_ctx *ctx, char *fname) { - libusb_device_handle *dev = ctx->dev; - uint8_t endp_down = ctx->endp_down; - uint8_t endp_up = ctx->endp_up; - uint8_t cmdbuf[16]; uint8_t respbuf[64]; int ret, num = 0; @@ -304,18 +629,15 @@ static int kodak6800_get_tonecurve(struct kodak6800_ctx *ctx, char *fname) cmdbuf[14] = 0x00; cmdbuf[15] = 0x00; - if ((ret = send_data(dev, endp_down, - cmdbuf, 16))) - goto done; - - ret = read_data(dev, endp_up, - respbuf, sizeof(respbuf), &num); - if (ret < 0) - goto done; - - if (num != 51) { - ERROR("Short read! (%d/%d)\n", num, 51); - ret = 4; + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf), + respbuf, sizeof(respbuf), + &num))) + + /* Validate proper response */ + if (respbuf[0] != CMD_CODE_OK) { + ERROR("Unexpected response from tonecurve query!\n"); + ret = -99; goto done; } @@ -332,13 +654,10 @@ static int kodak6800_get_tonecurve(struct kodak6800_ctx *ctx, char *fname) cmdbuf[9] = 0x45; cmdbuf[10] = 0x20; for (i = 0 ; i < 24 ; i++) { - if ((ret = send_data(dev, endp_down, - cmdbuf, 11))) - goto done; - - ret = read_data(dev, endp_up, - respbuf, sizeof(respbuf), &num); - if (ret < 0) + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf), + respbuf, sizeof(respbuf), + &num))) goto done; if (num != 64) { @@ -371,15 +690,11 @@ static int kodak6800_get_tonecurve(struct kodak6800_ctx *ctx, char *fname) /* We're done */ free(data); - return 0; + return ret; } static int kodak6800_set_tonecurve(struct kodak6800_ctx *ctx, char *fname) { - libusb_device_handle *dev = ctx->dev; - uint8_t endp_down = ctx->endp_down; - uint8_t endp_up = ctx->endp_up; - uint8_t cmdbuf[64]; uint8_t respbuf[64]; int ret, num = 0; @@ -392,7 +707,7 @@ static int kodak6800_set_tonecurve(struct kodak6800_ctx *ctx, char *fname) ERROR("Memory Allocation Failure\n"); return -1; } - + INFO("Set Tone Curve from '%s'\n", fname); /* Read in file */ @@ -430,21 +745,24 @@ static int kodak6800_set_tonecurve(struct kodak6800_ctx *ctx, char *fname) cmdbuf[14] = 0x00; cmdbuf[15] = 0x00; - if ((ret = send_data(dev, endp_down, - cmdbuf, 16))) - goto done; - - ret = read_data(dev, endp_up, - respbuf, sizeof(respbuf), &num); - if (ret < 0) - goto done; - + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf), + respbuf, sizeof(respbuf), + &num))) + + /* Validate proper response */ if (num != 51) { ERROR("Short read! (%d/%d)\n", num, 51); ret = 4; goto done; } + if (respbuf[0] != CMD_CODE_OK) { + ERROR("Unexpected response from tonecurve set!\n"); + ret = -99; + goto done; + } + ptr = (uint8_t*) data; remain = UPDATE_SIZE; while (remain > 0) { @@ -456,24 +774,23 @@ static int kodak6800_set_tonecurve(struct kodak6800_ctx *ctx, char *fname) remain -= count; ptr += count; - /* Send next block over */ - if ((ret = send_data(dev, endp_down, - cmdbuf, count+1))) - goto done; + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, cmdbuf, count + 1, + respbuf, sizeof(respbuf), + &num))) - - ret = read_data(dev, endp_up, - respbuf, sizeof(respbuf), &num); - if (ret < 0) - goto done; - if (num != 51) { ERROR("Short read! (%d/%d)\n", num, 51); ret = 4; goto done; } + if (respbuf[0] != CMD_CODE_OK) { + ERROR("Unexpected response from tonecurve set!\n"); + ret = -99; + goto done; + } }; - + done: /* We're done */ free(data); @@ -482,6 +799,12 @@ done: static int kodak6800_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len) { + struct kodak6800_ctx ctx = { + .dev = dev, + .endp_up = endp_up, + .endp_down = endp_down, + }; + int ret; int num; @@ -496,23 +819,19 @@ static int kodak6800_query_serno(struct libusb_device_handle *dev, uint8_t endp_ req[2] = 0x43; req[3] = 0x48; req[4] = 0x43; - req[5] = 0x03; + req[5] = 0x12; - /* Send request */ - if ((ret = send_data(dev, endp_down, - req, sizeof(req)))) + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(&ctx, req, sizeof(req), + resp, sizeof(resp), + &num))) return ret; - /* Get response */ - ret = read_data(dev, endp_up, - resp, sizeof(resp) - 1, &num); - - if (ret < 0) - return ret; if (num != 32) { ERROR("Short read! (%d/%d)\n", num, 32); - return 4; + return -2; } + strncpy(buf, (char*)resp+24, buf_len); buf[buf_len-1] = 0; @@ -521,45 +840,35 @@ static int kodak6800_query_serno(struct libusb_device_handle *dev, uint8_t endp_ static int kodak6850_send_init(struct kodak6800_ctx *ctx) { - uint8_t cmdbuf[64]; + uint8_t cmdbuf[16]; uint8_t rdbuf[64]; int ret = 0, num = 0; - memset(cmdbuf, 0, CMDBUF_LEN); + memset(cmdbuf, 0, sizeof(cmdbuf)); cmdbuf[0] = 0x03; cmdbuf[1] = 0x1b; cmdbuf[2] = 0x43; cmdbuf[3] = 0x48; cmdbuf[4] = 0x43; cmdbuf[5] = 0x4c; - - if ((ret = send_data(ctx->dev, ctx->endp_down, - cmdbuf, CMDBUF_LEN -1))) - return CUPS_BACKEND_FAILED; - - /* Read response */ - ret = read_data(ctx->dev, ctx->endp_up, - rdbuf, READBACK_LEN, &num); - if (ret < 0) - return CUPS_BACKEND_FAILED; - - if (num < 51) { - ERROR("Short read! (%d/%d)\n", num, 51); - return CUPS_BACKEND_FAILED; - } - + + /* Issue command and get response */ + if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf), + rdbuf, sizeof(rdbuf), + &num))) + return -1; + if (num != 51) { - ERROR("Unexpected readback from printer (%d/%d from 0x%02x))\n", - num, READBACK_LEN, ctx->endp_up); + ERROR("Short read! (%d/%d)\n", num, 51); return CUPS_BACKEND_FAILED; } - - if (rdbuf[0] != 0x01 || + + if (rdbuf[0] != CMD_CODE_OK || rdbuf[2] != 0x43) { ERROR("Unexpected response from printer init!\n"); return CUPS_BACKEND_FAILED; } - + // XXX I believe this the media position // saying when we have a 4x6 left on an 8x6 blank if (rdbuf[1] != 0x01 && rdbuf[1] != 0x00) { @@ -575,6 +884,7 @@ static void kodak6800_cmdline(void) DEBUG("\t\t[ -C filename ] # Set tone curve\n"); DEBUG("\t\t[ -m ] # Query media\n"); DEBUG("\t\t[ -s ] # Query status\n"); + DEBUG("\t\t[ -X jobid ] # Cancel Job\n"); } static int kodak6800_cmdline_arg(void *vctx, int argc, char **argv) @@ -582,49 +892,31 @@ static int kodak6800_cmdline_arg(void *vctx, int argc, char **argv) struct kodak6800_ctx *ctx = vctx; int i, j = 0; - /* Reset arg parsing */ - optind = 1; - opterr = 0; - while ((i = getopt(argc, argv, "C:c:ms")) >= 0) { + if (!ctx) + return -1; + + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "C:c:msX:")) >= 0) { switch(i) { + GETOPT_PROCESS_GLOBAL case 'c': - if (ctx) { - j = kodak6800_get_tonecurve(ctx, optarg); - break; - } - return 1; + j = kodak6800_get_tonecurve(ctx, optarg); + break; case 'C': - if (ctx) { - j = kodak6800_set_tonecurve(ctx, optarg); - break; - } - return 1; + j = kodak6800_set_tonecurve(ctx, optarg); + break; case 'm': - if (ctx) { - uint8_t mediabuf[MAX_MEDIA_LEN]; - struct kodak68x0_media_readback *media = (struct kodak68x0_media_readback*)mediabuf; - j = kodak6800_get_mediainfo(ctx, media); - if (!j) - kodak68x0_dump_mediainfo(media); - break; - } - return 1; - case 's': - if (ctx) { - uint8_t mediabuf[MAX_MEDIA_LEN]; - struct kodak68x0_media_readback *media = (struct kodak68x0_media_readback*)mediabuf; - struct kodak68x0_status_readback status; - j = kodak6800_get_mediainfo(ctx, media); - if (!j) - j = kodak6800_get_status(ctx, &status); - if (!j) - kodak68x0_dump_status(ctx, &status); - - break; - } - - return 1; - + kodak68x0_dump_mediainfo(ctx->media); + break; + case 's': { + struct kodak68x0_status_readback status; + j = kodak6800_get_status(ctx, &status); + if (!j) + kodak68x0_dump_status(ctx, &status); + break; + } + case 'X': + j = kodak68x0_canceljob(ctx, atoi(optarg)); + break; default: break; /* Ignore completely */ } @@ -635,7 +927,6 @@ static int kodak6800_cmdline_arg(void *vctx, int argc, char **argv) return 0; } - static void *kodak6800_init(void) { struct kodak6800_ctx *ctx = malloc(sizeof(struct kodak6800_ctx)); @@ -645,8 +936,9 @@ static void *kodak6800_init(void) } memset(ctx, 0, sizeof(struct kodak6800_ctx)); + ctx->media = malloc(MAX_MEDIA_LEN); + ctx->type = P_ANY; - ctx->media = -1; return ctx; } @@ -658,20 +950,23 @@ static void kodak6800_attach(void *vctx, struct libusb_device_handle *dev, struct libusb_device *device; struct libusb_device_descriptor desc; - UNUSED(jobid); - - ctx->dev = dev; + ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; device = libusb_get_device(dev); libusb_get_device_descriptor(device, &desc); - /* Map out device type */ - if (desc.idProduct == USB_PID_KODAK_6850) - ctx->type = P_KODAK_6850; - else - ctx->type = P_KODAK_6800; + ctx->type = lookup_printer_type(&kodak6800_backend, + desc.idVendor, desc.idProduct); + + /* Ensure jobid is sane */ + ctx->jobid = (jobid & 0x7f) + 1; + + /* Query media info */ + if (kodak6800_get_mediainfo(ctx, ctx->media)) { + ERROR("Can't query media\n"); + } } static void kodak6800_teardown(void *vctx) { @@ -746,144 +1041,123 @@ static int kodak6800_main_loop(void *vctx, int copies) { struct kodak6800_ctx *ctx = vctx; struct kodak68x0_status_readback status; - uint8_t cmdbuf[CMDBUF_LEN]; - - uint8_t mediabuf[MAX_MEDIA_LEN]; - struct kodak68x0_media_readback *media = (struct kodak68x0_media_readback*)mediabuf; - int num, ret; if (!ctx) return CUPS_BACKEND_FAILED; + /* Fix max print count. */ + if (copies > 9999) // XXX test against remaining media + copies = 9999; + /* Printer handles generating copies.. */ - if (ctx->hdr.copies < copies) - ctx->hdr.copies = copies; - copies = 1; - - /* Query loaded media */ - INFO("Querying loaded media\n"); - ret = kodak6800_get_mediainfo(ctx, media); - if (ret < 0) - return CUPS_BACKEND_FAILED; - - /* Appears to depend on media */ - if (media->media != KODAK68x0_MEDIA_6R && - media->media != 0x03) { - ERROR("Unrecognized media type %02x\n", media->media); + ctx->hdr.copies = cpu_to_be16(uint16_to_packed_bcd(copies)); + + /* Validate media */ + if (ctx->media->media != KODAK68x0_MEDIA_6R && + ctx->media->media != KODAK68x0_MEDIA_UNK) { + ERROR("Unrecognized media type %02x\n", ctx->media->media); return CUPS_BACKEND_STOP; } /* Validate against supported media list */ - for (num = 0 ; num < media->count; num++) { - if (media->sizes[num].height == ctx->hdr.rows && - media->sizes[num].width == ctx->hdr.columns) + for (num = 0 ; num < ctx->media->count; num++) { + if (ctx->media->sizes[num].height == ctx->hdr.rows && + ctx->media->sizes[num].width == ctx->hdr.columns && + ctx->media->sizes[num].code2 == 0x00) break; } - if (num == media->count) { + if (num == ctx->media->count) { ERROR("Print size unsupported by media!\n"); return CUPS_BACKEND_HOLD; } -top: INFO("Waiting for printer idle\n"); while(1) { if (kodak6800_get_status(ctx, &status)) return CUPS_BACKEND_FAILED; - if (status.errtype || status.errtype2 || status.errcode) { - ERROR("Printer error reported: %s (%d/%d) # %d\n", - kodak68x0_error_codes(status.errtype, status.errtype2), - status.errtype, status.errtype2, status.errcode); + if (status.status1 == STATE_STATUS1_ERROR) { + INFO("Printer State: %s # %02x %08x %02x\n", + kodak68x0_status_str(&status), + status.status1, be32_to_cpu(status.status2), status.errcode); return CUPS_BACKEND_FAILED; } - if (status.sts1 == 0x01) { - // do nothing, this is expected. - sleep(1); - continue; - } else if (status.sts1 != 0x02) { - ERROR("Unknown status1 0x%02x\n", status.sts1); - return CUPS_BACKEND_FAILED; - } - - if (status.sts2 == 0x02) { - ERROR("Printer is out of media!\n"); - return CUPS_BACKEND_STOP; - } else if (status.sts2 == 0x03) { - ERROR("Printer is offline!\n"); - return CUPS_BACKEND_STOP; - } else if (status.sts2 != 0x01) { - ERROR("Unknown status 0x%02x\n", status.sts2); - return CUPS_BACKEND_FAILED; - } else { + if (status.status == STATUS_IDLE) break; - } + + /* See if we have an open bank */ + if (!status.b1_remain || + !status.b2_remain) + break; + + sleep(1); } if (ctx->type == P_KODAK_6850) { - INFO("Sending 6850 init sequence\n"); +// INFO("Sending 6850 init sequence\n"); ret = kodak6850_send_init(ctx); if (ret) return ret; - sleep(1); } - - /* Set up print job header */ - memcpy(cmdbuf, &ctx->hdr, CMDBUF_LEN); - - /* 6850 uses same spool format but different header gets sent */ - if (ctx->type == P_KODAK_6850) { - if (ctx->hdr.size == 0x00) - cmdbuf[7] = 0x04; - else if (ctx->hdr.size == 0x06) - cmdbuf[7] = 0x05; /* XXX audit this! */ - } - - /* If we're printing a 4x6 on 8x6 media... */ + + ctx->hdr.jobid = ctx->jobid; + +#if 0 + /* If we want to disable 4x6 rewind on 8x6 media.. */ if (ctx->hdr.size == 0x00 && - be16_to_cpu(media->sizes[0].width) == 0x0982) { - cmdbuf[14] = 0x06; - cmdbuf[16] = 0x01; + be16_to_cpu(ctx->media->sizes[0].width) == 0x0982) { + ctx->hdr.size = 0x06; + ctx->hdr.mode = 0x01; } - - INFO("Sending image header\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, - cmdbuf, CMDBUF_LEN))) +#endif + + INFO("Initiating Print Job\n"); + if ((ret = kodak6800_do_cmd(ctx, (uint8_t*) &ctx->hdr, sizeof(ctx->hdr), + &status, sizeof(status), + &num))) return ret; - sleep(1); + + if (status.hdr != CMD_CODE_OK) { + ERROR("Unexpected response from print command!\n"); + return CUPS_BACKEND_FAILED; + } + +// sleep(1); // Appears to be necessary for reliability INFO("Sending image data\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, + if ((ret = send_data(ctx->dev, ctx->endp_down, ctx->databuf, ctx->datalen))) return CUPS_BACKEND_FAILED; INFO("Waiting for printer to acknowledge completion\n"); - sleep(1); - while(1) { + do { + sleep(1); if (kodak6800_get_status(ctx, &status)) return CUPS_BACKEND_FAILED; - if (status.sts1 == 0x01) { - // do nothing, this is expected. - } else if (status.sts1 != 0x02) { - ERROR("Unknown status1 0x%02x\n", status.sts1); + if (status.status1 == STATE_STATUS1_ERROR) { + INFO("Printer State: %s # %02x %08x %02x\n", + kodak68x0_status_str(&status), + status.status1, be32_to_cpu(status.status2), status.errcode); return CUPS_BACKEND_FAILED; - } else { + } + + /* If all prints are complete, we're done! */ + if (status.b1_jobid == ctx->hdr.jobid && status.b1_complete == status.b1_total) + break; + if (status.b2_jobid == ctx->hdr.jobid && status.b2_complete == status.b2_total) + break; + + if (fast_return) { + INFO("Fast return mode enabled.\n"); break; } - sleep(1); - } - - /* Clean up */ - if (terminate) - copies = 1; - INFO("Print complete (%d copies remaining)\n", copies - 1); + } while (1); - if (copies && --copies) { - goto top; - } + INFO("Print complete\n"); return CUPS_BACKEND_OK; } @@ -891,7 +1165,7 @@ top: /* Exported */ struct dyesub_backend kodak6800_backend = { .name = "Kodak 6800/6850", - .version = "0.43", + .version = "0.51", .uri_prefix = "kodak6800", .cmdline_usage = kodak6800_cmdline, .cmdline_arg = kodak6800_cmdline_arg, @@ -901,7 +1175,7 @@ struct dyesub_backend kodak6800_backend = { .read_parse = kodak6800_read_parse, .main_loop = kodak6800_main_loop, .query_serno = kodak6800_query_serno, - .devices = { + .devices = { { USB_VID_KODAK, USB_PID_KODAK_6800, P_KODAK_6800, "Kodak"}, { USB_VID_KODAK, USB_PID_KODAK_6850, P_KODAK_6850, "Kodak"}, { 0, 0, 0, ""} @@ -915,148 +1189,25 @@ struct dyesub_backend kodak6800_backend = { 6850 Adds support for 5x7, with 1548 pixels per row and 2140 columns. - Header: + All fields are BIG ENDIAN unless otherwise specified. - 03 1b 43 48 43 0a 00 01 00 Fixed header - NN Number of copies (01-255) - WW WW Number of columns, big endian. (Fixed at 1844 on 6800) - HH HH Number of rows, big endian. - SS 0x00 (4x6) 0x06 (8x6) 0x07 (5x7 on 6850) - LL Laminate, 0x00 (off) or 0x01 (on) - UU 0x01 for multi-cut, 0x00 otherwise. + Header: - Note: For 4x6 prints on 6x8 media, print size (SS) is set to 0x06 and the - final octet is set to 0x01. + 03 1b 43 48 43 0a 00 01 Fixed header + NN NN Number of copies in BCD form (0001->9999) + WW WW Number of columns (Fixed at 1844 on 6800) + HH HH Number of rows. + SS Print size -- 0x00 (4x6) 0x06 (8x6) 0x07 (5x7 on 6850) + LL Laminate mode -- 0x00 (off) or 0x01 (on) + UU Print mode -- 0x00 (normal) or (0x01) 4x6 on 8x6 ************************************************************************ - Kodak 6800 Printer Comms: - - [[file header]] 03 1b 43 48 43 0a 00 01 00 NN WW WW HH HH SS LL UU - - (see above for details on fields) - --> 03 1b 43 48 43 03 00 00 00 00 00 00 00 00 00 00 [status query] -<- [51 octets] - - 01 02 01 00 00 00 00 00 00 00 a2 7b 00 00 a2 7b - 00 00 02 f4 00 00 e6 b1 00 00 00 1a 00 03 00 e8 - 00 01 00 83 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 - --> 03 1b 43 48 43 1a 00 00 00 00 00 00 00 00 00 00 [media query] -<- [58 octets] - - 01 XX 00 00 00 00 00 04 06 WW WW MM MM 01 00 00 [MM MM == max printable size of media, 09 82 == 2434 for 6x8!] - 00 00 06 WW WW 09 ba 01 02 00 00 00 06 WW WW HH [09 ba == 2940 == cut area?] - HH 01 01 00 00 00 06 WW WW MM MM 01 03 00 00 00 [XX == 0b or 03 == media type?] - 00 00 00 00 00 00 00 00 00 00 - - --> 03 1b 43 48 43 0a 00 01 00 01 WW WW HH HH 06 01 [ image header, modified, see above ] - 01 - -<- [51 octets] - - 01 02 01 00 00 00 00 00 00 00 a2 7b 00 00 a2 7b - 00 00 02 f4 00 00 e6 b1 00 00 00 1a 00 03 00 e8 - 00 01 00 83 01 00 00 01 00 00 00 01 00 00 00 00 [ note the "01" after "83", and the extra two "01"s ] - 00 00 00 - --> [4K of plane data] --> ... --> [4K of plane data] --> [remainder of plane data + 17 bytes of 0xff] - --> 03 1b 43 48 43 03 00 00 00 00 00 00 00 00 00 00 [status query] -<- [51 octets] - - 01 02 01 00 00 00 00 00 00 00 a2 7c 00 00 a2 7c [ note a2 7c vs a2 7b ] - 00 00 01 7a 00 00 e6 b3 00 00 00 1a 00 03 00 e8 [ note 01 7a vs 02 f4, e6 b3 vs e6 b1 ] - 00 01 00 83 01 00 00 00 00 01 00 01 00 00 00 00 [ note the moved '01' in the middle ] - 00 00 00 - --> 03 1b 43 48 43 03 00 00 00 00 00 00 00 00 00 00 [ status query ] -<- [51 octets, repeats] - - Possible Serial number query: - --> 03 1b 43 48 43 12 00 00 00 00 00 00 00 00 00 00 -<- [32 octets] - - 00 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 [[ Pascal string? ]] - 20 20 20 20 20 20 20 20 36 30 34 33 4d 32 38 31 [[ ..." 6043M281" ]] - --> 03 1b 43 48 43 0c 54 4f 4e 45 65 00 00 00 00 00 -<- [51 octets] - - [[ typical status response ]] - [[ Followed by reset. ]] - - Read tone curve data: - --> 03 1b 43 48 43 0c 54 4f 4e 45 72 01 00 00 00 00 -<- [51 octets] - - [[ typical status response ]] - --> 03 1b 43 48 43 0c 54 4f 4e 45 20 -<- [64 octets] - - 81 01 07 07 27 07 72 07 c8 07 f8 07 22 07 48 08 - 68 08 88 08 b3 08 db 08 f7 08 09 09 2e 09 49 09 - 65 09 80 09 aa 09 ca 09 e2 09 fa 09 12 0a 32 0a - 42 0a 66 0a 81 0a 9a 0a c3 0a d9 0a ee 0a 04 0b - --> 03 1b 43 48 43 0c 54 4f 4e 45 20 -<- [64 octets] - - [[ repeats for total of 24 packets. total of 1.5KiB. ]] - - Write tone curve data: - --> 03 1b 43 48 43 0c 54 4f 4e 45 77 01 00 00 00 00 -<- [51 octets] - - [[ typical status response ]] - --> 03 00 00 46 06 53 06 c0 06 07 07 37 07 5d 07 87 - 07 a1 07 c8 07 08 08 08 08 08 08 48 08 68 08 88 - 08 a9 08 b9 08 d9 08 f9 08 12 09 2e 09 49 09 70 - 09 89 08 99 09 ba 09 ca 08 da 09 0a 0a 24 0a 38 -<- [51 octets] - - [[ typical status response ]] - --> 03 0a 53 0a 66 0a 81 0a ... - .... --> 03 cf 38 0a 39 3d 39 79 39 96 39 b6 39 fb 39 01 - 34 0a 34 08 3a 0c 1a 10 3a -<- [51 octets] - - [[ typical status response ]] - - [[ total of 24 packets * 64, and then one final packet of 25: 1562 total. ]] - [[ It apepars the extra 25 bytes are to compensate for the leading '03' on - each of the 25 URBs. ]] - - *********************************************************************** + Note: 6800 is Shinko CHC-S1145-5A, 6850 is Shinko CHC-S1145-5B - Kodak 6850 Printer Comms: + Both are very similar to Shinko S1245! - [[file header]] 03 1b 43 48 43 0a 00 XX 00 CC WW WW HH HH SS LL UU - - Note: 'XX' paper code is 0x04 for 4x6, 0x06 for 6x8 on the 6850! - - (See above for details on all other fields) - --> 03 1b 43 48 43 03 00 00 00 00 00 00 00 00 00 00 [status query] -<- [51 octets] - - 01 02 01 00 00 00 00 00 00 00 21 75 00 00 08 52 - 00 00 01 29 00 00 3b 0a 00 00 00 0e 00 03 02 90 - 00 01 02 1d 03 00 00 00 00 01 00 01 00 00 00 00 - 00 00 00 + ************************************************************************ -> 03 1b 43 48 43 4c 00 00 00 00 00 00 00 00 00 00 [???] <- [51 octets] @@ -1071,149 +1222,4 @@ struct dyesub_backend kodak6800_backend = { 00 01 02 1c 00 00 00 00 00 01 00 01 00 00 00 00 00 00 00 --> 03 1b 43 48 43 03 00 00 00 00 00 00 00 00 00 00 [status query] -<- [51 octets -- same as status query before ] - --> 03 1b 43 48 43 1a 00 00 00 00 00 00 00 00 00 00 [media query] -<- [68 octets] - - 01 XX 00 00 00 00 00 06 06 WW WW MM MM 01 00 00 [MM MM == max printable size of media, 09 82 == 2434 for 6x8!] - 00 00 06 WW WW 09 ba 01 02 01 00 00 06 WW WW HH [09 ba == 2940 == cut area?] - HH 01 01 00 00 00 06 WW WW MM MM 01 03 00 00 00 [XX == 0b or 03 == media type?] - 06 WW WW 09 ba 01 05 01 00 00 06 WW WW HH HH 01 - 04 00 00 00 - --> 03 1b 43 48 43 0a 00 04 00 01 07 34 04 d8 06 01 [ image header, modified, see above ] - 01 - -<- [51 octets] - - 01 02 01 00 00 00 00 00 00 00 21 75 00 00 08 52 - 00 00 01 29 00 00 3b 0a 00 00 00 0e 00 03 02 90 - 00 01 02 1d 04 00 00 01 00 00 00 01 00 00 00 00 [ note the "04" after "1d", and the moved '01' ] - 00 00 00 - --> [4K of plane data] --> ... --> [4K of plane data] --> [remainder of plane data] - --> 03 1b 43 48 43 03 00 00 00 00 00 00 00 00 00 00 [status query] -<- [51 octets] - - 01 02 01 00 00 00 00 00 00 00 21 76 00 00 08 53 [ note 21 76, 08 53, 01 2a incremented by 1 ] - 00 00 01 2a 00 00 3b 0c 00 00 00 0e 00 03 02 90 [ note 3b 0c incremeted by 2 ] - 00 01 02 1d 04 00 00 01 00 00 00 01 00 00 00 00 - 00 00 00 - - Possible Serial number query: - --> 03 1b 43 48 43 12 00 00 00 00 00 00 00 00 00 00 - 00 -<- [32 octets] - - 00 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 [[ Pascal string? ]] - 20 20 20 20 20 20 20 20 36 30 39 37 4b 53 34 39 [[ ..." 6097KS49" ]] - - Read tone curve data: - --> 03 1b 43 48 43 0c 54 4f 4e 45 72 01 00 00 00 00 -<- [51 octets] - - [[ typical status response ]] - --> 03 1b 43 48 43 0c 54 4f 4e 45 20 -<- [64 octets] - - 81 01 07 07 27 07 72 07 c8 07 f8 07 22 07 48 08 - 68 08 88 08 b3 08 db 08 f7 08 09 09 2e 09 49 09 - 65 09 80 09 aa 09 ca 09 e2 09 fa 09 12 0a 32 0a - 42 0a 66 0a 81 0a 9a 0a c3 0a d9 0a ee 0a 04 0b - --> 03 1b 43 48 43 0c 54 4f 4e 45 20 -<- [64 octets] - - [[ repeats for total of 24 packets. total of 1.5KiB. ]] - --> 03 1b 43 48 43 0c 54 4f 4e 45 65 00 00 00 00 00 -<- [51 octets] - - [[ typical status response ]] - - Maybe this resets the calibration table: - --> 03 1b 43 48 43 05 00 00 00 00 00 00 00 00 00 00 [???] -<- [34 octets] - - 01 00 04 00 00 00 01 00 01 00 02 00 00 00 01 00 - 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 - - Write tone curve data: - --> 03 1b 43 48 43 0c 54 4f 4e 45 77 01 00 00 00 00 -<- [51 octets] - - [[ typical status response ]] - --> 03 00 00 46 06 53 06 c0 06 07 07 37 07 5d 07 87 - 07 a1 07 c8 07 08 08 08 08 08 08 48 08 68 08 88 - 08 a9 08 b9 08 d9 08 f9 08 12 09 2e 09 49 09 70 - 09 89 08 99 09 ba 09 ca 08 da 09 0a 0a 24 0a 38 -<- [51 octets] - - [[ typical status response ]] - --> 03 0a 53 0a 66 0a 81 0a ... - .... --> 03 cf 38 0a 39 3d 39 79 39 96 39 b6 39 fb 39 01 - 34 0a 34 08 3a 0c 1a 10 3a -<- [51 octets] - - [[ typical status response ]] - - [[ total of 24 packets * 64, and then one final packet of 25: 1562 total. ]] - [[ It apepars the extra 25 bytes are to compensate for the leading '03' on - each of the 25 URBs. ]] - - Also seen on the 6850: - -DEBUG: readback: - -01 02 03 00 00 00 01 00 00 01 5f 6f 00 01 5f 6f -00 00 00 09 00 02 90 44 00 00 00 55 00 03 02 90 -00 01 02 1d 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 - -INIT/??? -DEBUG: readback: - -01 02 03 00 00 00 00 00 00 01 5f 6f 00 01 5f 6f -00 00 00 09 00 02 90 44 00 00 00 55 00 03 02 90 -00 01 02 1d 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 - -??? 6x8c - -DEBUG: readback: - -01 02 01 00 00 00 00 00 00 01 5f 6f 00 01 5f 6f -00 00 00 09 00 02 90 44 00 00 00 55 00 03 02 90 -00 01 02 1d 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 - -Seen on the 6850 with no media loaded: - -01 02 02 00 00 00 10 00 00 00 5d 1d 00 00 5d 1d -00 00 00 00 00 00 b7 cc 00 00 00 00 00 03 02 8c -00 01 02 1c 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 - -Seen on 6850 with 6R media (6x8) while offline: - -01 02 03 00 00 00 03 00 00 00 5d 1f 00 00 5d 1f -00 00 00 01 00 00 b7 d3 00 00 00 5c 00 03 02 8c -00 01 02 1c 00 00 00 00 00 01 00 01 00 00 00 00 -00 00 00 - */ diff --git a/src/cups/mitsu70x_print.c b/src/cups/mitsu70x_print.c index 2b9e56d..dcff841 100644 --- a/src/cups/mitsu70x_print.c +++ b/src/cups/mitsu70x_print.c @@ -35,6 +35,8 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND mitsu70x_backend + #include "backend_common.h" #define USB_VID_MITSU 0x06D3 @@ -49,14 +51,13 @@ struct mitsu70x_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; uint8_t *databuf; int datalen; uint16_t rows; uint16_t cols; - - int k60; }; /* Program states */ @@ -155,12 +156,8 @@ static void mitsu70x_attach(void *vctx, struct libusb_device_handle *dev, device = libusb_get_device(dev); libusb_get_device_descriptor(device, &desc); - if (desc.idProduct == USB_PID_MITSU_K60) - ctx->k60 = 1; - - if (desc.idProduct == USB_PID_KODAK305) - ctx->k60 = 1; - + ctx->type = lookup_printer_type(&mitsu70x_backend, + desc.idVendor, desc.idProduct); } @@ -357,7 +354,7 @@ static int mitsu70x_get_status(struct mitsu70x_ctx *ctx, struct mitsu70x_status_ static int mitsu70x_main_loop(void *vctx, int copies) { struct mitsu70x_ctx *ctx = vctx; - struct mitsu70x_state rdbuf, rdbuf2; + struct mitsu70x_state rdbuf = { .hdr = 0 }, rdbuf2 = { .hdr = 0 }; int last_state = -1, state = S_IDLE; int ret; @@ -424,7 +421,7 @@ top: INFO("Sending header sequence\n"); /* K60 may require fixups */ - if (ctx->k60) { + if (ctx->type == P_MITSU_K60) { struct mitsu70x_hdr *hdr = (struct mitsu70x_hdr*) (ctx->databuf + sizeof(struct mitsu70x_hdr)); /* K60 only has a lower deck */ hdr->deck = 1; @@ -539,7 +536,7 @@ static int mitsu70x_query_status(struct mitsu70x_ctx *ctx) static int mitsu70x_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len) { int ret, i; - struct mitsu70x_status_resp resp; + struct mitsu70x_status_resp resp = { .hdr = { 0 } }; struct mitsu70x_ctx ctx = { .dev = dev, @@ -571,17 +568,18 @@ static int mitsu70x_cmdline_arg(void *vctx, int argc, char **argv) struct mitsu70x_ctx *ctx = vctx; int i, j = 0; + if (!ctx) + return -1; + /* Reset arg parsing */ optind = 1; opterr = 0; - while ((i = getopt(argc, argv, "s")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "s")) >= 0) { switch(i) { + GETOPT_PROCESS_GLOBAL case 's': - if (ctx) { - j = mitsu70x_query_status(ctx); - break; - } - return 1; + j = mitsu70x_query_status(ctx); + break; default: break; /* Ignore completely */ } @@ -596,7 +594,7 @@ static int mitsu70x_cmdline_arg(void *vctx, int argc, char **argv) /* Exported */ struct dyesub_backend mitsu70x_backend = { .name = "Mitsubishi CP-D70/D707/K60", - .version = "0.31", + .version = "0.32", .uri_prefix = "mitsu70x", .cmdline_usage = mitsu70x_cmdline, .cmdline_arg = mitsu70x_cmdline_arg, @@ -608,9 +606,9 @@ struct dyesub_backend mitsu70x_backend = { .query_serno = mitsu70x_query_serno, .devices = { { USB_VID_MITSU, USB_PID_MITSU_D70X, P_MITSU_D70X, ""}, - { USB_VID_MITSU, USB_PID_MITSU_K60, P_MITSU_D70X, ""}, + { USB_VID_MITSU, USB_PID_MITSU_K60, P_MITSU_K60, ""}, // { USB_VID_MITSU, USB_PID_MITSU_D80, P_MITSU_D70X, ""}, - { USB_VID_KODAK, USB_PID_KODAK305, P_MITSU_D70X, ""}, + { USB_VID_KODAK, USB_PID_KODAK305, P_MITSU_K60, ""}, { 0, 0, 0, ""} } }; diff --git a/src/cups/mitsu9550_print.c b/src/cups/mitsu9550_print.c index 7b83e6c..952baa3 100644 --- a/src/cups/mitsu9550_print.c +++ b/src/cups/mitsu9550_print.c @@ -35,6 +35,8 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND mitsu9550_backend + #include "backend_common.h" #define USB_VID_MITSU 0x06D3 @@ -46,14 +48,13 @@ struct mitsu9550_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; uint8_t *databuf; int datalen; int is_s_variant; - int fast_return; - uint16_t rows; uint16_t cols; }; @@ -149,10 +150,6 @@ static void *mitsu9550_init(void) } memset(ctx, 0, sizeof(struct mitsu9550_ctx)); - /* Use Fast return by default in CUPS mode */ - if (getenv("DEVICE_URI") || getenv("FAST_RETURN")) - ctx->fast_return = 1; - return ctx; } @@ -172,8 +169,8 @@ static void mitsu9550_attach(void *vctx, struct libusb_device_handle *dev, device = libusb_get_device(dev); libusb_get_device_descriptor(device, &desc); - if (desc.idProduct == USB_PID_MITSU_9550DS) - ctx->is_s_variant = 1; + ctx->type = lookup_printer_type(&mitsu9550_backend, + desc.idVendor, desc.idProduct); } @@ -288,28 +285,28 @@ static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int st static int validate_media(int type, int cols, int rows) { switch(type) { case 0x01: /* 3.5x5 */ - if (cols != 1812 || rows != 1240) + if (cols != 1812 && rows != 1240) return 1; break; case 0x02: /* 4x6 */ case 0x03: /* PC ??? */ if (cols != 2152) return 1; - if (rows != 1416 || rows != 1184 || + if (rows != 1416 && rows != 1184 && rows != 1240) return 1; break; case 0x04: /* 5x7 */ if (cols != 1812) return 1; - if (rows != 1240 || rows != 2452) + if (rows != 1240 && rows != 2452) return 1; break; case 0x05: /* 6x9 */ if (cols != 2152) return 1; - if (rows != 1416 || rows != 2972 || - rows != 2956 || rows != 3146) + if (rows != 1416 && rows != 2972 && + rows != 2956 && rows != 3146) return 1; break; case 0x06: /* V */ @@ -332,15 +329,15 @@ static int mitsu9550_main_loop(void *vctx, int copies) { if (!ctx) return CUPS_BACKEND_FAILED; - - /* This printer handles copies internally */ + + /* Update printjob header to reflect number of requested copies */ hdr2 = (struct mitsu9550_hdr2 *) (ctx->databuf + sizeof(struct mitsu9550_hdr1)); hdr2->copies = cpu_to_be16(copies); ptr = ctx->databuf; top: - if (ctx->is_s_variant) { + if (ctx->type == P_MITSU_9550S) { int num; /* Send "unknown 1" command */ @@ -405,7 +402,7 @@ top: /* Now it's time for the actual print job! */ - if (ctx->is_s_variant) { + if (ctx->type == P_MITSU_9550S) { cmd.cmd[0] = 0x1b; cmd.cmd[1] = 0x44; cmd.cmd[2] = 0; @@ -463,7 +460,7 @@ top: (uint8_t*) ptr, sizeof(struct mitsu9550_hdr3)))) return CUPS_BACKEND_FAILED; ptr += sizeof(struct mitsu9550_hdr3); - if (!ctx->is_s_variant) { + if (ctx->type != P_MITSU_9550S) { // XXX need to investigate what hdr4 is about if ((ret = send_data(ctx->dev, ctx->endp_down, (uint8_t*) ptr, sizeof(struct mitsu9550_hdr4)))) @@ -471,7 +468,7 @@ top: } ptr += sizeof(struct mitsu9550_hdr4); - if (ctx->is_s_variant) { + if (ctx->type == P_MITSU_9550S) { /* Send "start data" command */ cmd.cmd[0] = 0x1b; cmd.cmd[1] = 0x5a; @@ -546,7 +543,7 @@ top: } } - if (ctx->is_s_variant) { + if (ctx->type == P_MITSU_9550S) { /* Send "end data" command */ cmd.cmd[0] = 0x1b; cmd.cmd[1] = 0x50; @@ -592,12 +589,12 @@ top: if (!sts->sts1) /* If printer transitions to idle */ break; - if (ctx->fast_return && !be16_to_cpu(sts->copies)) { /* No remaining prints */ + if (fast_return && !be16_to_cpu(sts->copies)) { /* No remaining prints */ INFO("Fast return mode enabled.\n"); break; } - if (ctx->fast_return && !sts->sts5) { /* Ready for another job */ + if (fast_return && !sts->sts5) { /* Ready for another job */ INFO("Fast return mode enabled.\n"); break; } @@ -605,18 +602,7 @@ top: sleep(1); } - /* This printer handles copies internally */ - copies = 1; - - /* Clean up */ - if (terminate) - copies = 1; - - INFO("Print complete (%d copies remaining)\n", copies - 1); - - if (copies && --copies) { - goto top; - } + INFO("Print complete\n"); return CUPS_BACKEND_OK; } @@ -744,7 +730,6 @@ static void mitsu9550_cmdline(void) { DEBUG("\t\t[ -m ] # Query media\n"); DEBUG("\t\t[ -s ] # Query status\n"); - DEBUG("\t\t[ -f ] # Enable fast return mode\n"); } static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv) @@ -752,30 +737,21 @@ static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv) struct mitsu9550_ctx *ctx = vctx; int i, j = 0; + if (!ctx) + return -1; + /* Reset arg parsing */ optind = 1; opterr = 0; - while ((i = getopt(argc, argv, "mfs")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "ms")) >= 0) { switch(i) { - case 'm': - if (ctx) { - j = mitsu9550_query_media(ctx); - break; - } - return 1; + GETOPT_PROCESS_GLOBAL + case 'm': + j = mitsu9550_query_media(ctx); + break; case 's': - if (ctx) { - j = mitsu9550_query_status(ctx); - break; - } - return 1; - - case 'f': - if (ctx) { - ctx->fast_return = 1; - break; - } - return 1; + j = mitsu9550_query_status(ctx); + break; default: break; /* Ignore completely */ } @@ -789,7 +765,7 @@ static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv) /* Exported */ struct dyesub_backend mitsu9550_backend = { .name = "Mitsubishi CP-9550DW-S", - .version = "0.12", + .version = "0.15", .uri_prefix = "mitsu9550", .cmdline_usage = mitsu9550_cmdline, .cmdline_arg = mitsu9550_cmdline_arg, @@ -801,7 +777,7 @@ struct dyesub_backend mitsu9550_backend = { .query_serno = mitsu9550_query_serno, .devices = { { USB_VID_MITSU, USB_PID_MITSU_9550D, P_MITSU_9550, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9550DS, P_MITSU_9550, ""}, + { USB_VID_MITSU, USB_PID_MITSU_9550DS, P_MITSU_9550S, ""}, { 0, 0, 0, ""} } }; diff --git a/src/cups/selphy_print.c b/src/cups/selphy_print.c index 2c9bab0..8da09e0 100644 --- a/src/cups/selphy_print.c +++ b/src/cups/selphy_print.c @@ -35,8 +35,12 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND canonselphy_backend + #include "backend_common.h" +#define P_ES40_CP790 (P_END + 1) // used for detection only + /* Exported */ #define USB_VID_CANON 0x04a9 #define USB_PID_CANON_CP10 0x304A @@ -291,13 +295,6 @@ static struct printer_data selphy_printers[] = { .paper_code_offset = -1, .error_detect = es3_error_detect, }, - /* PLACEHOLDER FOR DETECTION PURPOSES ONLY */ - { .type = P_ES40_CP790, - .model = "SELPHY ES40/CP790", - .init_length = 16, - .foot_length = 12, - .pgcode_offset = 2, - }, { .type = P_ES40, .model = "SELPHY ES40", .init_length = 16, @@ -504,6 +501,7 @@ struct canonselphy_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; struct printer_data *printer; @@ -546,6 +544,8 @@ static void *canonselphy_init(void) return ctx; } +extern struct dyesub_backend canonselphy_backend; + static void canonselphy_attach(void *vctx, struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { @@ -562,26 +562,11 @@ static void canonselphy_attach(void *vctx, struct libusb_device_handle *dev, device = libusb_get_device(dev); libusb_get_device_descriptor(device, &desc); - /* Special cases for some models */ - if (ctx->printer->type == P_ES40_CP790) { - int i; - int printer_type; - - - if (desc.idProduct == USB_PID_CANON_CP790) - printer_type = P_CP790; - else if (desc.idProduct == USB_PID_CANON_ES40) - printer_type = P_ES40; + ctx->type = lookup_printer_type(&canonselphy_backend, + desc.idVendor, desc.idProduct); - for (i = 0; selphy_printers[i].type != -1; i++) { - if (selphy_printers[i].type == printer_type) { - ctx->printer = &selphy_printers[i]; - break; - } - } - } else if (desc.idProduct == USB_PID_CANON_CP900) { + if (desc.idProduct == USB_PID_CANON_CP900) ctx->cp900 = 1; - } } static void canonselphy_teardown(void *vctx) { @@ -627,6 +612,15 @@ static int canonselphy_early_parse(void *vctx, int data_fd) } printer_type = parse_printjob(ctx->buffer, &ctx->bw_mode, &ctx->plane_len); + /* Special cases for some models */ + if (printer_type == P_ES40_CP790) { + if (ctx->type == P_CP790) + printer_type = P_CP790; + else + printer_type = P_ES40; + } + + /* Look up the printer entry */ for (i = 0; selphy_printers[i].type != -1; i++) { if (selphy_printers[i].type == printer_type) { ctx->printer = &selphy_printers[i]; @@ -638,14 +632,19 @@ static int canonselphy_early_parse(void *vctx, int data_fd) return -1; } + INFO("%sFile intended for a '%s' printer\n", ctx->bw_mode? "B/W " : "", ctx->printer->model); + + if (ctx->printer->type != ctx->type) { + ERROR("Printer/Job mismatch (%d/%d)\n", ctx->type, ctx->printer->type); + return -1; + } + ctx->plane_len += 12; /* Add in plane header length! */ if (ctx->printer->pgcode_offset != -1) ctx->paper_code = ctx->printer->paper_codes[ctx->buffer[ctx->printer->pgcode_offset]]; else ctx->paper_code = -1; - INFO("%sFile intended for a '%s' printer\n", ctx->bw_mode? "B/W " : "", ctx->printer->model); - return printer_type; } @@ -657,6 +656,11 @@ static int canonselphy_read_parse(void *vctx, int data_fd) if (!ctx) return CUPS_BACKEND_FAILED; + /* Perform early parsing */ + i = canonselphy_early_parse(ctx, data_fd); + if (i < 0) + return CUPS_BACKEND_FAILED; + if (ctx->header) { free(ctx->header); ctx->header = NULL; @@ -794,7 +798,7 @@ top: /* Make sure paper/ribbon is correct */ if (ctx->paper_code != -1) { - if (ctx->printer->type == P_CP_XXX) { + if (ctx->type == P_CP_XXX) { uint8_t pc = rdbuf[ctx->printer->paper_code_offset]; if (((pc >> 4) & 0xf) != (ctx->paper_code & 0x0f)) { @@ -825,7 +829,7 @@ top: return CUPS_BACKEND_HOLD; /* Hold this job, don't stop queue */ } } - } else if (ctx->printer->type == P_CP790) { + } else if (ctx->type == P_CP790) { uint8_t ribbon = rdbuf[4] >> 4; uint8_t paper = rdbuf[5]; @@ -949,14 +953,36 @@ top: return CUPS_BACKEND_OK; } +static int canonselphy_cmdline_arg(void *vctx, int argc, char **argv) +{ + struct canonselphy_ctx *ctx = vctx; + int i, j = 0; + + if (!ctx) + return -1; + + /* Reset arg parsing */ + optind = 1; + opterr = 0; + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL)) >= 0) { + switch(i) { + GETOPT_PROCESS_GLOBAL + } + + if (j) return j; + } + + return 0; +} + struct dyesub_backend canonselphy_backend = { .name = "Canon SELPHY CP/ES", - .version = "0.87", + .version = "0.89", .uri_prefix = "canonselphy", + .cmdline_arg = canonselphy_cmdline_arg, .init = canonselphy_init, .attach = canonselphy_attach, .teardown = canonselphy_teardown, - .early_parse = canonselphy_early_parse, .read_parse = canonselphy_read_parse, .main_loop = canonselphy_main_loop, .devices = { @@ -980,7 +1006,7 @@ struct dyesub_backend canonselphy_backend = { { USB_VID_CANON, USB_PID_CANON_CP760, P_CP_XXX, ""}, { USB_VID_CANON, USB_PID_CANON_CP770, P_CP_XXX, ""}, { USB_VID_CANON, USB_PID_CANON_CP780, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP790, P_ES40_CP790, ""}, + { USB_VID_CANON, USB_PID_CANON_CP790, P_CP790, ""}, { USB_VID_CANON, USB_PID_CANON_CP800, P_CP_XXX, ""}, { USB_VID_CANON, USB_PID_CANON_CP810, P_CP_XXX, ""}, { USB_VID_CANON, USB_PID_CANON_CP900, P_CP_XXX, ""}, @@ -989,7 +1015,7 @@ struct dyesub_backend canonselphy_backend = { { USB_VID_CANON, USB_PID_CANON_ES20, P_ES2_20, ""}, { USB_VID_CANON, USB_PID_CANON_ES3, P_ES3_30, ""}, { USB_VID_CANON, USB_PID_CANON_ES30, P_ES3_30, ""}, - { USB_VID_CANON, USB_PID_CANON_ES40, P_ES40_CP790, ""}, + { USB_VID_CANON, USB_PID_CANON_ES40, P_ES40, ""}, { 0, 0, 0, ""} } }; diff --git a/src/cups/shinko_s1245_print.c b/src/cups/shinko_s1245_print.c new file mode 100644 index 0000000..72b5ec9 --- /dev/null +++ b/src/cups/shinko_s1245_print.c @@ -0,0 +1,1621 @@ +/* + * Shinko/Sinfonia CHC-S1245 CUPS backend -- libusb-1.0 version + * + * (c) 2013-2015 Solomon Peachy <pizza@shaftnet.org> + * + * Low-level documentation was provided by Sinfonia, Inc. Thank you! + * + * The latest version of this program can be found at: + * + * http://git.shaftnet.org/cgit/selphy_print.git + * + * 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. + * + * [http://www.gnu.org/licenses/gpl-2.0.html] + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <signal.h> + +#define BACKEND shinkos1245_backend + +#include "backend_common.h" + +/* Structure of printjob header. All fields are LITTLE ENDIAN */ +struct s1245_printjob_hdr { + uint32_t len1; /* Fixed at 0x10 */ + uint32_t model; /* Equal to the printer model (eg '1245' or '2145' decimal) */ + uint32_t unk2; /* Null */ + uint32_t unk3; /* Fixed at 0x01 */ + + uint32_t len2; /* Fixed at 0x64 */ + uint32_t unk5; /* Null */ + uint32_t media; /* Fixed at 0x10 */ + uint32_t unk6; /* Null */ + + uint32_t method; /* Print Method */ + uint32_t mode; /* Print Mode */ + uint32_t unk7; /* Null */ + int32_t mattedepth; /* 0x7fffffff for glossy, 0x00 +- 25 for matte */ + + uint32_t dust; /* Dust control */ + uint32_t columns; + uint32_t rows; + uint32_t copies; + + uint32_t unk10; /* Null */ + uint32_t unk11; /* Null */ + uint32_t unk12; /* Null */ + uint32_t unk13; /* 0xceffffff */ + + uint32_t unk14; /* Null */ + uint32_t unk15; /* 0xceffffff */ + uint32_t dpi; /* Fixed at '300' (decimal) */ + uint32_t unk16; /* 0xceffffff */ + + uint32_t unk17; /* Null */ + uint32_t unk18; /* 0xceffffff */ + uint32_t unk19; /* Null */ + uint32_t unk20; /* Null */ + + uint32_t unk21; /* Null */ +} __attribute__((packed)); + +/* Printer data structures */ +struct shinkos1245_cmd_hdr { + uint8_t prefix; /* 0x03 */ + uint8_t hdr[4]; /* 0x1b 0x43 0x48 0x43 */ +} __attribute__((packed)); + +/* Get Printer ID */ +struct shinkos1245_cmd_getid { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0x12 */ + uint8_t pad[11]; +} __attribute__((packed)); + +struct shinkos1245_resp_getid { + uint8_t id; /* 0x00 */ + uint8_t data[23]; /* padded with 0x20 (space) */ + uint8_t reserved[8]; // XXX actual serial number? +} __attribute__((packed)); + +/* Set Printer ID -- Returns Status */ +struct shinkos1245_cmd_setid { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[2]; /* 0x0a 0x22 */ + uint8_t id; /* 0x00 */ + uint8_t data[23]; /* pad with 0x20 (space) */ +} __attribute__((packed)); + +/* Print -- Returns Status */ +struct shinkos1245_cmd_print { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[2]; /* 0x0a 0x00 */ + uint8_t id; /* 1-255 */ + uint16_t count; /* # Copies in BCD, 1-9999 */ + uint16_t columns; /* Fixed at 2446 */ + uint16_t rows; + uint8_t media; /* Fixed at 0x10 */ + uint8_t mode; /* dust removal and lamination mode */ + uint8_t combo; /* aka "print method" in the spool file */ +} __attribute__((packed)); + +/* Get Status */ +struct shinkos1245_cmd_getstatus { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0x03 */ + uint8_t pad[10]; +} __attribute__((packed)); + +struct shinkos1245_resp_status { + uint8_t code; + uint8_t print_status; + struct { + uint8_t status1; + uint32_t status2; /* BE */ + uint8_t error; + } state; + struct { + uint32_t lifetime; /* BE */ + uint32_t maint; /* BE */ + uint32_t media; /* BE */ + uint32_t cutter; /* BE */ + uint8_t reserved; + uint8_t ver_boot; + uint8_t ver_ctrl; + uint8_t control_flag; // 0x00 == epson, 0x01 == cypress + } counters; + struct { + uint16_t main_boot; + uint16_t main_control; + uint16_t dsp_boot; + uint16_t dsp_control; + } versions; + struct { + uint8_t bank1_id; + uint8_t bank2_id; + uint16_t bank1_remain; /* BE */ + uint16_t bank1_complete; /* BE */ + uint16_t bank1_spec; /* BE */ + uint16_t bank2_remain; /* BE */ + uint16_t bank2_complete; /* BE */ + uint16_t bank2_spec; /* BE */ + } counters2; + uint8_t curve_status; +} __attribute__((packed)); + +enum { + CMD_CODE_OK = 1, + CMD_CODE_BAD = 2, +}; + +enum { + STATUS_PRINTING = 1, + STATUS_IDLE = 2, +}; + +enum { + STATE_STATUS1_STANDBY = 1, + STATE_STATUS1_ERROR = 2, + STATE_STATUS1_WAIT = 3, +}; + +#define STATE_STANDBY_STATUS2 0x0 + +enum { + WAIT_STATUS2_INIT = 0, + WAIT_STATUS2_RIBBON = 1, + WAIT_STATUS2_THERMAL = 2, + WAIT_STATUS2_OPERATING = 3, + WAIT_STATUS2_BUSY = 4, +}; + +#define ERROR_STATUS2_CTRL_CIRCUIT (1<<31) +#define ERROR_STATUS2_MECHANISM_CTRL (1<<30) +#define ERROR_STATUS2_SENSOR (1<<13) +#define ERROR_STATUS2_COVER_OPEN (1<<12) +#define ERROR_STATUS2_TEMP_SENSOR (1<<9) +#define ERROR_STATUS2_PAPER_JAM (1<<8) +#define ERROR_STATUS2_PAPER_EMPTY (1<<6) +#define ERROR_STATUS2_RIBBON_ERR (1<<4) + +enum { + CTRL_CIR_ERROR_EEPROM1 = 0x01, + CTRL_CIR_ERROR_EEPROM2 = 0x02, + CTRL_CIR_ERROR_DSP = 0x04, + CTRL_CIR_ERROR_CRC_MAIN = 0x06, + CTRL_CIR_ERROR_DL_MAIN = 0x07, + CTRL_CIR_ERROR_CRC_DSP = 0x08, + CTRL_CIR_ERROR_DL_DSP = 0x09, + CTRL_CIR_ERROR_ASIC = 0x0a, + CTRL_CIR_ERROR_DRAM = 0x0b, + CTRL_CIR_ERROR_DSPCOMM = 0x29, +}; + +enum { + MECH_ERROR_HEAD_UP = 0x01, + MECH_ERROR_HEAD_DOWN = 0x02, + MECH_ERROR_MAIN_PINCH_UP = 0x03, + MECH_ERROR_MAIN_PINCH_DOWN = 0x04, + MECH_ERROR_SUB_PINCH_UP = 0x05, + MECH_ERROR_SUB_PINCH_DOWN = 0x06, + MECH_ERROR_FEEDIN_PINCH_UP = 0x07, + MECH_ERROR_FEEDIN_PINCH_DOWN = 0x08, + MECH_ERROR_FEEDOUT_PINCH_UP = 0x09, + MECH_ERROR_FEEDOUT_PINCH_DOWN = 0x0a, + MECH_ERROR_CUTTER_LR = 0x0b, + MECH_ERROR_CUTTER_RL = 0x0c, +}; + +enum { + SENSOR_ERROR_CUTTER = 0x05, + SENSOR_ERROR_HEAD_DOWN = 0x09, + SENSOR_ERROR_HEAD_UP = 0x0a, + SENSOR_ERROR_MAIN_PINCH_DOWN = 0x0b, + SENSOR_ERROR_MAIN_PINCH_UP = 0x0c, + SENSOR_ERROR_FEED_PINCH_DOWN = 0x0d, + SENSOR_ERROR_FEED_PINCH_UP = 0x0e, + SENSOR_ERROR_EXIT_PINCH_DOWN = 0x0f, + SENSOR_ERROR_EXIT_PINCH_UP = 0x10, + SENSOR_ERROR_LEFT_CUTTER = 0x11, + SENSOR_ERROR_RIGHT_CUTTER = 0x12, + SENSOR_ERROR_CENTER_CUTTER = 0x13, + SENSOR_ERROR_UPPER_CUTTER = 0x14, + SENSOR_ERROR_PAPER_FEED_COVER = 0x15, +}; + +enum { + TEMP_SENSOR_ERROR_HEAD_HIGH = 0x01, + TEMP_SENSOR_ERROR_HEAD_LOW = 0x02, + TEMP_SENSOR_ERROR_ENV_HIGH = 0x03, + TEMP_SENSOR_ERROR_ENV_LOW = 0x04, +}; + +enum { + COVER_OPEN_ERROR_UPPER = 0x01, + COVER_OPEN_ERROR_LOWER = 0x02, +}; + +enum { + PAPER_EMPTY_ERROR = 0x00, +}; + +enum { + RIBBON_ERROR = 0x00, +}; + +enum { + CURVE_TABLE_STATUS_INITIAL = 0x00, + CURVE_TABLE_STATUS_USERSET = 0x01, + CURVE_TABLE_STATUS_CURRENT = 0x02, +}; + +// XXX Paper jam has 0x01 -> 0xff as error codes + +/* Query media info */ +struct shinkos1245_cmd_getmedia { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0x1a/0x2a/0x3a for A/B/C */ + uint8_t pad[10]; +} __attribute__((packed)); + +struct shinkos1245_mediadesc { + uint8_t code; /* Fixed at 0x10 */ + uint16_t columns; /* BE */ + uint16_t rows; /* BE */ + uint8_t type; /* MEDIA_TYPE_* */ + uint8_t print_type; /* aka "print method" in the spool file */ + uint8_t reserved[3]; +} __attribute__((packed)); + +#define NUM_MEDIAS 5 /* Maximum per message */ + +struct shinkos1245_resp_media { + uint8_t code; + uint8_t reserved[5]; + uint8_t count; /* 1-5? */ + struct shinkos1245_mediadesc data[NUM_MEDIAS]; +} __attribute__((packed)); + +enum { + MEDIA_TYPE_UNKNOWN = 0x00, + MEDIA_TYPE_PAPER = 0x01, +}; + +enum { + PRINT_TYPE_STANDARD = 0x00, + PRINT_TYPE_8x5_2up = 0x01, + PRINT_TYPE_8x4_2up = 0x02, + PRINT_TYPE_8x6_8x4 = 0x03, + PRINT_TYPE_8x5 = 0x04, + PRINT_TYPE_8x4 = 0x05, + PRINT_TYPE_8x6 = 0x06, + PRINT_TYPE_8x6_2up = 0x07, + PRINT_TYPE_8x4_3up = 0x08, + PRINT_TYPE_8x8 = 0x09, +}; + +/* Cancel Job -- returns Status */ +struct shinkos1245_cmd_canceljob { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0x13 */ + uint8_t id; /* 1-255 */ + uint8_t pad[9]; +} __attribute__((packed)); + +/* Reset printer -- returns Status */ +struct shinkos1245_cmd_reset { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0xc0 */ + uint8_t pad[10]; +} __attribute__((packed)); + +/* Tone curve manipulation -- returns Status */ +struct shinkos1245_cmd_tone { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0xc0 */ + uint8_t tone[4]; /* 0x54 0x4f 0x4e 0x45 */ + uint8_t cmd2[1]; /* 0x72/0x77/0x65/0x20 for read/write/end/data */ + union { + struct { + uint8_t tone_table; + uint8_t param_table; + uint8_t pad[3]; + } read_write; + struct { + uint8_t pad[5]; + } end_data; + }; +} __attribute__((packed)); + +enum { + TONE_TABLE_STANDARD = 0, + TONE_TABLE_USER = 1, + TONE_TABLE_CURRENT = 2, +}; +enum { + PARAM_TABLE_STANDARD = 1, + PARAM_TABLE_FINE = 2, +}; + +#define TONE_CURVE_SIZE 1536 +#define TONE_CURVE_DATA_BLOCK_SIZE 64 + +/* Query Model information */ +struct shinkos1245_cmd_getmodel { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0x02 */ + uint8_t pad[10]; +} __attribute__((packed)); + +struct shinkos1245_resp_getmodel { + uint8_t vendor_id[4]; + uint8_t product_id[4]; + uint8_t strings[40]; +} __attribute__((packed)); + + +/* Query and Set Matte info, returns a Matte response */ +struct shinkos1245_cmd_getmatte { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0x20 */ + uint8_t mode; /* Fixed at 0x00 */ + uint8_t pad[9]; +} __attribute__((packed)); + +struct shinkos1245_cmd_setmatte { + struct shinkos1245_cmd_hdr hdr; + uint8_t cmd[1]; /* 0x21 */ + uint8_t mode; /* Fixed at 0x00 */ + int8_t level; /* -25->+25 */ + uint8_t pad[8]; +} __attribute__((packed)); + +struct shinkos1245_resp_matte { + uint8_t code; + uint8_t mode; + int8_t level; + uint8_t reserved[3]; +} __attribute__((packed)); + +#define MATTE_MODE_MATTE 0x00 + +/* Private data stucture */ +struct shinkos1245_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + + uint8_t jobid; + + struct s1245_printjob_hdr hdr; + + struct shinkos1245_mediadesc medias[15]; + int num_medias; + + uint8_t *databuf; + int datalen; + int tonecurve; +}; + +enum { + S_IDLE = 0, + S_PRINTER_READY_CMD, + S_PRINTER_SENT_DATA, + S_FINISHED, +}; + + +/* Basic printer I/O stuffs */ +static void shinkos1245_fill_hdr(struct shinkos1245_cmd_hdr *hdr) +{ + hdr->prefix = 0x03; + hdr->hdr[0] = 0x1b; + hdr->hdr[1] = 0x43; + hdr->hdr[2] = 0x48; + hdr->hdr[3] = 0x43; +} + +static int shinkos1245_do_cmd(struct shinkos1245_ctx *ctx, + void *cmd, int cmd_len, + void *resp, int resp_len, + int *actual_len) +{ + int ret; + + /* Write command */ + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmd, cmd_len))) + return (ret < 0) ? ret : -99; + + /* Read response */ + ret = read_data(ctx->dev, ctx->endp_up, + resp, resp_len, actual_len); + if (ret < 0) + return ret; + if (*actual_len < resp_len) { + ERROR("Short read! (%d/%d))\n", *actual_len, resp_len); + return -99; + } + + return ret; +} + +static int shinkos1245_get_status(struct shinkos1245_ctx *ctx, + struct shinkos1245_resp_status *resp) +{ + struct shinkos1245_cmd_getstatus cmd; + int ret, num; + + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x03; + memset(cmd.pad, 0, sizeof(cmd.pad)); + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + resp, sizeof(*resp), &num); + if (ret < 0) { + ERROR("Failed to execute GET_STATUS command\n"); + return ret; + } + if (resp->code != CMD_CODE_OK) { + ERROR("Bad return code on GET_STATUS (%02x)\n", + resp->code); + return -99; + } + + return 0; +} + +static int shinkos1245_get_media(struct shinkos1245_ctx *ctx) +{ + struct shinkos1245_cmd_getmedia cmd; + struct shinkos1245_resp_media resp; + int i, j; + int ret = 0, num; + + shinkos1245_fill_hdr(&cmd.hdr); + memset(cmd.pad, 0, sizeof(cmd.pad)); + for (i = 1 ; i <= 3 ; i++) { + cmd.cmd[0] = 0x0a || (i << 4); + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &resp, sizeof(resp), &num); + if (ret < 0) { + ERROR("Failed to execute GET_MEDIA command\n"); + return ret; + } + if (resp.code != CMD_CODE_OK) { + ERROR("Bad return code on GET_MEDIA (%02x)\n", + resp.code); + return -99; + } + + if (resp.count > NUM_MEDIAS) + resp.count = NUM_MEDIAS; + + /* Store media info */ + for (j = 0; j < resp.count ; j++) { + ctx->medias[ctx->num_medias].code = resp.data[j].code; + ctx->medias[ctx->num_medias].columns = be16_to_cpu(resp.data[j].columns); + ctx->medias[ctx->num_medias].rows = be16_to_cpu(resp.data[j].rows); + ctx->medias[ctx->num_medias].type = resp.data[j].type; + ctx->medias[ctx->num_medias].print_type = resp.data[j].print_type; + ctx->num_medias++; + } + + if (resp.count < 5) + break; + } + return ret; +} + +static int shinkos1245_get_printerid(struct shinkos1245_ctx *ctx, + struct shinkos1245_resp_getid *resp) +{ + struct shinkos1245_cmd_getid cmd; + int ret, num; + + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x12; + memset(cmd.pad, 0, sizeof(cmd.pad)); + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + resp, sizeof(*resp), &num); + if (ret < 0) { + ERROR("Failed to execute GET_PRINTERID command\n"); + return ret; + } + + return 0; +} + +static int shinkos1245_set_printerid(struct shinkos1245_ctx *ctx, + char *id) +{ + struct shinkos1245_cmd_setid cmd; + struct shinkos1245_resp_status sts; + + int ret, num; + int i; + + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x0a; + cmd.cmd[1] = 0x22; + + for (i = 0 ; i < (int)sizeof(cmd.data) ; i++) { + if (*id) + cmd.data[i] = (uint8_t) *id; + else + cmd.data[i] = ' '; + } + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &sts, sizeof(sts), &num); + if (ret < 0) { + ERROR("Failed to execute SET_PRINTERID command\n"); + return ret; + } + if (sts.code != CMD_CODE_OK) { + ERROR("Bad return code on SET_PRINTERID command\n"); + return -99; + } + return 0; +} + +static int shinkos1245_canceljob(struct shinkos1245_ctx *ctx, + int id) +{ + struct shinkos1245_cmd_canceljob cmd; + struct shinkos1245_resp_status sts; + + int ret, num; + + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x13; + cmd.id = id; + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &sts, sizeof(sts), &num); + if (ret < 0) { + ERROR("Failed to execute CANCELJOB command\n"); + return ret; + } + if (sts.code != CMD_CODE_OK) { + ERROR("Bad return code on CANCELJOB command\n"); + return -99; + } + return 0; +} + +static int shinkos1245_set_matte(struct shinkos1245_ctx *ctx, + int intensity) +{ + struct shinkos1245_cmd_setmatte cmd; + struct shinkos1245_resp_matte sts; + + int ret, num; + + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x21; + cmd.mode = MATTE_MODE_MATTE; + cmd.level = intensity; + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &sts, sizeof(sts), &num); + if (ret < 0) { + ERROR("Failed to execute SET_MATTE command\n"); + return ret; + } + if (sts.code == CMD_CODE_OK) + return 0; + if (sts.code == CMD_CODE_BAD) + return 1; + + ERROR("Bad return code (%02x) on SET_MATTE command\n", sts.code); + return -99; +} + +static int shinkos1245_get_matte(struct shinkos1245_ctx *ctx, + int *intensity) +{ + struct shinkos1245_cmd_getmatte cmd; + struct shinkos1245_resp_matte sts; + + int ret, num; + + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x20; + cmd.mode = MATTE_MODE_MATTE; + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &sts, sizeof(sts), &num); + if (ret < 0) { + ERROR("Failed to execute GET_MATTE command\n"); + return ret; + } + if (sts.code != CMD_CODE_OK) { + ERROR("Bad return code (%02x) on GET_MATTE command\n", sts.code); + return -99; + } + *intensity = sts.level; + + return 0; +} + + +/* Structure dumps */ +static char *shinkos1245_status_str(struct shinkos1245_resp_status *resp) +{ + switch(resp->state.status1) { + case STATE_STATUS1_STANDBY: + return "Standby (Ready)"; + case STATE_STATUS1_WAIT: + switch (resp->state.status2) { + case WAIT_STATUS2_INIT: + return "Wait (Initializing)"; + case WAIT_STATUS2_RIBBON: + return "Wait (Ribbon Winding)"; + case WAIT_STATUS2_THERMAL: + return "Wait (Thermal Protection)"; + case WAIT_STATUS2_OPERATING: + return "Wait (Operating)"; + case WAIT_STATUS2_BUSY: + return "Wait (Busy)"; + default: + return "Wait (Unknown)"; + } + case STATE_STATUS1_ERROR: + switch (resp->state.status2) { + case ERROR_STATUS2_CTRL_CIRCUIT: + switch (resp->state.error) { + case CTRL_CIR_ERROR_EEPROM1: + return "Error (EEPROM1)"; + case CTRL_CIR_ERROR_EEPROM2: + return "Error (EEPROM2)"; + case CTRL_CIR_ERROR_DSP: + return "Error (DSP)"; + case CTRL_CIR_ERROR_CRC_MAIN: + return "Error (Main CRC)"; + case CTRL_CIR_ERROR_DL_MAIN: + return "Error (Main Download)"; + case CTRL_CIR_ERROR_CRC_DSP: + return "Error (DSP CRC)"; + case CTRL_CIR_ERROR_DL_DSP: + return "Error (DSP Download)"; + case CTRL_CIR_ERROR_ASIC: + return "Error (ASIC)"; + case CTRL_CIR_ERROR_DRAM: + return "Error (DRAM)"; + case CTRL_CIR_ERROR_DSPCOMM: + return "Error (DSP Communincation)"; + default: + return "Error (Unknown Circuit)"; + } + case ERROR_STATUS2_MECHANISM_CTRL: + switch (resp->state.error) { + case MECH_ERROR_HEAD_UP: + return "Error (Head Up Mechanism)"; + case MECH_ERROR_HEAD_DOWN: + return "Error (Head Down Mechanism)"; + case MECH_ERROR_MAIN_PINCH_UP: + return "Error (Main Pinch Up Mechanism)"; + case MECH_ERROR_MAIN_PINCH_DOWN: + return "Error (Main Pinch Down Mechanism)"; + case MECH_ERROR_SUB_PINCH_UP: + return "Error (Sub Pinch Up Mechanism)"; + case MECH_ERROR_SUB_PINCH_DOWN: + return "Error (Sub Pinch Down Mechanism)"; + case MECH_ERROR_FEEDIN_PINCH_UP: + return "Error (Feed-in Pinch Up Mechanism)"; + case MECH_ERROR_FEEDIN_PINCH_DOWN: + return "Error (Feed-in Pinch Down Mechanism)"; + case MECH_ERROR_FEEDOUT_PINCH_UP: + return "Error (Feed-out Pinch Up Mechanism)"; + case MECH_ERROR_FEEDOUT_PINCH_DOWN: + return "Error (Feed-out Pinch Down Mechanism)"; + case MECH_ERROR_CUTTER_LR: + return "Error (Left->Right Cutter)"; + case MECH_ERROR_CUTTER_RL: + return "Error (Right->Left Cutter)"; + default: + return "Error (Unknown Mechanism)"; + } + case ERROR_STATUS2_SENSOR: + switch (resp->state.error) { + case SENSOR_ERROR_CUTTER: + return "Error (Cutter Sensor)"; + case SENSOR_ERROR_HEAD_DOWN: + return "Error (Head Down Sensor)"; + case SENSOR_ERROR_HEAD_UP: + return "Error (Head Up Sensor)"; + case SENSOR_ERROR_MAIN_PINCH_DOWN: + return "Error (Main Pinch Down Sensor)"; + case SENSOR_ERROR_MAIN_PINCH_UP: + return "Error (Main Pinch Up Sensor)"; + case SENSOR_ERROR_FEED_PINCH_DOWN: + return "Error (Feed Pinch Down Sensor)"; + case SENSOR_ERROR_FEED_PINCH_UP: + return "Error (Feed Pinch Up Sensor)"; + case SENSOR_ERROR_EXIT_PINCH_DOWN: + return "Error (Exit Pinch Up Sensor)"; + case SENSOR_ERROR_EXIT_PINCH_UP: + return "Error (Exit Pinch Up Sensor)"; + case SENSOR_ERROR_LEFT_CUTTER: + return "Error (Left Cutter Sensor)"; + case SENSOR_ERROR_RIGHT_CUTTER: + return "Error (Right Cutter Sensor)"; + case SENSOR_ERROR_CENTER_CUTTER: + return "Error (Center Cutter Sensor)"; + case SENSOR_ERROR_UPPER_CUTTER: + return "Error (Upper Cutter Sensor)"; + case SENSOR_ERROR_PAPER_FEED_COVER: + return "Error (Paper Feed Cover)"; + default: + return "Error (Unknown Sensor)"; + } + case ERROR_STATUS2_COVER_OPEN: + switch (resp->state.error) { + case COVER_OPEN_ERROR_UPPER: + return "Error (Upper Cover Open)"; + case COVER_OPEN_ERROR_LOWER: + return "Error (Lower Cover Open)"; + default: + return "Error (Unknown Cover Open)"; + } + case ERROR_STATUS2_TEMP_SENSOR: + switch (resp->state.error) { + case TEMP_SENSOR_ERROR_HEAD_HIGH: + return "Error (Head Temperature High)"; + case TEMP_SENSOR_ERROR_HEAD_LOW: + return "Error (Head Temperature Low)"; + case TEMP_SENSOR_ERROR_ENV_HIGH: + return "Error (Environmental Temperature High)"; + case TEMP_SENSOR_ERROR_ENV_LOW: + return "Error (Environmental Temperature Low)"; + default: + return "Error (Unknown Temperature)"; + } + case ERROR_STATUS2_PAPER_JAM: + return "Error (Paper Jam)"; + case ERROR_STATUS2_PAPER_EMPTY: + return "Error (Paper Empty)"; + case ERROR_STATUS2_RIBBON_ERR: + return "Error (Ribbon)"; + default: + return "Error (Unknown)"; + } + default: + return "Unknown!"; + } +} + +static char* shinkos1245_tonecurves(int type, int table) +{ + switch (type) { + case TONE_TABLE_STANDARD: + switch (table) { + case PARAM_TABLE_STANDARD: + return "Standard/Standard"; + case PARAM_TABLE_FINE: + return "Standard/Fine"; + default: + return "Standard/Unknown"; + } + case TONE_TABLE_USER: + switch (table) { + case PARAM_TABLE_STANDARD: + return "User/Standard"; + case PARAM_TABLE_FINE: + return "User/Fine"; + default: + return "User/Unknown"; + } + case TONE_TABLE_CURRENT: + switch (table) { + case PARAM_TABLE_STANDARD: + return "Current/Standard"; + case PARAM_TABLE_FINE: + return "Current/Fine"; + default: + return "Current/Unknown"; + } + default: + return "Unknown"; + } +} + +static void shinkos1245_dump_status(struct shinkos1245_resp_status *sts) +{ + char *detail; + switch (sts->print_status) { + case STATUS_PRINTING: + detail = "Printing"; + break; + case STATUS_IDLE: + detail = "Idle"; + break; + default: + detail = "Unknown"; + break; + } + INFO("Printer Status: %s\n", detail); + + /* Byteswap */ + sts->state.status2 = be32_to_cpu(sts->state.status2); + + INFO("Printer State: %s # %02x %08x %02x\n", + shinkos1245_status_str(sts), + sts->state.status1, sts->state.status2, sts->state.error); + INFO("Counters:\n"); + INFO("\tLifetime : %d\n", be32_to_cpu(sts->counters.lifetime)); + INFO("\tThermal Head : %d\n", be32_to_cpu(sts->counters.maint)); + INFO("\tMedia : %d\n", be32_to_cpu(sts->counters.media)); + INFO("\tCutter : %d\n", be32_to_cpu(sts->counters.cutter)); + + INFO("Versions:\n"); + INFO("\tUSB Boot : %d\n", sts->counters.ver_boot); + INFO("\tUSB Control : %d\n", sts->counters.ver_ctrl); + INFO("\tMain Boot : %d\n", be16_to_cpu(sts->versions.main_boot)); + INFO("\tMain Control: %d\n", be16_to_cpu(sts->versions.main_control)); + INFO("\tDSP Boot : %d\n", be16_to_cpu(sts->versions.dsp_boot)); + INFO("\tDSP Control : %d\n", be16_to_cpu(sts->versions.dsp_control)); + +// INFO("USB TypeFlag: %02x\n", sts->counters.control_flag); + + INFO("Bank 1 ID: %d\n", sts->counters2.bank1_id); + INFO("\tPrints: %d/%d complete\n", + be16_to_cpu(sts->counters2.bank1_complete), + be16_to_cpu(sts->counters2.bank1_spec)); + INFO("Bank 2 ID: %d\n", sts->counters2.bank2_id); + INFO("\tPrints: %d/%d complete\n", + be16_to_cpu(sts->counters2.bank2_complete), + be16_to_cpu(sts->counters2.bank2_spec)); + + switch (sts->curve_status) { + case CURVE_TABLE_STATUS_INITIAL: + detail = "Initial/Default"; + break; + case CURVE_TABLE_STATUS_USERSET: + detail = "User Stored"; + break; + case CURVE_TABLE_STATUS_CURRENT: + detail = "Current"; + break; + default: + detail = "Unknown"; + break; + } + INFO("Tone Curve Status: %s\n", detail); +} + +static void shinkos1245_dump_media(struct shinkos1245_mediadesc *medias, + int count) +{ + int i; + + INFO("Supported print sizes: %d\n", count); + + for (i = 0 ; i < count ; i++) { + INFO("\t %02x: %04d*%04d (%02x/%02d)\n", + medias[i].print_type, + medias[i].columns, + medias[i].rows, + medias[i].code, medias[i].type); + } +} + +static int get_tonecurve(struct shinkos1245_ctx *ctx, int type, int table, char *fname) +{ + int ret = 0, num, remaining; + uint8_t *data, *ptr; + + struct shinkos1245_cmd_tone cmd; + struct shinkos1245_resp_status resp; + + INFO("Dump %s Tone Curve to '%s'\n", shinkos1245_tonecurves(type, table), fname); + + /* Issue a tone_read_start */ + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x0c; + cmd.tone[0] = 0x54; + cmd.tone[1] = 0x4f; + cmd.tone[2] = 0x4e; + cmd.tone[3] = 0x45; + cmd.cmd2[0] = 0x72; + cmd.read_write.tone_table = type; + cmd.read_write.param_table = table; + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &resp, sizeof(resp), &num); + + if (ret < 0) { + ERROR("Failed to execute TONE_READ command\n"); + return ret; + } + if (resp.code != CMD_CODE_OK) { + ERROR("Bad return code on TONE_READ (%02x)\n", + resp.code); + return -99; + } + + /* Get the data out */ + remaining = TONE_CURVE_SIZE; + data = malloc(remaining); + if (!data) { + ERROR("Memory Allocation Failure!\n"); + return -11; + } + ptr = data; + + while(remaining) { + /* Issue a tone_data message */ + cmd.cmd2[0] = 0x20; + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &resp, sizeof(resp), &num); + + if (ret < 0) { + ERROR("Failed to execute TONE_DATA command\n"); + goto done; + } + if (resp.code != CMD_CODE_OK) { + ERROR("Bad return code on TONE_DATA (%02x)\n", + resp.code); + ret = -99; + goto done; + } + + /* And read back 64-bytes of data */ + ret = read_data(ctx->dev, ctx->endp_up, + ptr, TONE_CURVE_DATA_BLOCK_SIZE, &num); + if (num != TONE_CURVE_DATA_BLOCK_SIZE) { + ret = -99; + goto done; + } + if (ret < 0) + goto done; + ptr += num; + } + + /* Issue a tone_end */ + cmd.cmd2[0] = 0x65; + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &resp, sizeof(resp), &num); + + if (ret < 0) { + ERROR("Failed to execute TONE_END command\n"); + goto done; + } + if (resp.code != CMD_CODE_OK) { + ERROR("Bad return code on TONE_END (%02x)\n", + resp.code); + ret = -99; + goto done; + } + + /* Open file and write it out */ + { + int tc_fd = open(fname, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); + if (tc_fd < 0) { + ret = tc_fd; + goto done; + } + + ret = write(tc_fd, data, TONE_CURVE_SIZE); + if (ret < 0) + goto done; + close(tc_fd); + } + +done: + free(data); + + return ret; +} + +static int set_tonecurve(struct shinkos1245_ctx *ctx, int type, int table, char *fname) +{ + int ret = 0, num, remaining; + uint8_t *data, *ptr; + + struct shinkos1245_cmd_tone cmd; + struct shinkos1245_resp_status resp; + + INFO("Read %d/%d Tone Curve from '%s'\n", type, table, fname); // XXX + + /* Allocate space */ + remaining = TONE_CURVE_SIZE; + data = malloc(remaining); + if (!data) { + ERROR("Memory Allocation Failure!\n"); + return -11; + } + ptr = data; + + /* Open file and read it in */ + { + int tc_fd = open(fname, O_RDONLY); + if (tc_fd < 0) { + ret = tc_fd; + goto done; + } + + ret = read(tc_fd, data, TONE_CURVE_SIZE); + if (ret < 0) { + close(tc_fd); + goto done; + } + + close(tc_fd); + } + + /* Issue a tone_write_start */ + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x0c; + cmd.tone[0] = 0x54; + cmd.tone[1] = 0x4f; + cmd.tone[2] = 0x4e; + cmd.tone[3] = 0x45; + cmd.cmd2[0] = 0x77; + cmd.read_write.tone_table = type; + cmd.read_write.param_table = table; + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &resp, sizeof(resp), &num); + + if (ret < 0) { + ERROR("Failed to execute TONE_WRITE command\n"); + goto done; + } + if (resp.code != CMD_CODE_OK) { + ERROR("Bad return code on TONE_WRITE (%02x)\n", + resp.code); + ret = -99; + goto done; + } + + while(remaining) { + /* Issue a tone_data message */ + cmd.cmd2[0] = 0x20; + + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &resp, sizeof(resp), &num); + + if (ret < 0) { + ERROR("Failed to execute TONE_DATA command\n"); + goto done; + } + if (resp.code != CMD_CODE_OK) { + ERROR("Bad return code on TONE_DATA (%02x)\n", + resp.code); + ret = -99; + goto done; + } + + /* Write 64-bytes of data */ + ret = send_data(ctx->dev, ctx->endp_up, + ptr, TONE_CURVE_DATA_BLOCK_SIZE); + if (ret < 0) + goto done; + ptr += num; + } + + /* Issue a tone_end */ + cmd.cmd2[0] = 0x65; + ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &resp, sizeof(resp), &num); + + if (ret < 0) { + ERROR("Failed to execute TONE_END command\n"); + goto done; + } + if (resp.code != CMD_CODE_OK) { + ERROR("Bad return code on TONE_END (%02x)\n", + resp.code); + ret = -99; + goto done; + } + +done: + free(data); + + return ret; +} + + +/* Driver API */ + +static void shinkos1245_cmdline(void) +{ + DEBUG("\t\t[ -m ] # Query media\n"); + DEBUG("\t\t[ -s ] # Query status\n"); + DEBUG("\t\t[ -u ] # Query user string\n"); + DEBUG("\t\t[ -U sometext ] # Set user string\n"); + DEBUG("\t\t[ -X jobid ] # Abort a printjob\n"); + DEBUG("\t\t[ -F ] # Tone curve refers to FINE mode\n"); + DEBUG("\t\t[ -c filename ] # Get user/NV tone curve\n"); + DEBUG("\t\t[ -C filename ] # Set user/NV tone curve\n"); + DEBUG("\t\t[ -l filename ] # Get current tone curve\n"); + DEBUG("\t\t[ -L filename ] # Set current tone curve\n"); +} + +int shinkos1245_cmdline_arg(void *vctx, int argc, char **argv) +{ + struct shinkos1245_ctx *ctx = vctx; + int i, j = 0; + + if (!ctx) + return -1; + + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "c:C:l:L:FmsuU:X:")) >= 0) { + switch(i) { + GETOPT_PROCESS_GLOBAL + case 'F': + ctx->tonecurve = PARAM_TABLE_FINE; + break; + case 'c': + j = get_tonecurve(ctx, TONE_TABLE_USER, ctx->tonecurve, optarg); + break; + case 'C': + j = set_tonecurve(ctx, TONE_TABLE_USER, ctx->tonecurve, optarg); + break; + case 'l': + j = get_tonecurve(ctx, TONE_TABLE_CURRENT, ctx->tonecurve, optarg); + break; + case 'L': + j = set_tonecurve(ctx, TONE_TABLE_CURRENT, ctx->tonecurve, optarg); + break; + case 'm': + j = shinkos1245_get_media(ctx); + if (!j) + shinkos1245_dump_media(ctx->medias, ctx->num_medias); + break; + case 's': { + struct shinkos1245_resp_status sts; + j = shinkos1245_get_status(ctx, &sts); + if (!j) + shinkos1245_dump_status(&sts); + break; + } + case 'u': { + struct shinkos1245_resp_getid resp; + j = shinkos1245_get_printerid(ctx, &resp); + if (!j) { + char buffer[sizeof(resp.data)+1]; + memcpy(buffer, resp.data, sizeof(resp.data)); + buffer[sizeof(resp.data)] = 0; + INFO("Printer ID: %02x '%s'\n", resp.id, buffer); + } + break; + } + case 'U': + j = shinkos1245_set_printerid(ctx, optarg); + break; + case 'X': + j = shinkos1245_canceljob(ctx, atoi(optarg)); + break; + default: + break; /* Ignore completely */ + } + + if (j) return j; + } + + return 0; +} + +static void *shinkos1245_init(void) +{ + struct shinkos1245_ctx *ctx = malloc(sizeof(struct shinkos1245_ctx)); + if (!ctx) { + ERROR("Memory Allocation Failure!\n"); + return NULL; + } + memset(ctx, 0, sizeof(struct shinkos1245_ctx)); + + ctx->tonecurve = PARAM_TABLE_STANDARD; + + return ctx; +} + +static void shinkos1245_attach(void *vctx, struct libusb_device_handle *dev, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +{ + struct shinkos1245_ctx *ctx = vctx; + struct libusb_device *device; + struct libusb_device_descriptor desc; + + ctx->dev = dev; + ctx->endp_up = endp_up; + ctx->endp_down = endp_down; + + device = libusb_get_device(dev); + libusb_get_device_descriptor(device, &desc); + + ctx->type = lookup_printer_type(&shinkos1245_backend, + desc.idVendor, desc.idProduct); + + /* Ensure jobid is sane */ + ctx->jobid = (jobid & 0x7f) + 1; +} + + +static void shinkos1245_teardown(void *vctx) { + struct shinkos1245_ctx *ctx = vctx; + + if (!ctx) + return; + + if (ctx->databuf) + free(ctx->databuf); + + free(ctx); +} + +static int shinkos1245_read_parse(void *vctx, int data_fd) { + struct shinkos1245_ctx *ctx = vctx; + int ret; + uint8_t tmpbuf[4]; + + if (!ctx) + return CUPS_BACKEND_FAILED; + + /* Read in then validate header */ + ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); + if (ret < 0) + return ret; + if (ret < 0 || ret != sizeof(ctx->hdr)) + return CUPS_BACKEND_CANCEL; + + if (le32_to_cpu(ctx->hdr.len1) != 0x10 || + le32_to_cpu(ctx->hdr.len2) != 0x64 || + le32_to_cpu(ctx->hdr.dpi) != 300) { + ERROR("Unrecognized header data format!\n"); + return CUPS_BACKEND_CANCEL; + } + + ctx->hdr.model = le32_to_cpu(ctx->hdr.model); + + if(ctx->hdr.model != 1245) { + ERROR("Unrecognized printer (%d)!\n", ctx->hdr.model); + return CUPS_BACKEND_CANCEL; + } + + /* Finish byteswapping */ + ctx->hdr.media = le32_to_cpu(ctx->hdr.media); + ctx->hdr.method = le32_to_cpu(ctx->hdr.method); + ctx->hdr.mode = le32_to_cpu(ctx->hdr.mode); + ctx->hdr.mattedepth = le32_to_cpu(ctx->hdr.mattedepth); + ctx->hdr.dust = le32_to_cpu(ctx->hdr.dust); + ctx->hdr.columns = le32_to_cpu(ctx->hdr.columns); + ctx->hdr.rows = le32_to_cpu(ctx->hdr.rows); + ctx->hdr.copies = le32_to_cpu(ctx->hdr.copies); + + /* Allocate space */ + if (ctx->databuf) { + free(ctx->databuf); + ctx->databuf = NULL; + } + + ctx->datalen = ctx->hdr.rows * ctx->hdr.columns * 3; + ctx->databuf = malloc(ctx->datalen); + if (!ctx->databuf) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_FAILED; + } + + { + int remain = ctx->datalen; + uint8_t *ptr = ctx->databuf; + do { + ret = read(data_fd, ptr, remain); + if (ret < 0) { + ERROR("Read failed (%d/%d/%d)\n", + ret, remain, ctx->datalen); + perror("ERROR: Read failed"); + return ret; + } + ptr += ret; + remain -= ret; + } while (remain); + } + + /* Make sure footer is sane too */ + ret = read(data_fd, tmpbuf, 4); + if (ret != 4) { + ERROR("Read failed (%d/%d/%d)\n", + ret, 4, 4); + perror("ERROR: Read failed"); + return ret; + } + if (tmpbuf[0] != 0x04 || + tmpbuf[1] != 0x03 || + tmpbuf[2] != 0x02 || + tmpbuf[3] != 0x01) { + ERROR("Unrecognized footer data format!\n"); + return CUPS_BACKEND_FAILED; + } + + return CUPS_BACKEND_OK; +} + +static int shinkos1245_main_loop(void *vctx, int copies) { + struct shinkos1245_ctx *ctx = vctx; + int i, num, last_state = -1, state = S_IDLE; + struct shinkos1245_resp_status status1, status2; + + // XXX query printer info + + /* Query Media information if necessary */ + if (!ctx->num_medias) + shinkos1245_get_media(ctx); + if (!ctx->num_medias) { + ERROR("Media Query Error\n"); + return CUPS_BACKEND_FAILED; + } + /* Make sure print size is supported */ + for (i = 0 ; i < ctx->num_medias ; i++) { + if (ctx->hdr.media == ctx->medias[i].code && + ctx->hdr.method == ctx->medias[i].print_type && + ctx->hdr.rows == ctx->medias[i].rows && + ctx->hdr.columns == ctx->medias[i].columns) + break; + } + if (i == ctx->num_medias) { + ERROR("Unsupported print type\n"); + return CUPS_BACKEND_HOLD; + } + + /* Fix max print count. */ + if (copies > 9999) // XXX test against remaining media + copies = 9999; + +top: + if (state != last_state) { + if (dyesub_debug) + DEBUG("last_state %d new %d\n", last_state, state); + } + + /* Send status query */ + i = shinkos1245_get_status(ctx, &status1); + if (i < 0) + return CUPS_BACKEND_FAILED; + + if (memcmp(&status1, &status2, sizeof(status1))) { + memcpy(&status2, &status1, sizeof(status1)); + // status changed, check for errors and whatnot + } else if (state == last_state) { + sleep(1); + goto top; + } + + /* Make sure we're not in an error state */ + if (status1.state.status1 == STATE_STATUS1_ERROR) + goto printer_error; + + last_state = state; + + fflush(stderr); + + switch (state) { + case S_IDLE: + INFO("Waiting for printer idle\n"); + + if (status1.state.status1 == STATE_STATUS1_STANDBY) { + state = S_PRINTER_READY_CMD; + break; + } + + if (status1.print_status == STATUS_IDLE) { + state = S_PRINTER_READY_CMD; + break; + } + + // XXX what about STATUS_WAIT ? + // XXX see if printer has an empty bank? + + /* If the printer is "busy" check to see if there's any + open memory banks so we can queue the next print */ + if (!status1.counters2.bank1_remain || + !status1.counters2.bank2_remain) { + state = S_PRINTER_READY_CMD; + break; + } + break; + case S_PRINTER_READY_CMD: { + struct shinkos1245_cmd_print cmd; + + /* Set matte intensity */ + if (ctx->hdr.mattedepth != 0x7fffffff) { + int current = -1; + i = shinkos1245_get_matte(ctx, ¤t); + if (i < 0) + goto printer_error; + if (current != ctx->hdr.mattedepth) { + i = shinkos1245_set_matte(ctx, ctx->hdr.mattedepth); + if (i < 0) + goto printer_error; + if (i > 0) { + INFO("Can't set matte intensity when printing in progres...\n"); + state = S_IDLE; + sleep(1); + break; + } + } + } + + INFO("Initiating print job (internal id %d)\n", ctx->jobid); + + shinkos1245_fill_hdr(&cmd.hdr); + cmd.cmd[0] = 0x0a; + cmd.cmd[1] = 0x00; + + cmd.id = ctx->jobid; + cmd.count = cpu_to_be16(uint16_to_packed_bcd(copies)); + cmd.columns = cpu_to_be16(ctx->hdr.columns); + cmd.rows = cpu_to_be16(ctx->hdr.rows); + cmd.media = ctx->hdr.media; + cmd.mode = (ctx->hdr.mode & 0x3f) || ((ctx->hdr.dust & 0x3) << 6); + cmd.combo = ctx->hdr.method; + + /* Issue print commmand */ + i = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd), + &status1, sizeof(status1), + &num); + if (i < 0) + goto printer_error; + + /* Check for buffer full state, and wait if we're full */ + if (status1.code != CMD_CODE_OK) { + if (status1.print_status == STATUS_PRINTING) { + sleep(1); + break; + } else { + goto printer_error; + } + } + + /* Check for error states */ + if (status1.state.status1 == STATE_STATUS1_ERROR) + goto printer_error; + + /* Send over data */ + INFO("Sending image data to printer\n"); + if ((i = send_data(ctx->dev, ctx->endp_down, + ctx->databuf, ctx->datalen))) + return CUPS_BACKEND_FAILED; + + INFO("Waiting for printer to acknowledge completion\n"); + sleep(1); + state = S_PRINTER_SENT_DATA; + break; + } + case S_PRINTER_SENT_DATA: + if (fast_return) { + INFO("Fast return mode enabled.\n"); + state = S_FINISHED; + } + /* Check for completion */ + if (status1.print_status == STATUS_IDLE) + state = S_FINISHED; + + break; + default: + break; + } + + if (state != S_FINISHED) + goto top; + + INFO("Print complete\n"); + + if (copies && --copies) { + state = S_IDLE; + goto top; + } + + return CUPS_BACKEND_OK; + +printer_error: + /* Byteswap */ + status1.state.status2 = be32_to_cpu(status1.state.status2); + + ERROR("Printer Error: %s # %02x %08x %02x\n", + shinkos1245_status_str(&status1), + status1.state.status1, status1.state.status2, status1.state.error); + + return CUPS_BACKEND_FAILED; +} + +static int shinkos1245_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len) +{ + struct shinkos1245_resp_getid resp; + int i; + + struct shinkos1245_ctx ctx = { + .dev = dev, + .endp_up = endp_up, + .endp_down = endp_down, + }; + + i = shinkos1245_get_printerid(&ctx, &resp); + if (i < 0) + return CUPS_BACKEND_FAILED; + + for (i = 0 ; i < (int) sizeof(resp.data) && i < buf_len; i++) { + buf[i] = resp.data[i]; + } + + /* Ensure null-termination */ + if (i < buf_len) + buf[i] = 0; + else + buf[buf_len-1] = 0; + + return CUPS_BACKEND_OK; +} + +/* Exported */ +#define USB_VID_SHINKO 0x10CE +#define USB_PID_SHINKO_S1245 0x0007 + +struct dyesub_backend shinkos1245_backend = { + .name = "Shinko/Sinfonia CHC-S1245", + .version = "0.07WIP", + .uri_prefix = "shinkos1245", + .cmdline_usage = shinkos1245_cmdline, + .cmdline_arg = shinkos1245_cmdline_arg, + .init = shinkos1245_init, + .attach = shinkos1245_attach, + .teardown = shinkos1245_teardown, + .read_parse = shinkos1245_read_parse, + .main_loop = shinkos1245_main_loop, + .query_serno = shinkos1245_query_serno, + .devices = { + { USB_VID_SHINKO, USB_PID_SHINKO_S1245, P_SHINKO_S1245, ""}, + { 0, 0, 0, ""} + } +}; + +/* CHC-S1245 data format + + Spool file consists of an 116-byte header, followed by RGB-packed data, + followed by a 4-byte footer. Header appears to consist of a series of + 4-byte Little Endian words. + + 10 00 00 00 MM MM 00 00 00 00 00 00 01 00 00 00 MM == Model (ie 1245d) + 64 00 00 00 00 00 00 00 TT 00 00 00 00 00 00 00 TT == Media Size (0x10 fixed) + MM 00 00 00 PP 00 00 00 00 00 00 00 ZZ ZZ ZZ ZZ MM = Print Method (aka cut control), PP = Default/Glossy/Matte (0x01/0x03/0x05), ZZ == matte intensity (0x7fffffff for glossy, else 0x00000000 +- 25 for matte) + VV 00 00 00 WW WW 00 00 HH HH 00 00 XX 00 00 00 VV == dust; 0x00 default, 0x01 off, 0x02 on, XX == Copies + 00 00 00 00 00 00 00 00 00 00 00 00 ce ff ff ff + 00 00 00 00 ce ff ff ff QQ QQ 00 00 ce ff ff ff QQ == DPI, ie 300. + 00 00 00 00 ce ff ff ff 00 00 00 00 00 00 00 00 + 00 00 00 00 + + [[Packed RGB payload of WW*HH*3 bytes]] + + 04 03 02 01 [[ footer ]] + + +*/ diff --git a/src/cups/shinko_s2145_print.c b/src/cups/shinko_s2145_print.c index 5196528..c6fd88f 100644 --- a/src/cups/shinko_s2145_print.c +++ b/src/cups/shinko_s2145_print.c @@ -39,8 +39,9 @@ #include <fcntl.h> #include <signal.h> -#include "backend_common.h" +#define BACKEND shinkos2145_backend +#include "backend_common.h" enum { S_IDLE = 0, @@ -53,7 +54,7 @@ enum { struct s2145_printjob_hdr { uint32_t len1; /* Fixed at 0x10 */ uint32_t model; /* Equal to the printer model (eg '2145' or '1245' decimal) */ - uint32_t med_type; /* 6145 only, media type */ + uint32_t unk2; uint32_t unk3; /* Fixed at 0x01 */ uint32_t len2; /* Fixed at 0x64 */ @@ -61,9 +62,9 @@ struct s2145_printjob_hdr { uint32_t media; uint32_t unk6; - uint32_t method; /* Method for 2145, 0x00 for 6245, multicut for 6145 */ - uint32_t mode; /* Mode for 2145, 0x00 for 6245, quality for 6145 */ - uint32_t oc_mode; /* 6145/6245 only, Matte/Glossy/None */ + uint32_t method; + uint32_t mode; + uint32_t unk7; uint32_t unk8; uint32_t unk9; @@ -94,13 +95,12 @@ struct shinkos2145_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; + uint8_t jobid; - uint8_t fast_return; struct s2145_printjob_hdr hdr; - uint32_t model; - uint8_t *databuf; int datalen; }; @@ -684,11 +684,11 @@ struct s2145_status_resp { static char *bank_statuses(uint8_t v) { switch (v) { - case 0: + case BANK_STATUS_FREE: return "Free"; - case 1: + case BANK_STATUS_XFER: return "Xfer"; - case 2: + case BANK_STATUS_FULL: return "Full"; default: return "Unknown"; @@ -723,7 +723,7 @@ struct s2145_mediainfo_item { uint16_t columns; uint16_t rows; uint8_t media_type; - uint8_t print_type; + uint8_t print_type; /* The same as the "print method" */ uint8_t reserved[3]; } __attribute__((packed)); @@ -900,7 +900,7 @@ static int get_fwinfo(struct shinkos2145_ctx *ctx) (uint8_t*)&cmd, sizeof(cmd), sizeof(*resp), &num)) < 0) { - ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); + ERROR("Failed to execute %s command (%d)\n", cmd_names(cmd.hdr.cmd), ret); continue; } @@ -1136,7 +1136,7 @@ static int get_tonecurve(struct shinkos2145_ctx *ctx, int type, char *fname) int ret, num = 0; uint8_t *data; - uint16_t curves[768]; + uint16_t curves[UPDATE_SIZE] = { 0 } ; int i,j; @@ -1161,6 +1161,7 @@ static int get_tonecurve(struct shinkos2145_ctx *ctx, int type, char *fname) if (!data) { ERROR("Memory allocation failure! (%d bytes)\n", resp->total_size * 2); + return -1; } i = 0; @@ -1189,11 +1190,11 @@ static int get_tonecurve(struct shinkos2145_ctx *ctx, int type, char *fname) goto done; } - for (i = 0 ; i < 768; i++) { + for (i = 0 ; i < UPDATE_SIZE; i++) { /* Byteswap appropriately */ curves[i] = cpu_to_be16(le16_to_cpu(curves[i])); - write(tc_fd, &curves[i], sizeof(uint16_t)); } + write(tc_fd, curves, UPDATE_SIZE * sizeof(uint16_t)); close(tc_fd); } @@ -1210,11 +1211,12 @@ static int set_tonecurve(struct shinkos2145_ctx *ctx, int target, char *fname) INFO("Set %s Tone Curve from '%s'\n", update_targets(target), fname); - uint16_t *data = malloc(UPDATE_SIZE); + uint16_t *data = malloc(UPDATE_SIZE * sizeof(uint16_t)); if (!data) { ERROR("Memory allocation failure! (%d bytes)\n", UPDATE_SIZE); + return -1; } /* Read in file */ @@ -1223,26 +1225,26 @@ static int set_tonecurve(struct shinkos2145_ctx *ctx, int target, char *fname) ret = -1; goto done; } - if (read(tc_fd, data, UPDATE_SIZE) != UPDATE_SIZE) { + if (read(tc_fd, data, UPDATE_SIZE * sizeof(uint16_t)) != (UPDATE_SIZE * sizeof(uint16_t))) { ret = -2; goto done; } close(tc_fd); /* Byteswap data to local CPU.. */ - for (ret = 0; ret < UPDATE_SIZE ; ret+=2) { + for (ret = 0; ret < UPDATE_SIZE ; ret++) { data[ret] = be16_to_cpu(data[ret]); } /* Set up command */ cmd.target = target; cmd.reserved = 0; - cmd.size = cpu_to_le32(UPDATE_SIZE); + cmd.size = cpu_to_le32(UPDATE_SIZE * sizeof(uint16_t)); cmd.hdr.cmd = cpu_to_le16(S2145_CMD_UPDATE); cmd.hdr.len = cpu_to_le16(sizeof(struct s2145_update_cmd)-sizeof(cmd.hdr)); /* Byteswap data to format printer is expecting.. */ - for (ret = 0; ret < UPDATE_SIZE ; ret+=2) { + for (ret = 0; ret < UPDATE_SIZE ; ret++) { data[ret] = cpu_to_le16(data[ret]); } @@ -1256,7 +1258,7 @@ static int set_tonecurve(struct shinkos2145_ctx *ctx, int target, char *fname) /* Sent transfer */ if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t *) data, UPDATE_SIZE))) { + (uint8_t *) data, UPDATE_SIZE * sizeof(uint16_t)))) { goto done; } @@ -1274,10 +1276,10 @@ static void shinkos2145_cmdline(void) DEBUG("\t\t[ -e ] # Query error log\n"); DEBUG("\t\t[ -f ] # Use fast return mode\n"); DEBUG("\t\t[ -F ] # Flash Printer LED\n"); + DEBUG("\t\t[ -i ] # Query printer info\n"); DEBUG("\t\t[ -l filename ] # Get current tone curve\n"); DEBUG("\t\t[ -L filename ] # Set current tone curve\n"); DEBUG("\t\t[ -m ] # Query media\n"); - DEBUG("\t\t[ -i ] # Query printer info\n"); DEBUG("\t\t[ -r ] # Reset user/NV tone curve\n"); DEBUG("\t\t[ -R ] # Reset printer to factory defaults\n"); DEBUG("\t\t[ -s ] # Query status\n"); @@ -1291,112 +1293,65 @@ int shinkos2145_cmdline_arg(void *vctx, int argc, char **argv) struct shinkos2145_ctx *ctx = vctx; int i, j = 0; + if (!ctx) + return -1; + /* Reset arg parsing */ optind = 1; opterr = 0; - while ((i = getopt(argc, argv, "b:c:C:efFil:L:mr:R:suU:X:")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "b:c:C:eFil:L:mr:R:suU:X:")) >= 0) { switch(i) { + GETOPT_PROCESS_GLOBAL case 'b': - if (ctx) { - if (optarg[0] == '1') - j = button_set(ctx, BUTTON_ENABLED); - else if (optarg[0] == '0') - j = button_set(ctx, BUTTON_DISABLED); - else - return -1; - break; - } - return 1; + if (optarg[0] == '1') + j = button_set(ctx, BUTTON_ENABLED); + else if (optarg[0] == '0') + j = button_set(ctx, BUTTON_DISABLED); + else + return -1; + break; case 'c': - if (ctx) { - j = get_tonecurve(ctx, TONECURVE_USER, optarg); - break; - } - return 1; + j = get_tonecurve(ctx, TONECURVE_USER, optarg); + break; case 'C': - if (ctx) { - j = set_tonecurve(ctx, TONECURVE_USER, optarg); - break; - } - return 1; + j = set_tonecurve(ctx, TONECURVE_USER, optarg); + break; case 'e': - if (ctx) { - j = get_errorlog(ctx); - break; - } - return 1; - case 'f': - if (ctx) { - ctx->fast_return = 1; - break; - } - return 1; + j = get_errorlog(ctx); + break; case 'F': - if (ctx) { - j = flash_led(ctx); - break; - } - return 1; + j = flash_led(ctx); + break; case 'i': - if (ctx) { - j = get_fwinfo(ctx); - break; - } - return 1; + j = get_fwinfo(ctx); + break; case 'l': - if (ctx) { - j = get_tonecurve(ctx, TONECURVE_CURRENT, optarg); - break; - } - return 1; + j = get_tonecurve(ctx, TONECURVE_CURRENT, optarg); + break; case 'L': - if (ctx) { - j = set_tonecurve(ctx, TONECURVE_CURRENT, optarg); - break; - } - return 1; + j = set_tonecurve(ctx, TONECURVE_CURRENT, optarg); + break; case 'm': - if (ctx) { - j = get_mediainfo(ctx); - break; - } - return 1; + j = get_mediainfo(ctx); + break; case 'r': - if (ctx) { - j = reset_curve(ctx, RESET_USER_CURVE); - break; - } - return 1; + j = reset_curve(ctx, RESET_USER_CURVE); + break; case 'R': - if (ctx) { - j = reset_curve(ctx, RESET_PRINTER); - break; - } - return 1; + j = reset_curve(ctx, RESET_PRINTER); + break; case 's': - if (ctx) { - j = get_status(ctx); - break; - } - return 1; + j = get_status(ctx); + break; case 'u': - if (ctx) { - j = get_user_string(ctx); - break; - } - return 1; + j = get_user_string(ctx); + break; case 'U': - if (ctx) { - j = set_user_string(ctx, optarg); - break; - } - return 1; + j = set_user_string(ctx, optarg); + break; case 'X': - if (ctx) { - j = cancel_job(ctx, optarg); - break; - } - return 1; + j = cancel_job(ctx, optarg); + break; default: break; /* Ignore completely */ } @@ -1418,10 +1373,6 @@ static void *shinkos2145_init(void) } memset(ctx, 0, sizeof(struct shinkos2145_ctx)); - /* Use Fast return by default in CUPS mode */ - if (getenv("DEVICE_URI") || getenv("FAST_RETURN")) - ctx->fast_return = 1; - return ctx; } @@ -1429,16 +1380,23 @@ static void shinkos2145_attach(void *vctx, struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct shinkos2145_ctx *ctx = vctx; + struct libusb_device *device; + struct libusb_device_descriptor desc; ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; + device = libusb_get_device(dev); + libusb_get_device_descriptor(device, &desc); + + ctx->type = lookup_printer_type(&shinkos2145_backend, + desc.idVendor, desc.idProduct); + /* Ensure jobid is sane */ ctx->jobid = (jobid & 0x7f) + 1; } - static void shinkos2145_teardown(void *vctx) { struct shinkos2145_ctx *ctx = vctx; @@ -1451,18 +1409,19 @@ static void shinkos2145_teardown(void *vctx) { free(ctx); } -static int shinkos2145_early_parse(void *vctx, int data_fd) { +static int shinkos2145_read_parse(void *vctx, int data_fd) { struct shinkos2145_ctx *ctx = vctx; - int printer_type, ret; + int ret; + uint8_t tmpbuf[4]; if (!ctx) - return -1; + return CUPS_BACKEND_FAILED; /* Read in then validate header */ ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); if (ret < 0 || ret != sizeof(ctx->hdr)) { if (ret == 0) - return -1; /* deliberate */ + return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d/%d)\n", ret, 0, (int)sizeof(ctx->hdr)); perror("ERROR: Read failed"); @@ -1473,42 +1432,20 @@ static int shinkos2145_early_parse(void *vctx, int data_fd) { le32_to_cpu(ctx->hdr.len2) != 0x64 || le32_to_cpu(ctx->hdr.dpi) != 300) { ERROR("Unrecognized header data format!\n"); - return -1; + return CUPS_BACKEND_CANCEL; } - ctx->model = le32_to_cpu(ctx->hdr.model); - - switch(ctx->model) { - case 2145: - printer_type = P_SHINKO_S2145; - break; - case 6145: - case 6245: - default: + if (le32_to_cpu(ctx->hdr.model) != 2145) { ERROR("Unrecognized printer (%d)!\n", le32_to_cpu(ctx->hdr.model)); - return -1; + return CUPS_BACKEND_CANCEL; } - INFO("File intended for an S%d printer\n", ctx->model); - - return printer_type; -} - -static int shinkos2145_read_parse(void *vctx, int data_fd) { - struct shinkos2145_ctx *ctx = vctx; - int ret; - uint8_t tmpbuf[4]; - - if (!ctx) - return CUPS_BACKEND_FAILED; - if (ctx->databuf) { free(ctx->databuf); ctx->databuf = NULL; } - ctx->datalen = le32_to_cpu(ctx->hdr.rows) * le32_to_cpu(ctx->hdr.columns) * 3; ctx->databuf = malloc(ctx->datalen); if (!ctx->databuf) { @@ -1585,7 +1522,8 @@ static int shinkos2145_main_loop(void *vctx, int copies) { for (i = 0; i < media->count ; i++) { /* Look for matching media */ if (le16_to_cpu(media->items[i].columns) == cpu_to_le16(le32_to_cpu(ctx->hdr.columns)) && - le16_to_cpu(media->items[i].rows) == cpu_to_le16(le32_to_cpu(ctx->hdr.rows))) + le16_to_cpu(media->items[i].rows) == cpu_to_le16(le32_to_cpu(ctx->hdr.rows)) && + media->items[i].print_type == le32_to_cpu(ctx->hdr.method)) break; } if (i == media->count) { @@ -1593,7 +1531,9 @@ static int shinkos2145_main_loop(void *vctx, int copies) { return CUPS_BACKEND_HOLD; } - top: + // XXX check copies against remaining media! + +top: if (state != last_state) { if (dyesub_debug) DEBUG("last_state %d new %d\n", last_state, state); @@ -1645,19 +1585,13 @@ static int shinkos2145_main_loop(void *vctx, int copies) { print->hdr.cmd = cpu_to_le16(S2145_CMD_PRINTJOB); print->hdr.len = cpu_to_le16(sizeof (*print) - sizeof(*cmd)); - if (ctx->model == 2145) { - print->id = ctx->jobid; - print->count = cpu_to_le16(copies); - print->columns = cpu_to_le16(le32_to_cpu(ctx->hdr.columns)); - print->rows = cpu_to_le16(le32_to_cpu(ctx->hdr.rows)); - print->media = le32_to_cpu(ctx->hdr.media); - print->mode = le32_to_cpu(ctx->hdr.mode); - print->method = le32_to_cpu(ctx->hdr.method); - } else { - // s6145, s6245 use different fields - ERROR("Don't know how to initiate print on non-2145 models!\n"); - return CUPS_BACKEND_FAILED; - } + print->id = ctx->jobid; + print->count = cpu_to_le16(copies); + print->columns = cpu_to_le16(le32_to_cpu(ctx->hdr.columns)); + print->rows = cpu_to_le16(le32_to_cpu(ctx->hdr.rows)); + print->media = le32_to_cpu(ctx->hdr.media); + print->mode = le32_to_cpu(ctx->hdr.mode); + print->method = le32_to_cpu(ctx->hdr.method); if ((ret = s2145_do_cmd(ctx, cmdbuf, sizeof(*print), @@ -1671,6 +1605,9 @@ static int shinkos2145_main_loop(void *vctx, int copies) { if (sts->hdr.error == ERROR_BUFFER_FULL) { INFO("Printer Buffers full, retrying\n"); break; + } else if ((sts->hdr.status & 0xf0) == 0x30 || sts->hdr.status == 0x21) { + INFO("Printer busy (%s), retrying\n", status_str(sts->hdr.status)); + break; } else if (sts->hdr.status != ERROR_NONE) goto printer_error; } @@ -1685,7 +1622,7 @@ static int shinkos2145_main_loop(void *vctx, int copies) { state = S_PRINTER_SENT_DATA; break; case S_PRINTER_SENT_DATA: - if (ctx->fast_return) { + if (fast_return) { INFO("Fast return mode enabled.\n"); state = S_FINISHED; } else if (sts->hdr.status == STATUS_READY || @@ -1700,19 +1637,7 @@ static int shinkos2145_main_loop(void *vctx, int copies) { if (state != S_FINISHED) goto top; - /* This printer handles copies internally */ - copies = 1; - - /* Clean up */ - if (terminate) - copies = 1; - - INFO("Print complete (%d copies remaining)\n", copies - 1); - - if (copies && --copies) { - state = S_IDLE; - goto top; - } + INFO("Print complete\n"); return CUPS_BACKEND_OK; @@ -1764,29 +1689,21 @@ static int shinkos2145_query_serno(struct libusb_device_handle *dev, uint8_t end /* Exported */ #define USB_VID_SHINKO 0x10CE #define USB_PID_SHINKO_S2145 0x000E -#define USB_PID_SHINKO_S6145 0x0019 -#define USB_PID_SHINKO_S6245 0x001D -//#define USB_VID_CIAAT xxxxxx -//#define USB_PID_CIAAT_BRAVA21 xxxxx struct dyesub_backend shinkos2145_backend = { .name = "Shinko/Sinfonia CHC-S2145", - .version = "0.40", + .version = "0.46", .uri_prefix = "shinkos2145", .cmdline_usage = shinkos2145_cmdline, .cmdline_arg = shinkos2145_cmdline_arg, .init = shinkos2145_init, .attach = shinkos2145_attach, .teardown = shinkos2145_teardown, - .early_parse = shinkos2145_early_parse, .read_parse = shinkos2145_read_parse, .main_loop = shinkos2145_main_loop, .query_serno = shinkos2145_query_serno, .devices = { { USB_VID_SHINKO, USB_PID_SHINKO_S2145, P_SHINKO_S2145, ""}, -// { USB_VID_SHINKO, USB_PID_SHINKO_S6145, P_SHINKO_S2145, ""}, -// { USB_VID_SHINKO, USB_PID_SHINKO_S6245, P_SHINKO_S2145, ""}, -// { USB_VID_CIAAT, USB_PID_CIAAT_BRAVA21, P_SHINKO_S2145, ""}, { 0, 0, 0, ""} } }; @@ -1810,67 +1727,4 @@ struct dyesub_backend shinkos2145_backend = { 04 03 02 01 [[ footer ]] - * CHC-S6245 data format - - Spool file consists of an 116-byte header, followed by RGB-packed data, - followed by a 4-byte footer. Header appears to consist of a series of - 4-byte Little Endian words. - - 10 00 00 00 MM MM 00 00 01 00 00 00 01 00 00 00 MM == Model (ie 6245d) - 64 00 00 00 00 00 00 00 TT 00 00 00 00 00 00 00 TT == 0x20 8x4, 0x21 8x5, 0x22 8x6, 0x23 8x8, 0x10 8x10, 0x11 8x12 - 00 00 00 00 00 00 00 00 XX 00 00 00 00 00 00 00 XX == 0x03 matte, 0x02 glossy, 0x01 no coat - 00 00 00 00 WW WW 00 00 HH HH 00 00 NN 00 00 00 WW/HH Width, Height (LE), NN == Copies - 00 00 00 00 00 00 00 00 00 00 00 00 ce ff ff ff - 00 00 00 00 ce ff ff ff QQ QQ 00 00 ce ff ff ff QQ == DPI (300) - 00 00 00 00 ce ff ff ff 00 00 00 00 00 00 00 00 - 00 00 00 00 - - [[Packed RGB payload of WW*HH*3 bytes]] - - 04 03 02 01 [[ footer ]] - - * CHC-S6145 data format - - Spool file consists of an 116-byte header, followed by RGB-packed data, - followed by a 4-byte footer. Header appears to consist of a series of - 4-byte Little Endian words. - - 10 00 00 00 MM MM 00 00 HH 00 00 00 01 00 00 00 MM == Model (ie 6145d), HH == 0x02 (5" media), 0x03 (6" media) - 64 00 00 00 00 00 00 00 TT 00 00 00 00 00 00 00 TT == 0x08 5x5, 0x03 5x7, 0x07 2x6, 0x00 4x6, 0x06 6x6/6x6+6x2/6x8 - UU 00 00 00 ZZ 00 00 00 XX 00 00 00 00 00 00 00 XX == 0x00 default, 0x02 glossy, 0x03 matte, ZZ == 0x00 default, 0x01 == std qual; UU == 0x00 normal, 0x04 2x6*2, 0x05 6x6+2x6 - 00 00 00 00 WW WW 00 00 HH HH 00 00 NN 00 00 00 WW/HH Width, Height (LE), NN == Copies - 00 00 00 00 00 00 00 00 00 00 00 00 ce ff ff ff - 00 00 00 00 ce ff ff ff QQ QQ 00 00 ce ff ff ff QQ == DPI (300) - 00 00 00 00 ce ff ff ff 00 00 00 00 00 00 00 00 - 00 00 00 00 - - [[Packed RGB payload of WW*HH*3 bytes]] - - 04 03 02 01 [[ footer ]] - - * CIAAT Brava 21 data format - - This printer is supposed to be a variant of the S6145, but uses a - different spool format -- but seems to use the same command language. - - 01 40 12 00 01 NN 00 YY YY XX XX TT 00 00 00 00 00 00 01 MM QQ 00 - - NN == copies - YY YY == Columns (LE) - XX XX == Rows (LE) - MM == Overcoat (02 = glossy, 03 = matte, 01 = none) - QQ == Multicut (00 = normal, 01 = none, 02 = 2*4x6, - 04 = 2*2x6, 80 = 4x6-notrim) - TT == Type (00 = 4x6, 03 = 5x7, 06 = 8x6, 07 = 2x6) - - 1844*2434 8x6 - 1844*2492 4x6*2 - 1548*2140 5x7 - 1844*1240 4x6 (and 2x6*2) - 1844*1210 4x6-notrim (WTF?) - 1844*634 2x6 - - - [[ Followed by XX*YY*3 bytes of image data, RGB ]] - */ diff --git a/src/cups/shinko_s6245_print.c b/src/cups/shinko_s6245_print.c new file mode 100644 index 0000000..e4b57e9 --- /dev/null +++ b/src/cups/shinko_s6245_print.c @@ -0,0 +1,1863 @@ +/* + * Shinko/Sinfonia CHC-S6245 CUPS backend -- libusb-1.0 version + * + * (c) 2013-2015 Solomon Peachy <pizza@shaftnet.org> + * + * Low-level documentation was provided by Sinfonia, Inc. Thank you! + * + * The latest version of this program can be found at: + * + * http://git.shaftnet.org/cgit/selphy_print.git + * + * 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. + * + * [http://www.gnu.org/licenses/gpl-2.0.html] + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <signal.h> +#include <time.h> + +#define BACKEND shinkos6245_backend + +#include "backend_common.h" + + +enum { + S_IDLE = 0, + S_PRINTER_READY_CMD, + S_PRINTER_SENT_DATA, + S_FINISHED, +}; + +/* Structure of printjob header. All fields are LITTLE ENDIAN */ +struct s6245_printjob_hdr { + uint32_t len1; /* Fixed at 0x10 */ + uint32_t model; /* Equal to the printer model (eg '6245' or '1245' decimal) */ + uint32_t unk2; + uint32_t unk3; /* Fixed at 0x01 */ + + uint32_t len2; /* Fixed at 0x64 */ + uint32_t unk5; + uint32_t media; // 8x4->8x12 + uint32_t unk6; + + uint32_t unk7; + uint32_t unk7a; + uint32_t oc_mode; /* 6145/6245 only, Matte/Glossy/None */ + uint32_t unk8; + + uint32_t unk9; + uint32_t columns; + uint32_t rows; + uint32_t copies; + + uint32_t unk10; + uint32_t unk11; + uint32_t unk12; + uint32_t unk13; + + uint32_t unk14; + uint32_t unk15; + uint32_t dpi; /* Fixed at '300' (decimal) */ + uint32_t unk16; + + uint32_t unk17; + uint32_t unk18; + uint32_t unk19; + uint32_t unk20; + + uint32_t unk21; +} __attribute__((packed)); + +/* Private data stucture */ +struct shinkos6245_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + + uint8_t jobid; + + struct s6245_printjob_hdr hdr; + + uint8_t *databuf; + int datalen; +}; + +/* Structs for printer */ +struct s6245_cmd_hdr { + uint16_t cmd; + uint16_t len; /* Not including this header */ +} __attribute__((packed)); + +#define S6245_CMD_GETSTATUS 0x0001 +#define S6245_CMD_MEDIAINFO 0x0002 +#define S6245_CMD_ERRORLOG 0x0004 +#define S6245_CMD_GETPARAM 0x0005 +#define S6245_CMD_GETSERIAL 0x0006 +#define S6245_CMD_PRINTSTAT 0x0007 +#define S6245_CMD_EXTCOUNTER 0x0008 + +#define S6245_CMD_PRINTJOB 0x4001 +#define S6245_CMD_CANCELJOB 0x4002 +#define S6245_CMD_FLASHLED 0x4003 +#define S6245_CMD_RESET 0x4004 +#define S6245_CMD_READTONE 0x4005 +#define S6245_CMD_SETPARAM 0x4007 + +#define S6245_CMD_GETEEPROM 0x400E +#define S6245_CMD_SETEEPROM 0x400F +#define S6245_CMD_SETTIME 0x4011 + +#define S6245_CMD_FWINFO 0xC003 +#define S6245_CMD_UPDATE 0xC004 + +static char *cmd_names(uint16_t v) { + switch (le16_to_cpu(v)) { + case S6245_CMD_GETSTATUS: + return "Get Status"; + case S6245_CMD_MEDIAINFO: + return "Get Media Info"; + case S6245_CMD_ERRORLOG: + return "Get Error Log"; + case S6245_CMD_GETPARAM: + return "Get Parameter"; + case S6245_CMD_GETSERIAL: + return "Get Serial Number"; + case S6245_CMD_PRINTSTAT: + return "Get Print ID Status"; + case S6245_CMD_EXTCOUNTER: + return "Get Extended Counters"; + case S6245_CMD_PRINTJOB: + return "Print"; + case S6245_CMD_CANCELJOB: + return "Cancel Print"; + case S6245_CMD_FLASHLED: + return "Flash LEDs"; + case S6245_CMD_RESET: + return "Reset"; + case S6245_CMD_READTONE: + return "Read Tone Curve"; + case S6245_CMD_SETPARAM: + return "Set Parameter"; + case S6245_CMD_GETEEPROM: + return "Get EEPROM Backup Parameter"; + case S6245_CMD_SETEEPROM: + return "Set EEPROM Backup Parameter"; + case S6245_CMD_SETTIME: + return "Time Setting"; + case S6245_CMD_FWINFO: + return "Get Firmware Info"; + case S6245_CMD_UPDATE: + return "Update"; + default: + return "Unknown Command"; + } +}; + +struct s6245_print_cmd { + struct s6245_cmd_hdr hdr; + uint8_t id; + uint16_t count; + uint16_t columns; + uint16_t rows; + uint8_t reserved[8]; // columns and rows repeated, then nulls + uint8_t mode; + uint8_t method; + uint8_t reserved2; +} __attribute__((packed)); + +#define PRINT_MODE_NO_OC 0x01 +#define PRINT_MODE_GLOSSY 0x02 +#define PRINT_MODE_MATTE 0x03 + +#if 0 +static char *print_modes(uint8_t v) { + switch (v) { + case PRINT_MODE_NO_OC: + return "No Overcoat"; + case PRINT_MODE_GLOSSY: + return "Glossy"; + case PRINT_MODE_MATTE: + return "Matte"; + default: + return "Unknown"; + } +} +#endif + +#define PRINT_METHOD_STD 0x00 +#define PRINT_METHOD_COMBO_2 0x02 +#define PRINT_METHOD_COMBO_3 0x03 + +#define PRINT_METHOD_DISABLE_ERR 0x10 + +static char *print_methods (uint8_t v) { + switch (v & 0xf) { + case PRINT_METHOD_STD: + return "Standard"; + case PRINT_METHOD_COMBO_2: + return "2up"; + case PRINT_METHOD_COMBO_3: + return "3up"; + default: + return "Unknown"; + } +} + +struct s6245_cancel_cmd { + struct s6245_cmd_hdr hdr; + uint8_t id; +} __attribute__((packed)); + +struct s6245_reset_cmd { + struct s6245_cmd_hdr hdr; + uint8_t target; + uint8_t curveid; +} __attribute__((packed)); + +#define RESET_PRINTER 0x03 +#define RESET_TONE_CURVE 0x04 + +#define TONE_CURVE_ID 0x01 + +struct s6245_readtone_cmd { + struct s6245_cmd_hdr hdr; + uint8_t target; + uint8_t curveid; +} __attribute__((packed)); + +#define READ_TONE_CURVE_USER 0x01 +#define READ_TONE_CURVE_CURR 0x02 + +struct s6245_setparam_cmd { + struct s6245_cmd_hdr hdr; + uint8_t target; + uint32_t param; +} __attribute__((packed)); + +#define PARAM_DRIVER_MODE 0x3e +#define PARAM_PAPER_MODE 0x3f +#define PARAM_SLEEP_TIME 0x54 + +#define PARAM_DRIVER_WIZOFF 0x00000000 +#define PARAM_DRIVER_WIZON 0x00000001 + +#define PARAM_PAPER_NOCUT 0x00000000 +#define PARAM_PAPER_CUTLOAD 0x00000001 + +#define PARAM_SLEEP_5MIN 0x00000000 +#define PARAM_SLEEP_15MIN 0x00000001 +#define PARAM_SLEEP_30MIN 0x00000002 +#define PARAM_SLEEP_60MIN 0x00000003 +#define PARAM_SLEEP_120MIN 0x00000004 +#define PARAM_SLEEP_240MIN 0x00000005 + +struct s6245_seteeprom_cmd { + struct s6245_cmd_hdr hdr; + uint8_t data[256]; /* Maxlen */ +} __attribute__((packed)); + +struct s6245_settime_cmd { + struct s6245_cmd_hdr hdr; + uint8_t enable; /* 0 or 1 */ + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t day; + uint8_t month; + uint8_t year; +} __attribute__((packed)); + +struct s6245_errorlog_cmd { + struct s6245_cmd_hdr hdr; + uint16_t index; /* 0 is latest */ +} __attribute__((packed)); + +struct s6245_getparam_cmd { + struct s6245_cmd_hdr hdr; + uint8_t target; +} __attribute__((packed)); + +struct s6245_getprintidstatus_cmd { + struct s6245_cmd_hdr hdr; + uint8_t id; +} __attribute__((packed)); + +struct s6245_fwinfo_cmd { + struct s6245_cmd_hdr hdr; + uint8_t target; +} __attribute__((packed)); + +#define FWINFO_TARGET_MAIN_BOOT 0x01 +#define FWINFO_TARGET_MAIN_APP 0x02 +#define FWINFO_TARGET_PRINT_TABLES 0x03 +#define FWINFO_TARGET_DSP 0x04 + +static char *fwinfo_targets (uint8_t v) { + switch (v) { + case FWINFO_TARGET_MAIN_BOOT: + return "Main Boot "; + case FWINFO_TARGET_MAIN_APP: + return "Main App "; + case FWINFO_TARGET_DSP: + return "DSP "; + case FWINFO_TARGET_PRINT_TABLES: + return "Print Tables"; + default: + return "Unknown "; + } +} + +struct s6245_update_cmd { + struct s6245_cmd_hdr hdr; + uint8_t target; + uint8_t curve_id; + uint8_t reset; // ?? + uint8_t reserved[3]; + uint32_t size; +} __attribute__((packed)); + +#define UPDATE_TARGET_USER 0x03 +#define UPDATE_TARGET_CURRENT 0x04 + +static char *update_targets (uint8_t v) { + switch (v) { + case UPDATE_TARGET_USER: + return "User"; + case UPDATE_TARGET_CURRENT: + return "Current"; + default: + return "Unknown"; + } +} + +#define UPDATE_SIZE 0x600 +/* Update is three channels, Y, M, C; + each is 256 entries of 11-bit data padded to 16-bits. + Printer expects LE data. We use BE data on disk. +*/ + +struct s6245_status_hdr { + uint8_t result; + uint8_t error; + uint8_t printer_major; + uint8_t printer_minor; + uint8_t reserved[2]; + uint8_t mode; + uint8_t status; + uint16_t payload_len; +} __attribute__((packed)); + +#define RESULT_SUCCESS 0x01 +#define RESULT_FAIL 0x02 + +#define ERROR_NONE 0x00 +#define ERROR_INVALID_PARAM 0x01 +#define ERROR_MAIN_APP_INACTIVE 0x02 +#define ERROR_COMMS_TIMEOUT 0x03 +#define ERROR_MAINT_NEEDED 0x04 +#define ERROR_BAD_COMMAND 0x05 +#define ERROR_PRINTER 0x11 +#define ERROR_BUFFER_FULL 0x21 + +static char *error_codes(uint8_t major, uint8_t minor) +{ + switch(major) { + case 0x01: /* "Controller Error" */ + switch(minor) { + case 0x01: + return "Controller: EEPROM Write Timeout"; + case 0x09: + return "Controller: DSP FW Boot"; + case 0x0A: + return "Controller: Invalid Print Parameter Table"; + case 0x0B: + return "Controller: DSP FW Mismatch"; + case 0x0C: + return "Controller: Print Parameter Table Mismatch"; + case 0x0D: + return "Controller: FPGA Configuration Failed"; + case 0x0F: + return "Controller: Main FW Checksum"; + case 0x10: + return "Controller: Flash Write Failed"; + case 0x11: + return "Controller: DSP Checksum"; + case 0x12: + return "Controller: DSP FW Write Failed"; + case 0x13: + return "Controller: Print Parameter Table Checksum"; + case 0x14: + return "Controller: Print Parameter Table Write Failed"; + case 0x15: + return "Controller: User Tone Curve Write Failed"; + case 0x16: + return "Controller: MSP Communication"; + case 0x17: + return "Controller: THV Autotuning"; + case 0x18: + return "Controller: THV Value Out of Range"; + case 0x19: + return "Controller: Thermal Head"; + case 0x1B: + return "Controller: DSP Communication"; + case 0x1C: + return "Controller: DSP DMA Failed"; + default: + return "Controller: Unknown"; + } + case 0x02: /* "Mechanical Error" */ + switch (minor) { + case 0x01: + return "Mechanical: Pinch Head Home"; + case 0x02: + return "Mechanical: Pinch Head (position 1)"; + case 0x03: + return "Mechanical: Pinch Head (position 2)"; + case 0x04: + return "Mechanical: Pinch Head (position 3)"; + case 0x0B: + return "Mechanical: Cutter (Right)"; + case 0x0C: + return "Mechanical: Cutter (Left)"; + default: + return "Mechanical: Unknown"; + } + case 0x03: /* "Sensor Error" */ + switch (minor) { + case 0x01: + return "Sensor: Head Up"; + case 0x02: + return "Sensor: Head Down"; + case 0x0B: + return "Sensor: Cutter Left"; + case 0x0C: + return "Sensor: Cutter Right"; + case 0x0D: + return "Sensor: Cutter Left+Right"; + case 0x15: + return "Sensor: Head Up Unstable"; + case 0x16: + return "Sensor: Head Down Unstable"; + case 0x17: + return "Sensor: Cutter Left Unstable"; + case 0x18: + return "Sensor: Cutter Right Unstable"; + case 0x19: + return "Sensor: Cover Open Unstable"; + case 0x1E: + return "Sensor: Ribbon Mark (Cyan)"; + case 0x1F: + return "Sensor: Ribbon Mark (OC)"; + default: + return "Sensor: Unknown"; + } + case 0x04: /* "Temperature Sensor Error" */ + switch (minor) { + case 0x01: + return "Temp Sensor: Thermal Head Low"; + case 0x02: + return "Temp Sensor: Thermal Head High"; + case 0x05: + return "Temp Sensor: Environment Low"; + case 0x06: + return "Temp Sensor: Environment High"; + case 0x07: + return "Temp Sensor: Preheat"; + case 0x08: + return "Temp Sensor: Thermal Protect"; + default: + return "Temp Sensor: Unknown"; + } + case 0x5: /* "Paper Jam" */ + switch (minor) { + case 0x01: + return "Paper Jam: Loading Paper Top On"; + case 0x02: + return "Paper Jam: Loading Print Position On"; + case 0x03: + return "Paper Jam: Loading Print Position Off"; + case 0x04: + return "Paper Jam: Loading Paper Top Off"; + case 0x05: + return "Paper Jam: Loading Cut Print Position Off"; + case 0x0C: + return "Paper Jam: Initializing Print Position Off"; + case 0x0D: + return "Paper Jam: Initializing Print Position On"; + case 0x15: + return "Paper Jam: Printing Print Position Off"; + case 0x16: + return "Paper Jam: Printing Paper Top On"; + case 0x17: + return "Paper Jam: Printing Paper Top Off"; + case 0x1F: + return "Paper Jam: Precut Print Position Off"; + case 0x20: + return "Paper Jam: Precut Print Position On"; + + case 0x29: + return "Paper Jam: Printing Paper Top On"; + case 0x2A: + return "Paper Jam: Printing Pre-Yellow Print Position Off"; + case 0x2B: + return "Paper Jam: Printing Yellow Print Position Off"; + case 0x2C: + return "Paper Jam: Printing Yellow Print Position On"; + case 0x2D: + return "Paper Jam: Printing Pre-Magenta Print Position Off"; + case 0x2E: + return "Paper Jam: Printing Magenta Print Position On"; + case 0x2F: + return "Paper Jam: Printing Magenta Print Position Off"; + case 0x30: + return "Paper Jam: Printing Pre-Cyan Print Position Off"; + case 0x31: + return "Paper Jam: Printing Cyan Print Position On"; + case 0x32: + return "Paper Jam: Printing Cyan Print Position Off"; + case 0x33: + return "Paper Jam: Printing Pre-OC Print Position Off"; + case 0x34: + return "Paper Jam: Printing OC Print Position On"; + case 0x35: + return "Paper Jam: Printing OC Print Position Off"; + case 0x36: + return "Paper Jam: Cut Print Position Off"; + case 0x37: + return "Paper Jam: Home Position Off"; + case 0x38: + return "Paper Jam: Paper Top Off"; + case 0x39: + return "Paper Jam: Print Position On"; + + case 0x51: + return "Paper Jam: Paper Empty On, Top On, Position On"; + case 0x52: + return "Paper Jam: Paper Empty On, Top On, Position Off"; + case 0x53: + return "Paper Jam: Paper Empty On, Top Off, Print Position On"; + case 0x54: + return "Paper Jam: Paper Empty On, Top Of, Position Off"; + case 0x55: + return "Paper Jam: Paper Empty Off, Top On, Position On"; + case 0x56: + return "Paper Jam: Paper Empty Off, Top On, Position Off"; + case 0x57: + return "Paper Jam: Paper Empty Off, Top Off, Position On"; + case 0x60: + return "Paper Jam: Cutter Right"; + case 0x61: + return "Paper Jam: Cutter Left"; + + default: + return "Paper Jam: Unknown"; + } + case 0x06: /* User Error */ + switch (minor) { + case 0x01: + return "Drawer Unit Open"; + case 0x02: + return "Incorrect Ribbon"; + case 0x03: + return "No/Empty Ribbon"; + case 0x04: + return "Mismatched Ribbon"; + case 0x08: + return "No Paper"; + case 0x0C: + return "Paper End"; + default: + return "Unknown"; + } + default: + return "Unknown"; + } +} + +static char *error_str(uint8_t v) { + switch (v) { + case ERROR_NONE: + return "None"; + case ERROR_INVALID_PARAM: + return "Invalid Command Parameter"; + case ERROR_MAIN_APP_INACTIVE: + return "Main App Inactive"; + case ERROR_COMMS_TIMEOUT: + return "Main Communication Timeout"; + case ERROR_MAINT_NEEDED: + return "Maintainence Needed"; + case ERROR_BAD_COMMAND: + return "Inappropriate Command"; + case ERROR_PRINTER: + return "Printer Error"; + case ERROR_BUFFER_FULL: + return "Buffer Full"; + default: + return "Unknown"; + } +} + +#define STATUS_READY 0x00 +#define STATUS_INIT_CPU 0x31 +#define STATUS_INIT_RIBBON 0x32 +#define STATUS_INIT_PAPER 0x33 +#define STATUS_THERMAL_PROTECT 0x34 +#define STATUS_USING_PANEL 0x35 +#define STATUS_SELF_DIAG 0x36 +#define STATUS_DOWNLOADING 0x37 + +#define STATUS_FEEDING_PAPER 0x61 +#define STATUS_PRE_HEAT 0x62 +#define STATUS_PRINT_Y 0x63 +#define STATUS_BACK_FEED_Y 0x64 +#define STATUS_PRINT_M 0x65 +#define STATUS_BACK_FEED_M 0x66 +#define STATUS_PRINT_C 0x67 +#define STATUS_BACK_FEED_C 0x68 +#define STATUS_PRINT_OP 0x69 +#define STATUS_PAPER_CUT 0x6A +#define STATUS_PAPER_EJECT 0x6B +#define STATUS_BACK_FEED_E 0x6C + +static char *status_str(uint8_t v) { + switch (v) { + case STATUS_READY: + return "Ready"; + case STATUS_INIT_CPU: + return "Initializing CPU"; + case STATUS_INIT_RIBBON: + return "Initializing Ribbon"; + case STATUS_INIT_PAPER: + return "Loading Paper"; + case STATUS_THERMAL_PROTECT: + return "Thermal Protection"; + case STATUS_USING_PANEL: + return "Using Operation Panel"; + case STATUS_SELF_DIAG: + return "Processing Self Diagnosis"; + case STATUS_DOWNLOADING: + return "Processing Download"; + case STATUS_FEEDING_PAPER: + return "Feeding Paper"; + case STATUS_PRE_HEAT: + return "Pre-Heating"; + case STATUS_PRINT_Y: + return "Printing Yellow"; + case STATUS_BACK_FEED_Y: + return "Back-Feeding - Yellow Complete"; + case STATUS_PRINT_M: + return "Printing Magenta"; + case STATUS_BACK_FEED_M: + return "Back-Feeding - Magenta Complete"; + case STATUS_PRINT_C: + return "Printing Cyan"; + case STATUS_BACK_FEED_C: + return "Back-Feeding - Cyan Complete"; + case STATUS_PRINT_OP: + return "Laminating"; + case STATUS_PAPER_CUT: + return "Cutting Paper"; + case STATUS_PAPER_EJECT: + return "Ejecting Paper"; + case STATUS_BACK_FEED_E: + return "Back-Feeding - Ejected"; + case ERROR_PRINTER: + return "Printer Error"; + default: + return "Unknown"; + } +} + +struct s6245_status_resp { + struct s6245_status_hdr hdr; + uint32_t count_lifetime; + uint32_t count_maint; + uint32_t count_paper; + uint32_t count_cutter; + uint32_t count_head; + uint32_t count_ribbon_left; + uint32_t reserved; + + uint8_t bank1_printid; + uint16_t bank1_remaining; + uint16_t bank1_finished; + uint16_t bank1_specified; + uint8_t bank1_status; + + uint8_t bank2_printid; + uint16_t bank2_remaining; + uint16_t bank2_finished; + uint16_t bank2_specified; + uint8_t bank2_status; + + uint8_t reserved2[16]; + uint8_t tonecurve_status; + uint8_t reserved3[6]; +} __attribute__((packed)); + +#define BANK_STATUS_FREE 0x00 +#define BANK_STATUS_XFER 0x01 +#define BANK_STATUS_FULL 0x02 +#define BANK_STATUS_PRINTING 0x12 + +static char *bank_statuses(uint8_t v) +{ + switch (v) { + case BANK_STATUS_FREE: + return "Free"; + case BANK_STATUS_XFER: + return "Xfer"; + case BANK_STATUS_FULL: + return "Full"; + case BANK_STATUS_PRINTING: + return "Printing"; + default: + return "Unknown"; + } +} + +#define TONECURVE_INIT 0x00 +#define TONECURVE_USER 0x01 +#define TONECURVE_CURRENT 0x02 + +static char *tonecurve_statuses (uint8_t v) +{ + switch(v) { + case 0: + return "Initial"; + case 1: + return "UserSet"; + case 2: + return "Current"; + default: + return "Unknown"; + } +} + +struct s6245_geteeprom_resp { + struct s6245_status_hdr hdr; + uint8_t data[256]; +} __attribute__((packed)); + +struct s6245_readtone_resp { + struct s6245_status_hdr hdr; + uint16_t total_size; +} __attribute__((packed)); + +struct s6245_mediainfo_item { + uint8_t media_code; + uint16_t columns; + uint16_t rows; + uint8_t reserved; + uint8_t print_method; /* PRINT_METHOD_* */ + uint8_t reserved2[3]; +} __attribute__((packed)); + +#define MEDIA_8x10 0x10 +#define MEDIA_8x12 0x11 +#define MEDIA_8x4 0x20 +#define MEDIA_8x5 0x21 +#define MEDIA_8x6 0x22 +#define MEDIA_8x8 0x23 +#define MEDIA_8x4_2 0x30 +#define MEDIA_8x5_2 0x31 +#define MEDIA_8x6_2 0x32 +#define MEDIA_8x4_3 0x40 + +static char *print_medias (uint8_t v) { + switch (v) { + case MEDIA_8x10: + return "8x10"; + case MEDIA_8x12: + return "8x12"; + case MEDIA_8x4: + return "8x4"; + case MEDIA_8x5: + return "8x5"; + case MEDIA_8x6: + return "8x6"; + case MEDIA_8x8: + return "8x8"; + case MEDIA_8x4_2: + return "8x4*2"; + case MEDIA_8x5_2: + return "8x5*2"; + case MEDIA_8x6_2: + return "8x6*2"; + case MEDIA_8x4_3: + return "8x4*3"; + default: + return "Unknown"; + } +} + +struct s6245_mediainfo_resp { + struct s6245_status_hdr hdr; + uint8_t count; + struct s6245_mediainfo_item items[10]; /* Not all necessarily used */ +} __attribute__((packed)); + +struct s6245_errorlog_resp { + struct s6245_status_hdr hdr; + uint16_t error_count; + uint8_t error_major; + uint8_t error_minor; + uint16_t reserved; + uint32_t print_counter; + uint16_t ribbon_remain; + uint8_t ribbon_takeup_diameter; + uint8_t ribbon_supply_diameter; + uint16_t main_fw_ver; + uint16_t dsp_fw_ver; + uint16_t print_param_ver; + uint16_t boot_fw_ver; + uint8_t time_sec; + uint8_t time_min; + uint8_t time_hour; + uint8_t time_day; + uint8_t time_month; + uint8_t time_year; + uint16_t reserved2; + uint8_t printer_thermistor; + uint8_t head_thermistor; + uint8_t printer_humidity; + uint8_t reserved3[13]; + uint8_t status; + uint8_t reserved4[3]; + uint16_t image_cols; + uint16_t image_rows; + uint8_t reserved5[8]; +} __attribute__((packed)); + +struct s6245_getparam_resp { + struct s6245_status_hdr hdr; + uint32_t param; +} __attribute__((packed)); + +struct s6245_getserial_resp { + struct s6245_status_hdr hdr; + uint8_t data[8]; +} __attribute__((packed)); + +struct s6245_getprintidstatus_resp { + struct s6245_status_hdr hdr; + uint8_t id; + uint16_t remaining; + uint16_t finished; + uint16_t specified; + uint16_t status; +} __attribute__((packed)); + +#define STATUS_WAITING 0x0000 +#define STATUS_PRINTING 0x0100 +#define STATUS_COMPLETED 0x0200 +#define STATUS_ERROR 0xFFFF + +struct s6245_getextcounter_resp { + struct s6245_status_hdr hdr; + uint32_t lifetime_distance; /* Inches */ + uint32_t maint_distance; + uint32_t head_distance; + uint8_t reserved[32]; +} __attribute__((packed)); + +struct s6245_fwinfo_resp { + struct s6245_status_hdr hdr; + uint8_t name[8]; + uint8_t type[16]; + uint8_t date[10]; + uint8_t major; + uint8_t minor; + uint16_t checksum; +} __attribute__((packed)); + + + +#define READBACK_LEN 512 /* Needs to be larger than largest response hdr */ +#define CMDBUF_LEN sizeof(struct s6245_print_cmd) + +uint8_t rdbuf[READBACK_LEN]; + +static int s6245_do_cmd(struct shinkos6245_ctx *ctx, + uint8_t *cmd, int cmdlen, + int minlen, int *num) +{ + int ret; + struct s6245_status_hdr *resp = (struct s6245_status_hdr *) rdbuf; + + libusb_device_handle *dev = ctx->dev; + uint8_t endp_up = ctx->endp_up; + uint8_t endp_down = ctx->endp_down; + + if ((ret = send_data(dev, endp_down, + cmd, cmdlen))) + return (ret < 0) ? ret : -99; + + ret = read_data(dev, endp_up, + rdbuf, READBACK_LEN, num); + + if (ret < 0) + return ret; + if (*num < minlen) { + ERROR("Short read! (%d/%d))\n", *num, minlen); + return -99; + } + + if (resp->result != RESULT_SUCCESS) { + INFO("Printer Status: %02x (%s)\n", resp->status, + status_str(resp->status)); + INFO(" Result: 0x%02x Error: 0x%02x (0x%02x/0x%02x = %s)\n", + resp->result, resp->error, resp->printer_major, + resp->printer_minor, error_codes(resp->printer_major, resp->printer_minor)); + return -99; + } + + return ret; +} + +static int get_status(struct shinkos6245_ctx *ctx) +{ + struct s6245_cmd_hdr cmd; + struct s6245_status_resp *resp = (struct s6245_status_resp *) rdbuf; + struct s6245_getextcounter_resp *resp2 = (struct s6245_getextcounter_resp *) rdbuf; + int ret, num = 0; + + cmd.cmd = cpu_to_le16(S6245_CMD_GETSTATUS); + cmd.len = cpu_to_le16(0); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return ret; + } + + INFO("Printer Status: 0x%02x (%s)\n", resp->hdr.status, + status_str(resp->hdr.status)); + if (resp->hdr.status == ERROR_PRINTER) { + if(resp->hdr.error == ERROR_NONE) + resp->hdr.error = resp->hdr.status; + INFO(" Error 0x%02x (%s) 0x%02x/0x%02x (%s)\n", + resp->hdr.error, + error_str(resp->hdr.error), + resp->hdr.printer_major, + resp->hdr.printer_minor, error_codes(resp->hdr.printer_major, resp->hdr.printer_minor)); + } + if (le16_to_cpu(resp->hdr.payload_len) != (sizeof(struct s6245_status_resp) - sizeof(struct s6245_status_hdr))) + return 0; + + INFO(" Print Counts:\n"); + INFO("\tSince Paper Changed:\t%08u\n", le32_to_cpu(resp->count_paper)); + INFO("\tLifetime:\t\t%08u\n", le32_to_cpu(resp->count_lifetime)); + INFO("\tMaintainence:\t\t%08u\n", le32_to_cpu(resp->count_maint)); + INFO("\tPrint Head:\t\t%08u\n", le32_to_cpu(resp->count_head)); + INFO(" Cutter Actuations:\t%08u\n", le32_to_cpu(resp->count_cutter)); + INFO(" Ribbon Remaining:\t%08u\n", le32_to_cpu(resp->count_ribbon_left)); + INFO("Bank 1: 0x%02x (%s) Job %03u @ %03u/%03u (%03u remaining)\n", + resp->bank1_status, bank_statuses(resp->bank1_status), + resp->bank1_printid, + le16_to_cpu(resp->bank1_finished), + le16_to_cpu(resp->bank1_specified), + le16_to_cpu(resp->bank1_remaining)); + + INFO("Bank 2: 0x%02x (%s) Job %03d @ %03d/%03d (%03d remaining)\n", + resp->bank2_status, bank_statuses(resp->bank1_status), + resp->bank2_printid, + le16_to_cpu(resp->bank2_finished), + le16_to_cpu(resp->bank2_specified), + le16_to_cpu(resp->bank2_remaining)); + + INFO("Tonecurve Status: 0x%02x (%s)\n", resp->tonecurve_status, tonecurve_statuses(resp->tonecurve_status)); + + /* Query Extended counters */ + cmd.cmd = cpu_to_le16(S6245_CMD_EXTCOUNTER); + cmd.len = cpu_to_le16(0); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp2), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return ret; + } + if (le16_to_cpu(resp2->hdr.payload_len) != (sizeof(struct s6245_getextcounter_resp) - sizeof(struct s6245_status_hdr))) + return 0; + + INFO("Lifetime Distance: %08d inches\n", le32_to_cpu(resp2->lifetime_distance)); + INFO("Maintainence Distance: %08d inches\n", le32_to_cpu(resp2->maint_distance)); + INFO("Head Distance: %08d inches\n", le32_to_cpu(resp2->head_distance)); + + return 0; +} + +static int get_fwinfo(struct shinkos6245_ctx *ctx) +{ + struct s6245_fwinfo_cmd cmd; + struct s6245_fwinfo_resp *resp = (struct s6245_fwinfo_resp *)rdbuf; + int num = 0; + int i; + + cmd.hdr.cmd = cpu_to_le16(S6245_CMD_FWINFO); + cmd.hdr.len = cpu_to_le16(1); + + INFO("FW Information:\n"); + + for (i = FWINFO_TARGET_MAIN_BOOT ; i <= FWINFO_TARGET_PRINT_TABLES ; i++) { + int ret; + cmd.target = i; + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command (%d)\n", cmd_names(cmd.hdr.cmd), ret); + continue; + } + + if (le16_to_cpu(resp->hdr.payload_len) != (sizeof(struct s6245_fwinfo_resp) - sizeof(struct s6245_status_hdr))) + continue; + + INFO(" %s\t ver %02x.%02x\n", fwinfo_targets(i), + resp->major, resp->minor); +#if 0 + INFO(" name: '%s'\n", resp->name); + INFO(" type: '%s'\n", resp->type); + INFO(" date: '%s'\n", resp->date); + INFO(" version: %02x.%02x (CRC %04x)\n", resp->major, resp->minor, + le16_to_cpu(resp->checksum)); +#endif + } + return 0; +} + +static int get_errorlog(struct shinkos6245_ctx *ctx) +{ + struct s6245_errorlog_cmd cmd; + struct s6245_errorlog_resp *resp = (struct s6245_errorlog_resp *) rdbuf; + int num = 0; + int i = 0; + + cmd.hdr.cmd = cpu_to_le16(S6245_CMD_ERRORLOG); + cmd.hdr.len = cpu_to_le16(2); + + do { + int ret; + cmd.index = i; + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command (%d)\n", cmd_names(cmd.hdr.cmd), ret); + return ret; + } + + if (le16_to_cpu(resp->hdr.payload_len) != (sizeof(struct s6245_errorlog_resp) - sizeof(struct s6245_status_hdr))) + return -2; + + INFO("Stored Error ID %d:\n", i); + INFO(" %04d-%02d-%02d %02d:%02d:%02d @ %08u prints : 0x%02x/0x%02x (%s)\n", + resp->time_year + 2000, resp->time_month, resp->time_day, + resp->time_hour, resp->time_min, resp->time_sec, + le32_to_cpu(resp->print_counter), + resp->error_major, resp->error_minor, + error_codes(resp->error_major, resp->error_minor)); + INFO(" Temp: %02d/%02d Hum: %02d\n", + resp->printer_thermistor, resp->head_thermistor, resp->printer_humidity); + } while (++i < le16_to_cpu(resp->error_count)); + + return 0; +} + +static int get_mediainfo(struct shinkos6245_ctx *ctx) +{ + struct s6245_cmd_hdr cmd; + struct s6245_mediainfo_resp *resp = (struct s6245_mediainfo_resp *) rdbuf; + int ret, num = 0; + int i; + + cmd.cmd = cpu_to_le16(S6245_CMD_MEDIAINFO); + cmd.len = cpu_to_le16(0); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return ret; + } + + if (le16_to_cpu(resp->hdr.payload_len) != (sizeof(struct s6245_mediainfo_resp) - sizeof(struct s6245_status_hdr))) + return -2; + + INFO("Supported Media Information: %d entries:\n", resp->count); + for (i = 0 ; i < resp->count ; i++) { + INFO(" %02d: C 0x%02x (%s), %04dx%04d, P 0x%02x (%s)\n", i, + resp->items[i].media_code, print_medias(resp->items[i].media_code), + le16_to_cpu(resp->items[i].columns), + le16_to_cpu(resp->items[i].rows), + resp->items[i].print_method, print_methods(resp->items[i].print_method)); + } + return 0; +} + +static int cancel_job(struct shinkos6245_ctx *ctx, char *str) +{ + struct s6245_cancel_cmd cmd; + struct s6245_status_hdr *resp = (struct s6245_status_hdr *) rdbuf; + int ret, num = 0; + + if (!str) + return -1; + + cmd.id = atoi(str); + + cmd.hdr.cmd = cpu_to_le16(S6245_CMD_CANCELJOB); + cmd.hdr.len = cpu_to_le16(1); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); + return ret; + } + + return 0; +} + +static int flash_led(struct shinkos6245_ctx *ctx) +{ + struct s6245_cmd_hdr cmd; + struct s6245_status_hdr *resp = (struct s6245_status_hdr *) rdbuf; + int ret, num = 0; + + cmd.cmd = cpu_to_le16(S6245_CMD_FLASHLED); + cmd.len = cpu_to_le16(0); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return ret; + } + + return 0; +} + + +static int set_param(struct shinkos6245_ctx *ctx, int target, uint32_t param) +{ + struct s6245_setparam_cmd cmd; + struct s6245_status_hdr *resp = (struct s6245_status_hdr *) rdbuf; + int ret, num = 0; + + /* Set up command */ + cmd.target = target; + cmd.param = cpu_to_le32(param); + + cmd.hdr.cmd = cpu_to_le16(S6245_CMD_SETPARAM); + cmd.hdr.len = cpu_to_le16(sizeof(struct s6245_setparam_cmd)-sizeof(cmd.hdr)); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command (%d)\n", cmd_names(cmd.hdr.cmd), ret); + } + + return ret; +} + +static int reset_curve(struct shinkos6245_ctx *ctx, int target) +{ + struct s6245_reset_cmd cmd; + struct s6245_status_hdr *resp = (struct s6245_status_hdr *) rdbuf; + int ret, num = 0; + + cmd.target = target; + + cmd.hdr.cmd = cpu_to_le16(S6245_CMD_RESET); + cmd.hdr.len = cpu_to_le16(1); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); + return ret; + } + + return 0; +} + +static int get_tonecurve(struct shinkos6245_ctx *ctx, int type, char *fname) +{ + struct s6245_readtone_cmd cmd; + struct s6245_readtone_resp *resp = (struct s6245_readtone_resp *) rdbuf; + int ret, num = 0; + + uint8_t *data; + uint16_t curves[UPDATE_SIZE] = { 0 }; + + int i,j; + + cmd.target = type; + cmd.curveid = TONE_CURVE_ID; + + cmd.hdr.cmd = cpu_to_le16(S6245_CMD_READTONE); + cmd.hdr.len = cpu_to_le16(1); + + INFO("Dump %s Tone Curve to '%s'\n", tonecurve_statuses(type), fname); + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); + return ret; + } + + resp->total_size = le16_to_cpu(resp->total_size); + + data = malloc(resp->total_size * 2); + if (!data) { + ERROR("Memory Allocation Failure!\n"); + return -1; + } + + i = 0; + while (i < resp->total_size) { + ret = read_data(ctx->dev, ctx->endp_up, + data + i, + resp->total_size * 2 - i, + &num); + if (ret < 0) + goto done; + i += num; + } + + i = j = 0; + while (i < resp->total_size) { + memcpy(curves + j, data + i+2, data[i+1]); + j += data[i+1] / 2; + i += data[i+1] + 2; + } + + /* Open file and write it out */ + { + int tc_fd = open(fname, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); + if (tc_fd < 0) { + ret = -1; + goto done; + } + + for (i = 0 ; i < UPDATE_SIZE; i++) { + /* Byteswap appropriately */ + curves[i] = cpu_to_be16(le16_to_cpu(curves[i])); + } + write(tc_fd, curves, UPDATE_SIZE * sizeof(uint16_t)); + close(tc_fd); + } + +done: + free(data); + return ret; +} + +static int set_tonecurve(struct shinkos6245_ctx *ctx, int target, char *fname) +{ + struct s6245_update_cmd cmd; + struct s6245_status_hdr *resp = (struct s6245_status_hdr *) rdbuf; + int ret, num = 0; + + INFO("Set %s Tone Curve from '%s'\n", update_targets(target), fname); + + uint16_t *data = malloc(UPDATE_SIZE * sizeof(uint16_t)); + if (!data) { + ERROR("Memory Allocation Failure!\n"); + return -1; + } + + /* Read in file */ + int tc_fd = open(fname, O_RDONLY); + if (tc_fd < 0) { + ret = -1; + goto done; + } + if (read(tc_fd, data, UPDATE_SIZE * sizeof(uint16_t)) != (UPDATE_SIZE * sizeof(uint16_t))) { + ret = -2; + goto done; + } + close(tc_fd); + /* Byteswap data to local CPU.. */ + for (ret = 0; ret < UPDATE_SIZE ; ret++) { + data[ret] = be16_to_cpu(data[ret]); + } + + /* Set up command */ + cmd.target = target; + cmd.reserved[0] = cmd.reserved[1] = cmd.reserved[2] = 0; + cmd.reset = 0; + cmd.size = cpu_to_le32(UPDATE_SIZE * sizeof(uint16_t)); + + cmd.hdr.cmd = cpu_to_le16(S6245_CMD_UPDATE); + cmd.hdr.len = cpu_to_le16(sizeof(struct s6245_update_cmd)-sizeof(cmd.hdr)); + + /* Byteswap data to format printer is expecting.. */ + for (ret = 0; ret < UPDATE_SIZE ; ret++) { + data[ret] = cpu_to_le16(data[ret]); + } + + if ((ret = s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); + goto done; + } + + /* Sent transfer */ + if ((ret = send_data(ctx->dev, ctx->endp_down, + (uint8_t *) data, UPDATE_SIZE * sizeof(uint16_t)))) { + goto done; + } + +done: + free(data); + + return ret; +} + +static void shinkos6245_cmdline(void) +{ + DEBUG("\t\t[ -c filename ] # Get user/NV tone curve\n"); + DEBUG("\t\t[ -C filename ] # Set user/NV tone curve\n"); + DEBUG("\t\t[ -e ] # Query error log\n"); + DEBUG("\t\t[ -F ] # Flash Printer LED\n"); + DEBUG("\t\t[ -i ] # Query printer info\n"); + DEBUG("\t\t[ -k num ] # Set sleep time (5-240 minutes)\n"); + DEBUG("\t\t[ -l filename ] # Get current tone curve\n"); + DEBUG("\t\t[ -L filename ] # Set current tone curve\n"); + DEBUG("\t\t[ -m ] # Query media\n"); + DEBUG("\t\t[ -r ] # Reset user/NV tone curve\n"); + DEBUG("\t\t[ -R ] # Reset printer to factory defaults\n"); + DEBUG("\t\t[ -s ] # Query status\n"); + DEBUG("\t\t[ -X jobid ] # Abort a printjob\n"); +} + +int shinkos6245_cmdline_arg(void *vctx, int argc, char **argv) +{ + struct shinkos6245_ctx *ctx = vctx; + int i, j = 0; + + if (!ctx) + return -1; + + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "c:C:eFik:l:L:mr:R:sX:")) >= 0) { + switch(i) { + GETOPT_PROCESS_GLOBAL + case 'c': + j = get_tonecurve(ctx, TONECURVE_USER, optarg); + break; + case 'C': + j = set_tonecurve(ctx, TONECURVE_USER, optarg); + break; + case 'e': + j = get_errorlog(ctx); + break; + case 'F': + j = flash_led(ctx); + break; + case 'i': + j = get_fwinfo(ctx); + break; + case 'k': { + uint32_t i = atoi(optarg); + if (i < 5) + i = 0; + else if (i < 15) + i = 1; + else if (i < 30) + i = 2; + else if (i < 60) + i = 3; + else if (i < 120) + i = 4; + else if (i < 240) + i = 5; + else + i = 5; + + j = set_param(ctx, PARAM_SLEEP_TIME, i); + break; + } + case 'l': + j = get_tonecurve(ctx, TONECURVE_CURRENT, optarg); + break; + case 'L': + j = set_tonecurve(ctx, TONECURVE_CURRENT, optarg); + break; + case 'm': + j = get_mediainfo(ctx); + break; + case 'r': + j = reset_curve(ctx, RESET_TONE_CURVE); + break; + case 'R': + j = reset_curve(ctx, RESET_PRINTER); + break; + case 's': + j = get_status(ctx); + break; + case 'X': + j = cancel_job(ctx, optarg); + break; + default: + break; /* Ignore completely */ + } + + if (j) return j; + } + + return 0; +} + +static void *shinkos6245_init(void) +{ + struct shinkos6245_ctx *ctx = malloc(sizeof(struct shinkos6245_ctx)); + if (!ctx) { + ERROR("Memory Allocation Failure!\n"); + return NULL; + } + memset(ctx, 0, sizeof(struct shinkos6245_ctx)); + + return ctx; +} + +static void shinkos6245_attach(void *vctx, struct libusb_device_handle *dev, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +{ + struct shinkos6245_ctx *ctx = vctx; + struct libusb_device *device; + struct libusb_device_descriptor desc; + + ctx->dev = dev; + ctx->endp_up = endp_up; + ctx->endp_down = endp_down; + + device = libusb_get_device(dev); + libusb_get_device_descriptor(device, &desc); + + ctx->type = lookup_printer_type(&shinkos6245_backend, + desc.idVendor, desc.idProduct); + + /* Ensure jobid is sane */ + ctx->jobid = (jobid & 0x7f) + 1; +} + +static void shinkos6245_teardown(void *vctx) { + struct shinkos6245_ctx *ctx = vctx; + + if (!ctx) + return; + + if (ctx->databuf) + free(ctx->databuf); + + free(ctx); +} + +static int shinkos6245_read_parse(void *vctx, int data_fd) { + struct shinkos6245_ctx *ctx = vctx; + int ret; + uint8_t tmpbuf[4]; + + if (!ctx) + return CUPS_BACKEND_FAILED; + + /* Read in then validate header */ + ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); + if (ret < 0 || ret != sizeof(ctx->hdr)) { + if (ret == 0) + return CUPS_BACKEND_CANCEL; + ERROR("Read failed (%d/%d/%d)\n", + ret, 0, (int)sizeof(ctx->hdr)); + perror("ERROR: Read failed"); + return ret; + } + + if (le32_to_cpu(ctx->hdr.len1) != 0x10 || + le32_to_cpu(ctx->hdr.len2) != 0x64 || + le32_to_cpu(ctx->hdr.dpi) != 300) { + ERROR("Unrecognized header data format!\n"); + return CUPS_BACKEND_CANCEL; + } + + if (le32_to_cpu(ctx->hdr.model) != 6245) { + ERROR("Unrecognized printer (%d)!\n", le32_to_cpu(ctx->hdr.model)); + + return CUPS_BACKEND_CANCEL; + } + + if (ctx->databuf) { + free(ctx->databuf); + ctx->databuf = NULL; + } + + ctx->datalen = le32_to_cpu(ctx->hdr.rows) * le32_to_cpu(ctx->hdr.columns) * 3; + ctx->databuf = malloc(ctx->datalen); + if (!ctx->databuf) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_FAILED; + } + + { + int remain = ctx->datalen; + uint8_t *ptr = ctx->databuf; + do { + ret = read(data_fd, ptr, remain); + if (ret < 0) { + ERROR("Read failed (%d/%d/%d)\n", + ret, remain, ctx->datalen); + perror("ERROR: Read failed"); + return ret; + } + ptr += ret; + remain -= ret; + } while (remain); + } + + /* Make sure footer is sane too */ + ret = read(data_fd, tmpbuf, 4); + if (ret != 4) { + ERROR("Read failed (%d/%d/%d)\n", + ret, 4, 4); + perror("ERROR: Read failed"); + return ret; + } + if (tmpbuf[0] != 0x04 || + tmpbuf[1] != 0x03 || + tmpbuf[2] != 0x02 || + tmpbuf[3] != 0x01) { + ERROR("Unrecognized footer data format!\n"); + return CUPS_BACKEND_FAILED; + } + + return CUPS_BACKEND_OK; +} + +static int shinkos6245_main_loop(void *vctx, int copies) { + struct shinkos6245_ctx *ctx = vctx; + + int ret, num; + uint8_t cmdbuf[CMDBUF_LEN]; + uint8_t rdbuf2[READBACK_LEN]; + + int i, last_state = -1, state = S_IDLE; + uint8_t mcut; + + struct s6245_cmd_hdr *cmd = (struct s6245_cmd_hdr *) cmdbuf;; + struct s6245_print_cmd *print = (struct s6245_print_cmd *) cmdbuf; + struct s6245_status_resp *sts = (struct s6245_status_resp *) rdbuf; + struct s6245_mediainfo_resp *media = (struct s6245_mediainfo_resp *) rdbuf; + + /* Cap copies */ + // XXX 120 for 8x10 media, 100 for 8x12 media (S6245) + // 250 for 8x12, 300 for 8x10 (Kodak 8810) + if (copies > 120) + copies = 120; + + /* Set up mcut */ + switch (le32_to_cpu(ctx->hdr.media)) { + case MEDIA_8x4_2: + case MEDIA_8x5_2: + case MEDIA_8x6_2: + mcut = PRINT_METHOD_COMBO_2; + break; + case MEDIA_8x4_3: + mcut = PRINT_METHOD_COMBO_3; + break; + default: + mcut = PRINT_METHOD_STD; + } + // XXX what about mcut |= PRINT_METHOD_DISABLE_ERR; + + /* Send Media Query */ + memset(cmdbuf, 0, CMDBUF_LEN); + cmd->cmd = cpu_to_le16(S6245_CMD_MEDIAINFO); + cmd->len = cpu_to_le16(0); + + if ((ret = s6245_do_cmd(ctx, + cmdbuf, sizeof(*cmd), + sizeof(*media), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd->cmd)); + return CUPS_BACKEND_FAILED; + } + + if (le16_to_cpu(media->hdr.payload_len) != (sizeof(struct s6245_mediainfo_resp) - sizeof(struct s6245_status_hdr))) + return CUPS_BACKEND_FAILED; + + /* Validate print sizes */ + for (i = 0; i < media->count ; i++) { + /* Look for matching media */ + if (le16_to_cpu(media->items[i].columns) == cpu_to_le16(le32_to_cpu(ctx->hdr.columns)) && + le16_to_cpu(media->items[i].rows) == cpu_to_le16(le32_to_cpu(ctx->hdr.rows))) + break; + } + if (i == media->count) { + ERROR("Incorrect media loaded for print!\n"); + return CUPS_BACKEND_HOLD; + } + + /* Send Set Time */ + { + struct s6245_settime_cmd *stime = (struct s6245_settime_cmd *)cmdbuf; + time_t now = time(NULL); + struct tm *cur = localtime(&now); + + memset(cmdbuf, 0, CMDBUF_LEN); + cmd->cmd = cpu_to_le16(S6245_CMD_SETTIME); + cmd->len = cpu_to_le16(0); + stime->enable = 1; + stime->second = cur->tm_sec; + stime->minute = cur->tm_min; + stime->hour = cur->tm_hour; + stime->day = cur->tm_mday; + stime->month = cur->tm_mon; + stime->year = cur->tm_year + 1900 - 2000; + + if ((ret = s6245_do_cmd(ctx, + cmdbuf, sizeof(*stime), + sizeof(struct s6245_status_hdr), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(stime->hdr.cmd)); + return CUPS_BACKEND_FAILED; + } + if (sts->hdr.result != RESULT_SUCCESS) + goto printer_error; + } + + // XXX check copies against remaining media! + +top: + if (state != last_state) { + if (dyesub_debug) + DEBUG("last_state %d new %d\n", last_state, state); + } + + /* Send Status Query */ + memset(cmdbuf, 0, CMDBUF_LEN); + cmd->cmd = cpu_to_le16(S6245_CMD_GETSTATUS); + cmd->len = cpu_to_le16(0); + + if ((ret = s6245_do_cmd(ctx, + cmdbuf, sizeof(*cmd), + sizeof(struct s6245_status_hdr), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd->cmd)); + return CUPS_BACKEND_FAILED; + } + + if (memcmp(rdbuf, rdbuf2, READBACK_LEN)) { + memcpy(rdbuf2, rdbuf, READBACK_LEN); + + INFO("Printer Status: 0x%02x (%s)\n", + sts->hdr.status, status_str(sts->hdr.status)); + if (sts->hdr.result != RESULT_SUCCESS) + goto printer_error; + if (sts->hdr.error == ERROR_PRINTER) + goto printer_error; + } else if (state == last_state) { + sleep(1); + goto top; + } + last_state = state; + + fflush(stderr); + + switch (state) { + case S_IDLE: + INFO("Waiting for printer idle\n"); + /* If either bank is free, continue */ + if (sts->bank1_status == BANK_STATUS_FREE || + sts->bank2_status == BANK_STATUS_FREE) + state = S_PRINTER_READY_CMD; + + break; + case S_PRINTER_READY_CMD: + // XXX send "get eeprom backup command" + + INFO("Initiating print job (internal id %d)\n", ctx->jobid); + + memset(cmdbuf, 0, CMDBUF_LEN); + print->hdr.cmd = cpu_to_le16(S6245_CMD_PRINTJOB); + print->hdr.len = cpu_to_le16(sizeof (*print) - sizeof(*cmd)); + + print->id = ctx->jobid; + print->count = cpu_to_le16(copies); + print->columns = cpu_to_le16(le32_to_cpu(ctx->hdr.columns)); + print->rows = cpu_to_le16(le32_to_cpu(ctx->hdr.rows)); + print->mode = le32_to_cpu(ctx->hdr.oc_mode); + print->method = mcut; + + if ((ret = s6245_do_cmd(ctx, + cmdbuf, sizeof(*print), + sizeof(struct s6245_status_hdr), + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(print->hdr.cmd)); + return ret; + } + + if (sts->hdr.result != RESULT_SUCCESS) { + if (sts->hdr.error == ERROR_BUFFER_FULL) { + INFO("Printer Buffers full, retrying\n"); + break; + } else if ((sts->hdr.status & 0xf0) == 0x30 || sts->hdr.status == 0x21) { + INFO("Printer busy (%s), retrying\n", status_str(sts->hdr.status)); + break; + } else if (sts->hdr.status != ERROR_NONE) + goto printer_error; + } + + INFO("Sending image data to printer\n"); + if ((ret = send_data(ctx->dev, ctx->endp_down, + ctx->databuf, ctx->datalen))) + return CUPS_BACKEND_FAILED; + + INFO("Waiting for printer to acknowledge completion\n"); + sleep(1); + state = S_PRINTER_SENT_DATA; + break; + case S_PRINTER_SENT_DATA: + if (fast_return) { + INFO("Fast return mode enabled.\n"); + state = S_FINISHED; + } else if (sts->hdr.status == STATUS_READY) { + state = S_FINISHED; + } + break; + default: + break; + }; + + if (state != S_FINISHED) + goto top; + + INFO("Print complete\n"); + + return CUPS_BACKEND_OK; + +printer_error: + ERROR("Printer reported error: %#x (%s) status: %#x (%s) -> %#x.%#x (%s)\n", + sts->hdr.error, + error_str(sts->hdr.error), + sts->hdr.status, + status_str(sts->hdr.status), + sts->hdr.printer_major, sts->hdr.printer_minor, + error_codes(sts->hdr.printer_major, sts->hdr.printer_minor)); + return CUPS_BACKEND_FAILED; +} + +static int shinkos6245_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len) +{ + struct s6245_cmd_hdr cmd; + struct s6245_getserial_resp *resp = (struct s6245_getserial_resp*) rdbuf; + int ret, num = 0; + + struct shinkos6245_ctx ctx = { + .dev = dev, + .endp_up = endp_up, + .endp_down = endp_down, + }; + + cmd.cmd = cpu_to_le16(S6245_CMD_GETSERIAL); + cmd.len = cpu_to_le16(0); + + if ((ret = s6245_do_cmd(&ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp) - 1, + &num)) < 0) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return ret; + } + + /* Null-terminate */ + resp->hdr.payload_len = le16_to_cpu(resp->hdr.payload_len); + if (resp->hdr.payload_len > 23) + resp->hdr.payload_len = 23; + resp->data[resp->hdr.payload_len] = 0; + strncpy(buf, (char*)resp->data, buf_len); + buf[buf_len-1] = 0; /* ensure it's null terminated */ + + return CUPS_BACKEND_OK; +} + +/* Exported */ +#define USB_VID_SHINKO 0x10CE +#define USB_PID_SHINKO_S6245 0x001D + +struct dyesub_backend shinkos6245_backend = { + .name = "Shinko/Sinfonia CHC-S6245", + .version = "0.04WIP", + .uri_prefix = "shinkos6245", + .cmdline_usage = shinkos6245_cmdline, + .cmdline_arg = shinkos6245_cmdline_arg, + .init = shinkos6245_init, + .attach = shinkos6245_attach, + .teardown = shinkos6245_teardown, + .read_parse = shinkos6245_read_parse, + .main_loop = shinkos6245_main_loop, + .query_serno = shinkos6245_query_serno, + .devices = { + { USB_VID_SHINKO, USB_PID_SHINKO_S6245, P_SHINKO_S6245, ""}, + { 0, 0, 0, ""} + } +}; + +/* CHC-S6245 data format + + Spool file consists of an 116-byte header, followed by RGB-packed data, + followed by a 4-byte footer. Header appears to consist of a series of + 4-byte Little Endian words. + + 10 00 00 00 MM MM 00 00 01 00 00 00 01 00 00 00 MM == Model (ie 6245d) + 64 00 00 00 00 00 00 00 TT 00 00 00 00 00 00 00 TT == 0x20 8x4, 0x21 8x5, 0x22 8x6, 0x23 8x8, 0x10 8x10, 0x11 8x12 + 00 00 00 00 00 00 00 00 XX 00 00 00 00 00 00 00 XX == 0x03 matte, 0x02 glossy, 0x01 no coat + 00 00 00 00 WW WW 00 00 HH HH 00 00 NN 00 00 00 WW/HH Width, Height (LE), NN == Copies + 00 00 00 00 00 00 00 00 00 00 00 00 ce ff ff ff + 00 00 00 00 ce ff ff ff QQ QQ 00 00 ce ff ff ff QQ == DPI (300) + 00 00 00 00 ce ff ff ff 00 00 00 00 00 00 00 00 + 00 00 00 00 + + [[Packed RGB payload of WW*HH*3 bytes]] + + 04 03 02 01 [[ footer ]] + +*/ diff --git a/src/cups/sony_updr150_print.c b/src/cups/sony_updr150_print.c index 5df9742..0121443 100644 --- a/src/cups/sony_updr150_print.c +++ b/src/cups/sony_updr150_print.c @@ -35,25 +35,27 @@ #include <fcntl.h> #include <signal.h> +#define BACKEND updr150_backend + #include "backend_common.h" /* Exported */ #define USB_VID_SONY 0x054C #define USB_PID_SONY_UPDR150 0x01E8 #define USB_PID_SONY_UPDR200 0x035F -#define USB_PID_SONY_UPCR10 1234 +#define USB_PID_SONY_UPCR10 0x0226 /* Private data stucture */ struct updr150_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; uint8_t *databuf; int datalen; uint32_t copies_offset; - uint8_t type; }; static void* updr150_init(void) @@ -82,11 +84,9 @@ static void updr150_attach(void *vctx, struct libusb_device_handle *dev, device = libusb_get_device(dev); libusb_get_device_descriptor(device, &desc); - if (desc.idProduct == USB_PID_SONY_UPDR150 || - desc.idProduct == USB_PID_SONY_UPDR200) - ctx->type = P_SONY_UPDR150; - else - ctx->type = P_SONY_UPCR10; // XXX + + ctx->type = lookup_printer_type(&updr150_backend, + desc.idVendor, desc.idProduct); ctx->copies_offset = 0; } @@ -255,10 +255,30 @@ top: return CUPS_BACKEND_OK; } +static int updr150_cmdline_arg(void *vctx, int argc, char **argv) +{ + struct updr150_ctx *ctx = vctx; + int i, j = 0; + + if (!ctx) + return -1; + + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL)) >= 0) { + switch(i) { + GETOPT_PROCESS_GLOBAL + } + + if (j) return j; + } + + return 0; +} + struct dyesub_backend updr150_backend = { .name = "Sony UP-DR150/UP-DR200/UP-CR10", - .version = "0.17", + .version = "0.18", .uri_prefix = "sonyupdr150", + .cmdline_arg = updr150_cmdline_arg, .init = updr150_init, .attach = updr150_attach, .teardown = updr150_teardown, diff --git a/src/cups/test-ppds b/src/cups/test-ppds index 7dd004b..542fd27 100644..100755 --- a/src/cups/test-ppds +++ b/src/cups/test-ppds @@ -10,7 +10,7 @@ make EXTRA_GENPPD_OPTS='-b -Z' ppd-clean ppd-global ppd-nls ppd-nonls # Also, a number of our media sizes aren't named correctly, but we'll # accept those issues rather than cluttering the namespace further and/or # changing tag names. -cupstestppdopts='-I profiles -W sizes' +cupstestppdopts='-I profiles -W sizes -I filters' ppd_count=`find ppd \( -name '*.ppd.gz' -o -name '*.ppd' \) -print | wc -l` diff --git a/src/cups/test-rastertogutenprint.in b/src/cups/test-rastertogutenprint.in index 373f127..0574704 100755 --- a/src/cups/test-rastertogutenprint.in +++ b/src/cups/test-rastertogutenprint.in @@ -23,12 +23,15 @@ fi single='' all_models='' verbose='' +valgrind=0 make_ppds=1 md5dir='' outdir='' cupsargs='' postscript='' npages=3 +enable_static='@ENABLE_STATIC@' +enable_shared='@ENABLE_SHARED@' usage() { echo "Usage: test-rastertogutenprint [-s] [-v|--valgrind]" @@ -40,7 +43,7 @@ set_args() { case "$1" in -s) single=1 ;; -h*|--h*) usage ;; - -v|--valgrind) valopts='--tool=memcheck' ; valgrind=`expr $valgrind + 1` ;; + -v|--valgrind) valopts='--tool=memcheck' ; valgrind=$((valgrind + 1)) ;; -c|--cachegrind) valopts='--tool=cachegrind'; valgrind=4 ;; -g|--gdb-attach) valopts='--gdb-attach=yes' ;; -V|--verbose) verbose=1 ;; @@ -59,6 +62,12 @@ set_args() { set_args `getopt hvcgsVnO:m:o:p: "$@"` +if [ "$valgrind" -gt 0 -a "$enable_shared" != "no" ] ; then + echo 'Valgrind is not compatible with --enable-shared in tree.' 1>&2 + echo 'Please use autogen.sh --disable-shared.' 1>&2 + exit 1 +fi + if [ -n "$outdir" -a ! -d "$outdir" ] ; then mkdir -p "$outdir" fi @@ -136,10 +145,10 @@ is_duplicate() { run_rastertogp() { case "$valgrind" in - 1) valgrind $valopts -q --num-callers=50 --leak-check=yes --error-limit=no ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; - 2) valgrind $valopts --num-callers=50 --leak-resolution=high --leak-check=yes --error-limit=no ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; - 3) valgrind $valopts --error-limit=no --num-callers=50 --show-reachable=yes --leak-resolution=high --leak-check=yes ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; - 4) valgrind $valopts ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; + 1) valgrind $valopts -q --log-fd=3 --num-callers=50 --leak-check=yes --error-limit=no ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; + 2) valgrind $valopts --log-fd=3 --num-callers=50 --leak-resolution=high --leak-check=yes --error-limit=no ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; + 3) valgrind $valopts --log-fd=3 --error-limit=no --num-callers=50 --show-reachable=yes --leak-resolution=high --leak-check=yes ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; + 4) valgrind $valopts --log-fd=3 ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; 5) cat ;; *) ./rastertogutenprint.$version 1 1 1 1 "$cupsargs" ;; esac @@ -241,17 +250,17 @@ if [ -d ppd/C ] ; then PPD=$f export PPD if [ -x "$cupsdir/cgpdftoraster" ] ; then - output="`($cupsdir/cgpdftoraster 1 1 1 1 $pages < $sdir/../../doc/gutenprint-users-manual.pdf 2>/dev/null | run_rastertogp | do_output) 2>&1`" + output="`($cupsdir/cgpdftoraster 1 1 1 1 $pages < $sdir/../../doc/gutenprint-users-manual.pdf 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" elif [ -f "$tfile" -a -x "$cupsdir/gstoraster" ] ; then - output="`($cupsdir/gstoraster 1 1 1 1 \"$cupsargs\" < $tfile 2>/dev/null | run_rastertogp | do_output) 2>&1`" + output="`($cupsdir/gstoraster 1 1 1 1 \"$cupsargs\" < $tfile 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" elif [ -f "$tfile" ] ; then - output="`($cupsdir/pstops 1 1 1 1 \"$cupsargs\" < $tfile 2>/dev/null | $cupsdir/pstoraster 2>/dev/null | run_rastertogp | do_output) 2>&1`" + output="`($cupsdir/pstops 1 1 1 1 \"$cupsargs\" < $tfile 2>/dev/null | $cupsdir/pstoraster 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" elif [ -x "$cupsdir/pstoraster" ] ; then - output="`($cupsdir/pdftops 1 1 1 1 \"$pages$cupsargs\" < $sdir/../../doc/gutenprint-users-manual.pdf 2>/dev/null | $cupsdir/pstops 1 1 1 1 \"$pages$cupsargs\" 2>/dev/null | $cupsdir/pstoraster 2>/dev/null | run_rastertogp | do_output) 2>&1`" + output="`($cupsdir/pdftops 1 1 1 1 \"$pages$cupsargs\" < $sdir/../../doc/gutenprint-users-manual.pdf 2>/dev/null | $cupsdir/pstops 1 1 1 1 \"$pages$cupsargs\" 2>/dev/null | $cupsdir/pstoraster 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" elif [ -x "$cupsdir/gstoraster" ] ; then - output="`($cupsdir/pdftops 1 1 1 1 \"$pages$cupsargs\" < $sdir/../../doc/gutenprint-users-manual.pdf 2>/dev/null | $cupsdir/gstoraster 1 1 1 1 \"$pages$cupsargs\" 2>/dev/null | run_rastertogp | do_output) 2>&1`" + output="`($cupsdir/pdftops 1 1 1 1 \"$pages$cupsargs\" < $sdir/../../doc/gutenprint-users-manual.pdf 2>/dev/null | $cupsdir/gstoraster 1 1 1 1 \"$pages$cupsargs\" 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" else - output="`($cupsdir/imagetoraster 1 1 1 1 \"$pages$cupsargs\" < calibrate.ppm 2>/dev/null | run_rastertogp | do_output) 2>&1`" + output="`($cupsdir/imagetoraster 1 1 1 1 \"$pages$cupsargs\" < calibrate.ppm 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2`" fi if [ $? -ne 0 ] ; then retval=1 |