canonselphyneo: Add a new backend for the CP820/910/1000/1200 printers.

master
Solomon Peachy 2016-12-02 23:49:22 -05:00
parent 2fb2f56623
commit 2ae41d59ff
8 changed files with 471 additions and 4 deletions

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ shinkos2145
shinkos6145
shinkos6245
canonselphy
canonselphyneo
mitsu70x
dnpds40
citizencw01

View File

@ -30,7 +30,7 @@ CPPFLAGS += -DURI_PREFIX=\"$(BACKEND_NAME)\"
CFLAGS += -funit-at-a-time
# List of backends
BACKENDS = sonyupdr150 kodak6800 kodak1400 shinkos2145 shinkos1245 canonselphy mitsu70x kodak605 dnpds40 citizencw01 mitsu9550 shinkos6245 shinkos6145
BACKENDS = sonyupdr150 kodak6800 kodak1400 shinkos2145 shinkos1245 canonselphy mitsu70x kodak605 dnpds40 citizencw01 mitsu9550 shinkos6245 shinkos6145 canonselphyneo
# For the s6145 and mitsu70x backends
CPPFLAGS += -DUSE_DLOPEN

20
README
View File

@ -23,7 +23,7 @@
Supported Printers:
Canon SELPHY ES series
Canon SELPHY CP series (except CP820, CP910, CP1000, CP1200)
Canon SELPHY CP series
Kodak Professional 1400
Kodak 305 Photo Printer
Kodak 605 Photo Printer
@ -54,6 +54,7 @@
Mitsubishi CP-D707DW
Mitsubishi CP-9000DW, CP-9500DW, and CP-9600DW-S
Mitsubishi CP-9800DW, CP-9800DW-S, CP-9810DW, and CP-9820DW-S
Canon SELPHY CP820 and CP1000
Fujifilm ASK-300
Citizen CW-02 and CX2
Olmec OP900 and OP900II
@ -212,9 +213,22 @@
ES20, CP-220, CP520, CP530, CP600, CP810
NOT supported: (Printer does not need any special backend)
This backend does not support additional commands.
CP820, CP910, CP1000, CP1200
***************************************************************************
BACKEND=canonselphyneo
Verified supported printers:
CP1200
Untested, but expected to work:
CP910
Unknown VID/PIDs, but should work:
CP820, CP1000
This backend does not support additional commands.

440
backend_canonselphyneo.c Normal file
View File

@ -0,0 +1,440 @@
/*
* Canon SELPHY CPneo series CUPS backend -- libusb-1.0 version
*
* (c) 2016 Solomon Peachy <pizza@shaftnet.org>
*
* The latest version of this program can be found at:
*
* http://git.shaftnet.org/cgit/selphy_print.git
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* [http://www.gnu.org/licenses/gpl-3.0.html]
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#define BACKEND canonselphyneo_backend
#include "backend_common.h"
/* Exported */
#define USB_VID_CANON 0x04a9
#define USB_PID_CANON_CP820 XXX
#define USB_PID_CANON_CP910 0x327a
#define USB_PID_CANON_CP1000 XXX
#define USB_PID_CANON_CP1200 0x32b1
/* Header data structure */
struct selphyneo_hdr {
uint8_t data[32];
} __attribute((packed));
/* Readback data structure */
struct selphyneo_readback {
uint8_t data[12];
} __attribute((packed));
/* Private data stucture */
struct selphyneo_ctx {
struct libusb_device_handle *dev;
uint8_t endp_up;
uint8_t endp_down;
uint8_t *databuf;
uint32_t datalen;
};
static char *selphyneo_statuses(uint8_t sts)
{
switch(sts) {
case 0x01:
return "Idle";
case 0x02:
return "Feeding Paper";
case 0x04:
return "Printing YELLOW";
case 0x08:
return "Printing MAGENTA";
case 0x10:
return "Printing CYAN";
case 0x20:
return "Printing LAMINATE";
default:
return "Unknown state!";
}
}
static char *selphyneo_errors(uint8_t err)
{
switch(err) {
case 0x00:
return "None";
case 0x02:
return "Paper Feed";
case 0x03:
return "No Paper";
case 0x07:
return "No Ink";
case 0x0A:
return "Incorrect media for job";
default:
return "Unknown Error";
}
}
static void *selphyneo_init(void)
{
struct selphyneo_ctx *ctx = malloc(sizeof(struct selphyneo_ctx));
if (!ctx) {
ERROR("Memory Allocation Failure!\n");
return NULL;
}
memset(ctx, 0, sizeof(struct selphyneo_ctx));
return ctx;
}
extern struct dyesub_backend selphyneo_backend;
static void selphyneo_attach(void *vctx, struct libusb_device_handle *dev,
uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
{
struct selphyneo_ctx *ctx = vctx;
struct libusb_device *device;
struct libusb_device_descriptor desc;
UNUSED(jobid);
ctx->dev = dev;
ctx->endp_up = endp_up;
ctx->endp_down = endp_down;
device = libusb_get_device(dev);
libusb_get_device_descriptor(device, &desc);
}
static void selphyneo_teardown(void *vctx) {
struct selphyneo_ctx *ctx = vctx;
if (!ctx)
return;
if (ctx->databuf)
free(ctx->databuf);
free(ctx);
}
static int selphyneo_read_parse(void *vctx, int data_fd)
{
struct selphyneo_ctx *ctx = vctx;
struct selphyneo_hdr hdr;
int i, remain;
if (!ctx)
return CUPS_BACKEND_FAILED;
/* Read the header.. */
i = read(data_fd, &hdr, sizeof(hdr));
if (i != sizeof(hdr)) {
if (i == 0)
return CUPS_BACKEND_CANCEL;
ERROR("Read failed (%d/%d)\n",
i, (int)sizeof(hdr));
perror("ERROR: Read failed");
return CUPS_BACKEND_FAILED;
}
/* Determine job length */
switch(hdr.data[18]) {
case 0x50:
remain = 1872 * 1248 * 3;
break;
case 0x4c:
remain = 1536 * 1104 * 3;
break;
case 0x43:
remain = 1088 * 668 * 3;
break;
default:
ERROR("Unknown print size! (%02x/%02x/%02x/%02x)\n",
hdr.data[10], hdr.data[24], hdr.data[28], hdr.data[29]);
return CUPS_BACKEND_CANCEL;
}
/* Allocate a buffer */
ctx->datalen = 0;
ctx->databuf = malloc(remain + sizeof(hdr));
if (!ctx->databuf) {
ERROR("Memory allocation failure!\n");
return CUPS_BACKEND_FAILED;
}
/* Store the read-in header */
memcpy(ctx->databuf, &hdr, sizeof(hdr));
ctx->datalen += sizeof(hdr);
/* Read in data */
while (remain > 0) {
i = read(data_fd, ctx->databuf + ctx->datalen, remain);
if (i < 0)
return CUPS_BACKEND_CANCEL;
remain -= i;
ctx->datalen += i;
}
return CUPS_BACKEND_OK;
}
static int selphyneo_main_loop(void *vctx, int copies) {
struct selphyneo_ctx *ctx = vctx;
struct selphyneo_readback rdback;
int ret, num;
/* Read in the printer status to clear last state */
ret = read_data(ctx->dev, ctx->endp_up,
(uint8_t*) &rdback, sizeof(rdback), &num);
top:
INFO("Waiting for printer idle\n");
do {
ret = read_data(ctx->dev, ctx->endp_up,
(uint8_t*) &rdback, sizeof(rdback), &num);
if (ret < 0)
return CUPS_BACKEND_FAILED;
if (rdback.data[0] == 0x01)
break;
INFO("Printer state: %s\n", selphyneo_statuses(rdback.data[0]));
switch (rdback.data[2]) {
case 0x00:
break;
case 0x0A:
ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]);
return CUPS_BACKEND_CANCEL;
default:
ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]);
return CUPS_BACKEND_STOP;
}
sleep(1);
} while(1);
// XXX dump over markers
#if 0
ATTR("marker-colors=#00FFFF#FF00FF#FFFF00\n");
ATTR("marker-high-levels=100\n");
ATTR("marker-low-levels=10\n");
ATTR("marker-names='%s'\n", ctx->printer->pgcode_names? ctx->printer->pgcode_names(rdbuf[ctx->printer->paper_code_offset]) : "Unknown");
ATTR("marker-types=ribbonWax\n");
ATTR("marker-levels=%d\n", -3); /* ie Unknown but OK */
#endif
INFO("Sending spool data\n");
/* Send the data over in 256K chunks */
{
int chunk = 256*1024;
int sent = 0;
while (chunk > 0) {
if ((ret = send_data(ctx->dev, ctx->endp_down,
ctx->databuf + sent, chunk)))
return CUPS_BACKEND_FAILED;
sent += chunk;
chunk = ctx->datalen - sent;
if (chunk > 256*1024)
chunk = 256*1024;
}
}
/* Read in the printer status to clear last state */
ret = read_data(ctx->dev, ctx->endp_up,
(uint8_t*) &rdback, sizeof(rdback), &num);
INFO("Waiting for printer acknowledgement\n");
do {
ret = read_data(ctx->dev, ctx->endp_up,
(uint8_t*) &rdback, sizeof(rdback), &num);
if (ret < 0)
return CUPS_BACKEND_FAILED;
if (rdback.data[0] == 0x01)
break;
INFO("Printer state: %s\n", selphyneo_statuses(rdback.data[0]));
switch (rdback.data[2]) {
case 0x00:
break;
case 0x0A:
ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]);
return CUPS_BACKEND_CANCEL;
default:
ERROR("Printer error: %s (%02x)\n", selphyneo_errors(rdback.data[2]), rdback.data[2]);
return CUPS_BACKEND_STOP;
}
if (rdback.data[0] > 0x02 && fast_return) {
INFO("Fast return mode enabled.\n");
break;
}
sleep(1);
} while(1);
/* Clean up */
if (terminate)
copies = 1;
INFO("Print complete (%d copies remaining)\n", copies - 1);
if (copies && --copies) {
goto top;
}
return CUPS_BACKEND_OK;
}
static int selphyneo_cmdline_arg(void *vctx, int argc, char **argv)
{
struct selphyneo_ctx *ctx = vctx;
int i, j = 0;
if (!ctx)
return -1;
while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL)) >= 0) {
switch(i) {
GETOPT_PROCESS_GLOBAL
}
if (j) return j;
}
return 0;
}
struct dyesub_backend canonselphyneo_backend = {
.name = "Canon SELPHY CPneo",
.version = "0.01wip",
.uri_prefix = "canonselphyneo",
.cmdline_arg = selphyneo_cmdline_arg,
.init = selphyneo_init,
.attach = selphyneo_attach,
.teardown = selphyneo_teardown,
.read_parse = selphyneo_read_parse,
.main_loop = selphyneo_main_loop,
.devices = {
// { USB_VID_CANON, USB_PID_CANON_CP820, P_CP910, ""},
{ USB_VID_CANON, USB_PID_CANON_CP910, P_CP910, ""},
// { USB_VID_CANON, USB_PID_CANON_CP1000, P_CP910, ""},
{ USB_VID_CANON, USB_PID_CANON_CP1200, P_CP910, ""},
{ 0, 0, 0, ""}
}
};
/*
***************************************************************************
Stream formats and readback codes for supported printers
***************************************************************************
Selphy CP820/CP910/CP1000/CP1200:
Radically different spool file format! 300dpi, same print sizes, but also
adding a 50x50mm sticker and 22x17.3mm ministickers, though I think the
driver treats all of those as 'C' sizes for printing purposes.
32-byte header:
0f 00 00 40 00 00 00 00 00 00 00 00 00 00 01 00
01 00 ?? 00 00 00 00 00 XX 04 00 00 WW ZZ 00 00
?? == 50 (P)
== 4c (L)
== 43 (C)
XX == e0 (P)
80 (L)
40 (C)
WW == 50 (P)
c0 (L)
9c (C)
ZZ == 07 (P)
05 (L)
02 (C)
P == 7008800 == 2336256 * 3 + 32 (1872*1248)
L == 5087264 == 1695744 * 3 + 32 (1536*1104)
C == 2180384 == 726784 * 3 + 32 (1088*668)
It is worth mentioning that the image payload is Y'CbCr rather than the
traditional YMC (or even BGR) of other dyseubs. Our best guess is that
we need to use the JPEG coefficients, although we realistically have
no way of confirming this.
It is hoped that the printers do support YMC data, but as of yet we
have no way of determining if this is possible.
Data Readback:
XX 00 YY 00 00 00 ZZ 00 00 00 00 00
XX == Status
01 Idle
02 Feeding Paper
04 Printing Y
08 Printing M
10 Printing C
20 Printing L
YY == Error
00 None
02 No Paper (?)
03 No Paper
07 No Ink
0A Media/Job mismatch
ZZ == Media?
01
10
11
Also, the first time a readback happens after plugging in the printer:
34 44 35 31 01 00 01 00 01 00 45 00 "4D51" ...??
*/

View File

@ -562,6 +562,7 @@ extern struct dyesub_backend shinkos2145_backend;
extern struct dyesub_backend shinkos6145_backend;
extern struct dyesub_backend shinkos6245_backend;
extern struct dyesub_backend canonselphy_backend;
extern struct dyesub_backend canonselphyneo_backend;
extern struct dyesub_backend mitsu70x_backend;
extern struct dyesub_backend mitsu9550_backend;
extern struct dyesub_backend dnpds40_backend;
@ -569,6 +570,7 @@ extern struct dyesub_backend cw01_backend;
static struct dyesub_backend *backends[] = {
&canonselphy_backend,
&canonselphyneo_backend,
&kodak6800_backend,
&kodak605_backend,
&kodak1400_backend,

View File

@ -95,6 +95,7 @@ enum {
P_CP790,
P_CP_XXX,
P_CP10,
P_CP910,
P_KODAK_6800,
P_KODAK_6850,
P_KODAK_1400_805,

View File

@ -78,6 +78,12 @@
# Canon SELPHY CP900
0x04a9 0x3255 blacklist
# Canon SELPHY CP910
0x04a9 0x327a blacklist
# Canon SELPHY CP1200
0x04a9 0x32b1 blacklist
# Canon SELPHY ES1
0x04a9 0x3141 blacklist

BIN
testjobs/canon_cp1200-p.raw (Stored with Git LFS) Normal file

Binary file not shown.