1326 lines
32 KiB
C
1326 lines
32 KiB
C
/*
|
|
* Shinko/Sinfonia CHC-S1245 CUPS backend
|
|
*
|
|
* (c) 2015-2024 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/gitea/slp/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;
|
|
} 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,
|
|
const 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.data.read_write.tone_table = type;
|
|
cmd.data.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)
|
|
goto done;
|
|
close(tc_fd);
|
|
}
|
|
|
|
done:
|
|
free(data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int set_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("Read %d/%d Tone Curve from '%s'\n", type, table, fname);
|
|
|
|
/* Allocate space */
|
|
remaining = TONE_CURVE_SIZE;
|
|
data = malloc(remaining);
|
|
if (!data) {
|
|
ERROR("Memory Allocation Failure!\n");
|
|
return -11;
|
|
}
|
|
ptr = data;
|
|
|
|
|
|
/* Read in file */
|
|
if ((ret = dyesub_read_file(fname, data, TONE_CURVE_SIZE, NULL))) {
|
|
ERROR("Failed to read Tone Curve file\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Issue a tone_write_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] = 0x77;
|
|
cmd.data.read_write.tone_table = type;
|
|
cmd.data.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_WRITE command\n");
|
|
goto done;
|
|
}
|
|
if (resp.code != CMD_CODE_OK) {
|
|
ERROR("Bad return code on TONE_WRITE (%02x)\n",
|
|
resp.code);
|
|
ret = -99;
|
|
goto done;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* Write 64-bytes of data */
|
|
ret = send_data(ctx->conn,
|
|
ptr, TONE_CURVE_DATA_BLOCK_SIZE);
|
|
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;
|
|
}
|
|
|
|
done:
|
|
free(data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Driver API */
|
|
|
|
static void shinkos1245_cmdline(void)
|
|
{
|
|
DEBUG("\t\t[ -m ] # Query media\n");
|
|
DEBUG("\t\t[ -s ] # Query status\n");
|
|
DEBUG("\t\t[ -u ] # Query user string\n");
|
|
DEBUG("\t\t[ -U sometext ] # Set user string\n");
|
|
DEBUG("\t\t[ -R ] # Reset printer\n");
|
|
DEBUG("\t\t[ -X jobid ] # Abort a printjob\n");
|
|
DEBUG("\t\t[ -F ] # Tone curve refers to FINE mode\n");
|
|
DEBUG("\t\t[ -c filename ] # Get user/NV tone curve\n");
|
|
DEBUG("\t\t[ -C filename ] # Set user/NV tone curve\n");
|
|
DEBUG("\t\t[ -l filename ] # Get current tone curve\n");
|
|
DEBUG("\t\t[ -L filename ] # Set current tone curve\n");
|
|
}
|
|
|
|
static int shinkos1245_cmdline_arg(void *vctx, int argc, char **argv)
|
|
{
|
|
struct shinkos1245_ctx *ctx = vctx;
|
|
int i, j = 0;
|
|
|
|
if (!ctx)
|
|
return -1;
|
|
|
|
while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "c:C:l:L:FmRsuU:X:")) >= 0) {
|
|
switch(i) {
|
|
GETOPT_PROCESS_GLOBAL
|
|
case 'F':
|
|
ctx->tonecurve = PARAM_TABLE_FINE;
|
|
break;
|
|
case 'c':
|
|
j = get_tonecurve(ctx, TONE_TABLE_USER, ctx->tonecurve, optarg);
|
|
break;
|
|
case 'C':
|
|
j = set_tonecurve(ctx, TONE_TABLE_USER, ctx->tonecurve, optarg);
|
|
break;
|
|
case 'l':
|
|
j = get_tonecurve(ctx, TONE_TABLE_CURRENT, ctx->tonecurve, optarg);
|
|
break;
|
|
case 'L':
|
|
j = set_tonecurve(ctx, TONE_TABLE_CURRENT, ctx->tonecurve, optarg);
|
|
break;
|
|
case 'm':
|
|
j = shinkos1245_get_media(ctx);
|
|
if (!j)
|
|
shinkos1245_dump_media(ctx->medias, ctx->media_8x12, ctx->num_medias);
|
|
break;
|
|
case 'R':
|
|
j = shinkos1245_reset(ctx);
|
|
break;
|
|
case 's': {
|
|
struct shinkos1245_resp_status sts;
|
|
j = shinkos1245_get_status(ctx, &sts);
|
|
if (!j)
|
|
shinkos1245_dump_status(ctx, &sts);
|
|
break;
|
|
}
|
|
case 'u': {
|
|
struct shinkos1245_resp_getid resp;
|
|
j = shinkos1245_get_printerid(ctx, &resp);
|
|
if (!j) {
|
|
char buffer[sizeof(resp.data)+1];
|
|
memcpy(buffer, resp.data, sizeof(resp.data));
|
|
buffer[sizeof(resp.data)] = 0;
|
|
INFO("Printer ID: %02x '%s'\n", resp.id, buffer);
|
|
}
|
|
break;
|
|
}
|
|
case 'U':
|
|
j = shinkos1245_set_printerid(ctx, optarg);
|
|
break;
|
|
case 'X':
|
|
j = shinkos1245_canceljob(ctx, atoi(optarg));
|
|
break;
|
|
default:
|
|
break; /* Ignore completely */
|
|
}
|
|
|
|
if (j) return j;
|
|
}
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static void *shinkos1245_init(void)
|
|
{
|
|
struct shinkos1245_ctx *ctx = malloc(sizeof(struct shinkos1245_ctx));
|
|
if (!ctx) {
|
|
ERROR("Memory Allocation Failure!\n");
|
|
return NULL;
|
|
}
|
|
memset(ctx, 0, sizeof(struct shinkos1245_ctx));
|
|
|
|
ctx->tonecurve = PARAM_TABLE_STANDARD;
|
|
|
|
return ctx;
|
|
}
|
|
|
|
static int shinkos1245_attach(void *vctx, struct dyesub_connection *conn, uint8_t jobid)
|
|
{
|
|
struct shinkos1245_ctx *ctx = vctx;
|
|
|
|
ctx->conn = conn;
|
|
|
|
/* Ensure jobid is sane */
|
|
ctx->jobid = jobid & 0x7f;
|
|
if (!ctx->jobid)
|
|
ctx->jobid++;
|
|
|
|
if (test_mode < TEST_MODE_NOATTACH) {
|
|
/* Query Media */
|
|
if (shinkos1245_get_media(ctx))
|
|
return CUPS_BACKEND_FAILED;
|
|
if (!ctx->num_medias) {
|
|
ERROR("Media Query Error\n");
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
} else {
|
|
int media_code = 1;
|
|
if (getenv("MEDIA_CODE"))
|
|
media_code = strtol(getenv("MEDIA_CODE"), NULL, 16);
|
|
|
|
ctx->media_8x12 = media_code;
|
|
ctx->num_medias = 0;
|
|
}
|
|
ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
|
|
ctx->marker.name = ctx->media_8x12 ? "8x12" : "8x10";
|
|
ctx->marker.numtype = ctx->media_8x12;
|
|
ctx->marker.levelmax = ctx->media_8x12 ? 230 : 280;
|
|
ctx->marker.levelnow = CUPS_MARKER_UNKNOWN;
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int shinkos1245_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
|
|
struct shinkos1245_ctx *ctx = vctx;
|
|
int ret;
|
|
|
|
struct sinfonia_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->common.jobsize = sizeof(*job);
|
|
|
|
/* Common read/parse code */
|
|
ret = sinfonia_read_parse(data_fd, 1245, job);
|
|
if (ret) {
|
|
free(job);
|
|
return ret;
|
|
}
|
|
|
|
/* Use larger of our copy counts */
|
|
if (job->common.copies < copies)
|
|
job->common.copies = copies;
|
|
|
|
*vjob = job;
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int shinkos1245_main_loop(void *vctx, const void *vjob, int wait_for_return) {
|
|
struct shinkos1245_ctx *ctx = vctx;
|
|
int i, num, last_state = -1, state = S_IDLE;
|
|
struct shinkos1245_resp_status status1, status2;
|
|
int copies;
|
|
|
|
const struct sinfonia_printjob *job = vjob;
|
|
|
|
if (!ctx)
|
|
return CUPS_BACKEND_FAILED;
|
|
if (!job)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
copies = job->common.copies;
|
|
|
|
/* Make sure print size is supported */
|
|
for (i = 0 ; i < ctx->num_medias ; i++) {
|
|
if (job->jp.media == ctx->medias[i].code &&
|
|
job->jp.method == ctx->medias[i].method &&
|
|
job->jp.rows == ctx->medias[i].rows &&
|
|
job->jp.columns == ctx->medias[i].columns)
|
|
break;
|
|
}
|
|
if (i == ctx->num_medias) {
|
|
ERROR("Unsupported print type\n");
|
|
return CUPS_BACKEND_HOLD;
|
|
}
|
|
|
|
/* Fix max print count. */
|
|
if (copies > 9999) // XXX test against remaining media?
|
|
copies = 9999;
|
|
|
|
top:
|
|
if (state != last_state) {
|
|
if (dyesub_debug)
|
|
DEBUG("last_state %d new %d\n", last_state, state);
|
|
}
|
|
|
|
/* Send status query */
|
|
i = shinkos1245_get_status(ctx, &status1);
|
|
if (i < 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
if (memcmp(&status1, &status2, sizeof(status1))) {
|
|
memcpy(&status2, &status1, sizeof(status1));
|
|
// status changed.
|
|
} else if (state == last_state) {
|
|
sleep(1);
|
|
goto top;
|
|
}
|
|
|
|
/* Make sure we're not in an error state */
|
|
if (status1.state.status1 == STATE_STATUS1_ERROR)
|
|
goto printer_error;
|
|
|
|
last_state = state;
|
|
|
|
fflush(logger);
|
|
|
|
switch (state) {
|
|
case S_IDLE:
|
|
if (status1.state.status1 == STATE_STATUS1_STANDBY) {
|
|
state = S_PRINTER_READY_CMD;
|
|
break;
|
|
}
|
|
|
|
#if 0 // XXX is this necessary
|
|
if (status1.state.status1 == STATE_STATUS1_WAIT) {
|
|
INFO("Printer busy: %s\n",
|
|
sinfonia_1x45_status_str(status1.state.status1, status1.state.status2, status1.state.error));
|
|
break;
|
|
}
|
|
#endif
|
|
/* If the printer is "busy" check to see if there's any
|
|
open memory banks so we can queue the next print */
|
|
|
|
/* make sure we're not colliding with an existing
|
|
jobid */
|
|
while (ctx->jobid == status1.counters2.bank1_id ||
|
|
ctx->jobid == status1.counters2.bank2_id) {
|
|
ctx->jobid++;
|
|
ctx->jobid &= 0x7f;
|
|
if (!ctx->jobid)
|
|
ctx->jobid++;
|
|
}
|
|
|
|
if (!status1.counters2.bank1_remain ||
|
|
!status1.counters2.bank2_remain) {
|
|
state = S_PRINTER_READY_CMD;
|
|
break;
|
|
}
|
|
break;
|
|
case S_PRINTER_READY_CMD: {
|
|
struct shinkos1245_cmd_print cmd;
|
|
|
|
/* Set matte intensity */
|
|
if (job->jp.mattedepth != 0x7fffffff) {
|
|
int current = -1;
|
|
i = shinkos1245_get_matte(ctx, ¤t);
|
|
if (i < 0)
|
|
goto printer_error2;
|
|
if (current != job->jp.mattedepth) {
|
|
i = shinkos1245_set_matte(ctx, job->jp.mattedepth);
|
|
if (i < 0)
|
|
goto printer_error2;
|
|
if (i > 0) {
|
|
INFO("Can't set matte intensity when printing in progress...\n");
|
|
state = S_IDLE;
|
|
sleep(1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
INFO("Sending print job (internal id %u)\n", ctx->jobid);
|
|
|
|
shinkos1245_fill_hdr(&cmd.hdr);
|
|
cmd.cmd[0] = 0x0a;
|
|
cmd.cmd[1] = 0x00;
|
|
|
|
cmd.id = ctx->jobid;
|
|
cmd.count = uint16_to_packed_bcd(copies);
|
|
cmd.columns = cpu_to_be16(job->jp.columns);
|
|
cmd.rows = cpu_to_be16(job->jp.rows);
|
|
cmd.media = job->jp.media;
|
|
cmd.mode = (job->jp.oc_mode & 0x3f) || ((job->jp.dust & 0x3) << 6);
|
|
cmd.combo = job->jp.method;
|
|
|
|
/* Issue print command */
|
|
i = shinkos1245_do_cmd(ctx, &cmd, sizeof(cmd),
|
|
&status1, sizeof(status1),
|
|
&num);
|
|
status1.state.status2 = be32_to_cpu(status1.state.status2);
|
|
if (i < 0)
|
|
goto printer_error;
|
|
|
|
/* Check for buffer full state, and wait if we're full */
|
|
if (status1.code != CMD_CODE_OK) {
|
|
if (status1.print_status == STATUS_PRINTING) {
|
|
sleep(1);
|
|
break;
|
|
} else {
|
|
goto printer_error;
|
|
}
|
|
}
|
|
|
|
/* Check for error states */
|
|
if (status1.state.status1 == STATE_STATUS1_ERROR)
|
|
goto printer_error;
|
|
|
|
/* Send over data */
|
|
INFO("Sending image data to printer\n");
|
|
if ((i = send_data(ctx->conn,
|
|
job->databuf, job->datalen)))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
INFO("Waiting for printer to acknowledge completion\n");
|
|
sleep(1);
|
|
state = S_PRINTER_SENT_DATA;
|
|
break;
|
|
}
|
|
case S_PRINTER_SENT_DATA:
|
|
if (!wait_for_return) {
|
|
INFO("Fast return mode enabled.\n");
|
|
state = S_FINISHED;
|
|
}
|
|
/* Check for completion */
|
|
if (status1.print_status == STATUS_IDLE)
|
|
state = S_FINISHED;
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (state != S_FINISHED)
|
|
goto top;
|
|
|
|
INFO("Print complete\n");
|
|
|
|
return CUPS_BACKEND_OK;
|
|
|
|
printer_error:
|
|
ERROR("Printer Error: %s # %02x %08x %02x\n",
|
|
sinfonia_1x45_status_str(status1.state.status1, status1.state.status2, status1.state.error),
|
|
status1.state.status1, status1.state.status2, status1.state.error);
|
|
printer_error2:
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
static int shinkos1245_query_serno(struct dyesub_connection *conn, char *buf, int buf_len)
|
|
{
|
|
struct shinkos1245_resp_getid resp;
|
|
int i;
|
|
|
|
struct shinkos1245_ctx ctx = {
|
|
.conn = conn,
|
|
};
|
|
|
|
i = shinkos1245_get_printerid(&ctx, &resp);
|
|
if (i < 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
for (i = 0 ; i < (int) sizeof(resp.data) && i < buf_len; i++) {
|
|
buf[i] = resp.data[i];
|
|
}
|
|
|
|
/* Ensure null-termination */
|
|
if (i < buf_len)
|
|
buf[i] = 0;
|
|
else
|
|
buf[buf_len-1] = 0;
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int shinkos1245_query_markers(void *vctx, struct marker **markers, int *count)
|
|
{
|
|
struct shinkos1245_ctx *ctx = vctx;
|
|
struct shinkos1245_resp_status status;
|
|
|
|
/* Query status */
|
|
if (shinkos1245_get_status(ctx, &status))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
ctx->marker.levelnow = ctx->marker.levelmax - be32_to_cpu(status.counters.media);
|
|
|
|
if (markers) *markers = &ctx->marker;
|
|
if (markers) *count = 1;
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int shinkos1245_query_stats(void *vctx, struct printerstats *stats)
|
|
{
|
|
struct shinkos1245_ctx *ctx = vctx;
|
|
struct shinkos1245_resp_status status;
|
|
|
|
if (shinkos1245_query_markers(ctx, NULL, NULL))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
if (shinkos1245_get_status(ctx, &status))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
stats->mfg = "Sinfonia";
|
|
stats->model = "E1 / S1245";
|
|
|
|
if (shinkos1245_query_serno(ctx->conn,
|
|
ctx->serial, sizeof(ctx->serial)))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
stats->serial = ctx->serial;
|
|
|
|
snprintf(ctx->fwver, sizeof(ctx->fwver)-1,
|
|
"%d / %d", be16_to_cpu(status.versions.main_control),
|
|
be16_to_cpu(status.versions.dsp_control));
|
|
stats->fwver = ctx->fwver;
|
|
|
|
stats->decks = 1;
|
|
stats->mediatype[0] = ctx->marker.name;
|
|
stats->levelmax[0] = ctx->marker.levelmax;
|
|
stats->levelnow[0] = ctx->marker.levelnow;
|
|
stats->name[0] = "Roll";
|
|
stats->status[0] = strdup(sinfonia_1x45_status_str(status.state.status1, status.state.status2, status.state.error));
|
|
stats->cnt_life[0] = be32_to_cpu(status.counters.lifetime);
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static const char *shinkos1245_prefixes[] = {
|
|
"shinkos1245", /* Family Name */
|
|
NULL
|
|
};
|
|
|
|
static const struct device_id shinkos1245_devices[] = {
|
|
{ 0x10ce, 0x0007, P_SHINKO_S1245, NULL, "shinko-chcs1245"},
|
|
{ 0x10ce, 0x0007, P_SHINKO_S1245, NULL, "sinfonia-chcs1245"}, /* Duplicate */
|
|
{ 0, 0, 0, NULL, NULL}
|
|
};
|
|
|
|
const struct dyesub_backend shinkos1245_backend = {
|
|
.name = "Shinko/Sinfonia CHC-S1245/E1",
|
|
.version = "0.35" " (lib " LIBSINFONIA_VER ")",
|
|
.uri_prefixes = shinkos1245_prefixes,
|
|
.devices = shinkos1245_devices,
|
|
.cmdline_usage = shinkos1245_cmdline,
|
|
.cmdline_arg = shinkos1245_cmdline_arg,
|
|
.init = shinkos1245_init,
|
|
.attach = shinkos1245_attach,
|
|
.cleanup_job = sinfonia_cleanup_job,
|
|
.read_parse = shinkos1245_read_parse,
|
|
.main_loop = shinkos1245_main_loop,
|
|
.query_serno = shinkos1245_query_serno,
|
|
.query_markers = shinkos1245_query_markers,
|
|
.query_stats = shinkos1245_query_stats,
|
|
};
|
|
|
|
/* CHC-S1245 data format
|
|
|
|
Spool file consists of an 116-byte header, followed by RGB-packed data,
|
|
followed by a 4-byte footer. Header appears to consist of a series of
|
|
4-byte Little Endian words.
|
|
|
|
10 00 00 00 MM MM 00 00 00 00 00 00 01 00 00 00 MM == Model (ie 1245d)
|
|
64 00 00 00 00 00 00 00 TT 00 00 00 00 00 00 00 TT == Media Size (0x10 fixed)
|
|
MM 00 00 00 PP 00 00 00 00 00 00 00 ZZ ZZ ZZ ZZ MM = Print Method (aka cut control), PP = Default/Glossy/Matte (0x01/0x03/0x05), ZZ == matte intensity (0x7fffffff for glossy, else 0x00000000 +- 25 for matte)
|
|
VV 00 00 00 WW WW 00 00 HH HH 00 00 XX XX 00 00 VV == dust; 0x00 default, 0x01 off, 0x02 on, XX == Copies
|
|
00 00 00 00 00 00 00 00 00 00 00 00 ce ff ff ff
|
|
00 00 00 00 ce ff ff ff QQ QQ 00 00 ce ff ff ff QQ == DPI, ie 300.
|
|
00 00 00 00 ce ff ff ff 00 00 00 00 00 00 00 00
|
|
00 00 00 00
|
|
|
|
[[Packed RGB payload of WW*HH*3 bytes]]
|
|
|
|
04 03 02 01 [[ footer ]]
|
|
|
|
|
|
*/
|