#include <usb.h>
#include <poll.h>
typedef struct usb_printer_s
{
struct usb_device *device;
int conf,
iface,
altset,
write_endp,
read_endp;
struct usb_dev_handle *handle;
} usb_printer_t;
typedef int (*usb_cb_t)(usb_printer_t *, const char *, const char *,
const void *);
static int close_device(usb_printer_t *printer);
static usb_printer_t *find_device(usb_cb_t cb, const void *data);
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 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 ssize_t side_cb(usb_printer_t *printer, int print_fd);
void
list_devices(void)
{
fputs("DEBUG: list_devices\n", stderr);
find_device(list_cb, NULL);
}
int
print_device(const char *uri,
const char *hostname,
const char *resource,
char *options,
int print_fd,
int copies,
int argc,
char *argv[])
{
usb_printer_t *printer;
ssize_t bytes,
tbytes;
char buffer[8192];
struct sigaction action;
struct pollfd pfds[2];
fputs("DEBUG: print_device\n", stderr);
while ((printer = find_device(print_cb, uri)) == NULL)
{
_cupsLangPuts(stderr,
_("INFO: Waiting for printer to become available...\n"));
sleep(5);
}
if (!print_fd)
{
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_IGN;
sigaction(SIGTERM, &action, NULL);
}
tbytes = 0;
pfds[0].fd = print_fd;
pfds[0].events = POLLIN;
pfds[1].fd = CUPS_SC_FD;
pfds[1].events = POLLIN;
while (copies > 0 && tbytes >= 0)
{
copies --;
if (print_fd != 0)
{
fputs("PAGE: 1 1\n", stderr);
lseek(print_fd, 0, SEEK_SET);
}
while (poll(pfds, 2, -1) > 0)
{
if (pfds[0].revents & POLLIN)
{
if ((bytes = read(print_fd, buffer, sizeof(buffer))) > 0)
{
if (usb_bulk_write(printer->handle, printer->write_endp, buffer,
bytes, 45000) < 0)
{
_cupsLangPrintf(stderr,
_("ERROR: Unable to write %d bytes to printer!\n"),
(int)bytes);
tbytes = -1;
break;
}
tbytes += bytes;
}
else if (bytes == 0 || (bytes < 0 && errno != EAGAIN && errno != EINTR))
break;
}
if (pfds[1].revents & POLLIN)
{
if ((bytes = side_cb(printer, print_fd)) < 0)
pfds[1].events = 0;
else
tbytes += bytes;
}
}
}
close_device(printer);
return (CUPS_BACKEND_OK);
}
static int
close_device(usb_printer_t *printer)
{
if (printer->handle)
{
int number = printer->device->config[printer->conf].
interface[printer->iface].
altsetting[printer->altset].bInterfaceNumber;
usb_release_interface(printer->handle, number);
if (number != 0)
usb_release_interface(printer->handle, 0);
usb_close(printer->handle);
printer->handle = NULL;
}
return (0);
}
static usb_printer_t *
find_device(usb_cb_t cb,
const void *data)
{
struct usb_bus *bus;
struct usb_device *device;
struct usb_config_descriptor *confptr;
struct usb_interface *ifaceptr;
struct usb_interface_descriptor *altptr;
struct usb_endpoint_descriptor *endpptr;
int conf,
iface,
altset,
protocol,
endp,
read_endp,
write_endp;
char device_id[1024],
device_uri[1024];
static usb_printer_t printer;
usb_init();
fprintf(stderr, "DEBUG: usb_find_busses=%d\n", usb_find_busses());
fprintf(stderr, "DEBUG: usb_find_devices=%d\n", usb_find_devices());
for (bus = usb_get_busses(); bus; bus = bus->next)
for (device = bus->devices; device; device = device->next)
{
if (!device->config || !device->descriptor.idVendor ||
!device->descriptor.idProduct)
continue;
for (conf = 0, confptr = device->config;
conf < device->descriptor.bNumConfigurations;
conf ++, confptr ++)
for (iface = 0, ifaceptr = confptr->interface;
iface < confptr->bNumInterfaces;
iface ++, ifaceptr ++)
{
protocol = 0;
for (altset = 0, altptr = ifaceptr->altsetting;
altset < ifaceptr->num_altsetting;
altset ++, altptr ++)
{
if (altptr->bInterfaceClass != USB_CLASS_PRINTER ||
altptr->bInterfaceSubClass != 1 ||
(altptr->bInterfaceProtocol != 1 &&
altptr->bInterfaceProtocol != 2) ||
altptr->bInterfaceProtocol < protocol)
continue;
read_endp = -1;
write_endp = -1;
for (endp = 0, endpptr = altptr->endpoint;
endp < altptr->bNumEndpoints;
endp ++, endpptr ++)
if ((endpptr->bmAttributes & USB_ENDPOINT_TYPE_MASK) ==
USB_ENDPOINT_TYPE_BULK)
{
if (endpptr->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
read_endp = endp;
else
write_endp = endp;
}
if (write_endp >= 0)
{
protocol = altptr->bInterfaceProtocol;
printer.altset = altset;
printer.write_endp = write_endp;
printer.read_endp = read_endp;
}
}
if (protocol > 0)
{
printer.device = device;
printer.conf = conf;
printer.iface = iface;
printer.handle = NULL;
if (!open_device(&printer, data != NULL))
{
if (!get_device_id(&printer, device_id, sizeof(device_id)))
{
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));
if ((*cb)(&printer, device_uri, device_id, data))
{
printer.read_endp = printer.device->config[printer.conf].
interface[printer.iface].
altsetting[printer.altset].
endpoint[printer.read_endp].
bEndpointAddress;
printer.write_endp = printer.device->config[printer.conf].
interface[printer.iface].
altsetting[printer.altset].
endpoint[printer.write_endp].
bEndpointAddress;
return (&printer);
}
}
close_device(&printer);
}
}
}
}
return (NULL);
}
static int
get_device_id(usb_printer_t *printer,
char *buffer,
size_t bufsize)
{
int length;
if (usb_control_msg(printer->handle,
USB_TYPE_CLASS | USB_ENDPOINT_IN | USB_RECIP_INTERFACE,
0, printer->conf, printer->iface,
buffer, bufsize, 5000) < 0)
{
*buffer = '\0';
return (-1);
}
length = (((unsigned)buffer[0] & 255) << 8) +
((unsigned)buffer[1] & 255);
if (length > bufsize)
length = (((unsigned)buffer[1] & 255) << 8) +
((unsigned)buffer[0] & 255);
if (length > bufsize)
length = bufsize;
length -= 2;
memmove(buffer, buffer + 2, length);
buffer[length] = '\0';
return (0);
}
static int
list_cb(usb_printer_t *printer,
const char *device_uri,
const char *device_id,
const void *data)
{
char make_model[1024];
backendGetMakeModel(device_id, make_model, sizeof(make_model));
cupsBackendReport("direct", device_uri, make_model, make_model, device_id,
NULL);
return (0);
}
static char *
make_device_uri(
usb_printer_t *printer,
const char *device_id,
char *uri,
size_t uri_size)
{
char options[1024];
int num_values;
cups_option_t *values;
const char *mfg,
*mdl,
*des,
*sern;
char tempmfg[256],
tempsern[256],
*tempptr;
num_values = _ppdGet1284Values(device_id, &values);
if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
if ((sern = cupsGetOption("SN", num_values, values)) == NULL)
{
int length = usb_get_string_simple(printer->handle,
printer->device->descriptor.
iSerialNumber,
tempsern, sizeof(tempsern) - 1);
if (length > 0)
{
tempsern[length] = '\0';
sern = tempsern;
}
}
if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
mfg = cupsGetOption("MFG", num_values, values);
if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
mdl = cupsGetOption("MDL", num_values, values);
#ifdef __APPLE__
if (!mfg)
#else
if (mfg)
{
if (!strcasecmp(mfg, "Hewlett-Packard"))
mfg = "HP";
else if (!strcasecmp(mfg, "Lexmark International"))
mfg = "Lexmark";
}
else
#endif
{
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 (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);
cupsFreeOptions(num_values, values);
return (uri);
}
static int
open_device(usb_printer_t *printer,
int verbose)
{
int number;
if (printer->handle)
return (0);
if ((printer->handle = usb_open(printer->device)) == NULL)
return (-1);
if (verbose)
fputs("STATE: +connecting-to-device\n", stderr);
number = printer->device->config[printer->conf].bConfigurationValue;
if (usb_set_configuration(printer->handle, number) < 0)
{
if (errno != EBUSY)
fprintf(stderr, "DEBUG: Failed to set configuration %d for %04x:%04x\n",
number, printer->device->descriptor.idVendor,
printer->device->descriptor.idProduct);
}
number = printer->device->config[printer->conf].interface[printer->iface].
altsetting[printer->altset].bInterfaceNumber;
while (usb_claim_interface(printer->handle, number) < 0)
{
if (errno != EBUSY)
fprintf(stderr, "DEBUG: Failed to claim interface %d for %04x:%04x: %s\n",
number, printer->device->descriptor.idVendor,
printer->device->descriptor.idProduct, strerror(errno));
goto error;
}
if (number != 0)
while (usb_claim_interface(printer->handle, 0) < 0)
{
if (errno != EBUSY)
fprintf(stderr, "DEBUG: Failed to claim interface 0 for %04x:%04x: %s\n",
printer->device->descriptor.idVendor,
printer->device->descriptor.idProduct, strerror(errno));
goto error;
}
number = printer->device->config[printer->conf].interface[printer->iface].
altsetting[printer->altset].bAlternateSetting;
while (usb_set_altinterface(printer->handle, number) < 0)
{
if (errno != EBUSY)
fprintf(stderr,
"DEBUG: Failed to set alternate interface %d for %04x:%04x: %s\n",
number, printer->device->descriptor.idVendor,
printer->device->descriptor.idProduct, strerror(errno));
goto error;
}
if (verbose)
fputs("STATE: -connecting-to-device\n", stderr);
return (0);
error:
if (verbose)
fputs("STATE: -connecting-to-device\n", stderr);
usb_close(printer->handle);
printer->handle = NULL;
return (-1);
}
static int
print_cb(usb_printer_t *printer,
const char *device_uri,
const char *device_id,
const void *data)
{
return (!strcmp((char *)data, device_uri));
}
static ssize_t
side_cb(usb_printer_t *printer,
int print_fd)
{
ssize_t bytes,
tbytes;
char buffer[8192];
struct pollfd pfd;
cups_sc_command_t command;
cups_sc_status_t status;
char data[2048];
int datalen;
tbytes = 0;
datalen = sizeof(data);
if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
return (-1);
switch (command)
{
case CUPS_SC_CMD_DRAIN_OUTPUT :
pfd.fd = print_fd;
pfd.events = POLLIN;
while (poll(&pfd, 1, 1000) > 0)
{
if ((bytes = read(print_fd, buffer, sizeof(buffer))) > 0)
{
while (usb_bulk_write(printer->handle, printer->write_endp, buffer,
bytes, 5000) < 0)
{
_cupsLangPrintf(stderr,
_("ERROR: Unable to write %d bytes to printer!\n"),
(int)bytes);
tbytes = -1;
break;
}
tbytes += bytes;
}
else if (bytes < 0 && errno != EAGAIN && errno != EINTR)
break;
}
if (tbytes < 0)
status = CUPS_SC_STATUS_IO_ERROR;
else
status = CUPS_SC_STATUS_OK;
datalen = 0;
break;
case CUPS_SC_CMD_GET_BIDI :
status = CUPS_SC_STATUS_OK;
data[0] = 0;
datalen = 1;
break;
case CUPS_SC_CMD_GET_DEVICE_ID :
if (get_device_id(printer, data, sizeof(data)))
{
status = CUPS_SC_STATUS_IO_ERROR;
datalen = 0;
}
else
{
status = CUPS_SC_STATUS_OK;
datalen = strlen(data);
}
break;
default :
status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
datalen = 0;
break;
}
cupsSideChannelWrite(command, status, data, datalen, 1.0);
return (tbytes);
}