I know this may be too late for 1.0.16 - but thought I'd submit it anyway. This patch adds usb functionality on OS/2 using the usbcalls interface from http://projects.netlabs.org/?category_id=13
I still need to patch configure.in to detect usbcalls.h and add a #define and link against usbcalls.. This shouldn't break anything on other platforms.... This isn't perfect yet, but works on quite a few usb scanners - see http://smedley.info/sane.html for details. Cheers, Paul. -------------- next part -------------- --- sanei_usb-cvs.c Sat Apr 16 06:18:24 2005 +++ sanei_usb.c Mon Jul 18 20:03:58 2005 @@ -1,5 +1,5 @@ /* sane - Scanner Access Now Easy. - Copyright (C) 2003 Rene Rebe (sanei_read_int) + Copyright (C) 2003 Rene Rebe (sanei_read_int,sanei_set_timeout) Copyright (C) 2001 - 2003 Henning Meier-Geinitz Copyright (C) 2001 Frank Zago (sanei_usb_control_msg) This file is part of the SANE package. @@ -62,6 +62,30 @@ #include <usb.h> #endif /* HAVE_LIBUSB */ +#ifdef HAVE_USBCALLS +#include <usb.h> +#include <os2.h> +#include <usbcalls.h> +#define MAX_RW 64000 +static int usbcalls_timeout = 30 * 1000; /* 30 seconds */ +USBHANDLE dh; +HEV UsbIrqStartHev; + +static +struct usb_descriptor_header *GetNextDescriptor( struct usb_descriptor_header *currHead, UCHAR *lastBytePtr) +{ + UCHAR *currBytePtr, *nextBytePtr; + + if (!currHead->bLength) + return (NULL); + currBytePtr=(UCHAR *)currHead; + nextBytePtr=currBytePtr+currHead->bLength; + if (nextBytePtr>=lastBytePtr) + return (NULL); + return ((struct usb_descriptor_header*)nextBytePtr); +} +#endif /* HAVE_USBCALLS */ + #define BACKEND_NAME sanei_usb #include "../include/sane/sane.h" #include "../include/sane/sanei_debug.h" @@ -72,7 +96,9 @@ { sanei_usb_method_scanner_driver = 0, /* kernel scanner driver (Linux, BSD) */ - sanei_usb_method_libusb + sanei_usb_method_libusb, + + sanei_usb_method_usbcalls } sanei_usb_access_method_type; @@ -138,6 +164,9 @@ void *data; } cmsg; +#elif defined(__BEOS__) +#include <drivers/USB_scanner.h> +#include <kernel/OS.h> #endif /* __linux__ */ static SANE_Bool inited = SANE_FALSE; @@ -202,7 +231,20 @@ DBG (3, "sanei_usb_get_vendor_product: ioctl (product) " "of device %d failed: %s\n", fd, strerror (errno)); } -#endif /* defined (__linux__) */ +#elif defined(__BEOS__) + { + uint16 vendor, product; + if (ioctl (fd, B_SCANNER_IOCTL_VENDOR, &vendor) != B_OK) + DBG (3, "kernel_get_vendor_product: ioctl (vendor) " + "of device %d failed: %s\n", fd, strerror (errno)); + if (ioctl (fd, B_SCANNER_IOCTL_PRODUCT, &product) != B_OK) + DBG (3, "sanei_usb_get_vendor_product: ioctl (product) " + "of device %d failed: %s\n", fd, strerror (errno)); + /* copy from 16 to 32 bit value */ + *vendorID = vendor; + *productID = product; + } +#endif /* defined (__linux__), defined(__BEOS__) */ /* put more os-dependant stuff ... */ } @@ -216,6 +258,8 @@ "/dev/usb/", "scanner", #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) "/dev/", "uscanner", +#elif defined(__BEOS__) + "/dev/scanner/usb/", "", #endif 0, 0 }; @@ -272,6 +316,10 @@ while ((dir_entry = readdir (dir)) != 0) { + /* skip standard dir entries */ + if (strcmp (dir_entry->d_name, ".") == 0 || strcmp (dir_entry->d_name, "..") == 0) + continue; + if (strncmp (base_name, dir_entry->d_name, strlen (base_name)) == 0) { if (strlen (dir_name) + strlen (dir_entry->d_name) + 1 > @@ -414,6 +462,81 @@ } } #endif /* HAVE_LIBUSB */ +#ifdef HAVE_USBCALLS + /* Check for devices using OS/2 USBCALLS Interface */ + + CHAR ucData[2048]; + struct usb_device_descriptor *pDevDesc; + struct usb_config_descriptor *pCfgDesc; + struct usb_interface_descriptor *intf; + struct usb_endpoint_descriptor *ep; + struct usb_descriptor_header *pDescHead; + + APIRET rc; + ULONG ulNumDev, ulDev, ulBufLen; + + ulBufLen = sizeof(ucData); + memset(&ucData,0,sizeof(ucData)); + rc = UsbQueryNumberDevices( &ulNumDev); + + if(rc==0 && ulNumDev) + { + for (ulDev=1; ulDev<=ulNumDev; ulDev++) + { + rc = UsbQueryDeviceReport( ulDev, + &ulBufLen, + ucData); + + pDevDesc = (struct usb_device_descriptor*)ucData; + pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor)); + int interface=0; + SANE_Bool found; + if (!pCfgDesc->bConfigurationValue) + { + DBG (1, "sanei_usb_init: device 0x%04x/0x%04x is not configured\n", + pDevDesc->idVendor, pDevDesc->idProduct); + continue; + } + if (pDevDesc->idVendor == 0 || pDevDesc->idProduct == 0) + { + DBG (5, "sanei_usb_init: device 0x%04x/0x%04x looks like a root hub\n", + pDevDesc->idVendor, pDevDesc->idProduct); + continue; + } + found = SANE_FALSE; + + if (pDevDesc->bDeviceClass = USB_CLASS_VENDOR_SPEC) + { + found = SANE_TRUE; + } + + if (!found) + { + DBG (5, "sanei_usb_init: device 0x%04x/0x%04x: no suitable interfaces\n", + pDevDesc->idVendor, pDevDesc->idProduct); + continue; + } + + snprintf (devname, sizeof (devname), "usbcalls:%d", + ulDev); + devices[dn].devname = strdup (devname); + devices[dn].fd = ulDev; /* store usbcalls device number */ + devices[dn].vendor = pDevDesc->idVendor; + devices[dn].product = pDevDesc->idProduct; + devices[dn].method = sanei_usb_method_usbcalls; + devices[dn].open = SANE_FALSE; + devices[dn].interface_nr = interface; + DBG (4, "sanei_usb_init: found usbcalls device (0x%04x/0x%04x) as device number %s\n", + pDevDesc->idVendor, pDevDesc->idProduct,devices[dn].devname); + dn++; + if (dn >= MAX_DEVICES) + return; + + } + } + +#endif /* HAVE_USBCALLS */ + DBG (5, "sanei_usb_init: found %d devices\n", dn); } @@ -486,13 +609,22 @@ return SANE_STATUS_UNSUPPORTED; #endif /* HAVE_LIBUSB */ } + else if (devices[dn].method == sanei_usb_method_usbcalls) + { +#ifdef HAVE_USBCALLS + vendorID = devices[dn].vendor; + productID = devices[dn].product; +#else + DBG (1, "sanei_usb_get_vendor_product: usbcalls support missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif /* HAVE_USBCALLS */ + } else { DBG (1, "sanei_usb_get_vendor_product: access method %d not " "implemented\n", devices[dn].method); return SANE_STATUS_INVAL; } - if (vendor) *vendor = vendorID; if (product) @@ -858,6 +990,153 @@ devname, strerror (errno)); } } + else if (devices[devcount].method == sanei_usb_method_usbcalls) + { +#ifdef HAVE_USBCALLS + CHAR ucData[2048]; + struct usb_device_descriptor *pDevDesc; + struct usb_config_descriptor *pCfgDesc; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_descriptor_header *pDescHead; + + ULONG ulBufLen; + ulBufLen = sizeof(ucData); + memset(&ucData,0,sizeof(ucData)); + + int result, num,rc; + int address, direction, transfer_type; + + DBG (5, "devname = %s, devcount = %d\n",devices[devcount].devname,devcount); + DBG (5, "USBCalls device number to open = %d\n",devices[devcount].fd); + DBG (5, "USBCalls Vendor/Product to open = 0x%04x/0x%04x\n", + devices[devcount].vendor,devices[devcount].product); + + rc = UsbOpen (&dh, + devices[devcount].vendor, + devices[devcount].product, + USB_ANY_PRODUCTVERSION, + USB_OPEN_FIRST_UNUSED); + DBG (1, "sanei_usb_open: UsbOpen rc = %d\n",rc); + if (rc!=0) + { + SANE_Status status = SANE_STATUS_INVAL; + DBG (1, "sanei_usb_open: can't open device `%s': %s\n", + devname, strerror (rc)); + return status; + } + rc = UsbQueryDeviceReport( devices[devcount].fd, + &ulBufLen, + ucData); + DBG (1, "sanei_usb_open: UsbQueryDeviceReport rc = %d\n",rc); + pDevDesc = (struct usb_device_descriptor*)ucData; + pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor)); + UCHAR *pCurPtr = (UCHAR*) pCfgDesc; + UCHAR *pEndPtr = pCurPtr+ pCfgDesc->wTotalLength; + pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength); + /* Set the configuration */ + if (pDevDesc->bNumConfigurations > 1) + { + DBG (3, "sanei_usb_open: more than one " + "configuration (%d), choosing first config (%d)\n", + pDevDesc->bNumConfigurations, + pCfgDesc->bConfigurationValue); + } + DBG (5, "UsbDeviceSetConfiguration parameters: dh = %p, bConfigurationValue = %d\n", + dh,pCfgDesc->bConfigurationValue); + result = UsbDeviceSetConfiguration (dh, + pCfgDesc->bConfigurationValue); + DBG (1, "sanei_usb_open: UsbDeviceSetConfiguration rc = %d\n",result); + if (result) + { + SANE_Status status = SANE_STATUS_INVAL; + DBG (1, "sanei_usb_open: usbcalls complained on UsbDeviceSetConfiguration, rc= %d\n", result); + status = SANE_STATUS_ACCESS_DENIED; + UsbClose (dh); + return status; + } + + /* Now we look for usable endpoints */ + + for (pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength); + pDescHead;pDescHead = GetNextDescriptor(pDescHead,pEndPtr) ) + { + switch(pDescHead->bDescriptorType) + { + case USB_DT_INTERFACE: + interface = (struct usb_interface_descriptor *) pDescHead; + DBG (5, "Found %d endpoints\n",interface->bNumEndpoints); + break; + case USB_DT_ENDPOINT: + endpoint = (struct usb_endpoint_descriptor*)pDescHead; + address = endpoint->bEndpointAddress; + //address = endpoint->bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK; + direction = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK; + transfer_type = endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK; + /* save the endpoints we need later */ + if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT) + { + DBG (5, "sanei_usb_open: found interupt-%s endpoint (address %2x)\n", + direction ? "in" : "out", address); + if (direction) /* in */ + { + if (devices[devcount].int_in_ep) + DBG (3, "sanei_usb_open: we already have a int-in endpoint " + "(address: %d), ignoring the new one\n", + devices[devcount].int_in_ep); + else + devices[devcount].int_in_ep = endpoint->bEndpointAddress; + } + else + if (devices[devcount].int_out_ep) + DBG (3, "sanei_usb_open: we already have a int-out endpoint " + "(address: %d), ignoring the new one\n", + devices[devcount].int_out_ep); + else + devices[devcount].int_out_ep = endpoint->bEndpointAddress; + } + else if (transfer_type == USB_ENDPOINT_TYPE_BULK) + { + DBG (5, "sanei_usb_open: found bulk-%s endpoint (address %2x)\n", + direction ? "in" : "out", address); + if (direction) /* in */ + { + if (devices[devcount].bulk_in_ep) + DBG (3, "sanei_usb_open: we already have a bulk-in endpoint " + "(address: %d), ignoring the new one\n", + devices[devcount].bulk_in_ep); + else + devices[devcount].bulk_in_ep = endpoint->bEndpointAddress; + } + else + { + if (devices[devcount].bulk_out_ep) + DBG (3, "sanei_usb_open: we already have a bulk-out endpoint " + "(address: %d), ignoring the new one\n", + devices[devcount].bulk_out_ep); + else + devices[devcount].bulk_out_ep = endpoint->bEndpointAddress; + } + } + /* ignore currently unsupported endpoints */ + else { + DBG (5, "sanei_usb_open: ignoring %s-%s endpoint " + "(address: %d)\n", + transfer_type == USB_ENDPOINT_TYPE_CONTROL ? "control" : + transfer_type == USB_ENDPOINT_TYPE_ISOCHRONOUS + ? "isochronous" : "interrupt", + direction ? "in" : "out", address); + continue; + } + break; + } + } +#else + DBG (1, "sanei_usb_open: can't open device `%s': " + "usbcalls support missing\n", devname); + return SANE_STATUS_UNSUPPORTED; +#endif /* HAVE_USBCALLS */ + } else { DBG (1, "sanei_usb_open: access method %d not implemented\n", @@ -889,6 +1168,16 @@ } if (devices[dn].method == sanei_usb_method_scanner_driver) close (devices[dn].fd); + else if (devices[dn].method == sanei_usb_method_usbcalls) + { +#ifdef HAVE_USBCALLS + int rc; + rc=UsbClose (dh); + DBG (5,"rc of UsbClose = %d\n",rc); +#else + DBG (1, "sanei_usb_close: usbcalls support missing\n"); +#endif + } else #ifdef HAVE_LIBUSB { @@ -913,6 +1202,16 @@ return; } +void +sanei_usb_set_timeout (SANE_Int timeout) +{ +#ifdef HAVE_LIBUSB + libusb_timeout = timeout; +#else + DBG (1, "sanei_usb_close: libusb support missing\n"); +#endif /* HAVE_LIBUSB */ +} + SANE_Status sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) { @@ -954,6 +1253,41 @@ return SANE_STATUS_UNSUPPORTED; } #endif /* not HAVE_LIBUSB */ + else if (devices[dn].method == sanei_usb_method_usbcalls) + { +#ifdef HAVE_USBCALLS + int rc; + while (*size) + { + ULONG ulToRead = (*size>MAX_RW)?MAX_RW:*size; + ULONG ulNum = ulToRead; + DBG (5, "Entered usbcalls UsbBulkRead with dn = %d\n",dn); + DBG (5, "Entered usbcalls UsbBulkRead with dh = %p\n",dh); + DBG (5, "Entered usbcalls UsbBulkRead with bulk_in_ep = 0x%02x\n",devices[dn].bulk_in_ep); + DBG (5, "Entered usbcalls UsbBulkRead with interface_nr = %d\n",devices[dn].interface_nr); + DBG (5, "Entered usbcalls UsbBulkRead with usbcalls_timeout = %d\n",usbcalls_timeout); + + if (devices[dn].bulk_in_ep){ + rc = UsbBulkRead (dh, devices[dn].bulk_in_ep, devices[dn].interface_nr, + &ulToRead, (char *) buffer, usbcalls_timeout); + DBG (1, "sanei_usb_read_bulk: rc = %d\n",rc);} + else + { + DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in endpoint\n"); + return SANE_STATUS_INVAL; + } + if (rc || (ulNum!=ulToRead)) return SANE_STATUS_INVAL; + *size -=ulToRead; + buffer += ulToRead; + read_size += ulToRead; + } +#else /* not HAVE_USBCALLS */ + { + DBG (1, "sanei_usb_read_bulk: usbcalls support missing\n"); + return SANE_STATUS_UNSUPPORTED; + } +#endif /* not HAVE_USBCALLS */ + } else { DBG (1, "sanei_usb_read_bulk: access method %d not implemented\n", @@ -1030,6 +1364,42 @@ return SANE_STATUS_UNSUPPORTED; } #endif /* not HAVE_LIBUSB */ + else if (devices[dn].method == sanei_usb_method_usbcalls) + { +#ifdef HAVE_USBCALLS + int rc; + DBG (5, "Entered usbcalls UsbBulkWrite with dn = %d\n",dn); + DBG (5, "Entered usbcalls UsbBulkWrite with dh = %p\n",dh); + DBG (5, "Entered usbcalls UsbBulkWrite with bulk_out_ep = 0x%02x\n",devices[dn].bulk_out_ep); + DBG (5, "Entered usbcalls UsbBulkWrite with interface_nr = %d\n",devices[dn].interface_nr); + DBG (5, "Entered usbcalls UsbBulkWrite with usbcalls_timeout = %d\n",usbcalls_timeout); + while (*size) + { + ULONG ulToWrite = (*size>MAX_RW)?MAX_RW:*size; + + DBG (5, "size requested to write = %lu, ulToWrite = %lu\n",(unsigned long) *size,ulToWrite); + if (devices[dn].bulk_out_ep){ + rc = UsbBulkWrite (dh, devices[dn].bulk_out_ep, devices[dn].interface_nr, + ulToWrite, (char*) buffer, usbcalls_timeout); + DBG (1, "sanei_usb_write_bulk: rc = %d\n",rc);} + else + { + DBG (1, "sanei_usb_write_bulk: can't read without a bulk-out endpoint\n"); + return SANE_STATUS_INVAL; + } + if (rc) return SANE_STATUS_INVAL; + *size -=ulToWrite; + buffer += ulToWrite; + write_size += ulToWrite; + DBG (5, "size = %d, write_size = %d\n",*size, write_size); + } +#else /* not HAVE_USBCALLS */ + { + DBG (1, "sanei_usb_write_bulk: usbcalls support missing\n"); + return SANE_STATUS_UNSUPPORTED; + } +#endif /* not HAVE_USBCALLS */ + } else { DBG (1, "sanei_usb_write_bulk: access method %d not implemented\n", @@ -1091,6 +1461,26 @@ if ((rtype & 0x80) && debug_level > 10) print_buffer (data, len); return SANE_STATUS_GOOD; +#elif defined(__BEOS__) + struct usb_scanner_ioctl_ctrlmsg c; + + c.req.request_type = rtype; + c.req.request = req; + c.req.value = value; + c.req.index = index; + c.req.length = len; + c.data = data; + + if (ioctl (devices[dn].fd, B_SCANNER_IOCTL_CTRLMSG, &c) < 0) + { + DBG (5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if ((rtype & 0x80) && debug_level > 10) + print_buffer (data, len); + + return SANE_STATUS_GOOD; #else /* not __linux__ */ DBG (5, "sanei_usb_control_msg: not supported on this OS\n"); return SANE_STATUS_UNSUPPORTED; @@ -1120,6 +1510,30 @@ return SANE_STATUS_UNSUPPORTED; } #endif /* not HAVE_LIBUSB */ + else if (devices[dn].method == sanei_usb_method_usbcalls) + { +#ifdef HAVE_USBCALLS + int result; + + result = UsbCtrlMessage (dh, rtype, req, + value, index, len, (char *) data, + usbcalls_timeout); + DBG (5, "rc of usb_control_msg = %d\n",result); + if (result < 0) + { + DBG (1, "sanei_usb_control_msg: usbcalls complained: %d\n",result); + return SANE_STATUS_INVAL; + } + if ((rtype & 0x80) && debug_level > 10) + print_buffer (data, len); + return SANE_STATUS_GOOD; +#else /* not HAVE_USBCALLS */ + { + DBG (1, "sanei_usb_control_msg: usbcalls support missing\n"); + return SANE_STATUS_UNSUPPORTED; + } +#endif /* not HAVE_USBCALLS */ + } else { DBG (1, "sanei_usb_control_msg: access method %d not implemented\n", @@ -1174,6 +1588,35 @@ return SANE_STATUS_UNSUPPORTED; } #endif /* not HAVE_LIBUSB */ + else if (devices[dn].method == sanei_usb_method_usbcalls) + { +#ifdef HAVE_USBCALLS + int rc; + USHORT usNumBytes=*size; + DBG (5, "Entered usbcalls UsbBulkWrite with dn = %d\n",dn); + DBG (5, "Entered usbcalls UsbBulkWrite with dh = %p\n",dh); + DBG (5, "Entered usbcalls UsbBulkWrite with int_in_ep = 0x%02x\n",devices[dn].int_in_ep); + DBG (5, "Entered usbcalls UsbBulkWrite with interface_nr = %d\n",devices[dn].interface_nr); + DBG (5, "Entered usbcalls UsbBulkWrite with bytes to read = %u\n",usNumBytes); + + if (devices[dn].int_in_ep){ + rc = UsbIrqStart (dh,devices[dn].int_in_ep,devices[dn].interface_nr, + usNumBytes, (char *) buffer, (HEV *) UsbIrqStartHev); + DBG (5, "rc of UsbIrqStart = %d\n",rc); + } + else + { + DBG (1, "sanei_usb_read_int: can't read without an int " + "endpoint\n"); + return SANE_STATUS_INVAL; + } + if (rc) return SANE_STATUS_INVAL; + read_size += usNumBytes; +#else + DBG (1, "sanei_usb_read_int: usbcalls support missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif /* HAVE_USBCALLS */ + } else { DBG (1, "sanei_usb_read_int: access method %d not implemented\n", From lists.ix.sane-de...@gregjohnson.com Mon Jul 18 12:13:41 2005 From: lists.ix.sane-de...@gregjohnson.com (Gregory C. Johnson) Date: Mon Jul 18 12:20:29 2005 Subject: [sane-devel] Color depth upsampling and compressibility In-Reply-To: <20050717105203.6b7328d...@mailwash5.pair.com> References: <20050717105203.6b7328d...@mailwash5.pair.com> Message-ID: <2502.69.40.28.111.1121688821.squir...@webmail3.pair.com> > On Mon, Jul 11, 2005 at 05:11:48PM +0200, Mattias Ellert wrote: >> Even better would be: >> >> *dst-- = ((*src << 4) & 0xf0) + ((*src) & 0x0f); >> *dst-- = ((*src) & 0xf0) + ((*src-- >> 4) & 0x0f); >> >> Then 0 would map to 0 and 15 to 255, i.e. white is white and black is >> black. > > You're right. I've changed taht in sp15c.c in CVS. Some quick Googling doesn't provide dispositive information on this strategy's impact on compressibility. However I suspect that introducing pseudo-random data in the low nybble will reduce compressibility beyond simply squaring the symbol table. (Hence, my initial ponderings on tweaking the constant) This might be a good candidate for specification in the SANE 2 standard, as there are clearly multiple strategies for getting from 4 to 8 bits, and each has different tradeoffs. Still, I can't imagine this dragon hasn't been slain many times before... Anyone know of a dispositive reference that we can just point to? Alternately, this should be trivial to explore exhaustively through brute-force. Thanks, -Greg