diff --git a/Makefile b/Makefile index 587fe0e..1aa4e15 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ LDLIBS=-lm -lusb-1.0 -CFLAGS=-Os -Wall +CFLAGS=-Os -Wall -Wextra DOCCO=docco usbscale: usbscale.c scales.h diff --git a/usbscale.c b/usbscale.c index 241298d..ab858d8 100644 --- a/usbscale.c +++ b/usbscale.c @@ -25,6 +25,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ + +#include #include #include #include @@ -54,10 +56,10 @@ along with this program. If not, see . #define CONTROL_REPORT_SIZE 2 // -// **find_scale** takes a libusb device list and finds the first USB device +// **is_scale** takes a libusb device list and finds the first USB device // that matches a device listed in scales.h. // -static libusb_device* find_scale(libusb_device**); +static bool is_scale(libusb_device*); // // **print_scale_data** takes the 6-byte output from the scale and interprets // it, printing out the result to the screen. It also returns a 1 if the @@ -103,6 +105,7 @@ int main(int argc, char **argv) ssize_t cnt; libusb_device* dev; libusb_device_handle* handle; + bool found_scale = false; int weigh_count = WEIGH_COUNT -1; @@ -127,154 +130,167 @@ int main(int argc, char **argv) return (int) cnt; // - // Once we have the list, we use **find_scale** to loop through and match - // every device against the scales.h list. **find_scale** will return the + // Once we have the list, we use **is_scale** to loop through and match + // every device against the scales.h list. **is_scale** will return the // first device that matches, or 0 if none of them matched. // - dev = find_scale(devs); - if(dev == 0) { - fprintf(stderr, "No USB scale found on this computer.\n"); - return -1; - } - // - // Once we have a pointer to the USB scale in question, we open it. - // - r = libusb_open(dev, &handle); - // - // Note that this requires that we have permission to access this device. - // If you get the "permission denied" error, check your udev rules. - // - if(r < 0) { - if(r == LIBUSB_ERROR_ACCESS) { - fprintf(stderr, "Permission denied to scale.\n"); + int i = 0; + while ((dev = devs[i++]) != NULL) { + if (!is_scale(dev)) { + continue; } - else if(r == LIBUSB_ERROR_NO_DEVICE) { - fprintf(stderr, "Scale has been disconnected.\n"); - } - return -1; - } - // - // On Linux, we typically need to detach the kernel driver so that we can - // handle this USB device. We are a userspace tool, after all. - // -#ifdef __linux__ - libusb_detach_kernel_driver(handle, 0); -#endif - // - // Finally, we can claim the interface to this device and begin I/O. - // - libusb_claim_interface(handle, 0); + found_scale = true; - - - /* - * Try to transfer data about status - * - * http://rowsandcolumns.blogspot.com/2011/02/read-from-magtek-card-swipe-reader-in.html - */ - unsigned char data[WEIGH_REPORT_SIZE]; - int len; - len = 0; - int scale_result = -1; - - // lowest bit is Enforced Zero Return, second bit is Zero Scale - unsigned char tare_report[] = {0x02, 0x02}; - - if (argc > 1 && strncmp(argv[1], "zero", 5) == 0) { - r = libusb_interrupt_transfer( - handle, - LIBUSB_ENDPOINT_OUT + 2, // direction=host to device, type=standard, recipient=device - tare_report, - CONTROL_REPORT_SIZE, - &len, - 10000 - ); - - if (r != 0) { - fprintf(stderr, "errno=%s r=%d (%s) transferred %d bytes\n", strerror(errno), r, libusb_error_name(r), len); - } else { - fprintf(stderr, "tared\n"); - } - } - - // - // For some reason, we get old data the first time, so let's just get that - // out of the way now. It can't hurt to grab another packet from the scale. - // - r = libusb_interrupt_transfer( - handle, - //bmRequestType => direction: in, type: class, - // recipient: interface - LIBUSB_ENDPOINT_IN + 1, - data, - WEIGH_REPORT_SIZE, // length of data - &len, - 10000 //timeout => 10 sec - ); - // - // We read data from the scale in an infinite loop, stopping when - // **print_scale_data** tells us that it's successfully gotten the weight - // from the scale, or if the scale or transmissions indicates an error. - // - for(;;) { // - // A `libusb_interrupt_transfer` of 6 bytes from the scale is the - // typical scale data packet, and the usage is laid out in *HID Point - // of Sale Usage Tables*, version 1.02. + // Once we have a pointer to the USB scale in question, we open it. + // + r = libusb_open(dev, &handle); + // + // Note that this requires that we have permission to access this device. + // If you get the "permission denied" error, check your udev rules. + // + if(r < 0) { + if(r == LIBUSB_ERROR_ACCESS) { + fprintf(stderr, "Permission denied to scale.\n"); + } + else if(r == LIBUSB_ERROR_NO_DEVICE) { + fprintf(stderr, "Scale has been disconnected.\n"); + } + return -1; + } + // + // On Linux, we typically need to detach the kernel driver so that we can + // handle this USB device. We are a userspace tool, after all. + // + #ifdef __linux__ + libusb_detach_kernel_driver(handle, 0); + #endif + // + // Finally, we can claim the interface to this device and begin I/O. + // + libusb_claim_interface(handle, 0); + + + + /* + * Try to transfer data about status + * + * http://rowsandcolumns.blogspot.com/2011/02/read-from-magtek-card-swipe-reader-in.html + */ + unsigned char data[WEIGH_REPORT_SIZE]; + int len; + len = 0; + int scale_result = -1; + + // lowest bit is Enforced Zero Return, second bit is Zero Scale + unsigned char tare_report[] = {0x02, 0x02}; + + if (argc > 1 && strncmp(argv[1], "zero", 5) == 0) { + r = libusb_interrupt_transfer( + handle, + LIBUSB_ENDPOINT_OUT + 2, // direction=host to device, type=standard, recipient=device + tare_report, + CONTROL_REPORT_SIZE, + &len, + 10000 + ); + + if (r != 0) { + fprintf(stderr, "errno=%s r=%d (%s) transferred %d bytes\n", strerror(errno), r, libusb_error_name(r), len); + } else { + fprintf(stderr, "tared\n"); + } + } + + // + // For some reason, we get old data the first time, so let's just get that + // out of the way now. It can't hurt to grab another packet from the scale. // r = libusb_interrupt_transfer( handle, //bmRequestType => direction: in, type: class, // recipient: interface - get_first_endpoint_address(dev), + LIBUSB_ENDPOINT_IN + 1, data, WEIGH_REPORT_SIZE, // length of data &len, 10000 //timeout => 10 sec ); // - // If the data transfer succeeded, then we pass along the data we - // received to **print_scale_data**. + // We read data from the scale in an infinite loop, stopping when + // **print_scale_data** tells us that it's successfully gotten the weight + // from the scale, or if the scale or transmissions indicates an error. // - if(r == 0) { -#ifdef DEBUG - int i; - for(i = 0; i < WEIGH_REPORT_SIZE; i++) { - printf("%x\n", data[i]); + for(;;) { + // + // A `libusb_interrupt_transfer` of 6 bytes from the scale is the + // typical scale data packet, and the usage is laid out in *HID Point + // of Sale Usage Tables*, version 1.02. + // + r = libusb_interrupt_transfer( + handle, + //bmRequestType => direction: in, type: class, + // recipient: interface + get_first_endpoint_address(dev), + data, + WEIGH_REPORT_SIZE, // length of data + &len, + 10000 //timeout => 10 sec + ); + // + // If the data transfer succeeded, then we pass along the data we + // received to **print_scale_data**. + // + if(r == 0) { + #ifdef DEBUG + int i; + for(i = 0; i < WEIGH_REPORT_SIZE; i++) { + printf("%x\n", data[i]); + } + #endif + if (weigh_count < 1) { + scale_result = print_scale_data(data); + if(scale_result != 1) + break; + } + weigh_count--; } -#endif - if (weigh_count < 1) { - scale_result = print_scale_data(data); - if(scale_result != 1) - break; + else { + fprintf(stderr, "Error in USB transfer\n"); + scale_result = -1; + break; } - weigh_count--; } - else { - fprintf(stderr, "Error in USB transfer\n"); - scale_result = -1; - break; + + // + // At the end, we make sure that we reattach the kernel driver that we + // detached earlier, close the handle to the device, free the device list + // that we retrieved, and exit libusb. + // + #ifdef __linux__ + libusb_attach_kernel_driver(handle, 0); + #endif + libusb_close(handle); + libusb_free_device_list(devs, 1); + libusb_exit(NULL); + + // + // The return code will be 0 for success or -1 for errors (see + // `libusb_init` above if it's neither 0 nor -1). + // + if (scale_result != 0) { + return scale_result; } } - // - // At the end, we make sure that we reattach the kernel driver that we - // detached earlier, close the handle to the device, free the device list - // that we retrieved, and exit libusb. + // if no scales were found, make sure to show a message // -#ifdef __linux__ - libusb_attach_kernel_driver(handle, 0); -#endif - libusb_close(handle); - libusb_free_device_list(devs, 1); - libusb_exit(NULL); - - // - // The return code will be 0 for success or -1 for errors (see - // `libusb_init` above if it's neither 0 nor -1). - // - return scale_result; + if(!found_scale) { + fprintf(stderr, "No USB scale found on this computer.\n"); + return -1; + } + return 0; } // @@ -372,75 +388,65 @@ static int print_scale_data(unsigned char* dat) { } // -// find_scale +// is_scale // ---------- // -// **find_scale** takes a `libusb_device\*\*` list and loop through it, +// **is_scale** takes a `libusb_device\*\*` list and loop through it, // matching each device's vendor and product IDs to the scales.h list. It // return the first matching `libusb_device\*` or 0 if no matching device is // found. // -static libusb_device* find_scale(libusb_device **devs) +static bool is_scale(libusb_device *dev) { - int i = 0; - libusb_device* dev = 0; - - // - // Loop through each USB device, and for each device, loop through the - // scales list to see if it's one of our listed scales. - // - while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor desc; - int r = libusb_get_device_descriptor(dev, &desc); - if (r < 0) { - fprintf(stderr, "failed to get device descriptor"); - return NULL; - } - int i; - for (i = 0; i < NSCALES; i++) { - if(desc.idVendor == scales[i][0] && - desc.idProduct == scales[i][1]) { - /* - * Debugging data about found scale - */ + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) { + fprintf(stderr, "failed to get device descriptor"); + return NULL; + } + int i; + for (i = 0; i < NSCALES; i++) { + if(desc.idVendor == scales[i][0] && + desc.idProduct == scales[i][1]) { + /* + * Debugging data about found scale + */ #ifdef DEBUG - fprintf(stderr, - "Found scale %04x:%04x (bus %d, device %d)\n", - desc.idVendor, - desc.idProduct, - libusb_get_bus_number(dev), - libusb_get_device_address(dev)); + fprintf(stderr, + "Found scale %04x:%04x (bus %d, device %d)\n", + desc.idVendor, + desc.idProduct, + libusb_get_bus_number(dev), + libusb_get_device_address(dev)); - fprintf(stderr, - "It has descriptors:\n\tmanufc: %d\n\tprodct: %d\n\tserial: %d\n\tclass: %d\n\tsubclass: %d\n", - desc.iManufacturer, - desc.iProduct, - desc.iSerialNumber, - desc.bDeviceClass, - desc.bDeviceSubClass); + fprintf(stderr, + "It has descriptors:\n\tmanufc: %d\n\tprodct: %d\n\tserial: %d\n\tclass: %d\n\tsubclass: %d\n", + desc.iManufacturer, + desc.iProduct, + desc.iSerialNumber, + desc.bDeviceClass, + desc.bDeviceSubClass); - /* - * A char buffer to pull string descriptors in from the device - */ - unsigned char string[256]; - libusb_device_handle* hand; - libusb_open(dev, &hand); + /* + * A char buffer to pull string descriptors in from the device + */ + unsigned char string[256]; + libusb_device_handle* hand; + libusb_open(dev, &hand); - r = libusb_get_string_descriptor_ascii(hand, desc.iManufacturer, - string, 256); - fprintf(stderr, - "Manufacturer: %s\n", string); - libusb_close(hand); + r = libusb_get_string_descriptor_ascii(hand, desc.iManufacturer, + string, 256); + fprintf(stderr, + "Manufacturer: %s\n", string); + libusb_close(hand); #endif - return dev; - - break; - } + return true; + break; } } - return NULL; + return false; } uint8_t get_first_endpoint_address(libusb_device* dev)