diff --git a/vendor/node-usb-native/.eslintrc b/vendor/node-usb-native/.eslintrc index 2b063c78c..3ade3b9dd 100644 --- a/vendor/node-usb-native/.eslintrc +++ b/vendor/node-usb-native/.eslintrc @@ -1,26 +1,26 @@ -{ - "extends": [ - "standard" - ], - "plugins": [ - "require-path-exists" - ], - "env": { - "node": true, - "mocha": true - }, - "rules": { - "arrow-parens": [2, "as-needed", {"requireForBlockBody": true }], - "no-unused-vars": [2, { "vars": "all", "args": "after-used" }], - "object-curly-spacing": [2, "always"], - "prefer-arrow-callback": 2, - "prefer-const": 2, - "prefer-template": 2, - "require-path-exists/exists": 2, - "require-path-exists/notEmpty": 2, - "require-path-exists/tooManyArguments": 2, - "semi": [2, "always", {"omitLastInOneLineBlock": true}], - "space-before-function-paren": [2, "never"], - "standard/object-curly-even-spacing": 0 - } +{ + "extends": [ + "standard" + ], + "plugins": [ + "require-path-exists" + ], + "env": { + "node": true, + "mocha": true + }, + "rules": { + "arrow-parens": [2, "as-needed", {"requireForBlockBody": true }], + "no-unused-vars": [2, { "vars": "all", "args": "after-used" }], + "object-curly-spacing": [2, "always"], + "prefer-arrow-callback": 2, + "prefer-const": 2, + "prefer-template": 2, + "require-path-exists/exists": 2, + "require-path-exists/notEmpty": 2, + "require-path-exists/tooManyArguments": 2, + "semi": [2, "always", {"omitLastInOneLineBlock": true}], + "space-before-function-paren": [2, "never"], + "standard/object-curly-even-spacing": 0 + } } \ No newline at end of file diff --git a/vendor/node-usb-native/.gitignore b/vendor/node-usb-native/.gitignore index 8d2b1ae24..41ace66fd 100644 --- a/vendor/node-usb-native/.gitignore +++ b/vendor/node-usb-native/.gitignore @@ -1,14 +1,14 @@ -$ cat .gitignore - -# Can ignore specific files -.settings.xml -.monitor -.DS_Store - -# Use wildcards as well -*~ -#*.swp - -# Can also ignore all directories and files in a directory. -node_modules/ -build/ +$ cat .gitignore + +# Can ignore specific files +.settings.xml +.monitor +.DS_Store + +# Use wildcards as well +*~ +#*.swp + +# Can also ignore all directories and files in a directory. +node_modules/ +build/ diff --git a/vendor/node-usb-native/.npmignore b/vendor/node-usb-native/.npmignore index b38db2f29..27b90ed90 100644 --- a/vendor/node-usb-native/.npmignore +++ b/vendor/node-usb-native/.npmignore @@ -1,2 +1,2 @@ -node_modules/ -build/ +node_modules/ +build/ diff --git a/vendor/node-usb-native/README.md b/vendor/node-usb-native/README.md index 5de5318a6..329164300 100644 --- a/vendor/node-usb-native/README.md +++ b/vendor/node-usb-native/README.md @@ -1,4 +1,4 @@ -This is a natvie package contains two modules: detector and serialport, detector provides the functionality of detecting usb changes and serialport provides the functionality of listing serial ports, openning serial ports and sending/receiving message to/from serial ports. - - require("../../../vendor/node-usb-native").SerialPort; - require("../../../vendor/node-usb-native").detector; +This is a natvie package contains two modules: detector and serialport, detector provides the functionality of detecting usb changes and serialport provides the functionality of listing serial ports, openning serial ports and sending/receiving message to/from serial ports. + + require("../../../vendor/node-usb-native").SerialPort; + require("../../../vendor/node-usb-native").detector; diff --git a/vendor/node-usb-native/binding.gyp b/vendor/node-usb-native/binding.gyp index c967103e1..10be49615 100644 --- a/vendor/node-usb-native/binding.gyp +++ b/vendor/node-usb-native/binding.gyp @@ -1,64 +1,64 @@ -{ - "targets": [ - { - "target_name": "usb-native", - "sources": [ - "src/detection.cpp", - "src/detection.h", - "src/deviceList.cpp", - "src/combined.cpp", - "src/serialport.cpp" - ], - "include_dirs": [ - " { - // Suss out the optional parameters - if (!pid && !callback) { - callback = vid; - vid = undefined; - } else if (!callback) { - callback = pid; - pid = undefined; - } - - return new Promise((resolve, reject) => { - // Assemble the optional args into something we can use with `apply` - var args = []; - if (vid) { - args = args.concat(vid); - } - if (pid) { - args = args.concat(pid); - } - - // Tack on our own callback that takes care of things - args = args.concat((err, devices) => { - // We call the callback if they passed one - if (callback) { - callback.call(callback, err, devices); - } - - // But also do the promise stuff - if (err) { - reject(err); - return; - } - resolve(devices); - }); - - // Fire off the `find` function that actually does all of the work - detection.find.apply(detection, args); - }); -}; -if (detection.registerAdded) { - detection.registerAdded((device) => { - detector.emit(`add:${device.vendorId}:${device.productId}`, device); - detector.emit(`insert:${device.vendorId}:${device.productId}`, device); - detector.emit(`add:${device.vendorId}`, device); - detector.emit(`insert:${device.vendorId}`, device); - detector.emit('add', device); - detector.emit('insert', device); - - detector.emit(`change:${device.vendorId}:${device.productId}`, device); - detector.emit(`change:${device.vendorId}`, device); - detector.emit('change', device); - }); - - detection.registerRemoved((device) => { - detector.emit(`remove:${device.vendorId}:${device.productId}`, device); - detector.emit(`remove:${device.vendorId}`, device); - detector.emit('remove', device); - - detector.emit(`change:${device.vendorId}:${device.productId}`, device); - detector.emit(`change:${device.vendorId}`, device); - detector.emit('change', device); - }); - - var started = true; - - detector.startMonitoring = () => { - if (started) { - return; - } - - started = true; - detection.startMonitoring(); - }; - - detector.stopMonitoring = () => { - if (!started) { - return; - } - - started = false; - detection.stopMonitoring(); - }; -} - -module.exports = detector; +var detection = require('./bindings'); +var EventEmitter2 = require('eventemitter2').EventEmitter2; + +var detector = new EventEmitter2({ + wildcard: true, + delimiter: ':', + maxListeners: 1000 // default would be 10! +}); + +// detector.find = detection.find; +detector.find = (vid, pid, callback) => { + // Suss out the optional parameters + if (!pid && !callback) { + callback = vid; + vid = undefined; + } else if (!callback) { + callback = pid; + pid = undefined; + } + + return new Promise((resolve, reject) => { + // Assemble the optional args into something we can use with `apply` + var args = []; + if (vid) { + args = args.concat(vid); + } + if (pid) { + args = args.concat(pid); + } + + // Tack on our own callback that takes care of things + args = args.concat((err, devices) => { + // We call the callback if they passed one + if (callback) { + callback.call(callback, err, devices); + } + + // But also do the promise stuff + if (err) { + reject(err); + return; + } + resolve(devices); + }); + + // Fire off the `find` function that actually does all of the work + detection.find.apply(detection, args); + }); +}; +if (detection.registerAdded) { + detection.registerAdded((device) => { + detector.emit(`add:${device.vendorId}:${device.productId}`, device); + detector.emit(`insert:${device.vendorId}:${device.productId}`, device); + detector.emit(`add:${device.vendorId}`, device); + detector.emit(`insert:${device.vendorId}`, device); + detector.emit('add', device); + detector.emit('insert', device); + + detector.emit(`change:${device.vendorId}:${device.productId}`, device); + detector.emit(`change:${device.vendorId}`, device); + detector.emit('change', device); + }); + + detection.registerRemoved((device) => { + detector.emit(`remove:${device.vendorId}:${device.productId}`, device); + detector.emit(`remove:${device.vendorId}`, device); + detector.emit('remove', device); + + detector.emit(`change:${device.vendorId}:${device.productId}`, device); + detector.emit(`change:${device.vendorId}`, device); + detector.emit('change', device); + }); + + var started = true; + + detector.startMonitoring = () => { + if (started) { + return; + } + + started = true; + detection.startMonitoring(); + }; + + detector.stopMonitoring = () => { + if (!started) { + return; + } + + started = false; + detection.stopMonitoring(); + }; +} + +module.exports = detector; diff --git a/vendor/node-usb-native/lib/index.js b/vendor/node-usb-native/lib/index.js index 3a5c973fe..09bfc7115 100644 --- a/vendor/node-usb-native/lib/index.js +++ b/vendor/node-usb-native/lib/index.js @@ -1,2 +1,2 @@ -exports.detector = require('./detector'); -exports.SerialPort = require('./serialport'); +exports.detector = require('./detector'); +exports.SerialPort = require('./serialport'); diff --git a/vendor/node-usb-native/lib/list-unix.js b/vendor/node-usb-native/lib/list-unix.js index e3b6de297..32e6719a9 100644 --- a/vendor/node-usb-native/lib/list-unix.js +++ b/vendor/node-usb-native/lib/list-unix.js @@ -1,109 +1,109 @@ -'use strict'; - -var childProcess = require('child_process'); -var fs = require('fs'); -var path = require('path'); - -function promisify(func) { - return (arg) => { - return new Promise((resolve, reject) => { - func(arg, (err, data) => { - if (err) { - return reject(err); - } - resolve(data); - }); - }); - }; -} - -function promisedFilter(func) { - return (data) => { - var shouldKeep = data.map(func); - return Promise.all(shouldKeep).then((keep) => { - return data.filter((path, index) => { - return keep[index]; - }); - }); - }; -} - -var statAsync = promisify(fs.stat); -var readdirAsync = promisify(fs.readdir); -var execAsync = promisify(childProcess.exec); - -function udevParser(output) { - var udevInfo = output.split('\n').reduce((info, line) => { - if (!line || line.trim() === '') { - return info; - } - var parts = line.split('=').map((part) => { - return part.trim(); - }); - - info[parts[0].toLowerCase()] = parts[1]; - - return info; - }, {}); - - var pnpId; - if (udevInfo.devlinks) { - udevInfo.devlinks.split(' ').forEach((path) => { - if (path.indexOf('/by-id/') === -1) { return } - pnpId = path.substring(path.lastIndexOf('/') + 1); - }); - } - - var vendorId = udevInfo.id_vendor_id; - if (vendorId && vendorId.substring(0, 2) !== '0x') { - vendorId = `0x${vendorId}`; - } - - var productId = udevInfo.id_model_id; - if (productId && productId.substring(0, 2) !== '0x') { - productId = `0x${productId}`; - } - - return { - comName: udevInfo.devname, - manufacturer: udevInfo.id_vendor, - serialNumber: udevInfo.id_serial, - pnpId: pnpId, - vendorId: vendorId, - productId: productId - }; -} - -function checkPathAndDevice(path) { - // get only serial port names - if (!(/(tty(S|ACM|USB|AMA|MFD)|rfcomm)/).test(path)) { - return false; - } - return statAsync(path).then((stats) => { - return stats.isCharacterDevice(); - }); -} - -function lookupPort(file) { - var udevadm = `udevadm info --query=property -p $(udevadm info -q path -n ${file})`; - return execAsync(udevadm).then(udevParser); -} - -function listUnix(callback) { - var dirName = '/dev'; - readdirAsync(dirName) - .catch((err) => { - // if this directory is not found we just pretend everything is OK - // TODO Depreciated this check? - if (err.errno === 34) { - return []; - } - throw err; - }) - .then((data) => { return data.map((file) => { return path.join(dirName, file) }) }) - .then(promisedFilter(checkPathAndDevice)) - .then((data) => { return Promise.all(data.map(lookupPort)) }) - .then((data) => { callback(null, data) }, (err) => { callback(err) }); -} - -module.exports = listUnix; +'use strict'; + +var childProcess = require('child_process'); +var fs = require('fs'); +var path = require('path'); + +function promisify(func) { + return (arg) => { + return new Promise((resolve, reject) => { + func(arg, (err, data) => { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + }; +} + +function promisedFilter(func) { + return (data) => { + var shouldKeep = data.map(func); + return Promise.all(shouldKeep).then((keep) => { + return data.filter((path, index) => { + return keep[index]; + }); + }); + }; +} + +var statAsync = promisify(fs.stat); +var readdirAsync = promisify(fs.readdir); +var execAsync = promisify(childProcess.exec); + +function udevParser(output) { + var udevInfo = output.split('\n').reduce((info, line) => { + if (!line || line.trim() === '') { + return info; + } + var parts = line.split('=').map((part) => { + return part.trim(); + }); + + info[parts[0].toLowerCase()] = parts[1]; + + return info; + }, {}); + + var pnpId; + if (udevInfo.devlinks) { + udevInfo.devlinks.split(' ').forEach((path) => { + if (path.indexOf('/by-id/') === -1) { return } + pnpId = path.substring(path.lastIndexOf('/') + 1); + }); + } + + var vendorId = udevInfo.id_vendor_id; + if (vendorId && vendorId.substring(0, 2) !== '0x') { + vendorId = `0x${vendorId}`; + } + + var productId = udevInfo.id_model_id; + if (productId && productId.substring(0, 2) !== '0x') { + productId = `0x${productId}`; + } + + return { + comName: udevInfo.devname, + manufacturer: udevInfo.id_vendor, + serialNumber: udevInfo.id_serial, + pnpId: pnpId, + vendorId: vendorId, + productId: productId + }; +} + +function checkPathAndDevice(path) { + // get only serial port names + if (!(/(tty(S|ACM|USB|AMA|MFD)|rfcomm)/).test(path)) { + return false; + } + return statAsync(path).then((stats) => { + return stats.isCharacterDevice(); + }); +} + +function lookupPort(file) { + var udevadm = `udevadm info --query=property -p $(udevadm info -q path -n ${file})`; + return execAsync(udevadm).then(udevParser); +} + +function listUnix(callback) { + var dirName = '/dev'; + readdirAsync(dirName) + .catch((err) => { + // if this directory is not found we just pretend everything is OK + // TODO Depreciated this check? + if (err.errno === 34) { + return []; + } + throw err; + }) + .then((data) => { return data.map((file) => { return path.join(dirName, file) }) }) + .then(promisedFilter(checkPathAndDevice)) + .then((data) => { return Promise.all(data.map(lookupPort)) }) + .then((data) => { callback(null, data) }, (err) => { callback(err) }); +} + +module.exports = listUnix; diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_ia32.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_ia32.node new file mode 100644 index 000000000..83b71d780 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_x64.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_x64.node new file mode 100644 index 000000000..5e379cb9a Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_darwin_6.1.4_x64.node b/vendor/node-usb-native/lib/native/usb-native_darwin_6.1.4_x64.node new file mode 100644 index 000000000..440a59200 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_darwin_6.1.4_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_6.1.4_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_6.1.4_ia32.node new file mode 100644 index 000000000..277de25a2 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_6.1.4_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_6.1.4_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_6.1.4_x64.node new file mode 100644 index 000000000..f31e6a342 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_6.1.4_x64.node differ diff --git a/vendor/node-usb-native/lib/native_loader.js b/vendor/node-usb-native/lib/native_loader.js index 0f1fc2ebf..ce4e0ea22 100644 --- a/vendor/node-usb-native/lib/native_loader.js +++ b/vendor/node-usb-native/lib/native_loader.js @@ -1,24 +1,24 @@ -const glob = require('glob'); -const path = require('path'); -const loadLibrary = function(parentFolder, libraryName) { - const nodegypFiles = glob(path.join(__dirname, `../build/+(Release|Debug)/${libraryName}.node`), { - sync: true - }); - const nodepregypFiles = glob(`${parentFolder.replace(/\\/g, '/')}/${libraryName}*${process.arch}*.node`, { - sync: true - }); - var binding = null; - nodegypFiles.concat(nodepregypFiles).forEach((file) => { - try { - var _temp = require(file); - binding = _temp; - console.log('using', file); - } catch (e) { - } - }); - if (!binding) { - console.log('[Warn]', 'no library available after trying files', nodegypFiles.concat(nodepregypFiles)); - } - return binding; -}; -exports.load = loadLibrary; +const glob = require('glob'); +const path = require('path'); +const loadLibrary = function(parentFolder, libraryName) { + const nodegypFiles = glob(path.join(__dirname, `../build/+(Release|Debug)/${libraryName}.node`), { + sync: true + }); + const nodepregypFiles = glob(`${parentFolder.replace(/\\/g, '/')}/${libraryName}*${process.arch}*.node`, { + sync: true + }); + var binding = null; + nodegypFiles.concat(nodepregypFiles).forEach((file) => { + try { + var _temp = require(file); + binding = _temp; + console.log('using', file); + } catch (e) { + } + }); + if (!binding) { + console.log('[Warn]', 'no library available after trying files', nodegypFiles.concat(nodepregypFiles)); + } + return binding; +}; +exports.load = loadLibrary; diff --git a/vendor/node-usb-native/lib/parsers.js b/vendor/node-usb-native/lib/parsers.js index 2783c1425..303fba50d 100644 --- a/vendor/node-usb-native/lib/parsers.js +++ b/vendor/node-usb-native/lib/parsers.js @@ -1,64 +1,64 @@ -'use strict'; - -// Copyright 2011 Chris Williams - -module.exports = { - raw: function(emitter, buffer) { - emitter.emit('data', buffer); - }, - - // encoding: ascii utf8 utf16le ucs2 base64 binary hex - // More: http://nodejs.org/api/buffer.html#buffer_buffer - readline: function(delimiter, encoding) { - if (typeof delimiter === 'undefined' || delimiter === null) { delimiter = '\r' } - if (typeof encoding === 'undefined' || encoding === null) { encoding = 'utf8' } - // Delimiter buffer saved in closure - var data = ''; - return function(emitter, buffer) { - // Collect data - data += buffer.toString(encoding); - // Split collected data by delimiter - var parts = data.split(delimiter); - data = parts.pop(); - parts.forEach((part) => { - emitter.emit('data', part); - }); - }; - }, - - // Emit a data event every `length` bytes - byteLength: function(length) { - var data = Buffer.alloc(0); - return function(emitter, buffer) { - data = Buffer.concat([data, buffer]); - while (data.length >= length) { - var out = data.slice(0, length); - data = data.slice(length); - emitter.emit('data', out); - } - }; - }, - - // Emit a data event each time a byte sequence (delimiter is an array of byte) is found - // Sample usage : byteDelimiter([10, 13]) - byteDelimiter: function(delimiter) { - if (Object.prototype.toString.call(delimiter) !== '[object Array]') { - delimiter = [ delimiter ]; - } - var buf = []; - var nextDelimIndex = 0; - return function(emitter, buffer) { - for (var i = 0; i < buffer.length; i++) { - buf[buf.length] = buffer[i]; - if (buf[buf.length - 1] === delimiter[nextDelimIndex]) { - nextDelimIndex++; - } - if (nextDelimIndex === delimiter.length) { - emitter.emit('data', buf); - buf = []; - nextDelimIndex = 0; - } - } - }; - } -}; +'use strict'; + +// Copyright 2011 Chris Williams + +module.exports = { + raw: function(emitter, buffer) { + emitter.emit('data', buffer); + }, + + // encoding: ascii utf8 utf16le ucs2 base64 binary hex + // More: http://nodejs.org/api/buffer.html#buffer_buffer + readline: function(delimiter, encoding) { + if (typeof delimiter === 'undefined' || delimiter === null) { delimiter = '\r' } + if (typeof encoding === 'undefined' || encoding === null) { encoding = 'utf8' } + // Delimiter buffer saved in closure + var data = ''; + return function(emitter, buffer) { + // Collect data + data += buffer.toString(encoding); + // Split collected data by delimiter + var parts = data.split(delimiter); + data = parts.pop(); + parts.forEach((part) => { + emitter.emit('data', part); + }); + }; + }, + + // Emit a data event every `length` bytes + byteLength: function(length) { + var data = Buffer.alloc(0); + return function(emitter, buffer) { + data = Buffer.concat([data, buffer]); + while (data.length >= length) { + var out = data.slice(0, length); + data = data.slice(length); + emitter.emit('data', out); + } + }; + }, + + // Emit a data event each time a byte sequence (delimiter is an array of byte) is found + // Sample usage : byteDelimiter([10, 13]) + byteDelimiter: function(delimiter) { + if (Object.prototype.toString.call(delimiter) !== '[object Array]') { + delimiter = [ delimiter ]; + } + var buf = []; + var nextDelimIndex = 0; + return function(emitter, buffer) { + for (var i = 0; i < buffer.length; i++) { + buf[buf.length] = buffer[i]; + if (buf[buf.length - 1] === delimiter[nextDelimIndex]) { + nextDelimIndex++; + } + if (nextDelimIndex === delimiter.length) { + emitter.emit('data', buf); + buf = []; + nextDelimIndex = 0; + } + } + }; + } +}; diff --git a/vendor/node-usb-native/lib/serialport.js b/vendor/node-usb-native/lib/serialport.js index 6fcc88e16..754f21769 100644 --- a/vendor/node-usb-native/lib/serialport.js +++ b/vendor/node-usb-native/lib/serialport.js @@ -1,502 +1,502 @@ -'use strict'; - -// Copyright 2011 Chris Williams - -const _debug = false; -const debug = (message) => { - if (_debug) console.log(message); -}; - -// shims -// Internal Dependencies -var SerialPortBinding = require('./bindings'); -var parsers = require('./parsers'); - -// Built-ins Dependencies -var fs = require('fs'); -var stream = require('stream'); -var util = require('util'); - -// VALIDATION ARRAYS -var DATABITS = [5, 6, 7, 8]; -var STOPBITS = [1, 1.5, 2]; -var PARITY = ['none', 'even', 'mark', 'odd', 'space']; -var FLOWCONTROLS = ['xon', 'xoff', 'xany', 'rtscts']; -var SET_OPTIONS = ['brk', 'cts', 'dtr', 'dts', 'rts']; - -// Stuff from ReadStream, refactored for our usage: -var kPoolSize = 40 * 1024; -var kMinPoolSpace = 128; - -var defaultSettings = { - baudRate: 9600, - autoOpen: true, - parity: 'none', - xon: false, - xoff: false, - xany: false, - rtscts: false, - hupcl: true, - dataBits: 8, - stopBits: 1, - bufferSize: 64 * 1024, - lock: true, - parser: parsers.raw, - platformOptions: SerialPortBinding.platformOptions -}; - -var defaultSetFlags = { - brk: false, - cts: false, - dtr: true, - dts: false, - rts: true -}; - -// deprecate the lowercase version of these options next major release -var LOWERCASE_OPTIONS = [ - 'baudRate', - 'dataBits', - 'stopBits', - 'bufferSize', - 'platformOptions' -]; - -function correctOptions(options) { - LOWERCASE_OPTIONS.forEach((name) => { - var lowerName = name.toLowerCase(); - if (options.hasOwnProperty(lowerName)) { - var value = options[lowerName]; - delete options[lowerName]; - options[name] = value; - } - }); - return options; -} - -function SerialPort(path, options, callback) { - if (typeof callback === 'boolean') { - throw new TypeError('`openImmediately` is now called `autoOpen` and is a property of options'); - } - - if (typeof options === 'function') { - callback = options; - options = {}; - } - - options = options || {}; - - stream.Stream.call(this); - - if (!path) { - throw new TypeError('No path specified'); - } - - this.path = path; - - var correctedOptions = correctOptions(options); - var settings = Object.assign({}, defaultSettings, correctedOptions); - - if (typeof settings.baudRate !== 'number') { - throw new TypeError(`Invalid "baudRate" must be a number got: ${settings.baudRate}`); - } - - if (DATABITS.indexOf(settings.dataBits) === -1) { - throw new TypeError(`Invalid "databits": ${settings.dataBits}`); - } - - if (STOPBITS.indexOf(settings.stopBits) === -1) { - throw new TypeError(`Invalid "stopbits": ${settings.stopBits}`); - } - - if (PARITY.indexOf(settings.parity) === -1) { - throw new TypeError(`Invalid "parity": ${settings.parity}`); - } - - FLOWCONTROLS.forEach((control) => { - if (typeof settings[control] !== 'boolean') { - throw new TypeError(`Invalid "${control}" is not boolean`); - } - }); - - settings.disconnectedCallback = this._disconnected.bind(this); - settings.dataCallback = settings.parser.bind(this, this); - - this.fd = null; - this.paused = true; - this.opening = false; - this.closing = false; - - if (process.platform !== 'win32') { - this.bufferSize = settings.bufferSize; - this.readable = true; - this.reading = false; - } - - this.options = settings; - - if (this.options.autoOpen) { - // is nextTick necessary? - process.nextTick(this.open.bind(this, callback)); - } -} - -util.inherits(SerialPort, stream.Stream); - -SerialPort.prototype._error = function(error, callback) { - if (callback) { - callback.call(this, error); - } else { - this.emit('error', error); - } -}; - -SerialPort.prototype.open = function(callback) { - if (this.isOpen()) { - return this._error(new Error('Port is already open'), callback); - } - - if (this.opening) { - return this._error(new Error('Port is opening'), callback); - } - - this.paused = true; - this.readable = true; - this.reading = false; - this.opening = true; - - SerialPortBinding.open(this.path, this.options, (err, fd) => { - this.opening = false; - if (err) { - debug('SerialPortBinding.open had an error', err); - return this._error(err, callback); - } - this.fd = fd; - this.paused = false; - - if (process.platform !== 'win32') { - this.serialPoller = new SerialPortBinding.SerialportPoller(this.fd, (err) => { - if (!err) { - this._read(); - } else { - this._disconnected(err); - } - }); - this.serialPoller.start(); - } - - this.emit('open'); - if (callback) { - callback.call(this, null); - } - }); -}; - -SerialPort.prototype.update = function(options, callback) { - if (!this.isOpen()) { - debug('update attempted, but port is not open'); - return this._error(new Error('Port is not open'), callback); - } - - var correctedOptions = correctOptions(options); - var settings = Object.assign({}, defaultSettings, correctedOptions); - this.options.baudRate = settings.baudRate; - - SerialPortBinding.update(this.fd, this.options, (err) => { - if (err) { - return this._error(err, callback); - } - if (callback) { - callback.call(this, null); - } - }); -}; - -SerialPort.prototype.isOpen = function() { - return this.fd !== null && !this.closing; -}; - -SerialPort.prototype.write = function(buffer, ending, callback) { - if (!this.isOpen()) { - debug('write attempted, but port is not open'); - return this._error(new Error('Port is not open'), callback); - } - - if (!Buffer.isBuffer(buffer)) { - buffer = Buffer.from(buffer); - } - - switch (ending) { - case 'Newline': - buffer = Buffer.concat([buffer, Buffer.from('\n')]); - break; - case 'Carriage return': - buffer = Buffer.concat([buffer, Buffer.from('\r')]); - break; - case 'Both NL & CR': - buffer = Buffer.concat([buffer, Buffer.from('\r\n')]); - break; - default: - break; - } - - debug(`write ${buffer.length} bytes of data`); - SerialPortBinding.write(this.fd, buffer, (err) => { - if (err) { - debug('SerialPortBinding.write had an error', err); - return this._error(err, callback); - } - if (callback) { - callback.call(this, null); - } - }); -}; - -if (process.platform !== 'win32') { - SerialPort.prototype._read = function() { - if (!this.readable || this.paused || this.reading || this.closing) { - return; - } - - this.reading = true; - - if (!this.pool || this.pool.length - this.pool.used < kMinPoolSpace) { - // discard the old pool. Can't add to the free list because - // users might have references to slices on it. - this.pool = Buffer.alloc(kPoolSize); - this.pool.used = 0; - } - - // Grab another reference to the pool in the case that while we're in the - // thread pool another read() finishes up the pool, and allocates a new - // one. - var toRead = Math.min(this.pool.length - this.pool.used, ~~this.bufferSize); - var start = this.pool.used; - - var _afterRead = (err, bytesRead, readPool, bytesRequested) => { - this.reading = false; - if (err) { - if (err.code && err.code === 'EAGAIN') { - if (this.isOpen()) { - this.serialPoller.start(); - } - // handle edge case were mac/unix doesn't clearly know the error. - } else if (err.code && (err.code === 'EBADF' || err.code === 'ENXIO' || (err.errno === -1 || err.code === 'UNKNOWN'))) { - this._disconnected(err); - } else { - this.fd = null; - this.readable = false; - this.emit('error', err); - } - return; - } - - // Since we will often not read the number of bytes requested, - // let's mark the ones we didn't need as available again. - this.pool.used -= bytesRequested - bytesRead; - - if (bytesRead === 0) { - if (this.isOpen()) { - this.serialPoller.start(); - } - } else { - var b = this.pool.slice(start, start + bytesRead); - - // do not emit events if the stream is paused - if (this.paused) { - if (!this.buffer) { - this.buffer = Buffer.alloc(0); - } - this.buffer = Buffer.concat([this.buffer, b]); - return; - } - this._emitData(b); - - // do not emit events anymore after we declared the stream unreadable - if (!this.readable) { - return; - } - this._read(); - } - }; - - fs.read(this.fd, this.pool, this.pool.used, toRead, null, (err, bytesRead) => { - var readPool = this.pool; - var bytesRequested = toRead; - _afterRead(err, bytesRead, readPool, bytesRequested); - }); - - this.pool.used += toRead; - }; - - SerialPort.prototype._emitData = function(data) { - this.options.dataCallback(data); - }; - - SerialPort.prototype.pause = function() { - this.paused = true; - }; - - SerialPort.prototype.resume = function() { - this.paused = false; - - if (this.buffer) { - var buffer = this.buffer; - this.buffer = null; - this._emitData(buffer); - } - - // No longer open? - if (!this.isOpen()) { - return; - } - - this._read(); - }; -} // if !'win32' - -SerialPort.prototype._disconnected = function(err) { - this.paused = true; - this.emit('disconnect', err); - if (this.closing) { - return; - } - - if (this.fd === null) { - return; - } - - this.closing = true; - if (process.platform !== 'win32') { - this.readable = false; - this.serialPoller.close(); - } - - SerialPortBinding.close(this.fd, (err) => { - this.closing = false; - if (err) { - debug('Disconnect close completed with error: ', err); - } - this.fd = null; - this.emit('close'); - }); -}; - -SerialPort.prototype.close = function(callback) { - this.paused = true; - - if (this.closing) { - debug('close attempted, but port is already closing'); - return this._error(new Error('Port is not open'), callback); - } - - if (!this.isOpen()) { - debug('close attempted, but port is not open'); - return this._error(new Error('Port is not open'), callback); - } - - this.closing = true; - - // Stop polling before closing the port. - if (process.platform !== 'win32') { - this.readable = false; - this.serialPoller.close(); - } - SerialPortBinding.close(this.fd, (err) => { - this.closing = false; - if (err) { - debug('SerialPortBinding.close had an error', err); - return this._error(err, callback); - } - - this.fd = null; - this.emit('close'); - if (callback) { - callback.call(this, null); - } - }); -}; - -SerialPort.prototype.flush = function(callback) { - if (!this.isOpen()) { - debug('flush attempted, but port is not open'); - return this._error(new Error('Port is not open'), callback); - } - - SerialPortBinding.flush(this.fd, (err, result) => { - if (err) { - debug('SerialPortBinding.flush had an error', err); - return this._error(err, callback); - } - if (callback) { - callback.call(this, null, result); - } - }); -}; - -SerialPort.prototype.set = function(options, callback) { - if (!this.isOpen()) { - debug('set attempted, but port is not open'); - return this._error(new Error('Port is not open'), callback); - } - - options = options || {}; - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - var settings = {}; - for (var i = SET_OPTIONS.length - 1; i >= 0; i--) { - var flag = SET_OPTIONS[i]; - if (options[flag] !== undefined) { - settings[flag] = options[flag]; - } else { - settings[flag] = defaultSetFlags[flag]; - } - } - - SerialPortBinding.set(this.fd, settings, (err) => { - if (err) { - debug('SerialPortBinding.set had an error', err); - return this._error(err, callback); - } - if (callback) { - callback.call(this, null); - } - }); -}; - -SerialPort.prototype.drain = function(callback) { - if (!this.isOpen()) { - debug('drain attempted, but port is not open'); - return this._error(new Error('Port is not open'), callback); - } - - SerialPortBinding.drain(this.fd, (err) => { - if (err) { - debug('SerialPortBinding.drain had an error', err); - return this._error(err, callback); - } - if (callback) { - callback.call(this, null); - } - }); -}; - -SerialPort.parsers = parsers; -SerialPort.list = SerialPortBinding.list; - -// Write a depreciation warning once -Object.defineProperty(SerialPort, 'SerialPort', { - get: function() { - // console.warn('DEPRECATION: Please use `require(\'serialport\')` instead of `require(\'serialport\').SerialPort`'); - Object.defineProperty(SerialPort, 'SerialPort', { - value: SerialPort - }); - return SerialPort; - }, - configurable: true -}); - -module.exports = SerialPort; +'use strict'; + +// Copyright 2011 Chris Williams + +const _debug = false; +const debug = (message) => { + if (_debug) console.log(message); +}; + +// shims +// Internal Dependencies +var SerialPortBinding = require('./bindings'); +var parsers = require('./parsers'); + +// Built-ins Dependencies +var fs = require('fs'); +var stream = require('stream'); +var util = require('util'); + +// VALIDATION ARRAYS +var DATABITS = [5, 6, 7, 8]; +var STOPBITS = [1, 1.5, 2]; +var PARITY = ['none', 'even', 'mark', 'odd', 'space']; +var FLOWCONTROLS = ['xon', 'xoff', 'xany', 'rtscts']; +var SET_OPTIONS = ['brk', 'cts', 'dtr', 'dts', 'rts']; + +// Stuff from ReadStream, refactored for our usage: +var kPoolSize = 40 * 1024; +var kMinPoolSpace = 128; + +var defaultSettings = { + baudRate: 9600, + autoOpen: true, + parity: 'none', + xon: false, + xoff: false, + xany: false, + rtscts: false, + hupcl: true, + dataBits: 8, + stopBits: 1, + bufferSize: 64 * 1024, + lock: true, + parser: parsers.raw, + platformOptions: SerialPortBinding.platformOptions +}; + +var defaultSetFlags = { + brk: false, + cts: false, + dtr: true, + dts: false, + rts: true +}; + +// deprecate the lowercase version of these options next major release +var LOWERCASE_OPTIONS = [ + 'baudRate', + 'dataBits', + 'stopBits', + 'bufferSize', + 'platformOptions' +]; + +function correctOptions(options) { + LOWERCASE_OPTIONS.forEach((name) => { + var lowerName = name.toLowerCase(); + if (options.hasOwnProperty(lowerName)) { + var value = options[lowerName]; + delete options[lowerName]; + options[name] = value; + } + }); + return options; +} + +function SerialPort(path, options, callback) { + if (typeof callback === 'boolean') { + throw new TypeError('`openImmediately` is now called `autoOpen` and is a property of options'); + } + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + options = options || {}; + + stream.Stream.call(this); + + if (!path) { + throw new TypeError('No path specified'); + } + + this.path = path; + + var correctedOptions = correctOptions(options); + var settings = Object.assign({}, defaultSettings, correctedOptions); + + if (typeof settings.baudRate !== 'number') { + throw new TypeError(`Invalid "baudRate" must be a number got: ${settings.baudRate}`); + } + + if (DATABITS.indexOf(settings.dataBits) === -1) { + throw new TypeError(`Invalid "databits": ${settings.dataBits}`); + } + + if (STOPBITS.indexOf(settings.stopBits) === -1) { + throw new TypeError(`Invalid "stopbits": ${settings.stopBits}`); + } + + if (PARITY.indexOf(settings.parity) === -1) { + throw new TypeError(`Invalid "parity": ${settings.parity}`); + } + + FLOWCONTROLS.forEach((control) => { + if (typeof settings[control] !== 'boolean') { + throw new TypeError(`Invalid "${control}" is not boolean`); + } + }); + + settings.disconnectedCallback = this._disconnected.bind(this); + settings.dataCallback = settings.parser.bind(this, this); + + this.fd = null; + this.paused = true; + this.opening = false; + this.closing = false; + + if (process.platform !== 'win32') { + this.bufferSize = settings.bufferSize; + this.readable = true; + this.reading = false; + } + + this.options = settings; + + if (this.options.autoOpen) { + // is nextTick necessary? + process.nextTick(this.open.bind(this, callback)); + } +} + +util.inherits(SerialPort, stream.Stream); + +SerialPort.prototype._error = function(error, callback) { + if (callback) { + callback.call(this, error); + } else { + this.emit('error', error); + } +}; + +SerialPort.prototype.open = function(callback) { + if (this.isOpen()) { + return this._error(new Error('Port is already open'), callback); + } + + if (this.opening) { + return this._error(new Error('Port is opening'), callback); + } + + this.paused = true; + this.readable = true; + this.reading = false; + this.opening = true; + + SerialPortBinding.open(this.path, this.options, (err, fd) => { + this.opening = false; + if (err) { + debug('SerialPortBinding.open had an error', err); + return this._error(err, callback); + } + this.fd = fd; + this.paused = false; + + if (process.platform !== 'win32') { + this.serialPoller = new SerialPortBinding.SerialportPoller(this.fd, (err) => { + if (!err) { + this._read(); + } else { + this._disconnected(err); + } + }); + this.serialPoller.start(); + } + + this.emit('open'); + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.update = function(options, callback) { + if (!this.isOpen()) { + debug('update attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + var correctedOptions = correctOptions(options); + var settings = Object.assign({}, defaultSettings, correctedOptions); + this.options.baudRate = settings.baudRate; + + SerialPortBinding.update(this.fd, this.options, (err) => { + if (err) { + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.isOpen = function() { + return this.fd !== null && !this.closing; +}; + +SerialPort.prototype.write = function(buffer, ending, callback) { + if (!this.isOpen()) { + debug('write attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + if (!Buffer.isBuffer(buffer)) { + buffer = Buffer.from(buffer); + } + + switch (ending) { + case 'Newline': + buffer = Buffer.concat([buffer, Buffer.from('\n')]); + break; + case 'Carriage return': + buffer = Buffer.concat([buffer, Buffer.from('\r')]); + break; + case 'Both NL & CR': + buffer = Buffer.concat([buffer, Buffer.from('\r\n')]); + break; + default: + break; + } + + debug(`write ${buffer.length} bytes of data`); + SerialPortBinding.write(this.fd, buffer, (err) => { + if (err) { + debug('SerialPortBinding.write had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +if (process.platform !== 'win32') { + SerialPort.prototype._read = function() { + if (!this.readable || this.paused || this.reading || this.closing) { + return; + } + + this.reading = true; + + if (!this.pool || this.pool.length - this.pool.used < kMinPoolSpace) { + // discard the old pool. Can't add to the free list because + // users might have references to slices on it. + this.pool = Buffer.alloc(kPoolSize); + this.pool.used = 0; + } + + // Grab another reference to the pool in the case that while we're in the + // thread pool another read() finishes up the pool, and allocates a new + // one. + var toRead = Math.min(this.pool.length - this.pool.used, ~~this.bufferSize); + var start = this.pool.used; + + var _afterRead = (err, bytesRead, readPool, bytesRequested) => { + this.reading = false; + if (err) { + if (err.code && err.code === 'EAGAIN') { + if (this.isOpen()) { + this.serialPoller.start(); + } + // handle edge case were mac/unix doesn't clearly know the error. + } else if (err.code && (err.code === 'EBADF' || err.code === 'ENXIO' || (err.errno === -1 || err.code === 'UNKNOWN'))) { + this._disconnected(err); + } else { + this.fd = null; + this.readable = false; + this.emit('error', err); + } + return; + } + + // Since we will often not read the number of bytes requested, + // let's mark the ones we didn't need as available again. + this.pool.used -= bytesRequested - bytesRead; + + if (bytesRead === 0) { + if (this.isOpen()) { + this.serialPoller.start(); + } + } else { + var b = this.pool.slice(start, start + bytesRead); + + // do not emit events if the stream is paused + if (this.paused) { + if (!this.buffer) { + this.buffer = Buffer.alloc(0); + } + this.buffer = Buffer.concat([this.buffer, b]); + return; + } + this._emitData(b); + + // do not emit events anymore after we declared the stream unreadable + if (!this.readable) { + return; + } + this._read(); + } + }; + + fs.read(this.fd, this.pool, this.pool.used, toRead, null, (err, bytesRead) => { + var readPool = this.pool; + var bytesRequested = toRead; + _afterRead(err, bytesRead, readPool, bytesRequested); + }); + + this.pool.used += toRead; + }; + + SerialPort.prototype._emitData = function(data) { + this.options.dataCallback(data); + }; + + SerialPort.prototype.pause = function() { + this.paused = true; + }; + + SerialPort.prototype.resume = function() { + this.paused = false; + + if (this.buffer) { + var buffer = this.buffer; + this.buffer = null; + this._emitData(buffer); + } + + // No longer open? + if (!this.isOpen()) { + return; + } + + this._read(); + }; +} // if !'win32' + +SerialPort.prototype._disconnected = function(err) { + this.paused = true; + this.emit('disconnect', err); + if (this.closing) { + return; + } + + if (this.fd === null) { + return; + } + + this.closing = true; + if (process.platform !== 'win32') { + this.readable = false; + this.serialPoller.close(); + } + + SerialPortBinding.close(this.fd, (err) => { + this.closing = false; + if (err) { + debug('Disconnect close completed with error: ', err); + } + this.fd = null; + this.emit('close'); + }); +}; + +SerialPort.prototype.close = function(callback) { + this.paused = true; + + if (this.closing) { + debug('close attempted, but port is already closing'); + return this._error(new Error('Port is not open'), callback); + } + + if (!this.isOpen()) { + debug('close attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + this.closing = true; + + // Stop polling before closing the port. + if (process.platform !== 'win32') { + this.readable = false; + this.serialPoller.close(); + } + SerialPortBinding.close(this.fd, (err) => { + this.closing = false; + if (err) { + debug('SerialPortBinding.close had an error', err); + return this._error(err, callback); + } + + this.fd = null; + this.emit('close'); + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.flush = function(callback) { + if (!this.isOpen()) { + debug('flush attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + SerialPortBinding.flush(this.fd, (err, result) => { + if (err) { + debug('SerialPortBinding.flush had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null, result); + } + }); +}; + +SerialPort.prototype.set = function(options, callback) { + if (!this.isOpen()) { + debug('set attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + options = options || {}; + if (!callback && typeof options === 'function') { + callback = options; + options = {}; + } + + var settings = {}; + for (var i = SET_OPTIONS.length - 1; i >= 0; i--) { + var flag = SET_OPTIONS[i]; + if (options[flag] !== undefined) { + settings[flag] = options[flag]; + } else { + settings[flag] = defaultSetFlags[flag]; + } + } + + SerialPortBinding.set(this.fd, settings, (err) => { + if (err) { + debug('SerialPortBinding.set had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.drain = function(callback) { + if (!this.isOpen()) { + debug('drain attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + SerialPortBinding.drain(this.fd, (err) => { + if (err) { + debug('SerialPortBinding.drain had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.parsers = parsers; +SerialPort.list = SerialPortBinding.list; + +// Write a depreciation warning once +Object.defineProperty(SerialPort, 'SerialPort', { + get: function() { + // console.warn('DEPRECATION: Please use `require(\'serialport\')` instead of `require(\'serialport\').SerialPort`'); + Object.defineProperty(SerialPort, 'SerialPort', { + value: SerialPort + }); + return SerialPort; + }, + configurable: true +}); + +module.exports = SerialPort; diff --git a/vendor/node-usb-native/license b/vendor/node-usb-native/license index 8cff664c4..4322c5ab6 100644 --- a/vendor/node-usb-native/license +++ b/vendor/node-usb-native/license @@ -1,19 +1,19 @@ -Copyright (c) 2013 Kaba AG - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +Copyright (c) 2013 Kaba AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/node-usb-native/package.json b/vendor/node-usb-native/package.json index c62428c71..7779f1140 100644 --- a/vendor/node-usb-native/package.json +++ b/vendor/node-usb-native/package.json @@ -1,34 +1,34 @@ -{ - "name": "usb-native", - "version": "0.0.1", - "description": "node-usb && node-serialport combined.", - "main": "lib/index.js", - "gypfile": true, - "keywords": [ - "usb", - "device", - "hardware", - "list", - "insert", - "add", - "remove", - "change", - "plug", - "unplug", - "notification" - ], - "license": "MIT", - "scripts": { - "install": "node-gyp rebuild --target=1.6.6 --arch=x64 --dist-url=https://atom.io/download/electron" - }, - "dependencies": { - "eventemitter2": "^4.1.0" - }, - "devDependencies": { - "chai": "^3.0.0", - "chai-as-promised": "^5.1.0", - "chalk": "^1.0.0", - "mocha": "^2.2.5", - "nan": "^2.12.1" - } +{ + "name": "usb-native", + "version": "0.0.1", + "description": "node-usb && node-serialport combined.", + "main": "lib/index.js", + "gypfile": true, + "keywords": [ + "usb", + "device", + "hardware", + "list", + "insert", + "add", + "remove", + "change", + "plug", + "unplug", + "notification" + ], + "license": "MIT", + "scripts": { + "install": "node-gyp rebuild --target=1.6.6 --arch=x64 --dist-url=https://atom.io/download/electron" + }, + "dependencies": { + "eventemitter2": "^4.1.0" + }, + "devDependencies": { + "chai": "^3.0.0", + "chai-as-promised": "^5.1.0", + "chalk": "^1.0.0", + "mocha": "^2.2.5", + "nan": "^2.12.1" + } } \ No newline at end of file diff --git a/vendor/node-usb-native/src/combined.cpp b/vendor/node-usb-native/src/combined.cpp index a7f4cc0ff..28c3ec526 100644 --- a/vendor/node-usb-native/src/combined.cpp +++ b/vendor/node-usb-native/src/combined.cpp @@ -1,24 +1,24 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - void init_serialport(v8::Handle target); -#ifndef DISABLE_USB_DETECTOR - void init_detector(v8::Handle target); -#endif - void initAll(v8::Handle target) { - init_serialport(target); - #ifndef DISABLE_USB_DETECTOR - init_detector(target); - #endif - } -} - -NODE_MODULE(detection, initAll); +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { + void init_serialport( v8::Local target); +#ifndef DISABLE_USB_DETECTOR + void init_detector( v8::Local target); +#endif + void initAll( v8::Local target) { + init_serialport(target); + #ifndef DISABLE_USB_DETECTOR + init_detector(target); + #endif + } +} + +NODE_MODULE(detection, initAll); diff --git a/vendor/node-usb-native/src/detection.cpp b/vendor/node-usb-native/src/detection.cpp index 5a16729f7..13f7bdc6a 100644 --- a/vendor/node-usb-native/src/detection.cpp +++ b/vendor/node-usb-native/src/detection.cpp @@ -1,222 +1,222 @@ -#include "detection.h" - - -#define OBJECT_ITEM_LOCATION_ID "locationId" -#define OBJECT_ITEM_VENDOR_ID "vendorId" -#define OBJECT_ITEM_PRODUCT_ID "productId" -#define OBJECT_ITEM_DEVICE_NAME "deviceName" -#define OBJECT_ITEM_MANUFACTURER "manufacturer" -#define OBJECT_ITEM_SERIAL_NUMBER "serialNumber" -#define OBJECT_ITEM_DEVICE_ADDRESS "deviceAddress" - - -Nan::Callback* addedCallback; -bool isAddedRegistered = false; - -Nan::Callback* removedCallback; -bool isRemovedRegistered = false; - -void RegisterAdded(const Nan::FunctionCallbackInfo& args) { - Nan::HandleScope scope; - - v8::Local callback; - - if (args.Length() == 0) { - return Nan::ThrowTypeError("First argument must be a function"); - } - - if (args.Length() == 1) { - // callback - if(!args[0]->IsFunction()) { - return Nan::ThrowTypeError("First argument must be a function"); - } - - callback = args[0].As(); - } - - addedCallback = new Nan::Callback(callback); - isAddedRegistered = true; -} - -void NotifyAdded(ListResultItem_t* it) { - Nan::HandleScope scope; - - if (it == NULL) { - return; - } - - if (isAddedRegistered){ - v8::Local argv[1]; - v8::Local item = Nan::New(); - item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New(it->locationId)); - item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New(it->vendorId)); - item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New(it->productId)); - item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New(it->deviceName.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New(it->manufacturer.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New(it->serialNumber.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New(it->deviceAddress)); - argv[0] = item; - - addedCallback->Call(1, argv); - } -} - -void RegisterRemoved(const Nan::FunctionCallbackInfo& args) { - Nan::HandleScope scope; - - v8::Local callback; - - if (args.Length() == 0) { - return Nan::ThrowTypeError("First argument must be a function"); - } - - if (args.Length() == 1) { - // callback - if(!args[0]->IsFunction()) { - return Nan::ThrowTypeError("First argument must be a function"); - } - - callback = args[0].As(); - } - - removedCallback = new Nan::Callback(callback); - isRemovedRegistered = true; -} - -void NotifyRemoved(ListResultItem_t* it) { - Nan::HandleScope scope; - - if (it == NULL) { - return; - } - - if (isRemovedRegistered) { - v8::Local argv[1]; - v8::Local item = Nan::New(); - item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New(it->locationId)); - item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New(it->vendorId)); - item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New(it->productId)); - item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New(it->deviceName.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New(it->manufacturer.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New(it->serialNumber.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New(it->deviceAddress)); - argv[0] = item; - - removedCallback->Call(1, argv); - } -} - -void Find(const Nan::FunctionCallbackInfo& args) { - Nan::HandleScope scope; - - int vid = 0; - int pid = 0; - v8::Local callback; - - if (args.Length() == 0) { - return Nan::ThrowTypeError("First argument must be a function"); - } - - if (args.Length() == 3) { - if (args[0]->IsNumber() && args[1]->IsNumber()) { - vid = (int) args[0]->NumberValue(); - pid = (int) args[1]->NumberValue(); - } - - // callback - if(!args[2]->IsFunction()) { - return Nan::ThrowTypeError("Third argument must be a function"); - } - - callback = args[2].As(); - } - - if (args.Length() == 2) { - if (args[0]->IsNumber()) { - vid = (int) args[0]->NumberValue(); - } - - // callback - if(!args[1]->IsFunction()) { - return Nan::ThrowTypeError("Second argument must be a function"); - } - - callback = args[1].As(); - } - - if (args.Length() == 1) { - // callback - if(!args[0]->IsFunction()) { - return Nan::ThrowTypeError("First argument must be a function"); - } - - callback = args[0].As(); - } - - ListBaton* baton = new ListBaton(); - strcpy(baton->errorString, ""); - baton->callback = new Nan::Callback(callback); - baton->vid = vid; - baton->pid = pid; - - uv_work_t* req = new uv_work_t(); - req->data = baton; - uv_queue_work(uv_default_loop(), req, EIO_Find, (uv_after_work_cb)EIO_AfterFind); -} - -void EIO_AfterFind(uv_work_t* req) { - Nan::HandleScope scope; - - ListBaton* data = static_cast(req->data); - - v8::Local argv[2]; - if(data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - argv[1] = Nan::Undefined(); - } - else { - v8::Local results = Nan::New(); - int i = 0; - for(std::list::iterator it = data->results.begin(); it != data->results.end(); it++, i++) { - v8::Local item = Nan::New(); - item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New((*it)->locationId)); - item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New((*it)->vendorId)); - item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New((*it)->productId)); - item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New((*it)->deviceName.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New((*it)->manufacturer.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New((*it)->serialNumber.c_str()).ToLocalChecked()); - item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New((*it)->deviceAddress)); - results->Set(i, item); - } - argv[0] = Nan::Undefined(); - argv[1] = results; - } - - data->callback->Call(2, argv); - - for(std::list::iterator it = data->results.begin(); it != data->results.end(); it++) { - delete *it; - } - delete data; - delete req; -} - -void StartMonitoring(const Nan::FunctionCallbackInfo& args) { - Start(); -} - -void StopMonitoring(const Nan::FunctionCallbackInfo& args) { - Stop(); -} - -extern "C" { - void init_detector (v8::Handle target) { - Nan::SetMethod(target, "find", Find); - Nan::SetMethod(target, "registerAdded", RegisterAdded); - Nan::SetMethod(target, "registerRemoved", RegisterRemoved); - Nan::SetMethod(target, "startMonitoring", StartMonitoring); - Nan::SetMethod(target, "stopMonitoring", StopMonitoring); - InitDetection(); - - } +#include "detection.h" + + +#define OBJECT_ITEM_LOCATION_ID "locationId" +#define OBJECT_ITEM_VENDOR_ID "vendorId" +#define OBJECT_ITEM_PRODUCT_ID "productId" +#define OBJECT_ITEM_DEVICE_NAME "deviceName" +#define OBJECT_ITEM_MANUFACTURER "manufacturer" +#define OBJECT_ITEM_SERIAL_NUMBER "serialNumber" +#define OBJECT_ITEM_DEVICE_ADDRESS "deviceAddress" + + +Nan::Callback* addedCallback; +bool isAddedRegistered = false; + +Nan::Callback* removedCallback; +bool isRemovedRegistered = false; + +void RegisterAdded(const Nan::FunctionCallbackInfo& args) { + Nan::HandleScope scope; + + v8::Local callback; + + if (args.Length() == 0) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + if (args.Length() == 1) { + // callback + if(!args[0]->IsFunction()) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + callback = args[0].As(); + } + + addedCallback = new Nan::Callback(callback); + isAddedRegistered = true; +} + +void NotifyAdded(ListResultItem_t* it) { + Nan::HandleScope scope; + + if (it == NULL) { + return; + } + + if (isAddedRegistered){ + v8::Local argv[1]; + v8::Local item = Nan::New(); + item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New(it->locationId)); + item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New(it->vendorId)); + item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New(it->productId)); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New(it->deviceName.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New(it->manufacturer.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New(it->serialNumber.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New(it->deviceAddress)); + argv[0] = item; + + addedCallback->Call(1, argv); + } +} + +void RegisterRemoved(const Nan::FunctionCallbackInfo& args) { + Nan::HandleScope scope; + + v8::Local callback; + + if (args.Length() == 0) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + if (args.Length() == 1) { + // callback + if(!args[0]->IsFunction()) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + callback = args[0].As(); + } + + removedCallback = new Nan::Callback(callback); + isRemovedRegistered = true; +} + +void NotifyRemoved(ListResultItem_t* it) { + Nan::HandleScope scope; + + if (it == NULL) { + return; + } + + if (isRemovedRegistered) { + v8::Local argv[1]; + v8::Local item = Nan::New(); + item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New(it->locationId)); + item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New(it->vendorId)); + item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New(it->productId)); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New(it->deviceName.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New(it->manufacturer.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New(it->serialNumber.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New(it->deviceAddress)); + argv[0] = item; + + removedCallback->Call(1, argv); + } +} + +void Find(const Nan::FunctionCallbackInfo& args) { + Nan::HandleScope scope; + + int vid = 0; + int pid = 0; + v8::Local callback; + + if (args.Length() == 0) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + if (args.Length() == 3) { + if (args[0]->IsNumber() && args[1]->IsNumber()) { + vid = (int) args[0]->NumberValue(); + pid = (int) args[1]->NumberValue(); + } + + // callback + if(!args[2]->IsFunction()) { + return Nan::ThrowTypeError("Third argument must be a function"); + } + + callback = args[2].As(); + } + + if (args.Length() == 2) { + if (args[0]->IsNumber()) { + vid = (int) args[0]->NumberValue(); + } + + // callback + if(!args[1]->IsFunction()) { + return Nan::ThrowTypeError("Second argument must be a function"); + } + + callback = args[1].As(); + } + + if (args.Length() == 1) { + // callback + if(!args[0]->IsFunction()) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + callback = args[0].As(); + } + + ListBaton* baton = new ListBaton(); + strcpy(baton->errorString, ""); + baton->callback = new Nan::Callback(callback); + baton->vid = vid; + baton->pid = pid; + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Find, (uv_after_work_cb)EIO_AfterFind); +} + +void EIO_AfterFind(uv_work_t* req) { + Nan::HandleScope scope; + + ListBaton* data = static_cast(req->data); + + v8::Local argv[2]; + if(data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + } + else { + v8::Local results = Nan::New(); + int i = 0; + for(std::list::iterator it = data->results.begin(); it != data->results.end(); it++, i++) { + v8::Local item = Nan::New(); + item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New((*it)->locationId)); + item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New((*it)->vendorId)); + item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New((*it)->productId)); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New((*it)->deviceName.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New((*it)->manufacturer.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New((*it)->serialNumber.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New((*it)->deviceAddress)); + results->Set(i, item); + } + argv[0] = Nan::Undefined(); + argv[1] = results; + } + + data->callback->Call(2, argv); + + for(std::list::iterator it = data->results.begin(); it != data->results.end(); it++) { + delete *it; + } + delete data; + delete req; +} + +void StartMonitoring(const Nan::FunctionCallbackInfo& args) { + Start(); +} + +void StopMonitoring(const Nan::FunctionCallbackInfo& args) { + Stop(); +} + +extern "C" { + void init_detector ( v8::Local target) { + Nan::SetMethod(target, "find", Find); + Nan::SetMethod(target, "registerAdded", RegisterAdded); + Nan::SetMethod(target, "registerRemoved", RegisterRemoved); + Nan::SetMethod(target, "startMonitoring", StartMonitoring); + Nan::SetMethod(target, "stopMonitoring", StopMonitoring); + InitDetection(); + + } } \ No newline at end of file diff --git a/vendor/node-usb-native/src/detection.h b/vendor/node-usb-native/src/detection.h index 39b21c735..dbcf672cd 100644 --- a/vendor/node-usb-native/src/detection.h +++ b/vendor/node-usb-native/src/detection.h @@ -1,42 +1,42 @@ - -#ifndef _USB_DETECTION_H -#define _USB_DETECTION_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "deviceList.h" - -void Find(const Nan::FunctionCallbackInfo& args); -void EIO_Find(uv_work_t* req); -void EIO_AfterFind(uv_work_t* req); -void InitDetection(); -void StartMonitoring(const Nan::FunctionCallbackInfo& args); -void Start(); -void StopMonitoring(const Nan::FunctionCallbackInfo& args); -void Stop(); - - -struct ListBaton { - public: - //v8::Persistent callback; - Nan::Callback* callback; - std::list results; - char errorString[1024]; - int vid; - int pid; -}; - -void RegisterAdded(const Nan::FunctionCallbackInfo& args); -void NotifyAdded(ListResultItem_t* it); -void RegisterRemoved(const Nan::FunctionCallbackInfo& args); -void NotifyRemoved(ListResultItem_t* it); - -#endif + +#ifndef _USB_DETECTION_H +#define _USB_DETECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "deviceList.h" + +void Find(const Nan::FunctionCallbackInfo& args); +void EIO_Find(uv_work_t* req); +void EIO_AfterFind(uv_work_t* req); +void InitDetection(); +void StartMonitoring(const Nan::FunctionCallbackInfo& args); +void Start(); +void StopMonitoring(const Nan::FunctionCallbackInfo& args); +void Stop(); + + +struct ListBaton { + public: + //v8::Persistent callback; + Nan::Callback* callback; + std::list results; + char errorString[1024]; + int vid; + int pid; +}; + +void RegisterAdded(const Nan::FunctionCallbackInfo& args); +void NotifyAdded(ListResultItem_t* it); +void RegisterRemoved(const Nan::FunctionCallbackInfo& args); +void NotifyRemoved(ListResultItem_t* it); + +#endif diff --git a/vendor/node-usb-native/src/detection_linux.cpp b/vendor/node-usb-native/src/detection_linux.cpp index aaa618f72..4c23877ce 100644 --- a/vendor/node-usb-native/src/detection_linux.cpp +++ b/vendor/node-usb-native/src/detection_linux.cpp @@ -1,317 +1,317 @@ -#include -#include - -#include "detection.h" -#include "deviceList.h" - -using namespace std; - - - -/********************************** - * Local defines - **********************************/ -#define DEVICE_ACTION_ADDED "add" -#define DEVICE_ACTION_REMOVED "remove" - -#define DEVICE_TYPE_DEVICE "usb_device" - -#define DEVICE_PROPERTY_NAME "ID_MODEL" -#define DEVICE_PROPERTY_SERIAL "ID_SERIAL_SHORT" -#define DEVICE_PROPERTY_VENDOR "ID_VENDOR" - - -/********************************** - * Local typedefs - **********************************/ - - - -/********************************** - * Local Variables - **********************************/ -ListResultItem_t* currentItem; -bool isAdded; - -struct udev *udev; -struct udev_enumerate *enumerate; -struct udev_list_entry *devices, *dev_list_entry; -struct udev_device *dev; - -struct udev_monitor *mon; -int fd; - -pthread_t thread; -pthread_mutex_t notify_mutex; -pthread_cond_t notifyNewDevice; -pthread_cond_t notifyDeviceHandled; - -bool newDeviceAvailable = false; -bool deviceHandled = true; - -bool isRunning = false; -/********************************** - * Local Helper Functions protoypes - **********************************/ -void BuildInitialDeviceList(); - -void* ThreadFunc(void* ptr); -void WaitForDeviceHandled(); -void SignalDeviceHandled(); -void WaitForNewDevice(); -void SignalDeviceAvailable(); - -/********************************** - * Public Functions - **********************************/ -void NotifyAsync(uv_work_t* req) { - WaitForNewDevice(); -} - -void NotifyFinished(uv_work_t* req) { - if (isRunning) { - if (isAdded) { - NotifyAdded(currentItem); - } - else { - NotifyRemoved(currentItem); - } - } - - // Delete Item in case of removal - if(isAdded == false) { - delete currentItem; - } - - SignalDeviceHandled(); - uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); -} - -void Start() { - isRunning = true; -} - -void Stop() { - isRunning = false; - pthread_mutex_lock(¬ify_mutex); - pthread_cond_signal(¬ifyNewDevice); - pthread_mutex_unlock(¬ify_mutex); -} - -void InitDetection() { - /* Create the udev object */ - udev = udev_new(); - if (!udev) - { - printf("Can't create udev\n"); - return; - } - - /* Set up a monitor to monitor devices */ - mon = udev_monitor_new_from_netlink(udev, "udev"); - udev_monitor_enable_receiving(mon); - - /* Get the file descriptor (fd) for the monitor. - This fd will get passed to select() */ - fd = udev_monitor_get_fd(mon); - - BuildInitialDeviceList(); - - pthread_mutex_init(¬ify_mutex, NULL); - pthread_cond_init(¬ifyNewDevice, NULL); - pthread_cond_init(¬ifyDeviceHandled, NULL); - - uv_work_t* req = new uv_work_t(); - uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); - - pthread_create(&thread, NULL, ThreadFunc, NULL); - - Start(); -} - - -void EIO_Find(uv_work_t* req) { - ListBaton* data = static_cast(req->data); - - CreateFilteredList(&data->results, data->vid, data->pid); -} - -/********************************** - * Local Functions - **********************************/ -void WaitForDeviceHandled() { - pthread_mutex_lock(¬ify_mutex); - if(deviceHandled == false) { - pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex); - } - deviceHandled = false; - pthread_mutex_unlock(¬ify_mutex); -} - -void SignalDeviceHandled() { - pthread_mutex_lock(¬ify_mutex); - deviceHandled = true; - pthread_cond_signal(¬ifyDeviceHandled); - pthread_mutex_unlock(¬ify_mutex); -} - -void WaitForNewDevice() { - pthread_mutex_lock(¬ify_mutex); - if(newDeviceAvailable == false){ - pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex); - } - newDeviceAvailable = false; - pthread_mutex_unlock(¬ify_mutex); -} - -void SignalDeviceAvailable() { - pthread_mutex_lock(¬ify_mutex); - newDeviceAvailable = true; - pthread_cond_signal(¬ifyNewDevice); - pthread_mutex_unlock(¬ify_mutex); -} - - - ListResultItem_t* GetProperties(struct udev_device* dev, ListResultItem_t* item) { - struct udev_list_entry* sysattrs; - struct udev_list_entry* entry; - sysattrs = udev_device_get_properties_list_entry(dev); - udev_list_entry_foreach(entry, sysattrs) { - const char *name, *value; - name = udev_list_entry_get_name(entry); - value = udev_list_entry_get_value(entry); - - if(strcmp(name, DEVICE_PROPERTY_NAME) == 0) { - item->deviceName = value; - } - else if(strcmp(name, DEVICE_PROPERTY_SERIAL) == 0) { - item->serialNumber = value; - } - else if(strcmp(name, DEVICE_PROPERTY_VENDOR) == 0) { - item->manufacturer = value; - } - } - item->vendorId = strtol(udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16); - item->productId = strtol(udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16); - item->deviceAddress = 0; - item->locationId = 0; - - return item; -} - -void DeviceAdded(struct udev_device* dev) { - DeviceItem_t* item = new DeviceItem_t(); - GetProperties(dev, &item->deviceParams); - - AddItemToList((char *)udev_device_get_devnode(dev), item); - - currentItem = &item->deviceParams; - isAdded = true; - - SignalDeviceAvailable(); -} - -void DeviceRemoved(struct udev_device* dev) { - ListResultItem_t* item = NULL; - - if(IsItemAlreadyStored((char *)udev_device_get_devnode(dev))) { - DeviceItem_t* deviceItem = GetItemFromList((char *)udev_device_get_devnode(dev)); - if(deviceItem) { - item = CopyElement(&deviceItem->deviceParams); - } - RemoveItemFromList(deviceItem); - delete deviceItem; - } - - if(item == NULL) { - item = new ListResultItem_t(); - GetProperties(dev, item); - } - - currentItem = item; - isAdded = false; - - SignalDeviceAvailable(); -} - - -void* ThreadFunc(void* ptr) { - while (1) { - /* Make the call to receive the device. - select() ensured that this will not block. */ - dev = udev_monitor_receive_device(mon); - if (dev) { - if(udev_device_get_devtype(dev) && strcmp(udev_device_get_devtype(dev), DEVICE_TYPE_DEVICE) == 0) { - if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_ADDED) == 0) { - WaitForDeviceHandled(); - DeviceAdded(dev); - } - else if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_REMOVED) == 0) { - WaitForDeviceHandled(); - DeviceRemoved(dev); - } - } - udev_device_unref(dev); - } - } - - return NULL; -} - - -void BuildInitialDeviceList() { - /* Create a list of the devices */ - enumerate = udev_enumerate_new(udev); - udev_enumerate_scan_devices(enumerate); - devices = udev_enumerate_get_list_entry(enumerate); - /* For each item enumerated, print out its information. - udev_list_entry_foreach is a macro which expands to - a loop. The loop will be executed for each member in - devices, setting dev_list_entry to a list entry - which contains the device's path in /sys. */ - udev_list_entry_foreach(dev_list_entry, devices) { - const char *path; - - /* Get the filename of the /sys entry for the device - and create a udev_device object (dev) representing it */ - path = udev_list_entry_get_name(dev_list_entry); - dev = udev_device_new_from_syspath(udev, path); - - /* usb_device_get_devnode() returns the path to the device node - itself in /dev. */ - if(udev_device_get_devnode(dev) == NULL || udev_device_get_sysattr_value(dev,"idVendor") == NULL) { - continue; - } - - /* From here, we can call get_sysattr_value() for each file - in the device's /sys entry. The strings passed into these - functions (idProduct, idVendor, serial, etc.) correspond - directly to the files in the /sys directory which - represents the USB device. Note that USB strings are - Unicode, UCS2 encoded, but the strings returned from - udev_device_get_sysattr_value() are UTF-8 encoded. */ - - DeviceItem_t* item = new DeviceItem_t(); - item->deviceParams.vendorId = strtol (udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16); - item->deviceParams.productId = strtol (udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16); - if(udev_device_get_sysattr_value(dev,"product") != NULL) { - item->deviceParams.deviceName = udev_device_get_sysattr_value(dev,"product"); - } - if(udev_device_get_sysattr_value(dev,"manufacturer") != NULL) { - item->deviceParams.manufacturer = udev_device_get_sysattr_value(dev,"manufacturer"); - } - if(udev_device_get_sysattr_value(dev,"serial") != NULL) { - item->deviceParams.serialNumber = udev_device_get_sysattr_value(dev, "serial"); - } - item->deviceParams.deviceAddress = 0; - item->deviceParams.locationId = 0; - - item->deviceState = DeviceState_Connect; - - AddItemToList((char *)udev_device_get_devnode(dev), item); - - udev_device_unref(dev); - } - /* Free the enumerator object */ - udev_enumerate_unref(enumerate); -} +#include +#include + +#include "detection.h" +#include "deviceList.h" + +using namespace std; + + + +/********************************** + * Local defines + **********************************/ +#define DEVICE_ACTION_ADDED "add" +#define DEVICE_ACTION_REMOVED "remove" + +#define DEVICE_TYPE_DEVICE "usb_device" + +#define DEVICE_PROPERTY_NAME "ID_MODEL" +#define DEVICE_PROPERTY_SERIAL "ID_SERIAL_SHORT" +#define DEVICE_PROPERTY_VENDOR "ID_VENDOR" + + +/********************************** + * Local typedefs + **********************************/ + + + +/********************************** + * Local Variables + **********************************/ +ListResultItem_t* currentItem; +bool isAdded; + +struct udev *udev; +struct udev_enumerate *enumerate; +struct udev_list_entry *devices, *dev_list_entry; +struct udev_device *dev; + +struct udev_monitor *mon; +int fd; + +pthread_t thread; +pthread_mutex_t notify_mutex; +pthread_cond_t notifyNewDevice; +pthread_cond_t notifyDeviceHandled; + +bool newDeviceAvailable = false; +bool deviceHandled = true; + +bool isRunning = false; +/********************************** + * Local Helper Functions protoypes + **********************************/ +void BuildInitialDeviceList(); + +void* ThreadFunc(void* ptr); +void WaitForDeviceHandled(); +void SignalDeviceHandled(); +void WaitForNewDevice(); +void SignalDeviceAvailable(); + +/********************************** + * Public Functions + **********************************/ +void NotifyAsync(uv_work_t* req) { + WaitForNewDevice(); +} + +void NotifyFinished(uv_work_t* req) { + if (isRunning) { + if (isAdded) { + NotifyAdded(currentItem); + } + else { + NotifyRemoved(currentItem); + } + } + + // Delete Item in case of removal + if(isAdded == false) { + delete currentItem; + } + + SignalDeviceHandled(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); +} + +void Start() { + isRunning = true; +} + +void Stop() { + isRunning = false; + pthread_mutex_lock(¬ify_mutex); + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + +void InitDetection() { + /* Create the udev object */ + udev = udev_new(); + if (!udev) + { + printf("Can't create udev\n"); + return; + } + + /* Set up a monitor to monitor devices */ + mon = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_enable_receiving(mon); + + /* Get the file descriptor (fd) for the monitor. + This fd will get passed to select() */ + fd = udev_monitor_get_fd(mon); + + BuildInitialDeviceList(); + + pthread_mutex_init(¬ify_mutex, NULL); + pthread_cond_init(¬ifyNewDevice, NULL); + pthread_cond_init(¬ifyDeviceHandled, NULL); + + uv_work_t* req = new uv_work_t(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + + pthread_create(&thread, NULL, ThreadFunc, NULL); + + Start(); +} + + +void EIO_Find(uv_work_t* req) { + ListBaton* data = static_cast(req->data); + + CreateFilteredList(&data->results, data->vid, data->pid); +} + +/********************************** + * Local Functions + **********************************/ +void WaitForDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + if(deviceHandled == false) { + pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex); + } + deviceHandled = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + deviceHandled = true; + pthread_cond_signal(¬ifyDeviceHandled); + pthread_mutex_unlock(¬ify_mutex); +} + +void WaitForNewDevice() { + pthread_mutex_lock(¬ify_mutex); + if(newDeviceAvailable == false){ + pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex); + } + newDeviceAvailable = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceAvailable() { + pthread_mutex_lock(¬ify_mutex); + newDeviceAvailable = true; + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + + + ListResultItem_t* GetProperties(struct udev_device* dev, ListResultItem_t* item) { + struct udev_list_entry* sysattrs; + struct udev_list_entry* entry; + sysattrs = udev_device_get_properties_list_entry(dev); + udev_list_entry_foreach(entry, sysattrs) { + const char *name, *value; + name = udev_list_entry_get_name(entry); + value = udev_list_entry_get_value(entry); + + if(strcmp(name, DEVICE_PROPERTY_NAME) == 0) { + item->deviceName = value; + } + else if(strcmp(name, DEVICE_PROPERTY_SERIAL) == 0) { + item->serialNumber = value; + } + else if(strcmp(name, DEVICE_PROPERTY_VENDOR) == 0) { + item->manufacturer = value; + } + } + item->vendorId = strtol(udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16); + item->productId = strtol(udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16); + item->deviceAddress = 0; + item->locationId = 0; + + return item; +} + +void DeviceAdded(struct udev_device* dev) { + DeviceItem_t* item = new DeviceItem_t(); + GetProperties(dev, &item->deviceParams); + + AddItemToList((char *)udev_device_get_devnode(dev), item); + + currentItem = &item->deviceParams; + isAdded = true; + + SignalDeviceAvailable(); +} + +void DeviceRemoved(struct udev_device* dev) { + ListResultItem_t* item = NULL; + + if(IsItemAlreadyStored((char *)udev_device_get_devnode(dev))) { + DeviceItem_t* deviceItem = GetItemFromList((char *)udev_device_get_devnode(dev)); + if(deviceItem) { + item = CopyElement(&deviceItem->deviceParams); + } + RemoveItemFromList(deviceItem); + delete deviceItem; + } + + if(item == NULL) { + item = new ListResultItem_t(); + GetProperties(dev, item); + } + + currentItem = item; + isAdded = false; + + SignalDeviceAvailable(); +} + + +void* ThreadFunc(void* ptr) { + while (1) { + /* Make the call to receive the device. + select() ensured that this will not block. */ + dev = udev_monitor_receive_device(mon); + if (dev) { + if(udev_device_get_devtype(dev) && strcmp(udev_device_get_devtype(dev), DEVICE_TYPE_DEVICE) == 0) { + if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_ADDED) == 0) { + WaitForDeviceHandled(); + DeviceAdded(dev); + } + else if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_REMOVED) == 0) { + WaitForDeviceHandled(); + DeviceRemoved(dev); + } + } + udev_device_unref(dev); + } + } + + return NULL; +} + + +void BuildInitialDeviceList() { + /* Create a list of the devices */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item enumerated, print out its information. + udev_list_entry_foreach is a macro which expands to + a loop. The loop will be executed for each member in + devices, setting dev_list_entry to a list entry + which contains the device's path in /sys. */ + udev_list_entry_foreach(dev_list_entry, devices) { + const char *path; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + + /* usb_device_get_devnode() returns the path to the device node + itself in /dev. */ + if(udev_device_get_devnode(dev) == NULL || udev_device_get_sysattr_value(dev,"idVendor") == NULL) { + continue; + } + + /* From here, we can call get_sysattr_value() for each file + in the device's /sys entry. The strings passed into these + functions (idProduct, idVendor, serial, etc.) correspond + directly to the files in the /sys directory which + represents the USB device. Note that USB strings are + Unicode, UCS2 encoded, but the strings returned from + udev_device_get_sysattr_value() are UTF-8 encoded. */ + + DeviceItem_t* item = new DeviceItem_t(); + item->deviceParams.vendorId = strtol (udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16); + item->deviceParams.productId = strtol (udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16); + if(udev_device_get_sysattr_value(dev,"product") != NULL) { + item->deviceParams.deviceName = udev_device_get_sysattr_value(dev,"product"); + } + if(udev_device_get_sysattr_value(dev,"manufacturer") != NULL) { + item->deviceParams.manufacturer = udev_device_get_sysattr_value(dev,"manufacturer"); + } + if(udev_device_get_sysattr_value(dev,"serial") != NULL) { + item->deviceParams.serialNumber = udev_device_get_sysattr_value(dev, "serial"); + } + item->deviceParams.deviceAddress = 0; + item->deviceParams.locationId = 0; + + item->deviceState = DeviceState_Connect; + + AddItemToList((char *)udev_device_get_devnode(dev), item); + + udev_device_unref(dev); + } + /* Free the enumerator object */ + udev_enumerate_unref(enumerate); +} diff --git a/vendor/node-usb-native/src/detection_mac.cpp b/vendor/node-usb-native/src/detection_mac.cpp index 30f161cd1..bcdfbef31 100644 --- a/vendor/node-usb-native/src/detection_mac.cpp +++ b/vendor/node-usb-native/src/detection_mac.cpp @@ -1,461 +1,461 @@ -#include "detection.h" -#include "deviceList.h" - -#include - -#include -#include -#include -#include - -#include -#include - -#include - - -typedef struct DeviceListItem { - io_object_t notification; - IOUSBDeviceInterface** deviceInterface; - DeviceItem_t* deviceItem; -} stDeviceListItem; - -static IONotificationPortRef gNotifyPort; -static io_iterator_t gAddedIter; -static CFRunLoopRef gRunLoop; - - -CFMutableDictionaryRef matchingDict; -CFRunLoopSourceRef runLoopSource; - -static pthread_t lookupThread; - -pthread_mutex_t notify_mutex; -pthread_cond_t notifyNewDevice; -pthread_cond_t notifyDeviceHandled; - -bool newDeviceAvailable = false; -bool deviceHandled = true; - -ListResultItem_t* notify_item; -bool isAdded = false; -bool isRunning = false; -bool intialDeviceImport = true; - -void WaitForDeviceHandled(); -void SignalDeviceHandled(); -void WaitForNewDevice(); -void SignalDeviceAvailable(); - -//================================================================================================ -// -// DeviceRemoved -// -// This routine will get called whenever any kIOGeneralInterest notification happens. We are -// interested in the kIOMessageServiceIsTerminated message so that's what we look for. Other -// messages are defined in IOMessage.h. -// -//================================================================================================ -void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) { - kern_return_t kr; - stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon; - DeviceItem_t* deviceItem = deviceListItem->deviceItem; - - if(messageType == kIOMessageServiceIsTerminated) { - if(deviceListItem->deviceInterface) { - kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface); - } - - kr = IOObjectRelease(deviceListItem->notification); - - - ListResultItem_t* item = NULL; - if(deviceItem) { - item = CopyElement(&deviceItem->deviceParams); - RemoveItemFromList(deviceItem); - delete deviceItem; - } - else { - item = new ListResultItem_t(); - } - - WaitForDeviceHandled(); - notify_item = item; - isAdded = false; - SignalDeviceAvailable(); - - } -} - -//================================================================================================ -// -// DeviceAdded -// -// This routine is the callback for our IOServiceAddMatchingNotification. When we get called -// we will look at all the devices that were added and we will: -// -// 1. Create some private data to relate to each device (in this case we use the service's name -// and the location ID of the device -// 2. Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device, -// using the refCon field to store a pointer to our private data. When we get called with -// this interest notification, we can grab the refCon and access our private data. -// -//================================================================================================ -void DeviceAdded(void *refCon, io_iterator_t iterator) { - kern_return_t kr; - io_service_t usbDevice; - IOCFPlugInInterface **plugInInterface = NULL; - SInt32 score; - HRESULT res; - - while((usbDevice = IOIteratorNext(iterator))) { - io_name_t deviceName; - CFStringRef deviceNameAsCFString; - UInt32 locationID; - UInt16 vendorId; - UInt16 productId; - UInt16 addr; - - DeviceItem_t* deviceItem = new DeviceItem_t(); - - // Get the USB device's name. - kr = IORegistryEntryGetName(usbDevice, deviceName); - if(KERN_SUCCESS != kr) { - deviceName[0] = '\0'; - } - - deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII); - - - if(deviceNameAsCFString) { - Boolean result; - char deviceName[MAXPATHLEN]; - - // Convert from a CFString to a C (NUL-terminated) - result = CFStringGetCString(deviceNameAsCFString, - deviceName, - sizeof(deviceName), - kCFStringEncodingUTF8); - - if(result) { - deviceItem->deviceParams.deviceName = deviceName; - } - - CFRelease(deviceNameAsCFString); - } - - CFStringRef manufacturerAsCFString = (CFStringRef)IORegistryEntrySearchCFProperty( - usbDevice, - kIOServicePlane, - CFSTR(kUSBVendorString), - kCFAllocatorDefault, - kIORegistryIterateRecursively - ); - - if(manufacturerAsCFString) { - Boolean result; - char manufacturer[MAXPATHLEN]; - - // Convert from a CFString to a C (NUL-terminated) - result = CFStringGetCString( - manufacturerAsCFString, - manufacturer, - sizeof(manufacturer), - kCFStringEncodingUTF8 - ); - - if(result) { - deviceItem->deviceParams.manufacturer = manufacturer; - } - - CFRelease(manufacturerAsCFString); - } - - CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty( - usbDevice, - kIOServicePlane, - CFSTR(kUSBSerialNumberString), - kCFAllocatorDefault, - kIORegistryIterateRecursively - ); - - if(serialNumberAsCFString) { - Boolean result; - char serialNumber[MAXPATHLEN]; - - // Convert from a CFString to a C (NUL-terminated) - result = CFStringGetCString( - serialNumberAsCFString, - serialNumber, - sizeof(serialNumber), - kCFStringEncodingUTF8 - ); - - if(result) { - deviceItem->deviceParams.serialNumber = serialNumber; - } - - CFRelease(serialNumberAsCFString); - } - - - // Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface - // for our device. This will create the necessary connections between our userland application and the - // kernel object for the USB Device. - kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); - - if((kIOReturnSuccess != kr) || !plugInInterface) { - fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr); - continue; - } - - stDeviceListItem *deviceListItem = new stDeviceListItem(); - - // Use the plugin interface to retrieve the device interface. - res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface); - - // Now done with the plugin interface. - (*plugInInterface)->Release(plugInInterface); - - if(res || deviceListItem->deviceInterface == NULL) { - fprintf(stderr, "QueryInterface returned %d.\n", (int) res); - continue; - } - - // Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h. - // In this case, fetch the locationID. The locationID uniquely identifies the device - // and will remain the same, even across reboots, so long as the bus topology doesn't change. - - kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID); - if(KERN_SUCCESS != kr) { - fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr); - continue; - } - deviceItem->deviceParams.locationId = locationID; - - - kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr); - if(KERN_SUCCESS != kr) { - fprintf(stderr, "GetDeviceAddress returned 0x%08x.\n", kr); - continue; - } - deviceItem->deviceParams.deviceAddress = addr; - - - kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId); - if(KERN_SUCCESS != kr) { - fprintf(stderr, "GetDeviceVendor returned 0x%08x.\n", kr); - continue; - } - deviceItem->deviceParams.vendorId = vendorId; - - kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId); - if(KERN_SUCCESS != kr) { - fprintf(stderr, "GetDeviceProduct returned 0x%08x.\n", kr); - continue; - } - deviceItem->deviceParams.productId = productId; - - - // Extract path name as unique key - io_string_t pathName; - IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName); - deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII); - char cPathName[MAXPATHLEN]; - - if(deviceNameAsCFString) { - Boolean result; - - // Convert from a CFString to a C (NUL-terminated) - result = CFStringGetCString( - deviceNameAsCFString, - cPathName, - sizeof(cPathName), - kCFStringEncodingUTF8 - ); - - - CFRelease(deviceNameAsCFString); - } - - AddItemToList(cPathName, deviceItem); - deviceListItem->deviceItem = deviceItem; - - if(intialDeviceImport == false) { - WaitForDeviceHandled(); - notify_item = &deviceItem->deviceParams; - isAdded = true; - SignalDeviceAvailable(); - } - - // Register for an interest notification of this device being removed. Use a reference to our - // private data as the refCon which will be passed to the notification callback. - kr = IOServiceAddInterestNotification( - gNotifyPort, // notifyPort - usbDevice, // service - kIOGeneralInterest, // interestType - DeviceRemoved, // callback - deviceListItem, // refCon - &(deviceListItem->notification) // notification - ); - - if(KERN_SUCCESS != kr) { - printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr); - } - - // Done with this USB device; release the reference added by IOIteratorNext - kr = IOObjectRelease(usbDevice); - } -} - - -void WaitForDeviceHandled() { - pthread_mutex_lock(¬ify_mutex); - if(deviceHandled == false) { - pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex); - } - deviceHandled = false; - pthread_mutex_unlock(¬ify_mutex); -} - -void SignalDeviceHandled() { - pthread_mutex_lock(¬ify_mutex); - deviceHandled = true; - pthread_cond_signal(¬ifyDeviceHandled); - pthread_mutex_unlock(¬ify_mutex); -} - -void WaitForNewDevice() { - pthread_mutex_lock(¬ify_mutex); - if(newDeviceAvailable == false) { - pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex); - } - newDeviceAvailable = false; - pthread_mutex_unlock(¬ify_mutex); -} - -void SignalDeviceAvailable() { - pthread_mutex_lock(¬ify_mutex); - newDeviceAvailable = true; - pthread_cond_signal(¬ifyNewDevice); - pthread_mutex_unlock(¬ify_mutex); -} - - -void *RunLoop(void * arg) { - - runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort); - - gRunLoop = CFRunLoopGetCurrent(); - CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode); - - // Start the run loop. Now we'll receive notifications. - CFRunLoopRun(); - - // We should never get here - fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n"); - - return NULL; -} - -void NotifyAsync(uv_work_t* req) { - WaitForNewDevice(); -} - -void NotifyFinished(uv_work_t* req) { - if(isRunning) { - if(isAdded) { - NotifyAdded(notify_item); - } - else { - NotifyRemoved(notify_item); - } - } - - // Delete Item in case of removal - if(isAdded == false) { - delete notify_item; - } - - if(isRunning) { - uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); - } - SignalDeviceHandled(); -} - -void Start() { - isRunning = true; -} - -void Stop() { - isRunning = false; - pthread_mutex_lock(¬ify_mutex); - pthread_cond_signal(¬ifyNewDevice); - pthread_mutex_unlock(¬ify_mutex); -} - -void InitDetection() { - - kern_return_t kr; - - // Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow - // the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7. - // See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X" - // . - // One exception is that you can use the matching dictionary "as is", i.e. without adding any matching - // criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will - // consume this dictionary reference, so there is no need to release it later on. - - // Interested in instances of class - // IOUSBDevice and its subclasses - matchingDict = IOServiceMatching(kIOUSBDeviceClassName); - - if (matchingDict == NULL) { - fprintf(stderr, "IOServiceMatching returned NULL.\n"); - } - - // Create a notification port and add its run loop event source to our run loop - // This is how async notifications get set up. - - gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); - - // Now set up a notification to be called when a device is first matched by I/O Kit. - kr = IOServiceAddMatchingNotification( - gNotifyPort, // notifyPort - kIOFirstMatchNotification, // notificationType - matchingDict, // matching - DeviceAdded, // callback - NULL, // refCon - &gAddedIter // notification - ); - - if (KERN_SUCCESS != kr) { - printf("IOServiceAddMatchingNotification returned 0x%08x.\n", kr); - } - - // Iterate once to get already-present devices and arm the notification - DeviceAdded(NULL, gAddedIter); - intialDeviceImport = false; - - - pthread_mutex_init(¬ify_mutex, NULL); - pthread_cond_init(¬ifyNewDevice, NULL); - pthread_cond_init(¬ifyDeviceHandled, NULL); - - int rc = pthread_create(&lookupThread, NULL, RunLoop, NULL); - if (rc) { - printf("ERROR; return code from pthread_create() is %d\n", rc); - exit(-1); - } - - uv_work_t* req = new uv_work_t(); - uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); - - Start(); -} - -void EIO_Find(uv_work_t* req) { - ListBaton* data = static_cast(req->data); - - CreateFilteredList(&data->results, data->vid, data->pid); -} +#include "detection.h" +#include "deviceList.h" + +#include + +#include +#include +#include +#include + +#include +#include + +#include + + +typedef struct DeviceListItem { + io_object_t notification; + IOUSBDeviceInterface** deviceInterface; + DeviceItem_t* deviceItem; +} stDeviceListItem; + +static IONotificationPortRef gNotifyPort; +static io_iterator_t gAddedIter; +static CFRunLoopRef gRunLoop; + + +CFMutableDictionaryRef matchingDict; +CFRunLoopSourceRef runLoopSource; + +static pthread_t lookupThread; + +pthread_mutex_t notify_mutex; +pthread_cond_t notifyNewDevice; +pthread_cond_t notifyDeviceHandled; + +bool newDeviceAvailable = false; +bool deviceHandled = true; + +ListResultItem_t* notify_item; +bool isAdded = false; +bool isRunning = false; +bool intialDeviceImport = true; + +void WaitForDeviceHandled(); +void SignalDeviceHandled(); +void WaitForNewDevice(); +void SignalDeviceAvailable(); + +//================================================================================================ +// +// DeviceRemoved +// +// This routine will get called whenever any kIOGeneralInterest notification happens. We are +// interested in the kIOMessageServiceIsTerminated message so that's what we look for. Other +// messages are defined in IOMessage.h. +// +//================================================================================================ +void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) { + kern_return_t kr; + stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon; + DeviceItem_t* deviceItem = deviceListItem->deviceItem; + + if(messageType == kIOMessageServiceIsTerminated) { + if(deviceListItem->deviceInterface) { + kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface); + } + + kr = IOObjectRelease(deviceListItem->notification); + + + ListResultItem_t* item = NULL; + if(deviceItem) { + item = CopyElement(&deviceItem->deviceParams); + RemoveItemFromList(deviceItem); + delete deviceItem; + } + else { + item = new ListResultItem_t(); + } + + WaitForDeviceHandled(); + notify_item = item; + isAdded = false; + SignalDeviceAvailable(); + + } +} + +//================================================================================================ +// +// DeviceAdded +// +// This routine is the callback for our IOServiceAddMatchingNotification. When we get called +// we will look at all the devices that were added and we will: +// +// 1. Create some private data to relate to each device (in this case we use the service's name +// and the location ID of the device +// 2. Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device, +// using the refCon field to store a pointer to our private data. When we get called with +// this interest notification, we can grab the refCon and access our private data. +// +//================================================================================================ +void DeviceAdded(void *refCon, io_iterator_t iterator) { + kern_return_t kr; + io_service_t usbDevice; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + HRESULT res; + + while((usbDevice = IOIteratorNext(iterator))) { + io_name_t deviceName; + CFStringRef deviceNameAsCFString; + UInt32 locationID; + UInt16 vendorId; + UInt16 productId; + UInt16 addr; + + DeviceItem_t* deviceItem = new DeviceItem_t(); + + // Get the USB device's name. + kr = IORegistryEntryGetName(usbDevice, deviceName); + if(KERN_SUCCESS != kr) { + deviceName[0] = '\0'; + } + + deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII); + + + if(deviceNameAsCFString) { + Boolean result; + char deviceName[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString(deviceNameAsCFString, + deviceName, + sizeof(deviceName), + kCFStringEncodingUTF8); + + if(result) { + deviceItem->deviceParams.deviceName = deviceName; + } + + CFRelease(deviceNameAsCFString); + } + + CFStringRef manufacturerAsCFString = (CFStringRef)IORegistryEntrySearchCFProperty( + usbDevice, + kIOServicePlane, + CFSTR(kUSBVendorString), + kCFAllocatorDefault, + kIORegistryIterateRecursively + ); + + if(manufacturerAsCFString) { + Boolean result; + char manufacturer[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString( + manufacturerAsCFString, + manufacturer, + sizeof(manufacturer), + kCFStringEncodingUTF8 + ); + + if(result) { + deviceItem->deviceParams.manufacturer = manufacturer; + } + + CFRelease(manufacturerAsCFString); + } + + CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty( + usbDevice, + kIOServicePlane, + CFSTR(kUSBSerialNumberString), + kCFAllocatorDefault, + kIORegistryIterateRecursively + ); + + if(serialNumberAsCFString) { + Boolean result; + char serialNumber[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString( + serialNumberAsCFString, + serialNumber, + sizeof(serialNumber), + kCFStringEncodingUTF8 + ); + + if(result) { + deviceItem->deviceParams.serialNumber = serialNumber; + } + + CFRelease(serialNumberAsCFString); + } + + + // Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface + // for our device. This will create the necessary connections between our userland application and the + // kernel object for the USB Device. + kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); + + if((kIOReturnSuccess != kr) || !plugInInterface) { + fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr); + continue; + } + + stDeviceListItem *deviceListItem = new stDeviceListItem(); + + // Use the plugin interface to retrieve the device interface. + res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface); + + // Now done with the plugin interface. + (*plugInInterface)->Release(plugInInterface); + + if(res || deviceListItem->deviceInterface == NULL) { + fprintf(stderr, "QueryInterface returned %d.\n", (int) res); + continue; + } + + // Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h. + // In this case, fetch the locationID. The locationID uniquely identifies the device + // and will remain the same, even across reboots, so long as the bus topology doesn't change. + + kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.locationId = locationID; + + + kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetDeviceAddress returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.deviceAddress = addr; + + + kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetDeviceVendor returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.vendorId = vendorId; + + kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetDeviceProduct returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.productId = productId; + + + // Extract path name as unique key + io_string_t pathName; + IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName); + deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII); + char cPathName[MAXPATHLEN]; + + if(deviceNameAsCFString) { + Boolean result; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString( + deviceNameAsCFString, + cPathName, + sizeof(cPathName), + kCFStringEncodingUTF8 + ); + + + CFRelease(deviceNameAsCFString); + } + + AddItemToList(cPathName, deviceItem); + deviceListItem->deviceItem = deviceItem; + + if(intialDeviceImport == false) { + WaitForDeviceHandled(); + notify_item = &deviceItem->deviceParams; + isAdded = true; + SignalDeviceAvailable(); + } + + // Register for an interest notification of this device being removed. Use a reference to our + // private data as the refCon which will be passed to the notification callback. + kr = IOServiceAddInterestNotification( + gNotifyPort, // notifyPort + usbDevice, // service + kIOGeneralInterest, // interestType + DeviceRemoved, // callback + deviceListItem, // refCon + &(deviceListItem->notification) // notification + ); + + if(KERN_SUCCESS != kr) { + printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr); + } + + // Done with this USB device; release the reference added by IOIteratorNext + kr = IOObjectRelease(usbDevice); + } +} + + +void WaitForDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + if(deviceHandled == false) { + pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex); + } + deviceHandled = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + deviceHandled = true; + pthread_cond_signal(¬ifyDeviceHandled); + pthread_mutex_unlock(¬ify_mutex); +} + +void WaitForNewDevice() { + pthread_mutex_lock(¬ify_mutex); + if(newDeviceAvailable == false) { + pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex); + } + newDeviceAvailable = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceAvailable() { + pthread_mutex_lock(¬ify_mutex); + newDeviceAvailable = true; + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + + +void *RunLoop(void * arg) { + + runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort); + + gRunLoop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode); + + // Start the run loop. Now we'll receive notifications. + CFRunLoopRun(); + + // We should never get here + fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n"); + + return NULL; +} + +void NotifyAsync(uv_work_t* req) { + WaitForNewDevice(); +} + +void NotifyFinished(uv_work_t* req) { + if(isRunning) { + if(isAdded) { + NotifyAdded(notify_item); + } + else { + NotifyRemoved(notify_item); + } + } + + // Delete Item in case of removal + if(isAdded == false) { + delete notify_item; + } + + if(isRunning) { + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + } + SignalDeviceHandled(); +} + +void Start() { + isRunning = true; +} + +void Stop() { + isRunning = false; + pthread_mutex_lock(¬ify_mutex); + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + +void InitDetection() { + + kern_return_t kr; + + // Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow + // the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7. + // See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X" + // . + // One exception is that you can use the matching dictionary "as is", i.e. without adding any matching + // criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will + // consume this dictionary reference, so there is no need to release it later on. + + // Interested in instances of class + // IOUSBDevice and its subclasses + matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + if (matchingDict == NULL) { + fprintf(stderr, "IOServiceMatching returned NULL.\n"); + } + + // Create a notification port and add its run loop event source to our run loop + // This is how async notifications get set up. + + gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); + + // Now set up a notification to be called when a device is first matched by I/O Kit. + kr = IOServiceAddMatchingNotification( + gNotifyPort, // notifyPort + kIOFirstMatchNotification, // notificationType + matchingDict, // matching + DeviceAdded, // callback + NULL, // refCon + &gAddedIter // notification + ); + + if (KERN_SUCCESS != kr) { + printf("IOServiceAddMatchingNotification returned 0x%08x.\n", kr); + } + + // Iterate once to get already-present devices and arm the notification + DeviceAdded(NULL, gAddedIter); + intialDeviceImport = false; + + + pthread_mutex_init(¬ify_mutex, NULL); + pthread_cond_init(¬ifyNewDevice, NULL); + pthread_cond_init(¬ifyDeviceHandled, NULL); + + int rc = pthread_create(&lookupThread, NULL, RunLoop, NULL); + if (rc) { + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + + uv_work_t* req = new uv_work_t(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + + Start(); +} + +void EIO_Find(uv_work_t* req) { + ListBaton* data = static_cast(req->data); + + CreateFilteredList(&data->results, data->vid, data->pid); +} diff --git a/vendor/node-usb-native/src/detection_win.cpp b/vendor/node-usb-native/src/detection_win.cpp index eb02e18c4..4d6645960 100644 --- a/vendor/node-usb-native/src/detection_win.cpp +++ b/vendor/node-usb-native/src/detection_win.cpp @@ -1,471 +1,471 @@ -#include -#include -#include -#include -#include - - -// Include Windows headers -#include -#include -#include -#include -#include - -#include "detection.h" -#include "deviceList.h" - -using namespace std; - -/********************************** - * Local defines - **********************************/ -#define VID_TAG "VID_" -#define PID_TAG "PID_" - -#define LIBRARY_NAME ("setupapi.dll") - - -#define DllImport __declspec(dllimport) - -#define MAX_THREAD_WINDOW_NAME 64 - -/********************************** - * Local typedefs - **********************************/ - - - -/********************************** - * Local Variables - **********************************/ -GUID GUID_DEVINTERFACE_USB_DEVICE = { - 0xA5DCBF10L, - 0x6530, - 0x11D2, - 0x90, - 0x1F, - 0x00, - 0xC0, - 0x4F, - 0xB9, - 0x51, - 0xED -}; - -HWND handle; -DWORD threadId; -HANDLE threadHandle; - -HANDLE deviceChangedRegisteredEvent; -HANDLE deviceChangedSentEvent; - -ListResultItem_t* currentDevice; -bool isAdded; -bool isRunning = false; - -HINSTANCE hinstLib; - - -typedef BOOL (WINAPI *_SetupDiEnumDeviceInfo) (HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); -typedef HDEVINFO (WINAPI *_SetupDiGetClassDevs) (const GUID *ClassGuid, PCTSTR Enumerator, HWND hwndParent, DWORD Flags); -typedef BOOL (WINAPI *_SetupDiDestroyDeviceInfoList) (HDEVINFO DeviceInfoSet); -typedef BOOL (WINAPI *_SetupDiGetDeviceInstanceId) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PTSTR DeviceInstanceId, DWORD DeviceInstanceIdSize, PDWORD RequiredSize); -typedef BOOL (WINAPI *_SetupDiGetDeviceRegistryProperty) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); - - -_SetupDiEnumDeviceInfo DllSetupDiEnumDeviceInfo; -_SetupDiGetClassDevs DllSetupDiGetClassDevs; -_SetupDiDestroyDeviceInfoList DllSetupDiDestroyDeviceInfoList; -_SetupDiGetDeviceInstanceId DllSetupDiGetDeviceInstanceId; -_SetupDiGetDeviceRegistryProperty DllSetupDiGetDeviceRegistryProperty; - - -/********************************** - * Local Helper Functions protoypes - **********************************/ -void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state); -DWORD WINAPI ListenerThread(LPVOID lpParam); - -void BuildInitialDeviceList(); - -void NotifyAsync(uv_work_t* req); -void NotifyFinished(uv_work_t* req); - -void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem); -bool CheckValidity(ListResultItem_t* item); - - -/********************************** - * Public Functions - **********************************/ -void NotifyAsync(uv_work_t* req) { - WaitForSingleObject(deviceChangedRegisteredEvent, INFINITE); -} - - -void NotifyFinished(uv_work_t* req) { - if (isRunning) { - if(isAdded) { - NotifyAdded(currentDevice); - } - else { - NotifyRemoved(currentDevice); - } - } - - // Delete Item in case of removal - if(isAdded == false) { - delete currentDevice; - } - - SetEvent(deviceChangedSentEvent); - - currentDevice = NULL; - uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); -} - -void LoadFunctions() { - - bool success; - - hinstLib = LoadLibrary(LIBRARY_NAME); - - if (hinstLib != NULL) { - DllSetupDiEnumDeviceInfo = (_SetupDiEnumDeviceInfo) GetProcAddress(hinstLib, "SetupDiEnumDeviceInfo"); - - DllSetupDiGetClassDevs = (_SetupDiGetClassDevs) GetProcAddress(hinstLib, "SetupDiGetClassDevsA"); - - DllSetupDiDestroyDeviceInfoList = (_SetupDiDestroyDeviceInfoList) GetProcAddress(hinstLib, "SetupDiDestroyDeviceInfoList"); - - DllSetupDiGetDeviceInstanceId = (_SetupDiGetDeviceInstanceId) GetProcAddress(hinstLib, "SetupDiGetDeviceInstanceIdA"); - - DllSetupDiGetDeviceRegistryProperty = (_SetupDiGetDeviceRegistryProperty) GetProcAddress(hinstLib, "SetupDiGetDeviceRegistryPropertyA"); - - success = ( - DllSetupDiEnumDeviceInfo != NULL && - DllSetupDiGetClassDevs != NULL && - DllSetupDiDestroyDeviceInfoList != NULL && - DllSetupDiGetDeviceInstanceId != NULL && - DllSetupDiGetDeviceRegistryProperty != NULL - ); - } - else { - success = false; - } - - if(!success) { - printf("Could not load library functions from dll -> abort (Check if %s is available)\r\n", LIBRARY_NAME); - exit(1); - } -} - -void Start() { - isRunning = true; -} - -void Stop() { - isRunning = false; - SetEvent(deviceChangedRegisteredEvent); -} - -void InitDetection() { - - LoadFunctions(); - - deviceChangedRegisteredEvent = CreateEvent(NULL, false /* auto-reset event */, false /* non-signalled state */, ""); - deviceChangedSentEvent = CreateEvent(NULL, false /* auto-reset event */, true /* non-signalled state */, ""); - - BuildInitialDeviceList(); - - threadHandle = CreateThread( - NULL, // default security attributes - 0, // use default stack size - ListenerThread, // thread function name - NULL, // argument to thread function - 0, // use default creation flags - &threadId - ); - - uv_work_t* req = new uv_work_t(); - uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); - - Start(); -} - - -void EIO_Find(uv_work_t* req) { - - ListBaton* data = static_cast(req->data); - - CreateFilteredList(&data->results, data->vid, data->pid); -} - - -/********************************** - * Local Functions - **********************************/ -void ToUpper(char * buf) { - char* c = buf; - while (*c != '\0') { - *c = toupper((unsigned char)*c); - c++; - } -} - - -void extractVidPid(char * buf, ListResultItem_t * item) { - if(buf == NULL) { - return; - } - - ToUpper(buf); - - char* string; - char* temp; - char* pidStr, *vidStr; - int vid = 0; - int pid = 0; - - string = new char[strlen(buf) + 1]; - memcpy(string, buf, strlen(buf) + 1); - - vidStr = strstr(string, VID_TAG); - pidStr = strstr(string, PID_TAG); - - if(vidStr != NULL) { - temp = (char*) (vidStr + strlen(VID_TAG)); - temp[4] = '\0'; - vid = strtol (temp, NULL, 16); - } - - if(pidStr != NULL) { - temp = (char*) (pidStr + strlen(PID_TAG)); - temp[4] = '\0'; - pid = strtol (temp, NULL, 16); - } - item->vendorId = vid; - item->productId = pid; - - delete string; -} - - -LRESULT CALLBACK DetectCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (msg == WM_DEVICECHANGE) { - if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) { - PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; - PDEV_BROADCAST_DEVICEINTERFACE pDevInf; - - if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { - pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; - UpdateDevice(pDevInf, wParam, (DBT_DEVICEARRIVAL == wParam) ? DeviceState_Connect : DeviceState_Disconnect); - } - } - } - - return 1; -} - - -DWORD WINAPI ListenerThread( LPVOID lpParam ) { - char className[MAX_THREAD_WINDOW_NAME]; - _snprintf_s(className, MAX_THREAD_WINDOW_NAME, "ListnerThreadUsbDetection_%d", GetCurrentThreadId()); - - WNDCLASSA wincl = {0}; - wincl.hInstance = GetModuleHandle(0); - wincl.lpszClassName = className; - wincl.lpfnWndProc = DetectCallback; - - if (!RegisterClassA(&wincl)) { - DWORD le = GetLastError(); - printf("RegisterClassA() failed [Error: %x]\r\n", le); - return 1; - } - - - HWND hwnd = CreateWindowExA(WS_EX_TOPMOST, className, className, 0, 0, 0, 0, 0, NULL, 0, 0, 0); - if (!hwnd) { - DWORD le = GetLastError(); - printf("CreateWindowExA() failed [Error: %x]\r\n", le); - return 1; - } - - DEV_BROADCAST_DEVICEINTERFACE_A notifyFilter = {0}; - notifyFilter.dbcc_size = sizeof(notifyFilter); - notifyFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - notifyFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; - - HDEVNOTIFY hDevNotify = RegisterDeviceNotificationA(hwnd, ¬ifyFilter, DEVICE_NOTIFY_WINDOW_HANDLE); - if (!hDevNotify) { - DWORD le = GetLastError(); - printf("RegisterDeviceNotificationA() failed [Error: %x]\r\n", le); - return 1; - } - - MSG msg; - while(TRUE) { - BOOL bRet = GetMessage(&msg, hwnd, 0, 0); - if ((bRet == 0) || (bRet == -1)) { - break; - } - - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return 0; -} - - -void BuildInitialDeviceList() { - TCHAR buf[MAX_PATH]; - DWORD dwFlag = (DIGCF_ALLCLASSES | DIGCF_PRESENT); - HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, "USB", NULL, dwFlag); - - if(INVALID_HANDLE_VALUE == hDevInfo) { - return; - } - - SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); - pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); - for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) { - DWORD nSize=0 ; - - if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { - break; - } - - DeviceItem_t* item = new DeviceItem_t(); - item->deviceState = DeviceState_Connect; - - DWORD DataT; - DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize); - - AddItemToList(buf, item); - ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &item->deviceParams); - } - - if(pspDevInfoData) { - HeapFree(GetProcessHeap(), 0, pspDevInfoData); - } - - if(hDevInfo) { - DllSetupDiDestroyDeviceInfoList(hDevInfo); - } -} - - -void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem) { - - DWORD DataT; - DWORD nSize; - static int dummy = 1; - - resultItem->locationId = 0; - resultItem->deviceAddress = dummy++; - - // device found - if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, buffSize, &nSize)) { - resultItem->deviceName = buf; - } - else if ( DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, buffSize, &nSize)) - { - resultItem->deviceName = buf; - } - if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_MFG, &DataT, (PBYTE)buf, buffSize, &nSize)) { - resultItem->manufacturer = buf; - } - if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, buffSize, &nSize)) { - // Use this to extract VID / PID - extractVidPid(buf, resultItem); - } -} - - -void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state) { - // dbcc_name: - // \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed} - // convert to - // USB\Vid_04e8&Pid_503b\0002F9A9828E0F06 - CString szDevId = pDevInf->dbcc_name+4; - int idx = szDevId.ReverseFind(_T('#')); - - szDevId.Truncate(idx); - szDevId.Replace(_T('#'), _T('\\')); - szDevId.MakeUpper(); - - CString szClass; - idx = szDevId.Find(_T('\\')); - szClass = szDevId.Left(idx); - - // if we are adding device, we only need present devices - // otherwise, we need all devices - DWORD dwFlag = DBT_DEVICEARRIVAL != wParam ? DIGCF_ALLCLASSES : (DIGCF_ALLCLASSES | DIGCF_PRESENT); - HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, szClass, NULL, dwFlag); - if(INVALID_HANDLE_VALUE == hDevInfo) { - return; - } - - SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); - pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); - for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) { - DWORD nSize=0 ; - TCHAR buf[MAX_PATH]; - - if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { - break; - } - - if(szDevId == buf) { - - WaitForSingleObject(deviceChangedSentEvent, INFINITE); - - DWORD DataT; - DWORD nSize; - DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize); - - if(state == DeviceState_Connect) { - DeviceItem_t* device = new DeviceItem_t(); - - AddItemToList(buf, device); - ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &device->deviceParams); - - currentDevice = &device->deviceParams; - isAdded = true; - } - else { - - ListResultItem_t* item = NULL; - if(IsItemAlreadyStored(buf)) { - DeviceItem_t* deviceItem = GetItemFromList(buf); - if(deviceItem) - { - item = CopyElement(&deviceItem->deviceParams); - } - RemoveItemFromList(deviceItem); - delete deviceItem; - } - - if(item == NULL) { - item = new ListResultItem_t(); - ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, item); - } - currentDevice = item; - isAdded = false; - } - - break; - } - } - - if (pspDevInfoData) { - HeapFree(GetProcessHeap(), 0, pspDevInfoData); - } - - if(hDevInfo) { - DllSetupDiDestroyDeviceInfoList(hDevInfo); - } - - SetEvent(deviceChangedRegisteredEvent); -} +#include +#include +#include +#include +#include + + +// Include Windows headers +#include +#include +#include +#include +#include + +#include "detection.h" +#include "deviceList.h" + +using namespace std; + +/********************************** + * Local defines + **********************************/ +#define VID_TAG "VID_" +#define PID_TAG "PID_" + +#define LIBRARY_NAME ("setupapi.dll") + + +#define DllImport __declspec(dllimport) + +#define MAX_THREAD_WINDOW_NAME 64 + +/********************************** + * Local typedefs + **********************************/ + + + +/********************************** + * Local Variables + **********************************/ +GUID GUID_DEVINTERFACE_USB_DEVICE = { + 0xA5DCBF10L, + 0x6530, + 0x11D2, + 0x90, + 0x1F, + 0x00, + 0xC0, + 0x4F, + 0xB9, + 0x51, + 0xED +}; + +HWND handle; +DWORD threadId; +HANDLE threadHandle; + +HANDLE deviceChangedRegisteredEvent; +HANDLE deviceChangedSentEvent; + +ListResultItem_t* currentDevice; +bool isAdded; +bool isRunning = false; + +HINSTANCE hinstLib; + + +typedef BOOL (WINAPI *_SetupDiEnumDeviceInfo) (HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); +typedef HDEVINFO (WINAPI *_SetupDiGetClassDevs) (const GUID *ClassGuid, PCTSTR Enumerator, HWND hwndParent, DWORD Flags); +typedef BOOL (WINAPI *_SetupDiDestroyDeviceInfoList) (HDEVINFO DeviceInfoSet); +typedef BOOL (WINAPI *_SetupDiGetDeviceInstanceId) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PTSTR DeviceInstanceId, DWORD DeviceInstanceIdSize, PDWORD RequiredSize); +typedef BOOL (WINAPI *_SetupDiGetDeviceRegistryProperty) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); + + +_SetupDiEnumDeviceInfo DllSetupDiEnumDeviceInfo; +_SetupDiGetClassDevs DllSetupDiGetClassDevs; +_SetupDiDestroyDeviceInfoList DllSetupDiDestroyDeviceInfoList; +_SetupDiGetDeviceInstanceId DllSetupDiGetDeviceInstanceId; +_SetupDiGetDeviceRegistryProperty DllSetupDiGetDeviceRegistryProperty; + + +/********************************** + * Local Helper Functions protoypes + **********************************/ +void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state); +DWORD WINAPI ListenerThread(LPVOID lpParam); + +void BuildInitialDeviceList(); + +void NotifyAsync(uv_work_t* req); +void NotifyFinished(uv_work_t* req); + +void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem); +bool CheckValidity(ListResultItem_t* item); + + +/********************************** + * Public Functions + **********************************/ +void NotifyAsync(uv_work_t* req) { + WaitForSingleObject(deviceChangedRegisteredEvent, INFINITE); +} + + +void NotifyFinished(uv_work_t* req) { + if (isRunning) { + if(isAdded) { + NotifyAdded(currentDevice); + } + else { + NotifyRemoved(currentDevice); + } + } + + // Delete Item in case of removal + if(isAdded == false) { + delete currentDevice; + } + + SetEvent(deviceChangedSentEvent); + + currentDevice = NULL; + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); +} + +void LoadFunctions() { + + bool success; + + hinstLib = LoadLibrary(LIBRARY_NAME); + + if (hinstLib != NULL) { + DllSetupDiEnumDeviceInfo = (_SetupDiEnumDeviceInfo) GetProcAddress(hinstLib, "SetupDiEnumDeviceInfo"); + + DllSetupDiGetClassDevs = (_SetupDiGetClassDevs) GetProcAddress(hinstLib, "SetupDiGetClassDevsA"); + + DllSetupDiDestroyDeviceInfoList = (_SetupDiDestroyDeviceInfoList) GetProcAddress(hinstLib, "SetupDiDestroyDeviceInfoList"); + + DllSetupDiGetDeviceInstanceId = (_SetupDiGetDeviceInstanceId) GetProcAddress(hinstLib, "SetupDiGetDeviceInstanceIdA"); + + DllSetupDiGetDeviceRegistryProperty = (_SetupDiGetDeviceRegistryProperty) GetProcAddress(hinstLib, "SetupDiGetDeviceRegistryPropertyA"); + + success = ( + DllSetupDiEnumDeviceInfo != NULL && + DllSetupDiGetClassDevs != NULL && + DllSetupDiDestroyDeviceInfoList != NULL && + DllSetupDiGetDeviceInstanceId != NULL && + DllSetupDiGetDeviceRegistryProperty != NULL + ); + } + else { + success = false; + } + + if(!success) { + printf("Could not load library functions from dll -> abort (Check if %s is available)\r\n", LIBRARY_NAME); + exit(1); + } +} + +void Start() { + isRunning = true; +} + +void Stop() { + isRunning = false; + SetEvent(deviceChangedRegisteredEvent); +} + +void InitDetection() { + + LoadFunctions(); + + deviceChangedRegisteredEvent = CreateEvent(NULL, false /* auto-reset event */, false /* non-signalled state */, ""); + deviceChangedSentEvent = CreateEvent(NULL, false /* auto-reset event */, true /* non-signalled state */, ""); + + BuildInitialDeviceList(); + + threadHandle = CreateThread( + NULL, // default security attributes + 0, // use default stack size + ListenerThread, // thread function name + NULL, // argument to thread function + 0, // use default creation flags + &threadId + ); + + uv_work_t* req = new uv_work_t(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + + Start(); +} + + +void EIO_Find(uv_work_t* req) { + + ListBaton* data = static_cast(req->data); + + CreateFilteredList(&data->results, data->vid, data->pid); +} + + +/********************************** + * Local Functions + **********************************/ +void ToUpper(char * buf) { + char* c = buf; + while (*c != '\0') { + *c = toupper((unsigned char)*c); + c++; + } +} + + +void extractVidPid(char * buf, ListResultItem_t * item) { + if(buf == NULL) { + return; + } + + ToUpper(buf); + + char* string; + char* temp; + char* pidStr, *vidStr; + int vid = 0; + int pid = 0; + + string = new char[strlen(buf) + 1]; + memcpy(string, buf, strlen(buf) + 1); + + vidStr = strstr(string, VID_TAG); + pidStr = strstr(string, PID_TAG); + + if(vidStr != NULL) { + temp = (char*) (vidStr + strlen(VID_TAG)); + temp[4] = '\0'; + vid = strtol (temp, NULL, 16); + } + + if(pidStr != NULL) { + temp = (char*) (pidStr + strlen(PID_TAG)); + temp[4] = '\0'; + pid = strtol (temp, NULL, 16); + } + item->vendorId = vid; + item->productId = pid; + + delete string; +} + + +LRESULT CALLBACK DetectCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_DEVICECHANGE) { + if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) { + PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; + PDEV_BROADCAST_DEVICEINTERFACE pDevInf; + + if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; + UpdateDevice(pDevInf, wParam, (DBT_DEVICEARRIVAL == wParam) ? DeviceState_Connect : DeviceState_Disconnect); + } + } + } + + return 1; +} + + +DWORD WINAPI ListenerThread( LPVOID lpParam ) { + char className[MAX_THREAD_WINDOW_NAME]; + _snprintf_s(className, MAX_THREAD_WINDOW_NAME, "ListnerThreadUsbDetection_%d", GetCurrentThreadId()); + + WNDCLASSA wincl = {0}; + wincl.hInstance = GetModuleHandle(0); + wincl.lpszClassName = className; + wincl.lpfnWndProc = DetectCallback; + + if (!RegisterClassA(&wincl)) { + DWORD le = GetLastError(); + printf("RegisterClassA() failed [Error: %x]\r\n", le); + return 1; + } + + + HWND hwnd = CreateWindowExA(WS_EX_TOPMOST, className, className, 0, 0, 0, 0, 0, NULL, 0, 0, 0); + if (!hwnd) { + DWORD le = GetLastError(); + printf("CreateWindowExA() failed [Error: %x]\r\n", le); + return 1; + } + + DEV_BROADCAST_DEVICEINTERFACE_A notifyFilter = {0}; + notifyFilter.dbcc_size = sizeof(notifyFilter); + notifyFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + notifyFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; + + HDEVNOTIFY hDevNotify = RegisterDeviceNotificationA(hwnd, ¬ifyFilter, DEVICE_NOTIFY_WINDOW_HANDLE); + if (!hDevNotify) { + DWORD le = GetLastError(); + printf("RegisterDeviceNotificationA() failed [Error: %x]\r\n", le); + return 1; + } + + MSG msg; + while(TRUE) { + BOOL bRet = GetMessage(&msg, hwnd, 0, 0); + if ((bRet == 0) || (bRet == -1)) { + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} + + +void BuildInitialDeviceList() { + TCHAR buf[MAX_PATH]; + DWORD dwFlag = (DIGCF_ALLCLASSES | DIGCF_PRESENT); + HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, "USB", NULL, dwFlag); + + if(INVALID_HANDLE_VALUE == hDevInfo) { + return; + } + + SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); + pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); + for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) { + DWORD nSize=0 ; + + if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { + break; + } + + DeviceItem_t* item = new DeviceItem_t(); + item->deviceState = DeviceState_Connect; + + DWORD DataT; + DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize); + + AddItemToList(buf, item); + ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &item->deviceParams); + } + + if(pspDevInfoData) { + HeapFree(GetProcessHeap(), 0, pspDevInfoData); + } + + if(hDevInfo) { + DllSetupDiDestroyDeviceInfoList(hDevInfo); + } +} + + +void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem) { + + DWORD DataT; + DWORD nSize; + static int dummy = 1; + + resultItem->locationId = 0; + resultItem->deviceAddress = dummy++; + + // device found + if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, buffSize, &nSize)) { + resultItem->deviceName = buf; + } + else if ( DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, buffSize, &nSize)) + { + resultItem->deviceName = buf; + } + if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_MFG, &DataT, (PBYTE)buf, buffSize, &nSize)) { + resultItem->manufacturer = buf; + } + if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, buffSize, &nSize)) { + // Use this to extract VID / PID + extractVidPid(buf, resultItem); + } +} + + +void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state) { + // dbcc_name: + // \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed} + // convert to + // USB\Vid_04e8&Pid_503b\0002F9A9828E0F06 + CString szDevId = pDevInf->dbcc_name+4; + int idx = szDevId.ReverseFind(_T('#')); + + szDevId.Truncate(idx); + szDevId.Replace(_T('#'), _T('\\')); + szDevId.MakeUpper(); + + CString szClass; + idx = szDevId.Find(_T('\\')); + szClass = szDevId.Left(idx); + + // if we are adding device, we only need present devices + // otherwise, we need all devices + DWORD dwFlag = DBT_DEVICEARRIVAL != wParam ? DIGCF_ALLCLASSES : (DIGCF_ALLCLASSES | DIGCF_PRESENT); + HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, szClass, NULL, dwFlag); + if(INVALID_HANDLE_VALUE == hDevInfo) { + return; + } + + SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); + pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); + for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) { + DWORD nSize=0 ; + TCHAR buf[MAX_PATH]; + + if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { + break; + } + + if(szDevId == buf) { + + WaitForSingleObject(deviceChangedSentEvent, INFINITE); + + DWORD DataT; + DWORD nSize; + DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize); + + if(state == DeviceState_Connect) { + DeviceItem_t* device = new DeviceItem_t(); + + AddItemToList(buf, device); + ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &device->deviceParams); + + currentDevice = &device->deviceParams; + isAdded = true; + } + else { + + ListResultItem_t* item = NULL; + if(IsItemAlreadyStored(buf)) { + DeviceItem_t* deviceItem = GetItemFromList(buf); + if(deviceItem) + { + item = CopyElement(&deviceItem->deviceParams); + } + RemoveItemFromList(deviceItem); + delete deviceItem; + } + + if(item == NULL) { + item = new ListResultItem_t(); + ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, item); + } + currentDevice = item; + isAdded = false; + } + + break; + } + } + + if (pspDevInfoData) { + HeapFree(GetProcessHeap(), 0, pspDevInfoData); + } + + if(hDevInfo) { + DllSetupDiDestroyDeviceInfoList(hDevInfo); + } + + SetEvent(deviceChangedRegisteredEvent); +} diff --git a/vendor/node-usb-native/src/deviceList.cpp b/vendor/node-usb-native/src/deviceList.cpp index 1cdcbdb1e..af9a96a0f 100644 --- a/vendor/node-usb-native/src/deviceList.cpp +++ b/vendor/node-usb-native/src/deviceList.cpp @@ -1,75 +1,75 @@ -#include -#include -#include - -#include "deviceList.h" - - -using namespace std; - -map deviceMap; - -void AddItemToList(char* key, DeviceItem_t * item) { - item->SetKey(key); - deviceMap.insert(pair(item->GetKey(), item)); -} - -void RemoveItemFromList(DeviceItem_t* item) { - deviceMap.erase(item->GetKey()); -} - -DeviceItem_t* GetItemFromList(char* key) { - map::iterator it; - - it = deviceMap.find(key); - if(it == deviceMap.end()) { - return NULL; - } - else { - return it->second; - } -} - -bool IsItemAlreadyStored(char* key) { - map::iterator it; - - it = deviceMap.find(key); - if(it == deviceMap.end()) { - return false; - } - else { - return true; - } - - return true; -} - -ListResultItem_t* CopyElement(ListResultItem_t* item) { - ListResultItem_t* dst = new ListResultItem_t(); - dst->locationId = item->locationId; - dst->vendorId = item->vendorId; - dst->productId = item->productId; - dst->deviceName = item->deviceName; - dst->manufacturer = item->manufacturer; - dst->serialNumber = item->serialNumber; - dst->deviceAddress = item->deviceAddress; - - return dst; -} - -void CreateFilteredList(list *filteredList, int vid, int pid) { - map::iterator it; - - for (it = deviceMap.begin(); it != deviceMap.end(); ++it) { - DeviceItem_t* item = it->second; - - if ( - (( vid != 0 && pid != 0) && (vid == item->deviceParams.vendorId && pid == item->deviceParams.productId)) - || ((vid != 0 && pid == 0) && vid == item->deviceParams.vendorId) - || (vid == 0 && pid == 0) - ) { - (*filteredList).push_back(CopyElement(&item->deviceParams)); - } - - } -} +#include +#include +#include + +#include "deviceList.h" + + +using namespace std; + +map deviceMap; + +void AddItemToList(char* key, DeviceItem_t * item) { + item->SetKey(key); + deviceMap.insert(pair(item->GetKey(), item)); +} + +void RemoveItemFromList(DeviceItem_t* item) { + deviceMap.erase(item->GetKey()); +} + +DeviceItem_t* GetItemFromList(char* key) { + map::iterator it; + + it = deviceMap.find(key); + if(it == deviceMap.end()) { + return NULL; + } + else { + return it->second; + } +} + +bool IsItemAlreadyStored(char* key) { + map::iterator it; + + it = deviceMap.find(key); + if(it == deviceMap.end()) { + return false; + } + else { + return true; + } + + return true; +} + +ListResultItem_t* CopyElement(ListResultItem_t* item) { + ListResultItem_t* dst = new ListResultItem_t(); + dst->locationId = item->locationId; + dst->vendorId = item->vendorId; + dst->productId = item->productId; + dst->deviceName = item->deviceName; + dst->manufacturer = item->manufacturer; + dst->serialNumber = item->serialNumber; + dst->deviceAddress = item->deviceAddress; + + return dst; +} + +void CreateFilteredList(list *filteredList, int vid, int pid) { + map::iterator it; + + for (it = deviceMap.begin(); it != deviceMap.end(); ++it) { + DeviceItem_t* item = it->second; + + if ( + (( vid != 0 && pid != 0) && (vid == item->deviceParams.vendorId && pid == item->deviceParams.productId)) + || ((vid != 0 && pid == 0) && vid == item->deviceParams.vendorId) + || (vid == 0 && pid == 0) + ) { + (*filteredList).push_back(CopyElement(&item->deviceParams)); + } + + } +} diff --git a/vendor/node-usb-native/src/deviceList.h b/vendor/node-usb-native/src/deviceList.h index 23d121bcf..6751e093c 100644 --- a/vendor/node-usb-native/src/deviceList.h +++ b/vendor/node-usb-native/src/deviceList.h @@ -1,63 +1,63 @@ -#ifndef _DEVICE_LIST_H -#define _DEVICE_LIST_H - -#include -#include - -typedef struct { - public: - int locationId; - int vendorId; - int productId; - std::string deviceName; - std::string manufacturer; - std::string serialNumber; - int deviceAddress; -} ListResultItem_t; - -typedef enum _DeviceState_t { - DeviceState_Connect, - DeviceState_Disconnect, -} DeviceState_t; - -typedef struct _DeviceItem_t { - ListResultItem_t deviceParams; - DeviceState_t deviceState; - - private: - char* key; - - - public: - _DeviceItem_t() { - key = NULL; - } - - ~_DeviceItem_t() { - if(this->key != NULL) { - delete this->key; - } - } - - void SetKey(char* key) { - if(this->key != NULL) { - delete this->key; - } - this->key = new char[strlen(key) + 1]; - memcpy(this->key, key, strlen(key) + 1); - } - - char* GetKey() { - return this->key; - } -} DeviceItem_t; - - -void AddItemToList(char* key, DeviceItem_t * item); -void RemoveItemFromList(DeviceItem_t* item); -bool IsItemAlreadyStored(char* identifier); -DeviceItem_t* GetItemFromList(char* key); -ListResultItem_t* CopyElement(ListResultItem_t* item); -void CreateFilteredList(std::list* filteredList, int vid, int pid); - -#endif +#ifndef _DEVICE_LIST_H +#define _DEVICE_LIST_H + +#include +#include + +typedef struct { + public: + int locationId; + int vendorId; + int productId; + std::string deviceName; + std::string manufacturer; + std::string serialNumber; + int deviceAddress; +} ListResultItem_t; + +typedef enum _DeviceState_t { + DeviceState_Connect, + DeviceState_Disconnect, +} DeviceState_t; + +typedef struct _DeviceItem_t { + ListResultItem_t deviceParams; + DeviceState_t deviceState; + + private: + char* key; + + + public: + _DeviceItem_t() { + key = NULL; + } + + ~_DeviceItem_t() { + if(this->key != NULL) { + delete this->key; + } + } + + void SetKey(char* key) { + if(this->key != NULL) { + delete this->key; + } + this->key = new char[strlen(key) + 1]; + memcpy(this->key, key, strlen(key) + 1); + } + + char* GetKey() { + return this->key; + } +} DeviceItem_t; + + +void AddItemToList(char* key, DeviceItem_t * item); +void RemoveItemFromList(DeviceItem_t* item); +bool IsItemAlreadyStored(char* identifier); +DeviceItem_t* GetItemFromList(char* key); +ListResultItem_t* CopyElement(ListResultItem_t* item); +void CreateFilteredList(std::list* filteredList, int vid, int pid); + +#endif diff --git a/vendor/node-usb-native/src/serialport.cpp b/vendor/node-usb-native/src/serialport.cpp index 755037b38..6e9027034 100644 --- a/vendor/node-usb-native/src/serialport.cpp +++ b/vendor/node-usb-native/src/serialport.cpp @@ -1,717 +1,717 @@ -#include "./serialport.h" - -#ifdef WIN32 -#define strncasecmp strnicmp -#else -#include "./serialport_poller.h" -#endif - -struct _WriteQueue { - const int _fd; // the fd that is associated with this write queue - QueuedWrite _write_queue; - uv_mutex_t _write_queue_mutex; - _WriteQueue *_next; - - _WriteQueue(const int fd) : _fd(fd), _write_queue(), _next(NULL) { - uv_mutex_init(&_write_queue_mutex); - } - - void lock() { uv_mutex_lock(&_write_queue_mutex); } - void unlock() { uv_mutex_unlock(&_write_queue_mutex); } - - QueuedWrite &get() { return _write_queue; } -}; - -static _WriteQueue *write_queues = NULL; - -static _WriteQueue *qForFD(const int fd) { - _WriteQueue *q = write_queues; - while (q != NULL) { - if (q->_fd == fd) { - return q; - } - q = q->_next; - } - return NULL; -} - -static _WriteQueue *newQForFD(const int fd) { - _WriteQueue *q = qForFD(fd); - - if (q == NULL) { - if (write_queues == NULL) { - write_queues = new _WriteQueue(fd); - return write_queues; - } else { - q = write_queues; - while (q->_next != NULL) { - q = q->_next; - } - q->_next = new _WriteQueue(fd); - return q->_next; - } - } - - return q; -} - -static void deleteQForFD(const int fd) { - if (write_queues == NULL) - return; - - _WriteQueue *q = write_queues; - if (write_queues->_fd == fd) { - write_queues = write_queues->_next; - delete q; - - return; - } - - while (q->_next != NULL) { - if (q->_next->_fd == fd) { - _WriteQueue *out_q = q->_next; - q->_next = q->_next->_next; - delete out_q; - - return; - } - q = q->_next; - } - - // It wasn't found... -} - -v8::Local getValueFromObject(v8::Local options, std::string key) { - v8::Local v8str = Nan::New(key).ToLocalChecked(); - return Nan::Get(options, v8str).ToLocalChecked(); -} - -int getIntFromObject(v8::Local options, std::string key) { - #if NODE_MAJOR_VERSION >= 10 - return getValueFromObject(options, key)->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - return getValueFromObject(options, key)->ToInt32()->Int32Value(); - #endif -} - -bool getBoolFromObject(v8::Local options, std::string key) { - return getValueFromObject(options, key)->ToBoolean()->BooleanValue(); -} - -v8::Local getStringFromObj(v8::Local options, std::string key) { - return getValueFromObject(options, key)->ToString(); -} - -double getDoubleFromObject(v8::Local options, std::string key) { - #if NODE_MAJOR_VERSION >= 10 - return getValueFromObject(options, key)->ToNumber(v8::Isolate::GetCurrent())->NumberValue(); - #else - return getValueFromObject(options, key)->ToNumber()->NumberValue(); - #endif -} - -NAN_METHOD(Open) { - // path - if (!info[0]->IsString()) { - Nan::ThrowTypeError("First argument must be a string"); - return; - } - v8::String::Utf8Value path(info[0]->ToString()); - - // options - if (!info[1]->IsObject()) { - Nan::ThrowTypeError("Second argument must be an object"); - return; - } - v8::Local options = info[1]->ToObject(); - - // callback - if (!info[2]->IsFunction()) { - Nan::ThrowTypeError("Third argument must be a function"); - return; - } - - OpenBaton* baton = new OpenBaton(); - memset(baton, 0, sizeof(OpenBaton)); - strcpy(baton->path, *path); - baton->baudRate = getIntFromObject(options, "baudRate"); - baton->dataBits = getIntFromObject(options, "dataBits"); - baton->bufferSize = getIntFromObject(options, "bufferSize"); - baton->parity = ToParityEnum(getStringFromObj(options, "parity")); - baton->stopBits = ToStopBitEnum(getDoubleFromObject(options, "stopBits")); - baton->rtscts = getBoolFromObject(options, "rtscts"); - baton->xon = getBoolFromObject(options, "xon"); - baton->xoff = getBoolFromObject(options, "xoff"); - baton->xany = getBoolFromObject(options, "xany"); - baton->hupcl = getBoolFromObject(options, "hupcl"); - baton->lock = getBoolFromObject(options, "lock"); - - v8::Local platformOptions = getValueFromObject(options, "platformOptions")->ToObject(); - baton->platformOptions = ParsePlatformOptions(platformOptions); - - baton->callback.Reset(info[2].As()); - baton->dataCallback = new Nan::Callback(getValueFromObject(options, "dataCallback").As()); - baton->disconnectedCallback = new Nan::Callback(getValueFromObject(options, "disconnectedCallback").As()); - baton->errorCallback = new Nan::Callback(getValueFromObject(options, "errorCallback").As()); - - uv_work_t* req = new uv_work_t(); - req->data = baton; - - uv_queue_work(uv_default_loop(), req, EIO_Open, (uv_after_work_cb)EIO_AfterOpen); - - return; -} - -void EIO_AfterOpen(uv_work_t* req) { - Nan::HandleScope scope; - - OpenBaton* data = static_cast(req->data); - - v8::Local argv[2]; - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - argv[1] = Nan::Undefined(); - // not needed because we're not calling AfterOpenSuccess - delete data->dataCallback; - delete data->errorCallback; - delete data->disconnectedCallback; - } else { - argv[0] = Nan::Null(); - argv[1] = Nan::New(data->result); - - int fd; - #if NODE_MAJOR_VERSION >= 10 - fd = argv[1]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - fd = argv[1]->ToInt32()->Int32Value(); - #endif - newQForFD(fd); - - AfterOpenSuccess(data->result, data->dataCallback, data->disconnectedCallback, data->errorCallback); - } - - data->callback.Call(2, argv); - - delete data->platformOptions; - delete data; - delete req; -} - -NAN_METHOD(Update) { - // file descriptor - if (!info[0]->IsInt32()) { - Nan::ThrowTypeError("First argument must be an int"); - return; - } - int fd; - #if NODE_MAJOR_VERSION >= 10 - fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - fd = info[0]->ToInt32()->Int32Value(); - #endif - - // options - if (!info[1]->IsObject()) { - Nan::ThrowTypeError("Second argument must be an object"); - return; - } - v8::Local options = info[1]->ToObject(); - - if (!Nan::Has(options, Nan::New("baudRate").ToLocalChecked()).FromMaybe(false)) { - Nan::ThrowTypeError("baudRate must be set on options object"); - return; - } - - // callback - if (!info[2]->IsFunction()) { - Nan::ThrowTypeError("Third argument must be a function"); - return; - } - - ConnectionOptionsBaton* baton = new ConnectionOptionsBaton(); - memset(baton, 0, sizeof(ConnectionOptionsBaton)); - - baton->fd = fd; - #if NODE_MAJOR_VERSION >= 10 - baton->baudRate = Nan::Get(options, Nan::New("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - baton->baudRate = Nan::Get(options, Nan::New("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); - #endif - baton->callback.Reset(info[2].As()); - - uv_work_t* req = new uv_work_t(); - req->data = baton; - - uv_queue_work(uv_default_loop(), req, EIO_Update, (uv_after_work_cb)EIO_AfterUpdate); - - return; -} - -void EIO_AfterUpdate(uv_work_t* req) { - Nan::HandleScope scope; - - ConnectionOptionsBaton* data = static_cast(req->data); - - v8::Local argv[1]; - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - } else { - argv[0] = Nan::Null(); - } - - data->callback.Call(1, argv); - - delete data; - delete req; -} - -NAN_METHOD(Write) { - // file descriptor - if (!info[0]->IsInt32()) { - Nan::ThrowTypeError("First argument must be an int"); - return; - } - int fd; - #if NODE_MAJOR_VERSION >= 10 - fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - fd = info[0]->ToInt32()->Int32Value(); - #endif - - // buffer - if (!info[1]->IsObject() || !node::Buffer::HasInstance(info[1])) { - Nan::ThrowTypeError("Second argument must be a buffer"); - return; - } - v8::Local buffer = info[1]->ToObject(); - char* bufferData = node::Buffer::Data(buffer); - size_t bufferLength = node::Buffer::Length(buffer); - - // callback - if (!info[2]->IsFunction()) { - Nan::ThrowTypeError("Third argument must be a function"); - return; - } - - WriteBaton* baton = new WriteBaton(); - memset(baton, 0, sizeof(WriteBaton)); - baton->fd = fd; - baton->buffer.Reset(buffer); - baton->bufferData = bufferData; - baton->bufferLength = bufferLength; - baton->offset = 0; - baton->callback.Reset(info[2].As()); - - QueuedWrite* queuedWrite = new QueuedWrite(); - memset(queuedWrite, 0, sizeof(QueuedWrite)); - queuedWrite->baton = baton; - queuedWrite->req.data = queuedWrite; - - _WriteQueue *q = qForFD(fd); - if (!q) { - Nan::ThrowTypeError("There's no write queue for that file descriptor (write)!"); - return; - } - - q->lock(); - QueuedWrite &write_queue = q->get(); - bool empty = write_queue.empty(); - - write_queue.insert_tail(queuedWrite); - - if (empty) { - uv_queue_work(uv_default_loop(), &queuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); - } - q->unlock(); - - return; -} - -void EIO_AfterWrite(uv_work_t* req) { - Nan::HandleScope scope; - - QueuedWrite* queuedWrite = static_cast(req->data); - WriteBaton* data = static_cast(queuedWrite->baton); - - v8::Local argv[1]; - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - } else { - argv[0] = Nan::Null(); - } - - if (data->offset < data->bufferLength && !data->errorString[0]) { - // We're not done with this baton, so throw it right back onto the queue. - // Don't re-push the write in the event loop if there was an error; because same error could occur again! - // TODO: Add a uv_poll here for unix... - // fprintf(stderr, "Write again...\n"); - uv_queue_work(uv_default_loop(), req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); - return; - } - - // throwing errors instead of returning them at this point is rude - int fd = data->fd; - _WriteQueue *q = qForFD(fd); - if (!q) { - Nan::ThrowTypeError("There's no write queue for that file descriptor (after write)!"); - return; - } - - q->lock(); - QueuedWrite &write_queue = q->get(); - - // remove this one from the list - queuedWrite->remove(); - - data->callback.Call(1, argv); - - // If there are any left, start a new thread to write the next one. - if (!write_queue.empty()) { - // Always pull the next work item from the head of the queue - QueuedWrite* nextQueuedWrite = write_queue.next; - uv_queue_work(uv_default_loop(), &nextQueuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); - } - q->unlock(); - - data->buffer.Reset(); - delete data; - delete queuedWrite; -} - -NAN_METHOD(Close) { - // file descriptor - if (!info[0]->IsInt32()) { - Nan::ThrowTypeError("First argument must be an int"); - return; - } - - // callback - if (!info[1]->IsFunction()) { - Nan::ThrowTypeError("Second argument must be a function"); - return; - } - - CloseBaton* baton = new CloseBaton(); - memset(baton, 0, sizeof(CloseBaton)); - #if NODE_MAJOR_VERSION >= 10 - baton->fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - baton->fd = info[0]->ToInt32()->Int32Value(); - #endif - - baton->callback.Reset(info[1].As()); - - uv_work_t* req = new uv_work_t(); - req->data = baton; - uv_queue_work(uv_default_loop(), req, EIO_Close, (uv_after_work_cb)EIO_AfterClose); - - return; -} - -void EIO_AfterClose(uv_work_t* req) { - Nan::HandleScope scope; - CloseBaton* data = static_cast(req->data); - - v8::Local argv[1]; - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - } else { - argv[0] = Nan::Null(); - - // We don't have an error, so clean up the write queue for that fd - _WriteQueue *q = qForFD(data->fd); - if (q) { - q->lock(); - QueuedWrite &write_queue = q->get(); - while (!write_queue.empty()) { - QueuedWrite *del_q = write_queue.next; - del_q->baton->buffer.Reset(); - del_q->remove(); - } - q->unlock(); - deleteQForFD(data->fd); - } - } - data->callback.Call(1, argv); - - delete data; - delete req; -} - -NAN_METHOD(List) { - // callback - if (!info[0]->IsFunction()) { - Nan::ThrowTypeError("First argument must be a function"); - return; - } - - ListBaton* baton = new ListBaton(); - strcpy(baton->errorString, ""); - baton->callback.Reset(info[0].As()); - - uv_work_t* req = new uv_work_t(); - req->data = baton; - uv_queue_work(uv_default_loop(), req, EIO_List, (uv_after_work_cb)EIO_AfterList); - - return; -} - -void setIfNotEmpty(v8::Local item, std::string key, const char *value) { - v8::Local v8key = Nan::New(key).ToLocalChecked(); - if (strlen(value) > 0) { - Nan::Set(item, v8key, Nan::New(value).ToLocalChecked()); - } else { - Nan::Set(item, v8key, Nan::Undefined()); - } - -} - -void EIO_AfterList(uv_work_t* req) { - Nan::HandleScope scope; - - ListBaton* data = static_cast(req->data); - - v8::Local argv[2]; - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - argv[1] = Nan::Undefined(); - } else { - v8::Local results = Nan::New(); - int i = 0; - for (std::list::iterator it = data->results.begin(); it != data->results.end(); ++it, i++) { - v8::Local item = Nan::New(); - - setIfNotEmpty(item, "comName", (*it)->comName.c_str()); - setIfNotEmpty(item, "manufacturer", (*it)->manufacturer.c_str()); - setIfNotEmpty(item, "serialNumber", (*it)->serialNumber.c_str()); - setIfNotEmpty(item, "pnpId", (*it)->pnpId.c_str()); - setIfNotEmpty(item, "locationId", (*it)->locationId.c_str()); - setIfNotEmpty(item, "vendorId", (*it)->vendorId.c_str()); - setIfNotEmpty(item, "productId", (*it)->productId.c_str()); - - Nan::Set(results, i, item); - } - argv[0] = Nan::Null(); - argv[1] = results; - } - data->callback.Call(2, argv); - - for (std::list::iterator it = data->results.begin(); it != data->results.end(); ++it) { - delete *it; - } - delete data; - delete req; -} - -NAN_METHOD(Flush) { - // file descriptor - if (!info[0]->IsInt32()) { - Nan::ThrowTypeError("First argument must be an int"); - return; - } - int fd; - #if NODE_MAJOR_VERSION >= 10 - fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - fd = info[0]->ToInt32()->Int32Value(); - #endif - - // callback - if (!info[1]->IsFunction()) { - Nan::ThrowTypeError("Second argument must be a function"); - return; - } - v8::Local callback = info[1].As(); - - FlushBaton* baton = new FlushBaton(); - memset(baton, 0, sizeof(FlushBaton)); - baton->fd = fd; - baton->callback.Reset(callback); - - uv_work_t* req = new uv_work_t(); - req->data = baton; - uv_queue_work(uv_default_loop(), req, EIO_Flush, (uv_after_work_cb)EIO_AfterFlush); - - return; -} - -void EIO_AfterFlush(uv_work_t* req) { - Nan::HandleScope scope; - - FlushBaton* data = static_cast(req->data); - - v8::Local argv[2]; - - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - argv[1] = Nan::Undefined(); - } else { - argv[0] = Nan::Undefined(); - argv[1] = Nan::New(data->result); - } - - data->callback.Call(2, argv); - - delete data; - delete req; -} - -NAN_METHOD(Set) { - // file descriptor - if (!info[0]->IsInt32()) { - Nan::ThrowTypeError("First argument must be an int"); - return; - } - int fd; - #if NODE_MAJOR_VERSION >= 10 - fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - fd = info[0]->ToInt32()->Int32Value(); - #endif - - // options - if (!info[1]->IsObject()) { - Nan::ThrowTypeError("Second argument must be an object"); - return; - } - v8::Local options = info[1]->ToObject(); - - // callback - if (!info[2]->IsFunction()) { - Nan::ThrowTypeError("Third argument must be a function"); - return; - } - v8::Local callback = info[2].As(); - - SetBaton* baton = new SetBaton(); - memset(baton, 0, sizeof(SetBaton)); - baton->fd = fd; - baton->callback.Reset(callback); - baton->brk = getBoolFromObject(options, "brk"); - baton->rts = getBoolFromObject(options, "rts"); - baton->cts = getBoolFromObject(options, "cts"); - baton->dtr = getBoolFromObject(options, "dtr"); - baton->dsr = getBoolFromObject(options, "dsr"); - - uv_work_t* req = new uv_work_t(); - req->data = baton; - uv_queue_work(uv_default_loop(), req, EIO_Set, (uv_after_work_cb)EIO_AfterSet); - - return; -} - -void EIO_AfterSet(uv_work_t* req) { - Nan::HandleScope scope; - - SetBaton* data = static_cast(req->data); - - v8::Local argv[1]; - - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - } else { - argv[0] = Nan::Null(); - } - data->callback.Call(1, argv); - - delete data; - delete req; -} - -NAN_METHOD(Drain) { - // file descriptor - if (!info[0]->IsInt32()) { - Nan::ThrowTypeError("First argument must be an int"); - return; - } - int fd; - #if NODE_MAJOR_VERSION >= 10 - fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - fd = info[0]->ToInt32()->Int32Value(); - #endif - - // callback - if (!info[1]->IsFunction()) { - Nan::ThrowTypeError("Second argument must be a function"); - return; - } - - DrainBaton* baton = new DrainBaton(); - memset(baton, 0, sizeof(DrainBaton)); - baton->fd = fd; - baton->callback.Reset(info[1].As()); - - uv_work_t* req = new uv_work_t(); - req->data = baton; - uv_queue_work(uv_default_loop(), req, EIO_Drain, (uv_after_work_cb)EIO_AfterDrain); - - return; -} - -void EIO_AfterDrain(uv_work_t* req) { - Nan::HandleScope scope; - - DrainBaton* data = static_cast(req->data); - - v8::Local argv[1]; - - if (data->errorString[0]) { - argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); - } else { - argv[0] = Nan::Null(); - } - data->callback.Call(1, argv); - - delete data; - delete req; -} - -SerialPortParity NAN_INLINE(ToParityEnum(const v8::Local& v8str)) { - Nan::HandleScope scope; - Nan::Utf8String str(v8str); - size_t count = strlen(*str); - SerialPortParity parity = SERIALPORT_PARITY_NONE; - if (!strncasecmp(*str, "none", count)) { - parity = SERIALPORT_PARITY_NONE; - } else if (!strncasecmp(*str, "even", count)) { - parity = SERIALPORT_PARITY_EVEN; - } else if (!strncasecmp(*str, "mark", count)) { - parity = SERIALPORT_PARITY_MARK; - } else if (!strncasecmp(*str, "odd", count)) { - parity = SERIALPORT_PARITY_ODD; - } else if (!strncasecmp(*str, "space", count)) { - parity = SERIALPORT_PARITY_SPACE; - } - return parity; -} - -SerialPortStopBits NAN_INLINE(ToStopBitEnum(double stopBits)) { - if (stopBits > 1.4 && stopBits < 1.6) { - return SERIALPORT_STOPBITS_ONE_FIVE; - } - if (stopBits == 2) { - return SERIALPORT_STOPBITS_TWO; - } - return SERIALPORT_STOPBITS_ONE; -} - -extern "C" { - void init_serialport(v8::Handle target) { - Nan::HandleScope scope; - Nan::SetMethod(target, "set", Set); - Nan::SetMethod(target, "open", Open); - Nan::SetMethod(target, "update", Update); - Nan::SetMethod(target, "write", Write); - Nan::SetMethod(target, "close", Close); - Nan::SetMethod(target, "list", List); - Nan::SetMethod(target, "flush", Flush); - Nan::SetMethod(target, "drain", Drain); - -#ifndef WIN32 - SerialportPoller::Init(target); -#endif - } -} - -//NODE_MODULE(serialport, init); +#include "./serialport.h" + +#ifdef WIN32 +#define strncasecmp strnicmp +#else +#include "./serialport_poller.h" +#endif + +struct _WriteQueue { + const int _fd; // the fd that is associated with this write queue + QueuedWrite _write_queue; + uv_mutex_t _write_queue_mutex; + _WriteQueue *_next; + + _WriteQueue(const int fd) : _fd(fd), _write_queue(), _next(NULL) { + uv_mutex_init(&_write_queue_mutex); + } + + void lock() { uv_mutex_lock(&_write_queue_mutex); } + void unlock() { uv_mutex_unlock(&_write_queue_mutex); } + + QueuedWrite &get() { return _write_queue; } +}; + +static _WriteQueue *write_queues = NULL; + +static _WriteQueue *qForFD(const int fd) { + _WriteQueue *q = write_queues; + while (q != NULL) { + if (q->_fd == fd) { + return q; + } + q = q->_next; + } + return NULL; +} + +static _WriteQueue *newQForFD(const int fd) { + _WriteQueue *q = qForFD(fd); + + if (q == NULL) { + if (write_queues == NULL) { + write_queues = new _WriteQueue(fd); + return write_queues; + } else { + q = write_queues; + while (q->_next != NULL) { + q = q->_next; + } + q->_next = new _WriteQueue(fd); + return q->_next; + } + } + + return q; +} + +static void deleteQForFD(const int fd) { + if (write_queues == NULL) + return; + + _WriteQueue *q = write_queues; + if (write_queues->_fd == fd) { + write_queues = write_queues->_next; + delete q; + + return; + } + + while (q->_next != NULL) { + if (q->_next->_fd == fd) { + _WriteQueue *out_q = q->_next; + q->_next = q->_next->_next; + delete out_q; + + return; + } + q = q->_next; + } + + // It wasn't found... +} + +v8::Local getValueFromObject(v8::Local options, std::string key) { + v8::Local v8str = Nan::New(key).ToLocalChecked(); + return Nan::Get(options, v8str).ToLocalChecked(); +} + +int getIntFromObject(v8::Local options, std::string key) { + #if NODE_MAJOR_VERSION >= 10 + return Nan::To(getValueFromObject(options, key)).ToLocalChecked()->Value(); + #else + return getValueFromObject(options, key)->ToInt32()->Int32Value(); + #endif +} + +bool getBoolFromObject(v8::Local options, std::string key) { + return getValueFromObject(options, key)->ToBoolean()->BooleanValue(); +} + +v8::Local getStringFromObj(v8::Local options, std::string key) { + return getValueFromObject(options, key)->ToString(); +} + +double getDoubleFromObject(v8::Local options, std::string key) { + #if NODE_MAJOR_VERSION >= 10 + return Nan::To(getValueFromObject(options, key)).FromMaybe(0); + #else + return getValueFromObject(options, key)->ToNumber()->NumberValue(); + #endif +} + +NAN_METHOD(Open) { + // path + if (!info[0]->IsString()) { + Nan::ThrowTypeError("First argument must be a string"); + return; + } + v8::String::Utf8Value path(info[0]->ToString()); + + // options + if (!info[1]->IsObject()) { + Nan::ThrowTypeError("Second argument must be an object"); + return; + } + v8::Local options = info[1]->ToObject(); + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + OpenBaton* baton = new OpenBaton(); + memset(baton, 0, sizeof(OpenBaton)); + strcpy(baton->path, *path); + baton->baudRate = getIntFromObject(options, "baudRate"); + baton->dataBits = getIntFromObject(options, "dataBits"); + baton->bufferSize = getIntFromObject(options, "bufferSize"); + baton->parity = ToParityEnum(getStringFromObj(options, "parity")); + baton->stopBits = ToStopBitEnum(getDoubleFromObject(options, "stopBits")); + baton->rtscts = getBoolFromObject(options, "rtscts"); + baton->xon = getBoolFromObject(options, "xon"); + baton->xoff = getBoolFromObject(options, "xoff"); + baton->xany = getBoolFromObject(options, "xany"); + baton->hupcl = getBoolFromObject(options, "hupcl"); + baton->lock = getBoolFromObject(options, "lock"); + + v8::Local platformOptions = getValueFromObject(options, "platformOptions")->ToObject(); + baton->platformOptions = ParsePlatformOptions(platformOptions); + + baton->callback.Reset(info[2].As()); + baton->dataCallback = new Nan::Callback(getValueFromObject(options, "dataCallback").As()); + baton->disconnectedCallback = new Nan::Callback(getValueFromObject(options, "disconnectedCallback").As()); + baton->errorCallback = new Nan::Callback(getValueFromObject(options, "errorCallback").As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + + uv_queue_work(uv_default_loop(), req, EIO_Open, (uv_after_work_cb)EIO_AfterOpen); + + return; +} + +void EIO_AfterOpen(uv_work_t* req) { + Nan::HandleScope scope; + + OpenBaton* data = static_cast(req->data); + + v8::Local argv[2]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + // not needed because we're not calling AfterOpenSuccess + delete data->dataCallback; + delete data->errorCallback; + delete data->disconnectedCallback; + } else { + argv[0] = Nan::Null(); + argv[1] = Nan::New(data->result); + + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = argv[1]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + fd = argv[1]->ToInt32()->Int32Value(); + #endif + newQForFD(fd); + + AfterOpenSuccess(data->result, data->dataCallback, data->disconnectedCallback, data->errorCallback); + } + + data->callback.Call(2, argv); + + delete data->platformOptions; + delete data; + delete req; +} + +NAN_METHOD(Update) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // options + if (!info[1]->IsObject()) { + Nan::ThrowTypeError("Second argument must be an object"); + return; + } + v8::Local options = info[1]->ToObject(); + + if (!Nan::Has(options, Nan::New("baudRate").ToLocalChecked()).FromMaybe(false)) { + Nan::ThrowTypeError("baudRate must be set on options object"); + return; + } + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + ConnectionOptionsBaton* baton = new ConnectionOptionsBaton(); + memset(baton, 0, sizeof(ConnectionOptionsBaton)); + + baton->fd = fd; + #if NODE_MAJOR_VERSION >= 10 + baton->baudRate = Nan::Get(options, Nan::New("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + baton->baudRate = Nan::Get(options, Nan::New("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); + #endif + baton->callback.Reset(info[2].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + + uv_queue_work(uv_default_loop(), req, EIO_Update, (uv_after_work_cb)EIO_AfterUpdate); + + return; +} + +void EIO_AfterUpdate(uv_work_t* req) { + Nan::HandleScope scope; + + ConnectionOptionsBaton* data = static_cast(req->data); + + v8::Local argv[1]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + + data->callback.Call(1, argv); + + delete data; + delete req; +} + +NAN_METHOD(Write) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // buffer + if (!info[1]->IsObject() || !node::Buffer::HasInstance(info[1])) { + Nan::ThrowTypeError("Second argument must be a buffer"); + return; + } + v8::Local buffer = info[1]->ToObject(); + char* bufferData = node::Buffer::Data(buffer); + size_t bufferLength = node::Buffer::Length(buffer); + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + WriteBaton* baton = new WriteBaton(); + memset(baton, 0, sizeof(WriteBaton)); + baton->fd = fd; + baton->buffer.Reset(buffer); + baton->bufferData = bufferData; + baton->bufferLength = bufferLength; + baton->offset = 0; + baton->callback.Reset(info[2].As()); + + QueuedWrite* queuedWrite = new QueuedWrite(); + memset(queuedWrite, 0, sizeof(QueuedWrite)); + queuedWrite->baton = baton; + queuedWrite->req.data = queuedWrite; + + _WriteQueue *q = qForFD(fd); + if (!q) { + Nan::ThrowTypeError("There's no write queue for that file descriptor (write)!"); + return; + } + + q->lock(); + QueuedWrite &write_queue = q->get(); + bool empty = write_queue.empty(); + + write_queue.insert_tail(queuedWrite); + + if (empty) { + uv_queue_work(uv_default_loop(), &queuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); + } + q->unlock(); + + return; +} + +void EIO_AfterWrite(uv_work_t* req) { + Nan::HandleScope scope; + + QueuedWrite* queuedWrite = static_cast(req->data); + WriteBaton* data = static_cast(queuedWrite->baton); + + v8::Local argv[1]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + + if (data->offset < data->bufferLength && !data->errorString[0]) { + // We're not done with this baton, so throw it right back onto the queue. + // Don't re-push the write in the event loop if there was an error; because same error could occur again! + // TODO: Add a uv_poll here for unix... + // fprintf(stderr, "Write again...\n"); + uv_queue_work(uv_default_loop(), req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); + return; + } + + // throwing errors instead of returning them at this point is rude + int fd = data->fd; + _WriteQueue *q = qForFD(fd); + if (!q) { + Nan::ThrowTypeError("There's no write queue for that file descriptor (after write)!"); + return; + } + + q->lock(); + QueuedWrite &write_queue = q->get(); + + // remove this one from the list + queuedWrite->remove(); + + data->callback.Call(1, argv); + + // If there are any left, start a new thread to write the next one. + if (!write_queue.empty()) { + // Always pull the next work item from the head of the queue + QueuedWrite* nextQueuedWrite = write_queue.next; + uv_queue_work(uv_default_loop(), &nextQueuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); + } + q->unlock(); + + data->buffer.Reset(); + delete data; + delete queuedWrite; +} + +NAN_METHOD(Close) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + + // callback + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Second argument must be a function"); + return; + } + + CloseBaton* baton = new CloseBaton(); + memset(baton, 0, sizeof(CloseBaton)); + #if NODE_MAJOR_VERSION >= 10 + baton->fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + baton->fd = info[0]->ToInt32()->Int32Value(); + #endif + + baton->callback.Reset(info[1].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Close, (uv_after_work_cb)EIO_AfterClose); + + return; +} + +void EIO_AfterClose(uv_work_t* req) { + Nan::HandleScope scope; + CloseBaton* data = static_cast(req->data); + + v8::Local argv[1]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + + // We don't have an error, so clean up the write queue for that fd + _WriteQueue *q = qForFD(data->fd); + if (q) { + q->lock(); + QueuedWrite &write_queue = q->get(); + while (!write_queue.empty()) { + QueuedWrite *del_q = write_queue.next; + del_q->baton->buffer.Reset(); + del_q->remove(); + } + q->unlock(); + deleteQForFD(data->fd); + } + } + data->callback.Call(1, argv); + + delete data; + delete req; +} + +NAN_METHOD(List) { + // callback + if (!info[0]->IsFunction()) { + Nan::ThrowTypeError("First argument must be a function"); + return; + } + + ListBaton* baton = new ListBaton(); + strcpy(baton->errorString, ""); + baton->callback.Reset(info[0].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_List, (uv_after_work_cb)EIO_AfterList); + + return; +} + +void setIfNotEmpty(v8::Local item, std::string key, const char *value) { + v8::Local v8key = Nan::New(key).ToLocalChecked(); + if (strlen(value) > 0) { + Nan::Set(item, v8key, Nan::New(value).ToLocalChecked()); + } else { + Nan::Set(item, v8key, Nan::Undefined()); + } + +} + +void EIO_AfterList(uv_work_t* req) { + Nan::HandleScope scope; + + ListBaton* data = static_cast(req->data); + + v8::Local argv[2]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + } else { + v8::Local results = Nan::New(); + int i = 0; + for (std::list::iterator it = data->results.begin(); it != data->results.end(); ++it, i++) { + v8::Local item = Nan::New(); + + setIfNotEmpty(item, "comName", (*it)->comName.c_str()); + setIfNotEmpty(item, "manufacturer", (*it)->manufacturer.c_str()); + setIfNotEmpty(item, "serialNumber", (*it)->serialNumber.c_str()); + setIfNotEmpty(item, "pnpId", (*it)->pnpId.c_str()); + setIfNotEmpty(item, "locationId", (*it)->locationId.c_str()); + setIfNotEmpty(item, "vendorId", (*it)->vendorId.c_str()); + setIfNotEmpty(item, "productId", (*it)->productId.c_str()); + + Nan::Set(results, i, item); + } + argv[0] = Nan::Null(); + argv[1] = results; + } + data->callback.Call(2, argv); + + for (std::list::iterator it = data->results.begin(); it != data->results.end(); ++it) { + delete *it; + } + delete data; + delete req; +} + +NAN_METHOD(Flush) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // callback + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Second argument must be a function"); + return; + } + v8::Local callback = info[1].As(); + + FlushBaton* baton = new FlushBaton(); + memset(baton, 0, sizeof(FlushBaton)); + baton->fd = fd; + baton->callback.Reset(callback); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Flush, (uv_after_work_cb)EIO_AfterFlush); + + return; +} + +void EIO_AfterFlush(uv_work_t* req) { + Nan::HandleScope scope; + + FlushBaton* data = static_cast(req->data); + + v8::Local argv[2]; + + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + } else { + argv[0] = Nan::Undefined(); + argv[1] = Nan::New(data->result); + } + + data->callback.Call(2, argv); + + delete data; + delete req; +} + +NAN_METHOD(Set) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // options + if (!info[1]->IsObject()) { + Nan::ThrowTypeError("Second argument must be an object"); + return; + } + v8::Local options = info[1]->ToObject(); + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + v8::Local callback = info[2].As(); + + SetBaton* baton = new SetBaton(); + memset(baton, 0, sizeof(SetBaton)); + baton->fd = fd; + baton->callback.Reset(callback); + baton->brk = getBoolFromObject(options, "brk"); + baton->rts = getBoolFromObject(options, "rts"); + baton->cts = getBoolFromObject(options, "cts"); + baton->dtr = getBoolFromObject(options, "dtr"); + baton->dsr = getBoolFromObject(options, "dsr"); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Set, (uv_after_work_cb)EIO_AfterSet); + + return; +} + +void EIO_AfterSet(uv_work_t* req) { + Nan::HandleScope scope; + + SetBaton* data = static_cast(req->data); + + v8::Local argv[1]; + + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + data->callback.Call(1, argv); + + delete data; + delete req; +} + +NAN_METHOD(Drain) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // callback + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Second argument must be a function"); + return; + } + + DrainBaton* baton = new DrainBaton(); + memset(baton, 0, sizeof(DrainBaton)); + baton->fd = fd; + baton->callback.Reset(info[1].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Drain, (uv_after_work_cb)EIO_AfterDrain); + + return; +} + +void EIO_AfterDrain(uv_work_t* req) { + Nan::HandleScope scope; + + DrainBaton* data = static_cast(req->data); + + v8::Local argv[1]; + + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + data->callback.Call(1, argv); + + delete data; + delete req; +} + +SerialPortParity NAN_INLINE(ToParityEnum(const v8::Local& v8str)) { + Nan::HandleScope scope; + Nan::Utf8String str(v8str); + size_t count = strlen(*str); + SerialPortParity parity = SERIALPORT_PARITY_NONE; + if (!strncasecmp(*str, "none", count)) { + parity = SERIALPORT_PARITY_NONE; + } else if (!strncasecmp(*str, "even", count)) { + parity = SERIALPORT_PARITY_EVEN; + } else if (!strncasecmp(*str, "mark", count)) { + parity = SERIALPORT_PARITY_MARK; + } else if (!strncasecmp(*str, "odd", count)) { + parity = SERIALPORT_PARITY_ODD; + } else if (!strncasecmp(*str, "space", count)) { + parity = SERIALPORT_PARITY_SPACE; + } + return parity; +} + +SerialPortStopBits NAN_INLINE(ToStopBitEnum(double stopBits)) { + if (stopBits > 1.4 && stopBits < 1.6) { + return SERIALPORT_STOPBITS_ONE_FIVE; + } + if (stopBits == 2) { + return SERIALPORT_STOPBITS_TWO; + } + return SERIALPORT_STOPBITS_ONE; +} + +extern "C" { + void init_serialport( v8::Local target) { + Nan::HandleScope scope; + Nan::SetMethod(target, "set", Set); + Nan::SetMethod(target, "open", Open); + Nan::SetMethod(target, "update", Update); + Nan::SetMethod(target, "write", Write); + Nan::SetMethod(target, "close", Close); + Nan::SetMethod(target, "list", List); + Nan::SetMethod(target, "flush", Flush); + Nan::SetMethod(target, "drain", Drain); + +#ifndef WIN32 + SerialportPoller::Init(target); +#endif + } +} + +//NODE_MODULE(serialport, init); diff --git a/vendor/node-usb-native/src/serialport.h b/vendor/node-usb-native/src/serialport.h index 392fc2017..883ef9b05 100644 --- a/vendor/node-usb-native/src/serialport.h +++ b/vendor/node-usb-native/src/serialport.h @@ -1,195 +1,195 @@ -#ifndef SRC_SERIALPORT_H_ -#define SRC_SERIALPORT_H_ - -#include -#include -#include -#include -#include -#include - -#define ERROR_STRING_SIZE 1024 - -NAN_METHOD(List); -void EIO_List(uv_work_t* req); -void EIO_AfterList(uv_work_t* req); - -NAN_METHOD(Open); -void EIO_Open(uv_work_t* req); -void EIO_AfterOpen(uv_work_t* req); -void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback); - -NAN_METHOD(Update); -void EIO_Update(uv_work_t* req); -void EIO_AfterUpdate(uv_work_t* req); - -NAN_METHOD(Write); -void EIO_Write(uv_work_t* req); -void EIO_AfterWrite(uv_work_t* req); - -NAN_METHOD(Close); -void EIO_Close(uv_work_t* req); -void EIO_AfterClose(uv_work_t* req); - -NAN_METHOD(Flush); -void EIO_Flush(uv_work_t* req); -void EIO_AfterFlush(uv_work_t* req); - -NAN_METHOD(Set); -void EIO_Set(uv_work_t* req); -void EIO_AfterSet(uv_work_t* req); - -NAN_METHOD(Drain); -void EIO_Drain(uv_work_t* req); -void EIO_AfterDrain(uv_work_t* req); - -enum SerialPortParity { - SERIALPORT_PARITY_NONE = 1, - SERIALPORT_PARITY_MARK = 2, - SERIALPORT_PARITY_EVEN = 3, - SERIALPORT_PARITY_ODD = 4, - SERIALPORT_PARITY_SPACE = 5 -}; - -enum SerialPortStopBits { - SERIALPORT_STOPBITS_ONE = 1, - SERIALPORT_STOPBITS_ONE_FIVE = 2, - SERIALPORT_STOPBITS_TWO = 3 -}; - -SerialPortParity ToParityEnum(const v8::Local& str); -SerialPortStopBits ToStopBitEnum(double stopBits); - -struct OpenBatonPlatformOptions { }; -OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options); - -struct OpenBaton { - char errorString[ERROR_STRING_SIZE]; - Nan::Callback callback; - char path[1024]; - int fd; - int result; - int baudRate; - int dataBits; - int bufferSize; - bool rtscts; - bool xon; - bool xoff; - bool xany; - bool dsrdtr; - bool hupcl; - bool lock; - Nan::Callback* dataCallback; - Nan::Callback* disconnectedCallback; - Nan::Callback* errorCallback; - SerialPortParity parity; - SerialPortStopBits stopBits; - OpenBatonPlatformOptions* platformOptions; -}; - -struct ConnectionOptionsBaton { - char errorString[ERROR_STRING_SIZE]; - Nan::Callback callback; - int fd; - int baudRate; -}; - -struct WriteBaton { - int fd; - char* bufferData; - size_t bufferLength; - size_t offset; - Nan::Persistent buffer; - Nan::Callback callback; - int result; - char errorString[ERROR_STRING_SIZE]; -}; - -struct QueuedWrite { - uv_work_t req; - QueuedWrite *prev; - QueuedWrite *next; - WriteBaton* baton; - - QueuedWrite() { - prev = this; - next = this; - - baton = 0; - } - - ~QueuedWrite() { - remove(); - } - - void remove() { - prev->next = next; - next->prev = prev; - - next = this; - prev = this; - } - - void insert_tail(QueuedWrite *qw) { - qw->next = this; - qw->prev = this->prev; - qw->prev->next = qw; - this->prev = qw; - } - - bool empty() { - return next == this; - } -}; - -struct CloseBaton { - int fd; - Nan::Callback callback; - char errorString[ERROR_STRING_SIZE]; -}; - -struct ListResultItem { - std::string comName; - std::string manufacturer; - std::string serialNumber; - std::string pnpId; - std::string locationId; - std::string vendorId; - std::string productId; -}; - -struct ListBaton { - Nan::Callback callback; - std::list results; - char errorString[ERROR_STRING_SIZE]; -}; - -struct FlushBaton { - int fd; - Nan::Callback callback; - int result; - char errorString[ERROR_STRING_SIZE]; -}; - -struct SetBaton { - int fd; - Nan::Callback callback; - int result; - char errorString[ERROR_STRING_SIZE]; - bool rts; - bool cts; - bool dtr; - bool dsr; - bool brk; -}; - -struct DrainBaton { - int fd; - Nan::Callback callback; - int result; - char errorString[ERROR_STRING_SIZE]; -}; - -int setup(int fd, OpenBaton *data); -int setBaudRate(ConnectionOptionsBaton *data); -#endif // SRC_SERIALPORT_H_ +#ifndef SRC_SERIALPORT_H_ +#define SRC_SERIALPORT_H_ + +#include +#include +#include +#include +#include +#include + +#define ERROR_STRING_SIZE 1024 + +NAN_METHOD(List); +void EIO_List(uv_work_t* req); +void EIO_AfterList(uv_work_t* req); + +NAN_METHOD(Open); +void EIO_Open(uv_work_t* req); +void EIO_AfterOpen(uv_work_t* req); +void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback); + +NAN_METHOD(Update); +void EIO_Update(uv_work_t* req); +void EIO_AfterUpdate(uv_work_t* req); + +NAN_METHOD(Write); +void EIO_Write(uv_work_t* req); +void EIO_AfterWrite(uv_work_t* req); + +NAN_METHOD(Close); +void EIO_Close(uv_work_t* req); +void EIO_AfterClose(uv_work_t* req); + +NAN_METHOD(Flush); +void EIO_Flush(uv_work_t* req); +void EIO_AfterFlush(uv_work_t* req); + +NAN_METHOD(Set); +void EIO_Set(uv_work_t* req); +void EIO_AfterSet(uv_work_t* req); + +NAN_METHOD(Drain); +void EIO_Drain(uv_work_t* req); +void EIO_AfterDrain(uv_work_t* req); + +enum SerialPortParity { + SERIALPORT_PARITY_NONE = 1, + SERIALPORT_PARITY_MARK = 2, + SERIALPORT_PARITY_EVEN = 3, + SERIALPORT_PARITY_ODD = 4, + SERIALPORT_PARITY_SPACE = 5 +}; + +enum SerialPortStopBits { + SERIALPORT_STOPBITS_ONE = 1, + SERIALPORT_STOPBITS_ONE_FIVE = 2, + SERIALPORT_STOPBITS_TWO = 3 +}; + +SerialPortParity ToParityEnum(const v8::Local& str); +SerialPortStopBits ToStopBitEnum(double stopBits); + +struct OpenBatonPlatformOptions { }; +OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options); + +struct OpenBaton { + char errorString[ERROR_STRING_SIZE]; + Nan::Callback callback; + char path[1024]; + int fd; + int result; + int baudRate; + int dataBits; + int bufferSize; + bool rtscts; + bool xon; + bool xoff; + bool xany; + bool dsrdtr; + bool hupcl; + bool lock; + Nan::Callback* dataCallback; + Nan::Callback* disconnectedCallback; + Nan::Callback* errorCallback; + SerialPortParity parity; + SerialPortStopBits stopBits; + OpenBatonPlatformOptions* platformOptions; +}; + +struct ConnectionOptionsBaton { + char errorString[ERROR_STRING_SIZE]; + Nan::Callback callback; + int fd; + int baudRate; +}; + +struct WriteBaton { + int fd; + char* bufferData; + size_t bufferLength; + size_t offset; + Nan::Persistent buffer; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; +}; + +struct QueuedWrite { + uv_work_t req; + QueuedWrite *prev; + QueuedWrite *next; + WriteBaton* baton; + + QueuedWrite() { + prev = this; + next = this; + + baton = 0; + } + + ~QueuedWrite() { + remove(); + } + + void remove() { + prev->next = next; + next->prev = prev; + + next = this; + prev = this; + } + + void insert_tail(QueuedWrite *qw) { + qw->next = this; + qw->prev = this->prev; + qw->prev->next = qw; + this->prev = qw; + } + + bool empty() { + return next == this; + } +}; + +struct CloseBaton { + int fd; + Nan::Callback callback; + char errorString[ERROR_STRING_SIZE]; +}; + +struct ListResultItem { + std::string comName; + std::string manufacturer; + std::string serialNumber; + std::string pnpId; + std::string locationId; + std::string vendorId; + std::string productId; +}; + +struct ListBaton { + Nan::Callback callback; + std::list results; + char errorString[ERROR_STRING_SIZE]; +}; + +struct FlushBaton { + int fd; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; +}; + +struct SetBaton { + int fd; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; + bool rts; + bool cts; + bool dtr; + bool dsr; + bool brk; +}; + +struct DrainBaton { + int fd; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; +}; + +int setup(int fd, OpenBaton *data); +int setBaudRate(ConnectionOptionsBaton *data); +#endif // SRC_SERIALPORT_H_ diff --git a/vendor/node-usb-native/src/serialport_poller.cpp b/vendor/node-usb-native/src/serialport_poller.cpp index 7471f2a38..b7db5af67 100644 --- a/vendor/node-usb-native/src/serialport_poller.cpp +++ b/vendor/node-usb-native/src/serialport_poller.cpp @@ -1,128 +1,128 @@ -// Copyright (C) 2013 Robert Giseburt -// serialport_poller.cpp Written as a part of https://github.com/voodootikigod/node-serialport -// License to use this is the same as that of node-serialport. - -#include -#include "./serialport_poller.h" - -using namespace v8; - -static Nan::Persistent serialportpoller_constructor; - -SerialportPoller::SerialportPoller() : Nan::ObjectWrap() {} -SerialportPoller::~SerialportPoller() { - // printf("~SerialportPoller\n"); - delete callback_; -} - -void _serialportReadable(uv_poll_t *req, int status, int events) { - SerialportPoller* sp = (SerialportPoller*) req->data; - // We can stop polling until we have read all of the data... - sp->_stop(); - sp->callCallback(status); -} - -void SerialportPoller::callCallback(int status) { - Nan::HandleScope scope; - // uv_work_t* req = new uv_work_t; - - // Call the callback to go read more data... - - v8::Local argv[1]; - if (status != 0) { - // error handling changed in libuv, see: - // https://github.com/joyent/libuv/commit/3ee4d3f183331 - #ifdef UV_ERRNO_H_ - const char* err_string = uv_strerror(status); - #else - uv_err_t errno = uv_last_error(uv_default_loop()); - const char* err_string = uv_strerror(errno); - #endif - snprintf(this->errorString, sizeof(this->errorString), "Error %s on polling", err_string); - argv[0] = v8::Exception::Error(Nan::New(this->errorString).ToLocalChecked()); - } else { - argv[0] = Nan::Undefined(); - } - - callback_->Call(1, argv); -} - - - -void SerialportPoller::Init(Handle target) { - Nan::HandleScope scope; - - // Prepare constructor template - Local tpl = Nan::New(New); - tpl->SetClassName(Nan::New("SerialportPoller").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - - // Prototype - - // SerialportPoller.close() - Nan::SetPrototypeMethod(tpl, "close", Close); - - // SerialportPoller.start() - Nan::SetPrototypeMethod(tpl, "start", Start); - - serialportpoller_constructor.Reset(tpl); - - Nan::Set(target, Nan::New("SerialportPoller").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); -} - -NAN_METHOD(SerialportPoller::New) { - if (!info[0]->IsInt32()) { - Nan::ThrowTypeError("First argument must be an fd"); - return; - } - - if (!info[1]->IsFunction()) { - Nan::ThrowTypeError("Third argument must be a function"); - return; - } - - SerialportPoller* obj = new SerialportPoller(); - #if NODE_MAJOR_VERSION >= 10 - obj->fd_ = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - obj->fd_ = info[0]->ToInt32()->Int32Value(); - #endif - obj->callback_ = new Nan::Callback(info[1].As()); - // obj->callCallback(); - - obj->Wrap(info.This()); - - obj->poll_handle_.data = obj; - - uv_poll_init(uv_default_loop(), &obj->poll_handle_, obj->fd_); - - uv_poll_start(&obj->poll_handle_, UV_READABLE, _serialportReadable); - - info.GetReturnValue().Set(info.This()); -} - -void SerialportPoller::_start() { - uv_poll_start(&poll_handle_, UV_READABLE, _serialportReadable); -} - -void SerialportPoller::_stop() { - uv_poll_stop(&poll_handle_); -} - - -NAN_METHOD(SerialportPoller::Start) { - SerialportPoller* obj = Nan::ObjectWrap::Unwrap(info.This()); - obj->_start(); - - return; -} - -NAN_METHOD(SerialportPoller::Close) { - SerialportPoller* obj = Nan::ObjectWrap::Unwrap(info.This()); - obj->_stop(); - - // DO SOMETHING! - - return; -} +// Copyright (C) 2013 Robert Giseburt +// serialport_poller.cpp Written as a part of https://github.com/voodootikigod/node-serialport +// License to use this is the same as that of node-serialport. + +#include +#include "./serialport_poller.h" + +using namespace v8; + +static Nan::Persistent serialportpoller_constructor; + +SerialportPoller::SerialportPoller() : Nan::ObjectWrap() {} +SerialportPoller::~SerialportPoller() { + // printf("~SerialportPoller\n"); + delete callback_; +} + +void _serialportReadable(uv_poll_t *req, int status, int events) { + SerialportPoller* sp = (SerialportPoller*) req->data; + // We can stop polling until we have read all of the data... + sp->_stop(); + sp->callCallback(status); +} + +void SerialportPoller::callCallback(int status) { + Nan::HandleScope scope; + // uv_work_t* req = new uv_work_t; + + // Call the callback to go read more data... + + v8::Local argv[1]; + if (status != 0) { + // error handling changed in libuv, see: + // https://github.com/joyent/libuv/commit/3ee4d3f183331 + #ifdef UV_ERRNO_H_ + const char* err_string = uv_strerror(status); + #else + uv_err_t errno = uv_last_error(uv_default_loop()); + const char* err_string = uv_strerror(errno); + #endif + snprintf(this->errorString, sizeof(this->errorString), "Error %s on polling", err_string); + argv[0] = v8::Exception::Error(Nan::New(this->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Undefined(); + } + + callback_->Call(1, argv); +} + + + +void SerialportPoller::Init(Local target) { + Nan::HandleScope scope; + + // Prepare constructor template + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("SerialportPoller").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + + // Prototype + + // SerialportPoller.close() + Nan::SetPrototypeMethod(tpl, "close", Close); + + // SerialportPoller.start() + Nan::SetPrototypeMethod(tpl, "start", Start); + + serialportpoller_constructor.Reset(tpl); + + Nan::Set(target, Nan::New("SerialportPoller").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); +} + +NAN_METHOD(SerialportPoller::New) { + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an fd"); + return; + } + + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + SerialportPoller* obj = new SerialportPoller(); + #if NODE_MAJOR_VERSION >= 10 + obj->fd_ = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + obj->fd_ = info[0]->ToInt32()->Int32Value(); + #endif + obj->callback_ = new Nan::Callback(info[1].As()); + // obj->callCallback(); + + obj->Wrap(info.This()); + + obj->poll_handle_.data = obj; + + uv_poll_init(uv_default_loop(), &obj->poll_handle_, obj->fd_); + + uv_poll_start(&obj->poll_handle_, UV_READABLE, _serialportReadable); + + info.GetReturnValue().Set(info.This()); +} + +void SerialportPoller::_start() { + uv_poll_start(&poll_handle_, UV_READABLE, _serialportReadable); +} + +void SerialportPoller::_stop() { + uv_poll_stop(&poll_handle_); +} + + +NAN_METHOD(SerialportPoller::Start) { + SerialportPoller* obj = Nan::ObjectWrap::Unwrap(info.This()); + obj->_start(); + + return; +} + +NAN_METHOD(SerialportPoller::Close) { + SerialportPoller* obj = Nan::ObjectWrap::Unwrap(info.This()); + obj->_stop(); + + // DO SOMETHING! + + return; +} diff --git a/vendor/node-usb-native/src/serialport_poller.h b/vendor/node-usb-native/src/serialport_poller.h index 4d20b07d6..1f4cc37c2 100644 --- a/vendor/node-usb-native/src/serialport_poller.h +++ b/vendor/node-usb-native/src/serialport_poller.h @@ -1,35 +1,35 @@ -// Copyright (C) 2013 Robert Giseburt -// serialport_poller.h Written as a part of https://github.com/voodootikigod/node-serialport -// License to use this is the same as that of node-serialport. - -#ifndef SERIALPORT_POLLER_H -#define SERIALPORT_POLLER_H - -#include -#include "./serialport.h" - -class SerialportPoller : public Nan::ObjectWrap { - public: - static void Init(v8::Handle target); - - void callCallback(int status); - - void _start(); - void _stop(); - - private: - SerialportPoller(); - ~SerialportPoller(); - - static NAN_METHOD(New); - static NAN_METHOD(Close); - static NAN_METHOD(Start); - - uv_poll_t poll_handle_; - int fd_; - char errorString[ERROR_STRING_SIZE]; - - Nan::Callback* callback_; -}; - -#endif +// Copyright (C) 2013 Robert Giseburt +// serialport_poller.h Written as a part of https://github.com/voodootikigod/node-serialport +// License to use this is the same as that of node-serialport. + +#ifndef SERIALPORT_POLLER_H +#define SERIALPORT_POLLER_H + +#include +#include "./serialport.h" + +class SerialportPoller : public Nan::ObjectWrap { + public: + static void Init( v8::Local target); + + void callCallback(int status); + + void _start(); + void _stop(); + + private: + SerialportPoller(); + ~SerialportPoller(); + + static NAN_METHOD(New); + static NAN_METHOD(Close); + static NAN_METHOD(Start); + + uv_poll_t poll_handle_; + int fd_; + char errorString[ERROR_STRING_SIZE]; + + Nan::Callback* callback_; +}; + +#endif diff --git a/vendor/node-usb-native/src/serialport_unix.cpp b/vendor/node-usb-native/src/serialport_unix.cpp index cd27b40c6..41cbe7710 100644 --- a/vendor/node-usb-native/src/serialport_unix.cpp +++ b/vendor/node-usb-native/src/serialport_unix.cpp @@ -1,740 +1,740 @@ -#include "./serialport.h" -#include "./serialport_poller.h" -#include -#include -#include -#include -#include - -#ifdef __APPLE__ -#include -#include -#include -#include -#include -#include - -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 -#include -#endif - -#if defined(__OpenBSD__) -#include -#endif - -#if defined(__linux__) -#include -#include -#endif - -struct UnixPlatformOptions : OpenBatonPlatformOptions { - uint8_t vmin; - uint8_t vtime; -}; - -OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options) { - Nan::HandleScope scope; - - UnixPlatformOptions* result = new UnixPlatformOptions(); - #if NODE_MAJOR_VERSION >= 10 - result->vmin = Nan::Get(options, Nan::New("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - result->vtime = Nan::Get(options, Nan::New("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); - #else - result->vmin = Nan::Get(options, Nan::New("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); - result->vtime = Nan::Get(options, Nan::New("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); - #endif - - return result; -} - -int ToBaudConstant(int baudRate); -int ToDataBitsConstant(int dataBits); -int ToStopBitsConstant(SerialPortStopBits stopBits); - -void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) { - delete dataCallback; - delete errorCallback; - delete disconnectedCallback; -} - -int ToBaudConstant(int baudRate) { - switch (baudRate) { - case 0: return B0; - case 50: return B50; - case 75: return B75; - case 110: return B110; - case 134: return B134; - case 150: return B150; - case 200: return B200; - case 300: return B300; - case 600: return B600; - case 1200: return B1200; - case 1800: return B1800; - case 2400: return B2400; - case 4800: return B4800; - case 9600: return B9600; - case 19200: return B19200; - case 38400: return B38400; - case 57600: return B57600; - case 115200: return B115200; - case 230400: return B230400; -#if defined(__linux__) - case 460800: return B460800; - case 500000: return B500000; - case 576000: return B576000; - case 921600: return B921600; - case 1000000: return B1000000; - case 1152000: return B1152000; - case 1500000: return B1500000; - case 2000000: return B2000000; - case 2500000: return B2500000; - case 3000000: return B3000000; - case 3500000: return B3500000; - case 4000000: return B4000000; -#endif - } - return -1; -} - -#ifdef __APPLE__ -typedef struct SerialDevice { - char port[MAXPATHLEN]; - char locationId[MAXPATHLEN]; - char vendorId[MAXPATHLEN]; - char productId[MAXPATHLEN]; - char manufacturer[MAXPATHLEN]; - char serialNumber[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; - case 7: return CS7; - case 6: return CS6; - case 5: return CS5; - } - return -1; -} - -void EIO_Open(uv_work_t* req) { - OpenBaton* data = static_cast(req->data); - - int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC); - int fd = open(data->path, flags); - - if (-1 == fd) { - snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot open %s", strerror(errno), data->path); - return; - } - - if (-1 == setup(fd, data)) { - close(fd); - return; - } - - data->result = fd; -} - -int setBaudRate(ConnectionOptionsBaton *data) { - // lookup the standard baudrates from the table - int baudRate = ToBaudConstant(data->baudRate); - int fd = data->fd; - - // get port options - struct termios options; - if (tcgetattr(fd, &options)) { - snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno)); - return -1; - } - - // If there is a custom baud rate on linux you can do the following trick with B38400 - #if defined(__linux__) && defined(ASYNC_SPD_CUST) - if (baudRate == -1) { - struct serial_struct serinfo; - serinfo.reserved_char[0] = 0; - if (-1 != ioctl(fd, TIOCGSERIAL, &serinfo)) { - serinfo.flags &= ~ASYNC_SPD_MASK; - serinfo.flags |= ASYNC_SPD_CUST; - serinfo.custom_divisor = (serinfo.baud_base + (data->baudRate / 2)) / data->baudRate; - if (serinfo.custom_divisor < 1) - serinfo.custom_divisor = 1; - - ioctl(fd, TIOCSSERIAL, &serinfo); - ioctl(fd, TIOCGSERIAL, &serinfo); - } else { - snprintf(data->errorString, sizeof(data->errorString), "Error: %s setting custom baud rate of %d", strerror(errno), data->baudRate); - return -1; - } - - // Now we use "B38400" to trigger the special baud rate. - baudRate = B38400; - } - #endif - - // On OS X, starting with Tiger, we can set a custom baud rate with ioctl - #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) - if (-1 == baudRate) { - speed_t speed = data->baudRate; - if (-1 == ioctl(fd, IOSSIOSPEED, &speed)) { - snprintf(data->errorString, sizeof(data->errorString), "Error: %s calling ioctl(.., IOSSIOSPEED, %ld )", strerror(errno), speed ); - return -1; - } else { - return 1; - } - } - #endif - - // If we have a good baud rate set it and lets go - if (-1 != baudRate) { - cfsetospeed(&options, baudRate); - cfsetispeed(&options, baudRate); - tcflush(fd, TCIFLUSH); - tcsetattr(fd, TCSANOW, &options); - return 1; - } - - snprintf(data->errorString, sizeof(data->errorString), "Error baud rate of %d is not supported on your platform", data->baudRate); - return -1; -} - -void EIO_Update(uv_work_t* req) { - ConnectionOptionsBaton* data = static_cast(req->data); - setBaudRate(data); -} - -int setup(int fd, OpenBaton *data) { - UnixPlatformOptions* platformOptions = static_cast(data->platformOptions); - - int dataBits = ToDataBitsConstant(data->dataBits); - if (-1 == dataBits) { - snprintf(data->errorString, sizeof(data->errorString), "Invalid data bits setting %d", data->dataBits); - return -1; - } - - // Snow Leopard doesn't have O_CLOEXEC - if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC)) { - snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot open %s", strerror(errno), data->path); - return -1; - } - - // Copy the connection options into the ConnectionOptionsBaton to set the baud rate - ConnectionOptionsBaton* connectionOptions = new ConnectionOptionsBaton(); - memset(connectionOptions, 0, sizeof(ConnectionOptionsBaton)); - connectionOptions->fd = fd; - connectionOptions->baudRate = data->baudRate; - - if (-1 == setBaudRate(connectionOptions)) { - strncpy(data->errorString, connectionOptions->errorString, sizeof(data->errorString)); - delete(connectionOptions); - return -1; - } - delete(connectionOptions); - - // Get port configuration for modification - struct termios options; - if (tcgetattr(fd, &options)) { - snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno)); - return -1; - } - - // IGNPAR: ignore bytes with parity errors - options.c_iflag = IGNPAR; - - // ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input) - // Future potential option - // options.c_iflag = ICRNL; - // otherwise make device raw (no other input processing) - - // Specify data bits - options.c_cflag &= ~CSIZE; - options.c_cflag |= dataBits; - - options.c_cflag &= ~(CRTSCTS); - - if (data->rtscts) { - options.c_cflag |= CRTSCTS; - // evaluate specific flow control options - } - - options.c_iflag &= ~(IXON | IXOFF | IXANY); - - if (data->xon) { - options.c_iflag |= IXON; - } - - if (data->xoff) { - options.c_iflag |= IXOFF; - } - - if (data->xany) { - options.c_iflag |= IXANY; - } - - switch (data->parity) { - case SERIALPORT_PARITY_NONE: - options.c_cflag &= ~PARENB; - // options.c_cflag &= ~CSTOPB; - // options.c_cflag &= ~CSIZE; - // options.c_cflag |= CS8; - break; - case SERIALPORT_PARITY_ODD: - options.c_cflag |= PARENB; - options.c_cflag |= PARODD; - // options.c_cflag &= ~CSTOPB; - // options.c_cflag &= ~CSIZE; - // options.c_cflag |= CS7; - break; - case SERIALPORT_PARITY_EVEN: - options.c_cflag |= PARENB; - options.c_cflag &= ~PARODD; - // options.c_cflag &= ~CSTOPB; - // options.c_cflag &= ~CSIZE; - // options.c_cflag |= CS7; - break; - default: - snprintf(data->errorString, sizeof(data->errorString), "Invalid parity setting %d", data->parity); - return -1; - } - - switch (data->stopBits) { - case SERIALPORT_STOPBITS_ONE: - options.c_cflag &= ~CSTOPB; - break; - case SERIALPORT_STOPBITS_TWO: - options.c_cflag |= CSTOPB; - break; - default: - snprintf(data->errorString, sizeof(data->errorString), "Invalid stop bits setting %d", data->stopBits); - return -1; - } - - options.c_cflag |= CLOCAL; // ignore status lines - options.c_cflag |= CREAD; // enable receiver - if (data->hupcl) { - options.c_cflag |= HUPCL; // drop DTR (i.e. hangup) on close - } - - // Raw output - options.c_oflag = 0; - - // ICANON makes partial lines not readable. It should be optional. - // It works with ICRNL. - options.c_lflag = 0; // ICANON; - - options.c_cc[VMIN]= platformOptions->vmin; - options.c_cc[VTIME]= platformOptions->vtime; - - // why? - tcflush(fd, TCIFLUSH); - - // check for error? - tcsetattr(fd, TCSANOW, &options); - - if (data->lock){ - if (-1 == flock(fd, LOCK_EX | LOCK_NB)) { - snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot lock port", strerror(errno)); - return -1; - } - } - - return 1; -} - -void EIO_Write(uv_work_t* req) { - QueuedWrite* queuedWrite = static_cast(req->data); - WriteBaton* data = static_cast(queuedWrite->baton); - int bytesWritten = 0; - - do { - errno = 0; // probably don't need this - bytesWritten = write(data->fd, data->bufferData + data->offset, data->bufferLength - data->offset); - if (-1 != bytesWritten) { - // there wasn't an error, do the math on what we actually wrote and keep writing until finished - data->offset += bytesWritten; - continue; - } - - // The write call was interrupted before anything was written, try again immediately. - if (errno == EINTR) { - // why try again right away instead of in another event loop? - continue; - } - - // Try again in another event loop - if (errno == EAGAIN || errno == EWOULDBLOCK){ - return; - } - - // EBAD would mean we're "disconnected" - - // a real error so lets bail - snprintf(data->errorString, sizeof(data->errorString), "Error: %s, calling write", strerror(errno)); - return; - } while (data->bufferLength > data->offset); -} - -void EIO_Close(uv_work_t* req) { - CloseBaton* data = static_cast(req->data); - if (-1 == close(data->fd)) { - snprintf(data->errorString, sizeof(data->errorString), "Error: %s, unable to close fd %d", strerror(errno), data->fd); - } -} - -#ifdef __APPLE__ - -// Function prototypes -static kern_return_t FindModems(io_iterator_t *matchingServices); -static io_service_t GetUsbDevice(io_service_t service); -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_service_t GetUsbDevice(io_service_t service) { - IOReturn status; - io_iterator_t iterator = 0; - io_service_t device = 0; - - if (!service) { - return device; - } - - status = IORegistryEntryCreateIterator(service, - kIOServicePlane, - (kIORegistryIterateParents | kIORegistryIterateRecursively), - &iterator); - - if (status == kIOReturnSuccess) { - io_service_t currentService; - while ((currentService = IOIteratorNext(iterator)) && device == 0) { - io_name_t serviceName; - status = IORegistryEntryGetNameInPlane(currentService, kIOServicePlane, serviceName); - if (status == kIOReturnSuccess && IOObjectConformsTo(currentService, kIOUSBDeviceClassName)) { - device = currentService; - } else { - // Release the service object which is no longer needed - (void) IOObjectRelease(currentService); - } - } - - // Release the iterator - (void) IOObjectRelease(iterator); - } - - return device; -} - -static void ExtractUsbInformation(stSerialDevice *serialDevice, IOUSBDeviceInterface **deviceInterface) { - kern_return_t kernResult; - 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; - io_iterator_t serialPortIterator; - char bsdPath[MAXPATHLEN]; - - 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)); - serialDevice->manufacturer[0] = '\0'; - serialDevice->serialNumber[0] = '\0'; - 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_service_t device = GetUsbDevice(modemService); - - if (device) { - CFStringRef manufacturerAsCFString = (CFStringRef) IORegistryEntryCreateCFProperty(device, - CFSTR(kUSBVendorString), - kCFAllocatorDefault, - 0); - - if (manufacturerAsCFString) { - Boolean result; - char manufacturer[MAXPATHLEN]; - - // Convert from a CFString to a C (NUL-terminated) - result = CFStringGetCString(manufacturerAsCFString, - manufacturer, - sizeof(manufacturer), - kCFStringEncodingUTF8); - - if (result) { - strcpy(serialDevice->manufacturer, manufacturer); - } - - CFRelease(manufacturerAsCFString); - } - - CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(device, - kIOServicePlane, - CFSTR(kUSBSerialNumberString), - kCFAllocatorDefault, - kIORegistryIterateRecursively); - - if (serialNumberAsCFString) { - Boolean result; - char serialNumber[MAXPATHLEN]; - - // Convert from a CFString to a C (NUL-terminated) - result = CFStringGetCString(serialNumberAsCFString, - serialNumber, - sizeof(serialNumber), - kCFStringEncodingUTF8); - - if (result) { - strcpy(serialDevice->serialNumber, serialNumber); - } - - CFRelease(serialNumberAsCFString); - } - - 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) { - ListBaton* data = static_cast(req->data); - -#ifndef __APPLE__ - // This code exists in javascript for other unix platforms - snprintf(data->errorString, sizeof(data->errorString), "List is not Implemented"); - return; -# else - if (!lockInitialised) { - uv_mutex_init(&list_mutex); - lockInitialised = TRUE; - } - - 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) { - resultItem->locationId = device.locationId; - } - if (*device.vendorId) { - resultItem->vendorId = device.vendorId; - } - if (*device.productId) { - resultItem->productId = device.productId; - } - if (*device.manufacturer) { - resultItem->manufacturer = device.manufacturer; - } - if (*device.serialNumber) { - resultItem->serialNumber = device.serialNumber; - } - 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) { - FlushBaton* data = static_cast(req->data); - - data->result = tcflush(data->fd, TCIFLUSH); -} - -void EIO_Set(uv_work_t* req) { - SetBaton* data = static_cast(req->data); - - int bits; - ioctl(data->fd, TIOCMGET, &bits); - - bits &= ~(TIOCM_RTS | TIOCM_CTS | TIOCM_DTR | TIOCM_DSR); - - if (data->rts) { - bits |= TIOCM_RTS; - } - - if (data->cts) { - bits |= TIOCM_CTS; - } - - if (data->dtr) { - bits |= TIOCM_DTR; - } - - if (data->dsr) { - bits |= TIOCM_DSR; - } - - int result = 0; - if (data->brk) { - result = ioctl(data->fd, TIOCSBRK, NULL); - } else { - result = ioctl(data->fd, TIOCCBRK, NULL); - } - - if (-1 == result) { - snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); - return; - } - - if (-1 == ioctl(data->fd, TIOCMSET, &bits)) { - snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); - return; - } -} - -void EIO_Drain(uv_work_t* req) { - DrainBaton* data = static_cast(req->data); - - if (-1 == tcdrain(data->fd)) { - snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); - return; - } -} +#include "./serialport.h" +#include "./serialport_poller.h" +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#include + +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 +#include +#endif + +#if defined(__OpenBSD__) +#include +#endif + +#if defined(__linux__) +#include +#include +#endif + +struct UnixPlatformOptions : OpenBatonPlatformOptions { + uint8_t vmin; + uint8_t vtime; +}; + +OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options) { + Nan::HandleScope scope; + + UnixPlatformOptions* result = new UnixPlatformOptions(); + #if NODE_MAJOR_VERSION >= 10 + result->vmin = Nan::Get(options, Nan::New("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + result->vtime = Nan::Get(options, Nan::New("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(); + #else + result->vmin = Nan::Get(options, Nan::New("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); + result->vtime = Nan::Get(options, Nan::New("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); + #endif + + return result; +} + +int ToBaudConstant(int baudRate); +int ToDataBitsConstant(int dataBits); +int ToStopBitsConstant(SerialPortStopBits stopBits); + +void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) { + delete dataCallback; + delete errorCallback; + delete disconnectedCallback; +} + +int ToBaudConstant(int baudRate) { + switch (baudRate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; +#if defined(__linux__) + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; +#endif + } + return -1; +} + +#ifdef __APPLE__ +typedef struct SerialDevice { + char port[MAXPATHLEN]; + char locationId[MAXPATHLEN]; + char vendorId[MAXPATHLEN]; + char productId[MAXPATHLEN]; + char manufacturer[MAXPATHLEN]; + char serialNumber[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; + case 7: return CS7; + case 6: return CS6; + case 5: return CS5; + } + return -1; +} + +void EIO_Open(uv_work_t* req) { + OpenBaton* data = static_cast(req->data); + + int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC); + int fd = open(data->path, flags); + + if (-1 == fd) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot open %s", strerror(errno), data->path); + return; + } + + if (-1 == setup(fd, data)) { + close(fd); + return; + } + + data->result = fd; +} + +int setBaudRate(ConnectionOptionsBaton *data) { + // lookup the standard baudrates from the table + int baudRate = ToBaudConstant(data->baudRate); + int fd = data->fd; + + // get port options + struct termios options; + if (tcgetattr(fd, &options)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno)); + return -1; + } + + // If there is a custom baud rate on linux you can do the following trick with B38400 + #if defined(__linux__) && defined(ASYNC_SPD_CUST) + if (baudRate == -1) { + struct serial_struct serinfo; + serinfo.reserved_char[0] = 0; + if (-1 != ioctl(fd, TIOCGSERIAL, &serinfo)) { + serinfo.flags &= ~ASYNC_SPD_MASK; + serinfo.flags |= ASYNC_SPD_CUST; + serinfo.custom_divisor = (serinfo.baud_base + (data->baudRate / 2)) / data->baudRate; + if (serinfo.custom_divisor < 1) + serinfo.custom_divisor = 1; + + ioctl(fd, TIOCSSERIAL, &serinfo); + ioctl(fd, TIOCGSERIAL, &serinfo); + } else { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s setting custom baud rate of %d", strerror(errno), data->baudRate); + return -1; + } + + // Now we use "B38400" to trigger the special baud rate. + baudRate = B38400; + } + #endif + + // On OS X, starting with Tiger, we can set a custom baud rate with ioctl + #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) + if (-1 == baudRate) { + speed_t speed = data->baudRate; + if (-1 == ioctl(fd, IOSSIOSPEED, &speed)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s calling ioctl(.., IOSSIOSPEED, %ld )", strerror(errno), speed ); + return -1; + } else { + return 1; + } + } + #endif + + // If we have a good baud rate set it and lets go + if (-1 != baudRate) { + cfsetospeed(&options, baudRate); + cfsetispeed(&options, baudRate); + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &options); + return 1; + } + + snprintf(data->errorString, sizeof(data->errorString), "Error baud rate of %d is not supported on your platform", data->baudRate); + return -1; +} + +void EIO_Update(uv_work_t* req) { + ConnectionOptionsBaton* data = static_cast(req->data); + setBaudRate(data); +} + +int setup(int fd, OpenBaton *data) { + UnixPlatformOptions* platformOptions = static_cast(data->platformOptions); + + int dataBits = ToDataBitsConstant(data->dataBits); + if (-1 == dataBits) { + snprintf(data->errorString, sizeof(data->errorString), "Invalid data bits setting %d", data->dataBits); + return -1; + } + + // Snow Leopard doesn't have O_CLOEXEC + if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC)) { + snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot open %s", strerror(errno), data->path); + return -1; + } + + // Copy the connection options into the ConnectionOptionsBaton to set the baud rate + ConnectionOptionsBaton* connectionOptions = new ConnectionOptionsBaton(); + memset(connectionOptions, 0, sizeof(ConnectionOptionsBaton)); + connectionOptions->fd = fd; + connectionOptions->baudRate = data->baudRate; + + if (-1 == setBaudRate(connectionOptions)) { + strncpy(data->errorString, connectionOptions->errorString, sizeof(data->errorString)); + delete(connectionOptions); + return -1; + } + delete(connectionOptions); + + // Get port configuration for modification + struct termios options; + if (tcgetattr(fd, &options)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno)); + return -1; + } + + // IGNPAR: ignore bytes with parity errors + options.c_iflag = IGNPAR; + + // ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input) + // Future potential option + // options.c_iflag = ICRNL; + // otherwise make device raw (no other input processing) + + // Specify data bits + options.c_cflag &= ~CSIZE; + options.c_cflag |= dataBits; + + options.c_cflag &= ~(CRTSCTS); + + if (data->rtscts) { + options.c_cflag |= CRTSCTS; + // evaluate specific flow control options + } + + options.c_iflag &= ~(IXON | IXOFF | IXANY); + + if (data->xon) { + options.c_iflag |= IXON; + } + + if (data->xoff) { + options.c_iflag |= IXOFF; + } + + if (data->xany) { + options.c_iflag |= IXANY; + } + + switch (data->parity) { + case SERIALPORT_PARITY_NONE: + options.c_cflag &= ~PARENB; + // options.c_cflag &= ~CSTOPB; + // options.c_cflag &= ~CSIZE; + // options.c_cflag |= CS8; + break; + case SERIALPORT_PARITY_ODD: + options.c_cflag |= PARENB; + options.c_cflag |= PARODD; + // options.c_cflag &= ~CSTOPB; + // options.c_cflag &= ~CSIZE; + // options.c_cflag |= CS7; + break; + case SERIALPORT_PARITY_EVEN: + options.c_cflag |= PARENB; + options.c_cflag &= ~PARODD; + // options.c_cflag &= ~CSTOPB; + // options.c_cflag &= ~CSIZE; + // options.c_cflag |= CS7; + break; + default: + snprintf(data->errorString, sizeof(data->errorString), "Invalid parity setting %d", data->parity); + return -1; + } + + switch (data->stopBits) { + case SERIALPORT_STOPBITS_ONE: + options.c_cflag &= ~CSTOPB; + break; + case SERIALPORT_STOPBITS_TWO: + options.c_cflag |= CSTOPB; + break; + default: + snprintf(data->errorString, sizeof(data->errorString), "Invalid stop bits setting %d", data->stopBits); + return -1; + } + + options.c_cflag |= CLOCAL; // ignore status lines + options.c_cflag |= CREAD; // enable receiver + if (data->hupcl) { + options.c_cflag |= HUPCL; // drop DTR (i.e. hangup) on close + } + + // Raw output + options.c_oflag = 0; + + // ICANON makes partial lines not readable. It should be optional. + // It works with ICRNL. + options.c_lflag = 0; // ICANON; + + options.c_cc[VMIN]= platformOptions->vmin; + options.c_cc[VTIME]= platformOptions->vtime; + + // why? + tcflush(fd, TCIFLUSH); + + // check for error? + tcsetattr(fd, TCSANOW, &options); + + if (data->lock){ + if (-1 == flock(fd, LOCK_EX | LOCK_NB)) { + snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot lock port", strerror(errno)); + return -1; + } + } + + return 1; +} + +void EIO_Write(uv_work_t* req) { + QueuedWrite* queuedWrite = static_cast(req->data); + WriteBaton* data = static_cast(queuedWrite->baton); + int bytesWritten = 0; + + do { + errno = 0; // probably don't need this + bytesWritten = write(data->fd, data->bufferData + data->offset, data->bufferLength - data->offset); + if (-1 != bytesWritten) { + // there wasn't an error, do the math on what we actually wrote and keep writing until finished + data->offset += bytesWritten; + continue; + } + + // The write call was interrupted before anything was written, try again immediately. + if (errno == EINTR) { + // why try again right away instead of in another event loop? + continue; + } + + // Try again in another event loop + if (errno == EAGAIN || errno == EWOULDBLOCK){ + return; + } + + // EBAD would mean we're "disconnected" + + // a real error so lets bail + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, calling write", strerror(errno)); + return; + } while (data->bufferLength > data->offset); +} + +void EIO_Close(uv_work_t* req) { + CloseBaton* data = static_cast(req->data); + if (-1 == close(data->fd)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, unable to close fd %d", strerror(errno), data->fd); + } +} + +#ifdef __APPLE__ + +// Function prototypes +static kern_return_t FindModems(io_iterator_t *matchingServices); +static io_service_t GetUsbDevice(io_service_t service); +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_service_t GetUsbDevice(io_service_t service) { + IOReturn status; + io_iterator_t iterator = 0; + io_service_t device = 0; + + if (!service) { + return device; + } + + status = IORegistryEntryCreateIterator(service, + kIOServicePlane, + (kIORegistryIterateParents | kIORegistryIterateRecursively), + &iterator); + + if (status == kIOReturnSuccess) { + io_service_t currentService; + while ((currentService = IOIteratorNext(iterator)) && device == 0) { + io_name_t serviceName; + status = IORegistryEntryGetNameInPlane(currentService, kIOServicePlane, serviceName); + if (status == kIOReturnSuccess && IOObjectConformsTo(currentService, kIOUSBDeviceClassName)) { + device = currentService; + } else { + // Release the service object which is no longer needed + (void) IOObjectRelease(currentService); + } + } + + // Release the iterator + (void) IOObjectRelease(iterator); + } + + return device; +} + +static void ExtractUsbInformation(stSerialDevice *serialDevice, IOUSBDeviceInterface **deviceInterface) { + kern_return_t kernResult; + 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; + io_iterator_t serialPortIterator; + char bsdPath[MAXPATHLEN]; + + 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)); + serialDevice->manufacturer[0] = '\0'; + serialDevice->serialNumber[0] = '\0'; + 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_service_t device = GetUsbDevice(modemService); + + if (device) { + CFStringRef manufacturerAsCFString = (CFStringRef) IORegistryEntryCreateCFProperty(device, + CFSTR(kUSBVendorString), + kCFAllocatorDefault, + 0); + + if (manufacturerAsCFString) { + Boolean result; + char manufacturer[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString(manufacturerAsCFString, + manufacturer, + sizeof(manufacturer), + kCFStringEncodingUTF8); + + if (result) { + strcpy(serialDevice->manufacturer, manufacturer); + } + + CFRelease(manufacturerAsCFString); + } + + CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(device, + kIOServicePlane, + CFSTR(kUSBSerialNumberString), + kCFAllocatorDefault, + kIORegistryIterateRecursively); + + if (serialNumberAsCFString) { + Boolean result; + char serialNumber[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString(serialNumberAsCFString, + serialNumber, + sizeof(serialNumber), + kCFStringEncodingUTF8); + + if (result) { + strcpy(serialDevice->serialNumber, serialNumber); + } + + CFRelease(serialNumberAsCFString); + } + + 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) { + ListBaton* data = static_cast(req->data); + +#ifndef __APPLE__ + // This code exists in javascript for other unix platforms + snprintf(data->errorString, sizeof(data->errorString), "List is not Implemented"); + return; +# else + if (!lockInitialised) { + uv_mutex_init(&list_mutex); + lockInitialised = TRUE; + } + + 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) { + resultItem->locationId = device.locationId; + } + if (*device.vendorId) { + resultItem->vendorId = device.vendorId; + } + if (*device.productId) { + resultItem->productId = device.productId; + } + if (*device.manufacturer) { + resultItem->manufacturer = device.manufacturer; + } + if (*device.serialNumber) { + resultItem->serialNumber = device.serialNumber; + } + 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) { + FlushBaton* data = static_cast(req->data); + + data->result = tcflush(data->fd, TCIFLUSH); +} + +void EIO_Set(uv_work_t* req) { + SetBaton* data = static_cast(req->data); + + int bits; + ioctl(data->fd, TIOCMGET, &bits); + + bits &= ~(TIOCM_RTS | TIOCM_CTS | TIOCM_DTR | TIOCM_DSR); + + if (data->rts) { + bits |= TIOCM_RTS; + } + + if (data->cts) { + bits |= TIOCM_CTS; + } + + if (data->dtr) { + bits |= TIOCM_DTR; + } + + if (data->dsr) { + bits |= TIOCM_DSR; + } + + int result = 0; + if (data->brk) { + result = ioctl(data->fd, TIOCSBRK, NULL); + } else { + result = ioctl(data->fd, TIOCCBRK, NULL); + } + + if (-1 == result) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); + return; + } + + if (-1 == ioctl(data->fd, TIOCMSET, &bits)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); + return; + } +} + +void EIO_Drain(uv_work_t* req) { + DrainBaton* data = static_cast(req->data); + + if (-1 == tcdrain(data->fd)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); + return; + } +} diff --git a/vendor/node-usb-native/src/serialport_win.cpp b/vendor/node-usb-native/src/serialport_win.cpp index 0440da987..340f72beb 100644 --- a/vendor/node-usb-native/src/serialport_win.cpp +++ b/vendor/node-usb-native/src/serialport_win.cpp @@ -1,582 +1,582 @@ -#include -#include -#include -#include "./serialport.h" -#include -#include -#include -#include -#pragma comment (lib, "setupapi.lib") - -#ifdef WIN32 - -#define MAX_BUFFER_SIZE 1000 - -struct WindowsPlatformOptions : OpenBatonPlatformOptions { -}; - -OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options) { - // currently none - return new WindowsPlatformOptions(); -} - -// Declare type of pointer to CancelIoEx function -typedef BOOL (WINAPI *CancelIoExType)(HANDLE hFile, LPOVERLAPPED lpOverlapped); - -std::list g_closingHandles; -int bufferSize; -void ErrorCodeToString(const char* prefix, int errorCode, char *errorStr) { - switch (errorCode) { - case ERROR_FILE_NOT_FOUND: - _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: File not found", prefix); - break; - case ERROR_INVALID_HANDLE: - _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Invalid handle", prefix); - break; - case ERROR_ACCESS_DENIED: - _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Access denied", prefix); - break; - case ERROR_OPERATION_ABORTED: - _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: operation aborted", prefix); - break; - default: - _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Unknown error code %d", prefix, errorCode); - break; - } -} - -void EIO_Open(uv_work_t* req) { - OpenBaton* data = static_cast(req->data); - - char originalPath[1024]; - strncpy_s(originalPath, sizeof(originalPath), data->path, _TRUNCATE); - // data->path is char[1024] but on Windows it has the form "COMx\0" or "COMxx\0" - // We want to prepend "\\\\.\\" to it before we call CreateFile - strncpy(data->path + 20, data->path, 10); - strncpy(data->path, "\\\\.\\", 4); - strncpy(data->path + 4, data->path + 20, 10); - - int shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - if (data->lock) { - shareMode = 0; - } - - HANDLE file = CreateFile( - data->path, - GENERIC_READ | GENERIC_WRITE, - shareMode, // dwShareMode 0 Prevents other processes from opening if they request delete, read, or write access - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, // allows for reading and writing at the same time and sets the handle for asynchronous I/O - NULL - ); - - if (file == INVALID_HANDLE_VALUE) { - DWORD errorCode = GetLastError(); - char temp[100]; - _snprintf_s(temp, sizeof(temp), _TRUNCATE, "Opening %s", originalPath); - ErrorCodeToString(temp, errorCode, data->errorString); - return; - } - - bufferSize = data->bufferSize; - if (bufferSize > MAX_BUFFER_SIZE) { - bufferSize = MAX_BUFFER_SIZE; - } - - DCB dcb = { 0 }; - SecureZeroMemory(&dcb, sizeof(DCB)); - dcb.DCBlength = sizeof(DCB); - - if (!GetCommState(file, &dcb)) { - ErrorCodeToString("Open (GetCommState)", GetLastError(), data->errorString); - CloseHandle(file); - return; - } - - if (data->hupcl) { - dcb.fDtrControl = DTR_CONTROL_ENABLE; - } else { - dcb.fDtrControl = DTR_CONTROL_DISABLE; // disable DTR to avoid reset - } - - dcb.Parity = NOPARITY; - dcb.ByteSize = 8; - dcb.StopBits = ONESTOPBIT; - dcb.fInX = FALSE; - dcb.fOutX = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fOutxCtsFlow = FALSE; - dcb.fRtsControl = RTS_CONTROL_ENABLE; - - dcb.fBinary = true; - dcb.BaudRate = data->baudRate; - dcb.ByteSize = data->dataBits; - - switch (data->parity) { - case SERIALPORT_PARITY_NONE: - dcb.Parity = NOPARITY; - break; - case SERIALPORT_PARITY_MARK: - dcb.Parity = MARKPARITY; - break; - case SERIALPORT_PARITY_EVEN: - dcb.Parity = EVENPARITY; - break; - case SERIALPORT_PARITY_ODD: - dcb.Parity = ODDPARITY; - break; - case SERIALPORT_PARITY_SPACE: - dcb.Parity = SPACEPARITY; - break; - } - - switch (data->stopBits) { - case SERIALPORT_STOPBITS_ONE: - dcb.StopBits = ONESTOPBIT; - break; - case SERIALPORT_STOPBITS_ONE_FIVE: - dcb.StopBits = ONE5STOPBITS; - break; - case SERIALPORT_STOPBITS_TWO: - dcb.StopBits = TWOSTOPBITS; - break; - } - - if (!SetCommState(file, &dcb)) { - ErrorCodeToString("Open (SetCommState)", GetLastError(), data->errorString); - CloseHandle(file); - return; - } - - // Set the com port read/write timeouts - DWORD serialBitsPerByte = 8/*std data bits*/ + 1/*start bit*/; - serialBitsPerByte += (data->parity == SERIALPORT_PARITY_NONE) ? 0 : 1; - serialBitsPerByte += (data->stopBits == SERIALPORT_STOPBITS_ONE) ? 1 : 2; - DWORD msPerByte = (data->baudRate > 0) ? - ((1000 * serialBitsPerByte + data->baudRate - 1) / data->baudRate) : - 1; - if (msPerByte < 1) { - msPerByte = 1; - } - COMMTIMEOUTS commTimeouts = {0}; - commTimeouts.ReadIntervalTimeout = msPerByte; // Minimize chance of concatenating of separate serial port packets on read - commTimeouts.ReadTotalTimeoutMultiplier = 0; // Do not allow big read timeout when big read buffer used - commTimeouts.ReadTotalTimeoutConstant = 1000; // Total read timeout (period of read loop) - commTimeouts.WriteTotalTimeoutConstant = 1000; // Const part of write timeout - commTimeouts.WriteTotalTimeoutMultiplier = msPerByte; // Variable part of write timeout (per byte) - if (!SetCommTimeouts(file, &commTimeouts)) { - ErrorCodeToString("Open (SetCommTimeouts)", GetLastError(), data->errorString); - CloseHandle(file); - return; - } - - // Remove garbage data in RX/TX queues - PurgeComm(file, PURGE_RXCLEAR); - PurgeComm(file, PURGE_TXCLEAR); - - data->result = (int)file; -} - -struct WatchPortBaton { - HANDLE fd; - DWORD bytesRead; - char buffer[MAX_BUFFER_SIZE]; - char errorString[ERROR_STRING_SIZE]; - DWORD errorCode; - bool disconnected; - Nan::Callback* dataCallback; - Nan::Callback* errorCallback; - Nan::Callback* disconnectedCallback; -}; - -void EIO_Update(uv_work_t* req) { - ConnectionOptionsBaton* data = static_cast(req->data); - - DCB dcb = { 0 }; - SecureZeroMemory(&dcb, sizeof(DCB)); - dcb.DCBlength = sizeof(DCB); - - if (!GetCommState((HANDLE)data->fd, &dcb)) { - ErrorCodeToString("GetCommState", GetLastError(), data->errorString); - return; - } - - dcb.BaudRate = data->baudRate; - - if (!SetCommState((HANDLE)data->fd, &dcb)) { - ErrorCodeToString("SetCommState", GetLastError(), data->errorString); - return; - } -} - -void EIO_Set(uv_work_t* req) { - SetBaton* data = static_cast(req->data); - - if (data->rts) { - EscapeCommFunction((HANDLE)data->fd, SETRTS); - } else { - EscapeCommFunction((HANDLE)data->fd, CLRRTS); - } - - if (data->dtr) { - EscapeCommFunction((HANDLE)data->fd, SETDTR); - } else { - EscapeCommFunction((HANDLE)data->fd, CLRDTR); - } - - if (data->brk) { - EscapeCommFunction((HANDLE)data->fd, SETBREAK); - } else { - EscapeCommFunction((HANDLE)data->fd, CLRBREAK); - } - - DWORD bits = 0; - - GetCommMask((HANDLE)data->fd, &bits); - - bits &= ~(EV_CTS | EV_DSR); - - if (data->cts) { - bits |= EV_CTS; - } - - if (data->dsr) { - bits |= EV_DSR; - } - - if (!SetCommMask((HANDLE)data->fd, bits)) { - ErrorCodeToString("Setting options on COM port (SetCommMask)", GetLastError(), data->errorString); - return; - } -} - - -void EIO_WatchPort(uv_work_t* req) { - WatchPortBaton* data = static_cast(req->data); - data->bytesRead = 0; - data->disconnected = false; - - // Event used by GetOverlappedResult(..., TRUE) to wait for incoming data or timeout - // Event MUST be used if program has several simultaneous asynchronous operations - // on the same handle (i.e. ReadFile and WriteFile) - HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - while (true) { - OVERLAPPED ov = {0}; - ov.hEvent = hEvent; - - // Start read operation - synchrounous or asynchronous - DWORD bytesReadSync = 0; - if (!ReadFile((HANDLE)data->fd, data->buffer, bufferSize, &bytesReadSync, &ov)) { - data->errorCode = GetLastError(); - if (data->errorCode != ERROR_IO_PENDING) { - // Read operation error - if (data->errorCode == ERROR_OPERATION_ABORTED) { - data->disconnected = true; - } else { - ErrorCodeToString("Reading from COM port (ReadFile)", data->errorCode, data->errorString); - CloseHandle(hEvent); - return; - } - break; - } - - // Read operation is asynchronous and is pending - // We MUST wait for operation completion before deallocation of OVERLAPPED struct - // or read data buffer - - // Wait for async read operation completion or timeout - DWORD bytesReadAsync = 0; - if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesReadAsync, TRUE)) { - // Read operation error - data->errorCode = GetLastError(); - if (data->errorCode == ERROR_OPERATION_ABORTED) { - data->disconnected = true; - } else { - ErrorCodeToString("Reading from COM port (GetOverlappedResult)", data->errorCode, data->errorString); - CloseHandle(hEvent); - return; - } - break; - } else { - // Read operation completed asynchronously - data->bytesRead = bytesReadAsync; - } - } else { - // Read operation completed synchronously - data->bytesRead = bytesReadSync; - } - - // Return data received if any - if (data->bytesRead > 0) { - break; - } - } - - CloseHandle(hEvent); -} - -bool IsClosingHandle(int fd) { - for (std::list::iterator it = g_closingHandles.begin(); it != g_closingHandles.end(); ++it) { - if (fd == *it) { - g_closingHandles.remove(fd); - return true; - } - } - return false; -} - -void DisposeWatchPortCallbacks(WatchPortBaton* data) { - delete data->dataCallback; - delete data->errorCallback; - delete data->disconnectedCallback; -} - -// FinalizerCallback will prevent WatchPortBaton::buffer from getting -// collected by gc while finalizing v8::ArrayBuffer. The buffer will -// get cleaned up through this callback. -static void FinalizerCallback(char* data, void* hint) { - uv_work_t* req = reinterpret_cast(hint); - WatchPortBaton* wpb = static_cast(req->data); - delete wpb; - delete req; -} - -void EIO_AfterWatchPort(uv_work_t* req) { - Nan::HandleScope scope; - - WatchPortBaton* data = static_cast(req->data); - if (data->disconnected) { - data->disconnectedCallback->Call(0, NULL); - DisposeWatchPortCallbacks(data); - goto cleanup; - } - - bool skipCleanup = false; - if (data->bytesRead > 0) { - v8::Local argv[1]; - argv[0] = Nan::NewBuffer(data->buffer, data->bytesRead, FinalizerCallback, req).ToLocalChecked(); - skipCleanup = true; - data->dataCallback->Call(1, argv); - } else if (data->errorCode > 0) { - if (data->errorCode == ERROR_INVALID_HANDLE && IsClosingHandle((int)data->fd)) { - DisposeWatchPortCallbacks(data); - goto cleanup; - } else { - v8::Local argv[1]; - argv[0] = Nan::Error(data->errorString); - data->errorCallback->Call(1, argv); - Sleep(100); // prevent the errors from occurring too fast - } - } - AfterOpenSuccess((int)data->fd, data->dataCallback, data->disconnectedCallback, data->errorCallback); - -cleanup: - if (!skipCleanup) { - delete data; - delete req; - } -} - -void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) { - WatchPortBaton* baton = new WatchPortBaton(); - memset(baton, 0, sizeof(WatchPortBaton)); - baton->fd = (HANDLE)fd; - baton->dataCallback = dataCallback; - baton->errorCallback = errorCallback; - baton->disconnectedCallback = disconnectedCallback; - - uv_work_t* req = new uv_work_t(); - req->data = baton; - - uv_queue_work(uv_default_loop(), req, EIO_WatchPort, (uv_after_work_cb)EIO_AfterWatchPort); -} - -void EIO_Write(uv_work_t* req) { - QueuedWrite* queuedWrite = static_cast(req->data); - WriteBaton* data = static_cast(queuedWrite->baton); - data->result = 0; - - do { - OVERLAPPED ov = {0}; - // Event used by GetOverlappedResult(..., TRUE) to wait for outgoing data or timeout - // Event MUST be used if program has several simultaneous asynchronous operations - // on the same handle (i.e. ReadFile and WriteFile) - ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - // Start write operation - synchronous or asynchronous - DWORD bytesWritten = 0; - if (!WriteFile((HANDLE)data->fd, data->bufferData, static_cast(data->bufferLength), &bytesWritten, &ov)) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_IO_PENDING) { - // Write operation error - ErrorCodeToString("Writing to COM port (WriteFile)", lastError, data->errorString); - CloseHandle(ov.hEvent); - return; - } - // Write operation is completing asynchronously - // We MUST wait for the operation completion before deallocation of OVERLAPPED struct - // or write data buffer - - // block for async write operation completion - bytesWritten = 0; - if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesWritten, TRUE)) { - // Write operation error - DWORD lastError = GetLastError(); - ErrorCodeToString("Writing to COM port (GetOverlappedResult)", lastError, data->errorString); - CloseHandle(ov.hEvent); - return; - } - } - // Write operation completed synchronously - data->result = bytesWritten; - data->offset += data->result; - CloseHandle(ov.hEvent); - } while (data->bufferLength > data->offset); -} - -void EIO_Close(uv_work_t* req) { - CloseBaton* data = static_cast(req->data); - - g_closingHandles.push_back(data->fd); - - HMODULE hKernel32 = LoadLibrary("kernel32.dll"); - // Look up function address - CancelIoExType pCancelIoEx = (CancelIoExType)GetProcAddress(hKernel32, "CancelIoEx"); - // Do something with it - if (pCancelIoEx) { - // Function exists so call it - // Cancel all pending IO Requests for the current device - pCancelIoEx((HANDLE)data->fd, NULL); - } - if (!CloseHandle((HANDLE)data->fd)) { - ErrorCodeToString("closing connection", GetLastError(), data->errorString); - return; - } -} - -char *copySubstring(char *someString, int n) -{ - char *new_ = (char*)malloc(sizeof(char)*n + 1); - strncpy_s(new_, n + 1, someString, n); - new_[n] = '\0'; - return new_; -} - -void EIO_List(uv_work_t* req) { - ListBaton* data = static_cast(req->data); - - GUID *guidDev = (GUID*)& GUID_DEVCLASS_PORTS; - HDEVINFO hDevInfo = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE); - SP_DEVINFO_DATA deviceInfoData; - - int memberIndex = 0; - DWORD dwSize, dwPropertyRegDataType; - char szBuffer[400]; - char *pnpId; - char *vendorId; - char *productId; - char *name; - char *manufacturer; - char *locationId; - bool isCom; - while (true) { - pnpId = NULL; - vendorId = NULL; - productId = NULL; - name = NULL; - manufacturer = NULL; - locationId = NULL; - - ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA)); - deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - - if (SetupDiEnumDeviceInfo(hDevInfo, memberIndex, &deviceInfoData) == FALSE) { - if (GetLastError() == ERROR_NO_MORE_ITEMS) { - break; - } - } - - dwSize = sizeof(szBuffer); - SetupDiGetDeviceInstanceId(hDevInfo, &deviceInfoData, szBuffer, dwSize, &dwSize); - szBuffer[dwSize] = '\0'; - pnpId = strdup(szBuffer); - - vendorId = strstr(szBuffer, "VID_"); - if (vendorId) { - vendorId += 4; - vendorId = copySubstring(vendorId, 4); - } - productId = strstr(szBuffer, "PID_"); - if (productId) { - productId += 4; - productId = copySubstring(productId, 4); - } - - if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_LOCATION_INFORMATION, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { - locationId = strdup(szBuffer); - } - if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_MFG, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { - manufacturer = strdup(szBuffer); - } - - HKEY hkey = SetupDiOpenDevRegKey(hDevInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); - if (hkey != INVALID_HANDLE_VALUE) { - dwSize = sizeof(szBuffer); - if (RegQueryValueEx(hkey, "PortName", NULL, NULL, (LPBYTE)&szBuffer, &dwSize) == ERROR_SUCCESS) { - szBuffer[dwSize] = '\0'; - name = strdup(szBuffer); - isCom = strstr(szBuffer, "COM") != NULL; - } - } - if (isCom) { - ListResultItem* resultItem = new ListResultItem(); - resultItem->comName = name; - resultItem->manufacturer = manufacturer; - resultItem->pnpId = pnpId; - if (vendorId) { - resultItem->vendorId = vendorId; - } - if (productId) { - resultItem->productId = productId; - } - if (locationId) { - resultItem->locationId = locationId; - } - data->results.push_back(resultItem); - } - free(pnpId); - free(vendorId); - free(productId); - free(locationId); - free(manufacturer); - free(name); - - RegCloseKey(hkey); - memberIndex++; - } - if (hDevInfo) { - SetupDiDestroyDeviceInfoList(hDevInfo); - } -} - -void EIO_Flush(uv_work_t* req) { - FlushBaton* data = static_cast(req->data); - - if (!FlushFileBuffers((HANDLE)data->fd)) { - ErrorCodeToString("flushing connection (FlushFileBuffers)", GetLastError(), data->errorString); - return; - } -} - -void EIO_Drain(uv_work_t* req) { - DrainBaton* data = static_cast(req->data); - - if (!FlushFileBuffers((HANDLE)data->fd)) { - ErrorCodeToString("draining connection (FlushFileBuffers)", GetLastError(), data->errorString); - return; - } -} - -#endif +#include +#include +#include +#include "./serialport.h" +#include +#include +#include +#include +#pragma comment (lib, "setupapi.lib") + +#ifdef WIN32 + +#define MAX_BUFFER_SIZE 1000 + +struct WindowsPlatformOptions : OpenBatonPlatformOptions { +}; + +OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options) { + // currently none + return new WindowsPlatformOptions(); +} + +// Declare type of pointer to CancelIoEx function +typedef BOOL (WINAPI *CancelIoExType)(HANDLE hFile, LPOVERLAPPED lpOverlapped); + +std::list g_closingHandles; +int bufferSize; +void ErrorCodeToString(const char* prefix, int errorCode, char *errorStr) { + switch (errorCode) { + case ERROR_FILE_NOT_FOUND: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: File not found", prefix); + break; + case ERROR_INVALID_HANDLE: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Invalid handle", prefix); + break; + case ERROR_ACCESS_DENIED: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Access denied", prefix); + break; + case ERROR_OPERATION_ABORTED: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: operation aborted", prefix); + break; + default: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Unknown error code %d", prefix, errorCode); + break; + } +} + +void EIO_Open(uv_work_t* req) { + OpenBaton* data = static_cast(req->data); + + char originalPath[1024]; + strncpy_s(originalPath, sizeof(originalPath), data->path, _TRUNCATE); + // data->path is char[1024] but on Windows it has the form "COMx\0" or "COMxx\0" + // We want to prepend "\\\\.\\" to it before we call CreateFile + strncpy(data->path + 20, data->path, 10); + strncpy(data->path, "\\\\.\\", 4); + strncpy(data->path + 4, data->path + 20, 10); + + int shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + if (data->lock) { + shareMode = 0; + } + + HANDLE file = CreateFile( + data->path, + GENERIC_READ | GENERIC_WRITE, + shareMode, // dwShareMode 0 Prevents other processes from opening if they request delete, read, or write access + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, // allows for reading and writing at the same time and sets the handle for asynchronous I/O + NULL + ); + + if (file == INVALID_HANDLE_VALUE) { + DWORD errorCode = GetLastError(); + char temp[100]; + _snprintf_s(temp, sizeof(temp), _TRUNCATE, "Opening %s", originalPath); + ErrorCodeToString(temp, errorCode, data->errorString); + return; + } + + bufferSize = data->bufferSize; + if (bufferSize > MAX_BUFFER_SIZE) { + bufferSize = MAX_BUFFER_SIZE; + } + + DCB dcb = { 0 }; + SecureZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + + if (!GetCommState(file, &dcb)) { + ErrorCodeToString("Open (GetCommState)", GetLastError(), data->errorString); + CloseHandle(file); + return; + } + + if (data->hupcl) { + dcb.fDtrControl = DTR_CONTROL_ENABLE; + } else { + dcb.fDtrControl = DTR_CONTROL_DISABLE; // disable DTR to avoid reset + } + + dcb.Parity = NOPARITY; + dcb.ByteSize = 8; + dcb.StopBits = ONESTOPBIT; + dcb.fInX = FALSE; + dcb.fOutX = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fRtsControl = RTS_CONTROL_ENABLE; + + dcb.fBinary = true; + dcb.BaudRate = data->baudRate; + dcb.ByteSize = data->dataBits; + + switch (data->parity) { + case SERIALPORT_PARITY_NONE: + dcb.Parity = NOPARITY; + break; + case SERIALPORT_PARITY_MARK: + dcb.Parity = MARKPARITY; + break; + case SERIALPORT_PARITY_EVEN: + dcb.Parity = EVENPARITY; + break; + case SERIALPORT_PARITY_ODD: + dcb.Parity = ODDPARITY; + break; + case SERIALPORT_PARITY_SPACE: + dcb.Parity = SPACEPARITY; + break; + } + + switch (data->stopBits) { + case SERIALPORT_STOPBITS_ONE: + dcb.StopBits = ONESTOPBIT; + break; + case SERIALPORT_STOPBITS_ONE_FIVE: + dcb.StopBits = ONE5STOPBITS; + break; + case SERIALPORT_STOPBITS_TWO: + dcb.StopBits = TWOSTOPBITS; + break; + } + + if (!SetCommState(file, &dcb)) { + ErrorCodeToString("Open (SetCommState)", GetLastError(), data->errorString); + CloseHandle(file); + return; + } + + // Set the com port read/write timeouts + DWORD serialBitsPerByte = 8/*std data bits*/ + 1/*start bit*/; + serialBitsPerByte += (data->parity == SERIALPORT_PARITY_NONE) ? 0 : 1; + serialBitsPerByte += (data->stopBits == SERIALPORT_STOPBITS_ONE) ? 1 : 2; + DWORD msPerByte = (data->baudRate > 0) ? + ((1000 * serialBitsPerByte + data->baudRate - 1) / data->baudRate) : + 1; + if (msPerByte < 1) { + msPerByte = 1; + } + COMMTIMEOUTS commTimeouts = {0}; + commTimeouts.ReadIntervalTimeout = msPerByte; // Minimize chance of concatenating of separate serial port packets on read + commTimeouts.ReadTotalTimeoutMultiplier = 0; // Do not allow big read timeout when big read buffer used + commTimeouts.ReadTotalTimeoutConstant = 1000; // Total read timeout (period of read loop) + commTimeouts.WriteTotalTimeoutConstant = 1000; // Const part of write timeout + commTimeouts.WriteTotalTimeoutMultiplier = msPerByte; // Variable part of write timeout (per byte) + if (!SetCommTimeouts(file, &commTimeouts)) { + ErrorCodeToString("Open (SetCommTimeouts)", GetLastError(), data->errorString); + CloseHandle(file); + return; + } + + // Remove garbage data in RX/TX queues + PurgeComm(file, PURGE_RXCLEAR); + PurgeComm(file, PURGE_TXCLEAR); + + data->result = (int)file; +} + +struct WatchPortBaton { + HANDLE fd; + DWORD bytesRead; + char buffer[MAX_BUFFER_SIZE]; + char errorString[ERROR_STRING_SIZE]; + DWORD errorCode; + bool disconnected; + Nan::Callback* dataCallback; + Nan::Callback* errorCallback; + Nan::Callback* disconnectedCallback; +}; + +void EIO_Update(uv_work_t* req) { + ConnectionOptionsBaton* data = static_cast(req->data); + + DCB dcb = { 0 }; + SecureZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + + if (!GetCommState((HANDLE)data->fd, &dcb)) { + ErrorCodeToString("GetCommState", GetLastError(), data->errorString); + return; + } + + dcb.BaudRate = data->baudRate; + + if (!SetCommState((HANDLE)data->fd, &dcb)) { + ErrorCodeToString("SetCommState", GetLastError(), data->errorString); + return; + } +} + +void EIO_Set(uv_work_t* req) { + SetBaton* data = static_cast(req->data); + + if (data->rts) { + EscapeCommFunction((HANDLE)data->fd, SETRTS); + } else { + EscapeCommFunction((HANDLE)data->fd, CLRRTS); + } + + if (data->dtr) { + EscapeCommFunction((HANDLE)data->fd, SETDTR); + } else { + EscapeCommFunction((HANDLE)data->fd, CLRDTR); + } + + if (data->brk) { + EscapeCommFunction((HANDLE)data->fd, SETBREAK); + } else { + EscapeCommFunction((HANDLE)data->fd, CLRBREAK); + } + + DWORD bits = 0; + + GetCommMask((HANDLE)data->fd, &bits); + + bits &= ~(EV_CTS | EV_DSR); + + if (data->cts) { + bits |= EV_CTS; + } + + if (data->dsr) { + bits |= EV_DSR; + } + + if (!SetCommMask((HANDLE)data->fd, bits)) { + ErrorCodeToString("Setting options on COM port (SetCommMask)", GetLastError(), data->errorString); + return; + } +} + + +void EIO_WatchPort(uv_work_t* req) { + WatchPortBaton* data = static_cast(req->data); + data->bytesRead = 0; + data->disconnected = false; + + // Event used by GetOverlappedResult(..., TRUE) to wait for incoming data or timeout + // Event MUST be used if program has several simultaneous asynchronous operations + // on the same handle (i.e. ReadFile and WriteFile) + HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + while (true) { + OVERLAPPED ov = {0}; + ov.hEvent = hEvent; + + // Start read operation - synchrounous or asynchronous + DWORD bytesReadSync = 0; + if (!ReadFile((HANDLE)data->fd, data->buffer, bufferSize, &bytesReadSync, &ov)) { + data->errorCode = GetLastError(); + if (data->errorCode != ERROR_IO_PENDING) { + // Read operation error + if (data->errorCode == ERROR_OPERATION_ABORTED) { + data->disconnected = true; + } else { + ErrorCodeToString("Reading from COM port (ReadFile)", data->errorCode, data->errorString); + CloseHandle(hEvent); + return; + } + break; + } + + // Read operation is asynchronous and is pending + // We MUST wait for operation completion before deallocation of OVERLAPPED struct + // or read data buffer + + // Wait for async read operation completion or timeout + DWORD bytesReadAsync = 0; + if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesReadAsync, TRUE)) { + // Read operation error + data->errorCode = GetLastError(); + if (data->errorCode == ERROR_OPERATION_ABORTED) { + data->disconnected = true; + } else { + ErrorCodeToString("Reading from COM port (GetOverlappedResult)", data->errorCode, data->errorString); + CloseHandle(hEvent); + return; + } + break; + } else { + // Read operation completed asynchronously + data->bytesRead = bytesReadAsync; + } + } else { + // Read operation completed synchronously + data->bytesRead = bytesReadSync; + } + + // Return data received if any + if (data->bytesRead > 0) { + break; + } + } + + CloseHandle(hEvent); +} + +bool IsClosingHandle(int fd) { + for (std::list::iterator it = g_closingHandles.begin(); it != g_closingHandles.end(); ++it) { + if (fd == *it) { + g_closingHandles.remove(fd); + return true; + } + } + return false; +} + +void DisposeWatchPortCallbacks(WatchPortBaton* data) { + delete data->dataCallback; + delete data->errorCallback; + delete data->disconnectedCallback; +} + +// FinalizerCallback will prevent WatchPortBaton::buffer from getting +// collected by gc while finalizing v8::ArrayBuffer. The buffer will +// get cleaned up through this callback. +static void FinalizerCallback(char* data, void* hint) { + uv_work_t* req = reinterpret_cast(hint); + WatchPortBaton* wpb = static_cast(req->data); + delete wpb; + delete req; +} + +void EIO_AfterWatchPort(uv_work_t* req) { + Nan::HandleScope scope; + + WatchPortBaton* data = static_cast(req->data); + if (data->disconnected) { + data->disconnectedCallback->Call(0, NULL); + DisposeWatchPortCallbacks(data); + goto cleanup; + } + + bool skipCleanup = false; + if (data->bytesRead > 0) { + v8::Local argv[1]; + argv[0] = Nan::NewBuffer(data->buffer, data->bytesRead, FinalizerCallback, req).ToLocalChecked(); + skipCleanup = true; + data->dataCallback->Call(1, argv); + } else if (data->errorCode > 0) { + if (data->errorCode == ERROR_INVALID_HANDLE && IsClosingHandle((int)data->fd)) { + DisposeWatchPortCallbacks(data); + goto cleanup; + } else { + v8::Local argv[1]; + argv[0] = Nan::Error(data->errorString); + data->errorCallback->Call(1, argv); + Sleep(100); // prevent the errors from occurring too fast + } + } + AfterOpenSuccess((int)data->fd, data->dataCallback, data->disconnectedCallback, data->errorCallback); + +cleanup: + if (!skipCleanup) { + delete data; + delete req; + } +} + +void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) { + WatchPortBaton* baton = new WatchPortBaton(); + memset(baton, 0, sizeof(WatchPortBaton)); + baton->fd = (HANDLE)fd; + baton->dataCallback = dataCallback; + baton->errorCallback = errorCallback; + baton->disconnectedCallback = disconnectedCallback; + + uv_work_t* req = new uv_work_t(); + req->data = baton; + + uv_queue_work(uv_default_loop(), req, EIO_WatchPort, (uv_after_work_cb)EIO_AfterWatchPort); +} + +void EIO_Write(uv_work_t* req) { + QueuedWrite* queuedWrite = static_cast(req->data); + WriteBaton* data = static_cast(queuedWrite->baton); + data->result = 0; + + do { + OVERLAPPED ov = {0}; + // Event used by GetOverlappedResult(..., TRUE) to wait for outgoing data or timeout + // Event MUST be used if program has several simultaneous asynchronous operations + // on the same handle (i.e. ReadFile and WriteFile) + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + // Start write operation - synchronous or asynchronous + DWORD bytesWritten = 0; + if (!WriteFile((HANDLE)data->fd, data->bufferData, static_cast(data->bufferLength), &bytesWritten, &ov)) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_IO_PENDING) { + // Write operation error + ErrorCodeToString("Writing to COM port (WriteFile)", lastError, data->errorString); + CloseHandle(ov.hEvent); + return; + } + // Write operation is completing asynchronously + // We MUST wait for the operation completion before deallocation of OVERLAPPED struct + // or write data buffer + + // block for async write operation completion + bytesWritten = 0; + if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesWritten, TRUE)) { + // Write operation error + DWORD lastError = GetLastError(); + ErrorCodeToString("Writing to COM port (GetOverlappedResult)", lastError, data->errorString); + CloseHandle(ov.hEvent); + return; + } + } + // Write operation completed synchronously + data->result = bytesWritten; + data->offset += data->result; + CloseHandle(ov.hEvent); + } while (data->bufferLength > data->offset); +} + +void EIO_Close(uv_work_t* req) { + CloseBaton* data = static_cast(req->data); + + g_closingHandles.push_back(data->fd); + + HMODULE hKernel32 = LoadLibrary("kernel32.dll"); + // Look up function address + CancelIoExType pCancelIoEx = (CancelIoExType)GetProcAddress(hKernel32, "CancelIoEx"); + // Do something with it + if (pCancelIoEx) { + // Function exists so call it + // Cancel all pending IO Requests for the current device + pCancelIoEx((HANDLE)data->fd, NULL); + } + if (!CloseHandle((HANDLE)data->fd)) { + ErrorCodeToString("closing connection", GetLastError(), data->errorString); + return; + } +} + +char *copySubstring(char *someString, int n) +{ + char *new_ = (char*)malloc(sizeof(char)*n + 1); + strncpy_s(new_, n + 1, someString, n); + new_[n] = '\0'; + return new_; +} + +void EIO_List(uv_work_t* req) { + ListBaton* data = static_cast(req->data); + + GUID *guidDev = (GUID*)& GUID_DEVCLASS_PORTS; + HDEVINFO hDevInfo = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE); + SP_DEVINFO_DATA deviceInfoData; + + int memberIndex = 0; + DWORD dwSize, dwPropertyRegDataType; + char szBuffer[400]; + char *pnpId; + char *vendorId; + char *productId; + char *name; + char *manufacturer; + char *locationId; + bool isCom; + while (true) { + pnpId = NULL; + vendorId = NULL; + productId = NULL; + name = NULL; + manufacturer = NULL; + locationId = NULL; + + ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA)); + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + if (SetupDiEnumDeviceInfo(hDevInfo, memberIndex, &deviceInfoData) == FALSE) { + if (GetLastError() == ERROR_NO_MORE_ITEMS) { + break; + } + } + + dwSize = sizeof(szBuffer); + SetupDiGetDeviceInstanceId(hDevInfo, &deviceInfoData, szBuffer, dwSize, &dwSize); + szBuffer[dwSize] = '\0'; + pnpId = strdup(szBuffer); + + vendorId = strstr(szBuffer, "VID_"); + if (vendorId) { + vendorId += 4; + vendorId = copySubstring(vendorId, 4); + } + productId = strstr(szBuffer, "PID_"); + if (productId) { + productId += 4; + productId = copySubstring(productId, 4); + } + + if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_LOCATION_INFORMATION, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { + locationId = strdup(szBuffer); + } + if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_MFG, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { + manufacturer = strdup(szBuffer); + } + + HKEY hkey = SetupDiOpenDevRegKey(hDevInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (hkey != INVALID_HANDLE_VALUE) { + dwSize = sizeof(szBuffer); + if (RegQueryValueEx(hkey, "PortName", NULL, NULL, (LPBYTE)&szBuffer, &dwSize) == ERROR_SUCCESS) { + szBuffer[dwSize] = '\0'; + name = strdup(szBuffer); + isCom = strstr(szBuffer, "COM") != NULL; + } + } + if (isCom) { + ListResultItem* resultItem = new ListResultItem(); + resultItem->comName = name; + resultItem->manufacturer = manufacturer; + resultItem->pnpId = pnpId; + if (vendorId) { + resultItem->vendorId = vendorId; + } + if (productId) { + resultItem->productId = productId; + } + if (locationId) { + resultItem->locationId = locationId; + } + data->results.push_back(resultItem); + } + free(pnpId); + free(vendorId); + free(productId); + free(locationId); + free(manufacturer); + free(name); + + RegCloseKey(hkey); + memberIndex++; + } + if (hDevInfo) { + SetupDiDestroyDeviceInfoList(hDevInfo); + } +} + +void EIO_Flush(uv_work_t* req) { + FlushBaton* data = static_cast(req->data); + + if (!FlushFileBuffers((HANDLE)data->fd)) { + ErrorCodeToString("flushing connection (FlushFileBuffers)", GetLastError(), data->errorString); + return; + } +} + +void EIO_Drain(uv_work_t* req) { + DrainBaton* data = static_cast(req->data); + + if (!FlushFileBuffers((HANDLE)data->fd)) { + ErrorCodeToString("draining connection (FlushFileBuffers)", GetLastError(), data->errorString); + return; + } +} + +#endif