static int mpu_handle_mlsl(void *sl_handle, unsigned char addr, unsigned int cmd, struct mpu_read_write __user *usr_msg) { int retval = 0; struct mpu_read_write msg; unsigned char *user_data; retval = copy_from_user(&msg, usr_msg, sizeof(msg)); if (retval) return -EFAULT; user_data = msg.data; if (msg.length && msg.data) { unsigned char *data; data = kmalloc(msg.length, GFP_KERNEL); if (!data) return -ENOMEM; retval = copy_from_user(data, (void __user *)msg.data, msg.length); if (retval) { retval = -EFAULT; kfree(data); return retval; } msg.data = data; } else { return -EPERM; } switch (cmd) { case MPU_READ: retval = inv_serial_read(sl_handle, addr, msg.address, msg.length, msg.data); break; case MPU_WRITE: retval = inv_serial_write(sl_handle, addr, msg.length, msg.data); break; case MPU_READ_MEM: retval = inv_serial_read_mem(sl_handle, addr, msg.address, msg.length, msg.data); break; case MPU_WRITE_MEM: retval = inv_serial_write_mem(sl_handle, addr, msg.address, msg.length, msg.data); break; case MPU_READ_FIFO: retval = inv_serial_read_fifo(sl_handle, addr, msg.length, msg.data); break; case MPU_WRITE_FIFO: retval = inv_serial_write_fifo(sl_handle, addr, msg.length, msg.data); break; }; if (retval) { dev_err(&((struct i2c_adapter *)sl_handle)->dev, "%s: i2c %d error %d\n", __func__, cmd, retval); kfree(msg.data); return retval; } retval = copy_to_user((unsigned char __user *)user_data, msg.data, msg.length); kfree(msg.data); return retval; }
/** * @brief Test the gyroscope sensor. * Implements the core logic of the MPU Self Test. * Produces the PASS/FAIL result. Loads the calculated gyro biases * and temperature datum into the corresponding pointers. * @param mlsl_handle * serial interface handle to allow serial communication with the * device, both gyro and accelerometer. * @param gyro_biases * output pointer to store the initial bias calculation provided * by the MPU Self Test. Requires 3 elements for gyro X, Y, * and Z. * @param temp_avg * output pointer to store the initial average temperature as * provided by the MPU Self Test. * @param perform_full_test * If 1: * Complete calibration test: * Calculate offset, drive frequency, and noise and compare it * against set thresholds. * When 0: * Skip the noise and drive frequency calculation, * simply calculate the gyro biases. * * @return 0 on success. * On error, the return value is a bitmask representing: * 0, 1, 2 Failures with PLLs on X, Y, Z gyros respectively * (decimal values will be 1, 2, 4 respectively). * 3, 4, 5 Excessive offset with X, Y, Z gyros respectively * (decimal values will be 8, 16, 32 respectively). * 6, 7, 8 Excessive noise with X, Y, Z gyros respectively * (decimal values will be 64, 128, 256 respectively). * 9 If any of the RMS noise values is zero, it may be * due to a non-functional gyro or FIFO/register failure. * (decimal value will be 512). */ int test_gyro(void *mlsl_handle, short gyro_biases[3], short *temp_avg, uint_fast8_t perform_full_test) { int ret_val = 0; inv_error_t result; int total_count = 0; int total_count_axis[3] = {0, 0, 0}; int packet_count; short x[DEF_PERIOD_CAL * DEF_TESTS_PER_AXIS / 8 * 4] = {0}; short y[DEF_PERIOD_CAL * DEF_TESTS_PER_AXIS / 8 * 4] = {0}; short z[DEF_PERIOD_CAL * DEF_TESTS_PER_AXIS / 8 * 4] = {0}; int temperature = 0; float avg[3]; float rms[3]; unsigned long test_start = inv_get_tick_count(); int i, j, tmp; char tmpStr[200]; unsigned char regs[7] = {0}; /* make sure the DMP is disabled first */ result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_USER_CTRL, 0x00); if (result) { LOG_RESULT_LOCATION(result); return result; } /* reset the gyro offset values */ regs[0] = MPUREG_XG_OFFS_USRH; result = inv_serial_write(mlsl_handle, mldl_cfg->mpu_chip_info->addr, 6, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } /* sample rate */ if (perform_full_test) { /* = 8ms */ result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_SMPLRT_DIV, 0x07); test_setup.bias_thresh = (int)( DEF_BIAS_THRESH_CAL * test_setup.gyro_sens); } else { /* = 1ms */ result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_SMPLRT_DIV, 0x00); test_setup.bias_thresh = (int)( DEF_BIAS_THRESH_SELF * test_setup.gyro_sens); } if (result) { LOG_RESULT_LOCATION(result); return result; } regs[0] = 0x03; /* filter = 42Hz, analog_sample rate = 1 KHz */ switch (test_setup.gyro_fs) { case 2000: regs[0] |= 0x18; break; case 1000: regs[0] |= 0x10; break; case 500: regs[0] |= 0x08; break; case 250: default: regs[0] |= 0x00; break; } result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_CONFIG, regs[0]); if (result) { LOG_RESULT_LOCATION(result); return result; } result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_INT_ENABLE, 0x00); if (result) { LOG_RESULT_LOCATION(result); return result; } /* 1st, timing test */ for (j = 0; j < 3; j++) { MPL_LOGI("Collecting gyro data from %s gyro PLL\n", a_name[j]); /* turn on all gyros, use gyro X for clocking Set to Y and Z for 2nd and 3rd iteration */ result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_PWR_MGMT_1, j + 1); if (result) { LOG_RESULT_LOCATION(result); return result; } /* wait for 2 ms after switching clock source */ usleep(2000); /* enable & reset FIFO */ result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_USER_CTRL, BIT_FIFO_EN | BIT_FIFO_RST); if (result) { LOG_RESULT_LOCATION(result); return result; } tmp = test_setup.tests_per_axis; while (tmp-- > 0) { const unsigned char fifo_en_reg = MPUREG_FIFO_EN; /* enable XYZ gyro in FIFO and nothing else */ result = inv_serial_single_write(mlsl_handle, mldl_cfg->mpu_chip_info->addr, fifo_en_reg, BIT_GYRO_XOUT | BIT_GYRO_YOUT | BIT_GYRO_ZOUT); if (result) { LOG_RESULT_LOCATION(result); return result; } /* wait one period for data */ if (perform_full_test) usleep(DEF_PERIOD_CAL * 1000); else usleep(DEF_PERIOD_SELF * 1000); /* stop storing gyro in the FIFO */ result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, fifo_en_reg, 0x00); if (result) { LOG_RESULT_LOCATION(result); return result; } /* Getting number of bytes in FIFO */ result = inv_serial_read( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_FIFO_COUNTH, 2, dataout); if (result) { LOG_RESULT_LOCATION(result); return result; } /* number of 6 B packets in the FIFO */ packet_count = inv_big8_to_int16(dataout) / 6; sprintf(tmpStr, "Packet Count: %d - ", packet_count); if (abs(packet_count - test_setup.packet_thresh) <= /* Within total_timing_tol % range, rounded up */ (int)(test_setup.total_timing_tol * test_setup.packet_thresh + 1)) { for (i = 0; i < packet_count; i++) { /* getting FIFO data */ result = inv_serial_read_fifo(mlsl_handle, mldl_cfg->mpu_chip_info->addr, 6, dataout); if (result) { LOG_RESULT_LOCATION(result); return result; } x[total_count + i] = inv_big8_to_int16(&dataout[0]); y[total_count + i] = inv_big8_to_int16(&dataout[2]); z[total_count + i] = inv_big8_to_int16(&dataout[4]); if (VERBOSE_OUT) { MPL_LOGI("Gyros %-4d : %+13d %+13d %+13d\n", total_count + i, x[total_count + i], y[total_count + i], z[total_count + i]); } } total_count += packet_count; total_count_axis[j] += packet_count; sprintf(tmpStr, "%sOK", tmpStr); } else { ret_val |= 1 << j; sprintf(tmpStr, "%sNOK - samples ignored", tmpStr); } MPL_LOGI("%s\n", tmpStr); } /* remove gyros from FIFO */ result = inv_serial_single_write( mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_FIFO_EN, 0x00); if (result) { LOG_RESULT_LOCATION(result); return result; } /* Read Temperature */ result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, MPUREG_TEMP_OUT_H, 2, dataout); if (result) { LOG_RESULT_LOCATION(result); return result; } temperature += (short)inv_big8_to_int16(dataout); } MPL_LOGI("\n"); MPL_LOGI("Total %d samples\n", total_count); MPL_LOGI("\n"); /* 2nd, check bias from X, Y, and Z PLL clock source */ tmp = total_count != 0 ? total_count : 1; for (i = 0, avg[X] = .0f, avg[Y] = .0f, avg[Z] = .0f; i < total_count; i++) { avg[X] += 1.f * x[i] / tmp; avg[Y] += 1.f * y[i] / tmp; avg[Z] += 1.f * z[i] / tmp; } MPL_LOGI("bias : %+13.3f %+13.3f %+13.3f (LSB)\n", avg[X], avg[Y], avg[Z]); if (VERBOSE_OUT) { MPL_LOGI(" : %+13.3f %+13.3f %+13.3f (dps)\n", avg[X] / adj_gyro_sens, avg[Y] / adj_gyro_sens, avg[Z] / adj_gyro_sens); } for (j = 0; j < 3; j++) { if (fabs(avg[j]) > test_setup.bias_thresh) { MPL_LOGI("%s-Gyro bias (%.0f) exceeded threshold " "(threshold = %d)\n", a_name[j], avg[j], test_setup.bias_thresh); ret_val |= 1 << (3+j); } } /* 3rd, check RMS for dead gyros If any of the RMS noise value returns zero, then we might have dead gyro or FIFO/register failure, the part is sleeping, or the part is not responsive */ for (i = 0, rms[X] = 0.f, rms[Y] = 0.f, rms[Z] = 0.f; i < total_count; i++) { rms[X] += (x[i] - avg[X]) * (x[i] - avg[X]); rms[Y] += (y[i] - avg[Y]) * (y[i] - avg[Y]); rms[Z] += (z[i] - avg[Z]) * (z[i] - avg[Z]); } if (rms[X] == 0 || rms[Y] == 0 || rms[Z] == 0) { ret_val |= 1 << 9; } /* 4th, temperature average */ temperature /= 3; if (VERBOSE_OUT) MPL_LOGI("Temperature : %+13.3f %13s %13s (deg. C)\n", (float)inv_decode_temperature(temperature) / (1L << 16), "", ""); /* load into final storage */ *temp_avg = (short)temperature; gyro_biases[X] = FLOAT_TO_SHORT(avg[X]); gyro_biases[Y] = FLOAT_TO_SHORT(avg[Y]); gyro_biases[Z] = FLOAT_TO_SHORT(avg[Z]); MPL_LOGI("\n"); MPL_LOGI("Test time : %ld ms\n", inv_get_tick_count() - test_start); return ret_val; }