selphy_print/backend_common.c

1080 lines
26 KiB
C
Raw Normal View History

/*
* CUPS Backend common code
*
* Copyright (c) 2007-2016 Solomon Peachy <pizza@shaftnet.org>
*
* The latest version of this program can be found at:
2014-01-12 17:26:29 -05:00
*
* http://git.shaftnet.org/cgit/selphy_print.git
2014-01-12 17:26:29 -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 "backend_common.h"
#define BACKEND_VERSION "0.66"
#ifndef URI_PREFIX
#error "Must Define URI_PREFIX"
#endif
#define NUM_CLAIM_ATTEMPTS 10
2015-07-04 09:13:42 -04:00
/* Global Variables */
int dyesub_debug = 0;
2015-07-04 09:13:42 -04:00
int terminate = 0;
int fast_return = 0;
int extra_vid = -1;
int extra_pid = -1;
int extra_type = -1;
int copies = 1;
char *use_serno = NULL;
int current_page = 0;
2015-07-04 09:13:42 -04:00
/* Support Functions */
static int backend_claim_interface(struct libusb_device_handle *dev, int iface)
{
int attempts = NUM_CLAIM_ATTEMPTS;
int ret;
do {
ret = libusb_claim_interface(dev, iface);
if (!ret)
break;
sleep(1);
} while (--attempts > 0);
if (ret)
ERROR("Printer open failure (Could not claim printer interface after %d attempts) (%d)\n", NUM_CLAIM_ATTEMPTS, ret);
return ret;
}
#define ID_BUF_SIZE 2048
static char *get_device_id(struct libusb_device_handle *dev)
{
int length;
int iface = 0;
char *buf = malloc(ID_BUF_SIZE + 1);
if (!buf) {
ERROR("Memory allocation failure (%d bytes)\n", ID_BUF_SIZE+1);
return NULL;
}
2016-01-24 16:59:56 -05:00
if (libusb_kernel_driver_active(dev, iface))
libusb_detach_kernel_driver(dev, iface);
if (backend_claim_interface(dev, iface))
return NULL;
if (libusb_control_transfer(dev,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_IN |
LIBUSB_RECIPIENT_INTERFACE,
0, 0,
(iface << 8),
(unsigned char *)buf, ID_BUF_SIZE, 5000) < 0)
{
*buf = '\0';
goto done;
}
/* length is the first two bytes, MSB first */
length = (((unsigned)buf[0] & 255) << 8) |
((unsigned)buf[1] & 255);
/* Sanity checks */
if (length > ID_BUF_SIZE || length < 14)
length = (((unsigned)buf[1] & 255) << 8) |
((unsigned)buf[0] & 255);
2016-01-24 16:59:56 -05:00
if (length > ID_BUF_SIZE)
length = ID_BUF_SIZE;
2016-01-24 16:59:56 -05:00
if (length < 14) {
*buf = '\0';
goto done;
}
/* Move, and terminate */
memmove(buf, buf + 2, length);
buf[length] = '\0';
done:
libusb_release_interface(dev, iface);
return buf;
}
/* Used with the IEEE1284 deviceid string parsing */
struct deviceid_dict {
char *key;
char *val;
};
#define MAX_DICT 32
static int parse1284_data(const char *device_id, struct deviceid_dict* dict)
{
char *ptr;
char key[256];
char val[256];
int num = 0;
if (!device_id)
return 0;
//[whitespace]key[whitespace]:[whitespace]value[whitespace];
while (*device_id && num < MAX_DICT) {
/* Skip leading spaces */
if (*device_id == ' ')
device_id++;
if (!*device_id)
break;
/* Work out key */
for (ptr = key; *device_id && *device_id != ':'; device_id++)
*ptr++ = *device_id;
if (!*device_id)
break;
2014-03-13 19:50:48 -04:00
while (ptr > key && *(ptr-1) == ' ')
ptr--;
*ptr = 0;
device_id++;
if (!*device_id)
break;
/* Next up, value */
2014-03-13 19:50:48 -04:00
for (ptr = val; *device_id && *device_id != ';'; device_id++)
*ptr++ = *device_id;
if (!*device_id)
break;
2014-03-13 19:50:48 -04:00
while (ptr > val && *(ptr-1) == ' ')
ptr--;
*ptr = 0;
device_id++;
/* Add it to the dictionary */
dict[num].key = strdup(key);
dict[num].val = strdup(val);
num++;
2014-03-13 19:50:48 -04:00
if (!*device_id)
break;
}
return num;
};
static char *dict_find(const char *key, int dlen, struct deviceid_dict* dict)
{
while(dlen) {
if (!strcmp(key, dict->key))
return dict->val;
dlen--;
dict++;
}
return NULL;
}
/* I/O functions */
int read_data(struct libusb_device_handle *dev, uint8_t endp,
uint8_t *buf, int buflen, int *readlen)
{
int ret;
/* Clear buffer */
memset(buf, 0, buflen);
ret = libusb_bulk_transfer(dev, endp,
buf,
buflen,
readlen,
10000);
if (ret < 0) {
ERROR("Failure to receive data from printer (libusb error %d: (%d/%d from 0x%02x))\n", ret, *readlen, buflen, endp);
goto done;
}
2015-01-07 10:22:03 -05:00
if (dyesub_debug) {
DEBUG("Received %d bytes from printer\n", *readlen);
}
2016-01-24 16:59:56 -05:00
if ((dyesub_debug > 1 && buflen < 4096) ||
dyesub_debug > 2) {
int i = *readlen;
DEBUG("<- ");
while(i > 0) {
2015-01-07 21:48:07 -05:00
if ((*readlen-i) != 0 &&
(*readlen-i) % 16 == 0) {
2014-12-31 14:01:32 -05:00
DEBUG2("\n");
DEBUG(" ");
}
2015-01-07 10:22:03 -05:00
DEBUG2("%02x ", buf[*readlen-i]);
i--;
}
DEBUG2("\n");
}
done:
return ret;
}
int send_data(struct libusb_device_handle *dev, uint8_t endp,
uint8_t *buf, int len)
{
2013-09-30 09:52:05 -04:00
int num = 0;
2014-03-12 13:32:21 -04:00
if (dyesub_debug) {
DEBUG("Sending %d bytes to printer\n", len);
}
while (len) {
int len2 = (len > 65536) ? 65536: len;
int ret = libusb_bulk_transfer(dev, endp,
buf, len2,
&num, 15000);
if ((dyesub_debug > 1 && len < 4096) ||
dyesub_debug > 2) {
int i = num;
DEBUG("-> ");
while(i > 0) {
2015-01-07 21:48:07 -05:00
if ((num-i) != 0 &&
(num-i) % 16 == 0) {
2014-12-31 14:01:32 -05:00
DEBUG2("\n");
DEBUG(" ");
}
2015-01-07 10:22:03 -05:00
DEBUG2("%02x ", buf[num-i]);
i--;
}
DEBUG2("\n");
}
if (ret < 0) {
ERROR("Failure to send data to printer (libusb error %d: (%d/%d to 0x%02x))\n", ret, num, len, endp);
return ret;
}
len -= num;
buf += num;
}
return 0;
}
/* More stuff */
static void sigterm_handler(int signum) {
2013-11-23 19:51:55 -05:00
UNUSED(signum);
terminate = 1;
INFO("Job Cancelled");
}
static char *sanitize_string(char *str) {
int len = strlen(str);
while(len && (str[len-1] <= 0x20)) {
str[len-1] = 0;
len--;
}
return str;
}
2016-01-24 16:59:56 -05:00
/*
These functions are Public Domain code obtained from:
http://www.geekhideout.com/urlcode.shtml
*/
#include <ctype.h> /* for isalnum() */
static char to_hex(char code) {
static const char hex[] = "0123456789abcdef";
return hex[code & 15];
}
static char from_hex(char ch) {
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}
/* Note -- caller must free returned pointer! */
static char *url_encode(char *str) {
char *pstr = str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf;
if (!buf) {
ERROR("Memory allocation failure (%d bytes)\n", (int) strlen(str)*3 + 1);
return NULL;
}
2016-01-24 16:59:56 -05:00
while (*pstr) {
if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
*pbuf++ = *pstr;
2016-01-24 16:59:56 -05:00
else if (*pstr == ' ')
*pbuf++ = '+';
2016-01-24 16:59:56 -05:00
else
*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
pstr++;
}
*pbuf = '\0';
return buf;
}
static char *url_decode(char *str) {
char *pstr = str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
if (!buf) {
ERROR("Memory allocation failure (%d bytes)\n", (int) strlen(str) + 1);
return NULL;
}
while (*pstr) {
if (*pstr == '%') {
if (pstr[1] && pstr[2]) {
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
pstr += 2;
}
2016-01-24 16:59:56 -05:00
} else if (*pstr == '+') {
*pbuf++ = ' ';
} else {
*pbuf++ = *pstr;
}
pstr++;
}
*pbuf = '\0';
return buf;
}
/* And now back to our regularly-scheduled programming */
static int print_scan_output(struct libusb_device *device,
struct libusb_device_descriptor *desc,
char *prefix, char *manuf2,
int found,
int scan_only, char *match_serno,
struct dyesub_backend *backend)
{
struct libusb_device_handle *dev;
char buf[256];
2014-03-13 19:50:48 -04:00
char *product = NULL, *serial = NULL, *manuf = NULL, *descr = NULL;
int dlen = 0;
struct deviceid_dict dict[MAX_DICT];
2015-11-17 23:06:09 -05:00
char *ieee_id = NULL;
if (libusb_open(device, &dev)) {
ERROR("Could not open device %04x:%04x (need to be root?)\n", desc->idVendor, desc->idProduct);
found = -1;
goto abort;
}
2014-03-13 19:50:48 -04:00
ieee_id = get_device_id(dev);
/* Get IEEE1284 info */
dlen = parse1284_data(ieee_id, dict);
/* Look up mfg string. */
2014-03-13 19:50:48 -04:00
if (manuf2 && strlen(manuf2)) {
manuf = url_encode(manuf2); /* Backend supplied */
} else if ((manuf = dict_find("MANUFACTURER", dlen, dict))) {
manuf = url_encode(manuf);
} else if ((manuf = dict_find("MFG", dlen, dict))) {
manuf = url_encode(manuf);
} else if ((manuf = dict_find("MFR", dlen, dict))) {
manuf = url_encode(manuf);
} else if (desc->iManufacturer) { /* Get from USB descriptor */
buf[0] = 0;
libusb_get_string_descriptor_ascii(dev, desc->iManufacturer, (unsigned char*)buf, STR_LEN_MAX);
sanitize_string(buf);
manuf = url_encode(buf);
}
if (!manuf || !strlen(manuf)) { /* Last-ditch */
if (manuf) free(manuf);
manuf = url_encode("Unknown");
}
/* Look up model number */
if ((product = dict_find("MODEL", dlen, dict))) {
product = url_encode(product);
} else if ((product = dict_find("MDL", dlen, dict))) {
product = url_encode(product);
} else if (desc->iProduct) { /* Get from USB descriptor */
buf[0] = 0;
libusb_get_string_descriptor_ascii(dev, desc->iProduct, (unsigned char*)buf, STR_LEN_MAX);
sanitize_string(buf);
product = url_encode(buf);
}
if (!product || !strlen(product)) { /* Last-ditch */
if (!product) free(product);
product = url_encode("Unknown");
}
2014-03-13 19:50:48 -04:00
/* Look up description */
if ((descr = dict_find("DESCRIPTION", dlen, dict))) {
descr = strdup(descr);
} else if ((descr = dict_find("DES", dlen, dict))) {
descr = strdup(descr);
}
if (!descr || !strlen(descr)) { /* Last-ditch, generate */
char *product2 = url_decode(product);
char *manuf3 = url_decode(manuf);
descr = malloc(256);
if (!descr) {
ERROR("Memory allocation failure (%d bytes)\n", 256);
if (manuf3)
free(manuf3);
if (product2)
free(product2);
return -1;
}
2016-01-24 16:59:56 -05:00
2014-03-13 19:50:48 -04:00
sprintf(descr, "%s %s", manuf3, product2);
free(product2);
free(manuf3);
}
/* Look up serial number */
if ((serial = dict_find("SERIALNUMBER", dlen, dict))) {
serial = url_encode(serial);
} else if ((serial = dict_find("SN", dlen, dict))) {
serial = url_encode(serial);
} else if ((serial = dict_find("SER", dlen, dict))) {
serial = url_encode(serial);
} else if ((serial = dict_find("SERN", dlen, dict))) {
serial = url_encode(serial);
} else if (desc->iSerialNumber) { /* Get from USB descriptor */
libusb_get_string_descriptor_ascii(dev, desc->iSerialNumber, (unsigned char*)buf, STR_LEN_MAX);
sanitize_string(buf);
serial = url_encode(buf);
} else if (backend->query_serno) { /* Get from backend hook */
int iface = 0;
struct libusb_config_descriptor *config = NULL;
if (libusb_kernel_driver_active(dev, iface))
libusb_detach_kernel_driver(dev, iface);
/* Try to claim the printer, and handle transient failures */
if (!backend_claim_interface(dev, iface)) {
int i;
uint8_t endp_up = 0, endp_down = 0;
libusb_get_active_config_descriptor(device, &config);
for (i = 0 ; i < config->interface[0].altsetting[0].bNumEndpoints ; i++) {
if ((config->interface[0].altsetting[0].endpoint[i].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_BULK) {
if (config->interface[0].altsetting[0].endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_IN)
endp_up = config->interface[0].altsetting[0].endpoint[i].bEndpointAddress;
else
endp_down = config->interface[0].altsetting[0].endpoint[i].bEndpointAddress;
}
}
buf[0] = 0;
/* Ignore result since a failure isn't critical here */
backend->query_serno(dev, endp_up, endp_down, buf, STR_LEN_MAX);
libusb_release_interface(dev, iface);
}
serial = url_encode(buf);
if (config)
libusb_free_config_descriptor(config);
}
2014-03-12 10:55:35 -04:00
if (!serial || !strlen(serial)) { /* Last-ditch */
if (serial) free(serial);
WARNING("**** THIS PRINTER DOES NOT REPORT A SERIAL NUMBER!\n");
2014-03-12 10:55:35 -04:00
WARNING("**** If you intend to use multiple printers of this type, you\n");
WARNING("**** must only plug one in at a time or unexpected behaivor will occur!\n");
serial = strdup("NONE_UNKNOWN");
}
if (dyesub_debug)
DEBUG("VID: %04X PID: %04X Manuf: '%s' Product: '%s' Serial: '%s'\n",
desc->idVendor, desc->idProduct, manuf, product, serial);
2016-01-24 16:59:56 -05:00
if (scan_only) {
int k = 0;
/* URLify the manuf and model strings */
strncpy(buf, manuf, sizeof(buf) - 2);
k = strlen(buf);
buf[k++] = '/';
buf[k] = 0;
strncpy(buf + k, product, sizeof(buf)-k);
2014-03-13 19:50:48 -04:00
fprintf(stdout, "direct %s://%s?serial=%s&backend=%s \"%s\" \"%s\" \"%s\" \"\"\n",
prefix, buf, serial, backend->uri_prefix,
2014-03-13 19:50:48 -04:00
descr, descr,
ieee_id? ieee_id : "");
}
2016-01-24 16:59:56 -05:00
/* If a serial number was passed down, use it. */
2014-02-10 22:23:26 -05:00
if (match_serno && strcmp(match_serno, (char*)serial)) {
found = -1;
}
/* Free things up */
if(serial) free(serial);
if(manuf) free(manuf);
if(product) free(product);
2014-03-13 19:50:48 -04:00
if(descr) free(descr);
2015-11-17 22:54:43 -05:00
if (ieee_id) free(ieee_id);
libusb_close(dev);
abort:
/* Clean up the dictionary */
while (dlen--) {
free (dict[dlen].key);
free (dict[dlen].val);
}
return found;
}
extern struct dyesub_backend updr150_backend;
extern struct dyesub_backend kodak6800_backend;
extern struct dyesub_backend kodak605_backend;
extern struct dyesub_backend kodak1400_backend;
extern struct dyesub_backend shinkos1245_backend;
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 mitsu70x_backend;
extern struct dyesub_backend mitsu9550_backend;
extern struct dyesub_backend dnpds40_backend;
extern struct dyesub_backend cw01_backend;
static struct dyesub_backend *backends[] = {
&canonselphy_backend,
&kodak6800_backend,
&kodak605_backend,
&kodak1400_backend,
&shinkos1245_backend,
&shinkos2145_backend,
2015-09-13 10:31:00 -04:00
&shinkos6145_backend,
&shinkos6245_backend,
&updr150_backend,
&mitsu70x_backend,
&mitsu9550_backend,
&dnpds40_backend,
&cw01_backend,
NULL,
};
static int find_and_enumerate(struct libusb_context *ctx,
struct libusb_device ***list,
struct dyesub_backend *backend,
char *match_serno,
int scan_only)
{
int num;
int i, j = 0, k;
int found = -1;
/* Enumerate and find suitable device */
num = libusb_get_device_list(ctx, list);
for (i = 0 ; i < num ; i++) {
struct libusb_device_descriptor desc;
libusb_get_device_descriptor((*list)[i], &desc);
2014-03-13 19:50:48 -04:00
for (k = 0 ; backends[k] ; k++) {
if (backend && backend != backends[k])
continue;
for (j = 0 ; backends[k]->devices[j].vid ; j++) {
if (extra_pid != -1 &&
extra_vid != -1 &&
extra_type != -1) {
if (backends[k]->devices[j].type == extra_type &&
extra_vid == desc.idVendor &&
extra_pid == desc.idProduct) {
found = i;
goto match;
}
}
if (desc.idVendor == backends[k]->devices[j].vid &&
desc.idProduct == backends[k]->devices[j].pid) {
found = i;
goto match;
}
}
}
2015-09-13 09:56:18 -04:00
continue;
match:
found = print_scan_output((*list)[i], &desc,
URI_PREFIX, backends[k]->devices[j].manuf_str,
found,
scan_only, match_serno,
backends[k]);
if (found != -1 && !scan_only)
break;
}
return found;
}
static struct dyesub_backend *find_backend(char *uri_prefix)
{
int i;
if (!uri_prefix)
return NULL;
for (i = 0; ; i++) {
struct dyesub_backend *backend = backends[i];
if (!backend)
return NULL;
if (!strcmp(uri_prefix, backend->uri_prefix))
return backend;
}
return NULL;
}
void print_license_blurb(void)
{
const char *license = "\n\
Copyright 2007-2016 Solomon Peachy <pizza AT shaftnet DOT org>\n\
\n\
This program is free software; you can redistribute it and/or modify it\n\
under the terms of the GNU General Public License as published by the Free\n\
Software Foundation; either version 3 of the License, or (at your option)\n\
any later version.\n\
\n\
This program is distributed in the hope that it will be useful, but\n\
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n\
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n\
for more details.\n\
\n\
You should have received a copy of the GNU General Public License\n\
along with this program; if not, write to the Free Software\n\
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n\
\n [http://www.gnu.org/licenses/gpl-3.0.html]\n\n";
fprintf(stderr, "%s", license);
}
void print_help(char *argv0, struct dyesub_backend *backend)
{
struct libusb_context *ctx = NULL;
struct libusb_device **list = NULL;
int i;
char *ptr = strrchr(argv0, '/');
if (ptr)
ptr++;
else
ptr = argv0;
if (!backend)
backend = find_backend(ptr);
2016-01-24 16:59:56 -05:00
if (!backend) {
int i;
DEBUG("Environment variables:\n");
DEBUG(" DYESUB_DEBUG EXTRA_PID EXTRA_VID EXTRA_TYPE BACKEND SERIAL\n");
DEBUG("CUPS Usage:\n");
DEBUG("\tDEVICE_URI=someuri %s job user title num-copies options [ filename ]\n", URI_PREFIX);
DEBUG("\n");
DEBUG("Standalone Usage:\n");
DEBUG("\t%s\n", URI_PREFIX);
DEBUG(" [ -D ] [ -G ] [ -f ]\n");
DEBUG(" [ backend_specific_args ] \n");