2013-06-27 23:02:34 -04:00
|
|
|
/*
|
|
|
|
* CUPS Backend common code
|
|
|
|
*
|
2023-03-19 21:33:55 -04:00
|
|
|
* Copyright (c) 2007-2023 Solomon Peachy <pizza@shaftnet.org>
|
2013-06-27 23:02:34 -04:00
|
|
|
*
|
|
|
|
* The latest version of this program can be found at:
|
2014-01-12 17:26:29 -05:00
|
|
|
*
|
2021-01-23 10:47:01 -05:00
|
|
|
* https://git.shaftnet.org/cgit/selphy_print.git
|
2014-01-12 17:26:29 -05:00
|
|
|
*
|
2013-06-27 23:02:34 -04: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
|
2020-01-17 16:50:56 -05:00
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2013-06-27 23:02:34 -04:00
|
|
|
*
|
2017-11-17 13:34:26 -05:00
|
|
|
* SPDX-License-Identifier: GPL-3.0+
|
|
|
|
*
|
2013-06-27 23:02:34 -04:00
|
|
|
*/
|
|
|
|
|
2013-07-18 08:46:44 -04:00
|
|
|
#include "backend_common.h"
|
2019-01-13 15:44:29 -05:00
|
|
|
#include <errno.h>
|
2019-09-28 11:05:42 -04:00
|
|
|
#include <signal.h>
|
2020-03-24 17:48:52 -04:00
|
|
|
#include <strings.h> /* For strncasecmp */
|
2013-06-27 23:02:34 -04:00
|
|
|
|
2022-03-18 10:50:33 -04:00
|
|
|
#define BACKEND_VERSION "0.123"
|
2013-06-27 23:02:34 -04:00
|
|
|
|
2020-02-16 16:19:44 -05:00
|
|
|
#ifndef CORRTABLE_PATH
|
|
|
|
#ifdef PACKAGE_DATA_DIR
|
|
|
|
#define CORRTABLE_PATH PACKAGE_DATA_DIR "/backend_data"
|
|
|
|
#else
|
|
|
|
#error "Must define CORRTABLE_PATH or PACKAGE_DATA_DIR!"
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2017-04-19 23:04:27 -04:00
|
|
|
#define URB_XFER_SIZE (64*1024)
|
|
|
|
#define XFER_TIMEOUT 15000
|
|
|
|
|
2017-11-08 13:45:14 -05:00
|
|
|
#define USB_SUBCLASS_PRINTER 0x1
|
|
|
|
#define USB_INTERFACE_PROTOCOL_BIDIR 0x2
|
2018-11-11 22:56:52 -05:00
|
|
|
#define USB_INTERFACE_PROTOCOL_IPP 0x4
|
2017-11-08 13:45:14 -05:00
|
|
|
|
2015-07-04 09:13:42 -04:00
|
|
|
/* Global Variables */
|
2014-02-10 20:10:36 -05:00
|
|
|
int dyesub_debug = 0;
|
2015-07-04 09:13:42 -04:00
|
|
|
int terminate = 0;
|
2015-08-12 20:56:51 -04:00
|
|
|
int fast_return = 0;
|
2015-08-12 22:51:45 -04:00
|
|
|
int extra_vid = -1;
|
|
|
|
int extra_pid = -1;
|
|
|
|
int extra_type = -1;
|
2019-05-06 21:10:43 -04:00
|
|
|
int ncopies = 1;
|
2019-05-12 11:46:41 -04:00
|
|
|
int collate = 0;
|
2018-05-09 23:23:25 -04:00
|
|
|
int test_mode = 0;
|
2018-10-23 11:41:57 -04:00
|
|
|
int quiet = 0;
|
2021-05-05 21:14:59 -04:00
|
|
|
int stats_only = 0;
|
2020-08-11 12:48:38 -04:00
|
|
|
FILE *logger;
|
2013-07-17 23:39:31 -04:00
|
|
|
|
2020-02-16 16:19:44 -05:00
|
|
|
const char *corrtable_path = CORRTABLE_PATH;
|
2017-04-19 23:04:27 -04:00
|
|
|
static int max_xfer_size = URB_XFER_SIZE;
|
|
|
|
static int xfer_timeout = XFER_TIMEOUT;
|
2021-02-07 11:28:40 -05:00
|
|
|
|
|
|
|
#ifdef OLD_URI
|
|
|
|
static int old_uri = 1;
|
|
|
|
#else
|
2020-02-16 16:19:44 -05:00
|
|
|
static int old_uri = 0;
|
2021-02-07 11:28:40 -05:00
|
|
|
#endif
|
2017-04-19 23:04:27 -04:00
|
|
|
|
2015-07-04 09:13:42 -04:00
|
|
|
/* Support Functions */
|
2019-01-11 07:54:34 -05:00
|
|
|
int backend_claim_interface(struct libusb_device_handle *dev, int iface,
|
|
|
|
int num_claim_attempts)
|
2015-07-02 19:44:26 -04:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
do {
|
|
|
|
ret = libusb_claim_interface(dev, iface);
|
|
|
|
if (!ret)
|
|
|
|
break;
|
2016-10-26 17:15:27 -04:00
|
|
|
if (ret != LIBUSB_ERROR_BUSY)
|
|
|
|
break;
|
2018-02-16 07:38:39 -05:00
|
|
|
if (--num_claim_attempts == 0)
|
|
|
|
break;
|
2015-07-02 19:44:26 -04:00
|
|
|
sleep(1);
|
2018-02-16 07:38:39 -05:00
|
|
|
} while (1);
|
2015-07-02 19:44:26 -04:00
|
|
|
|
|
|
|
if (ret)
|
2018-02-16 09:32:02 -05:00
|
|
|
ERROR("Failed to claim interface %d (%d)\n", iface, ret);
|
2015-07-02 19:44:26 -04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-05-09 23:09:01 -04:00
|
|
|
static int lookup_printer_type(struct dyesub_backend *backend, uint16_t idVendor, uint16_t idProduct)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int type = P_UNKNOWN;
|
|
|
|
|
|
|
|
for (i = 0 ; backend->devices[i].vid ; i++) {
|
|
|
|
if (extra_pid != -1 &&
|
|
|
|
extra_vid != -1 &&
|
|
|
|
extra_type != -1) {
|
|
|
|
if (backend->devices[i].type == extra_type &&
|
|
|
|
extra_vid == idVendor &&
|
|
|
|
extra_pid == idProduct) {
|
|
|
|
return extra_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (idVendor == backend->devices[i].vid &&
|
|
|
|
idProduct == backend->devices[i].pid) {
|
|
|
|
return backend->devices[i].type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2016-11-01 20:19:39 -04:00
|
|
|
/* Interface **MUST** already be claimed! */
|
2013-06-27 23:02:34 -04:00
|
|
|
#define ID_BUF_SIZE 2048
|
2019-11-02 08:25:34 -04:00
|
|
|
char *get_device_id(struct libusb_device_handle *dev, int iface)
|
2013-06-27 23:02:34 -04:00
|
|
|
{
|
2013-07-17 23:39:31 -04:00
|
|
|
int length;
|
2013-06-27 23:02:34 -04:00
|
|
|
char *buf = malloc(ID_BUF_SIZE + 1);
|
|
|
|
|
2015-06-23 20:32:41 -04:00
|
|
|
if (!buf) {
|
|
|
|
ERROR("Memory allocation failure (%d bytes)\n", ID_BUF_SIZE+1);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-01-24 16:59:56 -05:00
|
|
|
|
2013-06-27 23:02:34 -04:00
|
|
|
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
|
|
|
|
2013-06-27 23:02:34 -04:00
|
|
|
if (length > ID_BUF_SIZE)
|
|
|
|
length = ID_BUF_SIZE;
|
2016-01-24 16:59:56 -05:00
|
|
|
|
2013-06-27 23:02:34 -04:00
|
|
|
if (length < 14) {
|
|
|
|
*buf = '\0';
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2018-05-01 20:36:18 -04:00
|
|
|
/* IEEE1284 length field includs the header! */
|
|
|
|
length -= 2;
|
2018-04-27 14:47:56 -04:00
|
|
|
|
2013-06-27 23:02:34 -04:00
|
|
|
/* Move, and terminate */
|
|
|
|
memmove(buf, buf + 2, length);
|
|
|
|
buf[length] = '\0';
|
|
|
|
|
|
|
|
done:
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2014-03-13 10:58:33 -04:00
|
|
|
/* Used with the IEEE1284 deviceid string parsing */
|
2019-11-02 08:25:34 -04:00
|
|
|
int parse1284_data(const char *device_id, struct deviceid_dict* dict)
|
2014-03-13 10:58:33 -04:00
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
char key[256];
|
|
|
|
char val[256];
|
|
|
|
int num = 0;
|
|
|
|
|
2015-07-04 09:43:46 -04:00
|
|
|
if (!device_id)
|
2019-12-12 07:15:25 -05:00
|
|
|
return CUPS_BACKEND_OK;
|
2015-07-04 09:43:46 -04:00
|
|
|
|
2014-03-13 10:58:33 -04:00
|
|
|
//[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) == ' ')
|
2014-03-13 10:58:33 -04:00
|
|
|
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++)
|
2014-03-13 10:58:33 -04:00
|
|
|
*ptr++ = *device_id;
|
2014-03-13 19:50:48 -04:00
|
|
|
while (ptr > val && *(ptr-1) == ' ')
|
2014-03-13 10:58:33 -04:00
|
|
|
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;
|
2014-03-13 10:58:33 -04:00
|
|
|
}
|
|
|
|
return num;
|
2016-12-15 09:37:31 -05:00
|
|
|
}
|
2014-03-13 10:58:33 -04:00
|
|
|
|
2019-11-02 08:25:34 -04:00
|
|
|
char *dict_find(const char *key, int dlen, struct deviceid_dict* dict)
|
2014-03-13 10:58:33 -04:00
|
|
|
{
|
|
|
|
while(dlen) {
|
|
|
|
if (!strcmp(key, dict->key))
|
|
|
|
return dict->val;
|
|
|
|
dlen--;
|
|
|
|
dict++;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* I/O functions */
|
2020-08-11 20:27:26 -04:00
|
|
|
|
|
|
|
int read_data(struct dyesub_connection *conn, uint8_t *buf, int buflen, int *readlen)
|
2013-12-21 22:39:15 -05:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Clear buffer */
|
2014-02-11 13:41:15 -05:00
|
|
|
memset(buf, 0, buflen);
|
2013-12-21 22:39:15 -05:00
|
|
|
|
2020-08-11 20:27:26 -04:00
|
|
|
ret = libusb_bulk_transfer(conn->dev, conn->endp_up,
|
2013-12-21 22:39:15 -05:00
|
|
|
buf,
|
|
|
|
buflen,
|
|
|
|
readlen,
|
2017-04-19 23:04:27 -04:00
|
|
|
xfer_timeout);
|
2013-12-21 22:39:15 -05:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2020-08-11 20:27:26 -04:00
|
|
|
ERROR("Failure to receive data from printer (libusb error %d: (%d/%d from 0x%02x))\n", ret, *readlen, buflen, conn->endp_up);
|
2013-12-21 22:39:15 -05:00
|
|
|
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
|
|
|
|
2021-05-14 06:29:29 -04:00
|
|
|
if ((dyesub_debug > 1 && *readlen < 4096) ||
|
2014-06-03 20:27:23 -04:00
|
|
|
dyesub_debug > 2) {
|
2014-12-31 13:55:57 -05:00
|
|
|
int i = *readlen;
|
|
|
|
|
2013-12-21 22:39:15 -05:00
|
|
|
DEBUG("<- ");
|
2014-12-31 13:55:57 -05:00
|
|
|
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]);
|
2014-12-31 13:55:57 -05:00
|
|
|
i--;
|
2013-12-21 22:39:15 -05:00
|
|
|
}
|
|
|
|
DEBUG2("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
return ret;
|
|
|
|
}
|
2013-07-17 23:39:31 -04:00
|
|
|
|
2020-08-11 20:27:26 -04:00
|
|
|
int send_data(struct dyesub_connection *conn, const uint8_t *buf, int len)
|
2013-06-27 23:02:34 -04:00
|
|
|
{
|
2013-09-30 09:52:05 -04:00
|
|
|
int num = 0;
|
2013-06-27 23:02:34 -04:00
|
|
|
|
2014-03-12 13:32:21 -04:00
|
|
|
if (dyesub_debug) {
|
2013-12-11 07:49:57 -05:00
|
|
|
DEBUG("Sending %d bytes to printer\n", len);
|
|
|
|
}
|
|
|
|
|
2013-06-30 11:15:03 -04:00
|
|
|
while (len) {
|
2017-04-19 23:04:27 -04:00
|
|
|
int len2 = (len > max_xfer_size) ? max_xfer_size: len;
|
2013-12-11 07:49:57 -05:00
|
|
|
|
2019-12-12 07:23:38 -05:00
|
|
|
if ((dyesub_debug > 1 && len2 < 4096) ||
|
2014-12-31 13:55:57 -05:00
|
|
|
dyesub_debug > 2) {
|
2019-12-12 07:23:38 -05:00
|
|
|
int i = len2;
|
2014-12-31 13:55:57 -05:00
|
|
|
|
2013-12-11 07:49:57 -05:00
|
|
|
DEBUG("-> ");
|
2014-12-31 13:55:57 -05:00
|
|
|
while(i > 0) {
|
2019-12-12 07:23:38 -05:00
|
|
|
if ((len2-i) != 0 &&
|
|
|
|
(len2-i) % 16 == 0) {
|
2014-12-31 14:01:32 -05:00
|
|
|
DEBUG2("\n");
|
|
|
|
DEBUG(" ");
|
|
|
|
}
|
2019-12-12 07:23:38 -05:00
|
|
|
DEBUG2("%02x ", buf[len2-i]);
|
2014-12-31 13:55:57 -05:00
|
|
|
i--;
|
2013-12-11 07:49:57 -05:00
|
|
|
}
|
|
|
|
DEBUG2("\n");
|
|
|
|
}
|
|
|
|
|
2020-08-11 20:27:26 -04:00
|
|
|
int ret = libusb_bulk_transfer(conn->dev, conn->endp_down,
|
2019-12-12 07:23:38 -05:00
|
|
|
(uint8_t*) buf, len2,
|
|
|
|
&num, xfer_timeout);
|
|
|
|
|
2013-06-30 11:15:03 -04:00
|
|
|
if (ret < 0) {
|
2020-08-11 20:27:26 -04:00
|
|
|
ERROR("Failure to send data to printer (libusb error %d: (%d/%d to 0x%02x))\n", ret, num, len2, conn->endp_down);
|
2013-06-30 11:15:03 -04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
len -= num;
|
|
|
|
buf += num;
|
2013-06-27 23:02:34 -04:00
|
|
|
}
|
2013-06-30 11:15:03 -04:00
|
|
|
|
2019-12-12 07:15:25 -05:00
|
|
|
return CUPS_BACKEND_OK;
|
2013-06-27 23:02:34 -04:00
|
|
|
}
|
|
|
|
|
2014-03-13 10:58:33 -04:00
|
|
|
/* More stuff */
|
2020-01-25 07:15:02 -05:00
|
|
|
#ifndef _WIN32
|
2013-06-30 11:15:03 -04:00
|
|
|
static void sigterm_handler(int signum) {
|
2013-11-23 19:51:55 -05:00
|
|
|
UNUSED(signum);
|
|
|
|
|
2013-06-27 23:02:34 -04:00
|
|
|
terminate = 1;
|
|
|
|
INFO("Job Cancelled");
|
|
|
|
}
|
2020-01-25 07:15:02 -05:00
|
|
|
#endif
|
2013-06-30 11:15:03 -04:00
|
|
|
|
2013-07-06 20:58:23 -04:00
|
|
|
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
|
|
|
/*
|
2014-02-09 23:46:02 -05:00
|
|
|
|
|
|
|
These functions are Public Domain code obtained from:
|
|
|
|
|
2021-01-23 10:47:01 -05:00
|
|
|
https://www.geekhideout.com/urlcode.shtml
|
2014-02-09 23:46:02 -05:00
|
|
|
|
|
|
|
*/
|
|
|
|
#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! */
|
2019-09-04 00:05:29 -04:00
|
|
|
static char *url_encode(const char *str) {
|
|
|
|
const char *pstr = str;
|
|
|
|
char *buf = malloc(strlen(str) * 3 + 1);
|
|
|
|
char *pbuf = buf;
|
2014-02-09 23:46:02 -05:00
|
|
|
|
2015-06-23 20:32:41 -04:00
|
|
|
if (!buf) {
|
|
|
|
ERROR("Memory allocation failure (%d bytes)\n", (int) strlen(str)*3 + 1);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-01-24 16:59:56 -05:00
|
|
|
|
2014-02-09 23:46:02 -05:00
|
|
|
while (*pstr) {
|
2017-07-10 20:15:56 -04:00
|
|
|
if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
|
2014-02-09 23:46:02 -05:00
|
|
|
*pbuf++ = *pstr;
|
2016-01-24 16:59:56 -05:00
|
|
|
else if (*pstr == ' ')
|
2014-02-09 23:46:02 -05:00
|
|
|
*pbuf++ = '+';
|
2016-01-24 16:59:56 -05:00
|
|
|
else
|
2014-02-09 23:46:02 -05:00
|
|
|
*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;
|
2015-06-23 20:32:41 -04:00
|
|
|
|
|
|
|
if (!buf) {
|
|
|
|
ERROR("Memory allocation failure (%d bytes)\n", (int) strlen(str) + 1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-02-09 23:46:02 -05:00
|
|
|
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 == '+') {
|
2014-02-09 23:46:02 -05:00
|
|
|
*pbuf++ = ' ';
|
|
|
|
} else {
|
|
|
|
*pbuf++ = *pstr;
|
|
|
|
}
|
|
|
|
pstr++;
|
|
|
|
}
|
|
|
|
*pbuf = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And now back to our regularly-scheduled programming */
|
|
|
|
|
2018-02-16 07:38:39 -05:00
|
|
|
static int probe_device(struct libusb_device *device,
|
|
|
|
struct libusb_device_descriptor *desc,
|
2020-02-11 21:27:06 -05:00
|
|
|
const char *make,
|
2020-08-13 21:36:41 -04:00
|
|
|
const char *uri_prefix, const char *manuf_override,
|
2018-02-16 07:38:39 -05:00
|
|
|
int found, int num_claim_attempts,
|
2019-09-04 00:05:29 -04:00
|
|
|
int scan_only, const char *match_serno,
|
2020-08-11 20:27:26 -04:00
|
|
|
struct dyesub_connection *conn,
|
2018-02-16 07:38:39 -05:00
|
|
|
struct dyesub_backend *backend)
|
2013-06-30 11:15:03 -04:00
|
|
|
{
|
2013-06-30 11:32:41 -04:00
|
|
|
struct libusb_device_handle *dev;
|
2014-02-09 23:46:02 -05:00
|
|
|
char buf[256];
|
2014-03-13 19:50:48 -04:00
|
|
|
char *product = NULL, *serial = NULL, *manuf = NULL, *descr = NULL;
|
2017-11-08 13:45:14 -05:00
|
|
|
uint8_t iface, altset;
|
2016-11-17 12:16:29 -05:00
|
|
|
struct libusb_config_descriptor *config = NULL;
|
2014-03-13 10:58:33 -04:00
|
|
|
int dlen = 0;
|
|
|
|
struct deviceid_dict dict[MAX_DICT];
|
2015-11-17 23:06:09 -05:00
|
|
|
char *ieee_id = NULL;
|
2016-11-17 12:16:29 -05:00
|
|
|
int i;
|
2016-12-01 17:06:10 -05:00
|
|
|
uint8_t endp_up, endp_down;
|
2014-03-13 10:58:33 -04:00
|
|
|
|
2016-11-01 20:19:39 -04:00
|
|
|
DEBUG("Probing VID: %04X PID: %04x\n", desc->idVendor, desc->idProduct);
|
|
|
|
|
2020-03-18 14:05:04 -04:00
|
|
|
if ((i = libusb_open(device, &dev))) {
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (i == LIBUSB_ERROR_NOT_SUPPORTED)
|
|
|
|
ERROR("Could not open device %04x:%04x! (Genric USB driver missing, See README)\n", desc->idVendor, desc->idProduct);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
ERROR("Could not open device %04x:%04x - %d (need to be root?)\n", desc->idVendor, desc->idProduct, i);
|
2013-06-30 12:05:32 -04:00
|
|
|
found = -1;
|
|
|
|
goto abort;
|
2013-06-30 11:32:41 -04:00
|
|
|
}
|
2014-02-09 23:46:02 -05:00
|
|
|
|
2019-10-04 09:54:59 -04:00
|
|
|
#if 0
|
|
|
|
/* XXX FIXME: Iterate through bNumConfigurations */
|
|
|
|
|
|
|
|
/* Force reset of device configuration */
|
|
|
|
{
|
|
|
|
int cfgnum = -1;
|
|
|
|
if (libusb_get_configuration(dev, &cfgnum)) {
|
|
|
|
ERROR("Can't get config!");
|
|
|
|
}
|
|
|
|
INFO("Config %d\n", config);
|
|
|
|
if (config < 1)
|
|
|
|
cfgnum = 1;
|
|
|
|
if (libusb_set_configuration(dev, cfgnum)) {
|
|
|
|
ERROR("Can't set config\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Get descriptor for active configuration */
|
2017-11-08 13:45:14 -05:00
|
|
|
if (libusb_get_active_config_descriptor(device, &config)) {
|
|
|
|
found = -1;
|
|
|
|
goto abort_close;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop through all interfaces and altsettings to find candidates */
|
|
|
|
for (iface = 0 ; iface < config->bNumInterfaces ; iface ++) {
|
|
|
|
for (altset = 0 ; altset < config->interface[iface].num_altsetting ; altset++) {
|
|
|
|
/* Skip interfaces that don't have enough endpoints */
|
|
|
|
if (config->interface[iface].altsetting[altset].bNumEndpoints < 2) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-04-28 22:33:29 -04:00
|
|
|
/* Note we actually only match on explicit VID+PIDs so there's no need to filter based
|
|
|
|
on specific class/type */
|
|
|
|
|
2018-11-11 22:56:52 -05:00
|
|
|
/* Explicitly exclude IPP-over-USB interfaces */
|
2018-11-17 15:39:48 -05:00
|
|
|
if (desc->bDeviceClass == LIBUSB_CLASS_PER_INTERFACE &&
|
|
|
|
config->interface[iface].altsetting[altset].bInterfaceClass == LIBUSB_CLASS_PRINTER &&
|
|
|
|
config->interface[iface].altsetting[altset].bInterfaceSubClass == USB_SUBCLASS_PRINTER &&
|
|
|
|
config->interface[iface].altsetting[altset].bInterfaceProtocol == USB_INTERFACE_PROTOCOL_IPP) {
|
2018-11-11 22:56:52 -05:00
|
|
|
continue;
|
|
|
|
}
|
2017-11-08 13:45:14 -05:00
|
|
|
|
|
|
|
/* Find the first set of endpoints! */
|
|
|
|
endp_up = endp_down = 0;
|
|
|
|
for (i = 0 ; i < config->interface[iface].altsetting[altset].bNumEndpoints ; i++) {
|
|
|
|
if ((config->interface[iface].altsetting[altset].endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
|
|
|
|
if (config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_IN)
|
|
|
|
endp_up = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress;
|
|
|
|
else
|
|
|
|
endp_down = config->interface[iface].altsetting[altset].endpoint[i].bEndpointAddress;
|
|
|
|
}
|
|
|
|
if (endp_up && endp_down)
|
|
|
|
goto candidate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we got here, we didn't find a match. */
|
|
|
|
found = -1;
|
|
|
|
goto abort_close;
|
|
|
|
|
|
|
|
candidate:
|
|
|
|
|
|
|
|
/* We've now found an interface/altset we need to query in more detail */
|
|
|
|
/* Detach the kernel driver */
|
2016-11-01 20:19:39 -04:00
|
|
|
if (libusb_kernel_driver_active(dev, iface))
|
|
|
|
libusb_detach_kernel_driver(dev, iface);
|
2014-03-13 19:50:48 -04:00
|
|
|
|
2017-11-08 13:45:14 -05:00
|
|
|
/* Claim the interface so we can start querying things! */
|
2018-02-16 07:38:39 -05:00
|
|
|
if (backend_claim_interface(dev, iface, num_claim_attempts)) {
|
2016-11-01 20:19:39 -04:00
|
|
|
found = -1;
|
2016-11-17 12:16:29 -05:00
|
|
|
goto abort_release;
|
|
|
|
}
|
|
|
|
|
2018-02-16 09:32:02 -05:00
|
|
|
/* Use the appropriate altesetting, but only if the
|
|
|
|
printer supports more than one. Some printers don't like
|
|
|
|
us unconditionally setting this. */
|
|
|
|
if (config->interface[iface].num_altsetting > 1) {
|
2018-01-09 12:40:13 -05:00
|
|
|
if (libusb_set_interface_alt_setting(dev, iface, altset)) {
|
2018-02-16 09:32:02 -05:00
|
|
|
ERROR("Failed to set alternative interface %d/%d\n", iface, altset);
|
2018-01-09 12:40:13 -05:00
|
|
|
found = -1;
|
|
|
|
goto abort_release;
|
|
|
|
}
|
2016-11-17 12:16:29 -05:00
|
|
|
}
|
|
|
|
|
2017-07-10 20:15:56 -04:00
|
|
|
/* Query IEEE1284 info only if it's a PRINTER class */
|
2016-11-17 12:16:29 -05:00
|
|
|
if (desc->bDeviceClass == LIBUSB_CLASS_PRINTER ||
|
|
|
|
(desc->bDeviceClass == LIBUSB_CLASS_PER_INTERFACE &&
|
2017-11-08 13:45:14 -05:00
|
|
|
config->interface[iface].altsetting[altset].bInterfaceClass == LIBUSB_CLASS_PRINTER &&
|
|
|
|
config->interface[iface].altsetting[altset].bInterfaceSubClass == USB_SUBCLASS_PRINTER)) {
|
2016-11-01 20:19:39 -04:00
|
|
|
ieee_id = get_device_id(dev, iface);
|
|
|
|
dlen = parse1284_data(ieee_id, dict);
|
|
|
|
}
|
2014-03-13 10:58:33 -04:00
|
|
|
|
2021-04-28 23:03:46 -04:00
|
|
|
if (!old_uri && !scan_only && !dyesub_debug) goto skip_manuf_model;
|
2020-08-13 21:36:41 -04:00
|
|
|
|
2021-04-28 22:33:29 -04:00
|
|
|
/* Look up mfg string in IEEE1284 data */
|
2017-11-08 19:19:00 -05:00
|
|
|
if (manuf_override && strlen(manuf_override)) {
|
2021-04-28 22:33:29 -04:00
|
|
|
manuf = url_encode(manuf_override);
|
2014-03-13 10:58:33 -04:00
|
|
|
} 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);
|
2021-04-28 22:33:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If no manufacturer string, fall back to USB iManufacturer */
|
|
|
|
if ((!manuf || !strlen(manuf)) &&
|
|
|
|
desc->iManufacturer) {
|
2014-03-13 10:58:33 -04:00
|
|
|
buf[0] = 0;
|
2014-02-09 23:46:02 -05:00
|
|
|
libusb_get_string_descriptor_ascii(dev, desc->iManufacturer, (unsigned char*)buf, STR_LEN_MAX);
|
|
|
|
sanitize_string(buf);
|
|
|
|
manuf = url_encode(buf);
|
2013-06-30 11:32:41 -04:00
|
|
|
}
|
2021-04-28 22:33:29 -04:00
|
|
|
|
2014-03-13 10:58:33 -04:00
|
|
|
if (!manuf || !strlen(manuf)) { /* Last-ditch */
|
|
|
|
if (manuf) free(manuf);
|
2021-04-28 22:33:29 -04:00
|
|
|
WARNING("**** THIS PRINTER DOES NOT REPORT A VALID MANUFACTURER STRING!\n");
|
2017-03-01 15:40:11 -05:00
|
|
|
manuf = url_encode("Unknown"); // XXX use USB VID?
|
2014-03-13 10:58:33 -04:00
|
|
|
}
|
|
|
|
|
2021-04-28 22:33:29 -04:00
|
|
|
/* Look up model string in IEEE1284 data */
|
2014-03-13 10:58:33 -04:00
|
|
|
if ((product = dict_find("MODEL", dlen, dict))) {
|
|
|
|
product = url_encode(product);
|
|
|
|
} else if ((product = dict_find("MDL", dlen, dict))) {
|
|
|
|
product = url_encode(product);
|
2021-04-28 22:33:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If no manufacturer string, fall back to USB iProduct */
|
|
|
|
if ((!product || !strlen(product)) &&
|
|
|
|
desc->iProduct) {
|
2014-03-13 10:58:33 -04:00
|
|
|
buf[0] = 0;
|
|
|
|
libusb_get_string_descriptor_ascii(dev, desc->iProduct, (unsigned char*)buf, STR_LEN_MAX);
|
2014-02-09 23:46:02 -05:00
|
|
|
sanitize_string(buf);
|
|
|
|
product = url_encode(buf);
|
2013-06-30 11:15:03 -04:00
|
|
|
}
|
2014-03-13 10:58:33 -04:00
|
|
|
|
|
|
|
if (!product || !strlen(product)) { /* Last-ditch */
|
|
|
|
if (!product) free(product);
|
2021-04-28 22:33:29 -04:00
|
|
|
WARNING("**** THIS PRINTER DOES NOT REPORT A VALID MODEL STRING!\n");
|
2017-03-01 15:40:11 -05:00
|
|
|
product = url_encode("Unknown"); // XXX Use USB PID?
|
2014-03-13 10:58:33 -04:00
|
|
|
}
|
|
|
|
|
2021-04-28 22:33:29 -04:00
|
|
|
/* Look up decription string in IEEE1284 data */
|
2014-03-13 19:50:48 -04:00
|
|
|
if ((descr = dict_find("DESCRIPTION", dlen, dict))) {
|
|
|
|
descr = strdup(descr);
|
|
|
|
} else if ((descr = dict_find("DES", dlen, dict))) {
|
|
|
|
descr = strdup(descr);
|
|
|
|
}
|
2021-04-28 22:33:29 -04:00
|
|
|
|
2014-03-13 19:50:48 -04:00
|
|
|
if (!descr || !strlen(descr)) { /* Last-ditch, generate */
|
|
|
|
char *product2 = url_decode(product);
|
|
|
|
char *manuf3 = url_decode(manuf);
|
2018-09-24 21:15:36 -04:00
|
|
|
descr = malloc(514); /* 256 + 256 + 1 + 1 */
|
2015-06-23 20:32:41 -04:00
|
|
|
if (!descr) {
|
2018-09-24 21:15:36 -04:00
|
|
|
ERROR("Memory allocation failure (%d bytes)\n", 514);
|
2015-07-04 11:03:52 -04:00
|
|
|
if (manuf3)
|
|
|
|
free(manuf3);
|
|
|
|
if (product2)
|
|
|
|
free(product2);
|
2015-08-12 22:51:45 -04:00
|
|
|
return -1;
|
2015-06-23 20:32:41 -04:00
|
|
|
}
|
2016-01-24 16:59:56 -05:00
|
|
|
|
2018-09-24 21:15:36 -04:00
|
|
|
snprintf(descr, 514, "%s %s", manuf3, product2);
|
2014-03-13 19:50:48 -04:00
|
|
|
free(product2);
|
|
|
|
free(manuf3);
|
|
|
|
}
|
|
|
|
|
2021-04-28 22:33:29 -04:00
|
|
|
skip_manuf_model:
|
2020-08-13 21:36:41 -04:00
|
|
|
|
2021-04-28 22:33:29 -04:00
|
|
|
/* Prefer IEEE1284-reported serial number */
|
2014-03-13 10:58:33 -04:00
|
|
|
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);
|
2021-04-28 22:33:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If it's not valid, fall back to USB iSerial */
|
|
|
|
if ((!serial || !strlen(serial)) &&
|
|
|
|
!(backend->flags & BACKEND_FLAG_BADISERIAL) &&
|
|
|
|
desc->iSerialNumber) {
|
2014-02-09 23:46:02 -05:00
|
|
|
libusb_get_string_descriptor_ascii(dev, desc->iSerialNumber, (unsigned char*)buf, STR_LEN_MAX);
|
|
|
|
sanitize_string(buf);
|
2014-03-13 10:58:33 -04:00
|
|
|
serial = url_encode(buf);
|
2021-04-28 22:33:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: What about situations where iSerial does not match IEEE1284?
|
|
|
|
Or if the '1284 data is bogus? */
|
|
|
|
|
|
|
|
/* If still no serial, fall back to backend hook */
|
|
|
|
if ((!serial || !strlen(serial)) &&
|
|
|
|
backend->query_serno) { /* Get from backend hook */
|
2020-08-11 20:27:26 -04:00
|
|
|
struct dyesub_connection c2;
|
|
|
|
c2.dev = dev;
|
|
|
|
c2.iface = iface;
|
|
|
|
c2.altset = altset;
|
|
|
|
c2.endp_up = endp_up;
|
|
|
|
c2.endp_down = endp_down;
|
|
|
|
backend->query_serno(&c2, buf, STR_LEN_MAX);
|
2016-11-01 20:19:39 -04:00
|
|
|
serial = url_encode(buf);
|
2013-06-30 11:32:41 -04:00
|
|
|
}
|
2014-03-12 10:55:35 -04:00
|
|
|
|
2021-04-28 22:33:29 -04:00
|
|
|
/* Last-ditch serial number fallback */
|
|
|
|
if (!serial || !strlen(serial)) {
|
2014-03-13 10:58:33 -04:00
|
|
|
if (serial) free(serial);
|
2014-02-09 10:23:57 -05:00
|
|
|
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");
|
2017-05-05 08:06:28 -04:00
|
|
|
WARNING("**** must only plug one in at a time or unexpected behavior will occur!\n");
|
2014-02-11 16:33:53 -05:00
|
|
|
serial = strdup("NONE_UNKNOWN");
|
2013-07-16 21:08:33 -04:00
|
|
|
}
|
2014-02-09 23:46:02 -05:00
|
|
|
|
2013-07-17 22:43:49 -04:00
|
|
|
if (scan_only) {
|
2018-08-14 16:00:23 -04:00
|
|
|
if (!old_uri) {
|
|
|
|
fprintf(stdout, "direct %s://%s/%s \"%s\" \"%s\" \"%s\" \"\"\n",
|
2020-08-13 21:36:41 -04:00
|
|
|
uri_prefix, make, serial,
|
2018-08-14 16:00:23 -04:00
|
|
|
descr, descr,
|
|
|
|
ieee_id ? ieee_id : "");
|
|
|
|
} else {
|
|
|
|
int k = 0;
|
2013-12-18 07:48:30 -05:00
|
|
|
|
2018-08-14 16:00:23 -04:00
|
|
|
/* URLify the manuf and model strings */
|
|
|
|
strncpy(buf, manuf, sizeof(buf) - 2);
|
|
|
|
k = strlen(buf);
|
|
|
|
buf[k++] = '/';
|
|
|
|
buf[k] = 0;
|
2014-02-09 23:46:02 -05:00
|
|
|
|
2018-08-14 16:00:23 -04:00
|
|