From fc9a4d3d9a25b4c5513889699a6cfb2c8d36c65a Mon Sep 17 00:00:00 2001 From: Doug Turner Date: Tue, 13 Mar 2012 09:59:23 -0700 Subject: [PATCH] Bug 733650 - Use smslib as the device orientation backend on the mac. r=jdm --- dom/system/cocoa/Makefile.in | 1 + dom/system/cocoa/nsDeviceMotionSystem.mm | 136 +--- dom/system/cocoa/smslib.h | 159 ++++ dom/system/cocoa/smslib.mm | 937 +++++++++++++++++++++++ toolkit/content/license.html | 54 ++ 5 files changed, 1163 insertions(+), 124 deletions(-) create mode 100644 dom/system/cocoa/smslib.h create mode 100644 dom/system/cocoa/smslib.mm diff --git a/dom/system/cocoa/Makefile.in b/dom/system/cocoa/Makefile.in index bbc3d49fe45..77041a2b87b 100644 --- a/dom/system/cocoa/Makefile.in +++ b/dom/system/cocoa/Makefile.in @@ -52,6 +52,7 @@ EXPORT_LIBRARY = 1 include $(topsrcdir)/config/config.mk CMMSRCS = \ + smslib.mm \ nsDeviceMotionSystem.mm \ $(NULL) diff --git a/dom/system/cocoa/nsDeviceMotionSystem.mm b/dom/system/cocoa/nsDeviceMotionSystem.mm index 6fc0385c2ee..6eefae0416c 100644 --- a/dom/system/cocoa/nsDeviceMotionSystem.mm +++ b/dom/system/cocoa/nsDeviceMotionSystem.mm @@ -42,8 +42,8 @@ #include #include -#define MODEL_NAME_LENGTH 64 -static char gModelName[MODEL_NAME_LENGTH]; +#import "smslib.h" +#define MEAN_GRAVITY 9.80665 nsDeviceMotionSystem::nsDeviceMotionSystem() { @@ -53,24 +53,6 @@ nsDeviceMotionSystem::~nsDeviceMotionSystem() { } -// Data format returned from IOConnectMethodStructureIStructureO. -// I am not sure what the other bits in this structure are, -// or if there are any, but this has to be 40 bytes long or -// the call to read fails. -// -// Since we make the SmsData struct larger than any members we plan to access we -// keep track of the the size of the part of the struct we plan to access for -// use in bounds checking. -#define SMSDATA_PADDING_SIZE 34 -typedef struct -{ - PRInt16 x; - PRInt16 y; - PRInt16 z; - PRInt8 unknown[SMSDATA_PADDING_SIZE]; -} SmsData; -#define SMSDATA_USED_SIZE (sizeof(SmsData) - SMSDATA_PADDING_SIZE) - void nsDeviceMotionSystem::UpdateHandler(nsITimer *aTimer, void *aClosure) { @@ -79,112 +61,19 @@ nsDeviceMotionSystem::UpdateHandler(nsITimer *aTimer, void *aClosure) NS_ERROR("no self"); return; } + sms_acceleration accel; + smsGetData(&accel); - size_t bufferLen = sizeof(SmsData); - - void * input = malloc(bufferLen); - void * output = malloc(bufferLen); - - if (!input || !output) - return; - - memset(input, 0, bufferLen); - memset(output, 0, bufferLen); - - size_t structureOutputSize = bufferLen; -#if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) - kern_return_t result = ::IOConnectMethodStructureIStructureO(self->mSmsConnection, - 5, /* Magic number for SMCMotionSensor */ - bufferLen, - (IOByteCount*)&structureOutputSize, - input, - output); -#else - kern_return_t result = ::IOConnectCallStructMethod((mach_port_t)self->mSmsConnection, - 5, /* Magic number for SMCMotionSensor */ - input, - bufferLen, - output, - &structureOutputSize); -#endif - - if ((result != kIOReturnSuccess) || (structureOutputSize < SMSDATA_USED_SIZE)) { - free(input); - free(output); - return; - } - - SmsData *data = (SmsData*) output; - - float xf, yf, zf; - - // we want to normalize the return result from the chip to - // something between -1 and 1 where 0 is the balance point. - - const int normalizeFactor = 250.5; - - if (!strcmp(gModelName, "MacBookPro5,1")) { - xf = ((float)data->x) / normalizeFactor; - yf = (((float)data->y) / normalizeFactor) * -1; - zf = ((float)data->z) / normalizeFactor; - } - else if (!strcmp(gModelName, "MacBookPro5,3")) { - xf = ((float)data->y) / normalizeFactor; - yf = (((float)data->x) / normalizeFactor) * -1; - zf = (((float)data->z) / normalizeFactor) * -1; - } - else - { - xf = (((float)data->x) / normalizeFactor) * -1; - yf = ((float)data->y) / normalizeFactor; - zf = ((float)data->z) / normalizeFactor; - } - - free(input); - free(output); - - self->DeviceMotionChanged(nsIDeviceMotionData::TYPE_ACCELERATION, xf, yf, zf ); + self->DeviceMotionChanged(nsIDeviceMotionData::TYPE_ACCELERATION, + accel.x * MEAN_GRAVITY, + accel.y * MEAN_GRAVITY, + accel.z * MEAN_GRAVITY); } void nsDeviceMotionSystem::Startup() { - // we can fail, and that just means the caller will not see any changes. - - mach_port_t port; - kern_return_t result = ::IOMasterPort(MACH_PORT_NULL, &port); - if (result != kIOReturnSuccess) - return; - - CFMutableDictionaryRef dict = ::IOServiceMatching("SMCMotionSensor"); - if (!dict) - return; - - io_iterator_t iter; - result = ::IOServiceGetMatchingServices(port, dict, &iter); - if (result != kIOReturnSuccess) - return; - - io_object_t device = ::IOIteratorNext(iter); - - ::IOObjectRelease(iter); - - if (!device) - return; - - result = ::IOServiceOpen(device, mach_task_self(), 0, &mSmsConnection); - ::IOObjectRelease(device); - - if (result != kIOReturnSuccess) - return; - - mach_port_deallocate(mach_task_self(), port); - - /* get the version of the hardware we are running on. */ - int mib[2]; - size_t len = MODEL_NAME_LENGTH; - mib[0] = CTL_HW; - mib[1] = HW_MODEL; - sysctl(mib, 2, gModelName, &len, NULL, 0); + smsStartup(nil, nil); + smsLoadCalibration(); mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1"); if (mUpdateTimer) @@ -196,12 +85,11 @@ void nsDeviceMotionSystem::Startup() void nsDeviceMotionSystem::Shutdown() { - if (mSmsConnection) - ::IOServiceClose(mSmsConnection); - if (mUpdateTimer) { mUpdateTimer->Cancel(); mUpdateTimer = nsnull; } + + smsShutdown(); } diff --git a/dom/system/cocoa/smslib.h b/dom/system/cocoa/smslib.h new file mode 100644 index 00000000000..2f0b2664e45 --- /dev/null +++ b/dom/system/cocoa/smslib.h @@ -0,0 +1,159 @@ +/* + * smslib.h + * + * SMSLib Sudden Motion Sensor Access Library + * Copyright (c) 2010 Suitable Systems + * All rights reserved. + * + * Developed by: Daniel Griscom + * Suitable Systems + * http://www.suitable.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal with 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: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimers. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the names of Suitable Systems nor the names of its + * contributors may be used to endorse or promote products derived from + * this Software without specific prior written permission. + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + * For more information about SMSLib, see + * + * or contact + * Daniel Griscom + * Suitable Systems + * 1 Centre Street, Suite 204 + * Wakefield, MA 01880 + * (781) 665-0053 + * + */ + +#import + +#define SMSLIB_VERSION "1.8" + +#pragma mark Structure definitions + +// Structure for specifying a 3-axis acceleration. 0.0 means "zero gravities", +// 1.0 means "one gravity". +typedef struct sms_acceleration { + float x; // Right-left acceleration (positive is rightwards) + float y; // Front-rear acceleration (positive is rearwards) + float z; // Up-down acceleration (positive is upwards) +} sms_acceleration; + +// Structure for specifying a calibration. +typedef struct sms_calibration { + float zeros[3]; // Zero points for three axes (X, Y, Z) + float onegs[3]; // One gravity values for three axes +} sms_calibration; + +#pragma mark Return value definitions + +// These are the return values for accelStartup(), giving the +// various stages where the most successful attempt at accessing +// the accelerometer failed. The higher the value, the further along the +// software progressed before failing. The options are: +// - Didn't match model name +#define SMS_FAIL_MODEL (-7) +// - Failure getting dictionary matching desired services +#define SMS_FAIL_DICTIONARY (-6) +// - Failure getting list of services +#define SMS_FAIL_LIST_SERVICES (-5) +// - Failure if list of services is empty. The process generally fails +// here if run on a machine without a Sudden Motion Sensor. +#define SMS_FAIL_NO_SERVICES (-4) +// - Failure if error opening device. +#define SMS_FAIL_OPENING (-3) +// - Failure if opened, but didn't get a connection +#define SMS_FAIL_CONNECTION (-2) +// - Failure if couldn't access connction using given function and size. This +// is where the process would probably fail with a change in Apple's API. +// Driver problems often also cause failures here. +#define SMS_FAIL_ACCESS (-1) +// - Success! +#define SMS_SUCCESS (0) + +#pragma mark Function declarations + +// This starts up the accelerometer code, trying each possible sensor +// specification. Note that for logging purposes it +// takes an object and a selector; the object's selector is then invoked +// with a single NSString as argument giving progress messages. Example +// logging method: +// - (void)logMessage: (NSString *)theString +// which would be used in accelStartup's invocation thusly: +// result = accelStartup(self, @selector(logMessage:)); +// If the object is nil, then no logging is done. Sets calibation from built-in +// value table. Returns ACCEL_SUCCESS for success, and other (negative) +// values for various failures (returns value indicating result of +// most successful trial). +int smsStartup(id logObject, SEL logSelector); + +// This starts up the library in debug mode, ignoring the actual hardware. +// Returned data is in the form of 1Hz sine waves, with the X, Y and Z +// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5); +// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0, +// Z axes centered on 1 (calibrated) or 256 (uncalibrated). +// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS. +int smsDebugStartup(id logObject, SEL logSelector); + +// Returns the current calibration values. +void smsGetCalibration(sms_calibration *calibrationRecord); + +// Sets the calibration, but does NOT store it as a preference. If the argument +// is nil then the current calibration is set from the built-in value table. +void smsSetCalibration(sms_calibration *calibrationRecord); + +// Stores the current calibration values as a stored preference. +void smsStoreCalibration(void); + +// Loads the stored preference values into the current calibration. +// Returns YES if successful. +BOOL smsLoadCalibration(void); + +// Deletes any stored calibration, and then takes the current calibration values +// from the built-in value table. +void smsDeleteCalibration(void); + +// Fills in the accel record with calibrated acceleration data. Takes +// 1-2ms to return a value. Returns 0 if success, error number if failure. +int smsGetData(sms_acceleration *accel); + +// Fills in the accel record with uncalibrated acceleration data. +// Returns 0 if success, error number if failure. +int smsGetUncalibratedData(sms_acceleration *accel); + +// Returns the length of a raw block of data for the current type of sensor. +int smsGetBufferLength(void); + +// Takes a pointer to accelGetRawLength() bytes; sets those bytes +// to return value from sensor. Make darn sure the buffer length is right! +void smsGetBufferData(char *buffer); + +// This returns an NSString describing the current calibration in +// human-readable form. Also include a description of the machine. +NSString *smsGetCalibrationDescription(void); + +// Shuts down the accelerometer. +void smsShutdown(void); + diff --git a/dom/system/cocoa/smslib.mm b/dom/system/cocoa/smslib.mm new file mode 100644 index 00000000000..8761a81f48f --- /dev/null +++ b/dom/system/cocoa/smslib.mm @@ -0,0 +1,937 @@ +/* + * smslib.m + * + * SMSLib Sudden Motion Sensor Access Library + * Copyright (c) 2010 Suitable Systems + * All rights reserved. + * + * Developed by: Daniel Griscom + * Suitable Systems + * http://www.suitable.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal with 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: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimers. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the names of Suitable Systems nor the names of its + * contributors may be used to endorse or promote products derived from + * this Software without specific prior written permission. + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + * For more information about SMSLib, see + * + * or contact + * Daniel Griscom + * Suitable Systems + * 1 Centre Street, Suite 204 + * Wakefield, MA 01880 + * (781) 665-0053 + * + */ + +#import +#import +#import +#import "smslib.h" + +#pragma mark Internal structures + +// Represents a single axis of a type of sensor. +typedef struct axisStruct { + int enabled; // Non-zero if axis is valid in this sensor + int index; // Location in struct of first byte + int size; // Number of bytes + float zerog; // Value meaning "zero g" + float oneg; // Change in value meaning "increase of one g" + // (can be negative if axis sensor reversed) +} axisStruct; + +// Represents the configuration of a type of sensor. +typedef struct sensorSpec { + char *model; // Prefix of model to be tested + char *name; // Name of device to be read + unsigned int function; // Kernel function index + int recordSize; // Size of record to be sent/received + axisStruct axes[3]; // Description of three axes (X, Y, Z) +} sensorSpec; + +// Configuration of all known types of sensors. The configurations are +// tried in order until one succeeds in returning data. +// All default values are set here, but each axis' zerog and oneg values +// may be changed to saved (calibrated) values. +// +// These values came from SeisMaCalibrate calibration reports. In general I've +// found the following: +// - All Intel-based SMSs have 250 counts per g, centered on 0, but the signs +// are different (and in one case two axes are swapped) +// - PowerBooks and iBooks all have sensors centered on 0, and reading +// 50-53 steps per gravity (but with differing polarities!) +// - PowerBooks and iBooks of the same model all have the same axis polarities +// - PowerBook and iBook access methods are model- and OS version-specific +// +// So, the sequence of tests is: +// - Try model-specific access methods. Note that the test is for a match to the +// beginning of the model name, e.g. the record with model name "MacBook" +// matches computer models "MacBookPro1,2" and "MacBook1,1" (and "" +// matches any model). +// - If no model-specific record's access fails, then try each model-independent +// access method in order, stopping when one works. +static const sensorSpec sensors[] = { + // ****** Model-dependent methods ****** + // The PowerBook5,6 is one of the G4 models that seems to lose + // SMS access until the next reboot. + {"PowerBook5,6", "IOI2CMotionSensor", 21, 60, { + {1, 0, 1, 0, 51.5}, + {1, 1, 1, 0, -51.5}, + {1, 2, 1, 0, -51.5} + } + }, + // The PowerBook5,7 is one of the G4 models that seems to lose + // SMS access until the next reboot. + {"PowerBook5,7", "IOI2CMotionSensor", 21, 60, { + {1, 0, 1, 0, 51.5}, + {1, 1, 1, 0, 51.5}, + {1, 2, 1, 0, 51.5} + } + }, + // Access seems to be reliable on the PowerBook5,8 + {"PowerBook5,8", "PMUMotionSensor", 21, 60, { + {1, 0, 1, 0, -51.5}, + {1, 1, 1, 0, 51.5}, + {1, 2, 1, 0, -51.5} + } + }, + // Access seems to be reliable on the PowerBook5,9 + {"PowerBook5,9", "PMUMotionSensor", 21, 60, { + {1, 0, 1, 0, 51.5}, + {1, 1, 1, 0, -51.5}, + {1, 2, 1, 0, -51.5} + } + }, + // The PowerBook6,7 is one of the G4 models that seems to lose + // SMS access until the next reboot. + {"PowerBook6,7", "IOI2CMotionSensor", 21, 60, { + {1, 0, 1, 0, 51.5}, + {1, 1, 1, 0, 51.5}, + {1, 2, 1, 0, 51.5} + } + }, + // The PowerBook6,8 is one of the G4 models that seems to lose + // SMS access until the next reboot. + {"PowerBook6,8", "IOI2CMotionSensor", 21, 60, { + {1, 0, 1, 0, 51.5}, + {1, 1, 1, 0, 51.5}, + {1, 2, 1, 0, 51.5} + } + }, + // MacBook Pro Core 2 Duo 17". Note the reversed Y and Z axes. + {"MacBookPro2,1", "SMCMotionSensor", 5, 40, { + {1, 0, 2, 0, 251}, + {1, 2, 2, 0, -251}, + {1, 4, 2, 0, -251} + } + }, + // MacBook Pro Core 2 Duo 15" AND 17" with LED backlight, introduced June '07. + // NOTE! The 17" machines have the signs of their X and Y axes reversed + // from this calibration, but there's no clear way to discriminate between + // the two machines. + {"MacBookPro3,1", "SMCMotionSensor", 5, 40, { + {1, 0, 2, 0, -251}, + {1, 2, 2, 0, 251}, + {1, 4, 2, 0, -251} + } + }, + // ... specs? + {"MacBook5,2", "SMCMotionSensor", 5, 40, { + {1, 0, 2, 0, -251}, + {1, 2, 2, 0, 251}, + {1, 4, 2, 0, -251} + } + }, + // ... specs? + {"MacBookPro5,1", "SMCMotionSensor", 5, 40, { + {1, 0, 2, 0, -251}, + {1, 2, 2, 0, -251}, + {1, 4, 2, 0, 251} + } + }, + // ... specs? + {"MacBookPro5,2", "SMCMotionSensor", 5, 40, { + {1, 0, 2, 0, -251}, + {1, 2, 2, 0, -251}, + {1, 4, 2, 0, 251} + } + }, + // This is speculative, based on a single user's report. Looks like the X and Y axes + // are swapped. This is true for no other known Appple laptop. + {"MacBookPro5,3", "SMCMotionSensor", 5, 40, { + {1, 2, 2, 0, -251}, + {1, 0, 2, 0, -251}, + {1, 4, 2, 0, -251} + } + }, + // ... specs? + {"MacBookPro5,4", "SMCMotionSensor", 5, 40, { + {1, 0, 2, 0, -251}, + {1, 2, 2, 0, -251}, + {1, 4, 2, 0, 251} + } + }, + // ****** Model-independent methods ****** + // Seen once with PowerBook6,8 under system 10.3.9; I suspect + // other G4-based 10.3.* systems might use this + {"", "IOI2CMotionSensor", 24, 60, { + {1, 0, 1, 0, 51.5}, + {1, 1, 1, 0, 51.5}, + {1, 2, 1, 0, 51.5} + } + }, + // PowerBook5,6 , PowerBook5,7 , PowerBook6,7 , PowerBook6,8 + // under OS X 10.4.* + {"", "IOI2CMotionSensor", 21, 60, { + {1, 0, 1, 0, 51.5}, + {1, 1, 1, 0, 51.5}, + {1, 2, 1, 0, 51.5} + } + }, + // PowerBook5,8 , PowerBook5,9 under OS X 10.4.* + {"", "PMUMotionSensor", 21, 60, { + // Each has two out of three gains negative, but it's different + // for the different models. So, this will be right in two out + // of three axis for either model. + {1, 0, 1, 0, -51.5}, + {1, 1, 1, -6, -51.5}, + {1, 2, 1, 0, -51.5} + } + }, + // All MacBook, MacBookPro models. Hardware (at least on early MacBookPro 15") + // is Kionix KXM52-1050 three-axis accelerometer chip. Data is at + // http://kionix.com/Product-Index/product-index.htm. Specific MB and MBP models + // that use this are: + // MacBook1,1 + // MacBook2,1 + // MacBook3,1 + // MacBook4,1 + // MacBook5,1 + // MacBook6,1 + // MacBookAir1,1 + // MacBookPro1,1 + // MacBookPro1,2 + // MacBookPro4,1 + // MacBookPro5,5 + {"", "SMCMotionSensor", 5, 40, { + {1, 0, 2, 0, 251}, + {1, 2, 2, 0, 251}, + {1, 4, 2, 0, 251} + } + } +}; + +#define SENSOR_COUNT (sizeof(sensors)/sizeof(sensorSpec)) + +#pragma mark Internal prototypes + +static int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector); +static float getAxis(int which, int calibrated); +static int signExtend(int value, int size); +static NSString *getModelName(void); +static NSString *getOSVersion(void); +static BOOL loadCalibration(void); +static void storeCalibration(void); +static void defaultCalibration(void); +static void deleteCalibration(void); +static int prefIntRead(NSString *prefName, BOOL *success); +static void prefIntWrite(NSString *prefName, int prefValue); +static float prefFloatRead(NSString *prefName, BOOL *success); +static void prefFloatWrite(NSString *prefName, float prefValue); +static void prefDelete(NSString *prefName); +static void prefSynchronize(void); +// static long getMicroseconds(void); +float fakeData(NSTimeInterval time); + +#pragma mark Static variables + +static int debugging = NO; // True if debugging (synthetic data) +static io_connect_t connection; // Connection for reading accel values +static int running = NO; // True if we successfully started +static int sensorNum = 0; // The current index into sensors[] +static char *serviceName; // The name of the current service +static char *iRecord, *oRecord; // Pointers to read/write records for sensor +static int recordSize; // Size of read/write records +static unsigned int function; // Which kernel function should be used +static float zeros[3]; // X, Y and Z zero calibration values +static float onegs[3]; // X, Y and Z one-g calibration values + +#pragma mark Defines + +// Pattern for building axis letter from axis number +#define INT_TO_AXIS(a) (a == 0 ? @"X" : a == 1 ? @"Y" : @"Z") +// Name of configuration for given axis' zero (axis specified by integer) +#define ZERO_NAME(a) [NSString stringWithFormat:@"%@-Axis-Zero", INT_TO_AXIS(a)] +// Name of configuration for given axis' oneg (axis specified by integer) +#define ONEG_NAME(a) [NSString stringWithFormat:@"%@-Axis-One-g", INT_TO_AXIS(a)] +// Name of "Is calibrated" preference +#define CALIBRATED_NAME (@"Calibrated") +// Application domain for SeisMac library +#define APP_ID ((CFStringRef)@"com.suitable.SeisMacLib") + +// These #defines make the accelStartup code a LOT easier to read. +#define LOG(message) \ + if (logObject) { \ + [logObject performSelector:logSelector withObject:message]; \ + } +#define LOG_ARG(format, var1) \ + if (logObject) { \ + [logObject performSelector:logSelector \ + withObject:[NSString stringWithFormat:format, var1]]; \ + } +#define LOG_2ARG(format, var1, var2) \ + if (logObject) { \ + [logObject performSelector:logSelector \ + withObject:[NSString stringWithFormat:format, var1, var2]]; \ + } +#define LOG_3ARG(format, var1, var2, var3) \ + if (logObject) { \ + [logObject performSelector:logSelector \ + withObject:[NSString stringWithFormat:format, var1, var2, var3]]; \ + } + +#pragma mark Function definitions + +// This starts up the accelerometer code, trying each possible sensor +// specification. Note that for logging purposes it +// takes an object and a selector; the object's selector is then invoked +// with a single NSString as argument giving progress messages. Example +// logging method: +// - (void)logMessage: (NSString *)theString +// which would be used in accelStartup's invocation thusly: +// result = accelStartup(self, @selector(logMessage:)); +// If the object is nil, then no logging is done. Sets calibation from built-in +// value table. Returns ACCEL_SUCCESS for success, and other (negative) +// values for various failures (returns value indicating result of +// most successful trial). +int smsStartup(id logObject, SEL logSelector) { + io_iterator_t iterator; + io_object_t device; + kern_return_t result; + sms_acceleration accel; + int failure_result = SMS_FAIL_MODEL; + + running = NO; + debugging = NO; + + NSString *modelName = getModelName(); + + LOG_ARG(@"Machine model: %@\n", modelName); + LOG_ARG(@"OS X version: %@\n", getOSVersion()); + LOG_ARG(@"Accelerometer library version: %s\n", SMSLIB_VERSION); + + for (sensorNum = 0; sensorNum < SENSOR_COUNT; sensorNum++) { + + // Set up all specs for this type of sensor + serviceName = sensors[sensorNum].name; + recordSize = sensors[sensorNum].recordSize; + function = sensors[sensorNum].function; + + LOG_3ARG(@"Trying service \"%s\" with selector %d and %d byte record:\n", + serviceName, function, recordSize); + + NSString *targetName = [NSString stringWithCString:sensors[sensorNum].model + encoding:NSMacOSRomanStringEncoding]; + LOG_ARG(@" Comparing model name to target \"%@\": ", targetName); + if ([targetName length] == 0 || [modelName hasPrefix:targetName]) { + LOG(@"success.\n"); + } else { + LOG(@"failure.\n"); + // Don't need to increment failure_result. + continue; + } + + LOG(@" Fetching dictionary for service: "); + CFMutableDictionaryRef dict = IOServiceMatching(serviceName); + + if (dict) { + LOG(@"success.\n"); + } else { + LOG(@"failure.\n"); + if (failure_result < SMS_FAIL_DICTIONARY) { + failure_result = SMS_FAIL_DICTIONARY; + } + continue; + } + + LOG(@" Getting list of matching services: "); + result = IOServiceGetMatchingServices(kIOMasterPortDefault, + dict, + &iterator); + + if (result == KERN_SUCCESS) { + LOG(@"success.\n"); + } else { + LOG_ARG(@"failure, with return value 0x%x.\n", result); + if (failure_result < SMS_FAIL_LIST_SERVICES) { + failure_result = SMS_FAIL_LIST_SERVICES; + } + continue; + } + + LOG(@" Getting first device in list: "); + device = IOIteratorNext(iterator); + + if (device == 0) { + LOG(@"failure.\n"); + if (failure_result < SMS_FAIL_NO_SERVICES) { + failure_result = SMS_FAIL_NO_SERVICES; + } + continue; + } else { + LOG(@"success.\n"); + LOG(@" Opening device: "); + } + + result = IOServiceOpen(device, mach_task_self(), 0, &connection); + + if (result != KERN_SUCCESS) { + LOG_ARG(@"failure, with return value 0x%x.\n", result); + IOObjectRelease(device); + if (failure_result < SMS_FAIL_OPENING) { + failure_result = SMS_FAIL_OPENING; + } + continue; + } else if (connection == 0) { + LOG_ARG(@"'success', but didn't get a connection.\n", result); + IOObjectRelease(device); + if (failure_result < SMS_FAIL_CONNECTION) { + failure_result = SMS_FAIL_CONNECTION; + } + continue; + } else { + IOObjectRelease(device); + LOG(@"success.\n"); + } + LOG(@" Testing device.\n"); + + defaultCalibration(); + + iRecord = (char*) malloc(recordSize); + oRecord = (char*) malloc(recordSize); + + running = YES; + result = getData(&accel, true, logObject, logSelector); + running = NO; + + if (result) { + LOG_ARG(@" Failure testing device, with result 0x%x.\n", result); + free(iRecord); + iRecord = 0; + free(oRecord); + oRecord = 0; + if (failure_result < SMS_FAIL_ACCESS) { + failure_result = SMS_FAIL_ACCESS; + } + continue; + } else { + LOG(@" Success testing device!\n"); + running = YES; + return SMS_SUCCESS; + } + } + return failure_result; +} + +// This starts up the library in debug mode, ignoring the actual hardware. +// Returned data is in the form of 1Hz sine waves, with the X, Y and Z +// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5); +// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0, +// Z axes centered on 1 (calibrated) or 256 (uncalibrated). +// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS. +int smsDebugStartup(id logObject, SEL logSelector) { + LOG(@"Starting up in debug mode\n"); + debugging = YES; + return SMS_SUCCESS; +} + +// Returns the current calibration values. +void smsGetCalibration(sms_calibration *calibrationRecord) { + int x; + + for (x = 0; x < 3; x++) { + calibrationRecord->zeros[x] = (debugging ? 0 : zeros[x]); + calibrationRecord->onegs[x] = (debugging ? 256 : onegs[x]); + } +} + +// Sets the calibration, but does NOT store it as a preference. If the argument +// is nil then the current calibration is set from the built-in value table. +void smsSetCalibration(sms_calibration *calibrationRecord) { + int x; + + if (!debugging) { + if (calibrationRecord) { + for (x = 0; x < 3; x++) { + zeros[x] = calibrationRecord->zeros[x]; + onegs[x] = calibrationRecord->onegs[x]; + } + } else { + defaultCalibration(); + } + } +} + +// Stores the current calibration values as a stored preference. +void smsStoreCalibration(void) { + if (!debugging) + storeCalibration(); +} + +// Loads the stored preference values into the current calibration. +// Returns YES if successful. +BOOL smsLoadCalibration(void) { + if (debugging) { + return YES; + } else if (loadCalibration()) { + return YES; + } else { + defaultCalibration(); + return NO; + } +} + +// Deletes any stored calibration, and then takes the current calibration values +// from the built-in value table. +void smsDeleteCalibration(void) { + if (!debugging) { + deleteCalibration(); + defaultCalibration(); + } +} + +// Fills in the accel record with calibrated acceleration data. Takes +// 1-2ms to return a value. Returns 0 if success, error number if failure. +int smsGetData(sms_acceleration *accel) { + NSTimeInterval time; + if (debugging) { + usleep(1500); // Usually takes 1-2 milliseconds + time = [NSDate timeIntervalSinceReferenceDate]; + accel->x = fakeData(time)/5; + accel->y = fakeData(time - 1)/5; + accel->z = fakeData(time - 2)/5 + 1.0; + return true; + } else { + return getData(accel, true, nil, nil); + } +} + +// Fills in the accel record with uncalibrated acceleration data. +// Returns 0 if success, error number if failure. +int smsGetUncalibratedData(sms_acceleration *accel) { + NSTimeInterval time; + if (debugging) { + usleep(1500); // Usually takes 1-2 milliseconds + time = [NSDate timeIntervalSinceReferenceDate]; + accel->x = fakeData(time) * 256 / 5; + accel->y = fakeData(time - 1) * 256 / 5; + accel->z = fakeData(time - 2) * 256 / 5 + 256; + return true; + } else { + return getData(accel, false, nil, nil); + } +} + +// Returns the length of a raw block of data for the current type of sensor. +int smsGetBufferLength(void) { + if (debugging) { + return 0; + } else if (running) { + return sensors[sensorNum].recordSize; + } else { + return 0; + } +} + +// Takes a pointer to accelGetRawLength() bytes; sets those bytes +// to return value from sensor. Make darn sure the buffer length is right! +void smsGetBufferData(char *buffer) { + IOItemCount iSize = recordSize; + IOByteCount oSize = recordSize; + kern_return_t result; + + if (debugging || running == NO) { + return; + } + + memset(iRecord, 1, iSize); + memset(buffer, 0, oSize); +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + const size_t InStructSize = recordSize; + size_t OutStructSize = recordSize; + result = IOConnectCallStructMethod(connection, + function, // magic kernel function number + (const void *)iRecord, + InStructSize, + (void *)buffer, + &OutStructSize + ); +#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050 + result = IOConnectMethodStructureIStructureO(connection, + function, // magic kernel function number + iSize, + &oSize, + iRecord, + buffer + ); +#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050 + + if (result != KERN_SUCCESS) { + running = NO; + } +} + +// This returns an NSString describing the current calibration in +// human-readable form. Also include a description of the machine. +NSString *smsGetCalibrationDescription(void) { + BOOL success; + NSMutableString *s = [[NSMutableString alloc] init]; + + if (debugging) { + [s release]; + return @"Debugging!"; + } + + [s appendString:@"---- SeisMac Calibration Record ----\n \n"]; + [s appendFormat:@"Machine model: %@\n", + getModelName()]; + [s appendFormat:@"OS X build: %@\n", + getOSVersion()]; + [s appendFormat:@"SeisMacLib version %s, record %d\n \n", + SMSLIB_VERSION, sensorNum]; + [s appendFormat:@"Using service \"%s\", function index %d, size %d\n \n", + serviceName, function, recordSize]; + if (prefIntRead(CALIBRATED_NAME, &success) && success) { + [s appendString:@"Calibration values (from calibration):\n"]; + } else { + [s appendString:@"Calibration values (from defaults):\n"]; + } + [s appendFormat:@" X-Axis-Zero = %.2f\n", zeros[0]]; + [s appendFormat:@" X-Axis-One-g = %.2f\n", onegs[0]]; + [s appendFormat:@" Y-Axis-Zero = %.2f\n", zeros[1]]; + [s appendFormat:@" Y-Axis-One-g = %.2f\n", onegs[1]]; + [s appendFormat:@" Z-Axis-Zero = %.2f\n", zeros[2]]; + [s appendFormat:@" Z-Axis-One-g = %.2f\n \n", onegs[2]]; + [s appendString:@"---- End Record ----\n"]; + return s; +} + +// Shuts down the accelerometer. +void smsShutdown(void) { + if (!debugging) { + running = NO; + if (iRecord) free(iRecord); + if (oRecord) free(oRecord); + IOServiceClose(connection); + } +} + +#pragma mark Internal functions + +// Loads the current calibration from the stored preferences. +// Returns true iff successful. +BOOL loadCalibration(void) { + BOOL thisSuccess, allSuccess; + int x; + + prefSynchronize(); + + if (prefIntRead(CALIBRATED_NAME, &thisSuccess) && thisSuccess) { + // Calibrated. Set all values from saved values. + allSuccess = YES; + for (x = 0; x < 3; x++) { + zeros[x] = prefFloatRead(ZERO_NAME(x), &thisSuccess); + allSuccess &= thisSuccess; + onegs[x] = prefFloatRead(ONEG_NAME(x), &thisSuccess); + allSuccess &= thisSuccess; + } + return allSuccess; + } + + return NO; +} + +// Stores the current calibration into the stored preferences. +static void storeCalibration(void) { + int x; + prefIntWrite(CALIBRATED_NAME, 1); + for (x = 0; x < 3; x++) { + prefFloatWrite(ZERO_NAME(x), zeros[x]); + prefFloatWrite(ONEG_NAME(x), onegs[x]); + } + prefSynchronize(); +} + + +// Sets the calibration to its default values. +void defaultCalibration(void) { + int x; + for (x = 0; x < 3; x++) { + zeros[x] = sensors[sensorNum].axes[x].zerog; + onegs[x] = sensors[sensorNum].axes[x].oneg; + } +} + +// Deletes the stored preferences. +static void deleteCalibration(void) { + int x; + + prefDelete(CALIBRATED_NAME); + for (x = 0; x < 3; x++) { + prefDelete(ZERO_NAME(x)); + prefDelete(ONEG_NAME(x)); + } + prefSynchronize(); +} + +// Read a named floating point value from the stored preferences. Sets +// the success boolean based on, you guessed it, whether it succeeds. +static float prefFloatRead(NSString *prefName, BOOL *success) { + float result = 0.0f; + + CFPropertyListRef ref = CFPreferencesCopyAppValue((CFStringRef)prefName, + APP_ID); + // If there isn't such a preference, fail + if (ref == NULL) { + *success = NO; + return result; + } + CFTypeID typeID = CFGetTypeID(ref); + // Is it a number? + if (typeID == CFNumberGetTypeID()) { + // Is it a floating point number? + if (CFNumberIsFloatType((CFNumberRef)ref)) { + // Yup: grab it. + *success = CFNumberGetValue((__CFNumber*)ref, kCFNumberFloat32Type, &result); + } else { + // Nope: grab as an integer, and convert to a float. + long num; + if (CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &num)) { + result = num; + *success = YES; + } else { + *success = NO; + } + } + // Or is it a string (e.g. set by the command line "defaults" command)? + } else if (typeID == CFStringGetTypeID()) { + result = (float)CFStringGetDoubleValue((CFStringRef)ref); + *success = YES; + } else { + // Can't convert to a number: fail. + *success = NO; + } + CFRelease(ref); + return result; +} + +// Writes a named floating point value to the stored preferences. +static void prefFloatWrite(NSString *prefName, float prefValue) { + CFNumberRef cfFloat = CFNumberCreate(kCFAllocatorDefault, + kCFNumberFloatType, + &prefValue); + CFPreferencesSetAppValue((CFStringRef)prefName, + cfFloat, + APP_ID); + CFRelease(cfFloat); +} + +// Reads a named integer value from the stored preferences. +static int prefIntRead(NSString *prefName, BOOL *success) { + Boolean internalSuccess; + CFIndex result = CFPreferencesGetAppIntegerValue((CFStringRef)prefName, + APP_ID, + &internalSuccess); + *success = internalSuccess; + + return result; +} + +// Writes a named integer value to the stored preferences. +static void prefIntWrite(NSString *prefName, int prefValue) { + CFPreferencesSetAppValue((CFStringRef)prefName, + (CFNumberRef)[NSNumber numberWithInt:prefValue], + APP_ID); +} + +// Deletes the named preference values. +static void prefDelete(NSString *prefName) { + CFPreferencesSetAppValue((CFStringRef)prefName, + NULL, + APP_ID); +} + +// Synchronizes the local preferences with the stored preferences. +static void prefSynchronize(void) { + CFPreferencesAppSynchronize(APP_ID); +} + +// Internal version of accelGetData, with logging +int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector) { + IOItemCount iSize = recordSize; + IOByteCount oSize = recordSize; + kern_return_t result; + + if (running == NO) { + return -1; + } + + memset(iRecord, 1, iSize); + memset(oRecord, 0, oSize); + + LOG_2ARG(@" Querying device: ", + sensors[sensorNum].function, sensors[sensorNum].recordSize); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + const size_t InStructSize = recordSize; + size_t OutStructSize = recordSize; + result = IOConnectCallStructMethod(connection, + function, // magic kernel function number + (const void *)iRecord, + InStructSize, + (void *)oRecord, + &OutStructSize + ); +#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050 + result = IOConnectMethodStructureIStructureO(connection, + function, // magic kernel function number + iSize, + &oSize, + iRecord, + oRecord + ); +#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050 + + if (result != KERN_SUCCESS) { + LOG(@"failed.\n"); + running = NO; + return result; + } else { + LOG(@"succeeded.\n"); + + accel->x = getAxis(0, calibrated); + accel->y = getAxis(1, calibrated); + accel->z = getAxis(2, calibrated); + return 0; + } +} + +// Given the returned record, extracts the value of the given axis. If +// calibrated, then zero G is 0.0, and one G is 1.0. +float getAxis(int which, int calibrated) { + // Get various values (to make code cleaner) + int indx = sensors[sensorNum].axes[which].index; + int size = sensors[sensorNum].axes[which].size; + float zerog = zeros[which]; + float oneg = onegs[which]; + // Storage for value to be returned + int value = 0; + + // Although the values in the returned record should have the proper + // endianness, we still have to get it into the proper end of value. +#if (BYTE_ORDER == BIG_ENDIAN) + // On PowerPC processors + memcpy(((char *)&value) + (sizeof(int) - size), &oRecord[indx], size); +#endif +#if (BYTE_ORDER == LITTLE_ENDIAN) + // On Intel processors + memcpy(&value, &oRecord[indx], size); +#endif + + value = signExtend(value, size); + + if (calibrated) { + // Scale and shift for zero. + return ((float)(value - zerog)) / oneg; + } else { + return value; + } +} + +// Extends the sign, given the length of the value. +int signExtend(int value, int size) { + // Extend sign + switch (size) { + case 1: + if (value & 0x00000080) + value |= 0xffffff00; + break; + case 2: + if (value & 0x00008000) + value |= 0xffff0000; + break; + case 3: + if (value & 0x00800000) + value |= 0xff000000; + break; + } + return value; +} + +// Returns the model name of the computer (e.g. "MacBookPro1,1") +NSString *getModelName(void) { + char model[32]; + size_t len = sizeof(model); + int name[2] = {CTL_HW, HW_MODEL}; + NSString *result; + + if (sysctl(name, 2, &model, &len, NULL, 0) == 0) { + result = [NSString stringWithFormat:@"%s", model]; + } else { + result = @""; + } + + return result; +} + +// Returns the current OS X version and build (e.g. "10.4.7 (build 8J2135a)") +NSString *getOSVersion(void) { + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: + @"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *versionString = [dict objectForKey:@"ProductVersion"]; + NSString *buildString = [dict objectForKey:@"ProductBuildVersion"]; + NSString *wholeString = [NSString stringWithFormat:@"%@ (build %@)", + versionString, buildString]; + return wholeString; +} + +// Returns time within the current second in microseconds. +// long getMicroseconds() { +// struct timeval t; +// gettimeofday(&t, 0); +// return t.tv_usec; +//} + +// Returns fake data given the time. Range is +/-1. +float fakeData(NSTimeInterval time) { + long secs = lround(floor(time)); + int secsMod3 = secs % 3; + double angle = time * 10 * M_PI * 2; + double mag = exp(-(time - (secs - secsMod3)) * 2); + return sin(angle) * mag; +} + diff --git a/toolkit/content/license.html b/toolkit/content/license.html index 27775536b33..17b2bebc351 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -94,6 +94,7 @@
  • Skia License
  • Snappy License
  • Sparkle License
  • +
  • Suitable Systems License
  • SunSoft License
  • University of California License
  • University of Cambridge License
  • @@ -2234,6 +2235,59 @@ 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. +
    + +

    Suitable Systems License

    + +

    This license applies to certain files in the directory + dom/system/cocoa/.

    + +
    +SMSLib Sudden Motion Sensor Access Library
    +Copyright (c) 2010 Suitable Systems
    +All rights reserved.
    +
    +Developed by: Daniel Griscom
    +              Suitable Systems
    +              http://www.suitable.com
    +
    +Permission is hereby granted, free of charge, to any person obtaining a
    +copy of this software and associated documentation files (the
    +"Software"), to deal with 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:
    +
    +- Redistributions of source code must retain the above copyright notice,
    +this list of conditions and the following disclaimers.
    +
    +- Redistributions in binary form must reproduce the above copyright
    +notice, this list of conditions and the following disclaimers in the
    +documentation and/or other materials provided with the distribution.
    +
    +- Neither the names of Suitable Systems nor the names of its
    +contributors may be used to endorse or promote products derived from
    +this Software without specific prior written permission.
    +
    +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 CONTRIBUTORS 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 WITH THE SOFTWARE.
    +
    +For more information about SMSLib, see
    +	
    +or contact
    +	Daniel Griscom
    +	Suitable Systems
    +	1 Centre Street, Suite 204
    +	Wakefield, MA 01880
    +	(781) 665-0053
    +
    +