//****************************************************************************** /// \brief Perform fallback reset /// \return #mxt_rc static int mxt_send_reset_command(struct mxt_device *mxt, bool bootloader_mode) { int ret; uint16_t t6_addr; unsigned char write_value = RESET_COMMAND; /* Obtain command processor's address */ t6_addr = mxt_get_object_address(mxt, GEN_COMMANDPROCESSOR_T6, 0); if (t6_addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; /* The value written determines which mode the chip will boot into */ if (bootloader_mode) { mxt_info(mxt->ctx, "Resetting in bootloader mode"); write_value = BOOTLOADER_COMMAND; } else { mxt_info(mxt->ctx, "Sending reset command"); } /* Write to command processor register to perform command */ ret = mxt_write_register ( mxt, &write_value, t6_addr + MXT_T6_RESET_OFFSET, 1 ); return ret; }
//****************************************************************************** /// \brief Print T25 limits for each enabled touch object static void print_t25_limits(struct mxt_device *mxt, uint16_t t25_addr) { int i; struct mxt_object obj; int touch_object = 0; uint8_t buf[4]; uint16_t upsiglim; uint16_t losiglim; int instance; for (i = 0; i < mxt->info.id->num_objects; i++) { obj = mxt->info.objects[i]; switch (obj.type) { case TOUCH_MULTITOUCHSCREEN_T9: case TOUCH_SINGLETOUCHSCREEN_T10: case TOUCH_XSLIDER_T11: case TOUCH_YSLIDER_T12: case TOUCH_XWHEEL_T13: case TOUCH_YWHEEL_T14: case TOUCH_KEYARRAY_T15: case TOUCH_PROXIMITY_T23: case TOUCH_KEYSET_T31: case TOUCH_XSLIDERSET_T32: for (instance = 0; (instance < MXT_INSTANCES(obj)); instance++) { mxt_read_register(mxt, (uint8_t *)&buf, mxt_get_start_position(obj, instance), 1); mxt_info(mxt->ctx, "%s[%d] %s", mxt_get_object_name(obj.type), instance, buf[0] & 0x01 ? "enabled":"disabled"); mxt_read_register(mxt, (uint8_t *)&buf, t25_addr + 2 + touch_object * 4, 4); upsiglim = (uint16_t)((buf[1] << 8u) | buf[0]); losiglim = (uint16_t)((buf[3] << 8u) | buf[2]); mxt_info(mxt->ctx, " UPSIGLIM:%d", upsiglim); mxt_info(mxt->ctx, " LOSIGLIM:%d", losiglim); touch_object++; } break; default: break; } } }
//****************************************************************************** /// \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 Logs information about the chip. */ void mxt_display_chip_info(struct mxt_device *mxt) { struct mxt_object obj; char firmware_version[MXT_FW_VER_LEN]; struct mxt_id_info *id = mxt->info.id; int i; mxt_get_firmware_version(mxt, (char *)&firmware_version); /* Display ID information */ mxt_info(mxt->ctx, "Family ID = %u (0x%02X)", id->family, id->family); mxt_info(mxt->ctx, "Variant ID = %u (0x%02X)", id->variant, id->variant); mxt_info(mxt->ctx, "Firmware Version = %s", firmware_version); mxt_info(mxt->ctx, "Matrix X Size = %d", id->matrix_x_size); mxt_info(mxt->ctx, "Matrix Y Size = %d", id->matrix_y_size); mxt_info(mxt->ctx, "Number of elements in the Object Table = %d", id->num_objects); /* Display information about specific objects */ for (i = 0; i < id->num_objects; i++) { obj = mxt->info.objects[i]; mxt_info(mxt->ctx, "T%u size:%u instances:%u address:%u", obj.type, MXT_SIZE(obj), MXT_INSTANCES(obj), mxt_get_start_position(obj, 0)); } }
//****************************************************************************** /// \brief Write DATATYPE /// \return #mxt_rc static int mxt_t68_write_datatype(struct t68_ctx *ctx) { uint8_t buf[2]; buf[0] = (ctx->t68_datatype & 0xFF); buf[1] = (ctx->t68_datatype & 0xFF00) >> 8; mxt_info(ctx->lc, "Writing %u to DATATYPE register", ctx->t68_datatype); return mxt_write_register(ctx->mxt, &buf[0], ctx->t68_addr + T68_DATATYPE, sizeof(buf)); }
//****************************************************************************** /// \brief Handle bridge read /// \return #mxt_rc static int bridge_rea_cmd(struct mxt_device *mxt, int sockfd, uint16_t address, uint16_t count) { int ret; uint8_t *databuf; char *response; const char * const PREFIX = "RRP "; size_t response_len; int i; databuf = calloc(count, sizeof(uint8_t)); if (!databuf) { mxt_err(mxt->ctx, "Failed to allocate memory"); return MXT_ERROR_NO_MEM; } /* Allow for newline/null byte */ response_len = strlen(PREFIX) + count*2 + 1; response = calloc(response_len, sizeof(uint8_t)); if (!response) { mxt_err(mxt->ctx, "Failed to allocate memory"); ret = MXT_ERROR_NO_MEM; goto free_databuf; } strcpy(response, PREFIX); ret = mxt_read_register(mxt, databuf, address, count); if (ret) { mxt_warn(mxt->ctx, "RRP ERR"); strcpy(response + strlen(PREFIX), "ERR\n"); response_len = strlen(response); } else { for (i = 0; i < count; i++) { sprintf(response + strlen(PREFIX) + i*2, "%02X", databuf[i]); } mxt_info(mxt->ctx, "%s", response); response[response_len - 1] = '\n'; } ret = write(sockfd, response, response_len); if (ret < 0) { mxt_err(mxt->ctx, "Socket write error: %s (%d)", strerror(errno), errno); ret = mxt_errno_to_rc(errno); goto free; } ret = MXT_SUCCESS; free: free(response); free_databuf: free(databuf); return ret; }
//****************************************************************************** /// \brief Print T68 status messages static void mxt_t68_print_status(struct t68_ctx *ctx, uint8_t status) { mxt_info(ctx->lc, "T68 status: %02X %s%s%s%s%s%s%s", status, (status == 0x00) ? "Success/No error" : "", (status == 0x01) ? "Command supplied in CMD.COMMAND is out of sequence" : "", (status == 0x02) ? "Supplied DATATYPE value is not supported" : "", (status == 0x03) ? "Supplied LENGTH value exceeds length of DATA[] array" : "", (status == 0x04) ? "More bytes supplied than can be accommodated by this data type" : "", (status == 0x05) ? "Data content is invalid" : "", (status == 0x0F) ? "The action could not be completed due to an error outside of this object" : ""); }
//****************************************************************************** /// \brief Run self test int run_self_tests(struct mxt_device *mxt, uint8_t cmd) { uint16_t t25_addr; uint8_t enable = 3; mxt_msg_reset(mxt); // Enable self test object & reporting t25_addr = mxt_get_object_address(mxt, SPT_SELFTEST_T25, 0); mxt_info(mxt->ctx, "Enabling self test object"); mxt_write_register(mxt, &enable, t25_addr, 1); mxt_info(mxt->ctx, "Disabling noise suppression"); disable_noise_suppression(mxt); print_t25_limits(mxt, t25_addr); mxt_info(mxt->ctx, "Running tests"); mxt_write_register(mxt, &cmd, t25_addr + 1, 1); return self_test_handle_messages(mxt); }
//****************************************************************************** /// \brief Send frames of T68 data to chip /// \return #mxt_rc static int mxt_t68_send_frames(struct t68_ctx *ctx) { int ret; size_t offset = 0; uint16_t frame_size; int frame = 1; uint8_t cmd; while (offset < ctx->buf.size) { frame_size = MIN(ctx->buf.size - offset, ctx->t68_data_size); mxt_info(ctx->lc, "Writing frame %u, %u bytes", frame, frame_size); if (frame_size > UCHAR_MAX) { mxt_err(ctx->lc, "Serial data frame size miscalculation"); return MXT_INTERNAL_ERROR; } ret = mxt_write_register(ctx->mxt, ctx->buf.data + offset, ctx->t68_addr + T68_DATA, frame_size); if (ret) return ret; ret = mxt_t68_write_length(ctx, frame_size); if (ret) return ret; offset += frame_size; if (frame == 1) cmd = T68_CMD_START; else if (offset >= ctx->buf.size) cmd = T68_CMD_END; else cmd = T68_CMD_CONTINUE; ret = mxt_t68_command(ctx, cmd); if (ret) return ret; frame++; } return MXT_SUCCESS; }
//****************************************************************************** /// \brief Handle calibration messages /// \return #mxt_rc static int handle_calibrate_msg(struct mxt_device *mxt, uint8_t *msg, void *context, uint8_t size) { int *last_status = context; int status = msg[1]; if (mxt_report_id_to_type(mxt, msg[0]) == GEN_COMMANDPROCESSOR_T6) { if (status & 0x10) { mxt_dbg(mxt->ctx, "Device calibrating"); } else if (!(status & 0x10) && (*last_status & 0x10)) { mxt_info(mxt->ctx, "Device calibrated"); return MXT_SUCCESS; } *last_status = status; } return MXT_MSG_CONTINUE; }
//****************************************************************************** /// \brief Open device /// \return #mxt_rc int sysfs_open(struct mxt_device *mxt) { struct sysfs_conn_info *conn = &mxt->conn->sysfs; char *filename; struct stat filestat; int ret; mxt->sysfs.path_max = strlen(conn->path) + 20; // Allocate temporary path space mxt->sysfs.temp_path = calloc(mxt->sysfs.path_max + 1, sizeof(char)); if (!mxt->sysfs.temp_path) return MXT_ERROR_NO_MEM; // Cache memory access path for fast access mxt->sysfs.mem_access_path = calloc(mxt->sysfs.path_max + 1, sizeof(char)); if (!mxt->sysfs.mem_access_path) return MXT_ERROR_NO_MEM; snprintf(mxt->sysfs.mem_access_path, mxt->sysfs.path_max, "%s/mem_access", conn->path); // Check whether debug v2 or not filename = make_path(mxt, "debug_msg"); ret = stat(filename, &filestat); if (ret < 0) { if (errno == ENOENT) { mxt->sysfs.debug_v2 = false; } else { mxt_err(mxt->ctx, "Could not stat %s, error %s (%d)", filename, strerror(errno), errno); return mxt_errno_to_rc(errno); } } else { mxt->sysfs.debug_v2 = true; } mxt_info(mxt->ctx, "Registered sysfs path:%s", conn->path); return MXT_SUCCESS; }
//****************************************************************************** /// \brief Handle bridge reset command /// \return #mxt_rc static int bridge_handle_reset(struct mxt_device *mxt, struct bridge_context *bridge_ctx, uint16_t address) { int ret; char *response; const char * const PREFIX = "RST "; size_t response_len = 8; /* Allow for newline/null byte */ response = calloc(response_len, sizeof(uint8_t)); if (!response) { mxt_err(mxt->ctx, "Failed to allocate memory"); ret = MXT_ERROR_NO_MEM; goto free; } strcpy(response, PREFIX); ret = mxt_reset_chip(mxt, false); if (ret) { mxt_warn(mxt->ctx, "RST ERR"); strcpy(response + strlen(PREFIX), "ERR\n"); response_len = strlen(response); } else { mxt_info(mxt->ctx, "RST OK"); strcpy(response + strlen(PREFIX), "OK\n"); response_len = strlen(response); } ret = write(bridge_ctx->sockfd, response, response_len); if (ret < 0) { mxt_err(mxt->ctx, "Socket write error: %s (%d)", strerror(errno), errno); ret = mxt_errno_to_rc(errno); goto free; } ret = MXT_SUCCESS; free: free(response); return ret; }
//****************************************************************************** /// \brief Bridge client /// \return #mxt_rc int mxt_socket_client(struct mxt_device *mxt, char *ip_address, uint16_t port) { struct hostent *server; struct bridge_context bridge_ctx; bridge_ctx.msgs_enabled = false; int ret; struct sockaddr_in serv_addr; server = gethostbyname(ip_address); if (server == NULL) { mxt_err(mxt->ctx, "Error, no such host"); return MXT_ERROR_CONNECTION_FAILURE; } bridge_ctx.sockfd = socket(AF_INET, SOCK_STREAM, 0); if (bridge_ctx.sockfd < 0) { mxt_err(mxt->ctx, "Socket error: %s (%d)", strerror(errno), errno); return MXT_ERROR_CONNECTION_FAILURE; } /* Set up socket options */ bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(port); /* Connect */ mxt_info(mxt->ctx, "Connecting to %s:%u", ip_address, port); ret = connect(bridge_ctx.sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); if (ret < 0) { mxt_err(mxt->ctx, "Connect error: %s (%d)", strerror(errno), errno); return MXT_ERROR_CONNECTION_FAILURE; } ret = bridge(mxt, &bridge_ctx); close(bridge_ctx.sockfd); return ret; }
//****************************************************************************** /// \brief Backup configuration settings to non-volatile memory /// \return #mxt_rc int mxt_backup_config(struct mxt_device *mxt, uint8_t backup_command) { int ret; uint16_t t6_addr; /* Obtain command processor's address */ t6_addr = mxt_get_object_address(mxt, GEN_COMMANDPROCESSOR_T6, 0); if (t6_addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; /* Write to command processor register to perform command */ ret = mxt_write_register ( mxt, &backup_command, t6_addr + MXT_T6_BACKUPNV_OFFSET, 1 ); if (ret == MXT_SUCCESS) mxt_info(mxt->ctx, "Backed up settings to the non-volatile memory"); else mxt_err(mxt->ctx, "Failed to back up settings"); return ret; }
//****************************************************************************** /// \brief Calibrate maxtouch chip /// \return 0 = success, negative = fail int mxt_calibrate_chip(struct mxt_device *mxt) { int ret; uint16_t t6_addr; unsigned char write_value = CALIBRATE_COMMAND; /* Obtain command processor's address */ t6_addr = mxt_get_object_address(mxt, GEN_COMMANDPROCESSOR_T6, 0); if (t6_addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; mxt_flush_msgs(mxt); /* Write to command processor register to perform command */ ret = mxt_write_register(mxt, &write_value, t6_addr + MXT_T6_CALIBRATE_OFFSET, 1); if (ret == 0) { mxt_info(mxt->ctx, "Sent calibration command"); } else { mxt_err(mxt->ctx, "Failed to send calibration command"); } int state = 0; int flag = false; ret = mxt_read_messages(mxt, MXT_CALIBRATE_TIMEOUT, &state, handle_calibrate_msg, &flag); if (ret == MXT_ERROR_TIMEOUT) { mxt_warn(mxt->ctx, "WARN: timed out waiting for calibrate status"); return MXT_SUCCESS; } else if (ret) { mxt_err(mxt->ctx, "FAIL: device calibration failed"); return ret; } return MXT_SUCCESS; }
//****************************************************************************** /// \brief Issue REPORTALL command to device /// \return #mxt_rc int mxt_report_all(struct mxt_device *mxt) { int ret; uint16_t t6_addr; const uint8_t report_all_cmd = 0xff; /* Obtain command processor's address */ t6_addr = mxt_get_object_address(mxt, GEN_COMMANDPROCESSOR_T6, 0); if (t6_addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; /* Write to command processor register to perform command */ ret = mxt_write_register ( mxt, &report_all_cmd, t6_addr + MXT_T6_REPORTALL_OFFSET, 1 ); if (ret == MXT_SUCCESS) mxt_info(mxt->ctx, "REPORTALL command issued"); else mxt_err(mxt->ctx, "Failed to issue REPORTALL command"); 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 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 Run self cap tuning procedure without updating the config /// checksum /// \return #mxt_rc int mxt_self_cap_tune(struct mxt_device *mxt, mxt_app_cmd cmd) { int ret; uint16_t t6_addr; uint16_t t109_addr; uint8_t backupnv_value; uint8_t t109_command; mxt_msg_reset(mxt); // Enable self test object & reporting t6_addr = mxt_get_object_address(mxt, GEN_COMMANDPROCESSOR_T6, 0); if (t6_addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; t109_addr = mxt_get_object_address(mxt, SPT_SELFCAPGLOBALCONFIG_T109, 0); if (t109_addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; mxt_info(mxt->ctx, "Stopping T70"); backupnv_value = 0x33; ret = mxt_write_register(mxt, &backupnv_value, t6_addr + MXT_T6_BACKUPNV_OFFSET, 1); if (ret) return ret; // Wait for backup operation to complete (otherwise T109 report may be missed) mxt_msg_wait(mxt, 100); mxt_info(mxt->ctx, "Tuning"); t109_command = T109_CMD_TUNE; mxt_info(mxt->ctx, "Writing %u to T109 CMD register", cmd); ret = mxt_write_register(mxt, &t109_command, t109_addr + T109_CMD_OFFSET, 1); if (ret) return ret; ret = mxt_read_messages(mxt, T109_TIMEOUT, &t109_command, mxt_self_cap_command, (int *)&mxt_sigint_rx); if (ret) return ret; switch (cmd) { case CMD_SELF_CAP_TUNE_CONFIG: mxt_info(mxt->ctx, "Store to Config"); t109_command = T109_CMD_STORE_TO_CONFIG_RAM; break; default: case CMD_SELF_CAP_TUNE_NVRAM: mxt_info(mxt->ctx, "Store to NVRAM"); t109_command = T109_CMD_STORE_TO_NVM; break; } mxt_info(mxt->ctx, "Writing %u to T109 CMD register", cmd); ret = mxt_write_register(mxt, &t109_command, t109_addr + T109_CMD_OFFSET, 1); if (ret) return ret; ret = mxt_read_messages(mxt, 100, (void *) &t109_command, mxt_self_cap_command, (int *)&mxt_sigint_rx); if (ret) return ret; mxt_info(mxt->ctx, "Saving configuration"); ret = mxt_backup_config(mxt, BACKUPNV_COMMAND); if (ret) return ret; ret = mxt_reset_chip(mxt, false); if (ret) return ret; return MXT_SUCCESS; }
//****************************************************************************** /// \brief Main bridge function to handle a single connection /// \return #mxt_rc static int bridge(struct mxt_device *mxt, int sockfd) { int ret, pollret; struct pollfd fds[2]; int fopts = 0; int debug_ng_fd = 0; int numfds = 1; int timeout; mxt_info(mxt->ctx, "Connected"); ret = mxt_msg_reset(mxt); if (ret) mxt_err(mxt->ctx, "Failure to reset msgs"); fds[0].fd = sockfd; fds[0].events = POLLIN | POLLERR; ret = send_chip_attach(mxt, sockfd); if (ret) return ret; while (1) { debug_ng_fd = mxt_get_msg_poll_fd(mxt); if (debug_ng_fd) { fds[1].fd = debug_ng_fd; fds[1].events = POLLPRI; numfds = 2; timeout = -1; } else { timeout = 100; // milliseconds } pollret = poll(fds, numfds, timeout); if (pollret == -1 && errno == EINTR) { mxt_dbg(mxt->ctx, "Interrupted"); continue; } else if (pollret == -1) { mxt_err(mxt->ctx, "Poll returned %d (%s)", errno, strerror(errno)); ret = mxt_errno_to_rc(errno); goto disconnect; } /* Detect socket disconnect */ if (fcntl(sockfd, F_GETFL, &fopts) < 0) { ret = MXT_SUCCESS; mxt_dbg(mxt->ctx, "Socket disconnected"); goto disconnect; } if (fds[0].revents) { ret = handle_cmd(mxt, sockfd); if (ret) { mxt_dbg(mxt->ctx, "handle_cmd returned %d", ret); goto disconnect; } } /* If timeout or msg poll fd event */ if (pollret == 0 || fds[1].revents) { ret = handle_messages(mxt, sockfd); if (ret) goto disconnect; } } disconnect: mxt_info(mxt->ctx, "Disconnected"); return ret; }
//****************************************************************************** /// \brief Upload file to T68 Serial Data Object /// \return #mxt_rc int mxt_serial_data_upload(struct mxt_device *mxt, const char *filename, uint16_t datatype) { int ret; struct t68_ctx ctx; ctx.mxt = mxt; ctx.lc = mxt->ctx; ret = mxt_msg_reset(mxt); if (ret) return ret; mxt_info(ctx.lc, "Checking T7 Power Config"); ret = mxt_t68_check_power_cfg(&ctx); if (ret) return ret; /* Check for existence of T68 object */ ctx.t68_addr = mxt_get_object_address(mxt, SERIAL_DATA_COMMAND_T68, 0); if (ctx.t68_addr == OBJECT_NOT_FOUND) return MXT_ERROR_OBJECT_NOT_FOUND; /* Calculate position of CMD register */ ctx.t68_size = mxt_get_object_size(mxt, SERIAL_DATA_COMMAND_T68); ctx.t68_cmd_addr = ctx.t68_addr + ctx.t68_size - 3; /* Calculate frame size */ ctx.t68_data_size = ctx.t68_size - 9; /* Set datatype from command line */ ctx.t68_datatype = datatype; /* Read input file */ ctx.filename = filename; ret = mxt_t68_load_file(&ctx); if (ret) return ret; ret = mxt_t68_enable(&ctx); if (ret) goto release; ret = mxt_t68_zero_data(&ctx); if (ret) goto release; ret = mxt_t68_write_length(&ctx, 0); if (ret) goto release; mxt_info(ctx.lc, "Configuring T68"); ret = mxt_t68_write_datatype(&ctx); if (ret) goto release; mxt_info(ctx.lc, "Sending data"); ret = mxt_t68_send_frames(&ctx); if (ret) { mxt_err(ctx.lc, "Error sending data"); goto release; } mxt_info(ctx.lc, "Done"); ret = MXT_SUCCESS; release: mxt_buf_free(&ctx.buf); return ret; }
//****************************************************************************** /// \brief Retrieve data from the T37 Diagnostic Data object /// \return #mxt_rc int mxt_debug_dump(struct mxt_device *mxt, int mode, const char *csv_file, uint16_t frames) { struct t37_ctx ctx; time_t t1; time_t t2; int ret; ctx.lc = mxt->ctx; ctx.mxt = mxt; ctx.mode = mode; if (frames == 0) { mxt_warn(ctx.lc, "Defaulting to 1 frame"); frames = 1; } ret = mxt_debug_dump_initialise(&ctx); if (ret) return ret; /* Open Hawkeye output file */ ctx.hawkeye = fopen(csv_file,"w"); if (!ctx.hawkeye) { mxt_err(ctx.lc, "Failed to open file!"); ret = MXT_ERROR_IO; goto free; } ret = mxt_generate_hawkeye_header(&ctx); if (ret) goto close; mxt_info(ctx.lc, "Reading %u frames", frames); t1 = time(NULL); for (ctx.frame = 1; ctx.frame <= frames; ctx.frame++) { if (ctx.self_cap) { ret = mxt_read_diagnostic_data_self_cap(&ctx); } else if (ctx.active_stylus){ ret = mxt_read_diagnostic_data_ast(&ctx); } else { ret = mxt_read_diagnostic_data_frame(&ctx); } if (ret) goto close; ret = mxt_hawkeye_output(&ctx); if (ret) goto close; } t2 = time(NULL); mxt_info(ctx.lc, "%u frames in %d seconds", frames, (int)(t2-t1)); ret = MXT_SUCCESS; close: fclose(ctx.hawkeye); free: free(ctx.data_buf); ctx.data_buf = NULL; free(ctx.t37_buf); ctx.t37_buf = NULL; return ret; }
//****************************************************************************** /// \brief Register hidraw device /// \return #mxt_rc int hidraw_register(struct mxt_device *mxt) { mxt_info(mxt->ctx, "Registered hidraw adapter:%s", mxt->conn->hidraw.node ); return MXT_SUCCESS; }
//****************************************************************************** /// \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 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; }