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_shinkos2145.c

1284 lines
32 KiB

/*
* Shinko/Sinfonia CHC-S2145 CUPS backend -- libusb-1.0 version
*
* (c) 2013-2019 Solomon Peachy <pizza@shaftnet.org>
*
* Development of this backend was sponsored by:
*
* LiveLink Technology [ www.livelinktechnology.net ]
*
* The latest version of this program can be found at:
*
* http://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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* [http://www.gnu.org/licenses/gpl-3.0.html]
*
* SPDX-License-Identifier: GPL-3.0+
*
*/
#define BACKEND shinkos2145_backend
#include "backend_common.h"
#include "backend_sinfonia.h"
enum {
S_IDLE = 0,
S_PRINTER_READY_CMD,
S_PRINTER_SENT_DATA,
S_FINISHED,
};
/* Structs for printer */
static int print_counts (uint8_t v) {
switch (v) {
case CODE_4x6:
return 700;
case CODE_3_5x5:
return 800;
case CODE_5x7:
return 400;
case CODE_6x9:
return 310;
case CODE_6x8:
return 350;
default:
return 700;
}
}
#if 0
#define PRINT_MODE_DEFAULT 0x01
#define PRINT_MODE_STD_GLOSSY 0x02
#define PRINT_MODE_FINE_GLOSSY 0x03
#define PRINT_MODE_STD_MATTE 0x04
#define PRINT_MODE_FINE_MATTE 0x05
#define PRINT_MODE_STD_EGLOSSY 0x06
#define PRINT_MODE_FINE_EGLOSSY 0x07
static char *s2145_print_modes(uint8_t v) {
switch (v) {
case PRINT_MODE_DEFAULT:
return "Default";
case PRINT_MODE_STD_GLOSSY:
return "Std Glossy";
case PRINT_MODE_FINE_GLOSSY:
return "Fine Glossy";
case PRINT_MODE_STD_MATTE:
return "Std Matte";
case PRINT_MODE_FINE_MATTE:
return "Fine Matte";
case PRINT_MODE_STD_EGLOSSY:
return "Std ExGlossy";
case PRINT_MODE_FINE_EGLOSSY:
return "Fine ExGlossy";
default:
return "Unknown";
}
}
#endif
struct s2145_reset_cmd {
struct sinfonia_cmd_hdr hdr;
uint8_t target;
} __attribute__((packed));
#define RESET_PRINTER 0x03
#define RESET_USER_CURVE 0x04
struct s2145_readtone_cmd {
struct sinfonia_cmd_hdr hdr;
uint8_t curveid;
} __attribute__((packed));
#define FWINFO_TARGET_MAIN_BOOT 0x01
#define FWINFO_TARGET_MAIN_APP 0x02
#define FWINFO_TARGET_DSP_BOOT 0x03
#define FWINFO_TARGET_DSP_APP 0x04
#define FWINFO_TARGET_USB_BOOT 0x05
#define FWINFO_TARGET_USB_APP 0x06
#define FWINFO_TARGET_TABLES 0x07
static char *fwinfo_targets (uint8_t v) {
switch (v) {
case FWINFO_TARGET_MAIN_BOOT:
return "Main Boot";
case FWINFO_TARGET_MAIN_APP:
return "Main App ";
case FWINFO_TARGET_DSP_BOOT:
return "DSP Boot ";
case FWINFO_TARGET_DSP_APP:
return "DSP App ";
case FWINFO_TARGET_USB_BOOT:
return "USB Boot ";
case FWINFO_TARGET_USB_APP:
return "USB App ";
case FWINFO_TARGET_TABLES:
return "Tables ";
default:
return "Unknown ";
}
}
struct s2145_update_cmd {
struct sinfonia_cmd_hdr hdr;
uint8_t target;
uint32_t reserved;
uint32_t size;
} __attribute__((packed));
struct s2145_setunique_cmd {
struct sinfonia_cmd_hdr hdr;
uint8_t len;
uint8_t data[23]; /* Not necessarily all used. */
} __attribute__((packed));
static const char *error_codes(uint8_t major, uint8_t minor)
{
switch(major) {
case 0x01: /* "Controller Error" */
switch(minor) {
case 0x01:
return "Controller: EEPROM Write Timeout";
case 0x02:
return "Controller: EEPROM Verify";
case 0x04:
return "Controller: DSP Inactive";
case 0x05:
return "Controller: DSP Application Inactive";
case 0x06:
return "Controller: Main FW Data";
case 0x07:
return "Controller: Main FW Write";
case 0x08:
return "Controller: DSP FW Data";
case 0x09:
return "Controller: DSP FW Write";
case 0x0A:
return "Controller: 0A ASIC??";
case 0x0B:
return "Controller: 0B FPGA??";
case 0x0D:
return "Controller: Tone Curve Write";
case 0x16:
return "Controller: Invalid Parameter Table";
case 0x17:
return "Controller: Parameter Table Data";
case 0x18:
return "Controller: Parameter Table Write";
case 0x29:
return "Controller: DSP Communication";
case 0x2A:
return "Controller: DSP DMA Failure";
default:
return "Controller: Unknown";
}
case 0x02: /* "Mechanical Error" */
switch (minor) {
case 0x01:
return "Mechanical: Thermal Head (Upper Up)";
case 0x02:
return "Mechanical: Thermal Head (Head Up)";
case 0x03:
return "Mechanical: Thermal Head (Head Down)";
case 0x04:
return "Mechanical: Pinch Roller (Initialize)";
case 0x05:
return "Mechanical: Pinch Roller (Mode1)";
case 0x06:
return "Mechanical: Pinch Roller (Mode2)";
case 0x07:
return "Mechanical: Pinch Roller (Mode3)";
case 0x08:
return "Mechanical: Pinch Roller (Mode4)";
case 0x09:
return "Mechanical: Cutter (Right)";
case 0x0A:
return "Mechanical: Cutter (Left)";
case 0x0B:
return "Mechanical: Thermal Head (Head Down Recovery)";
default:
return "Mechanical: Unknown";
}
case 0x03: /* "Sensor Error" */
switch (minor) {
case 0x01:
return "Sensor: Thermal Head";
case 0x02:
return "Sensor: Pinch Roller";
case 0x03:
return "Sensor: Cutter Left";
case 0x04:
return "Sensor: Cutter Right";
case 0x05:
return "Sensor: Cutter Unknown";
case 0x08:
return "Sensor: Ribbon Encoder (Supply)";
case 0x09:
return "Sensor: Ribbon Encoder (Takeup)";
case 0x13:
return "Sensor: Thermal Head";
default:
return "Sensor: Unknown";
}
case 0x04: /* "Temperature Sensor Error" */
switch (minor) {
case 0x01:
return "Temp Sensor: Thermal Head High";
case 0x02:
return "Temp Sensor: Thermal Head Low";
case 0x03:
return "Temp Sensor: Environment High";
case 0x04:
return "Temp Sensor: Environment Low";
case 0x05:
return "Temp Sensor: Warmup Timed Out";
default:
return "Temp Sensor: Unknown";
}
case 0x5: /* "Paper Jam" */
switch (minor) {
case 0x01:
return "Paper Jam: Loading Leading Edge Off";
case 0x02:
return "Paper Jam: Loading Print Position On";
case 0x03:
return "Paper Jam: Loading Print Position Off";
case 0x04:
return "Paper Jam: Loading Print Position On";
case 0x05:
return "Paper Jam: Loading Leading Edge On";
case 0x11:
return "Paper Jam: Initializing Print Position Off";
case 0x12:
return "Paper Jam: Initializing Print Position On";
case 0x13:
return "Paper Jam: Initializing Leading Edge On";
case 0x14:
return "Paper Jam: Initializing Print Position On";
case 0x15:
return "Paper Jam: Initializing Print Position Off";
case 0x16:
return "Paper Jam: Initializing Print Position On";
case 0x21:
return "Paper Jam: Initializing Print Position On";
case 0x22:
return "Paper Jam: Rewinding Print Position On";
case 0x40:
return "Paper Jam: Pre-Printing Print Position Off";
case 0x41:
return "Paper Jam: Pre-Printing Print Position Off";
case 0x42:
return "Paper Jam: Printing Leading Edge Off";
case 0x43:
return "Paper Jam: After Returning Lead Edge Off";
case 0x44:
return "Paper Jam: After Printing Print Position Off";
case 0x45:
return "Paper Jam: After Printing Print Position On";
case 0x46:
return "Paper Jam: After Printing Print Position On";
case 0x47:
return "Paper Jam: After Printing Print Position Off";
case 0x49:
return "Paper Jam: Printing Lost Ribbon Mark";
case 0x4A:
return "Paper Jam: Printing Ribbon Cut";
case 0x4D:
return "Paper Jam: Printing Lost M Mark";
case 0x4E:
return "Paper Jam: Printing Lost C Mark";
case 0x4F:
return "Paper Jam: Printing Lost OP Mark";
case 0x61:
return "Paper Jam: Initializing Lead Edge On";
case 0x62:
return "Paper Jam: Initizlizing Print Position On";
case 0x64:
return "Paper Jam: Initizlizing Paper Size On";
default:
return "Paper Jam: Unknown";
}
case 0x06: /* User Error */
switch (minor) {
case 0x01:
return "Front Cover Open";
case 0x02:
return "Incorrect Ribbon";
case 0x03:
return "No Ribbon";
case 0x04:
return "Mismatched Ribbon";
case 0x05:
return "Mismatched Paper";
case 0x06:
return "Paper Empty";
case 0x08:
return "No Paper";
case 0x09:
return "Take Out Paper";
case 0x0A:
return "Cover Open Error";
case 0x0B:
return "Thermal Head Damaged";
case 0x0C:
return "Thermal Head Recovery";
default:
return "Unknown";
}
default:
return "Unknown";
}
}
struct s2145_status_resp {
struct sinfonia_status_hdr hdr;
uint32_t count_lifetime;
uint32_t count_maint;
uint32_t count_paper;
uint32_t count_cutter;
uint32_t count_head;
uint32_t count_ribbon_left;
uint8_t bank1_printid;
uint8_t bank2_printid;
uint16_t bank1_remaining;
uint16_t bank1_finished;
uint16_t bank1_specified;
uint8_t bank1_status;
uint16_t bank2_remaining;
uint16_t bank2_finished;
uint16_t bank2_specified;
uint8_t bank2_status;
uint8_t tonecurve_status;
} __attribute__((packed));
struct s2145_readtone_resp {
struct sinfonia_status_hdr hdr;
uint16_t total_size;
} __attribute__((packed));
struct s2145_mediainfo_resp {
struct sinfonia_status_hdr hdr;
uint8_t count;
struct sinfonia_mediainfo_item items[10]; /* Not all necessarily used */
} __attribute__((packed));
struct s2145_modelname_resp {
struct sinfonia_status_hdr hdr;
uint8_t vendor[4];
uint8_t product[4];
uint8_t modelname[40];
} __attribute__((packed));
struct s2145_getunique_resp {
struct sinfonia_status_hdr hdr;
uint8_t data[24]; /* Not necessarily all used. */
} __attribute__((packed));
/* Private data structure */
struct shinkos2145_ctx {
struct sinfonia_usbdev dev;
uint8_t jobid;
char serial[32];
char fwver[32];
struct s2145_mediainfo_resp media;
struct marker marker;
int media_code;
};
static int get_status(struct shinkos2145_ctx *ctx)
{
struct sinfonia_cmd_hdr cmd;
struct s2145_status_resp resp;
int ret, num = 0;
cmd.cmd = cpu_to_le16(SINFONIA_CMD_GETSTATUS);
cmd.len = cpu_to_le16(0);
if ((ret = sinfonia_docmd(&ctx->dev,
(uint8_t*)&cmd, sizeof(cmd),
(uint8_t*)&resp, sizeof(resp),
&num))) {
return ret;
}
INFO("Printer Status: 0x%02x (%s)\n", resp.hdr.status,
sinfonia_status_str(resp.hdr.status));
if (resp.hdr.status == ERROR_PRINTER) {
if(resp.hdr.error == ERROR_NONE)
resp.hdr.error = resp.hdr.status;
INFO(" Error 0x%02x (%s) 0x%02x/0x%02x (%s)\n",
resp.hdr.error,
sinfonia_error_str(resp.hdr.error),
resp.hdr.printer_major,
resp.hdr.printer_minor, error_codes(resp.hdr.printer_major, resp.hdr.printer_minor));
}
if (le16_to_cpu(resp.hdr.payload_len) != (sizeof(struct s2145_status_resp) - sizeof(struct sinfonia_status_hdr)))
return CUPS_BACKEND_OK;
INFO(" Print Counts:\n");
INFO("\tSince Paper Changed:\t%08u\n", le32_to_cpu(resp.count_paper));
INFO("\tLifetime:\t\t%08u\n", le32_to_cpu(resp.count_lifetime));
INFO("\tMaintenance:\t\t%08u\n", le32_to_cpu(resp.count_maint));
INFO("\tPrint Head:\t\t%08u\n", le32_to_cpu(resp.count_head));
INFO(" Cutter Actuations:\t%08u\n", le32_to_cpu(resp.count_cutter));
INFO(" Ribbon Remaining:\t%08u\n", le32_to_cpu(resp.count_ribbon_left));
INFO("Bank 1: 0x%02x (%s) Job %03u @ %03u/%03u (%03u remaining)\n",
resp.bank1_status, sinfonia_bank_statuses(resp.bank1_status),
resp.bank1_printid,
le16_to_cpu(resp.bank1_finished),
le16_to_cpu(resp.bank1_specified),
le16_to_cpu(resp.bank1_remaining));
INFO("Bank 2: 0x%02x (%s) Job %03u @ %03u/%03u (%03u remaining)\n",
resp.bank2_status, sinfonia_bank_statuses(resp.bank1_status),
resp.bank2_printid,
le16_to_cpu(resp.bank2_finished),
le16_to_cpu(resp.bank2_specified),
le16_to_cpu(resp.bank2_remaining));
INFO("Tonecurve Status: 0x%02x (%s)\n", resp.tonecurve_status, sinfonia_tonecurve_statuses(resp.tonecurve_status));
return CUPS_BACKEND_OK;
}
static int get_fwinfo(struct shinkos2145_ctx *ctx)
{
struct sinfonia_fwinfo_cmd cmd;
struct sinfonia_fwinfo_resp resp;
int num = 0;
int i;
cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_FWINFO);
cmd.hdr.len = cpu_to_le16(1);
INFO("FW Information:\n");
for (i = FWINFO_TARGET_MAIN_BOOT ; i <= FWINFO_TARGET_TABLES ; i++) {
int ret;
cmd.target = i;
if ((ret = sinfonia_docmd(&ctx->dev,
(uint8_t*)&cmd, sizeof(cmd),
(uint8_t*)&resp, sizeof(resp),
&num))) {
continue;
}
if (le16_to_cpu(resp.hdr.payload_len) != (sizeof(struct sinfonia_fwinfo_resp) - sizeof(struct sinfonia_status_hdr)))
continue;
INFO(" %s\t ver %02x.%02x\n", fwinfo_targets(i),
resp.major, resp.minor);
#if 0
INFO(" name: '%s'\n", resp.name);
INFO(" type: '%s'\n", resp.type);
INFO(" date: '%s'\n", resp.date);
INFO(" version: %02x.%02x (CRC %04x)\n", resp.major, resp.minor,
le16_to_cpu(resp.checksum));
#endif
}
return CUPS_BACKEND_OK;
}
static void dump_mediainfo(struct s2145_mediainfo_resp *resp)
{
int i;
INFO("Supported Media Information: %u entries:\n", resp->count);
for (i = 0 ; i < resp->count ; i++) {
INFO(" %02d: C 0x%02x (%s), %04ux%04u, P 0x%02x (%s)\n", i,
resp->items[i].code,
sinfonia_print_codes(resp->items[i].code, 0),
resp->items[i].columns,
resp->items[i].rows,
resp->items[i].method,
sinfonia_print_methods(resp->items[i].method));
}
}
static int get_user_string(struct shinkos2145_ctx *ctx)
{
struct sinfonia_cmd_hdr cmd;
struct s2145_getunique_resp resp;
int ret, num = 0;
cmd.cmd = cpu_to_le16(SINFONIA_CMD_GETUNIQUE);
cmd.len = cpu_to_le16(0);
if ((ret = sinfonia_docmd(&ctx->dev,
(uint8_t*)&cmd, sizeof(cmd),
(uint8_t*)&resp, sizeof(resp),
&num))) {
return ret;
}
/* Null-terminate */
resp.hdr.payload_len = le16_to_cpu(resp.hdr.payload_len);
if (resp.hdr.payload_len > 23)
resp.hdr.payload_len = 23;
resp.data[resp.hdr.payload_len] = 0;
INFO("Unique String: '%s'\n", resp.data);
return CUPS_BACKEND_OK;
}
static int set_user_string(struct shinkos2145_ctx *ctx, char *str)
{
struct s2145_setunique_cmd cmd;
struct sinfonia_status_hdr resp;
int ret, num = 0;
if (str) {
cmd.len = strlen(str);
if (cmd.len > 23)
cmd.len = 23;
memset(cmd.data, 0, sizeof(cmd.data));
strncpy((char*)cmd.data, str, cmd.len);
} else {
cmd.len = 0;
}
cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_SETUNIQUE);
cmd.hdr.len = cpu_to_le16(cmd.len + 1);
if ((ret = sinfonia_docmd(&ctx->dev,
(uint8_t*)&cmd, cmd.len + 1 + sizeof(cmd.hdr),
(uint8_t*)&resp, sizeof(resp),
&num))) {
return ret;
}
return CUPS_BACKEND_OK;
}
static int reset_curve(struct shinkos2145_ctx *ctx, int target)
{
struct s2145_reset_cmd cmd;
struct sinfonia_status_hdr resp;
int ret, num = 0;
cmd.target = target;
cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_RESET);
cmd.hdr.len = cpu_to_le16(1);
if ((ret = sinfonia_docmd(&ctx->dev,
(uint8_t*)&cmd, sizeof(cmd),
(uint8_t*)&resp, sizeof(resp),
&num))) {
return ret;
}
return CUPS_BACKEND_OK;
}
static int get_tonecurve(struct shinkos2145_ctx *ctx, int type, char *fname)
{
struct s2145_readtone_cmd cmd;
struct s2145_readtone_resp resp;
int ret, num = 0;
uint8_t *data;
uint16_t curves[TONE_CURVE_SIZE] = { 0 } ;
int i,j;
cmd.curveid = type;
cmd.hdr.cmd = cpu_to_le16(SINFONIA_CMD_READTONE);
cmd.hdr.len = cpu_to_le16(1);
INFO("Dump %s Tone Curve to '%s'\n", sinfonia_tonecurve_statuses(type), fname);
if ((ret = sinfonia_docmd(&ctx->dev,
(uint8_t*)&cmd, sizeof(cmd),
(uint8_t*)&resp, sizeof(resp),
&num))) {
return ret;
}
resp.total_size = le16_to_cpu(resp.total_size);
data = malloc(resp.total_size * 2);
if (!data) {
ERROR("Memory allocation failure! (%d bytes)\n",
resp.total_size * 2);
return -1;
}
i = 0;
while (i < resp.total_size) {
ret = read_data(ctx->dev.dev, ctx->dev.endp_up,
data + i,
resp.total_size * 2 - i,
&num);
if (ret < 0)
goto done;
i += num;
}
i = j = 0;
while (i < resp.total_size) {
memcpy(curves + j, data + i+2, data[i+1]);
j += data[i+1] / 2;
i += data[i+1] + 2;
}
/* Open file and write it out */
{
int tc_fd = open(fname, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
if (tc_fd < 0) {
ret = -1;
goto done;
}
for (i = 0 ; i < TONE_CURVE_SIZE; i++) {
/* Byteswap appropriately */
curves[i] = cpu_to_be16(le16_to_cpu(curves[i]));
}
ret = write(tc_fd, curves, TONE_CURVE_SIZE * sizeof(uint16_t));
if (ret < 0)
ERROR("Can't write curve file\n");
else
ret = 0;
close(tc_fd);
}
done:
free(data);
return ret;
}
static int set_tonecurve(struct shinkos2145_ctx *ctx, int target, char *fname)
{
struct s2145_update_cmd cmd;
struct sinfonia_status_hdr resp;
int ret, num = 0;
INFO("Set %s Tone Curve from '%s'\n", sinfonia_update_targets(target), fname);
uint16_t *data = malloc(TONE_CURVE_SIZE * sizeof(uint16_t));
if (!data) {
ERROR("Memory allocation failure! (%d bytes)\n",
TONE_CURVE_SIZE);
return -1;
}