515 lines
18 KiB
C
515 lines
18 KiB
C
#include "hw/arm/ipod_touch_multitouch.h"
|
|
#include "qemu/log.h"
|
|
#include "qemu/bswap.h"
|
|
|
|
static void prepare_interface_version_response(IPodTouchMultitouchState *s) {
|
|
memset(s->out_buffer + 1, 0, 15);
|
|
|
|
// set the interface version
|
|
s->out_buffer[2] = MT_INTERFACE_VERSION;
|
|
|
|
// set the max packet size
|
|
s->out_buffer[3] = (MT_MAX_PACKET_SIZE & 0xFF);
|
|
s->out_buffer[4] = (MT_MAX_PACKET_SIZE >> 8) & 0xFF;
|
|
|
|
// compute and set the checksum
|
|
uint32_t checksum = 0;
|
|
for(int i = 0; i < 14; i++) {
|
|
checksum += s->out_buffer[i];
|
|
}
|
|
|
|
s->out_buffer[14] = (checksum & 0xFF);
|
|
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
|
}
|
|
|
|
static void prepare_cmd_status_response(IPodTouchMultitouchState *s) {
|
|
memset(s->out_buffer + 1, 0, 15);
|
|
|
|
// TODO we should probably set some CMD status here
|
|
|
|
// compute and set the checksum
|
|
uint32_t checksum = 0;
|
|
for(int i = 0; i < 14; i++) {
|
|
checksum += s->out_buffer[i];
|
|
}
|
|
|
|
s->out_buffer[14] = (checksum & 0xFF);
|
|
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
|
}
|
|
|
|
static void prepare_report_info_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
|
memset(s->out_buffer + 1, 0, 15);
|
|
|
|
// set the error
|
|
s->out_buffer[2] = 0;
|
|
|
|
// set the report length
|
|
uint32_t report_length = 0;
|
|
if(report_id == MT_REPORT_UNKNOWN1) {
|
|
report_length = MT_REPORT_UNKNOWN1_SIZE;
|
|
}
|
|
else if(report_id == MT_REPORT_FAMILY_ID) {
|
|
report_length = MT_REPORT_FAMILY_ID_SIZE;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
|
report_length = MT_REPORT_SENSOR_INFO_SIZE;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
|
report_length = MT_REPORT_SENSOR_REGION_DESC_SIZE;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
|
report_length = MT_REPORT_SENSOR_REGION_PARAM_SIZE;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
|
report_length = MT_REPORT_SENSOR_DIMENSIONS_SIZE;
|
|
}
|
|
else {
|
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: rapport inconnu 0x%02x\n",
|
|
__func__, report_id);
|
|
s->out_buffer[2] = 1;
|
|
}
|
|
|
|
s->out_buffer[3] = (report_length & 0xFF);
|
|
s->out_buffer[4] = (report_length >> 8) & 0xFF;
|
|
|
|
// compute and set the checksum
|
|
uint32_t checksum = 0;
|
|
for(int i = 0; i < 14; i++) {
|
|
checksum += s->out_buffer[i];
|
|
}
|
|
|
|
s->out_buffer[14] = (checksum & 0xFF);
|
|
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
|
}
|
|
|
|
static void prepare_short_control_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
|
memset(s->out_buffer + 1, 0, 15);
|
|
|
|
if(report_id == MT_REPORT_FAMILY_ID) {
|
|
s->out_buffer[3] = MT_FAMILY_ID;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
|
s->out_buffer[3] = MT_ENDIANNESS;
|
|
s->out_buffer[4] = MT_SENSOR_ROWS;
|
|
s->out_buffer[5] = MT_SENSOR_COLUMNS;
|
|
s->out_buffer[6] = (MT_BCD_VERSION & 0xFF);
|
|
s->out_buffer[7] = (MT_BCD_VERSION >> 8) & 0xFF;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
|
s->out_buffer[3] = MT_SENSOR_REGION_DESC;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
|
s->out_buffer[3] = MT_SENSOR_REGION_PARAM;
|
|
}
|
|
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
|
stl_le_p(&s->out_buffer[3], MT_SENSOR_SURFACE_WIDTH);
|
|
stl_le_p(&s->out_buffer[7], MT_SENSOR_SURFACE_HEIGHT);
|
|
}
|
|
else {
|
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: rapport inconnu 0x%02x\n",
|
|
__func__, report_id);
|
|
s->out_buffer[2] = 1;
|
|
}
|
|
|
|
// compute and set the checksum
|
|
uint32_t checksum = 0;
|
|
for(int i = 0; i < 14; i++) {
|
|
checksum += s->out_buffer[i];
|
|
}
|
|
|
|
s->out_buffer[14] = (checksum & 0xFF);
|
|
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
|
}
|
|
|
|
static MTFrame *get_frame(IPodTouchMultitouchState *s, uint8_t event,
|
|
float x, float y, uint16_t radius1, uint16_t radius2,
|
|
uint16_t radius3, uint16_t contactDensity);
|
|
|
|
static void finish_transfer(IPodTouchMultitouchState *s)
|
|
{
|
|
g_clear_pointer(&s->in_buffer, g_free);
|
|
g_clear_pointer(&s->out_buffer, g_free);
|
|
s->cur_cmd = 0;
|
|
s->buf_size = s->buf_ind = s->in_buffer_ind = 0;
|
|
s->in_buffer_capacity = 0;
|
|
}
|
|
|
|
static uint32_t ipod_touch_multitouch_transfer(SSIPeripheral *dev, uint32_t value)
|
|
{
|
|
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(dev);
|
|
|
|
//printf("<MULTITOUCH> Got value: 0x%02x\n", value);
|
|
|
|
if(s->cur_cmd == 0) {
|
|
//printf("Starting command 0x%02x\n", value);
|
|
// we're currently not in a command - start a new command
|
|
s->cur_cmd = value;
|
|
s->out_buffer = g_malloc0(0x100);
|
|
s->out_buffer[0] = value; // the response header
|
|
s->buf_ind = 0;
|
|
s->in_buffer = g_malloc0(0x100);
|
|
s->in_buffer_capacity = 0x100;
|
|
s->in_buffer_ind = 0;
|
|
|
|
if(value == 0x18) { // filler packet??
|
|
s->buf_size = 2;
|
|
s->out_buffer[1] = 0xE1;
|
|
}
|
|
else if(value == 0x1A) { // HBPP ACK
|
|
s->buf_size = 2;
|
|
if(s->hbpp_atn_ack_response[0] == 0 && s->hbpp_atn_ack_response[1] == 0) {
|
|
// return the default ACK response
|
|
s->out_buffer[0] = 0x4B;
|
|
s->out_buffer[1] = 0xC1;
|
|
}
|
|
else {
|
|
s->out_buffer[0] = s->hbpp_atn_ack_response[0];
|
|
s->out_buffer[1] = s->hbpp_atn_ack_response[1];
|
|
}
|
|
|
|
}
|
|
else if(value == 0x1C) { // read register
|
|
s->buf_size = 8;
|
|
memset(s->out_buffer, 0, 8); // just return zeros
|
|
}
|
|
else if(value == 0x1D) { // execute
|
|
s->buf_size = 12;
|
|
memset(s->out_buffer, 0, 12); // just return zeros
|
|
}
|
|
else if(value == 0x1F) { // calibration
|
|
s->buf_size = 2;
|
|
s->out_buffer[1] = 0x0;
|
|
}
|
|
else if(value == 0x1E) { // write register
|
|
s->buf_size = 16;
|
|
memset(s->out_buffer, 0, 16); // just return zeros
|
|
}
|
|
else if(value == 0x1F) { // calibration
|
|
s->buf_size = 2;
|
|
s->out_buffer[1] = 0x0;
|
|
}
|
|
else if(value == MT_CMD_HBPP_DATA_PACKET) {
|
|
s->buf_size = 20; // should be enough initially, until we get the packet length
|
|
memset(s->out_buffer + 1, 0, 20 - 1); // just return zeros
|
|
}
|
|
else if(value == 0x47) { // unknown command, probably used to clear the interrupt
|
|
s->buf_size = 2;
|
|
}
|
|
else if(value == MT_CMD_GET_CMD_STATUS) {
|
|
s->buf_size = 16;
|
|
prepare_cmd_status_response(s);
|
|
}
|
|
else if(value == MT_CMD_GET_INTERFACE_VERSION) {
|
|
s->buf_size = 16;
|
|
prepare_interface_version_response(s);
|
|
}
|
|
else if(value == MT_CMD_GET_REPORT_INFO) {
|
|
s->buf_size = 16;
|
|
}
|
|
else if(value == MT_CMD_SHORT_CONTROL_WRITE) {
|
|
s->buf_size = 16;
|
|
}
|
|
else if(value == MT_CMD_SHORT_CONTROL_READ) {
|
|
s->buf_size = 16;
|
|
}
|
|
else if(value == MT_CMD_FRAME_READ || value == MT_CMD_FRAME_READ_ALT) {
|
|
s->buf_size = sizeof(MTFrameLengthPacket);
|
|
g_free(s->out_buffer);
|
|
/* La trame doit rester stable entre sa longueur et ses données,
|
|
même si un événement hôte arrive pendant les lectures SPI. */
|
|
s->out_buffer = g_memdup2(s->read_frame ? s->read_frame : s->next_frame,
|
|
sizeof(MTFrame));
|
|
}
|
|
else {
|
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: commande inconnue 0x%02x\n",
|
|
__func__, value);
|
|
finish_transfer(s);
|
|
return 0xff;
|
|
}
|
|
}
|
|
|
|
if (s->buf_ind >= s->buf_size || s->in_buffer_ind >= s->in_buffer_capacity) {
|
|
finish_transfer(s);
|
|
return 0xff;
|
|
}
|
|
s->in_buffer[s->in_buffer_ind] = value;
|
|
s->in_buffer_ind++;
|
|
|
|
if ((s->cur_cmd == MT_CMD_FRAME_READ || s->cur_cmd == MT_CMD_FRAME_READ_ALT) &&
|
|
s->in_buffer_ind == 3) {
|
|
/* Le troisième octet distingue longueur (0) et données (1).
|
|
Chaque requête est une transaction complète : une lecture de
|
|
longueur abandonnée pendant la veille ne décale pas la suivante. */
|
|
if (value == 1) {
|
|
MTFrame *frame = (MTFrame *)s->out_buffer;
|
|
/* Le deuxième octet est déjà émis avant de connaître le sélecteur.
|
|
Il est ignoré dans les données, mais participe au checksum. */
|
|
uint8_t second = s->out_buffer[1];
|
|
frame->frame_packet.unused1 = second;
|
|
frame->frame_packet.checksum_pad = -(frame->frame_packet.cmd +
|
|
second + frame->frame_packet.length1 + frame->frame_packet.length2);
|
|
memmove(s->out_buffer, &frame->frame_packet,
|
|
sizeof(MTFrame) - sizeof(MTFrameLengthPacket));
|
|
s->buf_size = sizeof(MTFrame) - sizeof(MTFrameLengthPacket);
|
|
g_clear_pointer(&s->read_frame, g_free);
|
|
} else if (value == 0) {
|
|
g_free(s->read_frame);
|
|
s->read_frame = g_memdup2(s->next_frame, sizeof(MTFrame));
|
|
memcpy(s->out_buffer, s->read_frame, sizeof(MTFrame));
|
|
} else {
|
|
finish_transfer(s);
|
|
return 0xff;
|
|
}
|
|
}
|
|
else if(s->cur_cmd == MT_CMD_HBPP_DATA_PACKET && s->in_buffer_ind == 10) {
|
|
// verify the header checksum
|
|
uint32_t checksum = 0;
|
|
for(int i = 2; i < 8; i++) {
|
|
checksum += s->in_buffer[i];
|
|
}
|
|
|
|
if(checksum != (s->in_buffer[8] << 8 | s->in_buffer[9])) {
|
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: somme de contrôle HBPP invalide\n",
|
|
__func__);
|
|
finish_transfer(s);
|
|
return 0xff;
|
|
}
|
|
|
|
uint32_t data_len = ((s->in_buffer[2] << 10) | (s->in_buffer[3] << 2)) + 5;
|
|
if (data_len < s->in_buffer_ind) {
|
|
finish_transfer(s);
|
|
return 0xff;
|
|
}
|
|
/* Conserver l'en-tête reçu et dimensionner aussi les petites trames. */
|
|
s->in_buffer_capacity = MAX(data_len + 10, 0x100);
|
|
s->in_buffer = g_realloc(s->in_buffer, s->in_buffer_capacity);
|
|
g_free(s->out_buffer);
|
|
s->out_buffer = g_malloc0(data_len);
|
|
s->buf_size = data_len;
|
|
s->buf_ind = 0;
|
|
}
|
|
else if(s->cur_cmd == MT_CMD_GET_REPORT_INFO && s->in_buffer_ind == 2) {
|
|
prepare_report_info_response(s, s->in_buffer[1]);
|
|
}
|
|
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_WRITE && s->in_buffer_ind == 16) {
|
|
// TODO we should persist the report here!
|
|
}
|
|
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_READ && s->in_buffer_ind == 2) {
|
|
prepare_short_control_response(s, s->in_buffer[1]);
|
|
}
|
|
|
|
// TODO process register writes!
|
|
|
|
uint8_t ret_val = s->out_buffer[s->buf_ind];
|
|
s->buf_ind++;
|
|
|
|
//printf("<MULTITOUCH> Got value: 0x%02x, returning 0x%02x (index: %d, buffer length: %d)\n", value, ret_val, s->buf_ind, s->buf_size);
|
|
|
|
if(s->buf_ind == s->buf_size) {
|
|
//printf("Finished command 0x%02x\n", s->cur_cmd);
|
|
|
|
if(s->cur_cmd == 0x1E) {
|
|
// make sure we return a success status on the next HBPP ACK
|
|
s->hbpp_atn_ack_response[0] = 0x4A;
|
|
s->hbpp_atn_ack_response[1] = 0xD1;
|
|
}
|
|
|
|
// we're done with the command
|
|
finish_transfer(s);
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
static MTFrame *get_frame(IPodTouchMultitouchState *s, uint8_t event, float x, float y, uint16_t radius1, uint16_t radius2, uint16_t radius3, uint16_t contactDensity) {
|
|
MTFrame *frame = g_new0(MTFrame, 1);
|
|
|
|
uint16_t data_len = sizeof(MTFrameHeader) + sizeof(FingerData) + 2;
|
|
|
|
/// create the frame length packet
|
|
frame->frame_length.cmd = MT_CMD_FRAME_READ;
|
|
frame->frame_length.length1 = (data_len & 0xFF);
|
|
frame->frame_length.length2 = (data_len >> 8) & 0xFF;
|
|
|
|
uint16_t checksum = 0;
|
|
for(int i = 0; i < 14; i++) {
|
|
checksum += ((uint8_t *) &frame->frame_length)[i];
|
|
}
|
|
frame->frame_length.checksum1 = (checksum & 0xFF);
|
|
frame->frame_length.checksum2 = (checksum >> 8) & 0xFF;
|
|
|
|
// create the frame packet
|
|
frame->frame_packet.cmd = MT_CMD_FRAME_READ;
|
|
frame->frame_packet.length1 = (data_len & 0xFF);
|
|
frame->frame_packet.length2 = (data_len >> 8) & 0xFF;
|
|
|
|
checksum = 0;
|
|
for(int i = 0; i < 4; i++) {
|
|
checksum += ((uint8_t *) &frame->frame_length)[i];
|
|
}
|
|
|
|
// the first five bytes have to sum up to 0.
|
|
frame->frame_packet.checksum_pad = 0xFF - (checksum & 0xFF) + 1;
|
|
|
|
frame->frame_packet.header.type = MT_FRAME_TYPE_PATH;
|
|
frame->frame_packet.header.frameNum = s->frame_counter;
|
|
frame->frame_packet.header.headerLen = sizeof(MTFrameHeader);
|
|
uint64_t elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 1000000;
|
|
frame->frame_packet.header.timestamp = elapsed_ns;
|
|
frame->frame_packet.header.numFingers = 1;
|
|
frame->frame_packet.header.fingerDataLen = sizeof(FingerData);
|
|
|
|
// create the finger data
|
|
frame->finger_data.id = 1;
|
|
frame->finger_data.event = event;
|
|
frame->finger_data.unk_2 = 2;
|
|
frame->finger_data.unk_3 = 1;
|
|
|
|
// compute the velocity
|
|
int diff_x = (int)((x - s->prev_touch_x) * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
|
|
int diff_y = (int)((y - s->prev_touch_y) * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
|
|
uint64_t dt = MAX(elapsed_ns - s->last_frame_timestamp, 1);
|
|
frame->finger_data.velX = CLAMP((int64_t)diff_x * 1000 / (int64_t)dt,
|
|
INT16_MIN, INT16_MAX);
|
|
frame->finger_data.velY = CLAMP((int64_t)diff_y * 1000 / (int64_t)dt,
|
|
INT16_MIN, INT16_MAX);
|
|
s->prev_touch_x = x;
|
|
s->prev_touch_y = y;
|
|
|
|
frame->finger_data.x = (int)(x * MT_INTERNAL_SENSOR_SURFACE_WIDTH) + MT_INTERNAL_SENSOR_ORIGIN_X;
|
|
frame->finger_data.y = (int)(y * MT_INTERNAL_SENSOR_SURFACE_HEIGHT) + MT_INTERNAL_SENSOR_ORIGIN_Y;
|
|
frame->finger_data.radius1 = radius1;
|
|
frame->finger_data.radius2 = radius2;
|
|
frame->finger_data.radius3 = radius3;
|
|
frame->finger_data.angle = 19317;
|
|
frame->finger_data.contactDensity = contactDensity; // seems to be a medium press
|
|
|
|
// compute the checksum over the frame data.
|
|
checksum = 0;
|
|
for(int i = 0; i < data_len - 2; i++) {
|
|
checksum += ((uint8_t *) &frame->frame_packet.header)[i];
|
|
}
|
|
frame->checksum1 = (checksum & 0xFF);
|
|
frame->checksum2 = (checksum >> 8) & 0xFF;
|
|
|
|
s->last_frame_timestamp = elapsed_ns;
|
|
s->frame_counter += 1;
|
|
|
|
return frame;
|
|
}
|
|
|
|
static void ipod_touch_multitouch_inform_frame_ready(IPodTouchMultitouchState *s) {
|
|
s->sysic->gpio_int_status[3] |= (1 << 13); // the multitouch interrupt bit is in group 3 (32 interrupts per group), and the 13th of the 3th group
|
|
qemu_irq_raise(s->sysic->gpio_irqs[3]);
|
|
}
|
|
|
|
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s) {
|
|
s->touch_down = true;
|
|
|
|
g_free(s->next_frame);
|
|
s->next_frame = get_frame(s, MT_EVENT_TOUCH_START, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
|
ipod_touch_multitouch_inform_frame_ready(s);
|
|
|
|
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
|
}
|
|
|
|
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s) {
|
|
g_free(s->next_frame);
|
|
s->next_frame = get_frame(s, MT_EVENT_TOUCH_ENDED, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
|
s->touch_down = false;
|
|
ipod_touch_multitouch_inform_frame_ready(s);
|
|
|
|
timer_del(s->touch_timer);
|
|
timer_mod(s->touch_end_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
|
}
|
|
|
|
static void touch_timer_tick(void *opaque)
|
|
{
|
|
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
|
|
|
g_free(s->next_frame);
|
|
s->next_frame = get_frame(s, MT_EVENT_TOUCH_MOVED, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
|
ipod_touch_multitouch_inform_frame_ready(s);
|
|
|
|
if(s->touch_down) {
|
|
// reschedule the timer
|
|
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
|
}
|
|
}
|
|
|
|
static void touch_end_timer_tick(void *opaque)
|
|
{
|
|
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
|
g_free(s->next_frame);
|
|
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
|
s->touch_down = false;
|
|
ipod_touch_multitouch_inform_frame_ready(s);
|
|
}
|
|
|
|
static void multitouch_cs(void *opaque, int pin, int level)
|
|
{
|
|
IPodTouchMultitouchState *s = opaque;
|
|
if (level) finish_transfer(s);
|
|
}
|
|
|
|
static void ipod_touch_multitouch_realize(SSIPeripheral *d, Error **errp)
|
|
{
|
|
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(d);
|
|
qdev_init_gpio_in_named(DEVICE(d), multitouch_cs, SSI_GPIO_CS, 1);
|
|
memset(s->hbpp_atn_ack_response, 0, 2);
|
|
s->touch_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_timer_tick, s);
|
|
s->touch_end_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_end_timer_tick, s);
|
|
|
|
s->prev_touch_x = 0;
|
|
s->prev_touch_y = 0;
|
|
s->last_frame_timestamp = 0;
|
|
/* Une lecture sans toucher doit renvoyer une trame de fin valide. */
|
|
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, 0, 0, 0, 0, 0, 0);
|
|
}
|
|
|
|
static void ipod_touch_multitouch_reset(DeviceState *dev)
|
|
{
|
|
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(dev);
|
|
|
|
finish_transfer(s);
|
|
timer_del(s->touch_timer);
|
|
timer_del(s->touch_end_timer);
|
|
g_clear_pointer(&s->read_frame, g_free);
|
|
g_clear_pointer(&s->next_frame, g_free);
|
|
memset(s->hbpp_atn_ack_response, 0, sizeof s->hbpp_atn_ack_response);
|
|
s->touch_down = false;
|
|
s->touch_x = s->touch_y = s->prev_touch_x = s->prev_touch_y = 0;
|
|
s->frame_counter = 0;
|
|
s->last_frame_timestamp = 0;
|
|
if (s->sysic) {
|
|
s->sysic->gpio_int_status[3] &= ~(1 << 13);
|
|
if (!s->sysic->gpio_int_status[3]) {
|
|
qemu_irq_lower(s->sysic->gpio_irqs[3]);
|
|
}
|
|
}
|
|
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, 0, 0, 0, 0, 0, 0);
|
|
}
|
|
|
|
static void ipod_touch_multitouch_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
|
|
dc->reset = ipod_touch_multitouch_reset;
|
|
k->realize = ipod_touch_multitouch_realize;
|
|
k->transfer = ipod_touch_multitouch_transfer;
|
|
}
|
|
|
|
static const TypeInfo ipod_touch_multitouch_type_info = {
|
|
.name = TYPE_IPOD_TOUCH_MULTITOUCH,
|
|
.parent = TYPE_SSI_PERIPHERAL,
|
|
.instance_size = sizeof(IPodTouchMultitouchState),
|
|
.class_init = ipod_touch_multitouch_class_init,
|
|
};
|
|
|
|
static void ipod_touch_multitouch_register_types(void)
|
|
{
|
|
type_register_static(&ipod_touch_multitouch_type_info);
|
|
}
|
|
|
|
type_init(ipod_touch_multitouch_register_types)
|