2013-12-03 22:21:44 -05:00
|
|
|
/*
|
|
|
|
* DNP DS40/DS80 Photo Printer CUPS backend -- libusb-1.0 version
|
|
|
|
*
|
|
|
|
* (c) 2013 Solomon Peachy <pizza@shaftnet.org>
|
|
|
|
*
|
|
|
|
* 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]
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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"
|
|
|
|
|
|
|
|
#define USB_VID_DNP 0x1343
|
|
|
|
#define USB_PID_DNP_DS40 0x0003
|
2013-12-04 07:37:11 -05:00
|
|
|
#define USB_PID_DNP_DS80 0x0004
|
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;
|
|
|
|
|
|
|
|
int type;
|
|
|
|
|
|
|
|
uint8_t *databuf;
|
|
|
|
int datalen;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct dnpds40_cmd {
|
|
|
|
uint8_t esc;
|
|
|
|
uint8_t p;
|
|
|
|
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)
|
|
|
|
{
|
2013-12-10 08:54:07 -05:00
|
|
|
#if 0
|
2013-12-04 07:37:11 -05:00
|
|
|
uint i;
|
2013-12-10 08:54:07 -05:00
|
|
|
#endif
|
2013-12-03 22:21:44 -05:00
|
|
|
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)
|
|
|
|
snprintf((char*)cmd->arg3, 8, "%08d", arg3_len);
|
2013-12-04 07:37:11 -05:00
|
|
|
|
2013-12-10 08:54:07 -05:00
|
|
|
#if 0
|
2013-12-08 07:14:27 -05:00
|
|
|
DEBUG("command: ");
|
2013-12-04 07:37:11 -05:00
|
|
|
for (i = 0 ; i < sizeof(*cmd); i++) {
|
|
|
|
DEBUG2("%02x ", *(((uint8_t*)cmd)+i));
|
|
|
|
}
|
|
|
|
DEBUG2("\n");
|
2013-12-10 08:54:07 -05:00
|
|
|
#endif
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dnpds40_cleanup_string(char *start, int len)
|
|
|
|
{
|
|
|
|
char *ptr = strchr(start, 0x0d);
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
if (ptr && (ptr - start < len))
|
2013-12-03 22:21:44 -05:00
|
|
|
*ptr = 0x00; /* If there is a <CR>, terminate there */
|
|
|
|
else
|
|
|
|
*(start + len - 1) = 0x00; /* force null-termination */
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *dnpds40_media_types(char *str)
|
|
|
|
{
|
|
|
|
char tmp[4];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
memcpy(tmp, str + 4, 3);
|
|
|
|
tmp[3] = 0;
|
|
|
|
|
|
|
|
i = atoi(tmp);
|
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
|
2013-12-03 22:32:08 -05:00
|
|
|
static char *dnpds40_statuses(char *str)
|
|
|
|
{
|
|
|
|
char tmp[6];
|
|
|
|
int i;
|
|
|
|
memcpy(tmp, str, 5);
|
|
|
|
tmp[5] = 0;
|
|
|
|
|
|
|
|
i = atoi(tmp);
|
2013-12-10 08:54:07 -05:00
|
|
|
|
2013-12-03 22:32:08 -05:00
|
|
|
switch (i) {
|
|
|
|
case 0: return "Idle";
|
|
|
|
case 1: return "Printing";
|
|
|
|
case 500: return "Cooling Print Head";
|
|
|
|
case 510: return "Cooling Paper Motor";
|
|
|
|
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 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "Unkown type";
|
|
|
|
}
|
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
static int dnpds40_do_cmd(struct dnpds40_ctx *ctx,
|
|
|
|
struct dnpds40_cmd *cmd,
|
|
|
|
uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = send_data(ctx->dev, ctx->endp_down,
|
|
|
|
(uint8_t*)cmd, sizeof(*cmd))))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (*data && len)
|
|
|
|
if ((ret = send_data(ctx->dev, ctx->endp_down,
|
|
|
|
data, len)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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 */
|
|
|
|
ret = libusb_bulk_transfer(ctx->dev, ctx->endp_up,
|
|
|
|
(uint8_t*)tmp,
|
|
|
|
8,
|
|
|
|
&num,
|
|
|
|
5000);
|
|
|
|
|
|
|
|
if (ret < 0 || num != 8) {
|
|
|
|
ERROR("Failure to receive data from printer (libusb error %d: (%d/%d from 0x%02x))\n", ret, num, 8, ctx->endp_up);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = atoi(tmp); /* Length of payload in bytes, possibly padded */
|
2013-12-10 09:36:18 -05:00
|
|
|
#if 0
|
2013-12-04 10:31:41 -05:00
|
|
|
DEBUG("readback: '%s' len %d/%d\n", (char*) tmp, i, num);
|
2013-12-10 09:36:18 -05:00
|
|
|
#endif
|
2013-12-04 10:31:41 -05:00
|
|
|
|
|
|
|
respbuf = malloc(i);
|
|
|
|
|
|
|
|
/* Read in the actual response */
|
|
|
|
memset(respbuf, 0, i);
|
|
|
|
ret = libusb_bulk_transfer(ctx->dev, ctx->endp_up,
|
|
|
|
respbuf,
|
|
|
|
i,
|
|
|
|
&num,
|
|
|
|
5000);
|
|
|
|
|
|
|
|
if (ret < 0 || num != i) {
|
|
|
|
ERROR("Failure to receive data from printer (libusb error %d: (%d/%d from 0x%02x))\n", ret, num, i, ctx->endp_up);
|
|
|
|
|
|
|
|
free(respbuf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-12-10 09:36:18 -05:00
|
|
|
#if 0
|
2013-12-04 10:31:41 -05:00
|
|
|
DEBUG("response: ");
|
|
|
|
for (i = 0 ; i < num; i++) {
|
|
|
|
DEBUG2("%02x ", respbuf[i]);
|
|
|
|
}
|
|
|
|
DEBUG2("\n");
|
2013-12-10 09:36:18 -05:00
|
|
|
#endif
|
2013-12-04 10:31:41 -05:00
|
|
|
|
|
|
|
*len = num;
|
|
|
|
return respbuf;
|
|
|
|
}
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
static void *dnpds40_init(void)
|
|
|
|
{
|
|
|
|
struct dnpds40_ctx *ctx = malloc(sizeof(struct dnpds40_ctx));
|
|
|
|
if (!ctx)
|
|
|
|
return NULL;
|
|
|
|
memset(ctx, 0, sizeof(struct dnpds40_ctx));
|
|
|
|
|
|
|
|
ctx->type = P_ANY;
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
2013-12-10 08:54:07 -05:00
|
|
|
static void dnpds40_attach(void *vctx, struct libusb_device_handle *dev,
|
2013-12-03 22:21:44 -05:00
|
|
|
uint8_t endp_up, uint8_t endp_down, uint8_t jobid)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* Map out device type */
|
|
|
|
if (desc.idProduct == USB_PID_DNP_DS40)
|
|
|
|
ctx->type = P_DNP_DS40;
|
|
|
|
else
|
|
|
|
ctx->type = P_DNP_DS80;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dnpds40_teardown(void *vctx) {
|
|
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ctx->databuf)
|
|
|
|
free(ctx->databuf);
|
|
|
|
free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_PRINTJOB_LEN (27927714+1024) // Add a little bit of padding
|
|
|
|
|
|
|
|
static int dnpds40_read_parse(void *vctx, int data_fd) {
|
|
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
ctx->databuf = malloc(MAX_PRINTJOB_LEN);
|
|
|
|
if (!ctx->databuf) {
|
|
|
|
ERROR("Memory allocation failure!\n");
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
i = read(data_fd, ctx->databuf, sizeof(struct dnpds40_cmd));
|
|
|
|
if (i < 0)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
ctx->databuf += i;
|
|
|
|
|
|
|
|
// XXX no way to figure out print job length without parsing stream
|
|
|
|
// until we get to the plane data
|
|
|
|
|
|
|
|
if (ctx->databuf[0] != 0x1b ||
|
|
|
|
ctx->databuf[1] != 0x50) {
|
|
|
|
ERROR("Unrecognized header data format!\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
while((i = read(data_fd, ctx->databuf + ctx->datalen, 4096)) > 0) {
|
|
|
|
ctx->datalen += i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dnpds40_main_loop(void *vctx, int copies) {
|
|
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
int ret;
|
2013-12-10 21:53:46 -05:00
|
|
|
struct dnpds40_cmd cmd;
|
|
|
|
uint8_t *resp = NULL;
|
|
|
|
int len = 0;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return 1;
|
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
top:
|
2013-12-08 07:14:27 -05:00
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
if (resp) free(resp);
|
2013-12-08 07:14:27 -05:00
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
/* Query status */
|
|
|
|
dnpds40_build_cmd(&cmd, "STATUS", "", 0);
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
/* If we're not idle */
|
|
|
|
if (strcmp("00000", (char*)resp)) {
|
|
|
|
if (!strcmp("00001", (char*)resp) ||
|
|
|
|
!strcmp("00500", (char*)resp) ||
|
|
|
|
!strcmp("00510", (char*)resp)) {
|
|
|
|
INFO("Printer busy, retrying...\n");
|
|
|
|
/* We're printing or cooling still.. */
|
|
|
|
sleep(1);
|
|
|
|
goto top;
|
|
|
|
}
|
|
|
|
ERROR("Printer Status: %s\n", dnpds40_statuses((char*)resp));
|
|
|
|
return 1;
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
2013-12-10 21:53:46 -05:00
|
|
|
|
|
|
|
/* Query buffer state */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "FREE_PBUFFER", 0);
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
/* We need a minumum of two buffers to be safe everywhere */
|
|
|
|
if (!strcmp("FBP00", (char*)resp) ||
|
|
|
|
!strcmp("FBP01", (char*)resp)) {
|
|
|
|
/* We don't have enough buffers */
|
|
|
|
INFO("Insufficient printer buffers, retrying...\n");
|
|
|
|
sleep(1);
|
|
|
|
goto top;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX for now, dump the whole spool file over. Parse first?
|
|
|
|
|
|
|
|
DEBUG("Sending %d bytes to printer\n", ctx->datalen);
|
|
|
|
if ((ret = send_data(ctx->dev, ctx->endp_down,
|
|
|
|
ctx->databuf, ctx->datalen)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Clean up */
|
|
|
|
if (terminate)
|
|
|
|
copies = 1;
|
|
|
|
|
|
|
|
INFO("Print complete (%d remaining)\n", copies);
|
|
|
|
|
|
|
|
if (copies && --copies) {
|
|
|
|
goto top;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resp) free(resp);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dnpds40_get_info(struct dnpds40_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct dnpds40_cmd cmd;
|
2013-12-04 10:31:41 -05:00
|
|
|
uint8_t *resp;
|
|
|
|
int len = 0;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
/* Get Firmware Version */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "FVER", 0);
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
INFO("Firmware Version: '%s'\n", (char*)resp);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
free(resp);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
/* Get Sensor Info */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "SENSOR", 0);
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
INFO("Sensor Info: '%s'\n", (char*)resp);
|
2013-12-10 19:42:28 -05:00
|
|
|
// XXX parse this out. Each token is 'XXX-###' delimited by '; '
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
free(resp);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
/* Get Media Info */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "MEDIA", 0);
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
INFO("Media Type: '%s'\n", (char*)resp);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-10 08:54:07 -05:00
|
|
|
INFO(" %s\n", dnpds40_media_types((char*)resp));
|
|
|
|
switch (*(resp+3)) {
|
2013-12-03 22:21:44 -05:00
|
|
|
case '1':
|
|
|
|
INFO(" Stickier paper\n");
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
INFO(" Standard paper\n");
|
|
|
|
break;
|
|
|
|
default:
|
2013-12-04 10:31:41 -05:00
|
|
|
INFO(" Unknown paper(%c)\n", *(resp+4));
|
2013-12-03 22:21:44 -05:00
|
|
|
break;
|
|
|
|
}
|
2013-12-10 08:54:07 -05:00
|
|
|
switch (*(resp+6)) {
|
2013-12-03 22:21:44 -05:00
|
|
|
case '1':
|
|
|
|
INFO(" With mark\n");
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
INFO(" Without mark\n");
|
|
|
|
break;
|
|
|
|
default:
|
2013-12-04 10:31:41 -05:00
|
|
|
INFO(" Unknown mark(%c)\n", *(resp+7));
|
2013-12-03 22:21:44 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
free(resp);
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
/* Get Media remaining */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "MQTY", 0);
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
2013-12-04 07:37:11 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
INFO("Prints Remaining: '%s'\n", (char*)resp + 4);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
free(resp);
|
2013-12-03 22:21:44 -05:00
|
|
|
|
2013-12-10 08:54:07 -05:00
|
|
|
/* Get Horizonal resolution */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_H", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
2013-12-10 09:36:18 -05:00
|
|
|
INFO("Horizontal Resolution: '%s' dpi\n", (char*)resp + 3);
|
2013-12-10 08:54:07 -05:00
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
/* Get Vertical resolution */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "RESOLUTION_V", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
2013-12-10 09:36:18 -05:00
|
|
|
INFO("Vertical Resolution: '%s' dpi\n", (char*)resp + 3);
|
2013-12-10 08:54:07 -05:00
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
2013-12-10 19:42:28 -05:00
|
|
|
/* Get Media Color offset */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "MCOLOR", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("Media Color Offset: '%02x%02x%02x%02x'\n", *(resp+2), *(resp+3),
|
|
|
|
*(resp+4), *(resp+5));
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
/* Get Media Lot */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "MLOT", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("Media Lot Code: '%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x'\n",
|
|
|
|
*(resp+2), *(resp+3), *(resp+4), *(resp+5), *(resp+6), *(resp+7),
|
|
|
|
*(resp+8), *(resp+9), *(resp+10), *(resp+11), *(resp+12), *(resp+13));
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-03 22:32:08 -05:00
|
|
|
static int dnpds40_get_status(struct dnpds40_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct dnpds40_cmd cmd;
|
2013-12-04 10:31:41 -05:00
|
|
|
uint8_t *resp;
|
|
|
|
int len = 0;
|
2013-12-03 22:32:08 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
/* Generate command */
|
2013-12-03 22:32:08 -05:00
|
|
|
dnpds40_build_cmd(&cmd, "STATUS", "", 0);
|
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
2013-12-03 22:32:08 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
2013-12-04 07:37:11 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
INFO("Printer Status: %s => %s\n", (char*)resp, dnpds40_statuses((char*)resp));
|
2013-12-03 22:32:08 -05:00
|
|
|
|
2013-12-04 10:31:41 -05:00
|
|
|
free(resp);
|
2013-12-03 22:32:08 -05:00
|
|
|
|
2013-12-10 08:54:07 -05:00
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "INFO", "FREE_PBUFFER", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("Free Buffers: '%s'\n", (char*)resp + 3);
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
2013-12-03 22:32:08 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-10 19:42:28 -05:00
|
|
|
static int dnpds40_get_counters(struct dnpds40_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct dnpds40_cmd cmd;
|
|
|
|
uint8_t *resp;
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_LIFE", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("Lifetime Counter: '%s'\n", (char*)resp+2);
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_A", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("A Counter: '%s'\n", (char*)resp+2);
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_B", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("B Counter: '%s'\n", (char*)resp+2);
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_P", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("P Counter: '%s'\n", (char*)resp+2);
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_M", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("M Counter: '%s'\n", (char*)resp+2);
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "MNT_RD", "COUNTER_MATTE", 0);
|
|
|
|
|
|
|
|
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
|
|
|
|
if (!resp)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dnpds40_cleanup_string((char*)resp, len);
|
|
|
|
|
|
|
|
INFO("Matte Counter: '%s'\n", (char*)resp+2);
|
|
|
|
|
|
|
|
free(resp);
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-10 21:53:46 -05:00
|
|
|
static int dnpds40_clear_counter(struct dnpds40_ctx *ctx, char counter)
|
|
|
|
{
|
|
|
|
struct dnpds40_cmd cmd;
|
|
|
|
char msg[4];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Generate command */
|
|
|
|
dnpds40_build_cmd(&cmd, "MNT_WT", "COUNTER_CLR", 0);
|
|
|
|
msg[0] = 'C';
|
|
|
|
msg[1] = counter;
|
|
|
|
msg[2] = '\r';
|
|
|
|
msg[3] = 0;
|
|
|
|
|
|
|
|
if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)msg, sizeof(msg))))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
static void dnpds40_cmdline(char *caller)
|
|
|
|
{
|
2013-12-10 19:42:28 -05:00
|
|
|
DEBUG("\t\t%s [ -qs | -qi | -qc ]\n", caller);
|
2013-12-10 21:53:46 -05:00
|
|
|
DEBUG("\t\t%s [ -cca | -ccb | -ccm \n", caller);
|
2013-12-03 22:21:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dnpds40_cmdline_arg(void *vctx, int run, char *arg1, char *arg2)
|
|
|
|
{
|
|
|
|
struct dnpds40_ctx *ctx = vctx;
|
|
|
|
|
|
|
|
UNUSED(arg2);
|
|
|
|
|
|
|
|
if (!run || !ctx)
|
|
|
|
return (!strcmp("-qs", arg1) ||
|
2013-12-10 19:42:28 -05:00
|
|
|
!strcmp("-qi", arg1) ||
|
2013-12-10 21:53:46 -05:00
|
|
|
!strcmp("-qc", arg1) ||
|
|
|
|
!strcmp("-cca", arg1) ||
|
|
|
|
!strcmp("-ccb", arg1) ||
|
|
|
|
!strcmp("-ccm", arg1));
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
if (!strcmp("-qs", arg1))
|
|
|
|
return dnpds40_get_status(ctx);
|
|
|
|
if (!strcmp("-qi", arg1))
|
|
|
|
return dnpds40_get_info(ctx);
|
2013-12-10 19:42:28 -05:00
|
|
|
if (!strcmp("-qc", arg1))
|
|
|
|
return dnpds40_get_counters(ctx);
|
2013-12-10 21:53:46 -05:00
|
|
|
if (!strcmp("-cca", arg1))
|
|
|
|
return dnpds40_clear_counter(ctx, 'A');
|
|
|
|
if (!strcmp("-ccb", arg1))
|
|
|
|
return dnpds40_clear_counter(ctx, 'B');
|
|
|
|
if (!strcmp("-ccm", arg1))
|
|
|
|
return dnpds40_clear_counter(ctx, 'M');
|
2013-12-10 19:42:28 -05:00
|
|
|
|
2013-12-03 22:21:44 -05:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Exported */
|
|
|
|
struct dyesub_backend dnpds40_backend = {
|
|
|
|
.name = "DNP DS40/DS80",
|
2013-12-10 21:53:46 -05:00
|
|
|
.version = "0.09",
|
2013-12-03 22:21:44 -05:00
|
|
|
.uri_prefix = "dnpds40",
|
|
|
|
.cmdline_usage = dnpds40_cmdline,
|
|
|
|
.cmdline_arg = dnpds40_cmdline_arg,
|
|
|
|
.init = dnpds40_init,
|
|
|
|
.attach = dnpds40_attach,
|
|
|
|
.teardown = dnpds40_teardown,
|
|
|
|
.read_parse = dnpds40_read_parse,
|
|
|
|
.main_loop = dnpds40_main_loop,
|
2013-12-10 08:54:07 -05:00
|
|
|
.devices = {
|
2013-12-04 07:37:11 -05:00
|
|
|
{ USB_VID_DNP, USB_PID_DNP_DS40, P_DNP_DS40, ""},
|
|
|
|
{ USB_VID_DNP, USB_PID_DNP_DS80, P_DNP_DS80, ""},
|
2013-12-03 22:21:44 -05:00
|
|
|
{ 0, 0, 0, ""}
|
|
|
|
}
|
|
|
|
};
|
2013-12-03 23:07:19 -05:00
|
|
|
|
|
|
|
/* DNP DS40 Windows Driver printer spool format:
|
|
|
|
|
|
|
|
NOTE: This backend (and gutenprint) do *NOT* use this format.
|
|
|
|
|
|
|
|
UNKNOWN variables/offsets:
|
|
|
|
|
|
|
|
- 300 vs 600dpi selection
|
|
|
|
- number of copies
|
|
|
|
- lamination type
|
|
|
|
- media type
|
|
|
|
|
|
|
|
4x6, 300dpi, 1 copy, 0 sharpen, glossy
|
|
|
|
|
|
|
|
Page header:
|
|
|
|
|
|
|
|
01 00 01 00 <- page setup?
|
|
|
|
28 58 24 00 <- Total plane len == 40 + x*y + 1024 (2381864)
|
|
|
|
00 00 00 00 <- ??
|
|
|
|
|
|
|
|
Plane header (ie one for each plane)
|
|
|
|
|
|
|
|
28 00 00 00
|
|
|
|
80 07 00 00 <- X res (1920) ( = 6.4" @ 300dpi)
|
|
|
|
d8 04 00 00 <- Y res (1240) ( = 4.13" @ 300dpi)
|
|
|
|
01 00 08 00
|
|
|
|
00 00 00 00
|
|
|
|
00 00 00 00
|
|
|
|
20 2e 00 00 <- 11808 = ??
|
|
|
|
20 2e 00 00 <- 11808
|
|
|
|
00 01 00 00
|
|
|
|
00 00 00 00
|
|
|
|
|
|
|
|
[ folowed by 256 entries of color mapping starting with 0xff -> ff ff ff 00 ]
|
|
|
|
[ followed by x*y bytes of plane data ]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5x7, "600x600dpi", 2 copies, 0 sharpen, matte
|
|
|
|
|
|
|
|
Page header:
|
|
|
|
|
|
|
|
02 02 02 00 <- page setup ??
|
|
|
|
28 4a 7d 00 <- Total plane len == 40 + x*y + 1024
|
|
|
|
02 00 00 00 <- ??
|
|
|
|
|
|
|
|
Plane header (ie one for each plane)
|
|
|
|
|
|
|
|
28 00 00 00
|
|
|
|
80 07 00 00 <- X res (1920) ( = 6.4" @ 300dpi)
|
|
|
|
b4 10 00 00 <- Y res (4276) ( =~ 7.13" @ 600 dpi )
|
|
|
|
01 00 08 00
|
|
|
|
00 00 00 00
|
|
|
|
00 00 00 00
|
|
|
|
40 5c 00 00 <- 23615 = ?
|
|
|
|
40 5c 00 00 <- 23615
|
|
|
|
00 01 00 00
|
|
|
|
00 00 00 00
|
|
|
|
|
|
|
|
[ folowed by 256 entries of color mapping starting with 0xff -> ff ff ff 00 ]
|
|
|
|
[ followed by x*y bytes of plane data ]
|
|
|
|
|
|
|
|
*/
|