blob: e1f3d302805be5730e4ac51f7474d37e5bd44ef3 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Copyright (C) 2013, 2014 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "I2cServiceJNI"
#include "utils/Log.h"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <stdint.h>
namespace android
{
// These I/O direction and status enums must match those in
// I2cTransaction.java
enum {
IO_DIRECTION_WRITE = 0,
IO_DIRECTION_READ = 1,
};
enum {
STATUS_NOT_STARTED = 1,
STATUS_OK = 0,
STATUS_ERR = -1,
};
static struct i2c_transaction_offsets_t
{
jfieldID mIoDirection;
jfieldID mStatus;
jfieldID mData;
} gI2cTransactionOffsets;
static jint android_server_I2cService_native_perform_i2c_transactions(
JNIEnv *env, jobject thiz, jstring name, jint address, jobjectArray txns)
{
jint ret = 0;
// Grab a file descriptor.
const char *pathStr = env->GetStringUTFChars(name, NULL);
int fd = open(pathStr, O_RDWR);
if (fd < 0) {
ALOGE("can't open %s", pathStr);
env->ReleaseStringUTFChars(name, pathStr);
return fd;
}
env->ReleaseStringUTFChars(name, pathStr);
// Prepare the messages.
jsize nTxns = env->GetArrayLength(txns);
struct i2c_msg messages[nTxns];
for (jsize i = 0; i < nTxns; i++) {
jobject txn = env->GetObjectArrayElement(txns, i);
jint iod = env->GetIntField(txn, gI2cTransactionOffsets.mIoDirection);
jobject dataObj = env->GetObjectField(txn, gI2cTransactionOffsets.mData);
jbyteArray *dataAp = reinterpret_cast<jbyteArray*>(&dataObj);
jsize dataLength = env->GetArrayLength(*dataAp);
jbyte *datap = env->GetByteArrayElements(*dataAp, NULL);
messages[i].addr = (uint8_t)address;
messages[i].flags = (iod == IO_DIRECTION_READ) ? I2C_M_RD : 0;
messages[i].len = (short)dataLength;
messages[i].buf = (char*)datap;
env->DeleteLocalRef(txn);
}
// Do the I/O.
struct i2c_rdwr_ioctl_data packets;
packets.msgs = messages;
packets.nmsgs = nTxns;
int io_rc = ioctl(fd, I2C_RDWR, &packets);
if (io_rc < 0) {
ALOGE("I/O error: %d", io_rc);
ret = io_rc;
}
// Release resources and set status codes.
for (jsize i = 0; i < nTxns; i++) {
jobject txn = env->GetObjectArrayElement(txns, i);
jobject dataObj = env->GetObjectField(txn, gI2cTransactionOffsets.mData);
jbyteArray *dataAp = reinterpret_cast<jbyteArray*>(&dataObj);
env->ReleaseByteArrayElements(*dataAp, (jbyte*)messages[i].buf, 0);
env->SetIntField(txn, gI2cTransactionOffsets.mStatus,
ret < 0 ? STATUS_ERR : STATUS_OK);
env->DeleteLocalRef(txn);
}
// And done
close(fd);
return ret;
}
static JNINativeMethod method_table[] = {
{ "native_perform_i2c_transactions", "(Ljava/lang/String;I[Landroid/hardware/I2cTransaction;)I",
(void*)android_server_I2cService_native_perform_i2c_transactions },
};
int register_android_server_I2cService(JNIEnv *env)
{
jclass clazz = env->FindClass("com/android/server/I2cService");
if (clazz == NULL) {
ALOGE("Can't find com/android/server/I2cService");
return -1;
}
clazz = env->FindClass("android/hardware/I2cTransaction");
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.hardware.I2cTransaction");
gI2cTransactionOffsets.mIoDirection = env->GetFieldID(clazz, "ioDirection", "I");
gI2cTransactionOffsets.mStatus = env->GetFieldID(clazz, "status", "I");
gI2cTransactionOffsets.mData = env->GetFieldID(clazz, "data", "[B");
return jniRegisterNativeMethods(env, "com/android/server/I2cService",
method_table, NELEM(method_table));
}
}