/* * libptouch — USB (libusb): open/close, bulk transfers * * Author: knb * Email: knb@artif.org */ #include "libptouch_internal.h" #include #include /** * バルク 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; }