Gutenprint + CUPS backends for Dye Sublimation printers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
selphy_print/backend_shinkos1245.c

1324 lines
32 KiB

/*
* Shinko/Sinfonia CHC-S1245 CUPS backend -- libusb-1.0 version
*
* (c) 2015-2021 Solomon Peachy <pizza@shaftnet.org>
*
* Low-level documentation was provided by Sinfonia, Inc. Thank you!
*
* 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 shinkos1245_backend
#include "backend_common.h"
#include "backend_sinfonia.h"
/* Printer data structures */
struct shinkos1245_cmd_hdr {
uint8_t prefix; /* 0x03 */
uint8_t hdr[4]; /* 0x1b 0x43 0x48 0x43 */
} __attribute__((packed));
/* Get Printer ID */
struct shinkos1245_cmd_getid {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0x12 */
uint8_t pad[11];
} __attribute__((packed));
struct shinkos1245_resp_getid {
uint8_t id; /* 0x00 */
uint8_t data[23]; /* padded with 0x20 (space) */
uint8_t reserved[8]; // XXX actual serial number?
} __attribute__((packed));
/* Set Printer ID -- Returns Status */
struct shinkos1245_cmd_setid {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[2]; /* 0x0a 0x22 */
uint8_t id; /* 0x00 */
uint8_t data[23]; /* pad with 0x20 (space) */
} __attribute__((packed));
/* Print -- Returns Status */
struct shinkos1245_cmd_print {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[2]; /* 0x0a 0x00 */
uint8_t id; /* 1-255 */
uint16_t count; /* # Copies in BCD, 1-9999 */
uint16_t columns; /* Fixed at 2446 */
uint16_t rows;
uint8_t media; /* Fixed at 0x10 */
uint8_t mode; /* dust removal and lamination mode */
uint8_t combo; /* aka "print method" in the spool file */
} __attribute__((packed));
/* Get Status */
struct shinkos1245_cmd_getstatus {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0x03 */
uint8_t pad[10];
} __attribute__((packed));
struct shinkos1245_resp_status {
uint8_t code;
uint8_t print_status;
struct {
uint8_t status1;
uint32_t status2; /* BE */
uint8_t error;
} __attribute__((packed)) state;
struct {
uint32_t lifetime; /* BE */
uint32_t maint; /* BE */
uint32_t media; /* BE */
uint32_t cutter; /* BE */
uint8_t reserved;
uint8_t ver_boot;
uint8_t ver_ctrl;
uint8_t control_flag; // 0x00 == epson, 0x01 == cypress
} __attribute__((packed)) counters;
struct {
uint16_t main_boot;
uint16_t main_control;
uint16_t dsp_boot;
uint16_t dsp_control;
} __attribute__((packed)) versions;
struct {
uint8_t bank1_id;
uint8_t bank2_id;
uint16_t bank1_remain; /* BE */
uint16_t bank1_complete; /* BE */
uint16_t bank1_spec; /* BE */
uint16_t bank2_remain; /* BE */
uint16_t bank2_complete; /* BE */
uint16_t bank2_spec; /* BE */
} __attribute__((packed)) counters2;
uint8_t curve_status;
} __attribute__((packed));
/* Query media info */
struct shinkos1245_cmd_getmedia {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0x1a/0x2a/0x3a for A/B/C */
uint8_t pad[10];
} __attribute__((packed));
#define NUM_MEDIAS 5 /* Maximum per message */
struct shinkos1245_resp_media {
uint8_t code;
uint8_t reserved[6];
uint8_t count; /* 1-5? */
struct sinfonia_mediainfo_item data[NUM_MEDIAS];
} __attribute__((packed));
enum {
PRINT_TYPE_STANDARD = 0x00,
PRINT_TYPE_8x5_2up = 0x01,
PRINT_TYPE_8x4_2up = 0x02,
PRINT_TYPE_8x6_8x4 = 0x03,
PRINT_TYPE_8x5 = 0x04,
PRINT_TYPE_8x4 = 0x05,
PRINT_TYPE_8x6 = 0x06,
PRINT_TYPE_8x6_2up = 0x07,
PRINT_TYPE_8x4_3up = 0x08,
PRINT_TYPE_8x8 = 0x09,
};
/* Cancel Job -- returns Status */
struct shinkos1245_cmd_canceljob {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0x13 */
uint8_t id; /* 1-255 */
uint8_t pad[9];
} __attribute__((packed));
/* Reset printer -- returns Status */
struct shinkos1245_cmd_reset {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0xc0 */
uint8_t pad[10];
} __attribute__((packed));
/* Tone curve manipulation -- returns Status */
struct shinkos1245_cmd_tone {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0xc0 */
uint8_t tone[4]; /* 0x54 0x4f 0x4e 0x45 */
uint8_t cmd2[1]; /* 0x72/0x77/0x65/0x20 for read/write/end/data */
union {
struct {
uint8_t tone_table;
uint8_t param_table;
uint8_t pad[3];
} read_write;
struct {
uint8_t pad[5];
} end_data;
};
} __attribute__((packed));
#define TONE_CURVE_DATA_BLOCK_SIZE 64
/* Query Model information */
struct shinkos1245_cmd_getmodel {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0x02 */
uint8_t pad[10];
} __attribute__((packed));
struct shinkos1245_resp_getmodel {
uint8_t vendor_id[4];
uint8_t product_id[4];
uint8_t strings[40];
} __attribute__((packed));
/* Query and Set Matte info, returns a Matte response */
struct shinkos1245_cmd_getmatte {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0x20 */
uint8_t mode; /* Fixed at 0x00 */
uint8_t pad[9];
} __attribute__((packed));
struct shinkos1245_cmd_setmatte {
struct shinkos1245_cmd_hdr hdr;
uint8_t cmd[1]; /* 0x21 */
uint8_t mode; /* Fixed at 0x00 */
int8_t level; /* -25->+25 */
uint8_t pad[8];
} __attribute__((packed));
struct shinkos1245_resp_matte {
uint8_t code;
uint8_t mode;
int8_t level;
uint8_t reserved[4];
} __attribute__((packed));
#define MATTE_MODE_MATTE 0x00
#define MAX_MEDIA_ITEMS 15
/* Private data structure */
struct shinkos1245_ctx {
struct dyesub_connection *conn;
uint8_t jobid;
struct sinfonia_mediainfo_item medias[MAX_MEDIA_ITEMS];
int num_medias;
int media_8x12;
char serial[32];
char fwver[32];
struct marker marker;
int tonecurve;
};
enum {
S_IDLE = 0,
S_PRINTER_READY_CMD,
S_PRINTER_SENT_DATA,
S_FINISHED,
};
/* Basic printer I/O stuffs */
static void shinkos1245_fill_hdr(struct shinkos1245_cmd_hdr *hdr)
{
hdr->prefix = 0x03;
hdr->hdr[0] = 0x1b;
hdr->hdr[1] = 0x43;
hdr->hdr[2] = 0x48;
hdr->hdr[3] = 0x43;
}
static int shinkos1245_do_cmd(struct shinkos1245_ctx *ctx,
void *cmd, int cmd_len,
void *resp, int resp_len,
int *actual_len)
{
int ret;
/* Write command */
if ((ret = send_data(ctx->conn,
cmd, cmd_len)))
return (ret < 0) ? ret : -99;
/* Read response */
ret = read_data(ctx->conn,
resp, resp_len, actual_len);
if (ret < 0)
return ret;
if (*actual_len < resp_len) {
ERROR("Short read! (%d/%d))\n", *actual_len, resp_len);
return -99;
}
return ret;
}
static int shinkos1245_get_status(struct shinkos1245_ctx *ctx,
struct shinkos1245_resp_status *resp)
{
struct shinkos1245_cmd_getstatus cmd;
int ret, num;
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0x03;
memset(cmd.pad, 0, sizeof(cmd.pad));
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
resp, sizeof(*resp), &num);
if (ret < 0) {
ERROR("Failed to execute GET_STATUS command\n");
return ret;
}
if (resp->code != CMD_CODE_OK) {
ERROR("Bad return code on GET_STATUS (%02x)\n",
resp->code);
return -99;
}
/* Byteswap important stuff */
resp->state.status2 = be32_to_cpu(resp->state.status2);
return CUPS_BACKEND_OK;
}
static int shinkos1245_get_media(struct shinkos1245_ctx *ctx)
{
struct shinkos1245_cmd_getmedia cmd;
struct shinkos1245_resp_media resp;
int i, j;
int ret = 0, num;
shinkos1245_fill_hdr(&cmd.hdr);
memset(cmd.pad, 0, sizeof(cmd.pad));
ctx->media_8x12 = 0;
for (i = 1 ; i <= 3 ; i++) {
cmd.cmd[0] = 0x0a | (i << 4);
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&resp, sizeof(resp), &num);
if (ret < 0) {
ERROR("Failed to execute GET_MEDIA command\n");
return ret;
}
if (resp.code != CMD_CODE_OK) {
ERROR("Bad return code on GET_MEDIA (%02x)\n",
resp.code);
return -99;
}
/* Store media info */
for (j = 0; j < NUM_MEDIAS && ctx->num_medias < resp.count ; j++) {
ctx->medias[ctx->num_medias].code = resp.data[j].code;
ctx->medias[ctx->num_medias].columns = be16_to_cpu(resp.data[j].columns);
ctx->medias[ctx->num_medias].rows = be16_to_cpu(resp.data[j].rows);
ctx->medias[ctx->num_medias].type = resp.data[j].type;
ctx->medias[ctx->num_medias].method = resp.data[j].method;
ctx->num_medias++;
if (ctx->medias[i].rows >= 3636)
ctx->media_8x12 = 1;
}
/* Once we've parsed them all.. we're done */
if (ctx->num_medias == resp.count)
break;
}
return ret;
}
static int shinkos1245_get_printerid(struct shinkos1245_ctx *ctx,
struct shinkos1245_resp_getid *resp)
{
struct shinkos1245_cmd_getid cmd;
int ret, num;
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0x12;
memset(cmd.pad, 0, sizeof(cmd.pad));
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
resp, sizeof(*resp), &num);
if (ret < 0) {
ERROR("Failed to execute GET_PRINTERID command\n");
return ret;
}
return CUPS_BACKEND_OK;
}
static int shinkos1245_set_printerid(struct shinkos1245_ctx *ctx,
char *id)
{
struct shinkos1245_cmd_setid cmd;
struct shinkos1245_resp_status sts;
int ret, num;
int i;
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0x0a;
cmd.cmd[1] = 0x22;
for (i = 0 ; i < (int)sizeof(cmd.data) ; i++) {
if (*id)
cmd.data[i] = (uint8_t) *id++;
else
cmd.data[i] = ' ';
}
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&sts, sizeof(sts), &num);
if (ret < 0) {
ERROR("Failed to execute SET_PRINTERID command\n");
return ret;
}
if (sts.code != CMD_CODE_OK) {
ERROR("Bad return code on SET_PRINTERID command\n");
return -99;
}
return CUPS_BACKEND_OK;
}
static int shinkos1245_canceljob(struct shinkos1245_ctx *ctx,
int id)
{
struct shinkos1245_cmd_canceljob cmd;
struct shinkos1245_resp_status sts;
int ret, num;
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0x13;
cmd.id = id;
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&sts, sizeof(sts), &num);
if (ret < 0) {
ERROR("Failed to execute CANCELJOB command\n");
return ret;
}
if (sts.code != CMD_CODE_OK) {
ERROR("Bad return code on CANCELJOB command\n");
return -99;
}
return CUPS_BACKEND_OK;
}
static int shinkos1245_reset(struct shinkos1245_ctx *ctx)
{
struct shinkos1245_cmd_reset cmd;
struct shinkos1245_resp_status sts;
int ret, num;
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0xc0;
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&sts, sizeof(sts), &num);
if (ret < 0) {
ERROR("Failed to execute RESET command\n");
return ret;
}
if (sts.code != CMD_CODE_OK) {
ERROR("Bad return code on RESET command\n");
return -99;
}
return CUPS_BACKEND_OK;
}
static int shinkos1245_set_matte(struct shinkos1245_ctx *ctx,
int intensity)
{
struct shinkos1245_cmd_setmatte cmd;
struct shinkos1245_resp_matte sts;
int ret, num;
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0x21;
cmd.mode = MATTE_MODE_MATTE;
cmd.level = intensity;
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&sts, sizeof(sts), &num);
if (ret < 0) {
ERROR("Failed to execute SET_MATTE command\n");
return ret;
}
if (sts.code == CMD_CODE_OK)
return CUPS_BACKEND_OK;
if (sts.code == CMD_CODE_BAD)
return 1;
ERROR("Bad return code (%02x) on SET_MATTE command\n", sts.code);
return -99;
}
static int shinkos1245_get_matte(struct shinkos1245_ctx *ctx,
int *intensity)
{
struct shinkos1245_cmd_getmatte cmd;
struct shinkos1245_resp_matte sts;
int ret, num;
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0x20;
cmd.mode = MATTE_MODE_MATTE;
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&sts, sizeof(sts), &num);
if (ret < 0) {
ERROR("Failed to execute GET_MATTE command\n");
return ret;
}
if (sts.code != CMD_CODE_OK) {
ERROR("Bad return code (%02x) on GET_MATTE command\n", sts.code);
return -99;
}
*intensity = sts.level;
return CUPS_BACKEND_OK;
}
static const char* shinkos1245_tonecurves(int type, int table)
{
switch (type) {
case TONE_TABLE_STANDARD:
switch (table) {
case PARAM_TABLE_STANDARD:
return "Standard/Standard";
case PARAM_TABLE_FINE:
return "Standard/Fine";
default:
return "Standard/Unknown";
}
case TONE_TABLE_USER:
switch (table) {
case PARAM_TABLE_STANDARD:
return "User/Standard";
case PARAM_TABLE_FINE:
return "User/Fine";
default:
return "User/Unknown";
}
case TONE_TABLE_CURRENT:
switch (table) {
case PARAM_TABLE_STANDARD:
return "Current/Standard";
case PARAM_TABLE_FINE:
return "Current/Fine";
default:
return "Current/Unknown";
}
default:
return "Unknown";
}
}
static void shinkos1245_dump_status(struct shinkos1245_ctx *ctx,
struct shinkos1245_resp_status *sts)
{
const char *detail;
switch (sts->print_status) {
case STATUS_PRINTING:
detail = "Printing";
break;
case STATUS_IDLE:
detail = "Idle";
break;
default:
detail = "Unknown";
break;
}
INFO("Printer Status: %s\n", detail);
/* Byteswap */
INFO("Printer State: %s # %02x %08x %02x\n",
sinfonia_1x45_status_str(sts->state.status1, sts->state.status2, sts->state.error),
sts->state.status1, sts->state.status2, sts->state.error);
INFO("Counters:\n");
INFO("\tLifetime : %u\n", be32_to_cpu(sts->counters.lifetime));
INFO("\tThermal Head : %u\n", be32_to_cpu(sts->counters.maint));
INFO("\tMedia : %u\n", be32_to_cpu(sts->counters.media));
INFO("\tRemaining : %u\n", ctx->marker.levelmax - be32_to_cpu(sts->counters.media));
INFO("\tCutter : %u\n", be32_to_cpu(sts->counters.cutter));
INFO("Versions:\n");
INFO("\tUSB Boot : %u\n", sts->counters.ver_boot);
INFO("\tUSB Control : %u\n", sts->counters.ver_ctrl);
INFO("\tMain Boot : %u\n", be16_to_cpu(sts->versions.main_boot));
INFO("\tMain Control: %u\n", be16_to_cpu(sts->versions.main_control));
INFO("\tDSP Boot : %u\n", be16_to_cpu(sts->versions.dsp_boot));
INFO("\tDSP Control : %u\n", be16_to_cpu(sts->versions.dsp_control));
// INFO("USB TypeFlag: %02x\n", sts->counters.control_flag);
INFO("Bank 1 ID: %u\n", sts->counters2.bank1_id);
INFO("\tPrints: %d/%d complete\n",
be16_to_cpu(sts->counters2.bank1_complete),
be16_to_cpu(sts->counters2.bank1_spec));
INFO("Bank 2 ID: %u\n", sts->counters2.bank2_id);
INFO("\tPrints: %d/%d complete\n",
be16_to_cpu(sts->counters2.bank2_complete),
be16_to_cpu(sts->counters2.bank2_spec));
switch (sts->curve_status) {
case CURVE_TABLE_STATUS_INITIAL:
detail = "Initial/Default";
break;
case CURVE_TABLE_STATUS_USERSET:
detail = "User Stored";
break;
case CURVE_TABLE_STATUS_CURRENT:
detail = "Current";
break;
default:
detail = "Unknown";
break;
}
INFO("Tone Curve Status: %s\n", detail);
}
static void shinkos1245_dump_media(struct sinfonia_mediainfo_item *medias,
int media_8x12,
int count)
{
int i;
INFO("Loaded media type: %s\n", media_8x12 ? "8x12" : "8x10");
INFO("Supported print sizes: %d\n", count);
for (i = 0 ; i < count ; i++) {
INFO("\t %02d: %04u*%04u (%02x/%02u)\n",
i,
medias[i].columns,
medias[i].rows,
medias[i].type,
medias[i].method);
}
}
static int get_tonecurve(struct shinkos1245_ctx *ctx, int type, int table, char *fname)
{
int ret = 0, num, remaining;
uint8_t *data, *ptr;
struct shinkos1245_cmd_tone cmd;
struct shinkos1245_resp_status resp;
INFO("Dump %s Tone Curve to '%s'\n", shinkos1245_tonecurves(type, table), fname);
/* Issue a tone_read_start */
shinkos1245_fill_hdr(&cmd.hdr);
cmd.cmd[0] = 0x0c;
cmd.tone[0] = 0x54;
cmd.tone[1] = 0x4f;
cmd.tone[2] = 0x4e;
cmd.tone[3] = 0x45;
cmd.cmd2[0] = 0x72;
cmd.read_write.tone_table = type;
cmd.read_write.param_table = table;
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&resp, sizeof(resp), &num);
if (ret < 0) {
ERROR("Failed to execute TONE_READ command\n");
return ret;
}
if (resp.code != CMD_CODE_OK) {
ERROR("Bad return code on TONE_READ (%02x)\n",
resp.code);
return -99;
}
/* Get the data out */
remaining = TONE_CURVE_SIZE;
data = malloc(remaining);
if (!data) {
ERROR("Memory Allocation Failure!\n");
return -11;
}
ptr = data;
while(remaining) {
/* Issue a tone_data message */
cmd.cmd2[0] = 0x20;
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&resp, sizeof(resp), &num);
if (ret < 0) {
ERROR("Failed to execute TONE_DATA command\n");
goto done;
}
if (resp.code != CMD_CODE_OK) {
ERROR("Bad return code on TONE_DATA (%02x)\n",
resp.code);
ret = -99;
goto done;
}
/* And read back 64-bytes of data */
ret = read_data(ctx->conn,
ptr, TONE_CURVE_DATA_BLOCK_SIZE, &num);
if (num != TONE_CURVE_DATA_BLOCK_SIZE) {
ret = -99;
goto done;
}
if (ret < 0)
goto done;
ptr += num;
remaining -= num;
}
/* Issue a tone_end */
cmd.cmd2[0] = 0x65;
ret = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
&resp, sizeof(resp), &num);
if (ret < 0) {
ERROR("Failed to execute TONE_END command\n");
goto done;
}
if (resp.code != CMD_CODE_OK) {
ERROR("Bad return code on TONE_END (%02x)\n",
resp.code);
ret = -99;
goto done;
}
/* Open file and write it out */
{
int tc_fd = open(fname, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
if (tc_fd < 0) {
ret = tc_fd;
goto done;
}
ret = write(tc_fd, data, TONE_CURVE_SIZE);
if (ret < 0)