summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2016-12-03 23:21:59 -0500
committerSolomon Peachy <pizza@shaftnet.org>2016-12-03 23:33:18 -0500
commitf4af8f648bcd3c8d5815ef7605479e9c6726a52e (patch)
treea8cb5aee806ddce0eff58d77bde11af0febbe121
parent5d9a1b29e6a6d5724569d667642fbccb3afda76e (diff)
downloadselphy_print-f4af8f648bcd3c8d5815ef7605479e9c6726a52e.tar.gz
selphy_print-f4af8f648bcd3c8d5815ef7605479e9c6726a52e.tar.bz2
selphy_print-f4af8f648bcd3c8d5815ef7605479e9c6726a52e.zip
mitsup95d: Add in support for the Mitsubishi P95D.
-rw-r--r--.gitignore1
-rw-r--r--Makefile2
-rw-r--r--README10
-rw-r--r--backend_common.c2
-rw-r--r--backend_common.h1
-rw-r--r--backend_mitsup95d.c535
-rw-r--r--blacklist3
7 files changed, 553 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index ae7736b..289a26e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ dnpds40
citizencw01
dyesub_backend
mitsu9550
+mitsup95d
diff --git a/Makefile b/Makefile
index ff38323..76cb7d0 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,7 @@ CPPFLAGS += -DURI_PREFIX=\"$(BACKEND_NAME)\"
CFLAGS += -funit-at-a-time
# List of backends
-BACKENDS = sonyupdr150 kodak6800 kodak1400 shinkos2145 shinkos1245 canonselphy mitsu70x kodak605 dnpds40 citizencw01 mitsu9550 shinkos6245 shinkos6145 canonselphyneo
+BACKENDS = sonyupdr150 kodak6800 kodak1400 shinkos2145 shinkos1245 canonselphy mitsu70x kodak605 dnpds40 citizencw01 mitsu9550 shinkos6245 shinkos6145 canonselphyneo mitsup95d
# For the s6145 and mitsu70x backends
CPPFLAGS += -DUSE_DLOPEN
diff --git a/README b/README
index 942e647..dcde95e 100644
--- a/README
+++ b/README
@@ -44,6 +44,7 @@
Mitsubishi CP-K60DW-S
Mitsubishi CP-D70DW
Mitsubishi CP-D80DW
+ Mitsubishi P95DW
Shinko CHC-S6145 (aka Sinfonia CS2)
Ciaat Brava 21
@@ -658,6 +659,15 @@
of figuring out how everything works. Progress is limited.
***************************************************************************
+ BACKEND=mitsup95d
+
+ Verified supported printers:
+
+ Mitsubishi P95DW
+
+ This backend does not support additional commands.
+
+ ***************************************************************************
BACKEND=dnpds40
Verified supported printers:
diff --git a/backend_common.c b/backend_common.c
index c1ec2e7..a3beeb3 100644
--- a/backend_common.c
+++ b/backend_common.c
@@ -565,6 +565,7 @@ extern struct dyesub_backend canonselphy_backend;
extern struct dyesub_backend canonselphyneo_backend;
extern struct dyesub_backend mitsu70x_backend;
extern struct dyesub_backend mitsu9550_backend;
+extern struct dyesub_backend mitsup95d_backend;
extern struct dyesub_backend dnpds40_backend;
extern struct dyesub_backend cw01_backend;
@@ -581,6 +582,7 @@ static struct dyesub_backend *backends[] = {
&updr150_backend,
&mitsu70x_backend,
&mitsu9550_backend,
+ &mitsup95d_backend,
&dnpds40_backend,
&cw01_backend,
NULL,
diff --git a/backend_common.h b/backend_common.h
index 15c6ff7..3b69128 100644
--- a/backend_common.h
+++ b/backend_common.h
@@ -117,6 +117,7 @@ enum {
P_MITSU_9800,
P_MITSU_9800S,
P_MITSU_9810,
+ P_MITSU_P95D,
P_DNP_DS40,
P_DNP_DS80,
P_DNP_DS80D,
diff --git a/backend_mitsup95d.c b/backend_mitsup95d.c
new file mode 100644
index 0000000..d074092
--- /dev/null
+++ b/backend_mitsup95d.c
@@ -0,0 +1,535 @@
+/*
+ * Mitsubishi P95D Monochrome Thermal Photo Printer CUPS backend
+ *
+ * (c) 2016 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 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>
+
+#define BACKEND mitsup95d_backend
+
+#include "backend_common.h"
+
+#define USB_VID_MITSU 0x06D3
+#define USB_PID_MITSU_P95D 0x3b10
+
+/* Private data stucture */
+struct mitsup95d_ctx {
+ struct libusb_device_handle *dev;
+ uint8_t endp_up;
+ uint8_t endp_down;
+
+ uint8_t mem_clr[4]; // 1b 5a 43 00
+ int mem_clr_present;
+
+ uint8_t hdr[2]; // 1b 51
+
+ uint8_t hdr1[50]; // 1b 57 20 2e ...
+ uint8_t hdr2[50]; // 1b 57 21 2e ...
+ uint8_t hdr3[50]; // 1b 57 22 2e ...
+
+ uint8_t hdr4[36]; // 1b 58 ...
+ uint8_t plane[12]; // 1b 5a 74 00 ...
+
+ uint8_t *databuf;
+ uint32_t datalen;
+
+ uint8_t ftr[2];
+};
+
+
+static void *mitsup95d_init(void)
+{
+ struct mitsup95d_ctx *ctx = malloc(sizeof(struct mitsup95d_ctx));
+ if (!ctx) {
+ ERROR("Memory Allocation Failure!\n");
+ return NULL;
+ }
+ memset(ctx, 0, sizeof(struct mitsup95d_ctx));
+
+ return ctx;
+}
+
+static void mitsup95d_attach(void *vctx, struct libusb_device_handle *dev,
+ uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
+{
+ struct mitsup95d_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);
+
+}
+
+static void mitsup95d_teardown(void *vctx) {
+ struct mitsup95d_ctx *ctx = vctx;
+
+ if (!ctx)
+ return;
+
+ if (ctx->databuf)
+ free(ctx->databuf);
+ free(ctx);
+}
+
+static int mitsup95d_read_parse(void *vctx, int data_fd) {
+ struct mitsup95d_ctx *ctx = vctx;
+ uint8_t buf[2]; /* Enough to read in any header */
+ uint8_t tmphdr[50];
+ uint8_t *ptr;
+ int i;
+ int remain;
+ int ptr_offset;
+
+ if (!ctx)
+ return CUPS_BACKEND_FAILED;
+
+ if (ctx->databuf) {
+ free(ctx->databuf);
+ ctx->databuf = NULL;
+ }
+ ctx->mem_clr_present = 0;
+
+top:
+ i = read(data_fd, buf, sizeof(buf));
+ if (i == 0)
+ return CUPS_BACKEND_CANCEL;
+ if (i < 0)
+ return CUPS_BACKEND_CANCEL;
+
+ if (buf[0] != 0x1b) {
+ ERROR("malformed data stream\n");
+ return CUPS_BACKEND_CANCEL;
+ }
+
+ switch (buf[1]) {
+ case 0x43: /* Memory Clear */
+ remain = 4;
+ ptr = ctx->mem_clr;
+ ctx->mem_clr_present = 1;
+ break;
+ case 0x50: /* Footer */
+ remain = 2;
+ ptr = ctx->ftr;
+ break;
+ case 0x51: /* Job Header */
+ remain = 2;
+ ptr = ctx->hdr;
+ break;
+ case 0x57: /* Geeneral headers */
+ remain = sizeof(tmphdr);
+ ptr = tmphdr;
+ break;
+ case 0x58: /* User Comment */
+ remain = 36;
+ ptr = ctx->hdr4;
+ break;
+ case 0x5a: /* Plane header */
+ remain = 12;
+ ptr = ctx->plane;
+ break;
+ default:
+ ERROR("Unrecognized command! (%02x %02x)\n", buf[0], buf[1]);
+ return CUPS_BACKEND_CANCEL;
+ }
+
+ memcpy(ptr, buf, sizeof(buf));
+ remain -= sizeof(buf);
+ ptr_offset = sizeof(buf);
+
+ while (remain) {
+ i = read(data_fd, ptr + ptr_offset, remain);
+ if (i == 0)
+ return CUPS_BACKEND_CANCEL;
+ if (i < 0)
+ return CUPS_BACKEND_CANCEL;
+ remain -= i;
+ ptr_offset += i;
+ }
+
+ if (ptr == tmphdr) {
+ if (tmphdr[3] != 46) {
+ ERROR("Unexpected header chunk: %02x %02x %02x %02x\n",
+ tmphdr[0], tmphdr[1], tmphdr[2], tmphdr[3]);
+ return CUPS_BACKEND_CANCEL;
+ }
+ switch (tmphdr[2]) {
+ case 0x20:
+ ptr = ctx->hdr1;
+ break;
+ case 0x21:
+ ptr = ctx->hdr2;
+ break;
+ case 0x22:
+ ptr = ctx->hdr3;
+ break;
+ default:
+ ERROR("Unexpected header chunk: %02x %02x %02x %02x\n",
+ tmphdr[0], tmphdr[1], tmphdr[2], tmphdr[3]);
+ }
+ memcpy(ptr, tmphdr, sizeof(tmphdr));
+ } else if (ptr == ctx->plane) {
+ uint16_t rows = ctx->plane[10] << 8 | ctx->plane[11];
+ uint16_t cols = ctx->plane[8] << 8 | ctx->plane[9];
+
+ remain = rows * cols;
+
+ /* Allocate buffer for the payload */
+ ctx->datalen = 0;
+ ctx->databuf = malloc(remain);
+ if (!ctx->databuf) {
+ ERROR("Memory allocation failure!\n");
+ return CUPS_BACKEND_FAILED;
+ }
+
+ /* Read it in */
+ 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;
+ remain -= i;
+ ctx->datalen += i;
+ }
+ } else if (ptr == ctx->ftr) {
+ return CUPS_BACKEND_OK;
+ }
+
+ goto top;
+}
+
+static int mitsup95d_main_loop(void *vctx, int copies) {
+ struct mitsup95d_ctx *ctx = vctx;
+ uint8_t querycmd[4] = { 0x1b, 0x72, 0x00, 0x00 };
+ uint8_t queryresp[9];
+
+ int ret;
+ int num;
+
+ if (!ctx)
+ return CUPS_BACKEND_FAILED;
+
+ /* Update printjob header to reflect number of requested copies */
+ if (ctx->hdr2[13] != 0xff)
+ ctx->hdr2[13] = copies;
+
+ /* XXX Update unknown header field to match sniffs */
+ if (ctx->hdr1[18] == 0x00)
+ ctx->hdr1[18] = 0x01;
+
+ INFO("Waiting for printer idle\n");
+
+ /* Query Status to make sure printer is idle */
+ do {
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ querycmd, sizeof(querycmd))))
+ return CUPS_BACKEND_FAILED;
+ ret = read_data(ctx->dev, ctx->endp_up,
+ queryresp, sizeof(queryresp), &num);
+ if (num != sizeof(queryresp) || ret < 0) {
+ return CUPS_BACKEND_FAILED;
+ }
+
+ if (queryresp[5] & 0x40) {
+ ERROR("Printer error %02x\n", queryresp[5]); // XXX decode
+ return CUPS_BACKEND_STOP;
+ }
+ if (queryresp[5] == 0x00)
+ break;
+
+ sleep(1);
+ } while (1);
+
+ /* Send over Memory Clear, if present */
+ if (ctx->mem_clr_present) {
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->mem_clr, sizeof(ctx->mem_clr))))
+ return CUPS_BACKEND_FAILED;
+ }
+
+ /* Send Job Start */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->hdr, sizeof(ctx->hdr))))
+ return CUPS_BACKEND_FAILED;
+
+ /* Send over headers */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->hdr1, sizeof(ctx->hdr1))))
+ return CUPS_BACKEND_FAILED;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->hdr2, sizeof(ctx->hdr2))))
+ return CUPS_BACKEND_FAILED;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->hdr3, sizeof(ctx->hdr3))))
+ return CUPS_BACKEND_FAILED;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->hdr4, sizeof(ctx->hdr4))))
+ return CUPS_BACKEND_FAILED;
+
+ /* Send plane header and image data */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->plane, sizeof(ctx->plane))))
+ return CUPS_BACKEND_FAILED;
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->databuf, ctx->datalen)))
+ return CUPS_BACKEND_FAILED;
+
+ /* Query Status to sanity-check job */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ querycmd, sizeof(querycmd))))
+ return CUPS_BACKEND_FAILED;
+ ret = read_data(ctx->dev, ctx->endp_up,
+ queryresp, sizeof(queryresp), &num);
+ if (num != sizeof(queryresp) || ret < 0) {
+ return CUPS_BACKEND_FAILED;
+ }
+ if (queryresp[5] & 0x40) {
+ ERROR("Printer error %02x\n", queryresp[5]); // XXX decode
+ return CUPS_BACKEND_STOP;
+ }
+ if (queryresp[5] != 0x00) {
+ ERROR("Printer not ready (%02x)!\n", queryresp[5]);
+ return CUPS_BACKEND_CANCEL;
+ }
+
+ /* Send over Footer */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ ctx->ftr, sizeof(ctx->ftr))))
+ return CUPS_BACKEND_FAILED;
+
+ /* Query status until we're done.. */
+ do {
+ sleep(1);
+
+ /* Query Status */
+ if ((ret = send_data(ctx->dev, ctx->endp_down,
+ querycmd, sizeof(querycmd))))
+ return CUPS_BACKEND_FAILED;
+ ret = read_data(ctx->dev, ctx->endp_up,
+ queryresp, sizeof(queryresp), &num);
+ if (num != sizeof(queryresp) || ret < 0) {
+ return CUPS_BACKEND_FAILED;
+ }
+
+ if (queryresp[5] & 0x40) {
+ ERROR("Printer error %02x\n", queryresp[5]); // XXX decode
+ return CUPS_BACKEND_STOP;
+ }
+ if (queryresp[5] == 0 && queryresp[7] == 0)
+ break;
+
+ if (queryresp[7] > 0) {
+ if (fast_return) {
+ INFO("Fast return mode enabled.\n");
+ break;
+ }
+ }
+ } while(1);
+
+ INFO("Print complete\n");
+ return CUPS_BACKEND_OK;
+}
+
+static int mitsup95d_cmdline_arg(void *vctx, int argc, char **argv)
+{
+ struct canonselphy_ctx *ctx = vctx;
+ int i, j = 0;
+
+ if (!ctx)
+ return -1;
+
+ while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL)) >= 0) {
+ switch(i) {
+ GETOPT_PROCESS_GLOBAL
+ }
+
+ if (j) return j;
+ }
+
+ return 0;
+}
+
+/* Exported */
+struct dyesub_backend mitsup95d_backend = {
+ .name = "Mitsubishi P95D",
+ .version = "0.01",
+ .uri_prefix = "mitsup95d",
+ .cmdline_arg = mitsup95d_cmdline_arg,
+ .init = mitsup95d_init,
+ .attach = mitsup95d_attach,
+ .teardown = mitsup95d_teardown,
+ .read_parse = mitsup95d_read_parse,
+ .main_loop = mitsup95d_main_loop,
+ .devices = {
+ { USB_VID_MITSU, USB_PID_MITSU_P95D, P_MITSU_P95D, ""},
+ { 0, 0, 0, ""}
+ }
+};
+
+/*****************************************************
+
+ Mitsubishi P95D Spool Format
+
+ ...All fields are BIG ENDIAN.
+
+ MEMORY_CLEAR (optional)
+
+ 1b 5a 43 00
+
+ JOB_HDR
+
+ 1b 51
+
+ PRINT_SETUP
+
+ 1b 57 20 2e 00 0a 00 02 00 00 00 00 00 00 CC CC
+ RR RR XX 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
+
+ XX == 01 seen in sniffs, 00 seen in dumps. Unknown!
+
+ CC CC = columns, RR RR = rows (print dimensions)
+
+ PRINT_OPTIONS
+
+
+ 1b 57 21 2e 00 4a aa 00 20 TT 00 00 64 NN 00 MM
+ [[ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 ]] 00 00 00 02 00 00 00 00 00 00 00 00 00 00
+ 00 XY
+
+ NN = copies
+ 1..200
+ 0xff (continuous print)
+ MM = comment type
+ 00 = None
+ 01 = Printer Setting
+ 02 = Date
+ 03 = DateTime
+ [[ .. ]] = actual comment (18 bytes), see below.
+ TT = media type
+ 00 = Standard
+ 01 = High Density
+ 02 = High Glossy
+ 03 = High Glossy (K95HG)
+ X = media cut length
+ 4..8 (mm)
+ Y = flags
+ 0x04 = Paper save
+ 0x03 = Buzzer (3 = high, 2 = low, 0 = off)
+
+
+ GAMMA ????
+
+ 1b 57 22 2e 00 15 TT 00 00 00 00 00 LL BB CC 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 ]]
+
+ LL = Gamma table
+ 00 = Printer Setting
+ 01..05 Gamma table 1..5
+ 10 = Use LUT
+ BB = Brightness (signed 8-bit)
+ CC = Contrast (signed 8-bit)
+ TT = Table present
+ 00 = No
+ 01 = Yes
+ [[ .. ]] = Gamma table, loaded from LUT on disk. (skip first 16 bytes)
+
+ USER_COMMENT
+
+ 1b 58 [[ 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 ]]
+
+ [[ .. ]] = Actual comment. 34 bytes payload, 0x20 -> 0x7e
+ (Null terminated?)
+
+ IMAGE_DATA
+
+ 1b 5a 74 00 00 00 YY YY CC CC RR RR
+ [[ .. data ... ]]
+
+ CC CC = columns
+ RR CC = rows
+ YY YY = row offset
+
+ Followed by C*R bytes of monochrome data, 0xff = white, 0x00 = black
+
+ PRINT_START
+
+ 1b 50
+
+ *********************************
+
+ Printer Comms:
+
+ STATUS query
+
+ -> 1b 72 00 00
+ <- e4 72 00 00 04 XX 00 YY 00
+
+ YY == remaining copies
+ XX == Status?
+ 00 == Idle
+ 02 == Printing
+ 43 == Door open
+ 44 == No Paper
+ 4? == "Button"
+ 4? == "Gear Lock"
+ 4? == Head Up
+ ^
+ \--- 0x40 appears to be a flag that indicates error.
+
+ ****************************
+
+UNKNOWNS:
+
+ * How multiple images are stacked for printing on a single page
+ (col offset too? write four, then tell PRINT?) Is this the mystery 0x01?
+ * How to adjust printer sharpness?
+ * Serial number query (iSerial appears bogus)
+ * What "custom gamma" table does to spool file?
+
+*/
+
+
diff --git a/blacklist b/blacklist
index a8c0380..a3cb817 100644
--- a/blacklist
+++ b/blacklist
@@ -175,6 +175,9 @@
# Mitsubishi CP-9810D/DW
0x06d3 0x3b21 blacklist
+# Mitsubishi P95D
+0x06d3 0x3b10 blacklist
+
# Citizen CW-01
0x1343 0x0002 blacklist