1 /*
2  *   Kodak 6800/6850 Photo Printer CUPS backend -- libusb-1.0 version
3  *
4  *   (c) 2013-2019 Solomon Peachy <pizza@shaftnet.org>
5  *
6  *   Development of this backend was sponsored by:
7  *
8  *     LiveLink Technology [ www.livelinktechnology.net ]
9  *
10  *   The latest version of this program can be found at:
11  *
12  *     http://git.shaftnet.org/cgit/selphy_print.git
13  *
14  *   This program is free software; you can redistribute it and/or modify it
15  *   under the terms of the GNU General Public License as published by the Free
16  *   Software Foundation; either version 2 of the License, or (at your option)
17  *   any later version.
18  *
19  *   This program is distributed in the hope that it will be useful, but
20  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
22  *   for more details.
23  *
24  *   You should have received a copy of the GNU General Public License
25  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
26  *
27  *          [http://www.gnu.org/licenses/gpl-2.0.html]
28  *
29  *   SPDX-License-Identifier: GPL-2.0+
30  *
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <signal.h>
42 
43 #define BACKEND kodak6800_backend
44 
45 #include "backend_common.h"
46 #include "backend_sinfonia.h"
47 
48 #define USB_VID_KODAK       0x040A
49 #define USB_PID_KODAK_6800  0x4021
50 #define USB_PID_KODAK_6850  0x402B
51 
52 /* File header */
53 struct kodak6800_hdr {
54 	uint8_t  hdr[7];   /* Always 03 1b 43 48 43 0a 00 */
55 	uint8_t  jobid;    /* Non-zero */
56 	uint16_t copies;   /* BE, in BCD format (1-9999) */
57 	uint16_t columns;  /* BE */
58 	uint16_t rows;     /* BE */
59 	uint8_t  size;     /* media size; 0x06 for 6x8, 0x00 for 6x4, 0x07 for 5x7 */
60 	uint8_t  laminate; /* 0x01 to laminate, 0x00 for not */
61 	uint8_t  method;     /* 0x00 or 0x01 (for 4x6 on 6x8 media), 0x21 for 2x6, 0x23 for 3x6 */
62 } __attribute__((packed));
63 
64 struct kodak68x0_status_readback {
65 	uint8_t  hdr;      /* Always 01 */
66 	uint8_t  status;   /* STATUS_* */
67 	uint8_t  status1;  /* STATUS1_* */
68 	uint32_t status2;  /* STATUS2_* */
69 	uint8_t  errcode;  /* Error ## */
70 	uint32_t lifetime; /* Lifetime Prints (BE) */
71 	uint32_t maint;    /* Maint Prints (BE) */
72 	uint32_t media;    /* Media Prints (6850), Unknown (6800) (BE) */
73 	uint32_t cutter;   /* Cutter Actuations (BE) */
74 	uint8_t  nullB[2];
75 	uint8_t  errtype;   /* seen 0x00 or 0xd0 */
76 	uint8_t  donor;     /* Percentage, 0-100 */
77 	uint16_t main_boot; /* Always 003 */
78 	uint16_t main_fw;   /* seen 6xx/8xx (6850) and 2xx/3xx/4xx (6800) */
79 	uint16_t dsp_boot;  /* Always 001 */
80 	uint16_t dsp_fw;    /* Seen 5xx (6850) and 1xx (6800) */
81 	uint8_t  b1_jobid;
82 	uint8_t  b2_jobid;
83 	uint16_t b1_remain;   /* Remaining prints in job */
84 	uint16_t b1_complete; /* Completed prints in job */
85 	uint16_t b1_total;    /* Total prints in job */
86 	uint16_t b2_remain;   /* Remaining prints in job */
87 	uint16_t b2_complete; /* Completed prints in job */
88 	uint16_t b2_total;    /* Total prints in job */
89 	uint8_t  curve_status; /* Always seems to be 0x00 */
90 } __attribute__((packed));
91 
92 #define MAX_MEDIAS 16
93 
94 struct kodak68x0_media_readback {
95 	uint8_t  hdr;      /* Always 0x01 */
96 	uint8_t  type;     /* Media code, KODAK68x0_MEDIA_xxx */
97 	uint8_t  null[5];
98 	uint8_t  count;    /* Always 0x04 (6800) or 0x06 (6850)? */
99 	struct sinfonia_mediainfo_item sizes[];
100 } __attribute__((packed));
101 
102 #define CMDBUF_LEN 17
103 
104 /* Private data structure */
105 struct kodak6800_ctx {
106 	struct libusb_device_handle *dev;
107 	uint8_t endp_up;
108 	uint8_t endp_down;
109 
110 	int type;
111 	int supports_sub4x6;
112 
113 	uint8_t jobid;
114 
115 	struct sinfonia_mediainfo_item sizes[MAX_MEDIAS];
116 	uint8_t media_count;
117 	uint8_t media_type;
118 
119 	struct kodak68x0_status_readback sts;
120 
121 	struct marker marker;
122 };
123 
124 /* Baseline commands */
kodak6800_do_cmd(struct kodak6800_ctx * ctx,void * cmd,int cmd_len,void * resp,int resp_len,int * actual_len)125 static int kodak6800_do_cmd(struct kodak6800_ctx *ctx,
126 			    void *cmd, int cmd_len,
127 			    void *resp, int resp_len,
128 			    int *actual_len)
129 {
130         int ret;
131 
132         /* Write command */
133         if ((ret = send_data(ctx->dev, ctx->endp_down,
134                              cmd, cmd_len)))
135                 return (ret < 0) ? ret : -99;
136 
137         /* Read response */
138         ret = read_data(ctx->dev, ctx->endp_up,
139                         resp, resp_len, actual_len);
140         if (ret < 0)
141                 return ret;
142 
143         return 0;
144 }
145 
kodak68x0_dump_mediainfo(struct sinfonia_mediainfo_item * sizes,uint8_t media_count,uint8_t media_type)146 static void kodak68x0_dump_mediainfo(struct sinfonia_mediainfo_item *sizes,
147 				     uint8_t media_count, uint8_t media_type)
148 {
149 	int i;
150 
151 	if (media_type == KODAK6_MEDIA_NONE) {
152 		INFO("No Media Loaded\n");
153 		return;
154 	}
155 	kodak6_dumpmediacommon(media_type);
156 
157 	INFO("Legal print sizes:\n");
158 	for (i = 0 ; i < media_count ; i++) {
159 		INFO("\t%d: %dx%d (%02x)\n", i,
160 		     sizes[i].columns,
161 		     sizes[i].rows,
162 		     sizes[i].method);
163 	}
164 	INFO("\n");
165 }
166 
167 #define MAX_MEDIA_LEN (sizeof(struct kodak68x0_media_readback) + MAX_MEDIAS * sizeof(struct sinfonia_mediainfo_item))
168 
kodak6800_get_mediainfo(struct kodak6800_ctx * ctx)169 static int kodak6800_get_mediainfo(struct kodak6800_ctx *ctx)
170 {
171 	struct kodak68x0_media_readback *media;
172 	uint8_t req[16];
173 	int ret, num, i, j;
174 
175 	memset(req, 0, sizeof(req));
176 	media = malloc(MAX_MEDIA_LEN);
177 	if (!media) {
178 		ERROR("Memory allocation failure!\n");
179 		return CUPS_BACKEND_RETRY_CURRENT;
180 	}
181 
182 	for (j = 0 ; j < 2 ; j ++) {
183 		memset(media, 0, sizeof(*media));
184 
185 		req[0] = 0x03;
186 		req[1] = 0x1b;
187 		req[2] = 0x43;
188 		req[3] = 0x48;
189 		req[4] = 0x43;
190 		req[5] = 0x1a;
191 		req[6] = j;
192 
193 		/* Issue command and get response */
194 		if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
195 					    media, MAX_MEDIA_LEN,
196 					    &num))) {
197 			free(media);
198 			return ret;
199 		}
200 
201 		/* Validate proper response */
202 		if (media->hdr != CMD_CODE_OK ||
203 		    media->null[0] != 0x00) {
204 			ERROR("Unexpected response from media query!\n");
205 			free(media);
206 			return CUPS_BACKEND_STOP;
207 		}
208 		ctx->media_type = media->type;
209 
210 		for (i = 0; i < media->count ; i++) {
211 			memcpy(&ctx->sizes[ctx->media_count], &media->sizes[i], sizeof(struct sinfonia_mediainfo_item));
212 			ctx->sizes[ctx->media_count].rows = be16_to_cpu(ctx->sizes[ctx->media_count].rows);
213 			ctx->sizes[ctx->media_count].columns = be16_to_cpu(ctx->sizes[ctx->media_count].columns);
214 			ctx->media_count++;
215 		}
216 		if (i < 6)
217 			break;
218 	}
219 
220 	free(media);
221 	return CUPS_BACKEND_OK;
222 }
223 
kodak68x0_canceljob(struct kodak6800_ctx * ctx,int id)224 static int kodak68x0_canceljob(struct kodak6800_ctx *ctx,
225 			       int id)
226 {
227 	uint8_t req[16];
228 	int ret, num;
229 
230 	memset(req, 0, sizeof(req));
231 
232 	req[0] = 0x03;
233 	req[1] = 0x1b;
234 	req[2] = 0x43;
235 	req[3] = 0x48;
236 	req[4] = 0x43;
237 	req[5] = 0x13;
238 	req[6] = id;
239 
240 	/* Issue command and get response */
241 	if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
242 				    &ctx->sts, sizeof(ctx->sts),
243 				    &num)))
244 		return ret;
245 
246 	/* Validate proper response */
247 	if (ctx->sts.hdr != CMD_CODE_OK) {
248 		ERROR("Unexpected response from job cancel!\n");
249 		return -99;
250 	}
251 
252 	return 0;
253 }
254 
kodak68x0_reset(struct kodak6800_ctx * ctx)255 static int kodak68x0_reset(struct kodak6800_ctx *ctx)
256 {
257 	uint8_t req[16];
258 	int ret, num;
259 
260 	memset(req, 0, sizeof(req));
261 
262 	req[0] = 0x03;
263 	req[1] = 0x1b;
264 	req[2] = 0x43;
265 	req[3] = 0x48;
266 	req[4] = 0xc0;
267 
268 	/* Issue command and get response */
269 	if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
270 				    &ctx->sts, sizeof(ctx->sts),
271 				    &num)))
272 		return ret;
273 
274 	/* Validate proper response */
275 	if (ctx->sts.hdr != CMD_CODE_OK) {
276 		ERROR("Unexpected response from job cancel!\n");
277 		return -99;
278 	}
279 
280 	return 0;
281 }
282 
kodak68x0_dump_status(struct kodak6800_ctx * ctx,struct kodak68x0_status_readback * status)283 static void kodak68x0_dump_status(struct kodak6800_ctx *ctx, struct kodak68x0_status_readback *status)
284 {
285 	char *detail;
286 
287 	switch (status->status) {
288         case STATUS_PRINTING:
289                 detail = "Printing";
290                 break;
291         case STATUS_IDLE:
292                 detail = "Idle";
293                 break;
294         default:
295                 detail = "Unknown";
296                 break;
297         }
298         INFO("Printer Status :  %s\n", detail);
299 
300         INFO("Printer State  : %s # %02x %08x %02x\n",
301 	     sinfonia_1x45_status_str(status->status1, status->status2, status->errcode),
302              status->status1, status->status2, status->errcode);
303 
304 	INFO("Bank 1 ID: %u\n", status->b1_jobid);
305 	INFO("\tPrints:  %d/%d complete\n",
306 	     be16_to_cpu(status->b1_complete), be16_to_cpu(status->b1_total));
307 	INFO("Bank 2 ID: %u\n", status->b2_jobid);
308 	INFO("\tPrints:  %d/%d complete\n",
309 	     be16_to_cpu(status->b2_complete), be16_to_cpu(status->b2_total));
310 
311 	switch (status->curve_status) {
312 	case CURVE_TABLE_STATUS_INITIAL:
313 		detail = "Initial/Default";
314 		break;
315 	case CURVE_TABLE_STATUS_USERSET:
316 		detail = "User Stored";
317 		break;
318 	case CURVE_TABLE_STATUS_CURRENT:
319 		detail = "Current";
320 		break;
321 	default:
322 		detail = "Unknown";
323 		break;
324 	}
325 	INFO("Tone Curve Status: %s\n", detail);
326 
327 	INFO("Counters:\n");
328 	INFO("\tLifetime      : %u\n", be32_to_cpu(status->lifetime));
329 	INFO("\tThermal Head  : %u\n", be32_to_cpu(status->maint));
330 	INFO("\tCutter        : %u\n", be32_to_cpu(status->cutter));
331 
332 	if (ctx->type == P_KODAK_6850) {
333 		int max;
334 
335 		INFO("\tMedia         : %u\n", be32_to_cpu(status->media));
336 
337 		switch(ctx->media_type) {
338 		case KODAK6_MEDIA_6R:
339 		case KODAK6_MEDIA_6TR2:
340 			max = 375;
341 			break;
342 		default:
343 			max = 0;
344 			break;
345 		}
346 
347 		if (max) {
348 			INFO("\t  Remaining   : %u\n", max - be32_to_cpu(status->media));
349 		} else {
350 			INFO("\t  Remaining   : Unknown\n");
351 		}
352 	}
353 	INFO("Main FW version : %d\n", be16_to_cpu(status->main_fw));
354 	INFO("DSP FW version  : %d\n", be16_to_cpu(status->dsp_fw));
355 	INFO("Donor           : %u%%\n", status->donor);
356 	INFO("\n");
357 }
358 
kodak6800_get_status(struct kodak6800_ctx * ctx,struct kodak68x0_status_readback * status)359 static int kodak6800_get_status(struct kodak6800_ctx *ctx,
360 				struct kodak68x0_status_readback *status)
361 {
362 	uint8_t req[16];
363 	int ret, num;
364 
365 	memset(req, 0, sizeof(req));
366 	memset(status, 0, sizeof(*status));
367 
368 	req[0] = 0x03;
369 	req[1] = 0x1b;
370 	req[2] = 0x43;
371 	req[3] = 0x48;
372 	req[4] = 0x43;
373 	req[5] = 0x03;
374 
375 	/* Issue command and get response */
376 	if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
377 				    status, sizeof(*status),
378 				    &num)))
379 		return ret;
380 
381 	/* Validate proper response */
382 	if (status->hdr != CMD_CODE_OK) {
383 		ERROR("Unexpected response from status query!\n");
384 		return -99;
385 	}
386 
387 	/* Byteswap important stuff */
388 	status->status2 = be32_to_cpu(status->status2);
389 
390 	return 0;
391 }
392 
kodak6800_get_tonecurve(struct kodak6800_ctx * ctx,char * fname)393 static int kodak6800_get_tonecurve(struct kodak6800_ctx *ctx, char *fname)
394 {
395 	uint8_t cmdbuf[16];
396 	uint8_t respbuf[64];
397 	int ret, num = 0;
398 	int i;
399 
400 	uint16_t *data = malloc(TONE_CURVE_SIZE);
401 	if (!data) {
402 		ERROR("Memory Allocation Failure\n");
403 		return -1;
404 	}
405 
406 	INFO("Dump Tone Curve to '%s'\n", fname);
407 
408 	/* Initial Request */
409 	cmdbuf[0] = 0x03;
410 	cmdbuf[1] = 0x1b;
411 	cmdbuf[2] = 0x43;
412 	cmdbuf[3] = 0x48;
413 	cmdbuf[4] = 0x43;
414 	cmdbuf[5] = 0x0c;
415 	cmdbuf[6] = 0x54;
416 	cmdbuf[7] = 0x4f;
417 	cmdbuf[8] = 0x4e;
418 	cmdbuf[9] = 0x45;
419 	cmdbuf[10] = 0x72;
420 	cmdbuf[11] = 0x01; /* 01 for user tonecurve, can be 00 or 02 */
421 	cmdbuf[12] = 0x00; /* param table? */
422 	cmdbuf[13] = 0x00;
423 	cmdbuf[14] = 0x00;
424 	cmdbuf[15] = 0x00;
425 
426 	respbuf[0] = 0xff;
427 	/* Issue command and get response */
428 	if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf),
429 				    respbuf, sizeof(respbuf),
430 				    &num)))
431 
432 	/* Validate proper response */
433 	if (respbuf[0] != CMD_CODE_OK) {
434 		ERROR("Unexpected response from tonecurve query!\n");
435 		ret = -99;
436 		goto done;
437 	}
438 
439 	/* Then we can poll the data */
440 	cmdbuf[0] = 0x03;
441 	cmdbuf[1] = 0x1b;
442 	cmdbuf[2] = 0x43;
443 	cmdbuf[3] = 0x48;
444 	cmdbuf[4] = 0x43;
445 	cmdbuf[5] = 0x0c;
446 	cmdbuf[6] = 0x54;
447 	cmdbuf[7] = 0x4f;
448 	cmdbuf[8] = 0x4e;
449 	cmdbuf[9] = 0x45;
450 	cmdbuf[10] = 0x20;
451 	for (i = 0 ; i < 24 ; i++) {
452 		/* Issue command and get response */
453 		if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf),
454 					    respbuf, sizeof(respbuf),
455 					    &num)))
456 			goto done;
457 
458 		if (num != 64) {
459 			ERROR("Short read! (%d/%d)\n", num, 51);
460 			ret = 4;
461 			goto done;
462 		}
463 
464 		/* Copy into buffer */
465 		memcpy(((uint8_t*)data)+i*64, respbuf, 64);
466 	}
467 
468 	/* Open file and write it out */
469 	{
470 		int tc_fd = open(fname, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
471 		if (tc_fd < 0) {
472 			ret = 4;
473 			goto done;
474 		}
475 
476 		for (i = 0 ; i < 768; i++) {
477 			/* Byteswap appropriately */
478 			data[i] = cpu_to_be16(le16_to_cpu(data[i]));
479 			ret = write(tc_fd, &data[i], sizeof(uint16_t));
480 		}
481 		close(tc_fd);
482 	}
483 
484  done:
485 	/* We're done */
486 	free(data);
487 
488 	return ret;
489 }
490 
kodak6800_set_tonecurve(struct kodak6800_ctx * ctx,char * fname)491 static int kodak6800_set_tonecurve(struct kodak6800_ctx *ctx, char *fname)
492 {
493 	uint8_t cmdbuf[64];
494 	uint8_t respbuf[64];
495 	int ret, num = 0;
496 	int remain;
497 
498 	uint16_t *data = malloc(TONE_CURVE_SIZE);
499 	uint8_t *ptr;
500 
501 	if (!data) {
502 		ERROR("Memory Allocation Failure\n");
503 		return -1;
504 	}
505 
506 	INFO("Set Tone Curve from '%s'\n", fname);
507 
508 	/* Read in file */
509 	if ((ret = dyesub_read_file(fname, data, TONE_CURVE_SIZE, NULL))) {
510 		ERROR("Failed to read Tone Curve file\n");
511 		goto done;
512 	}
513 
514 	/* Byteswap data to printer's format */
515 	for (ret = 0; ret < (TONE_CURVE_SIZE)/2 ; ret++) {
516 		data[ret] = cpu_to_le16(be16_to_cpu(data[ret]));
517 	}
518 
519 	/* Initial Request */
520 	cmdbuf[0] = 0x03;
521 	cmdbuf[1] = 0x1b;
522 	cmdbuf[2] = 0x43;
523 	cmdbuf[3] = 0x48;
524 	cmdbuf[4] = 0x43;
525 	cmdbuf[5] = 0x0c;
526 	cmdbuf[6] = 0x54;
527 	cmdbuf[7] = 0x4f;
528 	cmdbuf[8] = 0x4e;
529 	cmdbuf[9] = 0x45;
530 	cmdbuf[10] = 0x77;
531 	cmdbuf[11] = 0x01; /* User TC.  Can be 00 or 02 */
532 	cmdbuf[12] = 0x00; /* param table? */
533 	cmdbuf[13] = 0x00;
534 	cmdbuf[14] = 0x00;
535 	cmdbuf[15] = 0x00;
536 
537 	/* Issue command and get response */
538 	if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf),
539 				    respbuf, sizeof(respbuf),
540 				    &num)))
541 
542 	/* Validate proper response */
543 	if (num != 51) {
544 		ERROR("Short read! (%d/%d)\n", num, 51);
545 		ret = 4;
546 		goto done;
547 	}
548 
549 	if (respbuf[0] != CMD_CODE_OK) {
550 		ERROR("Unexpected response from tonecurve set!\n");
551 		ret = -99;
552 		goto done;
553 	}
554 
555 	ptr = (uint8_t*) data;
556 	remain = TONE_CURVE_SIZE;
557 	while (remain > 0) {
558 		int count = remain > 63 ? 63 : remain;
559 
560 		cmdbuf[0] = 0x03;
561 		memcpy(cmdbuf+1, ptr, count);
562 
563 		remain -= count;
564 		ptr += count;
565 
566 		/* Issue command and get response */
567 		if ((ret = kodak6800_do_cmd(ctx, cmdbuf, count + 1,
568 					    respbuf, sizeof(respbuf),
569 					    &num)))
570 
571 		if (num != 51) {
572 			ERROR("Short read! (%d/%d)\n", num, 51);
573 			ret = 4;
574 			goto done;
575 		}
576 		if (respbuf[0] != CMD_CODE_OK) {
577 			ERROR("Unexpected response from tonecurve set!\n");
578 			ret = -99;
579 			goto done;
580 		}
581 	};
582 
583 done:
584 	/* We're done */
585 	free(data);
586 	return ret;
587 }
588 
kodak6800_query_serno(struct libusb_device_handle * dev,uint8_t endp_up,uint8_t endp_down,char * buf,int buf_len)589 static int kodak6800_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len)
590 {
591 	struct kodak6800_ctx ctx = {
592 		.dev = dev,
593 		.endp_up = endp_up,
594 		.endp_down = endp_down,
595 	};
596 
597 	int ret;
598 	int num;
599 
600 	uint8_t resp[33];
601 	uint8_t req[16];
602 
603 	memset(req, 0, sizeof(req));
604 	memset(resp, 0, sizeof(resp));
605 
606 	req[0] = 0x03;
607 	req[1] = 0x1b;
608 	req[2] = 0x43;
609 	req[3] = 0x48;
610 	req[4] = 0x43;
611 	req[5] = 0x12;
612 
613 	/* Issue command and get response */
614 	if ((ret = kodak6800_do_cmd(&ctx, req, sizeof(req),
615 				    resp, sizeof(resp),
616 				    &num)))
617 		return ret;
618 
619 	if (num != 32) {
620 		ERROR("Short read! (%d/%d)\n", num, 32);
621 		return -2;
622 	}
623 
624 	strncpy(buf, (char*)resp+24, buf_len);
625 	buf[buf_len-1] = 0;
626 
627 	return 0;
628 }
629 
kodak6850_send_unk(struct kodak6800_ctx * ctx)630 static int kodak6850_send_unk(struct kodak6800_ctx *ctx)
631 {
632 	uint8_t cmdbuf[16];
633 	uint8_t rdbuf[64];
634 	int ret = 0, num = 0;
635 
636 	memset(cmdbuf, 0, sizeof(cmdbuf));
637 	cmdbuf[0] = 0x03;
638 	cmdbuf[1] = 0x1b;
639 	cmdbuf[2] = 0x43;
640 	cmdbuf[3] = 0x48;
641 	cmdbuf[4] = 0x43;
642 	cmdbuf[5] = 0x4c;
643 
644 	/* Issue command and get response */
645 	if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf),
646 				    rdbuf, sizeof(rdbuf),
647 				    &num)))
648 		return -1;
649 
650 	if (num != 51) {
651 		ERROR("Short read! (%d/%d)\n", num, 51);
652 		return CUPS_BACKEND_FAILED;
653 	}
654 
655 	if (rdbuf[0] != CMD_CODE_OK ||
656 	    rdbuf[2] != 0x43) {
657 		ERROR("Unexpected response from printer init!\n");
658 		return CUPS_BACKEND_FAILED;
659 	}
660 
661 #if 0
662 	// XXX No particular idea what this actually is
663 	if (rdbuf[1] != 0x01 && rdbuf[1] != 0x00) {
664 		ERROR("Unexpected status code (0x%02x)!\n", rdbuf[1]);
665 		return CUPS_BACKEND_FAILED;
666 	}
667 #endif
668 	return ret;
669 }
670 
kodak6800_cmdline(void)671 static void kodak6800_cmdline(void)
672 {
673 	DEBUG("\t\t[ -c filename ]  # Get tone curve\n");
674 	DEBUG("\t\t[ -C filename ]  # Set tone curve\n");
675 	DEBUG("\t\t[ -m ]           # Query media\n");
676 	DEBUG("\t\t[ -s ]           # Query status\n");
677 	DEBUG("\t\t[ -R ]           # Reset printer\n");
678 	DEBUG("\t\t[ -X jobid ]     # Cancel Job\n");
679 }
680 
kodak6800_cmdline_arg(void * vctx,int argc,char ** argv)681 static int kodak6800_cmdline_arg(void *vctx, int argc, char **argv)
682 {
683 	struct kodak6800_ctx *ctx = vctx;
684 	int i, j = 0;
685 
686 	if (!ctx)
687 		return -1;
688 
689 	while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "C:c:mRsX:")) >= 0) {
690 		switch(i) {
691 		GETOPT_PROCESS_GLOBAL
692 		case 'c':
693 			j = kodak6800_get_tonecurve(ctx, optarg);
694 			break;
695 		case 'C':
696 			j = kodak6800_set_tonecurve(ctx, optarg);
697 			break;
698 		case 'm':
699 			kodak68x0_dump_mediainfo(ctx->sizes, ctx->media_count, ctx->media_type);
700 			break;
701 		case 'R':
702 			kodak68x0_reset(ctx);
703 			break;
704 		case 's': {
705 			j = kodak6800_get_status(ctx, &ctx->sts);
706 			if (!j)
707 				kodak68x0_dump_status(ctx, &ctx->sts);
708 			break;
709 		}
710 		case 'X':
711 			j = kodak68x0_canceljob(ctx, atoi(optarg));
712 			break;
713 		default:
714 			break;  /* Ignore completely */
715 		}
716 
717 		if (j) return j;
718 	}
719 
720 	return 0;
721 }
722 
kodak6800_init(void)723 static void *kodak6800_init(void)
724 {
725 	struct kodak6800_ctx *ctx = malloc(sizeof(struct kodak6800_ctx));
726 	if (!ctx) {
727 		ERROR("Memory Allocation Failure\n");
728 		return NULL;
729 	}
730 	memset(ctx, 0, sizeof(struct kodak6800_ctx));
731 
732 	return ctx;
733 }
734 
kodak6800_attach(void * vctx,struct libusb_device_handle * dev,int type,uint8_t endp_up,uint8_t endp_down,uint8_t jobid)735 static int kodak6800_attach(void *vctx, struct libusb_device_handle *dev, int type,
736 			    uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
737 {
738 	struct kodak6800_ctx *ctx = vctx;
739 
740 	ctx->dev = dev;
741 	ctx->endp_up = endp_up;
742 	ctx->endp_down = endp_down;
743 	ctx->type = type;
744 
745         /* Ensure jobid is sane */
746         ctx->jobid = jobid & 0x7f;
747 	if (!ctx->jobid)
748 		ctx->jobid++;
749 
750 	if (test_mode < TEST_MODE_NOATTACH) {
751 		/* Query printer status */
752 		if (kodak6800_get_status(ctx, &ctx->sts)) {
753 			ERROR("Can't query status\n");
754 			return CUPS_BACKEND_FAILED;
755 		}
756 		uint16_t fw = be16_to_cpu(ctx->sts.main_fw);
757 		if (ctx->type == P_KODAK_6850) {
758 			if ((fw >= 878) ||
759 			    (fw < 800 && fw >= 678)) {
760 				ctx->supports_sub4x6 = 1;
761 			} else {
762 				WARNING("Printer FW out of date, recommend updating for current media and features\n");
763 			}
764 		} else {
765 			if ((fw >= 459) ||
766 			    (fw < 400 && fw >= 359) ||
767 			    (fw < 300 && fw >= 259)) {
768 				ctx->supports_sub4x6 = 1;
769 			} else {
770 				WARNING("Printer FW out of date, recommend updating for current media and features\n");
771 			}
772 		}
773 
774 		/* Query media info */
775 		if (kodak6800_get_mediainfo(ctx)) {
776 			ERROR("Can't query media\n");
777 			return CUPS_BACKEND_FAILED;
778 		}
779 	} else {
780 		int media_code = KODAK6_MEDIA_6TR2;
781 		if (getenv("MEDIA_CODE"))
782 			media_code = atoi(getenv("MEDIA_CODE"));
783 
784 		ctx->media_type = media_code;
785 		ctx->supports_sub4x6 = 1;
786 	}
787 
788 	ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
789 	ctx->marker.name = kodak6_mediatypes(ctx->media_type);
790 	ctx->marker.levelmax = 100; /* Ie percentage */
791 	ctx->marker.levelnow = -2;
792 
793 	return CUPS_BACKEND_OK;
794 }
795 
kodak6800_read_parse(void * vctx,const void ** vjob,int data_fd,int copies)796 static int kodak6800_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
797 	struct kodak6800_ctx *ctx = vctx;
798 	int ret;
799 
800 	struct kodak6800_hdr hdr;
801 	struct sinfonia_printjob *job = NULL;
802 
803 	if (!ctx)
804 		return CUPS_BACKEND_FAILED;
805 
806 	job = malloc(sizeof(*job));
807 	if (!job) {
808 		ERROR("Memory allocation failure!\n");
809 		return CUPS_BACKEND_RETRY_CURRENT;
810 	}
811 	memset(job, 0, sizeof(*job));
812 
813 	/* Read in then validate header */
814 	ret = read(data_fd, &hdr, sizeof(hdr));
815 	if (ret < 0 || ret != sizeof(hdr)) {
816 		if (ret == 0) {
817 			sinfonia_cleanup_job(job);
818 			return CUPS_BACKEND_CANCEL;
819 		}
820 		ERROR("Read failed (%d/%d/%d)\n",
821 		      ret, 0, (int)sizeof(hdr));
822 		perror("ERROR: Read failed");
823 		sinfonia_cleanup_job(job);
824 		return CUPS_BACKEND_CANCEL;
825 	}
826 	if (hdr.hdr[0] != 0x03 ||
827 	    hdr.hdr[1] != 0x1b ||
828 	    hdr.hdr[2] != 0x43 ||
829 	    hdr.hdr[3] != 0x48 ||
830 	    hdr.hdr[4] != 0x43) {
831 		ERROR("Unrecognized data format!\n");
832 		sinfonia_cleanup_job(job);
833 		return CUPS_BACKEND_CANCEL;
834 	}
835 
836 	uint16_t rows = be16_to_cpu(hdr.rows);
837 	uint16_t cols = be16_to_cpu(hdr.columns);
838 	if (rows != 1240 && rows != 2434 && rows != 2140 && !ctx->supports_sub4x6) {
839 		ERROR("Printer Firmware does not support non-4x6/8x6/5x7 prints, please upgrade!\n");
840 		sinfonia_cleanup_job(job);
841 		return CUPS_BACKEND_CANCEL;
842 	}
843 
844 	job->datalen = rows * cols * 3;
845 	job->databuf = malloc(job->datalen);
846 	if (!job->databuf) {
847 		ERROR("Memory allocation failure!\n");
848 		sinfonia_cleanup_job(job);
849 		return CUPS_BACKEND_RETRY_CURRENT;
850 	}
851 
852 	/* Windows driver only sends 634 rows of data, work around */
853 	if (rows == 636 && hdr.size == 6 && hdr.method == 0) {
854 		rows = 634;
855 		job->datalen -= 1844*2*3;
856 	}
857 
858 	/* Read in the spool data */
859 	{
860 		int remain = job->datalen;
861 		uint8_t *ptr = job->databuf;
862 		do {
863 			ret = read(data_fd, ptr, remain);
864 			if (ret < 0) {
865 				ERROR("Read failed (%d/%d/%d)\n",
866 				      ret, remain, job->datalen);
867 				perror("ERROR: Read failed");
868 				sinfonia_cleanup_job(job);
869 				return CUPS_BACKEND_CANCEL;
870 			}
871 			ptr += ret;
872 			remain -= ret;
873 		} while (remain);
874 	}
875 
876 	/* Undo the Windows workaround... */
877 	if (rows == 634) {
878 		rows = 636;
879 		job->datalen += 1844*2*3;
880 	}
881 
882 	/* Perform some header re-jiggery */
883 	if (hdr.size == 0) {
884 		if (cols == 1844)
885 			hdr.size = 6;
886 		else if (cols == 1548)
887 			hdr.size = 7;
888 	}
889 	if (hdr.method == 0) {
890 		if (rows == 636) {
891 			hdr.method = 0x21;
892 		} else if (rows == 936) {
893 			hdr.method = 0x23;
894 		} else if (rows == 1240) {
895 			hdr.method = 0x01;
896 		} else if (rows == 1282) {
897 			hdr.method = 0x20;
898 		} else if (rows == 1882) {
899 			hdr.method = 0x22;
900 		} else if (rows == 2490) {
901 			hdr.method = 0x2;
902 		}
903 	}
904 
905 	hdr.copies = be16_to_cpu(hdr.copies);
906 	hdr.copies = packed_bcd_to_uint32((char*)&hdr.copies, 2);
907 	if (hdr.copies > 1)
908 		copies = hdr.copies;
909 
910 	/* Fill out job structure */
911 	job->jp.copies = copies;
912 	job->jp.rows = rows;
913 	job->jp.columns = cols;
914 	job->jp.media = hdr.size;
915 	job->jp.oc_mode = hdr.laminate;
916 	job->jp.method = hdr.method;
917 
918 	*vjob = job;
919 
920 	return CUPS_BACKEND_OK;
921 }
922 
kodak6800_main_loop(void * vctx,const void * vjob)923 static int kodak6800_main_loop(void *vctx, const void *vjob) {
924 	struct kodak6800_ctx *ctx = vctx;
925 
926 	int num, ret;
927 	int copies;
928 
929 	const struct sinfonia_printjob *job = vjob;
930 
931 	if (!ctx)
932 		return CUPS_BACKEND_FAILED;
933 	if (!job)
934 		return CUPS_BACKEND_FAILED;
935 
936 	copies = job->jp.copies;
937 
938 	/* Validate against supported media list */
939 	for (num = 0 ; num < ctx->media_count; num++) {
940 		if (ctx->sizes[num].rows == job->jp.rows &&
941 		    ctx->sizes[num].columns == job->jp.columns &&
942 		    ctx->sizes[num].method == job->jp.method)
943 			break;
944 	}
945 	if (num == ctx->media_count) {
946 		ERROR("Print size unsupported by media!\n");
947 		return CUPS_BACKEND_HOLD;
948 	}
949 
950 	INFO("Waiting for printer idle\n");
951 
952 	while(1) {
953 		if (kodak6800_get_status(ctx, &ctx->sts))
954 			return CUPS_BACKEND_FAILED;
955 
956 		if (ctx->marker.levelnow != ctx->sts.donor) {
957 			ctx->marker.levelnow = ctx->sts.donor;
958 			dump_markers(&ctx->marker, 1, 0);
959 		}
960 
961 		if (ctx->sts.status1 == STATE_STATUS1_ERROR) {
962 			INFO("Printer State: %s # %02x %08x %02x\n",
963 			     sinfonia_1x45_status_str(ctx->sts.status1, ctx->sts.status2, ctx->sts.errcode),
964 			     ctx->sts.status1, ctx->sts.status2, ctx->sts.errcode);
965 			return CUPS_BACKEND_FAILED;
966 		}
967 
968 		/* make sure we're not colliding with an existing
969 		   jobid */
970 		while (ctx->jobid == ctx->sts.b1_jobid ||
971 		       ctx->jobid == ctx->sts.b2_jobid) {
972 			ctx->jobid++;
973 			ctx->jobid &= 0x7f;
974 			if (!ctx->jobid)
975 				ctx->jobid++;
976 		}
977 
978 		/* See if we have an open bank */
979                 if (!ctx->sts.b1_remain ||
980                     !ctx->sts.b2_remain)
981                         break;
982 
983 		sleep(1);
984 	}
985 
986 	/* This command is unknown, sort of a secondary status query */
987 	if (ctx->type == P_KODAK_6850) {
988 		ret = kodak6850_send_unk(ctx);
989 		if (ret)
990 			return ret;
991 	}
992 
993         /* Fix max print count. */
994         if (copies > 9999)
995                 copies = 9999;
996 
997 	/* Fill out printjob header */
998 	struct kodak6800_hdr hdr;
999 	hdr.hdr[0] = 0x03;
1000 	hdr.hdr[1] = 0x1b;
1001 	hdr.hdr[2] = 0x43;
1002 	hdr.hdr[3] = 0x48;
1003 	hdr.hdr[4] = 0x43;
1004 	hdr.hdr[5] = 0x0a;
1005 	hdr.hdr[6] = 0x00;
1006 	hdr.jobid = ctx->jobid;
1007 	hdr.copies = uint16_to_packed_bcd(copies);
1008 	hdr.columns = cpu_to_be16(job->jp.columns);
1009 	hdr.rows = cpu_to_be16(job->jp.rows);
1010 	hdr.size = job->jp.media;
1011 	hdr.laminate = job->jp.oc_mode;
1012 	hdr.method = job->jp.method;
1013 
1014 	INFO("Sending Print Job (internal id %u)\n", ctx->jobid);
1015 	if ((ret = kodak6800_do_cmd(ctx, (uint8_t*) &hdr, sizeof(hdr),
1016 				    &ctx->sts, sizeof(ctx->sts),
1017 				    &num)))
1018 		return ret;
1019 
1020 	if (ctx->sts.hdr != CMD_CODE_OK) {
1021 		ERROR("Unexpected response from print command!\n");
1022 		return CUPS_BACKEND_FAILED;
1023 	}
1024 
1025 //	sleep(1); // Appears to be necessary for reliability
1026 	INFO("Sending image data\n");
1027 	if ((send_data(ctx->dev, ctx->endp_down,
1028 			     job->databuf, job->datalen)) != 0)
1029 		return CUPS_BACKEND_FAILED;
1030 
1031 	INFO("Waiting for printer to acknowledge completion\n");
1032 	do {
1033 		sleep(1);
1034 		if (kodak6800_get_status(ctx, &ctx->sts))
1035 			return CUPS_BACKEND_FAILED;
1036 
1037 		if (ctx->marker.levelnow != ctx->sts.donor) {
1038 			ctx->marker.levelnow = ctx->sts.donor;
1039 			dump_markers(&ctx->marker, 1, 0);
1040 		}
1041 
1042 		if (ctx->sts.status1 == STATE_STATUS1_ERROR) {
1043 			INFO("Printer State: %s # %02x %08x %02x\n",
1044 			     sinfonia_1x45_status_str(ctx->sts.status1, ctx->sts.status2, ctx->sts.errcode),
1045 			     ctx->sts.status1, ctx->sts.status2, ctx->sts.errcode);
1046 			return CUPS_BACKEND_FAILED;
1047 		}
1048 
1049 		/* If all prints are complete, we're done! */
1050 		if (ctx->sts.b1_jobid == hdr.jobid && ctx->sts.b1_complete == ctx->sts.b1_total)
1051 			break;
1052 		if (ctx->sts.b2_jobid == hdr.jobid && ctx->sts.b2_complete == ctx->sts.b2_total)
1053 			break;
1054 
1055 		if (fast_return) {
1056 			INFO("Fast return mode enabled.\n");
1057 			break;
1058 		}
1059 
1060 	} while (1);
1061 
1062 	INFO("Print complete\n");
1063 
1064 	return CUPS_BACKEND_OK;
1065 }
1066 
kodak6800_query_markers(void * vctx,struct marker ** markers,int * count)1067 static int kodak6800_query_markers(void *vctx, struct marker **markers, int *count)
1068 {
1069 	struct kodak6800_ctx *ctx = vctx;
1070 
1071 	/* Query printer status */
1072 	if (kodak6800_get_status(ctx, &ctx->sts))
1073 		return CUPS_BACKEND_FAILED;
1074 
1075 	ctx->marker.levelnow = ctx->sts.donor;
1076 
1077 	*markers = &ctx->marker;
1078 	*count = 1;
1079 
1080 	return CUPS_BACKEND_OK;
1081 }
1082 
1083 static const char *kodak6800_prefixes[] = {
1084 	"kodak68x0", // Family driver, do not nuke.
1085 	"kodak-6800", "kodak-6850",
1086 	// Backwards-compatibility
1087 	"kodak6800", "kodak6850",
1088 	NULL
1089 };
1090 
1091 /* Exported */
1092 struct dyesub_backend kodak6800_backend = {
1093 	.name = "Kodak 6800/6850",
1094 	.version = "0.73" " (lib " LIBSINFONIA_VER ")",
1095 	.uri_prefixes = kodak6800_prefixes,
1096 	.cmdline_usage = kodak6800_cmdline,
1097 	.cmdline_arg = kodak6800_cmdline_arg,
1098 	.init = kodak6800_init,
1099 	.attach = kodak6800_attach,
1100 	.cleanup_job = sinfonia_cleanup_job,
1101 	.read_parse = kodak6800_read_parse,
1102 	.main_loop = kodak6800_main_loop,
1103 	.query_serno = kodak6800_query_serno,
1104 	.query_markers = kodak6800_query_markers,
1105 	.devices = {
1106 		{ USB_VID_KODAK, USB_PID_KODAK_6800, P_KODAK_6800, "Kodak", "kodak-6800"},
1107 		{ USB_VID_KODAK, USB_PID_KODAK_6850, P_KODAK_6850, "Kodak", "kodak-6850"},
1108 		{ 0, 0, 0, NULL, NULL}
1109 	}
1110 };
1111 
1112 /* Kodak 6800/6850 data format
1113 
1114   Spool file consists of 17-byte header followed by plane-interleaved BGR data.
1115   Native printer resolution is 1844 pixels per row, and 1240 or 2434 rows.
1116 
1117   6850 Adds support for 5x7, with 1548 pixels per row and 2140 columns.
1118 
1119   All fields are BIG ENDIAN unless otherwise specified.
1120 
1121   Header:
1122 
1123   03 1b 43 48 43 0a 00           Fixed header
1124   II                             Job ID (1-255)
1125   NN NN                          Number of copies in BCD form (0001->9999)
1126   WW WW                          Number of columns (Fixed at 1844 on 6800)
1127   HH HH                          Number of rows.
1128   SS                             Print size -- 0x00 (4x6) 0x06 (8x6) 0x07 (5x7 on 6850)
1129   LL                             Laminate mode -- 0x00 (off) or 0x01 (on)
1130   UU                             Print mode -- 0x00 (normal) or 0x01 (4x6 on 8x6) 0x21 (2x6) 0x23 (3x6)
1131 
1132   ************************************************************************
1133 
1134   Note:  6800 is Shinko CHC-S1145-5A, 6850 is Shinko CHC-S1145-5B
1135 
1136   Both are very similar to Shinko S1245!
1137 
1138   ************************************************************************
1139 
1140   This command is unique to the 6850:
1141 
1142 ->  03 1b 43 48 43 4c 00 00  00 00 00 00 00 00 00 00  [???]
1143 <-  [51 octets]
1144 
1145     01 01 43 48 43 4c 00 00  00 00 00 00 00 00 00 00 <-- Everything after this
1146     00 00 01 29 00 00 3b 0a  00 00 00 0e 00 03 02 90     line is the same as
1147     00 01 02 1d 03 00 00 00  00 01 00 01 00 00 00 00     the "status" resp.
1148     00 00 00
1149 
1150     01 00 43 48 43 4c 00 00  00 00 00 00 00 00 00 00
1151     00 00 00 01 00 00 b7 d3  00 00 00 5c 00 03 02 8c
1152     00 01 02 1c 00 00 00 00  00 01 00 01 00 00 00 00
1153     00 00 00
1154 
1155   An additional command that's also unknown
1156 
1157 ->  03 1b 43 48 43 4d 01 00  00 00 00 00 00 00 00 00
1158 <-  01 02 01 00 00 00 00 00  00 00 5d ca 00 00 5d ca
1159     00 00 00 15 00 00 b8 f8  00 00 00 40 00 03 02 a6
1160     00 01 02 31 1e 00 00 00  00 01 00 01 00 00 00 00
1161     00 00 00
1162 
1163   One more note for the 6850.  These sizes have been seen:
1164 
1165      1844x2434, method 0x03
1166      1844x2490, method 0x05
1167      1844x2222, method 0x00
1168 
1169 */
1170