static void print_matrix(struct libmaxtouch_ctx *ctx, double *mat, int n) { int i, j; mxt_verb(ctx, "\nMatrix:"); for (i = 0; i < n; i++) { /* each row */ for (j = 0; j < n; j++) { /* each column */ mxt_verb(ctx, "%0.2f\t\t", mat[i * n + j]); } mxt_verb(ctx, "\n"); } return; }
//****************************************************************************** /// \brief Check chip is not in deep sleep /// \return #mxt_rc static int mxt_t68_check_power_cfg(struct t68_ctx *ctx) { uint16_t t7_addr; uint8_t buf[2]; int ret; /* Skip if object not present */ t7_addr = mxt_get_object_address(ctx->mxt, GEN_POWERCONFIG_T7, 0); if (t7_addr == OBJECT_NOT_FOUND) return MXT_SUCCESS; ret = mxt_read_register(ctx->mxt, &buf[0], t7_addr, sizeof(buf)); if (ret) return ret; mxt_verb(ctx->lc, "T7 IDLEACQINT=%u ACTVACQINT=%u", buf[0], buf[1]); if ((buf[0] == 0) || (buf[1] == 0)) { mxt_err(ctx->lc, "Warning: The T7 power configuration object shows that the chip " "is in deep sleep, and so will not process T68 serial data " "commands. Please set the T7 power configuration idle acquisition " "interval to a non-zero value and try again."); return MXT_ERROR_UNEXPECTED_DEVICE_STATE; } else { return MXT_SUCCESS; } }
//****************************************************************************** /// \brief Get next MSG into byte buffer /// \return #mxt_rc int t44_get_msg_bytes(struct mxt_device *mxt, unsigned char *buf, size_t buflen, int *count) { int ret; uint16_t addr; uint16_t size; addr = mxt_get_object_address(mxt, GEN_MESSAGEPROCESSOR_T5, 0); if (addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; /* Do not read CRC byte */ size = mxt_get_object_size(mxt, GEN_MESSAGEPROCESSOR_T5) - 1; if (size > buflen) { mxt_err(mxt->ctx, "Buffer too small!"); return MXT_ERROR_NO_MEM; } ret = mxt_read_register(mxt, buf, addr, size); if (ret) return ret; /* Check for invalid message */ if (buf[0] == 255u) { mxt_verb(mxt->ctx, "Invalid message"); return MXT_ERROR_NO_MESSAGE; } *count = size; return MXT_SUCCESS; }
//****************************************************************************** /// \brief Write packet to MXT chip /// \return Number of databytes sent static int hidraw_write_packet(struct mxt_device *mxt, struct hid_packet *write_pkt, uint8_t *byte_count) { int ret; uint8_t pkt_size = write_pkt->rx_bytes + 4; /* allowing for header */ if ((ret = write(mxt->conn->hidraw.fd, write_pkt, pkt_size)) != pkt_size) { mxt_verb(mxt->ctx, "HIDRAW retry"); usleep(HIDRAW_WRITE_RETRY_DELAY_US); if ((ret = write(mxt->conn->hidraw.fd, &write_pkt, pkt_size)) != pkt_size) { mxt_err(mxt->ctx, "Error %s (%d) writing to hidraw", strerror(errno), errno); ret = mxt_errno_to_rc(errno); } } mxt_log_buffer(mxt->ctx, LOG_VERBOSE, "PKT TX:", (const unsigned char *) write_pkt, pkt_size); *byte_count = ret - 6; mxt_dbg(mxt->ctx, "Sending packet: size: %d No. data bytes TX: %d", pkt_size, *byte_count); return MXT_SUCCESS; }
/*! * @param mxt Maxtouch Device * @param object_type Object ID number. * @param instance Instance number of the object. * * @brief Returns the start address of the selected object and instance number * in the chip's memory map. * @return Object address, or OBJECT_NOT_FOUND if object/instance not found. */ uint16_t mxt_get_object_address(struct mxt_device *mxt, uint16_t object_type, uint8_t instance) { struct mxt_id_info *id = mxt->info.id; int i = 0; struct mxt_object obj; for (i = 0; i < id->num_objects; i++) { obj = mxt->info.objects[i]; /* Does object type match? */ if (obj.type == object_type) { /* Are there enough instances defined in the firmware? */ if (obj.instances_minus_one >= instance) { return mxt_get_start_position(obj, instance); } else { mxt_warn(mxt->ctx, "T%u instance %u not present on device", object_type, instance); return OBJECT_NOT_FOUND; } } } mxt_verb(mxt->ctx, "T%u not present on device", object_type); return OBJECT_NOT_FOUND; }
//****************************************************************************** /// \brief Get messages (V2 interface) /// \param mxt Device context /// \param count Number of messages retrieved /// \return #mxt_rc int sysfs_get_msgs_v2(struct mxt_device *mxt, int *count) { int num_bytes; uint16_t t5_size; char *filename; struct stat filestat; int ret; int fd; sysfs_reopen_notify_fd(mxt); filename = make_path(mxt, "debug_msg"); ret = stat(filename, &filestat); if (ret < 0) { mxt_err(mxt->ctx, "Could not stat %s, error %s (%d)", filename, strerror(errno), errno); return mxt_errno_to_rc(errno); } mxt->sysfs.debug_v2_size = filestat.st_size; if (mxt->sysfs.debug_v2_msg_buf) { free(mxt->sysfs.debug_v2_msg_buf); mxt->sysfs.debug_v2_msg_buf = NULL; } mxt->sysfs.debug_v2_msg_buf = calloc(mxt->sysfs.debug_v2_size, sizeof(uint8_t)); fd = open(filename, O_RDWR); if (fd < 0) { mxt_err(mxt->ctx, "Could not open %s, error %s (%d)", filename, strerror(errno), errno); ret = mxt_errno_to_rc(errno); goto close; } t5_size = mxt_get_object_size(mxt, GEN_MESSAGEPROCESSOR_T5) - 1; num_bytes = read(fd, mxt->sysfs.debug_v2_msg_buf, mxt->sysfs.debug_v2_size); if (num_bytes < 0) { mxt_err(mxt->ctx, "read error %s (%d)", strerror(errno), errno); ret = mxt_errno_to_rc(errno); goto close; } mxt->sysfs.debug_v2_msg_count = num_bytes / t5_size; mxt->sysfs.debug_v2_msg_ptr = 0; ret = MXT_SUCCESS; *count = mxt->sysfs.debug_v2_msg_count; mxt_verb(mxt->ctx, "count = %d", mxt->sysfs.debug_v2_msg_count); close: close(fd); return ret; }
//****************************************************************************** /// \brief Send command then check status /// \return #mxt_rc static int mxt_t68_command(struct t68_ctx *ctx, uint8_t cmd) { int ret; mxt_verb(ctx->lc, "Writing %u to CMD register", cmd); ret = mxt_write_register(ctx->mxt, &cmd, ctx->t68_cmd_addr, 1); if (ret) return ret; return mxt_read_messages_sigint(ctx->mxt, T68_TIMEOUT, ctx, mxt_t68_get_status); }
//****************************************************************************** /// \brief Read and deal with incoming command /// \return #mxt_rc static int handle_cmd(struct mxt_device *mxt, int sockfd) { int ret; uint16_t address; uint16_t count; struct mxt_buffer linebuf; char *line; int offset; ret = mxt_buf_init(&linebuf); if (ret) return ret; ret = readline(mxt, sockfd, &linebuf); if (ret) { mxt_dbg(mxt->ctx, "Error reading or peer closed socket"); goto free; } line = (char *)linebuf.data; if (strlen(line) == 0) { ret = MXT_SUCCESS; goto free; } mxt_verb(mxt->ctx, "%s", line); if (!strcmp(line, "SAT")) { mxt_info(mxt->ctx, "Server attached"); ret = MXT_SUCCESS; } else if (!strcmp(line, "SDT")) { mxt_info(mxt->ctx, "Server detached"); ret = MXT_SUCCESS; } else if (sscanf(line, "REA %" SCNu16 " %" SCNu16, &address, &count) == 2) { ret = bridge_rea_cmd(mxt, sockfd, address, count); } else if (sscanf(line, "WRI %" SCNu16 "%n", &address, &offset) == 1) { /* skip space */ offset += 1; ret = bridge_wri_cmd(mxt, sockfd, address, line + offset, strlen(line) - offset); } else { mxt_info(mxt->ctx, "Unrecognised cmd \"%s\"", line); ret = MXT_SUCCESS; } free: mxt_buf_free(&linebuf); return ret; }
//****************************************************************************** /// \brief Enable T68 /// \return #mxt_rc static int mxt_t68_enable(struct t68_ctx *ctx) { int ret; uint8_t cmd = T68_CTRL_RPTEN | T68_CTRL_ENABLE; mxt_dbg(ctx->lc, "Enabling T68 object"); mxt_verb(ctx->lc, "Writing %u to ctrl register", cmd); ret = mxt_write_register(ctx->mxt, &cmd, ctx->t68_addr + T68_CTRL, 1); if (ret) return ret; return MXT_SUCCESS; }
//****************************************************************************** /// \brief Discard all messages /// \return #mxt_rc int t44_msg_reset(struct mxt_device *mxt) { int count, i, ret, size; unsigned char databuf[20]; ret = t44_get_msg_count(mxt, &count); if (ret) { mxt_verb(mxt->ctx, "rc = %d", ret); return ret; } for (i = 0; i < count; i++) { ret = t44_get_msg_bytes(mxt, &databuf[0], sizeof(databuf), &size); if (ret) { mxt_verb(mxt->ctx, "rc = %d", ret); return ret; } } return MXT_SUCCESS; }
/*! * @brief Populates a look-up table for the report IDs. * @return #mxt_rc */ int mxt_calc_report_ids(struct mxt_device *mxt) { /* Report ID zero is reserved - start from one */ int num_report_ids = 1; int report_id_count = 1; int i; int instance; int report_index; struct mxt_object obj; /* Calculate the number of report IDs */ for (i = 0; i < mxt->info.id->num_objects; i++) { obj = mxt->info.objects[i]; num_report_ids += MXT_INSTANCES(obj) * obj.num_report_ids; } /* Allocate memory for report ID look-up table */ mxt->report_id_map = calloc(num_report_ids, sizeof(struct mxt_report_id_map)); if (mxt->report_id_map == NULL) { mxt_err(mxt->ctx, "calloc failure"); return MXT_ERROR_NO_MEM; } /* Store the object and instance for each report ID */ for (i = 0; i < mxt->info.id->num_objects; i++) { obj = mxt->info.objects[i]; for (instance = 0; instance < MXT_INSTANCES(obj); instance++) { for (report_index = 0; report_index < obj.num_report_ids; report_index++) { mxt->report_id_map[report_id_count].object_type = obj.type; mxt->report_id_map[report_id_count].instance = instance; report_id_count++; } } } mxt_verb(mxt->ctx, "Created a look-up table of %d Report IDs", report_id_count); return MXT_SUCCESS; }
//****************************************************************************** /// \brief Handle status messages from the T68 Serial Data Command object /// \return #mxt_rc static int mxt_t68_get_status(struct mxt_device *mxt, uint8_t *msg, void *context, uint8_t size) { struct t68_ctx *ctx = context; unsigned int object_type = mxt_report_id_to_type(mxt, msg[0]); uint8_t status; mxt_verb(mxt->ctx, "Received message from T%u", object_type); if (object_type == SERIAL_DATA_COMMAND_T68) { /* mask off reserved bits */ status = msg[1] & 0x0F; mxt_t68_print_status(ctx, status); return (status == 0) ? MXT_SUCCESS : MXT_ERROR_SERIAL_DATA_FAILURE; } else if (object_type == GEN_COMMANDPROCESSOR_T6) { print_t6_status(msg[1]); } return MXT_MSG_CONTINUE; }
//****************************************************************************** /// \brief Check status of previously sent command /// \return #mxt_rc static int mxt_self_cap_command(struct mxt_device *mxt, uint8_t *msg, void *context, uint8_t size) { unsigned int object_type = mxt_report_id_to_type(mxt, msg[0]); uint8_t *cmd = context; mxt_verb(mxt->ctx, "Received message from T%u", object_type); if (object_type == SPT_SELFCAPGLOBALCONFIG_T109) { if (msg[1] == *cmd) { switch (msg[2]) { case 0: return MXT_SUCCESS; case 1: return MXT_ERROR_SELFCAP_TUNE; } } } else if (object_type == GEN_COMMANDPROCESSOR_T6) { print_t6_status(msg[1]); } return MXT_MSG_CONTINUE; }
//****************************************************************************** /// \brief Handle bridge write /// \return #mxt_rc static int bridge_wri_cmd(struct mxt_device *mxt, int sockfd, uint16_t address, char *hex, uint16_t bytes) { int ret; uint16_t count; const char * const FAIL = "WRP ERR\n"; const char * const PASS = "******"; const char *response; uint8_t *databuf; databuf = calloc(bytes, sizeof(uint8_t)); if (!databuf) { mxt_err(mxt->ctx, "Failed to allocate memory"); return MXT_ERROR_NO_MEM; } ret = mxt_convert_hex(hex, databuf, &count, bytes); if (ret) { response = FAIL; } else { ret = mxt_write_register(mxt, databuf, address, count); if (ret) { mxt_verb(mxt->ctx, "WRI OK"); response = FAIL; } else { response = PASS; } } ret = write(sockfd, response, strlen(response)); if (ret < 0) { mxt_err(mxt->ctx, "Socket write error: %s (%d)", strerror(errno), errno); ret = mxt_errno_to_rc(errno); } ret = MXT_SUCCESS; free(databuf); return ret; }
//****************************************************************************** /// \brief Read registers from MXT chip, in blocks /// \return #mxt_rc int mxt_read_register(struct mxt_device *mxt, uint8_t *buf, int start_register, size_t count) { int ret; size_t received; size_t off = 0; mxt_verb(mxt->ctx, "%s start_register:%d count:%zu", __func__, start_register, count); while (off < count) { ret = mxt_read_register_block(mxt, buf + off, start_register + off, count - off, &received); if (ret) return ret; off += received; } mxt_log_buffer(mxt->ctx, LOG_VERBOSE, "RX:", buf, count); return MXT_SUCCESS; }
//****************************************************************************** /// \brief Write register to MXT chip /// \return #mxt_rc int mxt_write_register(struct mxt_device *mxt, uint8_t const *buf, int start_register, size_t count) { int ret; mxt_verb(mxt->ctx, "%s start_register:%d count:%zu", __func__, start_register, count); switch (mxt->conn->type) { case E_SYSFS: ret = sysfs_write_register(mxt, buf, start_register, count); break; case E_I2C_DEV: ret = i2c_dev_write_register(mxt, buf, start_register, count); break; #ifdef HAVE_LIBUSB case E_USB: ret = usb_write_register(mxt, buf, start_register, count); break; #endif /* HAVE_LIBUSB */ case E_HIDRAW: ret = hidraw_write_register(mxt, buf, start_register, count); break; default: mxt_err(mxt->ctx, "Device type not supported"); ret = MXT_ERROR_NOT_SUPPORTED; } if (ret == MXT_SUCCESS) mxt_log_buffer(mxt->ctx, LOG_VERBOSE, "TX:", buf, count); return ret; }
//****************************************************************************** /// \brief Get messages /// \param mxt Maxtouch Device /// \param count Number of messages available /// \param init_timestamp Read newest dmesg line and initialise timestamp /// \return #mxt_rc int dmesg_get_msgs(struct mxt_device *mxt, int *count, bool init_timestamp) { char msg[BUFFERSIZE]; int ep, sp; int ret = MXT_SUCCESS; unsigned long sec, msec, lastsec = 0, lastmsec = 0; // Read entire kernel log buffer ep = klogctl(SYSLOG_ACTION_READ_ALL, mxt->sysfs.debug_msg_buf, mxt->sysfs.debug_msg_buf_size); // Return if no bytes read if (ep < 0) { mxt_warn(mxt->ctx, "klogctl error %d (%s)", errno, strerror(errno)); ret = mxt_errno_to_rc(errno); } else { // null terminate mxt->sysfs.debug_msg_buf[ep] = 0; sp = ep; if (!init_timestamp) dmesg_list_empty(mxt); // Search for next new line character while (true) { sp--; while (sp >= 0 && *(mxt->sysfs.debug_msg_buf + sp) != '\n') sp--; if (sp <= 0) break; // Try to parse dmesg line if (sscanf(mxt->sysfs.debug_msg_buf+sp+1, "< %*c>[ %lu.%06lu] %255[^\n]", &sec, &msec, msg) == 3) { if (init_timestamp) { mxt->sysfs.timestamp = sec; mxt->sysfs.mtimestamp = msec; mxt_verb(mxt->ctx, "%s - init [%5lu.%06lu]", __func__, sec, msec); break; } // Store time of last message in buffer if (lastsec == 0) { lastsec = sec; lastmsec = msec; } // Only 500 at a time, otherwise we overrun JNI reference limit. // Timestamp must be greater than previous messages, slightly // complicated by seconds and microseconds if ((mxt->sysfs.dmesg_count > MAX_DMESG_COUNT) || (sec == mxt->sysfs.timestamp && msec <= mxt->sysfs.mtimestamp) || (sec < mxt->sysfs.timestamp)) { mxt->sysfs.timestamp = lastsec; mxt->sysfs.mtimestamp = lastmsec; break; } char* msgptr; msg[sizeof(msg) - 1] = '\0'; msgptr = strstr(msg, "MXT MSG"); if (msgptr) dmesg_list_add(mxt, sec, msec, msgptr); } } if (!init_timestamp) { *count = mxt->sysfs.dmesg_count; mxt->sysfs.dmesg_ptr = mxt->sysfs.dmesg_head; } } return ret; }
//****************************************************************************** /// \brief Handle messages from the self test object /// \return #mxt_rc static int self_test_handle_messages(struct mxt_device *mxt) { bool done = false; int count, i; time_t now; time_t start_time = time(NULL); static const uint8_t TIMEOUT = 10; // seconds uint8_t buf[10]; int len; unsigned int object_type; int ret; while (!done) { mxt_msg_wait(mxt, 100); now = time(NULL); if ((now - start_time) > TIMEOUT) { mxt_err(mxt->ctx, "Timeout"); return MXT_ERROR_TIMEOUT; } ret = mxt_get_msg_count(mxt, &count); if (ret) return ret; if (count > 0) { for (i = 0; i < count; i++) { ret = mxt_get_msg_bytes(mxt, buf, sizeof(buf), &len); if (ret) return ret; if (len > 0) { object_type = mxt_report_id_to_type(mxt, buf[0]); mxt_verb(mxt->ctx, "Received message from T%u", object_type); if (object_type == SPT_SELFTEST_T25) { switch (buf[1]) { case SELF_TEST_ALL: mxt_info(mxt->ctx, "PASS: All tests passed"); ret = MXT_SUCCESS; break; case SELF_TEST_INVALID: mxt_err(mxt->ctx, "FAIL: Invalid test command"); ret = MXT_ERROR_NOT_SUPPORTED; break; case SELF_TEST_TIMEOUT: mxt_err(mxt->ctx, "FAIL: Test timeout"); ret = MXT_ERROR_TIMEOUT; break; case SELF_TEST_ANALOG: mxt_err(mxt->ctx, "FAIL: AVdd Analog power is not present"); ret = MXT_ERROR_SELF_TEST_ANALOG; break; case SELF_TEST_PIN_FAULT: mxt_err(mxt->ctx, "FAIL: Pin fault"); ret = MXT_ERROR_SELF_TEST_PIN_FAULT; break; case SELF_TEST_PIN_FAULT_2: mxt_err(mxt->ctx, "FAIL: Pin fault 2"); ret = MXT_ERROR_SELF_TEST_PIN_FAULT; break; case SELF_TEST_AND_GATE: mxt_err(mxt->ctx, "FAIL: AND Gate Fault"); ret = MXT_ERROR_SELF_TEST_AND_GATE; break; case SELF_TEST_SIGNAL_LIMIT: mxt_err(mxt->ctx, "FAIL: Signal limit fault"); ret = MXT_ERROR_SELF_TEST_SIGNAL_LIMIT; break; case SELF_TEST_GAIN: mxt_err(mxt->ctx, "FAIL: Gain error"); ret = MXT_ERROR_SELF_TEST_GAIN; break; default: mxt_err(mxt->ctx, "Unrecognised status %02X", buf[1]); ret = MXT_ERROR_UNEXPECTED_DEVICE_STATE; break; } done = true; } } } } } return ret; }
//****************************************************************************** /// \brief Main function for mxt-app int main (int argc, char *argv[]) { int ret; int c; int msgs_timeout = MSG_CONTINUOUS; bool msgs_enabled = false; uint8_t backup_cmd = BACKUPNV_COMMAND; unsigned char self_test_cmd = SELF_TEST_ALL; uint16_t address = 0; uint16_t count = 0; struct mxt_conn_info *conn = NULL; uint16_t object_type = 0; uint16_t msg_filter_type = 0; uint8_t instance = 0; uint8_t verbose = 2; uint16_t t37_frames = 1; uint8_t t37_mode = DELTAS_MODE; bool format = false; uint16_t port = 4000; uint8_t t68_datatype = 1; unsigned char databuf; char strbuf2[BUF_SIZE]; char strbuf[BUF_SIZE]; strbuf[0] = '\0'; strbuf2[0] = '\0'; mxt_app_cmd cmd = CMD_NONE; while (1) { int option_index = 0; static struct option long_options[] = { {"backup", optional_argument, 0, 0}, {"bootloader-version", no_argument, 0, 0}, {"bridge-client", required_argument, 0, 'C'}, {"calibrate", no_argument, 0, 0}, {"checksum", required_argument, 0, 0}, {"debug-dump", required_argument, 0, 0}, {"device", required_argument, 0, 'd'}, {"t68-file", required_argument, 0, 0}, {"t68-datatype", required_argument, 0, 0}, {"msg-filter", required_argument, 0, 'F'}, {"format", no_argument, 0, 'f'}, {"flash", required_argument, 0, 0}, {"firmware-version", required_argument, 0, 0}, {"frames", required_argument, 0, 0}, {"help", no_argument, 0, 'h'}, {"info", no_argument, 0, 'i'}, {"instance", required_argument, 0, 'I'}, {"load", required_argument, 0, 0}, {"save", required_argument, 0, 0}, {"messages", optional_argument, 0, 'M'}, {"count", required_argument, 0, 'n'}, {"port", required_argument, 0, 'p'}, {"query", no_argument, 0, 'q'}, {"read", no_argument, 0, 'R'}, {"reset", no_argument, 0, 0}, {"reset-bootloader", no_argument, 0, 0}, {"register", required_argument, 0, 'r'}, {"references", no_argument, 0, 0}, {"self-cap-tune-config", no_argument, 0, 0}, {"self-cap-tune-nvram", no_argument, 0, 0}, {"self-cap-signals", no_argument, 0, 0}, {"self-cap-deltas", no_argument, 0, 0}, {"self-cap-refs", no_argument, 0, 0}, {"bridge-server", no_argument, 0, 'S'}, {"test", optional_argument, 0, 't'}, {"type", required_argument, 0, 'T'}, {"verbose", required_argument, 0, 'v'}, {"version", no_argument, 0, 0}, {"write", no_argument, 0, 'W'}, {"zero", no_argument, 0, 0}, {0, 0, 0, 0 } }; c = getopt_long(argc, argv, "C:d:D:fF:ghiI:M::m:n:p:qRr:St::T:v:W", long_options, &option_index); if (c == -1) break; switch (c) { case 0: if (!strcmp(long_options[option_index].name, "t68-file")) { if (cmd == CMD_NONE) { cmd = CMD_SERIAL_DATA; strncpy(strbuf, optarg, sizeof(strbuf)); strbuf[sizeof(strbuf) - 1] = '\0'; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "t68-datatype")) { t68_datatype = strtol(optarg, NULL, 0); } else if (!strcmp(long_options[option_index].name, "flash")) { if (cmd == CMD_NONE) { cmd = CMD_FLASH; strncpy(strbuf, optarg, sizeof(strbuf)); strbuf[sizeof(strbuf) - 1] = '\0'; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "backup")) { if (cmd == CMD_NONE) { cmd = CMD_BACKUP; if (optarg) { ret = mxt_convert_hex(optarg, &databuf, &count, sizeof(databuf)); if (ret || count == 0) { fprintf(stderr, "Hex convert error\n"); ret = MXT_ERROR_BAD_INPUT; } backup_cmd = databuf; } } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "calibrate")) { if (cmd == CMD_NONE) { cmd = CMD_CALIBRATE; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "debug-dump")) { if (cmd == CMD_NONE) { cmd = CMD_DEBUG_DUMP; strncpy(strbuf, optarg, sizeof(strbuf)); strbuf[sizeof(strbuf) - 1] = '\0'; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "reset")) { if (cmd == CMD_NONE) { cmd = CMD_RESET; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "self-cap-tune-config")) { if (cmd == CMD_NONE) { cmd = CMD_SELF_CAP_TUNE_CONFIG; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "self-cap-tune-nvram")) { if (cmd == CMD_NONE) { cmd = CMD_SELF_CAP_TUNE_NVRAM; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "load")) { if (cmd == CMD_NONE) { cmd = CMD_LOAD_CFG; strncpy(strbuf, optarg, sizeof(strbuf)); strbuf[sizeof(strbuf) - 1] = '\0'; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "save")) { if (cmd == CMD_NONE) { cmd = CMD_SAVE_CFG; strncpy(strbuf, optarg, sizeof(strbuf)); strbuf[sizeof(strbuf) - 1] = '\0'; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "reset-bootloader")) { if (cmd == CMD_NONE) { cmd = CMD_RESET_BOOTLOADER; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "bootloader-version")) { if (cmd == CMD_NONE) { cmd = CMD_BOOTLOADER_VERSION; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "checksum")) { if (cmd == CMD_NONE) { cmd = CMD_CRC_CHECK; strncpy(strbuf, optarg, sizeof(strbuf)); strbuf[sizeof(strbuf) - 1] = '\0'; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "zero")) { if (cmd == CMD_NONE) { cmd = CMD_ZERO_CFG; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } else if (!strcmp(long_options[option_index].name, "firmware-version")) { strncpy(strbuf2, optarg, sizeof(strbuf2)); } else if (!strcmp(long_options[option_index].name, "frames")) { t37_frames = strtol(optarg, NULL, 0); } else if (!strcmp(long_options[option_index].name, "references")) { t37_mode = REFS_MODE; } else if (!strcmp(long_options[option_index].name, "self-cap-signals")) { t37_mode = SELF_CAP_SIGNALS; } else if (!strcmp(long_options[option_index].name, "self-cap-refs")) { t37_mode = SELF_CAP_REFS; } else if (!strcmp(long_options[option_index].name, "self-cap-deltas")) { t37_mode = SELF_CAP_DELTAS; } else if (!strcmp(long_options[option_index].name, "version")) { printf("mxt-app %s%s\n", MXT_VERSION, ENABLE_DEBUG ? " DEBUG":""); return MXT_SUCCESS; } else { fprintf(stderr, "Unknown option %s\n", long_options[option_index].name); } break; case 'd': if (optarg) { if (!strncmp(optarg, "i2c-dev:", 8)) { ret = mxt_new_conn(&conn, E_I2C_DEV); if (ret) return ret; if (sscanf(optarg, "i2c-dev:%d-%x", &conn->i2c_dev.adapter, &conn->i2c_dev.address) != 2) { fprintf(stderr, "Invalid device string %s\n", optarg); conn = mxt_unref_conn(conn); return MXT_ERROR_NO_MEM; } } else if (!strncmp(optarg, "sysfs:", 6)) { ret = mxt_new_conn(&conn, E_SYSFS); if (ret) return ret; conn->sysfs.path = (char *)calloc(strlen(optarg) + 1, sizeof(char)); if (!conn->sysfs.path) { fprintf(stderr, "malloc failure\n"); conn = mxt_unref_conn(conn); return MXT_ERROR_NO_MEM; } memcpy(conn->sysfs.path, optarg + 6, strlen(optarg) - 6); } #ifdef HAVE_LIBUSB else if (!strncmp(optarg, "usb:", 4)) { ret = mxt_new_conn(&conn, E_USB); if (ret) return ret; if (sscanf(optarg, "usb:%d-%d", &conn->usb.bus, &conn->usb.device) != 2) { fprintf(stderr, "Invalid device string %s\n", optarg); conn = mxt_unref_conn(conn); return MXT_ERROR_NO_MEM; } } #endif else if (!strncmp(optarg, "hidraw:", 7)) { ret = mxt_new_conn(&conn, E_HIDRAW); if (ret) return ret; conn->hidraw.report_id = HIDRAW_REPORT_ID; if (sscanf(optarg, "hidraw:%s", conn->hidraw.node) != 1) { fprintf(stderr, "Invalid device string %s\n", optarg); conn = mxt_unref_conn(conn); return MXT_ERROR_NO_MEM; } } else { fprintf(stderr, "Invalid device string %s\n", optarg); conn = mxt_unref_conn(conn); return MXT_ERROR_BAD_INPUT; } } break; case 'C': if (cmd == CMD_NONE) { cmd = CMD_BRIDGE_CLIENT; strncpy(strbuf, optarg, sizeof(strbuf)); strbuf[sizeof(strbuf) - 1] = '\0'; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; case 'g': if (cmd == CMD_NONE) { cmd = CMD_GOLDEN_REFERENCES; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; case 'h': print_usage(argv[0]); return MXT_SUCCESS; case 'f': format = true; break; case 'I': if (optarg) { instance = strtol(optarg, NULL, 0); } break; case 'M': msgs_enabled = true; if (cmd == CMD_NONE) { cmd = CMD_MESSAGES; } if (optarg) msgs_timeout = strtol(optarg, NULL, 0); break; case 'F': if (optarg) { msg_filter_type = strtol(optarg, NULL, 0); } break; case 'n': if (optarg) { count = strtol(optarg, NULL, 0); } break; case 'p': if (optarg) { port = strtol(optarg, NULL, 0); } break; case 'q': if (cmd == CMD_NONE) { cmd = CMD_QUERY; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; case 'r': if (optarg) { address = strtol(optarg, NULL, 0); } break; case 'R': if (cmd == CMD_NONE) { cmd = CMD_READ; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; case 'S': if (cmd == CMD_NONE) { cmd = CMD_BRIDGE_SERVER; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; case 'T': if (optarg) { object_type = strtol(optarg, NULL, 0); } break; case 'i': if (cmd == CMD_NONE) { cmd = CMD_INFO; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; case 't': if (cmd == CMD_NONE) { if (optarg) { ret = mxt_convert_hex(optarg, &databuf, &count, sizeof(databuf)); if (ret) { fprintf(stderr, "Hex convert error\n"); ret = MXT_ERROR_BAD_INPUT; } else { self_test_cmd = databuf; } } cmd = CMD_TEST; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; case 'v': if (optarg) { verbose = strtol(optarg, NULL, 0); } break; case 'W': if (cmd == CMD_NONE) { cmd = CMD_WRITE; } else { print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } break; default: /* Output newline to create space under getopt error output */ fprintf(stderr, "\n\n"); print_usage(argv[0]); return MXT_ERROR_BAD_INPUT; } } struct mxt_device *mxt = NULL; struct libmaxtouch_ctx *ctx; ret = mxt_new(&ctx); if (ret) { mxt_err(ctx, "Failed to init libmaxtouch"); return ret; } /* Set debug level */ mxt_set_log_level(ctx, verbose); mxt_verb(ctx, "verbose:%u", verbose); /* Debug does not work until mxt_set_verbose() is called */ mxt_info(ctx, "Version:%s", MXT_VERSION); if (cmd == CMD_WRITE || cmd == CMD_READ) { mxt_verb(ctx, "instance:%u", instance); mxt_verb(ctx, "count:%u", count); mxt_verb(ctx, "address:%u", address); mxt_verb(ctx, "object_type:%u", object_type); mxt_verb(ctx, "format:%s", format ? "true" : "false"); } if (cmd == CMD_QUERY) { ret = mxt_scan(ctx, &conn, true); goto free; } else if (cmd != CMD_FLASH && cmd != CMD_BOOTLOADER_VERSION) { ret = mxt_init_chip(ctx, &mxt, &conn); if (ret && cmd != CMD_CRC_CHECK ) goto free; if (mxt) mxt_set_debug(mxt, true); } switch (cmd) { case CMD_WRITE: mxt_verb(ctx, "Write command"); ret = mxt_handle_write_cmd(mxt, object_type, count, instance, address, argc, argv); if (ret == MXT_ERROR_BAD_INPUT) goto free; break; case CMD_READ: mxt_verb(ctx, "Read command"); ret = mxt_read_object(mxt, object_type, instance, address, count, format); break; case CMD_INFO: mxt_verb(ctx, "CMD_INFO"); mxt_print_info_block(mxt); ret = MXT_SUCCESS; break; case CMD_GOLDEN_REFERENCES: mxt_verb(ctx, "CMD_GOLDEN_REFERENCES"); ret = mxt_store_golden_refs(mxt); break; case CMD_BRIDGE_SERVER: mxt_verb(ctx, "CMD_BRIDGE_SERVER"); mxt_verb(ctx, "port:%u", port); ret = mxt_socket_server(mxt, port); break; case CMD_BRIDGE_CLIENT: mxt_verb(ctx, "CMD_BRIDGE_CLIENT"); ret = mxt_socket_client(mxt, strbuf, port); break; case CMD_SERIAL_DATA: mxt_verb(ctx, "CMD_SERIAL_DATA"); mxt_verb(ctx, "t68_datatype:%u", t68_datatype); ret = mxt_serial_data_upload(mxt, strbuf, t68_datatype); break; case CMD_TEST: mxt_verb(ctx, "CMD_TEST"); ret = run_self_tests(mxt, self_test_cmd); break; case CMD_FLASH: mxt_verb(ctx, "CMD_FLASH"); ret = mxt_flash_firmware(ctx, mxt, strbuf, strbuf2, conn); break; case CMD_RESET: mxt_verb(ctx, "CMD_RESET"); ret = mxt_reset_chip(mxt, false); break; case CMD_RESET_BOOTLOADER: mxt_verb(ctx, "CMD_RESET_BOOTLOADER"); ret = mxt_reset_chip(mxt, true); break; case CMD_BOOTLOADER_VERSION: mxt_verb(ctx, "CMD_RESET_BOOTLOADER"); ret = mxt_bootloader_version(ctx, mxt, conn); break; case CMD_MESSAGES: // Messages handled after switch break; case CMD_BACKUP: mxt_verb(ctx, "CMD_BACKUP"); ret = mxt_backup_config(mxt, backup_cmd); break; case CMD_CALIBRATE: mxt_verb(ctx, "CMD_CALIBRATE"); ret = mxt_calibrate_chip(mxt); break; case CMD_DEBUG_DUMP: mxt_verb(ctx, "CMD_DEBUG_DUMP"); mxt_verb(ctx, "mode:%u", t37_mode); mxt_verb(ctx, "frames:%u", t37_frames); ret = mxt_debug_dump(mxt, t37_mode, strbuf, t37_frames); break; case CMD_ZERO_CFG: mxt_verb(ctx, "CMD_ZERO_CFG"); ret = mxt_zero_config(mxt); if (ret) mxt_err(ctx, "Error zeroing all configuration settings"); break; case CMD_LOAD_CFG: mxt_verb(ctx, "CMD_LOAD_CFG"); mxt_verb(ctx, "filename:%s", strbuf); ret = mxt_load_config_file(mxt, strbuf); if (ret) { mxt_err(ctx, "Error loading the configuration"); } else { mxt_info(ctx, "Configuration loaded"); ret = mxt_backup_config(mxt, backup_cmd); if (ret) { mxt_err(ctx, "Error backing up"); } else { mxt_info(ctx, "Configuration backed up"); ret = mxt_reset_chip(mxt, false); if (ret) { mxt_err(ctx, "Error resetting"); } else { mxt_info(ctx, "Chip reset"); } } } break; case CMD_SAVE_CFG: mxt_verb(ctx, "CMD_SAVE_CFG"); mxt_verb(ctx, "filename:%s", strbuf); ret = mxt_save_config_file(mxt, strbuf); break; case CMD_SELF_CAP_TUNE_CONFIG: case CMD_SELF_CAP_TUNE_NVRAM: mxt_verb(ctx, "CMD_SELF_CAP_TUNE"); ret = mxt_self_cap_tune(mxt, cmd); break; case CMD_CRC_CHECK: mxt_verb(ctx, "CMD_CRC_CHECK"); mxt_verb(ctx, "filename:%s", strbuf); ret = mxt_checkcrc(ctx, mxt, strbuf); break; case CMD_NONE: default: mxt_verb(ctx, "cmd: %d", cmd); mxt_set_log_fn(ctx, mxt_log_stdout); if (verbose <= 2) mxt_set_log_level(ctx, 2); ret = mxt_menu(mxt); break; } if (cmd == CMD_MESSAGES || (msgs_enabled && ret == MXT_SUCCESS)) { mxt_verb(ctx, "CMD_MESSAGES"); mxt_verb(ctx, "msgs_timeout:%d", msgs_timeout); // Support message filtering with -T if (cmd == CMD_MESSAGES && !msg_filter_type) msg_filter_type = object_type; ret = print_raw_messages(mxt, msgs_timeout, msg_filter_type); } if (cmd != CMD_FLASH && cmd != CMD_BOOTLOADER_VERSION && mxt) { mxt_set_debug(mxt, false); mxt_free_device(mxt); mxt_unref_conn(conn); } free: mxt_free(ctx); return ret; }
//****************************************************************************** /// \brief Check sysfs device directory for correct attributes /// \return #mxt_rc static int scan_sysfs_directory(struct libmaxtouch_ctx *ctx, struct mxt_conn_info **conn, struct dirent *i2c_dir, const char *dirname, bool acpi) { char *pszDirname; size_t length; DIR *pDirectory; struct dirent *pEntry; bool mem_access_found = false; bool debug_found = false; bool debug_v2_found = false; int ret; length = strlen(dirname) + strlen(i2c_dir->d_name) + 2; if ((pszDirname = (char *)calloc(length, sizeof(char))) == NULL) { ret = MXT_ERROR_NO_MEM; goto free; } snprintf(pszDirname, length, "%s/%s", dirname, i2c_dir->d_name); pDirectory = opendir(pszDirname); if (!pDirectory) { ret = MXT_ERROR_NO_MEM; goto free; } while ((pEntry = readdir(pDirectory)) != NULL) { if (!strcmp(pEntry->d_name, "mem_access")) { mxt_dbg(ctx, "Found mem_access interface at %s/mem_access", pszDirname); mem_access_found = true; } else if (!strcmp(pEntry->d_name, "debug_enable")) { mxt_dbg(ctx, "Found debug_enable interface at %s/debug_enable", pszDirname); debug_found = true; } else if (!strcmp(pEntry->d_name, "debug_msg")) { mxt_dbg(ctx, "Found Debug V2 at %s/debug_msg", pszDirname); debug_v2_found = true; } } /* If device found, store it and return success */ if (mem_access_found && (debug_found || debug_v2_found)) { ctx->scan_count++; if (ctx->query) { printf("sysfs:%s Atmel %s interface\n", pszDirname, debug_v2_found ? "Debug V2" : "Debug"); } else { ret = sysfs_new_connection(ctx, conn, pszDirname, acpi); mxt_dbg(ctx, "Found %s", pszDirname); goto close; } } else { mxt_verb(ctx, "Ignoring %s", pszDirname); } ret = MXT_ERROR_NO_DEVICE; close: (void)closedir(pDirectory); free: free(pszDirname); return ret; }
/* Takes n = (degree+1) doubles, and fills in result with the n * coefficients of the polynomial that will fit them. */ bool ft_polyfit(struct libmaxtouch_ctx *ctx, double *xdata, double *ydata, double *result, int len) { int k, j, i; int n = POLY_DEGREE + 1; double mat1[n * n]; double mat2[n]; double d; /* build input matrix - x values */ for (i = 0; i < n; i++) { /* each matrix row */ for (j = 0; j < n; j++) { mat1[i * n + j] = 0; /* each matrix col */ for (k = 0; k < len; k++) { /* for each x value */ mat1[i * n + j] += pow(xdata[k], (double)(i + j)); mxt_verb(ctx, "i: %d j: %d k: %d, mat1[%d] = %0.2f", i, j, k, i * n + j, mat1[i * n + j]); } } } print_matrix(ctx, mat1, n); /* build & print resultant matrix - y values */ mxt_verb(ctx, "\nResultant:"); mat2[0] = 0; /* first row just the sum of y values */ for (i = 0; i < len; i++) mat2[0] += ydata[i]; mxt_verb(ctx, "mat2[0] = %0.2f", mat2[0]); for (i = 1; i < n; i++) { mat2[i] = 0; for (j = 0; j < len; j++) mat2[i] += ydata[j] * pow(xdata[j], i); mxt_verb(ctx, "mat2[%d] = %0.2f", i, mat2[i]); } /* Do Gauss-Jordan elimination on mat1. */ for (i = 0; i < n; i++) { int lindex; double largest; /* choose largest pivot */ for (j=i, largest = mat1[i * n + i], lindex = i; j < n; j++) { if (fabs(mat1[j * n + i]) > largest) { largest = fabs(mat1[j * n + i]); lindex = j; } } if (lindex != i) { /* swap rows i and lindex */ for (k = 0; k < n; k++) { d = mat1[i * n + k]; mat1[i * n + k] = mat1[lindex * n + k]; mat1[lindex * n + k] = d; } d = mat2[i]; mat2[i] = mat2[lindex]; mat2[lindex] = d; } /* Make sure we have a non-zero pivot. */ if (mat1[i * n + i] == 0.0) { /* this should be rotated. */ mxt_err(ctx, "ERROR: Non-zero pivot"); return false; } for (j = i + 1; j < n; j++) { d = mat1[j * n + i] / mat1[i * n + i]; for (k = 0; k < n; k++) mat1[j * n + k] -= d * mat1[i * n + k]; mat2[j] -= d * mat2[i]; } } for (i = n - 1; i > 0; i--) for (j = i - 1; j >= 0; j--) { d = mat1[j * n + i] / mat1[i * n + i]; for (k = 0; k < n; k++) mat1[j * n + k] -= d * mat1[i * n + k]; mat2[j] -= d * mat2[i]; } /* Now write the stuff into the result vector. */ for (i = 0; i < n; i++) { result[i] = mat2[i] / mat1[i * n + i]; mxt_verb(ctx, "coeff[%d] = %f", i, result[i]); } return true; }
//****************************************************************************** /// \brief Read hex encoded data from file /// \return #mxt_rc static int mxt_t68_load_file(struct t68_ctx *ctx) { int ret; uint8_t value = 0; FILE *fp; bool file_read = false; char buf[256]; uint16_t hexcount; int c; /* open file */ fp = fopen(ctx->filename, "r"); if (fp == NULL) { mxt_err(ctx->lc, "Error opening %s", ctx->filename); return mxt_errno_to_rc(errno); } ret = mxt_buf_init(&ctx->buf); if (ret) { mxt_err(ctx->lc, "Error initialising buffer"); goto close; } while (!file_read) { /* Read next value from file */ c = getc(fp); if (c == EOF) { break; } /* skip spaces, newlines, commas*/ else if (c == 0x20 || c == '\r' || c == '\n' || c == ',') { continue; } /* Ignore comment lines */ else if (c == '[') { // Grab comment key if (fscanf(fp, "%255[^]]", buf) != 1) { ret = MXT_ERROR_FILE_FORMAT; goto fail; } mxt_verb(ctx->lc, "[%s]", buf); if (!strncasecmp(buf, "datatype=", 9)) { if (sscanf(buf + 9, "%d", &c) != 1) { mxt_warn(ctx->lc, "Unable to parse datatype"); } else { ctx->t68_datatype = c; mxt_info(ctx->lc, "DATATYPE set to %u by file", ctx->t68_datatype); } } // Read until end of line while (c != '\n') { c = getc(fp); } continue; } /* A value looks like "0xABu," */ else if (c == '0') { if (fscanf(fp, "x%2su", (char *)&buf) != 1) { mxt_err(ctx->lc, "Parse error"); ret = MXT_ERROR_FILE_FORMAT; goto fail; } ret = mxt_convert_hex(buf, &value, &hexcount, 3); if (ret) goto fail; ret = mxt_buf_add(&ctx->buf, value); if (ret) goto fail; } else { mxt_err(ctx->lc, "Unexpected character \"%c\"", c); ret = MXT_ERROR_FILE_FORMAT; goto fail; } } mxt_info(ctx->lc, "Loaded file %s, %zu bytes", ctx->filename, ctx->buf.size); return MXT_SUCCESS; fail: mxt_buf_free(&ctx->buf); close: fclose(fp); return ret; }
//****************************************************************************** /// \brief Read and deal with incoming command /// \return #mxt_rc static int handle_cmd(struct mxt_device *mxt, struct bridge_context *bridge_ctx) { int ret; uint16_t address; uint16_t count; struct mxt_buffer linebuf; char *line; int offset; ret = mxt_buf_init(&linebuf); if (ret) return ret; ret = readline(mxt, bridge_ctx->sockfd, &linebuf); if (ret) { mxt_dbg(mxt->ctx, "Error reading or peer closed socket"); goto free; } line = (char *)linebuf.data; if (strlen(line) == 0) { ret = MXT_SUCCESS; goto free; } mxt_verb(mxt->ctx, "%s", line); if (!strcmp(line, "SAT")) { mxt_info(mxt->ctx, "Server attached"); ret = MXT_SUCCESS; } else if (!strcmp(line, "SDT")) { mxt_info(mxt->ctx, "Server detached"); ret = MXT_SUCCESS; } else if (sscanf(line, "REA %" SCNu16 " %" SCNu16, &address, &count) == 2) { ret = bridge_rea_cmd(mxt, bridge_ctx, address, count); } else if (sscanf(line, "WRI %" SCNu16 "%n", &address, &offset) == 1) { /* skip space */ offset += 1; ret = bridge_wri_cmd(mxt, bridge_ctx, address, line + offset, strlen(line) - offset); } else if (sscanf(line, "RST %" SCNu16 "%n", &address, &offset) == 1) { ret = bridge_handle_reset(mxt, bridge_ctx, address); ret = MXT_SUCCESS; } else if (sscanf(line, "MSGCFG %" SCNu16 "%n", &address, &offset) == 1) { mxt_info(mxt->ctx, "Configuring Messages"); bridge_ctx->msgs_enabled = true; const char * const msg = "MSGCFG OK\n"; ret = write(bridge_ctx->sockfd, msg, strlen(msg)); if (ret < 0) { mxt_err(mxt->ctx, "Socket write error: %s (%d)", strerror(errno), errno); ret = mxt_errno_to_rc(errno); } ret = MXT_SUCCESS; } else { mxt_info(mxt->ctx, "Unrecognised cmd \"%s\"", line); ret = MXT_SUCCESS; } free: mxt_buf_free(&linebuf); return ret; }