serial: Make serial device event sources more robust
Disallow polling for input/error and output-ready events at the same time, and ensure only a single FD event source is installed. Also, do not leak if the FD event source is removed by means other than calling serial_source_remove().
This commit is contained in:
parent
0c536bcd00
commit
cbc1413f31
|
@ -574,10 +574,6 @@ struct sr_serial_dev_inst {
|
||||||
char *serialcomm;
|
char *serialcomm;
|
||||||
/** libserialport port handle */
|
/** libserialport port handle */
|
||||||
struct sp_port *data;
|
struct sp_port *data;
|
||||||
/** libserialport event set */
|
|
||||||
struct sp_event_set *event_set;
|
|
||||||
/** GPollFDs for event polling */
|
|
||||||
GPollFD *pollfds;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -732,6 +728,9 @@ SR_PRIV int sr_session_source_remove_internal(struct sr_session *session,
|
||||||
void *key);
|
void *key);
|
||||||
SR_PRIV int sr_session_source_destroyed(struct sr_session *session,
|
SR_PRIV int sr_session_source_destroyed(struct sr_session *session,
|
||||||
void *key, GSource *source);
|
void *key, GSource *source);
|
||||||
|
SR_PRIV int sr_session_fd_source_add(struct sr_session *session,
|
||||||
|
void *key, gintptr fd, int events, int timeout,
|
||||||
|
sr_receive_data_callback cb, void *cb_data);
|
||||||
SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
|
SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
|
||||||
const struct sr_datafeed_packet *packet);
|
const struct sr_datafeed_packet *packet);
|
||||||
SR_PRIV int sr_sessionfile_check(const char *filename);
|
SR_PRIV int sr_sessionfile_check(const char *filename);
|
||||||
|
|
75
src/serial.c
75
src/serial.c
|
@ -799,10 +799,17 @@ SR_PRIV int serial_source_add(struct sr_session *session,
|
||||||
struct sr_serial_dev_inst *serial, int events, int timeout,
|
struct sr_serial_dev_inst *serial, int events, int timeout,
|
||||||
sr_receive_data_callback cb, void *cb_data)
|
sr_receive_data_callback cb, void *cb_data)
|
||||||
{
|
{
|
||||||
|
struct sp_event_set *event_set;
|
||||||
|
gintptr poll_fd;
|
||||||
|
unsigned int poll_events;
|
||||||
enum sp_event mask = 0;
|
enum sp_event mask = 0;
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
if (sp_new_event_set(&serial->event_set) != SP_OK)
|
if ((events & (G_IO_IN|G_IO_ERR)) && (events & G_IO_OUT)) {
|
||||||
|
sr_err("Cannot poll input/error and output simultaneously.");
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sp_new_event_set(&event_set) != SP_OK)
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
|
|
||||||
if (events & G_IO_IN)
|
if (events & G_IO_IN)
|
||||||
|
@ -812,54 +819,44 @@ SR_PRIV int serial_source_add(struct sr_session *session,
|
||||||
if (events & G_IO_ERR)
|
if (events & G_IO_ERR)
|
||||||
mask |= SP_EVENT_ERROR;
|
mask |= SP_EVENT_ERROR;
|
||||||
|
|
||||||
if (sp_add_port_events(serial->event_set, serial->data, mask) != SP_OK) {
|
if (sp_add_port_events(event_set, serial->data, mask) != SP_OK) {
|
||||||
sp_free_event_set(serial->event_set);
|
sp_free_event_set(event_set);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
if (event_set->count != 1) {
|
||||||
|
sr_err("Unexpected number (%u) of event handles to poll.",
|
||||||
|
event_set->count);
|
||||||
|
sp_free_event_set(event_set);
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
serial->pollfds = g_new0(GPollFD, serial->event_set->count);
|
poll_fd = (gintptr) ((event_handle *)event_set->handles)[0];
|
||||||
|
mask = event_set->masks[0];
|
||||||
|
|
||||||
for (i = 0; i < serial->event_set->count; i++) {
|
sp_free_event_set(event_set);
|
||||||
|
|
||||||
serial->pollfds[i].fd = (gintptr)
|
poll_events = 0;
|
||||||
((event_handle *)serial->event_set->handles)[i];
|
if (mask & SP_EVENT_RX_READY)
|
||||||
mask = serial->event_set->masks[i];
|
poll_events |= G_IO_IN;
|
||||||
|
if (mask & SP_EVENT_TX_READY)
|
||||||
if (mask & SP_EVENT_RX_READY)
|
poll_events |= G_IO_OUT;
|
||||||
serial->pollfds[i].events |= G_IO_IN;
|
if (mask & SP_EVENT_ERROR)
|
||||||
if (mask & SP_EVENT_TX_READY)
|
poll_events |= G_IO_ERR;
|
||||||
serial->pollfds[i].events |= G_IO_OUT;
|
/*
|
||||||
if (mask & SP_EVENT_ERROR)
|
* Using serial->data as the key for the event source is not quite
|
||||||
serial->pollfds[i].events |= G_IO_ERR;
|
* proper, as it makes it impossible to create another event source
|
||||||
|
* for the same serial port. However, these fixed keys will soon be
|
||||||
if (sr_session_source_add_pollfd(session, &serial->pollfds[i],
|
* removed from the API anyway, so this is OK for now.
|
||||||
timeout, cb, cb_data) != SR_OK)
|
*/
|
||||||
return SR_ERR;
|
return sr_session_fd_source_add(session, serial->data,
|
||||||
}
|
poll_fd, poll_events, timeout, cb, cb_data);
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
SR_PRIV int serial_source_remove(struct sr_session *session,
|
SR_PRIV int serial_source_remove(struct sr_session *session,
|
||||||
struct sr_serial_dev_inst *serial)
|
struct sr_serial_dev_inst *serial)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
return sr_session_source_remove_internal(session, serial->data);
|
||||||
|
|
||||||
if (!serial->event_set)
|
|
||||||
return SR_OK;
|
|
||||||
|
|
||||||
for (i = 0; i < serial->event_set->count; i++)
|
|
||||||
if (sr_session_source_remove_pollfd(session, &serial->pollfds[i]) != SR_OK)
|
|
||||||
return SR_ERR;
|
|
||||||
|
|
||||||
g_free(serial->pollfds);
|
|
||||||
sp_free_event_set(serial->event_set);
|
|
||||||
|
|
||||||
serial->pollfds = NULL;
|
|
||||||
serial->event_set = NULL;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -985,8 +985,8 @@ SR_PRIV int sr_session_source_add_internal(struct sr_session *session,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int attach_fd_source(struct sr_session *session,
|
SR_PRIV int sr_session_fd_source_add(struct sr_session *session,
|
||||||
void *key, int fd, int events, int timeout,
|
void *key, gintptr fd, int events, int timeout,
|
||||||
sr_receive_data_callback cb, void *cb_data)
|
sr_receive_data_callback cb, void *cb_data)
|
||||||
{
|
{
|
||||||
GSource *source;
|
GSource *source;
|
||||||
|
@ -1027,7 +1027,7 @@ SR_API int sr_session_source_add(struct sr_session *session, int fd,
|
||||||
sr_err("Cannot create timer source without timeout.");
|
sr_err("Cannot create timer source without timeout.");
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
}
|
}
|
||||||
return attach_fd_source(session, GINT_TO_POINTER(fd),
|
return sr_session_fd_source_add(session, GINT_TO_POINTER(fd),
|
||||||
fd, events, timeout, cb, cb_data);
|
fd, events, timeout, cb, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1054,7 +1054,7 @@ SR_API int sr_session_source_add_pollfd(struct sr_session *session,
|
||||||
sr_err("%s: pollfd was NULL", __func__);
|
sr_err("%s: pollfd was NULL", __func__);
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
}
|
}
|
||||||
return attach_fd_source(session, pollfd, pollfd->fd,
|
return sr_session_fd_source_add(session, pollfd, pollfd->fd,
|
||||||
pollfd->events, timeout, cb, cb_data);
|
pollfd->events, timeout, cb, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1093,7 +1093,7 @@ SR_API int sr_session_source_add_channel(struct sr_session *session,
|
||||||
pollfd.fd = g_io_channel_unix_get_fd(channel);
|
pollfd.fd = g_io_channel_unix_get_fd(channel);
|
||||||
pollfd.events = events;
|
pollfd.events = events;
|
||||||
#endif
|
#endif
|
||||||
return attach_fd_source(session, channel, pollfd.fd,
|
return sr_session_fd_source_add(session, channel, pollfd.fd,
|
||||||
pollfd.events, timeout, cb, cb_data);
|
pollfd.events, timeout, cb, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue