Files
ptouch_label/src/lib/libptouch_usb.c
knb e10a430f9e add cut bitmask option and debug dump support
Replace auto-cut toggle with --cut bit flags (default 011), wire flags through C/Ruby APIs, and document the new cut/debug-dump behavior in both READMEs.

Made-with: Cursor
2026-04-20 04:22:27 +09:00

208 lines
5.1 KiB
C

/*
* libptouch — USB (libusb): open/close, bulk transfers
*
* Author: knb
* Email: knb@artif.org
*/
#include "libptouch_internal.h"
#include <stdio.h>
#include <string.h>
/**
* バルク IN/OUT を両方持つインタフェースを探して claim する。
* USB Printer クラス (bInterfaceClass==7) を優先する。
*/
static int claim_interface_with_bulk(libusb_device_handle *h, int *out_iface,
uint8_t *out_ep, uint8_t *in_ep)
{
libusb_device *dev = libusb_get_device(h);
struct libusb_config_descriptor *cfg = NULL;
int r = libusb_get_active_config_descriptor(dev, &cfg);
if (r != 0 || !cfg)
return -1;
for (int pass = 0; pass < 2; pass++) {
for (int i = 0; i < cfg->bNumInterfaces; i++) {
const struct libusb_interface_descriptor *alt =
&cfg->interface[i].altsetting[0];
if (pass == 0 &&
alt->bInterfaceClass != LIBUSB_CLASS_PRINTER)
continue;
uint8_t ep_out = 0, ep_in = 0;
for (uint8_t k = 0; k < alt->bNumEndpoints; k++) {
const struct libusb_endpoint_descriptor *ep =
&alt->endpoint[k];
if ((ep->bmAttributes &
LIBUSB_TRANSFER_TYPE_MASK) ==
LIBUSB_TRANSFER_TYPE_BULK) {
if (ep->bEndpointAddress &
LIBUSB_ENDPOINT_IN)
ep_in = ep->bEndpointAddress;
else
ep_out = ep->bEndpointAddress;
}
}
if (!ep_out || !ep_in)
continue;
r = libusb_claim_interface(h, i);
if (r == 0) {
*out_iface = i;
*out_ep = ep_out;
*in_ep = ep_in;
libusb_free_config_descriptor(cfg);
return 0;
}
}
}
libusb_free_config_descriptor(cfg);
return -1;
}
libptouch_err_t libptouch_open_usb_vid_pid(libptouch_ctx *ctx, uint16_t vid,
uint16_t pid)
{
if (!ctx) {
return LIBPTOUCH_ERR_ARG;
}
if (ctx->usb_handle) {
ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "USB already open");
return LIBPTOUCH_ERR_ARG;
}
int r = libusb_init(&ctx->usb_ctx);
if (r != LIBUSB_SUCCESS) {
ptouch_set_error_usb(ctx, r, "libusb_init");
ctx->usb_ctx = NULL;
return LIBPTOUCH_ERR_USB;
}
libusb_device_handle *h =
libusb_open_device_with_vid_pid(ctx->usb_ctx, vid, pid);
if (!h) {
libusb_exit(ctx->usb_ctx);
ctx->usb_ctx = NULL;
char buf[160];
snprintf(buf, sizeof(buf),
"no USB device 0x%04x:0x%04x (check cable, power, udev)",
(unsigned)vid, (unsigned)pid);
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOT_FOUND, buf);
return LIBPTOUCH_ERR_NOT_FOUND;
}
ctx->usb_handle = h;
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106)
(void)libusb_set_auto_detach_kernel_driver(h, 1);
#endif
if (claim_interface_with_bulk(h, &ctx->claimed_interface,
&ctx->bulk_out_ep, &ctx->bulk_in_ep) != 0) {
ptouch_set_error(ctx, LIBPTOUCH_ERR_USB,
"no bulk IN/OUT interface (permissions or driver?)");
libusb_close(h);
ctx->usb_handle = NULL;
libusb_exit(ctx->usb_ctx);
ctx->usb_ctx = NULL;
return LIBPTOUCH_ERR_USB;
}
(void)libusb_clear_halt(h, ctx->bulk_out_ep);
(void)libusb_clear_halt(h, ctx->bulk_in_ep);
ctx->usb_pid = pid;
ctx->usb_open = 1;
ptouch_set_error(ctx, LIBPTOUCH_OK, "");
return LIBPTOUCH_OK;
}
libptouch_err_t libptouch_open_usb(libptouch_ctx *ctx)
{
return libptouch_open_usb_vid_pid(ctx, LIBPTOUCH_USB_VID_BROTHER,
LIBPTOUCH_USB_PID_PTP900W);
}
void libptouch_close(libptouch_ctx *ctx)
{
if (!ctx)
return;
if (ctx->usb_handle) {
if (ctx->claimed_interface >= 0) {
(void)libusb_release_interface(ctx->usb_handle,
ctx->claimed_interface);
ctx->claimed_interface = -1;
}
libusb_close(ctx->usb_handle);
ctx->usb_handle = NULL;
}
if (ctx->usb_ctx) {
libusb_exit(ctx->usb_ctx);
ctx->usb_ctx = NULL;
}
ctx->bulk_out_ep = 0;
ctx->bulk_in_ep = 0;
ctx->usb_pid = 0;
ctx->usb_open = 0;
}
int ptouch_bulk_out(libusb_device_handle *h, uint8_t ep, const uint8_t *data,
int len, unsigned int timeout_ms)
{
int tr = 0;
int r = libusb_bulk_transfer(h, ep, (unsigned char *)data, len, &tr,
(int)timeout_ms);
if (r != 0)
return r;
if (tr != len)
return LIBUSB_ERROR_IO;
return 0;
}
int ptouch_bulk_in_exact(libusb_device_handle *h, uint8_t ep, uint8_t *buf,
int len, unsigned int timeout_ms)
{
int got = 0;
while (got < len) {
int tr = 0;
int r = libusb_bulk_transfer(h, ep, buf + got, len - got, &tr,
(int)timeout_ms);
if (r != 0)
return r;
if (tr == 0)
return LIBUSB_ERROR_IO;
got += tr;
}
return 0;
}
libptouch_err_t ptouch_bulk_send_job(libptouch_ctx *ctx, const uint8_t *buf,
size_t len, const char *what)
{
int tr = 0;
int r = libusb_bulk_transfer(ctx->usb_handle, ctx->bulk_out_ep,
(unsigned char *)buf, (int)len, &tr,
120000);
if (r != 0) {
ptouch_set_error_usb(ctx, r, what);
return LIBPTOUCH_ERR_USB;
}
if ((size_t)tr != len) {
char msg[96];
snprintf(msg, sizeof(msg), "%s: short write %d/%zu", what, tr,
len);
ptouch_set_error(ctx, LIBPTOUCH_ERR_USB, msg);
return LIBPTOUCH_ERR_USB;
}
if (ctx->debug_dump_path && ctx->debug_dump_path[0]) {
FILE *fp = fopen(ctx->debug_dump_path,
ctx->debug_dump_truncate_next ? "wb" : "ab");
if (fp) {
(void)fwrite(buf, 1, len, fp);
(void)fclose(fp);
}
ctx->debug_dump_truncate_next = 0;
}
return LIBPTOUCH_OK;
}