summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2018-04-10 21:18:05 -0400
committerSolomon Peachy <pizza@shaftnet.org>2018-04-11 07:45:56 -0400
commit49d0a0c32924788dcaf15410eab0553a877294f7 (patch)
tree14442b24b776af4a93b6bae6031b0924599c25e6
parent3ae551083239b983ba5a24bef79b4ba1ced994fb (diff)
downloadselphy_print-49d0a0c32924788dcaf15410eab0553a877294f7.tar.gz
selphy_print-49d0a0c32924788dcaf15410eab0553a877294f7.tar.bz2
selphy_print-49d0a0c32924788dcaf15410eab0553a877294f7.zip
dnpds40: Add Citizen CW-01 support to the DNP/Citizen backend.
Supports the legacy spool format, but the intent is to move Gutenprint to generating the native data stream.
-rw-r--r--Makefile2
-rw-r--r--README29
-rw-r--r--backend_citizencw01.c915
-rw-r--r--backend_common.c2
-rw-r--r--backend_dnpds40.c293
5 files changed, 294 insertions, 947 deletions
diff --git a/Makefile b/Makefile
index 2801bd7..2f90505 100644
--- a/Makefile
+++ b/Makefile
@@ -46,7 +46,7 @@ CPPFLAGS += -DURI_PREFIX=\"$(BACKEND_NAME)\"
CFLAGS += -funit-at-a-time
# List of backends
-BACKENDS = sonyupdr150 kodak6800 kodak1400 shinkos2145 shinkos1245 canonselphy mitsu70x kodak605 dnpds40 citizencw01 mitsu9550 shinkos6245 shinkos6145 canonselphyneo mitsup95d magicard
+BACKENDS = sonyupdr150 kodak6800 kodak1400 shinkos2145 shinkos1245 canonselphy mitsu70x kodak605 dnpds40 mitsu9550 shinkos6245 shinkos6145 canonselphyneo mitsup95d magicard
# For the s6145 and mitsu70x backends
CPPFLAGS += -DUSE_DLOPEN
diff --git a/README b/README
index 10752e9..55d6018 100644
--- a/README
+++ b/README
@@ -725,11 +725,17 @@
Theoretically supported printers: (Untested)
+ Citizen CW-01 / OP900
Citizen CW-02 / OP900II
Citizen CX-02
DNP DS80DX
Mitsubishi CP-3800DW
+ Work-in-progress printers: (USB PIDs unknown)
+
+ Olmec OP900
+ ICI Imagedata OP900
+
Development was sponsored in part by:
Marco Di Antonio
@@ -761,29 +767,6 @@ Notes:
[3] Only supported by RX1HS, DS620, and DS820
***************************************************************************
- BACKEND=citizencw01
-
- Verified supported printers:
-
- Citizen CW-01
-
- Work-in-progress printers: (USB PIDs unknown)
-
- Olmec OP900
- ICI Imagedata OP900
-
- This backend supports additional commands:
-
- cw01 [command [arg] ]
-
- Valid commands:
-
- -i Query printer information (resolution, etc)
- -n Query printer counters
- -s Query printer status
- -N [ A | B ] Reset Counter A/B
-
- ***************************************************************************
BACKEND=magicard
Verified supported printers:
diff --git a/backend_citizencw01.c b/backend_citizencw01.c
deleted file mode 100644
index 1180082..0000000
--- a/backend_citizencw01.c
+++ /dev/null
@@ -1,915 +0,0 @@
-/*
- * Citizen CW-01 Photo Printer CUPS backend -- libusb-1.0 version
- *
- * (c) 2014-2018 Solomon Peachy <pizza@shaftnet.org>
- *
- * 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 3 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-3.0.html]
- *
- * SPDX-License-Identifier: GPL-3.0+
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#define BACKEND 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 <CR>, 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_RETRY_CURRENT;
- }
-
- 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;
-}
-
-static const char *citizencw01_prefixes[] = {
- "citizencw01",
- NULL,
-};
-
-/* Exported */
-struct dyesub_backend cw01_backend = {
- .name = "Citizen CW-01",
- .version = "0.13",
- .uri_prefixes = citizencw01_prefixes,
- .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, NULL, "citizencw01"},
-// { USB_VID_CITIZEN, USB_PID_OLMEC_OP900, P_CITIZEN_CW01, NULL, "citizenop900"}, // XXX add me
- { 0, 0, 0, NULL, NULL}
- }
-};
-
-/*
-
-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/backend_common.c b/backend_common.c
index 76fe83f..8c0b858 100644
--- a/backend_common.c
+++ b/backend_common.c
@@ -635,7 +635,6 @@ 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;
static struct dyesub_backend *backends[] = {
@@ -653,7 +652,6 @@ static struct dyesub_backend *backends[] = {
&mitsu9550_backend,
&mitsup95d_backend,
&dnpds40_backend,
- &cw01_backend,
&magicard_backend,
NULL,
};
diff --git a/backend_dnpds40.c b/backend_dnpds40.c
index b174dca..ba7ecb3 100644
--- a/backend_dnpds40.c
+++ b/backend_dnpds40.c
@@ -1,5 +1,5 @@
/*
- * DNP DS40/DS80 Photo Printer CUPS backend -- libusb-1.0 version
+ * Citizen / DNP Photo Printer CUPS backend -- libusb-1.0 version
*
* (c) 2013-2018 Solomon Peachy <pizza@shaftnet.org>
*
@@ -189,6 +189,33 @@ 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_ctx *ctx, int data_fd,
+ struct cw01_spool_hdr *hdr, int read_data);
+
+
+
static void dnpds40_build_cmd(struct dnpds40_cmd *cmd, char *arg1, char *arg2, uint32_t arg3_len)
{
memset(cmd, 0x20, sizeof(*cmd));
@@ -230,6 +257,7 @@ 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;
}
@@ -692,6 +720,10 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev,
if (FW_VER_CHECK(1,11))
ctx->supports_2x6 = 1;
break;
+ case P_CITIZEN_CW01:
+ ctx->native_width = 2048;
+ ctx->supports_6x9 = 1;
+ break;
case P_DNP_DS620:
ctx->native_width = 1920;
ctx->correct_count = 1;
@@ -856,6 +888,22 @@ static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev,
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 = 999; // non-zero
+ break;
+ }
+ break;
case P_DNP_DS80:
case P_DNP_DS80D:
switch (ctx->media) {
@@ -963,7 +1011,24 @@ static int dnpds40_read_parse(void *vctx, int data_fd) {
if (ctx->databuf[ctx->datalen + 0] != 0x1b ||
ctx->databuf[ctx->datalen + 1] != 0x50) {
- ERROR("Unrecognized header data format @%d!\n", ctx->datalen);
+ struct cw01_spool_hdr hdr;
+ /* See if it's the "classic" CW01 header */
+ memcpy(&hdr, ctx->databuf + ctx->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", ctx->datalen);
+ } else {
+ dpi = (hdr.res == DPI_600) ? 600 : 334;
+ i = cw01_read_parse(ctx, data_fd, &hdr, i);
+ if (i == CUPS_BACKEND_OK)
+ goto parsed;
+ else
+ return i;
+ }
+
return CUPS_BACKEND_CANCEL;
}
@@ -1096,7 +1161,7 @@ static int dnpds40_read_parse(void *vctx, int data_fd) {
/* Add in the size of this chunk */
ctx->datalen += sizeof(struct dnpds40_cmd) + j;
}
-
+parsed:
/* If we have no data.. don't bother */
if (!ctx->datalen)
return CUPS_BACKEND_CANCEL;
@@ -1129,7 +1194,7 @@ static int dnpds40_read_parse(void *vctx, int data_fd) {
}
/* Make sure MULTICUT is sane, most validation needs this */
- if (!ctx->multicut) {
+ if (!ctx->multicut && ctx->type != P_CITIZEN_CW01) {
WARNING("Missing or illegal MULTICUT command!\n");
if (dpi == 300)
ctx->buf_needed = 1;
@@ -1189,6 +1254,9 @@ static int dnpds40_read_parse(void *vctx, int data_fd) {
case P_DNP_DS820:
// Nothing; all sizes only need 1 buffer
break;
+ case P_CITIZEN_CW01:
+ ctx->buf_needed = 2;
+ break;
default: /* DS40/CX/RX1/CY/everything else */
if (ctx->matte) {
if (ctx->multicut == MULTICUT_6x8 ||
@@ -1207,6 +1275,11 @@ static int dnpds40_read_parse(void *vctx, int data_fd) {
break;
}
}
+ if (dpi == 334 && ctx->type != P_CITIZEN_CW01)
+ {
+ ERROR("Illegal resolution (%d) for printer!\n", dpi);
+ return CUPS_BACKEND_CANCEL;
+ }
/* Sanity-check type vs loaded media */
if (ctx->multicut < 100) {
@@ -1543,6 +1616,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
@@ -1808,6 +1901,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);
@@ -1860,6 +2007,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);
@@ -2047,6 +2197,7 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx)
free(resp);
}
+skip:
return CUPS_BACKEND_OK;
}
@@ -2547,7 +2698,7 @@ static int dnpds40_cmdline_arg(void *vctx, int argc, char **argv)
static const char *dnpds40_prefixes[] = {
"dnp_citizen",
"dnpds40", "dnpds80", "dnpds80dx", "dnpds620", "dnpds820", "dnprx1",
- "citizencw02", "citizencx02",
+ "citizencw01", "citizencw02", "citizencx02",
NULL
};
@@ -2558,6 +2709,7 @@ static const char *dnpds40_prefixes[] = {
#define USB_PID_DNP_DS80D 0x0007
#define USB_PID_DNP_DS620_OLD 0x0008
+#define USB_PID_CITIZEN_CW01 0x0002 // Maybe others?
#define USB_PID_CITIZEN_CW02 0x0006 // Also OP900II
#define USB_PID_CITIZEN_CX02 0x000A
@@ -2568,7 +2720,7 @@ static const char *dnpds40_prefixes[] = {
/* Exported */
struct dyesub_backend dnpds40_backend = {
.name = "DNP DS-series / Citizen C-series",
- .version = "0.98",
+ .version = "0.99",
.uri_prefixes = dnpds40_prefixes,
.cmdline_usage = dnpds40_cmdline,
.cmdline_arg = dnpds40_cmdline_arg,
@@ -2585,9 +2737,138 @@ struct dyesub_backend dnpds40_backend = {
{ USB_VID_CITIZEN, USB_PID_DNP_DS620_OLD, P_DNP_DS620, NULL, "dnpds620"},
{ USB_VID_DNP, USB_PID_DNP_DS620, P_DNP_DS620, NULL, "dnpds620"},
{ USB_VID_DNP, USB_PID_DNP_DS80D, P_DNP_DS80D, NULL, "dnpds80dx"},
+ { USB_VID_CITIZEN, USB_PID_CITIZEN_CW01, P_CITIZEN_CW01, NULL, "citizencw01"}, // Also OP900 ?
{ USB_VID_CITIZEN, USB_PID_CITIZEN_CW02, P_CITIZEN_OP900II, NULL, "citizencw02"}, // Also OP900II
{ USB_VID_CITIZEN, USB_PID_CITIZEN_CX02, P_DNP_DS620, NULL, "citizencx02"},
{ USB_VID_DNP, USB_PID_DNP_DS820, P_DNP_DS820, NULL, "dnpds820"},
{ 0, 0, 0, NULL, NULL}
}
};
+
+/* Legacy CW-01 spool file support */
+
+static int cw01_read_parse(struct dnpds40_ctx *ctx, 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, ctx->databuf, j);
+ remain -= j;
+ /* Read in the remaining spool data */
+ while (remain) {
+ i = read(data_fd, buf + j, remain);
+
+ if (i < 0)
+ 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 ctx->databuf! */
+#if 0
+ ctx->datalen += sprintf((char*)ctx->databuf + ctx->datalen,
+ "\033PCNTRL QTY 00000008%07d\r", hdr->copies);
+ ctx->datalen += sprintf((char*)ctx->databuf + ctx->datalen,
+ "\033PCNTRL CUTTER 0000000800000000");
+#else
+ /* QTY is stripped from the stream, and CUTTER is stashed away */
+ ctx->cutter = 0;
+#endif
+
+ j = 0;
+
+ /* Y plane */
+ ctx->datalen += sprintf((char*)ctx->databuf + ctx->datalen,
+ "\033PIMAGE YPLANE %08u", hdr->plane_len + 24);
+ memcpy(ctx->databuf + ctx->datalen, plane_hdr, sizeof(plane_hdr));
+ ctx->datalen += sizeof(plane_hdr);
+ memcpy(ctx->databuf + ctx->datalen, buf + j, hdr->plane_len);
+ ctx->datalen += hdr->plane_len;
+ j += hdr->plane_len;
+ memset(ctx->databuf + ctx->datalen, 0, 10);
+ ctx->datalen += 10;
+
+ /* M plane */
+ ctx->datalen += sprintf((char*)ctx->databuf + ctx->datalen,
+ "\033PIMAGE MPLANE %08u", hdr->plane_len + 24);
+ memcpy(ctx->databuf + ctx->datalen, plane_hdr, sizeof(plane_hdr));
+ ctx->datalen += sizeof(plane_hdr);
+ memcpy(ctx->databuf + ctx->datalen, buf + j, hdr->plane_len);
+ ctx->datalen += hdr->plane_len;
+ j += hdr->plane_len;
+ memset(ctx->databuf + ctx->datalen, 0, 10);
+ ctx->datalen += 10;
+
+ /* C plane */
+ ctx->datalen += sprintf((char*)ctx->databuf + ctx->datalen,
+ "\033PIMAGE CPLANE %08u", hdr->plane_len + 24);
+ memcpy(ctx->databuf + ctx->datalen, plane_hdr, sizeof(plane_hdr));
+ ctx->datalen += sizeof(plane_hdr);
+ memcpy(ctx->databuf + ctx->datalen, buf + j, hdr->plane_len);
+ ctx->datalen += hdr->plane_len;
+ j += hdr->plane_len;
+ memset(ctx->databuf + ctx->datalen, 0, 10);
+ ctx->datalen += 10;
+
+ /* Start */
+ ctx->datalen += sprintf((char*)ctx->databuf + ctx->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.
+
+*/