diff --git a/README b/README index 6a3b901..7981009 100644 --- a/README +++ b/README @@ -35,6 +35,7 @@ DNP DS-RX1 / DS-RX1HS / Citizen CY / CY-02 DNP DS620 / DS620A / Citizen CX-02 DNP DS820 / DS820A / Citizen CX-02W + DNP QW410 / Citizen CZ-01 Fujifilm ASK-300 HiTi P520L HiTi P525L @@ -71,8 +72,6 @@ Work-in-progress Printers: - Citizen CZ-01 - DNP QW410 DNP M4 / Q4 DNP M8 HiTi P110 series @@ -1120,8 +1119,10 @@ Model IDs recognized: dnp-ds40 dnp-ds80 dnp-ds80dx dnp-ds620 dnp-ds820 dnp-dsrx1 dnp-ds620 - citizen-cw-01 citizen-cw-02 citizen-cx-02 citizen-cx citizen-cx-w citizen-cy - citizen-cy-02 citizen-op900 citizen-op900ii citicen-cx-02w + dnp-qw410 + citizen-cw-01 citizen-cw-02 citizen-cx-02 citizen-cx citizen-cx-w + citizen-cy citizen-cy-02 citizen-op900 citizen-op900ii citicen-cx-02w + citizen-cz-01 Model IDs for backwards compatibility with older releases: @@ -1136,18 +1137,18 @@ DNP DS620 / DS620A (aka Citizen CX-02) DNP DS820 / DS820A (aka Citizen CX-02W) DNP DS-RX1 / DNP DS-RX1HS (aka Citizen CY and CY-02) + DNP DS-QW410 Theoretically supported printers: (Untested) Citizen CW-01 / OP900 Citizen CW-02 / OP900II Citizen CX-02 + Citizen CZ-01 Mitsubishi CP-3800DW Work-in-progress printers: (USB PIDs unknown) - Citizen CZ-01 - DNP QW410 DNP M4 / Q4 Olmec OP900 ICI Imagedata OP900 @@ -1167,7 +1168,7 @@ -p num Set 'P' counter to 'num' [2] -s Query printer status -k num Specify standby delay (1-99 minutes, 0 disables) [1] - -K num Keep Media Status Across Power Cycles (1 yes, 0 no) [1] + -K num Keep Media Status Across Power Cycles (1 yes, 0 no) [4] -x num Set USB iSerialNumber reporting (1 yes, 0 no) [3] -X Cancel current job -R Reset printer to defaults @@ -1176,7 +1177,8 @@ Notes: [1] Only supported by DS620 and DS820 [2] Certain printer features may require updating the printer firmware - [3] Only supported by RX1HS, DS620, and DS820 + [3] Only supported by RX1HS, DS620, DS820, and QW410 + [4] Only supported by DS620, DS820, and QW410 *************************************************************************** BACKEND=magicard diff --git a/backend_common.h b/backend_common.h index 9f1d87b..54d08b8 100644 --- a/backend_common.h +++ b/backend_common.h @@ -115,6 +115,7 @@ enum { P_DNP_DS620, P_DNP_DS820, P_DNP_DSRX1, + P_DNP_QW410, P_ES1, P_ES2_20, P_ES3_30, diff --git a/backend_dnpds40.c b/backend_dnpds40.c index 598a522..fffaf0a 100644 --- a/backend_dnpds40.c +++ b/backend_dnpds40.c @@ -42,6 +42,8 @@ #include "backend_common.h" +#include + /* Private data structure */ struct dnpds40_printjob { size_t jobsize; @@ -125,6 +127,7 @@ struct dnpds40_ctx { int supports_printspeed; int supports_lowspeed; int supports_highdensity; + int supports_systime; int supports_mediaclassrfid; int supports_gamma; }; @@ -179,6 +182,13 @@ struct dnpds40_cmd { #define MULTICUT_A4 41 #define MULTICUT_A4x5X2 43 +#define MULTICUT_4x4 47 +#define MULTICUT_4x6 48 +#define MULTICUT_4x8 49 +#define MULTICUT_4_5x4_5 50 +#define MULTICUT_4_5x6 51 +#define MULTICUT_4_5x8 52 + #define MULTICUT_S_SIMPLEX 100 #define MULTICUT_S_FRONT 200 #define MULTICUT_S_BACK 300 @@ -205,6 +215,7 @@ static int legacy_cw01_read_parse(struct dnpds40_printjob *job, int data_fd, int static int legacy_dnp_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data); static int legacy_dnp620_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data); static int legacy_dnp820_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data); +static int legacy_qw410_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data); static void dnpds40_cleanup_job(const void *vjob); static int dnpds40_query_markers(void *vctx, struct marker **markers, int *count); @@ -423,6 +434,7 @@ static const char *dnpds40_printer_type(int type, int mfg) case P_DNP_DSRX1: return mfg == 1 ? "CY" : "DSRX1"; case P_DNP_DS620: return mfg == 1 ? "CX-02" : "DS620"; case P_DNP_DS820: return type == 1 ? "CX-02W" : "DS820"; + case P_DNP_QW410: return mfg == 1 ? "CZ-01" : "QW410"; case P_CITIZEN_CW01: return "CW01"; case P_CITIZEN_OP900II: return "CW-02 / OP900ii"; default: break; @@ -435,6 +447,10 @@ static const char *dnpds40_media_types(int media) switch (media) { case 100: return "UNKNOWN100"; // seen in driver dumps case 110: return "UNKNOWN110"; // seen in driver dumps + case 150: return "4x6 (PC)"; + case 151: return "4x8"; + case 160: return "4.5x6"; + case 161: return "4.5x8"; case 200: return "5x3.5 (L)"; case 210: return "5x7 (2L)"; case 300: return "6x4 (PC)"; @@ -467,6 +483,7 @@ static const char *rfid_media_subtypes(int media) { switch (media) { case 1: return "SD"; + case 2: return "PD"; case 3: return "PP"; default: break; @@ -594,6 +611,7 @@ static const char *dnpds40_statuses(int status) case 1500: return "Paper Definition Error"; case 1600: return "Data Error"; case 2000: return "Head Voltage Error"; + case 2010: return "USB Power Supply Error"; case 2100: return "Head Position Error"; case 2200: return "Power Supply Fan Error"; case 2300: return "Cutter Error"; @@ -806,7 +824,7 @@ static int dnpds40_attach(void *vctx, struct libusb_device_handle *dev, int type ctx->type = type; ctx->iface = iface; - /* All current models support 600dpi */ + /* Nearly all models support 600dpi */ ctx->supports_600dpi = 1; if (test_mode < TEST_MODE_NOATTACH) { @@ -954,10 +972,31 @@ static int dnpds40_attach(void *vctx, struct libusb_device_handle *dev, int type ctx->supports_lowspeed = 1; ctx->supports_highdensity = 1; ctx->supports_ctrld_ext = 1; + ctx->supports_mediaoffset = 1; ctx->supports_mediaclassrfid = 1; if (FW_VER_CHECK(0,50)) ctx->supports_gamma = 1; break; + case P_DNP_QW410: + ctx->native_width = 1408; + ctx->max_height = 2436; + ctx->correct_count = 1; + ctx->supports_600dpi = 0; + ctx->supports_counterp = 1; + ctx->supports_matte = 1; + ctx->supports_fullcut = 1; + ctx->supports_mqty_default = 1; + ctx->supports_keepmode = 1; + ctx->supports_iserial = 1; + ctx->supports_adv_fullcut = 1; + ctx->supports_advmatte = 1; + ctx->supports_printspeed = 1; + ctx->supports_lowspeed = 1; + ctx->supports_ctrld_ext = 1; + ctx->supports_mediaoffset = 1; + ctx->supports_mediaclassrfid = 1; + ctx->supports_systime = 1; + break; default: ERROR("Unknown printer type %d\n", ctx->type); return CUPS_BACKEND_FAILED; @@ -994,10 +1033,11 @@ static int dnpds40_attach(void *vctx, struct libusb_device_handle *dev, int type ctx->media = atoi(tmp); - /* Subtract out the "mark" type */ - if (ctx->media & 1) - ctx->media--; - + if (ctx->type != P_DNP_QW410) { + /* Subtract out the "mark" type */ + if (ctx->media & 1) + ctx->media--; + } free(resp); } else { return CUPS_BACKEND_FAILED; @@ -1087,7 +1127,6 @@ static int dnpds40_attach(void *vctx, struct libusb_device_handle *dev, int type } } #endif - if (test_mode < TEST_MODE_NOATTACH && ctx->supports_mediaoffset) { /* Get Media Offset */ struct dnpds40_cmd cmd; @@ -1251,12 +1290,41 @@ static int dnpds40_attach(void *vctx, struct libusb_device_handle *dev, int type break; } break; + case P_DNP_QW410: + switch (ctx->media) { + case 150: // 4x6 + case 160: // 4.5x6 + ctx->media_count_new = 150; + break; + case 151: // 4x8 + case 161: // 4.5x8 + ctx->media_count_new = 110; + break; + default: + ctx->media_count_new = 0; + break; + } + break; default: ctx->media_count_new = 0; break; } } + if (test_mode < TEST_MODE_NOATTACH && ctx->supports_systime) { + /* Set Printer Time */ + struct dnpds40_cmd cmd; + char buf[16]; + struct tm *tm; + + time_t now = time(NULL); + tm = localtime(&now); + strftime(buf, sizeof(buf), "%Y%m%d%H%M%S\r", tm); /* YYYYMMDDHHMMSS\n\0 */ + dnpds40_build_cmd(&cmd, "CNTRL", "SET_SYS_TIME", 0); + if ((dnpds40_do_cmd(ctx, &cmd, (uint8_t*) buf, sizeof(buf))) != 0) + return CUPS_BACKEND_FAILED; + } + /* Fill out marker message */ if (ctx->supports_mediaclassrfid) { snprintf(ctx->media_text, sizeof(ctx->media_text), @@ -1389,6 +1457,9 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co if (job->databuf[job->datalen + 0] != 0x1b || job->databuf[job->datalen + 1] != 0x50) { switch(ctx->type) { + case P_DNP_QW410: + i = legacy_qw410_read_parse(job, data_fd, i); + break; case P_CITIZEN_CW01: i = legacy_cw01_read_parse(job, data_fd, i); break; @@ -1577,6 +1648,11 @@ parsed: job->matte = 1; } + /* QW410 only supports 0 and 3, supposedly. */ + if (ctx->type == P_DNP_QW410 && job->printspeed > 1) { + job->printspeed = 3; + } + /* Pick a sane default value for printspeed if not specified */ if (job->printspeed == -1 || job->printspeed > 3) { @@ -1698,6 +1774,40 @@ parsed: if (job->multicut < 100) { switch(ctx->media) { + case 150: // 4x6, QW410 + if (job->multicut != MULTICUT_4x4 && + job->multicut != MULTICUT_4x6) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + break; + case 151: // 4x8, QW410 + if (job->multicut != MULTICUT_4x4 && + job->multicut != MULTICUT_4x6 && + job->multicut != MULTICUT_4x8) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + break; + case 160: // 4.5x6, QW410 + if (job->multicut != MULTICUT_4_5x4_5 && + job->multicut != MULTICUT_4_5x6) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + break; + case 161: // 4.5x8, QW410 + if (job->multicut != MULTICUT_4_5x4_5 && + job->multicut != MULTICUT_4_5x6 && + job->multicut != MULTICUT_4_5x8) { + ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); + dnpds40_cleanup_job(job); + return CUPS_BACKEND_CANCEL; + } + break; case 200: //"5x3.5 (L)" if (job->multicut != MULTICUT_5x3_5) { ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut); @@ -2284,6 +2394,8 @@ static int dnpds40_get_sensors(struct dnpds40_ctx *ctx) INFO("Color Sensor Green : %s\n", val); } else if (!strcmp("CSB", tok)) { INFO("Color Sensor Blue : %s\n", val); + } else if (!strcmp("DC5", tok)) { + INFO("USB Supply Voltage : %s\n", val); } else { INFO("Unknown Sensor: '%s' '%s'\n", tok, val); @@ -2298,11 +2410,11 @@ static int dnpds40_get_sensors(struct dnpds40_ctx *ctx) static int dnpds40_get_info(struct dnpds40_ctx *ctx) { struct dnpds40_cmd cmd; + uint8_t *resp; + int len = 0; int cwd_extra = 0; int cwd_index = 1; uint8_t cwd_buf[5]; - uint8_t *resp; - int len = 0; INFO("Model: %s\n", dnpds40_printer_type(ctx->type, ctx->mfg)); @@ -2581,7 +2693,10 @@ CWD_TOP: } if (cwd_extra && cwd_index == 1) { - cwd_index = 3; + if (ctx->type == P_DNP_QW410) + cwd_index = 2; + else + cwd_index = 3; goto CWD_TOP; } @@ -3284,15 +3399,17 @@ static const char *dnpds40_prefixes[] = { #define USB_PID_CITIZEN_CW02 0x0006 // Also OP900II #define USB_PID_CITIZEN_CX02 0x000A #define USB_PID_CITIZEN_CX02W 0x000B +#define USB_PID_CITIZEN_CZ01 0x000C #define USB_VID_DNP 0x1452 #define USB_PID_DNP_DS620 0x8b01 #define USB_PID_DNP_DS820 0x9001 +#define USB_PID_DNP_QW410 0x9201 /* Exported */ struct dyesub_backend dnpds40_backend = { .name = "DNP DS-series / Citizen C-series", - .version = "0.132", + .version = "0.133", .uri_prefixes = dnpds40_prefixes, .cmdline_usage = dnpds40_cmdline, .cmdline_arg = dnpds40_cmdline_arg, @@ -3319,12 +3436,14 @@ struct dyesub_backend dnpds40_backend = { { USB_VID_CITIZEN, USB_PID_DNP_DSRX1, P_DNP_DSRX1, NULL, "citizen-cy-02"}, /* Duplicate */ { 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_DNP, USB_PID_DNP_QW410, P_DNP_QW410, NULL, "dnp-qw410"}, { USB_VID_CITIZEN, USB_PID_CITIZEN_CW01, P_CITIZEN_CW01, NULL, "citizen-cw-01"}, { USB_VID_CITIZEN, USB_PID_CITIZEN_CW01, P_CITIZEN_CW01, NULL, "citizen-op900"}, /* Duplicate */ { USB_VID_CITIZEN, USB_PID_CITIZEN_CW02, P_CITIZEN_OP900II, NULL, "citizen-cw-02"}, { USB_VID_CITIZEN, USB_PID_CITIZEN_CW02, P_CITIZEN_OP900II, NULL, "citizen-op900ii"}, /* Duplicate */ { USB_VID_CITIZEN, USB_PID_CITIZEN_CX02, P_DNP_DS620, NULL, "citizen-cx-02"}, { USB_VID_CITIZEN, USB_PID_CITIZEN_CX02W, P_DNP_DS820, NULL, "citizen-cx-02w"}, + { USB_VID_CITIZEN, USB_PID_CITIZEN_CZ01, P_DNP_QW410, NULL, "citizen-cz-01"}, { 0, 0, 0, NULL, NULL} } }; @@ -3628,6 +3747,50 @@ static int legacy_dnp820_read_parse(struct dnpds40_printjob *job, int data_fd, i sizeof(hdr), plane_len, 1); } +struct qw410_spool_hdr { + uint8_t type; /* MULTICUT_?? -1 */ + uint8_t null0[5]; + uint16_t copies; /* LE, 01 or bigger */ + + uint32_t plane_len; /* LE */ + uint8_t matte; + uint8_t null1[3]; + + uint8_t unk; /* always 01 */ + uint8_t null2[3]; + uint8_t cut2; + uint8_t null3[3]; + + uint8_t hd; + uint8_t null4[7]; +} __attribute__((packed)); + +static int legacy_qw410_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data) +{ + struct qw410_spool_hdr hdr; + uint32_t plane_len; + + /* get original header out of structure */ + memcpy(&hdr, job->databuf + job->datalen, sizeof(hdr)); + + /* Early parsing and sanity checking */ + plane_len = le32_to_cpu(hdr.plane_len); + + if (hdr.type < MULTICUT_4x4 || hdr.type > MULTICUT_4_5x8 || + hdr.null0[0] || hdr.null0[1] || hdr.null0[2]) { + ERROR("Unrecognized header data format @%d!\n", job->datalen); + return CUPS_BACKEND_CANCEL; + } + + /* Don't bother with FW version checks for legacy stuff */ + job->multicut = hdr.type + 1; + job->matte = hdr.matte; + job->cutter = (hdr.cut2) ? 120 : 0; + job->printspeed = (hdr.hd) ? 3 : 0; + + return legacy_spool_helper(job, data_fd, read_data, + sizeof(hdr), plane_len, 1); +} /* diff --git a/blacklist b/blacklist index ebd7d23..3d9cea5 100644 --- a/blacklist +++ b/blacklist @@ -247,6 +247,9 @@ # Citizen CX-02W 0x1343 0x000B blacklist +# Citizen CZ-01 +0x1343 0x000C blacklist + # DNP DS80D 0x1343 0x0008 blacklist @@ -256,6 +259,9 @@ # DNP DS820 0x1452 0x9001 blacklist +# DNP QW410 +0x1452 0x9201 blacklist + # CIAAT Brava 21 0x10ce 0x001e blacklist diff --git a/icm/DNP/DNP_QW410/PD_QW410-191113-7.icc b/icm/DNP/DNP_QW410/PD_QW410-191113-7.icc new file mode 100644 index 0000000..89b2dcb --- /dev/null +++ b/icm/DNP/DNP_QW410/PD_QW410-191113-7.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3225409b07be741f139abc43a1ad675f9dff2d191ce4db4eddb0b962dc7a1962 +size 3853604 diff --git a/icm/DNP/DNP_QW410/PD_QW410_0100.icc b/icm/DNP/DNP_QW410/PD_QW410_0100.icc new file mode 100644 index 0000000..53874d6 --- /dev/null +++ b/icm/DNP/DNP_QW410/PD_QW410_0100.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95e9491d25816e0ee2be66090867ce69d9ec9eece28e1c926338f2648e0c6edb +size 1254944 diff --git a/icm/DNP/DNP_QW410/SD_QW410_0100.icc b/icm/DNP/DNP_QW410/SD_QW410_0100.icc new file mode 100644 index 0000000..2943fd2 --- /dev/null +++ b/icm/DNP/DNP_QW410/SD_QW410_0100.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17d9c4a61192e045a2e9907783788f9debacbd2868e3f7bb103b6fdf3367c54d +size 1255072 diff --git a/regression-gp.csv b/regression-gp.csv index 3e44dcc..f73a7a0 100644 --- a/regression-gp.csv +++ b/regression-gp.csv @@ -673,6 +673,36 @@ dnp-ds80dx,0x1343,0x0008,510,PageSize=w576h864;Resolution=300x600dpi;StpLaminate dnp-ds80dx,0x1343,0x0008,510,PageSize=w576h864;Resolution=300x600dpi;MediaType=Sheet;Duplex=DuplexNoTumble dnp-ds80dx,0x1343,0x0008,510,PageSize=w576h864;Resolution=300x600dpi;MediaType=Sheet;Duplex=DuplexTumble # +dnp-qw410,0x1452,0x9201,150,PageSize=w288h216 +dnp-qw410,0x1452,0x9201,150,PageSize=w288h288 +dnp-qw410,0x1452,0x9201,150,PageSize=w288h288-div2 +dnp-qw410,0x1452,0x9201,150,PageSize=w288h288_w288h144 +dnp-qw410,0x1452,0x9201,150,PageSize=w288h432 +dnp-qw410,0x1452,0x9201,150,PageSize=w288h432-div2 +dnp-qw410,0x1452,0x9201,150,PageSize=w288h432-div3 +dnp-qw410,0x1452,0x9201,151,PageSize=w288h576 +dnp-qw410,0x1452,0x9201,151,PageSize=w288h576-div2 +dnp-qw410,0x1452,0x9201,151,PageSize=w288h576-div4 +dnp-qw410,0x1452,0x9201,151,PageSize=w288h432_w288h144 +dnp-qw410,0x1452,0x9201,160,PageSize=w324h216 +dnp-qw410,0x1452,0x9201,160,PageSize=w324h288 +dnp-qw410,0x1452,0x9201,160,PageSize=w324h324 +dnp-qw410,0x1452,0x9201,160,PageSize=w324h432 +dnp-qw410,0x1452,0x9201,160,PageSize=w324h432-div2 +dnp-qw410,0x1452,0x9201,160,PageSize=w324h432-div3 +dnp-qw410,0x1452,0x9201,161,PageSize=w324h486 +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576 +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576-div2 +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576-div4 +dnp-qw410,0x1452,0x9201,161,PageSize=w324h432_w324h144 +dnp-qw410,0x1452,0x9201,161,PageSize=w324h432-div2_w324h144 +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576;StpLaminate=Glossy +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576;StpLaminate=Matte +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576;StpPrintSpeed=Normal +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576;StpPrintSpeed=LowSpeed +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576;StpDeCurl=True +dnp-qw410,0x1452,0x9201,161,PageSize=w324h576;StpDeCurl=False +# hiti-p520l,0x0d16,0x0502,1,PageSize=w288h432 hiti-p520l,0x0d16,0x0502,1,PageSize=w288h432-div2 hiti-p520l,0x0d16,0x0502,2,PageSize=B7 diff --git a/regression.csv b/regression.csv index 4432cd1..a584886 100644 --- a/regression.csv +++ b/regression.csv @@ -189,6 +189,7 @@ dnprx1,0x1343,0x0005,dnp_dsrx1-4x6c.raw,400 citizencw02,0x1343,0x0006,dnp_ds40_4x6.raw,300 citizencw02,0x1343,0x0006,dnp_ds40_4x6.raw,310 citizencw02,0x1343,0x0006,dnp_ds40_6x8-600.raw,400 +dnp-qw410,0x1452,0x9201,dnp_qw410_4x6.raw,150 # # 'magicard # diff --git a/testjobs/dnp_qw410_4x6.raw b/testjobs/dnp_qw410_4x6.raw new file mode 100644 index 0000000..2d50eb6 --- /dev/null +++ b/testjobs/dnp_qw410_4x6.raw @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54967daf61c727462ee9ce0c1c6978387d994ded0d20a4c4891350a2e51da7d9 +size 7758488