selphy_print/backend_kodak1400.c

801 lines
19 KiB
C
Raw Normal View History

/*
2013-07-27 07:32:21 -04:00
* Kodak Professional 1400/805 CUPS backend -- libusb-1.0 version
*
2021-02-23 07:39:47 -05:00
* (c) 2013-2021 Solomon Peachy <pizza@shaftnet.org>
*
* The latest version of this program can be found at:
*
* https://git.shaftnet.org/cgit/selphy_print.git
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0+
*
*/
#define BACKEND kodak1400_backend
#include "backend_common.h"
/* Program states */
enum {
S_IDLE = 0,
S_PRINTER_READY_Y,
S_PRINTER_SENT_Y,
S_PRINTER_READY_M,
S_PRINTER_SENT_M,
S_PRINTER_READY_C,
S_PRINTER_SENT_C,
S_PRINTER_READY_L,
S_PRINTER_SENT_L,
S_PRINTER_DONE,
S_FINISHED,
};
#define CMDBUF_LEN 96
#define READBACK_LEN 8
/* File header */
struct kodak1400_hdr {
uint8_t hdr[4];
uint16_t columns;
uint16_t null1;
uint16_t rows;
uint16_t null2;
uint32_t planesize;
uint32_t null3;
uint8_t matte;
uint8_t laminate;
uint8_t unk1; /* Always 0x01 */
uint8_t lam_strength;
uint8_t null4[12];
} __attribute__((packed));
/* Private data structure */
struct kodak1400_printjob {
2021-02-23 07:39:47 -05:00
size_t jobsize;
int copies;
struct kodak1400_hdr hdr;
uint8_t *plane_r;
uint8_t *plane_g;
uint8_t *plane_b;
};
struct kodak1400_ctx {
struct dyesub_connection *conn;
struct marker marker;
};
static int send_plane(struct kodak1400_ctx *ctx,
const struct kodak1400_printjob *job,
uint8_t planeno, uint8_t *planedata,
uint8_t *cmdbuf)
{
uint16_t temp16;
int ret;
if (planeno != 1) {
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x74;
cmdbuf[2] = 0x00;
cmdbuf[3] = 0x50;
2017-07-10 20:15:56 -04:00
if ((ret = send_data(ctx->conn,
cmdbuf, CMDBUF_LEN)))
return ret;
}
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x5a;
cmdbuf[2] = 0x54;
cmdbuf[3] = planeno;
2013-01-19 22:26:52 -05:00
if (planedata) {
temp16 = be16_to_cpu(job->hdr.columns);
2013-01-19 22:26:52 -05:00
memcpy(cmdbuf+7, &temp16, 2);
temp16 = be16_to_cpu(job->hdr.rows);
2013-01-19 22:26:52 -05:00
memcpy(cmdbuf+9, &temp16, 2);
}
if ((ret = send_data(ctx->conn,
cmdbuf, CMDBUF_LEN)))
return ret;
if (planedata) {
int i;
for (i = 0 ; i < job->hdr.rows ; i++) {
if ((ret = send_data(ctx->conn,
planedata + i * job->hdr.columns,
job->hdr.columns)))
2013-01-19 22:26:52 -05:00
return ret;
}
}
2013-01-19 22:26:52 -05:00
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x74;
cmdbuf[2] = 0x01;
cmdbuf[3] = 0x50;
2017-07-10 20:15:56 -04:00
if ((ret = send_data(ctx->conn,
cmdbuf, CMDBUF_LEN)))
return ret;
return CUPS_BACKEND_OK;
}
#define TONE_CURVE_SIZE 1552
static int kodak1400_set_tonecurve(struct kodak1400_ctx *ctx, char *fname)
{
uint8_t cmdbuf[8];
uint8_t respbuf[64];
int ret = 0, num = 0;
INFO("Set Tone Curve from '%s'\n", fname);
uint16_t *data = malloc(TONE_CURVE_SIZE);
if (!data) {
ERROR("Memory Allocation Failure!\n");
return -1;
}
/* Read in file */
if ((ret = dyesub_read_file(fname, data, TONE_CURVE_SIZE, NULL))) {
ERROR("Failed to read Tone Curve file\n");
goto done;
}
/* Byteswap data to printer's format */
for (ret = 0; ret < (TONE_CURVE_SIZE-16)/2 ; ret++) {
data[ret] = cpu_to_le16(be16_to_cpu(data[ret]));
}
/* Null-terminate */
memset(data + (TONE_CURVE_SIZE-16)/2, 0, 16);
/* Clear tables */
memset(cmdbuf, 0, sizeof(cmdbuf));
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0xa2;
if ((ret = send_data(ctx->conn,
cmdbuf, 2))) {
ret = -3;
goto done;
}
2017-07-10 20:15:56 -04:00
ret = read_data(ctx->conn,
respbuf, sizeof(respbuf), &num);
2017-07-10 20:15:56 -04:00
if (ret < 0)
goto done;
if (num != 8) {
ERROR("Short Read! (%d/%d)\n", num, 8);
ret = -4;
goto done;
}
if (respbuf[1] != 0x01) {
ERROR("Received unexpected response\n");
ret = -5;
goto done;
}
/* Set up the update command */
memset(cmdbuf, 0, sizeof(cmdbuf));
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0xa0;
cmdbuf[2] = 0x02;
cmdbuf[3] = 0x03;
cmdbuf[4] = 0x06;
cmdbuf[5] = 0x10; /* 06 10 == TONE_CURVE_SIZE */
if ((ret = send_data(ctx->conn,
cmdbuf, 6)))
goto done;
/* Send the payload over */
if ((ret = send_data(ctx->conn,
(uint8_t *) data, TONE_CURVE_SIZE)))
goto done;
/* get the response */
ret = read_data(ctx->conn,
respbuf, sizeof(respbuf), &num);
2017-07-10 20:15:56 -04:00
if (ret < 0)
goto done;
if (num != 8) {
ERROR("Short Read! (%d/%d)\n", num, 8);
ret = -6;
goto done;
}
if (respbuf[1] != 0x00) {
ERROR("Received unexpected response!\n");
ret = -7;
goto done;
}
done:
free(data);
return ret;
}
static void kodak1400_cmdline(void)
{
DEBUG("\t\t[ -C filename ] # Set tone curve\n");
}
static int kodak1400_cmdline_arg(void *vctx, int argc, char **argv)
{
struct kodak1400_ctx *ctx = vctx;
int i, j = 0;
if (!ctx)
return -1;
while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "C:")) >= 0) {
switch(i) {
GETOPT_PROCESS_GLOBAL
case 'C':
j = kodak1400_set_tonecurve(ctx, optarg);
break;
default:
break; /* Ignore completely */
}
if (j) return j;
}
return CUPS_BACKEND_OK;
}
static void *kodak1400_init(void)
{
struct kodak1400_ctx *ctx = malloc(sizeof(struct kodak1400_ctx));
if (!ctx) {
ERROR("Memory Allocation Failure!\n");
return NULL;
}
memset(ctx, 0, sizeof(struct kodak1400_ctx));
2017-07-10 20:15:56 -04:00
return ctx;
}
static int kodak1400_attach(void *vctx, struct dyesub_connection *conn,
uint8_t jobid)
{
struct kodak1400_ctx *ctx = vctx;
2013-11-23 19:51:55 -05:00
UNUSED(jobid);
ctx->conn = conn;
ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
ctx->marker.name = "Unknown";
ctx->marker.numtype = -1;
ctx->marker.levelmax = CUPS_MARKER_UNAVAILABLE;
ctx->marker.levelnow = CUPS_MARKER_UNKNOWN;
return CUPS_BACKEND_OK;
}
static void kodak1400_cleanup_job(const void *vjob)
{
const struct kodak1400_printjob *job = vjob;
if (job->plane_r)
free(job->plane_r);
if (job->plane_g)
free(job->plane_g);
if (job->plane_b)
free(job->plane_b);
free((void*)job);
}
static int kodak1400_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
struct kodak1400_ctx *ctx = vctx;
int i, ret;
struct kodak1400_printjob *job = NULL;
if (!ctx)
return CUPS_BACKEND_FAILED;
job = malloc(sizeof(*job));
if (!job) {
ERROR("Memory allocation failure!\n");
return CUPS_BACKEND_RETRY_CURRENT;
}
memset(job, 0, sizeof(*job));
job->copies = copies;
/* Read in then validate header */
ret = read(data_fd, &job->hdr, sizeof(job->hdr));
if (ret < 0 || ret != sizeof(job->hdr)) {
2021-02-23 07:39:47 -05:00
if (ret == 0) {
kodak1400_cleanup_job(job);
return CUPS_BACKEND_CANCEL;
2021-02-23 07:39:47 -05:00
}
2017-07-10 20:15:56 -04:00
ERROR("Read failed (%d/%d/%d)\n",
ret, 0, (int)sizeof(job->hdr));
perror("ERROR: Read failed");
return CUPS_BACKEND_CANCEL;
}
if (job->hdr.hdr[0] != 'P' ||
job->hdr.hdr[1] != 'G' ||
job->hdr.hdr[2] != 'H' ||
job->hdr.hdr[3] != 'D') {
ERROR("Unrecognized data format!\n");
return CUPS_BACKEND_CANCEL;
}
job->hdr.planesize = le32_to_cpu(job->hdr.planesize);
job->hdr.rows = le16_to_cpu(job->hdr.rows);
job->hdr.columns = le16_to_cpu(job->hdr.columns);
2017-07-10 20:15:56 -04:00
/* Set up plane data */
job->plane_r = malloc(job->hdr.planesize);
job->plane_g = malloc(job->hdr.planesize);
job->plane_b = malloc(job->hdr.planesize);
if (!job->plane_r || !job->plane_g || !job->plane_b) {
ERROR("Memory allocation failure!\n");
return CUPS_BACKEND_RETRY_CURRENT;
}
for (i = 0 ; i < job->hdr.rows ; i++) {
int j;
uint8_t *ptr;
for (j = 0 ; j < 3 ; j++) {
int remain;
if (j == 0)
ptr = job->plane_r + i * job->hdr.columns;
else if (j == 1)
ptr = job->plane_g + i * job->hdr.columns;
else if (j == 2)
ptr = job->plane_b + i * job->hdr.columns;
2018-05-01 14:46:51 -04:00
else
ptr = NULL;
remain = job->hdr.columns;
do {
ret = read(data_fd, ptr, remain);
if (ret < 0) {
2017-07-10 20:15:56 -04:00
ERROR("Read failed (%d/%d/%u) (%d/%u @ %d)\n",
ret, remain, job->hdr.columns,
i, job->hdr.rows, j);
perror("ERROR: Read failed");
return CUPS_BACKEND_CANCEL;
}
ptr += ret;
remain -= ret;
} while (remain);
}
}
*vjob = job;
return CUPS_BACKEND_OK;
}
static uint8_t idle_data[READBACK_LEN] = { 0xe4, 0x72, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
static int kodak1400_main_loop(void *vctx, const void *vjob) {
struct kodak1400_ctx *ctx = vctx;
uint8_t rdbuf[READBACK_LEN], rdbuf2[READBACK_LEN];
uint8_t cmdbuf[CMDBUF_LEN];
int last_state = -1, state = S_IDLE;
int num, ret;
uint16_t temp16;
int copies;
const struct kodak1400_printjob *job = vjob;
if (!ctx)
return CUPS_BACKEND_FAILED;
if (!job)
return CUPS_BACKEND_FAILED;
copies = job->copies;
top:
if (state != last_state) {
if (dyesub_debug)
DEBUG("last_state %d new %d\n", last_state, state);
}
/* Send Status Query */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x72;
if ((ret = send_data(ctx->conn,
2013-01-19 22:17:33 -05:00
cmdbuf, CMDBUF_LEN)))
return CUPS_BACKEND_FAILED;
/* Read in the printer status */
ret = read_data(ctx->conn,
rdbuf, READBACK_LEN, &num);
2017-07-10 20:15:56 -04:00
if (ret < 0)
return CUPS_BACKEND_FAILED;
if (memcmp(rdbuf, rdbuf2, READBACK_LEN)) {
memcpy(rdbuf2, rdbuf, READBACK_LEN);
} else if (state == last_state) {
sleep(1);
}
last_state = state;
/* Error handling */
if (rdbuf[4] || rdbuf[5]) {
ERROR("Error code reported by printer (%02x/%02x), terminating print\n",
rdbuf[4], rdbuf[5]);
return CUPS_BACKEND_STOP; // HOLD/CANCEL/FAILED? XXXX parse error!
}
fflush(logger);
switch (state) {
case S_IDLE:
INFO("Printing started\n");
/* Send reset/attention */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
if ((ret = send_data(ctx->conn,
cmdbuf, CMDBUF_LEN)))
return CUPS_BACKEND_FAILED;
/* Send page setup */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x5a;
cmdbuf[2] = 0x53;
temp16 = be16_to_cpu(job->hdr.columns);
memcpy(cmdbuf+3, &temp16, 2);
temp16 = be16_to_cpu(job->hdr.rows);
memcpy(cmdbuf+5, &temp16, 2);
if ((ret = send_data(ctx->conn,
2013-01-19 22:17:33 -05:00
cmdbuf, CMDBUF_LEN)))
return CUPS_BACKEND_FAILED;
/* Send lamination toggle? */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x59;
cmdbuf[2] = job->hdr.matte; // ???
if ((ret = send_data(ctx->conn,
2013-01-19 22:17:33 -05:00
cmdbuf, CMDBUF_LEN)))
return CUPS_BACKEND_FAILED;
/* Send matte toggle */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x60;
cmdbuf[2] = job->hdr.laminate;
if (send_data(ctx->conn,
2013-01-19 22:17:33 -05:00
cmdbuf, CMDBUF_LEN))
return CUPS_BACKEND_FAILED;
/* Send lamination strength */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x62;
cmdbuf[2] = job->hdr.lam_strength;
if ((ret = send_data(ctx->conn,
2013-01-19 22:17:33 -05:00
cmdbuf, CMDBUF_LEN)))
return CUPS_BACKEND_FAILED;
/* Send unknown */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x61;
cmdbuf[2] = job->hdr.unk1; // ???
if ((ret = send_data(ctx->conn,
2013-01-19 22:17:33 -05:00
cmdbuf, CMDBUF_LEN)))
return CUPS_BACKEND_FAILED;
2013-01-19 21:52:02 -05:00
state = S_PRINTER_READY_Y;
break;
case S_PRINTER_READY_Y:
INFO("Sending YELLOW plane\n");
if ((ret = send_plane(ctx, job, 1, job->plane_b, cmdbuf)))
return CUPS_BACKEND_FAILED;
state = S_PRINTER_SENT_Y;
break;
case S_PRINTER_SENT_Y:
if (!memcmp(rdbuf, idle_data, READBACK_LEN))
state = S_PRINTER_READY_M;
break;
case S_PRINTER_READY_M:
INFO("Sending MAGENTA plane\n");
if ((ret = send_plane(ctx, job, 2, job->plane_g, cmdbuf)))
return CUPS_BACKEND_FAILED;
state = S_PRINTER_SENT_M;
break;
case S_PRINTER_SENT_M:
if (!memcmp(rdbuf, idle_data, READBACK_LEN))
state = S_PRINTER_READY_C;
break;
case S_PRINTER_READY_C:
INFO("Sending CYAN plane\n");
if ((ret = send_plane(ctx, job, 3, job->plane_r, cmdbuf)))
return CUPS_BACKEND_FAILED;
state = S_PRINTER_SENT_C;
break;
case S_PRINTER_SENT_C:
if (!memcmp(rdbuf, idle_data, READBACK_LEN)) {
if (job->hdr.laminate)
state = S_PRINTER_READY_L;
else
state = S_PRINTER_DONE;
}
break;
case S_PRINTER_READY_L:
INFO("Laminating page\n");
if ((ret = send_plane(ctx, job, 4, NULL, cmdbuf)))
return CUPS_BACKEND_FAILED;
state = S_PRINTER_SENT_L;
break;
case S_PRINTER_SENT_L:
if (!memcmp(rdbuf, idle_data, READBACK_LEN))
state = S_PRINTER_DONE;
break;
case S_PRINTER_DONE:
INFO("Cleaning up\n");
2013-01-19 21:44:58 -05:00
/* Cleanup */
memset(cmdbuf, 0, CMDBUF_LEN);
cmdbuf[0] = 0x1b;
cmdbuf[1] = 0x74;
cmdbuf[2] = 0x00;
cmdbuf[3] = 0x50;
if ((ret = send_data(ctx->conn,
2013-01-19 22:17:33 -05:00
cmdbuf, CMDBUF_LEN)))
return CUPS_BACKEND_FAILED;
2013-01-19 21:44:58 -05:00
state = S_FINISHED;
break;
default:
break;
};
if (state != S_FINISHED)
goto top;
/* Clean up */
if (terminate)
copies = 1;
INFO("Print complete (%d copies remaining)\n", copies - 1);
if (copies && --copies) {
state = S_IDLE;
goto top;
}
return CUPS_BACKEND_OK;
}
static int kodak1400_query_markers(void *vctx, struct marker **markers, int *count)
{
struct kodak1400_ctx *ctx = vctx;
*markers = &ctx->marker;
*count = 1;
return CUPS_BACKEND_OK;
}
/* Exported */
#define USB_VID_KODAK 0x040A
#define USB_PID_KODAK_1400 0x4022
#define USB_PID_KODAK_805 0x4034
#define USB_VID_MITSU 0x06D3
#define USB_PID_MITSU_3020D 0x038B
#define USB_PID_MITSU_3020DA 0x03AA