2013-12-03 22:21:44 -05:00
|
|
|
/*
|
|
|
|
* DNP DS40/DS80 Photo Printer CUPS backend -- libusb-1.0 version
|
|
|
|
*
|
2015-01-05 21:39:22 -05:00
|
|
|
* (c) 2013-2015 Solomon Peachy <pizza@shaftnet.org>
|
2013-12-03 22:21:44 -05:00
|
|
|
*
|
2013-12-17 08:31:45 -05:00
|
|
|
* Development of this backend was sponsored by:
|
|
|
|
*
|
2014-06-03 20:43:31 -04:00
|
|
|
* Marco Di Antonio and [ ilgruppodigitale.com ]
|
|
|
|
* LiveLink Technology [ www.livelinktechnology.net ]
|
2013-12-17 08:31:45 -05:00
|
|
|
*
|
2013-12-03 22:21:44 -05:00
|
|
|
* The latest version of this program can be found at:
|
2013-12-10 08:54:07 -05:00
|
|
|
*
|
2013-12-03 22:21:44 -05:00
|
|
|
* http://git.shaftnet.org/cgit/selphy_print.git
|
2013-12-10 08:54:07 -05:00
|
|
|
*
|
2013-12-03 22:21:44 -05:00
|
|
|
* 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]
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2015-06-22 23:51:17 -04:00
|
|
|
//#define MATTE_STATE
|
|
|
|
//#define DNP_ONLY
|
|
|
|
#define MATTE_GLOSSY_2BUF
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
#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"
|
|
|
|
|
2015-06-19 13:47:43 -04:00
|
|
|
#define USB_VID_CITIZEN 0x1343
|
2014-02-04 19:24:20 -05:00
|
|
|
#define USB_PID_DNP_DS40 0x0003 // Also Citizen CX
|
2014-12-03 00:10:53 -05:00
|
|
|
#define USB_PID_DNP_DS80 0x0004 // Also Citizen CX-W, and Mitsubishi CP-3800DW
|
2014-02-04 19:24:20 -05:00
|
|
|
#define USB_PID_DNP_DSRX1 0x0005 // Also Citizen CY
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2015-06-19 13:47:43 -04:00
|
|
|
#define USB_VID_DNP 0x1452
|
|
|
|
#define USB_PID_DNP_DS620 0x8b01
|
|
|
|
|
2015-06-16 20:26:27 -04:00
|
|
|
//#define USB_PID_DNP_DS80D XXXX
|
|
|
|
|
2014-01-31 19:55:47 -05:00
|
|
|
//#define USB_PID_CITIZEN_CW-02 XXXXX
|
|
|
|
//#define USB_PID_CITIZEN_OP900II XXXXX
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
/* Private data stucture */
|
|
|
|
struct dnpds40_ctx {
|
|
|
|
struct libusb_device_handle *dev;
|
|
|
|
uint8_t endp_up;
|
|
|
|
uint8_t endp_down;
|
|
|
|
|
2014-02-02 14:11:17 -05:00
|
|
|
int type;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2015-06-17 22:57:31 -04:00
|
|
|
char *serno;
|
|
|
|
char *version;
|
|
|
|
|
2013-12-17 00:36:06 -05:00
|
|
|
int buf_needed;
|
2015-06-08 08:06:18 -04:00
|
|
|
int last_matte;
|
2015-06-08 23:17:12 -04:00
|
|
|
|
2015-06-13 17:20:19 -04:00
|
|
|
int ver_major;
|
|
|
|
int ver_minor;
|
2015-06-22 23:32:48 -04:00
|
|
|
int media;
|
2015-06-13 17:20:19 -04:00
|
|
|
|
2015-06-08 08:06:18 -04:00
|
|
|
uint32_t multicut;
|
2015-06-08 23:17:12 -04:00
|
|
|
int matte;
|
2015-06-13 17:20:19 -04:00
|
|
|
int cutter;
|
2015-06-22 23:32:48 -04:00
|
|
|
int can_rewind;
|
2015-06-13 17:20:19 -04:00
|
|
|
|
|
|
|
int supports_6x9;
|
|
|
|
int supports_2x6;
|
2015-06-13 22:34:54 -04:00
|
|
|
int supports_3x5x2;
|
2015-06-13 17:20:19 -04:00
|
|
|
int supports_matte;
|
2015-06-14 09:05:55 -04:00
|
|
|
int supports_fullcut;
|
|
|
|
int supports_rewind;
|
|
|
|
int supports_standby;
|
2015-06-22 18:46:26 -04:00
|
|
|
int supports_6x4_5;
|
2015-07-11 15:12:47 -04:00
|
|
|
int supports_mqty_default;
|
2014-02-02 14:17:12 -05:00
|
|
|
|
2013-12-17 00:36:06 -05:00
|
|
|
uint8_t *qty_offset;
|
2015-06-11 08:40:31 -04:00
|
|
|
uint8_t *buffctrl_offset;
|
2015-06-14 09:05:55 -04:00
|
|
|
uint8_t *multicut_offset;
|
2013-12-17 00:36:06 -05:00
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
uint8_t *databuf;
|
|
|
|
int datalen;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct dnpds40_cmd {
|
2013-12-16 20:15:48 -05:00
|
|
|
uint8_t esc; /* Fixed at ascii ESC, aka 0x1B */
|
|
|
|
uint8_t p; /* Fixed at ascii 'P' aka 0x50 */
|
2013-12-03 22:21:44 -05:00
|
|
|
uint8_t arg1[6];
|
|
|
|
uint8_t arg2[16];
|
|
|
|
uint8_t arg3[8]; /* Decimal value of arg4's length, or empty */
|
2013-12-08 07:14:27 -05:00
|
|
|
uint8_t arg4[0]; /* Extra payload if arg3 is non-empty
|
|
|
|
Doesn't have to be sent in the same URB */
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-08 07:14:27 -05:00
|
|
|
/* All unused elements are set to 0x20 (ie ascii space) */
|
2013-12-03 22:21:44 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
#define min(__x, __y) ((__x) < (__y)) ? __x : __y
|
|
|
|
|
|
|
|
static void dnpds40_build_cmd(struct dnpds40_cmd *cmd, char *arg1, char *arg2, uint32_t arg3_len)
|
|
|
|
{
|
|
|
|
memset(cmd, 0x20, sizeof(*cmd));
|
|
|
|
cmd->esc = 0x1b;
|
|
|
|
cmd->p = 0x50;
|
|
|
|
memcpy(cmd->arg1, arg1, min(strlen(arg1), sizeof(cmd->arg1)));
|
|
|
|
memcpy(cmd->arg2, arg2, min(strlen(arg2), sizeof(cmd->arg2)));
|
2013-12-16 20:15:48 -05:00
|
|
|
if (arg3_len) {
|
2014-02-11 13:41:15 -05:00
|
|
|
char buf[9];
|
2014-02-11 20:11:33 -05:00
|
|
|
snprintf(buf, sizeof(buf), "%08u", arg3_len);
|
2013-12-16 20:15:48 -05:00
|
|
|
memcpy(cmd->arg3, buf, 8);
|
|
|
|
}
|
2013-12-04 07:37:11 -05:00
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dnpds40_cleanup_string(char *start, int len)
|
|
|
|
{
|
|
|
|
char *ptr = strchr(start, 0x0d);
|
|
|
|
|
2014-02-11 20:49:08 -05:00
|
|
|
if (ptr && (ptr - start < len)) {
|
2013-12-03 22:21:44 -05:00
|
|
|
*ptr = 0x00; /* If there is a <CR>, terminate there */
|
2014-02-11 20:49:08 -05:00
|
|
|
len = ptr - start;
|
|
|
|
} else {
|
|
|
|
start[--len] = 0x00; /* force null-termination */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Trim trailing spaces */
|
|
|
|
while (len && start[len-1] == ' ') {
|
|
|
|
start[--len] = 0;
|
|
|
|
}
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
|
|
|
|
2015-06-22 23:55:48 -04:00
|
|
|
static char *dnpds40_media_types(int media)
|
2013-12-03 22:21:44 -05:00
|
|
|
{
|
2015-06-22 23:55:48 -04:00
|
|
|
switch (media) {
|
2013-12-03 22:21:44 -05:00
|
|
|
case 200: return "5x3.5 (L)";
|
|
|
|
case 210: return "5x7 (2L)";
|
|
|
|
case 300: return "6x4 (PC)";
|
|
|
|
case 310: return "6x8 (A5)";
|
|
|
|
case 400: return "6x9 (A5W)";
|
|
|
|
case 500: return "8x10";
|
|
|
|
case 510: return "8x12";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "Unknown type";
|
|
|
|
}
|
|
|
|
|
2015-06-23 19:25:23 -04:00
|
|
|
static char *dnpds40_statuses(int status)
|
2013-12-03 22:32:08 -05:00
|
|
|
{
|
2015-06-23 19:25:23 -04:00
|
|
|
switch (status) {
|
2013-12-03 22:32:08 -05:00
|
|
|
case 0: return "Idle";
|
|
|
|
case 1: return "Printing";
|
|
|
|
case 500: return "Cooling Print Head";
|
|
|
|
case 510: return "Cooling Paper Motor";
|
2015-06-17 20:55:29 -04:00
|
|
|
case 900: return "Standby Mode";
|
2013-12-03 22:32:08 -05:00
|
|
|
case 1000: return "Cover Open";
|
|
|
|
case 1010: return "No Scrap Box";
|
|
|
|
case 1100: return "Paper End";
|
|
|
|
case 1200: return "Ribbon End";
|
2014-02-02 14:11:17 -05:00
|
|
|
case 1300: return "Paper Jam";
|
|
|
|
case 1400: return "Ribbon Error";
|
2013-12-03 22:32:08 -05:00
|
|
|
case 1500: return "Paper Definition Error";
|
|
|
|
case 1600: return "Data Error";
|
|
|
|
case 2000: return "Head Voltage Error";
|
|
|
|
case 2100: return "Head Position Error";
|
|
|
|
case 2200: return "Power Supply Fan Error";
|
|
|
|
case 2300: return "Cutter Error";
|
|
|
|
case 2400: return "Pinch Roller Error";
|
|
|
|
case 2500: return "Abnormal Head Temperature";
|
|
|
|
case 2600: return "Abnormal Media Temperature";
|
|
|
|
case 2610: return "Abnormal Paper Motor Temperature";
|
|
|
|
case 2700: return "Ribbon Tension Error";
|
|
|
|
case 2800: return "RF-ID Module Error";
|
|
|
|
case 3000: return "System Error";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-02-02 14:11:17 -05:00
|
|
|
return "Unkown Error";
|
2013-12-03 22:32:08 -05:00
|
|
|
}
|
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
static int dnpds40_do_cmd(struct dnpds40_ctx *ctx,
|
2013-12-11 07:50:31 -05:00
|
|
|
struct dnpds40_cmd *cmd,
|
|
|
|
uint8_t *data, int len)
|
2013-12-10 21:53:46 -05:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = send_data(ctx->dev, ctx->endp_down,
|
|
|
|
(uint8_t*)cmd, sizeof(*cmd))))
|
|
|
|
return ret;
|
|
|
|
|
2013-12-11 07:50:31 -05:00
|
|
|
if (data && len)
|
2013-12-10 21:53:46 -05:00
|
|
|
if ((ret = send_data(ctx->dev, ctx->endp_down,
|
|
|
|
data, len)))
|
|
|
|
return ret;
|
|
|
|
|
2014-04-20 11:51:06 -04:00
|
|
|
return CUPS_BACKEND_OK;
|
2013-12-10 21:53:46 -05:00
|
|
|
}
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
static uint8_t * dnpds40_resp_cmd(struct dnpds40_ctx *ctx,
|
|
|
|
struct dnpds40_cmd *cmd,
|
|
|
|
int *len)
|
|
|
|
{
|
|
|
|
char tmp[9];
|
|
|
|
uint8_t *respbuf;
|
|
|
|
|
|
|
|
int ret, i, num = 0;
|
2013-12-08 07:14:27 -05:00
|
|
|
|
|
|
|
memset(tmp, 0, sizeof(tmp));
|
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
if ((ret = dnpds40_do_cmd(ctx, cmd, NULL, 0)))
|
2013-12-04 10:31:41 -05:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Read in the response header */
|
2013-12-21 22:55:33 -05:00
|
|
|
ret = read_data(ctx->dev, ctx->endp_up,
|
|
|
|
(uint8_t*)tmp, 8, &num);
|
|
|
|
if (ret < 0)
|
2013-12-04 10:31:41 -05:00
|
|
|
return NULL;
|
|
|
|
|
2013-12-21 22:55:33 -05:00
|
|
|
if (num != 8) {
|
|
|
|
ERROR("Short read! (%d/%d)\n", num, 8);
|
|
|
|
return NULL;
|
2013-12-11 07:50:31 -05:00
|
|
|
}
|
2013-12-04 10:31:41 -05:00
|
|
|
|
2013-12-11 07:50:31 -05:00
|
|
|
i = atoi(tmp); /* Length of payload in bytes, possibly padded */
|
2013-12-04 10:31:41 -05:00
|
|
|
respbuf = malloc(i);
|
2015-06-23 20:32:41 -04:00
|
|
|
if (!respbuf) {
|
|
|
|
ERROR("Memory allocation failure (%d bytes)!\n", i);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-12-04 10:31:41 -05:00
|
|
|
|
|
|
|
/* Read in the actual response */
|
2013-12-21 22:55:33 -05:00
|
|
|
ret = read_data(ctx->dev, ctx->endp_up,
|
|
|
|
respbuf, i, &num);
|
|
|
|
if (ret < 0) {
|
|
|
|
free(respbuf);
|
|
|
|
return NULL;
|
2013-12-11 07:50:31 -05:00
|
|
|
}
|
|
|
|
|
2013-12-21 22:55:33 -05:00
|
|
|
if (num != i) {
|
|
|
|
ERROR("Short read! (%d/%d)\n", num, i);
|
2013-12-04 10:31:41 -05:00
|
|
|
free(respbuf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*len = num;
|
|
|
|
return respbuf;
|
|
|
|
}
|
|
|
|
|
2013-12-16 20:15:48 -05:00
|
|
|
static int dnpds40_query_serno(struct libusb_device_handle *dev, uint8_t endp_up, uint8_t endp_down, char *buf, int buf_len)
|
|
|
|
{
|
|
|
|
struct dnpds40_cmd cmd;
|
|
|
|
uint8_t *resp;
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
struct dnpds40_ctx ctx = {
|
|
|
|
.dev = dev,
|
|
|
|
.endp_up = endp_up,
|
|
|
|
.endp_down = endp_down,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Get Serial Number */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "SERIAL_NUMBER", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(&ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
2014-04-20 11:51:06 -04:00
|
|
|
return CUPS_BACKEND_FAILED;
|
2013-12-16 20:15:48 -05:00
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
strncpy(buf, (char*)resp, buf_len);
|
|
|
|
buf[buf_len-1] = 0;
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
2014-04-20 11:51:06 -04:00
|
|
|
return CUPS_BACKEND_OK;
|
2013-12-16 20:15:48 -05:00
|
|
|
}
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
static void *dnpds40_init(void)
|
|
|
|
{
|
|
|
|
struct dnpds40_ctx *ctx = malloc(sizeof(struct dnpds40_ctx));
|
2015-06-23 20:32:41 -04:00
|
|
|
if (!ctx) {
|
|
|
|
ERROR("Memory allocation failure (%d bytes)!\n", (int)sizeof(struct dnpds40_ctx));
|
2013-12-03 22:21:44 -05:00
|
|
|
return NULL;
|
2015-06-23 20:32:41 -04:00
|
|
|
}
|
2013-12-03 22:21:44 -05:00
|
|
|
memset(ctx, 0, sizeof(struct dnpds40_ctx));
|
|
|
|
|
|
|
|
ctx->type = P_ANY;
|
2015-06-08 08:06:18 -04:00
|
|
|
ctx->last_matte = -1;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
2015-06-22 18:46:26 -04:00
|
|
|
#define FW_VER_CHECK(__major, __minor) \
|
|
|
|
((ctx->ver_major > (__major)) || \
|
|
|
|
(ctx->ver_major == (__major) && ctx->ver_minor >= (__minor)))
|
|
|
|
|
2013-12-10 08:54:07 -05:00
|
|
|
static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev,
|
2015-06-11 08:40:31 -04:00
|
|
|
uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
|
2013-12-03 22:21:44 -05:00
|
|
|
{
|
|
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
struct libusb_device *device;
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
|
|
|
UNUSED(jobid);
|
|
|
|
|
2013-12-10 08:54:07 -05:00
|
|
|
ctx->dev = dev;
|
2013-12-03 22:21:44 -05:00
|
|
|
ctx->endp_up = endp_up;
|
|
|
|
ctx->endp_down = endp_down;
|
|
|
|
|
|
|
|
device = libusb_get_device(dev);
|
|
|
|
libusb_get_device_descriptor(device, &desc);
|
|
|
|
|
2015-06-13 17:20:19 -04:00
|
|
|
{
|
2015-06-17 22:57:31 -04:00
|
|
|
/* Get Firmware Version */
|
2015-06-13 17:20:19 -04:00
|
|
|
struct dnpds40_cmd cmd;
|
|
|
|
uint8_t *resp;
|
|
|
|
int len = 0;
|
2015-06-22 23:32:48 -04:00
|
|
|
|
2015-06-13 17:20:19 -04:00
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "FVER", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (resp) {
|
|
|
|
char *ptr;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
2015-06-20 16:32:29 -04:00
|
|
|
ctx->version = strdup((char*) resp);
|
2015-06-13 17:20:19 -04:00
|
|
|
|
|
|
|
/* Parse version */
|
2015-07-04 11:03:52 -04:00
|
|
|
/* ptr = */ strtok((char*)resp, " .");
|
2015-06-13 17:20:19 -04:00
|
|
|
ptr = strtok(NULL, ".");
|
|
|
|
ctx->ver_major = atoi(ptr);
|
|
|
|
ptr = strtok(NULL, ".");
|
|
|
|
ctx->ver_minor = atoi(ptr);
|
2015-06-20 16:32:29 -04:00
|
|
|
free(resp);
|
2015-06-17 22:57:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get Serial Number */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "SERIAL_NUMBER", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (resp) {
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
ctx->serno = (char*) resp;
|
|
|
|
/* Do NOT free resp! */
|
2015-06-13 17:20:19 -04:00
|
|
|
}
|
2015-06-22 23:32:48 -04:00
|
|
|
|
|
|
|
/* Query Media Info */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "MEDIA", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (resp) {
|
|
|
|
char tmp[4];
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
memcpy(tmp, resp + 4, 3);
|
|
|
|
tmp[3] = 0;
|
|
|
|
|
|
|
|
ctx->media = atoi(tmp);
|
|
|
|
|
|
|
|
/* Subtract out the "mark" type */
|
|
|
|
if (ctx->media & 1)
|
|
|
|
ctx->media--;
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
}
|
2015-06-13 17:20:19 -04:00
|
|
|
}
|
|
|
|
|
2015-06-17 22:57:31 -04:00
|
|
|
#ifdef DNP_ONLY
|
|
|
|
/* Only allow DNP printers to work. Rebadged versions should not. */
|
|
|
|
|
|
|
|
{ /* Validate USB Vendor String is "Dai Nippon Printing" */
|
|
|
|
char buf[256];
|
|
|
|
buf[0] = 0;
|
|
|
|
libusb_get_string_descriptor_ascii(dev, desc->iManufacturer, (unsigned char*)buf, STR_LEN_MAX);
|
|
|
|
sanitize_string(buf);
|
|
|
|
if (strncmp(buf, "Dai", 3))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-06-13 17:20:19 -04:00
|
|
|
/* Per-printer options */
|
|
|
|
switch (desc.idProduct) {
|
|
|
|
case USB_PID_DNP_DS40:
|
2013-12-03 22:21:44 -05:00
|
|
|
ctx->type = P_DNP_DS40;
|
2015-06-13 17:20:19 -04:00
|
|
|
ctx->supports_6x9 = 1;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(1,30))
|
2015-06-13 17:20:19 -04:00
|
|
|
ctx->supports_matte = 1;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(1,40))
|
2015-06-13 17:20:19 -04:00
|
|
|
ctx->supports_2x6 = 1;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(1,50))
|
2015-06-13 22:34:54 -04:00
|
|
|
ctx->supports_3x5x2 = 1;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(1,51))
|
2015-06-14 09:05:55 -04:00
|
|
|
ctx->supports_fullcut = 1;
|
2015-06-13 17:20:19 -04:00
|
|
|
break;
|
|
|
|
case USB_PID_DNP_DS80:
|
2013-12-03 22:21:44 -05:00
|
|
|
ctx->type = P_DNP_DS80;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(1,30))
|
2015-06-13 17:20:19 -04:00
|
|
|
ctx->supports_matte = 1;
|
|
|
|
break;
|
|
|
|
case USB_PID_DNP_DSRX1:
|
|
|
|
ctx->type = P_DNP_DSRX1;
|
|
|
|
ctx->supports_matte = 1;
|
2015-07-11 15:12:47 -04:00
|
|
|
ctx->supports_mqty_default = 1; // 1.10 does. Maybe older too?
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(1,10))
|
2015-06-13 17:20:19 -04:00
|
|
|
ctx->supports_2x6 = 1;
|
|
|
|
break;
|
|
|
|
case USB_PID_DNP_DS620:
|
|
|
|
ctx->type = P_DNP_DS620;
|
|
|
|
ctx->supports_matte = 1;
|
|
|
|
ctx->supports_2x6 = 1;
|
2015-06-14 09:05:55 -04:00
|
|
|
ctx->supports_fullcut = 1;
|
2015-07-11 15:12:47 -04:00
|
|
|
ctx->supports_mqty_default = 1;
|
|
|
|
ctx->supports_rewind = 1;
|
2015-06-14 09:05:55 -04:00
|
|
|
ctx->supports_standby = 1;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(0,30))
|
2015-06-13 22:34:54 -04:00
|
|
|
ctx->supports_3x5x2 = 1;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (FW_VER_CHECK(1,10))
|
|
|
|
ctx->supports_6x9 = ctx->supports_6x4_5 = 1;
|
2015-06-13 17:20:19 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERROR("Unknown USB PID...\n");
|
|
|
|
return;
|
|
|
|
}
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dnpds40_teardown(void *vctx) {
|
|
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ctx->databuf)
|
|
|
|
free(ctx->databuf);
|
2015-06-17 22:57:31 -04:00
|
|
|
if (ctx->serno)
|
|
|
|
free(ctx->serno);
|
|
|
|
if (ctx->version)
|
|
|
|
free(ctx->version);
|
2013-12-03 22:21:44 -05:00
|
|
|
free(ctx);
|
|
|
|
}
|
|
|
|
|
2015-06-23 19:40:35 -04:00
|
|
|
#define MAX_PRINTJOB_LEN (((2560*7536+1024+54))*3+1024) /* Worst-case */
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
static int dnpds40_read_parse(void *vctx, int data_fd) {
|
|
|
|
struct dnpds40_ctx *ctx = vctx;
|
2014-02-11 13:41:15 -05:00
|
|
|
int run = 1;
|
2013-12-17 00:36:06 -05:00
|
|
|
char buf[9] = { 0 };
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2015-06-22 23:32:48 -04:00
|
|
|
uint32_t matte, dpi, cutter;
|
2014-02-02 14:11:17 -05:00
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
if (!ctx)
|
2014-04-20 11:51:06 -04:00
|
|
|
return CUPS_BACKEND_FAILED;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2014-01-21 20:34:00 -05:00
|
|
|
if (ctx->databuf) {
|
2014-01-19 19:23:24 -05:00
|
|
|
free(ctx->databuf);
|
2014-01-21 20:34:00 -05:00
|
|
|
ctx->databuf = NULL;
|
|
|
|
}
|
2014-01-19 19:23:24 -05:00
|
|
|
|
2014-02-02 14:11:17 -05:00
|
|
|
/* There's no way to figure out the total job length in advance, we
|
|
|
|
have to parse the stream until we get to the image plane data,
|
|
|
|
and even then the stream can contain arbitrary commands later.
|
|
|
|
|
|
|
|
So instead, we allocate a buffer of the maximum possible length,
|
|
|
|
then parse the incoming stream until we hit the START command at
|
|
|
|
the end of the job.
|
|
|
|
*/
|
|
|
|
|
2013-12-17 00:36:06 -05:00
|
|
|
ctx->datalen = 0;
|
2013-12-03 22:21:44 -05:00
|
|
|
ctx->databuf = malloc(MAX_PRINTJOB_LEN);
|
|
|
|
if (!ctx->databuf) {
|
|
|
|
ERROR("Memory allocation failure!\n");
|
2014-04-20 11:51:06 -04:00
|
|
|
return CUPS_BACKEND_CANCEL;
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
|
|
|
|
2015-05-31 18:00:39 -04:00
|
|
|
/* Clear everything out */
|
|
|
|
matte = 0;
|
|
|
|
dpi = 0;
|
2015-06-13 17:20:19 -04:00
|
|
|
cutter = 0;
|
2015-06-22 23:32:48 -04:00
|
|
|
ctx->multicut = 0;
|
2015-06-14 09:05:55 -04:00
|
|
|
ctx->buffctrl_offset = ctx->qty_offset = ctx->multicut_offset = 0;
|
2015-05-31 18:00:39 -04:00
|
|
|
|
2014-01-19 19:23:24 -05:00
|
|
|
while (run) {
|
2014-02-11 13:41:15 -05:00
|
|
|
int remain, i, j;
|
2014-02-02 14:11:17 -05:00
|
|
|
/* Read in command header */
|
2013-12-17 00:36:06 -05:00
|
|
|
i = read(data_fd, ctx->databuf + ctx->datalen,
|
|
|
|
sizeof(struct dnpds40_cmd));
|
|
|
|
if (i < 0)
|
|
|
|
return i;
|
|
|
|
if (i == 0)
|
|
|
|
break;
|
|
|
|
if (i < (int) sizeof(struct dnpds40_cmd))
|
2014-04-20 11:51:06 -04:00
|
|
|
return CUPS_BACKEND_CANCEL;
|
2013-12-17 00:36:06 -05:00
|
|
|
|
|
|
|
if (ctx->databuf[ctx->datalen + 0] != 0x1b ||
|
|
|
|
ctx->databuf[ctx->datalen + 1] != 0x50) {
|
|
|
|
ERROR("Unrecognized header data format @%d!\n", ctx->datalen);
|
2014-04-20 11:51:06 -04:00
|
|
|
return CUPS_BACKEND_CANCEL;
|
2013-12-17 00:36:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse out length of data chunk, if any */
|
|
|
|
memcpy(buf, ctx->databuf + ctx->datalen + 24, 8);
|
|
|
|
j = atoi(buf);
|
|
|
|
|
2013-12-19 07:56:16 -05:00
|
|
|
/* Read in data chunk as quickly as possible */
|
|
|
|
remain = j;
|
|
|
|
while (remain > 0) {
|
|
|
|
i = read(data_fd, ctx->databuf + ctx->datalen + sizeof(struct dnpds40_cmd),
|
|
|
|
remain);
|
2015-06-23 19:40:35 -04:00
|
|
|
if (i < 0) {
|
|
|
|
ERROR("Data Read Error: %d (%d/%d @%d)\n", i, remain, j, ctx->datalen);
|
2013-12-19 07:56:16 -05:00
|
|
|
return i;
|
2015-06-23 19:40:35 -04:00
|
|
|
}
|
2013-12-19 07:56:16 -05:00
|
|
|
if (i == 0)
|
|
|
|
return 1;
|
|
|
|
ctx->datalen += i;
|
|
|
|
remain -= i;
|
|
|
|
}
|
|
|
|
ctx->datalen -= j; /* Back it off */
|
2013-12-17 00:36:06 -05:00
|
|
|
|
|
|
|
/* Check for some offsets */
|
|
|
|
if(!memcmp("CNTRL QTY", ctx->databuf + ctx->datalen+2, 9)) {
|
|
|
|
ctx->qty_offset = ctx->databuf + ctx->datalen + 32;
|
|
|
|
}
|
2015-06-13 17:20:19 -04:00
|
|
|
if(!memcmp("CNTRL CUTTER", ctx->databuf + ctx->datalen+2, 12)) {
|
|
|
|
memcpy(buf, ctx->databuf + ctx->datalen + 32, 8);
|
|
|
|
cutter = atoi(buf);
|
|
|
|
}
|
2015-06-11 08:40:31 -04:00
|
|
|
if(!memcmp("CNTRL BUFFCNTRL", ctx->databuf + ctx->datalen+2, 15)) {
|
2015-06-13 17:20:19 -04:00
|
|
|
/* If the printer doesn't support matte, it doesn't
|
|
|
|
support buffcntrl. strip it from the stream */
|
|
|
|
if (ctx->supports_matte) {
|
|
|
|
ctx->buffctrl_offset = ctx->databuf + ctx->datalen + 32;
|
|
|
|
} else {
|
|
|
|
WARNING("Printer FW does not support BUFFCNTRL, please update\n");
|
|
|
|
continue;
|
|
|
|
}
|
2015-06-11 08:40:31 -04:00
|
|
|
}
|
2014-02-02 14:11:17 -05:00
|
|
|
if(!memcmp("CNTRL OVERCOAT", ctx->databuf + ctx->datalen+2, 14)) {
|
2015-06-13 17:20:19 -04:00
|
|
|
/* If the printer doesn't support matte, it doesn't
|
|
|
|
support buffcntrl. strip it from the stream */
|
|
|
|
if (ctx->supports_matte) {
|
|
|
|
memcpy(buf, ctx->databuf + ctx->datalen + 32, 8);
|
|
|
|
matte = atoi(buf);
|
|
|
|
} else {
|
|
|
|
WARNING("Printer FW does not support matte prints, please update\n");
|
|
|
|
continue;
|
|
|
|
}
|
2014-02-02 14:11:17 -05:00
|
|
|
}
|
2015-06-20 16:32:29 -04:00
|
|
|
if(!memcmp("IMAGE MULTICUT", ctx->databuf + ctx->datalen+2, 14)) {
|
2015-06-14 09:05:55 -04:00
|
|
|
ctx->multicut_offset = ctx->databuf + ctx->datalen + 32;
|
2014-02-02 14:11:17 -05:00
|
|
|
memcpy(buf, ctx->databuf + ctx->datalen + 32, 8);
|
2015-06-22 23:32:48 -04:00
|
|
|
ctx->multicut = atoi(buf);
|
2014-02-02 14:11:17 -05:00
|
|
|
}
|
2015-06-14 09:05:55 -04:00
|
|
|
if(!memcmp("CNTRL FULL_CUTTER_SET", ctx->databuf + ctx->datalen+2, 21)) {
|
|
|
|
if (!ctx->supports_fullcut) {
|
|
|
|
WARNING("Printer FW does not support cutter control, please update!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2015-05-31 18:00:39 -04:00
|
|
|
if(!memcmp("IMAGE YPLANE", ctx->databuf + ctx->datalen + 2, 12)) {
|
2015-06-13 12:25:45 -04:00
|
|
|
uint32_t y_ppm; /* Pixels Per Meter */
|
2015-05-31 18:00:39 -04:00
|
|
|
|
2015-06-13 12:25:45 -04:00
|
|
|
/* Validate vertical resolution */
|
|
|
|
memcpy(&y_ppm, ctx->databuf + ctx->datalen + 32 + 42, sizeof(y_ppm));
|
|
|
|
y_ppm = le32_to_cpu(y_ppm);
|
2014-02-02 14:11:17 -05:00
|
|
|
|
2015-06-13 12:25:45 -04:00
|
|
|
switch (y_ppm) {
|
2015-05-31 18:00:39 -04:00
|
|
|
case 11808:
|
|
|
|
dpi = 300;
|
|
|
|
break;
|
|
|
|
case 23615:
|
2014-02-02 14:11:17 -05:00
|
|
|
dpi = 600;
|
2015-05-31 18:00:39 -04:00
|
|
|
break;
|
|
|
|
default:
|
2015-06-13 12:25:45 -04:00
|
|
|
ERROR("Unrecognized printjob resolution (%d ppm)\n", y_ppm);
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Validate horizontal size */
|
|
|
|
memcpy(&y_ppm, ctx->databuf + ctx->datalen + 32 + 18, sizeof(y_ppm));
|
|
|
|
y_ppm = le32_to_cpu(y_ppm);
|
|
|
|
if (ctx->type == P_DNP_DS80) {
|
|
|
|
if (y_ppm != 2560) {
|
|
|
|
ERROR("Incorrect horizontal resolution (%d), aborting!\n", y_ppm);
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (y_ppm != 1920) {
|
|
|
|
ERROR("Incorrect horizontal resolution (%d), aborting!\n", y_ppm);
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
2013-12-17 00:36:06 -05:00
|
|
|
}
|
|
|
|
}
|
2013-12-10 21:53:46 -05:00
|
|
|
|
2014-01-19 19:23:24 -05:00
|
|
|
/* This is the last block.. */
|
|
|
|
if(!memcmp("CNTRL START", ctx->databuf + ctx->datalen + 2, 11))
|
|
|
|
run = 0;
|
|
|
|
|
2013-12-17 00:36:06 -05:00
|
|
|
/* Add in the size of this chunk */
|
|
|
|
ctx->datalen += sizeof(struct dnpds40_cmd) + j;
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
2014-02-02 14:17:12 -05:00
|
|
|
|
2015-06-13 12:06:33 -04:00
|
|
|
if (!ctx->datalen)
|
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
|
2015-05-31 18:00:39 -04:00
|
|
|
/* Figure out the number of buffers we need. Most only need one. */
|
2015-06-22 23:32:48 -04:00
|
|
|
if (ctx->multicut) {
|
2015-06-13 12:01:08 -04:00
|
|
|
ctx->buf_needed = 1;
|
|
|
|
|
|
|
|
if (dpi == 600) {
|
2015-06-16 18:47:12 -04:00
|
|
|
if (ctx->type == P_DNP_DS620) {
|
2015-06-22 23:32:48 -04:00
|
|
|
if (ctx->multicut == 5 || // 6x9
|
2015-06-22 23:48:30 -04:00
|
|
|
ctx->multicut == 31) // 6x4.5*2
|
2015-06-22 18:46:26 -04:00
|
|
|
ctx->buf_needed = 2;
|
2015-06-16 18:47:12 -04:00
|
|
|
} else if (ctx->type == P_DNP_DS80) { /* DS80/CX-W */
|
2015-06-22 23:32:48 -04:00
|
|
|
if (matte && (ctx->multicut == 21 || // A4 length
|
|
|
|
ctx->multicut == 20 || // 8x4*3
|
|
|
|
ctx->multicut == 19 || // 8x8+8x4
|
|
|
|
ctx->multicut == 15 || // 8x6*2
|
|
|
|
ctx->multicut == 7)) // 8x12
|
2015-06-13 12:01:08 -04:00
|
|
|
ctx->buf_needed = 2;
|
2015-06-22 23:48:30 -04:00
|
|
|
} else { /* DS40/CX/RX1/CY/etc */
|
2015-06-22 23:32:48 -04:00
|
|
|
if (ctx->multicut == 4 || // 6x8
|
|
|
|
ctx->multicut == 5 || // 6x9
|
|
|
|
ctx->multicut == 12) // 6x4*2
|
2015-06-13 12:01:08 -04:00
|
|
|
ctx->buf_needed = 2;
|
2015-06-22 23:32:48 -04:00
|
|
|
else if (matte && ctx->multicut == 3) // 5x7
|
2015-06-13 12:01:08 -04:00
|
|
|
ctx->buf_needed = 2;
|
|
|
|
}
|
2015-05-31 18:00:39 -04:00
|
|
|
}
|
2015-06-13 12:01:08 -04:00
|
|
|
} else {
|
|
|
|
WARNING("Missing or illegal MULTICUT command, can't validate print job against loaded media!\n");
|
|
|
|
if (dpi == 300)
|
|
|
|
ctx->buf_needed = 1;
|
|
|
|
else
|
|
|
|
ctx->buf_needed = 2;
|
2015-05-31 18:00:39 -04:00
|
|
|
}
|
|
|
|
|
2015-06-08 23:17:12 -04:00
|
|
|
ctx->matte = (int)matte;
|
2015-06-13 17:20:19 -04:00
|
|
|
ctx->cutter = cutter;
|
2015-06-22 23:32:48 -04:00
|
|
|
ctx->can_rewind = 0;
|
2015-05-31 18:00:39 -04:00
|
|
|
|
2015-06-13 17:20:19 -04:00
|
|
|
DEBUG("dpi %u matte %u mcut %u cutter %d, bufs %d\n",
|
2015-06-22 23:32:48 -04:00
|
|
|
dpi, matte, ctx->multicut, cutter, ctx->buf_needed);
|
2013-12-16 23:01:30 -05:00
|
|
|
|
2015-06-22 23:32:48 -04:00
|
|
|
/* Sanity-check printjob type vs loaded media */
|
2015-06-13 12:01:08 -04:00
|
|
|
if (ctx->multicut) {
|
2015-06-22 23:32:48 -04:00
|
|
|
switch(ctx->media) {
|
2015-06-08 08:06:18 -04:00
|
|
|
case 200: //"5x3.5 (L)"
|
|
|
|
if (ctx->multicut != 1) {
|
2015-06-22 23:32:48 -04:00
|
|
|
ERROR("Incorrect media for job loaded (%d vs %d)\n", ctx->media, ctx->multicut);
|
2015-06-08 08:06:18 -04:00
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 210: //"5x7 (2L)"
|
2015-06-22 23:32:48 -04:00
|
|
|
ctx->can_rewind = 1;
|
2015-06-22 18:46:26 -04:00
|
|
|
if (ctx->multicut != 1 && ctx->multicut != 3 &&
|
|
|
|
ctx->multicut != 22 && ctx->multicut != 29) {
|
2015-06-22 23:32:48 -04:00
|
|
|
ERROR("Incorrect media for job loaded (%d vs %d)\n", ctx->media, ctx->multicut);
|
2015-06-08 08:06:18 -04:00
|
|
|
return CUPS_BACKEND_CANCEL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 300: //"6x4 (PC)"
|
2015-06-13 17:20:19 -04:00
|
|
|
if (ctx->multicut != 2) {
|
2015-06-22 23:32:48 -04:00
|
|
|
ERROR("Incorrect media for job loaded (%d vs %d)\n", ctx->media, ctx->multicut);
|
|