diff options
Diffstat (limited to 'src/cups/citizencw01_print.c')
-rw-r--r-- | src/cups/citizencw01_print.c | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/src/cups/citizencw01_print.c b/src/cups/citizencw01_print.c new file mode 100644 index 0000000..b99822c --- /dev/null +++ b/src/cups/citizencw01_print.c @@ -0,0 +1,918 @@ +/* + * Citizen CW-01 Photo Printer CUPS backend -- libusb-1.0 version + * + * (c) 2014-2015 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 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * [http://www.gnu.org/licenses/gpl-2.0.html] + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <signal.h> + +#include "backend_common.h" + +#define USB_VID_CITIZEN 0x1343 +#define USB_PID_CITIZEN_CW01 0x0002 // Maybe others? +//#define USB_PID_OLMEC_OP900 XXXX + +/* Private data stucture */ +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; + + 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 "Unkown 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; + + UNUSED(jobid); + + ctx->dev = dev; + ctx->endp_up = endp_up; + ctx->endp_down = endp_down; +} + +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 */ + 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; + + /* This printer handles copies internally */ + copies = 1; + + /* Clean up */ + if (terminate) + copies = 1; + + INFO("Print complete (%d copies remaining)\n", copies - 1); + + if (copies && --copies) { + goto top; + } + + 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; + + /* Reset arg parsing */ + optind = 1; + opterr = 0; + while ((i = getopt(argc, argv, "inN:s")) >= 0) { + switch(i) { + case 'i': + if (ctx) { + j = cw01_get_info(ctx); + break; + } + return 1; + case 'n': + if (ctx) { + j = cw01_get_counters(ctx); + break; + } + return 1; + case 'N': + if (optarg[0] != 'A' && + optarg[0] != 'B') + return CUPS_BACKEND_FAILED; + if (ctx) { + j = cw01_clear_counter(ctx, optarg[0]); + break; + } + return 1; + case 's': + if (ctx) { + j = cw01_get_status(ctx); + break; + } + return 1; + default: + break; /* Ignore completely */ + } + + if (j) return j; + } + + return 0; +} + +/* Exported */ +struct dyesub_backend cw01_backend = { + .name = "Citizen CW-01", + .version = "0.10", + .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. + +*/ |