1 /*
2 * Mitsubishi CP-9xxx Photo Printer Family CUPS backend
3 *
4 * (c) 2014-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 /* For Integration into gutenprint */
40 #if defined(HAVE_CONFIG_H)
41 #include <config.h>
42 #endif
43
44 #define BACKEND mitsu9550_backend
45
46
47 #if defined(USE_DLOPEN)
48 #define WITH_DYNAMIC
49 #include <dlfcn.h>
50 #define DL_INIT() do {} while(0)
51 #define DL_OPEN(__x) dlopen(__x, RTLD_NOW)
52 #define DL_SYM(__x, __y) dlsym(__x, __y)
53 #define DL_CLOSE(__x) dlclose(__x)
54 #define DL_EXIT() do {} while(0)
55 #elif defined(USE_LTDL)
56 #define WITH_DYNAMIC
57 #include <ltdl.h>
58 #define DL_INIT() lt_dlinit()
59 #define DL_OPEN(__x) lt_dlopen(__x)
60 #define DL_SYM(__x, __y) lt_dlsym(__x, __y)
61 #define DL_CLOSE(__x) do {} while(0)
62 #define DL_EXIT() lt_dlexit()
63 #else
64 #define DL_INIT() do {} while(0)
65 #define DL_CLOSE(__x) do {} while(0)
66 #define DL_EXIT() do {} while(0)
67 #warning "No dynamic loading support!"
68 #endif
69
70 #include "backend_common.h"
71
72 // #include "lib70x/libMitsuD70ImageReProcess.h"
73
74 #ifndef LUT_LEN
75 #define COLORCONV_RGB 0
76 #define COLORCONV_BGR 1
77
78 #define LUT_LEN 14739
79 struct BandImage {
80 void *imgbuf;
81 int32_t bytes_per_row;
82 uint16_t origin_cols;
83 uint16_t origin_rows;
84 uint16_t cols;
85 uint16_t rows;
86 };
87 #endif
88
89 #define REQUIRED_LIB_APIVERSION 4
90
91 /* Image processing library function prototypes */
92 #define LIB_NAME_RE "libMitsuD70ImageReProcess.so" // Reimplemented library
93
94 typedef int (*lib70x_getapiversionFN)(void);
95 typedef int (*Get3DColorTableFN)(uint8_t *buf, const char *filename);
96 typedef struct CColorConv3D *(*Load3DColorTableFN)(const uint8_t *ptr);
97 typedef void (*Destroy3DColorTableFN)(struct CColorConv3D *this);
98 typedef void (*DoColorConvFN)(struct CColorConv3D *this, uint8_t *data, uint16_t cols, uint16_t rows, uint32_t bytes_per_row, int rgb_bgr);
99
100 #ifndef CORRTABLE_PATH
101 #ifdef PACKAGE_DATA_DIR
102 #define CORRTABLE_PATH PACKAGE_DATA_DIR "/backend_data"
103 #else
104 #error "Must define CORRTABLE_PATH or PACKAGE_DATA_DIR!"
105 #endif
106 #endif
107
108 #define MITSU_M98xx_LAMINATE_FILE CORRTABLE_PATH "/M98MATTE.raw"
109 #define MITSU_M98xx_DATATABLE_FILE CORRTABLE_PATH "/M98TABLE.dat"
110 #define MITSU_M98xx_LUT_FILE CORRTABLE_PATH "/M98XXL01.lut"
111 #define LAMINATE_STRIDE 1868
112 #define DATATABLE_SIZE 42204
113
114 /* USB VIDs and PIDs */
115
116 #define USB_VID_MITSU 0x06D3
117 #define USB_PID_MITSU_9500D 0x0393
118 #define USB_PID_MITSU_9000D 0x0394
119 #define USB_PID_MITSU_9000AM 0x0395
120 #define USB_PID_MITSU_9550D 0x03A1
121 #define USB_PID_MITSU_9550DS 0x03A5 // or DZ/DZS/DZU
122 #define USB_PID_MITSU_9600D 0x03A9
123 //#define USB_PID_MITSU_9600DS XXXXXX
124 #define USB_PID_MITSU_9800D 0x03AD
125 #define USB_PID_MITSU_9800DS 0x03AE
126 #define USB_PID_MITSU_98__D 0x3B21
127 //#define USB_PID_MITSU_9810D XXXXXX
128 //#define USB_PID_MITSU_9820DS XXXXXX
129
130 /* Spool file structures */
131
132 /* Print parameters1 */
133 struct mitsu9550_hdr1 {
134 uint8_t cmd[4]; /* 1b 57 20 2e */
135 uint8_t unk[10]; /* 00 0a 10 00 [...] */
136 uint16_t cols; /* BE */
137 uint16_t rows; /* BE */
138 uint8_t matte; /* CP9810/9820 only. 01 for matte, 00 glossy */
139 uint8_t null[31];
140 } __attribute__((packed));
141
142 /* Print parameters2 */
143 struct mitsu9550_hdr2 {
144 uint8_t cmd[4]; /* 1b 57 21 2e */
145 uint8_t unk[24]; /* 00 80 00 22 08 03 [...] */
146 uint16_t copies; /* BE, 1-680 */
147 uint8_t null[2];
148 uint8_t cut; /* 00 == normal, 83 == 2x6*2 */
149 uint8_t unkb[5];
150 uint8_t mode; /* 00 == fine, 80 == superfine */
151 uint8_t unkc[11]; /* 00 [...] 00 01 */
152 } __attribute__((packed));
153
154 /* Fine Deep selection (9550 only) */
155 struct mitsu9550_hdr3 {
156 uint8_t cmd[4]; /* 1b 57 22 2e */
157 uint8_t unk[7]; /* 00 40 00 [...] */
158 uint8_t mode2; /* 00 == normal, 01 == finedeep */
159 uint8_t null[38];
160 } __attribute__((packed));
161
162 /* Error policy? */
163 struct mitsu9550_hdr4 {
164 uint8_t cmd[4]; /* 1b 57 26 2e */
165 uint8_t unk[46]; /* 00 70 00 00 00 00 00 00 01 01 00 [...] */
166 } __attribute__((packed));
167
168 /* Data plane header */
169 struct mitsu9550_plane {
170 uint8_t cmd[4]; /* 1b 5a 54 XX */ /* XX == 0x10 if 16bpp, 0x00 for 8bpp */
171 uint16_t col_offset; /* BE, normally 0, where we start dumping data */
172 uint16_t row_offset; /* BE, normally 0, where we start dumping data */
173 uint16_t cols; /* BE */
174 uint16_t rows; /* BE */
175 } __attribute__((packed));
176
177 /* CP98xx Tabular Data, as stored in data file! */
178 struct mitsu98xx_data {
179 /* @ 0 */ uint16_t GNMby[256]; /* BGR Order uncertain */
180 /* @ 512 */ uint16_t GNMgm[256];
181 /* @ 1024 */ uint16_t GNMrc[256];
182 /* @ 1536 */ uint16_t unk_sharp[20]; /* Actual format is: u16, u16[9], u16, u16[9] */
183 /* @ 1576 */ double GammaAdj[3]; /* Assumed to be same order as tables (BGR?) */
184 /* @ 1600 */ struct {
185 /* @ 0 */ double unka[256];
186 /* @ 2048 */ double unkb[256];
187 /* @ 4096 */ uint32_t unkc[10];
188 /* @ 4136 */ double unkd[256];
189 /* @ 6184 */ double unke[256]; // *= sharp->coef[X]
190 /* @ 8232 */ uint32_t unkf[10];
191 /* @ 8272 */ double unkg[256];
192 /* @10320 */
193 } WMAM;
194 /* @11920 */ double sharp_coef[11]; /* 0 is off, 1-10 are the levels. Default is 5. [4 in settings] */
195 /* @12008 */ uint32_t unk_kh[3];
196 /* @12020 */ uint8_t KH[2048];
197 /* @14068 */
198 } __attribute__((packed));
199
200 struct mitsu98xx_tables {
201 struct mitsu98xx_data superfine;
202 struct mitsu98xx_data fine_std;
203 struct mitsu98xx_data fine_hg;
204 } __attribute__((packed));
205
206 /* Command header */
207 struct mitsu9550_cmd {
208 uint8_t cmd[4];
209 } __attribute__((packed));
210
211 /* Private data structure */
212 struct mitsu9550_printjob {
213 uint8_t *databuf;
214 uint32_t datalen;
215
216 uint16_t rows;
217 uint16_t cols;
218 uint32_t plane_len;
219 int is_raw;
220
221 int copies;
222
223 /* Parse headers separately */
224 struct mitsu9550_hdr1 hdr1;
225 int hdr1_present;
226 struct mitsu9550_hdr2 hdr2;
227 int hdr2_present;
228 struct mitsu9550_hdr3 hdr3;
229 int hdr3_present;
230 struct mitsu9550_hdr4 hdr4;
231 int hdr4_present;
232 };
233
234 struct mitsu9550_ctx {
235 struct libusb_device_handle *dev;
236 uint8_t endp_up;
237 uint8_t endp_down;
238 int type;
239 int is_s;
240 int is_98xx;
241
242 struct marker marker;
243
244 /* CP98xx stuff */
245 void *dl_handle;
246 lib70x_getapiversionFN GetAPIVersion;
247 Get3DColorTableFN Get3DColorTable;
248 Load3DColorTableFN Load3DColorTable;
249 Destroy3DColorTableFN Destroy3DColorTable;
250 DoColorConvFN DoColorConv;
251
252 struct CColorConv3D *lut;
253 struct mitsu98xx_tables *m98xxdata;
254 };
255
256 /* Printer data structures */
257 struct mitsu9550_media {
258 uint8_t hdr[2]; /* 24 2e */
259 uint8_t unk[12];
260 uint8_t type;
261 uint8_t unka[13];
262 uint16_t max; /* BE, prints per media */
263 uint8_t unkb[2];
264 uint16_t remain; /* BE, prints remaining */
265 uint8_t unkc[14];
266 } __attribute__((packed));
267
268 struct mitsu9550_status {
269 uint8_t hdr[2]; /* 30 2e */
270 uint8_t null[4];
271 uint8_t sts1; // MM
272 uint8_t nullb[1];
273 uint16_t copies; // BE, NN
274 uint8_t sts2; // ZZ (9600 only?)
275 uint8_t nullc[5];
276 uint8_t sts3; // QQ
277 uint8_t sts4; // RR
278 uint8_t sts5; // SS
279 uint8_t nulld[25];
280 uint8_t sts6; // TT
281 uint8_t sts7; // UU
282 uint8_t nulle[2];
283 } __attribute__((packed));
284
285 struct mitsu9550_status2 {
286 uint8_t hdr[2]; /* 21 2e / 24 2e on 9550/9800 */
287 uint8_t unk[40];
288 uint16_t remain; /* BE, media remaining */
289 uint8_t unkb[4]; /* 0a 00 00 01 */
290 } __attribute__((packed));
291
292 static int mitsu9550_main_loop(void *vctx, const void *vjob);
293
294 #define CMDBUF_LEN 64
295 #define READBACK_LEN 128
296
297 #define QUERY_STATUS() \
298 do {\
299 struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;\
300 /* struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf; */ \
301 struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf; \
302 uint16_t donor; \
303 /* media */ \
304 ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); \
305 if (ret < 0) \
306 return CUPS_BACKEND_FAILED; \
307 \
308 donor = be16_to_cpu(media->remain); \
309 if (donor != ctx->marker.levelnow) { \
310 ctx->marker.levelnow = donor; \
311 dump_markers(&ctx->marker, 1, 0); \
312 } \
313 /* Sanity-check media response */ \
314 if (media->remain == 0 || media->max == 0) { \
315 ERROR("Printer out of media!\n"); \
316 return CUPS_BACKEND_HOLD; \
317 } \
318 if (validate_media(ctx->type, media->type, job->cols, job->rows)) { \
319 ERROR("Incorrect media (%u) type for printjob (%ux%u)!\n", media->type, job->cols, job->rows); \
320 return CUPS_BACKEND_HOLD; \
321 } \
322 /* status2 */ \
323 ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); \
324 if (ret < 0) \
325 return CUPS_BACKEND_FAILED; \
326 /* status */ \
327 ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); \
328 if (ret < 0) \
329 return CUPS_BACKEND_FAILED; \
330 \
331 /* Make sure we're idle */ \
332 if (sts->sts5 != 0) { /* Printer ready for another job */ \
333 sleep(1); \
334 goto top; \
335 } \
336 /* Check for known errors */ \
337 if (sts->sts2 != 0) { \
338 ERROR("Printer cover open!\n"); \
339 return CUPS_BACKEND_STOP; \
340 } \
341 } while (0);
342
mitsu98xx_dogamma(uint8_t * src,uint16_t * dest,uint8_t plane,uint16_t * table,uint32_t len)343 static void mitsu98xx_dogamma(uint8_t *src, uint16_t *dest, uint8_t plane,
344 uint16_t *table, uint32_t len)
345 {
346 src += plane;
347 while(len--) {
348 *dest++ = cpu_to_be16(table[*src]);
349 src += 3;
350 }
351 }
352
mitsu98xx_fillmatte(struct mitsu9550_printjob * job)353 static int mitsu98xx_fillmatte(struct mitsu9550_printjob *job)
354 {
355 int fd, i;
356 uint32_t j, remain;
357
358 DEBUG("Reading %d bytes of matte data from disk (%d/%d)\n", job->cols * job->rows * 2, job->cols, LAMINATE_STRIDE);
359 fd = open(MITSU_M98xx_LAMINATE_FILE, O_RDONLY);
360 if (fd < 0) {
361 WARNING("Unable to open matte lamination data file '%s'\n", MITSU_M98xx_LAMINATE_FILE);
362 job->hdr1.matte = 0;
363 goto done;
364 }
365
366 /* Fill in the lamination plane header */
367 struct mitsu9550_plane *matte = (struct mitsu9550_plane *)(job->databuf + job->datalen);
368 matte->cmd[0] = 0x1b;
369 matte->cmd[1] = 0x5a;
370 matte->cmd[2] = 0x54;
371 matte->cmd[3] = 0x10;
372 matte->row_offset = 0;
373 matte->col_offset = 0;
374 matte->cols = cpu_to_be16(job->hdr1.cols);
375 matte->rows = cpu_to_be16(job->hdr1.rows);
376 job->datalen += sizeof(struct mitsu9550_plane);
377
378 /* Read in the matte data plane */
379 for (j = 0 ; j < job->rows ; j++) {
380 remain = LAMINATE_STRIDE * 2;
381
382 /* Read one row of lamination data at a time */
383 while (remain) {
384 i = read(fd, job->databuf + job->datalen, remain);
385 if (i < 0)
386 return CUPS_BACKEND_CANCEL;
387 if (i == 0) {
388 /* We hit EOF, restart from beginning */
389 lseek(fd, 0, SEEK_SET);
390 continue;
391 }
392 job->datalen += i;
393 remain -= i;
394 }
395 /* Back off the buffer so we "wrap" on the print row. */
396 job->datalen -= ((LAMINATE_STRIDE - job->cols) * 2);
397 }
398 /* We're done! */
399 close(fd);
400
401 /* Fill in the lamination plane footer */
402 job->databuf[job->datalen++] = 0x1b;
403 job->databuf[job->datalen++] = 0x50;
404 job->databuf[job->datalen++] = 0x56;
405 job->databuf[job->datalen++] = 0x00;
406
407 done:
408 return CUPS_BACKEND_OK;
409 }
410
411 #ifndef LUT_LEN
412 #define LUT_LEN 14739
413 #define COLORCONV_RGB 0
414 #define COLORCONV_BGR 1
415 #endif
416
417 static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int status, int status2, int media);
418 static char *mitsu9550_media_types(uint8_t type, uint8_t is_s);
419
mitsu9550_init(void)420 static void *mitsu9550_init(void)
421 {
422 struct mitsu9550_ctx *ctx = malloc(sizeof(struct mitsu9550_ctx));
423 if (!ctx) {
424 ERROR("Memory Allocation Failure!\n");
425 return NULL;
426 }
427 memset(ctx, 0, sizeof(struct mitsu9550_ctx));
428
429 DL_INIT();
430
431 return ctx;
432 }
433
mitsu9550_attach(void * vctx,struct libusb_device_handle * dev,int type,uint8_t endp_up,uint8_t endp_down,uint8_t jobid)434 static int mitsu9550_attach(void *vctx, struct libusb_device_handle *dev, int type,
435 uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
436 {
437 struct mitsu9550_ctx *ctx = vctx;
438 struct mitsu9550_media media;
439
440 UNUSED(jobid);
441
442 ctx->dev = dev;
443 ctx->endp_up = endp_up;
444 ctx->endp_down = endp_down;
445 ctx->type = type;
446
447 if (ctx->type == P_MITSU_9550S ||
448 ctx->type == P_MITSU_9800S)
449 ctx->is_s = 1;
450
451 if (ctx->type == P_MITSU_9800 ||
452 ctx->type == P_MITSU_9800S ||
453 ctx->type == P_MITSU_9810)
454 ctx->is_98xx = 1;
455
456 /* Attempt to open the library */
457 #if defined(WITH_DYNAMIC)
458 if (!ctx->is_98xx) goto skip;
459
460 DEBUG("Attempting to load image processing library\n");
461 ctx->dl_handle = DL_OPEN(LIB_NAME_RE);
462 if (!ctx->dl_handle)
463 WARNING("Image processing library not found, using internal fallback code\n");
464 if (ctx->dl_handle) {
465 ctx->GetAPIVersion = DL_SYM(ctx->dl_handle, "lib70x_getapiversion");
466 if (!ctx->GetAPIVersion) {
467 ERROR("Problem resolving API Version symbol in imaging processing library, too old or not installed?\n");
468 DL_CLOSE(ctx->dl_handle);
469 ctx->dl_handle = NULL;
470 return CUPS_BACKEND_FAILED;
471 }
472 if (ctx->GetAPIVersion() != REQUIRED_LIB_APIVERSION) {
473 ERROR("Image processing library API version mismatch!\n");
474 DL_CLOSE(ctx->dl_handle);
475 ctx->dl_handle = NULL;
476 return CUPS_BACKEND_FAILED;
477 }
478
479 ctx->Get3DColorTable = DL_SYM(ctx->dl_handle, "CColorConv3D_Get3DColorTable");
480 ctx->Load3DColorTable = DL_SYM(ctx->dl_handle, "CColorConv3D_Load3DColorTable");
481 ctx->Destroy3DColorTable = DL_SYM(ctx->dl_handle, "CColorConv3D_Destroy3DColorTable");
482 ctx->DoColorConv = DL_SYM(ctx->dl_handle, "CColorConv3D_DoColorConv");
483 if (!ctx->Get3DColorTable || !ctx->Load3DColorTable ||
484 !ctx->Destroy3DColorTable || !ctx->DoColorConv ) {
485 ERROR("Problem resolving symbols in imaging processing library\n");
486 DL_CLOSE(ctx->dl_handle);
487 ctx->dl_handle = NULL;
488 return CUPS_BACKEND_FAILED;
489 } else {
490 DEBUG("Image processing library successfully loaded\n");
491 }
492 }
493 skip:
494 #endif
495
496 if (test_mode < TEST_MODE_NOATTACH) {
497 if (mitsu9550_get_status(ctx, (uint8_t*) &media, 0, 0, 1))
498 return CUPS_BACKEND_FAILED;
499 } else {
500 int media_code = 0x2;
501 if (getenv("MEDIA_CODE"))
502 media_code = atoi(getenv("MEDIA_CODE")) & 0xf;
503
504 media.max = cpu_to_be16(400);
505 media.remain = cpu_to_be16(330);
506 media.type = media_code;
507 }
508
509 ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
510 ctx->marker.name = mitsu9550_media_types(media.type, ctx->is_s);
511 ctx->marker.levelmax = be16_to_cpu(media.max);
512 ctx->marker.levelnow = be16_to_cpu(media.remain);
513
514 return CUPS_BACKEND_OK;
515 }
516
mitsu9550_cleanup_job(const void * vjob)517 static void mitsu9550_cleanup_job(const void *vjob)
518 {
519 const struct mitsu9550_printjob *job = vjob;
520
521 if (job->databuf)
522 free(job->databuf);
523
524 free((void*)job);
525 }
526
mitsu9550_teardown(void * vctx)527 static void mitsu9550_teardown(void *vctx) {
528 struct mitsu9550_ctx *ctx = vctx;
529
530 if (!ctx)
531 return;
532
533 if (ctx->dl_handle) {
534 if (ctx->lut)
535 ctx->Destroy3DColorTable(ctx->lut);
536 if (ctx->m98xxdata)
537 free(ctx->m98xxdata);
538 DL_CLOSE(ctx->dl_handle);
539 }
540
541 free(ctx);
542 }
543
mitsu9550_read_parse(void * vctx,const void ** vjob,int data_fd,int copies)544 static int mitsu9550_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
545 struct mitsu9550_ctx *ctx = vctx;
546 uint8_t buf[sizeof(struct mitsu9550_hdr1)];
547 int remain, i;
548 uint32_t planelen = 0;
549
550 struct mitsu9550_printjob *job = NULL;
551
552 if (!ctx)
553 return CUPS_BACKEND_FAILED;
554
555 job = malloc(sizeof(*job));
556 if (!job) {
557 ERROR("Memory allocation failure!\n");
558 return CUPS_BACKEND_RETRY_CURRENT;
559 }
560 memset(job, 0, sizeof(*job));
561 job->is_raw = 1;
562
563 top:
564 /* Read in initial header */
565 remain = sizeof(buf);
566 while (remain > 0) {
567 i = read(data_fd, buf + sizeof(buf) - remain, remain);
568 if (i == 0) {
569 mitsu9550_cleanup_job(job);
570 return CUPS_BACKEND_CANCEL;
571 }
572 if (i < 0) {
573 mitsu9550_cleanup_job(job);
574 return CUPS_BACKEND_CANCEL;
575 }
576 remain -= i;
577 }
578
579 /* Sanity check */
580 if (buf[0] != 0x1b || buf[1] != 0x57 || buf[3] != 0x2e) {
581 if (!job->hdr1_present || !job->hdr2_present) {
582 ERROR("Unrecognized data format (%02x%02x%02x%02x)!\n",
583 buf[0], buf[1], buf[2], buf[3]);
584 mitsu9550_cleanup_job(job);
585 return CUPS_BACKEND_CANCEL;
586 } else if (buf[0] == 0x1b &&
587 buf[1] == 0x5a &&
588 buf[2] == 0x54) {
589
590 /* We're in the data portion now */
591 if (buf[3] == 0x10)
592 planelen *= 2;
593 else if (ctx->is_98xx && buf[3] == 0x80)
594 job->is_raw = 0;
595
596 goto hdr_done;
597 } else {
598 ERROR("Unrecognized data block (%02x%02x%02x%02x)!\n",
599 buf[0], buf[1], buf[2], buf[3]);
600 mitsu9550_cleanup_job(job);
601 return CUPS_BACKEND_CANCEL;
602 }
603 }
604
605 switch(buf[2]) {
606 case 0x20: /* header 1 */
607 memcpy(&job->hdr1, buf, sizeof(job->hdr1));
608 job->hdr1_present = 1;
609
610 /* Work out printjob size */
611 job->rows = be16_to_cpu(job->hdr1.rows);
612 job->cols = be16_to_cpu(job->hdr1.cols);
613 planelen = job->rows * job->cols;
614
615 break;
616 case 0x21: /* header 2 */
617 memcpy(&job->hdr2, buf, sizeof(job->hdr2));
618 job->hdr2_present = 1;
619 break;
620 case 0x22: /* header 3 */
621 memcpy(&job->hdr3, buf, sizeof(job->hdr3));
622 job->hdr3_present = 1;
623 break;
624 case 0x26: /* header 4 */
625 memcpy(&job->hdr4, buf, sizeof(job->hdr4));
626 job->hdr4_present = 1;
627 break;
628 default:
629 ERROR("Unrecognized header format (%02x)!\n", buf[2]);
630 mitsu9550_cleanup_job(job);
631 return CUPS_BACKEND_CANCEL;
632 }
633
634 /* Read in the next chunk */
635 goto top;
636
637 hdr_done:
638
639 /* Read in CP98xx data tables if necessary */
640 if (ctx->is_98xx && !job->is_raw && !ctx->m98xxdata) {
641 int ret;
642 ctx->m98xxdata = malloc(DATATABLE_SIZE);
643 if (!ctx->m98xxdata) {
644 ERROR("Memory allocation Failure!\n");
645 mitsu9550_cleanup_job(job);
646 return CUPS_BACKEND_RETRY_CURRENT;
647 }
648
649 DEBUG("Reading in 98xx data from disk\n");
650 if ((ret = dyesub_read_file(MITSU_M98xx_DATATABLE_FILE, ctx->m98xxdata, DATATABLE_SIZE, NULL))) {
651 ERROR("Unable to read 98xx data table file '%s'\n", MITSU_M98xx_DATATABLE_FILE);
652 free(ctx->m98xxdata);
653 return ret;
654 }
655
656 /* Byteswap data table to native endianness, if necessary */
657 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
658 int j;
659 struct mitsu98xx_data *ptr = &ctx->m98xxdata->superfine;
660 for (j = 0 ; j < 3 ; j++) {
661 for (i = 3 ; i < 3 ; i++) {
662 ptr->GammaAdj[i] = be64_to_cpu(ptr->GammaAdj[i]);
663 ptr->unk_kh[i] = be32_to_cpu(ptr->unk_kh[i]);
664 }
665 for (i = 0 ; i < 10 ; i++) {
666 ptr->WMAM.unkc[i] = be32_to_cpu(ptr->WMAM.unkc[i]);
667 ptr->WMAM.unkf[i] = be32_to_cpu(ptr->WMAM.unkf[i]);
668 }
669 for (i = 0 ; i < 11 ; i++) {
670 ptr->sharp_coef[i] = be64_to_cpu(ptr->sharp_coef[i]);
671 }
672 for (i = 0 ; i < 20 ; i++) {
673 ptr->unk_sharp[i] = be16_to_cpu(ptr->unk_sharp[i]);
674 }
675 for (i = 0 ; i < 256 ; i++) {
676 ptr->WMAM.unka[i] = be64_to_cpu(ptr->WMAM.unka[i]);
677 ptr->WMAM.unkb[i] = be64_to_cpu(ptr->WMAM.unkb[i]);
678 ptr->WMAM.unkd[i] = be64_to_cpu(ptr->WMAM.unkd[i]);
679 ptr->WMAM.unke[i] = be64_to_cpu(ptr->WMAM.unke[i]);
680 ptr->WMAM.unkg[i] = be64_to_cpu(ptr->WMAM.unkg[i]);
681
682 ptr->GNMby[i] = be16_to_cpu(ptr->GNMby[i]);
683 ptr->GNMgm[i] = be16_to_cpu(ptr->GNMgm[i]);
684 ptr->GNMrc[i] = be16_to_cpu(ptr->GNMrc[i]);
685
686 }
687 // XXX TODO: KH[2048]
688 ptr++;
689 }
690 #endif
691 }
692
693 if (job->is_raw) {
694 /* We have three planes + headers and the final terminator to read */
695 remain = 3 * (planelen + sizeof(struct mitsu9550_plane)) + sizeof(struct mitsu9550_cmd);
696 } else {
697 /* We have one plane + header and the final terminator to read */
698 remain = planelen * 3 + sizeof(struct mitsu9550_plane) + sizeof(struct mitsu9550_cmd);
699 }
700
701 /* Mitsu9600 windows spool uses more, smaller blocks, but plane data is the same */
702 if (ctx->type == P_MITSU_9600) {
703 remain += 128 * sizeof(struct mitsu9550_plane); /* 39 extra seen on 4x6" */
704 }
705
706 /* 9550S/9800S doesn't typically sent over hdr4! */
707 if (ctx->type == P_MITSU_9550S ||
708 ctx->type == P_MITSU_9800S) {
709 /* XXX Has to do with error policy, but not sure what.
710 Mitsu9550-S/9800-S will set this based on a command,
711 but it's not part of the standard job spool */
712 job->hdr4_present = 0;
713 }
714
715 /* Disable matte if the printer doesn't support it */
716 if (job->hdr1.matte) {
717 if (ctx->type != P_MITSU_9810) {
718 WARNING("Matte not supported on this printer, disabling\n");
719 job->hdr1.matte = 0;
720 } else if (job->is_raw) {
721 remain += planelen + sizeof(struct mitsu9550_plane) + sizeof(struct mitsu9550_cmd);
722 }
723 }
724
725 /* Allocate buffer for the payload */
726 job->datalen = 0;
727 job->databuf = malloc(remain);
728 if (!job->databuf) {
729 ERROR("Memory allocation failure!\n");
730 mitsu9550_cleanup_job(job);
731 return CUPS_BACKEND_RETRY_CURRENT;
732 }
733
734 /* Load up the data blocks.*/
735 while(1) {
736 /* Note that 'buf' needs to be already filled here! */
737 struct mitsu9550_plane *plane = (struct mitsu9550_plane *)buf;
738
739 /* Sanity check header... */
740 if (plane->cmd[0] != 0x1b ||
741 plane->cmd[1] != 0x5a ||
742 plane->cmd[2] != 0x54) {
743 ERROR("Unrecognized data read (%02x%02x%02x%02x)!\n",
744 plane->cmd[0], plane->cmd[1], plane->cmd[2], plane->cmd[3]);
745 mitsu9550_cleanup_job(job);
746 return CUPS_BACKEND_CANCEL;
747 }
748
749 /* Work out the length of this block */
750 planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols);
751 if (plane->cmd[3] == 0x10)
752 planelen *= 2;
753 if (plane->cmd[3] == 0x80)
754 planelen *= 3;
755
756 /* Copy plane header into buffer */
757 memcpy(job->databuf + job->datalen, buf, sizeof(buf));
758 job->datalen += sizeof(buf);
759 planelen -= sizeof(buf) - sizeof(struct mitsu9550_plane);
760
761 /* Read in the spool data */
762 while(planelen > 0) {
763 i = read(data_fd, job->databuf + job->datalen, planelen);
764 if (i == 0) {
765 mitsu9550_cleanup_job(job);
766 return CUPS_BACKEND_CANCEL;
767 }
768 if (i < 0) {
769 mitsu9550_cleanup_job(job);
770 return CUPS_BACKEND_CANCEL;
771 }
772 job->datalen += i;
773 planelen -= i;
774 }
775
776 /* Try to read in the next chunk. It will be one of:
777 - Additional block header (12B)
778 - Job footer (4B)
779 */
780 i = read(data_fd, buf, 4);
781 if (i == 0) {
782 mitsu9550_cleanup_job(job);
783 return CUPS_BACKEND_CANCEL;
784 }
785 if (i < 0) {
786 mitsu9550_cleanup_job(job);
787 return CUPS_BACKEND_CANCEL;
788 }
789
790 /* Is this a "job end" marker? */
791 if (plane->cmd[0] == 0x1b &&
792 plane->cmd[1] == 0x50 &&
793 plane->cmd[3] == 0x00) {
794 /* store it in the buffer */
795 memcpy(job->databuf + job->datalen, buf, 4);
796 job->datalen += 4;
797
798 /* Unless we have a raw matte plane following,
799 we're done */
800 if (job->hdr1.matte != 0x01 ||
801 !job->is_raw)
802 break;
803 remain = sizeof(buf);
804 } else {
805 /* It's part of a block header, mark what we've read */
806 remain = sizeof(buf) - 4;
807 }
808
809 /* Read in the rest of the header */
810 while (remain > 0) {
811 i = read(data_fd, buf + sizeof(buf) - remain, remain);
812 if (i == 0) {
813 mitsu9550_cleanup_job(job);
814 return CUPS_BACKEND_CANCEL;
815 }
816 if (i < 0) {
817 mitsu9550_cleanup_job(job);
818 return CUPS_BACKEND_CANCEL;
819 }
820 remain -= i;
821 }
822 }
823
824 /* Apply LUT, if job calls for it.. */
825 if (ctx->is_98xx && !job->is_raw && job->hdr2.unkc[9]) {
826
827 if (!ctx->dl_handle) {
828 // XXXFALLBACK write fallback code?
829 ERROR("!!! Image Processing Library not found, aborting!\n");
830 mitsu9550_cleanup_job(job);
831 return CUPS_BACKEND_CANCEL;
832 }
833
834 if (!ctx->lut) {
835 uint8_t *lbuf = malloc(LUT_LEN);
836 if (!lbuf) {
837 ERROR("Memory allocation failure!\n");
838 mitsu9550_cleanup_job(job);
839 return CUPS_BACKEND_RETRY_CURRENT;
840 }
841 if (ctx->Get3DColorTable(lbuf, MITSU_M98xx_LUT_FILE)) {
842 ERROR("Unable to open LUT file '%s'\n", MITSU_M98xx_LUT_FILE);
843 mitsu9550_cleanup_job(job);
844 return CUPS_BACKEND_CANCEL;
845 }
846 ctx->lut = ctx->Load3DColorTable(lbuf);
847 free(lbuf);
848 if (!ctx->lut) {
849 ERROR("Unable to parse LUT\n");
850 mitsu9550_cleanup_job(job);
851 return CUPS_BACKEND_CANCEL;
852 }
853 }
854
855 DEBUG("Running print data through 3D LUT\n");
856 ctx->DoColorConv(ctx->lut, job->databuf + sizeof(struct mitsu9550_plane),
857 job->cols, job->rows, job->cols * 3, COLORCONV_BGR);
858 job->hdr2.unkc[9] = 0;
859 }
860
861 /* Update printjob header to reflect number of requested copies */
862 if (job->hdr2_present) {
863 copies = 1;
864 job->hdr2.copies = cpu_to_be16(copies);
865 }
866 job->copies = copies;
867
868 /* All further work is in main loop */
869 if (test_mode >= TEST_MODE_NOPRINT)
870 mitsu9550_main_loop(ctx, job);
871
872 *vjob = job;
873
874 return CUPS_BACKEND_OK;
875 }
876
mitsu9550_get_status(struct mitsu9550_ctx * ctx,uint8_t * resp,int status,int status2,int media)877 static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int status, int status2, int media)
878 {
879 struct mitsu9550_cmd cmd;
880 int num, ret;
881
882 /* Send Printer Query */
883 cmd.cmd[0] = 0x1b;
884 cmd.cmd[1] = 0x56;
885 if (status)
886 cmd.cmd[2] = 0x30;
887 else if (status2)
888 cmd.cmd[2] = 0x21;
889 else if (media)
890 cmd.cmd[2] = 0x24;
891 cmd.cmd[3] = 0x00;
892 if ((ret = send_data(ctx->dev, ctx->endp_down,
893 (uint8_t*) &cmd, sizeof(cmd))))
894 return ret;
895 ret = read_data(ctx->dev, ctx->endp_up,
896 resp, sizeof(struct mitsu9550_status), &num);
897
898 if (ret < 0)
899 return ret;
900 if (num != sizeof(struct mitsu9550_status)) {
901 ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(struct mitsu9550_status));
902 return 4;
903 }
904
905 return 0;
906 }
907
mitsu9550_media_types(uint8_t type,uint8_t is_s)908 static char *mitsu9550_media_types(uint8_t type, uint8_t is_s)
909 {
910 if (is_s) {
911 switch (type & 0xf) { /* values can be 0x0? or 0x4? */
912 case 0x02:
913 return "CK9015 (4x6)";
914 case 0x04:
915 return "CK9318 (5x7)";
916 case 0x05:
917 return "CK9523 (6x9)";
918 default:
919 return "Unknown";
920 }
921 return NULL;
922 }
923
924 switch (type & 0xf) { /* values can be 0x0? or 0x4? */
925 case 0x01:
926 return "CK9035 (3.5x5)";
927 case 0x02:
928 return "CK9046 (4x6)";
929 case 0x03:
930 return "CK9046PST (4x6)";
931 case 0x04:
932 return "CK9057 (5x7)";
933 case 0x05:
934 return "CK9069 (6x9)";
935 case 0x06:
936 return "CK9068 (6x8)";
937 default:
938 return "Unknown";
939 }
940 return NULL;
941 }
942
validate_media(int type,int media,int cols,int rows)943 static int validate_media(int type, int media, int cols, int rows)
944 {
945 switch(type) {
946 case P_MITSU_9550:
947 switch(media & 0xf) {
948 case 0x01: /* 3.5x5 */
949 if (cols != 1812 && rows != 1240)
950 return 1;
951 break;
952 case 0x02: /* 4x6 */
953 case 0x03: /* 4x6 postcard */
954 if (cols != 2152)
955 return 1;
956 if (rows != 1416 && rows != 1184 && rows != 1240)
957 return 1;
958 break;
959 case 0x04: /* 5x7 */
960 if (cols != 1812)
961 return 1;
962 if (rows != 1240 && rows != 2452)
963 return 1;
964 break;
965 case 0x05: /* 6x9 */
966 if (cols != 2152)
967 return 1;
968 if (rows != 1416 && rows != 2792 &&
969 rows != 2956 && rows != 3146)
970 return 1;
971 break;
972 case 0x06: /* V (6x8??) */
973 if (cols != 2152)
974 return 1;
975 if (rows != 1416 && rows != 2792)
976 return 1;
977 break;
978 default: /* Unknown */
979 WARNING("Unknown media type %02x\n", media);
980 break;
981 }
982 break;
983 case P_MITSU_9550S:
984 switch(media & 0xf) {
985 case 0x02: /* 4x6 */
986 case 0x03: /* 4x6 postcard */
987 if (cols != 2152)
988 return 1;
989 if (rows != 1416 && rows != 1184 && rows != 1240)
990 return 1;
991 break;
992 case 0x04: /* 5x7 */
993 if (cols != 1812 && rows != 2452)
994 return 1;
995 break;
996 case 0x05: /* 6x9 */
997 if (cols != 2152)
998 return 1;
999 if (rows != 1416 && rows != 2792 &&
1000 rows != 2956 && rows != 3146)
1001 return 1;
1002 break;
1003 case 0x06: /* V (6x8??) */
1004 if (cols != 2152)
1005 return 1;
1006 if (rows != 1416 && rows != 2792)
1007 return 1;
1008 break;
1009 default: /* Unknown */
1010 WARNING("Unknown media type %02x\n", media);
1011 break;
1012 }
1013 break;
1014 case P_MITSU_9600: // XXX 9600S doesn't support 5" media at all!
1015 switch(media & 0xf) {
1016 case 0x01: /* 3.5x5 */
1017 if (cols == 1572) {
1018 if (rows == 1076)
1019 break;
1020 } else if (cols == 3144) {
1021 if (rows == 2152)
1022 break;
1023 }
1024 return 1;
1025 case 0x02: /* 4x6 */
1026 case 0x03: /* 4x6 postcard */
1027 if (cols == 1868) {
1028 if (rows == 1228)
1029 break;
1030 } else if (cols == 3736) {
1031 if (rows == 2458)
1032 break;
1033 }
1034 return 1;
1035 case 0x04: /* 5x7 */
1036 if (cols == 1572) {
1037 if (rows == 1076 || rows == 2128)
1038 break;
1039 } else if (cols == 3144) {
1040 if (rows == 2152 || rows == 4256)
1041 break;
1042 }
1043 return 1;
1044 case 0x05: /* 6x9 */
1045 if (cols == 1868) {
1046 if (rows == 1228 || rows == 2442 || rows == 2564 || rows == 2730)
1047 break;
1048 } else if (cols == 3736) {
1049 if (rows == 2458 || rows == 4846 || rows == 5130 || rows == 5462)
1050 break;
1051 }
1052 return 1;
1053 case 0x06: /* V (6x8??) */
1054 if (cols == 1868) {
1055 if (rows == 1228 || rows == 2442)
1056 break;
1057 } else if (cols == 3736) {
1058 if (rows == 2458 || rows == 4846)
1059 break;
1060 }
1061 return 1;
1062 default: /* Unknown */
1063 WARNING("Unknown media type %02x\n", media);
1064 break;
1065 }
1066 break;
1067 case P_MITSU_9800:
1068 case P_MITSU_9810: // XXX and don't forget the 9820S
1069 switch(media & 0xf) {
1070 case 0x01: /* 3.5x5 */
1071 if (cols != 1572 && rows != 1076)
1072 return 1;
1073 break;
1074 case 0x02: /* 4x6 */
1075 case 0x03: /* 4x6 postcard */
1076 if (cols != 1868 && rows != 1228)
1077 return 1;
1078 break;
1079 case 0x04: /* 5x7 */
1080 if (cols != 1572 && rows != 2128)
1081 return 1;
1082 break;
1083 case 0x05: /* 6x9 */
1084 if (cols != 1868)
1085 return 1;
1086 if (rows != 1228 && rows != 2442 &&
1087 rows != 2564 && rows != 2730)
1088 return 1;
1089 break;
1090 case 0x06: /* V (6x8??) */
1091 if (cols != 1868)
1092 return 1;
1093 if (rows != 1228 && rows != 2442)
1094 return 1;
1095 break;
1096 default: /* Unknown */
1097 WARNING("Unknown media type %02x\n", media);
1098 break;
1099 }
1100 break;
1101 case P_MITSU_9800S:
1102 switch(media & 0xf) {
1103 case 0x02: /* 4x6 */
1104 case 0x03: /* 4x6 postcard */
1105 if (cols != 1868 && rows != 1228)
1106 return 1;
1107 break;
1108 case 0x04: /* 5x7 */
1109 if (cols != 1572 && rows != 2128)
1110 return 1;
1111 break;
1112 case 0x05: /* 6x9 */
1113 if (cols != 1868)
1114 return 1;
1115 if (rows != 1228 && rows != 2442 &&
1116 rows != 2564 && rows != 2730)
1117 return 1;
1118 break;
1119 case 0x06: /* V (6x8??) */
1120 if (cols != 1868)
1121 return 1;
1122 if (rows != 1228 && rows != 2442)
1123 return 1;
1124 break;
1125 default: /* Unknown */
1126 WARNING("Unknown media type %02x\n", media);
1127 break;
1128 }
1129 break;
1130 default:
1131 WARNING("Unknown printer type %d\n", type);
1132 break;
1133 }
1134 return 0;
1135 }
1136
mitsu9550_main_loop(void * vctx,const void * vjob)1137 static int mitsu9550_main_loop(void *vctx, const void *vjob) {
1138 struct mitsu9550_ctx *ctx = vctx;
1139 struct mitsu9550_cmd cmd;
1140 uint8_t rdbuf[READBACK_LEN];
1141 uint8_t *ptr;
1142
1143 int ret;
1144 #if 0
1145 int copies;
1146 #endif
1147
1148 // const struct mitsu9550_printjob *job = vjob;
1149 struct mitsu9550_printjob *job = (struct mitsu9550_printjob*) vjob; // XXX not good.
1150
1151 if (!ctx)
1152 return CUPS_BACKEND_FAILED;
1153 if (!job)
1154 return CUPS_BACKEND_FAILED;
1155
1156 /* Okay, let's do this thing */
1157 ptr = job->databuf;
1158
1159 #if 0
1160 /* If hdr2 is not present, we have to generate copies ourselves! */
1161 if (job->hdr2_present)
1162 copies = job->copies;
1163 // XXX..
1164 #endif
1165
1166 /* Do the 98xx processing here */
1167 if (!ctx->is_98xx || job->is_raw)
1168 goto bypass;
1169
1170 uint8_t *newbuf;
1171 uint32_t newlen = 0;
1172 struct mitsu98xx_data *table;
1173 int i, remain, planelen;
1174
1175 planelen = job->rows * job->cols * 2;
1176 remain = (job->hdr1.matte ? 4 : 3) * (planelen + sizeof(struct mitsu9550_plane)) + sizeof(struct mitsu9550_cmd) * (job->hdr1.matte? 2 : 1) + LAMINATE_STRIDE * 2;
1177 newbuf = malloc(remain);
1178 if (!newbuf) {
1179 ERROR("Memory allocation Failure!\n");
1180 return CUPS_BACKEND_RETRY_CURRENT;
1181 }
1182 switch (job->hdr2.mode) {
1183 case 0x80:
1184 table = &ctx->m98xxdata->superfine;
1185 break;
1186 case 0x11:
1187 table = &ctx->m98xxdata->fine_hg;
1188 job->hdr2.mode = 0x10;
1189 break;
1190 case 0x10:
1191 default:
1192 table = &ctx->m98xxdata->fine_std;
1193 break;
1194 }
1195
1196 DEBUG("Applying 8bpp->12bpp Gamma Correction\n");
1197 /* For B/Y plane */
1198 memcpy(newbuf + newlen, job->databuf, sizeof(struct mitsu9550_plane));
1199 newbuf[newlen + 3] = 0x10; /* ie 16bpp data */
1200 newlen += sizeof(struct mitsu9550_plane);
1201 mitsu98xx_dogamma(job->databuf + sizeof(struct mitsu9550_plane),
1202 (uint16_t*) (newbuf + newlen),
1203 0,
1204 table->GNMby,
1205 planelen / 2);
1206 newlen += planelen;
1207
1208 /* For G/M plane */
1209 memcpy(newbuf + newlen, job->databuf, sizeof(struct mitsu9550_plane));
1210 newbuf[newlen + 3] = 0x10; /* ie 16bpp data */
1211 newlen += sizeof(struct mitsu9550_plane);
1212 mitsu98xx_dogamma(job->databuf + sizeof(struct mitsu9550_plane),
1213 (uint16_t*) (newbuf + newlen),
1214 1,
1215 table->GNMgm,
1216 planelen / 2);
1217 newlen += planelen;
1218
1219 /* For R/C plane */
1220 memcpy(newbuf + newlen, job->databuf, sizeof(struct mitsu9550_plane));
1221 newbuf[newlen + 3] = 0x10; /* ie 16bpp data */
1222 newlen += sizeof(struct mitsu9550_plane);
1223 mitsu98xx_dogamma(job->databuf + sizeof(struct mitsu9550_plane),
1224 (uint16_t*) (newbuf + newlen),
1225 2,
1226 table->GNMrc,
1227 planelen / 2);
1228 newlen += planelen;
1229
1230 /* And finally, the job footer. */
1231 memcpy(newbuf + newlen, job->databuf + sizeof(struct mitsu9550_plane) + planelen/2 * 3, sizeof(struct mitsu9550_cmd));
1232 newlen += sizeof(struct mitsu9550_cmd);
1233
1234 /* Clean up, and move pointer to new buffer; */
1235 free(job->databuf);
1236 job->databuf = newbuf;
1237 job->datalen = newlen;
1238 ptr = job->databuf;
1239
1240 /* Now handle the matte plane generation */
1241 if (job->hdr1.matte) {
1242 if ((i = mitsu98xx_fillmatte(job))) {
1243 return i;
1244 }
1245 }
1246
1247 bypass:
1248 /* Bypass */
1249 if (test_mode >= TEST_MODE_NOPRINT)
1250 return CUPS_BACKEND_OK;
1251
1252 top:
1253 if (ctx->is_s) {
1254 int num;
1255
1256 /* Send "unknown 1" command */
1257 cmd.cmd[0] = 0x1b;
1258 cmd.cmd[1] = 0x53;
1259 cmd.cmd[2] = 0xc5;
1260 cmd.cmd[3] = 0x9d;
1261 if ((ret = send_data(ctx->dev, ctx->endp_down,
1262 (uint8_t*) &cmd, sizeof(cmd))))
1263 return CUPS_BACKEND_FAILED;
1264
1265 /* Send "unknown 2" command */
1266 cmd.cmd[0] = 0x1b;
1267 cmd.cmd[1] = 0x4b;
1268 cmd.cmd[2] = 0x7f;
1269 cmd.cmd[3] = 0x00;
1270 if ((ret = send_data(ctx->dev, ctx->endp_down,
1271 (uint8_t*) &cmd, sizeof(cmd))))
1272 return CUPS_BACKEND_FAILED;
1273
1274 ret = read_data(ctx->dev, ctx->endp_up,
1275 rdbuf, READBACK_LEN, &num);
1276 if (ret < 0)
1277 return CUPS_BACKEND_FAILED;
1278 // seen so far: eb 4b 7f 00 02 00 5e
1279 }
1280
1281 if (ctx->type == P_MITSU_9800S) {
1282 int num;
1283
1284 /* Send "unknown 3" command */
1285 cmd.cmd[0] = 0x1b;
1286 cmd.cmd[1] = 0x4b;
1287 cmd.cmd[2] = 0x01;
1288 cmd.cmd[3] = 0x00;
1289 if ((ret = send_data(ctx->dev, ctx->endp_down,
1290 (uint8_t*) &cmd, sizeof(cmd))))
1291 return CUPS_BACKEND_FAILED;
1292
1293 ret = read_data(ctx->dev, ctx->endp_up,
1294 rdbuf, READBACK_LEN, &num);
1295 if (ret < 0)
1296 return CUPS_BACKEND_FAILED;
1297 // seen so far: e4 4b 01 00 02 00 78
1298 }
1299
1300 QUERY_STATUS();
1301
1302 /* Now it's time for the actual print job! */
1303
1304 QUERY_STATUS();
1305
1306 /* Send printjob headers from spool data */
1307 if (job->hdr1_present)
1308 if ((ret = send_data(ctx->dev, ctx->endp_down,
1309 (uint8_t*) &job->hdr1, sizeof(job->hdr1))))
1310 return CUPS_BACKEND_FAILED;
1311 if (job->hdr2_present)
1312 if ((ret = send_data(ctx->dev, ctx->endp_down,
1313 (uint8_t*) &job->hdr2, sizeof(job->hdr2))))
1314 return CUPS_BACKEND_FAILED;
1315 if (job->hdr3_present)
1316 if ((ret = send_data(ctx->dev, ctx->endp_down,
1317 (uint8_t*) &job->hdr3, sizeof(job->hdr3))))
1318 return CUPS_BACKEND_FAILED;
1319 if (job->hdr4_present)
1320 if ((ret = send_data(ctx->dev, ctx->endp_down,
1321 (uint8_t*) &job->hdr4, sizeof(struct mitsu9550_hdr4))))
1322 return CUPS_BACKEND_FAILED;
1323
1324 if (ctx->is_s) {
1325 /* I think this a "clear memory' command...? */
1326 cmd.cmd[0] = 0x1b;
1327 cmd.cmd[1] = 0x5a;
1328 cmd.cmd[2] = 0x43;
1329 cmd.cmd[3] = 0x00;
1330
1331 if ((ret = send_data(ctx->dev, ctx->endp_down,
1332 (uint8_t*) &cmd, sizeof(cmd))))
1333 return CUPS_BACKEND_FAILED;
1334 }
1335
1336 /* Send over plane data */
1337 while(ptr < (job->databuf + job->datalen)) {
1338 struct mitsu9550_plane *plane = (struct mitsu9550_plane *)ptr;
1339 if (plane->cmd[0] != 0x1b ||
1340 plane->cmd[1] != 0x5a ||
1341 plane->cmd[2] != 0x54)
1342 break;
1343
1344 planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols);
1345 if (plane->cmd[3] == 0x10)
1346 planelen *= 2;
1347
1348 if ((ret = send_data(ctx->dev, ctx->endp_down,
1349 (uint8_t*) ptr, sizeof(struct mitsu9550_plane))))
1350 return CUPS_BACKEND_FAILED;
1351 ptr += sizeof(struct mitsu9550_plane);
1352 if ((ret = send_data(ctx->dev, ctx->endp_down,
1353 (uint8_t*) ptr, planelen)))
1354 return CUPS_BACKEND_FAILED;
1355 ptr += planelen;
1356 }
1357
1358 /* Query statuses */
1359 {
1360 struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;
1361 // struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf;
1362 struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf;
1363 uint16_t donor;
1364
1365 ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media
1366 if (ret < 0)
1367 return CUPS_BACKEND_FAILED;
1368
1369 donor = be16_to_cpu(media->remain);
1370 if (donor != ctx->marker.levelnow) {
1371 ctx->marker.levelnow = donor;
1372 dump_markers(&ctx->marker, 1, 0);
1373 }
1374 /* Sanity-check media response */
1375 if (media->remain == 0 || media->max == 0) {
1376 ERROR("Printer out of media!\n");
1377 return CUPS_BACKEND_HOLD;
1378 }
1379 ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2
1380 if (ret < 0)
1381 return CUPS_BACKEND_FAILED;
1382
1383 ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); // status
1384 if (ret < 0)
1385 return CUPS_BACKEND_FAILED;
1386
1387 /* Make sure we're ready to proceed */
1388 if (sts->sts5 != 0) {
1389 ERROR("Unexpected response (sts5 %02x)\n", sts->sts5);
1390 return CUPS_BACKEND_FAILED;
1391 }
1392 if (!(sts->sts3 & 0xc0)) {
1393 ERROR("Unexpected response (sts3 %02x)\n", sts->sts3);
1394 return CUPS_BACKEND_FAILED;
1395 }
1396 /* Check for known errors */
1397 if (sts->sts2 != 0) {
1398 ERROR("Printer cover open!\n");
1399 return CUPS_BACKEND_STOP;
1400 }
1401 }
1402
1403 /* Send "end data" command */
1404 if (ctx->type == P_MITSU_9550S) {
1405 /* Override spool, which may be wrong */
1406 cmd.cmd[0] = 0x1b;
1407 cmd.cmd[1] = 0x50;
1408 cmd.cmd[2] = 0x47;
1409 cmd.cmd[3] = 0x00;
1410 if ((ret = send_data(ctx->dev, ctx->endp_down,
1411 (uint8_t*) &cmd, sizeof(cmd))))
1412 return CUPS_BACKEND_FAILED;
1413 } else if (ctx->type == P_MITSU_9800S) {
1414 /* Override spool, which may be wrong */
1415 cmd.cmd[0] = 0x1b;
1416 cmd.cmd[1] = 0x50;
1417 cmd.cmd[2] = 0x4e;
1418 cmd.cmd[3] = 0x00;
1419 if ((ret = send_data(ctx->dev, ctx->endp_down,
1420 (uint8_t*) &cmd, sizeof(cmd))))
1421 return CUPS_BACKEND_FAILED;
1422 } else {
1423 /* Send from spool file */
1424 if ((ret = send_data(ctx->dev, ctx->endp_down,
1425 ptr, sizeof(cmd))))
1426 return CUPS_BACKEND_FAILED;
1427 ptr += sizeof(cmd);
1428 }
1429
1430 /* Don't forget the 9810's matte plane */
1431 if (job->hdr1.matte) {
1432 struct mitsu9550_plane *plane = (struct mitsu9550_plane *)ptr;
1433 planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols);
1434
1435 if (plane->cmd[3] == 0x10)
1436 planelen *= 2;
1437
1438 // XXX include a status loop here too?
1439 if ((ret = send_data(ctx->dev, ctx->endp_down,
1440 (uint8_t*) ptr, sizeof(struct mitsu9550_plane))))
1441 return CUPS_BACKEND_FAILED;
1442 ptr += sizeof(struct mitsu9550_plane);
1443 if ((ret = send_data(ctx->dev, ctx->endp_down,
1444 (uint8_t*) ptr, planelen)))
1445 return CUPS_BACKEND_FAILED;
1446 ptr += planelen;
1447
1448 /* Send "lamination end data" command from spool file */
1449 if ((ret = send_data(ctx->dev, ctx->endp_down,
1450 ptr, sizeof(cmd))))
1451 return CUPS_BACKEND_FAILED;
1452 // ptr += sizeof(cmd);
1453 }
1454
1455 /* Status loop, run until printer reports completion */
1456 while(1) {
1457 struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;
1458 // struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf;
1459 struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf;
1460 uint16_t donor;
1461
1462 ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media
1463 if (ret < 0)
1464 return CUPS_BACKEND_FAILED;
1465
1466 donor = be16_to_cpu(media->remain);
1467 if (donor != ctx->marker.levelnow) {
1468 ctx->marker.levelnow = donor;
1469 dump_markers(&ctx->marker, 1, 0);
1470 }
1471 /* Sanity-check media response */
1472 if (media->remain == 0 || media->max == 0) {
1473 ERROR("Printer out of media!\n");
1474 return CUPS_BACKEND_HOLD;
1475 }
1476 ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2
1477 if (ret < 0)
1478 return CUPS_BACKEND_FAILED;
1479
1480 ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); // status
1481 if (ret < 0)
1482 return CUPS_BACKEND_FAILED;
1483
1484 INFO("%03d copies remaining\n", be16_to_cpu(sts->copies));
1485
1486 if (!sts->sts1) /* If printer transitions to idle */
1487 break;
1488
1489 if (fast_return && !be16_to_cpu(sts->copies)) { /* No remaining prints */
1490 INFO("Fast return mode enabled.\n");
1491 break;
1492 }
1493
1494 if (fast_return && !sts->sts5) { /* Ready for another job */
1495 INFO("Fast return mode enabled.\n");
1496 break;
1497 }
1498 /* Check for known errors */
1499 if (sts->sts2 != 0) {
1500 ERROR("Printer cover open!\n");
1501 return CUPS_BACKEND_STOP;
1502 }
1503 sleep(1);
1504 }
1505
1506 INFO("Print complete\n");
1507
1508 return CUPS_BACKEND_OK;
1509 }
1510
mitsu9550_dump_media(struct mitsu9550_media * resp,int is_s)1511 static void mitsu9550_dump_media(struct mitsu9550_media *resp, int is_s)
1512 {
1513 INFO("Media type : %02x (%s)\n",
1514 resp->type, mitsu9550_media_types(resp->type, is_s));
1515 INFO("Media remaining : %03d/%03d\n",
1516 be16_to_cpu(resp->remain), be16_to_cpu(resp->max));
1517 }
1518
mitsu9550_dump_status(struct mitsu9550_status * resp)1519 static void mitsu9550_dump_status(struct mitsu9550_status *resp)
1520 {
1521 INFO("Printer status : %02x (%s)\n",
1522 resp->sts1, resp->sts1 ? "Printing": "Idle");
1523 INFO("Pages remaining : %03d\n",
1524 be16_to_cpu(resp->copies));
1525 INFO("Other status : %02x %02x %02x %02x %02x %02x\n",
1526 resp->sts2, resp->sts3, resp->sts4,
1527 resp->sts5, resp->sts6, resp->sts7);
1528 }
1529
mitsu9550_dump_status2(struct mitsu9550_status2 * resp)1530 static void mitsu9550_dump_status2(struct mitsu9550_status2 *resp)
1531 {
1532 INFO("Prints remaining on media : %03d\n",
1533 be16_to_cpu(resp->remain));
1534 }
1535
mitsu9550_query_media(struct mitsu9550_ctx * ctx)1536 static int mitsu9550_query_media(struct mitsu9550_ctx *ctx)
1537 {
1538 struct mitsu9550_media resp;
1539 int ret;
1540
1541 ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, 0, 0, 1);
1542
1543 if (!ret)
1544 mitsu9550_dump_media(&resp, ctx->is_s);
1545
1546 return ret;
1547 }
1548
mitsu9550_query_status(struct mitsu9550_ctx * ctx)1549 static int mitsu9550_query_status(struct mitsu9550_ctx *ctx)
1550 {
1551 struct mitsu9550_status resp;
1552 int ret;
1553
1554 ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, 1, 0, 0);
1555
1556 if (!ret)
1557 mitsu9550_dump_status(&resp);
1558
1559 return ret;
1560 }
1561
mitsu9550_query_status2(struct mitsu9550_ctx * ctx)1562 static int mitsu9550_query_status2(struct mitsu9550_ctx *ctx)
1563 {
1564 struct mitsu9550_status2 resp;
1565 int ret;
1566
1567 ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, 0, 1, 0);
1568
1569 if (!ret)
1570 mitsu9550_dump_status2(&resp);
1571
1572 return ret;
1573 }
1574
mitsu9550_query_serno(struct libusb_device_handle * dev,uint8_t endp_up,uint8_t endp_down,char * buf,int buf_len)1575 static int mitsu9550_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len)
1576 {
1577 struct mitsu9550_cmd cmd;
1578 uint8_t rdbuf[READBACK_LEN];
1579 uint8_t *ptr;
1580 int ret, num, i;
1581
1582 cmd.cmd[0] = 0x1b;
1583 cmd.cmd[1] = 0x72;
1584 cmd.cmd[2] = 0x6e;
1585 cmd.cmd[3] = 0x00;
1586
1587 if ((ret = send_data(dev, endp_down,
1588 (uint8_t*) &cmd, sizeof(cmd))))
1589 return (ret < 0) ? ret : CUPS_BACKEND_FAILED;
1590
1591 ret = read_data(dev, endp_up,
1592 rdbuf, READBACK_LEN, &num);
1593
1594 if (ret < 0)
1595 return CUPS_BACKEND_FAILED;
1596
1597 if ((unsigned int)num < sizeof(cmd) + 1) /* Short read */
1598 return CUPS_BACKEND_FAILED;
1599
1600 if (rdbuf[0] != 0xe4 ||
1601 rdbuf[1] != 0x72 ||
1602 rdbuf[2] != 0x6e ||
1603 rdbuf[3] != 0x00) /* Bad response */
1604 return CUPS_BACKEND_FAILED;
1605
1606 /* If response is truncated, handle it */
1607 num -= (sizeof(cmd) + 1);
1608 if ((unsigned int) num != rdbuf[4])
1609 WARNING("Short serno read! (%d vs %u)\r\n",
1610 num, rdbuf[4]);
1611
1612 /* model and serial number are encoded as 16-bit unicode,
1613 little endian, separated by spaces. */
1614 i = num;
1615 ptr = rdbuf + 5;
1616 while (i > 0 && buf_len > 1) {
1617 if (*ptr != 0x20)
1618 *buf++ = *ptr;
1619 buf_len--;
1620 ptr += 2;
1621 i -= 2;
1622 }
1623 *buf = 0; /* Null-terminate the returned string */
1624
1625 return ret;
1626 }
1627
mitsu9550_cancel_job(struct mitsu9550_ctx * ctx)1628 static int mitsu9550_cancel_job(struct mitsu9550_ctx *ctx)
1629 {
1630 int ret;
1631
1632 uint8_t buf[2] = { 0x1b, 0x44 };
1633 ret = send_data(ctx->dev, ctx->endp_down, buf, sizeof(buf));
1634
1635 return ret;
1636 }
1637
mitsu9550_cmdline(void)1638 static void mitsu9550_cmdline(void)
1639 {
1640 DEBUG("\t\t[ -m ] # Query media\n");
1641 DEBUG("\t\t[ -s ] # Query status\n");
1642 DEBUG("\t\t[ -X ] # Cancel current job\n");
1643 }
1644
mitsu9550_cmdline_arg(void * vctx,int argc,char ** argv)1645 static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv)
1646 {
1647 struct mitsu9550_ctx *ctx = vctx;
1648 int i, j = 0;
1649
1650 if (!ctx)
1651 return -1;
1652
1653 while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "msX")) >= 0) {
1654 switch(i) {
1655 GETOPT_PROCESS_GLOBAL
1656 case 'm':
1657 j = mitsu9550_query_media(ctx);
1658 break;
1659 case 's':
1660 j = mitsu9550_query_status(ctx);
1661 if (!j)
1662 j = mitsu9550_query_status2(ctx);
1663 break;
1664 case 'X':
1665 j = mitsu9550_cancel_job(ctx);
1666 break;
1667 default:
1668 break; /* Ignore completely */
1669 }
1670
1671 if (j) return j;
1672 }
1673
1674 return 0;
1675 }
1676
mitsu9550_query_markers(void * vctx,struct marker ** markers,int * count)1677 static int mitsu9550_query_markers(void *vctx, struct marker **markers, int *count)
1678 {
1679 struct mitsu9550_ctx *ctx = vctx;
1680 struct mitsu9550_media media;
1681
1682 /* Query printer status */
1683 if (mitsu9550_get_status(ctx, (uint8_t*) &media, 0, 0, 1))
1684 return CUPS_BACKEND_FAILED;
1685
1686 ctx->marker.levelnow = be16_to_cpu(media.remain);
1687
1688 *markers = &ctx->marker;
1689 *count = 1;
1690
1691 return CUPS_BACKEND_OK;
1692 }
1693
1694 static const char *mitsu9550_prefixes[] = {
1695 "mitsu9xxx", // Family driver, do not nuke.
1696 "mitsubishi-9000dw", "mitsubishi-9500dw",
1697 "mitsubishi-9550dw", "mitsubishi-9550dw-s",
1698 "mitsubishi-9600dw", // "mitsubishi-9600dw-s",
1699 "mitsubishi-9800dw", "mitsubishi-9800dw-s",
1700 "mitsubishi-9810dw",
1701 // extras
1702 "mitsubishi-9550d", "mitsubishi-9550dz", "mitsubishi-9800d", "mitsubishi-9800dz", "mitsubishi-9810d",
1703 // Backwards compatibility
1704 "mitsu9000", "mitsu9500", "mitsu9550", "mitsu9600", "mitsu9800", "mitsu9810",
1705 NULL
1706 };
1707
1708 /* Exported */
1709 struct dyesub_backend mitsu9550_backend = {
1710 .name = "Mitsubishi CP9xxx family",
1711 .version = "0.47",
1712 .uri_prefixes = mitsu9550_prefixes,
1713 .cmdline_usage = mitsu9550_cmdline,
1714 .cmdline_arg = mitsu9550_cmdline_arg,
1715 .init = mitsu9550_init,
1716 .attach = mitsu9550_attach,
1717 .teardown = mitsu9550_teardown,
1718 .cleanup_job = mitsu9550_cleanup_job,
1719 .read_parse = mitsu9550_read_parse,
1720 .main_loop = mitsu9550_main_loop,
1721 .query_serno = mitsu9550_query_serno,
1722 .query_markers = mitsu9550_query_markers,
1723 .devices = {
1724 { USB_VID_MITSU, USB_PID_MITSU_9000AM, P_MITSU_9550, NULL, "mitsubishi-9000dw"}, // XXX -am instead?
1725 { USB_VID_MITSU, USB_PID_MITSU_9000D, P_MITSU_9550, NULL, "mitsubishi-9000dw"},
1726 { USB_VID_MITSU, USB_PID_MITSU_9500D, P_MITSU_9550, NULL, "mitsubishi-9500dw"},
1727 { USB_VID_MITSU, USB_PID_MITSU_9550D, P_MITSU_9550, NULL, "mitsubishi-9550dw"},
1728 { USB_VID_MITSU, USB_PID_MITSU_9550DS, P_MITSU_9550S, NULL, "mitsubishi-9550dw-s"},
1729 { USB_VID_MITSU, USB_PID_MITSU_9600D, P_MITSU_9600, NULL, "mitsubishi-9600dw"},
1730 // { USB_VID_MITSU, USB_PID_MITSU_9600D, P_MITSU_9600S, NULL, "mitsubishi-9600dw-s"},
1731 { USB_VID_MITSU, USB_PID_MITSU_9800D, P_MITSU_9800, NULL, "mitsubishi-9800dw"},
1732 { USB_VID_MITSU, USB_PID_MITSU_9800DS, P_MITSU_9800S, NULL, "mitsubishi-9800dw-s"},
1733 { USB_VID_MITSU, USB_PID_MITSU_98__D, P_MITSU_9810, NULL, "mitsubishi-9810dw"},
1734 // { USB_VID_MITSU, USB_PID_MITSU_9810D, P_MITSU_9810, NULL, "mitsubishi-9810dw"},
1735 // { USB_VID_MITSU, USB_PID_MITSU_9820DS, P_MITSU_9820S, NULL, "mitsubishi-9820dw-s"},
1736 { 0, 0, 0, NULL, NULL}
1737 }
1738 };
1739
1740 /* Mitsubish CP-9500/9550/9600/9800/9810/9820 spool format:
1741
1742 Spool file consists of 3 (or 4) 50-byte headers, followed by three
1743 image planes, each with a 12-byte header, then a 4-byte footer.
1744
1745 All multi-byte numbers are big endian.
1746
1747 ~~~ Header 1
1748
1749 1b 57 20 2e 00 QQ QQ 00 00 00 00 00 00 00 XX XX :: XX XX == columns
1750 YY YY 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: YY YY == rows
1751 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: QQ == 0x0a90 on 9810, 0x0a10 on all others.
1752 00 00
1753
1754 ~~~ Header 2
1755
1756 1b 57 21 2e 00 80 00 22 QQ QQ 00 00 00 00 00 00 :: ZZ ZZ = num copies (>= 0x01)
1757 00 00 00 00 00 00 00 00 00 00 00 00 ZZ ZZ 00 00 :: YY = 00/80 Fine/SuperFine (9550), 10/80 Fine/Superfine (98x0), 00 (9600)
1758 XX 00 00 00 00 00 YY 00 00 00 00 00 00 00 00 00 :: XX = 00 normal, 83 Cut 2x6 (9550 only!)
1759 RR 01 :: QQ QQ = 0x0803 on 9550, 0x0801 on 98x0, 0x0003 on 9600, 0xa803 on 9500
1760 :: RR = 01 for "use LUT" on 98xx, 0x00 otherwise. Extension to stock.
1761
1762 ~~~ Header 3 (9550 and 9800-S only..)
1763
1764 1b 57 22 2e 00 QQ 00 00 00 00 00 XX 00 00 00 00 :: XX = 00 normal, 01 FineDeep
1765 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: QQ = 0xf0 on 9500, 0x40 on the rest
1766 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1767 00 00
1768
1769 ~~~ Header 4 (all but 9550-S and 9800-S, involves error policy?)
1770
1771 1b 57 26 2e 00 QQ 00 00 00 00 00 SS RR 01 00 00 :: QQ = 0x70 on 9550/98x0, 0x60 on 9600 or 9800S
1772 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: RR = 0x01 on 9550/98x0, 0x00 on 9600
1773 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: SS = 0x01 on 9800S, 0x00 otherwise.
1774 00 00
1775
1776 ~~~~ Data follows:
1777
1778 Format is: planar YMC16 for 98x0 (but only 12 bits used, BIG endian)
1779 planar RGB for all others
1780
1781 1b 5a 54 ?? RR RR CC CC 07 14 04 d8 :: 0714 == columns, 04d8 == rows
1782 :: RRRR == row offset for data, CCCC == col offset for data
1783 :: ?? == 0x00 for 8bpp, 0x10 for 16/12bpp.
1784 :: 0x80 for PACKED BGR!
1785
1786 Data follows immediately, no padding.
1787
1788 1b 5a 54 00 00 00 00 00 07 14 04 d8 :: Another plane.
1789
1790 Data follows immediately, no padding.
1791
1792 1b 5a 54 00 00 00 00 00 07 14 04 d8 :: Another plane.
1793
1794 Data follows immediately, no padding.
1795
1796 ~~~~ Footer:
1797
1798 1b 50 57 00 (9500)
1799 1b 50 46 00 (9550)
1800 1b 50 47 00 (9550-S)
1801 1b 50 48 00 (9600)
1802 1b 50 4c 00 (9800/9810)
1803 1b 50 4e 00 (9800-S)
1804
1805 Unknown: 9600-S, 9820-S
1806
1807 ~~~~ Lamination data follows (on 9810 only, if matte selected)
1808
1809 1b 5a 54 10 00 00 00 00 06 24 04 34
1810
1811 Data follows immediately, no padding.
1812
1813 1b 50 56 00 (Lamination footer)
1814
1815 ~~~~ QUESTIONS:
1816
1817 * Lamination control?
1818 * Other 9550 multi-cut modes (on 6x9 media: 4x6*2, 4.4x6*2, 3x6*3, 2x6*4)
1819 * 9600/98x0 multi-cut modes?
1820
1821 ***********************************************************************
1822
1823 * Mitsubishi ** CP-9550DW-S/9800DW-S ** Communications Protocol:
1824
1825 [[ Unknown ]]
1826
1827 -> 1b 53 c5 9d
1828
1829 [[ Unknown, query some parameter? ]]
1830
1831 -> 1b 4b 7f 00
1832 <- eb 4b 8f 00 02 00 5e [[ '02' seems to be a length ]]
1833
1834 [[ Unknown ]]
1835
1836 -> 1b 53 00 00
1837
1838 Query Model & Serial number
1839
1840 -> 1b 72 6e 00
1841 <- e4 82 6e 00 LL 39 00 35 00 35 00 30 00 5a 00 20
1842 00 41 00 32 00 30 00 30 00 36 00 37 00
1843
1844 'LL' is length. Data is returned in 16-bit unicode, LE.
1845 Contents are model ('9550Z'), then space, then serialnum ('A20067')
1846
1847 Media Query
1848
1849 -> 1b 56 24 00
1850 <- 24 2e 00 00 00 00 00 00 00 00 00 00 00 00 TT 00 :: TT = Type
1851 00 00 00 00 00 00 00 00 00 00 00 00 MM MM 00 00 :: MM MM = Max prints
1852 NN NN 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: NN NN = Remaining
1853
1854 [[ unknown query, 9800-only ]]
1855
1856 -> 1b 4b 01 00
1857 <- e4 4b 01 00 02 00 78
1858
1859 Status Query
1860
1861 -> 1b 56 30 00
1862 -> 30 2e 00 00 00 00 MM 00 NN NN ZZ 00 00 00 00 00 :: MM, NN, ZZ
1863 QQ RR SS 00 00 00 00 00 00 00 00 00 00 00 00 00 :: QQ, RR, SS
1864 00 00 00 00 00 00 00 00 00 00 00 00 TT UU 00 00 :: TT, UU
1865
1866 Status Query B (not sure what to call this)
1867
1868 -> 1b 56 21 00
1869 <- 21 2e 00 80 00 22 a8 0b 00 00 00 00 00 00 00 00
1870 00 00 00 00 00 00 00 00 00 00 00 QQ 00 00 00 00 :: QQ == Prints in job?
1871 00 00 00 00 00 00 00 00 00 00 NN NN 0A 00 00 01 :: NN NN = Remaining media
1872
1873 [[ Job Cancel ]]
1874
1875 -> 1b 44
1876
1877 [[ Header 1 -- See above ]]
1878
1879 -> 1b 57 20 2e ....
1880
1881 [[ Header 2 -- See above ]]
1882
1883 -> 1b 57 21 2e ....
1884
1885 [[ Header 3 -- See above ]]
1886
1887 -> 1b 57 22 2e ....
1888
1889 [[ Unknown -- Start Data ? ]]
1890
1891 -> 1b 5a 43 00
1892
1893 [[ Plane header #1 (Blue) ]]
1894
1895 -> 1b 5a 54 00 00 00 00 00 XX XX YY YY :: XX XX == Columns, YY YY == Rows
1896
1897 Followed by image plane #1 (Blue), XXXX * YYYY bytes
1898
1899 [[ Plane header #2 (Green) ]]
1900
1901 -> 1b 5a 54 00 00 00 00 00 XX XX YY YY :: XX XX == Columns, YY YY == Rows
1902
1903 Followed by image plane #2 (Green), XXXX * YYYY bytes
1904
1905 [[ Plane header #3 (Red) ]]
1906
1907 -> 1b 5a 54 00 00 00 00 00 XX XX YY YY :: XX XX == Columns, YY YY == Rows
1908
1909 Followed by image plane #3 (Red), XXXX * YYYY bytes
1910
1911 [[ Footer -- End Data aka START print? See above for other models ]]
1912
1913 -> 1b 50 47 00 [9550S]
1914 -> 1b 50 4e 00 [9800S]
1915
1916 [[ At this point, loop status/status b/media queries until printer idle ]]
1917
1918 MM, QQ RR SS, TT UU
1919
1920 <- 00 3e 00 00 8a 44 :: Idle.
1921 00 7e 00 00 8a 44 :: Plane data submitted, pre "end data" cmd
1922 00 7e 40 01 8a 44 :: "end data" sent
1923 30 7e 40 01 8a 44
1924 38 7e 40 01 8a 44
1925 59 7e 40 01 8a 44
1926 59 7e 40 00 8a 44
1927 4d 7e 40 00 8a 44
1928 [...]
1929 43 7e 40 00 82 44
1930 [...]
1931 50 7e 40 00 80 44
1932 [...]
1933 31 7e 40 00 7d 44
1934 [...]
1935 00 3e 00 00 80 44 :: Idle.
1936
1937 Also seen:
1938
1939 00 3e 00 00 96 4b :: Idle
1940 00 be 00 00 96 4b :: Data submitted, pre "start"
1941 00 be 80 01 96 4b :: print start sent
1942 30 be 80 01 96 4c
1943 [...]
1944 30 be 80 01 89 4b
1945 38 be 80 01 8a 4b
1946 59 be 80 01 8b 4b
1947 [...]
1948 4d be 80 01 89 4b
1949 [...]
1950 43 be 80 01 89 4b
1951 [...]
1952 50 be 80 01 82 4b
1953 [...]
1954 31 be 80 01 80 4b
1955 [...]
1956
1957 Seen on 9600DW
1958
1959 ZZ == 08 Door open
1960
1961 Working theory of interpreting the status flags:
1962
1963 MM :: 00 is idle, else mechanical printer state.
1964 NN :: Remaining prints in job, or 0x00 0x00 when idle.
1965 QQ :: ?? 0x3e + 0x40 or 0x80 (see below)
1966 RR :: ?? 0x00 is idle, 0x40 or 0x80 is "printing"?
1967 SS :: ?? 0x00 means "ready for another print" but 0x01 is "busy"
1968 TT :: ?? seen values between 0x7c through 0x96)
1969 UU :: ?? seen values between 0x43 and 0x4c -- temperature?
1970
1971 ***
1972
1973 Other printer commands seen:
1974
1975 [[ Set error policy ?? aka "header 4" ]]
1976
1977 -> 1b 57 26 2e 00 QQ 00 00 00 00 00 00 RR SS 00 00 :: QQ/RR/SS 00 00 00 [9550S]
1978 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: 20 01 00 [9550S w/ ignore failures on]
1979 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: 70 01 01 [9550]
1980 00 00
1981
1982 */
1983