Skip to content

Commit

Permalink
Added base functionality
Browse files Browse the repository at this point in the history
 - device.reset() -> libusb_reset_device (fully asynchronous)
 - device.ref() -> libusb_device_ref()
 - device.unref() -> libusb_device_unref()
 - interface.detach() -> libusb_detach_kernel_driver()
 - interface.attach() -> libusb_attach_kernel_driver()
 - interface.claim() -> libusb_claim_interface()
 - interface.release() -> libusb_release_interface() (fully asynchronous)
 - interface.setAlternateSetting() -> libusb_set_alternate_setting (fully asynchronous)
  • Loading branch information
Christopher Klein committed Nov 30, 2010
1 parent bcaef7e commit 24bb073
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 9 deletions.
35 changes: 34 additions & 1 deletion src/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,50 @@
#endif

#define THROW_BAD_ARGS(fail) return ThrowException(Exception::TypeError(V8STR(fail)));
#define THROW_NOT_YET return ThrowException(Exception::TypeError(String::Concat(String::New(__FUNCTION__), String::New("not yet supported"))));
#define THROW_ERROR(fail) return ThrowException(Exception::Error(V8STR(fail))));
#define THROW_NOT_YET return ThrowException(Exception::Error(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 CHECK_USB_HANDLE_OPENED(handle, scope) CHECK_USB(libusb_open(self->device, handle), scope)

#define LOCAL(type, varname, ref) \
HandleScope scope;\
type *varname = OBJUNWRAP<type>(ref);

#define EIO_CAST(type, varname) struct type *varname = reinterpret_cast<struct type *>(req->data);
#define EIO_NEW(type, varname) struct type *varname = (struct type *) calloc(1, sizeof(struct type));
#define EIO_DELEGATION(varname) \
Local<Function> callback; \
if (args.Length() == 1 && args[0]->IsFunction()) { \
callback = Local<Function>::Cast(args[0]); \
} \
if (!varname) { \
V8::LowMemoryNotification(); \
} \
varname->callback = Persistent<Function>::New(callback); \
varname->error = Persistent<Object>::New(Object::New()); \

#define EIO_AFTER(varname) HandleScope scope; \
ev_unref(EV_DEFAULT_UC); \
if (sizeof(varname->callback) > 0) { \
Local<Value> argv[1]; \
argv[0] = Local<Value>::New(scope.Close(varname->error)); \
varname->callback->Call(Context::GetCurrent()->Global(), 1, argv); \
varname->callback.Dispose(); \
}


namespace NodeUsb {
// intermediate EIO structure for device
struct device_request {
Persistent<Function> callback;
Persistent<Object> error;
libusb_device *device;
};

static inline Local<Value> errno_exception(int errorno) {
Local<Value> e = Exception::Error(String::NewSymbol(strerror(errorno)));
Local<Object> obj = e->ToObject();
Expand Down
76 changes: 72 additions & 4 deletions src/device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ namespace NodeUsb {
free(config_descriptor);
DEBUG("Device object destroyed")
}

Handle<Value> Device::New(const Arguments& args) {
HandleScope scope;
DEBUG("New Device object created")
Expand Down Expand Up @@ -95,12 +95,80 @@ namespace NodeUsb {
return scope.Close(Integer::New(address));
}

Handle<Value> Device::Ref(const Arguments& args) {
LOCAL(Device, self, args.This())
libusb_ref_device(self->device);

return Undefined();
}

Handle<Value> Device::Unref(const Arguments& args) {
LOCAL(Device, self, args.This())
libusb_unref_device(self->device);

return Undefined();
}

/**
* libusb_reset_device incurs a noticeable delay, so this is asynchronous
*/
Handle<Value> Device::Reset(const Arguments& args) {
HandleScope scope;
THROW_NOT_YET
return scope.Close(True());
LOCAL(Device, self, args.This())

// allocation of intermediate EIO structure
EIO_NEW(device_request, reset_req)

// create default delegation
EIO_DELEGATION(reset_req)

reset_req->device = self->device;

// Make asynchronous call
eio_custom(EIO_Reset, EIO_PRI_DEFAULT, EIO_After_Reset, reset_req);

// add reference
ev_ref(EV_DEFAULT_UC);

return Undefined();
}

/**
* Contains the blocking libusb_reset_device function
*/
int Device::EIO_Reset(eio_req *req) {
EIO_CAST(device_request, reset_req)

libusb_device_handle *handle;

int errcode = 0;

if ((errcode = libusb_open(reset_req->device, &handle)) >= LIBUSB_SUCCESS) {
if ((errcode = libusb_reset_device(handle)) < LIBUSB_SUCCESS) {
libusb_close(handle);
reset_req->error->Set(V8STR("error_source"), V8STR("reset"));
}
} else {
reset_req->error->Set(V8STR("error_source"), V8STR("open"));
}

reset_req->error->Set(V8STR("error_code"), Uint32::New(errcode));

// needed for EIO so that the EIO_After_Reset method will be called
req->result = 0;

return 0;
}

int Device::EIO_After_Reset(eio_req *req) {
EIO_CAST(device_request, reset_req)
EIO_AFTER(reset_req)

// release intermediate structure
free(reset_req);

return 0;
}


// TODO: Read-Only
#define LIBUSB_CONFIG_DESCRIPTOR_STRUCT_TO_V8(name) \
Expand Down
5 changes: 5 additions & 0 deletions src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ namespace NodeUsb {
// exposed to V8
static Handle<Value> New(const Arguments& args);
static Handle<Value> Close(const Arguments& args);
static Handle<Value> Ref(const Arguments& args);
static Handle<Value> Unref(const Arguments& args);
static Handle<Value> Reset(const Arguments& args);
// Reset -> Async
static int EIO_After_Reset(eio_req *req);
static int EIO_Reset(eio_req *req);
static Handle<Value> GetConfigDescriptor(const Arguments& args);
static Handle<Value> GetDeviceDescriptor(const Arguments& args);
};
Expand Down
158 changes: 154 additions & 4 deletions src/interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ namespace NodeUsb {
instance_template->SetAccessor(V8STR("__isKernelDriverAttached"), Interface::IsKernelDriverActiveGetter);

// methods exposed to node.js
NODE_SET_PROTOTYPE_METHOD(t, "detach", Interface::DetachKernelDriver);
NODE_SET_PROTOTYPE_METHOD(t, "attach", Interface::AttachKernelDriver);
NODE_SET_PROTOTYPE_METHOD(t, "claim", Interface::Claim);
NODE_SET_PROTOTYPE_METHOD(t, "release", Interface::Release);
NODE_SET_PROTOTYPE_METHOD(t, "setAlternateSetting", Interface::AlternateSetting);

// Make it visible in JavaScript
target->Set(String::NewSymbol("Interface"), t->GetFunction());
Expand Down Expand Up @@ -83,13 +88,158 @@ namespace NodeUsb {

Handle<Value> Interface::IsKernelDriverActiveGetter(Local<String> property, const AccessorInfo &info) {
LOCAL(Interface, self, info.Holder())

CHECK_USB_HANDLE_OPENED(&(self->handle), scope)

int isKernelDriverActive = 0;

if ((isKernelDriverActive = libusb_open(self->device, &(self->handle))) >= 0) {
isKernelDriverActive = libusb_kernel_driver_active(self->handle, self->descriptor->bInterfaceNumber);
}
CHECK_USB((isKernelDriverActive = libusb_kernel_driver_active(self->handle, self->descriptor->bInterfaceNumber)), scope)

return scope.Close(Integer::New(isKernelDriverActive));
}

Handle<Value> Interface::DetachKernelDriver(const Arguments& args) {
LOCAL(Interface, self, args.This())
CHECK_USB_HANDLE_OPENED(&(self->handle), scope)
CHECK_USB(libusb_detach_kernel_driver(self->handle, self->descriptor->bInterfaceNumber), scope)

return Undefined();
}

/**
* Attach kernel driver to current interface; delegates to libusb_attach_kernel_driver()
*/
Handle<Value> Interface::AttachKernelDriver(const Arguments& args) {
LOCAL(Interface, self, args.This())
CHECK_USB_HANDLE_OPENED(&(self->handle), scope)
CHECK_USB(libusb_attach_kernel_driver(self->handle, self->descriptor->bInterfaceNumber), scope)

return Undefined();
}

/**
* Claim current interface; delegates to libusb_claim_interface()
*/
Handle<Value> Interface::Claim(const Arguments& args) {
LOCAL(Interface, self, args.This())
CHECK_USB_HANDLE_OPENED(&(self->handle), scope)
CHECK_USB(libusb_claim_interface(self->handle, self->descriptor->bInterfaceNumber), scope)

return Undefined();
}


/**
* Releases current interface; delegates to libusb_release_interface()
* non-blocking!
*/
Handle<Value> Interface::Release(const Arguments& args) {
LOCAL(Interface, self, args.This())

// allocation of intermediate EIO structure
EIO_NEW(release_request, release_req)

// create default delegation
EIO_DELEGATION(release_req)

release_req->handle = self->handle;
release_req->interface_number = self->descriptor->bInterfaceNumber;
eio_custom(EIO_Release, EIO_PRI_DEFAULT, EIO_After_Release, release_req);

// add reference
ev_ref(EV_DEFAULT_UC);

return Undefined();
}

int Interface::EIO_Release(eio_req *req) {
EIO_CAST(release_request, release_req)


int errcode = 0;

if ((errcode = libusb_open(release_req->device, &(release_req->handle))) >= LIBUSB_SUCCESS) {
if ((errcode = libusb_release_interface(release_req->handle, release_req->interface_number)) < LIBUSB_SUCCESS) {
libusb_close(release_req->handle);
release_req->error->Set(V8STR("error_source"), V8STR("release"));
}
} else {
release_req->error->Set(V8STR("error_source"), V8STR("open"));
}

release_req->error->Set(V8STR("error_code"), Uint32::New(errcode));

// needed for EIO so that the EIO_After_Reset method will be called
req->result = 0;

return 0;
}

int Interface::EIO_After_Release(eio_req *req) {
EIO_CAST(release_request, release_req)
EIO_AFTER(release_req)

free(release_req);

return 0;
}


/**
* alternate setting for interface current interface; delegates to libusb_set_interface_alt_setting()
* non-blocking!
*/
Handle<Value> Interface::AlternateSetting(const Arguments& args) {
LOCAL(Interface, self, args.This())

if (args.Length() != 1 || args[0]->IsUint32()) {
THROW_BAD_ARGS("Interface::AlternateSetting expects [uint32:setting] as first parameter")
}

// allocation of intermediate EIO structure
EIO_NEW(alternate_setting_request, alt_req)

// create default delegation
EIO_DELEGATION(alt_req)

alt_req->handle = self->handle;
alt_req->interface_number = self->descriptor->bInterfaceNumber;
alt_req->alternate_setting = args[0]->Uint32Value();
eio_custom(EIO_AlternateSetting, EIO_PRI_DEFAULT, EIO_After_AlternateSetting, alt_req);

// add reference
ev_ref(EV_DEFAULT_UC);

return Undefined();
}

int Interface::EIO_AlternateSetting(eio_req *req) {
EIO_CAST(alternate_setting_request, alt_req)

int errcode = 0;

if ((errcode = libusb_open(alt_req->device, &(alt_req->handle))) >= LIBUSB_SUCCESS) {
if ((errcode = libusb_set_interface_alt_setting(alt_req->handle, alt_req->interface_number, alt_req->alternate_setting)) < LIBUSB_SUCCESS) {
libusb_close(alt_req->handle);
alt_req->error->Set(V8STR("error_source"), V8STR("release"));
}
} else {
alt_req->error->Set(V8STR("error_source"), V8STR("open"));
}

alt_req->error->Set(V8STR("error_code"), Uint32::New(errcode));

req->result = 0;

return 0;
}

int Interface::EIO_After_AlternateSetting(eio_req *req) {
EIO_CAST(alternate_setting_request, alt_req)
EIO_AFTER(alt_req)

free(alt_req);

return 0;
}

}
18 changes: 18 additions & 0 deletions src/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ namespace NodeUsb {
static Handle<Value> IsKernelDriverActiveGetter(Local<String> property, const AccessorInfo &info);
// exposed to V8
static Handle<Value> New(const Arguments& args);
static Handle<Value> DetachKernelDriver(const Arguments& args);
static Handle<Value> AttachKernelDriver(const Arguments& args);
static Handle<Value> Claim(const Arguments& args);

struct release_request:device_request {
libusb_device_handle *handle;
int interface_number;
};
static Handle<Value> Release(const Arguments& args);
static int EIO_Release(eio_req *req);
static int EIO_After_Release(eio_req *req);

struct alternate_setting_request:release_request {
int alternate_setting;
};
static Handle<Value> AlternateSetting(const Arguments& args);
static int EIO_AlternateSetting(eio_req *req);
static int EIO_After_AlternateSetting(eio_req *req);
};
}

Expand Down
17 changes: 17 additions & 0 deletions src/usb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace NodeUsb {

// Bindings to nodejs
NODE_SET_PROTOTYPE_METHOD(t, "refresh", Usb::Refresh);
NODE_SET_PROTOTYPE_METHOD(t, "setDebugLevel", Usb::SetDebugLevel);
NODE_SET_PROTOTYPE_METHOD(t, "getDevices", Usb::GetDeviceList);
NODE_SET_PROTOTYPE_METHOD(t, "close", Usb::Close);

Expand Down Expand Up @@ -158,7 +159,23 @@ namespace NodeUsb {
return scope.Close(True());
}

/**
* Set debug level
*/
Handle<Value> Usb::SetDebugLevel(const Arguments& args) {
LOCAL(Usb, self, args.This())
CHECK_USB(self->Init(), scope);

// need libusb_device structure as first argument
if (args.Length() != 1 || !args[0]->IsUint32() || !(((uint32_t)args[0]->Uint32Value() >= 0) && ((uint32_t)args[0]->Uint32Value() < 4))) {
THROW_BAD_ARGS("Usb::SetDebugLevel argument is invalid. [uint:[0-3]]!")
}

libusb_set_debug(NULL, args[0]->Uint32Value());

return Undefined();
}

/**
* Returns the devices discovered by libusb
* @return array[Device]
Expand Down
1 change: 1 addition & 0 deletions src/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace NodeUsb {

// exposed to V8
static Handle<Value> New(const Arguments& args);
static Handle<Value> SetDebugLevel(const Arguments& args);
static Handle<Value> GetDeviceList(const Arguments& args);
static Handle<Value> Refresh(const Arguments& args);
static Handle<Value> Close(const Arguments& args);
Expand Down
Loading

0 comments on commit 24bb073

Please sign in to comment.