Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

optimize osx serialport.list #108

Merged
merged 4 commits into from

3 participants

@adrai
Collaborator

I'm not a chief in mac os x but this code seams to work for more devices...
Can you verify?

@adrai
Collaborator

I'm not so happy with this pull request, next week I will try a native approach...

@adrai
Collaborator

I'm not very good in c... can someone verify my code?

@voodootikigod

Need a bit of time to merge properly. I gave you collaborator rights to assist ongoing as things look good thus far, just got behind on PRs and need to delicately merge this in.

@adrai adrai referenced this pull request from a commit
@adrai adrai solve merge conflicts of pull request #108 07ca55b
@adrai adrai merged commit b1f2cf5 into from
@timnew

I found the native solution yields different result comparing to the js version:

Here is native result:

{ comName: '/dev/cu.Bluetooth-PDA-Sync',
  manufacturer: '',
  pnpId: '',
  locationId: '',
  vendorId: '',
  productId: '' }

{ comName: '/dev/cu.Bluetooth-Modem',
  manufacturer: '',
  pnpId: '',
  locationId: '',
  vendorId: '',
  productId: '' }

{ comName: '/dev/cu.usbmodemfd111',
  manufacturer: '',
  pnpId: '',
  locationId: '0xfd110000',
  vendorId: '0x2341',
  productId: '0x0042' }

And here is js version output:

{ productId: '0x850a',
  vendorId: '0x05ac  (Apple Inc.)',
  serialNumber: 'CCGBAB02ENDL8LFX',
  manufacturer: 'Apple Inc.',
  locationId: '0xfa200000 / 3',
  comName: '/dev/cu.usbmodemfa201' }

{ productId: '0x0042',
  vendorId: '0x2341',
  locationId: '0xfd110000 / 3',
  manufacturer: 'Arduino (www.arduino.cc)',
  serialNumber: '752303138333511062F1',
  comName: '/dev/cu.usbmodemfd111' }
@timnew

And native version doesn't populate the manufacturer correctly, which I used to identify the device.

@adrai
Collaborator

ok, manufacturer and serialNumber are added now...

@timnew

Awesome, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 8, 2012
  1. optimize osx serialport.list

    Adriano Raiano authored
Commits on Nov 12, 2012
  1. native list approach for mac

    Adriano Raiano authored
Commits on Mar 13, 2013
  1. @adrai

    release ressources

    adrai authored
  2. @adrai

    lock getUsbDevice for mac os

    adrai authored
This page is out of date. Refresh to see the latest.
View
84 serialport.js
@@ -152,7 +152,7 @@ SerialPort.prototype.write = function (buffer, callback) {
SerialPort.prototype.close = function (callback) {
var self = this;
-
+
var fd = this.fd;
this.fd = 0;
@@ -198,6 +198,7 @@ function listUnix (callback) {
}
return console.log(err);
}
+
var dirName = "/dev/serial/by-id";
async.map(files, function (file, callback) {
var fileName = path.join(dirName, file);
@@ -211,81 +212,8 @@ function listUnix (callback) {
manufacturer: undefined,
pnpId: file
});
- }, callback);
- // Suspect code per ticket: #104 removed for deeper inspection.
- // fs.readdir("/dev/serial/by-path", function(err_path, paths) {
- // if (err_path) {
- // if (err.errno === 34) return callback(null, []);
- // return console.log(err);
- // }
-
- // var dirName, items;
- // //check if multiple devices of the same id are connected
- // if (files.length !== paths.length) {
- // dirName = "/dev/serial/by-path";
- // items = paths;
- // } else {
- // dirName = "/dev/serial/by-id";
- // items = files;
- // }
-
- // async.map(items, function (file, callback) {
- // var fileName = path.join(dirName, file);
- // fs.readlink(fileName, function (err, link) {
- // if (err) {
- // return callback(err);
- // }
- // link = path.resolve(dirName, link);
- // callback(null, {
- // comName: link,
- // manufacturer: undefined,
- // pnpId: file
- // });
- // });
- // }, callback);
- });
- });
-}
-
-function listOSX (callback) {
- child_process.exec('/usr/sbin/system_profiler SPUSBDataType', function (err, stdout, stderr) {
- if (err) {
- return callback(err);
- }
-
- stderr = stderr.trim();
- if (stderr.length > 0) {
- return callback(new Error(stderr));
- }
-
- var lines = stdout.split('\n');
-
- var items = [];
- var currentItem = {};
- lines.forEach(function (line) {
- line = line.trim();
- line = line.replace(/\s+/, ' ');
- var m;
-
- if (m = line.match(/^Serial Number: (.+)$/)) {
- currentItem['serialNumber'] = m[1];
- } else if (m = line.match(/^Location ID: (.+)$/)) {
- currentItem['locationId'] = m[1];
- } else if (m = line.match(/^Product ID: (.+)$/)) {
- currentItem['productId'] = m[1];
- } else if (m = line.match(/^Vendor ID: (.+)$/)) {
- currentItem['vendorId'] = m[1];
- } else if (m = line.match(/^Manufacturer: (.+)$/)) {
- currentItem['manufacturer'] = m[1];
- } else if (/^$/.test(line)) {
- if ('serialNumber' in currentItem) {
- currentItem['comName'] = "/dev/cu.usbmodem" + currentItem['locationId'].substring(2, 6) + '1';
- items.push(currentItem);
- currentItem = {};
- }
- }
- });
- callback(null, items);
+ });
+ }, callback);
});
}
@@ -293,7 +221,7 @@ exports.list = function (callback) {
if (process.platform === 'win32') {
SerialPortBinding.list(callback);
} else if (process.platform === 'darwin') {
- listOSX(callback);
+ SerialPortBinding.list(callback);
} else {
listUnix(callback);
}
@@ -322,4 +250,4 @@ SerialPort.prototype.flush = function (callback) {
};
module.exports.SerialPort = SerialPort;
-module.exports.parsers = parsers;
+module.exports.parsers = parsers;
View
3  src/serialport.cpp
@@ -231,6 +231,9 @@ void EIO_AfterList(uv_work_t* req) {
item->Set(v8::String::New("comName"), v8::String::New((*it)->comName.c_str()));
item->Set(v8::String::New("manufacturer"), v8::String::New((*it)->manufacturer.c_str()));
item->Set(v8::String::New("pnpId"), v8::String::New((*it)->pnpId.c_str()));
+ item->Set(v8::String::New("locationId"), v8::String::New((*it)->locationId.c_str()));
+ item->Set(v8::String::New("vendorId"), v8::String::New((*it)->vendorId.c_str()));
+ item->Set(v8::String::New("productId"), v8::String::New((*it)->productId.c_str()));
results->Set(i, item);
}
argv[0] = v8::Undefined();
View
3  src/serialport.h
@@ -96,6 +96,9 @@ struct ListResultItem {
std::string comName;
std::string manufacturer;
std::string pnpId;
+ std::string locationId;
+ std::string vendorId;
+ std::string productId;
};
struct ListBaton {
View
293 src/serialport_unix.cpp
@@ -1,4 +1,3 @@
-
#ifndef WIN32
#include "serialport.h"
#include <unistd.h>
@@ -8,6 +7,15 @@
#ifdef __APPLE__
#include <AvailabilityMacros.h>
+#include <sys/param.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+
+uv_mutex_t list_mutex;
+Boolean lockInitialised = FALSE;
+
#endif
#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
#include <sys/ioctl.h>
@@ -15,7 +23,6 @@
#include <errno.h>
#endif
-
int ToBaudConstant(int baudRate);
int ToDataBitsConstant(int dataBits);
int ToStopBitsConstant(SerialPortStopBits stopBits);
@@ -64,6 +71,21 @@ int ToBaudConstant(int baudRate) {
return -1;
}
+#ifdef __APPLE__
+typedef struct SerialDevice {
+ char port[MAXPATHLEN];
+ char locationId[MAXPATHLEN];
+ char vendorId[MAXPATHLEN];
+ char productId[MAXPATHLEN];
+} stSerialDevice;
+
+typedef struct DeviceListItem {
+ struct SerialDevice value;
+ struct DeviceListItem *next;
+ int* length;
+} stDeviceListItem;
+#endif
+
int ToDataBitsConstant(int dataBits) {
switch (dataBits) {
case 8: default: return CS8;
@@ -192,7 +214,7 @@ void EIO_Open(uv_work_t* req) {
#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
speed_t speed = data->baudRate;
- if ( ioctl( fd, IOSSIOSPEED, &speed ) == -1 )
+ if ( ioctl( fd, IOSSIOSPEED, &speed ) == -1 )
{
printf( "Error %d calling ioctl( ..., IOSSIOSPEED, ... )\n", errno );
}
@@ -217,8 +239,271 @@ void EIO_Close(uv_work_t* req) {
close(data->fd);
}
+#ifdef __APPLE__
+
+// Function prototypes
+static kern_return_t FindModems(io_iterator_t *matchingServices);
+static io_registry_entry_t GetUsbDevice(char *pathName);
+static stDeviceListItem* GetSerialDevices();
+
+
+static kern_return_t FindModems(io_iterator_t *matchingServices)
+{
+ kern_return_t kernResult;
+ CFMutableDictionaryRef classesToMatch;
+ classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if (classesToMatch != NULL)
+ {
+ CFDictionarySetValue(classesToMatch,
+ CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDAllTypes));
+ }
+
+ kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices);
+
+ return kernResult;
+}
+
+static io_registry_entry_t GetUsbDevice(char* pathName)
+{
+ io_registry_entry_t device = 0;
+
+ CFMutableDictionaryRef classesToMatch = IOServiceMatching(kIOUSBDeviceClassName);
+ if (classesToMatch != NULL)
+ {
+ io_iterator_t matchingServices;
+ kern_return_t kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &matchingServices);
+ if (KERN_SUCCESS == kernResult)
+ {
+ io_service_t service;
+ Boolean deviceFound = false;
+
+ while ((service = IOIteratorNext(matchingServices)) && !deviceFound)
+ {
+ CFStringRef bsdPathAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(service, kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, kIORegistryIterateRecursively);
+
+ if (bsdPathAsCFString)
+ {
+ Boolean result;
+ char bsdPath[MAXPATHLEN];
+
+ // Convert the path from a CFString to a C (NUL-terminated)
+ result = CFStringGetCString(bsdPathAsCFString,
+ bsdPath,
+ sizeof(bsdPath),
+ kCFStringEncodingUTF8);
+
+ CFRelease(bsdPathAsCFString);
+
+ if (result && (strcmp(bsdPath, pathName) == 0))
+ {
+ deviceFound = true;
+ //memset(bsdPath, 0, sizeof(bsdPath));
+ device = service;
+ }
+ else
+ {
+ // Release the object which are no longer needed
+ (void) IOObjectRelease(service);
+ }
+ }
+ }
+ // Release the iterator.
+ IOObjectRelease(matchingServices);
+ }
+ }
+
+ return device;
+}
+
+static void ExtractUsbInformation(stSerialDevice *serialDevice, IOUSBDeviceInterface **deviceInterface)
+{
+ kern_return_t kernResult = KERN_FAILURE;
+ UInt32 locationID;
+ kernResult = (*deviceInterface)->GetLocationID(deviceInterface, &locationID);
+ if (KERN_SUCCESS == kernResult)
+ {
+ snprintf(serialDevice->locationId, 11, "0x%08x", locationID);
+ }
+
+ UInt16 vendorID;
+ kernResult = (*deviceInterface)->GetDeviceVendor(deviceInterface, &vendorID);
+ if (KERN_SUCCESS == kernResult)
+ {
+ snprintf(serialDevice->vendorId, 7, "0x%04x", vendorID);
+ }
+
+ UInt16 productID;
+ kernResult = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productID);
+ if (KERN_SUCCESS == kernResult)
+ {
+ snprintf(serialDevice->productId, 7, "0x%04x", productID);
+ }
+}
+
+static stDeviceListItem* GetSerialDevices()
+{
+ kern_return_t kernResult = KERN_FAILURE;
+ io_iterator_t serialPortIterator;
+ char bsdPath[MAXPATHLEN];
+
+ kernResult = FindModems(&serialPortIterator);
+
+ io_service_t modemService;
+ kernResult = KERN_FAILURE;
+ Boolean modemFound = false;
+
+ // Initialize the returned path
+ *bsdPath = '\0';
+
+ stDeviceListItem* devices = NULL;
+ stDeviceListItem* lastDevice = NULL;
+ int length = 0;
+
+ while ((modemService = IOIteratorNext(serialPortIterator)))
+ {
+ CFTypeRef bsdPathAsCFString;
+
+ bsdPathAsCFString = IORegistryEntrySearchCFProperty(modemService, kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, kIORegistryIterateRecursively);
+
+ if (bsdPathAsCFString)
+ {
+ Boolean result;
+
+ // Convert the path from a CFString to a C (NUL-terminated)
+
+ result = CFStringGetCString((CFStringRef) bsdPathAsCFString,
+ bsdPath,
+ sizeof(bsdPath),
+ kCFStringEncodingUTF8);
+ CFRelease(bsdPathAsCFString);
+
+ if (result)
+ {
+ stDeviceListItem *deviceListItem = (stDeviceListItem*) malloc(sizeof(stDeviceListItem));
+ stSerialDevice *serialDevice = &(deviceListItem->value);
+ strcpy(serialDevice->port, bsdPath);
+ memset(serialDevice->locationId, 0, sizeof(serialDevice->locationId));
+ memset(serialDevice->vendorId, 0, sizeof(serialDevice->vendorId));
+ memset(serialDevice->productId, 0, sizeof(serialDevice->productId));
+ deviceListItem->next = NULL;
+ deviceListItem->length = &length;
+
+ if (devices == NULL) {
+ devices = deviceListItem;
+ }
+ else {
+ lastDevice->next = deviceListItem;
+ }
+
+ lastDevice = deviceListItem;
+ length++;
+
+ modemFound = true;
+ kernResult = KERN_SUCCESS;
+
+ uv_mutex_lock(&list_mutex);
+
+ io_registry_entry_t device = GetUsbDevice(bsdPath);
+
+ if (device) {
+ IOCFPlugInInterface **plugInInterface = NULL;
+ SInt32 score;
+ HRESULT res;
+
+ IOUSBDeviceInterface **deviceInterface = NULL;
+
+ kernResult = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+
+ if ((kIOReturnSuccess != kernResult) || !plugInInterface) {
+ continue;
+ }
+
+ // Use the plugin interface to retrieve the device interface.
+ res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
+ (LPVOID*) &deviceInterface);
+
+ // Now done with the plugin interface.
+ (*plugInInterface)->Release(plugInInterface);
+
+ if (res || deviceInterface == NULL) {
+ continue;
+ }
+
+ // Extract the desired Information
+ ExtractUsbInformation(serialDevice, deviceInterface);
+
+ // Release the Interface
+ (*deviceInterface)->Release(deviceInterface);
+
+ // Release the device
+ (void) IOObjectRelease(device);
+ }
+
+ uv_mutex_unlock(&list_mutex);
+ }
+ }
+
+ // Release the io_service_t now that we are done with it.
+ (void) IOObjectRelease(modemService);
+ }
+
+ IOObjectRelease(serialPortIterator); // Release the iterator.
+
+ return devices;
+}
+
+#endif
+
void EIO_List(uv_work_t* req) {
// This code exists in javascript for unix platforms
+
+#ifdef __APPLE__
+ if(!lockInitialised)
+ {
+ uv_mutex_init(&list_mutex);
+ lockInitialised = TRUE;
+ }
+
+ ListBaton* data = static_cast<ListBaton*>(req->data);
+
+ stDeviceListItem* devices = GetSerialDevices();
+
+ if (*(devices->length) > 0)
+ {
+ stDeviceListItem* next = devices;
+
+ for (int i = 0, len = *(devices->length); i < len; i++) {
+ stSerialDevice device = (* next).value;
+
+ ListResultItem* resultItem = new ListResultItem();
+ resultItem->comName = device.port;
+
+ if (device.locationId != NULL) {
+ resultItem->locationId = device.locationId;
+ }
+ if (device.vendorId != NULL) {
+ resultItem->vendorId = device.vendorId;
+ }
+ if (device.productId != NULL) {
+ resultItem->productId = device.productId;
+ }
+ data->results.push_back(resultItem);
+
+ stDeviceListItem* current = next;
+
+ if (next->next != NULL)
+ {
+ next = next->next;
+ }
+
+ free(current);
+ }
+
+ }
+
+#endif
}
void EIO_Flush(uv_work_t* req) {
@@ -227,4 +512,4 @@ void EIO_Flush(uv_work_t* req) {
data->result = tcflush(data->fd, TCIFLUSH);
}
-#endif
+#endif
Something went wrong with that request. Please try again.