2011 lines
54 KiB
C
2011 lines
54 KiB
C
/*
|
|
* Mitsubishi CP-9xxx Photo Printer Family CUPS backend
|
|
*
|
|
* (c) 2014-2024 Solomon Peachy <pizza@shaftnet.org>
|
|
*
|
|
* 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 mitsu9550_backend
|
|
|
|
#include "backend_common.h"
|
|
#include "backend_mitsu.h"
|
|
|
|
#define MITSU_M98xx_LAMINATE_FILE "M98MATTE.raw"
|
|
#define MITSU_M98xx_DATATABLE_FILE "M98TABLE.dat"
|
|
#define MITSU_M98xx_LUT_FILE "M98XXL01.lut"
|
|
#define MITSU_CP30D_LUT_FILE "CP30LT_1.lut"
|
|
#define LAMINATE_STRIDE 1868
|
|
|
|
/* Spool file structures */
|
|
|
|
/* Print parameters1 */
|
|
struct mitsu9550_hdr1 {
|
|
uint8_t cmd[4]; /* 1b 57 20 2e */
|
|
uint8_t unk[10]; /* 00 0a 10 00 [...] */
|
|
uint16_t cols; /* BE */
|
|
uint16_t rows; /* BE */
|
|
uint8_t matte; /* CP9810/9820 only. 01 for matte, 00 glossy */
|
|
uint8_t null[31];
|
|
} __attribute__((packed));
|
|
|
|
/* Print parameters2 */
|
|
struct mitsu9550_hdr2 {
|
|
uint8_t cmd[4]; /* 1b 57 21 2e */
|
|
uint8_t unk[24]; /* 00 80 00 22 08 03 [...] */
|
|
uint16_t copies; /* BE, 1-680 */
|
|
uint8_t null[2];
|
|
uint8_t cut; /* 00 == normal, 83 == 2x6*2 */
|
|
uint8_t unkb[5];
|
|
uint8_t mode; /* 00 == fine, 80 == superfine */
|
|
uint8_t unkc[11]; /* 00 [...] 00 01 ; note [7][8][9] are cp98xx extensions for sharpness/reversed/lut */
|
|
} __attribute__((packed));
|
|
|
|
/* Fine Deep selection (9550 only) */
|
|
struct mitsu9550_hdr3 {
|
|
uint8_t cmd[4]; /* 1b 57 22 2e */
|
|
uint8_t unk[7]; /* 00 40 00 [...] */
|
|
uint8_t mode2; /* 00 == normal, 01 == finedeep */
|
|
uint8_t null[38];
|
|
} __attribute__((packed));
|
|
|
|
/* Error policy? */
|
|
struct mitsu9550_hdr4 {
|
|
uint8_t cmd[4]; /* 1b 57 26 2e */
|
|
uint8_t unk[46]; /* 00 70 00 00 00 00 00 00 01 01 00 [...] */
|
|
} __attribute__((packed));
|
|
|
|
/* Data plane header */
|
|
struct mitsu9550_plane {
|
|
uint8_t cmd[4]; /* 1b 5a 54 XX */ /* XX == 0x10 if 16bpp, 0x00 for 8bpp */
|
|
uint16_t col_offset; /* BE, normally 0, where we start dumping data */
|
|
uint16_t row_offset; /* BE, normally 0, where we start dumping data */
|
|
uint16_t cols; /* BE */
|
|
uint16_t rows; /* BE */
|
|
} __attribute__((packed));
|
|
|
|
/* Command header */
|
|
struct mitsu9550_cmd {
|
|
uint8_t cmd[4];
|
|
} __attribute__((packed));
|
|
|
|
/* Private data structure */
|
|
struct mitsu9550_printjob {
|
|
struct dyesub_job_common common;
|
|
|
|
uint8_t *databuf;
|
|
uint32_t datalen;
|
|
|
|
uint16_t rows;
|
|
uint16_t cols;
|
|
uint32_t plane_len;
|
|
int is_raw;
|
|
|
|
/* Parse headers separately */
|
|
struct mitsu9550_hdr1 hdr1;
|
|
int hdr1_present;
|
|
struct mitsu9550_hdr2 hdr2;
|
|
int hdr2_present;
|
|
struct mitsu9550_hdr3 hdr3;
|
|
int hdr3_present;
|
|
struct mitsu9550_hdr4 hdr4;
|
|
int hdr4_present;
|
|
};
|
|
|
|
struct mitsu9550_ctx {
|
|
struct dyesub_connection *conn;
|
|
|
|
int is_s;
|
|
int is_98xx;
|
|
int need_lib;
|
|
int footer_len;
|
|
const char *lut_fname;
|
|
|
|
char fwver[7]; /* 6 + null */
|
|
|
|
struct marker marker;
|
|
|
|
/* CP98xx stuff */
|
|
struct mitsu_lib lib;
|
|
const struct mitsu98xx_data *m98xxdata;
|
|
};
|
|
|
|
/* Printer data structures */
|
|
#define CP9XXX_STS_x20 0x20
|
|
#define CP9XXX_STS_x21 0x21 /* struct mitsu9550_status2 */
|
|
#define CP9XXX_STS_x22 0x22
|
|
#define CP9XXX_STS_FWVER 0x23
|
|
#define CP9XXX_STS_MEDIA 0x24 /* struct mitsu9550_media */
|
|
#define CP9XXX_STS_x26 0x26
|
|
#define CP9XXX_STS_x30 0x30 /* struct mitsu9550_status */
|
|
#define CP9XXX_STS_x32 0x32 /* struct mitsucp30_status */
|
|
|
|
struct mitsu9550_media {
|
|
uint8_t hdr[2]; /* 24 2e */
|
|
uint8_t unk[12];
|
|
uint8_t type;
|
|
uint8_t unka[11];
|
|
uint16_t unkb; /* 0d 00 (CP9810), 00 00 (others?) */
|
|
uint16_t max; /* BE, prints per media */
|
|
uint16_t remain2; /* BE, prints remaining (CP30)*/
|
|
uint16_t remain; /* BE, prints remaining (Everything else) */
|
|
uint8_t unkc[14];
|
|
} __attribute__((packed));
|
|
|
|
struct mitsu9550_status {
|
|
uint8_t hdr[2]; /* 30 2e */
|
|
uint8_t null[4];
|
|
uint8_t sts1; // MM
|
|
uint8_t nullb[1];
|
|
uint16_t copies; // BE, NN
|
|
uint8_t sts2; // ZZ (9600 only?)
|
|
uint8_t nullc[5];
|
|
uint8_t sts3; // QQ
|
|
uint8_t sts4; // RR
|
|
uint8_t sts5; // SS
|
|
uint8_t nulld[25];
|
|
uint8_t sts6; // TT
|
|
uint8_t sts7; // UU
|
|
uint8_t nulle[2];
|
|
} __attribute__((packed));
|
|
|
|
struct mitsucp30_status {
|
|
uint8_t hdr[2]; /* 32 2e */
|
|
uint8_t zero[2];
|
|
uint16_t serno[13]; /* UTF16, BE */
|
|
uint8_t sts; /* CP30_STS_* */
|
|
uint8_t sts2;
|
|
uint8_t zerob;
|
|
uint16_t err; /* CP30_ERR_* */
|
|
uint8_t zeroc[2];
|
|
uint8_t remain; /* on media */
|
|
uint8_t zerod[9];
|
|
} __attribute__((packed));
|
|
|
|
#define CP30_STS_IDLE 0x00
|
|
#define CP30_STS_PRINT 0x20
|
|
|
|
#define CP30_STS_PRINT_A 0x10 //Load
|
|
#define CP30_STS_PRINT_B 0x20 //Y
|
|
#define CP30_STS_PRINT_C 0x30 //M
|
|
#define CP30_STS_PRINT_D 0x40 //C
|
|
#define CP30_STS_PRINT_E 0x60 //Eject?
|
|
|
|
#define CP30_ERR_OK 0x0000
|
|
#define CP30_ERR_NOPC 0x0303
|
|
#define CP30_ERR_NORIBBON 0x0101
|
|
#define CP30_ERR_TRAYFULL 0x1404
|
|
|
|
struct mitsu9550_status2 {
|
|
uint8_t hdr[2]; /* 21 2e */
|
|
uint8_t unk[40];
|
|
uint16_t remain; /* BE, media remaining */
|
|
uint8_t unkb[4]; /* 0a 00 00 01 */
|
|
} __attribute__((packed));
|
|
|
|
static int mitsu9550_main_loop(void *vctx, const void *vjob, int wait_for_return);
|
|
|
|
static const char *cp30_errors(uint16_t err)
|
|
{
|
|
switch(err){
|
|
case CP30_ERR_OK: return "None";
|
|
case CP30_ERR_NOPC: return "No Paper Cassette";
|
|
case CP30_ERR_NORIBBON: return "No Ribbon Loaded";
|
|
case CP30_ERR_TRAYFULL: return "Output Tray Full";
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
|
|
static const char *cp30_media_types(uint16_t remain)
|
|
{
|
|
switch(remain) {
|
|
case 80: return "CK30S";
|
|
case 50: return "CK30L";
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
|
|
#define CMDBUF_LEN 64
|
|
#define READBACK_LEN 128
|
|
|
|
#define QUERY_STATUS_I \
|
|
const struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf; \
|
|
const struct mitsucp30_status *sts30 = (struct mitsucp30_status*) rdbuf; \
|
|
const struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf; \
|
|
uint16_t donor; \
|
|
/* media */ \
|
|
ret = mitsu9550_get_status(ctx, rdbuf, CP9XXX_STS_MEDIA); \
|
|
if (ret < 0) \
|
|
return CUPS_BACKEND_FAILED; \
|
|
\
|
|
if (ctx->conn->type == P_MITSU_CP30D) { \
|
|
donor = be16_to_cpu(media->remain2); \
|
|
} else { \
|
|
donor = be16_to_cpu(media->remain); \
|
|
} \
|
|
if (donor != ctx->marker.levelnow) { \
|
|
ctx->marker.levelnow = donor; \
|
|
dump_markers(&ctx->marker, 1, 0); \
|
|
} \
|
|
/* Sanity-check media response */ \
|
|
if ((media->remain == 0 && media->remain2 == 0) || media->max == 0) { \
|
|
ERROR("Printer out of media!\n"); \
|
|
return CUPS_BACKEND_STOP; \
|
|
} \
|
|
|
|
#define QUERY_STATUS_II \
|
|
if (ctx->conn->type != P_MITSU_CP30D) { \
|
|
/* struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf; */ \
|
|
ret = mitsu9550_get_status(ctx, rdbuf, CP9XXX_STS_x21); \
|
|
if (ret < 0) \
|
|
return CUPS_BACKEND_FAILED; \
|
|
/* XXX validate status2 ? */ \
|
|
}
|
|
|
|
#define QUERY_STATUS_III \
|
|
/* Check for known errors */ \
|
|
if (sts->sts2 != 0) { \
|
|
ERROR("Printer cover open!\n"); \
|
|
return CUPS_BACKEND_STOP; \
|
|
}
|
|
|
|
#define QUERY_STATUS_IIIB \
|
|
/* Check for known errors */ \
|
|
if (sts30->err != CP30_ERR_OK) { \
|
|
ERROR("%s (%04x)!\n", cp30_errors(be16_to_cpu(sts30->err)), be16_to_cpu(sts30->err)); \
|
|
return CUPS_BACKEND_STOP; \
|
|
}
|
|
|
|
#define QUERY_STATUS_IV \
|
|
if (ctx->conn->type == P_MITSU_CP30D) { \
|
|
ret = mitsu9550_get_status(ctx, rdbuf, CP9XXX_STS_x32); \
|
|
if (ret < 0) \
|
|
return CUPS_BACKEND_FAILED; \
|
|
\
|
|
if (sts30->sts != CP30_STS_IDLE) { \
|
|
sleep(1); \
|
|
goto top; \
|
|
} \
|
|
QUERY_STATUS_IIIB; \
|
|
} else { \
|
|
ret = mitsu9550_get_status(ctx, rdbuf, CP9XXX_STS_x30); \
|
|
if (ret < 0) \
|
|
return CUPS_BACKEND_FAILED; \
|
|
\
|
|
/* Make sure we're idle */ \
|
|
if (sts->sts5 != 0) { \
|
|
sleep(1); \
|
|
goto top; \
|
|
} \
|
|
QUERY_STATUS_III; \
|
|
} \
|
|
|
|
static int mitsu98xx_fillmatte(struct mitsu9550_printjob *job)
|
|
{
|
|
int ret;
|
|
|
|
/* Fill in the lamination plane header */
|
|
struct mitsu9550_plane *matte = (struct mitsu9550_plane *)(job->databuf + job->datalen);
|
|
matte->cmd[0] = 0x1b;
|
|
matte->cmd[1] = 0x5a;
|
|
matte->cmd[2] = 0x54;
|
|
matte->cmd[3] = 0x10;
|
|
matte->row_offset = 0;
|
|
matte->col_offset = 0;
|
|
matte->cols = cpu_to_be16(job->cols);
|
|
matte->rows = cpu_to_be16(job->rows);
|
|
job->datalen += sizeof(struct mitsu9550_plane);
|
|
|
|
ret = mitsu_readlamdata(MITSU_M98xx_LAMINATE_FILE, LAMINATE_STRIDE,
|
|
job->databuf, &job->datalen,
|
|
job->rows, job->cols, 2);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Fill in the lamination plane footer */
|
|
job->databuf[job->datalen++] = 0x1b;
|
|
job->databuf[job->datalen++] = 0x50;
|
|
job->databuf[job->datalen++] = 0x56;
|
|
job->databuf[job->datalen++] = 0x00;
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int type);
|
|
static const char *mitsu9550_media_types(uint8_t type, uint8_t is_s);
|
|
|
|
static void *mitsu9550_init(void)
|
|
{
|
|
struct mitsu9550_ctx *ctx = malloc(sizeof(struct mitsu9550_ctx));
|
|
if (!ctx) {
|
|
ERROR("Memory Allocation Failure!\n");
|
|
return NULL;
|
|
}
|
|
memset(ctx, 0, sizeof(struct mitsu9550_ctx));
|
|
|
|
return ctx;
|
|
}
|
|
|
|
static int mitsu9550_attach(void *vctx, struct dyesub_connection *conn, uint8_t jobid)
|
|
{
|
|
struct mitsu9550_ctx *ctx = vctx;
|
|
struct mitsu9550_media media;
|
|
|
|
UNUSED(jobid);
|
|
|
|
ctx->conn = conn;
|
|
|
|
if (ctx->conn->type == P_MITSU_9500S ||
|
|
ctx->conn->type == P_MITSU_9550S ||
|
|
ctx->conn->type == P_MITSU_9600S ||
|
|
ctx->conn->type == P_MITSU_9800S ||
|
|
ctx->conn->type == P_MITSU_9820S)
|
|
ctx->is_s = 1;
|
|
|
|
if (ctx->conn->type == P_MITSU_9800 ||
|
|
ctx->conn->type == P_MITSU_9800S ||
|
|
ctx->conn->type == P_MITSU_9820S ||
|
|
ctx->conn->type == P_MITSU_9810) {
|
|
ctx->is_98xx = 1;
|
|
ctx->need_lib = 1;
|
|
ctx->lut_fname = MITSU_M98xx_LUT_FILE;
|
|
}
|
|
|
|
if (ctx->conn->type == P_MITSU_CP30D) {
|
|
ctx->need_lib = 1;
|
|
}
|
|
if (ctx->need_lib) {
|
|
#if defined(WITH_DYNAMIC)
|
|
/* Attempt to open the library */
|
|
if (mitsu_loadlib(&ctx->lib, ctx->conn->type))
|
|
#endif
|
|
WARNING("Dynamic library support not loaded, will be unable to print.");
|
|
}
|
|
|
|
if (ctx->conn->type == P_MITSU_CP30D) {
|
|
ctx->footer_len = 6;
|
|
ctx->lut_fname = MITSU_CP30D_LUT_FILE;
|
|
} else {
|
|
ctx->footer_len = 4;
|
|
}
|
|
|
|
if (test_mode < TEST_MODE_NOATTACH) {
|
|
uint8_t buf[48];
|
|
if (mitsu9550_get_status(ctx, (uint8_t*) &media, CP9XXX_STS_MEDIA))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
/* Get FW Version */
|
|
if (mitsu9550_get_status(ctx, buf, CP9XXX_STS_FWVER))
|
|
return CUPS_BACKEND_FAILED;
|
|
memcpy(ctx->fwver, &buf[6], 6);
|
|
ctx->fwver[6] = 0;
|
|
// XXX get serial number too?
|
|
} else {
|
|
int media_code = 0x2;
|
|
if (getenv("MEDIA_CODE"))
|
|
media_code = strtol(getenv("MEDIA_CODE"), NULL, 16) & 0xf;
|
|
|
|
media.max = cpu_to_be16(400);
|
|
media.remain = cpu_to_be16(330);
|
|
media.remain2 = cpu_to_be16(330);
|
|
media.type = media_code;
|
|
ctx->fwver[0] = 0;
|
|
}
|
|
|
|
ctx->marker.color = "#00FFFF#FF00FF#FFFF00";
|
|
ctx->marker.numtype = media.type;
|
|
ctx->marker.levelmax = be16_to_cpu(media.max);
|
|
|
|
if (ctx->conn->type == P_MITSU_CP30D) {
|
|
ctx->marker.name = cp30_media_types(be16_to_cpu(media.max));
|
|
ctx->marker.levelnow = be16_to_cpu(media.remain2);
|
|
} else {
|
|
ctx->marker.name = mitsu9550_media_types(media.type, ctx->is_s);
|
|
ctx->marker.levelnow = be16_to_cpu(media.remain);
|
|
}
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static void mitsu9550_cleanup_job(const void *vjob)
|
|
{
|
|
const struct mitsu9550_printjob *job = vjob;
|
|
|
|
if (job->databuf)
|
|
free(job->databuf);
|
|
|
|
free((void*)job);
|
|
}
|
|
|
|
static void mitsu9550_teardown(void *vctx) {
|
|
struct mitsu9550_ctx *ctx = vctx;
|
|
|
|
if (!ctx)
|
|
return;
|
|
|
|
if (ctx->m98xxdata)
|
|
ctx->lib.CP98xx_DestroyData(ctx->m98xxdata);
|
|
|
|
mitsu_destroylib(&ctx->lib);
|
|
|
|
free(ctx);
|
|
}
|
|
|
|
static int mitsu9550_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
|
|
struct mitsu9550_ctx *ctx = vctx;
|
|
uint8_t buf[sizeof(struct mitsu9550_hdr1)];
|
|
int planes = 3, i;
|
|
uint32_t planelen = 0, remain = 0;
|
|
|
|
struct mitsu9550_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->is_raw = 0;
|
|
job->common.jobsize = sizeof(*job);
|
|
job->common.copies = copies;
|
|
|
|
top:
|
|
/* Read in first two bytes */
|
|
i = read(data_fd, buf, 2);
|
|
if (i == 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
if (i < 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
if (buf[0] != 0x1b) {
|
|
ERROR("Unrecognized cmd sequence (%02x %02x)!\n",
|
|
buf[0], buf[1]);
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
switch (buf[1]) {
|
|
case 0x57:
|
|
remain = sizeof(buf) - 2;
|
|
break;
|
|
case 0x5a:
|
|
remain = sizeof(struct mitsu9550_plane) - 2;
|
|
break;
|
|
default:
|
|
ERROR("Unrecognized cmd sequence (%02x %02x)!\n",
|
|
buf[0], buf[1]);
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Read in remainder of header */
|
|
while (remain) {
|
|
i = read(data_fd, buf + 2, remain);
|
|
if (i == 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
if (i < 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
remain -= i;
|
|
}
|
|
|
|
if (buf[1] == 0x5a) {
|
|
/* We're in the data portion now, bail on header processing */
|
|
if (ctx->is_98xx && buf[3] == 0x10) {
|
|
job->is_raw = 1;
|
|
planelen *= 2;
|
|
if (job->hdr1.matte)
|
|
planes ++;
|
|
} else if (ctx->is_98xx && buf[3] == 0x80) {
|
|
planelen *= 3;
|
|
planes = 1;
|
|
}
|
|
goto hdr_done;
|
|
}
|
|
|
|
switch(buf[2]) {
|
|
case 0x20: /* header 1 */
|
|
memcpy(&job->hdr1, buf, sizeof(job->hdr1));
|
|
job->hdr1_present = 1;
|
|
|
|
/* Work out printjob size */
|
|
job->rows = be16_to_cpu(job->hdr1.rows);
|
|
job->cols = be16_to_cpu(job->hdr1.cols);
|
|
planelen = job->rows * job->cols;
|
|
|
|
break;
|
|
case 0x21: /* header 2 */
|
|
memcpy(&job->hdr2, buf, sizeof(job->hdr2));
|
|
job->hdr2_present = 1;
|
|
break;
|
|
case 0x22: /* header 3 */
|
|
memcpy(&job->hdr3, buf, sizeof(job->hdr3));
|
|
job->hdr3_present = 1;
|
|
break;
|
|
case 0x26: /* header 4 */
|
|
memcpy(&job->hdr4, buf, sizeof(job->hdr4));
|
|
job->hdr4_present = 1;
|
|
break;
|
|
default:
|
|
ERROR("Unrecognized header format (%02x)!\n", buf[2]);
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Read in the next chunk */
|
|
goto top;
|
|
|
|
hdr_done:
|
|
|
|
/* 9550S/9800S doesn't typically sent over hdr4! */
|
|
if (ctx->conn->type == P_MITSU_9500S ||
|
|
ctx->conn->type == P_MITSU_9550S ||
|
|
ctx->conn->type == P_MITSU_9600S ||
|
|
ctx->conn->type == P_MITSU_9800S ||
|
|
ctx->conn->type == P_MITSU_9820S) {
|
|
/* XXX Has to do with error policy, but not sure what.
|
|
Mitsu9550-S/9800-S will set this based on a command,
|
|
but it's not part of the standard job spool */
|
|
job->hdr4_present = 0;
|
|
}
|
|
|
|
/* Disable matte if the printer doesn't support it */
|
|
if (job->hdr1.matte) {
|
|
if (ctx->conn->type != P_MITSU_9810 &&
|
|
ctx->conn->type != P_MITSU_9820S) {
|
|
WARNING("Matte not supported on this printer, disabling\n");
|
|
job->hdr1.matte = 0;
|
|
}
|
|
}
|
|
|
|
/* Read in CP98xx data tables if necessary */
|
|
if (ctx->is_98xx && !job->is_raw && !ctx->m98xxdata) {
|
|
char full[2048];
|
|
|
|
if (!ctx->lib.dl_handle) {
|
|
ERROR("!!! Image Processing Library not found, aborting!\n");
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
snprintf(full, sizeof(full), "%s/%s", corrtable_path, MITSU_M98xx_DATATABLE_FILE);
|
|
|
|
DEBUG("Reading in 98xx data from disk\n");
|
|
ctx->m98xxdata = ctx->lib.CP98xx_GetData(full);
|
|
if (!ctx->m98xxdata) {
|
|
ERROR("Unable to read 98xx data table file '%s'\n", full);
|
|
}
|
|
}
|
|
|
|
/* Work out the raw job size */
|
|
remain = planes * (planelen + sizeof(struct mitsu9550_plane)) + (ctx->footer_len * (job->hdr1.matte ? 2 : 1));
|
|
|
|
/* Allocate buffer for the payload */
|
|
job->datalen = 0;
|
|
job->databuf = malloc(remain);
|
|
if (!job->databuf) {
|
|
ERROR("Memory allocation failure!\n");
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_RETRY_CURRENT;
|
|
}
|
|
|
|
/* Load up the data blocks.*/
|
|
while(planes--) {
|
|
/* Note that 'buf' needs to be already filled here! */
|
|
struct mitsu9550_plane *plane = (struct mitsu9550_plane *)buf;
|
|
|
|
/* Sanity check header... */
|
|
if (plane->cmd[0] != 0x1b ||
|
|
plane->cmd[1] != 0x5a ||
|
|
plane->cmd[2] != 0x54) {
|
|
ERROR("Unrecognized data read @%d (%02x%02x%02x%02x)!\n",
|
|
job->datalen,
|
|
plane->cmd[0], plane->cmd[1], plane->cmd[2], plane->cmd[3]);
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Work out the length of this block */
|
|
remain = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols);
|
|
if (plane->cmd[3] == 0x10)
|
|
remain *= 2;
|
|
if (plane->cmd[3] == 0x80)
|
|
remain *= 3;
|
|
|
|
if (planelen != remain) {
|
|
ERROR("Plane length mismatch! job %d vs plane %d\n", planelen, remain);
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Move plane header into job buffer */
|
|
memcpy(job->databuf + job->datalen, buf, sizeof(struct mitsu9550_plane));
|
|
job->datalen += sizeof(struct mitsu9550_plane);
|
|
|
|
/* Final plane and Matte plane get an additional footer */
|
|
if (planes == 0 || (planes == 1 && job->hdr1.matte && job->is_raw))
|
|
remain += ctx->footer_len;
|
|
|
|
/* Read in the plane data */
|
|
while(remain) {
|
|
i = read(data_fd, job->databuf + job->datalen, remain);
|
|
if (i == 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
if (i < 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
job->datalen += i;
|
|
remain -= i;
|
|
}
|
|
|
|
|
|
/* If we're not done with our planes, read the next header */
|
|
if (planes)
|
|
remain = sizeof(struct mitsu9550_plane);
|
|
|
|
while (remain) {
|
|
i = read(data_fd, buf + sizeof(struct mitsu9550_plane) - remain, remain);
|
|
if (i == 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
if (i < 0) {
|
|
mitsu9550_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
remain -= i;
|
|
}
|
|
}
|
|
|
|
/* One bit of fixup */
|
|
if (ctx->conn->type == P_MITSU_CP30D)
|
|
job->is_raw = 0;
|
|
|
|
/* Apply LUT, if job calls for it.. */
|
|
if (ctx->lut_fname && !job->is_raw && job->hdr2.unkc[9]) {
|
|
int ret;
|
|
if (ctx->conn->type == P_MITSU_CP30D) {
|
|
uint32_t plen = job->rows * job->cols + sizeof(struct mitsu9550_plane);
|
|
ret = mitsu_apply3dlut_plane(&ctx->lib, ctx->lut_fname,
|
|
job->databuf + sizeof(struct mitsu9550_plane),
|
|
job->databuf + (plen + sizeof(struct mitsu9550_plane)),
|
|
job->databuf + (plen * 2 + sizeof(struct mitsu9550_plane)),
|
|
job->cols, job->rows);
|
|
} else {
|
|
ret = mitsu_apply3dlut_packed(&ctx->lib, ctx->lut_fname,
|
|
job->databuf + sizeof(struct mitsu9550_plane),
|
|
job->cols, job->rows,
|
|
job->cols * 3, COLORCONV_BGR);
|
|
}
|
|
if (ret) {
|
|
mitsu9550_cleanup_job(job);
|
|
return ret;
|
|
}
|
|
|
|
job->hdr2.unkc[9] = 0;
|
|
}
|
|
|
|
/* Update printjob header to reflect number of requested copies */
|
|
// XXX use larger?
|
|
if (job->hdr2_present) {
|
|
if (be16_to_cpu(job->hdr2.copies) < copies)
|
|
job->hdr2.copies = cpu_to_be16(copies);
|
|
copies = 1;
|
|
}
|
|
job->common.copies = copies;
|
|
|
|
/* All further work is in main loop */
|
|
if (test_mode >= TEST_MODE_NOPRINT)
|
|
mitsu9550_main_loop(ctx, job, 1);
|
|
|
|
*vjob = job;
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int type)
|
|
{
|
|
struct mitsu9550_cmd cmd;
|
|
int num, ret;
|
|
|
|
/* Send Printer Query */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x56; // XXX need version for 0x72
|
|
cmd.cmd[2] = type;
|
|
cmd.cmd[3] = 0x00;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return ret;
|
|
ret = read_data(ctx->conn,
|
|
resp, sizeof(struct mitsu9550_status), &num);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
if (num != sizeof(struct mitsu9550_status)) {
|
|
ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(struct mitsu9550_status));
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static const char *mitsu9550_media_types(uint8_t type, uint8_t is_s)
|
|
{
|
|
if (is_s) {
|
|
switch (type & 0xf) { /* values can be 0x0? or 0x4? */
|
|
case 0x02:
|
|
return "CK9015 (4x6)";
|
|
case 0x04:
|
|
return "CK9318 (5x7)";
|
|
case 0x05:
|
|
return "CK9523 (6x9)";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
switch (type & 0xf) { /* values can be 0x0? or 0x4? */
|
|
case 0x01:
|
|
return "CK9035 (3.5x5)";
|
|
case 0x02:
|
|
return "CK9046 (4x6)";
|
|
case 0x03:
|
|
return "CK9046PST (4x6)";
|
|
case 0x04:
|
|
return "CK9057 (5x7)";
|
|
case 0x05:
|
|
return "CK9069 (6x9)";
|
|
case 0x06:
|
|
return "CK9068 (6x8)";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int validate_media(int type, int media, int cols, int rows)
|
|
{
|
|
switch(type) {
|
|
case P_MITSU_9550:
|
|
switch(media & 0xf) {
|
|
case 0x01: /* 3.5x5 */
|
|
if (cols != 1812 && rows != 1240)
|
|
return 1;
|
|
break;
|
|
case 0x02: /* 4x6 */
|
|
case 0x03: /* 4x6 postcard */
|
|
if (cols != 2152)
|
|
return 1;
|
|
if (rows != 1416 && rows != 1184 && rows != 1240)
|
|
return 1;
|
|
break;
|
|
case 0x04: /* 5x7 */
|
|
if (cols != 1812)
|
|
return 1;
|
|
if (rows != 1240 && rows != 2452)
|
|
return 1;
|
|
break;
|
|
case 0x05: /* 6x9 */
|
|
if (cols != 2152)
|
|
return 1;
|
|
if (rows != 1416 && rows != 2792 &&
|
|
rows != 2956 && rows != 3146)
|
|
return 1;
|
|
break;
|
|
case 0x06: /* V (6x8??) */
|
|
if (cols != 2152)
|
|
return 1;
|
|
if (rows != 1416 && rows != 2792)
|
|
return 1;
|
|
break;
|
|
default: /* Unknown */
|
|
WARNING("Unknown media type %02x\n", media);
|
|
break;
|
|
}
|
|
break;
|
|
case P_MITSU_9500S:
|
|
case P_MITSU_9550S:
|
|
switch(media & 0xf) {
|
|
case 0x02: /* 4x6 */
|
|
case 0x03: /* 4x6 postcard */
|
|
if (cols != 2152)
|
|
return 1;
|
|
if (rows != 1416 && rows != 1184 && rows != 1240)
|
|
return 1;
|
|
break;
|
|
case 0x04: /* 5x7 */
|
|
if (cols != 1812 && rows != 2452)
|
|
return 1;
|
|
break;
|
|
case 0x05: /* 6x9 */
|
|
if (cols != 2152)
|
|
return 1;
|
|
if (rows != 1416 && rows != 2792 &&
|
|
rows != 2956 && rows != 3146)
|
|
return 1;
|
|
break;
|
|
case 0x06: /* V (6x8??) */
|
|
if (cols != 2152)
|
|
return 1;
|
|
if (rows != 1416 && rows != 2792)
|
|
return 1;
|
|
break;
|
|
default: /* Unknown */
|
|
WARNING("Unknown media type %02x\n", media);
|
|
break;
|
|
}
|
|
break;
|
|
case P_MITSU_9600:
|
|
case P_MITSU_9600S:
|
|
switch(media & 0xf) {
|
|
case 0x01: /* 3.5x5 */
|
|
if (cols == 1572) {
|
|
if (rows == 1076)
|
|
break;
|
|
} else if (cols == 3144) {
|
|
if (rows == 2152)
|
|
break;
|
|
}
|
|
return 1;
|
|
case 0x02: /* 4x6 */
|
|
case 0x03: /* 4x6 postcard */
|
|
if (cols == 1868) {
|
|
if (rows == 1228)
|
|
break;
|
|
} else if (cols == 3736) {
|
|
if (rows == 2458)
|
|
break;
|
|
}
|
|
return 1;
|
|
case 0x04: /* 5x7 */
|
|
if (cols == 1572) {
|
|
if (rows == 1076 || rows == 2128)
|
|
break;
|
|
} else if (cols == 3144) {
|
|
if (rows == 2152 || rows == 4256)
|
|
break;
|
|
}
|
|
return 1;
|
|
case 0x05: /* 6x9 */
|
|
if (cols == 1868) {
|
|
if (rows == 1228 || rows == 2442 || rows == 2564 || rows == 2730)
|
|
break;
|
|
} else if (cols == 3736) {
|
|
if (rows == 2458 || rows == 4846 || rows == 5130 || rows == 5462)
|
|
break;
|
|
}
|
|
return 1;
|
|
case 0x06: /* V (6x8??) */
|
|
if (cols == 1868) {
|
|
if (rows == 1228 || rows == 2442)
|
|
break;
|
|
} else if (cols == 3736) {
|
|
if (rows == 2458 || rows == 4846)
|
|
break;
|
|
}
|
|
return 1;
|
|
default: /* Unknown */
|
|
WARNING("Unknown media type %02x\n", media);
|
|
break;
|
|
}
|
|
break;
|
|
case P_MITSU_9800:
|
|
case P_MITSU_9810:
|
|
switch(media & 0xf) {
|
|
case 0x01: /* 3.5x5 */
|
|
if (cols != 1572 && rows != 1076)
|
|
return 1;
|
|
break;
|
|
case 0x02: /* 4x6 */
|
|
case 0x03: /* 4x6 postcard */
|
|
if (cols != 1868 && rows != 1228)
|
|
return 1;
|
|
break;
|
|
case 0x04: /* 5x7 */
|
|
if (cols != 1572 && rows != 2128)
|
|
return 1;
|
|
break;
|
|
case 0x05: /* 6x9 */
|
|
if (cols != 1868)
|
|
return 1;
|
|
if (rows != 1228 && rows != 2442 &&
|
|
rows != 2564 && rows != 2730)
|
|
return 1;
|
|
break;
|
|
case 0x06: /* V (6x8??) */
|
|
if (cols != 1868)
|
|
return 1;
|
|
if (rows != 1228 && rows != 2442)
|
|
return 1;
|
|
break;
|
|
default: /* Unknown */
|
|
WARNING("Unknown media type %02x\n", media);
|
|
break;
|
|
}
|
|
break;
|
|
case P_MITSU_9800S:
|
|
case P_MITSU_9820S:
|
|
switch(media & 0xf) {
|
|
case 0x02: /* 4x6 */
|
|
case 0x03: /* 4x6 postcard */
|
|
if (cols != 1868 && rows != 1228)
|
|
return 1;
|
|
break;
|
|
case 0x04: /* 5x7 */
|
|
if (cols != 1572 && rows != 2128)
|
|
return 1;
|
|
break;
|
|
case 0x05: /* 6x9 */
|
|
if (cols != 1868)
|
|
return 1;
|
|
if (rows != 1228 && rows != 2442 &&
|
|
rows != 2564 && rows != 2730)
|
|
return 1;
|
|
break;
|
|
case 0x06: /* V (6x8??) */
|
|
if (cols != 1868)
|
|
return 1;
|
|
if (rows != 1228 && rows != 2442)
|
|
return 1;
|
|
break;
|
|
default: /* Unknown */
|
|
WARNING("Unknown media type %02x\n", media);
|
|
break;
|
|
}
|
|
break;
|
|
case P_MITSU_CP30D:
|
|
if (cols != 1600)
|
|
return 1;
|
|
if (rows != 1200 && rows != 2100)
|
|
return 1;
|
|
// XXX validate media type vs job size. Don't know readback codes.
|
|
// S == 1600x1200
|
|
// L == 1600x2100 ( type 00 ?)
|
|
break;
|
|
default:
|
|
WARNING("Unknown printer type %d\n", type);
|
|
break;
|
|
}
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int mitsu9550_main_loop(void *vctx, const void *vjob, int wait_for_return) {
|
|
struct mitsu9550_ctx *ctx = vctx;
|
|
struct mitsu9550_cmd cmd;
|
|
uint8_t rdbuf[READBACK_LEN];
|
|
uint8_t *ptr;
|
|
|
|
int ret;
|
|
#if 0
|
|
int copies = 1;
|
|
#endif
|
|
|
|
struct mitsu9550_printjob *job = (struct mitsu9550_printjob*) vjob;
|
|
|
|
if (!ctx)
|
|
return CUPS_BACKEND_FAILED;
|
|
if (!job)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
/* Okay, let's do this thing */
|
|
ptr = job->databuf;
|
|
|
|
int sharpness = job->hdr2.unkc[7];
|
|
job->hdr2.unkc[7] = 0; /* Clear "sharpness" parameter */
|
|
|
|
/* Do the 98xx processing here */
|
|
if (!ctx->is_98xx || job->is_raw)
|
|
goto non_98xx;
|
|
|
|
/* Special CP98xx handling code */
|
|
uint8_t *newbuf;
|
|
uint32_t newlen = 0;
|
|
int i, remain, planelen;
|
|
|
|
planelen = job->rows * job->cols * 2;
|
|
remain = (job->hdr1.matte ? 4 : 3) * (planelen + sizeof(struct mitsu9550_plane)) + sizeof(struct mitsu9550_cmd) * (job->hdr1.matte? 2 : 1) + LAMINATE_STRIDE * 2;
|
|
newbuf = malloc(remain);
|
|
if (!newbuf) {
|
|
ERROR("Memory allocation Failure!\n");
|
|
return CUPS_BACKEND_RETRY_CURRENT;
|
|
}
|
|
|
|
DEBUG("Running print data through processing library\n");
|
|
|
|
/* Create band images for input and output */
|
|
struct BandImage input;
|
|
struct BandImage output;
|
|
|
|
uint8_t *convbuf = malloc(planelen * 3);
|
|
if (!convbuf) {
|
|
free(newbuf);
|
|
ERROR("Memory allocation Failure!\n");
|
|
return CUPS_BACKEND_RETRY_CURRENT;
|
|
}
|
|
|
|
input.origin_rows = input.origin_cols = 0;
|
|
input.rows = job->rows;
|
|
input.cols = job->cols;
|
|
input.imgbuf = job->databuf + sizeof(struct mitsu9550_plane);
|
|
input.bytes_per_row = job->cols * 3;
|
|
|
|
output.origin_rows = output.origin_cols = 0;
|
|
output.rows = job->rows;
|
|
output.cols = job->cols;
|
|
output.imgbuf = convbuf;
|
|
output.bytes_per_row = job->cols * 3 * sizeof(uint16_t);
|
|
|
|
if (!ctx->lib.CP98xx_DoConvert(ctx->m98xxdata, &input, &output, job->hdr2.mode, sharpness, job->hdr2.unkc[8])) {
|
|
free(convbuf);
|
|
free(newbuf);
|
|
ERROR("CP98xx_DoConvert() failed!\n");
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
/* Clear special extension flags used by our backend */
|
|
if (job->hdr2.mode == 0x11)
|
|
job->hdr2.mode = 0x10;
|
|
job->hdr2.unkc[8] = 0; /* Clear "already reversed" flag */
|
|
|
|
/* Library is done, but its output is packed YMC16.
|
|
We need to convert this to planar YMC16, with a header for
|
|
each plane. */
|
|
uint8_t *yPtr, *mPtr, *cPtr;
|
|
int j, offset;
|
|
|
|
yPtr = newbuf + newlen;
|
|
memcpy(yPtr, job->databuf, sizeof(struct mitsu9550_plane));
|
|
yPtr[3] = 0x10; /* ie 16bpp data */
|
|
yPtr += sizeof(struct mitsu9550_plane);
|
|
newlen += sizeof(struct mitsu9550_plane) + planelen;
|
|
|
|
mPtr = newbuf + newlen;
|
|
memcpy(mPtr, job->databuf, sizeof(struct mitsu9550_plane));
|
|
mPtr[3] = 0x10; /* ie 16bpp data */
|
|
mPtr += sizeof(struct mitsu9550_plane);
|
|
newlen += sizeof(struct mitsu9550_plane) + planelen;
|
|
|
|
cPtr = newbuf + newlen;
|
|
memcpy(cPtr, job->databuf, sizeof(struct mitsu9550_plane));
|
|
cPtr[3] = 0x10; /* ie 16bpp data */
|
|
cPtr += sizeof(struct mitsu9550_plane);
|
|
newlen += sizeof(struct mitsu9550_plane) + planelen;
|
|
|
|
for (offset = 0, i = 0; i < output.rows ; i++) {
|
|
for (j = 0 ; j < output.cols ; j ++, offset += 3) {
|
|
((uint16_t*)yPtr)[i*output.cols + j] = ((uint16_t*)convbuf)[0 + offset];
|
|
((uint16_t*)mPtr)[i*output.cols + j] = ((uint16_t*)convbuf)[1 + offset];
|
|
((uint16_t*)cPtr)[i*output.cols + j] = ((uint16_t*)convbuf)[2 + offset];
|
|
}
|
|
}
|
|
|
|
/* All done with conversion buffer, nuke it */
|
|
free(convbuf);
|
|
|
|
/* And finally, append the job footer. */
|
|
memcpy(newbuf + newlen, job->databuf + sizeof(struct mitsu9550_plane) + planelen/2 * 3, ctx->footer_len);
|
|
newlen += sizeof(struct mitsu9550_cmd);
|
|
|
|
/* Clean up, and move pointer to new buffer; */
|
|
free(job->databuf);
|
|
job->databuf = newbuf;
|
|
job->datalen = newlen;
|
|
ptr = job->databuf;
|
|
|
|
/* Now handle the matte plane generation */
|
|
if (job->hdr1.matte) {
|
|
if ((i = mitsu98xx_fillmatte(job))) {
|
|
return i;
|
|
}
|
|
}
|
|
job->is_raw = 1;
|
|
|
|
non_98xx:
|
|
/* Bypass */
|
|
if (test_mode >= TEST_MODE_NOPRINT)
|
|
return CUPS_BACKEND_OK;
|
|
|
|
top:
|
|
if (ctx->is_s) {
|
|
int num;
|
|
|
|
/* Send "unknown 1" command */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x53;
|
|
cmd.cmd[2] = 0xc5;
|
|
cmd.cmd[3] = 0x9d;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
/* Send "unknown query 1" command */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x4b;
|
|
cmd.cmd[2] = 0x7f;
|
|
cmd.cmd[3] = 0x00;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
ret = read_data(ctx->conn,
|
|
rdbuf, READBACK_LEN, &num);
|
|
if (ret < 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
// XXX no idea how to interpret this.
|
|
}
|
|
|
|
if (ctx->conn->type == P_MITSU_9800S ||
|
|
ctx->conn->type == P_MITSU_9820S) {
|
|
int num;
|
|
|
|
/* Send "unknown query 2" command */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x4b;
|
|
cmd.cmd[2] = 0x01;
|
|
cmd.cmd[3] = 0x00;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
ret = read_data(ctx->conn,
|
|
rdbuf, READBACK_LEN, &num);
|
|
if (ret < 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
// XXX no idea how to interpret this.
|
|
}
|
|
|
|
/* Sanity-check the printer state. */
|
|
{
|
|
QUERY_STATUS_I; \
|
|
if (validate_media(ctx->conn->type, media->type, job->cols, job->rows)) { \
|
|
ERROR("Incorrect media (%u) type for printjob (%ux%u)!\n", media->type, job->cols, job->rows); \
|
|
return CUPS_BACKEND_HOLD; \
|
|
} \
|
|
QUERY_STATUS_II; \
|
|
QUERY_STATUS_IV; \
|
|
}
|
|
|
|
/*** Now it's time for the actual print job! ***/
|
|
|
|
/* Send printjob headers from spool data */
|
|
if (job->hdr1_present)
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &job->hdr1, sizeof(job->hdr1))))
|
|
return CUPS_BACKEND_FAILED;
|
|
if (job->hdr2_present)
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &job->hdr2, sizeof(job->hdr2))))
|
|
return CUPS_BACKEND_FAILED;
|
|
if (job->hdr3_present)
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &job->hdr3, sizeof(job->hdr3))))
|
|
return CUPS_BACKEND_FAILED;
|
|
if (job->hdr4_present)
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &job->hdr4, sizeof(struct mitsu9550_hdr4))))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
if (ctx->is_s) {
|
|
/* I think this a "clear memory' command...? */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x5a;
|
|
cmd.cmd[2] = 0x43;
|
|
cmd.cmd[3] = 0x00;
|
|
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
/* Send over plane data */
|
|
while(ptr < (job->databuf + job->datalen)) {
|
|
struct mitsu9550_plane *plane = (struct mitsu9550_plane *)ptr;
|
|
if (plane->cmd[0] != 0x1b ||
|
|
plane->cmd[1] != 0x5a ||
|
|
plane->cmd[2] != 0x54)
|
|
break;
|
|
|
|
planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols);
|
|
if (plane->cmd[3] == 0x10)
|
|
planelen *= 2;
|
|
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) ptr, sizeof(struct mitsu9550_plane))))
|
|
return CUPS_BACKEND_FAILED;
|
|
ptr += sizeof(struct mitsu9550_plane);
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) ptr, planelen)))
|
|
return CUPS_BACKEND_FAILED;
|
|
ptr += planelen;
|
|
}
|
|
|
|
/* Query statuses after sending data */
|
|
{
|
|
QUERY_STATUS_I;
|
|
QUERY_STATUS_II;
|
|
QUERY_STATUS_IV;
|
|
}
|
|
|
|
/* Send "end data" command */
|
|
if (getenv("CP9XXXX_START")) {
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x50;
|
|
cmd.cmd[2] = strtol(getenv("CP9XXXX_START"), NULL, 16);
|
|
cmd.cmd[3] = 0x00;
|
|
|
|
DEBUG("CP9XXXX Override start = 0x%02x\n", cmd.cmd[2]);
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
} else if (ctx->conn->type == P_MITSU_9500S) {
|
|
/* Override spool, which may be wrong */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x50;
|
|
cmd.cmd[2] = 0x45;
|
|
cmd.cmd[3] = 0x00;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
} else if (ctx->conn->type == P_MITSU_9550S) {
|
|
/* Override spool, which may be wrong */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x50;
|
|
cmd.cmd[2] = 0x47;
|
|
cmd.cmd[3] = 0x00;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
} else if (ctx->conn->type == P_MITSU_9600S) {
|
|
/* Override spool, which may be wrong */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x50;
|
|
cmd.cmd[2] = 0x49;
|
|
cmd.cmd[3] = 0x00;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
} else if (ctx->conn->type == P_MITSU_9800S ||
|
|
ctx->conn->type == P_MITSU_9820S) {
|
|
/* Override spool, which may be wrong */
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x50;
|
|
cmd.cmd[2] = 0x4e;
|
|
cmd.cmd[3] = 0x00;
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
} else {
|
|
/* Send from spool file */
|
|
if ((ret = send_data(ctx->conn,
|
|
ptr, ctx->footer_len)))
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
ptr += ctx->footer_len;
|
|
|
|
/* Don't forget the 9810's matte plane */
|
|
if (job->hdr1.matte) {
|
|
struct mitsu9550_plane *plane = (struct mitsu9550_plane *)ptr;
|
|
planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols);
|
|
|
|
if (plane->cmd[3] == 0x10)
|
|
planelen *= 2;
|
|
|
|
// XXX include a status loop here too?
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) ptr, sizeof(struct mitsu9550_plane))))
|
|
return CUPS_BACKEND_FAILED;
|
|
ptr += sizeof(struct mitsu9550_plane);
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*) ptr, planelen)))
|
|
return CUPS_BACKEND_FAILED;
|
|
ptr += planelen;
|
|
|
|
/* Send "lamination end data" command from spool file */
|
|
if ((ret = send_data(ctx->conn,
|
|
ptr, sizeof(cmd))))
|
|
return CUPS_BACKEND_FAILED;
|
|
// ptr += sizeof(cmd); /* Unnecessary */
|
|
}
|
|
|
|
/* Status loop, run until printer reports completion */
|
|
while(1) {
|
|
sleep(1);
|
|
|
|
QUERY_STATUS_I;
|
|
QUERY_STATUS_II;
|
|
|
|
if (ctx->conn->type == P_MITSU_CP30D) {
|
|
ret = mitsu9550_get_status(ctx, rdbuf, CP9XXX_STS_x32);
|
|
if (ret < 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
if (sts30->err == CP30_ERR_TRAYFULL) {
|
|
ERROR("Output Tray Full!\n");
|
|
return CUPS_BACKEND_STOP;
|
|
}
|
|
// XXX figure out remaining copy count?
|
|
// print copy remaining
|
|
|
|
if (sts30->sts == CP30_STS_IDLE) /* If printer transitions to idle */
|
|
break;
|
|
|
|
// XXX if (!wait_for_return && copies_remaining == 0) break...
|
|
|
|
if (!wait_for_return && sts30->sts != CP30_STS_IDLE) {
|
|
INFO("Fast return mode enabled.\n");
|
|
break;
|
|
}
|
|
|
|
QUERY_STATUS_IIIB;
|
|
} else {
|
|
ret = mitsu9550_get_status(ctx, rdbuf, CP9XXX_STS_x30);
|
|
if (ret < 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
INFO("%03d copies remaining\n", be16_to_cpu(sts->copies));
|
|
|
|
if (!sts->sts1) /* If printer transitions to idle */
|
|
break;
|
|
|
|
if (!wait_for_return && !be16_to_cpu(sts->copies)) { /* No remaining prints */
|
|
INFO("Fast return mode enabled.\n");
|
|
break;
|
|
}
|
|
|
|
if (!wait_for_return && !sts->sts5) {
|
|
INFO("Fast return mode enabled.\n");
|
|
break;
|
|
}
|
|
QUERY_STATUS_III;
|
|
}
|
|
}
|
|
|
|
INFO("Print complete\n");
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static void mitsu9550_dump_media(struct mitsu9550_ctx *ctx, struct mitsu9550_media *resp)
|
|
{
|
|
INFO("Media type : %02x (%s)\n",
|
|
resp->type, (ctx->conn->type == P_MITSU_CP30D ? cp30_media_types(be16_to_cpu(resp->max)):
|
|
mitsu9550_media_types(resp->type, ctx->is_s)));
|
|
INFO("Media remaining : %03d/%03d\n",
|
|
(ctx->conn->type == P_MITSU_CP30D) ? be16_to_cpu(resp->remain2) : be16_to_cpu(resp->remain), be16_to_cpu(resp->max));
|
|
}
|
|
|
|
static void mitsu9550_dump_status(struct mitsu9550_status *resp)
|
|
{
|
|
INFO("Printer status : %02x (%s)\n",
|
|
resp->sts1, resp->sts1 ? "Printing": "Idle");
|
|
INFO("Pages remaining : %03d\n",
|
|
be16_to_cpu(resp->copies));
|
|
INFO("Other status : %02x %02x %02x %02x %02x %02x\n",
|
|
resp->sts2, resp->sts3, resp->sts4,
|
|
resp->sts5, resp->sts6, resp->sts7);
|
|
}
|
|
|
|
static void mitsucp30_dump_status(struct mitsucp30_status *resp)
|
|
{
|
|
INFO("Printer status : %02x %02x\n",
|
|
resp->sts, resp->sts2);
|
|
INFO("Printer error : %s (%04x)\n",
|
|
cp30_errors(be16_to_cpu(resp->err)),
|
|
be16_to_cpu(resp->err));
|
|
}
|
|
|
|
static void mitsu9550_dump_status2(struct mitsu9550_status2 *resp)
|
|
{
|
|
INFO("Prints remaining on media : %03d\n",
|
|
be16_to_cpu(resp->remain));
|
|
}
|
|
|
|
static int mitsu9550_query_media(struct mitsu9550_ctx *ctx)
|
|
{
|
|
struct mitsu9550_media resp;
|
|
int ret;
|
|
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_MEDIA);
|
|
|
|
if (!ret)
|
|
mitsu9550_dump_media(ctx, &resp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mitsu9550_query_status2(struct mitsu9550_ctx *ctx)
|
|
{
|
|
struct mitsu9550_status2 resp;
|
|
int ret;
|
|
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_x21);
|
|
|
|
if (!ret && ctx->conn->type != P_MITSU_CP30D)
|
|
mitsu9550_dump_status2(&resp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mitsu9550_query_status(struct mitsu9550_ctx *ctx)
|
|
{
|
|
int ret;
|
|
|
|
if (ctx->conn->type == P_MITSU_CP30D) {
|
|
struct mitsucp30_status resp;
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_x32);
|
|
if (!ret) {
|
|
mitsucp30_dump_status(&resp);
|
|
ret = mitsu9550_query_status2(ctx);
|
|
}
|
|
} else {
|
|
struct mitsu9550_status resp;
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_x30);
|
|
if (!ret) {
|
|
mitsu9550_dump_status(&resp);
|
|
ret = mitsu9550_query_status2(ctx);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mitsu9550_query_statusX(struct mitsu9550_ctx *ctx)
|
|
{
|
|
struct mitsu9550_status2 resp;
|
|
int ret;
|
|
|
|
#if 0
|
|
int i;
|
|
for (i = 0 ; i < 256 ; i++) {
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, i);
|
|
if (!ret) {
|
|
DEBUG("Query %02x OK\n", i);
|
|
}
|
|
}
|
|
#else
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_x20);
|
|
if (!ret)
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_FWVER);
|
|
if (!ret)
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_x22);
|
|
if (!ret)
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_x26);
|
|
if (!ret)
|
|
ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, CP9XXX_STS_x32);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int mitsu9550_query_serno(struct dyesub_connection *conn, char *buf, int buf_len)
|
|
{
|
|
struct mitsu9550_cmd cmd;
|
|
uint8_t rdbuf[READBACK_LEN];
|
|
uint8_t *ptr;
|
|
int ret, num, i;
|
|
|
|
cmd.cmd[0] = 0x1b;
|
|
cmd.cmd[1] = 0x72;
|
|
cmd.cmd[2] = 0x6e;
|
|
cmd.cmd[3] = 0x00;
|
|
|
|
if ((ret = send_data(conn,
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
|
return (ret < 0) ? ret : CUPS_BACKEND_FAILED;
|
|
|
|
ret = read_data(conn,
|
|
rdbuf, READBACK_LEN, &num);
|
|
|
|
if (ret < 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
if ((unsigned int)num < sizeof(cmd) + 1) /* Short read */
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
if (rdbuf[0] != 0xe4 ||
|
|
rdbuf[1] != 0x72 ||
|
|
rdbuf[2] != 0x6e ||
|
|
rdbuf[3] != 0x00) /* Bad response */
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
/* If response is truncated, handle it */
|
|
num -= (sizeof(cmd) + 1);
|
|
if ((unsigned int) num != rdbuf[4])
|
|
WARNING("Short serno read! (%d vs %u)\r\n",
|
|
num, rdbuf[4]);
|
|
|
|
/* model and serial number are encoded as 16-bit unicode,
|
|
little endian, separated by spaces. */
|
|
i = num;
|
|
ptr = rdbuf + 5;
|
|
while (i > 0 && buf_len > 1) {
|
|
if (*ptr != 0x20)
|
|
*buf++ = *ptr;
|
|
buf_len--;
|
|
ptr += 2;
|
|
i -= 2;
|
|
}
|
|
*buf = 0; /* Null-terminate the returned string */
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mitsu9550_cancel_job(struct mitsu9550_ctx *ctx)
|
|
{
|
|
int ret;
|
|
|
|
uint8_t buf[2] = { 0x1b, 0x44 };
|
|
ret = send_data(ctx->conn, buf, sizeof(buf));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mitsu9550_cmdline(void)
|
|
{
|
|
DEBUG("\t\t[ -m ] # Query media\n");
|
|
DEBUG("\t\t[ -s ] # Query status\n");
|
|
DEBUG("\t\t[ -X ] # Cancel current job\n");
|
|
// DEBUG("\t\t[ -Z ] # Dump all parameters\n");
|
|
}
|
|
|
|
static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv)
|
|
{
|
|
struct mitsu9550_ctx *ctx = vctx;
|
|
int i, j = 0;
|
|
|
|
if (!ctx)
|
|
return -1;
|
|
|
|
while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "msXZ")) >= 0) {
|
|
switch(i) {
|
|
GETOPT_PROCESS_GLOBAL
|
|
case 'm':
|
|
j = mitsu9550_query_media(ctx);
|
|
break;
|
|
case 's':
|
|
j = mitsu9550_query_status(ctx);
|
|
INFO("Firmware Version: %s\n", ctx->fwver);
|
|
break;
|
|
case 'X':
|
|
j = mitsu9550_cancel_job(ctx);
|
|
break;
|
|
case 'Z':
|
|
j = mitsu9550_query_statusX(ctx);
|
|
break;
|
|
default:
|
|
break; /* Ignore completely */
|
|
}
|
|
|
|
if (j) return j;
|
|
}
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int mitsu9550_query_markers(void *vctx, struct marker **markers, int *count)
|
|
{
|
|
struct mitsu9550_ctx *ctx = vctx;
|
|
struct mitsu9550_media media;
|
|
|
|
/* Query printer status */
|
|
if (mitsu9550_get_status(ctx, (uint8_t*) &media, CP9XXX_STS_MEDIA))
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
if (ctx->conn->type == P_MITSU_CP30D) {
|
|
ctx->marker.levelnow = be16_to_cpu(media.remain2);
|
|
} else {
|
|
ctx->marker.levelnow = be16_to_cpu(media.remain);
|
|
}
|
|
|
|
*markers = &ctx->marker;
|
|
*count = 1;
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static const char *mitsu9550_prefixes[] = {
|
|
"mitsu9xxx", // Family driver, do not nuke.
|
|
// Backwards compatibility
|
|
"mitsu9000", "mitsu9500", "mitsu9550", "mitsu9600", "mitsu9800", "mitsu9810",
|
|
NULL
|
|
};
|
|
|
|
static const struct device_id mitsu9550_devices[] = {
|
|
// { 0x06d3, 0x0380, P_MITSU_9550, NULL, "mitsubishi-8000d"},
|
|
// { 0x06d3, 0x0381, P_MITSU_9550, NULL, "mitsubishi-770d"},
|
|
// { 0x06d3, 0x0385, P_MITSU_9550, NULL, "mitsubishi-900d"},
|
|
// { 0x06d3, 0x0387, P_MITSU_9550, NULL, "mitsubishi-980d"},
|
|
// { 0x06d3, 0x038b, P_MITSU_CP3020, NULL, "mitsubishi-cp3020d"},
|
|
// { 0x06d3, 0x038c, P_MITSU_9550, NULL, "mitsubishi-900did"},
|
|
{ 0x06d3, 0x0395, P_MITSU_9550, NULL, "mitsubishi-9000dw"}, // XXX -am instead?
|
|
{ 0x06d3, 0x0393, P_MITSU_9550, NULL, "mitsubishi-9500dw"},
|
|
{ 0x06d3, 0x0394, P_MITSU_9550, NULL, "mitsubishi-9000dw"},
|
|
{ 0x06d3, 0x039e, P_MITSU_9500S, NULL, "mitsubishi-9500dw-s"},
|
|
{ 0x06d3, 0x03a1, P_MITSU_9550, NULL, "mitsubishi-9550dw"},
|
|
{ 0x06d3, 0x03a1, P_MITSU_9550, NULL, "mitsubishi-9550d"}, /* Duplicate */
|
|
{ 0x06d3, 0x03a5, P_MITSU_9550S, NULL, "mitsubishi-9550dw-s"}, // or DZ/DZS/DZU
|
|
{ 0x06d3, 0x03a5, P_MITSU_9550S, NULL, "mitsubishi-9550dz"}, /* Duplicate */
|
|
{ 0x06d3, 0x03a6, P_MITSU_9600S, NULL, "mitsubishi-9600dw-s"},
|
|
{ 0x06d3, 0x03a9, P_MITSU_9600, NULL, "mitsubishi-9600dw"},
|
|
// { 0x06d3, 0x03aa, P_MITSU_CP3020, NULL, "mitsubishi-cp3020da"},
|
|
{ 0x06d3, 0x03ab, P_MITSU_CP30D, NULL, "mitsubishi-cp30dw"},
|
|
{ 0x06d3, 0x03ad, P_MITSU_9800, NULL, "mitsubishi-9800dw"},
|
|
{ 0x06d3, 0x03ad, P_MITSU_9800, NULL, "mitsubishi-9800d"}, /* Duplicate */
|
|
{ 0x06d3, 0x03ae, P_MITSU_9800S, NULL, "mitsubishi-9800dw-s"},
|
|
{ 0x06d3, 0x03ae, P_MITSU_9800S, NULL, "mitsubishi-9800dz"}, /* Duplicate */
|
|
{ 0x06d3, 0x3b20, P_MITSU_9820S, NULL, "mitsubishi-9820dw-ag"},
|
|
{ 0x06d3, 0x3b21, P_MITSU_9810, NULL, "mitsubishi-9810dw"},
|
|
{ 0x06d3, 0x3b21, P_MITSU_9810, NULL, "mitsubishi-9810d"}, /* Duplicate */
|
|
// { 0x06d3, 0x3b2f, P_MITSU_9810, NULL, "mitsubishi-ls9820a"},
|
|
{ 0, 0, 0, NULL, NULL}
|
|
};
|
|
|
|
/* Exported */
|
|
const struct dyesub_backend mitsu9550_backend = {
|
|
.name = "Mitsubishi CP9xxx family",
|
|
.version = "0.71" " (lib " LIBMITSU_VER ")",
|
|
.uri_prefixes = mitsu9550_prefixes,
|
|
.devices = mitsu9550_devices,
|
|
.cmdline_usage = mitsu9550_cmdline,
|
|
.cmdline_arg = mitsu9550_cmdline_arg,
|
|
.init = mitsu9550_init,
|
|
.attach = mitsu9550_attach,
|
|
.teardown = mitsu9550_teardown,
|
|
.cleanup_job = mitsu9550_cleanup_job,
|
|
.read_parse = mitsu9550_read_parse,
|
|
.main_loop = mitsu9550_main_loop,
|
|
.query_serno = mitsu9550_query_serno,
|
|
.query_markers = mitsu9550_query_markers,
|
|
};
|
|
|
|
/* Mitsubish CP-30/9500/9550/9600/9800/9810/9820 spool format:
|
|
|
|
Spool file consists of 3 or 4 50-byte headers, followed by three
|
|
image planes, each with a 12-byte header, then a 4 or 6-byte footer.
|
|
|
|
All multi-byte numbers are big endian.
|
|
|
|
~~~ Header 1
|
|
|
|
1b 57 20 2e 00 QQ QQ 00 00 00 00 00 00 00 XX XX :: XX XX == columns
|
|
YY YY 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: YY YY == rows
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: QQ == 0x0a90 on 9810, 0x0a10 on all others.
|
|
00 00
|
|
|
|
~~~ Header 2
|
|
|
|
1b 57 21 2e 00 GG 00 HH QQ QQ 00 00 00 00 00 00 :: ZZ ZZ = num copies (>= 0x01)
|
|
00 00 00 00 00 00 00 00 00 00 00 00 ZZ ZZ 00 00 :: YY = 00/80 Fine/SuperFine (9550), 10/80 Fine/Superfine (98x0), 00 (9600), 0x80/0x00 Powersave/Normal (CP30)
|
|
XX 00 00 00 00 00 YY 00 00 00 00 00 00 00 SS TT :: XX = 00 normal, 83 Cut 2x6 (9550 only!)
|
|
RR II :: QQ QQ = 0x0803 on 9550, 0x0801 on 98x0, 0x0003 on 9600, 0xa803 on 9500, 0x0802 on CP30
|
|
:: RR = 01 for "use LUT" on 98xx, 0x00 otherwise. Extension to stock.
|
|
:: TT = 01 for "already reversed". Extension to stock.
|
|
:: SS == sharpening level, 0 for off, 1-10 otherwise. Extesion to stock.
|
|
:: GG == 0x00 on CP30, 0x80 on others
|
|
:: HH == 0x20 on CP30, 0x22 on others
|
|
:: II == 0x00 on CP30, 0x01 on others.
|
|
|
|
~~~ Header 3 (9550, 9800-S, and CP30 only..)
|
|
|
|
1b 57 22 2e 00 QQ 00 00 00 00 00 XX 00 00 00 00 :: XX = 00 normal, 01 FineDeep
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: QQ = 0xf0 on 9500, 0x40 on the rest
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00
|
|
|
|
~~~ Header 4 (all but 9550-S and 9800-S, involves error policy?)
|
|
|
|
1b 57 26 2e 00 QQ TT 00 00 00 00 SS RR ZZ VV WW :: QQ = 0x70 on 9550/98x0, 0x60 on 9600 or 9800S, 0x3f on CP30 [also seen 0x20 & 0x00 on 9550S]
|
|
WW 00 WW 00 00 00 00 00 00 00 00 00 00 00 00 00 :: RR = 0x01 on 9550/98x0/CP30, 0x00 on 9600 [ "ignore errors? ]
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: SS = 0x01 on 9800S, 0x00 otherwise.
|
|
00 00
|
|
:: ZZ = Unknown; 0x01 [9550S/CP30] & 0x00 [9500].
|
|
:: TT = 0x80 on CP30, 0x00 otherwise
|
|
:: VV = 0x80 on CP30, 0x00 otherwise
|
|
:: WW = 0x10 on CP30, 0x00 otherwise
|
|
|
|
~~~~ Data follows:
|
|
|
|
Format is: planar YMC16 for 98x0 (but only 12 bits used, BIG endian)
|
|
planar RGB for all others
|
|
|
|
1b 5a 54 ?? RR RR CC CC 07 14 04 d8 :: 0714 == columns, 04d8 == rows
|
|
:: RRRR == row offset for data, CCCC == col offset for data
|
|
:: ?? == 0x00 for 8bpp, 0x10 for 16/12bpp.
|
|
:: 0x80 for PACKED BGR!
|
|
|
|
Data follows immediately, no padding.
|
|
|
|
1b 5a 54 00 00 00 00 00 07 14 04 d8 :: Another plane.
|
|
|
|
Data follows immediately, no padding.
|
|
|
|
1b 5a 54 00 00 00 00 00 07 14 04 d8 :: Another plane.
|
|
|
|
Data follows immediately, no padding.
|
|
|
|
~~~~ Footer:
|
|
|
|
1b 50 41 00 (9500AM)
|
|
1b 50 45 00 (9500-S)
|
|
1b 50 46 00 (9550)
|
|
1b 50 47 00 (9550-S)
|
|
1b 50 48 00 (9600)
|
|
1b 50 49 00 (9600-S)
|
|
1b 50 4c 00 (9800/9810)
|
|
1b 50 4d 00 (9000)
|
|
1b 50 4e 00 (9800-S, 9820-S) (#170/aa,173,174,180/b4,183)
|
|
1b 50 51 00 (CP3020DA)
|
|
1b 50 57 00 (9500)
|
|
1b 50 52 00 00 00 (CP30)
|
|
|
|
~~~~ Lamination data follows (on 9810 only, if matte selected)
|
|
|
|
1b 5a 54 10 00 00 00 00 06 24 04 34
|
|
|
|
Data follows immediately, no padding.
|
|
|
|
1b 50 56 00 (Lamination footer)
|
|
|
|
~~~~ QUESTIONS:
|
|
|
|
* Lamination control?
|
|
* Other 9550 multi-cut modes (on 6x9 media: 4x6*2, 4.4x6*2, 3x6*3, 2x6*4)
|
|
* 9600/98x0 multi-cut modes?
|
|
|
|
***********************************************************************
|
|
|
|
* Mitsubishi CP-9xxx Communications Protocol:
|
|
|
|
JOB CANCEL
|
|
|
|
-> 1b 44
|
|
|
|
[[ Unknown query 1 ]]
|
|
|
|
-> 1b 4b 7f 00
|
|
<- eb 4b 8f 00 02 00 5e [[ '02' seems to be a length ]]
|
|
|
|
[[ unknown query 2, 9800-only ]]
|
|
|
|
-> 1b 4b 01 00
|
|
<- e4 4b 01 00 02 00 78
|
|
|
|
PRINT START
|
|
|
|
-> 1b 50 47 00 [9550S]
|
|
-> 1b 50 4e 00 [9800S]
|
|
-> 1b 50 56 00 [9810 Lamination]
|
|
[[ see "footer" above for other models ]]
|
|
|
|
[[ Unknown 1 ]]
|
|
|
|
-> 1b 51 c5 9d
|
|
|
|
[[ Unknown ]]
|
|
|
|
-> 1b 53 00 00
|
|
|
|
[[ Unknown ]]
|
|
|
|
-> 1b 53 c5 9d
|
|
|
|
[[ Status Query B ]]
|
|
|
|
-> 1b 56 20 00 [ CP30 ]
|
|
<- 20 2e 00 0a 10 00 00 00 00 00 00 00 CC CC RR RR :: CC == cols
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: RR == rows (04b0 for L??)
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
[[ Status Query C ]]
|
|
|
|
-> 1b 56 21 00 [ Most models ]
|
|
<- 21 2e 00 80 00 22 XX 0b 00 00 00 00 00 00 00 00 :: XX == a8 (most) 08 (9810)
|
|
00 00 00 00 00 00 00 00 00 00 00 QQ 00 00 00 00 :: QQ == Prints in job?
|
|
00 00 00 00 00 00 00 00 00 00 NN NN 0a 00 00 01 :: NN NN = Remaining media
|
|
|
|
21 2e 00 00 00 20 08 02 00 00 00 00 00 00 00 00 [ CP30 ]
|
|
00 00 00 00 00 00 00 00 00 00 00 XX 00 00 00 00 :: XX == seen 01 and 02. Unknown.
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
[[ Status Query D (unknown, possibly lifetime print count?) ]]
|
|
|
|
-> 1b 56 22 00 [ CP30 ]
|
|
<- 22 2e 00 40 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
FIRWARE VERSIONS
|
|
|
|
-> 1b 56 23 00 [ CP30 ]
|
|
<- 23 2e 00 00 00 00 32 32 33 46 31 30 32 32 34 42 223F10 224B10 ... 222A10
|
|
31 30 00 00 00 00 00 00 32 32 32 41 31 30 00 00
|
|
00 00 00 05 d9 3f 79 20 00 00 13 97 00 00 00 00
|
|
|
|
MEDIA INFO
|
|
|
|
-> 1b 56 24 00
|
|
<- 24 2e 00 00 00 00 00 00 00 00 00 00 XX 00 TT 00 :: TT = Type (!CP30) ; XX = 0x02 on CP30, 0x00 otherwise.
|
|
00 00 00 00 00 00 00 00 00 00 00 00 MM MM N1 N1 :: MM MM = Max prints
|
|
NN NN 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: NN NN = Remaining (!CP30) ; N1 N1 = Remaining (CP30)
|
|
|
|
[[ Status Query E ]]
|
|
|
|
-> 1b 56 26 00 [ CP30 ]
|
|
<- 26 2e 00 3f 80 00 00 00 00 00 01 01 80 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
Status Query [All models except CP30]
|
|
|
|
-> 1b 56 30 00
|
|
-> 30 2e 00 00 00 00 MM 00 NN NN ZZ 00 00 00 00 00 :: MM, NN, ZZ
|
|
QQ RR SS 00 00 00 00 00 00 00 00 00 00 00 00 00 :: QQ, RR, SS
|
|
00 00 00 00 00 00 00 00 00 00 00 00 TT UU 00 00 :: TT, UU
|
|
|
|
Status Query [CP30]
|
|
|
|
-> 1b 56 32 00
|
|
<- 32 2e 00 00 00 43 00 50 00 33 00 30 00 44 00 20 :: Unicode, "CP30D 204578"
|
|
00 32 00 30 00 34 00 35 00 37 00 38 00 00 SS SS :: SS SS = status code (see CP30_STS_*)
|
|
00 EE EE 00 00 00 NN 00 00 00 00 00 00 00 00 00 :: NN = remaining prints on paper
|
|
:: EE EE = error code (see CP30_ERR_*)
|
|
|
|
Status Query X (unknown)
|
|
|
|
-> 1b 56 33 00
|
|
<- ??? 48 bytes?
|
|
|
|
Status Query Y (unknown)
|
|
|
|
-> 1b 56 36 00
|
|
<- ??? 48 bytes?
|
|
|
|
[[ Header 1 -- See above ]]
|
|
|
|
-> 1b 57 20 2e ....
|
|
|
|
[[ Header 2 -- See above ]]
|
|
|
|
-> 1b 57 21 2e ....
|
|
|
|
[[ Header 3 -- See above ]]
|
|
|
|
-> 1b 57 22 2e ....
|
|
|
|
[[ Header 4 -- See above ]]
|
|
|
|
-> 1b 57 26 2e ....
|
|
|
|
DATA Start [[ Or maybe it's "DATA Clear" ? Only seen on -S models ]]
|
|
|
|
-> 1b 5a 43 00
|
|
|
|
PLANE Data
|
|
|
|
-> 1b 5a 54 ?? 00 00 00 00 XX XX YY YY :: XX XX == Columns, YY YY == Rows
|
|
:: ?? == x00 8bpp, x10 16bpp
|
|
|
|
Followed by image plane data, XXXX * YYYY [ * 2 ] bytes
|
|
|
|
[ Three planes are needed! ]
|
|
|
|
Query Model & FW Version (XXX Confirm this!)
|
|
|
|
-> 1b 72 01 00
|
|
<- e4 82 01 00 LL 39 00 35 00 35 00 30 00 5a 00 20
|
|
00 41 00 32 00 30 00 30 00 36 00 37 00
|
|
|
|
'LL' is length. Data is returned in 16-bit unicode, LE.
|
|
Contents are model ('9550Z'), then space, then serialnum ('A20067')
|
|
|
|
[[ Unknown query.. "Printer number" related? Seen in driver dump ]]
|
|
|
|
-> 1b 72 10 00
|
|
<- e4 82 10 00 LL [ 10 unknown bytes. guess. ]
|
|
|
|
Query Model & Serial number
|
|
|
|
-> 1b 72 6e 00
|
|
<- e4 82 6e 00 LL 39 00 35 00 35 00 30 00 5a 00 20
|
|
00 41 00 32 00 30 00 30 00 36 00 37 00
|
|
|
|
'LL' is length. Data is returned in 16-bit unicode, LE.
|
|
Contents are model ('9550Z'), then space, then serialnum ('A20067')
|
|
|
|
|
|
**** After print starts, loop status/status b/media queries until printer idle
|
|
|
|
MM, QQ RR SS, TT UU
|
|
|
|
<- 00 3e 00 00 8a 44 :: Idle.
|
|
00 7e 00 00 8a 44 :: Plane data submitted, pre "end data" cmd
|
|
00 7e 40 01 8a 44 :: "end data" sent
|
|
30 7e 40 01 8a 44
|
|
38 7e 40 01 8a 44
|
|
59 7e 40 01 8a 44
|
|
59 7e 40 00 8a 44
|
|
4d 7e 40 00 8a 44
|
|
[...]
|
|
43 7e 40 00 82 44
|
|
[...]
|
|
50 7e 40 00 80 44
|
|
[...]
|
|
31 7e 40 00 7d 44
|
|
[...]
|
|
00 3e 00 00 80 44 :: Idle.
|
|
|
|
Also seen:
|
|
|
|
00 3e 00 00 96 4b :: Idle
|
|
00 be 00 00 96 4b :: Data submitted, pre "start"
|
|
00 be 80 01 96 4b :: print start sent
|
|
30 be 80 01 96 4c
|
|
[...]
|
|
30 be 80 01 89 4b
|
|
38 be 80 01 8a 4b
|
|
59 be 80 01 8b 4b
|
|
[...]
|
|
4d be 80 01 89 4b
|
|
[...]
|
|
43 be 80 01 89 4b
|
|
[...]
|
|
50 be 80 01 82 4b
|
|
[...]
|
|
31 be 80 01 80 4b
|
|
[...]
|
|
|
|
00 be 80 01 76 37 :: printed 4x6 on 6x9?
|
|
|
|
Working theory of interpreting the status flags:
|
|
|
|
MM :: 00 is idle, else mechanical printer state.
|
|
NN :: Remaining prints in job, or 0x00 0x00 when idle.
|
|
QQ :: ?? 0x3e + 0x40 or 0x80 (see below)
|
|
RR :: ?? 0x00 is idle, 0x40 or 0x80 is "printing"?
|
|
SS :: ?? 0x00 means "ready for another print" but 0x01 is "busy"
|
|
TT :: ?? seen values between 0x76 through 0x96)
|
|
UU :: ?? seen values between 0x37 and 0x4c -- temperature?
|
|
ZZ :: ?? Error code (08 = Door open on 9600)
|
|
|
|
***
|
|
|
|
Other printer commands seen:
|
|
|
|
[[ Set error policy ?? aka "header 4" ]]
|
|
|
|
-> 1b 57 26 2e 00 QQ 00 00 00 00 00 00 RR ZZ 00 00 :: QQ/RR/ZZ 00 00 00 [9550S]
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: 20 01 00 [9550S w/ ignore failures on]
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: 70 01 01 [9550]
|
|
00 00
|
|
|
|
*/
|