selphy_print/backend_shinkos1245.c

1078 lines
26 KiB
C
Raw Normal View History

/*
* Shinko/Sinfonia CHC-S1245 CUPS backend -- libusb-1.0 version
*
* (c) 2013-2015 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]
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "backend_common.h"
/* Structure of printjob header. All fields are LITTLE ENDIAN */
struct s1245_printjob_hdr {
uint32_t len1; /* Fixed at 0x10 */
uint32_t model; /* Equal to the printer model (eg '1245' or '2145' decimal) */
uint32_t unk2; /* Null */
uint32_t unk3; /* Fixed at 0x01 */
uint32_t len2; /* Fixed at 0x64 */
uint32_t unk5; /* Null */
uint32_t media; /* Fixed at 0x10 */
uint32_t unk6; /* Null */
uint32_t method; /* Print Method */
uint32_t mode; /* Print Mode */
uint32_t unk7; /* Null */
int32_t mattedepth; /* 0x7fffffff for glossy, 0x00 +- 25 for matte */
uint32_t dust; /* Dust control */
uint32_t columns;
uint32_t rows;
uint32_t copies;
uint32_t unk10; /* Null */
uint32_t unk11; /* Null */
uint32_t unk12; /* Null */
uint32_t unk13; /* 0xceffffff */
uint32_t unk14; /* Null */
uint32_t unk15; /* 0xceffffff */
uint32_t dpi; /* Fixed at '300' (decimal) */
uint32_t unk16; /* 0xceffffff */
uint32_t unk17; /* Null */
uint32_t unk18; /* 0xceffffff */
uint32_t unk19; /* Null */
uint32_t unk20; /* Null */
uint32_t unk21; /* Null */
} __attribute__((packed));
/* 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];
} __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 */
uint8_t count[2]; /* # Copies in BCD, 1-9999 */
uint16_t columns; /* Fixed at 2448 */
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; /* LE */
uint8_t error;
} 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;
2015-02-08 18:59:26 -05:00
uint8_t ver_ctrl;
uint8_t control_flag; // 0x00 == epson, 0x01 == cypress
} counters;
struct {
uint16_t main_boot;
uint16_t main_control;
uint16_t dsp_boot;
uint16_t dsp_control;
} 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 */
} counters2;
uint8_t curve_status;
} __attribute__((packed));
enum {
CMD_CODE_OK = 1,
CMD_CODE_BAD = 2,
};
enum {
STATUS_PRINTING = 1,
STATUS_IDLE = 2,
};
enum {
STATE_STATUS1_STANDBY = 1,
STATE_STATUS1_ERROR = 2,
STATE_STATUS1_WAIT = 3,
};
2015-02-08 18:59:26 -05:00
#define STATE_STANDBY_STATUS2 0x0
enum {
WAIT_STATUS2_INIT = 0,
WAIT_STATUS2_RIBBON = 1,
WAIT_STATUS2_THERMAL = 2,
WAIT_STATUS2_OPERATING = 3,
WAIT_STATUS2_BUSY = 4,
};
#define ERROR_STATUS2_CTRL_CIRCUIT (1<<31)
#define ERROR_STATUS2_MECHANISM_CTRL (1<<30)
#define ERROR_STATUS2_SENSOR (1<<13)
#define ERROR_STATUS2_COVER_OPEN (1<<12)
#define ERROR_STATUS2_TEMP_SENSOR (1<<9)
#define ERROR_STATUS2_PAPER_JAM (1<<8)
#define ERROR_STATUS2_PAPER_EMPTY (1<<6)
#define ERROR_STATUS2_RIBBON_ERR (1<<4)
enum {
CTRL_CIR_ERROR_EEPROM1 = 0x01,
CTRL_CIR_ERROR_EEPROM2 = 0x02,
CTRL_CIR_ERROR_DSP = 0x04,
CTRL_CIR_ERROR_CRC_MAIN = 0x06,
CTRL_CIR_ERROR_DL_MAIN = 0x07,
CTRL_CIR_ERROR_CRC_DSP = 0x08,
CTRL_CIR_ERROR_DL_DSP = 0x09,
CTRL_CIR_ERROR_ASIC = 0x0a,
CTRL_CIR_ERROR_DRAM = 0x0b,
CTRL_CIR_ERROR_DSPCOMM = 0x29,
};
enum {
MECH_ERROR_HEAD_UP = 0x01,
MECH_ERROR_HEAD_DOWN = 0x02,
MECH_ERROR_MAIN_PINCH_UP = 0x03,
MECH_ERROR_MAIN_PINCH_DOWN = 0x04,
MECH_ERROR_SUB_PINCH_UP = 0x05,
MECH_ERROR_SUB_PINCH_DOWN = 0x06,
MECH_ERROR_FEEDIN_PINCH_UP = 0x07,
MECH_ERROR_FEEDIN_PINCH_DOWN = 0x08,
MECH_ERROR_FEEDOUT_PINCH_UP = 0x09,
MECH_ERROR_FEEDOUT_PINCH_DOWN = 0x0a,
MECH_ERROR_CUTTER_LR = 0x0b,
MECH_ERROR_CUTTER_RL = 0x0c,
};
enum {
SENSOR_ERROR_CUTTER = 0x05,
SENSOR_ERROR_HEAD_DOWN = 0x09,
SENSOR_ERROR_HEAD_UP = 0x0a,
SENSOR_ERROR_MAIN_PINCH_DOWN = 0x0b,
SENSOR_ERROR_MAIN_PINCH_UP = 0x0c,
SENSOR_ERROR_FEED_PINCH_DOWN = 0x0d,
SENSOR_ERROR_FEED_PINCH_UP = 0x0e,
SENSOR_ERROR_EXIT_PINCH_DOWN = 0x0f,
SENSOR_ERROR_EXIT_PINCH_UP = 0x10,
SENSOR_ERROR_LEFT_CUTTER = 0x11,
SENSOR_ERROR_RIGHT_CUTTER = 0x12,
SENSOR_ERROR_CENTER_CUTTER = 0x13,
SENSOR_ERROR_UPPER_CUTTER = 0x14,
SENSOR_ERROR_PAPER_FEED_COVER = 0x15,
};
enum {
TEMP_SENSOR_ERROR_HEAD_HIGH = 0x01,
TEMP_SENSOR_ERROR_HEAD_LOW = 0x02,
TEMP_SENSOR_ERROR_ENV_HIGH = 0x03,
TEMP_SENSOR_ERROR_ENV_LOW = 0x04,
};
enum {
COVER_OPEN_ERROR_UPPER = 0x01,
COVER_OPEN_ERROR_LOWER = 0x02,
};
enum {
PAPER_EMPTY_ERROR = 0x00,
};
enum {
RIBBON_ERROR = 0x00,
};
enum {
CURVE_TABLE_STATUS_INITIAL = 0x00,
CURVE_TABLE_STATUS_USERSET = 0x01,
CURVE_TABLE_STATUS_CURRENT = 0x02,
};
// XXX Paper jam has 0x01 -> 0xff as error codes
/* 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));
2015-02-08 18:59:26 -05:00
struct shinkos1245_mediadesc {
uint8_t code; /* Fixed at 0x10 */
uint16_t columns; /* BE */
uint16_t rows; /* BE */
uint8_t type; /* MEDIA_TYPE_* */
uint8_t print_type; /* aka "print method" in the spool file */
uint8_t reserved[3];
} __attribute__((packed));
struct shinkos1245_resp_media {
uint8_t code;
uint8_t reserved[5];
2015-02-08 18:59:26 -05:00
uint8_t count; /* 1-5? */
struct shinkos1245_mediadesc data[5];
} __attribute__((packed));
enum {
MEDIA_TYPE_UNKNOWN = 0x00,
MEDIA_TYPE_PAPER = 0x01,
};
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 */
2015-02-08 18:59:26 -05:00
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));
enum {
TONE_TABLE_STANDARD = 0,
TONE_TABLE_USER = 1,
TONE_TABLE_CURRENT = 2,
};
enum {
PARAM_TABLE_STANDARD = 1,
PARAM_TABLE_FINE = 2,
};
/* 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;
uint8_t level;
uint8_t reserved[3];
} __attribute__((packed));
#define MATTE_MODE_MATTE 0x00
/* Private data stucture */
struct shinkos1245_ctx {
struct libusb_device_handle *dev;
uint8_t endp_up;
uint8_t endp_down;
uint8_t jobid;
uint8_t fast_return;
struct s1245_printjob_hdr hdr;
struct shinkos1245_mediadesc medias[15];
int num_medias;
uint8_t *databuf;
int datalen;
};
enum {
S_IDLE = 0,
S_PRINTER_READY_CMD,
S_PRINTER_SENT_DATA,
S_FINISHED,
};
2015-02-08 18:59:26 -05:00
/* 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->dev, ctx->endp_down,
cmd, cmd_len)))
return (ret < 0) ? ret : -99;
/* Read response */
ret = read_data(ctx->dev, ctx->endp_up,
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;
}
return 0;
}
static int shinkos1245_get_media(struct shinkos1245_ctx *ctx)
{
struct shinkos1245_cmd_getmedia cmd;
struct shinkos1245_resp_media resp;
int i, j;
int ret, num;
shinkos1245_fill_hdr(&cmd.hdr);
memset(cmd.pad, 0, sizeof(cmd.pad));
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 < 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].print_type = resp.data[j].print_type;
ctx->num_medias++;
}
if (resp.count < 5)
break;
}
return 0;
}
static int shinkos1245_get_printerid(struct shinkos1245_ctx *ctx,
struct shinkos1245_resp_getid *resp)
{
struct shinkos1245_cmd_getstatus 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 0;
}
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 0;
}
/* Structure dumps */
2015-02-08 18:59:26 -05:00
static void shinkos1245_dump_status(struct shinkos1245_resp_status *sts)
{
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);
switch(sts->state.status1) {
case STATE_STATUS1_STANDBY:
INFO("Printer State: Standby\n");
break;
case STATE_STATUS1_WAIT:
switch(sts->state.status2) {
case WAIT_STATUS2_INIT:
detail = "Initializing";
break;
case WAIT_STATUS2_RIBBON:
detail = "Ribbon Winding";
break;
case WAIT_STATUS2_THERMAL:
detail = "Cooling Down";
break;
case WAIT_STATUS2_OPERATING:
detail = "Operating section busy";
break;
case WAIT_STATUS2_BUSY:
detail = "In progress";
break;
default:
detail = "Unknown";
break;
}
INFO("Printer State: Wait (%s)\n", detail);
break;
case STATE_STATUS1_ERROR:
switch (sts->state.status2) {
case ERROR_STATUS2_CTRL_CIRCUIT:
detail = "Control Circuit";
break;
case ERROR_STATUS2_MECHANISM_CTRL:
detail = "Mechanism Control";
break;
case ERROR_STATUS2_SENSOR:
detail = "Sensor";
break;
case ERROR_STATUS2_COVER_OPEN:
detail = "Cover Open";
break;
case ERROR_STATUS2_TEMP_SENSOR:
detail = "Temperature Sensor";
break;
case ERROR_STATUS2_PAPER_JAM:
detail = "Paper Jam";
break;
case ERROR_STATUS2_PAPER_EMPTY:
detail = "Paper Empty";
break;
case ERROR_STATUS2_RIBBON_ERR:
detail = "Ribbon Error";
break;
default:
detail = "Unknown";
}
INFO("Printer State: Error (%s - %02x)\n", detail,
sts->state.error);
break;
default:
WARNING("Printer State: Unknown (%02x/%08x/%02x)\n", sts->state.status1, sts->state.status2, sts->state.error);
break;
}
INFO("Counters:\n");
INFO("\tLifetime : %d\n", be32_to_cpu(sts->counters.lifetime));
INFO("\tThermal Head : %d\n", be32_to_cpu(sts->counters.maint));
INFO("\tMedia : %d\n", be32_to_cpu(sts->counters.media));
INFO("\tCutter : %d\n", be32_to_cpu(sts->counters.cutter));
INFO("Versions:\n");
INFO("\tUSB Boot : %d\n", sts->counters.ver_boot);
INFO("\tUSB Control : %d\n", sts->counters.ver_ctrl);
INFO("\tMain Boot : %d\n", be16_to_cpu(sts->versions.main_boot));
INFO("\tMain Control: %d\n", be16_to_cpu(sts->versions.main_control));
INFO("\tDSP Boot : %d\n", be16_to_cpu(sts->versions.dsp_boot));
INFO("\tDSP Control : %d\n", be16_to_cpu(sts->versions.dsp_control));
// INFO("USB TypeFlag: %02x\n", sts->counters.control_flag);
INFO("Bank 1 ID: %d\n", sts->counters2.bank1_id);
INFO("\tPrints: %d/%d (%d complete)\n",
sts->counters2.bank1_remain, sts->counters2.bank1_spec,
sts->counters2.bank1_complete);
INFO("Bank 2 ID: %d\n", sts->counters2.bank2_id);
INFO("\tPrints: %d/%d (%d complete)\n",
sts->counters2.bank2_remain, sts->counters2.bank2_spec,
sts->counters2.bank2_complete);
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 shinkos1245_mediadesc *medias,
int count)
{
int i;
INFO("Supported print sizes: %d\n", count);
for (i = 0 ; i < count ; i++) {
INFO("\t %02x: %04d*%04d (%02x/%02d)\n",
medias[i].print_type,
medias[i].columns,
medias[i].rows,
medias[i].code, medias[i].type);
}
}
2015-02-08 18:59:26 -05:00
/* Driver API */
static void shinkos1245_cmdline(void)
{
DEBUG("\t\t[ -f ] # Use fast return mode\n");
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");
}
int shinkos1245_cmdline_arg(void *vctx, int argc, char **argv)
{
struct shinkos1245_ctx *ctx = vctx;
int i, j = 0;
/* Reset arg parsing */
optind = 1;
opterr = 0;
while ((i = getopt(argc, argv, "fmsuU:")) >= 0) {
switch(i) {
case 'f':
if (!ctx)
return 1;
ctx->fast_return = 1;
break;
case 'm':
if (!ctx)
return 1;
j = shinkos1245_get_media(ctx);
if (!j)
shinkos1245_dump_media(ctx->medias, ctx->num_medias);
break;
case 's': {
if (!ctx)
return 1;
struct shinkos1245_resp_status sts;
j = shinkos1245_get_status(ctx, &sts);
if (!j)
shinkos1245_dump_status(&sts);
break;
}
case 'u': {
if (!ctx)
return 1;
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':
if (!ctx)
return 1;
j = shinkos1245_set_printerid(ctx, optarg);
break;
2015-02-08 18:59:26 -05:00
default:
break; /* Ignore completely */
}
if (j) return j;
}
return 0;
}
static void *shinkos1245_init(void)
{
struct shinkos1245_ctx *ctx = malloc(sizeof(struct shinkos1245_ctx));
if (!ctx)
return NULL;
memset(ctx, 0, sizeof(struct shinkos1245_ctx));
/* Use Fast return by default in CUPS mode */
if (getenv("DEVICE_URI") || getenv("FAST_RETURN"))
ctx->fast_return = 1;
return ctx;
}
static void shinkos1245_attach(void *vctx, struct libusb_device_handle *dev,
uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
{
struct shinkos1245_ctx *ctx = vctx;
ctx->dev = dev;
ctx->endp_up = endp_up;
ctx->endp_down = endp_down;
/* Ensure jobid is sane */
ctx->jobid = (jobid & 0x7f) + 1;
}
static void shinkos1245_teardown(void *vctx) {
struct shinkos1245_ctx *ctx = vctx;
if (!ctx)
return;
if (ctx->databuf)
free(ctx->databuf);
free(ctx);
}
static int shinkos1245_read_parse(void *vctx, int data_fd) {
struct shinkos1245_ctx *ctx = vctx;
int ret;
uint8_t tmpbuf[4];
if (!ctx)
return CUPS_BACKEND_FAILED;
/* Read in then validate header */
ret = read(data_fd, &ctx->hdr, sizeof(ctx->hdr));
if (ret < 0)
return ret;
if (ret < 0 || ret != sizeof(ctx->hdr))
return CUPS_BACKEND_CANCEL;
if (le32_to_cpu(ctx->hdr.len1) != 0x10 ||
le32_to_cpu(ctx->hdr.len2) != 0x64 ||
le32_to_cpu(ctx->hdr.dpi) != 300) {
ERROR("Unrecognized header data format!\n");
return CUPS_BACKEND_CANCEL;
}
ctx->hdr.model = le32_to_cpu(ctx->hdr.model);
switch(ctx->hdr.model != 1245) {
ERROR("Unrecognized printer (%d)!\n", ctx->hdr.model);
return CUPS_BACKEND_CANCEL;
}
/* Finish byteswapping */
ctx->hdr.media = le32_to_cpu(ctx->hdr.media);
ctx->hdr.method = le32_to_cpu(ctx->hdr.method);
ctx->hdr.mode = le32_to_cpu(ctx->hdr.mode);
ctx->hdr.mattedepth = le32_to_cpu(ctx->hdr.mattedepth);
ctx->hdr.dust = le32_to_cpu(ctx->hdr.dust);
ctx->hdr.columns = le32_to_cpu(ctx->hdr.columns);
ctx->hdr.rows = le32_to_cpu(ctx->hdr.rows);
ctx->hdr.copies = le32_to_cpu(ctx->hdr.copies);
/* Allocate space */
if (ctx->databuf) {
free(ctx->databuf);
ctx->databuf = NULL;
}
ctx->datalen = ctx->hdr.rows * ctx->hdr.columns * 3;
ctx->databuf = malloc(ctx->datalen);
if (!ctx->databuf) {
ERROR("Memory allocation failure!\n");
return CUPS_BACKEND_FAILED;
}
{
int remain = ctx->datalen;
uint8_t *ptr = ctx->databuf;
do {
ret = read(data_fd, ptr, remain);
if (ret < 0) {
ERROR("Read failed (%d/%d/%d)\n",
ret, remain, ctx->datalen);
perror("ERROR: Read failed");
return ret;
}
ptr += ret;
remain -= ret;
} while (remain);
}
/* Make sure footer is sane too */
ret = read(data_fd, tmpbuf, 4);
if (ret != 4) {
ERROR("Read failed (%d/%d/%d)\n",
ret, 4, 4);
perror("ERROR: Read failed");
return ret;
}
if (tmpbuf[0] != 0x04 ||
tmpbuf[1] != 0x03 ||
tmpbuf[2] != 0x02 ||
tmpbuf[3] != 0x01) {
ERROR("Unrecognized footer data format!\n");
return CUPS_BACKEND_FAILED;
}
return CUPS_BACKEND_OK;
}
static int shinkos1245_main_loop(void *vctx, int copies) {
struct shinkos1245_ctx *ctx = vctx;
int i, last_state = -1, state = S_IDLE;
struct shinkos1245_resp_status status1, status2;
// XXX query printer info
/* Query Media information if necessary */
if (!ctx->num_medias)
shinkos1245_get_media(ctx);
if (!ctx->num_medias) {
ERROR("Media Query Error\n");
return CUPS_BACKEND_FAILED;
}
/* Make sure print size is supported */
for (i = 0 ; i < ctx->num_medias ; i++) {
if (ctx->hdr.media != ctx->medias[i].code)
break;
if (ctx->hdr.method != ctx->medias[i].print_type)
break;
if (ctx->hdr.rows != ctx->medias[i].rows)
break;
if (ctx->hdr.columns != ctx->medias[i].columns)
break;
}
if (i == ctx->num_medias) {
ERROR("Unsupported print type\n");
return CUPS_BACKEND_HOLD;
}
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, check for errors and whatnot
} else if (state == last_state) {
sleep(1);
goto top;
}
last_state = state;
fflush(stderr);
switch (state) {
case S_IDLE:
// check to see if printer has free bank, if so, continue
break;
case S_PRINTER_READY_CMD:
// issue a print command, send over data.
break;
case S_PRINTER_SENT_DATA:
if (ctx->fast_return) {
INFO("Fast return mode enabled.\n");
state = S_FINISHED;
}
// check for completion
break;
default:
break;
}
if (state != S_FINISHED)
goto top;
/* This printer handles copies internally */
copies = 1;
/* 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;
printer_error:
return CUPS_BACKEND_FAILED;
}
static int shinkos1245_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len)
{
struct shinkos1245_resp_getid resp;
int i;
struct shinkos1245_ctx ctx = {
.dev = dev,
.endp_up = endp_up,
.endp_down = endp_down,
};
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;
}
/* Exported */
#define USB_VID_SHINKO 0x10CE
#define USB_PID_SHINKO_S1245 0x0007
struct dyesub_backend shinkos1245_backend = {