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
208 lines
5.1 KiB
C
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;
|
|
}
|