1 /*
2  *   Sony UP-D series Photo Printer CUPS backend -- libusb-1.0 version
3  *
4  *   (c) 2013-2019 Solomon Peachy <pizza@shaftnet.org>
5  *
6  *   The latest version of this program can be found at:
7  *
8  *     http://git.shaftnet.org/cgit/selphy_print.git
9  *
10  *   This program is free software; you can redistribute it and/or modify it
11  *   under the terms of the GNU General Public License as published by the Free
12  *   Software Foundation; either version 2 of the License, or (at your option)
13  *   any later version.
14  *
15  *   This program is distributed in the hope that it will be useful, but
16  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18  *   for more details.
19  *
20  *   You should have received a copy of the GNU General Public License
21  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  *
23  *          [http://www.gnu.org/licenses/gpl-2.0.html]
24  *
25  *   SPDX-License-Identifier: GPL-2.0+
26  *
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 
39 #define BACKEND sonyupd_backend
40 
41 #include "backend_common.h"
42 
43 /* Printer status
44   --> 1b e0 00 00 00 00 XX 00   [[ XX is 0xe on UPD895, 0xf on others ]]
45   <-- this struct
46 */
47 struct sony_updsts {
48 	uint8_t  len;      /* 0x0d/0x0e (ie number of bytes AFTER this one) */
49 	uint8_t  zero1;    /* 0x00 */
50 	uint8_t  printing; /* UPD_PRINTING_* */
51 	uint8_t  remain;   /* Number of remaining pages */
52 	uint8_t  zero2;
53 	uint8_t  sts1;     /* UPD_STS1_* */
54 	uint8_t  sts2;     /* seconday status */
55 	uint8_t  sts3;     /* tertiary status */
56 	uint16_t unk;      /* seen 0x04a0 UP-CR10L, 0x04a8 on UP-DR150 */
57 	uint16_t max_cols; /* BE */
58 	uint16_t max_rows; /* BE */
59 	uint8_t  percent;  /* 0-99, if job is printing (UP-D89x) */
60 } __attribute__((packed));
61 
62 #define UPD_PRINTING_BW    0xe0  /* UPD-895/897 only */
63 #define UPD_PRINTING_Y     0x40
64 #define UPD_PRINTING_M     0x80
65 #define UPD_PRINTING_C     0xc0
66 #define UPD_PRINTING_O     0x20
67 #define UPD_PRINTING_IDLE  0x00
68 
69 #define UPD_STS1_IDLE      0x00
70 #define UPD_STS1_DOOROPEN  0x08
71 #define UPD_STS1_NOPAPER   0x40
72 #define UPD_STS1_PRINTING  0x80
73 
74 /* Private data structures */
75 struct upd_printjob {
76 	uint8_t *databuf;
77 	int datalen;
78 
79 	int copies;
80 	uint16_t rows;
81 	uint16_t cols;
82 	uint32_t imglen;
83 };
84 
85 struct upd_ctx {
86 	struct libusb_device_handle *dev;
87 	uint8_t endp_up;
88 	uint8_t endp_down;
89 	int type;
90 
91 	int native_bpp;
92 
93 	struct sony_updsts stsbuf;
94 
95 	struct marker marker;
96 };
97 
98 /* Now for the code */
upd_init(void)99 static void* upd_init(void)
100 {
101 	struct upd_ctx *ctx = malloc(sizeof(struct upd_ctx));
102 	if (!ctx) {
103 		ERROR("Memory Allocation Failure!");
104 		return NULL;
105 	}
106 	memset(ctx, 0, sizeof(struct upd_ctx));
107 	return ctx;
108 }
109 
upd_attach(void * vctx,struct libusb_device_handle * dev,int type,uint8_t endp_up,uint8_t endp_down,uint8_t jobid)110 static int upd_attach(void *vctx, struct libusb_device_handle *dev, int type,
111 			  uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
112 {
113 	struct upd_ctx *ctx = vctx;
114 
115 	UNUSED(jobid);
116 
117 	ctx->dev = dev;
118 	ctx->endp_up = endp_up;
119 	ctx->endp_down = endp_down;
120 	ctx->type = type;
121 
122 	if (ctx->type == P_SONY_UPD895 || ctx->type == P_SONY_UPD897) {
123 		ctx->marker.color = "#000000";  /* Ie black! */
124 		ctx->native_bpp = 1;
125 	} else {
126 		ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
127 		ctx->native_bpp = 3;
128 	}
129 
130 	ctx->marker.name = "Unknown";
131 	ctx->marker.levelmax = -1;
132 	ctx->marker.levelnow = -2;
133 
134 	return CUPS_BACKEND_OK;
135 }
136 
upd_cleanup_job(const void * vjob)137 static void upd_cleanup_job(const void *vjob)
138 {
139 	const struct upd_printjob *job = vjob;
140 
141 	if (job->databuf)
142 		free(job->databuf);
143 
144 	free((void*)job);
145 }
146 
147 // UP-DR200
148 // 2UPC-R203  3.5x5  (770)
149 // 2UPC-R204  4x6    (700)
150 // 2UPC-R205  5x7    (400)
151 // 2UPC-R206  6x8    (350)
152 
153 // UP-DR150
154 // 2UPC-R153         (610)
155 // 2UPC-R154         (550)
156 // 2UPC-R155         (335)
157 // 2UPC-R156         (295)
158 
159 // print order:  ->YMCO->
160 // current prints (power on)
161 // total prints (lifetime)
162 // f/w version
163 
upd895_statuses(uint8_t code)164 static char* upd895_statuses(uint8_t code)
165 {
166 	switch (code) {
167 	case UPD_STS1_IDLE:
168 		return "Idle";
169 	case UPD_STS1_DOOROPEN:
170 		return "Door open";
171 	case UPD_STS1_NOPAPER:
172 		return "No paper";
173 	case UPD_STS1_PRINTING:
174 		return "Printing";
175 	default:
176 		return "Unknown";
177 	}
178 }
179 
sony_get_status(struct upd_ctx * ctx,struct sony_updsts * buf)180 static int sony_get_status(struct upd_ctx *ctx, struct sony_updsts *buf)
181 {
182 	int ret, num = 0;
183 	uint8_t query[7] = { 0x1b, 0xe0, 0, 0, 0, 0x0f, 0 };
184 
185 	if (ctx->type == P_SONY_UPD895)
186 		query[5] = 0x0e;
187 
188 	if ((ret = send_data(ctx->dev, ctx->endp_down,
189 			     query, sizeof(query))))
190 		return CUPS_BACKEND_FAILED;
191 
192 	ret = read_data(ctx->dev, ctx->endp_up, (uint8_t*) buf, sizeof(*buf),
193 			&num);
194 
195 	if (ret < 0)
196 		return CUPS_BACKEND_FAILED;
197 #if 0
198 	if (ctx->type == P_SONY_UPD895 && ret != 14)
199 		return CUPS_BACKEND_FAILED;
200 	else if (ret != 15)
201 		return CUPS_BACKEND_FAILED;
202 #endif
203 
204 	ctx->stsbuf.max_cols = be16_to_cpu(ctx->stsbuf.max_cols);
205 	ctx->stsbuf.max_rows = be16_to_cpu(ctx->stsbuf.max_rows);
206 
207 	return CUPS_BACKEND_OK;
208 }
209 
210 #define MAX_PRINTJOB_LEN (2048*2764*3 + 2048)
211 
upd_read_parse(void * vctx,const void ** vjob,int data_fd,int copies)212 static int upd_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
213 	struct upd_ctx *ctx = vctx;
214 	int len, run = 1;
215 	uint32_t copies_offset = 0;
216 	uint32_t param_offset = 0;
217 	uint32_t data_offset = 0;
218 
219 	struct upd_printjob *job = NULL;
220 
221 	if (!ctx)
222 		return CUPS_BACKEND_FAILED;
223 
224 	job = malloc(sizeof(*job));
225 	if (!job) {
226 		ERROR("Memory allocation failure!\n");
227 		return CUPS_BACKEND_RETRY_CURRENT;
228 	}
229 	memset(job, 0, sizeof(*job));
230 	job->copies = copies;
231 
232 	job->datalen = 0;
233 	job->databuf = malloc(MAX_PRINTJOB_LEN);
234 	if (!job->databuf) {
235 		ERROR("Memory allocation failure!\n");
236 		upd_cleanup_job(job);
237 		return CUPS_BACKEND_RETRY_CURRENT;
238 	}
239 
240 	while(run) {
241 		int i;
242 		int keep = 0;
243 		i = read(data_fd, job->databuf + job->datalen, 4);
244 		if (i < 0) {
245 			upd_cleanup_job(job);
246 			return CUPS_BACKEND_CANCEL;
247 		}
248 		if (i == 0)
249 			break;
250 
251 		memcpy(&len, job->databuf + job->datalen, sizeof(len));
252 		len = le32_to_cpu(len);
253 
254 		/* Filter out chunks we don't send to the printer */
255 		if (len & 0xf0000000) {
256 			switch (len) {
257 			case 0xfffffff3:
258 				if(dyesub_debug)
259 					DEBUG("Block ID '%08x' (len %d)\n", len, 0);
260 				len = 0;
261 				if (ctx->type == P_SONY_UPDR150)
262 					run = 0;
263 				break;
264 			case 0xfffffff7:
265 				if(dyesub_debug)
266 					DEBUG("Block ID '%08x' (len %d)\n", len, 0);
267 				len = 0;
268 				if (ctx->type == P_SONY_UPCR10)
269 					run = 0;
270 				break;
271 			case 0xfffffff8: // 895
272 			case 0xfffffff4: // 897
273 				if(dyesub_debug)
274 					DEBUG("Block ID '%08x' (len %d)\n", len, 0);
275 				len = 0;
276 				if (ctx->type == P_SONY_UPD895 || ctx->type == P_SONY_UPD897)
277 					run = 0;
278 				break;
279 			case 0xffffff97:
280 				if(dyesub_debug)
281 					DEBUG("Block ID '%08x' (len %d)\n", len, 12);
282 				len = 12;
283 				break;
284 			case 0xffffffef:
285 				if (ctx->type == P_SONY_UPD895 || ctx->type == P_SONY_UPD897) {
286 					if(dyesub_debug)
287 						DEBUG("Block ID '%08x' (len %d)\n", len, 0);
288 					len = 0;
289 					break;
290 				}
291 				/* Intentional Fallthrough */
292 			case 0xffffffeb:
293 			case 0xffffffee:
294 			case 0xfffffff5:
295 				if(dyesub_debug)
296 					DEBUG("Block ID '%08x' (len %d)\n", len, 4);
297 				len = 4;
298 				break;
299 			case 0xffffffec:
300 				if (ctx->type == P_SONY_UPD897) {
301 					if(dyesub_debug)
302 						DEBUG("Block ID '%08x' (len %d)\n", len, 4);
303 					len = 4;
304 					break;
305 				}
306 				/* Intentional Fallthrough */
307 			default:
308 				if(dyesub_debug)
309 					DEBUG("Block ID '%08x' (len %d)\n", len, 0);
310 				len = 0;
311 				break;
312 			}
313 		} else {
314 			/* Only keep these chunks */
315 			if(dyesub_debug)
316 				DEBUG("Data block (len %d)\n", len);
317 			if (len > 0)
318 				keep = 1;
319 		}
320 		if (keep)
321 			job->datalen += sizeof(uint32_t);
322 
323 		/* Make sure we're not too large */
324 		if (job->datalen + len > MAX_PRINTJOB_LEN) {
325 			ERROR("Buffer overflow when parsing printjob! (%d+%d)\n",
326 			      job->datalen, len);
327 			upd_cleanup_job(job);
328 			return CUPS_BACKEND_CANCEL;
329 		}
330 
331 		/* Read in the data chunk */
332 		while(len > 0) {
333 			i = read(data_fd, job->databuf + job->datalen, len);
334 			if (i < 0) {
335 				upd_cleanup_job(job);
336 				return CUPS_BACKEND_CANCEL;
337 			}
338 			if (i == 0)
339 				break;
340 
341 			/* Work out offset of copies command */
342 			if (job->databuf[job->datalen] == 0x1b) {
343 				int offset = 0;
344 				if (i == 7)
345 					offset = 4;
346 
347 				switch (job->databuf[job->datalen + 1]) {
348 				case 0x15: /* Print dimensions */
349 					param_offset = job->datalen + 16 + offset;
350 					break;
351 				case 0xee:
352 					copies_offset = job->datalen + 7 + offset;
353 					break;
354 				case 0xe1: /* Image dimensions */
355 					param_offset = job->datalen + 14 + offset;
356 					break;
357 				case 0xea:
358 					data_offset = job->datalen + 6 + offset;
359 					break;
360 				default:
361 					break;
362 				}
363 			}
364 
365 			if (keep)
366 				job->datalen += i;
367 			len -= i;
368 		}
369 	}
370 	if (!job->datalen) {
371 		upd_cleanup_job(job);
372 		return CUPS_BACKEND_CANCEL;
373 	}
374 
375 	/* Some models specify copies in the print job */
376 	if (copies_offset) {
377 		uint16_t tmp = copies;
378 		tmp = cpu_to_be16(copies);
379 		memcpy(job->databuf + copies_offset, &tmp, sizeof(tmp));
380 		job->copies = 1;
381 	}
382 
383 	/* Parse some other stuff */
384 	if (param_offset) {
385 		memcpy(&job->cols, job->databuf + param_offset, sizeof(uint16_t));
386 		memcpy(&job->rows, job->databuf + param_offset + 2, sizeof(uint16_t));
387 		job->cols = be16_to_cpu(job->cols);
388 		job->rows = be16_to_cpu(job->rows);
389 	}
390 	if (data_offset) {
391 		memcpy(&job->imglen, job->databuf + data_offset, sizeof(uint32_t));
392 		job->imglen = be32_to_cpu(job->imglen);
393 	}
394 
395 	/* Sanity check job parameters */
396 	if (job->imglen != (uint32_t)(job->rows * job->cols * ctx->native_bpp))
397 	{
398 		ERROR("Job data length mismatch (%u vs %d)!\n",
399 		      job->imglen, job->rows * job->cols * ctx->native_bpp);
400 		upd_cleanup_job(job);
401 		return CUPS_BACKEND_CANCEL;
402 	}
403 
404 	*vjob = job;
405 
406 	return CUPS_BACKEND_OK;
407 }
408 
upd_main_loop(void * vctx,const void * vjob)409 static int upd_main_loop(void *vctx, const void *vjob) {
410 	struct upd_ctx *ctx = vctx;
411 	int i, ret;
412 	int copies;
413 
414 	const struct upd_printjob *job = vjob;
415 
416 	if (!ctx)
417 		return CUPS_BACKEND_FAILED;
418 	if (!job)
419 		return CUPS_BACKEND_FAILED;
420 
421 	copies = job->copies;
422 
423 top:
424 	/* Send Unknown CMD.  Resets? */
425 	if (ctx->type == P_SONY_UPD897) {
426 		const uint8_t cmdbuf[7] = { 0x1b, 0x1f, 0, 0, 0, 0, 0 };
427 		ret = send_data(ctx->dev, ctx->endp_down,
428 				cmdbuf, sizeof(cmdbuf));
429 		if (ret)
430 			return CUPS_BACKEND_FAILED;
431 	}
432 
433 	/* Query printer status */
434 	ret = sony_get_status(ctx, &ctx->stsbuf);
435 
436 	if (ret)
437 		return CUPS_BACKEND_FAILED;
438 
439 	/* Sanity check job parameters */
440 	if (job->rows > ctx->stsbuf.max_rows ||
441 	    job->cols > ctx->stsbuf.max_cols) {
442 		ERROR("Job dimensions (%u/%u) exceed printer max (%u/%u)\n",
443 		      job->cols, job->rows,
444 		      ctx->stsbuf.max_cols,
445 		      ctx->stsbuf.max_rows);
446 		return CUPS_BACKEND_CANCEL;
447 	}
448 
449 	/* Check for idle */
450 	if (ctx->stsbuf.sts1 != 0x00) {
451 		if (ctx->stsbuf.sts1 == 0x80) {
452 			INFO("Waiting for printer idle...\n");
453 			sleep(1);
454 			goto top;
455 		}
456 	}
457 
458 	/* Send RESET */
459 	if (ctx->type != P_SONY_UPD895) {
460 		const uint8_t rstbuf[7] = { 0x1b, 0x16, 0, 0, 0, 0, 0 };
461 		ret = send_data(ctx->dev, ctx->endp_down,
462 				rstbuf, sizeof(rstbuf));
463 		if (ret)
464 			return CUPS_BACKEND_FAILED;
465 	}
466 
467 #if 0 /* Unknown query */
468 	if (ctx->type == P_SONY_UPD897) {
469 		// -> 1b e6 00 00 00 08 00
470 		// <- ???
471 	}
472 #endif
473 
474 	/* Send over job */
475 	i = 0;
476 	while (i < job->datalen) {
477 		uint32_t len;
478 		memcpy(&len, job->databuf + i, sizeof(len));
479 		len = le32_to_cpu(len);
480 
481 		i += sizeof(uint32_t);
482 
483 		if ((ret = send_data(ctx->dev, ctx->endp_down,
484 				     job->databuf + i, len)))
485 			return CUPS_BACKEND_FAILED;
486 
487 		i += len;
488 	}
489 
490 	// XXX generate and send copy cmd instead of using the offset.
491 	// 1b ee 00 00 00 02 00  NN NN  (BE)
492 
493 	/* Wait for completion! */
494 retry:
495 	sleep(1);
496 
497 	/* Check for idle */
498 	ret = sony_get_status(ctx, &ctx->stsbuf);
499 	if (ret)
500 		return ret;
501 
502 	switch (ctx->stsbuf.sts1) {
503 	case UPD_STS1_IDLE:
504 		goto done;
505 	case UPD_STS1_PRINTING:
506 		break;
507 	default:
508 		ERROR("Printer error: %s (%02x)\n", upd895_statuses(ctx->stsbuf.sts1),
509 		      ctx->stsbuf.sts1);
510 		return CUPS_BACKEND_STOP;
511 	}
512 
513 	if (fast_return && ctx->stsbuf.printing != UPD_PRINTING_IDLE) {
514 		INFO("Fast return mode enabled.\n");
515 	} else {
516 		goto retry;
517 	}
518 
519 	/* Clean up */
520 	if (terminate)
521 		copies = 1;
522 
523 done:
524 	INFO("Print complete (%d copies remaining)\n", copies - 1);
525 
526 	if (copies && --copies) {
527 		goto top;
528 	}
529 
530 	return CUPS_BACKEND_OK;
531 }
532 
upd895_dump_status(struct upd_ctx * ctx)533 static int upd895_dump_status(struct upd_ctx *ctx)
534 {
535 	int ret = sony_get_status(ctx, &ctx->stsbuf);
536 	if (ret < 0)
537 		return CUPS_BACKEND_FAILED;
538 
539 	INFO("Printer status: %s (%02x)\n", upd895_statuses(ctx->stsbuf.sts1), ctx->stsbuf.sts1);
540 	if (ctx->stsbuf.printing != UPD_PRINTING_IDLE &&
541 	    ctx->stsbuf.sts1 == UPD_STS1_PRINTING)
542 		INFO("Remaining copies: %d\n", ctx->stsbuf.remain);
543 
544 	return CUPS_BACKEND_OK;
545 }
546 
547 
upd_cmdline(void)548 static void upd_cmdline(void)
549 {
550 	DEBUG("\t\t[ -s ]           # Query printer status\n");
551 }
552 
upd_cmdline_arg(void * vctx,int argc,char ** argv)553 static int upd_cmdline_arg(void *vctx, int argc, char **argv)
554 {
555 	struct upd_ctx *ctx = vctx;
556 	int i, j = 0;
557 
558 	if (!ctx)
559 		return -1;
560 
561 	while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "s")) >= 0) {
562 		switch(i) {
563 		GETOPT_PROCESS_GLOBAL
564 		case 's':
565 			j = upd895_dump_status(ctx);
566 			break;
567 		}
568 
569 		if (j) return j;
570 	}
571 
572 	return 0;
573 }
574 
upd_query_markers(void * vctx,struct marker ** markers,int * count)575 static int upd_query_markers(void *vctx, struct marker **markers, int *count)
576 {
577 	struct upd_ctx *ctx = vctx;
578 	int ret = sony_get_status(ctx, &ctx->stsbuf);
579 
580 	*markers = &ctx->marker;
581 	*count = 1;
582 
583 	if (ret)
584 		return CUPS_BACKEND_FAILED;
585 
586 	if (ctx->stsbuf.sts1 == 0x40 ||
587 	    ctx->stsbuf.sts1 == 0x08) {
588 		ctx->marker.levelnow = 0;
589 		STATE("+media-empty\n");
590 	} else {
591 		ctx->marker.levelnow = -3;
592 		STATE("-media-empty\n");
593 	}
594 
595 	return CUPS_BACKEND_OK;
596 }
597 
598 static const char *sonyupd_prefixes[] = {
599 	"sonyupd",
600 	"sony-updr150", "sony-updr200", "sony-upcr10l",
601 	"sony-upd895", "sony-upd897", "dnp-sl10",
602 	// Backwards compatibility
603 	"sonyupdr150",
604 	"sonyupdr200", "sonyupcr10",
605 	NULL
606 };
607 
608 /* Exported */
609 #define USB_VID_SONY         0x054C
610 #define USB_PID_SONY_UPDR150 0x01E8
611 #define USB_PID_SONY_UPDR200 0x035F
612 #define USB_PID_SONY_UPCR10  0x0226
613 #define USB_PID_SONY_UPD895  0x0049
614 #define USB_PID_SONY_UPD897  0x01E7
615 
616 struct dyesub_backend sonyupd_backend = {
617 	.name = "Sony UP-D",
618 	.version = "0.37",
619 	.uri_prefixes = sonyupd_prefixes,
620 	.cmdline_arg = upd_cmdline_arg,
621 	.cmdline_usage = upd_cmdline,
622 	.init = upd_init,
623 	.attach = upd_attach,
624 	.cleanup_job = upd_cleanup_job,
625 	.read_parse = upd_read_parse,
626 	.main_loop = upd_main_loop,
627 	.query_markers = upd_query_markers,
628 	.devices = {
629 		{ USB_VID_SONY, USB_PID_SONY_UPDR150, P_SONY_UPDR150, NULL, "sony-updr150"},
630 		{ USB_VID_SONY, USB_PID_SONY_UPDR200, P_SONY_UPDR150, NULL, "sony-updr200"},
631 		{ USB_VID_SONY, USB_PID_SONY_UPCR10, P_SONY_UPCR10, NULL, "sony-upcr10l"},
632 		{ USB_VID_SONY, USB_PID_SONY_UPD895, P_SONY_UPD895, NULL, "sonyupd895"},
633 		{ USB_VID_SONY, USB_PID_SONY_UPD897, P_SONY_UPD897, NULL, "sony-upd897"},
634 		{ 0, 0, 0, NULL, NULL}
635 	}
636 };
637 
638 /* Sony spool file format
639 
640    The spool file is a series of 4-byte commands, followed by optional
641    arguments.  The purpose of the commands is unknown, but they presumably
642    instruct the driver to perform certain things.
643 
644    If you treat these 4 bytes as a 32-bit little-endian number, if any of the
645    least significant 4 bits are non-zero, the value is is to
646    be interpreted as a driver command.  If the most significant bits are
647    zero, the value signifies that the following N bytes of data should be
648    sent to the printer as-is.
649 
650    Known driver "commands":
651 
652     97 ff ff ff
653     eb ff ff ff  ?? 00 00 00
654     ec ff ff ff  ?? 00 00 00
655     ed ff ff ff  ?? 00 00 00
656     ee ff ff ff  ?? 00 00 00
657     ef ff ff ff  XX 00 00 00   # XX == print size (0x01/0x02/0x03/0x04)
658     ef ff ff ff                # On UP-D895/897
659     f3 ff ff ff
660     f4 ff ff ff                # End of job on UP-D897
661     f5 ff ff ff  YY 00 00 00   # YY == ??? (seen 0x01)
662     f7 ff ff ff                # End of job on UP-D895
663 
664    All printer commands start with 0x1b, and are at least 7 bytes long.
665    General Command format:
666 
667     1b XX ?? ?? ?? LL 00       # XX is cmd, LL is data or response length.
668 
669    UNKNOWN QUERY  [possibly media?]
670 
671  <- 1b 03 00 00 00 13 00
672  -> 70 00 00 00 00 00 00 0b  00 00 00 00 00 00 00 00
673     00 00 00
674 
675    UNKNOWN CMD (UP-DR & SL10 & UP-D895 & UP-DP10)  [ possibly START ]
676 
677  <- 1b 0a 00 00 00 00 00
678 
679    PRINT DIMENSIONS
680 
681  <- 1b 15 00 00 00 0d 00
682  <- 00 00 00 00 ZZ QQ QQ WW  WW YY YY XX XX
683 
684     QQ/WW/YY/XX are (origin_cols/origin_rows/cols/rows) in BE.
685     ZZ is 0x07 on UP-DR series, 0x01 on UP-D89x series.
686 
687    RESET
688 
689  <- 1b 16 00 00 00 00 00
690 
691    UNKNOWN CMD (UP-DR & SL & UP-D897, may be PRINT START?)
692 
693  <- 1b 17 00 00 00 00 00
694 
695    UNKNOWN CMD
696 
697  <- 1b 1f 00 00 00 00 00
698 
699    SET PARAM
700 
701  <- 1b c0 00 NN LL 00 00    # LL is response length, NN is number.
702  <- [ NN bytes]
703 
704    QUERY PARAM
705 
706  <- 1b c1 00 NN LL 00 00    # LL is response length, NN is number.
707  -> [ NN bytes ]
708 
709       PARAMS SEEN:
710     03, len 5    [ 02 03 00 01 XX ]                   (UPDR200, 00 = normal, 02 is multicut/div2 print, 01 seen at end of stream too..
711     02, len 06   [ 02 02 00 03 00 00 ]
712     01, len 10   [ 02 01 00 06 00 02 00 00 00 00 ]    (UP-D897)
713     00, len 5    [ 02 01 00 01 XX ]                   XX == Gamma table
714 
715    STATUS QUERY
716 
717  <- 1b e0 00 00 00 XX 00       # XX = 0xe (UP-D895), 0xf (All others)
718  -> [14 or 15 bytes, see 'struct sony_updsts' ]
719 
720    IMAGE DIMENSIONS & OVERCOAT
721 
722  <- 1b e1 00 00 00 0b 00
723  <- 00 ZZ QQ 00 00 00 00 XX XX YY YY  # XX = cols, YY == rows, ZZ == 0x04 on UP-DP10, otherwise 0x80. QQ == 00 glossy, 08 texture (UP-DP10 + UP-DR150), 0c matte, +0x10 for "nocorrection" on UP-DR200..
724 
725    UNKNOWN
726 
727  <- 1b e5 00 00 00 08 00
728  <- 00 00 00 00 00 00 00 XX  00  # Seen 01, 12, 0d, etc.
729 
730    UNKNOWN  (UP-D897)
731 
732  <- 1b e6 00 00 00 08 00
733  <- 07 00 00 00 00 00 00 00
734 
735    DATA TRANSFER
736 
737  <- 1b ea 00 00 00 00 ZZ ZZ ZZ ZZ 00  # ZZ is BIG ENDIAN
738  <- [ ZZ ZZ ZZ ZZ bytes of data ]
739 
740    UNKNOWN CMD (UP-DR and UP-D)
741 
742  <- 1b ed 00 00 00 00 00
743 
744    UNKNOWN (UPDR series)
745 
746  <- 1b ef 00 00 00 06 00
747  -> 05 00 00 00 00 22
748 
749    COPIES
750 
751  <- 1b ee 00 00 00 02 00
752  <- NN NN                        # Number of copies (BE, 1-???)
753 
754    UNKNOWN (UPDR series)
755 
756  <- 1b f5 00 00 00 02 00
757  <- ?? ??
758 
759   ************************************************************************
760 
761   The data stream sent to the printer consists of all the commands in the
762   spool file, plus a couple other ones that generate responses.  It is
763   unknown if those additional commands are necessary.  This is a typical
764   sequence:
765 
766 [[ Sniff start of a UP-DR150 ]]
767 
768 <- 1b e0 00 00 00 0f 00   [ STATUS QUERY ]
769 -> 0e 00 00 00 00 00 00 00  04 a8 08 00 0a a4 00
770                                   ----- -----
771                                   MAX_C MAX_R
772 <- 1b 16 00 00 00 00 00
773 -> "reset" ??
774 
775 [[ begin job ]]
776 
777 <- 1b ef 00 00 00 06 00
778 -> 05 00 00 00 00 22
779 
780 <- 1b e5 00 00 00 08 00       ** In spool file
781 <- 00 00 00 00 00 00 01 00
782 
783 <- 1b c1 00 02 06 00 00       [ Query Param 2, length 6 ]
784 -> 02 02 00 03 00 00
785 
786 <- 1b ee 00 00 00 02 00       ** In spool file
787 <- 00 01
788 
789 <- 1b 15 00 00 00 0d 00       ** In spool file
790 <- 00 00 00 00 07 00 00 00  00 08 00 0a a4
791 
792 <- 1b 03 00 00 00 13 00
793 -> 70 00 00 00 00 00 00 0b  00 00 00 00 00 00 00 00
794    00 00 00
795 
796 <- 1b e1 00 00 00 0b 00       ** In spool file
797 <- 00 80 00 00 00 00 00 08  00 0a a4
798 
799 <- 1b 03 00 00 00 13 00
800 -> 70 00 00 00 00 00 00 0b  00 00 00 00 00 00 00 00
801    00 00 00
802 
803 <- 1b ea 00 00 00 00 00 ff  60 00 00   ** In spool file
804 <- [[ 0x0060ff00 bytes of data ]]
805 
806 <- 1b e0 00 00 00 0f 00
807 -> 0e 00 00 00 00 00 00 00  04 a8 08 00 0a a4 00
808 
809 <- 1b 0a 00 00 00 00 00   ** In spool file
810 <- 1b 17 00 00 00 00 00   ** In spool file
811 
812 [[fin]]
813 
814  **************
815 
816   Sony UP-CL10 / DNP SL-10 spool format:
817 
818 60 ff ff ff f8 ff ff ff  fd ff ff ff
819 14 00 00 00
820 1b 15 00 00 00 0d 00 00  00 00 00 07 00 00 00 00  WW WW HH HH
821 fb ff ff ff f4 ff ff ff
822 0b 00 00 00
823 1b ea 00 00 00 00 SH SH  SH SH 00
824 SL SL SL SL
825 
826  [[ Data, rows * cols * 3 bytes ]]
827 
828 f3 ff ff ff
829 0f 00 00 00
830 1b e5 00 00 00 08 00 00  00 00 00 00 00 00 00
831 12 00 00 00
832 1b e1 00 00 00 0b 00 00  80 00 00 00 00 00 WW WW  HH HH
833 fa ff ff ff
834 09 00 00 00
835 1b ee 00 00 00 02 00 00  NN
836 07 00 00 00
837 1b 0a 00 00 00 00 00
838 f9 ff ff ff fc ff ff ff
839 07 00 00 00
840 1b 17 00 00 00 00 00
841 f7 ff ff ff
842 
843  WW WW == Columns, Big Endian
844  HH HH == Rows, Big Endian
845  SL SL SL SL == Plane size, Little Endian (Rows * Cols * 3)
846  SH SH SH SH == Plane size, Big Endian (Rows * Cols * 3)
847  NN == Copies
848 
849  **************
850 
851   Sony UP-D895 spool format:
852 
853  XX XX == cols, BE (fixed at 1280/0x500)
854  YY YY == rows, BE (798/0x031e,1038/0x040e,1475/0x05c3, 2484/09b4) @ 960/1280/1920/3840+4096
855  SS SS SS SS == data len (rows * cols, LE)
856  S' S' S' S' == data len (rows * cols, BE)
857  NN  == copies (1 -> ??)
858  GG GG == ???  0000/0050/011b/04aa/05aa at each resolution.
859  G' == Gamma  01 (soft), 03 (hard), 02 (normal)
860 
861  9c ff ff ff 97 ff ff ff  00 00 00 00 00 00 00 00  00 00 00 00 ff ff ff ff
862 
863  14 00 00 00
864  1b 15 00 00 00  0d 00  00 00 00 00 01 GG GG 00 00 YY YY XX XX
865  0b 00 00 00
866  1b ea 00 00 00  00 S' S' S' S' 00
867  SS SS SS SS
868  ...DATA... (rows * cols)
869  ff ff ff ff
870  09 00 00 00
871  1b ee 00 00 00  02 00  00 NN
872  0f 00 00 00
873  1b e5 00 00 00  08 00  00 00 00 00 00 00 00 00
874  0c 00 00 00
875  1b c0 00 00 00  05 00  02 00  00 01  G'
876  11 00 00 00
877  1b c0 00 01 00  0a 00  02 01  00 06  00 00 00 00 00 00
878  12 00 00 00
879  1b e1 00 00 00  0b 00  00 08 00 GG GG 00 00 YY YY XX XX
880  07 00 00 00
881  1b 0a 00 00 00  00 00
882  fd ff ff ff f7 ff ff ff  f8 ff ff ff
883 
884  **************
885 
886   Sony UP-D897 spool format:
887 
888  NN NN == copies  (00 for printer selected)
889  XX XX == cols (fixed @ 1280)
890  YY YY == rows
891  GG    == gamma -- Table 2 == 2, Table 1 == 3, Table 3 == 1, Table 4 == 4
892  DD    == "dark"  +- 64.
893  LL    == "light" +- 64.
894  AA    == "advanced" +- 32.
895  SS    == Sharpness 0-14
896  ZZ ZZ ZZ ZZ == Data length (BE)
897  Z` Z` Z` Z` == Data length (LE)
898 
899 
900  83 ff ff ff fc ff ff ff
901  fb ff ff ff f5 ff ff ff
902  f1 ff ff ff f0 ff ff ff
903  ef ff ff ff
904 
905  14 00 00 00
906  1b 15 00 00 00  0d 00  00 00 00 00 01 00 a2 00 00 YY YY XX XX
907 
908  0b 00 00 00
909  1b ea 00 00 00  00 ZZ ZZ  ZZ ZZ 00
910 
911  Z` Z` Z` Z`
912  ...DATA...
913 
914  ea ff ff ff
915 
916  09 00 00 00
917  1b ee 00 00 00  02 00  NN NN
918 
919  ee ff ff ff 01 00 00 00
920 
921  0e 00 00 00
922  1b e5 00 00 00  08 00  00 00 00 00 DD LL SS AA
923 
924  eb ff ff ff ?? 00 00 00   <--- 02/05   5 at #3, 2 otherwise.  Sharpness?
925 
926  0c 00 00 00
927  1b c0 00 00 00  05 00  02 00  00 01  GG
928 
929  ec ff ff ff ?? 00 00 00   <--- 01/00/02/01/01  Seen.  Unknown.
930 
931  11 00 00 00
932  1b c0 00 01 00  0a 00  02 01  00 06  00 00 00 00 00 00
933 
934  ed ff ff ff 00 00 00 00
935 
936  12 00 00 00
937  1b e1 00 00 00  0b 00  00 08 00 00 a2 00 00 YY YY XX XX
938 
939  fa ff ff ff
940 
941  07 00 00 00
942  1b 0a 00 00 00 00 00
943 
944  fc ff ff ff fd ff ff ff ff ff ff ff
945 
946  07 00 00 00
947  1b 17 00 00 00 00 00
948 
949  f4 ff ff ff
950 
951  ****************
952 
953  UP-D895 comms protocol:
954 
955  <-- 1b e0 00 00 00 0e 00
956  --> 0d 00 XX YY 00 SS 00 ZZ  00 00 10 00 05 00
957 
958   XX : 0xe0 when printing, 0x00 otherwise.
959   YY : Number of remaining copies
960   SS : Status
961        0x00 = Idle
962        0x08 = Door open
963        0x40 = Paper empty
964        0x80 = Printing
965        ??   = Cooling down
966        ??   = Busy / Waiting
967   ZZ : Status2
968        0x01 = Print complete
969        0x02 = no prints yet
970 
971  UP-D897 comms protocol:
972 
973  <-- 1b e0 00 00 00 0f 00
974  --> 0e 00 XX YY 00 SS RR 01  02 02 10 00 05 00 PP
975 
976   XX : 0xe0 when printing, 0x00 otherwise.
977   YY : Number of remaining copies
978   SS : Status
979        0x00 = Idle
980 ??       0x08 = Door open
981        0x40 = Paper empty
982        0x80 = Printing
983        ??   = Cooling down
984        ??   = Busy / Waiting
985   RR : Status 2
986        0x00 = Okay
987        0x08 = ?? Error state?
988        0x80 = Printing
989   PP : Percentage complete (0-99)
990 
991 Other commands seen:
992 
993  <-- 1b 16 00 00 00 00 00   -- Reset
994 
995  <-- 1b c1 00 01 00 0a 00   -- Query ID 1, legth 10
996  --> 02 01 00 06 00 02 00 00  00 00
997 
998  <-- 1b c1 00 00 00 05 00   -- Query id 0, length 5
999  --> 02 01 00 01 03
1000 
1001  <-- 1b e6 00 00 00 08 00
1002  --> 07 00 00 00 00 00 00 00
1003 
1004  <-- 1b 17 00 00 00 00 00   -- Unknown?
1005 
1006    UP-CR10L
1007 
1008    2UPC-C13          (300)
1009    2UPC-C14          (200)
1010    2UPC-C15          (172)
1011 
1012  Status progression when printing:
1013 
1014  0e 00 00 00 00 00 00 00 04 a0 04 e0 07 38 00
1015  0e 00 00 01 00 80 00 01 04 a0 04 e0 07 38 64
1016  0e 00 40 01 00 80 00 01 04 a0 04 e0 07 38 64  <-- Y
1017  0e 00 80 01 00 80 00 01 04 a0 04 e0 07 38 64  <-- M
1018  0e 00 c0 01 00 80 00 01 04 a0 04 e0 07 38 64  <-- C
1019  0e 00 20 01 00 80 00 01 04 a0 04 e0 07 38 64  <-- O
1020  0e 00 20 01 00 80 00 01 04 a0 04 e0 07 38 00
1021  0e 00 00 01 00 80 00 01 04 a0 04 e0 07 38 00
1022  0e 00 00 00 00 00 00 00 04 a0 04 e0 07 38 00
1023 
1024 */
1025