diff --git a/include/ptouch.h b/include/ptouch.h index 1e5c21b..2b245b5 100644 --- a/include/ptouch.h +++ b/include/ptouch.h @@ -107,6 +107,7 @@ struct _ptouch_dev { typedef struct _ptouch_dev *ptouch_dev; int ptouch_open(ptouch_dev *ptdev); +int ptouch_open_serial(ptouch_dev *ptdev, const char *serial); int ptouch_close(ptouch_dev ptdev); int ptouch_send(ptouch_dev ptdev, uint8_t *data, size_t len); int ptouch_init(ptouch_dev ptdev); @@ -130,6 +131,7 @@ int ptouch_rasterstart(ptouch_dev ptdev); int ptouch_sendraster(ptouch_dev ptdev, uint8_t *data, size_t len); void ptouch_rawstatus(uint8_t raw[32]); void ptouch_list_supported(); +int ptouch_list_connected(); const char* pt_mediatype(unsigned char media_type); const char* pt_tapecolor(unsigned char tape_color); diff --git a/ptouch-print.1 b/ptouch-print.1 index fb55623..0acd39c 100644 --- a/ptouch-print.1 +++ b/ptouch-print.1 @@ -34,6 +34,10 @@ Set the seconds ptouch-print is waiting for finishing a previous ptouch-print co Default is 1 second. 0 (zero) means wait forever. Useful if ptouch-print is used in a script multiple times, and the device is waiting for the user to use the mechanical cutter. +.TP +.BR \-\-serial\ \fI +Use the printer with this USB serial number, when more than one printer is connected. +Use \-\-list\-connected to see the serial numbers. .SS "Font selection options" .TP @@ -90,6 +94,9 @@ Show info about the tape detected (like printing width etc.) and exit. .TP .BR \-\-list-supported List all supported printers +.TP +.BR \-\-list-connected +List all connected printers with their USB serial numbers, for use with \-\-serial .SH DEFAULTS The default font used is 'DejaVuSans'. diff --git a/src/libptouch.c b/src/libptouch.c index c1c61a6..0ce5ee4 100644 --- a/src/libptouch.c +++ b/src/libptouch.c @@ -112,6 +112,13 @@ struct _pt_dev_info ptdevs[] = { }; int ptouch_open(ptouch_dev *ptdev) +{ + return ptouch_open_serial(ptdev, NULL); +} + +/* Like ptouch_open(), but when serial is not NULL only a printer whose + USB serial number matches it is used. */ +int ptouch_open_serial(ptouch_dev *ptdev, const char *serial) { libusb_device **devs; libusb_device *dev; @@ -148,22 +155,43 @@ int ptouch_open(ptouch_dev *ptdev) } for (int k=0; ptdevs[k].vid > 0; ++k) { if ((desc.idVendor == ptdevs[k].vid) && (desc.idProduct == ptdevs[k].pid) && (ptdevs[k].flags >= 0)) { - fprintf(stderr, _("%s found on USB bus %d, device %d\n"), + unsigned char buf[64] = {0}; + if ((r=libusb_open(dev, &handle)) != 0) { + fprintf(stderr, _("libusb_open error :%s\n"), libusb_error_name(r)); + if (serial) { + /* can't read its serial, so it can't be the one we want */ + break; + } + libusb_free_device_list(devs, 1); + return -1; + } + if (desc.iSerialNumber) { + libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)); + } + fprintf(stderr, _("%s found on USB bus %d, device %d, serial %s\n"), ptdevs[k].name, libusb_get_bus_number(dev), - libusb_get_device_address(dev)); + libusb_get_device_address(dev), + buf[0] ? (char *)buf : "-"); + /* check the serial before the P-Lite/unsupported checks, so + a different printer in the wrong mode doesn't end the search */ + if (serial && (strcmp((char *)buf, serial) != 0)) { + libusb_close(handle); + handle = NULL; + break; /* next device */ + } if (ptdevs[k].flags & FLAG_PLITE) { printf("Printer is in P-Lite Mode, which is unsupported\n\n"); printf("Turn off P-Lite mode by changing switch from position EL to position E\n"); printf("or by pressing the PLite button for ~ 2 seconds (or consult the manual)\n"); + libusb_close(handle); + libusb_free_device_list(devs, 1); return -1; } if (ptdevs[k].flags & FLAG_UNSUP_RASTER) { printf("Unfortunately, that printer currently is unsupported (it has a different raster data transfer)\n"); - return -1; - } - if ((r=libusb_open(dev, &handle)) != 0) { - fprintf(stderr, _("libusb_open error :%s\n"), libusb_error_name(r)); + libusb_close(handle); + libusb_free_device_list(devs, 1); return -1; } libusb_free_device_list(devs, 1); @@ -186,7 +214,11 @@ int ptouch_open(ptouch_dev *ptdev) } } } - fprintf(stderr, _("No P-Touch printer found on USB (remember to put switch to position E)\n")); + if (serial) { + fprintf(stderr, _("No P-Touch printer with serial %s found\n"), serial); + } else { + fprintf(stderr, _("No P-Touch printer found on USB (remember to put switch to position E)\n")); + } libusb_free_device_list(devs, 1); return -1; } @@ -555,6 +587,57 @@ void ptouch_list_supported() return; } +/* Print every connected P-Touch printer with its USB serial number, which + can then be passed to ptouch_open_serial(). Returns the number of + printers found, or -1 on error. */ +int ptouch_list_connected() +{ + libusb_device **devs; + libusb_device *dev; + libusb_device_handle *handle; + struct libusb_device_descriptor desc; + int r, i=0, found=0; + + if ((libusb_init(NULL)) < 0) { + fprintf(stderr, _("libusb_init() failed\n")); + return -1; + } + if (libusb_get_device_list(NULL, &devs) < 0) { + libusb_exit(NULL); + return -1; + } + while ((dev=devs[i++]) != NULL) { + if (libusb_get_device_descriptor(dev, &desc) < 0) { + continue; + } + for (int k=0; ptdevs[k].vid > 0; ++k) { + if ((desc.idVendor == ptdevs[k].vid) && (desc.idProduct == ptdevs[k].pid) && (ptdevs[k].flags >= 0)) { + unsigned char buf[64] = {0}; + const char *serial = "-"; + if ((r=libusb_open(dev, &handle)) != 0) { + serial = libusb_error_name(r); + } else { + if (desc.iSerialNumber && (libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)) > 0)) { + serial = (char *)buf; + } + libusb_close(handle); + } + printf(_("%s\tserial %s\t(USB bus %d, device %d)%s\n"), + ptdevs[k].name, serial, + libusb_get_bus_number(dev), + libusb_get_device_address(dev), + (ptdevs[k].flags & FLAG_PLITE) ? _(" - in P-Lite mode, unsupported") : + (ptdevs[k].flags & FLAG_UNSUP_RASTER) ? _(" - unsupported") : ""); + ++found; + break; + } + } + } + libusb_free_device_list(devs, 1); + libusb_exit(NULL); + return found; +} + const char* pt_mediatype(const uint8_t media_type) { switch (media_type) { diff --git a/src/ptouch-print.c b/src/ptouch-print.c index c1dff6e..f5f2c65 100644 --- a/src/ptouch-print.c +++ b/src/ptouch-print.c @@ -65,6 +65,7 @@ struct arguments { char *save_png; int verbose; int timeout; + char *serial; }; typedef enum { JOB_CUTMARK, JOB_IMAGE, JOB_PAD, JOB_TEXT, JOB_UNDEFINED } job_type_t; @@ -109,6 +110,7 @@ static struct argp_option options[] = { { "force-tape-width", 6, "", 0, "Set tape width in pixels, use together with --writepng without a printer connected", 1}, { "copies", 7, "", 0, "Sets the number of identical prints", 1}, { "timeout", 8, "", 0, "Set timeout waiting for finishing previous job. Default:1, or a per-printer minimum where one is known. 0 means infinity", 1}, + { "serial", 12, "", 0, "Use the printer with this USB serial number", 1 }, { 0, 0, 0, 0, "print commands:", 2}, { "image", 'i', "", 0, "Print the given image which must be a 2 color (black/white) png", 2}, @@ -123,6 +125,7 @@ static struct argp_option options[] = { { 0, 0, 0, 0, "other commands:", 3}, { "info", 20, 0, 0, "Show info about detected tape", 3}, { "list-supported", 21, 0, 0, "Show printers supported by this version", 3}, + { "list-connected", 22, 0, 0, "Show connected printers and their USB serial numbers (for --serial)", 3}, { 0 } }; @@ -141,7 +144,8 @@ struct arguments arguments = { .forced_tape_width = 0, .save_png = NULL, .verbose = 0, - .timeout = -1 /* -1 = not given; 0 is already "infinity" */ + .timeout = -1, /* -1 = not given; 0 is already "infinity" */ + .serial = NULL }; job_t *jobs = NULL; @@ -688,6 +692,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 11: // precut arguments->precut = true; break; + case 12: // serial + arguments->serial = arg; + break; case 'a': // align if ((strcmp(arg, "c") == 0) || (strcmp(arg, "center") == 0)) { arguments->align = ALIGN_CENTER; @@ -709,6 +716,13 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 21: // list-supported ptouch_list_supported(); exit(0); + case 22: { // list-connected + int n = ptouch_list_connected(); + if (n == 0) { + fprintf(stderr, _("No P-Touch printer found on USB (remember to put switch to position E)\n")); + } + exit(n > 0 ? 0 : 5); + } case ARGP_KEY_ARG: argp_failure(state, 1, E2BIG, _("No arguments supported")); break; @@ -745,7 +759,7 @@ int main(int argc, char *argv[]) argp_parse(&argp, argc, argv, 0, 0, &arguments); if (!arguments.forced_tape_width) { - if ((ptouch_open(&ptdev)) < 0) { + if ((ptouch_open_serial(&ptdev, arguments.serial)) < 0) { return 5; } if (ptouch_init(ptdev) != 0) {