From 1c9ce9b6804a693552724ca74f6878d579999234 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Feb 2023 15:28:08 +1000 Subject: [PATCH] Add serial numbers to the protocol Signed-off-by: Peter Hutterer --- proto/protocol.xml | 31 +++++++++++++++++++++++++++++-- src/libei-connection-setup.c | 3 ++- src/libei-device.c | 33 ++++++++++++++++++++++++--------- src/libei-private.h | 8 ++++++++ src/libei.c | 14 +++++++++++--- src/libeis-client.c | 20 ++++++++++++++++++-- src/libeis-client.h | 9 +++++++++ src/libeis-connection-setup.c | 4 +++- src/libeis-device.c | 33 +++++++++++++++++++++++++-------- 9 files changed, 129 insertions(+), 26 deletions(-) diff --git a/proto/protocol.xml b/proto/protocol.xml index 9787b21..57d3b45 100644 --- a/proto/protocol.xml +++ b/proto/protocol.xml @@ -68,6 +68,20 @@ the client with an error or (if appropriate) the client immediately disconnecting from the EIS implementation. + Serial numbers: + Some events have serial numbers assiged by the EIS implementation. A serial is + a monotonically increasing unsigned 32-bit number (that wraps, clients must handle + this). Clients must use the most recently seen EIS serial number in requests that have + a last_serial argument. + + The serial number aims to help a client track failure cases and the EIS + implementation to errors caused by the protocol's asynchronicity. + For example, if the EIS implementation removes a device at the serial number N, + client-initiated events for N-1 are caused by the client lagging behind and are not + a protocol violation. + + Initial Connection: + The initial connection is a two-step process: An ei_connection_setup object with the special ID 0 is guaranteed to exists. The client must send the appropriate requests to set up @@ -240,6 +254,7 @@ interface as announced by ei_connection_setup.interface, or any lower version. + @@ -323,7 +338,9 @@ This event may be sent by the EIS implementation immediately before - the client is disconnected. + the client is disconnected. The last_serial argument is set to the last + serial number used in a request by the client or zero if the client has not + yet issued a request. Where a client is disconnected by EIS on purpose, for example after a user interaction, the reason is disconnect_reason.disconnected (i.e. zero) @@ -341,6 +358,7 @@ There is no guarantee this event is sent - the connection may be closed without a disconnection event. + @@ -369,6 +387,7 @@ This event is sent by the EIS implementation when an object that does not exist as seen by the EIS implementation. + @@ -609,6 +628,7 @@ It is a protocol violation to send this request for a client of an ei_connection_setup.context_type other than sender. + @@ -620,6 +640,7 @@ It is a protocol violation to send this request for a client of an ei_connection_setup.context_type other than sender. + @@ -634,6 +655,7 @@ It is a protocol violation to send this request for a client of an ei_connection_setup.context_type other than sender. + @@ -815,9 +837,11 @@ + + @@ -826,7 +850,8 @@ It is a protocol violation to send this event for a client of an ei_connection_setup.context_type other than receiver. - > + + @@ -837,6 +862,7 @@ It is a protocol violation to send this event for a client of an ei_connection_setup.context_type other than receiver. + @@ -846,6 +872,7 @@ It is a protocol violation to send this event for a client of an ei_connection_setup.context_type other than receiver. + diff --git a/src/libei-connection-setup.c b/src/libei-connection-setup.c index d888475..8f6ba43 100644 --- a/src/libei-connection-setup.c +++ b/src/libei-connection-setup.c @@ -144,7 +144,7 @@ connected(struct ei_connection *connection, void *user_data) } static struct brei_result * -handle_msg_connection(struct ei_connection_setup *setup, uint32_t id, uint32_t version) +handle_msg_connection(struct ei_connection_setup *setup, uint32_t serial, uint32_t id, uint32_t version) { struct ei *ei = ei_connection_setup_get_context(setup); assert(setup == ei->connection_setup); @@ -153,6 +153,7 @@ handle_msg_connection(struct ei_connection_setup *setup, uint32_t id, uint32_t v ei->connection = ei_connection_new(ei, id, version); ei->state = EI_STATE_CONNECTING; + ei_update_serial(ei, serial); /* Send a sync on the connection - EIS should immediately send a * disconnect event where applicable, so if we get through to our diff --git a/src/libei-device.c b/src/libei-device.c index 527c20c..d15be86 100644 --- a/src/libei-device.c +++ b/src/libei-device.c @@ -249,8 +249,9 @@ handle_msg_done(struct ei_device *device) } static struct brei_result * -handle_msg_resumed(struct ei_device *device) +handle_msg_resumed(struct ei_device *device, uint32_t serial) { + ei_update_serial(ei_device_get_context(device), serial); ei_device_resumed(device); ei_queue_device_resumed_event(device); @@ -258,8 +259,9 @@ handle_msg_resumed(struct ei_device *device) } static struct brei_result * -handle_msg_paused(struct ei_device *device) +handle_msg_paused(struct ei_device *device, uint32_t serial) { + ei_update_serial(ei_device_get_context(device), serial); ei_device_paused(device); ei_queue_device_paused_event(device); @@ -275,12 +277,14 @@ handle_msg_paused(struct ei_device *device) } while(0) static struct brei_result * -handle_msg_start_emulating(struct ei_device *device, uint32_t sequence) +handle_msg_start_emulating(struct ei_device *device, uint32_t serial, uint32_t sequence) { struct brei_result *result = NULL; DISCONNECT_IF_SENDER_CONTEXT(device); + ei_update_serial(ei_device_get_context(device), serial); + switch (device->state) { case EI_DEVICE_STATE_DEAD: case EI_DEVICE_STATE_NEW: @@ -303,12 +307,14 @@ handle_msg_start_emulating(struct ei_device *device, uint32_t sequence) } static struct brei_result * -handle_msg_stop_emulating(struct ei_device *device) +handle_msg_stop_emulating(struct ei_device *device, uint32_t serial) { struct brei_result *result = NULL; DISCONNECT_IF_SENDER_CONTEXT(device); + ei_update_serial(ei_device_get_context(device), serial); + switch (device->state) { case EI_DEVICE_STATE_DEAD: case EI_DEVICE_STATE_NEW: @@ -353,10 +359,12 @@ maybe_error_on_device_state(struct ei_device *device, const char *event_type) } static struct brei_result * -handle_msg_frame(struct ei_device *device, uint32_t time, uint32_t micros) +handle_msg_frame(struct ei_device *device, uint32_t serial, uint32_t time, uint32_t micros) { DISCONNECT_IF_SENDER_CONTEXT(device); + ei_update_serial(ei_device_get_context(device), serial); + if (device->state == EI_DEVICE_STATE_EMULATING) { ei_queue_frame_event(device, ms2us(time) + micros); return NULL; @@ -903,7 +911,8 @@ ei_device_close(struct ei_device *device) break; case EI_DEVICE_STATE_EMULATING: if (ei_is_sender(ei_device_get_context(device))) - ei_device_request_stop_emulating(device); + ei_device_request_stop_emulating(device, + ei_get_serial(ei_device_get_context(device))); _fallthrough_; case EI_DEVICE_STATE_PAUSED: case EI_DEVICE_STATE_RESUMED: @@ -1043,13 +1052,15 @@ _flush_frame(struct ei_device *device, const char *func) _public_ void ei_device_start_emulating(struct ei_device *device, uint32_t sequence) { + struct ei *ei = ei_device_get_context(device); + if (device->state != EI_DEVICE_STATE_RESUMED) return; assert(!device->send_frame_event); device->state = EI_DEVICE_STATE_EMULATING; - int rc = ei_device_request_start_emulating(device, sequence); + int rc = ei_device_request_start_emulating(device, ei_get_serial(ei), sequence); if (rc) ei_disconnect(ei_device_get_context(device)); } @@ -1057,12 +1068,14 @@ ei_device_start_emulating(struct ei_device *device, uint32_t sequence) _public_ void ei_device_stop_emulating(struct ei_device *device) { + struct ei *ei = ei_device_get_context(device); + if (device->state != EI_DEVICE_STATE_EMULATING) return; ei_device_flush_frame(device); device->state = EI_DEVICE_STATE_RESUMED; - int rc = ei_device_request_stop_emulating(device); + int rc = ei_device_request_stop_emulating(device, ei_get_serial(ei)); if (rc) ei_disconnect(ei_device_get_context(device)); } @@ -1587,6 +1600,8 @@ ei_touch_up(struct ei_touch *touch) _public_ void ei_device_frame(struct ei_device *device, uint64_t time) { + struct ei *ei = ei_device_get_context(device); + if (device->state != EI_DEVICE_STATE_EMULATING) return; @@ -1595,7 +1610,7 @@ ei_device_frame(struct ei_device *device, uint64_t time) device->send_frame_event = false; - int rc = ei_device_request_frame(device, us2ms(time), time % 1000); + int rc = ei_device_request_frame(device, ei_get_serial(ei), us2ms(time), time % 1000); if (rc) ei_disconnect(ei_device_get_context(device)); return; diff --git a/src/libei-private.h b/src/libei-private.h index b872d46..9cf66d1 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -81,6 +81,8 @@ struct ei { struct list proto_objects; /* brei_object list */ uint32_t next_object_id; + uint32_t serial; + void *user_data; struct brei_context *brei; struct sink *sink; @@ -118,6 +120,12 @@ ei_get_connection(struct ei *ei); uint32_t ei_get_new_id(struct ei *ei); +void +ei_update_serial(struct ei *ei, uint32_t serial); + +uint32_t +ei_get_serial(struct ei *ei); + void ei_register_object(struct ei *ei, struct brei_object *object); diff --git a/src/libei.c b/src/libei.c index e04d4c6..c936172 100644 --- a/src/libei.c +++ b/src/libei.c @@ -80,6 +80,7 @@ OBJECT_IMPLEMENT_SETTER(ei, user_data, void *); _public_ OBJECT_IMPLEMENT_GETTER(ei, user_data, void *); OBJECT_IMPLEMENT_GETTER(ei, connection, struct ei_connection *); +OBJECT_IMPLEMENT_GETTER(ei, serial, uint32_t); DEFINE_UNREF_CLEANUP_FUNC(brei_result); DEFINE_UNREF_CLEANUP_FUNC(ei_device); @@ -140,6 +141,12 @@ ei_get_new_id(struct ei *ei) return ei->next_object_id++ & ~server_range; } +void +ei_update_serial(struct ei *ei, uint32_t serial) +{ + ei->serial = serial; +} + void ei_register_object(struct ei *ei, struct brei_object *object) { @@ -656,7 +663,8 @@ ei_connected(struct ei *ei) } static struct brei_result * -handle_msg_disconnected(struct ei_connection *connection, uint32_t reason, const char *explanation) +handle_msg_disconnected(struct ei_connection *connection, uint32_t last_serial, + uint32_t reason, const char *explanation) { struct ei *ei = ei_connection_get_context(connection); @@ -672,11 +680,11 @@ handle_msg_disconnected(struct ei_connection *connection, uint32_t reason, const } static struct brei_result * -handle_msg_invalid_object(struct ei_connection *connection, uint32_t object) +handle_msg_invalid_object(struct ei_connection *connection, uint32_t last_serial, uint32_t object) { struct ei *ei = ei_connection_get_context(connection); - log_bug(ei, "Invalid object %#x, I don't yet know how to handle that", object); + log_bug(ei, "Invalid object %#x after %u, I don't yet know how to handle that", last_serial, object); return NULL; } diff --git a/src/libeis-client.c b/src/libeis-client.c index dee74bd..71f6c1c 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -78,6 +78,18 @@ OBJECT_IMPLEMENT_SETTER(eis_client, user_data, void*); _public_ OBJECT_IMPLEMENT_GETTER(eis_client, user_data, void*); +uint32_t +eis_client_get_next_serial(struct eis_client *client) +{ + return ++client->serial; +} + +void +eis_client_update_client_serial(struct eis_client *client, uint32_t serial) +{ + client->last_client_serial = serial; +} + _public_ struct eis* eis_client_get_context(struct eis_client *client) { @@ -224,7 +236,9 @@ client_disconnect(struct eis_client *client, } } eis_queue_disconnect_event(client); - eis_connection_event_disconnected(client->connection, reason, explanation); + eis_connection_event_disconnected(client->connection, + client->last_client_serial, + reason, explanation); client->connection = eis_connection_unref(client->connection); _fallthrough_; case EIS_CLIENT_STATE_NEW: @@ -342,7 +356,9 @@ lookup_object(uint32_t object_id, struct brei_object **object, void *userdata) log_debug(eis_client_get_context(client), "Failed to find object %#x", object_id); if (client->connection) - eis_connection_event_invalid_object(client->connection, object_id); + eis_connection_event_invalid_object(client->connection, + client->last_client_serial, + object_id); return -ENOENT; } diff --git a/src/libeis-client.h b/src/libeis-client.h index 96bed34..7395017 100644 --- a/src/libeis-client.h +++ b/src/libeis-client.h @@ -58,6 +58,9 @@ struct eis_client { struct list proto_objects; /* struct brei_objects list */ uint32_t next_object_id; + uint32_t serial; + uint32_t last_client_serial; + struct eis_connection_setup *setup; struct eis_client_interface_versions interface_versions; @@ -87,6 +90,12 @@ OBJECT_DECLARE_GETTER(eis_client, proto_object, const struct brei_object *); struct eis_client * eis_client_new(struct eis *eis, int fd); +uint32_t +eis_client_get_next_serial(struct eis_client *client); + +void +eis_client_update_client_serial(struct eis_client *client, uint32_t serial); + uint32_t eis_client_get_new_id(struct eis_client *client); diff --git a/src/libeis-connection-setup.c b/src/libeis-connection-setup.c index 54296cf..c4400c8 100644 --- a/src/libeis-connection-setup.c +++ b/src/libeis-connection-setup.c @@ -102,7 +102,9 @@ client_msg_done(struct eis_connection_setup *setup) eis_client_setup_done(client, setup->name, setup->is_sender, &setup->client_versions); client->connection = eis_connection_new(client); - eis_connection_setup_event_connection(setup, eis_connection_get_id(client->connection), + eis_connection_setup_event_connection(setup, + eis_client_get_next_serial(client), + eis_connection_get_id(client->connection), eis_connection_get_version(client->connection)); /* These aren't required but libei is pointless without them, so let's enforce them diff --git a/src/libeis-device.c b/src/libeis-device.c index cf624d0..eedd855 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -234,10 +234,12 @@ client_msg_release(struct eis_device *device) static struct brei_result * -client_msg_start_emulating(struct eis_device *device, uint32_t sequence) +client_msg_start_emulating(struct eis_device *device, uint32_t serial, uint32_t sequence) { struct brei_result *result = NULL; + eis_client_update_client_serial(eis_device_get_client(device), serial); + DISCONNECT_IF_RECEIVER_CONTEXT(device); switch (device->state) { @@ -261,10 +263,12 @@ client_msg_start_emulating(struct eis_device *device, uint32_t sequence) } static struct brei_result * -client_msg_stop_emulating(struct eis_device *device) +client_msg_stop_emulating(struct eis_device *device, uint32_t serial) { struct brei_result *result = NULL; + eis_client_update_client_serial(eis_device_get_client(device), serial); + DISCONNECT_IF_RECEIVER_CONTEXT(device); switch (device->state) { @@ -316,8 +320,10 @@ maybe_error_on_device_state(struct eis_device *device, const char *event_type) } static struct brei_result * -client_msg_frame(struct eis_device *device, uint32_t time, uint32_t micros) +client_msg_frame(struct eis_device *device, uint32_t serial, uint32_t time, uint32_t micros) { + eis_client_update_client_serial(eis_device_get_client(device), serial); + DISCONNECT_IF_RECEIVER_CONTEXT(device); if (device->state == EIS_DEVICE_STATE_EMULATING) { @@ -854,6 +860,8 @@ _flush_frame(struct eis_device *device, const char *func) _public_ void eis_device_start_emulating(struct eis_device *device, uint32_t sequence) { + struct eis_client *client = eis_device_get_client(device); + if (device->state != EIS_DEVICE_STATE_RESUMED) return; @@ -861,12 +869,14 @@ eis_device_start_emulating(struct eis_device *device, uint32_t sequence) device->state = EIS_DEVICE_STATE_EMULATING; - eis_device_event_start_emulating(device, sequence); + eis_device_event_start_emulating(device, eis_client_get_next_serial(client), sequence); } _public_ void eis_device_stop_emulating(struct eis_device *device) { + struct eis_client *client = eis_device_get_client(device); + if (device->state != EIS_DEVICE_STATE_EMULATING) return; @@ -874,7 +884,7 @@ eis_device_stop_emulating(struct eis_device *device) device->state = EIS_DEVICE_STATE_RESUMED; - eis_device_event_stop_emulating(device); + eis_device_event_stop_emulating(device, eis_client_get_next_serial(client)); } _public_ void @@ -1187,6 +1197,8 @@ eis_touch_up(struct eis_touch *touch) _public_ void eis_device_frame(struct eis_device *device, uint64_t time) { + struct eis_client *client = eis_device_get_client(device); + if (device->state != EIS_DEVICE_STATE_EMULATING) return; @@ -1195,7 +1207,8 @@ eis_device_frame(struct eis_device *device, uint64_t time) device->send_frame_event = false; - eis_device_event_frame(device, us2ms(time), time % 1000); + eis_device_event_frame(device, eis_client_get_next_serial(client), + us2ms(time), time % 1000); } void @@ -1222,21 +1235,25 @@ eis_device_closed_by_client(struct eis_device *device) _public_ void eis_device_pause(struct eis_device *device) { + struct eis_client *client = eis_device_get_client(device); + if (device->state != EIS_DEVICE_STATE_RESUMED) return; device->state = EIS_DEVICE_STATE_PAUSED; - eis_device_event_paused(device); + eis_device_event_paused(device, eis_client_get_next_serial(client)); } _public_ void eis_device_resume(struct eis_device *device) { + struct eis_client *client = eis_device_get_client(device); + if (device->state != EIS_DEVICE_STATE_PAUSED) return; device->state = EIS_DEVICE_STATE_RESUMED; - eis_device_event_resumed(device); + eis_device_event_resumed(device, eis_client_get_next_serial(client)); } _public_ void