UNPKG

bonescriptjtjk

Version:

Physical computing library for embedded Linux

525 lines (483 loc) 16.8 kB
// Copyright (C) 2013 - Texas Instruments, Jason Kridner // // This is meant to hold some private functions // var fs = require('fs'); var winston = require('winston'); var child_process = require('child_process'); var bone = require('./bone'); var g = require('./constants'); var ffi = require("ffi"); var debug = process.env.DEBUG ? true : false; var sysfsFiles = {}; var libc = new ffi.Library(null, { "system": ["int32", ["string"]] }); var system = libc.system; exports.require = function(packageName, onfail) { var y = {}; try { y = require(packageName); y.exists = true; } catch(ex) { y.exists = false; if(debug) winston.debug("Optional package '" + packageName + "' not loaded"); if(onfail) onfail(); } return(y); }; var fibers = exports.require('fibers'); exports.is_new_capemgr = function(callback) { var exists = exports.file_existsSync('/sys/devices/platform/bone_capemgr/slots'); if(callback) callback(exists); return(exists); }; exports.is_capemgr = function(callback) { return(exports.find_sysfsFile('capemgr', '/sys/devices', 'bone_capemgr.', callback)); }; exports.is_ocp = function(callback) { var found = exports.find_sysfsFile('ocp', '/sys/devices', 'ocp.', callback); if(debug) winston.debug("is_ocp, found = " + found); if(typeof found == 'undefined') { found = exports.find_sysfsFile('ocp', '/sys/devices/platform', 'ocp', callback); if(debug) winston.debug("is_ocp, found2 = " + found); } return(found); }; exports.is_cape_universal = function(callback) { var ocp = exports.is_ocp(); if(debug) winston.debug('is_ocp() = ' + ocp); var cape_universal = exports.find_sysfsFile('cape-universal', ocp, 'cape-universal.', callback); if(debug) winston.debug('is_cape_universal() = ' + cape_universal); return(cape_universal); }; exports.find_sysfsFile = function(name, path, prefix, callback) { if(typeof sysfsFiles[name] == 'undefined') { if(callback) { sysfsFiles[name] = exports.file_find(path, prefix, 1, onFindCapeMgr); } else { sysfsFiles[name] = exports.file_find(path, prefix, 1); } } else { if(callback) callback({path: sysfsFiles[name]}); } function onFindCapeMgr(resp) { if(typeof resp.path == 'undefined') sysfsFiles[name] = false; else sysfsFiles[name] = resp.path; callback(resp); } return(sysfsFiles[name]); }; exports.file_exists = fs.exists; exports.file_existsSync = fs.existsSync; if(typeof exports.file_exists == 'undefined') { var path = require('path'); exports.file_exists = path.exists; exports.file_existsSync = path.existsSync; } exports.file_find = function(path, prefix, attempts, callback) { var resp = {}; resp.attempts = 0; if(typeof attempts == 'undefined') attempts = 1; if(callback) { fs.readdir(path, onReadDir); } else { while(resp.attempts < attempts) { try { var files = fs.readdirSync(path); onReadDir(null, files); } catch(ex) { if(debug) winston.debug('Error reading ' + path); return(resp.path); } if(resp.path) return(resp.path); } return(resp.path); } function onReadDir(err, files) { resp.attempts++; if(err) { resp.err = 'Error listing directory ' + path + ': ' + err; if(callback) callback(resp); return; } for(var j in files) { if(files[j].indexOf(prefix) === 0) { resp.path = path + '/' + files[j]; if(callback) callback(resp); return; } } if(callback) { if(resp.attempts >= attempts) { resp.err = 'Did not find ' + prefix + ' in ' + path; callback(resp); } else { fs.readdir(path, onReadDir); } } } return(resp.path); }; // Note, this just makes sure there was an attempt to load the // devicetree fragment, not if it was successful exports.load_dt = function(name, pin, resp, callback) { if(debug) winston.debug('load_dt(' + [name, pin ? pin.key : null, JSON.stringify(resp)] + ')'); resp = resp || {}; var slotsFile; var lastSlots; var writeAttempts = 0; var readAttempts = 0; if(pin) { var slotRegex = new RegExp('\\d+(?=\\s*:.*,bs.*' + pin.key + ')', 'gm'); } var capemgr = exports.is_capemgr(); onFindCapeMgr({path:capemgr}); function onFindCapeMgr(x) { if(debug) winston.debug('onFindCapeMgr: path = ' + x.path); if(typeof x.path == 'undefined') { resp.err = "CapeMgr not found: " + x.err; winston.error(resp.err); if(callback) callback(resp); return(false); } slotsFile = x.path + '/slots'; var slots; try { slots = fs.readFileSync(slotsFile, 'ascii'); } catch(ex) { resp.err = ex; } onReadSlots(resp.err, slots); } function onReadSlots(err, slots) { readAttempts++; if(err) { resp.err = 'Unable to read from CapeMgr slots: ' + err; winston.error(resp.err); if(callback) callback(resp); return(false); } lastSlots = slots; var index = slots.indexOf(name); if(debug) winston.debug('onReadSlots: index = ' + index + ', readAttempts = ' + readAttempts); if(index >= 0) { // Fragment is already loaded if(callback) callback(resp); } else if (readAttempts <= 1) { // Attempt to load fragment try { if(debug) winston.debug('Writing ' + name + ' to ' + slotsFile); fs.writeFileSync(slotsFile, name, 'ascii'); } catch(ex) { resp.err = ex; } onWriteSlots(resp.err); } else { resp.err = 'Error waiting for CapeMgr slot to load'; callback(resp); } } function onWriteSlots(err) { writeAttempts++; if(err) { resp.err = 'Write to CapeMgr slots failed: ' + err; if(pin && writeAttempts <= 1) unloadSlot(); else { if(callback) callback(resp); return(false); } } var slots; try { slots = fs.readFileSync(slotsFile, 'ascii'); } catch(ex) { resp.err = ex; } onReadSlots(resp.err, slots); } function unloadSlot() { var slot = lastSlots.match(slotRegex); if(slot && slot[0]) { if(debug) winston.debug('Attempting to unload conflicting slot ' + slot[0] + ' for ' + name); try { fs.writeFileSync(slotsFile, '-'+slot[0], 'ascii'); } catch(ex) { resp.err = ex; } onUnloadSlot(resp.err); } else { if(callback) callback(resp); return(false); } } function onUnloadSlot(err) { if(err) { resp.err = "Unable to unload conflicting slot: " + err; callback(resp); return; } try { fs.writeFileSync(slotsFile, name, 'ascii'); } catch(ex) { resp.err = ex; } onWriteSlots(resp.err); } if(debug) winston.debug('load_dt resp: ' + JSON.stringify(resp)); if(debug) winston.debug('load_dt return: ' + (typeof resp.err == 'undefined')); return(typeof resp.err == 'undefined'); }; exports.create_dt = function(pin, data, template, load, force_create, resp, callback) { if(debug) winston.debug('create_dt(' + [pin.key, data, template, load, force_create, JSON.stringify(resp)] + ')'); resp = resp || {}; template = template || 'bspm'; load = (typeof load === 'undefined') ? true : load; var fragment = template + '_' + pin.key + '_' + data.toString(16); var dtsFilename = '/lib/firmware/' + fragment + '-00A0.dts'; var dtboFilename = '/lib/firmware/' + fragment + '-00A0.dtbo'; if(force_create) { createDTS(); } else { var exists = exports.file_existsSync(dtboFilename); onDTBOExistsTest(exists); } function onDTBOExistsTest(exists) { if(exists) { onDTBOExists(); } else { createDTS(); } } function createDTS() { var templateFilename = require.resolve('bonescript').replace('main.js', 'dts/' + template + '_template.dts'); if(debug) winston.debug('Creating template: ' + templateFilename); var dts = fs.readFileSync(templateFilename, 'utf8'); dts = dts.replace(/!PIN_KEY!/g, pin.key); dts = dts.replace(/!PIN_DOT_KEY!/g, pin.key.replace(/_/, '.')); dts = dts.replace(/!PIN_FUNCTION!/g, pin.options[data&7]); dts = dts.replace(/!PIN_OFFSET!/g, pin.muxRegOffset); dts = dts.replace(/!DATA!/g, '0x' + data.toString(16)); if(pin.pwm) { dts = dts.replace(/!PWM_MODULE!/g, pin.pwm.module); dts = dts.replace(/!PWM_INDEX!/g, pin.pwm.index); dts = dts.replace(/!DUTY_CYCLE!/g, 500000); } try { fs.writeFileSync(dtsFilename, dts, 'ascii'); } catch(ex) { resp.err = ex; } onDTSWrite(resp.err); } function onDTSWrite(err) { if(err) { resp.err = 'Error writing ' + dtsFilename + ': ' + err; if(debug) winston.debug(resp.err); if(callback) callback(resp); return(resp); } var command = 'dtc -O dtb -o ' + dtboFilename + ' -b 0 -@ ' + dtsFilename; try { system(command); } catch(ex) { resp.err = ex; } dtcHandler(resp.err); } function dtcHandler(error, stdout, stderr) { if(debug) winston.debug('dtcHandler: ' + JSON.stringify({error:error, stdout:stdout, stderr:stderr})); if(!error) onDTBOExists(); } function onDTBOExists() { if(debug) winston.debug('onDTBOExists()'); if(load) exports.load_dt(fragment, pin, resp); } if(callback) callback(resp); return(typeof resp.err == 'undefined'); }; exports.myeval = function(x) { winston.debug('myeval("' + x + '");'); var y; try { y = eval(x); } catch(ex) { y = undefined; winston.error('myeval error: ' + ex); throw('myeval error: ' + ex); } winston.debug('result = ' + y); return(y); }; exports.getpin = function(pin) { if(typeof pin == 'object') return(pin); else if(typeof pin == 'string') return(bone.pins[pin]); else if(typeof pin == 'number') return(bone.pinIndex[pin]); else throw("Invalid pin: " + pin); }; exports.wrapCall = function(m, func, funcArgs, cbArgs) { if(!m.module.exists) { if(debug) winston.debug(m.name + ' support module not loaded.'); return(function(){}); } funcArgs.unshift('port'); funcArgs.push('callback'); var newFunction = function() { var args = []; var port = arguments[0]; var callback = false; for(var i = 1; i < arguments.length; i++) { winston.debug('Adding argument ' + funcArgs[i] + ' to wrapper'); if(funcArgs[i] == 'callback') { callback = arguments[i]; var wrappedCallback = function() { var cbData = {}; for(var j = 0; j < cbArgs.length; j++) { cbData[cbArgs[j]] = arguments[j]; } cbData.event = 'callback'; winston.debug('cbData = ' + JSON.stringify(cbData)); callback(cbData); }; args.push(wrappedCallback); } else { args.push(arguments[i]); } } if(!m.openPorts[port]) { if(callback) callback({'err': m.name + ' ' + port + ' not opened'}); return(false); } winston.debug('Calling ' + m.name + '[' + port + '].' + func + '(' + args + ')'); var x = m.openPorts[port][func].apply( m.openPorts[port], args); if(callback) callback({'event': 'return', 'return': x}); return(x); }; newFunction.args = funcArgs; return(newFunction); }; exports.wrapOpen = function(m, openArgs) { if(!m.module.exists) { if(debug) winston.debug(m.name + ' support module not loaded.'); return(function(){}); } openArgs.unshift('port'); openArgs.push('callback'); var newFunction = function() { var args = {}; for(var i = 0; i < openArgs.length; i++) { args[openArgs[i]] = arguments[i]; } var port = args.port; var callback = args.callback; winston.debug(m.name + ' opened with ' + JSON.stringify(arguments)); if(m.ports[port] && m.ports[port].devicetree) { var fragment = m.ports[port].devicetree; if(!exports.is_capemgr()) { if(callback) callback({err:'Kernel does not include CapeMgr module'}); return(false); } if(!exports.load_dt(fragment)) { if(callback) callback({'err': 'Devicetree overlay fragment ' + fragment + ' not loaded'}); return(false); } } m.openPorts[port] = m.doOpen(args); if(!m.openPorts[port]) { if(callback) callback({'err': 'Unable to ' + m.name}); return(false); } for(var e in m.events) { var addHandler = function(m, port, e) { var handler = function() { var myargs = arguments; myargs.event = e; for(var i = 0; i < arguments.length; i++) { myargs[m.events[e][i]] = arguments[i]; } callback(myargs); }; m.openPorts[port].on(e, handler); }; addHandler(m, port, e); } if(callback) callback({'event':'return', 'value':true}); return(true); }; newFunction.args = openArgs; return(newFunction); }; exports.pin_data = function(slew, direction, pullup, mux) { var pinData = 0; if(slew == 'slow') pinData |= 0x40; if(direction != g.OUTPUT) pinData |= 0x20; switch(pullup) { case 'disabled': pinData |= 0x08; break; case 'pullup': pinData |= 0x10; break; default: break; } pinData |= (mux & 0x07); return(pinData); }; // Inspired by // https://github.com/luciotato/waitfor/blob/master/waitfor.js // https://github.com/0ctave/node-sync/blob/master/lib/sync.js exports.wait_for = function(fn, myargs, result_name, no_error) { var fiber = fibers.current; var yielded = false; var args = []; var result; for(var i in fn.args) { if(fn.args[i] == 'callback') { args.push(myCallback); } else { args.push(myargs[i]); } } if(typeof fiber == 'undefined') { if(no_error) { // No callback required (fire and forget) fn.apply(this, args); } else { var stack = new Error().stack; var err = 'As of BoneScript 0.2.5, synchronous calls must be made\n' + 'within a fiber such as within loop() or setup():\n' + stack; winston.error(err); throw(err); } } else { fn.apply(this, args); if(!myCallback.called) { yielded = true; fibers.yield(); } } function myCallback(x) { if(debug) winston.debug('Callback: ' + fn.name + ' ' + x.err); if(myCallback.called) return; if(typeof result_name == 'undefined') { result = x; } else if(typeof x[result_name] != 'undefined') { result = x[result_name]; } if(typeof x.err != 'undefined' && x.err) { //var fn_name = fn.toString().substr('function '.length); //fn_name = fn_name.substr(0, fn_name.indexOf('(')); if(debug) winston.debug(fn.name + ' ' + x.err); } myCallback.called = true; if(typeof fiber == 'undefined' && no_error) return; if(yielded) fiber.run(); } return(result); };