diff --git a/.gitignore b/.gitignore index 289a26e..59a0611 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ citizencw01 dyesub_backend mitsu9550 mitsup95d +magicard diff --git a/Makefile b/Makefile index 76cb7d0..ecda927 100644 --- a/Makefile +++ b/Makefile @@ -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 canonselphyneo mitsup95d +BACKENDS = sonyupdr150 kodak6800 kodak1400 shinkos2145 shinkos1245 canonselphy mitsu70x kodak605 dnpds40 citizencw01 mitsu9550 shinkos6245 shinkos6145 canonselphyneo mitsup95d magicard # For the s6145 and mitsu70x backends CPPFLAGS += -DUSE_DLOPEN diff --git a/README b/README index 9df2f0f..9ff5f5e 100644 --- a/README +++ b/README @@ -69,6 +69,7 @@ Shinko CHC-S6245 (aka Sinfonia CE1) Sony UP-CR10L (aka DNP DS-SL10) Shinko CHC-S6145-5A (aka Sinfonia CS2-c) + Magicard Tango 2E *************************************************************************** @@ -777,3 +778,19 @@ Notes: -N [ A | B ] Reset Counter A/B *************************************************************************** + BACKEND=magicard + + Work-in-progress printers: + + Tango 2E + (All others) + + This backend supports additional commands: + + magicard [command [arg] ] + + Valid commands: + + -s Query printer status + + *************************************************************************** diff --git a/backend_common.c b/backend_common.c index 0a49aae..220e0e3 100644 --- a/backend_common.c +++ b/backend_common.c @@ -574,6 +574,7 @@ extern struct dyesub_backend mitsu9550_backend; extern struct dyesub_backend mitsup95d_backend; extern struct dyesub_backend dnpds40_backend; extern struct dyesub_backend cw01_backend; +extern struct dyesub_backend magicard_backend; static struct dyesub_backend *backends[] = { &canonselphy_backend, @@ -591,6 +592,7 @@ static struct dyesub_backend *backends[] = { &mitsup95d_backend, &dnpds40_backend, &cw01_backend, + &magicard_backend, NULL, }; diff --git a/backend_common.h b/backend_common.h index b44b7bb..fe8586e 100644 --- a/backend_common.h +++ b/backend_common.h @@ -127,6 +127,7 @@ enum { P_DNP_DS620, P_DNP_DS820, P_FUJI_ASK300, + P_MAGICARD, P_END, }; diff --git a/backend_magicard.c b/backend_magicard.c new file mode 100644 index 0000000..90af92c --- /dev/null +++ b/backend_magicard.c @@ -0,0 +1,359 @@ +/* + * Magicard card printer family CUPS backend -- libusb-1.0 version + * + * (c) 2017 Solomon Peachy + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#define BACKEND magicard_backend + +#include "backend_common.h" + +/* Exported */ +#define USB_VID_MAGICARD 0x0C1F +#define USB_PID_MAGICARD_TANGO2E 0x1800 + +/* Private data structure */ +struct magicard_ctx { + struct libusb_device_handle *dev; + uint8_t endp_up; + uint8_t endp_down; + uint8_t type; + + uint8_t *databuf; + int datalen; +}; + +struct magicard_cmd_header { + uint8_t guard[9]; /* 0x05 */ + uint8_t guard2[1]; /* 0x01 */ + uint8_t cmd[4]; /* 'REQ,' */ + uint8_t subcmd[4]; /* '???,' */ + uint8_t arg[4]; /* '???,' */ + uint8_t footer[2]; /* 0x1c 0x03 */ +}; + +struct magicard_resp_header { + uint8_t guard[1]; /* 0x01 */ + uint8_t subcmd_arg[7]; /* '???,???' */ + uint8_t data[0]; /* freeform resp */ +// uint8_t term[2]; /* 0x2c 0x03 terminates! */ +}; + +struct magicard_requests { + char *key; + char *desc; + uint8_t type; +}; + +enum { + TYPE_UNKNOWN = 0, + TYPE_STRING, + TYPE_STRINGINT, + TYPE_IPADDR, + TYPE_YESNO, + TYPE_MODEL, +}; + +/* Data definitions */ +static struct magicard_requests magicard_sta_requests[] = { + { "MSR", "Serial Number", TYPE_STRING }, + { "VRS", "Firmware Version", TYPE_STRING }, + { "FDC", "Head Density", TYPE_STRINGINT }, + { "FSP", "Image Start", TYPE_STRINGINT }, + { "FEP", "Image End", TYPE_STRINGINT }, + { "FPP", "Head Position", TYPE_STRINGINT }, + { "MDL", "Model", TYPE_MODEL }, /* 0 == Standard. Others? */ + { "PID", "USB PID", TYPE_STRINGINT }, + { "MAC", "Ethernet MAC Address", TYPE_STRING }, + { "DYN", "Dynamic Address", TYPE_YESNO }, /* 1 == yes, 0 == no */ + { "IPA", "IP Address", TYPE_IPADDR }, /* ASCII signed integer */ + { "SNM", "IP Netmask", TYPE_IPADDR }, /* ASCII signed integer */ + { "GWY", "IP Gateway", TYPE_IPADDR }, /* ASCII signed integer */ + + { "TCQ", "Total Prints", TYPE_STRINGINT }, + { "TCP", "Total Prints on Head", TYPE_STRINGINT }, + { "TCN", "Total Cleaning Cycles", TYPE_STRINGINT }, + { "CCQ", "Prints After Last Cleaning", TYPE_STRINGINT }, + { NULL, NULL, 0 } +}; + +/* Helper functions */ +static int magicard_build_cmd(uint8_t *buf, + char *cmd, char *subcmd, char *arg) +{ + struct magicard_cmd_header *hdr = (struct magicard_cmd_header *) buf; + + memset(hdr->guard, 0x05, sizeof(hdr->guard)); + hdr->guard2[0] = 0x01; + memcpy(hdr->cmd, cmd, 3); + hdr->cmd[3] = ','; + memcpy(hdr->subcmd, subcmd, 3); + hdr->subcmd[3] = ','; + memcpy(hdr->arg, arg, 3); + hdr->arg[3] = ','; + hdr->footer[0] = 0x1c; + hdr->footer[1] = 0x03; + + return sizeof(*hdr); +} + +static uint8_t * magicard_parse_resp(uint8_t *buf, uint16_t len, uint16_t *resplen) +{ + struct magicard_resp_header *hdr = (struct magicard_resp_header *) buf; + + *resplen = len - sizeof(hdr->guard) - sizeof(hdr->subcmd_arg) - 2; + + return hdr->data; +} + +static int magicard_query_status(struct magicard_ctx *ctx) +{ + int ret = 0; + int i; + uint8_t buf[256]; + + for (i = 0 ; ; i++) { + uint16_t resplen = 0; + uint8_t *resp; + int num = 0; + + if (magicard_sta_requests[i].key == NULL) + break; + + ret = magicard_build_cmd(buf, "REQ", "STA", + magicard_sta_requests[i].key); + + if ((ret = send_data(ctx->dev, ctx->endp_down, + buf, ret))) + return ret; + + memset(buf, 0, sizeof(buf)); + + ret = read_data(ctx->dev, ctx->endp_up, + buf, sizeof(buf), &num); + + if (ret < 0) + return ret; + + resp = magicard_parse_resp(buf, num, &resplen); + resp[resplen] = 0; + switch(magicard_sta_requests[i].type) { + case TYPE_IPADDR: { + int32_t ipaddr; + uint8_t *addr = (uint8_t *) &ipaddr; + ipaddr = atoi((char*)resp); + INFO("%s:\t%d.%d.%d.%d\n", + magicard_sta_requests[i].desc, + addr[3], addr[2], addr[1], addr[0]); + break; + } + case TYPE_YESNO: { + int val = atoi((char*)resp); + INFO("%s:\t%s\n", + magicard_sta_requests[i].desc, + val? "Yes" : "No"); + break; + } + case TYPE_MODEL: { + int val = atoi((char*)resp); + INFO("%s:\t%s\n", + magicard_sta_requests[i].desc, + val == 0? "Standard" : "Unknown"); + break; + } + case TYPE_STRINGINT: + // treat differently? + case TYPE_STRING: + case TYPE_UNKNOWN: + default: + INFO("%s:\t%s\n", + magicard_sta_requests[i].desc, + resp); + } + } + + return ret; +} + +/* Main driver */ +static void* magicard_init(void) +{ + struct magicard_ctx *ctx = malloc(sizeof(struct magicard_ctx)); + if (!ctx) { + ERROR("Memory Allocation Failure!"); + return NULL; + } + memset(ctx, 0, sizeof(struct magicard_ctx)); + return ctx; +} + +static void magicard_attach(void *vctx, struct libusb_device_handle *dev, + uint8_t endp_up, uint8_t endp_down, uint8_t jobid) +{ + struct magicard_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); + + ctx->type = lookup_printer_type(&magicard_backend, + desc.idVendor, desc.idProduct); + +} + +static void magicard_teardown(void *vctx) { + struct magicard_ctx *ctx = vctx; + + if (!ctx) + return; + + if (ctx->databuf) + free(ctx->databuf); + + free(ctx); +} + +#define MAX_PRINTJOB_LEN 5218176 + 20*1024 /* 1016*642 * 4color * 2sides */ +static int magicard_read_parse(void *vctx, int data_fd) { + struct magicard_ctx *ctx = vctx; + int run = 1; + + if (!ctx) + return CUPS_BACKEND_FAILED; + + if (ctx->databuf) { + free(ctx->databuf); + ctx->databuf = NULL; + } + + ctx->datalen = 0; + ctx->databuf = malloc(MAX_PRINTJOB_LEN); + if (!ctx->databuf) { + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_FAILED; + } + + while(run) { +// int i; + // cmd stream is 64 * 0x5a, followed by comma-separated + // commands. end of command list is 0x1c. + // then bulk data appended, using lengths specified in + // SZB, SZG, SZR, SZK. + // at end, 0x1c. + // Then one final command: 0x4b, 0x3a, 0x03. 'K:' + } + + return CUPS_BACKEND_OK; +} + +static int magicard_main_loop(void *vctx, int copies) { + struct magicard_ctx *ctx = vctx; + int ret; + + if (!ctx) + return CUPS_BACKEND_FAILED; + +top: + if ((ret = send_data(ctx->dev, ctx->endp_down, + ctx->databuf, ctx->datalen))) + return CUPS_BACKEND_FAILED; + + /* 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 void magicard_cmdline(void) +{ + DEBUG("\t\t[ -s ] # Query status\n"); +} + +static int magicard_cmdline_arg(void *vctx, int argc, char **argv) +{ + struct magicard_ctx *ctx = vctx; + int i, j = 0; + + if (!ctx) + return -1; + + while ((i = getopt(argc, argv, GETOPT_LIST_GLOBAL "s")) >= 0) { + switch(i) { + GETOPT_PROCESS_GLOBAL + case 's': + j = magicard_query_status(ctx); + break; + } + + if (j) return j; + } + + return 0; +} + +struct dyesub_backend magicard_backend = { + .name = "Magicard family", + .version = "0.01WIP", + .uri_prefix = "magicard", + .cmdline_arg = magicard_cmdline_arg, + .cmdline_usage = magicard_cmdline, + .init = magicard_init, + .attach = magicard_attach, + .teardown = magicard_teardown, + .read_parse = magicard_read_parse, + .main_loop = magicard_main_loop, + .devices = { + { USB_VID_MAGICARD, USB_PID_MAGICARD_TANGO2E, P_MAGICARD, ""}, + { USB_VID_MAGICARD, 0xFFFF, P_MAGICARD, ""}, + { 0, 0, 0, ""} + } +}; + +/* Magicard family Spool file format + +*/