Add serial numbers to the protocol

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2023-02-21 15:28:08 +10:00
parent 845f990883
commit 1c9ce9b680
9 changed files with 129 additions and 26 deletions

View file

@ -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.
</description>
<arg name="serial" type="uint" help="this events serial number"/>
<arg name="connection" type="new_id" interface="ei_connection"
summary="the connection object" />
<arg name="version" type="uint" summary="the version of the connection object"/>
@ -323,7 +338,9 @@
<event name="disconnected" type="destructor" since="1">
<description summary="disconnection event">
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.
</description>
<arg name="last_serial" type="uint" help="the last serial sent by the EIS implementation"/>
<arg name="reason" type="uint" enum="disconnect_reason"/>
<arg name="explanation" type="string"/>
</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.
</description>
<arg name="last_serial" type="uint" help="the last serial sent by the EIS implementation"/>
<arg name="invalid_id" type="uint" />
</event>
@ -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.
</description>
<arg name="last_serial" type="uint" help="the last serial sent by the EIS implementation"/>
<arg name="sequence" type="uint"/>
</request>
@ -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.
</description>
<arg name="last_serial" type="uint" help="the last serial sent by the EIS implementation"/>
</request>
<request name="frame" since="1">
@ -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.
</description>
<arg name="last_serial" type="uint" help="the last serial sent by the EIS implementation"/>
<arg name="timestamp" type="uint" help="timestamp in milliseconds"/>
<arg name="micros" type="uint" help="microseconds in addition to the millis"/>
</request>
@ -815,9 +837,11 @@
</event>
<event name="resumed" since="1">
<arg name="serial" type="uint" help="this events serial number"/>
</event>
<event name="paused" since="1">
<arg name="serial" type="uint" help="this events serial number"/>
</event>
<event name="start_emulating" since="1">
@ -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.
</description>>
</description>
<arg name="serial" type="uint" help="this events serial number"/>
<arg name="sequence" type="uint"/>
</event>
@ -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.
</description>
<arg name="serial" type="uint" help="this events serial number"/>
</event>
<event name="frame" since="1">
@ -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.
</description>
<arg name="serial" type="uint" help="this events serial number"/>
<arg name="timestamp" type="uint" help="timestamp in milliseconds"/>
<arg name="micros" type="uint" help="microseconds in addition to the millis"/>
</event>

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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