1  /*
2  *   Shinko/Sinfonia CHC-S2145 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  *     LiveLink Technology [ www.livelinktechnology.net ]
9  *
10  *   The latest version of this program can be found at:
11  *
12  *     http://git.shaftnet.org/cgit/selphy_print.git
13  *
14  *   This program is free software; you can redistribute it and/or modify it
15  *   under the terms of the GNU General Public License as published by the Free
16  *   Software Foundation; either version 2 of the License, or (at your option)
17  *   any later version.
18  *
19  *   This program is distributed in the hope that it will be useful, but
20  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
22  *   for more details.
23  *
24  *   You should have received a copy of the GNU General Public License
25  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
26  *
27  *          [http://www.gnu.org/licenses/gpl-2.0.html]
28  *
29  *   SPDX-License-Identifier: GPL-2.0+
30  *
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <signal.h>
42 
43 #define BACKEND shinkos2145_backend
44 
45 #include "backend_common.h"
46 #include "backend_sinfonia.h"
47 
48 enum {
49 	S_IDLE = 0,
50 	S_PRINTER_READY_CMD,
51 	S_PRINTER_SENT_DATA,
52 	S_FINISHED,
53 };
54 
55 /* Structs for printer */
print_counts(uint8_t v)56 static int print_counts (uint8_t v) {
57 	switch (v) {
58 	case CODE_4x6:
59 		return 700;
60 	case CODE_3_5x5:
61 		return 800;
62 	case CODE_5x7:
63 		return 400;
64 	case CODE_6x9:
65 		return 310;
66 	case CODE_6x8:
67 		return 350;
68 	default:
69 		return 700;
70 	}
71 }
72 
73 #if 0
74 #define PRINT_MODE_DEFAULT      0x01
75 #define PRINT_MODE_STD_GLOSSY   0x02
76 #define PRINT_MODE_FINE_GLOSSY  0x03
77 #define PRINT_MODE_STD_MATTE    0x04
78 #define PRINT_MODE_FINE_MATTE   0x05
79 #define PRINT_MODE_STD_EGLOSSY  0x06
80 #define PRINT_MODE_FINE_EGLOSSY 0x07
81 
82 static char *s2145_print_modes(uint8_t v) {
83 	switch (v) {
84 	case PRINT_MODE_DEFAULT:
85 		return "Default";
86 	case PRINT_MODE_STD_GLOSSY:
87 		return "Std Glossy";
88 	case PRINT_MODE_FINE_GLOSSY:
89 		return "Fine Glossy";
90 	case PRINT_MODE_STD_MATTE:
91 		return "Std Matte";
92 	case PRINT_MODE_FINE_MATTE:
93 		return "Fine Matte";
94 	case PRINT_MODE_STD_EGLOSSY:
95 		return "Std ExGlossy";
96 	case PRINT_MODE_FINE_EGLOSSY:
97 		return "Fine ExGlossy";
98 	default:
99 		return "Unknown";
100 	}
101 }
102 #endif
103 
104 struct s2145_reset_cmd {
105 	struct sinfonia_cmd_hdr hdr;
106 	uint8_t  target;
107 } __attribute__((packed));
108 
109 #define RESET_PRINTER       0x03
110 #define RESET_USER_CURVE    0x04
111 
112 struct s2145_readtone_cmd {
113 	struct sinfonia_cmd_hdr hdr;
114 	uint8_t  curveid;
115 } __attribute__((packed));
116 
117 struct s2145_button_cmd {
118 	struct sinfonia_cmd_hdr hdr;
119 	uint8_t  enabled;
120 } __attribute__((packed));
121 
122 #define BUTTON_ENABLED  0x01
123 #define BUTTON_DISABLED 0x00
124 
125 #define FWINFO_TARGET_MAIN_BOOT 0x01
126 #define FWINFO_TARGET_MAIN_APP  0x02
127 #define FWINFO_TARGET_DSP_BOOT  0x03
128 #define FWINFO_TARGET_DSP_APP   0x04
129 #define FWINFO_TARGET_USB_BOOT  0x05
130 #define FWINFO_TARGET_USB_APP   0x06
131 #define FWINFO_TARGET_TABLES    0x07
132 
fwinfo_targets(uint8_t v)133 static char *fwinfo_targets (uint8_t v) {
134 	switch (v) {
135 	case FWINFO_TARGET_MAIN_BOOT:
136 		return "Main Boot";
137 	case FWINFO_TARGET_MAIN_APP:
138 		return "Main App ";
139 	case FWINFO_TARGET_DSP_BOOT:
140 		return "DSP Boot ";
141 	case FWINFO_TARGET_DSP_APP:
142 		return "DSP App  ";
143 	case FWINFO_TARGET_USB_BOOT:
144 		return "USB Boot ";
145 	case FWINFO_TARGET_USB_APP:
146 		return "USB App  ";
147 	case FWINFO_TARGET_TABLES:
148 		return "Tables   ";
149 	default:
150 		return "Unknown  ";
151 	}
152 }
153 
154 struct s2145_update_cmd {
155 	struct sinfonia_cmd_hdr hdr;
156 	uint8_t  target;
157 	uint32_t reserved;
158 	uint32_t size;
159 } __attribute__((packed));
160 
161 struct s2145_setunique_cmd {
162 	struct sinfonia_cmd_hdr hdr;
163 	uint8_t  len;
164 	uint8_t  data[23];  /* Not necessarily all used. */
165 } __attribute__((packed));
166 
error_codes(uint8_t major,uint8_t minor)167 static const char *error_codes(uint8_t major, uint8_t minor)
168 {
169 	switch(major) {
170 	case 0x01: /* "Controller Error" */
171 		switch(minor) {
172 		case 0x01:
173 			return "Controller: EEPROM Write Timeout";
174 		case 0x02:
175 			return "Controller: EEPROM Verify";
176 		case 0x04:
177 			return "Controller: DSP Inactive";
178 		case 0x05:
179 			return "Controller: DSP Application Inactive";
180 		case 0x06:
181 			return "Controller: Main FW Data";
182 		case 0x07:
183 			return "Controller: Main FW Write";
184 		case 0x08:
185 			return "Controller: DSP FW Data";
186 		case 0x09:
187 			return "Controller: DSP FW Write";
188 		case 0x0A:
189 			return "Controller: 0A ASIC??";
190 		case 0x0B:
191 			return "Controller: 0B FPGA??";
192 		case 0x0D:
193 			return "Controller: Tone Curve Write";
194 		case 0x16:
195 			return "Controller: Invalid Parameter Table";
196 		case 0x17:
197 			return "Controller: Parameter Table Data";
198 		case 0x18:
199 			return "Controller: Parameter Table Write";
200 		case 0x29:
201 			return "Controller: DSP Communication";
202 		case 0x2A:
203 			return "Controller: DSP DMA Failure";
204 		default:
205 			return "Controller: Unknown";
206 		}
207 	case 0x02: /* "Mechanical Error" */
208 		switch (minor) {
209 		case 0x01:
210 			return "Mechanical: Thermal Head (Upper Up)";
211 		case 0x02:
212 			return "Mechanical: Thermal Head (Head Up)";
213 		case 0x03:
214 			return "Mechanical: Thermal Head (Head Down)";
215 		case 0x04:
216 			return "Mechanical: Pinch Roller (Initialize)";
217 		case 0x05:
218 			return "Mechanical: Pinch Roller (Mode1)";
219 		case 0x06:
220 			return "Mechanical: Pinch Roller (Mode2)";
221 		case 0x07:
222 			return "Mechanical: Pinch Roller (Mode3)";
223 		case 0x08:
224 			return "Mechanical: Pinch Roller (Mode4)";
225 		case 0x09:
226 			return "Mechanical: Cutter (Right)";
227 		case 0x0A:
228 			return "Mechanical: Cutter (Left)";
229 		case 0x0B:
230 			return "Mechanical: Thermal Head (Head Down Recovery)";
231 		default:
232 			return "Mechanical: Unknown";
233 		}
234 	case 0x03: /* "Sensor Error" */
235 		switch (minor) {
236 		case 0x01:
237 			return "Sensor: Thermal Head";
238 		case 0x02:
239 			return "Sensor: Pinch Roller";
240 		case 0x03:
241 			return "Sensor: Cutter Left";
242 		case 0x04:
243 			return "Sensor: Cutter Right";
244 		case 0x05:
245 			return "Sensor: Cutter Unknown";
246 		case 0x08:
247 			return "Sensor: Ribbon Encoder (Supply)";
248 		case 0x09:
249 			return "Sensor: Ribbon Encoder (Takeup)";
250 		case 0x13:
251 			return "Sensor: Thermal Head";
252 		default:
253 			return "Sensor: Unknown";
254 		}
255 	case 0x04: /* "Temperature Sensor Error" */
256 		switch (minor) {
257 		case 0x01:
258 			return "Temp Sensor: Thermal Head High";
259 		case 0x02:
260 			return "Temp Sensor: Thermal Head Low";
261 		case 0x03:
262 			return "Temp Sensor: Environment High";
263 		case 0x04:
264 			return "Temp Sensor: Environment Low";
265 		case 0x05:
266 			return "Temp Sensor: Warmup Timed Out";
267 		default:
268 			return "Temp Sensor: Unknown";
269 		}
270 	case 0x5: /* "Paper Jam" */
271 		switch (minor) {
272 		case 0x01:
273 			return "Paper Jam: Loading Leading Edge Off";
274 		case 0x02:
275 			return "Paper Jam: Loading Print Position On";
276 		case 0x03:
277 			return "Paper Jam: Loading Print Position Off";
278 		case 0x04:
279 			return "Paper Jam: Loading Print Position On";
280 		case 0x05:
281 			return "Paper Jam: Loading Leading Edge On";
282 		case 0x11:
283 			return "Paper Jam: Initializing Print Position Off";
284 		case 0x12:
285 			return "Paper Jam: Initializing Print Position On";
286 		case 0x13:
287 			return "Paper Jam: Initializing Leading Edge On";
288 		case 0x14:
289 			return "Paper Jam: Initializing Print Position On";
290 		case 0x15:
291 			return "Paper Jam: Initializing Print Position Off";
292 		case 0x16:
293 			return "Paper Jam: Initializing Print Position On";
294 		case 0x21:
295 			return "Paper Jam: Initializing Print Position On";
296 		case 0x22:
297 			return "Paper Jam: Rewinding Print Position On";
298 		case 0x40:
299 			return "Paper Jam: Pre-Printing Print Position Off";
300 		case 0x41:
301 			return "Paper Jam: Pre-Printing Print Position Off";
302 		case 0x42:
303 			return "Paper Jam: Printing Leading Edge Off";
304 		case 0x43:
305 			return "Paper Jam: After Returning Lead Edge Off";
306 		case 0x44:
307 			return "Paper Jam: After Printing Print Position Off";
308 		case 0x45:
309 			return "Paper Jam: After Printing Print Position On";
310 		case 0x46:
311 			return "Paper Jam: After Printing Print Position On";
312 		case 0x47:
313 			return "Paper Jam: After Printing Print Position Off";
314 		case 0x49:
315 			return "Paper Jam: Printing Lost Ribbon Mark";
316 		case 0x4A:
317 			return "Paper Jam: Printing Ribbon Cut";
318 		case 0x4D:
319 			return "Paper Jam: Printing Lost M Mark";
320 		case 0x4E:
321 			return "Paper Jam: Printing Lost C Mark";
322 		case 0x4F:
323 			return "Paper Jam: Printing Lost OP Mark";
324 		case 0x61:
325 			return "Paper Jam: Initializing Lead Edge On";
326 		case 0x62:
327 			return "Paper Jam: Initizlizing Print Position On";
328 		case 0x64:
329 			return "Paper Jam: Initizlizing Paper Size On";
330 		default:
331 			return "Paper Jam: Unknown";
332 		}
333 	case 0x06: /* User Error */
334 		switch (minor) {
335 		case 0x01:
336 			return "Front Cover Open";
337 		case 0x02:
338 			return "Incorrect Ribbon";
339 		case 0x03:
340 			return "No Ribbon";
341 		case 0x04:
342 			return "Mismatched Ribbon";
343 		case 0x05:
344 			return "Mismatched Paper";
345 		case 0x06:
346 			return "Paper Empty";
347 		case 0x08:
348 			return "No Paper";
349 		case 0x09:
350 			return "Take Out Paper";
351 		case 0x0A:
352 			return "Cover Open Error";
353 		case 0x0B:
354 			return "Thermal Head Damaged";
355 		case 0x0C:
356 			return "Thermal Head Recovery";
357 		default:
358 			return "Unknown";
359 		}
360 	default:
361 		return "Unknown";
362 	}
363 }
364 
365 struct s2145_status_resp {
366 	struct sinfonia_status_hdr hdr;
367 	uint32_t count_lifetime;
368 	uint32_t count_maint;
369 	uint32_t count_paper;
370 	uint32_t count_cutter;
371 	uint32_t count_head;
372 	uint32_t count_ribbon_left;
373 	uint8_t  bank1_printid;
374 	uint8_t  bank2_printid;
375 	uint16_t bank1_remaining;
376 	uint16_t bank1_finished;
377 	uint16_t bank1_specified;
378 	uint8_t  bank1_status;
379 	uint16_t bank2_remaining;
380 	uint16_t bank2_finished;
381 	uint16_t bank2_specified;
382 	uint8_t  bank2_status;
383 	uint8_t  tonecurve_status;
384 } __attribute__((packed));
385 
386 struct s2145_readtone_resp {
387 	struct sinfonia_status_hdr hdr;
388 	uint16_t total_size;
389 } __attribute__((packed));
390 
391 struct s2145_mediainfo_resp {
392 	struct sinfonia_status_hdr hdr;
393 	uint8_t  count;
394 	struct sinfonia_mediainfo_item items[10];  /* Not all necessarily used */
395 } __attribute__((packed));
396 
397 struct s2145_modelname_resp {
398 	struct sinfonia_status_hdr hdr;
399 	uint8_t vendor[4];
400 	uint8_t product[4];
401 	uint8_t modelname[40];
402 } __attribute__((packed));
403 
404 struct s2145_getunique_resp {
405 	struct sinfonia_status_hdr hdr;
406 	uint8_t  data[24];  /* Not necessarily all used. */
407 } __attribute__((packed));
408 
409 /* Private data structure */
410 struct shinkos2145_ctx {
411 	struct sinfonia_usbdev dev;
412 
413 	uint8_t jobid;
414 
415 	struct s2145_mediainfo_resp media;
416 	struct marker marker;
417 	int media_code;
418 };
419 
get_status(struct shinkos2145_ctx * ctx)420 static int get_status(struct shinkos2145_ctx *ctx)
421 {
422 	struct sinfonia_cmd_hdr cmd;
423 	struct s2145_status_resp resp;
424 	int ret, num = 0;
425 
426 	cmd.cmd = cpu_to_le16(SINFONIA_CMD_GETSTATUS);
427 	cmd.len = cpu_to_le16(0);
428 
429 	if ((ret = sinfonia_docmd(&ctx->dev,
430 				(uint8_t*)&cmd, sizeof(cmd),
431 				(uint8_t*)&resp, sizeof(resp),
432 				&num)) < 0) {
433 		return ret;
434 	}
435 
436 	INFO("Printer Status:  0x%02x (%s)\n", resp.hdr.status,
437 	     sinfonia_status_str(resp.hdr.status));
438 	if (resp.hdr.status == ERROR_PRINTER) {
439 		if(resp.hdr.error == ERROR_NONE)
440 			resp.hdr.error = resp.hdr.status;
441 		INFO(" Error 0x%02x (%s) 0x%02x/0x%02x (%s)\n",
442 		     resp.hdr.error,
443 		     sinfonia_error_str(resp.hdr.error),
444 		     resp.hdr.printer_major,
445 		     resp.hdr.printer_minor, error_codes(resp.hdr.printer_major, resp.hdr.printer_minor));
446 	}
447 	if (le16_to_cpu(resp.hdr.payload_len) != (sizeof(struct s2145_status_resp) - sizeof(struct sinfonia_status_hdr)))
448 		return 0;
449 
450 	INFO(" Print Counts:\n");
451 	INFO("\tSince Paper Changed:\t%08u\n", le32_to_cpu(resp.count_paper));
452 	INFO("\tLifetime:\t\t%08u\n", le32_to_cpu(resp.count_lifetime));
453 	INFO("\tMaintenance:\t\t%08u\n", le32_to_cpu(resp.count_maint));
454 	INFO("\tPrint Head:\t\t%08u\n", le32_to_cpu(resp.count_head));
455 	INFO(" Cutter Actuations:\t%08u\n", le32_to_cpu(resp.count_cutter));
456 	INFO(" Ribbon Remaining:\t%08u\n", le32_to_cpu(resp.count_ribbon_left));
457 	INFO("Bank 1: 0x%02x (%s) Job %03u @ %03u/%03u (%03u remaining)\n",
458 	     resp.bank1_status, sinfonia_bank_statuses(resp.bank1_status),
459 	     resp.bank1_printid,
460 	     le16_to_cpu(resp.bank1_finished),
461 	     le16_to_cpu(resp.bank1_specified),
462 	     le16_to_cpu(resp.bank1_remaining));
463 
464 	INFO("Bank 2: 0x%02x (%s) Job %03u @ %03u/%03u (%03u remaining)\n",
465 	     resp.bank2_status, sinfonia_bank_statuses(resp.bank1_status),
466 	     resp.bank2_printid,
467 	     le16_to_cpu(resp.bank2_finished),
468 	     le16_to_cpu(resp.bank2_specified),
469 	     le16_to_cpu(resp.bank2_remaining));
470 
471 	INFO("Tonecurve Status: 0x%02x (%s)\n", resp.tonecurve_status, sinfonia_tonecurve_statuses(resp.tonecurve_status));
472 
473 	return 0;
474 }
475 
get_fwinfo(struct shinkos2145_ctx * ctx)476 static int get_fwinfo(struct shinkos2145_ctx *ctx)
477 {
478 	struct sinfonia_fwinfo_cmd  cmd;
479 	struct sinfonia_fwinfo_resp resp;
480 	int num = 0;
481 	int i;
482 
483 	cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_FWINFO);
484 	cmd.hdr.len = cpu_to_le16(1);
485 
486 	INFO("FW Information:\n");
487 
488 	for (i = FWINFO_TARGET_MAIN_BOOT ; i <= FWINFO_TARGET_TABLES ; i++) {
489 		int ret;
490 		cmd.target = i;
491 
492 		if ((ret = sinfonia_docmd(&ctx->dev,
493 					(uint8_t*)&cmd, sizeof(cmd),
494 					(uint8_t*)&resp, sizeof(resp),
495 					&num)) < 0) {
496 			continue;
497 		}
498 
499 		if (le16_to_cpu(resp.hdr.payload_len) != (sizeof(struct sinfonia_fwinfo_resp) - sizeof(struct sinfonia_status_hdr)))
500 			continue;
501 
502 		INFO(" %s\t ver %02x.%02x\n", fwinfo_targets(i),
503 		     resp.major, resp.minor);
504 #if 0
505 		INFO("  name:    '%s'\n", resp.name);
506 		INFO("  type:    '%s'\n", resp.type);
507 		INFO("  date:    '%s'\n", resp.date);
508 		INFO("  version: %02x.%02x (CRC %04x)\n", resp.major, resp.minor,
509 		     le16_to_cpu(resp.checksum));
510 #endif
511 	}
512 	return 0;
513 }
514 
dump_mediainfo(struct s2145_mediainfo_resp * resp)515 static void dump_mediainfo(struct s2145_mediainfo_resp *resp)
516 {
517 	int i;
518 
519 	INFO("Supported Media Information: %u entries:\n", resp->count);
520 	for (i = 0 ; i < resp->count ; i++) {
521 		INFO(" %02d: C 0x%02x (%s), %04ux%04u, P 0x%02x (%s)\n", i,
522 		     resp->items[i].code,
523 		     sinfonia_print_codes(resp->items[i].code, 0),
524 		     resp->items[i].columns,
525 		     resp->items[i].rows,
526 		     resp->items[i].method,
527 		     sinfonia_print_methods(resp->items[i].method));
528 	}
529 }
530 
get_user_string(struct shinkos2145_ctx * ctx)531 static int get_user_string(struct shinkos2145_ctx *ctx)
532 {
533 	struct sinfonia_cmd_hdr cmd;
534 	struct s2145_getunique_resp resp;
535 	int ret, num = 0;
536 
537 	cmd.cmd = cpu_to_le16(SINFONIA_CMD_GETUNIQUE);
538 	cmd.len = cpu_to_le16(0);
539 
540 	if ((ret = sinfonia_docmd(&ctx->dev,
541 				(uint8_t*)&cmd, sizeof(cmd),
542 				(uint8_t*)&resp, sizeof(resp),
543 				&num)) < 0) {
544 		return ret;
545 	}
546 
547 	/* Null-terminate */
548 	resp.hdr.payload_len = le16_to_cpu(resp.hdr.payload_len);
549 	if (resp.hdr.payload_len > 23)
550 		resp.hdr.payload_len = 23;
551 	resp.data[resp.hdr.payload_len] = 0;
552 	INFO("Unique String: '%s'\n", resp.data);
553 	return 0;
554 }
555 
set_user_string(struct shinkos2145_ctx * ctx,char * str)556 static int set_user_string(struct shinkos2145_ctx *ctx, char *str)
557 {
558 	struct s2145_setunique_cmd cmd;
559 	struct sinfonia_status_hdr resp;
560 	int ret, num = 0;
561 
562 	if (str) {
563 		cmd.len = strlen(str);
564 		if (cmd.len > 23)
565 			cmd.len = 23;
566 		memset(cmd.data, 0, sizeof(cmd.data));
567 		strncpy((char*)cmd.data, str, cmd.len);
568 	} else {
569 		cmd.len = 0;
570 	}
571 
572 	cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_SETUNIQUE);
573 	cmd.hdr.len = cpu_to_le16(cmd.len + 1);
574 
575 	if ((ret = sinfonia_docmd(&ctx->dev,
576 				(uint8_t*)&cmd, cmd.len + 1 + sizeof(cmd.hdr),
577 				(uint8_t*)&resp, sizeof(resp),
578 				&num)) < 0) {
579 		return ret;
580 	}
581 
582 	return 0;
583 }
584 
reset_curve(struct shinkos2145_ctx * ctx,int target)585 static int reset_curve(struct shinkos2145_ctx *ctx, int target)
586 {
587 	struct s2145_reset_cmd cmd;
588 	struct sinfonia_status_hdr resp;
589 
590 	int ret, num = 0;
591 
592 	cmd.target = target;
593 
594 	cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_RESET);
595 	cmd.hdr.len = cpu_to_le16(1);
596 
597 	if ((ret = sinfonia_docmd(&ctx->dev,
598 				(uint8_t*)&cmd, sizeof(cmd),
599 				(uint8_t*)&resp, sizeof(resp),
600 				&num)) < 0) {
601 		return ret;
602 	}
603 
604 	return 0;
605 }
606 
button_set(struct shinkos2145_ctx * ctx,int enable)607 static int button_set(struct shinkos2145_ctx *ctx, int enable)
608 {
609 	struct s2145_button_cmd cmd;
610 	struct sinfonia_status_hdr resp;
611 	int ret, num = 0;
612 
613 	cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_BUTTON);
614 	cmd.hdr.len = cpu_to_le16(1);
615 
616 	cmd.enabled = enable;
617 
618 	if ((ret = sinfonia_docmd(&ctx->dev,
619 				(uint8_t*)&cmd, sizeof(cmd),
620 				(uint8_t*)&cmd, sizeof(resp),
621 				&num)) < 0) {
622 		return ret;
623 	}
624 
625 	return 0;
626 }
627 
get_tonecurve(struct shinkos2145_ctx * ctx,int type,char * fname)628 static int get_tonecurve(struct shinkos2145_ctx *ctx, int type, char *fname)
629 {
630 	struct s2145_readtone_cmd  cmd;
631 	struct s2145_readtone_resp resp;
632 	int ret, num = 0;
633 
634 	uint8_t *data;
635 	uint16_t curves[TONE_CURVE_SIZE]  = { 0 } ;
636 
637 	int i,j;
638 
639 	cmd.curveid = type;
640 
641 	cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_READTONE);
642 	cmd.hdr.len = cpu_to_le16(1);
643 
644 	INFO("Dump %s Tone Curve to '%s'\n", sinfonia_tonecurve_statuses(type), fname);
645 
646 	if ((ret = sinfonia_docmd(&ctx->dev,
647 				(uint8_t*)&cmd, sizeof(cmd),
648 				(uint8_t*)&resp, sizeof(resp),
649 				&num)) < 0) {
650 		return ret;
651 	}
652 
653 	resp.total_size = le16_to_cpu(resp.total_size);
654 
655 	data = malloc(resp.total_size * 2);
656 	if (!data) {
657 		ERROR("Memory allocation failure! (%d bytes)\n",
658 		      resp.total_size * 2);
659 		return -1;
660 	}
661 
662 	i = 0;
663 	while (i < resp.total_size) {
664 		ret = read_data(ctx->dev.dev, ctx->dev.endp_up,
665 				data + i,
666 				resp.total_size * 2 - i,
667 				&num);
668 		if (ret < 0)
669 			goto done;
670 		i += num;
671 	}
672 
673 	i = j = 0;
674 	while (i < resp.total_size) {
675 		memcpy(curves + j, data + i+2, data[i+1]);
676 		j += data[i+1] / 2;
677 		i += data[i+1] + 2;
678 	}
679 
680 	/* Open file and write it out */
681 	{
682 		int tc_fd = open(fname, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
683 		if (tc_fd < 0) {
684 			ret = -1;
685 			goto done;
686 		}
687 
688 		for (i = 0 ; i < TONE_CURVE_SIZE; i++) {
689 			/* Byteswap appropriately */
690 			curves[i] = cpu_to_be16(le16_to_cpu(curves[i]));
691 		}
692 		ret = write(tc_fd, curves, TONE_CURVE_SIZE * sizeof(uint16_t));
693 		if (ret < 0)
694 			ERROR("Can't write curve file\n");
695 		else
696 			ret = 0;
697 
698 		close(tc_fd);
699 
700 	}
701 
702 done:
703 	free(data);
704 	return ret;
705 }
706 
set_tonecurve(struct shinkos2145_ctx * ctx,int target,char * fname)707 static int set_tonecurve(struct shinkos2145_ctx *ctx, int target, char *fname)
708 {
709 	struct s2145_update_cmd cmd;
710 	struct sinfonia_status_hdr resp;
711 	int ret, num = 0;
712 
713 	INFO("Set %s Tone Curve from '%s'\n", sinfonia_update_targets(target), fname);
714 
715 	uint16_t *data = malloc(TONE_CURVE_SIZE * sizeof(uint16_t));
716 
717 	if (!data) {
718 		ERROR("Memory allocation failure! (%d bytes)\n",
719 		      TONE_CURVE_SIZE);
720 		return -1;
721 	}
722 
723 	/* Read in file */
724 	if ((ret = dyesub_read_file(fname, data, TONE_CURVE_SIZE, NULL))) {
725 		ERROR("Failed to read Tone Curve file\n");
726 		goto done;
727 	}
728 
729 	/* Byteswap data to local CPU.. */
730 	for (ret = 0; ret < TONE_CURVE_SIZE ; ret++) {
731 		data[ret] = be16_to_cpu(data[ret]);
732 	}
733 
734 	/* Set up command */
735 	cmd.target = target;
736 	cmd.reserved = 0;
737 	cmd.size = cpu_to_le32(TONE_CURVE_SIZE * sizeof(uint16_t));
738 
739 	cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_UPDATE);
740 	cmd.hdr.len = cpu_to_le16(sizeof(struct s2145_update_cmd)-sizeof(cmd.hdr));
741 
742 	/* Byteswap data to format printer is expecting.. */
743 	for (ret = 0; ret < TONE_CURVE_SIZE ; ret++) {
744 		data[ret] = cpu_to_le16(data[ret]);
745 	}
746 
747 	if ((ret = sinfonia_docmd(&ctx->dev,
748 				(uint8_t*)&cmd, sizeof(cmd),
749 				(uint8_t*)&resp, sizeof(resp),
750 				&num)) < 0) {
751 		goto done;
752 	}
753 
754 	/* Sent transfer */
755 	if ((ret = send_data(ctx->dev.dev, ctx->dev.endp_down,
756 			     (uint8_t *) data, TONE_CURVE_SIZE * sizeof(uint16_t)))) {
757 		goto done;
758 	}
759 
760 done:
761 	free(data);
762 
763 	return ret;
764 }
765 
shinkos2145_cmdline(void)766 static void shinkos2145_cmdline(void)
767 {
768 	DEBUG("\t\t[ -b 0|1 ]       # Disable/Enable control panel\n");
769 	DEBUG("\t\t[ -c filename ]  # Get user/NV tone curve\n");
770 	DEBUG("\t\t[ -C filename ]  # Set user/NV tone curve\n");
771 	DEBUG("\t\t[ -e ]           # Query error log\n");
772 	DEBUG("\t\t[ -F ]           # Flash Printer LED\n");
773 	DEBUG("\t\t[ -i ]           # Query printer info\n");
774 	DEBUG("\t\t[ -l filename ]  # Get current tone curve\n");
775 	DEBUG("\t\t[ -L filename ]  # Set current tone curve\n");
776 	DEBUG("\t\t[ -m ]           # Query media\n");
777 	DEBUG("\t\t[ -r ]           # Reset user/NV tone curve\n");
778 	DEBUG("\t\t[ -R ]           # Reset printer to factory defaults\n");
779 	DEBUG("\t\t[ -s ]           # Query status\n");
780 	DEBUG("\t\t[ -u ]           # Query user string\n");
781 	DEBUG("\t\t[ -U sometext ]  # Set user string\n");
782 	DEBUG("\t\t[ -X jobid ]     # Abort a printjob\n");
783 }
784 
shinkos2145_cmdline_arg(void * vctx,int argc,char ** argv)785 int shinkos2145_cmdline_arg(void *vctx, int argc, char **argv)
786 {
787 	struct shinkos2145_ctx *ctx = vctx;
788 	int i, j = 0;
789 
790 	if (!ctx)
791 		return -1;
792 
793 	while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "b:c:C:eFil:L:mr:R:suU:X:")) >= 0) {
794 		switch(i) {
795 		GETOPT_PROCESS_GLOBAL
796 		case 'b':
797 			if (optarg[0] == '1')
798 				j = button_set(ctx, BUTTON_ENABLED);
799 			else if (optarg[0] == '0')
800 				j = button_set(ctx, BUTTON_DISABLED);
801 			else
802 				return -1;
803 			break;
804 		case 'c':
805 			j = get_tonecurve(ctx, TONECURVE_USER, optarg);
806 			break;
807 		case 'C':
808 			j = set_tonecurve(ctx, TONECURVE_USER, optarg);
809 			break;
810 		case 'e':
811 			j = sinfonia_geterrorlog(&ctx->dev);
812 			break;
813 		case 'F':
814 			j = sinfonia_flashled(&ctx->dev);
815 			break;
816 		case 'i':
817 			j = get_fwinfo(ctx);
818 			break;
819 		case 'l':
820 			j = get_tonecurve(ctx, TONECURVE_CURRENT, optarg);
821 			break;
822 		case 'L':
823 			j = set_tonecurve(ctx, TONECURVE_CURRENT, optarg);
824 			break;
825 		case 'm':
826 			dump_mediainfo(&ctx->media);
827 			break;
828 		case 'r':
829 			j = reset_curve(ctx, RESET_USER_CURVE);
830 			break;
831 		case 'R':
832 			j = reset_curve(ctx, RESET_PRINTER);
833 			break;
834 		case 's':
835 			j = get_status(ctx);
836 			break;
837 		case 'u':
838 			j = get_user_string(ctx);
839 			break;
840 		case 'U':
841 			j = set_user_string(ctx, optarg);
842 			break;
843 		case 'X':
844 			j = sinfonia_canceljob(&ctx->dev, atoi(optarg));
845 			break;
846 		default:
847 			break;  /* Ignore completely */
848 		}
849 
850 		if (j) return j;
851 	}
852 
853 	return 0;
854 }
855 
shinkos2145_init(void)856 static void *shinkos2145_init(void)
857 {
858 	struct shinkos2145_ctx *ctx = malloc(sizeof(struct shinkos2145_ctx));
859 	if (!ctx) {
860 		ERROR("Memory allocation failure! (%d bytes)\n",
861 		      (int)sizeof(struct shinkos2145_ctx));
862 
863 		return NULL;
864 	}
865 	memset(ctx, 0, sizeof(struct shinkos2145_ctx));
866 
867 	return ctx;
868 }
869 
shinkos2145_attach(void * vctx,struct libusb_device_handle * dev,int type,uint8_t endp_up,uint8_t endp_down,uint8_t jobid)870 static int shinkos2145_attach(void *vctx, struct libusb_device_handle *dev, int type,
871 			      uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
872 {
873 	struct shinkos2145_ctx *ctx = vctx;
874 
875 	ctx->dev.dev = dev;
876 	ctx->dev.endp_up = endp_up;
877 	ctx->dev.endp_down = endp_down;
878 	ctx->dev.type = type;
879 	ctx->dev.error_codes = &error_codes;
880 
881 	/* Ensure jobid is sane */
882 	ctx->jobid = (jobid & 0x7f);
883 	if (!ctx->jobid)
884 		ctx->jobid++;
885 
886 	int media_prints = 65536;
887 	if (test_mode < TEST_MODE_NOATTACH) {
888 		/* Query Media */
889 		struct sinfonia_cmd_hdr cmd;
890 		int num = 0;
891 		int i;
892 
893 		cmd.cmd = cpu_to_le16(SINFONIA_CMD_MEDIAINFO);
894 		cmd.len = cpu_to_le16(0);
895 
896 		if (sinfonia_docmd(&ctx->dev,
897 				 (uint8_t*)&cmd, sizeof(cmd),
898 				 (uint8_t*)&ctx->media, sizeof(ctx->media),
899 				 &num)) {
900 			return CUPS_BACKEND_FAILED;
901 		}
902 
903 		/* Byteswap media descriptor.. */
904 		for (i = 0 ; i < ctx->media.count ; i++) {
905 			ctx->media.items[i].columns = le16_to_cpu(ctx->media.items[i].columns);
906 			ctx->media.items[i].rows = le16_to_cpu(ctx->media.items[i].rows);
907 		}
908 
909 		/* Figure out the media type... */
910 		for (i = 0 ; i < ctx->media.count ; i++) {
911 			if (print_counts(ctx->media.items[i].code) < media_prints) {
912 				media_prints = print_counts(ctx->media.items[i].code);
913 				ctx->media_code = ctx->media.items[i].code;
914 			}
915 		}
916 	} else {
917 		int media_code = CODE_6x9;
918 		if (getenv("MEDIA_CODE"))
919 			media_code = atoi(getenv("MEDIA_CODE"));
920 
921 		media_prints = 680;
922 		ctx->media_code = media_code;
923 	}
924 
925 	ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
926 	ctx->marker.name = sinfonia_print_codes(ctx->media_code, 0);
927 	ctx->marker.levelmax = media_prints;
928 	ctx->marker.levelnow = -2;
929 
930 	return CUPS_BACKEND_OK;
931 }
932 
shinkos2145_read_parse(void * vctx,const void ** vjob,int data_fd,int copies)933 static int shinkos2145_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
934 	struct shinkos2145_ctx *ctx = vctx;
935 	struct sinfonia_printjob *job = NULL;
936 	int ret;
937 
938 	if (!ctx)
939 		return CUPS_BACKEND_FAILED;
940 
941 	job = malloc(sizeof(*job));
942 	if (!job) {
943 		ERROR("Memory allocation failure!\n");
944 		return CUPS_BACKEND_RETRY_CURRENT;
945 	}
946 	memset(job, 0, sizeof(*job));
947 
948 	/* Common read/parse code */
949 	ret = sinfonia_read_parse(data_fd, 2145, job);
950 	if (ret) {
951 		free(job);
952 		return ret;
953 	}
954 
955 	if (job->jp.copies > 1)
956 		job->copies = job->jp.copies;
957 	else
958 		job->copies = copies;
959 
960 	*vjob = job;
961 
962 	return CUPS_BACKEND_OK;
963 }
964 
shinkos2145_main_loop(void * vctx,const void * vjob)965 static int shinkos2145_main_loop(void *vctx, const void *vjob) {
966 	struct shinkos2145_ctx *ctx = vctx;
967 
968 	int ret, num;
969 
970 	int i, last_state = -1, state = S_IDLE;
971 
972 	struct sinfonia_printjob *job = (struct sinfonia_printjob*) vjob;
973 	struct sinfonia_cmd_hdr cmd;
974 	struct s2145_status_resp sts, sts2;
975 
976 	/* Validate print sizes */
977 	for (i = 0; i < ctx->media.count ; i++) {
978 		/* Look for matching media */
979 		if (ctx->media.items[i].columns == job->jp.columns &&
980 		    ctx->media.items[i].rows == job->jp.rows &&
981 		    ctx->media.items[i].method == job->jp.method)
982 			break;
983 	}
984 	if (i == ctx->media.count) {
985 		ERROR("Incorrect media loaded for print!\n");
986 		return CUPS_BACKEND_HOLD;
987 	}
988 
989 	// XXX check copies against remaining media!
990 
991 top:
992 	if (state != last_state) {
993 		if (dyesub_debug)
994 			DEBUG("last_state %d new %d\n", last_state, state);
995 	}
996 
997 	/* Send Status Query */
998 	cmd.cmd = cpu_to_le16(SINFONIA_CMD_GETSTATUS);
999 	cmd.len = cpu_to_le16(0);
1000 
1001 	if ((ret = sinfonia_docmd(&ctx->dev,
1002 				(uint8_t*)&cmd, sizeof(cmd),
1003 				(uint8_t*)&sts, sizeof(sts),
1004 				&num)) < 0) {
1005 		return CUPS_BACKEND_FAILED;
1006 	}
1007 
1008 	if (memcmp(&sts, &sts2, sizeof(sts))) {
1009 		memcpy(&sts2, &sts, sizeof(sts));
1010 
1011 		INFO("Printer Status: 0x%02x (%s)\n",
1012 		     sts.hdr.status, sinfonia_status_str(sts.hdr.status));
1013 
1014 		if (ctx->marker.levelnow != (int)sts.count_ribbon_left) {
1015 			ctx->marker.levelnow = sts.count_ribbon_left;
1016 			dump_markers(&ctx->marker, 1, 0);
1017 		}
1018 
1019 		if (sts.hdr.result != RESULT_SUCCESS)
1020 			goto printer_error;
1021 		if (sts.hdr.error == ERROR_PRINTER)
1022 			goto printer_error;
1023 	} else if (state == last_state) {
1024 		sleep(1);
1025 		goto top;
1026 	}
1027 	last_state = state;
1028 
1029 	fflush(stderr);
1030 
1031 	switch (state) {
1032 	case S_IDLE:
1033 		INFO("Waiting for printer idle\n");
1034 
1035 		/* make sure we're not colliding with an existing
1036 		   jobid */
1037 		while (ctx->jobid == sts.bank1_printid ||
1038 		       ctx->jobid == sts.bank2_printid) {
1039 			ctx->jobid++;
1040 			ctx->jobid &= 0x7f;
1041 			if (!ctx->jobid)
1042 				ctx->jobid++;
1043 		}
1044 
1045 		/* If either bank is free, continue */
1046 		if (sts.bank1_status == BANK_STATUS_FREE ||
1047 		    sts.bank2_status == BANK_STATUS_FREE)
1048 			state = S_PRINTER_READY_CMD;
1049 
1050 		break;
1051 	case S_PRINTER_READY_CMD: {
1052 		struct sinfonia_printcmd10_hdr print;
1053 
1054 		INFO("Sending print job (internal id %u)\n", ctx->jobid);
1055 
1056 		memset(&print, 0, sizeof(print));
1057 		print.hdr.cmd = cpu_to_le16(SINFONIA_CMD_PRINTJOB);
1058 		print.hdr.len = cpu_to_le16(sizeof(print) - sizeof(print.hdr));
1059 
1060 		print.jobid = ctx->jobid;
1061 		print.copies = cpu_to_le16(job->copies);
1062 		print.columns = cpu_to_le16(job->jp.columns);
1063 		print.rows = cpu_to_le16(job->jp.rows);
1064 		print.media = job->jp.media;
1065 		print.oc_mode = job->jp.oc_mode;
1066 		print.method = job->jp.method;
1067 
1068 		if ((ret = sinfonia_docmd(&ctx->dev,
1069 					(uint8_t*)&print, sizeof(print),
1070 					(uint8_t*)&sts, sizeof(sts),
1071 					&num)) < 0) {
1072 			return ret;
1073 		}
1074 
1075 		if (sts.hdr.result != RESULT_SUCCESS) {
1076 			if (sts.hdr.error == ERROR_BUFFER_FULL) {
1077 				INFO("Printer Buffers full, retrying\n");
1078 				break;
1079 			} else if ((sts.hdr.status & 0xf0) == 0x30 || sts.hdr.status == 0x21) {
1080 				INFO("Printer busy (%s), retrying\n", sinfonia_status_str(sts.hdr.status));
1081 				break;
1082 			} else if (sts.hdr.status != ERROR_NONE)
1083 				goto printer_error;
1084 		}
1085 
1086 		INFO("Sending image data to printer\n");
1087 		if ((ret = send_data(ctx->dev.dev, ctx->dev.endp_down,
1088 				     job->databuf, job->datalen)))
1089 			return CUPS_BACKEND_FAILED;
1090 
1091 		INFO("Waiting for printer to acknowledge completion\n");
1092 		sleep(1);
1093 		state = S_PRINTER_SENT_DATA;
1094 		break;
1095 	}
1096 	case S_PRINTER_SENT_DATA:
1097 		if (fast_return) {
1098 			INFO("Fast return mode enabled.\n");
1099 			state = S_FINISHED;
1100 		} else if (sts.hdr.status == STATUS_READY ||
1101 			   sts.hdr.status == STATUS_FINISHED ||
1102 			   sts.hdr.status == ERROR_PRINTER) {
1103 			state = S_FINISHED;
1104 		}
1105 		break;
1106 	default:
1107 		break;
1108 	};
1109 
1110 	if (state != S_FINISHED)
1111 		goto top;
1112 
1113 	if (sts.hdr.status == ERROR_PRINTER) {
1114 		if(sts.hdr.error == ERROR_NONE)
1115 			sts.hdr.error = sts.hdr.status;
1116 		INFO(" Error 0x%02x (%s) 0x%02x/0x%02x (%s)\n",
1117 		     sts.hdr.error,
1118 		     sinfonia_error_str(sts.hdr.error),
1119 		     sts.hdr.printer_major,
1120 		     sts.hdr.printer_minor, error_codes(sts.hdr.printer_major, sts.hdr.printer_minor));
1121 	}
1122 
1123 	INFO("Print complete\n");
1124 
1125 	return CUPS_BACKEND_OK;
1126 
1127 printer_error:
1128 	ERROR("Printer reported error: %#x (%s) status: %#x (%s) -> %#x.%#x (%s)\n",
1129 	      sts.hdr.error,
1130 	      sinfonia_error_str(sts.hdr.error),
1131 	      sts.hdr.status,
1132 	      sinfonia_status_str(sts.hdr.status),
1133 	      sts.hdr.printer_major, sts.hdr.printer_minor,
1134 	      error_codes(sts.hdr.printer_major, sts.hdr.printer_minor));
1135 	return CUPS_BACKEND_FAILED;
1136 }
1137 
shinkos2145_query_serno(struct libusb_device_handle * dev,uint8_t endp_up,uint8_t endp_down,char * buf,int buf_len)1138 static int shinkos2145_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len)
1139 {
1140 	struct sinfonia_cmd_hdr cmd;
1141 	struct s2145_getunique_resp resp;
1142 	int ret, num = 0;
1143 
1144 	struct sinfonia_usbdev sdev = {
1145 		.dev = dev,
1146 		.endp_up = endp_up,
1147 		.endp_down = endp_down,
1148 	};
1149 
1150 	cmd.cmd = cpu_to_le16(SINFONIA_CMD_GETUNIQUE);
1151 	cmd.len = cpu_to_le16(0);
1152 
1153 	if ((ret = sinfonia_docmd(&sdev,
1154 				  (uint8_t*)&cmd, sizeof(cmd),
1155 				  (uint8_t*)&resp, sizeof(resp),
1156 				  &num)) < 0) {
1157 		return ret;
1158 	}
1159 
1160 	/* Copy and Null-terminate */
1161 	num = (buf_len > (int)sizeof(resp.data)) ? (int)sizeof(resp.data) : (buf_len - 1);
1162 	memcpy(buf, resp.data, num);
1163 	buf[num] = 0;
1164 
1165 	return CUPS_BACKEND_OK;
1166 }
1167 
shinkos2145_query_markers(void * vctx,struct marker ** markers,int * count)1168 static int shinkos2145_query_markers(void *vctx, struct marker **markers, int *count)
1169 {
1170 	struct shinkos2145_ctx *ctx = vctx;
1171 	struct sinfonia_cmd_hdr cmd;
1172 	struct s2145_status_resp sts;
1173 	int num;
1174 
1175 	/* Query Status */
1176 	cmd.cmd = cpu_to_le16(SINFONIA_CMD_GETSTATUS);
1177 	cmd.len = cpu_to_le16(0);
1178 
1179 	if (sinfonia_docmd(&ctx->dev,
1180 			 (uint8_t*)&cmd, sizeof(cmd),
1181 			 (uint8_t*)&sts, sizeof(sts),
1182 			 &num)) {
1183 		return CUPS_BACKEND_FAILED;
1184 	}
1185 
1186 	ctx->marker.levelnow = ctx->marker.levelmax - le32_to_cpu(sts.count_ribbon_left);
1187 
1188 	*markers = &ctx->marker;
1189 	*count = 1;
1190 
1191 	return CUPS_BACKEND_OK;
1192 }
1193 
1194 /* Exported */
1195 #define USB_VID_SHINKO       0x10CE
1196 #define USB_PID_SHINKO_S2145 0x000E
1197 
1198 static const char *shinkos2145_prefixes[] = {
1199 	"shinko-chcs2145",
1200 	// extras
1201 	"sinfonia-chcs2145",
1202 	// Backwards compatibility
1203 	"shinkos2145",
1204 	NULL
1205 };
1206 
1207 struct dyesub_backend shinkos2145_backend = {
1208 	.name = "Shinko/Sinfonia CHC-S2145/S2",
1209 	.version = "0.61" " (lib " LIBSINFONIA_VER ")",
1210 	.uri_prefixes = shinkos2145_prefixes,
1211 	.cmdline_usage = shinkos2145_cmdline,
1212 	.cmdline_arg = shinkos2145_cmdline_arg,
1213 	.init = shinkos2145_init,
1214 	.attach = shinkos2145_attach,
1215 	.cleanup_job = sinfonia_cleanup_job,
1216 	.read_parse = shinkos2145_read_parse,
1217 	.main_loop = shinkos2145_main_loop,
1218 	.query_serno = shinkos2145_query_serno,
1219 	.query_markers = shinkos2145_query_markers,
1220 	.devices = {
1221 		{ USB_VID_SHINKO, USB_PID_SHINKO_S2145, P_SHINKO_S2145, NULL, "shinko-chc2145"},
1222 		{ 0, 0, 0, NULL, NULL}
1223 	}
1224 };
1225 
1226 /* CHC-S2145 data format
1227 
1228   Spool file consists of an 116-byte header, followed by RGB-packed data,
1229   followed by a 4-byte footer.  Header appears to consist of a series of
1230   4-byte Little Endian words.
1231 
1232    10 00 00 00 MM MM 00 00  00 00 00 00 01 00 00 00  MM == Model (ie 2145d)
1233    64 00 00 00 00 00 00 00  TT 00 00 00 00 00 00 00  TT == Media/Print Size
1234    MM 00 00 00 PP 00 00 00  00 00 00 00 00 00 00 00  MM = Print Method (aka cut control), PP = Print Mode
1235    00 00 00 00 WW WW 00 00  HH HH 00 00 XX 00 00 00  XX == Copies
1236    00 00 00 00 00 00 00 00  00 00 00 00 ce ff ff ff
1237    00 00 00 00 ce ff ff ff  QQ QQ 00 00 ce ff ff ff  QQ == DPI, ie 300.
1238    00 00 00 00 ce ff ff ff  00 00 00 00 00 00 00 00
1239    00 00 00 00
1240 
1241    [[Packed RGB payload of WW*HH*3 bytes]]
1242 
1243    04 03 02 01  [[ footer ]]
1244 
1245 */
1246