dnp_ds40: Many changes:

* Parse printjob properly, read it in chunks
 * Detect 300/600dpi mode based on printer header
 * Be smarter about in-printer copy generation
 * Send printjob to printer in chunks
 * If printer has sufficient buffers for our print, spool it immediately
   even if we're still printing another job
 * Fix some buffer overflows
 * Move media detection and remanining prints into status query page
This commit is contained in:
Solomon Peachy 2013-12-17 00:36:06 -05:00
parent 1e3d3b14c4
commit bb4958d5a2

View file

@ -49,6 +49,10 @@ struct dnpds40_ctx {
int type;
int y_res;
int buf_needed;
uint8_t *qty_offset;
uint8_t *databuf;
int datalen;
};
@ -319,34 +323,71 @@ static void dnpds40_teardown(void *vctx) {
static int dnpds40_read_parse(void *vctx, int data_fd) {
struct dnpds40_ctx *ctx = vctx;
int i;
int i, j;
char buf[9] = { 0 };
if (!ctx)
return 1;
ctx->datalen = 0;
ctx->databuf = malloc(MAX_PRINTJOB_LEN);
if (!ctx->databuf) {
ERROR("Memory allocation failure!\n");
return 2;
}
i = read(data_fd, ctx->databuf, sizeof(struct dnpds40_cmd));
if (i < 0)
return i;
ctx->datalen += 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;
}
/* Read in command header */
while (1) {
i = read(data_fd, ctx->databuf + ctx->datalen,
sizeof(struct dnpds40_cmd));
if (i < 0)
return i;
if (i == 0)
break;
if (i < (int) sizeof(struct dnpds40_cmd))
return 1;
while((i = read(data_fd, ctx->databuf + ctx->datalen, 4096)) > 0) {
ctx->datalen += i;
if (ctx->databuf[ctx->datalen + 0] != 0x1b ||
ctx->databuf[ctx->datalen + 1] != 0x50) {
ERROR("Unrecognized header data format @%d!\n", ctx->datalen);
return 1;
}
/* Parse out length of data chunk, if any */
memcpy(buf, ctx->databuf + ctx->datalen + 24, 8);
j = atoi(buf);
/* Read in data chunk */
i = read(data_fd, ctx->databuf + ctx->datalen + sizeof(struct dnpds40_cmd),
j);
if (i < 0)
return i;
if (i != j)
return 1;
/* Check for some offsets */
if(!memcmp("CNTRL QTY", ctx->databuf + ctx->datalen+2, 9)) {
ctx->qty_offset = ctx->databuf + ctx->datalen + 32;
}
if(!memcmp("IMAGE YPLANE", ctx->databuf + ctx->datalen + 2, 12)) {
uint32_t x;
memcpy(&x, ctx->databuf + ctx->datalen + 32 + 42, sizeof(x));
x = le32_to_cpu(x);
if (x == 23615) {
ctx->y_res = 600;
ctx->buf_needed = 2; // XXX not always true.
} else { // x == 11808 or anything else..
ctx->y_res = 300;
ctx->buf_needed = 1;
}
}
/* Add in the size of this chunk */
ctx->datalen += sizeof(struct dnpds40_cmd) + j;
}
return 0;
@ -366,26 +407,14 @@ static int dnpds40_main_loop(void *vctx, int copies) {
return 1;
/* Parse job to figure out quantity offset. */
if (copies > 1) {
int offset;
ptr = ctx->databuf;
while(ptr && ptr < (ctx->databuf + ctx->datalen)) {
if(!memcmp("CNTRL QTY", (char*)ptr+2, 9)) {
snprintf(buf, sizeof(buf), "%07d\r", copies);
memcpy(ptr + 24 + 8, buf, 8);
break;
}
buf[9] = 0;
memcpy(buf, ptr + 24, 8);
offset = atoi(buf);
ptr += 24+8+offset;
}
if (copies > 1 && ctx->qty_offset) {
snprintf(buf, sizeof(buf), "%07d\r", copies);
memcpy(ctx->qty_offset, buf, 8);
// XXX should we set/reset BUFFCNTRL?
// XXX should we verify we have sufficient media for prints?
}
return 0;
top:
if (resp) free(resp);
@ -399,11 +428,25 @@ top:
/* 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.. */
if (!strcmp("00001", (char*)resp)) {
free(resp);
/* 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);
if (!strcmp("FBP00", (char*)resp) ||
(ctx->buf_needed == 1 && !strcmp("FBP01", (char*)resp))) {
/* We don't have enough buffers */
INFO("Insufficient printer buffers, retrying...\n");
sleep(1);
goto top;
}
} else if (!strcmp("00500", (char*)resp) ||
!strcmp("00510", (char*)resp)) {
INFO("Printer cooling, retrying...\n");
sleep(1);
goto top;
}
@ -411,35 +454,16 @@ top:
ERROR("Printer Status: %s\n", dnpds40_statuses((char*)resp));
return 1;
}
free(resp);
/* 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);
// XXX should we query the vertical resolution?
/* We need a minumum of two buffers to be safe everywhere */
// XXX 300x300 dpi is always safe with one buffer.
// XXX 300x600 dpi often requires two buffers to be safe.
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;
}
/* Send the stream over as individual data chunks */
ptr = ctx->databuf;
while(ptr && ptr < (ctx->databuf + ctx->datalen)) {
int i;
buf[9] = 0;
buf[8] = 0;
memcpy(buf, ptr + 24, 8);
i = atoi(buf) + 24 + 8;
i = atoi(buf) + 32;
if ((ret = send_data(ctx->dev, ctx->endp_down,
ptr, i)))
@ -449,7 +473,8 @@ top:
}
/* This printer handles copies internally */
copies = 1;
if (ctx->qty_offset)
copies = 1;
/* Clean up */
if (terminate)
@ -512,56 +537,6 @@ static int dnpds40_get_info(struct dnpds40_ctx *ctx)
free(resp);
/* Get Media Info */
dnpds40_build_cmd(&cmd, "INFO", "MEDIA", 0);
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
if (!resp)
return -1;
dnpds40_cleanup_string((char*)resp, len);
INFO("Media Type: '%s'\n", (char*)resp);
INFO(" %s\n", dnpds40_media_types((char*)resp));
switch (*(resp+3)) {
case '1':
INFO(" Stickier paper\n");
break;
case '0':
INFO(" Standard paper\n");
break;
default:
INFO(" Unknown paper(%c)\n", *(resp+4));
break;
}
switch (*(resp+6)) {
case '1':
INFO(" With mark\n");
break;
case '0':
INFO(" Without mark\n");
break;
default:
INFO(" Unknown mark(%c)\n", *(resp+7));
break;
}
free(resp);
/* 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);
INFO("Prints Remaining: '%s'\n", (char*)resp + 4);
free(resp);
/* Get Qty of prints made on this media? */
dnpds40_build_cmd(&cmd, "INFO", "PQTY", 0);
@ -705,6 +680,57 @@ static int dnpds40_get_status(struct dnpds40_ctx *ctx)
free(resp);
/* Get Media Info */
dnpds40_build_cmd(&cmd, "INFO", "MEDIA", 0);
resp = dnpds40_resp_cmd(ctx, &cmd, &len);
if (!resp)
return -1;
dnpds40_cleanup_string((char*)resp, len);
INFO("Media Type: '%s'\n", dnpds40_media_types((char*)resp));
#if 0
switch (*(resp+3)) {
case '1':
INFO(" Stickier paper\n");
break;
case '0':
INFO(" Standard paper\n");
break;
default:
INFO(" Unknown paper(%c)\n", *(resp+4));
break;
}
switch (*(resp+6)) {
case '1':
INFO(" With mark\n");
break;
case '0':
INFO(" Without mark\n");
break;
default:
INFO(" Unknown mark(%c)\n", *(resp+7));
break;
}
#endif
free(resp);
/* 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);
INFO("Prints Remaining: '%s'\n", (char*)resp + 4);
free(resp);
return 0;
}
@ -872,7 +898,7 @@ static int dnpds40_cmdline_arg(void *vctx, int run, char *arg1, char *arg2)
/* Exported */
struct dyesub_backend dnpds40_backend = {
.name = "DNP DS40/DS80",
.version = "0.17",
.version = "0.18",
.uri_prefix = "dnpds40",
.cmdline_usage = dnpds40_cmdline,
.cmdline_arg = dnpds40_cmdline_arg,