Browse files

inital import

  • Loading branch information...
0 parents commit d4b8368396293de2ab52472ad32117c7093e82d7 Christopher Klein committed Nov 11, 2010
Showing with 728 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +9 −0 .lock-wscript
  3. +2 −0 Makefile
  4. +27 −0 README
  5. +9 −0 src/.lock-wscript
  6. +464 −0 src/bindings.cc
  7. +64 −0 src/bindings.h
  8. +26 −0 src/node_usb.cc
  9. +29 −0 src/node_usb.h
  10. +44 −0 tests/node-usb-test.js
  11. +10 −0 usb.js
  12. +1 −0 usb_bindings.node
  13. +40 −0 wscript
3 .gitignore
@@ -0,0 +1,3 @@
+build/*
+usb_bindings.node
+**/.lock*
9 .lock-wscript
@@ -0,0 +1,9 @@
+argv = ['/usr/local/bin/node-waf', 'configure', 'clean', 'build']
+blddir = '/home/ckl/dev/node-usb/build'
+commands = {'dist': 0, 'configure': True, 'distcheck': 0, 'install': 0, 'build': True, 'clean': True, 'distclean': 0, 'check': 0, 'uninstall': 0}
+cwd = '/home/ckl/dev/node-usb'
+environ = {'USERNAME': 'root', 'SUDO_UID': '1000', 'TERM': 'xterm', 'LOGNAME': 'root', 'USER': 'root', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin', 'HOME': '/home/ckl', 'DISPLAY': ':0.0', 'MAKEFLAGS': '', 'LANG': 'de_DE.utf8', 'SUDO_COMMAND': '/usr/bin/make', 'SUDO_GID': '1000', 'SHELL': '/bin/bash', 'XAUTHORITY': '/var/run/gdm/auth-for-ckl-CNyXyS/database', 'MAKELEVEL': '1', 'PWD': '/home/ckl/dev/node-usb', 'COLORTERM': 'gnome-terminal', 'SUDO_USER': 'ckl', 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:hl=44;37:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:', 'MFLAGS': ''}
+files = []
+hash = 0
+options = {'compile_targets': None, 'force': False, 'verbose': 0, 'nocache': False, 'progress_bar': 0, 'destdir': '', 'keep': False, 'zones': '', 'blddir': '', 'prefix': '/usr/local/', 'jobs': 1, 'srcdir': '', 'check_cxx_compiler': 'g++ icpc sunc++', 'check_c_compiler': 'gcc icc suncc'}
+srcdir = '/home/ckl/dev/node-usb'
2 Makefile
@@ -0,0 +1,2 @@
+make:
+ node-waf configure clean build; node tests/node-usb-test.js
27 README
@@ -0,0 +1,27 @@
+libusb-1.0 bindings for Node.js
+==============================
+node-usb is just a sample of how to interact between node.js -> Google V8 -> external libraries (in this case libusb-1.0).
+At the moment only discovering of availble devices is possible and no asynchronous support is integrated.
+
+Tested with Node version 0.2.4
+
+Installation
+==============================
+Make sure you have installed libusb-1.0-0-dev (Ubuntu: sudo apt-get install libusb-1.0-0-dev).
+Just run
+ make
+in current directory and wait. "Unit tests" can be executed with
+ node tests/node-usb-test.js
+
+
+TODO
+=============================
+* Move to asynchronous calls
+* read/write streams
+* ...
+
+More information
+==============================
+Christopher Klein <ckl[at]ecw[dot]de>
+http://twitter.com/schakko
+http://wap.ecw.de
9 src/.lock-wscript
@@ -0,0 +1,9 @@
+argv = ['/usr/local/bin/node-waf', 'configure', 'build']
+blddir = '/home/ckl/dev/node-usb/usb_native/build'
+commands = {'dist': 0, 'configure': True, 'distcheck': 0, 'install': 0, 'build': True, 'clean': 0, 'distclean': 0, 'check': 0, 'uninstall': 0}
+cwd = '/home/ckl/dev/node-usb/usb_native'
+environ = {'USERNAME': 'ckl', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'LOGNAME': 'ckl', 'USER': 'ckl', 'GNOME_KEYRING_CONTROL': '/tmp/keyring-eiCxiY', 'HOME': '/home/ckl', 'DISPLAY': ':0.0', 'MAKEFLAGS': '', 'SSH_AGENT_PID': '1169', 'LANG': 'de_DE.utf8', 'TERM': 'xterm', 'SHELL': '/bin/bash', 'XDG_SESSION_COOKIE': '5f62497e8f18d4f16405ea254cd7dfc5-1289218259.318980-1657822208', 'SESSION_MANAGER': 'local/ckl-node-js:@/tmp/.ICE-unix/1106,unix/ckl-node-js:/tmp/.ICE-unix/1106', 'SHLVL': '1', 'MANDATORY_PATH': '/usr/share/gconf/gnome.mandatory.path', 'WINDOWID': '62962412', 'ORBIT_SOCKETDIR': '/tmp/orbit-ckl', 'MFLAGS': '', 'GDM_KEYBOARD_LAYOUT': 'de', 'SSH_AUTH_SOCK': '/tmp/keyring-eiCxiY/ssh', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games', 'DEFAULTS_PATH': '/usr/share/gconf/gnome.default.path', 'XDG_DATA_DIRS': '/usr/share/gnome:/usr/local/share/:/usr/share/', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-gnome:/etc/xdg', 'DBUS_SESSION_BUS_ADDRESS': 'unix:abstract=/tmp/dbus-XmFi6QccuS,guid=78afb0f957543d1100a6a9f34cd7e8d6', '_': '/usr/bin/make', 'XAUTHORITY': '/var/run/gdm/auth-for-ckl-82xf9O/database', 'GDMSESSION': 'gnome', 'DESKTOP_SESSION': 'gnome', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'GNOME_KEYRING_PID': '1088', 'OLDPWD': '/home/ckl/dev/node-usb', 'GDM_LANG': 'de_DE.utf8', 'GTK_MODULES': 'canberra-gtk-module', 'SPEECHD_PORT': '7560', 'MAKELEVEL': '1', 'PWD': '/home/ckl/dev/node-usb/usb_native', 'COLORTERM': 'gnome-terminal', 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:hl=44;37:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:'}
+files = []
+hash = 0
+options = {'compile_targets': None, 'force': False, 'verbose': 0, 'nocache': False, 'progress_bar': 0, 'destdir': '', 'keep': False, 'zones': '', 'blddir': '', 'prefix': '/usr/local/', 'jobs': 1, 'srcdir': '', 'check_cxx_compiler': 'g++ icpc sunc++'}
+srcdir = '/home/ckl/dev/node-usb/usb_native'
464 src/bindings.cc
@@ -0,0 +1,464 @@
+#include "./bindings.h"
+
+#define THROW_BAD_ARGS ThrowException(Exception::TypeError(String::New("Bad argument"))))
+#define THROW_NOT_YET return ThrowException(Exception::TypeError(String::Concat(String::New(__FUNCTION__), String::New("not yet supported"))));
+#define CHECK_USB(r, scope) \
+ if (r < LIBUSB_SUCCESS) { \
+ return scope.Close(ThrowException(errno_exception(r)));\
+ }
+
+#define LOCAL(type, varname, ref) \
+ HandleScope scope;\
+ type *varname = OBJUNWRAP<type>(ref);
+
+/**
+ * Remarks
+ * * variable name "self" always refers to the unwraped object instance of static method class
+ * * there is no node-usb support for libusbs context. Want to keep it simple.
+ */
+
+namespace NodeUsb {
+/******************************* Helper functions */
+ static inline Local<Value> errno_exception(int errorno) {
+ Local<Value> e = Exception::Error(String::NewSymbol(strerror(errorno)));
+ Local<Object> obj = e->ToObject();
+ std::string err = "";
+
+ obj->Set(NODE_PSYMBOL("errno"), Integer::New(errorno));
+ // taken from pyusb
+ switch (errorno) {
+ case LIBUSB_ERROR_IO:
+ err = "Input/output error";
+ break;
+ case LIBUSB_ERROR_INVALID_PARAM:
+ err = "Invalid parameter";
+ break;
+ case LIBUSB_ERROR_ACCESS:
+ err = "Access denied (insufficient permissions)";
+ break;
+ case LIBUSB_ERROR_NO_DEVICE:
+ err = "No such device (it may have been disconnected)";
+ break;
+ case LIBUSB_ERROR_NOT_FOUND:
+ err = "Entity not found";
+ break;
+ case LIBUSB_ERROR_BUSY:
+ err = "Resource busy";
+ break;
+ case LIBUSB_ERROR_TIMEOUT:
+ err = "Operation timed out";
+ break;
+ case LIBUSB_ERROR_OVERFLOW:
+ err = "Overflow";
+ break;
+ case LIBUSB_ERROR_PIPE:
+ err = "Pipe error";
+ break;
+ case LIBUSB_ERROR_INTERRUPTED:
+ err = "System call interrupted (perhaps due to signal)";
+ break;
+ case LIBUSB_ERROR_NO_MEM:
+ err = "Insufficient memory";
+ break;
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ err = "Operation not supported or unimplemented on this platform";
+ break;
+ default:
+ err = "Unknown error";
+ break;
+ }
+ // convert err to const char* with help of c_str()
+ obj->Set(NODE_PSYMBOL("msg"), String::New(err.c_str()));
+ return e;
+ }
+
+/******************************* USB */
+ /**
+ * @param usb.isLibusbInitalized: boolean
+ */
+ void Usb::InitalizeUsb(Handle<Object> target) {
+ DEBUG("Entering")
+ HandleScope scope;
+
+ Local<FunctionTemplate> t = FunctionTemplate::New(Usb::New);
+
+ // Constructor
+ t->Inherit(EventEmitter::constructor_template);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ t->SetClassName(String::NewSymbol("Usb"));
+
+ Local<ObjectTemplate> instance_template = t->InstanceTemplate();
+
+ // Constants must be passed to ObjectTemplate and *not* to FunctionTemplate
+ // libusb_class_node
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_PER_INTERFACE);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_AUDIO);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_COMM);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_HID);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_PRINTER);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_PTP);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_MASS_STORAGE);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_HUB);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_DATA);
+ // both does not exist?
+ // NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_WIRELESS);
+ // NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_APPLICATION);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_CLASS_VENDOR_SPEC);
+ // libusb_descriptor_type
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_DEVICE);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_CONFIG);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_STRING);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_INTERFACE);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_ENDPOINT);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_HID);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_REPORT);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_PHYSICAL);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_DT_HUB);
+ // libusb_endpoint_direction
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ENDPOINT_IN);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ENDPOINT_OUT);
+ // libusb_transfer_type
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_TRANSFER_TYPE_CONTROL);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_TRANSFER_TYPE_ISOCHRONOUS);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_TRANSFER_TYPE_BULK);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_TRANSFER_TYPE_INTERRUPT);
+ // libusb_iso_sync_type
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ISO_SYNC_TYPE_NONE);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ISO_SYNC_TYPE_ASYNC);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ISO_SYNC_TYPE_ADAPTIVE);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ISO_SYNC_TYPE_SYNC);
+ // libusb_iso_usage_type
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ISO_USAGE_TYPE_DATA);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ISO_USAGE_TYPE_FEEDBACK);
+ NODE_DEFINE_CONSTANT(instance_template, LIBUSB_ISO_USAGE_TYPE_IMPLICIT);
+
+ // Properties
+ // TODO: should get_device_list be a property?
+ instance_template->SetAccessor(V8STR("isLibusbInitalized"), Usb::IsLibusbInitalizedGetter);
+
+ // Bindings to nodejs
+ NODE_SET_PROTOTYPE_METHOD(t, "refresh", Usb::Refresh);
+ NODE_SET_PROTOTYPE_METHOD(t, "getDevices", Usb::GetDeviceList);
+ NODE_SET_PROTOTYPE_METHOD(t, "close", Usb::Close);
+
+ // Export Usb class to V8/node.js environment
+ target->Set(String::NewSymbol("Usb"), t->GetFunction());
+ DEBUG("Leave")
+ }
+
+ Usb::Usb() : EventEmitter() {
+ is_initalized = false;
+ devices = NULL;
+ }
+
+ Usb::~Usb() {
+ if (devices != NULL) {
+ libusb_free_device_list(devices, 1);
+ }
+
+ libusb_exit(NULL);
+ DEBUG("NodeUsb::Usb object destroyed")
+ // TODO Freeing opened USB devices?
+ }
+
+ /**
+ * Methods not exposed to v8 - used only internal
+ */
+ int Usb::InitalizeLibusb() {
+ if (is_initalized) {
+ return LIBUSB_SUCCESS;
+ }
+
+ int r = libusb_init(NULL);
+
+ if (0 == r) {
+ is_initalized = true;
+ }
+
+ return r;
+ }
+
+ /**
+ * Returns libusb initalization status
+ * @return boolean
+ */
+ Handle<Value> Usb::IsLibusbInitalizedGetter(Local<String> property, const AccessorInfo &info) {
+ HandleScope scope;
+
+ Usb *self = OBJUNWRAP<Usb>(info.Holder());
+
+ if (self->is_initalized == true) {
+ return scope.Close(True());
+ }
+
+ return scope.Close(False());
+ }
+
+
+ /**
+ * Creates a new Usb object
+ */
+ Handle<Value> Usb::New(const Arguments& args) {
+ HandleScope scope;
+ // create a new object
+ Usb *self = new Usb();
+ // wrap self object to arguments
+ self->Wrap(args.This());
+
+ return args.This();
+ }
+
+ /**
+ * Refresh libusb environment
+ */
+ Handle<Value> Usb::Refresh(const Arguments& args) {
+ HandleScope scope;
+ Usb *self = OBJUNWRAP<Usb>(args.This());
+
+ CHECK_USB(self->InitalizeLibusb(), scope);
+ return scope.Close(True());
+ }
+
+ /**
+ * Returns the devices discovered by libusb
+ * @return array[Device]
+ */
+ Handle<Value> Usb::GetDeviceList(const Arguments& args) {
+ HandleScope scope;
+ Usb *self = OBJUNWRAP<Usb>(args.This());
+
+ CHECK_USB(self->InitalizeLibusb(), scope);
+
+ // dynamic array (sic!) which contains the devices discovered later
+ Local<Array> discoveredDevices = Array::New();
+
+ // TODO Google codeguide for ssize_t?
+ ssize_t cntDevices = 0;
+ ssize_t i = 0;
+
+ // if no devices were covered => get device list
+ if (self->devices == NULL) {
+ cntDevices = libusb_get_device_list(NULL, &(self->devices));
+ CHECK_USB(cntDevices, scope);
+ }
+
+ // js_device contains the Device instance
+ Local<Object> js_device;
+
+ for (; i < cntDevices; i++) {
+ // wrap libusb_device structure into a Local<Value>
+ Local<Value> arg = External::New(self->devices[i]);
+
+ // create new object instance of class NodeUsb::Device
+ Persistent<Object> js_device(Device::constructor_template->GetFunction()->NewInstance(1, &arg));
+
+ // push to array
+ discoveredDevices->Set(Integer::New(i), js_device);
+ }
+
+ return scope.Close(discoveredDevices);
+ }
+
+ /**
+ * Close current libusb context
+ * @return boolean
+ */
+ Handle<Value> Usb::Close(const Arguments& args) {
+ HandleScope scope;
+ Usb *self = OBJUNWRAP<Usb>(args.This());
+
+ if (false == self->is_initalized) {
+ return scope.Close(False());
+ }
+
+ delete self;
+
+ return scope.Close(True());
+ }
+
+/******************************* Device */
+ /** constructor template is needed for creating new Device objects from outside */
+ Persistent<FunctionTemplate> Device::constructor_template;
+
+ /**
+ * @param device.busNumber integer
+ * @param device.deviceAddress integer
+ */
+ void Device::InitalizeDevice(Handle<Object> target) {
+ DEBUG("Entering...")
+ HandleScope scope;
+ Local<FunctionTemplate> t = FunctionTemplate::New(Device::New);
+
+ // Constructor
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ t->SetClassName(String::NewSymbol("Device"));
+ Device::constructor_template = Persistent<FunctionTemplate>::New(t);
+
+ Local<ObjectTemplate> instance_template = t->InstanceTemplate();
+
+ // Constants
+ // no constants at the moment
+
+ // Properties
+ instance_template->SetAccessor(V8STR("busNumber"), Device::BusNumberGetter);
+ instance_template->SetAccessor(V8STR("deviceAddress"), Device::DeviceAddressGetter);
+
+ // Bindings to nodejs
+ NODE_SET_PROTOTYPE_METHOD(t, "open", Device::Open); // TODO Stream!
+ NODE_SET_PROTOTYPE_METHOD(t, "close", Device::Close); // TODO Stream!
+ NODE_SET_PROTOTYPE_METHOD(t, "reset", Device::Reset);
+ NODE_SET_PROTOTYPE_METHOD(t, "getDeviceDescriptor", Device::GetDeviceDescriptor);
+ NODE_SET_PROTOTYPE_METHOD(t, "getConfigDescriptor", Device::GetConfigDescriptor);
+
+ // Make it visible in JavaScript
+ target->Set(String::NewSymbol("Device"), t->GetFunction());
+ DEBUG("Leave")
+ }
+
+ Device::Device(libusb_device* _device) {
+ DEBUG("Assigning libusb_device structure to self")
+ device = _device;
+ config_descriptor = NULL;
+ is_opened = false;
+ }
+
+ Device::~Device() {
+ // TODO Closing opened streams/device handles
+ if (handle != NULL) {
+ libusb_close(handle);
+ }
+ DEBUG("Device object destroyed")
+ }
+
+ Handle<Value> Device::New(const Arguments& args) {
+ HandleScope scope;
+ DEBUG("New Device object created")
+
+ // need libusb_device structure as first argument
+ if (args.Length() <= 0 || !args[0]->IsExternal()) {
+ return ThrowException(Exception::TypeError(String::New("Device::New argument is invalid. Must be external!")));
+ }
+
+ // make local value reference to first parameter
+ Local<External> refDevice = Local<External>::Cast(args[0]);
+
+ // cast local reference to local libusb_device structure
+ libusb_device *libusbDevice = static_cast<libusb_device*>(refDevice->Value());
+ // create new Device object
+ Device *device = new Device(libusbDevice);
+
+ // wrap created Device object to v8
+ device->Wrap(args.This());
+
+ return args.This();
+ }
+
+ /**
+ * @return integer
+ */
+ Handle<Value> Device::BusNumberGetter(Local<String> property, const AccessorInfo &info) {
+ HandleScope scope;
+
+ Device *self = OBJUNWRAP<Device>(info.Holder());
+ uint8_t bus_number = libusb_get_bus_number(self->device);
+
+ return scope.Close(Integer::New(bus_number));
+ }
+
+ /**
+ * @return integer
+ */
+ Handle<Value> Device::DeviceAddressGetter(Local<String> property, const AccessorInfo &info) {
+ HandleScope scope;
+
+ Device *self = OBJUNWRAP<Device>(info.Holder());
+ uint8_t address = libusb_get_device_address(self->device);
+
+ return scope.Close(Integer::New(address));
+ }
+
+ Handle<Value> Device::Open(const Arguments& args) {
+ LOCAL(Device, self, args.This())
+ CHECK_USB(libusb_open(self->device, &(self->handle)), scope);
+
+ self->is_opened = true;
+ return scope.Close(True());
+ }
+
+ Handle<Value> Device::Close(const Arguments& args) {
+ LOCAL(Device, self, args.This())
+
+ if (true == self->is_opened) {
+ // libusb does not return any value so no CHECK_USB is needed
+ libusb_close(self->handle);
+ return scope.Close(True());
+ }
+
+ return scope.Close(False());
+ }
+
+ Handle<Value> Device::Reset(const Arguments& args) {
+ HandleScope scope;
+ THROW_NOT_YET
+ return scope.Close(True());
+ }
+
+
+// TODO: Read-Only
+#define LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(name) \
+ r->Set(V8STR(#name), Integer::New((*self->config_descriptor).name));
+
+ Handle<Value> Device::GetConfigDescriptor(const Arguments& args) {
+ LOCAL(Device, self, args.This())
+ CHECK_USB(libusb_get_active_config_descriptor(self->device, &(self->config_descriptor)), scope)
+ Local<Object> r = Object::New();
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(bLength)
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(bDescriptorType)
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(wTotalLength)
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(bNumInterfaces)
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(bConfigurationValue)
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(iConfiguration)
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(bmAttributes)
+ LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(extra_length)
+
+ // make new byte array. not very elegant but works. anyhow.
+ Local<Array> extra = Array::New();
+ for (int i = 0; i < (*self->config_descriptor).extra_length; i++) {
+ extra->Set(i, Integer::New((*self->config_descriptor).extra[i]));
+ }
+
+ r->Set(V8STR("extra"), extra);
+
+ // free it
+ libusb_free_config_descriptor(self->config_descriptor);
+
+ return scope.Close(r);
+ }
+
+// TODO: Read-Only
+#define LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(name) \
+ r->Set(V8STR(#name), Integer::New(self->device_descriptor.name));
+
+ Handle<Value> Device::GetDeviceDescriptor(const Arguments& args) {
+ LOCAL(Device, self, args.This())
+ CHECK_USB(libusb_get_device_descriptor(self->device, &(self->device_descriptor)), scope)
+ Local<Object> r = Object::New();
+
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bLength)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bDescriptorType)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bcdUSB)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bDeviceClass)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bDeviceSubClass)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bDeviceProtocol)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bMaxPacketSize0)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(idVendor)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(idProduct)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bcdDevice)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(iManufacturer)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(iProduct)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(iSerialNumber)
+ LIBUSB_DEVICE_DESCRIPTOR_STRUCT_TO_V8(bNumConfigurations)
+
+ return scope.Close(r);
+ }
+}
64 src/bindings.h
@@ -0,0 +1,64 @@
+#ifndef SRC_BINDINGS_H_
+#define SRC_BINDINGS_H_
+
+#include "node_usb.h"
+
+// Taken from node-libmysqlclient
+#define OBJUNWRAP ObjectWrap::Unwrap
+#define V8STR(str) String::New(str)
+#define DEBUG(str) fprintf(stderr, "node-usb [%s:%s() %d]: %s", __FILE__, __FUNCTION__, __LINE__, str); fprintf(stderr, "\n");
+
+namespace NodeUsb {
+ class Usb : public EventEmitter {
+ public:
+ /** called from outside to initalize V8 class template */
+ static void InitalizeUsb(Handle<Object> target);
+ Usb();
+ ~Usb();
+
+ protected:
+ /** members */
+ bool is_initalized;
+ libusb_device **devices;
+ /** methods */
+ int InitalizeLibusb();
+ /** exposed to V8 */
+ static Handle<Value> New(const Arguments& args);
+ /** V8 getter */
+ static Handle<Value> IsLibusbInitalizedGetter(Local<String> property, const AccessorInfo &info);
+ /** V8 functions */
+ static Handle<Value> GetDeviceList(const Arguments& args);
+ static Handle<Value> Refresh(const Arguments& args);
+ static Handle<Value> Close(const Arguments& args);
+ };
+
+ class Device : public EventEmitter {
+ public:
+ /** called from outside to initalize V8 class template */
+ static void InitalizeDevice(Handle<Object> target);
+ static Persistent<FunctionTemplate> constructor_template;
+ Device(libusb_device*);
+ ~Device();
+
+ protected:
+ /** members */
+ bool is_opened;
+ struct libusb_device *device;
+ struct libusb_device_handle *handle;
+ struct libusb_device_descriptor device_descriptor;
+ struct libusb_config_descriptor *config_descriptor;
+
+ /** exposed to V8 */
+ static Handle<Value> New(const Arguments& args);
+ /** V8 getter */
+ static Handle<Value> BusNumberGetter(Local<String> property, const AccessorInfo &info);
+ static Handle<Value> DeviceAddressGetter(Local<String> property, const AccessorInfo &info);
+ /** V8 functions */
+ static Handle<Value> Open(const Arguments& args);
+ static Handle<Value> Close(const Arguments& args);
+ static Handle<Value> Reset(const Arguments& args);
+ static Handle<Value> GetConfigDescriptor(const Arguments& args);
+ static Handle<Value> GetDeviceDescriptor(const Arguments& args);
+ };
+}
+#endif
26 src/node_usb.cc
@@ -0,0 +1,26 @@
+#include "./bindings.h"
+
+namespace NodeUsb {
+
+ void InitalizeAll(Handle<Object> target) {
+ DEBUG("Entering")
+ HandleScope scope;
+ Usb::InitalizeUsb(target);
+ Device::InitalizeDevice(target);
+
+ target->Set(String::NewSymbol("version"),
+ String::New(NODE_USB_VERSION));
+
+ Handle<ObjectTemplate> global = ObjectTemplate::New();
+ Handle<Context> context = Context::New(NULL, global);
+ Context::Scope context_scope(context);
+ context->Global()->Set(String::NewSymbol("Usb"), target);
+ }
+
+ extern "C" void init(Handle<Object> target) {
+ DEBUG("Initalizing NodeUsb")
+ HandleScope scope;
+ InitalizeAll(target);
+ }
+}
+
29 src/node_usb.h
@@ -0,0 +1,29 @@
+#ifndef SRC_NODE_USB_H_
+#define SRC_NODE_USB_H
+
+#include <libusb.h>
+#include <v8.h>
+#include <string.h>
+#include <errno.h>
+#include <iostream>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <node.h>
+#include <node_buffer.h>
+#include <node_events.h>
+// include std::string
+#include <cstring>
+#include <string>
+#include <cstdlib>
+
+#define NODE_USB_VERSION "0.1"
+
+using namespace v8;
+using namespace node;
+
+namespace NodeUsb {
+}
+
+#endif
44 tests/node-usb-test.js
@@ -0,0 +1,44 @@
+var assert = require('assert');
+var usb_driver = require("../usb.js");
+
+var instance = usb_driver.create()
+console.log(instance);
+console.log(instance.LIBUSB_CLASS_PER_INTERFACE);
+
+assert.notEqual(instance, undefined, "instance must be undefined");
+
+assert.equal(instance.isLibusbInitalized, false, "isLibusbInitalized must be false");
+assert.equal(instance.close(), false, "close() must be false because driver is not opened");
+
+assert.ok(instance.refresh(), "refresh() must be true");
+assert.ok(instance.isLibusbInitalized, "isLibusbInitalized must be true after refresh()");
+
+var devices = instance.getDevices();
+assert.notEqual(devices, undefined, "getDevices() must not be undefined");
+assert.ok((devices.length > 0), "getDevices() must be larger than 0 (assume that at least one host controller is available)");
+
+for (var i = 0; i < devices.length; i++) {
+ var device = devices[i];
+ console.log(device);
+ assert.ok((device.busNumber > 0), "busNumber must be larger than 0");
+ assert.ok((device.deviceAddress > 0), "deviceAddress must be larger than 0");
+ var id = device.busNumber + ":" + device.deviceAddress;
+ console.log("working on " + id);
+ assert.equal(device.close(), false, "close() must be false because device is not opened");
+
+ try {
+ device.open();
+ assert.ok(device.close());
+ }
+ catch (e) {
+ console.log("failed to open device [" + id + "]: " + e.message);
+ }
+
+ console.log(device.getDeviceDescriptor());
+ console.log(device.getConfigDescriptor());
+}
+
+assert.ok(instance.close());
+
+console.log("Tests were successful :-)");
+
10 usb.js
@@ -0,0 +1,10 @@
+/**
+ * Expose complete node-usb binding to node.js
+ */
+var binding = require("./usb_bindings");
+
+exports.create = function() {
+ var usbInstance = new binding.Usb();
+
+ return usbInstance;
+}
1 usb_bindings.node
40 wscript
@@ -0,0 +1,40 @@
+import Options, Utils
+from os import unlink, symlink, chdir
+from os.path import exists
+
+srcdir = '.'
+blddir = 'build'
+VERSION = '0.1'
+
+def set_options(opt):
+ opt.tool_options("compiler_cxx")
+ opt.tool_options("compiler_cc")
+
+def configure(conf):
+ conf.check_tool('compiler_cxx')
+ conf.check_tool("compiler_cc")
+ conf.check_tool('node_addon')
+
+ conf.check_cfg(package='libusb-1.0', mandatory=1, args='--cflags --libs')
+ conf.env.append_unique('CPPFLAGS', ["-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE"])
+ conf.env.append_unique('CXXFLAGS', ["-Wall"])
+
+ # libusb lbiraries
+ conf.check(lib='usb-1.0', libpath=['/lib', '/usr/local/lib/', '/usr/lib']);
+
+def build(bld):
+ obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
+ obj.target = 'usb_bindings'
+ obj.source = './src/node_usb.cc ./src/bindings.cc'
+ # TODO include path hard linked; should be build option
+ obj.includes = '/usr/include /usr/include/libusb-1.0'
+ obj.uselib = ["USB-1.0"]
+ obj.lib = ["usb-1.0"]
+ obj.name = "node-usb"
+ obj.linklags = ['/usr/lib/libusb-1.0a']
+
+def shutdown():
+ t = 'usb_bindings.node';
+
+ if exists('build/default/' + t) and not exists(t):
+ symlink('build/default/' + t, t)

0 comments on commit d4b8368

Please sign in to comment.