Patrick Debois
Patching Ruby USB on Mac OSX
When my son got himself Buzz Quiz, it struck me that there was no way to create your own quiz.
So that got me started on reading up on how I could use the Buzz Quiz controllers on my Mac.
### Selecting a toolkit
All effort has gone to libusb. But i did not want to into C code again. This links sums up a list of related wrappers or bindings:
I've considered the following options:
- libusbjava: java has javax.usb but nothing that works on Mac
- jusb:
- libusb: sorry but i did not want code C code again
- apple I/O Kit: apple includes a toolkit for USB programming, but would make it more mac specific
- python-usb: not so much for me, as I don't do much pythoin programming
- ruby-usb: hey I like ruby, why not
### Installing libUSB (using macports)
ruby-usb requires you to have libusb . This library has a current version of 1.0.3 , this version version not compatible with ruby-usb. Luckily macports still has the older versions available for us. The version we need is the _libusb-compat_ . Note: that my macports where installed in /opt/local so you might need to change some paths.
Lets list the versions available by macports:
```shell
$ sudo /opt/local/bin/port list |grep -i usb
usbprog @0.1.8 cross/usbprog
libusb @1.0.3 devel/libusb
libusb-compat @0.1.3 devel/libusb-compat
libusb-legacy @0.1.12 devel/libusb-legacy
```
We install the libusb-compat version:
```shell
$ sudo /opt/local/bin/port install libusb-compat
---> Computing dependencies for libusb-compat
---> Fetching libusb
---> Verifying checksum(s) for libusb
---> Extracting libusb
---> Applying patches to libusb
---> Configuring libusb
---> Building libusb
---> Staging libusb into destroot
---> Installing libusb @1.0.3_0
---> Activating libusb @1.0.3_0
---> Cleaning libusb
---> Fetching libusb-compat
---> Attempting to fetch libusb-compat-0.1.3.tar.bz2 from http://mesh.dl.sourceforge.net/libusb
---> Verifying checksum(s) for libusb-compat
---> Extracting libusb-compat
---> Configuring libusb-compat
---> Building libusb-compat
---> Staging libusb-compat into destroot
---> Installing libusb-compat @0.1.3_0
---> Activating libusb-compat @0.1.3_0
---> Cleaning libusb-compat
```
### Getting ruby-usb
The most recent success with lib-usb I found was described on
downloaded the latest cvs snapshot from http://www.a-k-r.org/ruby-usb/ . This will install version 0.2-something. The rubygems is only the version 0.1 version. There were some bug fixes since then.
```shell
$ svn co svn://svn@svn.a-k-r.org/akr/ruby-usb/trunk ruby-usb
$ cd ruby-usb
```
### Solving the library and include paths
replace the extconf.rb as follows where /opt/local is the place where you installed libusb
```ruby
require 'mkmf'
find_header("usb.h", "/opt/local/include")
find_library("usb", nil, "/opt/local/lib")
have_library("usb", "usb_init")
create_makefile('usb')
```
### Specify the correct build architecture
For some reason, the make mkmf decides to put both the ppc and the i386 as flags to the architecture. So we have to specify the i386 using the ARCHFLAGS variable:
```shell
$ ARCHFLAGS="-arch i386" ruby extconf.rb
checking for #include
... yes
checking for main() in -lusb... yes
checking for usb_init() in -lusb... yes
creating Makefile
```
Not specifying the architecture would result in a warning at compile time (file is not of required architecture)
```shell
gcc -I. -I. -I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0 -I. -I/opt/local/include -fno-common -arch ppc -arch i386 -Os -pipe -fno-common -c usb.c
cc -arch ppc -arch i386 -pipe -bundle -undefined dynamic_lookup -o usb.bundle usb.o -L. -L/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib -L/opt/local/lib -L. -arch ppc -arch i386 -lruby -lusb -lpthread -ldl -lm
ld warning: in /opt/local/lib/libusb.dylib, file is not of required architecture
```
At runtime this would result in an error
```yyshell
dyld: lazy symbol binding failed: Symbol not found: _usb_init
Referenced from: /Library/Ruby/Site/1.8/universal-darwin9.0/usb.bundle
Expected in: dynamic lookup
dyld: Symbol not found: _usb_init
Referenced from: /Library/Ruby/Site/1.8/universal-darwin9.0/usb.bundle
Expected in: dynamic lookup
Trace/BPT trap
```
Even though we have the usb_init compiled in the dynamic library
```shell
nm /opt/local/lib/libusb.dylib |grep -i usb_init
U _libusb_init
00001630 T _usb_init
```
The reason we only get an error at runtime is that ruby-usb dynamically load the library. In our case it will load the libusb.dylib which points to version 0.1.4 of the libusb.
```shell
/opt/local/lib/libusb-0.1.4.dylib
/opt/local/lib/libusb-1.0.0.dylib
/opt/local/lib/libusb-1.0.a
/opt/local/lib/libusb-1.0.dylib
/opt/local/lib/libusb-1.0.la
/opt/local/lib/libusb.a
/opt/local/lib/libusb.dylib
/opt/local/lib/libusb.la
$ ls -l /opt/local/lib/libusb.dylib
lrwxr-xr-x 1 root admin 18 Sep 29 09:24 /opt/local/lib/libusb.dylib -> libusb-0.1.4.dylib
```
Also check if old libraries exist in other places
```shell
$ ls -l /usr/lib/libusb.dylib
-rwxr-xr-x 1 root wheel 24084 Sep 29 10:46 /usr/lib/libusb.dylib
$ sudo rm /usr/lib/libusb.dylib
```
### Build the library with make
```shell
$ make
gcc -I. -I. -I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0 -I. -I/opt/local/include -fno-common -arch i386 -Os -pipe -fno-common -c usb.c
cc -arch i386 -pipe -bundle -undefined dynamic_lookup -o usb.bundle usb.o -L. -L/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib -L/opt/local/lib -L. -arch i386 -lruby -lusb -lusb -lpthread -ldl -lm
```
### Patching the usb.rb
The last thing to get it working is to have usb.rb catch the error _Errno::ERANGE_ .
We do this by adding it to the corresponding line in usb.rb
```shell
413c413
< rescue Errno::EPIPE, Errno::EFBIG, Errno::EPERM,Errno::ERANGE
> rescue Errno::EPIPE, Errno::EFBIG, Errno::EPERM
```
If we don't patch it , it would result in the following error:
```shell
[Errno::ERANGE: Result too large - usb_get_string_simple
from /Library/Ruby/Site/1.8/usb.rb:412:in 'usb_get_string_simple'
from /Library/Ruby/Site/1.8/usb.rb:412:in 'get_string_simple'
from /Library/Ruby/Site/1.8/usb.rb:349:in 'description'
from /Library/Ruby/Site/1.8/usb.rb:276:in 'open'
from /Library/Ruby/Site/1.8/usb.rb:349:in 'description'
from /Library/Ruby/Site/1.8/usb.rb:341:in 'inspect'
```
### Finally installing ruby-usb
```shell
$ sudo make install
mkdir -p /Library/Ruby/Site/1.8/universal-darwin9.0
/usr/bin/install -c -m 0755 usb.bundle /Library/Ruby/Site/1.8/universal-darwin9.0
/usr/bin/install -c -m 644 ./lib/usb.rb /Library/Ruby/Site/1.8
```
### Example program
```ruby
require 'usb'
require 'pp'
USB.devices.each do |dev|
pp dev
end
```
```shell
#
#
#
#
#
#
#
#
```
### More on Ruby USB
- Video explaining the use of ruby-usb :
- Powerpoint on ruby-usb
### Other projects using ruby-usb
BetaBrite
* Betabrite integration with ruby-usb :
* RubyUSB - Buffaloblog:
*
IBuddy:
*
*
USB Missile Launcher:
* How To Control USB Missile Launcher on Linux :
* AC Power Control through USB :