1 /*
2 * Mitsubishi CP-D90DW Photo Printer CUPS backend
3 *
4 * (c) 2018 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 mitsud90_backend
40
41 #include "backend_common.h"
42
43 #define USB_VID_MITSU 0x06D3
44 #define USB_PID_MITSU_D90 0x3B60
45
46 const char *mitsu70x_media_types(uint8_t brand, uint8_t type);
47 const char *mitsu70x_temperatures(uint8_t temp);
48
49 /* Printer data structures */
50 #define D90_STATUS_TYPE_MODEL 0x01 // 10, null-terminated ASCII. 'CPD90D'
51 #define D90_STATUS_TYPE_x02 0x02 // 1, 0x5f ?
52 #define D90_STATUS_TYPE_FW_0b 0x0b // 8, 34 31 34 42 31 31 a7 de (414D11)
53 #define D90_STATUS_TYPE_FW_MA 0x0c // 8, 34 31 35 41 38 31 86 bf (415A81) // MAIN FW
54 #define D90_STATUS_TYPE_FW_F 0x0d // 8, 34 31 36 41 35 31 dc 8a (416A51) // FPGA FW
55 #define D90_STATUS_TYPE_FW_T 0x0e // 8, 34 31 37 45 31 31 e7 e6 (417E11) // TABLE FW
56 #define D90_STATUS_TYPE_FW_0f 0x0f // 8, 34 31 38 41 31 32 6c 64 (418A12)
57 #define D90_STATUS_TYPE_FW_11 0x11 // 8, 34 32 31 51 31 31 74 f2 (421Q11)
58 #define D90_STATUS_TYPE_FW_ME 0x13 // 8, 34 31 39 45 31 31 15 bf (419E11) // MECHA FW
59
60 #define D90_STATUS_TYPE_ERROR 0x16 // 11 (see below)
61 #define D90_STATUS_TYPE_MECHA 0x17 // 2 (see below)
62 #define D90_STATUS_TYPE_x1e 0x1e // 1, power state or time? (x00)
63 #define D90_STATUS_TYPE_TEMP 0x1f // 1 (see below)
64 #define D90_STATUS_TYPE_x22 0x22 // 2, all 0
65 #define D90_STATUS_TYPE_x28 0x28 // 2, all 0, seen some sort of counter?
66 #define D90_STATUS_TYPE_x29 0x29 // 8, e0 07 00 00 21 e6 b3 22
67 #define D90_STATUS_TYPE_MEDIA 0x2a // 10 (see below)
68 #define D90_STATUS_TYPE_x2b 0x2b // 2, all 0
69 #define D90_STATUS_TYPE_x2c 0x2c // 2, 00 56
70 #define D90_STATUS_TYPE_x65 0x65 // 50, ac 80 00 01 bb b8 fe 48 05 13 5d 9c 00 33 00 00 00 00 00 00 00 00 00 00 00 00 02 39 00 00 00 00 03 13 00 02 10 40 00 00 00 00 00 00 05 80 00 3a 00 00
71 #define D90_STATUS_TYPE_x82 0x82 // 1, 80 (iserial disabled?)
72 #define D90_STATUS_TYPE_x83 0x83 // 1, 00
73 #define D90_STATUS_TYPE_x84 0x84 // 1, 00
74
75 //#define D90_STATUS_TYPE_x85 0x85 // 2, 00 ?? BE, wait time?
76 // combined total of 5.
77
78 struct mitsud90_fw_resp_single {
79 uint8_t version[6];
80 uint16_t csum;
81 } __attribute__((packed));
82
83 struct mitsud90_media_resp {
84 uint8_t hdr[4]; /* e4 47 44 30 */
85 struct {
86 uint8_t brand;
87 uint8_t type;
88 uint8_t unk_a[2];
89 uint16_t capacity; /* BE */
90 uint16_t remain; /* BE */
91 uint8_t unk_b[2];
92 } __attribute__((packed)) media; /* D90_STATUS_TYPE_MEDIA */
93 } __attribute__((packed));
94
95 struct mitsud90_status_resp {
96 uint8_t hdr[4]; /* e4 47 44 30 */
97 /* D90_STATUS_TYPE_ERROR */
98 uint8_t code[2]; /* 00 is ok, nonzero is error */
99 uint8_t unk[9];
100 /* D90_STATUS_TYPE_MECHA */
101 uint8_t mecha[2];
102 /* D90_STATUS_TYPE_TEMP */
103 uint8_t temp;
104 } __attribute__((packed));
105
106 struct mitsud90_info_resp {
107 uint8_t hdr[4]; /* e4 47 44 30 */
108 uint8_t model[10];
109 uint8_t x02;
110 struct mitsud90_fw_resp_single fw_vers[7];
111 uint8_t x1e;
112 uint8_t x22[2];
113 uint8_t x28[2];
114 uint8_t x29[8];
115 uint8_t x2b[2];
116 uint8_t x2c[2];
117 uint8_t x65[50];
118 uint8_t x82;
119 uint8_t x83;
120 uint8_t x84;
121 } __attribute__((packed));
122
123 #define D90_MECHA_STATUS_IDLE 0x00
124 #define D90_MECHA_STATUS_PRINTING 0x50
125 #define D90_MECHA_STATUS_INIT 0x80
126 #define D90_MECHA_STATUS_INIT_FEEDCUT 0x10
127
128 #define D90_MECHA_STATUS_PRINT_FEEDING 0x10 // feeding ?
129 #define D90_MECHA_STATUS_PRINT_PRE_Y 0x21 // pre Y ?
130 #define D90_MECHA_STATUS_PRINT_Y 0x22 // Y ?
131 #define D90_MECHA_STATUS_PRINT_PRE_M 0x23 // pre M ?
132 #define D90_MECHA_STATUS_PRINT_M 0x24 // M ?
133 #define D90_MECHA_STATUS_PRINT_PRE_C 0x25 // pre C ? guess!
134 #define D90_MECHA_STATUS_PRINT_C 0x26 // C ?
135 #define D90_MECHA_STATUS_PRINT_PRE_OC 0x27 // pre OC ? guess!
136 #define D90_MECHA_STATUS_PRINT_OC 0x28 // O C?
137 #define D90_MECHA_STATUS_PRINTING_x2f 0x2f // ??
138 #define D90_MECHA_STATUS_PRINTING_x38 0x38 // eject ?
139
140 #define D90_ERROR_STATUS_OK 0x00
141 #define D90_ERROR_STATUS_OK_WARMING 0x40
142 #define D90_ERROR_STATUS_OK_COOLING 0x80
143 #define D90_ERROR_STATUS_RIBBON 0x21
144 #define D90_ERROR_STATUS_PAPER 0x22
145 #define D90_ERROR_STATUS_PAP_RIB 0x23
146 #define D90_ERROR_STATUS_OPEN 0x29
147
148 struct mitsud90_job_query {
149 uint8_t hdr[4]; /* 1b 47 44 31 */
150 uint16_t jobid; /* BE */
151 };
152
153 struct mitsud90_job_resp {
154 uint8_t hdr[4]; /* e4 47 44 31 */
155 uint8_t unk1;
156 uint8_t unk2;
157 uint16_t unk3;
158 };
159
160 struct mitsud90_job_hdr {
161 uint8_t hdr[6]; /* 1b 53 50 30 00 33 */
162 uint16_t cols; /* BE */
163 uint16_t rows; /* BE */
164 uint8_t unk[5]; /* 64 00 00 01 00 */
165 union {
166 #if 0
167 struct {
168 uint8_t margin;
169 uint16_t position;
170 } cuts[3] __attribute__((packed));
171 #endif
172 uint8_t cutzero[9];
173 } __attribute__((packed));
174 uint8_t zero[24];
175
176 uint8_t overcoat;
177 uint8_t quality;
178 uint8_t colorcorr;
179 uint8_t sharp_h;
180 uint8_t sharp_v;
181 uint8_t zero_b[5];
182 union {
183 struct {
184 uint16_t pano_on; /* 0x0001 when pano is on, */
185 uint8_t pano_tot; /* 2 or 3 */
186 uint8_t pano_pg; /* 1, 2, 3 */
187 uint16_t pano_rows; /* always 0x097c (BE), ie 2428 ie 8" print */
188 uint16_t pano_rows2; /* Always 0x30 less than pano_rows */
189 uint16_t pano_zero; /* 0x0000 */
190 uint8_t pano_unk[6]; /* 02 58 00 0c 00 06 */
191 } pano __attribute__((packed));
192 uint8_t zero_c[16];
193 };
194 uint8_t zero_d[6];
195 uint8_t zero_fill[432];
196 } __attribute__((packed));
197
198 struct mitsud90_plane_hdr {
199 uint8_t hdr[10]; /* 1b 5a 54 01 00 09 00 00 00 00 */
200 uint16_t cols; /* BE */
201 uint16_t rows; /* BE */
202 uint8_t zero_fill[498];
203 };
204
205 struct mitsud90_job_footer {
206 uint8_t hdr[4]; /* 1b 42 51 31 */
207 uint8_t pad;
208 uint8_t seconds; /* 0x05 by default (windows) */
209 };
210
211 struct mitsud90_memcheck {
212 uint8_t hdr[4]; /* 1b 47 44 33 */
213 uint8_t unk[2]; /* 00 33 */
214 uint16_t cols; /* BE */
215 uint16_t rows; /* BE */
216 uint8_t unk_b[4]; /* 64 00 00 01 */
217 uint8_t zero_fill[498];
218 };
219
220 struct mitsud90_memcheck_resp {
221 uint8_t hdr[4]; /* e4 47 44 43 */
222 uint8_t size_bad; /* 0x00 is ok */
223 uint8_t mem_bad; /* 0x00 is ok */
224 };
225
mitsud90_mecha_statuses(const uint8_t * code)226 const char *mitsud90_mecha_statuses(const uint8_t *code)
227 {
228 switch (code[0]) {
229 case D90_MECHA_STATUS_IDLE:
230 return "Idle";
231 case D90_MECHA_STATUS_PRINTING:
232 switch (code[1]) {
233 case D90_MECHA_STATUS_PRINT_FEEDING:
234 return "Feeding Media";
235 case D90_MECHA_STATUS_PRINT_PRE_Y:
236 case D90_MECHA_STATUS_PRINT_Y:
237 return "Printing Yellow";
238 case D90_MECHA_STATUS_PRINT_PRE_M:
239 case D90_MECHA_STATUS_PRINT_M:
240 return "Printing Magenta";
241 case D90_MECHA_STATUS_PRINT_PRE_C:
242 case D90_MECHA_STATUS_PRINT_C:
243 return "Printing Cyan";
244 case D90_MECHA_STATUS_PRINT_PRE_OC:
245 case D90_MECHA_STATUS_PRINT_OC:
246 return "Applying Overcoat";
247 case D90_MECHA_STATUS_PRINTING_x2f:
248 case D90_MECHA_STATUS_PRINTING_x38:
249 return "Ejecting Media?";
250 default:
251 return "Printing (Unknown)";
252 }
253 case D90_MECHA_STATUS_INIT:
254 if (code[1] == D90_MECHA_STATUS_INIT_FEEDCUT)
255 return "Feed & Cut paper";
256 else
257 return "Initializing";
258 default:
259 return "Unknown";
260 }
261 }
262
mitsud90_error_codes(const uint8_t * code)263 const char *mitsud90_error_codes(const uint8_t *code)
264 {
265 switch(code[0]) {
266 case D90_ERROR_STATUS_OK:
267 if (code[1] & D90_ERROR_STATUS_OK_WARMING)
268 return "Heating";
269 else if (code[1] & D90_ERROR_STATUS_OK_COOLING)
270 return "Cooling Down";
271 else
272 return "Idle";
273 case D90_ERROR_STATUS_RIBBON:
274 switch (code[1]) {
275 case 0x00:
276 return "Ribbon exhausted";
277 case 0x10:
278 return "Insufficient remaining ribbon";
279 case 0x20:
280 return "Ribbon Cue Timeout";
281 case 0x30:
282 return "Cannot Cue Ribbon";
283 case 0x90:
284 return "No ribbon";
285 default:
286 return "Unknown Ribbon Error";
287 }
288 case D90_ERROR_STATUS_PAPER:
289 switch (code[1]) {
290 case 0x00:
291 return "No paper";
292 case 0x02:
293 return "Paper exhausted";
294 default:
295 return "Unknown Paper Error";
296 }
297 case D90_ERROR_STATUS_PAP_RIB:
298 switch (code[1]) {
299 case 0x00:
300 return "Ribbon/Paper mismatch";
301 case 0x90:
302 return "Ribbon/Job mismatch";
303 default:
304 return "Unknown ribbon match error";
305 }
306 case 0x26:
307 return "Illegal Ribbon";
308 case 0x28:
309 return "Cut Bin Missing";
310 case D90_ERROR_STATUS_OPEN:
311 switch (code[1]) {
312 case 0x00:
313 return "Printer Open during Stop";
314 case 0x10:
315 return "Printer Open during Initialization";
316 case 0x90:
317 return "Printer Open during Printing";
318 default:
319 return "Unknown Door error";
320 }
321 case 0x2f:
322 return "Printer turned off during printing";
323 case 0x31:
324 return "Ink feed stop";
325 case 0x32:
326 return "Ink Skip 1 timeout";
327 case 0x33:
328 return "Ink Skip 2 timeout";
329 case 0x34:
330 return "Ink Sticking";
331 case 0x35:
332 return "Ink return stop";
333 case 0x36:
334 return "Ink Rewind timeout";
335 case 0x37:
336 return "Winding sensing error";
337 case 0x40:
338 case 0x41:
339 case 0x42:
340 case 0x43:
341 case 0x44:
342 return "Paper Jam";
343 case 0x60:
344 if (code[1] == 0x20)
345 return "Preheat error";
346 else if (code[1] == 0x04)
347 return "Humidity sensor error";
348 else if (code[1] & 0x1f)
349 return "Thermistor error";
350 else
351 return "Unknown error";
352 case 0x61:
353 if (code[1] == 0x00)
354 return "Color Sensor Error";
355 else if (code[1] & 0x10)
356 return "Matte OP Error";
357 else
358 return "Unknown error";
359 case 0x62:
360 return "Data Transfer error";
361 case 0x63:
362 return "EEPROM error";
363 case 0x64:
364 return "Flash access error";
365 case 0x65:
366 return "FPGA configuration error";
367 case 0x66:
368 return "Power voltage Error";
369 case 0x67:
370 return "RFID access error";
371 case 0x68:
372 if (code[1] == 0x00)
373 return "Fan Lock Error";
374 else if (code[1] == 0x90)
375 return "MDA Error";
376 else
377 return "Unknown error";
378 case 0x69:
379 if (code[1] == 0x10)
380 return "DDR Error";
381 else if (code[1] == 0x00)
382 return "Firmware Error";
383 else
384 return "Unknown error";
385 case 0x70:
386 case 0x71:
387 case 0x73:
388 case 0x75:
389 return "Mechanical Error (check ribbon and power cycle)";
390 case 0x82:
391 return "USB Timeout";
392 case 0x83:
393 return "Illegal paper size";
394 case 0x84:
395 return "Illegal parameter";
396 case 0x85:
397 return "Job Cancel";
398 case 0x89:
399 return "Last Job Error";
400 default:
401 return "Unknown";
402 }
403 }
404
mitsud90_dump_status(struct mitsud90_status_resp * resp)405 static void mitsud90_dump_status(struct mitsud90_status_resp *resp)
406 {
407 INFO("Error Status: %s (%02x %02x) -- %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
408 mitsud90_error_codes(resp->code),
409 resp->code[0], resp->code[1],
410 resp->unk[0], resp->unk[1], resp->unk[2], resp->unk[3],
411 resp->unk[4], resp->unk[5], resp->unk[6], resp->unk[7],
412 resp->unk[8]);
413 INFO("Printer Status: %s (%02x %02x)\n",
414 mitsud90_mecha_statuses(resp->mecha),
415 resp->mecha[0], resp->mecha[1]);
416 INFO("Temperature Status: %s\n",
417 mitsu70x_temperatures(resp->temp));
418 }
419
420 /* Private data structure */
421 struct mitsud90_printjob {
422 uint8_t *databuf;
423 int datalen;
424 int copies;
425 };
426
427 struct mitsud90_ctx {
428 struct libusb_device_handle *dev;
429 uint8_t endp_up;
430 uint8_t endp_down;
431
432 int type;
433
434 /* Used in parsing.. */
435 struct mitsud90_job_footer holdover;
436 int holdover_on;
437
438 struct marker marker;
439 };
440
mitsud90_query_media(struct mitsud90_ctx * ctx,struct mitsud90_media_resp * resp)441 static int mitsud90_query_media(struct mitsud90_ctx *ctx, struct mitsud90_media_resp *resp)
442 {
443 uint8_t cmdbuf[8];
444 int ret, num;
445
446 cmdbuf[0] = 0x1b;
447 cmdbuf[1] = 0x47;
448 cmdbuf[2] = 0x44;
449 cmdbuf[3] = 0x30;
450 cmdbuf[4] = 0;
451 cmdbuf[5] = 0;
452 cmdbuf[6] = 0x01; /* Number of commands */
453 cmdbuf[7] = D90_STATUS_TYPE_MEDIA;
454
455 if ((ret = send_data(ctx->dev, ctx->endp_down,
456 cmdbuf, sizeof(cmdbuf))))
457 return ret;
458 memset(resp, 0, sizeof(*resp));
459
460 ret = read_data(ctx->dev, ctx->endp_up,
461 (uint8_t*) resp, sizeof(*resp), &num);
462
463 if (ret < 0)
464 return ret;
465 if (num != sizeof(*resp)) {
466 ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*resp));
467 return 4;
468 }
469
470 return CUPS_BACKEND_OK;
471 }
472
mitsud90_query_status(struct mitsud90_ctx * ctx,struct mitsud90_status_resp * resp)473 static int mitsud90_query_status(struct mitsud90_ctx *ctx, struct mitsud90_status_resp *resp)
474 {
475 uint8_t cmdbuf[10];
476 int ret, num;
477
478 cmdbuf[0] = 0x1b;
479 cmdbuf[1] = 0x47;
480 cmdbuf[2] = 0x44;
481 cmdbuf[3] = 0x30;
482 cmdbuf[4] = 0;
483 cmdbuf[5] = 0;
484 cmdbuf[6] = 0x03; /* Number of commands */
485 cmdbuf[7] = D90_STATUS_TYPE_ERROR;
486 cmdbuf[8] = D90_STATUS_TYPE_MECHA;
487 cmdbuf[9] = D90_STATUS_TYPE_TEMP;
488
489 if ((ret = send_data(ctx->dev, ctx->endp_down,
490 cmdbuf, sizeof(cmdbuf))))
491 return ret;
492 memset(resp, 0, sizeof(*resp));
493
494 ret = read_data(ctx->dev, ctx->endp_up,
495 (uint8_t*) resp, sizeof(*resp), &num);
496
497 if (ret < 0)
498 return ret;
499 if (num != sizeof(*resp)) {
500 ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*resp));
501 return 4;
502 }
503
504 return CUPS_BACKEND_OK;
505 }
506
507 /* Generic functions */
508
mitsud90_init(void)509 static void *mitsud90_init(void)
510 {
511 struct mitsud90_ctx *ctx = malloc(sizeof(struct mitsud90_ctx));
512 if (!ctx) {
513 ERROR("Memory Allocation Failure!\n");
514 return NULL;
515 }
516 memset(ctx, 0, sizeof(struct mitsud90_ctx));
517
518 return ctx;
519 }
520
mitsud90_attach(void * vctx,struct libusb_device_handle * dev,int type,uint8_t endp_up,uint8_t endp_down,uint8_t jobid)521 static int mitsud90_attach(void *vctx, struct libusb_device_handle *dev, int type,
522 uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
523 {
524 struct mitsud90_ctx *ctx = vctx;
525 struct mitsud90_media_resp resp;
526
527 UNUSED(jobid);
528
529 ctx->dev = dev;
530 ctx->endp_up = endp_up;
531 ctx->endp_down = endp_down;
532 ctx->type = type;
533
534 if (test_mode < TEST_MODE_NOATTACH) {
535 if (mitsud90_query_media(ctx, &resp))
536 return CUPS_BACKEND_FAILED;
537 } else {
538 resp.media.brand = 0xff;
539 resp.media.type = 0x0f;
540 resp.media.capacity = cpu_to_be16(230);
541 resp.media.remain = cpu_to_be16(200);
542 }
543
544 ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
545 ctx->marker.name = mitsu70x_media_types(resp.media.brand, resp.media.type);
546 ctx->marker.levelmax = be16_to_cpu(resp.media.capacity);
547 ctx->marker.levelnow = be16_to_cpu(resp.media.remain);
548
549 return CUPS_BACKEND_OK;
550 }
551
mitsud90_cleanup_job(const void * vjob)552 static void mitsud90_cleanup_job(const void *vjob)
553 {
554 const struct mitsud90_printjob *job = vjob;
555
556 if (job->databuf)
557 free(job->databuf);
558
559 free((void*)job);
560 }
561
mitsud90_read_parse(void * vctx,const void ** vjob,int data_fd,int copies)562 static int mitsud90_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
563 struct mitsud90_ctx *ctx = vctx;
564 int i, remain;
565 struct mitsud90_job_hdr *hdr;
566
567 struct mitsud90_printjob *job;;
568
569 if (!ctx)
570 return CUPS_BACKEND_FAILED;
571
572 job = malloc(sizeof(*job));
573 if (!job) {
574 ERROR("Memory allocation failure!\n");
575 return CUPS_BACKEND_RETRY_CURRENT;
576 }
577 memset(job, 0, sizeof(*job));
578 job->copies = copies;
579
580 /* Just allocate a worst-case buffer */
581 job->datalen = 0;
582 job->databuf = malloc(sizeof(struct mitsud90_job_hdr) +
583 sizeof(struct mitsud90_plane_hdr) +
584 sizeof(struct mitsud90_job_footer) +
585 1852*2729*3);
586 if (!job->databuf) {
587 ERROR("Memory allocation failure!\n");
588 mitsud90_cleanup_job(job);
589 return CUPS_BACKEND_RETRY_CURRENT;
590 }
591
592 /* Make sure there's no holdover */
593 if (ctx->holdover_on) {
594 memcpy(job->databuf, &ctx->holdover, sizeof(ctx->holdover));
595 job->datalen += sizeof(ctx->holdover);
596 ctx->holdover_on = 0;
597 }
598
599 /* Read in first header. */
600 remain = sizeof(struct mitsud90_job_hdr) - job->datalen;
601 while (remain) {
602 i = read(data_fd, (job->databuf + job->datalen), remain);
603 if (i == 0) {
604 mitsud90_cleanup_job(job);
605 return CUPS_BACKEND_CANCEL;
606 }
607 if (i < 0) {
608 mitsud90_cleanup_job(job);
609 return CUPS_BACKEND_CANCEL;
610 }
611 remain -= i;
612 job->datalen += i;
613 }
614
615 /* Sanity check header */
616 hdr = (struct mitsud90_job_hdr *) job->databuf;
617 if (hdr->hdr[0] != 0x1b ||
618 hdr->hdr[1] != 0x53 ||
619 hdr->hdr[2] != 0x50 ||
620 hdr->hdr[3] != 0x30 ) {
621 ERROR("Unrecognized data format (%02x%02x%02x%02x)!\n",
622 hdr->hdr[0], hdr->hdr[1], hdr->hdr[2], hdr->hdr[3]);
623 mitsud90_cleanup_job(job);
624 return CUPS_BACKEND_CANCEL;
625 }
626
627 /* Now read in the rest */
628 remain = sizeof(struct mitsud90_plane_hdr) + be16_to_cpu(hdr->cols) * be16_to_cpu(hdr->rows) * 3;
629 while(remain) {
630 i = read(data_fd, job->databuf + job->datalen, remain);
631 if (i == 0) {
632 mitsud90_cleanup_job(job);
633 return CUPS_BACKEND_CANCEL;
634 }
635 if (i < 0) {
636 mitsud90_cleanup_job(job);
637 return CUPS_BACKEND_CANCEL;
638 }
639 job->datalen += i;
640 remain -= i;
641 }
642
643 /* Read in the footer. Hopefully. */
644 remain = sizeof(struct mitsud90_job_footer);
645 i = read(data_fd, job->databuf + job->datalen, remain);
646 if (i == 0) {
647 mitsud90_cleanup_job(job);
648 return CUPS_BACKEND_CANCEL;
649 }
650 if (i < 0) {
651 mitsud90_cleanup_job(job);
652 return CUPS_BACKEND_CANCEL;
653 }
654
655 /* See if this is a job footer. If it is, keep, else holdover. */
656 if (job->databuf[job->datalen + 0] != 0x1b ||
657 job->databuf[job->datalen + 1] != 0x42 ||
658 job->databuf[job->datalen + 2] != 0x51 ||
659 job->databuf[job->datalen + 3] != 0x31) {
660 memcpy(&ctx->holdover, job->databuf + job->datalen, sizeof(struct mitsud90_job_footer));
661 ctx->holdover_on = 1;
662 } else {
663 job->datalen += i;
664 ctx->holdover_on = 0;
665 }
666
667 /* Sanity check */
668 if (hdr->pano.pano_on) {
669 ERROR("Unable to handle panorama jobs yet\n");
670 mitsud90_cleanup_job(job);
671 return CUPS_BACKEND_CANCEL;
672 }
673
674 *vjob = job;
675
676 return CUPS_BACKEND_OK;
677 }
678
mitsud90_main_loop(void * vctx,const void * vjob)679 static int mitsud90_main_loop(void *vctx, const void *vjob) {
680 struct mitsud90_ctx *ctx = vctx;
681 struct mitsud90_job_hdr *hdr;
682 struct mitsud90_status_resp resp;
683 uint8_t last_status[2] = {0xff, 0xff};
684
685 int sent;
686 int ret;
687 int copies;
688
689 const struct mitsud90_printjob *job = vjob;
690
691 if (!ctx)
692 return CUPS_BACKEND_FAILED;
693 if (!job)
694 return CUPS_BACKEND_FAILED;
695 copies = job->copies;
696
697 hdr = (struct mitsud90_job_hdr*) job->databuf;
698
699 INFO("Waiting for printer idle...\n");
700
701 top:
702 sent = 0;
703
704 // XXX Figure out if printer is asleep, and wake it up if necessary.
705
706 /* Query status, wait for idle or error out */
707 do {
708 if (mitsud90_query_status(ctx, &resp))
709 return CUPS_BACKEND_FAILED;
710
711 if (resp.code[0] != D90_ERROR_STATUS_OK) {
712 ERROR("Printer reported error condition: %s (%02x %02x)\n",
713 mitsud90_error_codes(resp.code), resp.code[0], resp.code[1]);
714 return CUPS_BACKEND_STOP;
715 }
716
717 if (resp.code[1] & D90_ERROR_STATUS_OK_WARMING ||
718 resp.temp & D90_ERROR_STATUS_OK_WARMING ) {
719 INFO("Printer warming up\n");
720 sleep(1);
721 continue;
722 }
723 if (resp.code[1] & D90_ERROR_STATUS_OK_COOLING ||
724 resp.temp & D90_ERROR_STATUS_OK_COOLING) {
725 INFO("Printer cooling down\n");
726 sleep(1);
727 continue;
728 }
729
730 if (resp.mecha[0] != last_status[0] ||
731 resp.mecha[1] != last_status[1]) {
732 INFO("Printer status: %s\n",
733 mitsud90_mecha_statuses(resp.mecha));
734 last_status[0] = resp.mecha[0];
735 last_status[1] = resp.mecha[1];
736 }
737
738 if (resp.mecha[0] == D90_MECHA_STATUS_IDLE) {
739 break;
740 // we don't have to wait until idle, just
741 // until we have free buffers. Don't know how
742 // to check this though.. XXXX
743 }
744 } while(1);
745
746
747 /* Send memory check */
748 {
749 struct mitsud90_memcheck mem;
750 struct mitsud90_memcheck_resp mem_resp;
751 int num;
752
753 memcpy(&mem, hdr, sizeof(mem));
754 mem.hdr[0] = 0x1b;
755 mem.hdr[1] = 0x47;
756 mem.hdr[2] = 0x44;
757 mem.hdr[3] = 0x33;
758
759 if ((ret = send_data(ctx->dev, ctx->endp_down,
760 (uint8_t*) &mem, sizeof(mem))))
761 return CUPS_BACKEND_FAILED;
762
763 ret = read_data(ctx->dev, ctx->endp_up,
764 (uint8_t*)&mem_resp, sizeof(mem_resp), &num);
765
766 if (ret < 0)
767 return ret;
768 if (num != sizeof(mem_resp)) {
769 ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(mem_resp));
770 return 4;
771 }
772 if (mem_resp.size_bad || mem_resp.mem_bad == 0xff) {
773 ERROR("Printer reported bad print params (%02x)\n", mem_resp.size_bad);
774 return CUPS_BACKEND_CANCEL;
775 }
776 if (mem_resp.mem_bad) {
777 ERROR("Printer buffers full, retrying!\n");
778 sleep(1);
779 goto top;
780 }
781 }
782
783 /* Send header */
784 if ((ret = send_data(ctx->dev, ctx->endp_down,
785 job->databuf + sent, sizeof(*hdr))))
786 return CUPS_BACKEND_FAILED;
787 sent += sizeof(*hdr);
788
789 /* Send Plane header */
790 if ((ret = send_data(ctx->dev, ctx->endp_down,
791 job->databuf + sent, sizeof(*hdr))))
792 return CUPS_BACKEND_FAILED;
793 sent += sizeof(*hdr);
794
795 /* Send payload + footer */
796 if ((ret = send_data(ctx->dev, ctx->endp_down,
797 job->databuf + sent, job->datalen - sent)))
798 return CUPS_BACKEND_FAILED;
799 // sent += (job->datalen - sent);
800
801 /* Wait for completion */
802 do {
803 sleep(1);
804
805 if (mitsud90_query_status(ctx, &resp))
806 return CUPS_BACKEND_FAILED;
807
808 if (resp.code[0] != D90_ERROR_STATUS_OK) {
809 ERROR("Printer reported error condition: %s (%02x %02x)\n",
810 mitsud90_error_codes(resp.code), resp.code[0], resp.code[1]);
811 return CUPS_BACKEND_STOP;
812 }
813
814 if (resp.mecha[0] != last_status[0] ||
815 resp.mecha[1] != last_status[1]) {
816 INFO("Printer status: %s\n",
817 mitsud90_mecha_statuses(resp.mecha));
818 last_status[0] = resp.mecha[0];
819 last_status[1] = resp.mecha[1];
820 }
821
822 /* Terminate when printing complete */
823 if (resp.mecha[0] == D90_MECHA_STATUS_IDLE) {
824 break;
825 }
826
827 if (fast_return && copies <= 1) { /* Copies generated by backend? */
828 INFO("Fast return mode enabled.\n");
829 break;
830 }
831 } while(1);
832
833 /* Clean up */
834 if (terminate)
835 copies = 1;
836
837 INFO("Print complete (%d copies remaining)\n", copies - 1);
838
839 if (copies && --copies) {
840 goto top;
841 }
842
843 return CUPS_BACKEND_OK;
844 }
845
mitsud90_query_job(struct mitsud90_ctx * ctx,uint16_t jobid,struct mitsud90_job_resp * resp)846 static int mitsud90_query_job(struct mitsud90_ctx *ctx, uint16_t jobid,
847 struct mitsud90_job_resp *resp)
848 {
849 struct mitsud90_job_query req;
850 int ret, num;
851
852 req.hdr[0] = 0x1b;
853 req.hdr[1] = 0x47;
854 req.hdr[2] = 0x44;
855 req.hdr[3] = 0x31;
856 req.jobid = cpu_to_be16(jobid);
857
858 if ((ret = send_data(ctx->dev, ctx->endp_down,
859 (uint8_t*) &req, sizeof(req))))
860 return ret;
861 memset(resp, 0, sizeof(*resp));
862 ret = read_data(ctx->dev, ctx->endp_up,
863 (uint8_t*) resp, sizeof(*resp), &num);
864
865 if (ret < 0)
866 return ret;
867 if (num != sizeof(*resp)) {
868 ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(*resp));
869 return 4;
870 }
871
872 return CUPS_BACKEND_OK;
873 }
874
mitsud90_get_jobstatus(struct mitsud90_ctx * ctx,uint16_t jobid)875 static int mitsud90_get_jobstatus(struct mitsud90_ctx *ctx, uint16_t jobid)
876 {
877 struct mitsud90_job_resp resp;
878
879 if (mitsud90_query_job(ctx, jobid, &resp))
880 return CUPS_BACKEND_FAILED;
881
882 INFO("Job Status: %04x = %02x/%02x/%04x\n",
883 jobid, resp.unk1, resp.unk2, be16_to_cpu(resp.unk3));
884
885 return CUPS_BACKEND_OK;
886 }
887
mitsud90_get_media(struct mitsud90_ctx * ctx)888 static int mitsud90_get_media(struct mitsud90_ctx *ctx)
889 {
890 struct mitsud90_media_resp resp;
891
892 if (mitsud90_query_media(ctx, &resp))
893 return CUPS_BACKEND_FAILED;
894
895 INFO("Media Type: %s (%02x/%02x)\n",
896 mitsu70x_media_types(resp.media.brand, resp.media.type),
897 resp.media.brand,
898 resp.media.type);
899 INFO("Prints Remaining: %03d/%03d\n",
900 be16_to_cpu(resp.media.remain),
901 be16_to_cpu(resp.media.capacity));
902
903 return CUPS_BACKEND_OK;
904 }
905
mitsud90_get_status(struct mitsud90_ctx * ctx)906 static int mitsud90_get_status(struct mitsud90_ctx *ctx)
907 {
908 struct mitsud90_status_resp resp;
909
910 if (mitsud90_query_status(ctx, &resp))
911 return CUPS_BACKEND_FAILED;
912
913 mitsud90_dump_status(&resp);
914
915 return CUPS_BACKEND_OK;
916 }
917
mitsud90_get_info(struct mitsud90_ctx * ctx)918 int mitsud90_get_info(struct mitsud90_ctx *ctx)
919 {
920 uint8_t cmdbuf[26];
921 int ret, num;
922 struct mitsud90_info_resp resp;
923
924 cmdbuf[0] = 0x1b;
925 cmdbuf[1] = 0x47;
926 cmdbuf[2] = 0x44;
927 cmdbuf[3] = 0x30;
928 cmdbuf[4] = 0;
929 cmdbuf[5] = 0;
930 cmdbuf[6] = 19; /* Number of commands */
931
932 cmdbuf[7] = D90_STATUS_TYPE_MODEL;
933 cmdbuf[8] = 0x02;
934 cmdbuf[9] = 0x0b;
935 cmdbuf[10] = 0x0c;
936
937 cmdbuf[11] = 0x0d;
938 cmdbuf[12] = 0x0e;
939 cmdbuf[13] = 0x0f;
940 cmdbuf[14] = 0x11;
941
942 cmdbuf[15] = 0x13;
943 cmdbuf[16] = 0x1e;
944 cmdbuf[17] = 0x22;
945 cmdbuf[18] = 0x28;
946
947 cmdbuf[19] = 0x29;
948 cmdbuf[20] = 0x2b;
949 cmdbuf[21] = 0x2c;
950 cmdbuf[22] = 0x65;
951
952 cmdbuf[23] = 0x82;
953 cmdbuf[24] = 0x83;
954 cmdbuf[25] = 0x84;
955
956 if ((ret = send_data(ctx->dev, ctx->endp_down,
957 cmdbuf, sizeof(cmdbuf))))
958 return ret;
959 memset(&resp, 0, sizeof(resp));
960
961 ret = read_data(ctx->dev, ctx->endp_up,
962 (uint8_t*) &resp, sizeof(resp), &num);
963
964 if (ret < 0)
965 return ret;
966 if (num != sizeof(resp)) {
967 ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(resp));
968 return 4;
969 }
970
971 /* start dumping output */
972 memset(cmdbuf, 0, sizeof(cmdbuf));
973 memcpy(cmdbuf, resp.model, sizeof(resp.model));
974 INFO("Model: %s\n", (char*)cmdbuf);
975 for (num = 0; num < 7 ; num++) {
976 memset(cmdbuf, 0, sizeof(cmdbuf));
977 memcpy(cmdbuf, resp.fw_vers[num].version, sizeof(resp.fw_vers[num].version));
978 INFO("FW Component %02d: %s (%04x)\n",
979 num, cmdbuf, be16_to_cpu(resp.fw_vers[num].csum));
980 }
981 INFO("TYPE_02: %02x\n", resp.x02);
982 INFO("TYPE_1e: %02x\n", resp.x1e);
983 INFO("TYPE_22: %02x %02x\n", resp.x22[0], resp.x22[1]);
984 INFO("TYPE_28: %02x %02x\n", resp.x28[0], resp.x28[1]);
985 INFO("TYPE_29: %02x %02x %02x %02x %02x %02x %02x %02x\n",
986 resp.x29[0], resp.x29[1], resp.x29[2], resp.x29[3],
987 resp.x29[4], resp.x29[5], resp.x29[6], resp.x29[7]);
988 INFO("TYPE_2b: %02x %02x\n", resp.x2b[0], resp.x2b[1]);
989 INFO("TYPE_2c: %02x %02x\n", resp.x2c[0], resp.x2c[1]);
990
991 INFO("TYPE_65:");
992 for (num = 0; num < 50 ; num++) {
993 DEBUG2(" %02x", resp.x65[num]);
994 }
995 DEBUG2("\n");
996 INFO("TYPE_1e: %82x\n", resp.x82);
997 INFO("TYPE_1e: %83x\n", resp.x83);
998
999 /* XXX Dump iSerial, sleep time settings */
1000 // XXX what about resume, wait time, "cut limit" ?
1001
1002 return CUPS_BACKEND_OK;
1003 }
1004
mitsud90_dumpall(struct mitsud90_ctx * ctx)1005 static int mitsud90_dumpall(struct mitsud90_ctx *ctx)
1006 {
1007 int i;
1008 uint8_t cmdbuf[8];
1009 uint8_t buf[256];
1010
1011 cmdbuf[0] = 0x1b;
1012 cmdbuf[1] = 0x47;
1013 cmdbuf[2] = 0x44;
1014 cmdbuf[3] = 0x30;
1015 cmdbuf[4] = 0;
1016 cmdbuf[5] = 0;
1017 cmdbuf[6] = 0x01; /* Number of commands */
1018
1019 for (i = 0 ; i < 256 ; i++) {
1020 int num, ret;
1021
1022 cmdbuf[7] = i;
1023
1024 if ((ret = send_data(ctx->dev, ctx->endp_down,
1025 cmdbuf, sizeof(cmdbuf))))
1026 return ret;
1027 memset(buf, 0, sizeof(buf));
1028
1029 ret = read_data(ctx->dev, ctx->endp_up,
1030 buf, sizeof(buf), &num);
1031
1032 if (ret <= 0)
1033 continue;
1034
1035 if (num > 4) {
1036 DEBUG("TYPE %02x LEN: %d (%d)\n", i, num, num - 4);
1037 DEBUG("<--");
1038 for (ret = 0; ret < num ; ret ++) {
1039 DEBUG2(" %x", buf[ret]);
1040 }
1041 DEBUG2("\n");
1042 }
1043 }
1044
1045 return CUPS_BACKEND_OK;
1046 }
1047
mitsud90_set_iserial(struct mitsud90_ctx * ctx,uint8_t enabled)1048 static int mitsud90_set_iserial(struct mitsud90_ctx *ctx, uint8_t enabled)
1049 {
1050 uint8_t cmdbuf[23];
1051 int ret, num;
1052
1053 enabled = (enabled) ? 0: 0x80;
1054
1055 /* Send Parameter.. */
1056 cmdbuf[0] = 0x1b;
1057 cmdbuf[1] = 0x31;
1058 cmdbuf[2] = 0x36;
1059 cmdbuf[3] = 0x30;
1060 cmdbuf[4] = 0x41;
1061 cmdbuf[5] = 0xbe;
1062 cmdbuf[6] = 0x00;
1063 cmdbuf[7] = 0x00;
1064
1065 cmdbuf[8] = 0x00;
1066 cmdbuf[9] = 0x01;
1067 cmdbuf[10] = 0x00;
1068 cmdbuf[11] = 0x00;
1069 cmdbuf[12] = 0x00;
1070 cmdbuf[13] = 0x11;
1071 cmdbuf[14] = 0xff;
1072 cmdbuf[15] = 0xff;
1073
1074 cmdbuf[16] = 0xff;
1075 cmdbuf[17] = 0xfe;
1076 cmdbuf[18] = 0xff;
1077 cmdbuf[19] = 0xff;
1078 cmdbuf[20] = 0xff;
1079 cmdbuf[21] = 0xfe;
1080 cmdbuf[22] = enabled;
1081
1082 if ((ret = send_data(ctx->dev, ctx->endp_down,
1083 cmdbuf, sizeof(cmdbuf))))
1084 return ret;
1085
1086 ret = read_data(ctx->dev, ctx->endp_up,
1087 cmdbuf, sizeof(cmdbuf), &num);
1088
1089 /* No response */
1090
1091 return ret;
1092 }
1093
mitsud90_set_sleeptime(struct mitsud90_ctx * ctx,uint16_t time)1094 static int mitsud90_set_sleeptime(struct mitsud90_ctx *ctx, uint16_t time)
1095 {
1096 uint8_t cmdbuf[24];
1097 int ret;
1098
1099 /* 255 minutes max, according to RE work */
1100 if (time > 255)
1101 time = 255;
1102
1103 /* Send Parameter.. */
1104 cmdbuf[0] = 0x1b;
1105 cmdbuf[1] = 0x31;
1106 cmdbuf[2] = 0x36;
1107 cmdbuf[3] = 0x30;
1108 cmdbuf[4] = 0x41;
1109 cmdbuf[5] = 0xbe;
1110 cmdbuf[6] = 0x00;
1111 cmdbuf[7] = 0x00;
1112
1113 cmdbuf[8] = 0x00;
1114 cmdbuf[9] = 0x02;
1115 cmdbuf[10] = 0x00;
1116 cmdbuf[11] = 0x00;
1117 cmdbuf[12] = 0x05;
1118 cmdbuf[13] = 0x02;
1119 cmdbuf[14] = 0xff;
1120 cmdbuf[15] = 0xff;
1121
1122 cmdbuf[16] = 0xff;
1123 cmdbuf[17] = 0xfd;
1124 cmdbuf[18] = 0xff;
1125 cmdbuf[19] = 0xff;
1126 cmdbuf[20] = 0xfa;
1127 cmdbuf[21] = 0xff;
1128 cmdbuf[22] = (time >> 8) & 0xff;
1129 cmdbuf[23] = time & 0xff;
1130
1131 if ((ret = send_data(ctx->dev, ctx->endp_down,
1132 cmdbuf, 4)))
1133 return ret;
1134
1135 /* No response */
1136
1137 return 0;
1138 }
1139
mitsud90_cmdline(void)1140 static void mitsud90_cmdline(void)
1141 {
1142 DEBUG("\t\t[ -i ] # Query printer info\n");
1143 DEBUG("\t\t[ -j jobid ] # Query job status\n");
1144 DEBUG("\t\t[ -k time ] # Set sleep time in minutes\n");
1145 DEBUG("\t\t[ -m ] # Query printer media\n");
1146 DEBUG("\t\t[ -s ] # Query printer status\n");
1147 DEBUG("\t\t[ -x 0|1 ] # Enable/disable iSerial reporting\n");
1148 // DEBUG("\t\t[ -Z ] # Dump all parameters\n");
1149 }
1150
mitsud90_cmdline_arg(void * vctx,int argc,char ** argv)1151 static int mitsud90_cmdline_arg(void *vctx, int argc, char **argv)
1152 {
1153 struct mitsud90_ctx *ctx = vctx;
1154 int i, j = 0;
1155
1156 if (!ctx)
1157 return -1;
1158
1159 while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "ij:k:msx:Z")) >= 0) {
1160 switch(i) {
1161 GETOPT_PROCESS_GLOBAL
1162 case 'i':
1163 j = mitsud90_get_info(ctx);
1164 break;
1165 case 'j':
1166 j = mitsud90_get_jobstatus(ctx, atoi(optarg));
1167 break;
1168 case 'k':
1169 j = mitsud90_set_sleeptime(ctx, atoi(optarg));
1170 break;
1171 case 'm':
1172 j = mitsud90_get_media(ctx);
1173 break;
1174 case 's':
1175 j = mitsud90_get_status(ctx);
1176 break;
1177 case 'x':
1178 j = mitsud90_set_iserial(ctx, atoi(optarg));
1179 break;
1180 case 'Z':
1181 j = mitsud90_dumpall(ctx);
1182 break;
1183 default:
1184 break; /* Ignore completely */
1185 }
1186
1187 if (j) return j;
1188 }
1189
1190 return 0;
1191 }
1192
mitsud90_query_markers(void * vctx,struct marker ** markers,int * count)1193 static int mitsud90_query_markers(void *vctx, struct marker **markers, int *count)
1194 {
1195 struct mitsud90_ctx *ctx = vctx;
1196 struct mitsud90_media_resp resp;
1197
1198 *markers = &ctx->marker;
1199 *count = 1;
1200
1201 if (mitsud90_query_media(ctx, &resp))
1202 return CUPS_BACKEND_FAILED;
1203
1204 ctx->marker.levelnow = be16_to_cpu(resp.media.remain);
1205
1206 return CUPS_BACKEND_OK;
1207 }
1208
1209 static const char *mitsud90_prefixes[] = {
1210 "mitsubishi-d90dw",
1211 // backwards compatibility
1212 "mitsud90",
1213 NULL
1214 };
1215
1216 /* Exported */
1217 struct dyesub_backend mitsud90_backend = {
1218 .name = "Mitsubishi CP-D90DW",
1219 .version = "0.13",
1220 .uri_prefixes = mitsud90_prefixes,
1221 .cmdline_arg = mitsud90_cmdline_arg,
1222 .cmdline_usage = mitsud90_cmdline,
1223 .init = mitsud90_init,
1224 .attach = mitsud90_attach,
1225 .cleanup_job = mitsud90_cleanup_job,
1226 .read_parse = mitsud90_read_parse,
1227 .main_loop = mitsud90_main_loop,
1228 .query_markers = mitsud90_query_markers,
1229 .devices = {
1230 { USB_VID_MITSU, USB_PID_MITSU_D90, P_MITSU_D90, NULL, "mitsubishi-d90dw"},
1231 { 0, 0, 0, NULL, NULL}
1232 }
1233 };
1234
1235 /*
1236 Mitsubishi CP-D90DW data format
1237
1238 All multi-byte values are BIG endian
1239
1240 [[HEADER 1]]
1241
1242 1b 53 50 30 00 33 XX XX YY YY 64 00 00 01 00 ?? XX XX == COLS, YY XX ROWS (BE)
1243 ?? ?? ?? ?? ?? ?? ?? ?? 00 00 00 00 00 00 00 00 <-- cut position, see below
1244 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1245 QQ RR SS HH VV 00 00 00 00 00 01 00 03 II 09 7c QQ == 02 matte, 00 glossy,
1246 09 4c 00 00 02 58 00 0c 00 06 RR == 00 auto, 03 == fine, 02 == superfine.
1247 SS == 00 colorcorr, 01 == none
1248 HH/VV sharpening for Horiz/Vert, 0-8, 0 is off, 4 is normal
1249 [pad to 512b]
1250
1251 normal == rows 00 00 00 00 00 00 00 00 00
1252 4x6div2 == 1226 00 02 65 01 00 00 01 00 00
1253 8x6div2 == 2488 01 04 be 00 00 00 00 00 00
1254
1255 guesses based on SDK docs:
1256
1257 9x6div2 == 2728 01 05 36 00 00 00 00 00 00
1258 9x6div3 == 2724 00 03 90 00 07 14 00 00 00
1259 9x6div4 == 2628 00 02 97 00 05 22 00 07 ad
1260
1261 from [01 00 03 03] onwards, only shows in 8x20" PANORAMA prints. Assume 2" overlap.
1262 II == 01 02 03 (which panel # in panorama!)
1263 [02 58] == 600, aka 2" * 300dpi?
1264 [09 4c] == 2380 (48 less than 8 size? (trim length on ends?)
1265 [09 7c] == 2428 (ie 8" print)
1266
1267 (6x20 == 1852x6036)
1268 (6x14 == 1852x4232)
1269
1270 3*8" panels == 2428*3=7284. -6036 = 1248. /2 = 624 (0x270)
1271
1272 [[DATA PLANE HEADER]]
1273
1274 1b 5a 54 01 00 09 00 00 00 00 XX XX YY YY 00 00
1275 ...
1276 [pad to 512b]
1277
1278 data, BGR packed, 8bpp. No padding to 512b!
1279
1280 [[FOOTER]]
1281
1282 1b 42 51 31 00 TT ## TT == secs to wait for second print
1283
1284
1285 ****************************************************
1286
1287 Comms Protocol for D90:
1288
1289 [[ ERROR STATUS ]]
1290
1291 -> 1b 47 44 30 00 00 01 16
1292 <- e4 47 44 30 00 00 00 00 00 00 00 00 00 00 00 [Normal/OK]
1293 <- e4 47 44 30 XX 00 00 00 00 00 00 00 00 3f 37 [Error condition]
1294 XX == 29 (printer open)
1295 28 (cut bin missing)
1296 <- e4 47 44 30 21 90 00 00 01 00 00 00 00 3f 37 No ribbon
1297
1298 [[ MEDIA STATUS ]]
1299
1300 -> 1b 47 44 30 00 00 01 2a
1301 <- e4 47 44 30 ff 0f 50 00 01 ae 01 9b 01 00 [Normal/OK]
1302 <- e4 47 44 30 ff ff ff ff ff ff ff ff ff ff [Error]
1303
1304 [[ MECHA STATUS ]]
1305
1306 -> 1b 47 44 30 00 00 01 17
1307 <- e4 47 44 30 SS SS
1308
1309 [[ TEMPERATURE QUERY ]]
1310
1311 -> 1b 47 44 30 00 00 01 1f
1312 <- e4 47 44 30 HH
1313
1314 [[ UNKNOWN QUERY ]]
1315 -> 1b 47 44 30 00 00 01 28
1316 <- e4 47 44 30 XX XX Unknown, seems to increment.
1317
1318 [[ JOB STATUS QUERY ?? ]]
1319
1320 -> 1b 47 44 31 00 00 JJ JJ Jobid?
1321 <- e4 47 44 31 XX YY ZZ ZZ No idea.. sure.
1322
1323 [[ COMBINED STATUS QUERIES ]]
1324
1325 -> 1b 47 44 30 00 00 04 16 17 1f 2a
1326 <- e4 47 44 30
1327
1328 MM NN 00 00 ZZ 00 00 00 00 QQ QQ [id 16, total 11]
1329 SS SS [id 17, total 2]
1330 HH [id 1f, total 1]
1331 VV TT WW 00 XX XX YY YY 01 00 [id 2a, total 10]
1332
1333 WW == 0x50 or 0x00 (seen, no idea what it means)
1334 VV == Media vendor (0xff etc)
1335 TT == Media type, 0x02/0x0f etc (see mitsu70x_media_types!)
1336 XX XX == Media capacity, BE
1337 YY YY == Media remain, BE
1338 QQ QQ == 00 00 normal, 3f 37 error
1339 MM NN == MM major err (00 if no error) NN minor error.
1340 ZZ == 01 seen for _some_ errors.
1341 SS SS == Mecha Status (00 == ready, 50 == printing, 80+10 == feedandcut, 80 == initializing?
1342 HH == Temperature state. 00 is OK, 0x40 is low, 0x80 is hot.
1343 II II == ??
1344 JJ JJ == ??
1345
1346 [[ WAKE UP PRINTER ]]
1347 -> 1b 45 57 55
1348
1349 [[ GET iSERIAL ]]
1350
1351 -> 1b 61 36 36 41 be 00 00
1352 00 01 00 00 00 11 ff ff
1353 ff fe ff ff ff ee
1354 <- e4 61 36 36 41 be 00 00
1355 00 01 00 00 00 11 ff ff
1356 ff fe ff ff ff ee XX <- XX is 0x80 or 0x00. (0x80) ISERIAL OFF
1357
1358 [[ GET CUT? ]]
1359
1360 -> 1b 61 36 36 45 ba 00 00
1361 00 01 00 00 05 07 ff ff
1362 ff fe ff ff fa f8
1363 -> e4 61 36 36 45 ba 00 00
1364 00 01 00 00 05 07 ff ff
1365 ff fe ff ff fa f8 XX <- XX is 0x80 or 0x00 (0x00) CUT ON?
1366
1367 [[ GET WAIT TIME ]]
1368
1369 -> 1b 61 36 36 45 00 00 00
1370 00 01 00 00 05 05 ff ff
1371 ff fe ff ff fa fb
1372 -> 1b 61 36 36 45 00 00 00
1373 00 01 00 00 05 05 ff ff
1374 ff fe ff ff fa fb XX <- XX is time in seconds.
1375
1376 [[ GET RESUME? ]]
1377
1378 -> 1b 61 36 36 45 ba 00 00
1379 00 01 00 00 05 06 ff ff
1380 ff fe ff ff fa f9
1381 -> e4 61 36 36 45 ba 00 00
1382 00 01 00 00 05 06 ff ff
1383 ff fe ff ff fa f9 XX <- XX is 0x80 or 0x00 (0x80) (OFF)
1384
1385 [[ GET SLEEP TIME! ]]
1386
1387 -> 1b 61 36 36 45 ba 00 00
1388 00 02 00 00 05 02 ff ff
1389 ff fd ff ff fa fd
1390 <- e4 61 36 36 45 00 00 00
1391 00 02 00 00 05 02 ff ff
1392 ff fd ff ff fa fd XX 00 <- XX, sleep time in minutes.
1393
1394 [[ SET SLEEP TIME! ]]
1395
1396 -> 1b 61 36 30 45 ba 00 00
1397 00 02 00 00 05 02 ff ff
1398 ff fd ff ff fa fd XX 00 <- XX, sleep time in minutes.
1399
1400 [[ SET iSERIAL ]]
1401
1402 -> 1b 61 36 30 41 be 00 00
1403 00 01 00 00 00 11 ff ff
1404 ff fe ff ff ff ee XX <- XX 0x80 OFF, 0x00 ON.
1405
1406 [[ SANITY CHECK PRINT ARGUMENTS / MEMTEST ]]
1407
1408 -> 1b 47 44 33 00 33 07 3c 04 ca 64 00 00 01 00 00
1409 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1411 00 00 00 04 04 00 00 00 00 00 00 00 00 00 00 00
1412 [[ pad to 512 ]]
1413
1414 ... 07 3c onwards is the same as main payload header.
1415
1416 <- e4 47 44 43 XX YY
1417
1418 ... possibly the same as the D70's "memorystatus"
1419 XX == size ok (non-zero if bad size)
1420 YY == memory ok (non-zero or 0xff if full?)
1421
1422 [[ SEND OVER HDRs and DATA ]]
1423
1424 ... Print arguments:
1425
1426 -> 1b 53 50 30 00 33 07 3c 04 ca 64 00 00 01 00 00
1427 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1428 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1429 00 00 00 04 04 00 00 00 00 00 00 00 00 00 00 00
1430 [[ pad to 512 ]]
1431
1432 ... Data transfer. Plane header:
1433
1434 -> 1b 5a 54 01 00 09 00 00 00 00 07 3c 04 ca 00 00
1435 [[ pad to 512 ]]
1436
1437 -> [[print data]] [[ padded? ]]
1438 -> [[print data]]
1439
1440 -> 1b 42 51 31 00 ZZ
1441
1442 ... Footer.
1443 ZZ == Seconds to wait for follow-up print (0x05)
1444
1445
1446 */
1447