From e50542121e724e851fc5d6c68bb773f80c0bc12c Mon Sep 17 00:00:00 2001 From: Didier Raboud Date: Tue, 25 Sep 2018 08:33:05 +0200 Subject: New upstream version 5.3.1 --- src/cups/COPYING | 3 +- src/cups/Makefile.am | 143 ++- src/cups/Makefile.in | 478 +++++--- src/cups/backend_canonselphy.c | 572 ++++++---- src/cups/backend_canonselphyneo.c | 261 ++++- src/cups/backend_citizencw01.c | 908 --------------- src/cups/backend_common.c | 774 ++++++++++--- src/cups/backend_common.h | 149 ++- src/cups/backend_dnpds40.c | 1355 ++++++++++++++++------ src/cups/backend_kodak1400.c | 208 ++-- src/cups/backend_kodak605.c | 257 +++-- src/cups/backend_kodak6800.c | 215 ++-- src/cups/backend_magicard.c | 1143 +++++++++++++++++++ src/cups/backend_mitsu70x.c | 1409 ++++++++++++++++------- src/cups/backend_mitsu9550.c | 955 ++++++++++++---- src/cups/backend_mitsud90.c | 1456 ++++++++++++++++++++++++ src/cups/backend_mitsup95d.c | 420 ++++--- src/cups/backend_shinkos1245.c | 224 ++-- src/cups/backend_shinkos2145.c | 331 +++--- src/cups/backend_shinkos6145.c | 421 ++++--- src/cups/backend_shinkos6245.c | 545 ++++----- src/cups/backend_sonyupdr150.c | 396 +++++-- src/cups/blacklist | 27 +- src/cups/command.types | 3 +- src/cups/commandtoepson.c | 3 +- src/cups/cups-calibrate.c | 3 +- src/cups/cups-genppd.c | 607 ++++++++++ src/cups/cups-genppdupdate.in | 47 +- src/cups/genppd.c | 910 +-------------- src/cups/genppd.h | 91 ++ src/cups/gutenprint.c | 253 +++++ src/cups/i18n.c | 3 +- src/cups/i18n.h | 3 +- src/cups/min-pagesize.in | 42 + src/cups/rastertogutenprint.c | 1748 +++++++++++++++++++++++++++++ src/cups/rastertoprinter.c | 1726 ---------------------------- src/cups/test-ppds | 56 - src/cups/test-ppds.in | 127 +++ src/cups/test-rastertogutenprint.check.in | 77 ++ src/cups/test-rastertogutenprint.in | 555 +++++---- 40 files changed, 12266 insertions(+), 6638 deletions(-) delete mode 100644 src/cups/backend_citizencw01.c create mode 100644 src/cups/backend_magicard.c create mode 100644 src/cups/backend_mitsud90.c create mode 100644 src/cups/cups-genppd.c create mode 100644 src/cups/genppd.h create mode 100644 src/cups/gutenprint.c create mode 100644 src/cups/min-pagesize.in create mode 100644 src/cups/rastertogutenprint.c delete mode 100644 src/cups/rastertoprinter.c delete mode 100755 src/cups/test-ppds create mode 100644 src/cups/test-ppds.in create mode 100644 src/cups/test-rastertogutenprint.check.in mode change 100755 => 100644 src/cups/test-rastertogutenprint.in (limited to 'src/cups') diff --git a/src/cups/COPYING b/src/cups/COPYING index 960fe74..b2579a3 100644 --- a/src/cups/COPYING +++ b/src/cups/COPYING @@ -1,8 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/src/cups/Makefile.am b/src/cups/Makefile.am index 4ef097d..2f70701 100644 --- a/src/cups/Makefile.am +++ b/src/cups/Makefile.am @@ -11,8 +11,7 @@ ## 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. +## along with this program. If not, see . ## Process this file with automake to produce Makefile.in. @@ -52,9 +51,11 @@ endif LOCAL_CPPFLAGS = $(GUTENPRINT_CFLAGS) $(CUPS_CFLAGS) -DBASE_VERSION=$(BASE_VERSION) -DSBINDIR=\"$(sbindir)/\" -STP_NONLS_ENV= STP_MODULE_PATH=$(top_builddir)/src/main/.libs:$(top_builddir)/src/main STP_DATA_PATH=$(top_srcdir)/src/xml +PPD_DIR=ppd -STP_ENV= $(STP_NONLS_ENV) STP_LOCALEDIR=$(top_srcdir)/src/cups/catalog +STP_NONLS_ENV= STP_MODULE_PATH=$(top_builddir)/src/main/.libs:$(top_builddir)/src/main STP_DATA_PATH=$(top_srcdir)/src/xml STP_LOCALEDIR= + +STP_ENV= $(STP_NONLS_ENV) STP_LOCALEDIR=$(top_builddir)/src/cups/$(PPD_DIR)catalog ## Programs if BUILD_TRANSLATED_CUPS_PPDS @@ -96,8 +97,14 @@ install-exec-hook: mv $(DESTDIR)$(pkglibdir)/backend/backend_gutenprint "$(DESTDIR)$(pkglibdir)/backend/gutenprint$(GUTENPRINT_MAJOR_VERSION)$(GUTENPRINT_MINOR_VERSION)+usb" endif -TESTS= test-ppds test-rastertogutenprint -noinst_SCRIPTS=test-rastertogutenprint +AM_TESTS_ENVIRONMENT=$(STP_ENV) +test-rastertogutenprint: min-pagesize +test-rastertogutenprint.check: test-rastertogutenprint +TESTS= test-ppds test-rastertogutenprint.check +noinst_SCRIPTS=test-ppds \ + test-rastertogutenprint \ + test-rastertogutenprint.check \ + min-pagesize endif if BUILD_GENPPD_STATIC @@ -114,23 +121,23 @@ commandtoepson_SOURCES = commandtoepson.c commandtoepson_LDADD = $(CUPS_LIBS) if BUILD_LIBUSB_BACKENDS -backend_gutenprint_SOURCES = backend_canonselphy.c backend_canonselphyneo.c backend_kodak1400.c backend_kodak6800.c backend_kodak605.c backend_shinkos2145.c backend_sonyupdr150.c backend_dnpds40.c backend_mitsu70x.c backend_citizencw01.c backend_mitsu9550.c backend_common.c backend_common.h backend_shinkos1245.c backend_shinkos6145.c backend_shinkos6245.c backend_mitsup95d.c +backend_gutenprint_SOURCES = backend_canonselphy.c backend_canonselphyneo.c backend_kodak1400.c backend_kodak6800.c backend_kodak605.c backend_shinkos2145.c backend_sonyupdr150.c backend_dnpds40.c backend_mitsu70x.c backend_mitsu9550.c backend_common.c backend_common.h backend_shinkos1245.c backend_shinkos6145.c backend_shinkos6245.c backend_mitsup95d.c backend_magicard.c backend_mitsud90.c backend_gutenprint_LDADD = $(LIBUSB_LIBS) $(LIBUSB_BACKEND_LIBDEPS) backend_gutenprint_CPPFLAGS = $(LIBUSB_CFLAGS) -DURI_PREFIX=\"gutenprint$(GUTENPRINT_MAJOR_VERSION)$(GUTENPRINT_MINOR_VERSION)+usb\" -DLIBUSB_PRE_1_0_10 endif -cups_genppd_@GUTENPRINT_RELEASE_VERSION@_SOURCES = genppd.c i18n.c i18n.h +cups_genppd_@GUTENPRINT_RELEASE_VERSION@_SOURCES = cups-genppd.c genppd.c genppd.h i18n.c i18n.h cups_genppd_@GUTENPRINT_RELEASE_VERSION@_CFLAGS = -DALL_LINGUAS='"$(ALL_LINGUAS)"' $(BUILD_SIMPLE_PPDS) $(TRANSLATE_PPDS) cups_genppd_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GENPPD_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ cups_genppd_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS = $(STATIC_LDOPTS) -gutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = genppd.c i18n.c i18n.h -gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS = -DCUPS_DRIVER_INTERFACE -DALL_LINGUAS='"$(ALL_LINGUAS)"' $(BUILD_SIMPLE_PPDS) $(TRANSLATE_PPDS) -gutenprint_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ +gutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = gutenprint.c genppd.c genppd.h i18n.c i18n.h +gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS = -DALL_LINGUAS='"$(ALL_LINGUAS)"' $(BUILD_SIMPLE_PPDS) $(TRANSLATE_PPDS) +gutenprint_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GENPPD_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ gutenprint_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS = $(STATIC_LDOPTS) -rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = rastertoprinter.c i18n.c i18n.h +rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = rastertogutenprint.c i18n.c i18n.h rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS = $(STATIC_LDOPTS) @@ -158,17 +165,17 @@ endif if BUILD_LIBUSB_BACKENDS INSTALL_BLACKLIST=install-blacklist install-blacklist: - $(mkdir_p) $(DESTDIR)$(cupsdata_blacklistdir) + $(MKDIR_P) $(DESTDIR)$(cupsdata_blacklistdir) $(INSTALL_DATA) $(srcdir)/blacklist $(DESTDIR)$(cupsdata_blacklistdir)/net.sf.gimp-print.usb-quirks endif install-data-local: $(INSTALL_DATA_LOCAL_DEPS) $(INSTALL_BLACKLIST) if test -n "$(CUPS_PKG)" -a -n "$(INSTALL_DATA_LOCAL_DEPS)" ; then \ - $(mkdir_p) $(DESTDIR)$(cups_modeldir); \ + $(MKDIR_P) $(DESTDIR)$(cups_modeldir); \ cd ppd ; \ for language in * ; do \ cd ..; \ - $(mkdir_p) $(DESTDIR)$(cups_modeldir)/$$language; \ + $(MKDIR_P) $(DESTDIR)$(cups_modeldir)/$$language; \ cd ppd/$$language; \ for ppdfile in * ; do \ (cd ../..; $(INSTALL_DATA) ppd/$$language/$$ppdfile $(DESTDIR)$(cups_modeldir)/$$language) ; \ @@ -176,10 +183,10 @@ install-data-local: $(INSTALL_DATA_LOCAL_DEPS) $(INSTALL_BLACKLIST) cd ..; \ done \ fi - $(mkdir_p) "$(DESTDIR)$(localedir)" + $(MKDIR_P) "$(DESTDIR)$(localedir)" for file in $(srcdir)/../../po/*.po; do \ lang=`basename $$file .po`; \ - $(mkdir_p) "$(DESTDIR)$(localedir)/$$lang"; \ + $(MKDIR_P) "$(DESTDIR)$(localedir)/$$lang"; \ $(INSTALL_DATA) $$file "$(DESTDIR)$(localedir)/$$lang/gutenprint_$$lang.po"; \ done @@ -208,13 +215,15 @@ install-data-hook: -rmdir $(DESTDIR)$(cupsexec_backenddir) -rmdir $(DESTDIR)$(cupsdata_blacklistdir) -rmdir $(DESTDIR)$(bindir) + -rmdir $(DESTDIR)$(sbindir) -rmdir $(DESTDIR)$(pkglibdir) -rmdir $(DESTDIR)$(pkgsysconfdir) -rmdir `dirname $(DESTDIR)$(pkgdatadir)` -rmdir `dirname $(DESTDIR)$(pkglibdir)` -rmdir `dirname $(DESTDIR)$(pkgsysconfdir)` -.PHONY: ppd ppd-stamp-pre ppd-stamp-nonls ppd-stamp-nls ppd-stamp-phony ppd-catalog-clean ppd-clean $(INSTALL_BLACKLIST) +.PHONY: ppd ppd-stamp-pre ppd-stamp-nonls ppd-stamp-nls ppd-stamp-phony \ + ppd-catalog-clean ppd-clean $(INSTALL_BLACKLIST) all-local: $(INSTALL_DATA_LOCAL_DEPS) @@ -228,76 +237,111 @@ ppd-stamp: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(top_srcdir)/src/xml/xml-st ppd-stamp-phony: $(PPD) ppd-catalog-clean: - $(RM) -rf catalog + $(RM) -rf $(PPD_DIR)catalog ppd-clean: - $(RM) -rf ppd + $(RM) -rf $(PPD_DIR) ppd-stamp-pre: ppd-catalog-clean ppd-clean ppd-catalog: ppd-catalog-clean - $(mkdir_p) catalog - for file in $(srcdir)/../../po/*.po; do \ + $(MKDIR_P) catalog + for file in $(top_srcdir)/po/*.po; do \ lang=`basename $$file .po`; \ - $(mkdir_p) "catalog/$$lang"; \ - $(INSTALL_DATA) $$file "catalog/$$lang/gutenprint_$$lang.po"; \ + $(MKDIR_P) "$(PPD_DIR)catalog/$$lang"; \ + $(INSTALL_DATA) $$file "$(PPD_DIR)catalog/$$lang/gutenprint_$$lang.po"; \ done ppd-nonls: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - $(mkdir_p) ppd/C + @echo "Non-localized PPDs:" + $(MKDIR_P) $(PPD_DIR)/C $(MAKE) ppd-catalog-clean - LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ $(EXTRA_GENPPD_OPTS) -l C -p ppd/C + LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l C -p $(PPD_DIR)/C @WHICH_PPDS@ ppd-nonls-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - $(mkdir_p) ppd/C + @echo "Non-localized PPDs (all):" + $(MKDIR_P) $(PPD_DIR)/C $(MAKE) ppd-catalog-clean - LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ $(EXTRA_GENPPD_OPTS) -l C -p ppd/C -a + LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l C -p $(PPD_DIR)/C -a + +ppd-nonls-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ + @echo "Non-localized PPDs (simplified):" + $(MKDIR_P) $(PPD_DIR)/C + $(MAKE) ppd-catalog-clean + LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l C -p $(PPD_DIR)/C -s ppd-global: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @echo "Global PPDs:" - $(mkdir_p) ppd/Global + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global @WHICH_PPDS@ $(MAKE) ppd-catalog-clean ppd-global-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - @echo "Global PPDs:" - $(mkdir_p) ppd/Global + @echo "Global PPDs (all):" + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global -a + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -a + $(MAKE) ppd-catalog-clean + +ppd-global-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ + @echo "Global PPDs (simplified):" + $(MKDIR_P) $(PPD_DIR)/Global + $(MAKE) ppd-catalog + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -s $(MAKE) ppd-catalog-clean ppd-global-ln: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @echo "Global PPDs (localized numbers for testing):" - $(mkdir_p) ppd/Global + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global -N + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -N @WHICH_PPDS@ $(MAKE) ppd-catalog-clean ppd-global-ln-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - @echo "Global PPDs (localized numbers for testing):" - $(mkdir_p) ppd/Global + @echo "Global PPDs (all, localized numbers for testing):" + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global -N -a + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -N -a + $(MAKE) ppd-catalog-clean + +ppd-global-ln-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ + @echo "Global PPDs (all, localized numbers for testing):" + $(MKDIR_P) $(PPD_DIR)/Global + $(MAKE) ppd-catalog + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -N -s $(MAKE) ppd-catalog-clean ppd-nls: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ ppd-nonls - $(mkdir_p) ppd + @echo "Localized PPD files:" + $(MKDIR_P) ppd + $(MAKE) ppd-catalog + for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ -L` ; do \ + $(MKDIR_P) $(PPD_DIR)/$$language ; \ + echo -n "$$language: " ; \ + LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l $$language -p $(PPD_DIR)/$$language; \ + done + $(MAKE) ppd-catalog-clean + +ppd-nls-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ ppd-nonls + @echo "Localized PPD files (all):" + $(MKDIR_P) ppd $(MAKE) ppd-catalog - for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ -L` ; do \ - $(mkdir_p) ppd/$$language ; \ + for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ -L` ; do \ + $(MKDIR_P) $(PPD_DIR)/$$language ; \ echo -n "$$language: " ; \ - LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p ppd/$$language; \ + LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p $(PPD_DIR)/$$language -a; \ done $(MAKE) ppd-catalog-clean -ppd-nls-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ ppd-nonls - $(mkdir_p) ppd +ppd-nls-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ ppd-nonls + @echo "Localized PPD files (simplified):" + $(MKDIR_P) ppd $(MAKE) ppd-catalog - for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ -L` ; do \ - $(mkdir_p) ppd/$$language ; \ + for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ -L` ; do \ + $(MKDIR_P) $(PPD_DIR)/$$language ; \ echo -n "$$language: " ; \ - LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p ppd/$$language -a; \ + LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p $(PPD_DIR)/$$language -s; \ done $(MAKE) ppd-catalog-clean @@ -319,7 +363,7 @@ clean-local: ppd-catalog-clean ppd-clean ## Clean CLEANFILES = ppd-stamp -DISTCLEANFILES = cups-genppdupdate +DISTCLEANFILES = cups-genppdupdate test-ppds MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = \ @@ -328,5 +372,4 @@ EXTRA_DIST = \ blacklist \ calibrate.ppm \ command.txt \ - command.types \ - test-ppds + command.types diff --git a/src/cups/Makefile.in b/src/cups/Makefile.in index 04419cb..605047b 100644 --- a/src/cups/Makefile.in +++ b/src/cups/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.13.4 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -22,7 +22,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -83,12 +93,6 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -DIST_COMMON = $(top_srcdir)/scripts/global.mk $(srcdir)/Makefile.in \ - $(srcdir)/Makefile.am $(srcdir)/Info.plist.in \ - $(srcdir)/cups-genppdupdate.in \ - $(srcdir)/test-rastertogutenprint.in \ - $(top_srcdir)/scripts/depcomp \ - $(top_srcdir)/scripts/test-driver COPYING README @BUILD_CUPS_1_2_TRUE@cupsexec_driver_PROGRAMS = gutenprint.@GUTENPRINT_RELEASE_VERSION@$(EXEEXT) @BUILD_CUPS_TRUE@bin_PROGRAMS = cups-calibrate$(EXEEXT) @BUILD_CUPS_TRUE@sbin_PROGRAMS = cups-genppd.@GUTENPRINT_RELEASE_VERSION@$(EXEEXT) @@ -99,21 +103,23 @@ DIST_COMMON = $(top_srcdir)/scripts/global.mk $(srcdir)/Makefile.in \ subdir = src/cups ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ - $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ - $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ - $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ - $(top_srcdir)/m4/stp.m4 $(top_srcdir)/m4/stp_cups.m4 \ - $(top_srcdir)/m4/stp_gimp.m4 $(top_srcdir)/m4/stp_option.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/stp.m4 \ + $(top_srcdir)/m4/stp_cups.m4 $(top_srcdir)/m4/stp_option.m4 \ $(top_srcdir)/m4/stp_release.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = Info.plist cups-genppdupdate \ - test-rastertogutenprint +CONFIG_CLEAN_FILES = Info.plist cups-genppdupdate test-ppds \ + min-pagesize test-rastertogutenprint \ + test-rastertogutenprint.check CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(cupsexec_backenddir)" \ @@ -128,9 +134,10 @@ am__backend_gutenprint_SOURCES_DIST = backend_canonselphy.c \ backend_canonselphyneo.c backend_kodak1400.c \ backend_kodak6800.c backend_kodak605.c backend_shinkos2145.c \ backend_sonyupdr150.c backend_dnpds40.c backend_mitsu70x.c \ - backend_citizencw01.c backend_mitsu9550.c backend_common.c \ - backend_common.h backend_shinkos1245.c backend_shinkos6145.c \ - backend_shinkos6245.c backend_mitsup95d.c + backend_mitsu9550.c backend_common.c backend_common.h \ + backend_shinkos1245.c backend_shinkos6145.c \ + backend_shinkos6245.c backend_mitsup95d.c backend_magicard.c \ + backend_mitsud90.c @BUILD_LIBUSB_BACKENDS_TRUE@am_backend_gutenprint_OBJECTS = backend_gutenprint-backend_canonselphy.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_canonselphyneo.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_kodak1400.$(OBJEXT) \ @@ -140,13 +147,14 @@ am__backend_gutenprint_SOURCES_DIST = backend_canonselphy.c \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_sonyupdr150.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_dnpds40.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_mitsu70x.$(OBJEXT) \ -@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_citizencw01.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_mitsu9550.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_common.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_shinkos1245.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_shinkos6145.$(OBJEXT) \ @BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_shinkos6245.$(OBJEXT) \ -@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_mitsup95d.$(OBJEXT) +@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_mitsup95d.$(OBJEXT) \ +@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_magicard.$(OBJEXT) \ +@BUILD_LIBUSB_BACKENDS_TRUE@ backend_gutenprint-backend_mitsud90.$(OBJEXT) backend_gutenprint_OBJECTS = $(am_backend_gutenprint_OBJECTS) am__DEPENDENCIES_1 = @BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_DEPENDENCIES = \ @@ -165,7 +173,7 @@ commandtoepson_DEPENDENCIES = $(am__DEPENDENCIES_1) am_cups_calibrate_OBJECTS = cups-calibrate.$(OBJEXT) cups_calibrate_OBJECTS = $(am_cups_calibrate_OBJECTS) cups_calibrate_DEPENDENCIES = -am_cups_genppd_@GUTENPRINT_RELEASE_VERSION@_OBJECTS = \ +am_cups_genppd_@GUTENPRINT_RELEASE_VERSION@_OBJECTS = cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.$(OBJEXT) \ cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.$(OBJEXT) \ cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.$(OBJEXT) cups_genppd_@GUTENPRINT_RELEASE_VERSION@_OBJECTS = \ @@ -178,19 +186,20 @@ cups_genppd_@GUTENPRINT_RELEASE_VERSION@_LINK = $(LIBTOOL) $(AM_V_lt) \ $(CFLAGS) $(cups_genppd_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS) \ $(LDFLAGS) -o $@ am_gutenprint_@GUTENPRINT_RELEASE_VERSION@_OBJECTS = \ + gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.$(OBJEXT) \ gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.$(OBJEXT) \ gutenprint_@GUTENPRINT_RELEASE_VERSION@-i18n.$(OBJEXT) gutenprint_@GUTENPRINT_RELEASE_VERSION@_OBJECTS = \ $(am_gutenprint_@GUTENPRINT_RELEASE_VERSION@_OBJECTS) gutenprint_@GUTENPRINT_RELEASE_VERSION@_DEPENDENCIES = \ - $(am__DEPENDENCIES_1) $(GUTENPRINT_LIBS) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(GUTENPRINT_LIBS) gutenprint_@GUTENPRINT_RELEASE_VERSION@_LINK = $(LIBTOOL) $(AM_V_lt) \ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ $(CCLD) $(gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) \ $(CFLAGS) $(gutenprint_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS) \ $(LDFLAGS) -o $@ am_rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_OBJECTS = \ - rastertoprinter.$(OBJEXT) i18n.$(OBJEXT) + rastertogutenprint.$(OBJEXT) i18n.$(OBJEXT) rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_OBJECTS = \ $(am_rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_OBJECTS) rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_DEPENDENCIES = \ @@ -242,7 +251,34 @@ am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/scripts/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = \ + ./$(DEPDIR)/backend_gutenprint-backend_canonselphy.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_canonselphyneo.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_common.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_dnpds40.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_kodak1400.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_kodak605.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_kodak6800.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_magicard.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_mitsu70x.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_mitsu9550.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_mitsud90.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_mitsup95d.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_shinkos1245.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_shinkos2145.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_shinkos6145.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_shinkos6245.Po \ + ./$(DEPDIR)/backend_gutenprint-backend_sonyupdr150.Po \ + ./$(DEPDIR)/commandtocanon.Po ./$(DEPDIR)/commandtoepson.Po \ + ./$(DEPDIR)/cups-calibrate.Po \ + ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Po \ + ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Po \ + ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.Po \ + ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Po \ + ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Po \ + ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-i18n.Po \ + ./$(DEPDIR)/i18n.Po ./$(DEPDIR)/rastertogutenprint.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -475,6 +511,13 @@ TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/scripts/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Info.plist.in $(srcdir)/Makefile.in \ + $(srcdir)/cups-genppdupdate.in $(srcdir)/min-pagesize.in \ + $(srcdir)/test-ppds.in \ + $(srcdir)/test-rastertogutenprint.check.in \ + $(srcdir)/test-rastertogutenprint.in \ + $(top_srcdir)/scripts/depcomp $(top_srcdir)/scripts/global.mk \ + $(top_srcdir)/scripts/test-driver COPYING README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkgdatadir = $(cups_conf_datadir) pkglibdir = $(cups_conf_serverbin) @@ -487,6 +530,8 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BASH = @BASH@ +BASHREAL = @BASHREAL@ BUILD_CUPS_PPDS = @BUILD_CUPS_PPDS@ BZIP2 = @BZIP2@ CC = @CC@ @@ -507,7 +552,6 @@ DB2PDF = @DB2PDF@ DB2PS = @DB2PS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ -DIALOG = @DIALOG@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ @@ -524,6 +568,7 @@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FIND = @FIND@ GENPPD_LIBS = @GENPPD_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GIMP2_CFLAGS = @GIMP2_CFLAGS@ GIMP2_LIBS = @GIMP2_LIBS@ GIMPTOOL2_CHECK = @GIMPTOOL2_CHECK@ @@ -586,10 +631,12 @@ LTALLOCA = @LTALLOCA@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAINTAINER_CFLAGS = @MAINTAINER_CFLAGS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MINIMAL_PRINTERS_TO_TEST = @MINIMAL_PRINTERS_TO_TEST@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ @@ -613,7 +660,6 @@ PKGROOT = @PKGROOT@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PLUG_IN_PATH = @PLUG_IN_PATH@ POSUB = @POSUB@ RANLIB = @RANLIB@ RELEASE_DATE = @RELEASE_DATE@ @@ -631,9 +677,11 @@ VERSION = @VERSION@ WHICH_PPDS = @WHICH_PPDS@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ XZ = @XZ@ YACC = @YACC@ YFLAGS = @YFLAGS@ +ZPAQ = @ZPAQ@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ @@ -701,7 +749,6 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(LOCAL_CPPFLAGS) $(GNUCFLAGS) -GUTENPRINTUI_LIBS = $(top_builddir)/src/gutenprintui/libgutenprintui.la empty = BASE_VERSION = \"@GUTENPRINT_BASE_VERSION@\" pkgsysconfdir = $(cups_conf_serverroot) @@ -713,8 +760,9 @@ cupsexec_filterdir = $(pkglibdir)/filter @CUPS_PPDS_AT_TOP_LEVEL_TRUE@cups_modeldir = $(pkgdatadir)/model/ @CUPS_PPDS_AT_TOP_LEVEL_TRUE@DONT_UNINSTALL_PPDS = true LOCAL_CPPFLAGS = $(GUTENPRINT_CFLAGS) $(CUPS_CFLAGS) -DBASE_VERSION=$(BASE_VERSION) -DSBINDIR=\"$(sbindir)/\" -STP_NONLS_ENV = STP_MODULE_PATH=$(top_builddir)/src/main/.libs:$(top_builddir)/src/main STP_DATA_PATH=$(top_srcdir)/src/xml -STP_ENV = $(STP_NONLS_ENV) STP_LOCALEDIR=$(top_srcdir)/src/cups/catalog +PPD_DIR = ppd +STP_NONLS_ENV = STP_MODULE_PATH=$(top_builddir)/src/main/.libs:$(top_builddir)/src/main STP_DATA_PATH=$(top_srcdir)/src/xml STP_LOCALEDIR= +STP_ENV = $(STP_NONLS_ENV) STP_LOCALEDIR=$(top_builddir)/src/cups/$(PPD_DIR)catalog @BUILD_GLOBALIZED_CUPS_PPDS_FALSE@@BUILD_TRANSLATED_CUPS_PPDS_TRUE@PPD = $(PPD_NLS_1) @BUILD_GLOBALIZED_CUPS_PPDS_TRUE@@BUILD_TRANSLATED_CUPS_PPDS_TRUE@PPD = ppd-global @BUILD_TRANSLATED_CUPS_PPDS_FALSE@PPD = ppd-nonls @@ -722,8 +770,13 @@ STP_ENV = $(STP_NONLS_ENV) STP_LOCALEDIR=$(top_srcdir)/src/cups/catalog @BUILD_TRANSLATED_CUPS_PPDS_TRUE@TRANSLATE_PPDS = -DCUPS_TRANSLATED_PPDS @BUILD_SIMPLIFIED_CUPS_PPDS_TRUE@BUILD_SIMPLE_PPDS = -DGENERATE_SIMPLIFIED_PPDS @BUILD_CUPS_TRUE@sbin_SCRIPTS = cups-genppdupdate -@BUILD_CUPS_TRUE@TESTS = test-ppds test-rastertogutenprint -@BUILD_CUPS_TRUE@noinst_SCRIPTS = test-rastertogutenprint +@BUILD_CUPS_TRUE@AM_TESTS_ENVIRONMENT = $(STP_ENV) +@BUILD_CUPS_TRUE@TESTS = test-ppds test-rastertogutenprint.check +@BUILD_CUPS_TRUE@noinst_SCRIPTS = test-ppds \ +@BUILD_CUPS_TRUE@ test-rastertogutenprint \ +@BUILD_CUPS_TRUE@ test-rastertogutenprint.check \ +@BUILD_CUPS_TRUE@ min-pagesize + @BUILD_GENPPD_STATIC_TRUE@STATIC_LDOPTS = -static -export-dynamic cups_calibrate_SOURCES = cups-calibrate.c cups_calibrate_LDADD = -lm @@ -731,18 +784,18 @@ commandtocanon_SOURCES = commandtocanon.c commandtocanon_LDADD = $(CUPS_LIBS) commandtoepson_SOURCES = commandtoepson.c commandtoepson_LDADD = $(CUPS_LIBS) -@BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_SOURCES = backend_canonselphy.c backend_canonselphyneo.c backend_kodak1400.c backend_kodak6800.c backend_kodak605.c backend_shinkos2145.c backend_sonyupdr150.c backend_dnpds40.c backend_mitsu70x.c backend_citizencw01.c backend_mitsu9550.c backend_common.c backend_common.h backend_shinkos1245.c backend_shinkos6145.c backend_shinkos6245.c backend_mitsup95d.c +@BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_SOURCES = backend_canonselphy.c backend_canonselphyneo.c backend_kodak1400.c backend_kodak6800.c backend_kodak605.c backend_shinkos2145.c backend_sonyupdr150.c backend_dnpds40.c backend_mitsu70x.c backend_mitsu9550.c backend_common.c backend_common.h backend_shinkos1245.c backend_shinkos6145.c backend_shinkos6245.c backend_mitsup95d.c backend_magicard.c backend_mitsud90.c @BUILD_LIBUSB_BACKENDS_TRUE@backend_gutenprint_LDADD = $(LIBUSB_LIBS) $(LIBUSB_BACKEND_LIBDEPS) @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 +cups_genppd_@GUTENPRINT_RELEASE_VERSION@_SOURCES = cups-genppd.c genppd.c genppd.h i18n.c i18n.h cups_genppd_@GUTENPRINT_RELEASE_VERSION@_CFLAGS = -DALL_LINGUAS='"$(ALL_LINGUAS)"' $(BUILD_SIMPLE_PPDS) $(TRANSLATE_PPDS) cups_genppd_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GENPPD_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ cups_genppd_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS = $(STATIC_LDOPTS) -gutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = genppd.c i18n.c i18n.h -gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS = -DCUPS_DRIVER_INTERFACE -DALL_LINGUAS='"$(ALL_LINGUAS)"' $(BUILD_SIMPLE_PPDS) $(TRANSLATE_PPDS) -gutenprint_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ +gutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = gutenprint.c genppd.c genppd.h i18n.c i18n.h +gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS = -DALL_LINGUAS='"$(ALL_LINGUAS)"' $(BUILD_SIMPLE_PPDS) $(TRANSLATE_PPDS) +gutenprint_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GENPPD_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ gutenprint_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS = $(STATIC_LDOPTS) -rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = rastertoprinter.c i18n.c i18n.h +rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_SOURCES = rastertogutenprint.c i18n.c i18n.h rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_LDADD = $(CUPS_LIBS) $(GUTENPRINT_LIBS) @LIBICONV@ rastertogutenprint_@GUTENPRINT_RELEASE_VERSION@_LDFLAGS = $(STATIC_LDOPTS) @BUILD_CUPS_TRUE@CUPS_PKG = calibrate.ppm @@ -753,7 +806,7 @@ pkgsysconf_DATA = $(CUPS_CONF) @USE_NLS_TRUE@PPD_NLS_1 = ppd-nls @BUILD_LIBUSB_BACKENDS_TRUE@INSTALL_BLACKLIST = install-blacklist CLEANFILES = ppd-stamp -DISTCLEANFILES = cups-genppdupdate +DISTCLEANFILES = cups-genppdupdate test-ppds MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = \ COPYING \ @@ -761,8 +814,7 @@ EXTRA_DIST = \ blacklist \ calibrate.ppm \ command.txt \ - command.types \ - test-ppds + command.types all: all-am @@ -780,16 +832,15 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/cups/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/cups/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; -$(top_srcdir)/scripts/global.mk: +$(top_srcdir)/scripts/global.mk $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -803,8 +854,14 @@ Info.plist: $(top_builddir)/config.status $(srcdir)/Info.plist.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ cups-genppdupdate: $(top_builddir)/config.status $(srcdir)/cups-genppdupdate.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +test-ppds: $(top_builddir)/config.status $(srcdir)/test-ppds.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +min-pagesize: $(top_builddir)/config.status $(srcdir)/min-pagesize.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ test-rastertogutenprint: $(top_builddir)/config.status $(srcdir)/test-rastertogutenprint.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +test-rastertogutenprint.check: $(top_builddir)/config.status $(srcdir)/test-rastertogutenprint.check.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ @@ -1120,45 +1177,54 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_canonselphy.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_canonselphyneo.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_citizencw01.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_common.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_dnpds40.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_kodak1400.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_kodak605.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_kodak6800.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_mitsu70x.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_mitsu9550.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_mitsup95d.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos1245.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos2145.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos6145.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos6245.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_sonyupdr150.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@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cups-calibrate.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-i18n.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i18n.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rastertoprinter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_canonselphy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_canonselphyneo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_dnpds40.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_kodak1400.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_kodak605.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_kodak6800.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_magicard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_mitsu70x.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_mitsu9550.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_mitsud90.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_mitsup95d.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos1245.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos2145.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos6145.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_shinkos6245.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_gutenprint-backend_sonyupdr150.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commandtocanon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commandtoepson.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cups-calibrate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-i18n.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i18n.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rastertogutenprint.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -1293,20 +1359,6 @@ backend_gutenprint-backend_mitsu70x.obj: backend_mitsu70x.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_mitsu70x.obj `if test -f 'backend_mitsu70x.c'; then $(CYGPATH_W) 'backend_mitsu70x.c'; else $(CYGPATH_W) '$(srcdir)/backend_mitsu70x.c'; fi` -backend_gutenprint-backend_citizencw01.o: backend_citizencw01.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-backend_citizencw01.o -MD -MP -MF $(DEPDIR)/backend_gutenprint-backend_citizencw01.Tpo -c -o backend_gutenprint-backend_citizencw01.o `test -f 'backend_citizencw01.c' || echo '$(srcdir)/'`backend_citizencw01.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-backend_citizencw01.Tpo $(DEPDIR)/backend_gutenprint-backend_citizencw01.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='backend_citizencw01.c' object='backend_gutenprint-backend_citizencw01.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-backend_citizencw01.o `test -f 'backend_citizencw01.c' || echo '$(srcdir)/'`backend_citizencw01.c - -backend_gutenprint-backend_citizencw01.obj: backend_citizencw01.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-backend_citizencw01.obj -MD -MP -MF $(DEPDIR)/backend_gutenprint-backend_citizencw01.Tpo -c -o backend_gutenprint-backend_citizencw01.obj `if test -f 'backend_citizencw01.c'; then $(CYGPATH_W) 'backend_citizencw01.c'; else $(CYGPATH_W) '$(srcdir)/backend_citizencw01.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-backend_citizencw01.Tpo $(DEPDIR)/backend_gutenprint-backend_citizencw01.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='backend_citizencw01.c' object='backend_gutenprint-backend_citizencw01.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-backend_citizencw01.obj `if test -f 'backend_citizencw01.c'; then $(CYGPATH_W) 'backend_citizencw01.c'; else $(CYGPATH_W) '$(srcdir)/backend_citizencw01.c'; fi` - backend_gutenprint-backend_mitsu9550.o: backend_mitsu9550.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-backend_mitsu9550.o -MD -MP -MF $(DEPDIR)/backend_gutenprint-backend_mitsu9550.Tpo -c -o backend_gutenprint-backend_mitsu9550.o `test -f 'backend_mitsu9550.c' || echo '$(srcdir)/'`backend_mitsu9550.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-backend_mitsu9550.Tpo $(DEPDIR)/backend_gutenprint-backend_mitsu9550.Po @@ -1391,6 +1443,48 @@ backend_gutenprint-backend_mitsup95d.obj: backend_mitsup95d.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_mitsup95d.obj `if test -f 'backend_mitsup95d.c'; then $(CYGPATH_W) 'backend_mitsup95d.c'; else $(CYGPATH_W) '$(srcdir)/backend_mitsup95d.c'; fi` +backend_gutenprint-backend_magicard.o: backend_magicard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-backend_magicard.o -MD -MP -MF $(DEPDIR)/backend_gutenprint-backend_magicard.Tpo -c -o backend_gutenprint-backend_magicard.o `test -f 'backend_magicard.c' || echo '$(srcdir)/'`backend_magicard.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-backend_magicard.Tpo $(DEPDIR)/backend_gutenprint-backend_magicard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='backend_magicard.c' object='backend_gutenprint-backend_magicard.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-backend_magicard.o `test -f 'backend_magicard.c' || echo '$(srcdir)/'`backend_magicard.c + +backend_gutenprint-backend_magicard.obj: backend_magicard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-backend_magicard.obj -MD -MP -MF $(DEPDIR)/backend_gutenprint-backend_magicard.Tpo -c -o backend_gutenprint-backend_magicard.obj `if test -f 'backend_magicard.c'; then $(CYGPATH_W) 'backend_magicard.c'; else $(CYGPATH_W) '$(srcdir)/backend_magicard.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-backend_magicard.Tpo $(DEPDIR)/backend_gutenprint-backend_magicard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='backend_magicard.c' object='backend_gutenprint-backend_magicard.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-backend_magicard.obj `if test -f 'backend_magicard.c'; then $(CYGPATH_W) 'backend_magicard.c'; else $(CYGPATH_W) '$(srcdir)/backend_magicard.c'; fi` + +backend_gutenprint-backend_mitsud90.o: backend_mitsud90.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-backend_mitsud90.o -MD -MP -MF $(DEPDIR)/backend_gutenprint-backend_mitsud90.Tpo -c -o backend_gutenprint-backend_mitsud90.o `test -f 'backend_mitsud90.c' || echo '$(srcdir)/'`backend_mitsud90.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-backend_mitsud90.Tpo $(DEPDIR)/backend_gutenprint-backend_mitsud90.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='backend_mitsud90.c' object='backend_gutenprint-backend_mitsud90.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-backend_mitsud90.o `test -f 'backend_mitsud90.c' || echo '$(srcdir)/'`backend_mitsud90.c + +backend_gutenprint-backend_mitsud90.obj: backend_mitsud90.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(backend_gutenprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT backend_gutenprint-backend_mitsud90.obj -MD -MP -MF $(DEPDIR)/backend_gutenprint-backend_mitsud90.Tpo -c -o backend_gutenprint-backend_mitsud90.obj `if test -f 'backend_mitsud90.c'; then $(CYGPATH_W) 'backend_mitsud90.c'; else $(CYGPATH_W) '$(srcdir)/backend_mitsud90.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/backend_gutenprint-backend_mitsud90.Tpo $(DEPDIR)/backend_gutenprint-backend_mitsud90.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='backend_mitsud90.c' object='backend_gutenprint-backend_mitsud90.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-backend_mitsud90.obj `if test -f 'backend_mitsud90.c'; then $(CYGPATH_W) 'backend_mitsud90.c'; else $(CYGPATH_W) '$(srcdir)/backend_mitsud90.c'; fi` + +cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.o: cups-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@-cups-genppd.o -MD -MP -MF $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Tpo -c -o cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.o `test -f 'cups-genppd.c' || echo '$(srcdir)/'`cups-genppd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Tpo $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cups-genppd.c' object='cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.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) $(AM_CPPFLAGS) $(CPPFLAGS) $(cups_genppd_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -c -o cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.o `test -f 'cups-genppd.c' || echo '$(srcdir)/'`cups-genppd.c + +cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.obj: cups-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@-cups-genppd.obj -MD -MP -MF $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Tpo -c -o cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.obj `if test -f 'cups-genppd.c'; then $(CYGPATH_W) 'cups-genppd.c'; else $(CYGPATH_W) '$(srcdir)/cups-genppd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Tpo $(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cups-genppd.c' object='cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.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) $(AM_CPPFLAGS) $(CPPFLAGS) $(cups_genppd_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -c -o cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.obj `if test -f 'cups-genppd.c'; then $(CYGPATH_W) 'cups-genppd.c'; else $(CYGPATH_W) '$(srcdir)/cups-genppd.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 @@ -1419,6 +1513,20 @@ cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.obj: i18n.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cups_genppd_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -c -o cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` +gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.o: gutenprint.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -MT gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.o -MD -MP -MF $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Tpo -c -o gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.o `test -f 'gutenprint.c' || echo '$(srcdir)/'`gutenprint.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Tpo $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gutenprint.c' object='gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.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) $(AM_CPPFLAGS) $(CPPFLAGS) $(gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -c -o gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.o `test -f 'gutenprint.c' || echo '$(srcdir)/'`gutenprint.c + +gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.obj: gutenprint.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -MT gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.obj -MD -MP -MF $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Tpo -c -o gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.obj `if test -f 'gutenprint.c'; then $(CYGPATH_W) 'gutenprint.c'; else $(CYGPATH_W) '$(srcdir)/gutenprint.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Tpo $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gutenprint.c' object='gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.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) $(AM_CPPFLAGS) $(CPPFLAGS) $(gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -c -o gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.obj `if test -f 'gutenprint.c'; then $(CYGPATH_W) 'gutenprint.c'; else $(CYGPATH_W) '$(srcdir)/gutenprint.c'; fi` + gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.o: genppd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gutenprint_@GUTENPRINT_RELEASE_VERSION@_CFLAGS) $(CFLAGS) -MT gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.o -MD -MP -MF $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Tpo -c -o gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.o `test -f 'genppd.c' || echo '$(srcdir)/'`genppd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Tpo $(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Po @@ -1577,7 +1685,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS) if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ - else \ + elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ @@ -1667,7 +1775,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS) fi; \ $$success || exit 1 -check-TESTS: +check-TESTS: @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @@ -1695,9 +1803,9 @@ test-ppds.log: test-ppds --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) -test-rastertogutenprint.log: test-rastertogutenprint - @p='test-rastertogutenprint'; \ - b='test-rastertogutenprint'; \ +test-rastertogutenprint.check.log: test-rastertogutenprint.check + @p='test-rastertogutenprint.check'; \ + b='test-rastertogutenprint.check'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ @@ -1717,7 +1825,10 @@ test-rastertogutenprint.log: test-rastertogutenprint @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -1804,7 +1915,34 @@ clean-am: clean-binPROGRAMS clean-cupsexec_backendPROGRAMS \ mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_canonselphy.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_canonselphyneo.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_common.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_dnpds40.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_kodak1400.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_kodak605.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_kodak6800.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_magicard.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsu70x.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsu9550.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsud90.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsup95d.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos1245.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos2145.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos6145.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos6245.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_sonyupdr150.Po + -rm -f ./$(DEPDIR)/commandtocanon.Po + -rm -f ./$(DEPDIR)/commandtoepson.Po + -rm -f ./$(DEPDIR)/cups-calibrate.Po + -rm -f ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Po + -rm -f ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Po + -rm -f ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.Po + -rm -f ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Po + -rm -f ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Po + -rm -f ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-i18n.Po + -rm -f ./$(DEPDIR)/i18n.Po + -rm -f ./$(DEPDIR)/rastertogutenprint.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -1856,7 +1994,34 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_canonselphy.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_canonselphyneo.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_common.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_dnpds40.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_kodak1400.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_kodak605.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_kodak6800.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_magicard.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsu70x.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsu9550.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsud90.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_mitsup95d.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos1245.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos2145.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos6145.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_shinkos6245.Po + -rm -f ./$(DEPDIR)/backend_gutenprint-backend_sonyupdr150.Po + -rm -f ./$(DEPDIR)/commandtocanon.Po + -rm -f ./$(DEPDIR)/commandtoepson.Po + -rm -f ./$(DEPDIR)/cups-calibrate.Po + -rm -f ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-cups-genppd.Po + -rm -f ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-genppd.Po + -rm -f ./$(DEPDIR)/cups_genppd_@GUTENPRINT_RELEASE_VERSION@-i18n.Po + -rm -f ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-genppd.Po + -rm -f ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-gutenprint.Po + -rm -f ./$(DEPDIR)/gutenprint_@GUTENPRINT_RELEASE_VERSION@-i18n.Po + -rm -f ./$(DEPDIR)/i18n.Po + -rm -f ./$(DEPDIR)/rastertogutenprint.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -1882,8 +2047,8 @@ uninstall-am: uninstall-binPROGRAMS uninstall-cupsexec_backendPROGRAMS \ .MAKE: check-am install-am install-data-am install-exec-am \ install-strip -.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \ - check-am clean clean-binPROGRAMS \ +.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ + check-TESTS check-am clean clean-binPROGRAMS \ clean-cupsexec_backendPROGRAMS clean-cupsexec_driverPROGRAMS \ clean-cupsexec_filterPROGRAMS clean-generic clean-libtool \ clean-local clean-sbinPROGRAMS cscopelist-am ctags ctags-am \ @@ -1909,6 +2074,8 @@ uninstall-am: uninstall-binPROGRAMS uninstall-cupsexec_backendPROGRAMS \ uninstall-pkgdataDATA uninstall-pkgsysconfDATA \ uninstall-sbinPROGRAMS uninstall-sbinSCRIPTS +.PRECIOUS: Makefile + @SET_MAKE@ @@ -1918,10 +2085,6 @@ $(top_builddir)/src/main/libgutenprint.la: cd $(top_builddir)/src/main; \ $(MAKE) -$(top_builddir)/src/gutenprintui/libgutenprintui.la: - cd $(top_builddir)/src/gutenprintui; \ - $(MAKE) - $(top_builddir)/src/gutenprintui2/libgutenprintui2.la: cd $(top_builddir)/src/gutenprintui2; \ $(MAKE) @@ -1929,17 +2092,19 @@ $(top_builddir)/src/gutenprintui2/libgutenprintui2.la: @BUILD_CUPS_TRUE@@BUILD_LIBUSB_BACKENDS_TRUE@install-exec-hook: @BUILD_CUPS_TRUE@@BUILD_LIBUSB_BACKENDS_TRUE@ chmod 700 $(DESTDIR)$(pkglibdir)/backend/backend_gutenprint @BUILD_CUPS_TRUE@@BUILD_LIBUSB_BACKENDS_TRUE@ mv $(DESTDIR)$(pkglibdir)/backend/backend_gutenprint "$(DESTDIR)$(pkglibdir)/backend/gutenprint$(GUTENPRINT_MAJOR_VERSION)$(GUTENPRINT_MINOR_VERSION)+usb" +@BUILD_CUPS_TRUE@test-rastertogutenprint: min-pagesize +@BUILD_CUPS_TRUE@test-rastertogutenprint.check: test-rastertogutenprint @BUILD_LIBUSB_BACKENDS_TRUE@install-blacklist: -@BUILD_LIBUSB_BACKENDS_TRUE@ $(mkdir_p) $(DESTDIR)$(cupsdata_blacklistdir) +@BUILD_LIBUSB_BACKENDS_TRUE@ $(MKDIR_P) $(DESTDIR)$(cupsdata_blacklistdir) @BUILD_LIBUSB_BACKENDS_TRUE@ $(INSTALL_DATA) $(srcdir)/blacklist $(DESTDIR)$(cupsdata_blacklistdir)/net.sf.gimp-print.usb-quirks install-data-local: $(INSTALL_DATA_LOCAL_DEPS) $(INSTALL_BLACKLIST) if test -n "$(CUPS_PKG)" -a -n "$(INSTALL_DATA_LOCAL_DEPS)" ; then \ - $(mkdir_p) $(DESTDIR)$(cups_modeldir); \ + $(MKDIR_P) $(DESTDIR)$(cups_modeldir); \ cd ppd ; \ for language in * ; do \ cd ..; \ - $(mkdir_p) $(DESTDIR)$(cups_modeldir)/$$language; \ + $(MKDIR_P) $(DESTDIR)$(cups_modeldir)/$$language; \ cd ppd/$$language; \ for ppdfile in * ; do \ (cd ../..; $(INSTALL_DATA) ppd/$$language/$$ppdfile $(DESTDIR)$(cups_modeldir)/$$language) ; \ @@ -1947,10 +2112,10 @@ install-data-local: $(INSTALL_DATA_LOCAL_DEPS) $(INSTALL_BLACKLIST) cd ..; \ done \ fi - $(mkdir_p) "$(DESTDIR)$(localedir)" + $(MKDIR_P) "$(DESTDIR)$(localedir)" for file in $(srcdir)/../../po/*.po; do \ lang=`basename $$file .po`; \ - $(mkdir_p) "$(DESTDIR)$(localedir)/$$lang"; \ + $(MKDIR_P) "$(DESTDIR)$(localedir)/$$lang"; \ $(INSTALL_DATA) $$file "$(DESTDIR)$(localedir)/$$lang/gutenprint_$$lang.po"; \ done @@ -1979,13 +2144,15 @@ install-data-hook: -rmdir $(DESTDIR)$(cupsexec_backenddir) -rmdir $(DESTDIR)$(cupsdata_blacklistdir) -rmdir $(DESTDIR)$(bindir) + -rmdir $(DESTDIR)$(sbindir) -rmdir $(DESTDIR)$(pkglibdir) -rmdir $(DESTDIR)$(pkgsysconfdir) -rmdir `dirname $(DESTDIR)$(pkgdatadir)` -rmdir `dirname $(DESTDIR)$(pkglibdir)` -rmdir `dirname $(DESTDIR)$(pkgsysconfdir)` -.PHONY: ppd ppd-stamp-pre ppd-stamp-nonls ppd-stamp-nls ppd-stamp-phony ppd-catalog-clean ppd-clean $(INSTALL_BLACKLIST) +.PHONY: ppd ppd-stamp-pre ppd-stamp-nonls ppd-stamp-nls ppd-stamp-phony \ + ppd-catalog-clean ppd-clean $(INSTALL_BLACKLIST) all-local: $(INSTALL_DATA_LOCAL_DEPS) @@ -1999,76 +2166,111 @@ ppd-stamp: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(top_srcdir)/src/xml/xml-st ppd-stamp-phony: $(PPD) ppd-catalog-clean: - $(RM) -rf catalog + $(RM) -rf $(PPD_DIR)catalog ppd-clean: - $(RM) -rf ppd + $(RM) -rf $(PPD_DIR) ppd-stamp-pre: ppd-catalog-clean ppd-clean ppd-catalog: ppd-catalog-clean - $(mkdir_p) catalog - for file in $(srcdir)/../../po/*.po; do \ + $(MKDIR_P) catalog + for file in $(top_srcdir)/po/*.po; do \ lang=`basename $$file .po`; \ - $(mkdir_p) "catalog/$$lang"; \ - $(INSTALL_DATA) $$file "catalog/$$lang/gutenprint_$$lang.po"; \ + $(MKDIR_P) "$(PPD_DIR)catalog/$$lang"; \ + $(INSTALL_DATA) $$file "$(PPD_DIR)catalog/$$lang/gutenprint_$$lang.po"; \ done ppd-nonls: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - $(mkdir_p) ppd/C + @echo "Non-localized PPDs:" + $(MKDIR_P) $(PPD_DIR)/C $(MAKE) ppd-catalog-clean - LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ $(EXTRA_GENPPD_OPTS) -l C -p ppd/C + LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l C -p $(PPD_DIR)/C @WHICH_PPDS@ ppd-nonls-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - $(mkdir_p) ppd/C + @echo "Non-localized PPDs (all):" + $(MKDIR_P) $(PPD_DIR)/C + $(MAKE) ppd-catalog-clean + LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l C -p $(PPD_DIR)/C -a + +ppd-nonls-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ + @echo "Non-localized PPDs (simplified):" + $(MKDIR_P) $(PPD_DIR)/C $(MAKE) ppd-catalog-clean - LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ $(EXTRA_GENPPD_OPTS) -l C -p ppd/C -a + LC_ALL= LANG= LANGUAGE= $(STP_NONLS_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l C -p $(PPD_DIR)/C -s ppd-global: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @echo "Global PPDs:" - $(mkdir_p) ppd/Global + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global @WHICH_PPDS@ $(MAKE) ppd-catalog-clean ppd-global-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - @echo "Global PPDs:" - $(mkdir_p) ppd/Global + @echo "Global PPDs (all):" + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global -a + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -a + $(MAKE) ppd-catalog-clean + +ppd-global-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ + @echo "Global PPDs (simplified):" + $(MKDIR_P) $(PPD_DIR)/Global + $(MAKE) ppd-catalog + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -s $(MAKE) ppd-catalog-clean ppd-global-ln: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @echo "Global PPDs (localized numbers for testing):" - $(mkdir_p) ppd/Global + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global -N + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -N @WHICH_PPDS@ $(MAKE) ppd-catalog-clean ppd-global-ln-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ - @echo "Global PPDs (localized numbers for testing):" - $(mkdir_p) ppd/Global + @echo "Global PPDs (all, localized numbers for testing):" + $(MKDIR_P) $(PPD_DIR)/Global + $(MAKE) ppd-catalog + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -N -a + $(MAKE) ppd-catalog-clean + +ppd-global-ln-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ + @echo "Global PPDs (all, localized numbers for testing):" + $(MKDIR_P) $(PPD_DIR)/Global $(MAKE) ppd-catalog - LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -p ppd/Global -N -a + LC_ALL= LANG= LANGUAGE= $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -p $(PPD_DIR)/Global -N -s $(MAKE) ppd-catalog-clean ppd-nls: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ ppd-nonls - $(mkdir_p) ppd + @echo "Localized PPD files:" + $(MKDIR_P) ppd + $(MAKE) ppd-catalog + for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ -L` ; do \ + $(MKDIR_P) $(PPD_DIR)/$$language ; \ + echo -n "$$language: " ; \ + LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) -l $$language -p $(PPD_DIR)/$$language; \ + done + $(MAKE) ppd-catalog-clean + +ppd-nls-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ ppd-nonls + @echo "Localized PPD files (all):" + $(MKDIR_P) ppd $(MAKE) ppd-catalog - for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ -L` ; do \ - $(mkdir_p) ppd/$$language ; \ + for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ -L` ; do \ + $(MKDIR_P) $(PPD_DIR)/$$language ; \ echo -n "$$language: " ; \ - LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p ppd/$$language; \ + LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p $(PPD_DIR)/$$language -a; \ done $(MAKE) ppd-catalog-clean -ppd-nls-a: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ ppd-nonls - $(mkdir_p) ppd +ppd-nls-s: cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ ppd-nonls + @echo "Localized PPD files (simplified):" + $(MKDIR_P) ppd $(MAKE) ppd-catalog - for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ -L` ; do \ - $(mkdir_p) ppd/$$language ; \ + for language in `$(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ @WHICH_PPDS@ -L` ; do \ + $(MKDIR_P) $(PPD_DIR)/$$language ; \ echo -n "$$language: " ; \ - LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p ppd/$$language -a; \ + LC_ALL=$$language LANG=$$language LANGUAGE=$$language $(STP_ENV) ./cups-genppd.@GUTENPRINT_RELEASE_VERSION@ $(EXTRA_GENPPD_OPTS) @WHICH_PPDS@ -l $$language -p $(PPD_DIR)/$$language -s; \ done $(MAKE) ppd-catalog-clean diff --git a/src/cups/backend_canonselphy.c b/src/cups/backend_canonselphy.c index 8b016df..42b3f45 100644 --- a/src/cups/backend_canonselphy.c +++ b/src/cups/backend_canonselphy.c @@ -1,7 +1,7 @@ /* * Canon SELPHY ES/CP series CUPS backend -- libusb-1.0 version * - * (c) 2007-2016 Solomon Peachy + * (c) 2007-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -79,50 +80,55 @@ struct printer_data { int type; /* P_??? */ char *model; /* eg "SELPHY ES1" */ - int init_length; - int foot_length; + uint16_t init_length; + uint16_t foot_length; int16_t init_readback[READBACK_LEN]; int16_t ready_y_readback[READBACK_LEN]; int16_t ready_m_readback[READBACK_LEN]; int16_t ready_c_readback[READBACK_LEN]; int16_t done_c_readback[READBACK_LEN]; uint8_t clear_error[READBACK_LEN]; - int clear_error_len; + uint8_t clear_error_len; int16_t paper_codes[256]; - int16_t pgcode_offset; /* Offset into printjob for paper type */ - int16_t paper_code_offset; /* Offset in readback for paper type */ - int (*error_detect)(uint8_t *rdbuf); - char *(*pgcode_names)(uint8_t pgcode); + int8_t pgcode_offset; /* Offset into printjob for paper type */ + int8_t paper_code_offset; /* Offset in readback for paper type */ + int8_t paper_code_offset2; /* Offset in readback for paper type (2nd) */ + uint8_t (*error_detect)(uint8_t *rdbuf); + char *(*pgcode_names)(uint8_t *rdbuf, struct printer_data *printer); }; -static char *generic_pgcode_names(uint8_t pgcode) +static char *generic_pgcode_names(uint8_t *rdbuf, struct printer_data *printer) { + uint8_t pgcode = 0, pgcode2 = 0; + + if (printer->paper_code_offset != -1) + pgcode = rdbuf[printer->paper_code_offset]; + if (printer->paper_code_offset2 != -1) + pgcode2 = rdbuf[printer->paper_code_offset2]; + switch(pgcode & 0xf) { case 0x01: return "P"; case 0x02: return "L"; - case 0x03: return "C"; + case 0x03: return pgcode2 ? "Cl" : "C"; case 0x04: return "W"; case 0x0f: return "None"; default: return "Unknown"; } } -static int es1_error_detect(uint8_t *rdbuf) +static uint8_t es1_error_detect(uint8_t *rdbuf) { if (rdbuf[1] == 0x01) { - if (rdbuf[9] == 0x00) { + if (rdbuf[9] == 0x00) ERROR("Cover open!\n"); - } else { + else ERROR("Unknown error %02x\n", rdbuf[9]); - } return 1; } else if (rdbuf[4] == 0x01 && rdbuf[5] == 0xff && rdbuf[6] == 0xff && rdbuf[7] == 0xff) { - ATTR("marker-levels=%d\n", 0); ERROR("No media loaded!\n"); return 1; } else if (rdbuf[0] == 0x0f) { - ATTR("marker-levels=%d\n", 0); ERROR("Out of media!\n"); return 1; } @@ -130,7 +136,7 @@ static int es1_error_detect(uint8_t *rdbuf) return 0; } -static int es2_error_detect(uint8_t *rdbuf) +static uint8_t es2_error_detect(uint8_t *rdbuf) { if (rdbuf[0] == 0x16 && rdbuf[1] == 0x01) { @@ -142,13 +148,11 @@ static int es2_error_detect(uint8_t *rdbuf) rdbuf[4] == 0x05 && rdbuf[5] == 0x05 && rdbuf[6] == 0x02) { - ATTR("marker-levels=%d\n", 0); ERROR("No media loaded!\n"); return 1; } if (rdbuf[0] == 0x14) { - ATTR("marker-levels=%d\n", 0); ERROR("Out of media!\n"); return 1; } @@ -156,22 +160,19 @@ static int es2_error_detect(uint8_t *rdbuf) return 0; } -static int es3_error_detect(uint8_t *rdbuf) +static uint8_t es3_error_detect(uint8_t *rdbuf) { if (rdbuf[8] == 0x01) { - if (rdbuf[10] == 0x0f) { + if (rdbuf[10] == 0x0f) ERROR("Communications Error\n"); - } else if (rdbuf[10] == 0x01) { - ATTR("marker-levels=%d\n", 0); + else if (rdbuf[10] == 0x01) ERROR("No media loaded!\n"); - } else { + else ERROR("Unknown error - %02x + %02x\n", rdbuf[8], rdbuf[10]); - } return 1; } else if (rdbuf[8] == 0x03 && rdbuf[10] == 0x02) { - ATTR("marker-levels=%d\n", 0); ERROR("No media loaded!\n"); return 1; } else if (rdbuf[8] == 0x08 && @@ -193,7 +194,7 @@ static int es3_error_detect(uint8_t *rdbuf) return 0; } -static int es40_error_detect(uint8_t *rdbuf) +static uint8_t es40_error_detect(uint8_t *rdbuf) { /* ES40 */ if (!rdbuf[3]) @@ -201,17 +202,15 @@ static int es40_error_detect(uint8_t *rdbuf) if (rdbuf[3] == 0x01) ERROR("Generic communication error\n"); - else if (rdbuf[3] == 0x32) { - ATTR("marker-levels=%d\n", 0); + else if (rdbuf[3] == 0x32) ERROR("Cover open or media empty!\n"); - } else + else ERROR("Unknown error - %02x\n", rdbuf[3]); - return 1; } -static int cp790_error_detect(uint8_t *rdbuf) +static uint8_t cp790_error_detect(uint8_t *rdbuf) { /* CP790 */ if (rdbuf[5] == 0xff) { @@ -221,18 +220,15 @@ static int cp790_error_detect(uint8_t *rdbuf) ERROR("No paper tray loaded!\n"); return 1; } else if (rdbuf[3]) { - if ((rdbuf[3] & 0xf) == 0x02) { // 0x12 0x22 - ATTR("marker-levels=%d\n", 0); + if ((rdbuf[3] & 0xf) == 0x02) // 0x12 0x22 ERROR("No paper tray loaded!\n"); - } else if ((rdbuf[3] & 0xf) == 0x03) { // 0x13 0x23 - ATTR("marker-levels=%d\n", 0); + else if ((rdbuf[3] & 0xf) == 0x03) // 0x13 0x23 ERROR("Empty paper tray or feed error!\n"); - } else if (rdbuf[3] == 0x11) + else if (rdbuf[3] == 0x11) ERROR("Paper feed error!\n"); - else if (rdbuf[3] == 0x21) { - ATTR("marker-levels=%d\n", 0); + else if (rdbuf[3] == 0x21) ERROR("Ribbon depleted!\n"); - } else + else ERROR("Unknown error - %02x\n", rdbuf[3]); return 1; } @@ -240,46 +236,42 @@ static int cp790_error_detect(uint8_t *rdbuf) return 0; } -static char *cp10_pgcode_names(uint8_t pgcode) +static char *cp10_pgcode_names(uint8_t *rdbuf, struct printer_data *printer) { - switch (pgcode) { - default: return "C"; - }; + UNUSED(rdbuf); + UNUSED(printer); + + return "C"; /* Printer only supports one media type */ } -static int cp10_error_detect(uint8_t *rdbuf) +static uint8_t cp10_error_detect(uint8_t *rdbuf) { if (!rdbuf[2]) return 0; - if (rdbuf[2] == 0x80) { - ATTR("marker-levels=%d\n", 0); + if (rdbuf[2] == 0x80) ERROR("No ribbon loaded\n"); - } else if (rdbuf[2] == 0x08) { - ATTR("marker-levels=%d\n", 0); + else if (rdbuf[2] == 0x08) ERROR("Ribbon depleted!\n"); - } else if (rdbuf[2] == 0x01) { - ATTR("marker-levels=%d\n", 0); + else if (rdbuf[2] == 0x01) ERROR("No paper loaded!\n"); - } else + else ERROR("Unknown error - %02x\n", rdbuf[2]); return 1; } -static int cpxxx_error_detect(uint8_t *rdbuf) +static uint8_t cpxxx_error_detect(uint8_t *rdbuf) { if (!rdbuf[2]) return 0; - if (rdbuf[2] == 0x01) { - ATTR("marker-levels=%d\n", 0); + if (rdbuf[2] == 0x01) ERROR("Paper feed problem!\n"); - } else if (rdbuf[2] == 0x04) + else if (rdbuf[2] == 0x04) ERROR("Ribbon problem!\n"); - else if (rdbuf[2] == 0x08) { - ATTR("marker-levels=%d\n", 0); + else if (rdbuf[2] == 0x08) ERROR("Ribbon depleted!\n"); - } else + else ERROR("Unknown error - %02x\n", rdbuf[2]); return 1; } @@ -298,6 +290,7 @@ static struct printer_data selphy_printers[] = { .clear_error_len = 12, .pgcode_offset = 3, .paper_code_offset = 6, + .paper_code_offset2 = -1, .error_detect = es1_error_detect, .pgcode_names = generic_pgcode_names, }, @@ -314,6 +307,7 @@ static struct printer_data selphy_printers[] = { .clear_error_len = 12, .pgcode_offset = 2, .paper_code_offset = 4, + .paper_code_offset2 = 6, .error_detect = es2_error_detect, .pgcode_names = generic_pgcode_names, }, @@ -330,6 +324,7 @@ static struct printer_data selphy_printers[] = { .clear_error_len = 12, .pgcode_offset = 2, .paper_code_offset = -1, + .paper_code_offset2 = -1, .error_detect = es3_error_detect, .pgcode_names = NULL, }, @@ -346,6 +341,7 @@ static struct printer_data selphy_printers[] = { .clear_error_len = 12, .pgcode_offset = 2, .paper_code_offset = 11, + .paper_code_offset2 = -1, .error_detect = es40_error_detect, .pgcode_names = generic_pgcode_names, }, @@ -362,6 +358,7 @@ static struct printer_data selphy_printers[] = { .clear_error_len = 12, .pgcode_offset = 2, .paper_code_offset = -1, /* Uses a different technique */ + .paper_code_offset2 = -1, .error_detect = cp790_error_detect, .pgcode_names = generic_pgcode_names, }, @@ -378,6 +375,7 @@ static struct printer_data selphy_printers[] = { .clear_error_len = 12, .pgcode_offset = 3, .paper_code_offset = 6, + .paper_code_offset2 = -1, .error_detect = cpxxx_error_detect, .pgcode_names = generic_pgcode_names, }, @@ -394,6 +392,7 @@ static struct printer_data selphy_printers[] = { .clear_error_len = 12, .pgcode_offset = 2, .paper_code_offset = -1, + .paper_code_offset2 = -1, .error_detect = cp10_error_detect, .pgcode_names = cp10_pgcode_names, }, @@ -536,17 +535,9 @@ done: } /* Private data structure */ -struct canonselphy_ctx { - struct libusb_device_handle *dev; - uint8_t endp_up; - uint8_t endp_down; - int type; - - struct printer_data *printer; - - uint8_t bw_mode; - +struct canonselphy_printjob { int16_t paper_code; + uint8_t bw_mode; uint32_t plane_len; @@ -556,11 +547,44 @@ struct canonselphy_ctx { uint8_t *plane_c; uint8_t *footer; - uint8_t *buffer; + int copies; +}; + +struct canonselphy_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + + struct printer_data *printer; + struct marker marker; uint8_t cp900; }; +static int canonselphy_get_status(struct canonselphy_ctx *ctx) +{ + uint8_t rdbuf[READBACK_LEN]; + int ret, num; + + /* Read in the printer status, twice. */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) rdbuf, READBACK_LEN, &num); + if (ret < 0) + return CUPS_BACKEND_FAILED; + + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) rdbuf, READBACK_LEN, &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + + INFO("Media type: %s\n", ctx->printer->pgcode_names? ctx->printer->pgcode_names(rdbuf, ctx->printer) : "Unknown"); + ctx->printer->error_detect(rdbuf); + + return CUPS_BACKEND_OK; +} + static int canonselphy_send_reset(struct canonselphy_ctx *ctx) { uint8_t rstcmd[12] = { 0x40, 0x10, 0x00, 0x00, @@ -587,39 +611,84 @@ static void *canonselphy_init(void) /* Static initialization */ setup_paper_codes(); - ctx->buffer = malloc(MAX_HEADER); - if (!ctx->buffer) { - ERROR("Memory Allocation Failure!\n"); - free(ctx); - ctx = NULL; - } - 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) +static int canonselphy_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct canonselphy_ctx *ctx = vctx; - struct libusb_device *device; - struct libusb_device_descriptor desc; + int i, num; + uint8_t rdbuf[READBACK_LEN]; UNUSED(jobid); ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; + ctx->type = type; - device = libusb_get_device(dev); - libusb_get_device_descriptor(device, &desc); + if (ctx->type == P_CP900) { + ctx->type = P_CP_XXX; + ctx->cp900 = 1; + } + for (i = 0 ; selphy_printers[i].type != -1; i++) { + if (selphy_printers[i].type == ctx->type) { + ctx->printer = &selphy_printers[i]; + } + } + if (!ctx->printer) { + ERROR("Error looking up printer type!\n"); + return CUPS_BACKEND_FAILED; + } - ctx->type = lookup_printer_type(&canonselphy_backend, - desc.idVendor, desc.idProduct); + /* Fill out marker structure */ + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.levelmax = -1; /* Unknown */ - if (desc.idProduct == USB_PID_CANON_CP900) - ctx->cp900 = 1; + if (test_mode < TEST_MODE_NOATTACH) { + /* Read printer status. Twice. */ + i = read_data(ctx->dev, ctx->endp_up, + rdbuf, READBACK_LEN, &num); + if (i < 0) + return CUPS_BACKEND_FAILED; + + i = read_data(ctx->dev, ctx->endp_up, + rdbuf, READBACK_LEN, &num); + if (i < 0) + return CUPS_BACKEND_FAILED; + + if (ctx->printer->error_detect(rdbuf)) + ctx->marker.levelnow = 0; /* Out of media */ + else + ctx->marker.levelnow = -3; /* Unknown but OK */ + ctx->marker.name = ctx->printer->pgcode_names? ctx->printer->pgcode_names(rdbuf, ctx->printer) : "Unknown"; + } else { + // XXX handle MEDIA_CODE at some point. + // we don't do any error checking here. + ctx->marker.name = "Unknown"; + } + + return CUPS_BACKEND_OK; +} + +static void canonselphy_cleanup_job(const void *vjob) { + const struct canonselphy_printjob *job = vjob; + + if (job->header) + free(job->header); + if (job->plane_y) + free(job->plane_y); + if (job->plane_m) + free(job->plane_m); + if (job->plane_c) + free(job->plane_c); + if (job->footer) + free(job->footer); + + free((void*)job); } static void canonselphy_teardown(void *vctx) { @@ -628,64 +697,63 @@ static void canonselphy_teardown(void *vctx) { if (!ctx) return; - if (ctx->header) - free(ctx->header); - if (ctx->plane_y) - free(ctx->plane_y); - if (ctx->plane_m) - free(ctx->plane_m); - if (ctx->plane_c) - free(ctx->plane_c); - if (ctx->footer) - free(ctx->footer); - - if (ctx->buffer) - free(ctx->buffer); - free(ctx); } -static int canonselphy_read_parse(void *vctx, int data_fd) +static int canonselphy_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct canonselphy_ctx *ctx = vctx; int i, remain; int printer_type; int offset = 0; + uint8_t rdbuf[MAX_HEADER]; + + struct canonselphy_printjob *job = NULL; if (!ctx) return CUPS_BACKEND_FAILED; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; + /* The CP900 job *may* have a 4-byte null footer after the job contents. Ignore it if it comes through here.. */ - i = read(data_fd, ctx->buffer, 4); + i = read(data_fd, rdbuf, 4); if (i != 4) { if (i == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d)\n", i, 4); perror("ERROR: Read failed"); + canonselphy_cleanup_job(job); return CUPS_BACKEND_FAILED; } /* if it's not the null header.. don't ignore! */ - if (ctx->buffer[0] != 0 || - ctx->buffer[1] != 0 || - ctx->buffer[2] != 0 || - ctx->buffer[3] != 0) { + if (rdbuf[0] != 0 || + rdbuf[1] != 0 || + rdbuf[2] != 0 || + rdbuf[3] != 0) { offset = 4; } /* Read the rest of the header.. */ - i = read(data_fd, ctx->buffer + offset, MAX_HEADER - offset); + i = read(data_fd, rdbuf + offset, MAX_HEADER - offset); if (i != MAX_HEADER - offset) { if (i == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d)\n", i, MAX_HEADER - offset); perror("ERROR: Read failed"); + canonselphy_cleanup_job(job); return CUPS_BACKEND_FAILED; } /* Figure out printer this file is intended for */ - printer_type = parse_printjob(ctx->buffer, &ctx->bw_mode, &ctx->plane_len); + printer_type = parse_printjob(rdbuf, &job->bw_mode, &job->plane_len); /* Special cases for some models */ if (printer_type == P_ES40_CP790) { if (ctx->type == P_CP790) @@ -694,97 +762,71 @@ static int canonselphy_read_parse(void *vctx, int data_fd) 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]; - break; - } - } - if (!ctx->printer) { - ERROR("Error mapping printjob to printer type!\n"); - return CUPS_BACKEND_FAILED; - } - - INFO("%sFile intended for a '%s' printer\n", ctx->bw_mode? "B/W " : "", ctx->printer->model); - - if (ctx->printer->type != ctx->type) { + if (printer_type != ctx->type) { ERROR("Printer/Job mismatch (%d/%d)\n", ctx->type, ctx->printer->type); + free(job); return CUPS_BACKEND_CANCEL; } + INFO("%sFile intended for a '%s' printer\n", job->bw_mode? "B/W " : "", ctx->printer->model); + /* Paper code setup */ if (ctx->printer->pgcode_offset != -1) - ctx->paper_code = ctx->printer->paper_codes[ctx->buffer[ctx->printer->pgcode_offset]]; + job->paper_code = ctx->printer->paper_codes[rdbuf[ctx->printer->pgcode_offset]]; else - ctx->paper_code = -1; + job->paper_code = -1; /* Add in plane header length! */ - ctx->plane_len += 12; - - /* Now prep for the job */ - if (ctx->header) { - free(ctx->header); - ctx->header = NULL; - } - if (ctx->plane_y) { - free(ctx->plane_y); - ctx->plane_y = NULL; - } - if (ctx->plane_m) { - free(ctx->plane_m); - ctx->plane_m = NULL; - } - if (ctx->plane_c) { - free(ctx->plane_c); - ctx->plane_c = NULL; - } - if (ctx->footer) { - free(ctx->footer); - ctx->footer = NULL; - } + job->plane_len += 12; /* Set up buffers */ - ctx->plane_y = malloc(ctx->plane_len); - ctx->plane_m = malloc(ctx->plane_len); - ctx->plane_c = malloc(ctx->plane_len); - ctx->header = malloc(ctx->printer->init_length); - ctx->footer = malloc(ctx->printer->foot_length); - if (!ctx->plane_y || !ctx->plane_m || !ctx->plane_c || !ctx->header || - (ctx->printer->foot_length && !ctx->footer)) { + job->plane_y = malloc(job->plane_len); + job->plane_m = malloc(job->plane_len); + job->plane_c = malloc(job->plane_len); + job->header = malloc(ctx->printer->init_length); + job->footer = malloc(ctx->printer->foot_length); + if (!job->plane_y || !job->plane_m || !job->plane_c || !job->header || + (ctx->printer->foot_length && !job->footer)) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + canonselphy_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } /* Move over chunks already read in */ - memcpy(ctx->header, ctx->buffer, ctx->printer->init_length); - memcpy(ctx->plane_y, ctx->buffer+ctx->printer->init_length, + memcpy(job->header, rdbuf, ctx->printer->init_length); + memcpy(job->plane_y, rdbuf+ctx->printer->init_length, MAX_HEADER-ctx->printer->init_length); /* Read in YELLOW plane */ - remain = ctx->plane_len - (MAX_HEADER-ctx->printer->init_length); + remain = job->plane_len - (MAX_HEADER-ctx->printer->init_length); while (remain > 0) { - i = read(data_fd, ctx->plane_y + (ctx->plane_len - remain), remain); - if (i < 0) + i = read(data_fd, job->plane_y + (job->plane_len - remain), remain); + if (i < 0) { + canonselphy_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; } /* Read in MAGENTA plane */ - remain = ctx->plane_len; + remain = job->plane_len; while (remain > 0) { - i = read(data_fd, ctx->plane_m + (ctx->plane_len - remain), remain); - if (i < 0) + i = read(data_fd, job->plane_m + (job->plane_len - remain), remain); + if (i < 0) { + canonselphy_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; } /* Read in CYAN plane */ - remain = ctx->plane_len; + remain = job->plane_len; while (remain > 0) { - i = read(data_fd, ctx->plane_c + (ctx->plane_len - remain), remain); - if (i < 0) + i = read(data_fd, job->plane_c + (job->plane_len - remain), remain); + if (i < 0) { + canonselphy_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; } @@ -792,22 +834,36 @@ static int canonselphy_read_parse(void *vctx, int data_fd) if (ctx->printer->foot_length) { remain = ctx->printer->foot_length; while (remain > 0) { - i = read(data_fd, ctx->footer + (ctx->printer->foot_length - remain), remain); - if (i < 0) + i = read(data_fd, job->footer + (ctx->printer->foot_length - remain), remain); + if (i < 0) { + canonselphy_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; } } + *vjob = job; + return CUPS_BACKEND_OK; } -static int canonselphy_main_loop(void *vctx, int copies) { +static int canonselphy_main_loop(void *vctx, const void *vjob) { struct canonselphy_ctx *ctx = vctx; uint8_t rdbuf[READBACK_LEN], rdbuf2[READBACK_LEN]; int last_state = -1, state = S_IDLE; int ret, num; + int copies; + + const struct canonselphy_printjob *job = vjob; + + if (!ctx) + return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; + + copies = job->copies; /* Read in the printer status to clear last state */ ret = read_data(ctx->dev, ctx->endp_up, @@ -815,14 +871,6 @@ static int canonselphy_main_loop(void *vctx, int copies) { if (ret < 0) return CUPS_BACKEND_FAILED; - - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", ctx->printer->pgcode_names? ctx->printer->pgcode_names(rdbuf[ctx->printer->paper_code_offset]) : "Unknown"); - ATTR("marker-types=ribbonWax\n"); - ATTR("marker-levels=%d\n", -3); /* ie Unknown but OK */ - top: if (state != last_state) { @@ -843,6 +891,7 @@ top: /* Error detection */ if (ctx->printer->error_detect(rdbuf)) { + dump_markers(&ctx->marker, 1, 0); if (ctx->printer->clear_error_len) /* Try to clear error state */ if ((ret = send_data(ctx->dev, ctx->endp_down, ctx->printer->clear_error, ctx->printer->clear_error_len))) @@ -866,10 +915,10 @@ top: break; /* Make sure paper/ribbon is correct */ - if (ctx->paper_code != -1) { + if (job->paper_code != -1) { if (ctx->type == P_CP_XXX) { uint8_t pc = rdbuf[ctx->printer->paper_code_offset]; - if (((pc >> 4) & 0xf) != (ctx->paper_code & 0x0f)) { + if (((pc >> 4) & 0xf) != (job->paper_code & 0x0f)) { if (pc & 0xf0) { ERROR("Incorrect paper tray loaded, aborting job!\n"); @@ -879,7 +928,7 @@ top: return CUPS_BACKEND_STOP; } } - if ((pc & 0xf) != (ctx->paper_code & 0xf)) { + if ((pc & 0xf) != (job->paper_code & 0xf)) { if (pc & 0x0f) { ERROR("Incorrect ribbon loaded, aborting job!\n"); return CUPS_BACKEND_HOLD; @@ -891,9 +940,9 @@ top: } } else { if (rdbuf[ctx->printer->paper_code_offset] != - ctx->paper_code) { + job->paper_code) { ERROR("Incorrect media/ribbon loaded (%02x vs %02x), aborting job!\n", - ctx->paper_code, + job->paper_code, rdbuf[ctx->printer->paper_code_offset]); return CUPS_BACKEND_HOLD; /* Hold this job, don't stop queue */ } @@ -905,14 +954,14 @@ top: if (ribbon == 0xf) { ERROR("No ribbon loaded, aborting!\n"); return CUPS_BACKEND_STOP; - } else if (ribbon != ctx->paper_code) { + } else if (ribbon != job->paper_code) { ERROR("Incorrect ribbon loaded, aborting job!\n"); return CUPS_BACKEND_HOLD; } if (paper == 0xf) { ERROR("No paper tray loaded, aborting!\n"); return CUPS_BACKEND_STOP; - } else if (paper != ctx->paper_code) { + } else if (paper != job->paper_code) { ERROR("Incorrect paper loaded, aborting job!\n"); return CUPS_BACKEND_HOLD; } @@ -923,7 +972,7 @@ top: case S_PRINTER_READY: INFO("Printing started; Sending init sequence\n"); /* Send printer init */ - if ((ret = send_data(ctx->dev, ctx->endp_down, ctx->header, ctx->printer->init_length))) + if ((ret = send_data(ctx->dev, ctx->endp_down, job->header, ctx->printer->init_length))) return CUPS_BACKEND_FAILED; state = S_PRINTER_INIT_SENT; @@ -934,19 +983,19 @@ top: } break; case S_PRINTER_READY_Y: - if (ctx->bw_mode) + if (job->bw_mode) INFO("Sending BLACK plane\n"); else INFO("Sending YELLOW plane\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, ctx->plane_y, ctx->plane_len))) + if ((ret = send_data(ctx->dev, ctx->endp_down, job->plane_y, job->plane_len))) return CUPS_BACKEND_FAILED; state = S_PRINTER_Y_SENT; break; case S_PRINTER_Y_SENT: if (!fancy_memcmp(rdbuf, ctx->printer->ready_m_readback, READBACK_LEN)) { - if (ctx->bw_mode) + if (job->bw_mode) state = S_PRINTER_DONE; else state = S_PRINTER_READY_M; @@ -955,7 +1004,7 @@ top: case S_PRINTER_READY_M: INFO("Sending MAGENTA plane\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, ctx->plane_m, ctx->plane_len))) + if ((ret = send_data(ctx->dev, ctx->endp_down, job->plane_m, job->plane_len))) return CUPS_BACKEND_FAILED; state = S_PRINTER_M_SENT; @@ -968,7 +1017,7 @@ top: case S_PRINTER_READY_C: INFO("Sending CYAN plane\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, ctx->plane_c, ctx->plane_len))) + if ((ret = send_data(ctx->dev, ctx->endp_down, job->plane_c, job->plane_len))) return CUPS_BACKEND_FAILED; state = S_PRINTER_C_SENT; @@ -996,7 +1045,7 @@ top: if (ctx->printer->foot_length) { INFO("Cleaning up\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, ctx->footer, ctx->printer->foot_length))) + if ((ret = send_data(ctx->dev, ctx->endp_down, job->footer, ctx->printer->foot_length))) return CUPS_BACKEND_FAILED; } state = S_FINISHED; @@ -1030,12 +1079,15 @@ static int canonselphy_cmdline_arg(void *vctx, int argc, char **argv) if (!ctx) return -1; - while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "R")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "Rs")) >= 0) { switch(i) { GETOPT_PROCESS_GLOBAL case 'R': canonselphy_send_reset(ctx); break; + case 's': + canonselphy_get_status(ctx); + break; } if (j) return j; @@ -1047,51 +1099,105 @@ static int canonselphy_cmdline_arg(void *vctx, int argc, char **argv) static void canonselphy_cmdline(void) { DEBUG("\t\t[ -R ] # Reset printer\n"); + DEBUG("\t\t[ -s ] # Query printer status\n"); +} + +static int canonselphy_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct canonselphy_ctx *ctx = vctx; + uint8_t rdbuf[READBACK_LEN]; + int ret, num; + + /* Read in the printer status, twice. */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) rdbuf, READBACK_LEN, &num); + if (ret < 0) + return CUPS_BACKEND_FAILED; + + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) rdbuf, READBACK_LEN, &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + + if (ctx->printer->error_detect(rdbuf)) + ctx->marker.levelnow = 0; + else + ctx->marker.levelnow = -3; + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; } +static const char *canonselphy_prefixes[] = { + "canonselphy", // Family name + "canon-cp10", "canon-cp100", "canon-cp200", "canon-cp220", + "canon-cp300", "canon-cp330", "canon-cp400", "canon-cp500", + "canon-cp510", "canon-cp520", "canon-cp530", "canon-cp600", + "canon-cp710", "canon-cp720", "canon-cp730", "canon-cp740", + "canon-cp750", "canon-cp760", "canon-cp770", "canon-cp780", + "canon-cp790", "canon-cp800", "canon-cp810", "canon-cp900", + "canon-es1", "canon-es2", "canon-es20", "canon-es3", + "canon-es30", "canon-es40", + // backwards compatibility + "selphycp10", "selphycp100", "selphycp200", "selphycp220", + "selphycp300", "selphycp330", "selphycp400", "selphycp500", + "selphycp510", "selphycp520", "selphycp530", "selphycp600", + "selphycp710", "selphycp720", "selphycp730", "selphycp740", + "selphycp750", "selphycp760", "selphycp770", "selphycp780", + "selphycp790", "selphycp800", "selphycp810", "selphycp900", + "selphyes1", "selphyes2", "selphyes20", "selphyes3", + "selphyes30", "selphyes40", + NULL +}; + struct dyesub_backend canonselphy_backend = { - .name = "Canon SELPHY CP/ES", - .version = "0.94", - .uri_prefix = "canonselphy", + .name = "Canon SELPHY CP/ES (legacy)", + .version = "0.104", + .uri_prefixes = canonselphy_prefixes, .cmdline_usage = canonselphy_cmdline, .cmdline_arg = canonselphy_cmdline_arg, .init = canonselphy_init, .attach = canonselphy_attach, .teardown = canonselphy_teardown, .read_parse = canonselphy_read_parse, + .cleanup_job = canonselphy_cleanup_job, .main_loop = canonselphy_main_loop, + .query_markers = canonselphy_query_markers, .devices = { - { USB_VID_CANON, USB_PID_CANON_CP10, P_CP10, ""}, - { USB_VID_CANON, USB_PID_CANON_CP100, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP200, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP220, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP300, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP330, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP400, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP500, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP510, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP520, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP530, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP600, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP710, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP720, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP730, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP740, P_CP_XXX, ""}, - { USB_VID_CANON, USB_PID_CANON_CP750, P_CP_XXX, ""}, - { 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_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, ""}, - { USB_VID_CANON, USB_PID_CANON_ES1, P_ES1, ""}, - { USB_VID_CANON, USB_PID_CANON_ES2, P_ES2_20, ""}, - { 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, ""}, - { 0, 0, 0, ""} + { USB_VID_CANON, USB_PID_CANON_CP10, P_CP10, NULL, "canon-cp10"}, + { USB_VID_CANON, USB_PID_CANON_CP100, P_CP_XXX, NULL, "canon-cp100"}, + { USB_VID_CANON, USB_PID_CANON_CP200, P_CP_XXX, NULL, "canon-cp200"}, + { USB_VID_CANON, USB_PID_CANON_CP220, P_CP_XXX, NULL, "canon-cp220"}, + { USB_VID_CANON, USB_PID_CANON_CP300, P_CP_XXX, NULL, "selpyhcp300"}, + { USB_VID_CANON, USB_PID_CANON_CP330, P_CP_XXX, NULL, "canon-cp330"}, + { USB_VID_CANON, USB_PID_CANON_CP400, P_CP_XXX, NULL, "canon-cp400"}, + { USB_VID_CANON, USB_PID_CANON_CP500, P_CP_XXX, NULL, "canon-cp500"}, + { USB_VID_CANON, USB_PID_CANON_CP510, P_CP_XXX, NULL, "canon-cp510"}, + { USB_VID_CANON, USB_PID_CANON_CP520, P_CP_XXX, NULL, "canon-cp520"}, + { USB_VID_CANON, USB_PID_CANON_CP530, P_CP_XXX, NULL, "canon-cp530"}, + { USB_VID_CANON, USB_PID_CANON_CP600, P_CP_XXX, NULL, "canon-cp600"}, + { USB_VID_CANON, USB_PID_CANON_CP710, P_CP_XXX, NULL, "canon-cp710"}, + { USB_VID_CANON, USB_PID_CANON_CP720, P_CP_XXX, NULL, "canon-cp720"}, + { USB_VID_CANON, USB_PID_CANON_CP730, P_CP_XXX, NULL, "canon-cp730"}, + { USB_VID_CANON, USB_PID_CANON_CP740, P_CP_XXX, NULL, "canon-cp740"}, + { USB_VID_CANON, USB_PID_CANON_CP750, P_CP_XXX, NULL, "canon-cp750"}, + { USB_VID_CANON, USB_PID_CANON_CP760, P_CP_XXX, NULL, "canon-cp760"}, + { USB_VID_CANON, USB_PID_CANON_CP770, P_CP_XXX, NULL, "canon-cp770"}, + { USB_VID_CANON, USB_PID_CANON_CP780, P_CP_XXX, NULL, "canon-cp780"}, + { USB_VID_CANON, USB_PID_CANON_CP790, P_CP790, NULL, "canon-cp790"}, + { USB_VID_CANON, USB_PID_CANON_CP800, P_CP_XXX, NULL, "canon-cp800"}, + { USB_VID_CANON, USB_PID_CANON_CP810, P_CP_XXX, NULL, "canon-cp810"}, + { USB_VID_CANON, USB_PID_CANON_CP900, P_CP_XXX, NULL, "canon-cp900"}, + { USB_VID_CANON, USB_PID_CANON_ES1, P_ES1, NULL, "canon-es1"}, + { USB_VID_CANON, USB_PID_CANON_ES2, P_ES2_20, NULL, "canon-es2"}, + { USB_VID_CANON, USB_PID_CANON_ES20, P_ES2_20, NULL, "canon-es20"}, + { USB_VID_CANON, USB_PID_CANON_ES3, P_ES3_30, NULL, "canon-es3"}, + { USB_VID_CANON, USB_PID_CANON_ES30, P_ES3_30, NULL, "canon-es30"}, + { USB_VID_CANON, USB_PID_CANON_ES40, P_ES40, NULL, "canon-es40"}, + { 0, 0, 0, NULL, NULL} } }; /* @@ -1184,10 +1290,8 @@ struct dyesub_backend canonselphy_backend = { 0x02 for L-papers 0x03 for C-papers - [pg2] is: 0x00 for P & L papers - 0x01 for Cl-paper - - *** note: may refer to Label (0x01) vs non-Label (0x00) media. + [pg2] is: 0x00 for Normal papers + 0x01 for Label papers *************************************************************************** Selphy ES3/30: diff --git a/src/cups/backend_canonselphyneo.c b/src/cups/backend_canonselphyneo.c index 85f51b2..93f539d 100644 --- a/src/cups/backend_canonselphyneo.c +++ b/src/cups/backend_canonselphyneo.c @@ -1,7 +1,7 @@ /* * Canon SELPHY CPneo series CUPS backend -- libusb-1.0 version * - * (c) 2016 Solomon Peachy + * (c) 2016-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -45,6 +46,7 @@ #define USB_PID_CANON_CP910 0x327a #define USB_PID_CANON_CP1000 0x32ae #define USB_PID_CANON_CP1200 0x32b1 +#define USB_PID_CANON_CP1300 0x32db /* Header data structure */ struct selphyneo_hdr { @@ -59,13 +61,20 @@ struct selphyneo_readback { } __attribute((packed)); /* Private data structure */ +struct selphyneo_printjob { + uint8_t *databuf; + uint32_t datalen; + + int copies; +}; + struct selphyneo_ctx { struct libusb_device_handle *dev; uint8_t endp_up; uint8_t endp_down; + int type; - uint8_t *databuf; - uint32_t datalen; + struct marker marker; }; static char *selphyneo_statuses(uint8_t sts) @@ -97,6 +106,8 @@ static char *selphyneo_errors(uint8_t err) return "Paper Feed"; case 0x03: return "No Paper"; + case 0x05: + return "Incorrect Paper loaded"; case 0x06: return "Ink Cassette Empty"; case 0x07: @@ -105,6 +116,8 @@ static char *selphyneo_errors(uint8_t err) return "No Paper and Ink"; case 0x0A: return "Incorrect media for job"; + case 0x0B: + return "Paper jam"; default: return "Unknown Error"; } @@ -140,6 +153,34 @@ static int selphyneo_send_reset(struct selphyneo_ctx *ctx) return CUPS_BACKEND_OK; } +static int selphyneo_get_status(struct selphyneo_ctx *ctx) +{ + struct selphyneo_readback rdback; + int ret, num; + + /* Read in the printer status to clear last state */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &rdback, sizeof(rdback), &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + + /* And again, for the markers */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &rdback, sizeof(rdback), &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + + INFO("Printer state: %s\n", selphyneo_statuses(rdback.data[0])); + INFO("Media type: %s\n", selphynew_pgcodes(rdback.data[6])); + if (rdback.data[2]) { + INFO("Printer error: %s\n", selphyneo_errors(rdback.data[2])); + } + + return CUPS_BACKEND_OK; +} + static void *selphyneo_init(void) { struct selphyneo_ctx *ctx = malloc(sizeof(struct selphyneo_ctx)); @@ -154,21 +195,60 @@ static void *selphyneo_init(void) extern struct dyesub_backend selphyneo_backend; -static void selphyneo_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int selphyneo_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct selphyneo_ctx *ctx = vctx; - struct libusb_device *device; - struct libusb_device_descriptor desc; + struct selphyneo_readback rdback; + int ret, num; UNUSED(jobid); ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; + ctx->type = type; + + if (test_mode < TEST_MODE_NOATTACH) { + /* Read in the printer status to clear last state */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &rdback, sizeof(rdback), &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + + /* And again, for the markers */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &rdback, sizeof(rdback), &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + } else { + rdback.data[2] = 0; + rdback.data[6] = 0x01; + if (getenv("MEDIA_CODE")) + rdback.data[6] = atoi(getenv("MEDIA_CODE")); + } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = selphynew_pgcodes(rdback.data[6]); + ctx->marker.levelmax = -1; + if (rdback.data[2]) { + ctx->marker.levelnow = 0; + } else { + ctx->marker.levelnow = -3; + } + + return CUPS_BACKEND_OK; +} + +static void selphyneo_cleanup_job(const void *vjob) { + const struct selphyneo_printjob *job = vjob; - device = libusb_get_device(dev); - libusb_get_device_descriptor(device, &desc); + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void selphyneo_teardown(void *vctx) { @@ -177,29 +257,39 @@ static void selphyneo_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); - free(ctx); } -static int selphyneo_read_parse(void *vctx, int data_fd) +static int selphyneo_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct selphyneo_ctx *ctx = vctx; struct selphyneo_hdr hdr; int i, remain; + struct selphyneo_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; + /* Read the header.. */ i = read(data_fd, &hdr, sizeof(hdr)); if (i != sizeof(hdr)) { - if (i == 0) + if (i == 0) { + selphyneo_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } ERROR("Read failed (%d/%d)\n", i, (int)sizeof(hdr)); perror("ERROR: Read failed"); + selphyneo_cleanup_job(job); return CUPS_BACKEND_FAILED; } @@ -213,53 +303,63 @@ static int selphyneo_read_parse(void *vctx, int data_fd) default: ERROR("Unknown print size! (%02x, %ux%u)\n", hdr.data[10], le32_to_cpu(hdr.cols), le32_to_cpu(hdr.rows)); + selphyneo_cleanup_job(job); return CUPS_BACKEND_CANCEL; } + // XXX Sanity check job against loaded media? + /* Allocate a buffer */ - ctx->datalen = 0; - ctx->databuf = malloc(remain + sizeof(hdr)); - if (!ctx->databuf) { + job->datalen = 0; + job->databuf = malloc(remain + sizeof(hdr)); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + selphyneo_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } /* Store the read-in header */ - memcpy(ctx->databuf, &hdr, sizeof(hdr)); - ctx->datalen += sizeof(hdr); + memcpy(job->databuf, &hdr, sizeof(hdr)); + job->datalen += sizeof(hdr); /* Read in data */ while (remain > 0) { - i = read(data_fd, ctx->databuf + ctx->datalen, remain); - if (i < 0) + i = read(data_fd, job->databuf + job->datalen, remain); + if (i < 0) { + selphyneo_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; - ctx->datalen += i; + job->datalen += i; } + *vjob = job; + return CUPS_BACKEND_OK; } -static int selphyneo_main_loop(void *vctx, int copies) { +static int selphyneo_main_loop(void *vctx, const void *vjob) { struct selphyneo_ctx *ctx = vctx; struct selphyneo_readback rdback; int ret, num; + int copies; - /* Read in the printer status to clear last state */ - ret = read_data(ctx->dev, ctx->endp_up, - (uint8_t*) &rdback, sizeof(rdback), &num); + const struct selphyneo_printjob *job = vjob; - /* And again, for the markers */ + if (!ctx) + return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; + + copies = job->copies; + + /* Read in the printer status to clear last state */ ret = read_data(ctx->dev, ctx->endp_up, (uint8_t*) &rdback, sizeof(rdback), &num); - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", selphynew_pgcodes(rdback.data[6])); - - ATTR("marker-types=ribbonWax\n"); + if (ret < 0) + return CUPS_BACKEND_FAILED; top: INFO("Waiting for printer idle\n"); @@ -281,18 +381,20 @@ top: break; case 0x0A: ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]); - ATTR("marker-levels=%d\n", 0); + ctx->marker.levelnow = 0; + dump_markers(&ctx->marker, 1, 0); return CUPS_BACKEND_CANCEL; default: ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]); - ATTR("marker-levels=%d\n", 0); + ctx->marker.levelnow = 0; + dump_markers(&ctx->marker, 1, 0); return CUPS_BACKEND_STOP; } sleep(1); } while(1); - ATTR("marker-levels=%d\n", -3); /* ie Unknown but OK */ + dump_markers(&ctx->marker, 1, 0); INFO("Sending spool data\n"); /* Send the data over in 256K chunks */ @@ -301,10 +403,10 @@ top: int sent = 0; while (chunk > 0) { if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf + sent, chunk))) + job->databuf + sent, chunk))) return CUPS_BACKEND_FAILED; sent += chunk; - chunk = ctx->datalen - sent; + chunk = job->datalen - sent; if (chunk > 256*1024) chunk = 256*1024; } @@ -314,6 +416,9 @@ top: ret = read_data(ctx->dev, ctx->endp_up, (uint8_t*) &rdback, sizeof(rdback), &num); + if (ret < 0) + return CUPS_BACKEND_FAILED; + INFO("Waiting for printer acknowledgement\n"); do { ret = read_data(ctx->dev, ctx->endp_up, @@ -332,15 +437,17 @@ top: break; case 0x0A: ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]); - ATTR("marker-levels=%d\n", 0); + ctx->marker.levelnow = 0; + dump_markers(&ctx->marker, 1, 0); return CUPS_BACKEND_CANCEL; default: ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]); - ATTR("marker-levels=%d\n", 0); + ctx->marker.levelnow = 0; + dump_markers(&ctx->marker, 1, 0); return CUPS_BACKEND_STOP; } - if (rdback.data[0] > 0x02 && fast_return) { + if (rdback.data[0] > 0x02 && fast_return && copies <= 1) { INFO("Fast return mode enabled.\n"); break; } @@ -369,12 +476,15 @@ static int selphyneo_cmdline_arg(void *vctx, int argc, char **argv) if (!ctx) return -1; - while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "R")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "Rs")) >= 0) { switch(i) { GETOPT_PROCESS_GLOBAL case 'R': selphyneo_send_reset(ctx); break; + case 's': + selphyneo_get_status(ctx); + break; } if (j) return j; @@ -386,25 +496,68 @@ static int selphyneo_cmdline_arg(void *vctx, int argc, char **argv) static void selphyneo_cmdline(void) { DEBUG("\t\t[ -R ] # Reset printer\n"); + DEBUG("\t\t[ -s ] # Query printer status\n"); } +static int selphyneo_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct selphyneo_ctx *ctx = vctx; + struct selphyneo_readback rdback; + int ret, num; + + /* Read in the printer status to clear last state */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &rdback, sizeof(rdback), &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + + /* And again, for the markers */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &rdback, sizeof(rdback), &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + + if (rdback.data[2]) + ctx->marker.levelnow = 0; + else + ctx->marker.levelnow = -3; + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + +static const char *canonselphyneo_prefixes[] = { + "canonselphyneo", // Family name + "canon-cp820", "canon-cp910", "canon-cp1000", "canon-cp1200", "canon-cp1300", + // backwards compatibility + "selphycp820", "selphycp910", "selphycp1000", "selphycp1200", "selphycp1300", + NULL +}; + struct dyesub_backend canonselphyneo_backend = { - .name = "Canon SELPHY CPneo", - .version = "0.08", - .uri_prefix = "canonselphyneo", + .name = "Canon SELPHY CP (new)", + .version = "0.20", + .uri_prefixes = canonselphyneo_prefixes, .cmdline_usage = selphyneo_cmdline, .cmdline_arg = selphyneo_cmdline_arg, .init = selphyneo_init, .attach = selphyneo_attach, + .cleanup_job = selphyneo_cleanup_job, .teardown = selphyneo_teardown, .read_parse = selphyneo_read_parse, .main_loop = selphyneo_main_loop, + .query_markers = selphyneo_query_markers, .devices = { - { USB_VID_CANON, USB_PID_CANON_CP820, P_CP910, ""}, - { USB_VID_CANON, USB_PID_CANON_CP910, P_CP910, ""}, - { USB_VID_CANON, USB_PID_CANON_CP1000, P_CP910, ""}, - { USB_VID_CANON, USB_PID_CANON_CP1200, P_CP910, ""}, - { 0, 0, 0, ""} + { USB_VID_CANON, USB_PID_CANON_CP820, P_CP910, NULL, "canon-cp820"}, + { USB_VID_CANON, USB_PID_CANON_CP910, P_CP910, NULL, "canon-cp910"}, + { USB_VID_CANON, USB_PID_CANON_CP1000, P_CP910, NULL, "canon-cp1000"}, + { USB_VID_CANON, USB_PID_CANON_CP1200, P_CP910, NULL, "canon-cp1200"}, + { USB_VID_CANON, USB_PID_CANON_CP1300, P_CP910, NULL, "canon-cp1300"}, + { 0, 0, 0, NULL, NULL} } }; /* @@ -468,9 +621,11 @@ struct dyesub_backend canonselphyneo_backend = { 00 None 02 No Paper (?) 03 No Paper + 05 Wrong Paper 07 No Ink 09 No Paper and Ink 0A Media/Job mismatch + 0B Paper Jam ZZ == Media? diff --git a/src/cups/backend_citizencw01.c b/src/cups/backend_citizencw01.c deleted file mode 100644 index 46cb8ee..0000000 --- a/src/cups/backend_citizencw01.c +++ /dev/null @@ -1,908 +0,0 @@ -/* - * Citizen CW-01 Photo Printer CUPS backend -- libusb-1.0 version - * - * (c) 2014-2016 Solomon Peachy - * - * 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 -#include -#include -#include - -#include -#include -#include -#include - -#define BACKEND cw01_backend - -#include "backend_common.h" - -#define USB_VID_CITIZEN 0x1343 -#define USB_PID_CITIZEN_CW01 0x0002 // Maybe others? -//#define USB_PID_OLMEC_OP900 XXXX - -/* Private data structure */ -struct cw01_spool_hdr { - uint8_t type; /* 0x00 -> 0x06 */ - uint8_t res; /* vertical resolution; 0x00 == 334dpi, 0x01 == 600dpi */ - uint8_t copies; /* number of prints */ - uint8_t null0; - uint32_t plane_len; /* LE */ - uint8_t null1[4]; -}; -#define DPI_334 0 -#define DPI_600 1 - -#define TYPE_DSC 0 -#define TYPE_L 1 -#define TYPE_PC 2 -#define TYPE_2DSC 3 -#define TYPE_3L 4 -#define TYPE_A5 5 -#define TYPE_A6 6 - -#define SPOOL_PLANE_HDR_LEN 1064 -#define PRINTER_PLANE_HDR_LEN 1088 - -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; -}; - -struct cw01_cmd { - uint8_t esc; /* Fixed at ascii ESC, aka 0x1B */ - uint8_t p; /* Fixed at ascii 'P' aka 0x50 */ - uint8_t arg1[6]; - uint8_t arg2[16]; - uint8_t arg3[8]; /* Decimal value of arg4's length, or empty */ - uint8_t arg4[0]; /* Extra payload if arg3 is non-empty - Doesn't have to be sent in the same URB */ - - /* All unused elements are set to 0x20 (ie ascii space) */ -}; - -#define min(__x, __y) ((__x) < (__y)) ? __x : __y - -static void cw01_build_cmd(struct cw01_cmd *cmd, char *arg1, char *arg2, uint32_t arg3_len) -{ - memset(cmd, 0x20, sizeof(*cmd)); - cmd->esc = 0x1b; - cmd->p = 0x50; - memcpy(cmd->arg1, arg1, min(strlen(arg1), sizeof(cmd->arg1))); - memcpy(cmd->arg2, arg2, min(strlen(arg2), sizeof(cmd->arg2))); - if (arg3_len) { - char buf[9]; - snprintf(buf, sizeof(buf), "%08u", arg3_len); - memcpy(cmd->arg3, buf, 8); - } - -} - -static void cw01_cleanup_string(char *start, int len) -{ - char *ptr = strchr(start, 0x0d); - - if (ptr && (ptr - start < len)) { - *ptr = 0x00; /* If there is a , terminate there */ - len = ptr - start; - } else { - start[--len] = 0x00; /* force null-termination */ - } - - /* Trim trailing spaces */ - while (len && start[len-1] == ' ') { - start[--len] = 0; - } -} - -static char *cw01_media_types(char *str) -{ - char tmp[4]; - int i; - - memcpy(tmp, str + 4, 3); - tmp[3] = 0; - - i = atoi(tmp); - - switch (i) { - case 100: return "UNK 100"; - case 110: return "UNK 110"; - case 200: return "?? 5x3.5 (L)"; - case 210: return "?? 5x7 (2L)"; - case 300: return "?? 6x4 (PC)"; - case 400: return "?? 6x9 (A5W)"; - default: - break; - } - - return "Unknown type"; -} - -static char *cw01_statuses(char *str) -{ - char tmp[6]; - int i; - memcpy(tmp, str, 5); - tmp[5] = 0; - - i = atoi(tmp); - - switch (i) { - case 0: return "Idle"; - case 1: return "Printing"; - case 500: return "Cooling Print Head"; - case 510: return "Cooling Paper Motor"; - case 1000: return "Cover Open"; - case 1010: return "No Scrap Box"; - case 1100: return "Paper End"; - case 1200: return "Ribbon End"; - case 1300: return "Paper Jam"; - case 1400: return "Ribbon Error"; - case 1500: return "Paper Definition Error"; - case 1600: return "Data Error"; - case 2000: return "Head Voltage Error"; - case 2100: return "Head Position Error"; - case 2200: return "Power Supply Fan Error"; - case 2300: return "Cutter Error"; - case 2400: return "Pinch Roller Error"; - case 2500: return "Abnormal Head Temperature"; - case 2600: return "Abnormal Media Temperature"; - case 2610: return "Abnormal Paper Motor Temperature"; - case 2700: return "Ribbon Tension Error"; - case 2800: return "RF-ID Module Error"; - case 3000: return "System Error"; - default: - break; - } - - return "Unknown Error"; -} - -static int cw01_do_cmd(struct cw01_ctx *ctx, - struct cw01_cmd *cmd, - uint8_t *data, int len) -{ - int ret; - - if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*)cmd, sizeof(*cmd)))) - return ret; - - if (data && len) - if ((ret = send_data(ctx->dev, ctx->endp_down, - data, len))) - return ret; - - return CUPS_BACKEND_OK; -} - -static uint8_t *cw01_resp_cmd(struct cw01_ctx *ctx, - struct cw01_cmd *cmd, - int *len) -{ - char tmp[9]; - uint8_t *respbuf; - - int ret, i, num = 0; - - memset(tmp, 0, sizeof(tmp)); - - if ((ret = cw01_do_cmd(ctx, cmd, NULL, 0))) - return NULL; - - /* Read in the response header */ - ret = read_data(ctx->dev, ctx->endp_up, - (uint8_t*)tmp, 8, &num); - if (ret < 0) - return NULL; - - if (num != 8) { - ERROR("Short read! (%d/%d)\n", num, 8); - return NULL; - } - - i = atoi(tmp); /* Length of payload in bytes, possibly padded */ - respbuf = malloc(i); - if (!respbuf) { - ERROR("Memory Allocation Failure!\n"); - return NULL; - } - - /* Read in the actual response */ - ret = read_data(ctx->dev, ctx->endp_up, - respbuf, i, &num); - if (ret < 0) { - free(respbuf); - return NULL; - } - - if (num != i) { - ERROR("Short read! (%d/%d)\n", num, i); - free(respbuf); - return NULL; - } - - *len = num; - return respbuf; -} - -static int cw01_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len) -{ - struct cw01_cmd cmd; - uint8_t *resp; - int len = 0; - - struct cw01_ctx ctx = { - .dev = dev, - .endp_up = endp_up, - .endp_down = endp_down, - }; - - /* Get Serial Number */ - cw01_build_cmd(&cmd, "INFO", "SERIAL_NUMBER", 0); - - resp = cw01_resp_cmd(&ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - strncpy(buf, (char*)resp, buf_len); - buf[buf_len-1] = 0; - - free(resp); - - return CUPS_BACKEND_OK; -} - -static void *cw01_init(void) -{ - struct cw01_ctx *ctx = malloc(sizeof(struct cw01_ctx)); - if (!ctx) { - ERROR("Memory Allocation Failure!\n"); - return NULL; - } - memset(ctx, 0, sizeof(struct cw01_ctx)); - - return ctx; -} - -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) { - struct cw01_ctx *ctx = vctx; - - if (!ctx) - return; - - if (ctx->databuf) - free(ctx->databuf); - free(ctx); -} - -static int cw01_read_parse(void *vctx, int data_fd) { - struct cw01_ctx *ctx = vctx; - int i, j, remain; - - if (!ctx) - return CUPS_BACKEND_FAILED; - - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; - } - - i = read(data_fd, (uint8_t*) &ctx->hdr, sizeof(struct cw01_spool_hdr)); - - if (i < 0) - return i; - if (i == 0) - return CUPS_BACKEND_CANCEL; - - if (i < (int)sizeof(struct cw01_spool_hdr)) - return CUPS_BACKEND_CANCEL; - - if (ctx->hdr.type > 0x06 || ctx->hdr.res > 0x01) { - ERROR("Unrecognized header data format!\n"); - return CUPS_BACKEND_CANCEL; - } - ctx->hdr.plane_len = le32_to_cpu(ctx->hdr.plane_len); - remain = ctx->hdr.plane_len * 3; - ctx->databuf = malloc(remain); - if (!ctx->databuf) { - ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_CANCEL; - } - - j = 0; - while (remain) { - i = read(data_fd, ctx->databuf + j, remain); - - if (i < 0) - return i; - - remain -= i; - j += i; - } - - return CUPS_BACKEND_OK; -} - -static int cw01_main_loop(void *vctx, int copies) { - struct cw01_ctx *ctx = vctx; - int ret; - struct cw01_cmd cmd; - uint8_t *resp = NULL; - int len = 0; - uint32_t tmp; - uint8_t *ptr; - char buf[9]; - uint8_t plane_hdr[PRINTER_PLANE_HDR_LEN]; - - if (!ctx) - return CUPS_BACKEND_FAILED; - -top: - - if (resp) free(resp); - - /* Query status */ - cw01_build_cmd(&cmd, "STATUS", "", 0); - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - cw01_cleanup_string((char*)resp, len); - - /* If we're not idle */ - if (strcmp("00000", (char*)resp)) { - if (!strcmp("00001", (char*)resp)) { - free(resp); - /* Query buffer state */ - cw01_build_cmd(&cmd, "INFO", "FREE_PBUFFER", 0); - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - cw01_cleanup_string((char*)resp, len); - - /* Check to see if we have sufficient buffers */ - // XXX audit these rules...? - if (!strcmp("FBP00", (char*)resp) || - (ctx->hdr.res == DPI_600 && !strcmp("FBP01", (char*)resp))) { - INFO("Insufficient printer buffers, retrying...\n"); - sleep(1); - goto top; - } - } else { - ERROR("Printer Status: %s\n", cw01_statuses((char*)resp)); - free(resp); - return CUPS_BACKEND_RETRY_CURRENT; - } - } - - free(resp); - /* Get Vertical resolution */ - cw01_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - -#if 0 - if (ctx->hdr.res == DPI_600 && strcmp("RV0334", *char*)resp) { - ERROR("600DPI prints not yet supported, need 600DPI CWD load"); - return CUPS_BACKEND_CANCEL; - } -#endif - - free(resp); - resp = NULL; - - /* 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); - if (ret) - return CUPS_BACKEND_FAILED; - - /* Cutter control. ??? */ - // cw01_build_cmd(&cmd, "CNTRL", "CUTTER", 8); - //snprintf(buf, sizeof(buf), "%08d", ???); - //ret = cw01_do_cmd(ctx, &cmd, (uint8_t*) buf, 8); - //if (ret) - // return CUPS_BACKEND_FAILED; - - /* Start sending image data */ - ptr = ctx->databuf; - - /* Generate plane header (same for all planes) */ - tmp = cpu_to_le32(ctx->hdr.plane_len) + 24; - memset(plane_hdr, 0, PRINTER_PLANE_HDR_LEN); - plane_hdr[0] = 0x42; - plane_hdr[1] = 0x4d; - memcpy(plane_hdr + 2, &tmp, sizeof(tmp)); - plane_hdr[10] = 0x40; - plane_hdr[11] = 0x04; - memcpy(plane_hdr + 14, ptr, SPOOL_PLANE_HDR_LEN); - - /******** Plane 1 */ - cw01_build_cmd(&cmd, "IMAGE", "YPLANE", ctx->hdr.plane_len - SPOOL_PLANE_HDR_LEN + PRINTER_PLANE_HDR_LEN); - ret = cw01_do_cmd(ctx, &cmd, plane_hdr, PRINTER_PLANE_HDR_LEN); - if (ret) - return CUPS_BACKEND_FAILED; - - /* Send plane data */ - if ((ret = send_data(ctx->dev, ctx->endp_down, - ptr + SPOOL_PLANE_HDR_LEN, ctx->hdr.plane_len - SPOOL_PLANE_HDR_LEN))) - return CUPS_BACKEND_FAILED; - - ptr += ctx->hdr.plane_len; - - /******** Plane 2 */ - cw01_build_cmd(&cmd, "IMAGE", "MPLANE", ctx->hdr.plane_len - SPOOL_PLANE_HDR_LEN + PRINTER_PLANE_HDR_LEN); - ret = cw01_do_cmd(ctx, &cmd, plane_hdr, PRINTER_PLANE_HDR_LEN); - if (ret) - return CUPS_BACKEND_FAILED; - - /* Send plane data */ - if ((ret = send_data(ctx->dev, ctx->endp_down, - ptr + SPOOL_PLANE_HDR_LEN, ctx->hdr.plane_len - SPOOL_PLANE_HDR_LEN))) - return CUPS_BACKEND_FAILED; - - ptr += ctx->hdr.plane_len; - - /******** Plane 3 */ - cw01_build_cmd(&cmd, "IMAGE", "CPLANE", ctx->hdr.plane_len - SPOOL_PLANE_HDR_LEN + PRINTER_PLANE_HDR_LEN); - ret = cw01_do_cmd(ctx, &cmd, plane_hdr, PRINTER_PLANE_HDR_LEN); - if (ret) - return CUPS_BACKEND_FAILED; - - /* Send plane data */ - if ((ret = send_data(ctx->dev, ctx->endp_down, - ptr + SPOOL_PLANE_HDR_LEN, ctx->hdr.plane_len - SPOOL_PLANE_HDR_LEN))) - return CUPS_BACKEND_FAILED; - - /* ptr += ctx->hdr.plane_len; */ - - /* Start print */ - cw01_build_cmd(&cmd, "CNTRL", "START", 0); - ret = cw01_do_cmd(ctx, &cmd, NULL, 0); - if (ret) - return CUPS_BACKEND_FAILED; - - INFO("Print complete\n"); - - if (resp) free(resp); - - return CUPS_BACKEND_OK; -} - -static int cw01_get_info(struct cw01_ctx *ctx) -{ - struct cw01_cmd cmd; - uint8_t *resp; - int len = 0; - - /* Get Serial Number */ - cw01_build_cmd(&cmd, "INFO", "SERIAL_NUMBER", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Serial Number: '%s'\n", (char*)resp); - - free(resp); - - /* Get Firmware Version */ - cw01_build_cmd(&cmd, "INFO", "FVER", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Firmware Version: '%s'\n", (char*)resp); - - free(resp); - - /* Get Sensor Info */ - cw01_build_cmd(&cmd, "INFO", "SENSOR", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Sensor Info: '%s'\n", (char*)resp); - // XXX parse this out. Each token is 'XXX-###' delimited by '; ' - - free(resp); - - /* Get Horizonal resolution */ - cw01_build_cmd(&cmd, "INFO", "RESOLUTION_H", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Horizontal Resolution: '%s' dpi\n", (char*)resp + 3); - - free(resp); - - /* Get Vertical resolution */ - cw01_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Vertical Resolution: '%s' dpi\n", (char*)resp + 3); - - free(resp); - - /* Get Media Color offset */ - cw01_build_cmd(&cmd, "INFO", "MCOLOR", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Media Color Offset: '%02x%02x%02x%02x'\n", *(resp+2), *(resp+3), - *(resp+4), *(resp+5)); - - free(resp); - - /* Get Media Lot */ - cw01_build_cmd(&cmd, "INFO", "MLOT", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Media Lot Code: '%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x'\n", - *(resp+2), *(resp+3), *(resp+4), *(resp+5), *(resp+6), *(resp+7), - *(resp+8), *(resp+9), *(resp+10), *(resp+11), *(resp+12), *(resp+13)); - - free(resp); - - /* Get Media ID Set (?) */ - cw01_build_cmd(&cmd, "MNT_RD", "MEDIA_ID_SET", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Media ID(?): '%s'\n", (char*)resp+4); - - free(resp); - - /* Get Color Control Data Version */ - cw01_build_cmd(&cmd, "TBL_RD", "Version", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Color Data Version: '%s'\n", (char*)resp); - - free(resp); - - /* Get Color Control Data Checksum */ - cw01_build_cmd(&cmd, "MNT_RD", "CTRLD_CHKSUM", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Color Data Checksum: '%s'\n", (char*)resp); - - free(resp); - - return CUPS_BACKEND_OK; -} - -static int cw01_get_status(struct cw01_ctx *ctx) -{ - struct cw01_cmd cmd; - uint8_t *resp; - int len = 0; - - /* Generate command */ - cw01_build_cmd(&cmd, "STATUS", "", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Printer Status: %s => %s\n", (char*)resp, cw01_statuses((char*)resp)); - - free(resp); - - /* Get remaining prints in this job */ - cw01_build_cmd(&cmd, "INFO", "PQTY", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Prints Remaining in job: '%s'\n", (char*)resp + 4); - - free(resp); - - /* Generate command */ - cw01_build_cmd(&cmd, "INFO", "FREE_PBUFFER", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Free Buffers: '%s'\n", (char*)resp + 3); - - free(resp); - - /* Get Media Info */ - cw01_build_cmd(&cmd, "INFO", "MEDIA", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Media Type: '%s'\n", cw01_media_types((char*)resp)); - - free(resp); - - /* Get Media remaining */ - cw01_build_cmd(&cmd, "INFO", "MQTY", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Prints Remaining: '%s'\n", (char*)resp + 4); - - free(resp); - - return 0; -} - -static int cw01_get_counters(struct cw01_ctx *ctx) -{ - struct cw01_cmd cmd; - uint8_t *resp; - int len = 0; - - /* Generate command */ - cw01_build_cmd(&cmd, "MNT_RD", "COUNTER_LIFE", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("Lifetime Counter: '%s'\n", (char*)resp+2); - - free(resp); - - /* Generate command */ - cw01_build_cmd(&cmd, "MNT_RD", "COUNTER_A", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("A Counter: '%s'\n", (char*)resp+2); - - free(resp); - - /* Generate command */ - cw01_build_cmd(&cmd, "MNT_RD", "COUNTER_B", 0); - - resp = cw01_resp_cmd(ctx, &cmd, &len); - if (!resp) - return CUPS_BACKEND_FAILED; - - cw01_cleanup_string((char*)resp, len); - - INFO("B Counter: '%s'\n", (char*)resp+2); - - free(resp); - - return CUPS_BACKEND_OK; -} - -static int cw01_clear_counter(struct cw01_ctx *ctx, char counter) -{ - struct cw01_cmd cmd; - char msg[4]; - int ret; - - /* Generate command */ - cw01_build_cmd(&cmd, "MNT_WT", "COUNTER_CLEAR", 4); - msg[0] = 'C'; - msg[1] = counter; - msg[2] = 0x0d; /* ie carriage return, ASCII '\r' */ - msg[3] = 0x00; - - if ((ret = cw01_do_cmd(ctx, &cmd, (uint8_t*)msg, 4))) - return ret; - - return 0; -} - - -static void cw01_cmdline(void) -{ - DEBUG("\t\t[ -i ] # Query printer 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"); -} - -static int cw01_cmdline_arg(void *vctx, int argc, char **argv) -{ - struct cw01_ctx *ctx = vctx; - int i, j = 0; - - if (!ctx) - return -1; - - while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "inN:s")) >= 0) { - switch(i) { - GETOPT_PROCESS_GLOBAL - case 'i': - j = cw01_get_info(ctx); - break; - case 'n': - j = cw01_get_counters(ctx); - break; - case 'N': - if (optarg[0] != 'A' && - optarg[0] != 'B') - return CUPS_BACKEND_FAILED; - j = cw01_clear_counter(ctx, optarg[0]); - break; - case 's': - j = cw01_get_status(ctx); - break; - default: - break; /* Ignore completely */ - } - - if (j) return j; - } - - return 0; -} - -/* Exported */ -struct dyesub_backend cw01_backend = { - .name = "Citizen CW-01", - .version = "0.12", - .uri_prefix = "citizencw01", - .cmdline_usage = cw01_cmdline, - .cmdline_arg = cw01_cmdline_arg, - .init = cw01_init, - .attach = cw01_attach, - .teardown = cw01_teardown, - .read_parse = cw01_read_parse, - .main_loop = cw01_main_loop, - .query_serno = cw01_query_serno, - .devices = { - { USB_VID_CITIZEN, USB_PID_CITIZEN_CW01, P_CITIZEN_CW01, ""}, -// { USB_VID_CITIZEN, USB_PID_OLMEC_OP900, P_CITIZEN_CW01, ""}, - { 0, 0, 0, ""} - } -}; - -/* - -Basic spool file format: - -TT RR NN 00 XX XX XX XX 00 00 00 00 <- FILE header. - - NN : copies (0x01 or more) - RR : resolution; 0 == 334 dpi, 1 == 600dpi - TT : type 0x02 == 4x6, 0x01 == 5x3.5 - XX XX XX XX : plane length (LE) - plane length * 3 + 12 == file length. - -Followed by three planes, each with this header: - -28 00 00 00 00 08 00 00 RR RR 00 00 01 00 08 00 -00 00 00 00 00 00 00 00 5a 33 00 00 YY YY 00 00 -00 01 00 00 00 00 00 00 - - RR RR : rows in LE format - YY YY : 0x335a (334dpi) or 0x5c40 (600dpi) - -Followed by 1024 bytes of color tables: - - ff ff ff 00 ... 00 00 00 00 - -1024+40 = 1064 bytes of header per plane. - -Always have 2048 columns of data. - -followed by (2048 * rows) bytes of data. - -*/ diff --git a/src/cups/backend_common.c b/src/cups/backend_common.c index d88a7d8..09ea078 100644 --- a/src/cups/backend_common.c +++ b/src/cups/backend_common.c @@ -1,7 +1,7 @@ /* * CUPS Backend common code * - * Copyright (c) 2007-2017 Solomon Peachy + * Copyright (c) 2007-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,22 +18,29 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include "backend_common.h" -#define BACKEND_VERSION "0.71G" +#define BACKEND_VERSION "0.88G" #ifndef URI_PREFIX #error "Must Define URI_PREFIX" #endif #define NUM_CLAIM_ATTEMPTS 10 +#define URB_XFER_SIZE (64*1024) +#define XFER_TIMEOUT 15000 + +#define USB_SUBCLASS_PRINTER 0x1 +#define USB_INTERFACE_PROTOCOL_BIDIR 0x2 + /* Global Variables */ int dyesub_debug = 0; int terminate = 0; @@ -42,11 +49,16 @@ int extra_vid = -1; int extra_pid = -1; int extra_type = -1; int copies = 1; +int test_mode = 0; +int old_uri = 0; + +static int max_xfer_size = URB_XFER_SIZE; +static int xfer_timeout = XFER_TIMEOUT; /* Support Functions */ -static int backend_claim_interface(struct libusb_device_handle *dev, int iface) +static int backend_claim_interface(struct libusb_device_handle *dev, int iface, + int num_claim_attempts) { - int attempts = NUM_CLAIM_ATTEMPTS; int ret; do { ret = libusb_claim_interface(dev, iface); @@ -54,15 +66,41 @@ static int backend_claim_interface(struct libusb_device_handle *dev, int iface) break; if (ret != LIBUSB_ERROR_BUSY) break; + if (--num_claim_attempts == 0) + break; sleep(1); - } while (--attempts > 0); + } while (1); if (ret) - ERROR("Printer open failure (Could not claim printer interface after %d attempts) (%d)\n", NUM_CLAIM_ATTEMPTS, ret); + ERROR("Failed to claim interface %d (%d)\n", iface, ret); return ret; } +static int lookup_printer_type(struct dyesub_backend *backend, uint16_t idVendor, uint16_t idProduct) +{ + int i; + int type = P_UNKNOWN; + + 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; +} + /* Interface **MUST** already be claimed! */ #define ID_BUF_SIZE 2048 static char *get_device_id(struct libusb_device_handle *dev, int iface) @@ -103,6 +141,9 @@ static char *get_device_id(struct libusb_device_handle *dev, int iface) goto done; } + /* IEEE1284 length field includs the header! */ + length -= 2; + /* Move, and terminate */ memmove(buf, buf + 2, length); buf[length] = '\0'; @@ -194,7 +235,7 @@ int read_data(struct libusb_device_handle *dev, uint8_t endp, buf, buflen, readlen, - 10000); + xfer_timeout); if (ret < 0) { ERROR("Failure to receive data from printer (libusb error %d: (%d/%d from 0x%02x))\n", ret, *readlen, buflen, endp); @@ -227,7 +268,7 @@ done: } int send_data(struct libusb_device_handle *dev, uint8_t endp, - uint8_t *buf, int len) + const uint8_t *buf, int len) { int num = 0; @@ -236,10 +277,10 @@ int send_data(struct libusb_device_handle *dev, uint8_t endp, } while (len) { - int len2 = (len > 65536) ? 65536: len; + int len2 = (len > max_xfer_size) ? max_xfer_size: len; int ret = libusb_bulk_transfer(dev, endp, - buf, len2, - &num, 15000); + (uint8_t*) buf, len2, + &num, xfer_timeout); if ((dyesub_debug > 1 && len < 4096) || dyesub_debug > 2) { @@ -350,27 +391,29 @@ static char *url_decode(char *str) { /* And now back to our regularly-scheduled programming */ -static int print_scan_output(struct libusb_device *device, - struct libusb_device_descriptor *desc, - char *prefix, char *manuf2, - int found, - int scan_only, char *match_serno, - struct dyesub_backend *backend) +static int probe_device(struct libusb_device *device, + struct libusb_device_descriptor *desc, + const char *uri_prefix, + const char *prefix, char *manuf_override, + int found, int num_claim_attempts, + int scan_only, char *match_serno, + uint8_t *r_iface, uint8_t *r_altset, + uint8_t *r_endp_up, uint8_t *r_endp_down, + struct dyesub_backend *backend) { struct libusb_device_handle *dev; char buf[256]; char *product = NULL, *serial = NULL, *manuf = NULL, *descr = NULL; - int iface = 0; // XXX loop through interfaces - int altset = 0; // XXX loop through altsetting + uint8_t iface, altset; struct libusb_config_descriptor *config = NULL; int dlen = 0; struct deviceid_dict dict[MAX_DICT]; char *ieee_id = NULL; int i; - uint8_t endp_up, endp_down; DEBUG("Probing VID: %04X PID: %04x\n", desc->idVendor, desc->idProduct); + STATE("+connecting-to-device\n"); if (libusb_open(device, &dev)) { ERROR("Could not open device %04x:%04x (need to be root?)\n", desc->idVendor, desc->idProduct); @@ -378,43 +421,86 @@ static int print_scan_output(struct libusb_device *device, goto abort; } + /* XXX FIXME: Iterate through possible configurations? */ + if (libusb_get_active_config_descriptor(device, &config)) { + found = -1; + goto abort_close; + } + + /* Loop through all interfaces and altsettings to find candidates */ + for (iface = 0 ; iface < config->bNumInterfaces ; iface ++) { + for (altset = 0 ; altset < config->interface[iface].num_altsetting ; altset++) { + /* Skip interfaces that don't have enough endpoints */ + if (config->interface[iface].altsetting[altset].bNumEndpoints < 2) { + continue; + } + +#if 0 + // Make sure it's a printer class device that supports bidir comms (XXX Is this always true?) + if (desc->bDeviceClass == LIBUSB_CLASS_PRINTER || + (desc->bDeviceClass == LIBUSB_CLASS_PER_INTERFACE && + config->interface[iface].altsetting[altset].bInterfaceClass == LIBUSB_CLASS_PRINTER && + config->interface[iface].altsetting[altset].bInterfaceSubClass == USB_SUBCLASS_PRINTER && + config->interface[iface].altsetting[altset].bInterfaceProtocol != USB_INTERFACE_PROTOCOL_BIDIR)) { + continue; + } +#endif + + /* Find the first set of endpoints! */ + endp_up = endp_down = 0; + for (i = 0 ; i < config->interface[iface].altsetting[altset].bNumEndpoints ; i++) { + if ((config->interface[iface].altsetting[altset].endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) { + if (config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_IN) + endp_up = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress; + else + endp_down = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress; + } + if (endp_up && endp_down) + goto candidate; + } + } + } + + /* If we got here, we didn't find a match. */ + found = -1; + goto abort_close; + +candidate: + + /* We've now found an interface/altset we need to query in more detail */ + /* Detach the kernel driver */ if (libusb_kernel_driver_active(dev, iface)) libusb_detach_kernel_driver(dev, iface); - if (backend_claim_interface(dev, iface)) { + /* Claim the interface so we can start querying things! */ + if (backend_claim_interface(dev, iface, num_claim_attempts)) { found = -1; - goto abort_close; - } - - if (libusb_get_active_config_descriptor(device, &config)) { - found = -1; goto abort_release; } - /* Find the endpoints */ - endp_up = endp_down = 0; - for (i = 0 ; i < config->interface[iface].altsetting[altset].bNumEndpoints ; i++) { - if ((config->interface[iface].altsetting[altset].endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) { - if (config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_IN) - endp_up = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress; - else - endp_down = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress; + /* Use the appropriate altesetting, but only if the + printer supports more than one. Some printers don't like + us unconditionally setting this. */ + if (config->interface[iface].num_altsetting > 1) { + if (libusb_set_interface_alt_setting(dev, iface, altset)) { + ERROR("Failed to set alternative interface %d/%d\n", iface, altset); + found = -1; + goto abort_release; } - if (endp_up && endp_down) - break; } /* Query IEEE1284 info only if it's a PRINTER class */ if (desc->bDeviceClass == LIBUSB_CLASS_PRINTER || (desc->bDeviceClass == LIBUSB_CLASS_PER_INTERFACE && - config->interface[iface].altsetting[altset].bInterfaceClass == LIBUSB_CLASS_PRINTER)) { + config->interface[iface].altsetting[altset].bInterfaceClass == LIBUSB_CLASS_PRINTER && + config->interface[iface].altsetting[altset].bInterfaceSubClass == USB_SUBCLASS_PRINTER)) { ieee_id = get_device_id(dev, iface); dlen = parse1284_data(ieee_id, dict); } /* Look up mfg string. */ - if (manuf2 && strlen(manuf2)) { - manuf = url_encode(manuf2); /* Backend supplied */ + if (manuf_override && strlen(manuf_override)) { + manuf = url_encode(manuf_override); /* Backend supplied */ } else if ((manuf = dict_find("MANUFACTURER", dlen, dict))) { manuf = url_encode(manuf); } else if ((manuf = dict_find("MFG", dlen, dict))) { @@ -502,20 +588,27 @@ static int print_scan_output(struct libusb_device *device, } if (scan_only) { - int k = 0; + if (!old_uri) { + fprintf(stdout, "direct %s://%s/%s \"%s\" \"%s\" \"%s\" \"\"\n", + prefix, uri_prefix, serial, + descr, descr, + ieee_id ? ieee_id : ""); + } else { + int k = 0; - /* URLify the manuf and model strings */ - strncpy(buf, manuf, sizeof(buf) - 2); - k = strlen(buf); - buf[k++] = '/'; - buf[k] = 0; + /* URLify the manuf and model strings */ + strncpy(buf, manuf, sizeof(buf) - 2); + k = strlen(buf); + buf[k++] = '/'; + buf[k] = 0; - strncpy(buf + k, product, sizeof(buf)-k); + strncpy(buf + k, product, sizeof(buf)-k); - fprintf(stdout, "direct %s://%s?serial=%s&backend=%s \"%s\" \"%s\" \"%s\" \"\"\n", - prefix, buf, serial, backend->uri_prefix, - descr, descr, - ieee_id? ieee_id : ""); + fprintf(stdout, "direct %s://%s?serial=%s&backend=%s \"%s\" \"%s\" \"%s\" \"\"\n", + prefix, buf, serial, uri_prefix, + descr, descr, + ieee_id? ieee_id : ""); + } } /* If a serial number was passed down, use it. */ @@ -527,6 +620,13 @@ static int print_scan_output(struct libusb_device *device, DEBUG("VID: %04X PID: %04X Manuf: '%s' Product: '%s' Serial: '%s' found: %d\n", desc->idVendor, desc->idProduct, manuf, product, serial, found); + if (found != -1) { + if (r_iface) *r_iface = iface; + if (r_altset) *r_altset = altset; + if (r_endp_up) *r_endp_up = endp_up; + if (r_endp_up) *r_endp_down = endp_down; + } + /* Free things up */ if(serial) free(serial); if(manuf) free(manuf); @@ -534,8 +634,6 @@ static int print_scan_output(struct libusb_device *device, if(descr) free(descr); if(ieee_id) free(ieee_id); - if (config) libusb_free_config_descriptor(config); - abort_release: libusb_release_interface(dev, iface); @@ -543,13 +641,18 @@ abort_release: abort_close: libusb_close(dev); + abort: + if (config) libusb_free_config_descriptor(config); + /* Clean up the dictionary */ while (dlen--) { free (dict[dlen].key); free (dict[dlen].val); } + STATE("-connecting-to-device\n"); + return found; } @@ -567,7 +670,8 @@ extern struct dyesub_backend mitsu70x_backend; extern struct dyesub_backend mitsu9550_backend; extern struct dyesub_backend mitsup95d_backend; extern struct dyesub_backend dnpds40_backend; -extern struct dyesub_backend cw01_backend; +extern struct dyesub_backend magicard_backend; +extern struct dyesub_backend mitsud90_backend; static struct dyesub_backend *backends[] = { &canonselphy_backend, @@ -581,10 +685,11 @@ static struct dyesub_backend *backends[] = { &shinkos6245_backend, &updr150_backend, &mitsu70x_backend, + &mitsud90_backend, &mitsu9550_backend, &mitsup95d_backend, &dnpds40_backend, - &cw01_backend, + &magicard_backend, NULL, }; @@ -592,11 +697,25 @@ static int find_and_enumerate(struct libusb_context *ctx, struct libusb_device ***list, struct dyesub_backend *backend, char *match_serno, - int scan_only) + int scan_only, int num_claim_attempts, + uint8_t *r_iface, uint8_t *r_altset, + uint8_t *r_endp_up, uint8_t *r_endp_down) { int num; int i, j = 0, k; int found = -1; + const char *prefix = NULL; + + if (test_mode >= TEST_MODE_NOATTACH) { + found = 1; + *r_endp_up = 0x82; + *r_endp_down = 0x01; + *r_iface = 0; + *r_altset = 0; + return found; + } + + STATE("+org.gutenprint-searching-for-device\n"); /* Enumerate and find suitable device */ num = libusb_get_device_list(ctx, list); @@ -616,11 +735,14 @@ static int find_and_enumerate(struct libusb_context *ctx, extra_vid == desc.idVendor && extra_pid == desc.idProduct) { found = i; + prefix = backends[k]->uri_prefixes[0]; goto match; } } if (desc.idVendor == backends[k]->devices[j].vid && - desc.idProduct == backends[k]->devices[j].pid) { + (desc.idProduct == backends[k]->devices[j].pid || + desc.idProduct == 0xffff)) { + prefix = backends[k]->devices[j].prefix; found = i; goto match; } @@ -630,16 +752,19 @@ static int find_and_enumerate(struct libusb_context *ctx, continue; match: - found = print_scan_output((*list)[i], &desc, - URI_PREFIX, backends[k]->devices[j].manuf_str, - found, - scan_only, match_serno, - backends[k]); + found = probe_device((*list)[i], &desc, prefix, + URI_PREFIX, backends[k]->devices[j].manuf_str, + found, num_claim_attempts, + scan_only, match_serno, + r_iface, r_altset, + r_endp_up, r_endp_down, + backends[k]); if (found != -1 && !scan_only) break; } + STATE("-org.gutenprint-searching-for-device\n"); return found; } @@ -652,22 +777,46 @@ static struct dyesub_backend *find_backend(char *uri_prefix) for (i = 0; ; i++) { struct dyesub_backend *backend = backends[i]; + const char **alias; if (!backend) return NULL; - if (!strcmp(uri_prefix, backend->uri_prefix)) - return backend; + for (alias = backend->uri_prefixes ; alias && *alias ; alias++) { + if (!strcmp(uri_prefix, *alias)) + return backend; + } } return NULL; } +static int query_markers(struct dyesub_backend *backend, void *ctx, int full) +{ + struct marker *markers = NULL; + int marker_count = 0; + int ret; + + if (!backend->query_markers) + return CUPS_BACKEND_OK; + + if (test_mode >= TEST_MODE_NOPRINT) + return CUPS_BACKEND_OK; + + ret = backend->query_markers(ctx, &markers, &marker_count); + if (ret) + return ret; + + dump_markers(markers, marker_count, full); + + return CUPS_BACKEND_OK; +} + void print_license_blurb(void) { const char *license = "\n\ -Copyright 2007-2017 Solomon Peachy \n\ +Copyright 2007-2018 Solomon Peachy \n\ \n\ This program is free software; you can redistribute it and/or modify it\n\ under the terms of the GNU General Public License as published by the Free\n\ -Software Foundation; either version 2 of the License, or (at your option)\n\ +Software Foundation; either version 3 of the License, or (at your option)\n\ any later version.\n\ \n\ This program is distributed in the hope that it will be useful, but\n\ @@ -676,8 +825,7 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n\ for more details.\n\ \n\ You should have received a copy of the GNU General Public License\n\ -along with this program; if not, write to the Free Software\n\ -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n\ +along with this program. If not, see https://www.gnu.org/licenses/.\n\ \n [http://www.gnu.org/licenses/gpl-2.0.html]\n\n"; fprintf(stderr, "%s", license); @@ -687,7 +835,6 @@ void print_help(char *argv0, struct dyesub_backend *backend) { struct libusb_context *ctx = NULL; struct libusb_device **list = NULL; - int i; char *ptr = strrchr(argv0, '/'); if (ptr) @@ -701,7 +848,7 @@ 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(" DYESUB_DEBUG EXTRA_PID EXTRA_VID EXTRA_TYPE BACKEND SERIAL OLD_URI_SCHEME\n"); DEBUG("CUPS Usage:\n"); DEBUG("\tDEVICE_URI=someuri %s job user title num-copies options [ filename ]\n", URI_PREFIX); DEBUG("\n"); @@ -712,18 +859,30 @@ void print_help(char *argv0, struct dyesub_backend *backend) DEBUG(" [ -d copies ] \n"); DEBUG(" [ - | infile ] \n"); for (i = 0; ; i++) { + const char **alias; + backend = backends[i]; if (!backend) break; - DEBUG(" BACKEND=%s\t# %s version %s\n", - backend->uri_prefix, backend->name, backend->version); + DEBUG("\t# %s version %s\n", + backend->name, backend->version); + DEBUG(" BACKEND="); + for (alias = backend->uri_prefixes ; alias && *alias ; alias++) + DEBUG2("%s ", *alias); + DEBUG2("\n"); + if (backend->cmdline_usage) backend->cmdline_usage(); } } else { + const char **alias; DEBUG("Standalone %s backend version %s\n", backend->name, backend->version); - DEBUG("\t%s\n", backend->uri_prefix); + DEBUG("\t supporting: "); + for (alias = backend->uri_prefixes ; alias && *alias ; alias++) + DEBUG2("%s ", *alias); + DEBUG2("\n"); + DEBUG("\t[ -D ] [ -G ] [ -f ]\n"); if (backend->cmdline_usage) backend->cmdline_usage(); @@ -731,35 +890,67 @@ void print_help(char *argv0, struct dyesub_backend *backend) } /* 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, 1); + find_and_enumerate(ctx, &list, backend, NULL, 1, 1, NULL, NULL, NULL, NULL); libusb_free_device_list(list, 1); - libusb_exit(ctx); } +int parse_cmdstream(struct dyesub_backend *backend, void *backend_ctx, int fd) +{ + FILE *fp = stdin; + char line[128]; + char *lp; + + if (fd != fileno(stdin)) { + fp = fdopen(fd, "r"); + if (!fp) { + ERROR("Can't open data stream!\n"); + return CUPS_BACKEND_FAILED; + } + } + while (fgets(line, sizeof(line), fp) != NULL) { + /* Strip trailing newline */ + lp = line + strlen(line) - 1; + if (*lp == '\n') + *lp = '\0'; + /* And leading spaces */ + for (lp = line; isspace(*lp); lp++); + /* And comments and blank lines */ + if (*lp == '#' || !*lp) + continue; + + /* Parse command! */ + if (strncasecmp(lp, "ReportLevels", 12) == 0) { + query_markers(backend, backend_ctx, 1); +/* XXX TODO: ReportStatus, AutoConfigure, PrintSelfTestPage? What about others, eg reset or cancel job? */ + } else { + WARNING("Invalid printer command \"%s\"!\n", lp); + } + } + + /* Clean up */ + if (fp != stdin) + fclose(fp); + + return CUPS_BACKEND_OK; +}; + int main (int argc, char **argv) { struct libusb_context *ctx = NULL; struct libusb_device **list = NULL; struct libusb_device_handle *dev; - struct libusb_config_descriptor *config = NULL; struct dyesub_backend *backend = NULL; void * backend_ctx = NULL; uint8_t endp_up, endp_down; - - int iface = 0; // XXX loop through interfaces - int altset = 0; // XXX loop through altsetting + uint8_t iface, altset; int data_fd = fileno(stdin); + const void *job = NULL; + int i; - int claimed; int ret = CUPS_BACKEND_OK; @@ -768,12 +959,14 @@ int main (int argc, char **argv) int current_page = 0; char *uri; + char *type; char *fname = NULL; char *use_serno = NULL; + int printer_type; DEBUG("Multi-Call Dye-sublimation CUPS Backend version %s\n", BACKEND_VERSION); - DEBUG("Copyright 2007-2016 Solomon Peachy\n"); + DEBUG("Copyright 2007-2018 Solomon Peachy\n"); DEBUG("This free software comes with ABSOLUTELY NO WARRANTY! \n"); DEBUG("Licensed under the GNU GPL. Run with '-G' for more details.\n"); DEBUG("\n"); @@ -784,15 +977,30 @@ int main (int argc, char **argv) if (getenv("EXTRA_PID")) extra_pid = strtol(getenv("EXTRA_PID"), NULL, 16); if (getenv("EXTRA_VID")) - extra_pid = strtol(getenv("EXTRA_VID"), NULL, 16); - if (getenv("EXTRA_PID")) + extra_vid = strtol(getenv("EXTRA_VID"), NULL, 16); + if (getenv("EXTRA_TYPE")) extra_type = atoi(getenv("EXTRA_TYPE")); if (getenv("BACKEND")) backend = find_backend(getenv("BACKEND")); if (getenv("FAST_RETURN")) fast_return++; + if (getenv("MAX_XFER_SIZE")) + max_xfer_size = atoi(getenv("MAX_XFER_SIZE")); + if (getenv("XFER_TIMEOUT")) + xfer_timeout = atoi(getenv("XFER_TIMEOUT")); + if (getenv("TEST_MODE")) + test_mode = atoi(getenv("TEST_MODE")); + if (getenv("OLD_URI_SCHEME")) + old_uri = atoi(getenv("OLD_URI_SCHEME")); + + if (test_mode >= TEST_MODE_NOATTACH && (extra_vid == -1 || extra_pid == -1)) { + ERROR("Must specify EXTRA_VID, EXTRA_PID in test mode > 1!\n"); + exit(1); + } + use_serno = getenv("SERIAL"); - uri = getenv("DEVICE_URI"); /* CUPS backend mode? */ + uri = getenv("DEVICE_URI"); /* CUPS backend mode! */ + type = getenv("FINAL_CONTENT_TYPE"); /* CUPS content type -- ie raster or command */ if (uri) { /* CUPS backend mode */ @@ -812,35 +1020,52 @@ int main (int argc, char **argv) /* Figure out backend based on URI */ { - char *ptr = strstr (uri, "backend="), *ptr2; - if (!ptr) { - ERROR("Invalid URI prefix (%s)\n", uri); - exit(1); - } - ptr += 8; - ptr2 = strchr(ptr, '&'); - if (ptr2) - *ptr2 = 0; + char *ptr = strstr(uri, "backend="), *ptr2; + if (ptr) { /* Original format */ + ptr += 8; + ptr2 = strchr(ptr, '&'); + if (ptr2) + *ptr2 = 0; + + backend = find_backend(ptr); + if (!backend) { + ERROR("Invalid backend (%s)\n", ptr); + exit(1); + } + if (ptr2) + *ptr2 = '&'; - backend = find_backend(ptr); - if (!backend) { - ERROR("Invalid backend (%s)\n", ptr); - exit(1); - } - if (ptr2) - *ptr2 = '&'; - } + use_serno = strchr(uri, '='); + if (!use_serno || !*(use_serno+1)) { + ERROR("Invalid URI (%s)\n", uri); + exit(1); + } + use_serno++; + ptr = strchr(use_serno, '&'); + if (ptr) + *ptr = 0; + } else { /* New format */ + // prefix://backend/serno + ptr = strchr(uri, '/'); + ptr += 2; + use_serno = strchr(ptr, '/'); + if (!use_serno || !*(use_serno+1)) { + ERROR("Invalid URI (%s)\n", uri); + exit(1); + } + *use_serno = 0; + use_serno++; - use_serno = strchr(uri, '='); - if (!use_serno || !*(use_serno+1)) { - ERROR("Invalid URI (%s)\n", uri); - exit(1); - } - use_serno++; - { - char *ptr = strchr(use_serno, '&'); - if (ptr) - *ptr = 0; + backend = find_backend(ptr); + if (!backend) { + ERROR("Invalid backend (%s)\n", ptr); + exit(1); + } + + ptr = strchr(ptr, '?'); + if (ptr) + *ptr = 0; + } } /* Always enable fast return in CUPS mode */ @@ -875,86 +1100,111 @@ int main (int argc, char **argv) ret = libusb_init(&ctx); if (ret) { ERROR("Failed to initialize libusb (%d)\n", ret); - ret = CUPS_BACKEND_STOP; + ret = CUPS_BACKEND_RETRY_CURRENT; 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); + ret = CUPS_BACKEND_OK; + goto done; } /* 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); + ret = CUPS_BACKEND_OK; + goto done; } } /* Enumerate devices */ - found = find_and_enumerate(ctx, &list, backend, use_serno, 0); + found = find_and_enumerate(ctx, &list, backend, use_serno, 0, NUM_CLAIM_ATTEMPTS, &iface, &altset, &endp_up, &endp_down); if (found == -1) { ERROR("Printer open failure (No matching printers found!)\n"); - ret = CUPS_BACKEND_HOLD; + ret = CUPS_BACKEND_RETRY; goto done; } + if (test_mode) { + WARNING("**** TEST MODE %d!\n", test_mode); + if (test_mode >= TEST_MODE_NOATTACH) + goto bypass; + } + /* Open an appropriate device */ ret = libusb_open(list[found], &dev); if (ret) { ERROR("Printer open failure (Need to be root?) (%d)\n", ret); - ret = CUPS_BACKEND_STOP; + ret = CUPS_BACKEND_RETRY_CURRENT; goto done; } - claimed = libusb_kernel_driver_active(dev, iface); - if (claimed) { + /* Detach the kernel driver */ + if (libusb_kernel_driver_active(dev, iface)) { ret = libusb_detach_kernel_driver(dev, iface); if (ret) { ERROR("Printer open failure (Could not detach printer from kernel) (%d)\n", ret); - ret = CUPS_BACKEND_STOP; + ret = CUPS_BACKEND_RETRY_CURRENT; goto done_close; } } - ret = backend_claim_interface(dev, iface); + /* Claim the interface so we can start using this! */ + ret = backend_claim_interface(dev, iface, NUM_CLAIM_ATTEMPTS); if (ret) { - ret = CUPS_BACKEND_STOP; + ERROR("Printer open failure (Unable to claim interface) (%d)\n", ret); + ret = CUPS_BACKEND_RETRY; goto done_close; } - ret = libusb_get_active_config_descriptor(list[found], &config); - if (ret) { - ERROR("Printer open failure (Could not fetch config descriptor) (%d)\n", ret); - ret = CUPS_BACKEND_STOP; - goto done_close; - } - - endp_up = endp_down = 0; - for (i = 0 ; i < config->interface[iface].altsetting[altset].bNumEndpoints ; i++) { - if ((config->interface[iface].altsetting[altset].endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) { - if (config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_IN) - endp_up = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress; - else - endp_down = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress; + /* Use the appropriate altesetting! */ + if (altset != 0) { + ret = libusb_set_interface_alt_setting(dev, iface, altset); + if (ret) { + ERROR("Printer open failure (Unable to issue altsettinginterface) (%d)\n", ret); + ret = CUPS_BACKEND_RETRY; + goto done_close; } - if (endp_up && endp_down) - break; } - if (config) - libusb_free_config_descriptor(config); - +bypass: /* Initialize backend */ DEBUG("Initializing '%s' backend (version %s)\n", backend->name, backend->version); backend_ctx = backend->init(); + if (test_mode < TEST_MODE_NOATTACH) { + struct libusb_device *device; + struct libusb_device_descriptor desc; + + device = libusb_get_device(dev); + libusb_get_device_descriptor(device, &desc); + + printer_type = lookup_printer_type(backend, + desc.idVendor, desc.idProduct); + } else { + printer_type = lookup_printer_type(backend, + extra_vid, extra_pid); + } + + if (printer_type <= P_UNKNOWN) { + ERROR("Unable to lookup printer type\n"); + ret = CUPS_BACKEND_FAILED; + goto done_close; + } + /* Attach backend to device */ - backend->attach(backend_ctx, dev, endp_up, endp_down, jobid); + if (backend->attach(backend_ctx, dev, printer_type, endp_up, endp_down, jobid)) { + ERROR("Unable to attach to printer!"); + ret = CUPS_BACKEND_FAILED; + goto done_close; + } + +// STATE("+org.gutenprint-attached-to-device\n"); if (!uri) { if (backend->cmdline_arg(backend_ctx, argc, argv) < 0) @@ -975,7 +1225,8 @@ int main (int argc, char **argv) data_fd = open(fname, O_RDONLY); if (data_fd < 0) { perror("ERROR:Can't open input file"); - exit(1); + ret = CUPS_BACKEND_FAILED; + goto done; } } @@ -983,13 +1234,15 @@ int main (int argc, char **argv) i = fcntl(data_fd, F_GETFL, 0); if (i < 0) { perror("ERROR:Can't open input"); - exit(1); + ret = CUPS_BACKEND_FAILED; + goto done; } i &= ~O_NONBLOCK; i = fcntl(data_fd, F_SETFL, i); if (i < 0) { perror("ERROR:Can't open input"); - exit(1); + ret = CUPS_BACKEND_FAILED; + goto done; } /* Ignore SIGPIPE */ @@ -999,19 +1252,52 @@ int main (int argc, char **argv) /* Time for the main processing loop */ INFO("Printing started (%d copies)\n", copies); + /* See if it's a CUPS command stream, and if yes, handle it! */ + if (type && !strcmp("application/vnd.cups-command", type)) + { + ret = parse_cmdstream(backend, backend_ctx, data_fd); + goto done_claimed; + } + newpage: /* Read in data */ - if ((ret = backend->read_parse(backend_ctx, data_fd))) { + if ((ret = backend->read_parse(backend_ctx, &job, data_fd, copies))) { if (current_page) goto done_multiple; else goto done_claimed; } + /* The backend parser might not return a job due to job dependencies. + Try and read another page. */ + if (!job) + goto newpage; + + /* Create our own joblist if necessary */ + if (!(backend->flags & BACKEND_FLAG_JOBLIST)) { + struct dyesub_joblist *list = dyesub_joblist_create(backend, backend_ctx); + if (!list) + goto done_claimed; + dyesub_joblist_addjob(list, job); + job = list; + } + + /* Dump the full marker dump */ + ret = query_markers(backend, backend_ctx, !current_page); + if (ret) + goto done_claimed; + INFO("Printing page %d\n", ++current_page); - ret = backend->main_loop(backend_ctx, copies); + if (test_mode >= TEST_MODE_NOPRINT ) { + WARNING("**** TEST MODE, bypassing printing!\n"); + } else { + ret = dyesub_joblist_print(job); + } + + dyesub_joblist_cleanup(job); + if (ret) goto done_claimed; @@ -1019,6 +1305,11 @@ newpage: if (!uri) PAGE("%d %d\n", current_page, copies); + /* Dump a marker status update */ + ret = query_markers(backend, backend_ctx, !current_page); + if (ret) + goto done_claimed; + /* Since we have no way of telling if there's more data remaining to be read (without actually trying to read it), always assume multiple print jobs. */ @@ -1033,49 +1324,113 @@ done_multiple: ret = CUPS_BACKEND_OK; done_claimed: - libusb_release_interface(dev, iface); + if (test_mode < TEST_MODE_NOATTACH) + libusb_release_interface(dev, iface); done_close: -#if 0 - if (claimed) - libusb_attach_kernel_driver(dev, iface); -#endif - libusb_close(dev); + if (test_mode < TEST_MODE_NOATTACH) + libusb_close(dev); done: - if (backend && backend_ctx) + if (backend && backend_ctx) { backend->teardown(backend_ctx); +// STATE("-org.gutenprint-attached-to-device"); + } if (list) libusb_free_device_list(list, 1); - if (ctx) - libusb_exit(ctx); + + libusb_exit(ctx); return ret; } -int lookup_printer_type(struct dyesub_backend *backend, uint16_t idVendor, uint16_t idProduct) +void dump_markers(struct marker *markers, int marker_count, int full) { 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 (!full) + goto minimal; + + ATTR("marker-colors="); + for (i = 0 ; i < marker_count; i++) { + DEBUG2(markers[i].color); + if ((i+1) < marker_count) + DEBUG2(","); + } + DEBUG2("\n"); + + ATTR("marker-high-levels="); + for (i = 0 ; i < marker_count; i++) { + DEBUG2("%d", 100); + if ((i+1) < marker_count) + DEBUG2(","); + } + DEBUG2("\n"); + + ATTR("marker-low-levels="); + for (i = 0 ; i < marker_count; i++) { + DEBUG2("%d", 10); + if ((i+1) < marker_count) + DEBUG2(","); + } + DEBUG2("\n"); + + ATTR("marker-names="); + for (i = 0 ; i < marker_count; i++) { + DEBUG2("'\"%s\"'", markers[i].name); + if ((i+1) < marker_count) + DEBUG2(","); + } + DEBUG2("\n"); + + ATTR("marker-types="); + for (i = 0 ; i < marker_count; i++) { + DEBUG2("ribbonWax"); + if ((i+1) < marker_count) + DEBUG2(","); + } + DEBUG2("\n"); + +minimal: + ATTR("marker-levels="); + for (i = 0 ; i < marker_count; i++) { + int val; + if (markers[i].levelmax <= 0 || markers[i].levelnow < 0) + val = (markers[i].levelnow <= 0) ? markers[i].levelnow : -1; + else if (markers[i].levelmax == 100) + val = markers[i].levelnow; + else + val = markers[i].levelnow * 100 / markers[i].levelmax; + DEBUG2("%d", val); + if ((i+1) < marker_count) + DEBUG2(","); + } + DEBUG2("\n"); + + /* Only dump a message if the marker is not a percentage */ + if (markers[0].levelmax != 100) { + ATTR("marker-message="); + for (i = 0 ; i < marker_count; i++) { + switch (markers[i].levelnow) { + case -1: + DEBUG2("'\"Unable to query remaining prints on %s media\"'", markers[i].name); + break; + case -2: + DEBUG2("'\"Unknown remaining prints on %s media\"'", markers[i].name); + break; + case -3: + DEBUG2("'\"One or more remaining prints on %s media\"'", markers[i].name); + break; + default: + DEBUG2("'\"%d native prints remaining on %s media\"'", markers[i].levelnow, markers[i].name); + break; } + if ((i+1) < marker_count) + DEBUG2(","); } - if (idVendor == backend->devices[i].vid && - idProduct == backend->devices[i].pid) { - return backend->devices[i].type; - } + DEBUG2("\n"); } - - return type; } uint16_t uint16_to_packed_bcd(uint16_t val) @@ -1112,3 +1467,66 @@ uint32_t packed_bcd_to_uint32(char *in, int len) } return out; } + +/* Job list manipulation */ +struct dyesub_joblist *dyesub_joblist_create(struct dyesub_backend *backend, void *ctx) +{ + struct dyesub_joblist *list; + + list = malloc(sizeof(struct dyesub_joblist)); + if (!list) { + ERROR("Memory allocation failure\n"); + return NULL; + } + list->backend = backend; + list->ctx = ctx; + list->num_entries = 0; + list->copies = 1; + + return list; +} + +void dyesub_joblist_cleanup(const struct dyesub_joblist *list) +{ + int i; + for (i = 0; i < list->num_entries ; i++) { + if (list->entries[i]) + list->backend->cleanup_job(list->entries[i]); + } + free((void*)list); +} + +int dyesub_joblist_addjob(struct dyesub_joblist *list, const void *job) +{ + if (list->num_entries >= DYESUB_MAX_JOB_ENTRIES) + return 1; + + list->entries[list->num_entries++] = job; + + return 0; +} + +int dyesub_joblist_print(const struct dyesub_joblist *list) +{ + int i, j; + int ret; + for (i = 0 ; i < list->copies ; i++) { + for (j = 0 ; j < list->num_entries ; j++) { + if (list->entries[j]) { + ret = list->backend->main_loop(list->ctx, list->entries[j]); + if (ret) + return ret; + +#if 0 + /* Free up the job as we go along + if we're on the final copy */ + if (i + 1 == list->copies) { + list->backend->cleanup_job(list->entries[j]); + list->entries[j] = NULL; + } +#endif + } + } + } + return CUPS_BACKEND_OK; +} diff --git a/src/cups/backend_common.h b/src/cups/backend_common.h index 4e489fc..3fd58db 100644 --- a/src/cups/backend_common.h +++ b/src/cups/backend_common.h @@ -1,7 +1,7 @@ /* * CUPS Backend common code * - * (c) 2013-2017 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -87,46 +88,51 @@ /* To enumerate supported devices */ enum { - P_ANY = 0, - P_ES1, - P_ES2_20, - P_ES3_30, - P_ES40, - P_CP790, - P_CP_XXX, - P_CP10, - P_CP910, - P_KODAK_6800, - P_KODAK_6850, - P_KODAK_1400_805, - P_KODAK_605, - P_KODAK_305, - P_SHINKO_S2145, - P_SHINKO_S1245, - P_SHINKO_S6245, - P_SHINKO_S6145, - P_SHINKO_S6145D, - P_SONY_UPDR150, - P_SONY_UPCR10, - P_MITSU_D70X, - P_MITSU_D80, - P_MITSU_K60, - P_MITSU_9550, - P_MITSU_9550S, - P_MITSU_9600, - P_MITSU_9800, - P_MITSU_9800S, - P_MITSU_9810, - P_MITSU_P93D, - P_MITSU_P95D, - P_DNP_DS40, - P_DNP_DS80, - P_DNP_DS80D, - P_CITIZEN_CW01, - P_DNP_DSRX1, - P_DNP_DS620, - P_DNP_DS820, - P_FUJI_ASK300, + P_UNKNOWN = 0, + P_CP_XXX = 1, + P_CP10 = 2, + P_CP790 = 3, + P_CP900 = 4, + P_CP910 = 5, + P_ES1 = 6, + P_ES2_20 = 7, + P_ES3_30 = 8, + P_ES40 = 9, + P_KODAK_1400_805 = 10, + P_KODAK_6800 = 11, + P_KODAK_6850 = 12, + P_KODAK_305 = 13, + P_KODAK_605 = 14, + P_SHINKO_S1245 = 15, + P_SHINKO_S2145 = 16, + P_SHINKO_S6145 = 17, + P_SHINKO_S6145D = 18, + P_SHINKO_S6245 = 19, + P_SONY_UPCR10 = 20, + P_SONY_UPDR150 = 21, + P_MITSU_9550 = 22, + P_MITSU_9550S = 23, + P_MITSU_9600 = 24, + P_MITSU_9800 = 25, + P_MITSU_9800S = 26, + P_MITSU_9810 = 27, + P_MITSU_D70X = 28, + P_MITSU_D80 = 29, + P_MITSU_D90 = 30, + P_MITSU_K60 = 31, + P_MITSU_P93D = 32, + P_MITSU_P95D = 33, + P_CITIZEN_CW01 = 34, + P_CITIZEN_OP900II = 35, + P_DNP_DS40 = 36, + P_DNP_DS620 = 37, + P_DNP_DS80 = 38, + P_DNP_DS80D = 39, + P_DNP_DS820 = 40, + P_DNP_DSRX1 = 41, + P_FUJI_ASK300 = 42, + P_MAGICARD = 43, + P_SONY_UPD89x = 44, P_END, }; @@ -135,31 +141,56 @@ struct device_id { uint16_t pid; int type; /* P_** */ char *manuf_str; + char *prefix; +}; + +struct marker { + const char *color; /* Eg "#00FFFF" */ + const char *name; /* Eg "CK9015 (4x6)" */ + int levelmax; /* Max media count, eg '600', or '-1' */ + int levelnow; /* Remaining media, -3, -2, -1, 0..N. See CUPS. */ }; +#define BACKEND_FLAG_JOBLIST 0x00000001 + /* Backend Functions */ struct dyesub_backend { - char *name; - char *version; - char *uri_prefix; + const char *name; + const char *version; + const char **uri_prefixes; + uint32_t flags; void (*cmdline_usage)(void); /* Optional */ void *(*init)(void); - void (*attach)(void *ctx, struct libusb_device_handle *dev, + int (*attach)(void *ctx, struct libusb_device_handle *dev, int type, 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 (*read_parse)(void *ctx, int data_fd); - int (*main_loop)(void *ctx, int copies); + int (*read_parse)(void *ctx, const void **job, int data_fd, int copies); + void (*cleanup_job)(const void *job); + int (*main_loop)(void *ctx, const void *job); int (*query_serno)(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len); /* Optional */ - struct device_id devices[]; + int (*query_markers)(void *ctx, struct marker **markers, int *count); + const struct device_id devices[]; +}; + +#define DYESUB_MAX_JOB_ENTRIES 2 + +struct dyesub_joblist { + // TODO: mutex/lock + struct dyesub_backend *backend; + void *ctx; + int num_entries; + int copies; + const void *entries[DYESUB_MAX_JOB_ENTRIES]; }; /* Exported functions */ int send_data(struct libusb_device_handle *dev, uint8_t endp, - uint8_t *buf, int len); + const 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); + +void dump_markers(struct marker *markers, int marker_count, int full); void print_license_blurb(void); void print_help(char *argv0, struct dyesub_backend *backend); @@ -167,6 +198,12 @@ void print_help(char *argv0, struct dyesub_backend *backend); uint16_t uint16_to_packed_bcd(uint16_t val); uint32_t packed_bcd_to_uint32(char *in, int len); +/* Job list manipulation */ +struct dyesub_joblist *dyesub_joblist_create(struct dyesub_backend *backend, void *ctx); +int dyesub_joblist_addjob(struct dyesub_joblist *list, const void *job); +void dyesub_joblist_cleanup(const struct dyesub_joblist *list); +int dyesub_joblist_print(const struct dyesub_joblist *list); + /* Global data */ extern int terminate; extern int dyesub_debug; @@ -175,6 +212,14 @@ extern int extra_vid; extern int extra_pid; extern int extra_type; extern int copies; +extern int test_mode; + +enum { + TEST_MODE_NONE = 0, + TEST_MODE_NOPRINT, + TEST_MODE_NOATTACH, + TEST_MODE_MAX, +}; #if defined(BACKEND) extern struct dyesub_backend BACKEND; diff --git a/src/cups/backend_dnpds40.c b/src/cups/backend_dnpds40.c index 0484eb9..b9ed3e4 100644 --- a/src/cups/backend_dnpds40.c +++ b/src/cups/backend_dnpds40.c @@ -1,7 +1,7 @@ /* - * DNP DS40/DS80 Photo Printer CUPS backend -- libusb-1.0 version + * Citizen / DNP Photo Printer CUPS backend -- libusb-1.0 version * - * (c) 2013-2017 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * Development of this backend was sponsored by: * @@ -24,14 +24,16 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ //#define DNP_ONLY +//#define CITIZEN_ONLY /* Enables caching of last print type to speed up job pipelining. Without this we always have to @@ -52,19 +54,22 @@ #include "backend_common.h" -#define USB_VID_CITIZEN 0x1343 -#define USB_PID_DNP_DS40 0x0003 // Also Citizen CX -#define USB_PID_DNP_DS80 0x0004 // Also Citizen CX-W, and Mitsubishi CP-3800DW -#define USB_PID_DNP_DSRX1 0x0005 // Also Citizen CY -#define USB_PID_CITIZEN_CW02 0x0006 -#define USB_PID_DNP_DS80D 0x0007 -#define USB_PID_DNP_DS620_OLD 0x0008 +/* Private data structure */ +struct dnpds40_printjob { + uint8_t *databuf; + int datalen; -#define USB_VID_DNP 0x1452 -#define USB_PID_DNP_DS620 0x8b01 -#define USB_PID_DNP_DS820 0x9001 + int copies; + uint32_t dpi; + int matte; + int cutter; + uint32_t multicut; + int fullcut; + int printspeed; + int can_rewind; + int buf_needed; +}; -/* Private data structure */ struct dnpds40_ctx { struct libusb_device_handle *dev; uint8_t endp_up; @@ -72,34 +77,29 @@ struct dnpds40_ctx { int type; + /* Version and whatnot */ char *serno; char *version; - - int buf_needed; - int ver_major; int ver_minor; + /* State */ uint32_t media; uint32_t duplex_media; uint16_t media_count_new; - uint32_t multicut; uint32_t last_multicut; int last_matte; - int fullcut; - int matte; - int cutter; - int can_rewind; - - int printspeed; - int mediaoffset; - int manual_copies; int correct_count; + int needs_mlot; + + struct marker marker; + /* Printer capabilities */ uint32_t native_width; + uint32_t max_height; int supports_6x9; int supports_2x6; int supports_3x5x2; @@ -123,8 +123,6 @@ struct dnpds40_ctx { int supports_lowspeed; int supports_highdensity; int supports_gamma; - uint8_t *databuf; - int datalen; }; struct dnpds40_cmd { @@ -197,6 +195,198 @@ struct dnpds40_cmd { #define min(__x, __y) ((__x) < (__y)) ? __x : __y +/* Legacy CW-01 spool file support */ +struct cw01_spool_hdr { + uint8_t type; /* 0x00 -> 0x06 */ + uint8_t res; /* vertical resolution; 0x00 == 334dpi, 0x01 == 600dpi */ + uint8_t copies; /* number of prints */ + uint8_t null0; + uint32_t plane_len; /* LE */ + uint8_t null1[4]; +}; + +#define DPI_334 0 +#define DPI_600 1 + +#define TYPE_DSC 0 +#define TYPE_L 1 +#define TYPE_PC 2 +#define TYPE_2DSC 3 +#define TYPE_3L 4 +#define TYPE_A5 5 +#define TYPE_A6 6 +/* Legacy CW-01 spool file support */ + +static int cw01_read_parse(struct dnpds40_printjob *job, int data_fd, + struct cw01_spool_hdr *hdr, int read_data); +static void dnpds40_cleanup_job(const void *vjob); + +#define JOB_EQUIV(__x) if (job1->__x != job2->__x) goto done + +static struct dnpds40_printjob *combine_jobs(const struct dnpds40_printjob *job1, + const struct dnpds40_printjob *job2) +{ + struct dnpds40_printjob *newjob = NULL; + uint32_t new_multicut; + uint16_t new_w, new_h; + uint16_t gap_bytes; + + /* Sanity check */ + if (!job1 || !job2) + goto done; + + /* Make sure pertinent paremeters are the same */ + JOB_EQUIV(dpi); + JOB_EQUIV(matte); + JOB_EQUIV(cutter); + JOB_EQUIV(fullcut); + JOB_EQUIV(multicut); // TODO: Support fancier modes for 8" models (eg 8x4+8x6, etc) + JOB_EQUIV(datalen); // <-- cheating a little? + // JOV_EQUIV(printspeed); <-- does it matter? + + /* Any cutter means we shouldn't bother */ + if (job1->fullcut || job1->cutter) + goto done; + +#if 0 + // XXX TODO: 2x6*2 + 2x6*2 --> 8x6+cutter! + // problem is that 8x6" size is 4 rows smaller than 2* 4x6" prints, posing a problem. + + /* Only handle cutter if it's for 2x6" strips */ + if (job1->cutter != 0 && job1->cutter != 120) + goto done; +#endif + + /* Make sure we can combine these two prints */ + switch (job1->multicut) { + case MULTICUT_5x3_5: + new_multicut = MULTICUT_5x3_5X2; + new_w = 1920; + new_h = 2176; + gap_bytes = 0; + break; + case MULTICUT_6x4: +#if 0 + if (job1->cutter != 120) { + new_multicut = MULTICUT_6x8; + new_h = 2436; + gap_bytes = -4; + } else { +#endif + new_multicut = MULTICUT_6x4X2; + new_h = 2498; + gap_bytes = 18; +#if 0 + } +#endif + new_w = 1920; + break; + case MULTICUT_6x4_5: + new_multicut = MULTICUT_6x4_5X2; + new_w = 1920; + new_h = 2802; + gap_bytes = 30; + break; + case MULTICUT_8x4: + new_multicut = MULTICUT_8x4X2; + new_w = 2560; + new_h = 2502; + gap_bytes = 30; + break; + case MULTICUT_8x5: + new_multicut = MULTICUT_8x5X2; + new_w = 2560; + new_h = 3102; + gap_bytes = 30; + break; + case MULTICUT_8x6: + new_multicut = MULTICUT_8x6X2; + new_w = 2560; + new_h = 3702; + gap_bytes = 30; + break; + default: + // 2-up 8x6 prints too? + /* Everything else is NOT handled */ + goto done; + } + gap_bytes *= new_w; + if (job1->dpi == 600) { + gap_bytes *= 2; + new_h *= 2; + } + + DEBUG("Combining jobs to save media\n"); + + /* Okay, it's kosher to proceed */ + + newjob = malloc(sizeof(*newjob)); + if (!newjob) { + ERROR("Memory allocation failure!\n"); + goto done; + } + memcpy(newjob, job1, sizeof(*newjob)); + + newjob->databuf = malloc(((new_w*new_h+1024+54+10))*3+1024); + newjob->datalen = 0; + newjob->multicut = new_multicut; + if (!newjob->databuf) { + dnpds40_cleanup_job(newjob); + newjob = NULL; + ERROR("Memory allocation failure!\n"); + goto done; + } + + /* Copy data blocks from job1 */ + uint8_t *ptr, *ptr2; + char buf[9]; + ptr = job1->databuf; + while(ptr && ptr < (job1->databuf + job1->datalen)) { + int i; + buf[8] = 0; + memcpy(buf, ptr + 24, 8); + i = atoi(buf) + 32; + memcpy(newjob->databuf + newjob->datalen, ptr, i); + + /* If we're on a plane data block... */ + if (!memcmp("PLANE", newjob->databuf + newjob->datalen + 9, 5)) { + long planelen = (new_w * new_h) + 1088; + uint32_t newlen; + + /* Fix up length in command */ + snprintf(buf, sizeof(buf), "%08ld", planelen); + memcpy(newjob->databuf + newjob->datalen + 24, buf, 8); + + /* Alter BMP header */ + newlen = cpu_to_le32(planelen); + memcpy(newjob->databuf + newjob->datalen + 32 + 2, &newlen, 4); + + /* alter DIB header */ + newlen = cpu_to_le32(new_h); + memcpy(newjob->databuf + newjob->datalen + 32 + 22, &newlen, 4); + + /* Insert gap/padding after first image */ + memset(newjob->databuf + newjob->datalen + i, 0, gap_bytes); + newjob->datalen += gap_bytes; + + // locate job2's PLANE properly? Assumption is it's in the same place. + ptr2 = job2->databuf + (ptr - job1->databuf); + /* Copy over job2's image data */ + memcpy(newjob->databuf + newjob->datalen + i, + ptr2 + 32 + 1088, i - 32 - 1088); + newjob->datalen += i - 32 - 1088; /* add in job2 length */ + } + + newjob->datalen += i; + ptr += i; + } + +done: + return newjob; +} + +#undef JOB_EQUIV + static void dnpds40_build_cmd(struct dnpds40_cmd *cmd, char *arg1, char *arg2, uint32_t arg3_len) { memset(cmd, 0x20, sizeof(*cmd)); @@ -238,6 +428,8 @@ static char *dnpds40_printer_type(int type) case P_DNP_DSRX1: return "DSRX1"; case P_DNP_DS620: return "DS620"; case P_DNP_DS820: return "DS820"; + case P_CITIZEN_CW01: return "CW01"; + case P_CITIZEN_OP900II: return "OP900ii"; default: break; } return "Unknown"; @@ -518,8 +710,6 @@ static void *dnpds40_init(void) } memset(ctx, 0, sizeof(struct dnpds40_ctx)); - ctx->type = P_ANY; - return ctx; } @@ -527,31 +717,53 @@ static void *dnpds40_init(void) ((ctx->ver_major > (__major)) || \ (ctx->ver_major == (__major) && ctx->ver_minor >= (__minor))) -static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int dnpds40_query_mqty(struct dnpds40_ctx *ctx) +{ + struct dnpds40_cmd cmd; + uint8_t *resp; + int len = 0, count; + + /* Get Media remaining */ + dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return -1; + + dnpds40_cleanup_string((char*)resp, len); + + count = atoi((char*)resp+4); + free(resp); + + if (count) { + /* Old-sk00l models report one less than they should */ + if (!ctx->correct_count) + count++; + + count -= ctx->mediaoffset; + } + + return count; +} + +static int dnpds40_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct dnpds40_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; + ctx->type = type; - 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 */ + if (test_mode < TEST_MODE_NOATTACH) { struct dnpds40_cmd cmd; uint8_t *resp; int len = 0; + /* Get Firmware Version */ dnpds40_build_cmd(&cmd, "INFO", "FVER", 0); resp = dnpds40_resp_cmd(ctx, &cmd, &len); @@ -567,6 +779,8 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ptr = strtok(NULL, "."); ctx->ver_minor = atoi(ptr); free(resp); + } else { + return CUPS_BACKEND_FAILED; } /* Get Serial Number */ @@ -577,6 +791,8 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, dnpds40_cleanup_string((char*)resp, len); ctx->serno = (char*) resp; /* Do NOT free resp! */ + } else { + return CUPS_BACKEND_FAILED; } /* Query Media Info */ @@ -598,53 +814,84 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->media--; free(resp); + } else { + return CUPS_BACKEND_FAILED; } - } - if (ctx->type == P_DNP_DS80D) { - struct dnpds40_cmd cmd; - uint8_t *resp; - int len = 0; + if (ctx->type == P_DNP_DS80D) { + struct dnpds40_cmd cmd; + uint8_t *resp; + int len = 0; - /* Query Duplex Media Info */ - dnpds40_build_cmd(&cmd, "INFO", "CUT_PAPER", 0); + /* Query Duplex Media Info */ + dnpds40_build_cmd(&cmd, "INFO", "CUT_PAPER", 0); - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (resp) { - char tmp[5]; + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (resp) { + char tmp[5]; - dnpds40_cleanup_string((char*)resp, len); + dnpds40_cleanup_string((char*)resp, len); - memcpy(tmp, resp + 4, 4); - tmp[4] = 0; + memcpy(tmp, resp + 4, 4); + tmp[4] = 0; - ctx->duplex_media = atoi(tmp); + ctx->duplex_media = atoi(tmp); - /* Subtract out the paper status */ - if (ctx->duplex_media & 3) - ctx->duplex_media -= (ctx->duplex_media & 3); + /* Subtract out the paper status */ + if (ctx->duplex_media & 3) + ctx->duplex_media -= (ctx->duplex_media & 3); - free(resp); + free(resp); + } else { + return CUPS_BACKEND_FAILED; + } } - } -#ifdef DNP_ONLY - /* Only allow DNP printers to work. Rebadged versions should not. */ +#if (defined(DNP_ONLY) || defined(CITIZEN_ONLY)) + { + char buf[256]; + buf[0] = 0; + libusb_get_string_descriptor_ascii(dev, desc->iManufacturer, (unsigned char*)buf, STR_LEN_MAX); + sanitize_string(buf); +#ifdef DNP_ONLY /* Only allow DNP printers to work. */ + if (strncmp(buf, "Dai", 3)) /* "Dai Nippon Printing" */ + return CUPS_BACKEND_FAILED; +#endif +#ifdef CITIZEN_ONLY /* Only allow CITIZEN printers to work. */ + if (strncmp(buf, "CIT", 3)) /* "CITIZEN SYSTEMS" */ + return CUPS_BACKEND_FAILED; +#endif + } +#endif + } else { + ctx->ver_major = 3; + ctx->ver_minor = 0; + ctx->version = strdup("UNKNOWN"); + switch(ctx->type) { + case P_DNP_DS80D: + ctx->duplex_media = 200; + /* Intentional fallthrough */ + case P_DNP_DS80: + case P_DNP_DS820: + ctx->media = 510; /* 8x12 */ + break; + case P_DNP_DSRX1: + ctx->media = 310; /* 6x8 */ + break; + default: + ctx->media = 400; /* 6x9 */ + break; + } - { /* Validate USB Vendor String is "Dai Nippon Printing" */ - char buf[256]; - buf[0] = 0; - libusb_get_string_descriptor_ascii(dev, desc->iManufacturer, (unsigned char*)buf, STR_LEN_MAX); - sanitize_string(buf); - if (strncmp(buf, "Dai", 3)) - return 0; + if (getenv("MEDIA_CODE")) + ctx->media = atoi(getenv("MEDIA_CODE")); } -#endif /* Per-printer options */ switch (ctx->type) { case P_DNP_DS40: ctx->native_width = 1920; + ctx->max_height = 5480; ctx->supports_6x9 = 1; if (FW_VER_CHECK(1,04)) ctx->supports_counterp = 1; @@ -652,10 +899,15 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->supports_matte = 1; if (FW_VER_CHECK(1,40)) ctx->supports_2x6 = 1; + if (FW_VER_CHECK(1,50)) + ctx->supports_3x5x2 = 1; + if (FW_VER_CHECK(1,60)) + ctx->supports_fullcut = ctx->supports_6x6 = 1; // No 5x5! break; case P_DNP_DS80: case P_DNP_DS80D: ctx->native_width = 2560; + ctx->max_height = 7536; if (FW_VER_CHECK(1,02)) ctx->supports_counterp = 1; if (FW_VER_CHECK(1,30)) @@ -663,6 +915,7 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, break; case P_DNP_DSRX1: ctx->native_width = 1920; + ctx->max_height = 5480; ctx->supports_counterp = 1; ctx->supports_matte = 1; if (FW_VER_CHECK(1,10)) @@ -670,12 +923,32 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, if (FW_VER_CHECK(1,20)) ctx->supports_3x5x2 = 1; if (FW_VER_CHECK(2,00)) { /* AKA RX1HS */ + ctx->needs_mlot = 1; ctx->supports_mediaoffset = 1; ctx->supports_iserial = 1; } + if (FW_VER_CHECK(2,06)) { + ctx->supports_5x5 = ctx->supports_6x6 = 1; + } + break; + case P_CITIZEN_OP900II: + ctx->native_width = 1920; + ctx->max_height = 5480; + ctx->supports_counterp = 1; + ctx->supports_matte = 1; + ctx->supports_mqty_default = 1; + ctx->supports_6x9 = 1; + if (FW_VER_CHECK(1,11)) + ctx->supports_2x6 = 1; + break; + case P_CITIZEN_CW01: + ctx->native_width = 2048; + ctx->max_height = 5480; + ctx->supports_6x9 = 1; break; case P_DNP_DS620: ctx->native_width = 1920; + ctx->max_height = 5480; ctx->correct_count = 1; ctx->supports_counterp = 1; ctx->supports_matte = 1; @@ -706,6 +979,7 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, break; case P_DNP_DS820: ctx->native_width = 2560; + ctx->max_height = 7536; ctx->correct_count = 1; ctx->supports_counterp = 1; ctx->supports_matte = 1; @@ -728,8 +1002,8 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->supports_gamma = 1; break; default: - ERROR("Unknown vid/pid %04x/%04x (%d)\n", desc.idVendor, desc.idProduct, ctx->type); - return; + ERROR("Unknown printer type %d\n", ctx->type); + return CUPS_BACKEND_FAILED; } ctx->last_matte = -1; @@ -748,7 +1022,7 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, } #endif - if (ctx->supports_mediaoffset) { + if (test_mode < TEST_MODE_NOATTACH && ctx->supports_mediaoffset) { /* Get Media Offset */ struct dnpds40_cmd cmd; uint8_t *resp; @@ -759,12 +1033,14 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, if (resp) { ctx->mediaoffset = atoi((char*)resp+4); free(resp); + } else { + return CUPS_BACKEND_FAILED; } } else if (!ctx->correct_count) { ctx->mediaoffset = 50; } - if (ctx->supports_mqty_default) { + if (test_mode < TEST_MODE_NOATTACH && ctx->supports_mqty_default) { struct dnpds40_cmd cmd; uint8_t *resp; int len = 0; @@ -777,6 +1053,8 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->media_count_new = atoi((char*)resp+4); free(resp); ctx->media_count_new -= ctx->mediaoffset; + } else { + return CUPS_BACKEND_FAILED; } } else { /* Look it up for legacy models & FW */ @@ -799,12 +1077,15 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->media_count_new = 180; break; default: - ctx->media_count_new = 999; // non-zero + ctx->media_count_new = 0; break; } break; case P_DNP_DSRX1: switch (ctx->media) { + case 210: // 2L + ctx->media_count_new = 350; + break; case 300: // PC ctx->media_count_new = 700; break; @@ -812,7 +1093,42 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->media_count_new = 350; break; default: - ctx->media_count_new = 999; // non-zero + ctx->media_count_new = 0; + break; + } + break; + case P_CITIZEN_OP900II: + switch (ctx->media) { + case 210: // 2L + ctx->media_count_new = 350; + break; + case 300: // PC + ctx->media_count_new = 600; + break; + case 310: // A5 + ctx->media_count_new = 300; + break; + case 400: // A5W + ctx->media_count_new = 280; + break; + default: + ctx->media_count_new = 0; + break; + } + break; + case P_CITIZEN_CW01: + switch (ctx->media) { + case 300: // PC + ctx->media_count_new = 600; + break; + case 350: // 2L + ctx->media_count_new = 230; + break; + case 400: // A5W + ctx->media_count_new = 280; + break; + default: + ctx->media_count_new = 0; break; } break; @@ -826,15 +1142,32 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev, ctx->media_count_new = 110; break; default: - ctx->media_count_new = 999; // non-zero + ctx->media_count_new = 0; break; } break; default: - ctx->media_count_new = 999; // non-zero + ctx->media_count_new = 0; break; } } + + /* Fill out marker structure */ + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = dnpds40_media_types(ctx->media); + ctx->marker.levelmax = ctx->media_count_new; + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void dnpds40_cleanup_job(const void *vjob) { + const struct dnpds40_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void dnpds40_teardown(void *vctx) { @@ -843,7 +1176,7 @@ static void dnpds40_teardown(void *vctx) { if (!ctx) return; - if (ctx->type == P_DNP_DS80D) { + if (test_mode < TEST_MODE_NOATTACH && ctx->type == P_DNP_DS80D) { struct dnpds40_cmd cmd; /* Check to see if last print was the front side @@ -856,8 +1189,6 @@ static void dnpds40_teardown(void *vctx) { } } - if (ctx->databuf) - free(ctx->databuf); if (ctx->serno) free(ctx->serno); if (ctx->version) @@ -865,22 +1196,28 @@ static void dnpds40_teardown(void *vctx) { free(ctx); } -#define MAX_PRINTJOB_LEN (((2560*7536+1024+54))*3+1024) /* Worst-case, YMC */ +#define MAX_PRINTJOB_LEN (((ctx->native_width*ctx->max_height+1024+54+10))*3+1024) /* Worst-case, YMC */ -static int dnpds40_read_parse(void *vctx, int data_fd) { +static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct dnpds40_ctx *ctx = vctx; int run = 1; char buf[9] = { 0 }; - uint32_t dpi; + struct dnpds40_printjob *job = NULL; + struct dyesub_joblist *list; + int can_combine = 0; if (!ctx) return CUPS_BACKEND_FAILED; - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } + memset(job, 0, sizeof(*job)); + job->printspeed = -1; + job->copies = copies; /* There's no way to figure out the total job length in advance, we have to parse the stream until we get to the image plane data, @@ -891,100 +1228,114 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { the end of the job. */ - ctx->datalen = 0; - ctx->databuf = malloc(MAX_PRINTJOB_LEN); - if (!ctx->databuf) { + job->databuf = malloc(MAX_PRINTJOB_LEN); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_CANCEL; + return CUPS_BACKEND_RETRY_CURRENT; } - /* Clear everything out */ - dpi = 0; - ctx->matte = 0; - ctx->cutter = 0; - ctx->manual_copies = 0; - ctx->multicut = 0; - ctx->fullcut = 0; - ctx->printspeed = -1; - ctx->can_rewind = 0; - ctx->buf_needed = 0; - while (run) { int remain, i, j; /* Read in command header */ - i = read(data_fd, ctx->databuf + ctx->datalen, + i = read(data_fd, job->databuf + job->datalen, sizeof(struct dnpds40_cmd)); - if (i < 0) + if (i < 0) { + dnpds40_cleanup_job(job); return i; + } if (i == 0) break; - if (i < (int) sizeof(struct dnpds40_cmd)) + if (i < (int) sizeof(struct dnpds40_cmd)) { + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } - if (ctx->databuf[ctx->datalen + 0] != 0x1b || - ctx->databuf[ctx->datalen + 1] != 0x50) { - ERROR("Unrecognized header data format @%d!\n", ctx->datalen); + if (job->databuf[job->datalen + 0] != 0x1b || + job->databuf[job->datalen + 1] != 0x50) { + struct cw01_spool_hdr hdr; + /* See if it's the "classic" CW01 header */ + memcpy(&hdr, job->databuf + job->datalen, sizeof(hdr)); + hdr.plane_len = le32_to_cpu(hdr.plane_len); + + if (hdr.type > 0x06 || + hdr.res > 0x01 || + hdr.null1[0] || hdr.null1[1] || hdr.null1[2] || hdr.null1[3]) { + ERROR("Unrecognized header data format @%d!\n", job->datalen); + dnpds40_cleanup_job(job); + } else { + job->dpi = (hdr.res == DPI_600) ? 600 : 334; + i = cw01_read_parse(job, data_fd, &hdr, i); + if (i == CUPS_BACKEND_OK) + goto parsed; + else { + dnpds40_cleanup_job(job); + return i; + } + } return CUPS_BACKEND_CANCEL; } /* Parse out length of data chunk, if any */ - memcpy(buf, ctx->databuf + ctx->datalen + 24, 8); + memcpy(buf, job->databuf + job->datalen + 24, 8); j = atoi(buf); /* Read in data chunk as quickly as possible */ remain = j; while (remain > 0) { - i = read(data_fd, ctx->databuf + ctx->datalen + sizeof(struct dnpds40_cmd), + i = read(data_fd, job->databuf + job->datalen + sizeof(struct dnpds40_cmd), remain); if (i < 0) { - ERROR("Data Read Error: %d (%d/%d @%d)\n", i, remain, j, ctx->datalen); + ERROR("Data Read Error: %d (%d/%d @%d)\n", i, remain, j, job->datalen); + dnpds40_cleanup_job(job); return i; } - if (i == 0) + if (i == 0) { + dnpds40_cleanup_job(job); return 1; - ctx->datalen += i; + } + job->datalen += i; remain -= i; } - ctx->datalen -= j; /* Back it off */ + job->datalen -= j; /* Back it off */ /* Check for some offsets */ - if(!memcmp("CNTRL QTY", ctx->databuf + ctx->datalen+2, 9)) { + if(!memcmp("CNTRL QTY", job->databuf + job->datalen+2, 9)) { /* Ignore this. We will insert our own later on */ continue; } - if(!memcmp("CNTRL CUTTER", ctx->databuf + ctx->datalen+2, 12)) { - memcpy(buf, ctx->databuf + ctx->datalen + 32, 8); - ctx->cutter = atoi(buf); + if(!memcmp("CNTRL CUTTER", job->databuf + job->datalen+2, 12)) { + memcpy(buf, job->databuf + job->datalen + 32, 8); + job->cutter = atoi(buf); /* We'll insert it ourselves later */ continue; } - if(!memcmp("CNTRL BUFFCNTRL", ctx->databuf + ctx->datalen+2, 15)) { + if(!memcmp("CNTRL BUFFCNTRL", job->databuf + job->datalen+2, 15)) { /* Ignore this. We will insert our own later on if the printer and job support it. */ continue; } - if(!memcmp("CNTRL OVERCOAT", ctx->databuf + ctx->datalen+2, 14)) { + if(!memcmp("CNTRL OVERCOAT", job->databuf + job->datalen+2, 14)) { if (ctx->supports_matte) { - memcpy(buf, ctx->databuf + ctx->datalen + 32, 8); - ctx->matte = atoi(buf); + memcpy(buf, job->databuf + job->datalen + 32, 8); + job->matte = atoi(buf); } else { WARNING("Printer FW does not support matte prints, using glossy mode\n"); } /* We'll insert our own later, if appropriate */ continue; } - if(!memcmp("IMAGE MULTICUT", ctx->databuf + ctx->datalen+2, 14)) { - memcpy(buf, ctx->databuf + ctx->datalen + 32, 8); - ctx->multicut = atoi(buf); + if(!memcmp("IMAGE MULTICUT", job->databuf + job->datalen+2, 14)) { + memcpy(buf, job->databuf + job->datalen + 32, 8); + job->multicut = atoi(buf); /* Backend automatically handles rewind support, so ignore application requests to use it. */ - if (ctx->multicut > 400) - ctx->multicut -= 400; + if (job->multicut > 400) + job->multicut -= 400; /* We'll insert this ourselves later. */ continue; } - if(!memcmp("CNTRL FULL_CUTTER_SET", ctx->databuf + ctx->datalen+2, 21)) { + if(!memcmp("CNTRL FULL_CUTTER_SET", job->databuf + job->datalen+2, 21)) { if (!ctx->supports_fullcut) { WARNING("Printer FW does not support full cutter control!\n"); continue; @@ -999,134 +1350,139 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { WARNING("Full cutter argument length incorrect, ignoring!\n"); continue; } else if (!ctx->supports_adv_fullcut) { - if (ctx->databuf[ctx->datalen + 32 + 12] != '0' || - ctx->databuf[ctx->datalen + 32 + 13] != '0' || - ctx->databuf[ctx->datalen + 32 + 14] != '0') { + if (job->databuf[job->datalen + 32 + 12] != '0' || + job->databuf[job->datalen + 32 + 13] != '0' || + job->databuf[job->datalen + 32 + 14] != '0') { WARNING("Full cutter scrap setting not supported on this firmware, ignoring!\n"); continue; } } // XXX enforce cut counts/sizes? - ctx->fullcut = 1; + job->fullcut = 1; } - if(!memcmp("IMAGE YPLANE", ctx->databuf + ctx->datalen + 2, 12)) { + if(!memcmp("IMAGE YPLANE", job->databuf + job->datalen + 2, 12)) { uint32_t y_ppm; /* Pixels Per Meter */ /* Validate vertical resolution */ - memcpy(&y_ppm, ctx->databuf + ctx->datalen + 32 + 42, sizeof(y_ppm)); + memcpy(&y_ppm, job->databuf + job->datalen + 32 + 42, sizeof(y_ppm)); y_ppm = le32_to_cpu(y_ppm); switch (y_ppm) { case 11808: - dpi = 300; + job->dpi = 300; break; case 23615: - dpi = 600; + job->dpi = 600; break; default: ERROR("Unrecognized printjob resolution (%u ppm)\n", y_ppm); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* Validate horizontal size */ - memcpy(&y_ppm, ctx->databuf + ctx->datalen + 32 + 18, sizeof(y_ppm)); + memcpy(&y_ppm, job->databuf + job->datalen + 32 + 18, sizeof(y_ppm)); y_ppm = le32_to_cpu(y_ppm); if (y_ppm != ctx->native_width) { ERROR("Incorrect horizontal resolution (%u), aborting!\n", y_ppm); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } } - if(!memcmp("CNTRL PRINTSPEED", ctx->databuf + ctx->datalen + 2, 16)) { + if(!memcmp("CNTRL PRINTSPEED", job->databuf + job->datalen + 2, 16)) { if (!ctx->supports_printspeed) { WARNING("Printer does not support PRINTSPEED\n"); continue; } - memcpy(buf, ctx->databuf + ctx->datalen + 32, 8); - ctx->printspeed = atoi(buf) / 10; + memcpy(buf, job->databuf + job->datalen + 32, 8); + job->printspeed = atoi(buf) / 10; /* We'll insert this ourselves later. */ continue; } /* This is the last block.. */ - if(!memcmp("CNTRL START", ctx->databuf + ctx->datalen + 2, 11)) + if(!memcmp("CNTRL START", job->databuf + job->datalen + 2, 11)) run = 0; /* Add in the size of this chunk */ - ctx->datalen += sizeof(struct dnpds40_cmd) + j; + job->datalen += sizeof(struct dnpds40_cmd) + j; } - +parsed: /* If we have no data.. don't bother */ - if (!ctx->datalen) + if (!job->datalen) { + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } /* Sanity check matte mode */ - if (ctx->matte == 21 && !ctx->supports_finematte) { + if (job->matte == 21 && !ctx->supports_finematte) { WARNING("Printer FW does not support Fine Matte mode, downgrading to normal matte\n"); - ctx->matte = 1; - } else if (ctx->matte == 22 && !ctx->supports_luster) { + job->matte = 1; + } else if (job->matte == 22 && !ctx->supports_luster) { WARNING("Printer FW does not support Luster mode, downgrading to normal matte\n"); - ctx->matte = 1; - } else if (ctx->matte > 1 && !ctx->supports_advmatte) { + job->matte = 1; + } else if (job->matte > 1 && !ctx->supports_advmatte) { WARNING("Printer FW does not support advanced matte modes, downgrading to normal matte\n"); - ctx->matte = 1; + job->matte = 1; } /* Pick a sane default value for printspeed if not specified */ - if (ctx->printspeed == -1 || ctx->printspeed > 3) + if (job->printspeed == -1 || job->printspeed > 3) { - if (dpi == 600) - ctx->printspeed = 1; + if (job->dpi == 600) + job->printspeed = 1; else - ctx->printspeed = 0; + job->printspeed = 0; } /* And sanity-check whatever value is there */ - if (ctx->printspeed == 0 && dpi == 600) { - ctx->printspeed = 1; - } else if (ctx->printspeed == 1 && dpi == 300) { - ctx->printspeed = 0; + if (job->printspeed == 0 && job->dpi == 600) { + job->printspeed = 1; + } else if (job->printspeed == 1 && job->dpi == 300) { + job->printspeed = 0; } /* Make sure MULTICUT is sane, most validation needs this */ - if (!ctx->multicut) { + if (!job->multicut && ctx->type != P_CITIZEN_CW01) { WARNING("Missing or illegal MULTICUT command!\n"); - if (dpi == 300) - ctx->buf_needed = 1; + if (job->dpi == 300) + job->buf_needed = 1; else - ctx->buf_needed = 2; + job->buf_needed = 2; goto skip_checks; } /* Only DS80D supports Cut Paper types */ - if (ctx->multicut > 100 && + if (job->multicut > 100 && ctx->type != P_DNP_DS80D) { ERROR("Only DS80D supports cut-paper sizes!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* Figure out the number of buffers we need. */ - ctx->buf_needed = 1; + job->buf_needed = 1; - if (dpi == 600) { + if (job->dpi == 600) { switch(ctx->type) { case P_DNP_DS620: - if (ctx->multicut == MULTICUT_6x9 || - ctx->multicut == MULTICUT_6x4_5X2) - ctx->buf_needed = 2; + if (job->multicut == MULTICUT_6x9 || + job->multicut == MULTICUT_6x4_5X2) + job->buf_needed = 2; break; case P_DNP_DS80: /* DS80/CX-W */ - if (ctx->matte && (ctx->multicut == MULTICUT_8xA4LEN || - ctx->multicut == MULTICUT_8x4X3 || - ctx->multicut == MULTICUT_8x8_8x4 || - ctx->multicut == MULTICUT_8x6X2 || - ctx->multicut == MULTICUT_8x12)) - ctx->buf_needed = 2; + if (job->matte && (job->multicut == MULTICUT_8xA4LEN || + job->multicut == MULTICUT_8x4X3 || + job->multicut == MULTICUT_8x8_8x4 || + job->multicut == MULTICUT_8x6X2 || + job->multicut == MULTICUT_8x12)) + job->buf_needed = 2; break; case P_DNP_DS80D: - if (ctx->matte) { - int mcut = ctx->multicut; + if (job->matte) { + int mcut = job->multicut; if (mcut > MULTICUT_S_BACK) mcut -= MULTICUT_S_BACK; @@ -1138,129 +1494,150 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { mcut == MULTICUT_8x8_8x4 || mcut == MULTICUT_8x6X2 || mcut == MULTICUT_8x12) - ctx->buf_needed = 2; + job->buf_needed = 2; if (mcut == MULTICUT_S_8x12 || mcut == MULTICUT_S_8x6X2 || mcut == MULTICUT_S_8x4X3) - ctx->buf_needed = 2; + job->buf_needed = 2; } break; case P_DNP_DS820: // Nothing; all sizes only need 1 buffer break; + case P_CITIZEN_CW01: + job->buf_needed = 2; + break; default: /* DS40/CX/RX1/CY/everything else */ - if (ctx->matte) { - if (ctx->multicut == MULTICUT_6x8 || - ctx->multicut == MULTICUT_6x9 || - ctx->multicut == MULTICUT_6x4X2 || - ctx->multicut == MULTICUT_5x7 || - ctx->multicut == MULTICUT_5x3_5X2) - ctx->buf_needed = 2; + if (job->matte) { + if (job->multicut == MULTICUT_6x8 || + job->multicut == MULTICUT_6x9 || + job->multicut == MULTICUT_6x4X2 || + job->multicut == MULTICUT_5x7 || + job->multicut == MULTICUT_5x3_5X2) + job->buf_needed = 2; } else { - if (ctx->multicut == MULTICUT_6x8 || - ctx->multicut == MULTICUT_6x9 || - ctx->multicut == MULTICUT_6x4X2) - ctx->buf_needed = 1; + if (job->multicut == MULTICUT_6x8 || + job->multicut == MULTICUT_6x9 || + job->multicut == MULTICUT_6x4X2) + job->buf_needed = 1; } break; } } + if (job->dpi == 334 && ctx->type != P_CITIZEN_CW01) + { + ERROR("Illegal resolution (%u) for printer!\n", job->dpi); + dnpds40_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } /* Sanity-check type vs loaded media */ - if (ctx->multicut < 100) { + if (job->multicut == 0) + goto skip_multicut; + + if (job->multicut < 100) { switch(ctx->media) { case 200: //"5x3.5 (L)" - if (ctx->multicut != MULTICUT_5x3_5) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + if (job->multicut != MULTICUT_5x3_5) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } break; case 210: //"5x7 (2L)" - if (ctx->multicut != MULTICUT_5x3_5 && ctx->multicut != MULTICUT_5x7 && - ctx->multicut != MULTICUT_5x3_5X2 && ctx->multicut != MULTICUT_5x5) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + if (job->multicut != MULTICUT_5x3_5 && job->multicut != MULTICUT_5x7 && + job->multicut != MULTICUT_5x3_5X2 && job->multicut != MULTICUT_5x5) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* Only 3.5x5 on 7x5 media can be rewound */ - if (ctx->multicut == MULTICUT_5x3_5) - ctx->can_rewind = 1; + if (job->multicut == MULTICUT_5x3_5) + job->can_rewind = 1; break; case 300: //"6x4 (PC)" - if (ctx->multicut != MULTICUT_6x4) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + if (job->multicut != MULTICUT_6x4) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } break; case 310: //"6x8 (A5)" - if (ctx->multicut != MULTICUT_6x4 && ctx->multicut != MULTICUT_6x8 && - ctx->multicut != MULTICUT_6x4X2 && - ctx->multicut != MULTICUT_6x6 && ctx->multicut != 30) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + if (job->multicut != MULTICUT_6x4 && job->multicut != MULTICUT_6x8 && + job->multicut != MULTICUT_6x4X2 && + job->multicut != MULTICUT_6x6 && job->multicut != 30) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* Only 6x4 on 6x8 media can be rewound */ - if (ctx->multicut == MULTICUT_6x4) - ctx->can_rewind = 1; + if (job->multicut == MULTICUT_6x4) + job->can_rewind = 1; break; case 400: //"6x9 (A5W)" - if (ctx->multicut != MULTICUT_6x4 && ctx->multicut != MULTICUT_6x8 && - ctx->multicut != MULTICUT_6x9 && ctx->multicut != MULTICUT_6x4X2 && - ctx->multicut != MULTICUT_6x6 && - ctx->multicut != MULTICUT_6x4_5 && ctx->multicut != MULTICUT_6x4_5X2) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + if (job->multicut != MULTICUT_6x4 && job->multicut != MULTICUT_6x8 && + job->multicut != MULTICUT_6x9 && job->multicut != MULTICUT_6x4X2 && + job->multicut != MULTICUT_6x6 && + job->multicut != MULTICUT_6x4_5 && job->multicut != MULTICUT_6x4_5X2) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* Only 6x4 or 6x4.5 on 6x9 media can be rewound */ - if (ctx->multicut == MULTICUT_6x4 || ctx->multicut == MULTICUT_6x4_5) - ctx->can_rewind = 1; + if (job->multicut == MULTICUT_6x4 || job->multicut == MULTICUT_6x4_5) + job->can_rewind = 1; break; case 500: //"8x10" if (ctx->type == P_DNP_DS820 && - (ctx->multicut == MULTICUT_8x7 || ctx->multicut == MULTICUT_8x9)) { + (job->multicut == MULTICUT_8x7 || job->multicut == MULTICUT_8x9)) { /* These are okay */ - } else if (ctx->multicut < MULTICUT_8x10 || ctx->multicut == MULTICUT_8x12 || - ctx->multicut == MULTICUT_8x6X2 || ctx->multicut >= MULTICUT_8x6_8x5 ) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + } else if (job->multicut < MULTICUT_8x10 || job->multicut == MULTICUT_8x12 || + job->multicut == MULTICUT_8x6X2 || job->multicut >= MULTICUT_8x6_8x5 ) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* 8x4, 8x5 can be rewound */ - if (ctx->multicut == MULTICUT_8x4 || - ctx->multicut == MULTICUT_8x5) - ctx->can_rewind = 1; + if (job->multicut == MULTICUT_8x4 || + job->multicut == MULTICUT_8x5) + job->can_rewind = 1; break; case 510: //"8x12" - if (ctx->multicut < MULTICUT_8x10 || (ctx->multicut > MULTICUT_8xA4LEN && !(ctx->multicut == MULTICUT_8x7 || ctx->multicut == MULTICUT_8x9))) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + if (job->multicut < MULTICUT_8x10 || (job->multicut > MULTICUT_8xA4LEN && !(job->multicut == MULTICUT_8x7 || job->multicut == MULTICUT_8x9))) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* 8x4, 8x5, 8x6 can be rewound */ - if (ctx->multicut == MULTICUT_8x4 || - ctx->multicut == MULTICUT_8x5 || - ctx->multicut == MULTICUT_8x6) - ctx->can_rewind = 1; + if (job->multicut == MULTICUT_8x4 || + job->multicut == MULTICUT_8x5 || + job->multicut == MULTICUT_8x6) + job->can_rewind = 1; break; case 600: //"A4" - if (ctx->multicut < MULTICUT_A5 || ctx->multicut > MULTICUT_A4x5X2) { - ERROR("Incorrect media for job loaded (%d vs %d)\n", ctx->media, ctx->multicut); + if (job->multicut < MULTICUT_A5 || job->multicut > MULTICUT_A4x5X2) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* A4xn and A5 can be rewound */ - if (ctx->multicut == MULTICUT_A4x4 || - ctx->multicut == MULTICUT_A4x5 || - ctx->multicut == MULTICUT_A4x6 || - ctx->multicut == MULTICUT_A5) - ctx->can_rewind = 1; + if (job->multicut == MULTICUT_A4x4 || + job->multicut == MULTICUT_A4x5 || + job->multicut == MULTICUT_A4x6 || + job->multicut == MULTICUT_A5) + job->can_rewind = 1; break; default: - ERROR("Unknown media (%u vs %u)!\n", ctx->media, ctx->multicut); + ERROR("Unknown media (%u vs %u)!\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - } else if (ctx->multicut < 400) { - int mcut = ctx->multicut; + } else if (job->multicut < 400) { + int mcut = job->multicut; switch(ctx->duplex_media) { case 100: //"8x10.75" @@ -1272,7 +1649,8 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { if (mcut == MULTICUT_S_8x12 || mcut == MULTICUT_S_8x6X2 || mcut == MULTICUT_S_8x4X3) { - ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, ctx->multicut); + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->duplex_media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } break; @@ -1280,73 +1658,111 @@ static int dnpds40_read_parse(void *vctx, int data_fd) { /* Everything is legal */ break; default: - ERROR("Unknown duplexer media (%u vs %u)!\n", ctx->duplex_media, ctx->multicut); + ERROR("Unknown duplexer media (%u vs %u)!\n", ctx->duplex_media, job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } } else { - ERROR("Multicut value out of range! (%u)\n", ctx->multicut); + ERROR("Multicut value out of range! (%u)\n", job->multicut); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } /* Additional santity checks, make sure printer support exists */ - if (!ctx->supports_6x6 && ctx->multicut == MULTICUT_6x6) { + if (!ctx->supports_6x6 && job->multicut == MULTICUT_6x6) { ERROR("Printer does not support 6x6 prints, aborting!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - if (!ctx->supports_5x5 && ctx->multicut == MULTICUT_5x5) { + if (!ctx->supports_5x5 && job->multicut == MULTICUT_5x5) { ERROR("Printer does not support 5x5 prints, aborting!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - if ((ctx->multicut == MULTICUT_6x4_5 || ctx->multicut == MULTICUT_6x4_5X2) && + if ((job->multicut == MULTICUT_6x4_5 || job->multicut == MULTICUT_6x4_5X2) && !ctx->supports_6x4_5) { ERROR("Printer does not support 6x4.5 prints, aborting!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - if (ctx->multicut == MULTICUT_6x9 && !ctx->supports_6x9) { + if (job->multicut == MULTICUT_6x9 && !ctx->supports_6x9) { ERROR("Printer does not support 6x9 prints, aborting!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - if (ctx->multicut == MULTICUT_5x3_5X2 && !ctx->supports_3x5x2) { + if (job->multicut == MULTICUT_5x3_5X2 && !ctx->supports_3x5x2) { ERROR("Printer does not support 3.5x5*2 prints, aborting!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - if (ctx->fullcut && !ctx->supports_adv_fullcut && - ctx->multicut != MULTICUT_6x8) { +skip_multicut: + + if (job->fullcut && !ctx->supports_adv_fullcut && + job->multicut != MULTICUT_6x8) { ERROR("Printer does not support full control on sizes other than 6x8, aborting!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - if (ctx->cutter == 120) { - if (ctx->multicut == MULTICUT_6x4 || ctx->multicut == MULTICUT_6x8) { + if (job->fullcut && job->cutter) { + WARNING("Cannot simultaneously use FULL_CUTTER and CUTTER, using the former\n"); + job->cutter = 0; + } + + if (job->cutter == 120) { + if (job->multicut == MULTICUT_6x4 || job->multicut == MULTICUT_6x8) { if (!ctx->supports_2x6) { ERROR("Printer does not support 2x6 prints, aborting!\n"); + dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } } else { ERROR("Printer only supports legacy 2-inch cuts on 4x6 or 8x6 jobs!"); + dnpds40_cleanup_job(job); 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? what about other printers? - ctx->manual_copies = 1; } skip_checks: - DEBUG("dpi %u matte %d mcut %u cutter %d, bufs %d spd %d\n", - dpi, ctx->matte, ctx->multicut, ctx->cutter, ctx->buf_needed, ctx->printspeed); + DEBUG("job->dpi %u matte %d mcut %u cutter %d/%d, bufs %d spd %d\n", + job->dpi, job->matte, job->multicut, job->cutter, job->fullcut, job->buf_needed, job->printspeed); + + list = dyesub_joblist_create(&dnpds40_backend, ctx); + + can_combine = job->can_rewind; /* Any rewindable size can be stacked */ + + /* Try to combine prints */ + if (copies > 1 && can_combine) { + struct dnpds40_printjob *combined; + combined = combine_jobs(job, job); + if (combined) { + combined->copies = job->copies / 2; + combined->can_rewind = 0; + dyesub_joblist_addjob(list, combined); + + if (job->copies & 1) { + job->copies = 1; + } else { + dnpds40_cleanup_job(job); + job = NULL; + } + } + } + if (job) { + dyesub_joblist_addjob(list, job); + } + + *vjob = list; return CUPS_BACKEND_OK; } -static int dnpds40_main_loop(void *vctx, int copies) { +static int dnpds40_main_loop(void *vctx, const void *vjob) { struct dnpds40_ctx *ctx = vctx; int ret; struct dnpds40_cmd cmd; @@ -1356,23 +1772,52 @@ static int dnpds40_main_loop(void *vctx, int copies) { char buf[9]; int status; int buf_needed; + int multicut; int count = 0; + int manual_copies = 0; + int copies; + + const struct dnpds40_printjob *job = vjob; if (!ctx) return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; - buf_needed = ctx->buf_needed; + buf_needed = job->buf_needed; + multicut = job->multicut; + copies = job->copies; /* If we switch major overcoat modes, we need both buffers */ - if (!!ctx->matte != ctx->last_matte) + if (!!job->matte != ctx->last_matte) buf_needed = 2; - if (ctx->media_count_new) { - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", dnpds40_media_types(ctx->media)); - ATTR("marker-types=ribbonWax\n"); + if (job->cutter == 120) { + /* 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? what about other printers? + manual_copies = 1; + } + + /* RX1HS requires HS media, but the only way to tell is that the + HS media reports a lot code, while the non-HS media does not. */ + if (ctx->needs_mlot) { + /* Get Media Lot */ + dnpds40_build_cmd(&cmd, "INFO", "MLOT", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + + len = strlen((char*)resp); + free(resp); + if (!len) { + ERROR("Media does not report a valid lot number (non-HS media in RX1HS?)\n"); + return CUPS_BACKEND_STOP; + } } top: @@ -1442,34 +1887,17 @@ top: { /* Figure out remaining native prints */ - dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0); - - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) + ctx->marker.levelnow = dnpds40_query_mqty(ctx); + if (ctx->marker.levelnow < 0) return CUPS_BACKEND_FAILED; + dump_markers(&ctx->marker, 1, 0); - dnpds40_cleanup_string((char*)resp, len); - - count = atoi((char*)resp+4); - free(resp); - - if (count) { - /* Old-sk00l models report one less than they should */ - if (!ctx->correct_count) - count++; - - count -= ctx->mediaoffset; - } - - if (ctx->media_count_new) { - ATTR("marker-levels=%d\n", count * 100 / ctx->media_count_new); - ATTR("marker-message=\"%d native prints remaining on '%s' ribbon\"\n", count, dnpds40_media_types(ctx->media)); - } + count = ctx->marker.levelnow; // For logic below. /* See if we can rewind to save media */ - if (ctx->can_rewind && ctx->supports_rewind) { + if (job->can_rewind && ctx->supports_rewind) { /* Tell printer to use rewind */ - ctx->multicut += 400; + multicut += 400; /* Get Media remaining */ dnpds40_build_cmd(&cmd, "INFO", "RQTY", 0); @@ -1483,6 +1911,26 @@ top: free(resp); } + if (ctx->type == P_CITIZEN_CW01) { + /* Get Vertical resolution */ + dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + +#if 0 // XXX Fix 600dpi support on CW01 + // have to read the last DPI, and send the correct CWD over? + if (ctx->dpi == 600 && strcmp("RV0334", *char*)resp) { + ERROR("600DPI prints not yet supported, need 600DPI CWD load"); + return CUPS_BACKEND_CANCEL; + } +#endif + free(resp); + } + /* Verify we have sufficient media for prints */ #if 0 // disabled this to allow error to be reported on the printer panel @@ -1491,22 +1939,21 @@ top: return CUPS_BACKEND_STOP; } #endif - if (count < copies) { WARNING("Printer does not have sufficient remaining media (%d) to complete job (%d)\n", copies, count); } } /* Store our last multicut state */ - ctx->last_multicut = ctx->multicut; + ctx->last_multicut = multicut; /* Tell printer how many copies to make */ - snprintf(buf, sizeof(buf), "%07d\r", ctx->manual_copies ? 1 : copies); + snprintf(buf, sizeof(buf), "%07d\r", manual_copies ? 1 : copies); dnpds40_build_cmd(&cmd, "CNTRL", "QTY", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) return CUPS_BACKEND_FAILED; - if (!ctx->manual_copies) + if (!manual_copies) copies = 1; /* Enable job resumption on correctable errors */ @@ -1515,7 +1962,7 @@ top: /* DS80D does not support BUFFCNTRL when using cut media; all others support this */ if (ctx->type != P_DNP_DS80D || - ctx->multicut < 100) { + multicut < 100) { dnpds40_build_cmd(&cmd, "CNTRL", "BUFFCNTRL", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) return CUPS_BACKEND_FAILED; @@ -1524,15 +1971,15 @@ top: /* Set overcoat parameters if appropriate */ if (ctx->supports_matte) { - snprintf(buf, sizeof(buf), "%08d", ctx->matte); + snprintf(buf, sizeof(buf), "%08d", job->matte); dnpds40_build_cmd(&cmd, "CNTRL", "OVERCOAT", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) return CUPS_BACKEND_FAILED; } /* Program in the cutter setting */ - if (ctx->cutter) { - snprintf(buf, sizeof(buf), "%08d", ctx->cutter); + if (job->cutter) { + snprintf(buf, sizeof(buf), "%08d", job->cutter); dnpds40_build_cmd(&cmd, "CNTRL", "CUTTER", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) return CUPS_BACKEND_FAILED; @@ -1540,21 +1987,23 @@ top: /* Send over the printspeed if appropriate */ if (ctx->supports_printspeed) { - snprintf(buf, sizeof(buf), "%08d", ctx->printspeed * 10); + snprintf(buf, sizeof(buf), "%08d", job->printspeed * 10); dnpds40_build_cmd(&cmd, "CNTRL", "PRINTSPEED", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) return CUPS_BACKEND_FAILED; } - /* Program in the multicut setting */ - snprintf(buf, sizeof(buf), "%08u", ctx->multicut); - dnpds40_build_cmd(&cmd, "IMAGE", "MULTICUT", 8); - if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) - return CUPS_BACKEND_FAILED; + /* Program in the multicut setting, if one exists */ + if (multicut) { + snprintf(buf, sizeof(buf), "%08u", multicut); + dnpds40_build_cmd(&cmd, "IMAGE", "MULTICUT", 8); + if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) + return CUPS_BACKEND_FAILED; + } /* Finally, send the stream over as individual data chunks */ - ptr = ctx->databuf; - while(ptr && ptr < (ctx->databuf + ctx->datalen)) { + ptr = job->databuf; + while(ptr && ptr < (job->databuf + job->datalen)) { int i; buf[8] = 0; memcpy(buf, ptr + 24, 8); @@ -1568,10 +2017,11 @@ top: } sleep(1); /* Give things a moment */ - if (fast_return) { + if (fast_return && !manual_copies) { INFO("Fast return mode enabled.\n"); } else { INFO("Waiting for job to complete...\n"); + int started = 0; while (1) { /* Query status */ @@ -1584,8 +2034,10 @@ top: free(resp); /* If we're idle or there's an error..*/ - if (status == 0) + if (status == 0 && started) break; + if (status) + started = 1; if (status >= 1000) { ERROR("Printer encountered error: %s\n", dnpds40_statuses(status)); break; @@ -1612,11 +2064,8 @@ top: count -= ctx->mediaoffset; } - - if (ctx->media_count_new) { - ATTR("marker-levels=%d\n", count * 100 / ctx->media_count_new); - ATTR("marker-message=\"%d native prints remaining on '%s' ribbon\"\n", count, dnpds40_media_types(ctx->media)); - } + ctx->marker.levelnow = count; + dump_markers(&ctx->marker, 1, 0); } /* Clean up */ @@ -1627,12 +2076,12 @@ top: if (copies && --copies) { /* No need to wait on buffers due to matte switching */ - buf_needed = ctx->buf_needed; + buf_needed = job->buf_needed; goto top; } /* Finally, account for overcoat mode of last print */ - ctx->last_matte = !!ctx->matte; + ctx->last_matte = !!job->matte; #ifdef STATE_DIR { /* Store last matte status into file */ @@ -1746,6 +2195,60 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) free(resp); } + if (ctx->type == P_CITIZEN_CW01) { + /* Get Horizonal resolution */ + dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_H", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + + INFO("Horizontal Resolution: %s dpi\n", (char*)resp + 3); + + free(resp); + + /* Get Vertical resolution */ + dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + + INFO("Vertical Resolution: %s dpi\n", (char*)resp + 3); + + free(resp); + + /* Get Color Control Data Version */ + dnpds40_build_cmd(&cmd, "TBL_RD", "Version", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + + INFO("Color Data Version: %s ", (char*)resp); + + free(resp); + + /* Get Color Control Data Checksum */ + dnpds40_build_cmd(&cmd, "MNT_RD", "CTRLD_CHKSUM", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + + DEBUG2("(%s)\n", (char*)resp); + + free(resp); + } + /* Get Media Color offset */ dnpds40_build_cmd(&cmd, "INFO", "MCOLOR", 0); @@ -1798,6 +2301,9 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) free(resp); + if (ctx->type == P_CITIZEN_CW01) + goto skip; + /* Get Ribbon ID code (?) */ dnpds40_build_cmd(&cmd, "MNT_RD", "RIBBON_ID_CODE", 0); @@ -1985,6 +2491,7 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx) free(resp); } +skip: return CUPS_BACKEND_OK; } @@ -2048,7 +2555,6 @@ static int dnpds40_get_status(struct dnpds40_ctx *ctx) dnpds40_cleanup_string((char*)resp, len); INFO("Free Buffers: %d\n", atoi((char*)resp + 3)); - free(resp); /* Report media */ @@ -2090,24 +2596,10 @@ static int dnpds40_get_status(struct dnpds40_ctx *ctx) INFO("Native Prints Available on New Media: %u\n", ctx->media_count_new); /* Get Media remaining */ - dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0); - - resp = dnpds40_resp_cmd(ctx, &cmd, &len); - if (!resp) + count = dnpds40_query_mqty(ctx); + if (count < 0) return CUPS_BACKEND_FAILED; - dnpds40_cleanup_string((char*)resp, len); - - count = atoi((char*)resp+4); - free(resp); - - if (count) { - /* Old-sk00l models report one less than they should */ - if (!ctx->correct_count) - count++; - - count -= ctx->mediaoffset; - } INFO("Native Prints Remaining on Media: %d\n", count); if (ctx->supports_rewind) { @@ -2433,7 +2925,7 @@ static int dnpds40_cmdline_arg(void *vctx, int argc, char **argv) optarg[0] != 'B' && optarg[0] != 'M') return CUPS_BACKEND_FAILED; - if (!ctx->supports_matte) { + if (optarg[0] == 'M' && !ctx->supports_matte) { ERROR("Printer FW does not support matte functions, please update!\n"); return CUPS_BACKEND_FAILED; } @@ -2483,28 +2975,203 @@ static int dnpds40_cmdline_arg(void *vctx, int argc, char **argv) return 0; } +static int dnpds40_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct dnpds40_ctx *ctx = vctx; + + *markers = &ctx->marker; + *count = 1; + + ctx->marker.levelnow = dnpds40_query_mqty(ctx); + if (ctx->marker.levelnow < 0) + return CUPS_BACKEND_FAILED; + + return CUPS_BACKEND_OK; +} + +static const char *dnpds40_prefixes[] = { + "dnp_citizen", "dnpds40", // Family names, do *not* nuke. + "dnp-ds40", "dnp-ds80", "dnp-ds80dx", "dnp-ds620", "dnp-ds820", "dnp-dsrx1", + "citizen-cw-01", "citizen-cw-02", "citizen-cx-02", + // backwards compatibility + "dnpds80", "dnpds80dx", "dnpds620", "dnpds820", "dnprx1", + "citizencw01", "citizencw02", "citizencx02", + // These are all extras. + "citizen-cx", "citizen-cx-w", "citizen-cy", "citizen-cy-02", + "citizen-op900", "citizen-op900ii", + NULL +}; + +#define USB_VID_CITIZEN 0x1343 +#define USB_PID_DNP_DS40 0x0003 // Also Citizen CX +#define USB_PID_DNP_DS80 0x0004 // Also Citizen CX-W and Mitsubishi CP-3800DW +#define USB_PID_DNP_DSRX1 0x0005 // Also Citizen CY +#define USB_PID_DNP_DS80D 0x0008 + +#define USB_PID_CITIZEN_CW01 0x0002 // Maybe others? +#define USB_PID_CITIZEN_CW02 0x0006 // Also OP900II +#define USB_PID_CITIZEN_CX02 0x000A + +#define USB_VID_DNP 0x1452 +#define USB_PID_DNP_DS620 0x8b01 +#define USB_PID_DNP_DS820 0x9001 + /* Exported */ struct dyesub_backend dnpds40_backend = { - .name = "DNP DS40/DS80/DSRX1/DS620", - .version = "0.92", - .uri_prefix = "dnpds40", + .name = "DNP DS-series / Citizen C-series", + .version = "0.109", + .uri_prefixes = dnpds40_prefixes, + .flags = BACKEND_FLAG_JOBLIST, .cmdline_usage = dnpds40_cmdline, .cmdline_arg = dnpds40_cmdline_arg, .init = dnpds40_init, .attach = dnpds40_attach, .teardown = dnpds40_teardown, .read_parse = dnpds40_read_parse, + .cleanup_job = dnpds40_cleanup_job, .main_loop = dnpds40_main_loop, .query_serno = dnpds40_query_serno, + .query_markers = dnpds40_query_markers, .devices = { - { USB_VID_CITIZEN, USB_PID_DNP_DS40, P_DNP_DS40, ""}, - { USB_VID_CITIZEN, USB_PID_DNP_DS80, P_DNP_DS80, ""}, - { USB_VID_CITIZEN, USB_PID_DNP_DSRX1, P_DNP_DSRX1, ""}, - { USB_VID_CITIZEN, USB_PID_DNP_DS620_OLD, P_DNP_DS620, ""}, - { USB_VID_DNP, USB_PID_DNP_DS620, P_DNP_DS620, ""}, - { USB_VID_DNP, USB_PID_DNP_DS80D, P_DNP_DS80D, ""}, - { USB_VID_CITIZEN, USB_PID_CITIZEN_CW02, P_DNP_DS40, ""}, - { USB_VID_DNP, USB_PID_DNP_DS820, P_DNP_DS820, ""}, - { 0, 0, 0, ""} + { USB_VID_CITIZEN, USB_PID_DNP_DS40, P_DNP_DS40, NULL, "dnp-ds40"}, // Also Citizen CX + { USB_VID_CITIZEN, USB_PID_DNP_DS80, P_DNP_DS80, NULL, "dnp-ds80"}, // Also Citizen CX-W and Mitsubishi CP-3800DW + { USB_VID_CITIZEN, USB_PID_DNP_DS80D, P_DNP_DS80D, NULL, "dnp-ds80dx"}, + { USB_VID_CITIZEN, USB_PID_DNP_DSRX1, P_DNP_DSRX1, NULL, "dnp-dsrx1"}, // Also Citizen CY + { USB_VID_DNP, USB_PID_DNP_DS620, P_DNP_DS620, NULL, "dnp-ds620"}, + { USB_VID_DNP, USB_PID_DNP_DS820, P_DNP_DS820, NULL, "dnp-ds820"}, + { USB_VID_CITIZEN, USB_PID_CITIZEN_CW01, P_CITIZEN_CW01, NULL, "citizen-cw-01"}, // Also OP900 ? + { USB_VID_CITIZEN, USB_PID_CITIZEN_CW02, P_CITIZEN_OP900II, NULL, "citizen-cw-02"}, // Also OP900II + { USB_VID_CITIZEN, USB_PID_CITIZEN_CX02, P_DNP_DS620, NULL, "citizen-cx-02"}, + { 0, 0, 0, NULL, NULL} } }; + +/* Legacy CW-01 spool file support */ + +static int cw01_read_parse(struct dnpds40_printjob *job, int data_fd, + struct cw01_spool_hdr *hdr, int read_data) +{ + int i, remain; + uint32_t j; + uint8_t *buf; + uint8_t plane_hdr[14]; + + remain = hdr->plane_len * 3; + buf = malloc(remain); + if (!buf) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + j = read_data - sizeof(*hdr); + memcpy(buf, job->databuf, j); + remain -= j; + /* Read in the remaining spool data */ + while (remain) { + i = read(data_fd, buf + j, remain); + + if (i < 0) { + free(buf); + return i; + } + + remain -= i; + j += i; + } + + /* Generate plane header (same for all planes) */ + j = cpu_to_le32(hdr->plane_len) + 24; + memset(plane_hdr, 0, sizeof(plane_hdr)); + plane_hdr[0] = 0x42; + plane_hdr[1] = 0x4d; + memcpy(plane_hdr + 2, &j, sizeof(j)); + plane_hdr[10] = 0x40; + plane_hdr[11] = 0x04; + + /* Okay, generate a new stream into job->databuf! */ +#if 0 + job->datalen += sprintf((char*)job->databuf + job->datalen, + "\033PCNTRL QTY 00000008%07d\r", hdr->copies); + job->datalen += sprintf((char*)job->databuf + job->datalen, + "\033PCNTRL CUTTER 0000000800000000"); +#else + /* QTY is stripped from the stream, and CUTTER is stashed away */ + job->cutter = 0; +#endif + + j = 0; + + /* Y plane */ + job->datalen += sprintf((char*)job->databuf + job->datalen, + "\033PIMAGE YPLANE %08u", hdr->plane_len + 24); + memcpy(job->databuf + job->datalen, plane_hdr, sizeof(plane_hdr)); + job->datalen += sizeof(plane_hdr); + memcpy(job->databuf + job->datalen, buf + j, hdr->plane_len); + job->datalen += hdr->plane_len; + j += hdr->plane_len; + memset(job->databuf + job->datalen, 0, 10); + job->datalen += 10; + + /* M plane */ + job->datalen += sprintf((char*)job->databuf + job->datalen, + "\033PIMAGE MPLANE %08u", hdr->plane_len + 24); + memcpy(job->databuf + job->datalen, plane_hdr, sizeof(plane_hdr)); + job->datalen += sizeof(plane_hdr); + memcpy(job->databuf + job->datalen, buf + j, hdr->plane_len); + job->datalen += hdr->plane_len; + j += hdr->plane_len; + memset(job->databuf + job->datalen, 0, 10); + job->datalen += 10; + + /* C plane */ + job->datalen += sprintf((char*)job->databuf + job->datalen, + "\033PIMAGE CPLANE %08u", hdr->plane_len + 24); + memcpy(job->databuf + job->datalen, plane_hdr, sizeof(plane_hdr)); + job->datalen += sizeof(plane_hdr); + memcpy(job->databuf + job->datalen, buf + j, hdr->plane_len); + job->datalen += hdr->plane_len; + j += hdr->plane_len; + memset(job->databuf + job->datalen, 0, 10); + job->datalen += 10; + + /* Start */ + job->datalen += sprintf((char*)job->databuf + job->datalen, + "\033PCNTRL START "); + + /* We're done */ + free(buf); + + return CUPS_BACKEND_OK; +} + +/* + +Basic spool file format for CW01 + +TT RR NN 00 XX XX XX XX 00 00 00 00 <- FILE header. + + NN : copies (0x01 or more) + RR : resolution; 0 == 334 dpi, 1 == 600dpi + TT : type 0x02 == 4x6, 0x01 == 5x3.5 + XX XX XX XX : plane length (LE) + plane length * 3 + 12 == file length. + +Followed by three planes, each with this header: + +28 00 00 00 00 08 00 00 RR RR 00 00 01 00 08 00 +00 00 00 00 00 00 00 00 5a 33 00 00 YY YY 00 00 +00 01 00 00 00 00 00 00 + + RR RR : rows in LE format + YY YY : 0x335a (334dpi) or 0x5c40 (600dpi) + +Followed by 1024 bytes of color tables: + + ff ff ff 00 ... 00 00 00 00 + +1024+40 = 1064 bytes of header per plane. + +Always have 2048 columns of data. + +followed by (2048 * rows) bytes of data. + +*/ diff --git a/src/cups/backend_kodak1400.c b/src/cups/backend_kodak1400.c index e87307f..99bd678 100644 --- a/src/cups/backend_kodak1400.c +++ b/src/cups/backend_kodak1400.c @@ -1,7 +1,7 @@ /* * Kodak Professional 1400/805 CUPS backend -- libusb-1.0 version * - * (c) 2013-2016 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -75,19 +76,26 @@ struct kodak1400_hdr { /* Private data structure */ +struct kodak1400_printjob { + struct kodak1400_hdr hdr; + uint8_t *plane_r; + uint8_t *plane_g; + uint8_t *plane_b; + + int copies; +}; + 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; - uint8_t *plane_g; - uint8_t *plane_b; + struct marker marker; }; static int send_plane(struct kodak1400_ctx *ctx, + const struct kodak1400_printjob *job, uint8_t planeno, uint8_t *planedata, uint8_t *cmdbuf) { @@ -113,9 +121,9 @@ static int send_plane(struct kodak1400_ctx *ctx, cmdbuf[3] = planeno; if (planedata) { - temp16 = htons(ctx->hdr.columns); + temp16 = htons(job->hdr.columns); memcpy(cmdbuf+7, &temp16, 2); - temp16 = htons(ctx->hdr.rows); + temp16 = htons(job->hdr.rows); memcpy(cmdbuf+9, &temp16, 2); } @@ -125,10 +133,10 @@ static int send_plane(struct kodak1400_ctx *ctx, if (planedata) { int i; - for (i = 0 ; i < ctx->hdr.rows ; i++) { + for (i = 0 ; i < job->hdr.rows ; i++) { if ((ret = send_data(ctx->dev, ctx->endp_down, - planedata + i * ctx->hdr.columns, - ctx->hdr.columns))) + planedata + i * job->hdr.columns, + job->hdr.columns))) return ret; } } @@ -292,24 +300,38 @@ static void *kodak1400_init(void) return ctx; } -static void kodak1400_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int kodak1400_attach(void *vctx, struct libusb_device_handle *dev, int type, + 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; + ctx->type = type; + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = "Unknown"; + ctx->marker.levelmax = -1; + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void kodak1400_cleanup_job(const void *vjob) +{ + const struct kodak1400_printjob *job = vjob; - device = libusb_get_device(dev); - libusb_get_device_descriptor(device, &desc); + if (job->plane_r) + free(job->plane_r); + if (job->plane_g) + free(job->plane_g); + if (job->plane_b) + free(job->plane_b); - ctx->type = lookup_printer_type(&kodak1400_backend, - desc.idVendor, desc.idProduct); + free((void*)job); } static void kodak1400_teardown(void *vctx) { @@ -318,83 +340,76 @@ static void kodak1400_teardown(void *vctx) { if (!ctx) return; - if (ctx->plane_r) - free(ctx->plane_r); - if (ctx->plane_g) - free(ctx->plane_g); - if (ctx->plane_b) - free(ctx->plane_b); free(ctx); } -static int kodak1400_read_parse(void *vctx, int data_fd) { +static int kodak1400_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct kodak1400_ctx *ctx = vctx; int i, ret; + struct kodak1400_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; - if (ctx->plane_r) { - free(ctx->plane_r); - ctx->plane_r = NULL; - } - if (ctx->plane_g) { - free(ctx->plane_g); - ctx->plane_g = NULL; - } - if (ctx->plane_b) { - free(ctx->plane_b); - ctx->plane_b = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } + memset(job, 0, sizeof(*job)); + job->copies = copies; /* Read in then validate header */ - ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); - if (ret < 0 || ret != sizeof(ctx->hdr)) { + ret = read(data_fd, &job->hdr, sizeof(job->hdr)); + if (ret < 0 || ret != sizeof(job->hdr)) { if (ret == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d/%d)\n", - ret, 0, (int)sizeof(ctx->hdr)); + ret, 0, (int)sizeof(job->hdr)); perror("ERROR: Read failed"); return CUPS_BACKEND_CANCEL; } - if (ctx->hdr.hdr[0] != 'P' || - ctx->hdr.hdr[1] != 'G' || - ctx->hdr.hdr[2] != 'H' || - ctx->hdr.hdr[3] != 'D') { + if (job->hdr.hdr[0] != 'P' || + job->hdr.hdr[1] != 'G' || + job->hdr.hdr[2] != 'H' || + job->hdr.hdr[3] != 'D') { ERROR("Unrecognized data format!\n"); return CUPS_BACKEND_CANCEL; } - ctx->hdr.planesize = le32_to_cpu(ctx->hdr.planesize); - ctx->hdr.rows = le16_to_cpu(ctx->hdr.rows); - ctx->hdr.columns = le16_to_cpu(ctx->hdr.columns); + job->hdr.planesize = le32_to_cpu(job->hdr.planesize); + job->hdr.rows = le16_to_cpu(job->hdr.rows); + job->hdr.columns = le16_to_cpu(job->hdr.columns); /* Set up plane data */ - ctx->plane_r = malloc(ctx->hdr.planesize); - ctx->plane_g = malloc(ctx->hdr.planesize); - ctx->plane_b = malloc(ctx->hdr.planesize); - if (!ctx->plane_r || !ctx->plane_g || !ctx->plane_b) { + job->plane_r = malloc(job->hdr.planesize); + job->plane_g = malloc(job->hdr.planesize); + job->plane_b = malloc(job->hdr.planesize); + if (!job->plane_r || !job->plane_g || !job->plane_b) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + return CUPS_BACKEND_RETRY_CURRENT; } - for (i = 0 ; i < ctx->hdr.rows ; i++) { + for (i = 0 ; i < job->hdr.rows ; i++) { int j; uint8_t *ptr; for (j = 0 ; j < 3 ; j++) { int remain; if (j == 0) - ptr = ctx->plane_r + i * ctx->hdr.columns; + ptr = job->plane_r + i * job->hdr.columns; else if (j == 1) - ptr = ctx->plane_g + i * ctx->hdr.columns; + ptr = job->plane_g + i * job->hdr.columns; else if (j == 2) - ptr = ctx->plane_b + i * ctx->hdr.columns; + ptr = job->plane_b + i * job->hdr.columns; + else + ptr = NULL; - remain = ctx->hdr.columns; + remain = job->hdr.columns; do { ret = read(data_fd, ptr, remain); if (ret < 0) { ERROR("Read failed (%d/%d/%u) (%d/%u @ %d)\n", - ret, remain, ctx->hdr.columns, - i, ctx->hdr.rows, j); + ret, remain, job->hdr.columns, + i, job->hdr.rows, j); perror("ERROR: Read failed"); return CUPS_BACKEND_CANCEL; } @@ -404,13 +419,15 @@ static int kodak1400_read_parse(void *vctx, int data_fd) { } } + *vjob = job; + return CUPS_BACKEND_OK; } static uint8_t idle_data[READBACK_LEN] = { 0xe4, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static int kodak1400_main_loop(void *vctx, int copies) { +static int kodak1400_main_loop(void *vctx, const void *vjob) { struct kodak1400_ctx *ctx = vctx; uint8_t rdbuf[READBACK_LEN], rdbuf2[READBACK_LEN]; @@ -418,6 +435,16 @@ static int kodak1400_main_loop(void *vctx, int copies) { int last_state = -1, state = S_IDLE; int num, ret; uint16_t temp16; + int copies; + + const struct kodak1400_printjob *job = vjob; + + if (!ctx) + return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; + + copies = job->copies; top: if (state != last_state) { @@ -473,9 +500,9 @@ top: cmdbuf[0] = 0x1b; cmdbuf[1] = 0x5a; cmdbuf[2] = 0x53; - temp16 = be16_to_cpu(ctx->hdr.columns); + temp16 = be16_to_cpu(job->hdr.columns); memcpy(cmdbuf+3, &temp16, 2); - temp16 = be16_to_cpu(ctx->hdr.rows); + temp16 = be16_to_cpu(job->hdr.rows); memcpy(cmdbuf+5, &temp16, 2); if ((ret = send_data(ctx->dev, ctx->endp_down, @@ -486,7 +513,7 @@ top: memset(cmdbuf, 0, CMDBUF_LEN); cmdbuf[0] = 0x1b; cmdbuf[1] = 0x59; - cmdbuf[2] = ctx->hdr.matte; // ??? + cmdbuf[2] = job->hdr.matte; // ??? if ((ret = send_data(ctx->dev, ctx->endp_down, cmdbuf, CMDBUF_LEN))) @@ -496,7 +523,7 @@ top: memset(cmdbuf, 0, CMDBUF_LEN); cmdbuf[0] = 0x1b; cmdbuf[1] = 0x60; - cmdbuf[2] = ctx->hdr.laminate; + cmdbuf[2] = job->hdr.laminate; if (send_data(ctx->dev, ctx->endp_down, cmdbuf, CMDBUF_LEN)) @@ -506,7 +533,7 @@ top: memset(cmdbuf, 0, CMDBUF_LEN); cmdbuf[0] = 0x1b; cmdbuf[1] = 0x62; - cmdbuf[2] = ctx->hdr.lam_strength; + cmdbuf[2] = job->hdr.lam_strength; if ((ret = send_data(ctx->dev, ctx->endp_down, cmdbuf, CMDBUF_LEN))) @@ -516,7 +543,7 @@ top: memset(cmdbuf, 0, CMDBUF_LEN); cmdbuf[0] = 0x1b; cmdbuf[1] = 0x61; - cmdbuf[2] = ctx->hdr.unk1; // ??? + cmdbuf[2] = job->hdr.unk1; // ??? if ((ret = send_data(ctx->dev, ctx->endp_down, cmdbuf, CMDBUF_LEN))) @@ -526,7 +553,7 @@ top: break; case S_PRINTER_READY_Y: INFO("Sending YELLOW plane\n"); - if ((ret = send_plane(ctx, 1, ctx->plane_b, cmdbuf))) + if ((ret = send_plane(ctx, job, 1, job->plane_b, cmdbuf))) return CUPS_BACKEND_FAILED; state = S_PRINTER_SENT_Y; break; @@ -536,7 +563,7 @@ top: break; case S_PRINTER_READY_M: INFO("Sending MAGENTA plane\n"); - if ((ret = send_plane(ctx, 2, ctx->plane_g, cmdbuf))) + if ((ret = send_plane(ctx, job, 2, job->plane_g, cmdbuf))) return CUPS_BACKEND_FAILED; state = S_PRINTER_SENT_M; break; @@ -546,13 +573,13 @@ top: break; case S_PRINTER_READY_C: INFO("Sending CYAN plane\n"); - if ((ret = send_plane(ctx, 3, ctx->plane_r, cmdbuf))) + if ((ret = send_plane(ctx, job, 3, job->plane_r, cmdbuf))) return CUPS_BACKEND_FAILED; state = S_PRINTER_SENT_C; break; case S_PRINTER_SENT_C: if (!memcmp(rdbuf, idle_data, READBACK_LEN)) { - if (ctx->hdr.laminate) + if (job->hdr.laminate) state = S_PRINTER_READY_L; else state = S_PRINTER_DONE; @@ -560,7 +587,7 @@ top: break; case S_PRINTER_READY_L: INFO("Laminating page\n"); - if ((ret = send_plane(ctx, 4, NULL, cmdbuf))) + if ((ret = send_plane(ctx, job, 4, NULL, cmdbuf))) return CUPS_BACKEND_FAILED; state = S_PRINTER_SENT_L; break; @@ -604,26 +631,53 @@ top: return CUPS_BACKEND_OK; } +static int kodak1400_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct kodak1400_ctx *ctx = vctx; + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + /* Exported */ #define USB_VID_KODAK 0x040A #define USB_PID_KODAK_1400 0x4022 #define USB_PID_KODAK_805 0x4034 +#define USB_VID_MITSU 0x06D3 +#define USB_PID_MITSU_3020D 0x038B +#define USB_PID_MITSU_3020DA 0x03AA + +static const char *kodak1400_prefixes[] = { + "kodak1400", // Family driver, do NOT nuke! + "kodak-1400", "kodak-805", "mitsubishi-3020d", "mitsubishi-3020da", + // backwards compatibility + "kodak805", "mitsu3020d", "mitsu3020da", + // Extras. + "mitsubishi-3020dae", "mitsubishi-3020de", "mitsubishi-3020du", + NULL, +}; struct dyesub_backend kodak1400_backend = { .name = "Kodak 1400/805", - .version = "0.34", - .uri_prefix = "kodak1400", + .version = "0.39", + .uri_prefixes = kodak1400_prefixes, .cmdline_usage = kodak1400_cmdline, .cmdline_arg = kodak1400_cmdline_arg, .init = kodak1400_init, .attach = kodak1400_attach, .teardown = kodak1400_teardown, + .cleanup_job = kodak1400_cleanup_job, .read_parse = kodak1400_read_parse, .main_loop = kodak1400_main_loop, + .query_markers = kodak1400_query_markers, .devices = { - { USB_VID_KODAK, USB_PID_KODAK_1400, P_KODAK_1400_805, "Kodak"}, - { USB_VID_KODAK, USB_PID_KODAK_805, P_KODAK_1400_805, "Kodak"}, - { 0, 0, 0, ""} + { USB_VID_KODAK, USB_PID_KODAK_1400, P_KODAK_1400_805, "Kodak", "kodak-1400"}, + { USB_VID_KODAK, USB_PID_KODAK_805, P_KODAK_1400_805, "Kodak", "kodak-805"}, + { USB_VID_MITSU, USB_PID_MITSU_3020D, P_KODAK_1400_805, NULL, "mitsubishi-3020d"}, + { USB_VID_MITSU, USB_PID_MITSU_3020DA, P_KODAK_1400_805, NULL, "mitsubishi-3020da" }, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_kodak605.c b/src/cups/backend_kodak605.c index b301739..64524d4 100644 --- a/src/cups/backend_kodak605.c +++ b/src/cups/backend_kodak605.c @@ -1,7 +1,7 @@ /* * Kodak 605 Photo Printer CUPS backend -- libusb-1.0 version * - * (c) 2013-2016 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -165,6 +166,12 @@ static const char *kodak68xx_mediatypes(int type) #define CMDBUF_LEN 4 /* Private data structure */ +struct kodak605_printjob { + struct kodak605_hdr hdr; + uint8_t *databuf; + int datalen; +}; + struct kodak605_ctx { struct libusb_device_handle *dev; uint8_t endp_up; @@ -172,14 +179,10 @@ struct kodak605_ctx { int type; uint8_t jobid; - struct kodak605_hdr hdr; - struct kodak605_media_list *media; - uint8_t *databuf; - int datalen; + struct marker marker; - uint8_t last_donor; }; static int kodak605_get_media(struct kodak605_ctx *ctx, struct kodak605_media_list *media) @@ -216,6 +219,40 @@ static int kodak605_get_media(struct kodak605_ctx *ctx, struct kodak605_media_li return 0; } +static int kodak605_get_status(struct kodak605_ctx *ctx, struct kodak605_status *sts) +{ + uint8_t cmdbuf[4]; + + 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, sizeof(cmdbuf)))) + return ret; + + /* Read in the printer status */ + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) sts, sizeof(*sts), &num); + if (ret < 0) + return ret; + + if (num < (int)sizeof(*sts)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*sts)); + return CUPS_BACKEND_FAILED; + } + + if (sts->hdr.result != RESULT_SUCCESS) { + ERROR("Unexpected response from status query (%x)!\n", sts->hdr.result); + return CUPS_BACKEND_FAILED; + } + + return 0; +} + static void *kodak605_init(void) { struct kodak605_ctx *ctx = malloc(sizeof(struct kodak605_ctx)); @@ -227,40 +264,54 @@ static void *kodak605_init(void) ctx->media = malloc(MAX_MEDIA_LEN); - ctx->type = P_ANY; - return ctx; } -static void kodak605_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int kodak605_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct kodak605_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(&kodak605_backend, - desc.idVendor, desc.idProduct); + ctx->type = type; /* Make sure jobid is sane */ ctx->jobid = jobid & 0x7f; if (!ctx->jobid) ctx->jobid++; - /* Init */ - ctx->last_donor = 255; + if (test_mode < TEST_MODE_NOATTACH) { + /* Query media info */ + if (kodak605_get_media(ctx, ctx->media)) { + ERROR("Can't query media\n"); + return CUPS_BACKEND_FAILED; + } + } else { + int media_code = KODAK68x0_MEDIA_6TR2; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")); - /* Query media info */ - if (kodak605_get_media(ctx, ctx->media)) { - ERROR("Can't query media\n"); + ctx->media->type = media_code; } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = kodak68xx_mediatypes(ctx->media->type); + ctx->marker.levelmax = 100; /* Ie percentage */ + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void kodak605_cleanup_job(const void *vjob) +{ + const struct kodak605_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void kodak605_teardown(void *vctx) { @@ -269,57 +320,59 @@ static void kodak605_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); free(ctx); } -static int kodak605_read_parse(void *vctx, int data_fd) { +static int kodak605_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct kodak605_ctx *ctx = vctx; int ret; + struct kodak605_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_CANCEL; - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } + memset(job, 0, sizeof(*job)); /* Read in then validate header */ - ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); - if (ret < 0 || ret != sizeof(ctx->hdr)) { + ret = read(data_fd, &job->hdr, sizeof(job->hdr)); + if (ret < 0 || ret != sizeof(job->hdr)) { if (ret == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d/%d)\n", - ret, 0, (int)sizeof(ctx->hdr)); + ret, 0, (int)sizeof(job->hdr)); perror("ERROR: Read failed"); return CUPS_BACKEND_CANCEL; } - if (ctx->hdr.hdr[0] != 0x01 || - ctx->hdr.hdr[1] != 0x40 || - ctx->hdr.hdr[2] != 0x0a || - ctx->hdr.hdr[3] != 0x00) { + if (job->hdr.hdr[0] != 0x01 || + job->hdr.hdr[1] != 0x40 || + job->hdr.hdr[2] != 0x0a || + job->hdr.hdr[3] != 0x00) { ERROR("Unrecognized data format!\n"); return CUPS_BACKEND_CANCEL; } - ctx->datalen = le16_to_cpu(ctx->hdr.rows) * le16_to_cpu(ctx->hdr.columns) * 3; - ctx->databuf = malloc(ctx->datalen); - if (!ctx->databuf) { + job->datalen = le16_to_cpu(job->hdr.rows) * le16_to_cpu(job->hdr.columns) * 3; + job->databuf = malloc(job->datalen); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + return CUPS_BACKEND_RETRY_CURRENT; } { - int remain = ctx->datalen; - uint8_t *ptr = ctx->databuf; + int remain = job->datalen; + uint8_t *ptr = job->databuf; do { ret = read(data_fd, ptr, remain); if (ret < 0) { ERROR("Read failed (%d/%d/%d)\n", - ret, remain, ctx->datalen); + ret, remain, job->datalen); perror("ERROR: Read failed"); return CUPS_BACKEND_CANCEL; } @@ -328,61 +381,36 @@ static int kodak605_read_parse(void *vctx, int data_fd) { } while (remain); } - return CUPS_BACKEND_OK; -} - -static int kodak605_get_status(struct kodak605_ctx *ctx, struct kodak605_status *sts) -{ - uint8_t cmdbuf[4]; - - 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, sizeof(cmdbuf)))) - return ret; - - /* Read in the printer status */ - ret = read_data(ctx->dev, ctx->endp_up, - (uint8_t*) sts, sizeof(*sts), &num); - if (ret < 0) - return ret; - - if (num < (int)sizeof(*sts)) { - ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*sts)); - return CUPS_BACKEND_FAILED; - } + /* Printer handles generating copies.. */ + if (le16_to_cpu(job->hdr.copies) < copies) + job->hdr.copies = cpu_to_le16(copies); - if (sts->hdr.result != RESULT_SUCCESS) { - ERROR("Unexpected response from status query (%x)!\n", sts->hdr.result); - return CUPS_BACKEND_FAILED; - } + *vjob = job; - return 0; + return CUPS_BACKEND_OK; } -static int kodak605_main_loop(void *vctx, int copies) { +static int kodak605_main_loop(void *vctx, const void *vjob) { struct kodak605_ctx *ctx = vctx; struct kodak605_status sts; int num, ret; + const struct kodak605_printjob *job = vjob; + if (!ctx) return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; - /* Printer handles generating copies.. */ - if (le16_to_cpu(ctx->hdr.copies) < copies) - ctx->hdr.copies = cpu_to_le16(copies); + struct kodak605_hdr hdr; + memcpy(&hdr, &job->hdr, sizeof(hdr)); /* 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) + if (ctx->media->entries[num].rows == hdr.rows && + ctx->media->entries[num].cols == hdr.columns) break; } if (num == ctx->media->count) { @@ -390,22 +418,15 @@ static int kodak605_main_loop(void *vctx, int copies) { return CUPS_BACKEND_HOLD; } - /* Tell CUPS about the consumables we report */ - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", kodak68xx_mediatypes(ctx->media->type)); - ATTR("marker-types=ribbonWax\n"); - INFO("Waiting for printer idle\n"); while(1) { if ((ret = kodak605_get_status(ctx, &sts))) return CUPS_BACKEND_FAILED; - if (ctx->last_donor != sts.donor) { - ctx->last_donor = sts.donor; - ATTR("marker-levels=%u\n", sts.donor); + if (ctx->marker.levelnow != sts.donor) { + ctx->marker.levelnow = sts.donor; + dump_markers(&ctx->marker, 1, 0); } // XXX check for errors @@ -430,12 +451,12 @@ static int kodak605_main_loop(void *vctx, int copies) { } /* Use specified jobid */ - ctx->hdr.jobid = ctx->jobid; + hdr.jobid = ctx->jobid; { INFO("Sending image header (internal id %u)\n", ctx->jobid); if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*)&ctx->hdr, sizeof(ctx->hdr)))) + (uint8_t*)&hdr, sizeof(hdr)))) return CUPS_BACKEND_FAILED; struct kodak605_sts_hdr resp; @@ -453,7 +474,7 @@ static int kodak605_main_loop(void *vctx, int copies) { INFO("Sending image data\n"); if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf, ctx->datalen))) + job->databuf, job->datalen))) return CUPS_BACKEND_FAILED; INFO("Image data sent\n"); @@ -464,13 +485,12 @@ static int kodak605_main_loop(void *vctx, int copies) { if ((kodak605_get_status(ctx, &sts)) != 0) return CUPS_BACKEND_FAILED; + if (ctx->marker.levelnow != sts.donor) { + ctx->marker.levelnow = sts.donor; + dump_markers(&ctx->marker, 1, 0); + } // XXX check for errors - if (ctx->last_donor != sts.donor) { - ctx->last_donor = sts.donor; - ATTR("marker-levels=%u\n", sts.donor); - } // XXX check for errors ? - /* Wait for completion */ if (sts.b1_id == ctx->jobid && sts.b1_complete == sts.b1_total) break; @@ -671,21 +691,46 @@ static int kodak605_cmdline_arg(void *vctx, int argc, char **argv) return 0; } +static int kodak605_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct kodak605_ctx *ctx = vctx; + struct kodak605_status sts; + + /* Query printer status */ + if (kodak605_get_status(ctx, &sts)) + return CUPS_BACKEND_FAILED; + + ctx->marker.levelnow = sts.donor; + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + +static const char *kodak605_prefixes[] = { + "kodak605", // Family driver, do NOT nuke. + "kodak-605", + NULL, +}; + /* Exported */ struct dyesub_backend kodak605_backend = { .name = "Kodak 605", - .version = "0.27", - .uri_prefix = "kodak605", + .version = "0.33", + .uri_prefixes = kodak605_prefixes, .cmdline_usage = kodak605_cmdline, .cmdline_arg = kodak605_cmdline_arg, .init = kodak605_init, .attach = kodak605_attach, .teardown = kodak605_teardown, + .cleanup_job = kodak605_cleanup_job, .read_parse = kodak605_read_parse, .main_loop = kodak605_main_loop, + .query_markers = kodak605_query_markers, .devices = { - { USB_VID_KODAK, USB_PID_KODAK_605, P_KODAK_605, "Kodak"}, - { 0, 0, 0, ""} + { USB_VID_KODAK, USB_PID_KODAK_605, P_KODAK_605, "Kodak", "kodak-605"}, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_kodak6800.c b/src/cups/backend_kodak6800.c index f535797..79d950b 100644 --- a/src/cups/backend_kodak6800.c +++ b/src/cups/backend_kodak6800.c @@ -1,7 +1,7 @@ /* * Kodak 6800/6850 Photo Printer CUPS backend -- libusb-1.0 version * - * (c) 2013-2017 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * Development of this backend was sponsored by: * @@ -22,11 +22,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -222,6 +223,13 @@ struct kodak68x0_media_readback { #define CMDBUF_LEN 17 /* Private data structure */ +struct kodak6800_printjob { + struct kodak6800_hdr hdr; + uint8_t *databuf; + int datalen; + int copies; +}; + struct kodak6800_ctx { struct libusb_device_handle *dev; uint8_t endp_up; @@ -233,11 +241,7 @@ struct kodak6800_ctx { struct kodak68x0_media_readback *media; - struct kodak6800_hdr hdr; - uint8_t *databuf; - int datalen; - - uint8_t last_donor; + struct marker marker; }; static const char *kodak68xx_mediatypes(int type) @@ -276,8 +280,6 @@ static int kodak6800_do_cmd(struct kodak6800_ctx *ctx, return 0; } - - static void kodak68x0_dump_mediainfo(struct kodak68x0_media_readback *media) { int i; @@ -690,6 +692,7 @@ static int kodak6800_get_tonecurve(struct kodak6800_ctx *ctx, char *fname) cmdbuf[14] = 0x00; cmdbuf[15] = 0x00; + respbuf[0] = 0xff; /* Issue command and get response */ if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf), respbuf, sizeof(respbuf), @@ -1004,40 +1007,54 @@ static void *kodak6800_init(void) ctx->media = malloc(MAX_MEDIA_LEN); - ctx->type = P_ANY; - return ctx; } -static void kodak6800_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int kodak6800_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct kodak6800_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(&kodak6800_backend, - desc.idVendor, desc.idProduct); + ctx->type = type; /* Ensure jobid is sane */ ctx->jobid = jobid & 0x7f; if (!ctx->jobid) ctx->jobid++; - /* Init */ - ctx->last_donor = 255; + if (test_mode < TEST_MODE_NOATTACH) { + /* Query media info */ + if (kodak6800_get_mediainfo(ctx, ctx->media)) { + ERROR("Can't query media\n"); + return CUPS_BACKEND_FAILED; + } + } else { + int media_code = KODAK68x0_MEDIA_6TR2; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")); - /* Query media info */ - if (kodak6800_get_mediainfo(ctx, ctx->media)) { - ERROR("Can't query media\n"); + ctx->media->type = media_code; } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = kodak68xx_mediatypes(ctx->media->type); + ctx->marker.levelmax = 100; /* Ie percentage */ + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void kodak6800_cleanup_job(const void *vjob) +{ + const struct kodak6800_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void kodak6800_teardown(void *vctx) { @@ -1046,57 +1063,59 @@ static void kodak6800_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); free(ctx); } -static int kodak6800_read_parse(void *vctx, int data_fd) { +static int kodak6800_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct kodak6800_ctx *ctx = vctx; int ret; + struct kodak6800_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } + memset(job, 0, sizeof(*job)); /* Read in then validate header */ - ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); - if (ret < 0 || ret != sizeof(ctx->hdr)) { + ret = read(data_fd, &job->hdr, sizeof(job->hdr)); + if (ret < 0 || ret != sizeof(job->hdr)) { if (ret == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d/%d)\n", - ret, 0, (int)sizeof(ctx->hdr)); + ret, 0, (int)sizeof(job->hdr)); perror("ERROR: Read failed"); return CUPS_BACKEND_CANCEL; } - if (ctx->hdr.hdr[0] != 0x03 || - ctx->hdr.hdr[1] != 0x1b || - ctx->hdr.hdr[2] != 0x43 || - ctx->hdr.hdr[3] != 0x48 || - ctx->hdr.hdr[4] != 0x43) { + if (job->hdr.hdr[0] != 0x03 || + job->hdr.hdr[1] != 0x1b || + job->hdr.hdr[2] != 0x43 || + job->hdr.hdr[3] != 0x48 || + job->hdr.hdr[4] != 0x43) { ERROR("Unrecognized data format!\n"); return CUPS_BACKEND_CANCEL; } - ctx->datalen = be16_to_cpu(ctx->hdr.rows) * be16_to_cpu(ctx->hdr.columns) * 3; - ctx->databuf = malloc(ctx->datalen); - if (!ctx->databuf) { + job->datalen = be16_to_cpu(job->hdr.rows) * be16_to_cpu(job->hdr.columns) * 3; + job->databuf = malloc(job->datalen); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + return CUPS_BACKEND_RETRY_CURRENT; } { - int remain = ctx->datalen; - uint8_t *ptr = ctx->databuf; + int remain = job->datalen; + uint8_t *ptr = job->databuf; do { ret = read(data_fd, ptr, remain); if (ret < 0) { ERROR("Read failed (%d/%d/%d)\n", - ret, remain, ctx->datalen); + ret, remain, job->datalen); perror("ERROR: Read failed"); return CUPS_BACKEND_CANCEL; } @@ -1105,29 +1124,39 @@ static int kodak6800_read_parse(void *vctx, int data_fd) { } while (remain); } + /* Fix max print count. */ + if (copies > 9999) // XXX test against remaining media + copies = 9999; + + /* Printer handles generating copies.. */ + if (le16_to_cpu(job->hdr.copies) < copies) + job->hdr.copies = cpu_to_be16(uint16_to_packed_bcd(copies)); + + *vjob = job; + return CUPS_BACKEND_OK; } -static int kodak6800_main_loop(void *vctx, int copies) { +static int kodak6800_main_loop(void *vctx, const void *vjob) { struct kodak6800_ctx *ctx = vctx; struct kodak68x0_status_readback status; int num, ret; + const struct kodak6800_printjob *job = vjob; + if (!ctx) return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; - /* Fix max print count. */ - if (copies > 9999) // XXX test against remaining media - copies = 9999; - - /* Printer handles generating copies.. */ - ctx->hdr.copies = cpu_to_be16(uint16_to_packed_bcd(copies)); + struct kodak6800_hdr hdr; + memcpy(&hdr, &job->hdr, sizeof(hdr)); /* Validate against supported media list */ 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 && + if (ctx->media->sizes[num].height == hdr.rows && + ctx->media->sizes[num].width == hdr.columns && ctx->media->sizes[num].code2 == 0x00) // XXX code2? break; } @@ -1136,22 +1165,15 @@ static int kodak6800_main_loop(void *vctx, int copies) { return CUPS_BACKEND_HOLD; } - /* Tell CUPS about the consumables we report */ - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", kodak68xx_mediatypes(ctx->media->type)); - ATTR("marker-types=ribbonWax\n"); - INFO("Waiting for printer idle\n"); while(1) { if (kodak6800_get_status(ctx, &status)) return CUPS_BACKEND_FAILED; - if (ctx->last_donor != status.donor) { - ctx->last_donor = status.donor; - ATTR("marker-levels=%u\n", status.donor); + if (ctx->marker.levelnow != status.donor) { + ctx->marker.levelnow = status.donor; + dump_markers(&ctx->marker, 1, 0); } if (status.status1 == STATE_STATUS1_ERROR) { @@ -1189,20 +1211,20 @@ static int kodak6800_main_loop(void *vctx, int copies) { return ret; } - ctx->hdr.jobid = ctx->jobid; + hdr.jobid = ctx->jobid; #if 0 /* If we want to disable 4x6 rewind on 8x6 media.. */ // XXX not sure about this...? - if (ctx->hdr.size == 0x00 && + if (hdr.size == 0x00 && be16_to_cpu(ctx->media->sizes[0].width) == 0x0982) { - ctx->hdr.size = 0x06; - ctx->hdr.mode = 0x01; + hdr.size = 0x06; + hdr.mode = 0x01; } #endif INFO("Sending Print Job (internal id %u)\n", ctx->jobid); - if ((ret = kodak6800_do_cmd(ctx, (uint8_t*) &ctx->hdr, sizeof(ctx->hdr), + if ((ret = kodak6800_do_cmd(ctx, (uint8_t*) &hdr, sizeof(hdr), &status, sizeof(status), &num))) return ret; @@ -1215,7 +1237,7 @@ static int kodak6800_main_loop(void *vctx, int copies) { // sleep(1); // Appears to be necessary for reliability INFO("Sending image data\n"); if ((send_data(ctx->dev, ctx->endp_down, - ctx->databuf, ctx->datalen)) != 0) + job->databuf, job->datalen)) != 0) return CUPS_BACKEND_FAILED; INFO("Waiting for printer to acknowledge completion\n"); @@ -1224,9 +1246,9 @@ static int kodak6800_main_loop(void *vctx, int copies) { if (kodak6800_get_status(ctx, &status)) return CUPS_BACKEND_FAILED; - if (ctx->last_donor != status.donor) { - ctx->last_donor = status.donor; - ATTR("marker-levels=%u\n", status.donor); + if (ctx->marker.levelnow != status.donor) { + ctx->marker.levelnow = status.donor; + dump_markers(&ctx->marker, 1, 0); } if (status.status1 == STATE_STATUS1_ERROR) { @@ -1237,9 +1259,9 @@ static int kodak6800_main_loop(void *vctx, int copies) { } /* If all prints are complete, we're done! */ - if (status.b1_jobid == ctx->hdr.jobid && status.b1_complete == status.b1_total) + if (status.b1_jobid == hdr.jobid && status.b1_complete == status.b1_total) break; - if (status.b2_jobid == ctx->hdr.jobid && status.b2_complete == status.b2_total) + if (status.b2_jobid == hdr.jobid && status.b2_complete == status.b2_total) break; if (fast_return) { @@ -1254,23 +1276,50 @@ static int kodak6800_main_loop(void *vctx, int copies) { return CUPS_BACKEND_OK; } +static int kodak6800_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct kodak6800_ctx *ctx = vctx; + struct kodak68x0_status_readback status; + + /* Query printer status */ + if (kodak6800_get_status(ctx, &status)) + return CUPS_BACKEND_FAILED; + + ctx->marker.levelnow = status.donor; + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + +static const char *kodak6800_prefixes[] = { + "kodak68x0", // Family driver, do not nuke. + "kodak-6800", "kodak-6850", + // Backwards-compatibility + "kodak6800", "kodak6850", + NULL +}; + /* Exported */ struct dyesub_backend kodak6800_backend = { .name = "Kodak 6800/6850", - .version = "0.58", - .uri_prefix = "kodak6800", + .version = "0.65", + .uri_prefixes = kodak6800_prefixes, .cmdline_usage = kodak6800_cmdline, .cmdline_arg = kodak6800_cmdline_arg, .init = kodak6800_init, .attach = kodak6800_attach, .teardown = kodak6800_teardown, + .cleanup_job = kodak6800_cleanup_job, .read_parse = kodak6800_read_parse, .main_loop = kodak6800_main_loop, .query_serno = kodak6800_query_serno, + .query_markers = kodak6800_query_markers, .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, ""} + { USB_VID_KODAK, USB_PID_KODAK_6800, P_KODAK_6800, "Kodak", "kodak-6800"}, + { USB_VID_KODAK, USB_PID_KODAK_6850, P_KODAK_6850, "Kodak", "kodak-6850"}, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_magicard.c b/src/cups/backend_magicard.c new file mode 100644 index 0000000..d356bba --- /dev/null +++ b/src/cups/backend_magicard.c @@ -0,0 +1,1143 @@ +/* + * Magicard card printer family CUPS backend -- libusb-1.0 version + * + * (c) 2017-2018 Solomon Peachy + * + * 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, see . + * + * [http://www.gnu.org/licenses/gpl-2.0.html] + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BACKEND magicard_backend + +#include "backend_common.h" + +/* Exported */ +#define USB_VID_MAGICARD 0x0C1F +#define USB_PID_MAGICARD_TANGO2E 0x1800 +#define USB_PID_MAGICARD_ENDURO 0x4800 // ?? +#define USB_PID_MAGICARD_ENDUROPLUS 0x880A // ?? + +/* Gamma tables computed with this perl program: + + my $input_bpp = 8; + my $output_bpp = 6; + my $gamma = 1/1.8; # or 1/2.2 or whatever. + + my $i; + + for (my $i = 0 ; $i < (2 ** $input_bpp) ; $i++) { + my $linear = $i / (2 ** $input_bpp); + my $gc = ($linear ** $gamma) * (2 ** $output_bpp); + $gc = int($gc); + print "$gc, "; + } + +*/ + +static uint8_t gammas[2][256] = { + /* Gamma = 2.2 */ + { + 0, 5, 7, 8, 9, 10, 11, 12, 13, 13, 14, 15, 15, 16, 17, + 17, 18, 18, 19, 19, 20, 20, 20, 21, 21, 22, 22, 23, 23, 23, + 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, + 29, 29, 29, 29, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, + 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, + 36, 36, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39, 39, + 39, 39, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 42, 42, + 42, 42, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, + 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, + 47, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 50, + 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 52, 52, + 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, + 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, + 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, + 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, + 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, + }, + /* Gamma = 1.8 */ + { + 0, 2, 4, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 12, + 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, + 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 24, + 24, 24, 24, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 28, 28, + 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, + 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, + 35, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 38, 38, 38, 38, + 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, + 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, + 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 47, 47, 47, + 47, 47, 47, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, + 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 52, 52, 52, + 52, 52, 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, + 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 57, 57, + 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, + 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, + 61, 61, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, + } +}; + +struct magicard_printjob { + uint8_t *databuf; + int datalen; + + int hdr_len; + int copies; +}; + +/* Private data structure */ +struct magicard_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + + struct marker marker; +}; + +struct magicard_cmd_header { + uint8_t guard[9]; /* 0x05 */ + uint8_t guard2[1]; /* 0x01 */ + uint8_t cmd[4]; /* 'REQ,' */ + uint8_t subcmd[4]; /* '???,' */ + uint8_t arg[4]; /* '???,' */ + uint8_t footer[2]; /* 0x1c 0x03 */ +}; + +struct magicard_cmd_simple_header { + uint8_t guard[9]; /* 0x05 */ + uint8_t guard2[1]; /* 0x01 */ + uint8_t cmd[]; /* '???' */ +// uint8_t footer[2]; /* 0x1c 0x03 */ +}; + +struct magicard_resp_header { + uint8_t guard[1]; /* 0x01 */ + uint8_t subcmd_arg[7]; /* '???,???' */ + uint8_t data[0]; /* freeform resp */ +// uint8_t term[2]; /* 0x2c 0x03 terminates! */ +}; + +struct magicard_requests { + char *key; + char *desc; + uint8_t type; +}; + +enum { + TYPE_UNKNOWN = 0, + TYPE_STRING, + TYPE_STRINGINT, + TYPE_STRINGINT_HEX, + TYPE_IPADDR, + TYPE_YESNO, + TYPE_MODEL, +}; + +/* Data definitions */ +static struct magicard_requests magicard_sta_requests[] = { + { "MSR", "Printer Serial Number", TYPE_STRING }, + { "PSR", "Print Head Serial Number", TYPE_STRING }, + { "BSR", "PCB Serial Number", TYPE_STRING }, + { "VRS", "Firmware Version", TYPE_STRING }, + { "FDC", "Head Density", TYPE_STRINGINT }, /* 25 per step */ + { "FSP", "Image Start", TYPE_STRINGINT }, /* 8 steps per pixel */ + { "FEP", "Image End", TYPE_STRINGINT }, /* 8 steps per pixel */ + { "FSS", "Ramp Adjust", TYPE_STRINGINT }, + { "FPP", "Head Position", TYPE_STRINGINT }, /* L-R alignment */ + { "MDL", "Model", TYPE_MODEL }, /* 0 == Standard. Others? */ + { "PID", "USB PID", TYPE_STRINGINT_HEX }, /* ASCII integer, but needs to be shown as hex */ + { "VID", "USB VID", TYPE_STRINGINT_HEX }, /* ASCII integer, but needs to be shown as hex */ + { "USN", "USB Serial Number", TYPE_STRING }, + { "UPN", "USB Manufacturer", TYPE_STRING }, + { "MAC", "Ethernet MAC Address", TYPE_STRING }, + { "DYN", "Dynamic Address", TYPE_YESNO }, /* 1 == yes, 0 == no */ + { "IPA", "IP Address", TYPE_IPADDR }, /* ASCII signed integer */ + { "SNM", "IP Netmask", TYPE_IPADDR }, /* ASCII signed integer */ + { "GWY", "IP Gateway", TYPE_IPADDR }, /* ASCII signed integer */ + + { "TCQ", "Total Cards Printed", TYPE_STRINGINT }, + { "TCP", "Prints on Head", TYPE_STRINGINT }, + { "TCN", "Cleaning Cycles", TYPE_STRINGINT }, + { "CCQ", "Cards Since Last Cleaning", TYPE_STRINGINT }, + { "TPQ", "Total Panels Printed", TYPE_STRINGINT }, + { "CCP", "Cards between Cleaning Prompts", TYPE_STRINGINT }, + { "CPQ", "Panels Since Last Cleaning", TYPE_STRINGINT }, + { "DFR", "Panels Remaining", TYPE_STRINGINT }, // cook somehow? + { "CLP", "Cleaning Prompt", TYPE_STRING }, + + // CRQ: OFF ?? Cleaning overdue? + // CHK: checksum of fw? (8 chars, hex?) + // TES: ??? signed int? IP addr? + // RAMP: ??? hangs. + + { NULL, NULL, 0 } +}; + +// Sensors: CAM1 CAM2 TACHO FLIP DYE BARCODE LID FRONT REAR BUTTON TEMP ON OFF +// Languages: ENG ITA POR FRA DEU ESP SCH + +/* Helper functions */ +static int magicard_build_cmd(uint8_t *buf, + char *cmd, char *subcmd, char *arg) +{ + struct magicard_cmd_header *hdr = (struct magicard_cmd_header *) buf; + + memset(hdr->guard, 0x05, sizeof(hdr->guard)); + hdr->guard2[0] = 0x01; + memcpy(hdr->cmd, cmd, 3); + hdr->cmd[3] = ','; + memcpy(hdr->subcmd, subcmd, 3); + hdr->subcmd[3] = ','; + memcpy(hdr->arg, arg, 3); + hdr->arg[3] = ','; + hdr->footer[0] = 0x1c; + hdr->footer[1] = 0x03; + + return sizeof(*hdr); +} + +static int magicard_build_cmd_simple(uint8_t *buf, + char *cmd) +{ + struct magicard_cmd_simple_header *hdr = (struct magicard_cmd_simple_header *) buf; + int len = strlen(cmd); + + memset(hdr->guard, 0x05, sizeof(hdr->guard)); + hdr->guard2[0] = 0x01; + strncpy((char*)hdr->cmd, cmd, len); + hdr->cmd[len] = 0x1c; + hdr->cmd[len+1] = 0x03; + + return (sizeof(*hdr) + len + 2); +} + + +static uint8_t * magicard_parse_resp(uint8_t *buf, uint16_t len, uint16_t *resplen) +{ + struct magicard_resp_header *hdr = (struct magicard_resp_header *) buf; + + *resplen = len - sizeof(hdr->guard) - sizeof(hdr->subcmd_arg) - 2; + + return hdr->data; +} + +static int magicard_query_sensors(struct magicard_ctx *ctx) +{ + int ret = 0; + int i; + uint8_t buf[256]; + char buf2[24]; + + for (i = 1 ; ; i++) { + int num = 0; + + snprintf(buf2, sizeof(buf2), "SNR%d", i); + ret = magicard_build_cmd_simple(buf, buf2); + + if ((ret = send_data(ctx->dev, ctx->endp_down, + buf, ret))) + return ret; + + memset(buf, 0, sizeof(buf)); + + ret = read_data(ctx->dev, ctx->endp_up, + buf, sizeof(buf), &num); + + if (ret < 0) + return ret; + + if (!memcmp(buf, "END", 3)) + break; + + buf[num] = 0; + INFO("%s\n", buf); + } + return 0; +} + +static int magicard_selftest_card(struct magicard_ctx *ctx) +{ + int ret = 0; + uint8_t buf[256]; + + ret = magicard_build_cmd_simple(buf, "TST,"); + + ret = send_data(ctx->dev, ctx->endp_down, + buf, ret); + return ret; +} + +static int magicard_reset(struct magicard_ctx *ctx) +{ + int ret = 0; + uint8_t buf[256]; + + ret = magicard_build_cmd_simple(buf, "RST,"); + + ret = send_data(ctx->dev, ctx->endp_down, + buf, ret); + return ret; +} + +static int magicard_eject(struct magicard_ctx *ctx) +{ + int ret = 0; + uint8_t buf[256]; + + ret = magicard_build_cmd_simple(buf, "EJT,"); + + ret = send_data(ctx->dev, ctx->endp_down, + buf, ret); + return ret; +} + +static int magicard_query_printer(struct magicard_ctx *ctx) +{ + int ret = 0; + int i; + uint8_t buf[256]; + char buf2[24]; + + for (i = 1 ; ; i++) { + int num = 0; + + snprintf(buf2, sizeof(buf2), "QPR%d", i); + ret = magicard_build_cmd_simple(buf, buf2); + + if ((ret = send_data(ctx->dev, ctx->endp_down, + buf, ret))) + return ret; + + memset(buf, 0, sizeof(buf)); + + ret = read_data(ctx->dev, ctx->endp_up, + buf, sizeof(buf), &num); + + if (ret < 0) + return ret; + + if (!memcmp(buf, "END", 3)) + break; + + buf[num] = 0; + INFO("%s\n", buf); + } + return 0; +} + +static int magicard_query_status(struct magicard_ctx *ctx) +{ + int ret = 0; + int i; + uint8_t buf[256]; + + for (i = 0 ; ; i++) { + uint16_t resplen = 0; + uint8_t *resp; + int num = 0; + + if (magicard_sta_requests[i].key == NULL) + break; + + ret = magicard_build_cmd(buf, "REQ", "STA", + magicard_sta_requests[i].key); + + if ((ret = send_data(ctx->dev, ctx->endp_down, + buf, ret))) + return ret; + + memset(buf, 0, sizeof(buf)); + + ret = read_data(ctx->dev, ctx->endp_up, + buf, sizeof(buf), &num); + + if (ret < 0) + return ret; + + resp = magicard_parse_resp(buf, num, &resplen); + resp[resplen] = 0; + switch(magicard_sta_requests[i].type) { + case TYPE_IPADDR: { + int32_t ipaddr; + uint8_t *addr = (uint8_t *) &ipaddr; + ipaddr = atoi((char*)resp); + INFO("%s:\t%d.%d.%d.%d\n", + magicard_sta_requests[i].desc, + addr[3], addr[2], addr[1], addr[0]); + break; + } + case TYPE_YESNO: { + int val = atoi((char*)resp); + INFO("%s:\t%s\n", + magicard_sta_requests[i].desc, + val? "Yes" : "No"); + break; + } + case TYPE_MODEL: { + int val = atoi((char*)resp); + INFO("%s:\t%s\n", + magicard_sta_requests[i].desc, + val == 0? "Standard" : "Unknown"); + break; + } + case TYPE_STRINGINT_HEX: { + int val = atoi((char*)resp); + INFO("%s:\t%X\n", + magicard_sta_requests[i].desc, + val); + break; + } + case TYPE_STRINGINT: + // treat differently? + case TYPE_STRING: + case TYPE_UNKNOWN: + default: + INFO("%s:\t%s\n", + magicard_sta_requests[i].desc, + resp); + } + } + + return ret; +} + +/* Main driver */ +static void* magicard_init(void) +{ + struct magicard_ctx *ctx = malloc(sizeof(struct magicard_ctx)); + if (!ctx) { + ERROR("Memory Allocation Failure!"); + return NULL; + } + memset(ctx, 0, sizeof(struct magicard_ctx)); + return ctx; +} + +static int magicard_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +{ + struct magicard_ctx *ctx = vctx; + + UNUSED(jobid); + + ctx->dev = dev; + ctx->endp_up = endp_up; + ctx->endp_down = endp_down; + ctx->type = type; + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; // XXX YMCK too! + ctx->marker.name = "Unknown"; // LC1/LC3/LC6/LC8 + ctx->marker.levelmax = -1; + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void magicard_cleanup_job(const void *vjob) +{ + const struct magicard_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); +} + +static void magicard_teardown(void *vctx) { + struct magicard_ctx *ctx = vctx; + + if (!ctx) + return; + + free(ctx); +} + +static void downscale_and_extract(int gamma, uint32_t pixels, + uint8_t *y_i, uint8_t *m_i, uint8_t *c_i, + uint8_t *y_o, uint8_t *m_o, uint8_t *c_o, uint8_t *k_o) +{ + uint32_t i; + + for (i = 0 ; i < pixels; i++) + { + uint8_t y, m, c; + uint8_t k = 0; + uint32_t j; + uint32_t row; + uint32_t col; + uint32_t b_offset; + uint8_t b_shift; + + /* Downscale color planes from 8bpp -> 6bpp; */ + if (gamma) { + if (gamma > 2) + gamma = 2; + gamma--; + y = gammas[gamma][*y_i++]; + m = gammas[gamma][*m_i++]; + c = gammas[gamma][*c_i++]; + } else { + y = *y_i++ >> 2; + m = *m_i++ >> 2; + c = *c_i++ >> 2; + } + + /* Extract "true black" from ymc data, if enabled */ + if (k_o && y == 0x3f && m == 0x3f && c == 0x3f) { + k = 1; + y = m = c = 0; + } + + /* Compute row number and offsets */ + row = i / 672; + col = i - (row * 672); + b_offset = col / 8; + b_shift = 7 - (col - (b_offset * 8)); + + /* Now, for each row, break it down into sub-chunks */ + for (j = 0 ; j < 6 ; j++) { + if (b_shift == 7) { + y_o[row * 504 + j * 84 + b_offset] = 0; + m_o[row * 504 + j * 84 + b_offset] = 0; + c_o[row * 504 + j * 84 + b_offset] = 0; + } + if (y & (1 << j)) + y_o[row * 504 + j * 84 + b_offset] |= (1 << b_shift); + if (m & (1 << j)) + m_o[row * 504 + j * 84 + b_offset] |= (1 << b_shift); + if (c & (1 << j)) + c_o[row * 504 + j * 84 + b_offset] |= (1 << b_shift); + } + + /* And resin black, if enabled */ + if (k_o) { + if (b_shift == 7) { + k_o[row * 84 + b_offset] = 0; + } + if (k) + k_o[row * 84 + b_offset] |= (1 << b_shift); + } + } +} + +#define MAX_PRINTJOB_LEN (1016*672*4) + 1024 /* 1016*672 * 4color */ +#define INITIAL_BUF_LEN 1024 +static int magicard_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { + struct magicard_ctx *ctx = vctx; + uint8_t initial_buf[INITIAL_BUF_LEN + 1]; + uint32_t buf_offset = 0; + int i; + + uint8_t *in_y, *in_m, *in_c; + uint8_t *out_y, *out_m, *out_c, *out_k; + uint32_t len_y = 0, len_m = 0, len_c = 0, len_k = 0; + int gamma = 0; + + uint8_t x_gp_8bpp; + uint8_t x_gp_rk; + uint8_t k_only; + + struct magicard_printjob *job = NULL; + + if (!ctx) + return CUPS_BACKEND_FAILED; + + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; + + /* Read in the first chunk */ + i = read(data_fd, initial_buf, INITIAL_BUF_LEN); + if (i < 0) { + magicard_cleanup_job(job); + return i; + } else if (i == 0) { + magicard_cleanup_job(job); + return CUPS_BACKEND_CANCEL; /* Ie no data, we're done */ + } else if (i < INITIAL_BUF_LEN) { + magicard_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + + /* Basic Sanity Check */ + if (initial_buf[0] != 0x05 || + initial_buf[64] != 0x01 || + initial_buf[65] != 0x2c) { + ERROR("Unrecognized header data format @%d!\n", job->datalen); + magicard_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + + initial_buf[INITIAL_BUF_LEN] = 0; + + /* We can start allocating! */ + if (job->databuf) { + free(job->databuf); + job->databuf = NULL; + } + job->datalen = 0; + job->databuf = malloc(MAX_PRINTJOB_LEN); + if (!job->databuf) { + ERROR("Memory allocation failure!\n"); + magicard_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; + } + + /* Copy over initial header */ + memcpy(job->databuf + job->datalen, initial_buf + buf_offset, 65); + job->datalen += 65; + buf_offset += 65; + + /* Start parsing headers */ + x_gp_8bpp = x_gp_rk = k_only = job->hdr_len = 0; + + char *ptr; + ptr = strtok((char*)initial_buf + ++buf_offset, ",\x1c"); + while (ptr && *ptr != 0x1c) { + if (!strcmp("X-GP-8", ptr)) { + x_gp_8bpp = 1; + } else if (!strncmp("TDT", ptr, 3)) { + /* Strip out the timestamp, replace it with one from the backend */ + } else if (!strncmp("IMF", ptr,3)) { + /* Strip out the image format, replace it with backend */ +// } else if (!strncmp("ESS", ptr, 3)) { +// /* Strip out copies */ + } else if (!strcmp("X-GP-RK", ptr)) { + x_gp_rk = 1; + } else if (!strncmp("ICC", ptr,3)) { + /* Gamma curve is not handled by printer, + strip it out and use it! */ + gamma = atoi(ptr + 3); + } else if (!strncmp("SZ", ptr, 2)) { + if (ptr[2] == 'B') { + len_y = atoi(ptr + 3); + } else if (ptr[2] == 'G') { + len_m = atoi(ptr + 3); + } else if (ptr[2] == 'R') { + len_c = atoi(ptr + 3); + } else if (ptr[2] == 'K') { + len_k = atoi(ptr + 3); + } + } else { + /* Everything else goes in */ + job->datalen += sprintf((char*)job->databuf + job->datalen, ",%s", ptr); + } + + /* Keep going */ + buf_offset += strlen(ptr) + 1; + /* Peek ahead to see if this is it */ + if (initial_buf[buf_offset + 1] == 0x1c) + break; + /* Otherwise continue to the next token */ + ptr = strtok(NULL, ",\x1c"); + } + + /* Sanity checks */ + if (!len_y || !len_m || !len_c) { + ERROR("Plane lengths missing? %u/%u/%u!\n", len_y, len_m, len_c); + magicard_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + if (len_y != len_m || len_y != len_c) { + ERROR("Inconsistent data plane lengths! %u/%u/%u!\n", len_y, len_m, len_c); + magicard_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + if (x_gp_rk && len_k) { + ERROR("Data stream already has a K layer!\n"); + magicard_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + + /* Generate a timestamp */ + job->datalen += sprintf((char*)job->databuf + job->datalen, ",TDT%08X", (uint32_t) time(NULL)); + + /* Generate image format tag */ + if (k_only == 1) { + job->datalen += sprintf((char*)job->databuf + job->datalen, ",IMFK"); + } else if (x_gp_rk || len_k) { + /* We're adding K, so make this BGRK */ + job->datalen += sprintf((char*)job->databuf + job->datalen, ",IMFBGRK"); + } else { + /* Just BGR */ + job->datalen += sprintf((char*)job->databuf + job->datalen, ",IMFBGR"); + } + + /* Insert SZB/G/R/K length descriptors */ + if (x_gp_8bpp) { + if (k_only == 1) { + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZK%u", len_c / 8); + } else { + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZB%u", len_y * 6 / 8); + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZG%u", len_m * 6 / 8); + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZR%u", len_c * 6 / 8); + /* Add in a SZK length indication if requested */ + if (x_gp_rk == 1) { + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZK%u", len_c / 8); + } + } + } else { + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZB%u", len_y); + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZG%u", len_m); + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZR%u", len_c); + /* Add in a SZK length indication if requested */ + if (len_k) { + job->datalen += sprintf((char*)job->databuf + job->datalen, ",SZK%u", len_k); + } + } + + /* Terminate command stream */ + job->databuf[job->datalen++] = 0x1c; + + /* Let's figure out how long the image data stream is supposed to be. */ + uint32_t remain; + if (k_only) { + remain = len_k + 3; + } else { + remain = len_y + len_m + len_c + 3 * 3; + if (len_k) + remain += len_k + 3; + } + /* Offset the stuff we already read in. */ + remain -= INITIAL_BUF_LEN - buf_offset; + remain++; /* Add in a byte for the end of job marker. This is our final value. */ + + /* This is how much of the initial buffer is the header length. */ + job->hdr_len = job->datalen; + + if (x_gp_8bpp) { + uint32_t srcbuf_offset = INITIAL_BUF_LEN - buf_offset; + uint8_t *srcbuf = malloc(MAX_PRINTJOB_LEN); + if (!srcbuf) { + magicard_cleanup_job(job); + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + + memcpy(srcbuf, initial_buf + buf_offset, srcbuf_offset); + + /* Finish loading the data */ + while (remain > 0) { + i = read(data_fd, srcbuf + srcbuf_offset, remain); + if (i < 0) { + ERROR("Data Read Error: %d (%u) @%u)\n", i, remain, srcbuf_offset); + magicard_cleanup_job(job); + free(srcbuf); + return i; + } + if (i == 0) { + ERROR("Short read! (%d/%u)\n", i, remain); + magicard_cleanup_job(job); + free(srcbuf); + return CUPS_BACKEND_CANCEL; + } + srcbuf_offset += i; + remain -= i; + } + + // XXX handle conversion of K-only jobs. if needed. + + /* set up source pointers */ + in_y = srcbuf; + in_m = in_y + len_y + 3; + in_c = in_m + len_m + 3; + + /* Set up destination pointers */ + out_y = job->databuf + job->datalen; + out_m = out_y + (len_y * 6 / 8) + 3; + out_c = out_m + (len_m * 6 / 8) + 3; + out_k = out_c + (len_c * 6 / 8) + 3; + + /* Termination of each plane */ + memcpy(out_m - 3, in_y + len_y, 3); + memcpy(out_c - 3, in_m + len_m, 3); + memcpy(out_k - 3, in_c + len_c, 3); + + if (!x_gp_rk) + out_k = NULL; + + INFO("Converting image data to printer's native format %s\n", x_gp_rk ? "and extracting K channel" : ""); + + downscale_and_extract(gamma, len_y, in_y, in_m, in_c, + out_y, out_m, out_c, out_k); + + /* Pad out the length appropriately. */ + job->datalen += ((len_c * 6 / 8) + 3) * 3; + + /* If there's a K plane, compute length.. */ + if (out_k) { + job->datalen += (len_c / 8); + job->databuf[job->datalen++] = 0x1c; + job->databuf[job->datalen++] = 0x4b; + job->databuf[job->datalen++] = 0x3a; + } + + /* Terminate the entire stream */ + job->databuf[job->datalen++] = 0x03; + + free(srcbuf); + } else { + uint32_t srcbuf_offset = INITIAL_BUF_LEN - buf_offset; + memcpy(job->databuf + job->datalen, initial_buf + buf_offset, srcbuf_offset); + job->datalen += srcbuf_offset; + + /* Finish loading the data */ + while (remain > 0) { + i = read(data_fd, job->databuf + job->datalen, remain); + if (i < 0) { + ERROR("Data Read Error: %d (%u) @%d)\n", i, remain, job->datalen); + magicard_cleanup_job(job); + return i; + } + if (i == 0) { + magicard_cleanup_job(job); + ERROR("Short read! (%d/%u)\n", i, remain); + return CUPS_BACKEND_CANCEL; + } + job->datalen += i; + remain -= i; + } + } + + *vjob = job; + + return CUPS_BACKEND_OK; +} + +static int magicard_main_loop(void *vctx, const void *vjob) { + struct magicard_ctx *ctx = vctx; + int ret; + + const struct magicard_printjob *job = vjob; + + // XXX printer handles copy generation.. + // but it's a numeric parameter. Bleh. + if (!ctx) + return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; + + copies = job->copies; +top: + if ((ret = send_data(ctx->dev, ctx->endp_down, + job->databuf, job->hdr_len))) + return CUPS_BACKEND_FAILED; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + job->databuf + job->hdr_len, job->datalen - job->hdr_len))) + return CUPS_BACKEND_FAILED; + + /* Clean up */ + if (terminate) + copies = 1; + + INFO("Print complete (%d copies remaining)\n", copies - 1); + + if (copies && --copies) { + goto top; + } + + return CUPS_BACKEND_OK; +} + +static void magicard_cmdline(void) +{ + DEBUG("\t\t[ -s ] # Query status\n"); + DEBUG("\t\t[ -q ] # Query information\n"); + DEBUG("\t\t[ -I ] # Query printer sensors\n"); + DEBUG("\t\t[ -E ] # Eject card\n"); + DEBUG("\t\t[ -T ] # Print self-test card\n"); + DEBUG("\t\t[ -R ] # Reset printer\n"); +} + +static int magicard_cmdline_arg(void *vctx, int argc, char **argv) +{ + struct magicard_ctx *ctx = vctx; + int i, j = 0; + + if (!ctx) + return -1; + + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "sqEIRT")) >= 0) { + switch(i) { + GETOPT_PROCESS_GLOBAL + case 's': + j = magicard_query_status(ctx); + break; + case 'q': + j = magicard_query_printer(ctx); + break; + case 'E': + j = magicard_eject(ctx); + break; + case 'I': + j = magicard_query_sensors(ctx); + break; + case 'R': + j = magicard_reset(ctx); + break; + case 'T': + j = magicard_selftest_card(ctx); + break; + } + + if (j) return j; + } + + return 0; +} + +static int magicard_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct magicard_ctx *ctx = vctx; + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + +static const char *magicard_prefixes[] = { + "magicard", // Family name + "magicard-tango-2e", "magicard-enduro", "magicard-enduroplus", + // extras + "magicard-rio-2e", + // backwards compatibility + "tango2e", "enduro", "enduroplus", + NULL +}; + +struct dyesub_backend magicard_backend = { + .name = "Magicard family", + .version = "0.15", + .uri_prefixes = magicard_prefixes, + .cmdline_arg = magicard_cmdline_arg, + .cmdline_usage = magicard_cmdline, + .init = magicard_init, + .attach = magicard_attach, + .teardown = magicard_teardown, + .cleanup_job = magicard_cleanup_job, + .read_parse = magicard_read_parse, + .main_loop = magicard_main_loop, + .query_markers = magicard_query_markers, + .devices = { + { USB_VID_MAGICARD, USB_PID_MAGICARD_TANGO2E, P_MAGICARD, NULL, "magicard-tango2e"}, + { USB_VID_MAGICARD, USB_PID_MAGICARD_ENDURO, P_MAGICARD, NULL, "magicard-enduro"}, + { USB_VID_MAGICARD, USB_PID_MAGICARD_ENDUROPLUS, P_MAGICARD, NULL, "magicard-enduroplus"}, + { USB_VID_MAGICARD, 0xFFFF, P_MAGICARD, NULL, "magicard"}, + { 0, 0, 0, NULL, "magicard"} + } +}; + +/* Magicard family Spool file format (Tango2e/Rio2e/AvalonE family) + + This one was rather fun to figure out. + + * Job starts with a sequence of 64 '0x05' + * Command sequence starts with 0x01 + * Commands are textual and comma-separated. + * Most are passed through ignored, except for: + * SZB, SZG, SZR, SZK -- indicate length of respective data plane + * IMF -- Image format (BGR/BGRK/K) + * X-GP-8 -- Tells backend to convert from Gutenprint's 8bpp data + * X-GP-RK -- Tells backend to extract K channel from color data + * Command sequence ends with 0x1c + * Image plane data follows, in the order of the SZ# entries + * Plane lengths are specified by the SZ# entry. + * Color planes are actually Y/M/C rather than B/G/R! + * Each plane terminates with 0x1c __ 0x3a, where __ is 0x42, 0x47, 0x52, + and 0x4b for B/G/R/K respectively. Terminator is _not_ part of length. + * Image data is 6bpp for B/G/R and 1bpp for K, 672*1016 pixels + * Organized in a series of 84-byte rows. + * Byte data is LSB first. + * Each row is a single stripe of a single bit of a pixel, so + color data is b0b0b0b0.. b1b1b1b1.. .. b5b5b5b5. + * Job ends with 0x03 + + ** ** ** ** ** ** + + Firmware updates: + + 0x05 (x9) 0x01 REQ,FRM###### 0x1c + + Where ###### is the length of the firmware image. + + Then send over 64 bytes at a time until it's done. + + Then send 0x03 to mark end of job. + + Follow it with: + + 0x01 STA,CHK########, 0x03 (8-digit checksum?) + + 0x05 (x9) 0x01 REQ,UPG, 0x1c 0x03 + + ** ** ** ** ** ** + + Known commands seen in print jobs: + + BAC%s Backside format (CKO, KO, C, CO, K) -- Only used with Duplex. + CKI%s Custom Holokote (ON or OFF) + CPW%s Color power level (0-100, default 50) + DPX%s Duplex (ON or OFF) + EOI%d Card alignment end (0-100, default 50) + ESS%d Number of copies (1-?) + HGT%d Image Height (always seems to be 1016) + HKM%06X Holokote hole. bitwise number, each bit corresponds to an area. + HKT%d Holokote type (1 is "ultra secure, 2 is "interlocking rings", etc) + HPH%s Holopatch (ON or OFF) + IMF%s Image Data Format (BGR, BGRK, K) + KPW%s Black power level (0-100, default 50) + LAN%s Printer display lanaguage (ENG, ITA, POR, FRA, DEU, ESP, SCH) + LC%d Force media type (LC1, LC3, LC6, LC8 for YMCKO/MONO/KO/YMCKOK) + NCT%d,%d,%d,%d Overcoat hole + OPW%s Overcoat power level (0-100, default 50) + OVR%s Overcoat (ON or OFF) + PAG%d Page number (always 1, except 2 if printing duplex backside) + PAT%d Holopatch area (0-24) + REJ%s Reject faulty cards (ON or OFF) + SOI%d Card alignment start (0-100, default 50) + SLW%s Colorsure (ON or OFF) + SZB%d Blue data length + SZG%d Green data length + SZK%d Black data length + SZR%d Red data length + TDT%08X Driver-supplied timestamp of print job. + USF%s Holokote (ON or OFF) + VER%s Inform the printer of the driver version (seems to be ignored) + WID%d Image Width (always seems to be 642) + + Mag-stripe encoding: + + MAG%d Magstripe position (1, 2, or 3) + BPI%d Bits per Inch (75 or 210) + MPC%d Character encoding (5 or 7) + COE%s 'H'igh or 'L'ow coercivity + + Unknown commands seen in print jobs: + + DDD%s ? (only seen '50') -- Could it be K alignment? + KEE ? + NNN%s ? (Seen 'OFF') + NOC%d ? (Seen '1') (Seems to start a job) + PCT%d,%d,%d,%d ? Print area, seems fixed @ 0,0, 1025, 641) + RT2 ? + TRO%d ? (Seen '0', appears with Holokote) + XCO%d ? X start offset (always seems to be 0) + YCO%d ? Y start offset (always seems to be 0) + + Unknown commands: (Seen in firmware guts) + + AAA + AMS + BBB%d Numeric parameter + CLR + FBF + FTC + HFD%s String parameter + IPM + KKK + LBL + LLL + LRC + MGV%s "ON" or "OFF" but no idea + MMM + PAR + RDM + SNR + SSP + + Unknown commands unique to Tango +L (ie w/ Laminator support) + + FRN + LAM + LAM_DLY + LAM_SPD + LAM_LEN + LAM_END + LAM_STA + LAM_DEG + LAM_FLM + LAM_KBD + LAM_MOD + + Commands consumed by backend: + + ICC%d Gamma curve (0, 1, 2) -- off, 2.2, or 1.8 respectively. + X-GP-8 Raw data is 8bpp. needs to be converted. + X-GP-RK Extract K channel from color data. + + Open questions: + + * How to query/read magstripe + * How to set IP address (etc) + * How to set other parameters + + "Simple Commands" (REQ,....,) + + RST Reset printer + TST Generate self-test page + EJT Eject card + + Other "Simple commands" referenced in Rio Pro/Enduro+ docs + + DEALERSERVICE%s ON/OFF (enter/exit dealer service mode) + CAM Reset print head cam position + CHP%s UP/DOWN Feed card into smart encoder + CLN Cleaning cycle + DYE Re-init dye film + ENC Test encoding cycle + FEED%d 0/1,+ 0/1, load card into standby, >1 feed N cards. + FLIP Flip card in printer + FRN%s ON/OFF -- Film saving + HEAD%s UP/DOWN -- Raise or lower print head. + RAMP%d 0-100 Density ramp, 50 default + SET Saves settings into NVDATA + STN Re-init Holokote + SNS Soak cycle, test all sensors + SHW%s CAM, TACHO, FLIP, DYE, LID, FRONT, MID, READ, BUTTON1, BUTTON2, + SMART, TEMP, ON, OFF + LNG%d 0/1/2/3/4/5 == ENG/POR/FRE/GER/SPA/ITA + RUN%s CAM, FEED, DYE, MAIN, FLIPPER, FLIPROLL, FAN, PANEL, POUT, CAL, LCD, + OFF + FLM%s Y/M/C/K/O Align ribbon at corresponding panel + FCL Init dye calibration routine + FCL###### Set dye color to ###### (RGB hex) + +*/ diff --git a/src/cups/backend_mitsu70x.c b/src/cups/backend_mitsu70x.c index 00547a0..154fabb 100644 --- a/src/cups/backend_mitsu70x.c +++ b/src/cups/backend_mitsu70x.c @@ -1,7 +1,7 @@ /* * Mitsubishi CP-D70/D707 Photo Printer CUPS backend -- libusb-1.0 version * - * (c) 2013-2017 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -112,7 +113,6 @@ typedef int (*send_image_dataFN)(struct BandImage *out, void *context, #define USB_PID_MITSU_D70X 0x3B30 #define USB_PID_MITSU_K60 0x3B31 #define USB_PID_MITSU_D80 0x3B36 -#define USB_PID_MITSU_D90 0x3B60 #define USB_VID_KODAK 0x040a #define USB_PID_KODAK305 0x404f #define USB_VID_FUJIFILM 0x04cb @@ -125,29 +125,47 @@ typedef int (*send_image_dataFN)(struct BandImage *out, void *context, #define CHUNK_LEN (256*1024) /* Private data structure */ -struct mitsu70x_ctx { - struct libusb_device_handle *dev; - uint8_t endp_up; - uint8_t endp_down; - int type; - +struct mitsu70x_printjob { uint8_t *databuf; int datalen; - uint32_t matte; + uint8_t *spoolbuf; + int spoolbuflen; - uint16_t jobid; uint16_t rows; uint16_t cols; + uint32_t planelen; + uint32_t matte; + int raw_format; + int copies; - uint16_t last_donor_l; - uint16_t last_donor_u; - int num_decks; + int decks_exact[2]; /* Media is exact match */ + int decks_ok[2]; /* Media can be used */ + + /* These are used only for the image processing */ + int sharpen; /* ie mhdr.sharpen - 1 */ + int reverse; char *laminatefname; char *lutfname; char *cpcfname; char *ecpcfname; +}; + +struct mitsu70x_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + + uint16_t jobid; + + struct marker marker[2]; + uint8_t medias[2]; + + uint16_t last_l; + uint16_t last_u; + int num_decks; void *dl_handle; lib70x_getapiversionFN GetAPIVersion; @@ -170,12 +188,6 @@ struct mitsu70x_ctx { char *last_cpcfname; char *last_ecpcfname; - int raw_format; - int reverse; - int sharpen; /* ie mhdr.sharpen - 1 */ - - uint8_t rew[2]; /* 1 for rewind ok (default!) */ - struct BandImage output; }; @@ -190,7 +202,9 @@ struct mitsu70x_jobstatus { uint8_t mecha_status[2]; uint8_t temperature; uint8_t error_status[3]; - uint8_t reserved[6]; + uint8_t mecha_status_up[2]; + uint8_t temperature_up; + uint8_t error_status_up[3]; } __attribute__((packed)); struct mitsu70x_job { @@ -306,8 +320,7 @@ struct mitsu70x_status_deck { uint8_t mecha_status[2]; uint8_t temperature; /* D70/D80 family only, K60 no? */ uint8_t error_status[3]; - uint8_t rsvd_a[10]; /* K60 family [1] == temperature? [3:6] == lifetime prints in BCD */ - + uint8_t rsvd_a[10]; /* K60 [1] == temperature? All: [3:6] == some counter in BCD. K60 [9] == ?? */ uint8_t media_brand; uint8_t media_type; uint8_t rsvd_b[2]; @@ -315,7 +328,8 @@ struct mitsu70x_status_deck { uint16_t remain; /* media remaining */ uint8_t rsvd_c[2]; uint8_t lifetime_prints[4]; /* lifetime prints on deck + 10, in BCD! */ - uint16_t rsvd_e[17]; + uint8_t rsvd_d[2]; // Unknown + uint16_t rsvd_e[16]; /* all 80 00 */ } __attribute__((packed)); struct mitsu70x_status_ver { @@ -324,13 +338,15 @@ struct mitsu70x_status_ver { } __attribute__((packed)); struct mitsu70x_printerstatus_resp { - uint8_t hdr[4]; /* E4 56 32 31 */ + uint8_t hdr[4]; /* E4 56 32 30 */ uint8_t memory; uint8_t power; uint8_t unk[20]; uint8_t sleeptime; /* In minutes, 0-60 */ uint8_t iserial; /* 0x00 for Enabled, 0x80 for Disabled */ - uint8_t unk_b[12]; + uint8_t unk_b[5]; // [4] == 0x44 on D70x, 0x02 on D80 + uint8_t dual_deck; /* 0x80 for dual-deck D707 */ + uint8_t unk_c[6]; // [3] == 0x5f on D70x, 0x01 on D80. [5] == 0xbd on D70x, 0x87 on D80 int16_t model[6]; /* LE, UTF-16 */ int16_t serno[6]; /* LE, UTF-16 */ struct mitsu70x_status_ver vers[7]; // components are 'MLRTF' @@ -340,12 +356,6 @@ struct mitsu70x_printerstatus_resp { struct mitsu70x_status_deck upper; } __attribute__((packed)); -#define MK60S_0105_M_CSUM 0x148C /* 1.05 316M3 1 148C */ -#define EK305_0104_M_CSUM 0x2878 /* 1.04 316F8 3 2878 */ -#define MD70X_0110_M_CSUM 0x064D /* 1.10 316V1 1 064D */ -#define MD70X_0112_M_CSUM 0x9FC3 /* 1.12 316W1 1 9FC3 */ -#define FA300_XXXX_M_CSUM 0x4431 /* ?.?? 416J2 1 4431 */ - struct mitsu70x_memorystatus_resp { uint8_t hdr[3]; /* E4 56 33 */ uint8_t memory; @@ -383,9 +393,27 @@ struct mitsu70x_hdr { uint8_t pad[447]; } __attribute__((packed)); +static int mitsu70x_get_printerstatus(struct mitsu70x_ctx *ctx, struct mitsu70x_printerstatus_resp *resp); +static int mitsu70x_main_loop(void *vctx, const void *vjob); + /* Error dumps, etc */ -static char *mitsu70x_mechastatus(uint8_t *sts) +const char *mitsu70x_temperatures(uint8_t temp) +{ + switch(temp) { + case TEMPERATURE_NORMAL: + return "Normal"; + case TEMPERATURE_PREHEAT: + return "Warming Up"; + case TEMPERATURE_COOLING: + return "Cooling Down"; + default: + break; + } + return "Unknown Temperature Status"; +} + +static const char *mitsu70x_mechastatus(uint8_t *sts) { switch(sts[0]) { case MECHA_STATUS_INIT: @@ -405,7 +433,7 @@ static char *mitsu70x_mechastatus(uint8_t *sts) return "Unknown Mechanical Status"; } -static char *mitsu70x_jobstatuses(uint8_t *sts) +static const char *mitsu70x_jobstatuses(uint8_t *sts) { switch(sts[0]) { case JOB_STATUS0_NONE: @@ -455,7 +483,7 @@ static char *mitsu70x_jobstatuses(uint8_t *sts) default: return "Unknown 'End Header' status2"; } - break; + break; case JOB_STATUS1_END_PRINT: switch(sts[2]) { case JOB_STATUS2_END_PRINT_MEDIA: @@ -492,7 +520,7 @@ static char *mitsu70x_jobstatuses(uint8_t *sts) return "Unknown status0"; } -static char *mitsu70x_errorclass(uint8_t *err) +static const char *mitsu70x_errorclass(uint8_t *err) { switch(err[1]) { case ERROR_STATUS1_PAPER: @@ -523,7 +551,7 @@ static char *mitsu70x_errorclass(uint8_t *err) return "Unknown error class"; } -static char *mitsu70x_errorrecovery(uint8_t *err) +static const char *mitsu70x_errorrecovery(uint8_t *err) { switch(err[1]) { case ERROR_STATUS2_AUTO: @@ -554,7 +582,7 @@ static char *mitsu70x_errorrecovery(uint8_t *err) return "Unknown recovery"; } -static char *mitsu70x_errors(uint8_t *err) +static const char *mitsu70x_errors(uint8_t *err) { switch(err[0]) { case ERROR_STATUS0_NOSTRIPBIN: @@ -620,7 +648,7 @@ static char *mitsu70x_errors(uint8_t *err) return "Unknown error"; } -static const char *mitsu70x_media_types(uint8_t brand, uint8_t type) +const char *mitsu70x_media_types(uint8_t brand, uint8_t type) { if (brand == 0xff && type == 0x01) return "CK-D735 (3.5x5)"; @@ -675,12 +703,10 @@ static void *mitsu70x_init(void) return ctx; } -static void mitsu70x_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int mitsu70x_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct mitsu70x_ctx *ctx = vctx; - struct libusb_device *device; - struct libusb_device_descriptor desc; ctx->jobid = jobid; if (!ctx->jobid) @@ -689,14 +715,9 @@ static void mitsu70x_attach(void *vctx, struct libusb_device_handle *dev, ctx->dev = dev; ctx->endp_up = endp_up; ctx->endp_down = endp_down; + ctx->type = type; - device = libusb_get_device(dev); - libusb_get_device_descriptor(device, &desc); - - ctx->type = lookup_printer_type(&mitsu70x_backend, - desc.idVendor, desc.idProduct); - - ctx->last_donor_l = ctx->last_donor_u = 65535; + ctx->last_l = ctx->last_u = 65535; /* Attempt to open the library */ #if defined(WITH_DYNAMIC) @@ -710,13 +731,13 @@ static void mitsu70x_attach(void *vctx, struct libusb_device_handle *dev, ERROR("Problem resolving API Version symbol in imaging processing library, too old or not installed?\n"); DL_CLOSE(ctx->dl_handle); ctx->dl_handle = NULL; - return; + return CUPS_BACKEND_FAILED; } if (ctx->GetAPIVersion() != REQUIRED_LIB_APIVERSION) { ERROR("Image processing library API version mismatch!\n"); DL_CLOSE(ctx->dl_handle); ctx->dl_handle = NULL; - return; + return CUPS_BACKEND_FAILED; } ctx->Get3DColorTable = DL_SYM(ctx->dl_handle, "CColorConv3D_Get3DColorTable"); @@ -737,6 +758,7 @@ static void mitsu70x_attach(void *vctx, struct libusb_device_handle *dev, ERROR("Problem resolving symbols in imaging processing library\n"); DL_CLOSE(ctx->dl_handle); ctx->dl_handle = NULL; + return CUPS_BACKEND_FAILED; } else { DEBUG("Image processing library successfully loaded\n"); } @@ -757,6 +779,100 @@ static void mitsu70x_attach(void *vctx, struct libusb_device_handle *dev, #else WARNING("Dynamic library support not enabled, using internal fallback code\n"); #endif + + struct mitsu70x_printerstatus_resp resp; + int ret; + + if (test_mode < TEST_MODE_NOATTACH) { + ret = mitsu70x_get_printerstatus(ctx, &resp); + if (ret) { + ERROR("Unable to get printer status! (%d)\n", ret); + return CUPS_BACKEND_FAILED; + } + } else { + int media_code = 0xf; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")) & 0xf; + + resp.upper.mecha_status[0] = MECHA_STATUS_INIT; + resp.lower.mecha_status[0] = MECHA_STATUS_INIT; + resp.upper.capacity = cpu_to_be16(230); + resp.lower.capacity = cpu_to_be16(230); + resp.upper.remain = cpu_to_be16(200); + resp.lower.remain = cpu_to_be16(200); + resp.upper.media_brand = 0xff; + resp.lower.media_brand = 0xff; + resp.upper.media_type = media_code; + resp.lower.media_type = media_code; + resp.dual_deck = 0x80; /* Make it a dual deck */ + } + + /* Figure out if we're a D707 with two decks */ + if (ctx->type == P_MITSU_D70X && + resp.dual_deck == 0x80) + ctx->num_decks = 2; + else + ctx->num_decks = 1; + + /* Set up markers */ + ctx->marker[0].color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker[0].name = mitsu70x_media_types(resp.lower.media_brand, resp.lower.media_type); + ctx->marker[0].levelmax = be16_to_cpu(resp.lower.capacity); + ctx->marker[0].levelnow = be16_to_cpu(resp.lower.remain); + ctx->medias[0] = resp.lower.media_type & 0xf; + + if (ctx->num_decks == 2) { + ctx->marker[1].color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker[1].name = mitsu70x_media_types(resp.upper.media_brand, resp.upper.media_type); + ctx->marker[1].levelmax = be16_to_cpu(resp.upper.capacity); + ctx->marker[1].levelnow = be16_to_cpu(resp.upper.remain); + ctx->medias[1] = resp.upper.media_type & 0xf; + } + + /* FW sanity checking */ + if (ctx->type == P_KODAK_305) { + /* Known versions: + v1.02: M 316E81 1433 (Add Ultrafine and matte support) + v1.04: M 316F83 2878 (Add 2x6 strip and support "Triton" media) + */ + if (strncmp(resp.vers[0].ver, "316F83", 6) < 0) + WARNING("Printer FW out of date. Highly recommend upgrading EK305 to v1.04 or newer!\n"); + } else if (ctx->type == P_MITSU_K60) { + /* Known versions: + v1.05: M 316M31 148C (Add HG media support) + */ + if (strncmp(resp.vers[0].ver, "316M31", 6) < 0) + WARNING("Printer FW out of date. Highly recommend upgrading K60 to v1.05 or newer!\n"); + } else if (ctx->type == P_MITSU_D70X) { + /* Known versions: + v1.10: M 316V11 064D (Add ultrafine mode, 6x6 support, 2x6 strip, and more?) + v1.12: M 316W11 9FC3 (??) + v1.13: (??) + */ + if (strncmp(resp.vers[0].ver, "316W11", 6) < 0) + WARNING("Printer FW out of date. Highly recommend upgrading D70/D707 to v1.12 or newer!\n"); + } else if (ctx->type == P_FUJI_ASK300) { + /* Known versions: + v?.??: M 316A21 7998 (ancient. no matte or ultrafine) + v?.??: M 316H21 F8EB + v4.20a: M 316J21 4431 (Add 2x6 strip support) + */ + if (strncmp(resp.vers[0].ver, "316J21", 6) < 0) + WARNING("Printer FW out of date. Highly recommend upgrading ASK300 to v4.20a or newer!\n"); + } + + return CUPS_BACKEND_OK; +} + +static void mitsu70x_cleanup_job(const void *vjob) { + const struct mitsu70x_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + if (job->spoolbuf) + free(job->spoolbuf); + + free((void*)job); } static void mitsu70x_teardown(void *vctx) { @@ -765,9 +881,6 @@ static void mitsu70x_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); - if (ctx->dl_handle) { if (ctx->cpcdata) ctx->DestroyCPCData(ctx->cpcdata); @@ -783,34 +896,174 @@ static void mitsu70x_teardown(void *vctx) { free(ctx); } -static int mitsu70x_read_parse(void *vctx, int data_fd) { +#define JOB_EQUIV(__x) if (job1->__x != job2->__x) goto done + +static struct mitsu70x_printjob *combine_jobs(const struct mitsu70x_printjob *job1, + const struct mitsu70x_printjob *job2) +{ + struct mitsu70x_printjob *newjob = NULL; + uint16_t newrows; + uint16_t newcols; + uint32_t newpad, finalpad; + uint16_t lamoffset; + + const struct mitsu70x_hdr *hdr1, *hdr2; + struct mitsu70x_hdr *newhdr; + + /* Sanity check */ + if (!job1 || !job2) + goto done; + + hdr1 = (struct mitsu70x_hdr *) job1->databuf; + hdr2 = (struct mitsu70x_hdr *) job2->databuf; + + JOB_EQUIV(rows); + JOB_EQUIV(cols); + JOB_EQUIV(matte); + JOB_EQUIV(sharpen); + + if (hdr1->multicut || hdr2->multicut) + goto done; + if (job1->raw_format || job2->raw_format) + goto done; + if (hdr1->speed != hdr2->speed) + goto done; + + switch (job1->rows) { + case 1218: /* K60, EK305 */ + newrows = 2454; + newpad = 16; + finalpad = 0; + lamoffset = 0; + break; + case 1228: /* D70, ASK300, D80 */ + newrows = 2730; + newpad = 38; + finalpad = 236; + lamoffset = 12; + break; + case 1076: /* EK305, K60 3.5x5" prints */ + newrows = 2190; + newpad = 49; + finalpad = 0; + lamoffset = 0; + break; + default: + goto done; + } + newcols = job1->cols; + newpad *= newcols; + finalpad *= newcols; + + /* Okay, it's kosher to proceed */ + + DEBUG("Combining jobs to save media\n"); + + newjob = malloc(sizeof(*newjob)); + if (!newjob) { + ERROR("Memory allocation failure!\n"); + goto done; + } + memcpy(newjob, job1, sizeof(*newjob)); + + newjob->spoolbuf = NULL; + newjob->rows = newrows; + newjob->cols = newcols; + newjob->planelen = (((newrows * newcols * 2) + 511) /512) * 512; + if (newjob->matte) { + newjob->matte = ((((newrows + lamoffset) * newcols * 2) + 511) / 512) * 512; + } + newjob->databuf = malloc(sizeof(*newhdr) + newjob->planelen * 3 + newjob->matte); + newjob->datalen = 0; + if (!newjob->databuf) { + mitsu70x_cleanup_job(newjob); + newjob = NULL; + ERROR("Memory allocation failure!\n"); + goto done; + } + newhdr = (struct mitsu70x_hdr *) newjob->databuf; + + /* Copy over header */ + memcpy(newhdr, hdr1, sizeof(*newhdr)); + newjob->datalen += sizeof(*newhdr); + + newhdr->rows = cpu_to_be16(newrows); + newhdr->cols = cpu_to_be16(newcols); + + if (newjob->matte) { + newhdr->lamrows = cpu_to_be16(newrows + lamoffset); + newhdr->lamcols = cpu_to_be16(newcols); + } + newhdr->multicut = 1; + newhdr->deck = 0; /* Let printer decide */ + + newjob->spoolbuf = malloc(newrows * newcols * 3); + newjob->spoolbuflen = 0; + if (!newjob->spoolbuf) { + mitsu70x_cleanup_job(newjob); + newjob = NULL; + ERROR("Memory allocation failure!\n"); + goto done; + } + + /* Fill in padding */ + memset(newjob->spoolbuf + newjob->spoolbuflen, 0xff, finalpad * 3); + newjob->spoolbuflen += finalpad * 3; + + /* Copy image payload */ + memcpy(newjob->spoolbuf + newjob->spoolbuflen, job1->spoolbuf, + job1->spoolbuflen); + newjob->spoolbuflen += job1->spoolbuflen; + + /* Fill in padding */ + memset(newjob->spoolbuf + newjob->spoolbuflen, 0xff, newpad * 3); + newjob->spoolbuflen += newpad * 3; + + /* Copy image payload */ + memcpy(newjob->spoolbuf + newjob->spoolbuflen, job2->spoolbuf, + job2->spoolbuflen); + newjob->spoolbuflen += job2->spoolbuflen; + + /* Okay, we're done. */ + +done: + return newjob; +} +#undef JOB_EQUIV + +static int mitsu70x_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct mitsu70x_ctx *ctx = vctx; int i, remain; struct mitsu70x_hdr mhdr; - uint32_t planelen; + + struct mitsu70x_printjob *job = NULL; + struct dyesub_joblist *list; + int can_combine; if (!ctx) return CUPS_BACKEND_FAILED; - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } - - /* Reset some state */ - ctx->matte = 0; - ctx->rew[0] = 1; - ctx->rew[1] = 1; + memset(job, 0, sizeof(*job)); + job->copies = copies; repeat: /* Read in initial header */ remain = sizeof(mhdr); while (remain > 0) { i = read(data_fd, ((uint8_t*)&mhdr) + sizeof(mhdr) - remain, remain); - if (i == 0) + if (i == 0) { + mitsu70x_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsu70x_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; } @@ -823,14 +1076,15 @@ repeat: } /* Sanity check header */ - if (mhdr.hdr[0] != 0x1b && - mhdr.hdr[1] != 0x5a && + if (mhdr.hdr[0] != 0x1b || + mhdr.hdr[1] != 0x5a || mhdr.hdr[2] != 0x54) { ERROR("Unrecognized data format!\n"); + mitsu70x_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - ctx->raw_format = !mhdr.mode; + job->raw_format = !mhdr.mode; /* Sanity check Matte mode */ if (!mhdr.laminate && mhdr.laminate_mode) { @@ -849,61 +1103,61 @@ repeat: /* Figure out the correction data table to use */ if (ctx->type == P_MITSU_D70X) { - ctx->laminatefname = CORRTABLE_PATH "/D70MAT01.raw"; - ctx->lutfname = CORRTABLE_PATH "/CPD70L01.lut"; + job->laminatefname = CORRTABLE_PATH "/D70MAT01.raw"; + job->lutfname = CORRTABLE_PATH "/CPD70L01.lut"; if (mhdr.speed == 3) { - ctx->cpcfname = CORRTABLE_PATH "/CPD70S01.cpc"; + job->cpcfname = CORRTABLE_PATH "/CPD70S01.cpc"; } else if (mhdr.speed == 4) { - ctx->cpcfname = CORRTABLE_PATH "/CPD70U01.cpc"; + job->cpcfname = CORRTABLE_PATH "/CPD70U01.cpc"; } else { - ctx->cpcfname = CORRTABLE_PATH "/CPD70N01.cpc"; + job->cpcfname = CORRTABLE_PATH "/CPD70N01.cpc"; } if (mhdr.hdr[3] != 0x01) { WARNING("Print job has wrong submodel specifier (%x)\n", mhdr.hdr[3]); mhdr.hdr[3] = 0x01; } } else if (ctx->type == P_MITSU_D80) { - ctx->laminatefname = CORRTABLE_PATH "/D80MAT01.raw"; - ctx->lutfname = CORRTABLE_PATH "/CPD80L01.lut"; + job->laminatefname = CORRTABLE_PATH "/D80MAT01.raw"; + job->lutfname = CORRTABLE_PATH "/CPD80L01.lut"; if (mhdr.speed == 3) { - ctx->cpcfname = CORRTABLE_PATH "/CPD80S01.cpc"; - ctx->ecpcfname = CORRTABLE_PATH "/CPD80E01.cpc"; + job->cpcfname = CORRTABLE_PATH "/CPD80S01.cpc"; + job->ecpcfname = CORRTABLE_PATH "/CPD80E01.cpc"; } else if (mhdr.speed == 4) { - ctx->cpcfname = CORRTABLE_PATH "/CPD80U01.cpc"; - ctx->ecpcfname = NULL; + job->cpcfname = CORRTABLE_PATH "/CPD80U01.cpc"; + job->ecpcfname = NULL; } else { - ctx->cpcfname = CORRTABLE_PATH "/CPD80N01.cpc"; - ctx->ecpcfname = NULL; + job->cpcfname = CORRTABLE_PATH "/CPD80N01.cpc"; + job->ecpcfname = NULL; } if (mhdr.hdr[3] != 0x01) { WARNING("Print job has wrong submodel specifier (%x)\n", mhdr.hdr[3]); mhdr.hdr[3] = 0x01; } } else if (ctx->type == P_MITSU_K60) { - ctx->laminatefname = CORRTABLE_PATH "/S60MAT02.raw"; - ctx->lutfname = CORRTABLE_PATH "/CPS60L01.lut"; + job->laminatefname = CORRTABLE_PATH "/S60MAT02.raw"; + job->lutfname = CORRTABLE_PATH "/CPS60L01.lut"; if (mhdr.speed == 3 || mhdr.speed == 4) { mhdr.speed = 4; /* Ultra Fine */ - ctx->cpcfname = CORRTABLE_PATH "/CPS60T03.cpc"; + job->cpcfname = CORRTABLE_PATH "/CPS60T03.cpc"; } else { - ctx->cpcfname = CORRTABLE_PATH "/CPS60T01.cpc"; + job->cpcfname = CORRTABLE_PATH "/CPS60T01.cpc"; } if (mhdr.hdr[3] != 0x00) { WARNING("Print job has wrong submodel specifier (%x)\n", mhdr.hdr[3]); mhdr.hdr[3] = 0x00; } } else if (ctx->type == P_KODAK_305) { - ctx->laminatefname = CORRTABLE_PATH "/EK305MAT.raw"; // Same as K60 - ctx->lutfname = CORRTABLE_PATH "/EK305L01.lut"; + job->laminatefname = CORRTABLE_PATH "/EK305MAT.raw"; // Same as K60 + job->lutfname = CORRTABLE_PATH "/EK305L01.lut"; if (mhdr.speed == 3 || mhdr.speed == 4) { mhdr.speed = 4; /* Ultra Fine */ - ctx->cpcfname = CORRTABLE_PATH "/EK305T03.cpc"; + job->cpcfname = CORRTABLE_PATH "/EK305T03.cpc"; } else { - ctx->cpcfname = CORRTABLE_PATH "/EK305T01.cpc"; + job->cpcfname = CORRTABLE_PATH "/EK305T01.cpc"; } // XXX what about using K60 media if we read back the proper code? if (mhdr.hdr[3] != 0x90) { @@ -911,13 +1165,13 @@ repeat: mhdr.hdr[3] = 0x90; } } else if (ctx->type == P_FUJI_ASK300) { - ctx->laminatefname = CORRTABLE_PATH "/ASK300M2.raw"; // Same as D70 -// ctx->lutfname = CORRTABLE_PATH "/CPD70L01.lut"; // XXX guess, driver did not come with external LUT! + job->laminatefname = CORRTABLE_PATH "/ASK300M2.raw"; // Same as D70 +// job->lutfname = CORRTABLE_PATH "/CPD70L01.lut"; // XXX guess, driver did not come with external LUT! if (mhdr.speed == 3 || mhdr.speed == 4) { mhdr.speed = 3; /* Super Fine */ - ctx->cpcfname = CORRTABLE_PATH "/ASK300T3.cpc"; + job->cpcfname = CORRTABLE_PATH "/ASK300T3.cpc"; } else { - ctx->cpcfname = CORRTABLE_PATH "/ASK300T1.cpc"; + job->cpcfname = CORRTABLE_PATH "/ASK300T1.cpc"; } if (mhdr.hdr[3] != 0x80) { WARNING("Print job has wrong submodel specifier (%x)\n", mhdr.hdr[3]); @@ -925,10 +1179,10 @@ repeat: } } if (!mhdr.use_lut) - ctx->lutfname = NULL; + job->lutfname = NULL; - ctx->sharpen = mhdr.sharpen - 1; - ctx->reverse = !mhdr.reversed; + job->sharpen = mhdr.sharpen - 1; + job->reverse = !mhdr.reversed; /* Clean up header back to pristine. */ mhdr.use_lut = 0; @@ -937,188 +1191,214 @@ repeat: mhdr.reversed = 0; /* Work out total printjob size */ - ctx->cols = be16_to_cpu(mhdr.cols); - ctx->rows = be16_to_cpu(mhdr.rows); + job->cols = be16_to_cpu(mhdr.cols); + job->rows = be16_to_cpu(mhdr.rows); - planelen = ctx->rows * ctx->cols * 2; - planelen = (planelen + 511) / 512 * 512; /* Round to nearest 512 bytes. */ + job->planelen = job->rows * job->cols * 2; + job->planelen = (job->planelen + 511) / 512 * 512; /* Round to nearest 512 bytes. */ if (!mhdr.laminate && mhdr.laminate_mode) { i = be16_to_cpu(mhdr.lamcols) * be16_to_cpu(mhdr.lamrows) * 2; i = (i + 511) / 512 * 512; /* Round to nearest 512 bytes. */ - ctx->matte = i; + job->matte = i; } - remain = 3 * planelen + ctx->matte; + remain = 3 * job->planelen + job->matte; - ctx->datalen = 0; - ctx->databuf = malloc(sizeof(mhdr) + remain + LAMINATE_STRIDE*2); /* Give us a bit extra */ + job->datalen = 0; + job->databuf = malloc(sizeof(mhdr) + remain + LAMINATE_STRIDE*2); /* Give us a bit extra */ - if (!ctx->databuf) { + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + mitsu70x_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } - memcpy(ctx->databuf + ctx->datalen, &mhdr, sizeof(mhdr)); - ctx->datalen += sizeof(mhdr); + memcpy(job->databuf + job->datalen, &mhdr, sizeof(mhdr)); + job->datalen += sizeof(mhdr); - if (ctx->raw_format) { /* RAW MODE */ + if (job->raw_format) { /* RAW MODE */ DEBUG("Reading in %d bytes of 16bpp YMCL data\n", remain); /* Read in the spool data */ while(remain) { - i = read(data_fd, ctx->databuf + ctx->datalen, remain); - if (i == 0) + i = read(data_fd, job->databuf + job->datalen, remain); + if (i == 0) { + mitsu70x_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsu70x_cleanup_job(job); return CUPS_BACKEND_CANCEL; - ctx->datalen += i; + } + job->datalen += i; remain -= i; } - } else { /* RAW MODE OFF */ - int spoolbuflen = 0; - uint8_t *spoolbuf; + goto done; + } - remain = ctx->rows * ctx->cols * 3; - DEBUG("Reading in %d bytes of 8bpp BGR data\n", remain); + /* Non-RAW mode! */ - spoolbuflen = 0; spoolbuf = malloc(remain); - if (!spoolbuf) { - ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; - } + remain = job->rows * job->cols * 3; + DEBUG("Reading in %d bytes of 8bpp BGR data\n", remain); - /* Read in the BGR data */ - while (remain) { - i = read(data_fd, spoolbuf + spoolbuflen, remain); - if (i == 0) - return CUPS_BACKEND_CANCEL; - if (i < 0) - return CUPS_BACKEND_CANCEL; - spoolbuflen += i; - remain -= i; - } + job->spoolbuflen = 0; + job->spoolbuf = malloc(remain); + if (!job->spoolbuf) { + ERROR("Memory allocation failure!\n"); + mitsu70x_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; + } - /* Run through basic LUT, if present and enabled */ - if (ctx->dl_handle && ctx->lutfname && !ctx->lut) { /* printer-specific, it is fixed per-job */ - DEBUG("Running print data through LUT\n"); - uint8_t *buf = malloc(LUT_LEN); - if (!buf) { - ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; - } - if (ctx->Get3DColorTable(buf, ctx->lutfname)) { - ERROR("Unable to open LUT file '%s'\n", ctx->lutfname); - return CUPS_BACKEND_CANCEL; - } - ctx->lut = ctx->Load3DColorTable(buf); - free(buf); - if (!ctx->lut) { - ERROR("Unable to parse LUT file '%s'!\n", ctx->lutfname); - return CUPS_BACKEND_CANCEL; - } - ctx->DoColorConv(ctx->lut, spoolbuf, ctx->cols, ctx->rows, ctx->cols * 3, COLORCONV_BGR); + /* Read in the BGR data */ + while (remain) { + i = read(data_fd, job->spoolbuf + job->spoolbuflen, remain); + if (i == 0) { + mitsu70x_cleanup_job(job); + return CUPS_BACKEND_CANCEL; } + if (i < 0) { + mitsu70x_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + job->spoolbuflen += i; + remain -= i; + } - if (ctx->dl_handle) { - struct BandImage input; - - - /* Load in the CPC file, if needed */ - if (ctx->cpcfname && ctx->cpcfname != ctx->last_cpcfname) { - ctx->last_cpcfname = ctx->cpcfname; - if (ctx->cpcdata) - ctx->DestroyCPCData(ctx->cpcdata); - ctx->cpcdata = ctx->GetCPCData(ctx->cpcfname); - if (!ctx->cpcdata) { - ERROR("Unable to load CPC file '%s'\n", ctx->cpcfname); - return CUPS_BACKEND_CANCEL; - } - } - - /* Load in the secondary CPC, if needed */ - if (ctx->ecpcfname != ctx->last_ecpcfname) { - ctx->last_ecpcfname = ctx->ecpcfname; - if (ctx->ecpcdata) - ctx->DestroyCPCData(ctx->ecpcdata); - if (ctx->ecpcfname) { - ctx->ecpcdata = ctx->GetCPCData(ctx->ecpcfname); - if (!ctx->ecpcdata) { - ERROR("Unable to load CPC file '%s'\n", ctx->cpcfname); - return CUPS_BACKEND_CANCEL; - } - } else { - ctx->ecpcdata = NULL; - } - } + if (!ctx->dl_handle) { + // XXXFALLBACK write fallback code? + ERROR("!!! Image Processing Library not found, aborting!\n"); + mitsu70x_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } - /* Convert using image processing library */ - input.origin_rows = input.origin_cols = 0; - input.rows = ctx->rows; - input.cols = ctx->cols; - input.imgbuf = spoolbuf; - input.bytes_per_row = ctx->cols * 3; - - ctx->output.origin_rows = ctx->output.origin_cols = 0; - ctx->output.rows = ctx->rows; - ctx->output.cols = ctx->cols; - ctx->output.imgbuf = ctx->databuf + ctx->datalen; - ctx->output.bytes_per_row = ctx->cols * 3 * 2; - - DEBUG("Running print data through processing library\n"); - if (ctx->DoImageEffect(ctx->cpcdata, ctx->ecpcdata, - &input, &ctx->output, ctx->sharpen, ctx->reverse, ctx->rew)) { - ERROR("Image Processing failed, aborting!\n"); - return CUPS_BACKEND_CANCEL; - } - } else { - // XXXFALLBACK write fallback code? - ERROR("!!! Image Processing Library not found, aborting!\n"); + /* Run through basic LUT, if present and enabled */ + if (job->lutfname && !ctx->lut) { /* printer-specific, it is fixed per-job */ + uint8_t *buf = malloc(LUT_LEN); + if (!buf) { + ERROR("Memory allocation failure!\n"); + mitsu70x_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; + } + if (ctx->Get3DColorTable(buf, job->lutfname)) { + ERROR("Unable to open LUT file '%s'\n", job->lutfname); + mitsu70x_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + ctx->lut = ctx->Load3DColorTable(buf); + free(buf); + if (!ctx->lut) { + ERROR("Unable to parse LUT file '%s'!\n", job->lutfname); + mitsu70x_cleanup_job(job); return CUPS_BACKEND_CANCEL; } + } - /* Move up the pointer to after the image data */ - ctx->datalen += 3*planelen; + if (job->lutfname && ctx->lut) { + DEBUG("Running print data through LUT\n"); + ctx->DoColorConv(ctx->lut, job->spoolbuf, job->cols, job->rows, job->cols * 3, COLORCONV_BGR); + } - /* Clean up */ - free(spoolbuf); +done: + list = dyesub_joblist_create(&mitsu70x_backend, ctx); + + for (i = 0 ; i < ctx->num_decks ; i++) { + switch (ctx->medias[i]) { + case 0x1: // 5x3.5 + if (job->rows == 1076) + job->decks_ok[i] = 1; + if (job->rows == 1076) + job->decks_exact[i] = 1; + break; + case 0x2: // 4x6 + if (job->rows == 1218 || + job->rows == 1228) + job->decks_ok[i] = 1; + if (job->rows == 1218 || + job->rows == 1228) + job->decks_exact[i] = 1; + break; + case 0x4: // 5x7 + if (job->rows == 1076 || + job->rows == 1524 || + job->rows == 2128) + job->decks_ok[i] = 1; + if (job->rows == 1524 || + job->rows == 2128) + job->decks_exact[i] = 1; + break; + case 0x5: // 6x9 + case 0xf: // 6x8 + /* This is made more complicated: + some 6x8" jobs are 6x9" sized. Let printer + sort these out. It's unlikely we'll have + 6x8" in one deck and 6x9" in the other! + */ + if (job->rows == 1218 || + job->rows == 1228 || + job->rows == 1820 || + job->rows == 2422 || + job->rows == 2564 || + job->rows == 2730) + job->decks_ok[i] = 1; + if (job->rows == 2422 || + job->rows == 2564 || + job->rows == 2730) + job->decks_exact[i] = 1; + break; + default: + job->decks_ok[i] = 0; + job->decks_exact[i] = 0; + break; + } + } - /* Now that we've filled everything in, read matte from file */ - if (ctx->matte) { - int fd; - uint32_t j; - DEBUG("Reading %d bytes of matte data from disk (%d/%d)\n", ctx->matte, ctx->cols, LAMINATE_STRIDE); - fd = open(ctx->laminatefname, O_RDONLY); - if (fd < 0) { - ERROR("Unable to open matte lamination data file '%s'\n", ctx->laminatefname); - return CUPS_BACKEND_CANCEL; - } + /* 6x4 can be combined, only on 6x8/6x9" media. */ + can_combine = 0; + if (job->decks_exact[0] || + job->decks_exact[1]) { + /* Exact media match, don't combine. */ + } else if (job->rows == 1218 || + job->rows == 1228) { + if (ctx->medias[0] == 0xf || + ctx->medias[0] == 0x5 || + ctx->medias[1] == 0xf || /* Two decks possible */ + ctx->medias[1] == 0x5) + can_combine = !job->raw_format; + } else if (job->rows == 1076) { + if (ctx->type == P_KODAK_305 || + ctx->type == P_MITSU_K60) { + if (ctx->medias[0] == 0x4) /* Only one deck */ + can_combine = !job->raw_format; + } + } - for (j = 0 ; j < be16_to_cpu(mhdr.lamrows) ; j++) { - remain = LAMINATE_STRIDE * 2; - - /* Read one row of lamination data at a time */ - while (remain) { - i = read(fd, ctx->databuf + ctx->datalen, remain); - if (i < 0) - return CUPS_BACKEND_CANCEL; - if (i == 0) { - /* We hit EOF, restart from beginning */ - lseek(fd, 0, SEEK_SET); - continue; - } - ctx->datalen += i; - remain -= i; - } - /* Back off the buffer so we "wrap" on the print row. */ - ctx->datalen -= ((LAMINATE_STRIDE - ctx->cols) * 2); + if (copies > 1 && can_combine) { + struct mitsu70x_printjob *combined; + combined = combine_jobs(job, job); + if (combined) { + combined->copies = job->copies / 2; + dyesub_joblist_addjob(list, combined); + + if (job->copies & 1) { + job->copies = 1; + } else { + mitsu70x_cleanup_job(job); + job = NULL; } - - /* Zero out the tail end of the buffer. */ - j = be16_to_cpu(mhdr.lamcols) * be16_to_cpu(mhdr.lamrows) * 2; - memset(ctx->databuf + ctx->datalen, 0, ctx->matte - j); } } + + if (job) { + dyesub_joblist_addjob(list, job); + } + + /* All further work is in main loop */ + if (test_mode >= TEST_MODE_NOPRINT) + dyesub_joblist_print(list); + + *vjob = list; + return CUPS_BACKEND_OK; } @@ -1191,7 +1471,7 @@ static int mitsu70x_get_jobs(struct mitsu70x_ctx *ctx, struct mitsu70x_jobs *res } #endif -static int mitsu70x_get_memorystatus(struct mitsu70x_ctx *ctx, struct mitsu70x_memorystatus_resp *resp) +static int mitsu70x_get_memorystatus(struct mitsu70x_ctx *ctx, const struct mitsu70x_printjob *job, uint8_t mcut, struct mitsu70x_memorystatus_resp *resp) { uint8_t cmdbuf[CMDBUF_LEN]; @@ -1205,11 +1485,21 @@ static int mitsu70x_get_memorystatus(struct mitsu70x_ctx *ctx, struct mitsu70x_m cmdbuf[1] = 0x56; cmdbuf[2] = 0x33; cmdbuf[3] = 0x00; - tmp = cpu_to_be16(ctx->cols); + tmp = cpu_to_be16(job->cols); memcpy(cmdbuf + 4, &tmp, 2); - tmp = cpu_to_be16(ctx->rows); + + /* We have to lie about print sizes in 4x6*2 multicut modes */ + tmp = job->rows; + if (tmp == 2730 && mcut == 1) { + if (ctx->type == P_MITSU_D70X || + ctx->type == P_FUJI_ASK300) { + tmp = 2422; + } + } + + tmp = cpu_to_be16(tmp); memcpy(cmdbuf + 6, &tmp, 2); - cmdbuf[8] = ctx->matte ? 0x80 : 0x00; + cmdbuf[8] = job->matte ? 0x80 : 0x00; cmdbuf[9] = 0x00; if ((ret = send_data(ctx->dev, ctx->endp_down, @@ -1238,7 +1528,6 @@ static int mitsu70x_get_memorystatus(struct mitsu70x_ctx *ctx, struct mitsu70x_m return 0; } - static int mitsu70x_get_printerstatus(struct mitsu70x_ctx *ctx, struct mitsu70x_printerstatus_resp *resp) { uint8_t cmdbuf[CMDBUF_LEN]; @@ -1249,7 +1538,8 @@ static int mitsu70x_get_printerstatus(struct mitsu70x_ctx *ctx, struct mitsu70x_ cmdbuf[0] = 0x1b; cmdbuf[1] = 0x56; cmdbuf[2] = 0x32; - cmdbuf[3] = 0x30; + cmdbuf[3] = 0x30; /* or x31 or x32, for SINGLE DECK query! + Results will only have one deck. */ if ((ret = send_data(ctx->dev, ctx->endp_down, cmdbuf, 4))) return ret; @@ -1361,23 +1651,41 @@ static int mitsu70x_set_printermode(struct mitsu70x_ctx *ctx, uint8_t enabled) return 0; } #endif -static int mitsu70x_wakeup(struct mitsu70x_ctx *ctx) + +static int mitsu70x_wakeup(struct mitsu70x_ctx *ctx, int wait) { int ret; uint8_t buf[512]; + struct mitsu70x_jobstatus jobstatus; - memset(buf, 0, sizeof(buf)); - buf[0] = 0x1b; - buf[1] = 0x45; - buf[2] = 0x57; // XXX also, 0x53, 0x54 seen. - buf[3] = 0x55; - - INFO("Waking up printer...\n"); - if ((ret = send_data(ctx->dev, ctx->endp_down, - buf, sizeof(buf)))) +top: + /* Query job status for jobid 0 (global) */ + ret = mitsu70x_get_jobstatus(ctx, &jobstatus, 0x0000); + if (ret) return CUPS_BACKEND_FAILED; - return 0; + /* Trigger a wakeup if necessary */ + if (jobstatus.power) { + INFO("Waking up printer...\n"); + + memset(buf, 0, sizeof(buf)); + buf[0] = 0x1b; + buf[1] = 0x45; + buf[2] = 0x57; // XXX also, 0x53, 0x54 seen. + buf[3] = 0x55; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + buf, sizeof(buf)))) + return CUPS_BACKEND_FAILED; + + if (wait) { + sleep(1); + goto top; + } + } + + + return CUPS_BACKEND_OK; } static int d70_library_callback(void *context, void *buffer, uint32_t len) @@ -1392,7 +1700,7 @@ static int d70_library_callback(void *context, void *buffer, uint32_t len) if (chunk > CHUNK_LEN) chunk = CHUNK_LEN; - ret = send_data(ctx->dev, ctx->endp_down, buffer + offset, chunk); + ret = send_data(ctx->dev, ctx->endp_down, (uint8_t*)buffer + offset, chunk); if (ret < 0) break; @@ -1403,7 +1711,7 @@ static int d70_library_callback(void *context, void *buffer, uint32_t len) return ret; } -static int mitsu70x_main_loop(void *vctx, int copies) +static int mitsu70x_main_loop(void *vctx, const void *vjob) { struct mitsu70x_ctx *ctx = vctx; struct mitsu70x_jobstatus jobstatus; @@ -1412,99 +1720,258 @@ static int mitsu70x_main_loop(void *vctx, int copies) uint8_t last_status[4] = {0xff, 0xff, 0xff, 0xff}; int ret; + int copies; + int deck; + + struct mitsu70x_printjob *job = (struct mitsu70x_printjob *) vjob; // XXX not clean. +// const struct mitsu70x_printjob *job = vjob; if (!ctx) return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; + + copies = job->copies; + hdr = (struct mitsu70x_hdr*) job->databuf; + + if (job->raw_format) + goto bypass; - hdr = (struct mitsu70x_hdr*) ctx->databuf; + struct BandImage input; + uint8_t rew[2] = { 1, 1 }; /* 1 for rewind ok (default!) */ + + /* Load in the CPC file, if needed */ + if (job->cpcfname && job->cpcfname != ctx->last_cpcfname) { + ctx->last_cpcfname = job->cpcfname; + if (ctx->cpcdata) + ctx->DestroyCPCData(ctx->cpcdata); + ctx->cpcdata = ctx->GetCPCData(job->cpcfname); + if (!ctx->cpcdata) { + ERROR("Unable to load CPC file '%s'\n", job->cpcfname); + return CUPS_BACKEND_CANCEL; + } + } + + /* Load in the secondary CPC, if needed */ + if (job->ecpcfname != ctx->last_ecpcfname) { + ctx->last_ecpcfname = job->ecpcfname; + if (ctx->ecpcdata) + ctx->DestroyCPCData(ctx->ecpcdata); + if (job->ecpcfname) { + ctx->ecpcdata = ctx->GetCPCData(job->ecpcfname); + if (!ctx->ecpcdata) { + ERROR("Unable to load CPC file '%s'\n", job->cpcfname); + return CUPS_BACKEND_CANCEL; + } + } else { + ctx->ecpcdata = NULL; + } + } + + /* Convert using image processing library */ + input.origin_rows = input.origin_cols = 0; + input.rows = job->rows; + input.cols = job->cols; + input.imgbuf = job->spoolbuf; + input.bytes_per_row = job->cols * 3; + + ctx->output.origin_rows = ctx->output.origin_cols = 0; + ctx->output.rows = job->rows; + ctx->output.cols = job->cols; + ctx->output.imgbuf = job->databuf + job->datalen; + ctx->output.bytes_per_row = job->cols * 3 * 2; + + DEBUG("Running print data through processing library\n"); + if (ctx->DoImageEffect(ctx->cpcdata, ctx->ecpcdata, + &input, &ctx->output, job->sharpen, job->reverse, rew)) { + ERROR("Image Processing failed, aborting!\n"); + return CUPS_BACKEND_CANCEL; + } + + /* Twiddle rewind stuff if needed */ + if (ctx->type != P_MITSU_D70X) { + hdr->rewind[0] = !rew[0]; + hdr->rewind[1] = !rew[1]; + DEBUG("Rewind Inhibit? %02x %02x\n", hdr->rewind[0], hdr->rewind[1]); + } + + /* Move up the pointer to after the image data */ + job->datalen += 3*job->planelen; + + /* Clean up */ + // XXX not really necessary. + free(job->spoolbuf); + job->spoolbuf = NULL; + job->spoolbuflen = 0; + + /* Now that we've filled everything in, read matte from file */ + if (job->matte) { + int fd; + uint32_t j; + DEBUG("Reading %u bytes of matte data from disk (%d/%d)\n", job->matte, job->cols, LAMINATE_STRIDE); + fd = open(job->laminatefname, O_RDONLY); + if (fd < 0) { + ERROR("Unable to open matte lamination data file '%s'\n", job->laminatefname); + return CUPS_BACKEND_CANCEL; + } + + for (j = 0 ; j < be16_to_cpu(hdr->lamrows) ; j++) { + int remain = LAMINATE_STRIDE * 2; + + /* Read one row of lamination data at a time */ + while (remain) { + int i = read(fd, job->databuf + job->datalen, remain); + if (i < 0) + return CUPS_BACKEND_CANCEL; + if (i == 0) { + /* We hit EOF, restart from beginning */ + lseek(fd, 0, SEEK_SET); + continue; + } + job->datalen += i; + remain -= i; + } + /* Back off the buffer so we "wrap" on the print row. */ + job->datalen -= ((LAMINATE_STRIDE - job->cols) * 2); + } + /* We're done */ + close(fd); + + /* Zero out the tail end of the buffer. */ + j = be16_to_cpu(hdr->lamcols) * be16_to_cpu(hdr->lamrows) * 2; + memset(job->databuf + job->datalen, 0, job->matte - j); + } + +bypass: + /* Bypass */ + if (test_mode >= TEST_MODE_NOPRINT) + return CUPS_BACKEND_OK; INFO("Waiting for printer idle...\n"); + /* Ensure printer is awake */ + ret = mitsu70x_wakeup(ctx, 1); + if (ret) + return CUPS_BACKEND_FAILED; + top: /* Query job status for jobid 0 (global) */ ret = mitsu70x_get_jobstatus(ctx, &jobstatus, 0x0000); if (ret) return CUPS_BACKEND_FAILED; - /* Make sure we're awake! */ - if (jobstatus.power) { - ret = mitsu70x_wakeup(ctx); - if (ret) - return CUPS_BACKEND_FAILED; + /* Figure out which deck(s) can be used. + This should be in the main loop due to copy retries */ - sleep(1); - goto top; + /* First, try to respect requested deck */ + if (ctx->type == P_MITSU_D70X) { + deck = hdr->deck; /* Respect D70 deck choice, 0 is automatic. */ + } else { + deck = 1; /* All others have one deck only */ } - /* Make sure temperature is sane */ - if (jobstatus.temperature == TEMPERATURE_COOLING) { - INFO("Printer cooling down...\n"); - sleep(1); - goto top; + /* If user requested a specific deck, go with it, if it's legal */ + if (deck == 1 && job->decks_ok[0]) { + deck = 1; + } else if (deck == 2 && job->decks_ok[1]) { + deck = 2; + /* If we have an exact match for media, use it exclusively */ + } else if (job->decks_exact[0] && job->decks_exact[1]) { + deck = 1 | 2; + } else if (job->decks_exact[0]) { + deck = 1; + } else if (job->decks_exact[1]) { + deck = 2; + /* Use a non-exact match only if we don't have an exact match */ + } else if (job->decks_ok[0] && job->decks_ok[1]) { + deck = 1 | 2; + } else if (job->decks_ok[0]) { + deck = 1; + } else if (job->decks_ok[1]) { + deck = 2; + } else { + ERROR("Loaded media does not match job!\n"); + return CUPS_BACKEND_CANCEL; } - /* See if we hit a printer error. */ - if (jobstatus.error_status[0]) { - ERROR("%s/%s -> %s: %02x/%02x/%02x\n", - mitsu70x_errorclass(jobstatus.error_status), - mitsu70x_errors(jobstatus.error_status), - mitsu70x_errorrecovery(jobstatus.error_status), - jobstatus.error_status[0], - jobstatus.error_status[1], - jobstatus.error_status[2]); - return CUPS_BACKEND_STOP; + if (ctx->num_decks > 1) + DEBUG("Deck selection mask: %d (%d %d %d/%d %d/%d) \n", + deck, hdr->deck, job->rows, + job->decks_exact[0], job->decks_exact[1], + job->decks_ok[0], job->decks_ok[1]); + + /* Okay, we know which decks are _legal_, pick one to use */ + if (deck & 1) { + if (jobstatus.temperature == TEMPERATURE_COOLING) { + if (ctx->num_decks == 2) + INFO("Lower deck cooling down...\n"); + else + INFO("Printer cooling down...\n"); + deck &= ~1; + } else if (jobstatus.error_status[0]) { + ERROR("%s/%s -> %s: %02x/%02x/%02x\n", + mitsu70x_errorclass(jobstatus.error_status), + mitsu70x_errors(jobstatus.error_status), + mitsu70x_errorrecovery(jobstatus.error_status), + jobstatus.error_status[0], + jobstatus.error_status[1], + jobstatus.error_status[2]); + deck &= ~1; + } else if (jobstatus.mecha_status[0] != MECHA_STATUS_IDLE) { + deck = ~1; + } + } + if (deck & 2) { + if (jobstatus.temperature_up == TEMPERATURE_COOLING) { + INFO("Upper deck cooling down...\n"); + deck &= ~2; + } else if (jobstatus.error_status_up[0]) { + ERROR("UPPER: %s/%s -> %s: %02x/%02x/%02x\n", + mitsu70x_errorclass(jobstatus.error_status_up), + mitsu70x_errors(jobstatus.error_status_up), + mitsu70x_errorrecovery(jobstatus.error_status_up), + jobstatus.error_status_up[0], + jobstatus.error_status_up[1], + jobstatus.error_status_up[2]); + deck &= ~2; + } else if (jobstatus.mecha_status_up[0] != MECHA_STATUS_IDLE) { + deck = ~2; + } } - if (ctx->num_decks) - goto skip_status; - - /* Tell CUPS about the consumables we report */ - ret = mitsu70x_get_printerstatus(ctx, &resp); - if (ret) - return CUPS_BACKEND_FAILED; + if (deck == 3) { + /* Both decks OK to use, pick one at random */ + if (rand() & 1) + deck = 1; + else + deck = 2; + } - if (resp.upper.mecha_status[0] != MECHA_STATUS_INIT) - ctx->num_decks = 2; - else - ctx->num_decks = 1; + if (ctx->num_decks > 1) + DEBUG("Deck selected: %d\n", deck); - if (ctx->type == P_MITSU_D70X && - ctx->num_decks == 2) { - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00,#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100,100\n"); - ATTR("marker-low-levels=10,10\n"); - ATTR("marker-names='\"%s\"','\"%s\"'\n", - mitsu70x_media_types(resp.lower.media_brand, resp.lower.media_type), - mitsu70x_media_types(resp.upper.media_brand, resp.upper.media_type)); - ATTR("marker-types=ribbonWax,ribbonWax\n"); - } else { - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", - mitsu70x_media_types(resp.lower.media_brand, resp.lower.media_type)); - ATTR("marker-types=ribbonWax\n"); - } + if (deck == 0) { + /* Halt queue if printer is entirely offline */ + if (ctx->num_decks == 2) { + if (jobstatus.error_status[0] && jobstatus.error_status_up[0]) + return CUPS_BACKEND_STOP; + // XXX what if we only have one legal deck, and it's unavailable? We don't want to retry indefinitely here.. + } else { + if (jobstatus.error_status[0]) + return CUPS_BACKEND_STOP; + } - /* FW sanity checking */ - if (ctx->type == P_KODAK_305) { - if (be16_to_cpu(resp.vers[0].checksum) != EK305_0104_M_CSUM) - WARNING("Printer FW out of date. Highly recommend upgrading EK305 to v1.04!\n"); - } else if (ctx->type == P_MITSU_K60) { - if (be16_to_cpu(resp.vers[0].checksum) != MK60S_0105_M_CSUM) - WARNING("Printer FW out of date. Highly recommend upgrading K60 to v1.05!\n"); - } else if (ctx->type == P_MITSU_D70X) { - if (be16_to_cpu(resp.vers[0].checksum) != MD70X_0112_M_CSUM) - WARNING("Printer FW out of date. Highly recommend upgrading D70/D707 to v1.12!\n"); + /* No decks available yet, retry */ + sleep(1); + goto top; } -skip_status: /* Perform memory status query */ { struct mitsu70x_memorystatus_resp memory; INFO("Checking Memory availability\n"); - ret = mitsu70x_get_memorystatus(ctx, &memory); + ret = mitsu70x_get_memorystatus(ctx, job, hdr->multicut, &memory); if (ret) return CUPS_BACKEND_FAILED; @@ -1548,25 +2015,12 @@ skip_status: hdr->jobid = cpu_to_be16(ctx->jobid); /* Set deck */ - if (ctx->type == P_MITSU_D70X) { - hdr->deck = 0; /* D70 use automatic deck selection */ - /* XXX alternatively route it based on state and media? */ - } else { - hdr->deck = 1; /* All others only have a "lower" deck. */ - } - - - /* Twiddle rewind stuff if needed */ - if (ctx->type != P_MITSU_D70X) { - hdr->rewind[0] = !ctx->rew[0]; - hdr->rewind[1] = !ctx->rew[1]; - DEBUG("Rewind Inhibit? %02x %02x\n", hdr->rewind[0], hdr->rewind[1]); - } + hdr->deck = deck; - /* Any other fixups? */ + /* K60 and EK305 need the mcut type 1 specified for 4x6 prints! */ if ((ctx->type == P_MITSU_K60 || ctx->type == P_KODAK_305) && - ctx->cols == 0x0748 && - ctx->rows == 0x04c2 && !hdr->multicut) { + job->cols == 0x0748 && + job->rows == 0x04c2 && !hdr->multicut) { hdr->multicut = 1; } @@ -1574,16 +2028,16 @@ skip_status: INFO("Sending Print Job (internal id %u)\n", ctx->jobid); if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf, + job->databuf, sizeof(struct mitsu70x_hdr)))) return CUPS_BACKEND_FAILED; - if (ctx->dl_handle && !ctx->raw_format) { + if (ctx->dl_handle && !job->raw_format) { if (ctx->SendImageData(&ctx->output, ctx, d70_library_callback)) return CUPS_BACKEND_FAILED; - if (ctx->matte) - if (d70_library_callback(ctx, ctx->databuf + ctx->datalen - ctx->matte, ctx->matte)) + if (job->matte) + if (d70_library_callback(ctx, job->databuf + job->datalen - job->matte, job->matte)) return CUPS_BACKEND_FAILED; } else { // Fallback code.. /* K60 and 305 need data sent in 256K chunks, but the first @@ -1593,10 +2047,10 @@ skip_status: int sent = 512; while (chunk > 0) { if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf + sent, chunk))) + job->databuf + sent, chunk))) return CUPS_BACKEND_FAILED; sent += chunk; - chunk = ctx->datalen - sent; + chunk = job->datalen - sent; if (chunk > CHUNK_LEN) chunk = CHUNK_LEN; } @@ -1606,38 +2060,23 @@ skip_status: INFO("Waiting for printer to acknowledge completion\n"); do { - uint16_t donor_u, donor_l; - sleep(1); ret = mitsu70x_get_printerstatus(ctx, &resp); if (ret) return CUPS_BACKEND_FAILED; - donor_l = be16_to_cpu(resp.lower.remain) * 100 / be16_to_cpu(resp.lower.capacity); - - if (ctx->type == P_MITSU_D70X && - ctx->num_decks == 2) { - donor_u = be16_to_cpu(resp.upper.remain) * 100 / be16_to_cpu(resp.upper.capacity); - if (donor_l != ctx->last_donor_l || - donor_u != ctx->last_donor_u) { - ctx->last_donor_l = donor_l; - ctx->last_donor_u = donor_u; - ATTR("marker-levels=%d,%d\n", donor_l, donor_u); - ATTR("marker-message='\"%d native prints remaining on %s media\"','\"%d native prints remaining on %s media\"'\n", - be16_to_cpu(resp.lower.remain), - mitsu70x_media_types(resp.lower.media_brand, resp.lower.media_type), - be16_to_cpu(resp.upper.remain), - mitsu70x_media_types(resp.upper.media_brand, resp.upper.media_type)); - } - } else { - if (donor_l != ctx->last_donor_l) { - ctx->last_donor_l = donor_l; - ATTR("marker-levels=%d\n", donor_l); - ATTR("marker-message=\"%d native prints remaining on %s media\"\n", - be16_to_cpu(resp.lower.remain), - mitsu70x_media_types(resp.lower.media_brand, resp.lower.media_type)); - } + ctx->marker[0].levelmax = be16_to_cpu(resp.lower.capacity); + ctx->marker[0].levelnow = be16_to_cpu(resp.lower.remain); + if (ctx->num_decks == 2) { + ctx->marker[1].levelmax = be16_to_cpu(resp.upper.capacity); + ctx->marker[1].levelnow = be16_to_cpu(resp.upper.remain); + } + if (ctx->marker[0].levelnow != ctx->last_l || + ctx->marker[1].levelnow != ctx->last_u) { + dump_markers(ctx->marker, ctx->num_decks, 0); + ctx->last_l = ctx->marker[0].levelnow; + ctx->last_u = ctx->marker[1].levelnow; } /* Query job status for our used jobid */ @@ -1646,18 +2085,31 @@ skip_status: return CUPS_BACKEND_FAILED; /* See if we hit a printer error. */ - if (jobstatus.error_status[0]) { - ERROR("%s/%s -> %s: %02x/%02x/%02x\n", - mitsu70x_errorclass(jobstatus.error_status), - mitsu70x_errors(jobstatus.error_status), - mitsu70x_errorrecovery(jobstatus.error_status), - jobstatus.error_status[0], - jobstatus.error_status[1], - jobstatus.error_status[2]); - return CUPS_BACKEND_STOP; + if (deck == 0) { + if (jobstatus.error_status[0]) { + ERROR("%s/%s -> %s: %02x/%02x/%02x\n", + mitsu70x_errorclass(jobstatus.error_status), + mitsu70x_errors(jobstatus.error_status), + mitsu70x_errorrecovery(jobstatus.error_status), + jobstatus.error_status[0], + jobstatus.error_status[1], + jobstatus.error_status[2]); + return CUPS_BACKEND_STOP; + } + } else if (deck == 1) { + if (jobstatus.error_status_up[0]) { + ERROR("UPPER: %s/%s -> %s: %02x/%02x/%02x\n", + mitsu70x_errorclass(jobstatus.error_status_up), + mitsu70x_errors(jobstatus.error_status_up), + mitsu70x_errorrecovery(jobstatus.error_status_up), + jobstatus.error_status_up[0], + jobstatus.error_status_up[1], + jobstatus.error_status_up[2]); + return CUPS_BACKEND_STOP; + } } - /* Only print if it's changed */ + /* Only print if job status is changed */ if (jobstatus.job_status[0] != last_status[0] || jobstatus.job_status[1] != last_status[1] || jobstatus.job_status[2] != last_status[2] || @@ -1669,6 +2121,7 @@ skip_status: jobstatus.job_status[2], jobstatus.job_status[3]); + /* Check for job completion */ if (jobstatus.job_status[0] == JOB_STATUS0_END) { if (jobstatus.job_status[1] || jobstatus.job_status[2] || @@ -1683,7 +2136,12 @@ skip_status: break; } - if (fast_return) { + /* On a two deck system, try to use the second deck + for additional copies. If we can't use it, we'll block. */ + if (ctx->num_decks > 1 && copies > 1) + break; + + if (fast_return && copies <= 1) { /* Copies generated by backend! */ INFO("Fast return mode enabled.\n"); break; } @@ -1705,7 +2163,8 @@ skip_status: return CUPS_BACKEND_OK; } -static void mitsu70x_dump_printerstatus(struct mitsu70x_printerstatus_resp *resp) +static void mitsu70x_dump_printerstatus(struct mitsu70x_ctx *ctx, + struct mitsu70x_printerstatus_resp *resp) { uint32_t i; @@ -1738,83 +2197,102 @@ static void mitsu70x_dump_printerstatus(struct mitsu70x_printerstatus_resp *resp } INFO("Standby Timeout: %d minutes\n", resp->sleeptime); INFO("iSerial Reporting: %s\n", resp->iserial ? "No" : "Yes" ); + INFO("Power Status: %s\n", resp->power ? "Sleeping" : "Awake"); - INFO("Lower Mechanical Status: %s\n", - mitsu70x_mechastatus(resp->lower.mecha_status)); if (resp->lower.error_status[0]) { INFO("Lower Error Status: %s/%s -> %s\n", mitsu70x_errorclass(resp->lower.error_status), mitsu70x_errors(resp->lower.error_status), mitsu70x_errorrecovery(resp->lower.error_status)); } - INFO("Lower Media type: %s (%02x/%02x)\n", + INFO("Lower Temperature: %s\n", mitsu70x_temperatures(resp->lower.temperature)); + INFO("Lower Mechanical Status: %s\n", + mitsu70x_mechastatus(resp->lower.mecha_status)); + INFO("Lower Media Type: %s (%02x/%02x)\n", mitsu70x_media_types(resp->lower.media_brand, resp->lower.media_type), resp->lower.media_brand, resp->lower.media_type); - INFO("Lower Prints remaining: %03d/%03d\n", + INFO("Lower Prints Remaining: %03d/%03d\n", be16_to_cpu(resp->lower.remain), be16_to_cpu(resp->lower.capacity)); - i = packed_bcd_to_uint32((char*)resp->lower.lifetime_prints, 4); if (i) i-= 10; - INFO("Lower Lifetime prints: %u\n", i); + INFO("Lower Lifetime Prints: %u\n", i); - if (resp->upper.mecha_status[0] != MECHA_STATUS_INIT) { - INFO("Upper Mechanical Status: %s\n", - mitsu70x_mechastatus(resp->upper.mecha_status)); + if (ctx->num_decks == 2) { if (resp->upper.error_status[0]) { INFO("Upper Error Status: %s/%s -> %s\n", mitsu70x_errorclass(resp->upper.error_status), mitsu70x_errors(resp->upper.error_status), mitsu70x_errorrecovery(resp->upper.error_status)); } - INFO("Upper Media type: %s (%02x/%02x)\n", + INFO("Upper Temperature: %s\n", mitsu70x_temperatures(resp->upper.temperature)); + INFO("Upper Mechanical Status: %s\n", + mitsu70x_mechastatus(resp->upper.mecha_status)); + INFO("Upper Media Type: %s (%02x/%02x)\n", mitsu70x_media_types(resp->upper.media_brand, resp->upper.media_type), resp->upper.media_brand, resp->upper.media_type); - INFO("Upper Prints remaining: %03d/%03d\n", + INFO("Upper Prints Remaining: %03d/%03d\n", be16_to_cpu(resp->upper.remain), be16_to_cpu(resp->upper.capacity)); i = packed_bcd_to_uint32((char*)resp->upper.lifetime_prints, 4); if (i) i-= 10; - INFO("Upper Lifetime prints: %u\n", i); + INFO("Upper Lifetime Prints: %u\n", i); } } -static int mitsu70x_query_status(struct mitsu70x_ctx *ctx) +static int mitsu70x_query_jobs(struct mitsu70x_ctx *ctx) { - struct mitsu70x_printerstatus_resp resp; #if 0 struct mitsu70x_jobs jobs; #endif struct mitsu70x_jobstatus jobstatus; - int ret; -top: ret = mitsu70x_get_jobstatus(ctx, &jobstatus, 0x0000); if (ret) - goto done; - - /* Make sure we're awake! */ - if (jobstatus.power) { - ret = mitsu70x_wakeup(ctx); - if (ret) - return CUPS_BACKEND_FAILED; - - sleep(1); - goto top; - } - - ret = mitsu70x_get_printerstatus(ctx, &resp); - if (!ret) - mitsu70x_dump_printerstatus(&resp); + return CUPS_BACKEND_FAILED; INFO("JOB00 ID : %06u\n", jobstatus.jobid); INFO("JOB00 status : %s\n", mitsu70x_jobstatuses(jobstatus.job_status)); + INFO("Power Status: %s\n", jobstatus.power ? "Sleeping" : "Awake"); + + if (ctx->num_decks == 2) { + INFO("Lower Deck Mechanical Status: %s\n", + mitsu70x_mechastatus(jobstatus.mecha_status)); + if (jobstatus.error_status[0]) { + INFO("%s/%s -> %s\n", + mitsu70x_errorclass(jobstatus.error_status), + mitsu70x_errors(jobstatus.error_status), + mitsu70x_errorrecovery(jobstatus.error_status)); + } + INFO("Lower Deck Temperature: %s\n", mitsu70x_temperatures(jobstatus.temperature)); + + INFO("Upper Deck Mechanical Status: %s\n", + mitsu70x_mechastatus(jobstatus.mecha_status_up)); + if (jobstatus.error_status_up[0]) { + INFO("%s/%s -> %s\n", + mitsu70x_errorclass(jobstatus.error_status_up), + mitsu70x_errors(jobstatus.error_status_up), + mitsu70x_errorrecovery(jobstatus.error_status_up)); + } + INFO("Upper Deck Temperature: %s\n", mitsu70x_temperatures(jobstatus.temperature_up)); + } else { + INFO("Mechanical Status: %s\n", + mitsu70x_mechastatus(jobstatus.mecha_status)); + if (jobstatus.error_status[0]) { + INFO("%s/%s -> %s\n", + mitsu70x_errorclass(jobstatus.error_status), + mitsu70x_errors(jobstatus.error_status), + mitsu70x_errorrecovery(jobstatus.error_status)); + } + INFO("Temperature: %s\n", mitsu70x_temperatures(jobstatus.temperature)); + } + // memory status? #if 0 ret = mitsu70x_get_jobs(ctx, &jobs); @@ -1827,9 +2305,21 @@ top: INFO("JOB%02d status : %s\n", i, mitsu70x_jobstatuses(jobs.jobs[i].status)); } } -#endif done: +#endif + return CUPS_BACKEND_OK; +} + +static int mitsu70x_query_status(struct mitsu70x_ctx *ctx) +{ + struct mitsu70x_printerstatus_resp resp; + int ret; + + ret = mitsu70x_get_printerstatus(ctx, &resp); + if (!ret) + mitsu70x_dump_printerstatus(ctx, &resp); + return ret; } @@ -1860,7 +2350,10 @@ static int mitsu70x_query_serno(struct libusb_device_handle *dev, uint8_t endp_u static void mitsu70x_cmdline(void) { - DEBUG("\t\t[ -s ] # Query status\n"); + DEBUG("\t\t[ -s ] # Query printer status\n"); + DEBUG("\t\t[ -j ] # Query job status\n"); + DEBUG("\t\t[ -w ] # Wake up printer\n"); + DEBUG("\t\t[ -W ] # Wake up printer and wait\n"); DEBUG("\t\t[ -f ] # Use fast return mode\n"); DEBUG("\t\t[ -k num ] # Set standby time (1-60 minutes, 0 disables)\n"); DEBUG("\t\t[ -x num ] # Set USB iSerialNumber Reporting (1 on, 0 off)\n"); @@ -1874,15 +2367,24 @@ static int mitsu70x_cmdline_arg(void *vctx, int argc, char **argv) if (!ctx) return -1; - while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "sk:X:x:")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "jk:swWX:x:")) >= 0) { switch(i) { GETOPT_PROCESS_GLOBAL + case 'j': + j = mitsu70x_query_jobs(ctx); + break; case 'k': j = mitsu70x_set_sleeptime(ctx, atoi(optarg)); break; case 's': j = mitsu70x_query_status(ctx); break; + case 'w': + j = mitsu70x_wakeup(ctx, 0); + break; + case 'W': + j = mitsu70x_wakeup(ctx, 1); + break; case 'x': j = mitsu70x_set_iserial(ctx, atoi(optarg)); break; @@ -1899,28 +2401,73 @@ static int mitsu70x_cmdline_arg(void *vctx, int argc, char **argv) return 0; } +static int mitsu70x_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct mitsu70x_ctx *ctx = vctx; + struct mitsu70x_printerstatus_resp resp; + int ret; + + *markers = ctx->marker; + *count = ctx->num_decks; + + /* Tell CUPS about the consumables we report */ + ret = mitsu70x_get_printerstatus(ctx, &resp); + if (ret) + return CUPS_BACKEND_FAILED; + + if (resp.power) { + ret = mitsu70x_wakeup(ctx, 1); + if (ret) + return CUPS_BACKEND_FAILED; + + ret = mitsu70x_get_printerstatus(ctx, &resp); + if (ret) + return CUPS_BACKEND_FAILED; + } + + ctx->marker[0].levelmax = be16_to_cpu(resp.lower.capacity); + ctx->marker[0].levelnow = be16_to_cpu(resp.lower.remain); + if (ctx->num_decks == 2) { + ctx->marker[1].levelmax = be16_to_cpu(resp.upper.capacity); + ctx->marker[1].levelnow = be16_to_cpu(resp.upper.remain); + } + + return CUPS_BACKEND_OK; +} + +static const char *mitsu70x_prefixes[] = { + "mitsu70x", // Family entry, do not nuke. + "mitsubishi-d70dw", "mitsubishi-d80dw", "mitsubishi-k60dw", "kodak-305", "fujifilm-ask-300" + // Extras + "mitsubishi-d707dw", "mitsubishi-k60dws", + // backwards compatibility + "mitsud80", "mitsuk60", "kodak305", "fujiask300", + NULL, +}; /* Exported */ struct dyesub_backend mitsu70x_backend = { - .name = "Mitsubishi CP-D70/D707/K60/D80", - .version = "0.66", - .uri_prefix = "mitsu70x", + .name = "Mitsubishi CP-D70 family", + .version = "0.88", + .uri_prefixes = mitsu70x_prefixes, + .flags = BACKEND_FLAG_JOBLIST, .cmdline_usage = mitsu70x_cmdline, .cmdline_arg = mitsu70x_cmdline_arg, .init = mitsu70x_init, .attach = mitsu70x_attach, .teardown = mitsu70x_teardown, + .cleanup_job = mitsu70x_cleanup_job, .read_parse = mitsu70x_read_parse, .main_loop = mitsu70x_main_loop, .query_serno = mitsu70x_query_serno, + .query_markers = mitsu70x_query_markers, .devices = { - { USB_VID_MITSU, USB_PID_MITSU_D70X, P_MITSU_D70X, ""}, - { USB_VID_MITSU, USB_PID_MITSU_K60, P_MITSU_K60, ""}, - { USB_VID_MITSU, USB_PID_MITSU_D80, P_MITSU_D80, ""}, -// { USB_VID_MITSU, USB_PID_MITSU_D90, P_MITSU_D90, ""}, - { USB_VID_KODAK, USB_PID_KODAK305, P_KODAK_305, ""}, - { USB_VID_FUJIFILM, USB_PID_FUJI_ASK300, P_FUJI_ASK300, ""}, - { 0, 0, 0, ""} + { USB_VID_MITSU, USB_PID_MITSU_D70X, P_MITSU_D70X, NULL, "mitsubishi-d70dw"}, + { USB_VID_MITSU, USB_PID_MITSU_K60, P_MITSU_K60, NULL, "mitsubishi-k60dw"}, + { USB_VID_MITSU, USB_PID_MITSU_D80, P_MITSU_D80, NULL, "mitsubishi-d80dw"}, + { USB_VID_KODAK, USB_PID_KODAK305, P_KODAK_305, NULL, "kodak-305"}, + { USB_VID_FUJIFILM, USB_PID_FUJI_ASK300, P_FUJI_ASK300, NULL, "fujifilm-ask-300"}, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_mitsu9550.c b/src/cups/backend_mitsu9550.c index 71b38de..ab7530a 100644 --- a/src/cups/backend_mitsu9550.c +++ b/src/cups/backend_mitsu9550.c @@ -1,7 +1,7 @@ /* * Mitsubishi CP-9xxx Photo Printer Family CUPS backend * - * (c) 2014-2016 Solomon Peachy + * (c) 2014-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -35,6 +36,11 @@ #include #include +/* For Integration into gutenprint */ +#if defined(HAVE_CONFIG_H) +#include +#endif + #define BACKEND mitsu9550_backend #include "backend_common.h" @@ -53,6 +59,20 @@ //#define USB_PID_MITSU_9810D XXXXXX //#define USB_PID_MITSU_9820DS XXXXXX +#ifndef CORRTABLE_PATH +#ifdef PACKAGE_DATA_DIR +#define CORRTABLE_PATH PACKAGE_DATA_DIR "/backend_data" +#else +#error "Must define CORRTABLE_PATH or PACKAGE_DATA_DIR!" +#endif +#endif + +#define MITSU_M98xx_LAMINATE_FILE CORRTABLE_PATH "/M98MATTE.raw" +#define MITSU_M98xx_DATATABLE_FILE CORRTABLE_PATH "/M98TABLE.dat" +#define MITSU_M98xx_LUT_FILE CORRTABLE_PATH "/M98XXL01.lut" +#define LAMINATE_STRIDE 1868 +#define DATATABLE_SIZE 42204 + /* Spool file structures */ /* Print parameters1 */ @@ -61,14 +81,14 @@ struct mitsu9550_hdr1 { uint8_t unk[10]; /* 00 0a 10 00 [...] */ uint16_t cols; /* BE */ uint16_t rows; /* BE */ - uint8_t matte; /* CP9810 only. 01 for matte, 00 glossy */ + uint8_t matte; /* CP9810/9820 only. 01 for matte, 00 glossy */ uint8_t null[31]; } __attribute__((packed)); /* Print parameters2 */ struct mitsu9550_hdr2 { uint8_t cmd[4]; /* 1b 57 21 2e */ - uint8_t unk[24]; /* 00 80 00 22 08 03 00 [...] */ + uint8_t unk[24]; /* 00 80 00 22 08 03 [...] */ uint16_t copies; /* BE, 1-680 */ uint8_t null[2]; uint8_t cut; /* 00 == normal, 83 == 2x6*2 */ @@ -94,34 +114,63 @@ struct mitsu9550_hdr4 { /* Data plane header */ struct mitsu9550_plane { uint8_t cmd[4]; /* 1b 5a 54 XX */ /* XX == 0x10 if 16bpp, 0x00 for 8bpp */ + uint16_t col_offset; /* BE, normally 0, where we start dumping data */ uint16_t row_offset; /* BE, normally 0, where we start dumping data */ - uint16_t null; /* ??? */ uint16_t cols; /* BE */ uint16_t rows; /* BE */ } __attribute__((packed)); +/* CP98xx Tabular Data */ +struct mitsu98xx_data { + uint16_t GNMby[256]; // @0 + uint16_t GNMgm[256]; // @512 + uint16_t GNMrc[256]; // @1024 + double GammaParams[3]; // @1536 + uint8_t KH[2048]; // @1560 + uint32_t unk_b[3]; // @3608 + + struct { + double unka[256]; // @0 + double unkb[256]; // @2048 + uint32_t unkc[10]; // @4096 + double unkd[256]; // @4136 + double unke[256]; // @6184 // *= sharp->coef[X] + uint32_t unkf[10]; // @8232 + double unkg[256]; // @8272 + // @10320 + } WMAM; // @3620 + uint8_t unc_d[4]; // @13940 @10320 (from wmam start) + struct { + uint32_t unk_a; // @13944/10324 (padding?) + double coef[10]; // @13948/10328 (sharpness coefficients, level 0-9) + uint32_t unk_b[5]; // @14028/10408 + } sharp; // total 104, @13944/10324 + uint8_t unk_e[20]; // @14048/10428 + // @14068/10448 +} __attribute__((packed)); + +struct mitsu98xx_tables { + struct mitsu98xx_data superfine; + struct mitsu98xx_data fine_std; + struct mitsu98xx_data fine_hg; +} __attribute__((packed)); + +/* Command header */ struct mitsu9550_cmd { uint8_t cmd[4]; } __attribute__((packed)); /* Private data structure */ -struct mitsu9550_ctx { - struct libusb_device_handle *dev; - uint8_t endp_up; - uint8_t endp_down; - int type; - int is_s; - +struct mitsu9550_printjob { uint8_t *databuf; uint32_t datalen; uint16_t rows; uint16_t cols; uint32_t plane_len; + int is_raw; - uint16_t last_donor; - uint16_t last_remain; - int marker_reported; + int copies; /* Parse headers separately */ struct mitsu9550_hdr1 hdr1; @@ -134,6 +183,21 @@ struct mitsu9550_ctx { int hdr4_present; }; +struct mitsu9550_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + int is_s; + int is_98xx; + + struct marker marker; + + /* CP98xx stuff */ + struct mitsu98xx_tables *m98xxdata; + struct CColorConv3D *lut; +}; + /* Printer data structures */ struct mitsu9550_media { uint8_t hdr[2]; /* 24 2e */ @@ -164,12 +228,14 @@ struct mitsu9550_status { } __attribute__((packed)); struct mitsu9550_status2 { - uint8_t hdr[2]; /* 21 2e */ - uint8_t unk[39]; + uint8_t hdr[2]; /* 21 2e / 24 2e on 9550/9800 */ + uint8_t unk[40]; uint16_t remain; /* BE, media remaining */ uint8_t unkb[4]; /* 0a 00 00 01 */ } __attribute__((packed)); +static int mitsu9550_main_loop(void *vctx, const void *vjob); + #define CMDBUF_LEN 64 #define READBACK_LEN 128 @@ -178,41 +244,24 @@ struct mitsu9550_status2 { struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;\ /* struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf; */ \ struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf; \ - uint16_t donor, remain; \ + uint16_t donor; \ /* media */ \ ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); \ if (ret < 0) \ return CUPS_BACKEND_FAILED; \ \ - /* Tell CUPS about the consumables we report */ \ - if (!ctx->marker_reported) { \ - ctx->marker_reported = 1; \ - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); \ - ATTR("marker-high-levels=100\n"); \ - ATTR("marker-low-levels=10\n"); \ - ATTR("marker-names='%s'\n", mitsu9550_media_types(media->type, ctx->is_s)); \ - ATTR("marker-types=ribbonWax\n"); \ + donor = be16_to_cpu(media->remain); \ + if (donor != ctx->marker.levelnow) { \ + ctx->marker.levelnow = donor; \ + dump_markers(&ctx->marker, 1, 0); \ } \ - \ /* Sanity-check media response */ \ if (media->remain == 0 || media->max == 0) { \ ERROR("Printer out of media!\n"); \ - ATTR("marker-levels=%d\n", 0); \ return CUPS_BACKEND_HOLD; \ } \ - remain = be16_to_cpu(media->remain); \ - donor = be16_to_cpu(media->max); \ - donor = remain/donor; \ - if (donor != ctx->last_donor) { \ - ctx->last_donor = donor; \ - ATTR("marker-levels=%u\n", donor); \ - } \ - if (remain != ctx->last_remain) { \ - ctx->last_remain = remain; \ - ATTR("marker-message=\"%u prints remaining on '%s' ribbon\"\n", remain, mitsu9550_media_types(media->type, ctx->is_s)); \ - } \ - if (validate_media(ctx->type, media->type, ctx->cols, ctx->rows)) { \ - ERROR("Incorrect media (%u) type for printjob (%ux%u)!\n", media->type, ctx->cols, ctx->rows); \ + if (validate_media(ctx->type, media->type, job->cols, job->rows)) { \ + ERROR("Incorrect media (%u) type for printjob (%ux%u)!\n", media->type, job->cols, job->rows); \ return CUPS_BACKEND_HOLD; \ } \ /* status2 */ \ @@ -236,6 +285,246 @@ struct mitsu9550_status2 { } \ } while (0); +static void mitsu98xx_dogamma(uint8_t *src, uint16_t *dest, uint8_t plane, + uint16_t *table, uint32_t len) +{ + src += plane; + while(len--) { + *dest++ = table[*src]; + src += 3; + } + /* TODO: Eventually, when we do real processing of this data, we will need to + have the gamma table in native endian format and generate BE data at the end. */ +} + +static int mitsu98xx_fillmatte(struct mitsu9550_printjob *job) +{ + int fd, i; + uint32_t j, remain; + + DEBUG("Reading %d bytes of matte data from disk (%d/%d)\n", job->cols * job->rows, job->cols, LAMINATE_STRIDE); + fd = open(MITSU_M98xx_LAMINATE_FILE, O_RDONLY); + if (fd < 0) { + WARNING("Unable to open matte lamination data file '%s'\n", MITSU_M98xx_LAMINATE_FILE); + job->hdr1.matte = 0; + goto done; + } + + /* Fill in the lamination plane header */ + struct mitsu9550_plane *matte = (struct mitsu9550_plane *)(job->databuf + job->datalen); + matte->cmd[0] = 0x1b; + matte->cmd[1] = 0x5a; + matte->cmd[2] = 0x54; + matte->cmd[3] = 0x10; + matte->row_offset = 0; + matte->col_offset = 0; + matte->cols = job->hdr1.cols; + matte->rows = job->hdr1.rows; + job->datalen += sizeof(struct mitsu9550_plane); + + /* Read in the matte data plane */ + for (j = 0 ; j < job->rows ; j++) { + remain = LAMINATE_STRIDE * 2; + + /* Read one row of lamination data at a time */ + while (remain) { + i = read(fd, job->databuf + job->datalen, remain); + if (i < 0) + return CUPS_BACKEND_CANCEL; + if (i == 0) { + /* We hit EOF, restart from beginning */ + lseek(fd, 0, SEEK_SET); + continue; + } + job->datalen += i; + remain -= i; + } + /* Back off the buffer so we "wrap" on the print row. */ + job->datalen -= ((LAMINATE_STRIDE - job->cols) * 2); + } + /* We're done! */ + close(fd); + + /* Fill in the lamination plane footer */ + job->databuf[job->datalen++] = 0x1b; + job->databuf[job->datalen++] = 0x50; + job->databuf[job->datalen++] = 0x56; + job->databuf[job->datalen++] = 0x00; + +done: + return CUPS_BACKEND_OK; +} + +/*** 3D color Lookup table stuff. Taken out of lib70x ****/ +#define LUT_LEN 14739 +#define COLORCONV_RGB 0 +#define COLORCONV_BGR 1 + +struct CColorConv3D { + uint8_t lut[17][17][17][3]; +}; + +/* Load the Lookup table off of disk into *PRE-ALLOCATED* buffer */ +int CColorConv3D_Get3DColorTable(uint8_t *buf, const char *filename) +{ + FILE *stream; + + if (!filename) + return 1; + if (!*filename) + return 2; + if (!buf) + return 3; + + stream = fopen(filename, "rb"); + if (!stream) + return 4; + + fseek(stream, 0, SEEK_END); + if (ftell(stream) < LUT_LEN) { + fclose(stream); + return 5; + } + fseek(stream, 0, SEEK_SET); + fread(buf, 1, LUT_LEN, stream); + fclose(stream); + + return 0; +} + +/* Parse the on-disk LUT data into the structure.... */ +struct CColorConv3D *CColorConv3D_Load3DColorTable(const uint8_t *ptr) +{ + struct CColorConv3D *this; + this = malloc(sizeof(*this)); + if (!this) + return NULL; + + int i, j, k; + + for (i = 0 ; i <= 16 ; i++) { + for (j = 0 ; j <= 16 ; j++) { + for (k = 0; k <= 16; k++) { + this->lut[k][j][i][2] = *ptr++; + this->lut[k][j][i][1] = *ptr++; + this->lut[k][j][i][0] = *ptr++; + } + } + } + return this; +} +void CColorConv3D_Destroy3DColorTable(struct CColorConv3D *this) +{ + free(this); +} + +/* Transform a single pixel. */ +static void CColorConv3D_DoColorConvPixel(struct CColorConv3D *this, uint8_t *redp, uint8_t *grnp, uint8_t *blup) +{ + int red_h; + int grn_h; + int blu_h; + int grn_li; + int red_li; + int blu_li; + int red_l; + int grn_l; + int blu_l; + + uint8_t *tab0; // @ 14743 + uint8_t *tab1; // @ 14746 + uint8_t *tab2; // @ 14749 + uint8_t *tab3; // @ 14752 + uint8_t *tab4; // @ 14755 + uint8_t *tab5; // @ 14758 + uint8_t *tab6; // @ 14761 + uint8_t *tab7; // @ 14764 + + red_h = *redp >> 4; + red_l = *redp & 0xF; + red_li = 16 - red_l; + + grn_h = *grnp >> 4; + grn_l = *grnp & 0xF; + grn_li = 16 - grn_l; + + blu_h = *blup >> 4; + blu_l = *blup & 0xF; + blu_li = 16 - blu_l; + +// printf("%d %d %d =>", *redp, *grnp, *blup); + + tab0 = this->lut[red_h+0][grn_h+0][blu_h+0]; + tab1 = this->lut[red_h+1][grn_h+0][blu_h+0]; + tab2 = this->lut[red_h+0][grn_h+1][blu_h+0]; + tab3 = this->lut[red_h+1][grn_h+1][blu_h+0]; + tab4 = this->lut[red_h+0][grn_h+0][blu_h+1]; + tab5 = this->lut[red_h+1][grn_h+0][blu_h+1]; + tab6 = this->lut[red_h+0][grn_h+1][blu_h+1]; + tab7 = this->lut[red_h+1][grn_h+1][blu_h+1]; + +#if 0 + printf(" %d %d %d ", tab0[0], tab0[1], tab0[2]); + printf(" %d %d %d ", tab1[0], tab1[1], tab1[2]); + printf(" %d %d %d ", tab2[0], tab2[1], tab2[2]); + printf(" %d %d %d ", tab3[0], tab3[1], tab3[2]); + printf(" %d %d %d ", tab4[0], tab4[1], tab4[2]); + printf(" %d %d %d ", tab5[0], tab5[1], tab5[2]); + printf(" %d %d %d ", tab6[0], tab6[1], tab6[2]); + printf(" %d %d %d ", tab7[0], tab7[1], tab7[2]); +#endif + *redp = (blu_li + * (grn_li * (red_li * tab0[0] + red_l * tab1[0]) + + grn_l * (red_li * tab2[0] + red_l * tab3[0])) + + blu_l + * (grn_li * (red_li * tab4[0] + red_l * tab5[0]) + + grn_l * (red_li * tab6[0] + red_l * tab7[0])) + + 2048) >> 12; + *grnp = (blu_li + * (grn_li * (red_li * tab0[1] + red_l * tab1[1]) + + grn_l * (red_li * tab2[1] + red_l * tab3[1])) + + blu_l + * (grn_li * (red_li * tab4[1] + red_l * tab5[1]) + + grn_l * (red_li * tab6[1] + red_l * tab7[1])) + + 2048) >> 12; + *blup = (blu_li + * (grn_li * (red_li * tab0[2] + red_l * tab1[2]) + + grn_l * (red_li * tab2[2] + red_l * tab3[2])) + + blu_l + * (grn_li * (red_li * tab4[2] + red_l * tab5[2]) + + grn_l * (red_li * tab6[2] + red_l * tab7[2])) + + 2048) >> 12; + +// printf("=> %d %d %d\n", *redp, *grnp, *blup); +} + +/* Perform a total conversion on an entire image */ +void CColorConv3D_DoColorConv(struct CColorConv3D *this, uint8_t *data, uint16_t cols, uint16_t rows, uint32_t stride, int rgb_bgr) +{ + uint16_t i, j; + + uint8_t *ptr; + + for ( i = 0; i < rows ; i++ ) + { + ptr = data; + for ( j = 0; cols > j; j++ ) + { + if (rgb_bgr) { + CColorConv3D_DoColorConvPixel(this, ptr + 2, ptr + 1, ptr); + } else { + CColorConv3D_DoColorConvPixel(this, ptr, ptr + 1, ptr + 2); + } + ptr += 3; + } + data += stride; + } +} + +/* ---- end 3D LUT ---- */ +static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int status, int status2, int media); +static char *mitsu9550_media_types(uint8_t type, uint8_t is_s); + static void *mitsu9550_init(void) { struct mitsu9550_ctx *ctx = malloc(sizeof(struct mitsu9550_ctx)); @@ -248,32 +537,58 @@ static void *mitsu9550_init(void) return ctx; } -static void mitsu9550_attach(void *vctx, struct libusb_device_handle *dev, +static int mitsu9550_attach(void *vctx, struct libusb_device_handle *dev, int type, uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct mitsu9550_ctx *ctx = vctx; - struct libusb_device *device; - struct libusb_device_descriptor desc; + struct mitsu9550_media media; 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(&mitsu9550_backend, - desc.idVendor, desc.idProduct); + ctx->type = type; if (ctx->type == P_MITSU_9550S || ctx->type == P_MITSU_9800S) ctx->is_s = 1; - ctx->last_donor = ctx->last_remain = 65535; + if (ctx->type == P_MITSU_9800 || + ctx->type == P_MITSU_9800S || + ctx->type == P_MITSU_9810) + ctx->is_98xx = 1; + + if (test_mode < TEST_MODE_NOATTACH) { + if (mitsu9550_get_status(ctx, (uint8_t*) &media, 0, 0, 1)) + return CUPS_BACKEND_FAILED; + } else { + int media_code = 0x2; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")) & 0xf; + + media.max = cpu_to_be16(400); + media.remain = cpu_to_be16(330); + media.type = media_code; + } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = mitsu9550_media_types(media.type, ctx->is_s); + ctx->marker.levelmax = be16_to_cpu(media.max); + ctx->marker.levelnow = be16_to_cpu(media.remain); + + return CUPS_BACKEND_OK; } +static void mitsu9550_cleanup_job(const void *vjob) +{ + const struct mitsu9550_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); +} static void mitsu9550_teardown(void *vctx) { struct mitsu9550_ctx *ctx = vctx; @@ -281,86 +596,100 @@ static void mitsu9550_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); + if (ctx->lut) + CColorConv3D_Destroy3DColorTable(ctx->lut); + if (ctx->m98xxdata) + free(ctx->m98xxdata); free(ctx); } -static int mitsu9550_read_parse(void *vctx, int data_fd) { +static int mitsu9550_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct mitsu9550_ctx *ctx = vctx; uint8_t buf[sizeof(struct mitsu9550_hdr1)]; int remain, i; uint32_t planelen = 0; + struct mitsu9550_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } - - ctx->hdr1_present = 0; - ctx->hdr2_present = 0; - ctx->hdr3_present = 0; - ctx->hdr4_present = 0; + memset(job, 0, sizeof(*job)); + job->is_raw = 1; top: /* Read in initial header */ remain = sizeof(buf); while (remain > 0) { i = read(data_fd, buf + sizeof(buf) - remain, remain); - if (i == 0) + if (i == 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; } /* Sanity check */ if (buf[0] != 0x1b || buf[1] != 0x57 || buf[3] != 0x2e) { - if (!ctx->hdr1_present || !ctx->hdr2_present) { - ERROR("Unrecognized data format!\n"); + if (!job->hdr1_present || !job->hdr2_present) { + ERROR("Unrecognized data format (%02x%02x%02x%02x)!\n", + buf[0], buf[1], buf[2], buf[3]); + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; - } else if (buf[0] == 0x1b && buf[1] == 0x5a && + } else if (buf[0] == 0x1b && + buf[1] == 0x5a && buf[2] == 0x54) { /* We're in the data portion now */ if (buf[3] == 0x10) planelen *= 2; + else if (ctx->is_98xx && buf[3] == 0x80) + job->is_raw = 0; goto hdr_done; } else { - ERROR("Unrecognized data block!\n"); + ERROR("Unrecognized data block (%02x%02x%02x%02x)!\n", + buf[0], buf[1], buf[2], buf[3]); + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; } } switch(buf[2]) { case 0x20: /* header 1 */ - memcpy(&ctx->hdr1, buf, sizeof(ctx->hdr1)); - ctx->hdr1_present = 1; + memcpy(&job->hdr1, buf, sizeof(job->hdr1)); + job->hdr1_present = 1; /* Work out printjob size */ - ctx->rows = be16_to_cpu(ctx->hdr1.rows); - ctx->cols = be16_to_cpu(ctx->hdr1.cols); - planelen = ctx->rows * ctx->cols; + job->rows = be16_to_cpu(job->hdr1.rows); + job->cols = be16_to_cpu(job->hdr1.cols); + planelen = job->rows * job->cols; break; case 0x21: /* header 2 */ - memcpy(&ctx->hdr2, buf, sizeof(ctx->hdr2)); - ctx->hdr2_present = 1; + memcpy(&job->hdr2, buf, sizeof(job->hdr2)); + job->hdr2_present = 1; break; case 0x22: /* header 3 */ - memcpy(&ctx->hdr3, buf, sizeof(ctx->hdr3)); - ctx->hdr3_present = 1; + memcpy(&job->hdr3, buf, sizeof(job->hdr3)); + job->hdr3_present = 1; break; case 0x26: /* header 4 */ - memcpy(&ctx->hdr4, buf, sizeof(ctx->hdr4)); - ctx->hdr4_present = 1; + memcpy(&job->hdr4, buf, sizeof(job->hdr4)); + job->hdr4_present = 1; break; default: ERROR("Unrecognized header format (%02x)!\n", buf[2]); + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; } @@ -369,34 +698,74 @@ top: hdr_done: - /* We have three planes and the final terminator to read */ - remain = 3 * (planelen + sizeof(struct mitsu9550_plane)) + sizeof(struct mitsu9550_cmd); + /* Read in CP98xx data tables if necessary */ + if (ctx->is_98xx && !job->is_raw && !ctx->m98xxdata) { + int fd; + + DEBUG("Reading in 98xx data from disk\n"); + fd = open(MITSU_M98xx_DATATABLE_FILE, O_RDONLY); + if (fd < 0) { + ERROR("Unable to open 98xx data table file '%s'\n", MITSU_M98xx_DATATABLE_FILE); + mitsu9550_cleanup_job(job); + return CUPS_BACKEND_FAILED; + } + ctx->m98xxdata = malloc(DATATABLE_SIZE); + if (!ctx->m98xxdata) { + ERROR("Memory allocation Failure!\n"); + mitsu9550_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; + } + remain = DATATABLE_SIZE; + while (remain) { + i = read(fd, ((uint8_t*)ctx->m98xxdata) + (DATATABLE_SIZE - remain), remain); + if (i < 0) { + mitsu9550_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + remain -= i; + } + close(fd); + } + + if (job->is_raw) { + /* We have three planes + headers and the final terminator to read */ + remain = 3 * (planelen + sizeof(struct mitsu9550_plane)) + sizeof(struct mitsu9550_cmd); + } else { + /* We have one plane + header and the final terminator to read */ + remain = planelen * 3 + sizeof(struct mitsu9550_plane) + sizeof(struct mitsu9550_cmd); + } /* Mitsu9600 windows spool uses more, smaller blocks, but plane data is the same */ if (ctx->type == P_MITSU_9600) { remain += 128 * sizeof(struct mitsu9550_plane); /* 39 extra seen on 4x6" */ } - /* Don't forget the matte plane! */ - if (ctx->hdr1.matte) { - remain += planelen + sizeof(struct mitsu9550_plane) + sizeof(struct mitsu9550_cmd); - } - /* 9550S/9800S doesn't typically sent over hdr4! */ if (ctx->type == P_MITSU_9550S || ctx->type == P_MITSU_9800S) { /* XXX Has to do with error policy, but not sure what. Mitsu9550-S/9800-S will set this based on a command, but it's not part of the standard job spool */ - ctx->hdr4_present = 0; + job->hdr4_present = 0; + } + + /* Disable matte if the printer doesn't support it */ + if (job->hdr1.matte) { + if (ctx->type != P_MITSU_9810) { + WARNING("Matte not supported on this printer, disabling\n"); + job->hdr1.matte = 0; + } else if (job->is_raw) { + remain += planelen + sizeof(struct mitsu9550_plane) + sizeof(struct mitsu9550_cmd); + } } /* Allocate buffer for the payload */ - ctx->datalen = 0; - ctx->databuf = malloc(remain); - if (!ctx->databuf) { + job->datalen = 0; + job->databuf = malloc(remain); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + mitsu9550_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } /* Load up the data blocks.*/ @@ -408,7 +777,9 @@ hdr_done: if (plane->cmd[0] != 0x1b || plane->cmd[1] != 0x5a || plane->cmd[2] != 0x54) { - ERROR("Unexpected data read, aborting job\n"); + ERROR("Unrecognized data read (%02x%02x%02x%02x)!\n", + plane->cmd[0], plane->cmd[1], plane->cmd[2], plane->cmd[3]); + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; } @@ -416,20 +787,26 @@ hdr_done: planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols); if (plane->cmd[3] == 0x10) planelen *= 2; + if (plane->cmd[3] == 0x80) + planelen *= 3; /* Copy plane header into buffer */ - memcpy(ctx->databuf + ctx->datalen, buf, sizeof(buf)); - ctx->datalen += sizeof(buf); + memcpy(job->databuf + job->datalen, buf, sizeof(buf)); + job->datalen += sizeof(buf); planelen -= sizeof(buf) - sizeof(struct mitsu9550_plane); /* Read in the spool data */ while(planelen > 0) { - i = read(data_fd, ctx->databuf + ctx->datalen, planelen); - if (i == 0) + i = read(data_fd, job->databuf + job->datalen, planelen); + if (i == 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; - ctx->datalen += i; + } + job->datalen += i; planelen -= i; } @@ -438,44 +815,87 @@ hdr_done: - Job footer (4B) */ i = read(data_fd, buf, 4); - if (i == 0) + if (i == 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } /* Is this a "job end" marker? */ - if (plane->cmd[0] != 0x1b || - plane->cmd[1] != 0x5a || - plane->cmd[2] != 0x54) { + if (plane->cmd[0] == 0x1b && + plane->cmd[1] == 0x50 && + plane->cmd[3] == 0x00) { /* store it in the buffer */ - memcpy(ctx->databuf + ctx->datalen, buf, 4); - ctx->datalen += 4; + memcpy(job->databuf + job->datalen, buf, 4); + job->datalen += 4; /* Unless we have a matte plane following, we're done */ - if (!ctx->hdr1.matte) + if (job->hdr1.matte != 0x01) break; - planelen = sizeof(buf); + remain = sizeof(buf); } else { /* It's part of a block header, mark what we've read */ - planelen = sizeof(buf) - 4; + remain = sizeof(buf) - 4; } /* Read in the rest of the header */ - while (planelen > 0) { - i = read(data_fd, buf + sizeof(buf) - planelen, planelen); - if (i == 0) + while (remain > 0) { + i = read(data_fd, buf + sizeof(buf) - remain, remain); + if (i == 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsu9550_cleanup_job(job); return CUPS_BACKEND_CANCEL; - planelen -= i; + } + remain -= i; } } - /* Disable matte if the printer doesn't support it */ - if (ctx->hdr1.matte && ctx->type != P_MITSU_9810) { - WARNING("Matte not supported on this printer, disabling\n"); - ctx->hdr1.matte = 0; + /* Apply LUT */ + if (ctx->is_98xx && !job->is_raw && job->hdr2.unkc[9]) { + DEBUG("Applying 3D LUT\n"); + if (!ctx->lut) { + uint8_t *buf = malloc(LUT_LEN); + if (!buf) { + ERROR("Memory allocation failure!\n"); + mitsu9550_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; + } + if (CColorConv3D_Get3DColorTable(buf, MITSU_M98xx_LUT_FILE)) { + ERROR("Unable to open LUT file '%s'\n", MITSU_M98xx_LUT_FILE); + mitsu9550_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + ctx->lut = CColorConv3D_Load3DColorTable(buf); + free(buf); + if (!ctx->lut) { + ERROR("Unable to parse LUT\n"); + mitsu9550_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + } + CColorConv3D_DoColorConv(ctx->lut, job->databuf + sizeof(struct mitsu9550_plane), + job->cols, job->rows, job->cols * 3, COLORCONV_BGR); + job->hdr2.unkc[9] = 0; + } + + /* Update printjob header to reflect number of requested copies */ + if (job->hdr2_present) { + copies = 1; + job->hdr2.copies = cpu_to_be16(copies); } + job->copies = copies; + + /* All further work is in main loop */ + if (test_mode >= TEST_MODE_NOPRINT) + mitsu9550_main_loop(ctx, job); + + *vjob = job; return CUPS_BACKEND_OK; } @@ -740,22 +1160,120 @@ static int validate_media(int type, int media, int cols, int rows) return 0; } -static int mitsu9550_main_loop(void *vctx, int copies) { +static int mitsu9550_main_loop(void *vctx, const void *vjob) { struct mitsu9550_ctx *ctx = vctx; struct mitsu9550_cmd cmd; uint8_t rdbuf[READBACK_LEN]; uint8_t *ptr; int ret; +#if 0 + int copies; +#endif + +// const struct mitsu9550_printjob *job = vjob; + struct mitsu9550_printjob *job = (struct mitsu9550_printjob*) vjob; // XXX not good. if (!ctx) return CUPS_BACKEND_FAILED; - - /* Update printjob header to reflect number of requested copies */ - ctx->hdr2.copies = cpu_to_be16(copies); + if (!job) + return CUPS_BACKEND_FAILED; /* Okay, let's do this thing */ - ptr = ctx->databuf; + ptr = job->databuf; + +#if 0 + /* If hdr2 is not present, we have to generate copies ourselves! */ + if (job->hdr2_present) + copies = job->copies; + // XXX.. +#endif + + /* Do the 98xx processing here */ + if (!ctx->is_98xx || job->is_raw) + goto bypass; + + uint8_t *newbuf; + uint32_t newlen = 0; + struct mitsu98xx_data *table; + int i, remain, planelen; + + planelen = job->rows * job->cols * 2; + remain = (job->hdr1.matte ? 3 : 4) * (planelen + sizeof(struct mitsu9550_plane)) + sizeof(struct mitsu9550_cmd); + newbuf = malloc(remain); + if (!newbuf) { + ERROR("Memory allocation Failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + switch (job->hdr2.mode) { + case 0x80: + table = &ctx->m98xxdata->superfine; + break; + case 0x11: + table = &ctx->m98xxdata->fine_hg; + job->hdr2.mode = 0x10; + break; + case 0x10: + default: + table = &ctx->m98xxdata->fine_std; + break; + } + + DEBUG("Applying 8bpp->12bpp Gamma Correction\n"); + /* For B/Y plane */ + memcpy(newbuf + newlen, job->databuf, sizeof(struct mitsu9550_plane)); + newbuf[newlen + 3] = 0x10; /* ie 16bpp data */ + newlen += sizeof(struct mitsu9550_plane); + mitsu98xx_dogamma(job->databuf + sizeof(struct mitsu9550_plane), + (uint16_t*) (newbuf + newlen), + 0, + table->GNMby, + planelen / 2); + newlen += planelen; + + /* For G/M plane */ + memcpy(newbuf + newlen, job->databuf, sizeof(struct mitsu9550_plane)); + newbuf[newlen + 3] = 0x10; /* ie 16bpp data */ + newlen += sizeof(struct mitsu9550_plane); + mitsu98xx_dogamma(job->databuf + sizeof(struct mitsu9550_plane), + (uint16_t*) (newbuf + newlen), + 1, + table->GNMgm, + planelen / 2); + newlen += planelen; + + /* For R/C plane */ + memcpy(newbuf + newlen, job->databuf, sizeof(struct mitsu9550_plane)); + newbuf[newlen + 3] = 0x10; /* ie 16bpp data */ + newlen += sizeof(struct mitsu9550_plane); + mitsu98xx_dogamma(job->databuf + sizeof(struct mitsu9550_plane), + (uint16_t*) (newbuf + newlen), + 2, + table->GNMrc, + planelen / 2); + newlen += planelen; + + /* And finally, the job footer. */ + memcpy(newbuf + newlen, job->databuf + sizeof(struct mitsu9550_plane) + planelen * 3, sizeof(struct mitsu9550_cmd)); + newlen += sizeof(struct mitsu9550_cmd); + + /* Clean up, and move pointer to new buffer; */ + free(job->databuf); + job->databuf = newbuf; + job->datalen = newlen; + ptr = job->databuf; + + /* Now handle the matte plane generation */ + if (job->hdr1.matte) { + if ((i = mitsu98xx_fillmatte(job))) { + return i; + } + } + +bypass: + /* Bypass */ + if (test_mode >= TEST_MODE_NOPRINT) + return CUPS_BACKEND_OK; top: if (ctx->is_s) { @@ -809,38 +1327,28 @@ top: /* Now it's time for the actual print job! */ - if (ctx->is_s) { - cmd.cmd[0] = 0x1b; - cmd.cmd[1] = 0x44; - cmd.cmd[2] = 0; - cmd.cmd[3] = 0; - if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*) &cmd, 4))) - return CUPS_BACKEND_FAILED; - } - QUERY_STATUS(); /* Send printjob headers from spool data */ - if (ctx->hdr1_present) + if (job->hdr1_present) if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*) &ctx->hdr1, sizeof(ctx->hdr1)))) + (uint8_t*) &job->hdr1, sizeof(job->hdr1)))) return CUPS_BACKEND_FAILED; - if (ctx->hdr2_present) + if (job->hdr2_present) if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*) &ctx->hdr2, sizeof(ctx->hdr2)))) + (uint8_t*) &job->hdr2, sizeof(job->hdr2)))) return CUPS_BACKEND_FAILED; - if (ctx->hdr3_present) + if (job->hdr3_present) if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*) &ctx->hdr3, sizeof(ctx->hdr3)))) + (uint8_t*) &job->hdr3, sizeof(job->hdr3)))) return CUPS_BACKEND_FAILED; - if (ctx->hdr4_present) + if (job->hdr4_present) if ((ret = send_data(ctx->dev, ctx->endp_down, - (uint8_t*) &ctx->hdr4, sizeof(struct mitsu9550_hdr4)))) + (uint8_t*) &job->hdr4, sizeof(struct mitsu9550_hdr4)))) return CUPS_BACKEND_FAILED; if (ctx->is_s) { - /* Send "start data" command */ + /* I think this a "clear memory' command...? */ cmd.cmd[0] = 0x1b; cmd.cmd[1] = 0x5a; cmd.cmd[2] = 0x43; @@ -852,13 +1360,15 @@ top: } /* Send over plane data */ - while(1) { + while(ptr < (job->databuf + job->datalen)) { struct mitsu9550_plane *plane = (struct mitsu9550_plane *)ptr; - uint32_t planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols); + uint32_t planelen; if (plane->cmd[0] != 0x1b || plane->cmd[1] != 0x5a || plane->cmd[2] != 0x54) break; + + planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols); if (plane->cmd[3] == 0x10) planelen *= 2; @@ -877,29 +1387,22 @@ top: struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf; // struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf; struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf; - uint16_t donor, remain; + uint16_t donor; ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media if (ret < 0) return CUPS_BACKEND_FAILED; + donor = be16_to_cpu(media->remain); + if (donor != ctx->marker.levelnow) { + ctx->marker.levelnow = donor; + dump_markers(&ctx->marker, 1, 0); + } /* Sanity-check media response */ if (media->remain == 0 || media->max == 0) { ERROR("Printer out of media!\n"); - ATTR("marker-levels=%d\n", 0); return CUPS_BACKEND_HOLD; } - remain = be16_to_cpu(media->remain); - donor = be16_to_cpu(media->max); - donor = remain/donor; - if (donor != ctx->last_donor) { - ctx->last_donor = donor; - ATTR("marker-levels=%u\n", donor); - } - if (remain != ctx->last_remain) { - ctx->last_remain = remain; - ATTR("marker-message=\"%u prints remaining on '%s' ribbon\"\n", remain, mitsu9550_media_types(media->type, ctx->is_s)); - } ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2 if (ret < 0) return CUPS_BACKEND_FAILED; @@ -952,7 +1455,7 @@ top: } /* Don't forget the 9810's matte plane */ - if (ctx->hdr1.matte) { + if (job->hdr1.matte) { struct mitsu9550_plane *plane = (struct mitsu9550_plane *)ptr; uint32_t planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols); @@ -973,7 +1476,7 @@ top: if ((ret = send_data(ctx->dev, ctx->endp_down, ptr, sizeof(cmd)))) return CUPS_BACKEND_FAILED; - ptr += sizeof(cmd); +// ptr += sizeof(cmd); } /* Status loop, run until printer reports completion */ @@ -981,29 +1484,22 @@ top: struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf; // struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf; struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf; - uint16_t donor, remain; + uint16_t donor; ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media if (ret < 0) return CUPS_BACKEND_FAILED; + donor = be16_to_cpu(media->remain); + if (donor != ctx->marker.levelnow) { + ctx->marker.levelnow = donor; + dump_markers(&ctx->marker, 1, 0); + } /* Sanity-check media response */ if (media->remain == 0 || media->max == 0) { ERROR("Printer out of media!\n"); - ATTR("marker-levels=%d\n", 0); return CUPS_BACKEND_HOLD; } - remain = be16_to_cpu(media->remain); - donor = be16_to_cpu(media->max); - donor = remain/donor; - if (donor != ctx->last_donor) { - ctx->last_donor = donor; - ATTR("marker-levels=%u\n", donor); - } - if (remain != ctx->last_remain) { - ctx->last_remain = remain; - ATTR("marker-message=\"%u prints remaining on '%s' ribbon\"\n", remain, mitsu9550_media_types(media->type, ctx->is_s)); - } ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2 if (ret < 0) return CUPS_BACKEND_FAILED; @@ -1156,10 +1652,21 @@ static int mitsu9550_query_serno(struct libusb_device_handle *dev, uint8_t endp_ return ret; } +static int mitsu9550_cancel_job(struct mitsu9550_ctx *ctx) +{ + int ret; + + uint8_t buf[2] = { 0x1b, 0x44 }; + ret = send_data(ctx->dev, ctx->endp_down, buf, sizeof(buf)); + + return ret; +} + static void mitsu9550_cmdline(void) { DEBUG("\t\t[ -m ] # Query media\n"); DEBUG("\t\t[ -s ] # Query status\n"); + DEBUG("\t\t[ -X ] # Cancel current job\n"); } static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv) @@ -1170,7 +1677,7 @@ static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv) if (!ctx) return -1; - while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "ms")) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "msX")) >= 0) { switch(i) { GETOPT_PROCESS_GLOBAL case 'm': @@ -1181,6 +1688,9 @@ static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv) if (!j) j = mitsu9550_query_status2(ctx); break; + case 'X': + j = mitsu9550_cancel_job(ctx); + break; default: break; /* Ignore completely */ } @@ -1191,37 +1701,70 @@ static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv) return 0; } +static int mitsu9550_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct mitsu9550_ctx *ctx = vctx; + struct mitsu9550_media media; + + /* Query printer status */ + if (mitsu9550_get_status(ctx, (uint8_t*) &media, 0, 0, 1)) + return CUPS_BACKEND_FAILED; + + ctx->marker.levelnow = be16_to_cpu(media.remain); + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + +static const char *mitsu9550_prefixes[] = { + "mitsu9xxx", // Family driver, do not nuke. + "mitsubishi-9000dw", "mitsubishi-9500dw", + "mitsubishi-9550dw", "mitsubishi-9550dw-s", + "mitsubishi-9600dw", // "mitsubishi-9600dw-s", + "mitsubishi-9800dw", "mitsubishi-9800dw-s", + "mitsubishi-9810dw", + // extras + "mitsubishi-9550d", "mitsubishi-9550dz", "mitsubishi-9800d", "mitsubishi-9800dz", "mitsubishi-9810d", + // Backwards compatibility + "mitsu9000", "mitsu9500", "mitsu9550", "mitsu9600", "mitsu9800", "mitsu9810", + NULL +}; + /* Exported */ struct dyesub_backend mitsu9550_backend = { - .name = "Mitsubishi CP-9550 family", - .version = "0.29", - .uri_prefix = "mitsu9550", + .name = "Mitsubishi CP9xxx family", + .version = "0.41", + .uri_prefixes = mitsu9550_prefixes, .cmdline_usage = mitsu9550_cmdline, .cmdline_arg = mitsu9550_cmdline_arg, .init = mitsu9550_init, .attach = mitsu9550_attach, .teardown = mitsu9550_teardown, + .cleanup_job = mitsu9550_cleanup_job, .read_parse = mitsu9550_read_parse, .main_loop = mitsu9550_main_loop, .query_serno = mitsu9550_query_serno, + .query_markers = mitsu9550_query_markers, .devices = { - { USB_VID_MITSU, USB_PID_MITSU_9000AM, P_MITSU_9550, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9000D, P_MITSU_9550, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9500D, P_MITSU_9550, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9550D, P_MITSU_9550, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9550DS, P_MITSU_9550S, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9600D, P_MITSU_9600, ""}, -// { USB_VID_MITSU, USB_PID_MITSU_9600D, P_MITSU_9600S, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9800D, P_MITSU_9800, ""}, - { USB_VID_MITSU, USB_PID_MITSU_9800DS, P_MITSU_9800S, ""}, - { USB_VID_MITSU, USB_PID_MITSU_98__D, P_MITSU_9810, ""}, -// { USB_VID_MITSU, USB_PID_MITSU_9810D, P_MITSU_9810, ""}, -// { USB_VID_MITSU, USB_PID_MITSU_9820DS, P_MITSU_9820S, ""}, - { 0, 0, 0, ""} + { USB_VID_MITSU, USB_PID_MITSU_9000AM, P_MITSU_9550, NULL, "mitsubishi-9000dw"}, // XXX -am instead? + { USB_VID_MITSU, USB_PID_MITSU_9000D, P_MITSU_9550, NULL, "mitsubishi-9000dw"}, + { USB_VID_MITSU, USB_PID_MITSU_9500D, P_MITSU_9550, NULL, "mitsubishi-9500dw"}, + { USB_VID_MITSU, USB_PID_MITSU_9550D, P_MITSU_9550, NULL, "mitsubishi-9550dw"}, + { USB_VID_MITSU, USB_PID_MITSU_9550DS, P_MITSU_9550S, NULL, "mitsubishi-9550dw-s"}, + { USB_VID_MITSU, USB_PID_MITSU_9600D, P_MITSU_9600, NULL, "mitsubishi-9600dw"}, +// { USB_VID_MITSU, USB_PID_MITSU_9600D, P_MITSU_9600S, NULL, "mitsubishi-9600dw-s"}, + { USB_VID_MITSU, USB_PID_MITSU_9800D, P_MITSU_9800, NULL, "mitsubishi-9800dw"}, + { USB_VID_MITSU, USB_PID_MITSU_9800DS, P_MITSU_9800S, NULL, "mitsubishi-9800dw-s"}, + { USB_VID_MITSU, USB_PID_MITSU_98__D, P_MITSU_9810, NULL, "mitsubishi-9810dw"}, +// { USB_VID_MITSU, USB_PID_MITSU_9810D, P_MITSU_9810, NULL, "mitsubishi-9810dw"}, +// { USB_VID_MITSU, USB_PID_MITSU_9820DS, P_MITSU_9820S, NULL, "mitsubishi-9820dw-s"}, + { 0, 0, 0, NULL, NULL} } }; -/* Mitsubish CP-9550/9600/9800/9810 spool format: +/* Mitsubish CP-9500/9550/9600/9800/9810/9820 spool format: Spool file consists of 3 (or 4) 50-byte headers, followed by three image planes, each with a 12-byte header, then a 4-byte footer. @@ -1240,12 +1783,13 @@ struct dyesub_backend mitsu9550_backend = { 1b 57 21 2e 00 80 00 22 QQ QQ 00 00 00 00 00 00 :: ZZ ZZ = num copies (>= 0x01) 00 00 00 00 00 00 00 00 00 00 00 00 ZZ ZZ 00 00 :: YY = 00/80 Fine/SuperFine (9550), 10/80 Fine/Superfine (98x0), 00 (9600) XX 00 00 00 00 00 YY 00 00 00 00 00 00 00 00 00 :: XX = 00 normal, 83 Cut 2x6 (9550 only!) - 00 01 :: QQ QQ = 0x0803 on 9550, 0x0801 on 98x0, 0x0003 on 9600 + RR 01 :: QQ QQ = 0x0803 on 9550, 0x0801 on 98x0, 0x0003 on 9600, 0xa803 on 9500 + :: RR = 01 for "use LUT" on 98xx, 0x00 otherwise. Extension to stock. ~~~ Header 3 (9550 and 9800-S only..) - 1b 57 22 2e 00 40 00 00 00 00 00 XX 00 00 00 00 :: XX = 00 normal, 01 FineDeep - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 1b 57 22 2e 00 QQ 00 00 00 00 00 XX 00 00 00 00 :: XX = 00 normal, 01 FineDeep + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: QQ = 0xf0 on 9500, 0x40 on the rest 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 @@ -1253,18 +1797,18 @@ struct dyesub_backend mitsu9550_backend = { 1b 57 26 2e 00 QQ 00 00 00 00 00 SS RR 01 00 00 :: QQ = 0x70 on 9550/98x0, 0x60 on 9600 or 9800S 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: RR = 0x01 on 9550/98x0, 0x00 on 9600 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: SS = 0x01 on 9800S + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: SS = 0x01 on 9800S, 0x00 otherwise. 00 00 ~~~~ Data follows: - Format is: planar YMC16 for 98x0 (only 12 bits used) - planar BGR for 9550DW - planar RGB for 9550DW-S and 9600DW + Format is: planar YMC16 for 98x0 (but only 12 bits used, BIG endian) + planar RGB for all others - 1b 5a 54 ?? RR RR 00 00 07 14 04 d8 :: 0714 == columns, 04d8 == rows - :: RRRR == row offset for data + 1b 5a 54 ?? RR RR CC CC 07 14 04 d8 :: 0714 == columns, 04d8 == rows + :: RRRR == row offset for data, CCCC == col offset for data :: ?? == 0x00 for 8bpp, 0x10 for 16/12bpp. + :: 0x80 for PACKED BGR! Data follows immediately, no padding. @@ -1278,12 +1822,15 @@ struct dyesub_backend mitsu9550_backend = { ~~~~ Footer: + 1b 50 57 00 (9500) 1b 50 46 00 (9550) 1b 50 47 00 (9550-S) 1b 50 48 00 (9600) - 1b 50 4c 00 (98x0) + 1b 50 4c 00 (9800/9810) 1b 50 4e 00 (9800-S) + Unknown: 9600-S, 9820-S + ~~~~ Lamination data follows (on 9810 only, if matte selected) 1b 5a 54 10 00 00 00 00 06 24 04 34 @@ -1306,7 +1853,7 @@ struct dyesub_backend mitsu9550_backend = { -> 1b 53 c5 9d - [[ Unknown ]] + [[ Unknown, query some parameter? ]] -> 1b 4b 7f 00 <- eb 4b 8f 00 02 00 5e [[ '02' seems to be a length ]] @@ -1331,7 +1878,7 @@ struct dyesub_backend mitsu9550_backend = { 00 00 00 00 00 00 00 00 00 00 00 00 MM MM 00 00 :: MM MM = Max prints NN NN 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: NN NN = Remaining - [[ unknown, 9800-only ]] + [[ unknown query, 9800-only ]] -> 1b 4b 01 00 <- e4 4b 01 00 02 00 78 @@ -1350,7 +1897,7 @@ struct dyesub_backend mitsu9550_backend = { 00 00 00 00 00 00 00 00 00 00 00 QQ 00 00 00 00 :: QQ == Prints in job? 00 00 00 00 00 00 00 00 00 00 NN NN 0A 00 00 01 :: NN NN = Remaining media - [[ Unknown ]] + [[ Job Cancel ]] -> 1b 44 @@ -1388,7 +1935,7 @@ struct dyesub_backend mitsu9550_backend = { Followed by image plane #3 (Red), XXXX * YYYY bytes - [[ Unknown -- End Data aka START print? ]] + [[ Footer -- End Data aka START print? See above for other models ]] -> 1b 50 47 00 [9550S] -> 1b 50 4e 00 [9800S] diff --git a/src/cups/backend_mitsud90.c b/src/cups/backend_mitsud90.c new file mode 100644 index 0000000..9655e55 --- /dev/null +++ b/src/cups/backend_mitsud90.c @@ -0,0 +1,1456 @@ +/* + * Mitsubishi CP-D90DW Photo Printer CUPS backend + * + * (c) 2018 Solomon Peachy + * + * 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, see . + * + * [http://www.gnu.org/licenses/gpl-2.0.html] + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BACKEND mitsud90_backend + +#include "backend_common.h" + +#define USB_VID_MITSU 0x06D3 +#define USB_PID_MITSU_D90 0x3B60 + +const char *mitsu70x_media_types(uint8_t brand, uint8_t type); +const char *mitsu70x_temperatures(uint8_t temp); + +/* Printer data structures */ +#define D90_STATUS_TYPE_MODEL 0x01 // 10, null-terminated ASCII. 'CPD90D' +#define D90_STATUS_TYPE_x02 0x02 // 1, 0x5f ? +#define D90_STATUS_TYPE_FW_0b 0x0b // 8, 34 31 34 42 31 31 a7 de (414D11) +#define D90_STATUS_TYPE_FW_MA 0x0c // 8, 34 31 35 41 38 31 86 bf (415A81) // MAIN FW +#define D90_STATUS_TYPE_FW_F 0x0d // 8, 34 31 36 41 35 31 dc 8a (416A51) // FPGA FW +#define D90_STATUS_TYPE_FW_T 0x0e // 8, 34 31 37 45 31 31 e7 e6 (417E11) // TABLE FW +#define D90_STATUS_TYPE_FW_0f 0x0f // 8, 34 31 38 41 31 32 6c 64 (418A12) +#define D90_STATUS_TYPE_FW_11 0x11 // 8, 34 32 31 51 31 31 74 f2 (421Q11) +#define D90_STATUS_TYPE_FW_ME 0x13 // 8, 34 31 39 45 31 31 15 bf (419E11) // MECHA FW + +#define D90_STATUS_TYPE_ERROR 0x16 // 11 (see below) +#define D90_STATUS_TYPE_MECHA 0x17 // 2 (see below) +#define D90_STATUS_TYPE_x1e 0x1e // 1, power state or time? (x00) +#define D90_STATUS_TYPE_TEMP 0x1f // 1 (see below) +#define D90_STATUS_TYPE_x22 0x22 // 2, all 0 +#define D90_STATUS_TYPE_x28 0x28 // 2, all 0, seen some sort of counter? +#define D90_STATUS_TYPE_x29 0x29 // 8, e0 07 00 00 21 e6 b3 22 +#define D90_STATUS_TYPE_MEDIA 0x2a // 10 (see below) +#define D90_STATUS_TYPE_x2b 0x2b // 2, all 0 +#define D90_STATUS_TYPE_x2c 0x2c // 2, 00 56 +#define D90_STATUS_TYPE_x65 0x65 // 50, ac 80 00 01 bb b8 fe 48 05 13 5d 9c 00 33 00 00 00 00 00 00 00 00 00 00 00 00 02 39 00 00 00 00 03 13 00 02 10 40 00 00 00 00 00 00 05 80 00 3a 00 00 +#define D90_STATUS_TYPE_x82 0x82 // 1, 80 (iserial disabled?) +#define D90_STATUS_TYPE_x83 0x83 // 1, 00 +#define D90_STATUS_TYPE_x84 0x84 // 1, 00 + +//#define D90_STATUS_TYPE_x85 0x85 // 2, 00 ?? BE, wait time? + // combined total of 5. + +struct mitsud90_fw_resp_single { + uint8_t version[6]; + uint16_t csum; +} __attribute__((packed)); + +struct mitsud90_media_resp { + uint8_t hdr[4]; /* e4 47 44 30 */ + struct { + uint8_t brand; + uint8_t type; + uint8_t unk_a[2]; + uint16_t capacity; /* BE */ + uint16_t remain; /* BE */ + uint8_t unk_b[2]; + } __attribute__((packed)) media; /* D90_STATUS_TYPE_MEDIA */ +} __attribute__((packed)); + +struct mitsud90_status_resp { + uint8_t hdr[4]; /* e4 47 44 30 */ + /* D90_STATUS_TYPE_ERROR */ + uint8_t code[2]; /* 00 is ok, nonzero is error */ + uint8_t unk[9]; + /* D90_STATUS_TYPE_MECHA */ + uint8_t mecha[2]; + /* D90_STATUS_TYPE_TEMP */ + uint8_t temp; +} __attribute__((packed)); + +struct mitsud90_info_resp { + uint8_t hdr[4]; /* e4 47 44 30 */ + uint8_t model[10]; + uint8_t x02; + struct mitsud90_fw_resp_single fw_vers[7]; + uint8_t x1e; + uint8_t x22[2]; + uint8_t x28[2]; + uint8_t x29[8]; + uint8_t x2b[2]; + uint8_t x2c[2]; + uint8_t x65[50]; + uint8_t x82; + uint8_t x83; + uint8_t x84; +} __attribute__((packed)); + +#define D90_MECHA_STATUS_IDLE 0x00 +#define D90_MECHA_STATUS_PRINTING 0x50 +#define D90_MECHA_STATUS_INIT 0x80 +#define D90_MECHA_STATUS_INIT_FEEDCUT 0x10 + +#define D90_MECHA_STATUS_PRINT_FEEDING 0x10 // feeding ? +#define D90_MECHA_STATUS_PRINT_PRE_Y 0x21 // pre Y ? +#define D90_MECHA_STATUS_PRINT_Y 0x22 // Y ? +#define D90_MECHA_STATUS_PRINT_PRE_M 0x23 // pre M ? +#define D90_MECHA_STATUS_PRINT_M 0x24 // M ? +#define D90_MECHA_STATUS_PRINT_PRE_C 0x25 // pre C ? guess! +#define D90_MECHA_STATUS_PRINT_C 0x26 // C ? +#define D90_MECHA_STATUS_PRINT_PRE_OC 0x27 // pre OC ? guess! +#define D90_MECHA_STATUS_PRINT_OC 0x28 // O C? +#define D90_MECHA_STATUS_PRINTING_x2f 0x2f // ?? +#define D90_MECHA_STATUS_PRINTING_x38 0x38 // eject ? + +#define D90_ERROR_STATUS_OK 0x00 +#define D90_ERROR_STATUS_OK_WARMING 0x40 +#define D90_ERROR_STATUS_OK_COOLING 0x80 +#define D90_ERROR_STATUS_RIBBON 0x21 +#define D90_ERROR_STATUS_PAPER 0x22 +#define D90_ERROR_STATUS_PAP_RIB 0x23 +#define D90_ERROR_STATUS_OPEN 0x29 + +struct mitsud90_job_query { + uint8_t hdr[4]; /* 1b 47 44 31 */ + uint16_t jobid; /* BE */ +}; + +struct mitsud90_job_resp { + uint8_t hdr[4]; /* e4 47 44 31 */ + uint8_t unk1; + uint8_t unk2; + uint16_t unk3; +}; + +struct mitsud90_job_hdr { + uint8_t hdr[6]; /* 1b 53 50 30 00 33 */ + uint16_t cols; /* BE */ + uint16_t rows; /* BE */ + uint8_t unk[5]; /* 64 00 00 01 00 */ + union { +#if 0 + struct { + uint8_t margin; + uint16_t position; + } cuts[3] __attribute__((packed)); +#endif + uint8_t cutzero[9]; + } __attribute__((packed)); + uint8_t zero[24]; + + uint8_t overcoat; + uint8_t quality; + uint8_t colorcorr; + uint8_t sharp_h; + uint8_t sharp_v; + uint8_t zero_b[5]; + union { + struct { + uint16_t pano_on; /* 0x0001 when pano is on, */ + uint8_t pano_tot; /* 2 or 3 */ + uint8_t pano_pg; /* 1, 2, 3 */ + uint16_t pano_rows; /* always 0x097c (BE), ie 2428 ie 8" print */ + uint16_t pano_rows2; /* Always 0x30 less than pano_rows */ + uint16_t pano_zero; /* 0x0000 */ + uint8_t pano_unk[6]; /* 02 58 00 0c 00 06 */ + } pano __attribute__((packed)); + uint8_t zero_c[16]; + }; + uint8_t zero_d[6]; + uint8_t zero_fill[432]; +} __attribute__((packed)); + +struct mitsud90_plane_hdr { + uint8_t hdr[10]; /* 1b 5a 54 01 00 09 00 00 00 00 */ + uint16_t cols; /* BE */ + uint16_t rows; /* BE */ + uint8_t zero_fill[498]; +}; + +struct mitsud90_job_footer { + uint8_t hdr[4]; /* 1b 42 51 31 */ + uint8_t pad; + uint8_t seconds; /* 0x05 by default (windows) */ +}; + +struct mitsud90_memcheck { + uint8_t hdr[4]; /* 1b 47 44 33 */ + uint8_t unk[2]; /* 00 33 */ + uint16_t cols; /* BE */ + uint16_t rows; /* BE */ + uint8_t unk_b[4]; /* 64 00 00 01 */ + uint8_t zero_fill[498]; +}; + +struct mitsud90_memcheck_resp { + uint8_t hdr[4]; /* e4 47 44 43 */ + uint8_t size_bad; /* 0x00 is ok */ + uint8_t mem_bad; /* 0x00 is ok */ +}; + +const char *mitsud90_mecha_statuses(const uint8_t *code) +{ + switch (code[0]) { + case D90_MECHA_STATUS_IDLE: + return "Idle"; + case D90_MECHA_STATUS_PRINTING: + switch (code[1]) { + case D90_MECHA_STATUS_PRINT_FEEDING: + return "Feeding Media"; + case D90_MECHA_STATUS_PRINT_PRE_Y: + case D90_MECHA_STATUS_PRINT_Y: + return "Printing Yellow"; + case D90_MECHA_STATUS_PRINT_PRE_M: + case D90_MECHA_STATUS_PRINT_M: + return "Printing Magenta"; + case D90_MECHA_STATUS_PRINT_PRE_C: + case D90_MECHA_STATUS_PRINT_C: + return "Printing Cyan"; + case D90_MECHA_STATUS_PRINT_PRE_OC: + case D90_MECHA_STATUS_PRINT_OC: + return "Applying Overcoat"; + case D90_MECHA_STATUS_PRINTING_x2f: + case D90_MECHA_STATUS_PRINTING_x38: + return "Ejecting Media?"; + default: + return "Printing (Unknown)"; + } + case D90_MECHA_STATUS_INIT: + if (code[1] == D90_MECHA_STATUS_INIT_FEEDCUT) + return "Feed & Cut paper"; + else + return "Initializing"; + default: + return "Unknown"; + } +} + +const char *mitsud90_error_codes(const uint8_t *code) +{ + switch(code[0]) { + case D90_ERROR_STATUS_OK: + if (code[1] & D90_ERROR_STATUS_OK_WARMING) + return "Heating"; + else if (code[1] & D90_ERROR_STATUS_OK_COOLING) + return "Cooling Down"; + else + return "Idle"; + case D90_ERROR_STATUS_RIBBON: + switch (code[1]) { + case 0x00: + return "Ribbon exhausted"; + case 0x10: + return "Insufficient remaining ribbon"; + case 0x20: + return "Ribbon Cue Timeout"; + case 0x30: + return "Cannot Cue Ribbon"; + case 0x90: + return "No ribbon"; + default: + return "Unknown Ribbon Error"; + } + case D90_ERROR_STATUS_PAPER: + switch (code[1]) { + case 0x00: + return "No paper"; + case 0x02: + return "Paper exhausted"; + default: + return "Unknown Paper Error"; + } + case D90_ERROR_STATUS_PAP_RIB: + switch (code[1]) { + case 0x00: + return "Ribbon/Paper mismatch"; + case 0x90: + return "Ribbon/Job mismatch"; + default: + return "Unknown ribbon match error"; + } + case 0x26: + return "Illegal Ribbon"; + case 0x28: + return "Cut Bin Missing"; + case D90_ERROR_STATUS_OPEN: + switch (code[1]) { + case 0x00: + return "Printer Open during Stop"; + case 0x10: + return "Printer Open during Initialization"; + case 0x90: + return "Printer Open during Printing"; + default: + return "Unknown Door error"; + } + case 0x2f: + return "Printer turned off during printing"; + case 0x31: + return "Ink feed stop"; + case 0x32: + return "Ink Skip 1 timeout"; + case 0x33: + return "Ink Skip 2 timeout"; + case 0x34: + return "Ink Sticking"; + case 0x35: + return "Ink return stop"; + case 0x36: + return "Ink Rewind timeout"; + case 0x37: + return "Winding sensing error"; + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + return "Paper Jam"; + case 0x60: + if (code[1] == 0x20) + return "Preheat error"; + else if (code[1] == 0x04) + return "Humidity sensor error"; + else if (code[1] & 0x1f) + return "Thermistor error"; + else + return "Unknown error"; + case 0x61: + if (code[1] == 0x00) + return "Color Sensor Error"; + else if (code[1] & 0x10) + return "Matte OP Error"; + else + return "Unknown error"; + case 0x62: + return "Data Transfer error"; + case 0x63: + return "EEPROM error"; + case 0x64: + return "Flash access error"; + case 0x65: + return "FPGA configuration error"; + case 0x66: + return "Power voltage Error"; + case 0x67: + return "RFID access error"; + case 0x68: + if (code[1] == 0x00) + return "Fan Lock Error"; + else if (code[1] == 0x90) + return "MDA Error"; + else + return "Unknown error"; + case 0x69: + if (code[1] == 0x10) + return "DDR Error"; + else if (code[1] == 0x00) + return "Firmware Error"; + else + return "Unknown error"; + case 0x70: + case 0x71: + case 0x73: + case 0x75: + return "Mechanical Error (check ribbon and power cycle)"; + case 0x82: + return "USB Timeout"; + case 0x83: + return "Illegal paper size"; + case 0x84: + return "Illegal parameter"; + case 0x85: + return "Job Cancel"; + case 0x89: + return "Last Job Error"; + default: + return "Unknown"; + } +} + +static void mitsud90_dump_status(struct mitsud90_status_resp *resp) +{ + INFO("Error Status: %s (%02x %02x) -- %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + mitsud90_error_codes(resp->code), + resp->code[0], resp->code[1], + resp->unk[0], resp->unk[1], resp->unk[2], resp->unk[3], + resp->unk[4], resp->unk[5], resp->unk[6], resp->unk[7], + resp->unk[8]); + INFO("Printer Status: %s (%02x %02x)\n", + mitsud90_mecha_statuses(resp->mecha), + resp->mecha[0], resp->mecha[1]); + INFO("Temperature Status: %s\n", + mitsu70x_temperatures(resp->temp)); +} + +/* Private data structure */ +struct mitsud90_printjob { + uint8_t *databuf; + int datalen; + int copies; +}; + +struct mitsud90_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + + int type; + + /* Used in parsing.. */ + struct mitsud90_job_footer holdover; + int holdover_on; + + struct marker marker; +}; + +static int mitsud90_query_media(struct mitsud90_ctx *ctx, struct mitsud90_media_resp *resp) +{ + uint8_t cmdbuf[8]; + int ret, num; + + cmdbuf[0] = 0x1b; + cmdbuf[1] = 0x47; + cmdbuf[2] = 0x44; + cmdbuf[3] = 0x30; + cmdbuf[4] = 0; + cmdbuf[5] = 0; + cmdbuf[6] = 0x01; /* Number of commands */ + cmdbuf[7] = D90_STATUS_TYPE_MEDIA; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmdbuf, sizeof(cmdbuf)))) + return ret; + memset(resp, 0, sizeof(*resp)); + + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) resp, sizeof(*resp), &num); + + if (ret < 0) + return ret; + if (num != sizeof(*resp)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*resp)); + return 4; + } + + return CUPS_BACKEND_OK; +} + +static int mitsud90_query_status(struct mitsud90_ctx *ctx, struct mitsud90_status_resp *resp) +{ + uint8_t cmdbuf[10]; + int ret, num; + + cmdbuf[0] = 0x1b; + cmdbuf[1] = 0x47; + cmdbuf[2] = 0x44; + cmdbuf[3] = 0x30; + cmdbuf[4] = 0; + cmdbuf[5] = 0; + cmdbuf[6] = 0x03; /* Number of commands */ + cmdbuf[7] = D90_STATUS_TYPE_ERROR; + cmdbuf[8] = D90_STATUS_TYPE_MECHA; + cmdbuf[9] = D90_STATUS_TYPE_TEMP; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmdbuf, sizeof(cmdbuf)))) + return ret; + memset(resp, 0, sizeof(*resp)); + + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) resp, sizeof(*resp), &num); + + if (ret < 0) + return ret; + if (num != sizeof(*resp)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*resp)); + return 4; + } + + return CUPS_BACKEND_OK; +} + +/* Generic functions */ + +static void *mitsud90_init(void) +{ + struct mitsud90_ctx *ctx = malloc(sizeof(struct mitsud90_ctx)); + if (!ctx) { + ERROR("Memory Allocation Failure!\n"); + return NULL; + } + memset(ctx, 0, sizeof(struct mitsud90_ctx)); + + return ctx; +} + +static int mitsud90_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +{ + struct mitsud90_ctx *ctx = vctx; + struct mitsud90_media_resp resp; + + UNUSED(jobid); + + ctx->dev = dev; + ctx->endp_up = endp_up; + ctx->endp_down = endp_down; + ctx->type = type; + + if (test_mode < TEST_MODE_NOATTACH) { + if (mitsud90_query_media(ctx, &resp)) + return CUPS_BACKEND_FAILED; + } else { + resp.media.brand = 0xff; + resp.media.type = 0x0f; + resp.media.capacity = cpu_to_be16(230); + resp.media.remain = cpu_to_be16(200); + } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = mitsu70x_media_types(resp.media.brand, resp.media.type); + ctx->marker.levelmax = be16_to_cpu(resp.media.capacity); + ctx->marker.levelnow = be16_to_cpu(resp.media.remain); + + return CUPS_BACKEND_OK; +} + +static void mitsud90_cleanup_job(const void *vjob) +{ + const struct mitsud90_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); +} + +static void mitsud90_teardown(void *vctx) { + struct mitsud90_ctx *ctx = vctx; + + if (!ctx) + return; + + free(ctx); +} + +static int mitsud90_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { + struct mitsud90_ctx *ctx = vctx; + int i, remain; + struct mitsud90_job_hdr *hdr; + + struct mitsud90_printjob *job;; + + if (!ctx) + return CUPS_BACKEND_FAILED; + + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; + + /* Just allocate a worst-case buffer */ + job->datalen = 0; + job->databuf = malloc(sizeof(struct mitsud90_job_hdr) + + sizeof(struct mitsud90_plane_hdr) + + sizeof(struct mitsud90_job_footer) + + 1852*2729*3); + if (!job->databuf) { + ERROR("Memory allocation failure!\n"); + mitsud90_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; + } + + /* Make sure there's no holdover */ + if (ctx->holdover_on) { + memcpy(job->databuf, &ctx->holdover, sizeof(ctx->holdover)); + job->datalen += sizeof(ctx->holdover); + ctx->holdover_on = 0; + } + + /* Read in first header. */ + remain = sizeof(struct mitsud90_job_hdr) - job->datalen; + while (remain) { + i = read(data_fd, (job->databuf + job->datalen), remain); + if (i == 0) { + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + if (i < 0) { + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + remain -= i; + job->datalen += i; + } + + /* Sanity check header */ + hdr = (struct mitsud90_job_hdr *) job->databuf; + if (hdr->hdr[0] != 0x1b || + hdr->hdr[1] != 0x53 || + hdr->hdr[2] != 0x50 || + hdr->hdr[3] != 0x30 ) { + ERROR("Unrecognized data format (%02x%02x%02x%02x)!\n", + hdr->hdr[0], hdr->hdr[1], hdr->hdr[2], hdr->hdr[3]); + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + + /* Now read in the rest */ + remain = sizeof(struct mitsud90_plane_hdr) + be16_to_cpu(hdr->cols) * be16_to_cpu(hdr->rows) * 3; + while(remain) { + i = read(data_fd, job->databuf + job->datalen, remain); + if (i == 0) { + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + if (i < 0) { + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + job->datalen += i; + remain -= i; + } + + /* Read in the footer. Hopefully. */ + remain = sizeof(struct mitsud90_job_footer); + i = read(data_fd, job->databuf + job->datalen, remain); + if (i == 0) { + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + if (i < 0) { + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + + /* See if this is a job footer. If it is, keep, else holdover. */ + if (job->databuf[job->datalen + 0] != 0x1b || + job->databuf[job->datalen + 1] != 0x42 || + job->databuf[job->datalen + 2] != 0x51 || + job->databuf[job->datalen + 3] != 0x31) { + memcpy(&ctx->holdover, job->databuf + job->datalen, sizeof(struct mitsud90_job_footer)); + ctx->holdover_on = 1; + } else { + job->datalen += i; + ctx->holdover_on = 0; + } + + /* Sanity check */ + if (hdr->pano.pano_on) { + ERROR("Unable to handle panorama jobs yet\n"); + mitsud90_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + + *vjob = job; + + return CUPS_BACKEND_OK; +} + +static int mitsud90_main_loop(void *vctx, const void *vjob) { + struct mitsud90_ctx *ctx = vctx; + struct mitsud90_job_hdr *hdr; + struct mitsud90_status_resp resp; + uint8_t last_status[2] = {0xff, 0xff}; + + int sent; + int ret; + int copies; + + const struct mitsud90_printjob *job = vjob; + + if (!ctx) + return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; + copies = job->copies; + + hdr = (struct mitsud90_job_hdr*) job->databuf; + + INFO("Waiting for printer idle...\n"); + +top: + sent = 0; + + // XXX Figure out if printer is asleep, and wake it up if necessary. + + /* Query status, wait for idle or error out */ + do { + if (mitsud90_query_status(ctx, &resp)) + return CUPS_BACKEND_FAILED; + + if (resp.code[0] != D90_ERROR_STATUS_OK) { + ERROR("Printer reported error condition: %s (%02x %02x)\n", + mitsud90_error_codes(resp.code), resp.code[0], resp.code[1]); + return CUPS_BACKEND_STOP; + } + + if (resp.code[1] & D90_ERROR_STATUS_OK_WARMING || + resp.temp & D90_ERROR_STATUS_OK_WARMING ) { + INFO("Printer warming up\n"); + sleep(1); + continue; + } + if (resp.code[1] & D90_ERROR_STATUS_OK_COOLING || + resp.temp & D90_ERROR_STATUS_OK_COOLING) { + INFO("Printer cooling down\n"); + sleep(1); + continue; + } + + if (resp.mecha[0] != last_status[0] || + resp.mecha[1] != last_status[1]) { + INFO("Printer status: %s\n", + mitsud90_mecha_statuses(resp.mecha)); + last_status[0] = resp.mecha[0]; + last_status[1] = resp.mecha[1]; + } + + if (resp.mecha[0] == D90_MECHA_STATUS_IDLE) { + break; + // we don't have to wait until idle, just + // until we have free buffers. Don't know how + // to check this though.. XXXX + } + } while(1); + + + /* Send memory check */ + { + struct mitsud90_memcheck mem; + struct mitsud90_memcheck_resp mem_resp; + int num; + + memcpy(&mem, hdr, sizeof(mem)); + mem.hdr[0] = 0x1b; + mem.hdr[1] = 0x47; + mem.hdr[2] = 0x44; + mem.hdr[3] = 0x33; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + (uint8_t*) &mem, sizeof(mem)))) + return CUPS_BACKEND_FAILED; + + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*)&mem_resp, sizeof(mem_resp), &num); + + if (ret < 0) + return ret; + if (num != sizeof(mem_resp)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(mem_resp)); + return 4; + } + if (mem_resp.size_bad || mem_resp.mem_bad == 0xff) { + ERROR("Printer reported bad print params (%02x)\n", mem_resp.size_bad); + return CUPS_BACKEND_CANCEL; + } + if (mem_resp.mem_bad) { + ERROR("Printer buffers full, retrying!\n"); + sleep(1); + goto top; + } + } + + /* Send header */ + if ((ret = send_data(ctx->dev, ctx->endp_down, + job->databuf + sent, sizeof(*hdr)))) + return CUPS_BACKEND_FAILED; + sent += sizeof(*hdr); + + /* Send Plane header */ + if ((ret = send_data(ctx->dev, ctx->endp_down, + job->databuf + sent, sizeof(*hdr)))) + return CUPS_BACKEND_FAILED; + sent += sizeof(*hdr); + + /* Send payload + footer */ + if ((ret = send_data(ctx->dev, ctx->endp_down, + job->databuf + sent, job->datalen - sent))) + return CUPS_BACKEND_FAILED; +// sent += (job->datalen - sent); + + /* Wait for completion */ + do { + sleep(1); + + if (mitsud90_query_status(ctx, &resp)) + return CUPS_BACKEND_FAILED; + + if (resp.code[0] != D90_ERROR_STATUS_OK) { + ERROR("Printer reported error condition: %s (%02x %02x)\n", + mitsud90_error_codes(resp.code), resp.code[0], resp.code[1]); + return CUPS_BACKEND_STOP; + } + + if (resp.mecha[0] != last_status[0] || + resp.mecha[1] != last_status[1]) { + INFO("Printer status: %s\n", + mitsud90_mecha_statuses(resp.mecha)); + last_status[0] = resp.mecha[0]; + last_status[1] = resp.mecha[1]; + } + + /* Terminate when printing complete */ + if (resp.mecha[0] == D90_MECHA_STATUS_IDLE) { + break; + } + + if (fast_return && copies <= 1) { /* Copies generated by backend? */ + INFO("Fast return mode enabled.\n"); + break; + } + } while(1); + + /* Clean up */ + if (terminate) + copies = 1; + + INFO("Print complete (%d copies remaining)\n", copies - 1); + + if (copies && --copies) { + goto top; + } + + return CUPS_BACKEND_OK; +} + +static int mitsud90_query_job(struct mitsud90_ctx *ctx, uint16_t jobid, + struct mitsud90_job_resp *resp) +{ + struct mitsud90_job_query req; + int ret, num; + + req.hdr[0] = 0x1b; + req.hdr[1] = 0x47; + req.hdr[2] = 0x44; + req.hdr[3] = 0x31; + req.jobid = cpu_to_be16(jobid); + + if ((ret = send_data(ctx->dev, ctx->endp_down, + (uint8_t*) &req, sizeof(req)))) + return ret; + memset(resp, 0, sizeof(*resp)); + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) resp, sizeof(*resp), &num); + + if (ret < 0) + return ret; + if (num != sizeof(*resp)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*resp)); + return 4; + } + + return CUPS_BACKEND_OK; +} + +static int mitsud90_get_jobstatus(struct mitsud90_ctx *ctx, uint16_t jobid) +{ + struct mitsud90_job_resp resp; + + if (mitsud90_query_job(ctx, jobid, &resp)) + return CUPS_BACKEND_FAILED; + + INFO("Job Status: %04x = %02x/%02x/%04x\n", + jobid, resp.unk1, resp.unk2, be16_to_cpu(resp.unk3)); + + return CUPS_BACKEND_OK; +} + +static int mitsud90_get_media(struct mitsud90_ctx *ctx) +{ + struct mitsud90_media_resp resp; + + if (mitsud90_query_media(ctx, &resp)) + return CUPS_BACKEND_FAILED; + + INFO("Media Type: %s (%02x/%02x)\n", + mitsu70x_media_types(resp.media.brand, resp.media.type), + resp.media.brand, + resp.media.type); + INFO("Prints Remaining: %03d/%03d\n", + be16_to_cpu(resp.media.remain), + be16_to_cpu(resp.media.capacity)); + + return CUPS_BACKEND_OK; +} + +static int mitsud90_get_status(struct mitsud90_ctx *ctx) +{ + struct mitsud90_status_resp resp; + + if (mitsud90_query_status(ctx, &resp)) + return CUPS_BACKEND_FAILED; + + mitsud90_dump_status(&resp); + + return CUPS_BACKEND_OK; +} + +int mitsud90_get_info(struct mitsud90_ctx *ctx) +{ + uint8_t cmdbuf[26]; + int ret, num; + struct mitsud90_info_resp resp; + + cmdbuf[0] = 0x1b; + cmdbuf[1] = 0x47; + cmdbuf[2] = 0x44; + cmdbuf[3] = 0x30; + cmdbuf[4] = 0; + cmdbuf[5] = 0; + cmdbuf[6] = 19; /* Number of commands */ + + cmdbuf[7] = D90_STATUS_TYPE_MODEL; + cmdbuf[8] = 0x02; + cmdbuf[9] = 0x0b; + cmdbuf[10] = 0x0c; + + cmdbuf[11] = 0x0d; + cmdbuf[12] = 0x0e; + cmdbuf[13] = 0x0f; + cmdbuf[14] = 0x11; + + cmdbuf[15] = 0x13; + cmdbuf[16] = 0x1e; + cmdbuf[17] = 0x22; + cmdbuf[18] = 0x28; + + cmdbuf[19] = 0x29; + cmdbuf[20] = 0x2b; + cmdbuf[21] = 0x2c; + cmdbuf[22] = 0x65; + + cmdbuf[23] = 0x82; + cmdbuf[24] = 0x83; + cmdbuf[25] = 0x84; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmdbuf, sizeof(cmdbuf)))) + return ret; + memset(&resp, 0, sizeof(resp)); + + ret = read_data(ctx->dev, ctx->endp_up, + (uint8_t*) &resp, sizeof(resp), &num); + + if (ret < 0) + return ret; + if (num != sizeof(resp)) { + ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(resp)); + return 4; + } + + /* start dumping output */ + memset(cmdbuf, 0, sizeof(cmdbuf)); + memcpy(cmdbuf, resp.model, sizeof(resp.model)); + INFO("Model: %s\n", (char*)cmdbuf); + for (num = 0; num < 7 ; num++) { + memset(cmdbuf, 0, sizeof(cmdbuf)); + memcpy(cmdbuf, resp.fw_vers[num].version, sizeof(resp.fw_vers[num].version)); + INFO("FW Component %02d: %s (%04x)\n", + num, cmdbuf, be16_to_cpu(resp.fw_vers[num].csum)); + } + INFO("TYPE_02: %02x\n", resp.x02); + INFO("TYPE_1e: %02x\n", resp.x1e); + INFO("TYPE_22: %02x %02x\n", resp.x22[0], resp.x22[1]); + INFO("TYPE_28: %02x %02x\n", resp.x28[0], resp.x28[1]); + INFO("TYPE_29: %02x %02x %02x %02x %02x %02x %02x %02x\n", + resp.x29[0], resp.x29[1], resp.x29[2], resp.x29[3], + resp.x29[4], resp.x29[5], resp.x29[6], resp.x29[7]); + INFO("TYPE_2b: %02x %02x\n", resp.x2b[0], resp.x2b[1]); + INFO("TYPE_2c: %02x %02x\n", resp.x2c[0], resp.x2c[1]); + + INFO("TYPE_65:"); + for (num = 0; num < 50 ; num++) { + DEBUG2(" %02x", resp.x65[num]); + } + DEBUG2("\n"); + INFO("TYPE_1e: %82x\n", resp.x82); + INFO("TYPE_1e: %83x\n", resp.x83); + + /* XXX Dump iSerial, sleep time settings */ + // XXX what about resume, wait time, "cut limit" ? + + return CUPS_BACKEND_OK; +} + +static int mitsud90_dumpall(struct mitsud90_ctx *ctx) +{ + int i; + uint8_t cmdbuf[8]; + uint8_t buf[256]; + + cmdbuf[0] = 0x1b; + cmdbuf[1] = 0x47; + cmdbuf[2] = 0x44; + cmdbuf[3] = 0x30; + cmdbuf[4] = 0; + cmdbuf[5] = 0; + cmdbuf[6] = 0x01; /* Number of commands */ + + for (i = 0 ; i < 256 ; i++) { + int num, ret; + + cmdbuf[7] = i; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmdbuf, sizeof(cmdbuf)))) + return ret; + memset(buf, 0, sizeof(buf)); + + ret = read_data(ctx->dev, ctx->endp_up, + buf, sizeof(buf), &num); + + if (ret <= 0) + continue; + + if (num > 4) { + DEBUG("TYPE %02x LEN: %d (%d)\n", i, num, num - 4); + DEBUG("<--"); + for (ret = 0; ret < num ; ret ++) { + DEBUG2(" %x", buf[ret]); + } + DEBUG2("\n"); + } + } + + return CUPS_BACKEND_OK; +} + +static int mitsud90_set_iserial(struct mitsud90_ctx *ctx, uint8_t enabled) +{ + uint8_t cmdbuf[23]; + int ret, num; + + enabled = (enabled) ? 0: 0x80; + + /* Send Parameter.. */ + cmdbuf[0] = 0x1b; + cmdbuf[1] = 0x31; + cmdbuf[2] = 0x36; + cmdbuf[3] = 0x30; + cmdbuf[4] = 0x41; + cmdbuf[5] = 0xbe; + cmdbuf[6] = 0x00; + cmdbuf[7] = 0x00; + + cmdbuf[8] = 0x00; + cmdbuf[9] = 0x01; + cmdbuf[10] = 0x00; + cmdbuf[11] = 0x00; + cmdbuf[12] = 0x00; + cmdbuf[13] = 0x11; + cmdbuf[14] = 0xff; + cmdbuf[15] = 0xff; + + cmdbuf[16] = 0xff; + cmdbuf[17] = 0xfe; + cmdbuf[18] = 0xff; + cmdbuf[19] = 0xff; + cmdbuf[20] = 0xff; + cmdbuf[21] = 0xfe; + cmdbuf[22] = enabled; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmdbuf, sizeof(cmdbuf)))) + return ret; + + ret = read_data(ctx->dev, ctx->endp_up, + cmdbuf, sizeof(cmdbuf), &num); + + /* No response */ + + return ret; +} + +static int mitsud90_set_sleeptime(struct mitsud90_ctx *ctx, uint16_t time) +{ + uint8_t cmdbuf[24]; + int ret; + + /* 255 minutes max, according to RE work */ + if (time > 255) + time = 255; + + /* Send Parameter.. */ + cmdbuf[0] = 0x1b; + cmdbuf[1] = 0x31; + cmdbuf[2] = 0x36; + cmdbuf[3] = 0x30; + cmdbuf[4] = 0x41; + cmdbuf[5] = 0xbe; + cmdbuf[6] = 0x00; + cmdbuf[7] = 0x00; + + cmdbuf[8] = 0x00; + cmdbuf[9] = 0x02; + cmdbuf[10] = 0x00; + cmdbuf[11] = 0x00; + cmdbuf[12] = 0x05; + cmdbuf[13] = 0x02; + cmdbuf[14] = 0xff; + cmdbuf[15] = 0xff; + + cmdbuf[16] = 0xff; + cmdbuf[17] = 0xfd; + cmdbuf[18] = 0xff; + cmdbuf[19] = 0xff; + cmdbuf[20] = 0xfa; + cmdbuf[21] = 0xff; + cmdbuf[22] = (time >> 8) & 0xff; + cmdbuf[23] = time & 0xff; + + if ((ret = send_data(ctx->dev, ctx->endp_down, + cmdbuf, 4))) + return ret; + + /* No response */ + + return 0; +} + +static void mitsud90_cmdline(void) +{ + DEBUG("\t\t[ -i ] # Query printer info\n"); + DEBUG("\t\t[ -j jobid ] # Query job status\n"); + DEBUG("\t\t[ -k time ] # Set sleep time in minutes\n"); + DEBUG("\t\t[ -m ] # Query printer media\n"); + DEBUG("\t\t[ -s ] # Query printer status\n"); + DEBUG("\t\t[ -x 0|1 ] # Enable/disable iSerial reporting\n"); +// DEBUG("\t\t[ -Z ] # Dump all parameters\n"); +} + +static int mitsud90_cmdline_arg(void *vctx, int argc, char **argv) +{ + struct mitsud90_ctx *ctx = vctx; + int i, j = 0; + + if (!ctx) + return -1; + + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "ij:k:msx:Z")) >= 0) { + switch(i) { + GETOPT_PROCESS_GLOBAL + case 'i': + j = mitsud90_get_info(ctx); + break; + case 'j': + j = mitsud90_get_jobstatus(ctx, atoi(optarg)); + break; + case 'k': + j = mitsud90_set_sleeptime(ctx, atoi(optarg)); + break; + case 'm': + j = mitsud90_get_media(ctx); + break; + case 's': + j = mitsud90_get_status(ctx); + break; + case 'x': + j = mitsud90_set_iserial(ctx, atoi(optarg)); + break; + case 'Z': + j = mitsud90_dumpall(ctx); + break; + default: + break; /* Ignore completely */ + } + + if (j) return j; + } + + return 0; +} + +static int mitsud90_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct mitsud90_ctx *ctx = vctx; + struct mitsud90_media_resp resp; + + *markers = &ctx->marker; + *count = 1; + + if (mitsud90_query_media(ctx, &resp)) + return CUPS_BACKEND_FAILED; + + ctx->marker.levelnow = be16_to_cpu(resp.media.remain); + + return CUPS_BACKEND_OK; +} + +static const char *mitsud90_prefixes[] = { + "mitsubishi-d90dw", + // backwards compatibility + "mitsud90", + NULL +}; + +/* Exported */ +struct dyesub_backend mitsud90_backend = { + .name = "Mitsubishi CP-D90DW", + .version = "0.13", + .uri_prefixes = mitsud90_prefixes, + .cmdline_arg = mitsud90_cmdline_arg, + .cmdline_usage = mitsud90_cmdline, + .init = mitsud90_init, + .attach = mitsud90_attach, + .cleanup_job = mitsud90_cleanup_job, + .teardown = mitsud90_teardown, + .read_parse = mitsud90_read_parse, + .main_loop = mitsud90_main_loop, + .query_markers = mitsud90_query_markers, + .devices = { + { USB_VID_MITSU, USB_PID_MITSU_D90, P_MITSU_D90, NULL, "mitsubishi-d90dw"}, + { 0, 0, 0, NULL, NULL} + } +}; + +/* + Mitsubishi CP-D90DW data format + + All multi-byte values are BIG endian + + [[HEADER 1]] + + 1b 53 50 30 00 33 XX XX YY YY 64 00 00 01 00 ?? XX XX == COLS, YY XX ROWS (BE) + ?? ?? ?? ?? ?? ?? ?? ?? 00 00 00 00 00 00 00 00 <-- cut position, see below + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + QQ RR SS HH VV 00 00 00 00 00 01 00 03 II 09 7c QQ == 02 matte, 00 glossy, + 09 4c 00 00 02 58 00 0c 00 06 RR == 00 auto, 03 == fine, 02 == superfine. + SS == 00 colorcorr, 01 == none + HH/VV sharpening for Horiz/Vert, 0-8, 0 is off, 4 is normal + [pad to 512b] + + normal == rows 00 00 00 00 00 00 00 00 00 + 4x6div2 == 1226 00 02 65 01 00 00 01 00 00 + 8x6div2 == 2488 01 04 be 00 00 00 00 00 00 + + guesses based on SDK docs: + + 9x6div2 == 2728 01 05 36 00 00 00 00 00 00 + 9x6div3 == 2724 00 03 90 00 07 14 00 00 00 + 9x6div4 == 2628 00 02 97 00 05 22 00 07 ad + + from [01 00 03 03] onwards, only shows in 8x20" PANORAMA prints. Assume 2" overlap. + II == 01 02 03 (which panel # in panorama!) + [02 58] == 600, aka 2" * 300dpi? + [09 4c] == 2380 (48 less than 8 size? (trim length on ends?) + [09 7c] == 2428 (ie 8" print) + + (6x20 == 1852x6036) + (6x14 == 1852x4232) + + 3*8" panels == 2428*3=7284. -6036 = 1248. /2 = 624 (0x270) + + [[DATA PLANE HEADER]] + + 1b 5a 54 01 00 09 00 00 00 00 XX XX YY YY 00 00 + ... + [pad to 512b] + + data, BGR packed, 8bpp. No padding to 512b! + + [[FOOTER]] + + 1b 42 51 31 00 TT ## TT == secs to wait for second print + + + **************************************************** + +Comms Protocol for D90: + + [[ ERROR STATUS ]] + +-> 1b 47 44 30 00 00 01 16 +<- e4 47 44 30 00 00 00 00 00 00 00 00 00 00 00 [Normal/OK] +<- e4 47 44 30 XX 00 00 00 00 00 00 00 00 3f 37 [Error condition] + XX == 29 (printer open) + 28 (cut bin missing) +<- e4 47 44 30 21 90 00 00 01 00 00 00 00 3f 37 No ribbon + + [[ MEDIA STATUS ]] + +-> 1b 47 44 30 00 00 01 2a +<- e4 47 44 30 ff 0f 50 00 01 ae 01 9b 01 00 [Normal/OK] +<- e4 47 44 30 ff ff ff ff ff ff ff ff ff ff [Error] + + [[ MECHA STATUS ]] + +-> 1b 47 44 30 00 00 01 17 +<- e4 47 44 30 SS SS + + [[ TEMPERATURE QUERY ]] + +-> 1b 47 44 30 00 00 01 1f +<- e4 47 44 30 HH + + [[ UNKNOWN QUERY ]] +-> 1b 47 44 30 00 00 01 28 +<- e4 47 44 30 XX XX Unknown, seems to increment. + + [[ JOB STATUS QUERY ?? ]] + +-> 1b 47 44 31 00 00 JJ JJ Jobid? +<- e4 47 44 31 XX YY ZZ ZZ No idea.. sure. + + [[ COMBINED STATUS QUERIES ]] + +-> 1b 47 44 30 00 00 04 16 17 1f 2a +<- e4 47 44 30 + + MM NN 00 00 ZZ 00 00 00 00 QQ QQ [id 16, total 11] + SS SS [id 17, total 2] + HH [id 1f, total 1] + VV TT WW 00 XX XX YY YY 01 00 [id 2a, total 10] + + WW == 0x50 or 0x00 (seen, no idea what it means) + VV == Media vendor (0xff etc) + TT == Media type, 0x02/0x0f etc (see mitsu70x_media_types!) + XX XX == Media capacity, BE + YY YY == Media remain, BE + QQ QQ == 00 00 normal, 3f 37 error + MM NN == MM major err (00 if no error) NN minor error. + ZZ == 01 seen for _some_ errors. + SS SS == Mecha Status (00 == ready, 50 == printing, 80+10 == feedandcut, 80 == initializing? + HH == Temperature state. 00 is OK, 0x40 is low, 0x80 is hot. + II II == ?? + JJ JJ == ?? + + [[ WAKE UP PRINTER ]] +-> 1b 45 57 55 + + [[ GET iSERIAL ]] + +-> 1b 61 36 36 41 be 00 00 + 00 01 00 00 00 11 ff ff + ff fe ff ff ff ee +<- e4 61 36 36 41 be 00 00 + 00 01 00 00 00 11 ff ff + ff fe ff ff ff ee XX <- XX is 0x80 or 0x00. (0x80) ISERIAL OFF + + [[ GET CUT? ]] + +-> 1b 61 36 36 45 ba 00 00 + 00 01 00 00 05 07 ff ff + ff fe ff ff fa f8 +-> e4 61 36 36 45 ba 00 00 + 00 01 00 00 05 07 ff ff + ff fe ff ff fa f8 XX <- XX is 0x80 or 0x00 (0x00) CUT ON? + + [[ GET WAIT TIME ]] + +-> 1b 61 36 36 45 00 00 00 + 00 01 00 00 05 05 ff ff + ff fe ff ff fa fb +-> 1b 61 36 36 45 00 00 00 + 00 01 00 00 05 05 ff ff + ff fe ff ff fa fb XX <- XX is time in seconds. + + [[ GET RESUME? ]] + +-> 1b 61 36 36 45 ba 00 00 + 00 01 00 00 05 06 ff ff + ff fe ff ff fa f9 +-> e4 61 36 36 45 ba 00 00 + 00 01 00 00 05 06 ff ff + ff fe ff ff fa f9 XX <- XX is 0x80 or 0x00 (0x80) (OFF) + + [[ GET SLEEP TIME! ]] + +-> 1b 61 36 36 45 ba 00 00 + 00 02 00 00 05 02 ff ff + ff fd ff ff fa fd +<- e4 61 36 36 45 00 00 00 + 00 02 00 00 05 02 ff ff + ff fd ff ff fa fd XX 00 <- XX, sleep time in minutes. + + [[ SET SLEEP TIME! ]] + +-> 1b 61 36 30 45 ba 00 00 + 00 02 00 00 05 02 ff ff + ff fd ff ff fa fd XX 00 <- XX, sleep time in minutes. + + [[ SET iSERIAL ]] + +-> 1b 61 36 30 41 be 00 00 + 00 01 00 00 00 11 ff ff + ff fe ff ff ff ee XX <- XX 0x80 OFF, 0x00 ON. + + [[ SANITY CHECK PRINT ARGUMENTS / MEMTEST ]] + +-> 1b 47 44 33 00 33 07 3c 04 ca 64 00 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 00 00 00 00 00 00 00 00 + 00 00 00 04 04 00 00 00 00 00 00 00 00 00 00 00 + [[ pad to 512 ]] + + ... 07 3c onwards is the same as main payload header. + +<- e4 47 44 43 XX YY + + ... possibly the same as the D70's "memorystatus" + XX == size ok (non-zero if bad size) + YY == memory ok (non-zero or 0xff if full?) + + [[ SEND OVER HDRs and DATA ]] + + ... Print arguments: + +-> 1b 53 50 30 00 33 07 3c 04 ca 64 00 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 00 00 00 00 00 00 00 00 + 00 00 00 04 04 00 00 00 00 00 00 00 00 00 00 00 + [[ pad to 512 ]] + + ... Data transfer. Plane header: + +-> 1b 5a 54 01 00 09 00 00 00 00 07 3c 04 ca 00 00 + [[ pad to 512 ]] + +-> [[print data]] [[ padded? ]] +-> [[print data]] + +-> 1b 42 51 31 00 ZZ + + ... Footer. + ZZ == Seconds to wait for follow-up print (0x05) + + + */ diff --git a/src/cups/backend_mitsup95d.c b/src/cups/backend_mitsup95d.c index 44fbe0e..c4e0054 100644 --- a/src/cups/backend_mitsup95d.c +++ b/src/cups/backend_mitsup95d.c @@ -1,7 +1,7 @@ /* * Mitsubishi P93D/P95D Monochrome Thermal Photo Printer CUPS backend * - * (c) 2016-2017 Solomon Peachy + * (c) 2016-2018 Solomon Peachy * * Development of this backend was sponsored by: * @@ -22,11 +22,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -48,32 +49,54 @@ #define USB_PID_MITSU_P95D 0x3b10 /* Private data structure */ -struct mitsup95d_ctx { - struct libusb_device_handle *dev; - uint8_t endp_up; - uint8_t endp_down; - - int type; - - uint8_t mem_clr[4]; // 1b 5a 43 00 - int mem_clr_present; +struct mitsup95d_printjob { + uint8_t *databuf; + uint32_t datalen; uint8_t hdr[2]; // 1b 51 - uint8_t hdr1[50]; // 1b 57 20 2e ... uint8_t hdr2[50]; // 1b 57 21 2e ... uint8_t hdr3[50]; // 1b 57 22 2e ... - uint8_t hdr4[42]; // 1b 58 ... int hdr4_len; // 36 (P95) or 42 (P93) + uint8_t plane[12]; // 1b 5a 74 00 ... - uint8_t *databuf; - uint32_t datalen; + uint8_t mem_clr[4]; // 1b 5a 43 00 + int mem_clr_present; uint8_t ftr[2]; }; +struct mitsup95d_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + + int type; + + struct marker marker; +}; + +#define QUERYRESP_SIZE_MAX 9 + +static const char *mitsup93d_errors(uint8_t code) +{ + switch (code) { + case 0x6f: return "Door Open"; + case 0x50: return "No Paper"; + default: return "Unknown Error"; + } +} + +static const char *mitsup95d_errors(uint8_t code) +{ + switch (code & 0xf) { + case 3: return "Door Open"; + case 4: return "No Paper"; + default: return "Unknown Error"; + } +} static void *mitsup95d_init(void) { @@ -87,24 +110,63 @@ static void *mitsup95d_init(void) return ctx; } -static void mitsup95d_attach(void *vctx, struct libusb_device_handle *dev, +static int mitsup95d_get_status(struct mitsup95d_ctx *ctx, uint8_t *resp) +{ + uint8_t querycmd[4] = { 0x1b, 0x72, 0x00, 0x00 }; + int ret; + int num; + + /* P93D is ... special. Windows switches to this halfway through + but it seems be okay to use it everywhere */ + if (ctx->type == P_MITSU_P93D) { + querycmd[2] = 0x03; + } + + /* Query Status to sanity-check job */ + if ((ret = send_data(ctx->dev, ctx->endp_down, + querycmd, sizeof(querycmd)))) + return CUPS_BACKEND_FAILED; + ret = read_data(ctx->dev, ctx->endp_up, + resp, QUERYRESP_SIZE_MAX, &num); + + if (ret < 0) + return CUPS_BACKEND_FAILED; + if (ctx->type == P_MITSU_P95D && num != 9) { + return CUPS_BACKEND_FAILED; + } else if (ctx->type == P_MITSU_P93D && num != 8) { + return CUPS_BACKEND_FAILED; + } + return CUPS_BACKEND_OK; +} + +static int mitsup95d_attach(void *vctx, struct libusb_device_handle *dev, int type, uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct mitsup95d_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; + ctx->type = type; - device = libusb_get_device(dev); - libusb_get_device_descriptor(device, &desc); + ctx->marker.color = "#000000"; /* Ie black! */ + ctx->marker.name = "Unknown"; + ctx->marker.levelmax = -1; + ctx->marker.levelnow = -2; - ctx->type = lookup_printer_type(&mitsup95d_backend, - desc.idVendor, desc.idProduct); + return CUPS_BACKEND_OK; +} + +static void mitsup95d_cleanup_job(const void *vjob) +{ + const struct mitsup95d_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void mitsup95d_teardown(void *vctx) { @@ -113,12 +175,10 @@ static void mitsup95d_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); free(ctx); } -static int mitsup95d_read_parse(void *vctx, int data_fd) { +static int mitsup95d_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct mitsup95d_ctx *ctx = vctx; uint8_t buf[2]; /* Enough to read in any header */ uint8_t tmphdr[50]; @@ -127,35 +187,45 @@ static int mitsup95d_read_parse(void *vctx, int data_fd) { int remain; int ptr_offset; + struct mitsup95d_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } - ctx->mem_clr_present = 0; + memset(job, 0, sizeof(*job)); + + job->mem_clr_present = 0; top: i = read(data_fd, buf, sizeof(buf)); - if (i == 0) + if (i == 0) { + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } if (buf[0] != 0x1b) { ERROR("malformed data stream\n"); + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; } switch (buf[1]) { case 0x50: /* Footer */ remain = 2; - ptr = ctx->ftr; + ptr = job->ftr; break; case 0x51: /* Job Header */ remain = 2; - ptr = ctx->hdr; + ptr = job->hdr; break; case 0x57: /* Geeneral headers */ remain = sizeof(tmphdr); @@ -163,11 +233,11 @@ top: break; case 0x58: /* User Comment */ if (ctx->type == P_MITSU_P93D) - ctx->hdr4_len = 42; + job->hdr4_len = 42; else - ctx->hdr4_len = 36; - remain = ctx->hdr4_len; - ptr = ctx->hdr4; + job->hdr4_len = 36; + remain = job->hdr4_len; + ptr = job->hdr4; break; case 0x5a: /* Plane header OR printer reset */ // reset memory: 1b 5a 43 ... [len 04] @@ -178,6 +248,7 @@ top: break; default: ERROR("Unrecognized command! (%02x %02x)\n", buf[0], buf[1]); + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; } @@ -187,21 +258,25 @@ top: while (remain) { i = read(data_fd, ptr + ptr_offset, remain); - if (i == 0) + if (i == 0) { + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; ptr_offset += i; /* Handle the ambiguous 0x5a block */ if (buf[1] == 0x5a && remain == 0) { if (tmphdr[2] == 0x74) { /* plane header */ - ptr = ctx->plane; + ptr = job->plane; remain = 12 - ptr_offset; /* Finish reading */ } else if (tmphdr[2] == 0x43) { /* reset memory */ - ptr = ctx->mem_clr; - ctx->mem_clr_present = 1; + ptr = job->mem_clr; + job->mem_clr_present = 1; remain = 4 - ptr_offset; } memcpy(ptr, tmphdr, ptr_offset); @@ -213,101 +288,96 @@ top: if (tmphdr[3] != 46) { ERROR("Unexpected header chunk: %02x %02x %02x %02x\n", tmphdr[0], tmphdr[1], tmphdr[2], tmphdr[3]); + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; } switch (tmphdr[2]) { case 0x20: - ptr = ctx->hdr1; + ptr = job->hdr1; break; case 0x21: - ptr = ctx->hdr2; + ptr = job->hdr2; break; case 0x22: - ptr = ctx->hdr3; + ptr = job->hdr3; break; default: - ERROR("Unexpected header chunk: %02x %02x %02x %02x\n", + WARNING("Unexpected header chunk: %02x %02x %02x %02x\n", tmphdr[0], tmphdr[1], tmphdr[2], tmphdr[3]); } memcpy(ptr, tmphdr, sizeof(tmphdr)); - } else if (ptr == ctx->plane) { - uint16_t rows = ctx->plane[10] << 8 | ctx->plane[11]; - uint16_t cols = ctx->plane[8] << 8 | ctx->plane[9]; + } else if (ptr == job->plane) { + uint16_t rows = job->plane[10] << 8 | job->plane[11]; + uint16_t cols = job->plane[8] << 8 | job->plane[9]; remain = rows * cols; /* Allocate buffer for the payload */ - ctx->datalen = 0; - ctx->databuf = malloc(remain); - if (!ctx->databuf) { + job->datalen = 0; + job->databuf = malloc(remain); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + mitsup95d_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } /* Read it in */ while (remain) { - i = read(data_fd, ctx->databuf + ctx->datalen, remain); - if (i == 0) + i = read(data_fd, job->databuf + job->datalen, remain); + if (i == 0) { + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; - if (i < 0) + } + if (i < 0) { + mitsup95d_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } remain -= i; - ctx->datalen += i; + job->datalen += i; } - } else if (ptr == ctx->ftr) { + } else if (ptr == job->ftr) { + + /* XXX Update unknown header field to match sniffs */ + if (ctx->type == P_MITSU_P95D) { + if (job->hdr1[18] == 0x00) + job->hdr1[18] = 0x01; + } + + /* Update printjob header to reflect number of requested copies */ + if (job->hdr2[13] != 0xff) + job->hdr2[13] = copies; + + *vjob = job; return CUPS_BACKEND_OK; } goto top; } -static int mitsup95d_main_loop(void *vctx, int copies) { +static int mitsup95d_main_loop(void *vctx, const void *vjob) { struct mitsup95d_ctx *ctx = vctx; - uint8_t querycmd[4] = { 0x1b, 0x72, 0x00, 0x00 }; - uint8_t queryresp[9]; - + uint8_t queryresp[QUERYRESP_SIZE_MAX]; int ret; - int num; + + const struct mitsup95d_printjob *job = vjob; if (!ctx) return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; - /* P93D is ... special. Windows switches to this halfway through - but it seems be okay to use it everywhere */ - if (ctx->type == P_MITSU_P93D) { - querycmd[2] = 0x03; - } - - /* Update printjob header to reflect number of requested copies */ - if (ctx->hdr2[13] != 0xff) - ctx->hdr2[13] = copies; - - if (ctx->type == P_MITSU_P95D) { - /* XXX Update unknown header field to match sniffs */ - if (ctx->hdr1[18] == 0x00) - ctx->hdr1[18] = 0x01; - } INFO("Waiting for printer idle\n"); /* Query Status to make sure printer is idle */ do { - if ((ret = send_data(ctx->dev, ctx->endp_down, - querycmd, sizeof(querycmd)))) - return CUPS_BACKEND_FAILED; - ret = read_data(ctx->dev, ctx->endp_up, - queryresp, sizeof(queryresp), &num); - if (ret < 0) - return CUPS_BACKEND_FAILED; - if (ctx->type == P_MITSU_P95D && num != 9) { - return CUPS_BACKEND_FAILED; - } else if (ctx->type == P_MITSU_P93D && num != 8) { - return CUPS_BACKEND_FAILED; - } + ret = mitsup95d_get_status(ctx, queryresp); + if (ret) + return ret; if (ctx->type == P_MITSU_P95D) { - if (queryresp[5] & 0x40) { - ERROR("Printer error %02x\n", queryresp[5]); // XXX decode + if (queryresp[6] & 0x40) { + INFO("Printer Status: %s (%02x)\n", mitsup95d_errors(queryresp[6]), queryresp[6]); return CUPS_BACKEND_STOP; } if (queryresp[5] == 0x00) @@ -327,62 +397,47 @@ static int mitsup95d_main_loop(void *vctx, int copies) { INFO("Sending print job\n"); /* Send over Memory Clear, if present */ - if (ctx->mem_clr_present) { + if (job->mem_clr_present) { if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->mem_clr, sizeof(ctx->mem_clr)))) + job->mem_clr, sizeof(job->mem_clr)))) return CUPS_BACKEND_FAILED; } /* Send Job Start */ if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->hdr, sizeof(ctx->hdr)))) + job->hdr, sizeof(job->hdr)))) return CUPS_BACKEND_FAILED; /* Send over headers */ if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->hdr1, sizeof(ctx->hdr1)))) + job->hdr1, sizeof(job->hdr1)))) return CUPS_BACKEND_FAILED; if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->hdr2, sizeof(ctx->hdr2)))) + job->hdr2, sizeof(job->hdr2)))) return CUPS_BACKEND_FAILED; if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->hdr3, sizeof(ctx->hdr3)))) + job->hdr3, sizeof(job->hdr3)))) return CUPS_BACKEND_FAILED; if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->hdr4, ctx->hdr4_len))) + job->hdr4, job->hdr4_len))) return CUPS_BACKEND_FAILED; /* Send plane header and image data */ if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->plane, sizeof(ctx->plane)))) + job->plane, sizeof(job->plane)))) return CUPS_BACKEND_FAILED; if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf, ctx->datalen))) + job->databuf, job->datalen))) return CUPS_BACKEND_FAILED; /* Query Status to sanity-check job */ - if ((ret = send_data(ctx->dev, ctx->endp_down, - querycmd, sizeof(querycmd)))) - return CUPS_BACKEND_FAILED; - ret = read_data(ctx->dev, ctx->endp_up, - queryresp, sizeof(queryresp), &num); - - if (ret < 0) - return CUPS_BACKEND_FAILED; - if (ctx->type == P_MITSU_P95D && num != 9) { - return CUPS_BACKEND_FAILED; - } else if (ctx->type == P_MITSU_P93D && num != 8) { - return CUPS_BACKEND_FAILED; - } - - if (queryresp[5] & 0x40) { - ERROR("Printer error %02x\n", queryresp[5]); // XXX decode - return CUPS_BACKEND_STOP; - } + ret = mitsup95d_get_status(ctx, queryresp); + if (ret) + return ret; if (ctx->type == P_MITSU_P95D) { - if (queryresp[5] & 0x40) { - ERROR("Printer error %02x\n", queryresp[5]); // XXX decode + if (queryresp[6] & 0x40) { + INFO("Printer Status: %s (%02x)\n", mitsup95d_errors(queryresp[6]), queryresp[6]); return CUPS_BACKEND_STOP; } if (queryresp[5] != 0x00) { @@ -391,7 +446,7 @@ static int mitsup95d_main_loop(void *vctx, int copies) { } } else { if (queryresp[6] == 0x45) { - ERROR("Printer error %02x\n", queryresp[7]); + INFO("Printer Status: %s (%02x)\n", mitsup93d_errors(queryresp[7]), queryresp[7]); return CUPS_BACKEND_STOP; } if (queryresp[6] != 0x30) { @@ -402,7 +457,7 @@ static int mitsup95d_main_loop(void *vctx, int copies) { /* Send over Footer */ if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->ftr, sizeof(ctx->ftr)))) + job->ftr, sizeof(job->ftr)))) return CUPS_BACKEND_FAILED; INFO("Waiting for completion\n"); @@ -412,23 +467,13 @@ static int mitsup95d_main_loop(void *vctx, int copies) { sleep(1); /* Query Status */ - if ((ret = send_data(ctx->dev, ctx->endp_down, - querycmd, sizeof(querycmd)))) - return CUPS_BACKEND_FAILED; - ret = read_data(ctx->dev, ctx->endp_up, - queryresp, sizeof(queryresp), &num); - - if (ret < 0) - return CUPS_BACKEND_FAILED; - if (ctx->type == P_MITSU_P95D && num != 9) { - return CUPS_BACKEND_FAILED; - } else if (ctx->type == P_MITSU_P93D && num != 8) { - return CUPS_BACKEND_FAILED; - } + ret = mitsup95d_get_status(ctx, queryresp); + if (ret) + return ret; if (ctx->type == P_MITSU_P95D) { - if (queryresp[5] & 0x40) { - ERROR("Printer error %02x\n", queryresp[5]); // XXX decode + if (queryresp[6] & 0x40) { + INFO("Printer Status: %s (%02x)\n", mitsup95d_errors(queryresp[6]), queryresp[6]); return CUPS_BACKEND_STOP; } if (queryresp[5] == 0x00) @@ -442,7 +487,7 @@ static int mitsup95d_main_loop(void *vctx, int copies) { } } else { if (queryresp[6] == 0x45) { - ERROR("Printer error %02x\n", queryresp[7]); + INFO("Printer Status: %s (%02x)\n", mitsup93d_errors(queryresp[7]), queryresp[7]); return CUPS_BACKEND_STOP; } if (queryresp[6] == 0x30) @@ -460,17 +505,57 @@ static int mitsup95d_main_loop(void *vctx, int copies) { return CUPS_BACKEND_OK; } +static int mitsup95d_dump_status(struct mitsup95d_ctx *ctx) +{ + uint8_t queryresp[QUERYRESP_SIZE_MAX]; + int ret; + + ret = mitsup95d_get_status(ctx, queryresp); + if (ret) + return ret; + + if (ctx->type == P_MITSU_P95D) { + if (queryresp[6] & 0x40) { + INFO("Printer Status: %s (%02x)\n", mitsup95d_errors(queryresp[6]), queryresp[6]); + } else if (queryresp[5] == 0x00) { + INFO("Printer Status: Idle\n"); + } else if (queryresp[5] == 0x02 && queryresp[7] > 0) { + INFO("Printer Status: Printing (%d) copies remaining\n", queryresp[7]); + } + } else { + if (queryresp[6] == 0x45) { + INFO("Printer Status: %s (%02x)\n", mitsup93d_errors(queryresp[7]), queryresp[7]); + } else if (queryresp[6] == 0x30) { + INFO("Printer Status: Idle\n"); + } else if (queryresp[6] == 0x43 && queryresp[7] > 0) { + INFO("Printer Status: Printing (%d) copies remaining\n", queryresp[7]); + } + } + + return CUPS_BACKEND_OK; +} + +static void mitsup95d_cmdline(void) +{ + DEBUG("\t\t[ -s ] # Query status\n"); +} + static int mitsup95d_cmdline_arg(void *vctx, int argc, char **argv) { - struct canonselphy_ctx *ctx = vctx; + struct mitsup95d_ctx *ctx = vctx; int i, j = 0; if (!ctx) return -1; - while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL)) >= 0) { + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "s")) >= 0) { switch(i) { GETOPT_PROCESS_GLOBAL + case 's': + j = mitsup95d_dump_status(ctx); + break; + default: + break; /* Ignore completely */ } if (j) return j; @@ -479,21 +564,58 @@ static int mitsup95d_cmdline_arg(void *vctx, int argc, char **argv) return 0; } +static int mitsup95d_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct mitsup95d_ctx *ctx = vctx; + uint8_t queryresp[QUERYRESP_SIZE_MAX]; + + if (mitsup95d_get_status(ctx, queryresp)) + return CUPS_BACKEND_FAILED; + + ctx->marker.levelnow = -3; + + if (ctx->type == P_MITSU_P95D) { + if (queryresp[6] & 0x40) { + ctx->marker.levelnow = 0; + } + } else { + if (queryresp[6] == 0x45) { + ctx->marker.levelnow = 0; + } + } + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + +static const char *mitsup95d_prefixes[] = { + "mitsup9x", // Family driver name + "mitsubishi-p95d", "mitsubishi-p93d", + // backwards compatibility + "mitsup95d", "mitsup93d", + NULL +}; + /* Exported */ struct dyesub_backend mitsup95d_backend = { .name = "Mitsubishi P93D/P95D", - .version = "0.05", - .uri_prefix = "mitsup95d", + .version = "0.11", + .uri_prefixes = mitsup95d_prefixes, .cmdline_arg = mitsup95d_cmdline_arg, + .cmdline_usage = mitsup95d_cmdline, .init = mitsup95d_init, .attach = mitsup95d_attach, .teardown = mitsup95d_teardown, + .cleanup_job = mitsup95d_cleanup_job, .read_parse = mitsup95d_read_parse, .main_loop = mitsup95d_main_loop, + .query_markers = mitsup95d_query_markers, .devices = { - { USB_VID_MITSU, USB_PID_MITSU_P93D, P_MITSU_P93D, ""}, - { USB_VID_MITSU, USB_PID_MITSU_P95D, P_MITSU_P95D, ""}, - { 0, 0, 0, ""} + { USB_VID_MITSU, USB_PID_MITSU_P93D, P_MITSU_P93D, NULL, "mitsubishi-p93d"}, + { USB_VID_MITSU, USB_PID_MITSU_P95D, P_MITSU_P95D, NULL, "mitsubishi-p95d"}, + { 0, 0, 0, NULL, NULL} } }; @@ -641,12 +763,14 @@ struct dyesub_backend mitsup95d_backend = { STATUS query -> 1b 72 00 00 - <- e4 72 00 00 04 XX 00 YY 00 + <- e4 72 00 00 04 XX ZZ YY 00 YY == remaining copies XX == Status? 00 == Idle 02 == Printing + ZZ == Error! + 00 == None 43 == Door open 44 == No Paper 4? == "Button" diff --git a/src/cups/backend_shinkos1245.c b/src/cups/backend_shinkos1245.c index 871dc32..cd0d34c 100644 --- a/src/cups/backend_shinkos1245.c +++ b/src/cups/backend_shinkos1245.c @@ -1,7 +1,7 @@ /* * Shinko/Sinfonia CHC-S1245 CUPS backend -- libusb-1.0 version * - * (c) 2015-2017 Solomon Peachy + * (c) 2015-2018 Solomon Peachy * * Low-level documentation was provided by Sinfonia, Inc. Thank you! * @@ -20,11 +20,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -395,12 +396,19 @@ struct shinkos1245_resp_matte { uint8_t code; uint8_t mode; int8_t level; - uint8_t reserved[3]; + uint8_t reserved[4]; } __attribute__((packed)); #define MATTE_MODE_MATTE 0x00 /* Private data structure */ +struct shinkos1245_printjob { + uint8_t *databuf; + int datalen; + + int copies; +}; + struct shinkos1245_ctx { struct libusb_device_handle *dev; uint8_t endp_up; @@ -415,8 +423,8 @@ struct shinkos1245_ctx { int num_medias; int media_8x12; - uint8_t *databuf; - int datalen; + struct marker marker; + int tonecurve; }; @@ -497,6 +505,7 @@ static int shinkos1245_get_media(struct shinkos1245_ctx *ctx) shinkos1245_fill_hdr(&cmd.hdr); memset(cmd.pad, 0, sizeof(cmd.pad)); + ctx->media_8x12 = 0; for (i = 1 ; i <= 3 ; i++) { cmd.cmd[0] = 0x0a | (i << 4); @@ -512,20 +521,21 @@ static int shinkos1245_get_media(struct shinkos1245_ctx *ctx) return -99; } - if (resp.count > NUM_MEDIAS) - resp.count = NUM_MEDIAS; - /* Store media info */ - for (j = 0; j < resp.count ; j++) { + for (j = 0; j < NUM_MEDIAS && ctx->num_medias < 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 (ctx->medias[i].rows >= 3636) + ctx->media_8x12 = 1; } - if (resp.count < 5) + /* Once we've parsed them all.. we're done */ + if (ctx->num_medias == resp.count) break; } return ret; @@ -566,7 +576,7 @@ static int shinkos1245_set_printerid(struct shinkos1245_ctx *ctx, for (i = 0 ; i < (int)sizeof(cmd.data) ; i++) { if (*id) - cmd.data[i] = (uint8_t) *id; + cmd.data[i] = (uint8_t) *id++; else cmd.data[i] = ' '; } @@ -869,7 +879,8 @@ static char* shinkos1245_tonecurves(int type, int table) } } -static void shinkos1245_dump_status(struct shinkos1245_resp_status *sts) +static void shinkos1245_dump_status(struct shinkos1245_ctx *ctx, + struct shinkos1245_resp_status *sts) { char *detail; switch (sts->print_status) { @@ -895,8 +906,8 @@ static void shinkos1245_dump_status(struct shinkos1245_resp_status *sts) INFO("\tLifetime : %u\n", be32_to_cpu(sts->counters.lifetime)); INFO("\tThermal Head : %u\n", be32_to_cpu(sts->counters.maint)); INFO("\tMedia : %u\n", be32_to_cpu(sts->counters.media)); + INFO("\tRemaining : %u\n", ctx->marker.levelmax - be32_to_cpu(sts->counters.media)); INFO("\tCutter : %u\n", be32_to_cpu(sts->counters.cutter)); - INFO("Versions:\n"); INFO("\tUSB Boot : %u\n", sts->counters.ver_boot); INFO("\tUSB Control : %u\n", sts->counters.ver_ctrl); @@ -934,10 +945,12 @@ static void shinkos1245_dump_status(struct shinkos1245_resp_status *sts) } static void shinkos1245_dump_media(struct shinkos1245_mediadesc *medias, + int media_8x12, int count) { int i; + INFO("Loaded media type: %s\n", media_8x12 ? "8x12" : "8x10"); INFO("Supported print sizes: %d\n", count); for (i = 0 ; i < count ; i++) { @@ -945,7 +958,7 @@ static void shinkos1245_dump_media(struct shinkos1245_mediadesc *medias, medias[i].print_type, medias[i].columns, medias[i].rows, - medias[i].code, medias[i].type); + medias[i].type, medias[i].print_type); } } @@ -1020,6 +1033,7 @@ static int get_tonecurve(struct shinkos1245_ctx *ctx, int type, int table, char if (ret < 0) goto done; ptr += num; + remaining -= num; } /* Issue a tone_end */ @@ -1143,6 +1157,7 @@ static int set_tonecurve(struct shinkos1245_ctx *ctx, int type, int table, char if (ret < 0) goto done; ptr += num; + remaining -= num; } /* Issue a tone_end */ @@ -1214,7 +1229,7 @@ int shinkos1245_cmdline_arg(void *vctx, int argc, char **argv) case 'm': j = shinkos1245_get_media(ctx); if (!j) - shinkos1245_dump_media(ctx->medias, ctx->num_medias); + shinkos1245_dump_media(ctx->medias, ctx->media_8x12, ctx->num_medias); break; case 'R': j = shinkos1245_reset(ctx); @@ -1223,7 +1238,7 @@ int shinkos1245_cmdline_arg(void *vctx, int argc, char **argv) struct shinkos1245_resp_status sts; j = shinkos1245_get_status(ctx, &sts); if (!j) - shinkos1245_dump_status(&sts); + shinkos1245_dump_status(ctx, &sts); break; } case 'u': { @@ -1267,29 +1282,54 @@ static void *shinkos1245_init(void) return ctx; } -static void shinkos1245_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int shinkos1245_attach(void *vctx, struct libusb_device_handle *dev, int type, + 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); + ctx->type = type; /* Ensure jobid is sane */ ctx->jobid = jobid & 0x7f; if (!ctx->jobid) ctx->jobid++; + + if (test_mode < TEST_MODE_NOATTACH) { + /* Query Media */ + if (shinkos1245_get_media(ctx)) + return CUPS_BACKEND_FAILED; + if (!ctx->num_medias) { + ERROR("Media Query Error\n"); + return CUPS_BACKEND_FAILED; + } + } else { + int media_code = 1; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")); + + ctx->media_8x12 = media_code; + ctx->num_medias = 0; + } + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = ctx->media_8x12 ? "8x12" : "8x10"; + ctx->marker.levelmax = ctx->media_8x12 ? 230 : 280; + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; } +static void shinkos1245_cleanup_job(const void *vjob) +{ + const struct shinkos1245_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); +} static void shinkos1245_teardown(void *vctx) { struct shinkos1245_ctx *ctx = vctx; @@ -1297,31 +1337,43 @@ static void shinkos1245_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); - free(ctx); } -static int shinkos1245_read_parse(void *vctx, int data_fd) { +static int shinkos1245_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct shinkos1245_ctx *ctx = vctx; int ret; uint8_t tmpbuf[4]; + struct shinkos1245_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; + /* Read in then validate header */ ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); - if (ret < 0) + if (ret < 0) { + shinkos1245_cleanup_job(job); return ret; - if (ret < 0 || ret != sizeof(ctx->hdr)) + } + if (ret != sizeof(ctx->hdr)) { + shinkos1245_cleanup_job(job); 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"); + shinkos1245_cleanup_job(job); return CUPS_BACKEND_CANCEL; } @@ -1329,6 +1381,7 @@ static int shinkos1245_read_parse(void *vctx, int data_fd) { if(ctx->hdr.model != 1245) { ERROR("Unrecognized printer (%u)!\n", ctx->hdr.model); + shinkos1245_cleanup_job(job); return CUPS_BACKEND_CANCEL; } @@ -1343,27 +1396,24 @@ static int shinkos1245_read_parse(void *vctx, int data_fd) { 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) { + job->datalen = ctx->hdr.rows * ctx->hdr.columns * 3; + job->databuf = malloc(job->datalen); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + shinkos1245_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } { - int remain = ctx->datalen; - uint8_t *ptr = ctx->databuf; + int remain = job->datalen; + uint8_t *ptr = job->databuf; do { ret = read(data_fd, ptr, remain); if (ret < 0) { ERROR("Read failed (%d/%d/%d)\n", - ret, remain, ctx->datalen); + ret, remain, job->datalen); perror("ERROR: Read failed"); + shinkos1245_cleanup_job(job); return ret; } ptr += ret; @@ -1377,6 +1427,7 @@ static int shinkos1245_read_parse(void *vctx, int data_fd) { ERROR("Read failed (%d/%d/%d)\n", ret, 4, 4); perror("ERROR: Read failed"); + shinkos1245_cleanup_job(job); return ret; } if (tmpbuf[0] != 0x04 || @@ -1384,29 +1435,30 @@ static int shinkos1245_read_parse(void *vctx, int data_fd) { tmpbuf[2] != 0x02 || tmpbuf[3] != 0x01) { ERROR("Unrecognized footer data format!\n"); + shinkos1245_cleanup_job(job); return CUPS_BACKEND_FAILED; } + *vjob = job; return CUPS_BACKEND_OK; } -static int shinkos1245_main_loop(void *vctx, int copies) { +static int shinkos1245_main_loop(void *vctx, const void *vjob) { struct shinkos1245_ctx *ctx = vctx; int i, num, last_state = -1, state = S_IDLE; struct shinkos1245_resp_status status1, status2; - /* Query Media information if necessary */ - if (!ctx->num_medias) - shinkos1245_get_media(ctx); - if (!ctx->num_medias) { - ERROR("Media Query Error\n"); + const struct shinkos1245_printjob *job = vjob; + + if (!ctx) return CUPS_BACKEND_FAILED; - } + if (!job) + return CUPS_BACKEND_FAILED; + + copies = job->copies; + /* Make sure print size is supported */ for (i = 0 ; i < ctx->num_medias ; i++) { - if (ctx->medias[i].rows >= 3636) - ctx->media_8x12 = 1; - if (ctx->hdr.media == ctx->medias[i].code && ctx->hdr.method == ctx->medias[i].print_type && ctx->hdr.rows == ctx->medias[i].rows && @@ -1422,13 +1474,6 @@ static int shinkos1245_main_loop(void *vctx, int copies) { if (copies > 9999) // XXX test against remaining media? copies = 9999; - /* Tell CUPS about the consumables we report */ - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", ctx->media_8x12? "8x12" : "8x10"); - ATTR("marker-types=ribbonWax\n"); - top: if (state != last_state) { if (dyesub_debug) @@ -1452,14 +1497,6 @@ top: if (status1.state.status1 == STATE_STATUS1_ERROR) goto printer_error; - /* Work out the remaining media percentage */ - { - int remain = ctx->media_8x12 ? 230 : 280; - - remain = (remain - be32_to_cpu(status1.counters.media)) * 100 / remain; - ATTR("marker-levels=%d\n", remain); - } - last_state = state; fflush(stderr); @@ -1515,7 +1552,7 @@ top: if (i < 0) goto printer_error; if (i > 0) { - INFO("Can't set matte intensity when printing in progres...\n"); + INFO("Can't set matte intensity when printing in progress...\n"); state = S_IDLE; sleep(1); break; @@ -1561,7 +1598,7 @@ top: /* Send over data */ INFO("Sending image data to printer\n"); if ((i = send_data(ctx->dev, ctx->endp_down, - ctx->databuf, ctx->datalen))) + job->databuf, job->datalen))) return CUPS_BACKEND_FAILED; INFO("Waiting for printer to acknowledge completion\n"); @@ -1588,11 +1625,6 @@ top: INFO("Print complete\n"); - if (copies && --copies) { - state = S_IDLE; - goto top; - } - return CUPS_BACKEND_OK; printer_error: @@ -1634,25 +1666,53 @@ static int shinkos1245_query_serno(struct libusb_device_handle *dev, uint8_t end return CUPS_BACKEND_OK; } +static int shinkos1245_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct shinkos1245_ctx *ctx = vctx; + struct shinkos1245_resp_status status; + + /* Query status */ + if (shinkos1245_get_status(ctx, &status)) + return CUPS_BACKEND_FAILED; + + ctx->marker.levelnow = ctx->marker.levelmax - be32_to_cpu(status.counters.media); + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + /* Exported */ #define USB_VID_SHINKO 0x10CE #define USB_PID_SHINKO_S1245 0x0007 +static const char *shinkos1245_prefixes[] = { + "shinko-chcs1245", + // extra + "sinfonia-chcs1245", + // backwards-compatibility + "shinkos1245", + NULL +}; + struct dyesub_backend shinkos1245_backend = { - .name = "Shinko/Sinfonia CHC-S1245", - .version = "0.13WIP", - .uri_prefix = "shinkos1245", + .name = "Shinko/Sinfonia CHC-S1245/E1", + .version = "0.26", + .uri_prefixes = shinkos1245_prefixes, .cmdline_usage = shinkos1245_cmdline, .cmdline_arg = shinkos1245_cmdline_arg, .init = shinkos1245_init, .attach = shinkos1245_attach, .teardown = shinkos1245_teardown, + .cleanup_job = shinkos1245_cleanup_job, .read_parse = shinkos1245_read_parse, .main_loop = shinkos1245_main_loop, .query_serno = shinkos1245_query_serno, + .query_markers = shinkos1245_query_markers, .devices = { - { USB_VID_SHINKO, USB_PID_SHINKO_S1245, P_SHINKO_S1245, ""}, - { 0, 0, 0, ""} + { USB_VID_SHINKO, USB_PID_SHINKO_S1245, P_SHINKO_S1245, NULL, "shinko-chcs1245"}, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_shinkos2145.c b/src/cups/backend_shinkos2145.c index c270d3c..749b59a 100644 --- a/src/cups/backend_shinkos2145.c +++ b/src/cups/backend_shinkos2145.c @@ -1,7 +1,7 @@ /* * Shinko/Sinfonia CHC-S2145 CUPS backend -- libusb-1.0 version * - * (c) 2013-2016 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * Development of this backend was sponsored by: * @@ -22,11 +22,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -90,25 +91,6 @@ struct s2145_printjob_hdr { uint32_t unk21; } __attribute__((packed)); -/* Private data structure */ -struct shinkos2145_ctx { - struct libusb_device_handle *dev; - uint8_t endp_up; - uint8_t endp_down; - int type; - - uint8_t jobid; - - struct s2145_printjob_hdr hdr; - - uint8_t *databuf; - int datalen; - - uint16_t last_donor; - uint16_t last_remain; - uint16_t media_prints; -}; - /* Structs for printer */ struct s2145_cmd_hdr { uint16_t cmd; @@ -163,7 +145,7 @@ static char *cmd_names(uint16_t v) { default: return "Unknown Command"; } -}; +} struct s2145_print_cmd { struct s2145_cmd_hdr hdr; @@ -802,6 +784,29 @@ struct s2145_getunique_resp { uint8_t data[24]; /* Not necessarily all used. */ } __attribute__((packed)); +/* Private data structure */ +struct shinkos2145_printjob { + struct s2145_printjob_hdr hdr; + + uint8_t *databuf; + int datalen; + int copies; +}; + +struct shinkos2145_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + + uint8_t jobid; + + int type; + + struct s2145_mediainfo_resp media; + struct marker marker; + int media_code; +}; + #define READBACK_LEN 128 /* Needs to be larger than largest response hdr */ #define CMDBUF_LEN sizeof(struct s2145_print_cmd) @@ -972,27 +977,10 @@ static int get_errorlog(struct shinkos2145_ctx *ctx) return 0; } -static int get_mediainfo(struct shinkos2145_ctx *ctx) +static void dump_mediainfo(struct s2145_mediainfo_resp *resp) { - struct s2145_cmd_hdr cmd; - struct s2145_mediainfo_resp *resp = (struct s2145_mediainfo_resp *) rdbuf; - int ret, num = 0; int i; - cmd.cmd = cpu_to_le16(S2145_CMD_MEDIAINFO); - cmd.len = cpu_to_le16(0); - - if ((ret = s2145_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 s2145_mediainfo_resp) - sizeof(struct s2145_status_hdr))) - return -2; - INFO("Supported Media Information: %u entries:\n", resp->count); for (i = 0 ; i < resp->count ; i++) { INFO(" %02d: C 0x%02x (%s), %04ux%04u, M 0x%02x (%s), P 0x%02x (%s)\n", i, @@ -1002,7 +990,6 @@ static int get_mediainfo(struct shinkos2145_ctx *ctx) resp->items[i].media_type, media_types(resp->items[i].media_type), resp->items[i].print_type, print_methods(resp->items[i].print_type)); } - return 0; } static int get_user_string(struct shinkos2145_ctx *ctx) @@ -1215,8 +1202,14 @@ static int get_tonecurve(struct shinkos2145_ctx *ctx, int type, char *fname) /* Byteswap appropriately */ curves[i] = cpu_to_be16(le16_to_cpu(curves[i])); } - write(tc_fd, curves, UPDATE_SIZE * sizeof(uint16_t)); + ret = write(tc_fd, curves, UPDATE_SIZE * sizeof(uint16_t)); + if (ret < 0) + ERROR("Can't write curve file\n"); + else + ret = 0; + close(tc_fd); + } done: @@ -1350,7 +1343,7 @@ int shinkos2145_cmdline_arg(void *vctx, int argc, char **argv) j = set_tonecurve(ctx, TONECURVE_CURRENT, optarg); break; case 'm': - j = get_mediainfo(ctx); + dump_mediainfo(&ctx->media); break; case 'r': j = reset_curve(ctx, RESET_USER_CURVE); @@ -1394,30 +1387,73 @@ static void *shinkos2145_init(void) return ctx; } -static void shinkos2145_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int shinkos2145_attach(void *vctx, struct libusb_device_handle *dev, int type, + 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; + int i; 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); + ctx->type = type; /* Ensure jobid is sane */ ctx->jobid = (jobid & 0x7f); if (!ctx->jobid) ctx->jobid++; - /* Initialize donor */ - ctx->last_donor = ctx->last_remain = ctx->media_prints = 65535; + int media_prints = 65536; + if (test_mode < TEST_MODE_NOATTACH) { + /* Query Media */ + struct s2145_cmd_hdr cmd; + struct s2145_mediainfo_resp *resp = (struct s2145_mediainfo_resp *) rdbuf; + int num = 0; + + cmd.cmd = cpu_to_le16(S2145_CMD_MEDIAINFO); + cmd.len = cpu_to_le16(0); + + if (s2145_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return CUPS_BACKEND_FAILED; + } + memcpy(&ctx->media, resp, sizeof(ctx->media)); + + /* Figure out the media type... */ + for (i = 0 ; i < ctx->media.count ; i++) { + if (print_counts(ctx->media.items[i].code) < media_prints) { + media_prints = print_counts(ctx->media.items[i].code); + ctx->media_code = ctx->media.items[i].code; + } + } + } else { + int media_code = PRINT_MEDIA_6x9; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")); + + media_prints = 680; + ctx->media_code = media_code; + } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = print_sizes(ctx->media_code); + ctx->marker.levelmax = media_prints; + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void shinkos2145_cleanup_job(const void *vjob) +{ + const struct shinkos2145_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void shinkos2145_teardown(void *vctx) { @@ -1426,64 +1462,66 @@ static void shinkos2145_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); - free(ctx); } -static int shinkos2145_read_parse(void *vctx, int data_fd) { +static int shinkos2145_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct shinkos2145_ctx *ctx = vctx; int ret; uint8_t tmpbuf[4]; + struct shinkos2145_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; // XXX hdr.copies + /* Read in then validate header */ - ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); - if (ret < 0 || ret != sizeof(ctx->hdr)) { + ret = read(data_fd, &job->hdr, sizeof(job->hdr)); + if (ret < 0 || ret != sizeof(job->hdr)) { if (ret == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d/%d)\n", - ret, 0, (int)sizeof(ctx->hdr)); + ret, 0, (int)sizeof(job->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) { + if (le32_to_cpu(job->hdr.len1) != 0x10 || + le32_to_cpu(job->hdr.len2) != 0x64 || + le32_to_cpu(job->hdr.dpi) != 300) { ERROR("Unrecognized header data format!\n"); return CUPS_BACKEND_CANCEL; } - if (le32_to_cpu(ctx->hdr.model) != 2145) { - ERROR("Unrecognized printer (%u)!\n", le32_to_cpu(ctx->hdr.model)); + if (le32_to_cpu(job->hdr.model) != 2145) { + ERROR("Unrecognized printer (%u)!\n", le32_to_cpu(job->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) { + job->datalen = le32_to_cpu(job->hdr.rows) * le32_to_cpu(job->hdr.columns) * 3; + job->databuf = malloc(job->datalen); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + return CUPS_BACKEND_RETRY_CURRENT; } { - int remain = ctx->datalen; - uint8_t *ptr = ctx->databuf; + int remain = job->datalen; + uint8_t *ptr = job->databuf; do { ret = read(data_fd, ptr, remain); if (ret < 0) { ERROR("Read failed (%d/%d/%d)\n", - ret, remain, ctx->datalen); + ret, remain, job->datalen); perror("ERROR: Read failed"); return ret; } @@ -1508,10 +1546,12 @@ static int shinkos2145_read_parse(void *vctx, int data_fd) { return CUPS_BACKEND_FAILED; } + *vjob = job; + return CUPS_BACKEND_OK; } -static int shinkos2145_main_loop(void *vctx, int copies) { +static int shinkos2145_main_loop(void *vctx, const void *vjob) { struct shinkos2145_ctx *ctx = vctx; int ret, num; @@ -1523,49 +1563,22 @@ static int shinkos2145_main_loop(void *vctx, int copies) { struct s2145_cmd_hdr *cmd = (struct s2145_cmd_hdr *) cmdbuf;; struct s2145_print_cmd *print = (struct s2145_print_cmd *) cmdbuf; struct s2145_status_resp *sts = (struct s2145_status_resp *) rdbuf; - struct s2145_mediainfo_resp *media = (struct s2145_mediainfo_resp *) rdbuf; - /* Send Media Query */ - memset(cmdbuf, 0, CMDBUF_LEN); - cmd->cmd = cpu_to_le16(S2145_CMD_MEDIAINFO); - cmd->len = cpu_to_le16(0); - - if ((ret = s2145_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 s2145_mediainfo_resp) - sizeof(struct s2145_status_hdr))) - return CUPS_BACKEND_FAILED; + struct shinkos2145_printjob *job = (struct shinkos2145_printjob*) vjob; /* Validate print sizes */ - for (i = 0; i < media->count ; i++) { - /* Figure out the media type... */ - int media_prints = print_counts(media->items[i].code); - if (media_prints < ctx->media_prints) - ctx->media_prints = media_prints; - + for (i = 0; i < ctx->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)) && - media->items[i].print_type == le32_to_cpu(ctx->hdr.method)) + if (le16_to_cpu(ctx->media.items[i].columns) == cpu_to_le16(le32_to_cpu(job->hdr.columns)) && + le16_to_cpu(ctx->media.items[i].rows) == cpu_to_le16(le32_to_cpu(job->hdr.rows)) && + ctx->media.items[i].print_type == le32_to_cpu(job->hdr.method)) break; } - if (i == media->count) { + if (i == ctx->media.count) { ERROR("Incorrect media loaded for print!\n"); return CUPS_BACKEND_HOLD; } - /* Tell CUPS about the consumables we report */ - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='Color'\n"); - ATTR("marker-types=ribbonWax\n"); - // XXX check copies against remaining media! top: @@ -1588,23 +1601,14 @@ top: } if (memcmp(rdbuf, rdbuf2, READBACK_LEN)) { - uint16_t donor, remain; - memcpy(rdbuf2, rdbuf, READBACK_LEN); INFO("Printer Status: 0x%02x (%s)\n", sts->hdr.status, status_str(sts->hdr.status)); - /* Guessimate a percentage for the remaining media */ - donor = le32_to_cpu(sts->count_ribbon_left) * 100 / ctx->media_prints; - if (donor != ctx->last_donor) { - ctx->last_donor = donor; - ATTR("marker-levels=%d\n", donor); - } - remain = le32_to_cpu(sts->count_ribbon_left); - if (remain != ctx->last_remain) { - ctx->last_remain = remain; - ATTR("marker-message=\"%d prints remaining on ribbon\"\n", remain); + if (ctx->marker.levelnow != (int)sts->count_ribbon_left) { + ctx->marker.levelnow = sts->count_ribbon_left; + dump_markers(&ctx->marker, 1, 0); } if (sts->hdr.result != RESULT_SUCCESS) @@ -1647,12 +1651,12 @@ top: 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->media = le32_to_cpu(ctx->hdr.media); - print->mode = le32_to_cpu(ctx->hdr.mode); - print->method = le32_to_cpu(ctx->hdr.method); + print->count = cpu_to_le16(job->copies); + print->columns = cpu_to_le16(le32_to_cpu(job->hdr.columns)); + print->rows = cpu_to_le16(le32_to_cpu(job->hdr.rows)); + print->media = le32_to_cpu(job->hdr.media); + print->mode = le32_to_cpu(job->hdr.mode); + print->method = le32_to_cpu(job->hdr.method); if ((ret = s2145_do_cmd(ctx, cmdbuf, sizeof(*print), @@ -1675,7 +1679,7 @@ top: INFO("Sending image data to printer\n"); if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf, ctx->datalen))) + job->databuf, job->datalen))) return CUPS_BACKEND_FAILED; INFO("Waiting for printer to acknowledge completion\n"); @@ -1687,7 +1691,8 @@ top: INFO("Fast return mode enabled.\n"); state = S_FINISHED; } else if (sts->hdr.status == STATUS_READY || - sts->hdr.status == STATUS_FINISHED) { + sts->hdr.status == STATUS_FINISHED || + sts->hdr.status == ERROR_PRINTER) { state = S_FINISHED; } break; @@ -1698,6 +1703,16 @@ top: if (state != S_FINISHED) goto top; + if (sts->hdr.status == ERROR_PRINTER) { + if(sts->hdr.error == ERROR_NONE) + sts->hdr.error = sts->hdr.status; + INFO(" Error 0x%02x (%s) 0x%02x/0x%02x (%s)\n", + sts->hdr.error, + error_str(sts->hdr.error), + sts->hdr.printer_major, + sts->hdr.printer_minor, error_codes(sts->hdr.printer_major, sts->hdr.printer_minor)); + } + INFO("Print complete\n"); return CUPS_BACKEND_OK; @@ -1747,25 +1762,63 @@ static int shinkos2145_query_serno(struct libusb_device_handle *dev, uint8_t end return CUPS_BACKEND_OK; } +static int shinkos2145_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct shinkos2145_ctx *ctx = vctx; + struct s2145_cmd_hdr cmd; + struct s2145_status_resp *sts = (struct s2145_status_resp *) rdbuf; + int num; + + /* Query Status */ + cmd.cmd = cpu_to_le16(S2145_CMD_STATUS); + cmd.len = cpu_to_le16(0); + + if (s2145_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*sts), + &num)) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return CUPS_BACKEND_FAILED; + } + + ctx->marker.levelnow = ctx->marker.levelmax - le32_to_cpu(sts->count_ribbon_left); + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + /* Exported */ #define USB_VID_SHINKO 0x10CE #define USB_PID_SHINKO_S2145 0x000E +static const char *shinkos2145_prefixes[] = { + "shinko-chcs2145", + // extras + "sinfonia-chcs2145", + // Backwards compatibility + "shinkos2145", + NULL +}; + struct dyesub_backend shinkos2145_backend = { - .name = "Shinko/Sinfonia CHC-S2145", - .version = "0.48", - .uri_prefix = "shinkos2145", + .name = "Shinko/Sinfonia CHC-S2145/S2", + .version = "0.55", + .uri_prefixes = shinkos2145_prefixes, .cmdline_usage = shinkos2145_cmdline, .cmdline_arg = shinkos2145_cmdline_arg, .init = shinkos2145_init, .attach = shinkos2145_attach, .teardown = shinkos2145_teardown, + .cleanup_job = shinkos2145_cleanup_job, .read_parse = shinkos2145_read_parse, .main_loop = shinkos2145_main_loop, .query_serno = shinkos2145_query_serno, + .query_markers = shinkos2145_query_markers, .devices = { - { USB_VID_SHINKO, USB_PID_SHINKO_S2145, P_SHINKO_S2145, ""}, - { 0, 0, 0, ""} + { USB_VID_SHINKO, USB_PID_SHINKO_S2145, P_SHINKO_S2145, NULL, "shinko-chc2145"}, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_shinkos6145.c b/src/cups/backend_shinkos6145.c index b9d782a..e795831 100644 --- a/src/cups/backend_shinkos6145.c +++ b/src/cups/backend_shinkos6145.c @@ -1,7 +1,7 @@ /* * Shinko/Sinfonia CHC-S6145 CUPS backend -- libusb-1.0 version * - * (c) 2015-2016 Solomon Peachy + * (c) 2015-2018 Solomon Peachy * * Low-level documentation was provided by Sinfonia. Thank you! * @@ -20,8 +20,7 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * @@ -33,6 +32,8 @@ * You must still adhere to all other terms of the license to this program * (ie GPLv2) and the license of the libS6145ImageProcess library. * + * SPDX-License-Identifier: GPL-2.0+ with special exception + * */ #include @@ -259,43 +260,6 @@ struct shinkos6145_correctionparam { uint8_t pad[3948]; // @12436, null. } __attribute__((packed)); /* 16384 bytes */ -/* Private data structure */ -struct shinkos6145_ctx { - struct libusb_device_handle *dev; - uint8_t endp_up; - uint8_t endp_down; - int type; - - uint8_t jobid; - - struct s6145_printjob_hdr hdr; - - uint8_t image_avg[3]; /* CMY */ - - uint8_t *databuf; - size_t datalen; - - uint8_t ribbon_type; - uint8_t input_ymc; - - uint16_t last_donor; - uint16_t last_remain; - uint16_t last_ribbon; - - uint8_t *eeprom; - size_t eepromlen; - - void *dl_handle; - ImageProcessingFN ImageProcessing; - ImageAvrCalcFN ImageAvrCalc; - - struct shinkos6145_correctionparam *corrdata; - size_t corrdatalen; -}; - -static int shinkos6145_get_imagecorr(struct shinkos6145_ctx *ctx); -static int shinkos6145_get_eeprom(struct shinkos6145_ctx *ctx); - /* Structs for printer */ struct s6145_cmd_hdr { uint16_t cmd; @@ -1115,6 +1079,43 @@ struct s6145_imagecorr_data { uint8_t data[16]; } __attribute__((packed)); +/* Private data structure */ +struct shinkos6145_printjob { + uint8_t *databuf; + size_t datalen; + + struct s6145_printjob_hdr hdr; + + int copies; +}; + +struct shinkos6145_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + + uint8_t jobid; + + uint8_t image_avg[3]; /* CMY */ + + struct marker marker; + + struct s6145_mediainfo_resp media; + + uint8_t *eeprom; + size_t eepromlen; + + void *dl_handle; + ImageProcessingFN ImageProcessing; + ImageAvrCalcFN ImageAvrCalc; + + struct shinkos6145_correctionparam *corrdata; + size_t corrdatalen; +}; + +static int shinkos6145_get_imagecorr(struct shinkos6145_ctx *ctx); +static int shinkos6145_get_eeprom(struct shinkos6145_ctx *ctx); static int get_param(struct shinkos6145_ctx *ctx, int target, uint32_t *param); #define READBACK_LEN 512 /* Needs to be larger than largest response hdr */ @@ -1234,7 +1235,7 @@ static int get_status(struct shinkos6145_ctx *ctx) INFO("Head Distance: %08u inches\n", le32_to_cpu(resp2->head_distance)); /* Query various params */ - if(ctx->type == P_SHINKO_S6145D) { + if (ctx->type == P_SHINKO_S6145D) { if ((ret = get_param(ctx, PARAM_REGION_CODE, &val))) { ERROR("Failed to execute command\n"); return ret; @@ -1355,27 +1356,10 @@ static int get_errorlog(struct shinkos6145_ctx *ctx) return 0; } -static int get_mediainfo(struct shinkos6145_ctx *ctx) +static void dump_mediainfo(struct s6145_mediainfo_resp *resp) { - struct s6145_cmd_hdr cmd; - struct s6145_mediainfo_resp *resp = (struct s6145_mediainfo_resp *) rdbuf; - int ret, num = 0; int i; - cmd.cmd = cpu_to_le16(S6145_CMD_MEDIAINFO); - cmd.len = cpu_to_le16(0); - - if ((ret = s6145_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 s6145_mediainfo_resp) - sizeof(struct s6145_status_hdr))) - return -2; - INFO("Loaded Media Type: %s\n", print_ribbons(resp->ribbon)); INFO("Supported Print Sizes: %u entries:\n", resp->count); for (i = 0 ; i < resp->count ; i++) { @@ -1385,7 +1369,6 @@ static int get_mediainfo(struct shinkos6145_ctx *ctx) 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 shinkos6145_ctx *ctx, char *str) @@ -1865,7 +1848,7 @@ int shinkos6145_cmdline_arg(void *vctx, int argc, char **argv) j = set_tonecurve(ctx, TONECURVE_CURRENT, optarg); break; case 'm': - j = get_mediainfo(ctx); + dump_mediainfo(&ctx->media); break; case 'q': j = shinkos6145_dump_eeprom(ctx, optarg); @@ -1909,22 +1892,15 @@ static void *shinkos6145_init(void) return ctx; } -static void shinkos6145_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int shinkos6145_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct shinkos6145_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(&shinkos6145_backend, - desc.idVendor, desc.idProduct); + ctx->type = type; /* Attempt to open the library */ #if defined(WITH_DYNAMIC) @@ -1952,8 +1928,44 @@ static void shinkos6145_attach(void *vctx, struct libusb_device_handle *dev, /* Ensure jobid is sane */ ctx->jobid = (jobid & 0x7f) + 1; - /* Initialize donor */ - ctx->last_donor = ctx->last_remain = 65535; + if (test_mode < TEST_MODE_NOATTACH) { + /* Query Media */ + struct s6145_mediainfo_resp *resp = (struct s6145_mediainfo_resp *) rdbuf; + struct s6145_cmd_hdr cmd; + int num; + + if (s6145_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*resp), + &num)) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return CUPS_BACKEND_FAILED; + } + memcpy(&ctx->media, resp, sizeof(*resp)); + } else { + int media_code = RIBBON_6x8; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")); + + ctx->media.ribbon = media_code; + } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = print_ribbons(ctx->media.ribbon); + ctx->marker.levelmax = ribbon_sizes(ctx->media.ribbon); + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void shinkos6145_cleanup_job(const void *vjob) +{ + const struct shinkos6145_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void shinkos6145_teardown(void *vctx) { @@ -1962,8 +1974,6 @@ static void shinkos6145_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); if (ctx->eeprom) free(ctx->eeprom); if (ctx->corrdata) @@ -1976,7 +1986,9 @@ static void shinkos6145_teardown(void *vctx) { free(ctx); } -static void lib6145_calc_avg(struct shinkos6145_ctx *ctx, uint16_t rows, uint16_t cols) +static void lib6145_calc_avg(struct shinkos6145_ctx *ctx, + const struct shinkos6145_printjob *job, + uint16_t rows, uint16_t cols) { uint32_t plane, i, planelen; planelen = rows * cols; @@ -1985,7 +1997,7 @@ static void lib6145_calc_avg(struct shinkos6145_ctx *ctx, uint16_t rows, uint16_ uint64_t sum = 0; for (i = 0 ; i < planelen ; i++) { - sum += ctx->databuf[(planelen * plane) + i]; + sum += job->databuf[(planelen * plane) + i]; } ctx->image_avg[plane] = (sum / planelen); } @@ -2066,35 +2078,72 @@ static void lib6145_process_image(uint8_t *src, uint16_t *dest, } } -static int shinkos6145_read_parse(void *vctx, int data_fd) { +static int shinkos6145_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct shinkos6145_ctx *ctx = vctx; int ret; uint8_t tmpbuf[4]; + uint8_t input_ymc; + + struct shinkos6145_printjob *job = NULL; if (!ctx) return CUPS_BACKEND_FAILED; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; // XXX hdr.copies? + /* Read in then validate header */ - ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); - if (ret < 0 || ret != sizeof(ctx->hdr)) { + ret = read(data_fd, &job->hdr, sizeof(job->hdr)); + if (ret < 0 || ret != sizeof(job->hdr)) { + shinkos6145_cleanup_job(job); if (ret == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d/%d)\n", - ret, 0, (int)sizeof(ctx->hdr)); + ret, 0, (int)sizeof(job->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) { +#define SWAP_HDR(__x) job->hdr.__x = le32_to_cpu(job->hdr.__x) + + SWAP_HDR(len1); + SWAP_HDR(model); + SWAP_HDR(media_w); + SWAP_HDR(len2); + SWAP_HDR(media); + SWAP_HDR(method); + SWAP_HDR(qual); + SWAP_HDR(oc_mode); + SWAP_HDR(columns); + SWAP_HDR(rows); + SWAP_HDR(copies); + SWAP_HDR(dpi); + SWAP_HDR(ext_flags); + +#undef SWAP_HDR + + if (job->hdr.len1 != 0x10 || + job->hdr.len2 != 0x64 || + job->hdr.dpi != 300) { ERROR("Unrecognized header data format!\n"); + shinkos6145_cleanup_job(job); return CUPS_BACKEND_CANCEL; } - if (le32_to_cpu(ctx->hdr.model) != 6145) { - ERROR("Unrecognized printer (%u)!\n", le32_to_cpu(ctx->hdr.model)); + if (job->hdr.model != 6145) { + ERROR("Unrecognized printer (%u)!\n", job->hdr.model); + shinkos6145_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + if (!job->hdr.rows || !job->hdr.columns) { + ERROR("Bad print job header!\n"); + shinkos6145_cleanup_job(job); return CUPS_BACKEND_CANCEL; } @@ -2102,29 +2151,26 @@ static int shinkos6145_read_parse(void *vctx, int data_fd) { When bit 0 is set, this tells the backend that the data is already in planar YMC format (vs packed RGB) so we don't need to do the conversion ourselves. Saves some processing overhead */ - ctx->input_ymc = le32_to_cpu(ctx->hdr.ext_flags) & 0x01; - - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; - } + input_ymc = job->hdr.ext_flags & 0x01; - ctx->datalen = le32_to_cpu(ctx->hdr.rows) * le32_to_cpu(ctx->hdr.columns) * 3; - ctx->databuf = malloc(ctx->datalen); - if (!ctx->databuf) { + job->datalen = job->hdr.rows * job->hdr.columns * 3; + job->databuf = malloc(job->datalen); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + shinkos6145_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } { - int remain = ctx->datalen; - uint8_t *ptr = ctx->databuf; + int remain = job->datalen; + uint8_t *ptr = job->databuf; do { ret = read(data_fd, ptr, remain); if (ret < 0) { ERROR("Read failed (%d/%d/%zu)\n", - ret, remain, ctx->datalen); + ret, remain, job->datalen); perror("ERROR: Read failed"); + shinkos6145_cleanup_job(job); return ret; } ptr += ret; @@ -2138,6 +2184,7 @@ static int shinkos6145_read_parse(void *vctx, int data_fd) { ERROR("Read failed (%d/%d/%d)\n", ret, 4, 4); perror("ERROR: Read failed"); + shinkos6145_cleanup_job(job); return ret; } if (tmpbuf[0] != 0x04 || @@ -2145,13 +2192,45 @@ static int shinkos6145_read_parse(void *vctx, int data_fd) { tmpbuf[2] != 0x02 || tmpbuf[3] != 0x01) { ERROR("Unrecognized footer data format!\n"); + shinkos6145_cleanup_job(job); return CUPS_BACKEND_FAILED; } + /* Convert packed RGB to planar YMC if necessary */ + if (!input_ymc) { + INFO("Converting Packed RGB to Planar YMC\n"); + int planelen = job->hdr.columns * job->hdr.rows; + uint8_t *databuf3 = malloc(job->datalen); + int i; + if (!databuf3) { + ERROR("Memory allocation failure!\n"); + shinkos6145_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; + } + for (i = 0 ; i < planelen ; i++) { + uint8_t r, g, b; + r = job->databuf[3*i]; + g = job->databuf[3*i+1]; + b = job->databuf[3*i+2]; + databuf3[i] = 255 - b; + databuf3[planelen + i] = 255 - g; + databuf3[planelen + planelen + i] = 255 - r; + } + free(job->databuf); + job->databuf = databuf3; + } + + // if (job->copies > 1 && hdr->media == 0 && hdr->method == 0) + // and if printer_media == 6x8 or 6x9 + // combine 4x6 + 4x6 -> 8x6 + // 1844x2492 = 1844x1240.. delta = 12. + + *vjob = job; + return CUPS_BACKEND_OK; } -static int shinkos6145_main_loop(void *vctx, int copies) { +static int shinkos6145_main_loop(void *vctx, const void *vjob) { struct shinkos6145_ctx *ctx = vctx; int ret, num; @@ -2167,6 +2246,11 @@ static int shinkos6145_main_loop(void *vctx, int copies) { uint32_t cur_mode; + struct shinkos6145_printjob *job = (struct shinkos6145_printjob*) vjob; /* XXX stupid, we can't do this. */ + + if (!job) + return CUPS_BACKEND_FAILED; + /* Send Media Query */ memset(cmdbuf, 0, CMDBUF_LEN); cmd->cmd = cpu_to_le16(S6145_CMD_MEDIAINFO); @@ -2186,10 +2270,10 @@ static int shinkos6145_main_loop(void *vctx, int copies) { /* 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)) && - media->items[i].print_method == le32_to_cpu(ctx->hdr.method) && - media->items[i].media_code == le32_to_cpu(ctx->hdr.media)) + if (le16_to_cpu(media->items[i].columns) == job->hdr.columns && + le16_to_cpu(media->items[i].rows) == job->hdr.rows && + media->items[i].print_method == job->hdr.method && + media->items[i].media_code == job->hdr.media) break; } if (i == media->count) { @@ -2197,16 +2281,6 @@ static int shinkos6145_main_loop(void *vctx, int copies) { return CUPS_BACKEND_HOLD; } - ctx->last_ribbon = media->ribbon; - - /* Tell CUPS about the consumables we report */ - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", print_ribbons(media->ribbon)); - ATTR("marker-types=ribbonWax\n"); - ctx->ribbon_type = media->ribbon; - // XXX check copies against remaining media? /* Query printer mode */ @@ -2236,23 +2310,14 @@ top: } if (memcmp(rdbuf, rdbuf2, READBACK_LEN)) { - uint16_t donor, remain; - memcpy(rdbuf2, rdbuf, READBACK_LEN); INFO("Printer Status: 0x%02x (%s)\n", sts->hdr.status, status_str(sts->hdr.status)); - /* Guessimate a percentage for the remaining media */ - donor = le32_to_cpu(sts->count_ribbon_left) * 100 / ribbon_sizes(ctx->ribbon_type); - if (donor != ctx->last_donor) { - ctx->last_donor = donor; - ATTR("marker-levels=%d\n", donor); - } - remain = le32_to_cpu(sts->count_ribbon_left); - if (remain != ctx->last_remain) { - ctx->last_remain = remain; - ATTR("marker-message=\"%d prints remaining on '%s' ribbon\"\n", remain, print_ribbons(media->ribbon)); + if (ctx->marker.levelnow != (int)sts->count_ribbon_left) { + ctx->marker.levelnow = sts->count_ribbon_left; + dump_markers(&ctx->marker, 1, 0); } if (sts->hdr.result != RESULT_SUCCESS) @@ -2279,7 +2344,7 @@ top: case S_PRINTER_READY_CMD: { /* Set matte/etc */ - uint32_t oc_mode = le32_to_cpu(ctx->hdr.oc_mode); + uint32_t oc_mode = job->hdr.oc_mode; uint32_t updated = 0; if (!oc_mode) /* if nothing set, default to glossy */ @@ -2320,51 +2385,35 @@ top: /* Set up library transform... */ uint32_t newlen = le16_to_cpu(ctx->corrdata->headDots) * - le32_to_cpu(ctx->hdr.rows) * sizeof(uint16_t) * 4; + job->hdr.rows * sizeof(uint16_t) * 4; uint16_t *databuf2 = malloc(newlen); /* Set the size in the correctiondata */ - ctx->corrdata->width = cpu_to_le16(le32_to_cpu(ctx->hdr.columns)); - ctx->corrdata->height = cpu_to_le16(le32_to_cpu(ctx->hdr.rows)); - - /* Convert packed RGB to planar YMC if necessary */ - if (!ctx->input_ymc) { - int planelen = le16_to_cpu(ctx->corrdata->width) * le16_to_cpu(ctx->corrdata->height); - uint8_t *databuf3 = malloc(ctx->datalen); - - for (i = 0 ; i < planelen ; i++) { - uint8_t r, g, b; - r = ctx->databuf[3*i]; - g = ctx->databuf[3*i+1]; - b = ctx->databuf[3*i+2]; - databuf3[i] = 255 - b; - databuf3[planelen + i] = 255 - g; - databuf3[planelen + planelen + i] = 255 - r; - } - free(ctx->databuf); - ctx->databuf = databuf3; - } + ctx->corrdata->width = cpu_to_le16(job->hdr.columns); + ctx->corrdata->height = cpu_to_le16(job->hdr.rows); + /* Perform the actual library transform */ if (ctx->dl_handle) { INFO("Calling image processing library...\n"); - if (ctx->ImageAvrCalc(ctx->databuf, le32_to_cpu(ctx->hdr.columns), le32_to_cpu(ctx->hdr.rows), ctx->image_avg)) { + if (ctx->ImageAvrCalc(job->databuf, job->hdr.columns, job->hdr.rows, ctx->image_avg)) { + free(databuf2); ERROR("Library returned error!\n"); return CUPS_BACKEND_FAILED; } - ctx->ImageProcessing(ctx->databuf, databuf2, ctx->corrdata); + ctx->ImageProcessing(job->databuf, databuf2, ctx->corrdata); } else { WARNING("Utilizing fallback internal image processing code\n"); WARNING(" *** Output quality will be poor! *** \n"); - lib6145_calc_avg(ctx, le32_to_cpu(ctx->hdr.columns), le32_to_cpu(ctx->hdr.rows)); - lib6145_process_image(ctx->databuf, databuf2, ctx->corrdata, oc_mode); + lib6145_calc_avg(ctx, job, job->hdr.columns, job->hdr.rows); + lib6145_process_image(job->databuf, databuf2, ctx->corrdata, oc_mode); } - free(ctx->databuf); - ctx->databuf = (uint8_t*) databuf2; - ctx->datalen = newlen; + free(job->databuf); + job->databuf = (uint8_t*) databuf2; + job->datalen = newlen; INFO("Sending print job (internal id %u)\n", ctx->jobid); @@ -2373,16 +2422,16 @@ top: 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->count = cpu_to_le16(job->copies); + print->columns = cpu_to_le16(job->hdr.columns); + print->rows = cpu_to_le16(job->hdr.rows); print->image_avg = ctx->image_avg[2]; /* Cyan level */ - print->method = cpu_to_le32(ctx->hdr.method); + print->method = cpu_to_le32(job->hdr.method); print->combo_wait = 0; /* Brava21 header has a few quirks */ if(ctx->type == P_SHINKO_S6145D) { - print->media = ctx->hdr.media; + print->media = job->hdr.media; print->unk_1 = 0x01; } @@ -2409,7 +2458,7 @@ top: // XXX we shouldn't send the lamination layer over if // it's not needed. hdr->oc_mode == PRINT_MODE_NO_OC if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf, ctx->datalen))) + job->databuf, job->datalen))) return CUPS_BACKEND_FAILED; INFO("Waiting for printer to acknowledge completion\n"); @@ -2481,27 +2530,65 @@ static int shinkos6145_query_serno(struct libusb_device_handle *dev, uint8_t end return CUPS_BACKEND_OK; } +static int shinkos6145_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct shinkos6145_ctx *ctx = vctx; + struct s6145_cmd_hdr cmd; + struct s6145_status_resp *sts = (struct s6145_status_resp *) rdbuf; + int num; + + /* Query Status */ + cmd.cmd = cpu_to_le16(S6145_CMD_GETSTATUS); + cmd.len = cpu_to_le16(0); + + if (s6145_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(*sts), + &num)) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return CUPS_BACKEND_FAILED; + } + + ctx->marker.levelnow = le32_to_cpu(sts->count_ribbon_left); + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + /* Exported */ #define USB_VID_SHINKO 0x10CE #define USB_PID_SHINKO_S6145 0x0019 #define USB_PID_SHINKO_S6145D 0x001E /* Aka CIAAT Brava 21 */ +static const char *shinkos6145_prefixes[] = { + "sinfonia-chcs6145", "ciaat-brava-21", + // extras + "shinko-chcs6145", + // backwards-compatiblity + "shinkos6145", "brava21", + NULL +}; + struct dyesub_backend shinkos6145_backend = { - .name = "Shinko/Sinfonia CHC-S6145", - .version = "0.22", - .uri_prefix = "shinkos6145", + .name = "Shinko/Sinfonia CHC-S6145/CS2", + .version = "0.30", + .uri_prefixes = shinkos6145_prefixes, .cmdline_usage = shinkos6145_cmdline, .cmdline_arg = shinkos6145_cmdline_arg, .init = shinkos6145_init, .attach = shinkos6145_attach, .teardown = shinkos6145_teardown, + .cleanup_job = shinkos6145_cleanup_job, .read_parse = shinkos6145_read_parse, .main_loop = shinkos6145_main_loop, .query_serno = shinkos6145_query_serno, + .query_markers = shinkos6145_query_markers, .devices = { - { USB_VID_SHINKO, USB_PID_SHINKO_S6145, P_SHINKO_S6145, ""}, - { USB_VID_SHINKO, USB_PID_SHINKO_S6145D, P_SHINKO_S6145D, ""}, - { 0, 0, 0, ""} + { USB_VID_SHINKO, USB_PID_SHINKO_S6145, P_SHINKO_S6145, NULL, "sinfonia-chcs6145"}, + { USB_VID_SHINKO, USB_PID_SHINKO_S6145D, P_SHINKO_S6145D, NULL, "ciaat-brava-21"}, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_shinkos6245.c b/src/cups/backend_shinkos6245.c index 44a2f7e..5d75e0d 100644 --- a/src/cups/backend_shinkos6245.c +++ b/src/cups/backend_shinkos6245.c @@ -1,7 +1,7 @@ /* * Shinko/Sinfonia CHC-S6245 CUPS backend -- libusb-1.0 version * - * (c) 2015-2016 Solomon Peachy + * (c) 2015-2018 Solomon Peachy * * Low-level documentation was provided by Sinfonia, Inc. Thank you! * @@ -20,11 +20,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -90,25 +91,6 @@ struct s6245_printjob_hdr { uint32_t unk21; } __attribute__((packed)); -/* Private data structure */ -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; - - uint16_t last_donor; - uint16_t last_remain; - uint8_t ribbon_code; -}; - /* Structs for printer */ struct s6245_cmd_hdr { uint16_t cmd; @@ -178,7 +160,7 @@ static char *cmd_names(uint16_t v) { default: return "Unknown Command"; } -}; +} struct s6245_print_cmd { struct s6245_cmd_hdr hdr; @@ -924,37 +906,50 @@ struct s6245_fwinfo_resp { uint16_t checksum; } __attribute__((packed)); +/* Private data structure */ +struct shinkos6245_printjob { + uint8_t *databuf; + int datalen; + int copies; + struct s6245_printjob_hdr hdr; +}; + +struct shinkos6245_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + int type; + + uint8_t jobid; + + struct marker marker; + + struct s6245_mediainfo_resp media; +}; #define READBACK_LEN 512 /* Needs to be larger than largest response hdr */ #define CMDBUF_LEN sizeof(struct s6245_print_cmd) -static uint8_t rdbuf[READBACK_LEN]; - static int s6245_do_cmd(struct shinkos6245_ctx *ctx, uint8_t *cmd, int cmdlen, - int minlen, int *num) + int buflen, + int *num, struct s6245_status_hdr *resp) { - 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; + int ret; if ((ret = send_data(dev, endp_down, cmd, cmdlen))) return (ret < 0) ? ret : -99; ret = read_data(dev, endp_up, - rdbuf, READBACK_LEN, num); + (uint8_t *)resp, buflen, 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, @@ -971,8 +966,8 @@ static int s6245_do_cmd(struct shinkos6245_ctx *ctx, 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; + struct s6245_status_resp resp; + struct s6245_getextcounter_resp resp2; int ret, num = 0; cmd.cmd = cpu_to_le16(S6245_CMD_GETSTATUS); @@ -980,48 +975,48 @@ static int get_status(struct shinkos6245_ctx *ctx) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 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("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)); + 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))) + 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("\tMaintenance:\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("\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("\tMaintenance:\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)); + 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 %03u @ %03u/%03u (%03u 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)); + 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)); + 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); @@ -1029,17 +1024,17 @@ static int get_status(struct shinkos6245_ctx *ctx) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp2), - &num)) < 0) { + sizeof(resp2), + &num, (void*)&resp2)) < 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))) + if (le16_to_cpu(resp2.hdr.payload_len) != (sizeof(struct s6245_getextcounter_resp) - sizeof(struct s6245_status_hdr))) return 0; - INFO("Lifetime Distance: %08u inches\n", le32_to_cpu(resp2->lifetime_distance)); - INFO("Maintenance Distance: %08u inches\n", le32_to_cpu(resp2->maint_distance)); - INFO("Head Distance: %08u inches\n", le32_to_cpu(resp2->head_distance)); + INFO("Lifetime Distance: %08u inches\n", le32_to_cpu(resp2.lifetime_distance)); + INFO("Maintenance Distance: %08u inches\n", le32_to_cpu(resp2.maint_distance)); + INFO("Head Distance: %08u inches\n", le32_to_cpu(resp2.head_distance)); return 0; } @@ -1047,7 +1042,7 @@ static int get_status(struct shinkos6245_ctx *ctx) static int get_fwinfo(struct shinkos6245_ctx *ctx) { struct s6245_fwinfo_cmd cmd; - struct s6245_fwinfo_resp *resp = (struct s6245_fwinfo_resp *)rdbuf; + struct s6245_fwinfo_resp resp; int num = 0; int i; @@ -1062,23 +1057,23 @@ static int get_fwinfo(struct shinkos6245_ctx *ctx) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 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))) + 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); + 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)); + 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; @@ -1087,7 +1082,7 @@ static int get_fwinfo(struct shinkos6245_ctx *ctx) static int get_errorlog(struct shinkos6245_ctx *ctx) { struct s6245_errorlog_cmd cmd; - struct s6245_errorlog_resp *resp = (struct s6245_errorlog_resp *) rdbuf; + struct s6245_errorlog_resp resp; int num = 0; int i = 0; @@ -1100,50 +1095,33 @@ static int get_errorlog(struct shinkos6245_ctx *ctx) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 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))) + 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-%02u-%02u %02u:%02u:%02u @ %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)); + 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: %02u/%02u Hum: %02u\n", - resp->printer_thermistor, resp->head_thermistor, resp->printer_humidity); - } while (++i < le16_to_cpu(resp->error_count)); + 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) +static void dump_mediainfo(struct s6245_mediainfo_resp *resp) { - 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("Loaded Media Type: %s\n", ribbon_sizes(resp->ribbon_code)); INFO("Supported Media Information: %u entries:\n", resp->count); for (i = 0 ; i < resp->count ; i++) { @@ -1153,13 +1131,12 @@ static int get_mediainfo(struct shinkos6245_ctx *ctx) 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; + struct s6245_status_hdr resp; int ret, num = 0; if (!str) @@ -1172,8 +1149,8 @@ static int cancel_job(struct shinkos6245_ctx *ctx, char *str) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 0) { ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); return ret; } @@ -1184,7 +1161,7 @@ static int cancel_job(struct shinkos6245_ctx *ctx, char *str) static int flash_led(struct shinkos6245_ctx *ctx) { struct s6245_cmd_hdr cmd; - struct s6245_status_hdr *resp = (struct s6245_status_hdr *) rdbuf; + struct s6245_status_hdr resp; int ret, num = 0; cmd.cmd = cpu_to_le16(S6245_CMD_FLASHLED); @@ -1192,8 +1169,8 @@ static int flash_led(struct shinkos6245_ctx *ctx) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 0) { ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); return ret; } @@ -1205,7 +1182,7 @@ static int flash_led(struct shinkos6245_ctx *ctx) 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; + struct s6245_status_hdr resp; int ret, num = 0; /* Set up command */ @@ -1217,8 +1194,8 @@ static int set_param(struct shinkos6245_ctx *ctx, int target, uint32_t param) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 0) { ERROR("Failed to execute %s command (%d)\n", cmd_names(cmd.hdr.cmd), ret); } @@ -1228,7 +1205,7 @@ static int set_param(struct shinkos6245_ctx *ctx, int target, uint32_t param) 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; + struct s6245_status_hdr resp; int ret, num = 0; cmd.target = target; @@ -1238,8 +1215,8 @@ static int reset_curve(struct shinkos6245_ctx *ctx, int target) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 0) { ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); return ret; } @@ -1249,8 +1226,8 @@ static int reset_curve(struct shinkos6245_ctx *ctx, int target) 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; + struct s6245_readtone_cmd cmd; + struct s6245_readtone_resp resp; int ret, num = 0; uint8_t *data; @@ -1268,25 +1245,25 @@ static int get_tonecurve(struct shinkos6245_ctx *ctx, int type, char *fname) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 0) { ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); return ret; } - resp->total_size = le16_to_cpu(resp->total_size); + resp.total_size = le16_to_cpu(resp.total_size); - data = malloc(resp->total_size * 2); + data = malloc(resp.total_size * 2); if (!data) { ERROR("Memory Allocation Failure!\n"); return -1; } i = 0; - while (i < resp->total_size) { + while (i < resp.total_size) { ret = read_data(ctx->dev, ctx->endp_up, data + i, - resp->total_size * 2 - i, + resp.total_size * 2 - i, &num); if (ret < 0) goto done; @@ -1294,7 +1271,7 @@ static int get_tonecurve(struct shinkos6245_ctx *ctx, int type, char *fname) } i = j = 0; - while (i < resp->total_size) { + while (i < resp.total_size) { memcpy(curves + j, data + i+2, data[i+1]); j += data[i+1] / 2; i += data[i+1] + 2; @@ -1324,7 +1301,7 @@ done: 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; + struct s6245_status_hdr resp; int ret, num = 0; INFO("Set %s Tone Curve from '%s'\n", update_targets(target), fname); @@ -1367,8 +1344,8 @@ static int set_tonecurve(struct shinkos6245_ctx *ctx, int target, char *fname) if ((ret = s6245_do_cmd(ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 0) { ERROR("Failed to execute %s command\n", cmd_names(cmd.hdr.cmd)); goto done; } @@ -1455,7 +1432,7 @@ int shinkos6245_cmdline_arg(void *vctx, int argc, char **argv) j = set_tonecurve(ctx, TONECURVE_CURRENT, optarg); break; case 'm': - j = get_mediainfo(ctx); + dump_mediainfo(&ctx->media); break; case 'r': j = reset_curve(ctx, RESET_TONE_CURVE); @@ -1491,30 +1468,61 @@ static void *shinkos6245_init(void) return ctx; } -static void shinkos6245_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int shinkos6245_attach(void *vctx, struct libusb_device_handle *dev, int type, + 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; + + int num; 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); + ctx->type = type; /* Ensure jobid is sane */ ctx->jobid = jobid & 0x7f; if (!ctx->jobid) ctx->jobid++; - /* Initialize donor */ - ctx->last_donor = ctx->last_remain = 65535; + /* Query Media */ + if (test_mode < TEST_MODE_NOATTACH) { + struct s6245_cmd_hdr cmd; + cmd.cmd = cpu_to_le16(S6245_CMD_MEDIAINFO); + cmd.len = cpu_to_le16(0); + + if (s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(ctx->media), + &num, (void*)&ctx->media)) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return CUPS_BACKEND_FAILED; + } + } else { + int media_code = 0x12; + if (getenv("MEDIA_CODE")) + media_code = atoi(getenv("MEDIA_CODE")); + + + ctx->media.ribbon_code = media_code; + } + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = ribbon_sizes(ctx->media.ribbon_code); + ctx->marker.levelmax = ribbon_counts(ctx->media.ribbon_code); + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} + +static void shinkos6245_cleanup_job(const void *vjob) +{ + const struct shinkos6245_printjob *job = vjob; + + if (job->databuf) + free(job->databuf); + + free((void*)job); } static void shinkos6245_teardown(void *vctx) { @@ -1523,64 +1531,71 @@ static void shinkos6245_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); - free(ctx); } -static int shinkos6245_read_parse(void *vctx, int data_fd) { +static int shinkos6245_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct shinkos6245_ctx *ctx = vctx; int ret; uint8_t tmpbuf[4]; + struct shinkos6245_printjob *job = NULL; + if (!ctx) return CUPS_BACKEND_FAILED; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + memset(job, 0, sizeof(*job)); + job->copies = copies; // XXX hdr.copies + /* Read in then validate header */ - ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr)); - if (ret < 0 || ret != sizeof(ctx->hdr)) { + ret = read(data_fd, &job->hdr, sizeof(job->hdr)); + if (ret < 0 || ret != sizeof(job->hdr)) { if (ret == 0) return CUPS_BACKEND_CANCEL; ERROR("Read failed (%d/%d/%d)\n", - ret, 0, (int)sizeof(ctx->hdr)); + ret, 0, (int)sizeof(job->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) { + if (le32_to_cpu(job->hdr.len1) != 0x10 || + le32_to_cpu(job->hdr.len2) != 0x64 || + le32_to_cpu(job->hdr.dpi) != 300) { ERROR("Unrecognized header data format!\n"); return CUPS_BACKEND_CANCEL; } - if (le32_to_cpu(ctx->hdr.model) != 6245) { - ERROR("Unrecognized printer (%u)!\n", le32_to_cpu(ctx->hdr.model)); + if (le32_to_cpu(job->hdr.model) != 6245) { + ERROR("Unrecognized printer (%u)!\n", le32_to_cpu(job->hdr.model)); return CUPS_BACKEND_CANCEL; } - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + if (job->databuf) { + free(job->databuf); + job->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) { + job->datalen = le32_to_cpu(job->hdr.rows) * le32_to_cpu(job->hdr.columns) * 3; + job->databuf = malloc(job->datalen); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + return CUPS_BACKEND_RETRY_CURRENT; } { - int remain = ctx->datalen; - uint8_t *ptr = ctx->databuf; + int remain = job->datalen; + uint8_t *ptr = job->databuf; do { ret = read(data_fd, ptr, remain); if (ret < 0) { ERROR("Read failed (%d/%d/%d)\n", - ret, remain, ctx->datalen); + ret, remain, job->datalen); perror("ERROR: Read failed"); return ret; } @@ -1605,23 +1620,29 @@ static int shinkos6245_read_parse(void *vctx, int data_fd) { return CUPS_BACKEND_FAILED; } + *vjob = job; + return CUPS_BACKEND_OK; } -static int shinkos6245_main_loop(void *vctx, int copies) { +static int shinkos6245_main_loop(void *vctx, const void *vjob) { 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; + int copies; 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; + struct s6245_status_resp sts, sts2; + struct s6245_status_hdr resp; + + struct shinkos6245_printjob *job = (struct shinkos6245_printjob*) vjob; + + copies = job->copies; /* Cap copies */ // XXX 120 for 8x10 media, 100 for 8x12 media (S6245) @@ -1630,7 +1651,7 @@ static int shinkos6245_main_loop(void *vctx, int copies) { copies = 120; /* Set up mcut */ - switch (le32_to_cpu(ctx->hdr.media)) { + switch (le32_to_cpu(job->hdr.media)) { case MEDIA_8x4_2: case MEDIA_8x5_2: case MEDIA_8x6_2: @@ -1644,34 +1665,17 @@ static int shinkos6245_main_loop(void *vctx, int copies) { } // 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++) { + for (i = 0; i < ctx->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))) + if (le16_to_cpu(ctx->media.items[i].columns) == cpu_to_le16(le32_to_cpu(job->hdr.columns)) && + le16_to_cpu(ctx->media.items[i].rows) == cpu_to_le16(le32_to_cpu(job->hdr.rows))) break; } - if (i == media->count) { + if (i == ctx->media.count) { ERROR("Incorrect media loaded for print!\n"); return CUPS_BACKEND_HOLD; } - ctx->ribbon_code = media->ribbon_code; /* Send Set Time */ { @@ -1693,21 +1697,14 @@ static int shinkos6245_main_loop(void *vctx, int copies) { if ((ret = s6245_do_cmd(ctx, cmdbuf, sizeof(*stime), sizeof(struct s6245_status_hdr), - &num)) < 0) { + &num, (void*)&resp)) < 0) { ERROR("Failed to execute %s command\n", cmd_names(stime->hdr.cmd)); return CUPS_BACKEND_FAILED; } - if (sts->hdr.result != RESULT_SUCCESS) + if (resp.result != RESULT_SUCCESS) goto printer_error; } - /* Tell CUPS about the consumables we report */ - ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); - ATTR("marker-high-levels=100\n"); - ATTR("marker-low-levels=10\n"); - ATTR("marker-names='%s'\n", ribbon_sizes(ctx->ribbon_code)); - ATTR("marker-types=ribbonWax\n"); - // XXX check copies against remaining media! top: @@ -1723,35 +1720,26 @@ top: if ((ret = s6245_do_cmd(ctx, cmdbuf, sizeof(*cmd), - sizeof(struct s6245_status_hdr), - &num)) < 0) { + sizeof(sts), + &num, (void*)&sts)) < 0) { ERROR("Failed to execute %s command\n", cmd_names(cmd->cmd)); return CUPS_BACKEND_FAILED; } - if (memcmp(rdbuf, rdbuf2, READBACK_LEN)) { - uint16_t donor, remain; - - memcpy(rdbuf2, rdbuf, READBACK_LEN); + if (memcmp(&sts2, &sts, sizeof(sts))) { + memcpy(&sts2, &sts, sizeof(sts)); INFO("Printer Status: 0x%02x (%s)\n", - sts->hdr.status, status_str(sts->hdr.status)); + sts.hdr.status, status_str(sts.hdr.status)); - /* Guessimate a percentage for the remaining media */ - donor = le32_to_cpu(sts->count_ribbon_left) * 100 / ribbon_counts(ctx->ribbon_code); - if (donor != ctx->last_donor) { - ctx->last_donor = donor; - ATTR("marker-levels=%d\n", donor); - } - remain = le32_to_cpu(sts->count_ribbon_left); - if (remain != ctx->last_remain) { - ctx->last_remain = remain; - ATTR("marker-message=\"%d prints remaining on '%s' ribbon\"\n", remain, ribbon_sizes(ctx->ribbon_code)); + if (ctx->marker.levelnow != (int)sts.count_ribbon_left) { + ctx->marker.levelnow = sts.count_ribbon_left; + dump_markers(&ctx->marker, 1, 0); } - if (sts->hdr.result != RESULT_SUCCESS) + if (sts.hdr.result != RESULT_SUCCESS) goto printer_error; - if (sts->hdr.error == ERROR_PRINTER) + if (sts.hdr.error == ERROR_PRINTER) goto printer_error; } else if (state == last_state) { sleep(1); @@ -1767,8 +1755,8 @@ top: /* make sure we're not colliding with an existing jobid */ - while (ctx->jobid == sts->bank1_printid || - ctx->jobid == sts->bank2_printid) { + while (ctx->jobid == sts.bank1_printid || + ctx->jobid == sts.bank2_printid) { ctx->jobid++; ctx->jobid &= 0x7f; if (!ctx->jobid) @@ -1776,8 +1764,8 @@ top: } /* If either bank is free, continue */ - if (sts->bank1_status == BANK_STATUS_FREE || - sts->bank2_status == BANK_STATUS_FREE) + if (sts.bank1_status == BANK_STATUS_FREE || + sts.bank2_status == BANK_STATUS_FREE) state = S_PRINTER_READY_CMD; break; @@ -1792,33 +1780,33 @@ top: 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->columns = cpu_to_le16(le32_to_cpu(job->hdr.columns)); + print->rows = cpu_to_le16(le32_to_cpu(job->hdr.rows)); + print->mode = le32_to_cpu(job->hdr.oc_mode); print->method = mcut; if ((ret = s6245_do_cmd(ctx, cmdbuf, sizeof(*print), - sizeof(struct s6245_status_hdr), - &num)) < 0) { + sizeof(resp), + &num, (void*)&resp)) < 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) { + if (resp.result != RESULT_SUCCESS) { + if (resp.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)); + } else if ((resp.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) + } else if (resp.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))) + job->databuf, job->datalen))) return CUPS_BACKEND_FAILED; INFO("Waiting for printer to acknowledge completion\n"); @@ -1829,7 +1817,7 @@ top: if (fast_return) { INFO("Fast return mode enabled.\n"); state = S_FINISHED; - } else if (sts->hdr.status == STATUS_READY) { + } else if (sts.hdr.status == STATUS_READY) { state = S_FINISHED; } break; @@ -1846,19 +1834,19 @@ top: 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)); + 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; + struct s6245_getserial_resp resp; int ret, num = 0; struct shinkos6245_ctx ctx = { @@ -1872,42 +1860,83 @@ static int shinkos6245_query_serno(struct libusb_device_handle *dev, uint8_t end if ((ret = s6245_do_cmd(&ctx, (uint8_t*)&cmd, sizeof(cmd), - sizeof(*resp) - 1, - &num)) < 0) { + sizeof(resp) - 1, + &num, (void*)&resp)) < 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); + 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; } +static int shinkos6245_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct shinkos6245_ctx *ctx = vctx; + struct s6245_cmd_hdr cmd; + struct s6245_status_resp status; + int num; + + /* Query Status */ + cmd.cmd = cpu_to_le16(S6245_CMD_GETSTATUS); + cmd.len = cpu_to_le16(0); + + if (s6245_do_cmd(ctx, + (uint8_t*)&cmd, sizeof(cmd), + sizeof(status), + &num, (void*)&status)) { + ERROR("Failed to execute %s command\n", cmd_names(cmd.cmd)); + return CUPS_BACKEND_FAILED; + } + + ctx->marker.levelnow = le32_to_cpu(status.count_ribbon_left); + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + /* Exported */ #define USB_VID_SHINKO 0x10CE #define USB_PID_SHINKO_S6245 0x001D +#define USB_VID_HITI 0x0D16 +#define USB_PID_HITI_P910L 0x000E + +static const char *shinkos6245_prefixes[] = { + "sinfonia-chcs6245", "hiti-p910l", + // extras + "shinko-chcs6245", + // backwards compatibility + "shinkos6245", "hitip910", + NULL +}; struct dyesub_backend shinkos6245_backend = { .name = "Shinko/Sinfonia CHC-S6245", - .version = "0.07WIP", - .uri_prefix = "shinkos6245", + .version = "0.14WIP", + .uri_prefixes = shinkos6245_prefixes, .cmdline_usage = shinkos6245_cmdline, .cmdline_arg = shinkos6245_cmdline_arg, .init = shinkos6245_init, .attach = shinkos6245_attach, .teardown = shinkos6245_teardown, + .cleanup_job = shinkos6245_cleanup_job, .read_parse = shinkos6245_read_parse, .main_loop = shinkos6245_main_loop, .query_serno = shinkos6245_query_serno, + .query_markers = shinkos6245_query_markers, .devices = { - { USB_VID_SHINKO, USB_PID_SHINKO_S6245, P_SHINKO_S6245, ""}, - { 0, 0, 0, ""} + { USB_VID_SHINKO, USB_PID_SHINKO_S6245, P_SHINKO_S6245, NULL, "shinfonia-chcs6245"}, + { USB_VID_HITI, USB_PID_HITI_P910L, P_SHINKO_S6245, NULL, "hiti-p910l"}, + { 0, 0, 0, NULL, NULL} } }; diff --git a/src/cups/backend_sonyupdr150.c b/src/cups/backend_sonyupdr150.c index dae8bf1..5e8ae2e 100644 --- a/src/cups/backend_sonyupdr150.c +++ b/src/cups/backend_sonyupdr150.c @@ -1,7 +1,7 @@ /* * Sony UP-DR150 Photo Printer CUPS backend -- libusb-1.0 version * - * (c) 2013-2016 Solomon Peachy + * (c) 2013-2018 Solomon Peachy * * The latest version of this program can be found at: * @@ -18,11 +18,12 @@ * 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. + * along with this program. If not, see . * * [http://www.gnu.org/licenses/gpl-2.0.html] * + * SPDX-License-Identifier: GPL-2.0+ + * */ #include @@ -39,23 +40,20 @@ #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 0x0226 - /* Private data structure */ +struct updr150_printjob { + uint8_t *databuf; + int datalen; + int copies; +}; + 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; + struct marker marker; }; static void* updr150_init(void) @@ -69,26 +67,34 @@ static void* updr150_init(void) return ctx; } -static void updr150_attach(void *vctx, struct libusb_device_handle *dev, - uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +static int updr150_attach(void *vctx, struct libusb_device_handle *dev, int type, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) { struct updr150_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; + ctx->type = type; + + ctx->marker.color = "#00FFFF#FF00FF#FFFF00"; + ctx->marker.name = "Unknown"; + ctx->marker.levelmax = -1; + ctx->marker.levelnow = -2; + + return CUPS_BACKEND_OK; +} - device = libusb_get_device(dev); - libusb_get_device_descriptor(device, &desc); +static void updr150_cleanup_job(const void *vjob) +{ + const struct updr150_printjob *job = vjob; - ctx->type = lookup_printer_type(&updr150_backend, - desc.idVendor, desc.idProduct); + if (job->databuf) + free(job->databuf); - ctx->copies_offset = 0; + free((void*)job); } static void updr150_teardown(void *vctx) { @@ -97,147 +103,164 @@ static void updr150_teardown(void *vctx) { if (!ctx) return; - if (ctx->databuf) - free(ctx->databuf); free(ctx); } #define MAX_PRINTJOB_LEN 16736455 -static int updr150_read_parse(void *vctx, int data_fd) { +static int updr150_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct updr150_ctx *ctx = vctx; int len, run = 1; + uint32_t copies_offset = 0; + + struct updr150_printjob *job = NULL; if (!ctx) return CUPS_BACKEND_FAILED; - if (ctx->databuf) { - free(ctx->databuf); - ctx->databuf = NULL; + job = malloc(sizeof(*job)); + if (!job) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; } + memset(job, 0, sizeof(*job)); + job->copies = copies; - ctx->datalen = 0; - ctx->databuf = malloc(MAX_PRINTJOB_LEN); - if (!ctx->databuf) { + job->datalen = 0; + job->databuf = malloc(MAX_PRINTJOB_LEN); + if (!job->databuf) { ERROR("Memory allocation failure!\n"); - return CUPS_BACKEND_FAILED; + updr150_cleanup_job(job); + return CUPS_BACKEND_RETRY_CURRENT; } while(run) { int i; int keep = 0; - i = read(data_fd, ctx->databuf + ctx->datalen, 4); - if (i < 0) + i = read(data_fd, job->databuf + job->datalen, 4); + if (i < 0) { + updr150_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } if (i == 0) break; - memcpy(&len, ctx->databuf + ctx->datalen, sizeof(len)); + memcpy(&len, job->databuf + job->datalen, sizeof(len)); len = le32_to_cpu(len); /* Filter out chunks we don't send to the printer */ - switch (len) { - case 0xffffff60: - case 0xffffff6a: - case 0xffffffeb: - case 0xffffffec: - case 0xffffffed: - case 0xfffffff4: - case 0xfffffff8: - case 0xfffffff9: - case 0xfffffffa: - case 0xfffffffb: - case 0xfffffffc: - case 0xffffffff: - if(dyesub_debug) - DEBUG("Block ID '%08x' (len %d)\n", len, 0); - len = 0; - break; - case 0xfffffff3: - if(dyesub_debug) - DEBUG("Block ID '%08x' (len %d)\n", len, 0); - len = 0; - if (ctx->type == P_SONY_UPDR150) - run = 0; - break; - case 0xfffffff7: - if(dyesub_debug) - DEBUG("Block ID '%08x' (len %d)\n", len, 0); - len = 0; - if (ctx->type == P_SONY_UPCR10) - run = 0; - break; - case 0xffffffef: - case 0xfffffff5: - if(dyesub_debug) - DEBUG("Block ID '%08x' (len %d)\n", len, 4); - len = 4; - break; - default: - if (len & 0xff000000) { - ERROR("Unknown block ID '%08x', aborting!\n", len); - return CUPS_BACKEND_CANCEL; - } else { - /* Only keep these chunks */ + if (len & 0xf0000000) { + switch (len) { + case 0xfffffff3: + if(dyesub_debug) + DEBUG("Block ID '%08x' (len %d)\n", len, 0); + len = 0; + if (ctx->type == P_SONY_UPDR150) + run = 0; + break; + case 0xfffffff7: + if(dyesub_debug) + DEBUG("Block ID '%08x' (len %d)\n", len, 0); + len = 0; + if (ctx->type == P_SONY_UPCR10) + run = 0; + break; + case 0xfffffff8: // 895 + case 0xfffffff4: // 897 + if(dyesub_debug) + DEBUG("Block ID '%08x' (len %d)\n", len, 0); + len = 0; + if (ctx->type == P_SONY_UPD89x) + run = 0; + break; + case 0xffffffeb: + case 0xffffffec: + case 0xffffffed: + case 0xffffffee: + case 0xffffffef: + case 0xfffffff5: if(dyesub_debug) - DEBUG("Data block (len %d)\n", len); - keep = 1; + DEBUG("Block ID '%08x' (len %d)\n", len, 4); + len = 4; + break; + default: + if(dyesub_debug) + DEBUG("Block ID '%08x' (len %d)\n", len, 0); + len = 0; + break; } - break; + } else { + /* Only keep these chunks */ + if(dyesub_debug) + DEBUG("Data block (len %d)\n", len); + keep = 1; } if (keep) - ctx->datalen += sizeof(uint32_t); + job->datalen += sizeof(uint32_t); /* Read in the data chunk */ while(len > 0) { - i = read(data_fd, ctx->databuf + ctx->datalen, len); - if (i < 0) + i = read(data_fd, job->databuf + job->datalen, len); + if (i < 0) { + updr150_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } if (i == 0) break; - if (ctx->databuf[ctx->datalen] == 0x1b && - ctx->databuf[ctx->datalen + 1] == 0xee) { + if (job->databuf[job->datalen] == 0x1b && + job->databuf[job->datalen + 1] == 0xee) { if (ctx->type == P_SONY_UPCR10) - ctx->copies_offset = ctx->datalen + 8; + copies_offset = job->datalen + 8; else - ctx->copies_offset = ctx->datalen + 12; + copies_offset = job->datalen + 12; } if (keep) - ctx->datalen += i; + job->datalen += i; len -= i; } } - if (!ctx->datalen) + if (!job->datalen) { + updr150_cleanup_job(job); return CUPS_BACKEND_CANCEL; + } + + /* Some models specify copies in the print job */ + if (copies_offset) { + job->databuf[copies_offset] = job->copies; + job->copies = 1; + } + + *vjob = job; return CUPS_BACKEND_OK; } -static int updr150_main_loop(void *vctx, int copies) { +static int updr150_main_loop(void *vctx, const void *vjob) { struct updr150_ctx *ctx = vctx; int i, ret; + int copies; + + const struct updr150_printjob *job = vjob; if (!ctx) return CUPS_BACKEND_FAILED; + if (!job) + return CUPS_BACKEND_FAILED; - /* Some models specify copies in the print job */ - if (ctx->copies_offset) { - ctx->databuf[ctx->copies_offset] = copies; - copies = 1; - } + copies = job->copies; top: i = 0; - while (i < ctx->datalen) { + while (i < job->datalen) { uint32_t len; - memcpy(&len, ctx->databuf + i, sizeof(len)); + memcpy(&len, job->databuf + i, sizeof(len)); len = le32_to_cpu(len); i += sizeof(uint32_t); if ((ret = send_data(ctx->dev, ctx->endp_down, - ctx->databuf + i, len))) + job->databuf + i, len))) return CUPS_BACKEND_FAILED; i += len; @@ -275,21 +298,54 @@ static int updr150_cmdline_arg(void *vctx, int argc, char **argv) return 0; } +static int updr150_query_markers(void *vctx, struct marker **markers, int *count) +{ + struct updr150_ctx *ctx = vctx; + + *markers = &ctx->marker; + *count = 1; + + return CUPS_BACKEND_OK; +} + +static const char *sonyupdr150_prefixes[] = { + "sonyupdr150", // Family name. + "sony-updr150", "sony-updr200", "sony-upcr10", + // Backwards compatibility + "sonyupdr200", "sonyupcr10", +// "sonyupd895", "sonyupd897", "sonyupd898", + NULL +}; + +/* Exported */ +#define USB_VID_SONY 0x054C +#define USB_PID_SONY_UPDR150 0x01E8 +#define USB_PID_SONY_UPDR200 0x035F +#define USB_PID_SONY_UPCR10 0x0226 +//#define USB_PID_SONY_UPD895 XXXXX // 0x7ea6? +//#define USB_PID_SONY_UPD897 XXXXX // 0xbce7? +//#define USB_PID_SONY_UPD898 XXXXX // 0x589a? + struct dyesub_backend updr150_backend = { .name = "Sony UP-DR150/UP-DR200/UP-CR10", - .version = "0.19", - .uri_prefix = "sonyupdr150", + .version = "0.26", + .uri_prefixes = sonyupdr150_prefixes, .cmdline_arg = updr150_cmdline_arg, .init = updr150_init, .attach = updr150_attach, .teardown = updr150_teardown, + .cleanup_job = updr150_cleanup_job, .read_parse = updr150_read_parse, .main_loop = updr150_main_loop, + .query_markers = updr150_query_markers, .devices = { - { USB_VID_SONY, USB_PID_SONY_UPDR150, P_SONY_UPDR150, ""}, - { USB_VID_SONY, USB_PID_SONY_UPDR200, P_SONY_UPDR150, ""}, - { USB_VID_SONY, USB_PID_SONY_UPCR10, P_SONY_UPCR10, ""}, - { 0, 0, 0, ""} + { USB_VID_SONY, USB_PID_SONY_UPDR150, P_SONY_UPDR150, NULL, "sony-updr150"}, + { USB_VID_SONY, USB_PID_SONY_UPDR200, P_SONY_UPDR150, NULL, "sony-updr200"}, + { USB_VID_SONY, USB_PID_SONY_UPCR10, P_SONY_UPCR10, NULL, "sony-upcr10"}, +// { USB_VID_SONY, USB_PID_SONY_UPD895MD, P_SONY_UPD89x, NULL, "sonyupd895"}, +// { USB_VID_SONY, USB_PID_SONY_UPD897MD, P_SONY_UPD89x, NULL, "sonyupd897"}, +// { USB_VID_SONY, USB_PID_SONY_UPD898MD, P_SONY_UPD89x, NULL, "sonyupd898"}, + { 0, 0, 0, NULL, NULL} } }; @@ -299,25 +355,18 @@ struct dyesub_backend updr150_backend = { arguments. The purpose of the commands is unknown, but they presumably instruct the driver to perform certain things. - If you treat these 4 bytes as a 32-bit little-endian number, if the - most significant four bits are bits are non-zero, the value is is to + If you treat these 4 bytes as a 32-bit little-endian number, if any of the + most significant 4 bits are non-zero, the value is is to be interpreted as a driver command. If the most significant bits are zero, the value signifies that the following N bytes of data should be sent to the printer as-is. Known driver "commands": - 6a ff ff ff - fc ff ff ff - fb ff ff ff - f4 ff ff ff - ed ff ff ff - f9 ff ff ff - f8 ff ff ff - ec ff ff ff - eb ff ff ff - fa ff ff ff - f3 ff ff ff + eb ff ff ff ?? 00 00 00 + ec ff ff ff ?? 00 00 00 + ed ff ff ff ?? 00 00 00 + ee ff ff ff ?? 00 00 00 ef ff ff ff XX 00 00 00 # XX == print size (0x01/0x02/0x03/0x04) f5 ff ff ff YY 00 00 00 # YY == ??? (seen 0x01) @@ -403,6 +452,117 @@ f7 ff ff ff SH SH SH SH == Plane size, Big Endian (Rows * Cols * 3) NN == Copies + ************** + + Sony UP-D895 spool format: + + XX XX == cols, BE (fixed at 1280/0x500) + YY YY == rows, BE (798/0x031e,1038/0x040e,1475/0x05c3, 2484/09b4) @ 960/1280/1920/3840+4096 + SS SS SS SS == data len (rows * cols, LE) + S' S' S' S' == data len (rows * cols, BE) + NN == copies (1 -> ??) + GG GG == ??? 0000/0050/011b/04aa/05aa at each resolution. + G' == Gamma 01 (soft), 03 (hard), 02 (normal) + + 9c ff ff ff 97 ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff ff + + 14 00 00 00 + 1b 15 00 00 00 0d 00 00 00 00 00 01 GG GG 00 00 YY YY XX XX + 0b 00 00 00 + 1b ea 00 00 00 00 S' S' S' S' 00 + SS SS SS SS + ...DATA... (rows * cols) + ff ff ff ff + 09 00 00 00 + 1b ee 00 00 00 02 00 00 NN + 0f 00 00 00 + 1b e5 00 00 00 08 00 00 00 00 00 00 00 00 00 + 0c 00 00 00 + 1b c0 00 00 00 05 00 02 00 00 01 G' + 11 00 00 00 + 1b c0 00 01 00 0a 00 02 01 00 06 00 00 00 00 00 00 + 12 00 00 00 + 1b e1 00 00 00 0b 00 00 08 00 GG GG 00 00 YY YY XX XX + 07 00 00 00 + 1b 0a 00 00 00 00 00 + fd ff ff ff f7 ff ff ff f8 ff ff ff + + ************** + + Sony UP-D897 spool format: + + NN NN == copies (00 for printer selected) + XX XX == cols (fixed @ 1280) + YY YY == rows + GG == gamma -- Table 2 == 2, Table 1 == 3, Table 3 == 1, Table 4 == 4 + DD == "dark" +- 64. + LL == "light" +- 64. + AA == "advanced" +- 32. + SS == Sharpness 0-14 + ZZ ZZ ZZ ZZ == Data length (BE) + Z` Z` Z` Z` == Data length (LE) + + + 83 ff ff ff fc ff ff ff fb ff ff ff f5 ff ff ff f1 ff ff ff f0 ff ff ff ef ff ff ff + + 07 00 00 00 + 1b 15 00 00 00 0d 00 + 0d 00 00 00 + 00 00 00 00 01 00 a2 00 00 YY YY XX XX + + 0b 00 00 00 + 1b ea 00 00 00 00 ZZ ZZ ZZ ZZ 00 + + Z` Z` Z` Z` + ...DATA... + + ea ff ff ff + + 07 00 00 00 + 1b ee 00 00 00 02 00 + 02 00 00 00 + 00 NN + + ee ff ff ff 01 00 00 00 + + 07 00 00 00 + 1b e5 00 00 00 08 00 + 08 00 00 00 + 00 00 00 00 DD LL SS AA + + eb ff ff ff ?? 00 00 00 <--- 02/05 5 at #3, 2 otherwise. Sharpness? + + 07 00 00 00 + 1b c0 00 00 00 05 00 + 05 00 00 00 + 02 00 00 01 GG + + ec ff ff ff ?? 00 00 00 <--- 01/00/02/01/01 Seen. Unknown. + + 07 00 00 00 + 1b c0 00 01 00 0a 00 + 0a 00 00 00 + 02 01 00 06 00 00 00 00 00 00 + + ed ff ff ff 00 00 00 00 + + 07 00 00 00 + 1b e1 00 00 00 0b 00 + 0b 00 00 00 + 00 08 00 00 a2 00 00 YY YY XX XX + + fa ff ff ff + + 07 00 00 00 + 1b 0a 00 00 00 00 00 + + fc ff ff ff + fd ff ff ff + ff ff ff ff + + 07 00 00 00 + 1b 17 00 00 00 00 00 + f4 ff ff ff */ diff --git a/src/cups/blacklist b/src/cups/blacklist index 0ca284c..157fd47 100644 --- a/src/cups/blacklist +++ b/src/cups/blacklist @@ -90,6 +90,9 @@ # Canon SELPHY CP1200 0x04a9 0x32b1 blacklist +# Canon SELPHY CP1300 +0x04a9 0x32db blacklist + # Canon SELPHY ES1 0x04a9 0x3141 blacklist @@ -153,6 +156,9 @@ # Mitsubishi CP-D80DW 0x06d3 0x3b36 blacklist +# Mitsubishi CP-D90DW +0x06d3 0x3b60 blacklist + # Mitsubishi CP-K60DW-S 0x06d3 0x3b31 blacklist @@ -196,17 +202,19 @@ # DNP DS80 + Citizen CX-W + Mitsubishi CP3800 0x1343 0x0004 blacklist -# DNP DS-RX1 + Citizen CY +# DNP DS-RX1 + Citizen CY (Plus RX1HS and CY-02) 0x1343 0x0005 blacklist -# Citizen CW-02 +# Citizen CW-02 / OP900II 0x1343 0x0006 blacklist +# Citizen CX-02 +0x1343 0x000A blacklist + # DNP DS80D -0x1343 0x0007 blacklist +0x1343 0x0008 blacklist # DNP DS620 -0x1343 0x0008 blacklist 0x1452 0x8b01 blacklist # DNP DS820 @@ -218,3 +226,14 @@ # Fujifilm ASK-300 0x04cb 0x5006 blacklist +# HiTi P910L +0x0d16 0x000e blacklist + +# Magicard Tango 2E +0x0c1f 0x1800 blacklist + +# Magicard Enduro +0x0c1f 0x4800 blacklist + +# Magicard Enduro+ +0x0c1f 0x880a blacklist diff --git a/src/cups/command.types b/src/cups/command.types index e03f094..f8eb8b2 100644 --- a/src/cups/command.types +++ b/src/cups/command.types @@ -13,8 +13,7 @@ # 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. +# along with this program. If not, see . # # Define the new application/vnd.cups-command filetype... diff --git a/src/cups/commandtoepson.c b/src/cups/commandtoepson.c index 0582df2..7bac83c 100644 --- a/src/cups/commandtoepson.c +++ b/src/cups/commandtoepson.c @@ -14,8 +14,7 @@ * 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. + * along with this program. If not, see . * * Contents: * diff --git a/src/cups/cups-calibrate.c b/src/cups/cups-calibrate.c index 205157b..de0a9e1 100644 --- a/src/cups/cups-calibrate.c +++ b/src/cups/cups-calibrate.c @@ -16,8 +16,7 @@ * 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. + * along with this program. If not, see . * * Contents: * diff --git a/src/cups/cups-genppd.c b/src/cups/cups-genppd.c new file mode 100644 index 0000000..fde7c46 --- /dev/null +++ b/src/cups/cups-genppd.c @@ -0,0 +1,607 @@ +/* + * PPD file generation program for the CUPS drivers. + * + * Copyright 1993-2008 by Mike Sweet and Robert Krawitz. + * + * 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, see . + * + * Contents: + * + * main() - Process files on the command-line... + * cat_ppd() - Copy the named PPD to stdout. + * generate_ppd() - Generate a PPD file. + * getlangs() - Get a list of available translations. + * help() - Show detailed help. + * is_special_option() - Determine if an option should be grouped. + * list_ppds() - List the available drivers. + * print_group_close() - Close a UI group. + * print_group_open() - Open a new UI group. + * printlangs() - Print list of available translations. + * printmodels() - Print a list of available models. + * usage() - Show program usage. + * write_ppd() - Write a PPD file. + */ + +#include "genppd.h" + +static int generate_ppd(const char *prefix, int verbose, + const stp_printer_t *p, const char *language, + ppd_type_t ppd_type, int use_compression); +static int generate_model_ppds(const char *prefix, int verbose, + const stp_printer_t *printer, + const char *language, int which_ppds, + int use_compression); +static void help(void); +static void printlangs(char** langs); +static void printmodels(int verbose); +static void usage(void); +static gpFile gpopen(const char *path, const char *mode, int use_compression); +static int gpclose(gpFile f, int use_compression); + +/* + * 'main()' - Process files on the command-line... + */ + +int /* O - Exit status */ +main(int argc, /* I - Number of command-line arguments */ + char *argv[]) /* I - Command-line arguments */ +{ + int i; /* Looping var */ + const char *prefix; /* Directory prefix for output */ + const char *language = NULL; /* Language */ + const stp_printer_t *printer; /* Pointer to printer driver */ + int verbose = 0; /* Verbose messages */ + char **langs = NULL; /* Available translations */ + char **models = NULL; /* Models to output, all if NULL */ + int opt_printlangs = 0; /* Print available translations */ + int opt_printmodels = 0;/* Print available models */ + int which_ppds = 2; /* Simplified PPD's = 1, full = 2, + no color opts = 4 */ + unsigned parallel = 1; /* Generate PPD files in parallel */ + unsigned rotor = 0; /* Rotor for generating PPD files in parallel */ + pid_t *subprocesses = NULL; + int parent = 1; +#ifdef HAVE_LIBZ + int use_compression = 1; +#else + int use_compression = 0; +#endif + int skip_duplicate_ppds = 0; + + + /* + * Parse command-line args... + */ + + prefix = CUPS_MODELDIR; + + for (;;) + { + if ((i = getopt(argc, argv, "23hvqc:p:l:LMVd:saNCbZzS")) == -1) + break; + + switch (i) + { + case '2': + cups_ppd_ps_level = 2; + break; + case '3': + cups_ppd_ps_level = 3; + break; + case 'h': + help(); + exit(EXIT_SUCCESS); + break; + case 'v': + verbose = 1; + break; + case 'q': + verbose = 0; + break; + case 'c': + fputs("ERROR: -c option no longer supported!\n", stderr); + break; + case 'p': + prefix = optarg; +# ifdef DEBUG + fprintf(stderr, "DEBUG: prefix: %s\n", prefix); +# endif + break; + case 'l': + language = optarg; + break; + case 'L': + opt_printlangs = 1; + break; + case 'M': + opt_printmodels = 1; + break; + case 'd': + cups_modeldir = optarg; + break; + case 's': + which_ppds = 1; + break; + case 'a': + which_ppds = 3; + break; + case 'C': + which_ppds |= 4; + break; + case 'N': + localize_numbers = !localize_numbers; + break; + case 'V': + printf("cups-genppd version %s, " + "Copyright 1993-2008 by Michael R Sweet and Robert Krawitz.\n\n", + VERSION); + printf("Default CUPS PPD PostScript Level: %d\n", cups_ppd_ps_level); + printf("Default PPD location (prefix): %s\n", CUPS_MODELDIR); + printf("Default base locale directory: %s\n\n", PACKAGE_LOCALE_DIR); + puts("This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License,\n" + "version 2, as published by the Free Software Foundation.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n"); + exit(EXIT_SUCCESS); + break; + case 'b': + use_base_version = 1; + break; + case 'z': +#ifdef HAVE_LIBZ + use_compression = 1; +#endif + break; + case 'Z': +#ifdef HAVE_LIBZ + use_compression = 0; +#endif + break; + case 'S': + skip_duplicate_ppds = 1; + break; + default: + usage(); + exit(EXIT_FAILURE); + break; + } + } +#ifdef HAVE_LIBZ + if (use_compression) + gpext = ".gz"; + else +#endif + gpext = ""; + if (optind < argc) { + int n, numargs; + numargs = argc-optind; + models = stp_malloc((numargs+1) * sizeof(char*)); + for (n=0; n 256) + parallel = 1; + } + if (parallel > 1) + { + subprocesses = stp_malloc(sizeof(pid_t) * parallel); + for (rotor = 0; rotor < parallel; rotor++) + { + pid_t pid = fork(); + if (pid == 0) /* Child */ + { + parent = 0; + break; + } + else if (pid > 0) + subprocesses[rotor] = pid; + else + { + fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); + return 1; + } + } + } + if (models) + { + int n; + for (n=0; models[n]; n++) + { + printer = stp_get_printer_by_driver(models[n]); + if (!printer) + printer = stp_get_printer_by_long_name(models[n]); + + if (n % parallel == rotor && printer) + { + if (printer) + { + if (generate_model_ppds(prefix, verbose, printer, language, + which_ppds, use_compression)) + return 1; + } + else + { + printf("Driver not found: %s\n", models[n]); + return (1); + } + } + } + stp_free(models); + } + else + { + stp_string_list_t *seen_models = NULL; + if (skip_duplicate_ppds) + seen_models = stp_string_list_create(); + + for (i = 0; i < stp_printer_model_count(); i++) + { + printer = stp_get_printer_by_index(i); + if (skip_duplicate_ppds) + { + char model_family[128]; + (void) snprintf(model_family, 127, "%d_%s", + stp_printer_get_model(printer), + stp_printer_get_family(printer)); + if (stp_string_list_is_present(seen_models, model_family)) + continue; + else + stp_string_list_add_string_unsafe(seen_models, model_family, + model_family); + } + + if (i % parallel == rotor && printer) + { + if (! verbose && (i % 100) == 0) + fputc('.',stderr); + if (generate_model_ppds(prefix, verbose, printer, language, + which_ppds, use_compression)) + return 1; + } + } + if (seen_models) + stp_string_list_destroy(seen_models); + } + if (subprocesses) + { + pid_t pid; + do + { + int status; + pid = waitpid(-1, &status, 0); + if (pid > 0 && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) + { + fprintf(stderr, "failed!\n"); + return 1; + } + } while (pid > 0); + stp_free(subprocesses); + } + if (parent && !verbose) + fprintf(stderr, " done.\n"); + + return (0); +} + +static int +generate_model_ppds(const char *prefix, int verbose, + const stp_printer_t *printer, const char *language, + int which_ppds, int use_compression) +{ + if ((which_ppds & 1) && + generate_ppd(prefix, verbose, printer, language, PPD_SIMPLIFIED, + use_compression)) + return (1); + if ((which_ppds & 2) && + generate_ppd(prefix, verbose, printer, language, PPD_STANDARD, + use_compression)) + return (1); + if ((which_ppds & 4) && + generate_ppd(prefix, verbose, printer, language, PPD_NO_COLOR_OPTS, + use_compression)) + return (1); + return 0; +} + +/* + * 'generate_ppd()' - Generate a PPD file. + */ + +static int /* O - Exit status */ +generate_ppd( + const char *prefix, /* I - PPD directory prefix */ + int verbose, /* I - Verbosity level */ + const stp_printer_t *p, /* I - Driver */ + const char *language, /* I - Primary language */ + ppd_type_t ppd_type, /* I - full, simplified, no color */ + int use_compression) /* I - compress output */ +{ + int status; /* Exit status */ + gpFile fp; /* File to write to */ + char filename[1024], /* Filename */ + ppd_location[1024]; /* Installed location */ + struct stat dir; /* Prefix dir status */ + const char *ppd_infix; + + /* + * Skip the PostScript drivers... + */ + + if (!strcmp(stp_printer_get_family(p), "ps") || + !strcmp(stp_printer_get_family(p), "raw")) + return (0); + + /* + * Make sure the destination directory exists... + */ + + if (stat(prefix, &dir) && !S_ISDIR(dir.st_mode)) + { + if (mkdir(prefix, 0777)) + { + printf("cups-genppd: Cannot create directory %s: %s\n", + prefix, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + /* + * The files will be named stp-...ppd, for + * example: + * + * stp-escp2-ex.5.0.ppd + * + * or + * + * stp-escp2-ex.5.0.ppd.gz + */ + + switch (ppd_type) + { + case PPD_SIMPLIFIED: + ppd_infix = ".sim"; + break; + case PPD_NO_COLOR_OPTS: + ppd_infix = ".nc"; + break; + default: + ppd_infix = ""; + } + + snprintf(filename, sizeof(filename) - 1, "%s/stp-%s.%s%s%s%s", + prefix, stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION, + ppd_infix, ppdext, gpext); + + /* + * Open the PPD file... + */ + + if ((fp = gpopen(filename, "wb", use_compression)) == NULL) + { + fprintf(stderr, "cups-genppd: Unable to create file \"%s\" - %s.\n", + filename, strerror(errno)); + return (2); + } + + if (verbose) + fprintf(stderr, "Writing %s...\n", filename); + + snprintf(ppd_location, sizeof(ppd_location), "%s%s%s/%s", + cups_modeldir, + cups_modeldir[strlen(cups_modeldir) - 1] == '/' ? "" : "/", + language ? language : "C", + basename(filename)); + + snprintf(filename, sizeof(filename) - 1, "stp-%s.%s%s%s", + stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION, + ppd_infix, ppdext); + + status = write_ppd(fp, p, language, ppd_location, ppd_type, + basename(filename), use_compression); + + gpclose(fp, use_compression); + + return (status); +} + + +/* + * 'help()' - Show detailed help. + */ + +void +help(void) +{ + puts("Generate Gutenprint PPD files for use with CUPS\n\n"); + usage(); + puts("\nExamples: LANG=de_DE cups-genppd -p ppd -c /usr/share/locale\n" + " cups-genppd -L -c /usr/share/locale\n" + " cups-genppd -M -v\n\n" + "Commands:\n" + " -h Show this help message.\n" + " -L List available translations (message catalogs).\n" + " -M List available printer models.\n" + " -V Show version information and defaults.\n" + " The default is to output PPDs.\n"); + puts("Options:\n" + " -N Localize numbers.\n" + " -l locale Output PPDs translated with messages for locale.\n" + " -p prefix Output PPDs in directory prefix.\n" + " -d prefix Embed directory prefix in PPD file.\n" + " -s Generate simplified PPD files.\n" + " -a Generate all (simplified and full) PPD files.\n" + " -q Quiet mode.\n" + " -v Verbose mode.\n"); + puts( +#ifdef HAVE_LIBZ + " -z Compress PPD files.\n" + " -Z Don't compress PPD files.\n" +#endif + " -S Skip PPD files with duplicate model identifiers.\n" + "models:\n" + " A list of printer models, either the driver or quoted full name.\n"); +} + +/* + * 'usage()' - Show program usage. + */ + +void +usage(void) +{ + puts("Usage: cups-genppd " + "[-l locale] [-p prefix] [-s | -a] [-q] [-v] models...\n" + " cups-genppd -L\n" + " cups-genppd -M [-v]\n" + " cups-genppd -h\n" + " cups-genppd -V\n"); +} + +/* + * 'printlangs()' - Print list of available translations. + */ + +void +printlangs(char **langs) /* I - Languages */ +{ + if (langs) + { + int n = 0; + while (langs && langs[n]) + { + puts(langs[n]); + n++; + } + } + exit(EXIT_SUCCESS); +} + + +/* + * 'printmodels()' - Print a list of available models. + */ + +void +printmodels(int verbose) /* I - Verbosity level */ +{ + const stp_printer_t *p; + int i; + + for (i = 0; i < stp_printer_model_count(); i++) + { + p = stp_get_printer_by_index(i); + if (p && + strcmp(stp_printer_get_family(p), "ps") != 0 && + strcmp(stp_printer_get_family(p), "raw") != 0) + { + if(verbose) + printf("%-20s%s\n", stp_printer_get_driver(p), + stp_printer_get_long_name(p)); + else + printf("%s\n", stp_printer_get_driver(p)); + } + } + exit(EXIT_SUCCESS); +} + +static gpFile +gpopen(const char *path, const char *mode, int use_compression) +{ +#ifdef HAVE_LIBZ + gpFile f = stp_malloc(sizeof(gpfile)); + if (use_compression) + { + f->gzf = gzopen(path, mode); + if (!f->gzf) + { + stp_free(f); + return NULL; + } + return f; + } + else +#endif + { + FILE *fl = fopen(path, mode); +#ifdef HAVE_LIBZ + if (fl) + { + f->f = fl; + return f; + } + else + { + stp_free(f); + return NULL; + } +#else + return fl; +#endif + } +} + +static int +gpclose(gpFile f, int use_compression) +{ + int status; +#ifdef HAVE_LIBZ + if (use_compression) + status = gzclose(f->gzf); + else + status = fclose(f->f); + stp_free(f); +#else + status = fclose(f); +#endif + return status; +} diff --git a/src/cups/cups-genppdupdate.in b/src/cups/cups-genppdupdate.in index 8f0137b..1376884 100644 --- a/src/cups/cups-genppdupdate.in +++ b/src/cups/cups-genppdupdate.in @@ -13,8 +13,7 @@ # 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. +# along with this program. If not, see . use strict; use Getopt::Long; @@ -44,6 +43,7 @@ our $opt_r; # Gutenprint version our $opt_i; # Interactive our $opt_f; # Force upgrade our $opt_l; # Language +our $opt_x; # Allow update across major.minor version my $debug = 0; my $verbose = 0; # Verbose output @@ -213,6 +213,7 @@ sub HELP_MESSAGE($;$$$) { print $fh " -r version Use PPD files for Gutenprint major.minor version.\n"; print $fh " -f Ignore new PPD file safety checks.\n"; print $fh " -i Prompt (interactively) for each PPD file.\n"; + print $fh " -x Allow update across major Gutenprint releases.\n"; print $fh " -l language Language choice (Gutenprint 5.1 or below).\n"; print $fh " Choices: " . join(" ", @languages) . "\n"; print $fh " Or -loriginal to preserve original language\n"; @@ -262,6 +263,7 @@ sub parse_options () { "p=s" => \$opt_p, "P=s" => \$opt_P, "v" => \$opt_v, + "x" => \$opt_x, "N" => \$opt_N, "o=s" => \$opt_o, "r=s" => \$opt_r, @@ -541,14 +543,28 @@ sub update_ppd ($) { return 0; } - my ($ndt, $nopt, $nres, $ndef, $source_data) = get_ppd_data($source_fd, 1, 1, 1, 1, 1); + my ($ndt, $nopt, $nres, $ndef, $source_data, $new_fileversion) = get_ppd_data($source_fd, 1, 1, 1, 1, 1); + my $new_majversion = $new_fileversion; + $new_majversion =~ s/^([[:digit:]]+\.[[:digit:]]).*/$1/; if (! defined $ndt) { print "Unable to retrieve PPD file for $ppd_source_filename!\n"; close ORIG; return 0; } + # Extract the default values from the original PPD... + + seek(ORIG, 0, 0); + + my ($odt, $oopt, $ores, $odef, $ignore, $old_fileversion) = get_ppd_data(ORIG, 1, 0, 1, 1, 0); + my $old_majversion = $old_fileversion; + $old_majversion =~ s/^([[:digit:]]+\.[[:digit:]]).*/$1/; + if ($interactive) { + if ($old_majversion ne $new_majversion) { + print "WARNING: Current PPD file $ppd_source_filename has different version ($old_majversion)\n"; + print " from new PPD file $new_ppd_filename ($new_majversion).\n"; + } print "Update PPD $ppd_source_filename from $new_ppd_filename [nyq]? "; my $input = readline(*STDIN); if ($input =~ /^q/i) { @@ -562,17 +578,16 @@ sub update_ppd ($) { } } - # Extract the default values from the original PPD... - - seek(ORIG, 0, 0); - - my ($odt, $oopt, $ores, $odef) = get_ppd_data(ORIG, 1, 0, 1, 1, 0); - # Close original and temporary files... close ORIG; if (! $server_multicat && ! close $source_fd) { - print "Unable to retrieve new PPD file: $!\n"; + print STDERR "Unable to retrieve new PPD file: $!\n"; + return -1; + } + + if (! $opt_x && !$opt_i && $old_majversion ne $new_majversion) { + print STDERR "Skipping $ppd_source_filename: mismatched file versions (old $old_majversion, new $new_majversion); will not update without -x!\n"; return -1; } @@ -668,7 +683,7 @@ sub update_ppd ($) { print STDOUT "Would update $ppd_source_filename to $ppd_dest_filename using $new_ppd_filename\n"; } } - return 0; + return -1; } if (! $reset_defaults) { @@ -877,6 +892,7 @@ sub get_ppd_data(*$$$$$) { my ($fh, $types, $opts, $resolutions, $defaults, $data) = @_; my (%options, %defaults, %resolution_map, %default_types); my ($fileversion_found) = 0; + my ($fileversion) = ""; my $cur_opt = ""; my (@optionlist); my ($source_data) = ""; @@ -890,7 +906,10 @@ sub get_ppd_data(*$$$$$) { if ($resolutions || $types || $opts || $defaults || $data) { while (<$fh>) { last if $_ eq "*%*%EOFEOF\n"; - $fileversion_found = 1 if (/^\*FileVersion:/); + if (/^\*FileVersion:/) { + $fileversion_found = 1; + ($fileversion) = /^\*FileVersion:\s*"(.*)"$/; + } $source_data .= $_ if ($data); chomp; if (($types || $opts) && m/^\*OpenUI/) { @@ -921,6 +940,6 @@ sub get_ppd_data(*$$$$$) { } } } - return (undef, undef, undef, undef, undef) if (! $fileversion_found); - return (\%default_types, \%options, \%resolution_map, \%defaults, $source_data); + return (undef, undef, undef, undef, undef, undef) if (! $fileversion_found); + return (\%default_types, \%options, \%resolution_map, \%defaults, $source_data, $fileversion); } diff --git a/src/cups/genppd.c b/src/cups/genppd.c index 2ae4124..aa47aad 100644 --- a/src/cups/genppd.c +++ b/src/cups/genppd.c @@ -14,8 +14,7 @@ * 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. + * along with this program. If not, see . * * Contents: * @@ -38,51 +37,22 @@ * Include necessary headers... */ +#include "genppd.h" #pragma GCC diagnostic ignored "-Wformat-nonliteral" -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #if defined(HAVE_VARARGS_H) && !defined(HAVE_STDARG_H) #include #else #include #endif -#ifdef CUPS_DRIVER_INTERFACE -# ifdef HAVE_LIBZ -# undef HAVE_LIBZ -# endif /* HAVE_LIBZ */ -#endif /* CUPS_DRIVER_INTERFACE */ -#ifdef HAVE_LIBZ -#include -static int use_compression = 1; -#endif - -static const char *cups_modeldir = CUPS_MODELDIR; -static const char *gpext = ""; - -#include -#include +const char *ppdext = ".ppd"; +const char *cups_modeldir = CUPS_MODELDIR; +const char *gpext = ""; -#include "i18n.h" +static int use_compression; -static int use_base_version = 0; +int use_base_version = 0; /* * Some applications use the XxYdpi tags rather than the actual @@ -96,13 +66,6 @@ static int use_base_version = 0; #define MAXIMUM_SAFE_PPD_Y_RESOLUTION (720) #define MAXIMUM_SAFE_PPD_X_RESOLUTION (1500) -typedef enum -{ - PPD_STANDARD = 0, - PPD_SIMPLIFIED = 1, - PPD_NO_COLOR_OPTS = 2 -} ppd_type_t; - /* * Note: * @@ -117,8 +80,6 @@ int localize_numbers = 0; * File handling stuff... */ -static const char *ppdext = ".ppd"; - typedef struct /**** Media size values ****/ { const char *name, /* Media size name */ @@ -166,43 +127,13 @@ const char *parameter_level_names[] = _("Extra 5") }; -#ifdef HAVE_LIBZ -typedef union -{ - gzFile gzf; - FILE *f; -} _gpfile; - -typedef _gpfile *gpFile; -#else -#define gpFile FILE * -#endif - /* * Local functions... */ -#ifdef CUPS_DRIVER_INTERFACE -static int cat_ppd(const char *uri); -static int list_ppds(const char *argv0); -#else /* !CUPS_DRIVER_INTERFACE */ -static int generate_ppd(const char *prefix, int verbose, - const stp_printer_t *p, const char *language, - ppd_type_t ppd_type); -static int generate_model_ppds(const char *prefix, int verbose, - const stp_printer_t *printer, - const char *language, int which_ppds); -static void help(void); -static void printlangs(char** langs); -static void printmodels(int verbose); -static void usage(void); -static gpFile gpopen(const char *path, const char *mode); -static int gpclose(gpFile f); -#endif /* !CUPS_DRIVER_INTERFACE */ static int gpputs(gpFile f, const char *s); static int gpprintf(gpFile f, const char *format, ...) __attribute__((format(__printf__, 2, 3))); -static char **getlangs(void); static int is_special_option(const char *name); static void print_group_close(gpFile fp, stp_parameter_class_t p_class, stp_parameter_level_t p_level, @@ -212,9 +143,6 @@ static void print_group_open(gpFile fp, stp_parameter_class_t p_class, stp_parameter_level_t p_level, const char *language, const stp_string_list_t *po); -static int write_ppd(gpFile fp, const stp_printer_t *p, - const char *language, const char *ppd_location, - ppd_type_t ppd_type, const char *filename); /* @@ -222,747 +150,6 @@ static int write_ppd(gpFile fp, const stp_printer_t *p, */ -#ifdef CUPS_DRIVER_INTERFACE - -/* - * 'main()' - Process files on the command-line... - */ - -int /* O - Exit status */ -main(int argc, /* I - Number of command-line arguments */ - char *argv[]) /* I - Command-line arguments */ -{ - /* - * Force POSIX locale, since stp_init incorrectly calls setlocale... - */ - - (void) setenv("LANG", "C", 1); - (void) setenv("LC_ALL", "C", 1); - (void) setenv("LC_NUMERIC", "C", 1); - - /* - * Initialise libgutenprint - */ - - stp_init(); - - /* - * Process command-line... - */ - - if (argc == 2 && !strcmp(argv[1], "list")) - return (list_ppds(argv[0])); - else if (argc == 3 && !strcmp(argv[1], "cat")) - return (cat_ppd(argv[2])); - else if (argc == 2 && !strcmp(argv[1], "org.gutenprint.multicat")) - { - char buf[1024]; - int status = 0; - while (fgets(buf, sizeof(buf) - 1, stdin)) - { - size_t len = strlen(buf); - if (len == 0) - continue; - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - status |= cat_ppd(buf); - fputs("*%*%EOFEOF\n", stdout); - (void) fflush(stdout); - } - } - else if (argc == 2 && !strcmp(argv[1], "VERSION")) - { - printf("%s\n", VERSION); - return (0); - } - else if (argc == 2 && !strcasecmp(argv[1], "org.gutenprint.extensions")) - { - printf("org.gutenprint.multicat"); - return (0); - } - else - { - fprintf(stderr, "Usage: %s list\n", argv[0]); - fprintf(stderr, " %s cat URI\n", argv[0]); - return (1); - } - return (0); -} - - -/* - * 'cat_ppd()' - Copy the named PPD to stdout. - */ - -static int /* O - Exit status */ -cat_ppd(const char *uri) /* I - Driver URI */ -{ - char scheme[64], /* URI scheme */ - userpass[32], /* URI user/pass (unused) */ - hostname[32], /* URI hostname */ - resource[1024]; /* URI resource */ - int port; /* URI port (unused) */ - http_uri_status_t status; /* URI decode status */ - const stp_printer_t *p; /* Printer driver */ - const char *lang = NULL; - char *s; - char filename[1024], /* Filename */ - ppd_location[1024]; /* Installed location */ - const char *infix = ""; - ppd_type_t ppd_type = PPD_STANDARD; - - if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, - scheme, sizeof(scheme), - userpass, sizeof(userpass), - hostname, sizeof(hostname), - &port, resource, sizeof(resource))) - < HTTP_URI_OK) - { - fprintf(stderr, "ERROR: Bad ppd-name \"%s\" (%d)!\n", uri, status); - return (1); - } - - if (strcmp(scheme, "gutenprint." GUTENPRINT_RELEASE_VERSION) != 0) - { - fprintf(stderr, "ERROR: Gutenprint version mismatch!\n"); - return(1); - } - - s = strchr(resource + 1, '/'); - if (s) - { - lang = s + 1; - *s = '\0'; - } - - if ((p = stp_get_printer_by_driver(hostname)) == NULL) - { - fprintf(stderr, "ERROR: Unable to find driver \"%s\"!\n", hostname); - return (1); - } - - if (strcmp(resource + 1, "simple") == 0) - { - infix = ".sim"; - ppd_type = PPD_SIMPLIFIED; - } - else if (strcmp(resource + 1, "nocolor") == 0) - { - infix = ".nc"; - ppd_type = PPD_NO_COLOR_OPTS; - } - - /* - * This isn't really the right thing to do. We really shouldn't - * be embedding filenames in automatically generated PPD files, but - * if the user ever decides to go back from generated PPD files to - * static PPD files we'll need to have this for genppdupdate to work. - */ - snprintf(filename, sizeof(filename) - 1, "stp-%s.%s%s%s", - hostname, GUTENPRINT_RELEASE_VERSION, infix, ppdext); - snprintf(ppd_location, sizeof(ppd_location) - 1, "%s%s%s/ppd/%s%s", - cups_modeldir, - cups_modeldir[strlen(cups_modeldir) - 1] == '/' ? "" : "/", - lang ? lang : "C", - filename, gpext); - - return (write_ppd(stdout, p, lang, ppd_location, ppd_type, filename)); -} - -/* - * 'list_ppds()' - List the available drivers. - */ - -static int /* O - Exit status */ -list_ppds(const char *argv0) /* I - Name of program */ -{ - const char *scheme; /* URI scheme */ - int i; /* Looping var */ - const stp_printer_t *printer; /* Pointer to printer driver */ - - if ((scheme = strrchr(argv0, '/')) != NULL) - scheme ++; - else - scheme = argv0; - - for (i = 0; i < stp_printer_model_count(); i++) - if ((printer = stp_get_printer_by_index(i)) != NULL) - { - const char *device_id; - if (!strcmp(stp_printer_get_family(printer), "ps") || - !strcmp(stp_printer_get_family(printer), "raw")) - continue; - - device_id = stp_printer_get_device_id(printer); - printf("\"%s://%s/expert\" " - "%s " - "\"%s\" " - "\"%s" CUPS_PPD_NICKNAME_STRING VERSION "\" " - "\"%s\"\n", - scheme, stp_printer_get_driver(printer), - "en", - stp_printer_get_manufacturer(printer), - stp_printer_get_long_name(printer), - device_id ? device_id : ""); - -#ifdef GENERATE_SIMPLIFIED_PPDS - printf("\"%s://%s/simple\" " - "%s " - "\"%s\" " - "\"%s" CUPS_PPD_NICKNAME_STRING VERSION " Simplified\" " - "\"%s\"\n", - scheme, stp_printer_get_driver(printer), - "en", - stp_printer_get_manufacturer(printer), - stp_printer_get_long_name(printer), - device_id ? device_id : ""); -#endif - -#ifdef GENERATE_NOCOLOR_PPDS - printf("\"%s://%s/nocolor\" " - "%s " - "\"%s\" " - "\"%s" CUPS_PPD_NICKNAME_STRING VERSION " No color options\" " - "\"%s\"\n", - scheme, stp_printer_get_driver(printer), - "en", - stp_printer_get_manufacturer(printer), - stp_printer_get_long_name(printer), - device_id ? device_id : ""); -#endif - } - - return (0); -} -#endif /* CUPS_DRIVER_INTERFACE */ - -#ifndef CUPS_DRIVER_INTERFACE - -/* - * 'main()' - Process files on the command-line... - */ - -int /* O - Exit status */ -main(int argc, /* I - Number of command-line arguments */ - char *argv[]) /* I - Command-line arguments */ -{ - int i; /* Looping var */ - const char *prefix; /* Directory prefix for output */ - const char *language = NULL; /* Language */ - const stp_printer_t *printer; /* Pointer to printer driver */ - int verbose = 0; /* Verbose messages */ - char **langs = NULL; /* Available translations */ - char **models = NULL; /* Models to output, all if NULL */ - int opt_printlangs = 0; /* Print available translations */ - int opt_printmodels = 0;/* Print available models */ - int which_ppds = 2; /* Simplified PPD's = 1, full = 2, - no color opts = 4 */ - unsigned parallel = 1; /* Generate PPD files in parallel */ - unsigned rotor = 0; /* Rotor for generating PPD files in parallel */ - pid_t *subprocesses = NULL; - int parent = 1; - - /* - * Parse command-line args... - */ - - prefix = CUPS_MODELDIR; - - for (;;) - { - if ((i = getopt(argc, argv, "23hvqc:p:l:LMVd:saNCbZz")) == -1) - break; - - switch (i) - { - case '2': - cups_ppd_ps_level = 2; - break; - case '3': - cups_ppd_ps_level = 3; - break; - case 'h': - help(); - exit(EXIT_SUCCESS); - break; - case 'v': - verbose = 1; - break; - case 'q': - verbose = 0; - break; - case 'c': - fputs("ERROR: -c option no longer supported!\n", stderr); - break; - case 'p': - prefix = optarg; -# ifdef DEBUG - fprintf(stderr, "DEBUG: prefix: %s\n", prefix); -# endif - break; - case 'l': - language = optarg; - break; - case 'L': - opt_printlangs = 1; - break; - case 'M': - opt_printmodels = 1; - break; - case 'd': - cups_modeldir = optarg; - break; - case 's': - which_ppds = 1; - break; - case 'a': - which_ppds = 3; - break; - case 'C': - which_ppds |= 4; - break; - case 'N': - localize_numbers = !localize_numbers; - break; - case 'V': - printf("cups-genppd version %s, " - "Copyright 1993-2008 by Michael R Sweet and Robert Krawitz.\n\n", - VERSION); - printf("Default CUPS PPD PostScript Level: %d\n", cups_ppd_ps_level); - printf("Default PPD location (prefix): %s\n", CUPS_MODELDIR); - printf("Default base locale directory: %s\n\n", PACKAGE_LOCALE_DIR); - puts("This program is free software; you can redistribute it and/or\n" - "modify it under the terms of the GNU General Public License,\n" - "version 2, as published by the Free Software Foundation.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n"); - exit(EXIT_SUCCESS); - break; - case 'b': - use_base_version = 1; - break; - case 'z': -#ifdef HAVE_LIBZ - use_compression = 1; -#endif - break; - case 'Z': -#ifdef HAVE_LIBZ - use_compression = 0; -#endif - break; - default: - usage(); - exit(EXIT_FAILURE); - break; - } - } -#ifdef HAVE_LIBZ - if (use_compression) - gpext = ".gz"; - else -#endif - gpext = ""; - if (optind < argc) { - int n, numargs; - numargs = argc-optind; - models = stp_malloc((numargs+1) * sizeof(char*)); - for (n=0; n 256) - parallel = 1; - } - if (parallel) - { - subprocesses = stp_malloc(sizeof(pid_t) * parallel); - for (rotor = 0; rotor < parallel; rotor++) - { - pid_t pid = fork(); - if (pid == 0) /* Child */ - { - parent = 0; - break; - } - else if (pid > 0) - subprocesses[rotor] = pid; - else - { - fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); - return 1; - } - } - } - if (models) - { - int n; - for (n=0; models[n]; n++) - { - printer = stp_get_printer_by_driver(models[n]); - if (!printer) - printer = stp_get_printer_by_long_name(models[n]); - - if (n % parallel == rotor && printer) - { - if (printer) - { - if (generate_model_ppds(prefix, verbose, printer, language, - which_ppds)) - return 1; - } - else - { - printf("Driver not found: %s\n", models[n]); - return (1); - } - } - } - stp_free(models); - } - else - { - for (i = 0; i < stp_printer_model_count(); i++) - { - printer = stp_get_printer_by_index(i); - - if (i % parallel == rotor && printer) - { - if (! verbose && (i % 50) == 0) - fputc('.',stderr); - if (generate_model_ppds(prefix, verbose, printer, language, - which_ppds)) - return 1; - } - } - } - if (subprocesses) - { - pid_t pid; - do - { - int status; - pid = waitpid(-1, &status, 0); - if (pid > 0 && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) - { - fprintf(stderr, "failed!\n"); - return 1; - } - } while (pid > 0); - stp_free(subprocesses); - } - if (parent && !verbose) - fprintf(stderr, " done.\n"); - - return (0); -} - -static int -generate_model_ppds(const char *prefix, int verbose, - const stp_printer_t *printer, const char *language, - int which_ppds) -{ - if ((which_ppds & 1) && - generate_ppd(prefix, verbose, printer, language, PPD_SIMPLIFIED)) - return (1); - if ((which_ppds & 2) && - generate_ppd(prefix, verbose, printer, language, PPD_STANDARD)) - return (1); - if ((which_ppds & 4) && - generate_ppd(prefix, verbose, printer, language, PPD_NO_COLOR_OPTS)) - return (1); - return 0; -} - -/* - * 'generate_ppd()' - Generate a PPD file. - */ - -static int /* O - Exit status */ -generate_ppd( - const char *prefix, /* I - PPD directory prefix */ - int verbose, /* I - Verbosity level */ - const stp_printer_t *p, /* I - Driver */ - const char *language, /* I - Primary language */ - ppd_type_t ppd_type) /* I - full, simplified, no color */ -{ - int status; /* Exit status */ - gpFile fp; /* File to write to */ - char filename[1024], /* Filename */ - ppd_location[1024]; /* Installed location */ - struct stat dir; /* Prefix dir status */ - const char *ppd_infix; - - /* - * Skip the PostScript drivers... - */ - - if (!strcmp(stp_printer_get_family(p), "ps") || - !strcmp(stp_printer_get_family(p), "raw")) - return (0); - - /* - * Make sure the destination directory exists... - */ - - if (stat(prefix, &dir) && !S_ISDIR(dir.st_mode)) - { - if (mkdir(prefix, 0777)) - { - printf("cups-genppd: Cannot create directory %s: %s\n", - prefix, strerror(errno)); - exit(EXIT_FAILURE); - } - } - - /* - * The files will be named stp-...ppd, for - * example: - * - * stp-escp2-ex.5.0.ppd - * - * or - * - * stp-escp2-ex.5.0.ppd.gz - */ - - switch (ppd_type) - { - case PPD_SIMPLIFIED: - ppd_infix = ".sim"; - break; - case PPD_NO_COLOR_OPTS: - ppd_infix = ".nc"; - break; - default: - ppd_infix = ""; - } - - snprintf(filename, sizeof(filename) - 1, "%s/stp-%s.%s%s%s%s", - prefix, stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION, - ppd_infix, ppdext, gpext); - - /* - * Open the PPD file... - */ - - if ((fp = gpopen(filename, "wb")) == NULL) - { - fprintf(stderr, "cups-genppd: Unable to create file \"%s\" - %s.\n", - filename, strerror(errno)); - return (2); - } - - if (verbose) - fprintf(stderr, "Writing %s...\n", filename); - - snprintf(ppd_location, sizeof(ppd_location), "%s%s%s/%s", - cups_modeldir, - cups_modeldir[strlen(cups_modeldir) - 1] == '/' ? "" : "/", - language ? language : "C", - basename(filename)); - - snprintf(filename, sizeof(filename) - 1, "stp-%s.%s%s%s", - stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION, - ppd_infix, ppdext); - - status = write_ppd(fp, p, language, ppd_location, ppd_type, - basename(filename)); - - gpclose(fp); - - return (status); -} - -/* - * 'help()' - Show detailed help. - */ - -void -help(void) -{ - puts("Generate Gutenprint PPD files for use with CUPS\n\n"); - usage(); - puts("\nExamples: LANG=de_DE cups-genppd -p ppd -c /usr/share/locale\n" - " cups-genppd -L -c /usr/share/locale\n" - " cups-genppd -M -v\n\n" - "Commands:\n" - " -h Show this help message.\n" - " -L List available translations (message catalogs).\n" - " -M List available printer models.\n" - " -V Show version information and defaults.\n" - " The default is to output PPDs.\n"); - puts("Options:\n" - " -N Localize numbers.\n" - " -l locale Output PPDs translated with messages for locale.\n" - " -p prefix Output PPDs in directory prefix.\n" - " -d prefix Embed directory prefix in PPD file.\n" - " -s Generate simplified PPD files.\n" - " -a Generate all (simplified and full) PPD files.\n" - " -q Quiet mode.\n" - " -v Verbose mode.\n"); - puts( -#ifdef HAVE_LIBZ - " -z Compress PPD files.\n" - " -Z Don't compress PPD files.\n" -#endif - "models:\n" - " A list of printer models, either the driver or quoted full name.\n"); -} - -/* - * 'usage()' - Show program usage. - */ - -void -usage(void) -{ - puts("Usage: cups-genppd " - "[-l locale] [-p prefix] [-s | -a] [-q] [-v] models...\n" - " cups-genppd -L\n" - " cups-genppd -M [-v]\n" - " cups-genppd -h\n" - " cups-genppd -V\n"); -} - -/* - * 'printlangs()' - Print list of available translations. - */ - -void -printlangs(char **langs) /* I - Languages */ -{ - if (langs) - { - int n = 0; - while (langs && langs[n]) - { - puts(langs[n]); - n++; - } - } - exit(EXIT_SUCCESS); -} - - -/* - * 'printmodels()' - Print a list of available models. - */ - -void -printmodels(int verbose) /* I - Verbosity level */ -{ - const stp_printer_t *p; - int i; - - for (i = 0; i < stp_printer_model_count(); i++) - { - p = stp_get_printer_by_index(i); - if (p && - strcmp(stp_printer_get_family(p), "ps") != 0 && - strcmp(stp_printer_get_family(p), "raw") != 0) - { - if(verbose) - printf("%-20s%s\n", stp_printer_get_driver(p), - stp_printer_get_long_name(p)); - else - printf("%s\n", stp_printer_get_driver(p)); - } - } - exit(EXIT_SUCCESS); -} - -static gpFile -gpopen(const char *path, const char *mode) -{ -#ifdef HAVE_LIBZ - gpFile f = stp_malloc(sizeof(_gpfile)); - if (use_compression) - { - f->gzf = gzopen(path, mode); - if (!f->gzf) - { - stp_free(f); - return NULL; - } - return f; - } - else -#endif - { - FILE *fl = fopen(path, mode); -#ifdef HAVE_LIBZ - if (fl) - { - f->f = fl; - return f; - } - else - { - stp_free(f); - return NULL; - } -#else - return fl; -#endif - } -} - -static int -gpclose(gpFile f) -{ - int status; -#ifdef HAVE_LIBZ - if (use_compression) - status = gzclose(f->gzf); - else - status = fclose(f->f); - stp_free(f); -#else - status = fclose(f); -#endif - return status; -} - -#endif /* !CUPS_DRIVER_INTERFACE */ - static int gpputs(gpFile f, const char *s) { @@ -1106,7 +293,8 @@ print_ppd_header(gpFile fp, ppd_type_t ppd_type, int model, const char *driver, gpputs(fp, "*% Copyright 1993-2008 by Mike Sweet and Robert Krawitz.\n"); gpputs(fp, "*% This program is free software; you can redistribute it and/or\n"); gpputs(fp, "*% modify it under the terms of the GNU General Public License,\n"); - gpputs(fp, "*% version 2, as published by the Free Software Foundation.\n"); + gpputs(fp, "*% either version 2, or (at your option) any later version,\n"); + gpputs(fp, "*% as published by the Free Software Foundation.\n"); gpputs(fp, "*%\n"); gpputs(fp, "*% This program is distributed in the hope that it will be useful, but\n"); gpputs(fp, "*% WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n"); @@ -1114,8 +302,7 @@ print_ppd_header(gpFile fp, ppd_type_t ppd_type, int model, const char *driver, gpputs(fp, "*% for more details.\n"); gpputs(fp, "*%\n"); gpputs(fp, "*% You should have received a copy of the GNU General Public License\n"); - gpputs(fp, "*% along with this program; if not, write to the Free Software\n"); - gpputs(fp, "*% Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"); + gpputs(fp, "*% along with this program. If not, see .\n"); gpputs(fp, "*%\n"); gpputs(fp, "*FormatVersion: \"4.3\"\n"); if (use_base_version) @@ -1200,7 +387,29 @@ print_ppd_header_3(gpFile fp, ppd_type_t ppd_type, int model, if (strcasecmp(manufacturer, "EPSON") == 0) gpputs(fp, "*cupsFilter: \"application/vnd.cups-command 33 commandtoepson\"\n"); if (device_id) - gpprintf(fp, "*1284DeviceID: \"%s\"\n", device_id); + { + if (strlen(device_id) > 200) + { + char buf[129]; + int bytes_left = strlen(device_id); + int offset = 0; + gpputs(fp, "*1284DeviceID: \""); + while (bytes_left > 0) + { + memset(buf, 0, 129); + strncpy(buf, device_id + offset, 128); + gpputs(fp, buf); + if (bytes_left <= 128) + gpputs(fp, "\""); + gpputs(fp, "\n"); + offset += 128; + bytes_left -= 128; + } + gpputs(fp, "*End\n"); + } + else + gpprintf(fp, "*1284DeviceID: \"%s\"\n", device_id); + } if (!language) { /* @@ -1263,31 +472,32 @@ print_page_sizes(gpFile fp, stp_vars_t *v, int simplified, { const stp_papersize_t *papersize; opt = stp_string_list_param(desc.bounds.str, i); - papersize = stp_get_papersize_by_name(opt->name); - - if (!papersize) + if (strcmp(opt->name, "Custom") == 0) { - printf("Unable to lookup size %s!\n", opt->name); + variable_sizes = 1; continue; } - if (strcmp(opt->name, "Custom") == 0) + papersize = stp_describe_papersize(v, opt->name); + + if (!papersize) { - variable_sizes = 1; + printf("Unable to lookup size %s!\n", opt->name); continue; } + if (simplified && num_opts >= 10 && (!desc.deflt.str || strcmp(opt->name, desc.deflt.str) != 0) && (papersize->paper_unit == PAPERSIZE_ENGLISH_EXTENDED || papersize->paper_unit == PAPERSIZE_METRIC_EXTENDED)) continue; + if (papersize->width <= 0 || papersize->height <= 0) + continue; + width = papersize->width; height = papersize->height; - if (width <= 0 || height <= 0) - continue; - stp_set_string_parameter(v, "PageSize", opt->name); stp_get_media_size(v, &width, &height); @@ -1978,14 +1188,15 @@ print_standard_fonts(gpFile fp) * 'write_ppd()' - Write a PPD file. */ -static int /* O - Exit status */ +int /* O - Exit status */ write_ppd( gpFile fp, /* I - File to write to */ const stp_printer_t *p, /* I - Printer driver */ const char *language, /* I - Primary language */ const char *ppd_location, /* I - Location of PPD file */ ppd_type_t ppd_type, /* I - 1 = simplified options */ - const char *filename) /* I - input filename */ + const char *filename, /* I - input filename */ + int compress) /* I - compress output */ { int i, j, k, l; /* Looping vars */ int num_opts; /* Number of printer options */ @@ -2014,7 +1225,12 @@ write_ppd( const stp_string_list_t *po = stp_i18n_load(language); /* Message catalog */ - + /* + * This is ugly. The right thing would be to pass this through, but + * then all calls to gpputs, gpprintf, etc. and callers would need to + * have this arg. + */ + use_compression = compress; /* * Initialize driver-specific variables... */ @@ -2057,8 +1273,10 @@ write_ppd( } stp_parameter_description_destroy(&desc); - if (stp_check_boolean_parameter(v, "NativeCopies", STP_PARAMETER_ACTIVE)) - nativecopies = stp_get_boolean_parameter(v, "NativeCopies"); + stp_describe_parameter(v, "NativeCopies", &desc); + if (desc.p_type == STP_PARAMETER_TYPE_BOOLEAN) + nativecopies = desc.deflt.boolean; + stp_parameter_description_destroy(&desc); if (nativecopies) gpputs(fp, "*cupsManualCopies: False\n"); @@ -2470,7 +1688,6 @@ write_ppd( print_group_close(fp, j, k, language, po); } } - stp_parameter_list_destroy(param_list); stp_describe_parameter(v, "ImageType", &desc); if (desc.is_active && desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) { @@ -2528,7 +1745,7 @@ write_ppd( { const stp_papersize_t *papersize; opt = stp_string_list_param(desc.bounds.str, i); - papersize = stp_get_papersize_by_name(opt->name); + papersize = stp_describe_papersize(v, opt->name); if (!papersize) continue; @@ -2539,12 +1756,11 @@ write_ppd( */ if (simplified && num_opts >= 10 && + (!desc.deflt.str || strcmp(opt->name, desc.deflt.str) != 0) && (papersize->paper_unit == PAPERSIZE_ENGLISH_EXTENDED || - papersize->paper_unit == PAPERSIZE_METRIC_EXTENDED)) - continue; - - if ((papersize->width <= 0 || papersize->height <= 0) && - strcmp(opt->name, "Custom") != 0) + papersize->paper_unit == PAPERSIZE_METRIC_EXTENDED || + ((papersize->width <= 0 || papersize->height <= 0) && + strcmp(opt->name, "Custom") != 0))) continue; gpprintf(fp, "*%s.PageSize %s/%s: \"\"\n", lang, opt->name, stp_i18n_lookup(po, opt->text)); @@ -2706,8 +1922,6 @@ write_ppd( gpprintf(fp, "*%s.StpiShrinkOutput %s/%s: \"\"\n", lang, "Crop", _("Crop (preserve dimensions)")); gpprintf(fp, "*%s.StpiShrinkOutput %s/%s: \"\"\n", lang, "Expand", _("Expand (use maximum page area)")); - param_list = stp_get_parameter_list(v); - for (j = 0; j <= STP_PARAMETER_CLASS_OUTPUT; j++) { for (k = 0; k <= maximum_level; k++) @@ -2733,7 +1947,6 @@ write_ppd( } } } - stp_parameter_list_destroy(param_list); stp_describe_parameter(v, "ImageType", &desc); if (desc.is_active && desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) { @@ -2753,6 +1966,7 @@ write_ppd( } po = savepo; } + stp_parameter_list_destroy(param_list); if (has_quality_parameter) stp_free(default_resolution); stp_string_list_destroy(resolutions); diff --git a/src/cups/genppd.h b/src/cups/genppd.h new file mode 100644 index 0000000..3452a99 --- /dev/null +++ b/src/cups/genppd.h @@ -0,0 +1,91 @@ +/* + * PPD file generation program for the CUPS drivers. + * + * Copyright 1993-2008 by Mike Sweet and Robert Krawitz. + * + * 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, see . + * + * Contents: + * + * main() - Process files on the command-line... + * cat_ppd() - Copy the named PPD to stdout. + * generate_ppd() - Generate a PPD file. + * getlangs() - Get a list of available translations. + * help() - Show detailed help. + * is_special_option() - Determine if an option should be grouped. + * list_ppds() - List the available drivers. + * print_group_close() - Close a UI group. + * print_group_open() - Open a new UI group. + * printlangs() - Print list of available translations. + * printmodels() - Print a list of available models. + * usage() - Show program usage. + * write_ppd() - Write a PPD file. + */ + +#include "i18n.h" +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern char **getlangs(void); + + +typedef enum +{ + PPD_STANDARD = 0, + PPD_SIMPLIFIED = 1, + PPD_NO_COLOR_OPTS = 2 +} ppd_type_t; + +#ifdef HAVE_LIBZ +#include +#endif + +typedef union +{ +#ifdef HAVE_LIBZ + gzFile gzf; +#endif + FILE *f; +} gpfile; +typedef gpfile *gpFile; + +extern const char *ppdext; +extern const char *cups_modeldir; +extern const char *gpext; +extern int cups_ppd_ps_level; +extern int localize_numbers; +extern int use_base_version; + +extern int write_ppd(gpFile fp, const stp_printer_t *p, + const char *language, const char *ppd_location, + ppd_type_t ppd_type, const char *filename, + int compress); diff --git a/src/cups/gutenprint.c b/src/cups/gutenprint.c new file mode 100644 index 0000000..6ab2194 --- /dev/null +++ b/src/cups/gutenprint.c @@ -0,0 +1,253 @@ +/* + * PPD file generation program for the CUPS drivers. + * + * Copyright 1993-2008 by Mike Sweet and Robert Krawitz. + * + * 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, see . + * + * Contents: + * + * main() - Process files on the command-line... + * cat_ppd() - Copy the named PPD to stdout. + * generate_ppd() - Generate a PPD file. + * getlangs() - Get a list of available translations. + * help() - Show detailed help. + * is_special_option() - Determine if an option should be grouped. + * list_ppds() - List the available drivers. + * print_group_close() - Close a UI group. + * print_group_open() - Open a new UI group. + * printlangs() - Print list of available translations. + * printmodels() - Print a list of available models. + * usage() - Show program usage. + * write_ppd() - Write a PPD file. + */ + +/* + * 'main()' - Process files on the command-line... + */ + +#include "genppd.h" + +static int cat_ppd(const char *uri); +static int list_ppds(const char *argv0); + + +int /* O - Exit status */ +main(int argc, /* I - Number of command-line arguments */ + char *argv[]) /* I - Command-line arguments */ +{ + /* + * Force POSIX locale, since stp_init incorrectly calls setlocale... + */ + + (void) setenv("LANG", "C", 1); + (void) setenv("LC_ALL", "C", 1); + (void) setenv("LC_NUMERIC", "C", 1); + + /* + * Initialise libgutenprint + */ + + stp_init(); + + /* + * Process command-line... + */ + + if (argc == 2 && !strcmp(argv[1], "list")) + return (list_ppds(argv[0])); + else if (argc == 3 && !strcmp(argv[1], "cat")) + return (cat_ppd(argv[2])); + else if (argc == 2 && !strcmp(argv[1], "org.gutenprint.multicat")) + { + char buf[1024]; + int status = 0; + while (fgets(buf, sizeof(buf) - 1, stdin)) + { + size_t len = strlen(buf); + if (len == 0) + continue; + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + status |= cat_ppd(buf); + fputs("*%*%EOFEOF\n", stdout); + (void) fflush(stdout); + } + } + else if (argc == 2 && !strcmp(argv[1], "VERSION")) + { + printf("%s\n", VERSION); + return (0); + } + else if (argc == 2 && !strcasecmp(argv[1], "org.gutenprint.extensions")) + { + printf("org.gutenprint.multicat"); + return (0); + } + else + { + fprintf(stderr, "Usage: %s list\n", argv[0]); + fprintf(stderr, " %s cat URI\n", argv[0]); + return (1); + } + return (0); +} + + +/* + * 'cat_ppd()' - Copy the named PPD to stdout. + */ + +static int /* O - Exit status */ +cat_ppd(const char *uri) /* I - Driver URI */ +{ + char scheme[64], /* URI scheme */ + userpass[32], /* URI user/pass (unused) */ + hostname[32], /* URI hostname */ + resource[1024]; /* URI resource */ + int port; /* URI port (unused) */ + http_uri_status_t status; /* URI decode status */ + const stp_printer_t *p; /* Printer driver */ + const char *lang = NULL; + char *s; + char filename[1024], /* Filename */ + ppd_location[1024]; /* Installed location */ + const char *infix = ""; + ppd_type_t ppd_type = PPD_STANDARD; + gpfile outFD; + + if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, + scheme, sizeof(scheme), + userpass, sizeof(userpass), + hostname, sizeof(hostname), + &port, resource, sizeof(resource))) + < HTTP_URI_OK) + { + fprintf(stderr, "ERROR: Bad ppd-name \"%s\" (%d)!\n", uri, status); + return (1); + } + + if (strcmp(scheme, "gutenprint." GUTENPRINT_RELEASE_VERSION) != 0) + { + fprintf(stderr, "ERROR: Gutenprint version mismatch!\n"); + return(1); + } + + s = strchr(resource + 1, '/'); + if (s) + { + lang = s + 1; + *s = '\0'; + } + + if ((p = stp_get_printer_by_driver(hostname)) == NULL) + { + fprintf(stderr, "ERROR: Unable to find driver \"%s\"!\n", hostname); + return (1); + } + + if (strcmp(resource + 1, "simple") == 0) + { + infix = ".sim"; + ppd_type = PPD_SIMPLIFIED; + } + else if (strcmp(resource + 1, "nocolor") == 0) + { + infix = ".nc"; + ppd_type = PPD_NO_COLOR_OPTS; + } + + /* + * This isn't really the right thing to do. We really shouldn't + * be embedding filenames in automatically generated PPD files, but + * if the user ever decides to go back from generated PPD files to + * static PPD files we'll need to have this for genppdupdate to work. + */ + snprintf(filename, sizeof(filename) - 1, "stp-%s.%s%s%s", + hostname, GUTENPRINT_RELEASE_VERSION, infix, ppdext); + snprintf(ppd_location, sizeof(ppd_location) - 1, "%s%s%s/ppd/%s%s", + cups_modeldir, + cups_modeldir[strlen(cups_modeldir) - 1] == '/' ? "" : "/", + lang ? lang : "C", + filename, gpext); + + outFD.f = stdout; + return (write_ppd(&outFD, p, lang, ppd_location, ppd_type, filename, 0)); +} + +/* + * 'list_ppds()' - List the available drivers. + */ + +static int /* O - Exit status */ +list_ppds(const char *argv0) /* I - Name of program */ +{ + const char *scheme; /* URI scheme */ + int i; /* Looping var */ + const stp_printer_t *printer; /* Pointer to printer driver */ + + if ((scheme = strrchr(argv0, '/')) != NULL) + scheme ++; + else + scheme = argv0; + + for (i = 0; i < stp_printer_model_count(); i++) + if ((printer = stp_get_printer_by_index(i)) != NULL) + { + const char *device_id; + if (!strcmp(stp_printer_get_family(printer), "ps") || + !strcmp(stp_printer_get_family(printer), "raw")) + continue; + + device_id = stp_printer_get_device_id(printer); + printf("\"%s://%s/expert\" " + "%s " + "\"%s\" " + "\"%s" CUPS_PPD_NICKNAME_STRING VERSION "\" " + "\"%s\"\n", + scheme, stp_printer_get_driver(printer), + "en", + stp_printer_get_manufacturer(printer), + stp_printer_get_long_name(printer), + device_id ? device_id : ""); + +#ifdef GENERATE_SIMPLIFIED_PPDS + printf("\"%s://%s/simple\" " + "%s " + "\"%s\" " + "\"%s" CUPS_PPD_NICKNAME_STRING VERSION " Simplified\" " + "\"%s\"\n", + scheme, stp_printer_get_driver(printer), + "en", + stp_printer_get_manufacturer(printer), + stp_printer_get_long_name(printer), + device_id ? device_id : ""); +#endif + +#ifdef GENERATE_NOCOLOR_PPDS + printf("\"%s://%s/nocolor\" " + "%s " + "\"%s\" " + "\"%s" CUPS_PPD_NICKNAME_STRING VERSION " No color options\" " + "\"%s\"\n", + scheme, stp_printer_get_driver(printer), + "en", + stp_printer_get_manufacturer(printer), + stp_printer_get_long_name(printer), + device_id ? device_id : ""); +#endif + } + + return (0); +} diff --git a/src/cups/i18n.c b/src/cups/i18n.c index f032d13..89ff2b6 100644 --- a/src/cups/i18n.c +++ b/src/cups/i18n.c @@ -14,8 +14,7 @@ * 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. + * along with this program. If not, see . * * Contents: * diff --git a/src/cups/i18n.h b/src/cups/i18n.h index 222b2c3..1ab5275 100644 --- a/src/cups/i18n.h +++ b/src/cups/i18n.h @@ -14,8 +14,7 @@ * 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. + * along with this program. If not, see . */ #include diff --git a/src/cups/min-pagesize.in b/src/cups/min-pagesize.in new file mode 100644 index 0000000..06cd148 --- /dev/null +++ b/src/cups/min-pagesize.in @@ -0,0 +1,42 @@ +#!@PERL@ + +# Find smallest page size in PPD file. +# +# Copyright 2018 Robert Krawitz (rlk@alum.mit.edu) +# +# 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, see . +# +# This is faster than using Gutenprint to do this; stp_init() is still +# quite expensive for such small operations. + +use strict; + +open(PPD, $ENV{'PPD'}) || die "Can't open PPD file $ENV{'PPD'}: $!\n"; +my ($min_size_name) = ""; +my ($min_size_dim) = 0; +while () { + if ($min_size_name ne '' && ! /^\*PaperDimension/) { + print "$min_size_name\n"; + exit; + } + next if (! /^\*PaperDimension/); + $_ =~ /^\*PaperDimension *([^\/]+)\/[^\"]*"([0-9.]+) *([0-9.]+)/; + my ($pname) = $1; + my ($x) = $2; + my ($y) = $3; + if ($min_size_name eq "" || ($x * $y < $min_size_dim)) { + $min_size_name = $pname; + $min_size_dim = $x * $y; + } +} diff --git a/src/cups/rastertogutenprint.c b/src/cups/rastertogutenprint.c new file mode 100644 index 0000000..5e6e8ba --- /dev/null +++ b/src/cups/rastertogutenprint.c @@ -0,0 +1,1748 @@ +/* + * Gutenprint based raster filter for the Common UNIX Printing System. + * + * Copyright 1993-2008 by Mike Sweet. + * + * 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, see . + * + * Contents: + * + * main() - Main entry and processing of driver. + * cups_writefunc() - Write data to a file... + * cancel_job() - Cancel the current job... + * Image_get_appname() - Get the application we are running. + * Image_get_row() - Get one row of the image. + * Image_height() - Return the height of an image. + * Image_init() - Initialize an image. + * Image_conclude() - Close the progress display. + * Image_width() - Return the width of an image. + */ + +/* + * Include necessary headers... + */ + +#if 0 +#define ENABLE_CUPS_LOAD_SAVE_OPTIONS +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIMITS_H +#include +#endif +#include "i18n.h" +#include + +/* Solaris with gcc has problems because gcc's limits.h doesn't #define */ +/* this */ +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +/* + * Structure for page raster data... + */ + +#if (CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1)) +#define CUPS_HEADER_T cups_page_header2_t +#define CUPS_READ_HEADER cupsRasterReadHeader2 +#else +#define CUPS_HEADER_T cups_page_header_t +#define CUPS_READ_HEADER cupsRasterReadHeader +#endif + +typedef struct +{ + cups_raster_t *ras; /* Raster stream to read from */ + int page; /* Current page number */ + int row; /* Current row number */ + int left; + int right; + int bottom; + int top; + int width; + int height; + int left_trim; + int right_trim; + int top_trim; + int bottom_trim; + int adjusted_width; + int adjusted_height; + stp_dimension_t d_left; + stp_dimension_t d_right; + stp_dimension_t d_bottom; + stp_dimension_t d_top; + stp_dimension_t d_width; + stp_dimension_t d_height; + stp_dimension_t d_left_trim; + stp_dimension_t d_right_trim; + stp_dimension_t d_bottom_trim; + stp_dimension_t d_top_trim; + int last_percent; + int shrink_to_fit; + CUPS_HEADER_T header; /* Page header from file */ +} cups_image_t; + +static void cups_writefunc(void *file, const char *buf, size_t bytes); +static void cups_errfunc(void *file, const char *buf, size_t bytes); +static void cups_dbgfunc(void *file, const char *buf, size_t bytes); +static void cancel_job(int sig); +static const char *Image_get_appname(stp_image_t *image); +static stp_image_status_t Image_get_row(stp_image_t *image, + unsigned char *data, + size_t byte_limit, int row); +static int Image_height(stp_image_t *image); +static int Image_width(stp_image_t *image); +static void Image_conclude(stp_image_t *image); +static void Image_init(stp_image_t *image); + +static stp_image_t theImage = +{ + Image_init, + NULL, /* reset */ + Image_width, + Image_height, + Image_get_row, + Image_get_appname, + Image_conclude, + NULL +}; + +static volatile stp_image_status_t Image_status = STP_IMAGE_STATUS_OK; +static double total_bytes_printed = 0; +static int print_messages_as_errors = 0; +static int suppress_messages = 0; +static int suppress_verbose_messages = 0; +static const stp_string_list_t *po = NULL; +#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS +static const char *save_file_name = NULL; +static const char *load_file_name = NULL; +#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ + +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +static inline void * +cast_safe(const void *ptr) +{ + return (void *)ptr; +} +#pragma GCC diagnostic pop + +static void +set_string_parameter(stp_vars_t *v, const char *name, const char *val) +{ + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set special string %s to %s\n", name, val); + stp_set_string_parameter(v, name, val); +} + + +static void +set_special_parameter(stp_vars_t *v, const char *name, int choice) +{ + stp_parameter_t desc; + stp_describe_parameter(v, name, &desc); + if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) + { +#if 0 + if (stp_check_string_parameter(v, name, STP_PARAMETER_ACTIVE)) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Not overriding special parameter %s (%s)\n", + name, stp_get_string_parameter(v, name)); + } + else +#endif + if (choice < 0) + { + stp_clear_string_parameter(v, name); + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Clear special parameter %s\n", + name); + } + else if (choice >= stp_string_list_count(desc.bounds.str)) + { + if (! suppress_messages) + stp_i18n_printf(po, _("ERROR: Unable to set Gutenprint option %s " + "(%d > %d)!\n"), name, choice, + stp_string_list_count(desc.bounds.str)); + } + else + { + stp_set_string_parameter + (v, name, stp_string_list_param(desc.bounds.str, choice)->name); + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set special parameter %s to choice %d (%s)\n", + name, choice, + stp_string_list_param(desc.bounds.str, choice)->name); + } + } + else + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Unable to set special %s: not a string\n", + name); + } + stp_parameter_description_destroy(&desc); +} + +static void +print_debug_block(const stp_vars_t *v, const cups_image_t *cups) +{ + fprintf(stderr, "DEBUG: Gutenprint: Page data:\n"); + fprintf(stderr, "DEBUG: Gutenprint: MediaClass = \"%s\"\n", cups->header.MediaClass); + fprintf(stderr, "DEBUG: Gutenprint: MediaColor = \"%s\"\n", cups->header.MediaColor); + fprintf(stderr, "DEBUG: Gutenprint: MediaType = \"%s\"\n", cups->header.MediaType); + fprintf(stderr, "DEBUG: Gutenprint: OutputType = \"%s\"\n", cups->header.OutputType); + + fprintf(stderr, "DEBUG: Gutenprint: AdvanceDistance = %d\n", cups->header.AdvanceDistance); + fprintf(stderr, "DEBUG: Gutenprint: AdvanceMedia = %d\n", cups->header.AdvanceMedia); + fprintf(stderr, "DEBUG: Gutenprint: Collate = %d\n", cups->header.Collate); + fprintf(stderr, "DEBUG: Gutenprint: CutMedia = %d\n", cups->header.CutMedia); + fprintf(stderr, "DEBUG: Gutenprint: Duplex = %d\n", cups->header.Duplex); + fprintf(stderr, "DEBUG: Gutenprint: HWResolution = [ %d %d ]\n", cups->header.HWResolution[0], + cups->header.HWResolution[1]); + fprintf(stderr, "DEBUG: Gutenprint: ImagingBoundingBox = [ %d %d %d %d ]\n", + cups->header.ImagingBoundingBox[0], cups->header.ImagingBoundingBox[1], + cups->header.ImagingBoundingBox[2], cups->header.ImagingBoundingBox[3]); + fprintf(stderr, "DEBUG: Gutenprint: InsertSheet = %d\n", cups->header.InsertSheet); + fprintf(stderr, "DEBUG: Gutenprint: Jog = %d\n", cups->header.Jog); + fprintf(stderr, "DEBUG: Gutenprint: LeadingEdge = %d\n", cups->header.LeadingEdge); + fprintf(stderr, "DEBUG: Gutenprint: Margins = [ %d %d ]\n", cups->header.Margins[0], + cups->header.Margins[1]); + fprintf(stderr, "DEBUG: Gutenprint: ManualFeed = %d\n", cups->header.ManualFeed); + fprintf(stderr, "DEBUG: Gutenprint: MediaPosition = %d\n", cups->header.MediaPosition); + fprintf(stderr, "DEBUG: Gutenprint: MediaWeight = %d\n", cups->header.MediaWeight); + fprintf(stderr, "DEBUG: Gutenprint: MirrorPrint = %d\n", cups->header.MirrorPrint); + fprintf(stderr, "DEBUG: Gutenprint: NegativePrint = %d\n", cups->header.NegativePrint); + fprintf(stderr, "DEBUG: Gutenprint: NumCopies = %d\n", cups->header.NumCopies); + fprintf(stderr, "DEBUG: Gutenprint: Orientation = %d\n", cups->header.Orientation); + fprintf(stderr, "DEBUG: Gutenprint: OutputFaceUp = %d\n", cups->header.OutputFaceUp); + fprintf(stderr, "DEBUG: Gutenprint: PageSize = [ %d %d ]\n", cups->header.PageSize[0], + cups->header.PageSize[1]); + fprintf(stderr, "DEBUG: Gutenprint: Separations = %d\n", cups->header.Separations); + fprintf(stderr, "DEBUG: Gutenprint: TraySwitch = %d\n", cups->header.TraySwitch); + fprintf(stderr, "DEBUG: Gutenprint: Tumble = %d\n", cups->header.Tumble); + fprintf(stderr, "DEBUG: Gutenprint: cupsWidth = %d\n", cups->header.cupsWidth); + fprintf(stderr, "DEBUG: Gutenprint: cupsHeight = %d\n", cups->header.cupsHeight); + fprintf(stderr, "DEBUG: Gutenprint: cups->width = %d\n", cups->width); + fprintf(stderr, "DEBUG: Gutenprint: cups->height = %d\n", cups->height); + fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_width = %d\n", cups->adjusted_width); + fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_height = %d\n", cups->adjusted_height); + fprintf(stderr, "DEBUG: Gutenprint: cupsMediaType = %d\n", cups->header.cupsMediaType); + fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerColor = %d\n", cups->header.cupsBitsPerColor); + fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerPixel = %d\n", cups->header.cupsBitsPerPixel); + fprintf(stderr, "DEBUG: Gutenprint: cupsBytesPerLine = %d\n", cups->header.cupsBytesPerLine); + fprintf(stderr, "DEBUG: Gutenprint: cupsColorOrder = %d\n", cups->header.cupsColorOrder); + fprintf(stderr, "DEBUG: Gutenprint: cupsColorSpace = %d\n", cups->header.cupsColorSpace); + fprintf(stderr, "DEBUG: Gutenprint: cupsCompression = %d\n", cups->header.cupsCompression); + fprintf(stderr, "DEBUG: Gutenprint: cupsRowCount = %d\n", cups->header.cupsRowCount); + fprintf(stderr, "DEBUG: Gutenprint: cupsRowFeed = %d\n", cups->header.cupsRowFeed); + fprintf(stderr, "DEBUG: Gutenprint: cupsRowStep = %d\n", cups->header.cupsRowStep); + fprintf(stderr, "DEBUG: Gutenprint: shrink page to fit %d\n", cups->shrink_to_fit); + stp_vars_print_error(v, "DEBUG"); + fprintf(stderr, "DEBUG: Gutenprint: End page data\n"); +} + +static int +printer_supports_bw(const stp_vars_t *v) +{ + stp_parameter_t desc; + int status = 0; + stp_describe_parameter(v, "PrintingMode", &desc); + if (stp_string_list_is_present(desc.bounds.str, "BW")) + status = 1; + stp_parameter_description_destroy(&desc); + return status; +} + +static void +validate_options(stp_vars_t *v, cups_image_t *cups) +{ + stp_parameter_list_t params = stp_get_parameter_list(v); + int nparams = stp_parameter_list_count(params); + int i; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Validating options\n"); + for (i = 0; i < nparams; i++) + { + const stp_parameter_t *param = stp_parameter_list_param(params, i); + stp_parameter_t desc; + stp_describe_parameter(v, param->name, &desc); + if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) + { + if (!stp_string_list_is_present + (desc.bounds.str, stp_get_string_parameter(v, desc.name))) + { + if (! suppress_messages) + { + const char *val = stp_get_string_parameter(v, desc.name); + fprintf(stderr, "DEBUG: Gutenprint: Clearing string %s (%s)\n", + desc.name, val ? val : "(null)"); + } + stp_clear_string_parameter(v, desc.name); + if (!desc.read_only && desc.is_mandatory && desc.is_active) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Setting default string %s to %s\n", + desc.name, desc.deflt.str ? desc.deflt.str : "(null)"); + stp_set_string_parameter(v, desc.name, desc.deflt.str); + if (strcmp(desc.name, "PageSize") == 0) + { + const stp_papersize_t *ps = + stp_describe_papersize(v, desc.deflt.str); + if (ps->width > 0) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Setting page width to %.3f\n", + ps->width); + if (ps->width < stp_get_page_width(v)) + stp_set_page_width(v, ps->width); + } + if (ps->height > 0) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Setting page height to %.3f\n", + ps->height); + if (ps->height < stp_get_page_height(v)) + stp_set_page_height(v, ps->height); + } + } + } + } + } + stp_parameter_description_destroy(&desc); + } + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Done validating options\n"); + stp_parameter_list_destroy(params); +} + +static stp_vars_t * +initialize_page(cups_image_t *cups, const stp_vars_t *default_settings, + const char *page_size_name) +{ + stp_dimension_t tmp_left, tmp_right, tmp_top, tmp_bottom; + stp_vars_t *v = stp_vars_create_copy(default_settings); + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Initialize page\n"); + + if (cups->header.cupsBitsPerColor == 16) + set_string_parameter(v, "ChannelBitDepth", "16"); + else + set_string_parameter(v, "ChannelBitDepth", "8"); + switch (cups->header.cupsColorSpace) + { + case CUPS_CSPACE_W : + /* DyeSub photo printers don't support black & white ink! */ + if (printer_supports_bw(v)) + set_string_parameter(v, "PrintingMode", "BW"); + set_string_parameter(v, "InputImageType", "Whitescale"); + break; + case CUPS_CSPACE_K : + /* DyeSub photo printers don't support black & white ink! */ + if (printer_supports_bw(v)) + set_string_parameter(v, "PrintingMode", "BW"); + set_string_parameter(v, "InputImageType", "Grayscale"); + break; + case CUPS_CSPACE_RGB : + set_string_parameter(v, "PrintingMode", "Color"); + set_string_parameter(v, "InputImageType", "RGB"); + break; + case CUPS_CSPACE_CMY : + set_string_parameter(v, "PrintingMode", "Color"); + set_string_parameter(v, "InputImageType", "CMY"); + break; + case CUPS_CSPACE_CMYK : + set_string_parameter(v, "PrintingMode", "Color"); + set_string_parameter(v, "InputImageType", "CMYK"); + break; + case CUPS_CSPACE_KCMY : + set_string_parameter(v, "PrintingMode", "Color"); + set_string_parameter(v, "InputImageType", "KCMY"); + break; + default : + stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace " + "(%d)!\n"), cups->header.cupsColorSpace); + break; + } + + set_special_parameter(v, "Resolution", cups->header.cupsCompression - 1); + + set_special_parameter(v, "Quality", cups->header.cupsRowFeed - 1); + + if (strlen(cups->header.MediaClass) > 0) + set_string_parameter(v, "InputSlot", cups->header.MediaClass); + + if (strlen(cups->header.MediaType) > 0) + set_string_parameter(v, "MediaType", cups->header.MediaType); + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: PageSize = %dx%d\n", cups->header.PageSize[0], + cups->header.PageSize[1]); + + if (page_size_name) + { + if (strcmp(page_size_name, "Custom") == 0) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Using custom page size for (%d, %d)\n", + cups->header.PageSize[1], cups->header.PageSize[0]); + stp_set_page_width(v, cups->header.PageSize[0]); + stp_set_page_height(v, cups->header.PageSize[1]); + } + else if (stp_describe_papersize(v, page_size_name)) + { + stp_dimension_t width, height; + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Using page size %s with (%d, %d)\n", + page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]); + set_string_parameter(v, "PageSize", page_size_name); + stp_get_media_size(v, &width, &height); + if (width > 0) + stp_set_page_width(v, width); + else + stp_set_page_width(v, cups->header.PageSize[0]); + if (height > 0) + stp_set_page_height(v, height); + else + stp_set_page_height(v, cups->header.PageSize[1]); + } + else + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Can't find page size %s with (%d, %d), using custom page size\n", + page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]); + stp_set_page_width(v, cups->header.PageSize[0]); + stp_set_page_height(v, cups->header.PageSize[1]); + } + } + else + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: No named media size for (%d, %d)\n", + cups->header.PageSize[1], cups->header.PageSize[0]); + stp_set_page_width(v, cups->header.PageSize[0]); + stp_set_page_height(v, cups->header.PageSize[1]); + } + + /* + * Duplex + * Note that the names MUST match those in the printer driver(s) + */ + + if (cups->header.Duplex != 0) + { + if (cups->header.Tumble != 0) + set_string_parameter(v, "Duplex", "DuplexTumble"); + else + set_string_parameter(v, "Duplex", "DuplexNoTumble"); + } + + cups->shrink_to_fit = + (stp_check_int_parameter(v, "CUPSShrinkPage", STP_PARAMETER_ACTIVE) ? + stp_get_int_parameter(v, "CUPSShrinkPage") : 0); + + set_string_parameter(v, "JobMode", "Job"); + validate_options(v, cups); + stp_get_media_size(v, &(cups->d_width), &(cups->d_height)); + stp_get_maximum_imageable_area(v, &tmp_left, &tmp_right, + &tmp_bottom, &tmp_top); + stp_get_imageable_area(v, &(cups->d_left), &(cups->d_right), + &(cups->d_bottom), &(cups->d_top)); + if (! suppress_messages) + { + fprintf(stderr, "DEBUG: Gutenprint: limits w %.3f l %.3f r %.3f h %.3f t %.3f b %.3f\n", + cups->d_width, cups->d_left, cups->d_right, cups->d_height, cups->d_top, cups->d_bottom); + fprintf(stderr, "DEBUG: Gutenprint: max limits l %.3f r %.3f t %.3f b %.3f\n", + tmp_left, tmp_right, tmp_top, tmp_bottom); + } + + if (tmp_left < 0) + tmp_left = 0; + if (tmp_top < 0) + tmp_top = 0; + if (tmp_right > tmp_left + cups->d_width) + tmp_right = cups->d_width; + if (tmp_bottom > tmp_top + cups->d_height) + tmp_bottom = cups->d_height; + if (tmp_left < cups->d_left) + { + if (cups->shrink_to_fit != 1) + { + cups->d_left_trim = cups->d_left - tmp_left; + tmp_left = cups->d_left; + } + else + cups->d_left_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: left margin %.3f\n", cups->d_left_trim); + } + else + { + cups->d_left_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Adjusting left margin from %.3f to %.3f\n", + cups->d_left, tmp_left); + cups->d_left = tmp_left; + } + if (tmp_right > cups->d_right) + { + if (cups->shrink_to_fit != 1) + { + cups->d_right_trim = tmp_right - cups->d_right; + tmp_right = cups->d_right; + } + else + cups->d_right_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: right margin %.3f\n", cups->d_right_trim); + } + else + { + cups->d_right_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Adjusting right margin from %.3f to %.3f\n", + cups->d_right, tmp_right); + cups->d_right = tmp_right; + } + if (tmp_top < cups->d_top) + { + if (cups->shrink_to_fit != 1) + { + cups->d_top_trim = cups->d_top - tmp_top; + tmp_top = cups->d_top; + } + else + cups->d_top_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: top margin %.3f\n", cups->d_top_trim); + } + else + { + cups->d_top_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Adjusting top margin from %.3f to %.3f\n", + cups->d_top, tmp_top); + cups->d_top = tmp_top; + } + if (tmp_bottom > cups->d_bottom) + { + if (cups->shrink_to_fit != 1) + { + cups->d_bottom_trim = tmp_bottom - cups->d_bottom; + tmp_bottom = cups->d_bottom; + } + else + cups->d_bottom_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: bottom margin %.3f\n", cups->d_bottom_trim); + } + else + { + cups->d_bottom_trim = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Adjusting bottom margin from %.3f to %.3f\n", + cups->d_bottom, tmp_bottom); + cups->d_bottom = tmp_bottom; + } + + if (cups->shrink_to_fit == 2) + { + stp_dimension_t t_left, t_right, t_bottom, t_top; + stp_get_imageable_area(v, &(t_left), &(t_right), &(t_bottom), &(t_top)); + stp_set_width(v, t_right - t_left); + stp_set_height(v, t_bottom - t_top); + stp_set_left(v, t_left); + stp_set_top(v, t_top); + } + else + { + stp_set_width(v, cups->d_right - cups->d_left); + stp_set_height(v, cups->d_bottom - cups->d_top); + stp_set_left(v, cups->d_left); + stp_set_top(v, cups->d_top); + } + + cups->d_right = cups->d_width - cups->d_right; + if (cups->shrink_to_fit == 1) + cups->d_width = tmp_right - tmp_left; + else + cups->d_width = cups->d_width - cups->d_left - cups->d_right; + cups->width = cups->header.HWResolution[0] * cups->d_width / 72; + cups->left = cups->header.HWResolution[0] * cups->d_left / 72; + cups->right = cups->header.HWResolution[0] * cups->d_right / 72; + cups->left_trim = cups->header.HWResolution[0] * cups->d_left_trim / 72; + cups->right_trim = cups->header.HWResolution[0] * cups->d_right_trim / 72; + cups->adjusted_width = cups->width; + if (cups->adjusted_width > cups->header.cupsWidth) + cups->adjusted_width = cups->header.cupsWidth; + + cups->d_bottom = cups->d_height - cups->d_bottom; + if (cups->shrink_to_fit == 1) + cups->d_height = tmp_bottom - tmp_top; + else + cups->d_height = cups->d_height - cups->d_top - cups->d_bottom; + cups->height = cups->header.HWResolution[1] * cups->d_height / 72; + cups->top = cups->header.HWResolution[1] * cups->d_top / 72; + cups->bottom = cups->header.HWResolution[1] * cups->d_bottom / 72; + cups->top_trim = cups->header.HWResolution[1] * cups->d_top_trim / 72; + cups->bottom_trim = cups->header.HWResolution[1] * cups->d_bottom_trim / 72; + cups->adjusted_height = cups->height; + if (cups->adjusted_height > cups->header.cupsHeight) + cups->adjusted_height = cups->header.cupsHeight; + if (! suppress_messages) + { + fprintf(stderr, "DEBUG: Gutenprint: CUPS settings w %d l %d r %d h %d t %d b %d\n", + cups->width, cups->left, cups->right, + cups->height, cups->top, cups->bottom); + fprintf(stderr, "DEBUG: Gutenprint: adjusted w %d h %d\n", + cups->adjusted_width, cups->adjusted_height); + + } + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: End initialize page\n"); + return v; +} + +static void +purge_excess_data(cups_image_t *cups) +{ + char *buffer = stp_malloc(cups->header.cupsBytesPerLine); + if (buffer) + { + if (! suppress_messages && ! suppress_verbose_messages ) + fprintf(stderr, "DEBUG2: Gutenprint: Purging %d row%s\n", + cups->header.cupsHeight - cups->row, + ((cups->header.cupsHeight - cups->row) == 1 ? "" : "s")); + while (cups->row < cups->header.cupsHeight) + { + cupsRasterReadPixels(cups->ras, (unsigned char *)buffer, + cups->header.cupsBytesPerLine); + cups->row ++; + } + } + stp_free(buffer); +} + +static void +set_all_options(stp_vars_t *v, cups_option_t *options, int num_options, + ppd_file_t *ppd) +{ + stp_parameter_list_t params = stp_get_parameter_list(v); + int nparams = stp_parameter_list_count(params); + int i; + const char *val; /* CUPS option value */ + ppd_option_t *ppd_option; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set options:\n"); + val = cupsGetOption("StpiShrinkOutput", num_options, options); + if (!val) + { + ppd_option = ppdFindOption(ppd, "StpiShrinkOutput"); + if (ppd_option) + val = ppd_option->defchoice; + } + if (val) + { + if (!strcasecmp(val, "crop")) + stp_set_int_parameter(v, "CUPSShrinkPage", 0); + else if (!strcasecmp(val, "expand")) + stp_set_int_parameter(v, "CUPSShrinkPage", 2); + else + stp_set_int_parameter(v, "CUPSShrinkPage", 1); + } + else + stp_set_int_parameter(v, "CUPSShrinkPage", 1); + for (i = 0; i < nparams; i++) + { + const stp_parameter_t *param = stp_parameter_list_param(params, i); + stp_parameter_t desc; + char *ppd_option_name = stp_malloc(strlen(param->name) + 8); /* StpFineFOO\0 */ + + stp_describe_parameter(v, param->name, &desc); + if (desc.p_type == STP_PARAMETER_TYPE_DOUBLE) + { + sprintf(ppd_option_name, "Stp%s", desc.name); + val = cupsGetOption(ppd_option_name, num_options, options); + if (!val) + { + ppd_option = ppdFindOption(ppd, ppd_option_name); + if (ppd_option) + val = ppd_option->defchoice; + } + if (val && !strncasecmp(val, "Custom.", 7)) + { + double dval = atof(val + 7); + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f\n", + desc.name, dval); + if (dval > desc.bounds.dbl.upper) + dval = desc.bounds.dbl.upper; + stp_set_float_parameter(v, desc.name, dval); + } + else if (val && strlen(val) > 0 && strcmp(val, "None") != 0) + { + double fine_val = 0; + if (strchr(val, (int) '.')) + { + fine_val = atof(val); + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f (%s)\n", + desc.name, fine_val, val); + } + else + { + double coarse_val = atof(val) * 0.001; + sprintf(ppd_option_name, "StpFine%s", desc.name); + val = cupsGetOption(ppd_option_name, num_options, options); + if (!val) + { + ppd_option = ppdFindOption(ppd, ppd_option_name); + if (ppd_option) + val = ppd_option->defchoice; + } + if (val && strlen(val) > 0 && strcmp(val, "None") != 0) + fine_val = atof(val) * 0.001; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f + %f\n", + desc.name, coarse_val, fine_val); + fine_val += coarse_val; + } + if (fine_val > desc.bounds.dbl.upper) + fine_val = desc.bounds.dbl.upper; + if (fine_val < desc.bounds.dbl.lower) + fine_val = desc.bounds.dbl.lower; + stp_set_float_parameter(v, desc.name, fine_val); + } + } + else + { + sprintf(ppd_option_name, "Stp%s", desc.name); + val = cupsGetOption(ppd_option_name, num_options, options); + if (!val) + { + ppd_option = ppdFindOption(ppd, ppd_option_name); + if (ppd_option) + val = ppd_option->defchoice; + } + if (val && ((strlen(val) > 0 && strcmp(val, "None") != 0) || + (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST))) + { + stp_curve_t *curve; + stp_raw_t *raw; + switch (desc.p_type) + { + case STP_PARAMETER_TYPE_STRING_LIST: + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set string %s to %s\n", + desc.name, val); + set_string_parameter(v, desc.name, val); + break; + case STP_PARAMETER_TYPE_INT: + if (!strncasecmp(val, "Custom.", 7)) + val += 7; + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set int %s to %s (%d)\n", + desc.name, val, atoi(val)); + stp_set_int_parameter(v, desc.name, atoi(val)); + break; + case STP_PARAMETER_TYPE_DIMENSION: + if (!strncasecmp(val, "Custom.", 7)) + val += 7; + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set dimension %s to %s (%d)\n", + desc.name, val, atoi(val)); + + stp_set_dimension_parameter(v, desc.name, atoi(val)); + break; + case STP_PARAMETER_TYPE_BOOLEAN: + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set bool %s to %s (%d)\n", + desc.name, val, strcasecmp(val, "true") == 0 ? 1 : 0); + stp_set_boolean_parameter + (v, desc.name, strcasecmp(val, "true") == 0 ? 1 : 0); + break; + case STP_PARAMETER_TYPE_CURVE: + curve = stp_curve_create_from_string(val); + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set curve %s to %s\n", + desc.name, curve ? val : "(NULL)"); + if (curve) + { + stp_set_curve_parameter(v, desc.name, curve); + stp_curve_destroy(curve); + } + break; + case STP_PARAMETER_TYPE_RAW: /* figure this out later, too */ + raw = stp_xmlstrtoraw(val); + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Set raw %s to %s\n", + desc.name, raw ? val : "(NULL)"); + if (raw) + { + stp_set_raw_parameter(v, desc.name, raw->data, raw->bytes); + stp_free(cast_safe(raw->data)); + stp_free(raw); + } + break; + case STP_PARAMETER_TYPE_FILE: /* Probably not, security hole */ + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Ignoring option %s %s type %d\n", + desc.name, val, desc.p_type); + break; + default: + break; + } + } + else if (val) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to '%s'\n", + desc.name, val); + } + else + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to (null)\n", + desc.name); + } + } + stp_parameter_description_destroy(&desc); + stp_free(ppd_option_name); + } + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: End options\n"); + stp_parameter_list_destroy(params); +} + +#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS +static void +save_options(const char *save_name, const stp_vars_t *v) +{ + FILE *f_options; + int i; + stp_vars_t *c = stp_vars_create(); + stp_parameter_list_t params = stp_get_parameter_list(v); + stp_parameter_t desc; + stp_mxml_node_t *mxml = NULL; + int param_count; + + if (!params) + { + stp_vars_destroy(c); + return; + } + f_options = fopen(save_name, "w"); + if (!f_options) + { + stp_parameter_list_destroy(params); + stp_vars_destroy(c); + return; + } + param_count = stp_parameter_list_count(params); + stp_set_driver(c, stp_get_driver(v)); + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Saving parameters to %s\n", save_name); + for (i = 0; i < param_count; i++) + { + const stp_parameter_t *lparam = + stp_parameter_list_param(params, i); + stp_describe_parameter(v, lparam->name, &desc); + if (desc.read_only || !strcmp(desc.name, "ChannelBitDepth") || + !stp_parameter_has_category_value(v, &desc, "Color", "Yes")) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping non-color %s\n", + desc.name); + stp_parameter_description_destroy(&desc); + continue; + } + switch (desc.p_type) + { + case STP_PARAMETER_TYPE_STRING_LIST: + if (stp_check_string_parameter(v, desc.name, + STP_PARAMETER_DEFAULTED)) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING string %s %s\n", + desc.name, stp_get_string_parameter(v, desc.name)); + stp_set_string_parameter(c, desc.name, + stp_get_string_parameter(v, desc.name)); + } + else if (desc.is_mandatory) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted string %s %s\n", + desc.name, desc.deflt.str); + stp_set_string_parameter(c, desc.name, desc.deflt.str); + } + else if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping string %s\n", desc.name); + break; + case STP_PARAMETER_TYPE_RAW: + if (stp_check_raw_parameter(v, desc.name, + STP_PARAMETER_DEFAULTED)) + { + const stp_raw_t *raw = stp_get_raw_parameter(v, desc.name); + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING raw %s\n", desc.name); + stp_set_raw_parameter(c, desc.name, raw->data, raw->bytes); + } + else if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping raw %s\n", + desc.name); + break; + case STP_PARAMETER_TYPE_BOOLEAN: + if (stp_check_boolean_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING bool %s %d\n", + desc.name, stp_get_boolean_parameter(v, desc.name)); + stp_set_boolean_parameter(c, desc.name, + stp_get_boolean_parameter(v, desc.name)); + } + else if (desc.is_mandatory) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted bool %s %d\n", + desc.name, desc.deflt.boolean); + stp_set_boolean_parameter(c, desc.name, desc.deflt.boolean); + } + else if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping bool %s\n", desc.name); + break; + case STP_PARAMETER_TYPE_INT: + if (stp_check_int_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING int %s %d\n", + desc.name, stp_get_int_parameter(v, desc.name)); + stp_set_int_parameter(c, desc.name, stp_get_int_parameter(v, desc.name)); + } + else if (desc.is_mandatory) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted int %s %d\n", + desc.name, desc.deflt.integer); + stp_set_int_parameter(c, desc.name, desc.deflt.integer); + } + else if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping int %s\n", desc.name); + break; + case STP_PARAMETER_TYPE_DOUBLE: + if (stp_check_float_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING float %s %f\n", + desc.name, stp_get_float_parameter(v, desc.name)); + stp_set_float_parameter(c, desc.name, + stp_get_float_parameter(v, desc.name)); + } + else if (desc.is_mandatory) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted float %s %f\n", + desc.name, desc.deflt.dbl); + stp_set_float_parameter(c, desc.name, desc.deflt.dbl); + } + else if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping float %s\n", desc.name); + break; + case STP_PARAMETER_TYPE_DIMENSION: + if (stp_check_dimension_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING dimension %s %d\n", + desc.name, stp_get_dimension_parameter(v, desc.name)); + stp_set_dimension_parameter(c, desc.name, + stp_get_dimension_parameter(v, desc.name)); + } + else if (desc.is_mandatory) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted dimension %s %d\n", + desc.name, desc.deflt.dimension); + stp_set_dimension_parameter(c, desc.name, desc.deflt.dimension); + } + else if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping dimension %s\n", desc.name); + break; + case STP_PARAMETER_TYPE_CURVE: + if (stp_check_curve_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING curve %s\n", desc.name); + stp_set_curve_parameter(c, desc.name, + stp_get_curve_parameter(v, desc.name)); + } + else if (desc.is_mandatory) + { + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted curve %s\n", desc.name); + stp_set_curve_parameter(c, desc.name, desc.deflt.curve); + } + else if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: skipping curve %s\n", + desc.name); + break; + default: + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Ignoring unknown type parameter %s (%d)\n", + desc.name, desc.p_type); + break; + } + stp_parameter_description_destroy(&desc); + } + stp_parameter_list_destroy(params); + mxml = stp_xmltree_create_from_vars(c); + if (mxml) + { + fputs("\n\n", f_options); + stp_mxmlSaveFile(mxml, f_options, STP_MXML_NO_CALLBACK); + stp_mxmlDelete(mxml); + } + (void) fclose(f_options); + stp_vars_destroy(c); + if (!suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Done saving parameters to %s\n", save_name); +} + +static stp_vars_t * +load_options(const char *load_name) +{ + FILE *f_options = fopen(load_name, "r"); + if (f_options) + { + stp_vars_t *settings = NULL; + stp_mxml_node_t *mxml; + mxml = stp_mxmlLoadFile(NULL, f_options, STP_MXML_NO_CALLBACK); + if (mxml) + { + stp_mxml_node_t *nxml = + stp_mxmlFindElement(mxml, mxml, "vars", NULL, NULL, + STP_MXML_DESCEND); + if (nxml) + { + settings = stp_vars_create_from_xmltree_ref(nxml->child, mxml); + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: loading options from %s\n", + load_file_name); + if (! suppress_messages) + stp_vars_print_error(settings, "DEBUG"); + } + } + else + fprintf(stderr, "DEBUG: Unable to load options from %s\n", + load_file_name); + fclose(f_options); + return settings; + } + return NULL; +} + +#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ + +/* + * 'main()' - Main entry and processing of driver. + */ + +int /* O - Exit status */ +main(int argc, /* I - Number of command-line arguments */ + char *argv[]) /* I - Command-line arguments */ +{ + int fd; /* File descriptor */ + cups_image_t cups; /* CUPS image */ + const char *ppdfile; /* PPD environment variable */ + ppd_file_t *ppd; /* PPD file */ + ppd_size_t *size; + const stp_printer_t *printer; /* Printer driver */ + int num_options; /* Number of CUPS options */ + cups_option_t *options; /* CUPS options */ + stp_vars_t *v = NULL; + stp_vars_t *default_settings; + int initialized_job = 0; + const char *version_id; + struct tms tms; + long clocks_per_sec; + struct timeval t1, t2; + char *page_size_name = NULL; + int aborted = 0; +#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS + stp_vars_t *loaded_settings = NULL; +#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ + + + /* + * Don't buffer error/status messages... + */ + + setbuf(stderr, NULL); + + if (getenv("STP_SUPPRESS_MESSAGES")) + suppress_messages = 1; + + if (getenv("STP_SUPPRESS_VERBOSE_MESSAGES")) + suppress_verbose_messages = 1; + + /* + * Initialize libgutenprint + */ + + po = stp_i18n_load(getenv("LANG")); + + theImage.rep = ∪︀ + + (void) gettimeofday(&t1, NULL); + stp_set_global_errfunc(cups_errfunc); + stp_set_global_dbgfunc(cups_dbgfunc); + stp_set_global_errdata(stderr); + stp_set_global_dbgdata(stderr); + stp_init(); + version_id = stp_get_version(); + default_settings = stp_vars_create(); + stp_set_outfunc(default_settings, cups_writefunc); + stp_set_outdata(default_settings, stdout); + + /* + * Check for valid arguments... + */ + if (argc < 6 || argc > 7) + { + /* + * We don't have the correct number of arguments; write an error message + * and return. + */ + + stp_i18n_printf(po, _("Usage: rastertoprinter job-id user title copies " + "options [file]\n")); + return (1); + } + + if (! suppress_messages) + { + fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n"); + fprintf(stderr, "DEBUG: Gutenprint: VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); + fprintf(stderr, "DEBUG: Gutenprint: %s Starting\n", version_id); + fprintf(stderr, "DEBUG: Gutenprint: command line: %s '%s' '%s' '%s' '%s' %s%s%s%s\n", + argv[0], argv[1], argv[2], argv[3], argv[4], "", + argc >= 7 ? " '" : "", + argc >= 7 ? argv[6] : "", + argc >= 7 ? "'" : ""); + } + + /* + * Get the PPD file... + */ + + if ((ppdfile = getenv("PPD")) == NULL) + { + stp_i18n_printf(po, _("ERROR: No PPD file, unable to continue!\n")); + return (1); + } + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: using PPD file %s\n", ppdfile); + + if ((ppd = ppdOpenFile(ppdfile)) == NULL) + { + stp_i18n_printf(po, _("ERROR: Gutenprint was unable to load PPD file " + "\"%s\"!\n"), ppdfile); + return (1); + } + + if (ppd->modelname == NULL) + { + stp_i18n_printf(po, _("ERROR: Gutenprint did not find a ModelName " + "attribute in PPD file \"%s\"!\n"), ppdfile); + ppdClose(ppd); + return (1); + } + + if (ppd->nickname == NULL) + { + stp_i18n_printf(po, _("ERROR: Gutenprint did not find a NickName attribute " + "in PPD file \"%s\"!\n"), ppdfile); + ppdClose(ppd); + return (1); + } + else if (strlen(ppd->nickname) < + strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING) + 3) + { + stp_i18n_printf(po, _("ERROR: Gutenprint found a corrupted NickName " + "attribute in PPD file \"%s\"!\n"), ppdfile); + ppdClose(ppd); + return (1); + } + else if (strcmp(ppd->nickname + strlen(ppd->modelname) + + strlen(CUPS_PPD_NICKNAME_STRING), version_id) != 0 && + (strlen(ppd->nickname + strlen(ppd->modelname) + + strlen(CUPS_PPD_NICKNAME_STRING)) < strlen(version_id) || + !((strncmp(ppd->nickname + strlen(ppd->modelname) + + strlen(CUPS_PPD_NICKNAME_STRING), version_id, + strlen(version_id)) == 0) && + *(ppd->nickname + strlen(ppd->modelname) + + strlen(CUPS_PPD_NICKNAME_STRING)) != ' '))) + { + stp_i18n_printf(po, _("ERROR: The PPD version (%s) is not compatible with " + "Gutenprint %s. Please run `%scups-genppdupdate' as administrator.\n"), + ppd->nickname+strlen(ppd->modelname)+strlen(CUPS_PPD_NICKNAME_STRING), + version_id, SBINDIR); + fprintf(stderr, "DEBUG: Gutenprint: If you have upgraded your version of Gutenprint\n"); + fprintf(stderr, "DEBUG: Gutenprint: recently, you must reinstall all printer queues.\n"); + fprintf(stderr, "DEBUG: Gutenprint: If the previous installed version of Gutenprint\n"); + fprintf(stderr, "DEBUG: Gutenprint: was 5.0.0 or higher, you can use the `cups-genppdupdate'\n"); + fprintf(stderr, "DEBUG: Gutenprint: program to do this; if the previous installed version\n"); + fprintf(stderr, "DEBUG: Gutenprint: was older, you can use the Modify Printer command via\n"); + fprintf(stderr, "DEBUG: Gutenprint: the CUPS web interface: http://localhost:631/printers.\n"); + ppdClose(ppd); + return 1; + } + + /* + * Get the STP options, if any... + */ + + num_options = cupsParseOptions(argv[5], 0, &options); + ppdMarkDefaults(ppd); + cupsMarkOptions(ppd, num_options, options); + size = ppdPageSize(ppd, NULL); + + if (size) + page_size_name = stp_strdup(size->name); + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: CUPS option count is %d (%d bytes)\n", + num_options, (int)strlen(argv[5])); + + if (num_options > 0) + { + int i; + for (i = 0; i < num_options; i++) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: CUPS option %d %s = %s\n", + i, options[i].name, options[i].value); +#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS + if (!strcmp(options[i].name, "SaveFileName")) + save_file_name = options[i].value; + if (!strcmp(options[i].name, "LoadFileName")) + load_file_name = options[i].value; +#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ + } + } + + /* + * Figure out which driver to use... + */ + + printer = stp_get_printer_by_driver(ppd->modelname); + if (!printer) + printer = stp_get_printer_by_long_name(ppd->modelname); + + if (printer == NULL) + { + stp_i18n_printf(po, _("ERROR: Unable to find Gutenprint driver named " + "\"%s\"!\n"), ppd->modelname); + ppdClose(ppd); + return (1); + } + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Driver %s\n", ppd->modelname); + + /* + * Open the page stream... + */ + + if (argc == 7) + { + if ((fd = open(argv[6], O_RDONLY)) == -1) + { + stp_i18n_printf(po, _("ERROR: Gutenprint was unable to open raster file " + "\"%s\" - %s"), argv[6], strerror(errno)); + sleep(1); + return (1); + } + } + else + fd = 0; + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Using fd %d\n", fd); + + stp_set_printer_defaults(default_settings, printer); +#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS + if (load_file_name) + loaded_settings = load_options(load_file_name); +#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ + stp_set_float_parameter(default_settings, "AppGamma", 1.0); + set_all_options(default_settings, options, num_options, ppd); + + cupsFreeOptions(num_options, options); + ppdClose(ppd); + + cups.ras = cupsRasterOpen(fd, CUPS_RASTER_READ); + + /* + * Process pages as needed... + */ + + cups.page = 0; + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: About to start printing loop.\n"); + + /* + * Read the first page header, which we need in order to set up + * the page. + */ + signal(SIGTERM, cancel_job); + while (CUPS_READ_HEADER(cups.ras, &cups.header)) + { + /* + * We don't know how many pages we're going to print, and + * we need to call stp_end_job at the completion of the job. + * Therefore, we need to keep v in scope after the termination + * of the loop to permit calling stp_end_job then. Therefore, + * we have to free the previous page's stp_vars_t at the start + * of the loop. + */ + if (v) + stp_vars_destroy(v); + + /* + * Setup printer driver variables... + */ + if (! suppress_messages) + { + fprintf(stderr, "DEBUG: Gutenprint: ================ Printing page %d ================\n", cups.page + 1); + fprintf(stderr, "PAGE: %d %d\n", cups.page + 1, cups.header.NumCopies); + } + v = initialize_page(&cups, default_settings, page_size_name); +#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS + if (loaded_settings) + stp_copy_vars_from(v, loaded_settings); + if (save_file_name) + { + save_options(save_file_name, v); + save_file_name = NULL; + } +#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ + if (! suppress_messages) + { + fprintf(stderr, "DEBUG: Gutenprint: Interim page settings:\n"); + stp_vars_print_error(v, "DEBUG"); + } + + stp_merge_printvars(v, stp_printer_get_defaults(printer)); + + /* Pass along Collation settings */ + stp_set_boolean_parameter(v, "Collate", cups.header.Collate); + stp_set_boolean_parameter_active(v, "Collate", STP_PARAMETER_ACTIVE); + /* Pass along Copy settings */ + stp_set_int_parameter(v, "NumCopies", cups.header.NumCopies); + stp_set_int_parameter_active(v, "NumCopies", STP_PARAMETER_ACTIVE); + /* Pass along the page number */ + stp_set_int_parameter(v, "PageNumber", cups.page); + cups.row = 0; + if (! suppress_messages) + print_debug_block(v, &cups); + print_messages_as_errors = 1; + + if (!initialized_job) + { + stp_start_job(v, &theImage); + initialized_job = 1; + } + + if (!stp_print(v, &theImage)) + { + if (Image_status != STP_IMAGE_STATUS_ABORT) + { + fprintf(stderr, "DEBUG: Gutenprint: Options failed to verify.\n"); + fprintf(stderr, "DEBUG: Gutenprint: Make sure that you are using ESP Ghostscript rather\n"); + fprintf(stderr, "DEBUG: Gutenprint: than GNU or AFPL Ghostscript with CUPS.\n"); + fprintf(stderr, "DEBUG: Gutenprint: If this is not the cause, set LogLevel to debug to identify the problem.\n"); + } + aborted = 1; + break; + } + print_messages_as_errors = 0; + + fflush(stdout); + + /* + * Purge any remaining bitmap data... + */ + if (cups.row < cups.header.cupsHeight) + purge_excess_data(&cups); + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: ================ Done printing page %d ================\n", cups.page + 1); + cups.page ++; + } + if (v) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: %s job\n", + aborted ? "Aborted" : "Ending"); + stp_end_job(v, &theImage); + fflush(stdout); + stp_vars_destroy(v); + } + cupsRasterClose(cups.ras); + (void) times(&tms); + (void) gettimeofday(&t2, NULL); + clocks_per_sec = sysconf(_SC_CLK_TCK); + fprintf(stderr, "DEBUG: Gutenprint: stats %.0fB, %.3fu, %.3fs, %.3fel\n", + total_bytes_printed, + (double) tms.tms_utime / clocks_per_sec, + (double) tms.tms_stime / clocks_per_sec, + (double) (t2.tv_sec - t1.tv_sec) + + ((double) (t2.tv_usec - t1.tv_usec)) / 1000000.0); + if (!suppress_messages) + { + fprintf(stderr, "DEBUG: Gutenprint: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); + fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n"); + } + stp_vars_destroy(default_settings); + if (page_size_name) + stp_free(page_size_name); + if (fd != 0) + close(fd); + return aborted ? 1 : 0; +} + + +/* + * 'cups_writefunc()' - Write data to a file... + */ + +static void +cups_writefunc(void *file, const char *buf, size_t bytes) +{ + FILE *prn = (FILE *)file; + total_bytes_printed += bytes; + fwrite(buf, 1, bytes, prn); +} + +static void +cups_errfunc(void *file, const char *buf, size_t bytes) +{ + size_t next_nl = 0; + size_t where = 0; + FILE *prn = (FILE *)file; + while (where < bytes) + { + if (bytes - where > 6 && strncmp(buf, "ERROR:", 6) == 0) + { + fputs("ERROR: Gutenprint:", prn); + buf += 6; + } + else if (print_messages_as_errors) + fputs("ERROR: Gutenprint: ", prn); + else if (strncmp(buf, "DEBUG", 5) != 0) + fputs("DEBUG: Gutenprint: ", prn); + while (next_nl < bytes) + { + if (buf[next_nl++] == '\n') + break; + } + fwrite(buf + where, 1, next_nl - where, prn); + where = next_nl; + } +} + +static void +cups_dbgfunc(void *file, const char *buf, size_t bytes) +{ + size_t next_nl = 0; + size_t where = 0; + FILE *prn = (FILE *)file; + while (where < bytes) + { + if (bytes - where > 6 && strncmp(buf, "ERROR:", 6) == 0) + { + fputs("ERROR: Gutenprint:", prn); + buf += 6; + } + else if (strncmp(buf, "DEBUG", 5) != 0) + fputs("DEBUG: Gutenprint: ", prn); + while (next_nl < bytes) + { + if (buf[next_nl++] == '\n') + break; + } + fwrite(buf + where, 1, next_nl - where, prn); + where = next_nl; + } +} + + +/* + * 'cancel_job()' - Cancel the current job... + */ + +static void +cancel_job(int sig) /* I - Signal */ +{ + (void)sig; + Image_status = STP_IMAGE_STATUS_ABORT; +} + +/* + * 'Image_get_appname()' - Get the application we are running. + */ + +static const char * /* O - Application name */ +Image_get_appname(stp_image_t *image) /* I - Image */ +{ + (void)image; + + return ("CUPS driver based on Gutenprint"); +} + + +/* + * 'Image_get_row()' - Get one row of the image. + */ + +static void +throwaway_data(int amount, cups_image_t *cups) +{ + unsigned char trash[4096]; /* Throwaway */ + int block_count = amount / 4096; + int leftover = amount % 4096; + while (block_count > 0) + { + cupsRasterReadPixels(cups->ras, trash, 4096); + block_count--; + } + if (leftover) + cupsRasterReadPixels(cups->ras, trash, leftover); +} + +static stp_image_status_t +Image_get_row(stp_image_t *image, /* I - Image */ + unsigned char *data, /* O - Row */ + size_t byte_limit, /* I - how many bytes in data */ + int row) /* I - Row number (unused) */ +{ + cups_image_t *cups; /* CUPS image */ + int i; /* Looping var */ + int bytes_per_line; + int margin; + stp_image_status_t tmp_image_status = Image_status; + unsigned char *orig = data; /* Temporary pointer */ + static int warned = 0; /* Error warning printed? */ + int new_percent; + int left_margin, right_margin; + + if ((cups = (cups_image_t *)(image->rep)) == NULL) + { + stp_i18n_printf(po, _("ERROR: Gutenprint image is not initialized! " + "Please report this bug to " + "gimp-print-devel@lists.sourceforge.net\n")); + return STP_IMAGE_STATUS_ABORT; + } + bytes_per_line = + ((cups->adjusted_width * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / + CHAR_BIT; + + left_margin = ((cups->left_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / + CHAR_BIT; + right_margin = ((cups->right_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / + CHAR_BIT; + margin = cups->header.cupsBytesPerLine - left_margin - bytes_per_line - + right_margin; + + if (cups->row < cups->header.cupsHeight) + { + if (! suppress_messages && ! suppress_verbose_messages) + fprintf(stderr, "DEBUG2: Gutenprint: Reading %d %d\n", + bytes_per_line, cups->row); + while (cups->row <= row && cups->row < cups->header.cupsHeight) + { + if (left_margin > 0) + { + if (! suppress_messages && ! suppress_verbose_messages) + fprintf(stderr, "DEBUG2: Gutenprint: Tossing left %d (%d)\n", + left_margin, cups->left_trim); + throwaway_data(left_margin, cups); + } + cupsRasterReadPixels(cups->ras, data, bytes_per_line); + cups->row ++; + if (margin + right_margin > 0) + { + if (! suppress_messages && ! suppress_verbose_messages) + fprintf(stderr, "DEBUG2: Gutenprint: Tossing right %d (%d) + %d\n", + right_margin, cups->right_trim, margin); + throwaway_data(margin + right_margin, cups); + } + } + } + else + { + switch (cups->header.cupsColorSpace) + { + case CUPS_CSPACE_K: + case CUPS_CSPACE_CMYK: + case CUPS_CSPACE_KCMY: + case CUPS_CSPACE_CMY: + memset(data, 0, bytes_per_line); + break; + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_W: + memset(data, ((1 << CHAR_BIT) - 1), bytes_per_line); + break; + default: + stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace " + "(%d)!\n"), cups->header.cupsColorSpace); + return STP_IMAGE_STATUS_ABORT; + } + } + + /* + * This exists to print non-ADSC input which has messed up the job + * input, such as that generated by psnup. The output is barely + * legible, but it's better than the garbage output otherwise. + */ + data = orig; + if (cups->header.cupsBitsPerPixel == 1) + { + if (warned == 0) + { + fputs(_("WARNING: Gutenprint detected a bad color depth (1). " + "Output quality is degraded. Are you using psnup or " + "non-ADSC PostScript?\n"), stderr); + warned = 1; + } + for (i = cups->adjusted_width - 1; i >= 0; i--) + { + if ( (data[i/8] >> (7 - i%8)) &0x1) + data[i]=255; + else + data[i]=0; + } + } + + new_percent = (int) (100.0 * cups->row / cups->header.cupsHeight); + if (new_percent > cups->last_percent) + { + if (! suppress_verbose_messages) + { + stp_i18n_printf(po, _("INFO: Printing page %d, %d%%\n"), + cups->page + 1, new_percent); + fprintf(stderr, "ATTR: job-media-progress=%d\n", new_percent); + } + cups->last_percent = new_percent; + } + + if (tmp_image_status != STP_IMAGE_STATUS_OK) + { + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Image status %d\n", tmp_image_status); + } + return tmp_image_status; +} + + +/* + * 'Image_height()' - Return the height of an image. + */ + +static int /* O - Height in pixels */ +Image_height(stp_image_t *image) /* I - Image */ +{ + cups_image_t *cups; /* CUPS image */ + + + if ((cups = (cups_image_t *)(image->rep)) == NULL) + return (0); + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Image_height %d\n", cups->adjusted_height); + return (cups->adjusted_height); +} + + +/* + * 'Image_init()' - Initialize an image. + */ + +static void +Image_init(stp_image_t *image) /* I - Image */ +{ + cups_image_t *cups; /* CUPS image */ + + if ((cups = (cups_image_t *)(image->rep)) == NULL) + return; + cups->last_percent = 0; + + if (! suppress_messages) + stp_i18n_printf(po, _("INFO: Starting page %d...\n"), cups->page + 1); + /* cups->page + 1 because users expect 1-based counting */ +} + +/* + * 'Image_progress_conclude()' - Close the progress display. + */ + +static void +Image_conclude(stp_image_t *image) /* I - Image */ +{ + cups_image_t *cups; /* CUPS image */ + + + if ((cups = (cups_image_t *)(image->rep)) == NULL) + return; + + if (! suppress_messages) + stp_i18n_printf(po, _("INFO: Finished page %d...\n"), cups->page + 1); +} + +/* + * 'Image_width()' - Return the width of an image. + */ + +static int /* O - Width in pixels */ +Image_width(stp_image_t *image) /* I - Image */ +{ + cups_image_t *cups; /* CUPS image */ + + + if ((cups = (cups_image_t *)(image->rep)) == NULL) + return (0); + + if (! suppress_messages) + fprintf(stderr, "DEBUG: Gutenprint: Image_width %d\n", cups->adjusted_width); + return (cups->adjusted_width); +} diff --git a/src/cups/rastertoprinter.c b/src/cups/rastertoprinter.c deleted file mode 100644 index 0fa0668..0000000 --- a/src/cups/rastertoprinter.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* - * Gutenprint based raster filter for the Common UNIX Printing System. - * - * Copyright 1993-2008 by Mike Sweet. - * - * 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. - * - * Contents: - * - * main() - Main entry and processing of driver. - * cups_writefunc() - Write data to a file... - * cancel_job() - Cancel the current job... - * Image_get_appname() - Get the application we are running. - * Image_get_row() - Get one row of the image. - * Image_height() - Return the height of an image. - * Image_init() - Initialize an image. - * Image_conclude() - Close the progress display. - * Image_width() - Return the width of an image. - */ - -/* - * Include necessary headers... - */ - -#if 0 -#define ENABLE_CUPS_LOAD_SAVE_OPTIONS -#endif - -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_LIMITS_H -#include -#endif -#include "i18n.h" -#include - -/* Solaris with gcc has problems because gcc's limits.h doesn't #define */ -/* this */ -#ifndef CHAR_BIT -#define CHAR_BIT 8 -#endif - -/* - * Structure for page raster data... - */ - -#if (CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1)) -#define CUPS_HEADER_T cups_page_header2_t -#define CUPS_READ_HEADER cupsRasterReadHeader2 -#else -#define CUPS_HEADER_T cups_page_header_t -#define CUPS_READ_HEADER cupsRasterReadHeader -#endif - -typedef struct -{ - cups_raster_t *ras; /* Raster stream to read from */ - int page; /* Current page number */ - int row; /* Current row number */ - int left; - int right; - int bottom; - int top; - int width; - int height; - int left_trim; - int right_trim; - int top_trim; - int bottom_trim; - int adjusted_width; - int adjusted_height; - stp_dimension_t d_left; - stp_dimension_t d_right; - stp_dimension_t d_bottom; - stp_dimension_t d_top; - stp_dimension_t d_width; - stp_dimension_t d_height; - stp_dimension_t d_left_trim; - stp_dimension_t d_right_trim; - stp_dimension_t d_bottom_trim; - stp_dimension_t d_top_trim; - int last_percent; - int shrink_to_fit; - CUPS_HEADER_T header; /* Page header from file */ -} cups_image_t; - -static void cups_writefunc(void *file, const char *buf, size_t bytes); -static void cups_errfunc(void *file, const char *buf, size_t bytes); -static void cancel_job(int sig); -static const char *Image_get_appname(stp_image_t *image); -static stp_image_status_t Image_get_row(stp_image_t *image, - unsigned char *data, - size_t byte_limit, int row); -static int Image_height(stp_image_t *image); -static int Image_width(stp_image_t *image); -static void Image_conclude(stp_image_t *image); -static void Image_init(stp_image_t *image); - -static stp_image_t theImage = -{ - Image_init, - NULL, /* reset */ - Image_width, - Image_height, - Image_get_row, - Image_get_appname, - Image_conclude, - NULL -}; - -static volatile stp_image_status_t Image_status = STP_IMAGE_STATUS_OK; -static double total_bytes_printed = 0; -static int print_messages_as_errors = 0; -static int suppress_messages = 0; -static int suppress_verbose_messages = 0; -static const stp_string_list_t *po = NULL; -#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS -static const char *save_file_name = NULL; -static const char *load_file_name = NULL; -#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ - -extern void stpi_vars_print_error(const stp_vars_t *v, const char *prefix); - -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -static inline void * -cast_safe(const void *ptr) -{ - return (void *)ptr; -} -#pragma GCC diagnostic pop - -static void -set_string_parameter(stp_vars_t *v, const char *name, const char *val) -{ - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set special string %s to %s\n", name, val); - stp_set_string_parameter(v, name, val); -} - - -static void -set_special_parameter(stp_vars_t *v, const char *name, int choice) -{ - stp_parameter_t desc; - stp_describe_parameter(v, name, &desc); - if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) - { -#if 0 - if (stp_check_string_parameter(v, name, STP_PARAMETER_ACTIVE)) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Not overriding special parameter %s (%s)\n", - name, stp_get_string_parameter(v, name)); - } - else -#endif - if (choice < 0) - { - stp_clear_string_parameter(v, name); - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Clear special parameter %s\n", - name); - } - else if (choice >= stp_string_list_count(desc.bounds.str)) - { - if (! suppress_messages) - stp_i18n_printf(po, _("ERROR: Unable to set Gutenprint option %s " - "(%d > %d)!\n"), name, choice, - stp_string_list_count(desc.bounds.str)); - } - else - { - stp_set_string_parameter - (v, name, stp_string_list_param(desc.bounds.str, choice)->name); - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set special parameter %s to choice %d (%s)\n", - name, choice, - stp_string_list_param(desc.bounds.str, choice)->name); - } - } - else - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Unable to set special %s: not a string\n", - name); - } - stp_parameter_description_destroy(&desc); -} - -static void -print_debug_block(const stp_vars_t *v, const cups_image_t *cups) -{ - fprintf(stderr, "DEBUG: Gutenprint: Page data:\n"); - fprintf(stderr, "DEBUG: Gutenprint: MediaClass = \"%s\"\n", cups->header.MediaClass); - fprintf(stderr, "DEBUG: Gutenprint: MediaColor = \"%s\"\n", cups->header.MediaColor); - fprintf(stderr, "DEBUG: Gutenprint: MediaType = \"%s\"\n", cups->header.MediaType); - fprintf(stderr, "DEBUG: Gutenprint: OutputType = \"%s\"\n", cups->header.OutputType); - - fprintf(stderr, "DEBUG: Gutenprint: AdvanceDistance = %d\n", cups->header.AdvanceDistance); - fprintf(stderr, "DEBUG: Gutenprint: AdvanceMedia = %d\n", cups->header.AdvanceMedia); - fprintf(stderr, "DEBUG: Gutenprint: Collate = %d\n", cups->header.Collate); - fprintf(stderr, "DEBUG: Gutenprint: CutMedia = %d\n", cups->header.CutMedia); - fprintf(stderr, "DEBUG: Gutenprint: Duplex = %d\n", cups->header.Duplex); - fprintf(stderr, "DEBUG: Gutenprint: HWResolution = [ %d %d ]\n", cups->header.HWResolution[0], - cups->header.HWResolution[1]); - fprintf(stderr, "DEBUG: Gutenprint: ImagingBoundingBox = [ %d %d %d %d ]\n", - cups->header.ImagingBoundingBox[0], cups->header.ImagingBoundingBox[1], - cups->header.ImagingBoundingBox[2], cups->header.ImagingBoundingBox[3]); - fprintf(stderr, "DEBUG: Gutenprint: InsertSheet = %d\n", cups->header.InsertSheet); - fprintf(stderr, "DEBUG: Gutenprint: Jog = %d\n", cups->header.Jog); - fprintf(stderr, "DEBUG: Gutenprint: LeadingEdge = %d\n", cups->header.LeadingEdge); - fprintf(stderr, "DEBUG: Gutenprint: Margins = [ %d %d ]\n", cups->header.Margins[0], - cups->header.Margins[1]); - fprintf(stderr, "DEBUG: Gutenprint: ManualFeed = %d\n", cups->header.ManualFeed); - fprintf(stderr, "DEBUG: Gutenprint: MediaPosition = %d\n", cups->header.MediaPosition); - fprintf(stderr, "DEBUG: Gutenprint: MediaWeight = %d\n", cups->header.MediaWeight); - fprintf(stderr, "DEBUG: Gutenprint: MirrorPrint = %d\n", cups->header.MirrorPrint); - fprintf(stderr, "DEBUG: Gutenprint: NegativePrint = %d\n", cups->header.NegativePrint); - fprintf(stderr, "DEBUG: Gutenprint: NumCopies = %d\n", cups->header.NumCopies); - fprintf(stderr, "DEBUG: Gutenprint: Orientation = %d\n", cups->header.Orientation); - fprintf(stderr, "DEBUG: Gutenprint: OutputFaceUp = %d\n", cups->header.OutputFaceUp); - fprintf(stderr, "DEBUG: Gutenprint: PageSize = [ %d %d ]\n", cups->header.PageSize[0], - cups->header.PageSize[1]); - fprintf(stderr, "DEBUG: Gutenprint: Separations = %d\n", cups->header.Separations); - fprintf(stderr, "DEBUG: Gutenprint: TraySwitch = %d\n", cups->header.TraySwitch); - fprintf(stderr, "DEBUG: Gutenprint: Tumble = %d\n", cups->header.Tumble); - fprintf(stderr, "DEBUG: Gutenprint: cupsWidth = %d\n", cups->header.cupsWidth); - fprintf(stderr, "DEBUG: Gutenprint: cupsHeight = %d\n", cups->header.cupsHeight); - fprintf(stderr, "DEBUG: Gutenprint: cups->width = %d\n", cups->width); - fprintf(stderr, "DEBUG: Gutenprint: cups->height = %d\n", cups->height); - fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_width = %d\n", cups->adjusted_width); - fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_height = %d\n", cups->adjusted_height); - fprintf(stderr, "DEBUG: Gutenprint: cupsMediaType = %d\n", cups->header.cupsMediaType); - fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerColor = %d\n", cups->header.cupsBitsPerColor); - fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerPixel = %d\n", cups->header.cupsBitsPerPixel); - fprintf(stderr, "DEBUG: Gutenprint: cupsBytesPerLine = %d\n", cups->header.cupsBytesPerLine); - fprintf(stderr, "DEBUG: Gutenprint: cupsColorOrder = %d\n", cups->header.cupsColorOrder); - fprintf(stderr, "DEBUG: Gutenprint: cupsColorSpace = %d\n", cups->header.cupsColorSpace); - fprintf(stderr, "DEBUG: Gutenprint: cupsCompression = %d\n", cups->header.cupsCompression); - fprintf(stderr, "DEBUG: Gutenprint: cupsRowCount = %d\n", cups->header.cupsRowCount); - fprintf(stderr, "DEBUG: Gutenprint: cupsRowFeed = %d\n", cups->header.cupsRowFeed); - fprintf(stderr, "DEBUG: Gutenprint: cupsRowStep = %d\n", cups->header.cupsRowStep); - fprintf(stderr, "DEBUG: Gutenprint: shrink page to fit %d\n", cups->shrink_to_fit); - stpi_vars_print_error(v, "DEBUG"); - fprintf(stderr, "DEBUG: Gutenprint: End page data\n"); -} - -static int -printer_supports_bw(const stp_vars_t *v) -{ - stp_parameter_t desc; - int status = 0; - stp_describe_parameter(v, "PrintingMode", &desc); - if (stp_string_list_is_present(desc.bounds.str, "BW")) - status = 1; - stp_parameter_description_destroy(&desc); - return status; -} - -static void -validate_options(stp_vars_t *v, cups_image_t *cups) -{ - stp_parameter_list_t params = stp_get_parameter_list(v); - int nparams = stp_parameter_list_count(params); - int i; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Validating options\n"); - for (i = 0; i < nparams; i++) - { - const stp_parameter_t *param = stp_parameter_list_param(params, i); - stp_parameter_t desc; - stp_describe_parameter(v, param->name, &desc); - if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) - { - if (!stp_string_list_is_present - (desc.bounds.str, stp_get_string_parameter(v, desc.name))) - { - if (! suppress_messages) - { - const char *val = stp_get_string_parameter(v, desc.name); - fprintf(stderr, "DEBUG: Gutenprint: Clearing string %s (%s)\n", - desc.name, val ? val : "(null)"); - } - stp_clear_string_parameter(v, desc.name); - if (!desc.read_only && desc.is_mandatory && desc.is_active) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Setting default string %s to %s\n", - desc.name, desc.deflt.str ? desc.deflt.str : "(null)"); - stp_set_string_parameter(v, desc.name, desc.deflt.str); - if (strcmp(desc.name, "PageSize") == 0) - { - const stp_papersize_t *ps = - stp_get_papersize_by_name(desc.deflt.str); - if (ps->width > 0) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Setting page width to %.3f\n", - ps->width); - if (ps->width < stp_get_page_width(v)) - stp_set_page_width(v, ps->width); - } - if (ps->height > 0) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Setting page height to %.3f\n", - ps->height); - if (ps->height < stp_get_page_height(v)) - stp_set_page_height(v, ps->height); - } - } - } - } - } - stp_parameter_description_destroy(&desc); - } - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Done validating options\n"); - stp_parameter_list_destroy(params); -} - -static stp_vars_t * -initialize_page(cups_image_t *cups, const stp_vars_t *default_settings, - const char *page_size_name) -{ - stp_dimension_t tmp_left, tmp_right, tmp_top, tmp_bottom; - stp_vars_t *v = stp_vars_create_copy(default_settings); - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Initialize page\n"); - - stp_set_outfunc(v, cups_writefunc); - stp_set_errfunc(v, cups_errfunc); - stp_set_outdata(v, stdout); - stp_set_errdata(v, stderr); - - if (cups->header.cupsBitsPerColor == 16) - set_string_parameter(v, "ChannelBitDepth", "16"); - else - set_string_parameter(v, "ChannelBitDepth", "8"); - switch (cups->header.cupsColorSpace) - { - case CUPS_CSPACE_W : - /* DyeSub photo printers don't support black & white ink! */ - if (printer_supports_bw(v)) - set_string_parameter(v, "PrintingMode", "BW"); - set_string_parameter(v, "InputImageType", "Whitescale"); - break; - case CUPS_CSPACE_K : - /* DyeSub photo printers don't support black & white ink! */ - if (printer_supports_bw(v)) - set_string_parameter(v, "PrintingMode", "BW"); - set_string_parameter(v, "InputImageType", "Grayscale"); - break; - case CUPS_CSPACE_RGB : - set_string_parameter(v, "PrintingMode", "Color"); - set_string_parameter(v, "InputImageType", "RGB"); - break; - case CUPS_CSPACE_CMY : - set_string_parameter(v, "PrintingMode", "Color"); - set_string_parameter(v, "InputImageType", "CMY"); - break; - case CUPS_CSPACE_CMYK : - set_string_parameter(v, "PrintingMode", "Color"); - set_string_parameter(v, "InputImageType", "CMYK"); - break; - case CUPS_CSPACE_KCMY : - set_string_parameter(v, "PrintingMode", "Color"); - set_string_parameter(v, "InputImageType", "KCMY"); - break; - default : - stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace " - "(%d)!\n"), cups->header.cupsColorSpace); - break; - } - - set_special_parameter(v, "Resolution", cups->header.cupsCompression - 1); - - set_special_parameter(v, "Quality", cups->header.cupsRowFeed - 1); - - if (cups->header.MediaClass && strlen(cups->header.MediaClass) > 0) - set_string_parameter(v, "InputSlot", cups->header.MediaClass); - - if (cups->header.MediaType && strlen(cups->header.MediaType) > 0) - set_string_parameter(v, "MediaType", cups->header.MediaType); - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: PageSize = %dx%d\n", cups->header.PageSize[0], - cups->header.PageSize[1]); - - if (page_size_name) - { - if (strcmp(page_size_name, "Custom") == 0) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Using custom page size for (%d, %d)\n", - cups->header.PageSize[1], cups->header.PageSize[0]); - stp_set_page_width(v, cups->header.PageSize[0]); - stp_set_page_height(v, cups->header.PageSize[1]); - } - else if (stp_get_papersize_by_name(page_size_name)) - { - stp_dimension_t width, height; - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Using page size %s with (%d, %d)\n", - page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]); - set_string_parameter(v, "PageSize", page_size_name); - stp_get_media_size(v, &width, &height); - if (width > 0) - stp_set_page_width(v, width); - else - stp_set_page_width(v, cups->header.PageSize[0]); - if (height > 0) - stp_set_page_height(v, height); - else - stp_set_page_height(v, cups->header.PageSize[1]); - } - else - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Can't find page size %s with (%d, %d), using custom page size\n", - page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]); - stp_set_page_width(v, cups->header.PageSize[0]); - stp_set_page_height(v, cups->header.PageSize[1]); - } - } - else - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: No named media size for (%d, %d)\n", - cups->header.PageSize[1], cups->header.PageSize[0]); - stp_set_page_width(v, cups->header.PageSize[0]); - stp_set_page_height(v, cups->header.PageSize[1]); - } - - /* - * Duplex - * Note that the names MUST match those in the printer driver(s) - */ - - if (cups->header.Duplex != 0) - { - if (cups->header.Tumble != 0) - set_string_parameter(v, "Duplex", "DuplexTumble"); - else - set_string_parameter(v, "Duplex", "DuplexNoTumble"); - } - - cups->shrink_to_fit = - (stp_check_int_parameter(v, "CUPSShrinkPage", STP_PARAMETER_ACTIVE) ? - stp_get_int_parameter(v, "CUPSShrinkPage") : 0); - - set_string_parameter(v, "JobMode", "Job"); - validate_options(v, cups); - stp_get_media_size(v, &(cups->d_width), &(cups->d_height)); - stp_get_maximum_imageable_area(v, &tmp_left, &tmp_right, - &tmp_bottom, &tmp_top); - stp_get_imageable_area(v, &(cups->d_left), &(cups->d_right), - &(cups->d_bottom), &(cups->d_top)); - if (! suppress_messages) - { - fprintf(stderr, "DEBUG: Gutenprint: limits w %.3f l %.3f r %.3f h %.3f t %.3f b %.3f\n", - cups->d_width, cups->d_left, cups->d_right, cups->d_height, cups->d_top, cups->d_bottom); - fprintf(stderr, "DEBUG: Gutenprint: max limits l %.3f r %.3f t %.3f b %.3f\n", - tmp_left, tmp_right, tmp_top, tmp_bottom); - } - - if (tmp_left < 0) - tmp_left = 0; - if (tmp_top < 0) - tmp_top = 0; - if (tmp_right > tmp_left + cups->d_width) - tmp_right = cups->d_width; - if (tmp_bottom > tmp_top + cups->d_height) - tmp_bottom = cups->d_height; - if (tmp_left < cups->d_left) - { - if (cups->shrink_to_fit != 1) - { - cups->d_left_trim = cups->d_left - tmp_left; - tmp_left = cups->d_left; - } - else - cups->d_left_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: left margin %.3f\n", cups->d_left_trim); - } - else - { - cups->d_left_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Adjusting left margin from %.3f to %.3f\n", - cups->d_left, tmp_left); - cups->d_left = tmp_left; - } - if (tmp_right > cups->d_right) - { - if (cups->shrink_to_fit != 1) - { - cups->d_right_trim = tmp_right - cups->d_right; - tmp_right = cups->d_right; - } - else - cups->d_right_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: right margin %.3f\n", cups->d_right_trim); - } - else - { - cups->d_right_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Adjusting right margin from %.3f to %.3f\n", - cups->d_right, tmp_right); - cups->d_right = tmp_right; - } - if (tmp_top < cups->d_top) - { - if (cups->shrink_to_fit != 1) - { - cups->d_top_trim = cups->d_top - tmp_top; - tmp_top = cups->d_top; - } - else - cups->d_top_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: top margin %.3f\n", cups->d_top_trim); - } - else - { - cups->d_top_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Adjusting top margin from %.3f to %.3f\n", - cups->d_top, tmp_top); - cups->d_top = tmp_top; - } - if (tmp_bottom > cups->d_bottom) - { - if (cups->shrink_to_fit != 1) - { - cups->d_bottom_trim = tmp_bottom - cups->d_bottom; - tmp_bottom = cups->d_bottom; - } - else - cups->d_bottom_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: bottom margin %.3f\n", cups->d_bottom_trim); - } - else - { - cups->d_bottom_trim = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Adjusting bottom margin from %.3f to %.3f\n", - cups->d_bottom, tmp_bottom); - cups->d_bottom = tmp_bottom; - } - - if (cups->shrink_to_fit == 2) - { - stp_dimension_t t_left, t_right, t_bottom, t_top; - stp_get_imageable_area(v, &(t_left), &(t_right), &(t_bottom), &(t_top)); - stp_set_width(v, t_right - t_left); - stp_set_height(v, t_bottom - t_top); - stp_set_left(v, t_left); - stp_set_top(v, t_top); - } - else - { - stp_set_width(v, cups->d_right - cups->d_left); - stp_set_height(v, cups->d_bottom - cups->d_top); - stp_set_left(v, cups->d_left); - stp_set_top(v, cups->d_top); - } - - cups->d_right = cups->d_width - cups->d_right; - if (cups->shrink_to_fit == 1) - cups->d_width = tmp_right - tmp_left; - else - cups->d_width = cups->d_width - cups->d_left - cups->d_right; - cups->width = cups->header.HWResolution[0] * cups->d_width / 72; - cups->left = cups->header.HWResolution[0] * cups->d_left / 72; - cups->right = cups->header.HWResolution[0] * cups->d_right / 72; - cups->left_trim = cups->header.HWResolution[0] * cups->d_left_trim / 72; - cups->right_trim = cups->header.HWResolution[0] * cups->d_right_trim / 72; - cups->adjusted_width = cups->width; - if (cups->adjusted_width > cups->header.cupsWidth) - cups->adjusted_width = cups->header.cupsWidth; - - cups->d_bottom = cups->d_height - cups->d_bottom; - if (cups->shrink_to_fit == 1) - cups->d_height = tmp_bottom - tmp_top; - else - cups->d_height = cups->d_height - cups->d_top - cups->d_bottom; - cups->height = cups->header.HWResolution[1] * cups->d_height / 72; - cups->top = cups->header.HWResolution[1] * cups->d_top / 72; - cups->bottom = cups->header.HWResolution[1] * cups->d_bottom / 72; - cups->top_trim = cups->header.HWResolution[1] * cups->d_top_trim / 72; - cups->bottom_trim = cups->header.HWResolution[1] * cups->d_bottom_trim / 72; - cups->adjusted_height = cups->height; - if (cups->adjusted_height > cups->header.cupsHeight) - cups->adjusted_height = cups->header.cupsHeight; - if (! suppress_messages) - { - fprintf(stderr, "DEBUG: Gutenprint: CUPS settings w %d l %d r %d h %d t %d b %d\n", - cups->width, cups->left, cups->right, - cups->height, cups->top, cups->bottom); - fprintf(stderr, "DEBUG: Gutenprint: adjusted w %d h %d\n", - cups->adjusted_width, cups->adjusted_height); - - } - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: End initialize page\n"); - return v; -} - -static void -purge_excess_data(cups_image_t *cups) -{ - char *buffer = stp_malloc(cups->header.cupsBytesPerLine); - if (buffer) - { - if (! suppress_messages && ! suppress_verbose_messages ) - fprintf(stderr, "DEBUG2: Gutenprint: Purging %d row%s\n", - cups->header.cupsHeight - cups->row, - ((cups->header.cupsHeight - cups->row) == 1 ? "" : "s")); - while (cups->row < cups->header.cupsHeight) - { - cupsRasterReadPixels(cups->ras, (unsigned char *)buffer, - cups->header.cupsBytesPerLine); - cups->row ++; - } - } - stp_free(buffer); -} - -static void -set_all_options(stp_vars_t *v, cups_option_t *options, int num_options, - ppd_file_t *ppd) -{ - stp_parameter_list_t params = stp_get_parameter_list(v); - int nparams = stp_parameter_list_count(params); - int i; - const char *val; /* CUPS option value */ - ppd_option_t *ppd_option; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set options:\n"); - val = cupsGetOption("StpiShrinkOutput", num_options, options); - if (!val) - { - ppd_option = ppdFindOption(ppd, "StpiShrinkOutput"); - if (ppd_option) - val = ppd_option->defchoice; - } - if (val) - { - if (!strcasecmp(val, "crop")) - stp_set_int_parameter(v, "CUPSShrinkPage", 0); - else if (!strcasecmp(val, "expand")) - stp_set_int_parameter(v, "CUPSShrinkPage", 2); - else - stp_set_int_parameter(v, "CUPSShrinkPage", 1); - } - else - stp_set_int_parameter(v, "CUPSShrinkPage", 1); - for (i = 0; i < nparams; i++) - { - const stp_parameter_t *param = stp_parameter_list_param(params, i); - stp_parameter_t desc; - char *ppd_option_name = stp_malloc(strlen(param->name) + 8); /* StpFineFOO\0 */ - - stp_describe_parameter(v, param->name, &desc); - if (desc.p_type == STP_PARAMETER_TYPE_DOUBLE) - { - sprintf(ppd_option_name, "Stp%s", desc.name); - val = cupsGetOption(ppd_option_name, num_options, options); - if (!val) - { - ppd_option = ppdFindOption(ppd, ppd_option_name); - if (ppd_option) - val = ppd_option->defchoice; - } - if (val && !strncasecmp(val, "Custom.", 7)) - { - double dval = atof(val + 7); - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f\n", - desc.name, dval); - if (dval > desc.bounds.dbl.upper) - dval = desc.bounds.dbl.upper; - stp_set_float_parameter(v, desc.name, dval); - } - else if (val && strlen(val) > 0 && strcmp(val, "None") != 0) - { - double fine_val = 0; - if (strchr(val, (int) '.')) - { - fine_val = atof(val); - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f (%s)\n", - desc.name, fine_val, val); - } - else - { - double coarse_val = atof(val) * 0.001; - sprintf(ppd_option_name, "StpFine%s", desc.name); - val = cupsGetOption(ppd_option_name, num_options, options); - if (!val) - { - ppd_option = ppdFindOption(ppd, ppd_option_name); - if (ppd_option) - val = ppd_option->defchoice; - } - if (val && strlen(val) > 0 && strcmp(val, "None") != 0) - fine_val = atof(val) * 0.001; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f + %f\n", - desc.name, coarse_val, fine_val); - fine_val += coarse_val; - } - if (fine_val > desc.bounds.dbl.upper) - fine_val = desc.bounds.dbl.upper; - if (fine_val < desc.bounds.dbl.lower) - fine_val = desc.bounds.dbl.lower; - stp_set_float_parameter(v, desc.name, fine_val); - } - } - else - { - sprintf(ppd_option_name, "Stp%s", desc.name); - val = cupsGetOption(ppd_option_name, num_options, options); - if (!val) - { - ppd_option = ppdFindOption(ppd, ppd_option_name); - if (ppd_option) - val = ppd_option->defchoice; - } - if (val && ((strlen(val) > 0 && strcmp(val, "None") != 0) || - (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST))) - { - stp_curve_t *curve; - stp_raw_t *raw; - switch (desc.p_type) - { - case STP_PARAMETER_TYPE_STRING_LIST: - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set string %s to %s\n", - desc.name, val); - set_string_parameter(v, desc.name, val); - break; - case STP_PARAMETER_TYPE_INT: - if (!strncasecmp(val, "Custom.", 7)) - val += 7; - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set int %s to %s (%d)\n", - desc.name, val, atoi(val)); - stp_set_int_parameter(v, desc.name, atoi(val)); - break; - case STP_PARAMETER_TYPE_DIMENSION: - if (!strncasecmp(val, "Custom.", 7)) - val += 7; - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set dimension %s to %s (%d)\n", - desc.name, val, atoi(val)); - - stp_set_dimension_parameter(v, desc.name, atoi(val)); - break; - case STP_PARAMETER_TYPE_BOOLEAN: - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set bool %s to %s (%d)\n", - desc.name, val, strcasecmp(val, "true") == 0 ? 1 : 0); - stp_set_boolean_parameter - (v, desc.name, strcasecmp(val, "true") == 0 ? 1 : 0); - break; - case STP_PARAMETER_TYPE_CURVE: - curve = stp_curve_create_from_string(val); - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set curve %s to %s\n", - desc.name, curve ? val : "(NULL)"); - if (curve) - { - stp_set_curve_parameter(v, desc.name, curve); - stp_curve_destroy(curve); - } - break; - case STP_PARAMETER_TYPE_RAW: /* figure this out later, too */ - raw = stp_xmlstrtoraw(val); - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Set raw %s to %s\n", - desc.name, raw ? val : "(NULL)"); - if (raw) - { - stp_set_raw_parameter(v, desc.name, raw->data, raw->bytes); - stp_free(cast_safe(raw->data)); - stp_free(raw); - } - break; - case STP_PARAMETER_TYPE_FILE: /* Probably not, security hole */ - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Ignoring option %s %s type %d\n", - desc.name, val, desc.p_type); - break; - default: - break; - } - } - else if (val) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to '%s'\n", - desc.name, val); - } - else - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to (null)\n", - desc.name); - } - } - stp_parameter_description_destroy(&desc); - stp_free(ppd_option_name); - } - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: End options\n"); - stp_parameter_list_destroy(params); -} - -#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS -static void -save_options(const char *save_name, const stp_vars_t *v) -{ - FILE *f_options; - int i; - stp_vars_t *c = stp_vars_create(); - stp_parameter_list_t params = stp_get_parameter_list(v); - stp_parameter_t desc; - stp_mxml_node_t *mxml = NULL; - int param_count; - - if (!params) - { - stp_vars_destroy(c); - return; - } - f_options = fopen(save_name, "w"); - if (!f_options) - { - stp_parameter_list_destroy(params); - stp_vars_destroy(c); - return; - } - param_count = stp_parameter_list_count(params); - stp_set_driver(c, stp_get_driver(v)); - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Saving parameters to %s\n", save_name); - for (i = 0; i < param_count; i++) - { - const stp_parameter_t *lparam = - stp_parameter_list_param(params, i); - stp_describe_parameter(v, lparam->name, &desc); - if (desc.read_only || !strcmp(desc.name, "ChannelBitDepth") || - !stp_parameter_has_category_value(v, &desc, "Color", "Yes")) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping non-color %s\n", - desc.name); - stp_parameter_description_destroy(&desc); - continue; - } - switch (desc.p_type) - { - case STP_PARAMETER_TYPE_STRING_LIST: - if (stp_check_string_parameter(v, desc.name, - STP_PARAMETER_DEFAULTED)) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING string %s %s\n", - desc.name, stp_get_string_parameter(v, desc.name)); - stp_set_string_parameter(c, desc.name, - stp_get_string_parameter(v, desc.name)); - } - else if (desc.is_mandatory) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted string %s %s\n", - desc.name, desc.deflt.str); - stp_set_string_parameter(c, desc.name, desc.deflt.str); - } - else if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping string %s\n", desc.name); - break; - case STP_PARAMETER_TYPE_RAW: - if (stp_check_raw_parameter(v, desc.name, - STP_PARAMETER_DEFAULTED)) - { - const stp_raw_t *raw = stp_get_raw_parameter(v, desc.name); - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING raw %s\n", desc.name); - stp_set_raw_parameter(c, desc.name, raw->data, raw->bytes); - } - else if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping raw %s\n", - desc.name); - break; - case STP_PARAMETER_TYPE_BOOLEAN: - if (stp_check_boolean_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING bool %s %d\n", - desc.name, stp_get_boolean_parameter(v, desc.name)); - stp_set_boolean_parameter(c, desc.name, - stp_get_boolean_parameter(v, desc.name)); - } - else if (desc.is_mandatory) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted bool %s %d\n", - desc.name, desc.deflt.boolean); - stp_set_boolean_parameter(c, desc.name, desc.deflt.boolean); - } - else if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping bool %s\n", desc.name); - break; - case STP_PARAMETER_TYPE_INT: - if (stp_check_int_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING int %s %d\n", - desc.name, stp_get_int_parameter(v, desc.name)); - stp_set_int_parameter(c, desc.name, stp_get_int_parameter(v, desc.name)); - } - else if (desc.is_mandatory) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted int %s %d\n", - desc.name, desc.deflt.integer); - stp_set_int_parameter(c, desc.name, desc.deflt.integer); - } - else if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping int %s\n", desc.name); - break; - case STP_PARAMETER_TYPE_DOUBLE: - if (stp_check_float_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING float %s %f\n", - desc.name, stp_get_float_parameter(v, desc.name)); - stp_set_float_parameter(c, desc.name, - stp_get_float_parameter(v, desc.name)); - } - else if (desc.is_mandatory) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted float %s %f\n", - desc.name, desc.deflt.dbl); - stp_set_float_parameter(c, desc.name, desc.deflt.dbl); - } - else if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping float %s\n", desc.name); - break; - case STP_PARAMETER_TYPE_DIMENSION: - if (stp_check_dimension_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING dimension %s %d\n", - desc.name, stp_get_dimension_parameter(v, desc.name)); - stp_set_dimension_parameter(c, desc.name, - stp_get_dimension_parameter(v, desc.name)); - } - else if (desc.is_mandatory) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted dimension %s %d\n", - desc.name, desc.deflt.dimension); - stp_set_dimension_parameter(c, desc.name, desc.deflt.dimension); - } - else if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping dimension %s\n", desc.name); - break; - case STP_PARAMETER_TYPE_CURVE: - if (stp_check_curve_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING curve %s\n", desc.name); - stp_set_curve_parameter(c, desc.name, - stp_get_curve_parameter(v, desc.name)); - } - else if (desc.is_mandatory) - { - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted curve %s\n", desc.name); - stp_set_curve_parameter(c, desc.name, desc.deflt.curve); - } - else if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: skipping curve %s\n", - desc.name); - break; - default: - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Ignoring unknown type parameter %s (%d)\n", - desc.name, desc.p_type); - break; - } - stp_parameter_description_destroy(&desc); - } - stp_parameter_list_destroy(params); - mxml = stp_xmltree_create_from_vars(c); - if (mxml) - { - fputs("\n\n", f_options); - stp_mxmlSaveFile(mxml, f_options, STP_MXML_NO_CALLBACK); - stp_mxmlDelete(mxml); - } - (void) fclose(f_options); - stp_vars_destroy(c); - if (!suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Done saving parameters to %s\n", save_name); -} - -static stp_vars_t * -load_options(const char *load_name) -{ - FILE *f_options = fopen(load_name, "r"); - if (f_options) - { - stp_vars_t *settings = NULL; - stp_mxml_node_t *mxml; - mxml = stp_mxmlLoadFile(NULL, f_options, STP_MXML_NO_CALLBACK); - if (mxml) - { - stp_mxml_node_t *nxml = - stp_mxmlFindElement(mxml, mxml, "vars", NULL, NULL, - STP_MXML_DESCEND); - if (nxml) - { - settings = stp_vars_create_from_xmltree_ref(nxml->child, mxml); - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: loading options from %s\n", - load_file_name); - if (! suppress_messages) - stpi_vars_print_error(settings, "DEBUG"); - } - } - else - fprintf(stderr, "DEBUG: Unable to load options from %s\n", - load_file_name); - fclose(f_options); - return settings; - } - return NULL; -} - -#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ - -/* - * 'main()' - Main entry and processing of driver. - */ - -int /* O - Exit status */ -main(int argc, /* I - Number of command-line arguments */ - char *argv[]) /* I - Command-line arguments */ -{ - int fd; /* File descriptor */ - cups_image_t cups; /* CUPS image */ - const char *ppdfile; /* PPD environment variable */ - ppd_file_t *ppd; /* PPD file */ - ppd_size_t *size; - const stp_printer_t *printer; /* Printer driver */ - int num_options; /* Number of CUPS options */ - cups_option_t *options; /* CUPS options */ - stp_vars_t *v = NULL; - stp_vars_t *default_settings; - int initialized_job = 0; - const char *version_id; - struct tms tms; - long clocks_per_sec; - struct timeval t1, t2; - char *page_size_name = NULL; - int aborted = 0; -#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS - stp_vars_t *loaded_settings = NULL; -#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ - - - /* - * Don't buffer error/status messages... - */ - - setbuf(stderr, NULL); - - if (getenv("STP_SUPPRESS_MESSAGES")) - suppress_messages = 1; - - if (getenv("STP_SUPPRESS_VERBOSE_MESSAGES")) - suppress_verbose_messages = 1; - - /* - * Initialize libgutenprint - */ - - po = stp_i18n_load(getenv("LANG")); - - theImage.rep = ∪︀ - - (void) gettimeofday(&t1, NULL); - stp_init(); - version_id = stp_get_version(); - - /* - * Check for valid arguments... - */ - if (argc < 6 || argc > 7) - { - /* - * We don't have the correct number of arguments; write an error message - * and return. - */ - - stp_i18n_printf(po, _("Usage: rastertoprinter job-id user title copies " - "options [file]\n")); - return (1); - } - - if (! suppress_messages) - { - fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n"); - fprintf(stderr, "DEBUG: Gutenprint: VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); - fprintf(stderr, "DEBUG: Gutenprint: %s Starting\n", version_id); - fprintf(stderr, "DEBUG: Gutenprint: command line: %s '%s' '%s' '%s' '%s' %s%s%s%s\n", - argv[0], argv[1], argv[2], argv[3], argv[4], "", - argc >= 7 ? " '" : "", - argc >= 7 ? argv[6] : "", - argc >= 7 ? "'" : ""); - } - - /* - * Get the PPD file... - */ - - if ((ppdfile = getenv("PPD")) == NULL) - { - stp_i18n_printf(po, _("ERROR: No PPD file, unable to continue!\n")); - return (1); - } - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: using PPD file %s\n", ppdfile); - - if ((ppd = ppdOpenFile(ppdfile)) == NULL) - { - stp_i18n_printf(po, _("ERROR: Gutenprint was unable to load PPD file " - "\"%s\"!\n"), ppdfile); - return (1); - } - - if (ppd->modelname == NULL) - { - stp_i18n_printf(po, _("ERROR: Gutenprint did not find a ModelName " - "attribute in PPD file \"%s\"!\n"), ppdfile); - ppdClose(ppd); - return (1); - } - - if (ppd->nickname == NULL) - { - stp_i18n_printf(po, _("ERROR: Gutenprint did not find a NickName attribute " - "in PPD file \"%s\"!\n"), ppdfile); - ppdClose(ppd); - return (1); - } - else if (strlen(ppd->nickname) < - strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING) + 3) - { - stp_i18n_printf(po, _("ERROR: Gutenprint found a corrupted NickName " - "attribute in PPD file \"%s\"!\n"), ppdfile); - ppdClose(ppd); - return (1); - } - else if (strcmp(ppd->nickname + strlen(ppd->modelname) + - strlen(CUPS_PPD_NICKNAME_STRING), version_id) != 0 && - (strlen(ppd->nickname + strlen(ppd->modelname) + - strlen(CUPS_PPD_NICKNAME_STRING)) < strlen(version_id) || - !((strncmp(ppd->nickname + strlen(ppd->modelname) + - strlen(CUPS_PPD_NICKNAME_STRING), version_id, - strlen(version_id)) == 0) && - *(ppd->nickname + strlen(ppd->modelname) + - strlen(CUPS_PPD_NICKNAME_STRING)) != ' '))) - { - stp_i18n_printf(po, _("ERROR: The PPD version (%s) is not compatible with " - "Gutenprint %s. Please run `%scups-genppdupdate' as administrator.\n"), - ppd->nickname+strlen(ppd->modelname)+strlen(CUPS_PPD_NICKNAME_STRING), - version_id, SBINDIR); - fprintf(stderr, "DEBUG: Gutenprint: If you have upgraded your version of Gutenprint\n"); - fprintf(stderr, "DEBUG: Gutenprint: recently, you must reinstall all printer queues.\n"); - fprintf(stderr, "DEBUG: Gutenprint: If the previous installed version of Gutenprint\n"); - fprintf(stderr, "DEBUG: Gutenprint: was 5.0.0 or higher, you can use the `cups-genppdupdate'\n"); - fprintf(stderr, "DEBUG: Gutenprint: program to do this; if the previous installed version\n"); - fprintf(stderr, "DEBUG: Gutenprint: was older, you can use the Modify Printer command via\n"); - fprintf(stderr, "DEBUG: Gutenprint: the CUPS web interface: http://localhost:631/printers.\n"); - ppdClose(ppd); - return 1; - } - - /* - * Get the STP options, if any... - */ - - num_options = cupsParseOptions(argv[5], 0, &options); - ppdMarkDefaults(ppd); - cupsMarkOptions(ppd, num_options, options); - size = ppdPageSize(ppd, NULL); - - if (size->name) - page_size_name = stp_strdup(size->name); - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: CUPS option count is %d (%d bytes)\n", - num_options, (int)strlen(argv[5])); - - if (num_options > 0) - { - int i; - for (i = 0; i < num_options; i++) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: CUPS option %d %s = %s\n", - i, options[i].name, options[i].value); -#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS - if (!strcmp(options[i].name, "SaveFileName")) - save_file_name = options[i].value; - if (!strcmp(options[i].name, "LoadFileName")) - load_file_name = options[i].value; -#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ - } - } - - /* - * Figure out which driver to use... - */ - - printer = stp_get_printer_by_driver(ppd->modelname); - if (!printer) - printer = stp_get_printer_by_long_name(ppd->modelname); - - if (printer == NULL) - { - stp_i18n_printf(po, _("ERROR: Unable to find Gutenprint driver named " - "\"%s\"!\n"), ppd->modelname); - ppdClose(ppd); - return (1); - } - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Driver %s\n", ppd->modelname); - - /* - * Open the page stream... - */ - - if (argc == 7) - { - if ((fd = open(argv[6], O_RDONLY)) == -1) - { - stp_i18n_printf(po, _("ERROR: Gutenprint was unable to open raster file " - "\"%s\" - %s"), argv[6], strerror(errno)); - sleep(1); - return (1); - } - } - else - fd = 0; - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Using fd %d\n", fd); - - default_settings = stp_vars_create_copy(stp_printer_get_defaults(printer)); - stp_set_printer_defaults(default_settings, printer); -#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS - if (load_file_name) - loaded_settings = load_options(load_file_name); -#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ - stp_set_float_parameter(default_settings, "AppGamma", 1.0); - set_all_options(default_settings, options, num_options, ppd); - - cupsFreeOptions(num_options, options); - ppdClose(ppd); - - cups.ras = cupsRasterOpen(fd, CUPS_RASTER_READ); - - /* - * Process pages as needed... - */ - - cups.page = 0; - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: About to start printing loop.\n"); - - /* - * Read the first page header, which we need in order to set up - * the page. - */ - signal(SIGTERM, cancel_job); - while (CUPS_READ_HEADER(cups.ras, &cups.header)) - { - /* - * We don't know how many pages we're going to print, and - * we need to call stp_end_job at the completion of the job. - * Therefore, we need to keep v in scope after the termination - * of the loop to permit calling stp_end_job then. Therefore, - * we have to free the previous page's stp_vars_t at the start - * of the loop. - */ - if (v) - stp_vars_destroy(v); - - /* - * Setup printer driver variables... - */ - if (! suppress_messages) - { - fprintf(stderr, "DEBUG: Gutenprint: ================ Printing page %d ================\n", cups.page + 1); - fprintf(stderr, "PAGE: %d %d\n", cups.page + 1, cups.header.NumCopies); - } - v = initialize_page(&cups, default_settings, page_size_name); -#ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS - if (loaded_settings) - stp_copy_vars_from(v, loaded_settings); - if (save_file_name) - { - save_options(save_file_name, v); - save_file_name = NULL; - } -#endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ - if (! suppress_messages) - { - fprintf(stderr, "DEBUG: Gutenprint: Interim page settings:\n"); - stpi_vars_print_error(v, "DEBUG"); - } - - stp_merge_printvars(v, stp_printer_get_defaults(printer)); - - /* Pass along Collation settings */ - stp_set_boolean_parameter(v, "Collate", cups.header.Collate); - stp_set_boolean_parameter_active(v, "Collate", STP_PARAMETER_ACTIVE); - /* Pass along Copy settings */ - stp_set_int_parameter(v, "NumCopies", cups.header.NumCopies); - stp_set_int_parameter_active(v, "NumCopies", STP_PARAMETER_ACTIVE); - /* Pass along the page number */ - stp_set_int_parameter(v, "PageNumber", cups.page); - cups.row = 0; - if (! suppress_messages) - print_debug_block(v, &cups); - print_messages_as_errors = 1; - if (!stp_verify(v)) - { - fprintf(stderr, "DEBUG: Gutenprint: Options failed to verify.\n"); - fprintf(stderr, "DEBUG: Gutenprint: Make sure that you are using ESP Ghostscript rather\n"); - fprintf(stderr, "DEBUG: Gutenprint: than GNU or AFPL Ghostscript with CUPS.\n"); - fprintf(stderr, "DEBUG: Gutenprint: If this is not the cause, set LogLevel to debug to identify the problem.\n"); - aborted = 1; - break; - } - - if (!initialized_job) - { - stp_start_job(v, &theImage); - initialized_job = 1; - } - - if (!stp_print(v, &theImage)) - { - aborted = 1; - break; - } - print_messages_as_errors = 0; - - fflush(stdout); - - /* - * Purge any remaining bitmap data... - */ - if (cups.row < cups.header.cupsHeight) - purge_excess_data(&cups); - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: ================ Done printing page %d ================\n", cups.page + 1); - cups.page ++; - } - if (v) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: %s job\n", - aborted ? "Aborted" : "Ending"); - stp_end_job(v, &theImage); - fflush(stdout); - stp_vars_destroy(v); - } - cupsRasterClose(cups.ras); - (void) times(&tms); - (void) gettimeofday(&t2, NULL); - clocks_per_sec = sysconf(_SC_CLK_TCK); - fprintf(stderr, "DEBUG: Gutenprint: stats %.0fB, %.3fu, %.3fs, %.3fel\n", - total_bytes_printed, - (double) tms.tms_utime / clocks_per_sec, - (double) tms.tms_stime / clocks_per_sec, - (double) (t2.tv_sec - t1.tv_sec) + - ((double) (t2.tv_usec - t1.tv_usec)) / 1000000.0); - if (!suppress_messages) - { - fprintf(stderr, "DEBUG: Gutenprint: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); - fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n"); - } - stp_vars_destroy(default_settings); - if (page_size_name) - stp_free(page_size_name); - if (fd != 0) - close(fd); - return aborted ? 1 : 0; -} - - -/* - * 'cups_writefunc()' - Write data to a file... - */ - -static void -cups_writefunc(void *file, const char *buf, size_t bytes) -{ - FILE *prn = (FILE *)file; - total_bytes_printed += bytes; - fwrite(buf, 1, bytes, prn); -} - -static void -cups_errfunc(void *file, const char *buf, size_t bytes) -{ - size_t next_nl = 0; - size_t where = 0; - FILE *prn = (FILE *)file; - while (where < bytes) - { - if (bytes - where > 6 && strncmp(buf, "ERROR:", 6) == 0) - { - fputs("ERROR: Gutenprint error:", prn); - buf += 6; - } - else if (print_messages_as_errors) - fputs("ERROR: Gutenprint error: ", prn); - else - fputs("DEBUG: Gutenprint internal: ", prn); - while (next_nl < bytes) - { - if (buf[next_nl++] == '\n') - break; - } - fwrite(buf + where, 1, next_nl - where, prn); - where = next_nl; - } -} - - -/* - * 'cancel_job()' - Cancel the current job... - */ - -static void -cancel_job(int sig) /* I - Signal */ -{ - (void)sig; - Image_status = STP_IMAGE_STATUS_ABORT; -} - -/* - * 'Image_get_appname()' - Get the application we are running. - */ - -static const char * /* O - Application name */ -Image_get_appname(stp_image_t *image) /* I - Image */ -{ - (void)image; - - return ("CUPS driver based on Gutenprint"); -} - - -/* - * 'Image_get_row()' - Get one row of the image. - */ - -static void -throwaway_data(int amount, cups_image_t *cups) -{ - unsigned char trash[4096]; /* Throwaway */ - int block_count = amount / 4096; - int leftover = amount % 4096; - while (block_count > 0) - { - cupsRasterReadPixels(cups->ras, trash, 4096); - block_count--; - } - if (leftover) - cupsRasterReadPixels(cups->ras, trash, leftover); -} - -static stp_image_status_t -Image_get_row(stp_image_t *image, /* I - Image */ - unsigned char *data, /* O - Row */ - size_t byte_limit, /* I - how many bytes in data */ - int row) /* I - Row number (unused) */ -{ - cups_image_t *cups; /* CUPS image */ - int i; /* Looping var */ - int bytes_per_line; - int margin; - stp_image_status_t tmp_image_status = Image_status; - unsigned char *orig = data; /* Temporary pointer */ - static int warned = 0; /* Error warning printed? */ - int new_percent; - int left_margin, right_margin; - - if ((cups = (cups_image_t *)(image->rep)) == NULL) - { - stp_i18n_printf(po, _("ERROR: Gutenprint image is not initialized! " - "Please report this bug to " - "gimp-print-devel@lists.sourceforge.net\n")); - return STP_IMAGE_STATUS_ABORT; - } - bytes_per_line = - ((cups->adjusted_width * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / - CHAR_BIT; - - left_margin = ((cups->left_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / - CHAR_BIT; - right_margin = ((cups->right_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / - CHAR_BIT; - margin = cups->header.cupsBytesPerLine - left_margin - bytes_per_line - - right_margin; - - if (cups->row < cups->header.cupsHeight) - { - if (! suppress_messages && ! suppress_verbose_messages) - fprintf(stderr, "DEBUG2: Gutenprint: Reading %d %d\n", - bytes_per_line, cups->row); - while (cups->row <= row && cups->row < cups->header.cupsHeight) - { - if (left_margin > 0) - { - if (! suppress_messages && ! suppress_verbose_messages) - fprintf(stderr, "DEBUG2: Gutenprint: Tossing left %d (%d)\n", - left_margin, cups->left_trim); - throwaway_data(left_margin, cups); - } - cupsRasterReadPixels(cups->ras, data, bytes_per_line); - cups->row ++; - if (margin + right_margin > 0) - { - if (! suppress_messages && ! suppress_verbose_messages) - fprintf(stderr, "DEBUG2: Gutenprint: Tossing right %d (%d) + %d\n", - right_margin, cups->right_trim, margin); - throwaway_data(margin + right_margin, cups); - } - } - } - else - { - switch (cups->header.cupsColorSpace) - { - case CUPS_CSPACE_K: - case CUPS_CSPACE_CMYK: - case CUPS_CSPACE_KCMY: - case CUPS_CSPACE_CMY: - memset(data, 0, bytes_per_line); - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_W: - memset(data, ((1 << CHAR_BIT) - 1), bytes_per_line); - break; - default: - stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace " - "(%d)!\n"), cups->header.cupsColorSpace); - return STP_IMAGE_STATUS_ABORT; - } - } - - /* - * This exists to print non-ADSC input which has messed up the job - * input, such as that generated by psnup. The output is barely - * legible, but it's better than the garbage output otherwise. - */ - data = orig; - if (cups->header.cupsBitsPerPixel == 1) - { - if (warned == 0) - { - fputs(_("WARNING: Gutenprint detected a bad color depth (1). " - "Output quality is degraded. Are you using psnup or " - "non-ADSC PostScript?\n"), stderr); - warned = 1; - } - for (i = cups->adjusted_width - 1; i >= 0; i--) - { - if ( (data[i/8] >> (7 - i%8)) &0x1) - data[i]=255; - else - data[i]=0; - } - } - - new_percent = (int) (100.0 * cups->row / cups->header.cupsHeight); - if (new_percent > cups->last_percent) - { - if (! suppress_messages) - { - stp_i18n_printf(po, _("INFO: Printing page %d, %d%%\n"), - cups->page + 1, new_percent); - fprintf(stderr, "ATTR: job-media-progress=%d\n", new_percent); - } - cups->last_percent = new_percent; - } - - if (tmp_image_status != STP_IMAGE_STATUS_OK) - { - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Image status %d\n", tmp_image_status); - } - return tmp_image_status; -} - - -/* - * 'Image_height()' - Return the height of an image. - */ - -static int /* O - Height in pixels */ -Image_height(stp_image_t *image) /* I - Image */ -{ - cups_image_t *cups; /* CUPS image */ - - - if ((cups = (cups_image_t *)(image->rep)) == NULL) - return (0); - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Image_height %d\n", cups->adjusted_height); - return (cups->adjusted_height); -} - - -/* - * 'Image_init()' - Initialize an image. - */ - -static void -Image_init(stp_image_t *image) /* I - Image */ -{ - cups_image_t *cups; /* CUPS image */ - - if ((cups = (cups_image_t *)(image->rep)) == NULL) - return; - cups->last_percent = 0; - - if (! suppress_messages) - stp_i18n_printf(po, _("INFO: Starting page %d...\n"), cups->page + 1); - /* cups->page + 1 because users expect 1-based counting */ -} - -/* - * 'Image_progress_conclude()' - Close the progress display. - */ - -static void -Image_conclude(stp_image_t *image) /* I - Image */ -{ - cups_image_t *cups; /* CUPS image */ - - - if ((cups = (cups_image_t *)(image->rep)) == NULL) - return; - - if (! suppress_messages) - stp_i18n_printf(po, _("INFO: Finished page %d...\n"), cups->page + 1); -} - -/* - * 'Image_width()' - Return the width of an image. - */ - -static int /* O - Width in pixels */ -Image_width(stp_image_t *image) /* I - Image */ -{ - cups_image_t *cups; /* CUPS image */ - - - if ((cups = (cups_image_t *)(image->rep)) == NULL) - return (0); - - if (! suppress_messages) - fprintf(stderr, "DEBUG: Gutenprint: Image_width %d\n", cups->adjusted_width); - return (cups->adjusted_width); -} diff --git a/src/cups/test-ppds b/src/cups/test-ppds deleted file mode 100755 index 27e5a8d..0000000 --- a/src/cups/test-ppds +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh - -# Test PPD conformance -# -# Copyright 2006-2017 Robert Krawitz (rlk@alum.mit.edu) -# -# 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. - -# Keeping this up to date with changing CUPS versions is a real headache - -make EXTRA_GENPPD_OPTS='-b -Z' ppd-clean ppd-global ppd-nls ppd-nonls - -# Most non-Macintosh systems won't have the Macintosh-specific profiles -# installed in Macintosh-specific locations. -# -# 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 -I filters' - -ppd_count=`find ppd \( -name '*.ppd.gz' -o -name '*.ppd' \) -print | wc -l` - -if [ -n "$STP_PARALLEL" ] ; then - PARALLEL="-P $STP_PARALLEL" -fi - -failures="`find ppd -name '*.ppd*' -print | sort -t/ -k3 -k2 | xargs $PARALLEL cupstestppd $cupstestppdopts |grep 'FAIL$' | awk -F: '{print $1}'`" - -if [ -z "$failures" ] ; then - echo "All $ppd_count PPD files pass" - exit 0 -fi - -# Use this with "normal" PPD files without translated numbers. -#echo $failures | xargs cupstestppd $cupstestppdopts 2>&1 | egrep -v 'Missing "[a-z][a-z](_[A-Z][A-Z])?" translation string for option Stp((Brightness|Contrast|Saturation), choice None|([a-zA-Z0-9]+, choice (-?[0-9]+)))!$' |egrep -v 'Missing cupsICCProfile file' - -# Use this with PPD files with translated numbers (genppd -N) -# With normal globalized PPD files this will yield hundreds of megabytes -# of errors. -echo $failures | xargs cupstestppd -r $cupstestppdopts 2>&1 |egrep -v 'Missing cupsICCProfile file' - -fail_count=`echo "$failures" | wc -l` -echo "$fail_count/$ppd_count fail" -exit 1 diff --git a/src/cups/test-ppds.in b/src/cups/test-ppds.in new file mode 100644 index 0000000..3664f4e --- /dev/null +++ b/src/cups/test-ppds.in @@ -0,0 +1,127 @@ +#!@BASHREAL@ + +# Test PPD conformance +# +# Copyright 2006-2017 Robert Krawitz (rlk@alum.mit.edu) +# +# 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, see . + +################################################################ +# TEST FOR PPD FILE COMPLIANCE +# +# Keeping this up to date with changing CUPS versions is a real headache. +# This test, however, is particularly important; PPD conformance failures +# are one of the most frequently observed problems, and a lot of apps +# (and CUPS itself) are very sensitive -- arguably overly so -- to +# violations of the spec. +# +# We can't skip the translated PPD files either; sometimes translations +# blow out token maximum lengths. + +if [[ -n "$STP_TEST_LOG_PREFIX" ]] ; then + redir="${STP_TEST_LOG_PREFIX}${0##*/}_$$.log" + if [[ -n $BUILD_VERBOSE ]] ; then + exec > >(tee -a "$redir" >&3) + else + exec 1>>"$redir" + fi + exec 2>&1 +fi + +[[ -n "$STP_TEST_DEBUG" ]] && DEBUG=echo + +[[ -z $STP_TEST_SUITE || -z $STP_TEST_PROFILE ]] && STP_TEST_PROFILE=full + +PPD_DIR=ppdtest + +PPD_PARALLEL=200 + +function test_full() { + rm -rf "$PPD_DIR" + echo "GENERATING PPD FILES (all):" + set -e + $DEBUG make "PPD_DIR=$PPD_DIR" EXTRA_GENPPD_OPTS='-b -Z' ppd-clean ppd-global-a ppd-nls-a ppd-nonls-a +} + +function test_fast() { + rm -rf "$PPD_DIR" + echo "GENERATING PPD FILES (fast):" + set -e + $DEBUG make "PPD_DIR=$PPD_DIR" EXTRA_GENPPD_OPTS='-b -Z' ppd-clean ppd-nonls + if [[ -n $STP_TEST_DIST ]] ; then + echo "GENERATING PPD FILES (global):" + set -e + $DEBUG make "PPD_DIR=$PPD_DIR" EXTRA_GENPPD_OPTS='-b -Z -S' ppd-global + fi + PPD_PARALLEL=20 +} + +function test_minimal() { + rm -rf "$PPD_DIR" + echo "GENERATING PPD FILES (minimal):" + set -e + $DEBUG make "PPD_DIR=$PPD_DIR" EXTRA_GENPPD_OPTS='-b -Z -S' ppd-clean ppd-nonls + PPD_PARALLEL=20 +} + +case "$STP_TEST_PROFILE" in + full|fast|minimal) + test_$STP_TEST_PROFILE + ;; + *) + exit 77 +esac + +if [[ -n "$STP_TEST_DEBUG" ]] ; then + echo "Would run $0 $@" + exit 0 +fi + +ppd_count=$(find "$PPD_DIR" -name '*.ppd' -print | wc -l) + +# Setting a limit on the number of files per invocation improves +# performance by about a minute by reducing the tail at the end. +[[ -n $STP_PARALLEL ]] && PARALLEL="-P $STP_PARALLEL -L $PPD_PARALLEL" + +# Most non-Macintosh systems won't have the Macintosh-specific profiles +# installed in Macintosh-specific locations. +# +# 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 -I filters' + +# This lets us report progress +testcmd="echo -n . 1>&2; cupstestppd $cupstestppdopts "'"$@"' + +echo -n "TESTING PPD FILES: " +failures="$(find $PPD_DIR -name '*.ppd*' -print | sort -t/ -k3 -k2 | xargs $PARALLEL sh -c "$testcmd" |grep 'FAIL$' | awk -F: '{print $1}')" + +if [[ -z "$failures" ]] ; then + echo "All $ppd_count PPD files pass" + rm -rf $PPD_DIR + exit 0 +fi + +# Use this with "normal" PPD files without translated numbers. +#echo $failures | xargs cupstestppd $cupstestppdopts 2>&1 | egrep -v 'Missing "[a-z][a-z](_[A-Z][A-Z])?" translation string for option Stp((Brightness|Contrast|Saturation), choice None|([a-zA-Z0-9]+, choice (-?[0-9]+)))!$' |egrep -v 'Missing cupsICCProfile file' + +# Use this with PPD files with translated numbers (genppd -N) +# With normal globalized PPD files this will yield hundreds of megabytes +# of errors. +echo $failures | xargs cupstestppd -r $cupstestppdopts 2>&1 + +fail_count=`echo "$failures" | wc -l` +echo "$fail_count/$ppd_count fail" +exit 1 diff --git a/src/cups/test-rastertogutenprint.check.in b/src/cups/test-rastertogutenprint.check.in new file mode 100644 index 0000000..cd3f38b --- /dev/null +++ b/src/cups/test-rastertogutenprint.check.in @@ -0,0 +1,77 @@ +#!@BASHREAL@ + +# Harness for rastertogutenprint tester. +# +# Copyright 2007-2017 Robert Krawitz (rlk@alum.mit.edu) +# +# 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, see . + +if [[ -n "$STP_TEST_LOG_PREFIX" ]] ; then + redir="${STP_TEST_LOG_PREFIX}${0##*/}_$$.log" + if [[ -n $BUILD_VERBOSE ]] ; then + exec > >(tee -a "$redir" >&3) + else + exec 1>>"$redir" + fi + exec 2>&1 +fi + +STP_TEST_PROFILE=${STP_TEST_PROFILE:-full} + +set -e + +declare FASTOPT="-o StpDitherAlgorithm=VeryFast -l -N -o ColorCorrection=Raw" +declare MIN_PRINTERS="@MINIMAL_PRINTERS_TO_TEST@" + +function runit() { + echo "================================================================" + echo "$@" + [[ -z $STP_TEST_DEBUG ]] && "$@" +} + +function test_minimal() { + runit ./test-rastertogutenprint -s $FASTOPT -p 1 \ + $MIN_PRINTERS +} + +function test_fast() { + runit ./test-rastertogutenprint -S -s $FASTOPT -p 2 +} + +function test_full() { + runit ./test-rastertogutenprint -l -p 2 $FASTOPT + runit ./test-rastertogutenprint -S -s -X +} + +function test_valgrind_minimal() { + runit ./test-rastertogutenprint -v -v -s $FASTOPT -p 1 \ + $MIN_PRINTERS +} + +function test_valgrind_fast() { + test_valgrind_minimal +} + +function test_valgrind() { + runit ./test-rastertogutenprint -v -v -S -s $FASTOPT -p 2 +} + +case "$STP_TEST_PROFILE" in + full|fast|valgrind|valgrind_minimal|valgrind_fast|minimal) + test_$STP_TEST_PROFILE + ;; + *) + exit 77 + ;; +esac diff --git a/src/cups/test-rastertogutenprint.in b/src/cups/test-rastertogutenprint.in old mode 100755 new mode 100644 index 75d4102..410814d --- a/src/cups/test-rastertogutenprint.in +++ b/src/cups/test-rastertogutenprint.in @@ -1,4 +1,4 @@ -#!@SHELL@ +#!@BASH@ # Driver for rastertogutenprint tester. # @@ -15,142 +15,175 @@ # 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. +# along with this program. If not, see . retval=0 -if [ -z "$srcdir" -o "$srcdir" = "." ] ; then - sdir=`pwd` -elif [ -n "`echo $srcdir |grep '^/'`" ] ; then +if [[ -z $srcdir || $srcdir = . ]] ; then + sdir=$(pwd) +elif [[ $srcdir =~ ^/ ]] ; then sdir="$srcdir" else - sdir="`pwd`/$srcdir" + sdir="$(pwd)/$srcdir" fi -if [ -z "$STP_DATA_PATH" ] ; then - STP_DATA_PATH="$sdir/../xml" - export STP_DATA_PATH -fi +export STP_DATA_PATH=${STP_DATA_PATH:-"$sdir/../xml"} +export STP_MODULE_PATH=${STP_MODULE_PATH:-"$sdir/../main:$sdir/../main/.libs"} + +declare single=0 +declare verbose=0 +declare valgrind=0 +declare make_ppds=1 +declare skip_simplified=0 +declare postscript=0 +declare use_explicit_quality=0 +declare extra_genppd_opts=-Z +declare use_smallest_pagesize=0 +declare cupsargs='' +declare printers_to_test='' -if [ -z "$STP_MODULE_PATH" ] ; then - STP_MODULE_PATH="$sdir/../main:$sdir/../main/.libs" - export STP_MODULE_PATH +if [ -n "$STP_TEST_DEBUG" ] ; then + echo "Would run with single=$single skip_simplified=$skip_simplified extra_genppd_opts=$extra_genppd_opts cupsargs=$cupsargs use_explicit_quality=$use_explicit_quality valopts=$valopts valgrind=$valgrind" + exit 0 fi -single='' -all_models='' -verbose='' -valgrind=0 -make_ppds=1 -md5dir='' -outdir='' -cupsargs='' -postscript='' -npages=3 -jobs=${STP_PARALLEL:-1} -enable_static='@ENABLE_STATIC@' -enable_shared='@ENABLE_SHARED@' - -if [ -r "$sdir/../../src/cups/gutenprint-users-manual.pdf" ] ; then +declare family=$STP_TEST_FAMILY +declare all_models='' +declare md5dir='' +declare outdir='' +declare qualarg='' +declare npages=3 +declare jobs=${STP_PARALLEL:-1} +declare -A all_models= + +if [[ -r $sdir/../../src/cups/gutenprint-users-manual.pdf ]] ; then testfile="$sdir/../../src/cups/gutenprint-users-manual.pdf" else testfile="$sdir/../../doc/gutenprint-users-manual.pdf" fi +quality_presets=(FastEconomy Economy Draft Standard High Photo HighPhoto UltraPhoto Best) + usage() { - echo "Usage: test-rastertogutenprint [-s] [-v|--valgrind]" - exit 0; + cat <<'EOF' +Usage: test-rastertogutenprint [options] [PPD files...] + Options: + -s Run only one PPD file with a given model ID/family + -v Use valgrind + -c Use cachegrind + -g Use GDB attach in valgrind + -V Verbose output + -n Don't build PPD files prior to run + -O dir Save output in specified directory + -o opt Set option on CUPS command line + -m dir Save MD5 checksums in specified directory + -p pages Specify page range of input document to use + -P Use PostScript rather than PDF input + -t jobs Run jobs in parallel (alternatively, use STP_PARALLEL) + -f family Run printers only in the particular family + -S Skip simplified PPD files + -l Use lowest available quality setting + -L Use highest available quality setting + -X Don't use explicit quality setting + -N Use the smallest available page size +EOF +exit 0; } -set_args() { - while true ; do - case "$1" in - -s) single=1 ;; - -h*|--h*) usage ;; - -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 ;; - -n|--no-make-ppds) make_ppds='' ;; - -O|--output) shift; outdir="$1" ;; - -o|--option) shift; cupsargs="$cupsargs $1" ;; - -m|--md5dir) shift; md5dir="$1" ;; - -p|--pages) shift; npages="$1" ;; - -P|--postscript) shift; postscript=1 ;; - -t|--parallel) shift; jobs="$1" ;; - --) shift; args="$@"; return ;; - *) return ;; - esac - shift - done -} +while getopts "hvcgsVnO:m:o:p:St:lLXf:N" opt ; do + case "$opt" in + h*) usage ;; + v) valgrind=$((valgrind + 1)) ;; + c) valgrind=4 ;; + g) valopts='--vgdb=yes --error-exitcode=1' ;; + s) single=1 ;; + V) verbose=$((verbose+1)) ;; + n) make_ppds='' ;; + O) outdir="$OPTARG"; mkdir -p "$outdir" ;; + o) cupsargs="$cupsargs $OPTARG" ;; + m) md5dir="$OPTARG"; mkdir -p "$md5dir" ;; + p) npages="$OPTARG" ;; + P) postscript=1 ;; + t) jobs="$OPTARG" ;; + f) family="$OPTARG" ;; + S) skip_simplified=1 ;; + X) use_explicit_quality=0 ;; + l) use_explicit_quality=1 ;; + L) use_explicit_quality=2 ;; + N) use_smallest_pagesize=1 ;; + \?) usage ;; + *) echo "Unknown argument $opt"; usage ;; + esac +done -set_args `getopt hvcgsVnO:m:o:p: "$@"` +case "$valgrind" in + 4) + valopts='--tool=callgrind --dump-instr=yes --trace-jump=yes --error-exitcode=1' + ;; + '') + ;; + *) + valopts='--tool=memcheck --error-exitcode=1' + ;; +esac -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 -if [ -n "$md5dir" -a ! -d "$md5dir" ] ; then - mkdir -p "$md5dir" -fi +shift $((OPTIND - 1)) version="@GUTENPRINT_RELEASE_VERSION@"; -cupsdir="/usr/lib/cups/filter" -if [ -x "$cupsdir/pstoraster" -o -x "$cupsdir/gstoraster" -o -x "$cupsdir/cgpdftoraster" ] ; then - pages="24-`expr 24 + $npages - 1`" - if [ -n "$postscript" ] ; then - pages="page-ranges=$pages" - fi -else - pages='' -fi +rgp="./rastertogutenprint.$version" +cupsdir="$(cups-config --serverbin)/filter" +cgpdftoraster="$cupsdir/cgpdftoraster" +gstoraster="$cupsdir/gstoraster" +imagetoraster="$cupsdir/imagetoraster" +pdftops="$cupsdir/pdftops" +pstops="$cupsdir/pstops" +pstoraster="$cupsdir/pstoraster" -if [ ! -x "$cupsdir/cgpdftoraster" -a ! -x "$cupsdir/pdftops" -a ! -x "$cupsdir/gstoraster" ] ; then +if [[ ! -x $cgpdftoraster && ! -x $pdftops && ! -x $gstoraster ]] ; then echo 'CUPS does not appear to be installed, skipping test' exit 0 fi +if [[ -x $pstoraster || -x $gstoraster || -x $cgpdftoraster ]] ; then + pages="24-$((24 + $npages - 1))" + (( $postscript > 0 )) && pages="page-ranges=$pages" +else + pages='' +fi + cleanup() { - if [ -f "$tfile" ] ; then - rm -f $tfile - fi + [[ -n $tfile ]] && rm -f "$tfile" exit 1 } -pdfjam="`which pdfjam`" -if [ -z "$pdfjam" ] ; then - postscript=1 -fi - -if [ -n "$postscript" ] ; then - pdftops="`type -p pdftops`" +pdfjam=$(type -p pdfjam) +[[ -n $pdfjam ]] && postscript=1 - if [ ! -n "$pdftops" -o ! -x "$pdftops" ] ; then - pdftops="`whence pdftops`" - fi +if (( $postscript > 0 )) ; then + pdftops=$(type -p pdftops) - if [ -n "$pdftops" -a ! -x "$cupsdir/cgpdftoraster" ] ; then - tfile=`mktemp` + if [[ -n $pdftops && ! -x $cgpdftoraster ]] ; then + tfile=$(mktemp) trap cleanup 1 2 3 6 14 15 30 - "$pdftops" -f 24 -l `expr 24 + $npages - 1` "$testfile" $tfile + "$pdftops" -f 24 -l $((24 + $npages - 1)) "$testfile" $tfile fi else - tfile=`mktemp` + tfile=$(mktemp) trap cleanup 1 2 3 6 14 15 30 "$pdfjam" -q "$testfile" "$pages" -o $tfile fi -if [ -z "$verbose" ] ; then - STP_SUPPRESS_MESSAGES=1 - export STP_SUPPRESS_MESSAGES -fi +case "$verbose" in + 1) + export STP_SUPPRESS_VERBOSE_MESSAGES=1 + ;; + 0|'') + export STP_SUPPRESS_MESSAGES=1 + export STP_SUPPRESS_VERBOSE_MESSAGES=1 + ;; + *) + ;; +esac # Note that using CUPS arguments may trigger valgrind memory leaks in # CUPS. @@ -159,70 +192,54 @@ fi #cupsargs='PageSize=w324h495 Resolution=180dpi' #cupsargs='PageSize=A8' -run_rastertogp() { - case "$valgrind" in - 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 - if [ $? -ne 0 ] ; then - retval=1 - fi - exit $retval -} - get_ppds() { - args="$@" - if [ -n "$args" ] ; then - for f in "$@" ; do - if [ -r "$f" ] ; then + if [[ -n $@ || -n $printers_to_test ]] ; then + for f in "$@" $printers_to_test ; do + if [[ -r $f ]] ; then echo $f - elif [ -r "ppd/C/$f" ] ; then + elif [[ -r ppd/C/$f ]] ; then echo "ppd/C/$f" - elif [ -f "ppd/C/${f}.ppd" ] ; then + elif [[ -f ppd/C/${f}.ppd ]] ; then echo "ppd/C/${f}.ppd" - elif [ -f "ppd/C/${f}.ppd.gz" ] ; then + elif [[ -f ppd/C/${f}.ppd.gz ]] ; then echo "ppd/C/${f}.ppd.gz" - elif [ -f "ppd/C/${f}.ppd.GZ" ] ; then + elif [[ -f ppd/C/${f}.ppd.GZ ]] ; then echo "ppd/C/${f}.ppd.GZ" - elif [ -f "ppd/C/${f}.ppd.bz2" ] ; then + elif [[ -f ppd/C/${f}.ppd.bz2 ]] ; then echo "ppd/C/${f}.ppd.bz2" - elif [ -f "ppd/C/${f}.ppd.BZ2" ] ; then + elif [[ -f ppd/C/${f}.ppd.BZ2 ]] ; then echo "ppd/C/${f}.ppd.BZ2" - elif [ -f "ppd/C/${f}.ppd.z" ] ; then + elif [[ -f ppd/C/${f}.ppd.z ]] ; then echo "ppd/C/${f}.ppd.z" - elif [ -f "ppd/C/${f}.ppd.Z" ] ; then + elif [[ -f ppd/C/${f}.ppd.Z ]] ; then echo "ppd/C/${f}.ppd.Z" - elif [ -f "ppd/C/stp-${f}.ppd" ] ; then + elif [[ -f ppd/C/stp-${f}.ppd ]] ; then echo "ppd/C/stp-${f}.ppd" - elif [ -f "ppd/C/stp-${f}.ppd.gz" ] ; then + elif [[ -f ppd/C/stp-${f}.ppd.gz ]] ; then echo "ppd/C/stp-${f}.ppd.gz" - elif [ -f "ppd/C/stp-${f}.ppd.GZ" ] ; then + elif [[ -f ppd/C/stp-${f}.ppd.GZ ]] ; then echo "ppd/C/stp-${f}.ppd.GZ" - elif [ -f "ppd/C/stp-${f}.ppd.bz2" ] ; then + elif [[ -f ppd/C/stp-${f}.ppd.bz2 ]] ; then echo "ppd/C/stp-${f}.ppd.bz2" - elif [ -f "ppd/C/stp-${f}.ppd.BZ2" ] ; then + elif [[ -f ppd/C/stp-${f}.ppd.BZ2 ]] ; then echo "ppd/C/stp-${f}.ppd.BZ2" - elif [ -f "ppd/C/stp-${f}.ppd.z" ] ; then + elif [[ -f ppd/C/stp-${f}.ppd.z ]] ; then echo "ppd/C/stp-${f}.ppd.z" - elif [ -f "ppd/C/stp-${f}.ppd.Z" ] ; then + elif [[ -f ppd/C/stp-${f}.ppd.Z ]] ; then echo "ppd/C/stp-${f}.ppd.Z" - elif [ -f "ppd/C/stp-${f}.${version}.ppd" ] ; then + elif [[ -f ppd/C/stp-${f}.${version}.ppd ]] ; then echo "ppd/C/stp-${f}.${version}.ppd" - elif [ -f "ppd/C/stp-${f}.${version}.ppd.gz" ] ; then + elif [[ -f ppd/C/stp-${f}.${version}.ppd.gz ]] ; then echo "ppd/C/stp-${f}.${version}.ppd.gz" - elif [ -f "ppd/C/stp-${f}.${version}.ppd.GZ" ] ; then + elif [[ -f ppd/C/stp-${f}.${version}.ppd.GZ ]] ; then echo "ppd/C/stp-${f}.${version}.ppd.GZ" - elif [ -f "ppd/C/stp-${f}.${version}.ppd.bz2" ] ; then + elif [[ -f ppd/C/stp-${f}.${version}.ppd.bz2 ]] ; then echo "ppd/C/stp-${f}.${version}.ppd.bz2" - elif [ -f "ppd/C/stp-${f}.${version}.ppd.BZ2" ] ; then + elif [[ -f ppd/C/stp-${f}.${version}.ppd.BZ2 ]] ; then echo "ppd/C/stp-${f}.${version}.ppd.BZ2" - elif [ -f "ppd/C/stp-${f}.${version}.ppd.z" ] ; then + elif [[ -f ppd/C/stp-${f}.${version}.ppd.z ]] ; then echo "ppd/C/stp-${f}.${version}.ppd.z" - elif [ -f "ppd/C/stp-${f}.${version}.ppd.Z" ] ; then + elif [[ -f ppd/C/stp-${f}.${version}.ppd.Z ]] ; then echo "ppd/C/stp-${f}.${version}.ppd.Z" fi done @@ -231,48 +248,156 @@ get_ppds() { fi } -if [ -n "$make_ppds" -o ! -d ppd/C ] ; then - rm -rf ppd/C # Clean out any leftover simplified PPDs - make ppd-nonls-a # that double the time for the test +if [[ -n $make_ppds || ! -d ppd/C ]] ; then + rm -rf ppd/C ## not all systems can work with gzipped PPDs - find ppd/C -name '*.ppd.gz' | xargs gunzip -f + if [[ $skip_simplified == 1 ]] ; then + EXTRA_GENPPD_OPTS="$extra_genppd_opts" make ppd-nonls + else + EXTRA_GENPPD_OPTS="$extra_genppd_opts" make ppd-nonls-a + fi fi -do_output() { - if [ -n "`echo "$PPD" |grep '\.gz$'`" ] ; then - driver=`gunzip -c "$PPD" | grep '^\*StpDriverName:' |awk '{print $2}' | sed 's/"//g'` +find_page_size() { + ppd=$1 + (( $use_smallest_pagesize == 0 )) && return; + driver=$(grep '^\*StpDriverName' "$ppd" | sed -e 's/^[^"]*"//' -e 's/"//g') + pagesize=$(./min-pagesize "$driver") + [[ -n "$pagesize" ]] && echo "PageSize=$pagesize" +} + +find_resolution() { + ppd=$1 + resolutions=$(grep "^\\*Resolution " "$ppd" |sed -e 's,/.*,,' -e 's/.* //') + [[ -z "$resolutions" ]] && return + low_resolution=9999999999 + low_resolution_name='' + high_resolution=0 + high_resolution_name='' + for r in $resolutions ; do + res=$(sed -e 's/dpi//' -e 's/x/ \\\* /' -e 's/^\([0-9]*\)$/\1 \\\* \1/' <<< $r) + resnum=$(eval "expr $res") + if (( $resnum > $high_resolution )) ; then + high_resolution=$resnum + high_resolution_name=$r + fi + if (( $resnum < $low_resolution )) ; then + low_resolution=$resnum + low_resolution_name=$r + fi + done + if (( $use_explicit_quality == 1 )) ; then + echo "Resolution=$low_resolution_name" + elif (( $use_explicit_quality == 2 )) ; then + echo "Resolution=$high_resolution_name" + fi + +} + +find_quality_preset() { + ppd=$1 + if (( $use_explicit_quality == 1 )) ; then + for q in ${quality_presets[@]} ; do + if grep -q "^\\*StpQuality $q" "$ppd" ; then + echo "StpQuality=$q" + return + fi + done + elif (( "$use_explicit_quality" == 2 )) ; then + best_quality='' + for q in ${quality_presets[@]} ; do + grep -q "^\\*StpQuality $q" "$ppd" && best_quality=$q + done + [[ -n $best_quality ]] && echo "StpQuality=$best_quality" + fi +} + +find_quality() { + ppd=$1 + if [[ ! -r $ppd ]] ; then + echo "Can't find $ppd!" 1>&2 + exit 1; + fi + (( "$use_explicit_quality" == 0 )) && return + if grep -q '\*Resolution' "$ppd" ; then + find_resolution $ppd else - driver=`grep '^\*StpDriverName:' "$PPD" |awk '{print $2}' | sed 's/"//g'` + find_quality_preset $ppd fi - if [ -n "$outdir" ] ; then +} + +xgrep() { + pat=$1 + file=$2 + if [[ $file == *.gz ]] ; then + egrep -m1 $pat $file + else + zgrep $pat $file + fi +} + +runcmd() { + qualarg=$(find_quality "$PPD") + sizearg=$(find_page_size "$PPD") + a='1 1 1 1' + qarg="$qualarg $sizearg $cupsargs" + if [[ -x $cgpdftoraster ]] ; then + # cgpdftoraster doesn't like arguments. How rude. + $cgpdftoraster $a "" < "$tfile" + elif [[ -f $tfile && -x $gstoraster ]] ; then + $gstoraster $a "$qarg" < "$tfile" + elif [[ -f $tfile ]] ; then + $pstops $a $"qarg" < "$tfile" + elif [[ -x $pstoraster ]] ; then + $pdftops $a "$qarg" < "$tfile" | $pstops $a "$pages$qarg" | $pstoraster + elif [[ -x $gstoraster ]] ; then + $pdftops $a "$qarg" < "$tfile" | $gstoraster $a "$pages$qarg" + else + $imagetoraster $a "$qarg" < calibrate.ppm + fi +} + +do_output() { + driver=$(xgrep '^\*StpDriverName:' "$PPD" |awk '{print $2}' | sed 's/"//g') + if [[ -n $outdir ]] ; then cat > "$outdir/$driver.prn" - if [ -n "$md5dir" ] ; then - (cd "$outdir"; cat "$driver.prn") | md5sum | sed "s/-/\*$driver/" > "$md5dir/$driver.md5" + if [[ -n $md5dir ]] ; then + md5sum < "$outdir/$driver.prn" | sed "s/-/\*$driver/" > "$md5dir/$driver.md5" fi - elif [ -n "$md5dir" ] ; then + elif [[ -n $md5dir ]] ; then cat | md5sum | sed "s/-/\*$driver/" > "$md5dir/$driver.md5" else - cat > /dev/null + cat >/dev/null fi } +run_rastertogp() { + qualarg=$(find_quality "$PPD") + sizearg=$(find_page_size "$PPD") + vg="libtool --mode=execute valgrind $valopts --log-fd=3" + vg1="$vg --num-callers=50 --leak-check=yes --error-limit=no --error-exitcode=1" + rgpc="$rgp 1 1 1 1" + qarg="$qualarg $sizearg $cupsargs" + case "$valgrind" in + 1) $vg1 -q $rgpc ;; + 2) $vg1 --leak-resolution=high $rgpc "$qarg" ;; + 3) $vg1 --leak-resolution=high --show-reachable=yes $rgpc "$qarg";; + 4) $vg $rgpc "$qarg" ;; + 5) cat ;; + 6) cat > /dev/null ;; + *) $rgp 1 1 1 1 "$qarg" + esac +} + runme() { f="$1" - p=$(echo -n "`basename $f |sed -e 's/stp-//' -e 's/@GUTENPRINT_RELEASE_VERSION@.ppd.*$//'`... ") - PPD=$f - export PPD - if [ -x "$cupsdir/cgpdftoraster" ] ; then - output="$p `($cupsdir/cgpdftoraster 1 1 1 1 "" < "$tfile" 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" - elif [ -f "$tfile" -a -x "$cupsdir/gstoraster" ] ; then - output="$p `($cupsdir/gstoraster 1 1 1 1 \"$cupsargs\" < "$tfile" 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" - elif [ -f "$tfile" ] ; then - output="$p `($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="$p `($cupsdir/pdftops 1 1 1 1 \"$cupsargs\" < "$tfile" 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="$p `($cupsdir/pdftops 1 1 1 1 \"$cupsargs\" < "$tfile" 2>/dev/null | $cupsdir/gstoraster 1 1 1 1 \"$pages$cupsargs\" 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2 `" + p=${f#*stp-} + p=${p/${version}./} + export PPD=$f + if [[ -n $outdir || -n $md5dir ]] ; then + output="${p%.ppd*}...$( (runcmd 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2)" else - output="$p `($cupsdir/imagetoraster 1 1 1 1 \"$cupsargs\" < calibrate.ppm 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2`" + output="${p%.ppd*}...$( (runcmd 2>/dev/null | run_rastertogp >/dev/null) 2>&1 3>&2)" fi return $? } @@ -280,65 +405,79 @@ runme() { runall() { jobs="${1:-1}" rotor="${2:-0}" - shift - shift + shift 2 retval=0 jobno=0 for f in "$@" ; do - skip='' - if [ $((jobno % jobs)) -eq $rotor ] ; then + if (( $jobno == $rotor )) ; then runme "$f" - if [ $? -ne 0 ] ; then - retval=1 - fi + (( $? != 0 )) && retval=1 echo "$output" - echo "$output" |grep ERROR - if [ $? -eq 0 ] ; then - retval=1 - fi + grep -q 'ERROR:' <<< "$output" && retval=1 fi - jobno=$((jobno+1)) + jobno=$((($jobno+1) % $jobs)) done return $retval } +get_models() { + re='\*StpDriverModelFamily: ' + if (( ${#all_models[*]} <= 1 )) ; then + declare -a models=($(xargs grep -m1 -H "^$re" <<< $@ | sed "s/:$re/=/")) + for m in ${models[@]} ; do + model=${m#*=} + file=${m%%=*} + all_models[$file]=$model + done + fi +} + retval=0 -if [ -d ppd/C ] ; then - files=$(get_ppds $args) - if [ -n "$single" ] ; then - all_models="" - nondup_files="" - for f in $files ; do - if [ "$(basename $f .gz)" = "$(basename $f)" ] ; then - model=$(grep '^.StpDriverModelFamily' $f | awk '{print $2}') - else - model=$(gunzip -c $f | grep '^.StpDriverModelFamily' | awk '{print $2}') - fi - skip=0 - for m in $all_models ; do - if [ "$model" = "$m" ] ; then - skip=1 - break - fi - done - if [ "$skip" -eq 0 ] ; then - all_models="$model $all_models" - nondup_files="$nondup_files $f" +if [[ -d ppd/C ]] ; then + declare -a files=($(get_ppds "$@")) + declare -A models + declare -a nfiles + if (( $skip_simplified > 0 )) ; then + for f in ${files[@]} ; do + [[ $f != *.sim.ppd* ]] && nfiles+=($f) + done + files=(${nfiles[@]}) + fi + if [[ -n $family ]] ; then + get_models ${files[@]} + nfiles=() + for f in ${files[@]} ; do + [[ ${all_models[$f]} =~ $family ]] && nfiles+=($f) + done + files=(${nfiles[@]}) + fi + if (( $single != 0 )) ; then + declare -A seen_models + nfiles=() + get_models ${files[@]} + for f in ${files[@]} ; do + model=${all_models[$f]} + [[ $f == *.sim.ppd ]] && model="${model}_sim" + if [[ -z ${seen_models[$model]} ]] ; then + nfiles+=($f) + seen_models[$model]=1 fi done - files=$nondup_files + files=(${nfiles[@]}) fi - for i in $(seq 0 $(($jobs-1))) ; do - runall $jobs $i $files & + (( ${#files[@]} < $jobs )) && jobs=${#files[@]} + declare -a subproc=($(seq 0 $((jobs-1)))) + # Fire 'em off in the background... + for i in ${subproc[@]} ; do + runall $jobs $i ${files[@]} & done - for i in $(seq 0 $(($jobs-1))) ; do + # And wait for them to complete. + for i in ${subproc[@]} ; do wait -n - if [ "$?" -gt 0 ] ; then - retval=1 - fi + (( $? != 0 )) && retval=1 done fi -if [ -f "$tfile" ] ; then - rm -f $tfile -fi + +[[ -n $tfile ]] && rm -f "$tfile" +(( $retval == 0 )) && rm -rf ppd/C && rmdir ppd exit $retval -- cgit v1.2.3