2014-12-11 16:16:56 -05:00
|
|
|
/*
|
2016-10-13 12:50:30 -04:00
|
|
|
* Mitsubishi CP-9xxx Photo Printer Family CUPS backend
|
2014-12-11 16:16:56 -05:00
|
|
|
*
|
2016-01-24 09:44:19 -05:00
|
|
|
* (c) 2014-2016 Solomon Peachy <pizza@shaftnet.org>
|
2014-12-11 16:16:56 -05:00
|
|
|
*
|
|
|
|
* 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>
|
|
|
|
|
2015-08-13 21:09:56 -04:00
|
|
|
#define BACKEND mitsu9550_backend
|
|
|
|
|
2014-12-11 16:16:56 -05:00
|
|
|
#include "backend_common.h"
|
|
|
|
|
2016-11-04 15:44:02 -04:00
|
|
|
#define USB_VID_MITSU 0x06D3
|
|
|
|
#define USB_PID_MITSU_9500D 0x0393
|
|
|
|
#define USB_PID_MITSU_9000D 0x0394
|
|
|
|
#define USB_PID_MITSU_9000AM 0x0395
|
2014-12-11 16:16:56 -05:00
|
|
|
#define USB_PID_MITSU_9550D 0x03A1
|
2014-12-15 07:57:29 -05:00
|
|
|
#define USB_PID_MITSU_9550DS 0x03A5 // or DZ/DZS/DZU
|
2016-10-12 23:28:54 -04:00
|
|
|
#define USB_PID_MITSU_9600D 0x03A9
|
2016-10-13 08:12:20 -04:00
|
|
|
//#define USB_PID_MITSU_9600DS XXXXXX
|
2016-11-04 15:44:02 -04:00
|
|
|
#define USB_PID_MITSU_9800D 0x03AD
|
2016-10-13 12:41:55 -04:00
|
|
|
#define USB_PID_MITSU_9800DS 0x03AE
|
2016-11-04 15:44:02 -04:00
|
|
|
#define USB_PID_MITSU_98__D 0x3B21
|
2016-10-12 23:28:54 -04:00
|
|
|
//#define USB_PID_MITSU_9810D XXXXXX
|
2016-10-14 07:42:24 -04:00
|
|
|
//#define USB_PID_MITSU_9820DS XXXXXX
|
2014-12-11 16:16:56 -05:00
|
|
|
|
|
|
|
/* Spool file structures */
|
2016-10-12 23:28:54 -04:00
|
|
|
|
|
|
|
/* Print parameters1 */
|
2014-12-11 16:16:56 -05:00
|
|
|
struct mitsu9550_hdr1 {
|
|
|
|
uint8_t cmd[4]; /* 1b 57 20 2e */
|
2016-10-12 23:28:54 -04:00
|
|
|
uint8_t unk[10]; /* 00 0a 10 00 [...] */
|
2014-12-11 16:16:56 -05:00
|
|
|
uint16_t cols; /* BE */
|
2015-01-07 10:22:26 -05:00
|
|
|
uint16_t rows; /* BE */
|
2016-10-12 20:29:02 -04:00
|
|
|
uint8_t matte; /* CP9810 only. 01 for matte, 00 glossy */
|
|
|
|
uint8_t null[31];
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
2016-10-12 23:28:54 -04:00
|
|
|
/* Print parameters2 */
|
2014-12-11 16:16:56 -05:00
|
|
|
struct mitsu9550_hdr2 {
|
2014-12-14 18:10:01 -05:00
|
|
|
uint8_t cmd[4]; /* 1b 57 21 2e */
|
2016-09-28 14:15:04 -04:00
|
|
|
uint8_t unk[24]; /* 00 80 00 22 08 03 00 [...] */
|
|
|
|
uint16_t copies; /* BE, 1-680 */
|
2014-12-14 18:10:01 -05:00
|
|
|
uint8_t null[2];
|
2014-12-11 16:16:56 -05:00
|
|
|
uint8_t cut; /* 00 == normal, 83 == 2x6*2 */
|
2014-12-25 08:23:18 -05:00
|
|
|
uint8_t unkb[5];
|
2016-10-12 23:28:54 -04:00
|
|
|
uint8_t mode; /* 00 == fine, 80 == superfine */
|
2016-09-28 14:15:04 -04:00
|
|
|
uint8_t unkc[11]; /* 00 [...] 00 01 */
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
2016-10-12 23:28:54 -04:00
|
|
|
/* Fine Deep selection (9550 only) */
|
2014-12-11 16:16:56 -05:00
|
|
|
struct mitsu9550_hdr3 {
|
|
|
|
uint8_t cmd[4]; /* 1b 57 22 2e */
|
2016-09-28 14:15:04 -04:00
|
|
|
uint8_t unk[7]; /* 00 40 00 [...] */
|
2014-12-11 16:16:56 -05:00
|
|
|
uint8_t mode2; /* 00 == normal, 01 == finedeep */
|
2016-09-28 14:15:04 -04:00
|
|
|
uint8_t null[38];
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
2016-10-12 23:28:54 -04:00
|
|
|
/* Error policy? */
|
2014-12-11 16:16:56 -05:00
|
|
|
struct mitsu9550_hdr4 {
|
|
|
|
uint8_t cmd[4]; /* 1b 57 26 2e */
|
2016-09-28 14:15:04 -04:00
|
|
|
uint8_t unk[46]; /* 00 70 00 00 00 00 00 00 01 01 00 [...] */
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
2016-10-13 08:12:20 -04:00
|
|
|
/* Data plane header */
|
2014-12-11 16:16:56 -05:00
|
|
|
struct mitsu9550_plane {
|
2016-10-23 09:32:43 -04:00
|
|
|
uint8_t cmd[4]; /* 1b 5a 54 XX */ /* XX == 0x10 if 16bpp, 0x00 for 8bpp */
|
|
|
|
uint16_t row_offset; /* BE, normally 0, where we start dumping data */
|
|
|
|
uint16_t null; /* ??? */
|
|
|
|
uint16_t cols; /* BE */
|
|
|
|
uint16_t rows; /* BE */
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
2014-12-14 19:09:32 -05:00
|
|
|
struct mitsu9550_cmd {
|
|
|
|
uint8_t cmd[4];
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
2016-10-13 08:12:20 -04:00
|
|
|
/* Private data stucture */
|
|
|
|
struct mitsu9550_ctx {
|
|
|
|
struct libusb_device_handle *dev;
|
|
|
|
uint8_t endp_up;
|
|
|
|
uint8_t endp_down;
|
|
|
|
int type;
|
2016-10-14 07:42:24 -04:00
|
|
|
int is_s;
|
2016-10-13 08:12:20 -04:00
|
|
|
|
|
|
|
uint8_t *databuf;
|
|
|
|
uint32_t datalen;
|
|
|
|
|
|
|
|
uint16_t rows;
|
|
|
|
uint16_t cols;
|
|
|
|
uint32_t plane_len;
|
|
|
|
|
|
|
|
uint16_t last_donor;
|
|
|
|
uint16_t last_remain;
|
|
|
|
int marker_reported;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
};
|
|
|
|
|
2014-12-11 16:16:56 -05:00
|
|
|
/* Printer data structures */
|
|
|
|
struct mitsu9550_media {
|
2014-12-14 20:52:21 -05:00
|
|
|
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];
|
2014-12-17 19:23:41 -05:00
|
|
|
uint16_t remain; /* BE, prints remaining */
|
2014-12-14 20:52:21 -05:00
|
|
|
uint8_t unkc[14];
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
struct mitsu9550_status {
|
2014-12-14 20:52:21 -05:00
|
|
|
uint8_t hdr[2]; /* 30 2e */
|
|
|
|
uint8_t null[4];
|
2014-12-15 09:11:41 -05:00
|
|
|
uint8_t sts1; // MM
|
2014-12-20 12:43:22 -05:00
|
|
|
uint8_t nullb[1];
|
2016-09-28 14:15:04 -04:00
|
|
|
uint16_t copies; // BE, NN
|
2016-10-31 14:02:23 -04:00
|
|
|
uint8_t sts2; // ZZ (9600 only?)
|
|
|
|
uint8_t nullc[5];
|
2014-12-15 09:11:41 -05:00
|
|
|
uint8_t sts3; // QQ
|
|
|
|
uint8_t sts4; // RR
|
|
|
|
uint8_t sts5; // SS
|
2014-12-14 20:52:21 -05:00
|
|
|
uint8_t nulld[25];
|
2014-12-15 09:11:41 -05:00
|
|
|
uint8_t sts6; // TT
|
|
|
|
uint8_t sts7; // UU
|
2014-12-14 20:52:21 -05:00
|
|
|
uint8_t nulle[2];
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
struct mitsu9550_status2 {
|
2014-12-14 20:52:21 -05:00
|
|
|
uint8_t hdr[2]; /* 21 2e */
|
2016-10-31 13:44:56 -04:00
|
|
|
uint8_t unk[39];
|
|
|
|
uint16_t remain; /* BE, media remaining */
|
2016-10-31 14:02:23 -04:00
|
|
|
uint8_t unkb[4]; /* 0a 00 00 01 */
|
2014-12-11 16:16:56 -05:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
#define CMDBUF_LEN 64
|
|
|
|
#define READBACK_LEN 128
|
|
|
|
|
2016-08-17 21:16:15 -04:00
|
|
|
#define QUERY_STATUS() \
|
|
|
|
do {\
|
|
|
|
struct mitsu9550_status *sts = (struct mitsu9550_status*) rdbuf;\
|
|
|
|
/* struct mitsu9550_status2 *sts2 = (struct mitsu9550_status2*) rdbuf; */ \
|
|
|
|
struct mitsu9550_media *media = (struct mitsu9550_media *) rdbuf; \
|
|
|
|
uint16_t donor, remain; \
|
|
|
|
/* media */ \
|
|
|
|
ret = mitsu9550_get_status(ctx, rdbuf, 0, 0, 1); \
|
|
|
|
if (ret < 0) \
|
|
|
|
return CUPS_BACKEND_FAILED; \
|
|
|
|
\
|
2016-08-19 17:40:12 -04:00
|
|
|
/* Tell CUPS about the consumables we report */ \
|
|
|
|
if (!ctx->marker_reported) { \
|
|
|
|
ctx->marker_reported = 1; \
|
|
|
|
ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n"); \
|
|
|
|
ATTR("marker-high-levels=100\n"); \
|
|
|
|
ATTR("marker-low-levels=10\n"); \
|
2016-10-14 07:42:24 -04:00
|
|
|
ATTR("marker-names='%s'\n", mitsu9550_media_types(media->type, ctx->is_s)); \
|
2016-08-19 17:40:12 -04:00
|
|
|
ATTR("marker-types=ribbonWax\n"); \
|
|
|
|
} \
|
|
|
|
\
|
2016-08-17 21:16:15 -04:00
|
|
|
/* Sanity-check media response */ \
|
|
|
|
if (media->remain == 0 || media->max == 0) { \
|
|
|
|
ERROR("Printer out of media!\n"); \
|
|
|
|
ATTR("marker-levels=%d\n", 0); \
|
|
|
|
return CUPS_BACKEND_HOLD; \
|
|
|
|
} \
|
2016-10-14 13:25:25 -04:00
|
|
|
remain = be16_to_cpu(media->remain); \
|
|
|
|
donor = be16_to_cpu(media->max); \
|
|
|
|
donor = remain/donor; \
|
2016-08-17 21:16:15 -04:00
|
|
|
if (donor != ctx->last_donor) { \
|
|
|
|
ctx->last_donor = donor; \
|
2016-08-21 10:23:00 -04:00
|
|
|
ATTR("marker-levels=%u\n", donor); \
|
2016-08-17 21:16:15 -04:00
|
|
|
} \
|
|
|
|
if (remain != ctx->last_remain) { \
|
|
|
|
ctx->last_remain = remain; \
|
2016-10-14 07:42:24 -04:00
|
|
|
ATTR("marker-message=\"%u prints remaining on '%s' ribbon\"\n", remain, mitsu9550_media_types(media->type, ctx->is_s)); \
|
2016-08-17 21:16:15 -04:00
|
|
|
} \
|
2016-10-14 13:25:25 -04:00
|
|
|
if (validate_media(ctx->type, media->type, ctx->cols, ctx->rows)) { \
|
2016-08-21 10:23:00 -04:00
|
|
|
ERROR("Incorrect media (%u) type for printjob (%ux%u)!\n", media->type, ctx->cols, ctx->rows); \
|
2016-08-17 21:16:15 -04:00
|
|
|
return CUPS_BACKEND_HOLD; \
|
|
|
|
} \
|
|
|
|
/* status2 */ \
|
|
|
|
ret = mitsu9550_get_status(ctx, rdbuf, 0, 1, 0); \
|
|
|
|
if (ret < 0) \
|
|
|
|
return CUPS_BACKEND_FAILED; \
|
|
|
|
/* status */ \
|
|
|
|
ret = mitsu9550_get_status(ctx, rdbuf, 1, 0, 0); \
|
|
|
|
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; \
|
|
|
|
} \
|
2016-10-31 14:02:23 -04:00
|
|
|
/* Check for known errors */ \
|
|
|
|
if (sts->sts2 != 0) { \
|
|
|
|
ERROR("Printer cover open!\n"); \
|
|
|
|
return CUPS_BACKEND_STOP; \
|
|
|
|
} \
|
2016-08-17 21:16:15 -04:00
|
|
|
} while (0);
|
|
|
|
|
2014-12-11 16:16:56 -05:00
|
|
|
static void *mitsu9550_init(void)
|
|
|
|
{
|
|
|
|
struct mitsu9550_ctx *ctx = malloc(sizeof(struct mitsu9550_ctx));
|
2015-06-23 20:32:41 -04:00
|
|
|
if (!ctx) {
|
|
|
|
ERROR("Memory Allocation Failure!\n");
|
2014-12-11 16:16:56 -05:00
|
|
|
return NULL;
|
2015-06-23 20:32:41 -04:00
|
|
|
}
|
2014-12-11 16:16:56 -05:00
|
|
|
memset(ctx, 0, sizeof(struct mitsu9550_ctx));
|
|
|
|
|
|
|
|
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;
|
2014-12-15 08:01:03 -05:00
|
|
|
struct libusb_device *device;
|
|
|
|
struct libusb_device_descriptor desc;
|
2014-12-11 16:16:56 -05:00
|
|
|
|
|
|
|
UNUSED(jobid);
|
|
|
|
|
|
|
|
ctx->dev = dev;
|
|
|
|
ctx->endp_up = endp_up;
|
|
|
|
ctx->endp_down = endp_down;
|
2014-12-15 08:01:03 -05:00
|
|
|
|
|
|
|
device = libusb_get_device(dev);
|
|
|
|
libusb_get_device_descriptor(device, &desc);
|
|
|
|
|
2015-08-12 22:56:29 -04:00
|
|
|
ctx->type = lookup_printer_type(&mitsu9550_backend,
|
2016-08-17 21:16:15 -04:00
|
|
|
desc.idVendor, desc.idProduct);
|
|
|
|
|
2016-10-14 07:42:24 -04:00
|
|
|
if (ctx->type == P_MITSU_9550S ||
|
|
|
|
ctx->type == P_MITSU_9800S)
|
|
|
|
ctx->is_s = 1;
|
|
|
|
|
2016-08-17 21:16:15 -04:00
|
|
|
ctx->last_donor = ctx->last_remain = 65535;
|
2014-12-11 16:16:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2016-10-13 08:12:20 -04:00
|
|
|
uint8_t buf[sizeof(struct mitsu9550_hdr1)];
|
2014-12-11 16:16:56 -05:00
|
|
|
int remain, i;
|
2016-10-23 09:32:43 -04:00
|
|
|
uint32_t planelen = 0;
|
2014-12-11 16:16:56 -05:00
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
|
|
|
|
if (ctx->databuf) {
|
|
|
|
free(ctx->databuf);
|
|
|
|
ctx->databuf = NULL;
|
|
|
|
}
|
|
|
|
|
2016-10-13 08:12:20 -04:00
|
|
|
ctx->hdr1_present = 0;
|
|
|
|
ctx->hdr2_present = 0;
|
|
|
|
ctx->hdr3_present = 0;
|
|
|
|
ctx->hdr4_present = 0;
|
|
|
|
|
|
|
|
top:
|
2014-12-11 16:16:56 -05:00
|
|
|
/* Read in initial header */
|
2016-10-13 08:12:20 -04:00
|
|
|
remain = sizeof(buf);
|
2014-12-11 16:16:56 -05:00
|
|
|
while (remain > 0) {
|
2016-10-13 08:12:20 -04:00
|
|
|
i = read(data_fd, buf + sizeof(buf) - remain, remain);
|
2014-12-11 16:16:56 -05:00
|
|
|
if (i == 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
if (i < 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
remain -= i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity check */
|
2016-10-13 08:12:20 -04:00
|
|
|
if (buf[0] != 0x1b || buf[1] != 0x57 || buf[3] != 0x2e) {
|
|
|
|
if (!ctx->hdr1_present || !ctx->hdr2_present) {
|
|
|
|
ERROR("Unrecognized data format!\n");
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
2016-10-14 07:42:24 -04:00
|
|
|
} else if (buf[0] == 0x1b && buf[1] == 0x5a &&
|
|
|
|
buf[2] == 0x54) {
|
2016-10-23 09:32:43 -04:00
|
|
|
|
|
|
|
/* We're in the data portion now */
|
|
|
|
if (buf[3] == 0x10)
|
|
|
|
planelen *= 2;
|
|
|
|
|
2016-10-13 08:12:20 -04:00
|
|
|
goto hdr_done;
|
|
|
|
} else {
|
2016-10-23 11:59:29 -04:00
|
|
|
ERROR("Unrecognized data block!\n");
|
2016-10-13 08:12:20 -04:00
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(buf[2]) {
|
|
|
|
case 0x20: /* header 1 */
|
|
|
|
memcpy(&ctx->hdr1, buf, sizeof(ctx->hdr1));
|
|
|
|
ctx->hdr1_present = 1;
|
2016-10-23 09:32:43 -04:00
|
|
|
|
|
|
|
/* Work out printjob size */
|
|
|
|
ctx->rows = be16_to_cpu(ctx->hdr1.rows);
|
|
|
|
ctx->cols = be16_to_cpu(ctx->hdr1.cols);
|
|
|
|
planelen = ctx->rows * ctx->cols;
|
|
|
|
|
2016-10-13 08:12:20 -04:00
|
|
|
break;
|
|
|
|
case 0x21: /* header 2 */
|
|
|
|
memcpy(&ctx->hdr2, buf, sizeof(ctx->hdr2));
|
|
|
|
ctx->hdr2_present = 1;
|
|
|
|
break;
|
|
|
|
case 0x22: /* header 3 */
|
|
|
|
memcpy(&ctx->hdr3, buf, sizeof(ctx->hdr3));
|
|
|
|
ctx->hdr3_present = 1;
|
|
|
|
break;
|
|
|
|
case 0x26: /* header 4 */
|
|
|
|
memcpy(&ctx->hdr4, buf, sizeof(ctx->hdr4));
|
|
|
|
ctx->hdr4_present = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERROR("Unrecognized header format (%02x)!\n", buf[2]);
|
2014-12-11 16:16:56 -05:00
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
|
|
|
|
2016-10-13 08:12:20 -04:00
|
|
|
/* Read in the next chunk */
|
|
|
|
goto top;
|
|
|
|
|
|
|
|
hdr_done:
|
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* We have three planes and the final terminator to read */
|
|
|
|
remain = 3 * (planelen + sizeof(struct mitsu9550_plane)) + sizeof(struct mitsu9550_cmd);
|
2014-12-11 16:16:56 -05:00
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* Mitsu9600 windows spool uses more, smaller blocks, but plane data is the same */
|
|
|
|
if (ctx->type == P_MITSU_9600) {
|
|
|
|
remain += 128 * sizeof(struct mitsu9550_plane); /* 39 extra seen on 4x6" */
|
|
|
|
}
|
2016-10-13 08:12:20 -04:00
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* Don't forget the matte plane! */
|
2016-10-13 08:12:20 -04:00
|
|
|
if (ctx->hdr1.matte) {
|
2016-10-23 09:32:43 -04:00
|
|
|
remain += planelen + sizeof(struct mitsu9550_plane) + sizeof(struct mitsu9550_cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 9550S/9800S doesn't typically sent over hdr4! */
|
|
|
|
if (ctx->type == P_MITSU_9550S ||
|
|
|
|
ctx->type == P_MITSU_9800S) {
|
|
|
|
/* 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 */
|
|
|
|
ctx->hdr4_present = 0;
|
2016-10-13 08:12:20 -04:00
|
|
|
}
|
2014-12-11 16:16:56 -05:00
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* Allocate buffer for the payload */
|
|
|
|
ctx->datalen = 0;
|
2016-10-13 08:12:20 -04:00
|
|
|
ctx->databuf = malloc(remain);
|
2014-12-11 16:16:56 -05:00
|
|
|
if (!ctx->databuf) {
|
|
|
|
ERROR("Memory allocation failure!\n");
|
|
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
}
|
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* Load up the data blocks.*/
|
|
|
|
while(1) {
|
2016-10-23 11:59:29 -04:00
|
|
|
/* Note that 'buf' needs to be already filled here! */
|
2016-10-23 09:32:43 -04:00
|
|
|
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("Unexpected data read, aborting job\n");
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Work out the length of this block */
|
|
|
|
planelen = be16_to_cpu(plane->rows) * be16_to_cpu(plane->cols);
|
|
|
|
if (plane->cmd[3] == 0x10)
|
|
|
|
planelen *= 2;
|
|
|
|
|
|
|
|
/* Copy plane header into buffer */
|
2016-10-23 13:56:59 -04:00
|
|
|
memcpy(ctx->databuf + ctx->datalen, buf, sizeof(buf));
|
2016-10-23 09:32:43 -04:00
|
|
|
ctx->datalen += sizeof(buf);
|
2016-10-23 11:59:29 -04:00
|
|
|
planelen -= sizeof(buf) - sizeof(struct mitsu9550_plane);
|
2016-10-23 09:32:43 -04:00
|
|
|
|
|
|
|
/* Read in the spool data */
|
|
|
|
while(planelen > 0) {
|
|
|
|
i = read(data_fd, ctx->databuf + ctx->datalen, planelen);
|
|
|
|
if (i == 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
if (i < 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
ctx->datalen += i;
|
|
|
|
planelen -= i;
|
|
|
|
}
|
2014-12-11 16:16:56 -05:00
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* Try to read in the next chunk. It will be one of:
|
|
|
|
- Additional block header (12B)
|
|
|
|
- Job footer (4B)
|
|
|
|
*/
|
|
|
|
i = read(data_fd, buf, 4);
|
2014-12-11 16:16:56 -05:00
|
|
|
if (i == 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
if (i < 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* Is this a "job end" marker? */
|
|
|
|
if (plane->cmd[0] != 0x1b ||
|
|
|
|
plane->cmd[1] != 0x5a ||
|
|
|
|
plane->cmd[2] != 0x54) {
|
|
|
|
/* store it in the buffer */
|
|
|
|
memcpy(ctx->databuf + ctx->datalen, buf, 4);
|
|
|
|
ctx->datalen += 4;
|
|
|
|
|
|
|
|
/* Unless we have a matte plane following, we're done */
|
|
|
|
if (!ctx->hdr1.matte)
|
|
|
|
break;
|
|
|
|
planelen = sizeof(buf);
|
|
|
|
} else {
|
|
|
|
/* It's part of a block header, mark what we've read */
|
|
|
|
planelen = sizeof(buf) - 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read in the rest of the header */
|
|
|
|
while (planelen > 0) {
|
|
|
|
i = read(data_fd, buf + sizeof(buf) - planelen, planelen);
|
|
|
|
if (i == 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
if (i < 0)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
planelen -= i;
|
|
|
|
}
|
2016-10-13 08:12:20 -04:00
|
|
|
}
|
|
|
|
|
2016-10-23 09:32:43 -04:00
|
|
|
/* Disable matte if the printer doesn't support it */
|
2016-10-23 11:59:29 -04:00
|
|
|
if (ctx->hdr1.matte && ctx->type != P_MITSU_9810) {
|
2016-10-23 09:32:43 -04:00
|
|
|
WARNING("Matte not supported on this printer, disabling\n");
|
|
|
|
ctx->hdr1.matte = 0;
|
2016-10-13 08:24:51 -04:00
|
|
|
}
|
2016-10-13 12:50:30 -04:00
|
|
|
|
2014-12-11 16:16:56 -05:00
|
|
|
return CUPS_BACKEND_OK;
|
|
|
|
}
|
|
|
|
|
2014-12-15 09:11:41 -05:00
|
|
|
static int mitsu9550_get_status(struct mitsu9550_ctx *ctx, uint8_t *resp, int status, int status2, int media)
|
2014-12-11 16:16:56 -05:00
|
|
|
{
|
2014-12-15 09:11:41 -05:00
|
|
|
struct mitsu9550_cmd cmd;
|
2014-12-14 20:52:21 -05:00
|
|
|
int num, ret;
|
|
|
|
|
|
|
|
/* Send Printer Query */
|
2014-12-15 09:11:41 -05:00
|
|
|
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;
|
2014-12-14 20:52:21 -05:00
|
|
|
if ((ret = send_data(ctx->dev, ctx->endp_down,
|
2014-12-15 09:11:41 -05:00
|
|
|
(uint8_t*) &cmd, sizeof(cmd))))
|
2014-12-14 20:52:21 -05:00
|
|
|
return ret;
|
|
|
|
ret = read_data(ctx->dev, ctx->endp_up,
|
2014-12-17 11:43:38 -05:00
|
|
|
resp, sizeof(struct mitsu9550_status), &num);
|
2014-12-14 20:52:21 -05:00
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2014-12-15 09:11:41 -05:00
|
|
|
if (num != sizeof(struct mitsu9550_status)) {
|
|
|
|
ERROR("Short Read! (%d/%d)\n", num, (int)sizeof(struct mitsu9550_status));
|
2014-12-14 20:52:21 -05:00
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-14 07:42:24 -04:00
|
|
|
static char *mitsu9550_media_types(uint8_t type, uint8_t is_s)
|
2016-08-19 17:40:12 -04:00
|
|
|
{
|
2016-10-14 07:42:24 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-09-28 14:15:04 -04:00
|
|
|
switch (type & 0xf) { /* values can be 0x0? or 0x4? */
|
2016-08-19 17:40:12 -04:00
|
|
|
case 0x01:
|
2016-09-28 14:15:04 -04:00
|
|
|
return "CK9035 (3.5x5)";
|
2016-08-19 17:40:12 -04:00
|
|
|
case 0x02:
|
2016-09-28 14:15:04 -04:00
|
|
|
return "CK9046 (4x6)";
|
2016-08-19 17:40:12 -04:00
|
|
|
case 0x03:
|
2016-09-28 14:15:04 -04:00
|
|
|
return "CK9046PST (4x6)";
|
2016-08-19 17:40:12 -04:00
|
|
|
case 0x04:
|
2016-09-28 14:15:04 -04:00
|
|
|
return "CK9057 (5x7)";
|
2016-08-19 17:40:12 -04:00
|
|
|
case 0x05:
|
2016-09-28 14:15:04 -04:00
|
|
|
return "CK9069 (6x9)";
|
2016-08-19 17:40:12 -04:00
|
|
|
case 0x06:
|
2016-09-28 14:15:04 -04:00
|
|
|
return "CK9068 (6x8)";
|
2016-08-19 17:40:12 -04:00
|
|
|
default:
|
|
|
|
return "Unknown";
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-10-14 13:25:25 -04:00
|
|
|
static int validate_media(int type, int media, int cols, int rows)
|
|
|
|
{
|
2015-01-17 08:46:50 -05:00
|
|
|
switch(type) {
|
2016-10-14 13:25:25 -04:00
|
|
|
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;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
break;
|
2016-10-17 14:59:44 -04:00
|
|
|
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: // XXX 9600S doesn't support 5" media at all!
|
2016-10-14 13:25:25 -04:00
|
|
|
switch(media & 0xf) {
|
|
|
|
case 0x01: /* 3.5x5 */
|
|
|
|
if (cols == 1572) {
|
|
|
|
if (rows == 1076)
|
|
|
|
break;
|
|
|
|
} else if (cols == 3144) {
|
|
|
|
if (rows == 2152)
|
|
|
|
break;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
return 1;
|
2016-10-14 13:25:25 -04:00
|
|
|
case 0x02: /* 4x6 */
|
|
|
|
case 0x03: /* 4x6 postcard */
|
|
|
|
if (cols == 1868) {
|
|
|
|
if (rows == 1228)
|
|
|
|
break;
|
|
|
|
} else if (cols == 3736) {
|
|
|
|
if (rows == 2458)
|
|
|
|
break;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
return 1;
|
2016-10-14 13:25:25 -04:00
|
|
|
case 0x04: /* 5x7 */
|
|
|
|
if (cols == 1572) {
|
|
|
|
if (rows == 1076 || rows == 2128)
|
|
|
|
break;
|
|
|
|
} else if (cols == 3144) {
|
|
|
|
if (rows == 2152 || rows == 4256)
|
|
|
|
break;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
return 1;
|
2016-10-14 13:25:25 -04:00
|
|
|
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;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
return 1;
|
2016-10-14 13:25:25 -04:00
|
|
|
case 0x06: /* V (6x8??) */
|
|
|
|
if (cols == 1868) {
|
|
|
|
if (rows == 1228 || rows == 2442)
|
|
|
|
break;
|
|
|
|
} else if (cols == 3736) {
|
|
|
|
if (rows == 2458 || rows == 4846)
|
|
|
|
break;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
return 1;
|
2016-10-14 13:25:25 -04:00
|
|
|
default: /* Unknown */
|
|
|
|
WARNING("Unknown media type %02x\n", media);
|
|
|
|
break;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
break;
|
2016-10-14 13:25:25 -04:00
|
|
|
case P_MITSU_9800:
|
2016-10-17 14:59:44 -04:00
|
|
|
case P_MITSU_9810: // XXX and don't forget the 9820S
|
2016-10-14 13:25:25 -04:00
|
|
|
switch(media & 0xf) {
|
|
|
|
case 0x01: /* 3.5x5 */
|
|
|
|
if (cols != 1572 && rows != 1076)
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 0x02: /* 4x6 */
|
2016-10-17 14:59:44 -04:00
|
|
|
case 0x03: /* 4x6 postcard */
|
2016-10-14 13:25:25 -04:00
|
|
|
if (cols != 1868 && rows != 1228)
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 0x04: /* 5x7 */
|
2016-10-17 14:59:44 -04:00
|
|
|
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)
|
2016-10-14 13:25:25 -04:00
|
|
|
return 1;
|
2016-10-17 14:59:44 -04:00
|
|
|
break;
|
|
|
|
default: /* Unknown */
|
|
|
|
WARNING("Unknown media type %02x\n", media);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case P_MITSU_9800S:
|
|
|
|
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)
|
2016-10-14 13:25:25 -04:00
|
|
|
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;
|
|
|
|
}
|
2015-01-17 08:46:50 -05:00
|
|
|
break;
|
2016-10-14 13:25:25 -04:00
|
|
|
default:
|
|
|
|
WARNING("Unknown printer type %d\n", type);
|
2015-01-17 08:46:50 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-11 16:16:56 -05:00
|
|
|
static int mitsu9550_main_loop(void *vctx, int copies) {
|
|
|
|
struct mitsu9550_ctx *ctx = vctx;
|
2014-12-15 08:25:05 -05:00
|
|
|
struct mitsu9550_cmd cmd;
|
|
|
|
uint8_t rdbuf[READBACK_LEN];
|
|
|
|
uint8_t *ptr;
|
2016-08-19 17:40:12 -04:00
|
|
|
|
2014-12-11 16:16:56 -05:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return CUPS_BACKEND_FAILED;
|
2015-08-20 23:13:52 -04:00
|
|
|
|
|
|
|
/* Update printjob header to reflect number of requested copies */
|
2016-10-13 08:12:20 -04:00
|
|
|
ctx->hdr2.copies = cpu_to_be16(copies);
|
2014-12-15 08:25:05 -05:00
|
|
|
|
2016-10-13 08:12:20 -04:00
|
|
|
/* Okay, let's do this thing */
|
2014-12-15 08:25:05 -05:00
|
|
|
ptr = ctx->databuf;
|
2016-08-17 21:16:15 -04:00
|
|
|
|
2014-12-11 16:16:56 -05:00
|
|
|
top:
|
2016-10-14 07:42:24 -04:00
|
|
|
if (ctx->is_s) {
|
2014-12-15 08:25:05 -05:00
|
|
|
int num;
|
2016-08-19 17:40:12 -04:00
|
|
|
|
2014-12-15 08:25:05 -05:00
|
|
|
/* 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;
|
2016-08-19 17:40:12 -04:00
|
|
|
|
2014-12-15 08:25:05 -05:00
|
|
|
/* 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;
|
2016-08-19 17:40:12 -04:00
|
|
|
|
2014-12-15 08:25:05 -05:00
|
|
|
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
|
|
|
|
}
|
2014-12-11 16:16:56 -05:00
|