/* Régressions du protocole tactile : commandes invalides et trames concurrentes. */ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "sysemu/cpus.h" #include "hw/arm/ipod_touch_multitouch.c" #include "hw/arm/ipod_touch_gpio.c" #include "hw/arm/ipod_touch_sysic.c" #include "hw/arm/ipod_touch_spi.c" static IPodTouchMultitouchState *new_sensor(void) { IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH( object_new(TYPE_IPOD_TOUCH_MULTITOUCH)); ipod_touch_multitouch_realize(SSI_PERIPHERAL(s), &error_abort); return s; } static void destroy_sensor(IPodTouchMultitouchState *s) { finish_transfer(s); timer_free(s->touch_timer); timer_free(s->touch_end_timer); g_free(s->next_frame); g_free(s->read_frame); object_unref(OBJECT(s)); } static uint8_t transfer(IPodTouchMultitouchState *s, uint8_t byte) { return ipod_touch_multitouch_transfer(SSI_PERIPHERAL(s), byte); } static void test_unknown(void) { IPodTouchMultitouchState *s = new_sensor(); for (int i = 0; i < 10000; i++) { g_assert_cmpuint(transfer(s, 0xab), ==, 0xff); g_assert_cmpuint(s->cur_cmd, ==, 0); g_assert_null(s->in_buffer); } uint8_t out[16]; out[0] = transfer(s, MT_CMD_GET_INTERFACE_VERSION); for (int i = 1; i < 16; i++) out[i] = transfer(s, 0); g_assert_cmpuint(out[2], ==, MT_INTERFACE_VERSION); destroy_sensor(s); } static void test_snapshot(void) { IPodTouchMultitouchState *s = new_sensor(); MTFrame expected = *s->next_frame; uint8_t out[sizeof(MTFrame)]; out[0] = transfer(s, MT_CMD_FRAME_READ); for (int i = 1; i < 16; i++) out[i] = transfer(s, 0); g_free(s->next_frame); s->next_frame = get_frame(s, MT_EVENT_TOUCH_START, .4, .6, 100, 660, 580, 150); g_assert_cmpuint(s->cur_cmd, ==, 0); out[16] = transfer(s, 0xeb); out[17] = transfer(s, 0); out[18] = transfer(s, 1); for (size_t i = 19; i < sizeof out; i++) out[i] = transfer(s, 0); expected.frame_packet.unused1 = expected.frame_length.length1; expected.frame_packet.checksum_pad = -(expected.frame_packet.cmd + expected.frame_packet.unused1 + expected.frame_packet.length1 + expected.frame_packet.length2); g_assert_cmpmem(out, sizeof out, &expected, sizeof expected); g_assert_cmpuint(s->next_frame->finger_data.event, ==, MT_EVENT_TOUCH_START); destroy_sensor(s); } static void test_reports(void) { IPodTouchMultitouchState *s = new_sensor(); uint8_t out[16]; for (int cmd = MT_CMD_GET_REPORT_INFO; cmd <= MT_CMD_SHORT_CONTROL_READ; cmd += 3) { out[0] = transfer(s, cmd); out[1] = transfer(s, 0xff); for (int i = 2; i < 16; i++) out[i] = transfer(s, 0); g_assert_cmpuint(out[2], ==, 1); unsigned sum = 0; for (int i = 0; i < 14; i++) sum += out[i]; g_assert_cmpuint(lduw_le_p(out + 14), ==, sum); } transfer(s, MT_CMD_SHORT_CONTROL_READ); transfer(s, MT_REPORT_SENSOR_DIMENSIONS); for (int i = 2; i < 16; i++) out[i] = transfer(s, 0); g_assert_cmpuint(ldl_le_p(out + 3), ==, 5000); g_assert_cmpuint(ldl_le_p(out + 7), ==, 7500); destroy_sensor(s); } static void test_spi_abort(void) { IPodTouchSPIState *spi = IPOD_TOUCH_SPI(object_new(TYPE_IPOD_TOUCH_SPI)); DEVICE(spi)->id = g_strdup("spi-test"); set_spi_base(4); ipod_touch_spi_realize(DEVICE(spi), &error_abort); ipod_touch_spi_write(spi, R_TXCNT, 2, 4); ipod_touch_spi_write(spi, R_RXCNT, 2, 4); ipod_touch_spi_write(spi, R_TXDATA, MT_CMD_FRAME_READ, 4); ipod_touch_spi_write(spi, R_TXDATA, 2, 4); ipod_touch_spi_write(spi, R_CTRL, R_CTRL_RUN, 4); g_assert_cmpuint(spi->mt->in_buffer_ind, ==, 2); ipod_touch_spi_write(spi, R_CTRL, R_CTRL_TX_RESET | R_CTRL_RX_RESET, 4); g_assert_cmpuint(spi->mt->cur_cmd, ==, 0); g_assert_null(spi->mt->in_buffer); uint8_t response[16]; response[0] = transfer(spi->mt, MT_CMD_GET_INTERFACE_VERSION); for (int i = 1; i < 16; i++) response[i] = transfer(spi->mt, 0); g_assert_cmpuint(response[2], ==, MT_INTERFACE_VERSION); spi->last_irq = 1; fifo8_push(&spi->tx_fifo, 0x12); fifo8_push(&spi->rx_fifo, 0x34); device_cold_reset(DEVICE(spi)); g_assert_cmpuint(spi->last_irq, ==, 0); g_assert_true(fifo8_is_empty(&spi->tx_fifo)); g_assert_true(fifo8_is_empty(&spi->rx_fifo)); g_assert_cmpuint(ipod_touch_spi_read(spi, R_TXCNT, 4), ==, 0); timer_free(spi->mt->touch_timer); timer_free(spi->mt->touch_end_timer); g_free(spi->mt->next_frame); fifo8_destroy(&spi->tx_fifo); fifo8_destroy(&spi->rx_fifo); object_unref(OBJECT(spi)); } static void test_hbpp(void) { IPodTouchMultitouchState *s = new_sensor(); uint8_t bad[10] = {MT_CMD_HBPP_DATA_PACKET, 0, 0, 4}; for (int i = 0; i < 10; i++) transfer(s, bad[i]); g_assert_cmpuint(s->cur_cmd, ==, 0); uint8_t good[10] = {MT_CMD_HBPP_DATA_PACKET, 0, 0, 4, 0, 0, 0, 0, 0, 4}; for (int i = 0; i < 10; i++) transfer(s, good[i]); for (int i = 1; i < 21; i++) transfer(s, 0); g_assert_cmpuint(s->cur_cmd, ==, 0); destroy_sensor(s); } static void test_velocity(void) { IPodTouchMultitouchState *s = new_sensor(); MTFrame *f = get_frame(s, MT_EVENT_TOUCH_MOVED, .4, .6, 1, 1, 1, 1); g_free(f); f = get_frame(s, MT_EVENT_TOUCH_MOVED, .4, .7, 1, 1, 1, 1); g_assert_cmpint(f->finger_data.velX, ==, 0); g_assert_cmpint(f->finger_data.velY, >, 0); g_free(f); f = get_frame(s, MT_EVENT_TOUCH_MOVED, .4, .7, 1, 1, 1, 1); g_assert_cmpint(f->finger_data.velX, ==, 0); g_assert_cmpint(f->finger_data.velY, ==, 0); g_free(f); destroy_sensor(s); } static void test_buttons(void) { IPodTouchGPIOState *s = IPOD_TOUCH_GPIO(object_new(TYPE_IPOD_TOUCH_GPIO)); g_assert_cmpuint(GPIO2PAD(GPIO_BUTTON_HOME), <, NUM_GPIO_PADS); gpio_set_on(s->gpio_state, GPIO_BUTTON_HOME); gpio_set_on(s->gpio_state, GPIO_BUTTON_POWER); g_assert_true(gpio_is_on(s->gpio_state, GPIO_BUTTON_HOME)); g_assert_true(gpio_is_on(s->gpio_state, GPIO_BUTTON_POWER)); gpio_set_off(s->gpio_state, GPIO_BUTTON_HOME); g_assert_true(gpio_is_off(s->gpio_state, GPIO_BUTTON_HOME)); g_assert_true(gpio_is_on(s->gpio_state, GPIO_BUTTON_POWER)); object_unref(OBJECT(s)); } static void test_gpio_ranges(void) { IPodTouchSYSICState *s = IPOD_TOUCH_SYSIC(object_new(TYPE_IPOD_TOUCH_SYSIC)); uint32_t arrays[4][GPIO_NUMINTGROUPS]; s->gpio_int_level[0] = 0x1234; memcpy(arrays[0], s->gpio_int_level, sizeof arrays[0]); memcpy(arrays[1], s->gpio_int_status, sizeof arrays[1]); memcpy(arrays[2], s->gpio_int_enabled, sizeof arrays[2]); memcpy(arrays[3], s->gpio_int_type, sizeof arrays[3]); /* Les quatre trous entre groupes ne doivent pas accéder au tableau suivant. */ for (int addr = GPIO_INTLEVEL; addr <= GPIO_INTTYPE; addr += 0x20) { ipod_touch_sysic_write(s, addr + GPIO_NUMINTGROUPS * 4, 0xffffffff, 4); g_assert_cmpuint(ipod_touch_sysic_read(s, addr + GPIO_NUMINTGROUPS * 4, 4), ==, 0); } g_assert_cmpmem(arrays[0], sizeof arrays[0], s->gpio_int_level, sizeof arrays[0]); g_assert_cmpmem(arrays[1], sizeof arrays[1], s->gpio_int_status, sizeof arrays[1]); g_assert_cmpmem(arrays[2], sizeof arrays[2], s->gpio_int_enabled, sizeof arrays[2]); g_assert_cmpmem(arrays[3], sizeof arrays[3], s->gpio_int_type, sizeof arrays[3]); object_unref(OBJECT(s)); } static void test_reset(void) { IPodTouchMultitouchState *s = new_sensor(); IPodTouchSYSICState *sysic = IPOD_TOUCH_SYSIC(object_new(TYPE_IPOD_TOUCH_SYSIC)); s->sysic = sysic; ipod_touch_multitouch_on_touch(s); /* Capture d'une trame, puis interruption au milieu d'une autre commande. */ transfer(s, MT_CMD_FRAME_READ); for (int i = 1; i < 16; i++) transfer(s, 0); transfer(s, MT_CMD_GET_INTERFACE_VERSION); s->hbpp_atn_ack_response[0] = 0xaa; g_assert_true(timer_pending(s->touch_timer)); g_assert_nonnull(s->read_frame); g_assert_nonnull(s->out_buffer); device_cold_reset(DEVICE(s)); g_assert_false(timer_pending(s->touch_timer)); g_assert_false(timer_pending(s->touch_end_timer)); g_assert_false(s->touch_down); g_assert_null(s->read_frame); g_assert_null(s->in_buffer); g_assert_null(s->out_buffer); g_assert_cmpuint(s->cur_cmd, ==, 0); g_assert_cmpuint(s->hbpp_atn_ack_response[0], ==, 0); g_assert_cmpuint(sysic->gpio_int_status[3], ==, 0); g_assert_cmpuint(s->next_frame->finger_data.event, ==, MT_EVENT_TOUCH_FULL_END); transfer(s, MT_CMD_GET_INTERFACE_VERSION); transfer(s, 0); g_assert_cmpuint(transfer(s, 0), ==, MT_INTERFACE_VERSION); destroy_sensor(s); object_unref(OBJECT(sysic)); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); module_call_init(MODULE_INIT_QOM); qemu_init_main_loop(&error_abort); qemu_init_cpu_loop(); qemu_mutex_lock_iothread(); g_test_add_func("/ipod-multitouch/unknown", test_unknown); g_test_add_func("/ipod-multitouch/snapshot", test_snapshot); g_test_add_func("/ipod-multitouch/hbpp", test_hbpp); g_test_add_func("/ipod-multitouch/spi-abort", test_spi_abort); g_test_add_func("/ipod-multitouch/reports", test_reports); g_test_add_func("/ipod-multitouch/velocity", test_velocity); g_test_add_func("/ipod-multitouch/reset", test_reset); g_test_add_func("/ipod-input/buttons", test_buttons); g_test_add_func("/ipod-input/gpio-ranges", test_gpio_ranges); return g_test_run(); }