5035 lines
128 KiB
C
5035 lines
128 KiB
C
/*
|
|
* Citizen / DNP Photo Printer CUPS backend
|
|
*
|
|
* (c) 2013-2024 Solomon Peachy <pizza@shaftnet.org>
|
|
*
|
|
* Development of this backend was sponsored by:
|
|
*
|
|
* Marco Di Antonio and [ ilgruppodigitale.com ]
|
|
* LiveLink Technology [ www.livelinktechnology.net ]
|
|
* A generous benefactor who wishes to remain anonymous
|
|
*
|
|
* The latest version of this program can be found at:
|
|
*
|
|
* https://git.shaftnet.org/gitea/slp/selphy_print.git
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 3 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0+
|
|
*
|
|
*/
|
|
|
|
//#define DNP_ONLY
|
|
//#define CITIZEN_ONLY
|
|
|
|
/* Enables caching of last print type to speed up
|
|
job pipelining. Without this we always have to
|
|
assume the worst */
|
|
//#define STATE_DIR "/tmp"
|
|
|
|
#define BACKEND dnpds40_backend
|
|
|
|
#include "backend_common.h"
|
|
|
|
#include <time.h>
|
|
|
|
/* Private data structure */
|
|
struct dnpds40_printjob {
|
|
struct dyesub_job_common common;
|
|
|
|
uint8_t *databuf;
|
|
int datalen;
|
|
|
|
uint32_t dpi;
|
|
int matte;
|
|
int cutter;
|
|
uint32_t multicut;
|
|
int fullcut;
|
|
int printspeed;
|
|
int can_rewind;
|
|
int buffcntrl;
|
|
|
|
int rows;
|
|
int is_pano;
|
|
|
|
int buf_needed;
|
|
int cut_paper;
|
|
};
|
|
|
|
#define MAX_PRINTJOB_LEN (((ctx->native_width*ctx->max_height+1024+54+10))*3+1024) /* Worst-case, YMC */
|
|
#define MAX_PANOPRINTJOB_LEN ((((ctx->native_width*ctx->max_height+1024+54+10))*3+1024)*3) /* Worst-case, YMC */
|
|
|
|
#define MFG_DNP 0
|
|
#define MFG_CITIZEN 1
|
|
#define MFG_MITSUBISHI 2
|
|
#define MFG_FUJIFILM 3
|
|
#define MFG_OTHER 4
|
|
|
|
#define USE_PANODATA_FILES
|
|
|
|
struct dnpds40_ctx {
|
|
struct dyesub_connection *conn;
|
|
|
|
int mfg; /* see MFG_* */
|
|
|
|
/* Version and whatnot */
|
|
char *serno;
|
|
char *version;
|
|
int ver_major;
|
|
int ver_minor;
|
|
|
|
int flash_mode;
|
|
|
|
/* State */
|
|
uint32_t media;
|
|
uint32_t media_subtype;
|
|
|
|
char media_text[32];
|
|
uint32_t duplex_media;
|
|
int duplex_media_status;
|
|
uint16_t media_count_new;
|
|
|
|
uint32_t last_multicut;
|
|
int last_matte;
|
|
int partialmatte;
|
|
|
|
int media_sticker;
|
|
int mediaoffset;
|
|
int correct_count;
|
|
int needs_mlot;
|
|
int pano;
|
|
|
|
struct marker marker[2];
|
|
int marker_count;
|
|
|
|
/* Printer capabilities */
|
|
uint32_t native_width;
|
|
uint32_t max_height;
|
|
int supports_600dpi;
|
|
int supports_6x9;
|
|
int supports_2x6;
|
|
int supports_3x5x2;
|
|
int supports_a4x6;
|
|
int supports_45_34;
|
|
int supports_matte;
|
|
int supports_finematte;
|
|
int supports_luster;
|
|
int supports_advmatte;
|
|
int supports_fullcut;
|
|
int supports_rewind;
|
|
int supports_standby;
|
|
int supports_keepmode;
|
|
int supports_6x4_5;
|
|
int supports_mqty_default;
|
|
int supports_iserial; /* 0 == no, 1 == configurable, 2 == always enabled */
|
|
int supports_6x6;
|
|
int supports_5x5;
|
|
int supports_counterp;
|
|
int supports_adv_fullcut;
|
|
int supports_mediaoffset;
|
|
int supports_ctrld_ext;
|
|
int supports_media_ext;
|
|
int supports_printspeed;
|
|
int supports_lowspeed;
|
|
int supports_highdensity;
|
|
int supports_systime;
|
|
int supports_mediaclassrfid;
|
|
int supports_gamma;
|
|
int supports_pano;
|
|
int supports_contpano;
|
|
};
|
|
|
|
struct dnpds40_cmd {
|
|
uint8_t esc; /* Fixed at ascii ESC, aka 0x1B */
|
|
uint8_t p; /* Fixed at ascii 'P' aka 0x50 */
|
|
uint8_t arg1[6];
|
|
uint8_t arg2[16];
|
|
uint8_t arg3[8]; /* Decimal value of arg4's length, or empty */
|
|
uint8_t arg4[]; /* Extra payload if arg3 is non-empty
|
|
Doesn't have to be sent in the same URB */
|
|
|
|
/* All unused elements are set to 0x20 (ie ascii space) */
|
|
};
|
|
|
|
#define MULTICUT_5x3_5 1
|
|
#define MULTICUT_6x4 2
|
|
#define MULTICUT_5x7 3
|
|
#define MULTICUT_6x8 4
|
|
#define MULTICUT_6x9 5
|
|
#define MULTICUT_8x10 6
|
|
#define MULTICUT_8x12 7
|
|
#define MULTICUT_8x4 8
|
|
#define MULTICUT_8x5 9
|
|
#define MULTICUT_8x6 10
|
|
#define MULTICUT_8x8 11
|
|
#define MULTICUT_6x4X2 12
|
|
#define MULTICUT_8x4X2 13
|
|
#define MULTICUT_8x5X2 14
|
|
#define MULTICUT_8x6X2 15
|
|
#define MULTICUT_8x5_8x4 16
|
|
#define MULTICUT_8x6_8x4 17
|
|
#define MULTICUT_8x6_8x5 18
|
|
#define MULTICUT_8x8_8x4 19
|
|
#define MULTICUT_8x4X3 20
|
|
#define MULTICUT_8xA4LEN 21
|
|
#define MULTICUT_5x3_5X2 22
|
|
#define MULTICUT_6x6 27
|
|
#define MULTICUT_5x5 29
|
|
#define MULTICUT_6x4_5 30
|
|
#define MULTICUT_6x4_5X2 31
|
|
#define MULTICUT_8x7 32
|
|
#define MULTICUT_8x9 33
|
|
#define MULTICUT_A5 34
|
|
#define MULTICUT_A5X2 35
|
|
#define MULTICUT_A4x4 36
|
|
#define MULTICUT_A4x5 37
|
|
#define MULTICUT_A4x6 38
|
|
#define MULTICUT_A4x8 39
|
|
#define MULTICUT_A4x10 40
|
|
#define MULTICUT_A4 41
|
|
#define MULTICUT_A4x5X2 43
|
|
|
|
#define MULTICUT_4x4 47
|
|
#define MULTICUT_4x6 48
|
|
#define MULTICUT_4x8 49
|
|
#define MULTICUT_4_5x4_5 50
|
|
#define MULTICUT_4_5x6 51
|
|
#define MULTICUT_4_5x8 52
|
|
|
|
#define MULTICUT_4x3 53
|
|
#define MULTICUT_4x4_5 54
|
|
#define MULTICUT_4_5x3 55
|
|
// #define MULTICUT_??? 56 // XXX WTF is missing?
|
|
#define MULTICUT_4_5x4 57
|
|
|
|
#if 0
|
|
// XXX do these exist? Or are they just the larger print cut in two?
|
|
// #define MULTICUT_4x3X2
|
|
// #define MULTICUT_4x4X2
|
|
// #define MULTICUT_4_5x3X2
|
|
// #define MULTICUT_4_5x4X2
|
|
#endif
|
|
|
|
#define MULTICUT_S_SIMPLEX 100
|
|
#define MULTICUT_S_FRONT 200
|
|
#define MULTICUT_S_BACK 300
|
|
|
|
#define MULTICUT_S_8x10 6
|
|
#define MULTICUT_S_8x12 7
|
|
#define MULTICUT_S_8x4 8
|
|
#define MULTICUT_S_8x5 9
|
|
#define MULTICUT_S_8x6 10
|
|
#define MULTICUT_S_8x8 11
|
|
#define MULTICUT_S_8x4X2 13
|
|
#define MULTICUT_S_8x5X2 14
|
|
#define MULTICUT_S_8x6X2 15
|
|
#define MULTICUT_S_8x10_5 25
|
|
#define MULTICUT_S_8x10_75 26
|
|
#define MULTICUT_S_8x4X3 28 // different than roll type.
|
|
|
|
/* Special panorama sizes */
|
|
#define MULTICUT_PANO_6x14 9000
|
|
#define MULTICUT_PANO_6x20 9001
|
|
#define MULTICUT_PANO_8x18 9010
|
|
#define MULTICUT_PANO_8x26 9011
|
|
#define MULTICUT_PANO_8x22 9012
|
|
#define MULTICUT_PANO_8x32 9013
|
|
//#define MULTICUT_PANO_A4x21 9014
|
|
//#define MULTICUT_PANO_A4x31 9015
|
|
|
|
#define MULTICUT_PANO_6x16 9020
|
|
#define MULTICUT_PANO_6x24 9021
|
|
#define MULTICUT_PANO_8x20 9030
|
|
#define MULTICUT_PANO_8x30 9031
|
|
#define MULTICUT_PANO_8x24 9032
|
|
#define MULTICUT_PANO_8x36 9033
|
|
#define MULTICUT_PANO_A4x2 9034
|
|
#define MULTICUT_PANO_A4x3 9035
|
|
|
|
#ifndef min
|
|
#define min(__x, __y) ((__x) < (__y)) ? __x : __y
|
|
#endif
|
|
|
|
/* Legacy spool file support */
|
|
static int legacy_cw01_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
|
|
static int legacy_dnp_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
|
|
static int legacy_dnp620_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
|
|
static int legacy_dnp820_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
|
|
static int legacy_qw410_read_parse(struct dnpds40_printjob *job, int data_fd, int read_data);
|
|
|
|
static void dnpds40_cleanup_job(const void *vjob);
|
|
static int dnpds40_query_markers(void *vctx, struct marker **markers, int *count);
|
|
|
|
/* Panorama crap */
|
|
#ifdef USE_PANODATA_FILES
|
|
#define PANODATA_DS620 "LUTData_0010.csv"
|
|
#define PANODATA_DS820 "LUTData820_0010.csv"
|
|
|
|
static struct dnp_panodata *dnp_read_panodata(const char *filename)
|
|
{
|
|
struct dnp_panodata *pano = NULL;
|
|
|
|
FILE *f;
|
|
char buf[4096];
|
|
int line;
|
|
char *ptr;
|
|
|
|
const char *delim = " ,\t\n";
|
|
|
|
if (!filename)
|
|
return NULL;
|
|
pano = malloc(sizeof(struct dnp_panodata));
|
|
if (!pano)
|
|
return NULL;
|
|
|
|
pano->elements = 0;
|
|
|
|
snprintf(buf, sizeof(buf), "%s/%s", corrtable_path, filename);
|
|
|
|
f = fopen(buf, "r");
|
|
if (!f)
|
|
goto done_free;
|
|
|
|
/* Skip the first four lines */
|
|
for (line = 0; line < DNP_PANO_MAXROWS ; line++) {
|
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
|
break;
|
|
|
|
ptr = strtok(buf, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
|
|
if (strcmp("REC", ptr))
|
|
continue;
|
|
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
pano->rows[pano->elements].start_row = strtol(ptr, NULL, 10);
|
|
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
pano->rows[pano->elements].rhYMC[0] = strtod(ptr,NULL);
|
|
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
pano->rows[pano->elements].rhYMC[1] = strtod(ptr,NULL);
|
|
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
pano->rows[pano->elements].rhYMC[2] = strtod(ptr,NULL);
|
|
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
pano->rows[pano->elements].lhYMC[0] = strtod(ptr,NULL);
|
|
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
pano->rows[pano->elements].lhYMC[1] = strtod(ptr,NULL);
|
|
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
pano->rows[pano->elements].lhYMC[2] = strtod(ptr,NULL);
|
|
|
|
pano->elements++;
|
|
}
|
|
if (pano->elements < 20)
|
|
goto abort;
|
|
|
|
fclose(f);
|
|
|
|
#if 0
|
|
f = fopen("/tmp/panodata.h", "w");
|
|
fprintf(f, "static struct dnp_panodata panodata = {\n");
|
|
fprintf(f, "\t.elements = %d,\n", pano->elements);
|
|
for (int i = 0 ; i < pano->elements ; i++) {
|
|
fprintf(f, "\t.rows[%2u] = { %4u, { %1.10f, %1.10f, %1.10f }, { %1.10f, %1.10f, %1.10f} },\n",
|
|
i,
|
|
pano->rows[i].start_row,
|
|
pano->rows[i].rhYMC[0],
|
|
pano->rows[i].rhYMC[1],
|
|
pano->rows[i].rhYMC[2],
|
|
pano->rows[i].lhYMC[0],
|
|
pano->rows[i].lhYMC[1],
|
|
pano->rows[i].lhYMC[2]);
|
|
}
|
|
fprintf(f, "};\n");
|
|
fclose(f);
|
|
exit(1);
|
|
#endif
|
|
|
|
return pano;
|
|
|
|
abort:
|
|
fclose(f);
|
|
|
|
done_free:
|
|
free(pano);
|
|
return NULL;
|
|
}
|
|
|
|
void dnp_free_panodata (struct dnp_panodata *pano)
|
|
{
|
|
if (pano)
|
|
free(pano);
|
|
}
|
|
#else /* !USE_PANODATA_FILES */
|
|
|
|
#include "backend_panodata.h"
|
|
|
|
#define dnp_read_panodata(__fname) &panodata
|
|
#define dnp_free_panodata(__pano)
|
|
|
|
#endif
|
|
|
|
#define PROCESS_PIXEL(__corr) \
|
|
outdata[r * cols + c] = 255 - ((255 - (double)indata[r * cols + c]) * (__corr))
|
|
|
|
static void dnp_applypano_plane(const struct dnp_panodata *pano,
|
|
const uint8_t *indata, uint8_t *outdata,
|
|
uint16_t rows, const uint16_t cols, const uint16_t pad_rows,
|
|
const uint16_t dpi, int overlap, const int plane,
|
|
const int rh, const int lh)
|
|
{
|
|
uint16_t r, c;
|
|
|
|
/* Fill the start margin with white */
|
|
if (pad_rows) {
|
|
memset(outdata, 0xff, pad_rows * cols);
|
|
outdata += pad_rows * cols;
|
|
}
|
|
|
|
for (r = 0 ; r < rows; r++) {
|
|
const struct panodata_row *lhc = pano ? &pano->rows[0] : NULL;
|
|
const struct panodata_row *rhc = pano ? &pano->rows[pano->elements - 1] : NULL;
|
|
|
|
if (rh && r < overlap) {
|
|
/* Row is in RH overlap portion of panel */
|
|
int i;
|
|
int row = (overlap - r);
|
|
if (dpi == 600)
|
|
row /= 2;
|
|
for (i = 0 ; i < pano->elements-1 ; i++) {
|
|
if (row >= pano->rows[i].start_row && row < pano->rows[i+1].start_row)
|
|
break;
|
|
}
|
|
rhc = &pano->rows[i];
|
|
} else if (lh && (rows -r) < overlap) {
|
|
/* Row is in LH overlap portion of panel */
|
|
int i;
|
|
int row = (rows -r);
|
|
if (dpi == 600)
|
|
row /= 2;
|
|
|
|
for (i = 0 ; i < pano->elements-1 ; i++) {
|
|
if (row >= pano->rows[i].start_row && row < pano->rows[i+1].start_row)
|
|
break;
|
|
}
|
|
lhc = &pano->rows[i];
|
|
} else {
|
|
/* No processing on row, pass through as-is */
|
|
memcpy(&outdata[r * cols], &indata[r * cols], cols);
|
|
continue;
|
|
}
|
|
|
|
if (rh && rhc && r < overlap) {
|
|
/* Fade RH row */
|
|
if (plane == 'Y')
|
|
for (c = 0 ; c < cols ; c++) {
|
|
PROCESS_PIXEL(rhc->rhYMC[0]);
|
|
}
|
|
else if (plane == 'M') {
|
|
for (c = 0 ; c < cols ; c++) {
|
|
PROCESS_PIXEL(rhc->rhYMC[1]);
|
|
}
|
|
} else {
|
|
for (c = 0 ; c < cols ; c++) {
|
|
PROCESS_PIXEL(rhc->rhYMC[2]);
|
|
}
|
|
}
|
|
} else if (lh && lhc && (rows -r) < overlap) {
|
|
/* Fade LH row */
|
|
if (plane == 'Y')
|
|
for (c = 0 ; c < cols ; c++) {
|
|
PROCESS_PIXEL(lhc->lhYMC[0]);
|
|
}
|
|
else if (plane == 'M') {
|
|
for (c = 0 ; c < cols ; c++) {
|
|
PROCESS_PIXEL(lhc->lhYMC[1]);
|
|
}
|
|
} else {
|
|
for (c = 0 ; c < cols ; c++) {
|
|
PROCESS_PIXEL(lhc->lhYMC[2]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Fill the tail margin with white */
|
|
if (pad_rows)
|
|
memset(&outdata[rows * cols], 0xff, pad_rows * cols);
|
|
}
|
|
|
|
static int dnp_panorama_splitjob(struct dnpds40_ctx *ctx,
|
|
const struct dnpds40_printjob *injob,
|
|
struct dnpds40_printjob **newjobs)
|
|
{
|
|
struct dnp_panodata *pano = NULL;
|
|
int overlap = injob->dpi * 2;
|
|
|
|
uint16_t out_rows = 0;
|
|
uint16_t in_rows = 0;
|
|
uint8_t num_panels = 0;
|
|
uint32_t new_multicut = 0;
|
|
uint16_t pad_rows = 0;
|
|
|
|
in_rows = injob->dpi == 600 ? injob->rows / 2 : injob->rows;
|
|
if (ctx->conn->type == P_DNP_DS620) {
|
|
out_rows = 2436;
|
|
new_multicut = MULTICUT_6x8;
|
|
|
|
if (in_rows == 4236) {
|
|
num_panels = 2;
|
|
pad_rows = 18;
|
|
} else if (in_rows == 6036) {
|
|
num_panels = 3;
|
|
pad_rows = 18;
|
|
} else if (in_rows == 2436*2) {
|
|
num_panels = 2;
|
|
overlap = 0;
|
|
} else if (in_rows == 2436*3) {
|
|
num_panels = 3;
|
|
overlap = 0;
|
|
} else {
|
|
ERROR("Invalid panorama size (%d rows)\n", injob->rows);
|
|
goto bail;
|
|
}
|
|
} else if (ctx->conn->type == P_DNP_DS820) {
|
|
if (in_rows == 5436) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4x10 : MULTICUT_8x10;
|
|
out_rows = 3036;
|
|
pad_rows = 18;
|
|
num_panels = 2;
|
|
} else if (in_rows == 7836) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4x10 : MULTICUT_8x10;
|
|
out_rows = 3036;
|
|
pad_rows = 18;
|
|
num_panels = 3;
|
|
} else if (in_rows == 3036*2) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4x10 : MULTICUT_8x10;
|
|
num_panels = 2;
|
|
out_rows = 3036;
|
|
overlap = 0;
|
|
} else if (in_rows == 3036*3) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4x10 : MULTICUT_8x10;
|
|
num_panels = 3;
|
|
out_rows = 3036;
|
|
overlap = 0;
|
|
} else if (in_rows == 6452) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4 : MULTICUT_8xA4LEN;
|
|
num_panels = 2;
|
|
pad_rows = 18;
|
|
out_rows = 3544;
|
|
} else if (in_rows == 9360) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4 : MULTICUT_8xA4LEN;
|
|
num_panels = 3;
|
|
pad_rows = 18;
|
|
out_rows = 3544;
|
|
} else if (in_rows == 3544*2) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4 : MULTICUT_8xA4LEN;
|
|
num_panels = 2;
|
|
out_rows = 3544;
|
|
overlap = 0;
|
|
} else if (in_rows == 3544*3) {
|
|
new_multicut = ctx->media == 600 ? MULTICUT_A4 : MULTICUT_8xA4LEN;
|
|
num_panels = 3;
|
|
out_rows = 3544;
|
|
overlap = 0;
|
|
} else if (in_rows == 6636) {
|
|
new_multicut = MULTICUT_8x12;
|
|
num_panels = 2;
|
|
pad_rows = 18;
|
|
out_rows = 3636;
|
|
} else if (in_rows == 9636) {
|
|
new_multicut = MULTICUT_8x12;
|
|
num_panels = 3;
|
|
pad_rows = 18;
|
|
out_rows = 3636;
|
|
} else if (in_rows == 3636*2) {
|
|
new_multicut = MULTICUT_8x12;
|
|
num_panels = 2;
|
|
out_rows = 3636;
|
|
overlap = 0;
|
|
} else if (in_rows == 3636*3) {
|
|
new_multicut = MULTICUT_8x12;
|
|
num_panels = 3;
|
|
out_rows = 3636;
|
|
overlap = 0;
|
|
} else {
|
|
ERROR("Invalid panorama size (%d rows)\n", injob->rows);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
/* Double everything up for >300dpi */
|
|
if (injob->dpi == 600) {
|
|
out_rows *= 2;
|
|
overlap *= 2;
|
|
pad_rows *= 2;
|
|
}
|
|
|
|
if (overlap) {
|
|
#ifdef USE_PANODATA_FILES
|
|
const char *filename = NULL;
|
|
|
|
if (ctx->conn->type == P_DNP_DS620)
|
|
filename = PANODATA_DS620;
|
|
else if (ctx->conn->type == P_DNP_DS820)
|
|
filename = PANODATA_DS820;
|
|
|
|
if (!filename || !(pano = dnp_read_panodata(filename))) {
|
|
ERROR("Failure to load panorama LUT file (%s)\n", filename);
|
|
goto bail;
|
|
}
|
|
#else
|
|
pano = dnp_read_panodata(NULL);
|
|
#endif
|
|
|
|
DEBUG("Splitting continuous panorama job... (%d panels of %d+%d rows, %d overlap)\n", num_panels, out_rows - 2*pad_rows, 2*pad_rows, overlap);
|
|
} else {
|
|
DEBUG("Splitting discrete panel panorama job...(%d panels of %d rows)\n", num_panels, out_rows);
|
|
}
|
|
|
|
for (int panel = 0 ; panel < num_panels ; panel++) {
|
|
int lh = (panel < (num_panels -1));
|
|
int rh = (panel > 0);
|
|
|
|
/* Create new job */
|
|
newjobs[panel] = malloc(sizeof(struct dnpds40_printjob));
|
|
if (!newjobs[panel]) {
|
|
ERROR("Memory allocation failure");
|
|
return CUPS_BACKEND_RETRY_CURRENT;
|
|
}
|
|
struct dnpds40_printjob *newjob = newjobs[panel];
|
|
memcpy(newjob, injob, sizeof(struct dnpds40_printjob));
|
|
|
|
newjob->common.copies = 1;
|
|
newjob->is_pano = lh; /* All but last */
|
|
newjob->databuf = malloc(MAX_PRINTJOB_LEN);
|
|
if (!newjob->databuf) {
|
|
dnpds40_cleanup_job(newjob);
|
|
newjobs[panel] = NULL;
|
|
ERROR("Memory allocation failure!\n");
|
|
return CUPS_BACKEND_RETRY_CURRENT;
|
|
}
|
|
newjob->datalen = 0;
|
|
|
|
newjob->multicut = new_multicut;
|
|
newjob->rows = out_rows;
|
|
|
|
uint32_t plane_len = out_rows * ctx->native_width + 1088;
|
|
|
|
/* Copy data blocks from injob */
|
|
uint8_t *ptr = injob->databuf;
|
|
while(ptr && ptr < (injob->databuf + injob->datalen)) {
|
|
uint32_t i;
|
|
int copy = 1;
|
|
if(!memcmp("IMAGE YPLANE", ptr +2, 12) ||
|
|
!memcmp("IMAGE MPLANE", ptr +2, 12) ||
|
|
!memcmp("IMAGE CPLANE", ptr +2, 12) ) {
|
|
|
|
if (ptr[8] == 'Y') {
|
|
/* Insert CONT_PANORAMA _before_ the image data */
|
|
int cont = (panel == num_panels-1) ? 0 : 1;
|
|
int lap = overlap;
|
|
if (injob->dpi == 600)
|
|
lap /= 2;
|
|
lap /= 3; /* Convert pixels into inches * 100 */
|
|
|
|
if (overlap) {
|
|
/* Continuous panorama */
|
|
newjob->datalen += sprintf((char*)newjob->databuf + newjob->datalen, "\033PCNTRL CONT_PANORAMA 00000008%04u%04u", cont, lap);
|
|
} else {
|
|
/* Discrete panel */
|
|
newjob->cutter = 1000;
|
|
}
|
|
}
|
|
|
|
uint8_t *bmp_hdr;
|
|
DEBUG("Panel %d plane %c\n", panel, ptr[8]);
|
|
newjob->datalen += sprintf((char*)newjob->databuf + newjob->datalen,
|
|
"\033PIMAGE %cPLANE %08u", ptr[8], plane_len);
|
|
|
|
/* Copy over old bitmap header*/
|
|
memcpy(newjob->databuf + newjob->datalen, ptr + 32, 1088);
|
|
bmp_hdr = newjob->databuf + newjob->datalen;
|
|
|
|
/* Mangle BMP header for new size and row count */
|
|
i = cpu_to_le32(plane_len);
|
|
memcpy(bmp_hdr + 2, &i, sizeof(i));
|
|
i = cpu_to_le32(out_rows);
|
|
memcpy(bmp_hdr + 22, &i, sizeof(i));
|
|
|
|
/* Split panorama */
|
|
dnp_applypano_plane(pano,
|
|
ptr + 32 + 1088 + (pad_rows * ctx->native_width) + (panel * (out_rows - pad_rows*2 - overlap) * ctx->native_width),
|
|
newjob->databuf + newjob->datalen + 1088,
|
|
out_rows - pad_rows*2, ctx->native_width, pad_rows, injob->dpi, overlap, ptr[8], rh, lh);
|
|
newjob->datalen += plane_len;
|
|
|
|
/* Don't copy anything else from source */
|
|
copy = 0;
|
|
} else if(!memcmp("CNTRL QTY", ptr + 2, 9)) {
|
|
/* Ignore copy count */
|
|
copy = 0;
|
|
} else if(!memcmp("CNTRL CUTTER", ptr + 2, 12) ||
|
|
!memcmp("CNTRL FULL_CUTTER_SET", ptr + 2, 21)) {
|
|
/* Ignore all cutter commands */
|
|
copy = 0;
|
|
}
|
|
|
|
/* Work out how much to copy from source, and increment source pointer */
|
|
char buf[10];
|
|
buf[8] = 0;
|
|
memcpy(buf, ptr + 24, 8);
|
|
i = atoi(buf) + 32;
|
|
if (copy) {
|
|
memcpy(newjob->databuf + newjob->datalen, ptr, i);
|
|
newjob->datalen += i;
|
|
}
|
|
ptr += i;
|
|
}
|
|
#if 0
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "/tmp/dump%d.raw", panel);
|
|
FILE *f = fopen(buf, "wb");
|
|
fwrite(newjob->databuf, newjob->datalen, 1, f);
|
|
fclose(f);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
dnp_free_panodata(pano);
|
|
return CUPS_BACKEND_OK;
|
|
|
|
bail:
|
|
dnp_free_panodata(pano);
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
#define JOB_EQUIV(__x) if (job1->__x != job2->__x) goto done
|
|
|
|
/* NOTE: Does _not_ free the input jobs */
|
|
static void *dnp_combine_jobs(const void *vjob1,
|
|
const void *vjob2)
|
|
{
|
|
const struct dnpds40_printjob *job1 = vjob1;
|
|
const struct dnpds40_printjob *job2 = vjob2;
|
|
struct dnpds40_printjob *newjob = NULL;
|
|
uint32_t new_multicut;
|
|
uint16_t new_w, new_h;
|
|
int32_t gap_bytes;
|
|
|
|
/* Sanity check */
|
|
if (!job1 || !job2)
|
|
goto done;
|
|
|
|
/* Make sure pertinent paremeters are the same */
|
|
JOB_EQUIV(dpi);
|
|
JOB_EQUIV(matte);
|
|
JOB_EQUIV(cutter);
|
|
JOB_EQUIV(fullcut);
|
|
JOB_EQUIV(multicut); // TODO: Support fancier modes for 8" models (eg 8x4+8x6, etc)
|
|
JOB_EQUIV(datalen); // <-- cheating a little?
|
|
// JOV_EQUIV(printspeed); <-- does it matter?
|
|
|
|
/* Any fancy cutter action means we pass */
|
|
if (job1->fullcut || job1->cutter > 120)
|
|
goto done;
|
|
/* Partial matte is no bueno */
|
|
if (job1->matte > 100 ||
|
|
job2->matte > 100)
|
|
goto done;
|
|
|
|
/* Make sure we can combine these two prints */
|
|
switch (job1->multicut) {
|
|
case MULTICUT_5x3_5:
|
|
new_multicut = MULTICUT_5x3_5X2;
|
|
new_w = 1920;
|
|
new_h = 2176;
|
|
gap_bytes = 0;
|
|
break;
|
|
case MULTICUT_6x4:
|
|
if (job1->cutter == 120) {
|
|
new_multicut = MULTICUT_6x8;
|
|
new_h = 2436;
|
|
gap_bytes = -44; /* Chop out the middle 44 rows */
|
|
} else {
|
|
new_multicut = MULTICUT_6x4X2;
|
|
new_h = 2498;
|
|
gap_bytes = 18;
|
|
}
|
|
new_w = 1920;
|
|
break;
|
|
case MULTICUT_6x4_5:
|
|
new_multicut = MULTICUT_6x4_5X2;
|
|
new_w = 1920;
|
|
new_h = 2802;
|
|
gap_bytes = 30;
|
|
break;
|
|
case MULTICUT_8x4:
|
|
new_multicut = MULTICUT_8x4X2;
|
|
new_w = 2560;
|
|
new_h = 2502;
|
|
gap_bytes = 30;
|
|
break;
|
|
case MULTICUT_8x5:
|
|
new_multicut = MULTICUT_8x5X2;
|
|
new_w = 2560;
|
|
new_h = 3102;
|
|
gap_bytes = 30;
|
|
break;
|
|
case MULTICUT_A4x5:
|
|
new_multicut = MULTICUT_A4x5X2;
|
|
new_w = 2560;
|
|
new_h = 3102;
|
|
gap_bytes = 30;
|
|
break;
|
|
case MULTICUT_A5:
|
|
new_multicut = MULTICUT_A5X2;
|
|
new_w = 2560;
|
|
new_h = 3598;
|
|
gap_bytes = 30;
|
|
break;
|
|
case MULTICUT_8x6:
|
|
new_multicut = MULTICUT_8x6X2;
|
|
new_w = 2560;
|
|
new_h = 3702;
|
|
gap_bytes = 30;
|
|
break;
|
|
#if 0 // XXX Do these printers handle automatic multicut?
|
|
// if not, implement using FULL_CUTTER_CONTROL!
|
|
case MULTICUT_4x3:
|
|
new_multicut = MULTICUT_4x6;
|
|
new_w = 1408;
|
|
new_h = 1836;
|
|
gap_bytes = 36;
|
|
break;
|
|
case MULTICUT_4x4:
|
|
new_multicut = MULTICUT_4x8;
|
|
new_w = 1408;
|
|
new_h = 2436;
|
|
gap_bytes = 36;
|
|
break;
|
|
case MULTICUT_4_5x3:
|
|
new_multicut = MULTICUT_4_5x6;
|
|
new_w = 1408;
|
|
new_h = 1836;
|
|
gap_bytes = 36;
|
|
break;
|
|
case MULTICUT_4_5x4:
|
|
new_multicut = MULTICUT_4_5x8;
|
|
new_w = 1408;
|
|
new_h = 2436;
|
|
gap_bytes = 36;
|
|
break;
|
|
#endif
|
|
default:
|
|
/* Everything else is NOT handled */
|
|
goto done;
|
|
}
|
|
gap_bytes *= new_w;
|
|
if (job1->dpi == 600) {
|
|
gap_bytes *= 2;
|
|
new_h *= 2;
|
|
}
|
|
|
|
DEBUG("Combining jobs to save media\n");
|
|
|
|
/* Okay, it's kosher to proceed */
|
|
|
|
newjob = malloc(sizeof(*newjob));
|
|
if (!newjob) {
|
|
ERROR("Memory allocation failure!\n");
|
|
goto done;
|
|
}
|
|
memcpy(newjob, job1, sizeof(*newjob));
|
|
|
|
newjob->databuf = malloc(((new_w*new_h+1024+54+10))*3+1024 + abs(gap_bytes));
|
|
newjob->datalen = 0;
|
|
newjob->multicut = new_multicut;
|
|
newjob->can_rewind = 0;
|
|
newjob->common.can_combine = 0;
|
|
if (!newjob->databuf) {
|
|
dnpds40_cleanup_job(newjob);
|
|
newjob = NULL;
|
|
ERROR("Memory allocation failure!\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Copy data blocks from job1 */
|
|
uint8_t *ptr, *ptr2;
|
|
char buf[10];
|
|
ptr = job1->databuf;
|
|
while(ptr && ptr < (job1->databuf + job1->datalen)) {
|
|
int i;
|
|
buf[8] = 0;
|
|
memcpy(buf, ptr + 24, 8);
|
|
i = atoi(buf) + 32;
|
|
memcpy(newjob->databuf + newjob->datalen, ptr, i);
|
|
|
|
/* If we're on a plane data block... */
|
|
if (!memcmp("PLANE", newjob->databuf + newjob->datalen + 9, 5)) {
|
|
long planelen = (new_w * new_h) + 1088;
|
|
uint32_t newlen;
|
|
|
|
/* Fix up length in command */
|
|
snprintf(buf, sizeof(buf), "%08lu", planelen);
|
|
memcpy(newjob->databuf + newjob->datalen + 24, buf, 8);
|
|
|
|
/* Alter BMP header */
|
|
newlen = cpu_to_le32(planelen);
|
|
memcpy(newjob->databuf + newjob->datalen + 32 + 2, &newlen, 4);
|
|
|
|
/* alter DIB header */
|
|
newlen = cpu_to_le32(new_h);
|
|
memcpy(newjob->databuf + newjob->datalen + 32 + 22, &newlen, 4);
|
|
|
|
if (gap_bytes > 0) {
|
|
/* Insert gap/padding after first image */
|
|
memset(newjob->databuf + newjob->datalen + i, 0xff, gap_bytes);
|
|
newjob->datalen += gap_bytes;
|
|
} else {
|
|
// uint8_t *ptrA = newjob->databuf + newjob->datalen + 1088;
|
|
// /* Back off by 1/2 the gap */
|
|
// memmove(ptrA, ptrA - (gap_bytes / 2), (i - 1088) + gap_bytes/2);
|
|
/* And chop the end off by half the gap */
|
|
newjob->datalen += gap_bytes / 2;
|
|
}
|
|
|
|
/* Locate job2's PLANE -- Assume it's in the same place! */
|
|
ptr2 = job2->databuf + (ptr - job1->databuf);
|
|
|
|
/* Copy over job2's image data */
|
|
memcpy(newjob->databuf + newjob->datalen + i,
|
|
ptr2 + 32 + 1088, i - 32 - 1088);
|
|
|
|
if (gap_bytes < 0) {
|
|
uint8_t *ptrA = newjob->databuf + newjob->datalen + i;
|
|
/* Back off by 1/2 the gap */
|
|
memmove(ptrA, ptrA - (gap_bytes / 2), (i - 1088) + gap_bytes/2);
|
|
/* And chop the end off by half the gap */
|
|
newjob->datalen += gap_bytes / 2;
|
|
}
|
|
newjob->datalen += i - 32 - 1088; /* add in job2 length */
|
|
}
|
|
|
|
newjob->datalen += i;
|
|
ptr += i;
|
|
}
|
|
|
|
done:
|
|
return newjob;
|
|
}
|
|
|
|
#undef JOB_EQUIV
|
|
|
|
static void dnpds40_build_cmd(struct dnpds40_cmd *cmd, const char *arg1, const 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)));
|
|
if (arg3_len) {
|
|
char buf[11]; /* Extra padding to shut up GCC 10 */
|
|
snprintf(buf, sizeof(buf), "%08u", arg3_len);
|
|
memcpy(cmd->arg3, buf, 8);
|
|
}
|
|
}
|
|
|
|
static void dnpds40_cleanup_string(char *start, int len)
|
|
{
|
|
char *ptr = strchr(start, 0x0d);
|
|
|
|
if (ptr && (ptr - start < len)) {
|
|
*ptr = 0x00; /* If there is a <CR>, terminate there */
|
|
len = ptr - start;
|
|
} else {
|
|
start[--len] = 0x00; /* force null-termination */
|
|
}
|
|
|
|
/* Trim trailing spaces */
|
|
while (len && start[len-1] == ' ') {
|
|
start[--len] = 0;
|
|
}
|
|
}
|
|
|
|
static const char *dnpds40_printer_type(int type, int mfg)
|
|
{
|
|
switch(type) {
|
|
case P_DNP_DS40: return mfg == MFG_CITIZEN? "CX" : "DS40";
|
|
case P_DNP_DS80: return mfg == MFG_CITIZEN? "CW" : (mfg == MFG_MITSUBISHI ? "CP3800" : "DS80");
|
|
case P_DNP_DS80D: return "DS80DX";
|
|
case P_DNP_DSRX1: return mfg == MFG_CITIZEN ? "CY" : "DSRX1";
|
|
case P_DNP_DS620: return mfg == MFG_CITIZEN ? "CX-02" : (mfg == MFG_FUJIFILM ? "ASK400" : "DS620");
|
|
case P_DNP_DS820: return mfg == MFG_CITIZEN ? "CX-02W" : "DS820";
|
|
case P_DNP_QW410: return mfg == MFG_CITIZEN ? "CZ-01" : "QW410";
|
|
case P_CITIZEN_CW01: return "CW01";
|
|
case P_CITIZEN_OP900II: return "CW-02 / OP900ii";
|
|
default: break;
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
static const char *dnpds40_media_types(int media, int sticker)
|
|
{
|
|
switch (media) {
|
|
case 100: return "UNKNOWN100"; // seen in driver dumps
|
|
case 110: return "UNKNOWN110"; // seen in driver dumps
|
|
case 150: return "4x6 (PC)";
|
|
case 151: return "4x8";
|
|
case 160: return "4.5x6";
|
|
case 161: return "4.5x8";
|
|
case 200: return sticker ? "5x3.5 (L): Sticker" : "5x3.5 (L)";
|
|
case 210: return sticker ? "5x7 (2L) Sticker" : "5x7 (2L)";
|
|
case 300: return sticker ? "6x4 (PC) Sticker" : "6x4 (PC)";
|
|
case 310: return sticker ? "6x8 (A5) Sticker" : "6x8 (A5)";
|
|
case 400: return sticker ? "6x9 (A5W) Sticker" : "6x9 (A5W)";
|
|
case 500: return "8x10";
|
|
case 510: return "8x12";
|
|
case 600: return "A4";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
static const char *dnpds620_media_extension_code(int media)
|
|
{
|
|
switch (media) {
|
|
case 0: return "Normal Paper";
|
|
case 1: return "Sticker Paper";
|
|
case 99: return "Unknown Paper";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
static const char *rfid_media_subtypes(int media)
|
|
{
|
|
switch (media) {
|
|
case 1: return "SD";
|
|
case 2: return "PD";
|
|
case 3: return "PP";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
static const char *dnpds80_duplex_media_types(int media)
|
|
{
|
|
switch (media) {
|
|
case 100: return "8x10.75";
|
|
case 200: return "8x12";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
#define DUPLEX_UNIT_PAPER_NONE 0
|
|
#define DUPLEX_UNIT_PAPER_PROTECTIVE 1
|
|
#define DUPLEX_UNIT_PAPER_PRESENT 2
|
|
|
|
static const char *dnpds80_duplex_paper_status(int media)
|
|
{
|
|
switch (media) {
|
|
case DUPLEX_UNIT_PAPER_NONE: return "No Paper";
|
|
case DUPLEX_UNIT_PAPER_PROTECTIVE: return "Protective Sheet";
|
|
case DUPLEX_UNIT_PAPER_PRESENT: return "Cut Paper Present";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
static const char *dnpds80_duplex_statuses(int status)
|
|
{
|
|
switch (status) {
|
|
case 5000: return "No Error";
|
|
|
|
case 5500: return "Duplex Unit Not Connected";
|
|
|
|
case 5017: return "Paper Jam: Supply Sensor On";
|
|
case 5018: return "Paper Jam: Supply Sensor Off";
|
|
case 5019: return "Paper Jam: Slot Sensor On";
|
|
case 5020: return "Paper Jam: Slot Sensor Off";
|
|
case 5021: return "Paper Jam: Pass Sensor On";
|
|
case 5022: return "Paper Jam: Pass Sensor Off";
|
|
case 5023: return "Paper Jam: Shell Sensor 1 On";
|
|
case 5024: return "Paper Jam: Shell Sensor 1 Off";
|
|
case 5025: return "Paper Jam: Shell Sensor 2 On";
|
|
case 5026: return "Paper Jam: Shell Sensor 2 Off";
|
|
case 5027: return "Paper Jam: Eject Sensor On";
|
|
case 5028: return "Paper Jam: Eject Sensor Off";
|
|
case 5029: return "Paper Jam: Slot FG Sensor";
|
|
case 5030: return "Paper Jam: Shell FG Sensor";
|
|
|
|
case 5033: return "Paper Supply Sensor Off";
|
|
case 5034: return "Printer Feed Slot Sensor Off";
|
|
case 5035: return "Pinch Pass Sensor Off";
|
|
case 5036: return "Shell Pass Sensor 1 Off";
|
|
case 5037: return "Shell Pass Sensor 2 Off";
|
|
case 5038: return "Eject Sensor Off";
|
|
|
|
case 5049: return "Capstan Drive Control Error";
|
|
case 5065: return "Shell Roller Error";
|
|
|
|
case 5081: return "Pinch Open Error";
|
|
case 5082: return "Pinch Close Error";
|
|
case 5083: return "Pinch Init Error";
|
|
case 5084: return "Pinch Position Error";
|
|
|
|
case 5097: return "Pass Guide Supply Error";
|
|
case 5098: return "Pass Guide Shell Error";
|
|
case 5099: return "Pass Guide Eject Error";
|
|
case 5100: return "Pass Guide Init Error";
|
|
case 5101: return "Pass Guide Position Error";
|
|
|
|
case 5113: return "Side Guide Home Error";
|
|
case 5114: return "Side Guide Position Error";
|
|
case 5115: return "Side Guide Init Error";
|
|
|
|
case 5129: return "Act Guide Home Error";
|
|
|
|
case 5145: return "Shell Rotate Home Error";
|
|
case 5146: return "Shell Rotate Rev Error";
|
|
|
|
case 5161: return "Paper Feed Lever Down Error";
|
|
case 5162: return "Paper Feed Lever Lock Error";
|
|
case 5163: return "Paper Feed Lever Up Error";
|
|
|
|
case 5177: return "Cutter Home Error";
|
|
case 5178: return "Cutter Away Error";
|
|
case 5179: return "Cutter Init Error";
|
|
case 5180: return "Cutter Position Error";
|
|
|
|
case 5193: return "Paper Tray Removed";
|
|
case 5209: return "Cover Opened";
|
|
case 5241: return "System Error";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown Duplexer Error";
|
|
}
|
|
|
|
static const char *dnpds40_statuses(int status)
|
|
{
|
|
if (status >= 5000 && status <= 5999)
|
|
return dnpds80_duplex_statuses(status);
|
|
|
|
switch (status) {
|
|
case 0: return "Idle";
|
|
case 1: return "Printing";
|
|
case 500: return "Cooling Print Head";
|
|
case 510: return "Cooling Paper Motor";
|
|
case 900: return "Standby Mode";
|
|
case 1000: return "Cover Open";
|
|
case 1010: return "No Scrap Box";
|
|
case 1100: return "Paper End";
|
|
case 1200: return "Ribbon End";
|
|
case 1300: return "Paper Jam";
|
|
case 1400: return "Ribbon Error";
|
|
case 1500: return "Paper Definition Error";
|
|
case 1600: return "Data Error";
|
|
case 2000: return "Head Voltage Error";
|
|
case 2010: return "USB Power Supply 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 2900: return "RS422 Communiation Error";
|
|
case 3000: return "System Error";
|
|
case 9999: return "Communication Failure"; /* Special */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown Error";
|
|
}
|
|
|
|
static const char *dnpds40_panorama_status(char *resp)
|
|
{
|
|
/* 00000 == okay to print
|
|
1xxxx == not okay to print
|
|
xxxx1 == head temp high
|
|
xxx2x == media temp low
|
|
xx1xx == humidity too high
|
|
*/
|
|
|
|
if (resp[4] == '0')
|
|
return "Yes";
|
|
if (resp[0] == '1')
|
|
return "No, High Head temp";
|
|
if (resp[1] == '2')
|
|
return "No, Low media temp";
|
|
if (resp[2] == '1')
|
|
return "No, High humidity";
|
|
|
|
return "No, Unknown reason";
|
|
}
|
|
|
|
static int dnpds40_do_cmd(struct dnpds40_ctx *ctx,
|
|
const struct dnpds40_cmd *cmd,
|
|
const uint8_t *data, int len)
|
|
{
|
|
int ret;
|
|
|
|
if ((ret = send_data(ctx->conn,
|
|
(uint8_t*)cmd, sizeof(*cmd))))
|
|
return ret;
|
|
|
|
if (data && len)
|
|
if ((ret = send_data(ctx->conn,
|
|
data, len)))
|
|
return ret;
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static uint8_t *dnpds40_resp_cmd2(struct dnpds40_ctx *ctx,
|
|
struct dnpds40_cmd *cmd,
|
|
int *len,
|
|
uint8_t *buf, uint32_t buf_len)
|
|
{
|
|
char tmp[9];
|
|
uint8_t *respbuf;
|
|
|
|
int ret, i, num = 0;
|
|
|
|
memset(tmp, 0, sizeof(tmp));
|
|
|
|
if ((ret = dnpds40_do_cmd(ctx, cmd, buf, buf_len)))
|
|
return NULL;
|
|
|
|
/* Read in the response header */
|
|
ret = read_data(ctx->conn,
|
|
(uint8_t*)tmp, 8, &num);
|
|
if (ret < 0)
|
|
return NULL;
|
|
|
|
if (num != 8) {
|
|
ERROR("Short read! (%d/%d)\n", num, 8);
|
|
return NULL;
|
|
}
|
|
|
|
i = atoi(tmp); /* Length of payload in bytes, possibly padded */
|
|
respbuf = malloc(i + 1);
|
|
if (!respbuf) {
|
|
ERROR("Memory allocation failure (%d bytes)!\n", i);
|
|
return NULL;
|
|
}
|
|
respbuf[i] = 0; /* Explicitly null-pad */
|
|
|
|
/* Read in the actual response */
|
|
ret = read_data(ctx->conn,
|
|
respbuf, i, &num);
|
|
if (ret < 0) {
|
|
free(respbuf);
|
|
return NULL;
|
|
}
|
|
|
|
if (num != i) {
|
|
ERROR("Short read! (%d/%d)\n", num, i);
|
|
free(respbuf);
|
|
return NULL;
|
|
}
|
|
|
|
*len = num;
|
|
return respbuf;
|
|
}
|
|
|
|
#define dnpds40_resp_cmd(__ctx, __cmd, __len) dnpds40_resp_cmd2(__ctx, __cmd, __len, NULL, 0)
|
|
|
|
static int dnpds40_query_serno(struct dyesub_connection *conn, char *buf, int buf_len)
|
|
{
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int len = 0;
|
|
|
|
struct dnpds40_ctx ctx = {
|
|
.conn = conn,
|
|
};
|
|
|
|
/* Get Serial Number */
|
|
dnpds40_build_cmd(&cmd, "INFO", "SERIAL_NUMBER", 0);
|
|
|
|
resp = dnpds40_resp_cmd(&ctx, &cmd, &len);
|
|
if (!resp)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
strncpy(buf, (char*)resp, buf_len);
|
|
buf[buf_len-1] = 0;
|
|
|
|
free(resp);
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static void *dnpds40_init(void)
|
|
{
|
|
struct dnpds40_ctx *ctx = malloc(sizeof(struct dnpds40_ctx));
|
|
if (!ctx) {
|
|
ERROR("Memory allocation failure (%d bytes)!\n", (int)sizeof(struct dnpds40_ctx));
|
|
return NULL;
|
|
}
|
|
memset(ctx, 0, sizeof(struct dnpds40_ctx));
|
|
|
|
return ctx;
|
|
}
|
|
|
|
#define FW_VER_CHECK(__major, __minor) \
|
|
((ctx->ver_major > (__major)) || \
|
|
(ctx->ver_major == (__major) && ctx->ver_minor >= (__minor)))
|
|
|
|
static int dnpds40_query_mqty(struct dnpds40_ctx *ctx)
|
|
{
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int len = 0, count;
|
|
|
|
/* Get Media remaining */
|
|
dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0);
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (!resp)
|
|
return -1;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
count = atoi((char*)resp+4);
|
|
free(resp);
|
|
|
|
if (count) {
|
|
/* Old-sk00l models report one less than they should */
|
|
if (!ctx->correct_count)
|
|
count++;
|
|
|
|
count -= ctx->mediaoffset;
|
|
if (count < 0) /* Just in case */
|
|
count = 0;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static int dnpds80dx_query_paper(struct dnpds40_ctx *ctx)
|
|
{
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int len = 0;
|
|
|
|
/* Query Duplex Media Info */
|
|
dnpds40_build_cmd(&cmd, "INFO", "UNIT_CUT_PAPER", 0);
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (resp) {
|
|
char tmp[5];
|
|
char status;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
memcpy(tmp, resp + 4, 4);
|
|
status = tmp[3];
|
|
tmp[3] = '0';
|
|
tmp[4] = 0;
|
|
|
|
ctx->duplex_media = atoi(tmp);
|
|
|
|
tmp[0] = tmp[1] = tmp[2] = '0';
|
|
tmp[3] = status;
|
|
ctx->duplex_media_status = atoi(tmp);
|
|
|
|
free(resp);
|
|
} else {
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static int dnpds40_attach(void *vctx, struct dyesub_connection *conn, uint8_t jobid)
|
|
{
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
UNUSED(jobid);
|
|
|
|
ctx->conn = conn;
|
|
|
|
/* Nearly all models support 600dpi */
|
|
ctx->supports_600dpi = 1;
|
|
|
|
if (test_mode < TEST_MODE_NOATTACH) {
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int len = 0;
|
|
|
|
/* Get Firmware Version */
|
|
dnpds40_build_cmd(&cmd, "INFO", "FVER", 0);
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (resp) {
|
|
char *ptr;
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
ctx->version = strdup((char*) resp);
|
|
|
|
/* See if we're in FW update mode */
|
|
if (strstr(ctx->version, "RW")) {
|
|
ctx->flash_mode = 1;
|
|
ctx->ver_major = 0;
|
|
ctx->ver_minor = 0;
|
|
} else {
|
|
/* Parse version */
|
|
/* ptr = */ strtok((char*)resp, " .");
|
|
ptr = strtok(NULL, ".");
|
|
ctx->ver_major = atoi(ptr);
|
|
ptr = strtok(NULL, ".");
|
|
ctx->ver_minor = atoi(ptr);
|
|
}
|
|
|
|
free(resp);
|
|
} else {
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
/* Figure out actual Manufacturer */
|
|
{
|
|
struct libusb_device_descriptor desc;
|
|
struct libusb_device *udev;
|
|
|
|
udev = libusb_get_device(ctx->conn->dev);
|
|
libusb_get_device_descriptor(udev, &desc);
|
|
|
|
char buf[STR_LEN_MAX + 1];
|
|
buf[0] = 0;
|
|
buf[STR_LEN_MAX] = 0;
|
|
libusb_get_string_descriptor_ascii(ctx->conn->dev, desc.iManufacturer, (unsigned char*)buf, STR_LEN_MAX);
|
|
|
|
if (!strncmp(buf, "Dai", 3)) /* "Dai Nippon Printing" */
|
|
ctx->mfg = MFG_DNP;
|
|
else if (!strncmp(buf, "CIT", 3)) /* "CITIZEN SYSTEMS" */
|
|
ctx->mfg = MFG_CITIZEN;
|
|
else if (!strncmp(buf, "M", 1)) /* "Mitsubishi" */
|
|
ctx->mfg = MFG_MITSUBISHI;
|
|
else if (!strncmp(buf, "F", 1)) /* "Fujifilm" */
|
|
ctx->mfg = MFG_FUJIFILM;
|
|
else
|
|
ctx->mfg = MFG_OTHER;
|
|
|
|
#ifdef DNP_ONLY /* Only allow DNP printers to work. */
|
|
if (ctx->mfg != MFG_DNP)
|
|
return CUPS_BACKEND_FAILED;
|
|
#endif
|
|
#ifdef CITIZEN_ONLY /* Only allow CITIZEN printers to work. */
|
|
if (ctx->mfg != MFG_CITIZEN)
|
|
return CUPS_BACKEND_FAILED
|
|
#endif
|
|
}
|
|
} else {
|
|
ctx->ver_major = 3;
|
|
ctx->ver_minor = 0;
|
|
ctx->version = strdup("UNKNOWN");
|
|
}
|
|
|
|
/* Per-printer options */
|
|
switch (ctx->conn->type) {
|
|
case P_DNP_DS40:
|
|
ctx->native_width = 1920;
|
|
ctx->max_height = 5480;
|
|
ctx->supports_6x9 = 1;
|
|
if (FW_VER_CHECK(1,04))
|
|
ctx->supports_counterp = 1;
|
|
if (FW_VER_CHECK(1,30))
|
|
ctx->supports_matte = 1;
|
|
if (FW_VER_CHECK(1,40))
|
|
ctx->supports_2x6 = 1;
|
|
if (FW_VER_CHECK(1,50))
|
|
ctx->supports_3x5x2 = 1;
|
|
if (FW_VER_CHECK(1,60))
|
|
ctx->supports_fullcut = ctx->supports_6x6 = 1; // No 5x5!
|
|
if (FW_VER_CHECK(1,70))
|
|
/* Always reports iSerial, not configurable */
|
|
ctx->supports_iserial = 2;
|
|
break;
|
|
case P_DNP_DS80:
|
|
case P_DNP_DS80D:
|
|
ctx->native_width = 2560;
|
|
ctx->max_height = 7536;
|
|
if (FW_VER_CHECK(1,02))
|
|
ctx->supports_counterp = 1;
|
|
if (FW_VER_CHECK(1,30))
|
|
ctx->supports_matte = 1;
|
|
if (FW_VER_CHECK(1,42))
|
|
/* Always reports iSerial, not configurable */
|
|
ctx->supports_iserial = 2;
|
|
break;
|
|
case P_DNP_DSRX1:
|
|
ctx->native_width = 1920;
|
|
ctx->max_height = 5480;
|
|
ctx->supports_counterp = 1;
|
|
ctx->supports_matte = 1;
|
|
if (FW_VER_CHECK(1,10))
|
|
ctx->supports_2x6 = ctx->supports_mqty_default = 1;
|
|
if (FW_VER_CHECK(1,20))
|
|
ctx->supports_3x5x2 = 1;
|
|
if (FW_VER_CHECK(2,00)) { /* AKA RX1HS */
|
|
ctx->needs_mlot = 1;
|
|
ctx->supports_mediaoffset = 1;
|
|
ctx->supports_iserial = 1;
|
|
}
|
|
if (FW_VER_CHECK(2,06)) {
|
|
ctx->supports_5x5 = ctx->supports_6x6 = 1;
|
|
}
|
|
break;
|
|
case P_CITIZEN_OP900II:
|
|
ctx->native_width = 1920;
|
|
ctx->max_height = 5480;
|
|
ctx->supports_counterp = 1;
|
|
ctx->supports_matte = 1;
|
|
ctx->supports_mqty_default = 1;
|
|
ctx->supports_6x9 = 1;
|
|
if (FW_VER_CHECK(1,11))
|
|
ctx->supports_2x6 = 1;
|
|
break;
|
|
case P_CITIZEN_CW01:
|
|
ctx->native_width = 2048;
|
|
ctx->max_height = 5480;
|
|
ctx->supports_6x9 = 1;
|
|
break;
|
|
case P_DNP_DS620:
|
|
ctx->native_width = 1920;
|
|
ctx->max_height = 5604;
|
|
ctx->correct_count = 1;
|
|
ctx->supports_counterp = 1;
|
|
ctx->supports_matte = 1;
|
|
ctx->supports_2x6 = 1;
|
|
ctx->supports_fullcut = 1;
|
|
ctx->supports_mqty_default = 1;
|
|
ctx->supports_standby = 1;
|
|
ctx->supports_keepmode = 1;
|
|
ctx->supports_iserial = 1;
|
|
ctx->supports_6x6 = 1;
|
|
ctx->supports_5x5 = 1;
|
|
ctx->supports_lowspeed = 1;
|
|
ctx->supports_3x5x2 = 1;
|
|
|
|
if (ctx->mfg == MFG_CITIZEN) { /* Citizen and DNP firmware diverge */
|
|
ctx->supports_adv_fullcut = 1;
|
|
ctx->supports_advmatte = 1;
|
|
ctx->supports_luster = 1;
|
|
ctx->supports_rewind = 1;
|
|
ctx->supports_pano = 1; // XXX what version?
|
|
|
|
if (FW_VER_CHECK(1,01)) {
|
|
ctx->supports_finematte = 1;
|
|
ctx->supports_contpano = 1; // XXX what version?
|
|
}
|
|
if (FW_VER_CHECK(1,10))
|
|
ctx->supports_6x9 = ctx->supports_6x4_5 = 1;
|
|
|
|
} else if (ctx->mfg == MFG_DNP) {
|
|
if (strchr(ctx->version, 'A'))
|
|
ctx->supports_rewind = 0;
|
|
else
|
|
ctx->supports_rewind = 1;
|
|
|
|
if (FW_VER_CHECK(1,10))
|
|
ctx->supports_6x9 = ctx->supports_6x4_5 = 1;
|
|
if (FW_VER_CHECK(1,20))
|
|
ctx->supports_adv_fullcut = ctx->supports_advmatte = ctx->supports_pano = 1;
|
|
if (FW_VER_CHECK(1,30))
|
|
ctx->supports_luster = 1;
|
|
if (FW_VER_CHECK(1,32))
|
|
ctx->supports_contpano = 1;
|
|
if (FW_VER_CHECK(1,33))
|
|
ctx->supports_media_ext = 1;
|
|
if (FW_VER_CHECK(1,52))
|
|
ctx->supports_finematte = 1;
|
|
} else if (ctx->mfg == MFG_FUJIFILM) {
|
|
ctx->supports_finematte = 1;
|
|
ctx->supports_luster = 1;
|
|
ctx->supports_media_ext = 1;
|
|
ctx->supports_6x4_5 = 1;
|
|
ctx->supports_6x9 = 1;
|
|
ctx->supports_rewind = 1;
|
|
// XXX Need to confirm these
|
|
ctx->supports_adv_fullcut = 1;
|
|
ctx->supports_advmatte = 1;
|
|
}
|
|
break;
|
|
case P_DNP_DS820:
|
|
ctx->native_width = 2560;
|
|
ctx->max_height = 7536;
|
|
ctx->correct_count = 1;
|
|
ctx->supports_counterp = 1;
|
|
ctx->supports_matte = 1;
|
|
ctx->supports_fullcut = 1;
|
|
ctx->supports_mqty_default = 1;
|
|
ctx->supports_standby = 1;
|
|
ctx->supports_keepmode = 1;
|
|
ctx->supports_iserial = 1;
|
|
ctx->supports_adv_fullcut = 1;
|
|
ctx->supports_advmatte = 1;
|
|
ctx->supports_luster = 1;
|
|
ctx->supports_finematte = 1;
|
|
ctx->supports_printspeed = 1;
|
|
ctx->supports_lowspeed = 1;
|
|
ctx->supports_highdensity = 1;
|
|
ctx->supports_ctrld_ext = 1;
|
|
ctx->supports_mediaoffset = 1;
|
|
ctx->supports_mediaclassrfid = 1;
|
|
ctx->supports_gamma = 1;
|
|
ctx->supports_pano = 1;
|
|
ctx->supports_contpano = 1;
|
|
|
|
if (ctx->mfg == MFG_CITIZEN) { /* Citizen and DNP firmware diverge */
|
|
ctx->supports_rewind = 1;
|
|
ctx->supports_a4x6 = 1; // XXX need to confirm this!
|
|
} else {
|
|
if (strchr(ctx->version, 'A'))
|
|
ctx->supports_rewind = 0;
|
|
else
|
|
ctx->supports_rewind = 1;
|
|
|
|
if (FW_VER_CHECK(1,06))
|
|
ctx->supports_a4x6 = 1;
|
|
}
|
|
break;
|
|
case P_DNP_QW410:
|
|
ctx->native_width = 1408;
|
|
ctx->max_height = 2436;
|
|
ctx->correct_count = 1;
|
|
ctx->supports_600dpi = 0;
|
|
ctx->supports_counterp = 1;
|
|
ctx->supports_matte = 1;
|
|
ctx->supports_fullcut = 1;
|
|
ctx->supports_mqty_default = 1;
|
|
ctx->supports_keepmode = 1;
|
|
ctx->supports_iserial = 1;
|
|
ctx->supports_adv_fullcut = 1;
|
|
ctx->supports_advmatte = 1;
|
|
ctx->supports_printspeed = 1;
|
|
ctx->supports_lowspeed = 1;
|
|
ctx->supports_ctrld_ext = 1;
|
|
ctx->supports_mediaoffset = 1;
|
|
ctx->supports_mediaclassrfid = 1;
|
|
ctx->supports_systime = 1;
|
|
if (FW_VER_CHECK(1,10))
|
|
ctx->supports_45_34 = 1;
|
|
break;
|
|
default:
|
|
ERROR("Unknown printer type %d\n", ctx->conn->type);
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
if (test_mode < TEST_MODE_NOATTACH) {
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int len = 0;
|
|
|
|
if (ctx->flash_mode) {
|
|
goto skip_queries;
|
|
}
|
|
|
|
/* 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! */
|
|
} else {
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
if (ctx->conn->type != P_DNP_QW410) {
|
|
/* Subtract out the "mark" type */
|
|
if (ctx->media & 1)
|
|
ctx->media--;
|
|
}
|
|
free(resp);
|
|
} else {
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
/* Try to figure out media subtype */
|
|
if (ctx->supports_mediaclassrfid) {
|
|
dnpds40_build_cmd(&cmd, "INFO", "MEDIA_CLASS_RFID", 0);
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (!resp)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
ctx->media_subtype = atoi((char*)resp);
|
|
free(resp);
|
|
}
|
|
|
|
/* And sticker */
|
|
if (ctx->supports_media_ext) {
|
|
int type;
|
|
dnpds40_build_cmd(&cmd, "INFO", "MEDIA_EXT_CODE", 0);
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (!resp)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
type = atoi((char*)resp+7);
|
|
ctx->media_sticker = (type == 1);
|
|
free(resp);
|
|
}
|
|
|
|
if (ctx->conn->type == P_DNP_DS80D) {
|
|
if (dnpds80dx_query_paper(ctx))
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
} else {
|
|
switch(ctx->conn->type) {
|
|
case P_DNP_DS80D:
|
|
ctx->duplex_media = 200;
|
|
/* Intentional fallthrough */
|
|
case P_DNP_DS80:
|
|
case P_DNP_DS820:
|
|
ctx->media = 510; /* 8x12 */
|
|
break;
|
|
case P_DNP_DSRX1:
|
|
ctx->media = 310; /* 6x8 */
|
|
break;
|
|
default:
|
|
ctx->media = 400; /* 6x9 */
|
|
break;
|
|
}
|
|
|
|
if (getenv("MEDIA_CODE"))
|
|
ctx->media = strtol(getenv("MEDIA_CODE"), NULL, 10);
|
|
}
|
|
|
|
ctx->last_matte = -1;
|
|
#ifdef STATE_DIR
|
|
/* Check our current job's lamination vs previous job. */
|
|
{
|
|
/* Load last matte status from file */
|
|
char buf[64];
|
|
FILE *f;
|
|
snprintf(buf, sizeof(buf), STATE_DIR "/%s-last", ctx->serno);
|
|
f = fopen(buf, "r");
|
|
if (f) {
|
|
fscanf(f, "%d", &ctx->last_matte);
|
|
fclose(f);
|
|
}
|
|
}
|
|
#endif
|
|
if (test_mode < TEST_MODE_NOATTACH && ctx->supports_mediaoffset) {
|
|
/* Get Media Offset */
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int len = 0;
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "MEDIA_OFFSET", 0);
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (resp) {
|
|
ctx->mediaoffset = atoi((char*)resp+4);
|
|
free(resp);
|
|
} else {
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
} else if (!ctx->correct_count) {
|
|
ctx->mediaoffset = 50;
|
|
}
|
|
|
|
if (test_mode < TEST_MODE_NOATTACH && ctx->supports_mqty_default) {
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int len = 0;
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "MQTY_DEFAULT", 0);
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (resp) {
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
ctx->media_count_new = atoi((char*)resp+4);
|
|
free(resp);
|
|
ctx->media_count_new -= ctx->mediaoffset;
|
|
} else {
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
} else {
|
|
/* Look it up for legacy models & FW */
|
|
switch (ctx->conn->type) {
|
|
case P_DNP_DS40:
|
|
switch (ctx->media) {
|
|
case 200: // L
|
|
ctx->media_count_new = 460;
|
|
break;
|
|
case 210: // 2L
|
|
ctx->media_count_new = 230;
|
|
break;
|
|
case 300: // PC
|
|
ctx->media_count_new = 400;
|
|
break;
|
|
case 310: // A5
|
|
ctx->media_count_new = 200;
|
|
break;
|
|
case 400: // A5W
|
|
ctx->media_count_new = 180;
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case P_DNP_DSRX1:
|
|
switch (ctx->media) {
|
|
case 210: // 2L
|
|
ctx->media_count_new = 350;
|
|
break;
|
|
case 300: // PC
|
|
ctx->media_count_new = 700;
|
|
break;
|
|
case 310: // A5
|
|
ctx->media_count_new = 350;
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case P_CITIZEN_OP900II:
|
|
switch (ctx->media) {
|
|
case 210: // 2L
|
|
ctx->media_count_new = 350;
|
|
break;
|
|
case 300: // PC
|
|
ctx->media_count_new = 600;
|
|
break;
|
|
case 310: // A5
|
|
ctx->media_count_new = 300;
|
|
break;
|
|
case 400: // A5W
|
|
ctx->media_count_new = 280;
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case P_CITIZEN_CW01:
|
|
switch (ctx->media) {
|
|
case 300: // PC
|
|
ctx->media_count_new = 600;
|
|
break;
|
|
case 350: // 2L
|
|
ctx->media_count_new = 230;
|
|
break;
|
|
case 400: // A5W
|
|
ctx->media_count_new = 280;
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case P_DNP_DS80:
|
|
case P_DNP_DS80D:
|
|
switch (ctx->media) {
|
|
case 500: // 8x10
|
|
ctx->media_count_new = 130;
|
|
break;
|
|
case 510: // 8x12
|
|
ctx->media_count_new = 110;
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case P_DNP_DS620:
|
|
switch (ctx->media) {
|
|
case 200: // L
|
|
ctx->media_count_new = 420;
|
|
break;
|
|
case 210: // 2L
|
|
ctx->media_count_new = 230;
|
|
break;
|
|
case 300: // PC
|
|
ctx->media_count_new = 400;
|
|
break;
|
|
case 310: // A5
|
|
ctx->media_count_new = 200;
|
|
break;
|
|
case 400: // A5W
|
|
ctx->media_count_new = 180;
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case P_DNP_DS820:
|
|
ctx->media_subtype = 1;
|
|
switch (ctx->media) {
|
|
case 500: // 8x10
|
|
ctx->media_count_new = 260; // ???
|
|
break;
|
|
case 510: // 8x12
|
|
ctx->media_count_new = 220; // ???
|
|
break;
|
|
case 600: // A4
|
|
ctx->media_count_new = 220; // ???
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case P_DNP_QW410:
|
|
switch (ctx->media) {
|
|
case 150: // 4x6
|
|
case 160: // 4.5x6
|
|
ctx->media_count_new = 150;
|
|
break;
|
|
case 151: // 4x8
|
|
case 161: // 4.5x8
|
|
ctx->media_count_new = 110;
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
ctx->media_count_new = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (test_mode < TEST_MODE_NOATTACH && ctx->supports_systime) {
|
|
/* Set Printer Time */
|
|
struct dnpds40_cmd cmd;
|
|
char buf[16];
|
|
struct tm *tm;
|
|
|
|
time_t now = time(NULL);
|
|
tm = localtime(&now);
|
|
strftime(buf, sizeof(buf), "%Y%m%d%H%M%S\r", tm); /* YYYYMMDDHHMMSS\n\0 */
|
|
dnpds40_build_cmd(&cmd, "CNTRL", "SET_SYS_TIME", 0);
|
|
if ((dnpds40_do_cmd(ctx, &cmd, (uint8_t*) buf, sizeof(buf))) != 0)
|
|
return CUPS_BACKEND_FAILED;
|
|
}
|
|
|
|
skip_queries:
|
|
/* Fill out marker message */
|
|
if (ctx->supports_mediaclassrfid) {
|
|
snprintf(ctx->media_text, sizeof(ctx->media_text),
|
|
"%s %s", dnpds40_media_types(ctx->media, ctx->media_sticker),
|
|
rfid_media_subtypes(ctx->media_subtype));
|
|
} else {
|
|
snprintf(ctx->media_text, sizeof(ctx->media_text),
|
|
"%s", dnpds40_media_types(ctx->media, ctx->media_sticker));
|
|
}
|
|
|
|
/* Fill out marker structure */
|
|
ctx->marker[0].color = "#00FFFF#FF00FF#FFFF00";
|
|
ctx->marker[0].name = ctx->media_text;
|
|
ctx->marker[0].numtype = ctx->media;
|
|
ctx->marker[0].levelmax = ctx->media_count_new;
|
|
ctx->marker[0].levelnow = CUPS_MARKER_UNKNOWN;
|
|
ctx->marker_count = 1;
|
|
|
|
if (ctx->conn->type == P_DNP_DS80D) {
|
|
ctx->marker[1].color = "#00FFFF#FF00FF#FFFF00";
|
|
ctx->marker[1].name = dnpds80_duplex_media_types(ctx->duplex_media);
|
|
ctx->marker[1].numtype = ctx->duplex_media;
|
|
ctx->marker[1].levelmax = ctx->marker[0].levelmax/2;
|
|
ctx->marker[1].levelnow = CUPS_MARKER_UNKNOWN;
|
|
ctx->marker_count++;
|
|
}
|
|
|
|
return CUPS_BACKEND_OK;
|
|
}
|
|
|
|
static void dnpds40_cleanup_job(const void *vjob) {
|
|
const struct dnpds40_printjob *job = vjob;
|
|
|
|
if (job->databuf)
|
|
free(job->databuf);
|
|
|
|
free((void*)job);
|
|
}
|
|
|
|
static void dnpds40_teardown(void *vctx) {
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
if (!ctx)
|
|
return;
|
|
|
|
if (test_mode < TEST_MODE_NOATTACH && ctx->conn->type == P_DNP_DS80D) {
|
|
struct dnpds40_cmd cmd;
|
|
|
|
/* Check to see if last print was the front side
|
|
of a duplex job, and if so, cancel things so we're done */
|
|
if (ctx->last_multicut >= 200 &&
|
|
ctx->last_multicut < 300) {
|
|
dnpds40_build_cmd(&cmd, "CNTRL", "DUPLEX_CANCEL", 0);
|
|
if ((dnpds40_do_cmd(ctx, &cmd, NULL, 0)) != 0)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ctx->serno)
|
|
free(ctx->serno);
|
|
if (ctx->version)
|
|
free(ctx->version);
|
|
free(ctx);
|
|
}
|
|
|
|
static int dnpds40_query_status(struct dnpds40_ctx *ctx)
|
|
{
|
|
struct dnpds40_cmd cmd;
|
|
uint8_t *resp;
|
|
int count, len;
|
|
|
|
if (test_mode >= TEST_MODE_NOATTACH)
|
|
return 9999;
|
|
|
|
/* Generate command */
|
|
dnpds40_build_cmd(&cmd, "STATUS", "", 0);
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
if (!resp)
|
|
return 9999;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
count = atoi((char*)resp);
|
|
free(resp);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
int run = 1;
|
|
char buf[9] = { 0 };
|
|
|
|
struct dnpds40_printjob *job = NULL;
|
|
|
|
if (!ctx)
|
|
return CUPS_BACKEND_FAILED;
|
|
|
|
job = malloc(sizeof(*job));
|
|
if (!job) {
|
|
ERROR("Memory allocation failure!\n");
|
|
return CUPS_BACKEND_RETRY_CURRENT;
|
|
}
|
|
memset(job, 0, sizeof(*job));
|
|
job->common.jobsize = sizeof(*job);
|
|
job->printspeed = -1;
|
|
job->buffcntrl = -1;
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
job->databuf = malloc(MAX_PANOPRINTJOB_LEN);
|
|
if (!job->databuf) {
|
|
dnpds40_cleanup_job(job);
|
|
ERROR("Memory allocation failure!\n");
|
|
return CUPS_BACKEND_RETRY_CURRENT;
|
|
}
|
|
|
|
while (run) {
|
|
int i, j;
|
|
uint32_t remain;
|
|
|
|
/* Make sure we won't overflow... */
|
|
if (job->datalen + sizeof(struct dnpds40_cmd) > MAX_PANOPRINTJOB_LEN) {
|
|
ERROR("Buffer overflow when parsing printjob! (%d+%lu)\n",
|
|
job->datalen, sizeof(struct dnpds40_cmd));
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Read in command header */
|
|
i = read(data_fd, job->databuf + job->datalen,
|
|
sizeof(struct dnpds40_cmd));
|
|
if (i < 0) {
|
|
dnpds40_cleanup_job(job);
|
|
return i;
|
|
} else if (i == 0) {
|
|
break;
|
|
} else if (i < (int) sizeof(struct dnpds40_cmd)) {
|
|
int r = i;
|
|
i = read(data_fd, job->databuf + job->datalen + r, sizeof(struct dnpds40_cmd) - r);
|
|
if (i < 0) {
|
|
dnpds40_cleanup_job(job);
|
|
return i;
|
|
} else if (i == 0) {
|
|
break;
|
|
} else if (i < (int)(sizeof(struct dnpds40_cmd) - r)) {
|
|
ERROR("Double short read (%d + %d vs %d)\n", r, i, (int)sizeof(struct dnpds40_cmd));
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
}
|
|
|
|
/* Special case handling for beginning of job */
|
|
if (job->datalen == 0) {
|
|
/* See if job lacks the standard ESC-P start sequence */
|
|
if (job->databuf[job->datalen + 0] != 0x1b ||
|
|
job->databuf[job->datalen + 1] != 0x50) {
|
|
switch(ctx->conn->type) {
|
|
case P_DNP_QW410:
|
|
i = legacy_qw410_read_parse(job, data_fd, i);
|
|
break;
|
|
case P_CITIZEN_CW01:
|
|
i = legacy_cw01_read_parse(job, data_fd, i);
|
|
break;
|
|
case P_DNP_DS620:
|
|
i = legacy_dnp620_read_parse(job, data_fd, i);
|
|
break;
|
|
case P_DNP_DS820:
|
|
i = legacy_dnp820_read_parse(job, data_fd, i);
|
|
break;
|
|
case P_DNP_DSRX1:
|
|
case P_DNP_DS40:
|
|
case P_DNP_DS80:
|
|
case P_DNP_DS80D:
|
|
default:
|
|
i = legacy_dnp_read_parse(job, data_fd, i);
|
|
break;
|
|
}
|
|
|
|
if (i == CUPS_BACKEND_OK) {
|
|
goto parsed;
|
|
}
|
|
dnpds40_cleanup_job(job);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/* Parse out length of data chunk, if any */
|
|
memcpy(buf, job->databuf + job->datalen + 24, 8);
|
|
j = atoi(buf);
|
|
|
|
/* Read in data chunk as quickly as possible */
|
|
remain = j;
|
|
|
|
/* Make sure we won't overflow... */
|
|
if (job->datalen + remain > MAX_PANOPRINTJOB_LEN) {
|
|
ERROR("Buffer overflow when parsing printjob! (%d+%d)\n",
|
|
job->datalen, remain);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
while (remain > 0) {
|
|
i = read(data_fd, job->databuf + job->datalen + sizeof(struct dnpds40_cmd),
|
|
remain);
|
|
if (i < 0) {
|
|
ERROR("Data Read Error: %d (%d/%d @%d/%d)\n", i, remain, j, job->datalen,MAX_PANOPRINTJOB_LEN);
|
|
dnpds40_cleanup_job(job);
|
|
return i;
|
|
}
|
|
if (i == 0) {
|
|
dnpds40_cleanup_job(job);
|
|
return 1;
|
|
}
|
|
job->datalen += i;
|
|
remain -= i;
|
|
}
|
|
job->datalen -= j; /* Back it off */
|
|
|
|
/* Check for some offsets */
|
|
if(!memcmp("CNTRL QTY", job->databuf + job->datalen+2, 9)) {
|
|
memcpy(buf, job->databuf + job->datalen + 32, 8);
|
|
job->common.copies = atoi(buf);
|
|
continue;
|
|
}
|
|
if(!memcmp("CNTRL CUTTER", job->databuf + job->datalen+2, 12)) {
|
|
memcpy(buf, job->databuf + job->datalen + 32, 8);
|
|
job->cutter = atoi(buf);
|
|
/* We'll insert it ourselves later */
|
|
|
|
if (job->cutter == 1000)
|
|
job->is_pano = 1;
|
|
|
|
continue;
|
|
}
|
|
if(!memcmp("CNTRL BUFFCNTRL", job->databuf + job->datalen+2, 15)) {
|
|
memcpy(buf, job->databuf + job->datalen + 32, 8);
|
|
job->buffcntrl = atoi(buf);
|
|
continue;
|
|
}
|
|
if(!memcmp("CNTRL OVERCOAT", job->databuf + job->datalen+2, 14)) {
|
|
if (ctx->supports_matte) {
|
|
memcpy(buf, job->databuf + job->datalen + 32, 8);
|
|
job->matte = atoi(buf);
|
|
} else {
|
|
WARNING("Printer FW does not support matte prints, using glossy mode\n");
|
|
}
|
|
|
|
if (job->matte && ctx->media_sticker) {
|
|
WARNING("Sticker media, forcing glossy mode\n");
|
|
job->matte = 0;
|
|
}
|
|
|
|
/* We'll insert our own later, if appropriate */
|
|
continue;
|
|
}
|
|
if(!memcmp("IMAGE MULTICUT", job->databuf + job->datalen+2, 14)) {
|
|
memcpy(buf, job->databuf + job->datalen + 32, 8);
|
|
job->multicut = atoi(buf);
|
|
/* Backend automatically handles rewind support, so
|
|
ignore application requests to use it. */
|
|
if (job->multicut > 400 && job->multicut < 1000)
|
|
job->multicut -= 400;
|
|
|
|
/* We'll insert this ourselves later. */
|
|
continue;
|
|
}
|
|
if(!memcmp("CNTRL FULL_CUTTER_SET", job->databuf + job->datalen+2, 21)) {
|
|
if (!ctx->supports_fullcut) {
|
|
WARNING("Printer FW does not support full cutter control!\n");
|
|
continue;
|
|
}
|
|
|
|
if (ctx->conn->type == P_DNP_DS820) {
|
|
if (j != 24) {
|
|
WARNING("Full cutter argument length incorrect, ignoring!\n");
|
|
continue;
|
|
}
|
|
} else if (j != 16) {
|
|
WARNING("Full cutter argument length incorrect, ignoring!\n");
|
|
continue;
|
|
} else if (!ctx->supports_adv_fullcut) {
|
|
if (job->databuf[job->datalen + 32 + 12] != '0' ||
|
|
job->databuf[job->datalen + 32 + 13] != '0' ||
|
|
job->databuf[job->datalen + 32 + 14] != '0') {
|
|
WARNING("Full cutter scrap setting not supported on this firmware, ignoring!\n");
|
|
continue;
|
|
}
|
|
}
|
|
// XXX enforce cut counts/sizes?
|
|
|
|
job->fullcut = 1;
|
|
}
|
|
if(!memcmp("IMAGE YPLANE", job->databuf + job->datalen + 2, 12)) {
|
|
uint32_t y_ppm; /* Pixels Per Meter */
|
|
|
|
/* Validate vertical resolution */
|
|
memcpy(&y_ppm, job->databuf + job->datalen + 32 + 42, sizeof(y_ppm));
|
|
y_ppm = le32_to_cpu(y_ppm);
|
|
|
|
switch (y_ppm) {
|
|
case 11808:
|
|
job->dpi = 300;
|
|
break;
|
|
case 13146:
|
|
job->dpi = 334;
|
|
break;
|
|
case 23615:
|
|
case 23616:
|
|
job->dpi = 600;
|
|
break;
|
|
default:
|
|
ERROR("Unrecognized printjob resolution (%u ppm)\n", y_ppm);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Validate horizontal size */
|
|
memcpy(&y_ppm, job->databuf + job->datalen + 32 + 18, sizeof(y_ppm));
|
|
y_ppm = le32_to_cpu(y_ppm);
|
|
if (y_ppm != ctx->native_width) {
|
|
ERROR("Incorrect horizontal resolution (%u), aborting!\n", y_ppm);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Capture number of rows */
|
|
memcpy(&y_ppm, job->databuf + job->datalen + 32 + 22, sizeof(y_ppm));
|
|
job->rows = le32_to_cpu(y_ppm);
|
|
}
|
|
if(!memcmp("CNTRL PRINTSPEED", job->databuf + job->datalen + 2, 16)) {
|
|
if (!ctx->supports_printspeed) {
|
|
WARNING("Printer does not support PRINTSPEED\n");
|
|
continue;
|
|
}
|
|
memcpy(buf, job->databuf + job->datalen + 32, 8);
|
|
job->printspeed = atoi(buf) / 10;
|
|
|
|
/* We'll insert this ourselves later. */
|
|
continue;
|
|
}
|
|
if(!memcmp("CNTRL CONT_PANORAMA", job->databuf + job->datalen + 2, 18)) {
|
|
if (!ctx->supports_contpano) {
|
|
ERROR("Printer does not support Continuous Panorama, aborting!\n");
|
|
continue;
|
|
}
|
|
memcpy(buf, job->databuf + job->datalen + 32 + 4, 4);
|
|
job->is_pano = atoi(buf);
|
|
}
|
|
|
|
/* This is the last block.. */
|
|
if(!memcmp("CNTRL START", job->databuf + job->datalen + 2, 11))
|
|
run = 0;
|
|
|
|
/* Add in the size of this chunk */
|
|
job->datalen += sizeof(struct dnpds40_cmd) + j;
|
|
}
|
|
|
|
/* Figure out the number of buffers we need. */
|
|
job->buf_needed = 1;
|
|
|
|
if (job->dpi == 600) {
|
|
switch(ctx->conn->type) {
|
|
case P_DNP_DS620:
|
|
if (job->multicut == MULTICUT_6x9 ||
|
|
job->multicut == MULTICUT_6x4_5X2)
|
|
job->buf_needed = 2;
|
|
break;
|
|
case P_DNP_DS80: /* DS80/CX-W */
|
|
if (job->matte && (job->multicut == MULTICUT_8xA4LEN ||
|
|
job->multicut == MULTICUT_8x4X3 ||
|
|
job->multicut == MULTICUT_8x8_8x4 ||
|
|
job->multicut == MULTICUT_8x6X2 ||
|
|
job->multicut == MULTICUT_8x12))
|
|
job->buf_needed = 2;
|
|
break;
|
|
case P_DNP_DS80D:
|
|
if (job->matte) {
|
|
int mcut = job->multicut;
|
|
|
|
if (mcut > MULTICUT_S_BACK)
|
|
mcut -= MULTICUT_S_BACK;
|
|
else if (mcut > MULTICUT_S_FRONT)
|
|
mcut -= MULTICUT_S_FRONT;
|
|
|
|
if (mcut == MULTICUT_8xA4LEN ||
|
|
mcut == MULTICUT_8x4X3 ||
|
|
mcut == MULTICUT_8x8_8x4 ||
|
|
mcut == MULTICUT_8x6X2 ||
|
|
mcut == MULTICUT_8x12)
|
|
job->buf_needed = 2;
|
|
|
|
if (mcut == MULTICUT_S_8x12 ||
|
|
mcut == MULTICUT_S_8x6X2 ||
|
|
mcut == MULTICUT_S_8x4X3)
|
|
job->buf_needed = 2;
|
|
}
|
|
break;
|
|
case P_DNP_DS820:
|
|
// Nothing; all sizes only need 1 buffer
|
|
break;
|
|
case P_CITIZEN_CW01:
|
|
job->buf_needed = 2;
|
|
break;
|
|
default: /* DS40/CX/RX1/CY/everything else */
|
|
if (job->matte) {
|
|
if (job->multicut == MULTICUT_6x8 ||
|
|
job->multicut == MULTICUT_6x9 ||
|
|
job->multicut == MULTICUT_6x4X2 ||
|
|
job->multicut == MULTICUT_5x7 ||
|
|
job->multicut == MULTICUT_5x3_5X2)
|
|
job->buf_needed = 2;
|
|
|
|
} else {
|
|
if (job->multicut == MULTICUT_6x8 ||
|
|
job->multicut == MULTICUT_6x9 ||
|
|
job->multicut == MULTICUT_6x4X2)
|
|
job->buf_needed = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (job->multicut >= MULTICUT_PANO_6x14)
|
|
{
|
|
int rval = dnp_panorama_splitjob(ctx, job, (struct dnpds40_printjob**)vjob);
|
|
|
|
/* Clean up original job regardless */
|
|
dnpds40_cleanup_job(job);
|
|
if (rval)
|
|
return rval;
|
|
|
|
/* And continue validating everything based on the 1st job */
|
|
job = (struct dnpds40_printjob*) *vjob;
|
|
}
|
|
|
|
parsed:
|
|
/* If we have no data.. don't bother */
|
|
if (!job->datalen) {
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Use the larger of the copy arguments */
|
|
if (!job->is_pano && job->common.copies < copies)
|
|
job->common.copies = copies;
|
|
|
|
/* Make sure advanced matte modes are supported */
|
|
if (job->matte > 100) {
|
|
if (!ctx->supports_advmatte) {
|
|
ERROR("Printer does not support advanced matte modes, aborting!\n");
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
if (ctx->partialmatte == 1) {
|
|
ctx->partialmatte = 2;
|
|
} else {
|
|
INFO("Partial matte lamination layer!\n");
|
|
ctx->partialmatte = 1;
|
|
}
|
|
} else {
|
|
if (ctx->partialmatte == 1)
|
|
ctx->partialmatte = 2;
|
|
}
|
|
|
|
/* Sanity check matte mode */
|
|
if ((job->matte == 21 || job->matte == 121) && !ctx->supports_finematte) {
|
|
WARNING("Printer FW does not support Fine Matte mode, downgrading to normal matte\n");
|
|
job->matte = 1;
|
|
} else if ((job->matte == 22 || job->matte == 122) && !ctx->supports_luster) {
|
|
WARNING("Printer FW does not support Luster mode, downgrading to normal matte\n");
|
|
job->matte = 1;
|
|
} else if (job->matte > 1 && !ctx->supports_advmatte) {
|
|
WARNING("Printer FW does not support advanced matte modes, downgrading to normal matte\n");
|
|
job->matte = 1;
|
|
}
|
|
|
|
/* QW410 only supports 0 and 3, supposedly. */
|
|
if (ctx->conn->type == P_DNP_QW410 && job->printspeed > 1) {
|
|
job->printspeed = 3;
|
|
}
|
|
|
|
/* Pick a sane default value for printspeed if not specified */
|
|
if (job->printspeed == -1 || job->printspeed > 3)
|
|
{
|
|
if (job->dpi == 600)
|
|
job->printspeed = 1;
|
|
else
|
|
job->printspeed = 0;
|
|
}
|
|
|
|
if (job->dpi == 600 && !ctx->supports_600dpi) {
|
|
ERROR("Printer does not support 600dpi!\n");
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* And sanity-check whatever value is there */
|
|
if (job->printspeed == 0 && job->dpi == 600) {
|
|
job->printspeed = 1;
|
|
} else if (job->printspeed >= 1 && job->dpi == 300) {
|
|
job->printspeed = 0;
|
|
}
|
|
|
|
/* Make sure MULTICUT is sane, most validation needs this */
|
|
if (!job->multicut && ctx->conn->type != P_CITIZEN_CW01) {
|
|
WARNING("Missing or illegal MULTICUT command!\n");
|
|
if (job->dpi == 300)
|
|
job->buf_needed = 1;
|
|
else
|
|
job->buf_needed = 2;
|
|
|
|
goto skip_checks;
|
|
}
|
|
|
|
/* Only DS80D supports Cut Paper types */
|
|
if (job->multicut > 100) {
|
|
if ( ctx->conn->type == P_DNP_DS80D) {
|
|
job->cut_paper = 1;
|
|
} else {
|
|
ERROR("Only DS80D supports cut-paper sizes!\n");
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
}
|
|
|
|
if (job->dpi == 334 && ctx->conn->type != P_CITIZEN_CW01)
|
|
{
|
|
ERROR("Illegal resolution (%u) for printer!\n", job->dpi);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* Sanity-check type vs loaded media */
|
|
if (job->multicut == 0)
|
|
goto skip_multicut;
|
|
|
|
/* Extra sanity checks */
|
|
if (ctx->media == 0) {
|
|
int status = dnpds40_query_status(ctx);
|
|
if (status > 1000) {
|
|
ERROR("User-Recoverable Printer Error: %d => %s, halting queue!\n", status, dnpds40_statuses(status));
|
|
return CUPS_BACKEND_STOP;
|
|
}
|
|
if (status > 2000) {
|
|
ERROR("Fatal Printer Hardware Error: %d => %s, halting queue!\n", status, dnpds40_statuses(status));
|
|
return CUPS_BACKEND_STOP;
|
|
}
|
|
}
|
|
|
|
if (job->multicut < 100) {
|
|
switch(ctx->media) {
|
|
case 150: // 4x6, QW410
|
|
if(job->multicut != MULTICUT_4x3 &&
|
|
job->multicut != MULTICUT_4x4 &&
|
|
job->multicut != MULTICUT_4x4_5 &&
|
|
// XXX job->multicut != MULTICUT_4x3X2 &&
|
|
job->multicut != MULTICUT_4x6) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
break;
|
|
case 151: // 4x8, QW410
|
|
if (job->multicut != MULTICUT_4x3 &&
|
|
job->multicut != MULTICUT_4x4 &&
|
|
job->multicut != MULTICUT_4x4_5 &&
|
|
job->multicut != MULTICUT_4x6 &&
|
|
// XXX job->multicut != MULTICUT_4x3X2 &&
|
|
// XXX job->multicut != MULTICUT_4x4X2 &&
|
|
job->multicut != MULTICUT_4x8) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
break;
|
|
case 160: // 4.5x6, QW410
|
|
if (job->multicut != MULTICUT_4_5x3 &&
|
|
job->multicut != MULTICUT_4_5x4 &&
|
|
job->multicut != MULTICUT_4_5x4_5 &&
|
|
// XXX job->multicut != MULTICUT_4_5x3X2 &&
|
|
job->multicut != MULTICUT_4_5x6) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
break;
|
|
case 161: // 4.5x8, QW410
|
|
if (job->multicut != MULTICUT_4_5x3 &&
|
|
job->multicut != MULTICUT_4_5x4 &&
|
|
job->multicut != MULTICUT_4_5x4_5 &&
|
|
job->multicut != MULTICUT_4_5x6 &&
|
|
// XXX job->multicut != MULTICUT_4_5x4X2 &&
|
|
// XXX job->multicut != MULTICUT_4_5x3X2 &&
|
|
job->multicut != MULTICUT_4_5x8) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
break;
|
|
case 200: //"5x3.5 (L)"
|
|
if (job->multicut != MULTICUT_5x3_5) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
break;
|
|
case 210: //"5x7 (2L)"
|
|
if (job->multicut != MULTICUT_5x3_5 && job->multicut != MULTICUT_5x7 &&
|
|
job->multicut != MULTICUT_5x3_5X2 && job->multicut != MULTICUT_5x5) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
/* Only 3.5x5 on 7x5 media can be rewound */
|
|
if (job->multicut == MULTICUT_5x3_5)
|
|
job->can_rewind = 1;
|
|
break;
|
|
case 300: //"6x4 (PC)"
|
|
if (job->multicut != MULTICUT_6x4) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
break;
|
|
case 310: //"6x8 (A5)"
|
|
if (job->multicut != MULTICUT_6x4 && job->multicut != MULTICUT_6x8 &&
|
|
job->multicut != MULTICUT_6x4X2 &&
|
|
job->multicut != MULTICUT_6x6 && job->multicut != 30) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
/* Only 6x4 on 6x8 media can be rewound */
|
|
if (job->multicut == MULTICUT_6x4)
|
|
job->can_rewind = 1;
|
|
break;
|
|
case 400: //"6x9 (A5W)"
|
|
if (job->multicut != MULTICUT_6x4 && job->multicut != MULTICUT_6x8 &&
|
|
job->multicut != MULTICUT_6x9 && job->multicut != MULTICUT_6x4X2 &&
|
|
job->multicut != MULTICUT_6x6 &&
|
|
job->multicut != MULTICUT_6x4_5 && job->multicut != MULTICUT_6x4_5X2) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
/* Only 6x4 or 6x4.5 on 6x9 media can be rewound */
|
|
if (job->multicut == MULTICUT_6x4 || job->multicut == MULTICUT_6x4_5)
|
|
job->can_rewind = 1;
|
|
break;
|
|
case 500: //"8x10"
|
|
if (ctx->conn->type == P_DNP_DS820 &&
|
|
(job->multicut == MULTICUT_8x7 || job->multicut == MULTICUT_8x9)) {
|
|
/* These are okay */
|
|
} else if (job->multicut < MULTICUT_8x10 || job->multicut == MULTICUT_8x12 ||
|
|
job->multicut == MULTICUT_8x6X2 || job->multicut >= MULTICUT_8x6_8x5 ) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* 8x4, 8x5 can be rewound */
|
|
if (job->multicut == MULTICUT_8x4 ||
|
|
job->multicut == MULTICUT_8x5)
|
|
job->can_rewind = 1;
|
|
break;
|
|
case 510: //"8x12"
|
|
if (job->multicut < MULTICUT_8x10 || (job->multicut > MULTICUT_8xA4LEN && !(job->multicut == MULTICUT_8x7 || job->multicut == MULTICUT_8x9))) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
|
|
/* 8x4, 8x5, 8x6 can be rewound */
|
|
if (job->multicut == MULTICUT_8x4 ||
|
|
job->multicut == MULTICUT_8x5 ||
|
|
job->multicut == MULTICUT_8x6)
|
|
job->can_rewind = 1;
|
|
break;
|
|
case 600: //"A4"
|
|
if (job->multicut < MULTICUT_A5 || job->multicut > MULTICUT_A4x5X2) {
|
|
ERROR("Incorrect media for job loaded (%u vs %u)\n", ctx->media, job->multicut);
|
|
dnpds40_cleanup_job(job);
|
|
return CUPS_BACKEND_CANCEL;
|
|
}
|
|
/* Only A4x4 and A5 can be rewound */
|
|
// XXX we can fake more with fancy multicuts...
|
|
if (job->multicut == MULTICUT_A4x5 ||
|
|
job< |