summaryrefslogtreecommitdiff
path: root/src/cups/mitsu9550_print.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cups/mitsu9550_print.c')
-rw-r--r--src/cups/mitsu9550_print.c1013
1 files changed, 1013 insertions, 0 deletions
diff --git a/src/cups/mitsu9550_print.c b/src/cups/mitsu9550_print.c
new file mode 100644
index 0000000..7b83e6c
--- /dev/null
+++ b/src/cups/mitsu9550_print.c
@@ -0,0 +1,1013 @@
+/*
+ * Mitsubishi CP-9550DW[-S] Photo Printer CUPS backend
+ *
+ * (c) 2014-2015 Solomon Peachy <pizza@shaftnet.org>
+ *
+ * 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 2 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-2.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"
+
+#define USB_VID_MITSU 0x06D3
+#define USB_PID_MITSU_9550D 0x03A1
+#define USB_PID_MITSU_9550DS 0x03A5 // or DZ/DZS/DZU
+
+/* Private data stucture */
+struct mitsu9550_ctx {
+ struct libusb_device_handle *dev;
+ uint8_t endp_up;
+ uint8_t endp_down;
+
+ uint8_t *databuf;
+ int datalen;
+
+ int is_s_variant;
+
+ int fast_return;
+
+ uint16_t rows;
+ uint16_t cols;
+};
+
+/* Spool file structures */
+struct mitsu9550_hdr1 {
+ uint8_t cmd[4]; /* 1b 57 20 2e */
+ uint8_t unk[10];
+ uint16_t cols; /* BE */
+ uint16_t rows; /* BE */
+ uint8_t null[32];
+} __attribute__((packed));
+
+struct mitsu9550_hdr2 {
+ uint8_t cmd[4]; /* 1b 57 21 2e */
+ uint8_t unk[24];
+ uint16_t copies; /* BE, 1-580 */
+ uint8_t null[2];
+ uint8_t cut; /* 00 == normal, 83 == 2x6*2 */
+ uint8_t unkb[5];
+ uint8_t mode; /* 00 == normal, 80 == fine */
+ uint8_t unkc[11];
+} __attribute__((packed));
+
+struct mitsu9550_hdr3 {
+ uint8_t cmd[4]; /* 1b 57 22 2e */
+ uint8_t unk[7];
+ uint8_t mode2; /* 00 == normal, 01 == finedeep */
+ uint8_t unkb[38];
+} __attribute__((packed));
+
+struct mitsu9550_hdr4 {
+ uint8_t cmd[4]; /* 1b 57 26 2e */
+ uint8_t unk[46];
+} __attribute__((packed));
+
+struct mitsu9550_plane {
+ uint8_t cmd[4]; /* 1b 5a 54 00 */
+ uint8_t null[2];
+ uint16_t rem_rows; /* BE, normally 0 */
+ uint16_t columns; /* BE */
+ uint16_t rows; /* BE */
+} __attribute__((packed));
+
+struct mitsu9550_cmd {
+ uint8_t cmd[4];
+} __attribute__((packed));
+
+/* Printer data structures */
+struct mitsu9550_media {
+ uint8_t hdr[2]; /* 24 2e */
+ uint8_t unk[12];
+ uint8_t type;
+ uint8_t unka[13];
+ uint16_t max; /* BE, prints per media */
+ uint8_t unkb[2];
+ uint16_t remain; /* BE, prints remaining */
+ 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; // NN
+ uint8_t nullc[6];
+ 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 mitsu9550_status2 {
+ uint8_t hdr[2]; /* 21 2e */
+ uint8_t unk[40];
+ uint8_t remain; /* BE, media remaining */
+ uint8_t unkb[4];
+} __attribute__((packed));
+
+#define CMDBUF_LEN 64
+#define READBACK_LEN 128
+
+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));
+
+ /* Use Fast return by default in CUPS mode */
+ if (getenv("DEVICE_URI") || getenv("FAST_RETURN"))
+ ctx->fast_return = 1;
+
+ return ctx;
+}
+
+static void mitsu9550_attach(void *vctx, struct libusb_device_handle *dev,
+ uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
+{
+ struct mitsu9550_ctx *ctx = vctx;
+ struct libusb_device *device;
+ struct libusb_device_descriptor desc;
+
+ UNUSED(jobid);
+
+ ctx->dev = dev;
+ ctx->endp_up = endp_up;
+ ctx->endp_down = endp_down;
+
+ device = libusb_get_device(dev);
+ libusb_get_device_descriptor(device, &desc);
+
+ if (desc.idProduct == USB_PID_MITSU_9550DS)
+ ctx->is_s_variant = 1;
+}
+
+
+static void mitsu9550_teardown(void *vctx) {
+ struct mitsu9550_ctx *ctx = vctx;
+
+ if (!ctx)
+ return;
+
+ if (ctx->databuf)
+ free(ctx->databuf);
+ free(ctx);
+}
+
+static int mitsu9550_read_parse(void *vctx, int data_fd) {
+ struct mitsu9550_ctx *ctx = vctx;
+ struct mitsu9550_hdr1 hdr;
+
+ int remain, i;
+
+ if (!ctx)
+ return CUPS_BACKEND_FAILED;
+
+ if (ctx->databuf) {
+ free(ctx->databuf);
+ ctx->databuf = NULL;
+ }
+
+ /* Read in initial header */
+ remain = sizeof(hdr);
+ while (remain > 0) {
+ i = read(data_fd, ((uint8_t*)&hdr) + sizeof(hdr) - remain, remain);
+ if (i == 0)
+ return CUPS_BACKEND_CANCEL;
+ if (i < 0)
+ return CUPS_BACKEND_CANCEL;
+ remain -= i;
+ }
+
+ /* Sanity check */
+ if (hdr.cmd[0] != 0x1b ||
+ hdr.cmd[1] != 0x57 ||
+ hdr.cmd[2] != 0x20 ||
+ hdr.cmd[3] != 0x2e) {
+ ERROR("Unrecognized data format!\n");
+ return CUPS_BACKEND_CANCEL;
+ }
+
+ /* Work out printjob size */
+ ctx->rows = be16_to_cpu(hdr.rows);
+ ctx->cols = be16_to_cpu(hdr.cols);
+
+ remain = ctx->rows * ctx->cols + sizeof(struct mitsu9550_plane);
+ remain *= 3;
+ remain += sizeof(struct mitsu9550_hdr2) + sizeof(struct mitsu9550_hdr3)+ sizeof(struct mitsu9550_hdr4) + sizeof(struct mitsu9550_cmd);
+
+ /* Allocate buffer */
+ ctx->databuf = malloc(remain + sizeof(struct mitsu9550_hdr1));
+ if (!ctx->databuf) {
+ ERROR("Memory allocation failure!\n");
+ return CUPS_BACKEND_FAILED;
+ }
+
+ memcpy(ctx->databuf, &hdr, sizeof(struct mitsu9550_hdr1));
+ ctx->datalen = sizeof(struct mitsu9550_hdr1);
+
+ /* Read in the spool data */
+ while(remain) {
+ i = read(data_fd, ctx->databuf + ctx->datalen, remain);
+ if (i == 0)
+ return CUPS_BACKEND_CANCEL;
+ if (i < 0)
+ return CUPS_BACKEND_CANCEL;
+ ctx->datalen += i;
+ remain -= i;
+ }
+
+ return CUPS_BACKEND_OK;
+}
+
+static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int status, int status2, int media)
+{
+ struct mitsu9550_cmd cmd;
+ int num, ret;
+
+ /* Send Printer Query */
+ cmd.cmd[0] = 0x1b;
+ cmd.cmd[1] = 0x56;
+ if (status)
+ cmd.cmd[2] = 0x30;
+ else if (status2)
+ cmd.cmd[2] = 0x21;
+ else if (media)
+ cmd.cmd[2] = 0x24;
+ cmd.cmd[3] = 0x00;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) &cmd, sizeof(cmd))))
+ return ret;
+ ret = read_data(ctx->dev, ctx->endp_up,
+ 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 4;
+ }
+
+ return 0;
+}
+
+static int validate_media(int type, int cols, int rows) {
+ switch(type) {
+ case 0x01: /* 3.5x5 */
+ if (cols != 1812 || rows != 1240)
+ return 1;
+ break;
+ case 0x02: /* 4x6 */
+ case 0x03: /* PC ??? */
+ 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 != 2972 ||
+ rows != 2956 || rows != 3146)
+ return 1;
+ break;
+ case 0x06: /* V */
+ break;
+ default: /* Unknown */
+ break;
+ }
+ return 0;
+}
+
+static int mitsu9550_main_loop(void *vctx, int copies) {
+ struct mitsu9550_ctx *ctx = vctx;
+ struct mitsu9550_hdr2 *hdr2;
+ struct mitsu9550_cmd cmd;
+ uint8_t rdbuf[READBACK_LEN];
+
+ uint8_t *ptr;
+
+ int ret;
+
+ if (!ctx)
+ return CUPS_BACKEND_FAILED;
+
+ /* This printer handles copies internally */
+ hdr2 = (struct mitsu9550_hdr2 *) (ctx->databuf + sizeof(struct mitsu9550_hdr1));
+ hdr2->copies = cpu_to_be16(copies);
+
+ ptr = ctx->databuf;
+
+top:
+ if (ctx->is_s_variant) {
+ 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->dev, ctx->endp_down,
+ (uint8_t*) &cmd, sizeof(cmd))))
+ return CUPS_BACKEND_FAILED;
+
+ /* Send "unknown 2" command */
+ cmd.cmd[0] = 0x1b;
+ cmd.cmd[1] = 0x4b;
+ cmd.cmd[2] = 0x7f;
+ cmd.cmd[3] = 0x00;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) &cmd, sizeof(cmd))))
+ return CUPS_BACKEND_FAILED;
+
+ ret = read_data(ctx->dev, ctx->endp_up,
+ rdbuf, READBACK_LEN, &num);
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+ // seen so far: eb 4b 7f 00 02 00 5e
+ }
+
+ /* Query statuses */
+ {
+ struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;
+ //struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf;
+ struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ /* Sanity-check media response */
+ if (media->remain == 0 || media->max == 0) {
+ ERROR("Printer out of media!\n");
+ return CUPS_BACKEND_HOLD;
+ }
+ if (validate_media(media->type, ctx->cols, ctx->rows)) {
+ ERROR("Incorrect media (%d) type for printjob (%dx%d)!\n", media->type, ctx->cols, ctx->rows);
+ return CUPS_BACKEND_HOLD;
+ }
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); // status
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ /* Make sure we're idle */
+ if (sts->sts5 != 0) { /* Printer ready for another job */
+ sleep(1);
+ goto top;
+ }
+ }
+
+ /* Now it's time for the actual print job! */
+
+ if (ctx->is_s_variant) {
+ cmd.cmd[0] = 0x1b;
+ cmd.cmd[1] = 0x44;
+ cmd.cmd[2] = 0;
+ cmd.cmd[3] = 0;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) &cmd, 4)))
+ return CUPS_BACKEND_FAILED;
+ }
+
+ /* Query statuses */
+ {
+ struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;
+// struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf;
+ struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ /* Sanity-check media response */
+ if (media->remain == 0 || media->max == 0) {
+ ERROR("Printer out of media!\n");
+ return CUPS_BACKEND_HOLD;
+ }
+ if (validate_media(media->type, ctx->cols, ctx->rows)) {
+ ERROR("Incorrect media (%d) type for printjob (%dx%d)!\n", media->type, ctx->cols, ctx->rows);
+ return CUPS_BACKEND_HOLD;
+ }
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); // status
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ /* Make sure we're idle */
+ if (sts->sts5 != 0) { /* Printer ready for another job */
+ sleep(1);
+ goto top;
+ }
+ }
+
+ /* Send printjob headers from spool data */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, sizeof(struct mitsu9550_hdr1))))
+ return CUPS_BACKEND_FAILED;
+ ptr += sizeof(struct mitsu9550_hdr1);
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, sizeof(struct mitsu9550_hdr2))))
+ return CUPS_BACKEND_FAILED;
+ ptr += sizeof(struct mitsu9550_hdr2);
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, sizeof(struct mitsu9550_hdr3))))
+ return CUPS_BACKEND_FAILED;
+ ptr += sizeof(struct mitsu9550_hdr3);
+ if (!ctx->is_s_variant) {
+ // XXX need to investigate what hdr4 is about
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, sizeof(struct mitsu9550_hdr4))))
+ return CUPS_BACKEND_FAILED;
+ }
+ ptr += sizeof(struct mitsu9550_hdr4);
+
+ if (ctx->is_s_variant) {
+ /* Send "start data" command */
+ cmd.cmd[0] = 0x1b;
+ cmd.cmd[1] = 0x5a;
+ cmd.cmd[2] = 0x43;
+ cmd.cmd[3] = 0x00;
+
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) &cmd, sizeof(cmd))))
+ return CUPS_BACKEND_FAILED;
+ }
+ /* Send plane data */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, sizeof(struct mitsu9550_plane))))
+ return CUPS_BACKEND_FAILED;
+ ptr += sizeof(struct mitsu9550_plane);
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, ctx->rows * ctx->cols)))
+ return CUPS_BACKEND_FAILED;
+ ptr += ctx->rows * ctx->cols;
+
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, sizeof(struct mitsu9550_plane))))
+ return CUPS_BACKEND_FAILED;
+ ptr += sizeof(struct mitsu9550_plane);
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, ctx->rows * ctx->cols)))
+ return CUPS_BACKEND_FAILED;
+ ptr += ctx->rows * ctx->cols;
+
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, sizeof(struct mitsu9550_plane))))
+ return CUPS_BACKEND_FAILED;
+ ptr += sizeof(struct mitsu9550_plane);
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) ptr, ctx->rows * ctx->cols)))
+ return CUPS_BACKEND_FAILED;
+ ptr += ctx->rows * ctx->cols;
+
+
+ /* Query statuses */
+ {
+ struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;
+// struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf;
+ struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ /* Sanity-check media response */
+ if (media->remain == 0 || media->max == 0) {
+ ERROR("Printer out of media!\n");
+ return CUPS_BACKEND_HOLD;
+ }
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); // status
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ /* Make sure we're ready to proceed */
+ if (sts->sts5 != 0) {
+ ERROR("Unexpected response (sts5 %02x)\n", sts->sts5);
+ return CUPS_BACKEND_FAILED;
+ }
+ if (!(sts->sts3 & 0xc0)) {
+ ERROR("Unexpected response (sts3 %02x)\n", sts->sts3);
+ return CUPS_BACKEND_FAILED;
+ }
+ }
+
+ if (ctx->is_s_variant) {
+ /* Send "end data" command */
+ cmd.cmd[0] = 0x1b;
+ cmd.cmd[1] = 0x50;
+ cmd.cmd[2] = 0x47;
+ cmd.cmd[3] = 0x00;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ (uint8_t*) &cmd, sizeof(cmd))))
+ return CUPS_BACKEND_FAILED;
+ } else {
+ /* Send "end data" command from spool file */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ptr, sizeof(cmd))))
+ return CUPS_BACKEND_FAILED;
+ ptr += sizeof(cmd);
+ }
+
+ /* Status loop, run until printer reports completion */
+ while(1) {
+ struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;
+// struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf;
+ struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); // media
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ /* Sanity-check media response */
+ if (media->remain == 0 || media->max == 0) {
+ ERROR("Printer out of media!\n");
+ return CUPS_BACKEND_HOLD;
+ }
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); // status2
+ if (ret < 0)
+ return CUPS_BACKEND_FAILED;
+
+ ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); // status
+ 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 (ctx->fast_return && !be16_to_cpu(sts->copies)) { /* No remaining prints */
+ INFO("Fast return mode enabled.\n");
+ break;
+ }
+
+ if (ctx->fast_return && !sts->sts5) { /* Ready for another job */
+ INFO("Fast return mode enabled.\n");
+ break;
+ }
+
+ sleep(1);
+ }
+
+ /* 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) {
+ goto top;
+ }
+
+ return CUPS_BACKEND_OK;
+}
+
+static char *mitsu9550_media_types(uint8_t type)
+{
+ switch (type) {
+ case 0x01:
+ return "3.5x5";
+ case 0x02:
+ return "4x6";
+ case 0x03:
+ return "PC";
+ case 0x04:
+ return "5x7";
+ case 0x05:
+ return "6x9";
+ case 0x06:
+ return "V";
+ default:
+ return "Unknown";
+ }
+ return NULL;
+}
+
+static void mitsu9550_dump_media(struct mitsu9550_media *resp)
+{
+ INFO("Media type : %02x (%s)\n",
+ resp->type, mitsu9550_media_types(resp->type));
+ INFO("Media remaining : %03d/%03d\n",
+ 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\n",
+ resp->sts3, resp->sts4, resp->sts5, resp->sts6, resp->sts7);
+
+}
+
+static int mitsu9550_query_media(struct mitsu9550_ctx *ctx)
+{
+ struct mitsu9550_media resp;
+ int ret;
+
+ ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, 0, 0, 1);
+
+ if (!ret)
+ mitsu9550_dump_media(&resp);
+
+ return ret;
+}
+
+static int mitsu9550_query_status(struct mitsu9550_ctx *ctx)
+{
+ struct mitsu9550_status resp;
+ int ret;
+
+ ret = mitsu9550_get_status(ctx, (uint8_t*) &resp, 1, 0, 0);
+
+ if (!ret)
+ mitsu9550_dump_status(&resp);
+
+ return ret;
+}
+
+static int mitsu9550_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, 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(dev, endp_down,
+ (uint8_t*) &cmd, sizeof(cmd))))
+ return (ret < 0) ? ret : CUPS_BACKEND_FAILED;
+
+ ret = read_data(dev, endp_up,
+ 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 %d)\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 void mitsu9550_cmdline(void)
+{
+ DEBUG("\t\t[ -m ] # Query media\n");
+ DEBUG("\t\t[ -s ] # Query status\n");
+ DEBUG("\t\t[ -f ] # Enable fast return mode\n");
+}
+
+static int mitsu9550_cmdline_arg(void *vctx, int argc, char **argv)
+{
+ struct mitsu9550_ctx *ctx = vctx;
+ int i, j = 0;
+
+ /* Reset arg parsing */
+ optind = 1;
+ opterr = 0;
+ while ((i = getopt(argc, argv, "mfs")) >= 0) {
+ switch(i) {
+ case 'm':
+ if (ctx) {
+ j = mitsu9550_query_media(ctx);
+ break;
+ }
+ return 1;
+ case 's':
+ if (ctx) {
+ j = mitsu9550_query_status(ctx);
+ break;
+ }
+ return 1;
+
+ case 'f':
+ if (ctx) {
+ ctx->fast_return = 1;
+ break;
+ }
+ return 1;
+ default:
+ break; /* Ignore completely */
+ }
+
+ if (j) return j;
+ }
+
+ return 0;
+}
+
+/* Exported */
+struct dyesub_backend mitsu9550_backend = {
+ .name = "Mitsubishi CP-9550DW-S",
+ .version = "0.12",
+ .uri_prefix = "mitsu9550",
+ .cmdline_usage = mitsu9550_cmdline,
+ .cmdline_arg = mitsu9550_cmdline_arg,
+ .init = mitsu9550_init,
+ .attach = mitsu9550_attach,
+ .teardown = mitsu9550_teardown,
+ .read_parse = mitsu9550_read_parse,
+ .main_loop = mitsu9550_main_loop,
+ .query_serno = mitsu9550_query_serno,
+ .devices = {
+ { USB_VID_MITSU, USB_PID_MITSU_9550D, P_MITSU_9550, ""},
+ { USB_VID_MITSU, USB_PID_MITSU_9550DS, P_MITSU_9550, ""},
+ { 0, 0, 0, ""}
+ }
+};
+
+/* Mitsubish CP-9550D/DW spool data format
+
+ Spool file consists of four 50-byte headers, followed by three image
+ planes (BGR, each with a 12-byte header), and a 4-byte footer.
+
+ All multi-byte numbers are big endian.
+
+ ~~~ Printer Init: 4x 50-byte blocks:
+
+ 1b 57 20 2e 00 0a 10 00 00 00 00 00 00 00 07 14 :: 0714 = 1812 = X res
+ 04 d8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: 04d8 = 1240 = Y res
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00
+
+ 1b 57 21 2e 00 80 00 22 08 03 00 00 00 00 00 00 :: ZZ = num copies (>= 0x01)
+ 00 00 00 00 00 00 00 00 00 00 00 00 ZZ ZZ 00 00 :: YY 00 = normal, 80 = Fine
+ XX 00 00 00 00 00 YY 00 00 00 00 00 00 00 00 00 :: XX 00 = normal, 83 = Cut 2x6
+ 00 01
+
+ 1b 57 22 2e 00 40 00 00 00 00 00 XX 00 00 00 00 :: 00 = normal, 01 = FineDeep
+ 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
+
+ 1b 57 26 2e 00 70 00 00 00 00 00 00 01 01 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
+
+ ~~~~ Data follows: Data is 8-bit BGR.
+
+ 1b 5a 54 00 00 00 00 00 07 14 04 d8 :: 0714 == row len, 04d8 == rows
+ ^^ ^^ :: 0000 == remaining rows
+
+ 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 46 00
+
+ ~~~~ QUESTIONS:
+
+ * Lamination control?
+ * Other multi-cut modes (on 6x9 media: 4x6*2, 4.4x6*2, 3x6*3, 2x6*4)
+
+ ***********************************************************************
+
+ * Mitsubishi ** CP-9550DW-S ** Communications Protocol:
+
+ [[ Unknown ]]
+
+ -> 1b 53 c5 9d
+
+ [[ Unknown ]]
+
+ -> 1b 4b 7f 00
+ <- eb 4b 8f 00 02 00 5e [[ '02' seems to be a length ]]
+
+ [[ Unknown ]]
+
+ -> 1b 53 00 00
+
+ 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')
+
+ Media Query
+
+ -> 1b 56 24 00
+ <- 24 2e 00 00 00 00 00 00 00 00 00 00 00 00 TT 00 :: TT = Type
+ 00 00 00 00 00 00 00 00 00 00 00 00 MM MM 00 00 :: MM MM = Max prints
+ NN NN 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :: NN NN = Remaining
+
+ Status Query
+
+ -> 1b 56 30 00
+ -> 30 2e 00 00 00 00 MM 00 NN NN 00 00 00 00 00 00 :: MM, NN
+ 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 B (not sure what to call this)
+
+ -> 1b 56 21 00
+ <- 21 2e 00 80 00 22 a8 0b 00 00 00 00 00 00 00 00
+ 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
+
+ [[ Unknown ]]
+
+ -> 1b 44
+
+ [[ Header 1 -- See above ]]
+
+ -> 1b 57 20 2e ....
+
+ [[ Header 2 -- See above ]]
+
+ -> 1b 57 21 2e ....
+
+ [[ Header 3 -- See above ]]
+
+ -> 1b 57 22 2e ....
+
+ [[ Unknown -- Start Data ? ]]
+
+ -> 1b 5a 43 00
+
+ [[ Plane header #1 (Blue) ]]
+
+ -> 1b 5a 54 00 00 00 00 00 XX XX YY YY :: XX XX == Columns, YY YY == Rows
+
+ Followed by image plane #1 (Blue), XXXX * YYYY bytes
+
+ [[ Plane header #2 (Green) ]]
+
+ -> 1b 5a 54 00 00 00 00 00 XX XX YY YY :: XX XX == Columns, YY YY == Rows
+
+ Followed by image plane #2 (Green), XXXX * YYYY bytes
+
+ [[ Plane header #3 (Red) ]]
+
+ -> 1b 5a 54 00 00 00 00 00 XX XX YY YY :: XX XX == Columns, YY YY == Rows
+
+ Followed by image plane #3 (Red), XXXX * YYYY bytes
+
+ [[ Unknown -- End Data aka START print? ]]
+
+ -> 1b 50 47 00
+
+ [[ At this point, loop status/status b/media queries until printer idle ]]
+
+ MM, NN, QQ RR SS, TT UU
+
+ <- 00 00 3e 00 00 8a 44 :: Idle.
+ 00 00 7e 00 00 8a 44 :: Plane data submitted, pre "end data" cmd
+ 00 00 7e 40 01 8a 44 :: "end data" sent
+ 30 01 7e 40 01 8a 44
+ 38 01 7e 40 01 8a 44
+ 59 01 7e 40 01 8a 44
+ 59 01 7e 40 00 8a 44
+ 4d 01 7e 40 00 8a 44
+ [...]
+ 43 01 7e 40 00 82 44
+ [...]
+ 50 01 7e 40 00 80 44
+ [...]
+ 31 01 7e 40 00 7d 44
+ [...]
+ 00 00 3e 00 00 80 44 :: Idle.
+
+ Also seen:
+
+ 00 00 3e 00 00 96 4b :: Idle
+ 00 00 be 00 00 96 4b :: Data submitted, pre "start"
+ 00 00 be 80 01 96 4b :: print start sent
+ 30 00 be 80 01 96 4c
+ [...]
+ 30 03 be 80 01 89 4b
+ 38 03 be 80 01 8a 4b
+ 59 03 be 80 01 8b 4b
+ [...]
+ 4d 03 be 80 01 89 4b
+ [...]
+ 43 03 be 80 01 89 4b
+ [...]
+ 50 03 be 80 01 82 4b
+ [...]
+ 31 03 be 80 01 80 4b
+ [...]
+
+ 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 0x7c through 0x96)
+ UU :: ?? seen values between 0x44 and 0x4c
+
+ ***
+
+ Other printer commands seen:
+
+ [[ Set error policy ?? aka "header 4" ]]
+
+ -> 1b 57 26 2e 00 QQ 00 00 00 00 00 00 RR SS 00 00 :: QQ/RR 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
+
+ */