<commit_before>// Copyright (C) 2011 - Texas Instruments, <NAME> 
//
// 
var fs = require('fs');
var child_process = require('child_process');
var http = require('http');
var url = require('url');
var path = require('path');
var cluster = require('cluster');
var winston = require('winston');
var eeprom = require('./eeprom');
var parse = require('./parse');
var platform = require('./bone').bone;
var functions = require('./functions');

var f = {};

winston.add(winston.transports.File, { filename: '/var/log/bonescript.log' });
winston.setLevels(winston.config.syslog.levels);

var myeval = function(x) {
    winston.info('eval("' + x + '");');
    var y;
    try {
        y = eval(x);
    } catch(ex) {
        y = undefined;
        winston.info('myeval error: ' + ex);
        throw('myeval error: ' + ex);
    }
    winston.info('result = ' + y);
    return(y);
};

var myrequire = function(packageName, onfail) {
    var y = {};
    try {
        y = require(packageName);
        y.exists = true;
    } catch(ex) {
        y.exists = false;
        winston.info("Optional package '" + packageName + "' not loaded");
        if(onfail) onfail();
    }
    return(y);
};

var socketio = myrequire('socket.io', function() {
    winston.info("Dynamic web features not enabled");
});
myrequire('systemd', function() {
    winston.info("Startup as socket-activated service under systemd not enabled");
});

var misc = myrequire('./build/Release/misc');

var gOUTPUT = "out";
var gINPUT = "in";
var gINPUT_PULLUP = "in_pullup";
var gHIGH = 1;
var gLOW = 0;
var gLSBFIRST = 1;  // used in: shiftOut(dataPin, clockPin, bitOrder, val)
var gMSBFIRST = 0;
var gCHANGE = "both";
var gRISING = "rising";
var gFALLING = "falling";

// Keep track of allocated resources
var gpio = [];
var pwm = [];

// returned object has:
//  mux: index of mux mode
//  options: array of mode names
//  slew: 'fast' or 'slow'
//  rx: 'enabled' or 'disabled'
//  pullup: 'diabled', 'pullup' or 'pulldown'
//  pin: key string for pin
//  name: pin name
f.getPinMode = function(pin, callback) {
    winston.debug('getPinMode(' + pin.key + '): ' + muxFile);
    var mode = {'pin': pin.key, 'name': pin.name};
    if(pin.options) mode.options = pin.options;
    var muxFile = '/sys/kernel/debug/omap_mux/' + pin.mux;
    var pinctrlFile = '/sys/kernel/debug/pinctrl/44e10800.pinmux/pins';
    var muxRegOffset = parseInt(pin.muxRegOffset, 16);
    var readOmapMux = function(err, data) {
        if(err) winston.info('readOmapMux error: ' + err);
        mode = parse.modeFromOmapMux(data, mode);
        callback(mode);
    };
    var readPinctrl = function(err, data) {
        if(err) winston.info('readPinctrl error: ' + err);
        mode = parse.modeFromPinctrl(data, muxRegOffset, 0x44e10800, mode);
        callback(mode);
    };
    var tryPinctrl = function(exists) {
        if(exists) {
            fs.readFile(pinctrlFile, 'utf8', readPinctrl);
        } else {
            winston.info('getPinMode(' + pin.key + '): no valid mux data');
            callback(mode);
        }
    };
    var tryOmapMux = function(exists) {
        if(exists) {
            fs.readFile(muxFile, 'utf8', readOmapMux);
        } else {
            path.exists(pinctrlFile, tryPinctrl);
        }
    };
    if(callback) {
        path.exists(muxFile, tryOmapMux);
    } else {
        try {
            var data = fs.readFileSync(muxFile, 'utf8');
            mode = parse.modeFromOmapMux(data, mode);
        } catch(ex) {
            try {
                var data2 = fs.readFileSync(pinctrlFile, 'utf8');
                mode = parse.modeFromPinctrl(data2, muxRegOffset, 0x44e10800, mode);
            } catch(ex2) {
                winston.info('getPinMode(' + pin.key + '): ' + ex2);
            }
        }
        return(mode);
    }
};

f.pinMode = function(pin, direction, mux, pullup, slew, callback) {
    if(direction == gINPUT_PULLUP) pullup = 'pullup';
    pullup = pullup || ((direction == gINPUT) ? 'pulldown' : 'disabled');
    slew = slew || 'fast';
    mux = mux || 7; // default to GPIO mode
    winston.debug('pinmode(' + [pin.key, direction, mux, pullup, slew].join(',') + ')');
    
    if(!pin.mux) {
        winston.info('Invalid pin object for pinMode: ' + pin);
        throw('Invalid pin object for pinMode: ' + pin);
    }

    var muxFile = '/sys/kernel/debug/omap_mux/' + pin.mux;
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    
    // Handle case where pin is allocated as a gpio-led
    if(pin.led) {
        if((direction != gOUTPUT) || (mux != 7)) {                    
            winston.info('pinMode only supports GPIO output for LEDs: ' + pin);
            if(callback) callback(false);
            return(false);
        }
        gpioFile = '/sys/class/leds/beaglebone::' + pin.led + '/brightness';
    }

    // Figure out the desired value
    var pinData = 0;
    if(slew == 'slow') pinData |= 0x40;
    if(direction != gOUTPUT) pinData |= 0x20;
    switch(pullup) {
    case 'disabled':
        pinData |= 0x08;
        break;
    case 'pullup':
        pinData |= 0x10;
        break;
    default:
        break;
    }
    pinData |= (mux & 0x07);
    
    try {
        var fd = fs.openSync(muxFile, 'w');
        fs.writeSync(fd, pinData.toString(16), null);
    } catch(ex) {
        winston.error('Unable to configure mux for pin ' + pin + ': ' + ex);
        gpio[n] = {};
        if(callback) callback(false);
        return(false);
    }

    // Enable GPIO, if not already done
    var n = pin.gpio;
    if(mux == 7) {
        if(!gpio[n] || !gpio[n].path) {
            gpio[n] = {'path': gpioFile};
    
            if(pin.led) {
                var pathA = "/sys/class/leds/beaglebone:";
                var pathB = pathA;
                pathA += ":" + pin.led + "/trigger";
                pathB += "green:" + pin.led + "/trigger";
                if(path.existsSync(pathA)) {
                    fs.writeFileSync(pathA, "gpio");
                } else {
                    if(path.existsSync(pathB)) {
                        fs.writeFileSync(pathB, "gpio");
                    } else {
                        winston.error("Unable to find LED: " + pin.led);
                    }
                }
            } else {
                // Export the GPIO controls
                var exists = path.existsSync(gpioFile);
                if(exists) {
                    winston.debug("gpio: " + n + " already exported.");
                    fs.writeFileSync("/sys/class/gpio/gpio" + n + "/direction",
                        direction, null);
                } else {
                    try {
                        fs.writeFileSync("/sys/class/gpio/export", "" + n, null);
                        fs.writeFileSync("/sys/class/gpio/gpio" + n + "/direction",
                            direction, null);
                    } catch(ex2) {
                        winston.error('Unable to export gpio-' + n + ': ' + ex2);
                        var gpioUsers = fs.readFileSync('/sys/kernel/debug/gpio', 'utf-8');
                        gpioUsers = gpioUsers.split('\n');
                        for(var x in gpioUsers) {
                            var y = gpioUsers[x].match(/gpio-(\d+)\s+\((\S+)\s*\)/);
                            if(y && y[1] == n) {
                                winston.error('gpio-' + n + ' consumed by ' + y[2]);
                            }
                        }
                        gpio[n] = {};
                        if(callback) callback(false);
                        return(false);
                    }
                }
            }
        }
    } else {
        gpio[n] = {};
    }
    
    if(callback) callback(true);
    return(true);
};

f.digitalWrite = function(pin, value, callback) {
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    if(pin.led) {
        var pathA = "/sys/class/leds/beaglebone:";
        var pathB = pathA;
        pathA += ":" + pin.led + "/brightness";
        pathB += "green:" + pin.led + "/brightness";
        if(path.existsSync(pathA)) {
            gpioFile = pathA;
        } else {
            if(path.existsSync(pathB)) {
                gpioFile = pathB;
            } else {
                winston.error("Unable to find LED: " + pin.led);
            }
        }
    }
    winston.debug("gpioFile = " + gpioFile);
    if(callback) {
        fs.writeFile(gpioFile, '' + value, null, callback);
    } else {
        fs.writeFileSync(gpioFile, '' + value, null);
    }
    return(true);
};

f.digitalRead = function(pin, callback) {
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    if(callback) {
        var readFile = function(err, data) {
            if(err) winston.info('digitalRead error: ' + err);
            var value = parseInt(data, 2);
            callback({'value':value});
        };
        fs.readFile(gpioFile, readFile);
        return(true);
    }
    var value = parseInt(fs.readFileSync(gpioFile), 2);
    return(value);
};

f.analogRead = function(pin, callback) {
    var ainFile = '/sys/bus/platform/devices/tsc/ain' + (pin.ain+1);
    if(callback) {
        var readFile = function(err, data) {
            if(err) winston.info('analogRead error: ' + err);
            var value = parseInt(data, 10) / pin.scale;
            callback({'value': value});
        };
        fs.readFile(ainFile, readFile);
        return(true);
    }
    var data = parseInt(fs.readFileSync(ainFile), 10);
    if(isNaN(data)) {
        throw('analogRead(' + pin.key + ') returned ' + data);
    }
    data = data / pin.scale;
    if(isNaN(data)) {
        throw('analogRead(' + pin.key + ') scaled to ' + data);
    }
    return(data);
}; 

f.shiftOut = function(dataPin, clockPin, bitOrder, val, callback) {
  var i;
  var bit;
  for (i = 0; i < 8; i++)  
  {
    if (bitOrder == gLSBFIRST) 
    {
         bit = val & (1 << i);
    } else
    {
         bit = val & (1 << (7 - i));
    }

    f.digitalWrite(dataPin, bit);
    f.digitalWrite(clockPin, gHIGH);
    f.digitalWrite(clockPin, gLOW);
    if(callback) callback();
  }
};

f.attachInterrupt = function(pin, handler, mode, callback) {
    if(!gpio[pin.gpio]) {
        if(callback) callback({'pin':pin, 'attached':false, 'configured':false});
        return(false);
    }
    if(gpio[pin.gpio].intProc) {
        if(callback) callback({'pin':pin, 'attached':false, 'configured':true});
        return(false);
    }
    winston.info('Adding handler ' + handler + ' to pin ' + pin.key);
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    fs.writeFileSync('/sys/class/gpio/gpio' + pin.gpio + '/edge', mode);
    handler = (typeof handler === "string") ? myeval('(' + handler + ')') : handler;
    var intHandler = function(m) {
        var output = handler({'pin':pin, 'value':m.value});
        if(output && callback) callback({'pin':pin, 'output':output});
    };
    var intProc;
    if(child_process.fork) {
        intProc = child_process.fork(__dirname + '/gpioint.js');
    } else {
        var fork = require('fork');
        intProc = fork.fork(__dirname + '/gpioint.js');
    }
    intProc.on('message', intHandler);
    intProc.on('exit', function(code, signal) {
        if(callback) callback({
            'pin':pin,
            'code':code,
            'signal':signal,
            'died':true
        });
    });
    intProc.send({'pin':pin, 'mode':mode, 'file':gpioFile});
    gpio[pin.gpio].intProc = intProc;
    process.on('SIGTERM', function() {
        intProc.kill();
        if(callback) callback({'pin':pin, 'died':true});
    });
    if(callback) callback({'pin':pin, 'attached':true});
    return(true);
};

f.detachInterrupt = function(pin, callback) {
    if(!gpio[pin.gpio] || !gpio[pin.gpio].intProc) {
        if(callback) callback({'pin':pin, 'detached':false});
        return(false);
    }
    gpio[pin.gpio].intProc.kill();
    delete gpio[pin.gpio].intProc;
    if(callback) callback({'pin':pin, 'detached':true});
};

// See http://processors.wiki.ti.com/index.php/AM335x_PWM_Driver's_Guide
f.analogWrite = function(pin, value, freq, callback) {
    freq = freq || 1000;
    var path = '/sys/class/pwm/' + pin.pwm.path;
    //var curMode = getPinMode(pin);
    // Not yet possible to implement this test
    //if(curMode.direction != OUTPUT) {
    //    throw(pin.key + ' must be configured as OUTPUT for analogWrite()');
    //}
    if(!pin.pwm) {
        throw(pin.key + ' does not support analogWrite()');
    }
    if(pwm[pin.pwm.path] && pwm[pin.pwm.path].key) {
        if(pwm[pin.pwm.path].key != pin.key) {
            throw(pin.key + ' requires pwm ' + pin.pwm.name +
                ' but it is already in use by ' +
                pwm[pin.pwm].key
            );
         }
    } else {
        pwm[pin.pwm.path] = {};
        pwm[pin.pwm.path].key = '' + pin.key;
        pwm[pin.pwm.path].freq = freq;
        f.pinMode(pin, gOUTPUT, pin.pwm.muxmode, 'disabled', 'fast');

        // Clear up any unmanaged usage
        fs.writeFileSync(path+'/request', '0');

        // Allocate and configure the PWM
        fs.writeFileSync(path+'/request', '1');
        fs.writeFileSync(path+'/period_freq', freq);
        fs.writeFileSync(path+'/polarity', '0');
        fs.writeFileSync(path+'/run', '1');
    }
    if(pwm[pin.pwm.path].freq != freq) {
        fs.writeFileSync(path+'/run', '0');
        fs.writeFileSync(path+'/duty_percent', '0');
        fs.writeFileSync(path+'/period_freq', freq);
        fs.writeFileSync(path+'/run', '1');
        pwm[pin.pwm.path].freq = freq;
    }
    fs.writeFileSync(path+'/duty_percent', Math.round(value*100));
    if(callback) callback();
};

f.getEeproms = function(callback) {
    var EepromFiles = {
        '/sys/bus/i2c/drivers/at24/1-0050/eeprom': { type: 'bone' },
        '/sys/bus/i2c/drivers/at24/3-0054/eeprom': { type: 'cape' },
        '/sys/bus/i2c/drivers/at24/3-0055/eeprom': { type: 'cape' },
        '/sys/bus/i2c/drivers/at24/3-0056/eeprom': { type: 'cape' },
        '/sys/bus/i2c/drivers/at24/3-0057/eeprom': { type: 'cape' }
    };
    var eeproms = eeprom.readEeproms(EepromFiles);
    if(eeproms == {}) {
        winston.info('No valid EEPROM contents found');
    }
    if(callback) {
        callback(eeproms);
    }
    return(eeproms);
};

var myWorkers = [];
f.addLoop = function(loopFunc, loopDelay, callback) {<commit_msg>getEeproms: quick hack for 3.8 kernel<commit_after>// Copyright (C) 2011 - Texas Instruments, <NAME> 
//
// 
var fs = require('fs');
var child_process = require('child_process');
var http = require('http');
var url = require('url');
var path = require('path');
var cluster = require('cluster');
var winston = require('winston');
var eeprom = require('./eeprom');
var parse = require('./parse');
var platform = require('./bone').bone;
var functions = require('./functions');

var f = {};

winston.add(winston.transports.File, { filename: '/var/log/bonescript.log' });
winston.setLevels(winston.config.syslog.levels);

var myeval = function(x) {
    winston.info('eval("' + x + '");');
    var y;
    try {
        y = eval(x);
    } catch(ex) {
        y = undefined;
        winston.info('myeval error: ' + ex);
        throw('myeval error: ' + ex);
    }
    winston.info('result = ' + y);
    return(y);
};

var myrequire = function(packageName, onfail) {
    var y = {};
    try {
        y = require(packageName);
        y.exists = true;
    } catch(ex) {
        y.exists = false;
        winston.info("Optional package '" + packageName + "' not loaded");
        if(onfail) onfail();
    }
    return(y);
};

var socketio = myrequire('socket.io', function() {
    winston.info("Dynamic web features not enabled");
});
myrequire('systemd', function() {
    winston.info("Startup as socket-activated service under systemd not enabled");
});

var misc = myrequire('./build/Release/misc');

var gOUTPUT = "out";
var gINPUT = "in";
var gINPUT_PULLUP = "in_pullup";
var gHIGH = 1;
var gLOW = 0;
var gLSBFIRST = 1;  // used in: shiftOut(dataPin, clockPin, bitOrder, val)
var gMSBFIRST = 0;
var gCHANGE = "both";
var gRISING = "rising";
var gFALLING = "falling";

// Keep track of allocated resources
var gpio = [];
var pwm = [];

// returned object has:
//  mux: index of mux mode
//  options: array of mode names
//  slew: 'fast' or 'slow'
//  rx: 'enabled' or 'disabled'
//  pullup: 'diabled', 'pullup' or 'pulldown'
//  pin: key string for pin
//  name: pin name
f.getPinMode = function(pin, callback) {
    winston.debug('getPinMode(' + pin.key + '): ' + muxFile);
    var mode = {'pin': pin.key, 'name': pin.name};
    if(pin.options) mode.options = pin.options;
    var muxFile = '/sys/kernel/debug/omap_mux/' + pin.mux;
    var pinctrlFile = '/sys/kernel/debug/pinctrl/44e10800.pinmux/pins';
    var muxRegOffset = parseInt(pin.muxRegOffset, 16);
    var readOmapMux = function(err, data) {
        if(err) winston.info('readOmapMux error: ' + err);
        mode = parse.modeFromOmapMux(data, mode);
        callback(mode);
    };
    var readPinctrl = function(err, data) {
        if(err) winston.info('readPinctrl error: ' + err);
        mode = parse.modeFromPinctrl(data, muxRegOffset, 0x44e10800, mode);
        callback(mode);
    };
    var tryPinctrl = function(exists) {
        if(exists) {
            fs.readFile(pinctrlFile, 'utf8', readPinctrl);
        } else {
            winston.info('getPinMode(' + pin.key + '): no valid mux data');
            callback(mode);
        }
    };
    var tryOmapMux = function(exists) {
        if(exists) {
            fs.readFile(muxFile, 'utf8', readOmapMux);
        } else {
            path.exists(pinctrlFile, tryPinctrl);
        }
    };
    if(callback) {
        path.exists(muxFile, tryOmapMux);
    } else {
        try {
            var data = fs.readFileSync(muxFile, 'utf8');
            mode = parse.modeFromOmapMux(data, mode);
        } catch(ex) {
            try {
                var data2 = fs.readFileSync(pinctrlFile, 'utf8');
                mode = parse.modeFromPinctrl(data2, muxRegOffset, 0x44e10800, mode);
            } catch(ex2) {
                winston.info('getPinMode(' + pin.key + '): ' + ex2);
            }
        }
        return(mode);
    }
};

f.pinMode = function(pin, direction, mux, pullup, slew, callback) {
    if(direction == gINPUT_PULLUP) pullup = 'pullup';
    pullup = pullup || ((direction == gINPUT) ? 'pulldown' : 'disabled');
    slew = slew || 'fast';
    mux = mux || 7; // default to GPIO mode
    winston.debug('pinmode(' + [pin.key, direction, mux, pullup, slew].join(',') + ')');
    
    if(!pin.mux) {
        winston.info('Invalid pin object for pinMode: ' + pin);
        throw('Invalid pin object for pinMode: ' + pin);
    }

    var muxFile = '/sys/kernel/debug/omap_mux/' + pin.mux;
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    
    // Handle case where pin is allocated as a gpio-led
    if(pin.led) {
        if((direction != gOUTPUT) || (mux != 7)) {                    
            winston.info('pinMode only supports GPIO output for LEDs: ' + pin);
            if(callback) callback(false);
            return(false);
        }
        gpioFile = '/sys/class/leds/beaglebone::' + pin.led + '/brightness';
    }

    // Figure out the desired value
    var pinData = 0;
    if(slew == 'slow') pinData |= 0x40;
    if(direction != gOUTPUT) pinData |= 0x20;
    switch(pullup) {
    case 'disabled':
        pinData |= 0x08;
        break;
    case 'pullup':
        pinData |= 0x10;
        break;
    default:
        break;
    }
    pinData |= (mux & 0x07);
    
    try {
        var fd = fs.openSync(muxFile, 'w');
        fs.writeSync(fd, pinData.toString(16), null);
    } catch(ex) {
        winston.error('Unable to configure mux for pin ' + pin + ': ' + ex);
        gpio[n] = {};
        if(callback) callback(false);
        return(false);
    }

    // Enable GPIO, if not already done
    var n = pin.gpio;
    if(mux == 7) {
        if(!gpio[n] || !gpio[n].path) {
            gpio[n] = {'path': gpioFile};
    
            if(pin.led) {
                var pathA = "/sys/class/leds/beaglebone:";
                var pathB = pathA;
                pathA += ":" + pin.led + "/trigger";
                pathB += "green:" + pin.led + "/trigger";
                if(path.existsSync(pathA)) {
                    fs.writeFileSync(pathA, "gpio");
                } else {
                    if(path.existsSync(pathB)) {
                        fs.writeFileSync(pathB, "gpio");
                    } else {
                        winston.error("Unable to find LED: " + pin.led);
                    }
                }
            } else {
                // Export the GPIO controls
                var exists = path.existsSync(gpioFile);
                if(exists) {
                    winston.debug("gpio: " + n + " already exported.");
                    fs.writeFileSync("/sys/class/gpio/gpio" + n + "/direction",
                        direction, null);
                } else {
                    try {
                        fs.writeFileSync("/sys/class/gpio/export", "" + n, null);
                        fs.writeFileSync("/sys/class/gpio/gpio" + n + "/direction",
                            direction, null);
                    } catch(ex2) {
                        winston.error('Unable to export gpio-' + n + ': ' + ex2);
                        var gpioUsers = fs.readFileSync('/sys/kernel/debug/gpio', 'utf-8');
                        gpioUsers = gpioUsers.split('\n');
                        for(var x in gpioUsers) {
                            var y = gpioUsers[x].match(/gpio-(\d+)\s+\((\S+)\s*\)/);
                            if(y && y[1] == n) {
                                winston.error('gpio-' + n + ' consumed by ' + y[2]);
                            }
                        }
                        gpio[n] = {};
                        if(callback) callback(false);
                        return(false);
                    }
                }
            }
        }
    } else {
        gpio[n] = {};
    }
    
    if(callback) callback(true);
    return(true);
};

f.digitalWrite = function(pin, value, callback) {
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    if(pin.led) {
        var pathA = "/sys/class/leds/beaglebone:";
        var pathB = pathA;
        pathA += ":" + pin.led + "/brightness";
        pathB += "green:" + pin.led + "/brightness";
        if(path.existsSync(pathA)) {
            gpioFile = pathA;
        } else {
            if(path.existsSync(pathB)) {
                gpioFile = pathB;
            } else {
                winston.error("Unable to find LED: " + pin.led);
            }
        }
    }
    winston.debug("gpioFile = " + gpioFile);
    if(callback) {
        fs.writeFile(gpioFile, '' + value, null, callback);
    } else {
        fs.writeFileSync(gpioFile, '' + value, null);
    }
    return(true);
};

f.digitalRead = function(pin, callback) {
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    if(callback) {
        var readFile = function(err, data) {
            if(err) winston.info('digitalRead error: ' + err);
            var value = parseInt(data, 2);
            callback({'value':value});
        };
        fs.readFile(gpioFile, readFile);
        return(true);
    }
    var value = parseInt(fs.readFileSync(gpioFile), 2);
    return(value);
};

f.analogRead = function(pin, callback) {
    var ainFile = '/sys/bus/platform/devices/tsc/ain' + (pin.ain+1);
    if(callback) {
        var readFile = function(err, data) {
            if(err) winston.info('analogRead error: ' + err);
            var value = parseInt(data, 10) / pin.scale;
            callback({'value': value});
        };
        fs.readFile(ainFile, readFile);
        return(true);
    }
    var data = parseInt(fs.readFileSync(ainFile), 10);
    if(isNaN(data)) {
        throw('analogRead(' + pin.key + ') returned ' + data);
    }
    data = data / pin.scale;
    if(isNaN(data)) {
        throw('analogRead(' + pin.key + ') scaled to ' + data);
    }
    return(data);
}; 

f.shiftOut = function(dataPin, clockPin, bitOrder, val, callback) {
  var i;
  var bit;
  for (i = 0; i < 8; i++)  
  {
    if (bitOrder == gLSBFIRST) 
    {
         bit = val & (1 << i);
    } else
    {
         bit = val & (1 << (7 - i));
    }

    f.digitalWrite(dataPin, bit);
    f.digitalWrite(clockPin, gHIGH);
    f.digitalWrite(clockPin, gLOW);
    if(callback) callback();
  }
};

f.attachInterrupt = function(pin, handler, mode, callback) {
    if(!gpio[pin.gpio]) {
        if(callback) callback({'pin':pin, 'attached':false, 'configured':false});
        return(false);
    }
    if(gpio[pin.gpio].intProc) {
        if(callback) callback({'pin':pin, 'attached':false, 'configured':true});
        return(false);
    }
    winston.info('Adding handler ' + handler + ' to pin ' + pin.key);
    var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
    fs.writeFileSync('/sys/class/gpio/gpio' + pin.gpio + '/edge', mode);
    handler = (typeof handler === "string") ? myeval('(' + handler + ')') : handler;
    var intHandler = function(m) {
        var output = handler({'pin':pin, 'value':m.value});
        if(output && callback) callback({'pin':pin, 'output':output});
    };
    var intProc;
    if(child_process.fork) {
        intProc = child_process.fork(__dirname + '/gpioint.js');
    } else {
        var fork = require('fork');
        intProc = fork.fork(__dirname + '/gpioint.js');
    }
    intProc.on('message', intHandler);
    intProc.on('exit', function(code, signal) {
        if(callback) callback({
            'pin':pin,
            'code':code,
            'signal':signal,
            'died':true
        });
    });
    intProc.send({'pin':pin, 'mode':mode, 'file':gpioFile});
    gpio[pin.gpio].intProc = intProc;
    process.on('SIGTERM', function() {
        intProc.kill();
        if(callback) callback({'pin':pin, 'died':true});
    });
    if(callback) callback({'pin':pin, 'attached':true});
    return(true);
};

f.detachInterrupt = function(pin, callback) {
    if(!gpio[pin.gpio] || !gpio[pin.gpio].intProc) {
        if(callback) callback({'pin':pin, 'detached':false});
        return(false);
    }
    gpio[pin.gpio].intProc.kill();
    delete gpio[pin.gpio].intProc;
    if(callback) callback({'pin':pin, 'detached':true});
};

// See http://processors.wiki.ti.com/index.php/AM335x_PWM_Driver's_Guide
f.analogWrite = function(pin, value, freq, callback) {
    freq = freq || 1000;
    var path = '/sys/class/pwm/' + pin.pwm.path;
    //var curMode = getPinMode(pin);
    // Not yet possible to implement this test
    //if(curMode.direction != OUTPUT) {
    //    throw(pin.key + ' must be configured as OUTPUT for analogWrite()');
    //}
    if(!pin.pwm) {
        throw(pin.key + ' does not support analogWrite()');
    }
    if(pwm[pin.pwm.path] && pwm[pin.pwm.path].key) {
        if(pwm[pin.pwm.path].key != pin.key) {
            throw(pin.key + ' requires pwm ' + pin.pwm.name +
                ' but it is already in use by ' +
                pwm[pin.pwm].key
            );
         }
    } else {
        pwm[pin.pwm.path] = {};
        pwm[pin.pwm.path].key = '' + pin.key;
        pwm[pin.pwm.path].freq = freq;
        f.pinMode(pin, gOUTPUT, pin.pwm.muxmode, 'disabled', 'fast');

        // Clear up any unmanaged usage
        fs.writeFileSync(path+'/request', '0');

        // Allocate and configure the PWM
        fs.writeFileSync(path+'/request', '1');
        fs.writeFileSync(path+'/period_freq', freq);
        fs.writeFileSync(path+'/polarity', '0');
        fs.writeFileSync(path+'/run', '1');
    }
    if(pwm[pin.pwm.path].freq != freq) {
        fs.writeFileSync(path+'/run', '0');
        fs.writeFileSync(path+'/duty_percent', '0');
        fs.writeFileSync(path+'/period_freq', freq);
        fs.writeFileSync(path+'/run', '1');
        pwm[pin.pwm.path].freq = freq;
    }
    fs.writeFileSync(path+'/duty_percent', Math.round(value*100));
    if(callback) callback();
};

f.getEeproms = function(callback) {
    var EepromFiles = {
        '/sys/bus/i2c/drivers/at24/0-0050/eeprom': { type: 'bone' },
        '/sys/bus/i2c/drivers/at24/1-0054/eeprom': { type: 'cape' },
        '/sys/bus/i2c/drivers/at24/1-0055/eeprom': { type: 'cape' },
        '/sys/bus/i2c/drivers/at24/1-0056/eeprom': { type: 'cape' },
        '/sys/bus/i2c/drivers/at24/1-0057/eeprom': { type: 'cape' }
    };
    var eeproms = eeprom.readEeproms(EepromFiles);
    if(eeproms == {}) {
        winston.info('No valid EEPROM contents found');
    }
    if(callback) {
        callback(eeproms);
    }
    return(eeproms);
};

var myWorkers = [];
f.addLoop = function(loopFunc, loopDelay, callback) {
{'fraction_non_alphanumeric': 0.1673138260990154, 'fraction_numerical': 0.009499375953404521, 'mean_word_length': 2.0175750601527356, 'pattern_counts': {'":': 4, '<': 15, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3845931', 'n_tokens_mistral': 9872, 'n_tokens_neox': 9510, 'n_words': 1895}