From b9c25285e4321854ba90c73cf059117496957f2e Mon Sep 17 00:00:00 2001 From: b30052170 Date: Fri, 16 Aug 2024 10:21:21 +0800 Subject: [PATCH] oh usb print patch Signed-off-by:baozewei@huawei.com Signed-off-by: b30052170 --- config.h | 1 + install.py | 4 +- ohos-usb-print.patch | 1839 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1843 insertions(+), 1 deletion(-) create mode 100644 ohos-usb-print.patch diff --git a/config.h b/config.h index 0dbca8b0..12c92031 100644 --- a/config.h +++ b/config.h @@ -93,6 +93,7 @@ #endif /* HAVE_ARC4RANDOM */ #define HAVE_LIBUSB 1 +#define HAVE_OPENHARMONY 1 #define HAVE_STATFS 1 #define HAVE_STATVFS 1 #define HAVE_SYS_MOUNT_H 1 diff --git a/install.py b/install.py index 926db0a2..5728a131 100755 --- a/install.py +++ b/install.py @@ -49,6 +49,7 @@ def move_file(src_path, dst_path): "backport-CVE-2023-4504.patch", "backport-CVE-2024-35235.patch", "ohos-usb-manager.patch", + "ohos-usb-print.patch", "config.h" ] for file in files: @@ -79,7 +80,8 @@ def do_patch(target_dir): "backport-CVE-2023-4504.patch", "backport-CVE-2024-35235.patch", "ohos_ip_conflict.patch", - "ohos-usb-manager.patch" + "ohos-usb-manager.patch", + "ohos-usb-print.patch" ] for patch in patch_file: diff --git a/ohos-usb-print.patch b/ohos-usb-print.patch new file mode 100644 index 00000000..4deec3fd --- /dev/null +++ b/ohos-usb-print.patch @@ -0,0 +1,1839 @@ +diff --git a/backend/usb-oh.c b/backend/usb-oh.c +new file mode 100644 +index 0000000..4f313a9 +--- /dev/null ++++ b/backend/usb-oh.c +@@ -0,0 +1,1817 @@ ++/* ++ * Copyright (c) 2024 Huawei Device Co., Ltd. ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++/* ++ * OH USB interface code for CUPS. ++ */ ++ ++/* ++ * Include necessary headers. ++ */ ++ ++#include "usb_manager.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * WAIT_EOF_DELAY is number of seconds we'll wait for responses from ++ * the printer after we've finished sending all the data ++ */ ++ ++#define WAIT_EOF 0 ++#define WAIT_EOF_DELAY 7 ++#define WAIT_SIDE_DELAY 3 ++ ++/* ++ * Quirks: various printer quirks are handled by this structure and its flags. ++ * ++ * The quirks table used to be compiled into the backend but is now loaded from ++ * one or more files in the /usr/share/cups/usb directory. ++ */ ++ ++#define USB_QUIRK_BLACKLIST 0x0001 /* Does not conform to the spec */ ++#define USB_QUIRK_NO_REATTACH 0x0002 /* After printing we cannot re-attach ++ the usblp kernel module */ ++#define USB_QUIRK_SOFT_RESET 0x0004 /* After printing do a soft reset ++ for clean-up */ ++#define USB_QUIRK_UNIDIR 0x0008 /* Requires unidirectional mode */ ++#define USB_QUIRK_USB_INIT 0x0010 /* Needs vendor USB init string */ ++#define USB_QUIRK_VENDOR_CLASS 0x0020 /* Descriptor uses vendor-specific ++ Class or SubClass */ ++#define USB_QUIRK_DELAY_CLOSE 0x0040 /* Delay close */ ++#define USB_QUIRK_WHITELIST 0x0000 /* no quirks */ ++ ++/* ++ * Local types... ++ */ ++ ++typedef struct usb_printer_s /**** USB Printer Data ****/ ++{ ++ ohusb_device_descriptor *device; /* Device info */ ++ int conf, /* Configuration */ ++ origconf, /* Original configuration */ ++ iface, /* Interface */ ++ altset, /* Alternate setting */ ++ write_endp, /* Write endpoint */ ++ read_endp, /* Read endpoint */ ++ protocol, /* Protocol: 1 = Uni-di, 2 = Bi-di. */ ++ usblp_attached, /* "usblp" kernel module attached? */ ++ reset_after_job;/* Set to 1 by print_device() */ ++ unsigned quirks; /* Quirks flags */ ++ ohusb_pipe *pipe; /* Open pipe to device */ ++} usb_printer_t; ++ ++typedef struct usb_quirk_s /* USB "quirk" information */ ++{ ++ int vendor_id, /* Affected vendor ID */ ++ product_id; /* Affected product ID or 0 for all */ ++ unsigned quirks; /* Quirks bitfield */ ++} usb_quirk_t; ++ ++typedef int (*usb_cb_t)(usb_printer_t *, const char *, const char *, const void *); ++ ++typedef struct usb_globals_s /* Global USB printer information */ ++{ ++ usb_printer_t *printer; /* Printer */ ++ ++ pthread_mutex_t read_thread_mutex; ++ pthread_cond_t read_thread_cond; ++ int read_thread_stop; ++ int read_thread_done; ++ ++ pthread_mutex_t readwrite_lock_mutex; ++ pthread_cond_t readwrite_lock_cond; ++ int readwrite_lock; ++ ++ int print_fd; /* File descriptor to print */ ++ ssize_t print_bytes; /* Print bytes read */ ++ ++ int wait_eof; ++ int drain_output; /* Drain all pending output */ ++ int bidi_flag; /* 0=unidirectional, 1=bidirectional */ ++ ++ pthread_mutex_t sidechannel_thread_mutex; ++ pthread_cond_t sidechannel_thread_cond; ++ int sidechannel_thread_stop; ++ int sidechannel_thread_done; ++} usb_globals_t; ++ ++/* ++ * Globals... ++ */ ++ ++cups_array_t *all_quirks; /* Array of printer quirks */ ++usb_globals_t g = { 0 }; /* Globals */ ++ohusb_device_descriptor **all_list; /* List of connected USB devices */ ++ ++/* ++ * Local functions... ++ */ ++ ++static int close_device(usb_printer_t *printer); ++static int compare_quirks(usb_quirk_t *a, usb_quirk_t *b); ++static usb_printer_t *find_device(usb_cb_t cb, const void *data); ++static unsigned find_quirks(int vendor_id, int product_id); ++static int get_device_id(usb_printer_t *printer, char *buffer, size_t bufsize); ++static int list_cb(usb_printer_t *printer, const char *device_uri, const char *device_id, const void *data); ++static void load_quirks(void); ++static char *make_device_uri(usb_printer_t *printer, const char *device_id, char *uri, size_t uri_size); ++static int open_device(usb_printer_t *printer, int verbose); ++static int print_cb(usb_printer_t *printer, const char *device_uri, const char *device_id, const void *data); ++static void *read_thread(void *reference); ++static void *sidechannel_thread(void *reference); ++static void soft_reset(void); ++static int soft_reset_printer(usb_printer_t *printer); ++ ++/* ++ * 'list_devices()' - List the available printers. ++ */ ++ ++void ++list_devices(void) ++{ ++ load_quirks(); ++ ++ fputs("DEBUG: list_devices\n", stderr); ++ find_device(list_cb, NULL); ++ fputs("DEBUG: list_devices out\n", stderr); ++} ++ ++int /* O - Exit status */ ++print_device(const char *uri, /* I - Device URI */ ++ const char *hostname, /* I - Hostname/manufacturer */ ++ const char *resource, /* I - Resource/modelname */ ++ char *options, /* I - Device options/serial number */ ++ int print_fd, /* I - File descriptor to print */ ++ int copies, /* I - Copies to print */ ++ int argc, /* I - Number of command-line arguments (6 or 7) */ ++ char *argv[]) /* I - Command-line arguments */ ++{ ++ int bytes; /* Bytes written */ ++ ssize_t total_bytes; /* Total bytes written */ ++ struct sigaction action; /* Actions for POSIX signals */ ++ int status = CUPS_BACKEND_OK, /* Function results */ ++ iostatus; /* Current IO status */ ++ pthread_t read_thread_id, /* Read thread */ ++ sidechannel_thread_id; /* Side-channel thread */ ++ int have_sidechannel = 0, /* Was the side-channel thread started? */ ++ have_backchannel = 0; /* Do we have a back channel? */ ++ struct stat sidechannel_info; /* Side-channel file descriptor info */ ++ unsigned char print_buffer[8192], /* Print data buffer */ ++ *print_ptr; /* Pointer into print data buffer */ ++ fd_set input_set; /* Input set for select() */ ++ int nfds; /* Number of file descriptors */ ++ struct timeval *timeout, /* Timeout pointer */ ++ tv; /* Time value */ ++ struct timespec cond_timeout; /* pthread condition timeout */ ++ int num_opts; /* Number of options */ ++ cups_option_t *opts; /* Options */ ++ const char *val; /* Option value */ ++ ++ fputs("DEBUG: print_device\n", stderr); ++ ++ load_quirks(); ++ ++ /* ++ * See if the side-channel descriptor is valid... ++ */ ++ ++ have_sidechannel = !fstat(CUPS_SC_FD, &sidechannel_info) && ++ S_ISSOCK(sidechannel_info.st_mode); ++ ++ g.wait_eof = WAIT_EOF; ++ ++ /* ++ * Connect to the printer... ++ */ ++ ++ fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri); ++ while ((g.printer = find_device(print_cb, uri)) == NULL) ++ { ++ _cupsLangPrintFilter(stderr, "INFO", _("Waiting for printer to become available.")); ++ sleep(5); ++ } ++ ++ g.print_fd = print_fd; ++ ++ /* ++ * Some devices need a reset after finishing a job, these devices are ++ * marked with the USB_QUIRK_SOFT_RESET quirk. ++ */ ++ g.printer->reset_after_job = (g.printer->quirks & USB_QUIRK_SOFT_RESET ? 1 : 0); ++ ++ /* ++ * If we are printing data from a print driver on stdin, ignore SIGTERM ++ * so that the driver can finish out any page data, e.g. to eject the ++ * current page. We only do this for stdin printing as otherwise there ++ * is no way to cancel a raw print job... ++ */ ++ ++ if (!print_fd) ++ { ++ memset(&action, 0, sizeof(action)); ++ ++ sigemptyset(&action.sa_mask); ++ action.sa_handler = SIG_IGN; ++ sigaction(SIGTERM, &action, NULL); ++ } ++ ++ /* ++ * Start the side channel thread if the descriptor is valid... ++ */ ++ ++ pthread_mutex_init(&g.readwrite_lock_mutex, NULL); ++ pthread_cond_init(&g.readwrite_lock_cond, NULL); ++ g.readwrite_lock = 1; ++ ++ if (have_sidechannel) ++ { ++ g.sidechannel_thread_stop = 0; ++ g.sidechannel_thread_done = 0; ++ ++ pthread_cond_init(&g.sidechannel_thread_cond, NULL); ++ pthread_mutex_init(&g.sidechannel_thread_mutex, NULL); ++ ++ if (pthread_create(&sidechannel_thread_id, NULL, sidechannel_thread, NULL)) ++ { ++ fprintf(stderr, "DEBUG: Fatal USB error.\n"); ++ _cupsLangPrintFilter(stderr, "ERROR", _("There was an unrecoverable USB error.")); ++ fputs("DEBUG: Couldn't create side-channel thread.\n", stderr); ++ close_device(g.printer); ++ return (CUPS_BACKEND_STOP); ++ } ++ } ++ ++ /* ++ * Debug mode: If option "usb-unidir" is given, always deactivate ++ * backchannel ++ */ ++ ++ num_opts = cupsParseOptions(argv[5], 0, &opts); ++ val = cupsGetOption("usb-unidir", num_opts, opts); ++ if (val && strcasecmp(val, "no") && strcasecmp(val, "off") && strcasecmp(val, "false")) ++ { ++ g.printer->read_endp = -1; ++ fprintf(stderr, "DEBUG: Forced uni-directional communication " ++ "via \"usb-unidir\" option.\n"); ++ } ++ ++ /* ++ * Debug mode: If option "usb-no-reattach" is given, do not re-attach ++ * the usblp kernel module after the job has completed. ++ */ ++ ++ val = cupsGetOption("usb-no-reattach", num_opts, opts); ++ if (val && strcasecmp(val, "no") && strcasecmp(val, "off") && strcasecmp(val, "false")) ++ { ++ g.printer->usblp_attached = 0; ++ fprintf(stderr, "DEBUG: Forced not re-attaching the usblp kernel module " ++ "after the job via \"usb-no-reattach\" option.\n"); ++ } ++ ++ /* ++ * Get the read thread going... ++ */ ++ ++ if (g.printer->read_endp != -1) ++ { ++ have_backchannel = 1; ++ ++ g.read_thread_stop = 0; ++ g.read_thread_done = 0; ++ ++ pthread_cond_init(&g.read_thread_cond, NULL); ++ pthread_mutex_init(&g.read_thread_mutex, NULL); ++ ++ if (pthread_create(&read_thread_id, NULL, read_thread, NULL)) ++ { ++ fprintf(stderr, "DEBUG: Fatal USB error.\n"); ++ _cupsLangPrintFilter(stderr, "ERROR", _("There was an unrecoverable USB error.")); ++ fputs("DEBUG: Couldn't create read thread.\n", stderr); ++ close_device(g.printer); ++ return (CUPS_BACKEND_STOP); ++ } ++ } ++ else ++ fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel " ++ "deactivated.\n"); ++ ++ /* ++ * The main thread sends the print file... ++ */ ++ ++ g.drain_output = 0; ++ g.print_bytes = 0; ++ total_bytes = 0; ++ print_ptr = print_buffer; ++ ++ while (status == CUPS_BACKEND_OK && copies-- > 0) ++ { ++ _cupsLangPrintFilter(stderr, "INFO", _("Sending data to printer.")); ++ ++ if (print_fd != STDIN_FILENO) ++ { ++ fputs("PAGE: 1 1\n", stderr); ++ lseek(print_fd, 0, SEEK_SET); ++ } ++ ++ while (status == CUPS_BACKEND_OK) ++ { ++ FD_ZERO(&input_set); ++ ++ if (!g.print_bytes) ++ FD_SET(print_fd, &input_set); ++ ++ /* ++ * Calculate select timeout... ++ * If we have data waiting to send timeout is 100ms. ++ * else if we're draining print_fd timeout is 0. ++ * else we're waiting forever... ++ */ ++ ++ if (g.print_bytes) ++ { ++ tv.tv_sec = 0; ++ tv.tv_usec = 100000; /* 100ms */ ++ timeout = &tv; ++ } ++ else if (g.drain_output) ++ { ++ tv.tv_sec = 0; ++ tv.tv_usec = 0; ++ timeout = &tv; ++ } ++ else ++ timeout = NULL; ++ ++ /* ++ * I/O is unlocked around select... ++ */ ++ ++ pthread_mutex_lock(&g.readwrite_lock_mutex); ++ g.readwrite_lock = 0; ++ pthread_cond_signal(&g.readwrite_lock_cond); ++ pthread_mutex_unlock(&g.readwrite_lock_mutex); ++ ++ nfds = select(print_fd + 1, &input_set, NULL, NULL, timeout); ++ ++ /* ++ * Reacquire the lock... ++ */ ++ ++ pthread_mutex_lock(&g.readwrite_lock_mutex); ++ while (g.readwrite_lock) ++ pthread_cond_wait(&g.readwrite_lock_cond, &g.readwrite_lock_mutex); ++ g.readwrite_lock = 1; ++ pthread_mutex_unlock(&g.readwrite_lock_mutex); ++ ++ if (nfds < 0) ++ { ++ if (errno == EINTR && total_bytes == 0) ++ { ++ fputs("DEBUG: Received an interrupt before any bytes were " ++ "written, aborting.\n", stderr); ++ close_device(g.printer); ++ return (CUPS_BACKEND_OK); ++ } ++ else if (errno != EAGAIN && errno != EINTR) ++ { ++ _cupsLangPrintFilter(stderr, "ERROR", _("Unable to read print data.")); ++ perror("DEBUG: select"); ++ close_device(g.printer); ++ return (CUPS_BACKEND_FAILED); ++ } ++ } ++ ++ /* ++ * If drain output has finished send a response... ++ */ ++ ++ if (g.drain_output && !nfds && !g.print_bytes) ++ { ++ /* Send a response... */ ++ cupsSideChannelWrite(CUPS_SC_CMD_DRAIN_OUTPUT, CUPS_SC_STATUS_OK, NULL, 0, 1.0); ++ g.drain_output = 0; ++ } ++ ++ /* ++ * Check if we have print data ready... ++ */ ++ ++ if (FD_ISSET(print_fd, &input_set)) ++ { ++ g.print_bytes = read(print_fd, print_buffer, sizeof(print_buffer)); ++ ++ if (g.print_bytes < 0) ++ { ++ /* ++ * Read error - bail if we don't see EAGAIN or EINTR... ++ */ ++ ++ if (errno != EAGAIN && errno != EINTR) ++ { ++ _cupsLangPrintFilter(stderr, "ERROR", _("Unable to read print data.")); ++ perror("DEBUG: read"); ++ close_device(g.printer); ++ return (CUPS_BACKEND_FAILED); ++ } ++ ++ g.print_bytes = 0; ++ } ++ else if (g.print_bytes == 0) ++ { ++ /* ++ * End of file, break out of the loop... ++ */ ++ ++ break; ++ } ++ ++ print_ptr = print_buffer; ++ ++ fprintf(stderr, "DEBUG: Read %d bytes of print data...\n", (int)g.print_bytes); ++ } ++ ++ if (g.print_bytes) ++ { ++ ohusb_transfer_pipe tpipe = { ++ g.printer->iface, ++ g.printer->write_endp ++ }; ++ iostatus = OH_BulkTransferWrite(g.printer->pipe, &tpipe, ++ print_buffer, g.print_bytes, &bytes, 0); ++ /* ++ * Ignore timeout errors, but retain the number of bytes written to ++ * avoid sending duplicate data... ++ */ ++ ++ if (iostatus == OHUSB_ERROR_TIMEOUT) ++ { ++ fputs("DEBUG: Got USB transaction timeout during write.\n", stderr); ++ iostatus = 0; ++ } ++ ++ /* ++ * Retry a write after an aborted write since we probably just got ++ * SIGTERM... ++ */ ++ ++ else if (iostatus == OHUSB_ERROR_NO_DEVICE) ++ { ++ fputs("DEBUG: Got USB return aborted during write.\n", stderr); ++ ++ iostatus = OH_BulkTransferWrite(g.printer->pipe, &tpipe, ++ print_buffer, g.print_bytes, &bytes, 0); ++ } ++ ++ if (iostatus) ++ { ++ /* ++ * Write error - bail if we don't see an error we can retry... ++ */ ++ ++ _cupsLangPrintFilter(stderr, "ERROR", _("Unable to send data to printer.")); ++ fprintf(stderr, "DEBUG: ohusb write operation returned %x.\n", iostatus); ++ ++ status = CUPS_BACKEND_FAILED; ++ break; ++ } ++ else if (bytes > 0) ++ { ++ fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes); ++ ++ g.print_bytes -= bytes; ++ print_ptr += bytes; ++ total_bytes += bytes; ++ } ++ fprintf(stderr, "DEBUG: %d bytes left to write...\n", (int)g.print_bytes); ++ } ++ ++ if (print_fd != 0 && status == CUPS_BACKEND_OK) ++ fprintf(stderr, "DEBUG: Sending print file, " CUPS_LLFMT " bytes...\n", ++ CUPS_LLCAST total_bytes); ++ } ++ } ++ ++ fprintf(stderr, "DEBUG: Sent " CUPS_LLFMT " bytes...\n", ++ CUPS_LLCAST total_bytes); ++ ++ /* ++ * Signal the side channel thread to exit... ++ */ ++ ++ if (have_sidechannel) ++ { ++ close(CUPS_SC_FD); ++ pthread_mutex_lock(&g.readwrite_lock_mutex); ++ g.readwrite_lock = 0; ++ pthread_cond_signal(&g.readwrite_lock_cond); ++ pthread_mutex_unlock(&g.readwrite_lock_mutex); ++ ++ g.sidechannel_thread_stop = 1; ++ pthread_mutex_lock(&g.sidechannel_thread_mutex); ++ ++ if (!g.sidechannel_thread_done) ++ { ++ gettimeofday(&tv, NULL); ++ cond_timeout.tv_sec = tv.tv_sec + WAIT_SIDE_DELAY; ++ cond_timeout.tv_nsec = tv.tv_usec * 1000; ++ ++ while (!g.sidechannel_thread_done) ++ { ++ if (pthread_cond_timedwait(&g.sidechannel_thread_cond, ++ &g.sidechannel_thread_mutex, ++ &cond_timeout) != 0) ++ break; ++ } ++ } ++ ++ pthread_mutex_unlock(&g.sidechannel_thread_mutex); ++ } ++ ++ /* ++ * Signal the read thread to exit then wait 7 seconds for it to complete... ++ */ ++ ++ if (have_backchannel) ++ { ++ g.read_thread_stop = 1; ++ ++ pthread_mutex_lock(&g.read_thread_mutex); ++ ++ if (!g.read_thread_done) ++ { ++ fputs("DEBUG: Waiting for read thread to exit...\n", stderr); ++ ++ gettimeofday(&tv, NULL); ++ cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY; ++ cond_timeout.tv_nsec = tv.tv_usec * 1000; ++ ++ while (!g.read_thread_done) ++ { ++ if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex, ++ &cond_timeout) != 0) ++ break; ++ } ++ ++ /* ++ * If it didn't exit abort the pending read and wait an additional ++ * second... ++ */ ++ ++ if (!g.read_thread_done) ++ { ++ fputs("DEBUG: Read thread still active, aborting the pending read...\n", stderr); ++ ++ g.wait_eof = 0; ++ ++ gettimeofday(&tv, NULL); ++ cond_timeout.tv_sec = tv.tv_sec + 1; ++ cond_timeout.tv_nsec = tv.tv_usec * 1000; ++ ++ while (!g.read_thread_done) ++ { ++ if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex, ++ &cond_timeout) != 0) ++ break; ++ } ++ } ++ } ++ ++ pthread_mutex_unlock(&g.read_thread_mutex); ++ } ++ ++ /* ++ * Close the connection and input file and general clean up... ++ */ ++ ++ if (g.printer->quirks & USB_QUIRK_DELAY_CLOSE) ++ sleep(1); ++ ++ close_device(g.printer); ++ ++ fputs("DEBUG: print_device out\n", stderr); ++ return (status); ++} ++ ++/* ++ * 'find_device()' - Find or enumerate USB printers. ++ */ ++ ++static usb_printer_t * /* O - Found printer */ ++find_device(usb_cb_t cb, /* I - Callback function */ ++ const void *data) /* I - User data for callback */ ++{ ++ ohusb_device_descriptor *list = NULL; /* List of connected USB devices */ ++ ohusb_device_descriptor *device = NULL; /* Current device */ ++ ohusb_config_descriptor *confptr = NULL; /* Pointer to current configuration */ ++ const ohusb_interface *ifaceptr = NULL; /* Pointer to current interface */ ++ const ohusb_interface_descriptor *altptr = NULL; /* Pointer to current alternate setting */ ++ const ohusb_endpoint_descriptor *endpptr = NULL; /* Pointer to current endpoint */ ++ ssize_t numdevs, /* number of connected devices */ ++ i = 0; ++ uint8_t conf, /* Current configuration */ ++ iface, /* Current interface */ ++ altset, /* Current alternate setting */ ++ protocol, /* Current protocol */ ++ endp, /* Current endpoint */ ++ read_endp, /* Current read endpoint */ ++ write_endp; /* Current write endpoint */ ++ char device_id[1024], /* IEEE-1284 device ID */ ++ device_uri[1024]; /* Device URI */ ++ static usb_printer_t printer; /* Current printer */ ++ int32_t ret; ++ ++ /* ++ * get ohusb devices... ++ */ ++ ++ fputs("DEBUG: find_device\n", stderr); ++ ret = OH_GetDevices(&list, &numdevs); ++ if (ret != OHUSB_SUCCESS) { ++ fprintf(stderr, "DEBUG: find_device OH_GetDevices fail\n"); ++ return (NULL); ++ } ++ fprintf(stderr, "DEBUG: find_device OH_GetDevices numdevs=%d\n", (int)numdevs); ++ ++ /* ++ * Then loop through the devices it found... ++ */ ++ ++ if (numdevs > 0) ++ for (i = 0; i < numdevs; i++) ++ { ++ device = list + i; ++ ++ /* ++ * Ignore devices with no configuration data and anything that is not ++ * a printer... ++ */ ++ ++ if (device == NULL) ++ continue; ++ ++ fprintf(stderr, "DEBUG: OH_GetDevices index=%zd, busNum=%d, devAddr=%d, idVendor=%d, idProduct=%d\n", ++ i, device->busNum, device->devAddr, device->idVendor, device->idProduct); ++ ++ if (!device->bNumConfigurations || !device->idVendor || !device->idProduct) ++ continue; ++ ++ printer.quirks = find_quirks(device->idVendor, device->idProduct); ++ fprintf(stderr, "DEBUG: find_device quirks=%d\n", printer.quirks); ++ ++ /* ++ * Ignore blacklisted printers... ++ */ ++ ++ if (printer.quirks & USB_QUIRK_BLACKLIST) ++ continue; ++ ++ for (conf = 0, confptr = device->config; conf < device->bNumConfigurations; conf ++, confptr ++) ++ { ++ if (confptr == NULL) ++ continue; ++ for (iface = 0, ifaceptr = confptr->interface; ++ iface < confptr->bNumInterfaces; ++ iface ++, ifaceptr ++) ++ { ++ /* ++ * Some printers offer multiple interfaces... ++ */ ++ ++ protocol = 0; ++ if (ifaceptr == NULL) ++ continue; ++ ++ for (altset = 0, altptr = ifaceptr->altsetting; ++ altset < ifaceptr->num_altsetting; ++ altset ++, altptr ++) ++ { ++ if (altptr == NULL) ++ continue; ++ /* ++ * Currently we only support unidirectional and bidirectional ++ * printers. Future versions of this code will support the ++ * 1284.4 (packet mode) protocol as well. ++ */ ++ fprintf(stderr, "DEBUG: find_device class=%x, subclass=%x, protocol=%x\n", ++ altptr->bInterfaceClass, altptr->bInterfaceSubClass, altptr->bInterfaceProtocol); ++ if (((altptr->bInterfaceClass != OHUSB_CLASS_PRINTER || altptr->bInterfaceSubClass != 1) && ++ ((printer.quirks & USB_QUIRK_VENDOR_CLASS) == 0)) || ++ (altptr->bInterfaceProtocol != 1 && /* Unidirectional */ ++ altptr->bInterfaceProtocol != 2) || /* Bidirectional */ ++ altptr->bInterfaceProtocol < protocol) ++ { ++ fprintf(stderr, "DEBUG: Not a usb printer.\n"); ++ continue; ++ } ++ ++ if (printer.quirks & USB_QUIRK_VENDOR_CLASS) ++ fprintf(stderr, "DEBUG: Printer does not report class 7 and/or " ++ "subclass 1 but works as a printer anyway\n"); ++ ++ read_endp = 0xff; ++ write_endp = 0xff; ++ ++ for (endp = 0, endpptr = altptr->endpoint; ++ endp < altptr->bNumEndpoints; ++ endp ++, endpptr ++) ++ { ++ if ((endpptr->bmAttributes & OHUSB_TRANSFER_TYPE_MASK) == OHUSB_TRANSFER_TYPE_BULK) ++ { ++ if (endpptr->bEndpointAddress & OHUSB_ENDPOINT_DIR_MASK) ++ read_endp = endp; ++ else ++ write_endp = endp; ++ } ++ } ++ ++ if (write_endp != 0xff) ++ { ++ /* ++ * Save the best match so far... ++ */ ++ ++ protocol = altptr->bInterfaceProtocol; ++ printer.altset = altset; ++ printer.write_endp = write_endp; ++ if (protocol > 1) ++ printer.read_endp = read_endp; ++ else ++ printer.read_endp = -1; ++ } ++ } ++ ++ if (protocol > 0) ++ { ++ printer.device = device; ++ printer.conf = conf; ++ printer.iface = iface; ++ printer.protocol = protocol; ++ ++ if (!open_device(&printer, data != NULL)) ++ { ++ get_device_id(&printer, device_id, sizeof(device_id)); ++ make_device_uri(&printer, device_id, device_uri, sizeof(device_uri)); ++ ++ fprintf(stderr, "DEBUG2: Printer found with device ID: %s " ++ "Device URI: %s\n", ++ device_id, device_uri); ++ ++ if ((*cb)(&printer, device_uri, device_id, data)) ++ { ++ fprintf(stderr, "DEBUG: Device protocol: %d\n", printer.protocol); ++ if (printer.quirks & USB_QUIRK_UNIDIR) ++ { ++ printer.read_endp = -1; ++ fprintf(stderr, "DEBUG: Printer reports bi-di support " ++ "but in reality works only uni-directionally\n"); ++ } ++ if (printer.read_endp != -1) ++ { ++ printer.read_endp = confptr->interface[printer.iface]. ++ altsetting[printer.altset]. ++ endpoint[printer.read_endp]. ++ bEndpointAddress; ++ } ++ else ++ fprintf(stderr, "DEBUG: Uni-directional USB communication " ++ "only!\n"); ++ printer.write_endp = confptr->interface[printer.iface]. ++ altsetting[printer.altset]. ++ endpoint[printer.write_endp]. ++ bEndpointAddress; ++ if (printer.quirks & USB_QUIRK_NO_REATTACH) ++ { ++ printer.usblp_attached = 0; ++ fprintf(stderr, "DEBUG: Printer does not like usblp " ++ "kernel module to be re-attached after job\n"); ++ } ++ return (&printer); ++ } ++ ++ close_device(&printer); ++ } ++ } ++ } ++ } ++ } ++ ++ fputs("DEBUG: find_device out\n", stderr); ++ return (NULL); ++} ++ ++/* ++ * 'open_device()' - Open a connection to the USB printer. ++ */ ++ ++static int /* O - 0 on success, -1 on error */ ++open_device(usb_printer_t *printer, /* I - Printer */ ++ int verbose) /* I - Update connecting-to-device state? */ ++{ ++ ohusb_config_descriptor confptr; ++ /* Pointer to current configuration */ ++ int number1 = -1, /* Configuration/interface/altset */ ++ number2 = -1, /* numbers */ ++ errcode = 0; ++ char current; /* Current configuration */ ++ ++ fprintf(stderr, "DEBUG: open_device\n"); ++ ++ /* ++ * Return immediately if we are already connected... ++ */ ++ ++ if (printer->pipe) ++ return (0); ++ ++ /* ++ * Try opening the printer... ++ */ ++ ++ if ((errcode = OH_OpenDevice(printer->device, &(printer->pipe))) < 0) ++ { ++ fprintf(stderr, "DEBUG: Failed to open device, code: %d\n", errcode); ++ return (-1); ++ } ++ fprintf(stderr, "DEBUG: OH_OpenDevice success busNum=%d, devAddr=%d\n", ++ printer->pipe->busNum, printer->pipe->devAddr); ++ ++ printer->usblp_attached = 0; ++ printer->reset_after_job = 0; ++ ++ if (verbose) ++ fputs("STATE: +connecting-to-device\n", stderr); ++ if (printer->device == NULL) ++ { ++ fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n", errcode); ++ goto error; ++ } ++ ++ /* ++ * Set the desired configuration, but only if it needs changing. Some ++ * printers (e.g., Samsung) don't like OH_SetConfiguration. It will ++ * succeed, but the following print job is sometimes silently lost by the ++ * printer. ++ */ ++ ++ ohusb_control_transfer_parameter ctrlParam = { ++ OHUSB_REQUEST_TYPE_STANDARD | OHUSB_ENDPOINT_IN | OHUSB_RECIPIENT_DEVICE, ++ 8, 0, 0, 5000 ++ }; ++ ++ if (OH_ControlTransferRead(printer->pipe, &ctrlParam, (unsigned char *)¤t, 1) < 0) ++ { ++ current = 0; /* Assume not configured */ ++ } ++ ++ printer->origconf = current; ++ fprintf(stderr, "DEBUG: open_device OH_ControlTransferRead current = %c, printer->origconf = %d\n", ++ current, printer->origconf); ++ ++ confptr = printer->device->config[printer->conf]; ++ number1 = confptr.iConfiguration; ++ ++ if (number1 != current) ++ { ++ fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n", ++ current, number1); ++ if ((errcode = OH_SetConfiguration(printer->pipe, number1)) < 0) ++ { ++ /* ++ * If the set fails, chances are that the printer only supports a ++ * single configuration. Technically these printers don't conform to ++ * the USB printer specification, but otherwise they'll work... ++ */ ++ ++ if (errcode != OHUSB_ERROR_BUSY) ++ fprintf(stderr, "DEBUG: Failed to set configuration %d for %04x:%04x\n", ++ number1, printer->device->idVendor, printer->device->idProduct); ++ } ++ } ++ ++ /* ++ * Claim interfaces as needed... ++ */ ++ ++ number1 = confptr.interface[printer->iface].altsetting[printer->altset].bInterfaceNumber; ++ ++ if ((errcode = OH_ClaimInterface(printer->pipe, number1, true)) < 0) ++ { ++ fprintf(stderr, ++ "DEBUG: Failed to claim interface %d for %04x:%04x: %s\n", ++ number1, printer->device->idVendor, printer->device->idProduct, strerror(errno)); ++ ++ goto error; ++ } ++ ++ /* ++ * Set alternate setting, but only if there is more than one option. Some ++ * printers (e.g., Samsung) don't like usb_set_altinterface. ++ */ ++ ++ if (confptr.interface[printer->iface].num_altsetting > 1) ++ { ++ number1 = confptr.interface[printer->iface]. ++ altsetting[printer->altset].bInterfaceNumber; ++ number2 = confptr.interface[printer->iface]. ++ altsetting[printer->altset].bAlternateSetting; ++ while ((errcode = OH_SetInterface(printer->pipe, number1, number2)) < 0) ++ { ++ if (errcode != OHUSB_ERROR_BUSY) ++ { ++ fprintf(stderr, ++ "DEBUG: Failed to set alternate interface %d for %04x:%04x: " ++ "%s\n", ++ number2, printer->device->idVendor, printer->device->idProduct, strerror(errno)); ++ ++ goto error; ++ } ++ } ++ } ++ ++ if (verbose) ++ fputs("STATE: -connecting-to-device\n", stderr); ++ ++ return (0); ++ ++ /* ++ * If we get here, there was a hard error... ++ */ ++ ++ error: ++ ++ if (verbose) ++ fputs("STATE: -connecting-to-device\n", stderr); ++ ++ OH_CloseDevice(printer->pipe); ++ printer->pipe = NULL; ++ ++ return (-1); ++} ++ ++/* ++ * 'get_device_id()' - Get the IEEE-1284 device ID for the printer. ++ */ ++ ++static int /* O - 0 on success, -1 on error */ ++get_device_id(usb_printer_t *printer, /* I - Printer */ ++ char *buffer, /* I - String buffer */ ++ size_t bufsize) /* I - Number of bytes in buffer */ ++{ ++ fprintf(stderr, "DEBUG: get_device_id\n"); ++ ++ int length; /* Length of device ID */ ++ ++ ohusb_control_transfer_parameter ctrlParam = { ++ OHUSB_REQUEST_TYPE_CLASS | OHUSB_ENDPOINT_IN | OHUSB_RECIPIENT_INTERFACE, ++ 0, printer->conf, (printer->iface << 8) | printer->altset, 5000 ++ }; ++ ++ if (OH_ControlTransferRead(printer->pipe, &ctrlParam, (unsigned char *)buffer, bufsize) < 0) ++ { ++ *buffer = '\0'; ++ return (-1); ++ } ++ ++ /* ++ * Extract the length of the device ID string from the first two ++ * bytes. The 1284 spec says the length is stored MSB first... ++ */ ++ ++ length = (int)((((unsigned)buffer[0] & 255) << 8) | ((unsigned)buffer[1] & 255)); ++ ++ /* ++ * Check to see if the length is larger than our buffer or less than 14 bytes ++ * (the minimum valid device ID is "MFG:x;MDL:y;" with 2 bytes for the length). ++ * ++ * If the length is out-of-range, assume that the vendor incorrectly ++ * implemented the 1284 spec and re-read the length as LSB first,.. ++ */ ++ ++ if (length > bufsize || length < 14) ++ length = (int)((((unsigned)buffer[1] & 255) << 8) | ((unsigned)buffer[0] & 255)); ++ ++ if (length > bufsize) ++ length = bufsize; ++ ++ if (length < 14) ++ { ++ /* ++ * Invalid device ID, clear it! ++ */ ++ ++ *buffer = '\0'; ++ fprintf(stderr, "DEBUG: get_device_id Invalid device ID\n"); ++ return (-1); ++ } ++ ++ length -= 2; ++ fprintf(stderr, "DEBUG: get_device_id length = %d\n", length); ++ ++ /* ++ * Copy the device ID text to the beginning of the buffer and ++ * nul-terminate. ++ */ ++ ++ memmove(buffer, buffer + 2, (size_t)length); ++ buffer[length] = '\0'; ++ ++ return (0); ++} ++ ++/* ++ * 'make_device_uri()' - Create a device URI for a USB printer. ++ */ ++ ++static char * /* O - Device URI */ ++make_device_uri( ++ usb_printer_t *printer, /* I - Printer */ ++ const char *device_id, /* I - IEEE-1284 device ID */ ++ char *uri, /* I - Device URI buffer */ ++ size_t uri_size) /* I - Size of device URI buffer */ ++{ ++ char options[1024]; /* Device URI options */ ++ int num_values; /* Number of 1284 parameters */ ++ cups_option_t *values; /* 1284 parameters */ ++ const char *mfg, /* Manufacturer */ ++ *mdl, /* Model */ ++ *des = NULL, /* Description */ ++ *sern = NULL; /* Serial number */ ++ size_t mfglen; /* Length of manufacturer string */ ++ char tempmdl[256], /* Temporary model string */ ++ tempmfg[256], /* Temporary manufacturer string */ ++ tempsern[256], /* Temporary serial number string */ ++ *tempptr; /* Pointer into temp string */ ++ ++ fprintf(stderr, "DEBUG: make_device_uri\n"); ++ ++ /* ++ * Get the make, model, and serial numbers... ++ */ ++ ++ num_values = _cupsGet1284Values(device_id, &values); ++ ++ if (printer->device != NULL && printer->device->iSerialNumber) ++ { ++ // Try getting the serial number from the device itself... ++ int length = OH_GetStringDescriptor(printer->pipe, printer->device->iSerialNumber, (unsigned char *)tempsern, sizeof(tempsern) - 1); ++ if (length > 0) ++ { ++ tempsern[length] = '\0'; ++ sern = tempsern; ++ } ++ else ++ fputs("DEBUG2: iSerialNumber could not be read.\n", stderr); ++ } ++ else ++ fputs("DEBUG2: iSerialNumber is not present.\n", stderr); ++ ++ if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL) ++ { ++ if ((mfg = cupsGetOption("MFG", num_values, values)) == NULL && printer->device->iManufacturer) ++ { ++ int length = OH_GetStringDescriptor(printer->pipe, printer->device->iManufacturer, (unsigned char *)tempmfg, sizeof(tempmfg) - 1); ++ if (length > 0) ++ { ++ tempmfg[length] = '\0'; ++ mfg = tempmfg; ++ } ++ } ++ } ++ ++ if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) ++ { ++ if ((mdl = cupsGetOption("MDL", num_values, values)) == NULL && printer->device->iProduct) ++ { ++ int length = OH_GetStringDescriptor(printer->pipe, printer->device->iProduct, (unsigned char *)tempmdl, sizeof(tempmdl) - 1); ++ if (length > 0) ++ { ++ tempmdl[length] = '\0'; ++ mdl = tempmdl; ++ } ++ } ++ } ++ ++ /* ++ * To maintain compatibility with the original character device backend on ++ * Linux and *BSD, map manufacturer names... ++ */ ++ ++ if (mfg) ++ { ++ if (!_cups_strcasecmp(mfg, "Hewlett-Packard")) ++ mfg = "HP"; ++ else if (!_cups_strcasecmp(mfg, "Lexmark International")) ++ mfg = "Lexmark"; ++ } ++ else ++ { ++ /* ++ * No manufacturer? Use the model string or description... ++ */ ++ ++ if (mdl) ++ _ppdNormalizeMakeAndModel(mdl, tempmfg, sizeof(tempmfg)); ++ else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL || ++ (des = cupsGetOption("DES", num_values, values)) != NULL) ++ _ppdNormalizeMakeAndModel(des, tempmfg, sizeof(tempmfg)); ++ else ++ strlcpy(tempmfg, "Unknown", sizeof(tempmfg)); ++ ++ if ((tempptr = strchr(tempmfg, ' ')) != NULL) ++ *tempptr = '\0'; ++ ++ mfg = tempmfg; ++ } ++ ++ if (!mdl) ++ { ++ /* ++ * No model? Use description... ++ */ ++ if (des) ++ mdl = des; /* We remove the manufacturer name below */ ++ else if (!strncasecmp(mfg, "Unknown", 7)) ++ mdl = "Printer"; ++ else ++ mdl = "Unknown Model"; ++ } ++ ++ mfglen = strlen(mfg); ++ ++ if (!strncasecmp(mdl, mfg, mfglen) && _cups_isspace(mdl[mfglen])) ++ { ++ mdl += mfglen + 1; ++ ++ while (_cups_isspace(*mdl)) ++ mdl ++; ++ } ++ ++ /* ++ * Generate the device URI from the manufacturer, model, serial number, ++ * and interface number... ++ */ ++ ++ if (sern) ++ { ++ if (printer->iface > 0) ++ snprintf(options, sizeof(options), "?serial=%s&interface=%d", sern, printer->iface); ++ else ++ snprintf(options, sizeof(options), "?serial=%s", sern); ++ } ++ else if (printer->iface > 0) ++ snprintf(options, sizeof(options), "?interface=%d", printer->iface); ++ else ++ options[0] = '\0'; ++ ++ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, "usb", NULL, mfg, 0, "/%s%s", mdl, options); ++ ++ fprintf(stderr, "DEBUG2: make_device_uri sern=\"%s\", mfg=\"%s\", mdl=\"%s\"\n", sern, mfg, mdl); ++ ++ cupsFreeOptions(num_values, values); ++ ++ return (uri); ++} ++ ++/* ++ * 'close_device()' - Close the connection to the USB printer. ++ */ ++ ++static int /* I - 0 on success, -1 on failure */ ++close_device(usb_printer_t *printer) /* I - Printer */ ++{ ++ ohusb_config_descriptor confptr; /* Pointer to current configuration */ ++ ++ if (printer->pipe) ++ { ++ /* ++ * Release interfaces before closing so that we know all data is written ++ * to the device... ++ */ ++ ++ int errcode; /* Return value of ohusb function */ ++ int number1, /* Interface number */ ++ number2; /* Configuration number */ ++ ++ if (printer->device != NULL && printer->device->config != NULL) ++ { ++ confptr = printer->device->config[printer->conf]; ++ number1 = confptr.interface[printer->iface]. ++ altsetting[printer->altset].bInterfaceNumber; ++ OH_ReleaseInterface(printer->pipe, number1); ++ ++ number2 = confptr.iConfiguration; ++ ++ /* ++ * If we have changed the configuration from one valid configuration ++ * to another, restore the old one ++ */ ++ if (printer->origconf > 0 && printer->origconf != number2) ++ { ++ fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n", ++ number2, printer->origconf); ++ if ((errcode = OH_SetConfiguration(printer->pipe, printer->origconf)) < 0) ++ { ++ fprintf(stderr, ++ "DEBUG: Failed to set configuration %d for %04x:%04x\n", ++ printer->origconf, printer->device->idVendor, printer->device->idProduct); ++ } ++ } ++ } ++ else ++ fprintf(stderr, ++ "DEBUG: Failed to get configuration descriptor %d\n", ++ printer->conf); ++ ++ /* ++ * Close the interface and return... ++ */ ++ ++ if (OH_CloseDevice(printer->pipe) == OHUSB_SUCCESS) ++ printer->pipe = NULL; ++ } ++ ++ return (0); ++} ++ ++/* ++ * 'read_thread()' - Thread to read the backchannel data on. ++ */ ++ ++static void *read_thread(void *reference) ++{ ++ unsigned char readbuffer[512]; ++ int rbytes; ++ int readstatus; ++ ohusb_transfer_pipe tpipe = { ++ g.printer->iface, ++ g.printer->read_endp ++ }; ++ ++ ++ (void)reference; ++ ++ do ++ { ++ /* ++ * Try reading from the OUT (to host) endpoint... ++ */ ++ ++ rbytes = sizeof(readbuffer); ++ readstatus = OH_BulkTransferRead(g.printer->pipe, ++ &tpipe, readbuffer, rbytes, &rbytes); ++ if (readstatus == OHUSB_SUCCESS && rbytes > 0) ++ { ++ fprintf(stderr, "DEBUG: Read %d bytes of back-channel data...\n", (int)rbytes); ++ cupsBackChannelWrite((const char *)readbuffer, (size_t)rbytes, 1.0); ++ } ++ else ++ fprintf(stderr, "DEBUG: Got USB transaction error during read, errorcode=%d\n", readstatus); ++ ++ /* ++ * Make sure this loop executes no more than once every 250 miliseconds... ++ */ ++ ++ if ((readstatus != OHUSB_SUCCESS || rbytes == 0) && (g.wait_eof || !g.read_thread_stop)) ++ usleep(250000); ++ } ++ while (g.wait_eof || !g.read_thread_stop); ++ ++ /* ++ * Let the main thread know that we have completed the read thread... ++ */ ++ ++ pthread_mutex_lock(&g.read_thread_mutex); ++ g.read_thread_done = 1; ++ pthread_cond_signal(&g.read_thread_cond); ++ pthread_mutex_unlock(&g.read_thread_mutex); ++ ++ return (NULL); ++} ++ ++/* ++ * 'sidechannel_thread()' - Handle side-channel requests. ++ */ ++ ++static void* ++sidechannel_thread(void *reference) ++{ ++ cups_sc_command_t command; /* Request command */ ++ cups_sc_status_t status; /* Request/response status */ ++ char data[2048]; /* Request/response data */ ++ int datalen; /* Request/response data size */ ++ ++ ++ (void)reference; ++ ++ do ++ { ++ datalen = sizeof(data); ++ ++ if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0)) ++ { ++ if (status == CUPS_SC_STATUS_TIMEOUT) ++ continue; ++ else ++ break; ++ } ++ ++ switch (command) ++ { ++ case CUPS_SC_CMD_SOFT_RESET: /* Do a soft reset */ ++ fputs("DEBUG: CUPS_SC_CMD_SOFT_RESET received from driver...\n", stderr); ++ ++ soft_reset(); ++ cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, NULL, 0, 1.0); ++ fputs("DEBUG: Returning status CUPS_STATUS_OK with no bytes...\n", ++ stderr); ++ break; ++ ++ case CUPS_SC_CMD_DRAIN_OUTPUT: /* Drain all pending output */ ++ fputs("DEBUG: CUPS_SC_CMD_DRAIN_OUTPUT received from driver...\n", stderr); ++ ++ g.drain_output = 1; ++ break; ++ ++ case CUPS_SC_CMD_GET_BIDI: /* Is the connection bidirectional? */ ++ fputs("DEBUG: CUPS_SC_CMD_GET_BIDI received from driver...\n", stderr); ++ ++ data[0] = (g.printer->protocol >= 2 ? 1 : 0); ++ cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0); ++ ++ fprintf(stderr, ++ "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n", ++ data[0]); ++ break; ++ ++ case CUPS_SC_CMD_GET_DEVICE_ID: /* Return IEEE-1284 device ID */ ++ fputs("DEBUG: CUPS_SC_CMD_GET_DEVICE_ID received from driver...\n", stderr); ++ ++ datalen = sizeof(data); ++ if (get_device_id(g.printer, data, sizeof(data))) ++ { ++ status = CUPS_SC_STATUS_IO_ERROR; ++ datalen = 0; ++ } ++ else ++ { ++ status = CUPS_SC_STATUS_OK; ++ datalen = strlen(data); ++ } ++ cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, datalen, 1.0); ++ ++ if (datalen < sizeof(data)) ++ data[datalen] = '\0'; ++ else ++ data[sizeof(data) - 1] = '\0'; ++ ++ fprintf(stderr, ++ "DEBUG: Returning CUPS_SC_STATUS_OK with %d bytes (%s)...\n", ++ datalen, data); ++ break; ++ ++ case CUPS_SC_CMD_GET_STATE: /* Return device state */ ++ fputs("DEBUG: CUPS_SC_CMD_GET_STATE received from driver...\n", stderr); ++ ++ data[0] = CUPS_SC_STATE_ONLINE; ++ cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0); ++ ++ fprintf(stderr, ++ "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n", ++ data[0]); ++ break; ++ ++ case CUPS_SC_CMD_GET_CONNECTED: /* Return whether device is ++ connected */ ++ fputs("DEBUG: CUPS_SC_CMD_GET_CONNECTED received from driver...\n", stderr); ++ ++ data[0] = (g.printer->device ? 1 : 0); ++ cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0); ++ ++ fprintf(stderr, ++ "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n", ++ data[0]); ++ break; ++ ++ default: ++ fprintf(stderr, "DEBUG: Unknown side-channel command (%d) received " ++ "from driver...\n", command); ++ ++ cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED, NULL, 0, 1.0); ++ ++ fputs("DEBUG: Returned CUPS_SC_STATUS_NOT_IMPLEMENTED with no bytes...\n", stderr); ++ break; ++ } ++ } ++ while (!g.sidechannel_thread_stop); ++ ++ pthread_mutex_lock(&g.sidechannel_thread_mutex); ++ g.sidechannel_thread_done = 1; ++ pthread_cond_signal(&g.sidechannel_thread_cond); ++ pthread_mutex_unlock(&g.sidechannel_thread_mutex); ++ ++ return (NULL); ++} ++ ++/* ++ * 'load_quirks()' - Load all quirks files in the /usr/share/cups/usb directory. ++ */ ++ ++static void ++load_quirks(void) ++{ ++ const char *datadir; /* CUPS_DATADIR environment variable */ ++ char filename[1024], /* Filename */ ++ line[1024]; /* Line from file */ ++ cups_dir_t *dir; /* Directory */ ++ cups_dentry_t *dent; /* Directory entry */ ++ cups_file_t *fp; /* Quirks file */ ++ usb_quirk_t *quirk; /* New quirk */ ++ ++ ++ all_quirks = cupsArrayNew((cups_array_func_t)compare_quirks, NULL); ++ ++ if ((datadir = getenv("CUPS_DATADIR")) == NULL) ++ datadir = CUPS_DATADIR; ++ ++ snprintf(filename, sizeof(filename), "%s/usb", datadir); ++ if ((dir = cupsDirOpen(filename)) == NULL) ++ { ++ perror(filename); ++ return; ++ } ++ ++ fprintf(stderr, "DEBUG: Loading USB quirks from \"%s\".\n", filename); ++ ++ while ((dent = cupsDirRead(dir)) != NULL) ++ { ++ if (!S_ISREG(dent->fileinfo.st_mode)) ++ continue; ++ ++ snprintf(filename, sizeof(filename), "%s/usb/%s", datadir, dent->filename); ++ if ((fp = cupsFileOpen(filename, "r")) == NULL) ++ { ++ perror(filename); ++ continue; ++ } ++ ++ while (cupsFileGets(fp, line, sizeof(line))) ++ { ++ /* ++ * Skip blank and comment lines... ++ */ ++ ++ if (line[0] == '#' || !line[0]) ++ continue; ++ ++ /* ++ * Add a quirk... ++ */ ++ ++ if ((quirk = calloc(1, sizeof(usb_quirk_t))) == NULL) ++ { ++ perror("DEBUG: Unable to allocate memory for quirk"); ++ break; ++ } ++ ++ if (sscanf(line, "%x%x", &quirk->vendor_id, &quirk->product_id) < 1) ++ { ++ fprintf(stderr, "DEBUG: Bad line: %s\n", line); ++ free(quirk); ++ continue; ++ } ++ ++ if (strstr(line, " blacklist")) ++ quirk->quirks |= USB_QUIRK_BLACKLIST; ++ ++ if (strstr(line, " delay-close")) ++ quirk->quirks |= USB_QUIRK_DELAY_CLOSE; ++ ++ if (strstr(line, " no-reattach")) ++ quirk->quirks |= USB_QUIRK_NO_REATTACH; ++ ++ if (strstr(line, " soft-reset")) ++ quirk->quirks |= USB_QUIRK_SOFT_RESET; ++ ++ if (strstr(line, " unidir")) ++ quirk->quirks |= USB_QUIRK_UNIDIR; ++ ++ if (strstr(line, " usb-init")) ++ quirk->quirks |= USB_QUIRK_USB_INIT; ++ ++ if (strstr(line, " vendor-class")) ++ quirk->quirks |= USB_QUIRK_VENDOR_CLASS; ++ ++ cupsArrayAdd(all_quirks, quirk); ++ } ++ ++ cupsFileClose(fp); ++ } ++ ++ fprintf(stderr, "DEBUG: Loaded %d quirks.\n", cupsArrayCount(all_quirks)); ++ ++ cupsDirClose(dir); ++} ++ ++/* ++ * 'find_quirks()' - Find the quirks for the given printer, if any. ++ * ++ * First looks for an exact match, then looks for the vendor ID wildcard match. ++ */ ++ ++static unsigned /* O - Quirks flags */ ++find_quirks(int vendor_id, /* I - Vendor ID */ ++ int product_id) /* I - Product ID */ ++{ ++ usb_quirk_t key, /* Search key */ ++ *match; /* Matching quirk entry */ ++ ++ ++ key.vendor_id = vendor_id; ++ key.product_id = product_id; ++ ++ if ((match = cupsArrayFind(all_quirks, &key)) != NULL) ++ return (match->quirks); ++ ++ key.product_id = 0; ++ ++ if ((match = cupsArrayFind(all_quirks, &key)) != NULL) ++ return (match->quirks); ++ ++ return (USB_QUIRK_WHITELIST); ++} ++ ++/* ++ * 'compare_quirks()' - Compare two quirks entries. ++ */ ++ ++static int /* O - Result of comparison */ ++compare_quirks(usb_quirk_t *a, /* I - First quirk entry */ ++ usb_quirk_t *b) /* I - Second quirk entry */ ++{ ++ int result; /* Result of comparison */ ++ ++ if ((result = b->vendor_id - a->vendor_id) == 0) ++ result = b->product_id - a->product_id; ++ ++ return (result); ++} ++ ++/* ++ * 'print_cb()' - Find a USB printer for printing. ++ */ ++ ++static int /* O - 0 to continue, 1 to stop (found) */ ++print_cb(usb_printer_t *printer, /* I - Printer */ ++ const char *device_uri, /* I - Device URI */ ++ const char *device_id, /* I - IEEE-1284 device ID */ ++ const void *data) /* I - User data (make, model, S/N) */ ++{ ++ char requested_uri[1024], /* Requested URI */ ++ *requested_ptr, /* Pointer into requested URI */ ++ detected_uri[1024], /* Detected URI */ ++ *detected_ptr; /* Pointer into detected URI */ ++ ++ ++ /* ++ * If we have an exact match, stop now... ++ */ ++ ++ if (!strcmp((char *)data, device_uri)) ++ return (1); ++ ++ /* ++ * Work on copies of the URIs... ++ */ ++ ++ strlcpy(requested_uri, (char *)data, sizeof(requested_uri)); ++ strlcpy(detected_uri, device_uri, sizeof(detected_uri)); ++ ++ /* ++ * ohusb-discovered URIs can have an "interface" specification and this ++ * never happens for usblp-discovered URIs, so remove the "interface" ++ * specification from the URI which we are checking currently. This way a ++ * queue for a usblp-discovered printer can now be accessed via ohusb. ++ * ++ * Similarly, strip "?serial=NNN...NNN" as needed. ++ */ ++ ++ if ((requested_ptr = strstr(requested_uri, "?interface=")) == NULL) ++ requested_ptr = strstr(requested_uri, "&interface="); ++ if ((detected_ptr = strstr(detected_uri, "?interface=")) == NULL) ++ detected_ptr = strstr(detected_uri, "&interface="); ++ ++ if (!requested_ptr && detected_ptr) ++ { ++ /* ++ * Strip "[?&]interface=nnn" from the detected printer. ++ */ ++ ++ *detected_ptr = '\0'; ++ } ++ else if (requested_ptr && !detected_ptr) ++ { ++ /* ++ * Strip "[?&]interface=nnn" from the requested printer. ++ */ ++ ++ *requested_ptr = '\0'; ++ } ++ ++ if ((requested_ptr = strstr(requested_uri, "?serial=?")) != NULL) ++ { ++ /* ++ * Strip "?serial=?" from the requested printer. This is a special ++ * case, as "?serial=?" means no serial number and not the serial ++ * number '?'. This is not covered by the checks below... ++ */ ++ ++ *requested_ptr = '\0'; ++ } ++ ++ if ((requested_ptr = strstr(requested_uri, "?serial=")) == NULL && ++ (detected_ptr = strstr(detected_uri, "?serial=")) != NULL) ++ { ++ /* ++ * Strip "?serial=nnn" from the detected printer. ++ */ ++ ++ *detected_ptr = '\0'; ++ } ++ else if (requested_ptr && !detected_ptr) ++ { ++ /* ++ * Strip "?serial=nnn" from the requested printer. ++ */ ++ ++ *requested_ptr = '\0'; ++ } ++ ++ return (!strcmp(requested_uri, detected_uri)); ++} ++ ++/* ++ * 'list_cb()' - List USB printers for discovery. ++ */ ++ ++static int /* O - 0 to continue, 1 to stop */ ++list_cb(usb_printer_t *printer, /* I - Printer */ ++ const char *device_uri, /* I - Device URI */ ++ const char *device_id, /* I - IEEE-1284 device ID */ ++ const void *data) /* I - User data (not used) */ ++{ ++ char make_model[1024]; /* Make and model */ ++ ++ ++ /* ++ * Get the device URI and make/model strings... ++ */ ++ ++ if (backendGetMakeModel(device_id, make_model, sizeof(make_model))) ++ strlcpy(make_model, "Unknown", sizeof(make_model)); ++ ++ /* ++ * Report the printer... ++ */ ++ ++ cupsBackendReport("direct", device_uri, make_model, make_model, device_id, NULL); ++ ++ /* ++ * Keep going... ++ */ ++ ++ return (0); ++} ++ ++/* ++ * 'soft_reset()' - Send a soft reset to the device. ++ */ ++ ++static void ++soft_reset(void) ++{ ++ fd_set input_set; /* Input set for select() */ ++ struct timeval tv; /* Time value */ ++ char buffer[2048]; /* Buffer */ ++ struct timespec cond_timeout; /* pthread condition timeout */ ++ ++ ++ /* ++ * Send an abort once a second until the I/O lock is released by the main ++ * thread... ++ */ ++ ++ pthread_mutex_lock(&g.readwrite_lock_mutex); ++ while (g.readwrite_lock) ++ { ++ gettimeofday(&tv, NULL); ++ cond_timeout.tv_sec = tv.tv_sec + 1; ++ cond_timeout.tv_nsec = tv.tv_usec * 1000; ++ ++ while (g.readwrite_lock) ++ { ++ if (pthread_cond_timedwait(&g.readwrite_lock_cond, ++ &g.readwrite_lock_mutex, ++ &cond_timeout) != 0) ++ break; ++ } ++ } ++ ++ g.readwrite_lock = 1; ++ pthread_mutex_unlock(&g.readwrite_lock_mutex); ++ ++ /* ++ * Flush bytes waiting on print_fd... ++ */ ++ ++ g.print_bytes = 0; ++ ++ FD_ZERO(&input_set); ++ FD_SET(g.print_fd, &input_set); ++ ++ tv.tv_sec = 0; ++ tv.tv_usec = 0; ++ ++ while (select(g.print_fd+1, &input_set, NULL, NULL, &tv) > 0) ++ if (read(g.print_fd, buffer, sizeof(buffer)) <= 0) ++ break; ++ ++ /* ++ * Send the reset... ++ */ ++ ++ soft_reset_printer(g.printer); ++ ++ /* ++ * Release the I/O lock... ++ */ ++ ++ pthread_mutex_lock(&g.readwrite_lock_mutex); ++ g.readwrite_lock = 0; ++ pthread_cond_signal(&g.readwrite_lock_cond); ++ pthread_mutex_unlock(&g.readwrite_lock_mutex); ++} ++ ++ ++/* ++ * 'soft_reset_printer()' - Do the soft reset request specific to printers ++ * ++ * This soft reset is specific to the printer device class and is much less ++ * invasive than the general USB reset OH_ResetDevice(). Especially it ++ * does never happen that the USB addressing and configuration changes. What ++ * is actually done is that all buffers get flushed and the bulk IN and OUT ++ * pipes get reset to their default states. This clears all stall conditions. ++ * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf ++ */ ++ ++static int /* O - 0 on success, < 0 on error */ ++soft_reset_printer( ++ usb_printer_t *printer) /* I - Printer */ ++{ ++ ohusb_config_descriptor confptr; /* Pointer to current configuration */ ++ int interface, /* Interface to reset */ ++ errcode; /* Error code */ ++ ++ ++ if (printer->device == NULL) { ++ interface = printer->iface; ++ } else { ++ confptr = printer->device->config[printer->conf]; ++ interface = confptr.interface[printer->iface]. ++ altsetting[printer->altset].bInterfaceNumber; ++ } ++ ++ ohusb_control_transfer_parameter ctrlParam = { ++ OHUSB_REQUEST_TYPE_CLASS | OHUSB_ENDPOINT_OUT | OHUSB_RECIPIENT_OTHER, ++ 2, 0, interface, 5000 ++ }; ++ ++ if ((errcode = OH_ControlTransferWrite(printer->pipe, &ctrlParam, NULL, 0)) < 0) ++ { ++ ctrlParam.requestType = OHUSB_REQUEST_TYPE_CLASS | OHUSB_ENDPOINT_OUT | OHUSB_RECIPIENT_INTERFACE; ++ errcode = OH_ControlTransferWrite(printer->pipe, &ctrlParam, NULL, 0); ++ } ++ ++ ++ return (errcode); ++} +\ No newline at end of file +diff --git a/backend/usb.c b/backend/usb.c +index e1b2c03..50c1bc3 100644 +--- a/backend/usb.c ++++ b/backend/usb.c +@@ -42,7 +42,9 @@ int print_device(const char *uri, const char *hostname, + * Include the vendor-specific USB implementation... + */ + +-#ifdef HAVE_LIBUSB ++#ifdef HAVE_OPENHARMONY ++# include "usb-oh.c" ++#elif defined(HAVE_LIBUSB) + # include "usb-libusb.c" + #elif defined(__APPLE__) + # include "usb-darwin.c"