1 /*
2 * Citizen / DNP Photo Printer CUPS backend -- libusb-1.0 version
3 *
4 * (c) 2013-2019 Solomon Peachy <pizza@shaftnet.org>
5 *
6 * Development of this backend was sponsored by:
7 *
8 * Marco Di Antonio and [ ilgruppodigitale.com ]
9 * LiveLink Technology [ www.livelinktechnology.net ]
10 * A generous benefactor who wishes to remain anonymous
11 *
12 * The latest version of this program can be found at:
13 *
14 * http://git.shaftnet.org/cgit/selphy_print.git
15 *
16 * This program is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the Free
18 * Software Foundation; either version 2 of the License, or (at your option)
19 * any later version.
20 *
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <https://www.gnu.org/licenses/>.
28 *
29 * [http://www.gnu.org/licenses/gpl-2.0.html]
30 *
31 * SPDX-License-Identifier: GPL-2.0+
32 *
33 */
34
35 //#define DNP_ONLY
36 //#define CITIZEN_ONLY
37
38 /* Enables caching of last print type to speed up
39 job pipelining. Without this we always have to
40 assume the worst */
41 //#define STATE_DIR "/tmp"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <fcntl.h>
51 #include <signal.h>
52
53 #define BACKEND dnpds40_backend
54
55 #include "backend_common.h"
56
57 /* Private data structure */
58 struct dnpds40_printjob {
59 uint8_t *databuf;
60 int datalen;
61
62 int copies;
63 uint32_t dpi;
64 int matte;
65 int cutter;
66 uint32_t multicut;
67 int fullcut;
68 int printspeed;
69 int can_rewind;
70 int buf_needed;
71 int cut_paper;
72 };
73
74 struct dnpds40_ctx {
75 struct libusb_device_handle *dev;
76 uint8_t endp_up;
77 uint8_t endp_down;
78
79 int type;
80
81 /* Version and whatnot */
82 char *serno;
83 char *version;
84 int ver_major;
85 int ver_minor;
86
87 /* State */
88 uint32_t media;
89 uint32_t duplex_media;
90 int duplex_media_status;
91 uint16_t media_count_new;
92
93 uint32_t last_multicut;
94 int last_matte;
95
96 int mediaoffset;
97 int correct_count;
98 int needs_mlot;
99
100 struct marker marker[2];
101 int marker_count;
102
103 /* Printer capabilities */
104 uint32_t native_width;
105 uint32_t max_height;
106 int supports_6x9;
107 int supports_2x6;
108 int supports_3x5x2;
109 int supports_matte;
110 int supports_finematte;
111 int supports_luster;
112 int supports_advmatte;
113 int supports_fullcut;
114 int supports_rewind;
115 int supports_standby;
116 int supports_6x4_5;
117 int supports_mqty_default;
118 int supports_iserial;
119 int supports_6x6;
120 int supports_5x5;
121 int supports_counterp;
122 int supports_adv_fullcut;
123 int supports_mediaoffset;
124 int supports_media_ext;
125 int supports_printspeed;
126 int supports_lowspeed;
127 int supports_highdensity;
128 int supports_gamma;
129 };
130
131 struct dnpds40_cmd {
132 uint8_t esc; /* Fixed at ascii ESC, aka 0x1B */
133 uint8_t p; /* Fixed at ascii 'P' aka 0x50 */
134 uint8_t arg1[6];
135 uint8_t arg2[16];
136 uint8_t arg3[8]; /* Decimal value of arg4's length, or empty */
137 uint8_t arg4[0]; /* Extra payload if arg3 is non-empty
138 Doesn't have to be sent in the same URB */
139
140 /* All unused elements are set to 0x20 (ie ascii space) */
141 };
142
143 #define MULTICUT_5x3_5 1
144 #define MULTICUT_6x4 2
145 #define MULTICUT_5x7 3
146 #define MULTICUT_6x8 4
147 #define MULTICUT_6x9 5
148 #define MULTICUT_8x10 6
149 #define MULTICUT_8x12 7
150 #define MULTICUT_8x4 8
151 #define MULTICUT_8x5 9
152 #define MULTICUT_8x6 10
153 #define MULTICUT_8x8 11
154 #define MULTICUT_6x4X2 12
155 #define MULTICUT_8x4X2 13
156 #define MULTICUT_8x5X2 14
157 #define MULTICUT_8x6X2 15
158 #define MULTICUT_8x5_8x4 16
159 #define MULTICUT_8x6_8x4 17
160 #define MULTICUT_8x6_8x5 18
161 #define MULTICUT_8x8_8x4 19
162 #define MULTICUT_8x4X3 20
163 #define MULTICUT_8xA4LEN 21
164 #define MULTICUT_5x3_5X2 22
165 #define MULTICUT_6x6 27
166 #define MULTICUT_5x5 29
167 #define MULTICUT_6x4_5 30
168 #define MULTICUT_6x4_5X2 31
169 #define MULTICUT_8x7 32
170 #define MULTICUT_8x9 33
171 #define MULTICUT_A5 34
172 #define MULTICUT_A5X2 35
173 #define MULTICUT_A4x4 36
174 #define MULTICUT_A4x5 37
175 #define MULTICUT_A4x6 38
176 #define MULTICUT_A4x8 39
177 #define MULTICUT_A4x10 40
178 #define MULTICUT_A4 41
179 #define MULTICUT_A4x5X2 43
180
181 #define MULTICUT_S_SIMPLEX 100
182 #define MULTICUT_S_FRONT 200
183 #define MULTICUT_S_BACK 300
184
185 #define MULTICUT_S_8x10 6
186 #define MULTICUT_S_8x12 7
187 #define MULTICUT_S_8x4 8
188 #define MULTICUT_S_8x5 9
189 #define MULTICUT_S_8x6 10
190 #define MULTICUT_S_8x8 11
191 #define MULTICUT_S_8x4X2 13
192 #define MULTICUT_S_8x5X2 14
193 #define MULTICUT_S_8x6X2 15
194 #define MULTICUT_S_8x10_5 25
195 #define MULTICUT_S_8x10_75 26
196 #define MULTICUT_S_8x4X3 28 // different than roll type.
197
198 #define min(__x, __y) ((__x) < (__y)) ? __x : __y
199
200 /* Legacy spool file support */
201 static int legacy_cw01_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
202 static int legacy_dnp_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
203 static int legacy_dnp620_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
204 static int legacy_dnp820_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
205
206 static void dnpds40_cleanup_job(const void *vjob);
207 static int dnpds40_query_markers(void *vctx, struct marker **markers, int *count);
208
209 #define JOB_EQUIV(__x) if (job1->__x != job2->__x) goto done
210
combine_jobs(const struct dnpds40_printjob * job1,const struct dnpds40_printjob * job2)211 static struct dnpds40_printjob *combine_jobs(const struct dnpds40_printjob *job1,
212 const struct dnpds40_printjob *job2)
213 {
214 struct dnpds40_printjob *newjob = NULL;
215 uint32_t new_multicut;
216 uint16_t new_w, new_h;
217 uint16_t gap_bytes;
218
219 /* Sanity check */
220 if (!job1 || !job2)
221 goto done;
222
223 /* Make sure pertinent paremeters are the same */
224 JOB_EQUIV(dpi);
225 JOB_EQUIV(matte);
226 JOB_EQUIV(cutter);
227 JOB_EQUIV(fullcut);
228 JOB_EQUIV(multicut); // TODO: Support fancier modes for 8" models (eg 8x4+8x6, etc)
229 JOB_EQUIV(datalen); // <-- cheating a little?
230 // JOV_EQUIV(printspeed); <-- does it matter?
231
232 /* Any cutter means we shouldn't bother */
233 if (job1->fullcut || job1->cutter)
234 goto done;
235
236 #if 0
237 // XXX TODO: 2x6*2 + 2x6*2 --> 8x6+cutter!
238 // problem is that 8x6" size is 4 rows smaller than 2* 4x6" prints, posing a problem.
239
240 /* Only handle cutter if it's for 2x6" strips */
241 if (job1->cutter != 0 && job1->cutter != 120)
242 goto done;
243 #endif
244
245 /* Make sure we can combine these two prints */
246 switch (job1->multicut) {
247 case MULTICUT_5x3_5:
248 new_multicut = MULTICUT_5x3_5X2;
249 new_w = 1920;
250 new_h = 2176;
251 gap_bytes = 0;
252 break;
253 case MULTICUT_6x4:
254 #if 0
255 if (job1->cutter != 120) {
256 new_multicut = MULTICUT_6x8;
257 new_h = 2436;
258 gap_bytes = -4;
259 } else {
260 #endif
261 new_multicut = MULTICUT_6x4X2;
262 new_h = 2498;
263 gap_bytes = 18;
264 #if 0
265 }
266 #endif
267 new_w = 1920;
268 break;
269 case MULTICUT_6x4_5:
270 new_multicut = MULTICUT_6x4_5X2;
271 new_w = 1920;
272 new_h = 2802;
273 gap_bytes = 30;
274 break;
275 case MULTICUT_8x4:
276 new_multicut = MULTICUT_8x4X2;
277 new_w = 2560;
278 new_h = 2502;
279 gap_bytes = 30;
280 break;
281 case MULTICUT_8x5:
282 new_multicut = MULTICUT_8x5X2;
283 new_w = 2560;
284 new_h = 3102;
285 gap_bytes = 30;
286 break;
287 case MULTICUT_8x6:
288 new_multicut = MULTICUT_8x6X2;
289 new_w = 2560;
290 new_h = 3702;
291 gap_bytes = 30;
292 break;
293 default:
294 // 2-up 8x6 prints too?
295 /* Everything else is NOT handled */
296 goto done;
297 }
298 gap_bytes *= new_w;
299 if (job1->dpi == 600) {
300 gap_bytes *= 2;
301 new_h *= 2;
302 }
303
304 DEBUG("Combining jobs to save media\n");
305
306 /* Okay, it's kosher to proceed */
307
308 newjob = malloc(sizeof(*newjob));
309 if (!newjob) {
310 ERROR("Memory allocation failure!\n");
311 goto done;
312 }
313 memcpy(newjob, job1, sizeof(*newjob));
314
315 newjob->databuf = malloc(((new_w*new_h+1024+54+10))*3+1024);
316 newjob->datalen = 0;
317 newjob->multicut = new_multicut;
318 if (!newjob->databuf) {
319 dnpds40_cleanup_job(newjob);
320 newjob = NULL;
321 ERROR("Memory allocation failure!\n");
322 goto done;
323 }
324
325 /* Copy data blocks from job1 */
326 uint8_t *ptr, *ptr2;
327 char buf[9];
328 ptr = job1->databuf;
329 while(ptr && ptr < (job1->databuf + job1->datalen)) {
330 int i;
331 buf[8] = 0;
332 memcpy(buf, ptr + 24, 8);
333 i = atoi(buf) + 32;
334 memcpy(newjob->databuf + newjob->datalen, ptr, i);
335
336 /* If we're on a plane data block... */
337 if (!memcmp("PLANE", newjob->databuf + newjob->datalen + 9, 5)) {
338 long planelen = (new_w * new_h) + 1088;
339 uint32_t newlen;
340
341 /* Fix up length in command */
342 snprintf(buf, sizeof(buf), "%08ld", planelen);
343 memcpy(newjob->databuf + newjob->datalen + 24, buf, 8);
344
345 /* Alter BMP header */
346 newlen = cpu_to_le32(planelen);
347 memcpy(newjob->databuf + newjob->datalen + 32 + 2, &newlen, 4);
348
349 /* alter DIB header */
350 newlen = cpu_to_le32(new_h);
351 memcpy(newjob->databuf + newjob->datalen + 32 + 22, &newlen, 4);
352
353 /* Insert gap/padding after first image */
354 memset(newjob->databuf + newjob->datalen + i, 0, gap_bytes);
355 newjob->datalen += gap_bytes;
356
357 // locate job2's PLANE properly? Assumption is it's in the same place.
358 ptr2 = job2->databuf + (ptr - job1->databuf);
359 /* Copy over job2's image data */
360 memcpy(newjob->databuf + newjob->datalen + i,
361 ptr2 + 32 + 1088, i - 32 - 1088);
362 newjob->datalen += i - 32 - 1088; /* add in job2 length */
363 }
364
365 newjob->datalen += i;
366 ptr += i;
367 }
368
369 done:
370 return newjob;
371 }
372
373 #undef JOB_EQUIV
374
dnpds40_build_cmd(struct dnpds40_cmd * cmd,char * arg1,char * arg2,uint32_t arg3_len)375 static void dnpds40_build_cmd(struct dnpds40_cmd *cmd, char *arg1, char *arg2, uint32_t arg3_len)
376 {
377 memset(cmd, 0x20, sizeof(*cmd));
378 cmd->esc = 0x1b;
379 cmd->p = 0x50;
380 memcpy(cmd->arg1, arg1, min(strlen(arg1), sizeof(cmd->arg1)));
381 memcpy(cmd->arg2, arg2, min(strlen(arg2), sizeof(cmd->arg2)));
382 if (arg3_len) {
383 char buf[9];
384 snprintf(buf, sizeof(buf), "%08u", arg3_len);
385 memcpy(cmd->arg3, buf, 8);
386 }
387
388 }
389
dnpds40_cleanup_string(char * start,int len)390 static void dnpds40_cleanup_string(char *start, int len)
391 {
392 char *ptr = strchr(start, 0x0d);
393
394 if (ptr && (ptr - start < len)) {
395 *ptr = 0x00; /* If there is a <CR>, terminate there */
396 len = ptr - start;
397 } else {
398 start[--len] = 0x00; /* force null-termination */
399 }
400
401 /* Trim trailing spaces */
402 while (len && start[len-1] == ' ') {
403 start[--len] = 0;
404 }
405 }
406
dnpds40_printer_type(int type)407 static const char *dnpds40_printer_type(int type)
408 {
409 switch(type) {
410 case P_DNP_DS40: return "DS40";
411 case P_DNP_DS80: return "DS80";
412 case P_DNP_DS80D: return "DS80DX";
413 case P_DNP_DSRX1: return "DSRX1";
414 case P_DNP_DS620: return "DS620";
415 case P_DNP_DS820: return "DS820";
416 case P_CITIZEN_CW01: return "CW01";
417 case P_CITIZEN_OP900II: return "OP900ii";
418 default: break;
419 }
420 return "Unknown";
421 }
422
dnpds40_media_types(int media)423 static const char *dnpds40_media_types(int media)
424 {
425 switch (media) {
426 case 100: return "UNKNOWN100"; // seen in driver dumps
427 case 110: return "UNKNOWN110"; // seen in driver dumps
428 case 200: return "5x3.5 (L)";
429 case 210: return "5x7 (2L)";
430 case 300: return "6x4 (PC)";
431 case 310: return "6x8 (A5)";
432 case 400: return "6x9 (A5W)";
433 case 500: return "8x10";
434 case 510: return "8x12";
435 case 600: return "A4";
436 default:
437 break;
438 }
439
440 return "Unknown type";
441 }
442
dnpds620_media_extension_code(int media)443 static const char *dnpds620_media_extension_code(int media)
444 {
445 switch (media) {
446 case 00: return "Normal Paper";
447 case 01: return "Sticky Paper";
448 case 99: return "Unknown Paper";
449 default:
450 break;
451 }
452
453 return "Unknown type";
454 }
455
dnpds820_media_subtypes(int media)456 static const char *dnpds820_media_subtypes(int media)
457 {
458 switch (media) {
459 case 0001: return "SD";
460 case 0003: return "PP";
461 default:
462 break;
463 }
464
465 return "Unknown type";
466 }
467
dnpds80_duplex_media_types(int media)468 static const char *dnpds80_duplex_media_types(int media)
469 {
470 switch (media) {
471 case 100: return "8x10.75";
472 case 200: return "8x12";
473 default:
474 break;
475 }
476
477 return "Unknown type";
478 }
479
480 #define DUPLEX_UNIT_PAPER_NONE 0
481 #define DUPLEX_UNIT_PAPER_PROTECTIVE 1
482 #define DUPLEX_UNIT_PAPER_PRESENT 2
483
dnpds80_duplex_paper_status(int media)484 static const char *dnpds80_duplex_paper_status(int media)
485 {
486 switch (media) {
487 case DUPLEX_UNIT_PAPER_NONE: return "No Paper";
488 case DUPLEX_UNIT_PAPER_PROTECTIVE: return "Protective Sheet";
489 case DUPLEX_UNIT_PAPER_PRESENT: return "Cut Paper Present";
490 default:
491 return "Unknown";
492 }
493 }
494
dnpds80_duplex_statuses(int status)495 static const char *dnpds80_duplex_statuses(int status)
496 {
497 switch (status) {
498 case 5000: return "No Error";
499
500 case 5500: return "Duplex Unit Not Connected";
501
502 case 5017: return "Paper Jam: Supply Sensor On";
503 case 5018: return "Paper Jam: Supply Sensor Off";
504 case 5019: return "Paper Jam: Slot Sensor On";
505 case 5020: return "Paper Jam: Slot Sensor Off";
506 case 5021: return "Paper Jam: Pass Sensor On";
507 case 5022: return "Paper Jam: Pass Sensor Off";
508 case 5023: return "Paper Jam: Shell Sensor 1 On";
509 case 5024: return "Paper Jam: Shell Sensor 1 Off";
510 case 5025: return "Paper Jam: Shell Sensor 2 On";
511 case 5026: return "Paper Jam: Shell Sensor 2 Off";
512 case 5027: return "Paper Jam: Eject Sensor On";
513 case 5028: return "Paper Jam: Eject Sensor Off";
514 case 5029: return "Paper Jam: Slot FG Sensor";
515 case 5030: return "Paper Jam: Shell FG Sensor";
516
517 case 5033: return "Paper Supply Sensor Off";
518 case 5034: return "Printer Feed Slot Sensor Off";
519 case 5035: return "Pinch Pass Sensor Off";
520 case 5036: return "Shell Pass Sensor 1 Off";
521 case 5037: return "Shell Pass Sensor 2 Off";
522 case 5038: return "Eject Sensor Off";
523
524 case 5049: return "Capstan Drive Control Error";
525 case 5065: return "Shell Roller Error";
526
527 case 5081: return "Pinch Open Error";
528 case 5082: return "Pinch Close Error";
529 case 5083: return "Pinch Init Error";
530 case 5084: return "Pinch Position Error";
531
532 case 5097: return "Pass Guide Supply Error";
533 case 5098: return "Pass Guide Shell Error";
534 case 5099: return "Pass Guide Eject Error";
535 case 5100: return "Pass Guide Init Error";
536 case 5101: return "Pass Guide Position Error";
537
538 case 5113: return "Side Guide Home Error";
539 case 5114: return "Side Guide Position Error";
540 case 5115: return "Side Guide Init Error";
541
542 case 5129: return "Act Guide Home Error";
543
544 case 5145: return "Shell Rotate Home Error";
545 case 5146: return "Shell Rotate Rev Error";
546
547 case 5161: return "Paper Feed Lever Down Error";
548 case 5162: return "Paper Feed Lever Lock Error";
549 case 5163: return "Paper Feed Lever Up Error";
550
551 case 5177: return "Cutter Home Error";
552 case 5178: return "Cutter Away Error";
553 case 5179: return "Cutter Init Error";
554 case 5180: return "Cutter Position Error";
555
556 case 5193: return "Paper Tray Removed";
557 case 5209: return "Cover Opened";
558 case 5241: return "System Error";
559
560 default:
561 break;
562 }
563
564 return "Unknown Duplexer Error";
565 }
566
dnpds40_statuses(int status)567 static const char *dnpds40_statuses(int status)
568 {
569 if (status >= 5000 && status <= 5999)
570 return dnpds80_duplex_statuses(status);
571
572 switch (status) {
573 case 0: return "Idle";
574 case 1: return "Printing";
575 case 500: return "Cooling Print Head";
576 case 510: return "Cooling Paper Motor";
577 case 900: return "Standby Mode";
578 case 1000: return "Cover Open";
579 case 1010: return "No Scrap Box";
580 case 1100: return "Paper End";
581 case 1200: return "Ribbon End";
582 case 1300: return "Paper Jam";
583 case 1400: return "Ribbon Error";
584 case 1500: return "Paper Definition Error";
585 case 1600: return "Data Error";
586 case 2000: return "Head Voltage Error";
587 case 2100: return "Head Position Error";
588 case 2200: return "Power Supply Fan Error";
589 case 2300: return "Cutter Error";
590 case 2400: return "Pinch Roller Error";
591 case 2500: return "Abnormal Head Temperature";
592 case 2600: return "Abnormal Media Temperature";
593 case 2610: return "Abnormal Paper Motor Temperature";
594 case 2700: return "Ribbon Tension Error";
595 case 2800: return "RF-ID Module Error";
596 case 3000: return "System Error";
597 default:
598 break;
599 }
600
601 return "Unknown Error";
602 }
603
dnpds40_do_cmd(struct dnpds40_ctx * ctx,struct dnpds40_cmd * cmd,uint8_t * data,int len)604 static int dnpds40_do_cmd(struct dnpds40_ctx *ctx,
605 struct dnpds40_cmd *cmd,
606 uint8_t *data, int len)
607 {
608 int ret;
609
610 if ((ret = send_data(ctx->dev, ctx->endp_down,
611 (uint8_t*)cmd, sizeof(*cmd))))
612 return ret;
613
614 if (data && len)
615 if ((ret = send_data(ctx->dev, ctx->endp_down,
616 data, len)))
617 return ret;
618
619 return CUPS_BACKEND_OK;
620 }
621
dnpds40_resp_cmd2(struct dnpds40_ctx * ctx,struct dnpds40_cmd * cmd,int * len,uint8_t * buf,uint32_t buf_len)622 static uint8_t *dnpds40_resp_cmd2(struct dnpds40_ctx *ctx,
623 struct dnpds40_cmd *cmd,
624 int *len,
625 uint8_t *buf, uint32_t buf_len)
626 {
627 char tmp[9];
628 uint8_t *respbuf;
629
630 int ret, i, num = 0;
631
632 memset(tmp, 0, sizeof(tmp));
633
634 if ((ret = dnpds40_do_cmd(ctx, cmd, buf, buf_len)))
635 return NULL;
636
637 /* Read in the response header */
638 ret = read_data(ctx->dev, ctx->endp_up,
639 (uint8_t*)tmp, 8, &num);
640 if (ret < 0)
641 return NULL;
642
643 if (num != 8) {
644 ERROR("Short read! (%d/%d)\n", num, 8);
645 return NULL;
646 }
647
648 i = atoi(tmp); /* Length of payload in bytes, possibly padded */
649 respbuf = malloc(i);
650 if (!respbuf) {
651 ERROR("Memory allocation failure (%d bytes)!\n", i);
652 return NULL;
653 }
654
655 /* Read in the actual response */
656 ret = read_data(ctx->dev, ctx->endp_up,
657 respbuf, i, &num);
658 if (ret < 0) {
659 free(respbuf);
660 return NULL;
661 }
662
663 if (num != i) {
664 ERROR("Short read! (%d/%d)\n", num, i);
665 free(respbuf);
666 return NULL;
667 }
668
669 *len = num;
670 return respbuf;
671 }
672
673 #define dnpds40_resp_cmd(__ctx, __cmd, __len) dnpds40_resp_cmd2(__ctx, __cmd, __len, NULL, 0)
674
dnpds40_query_serno(struct libusb_device_handle * dev,uint8_t endp_up,uint8_t endp_down,char * buf,int buf_len)675 static int dnpds40_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len)
676 {
677 struct dnpds40_cmd cmd;
678 uint8_t *resp;
679 int len = 0;
680
681 struct dnpds40_ctx ctx = {
682 .dev = dev,
683 .endp_up = endp_up,
684 .endp_down = endp_down,
685 };
686
687 /* Get Serial Number */
688 dnpds40_build_cmd(&cmd, "INFO", "SERIAL_NUMBER", 0);
689
690 resp = dnpds40_resp_cmd(&ctx, &cmd, &len);
691 if (!resp)
692 return CUPS_BACKEND_FAILED;
693
694 dnpds40_cleanup_string((char*)resp, len);
695
696 strncpy(buf, (char*)resp, buf_len);
697 buf[buf_len-1] = 0;
698
699 free(resp);
700
701 return CUPS_BACKEND_OK;
702 }
703
dnpds40_init(void)704 static void *dnpds40_init(void)
705 {
706 struct dnpds40_ctx *ctx = malloc(sizeof(struct dnpds40_ctx));
707 if (!ctx) {
708 ERROR("Memory allocation failure (%d bytes)!\n", (int)sizeof(struct dnpds40_ctx));
709 return NULL;
710 }
711 memset(ctx, 0, sizeof(struct dnpds40_ctx));
712
713 return ctx;
714 }
715
716 #define FW_VER_CHECK(__major, __minor) \
717 ((ctx->ver_major > (__major)) || \
718 (ctx->ver_major == (__major) && ctx->ver_minor >= (__minor)))
719
dnpds40_query_mqty(struct dnpds40_ctx * ctx)720 static int dnpds40_query_mqty(struct dnpds40_ctx *ctx)
721 {
722 struct dnpds40_cmd cmd;
723 uint8_t *resp;
724 int len = 0, count;
725
726 /* Get Media remaining */
727 dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0);
728
729 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
730 if (!resp)
731 return -1;
732
733 dnpds40_cleanup_string((char*)resp, len);
734
735 count = atoi((char*)resp+4);
736 free(resp);
737
738 if (count) {
739 /* Old-sk00l models report one less than they should */
740 if (!ctx->correct_count)
741 count++;
742
743 count -= ctx->mediaoffset;
744 }
745
746 return count;
747 }
748
dnpds80dx_query_paper(struct dnpds40_ctx * ctx)749 static int dnpds80dx_query_paper(struct dnpds40_ctx *ctx)
750 {
751 struct dnpds40_cmd cmd;
752 uint8_t *resp;
753 int len = 0;
754
755 /* Query Duplex Media Info */
756 dnpds40_build_cmd(&cmd, "INFO", "UNIT_CUT_PAPER", 0);
757
758 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
759 if (resp) {
760 char tmp[5];
761 char status;
762
763 dnpds40_cleanup_string((char*)resp, len);
764
765 memcpy(tmp, resp + 4, 4);
766 status = tmp[3];
767 tmp[3] = '0';
768 tmp[4] = 0;
769
770 ctx->duplex_media = atoi(tmp);
771
772 tmp[0] = tmp[1] = tmp[2] = '0';
773 tmp[3] = status;
774 ctx->duplex_media_status = atoi(tmp);
775
776 free(resp);
777 } else {
778 return CUPS_BACKEND_FAILED;
779 }
780
781 return CUPS_BACKEND_OK;
782 }
783
dnpds40_attach(void * vctx,struct libusb_device_handle * dev,int type,uint8_t endp_up,uint8_t endp_down,uint8_t jobid)784 static int dnpds40_attach(void *vctx, struct libusb_device_handle *dev, int type,
785 uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
786 {
787 struct dnpds40_ctx *ctx = vctx;
788
789 UNUSED(jobid);
790
791 ctx->dev = dev;
792 ctx->endp_up = endp_up;
793 ctx->endp_down = endp_down;
794 ctx->type = type;
795
796 if (test_mode < TEST_MODE_NOATTACH) {
797 struct dnpds40_cmd cmd;
798 uint8_t *resp;
799 int len = 0;
800
801 /* Get Firmware Version */
802 dnpds40_build_cmd(&cmd, "INFO", "FVER", 0);
803
804 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
805 if (resp) {
806 char *ptr;
807 dnpds40_cleanup_string((char*)resp, len);
808 ctx->version = strdup((char*) resp);
809
810 /* Parse version */
811 /* ptr = */ strtok((char*)resp, " .");
812 ptr = strtok(NULL, ".");
813 ctx->ver_major = atoi(ptr);
814 ptr = strtok(NULL, ".");
815 ctx->ver_minor = atoi(ptr);
816 free(resp);
817 } else {
818 return CUPS_BACKEND_FAILED;
819 }
820
821 /* Get Serial Number */
822 dnpds40_build_cmd(&cmd, "INFO", "SERIAL_NUMBER", 0);
823
824 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
825 if (resp) {
826 dnpds40_cleanup_string((char*)resp, len);
827 ctx->serno = (char*) resp;
828 /* Do NOT free resp! */
829 } else {
830 return CUPS_BACKEND_FAILED;
831 }
832
833 /* Query Media Info */
834 dnpds40_build_cmd(&cmd, "INFO", "MEDIA", 0);
835
836 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
837 if (resp) {
838 char tmp[4];
839
840 dnpds40_cleanup_string((char*)resp, len);
841
842 memcpy(tmp, resp + 4, 3);
843 tmp[3] = 0;
844
845 ctx->media = atoi(tmp);
846
847 /* Subtract out the "mark" type */
848 if (ctx->media & 1)
849 ctx->media--;
850
851 free(resp);
852 } else {
853 return CUPS_BACKEND_FAILED;
854 }
855
856 if (ctx->type == P_DNP_DS80D) {
857 if (dnpds80dx_query_paper(ctx))
858 return CUPS_BACKEND_FAILED;
859 }
860
861 #if (defined(DNP_ONLY) || defined(CITIZEN_ONLY))
862 {
863 char buf[256];
864 buf[0] = 0;
865 libusb_get_string_descriptor_ascii(dev, desc->iManufacturer, (unsigned char*)buf, STR_LEN_MAX);
866 sanitize_string(buf);
867 #ifdef DNP_ONLY /* Only allow DNP printers to work. */
868 if (strncmp(buf, "Dai", 3)) /* "Dai Nippon Printing" */
869 return CUPS_BACKEND_FAILED;
870 #endif
871 #ifdef CITIZEN_ONLY /* Only allow CITIZEN printers to work. */
872 if (strncmp(buf, "CIT", 3)) /* "CITIZEN SYSTEMS" */
873 return CUPS_BACKEND_FAILED;
874 #endif
875 }
876 #endif
877 } else {
878 ctx->ver_major = 3;
879 ctx->ver_minor = 0;
880 ctx->version = strdup("UNKNOWN");
881 switch(ctx->type) {
882 case P_DNP_DS80D:
883 ctx->duplex_media = 200;
884 /* Intentional fallthrough */
885 case P_DNP_DS80:
886 case P_DNP_DS820:
887 ctx->media = 510; /* 8x12 */
888 break;
889 case P_DNP_DSRX1:
890 ctx->media = 310; /* 6x8 */
891 break;
892 default:
893 ctx->media = 400; /* 6x9 */
894 break;
895 }
896
897 if (getenv("MEDIA_CODE"))
898 ctx->media = atoi(getenv("MEDIA_CODE"));
899 }
900
901 /* Per-printer options */
902 switch (ctx->type) {
903 case P_DNP_DS40:
904 ctx->native_width = 1920;
905 ctx->max_height = 5480;
906 ctx->supports_6x9 = 1;
907 if (FW_VER_CHECK(1,04))
908 ctx->supports_counterp = 1;
909 if (FW_VER_CHECK(1,30))
910 ctx->supports_matte = 1;
911 if (FW_VER_CHECK(1,40))
912 ctx->supports_2x6 = 1;
913 if (FW_VER_CHECK(1,50))
914 ctx->supports_3x5x2 = 1;
915 if (FW_VER_CHECK(1,60))
916 ctx->supports_fullcut = ctx->supports_6x6 = 1; // No 5x5!
917 break;
918 case P_DNP_DS80:
919 case P_DNP_DS80D:
920 ctx->native_width = 2560;
921 ctx->max_height = 7536;
922 if (FW_VER_CHECK(1,02))
923 ctx->supports_counterp = 1;
924 if (FW_VER_CHECK(1,30))
925 ctx->supports_matte = 1;
926 break;
927 case P_DNP_DSRX1:
928 ctx->native_width = 1920;
929 ctx->max_height = 5480;
930 ctx->supports_counterp = 1;
931 ctx->supports_matte = 1;
932 if (FW_VER_CHECK(1,10))
933 ctx->supports_2x6 = ctx->supports_mqty_default = 1;
934 if (FW_VER_CHECK(1,20))
935 ctx->supports_3x5x2 = 1;
936 if (FW_VER_CHECK(2,00)) { /* AKA RX1HS */
937 ctx->needs_mlot = 1;
938 ctx->supports_mediaoffset = 1;
939 ctx->supports_iserial = 1;
940 }
941 if (FW_VER_CHECK(2,06)) {
942 ctx->supports_5x5 = ctx->supports_6x6 = 1;
943 }
944 break;
945 case P_CITIZEN_OP900II:
946 ctx->native_width = 1920;
947 ctx->max_height = 5480;
948 ctx->supports_counterp = 1;
949 ctx->supports_matte = 1;
950 ctx->supports_mqty_default = 1;
951 ctx->supports_6x9 = 1;
952 if (FW_VER_CHECK(1,11))
953 ctx->supports_2x6 = 1;
954 break;
955 case P_CITIZEN_CW01:
956 ctx->native_width = 2048;
957 ctx->max_height = 5480;
958 ctx->supports_6x9 = 1;
959 break;
960 case P_DNP_DS620:
961 ctx->native_width = 1920;
962 ctx->max_height = 5604;
963 ctx->correct_count = 1;
964 ctx->supports_counterp = 1;
965 ctx->supports_matte = 1;
966 ctx->supports_2x6 = 1;
967 ctx->supports_fullcut = 1;
968 ctx->supports_mqty_default = 1;
969 if (strchr(ctx->version, 'A'))
970 ctx->supports_rewind = 0;
971 else
972 ctx->supports_rewind = 1;
973 ctx->supports_standby = 1;
974 ctx->supports_iserial = 1;
975 ctx->supports_6x6 = 1;
976 ctx->supports_5x5 = 1;
977 ctx->supports_lowspeed = 1;
978 if (FW_VER_CHECK(0,30))
979 ctx->supports_3x5x2 = 1;
980 if (FW_VER_CHECK(1,10))
981 ctx->supports_6x9 = ctx->supports_6x4_5 = 1;
982 if (FW_VER_CHECK(1,20))
983 ctx->supports_adv_fullcut = ctx->supports_advmatte = 1;
984 if (FW_VER_CHECK(1,30))
985 ctx->supports_luster = 1;
986 if (FW_VER_CHECK(1,33))
987 ctx->supports_media_ext = 1;
988 if (FW_VER_CHECK(1,52))
989 ctx->supports_finematte = 1;
990 break;
991 case P_DNP_DS820:
992 ctx->native_width = 2560;
993 ctx->max_height = 7536;
994 ctx->correct_count = 1;
995 ctx->supports_counterp = 1;
996 ctx->supports_matte = 1;
997 ctx->supports_fullcut = 1;
998 ctx->supports_mqty_default = 1;
999 if (strchr(ctx->version, 'A'))
1000 ctx->supports_rewind = 0;
1001 else
1002 ctx->supports_rewind = 1;
1003 ctx->supports_standby = 1;
1004 ctx->supports_iserial = 1;
1005 ctx->supports_adv_fullcut = 1;
1006 ctx->supports_advmatte = 1;
1007 ctx->supports_luster = 1;
1008 ctx->supports_finematte = 1;
1009 ctx->supports_printspeed = 1;
1010 ctx->supports_lowspeed = 1;
1011 ctx->supports_highdensity = 1;
1012 if (FW_VER_CHECK(0,50))
1013 ctx->supports_gamma = 1;
1014 break;
1015 default:
1016 ERROR("Unknown printer type %d\n", ctx->type);
1017 return CUPS_BACKEND_FAILED;
1018 }
1019
1020 ctx->last_matte = -1;
1021 #ifdef STATE_DIR
1022 /* Check our current job's lamination vs previous job. */
1023 {
1024 /* Load last matte status from file */
1025 char buf[64];
1026 FILE *f;
1027 snprintf(buf, sizeof(buf), STATE_DIR "/%s-last", ctx->serno);
1028 f = fopen(buf, "r");
1029 if (f) {
1030 fscanf(f, "%d", &ctx->last_matte);
1031 fclose(f);
1032 }
1033 }
1034 #endif
1035
1036 if (test_mode < TEST_MODE_NOATTACH && ctx->supports_mediaoffset) {
1037 /* Get Media Offset */
1038 struct dnpds40_cmd cmd;
1039 uint8_t *resp;
1040 int len = 0;
1041
1042 dnpds40_build_cmd(&cmd, "INFO", "MEDIA_OFFSET", 0);
1043 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
1044 if (resp) {
1045 ctx->mediaoffset = atoi((char*)resp+4);
1046 free(resp);
1047 } else {
1048 return CUPS_BACKEND_FAILED;
1049 }
1050 } else if (!ctx->correct_count) {
1051 ctx->mediaoffset = 50;
1052 }
1053
1054 if (test_mode < TEST_MODE_NOATTACH && ctx->supports_mqty_default) {
1055 struct dnpds40_cmd cmd;
1056 uint8_t *resp;
1057 int len = 0;
1058
1059 dnpds40_build_cmd(&cmd, "INFO", "MQTY_DEFAULT", 0);
1060
1061 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
1062 if (resp) {
1063 dnpds40_cleanup_string((char*)resp, len);
1064 ctx->media_count_new = atoi((char*)resp+4);
1065 free(resp);
1066 ctx->media_count_new -= ctx->mediaoffset;
1067 } else {
1068 return CUPS_BACKEND_FAILED;
1069 }
1070 } else {
1071 /* Look it up for legacy models & FW */
1072 switch (ctx->type) {
1073 case P_DNP_DS40:
1074 switch (ctx->media) {
1075 case 200: // L
1076 ctx->media_count_new = 460;
1077 break;
1078 case 210: // 2L
1079 ctx->media_count_new = 230;
1080 break;
1081 case 300: // PC
1082 ctx->media_count_new = 400;
1083 break;
1084 case 310: // A5
1085 ctx->media_count_new = 200;
1086 break;
1087 case 400: // A5W
1088 ctx->media_count_new = 180;
1089 break;
1090 default:
1091 ctx->media_count_new = 0;
1092 break;
1093 }
1094 break;
1095 case P_DNP_DSRX1:
1096 switch (ctx->media) {
1097 case 210: // 2L
1098 ctx->media_count_new = 350;
1099 break;
1100 case 300: // PC
1101 ctx->media_count_new = 700;
1102 break;
1103 case 310: // A5
1104 ctx->media_count_new = 350;
1105 break;
1106 default:
1107 ctx->media_count_new = 0;
1108 break;
1109 }
1110 break;
1111 case P_CITIZEN_OP900II:
1112 switch (ctx->media) {
1113 case 210: // 2L
1114 ctx->media_count_new = 350;
1115 break;
1116 case 300: // PC
1117 ctx->media_count_new = 600;
1118 break;
1119 case 310: // A5
1120 ctx->media_count_new = 300;
1121 break;
1122 case 400: // A5W
1123 ctx->media_count_new = 280;
1124 break;
1125 default:
1126 ctx->media_count_new = 0;
1127 break;
1128 }
1129 break;
1130 case P_CITIZEN_CW01:
1131 switch (ctx->media) {
1132 case 300: // PC
1133 ctx->media_count_new = 600;
1134 break;
1135 case 350: // 2L
1136 ctx->media_count_new = 230;
1137 break;
1138 case 400: // A5W
1139 ctx->media_count_new = 280;
1140 break;
1141 default:
1142 ctx->media_count_new = 0;
1143 break;
1144 }
1145 break;
1146 case P_DNP_DS80:
1147 case P_DNP_DS80D:
1148 switch (ctx->media) {
1149 case 500: // 8x10
1150 ctx->media_count_new = 130;
1151 break;
1152 case 510: // 8x12
1153 ctx->media_count_new = 110;
1154 break;
1155 default:
1156 ctx->media_count_new = 0;
1157 break;
1158 }
1159 break;
1160 default:
1161 ctx->media_count_new = 0;
1162 break;
1163 }
1164 }
1165
1166 /* Fill out marker structure */
1167 ctx->marker[0].color = "#00FFFF#FF00FF#FFFF00";
1168 ctx->marker[0].name = dnpds40_media_types(ctx->media);
1169 ctx->marker[0].levelmax = ctx->media_count_new;
1170 ctx->marker[0].levelnow = -2;
1171 ctx->marker_count = 1;
1172
1173 if (ctx->type == P_DNP_DS80D) {
1174 ctx->marker[1].color = "#00FFFF#FF00FF#FFFF00";
1175 ctx->marker[1].name = dnpds80_duplex_media_types(ctx->duplex_media);
1176 ctx->marker[1].levelmax = ctx->marker[0].levelmax/2;
1177 ctx->marker[1].levelnow = -2;
1178 ctx->marker_count++;
1179 }
1180
1181 return CUPS_BACKEND_OK;
1182 }
1183
dnpds40_cleanup_job(const void * vjob)1184 static void dnpds40_cleanup_job(const void *vjob) {
1185 const struct dnpds40_printjob *job = vjob;
1186
1187 if (job->databuf)
1188 free(job->databuf);
1189
1190 free((void*)job);
1191 }
1192
dnpds40_teardown(void * vctx)1193 static void dnpds40_teardown(void *vctx) {
1194 struct dnpds40_ctx *ctx = vctx;
1195
1196 if (!ctx)
1197 return;
1198
1199 if (test_mode < TEST_MODE_NOATTACH && ctx->type == P_DNP_DS80D) {
1200 struct dnpds40_cmd cmd;
1201
1202 /* Check to see if last print was the front side
1203 of a duplex job, and if so, cancel things so we're done */
1204 if (ctx->last_multicut >= 200 &&
1205 ctx->last_multicut < 300) {
1206 dnpds40_build_cmd(&cmd, "CNTRL", "DUPLEX_CANCEL", 0);
1207 if ((dnpds40_do_cmd(ctx, &cmd, NULL, 0)) != 0)
1208 return;
1209 }
1210 }
1211
1212 if (ctx->serno)
1213 free(ctx->serno);
1214 if (ctx->version)
1215 free(ctx->version);
1216 free(ctx);
1217 }
1218
1219 #define MAX_PRINTJOB_LEN (((ctx->native_width*ctx->max_height+1024+54+10))*3+1024) /* Worst-case, YMC */
1220
dnpds40_read_parse(void * vctx,const void ** vjob,int data_fd,int copies)1221 static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
1222 struct dnpds40_ctx *ctx = vctx;
1223 int run = 1;
1224 char buf[9] = { 0 };
1225
1226 struct dnpds40_printjob *job = NULL;
1227 struct dyesub_joblist *list;
1228 int can_combine = 0;
1229
1230 if (!ctx)
1231 return CUPS_BACKEND_FAILED;
1232
1233 job = malloc(sizeof(*job));
1234 if (!job) {
1235 ERROR("Memory allocation failure!\n");
1236 return CUPS_BACKEND_RETRY_CURRENT;
1237 }
1238 memset(job, 0, sizeof(*job));
1239 job->printspeed = -1;
1240 job->copies = copies;
1241
1242 /* There's no way to figure out the total job length in advance, we
1243 have to parse the stream until we get to the image plane data,
1244 and even then the stream can contain arbitrary commands later.
1245
1246 So instead, we allocate a buffer of the maximum possible length,
1247 then parse the incoming stream until we hit the START command at
1248 the end of the job.
1249 */
1250
1251 job->databuf = malloc(MAX_PRINTJOB_LEN);
1252 if (!job->databuf) {
1253 dnpds40_cleanup_job(job);
1254 ERROR("Memory allocation failure!\n");
1255 return CUPS_BACKEND_RETRY_CURRENT;
1256 }
1257
1258 while (run) {
1259 int remain, i, j;
1260 /* Read in command header */
1261 i = read(data_fd, job->databuf + job->datalen,
1262 sizeof(struct dnpds40_cmd));
1263 if (i < 0) {
1264 dnpds40_cleanup_job(job);
1265 return i;
1266 }
1267 if (i == 0)
1268 break;
1269 if (i < (int) sizeof(struct dnpds40_cmd)) {
1270 dnpds40_cleanup_job(job);
1271 return CUPS_BACKEND_CANCEL;
1272 }
1273
1274 /* Special case handling for beginning of job */
1275 if (job->datalen == 0) {
1276 /* See if job lacks the standard ESC-P start sequence */
1277 if (job->databuf[job->datalen + 0] != 0x1b ||
1278 job->databuf[job->datalen + 1] != 0x50) {
1279 switch(ctx->type) {
1280 case P_CITIZEN_CW01:
1281 i = legacy_cw01_read_parse(job, data_fd, i);
1282 break;
1283 case P_DNP_DS620:
1284 i = legacy_dnp620_read_parse(job, data_fd, i);
1285 break;
1286 case P_DNP_DS820:
1287 i = legacy_dnp820_read_parse(job, data_fd, i);
1288 break;
1289 case P_DNP_DSRX1:
1290 case P_DNP_DS40:
1291 case P_DNP_DS80:
1292 case P_DNP_DS80D:
1293 default:
1294 i = legacy_dnp_read_parse(job, data_fd, i);
1295 break;
1296 }
1297
1298 if (i == CUPS_BACKEND_OK) {
1299 goto parsed;
1300 }
1301 dnpds40_cleanup_job(job);
1302 return i;
1303 }
1304 }
1305
1306 /* Parse out length of data chunk, if any */
1307 memcpy(buf, job->databuf + job->datalen + 24, 8);
1308 j = atoi(buf);
1309
1310 /* Read in data chunk as quickly as possible */
1311 remain = j;
1312 while (remain > 0) {
1313 i = read(data_fd, job->databuf + job->datalen + sizeof(struct dnpds40_cmd),
1314 remain);
1315 if (i < 0) {
1316 ERROR("Data Read Error: %d (%d/%d @%d/%d)\n", i, remain, j, job->datalen,MAX_PRINTJOB_LEN);
1317 dnpds40_cleanup_job(job);
1318 return i;
1319 }
1320 if (i == 0) {
1321 dnpds40_cleanup_job(job);
1322 return 1;
1323 }
1324 job->datalen += i;
1325 remain -= i;
1326 }
1327 job->datalen -= j; /* Back it off */
1328
1329 /* Check for some offsets */
1330 if(!memcmp("CNTRL QTY", job->databuf + job->datalen+2, 9)) {
1331 /* Ignore this. We will insert our own later on */
1332 continue;
1333 }
1334 if(!memcmp("CNTRL CUTTER", job->databuf + job->datalen+2, 12)) {
1335 memcpy(buf, job->databuf + job->datalen + 32, 8);
1336 job->cutter = atoi(buf);
1337 /* We'll insert it ourselves later */
1338 continue;
1339 }
1340 if(!memcmp("CNTRL BUFFCNTRL", job->databuf + job->datalen+2, 15)) {
1341 /* Ignore this. We will insert our own later on
1342 if the printer and job support it. */
1343 continue;
1344 }
1345 if(!memcmp("CNTRL OVERCOAT", job->databuf + job->datalen+2, 14)) {
1346 if (ctx->supports_matte) {
1347 memcpy(buf, job->databuf + job->datalen + 32, 8);
1348 job->matte = atoi(buf);
1349 } else {
1350 WARNING("Printer FW does not support matte prints, using glossy mode\n");
1351 }
1352 /* We'll insert our own later, if appropriate */
1353 continue;
1354 }
1355 if(!memcmp("IMAGE MULTICUT", job->databuf + job->datalen+2, 14)) {
1356 memcpy(buf, job->databuf + job->datalen + 32, 8);
1357 job->multicut = atoi(buf);
1358 /* Backend automatically handles rewind support, so
1359 ignore application requests to use it. */
1360 if (job->multicut > 400)
1361 job->multicut -= 400;
1362
1363 /* We'll insert this ourselves later. */
1364 continue;
1365 }
1366 if(!memcmp("CNTRL FULL_CUTTER_SET", job->databuf + job->datalen+2, 21)) {
1367 if (!ctx->supports_fullcut) {
1368 WARNING("Printer FW does not support full cutter control!\n");
1369 continue;
1370 }
1371
1372 if (ctx->type == P_DNP_DS820) {
1373 if (j != 24) {
1374 WARNING("Full cutter argument length incorrect, ignoring!\n");
1375 continue;
1376 }
1377 } else if (j != 16) {
1378 WARNING("Full cutter argument length incorrect, ignoring!\n");
1379 continue;
1380 } else if (!ctx->supports_adv_fullcut) {
1381 if (job->databuf[job->datalen + 32 + 12] != '0' ||
1382 job->databuf[job->datalen + 32 + 13] != '0' ||
1383 job->databuf[job->datalen + 32 + 14] != '0') {
1384 WARNING("Full cutter scrap setting not supported on this firmware, ignoring!\n");
1385 continue;
1386 }
1387 }
1388 // XXX enforce cut counts/sizes?
1389
1390 job->fullcut = 1;
1391 }
1392 if(!memcmp("IMAGE YPLANE", job->databuf + job->datalen + 2, 12)) {
1393 uint32_t y_ppm; /* Pixels Per Meter */
1394
1395 /* Validate vertical resolution */
1396 memcpy(&y_ppm, job->databuf + job->datalen + 32 + 42, sizeof(y_ppm));
1397 y_ppm = le32_to_cpu(y_ppm);
1398
1399 switch (y_ppm) {
1400 case 11808:
1401 job->dpi = 300;
1402 break;
1403 case 13146:
1404 job->dpi = 334;
1405 break;
1406 case 23615:
1407 case 23616:
1408 job->dpi = 600;
1409 break;
1410 default:
1411 ERROR("Unrecognized printjob resolution (%u ppm)\n", y_ppm);
1412 dnpds40_cleanup_job(job);
1413 return CUPS_BACKEND_CANCEL;
1414 }
1415
1416 /* Validate horizontal size */
1417 memcpy(&y_ppm, job->databuf + job->datalen + 32 + 18, sizeof(y_ppm));
1418 y_ppm = le32_to_cpu(y_ppm);
1419 if (y_ppm != ctx->native_width) {
1420 ERROR("Incorrect horizontal resolution (%u), aborting!\n", y_ppm);
1421 dnpds40_cleanup_job(job);
1422 return CUPS_BACKEND_CANCEL;
1423 }
1424 }
1425 if(!memcmp("CNTRL PRINTSPEED", job->databuf + job->datalen + 2, 16)) {
1426 if (!ctx->supports_printspeed) {
1427 WARNING("Printer does not support PRINTSPEED\n");
1428 continue;
1429 }
1430 memcpy(buf, job->databuf + job->datalen + 32, 8);
1431 job->printspeed = atoi(buf) / 10;
1432
1433 /* We'll insert this ourselves later. */
1434 continue;
1435 }
1436
1437 /* This is the last block.. */
1438 if(!memcmp("CNTRL START", job->databuf + job->datalen + 2, 11))
1439 run = 0;
1440
1441 /* Add in the size of this chunk */
1442 job->datalen += sizeof(struct dnpds40_cmd) + j;
1443 }
1444 parsed:
1445 /* If we have no data.. don't bother */
1446 if (!job->datalen) {
1447 dnpds40_cleanup_job(job);
1448 return CUPS_BACKEND_CANCEL;
1449 }
1450
1451 /* Sanity check matte mode */
1452 if (job->matte == 21 && !ctx->supports_finematte) {
1453 WARNING("Printer FW does not support Fine Matte mode, downgrading to normal matte\n");
1454 job->matte = 1;
1455 } else if (job->matte == 22 && !ctx->supports_luster) {
1456 WARNING("Printer FW does not support Luster mode, downgrading to normal matte\n");
1457 job->matte = 1;
1458 } else if (job->matte > 1 && !ctx->supports_advmatte) {
1459 WARNING("Printer FW does not support advanced matte modes, downgrading to normal matte\n");
1460 job->matte = 1;
1461 }
1462
1463 /* Pick a sane default value for printspeed if not specified */
1464 if (job->printspeed == -1 || job->printspeed > 3)
1465 {
1466 if (job->dpi == 600)
1467 job->printspeed = 1;
1468 else
1469 job->printspeed = 0;
1470 }
1471 /* And sanity-check whatever value is there */
1472 if (job->printspeed == 0 && job->dpi == 600) {
1473 job->printspeed = 1;
1474 } else if (job->printspeed >= 1 && job->dpi == 300) {
1475 job->printspeed = 0;
1476 }
1477
1478 /* Make sure MULTICUT is sane, most validation needs this */
1479 if (!job->multicut && ctx->type != P_CITIZEN_CW01) {
1480 WARNING("Missing or illegal MULTICUT command!\n");
1481 if (job->dpi == 300)
1482 job->buf_needed = 1;
1483 else
1484 job->buf_needed = 2;
1485
1486 goto skip_checks;
1487 }
1488
1489 /* Only DS80D supports Cut Paper types */
1490 if (job->multicut > 100) {
1491 if ( ctx->type == P_DNP_DS80D) {
1492 job->cut_paper = 1;
1493 } else {
1494 ERROR("Only DS80D supports cut-paper sizes!\n");
1495 dnpds40_cleanup_job(job);
1496 return CUPS_BACKEND_CANCEL;
1497 }
1498 }
1499
1500 /* Figure out the number of buffers we need. */
1501 job->buf_needed = 1;
1502
1503 if (job->dpi == 600) {
1504 switch(ctx->type) {
1505 case P_DNP_DS620:
1506 if (job->multicut == MULTICUT_6x9 ||
1507 job->multicut == MULTICUT_6x4_5X2)
1508 job->buf_needed = 2;
1509 break;
1510 case P_DNP_DS80: /* DS80/CX-W */
1511 if (job->matte && (job->multicut == MULTICUT_8xA4LEN ||
1512 job->multicut == MULTICUT_8x4X3 ||
1513 job->multicut == MULTICUT_8x8_8x4 ||
1514 job->multicut == MULTICUT_8x6X2 ||
1515 job->multicut == MULTICUT_8x12))
1516 job->buf_needed = 2;
1517 break;
1518 case P_DNP_DS80D:
1519 if (job->matte) {
1520 int mcut = job->multicut;
1521
1522 if (mcut > MULTICUT_S_BACK)
1523 mcut -= MULTICUT_S_BACK;
1524 else if (mcut > MULTICUT_S_FRONT)
1525 mcut -= MULTICUT_S_FRONT;
1526
1527 if (mcut == MULTICUT_8xA4LEN ||
1528 mcut == MULTICUT_8x4X3 ||
1529 mcut == MULTICUT_8x8_8x4 ||
1530 mcut == MULTICUT_8x6X2 ||
1531 mcut == MULTICUT_8x12)
1532 job->buf_needed = 2;
1533
1534 if (mcut == MULTICUT_S_8x12 ||
1535 mcut == MULTICUT_S_8x6X2 ||
1536 mcut == MULTICUT_S_8x4X3)
1537 job->buf_needed = 2;
1538 }
1539 break;
1540 case P_DNP_DS820:
1541 // Nothing; all sizes only need 1 buffer
1542 break;
1543 case P_CITIZEN_CW01:
1544 job->buf_needed = 2;
1545 break;
1546 default: /* DS40/CX/RX1/CY/everything else */
1547 if (job->matte) {
1548 if (job->multicut == MULTICUT_6x8 ||
1549 job->multicut == MULTICUT_6x9 ||
1550 job->multicut == MULTICUT_6x4X2 ||
1551 job->multicut == MULTICUT_5x7 ||
1552 job->multicut == MULTICUT_5x3_5X2)
1553 job->buf_needed = 2;
1554
1555 } else {
1556 if (job->multicut == MULTICUT_6x8 ||
1557 job->multicut == MULTICUT_6x9 ||
1558 job->multicut == MULTICUT_6x4X2)
1559 job->buf_needed = 1;
1560 }
1561 break;
1562 }
1563 }
1564 if (job->dpi == 334 && ctx->type != P_CITIZEN_CW01)
1565 {
1566 ERROR("Illegal resolution (%u) for printer!\n", job->dpi);
1567 dnpds40_cleanup_job(job);
1568 return CUPS_BACKEND_CANCEL;
1569 }
1570
1571 /* Sanity-check type vs loaded media */
1572 if (job->multicut == 0)
1573 goto skip_multicut;
1574
1575 if (job->multicut < 100) {
1576 switch(ctx->media) {
1577 case 200: //"5x3.5 (L)"
1578 if (job->multicut != MULTICUT_5x3_5) {
1579 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1580 dnpds40_cleanup_job(job);
1581 return CUPS_BACKEND_CANCEL;
1582 }
1583 break;
1584 case 210: //"5x7 (2L)"
1585 if (job->multicut != MULTICUT_5x3_5 && job->multicut != MULTICUT_5x7 &&
1586 job->multicut != MULTICUT_5x3_5X2 && job->multicut != MULTICUT_5x5) {
1587 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1588 dnpds40_cleanup_job(job);
1589 return CUPS_BACKEND_CANCEL;
1590 }
1591 /* Only 3.5x5 on 7x5 media can be rewound */
1592 if (job->multicut == MULTICUT_5x3_5)
1593 job->can_rewind = 1;
1594 break;
1595 case 300: //"6x4 (PC)"
1596 if (job->multicut != MULTICUT_6x4) {
1597 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1598 dnpds40_cleanup_job(job);
1599 return CUPS_BACKEND_CANCEL;
1600 }
1601 break;
1602 case 310: //"6x8 (A5)"
1603 if (job->multicut != MULTICUT_6x4 && job->multicut != MULTICUT_6x8 &&
1604 job->multicut != MULTICUT_6x4X2 &&
1605 job->multicut != MULTICUT_6x6 && job->multicut != 30) {
1606 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1607 dnpds40_cleanup_job(job);
1608 return CUPS_BACKEND_CANCEL;
1609 }
1610 /* Only 6x4 on 6x8 media can be rewound */
1611 if (job->multicut == MULTICUT_6x4)
1612 job->can_rewind = 1;
1613 break;
1614 case 400: //"6x9 (A5W)"
1615 if (job->multicut != MULTICUT_6x4 && job->multicut != MULTICUT_6x8 &&
1616 job->multicut != MULTICUT_6x9 && job->multicut != MULTICUT_6x4X2 &&
1617 job->multicut != MULTICUT_6x6 &&
1618 job->multicut != MULTICUT_6x4_5 && job->multicut != MULTICUT_6x4_5X2) {
1619 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1620 dnpds40_cleanup_job(job);
1621 return CUPS_BACKEND_CANCEL;
1622 }
1623 /* Only 6x4 or 6x4.5 on 6x9 media can be rewound */
1624 if (job->multicut == MULTICUT_6x4 || job->multicut == MULTICUT_6x4_5)
1625 job->can_rewind = 1;
1626 break;
1627 case 500: //"8x10"
1628 if (ctx->type == P_DNP_DS820 &&
1629 (job->multicut == MULTICUT_8x7 || job->multicut == MULTICUT_8x9)) {
1630 /* These are okay */
1631 } else if (job->multicut < MULTICUT_8x10 || job->multicut == MULTICUT_8x12 ||
1632 job->multicut == MULTICUT_8x6X2 || job->multicut >= MULTICUT_8x6_8x5 ) {
1633 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1634 dnpds40_cleanup_job(job);
1635 return CUPS_BACKEND_CANCEL;
1636 }
1637
1638 /* 8x4, 8x5 can be rewound */
1639 if (job->multicut == MULTICUT_8x4 ||
1640 job->multicut == MULTICUT_8x5)
1641 job->can_rewind = 1;
1642 break;
1643 case 510: //"8x12"
1644 if (job->multicut < MULTICUT_8x10 || (job->multicut > MULTICUT_8xA4LEN && !(job->multicut == MULTICUT_8x7 || job->multicut == MULTICUT_8x9))) {
1645 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1646 dnpds40_cleanup_job(job);
1647 return CUPS_BACKEND_CANCEL;
1648 }
1649
1650 /* 8x4, 8x5, 8x6 can be rewound */
1651 if (job->multicut == MULTICUT_8x4 ||
1652 job->multicut == MULTICUT_8x5 ||
1653 job->multicut == MULTICUT_8x6)
1654 job->can_rewind = 1;
1655 break;
1656 case 600: //"A4"
1657 if (job->multicut < MULTICUT_A5 || job->multicut > MULTICUT_A4x5X2) {
1658 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
1659 dnpds40_cleanup_job(job);
1660 return CUPS_BACKEND_CANCEL;
1661 }
1662 /* A4xn and A5 can be rewound */
1663 if (job->multicut == MULTICUT_A4x4 ||
1664 job->multicut == MULTICUT_A4x5 ||
1665 job->multicut == MULTICUT_A4x6 ||
1666 job->multicut == MULTICUT_A5)
1667 job->can_rewind = 1;
1668 break;
1669 default:
1670 ERROR("Unknown media (%u vs %u)!\n", ctx->media, job->multicut);
1671 dnpds40_cleanup_job(job);
1672 return CUPS_BACKEND_CANCEL;
1673 }
1674 } else if (job->multicut < 400) {
1675 int mcut = job->multicut;
1676
1677 switch(ctx->duplex_media) {
1678 case 100: //"8x10.75"
1679 if (mcut > MULTICUT_S_BACK)
1680 mcut -= MULTICUT_S_BACK;
1681 else if (mcut > MULTICUT_S_FRONT)
1682 mcut -= MULTICUT_S_FRONT;
1683
1684 if (mcut == MULTICUT_S_8x12 ||
1685 mcut == MULTICUT_S_8x6X2 ||
1686 mcut == MULTICUT_S_8x4X3) {
1687 ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->duplex_media, job->multicut);
1688 dnpds40_cleanup_job(job);
1689 return CUPS_BACKEND_CANCEL;
1690 }
1691 break;
1692 case 200: //"8x12"
1693 /* Everything is legal */
1694 break;
1695 default:
1696 ERROR("Unknown duplexer media (%u vs %u)!\n", ctx->duplex_media, job->multicut);
1697 dnpds40_cleanup_job(job);
1698 return CUPS_BACKEND_CANCEL;
1699 }
1700 } else {
1701 ERROR("Multicut value out of range! (%u)\n", job->multicut);
1702 dnpds40_cleanup_job(job);
1703 return CUPS_BACKEND_CANCEL;
1704 }
1705
1706 /* Additional santity checks, make sure printer support exists */
1707 if (!ctx->supports_6x6 && job->multicut == MULTICUT_6x6) {
1708 ERROR("Printer does not support 6x6 prints, aborting!\n");
1709 dnpds40_cleanup_job(job);
1710 return CUPS_BACKEND_CANCEL;
1711 }
1712
1713 if (!ctx->supports_5x5 && job->multicut == MULTICUT_5x5) {
1714 ERROR("Printer does not support 5x5 prints, aborting!\n");
1715 dnpds40_cleanup_job(job);
1716 return CUPS_BACKEND_CANCEL;
1717 }
1718
1719 if ((job->multicut == MULTICUT_6x4_5 || job->multicut == MULTICUT_6x4_5X2) &&
1720 !ctx->supports_6x4_5) {
1721 ERROR("Printer does not support 6x4.5 prints, aborting!\n");
1722 dnpds40_cleanup_job(job);
1723 return CUPS_BACKEND_CANCEL;
1724 }
1725
1726 if (job->multicut == MULTICUT_6x9 && !ctx->supports_6x9) {
1727 ERROR("Printer does not support 6x9 prints, aborting!\n");
1728 dnpds40_cleanup_job(job);
1729 return CUPS_BACKEND_CANCEL;
1730 }
1731
1732 if (job->multicut == MULTICUT_5x3_5X2 && !ctx->supports_3x5x2) {
1733 ERROR("Printer does not support 3.5x5*2 prints, aborting!\n");
1734 dnpds40_cleanup_job(job);
1735 return CUPS_BACKEND_CANCEL;
1736 }
1737
1738 skip_multicut:
1739
1740 if (job->fullcut && !ctx->supports_adv_fullcut &&
1741 job->multicut != MULTICUT_6x8) {
1742 ERROR("Printer does not support full control on sizes other than 6x8, aborting!\n");
1743 dnpds40_cleanup_job(job);
1744 return CUPS_BACKEND_CANCEL;
1745 }
1746
1747 if (job->fullcut && job->cutter) {
1748 WARNING("Cannot simultaneously use FULL_CUTTER and CUTTER, using the former\n");
1749 job->cutter = 0;
1750 }
1751
1752 if (job->cutter == 120) {
1753 if (job->multicut == MULTICUT_6x4 || job->multicut == MULTICUT_6x8) {
1754 if (!ctx->supports_2x6) {
1755 ERROR("Printer does not support 2x6 prints, aborting!\n");
1756 dnpds40_cleanup_job(job);
1757 return CUPS_BACKEND_CANCEL;
1758 }
1759 } else {
1760 ERROR("Printer only supports legacy 2-inch cuts on 4x6 or 8x6 jobs!");
1761 dnpds40_cleanup_job(job);
1762 return CUPS_BACKEND_CANCEL;
1763 }
1764 }
1765
1766 skip_checks:
1767 DEBUG("job->dpi %u matte %d mcut %u cutter %d/%d, bufs %d spd %d\n",
1768 job->dpi, job->matte, job->multicut, job->cutter, job->fullcut, job->buf_needed, job->printspeed);
1769
1770 list = dyesub_joblist_create(&dnpds40_backend, ctx);
1771
1772 can_combine = job->can_rewind; /* Any rewindable size can be stacked */
1773
1774 /* Try to combine prints */
1775 if (copies > 1 && can_combine) {
1776 struct dnpds40_printjob *combined;
1777 combined = combine_jobs(job, job);
1778 if (combined) {
1779 combined->copies = job->copies / 2;
1780 combined->can_rewind = 0;
1781 dyesub_joblist_addjob(list, combined);
1782
1783 if (job->copies & 1) {
1784 job->copies = 1;
1785 } else {
1786 dnpds40_cleanup_job(job);
1787 job = NULL;
1788 }
1789 }
1790 }
1791 if (job) {
1792 dyesub_joblist_addjob(list, job);
1793 }
1794
1795 *vjob = list;
1796
1797 return CUPS_BACKEND_OK;
1798 }
1799
dnpds40_main_loop(void * vctx,const void * vjob)1800 static int dnpds40_main_loop(void *vctx, const void *vjob) {
1801 struct dnpds40_ctx *ctx = vctx;
1802 int ret;
1803 struct dnpds40_cmd cmd;
1804 uint8_t *resp;
1805 int len = 0;
1806 uint8_t *ptr;
1807 char buf[9];
1808 int status;
1809 int buf_needed;
1810 int multicut;
1811 int count = 0;
1812 int manual_copies = 0;
1813 int copies;
1814
1815 const struct dnpds40_printjob *job = vjob;
1816
1817 if (!ctx)
1818 return CUPS_BACKEND_FAILED;
1819 if (!job)
1820 return CUPS_BACKEND_FAILED;
1821
1822 buf_needed = job->buf_needed;
1823 multicut = job->multicut;
1824 copies = job->copies;
1825
1826 /* If we switch major overcoat modes, we need both buffers */
1827 if (!!job->matte != ctx->last_matte)
1828 buf_needed = 2;
1829
1830 if (job->cutter == 120) {
1831 /* Work around firmware bug on DS40 where if we run out
1832 of media, we can't resume the job without losing the
1833 cutter setting. */
1834 // XXX add version test? what about other printers?
1835 manual_copies = 1;
1836 }
1837
1838 /* RX1HS requires HS media, but the only way to tell is that the
1839 HS media reports a lot code, while the non-HS media does not. */
1840 if (ctx->needs_mlot) {
1841 /* Get Media Lot */
1842 dnpds40_build_cmd(&cmd, "INFO", "MLOT", 0);
1843
1844 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
1845 if (!resp)
1846 return CUPS_BACKEND_FAILED;
1847
1848 dnpds40_cleanup_string((char*)resp, len);
1849
1850 len = strlen((char*)resp);
1851 free(resp);
1852 if (!len) {
1853 ERROR("Media does not report a valid lot number (non-HS media in RX1HS?)\n");
1854 return CUPS_BACKEND_STOP;
1855 }
1856 }
1857
1858 top:
1859
1860 /* Query status */
1861 dnpds40_build_cmd(&cmd, "STATUS", "", 0);
1862 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
1863 if (!resp)
1864 return CUPS_BACKEND_FAILED;
1865 dnpds40_cleanup_string((char*)resp, len);
1866 status = atoi((char*)resp);
1867 free(resp);
1868
1869 /* Figure out what's going on */
1870 switch(status) {
1871 case 0: /* Idle; we can continue! */
1872 case 1: /* Printing */
1873 {
1874 int bufs;
1875
1876 /* Query buffer state */
1877 dnpds40_build_cmd(&cmd, "INFO", "FREE_PBUFFER", 0);
1878 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
1879
1880 if (!resp)
1881 return CUPS_BACKEND_FAILED;
1882
1883 dnpds40_cleanup_string((char*)resp, len);
1884 /* Check to see if we have sufficient buffers */
1885 bufs = atoi(((char*)resp)+3);
1886 free(resp);
1887 if (bufs < buf_needed) {
1888 INFO("Insufficient printer buffers (%d vs %d), retrying...\n", bufs, buf_needed);
1889 sleep(1);
1890 goto top;
1891 }
1892 break;
1893 }
1894 case 500: /* Cooling print head */
1895 case 510: /* Cooling paper motor */
1896 INFO("Printer cooling down...\n");
1897 sleep(1);
1898 goto top;
1899 case 900:
1900 INFO("Waking printer up from standby...\n");
1901 // XXX do someting here?
1902 break;
1903 case 1000: /* Cover open */
1904 case 1010: /* No Scrap Box */
1905 case 1100: /* Paper End */
1906 case 1200: /* Ribbon End */
1907 case 1300: /* Paper Jam */
1908 case 1400: /* Ribbon Error */
1909 WARNING("Printer not ready: %s, please correct...\n", dnpds40_statuses(status));
1910 sleep(1);
1911 goto top;
1912 case 1500: /* Paper definition error */
1913 ERROR("Paper definition error, aborting job\n");
1914 return CUPS_BACKEND_CANCEL;
1915 case 1600: /* Data error */
1916 ERROR("Data error, aborting job\n");
1917 return CUPS_BACKEND_CANCEL;
1918 default:
1919 ERROR("Fatal Printer Error: %d => %s, halting queue!\n", status, dnpds40_statuses(status));
1920 return CUPS_BACKEND_HOLD;
1921 }
1922
1923 {
1924 /* Figure out remaining native prints */
1925 if (dnpds40_query_markers(ctx, NULL, NULL))
1926 return CUPS_BACKEND_FAILED;
1927 if (ctx->marker[0].levelnow < 0)
1928 return CUPS_BACKEND_FAILED;
1929 dump_markers(ctx->marker, ctx->marker_count, 0);
1930
1931 // For logic below.
1932 count = ctx->marker[0].levelnow;
1933 if (job->cut_paper && count > ctx->marker[1].levelnow)
1934 count = ctx->marker[1].levelnow;
1935
1936 /* See if we can rewind to save media */
1937 if (job->can_rewind && ctx->supports_rewind) {
1938 /* Tell printer to use rewind */
1939 multicut += 400;
1940
1941 /* Get Media remaining */
1942 dnpds40_build_cmd(&cmd, "INFO", "RQTY", 0);
1943
1944 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
1945 if (!resp)
1946 return CUPS_BACKEND_FAILED;
1947
1948 dnpds40_cleanup_string((char*)resp, len);
1949 count = atoi((char*)resp+4);
1950 free(resp);
1951 }
1952
1953 if (ctx->type == P_CITIZEN_CW01) {
1954 /* Get Vertical resolution */
1955 dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0);
1956
1957 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
1958 if (!resp)
1959 return CUPS_BACKEND_FAILED;
1960
1961 dnpds40_cleanup_string((char*)resp, len);
1962
1963 #if 0 // XXX Fix 600dpi support on CW01
1964 // have to read the last DPI, and send the correct CWD over?
1965 if (ctx->dpi == 600 && strcmp("RV0334", *char*)resp) {
1966 ERROR("600DPI prints not yet supported, need 600DPI CWD load");
1967 return CUPS_BACKEND_CANCEL;
1968 }
1969 #endif
1970 free(resp);
1971 }
1972
1973 /* Verify we have sufficient media for prints */
1974
1975 #if 0 // disabled this to allow error to be reported on the printer panel
1976 if (count < 1) {
1977 ERROR("Printer out of media, please correct!\n");
1978 return CUPS_BACKEND_STOP;
1979 }
1980 #endif
1981 if (count < copies) {
1982 WARNING("Printer does not have sufficient remaining media (%d) to complete job (%d)\n", copies, count);
1983 }
1984 }
1985
1986 /* Work around a bug in older gutenprint releases. */
1987 if (ctx->last_multicut >= 200 && ctx->last_multicut < 300 &&
1988 multicut >= 200 && multicut < 300) {
1989 WARNING("Bogus multicut value for duplex page, correcting\n");
1990 multicut += 100;
1991 }
1992
1993 /* Store our last multicut state */
1994 ctx->last_multicut = multicut;
1995
1996 /* Tell printer how many copies to make */
1997 snprintf(buf, sizeof(buf), "%07d\r", manual_copies ? 1 : copies);
1998 dnpds40_build_cmd(&cmd, "CNTRL", "QTY", 8);
1999 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8)))
2000 return CUPS_BACKEND_FAILED;
2001
2002 if (!manual_copies)
2003 copies = 1;
2004
2005 /* Enable job resumption on correctable errors */
2006 if (ctx->supports_matte) {
2007 snprintf(buf, sizeof(buf), "%08d", 1);
2008 /* DS80D does not support BUFFCNTRL when using
2009 cut media; all others support this */
2010 if (ctx->type != P_DNP_DS80D ||
2011 multicut < 100) {
2012 dnpds40_build_cmd(&cmd, "CNTRL", "BUFFCNTRL", 8);
2013 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8)))
2014 return CUPS_BACKEND_FAILED;
2015 }
2016 }
2017
2018 /* Set overcoat parameters if appropriate */
2019 if (ctx->supports_matte) {
2020 snprintf(buf, sizeof(buf), "%08d", job->matte);
2021 dnpds40_build_cmd(&cmd, "CNTRL", "OVERCOAT", 8);
2022 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8)))
2023 return CUPS_BACKEND_FAILED;
2024 }
2025
2026 /* Program in the cutter setting */
2027 if (job->cutter) {
2028 snprintf(buf, sizeof(buf), "%08d", job->cutter);
2029 dnpds40_build_cmd(&cmd, "CNTRL", "CUTTER", 8);
2030 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8)))
2031 return CUPS_BACKEND_FAILED;
2032 }
2033
2034 /* Send over the printspeed if appropriate */
2035 if (ctx->supports_printspeed) {
2036 snprintf(buf, sizeof(buf), "%08d", job->printspeed * 10);
2037 dnpds40_build_cmd(&cmd, "CNTRL", "PRINTSPEED", 8);
2038 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8)))
2039 return CUPS_BACKEND_FAILED;
2040 }
2041
2042 /* Program in the multicut setting, if one exists */
2043 if (multicut) {
2044 snprintf(buf, sizeof(buf), "%08d", multicut);
2045 dnpds40_build_cmd(&cmd, "IMAGE", "MULTICUT", 8);
2046 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8)))
2047 return CUPS_BACKEND_FAILED;
2048 }
2049
2050 /* Finally, send the stream over as individual data chunks */
2051 ptr = job->databuf;
2052 while(ptr && ptr < (job->databuf + job->datalen)) {
2053 int i;
2054 buf[8] = 0;
2055 memcpy(buf, ptr + 24, 8);
2056 i = atoi(buf) + 32;
2057
2058 if ((ret = send_data(ctx->dev, ctx->endp_down,
2059 ptr, i)))
2060 return CUPS_BACKEND_FAILED;
2061
2062 ptr += i;
2063 }
2064 sleep(1); /* Give things a moment */
2065
2066 if (fast_return && !manual_copies) {
2067 INFO("Fast return mode enabled.\n");
2068 } else {
2069 INFO("Waiting for job to complete...\n");
2070 int started = 0;
2071
2072 while (1) {
2073 /* Query status */
2074 dnpds40_build_cmd(&cmd, "STATUS", "", 0);
2075 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2076 if (!resp)
2077 return CUPS_BACKEND_FAILED;
2078 dnpds40_cleanup_string((char*)resp, len);
2079 status = atoi((char*)resp);
2080 free(resp);
2081
2082 /* If we're idle or there's an error..*/
2083 if (status == 0 && started)
2084 break;
2085 if (status)
2086 started = 1;
2087 if (status >= 1000) {
2088 ERROR("Printer encountered error: %s\n", dnpds40_statuses(status));
2089 break;
2090 }
2091 sleep(1);
2092 }
2093
2094 /* Figure out remaining native prints */
2095 if (dnpds40_query_markers(ctx, NULL, NULL))
2096 return CUPS_BACKEND_FAILED;
2097 dump_markers(ctx->marker, ctx->marker_count, 0);
2098 }
2099
2100 /* Clean up */
2101 if (terminate)
2102 copies = 1;
2103
2104 INFO("Print complete (%d copies remaining)\n", copies - 1);
2105
2106 if (copies && --copies) {
2107 /* No need to wait on buffers due to matte switching */
2108 buf_needed = job->buf_needed;
2109 goto top;
2110 }
2111
2112 /* Finally, account for overcoat mode of last print */
2113 ctx->last_matte = !!job->matte;
2114 #ifdef STATE_DIR
2115 {
2116 /* Store last matte status into file */
2117 char buf[64];
2118 FILE *f;
2119 snprintf(buf, sizeof(buf), STATE_DIR "/%s-last", ctx->serno);
2120 f = fopen(buf, "w");
2121 if (f) {
2122 fprintf(f, "%08d", ctx->last_matte);
2123 fclose(f);
2124 }
2125 }
2126 #endif
2127
2128 return CUPS_BACKEND_OK;
2129 }
2130
dnpds40_get_sensors(struct dnpds40_ctx * ctx)2131 static int dnpds40_get_sensors(struct dnpds40_ctx *ctx)
2132 {
2133 struct dnpds40_cmd cmd;
2134 uint8_t *resp;
2135 int len = 0;
2136 char *tok;
2137
2138 /* Get Sensor Info */
2139 dnpds40_build_cmd(&cmd, "INFO", "SENSOR", 0);
2140
2141 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2142 if (!resp)
2143 return CUPS_BACKEND_FAILED;
2144
2145 dnpds40_cleanup_string((char*)resp, len);
2146
2147 tok = strtok((char*)resp, "; -");
2148 do {
2149 char *val = strtok(NULL, "; -");
2150
2151 if (!strcmp("HDT", tok)) {
2152 INFO("Head Temperature : %s\n", val);
2153 } else if (!strcmp("MDT", tok)) {
2154 INFO("Media Temperature : %s\n", val);
2155 } else if (!strcmp("PMK", tok)) {
2156 INFO("Paper Mark : %s\n", val);
2157 } else if (!strcmp("RML", tok)) {
2158 INFO("Ribbon Mark Left : %s\n", val);
2159 } else if (!strcmp("RMC", tok)) {
2160 INFO("Ribbon Mark Right : %s\n", val);
2161 } else if (!strcmp("RMR", tok)) {
2162 INFO("Ribbon Mark Center : %s\n", val);
2163 } else if (!strcmp("PSZ", tok)) {
2164 INFO("Paper Size : %s\n", val);
2165 } else if (!strcmp("PNT", tok)) {
2166 INFO("Paper Notch : %s\n", val);
2167 } else if (!strcmp("PJM", tok)) {
2168 INFO("Paper Jam : %s\n", val);
2169 } else if (!strcmp("PED", tok)) {
2170 INFO("Paper End : %s\n", val);
2171 } else if (!strcmp("PET", tok)) {
2172 INFO("Paper Empty : %s\n", val);
2173 } else if (!strcmp("HDV", tok)) {
2174 INFO("Head Voltage : %s\n", val);
2175 } else if (!strcmp("HMD", tok)) {
2176 INFO("Humidity : %s\n", val);
2177 } else if (!strcmp("RP1", tok)) {
2178 INFO("Roll Paper End 1 : %s\n", val);
2179 } else if (!strcmp("RP2", tok)) {
2180 INFO("Roll Paper End 2 : %s\n", val);
2181 } else if (!strcmp("CSR", tok)) {
2182 INFO("Color Sensor Red : %s\n", val);
2183 } else if (!strcmp("CSG", tok)) {
2184 INFO("Color Sensor Green : %s\n", val);
2185 } else if (!strcmp("CSB", tok)) {
2186 INFO("Color Sensor Blue : %s\n", val);
2187 } else {
2188 INFO("Unknown Sensor: '%s' '%s'\n",
2189 tok, val);
2190 }
2191 } while ((tok = strtok(NULL, "; -")) != NULL);
2192
2193 free(resp);
2194
2195 return CUPS_BACKEND_OK;
2196 }
2197
dnpds40_get_info(struct dnpds40_ctx * ctx)2198 static int dnpds40_get_info(struct dnpds40_ctx *ctx)
2199 {
2200 struct dnpds40_cmd cmd;
2201 uint8_t *resp;
2202 int len = 0;
2203
2204 INFO("Model: %s\n", dnpds40_printer_type(ctx->type));
2205
2206 /* Serial number already queried */
2207 INFO("Serial Number: %s\n", ctx->serno);
2208
2209 /* Firmware version already queried */
2210 INFO("Firmware Version: %s\n", ctx->version);
2211
2212 /* Figure out Duplexer */
2213 if (ctx->type == P_DNP_DS80D) {
2214 dnpds40_build_cmd(&cmd, "INFO", "UNIT_FVER", 0);
2215
2216 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2217 if (!resp)
2218 return CUPS_BACKEND_FAILED;
2219
2220 dnpds40_cleanup_string((char*)resp, len);
2221
2222 INFO("Duplexer Version: %s\n", resp);
2223
2224 free(resp);
2225 }
2226
2227 if (ctx->type == P_CITIZEN_CW01) {
2228 /* Get Horizonal resolution */
2229 dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_H", 0);
2230
2231 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2232 if (!resp)
2233 return CUPS_BACKEND_FAILED;
2234
2235 dnpds40_cleanup_string((char*)resp, len);
2236
2237 INFO("Horizontal Resolution: %s dpi\n", (char*)resp + 3);
2238
2239 free(resp);
2240
2241 /* Get Vertical resolution */
2242 dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0);
2243
2244 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2245 if (!resp)
2246 return CUPS_BACKEND_FAILED;
2247
2248 dnpds40_cleanup_string((char*)resp, len);
2249
2250 INFO("Vertical Resolution: %s dpi\n", (char*)resp + 3);
2251
2252 free(resp);
2253
2254 /* Get Color Control Data Version */
2255 dnpds40_build_cmd(&cmd, "TBL_RD", "Version", 0);
2256
2257 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2258 if (!resp)
2259 return CUPS_BACKEND_FAILED;
2260
2261 dnpds40_cleanup_string((char*)resp, len);
2262
2263 INFO("Color Data Version: %s ", (char*)resp);
2264
2265 free(resp);
2266
2267 /* Get Color Control Data Checksum */
2268 dnpds40_build_cmd(&cmd, "MNT_RD", "CTRLD_CHKSUM", 0);
2269
2270 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2271 if (!resp)
2272 return CUPS_BACKEND_FAILED;
2273
2274 dnpds40_cleanup_string((char*)resp, len);
2275
2276 DEBUG2("(%s)\n", (char*)resp);
2277
2278 free(resp);
2279 }
2280
2281 /* Get Media Color offset */
2282 dnpds40_build_cmd(&cmd, "INFO", "MCOLOR", 0);
2283
2284 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2285 if (!resp)
2286 return CUPS_BACKEND_FAILED;
2287
2288 dnpds40_cleanup_string((char*)resp, len);
2289
2290 INFO("Media Color Offset: Y %u M %u C %u L %u\n", *(resp+2), *(resp+3),
2291 *(resp+4), *(resp+5));
2292
2293 free(resp);
2294
2295 /* Get Media Class */
2296 dnpds40_build_cmd(&cmd, "INFO", "MEDIA_CLASS", 0);
2297
2298 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2299 if (!resp)
2300 return CUPS_BACKEND_FAILED;
2301
2302 dnpds40_cleanup_string((char*)resp, len);
2303
2304 INFO("Media Class: %d\n", atoi((char*)resp + 4));
2305
2306 free(resp);
2307
2308 /* Get Media Lot */
2309 dnpds40_build_cmd(&cmd, "INFO", "MLOT", 0);
2310
2311 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2312 if (!resp)
2313 return CUPS_BACKEND_FAILED;
2314
2315 dnpds40_cleanup_string((char*)resp, len);
2316
2317 INFO("Media Lot Code: %s\n", (char*)resp+2);
2318 free(resp);
2319
2320 /* Get Media ID Set (?) */
2321 dnpds40_build_cmd(&cmd, "MNT_RD", "MEDIA_ID_SET", 0);
2322
2323 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2324 if (!resp)
2325 return CUPS_BACKEND_FAILED;
2326
2327 dnpds40_cleanup_string((char*)resp, len);
2328
2329 INFO("Media ID: %d\n", atoi((char*)resp+4));
2330
2331 free(resp);
2332
2333 if (ctx->type == P_CITIZEN_CW01)
2334 goto skip;
2335
2336 /* Get Ribbon ID code (?) */
2337 dnpds40_build_cmd(&cmd, "MNT_RD", "RIBBON_ID_CODE", 0);
2338
2339 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2340 if (!resp)
2341 return CUPS_BACKEND_FAILED;
2342
2343 dnpds40_cleanup_string((char*)resp, len);
2344
2345 INFO("Ribbon ID: %s\n", (char*)resp);
2346
2347 free(resp);
2348
2349 /* Figure out control data and checksums */
2350
2351 /* 300 DPI */
2352 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD300_Version", 0);
2353
2354 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2355 if (!resp)
2356 return CUPS_BACKEND_FAILED;
2357
2358 dnpds40_cleanup_string((char*)resp, len);
2359
2360 INFO("300 DPI Color Data: %s ", (char*)resp);
2361
2362 free(resp);
2363
2364 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD300_Checksum", 0);
2365
2366 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2367 if (!resp)
2368 return CUPS_BACKEND_FAILED;
2369
2370 dnpds40_cleanup_string((char*)resp, len);
2371
2372 DEBUG2("(%s)\n", (char*)resp);
2373
2374 free(resp);
2375
2376 /* 600 DPI */
2377 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD600_Version", 0);
2378
2379 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2380 if (!resp)
2381 return CUPS_BACKEND_FAILED;
2382
2383 dnpds40_cleanup_string((char*)resp, len);
2384
2385 INFO("600 DPI Color Data: %s ", (char*)resp);
2386
2387 free(resp);
2388
2389 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD600_Checksum", 0);
2390
2391 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2392 if (!resp)
2393 return CUPS_BACKEND_FAILED;
2394
2395 dnpds40_cleanup_string((char*)resp, len);
2396
2397 DEBUG2("(%s)\n", (char*)resp);
2398
2399 free(resp);
2400
2401 if (ctx->supports_lowspeed) {
2402 /* "Low Speed" */
2403 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD610_Version", 0);
2404
2405 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2406 if (!resp)
2407 return CUPS_BACKEND_FAILED;
2408
2409 dnpds40_cleanup_string((char*)resp, len);
2410
2411 INFO("Low Speed Color Data: %s ", (char*)resp);
2412
2413 free(resp);
2414
2415 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD610_Checksum", 0);
2416
2417 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2418 if (!resp)
2419 return CUPS_BACKEND_FAILED;
2420
2421 dnpds40_cleanup_string((char*)resp, len);
2422
2423 DEBUG2("(%s)\n", (char*)resp);
2424
2425 free(resp);
2426 }
2427 if (ctx->supports_highdensity) {
2428 uint8_t buf[5];
2429 int i = 0;
2430
2431 snprintf((char*)buf, sizeof(buf), "%04d", i);
2432
2433 /* "High Density" */
2434 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD620_Version", 4);
2435
2436 resp = dnpds40_resp_cmd2(ctx, &cmd, &len, buf, 4);
2437 if (!resp)
2438 return CUPS_BACKEND_FAILED;
2439
2440 dnpds40_cleanup_string((char*)resp, len);
2441
2442 INFO("High Density Color Data: %s ", (char*)resp);
2443
2444 free(resp);
2445
2446 dnpds40_build_cmd(&cmd, "TBL_RD", "CWD620_Checksum", 4);
2447
2448 resp = dnpds40_resp_cmd2(ctx, &cmd, &len, buf, 4);
2449 if (!resp)
2450 return CUPS_BACKEND_FAILED;
2451
2452 dnpds40_cleanup_string((char*)resp, len);
2453
2454 DEBUG2("(%s)\n", (char*)resp);
2455
2456 free(resp);
2457 }
2458 if (ctx->supports_gamma) {
2459 /* "Low Speed" */
2460 dnpds40_build_cmd(&cmd, "TBL_RD", "CTRLD_GAMMA16", 0);
2461
2462 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2463 if (!resp)
2464 return CUPS_BACKEND_FAILED;
2465
2466 dnpds40_cleanup_string((char*)resp, len);
2467
2468 INFO("Gamma Correction Data Checksum: %s\n", (char*)resp);
2469
2470 free(resp);
2471 }
2472
2473 if (ctx->supports_standby) {
2474 /* Get Standby stuff */
2475 int i;
2476
2477 dnpds40_build_cmd(&cmd, "MNT_RD", "STANDBY_TIME", 0);
2478
2479 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2480 if (!resp)
2481 return CUPS_BACKEND_FAILED;
2482
2483 dnpds40_cleanup_string((char*)resp, len);
2484 i = atoi((char*)resp);
2485
2486 INFO("Standby Transition time: %d minutes\n", i);
2487
2488 free(resp);
2489
2490 /* Get Media End Keep */
2491 dnpds40_build_cmd(&cmd, "MNT_RD", "END_KEEP_MODE", 0);
2492
2493 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2494 if (!resp)
2495 return CUPS_BACKEND_FAILED;
2496
2497 dnpds40_cleanup_string((char*)resp, len);
2498 i = atoi((char*)resp);
2499 INFO("Media End kept across power cycles: %s\n",
2500 i ? "Yes" : "No");
2501
2502 free(resp);
2503 }
2504
2505 if (ctx->supports_iserial) {
2506 int i;
2507 /* Get USB serial descriptor status */
2508 dnpds40_build_cmd(&cmd, "MNT_RD", "USB_ISERI_SET", 0);
2509
2510 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2511 if (!resp)
2512 return CUPS_BACKEND_FAILED;
2513
2514 dnpds40_cleanup_string((char*)resp, len);
2515 i = atoi((char*)resp);
2516
2517 INFO("Report Serial Number in USB descriptor: %s\n",
2518 i ? "Yes" : "No");
2519
2520 free(resp);
2521 }
2522
2523 skip:
2524 return CUPS_BACKEND_OK;
2525 }
2526
dnpds40_get_status(struct dnpds40_ctx * ctx)2527 static int dnpds40_get_status(struct dnpds40_ctx *ctx)
2528 {
2529 struct dnpds40_cmd cmd;
2530 uint8_t *resp;
2531 int len = 0;
2532 int count;
2533
2534 /* Generate command */
2535 dnpds40_build_cmd(&cmd, "STATUS", "", 0);
2536
2537 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2538 if (!resp)
2539 return CUPS_BACKEND_FAILED;
2540
2541 dnpds40_cleanup_string((char*)resp, len);
2542 count = atoi((char*)resp);
2543
2544 INFO("Printer Status: %s (%d)\n", dnpds40_statuses(count), count);
2545
2546 free(resp);
2547
2548 /* Figure out Duplexer */
2549 if (ctx->type == P_DNP_DS80D) {
2550 dnpds40_build_cmd(&cmd, "INFO", "UNIT_STATUS", 0);
2551
2552 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2553 if (!resp)
2554 return CUPS_BACKEND_FAILED;
2555
2556 dnpds40_cleanup_string((char*)resp, len);
2557 count = atoi((char*)resp);
2558
2559 INFO("Duplexer Status: %s\n", dnpds80_duplex_statuses(count));
2560 INFO("Duplexer Media Status: %s\n", dnpds80_duplex_paper_status(ctx->duplex_media_status));
2561
2562 free(resp);
2563 }
2564
2565 /* Get remaining print quantity */
2566 dnpds40_build_cmd(&cmd, "INFO", "PQTY", 0);
2567
2568 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2569 if (!resp)
2570 return CUPS_BACKEND_FAILED;
2571
2572 dnpds40_cleanup_string((char*)resp, len);
2573
2574 INFO("Prints remaining in job: %d\n", atoi((char*)resp + 4));
2575
2576 free(resp);
2577
2578 /* Generate command */
2579 dnpds40_build_cmd(&cmd, "INFO", "FREE_PBUFFER", 0);
2580
2581 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2582 if (!resp)
2583 return CUPS_BACKEND_FAILED;
2584
2585 dnpds40_cleanup_string((char*)resp, len);
2586
2587 INFO("Free Buffers: %d\n", atoi((char*)resp + 3));
2588 free(resp);
2589
2590 /* Report media */
2591 INFO("Media Type: %s\n", dnpds40_media_types(ctx->media));
2592
2593 if (ctx->supports_media_ext) {
2594 int type;
2595 dnpds40_build_cmd(&cmd, "INFO", "MEDIA_EXT_CODE", 0);
2596 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2597 if (!resp)
2598 return CUPS_BACKEND_FAILED;
2599
2600 dnpds40_cleanup_string((char*)resp, len);
2601 *(resp+2) = 0; // Only the first two chars are used.
2602 type = atoi((char*)resp);
2603 INFO("Media Code: %s\n", dnpds620_media_extension_code(type));
2604 free(resp);
2605 }
2606
2607 /* Try to figure out media subtype */
2608 if (ctx->type == P_DNP_DS820) {
2609 int type;
2610 dnpds40_build_cmd(&cmd, "INFO", "MEDIA_CLASS_RFID", 0);
2611 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2612 if (!resp)
2613 return CUPS_BACKEND_FAILED;
2614
2615 dnpds40_cleanup_string((char*)resp, len);
2616 type = atoi((char*)resp);
2617 INFO("Media Subtype: %s\n", dnpds820_media_subtypes(type));
2618 free(resp);
2619 }
2620
2621 /* Report Cut Media */
2622 if (ctx->type == P_DNP_DS80D) {
2623 INFO("Duplex Media Type: %s\n", dnpds80_duplex_media_types(ctx->duplex_media));
2624 INFO("Duplexer Media Status: %s\n", dnpds80_duplex_paper_status(ctx->duplex_media_status));
2625 }
2626
2627 if (ctx->media_count_new)
2628 INFO("Native Prints Available on New Media: %u\n", ctx->media_count_new);
2629
2630 /* Get Media remaining */
2631 count = dnpds40_query_mqty(ctx);
2632 if (count < 0)
2633 return CUPS_BACKEND_FAILED;
2634
2635 INFO("Native Prints Remaining on Media: %d\n", count);
2636
2637 if (ctx->supports_rewind) {
2638 /* Get Media remaining */
2639 dnpds40_build_cmd(&cmd, "INFO", "RQTY", 0);
2640
2641 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2642 if (!resp)
2643 return CUPS_BACKEND_FAILED;
2644
2645 dnpds40_cleanup_string((char*)resp, len);
2646
2647 count = atoi((char*)resp+4);
2648 free(resp);
2649 } else {
2650 // Do nothing, re-use native print count.
2651 }
2652 INFO("Half-Size Prints Remaining on Media: %d\n", count);
2653
2654 return 0;
2655 }
2656
dnpds40_get_counters(struct dnpds40_ctx * ctx)2657 static int dnpds40_get_counters(struct dnpds40_ctx *ctx)
2658 {
2659 struct dnpds40_cmd cmd;
2660 uint8_t *resp;
2661 int len = 0;
2662
2663 /* Generate command */
2664 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_LIFE", 0);
2665
2666 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2667 if (!resp)
2668 return CUPS_BACKEND_FAILED;
2669
2670 dnpds40_cleanup_string((char*)resp, len);
2671
2672 INFO("Lifetime Counter: %d\n", atoi((char*)resp+2));
2673
2674 free(resp);
2675
2676 if (ctx->type == P_DNP_DS620 ||
2677 ctx->type == P_DNP_DS820) {
2678 /* Generate command */
2679 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_HEAD", 0);
2680
2681 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2682 if (!resp)
2683 return CUPS_BACKEND_FAILED;
2684
2685 dnpds40_cleanup_string((char*)resp, len);
2686
2687 INFO("Head Counter: %d\n", atoi((char*)resp+2));
2688
2689 free(resp);
2690 }
2691
2692 /* Generate command */
2693 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_A", 0);
2694
2695 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2696 if (!resp)
2697 return CUPS_BACKEND_FAILED;
2698
2699 dnpds40_cleanup_string((char*)resp, len);
2700
2701 INFO("A Counter: %d\n", atoi((char*)resp+2));
2702
2703 free(resp);
2704
2705 /* Generate command */
2706 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_B", 0);
2707
2708 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2709 if (!resp)
2710 return CUPS_BACKEND_FAILED;
2711
2712 dnpds40_cleanup_string((char*)resp, len);
2713
2714 INFO("B Counter: %d\n", atoi((char*)resp+2));
2715
2716 free(resp);
2717
2718 if (ctx->supports_counterp) {
2719 /* Generate command */
2720 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_P", 0);
2721
2722 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2723 if (!resp)
2724 return CUPS_BACKEND_FAILED;
2725
2726 dnpds40_cleanup_string((char*)resp, len);
2727
2728 INFO("P Counter: %d\n", atoi((char*)resp+2));
2729
2730 free(resp);
2731 }
2732
2733 if (ctx->supports_matte) {
2734 /* Generate command */
2735 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_M", 0);
2736
2737 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2738 if (!resp)
2739 return CUPS_BACKEND_FAILED;
2740
2741 dnpds40_cleanup_string((char*)resp, len);
2742
2743 INFO("M Counter: %d\n", atoi((char*)resp+2));
2744
2745 free(resp);
2746
2747 /* Generate command */
2748 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_MATTE", 0);
2749
2750 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2751 if (!resp)
2752 return CUPS_BACKEND_FAILED;
2753
2754 dnpds40_cleanup_string((char*)resp, len);
2755
2756 INFO("Matte Counter: %d\n", atoi((char*)resp+4));
2757
2758 free(resp);
2759 }
2760
2761 if (ctx->type == P_DNP_DS80D) {
2762 dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_DUPLEX", 0);
2763
2764 resp = dnpds40_resp_cmd(ctx, &cmd, &len);
2765 if (!resp)
2766 return CUPS_BACKEND_FAILED;
2767
2768 dnpds40_cleanup_string((char*)resp, len);
2769
2770 INFO("Duplexer Counter: %d\n", atoi((char*)resp));
2771
2772 free(resp);
2773 }
2774
2775 return CUPS_BACKEND_OK;
2776 }
2777
dnpds40_clear_counter(struct dnpds40_ctx * ctx,char counter)2778 static int dnpds40_clear_counter(struct dnpds40_ctx *ctx, char counter)
2779 {
2780 struct dnpds40_cmd cmd;
2781 char msg[4];
2782 int ret;
2783
2784 /* Generate command */
2785 dnpds40_build_cmd(&cmd, "MNT_WT", "COUNTER_CLEAR", 4);
2786 msg[0] = 'C';
2787 msg[1] = counter;
2788 msg[2] = 0x0d; /* ie carriage return, ASCII '\r' */
2789 msg[3] = 0x00;
2790
2791 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, 4)))
2792 return ret;
2793
2794 return 0;
2795 }
2796
dnpds40_cancel_job(struct dnpds40_ctx * ctx)2797 static int dnpds40_cancel_job(struct dnpds40_ctx *ctx)
2798 {
2799 struct dnpds40_cmd cmd;
2800 int ret;
2801
2802 /* Generate command */
2803 dnpds40_build_cmd(&cmd, "CNTRL", "CANCEL", 0);
2804
2805 if ((ret = dnpds40_do_cmd(ctx, &cmd, NULL, 0)))
2806 return ret;
2807
2808 return 0;
2809 }
2810
dnpds40_reset_printer(struct dnpds40_ctx * ctx)2811 static int dnpds40_reset_printer(struct dnpds40_ctx *ctx)
2812 {
2813 struct dnpds40_cmd cmd;
2814 int ret;
2815
2816 /* Generate command */
2817 dnpds40_build_cmd(&cmd, "CNTRL", "PRINTER_RESET", 0);
2818
2819 if ((ret = dnpds40_do_cmd(ctx, &cmd, NULL, 0)))
2820 return ret;
2821
2822 return 0;
2823 }
2824
dnpds620_standby_mode(struct dnpds40_ctx * ctx,int delay)2825 static int dnpds620_standby_mode(struct dnpds40_ctx *ctx, int delay)
2826 {
2827 struct dnpds40_cmd cmd;
2828 char msg[9];
2829 int ret;
2830
2831 /* Generate command */
2832 dnpds40_build_cmd(&cmd, "MNT_WT", "STANDBY_TIME", 8);
2833 snprintf(msg, sizeof(msg), "%08d", delay);
2834
2835 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, 8)))
2836 return ret;
2837
2838 return 0;
2839 }
2840
dnpds620_media_keep_mode(struct dnpds40_ctx * ctx,int delay)2841 static int dnpds620_media_keep_mode(struct dnpds40_ctx *ctx, int delay)
2842 {
2843 struct dnpds40_cmd cmd;
2844 char msg[9];
2845 int ret;
2846
2847 /* Generate command */
2848 dnpds40_build_cmd(&cmd, "MNT_WT", "END_KEEP_MODE", 4);
2849 snprintf(msg, sizeof(msg), "%02d\r", delay);
2850
2851 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, 4)))
2852 return ret;
2853
2854 return 0;
2855 }
2856
dnpds620_iserial_mode(struct dnpds40_ctx * ctx,int enable)2857 static int dnpds620_iserial_mode(struct dnpds40_ctx *ctx, int enable)
2858 {
2859 struct dnpds40_cmd cmd;
2860 char msg[9];
2861 int ret;
2862
2863 /* Generate command */
2864 dnpds40_build_cmd(&cmd, "MNT_WT", "USB_ISERI_SET", 4);
2865 snprintf(msg, sizeof(msg), "%02d\r", enable);
2866
2867 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, 8)))
2868 return ret;
2869
2870 return 0;
2871 }
2872
dnpds40_set_counter_p(struct dnpds40_ctx * ctx,char * arg)2873 static int dnpds40_set_counter_p(struct dnpds40_ctx *ctx, char *arg)
2874 {
2875 struct dnpds40_cmd cmd;
2876 char msg[9];
2877 int i = atoi(arg);
2878 int ret;
2879
2880 /* Generate command */
2881 dnpds40_build_cmd(&cmd, "MNT_WT", "COUNTERP_SET", 8);
2882 snprintf(msg, sizeof(msg), "%08d", i);
2883
2884 if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, 8)))
2885 return ret;
2886
2887 return 0;
2888 }
2889
dnpds40_cmdline(void)2890 static void dnpds40_cmdline(void)
2891 {
2892 DEBUG("\t\t[ -i ] # Query printer info\n");
2893 DEBUG("\t\t[ -I ] # Query sensor info\n");
2894 DEBUG("\t\t[ -k num ] # Set standby time (1-99 minutes, 0 disables)\n");
2895 DEBUG("\t\t[ -K num ] # Keep Media Status Across Power Cycles (1 on, 0 off)\n");
2896 DEBUG("\t\t[ -n ] # Query counters\n");
2897 DEBUG("\t\t[ -N A|B|M ] # Clear counter A/B/M\n");
2898 DEBUG("\t\t[ -p num ] # Set counter P\n");
2899 DEBUG("\t\t[ -R ] # Reset printer\n");
2900 DEBUG("\t\t[ -s ] # Query status\n");
2901 DEBUG("\t\t[ -x num ] # Set USB iSerialNumber Reporting (1 on, 0 off)\n");
2902 DEBUG("\t\t[ -X ] # Cancel current print job\n");
2903 }
2904
dnpds40_cmdline_arg(void * vctx,int argc,char ** argv)2905 static int dnpds40_cmdline_arg(void *vctx, int argc, char **argv)
2906 {
2907 struct dnpds40_ctx *ctx = vctx;
2908 int i, j = 0;
2909
2910 if (!ctx)
2911 return -1;
2912
2913 while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "iIk:K:nN:p:Rsx:X")) >= 0) {
2914 switch(i) {
2915 GETOPT_PROCESS_GLOBAL
2916 case 'i':
2917 j = dnpds40_get_info(ctx);
2918 break;
2919 case 'I':
2920 j = dnpds40_get_sensors(ctx);
2921 break;
2922 case 'k': {
2923 int sleeptime = atoi(optarg);
2924 if (!ctx->supports_standby) {
2925 ERROR("Printer does not support standby\n");
2926 j = -1;
2927 break;
2928 }
2929 if (sleeptime < 0 || sleeptime > 99) {
2930 ERROR("Value out of range (0-99)");
2931 j = -1;
2932 break;
2933 }
2934 j = dnpds620_standby_mode(ctx, sleeptime);
2935 break;
2936 }
2937 case 'K': {
2938 int keep = atoi(optarg);
2939 if (!ctx->supports_standby) {
2940 ERROR("Printer does not support media keep mode\n");
2941 j = -1;
2942 break;
2943 }
2944 if (keep < 0 || keep > 1) {
2945 ERROR("Value out of range (0-1)");
2946 j = -1;
2947 break;
2948 }
2949 j = dnpds620_media_keep_mode(ctx, keep);
2950 break;
2951 }
2952 case 'n':
2953 j = dnpds40_get_counters(ctx);
2954 break;
2955 case 'N':
2956 if (optarg[0] != 'A' &&
2957 optarg[0] != 'B' &&
2958 optarg[0] != 'M')
2959 return CUPS_BACKEND_FAILED;
2960 if (optarg[0] == 'M' && !ctx->supports_matte) {
2961 ERROR("Printer FW does not support matte functions, please update!\n");
2962 return CUPS_BACKEND_FAILED;
2963 }
2964 j = dnpds40_clear_counter(ctx, optarg[0]);
2965 break;
2966 case 'p':
2967 if (!ctx->supports_counterp) {
2968 ERROR("Printer FW dows not support P counter!\n");
2969 return CUPS_BACKEND_FAILED;
2970 }
2971 j = dnpds40_set_counter_p(ctx, optarg);
2972 break;
2973 case 'R': {
2974 j = dnpds40_reset_printer(ctx);
2975 break;
2976 }
2977 case 's': {
2978 j = dnpds40_get_status(ctx);
2979 break;
2980 }
2981 case 'x': {
2982 int enable = atoi(optarg);
2983 if (!ctx->supports_iserial) {
2984 ERROR("Printer does not support USB iSerialNumber reporting\n");
2985 j = -1;
2986 break;
2987 }
2988 if (enable < 0 || enable > 1) {
2989 ERROR("Value out of range (0-1)");
2990 j = -1;
2991 break;
2992 }
2993 j = dnpds620_iserial_mode(ctx, enable);
2994 break;
2995 }
2996 case 'X': {
2997 j = dnpds40_cancel_job(ctx);
2998 break;
2999 }
3000 default:
3001 break; /* Ignore completely */
3002 }
3003
3004 if (j) return j;
3005 }
3006
3007 return 0;
3008 }
3009
dnpds40_query_markers(void * vctx,struct marker ** markers,int * count)3010 static int dnpds40_query_markers(void *vctx, struct marker **markers, int *count)
3011 {
3012 struct dnpds40_ctx *ctx = vctx;
3013
3014 if (markers)
3015 *markers = ctx->marker;
3016 if (count)
3017 *count = ctx->marker_count;
3018
3019 ctx->marker[0].levelnow = dnpds40_query_mqty(ctx);
3020
3021 if (ctx->marker[0].levelnow < 0)
3022 return CUPS_BACKEND_FAILED;
3023
3024 if (ctx->type == P_DNP_DS80D) {
3025 if (dnpds80dx_query_paper(ctx))
3026 return CUPS_BACKEND_FAILED;
3027 switch (ctx->duplex_media_status) {
3028 case DUPLEX_UNIT_PAPER_NONE:
3029 ctx->marker[1].levelnow = 0;
3030 break;
3031 case DUPLEX_UNIT_PAPER_PROTECTIVE:
3032 ctx->marker[1].levelnow = -1;
3033 break;
3034 case DUPLEX_UNIT_PAPER_PRESENT:
3035 ctx->marker[1].levelnow = -3;
3036 break;
3037 }
3038 }
3039
3040 return CUPS_BACKEND_OK;
3041 }
3042
3043 static const char *dnpds40_prefixes[] = {
3044 "dnp_citizen", "dnpds40", // Family names, do *not* nuke.
3045 "dnp-ds40", "dnp-ds80", "dnp-ds80dx", "dnp-ds620", "dnp-ds820", "dnp-dsrx1",
3046 "citizen-cw-01", "citizen-cw-02", "citizen-cx-02",
3047 // backwards compatibility
3048 "dnpds80", "dnpds80dx", "dnpds620", "dnpds820", "dnprx1",
3049 "citizencw01", "citizencw02", "citizencx02",
3050 // These are all extras.
3051 "citizen-cx", "citizen-cx-w", "citizen-cy", "citizen-cy-02",
3052 "citizen-op900", "citizen-op900ii",
3053 NULL
3054 };
3055
3056 #define USB_VID_CITIZEN 0x1343
3057 #define USB_PID_DNP_DS40 0x0003 // Also Citizen CX
3058 #define USB_PID_DNP_DS80 0x0004 // Also Citizen CX-W and Mitsubishi CP-3800DW
3059 #define USB_PID_DNP_DSRX1 0x0005 // Also Citizen CY
3060 #define USB_PID_DNP_DS80D 0x0008
3061
3062 #define USB_PID_CITIZEN_CW01 0x0002 // Maybe others?
3063 #define USB_PID_CITIZEN_CW02 0x0006 // Also OP900II
3064 #define USB_PID_CITIZEN_CX02 0x000A
3065
3066 #define USB_VID_DNP 0x1452
3067 #define USB_PID_DNP_DS620 0x8b01
3068 #define USB_PID_DNP_DS820 0x9001
3069
3070 /* Exported */
3071 struct dyesub_backend dnpds40_backend = {
3072 .name = "DNP DS-series / Citizen C-series",
3073 .version = "0.117",
3074 .uri_prefixes = dnpds40_prefixes,
3075 .flags = BACKEND_FLAG_JOBLIST,
3076 .cmdline_usage = dnpds40_cmdline,
3077 .cmdline_arg = dnpds40_cmdline_arg,
3078 .init = dnpds40_init,
3079 .attach = dnpds40_attach,
3080 .teardown = dnpds40_teardown,
3081 .read_parse = dnpds40_read_parse,
3082 .cleanup_job = dnpds40_cleanup_job,
3083 .main_loop = dnpds40_main_loop,
3084 .query_serno = dnpds40_query_serno,
3085 .query_markers = dnpds40_query_markers,
3086 .devices = {
3087 { USB_VID_CITIZEN, USB_PID_DNP_DS40, P_DNP_DS40, NULL, "dnp-ds40"}, // Also Citizen CX
3088 { USB_VID_CITIZEN, USB_PID_DNP_DS80, P_DNP_DS80, NULL, "dnp-ds80"}, // Also Citizen CX-W and Mitsubishi CP-3800DW
3089 { USB_VID_CITIZEN, USB_PID_DNP_DS80D, P_DNP_DS80D, NULL, "dnp-ds80dx"},
3090 { USB_VID_CITIZEN, USB_PID_DNP_DSRX1, P_DNP_DSRX1, NULL, "dnp-dsrx1"}, // Also Citizen CY
3091 { USB_VID_DNP, USB_PID_DNP_DS620, P_DNP_DS620, NULL, "dnp-ds620"},
3092 { USB_VID_DNP, USB_PID_DNP_DS820, P_DNP_DS820, NULL, "dnp-ds820"},
3093 { USB_VID_CITIZEN, USB_PID_CITIZEN_CW01, P_CITIZEN_CW01, NULL, "citizen-cw-01"}, // Also OP900 ?
3094 { USB_VID_CITIZEN, USB_PID_CITIZEN_CW02, P_CITIZEN_OP900II, NULL, "citizen-cw-02"}, // Also OP900II
3095 { USB_VID_CITIZEN, USB_PID_CITIZEN_CX02, P_DNP_DS620, NULL, "citizen-cx-02"},
3096 { 0, 0, 0, NULL, NULL}
3097 }
3098 };
3099
3100 /* Windows spool file support */
legacy_spool_helper(struct dnpds40_printjob * job,int data_fd,int read_data,int hdrlen,uint32_t plane_len,int parse_dpi)3101 static int legacy_spool_helper(struct dnpds40_printjob *job, int data_fd,
3102 int read_data, int hdrlen, uint32_t plane_len,
3103 int parse_dpi)
3104 {
3105 uint8_t *buf;
3106 uint8_t bmp_hdr[14];
3107 int i, remain;
3108 uint32_t j;
3109
3110 /* Allocate a temp processing buffer */
3111 remain = plane_len * 3;
3112 buf = malloc(remain);
3113 if (!buf) {
3114 ERROR("Memory allocation failure!\n");
3115 return CUPS_BACKEND_RETRY_CURRENT;
3116 }
3117
3118 /* Copy over the post-jobhdr crap into our processing buffer */
3119 j = read_data - hdrlen;
3120 memcpy(buf, job->databuf + hdrlen, j);
3121 remain -= j;
3122
3123 /* Read in the remaining spool data */
3124 while (remain) {
3125 i = read(data_fd, buf + j, remain);
3126
3127 if (i < 0) {
3128 free(buf);
3129 return i;
3130 }
3131
3132 remain -= i;
3133 j += i;
3134 }
3135
3136 if (parse_dpi) {
3137 /* Parse out Y DPI */
3138 memcpy(&j, buf + 28, sizeof(j));
3139 j = le32_to_cpu(j);
3140 switch(j) {
3141 case 11808:
3142 job->dpi = 300;
3143 break;
3144 case 23615:
3145 case 23616:
3146 job->dpi = 600;
3147 break;
3148 default:
3149 ERROR("Unrecognized printjob resolution (%u ppm)\n", j);
3150 free(buf);
3151 return CUPS_BACKEND_CANCEL;
3152 }
3153 }
3154
3155 /* Generate bitmap file header (same for all planes) */
3156 j = cpu_to_le32(plane_len + 24);
3157 memset(bmp_hdr, 0, sizeof(bmp_hdr));
3158 bmp_hdr[0] = 0x42;
3159 bmp_hdr[1] = 0x4d;
3160 memcpy(bmp_hdr + 2, &j, sizeof(j));
3161 bmp_hdr[10] = 0x40;
3162 bmp_hdr[11] = 0x04;
3163
3164 /* Set up planes */
3165 j = 0;
3166
3167 /* Y plane */
3168 job->datalen += sprintf((char*)job->databuf + job->datalen,
3169 "\033PIMAGE YPLANE %08u", plane_len + 24);
3170 memcpy(job->databuf + job->datalen, bmp_hdr, sizeof(bmp_hdr));
3171 job->datalen += sizeof(bmp_hdr);
3172 memcpy(job->databuf + job->datalen, buf + j, plane_len);
3173 job->datalen += plane_len;
3174 j += plane_len;
3175 memset(job->databuf + job->datalen, 0, 10);
3176 job->datalen += 10;
3177
3178 /* M plane */
3179 job->datalen += sprintf((char*)job->databuf + job->datalen,
3180 "\033PIMAGE MPLANE %08u", plane_len + 24);
3181 memcpy(job->databuf + job->datalen, bmp_hdr, sizeof(bmp_hdr));
3182 job->datalen += sizeof(bmp_hdr);
3183 memcpy(job->databuf + job->datalen, buf + j, plane_len);
3184 job->datalen += plane_len;
3185 j += plane_len;
3186 memset(job->databuf + job->datalen, 0, 10);
3187 job->datalen += 10;
3188
3189 /* C plane */
3190 job->datalen += sprintf((char*)job->databuf + job->datalen,
3191 "\033PIMAGE CPLANE %08u", plane_len + 24);
3192 memcpy(job->databuf + job->datalen, bmp_hdr, sizeof(bmp_hdr));
3193 job->datalen += sizeof(bmp_hdr);
3194 memcpy(job->databuf + job->datalen, buf + j, plane_len);
3195 job->datalen += plane_len;
3196 j += plane_len;
3197 memset(job->databuf + job->datalen, 0, 10);
3198 job->datalen += 10;
3199
3200 /* Start */
3201 job->datalen += sprintf((char*)job->databuf + job->datalen,
3202 "\033PCNTRL START ");
3203
3204 /* We're done */
3205 free(buf);
3206
3207 return CUPS_BACKEND_OK;
3208 }
3209
3210 struct cw01_spool_hdr {
3211 uint8_t type; /* TYPE_??? */
3212 uint8_t res; /* DPI_??? */
3213 uint8_t copies; /* number of prints */
3214 uint8_t null0;
3215 uint32_t plane_len; /* LE */
3216 uint8_t null1[4];
3217 };
3218
3219 #define DPI_334 0
3220 #define DPI_600 1
3221
3222 #define TYPE_DSC 0
3223 #define TYPE_L 1
3224 #define TYPE_PC 2
3225 #define TYPE_2DSC 3
3226 #define TYPE_3L 4
3227 #define TYPE_A5 5
3228 #define TYPE_A6 6
3229
legacy_cw01_read_parse(struct dnpds40_printjob * job,int data_fd,int read_data)3230 static int legacy_cw01_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data)
3231 {
3232 struct cw01_spool_hdr hdr;
3233 uint32_t plane_len;
3234
3235 /* get original header out of structure */
3236 memcpy(&hdr, job->databuf + job->datalen, sizeof(hdr));
3237
3238 /* Early parsing and sanity checking */
3239 plane_len = le32_to_cpu(hdr.plane_len);
3240
3241 if (hdr.type > TYPE_A6 ||
3242 hdr.res > DPI_600 ||
3243 hdr.null1[0] || hdr.null1[1] || hdr.null1[2] || hdr.null1[3]) {
3244 ERROR("Unrecognized header data format @%d!\n", job->datalen);
3245 return CUPS_BACKEND_CANCEL;
3246 }
3247 job->dpi = (hdr.res == DPI_600) ? 600 : 334;
3248 job->cutter = 0;
3249
3250 return legacy_spool_helper(job, data_fd, read_data,
3251 sizeof(hdr), plane_len, 0);
3252 }
3253
3254 struct rx1_spool_hdr {
3255 uint8_t type; /* equals MULTICUT_?? - 1 */
3256 uint8_t null0;
3257 uint8_t copies; /* number of copies, always fixed at 01.. */
3258 uint8_t null1;
3259 uint32_t plane_len; /* LE */
3260 uint8_t flags; /* combination of FLAG_?? */
3261 uint8_t null2[3];
3262 };
3263
3264 #define FLAG_MATTE 0x02
3265 #define FLAG_NORETRY 0x08
3266 #define FLAG_2INCH 0x10
3267
legacy_dnp_read_parse(struct dnpds40_printjob * job,int data_fd,int read_data)3268 static int legacy_dnp_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data)
3269 {
3270 struct rx1_spool_hdr hdr;
3271 uint32_t plane_len;
3272
3273 /* get original header out of structure */
3274 memcpy(&hdr, job->databuf + job->datalen, sizeof(hdr));
3275
3276 /* Early parsing and sanity checking */
3277 plane_len = le32_to_cpu(hdr.plane_len);
3278
3279 if (hdr.type >= MULTICUT_A4x5X2 ||
3280 hdr.null2[0] || hdr.null2[1] || hdr.null2[2]) {
3281 ERROR("Unrecognized header data format @%d!\n", job->datalen);
3282 return CUPS_BACKEND_CANCEL;
3283 }
3284
3285 /* Don't bother with FW version checks for legacy stuff */
3286 job->multicut = hdr.type + 1;
3287 job->matte = (hdr.flags & FLAG_MATTE) ? 1 : 0;
3288 job->cutter = (hdr.flags & FLAG_2INCH) ? 120 : 0;
3289
3290 return legacy_spool_helper(job, data_fd, read_data,
3291 sizeof(hdr), plane_len, 1);
3292 }
3293
3294 struct ds620_spool_hdr {
3295 uint8_t type; /* MULTICUT_?? -1, but >0x90 is a flag for rewind */
3296 uint8_t copies; /* Always fixed at 01..*/
3297 uint8_t null0[2];
3298 uint8_t quality; /* 0x02 is HQ, 0x00 is HS. Equivalent to DPI. */
3299 uint8_t unk[3]; /* Always 00 01 00 */
3300 uint32_t plane_len; /* LE */
3301 uint8_t flags; /* FLAG_?? */
3302 uint8_t null1[3];
3303 };
3304 #define FLAG_LUSTER 0x04
3305 #define FLAG_FINEMATTE 0x06
3306
legacy_dnp620_read_parse(struct dnpds40_printjob * job,int data_fd,int read_data)3307 static int legacy_dnp620_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data)
3308 {
3309 struct ds620_spool_hdr hdr;
3310 uint32_t plane_len;
3311
3312 /* get original header out of structure */
3313 memcpy(&hdr, job->databuf + job->datalen, sizeof(hdr));
3314
3315 /* Early parsing and sanity checking */
3316 plane_len = le32_to_cpu(hdr.plane_len);
3317
3318 /* Used to signify rewind request. Backend handles automatically. */
3319 if (hdr.type > 0x90)
3320 hdr.type -= 0x90;
3321
3322 if (hdr.type > MULTICUT_A4x5X2 ||
3323 hdr.null1[0] || hdr.null1[1] || hdr.null1[2]) {
3324 ERROR("Unrecognized header data format @%d!\n", job->datalen);
3325 return CUPS_BACKEND_CANCEL;
3326 }
3327
3328 /* Don't bother with FW version checks for legacy stuff */
3329 job->multicut = hdr.type + 1;
3330 if ((hdr.flags & FLAG_FINEMATTE) == FLAG_FINEMATTE)
3331 job->matte = 21;
3332 else if (hdr.flags & FLAG_LUSTER)
3333 job->matte = 22;
3334 else if (hdr.flags & FLAG_MATTE)
3335 job->matte = 1;
3336
3337 job->cutter = (hdr.flags & FLAG_2INCH) ? 120 : 0;
3338
3339 return legacy_spool_helper(job, data_fd, read_data,
3340 sizeof(hdr), plane_len, 1);
3341 }
3342
3343 #define FLAG_820_HD 0x80
3344 #define FLAG_820_RETRY 0x20
3345 #define FLAG_820_LUSTER 0x06
3346 #define FLAG_820_FMATTE 0x04
3347 #define FLAG_820_MATTE 0x02
3348
legacy_dnp820_read_parse(struct dnpds40_printjob * job,int data_fd,int read_data)3349 static int legacy_dnp820_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data)
3350 {
3351 struct ds620_spool_hdr hdr;
3352 uint32_t plane_len;
3353
3354 /* get original header out of structure */
3355 memcpy(&hdr, job->databuf + job->datalen, sizeof(hdr));
3356
3357 /* Early parsing and sanity checking */
3358 plane_len = le32_to_cpu(hdr.plane_len);
3359
3360 /* Used to signify rewind request. Backend handles automatically. */
3361 if (hdr.type > 0x90)
3362 hdr.type -= 0x90;
3363
3364 if (hdr.type > MULTICUT_A4x5X2 ||
3365 hdr.null1[0] || hdr.null1[1] || hdr.null1[2]) {
3366 ERROR("Unrecognized header data format @%d!\n", job->datalen);
3367 return CUPS_BACKEND_CANCEL;
3368 }
3369
3370 /* Don't bother with FW version checks for legacy stuff */
3371 job->multicut = hdr.type + 1;
3372 if ((hdr.flags & FLAG_820_FMATTE) == FLAG_820_FMATTE)
3373 job->matte = 21;
3374 else if (hdr.flags & FLAG_820_LUSTER)
3375 job->matte = 22;
3376 else if (hdr.flags & FLAG_820_MATTE)
3377 job->matte = 1;
3378
3379 if (hdr.flags & FLAG_820_HD)
3380 job->printspeed = 3;
3381
3382 return legacy_spool_helper(job, data_fd, read_data,
3383 sizeof(hdr), plane_len, 1);
3384 }
3385
3386
3387 /*
3388
3389 Basic spool file format for CW01
3390
3391 TT RR NN 00 XX XX XX XX 00 00 00 00 <- FILE header.
3392
3393 NN : copies (0x01 or more)
3394 RR : resolution; 0 == 334 dpi, 1 == 600dpi
3395 TT : type 0x02 == 4x6, 0x01 == 5x3.5, 0x06 = 6x9
3396 XX XX XX XX : plane length (LE)
3397 plane length * 3 + 12 == file length.
3398
3399 Followed by three planes, each with this 40b header:
3400
3401 28 00 00 00 00 08 00 00 RR RR 00 00 01 00 08 00
3402 00 00 00 00 00 00 00 00 5a 33 00 00 YY YY 00 00
3403 00 01 00 00 00 00 00 00
3404
3405 RR RR : rows in LE format
3406 YY YY : 0x335a (334dpi) or 0x5c40 (600dpi)
3407
3408 Followed by 1024 bytes of color tables:
3409
3410 ff ff ff 00 ... 00 00 00 00
3411
3412 1024+40 = 1064 bytes of header per plane.
3413
3414 Always have 2048 columns of data.
3415
3416 followed by (2048 * rows) bytes of data.
3417
3418 */
3419