diff options
Diffstat (limited to 'src/mm-serial-port.c')
-rw-r--r-- | src/mm-serial-port.c | 610 |
1 files changed, 328 insertions, 282 deletions
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c index 2600ae5..ed44167 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -11,14 +11,14 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ #define _GNU_SOURCE /* for strcasestr() */ #include <stdio.h> #include <stdlib.h> -#include <termio.h> +#include <termios.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> @@ -51,20 +51,12 @@ enum { #define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate)) typedef struct { + guint32 open_count; int fd; GHashTable *reply_cache; GIOChannel *channel; GQueue *queue; - GString *command; - GString *response; - - gboolean connected; - - /* Response parser data */ - MMSerialResponseParserFn response_parser_fn; - gpointer response_parser_user_data; - GDestroyNotify response_parser_notify; - GSList *unsolicited_msg_handlers; + GByteArray *response; struct termios old_t; @@ -148,12 +140,12 @@ void mm_serial_port_print_config (MMSerialPort *port, const char *detail) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (port); - struct termio stbuf; + struct termios stbuf; int err; - err = ioctl (priv->fd, TCGETA, &stbuf); + err = tcgetattr (priv->fd, &stbuf); if (err) { - g_warning ("*** %s (%s): (%s) TCGETA error %d", + g_warning ("*** %s (%s): (%s) tcgetattr() error %d", __func__, detail, mm_port_get_device (MM_PORT (port)), errno); return; } @@ -165,33 +157,6 @@ mm_serial_port_print_config (MMSerialPort *port, const char *detail) } #endif -typedef struct { - GRegex *regex; - MMSerialUnsolicitedMsgFn callback; - gpointer user_data; - GDestroyNotify notify; -} MMUnsolicitedMsgHandler; - -static void -mm_serial_port_set_cached_reply (MMSerialPort *self, - const char *command, - const char *reply) -{ - if (reply) - g_hash_table_insert (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, - g_strdup (command), - g_strdup (reply)); - else - g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); -} - -static const char * -mm_serial_port_get_cached_reply (MMSerialPort *self, - const char *command) -{ - return (char *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); -} - static int parse_baudrate (guint i) { @@ -327,10 +292,10 @@ parse_stopbits (guint i) } static gboolean -config_fd (MMSerialPort *self, GError **error) +real_config_fd (MMSerialPort *self, int fd, GError **error) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - struct termio stbuf; + struct termios stbuf; int speed; int bits; int parity; @@ -341,9 +306,9 @@ config_fd (MMSerialPort *self, GError **error) parity = parse_parity (priv->parity); stopbits = parse_stopbits (priv->stopbits); - memset (&stbuf, 0, sizeof (struct termio)); - if (ioctl (priv->fd, TCGETA, &stbuf) != 0) { - g_warning ("%s (%s): TCGETA error: %d", + memset (&stbuf, 0, sizeof (struct termios)); + if (tcgetattr (fd, &stbuf) != 0) { + g_warning ("%s (%s): tcgetattr() error: %d", __func__, mm_port_get_device (MM_PORT (self)), errno); @@ -357,10 +322,16 @@ config_fd (MMSerialPort *self, GError **error) stbuf.c_cc[VTIME] = 0; stbuf.c_cc[VEOF] = 1; - stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); - stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); + /* Use software handshaking */ + stbuf.c_iflag |= (IXON | IXOFF | IXANY); - if (ioctl (priv->fd, TCSETA, &stbuf) < 0) { + /* Set up port speed and serial attributes; also ignore modem control + * lines since most drivers don't implement RTS/CTS anyway. + */ + stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | CRTSCTS); + stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits | CLOCAL); + + if (tcsetattr (fd, TCSANOW, &stbuf) < 0) { g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, @@ -373,105 +344,99 @@ config_fd (MMSerialPort *self, GError **error) } static void -serial_debug (MMSerialPort *self, const char *prefix, const char *buf, int len) +serial_debug (MMSerialPort *self, const char *prefix, const char *buf, gsize len) { - static GString *debug = NULL; - const char *s; - - if (!mm_options_debug ()) - return; - - if (len < 0) - len = strlen (buf); - - if (!debug) - debug = g_string_sized_new (256); - - g_string_append (debug, prefix); - g_string_append (debug, " '"); - - s = buf; - while (len--) { - if (g_ascii_isprint (*s)) - g_string_append_c (debug, *s); - else if (*s == '\r') - g_string_append (debug, "<CR>"); - else if (*s == '\n') - g_string_append (debug, "<LF>"); - else - g_string_append_printf (debug, "\\%d", *s); - - s++; - } + g_return_if_fail (len > 0); - g_string_append_c (debug, '\''); - g_debug ("(%s): %s", mm_port_get_device (MM_PORT (self)), debug->str); - g_string_truncate (debug, 0); + if (mm_options_debug () && MM_SERIAL_PORT_GET_CLASS (self)->debug_log) + MM_SERIAL_PORT_GET_CLASS (self)->debug_log (self, prefix, buf, len); } static gboolean mm_serial_port_send_command (MMSerialPort *self, - const char *command, + GByteArray *command, GError **error) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - const char *s; - int status; + int status, i = 0; int eagain_count = 1000; + const guint8 *p; if (priv->fd < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "%s", "Sending command failed: device is not enabled"); return FALSE; } if (mm_port_get_connected (MM_PORT (self))) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "%s", "Sending command failed: device is connected"); return FALSE; } - g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2); - g_string_append (priv->command, command); - - if (command[strlen (command)] != '\r') - g_string_append_c (priv->command, '\r'); - - serial_debug (self, "-->", priv->command->str, -1); + serial_debug (self, "-->", (const char *) command->data, command->len); /* Only accept about 3 seconds of EAGAIN */ if (priv->send_delay > 0) eagain_count = 3000000 / priv->send_delay; - s = priv->command->str; - while (*s) { - status = write (priv->fd, s, 1); + while (i < command->len) { + p = &command->data[i]; + status = write (priv->fd, p, 1); if (status < 0) { if (errno == EAGAIN) { eagain_count--; if (eagain_count <= 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: '%s'", strerror (errno)); break; } } else { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: '%s'", strerror (errno)); break; } } else - s++; + i++; if (priv->send_delay) usleep (priv->send_delay); } - return *s == '\0'; + return i == command->len; +} + +static void +mm_serial_port_set_cached_reply (MMSerialPort *self, + const GByteArray *command, + const GByteArray *response) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + g_return_if_fail (command != NULL); + + if (response) { + GByteArray *cmd_copy = g_byte_array_sized_new (command->len); + GByteArray *rsp_copy = g_byte_array_sized_new (response->len); + + g_byte_array_append (cmd_copy, command->data, command->len); + g_byte_array_append (rsp_copy, response->data, response->len); + g_hash_table_insert (priv->reply_cache, cmd_copy, rsp_copy); + } else + g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); +} + +static const GByteArray * +mm_serial_port_get_cached_reply (MMSerialPort *self, GByteArray *command) +{ + return (const GByteArray *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); } typedef struct { - char *command; - MMSerialResponseFn callback; + GByteArray *command; + GCallback callback; gpointer user_data; guint32 timeout; gboolean cached; @@ -500,11 +465,25 @@ mm_serial_port_schedule_queue_process (MMSerialPort *self) g_source_unref (source); } +static gsize +real_handle_response (MMSerialPort *self, + GByteArray *response, + GError *error, + GCallback callback, + gpointer callback_data) +{ + MMSerialResponseFn response_callback = (MMSerialResponseFn) callback; + + response_callback (self, response, error, callback_data); + return response->len; +} + static void mm_serial_port_got_response (MMSerialPort *self, GError *error) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); MMQueueData *info; + gsize consumed = priv->response->len; if (priv->timeout_id) { g_source_remove (priv->timeout_id); @@ -514,19 +493,26 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error) info = (MMQueueData *) g_queue_pop_head (priv->queue); if (info) { if (info->cached && !error) - mm_serial_port_set_cached_reply (self, info->command, priv->response->str); - - if (info->callback) - info->callback (self, priv->response, error, info->user_data); + mm_serial_port_set_cached_reply (self, info->command, priv->response); + + if (info->callback) { + g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->handle_response != NULL); + consumed = MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self, + priv->response, + error, + info->callback, + info->user_data); + } - g_free (info->command); + g_byte_array_free (info->command, TRUE); g_slice_free (MMQueueData, info); } if (error) g_error_free (error); - g_string_truncate (priv->response, 0); + if (consumed) + g_byte_array_remove_range (priv->response, 0, consumed); if (!g_queue_is_empty (priv->queue)) mm_serial_port_schedule_queue_process (self); } @@ -541,7 +527,7 @@ mm_serial_port_timed_out (gpointer data) priv->timeout_id = 0; error = g_error_new_literal (MM_SERIAL_ERROR, - MM_SERIAL_RESPONSE_TIMEOUT, + MM_SERIAL_ERROR_RESPONSE_TIMEOUT, "Serial command timed out"); /* FIXME: This is not completely correct - if the response finally arrives and there's some other command waiting for response right now, the other command will @@ -566,10 +552,10 @@ mm_serial_port_queue_process (gpointer data) return FALSE; if (info->cached) { - const char *cached = mm_serial_port_get_cached_reply (self, info->command); + const GByteArray *cached = mm_serial_port_get_cached_reply (self, info->command); if (cached) { - g_string_append (priv->response, cached); + g_byte_array_append (priv->response, cached->data, cached->len); mm_serial_port_got_response (self, NULL); return FALSE; } @@ -590,111 +576,16 @@ mm_serial_port_queue_process (gpointer data) return FALSE; } -void -mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self, - GRegex *regex, - MMSerialUnsolicitedMsgFn callback, - gpointer user_data, - GDestroyNotify notify) -{ - MMUnsolicitedMsgHandler *handler; - MMSerialPortPrivate *priv; - - g_return_if_fail (MM_IS_SERIAL_PORT (self)); - g_return_if_fail (regex != NULL); - - handler = g_slice_new (MMUnsolicitedMsgHandler); - handler->regex = g_regex_ref (regex); - handler->callback = callback; - handler->user_data = user_data; - handler->notify = notify; - - priv = MM_SERIAL_PORT_GET_PRIVATE (self); - priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler); -} - -void -mm_serial_port_set_response_parser (MMSerialPort *self, - MMSerialResponseParserFn fn, - gpointer user_data, - GDestroyNotify notify) -{ - MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - - g_return_if_fail (MM_IS_SERIAL_PORT (self)); - - if (priv->response_parser_notify) - priv->response_parser_notify (priv->response_parser_user_data); - - priv->response_parser_fn = fn; - priv->response_parser_user_data = user_data; - priv->response_parser_notify = notify; -} - -static gboolean -remove_eval_cb (const GMatchInfo *match_info, - GString *result, - gpointer user_data) -{ - int *result_len = (int *) user_data; - int start; - int end; - - if (g_match_info_fetch_pos (match_info, 0, &start, &end)) - *result_len -= (end - start); - - return FALSE; -} - -static void -parse_unsolicited_messages (MMSerialPort *self, - GString *response) -{ - MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - GSList *iter; - - for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) { - MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data; - GMatchInfo *match_info; - gboolean matches; - - matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL); - if (handler->callback) { - while (g_match_info_matches (match_info)) { - handler->callback (self, match_info, handler->user_data); - g_match_info_next (match_info, NULL); - } - } - - g_match_info_free (match_info); - - if (matches) { - /* Remove matches */ - char *str; - int result_len = response->len; - - str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0, - remove_eval_cb, &result_len, NULL); - - g_string_truncate (response, 0); - g_string_append_len (response, str, result_len); - g_free (str); - } - } -} - static gboolean parse_response (MMSerialPort *self, - GString *response, + GByteArray *response, GError **error) { - MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - - g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE); - - parse_unsolicited_messages (self, response); + if (MM_SERIAL_PORT_GET_CLASS (self)->parse_unsolicited) + MM_SERIAL_PORT_GET_CLASS (self)->parse_unsolicited (self, response); - return priv->response_parser_fn (priv->response_parser_user_data, response, error); + g_return_val_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->parse_response, FALSE); + return MM_SERIAL_PORT_GET_CLASS (self)->parse_response (self, response, error); } static gboolean @@ -709,13 +600,15 @@ data_available (GIOChannel *source, GIOStatus status; if (condition & G_IO_HUP) { - g_string_truncate (priv->response, 0); - mm_serial_port_close (self); + if (priv->response->len) + g_byte_array_remove_range (priv->response, 0, priv->response->len); + mm_serial_port_close_force (self); return FALSE; } if (condition & G_IO_ERR) { - g_string_truncate (priv->response, 0); + if (priv->response->len) + g_byte_array_remove_range (priv->response, 0, priv->response->len); return TRUE; } @@ -724,9 +617,13 @@ data_available (GIOChannel *source, status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err); if (status == G_IO_STATUS_ERROR) { - g_warning ("%s", err->message); - g_error_free (err); - err = NULL; + if (err && err->message) + g_warning ("%s", err->message); + g_clear_error (&err); + + /* Serial port is closed; we're done */ + if (priv->watch_id == 0) + break; } /* If no bytes read, just let g_io_channel wait for more data */ @@ -735,14 +632,14 @@ data_available (GIOChannel *source, if (bytes_read > 0) { serial_debug (self, "<--", buf, bytes_read); - g_string_append_len (priv->response, buf, bytes_read); + g_byte_array_append (priv->response, (const guint8 *) buf, bytes_read); } - /* Make sure the string doesn't grow too long */ - if (priv->response->len > SERIAL_BUF_SIZE) { - g_warning ("%s (%s): response buffer filled before repsonse received", - G_STRFUNC, mm_port_get_device (MM_PORT (self))); - g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2)); + /* Make sure the response doesn't grow too long */ + if (priv->response->len > SERIAL_BUF_SIZE) { + /* Notify listeners and then trim the buffer */ + g_signal_emit_by_name (self, "buffer-full", priv->response); + g_byte_array_remove_range (priv->response, 0, (SERIAL_BUF_SIZE / 2)); } if (parse_response (self, priv->response, &err)) @@ -792,13 +689,13 @@ mm_serial_port_open (MMSerialPort *self, GError **error) priv = MM_SERIAL_PORT_GET_PRIVATE (self); - if (priv->fd >= 0) { + device = mm_port_get_device (MM_PORT (self)); + + if (priv->open_count) { /* Already open */ - return TRUE; + goto success; } - device = mm_port_get_device (MM_PORT (self)); - g_message ("(%s) opening serial device...", device); devfile = g_strdup_printf ("/dev/%s", device); errno = 0; @@ -812,32 +709,29 @@ mm_serial_port_open (MMSerialPort *self, GError **error) */ g_set_error (error, MM_SERIAL_ERROR, - (errno == ENODEV) ? MM_SERIAL_OPEN_FAILED_NO_DEVICE : MM_SERIAL_OPEN_FAILED, + (errno == ENODEV) ? MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE : MM_SERIAL_ERROR_OPEN_FAILED, "Could not open serial device %s: %s", device, strerror (errno)); return FALSE; } if (ioctl (priv->fd, TIOCEXCL) < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Could not lock serial device %s: %s", device, strerror (errno)); - close (priv->fd); - priv->fd = -1; - return FALSE; + goto error; } - if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + /* Flush any waiting IO */ + tcflush (priv->fd, TCIOFLUSH); + + if (tcgetattr (priv->fd, &priv->old_t) < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Could not open serial device %s: %s", device, strerror (errno)); - close (priv->fd); - priv->fd = -1; - return FALSE; + goto error; } - if (!config_fd (self, error)) { - close (priv->fd); - priv->fd = -1; - return FALSE; - } + g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->config_fd); + if (!MM_SERIAL_PORT_GET_CLASS (self)->config_fd (self, priv->fd, error)) + goto error; priv->channel = g_io_channel_unix_new (priv->fd); g_io_channel_set_encoding (priv->channel, NULL, NULL); @@ -849,17 +743,58 @@ mm_serial_port_open (MMSerialPort *self, GError **error) priv->connected_id = g_signal_connect (self, "notify::" MM_PORT_CONNECTED, G_CALLBACK (port_connected), NULL); +success: + priv->open_count++; + if (mm_options_debug ()) { + GTimeVal tv; + + g_get_current_time (&tv); + g_debug ("<%ld.%ld> (%s) device open count is %d (open)", + tv.tv_sec, tv.tv_usec, device, priv->open_count); + } return TRUE; + +error: + close (priv->fd); + priv->fd = -1; + return FALSE; +} + +gboolean +mm_serial_port_is_open (MMSerialPort *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); + + return !!MM_SERIAL_PORT_GET_PRIVATE (self)->open_count; } void mm_serial_port_close (MMSerialPort *self) { MMSerialPortPrivate *priv; + const char *device; + int i; g_return_if_fail (MM_IS_SERIAL_PORT (self)); priv = MM_SERIAL_PORT_GET_PRIVATE (self); + g_return_if_fail (priv->open_count > 0); + + device = mm_port_get_device (MM_PORT (self)); + + priv->open_count--; + + if (mm_options_debug ()) { + GTimeVal tv; + + g_get_current_time (&tv); + g_debug ("<%ld.%ld> (%s) device open count is %d (close)", + tv.tv_sec, tv.tv_usec, device, priv->open_count); + } + + if (priv->open_count > 0) + return; if (priv->connected_id) { g_signal_handler_disconnect (self, priv->connected_id); @@ -867,31 +802,74 @@ mm_serial_port_close (MMSerialPort *self) } if (priv->fd >= 0) { - g_message ("(%s) closing serial device...", mm_port_get_device (MM_PORT (self))); + g_message ("(%s) closing serial device...", device); mm_port_set_connected (MM_PORT (self), FALSE); if (priv->channel) { g_source_remove (priv->watch_id); + priv->watch_id = 0; g_io_channel_shutdown (priv->channel, TRUE, NULL); g_io_channel_unref (priv->channel); priv->channel = NULL; } - if (priv->flash_id > 0) { - g_source_remove (priv->flash_id); - priv->flash_id = 0; - } + mm_serial_port_flash_cancel (self); - ioctl (priv->fd, TCSETA, &priv->old_t); + tcsetattr (priv->fd, TCSANOW, &priv->old_t); close (priv->fd); priv->fd = -1; } + + /* Clear the command queue */ + for (i = 0; i < g_queue_get_length (priv->queue); i++) { + MMQueueData *item = g_queue_peek_nth (priv->queue, i); + + if (item->callback) { + GError *error; + GByteArray *response; + + g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->handle_response != NULL); + error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_ERROR_SEND_FAILED, + "Serial port is now closed"); + response = g_byte_array_sized_new (1); + g_byte_array_append (response, (const guint8 *) "\0", 1); + MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self, + response, + error, + item->callback, + item->user_data); + g_error_free (error); + g_byte_array_free (response, TRUE); + } + + g_byte_array_free (item->command, TRUE); + g_slice_free (MMQueueData, item); + } + g_queue_clear (priv->queue); +} + +void +mm_serial_port_close_force (MMSerialPort *self) +{ + MMSerialPortPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + g_return_if_fail (priv->open_count > 0); + + /* Force the port to close */ + priv->open_count = 1; + mm_serial_port_close (self); } static void internal_queue_command (MMSerialPort *self, - const char *command, + GByteArray *command, + gboolean take_command, gboolean cached, guint32 timeout_seconds, MMSerialResponseFn callback, @@ -904,15 +882,20 @@ internal_queue_command (MMSerialPort *self, g_return_if_fail (command != NULL); info = g_slice_new0 (MMQueueData); - info->command = g_strdup (command); + if (take_command) + info->command = command; + else { + info->command = g_byte_array_sized_new (command->len); + g_byte_array_append (info->command, command->data, command->len); + } info->cached = cached; info->timeout = timeout_seconds; - info->callback = callback; + info->callback = (GCallback) callback; info->user_data = user_data; /* Clear the cached value for this command if not asking for cached value */ if (!cached) - mm_serial_port_set_cached_reply (self, command, NULL); + mm_serial_port_set_cached_reply (self, info->command, NULL); g_queue_push_tail (priv->queue, info); @@ -922,22 +905,24 @@ internal_queue_command (MMSerialPort *self, void mm_serial_port_queue_command (MMSerialPort *self, - const char *command, + GByteArray *command, + gboolean take_command, guint32 timeout_seconds, MMSerialResponseFn callback, gpointer user_data) { - internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data); + internal_queue_command (self, command, take_command, FALSE, timeout_seconds, callback, user_data); } void mm_serial_port_queue_command_cached (MMSerialPort *self, - const char *command, + GByteArray *command, + gboolean take_command, guint32 timeout_seconds, MMSerialResponseFn callback, gpointer user_data) { - internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data); + internal_queue_command (self, command, take_command, TRUE, timeout_seconds, callback, user_data); } typedef struct { @@ -1030,8 +1015,14 @@ flash_do (gpointer data) priv->flash_id = 0; - if (!set_speed (info->port, info->current_speed, &error)) - g_assert (error); + if (info->current_speed) { + if (!set_speed (info->port, info->current_speed, &error)) + g_assert (error); + } else { + error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_ERROR_FLASH_FAILED, + "Failed to retrieve current speed"); + } info->callback (info->port, error, info->user_data); g_clear_error (&error); @@ -1042,6 +1033,7 @@ flash_do (gpointer data) gboolean mm_serial_port_flash (MMSerialPort *self, guint32 flash_time, + gboolean ignore_errors, MMSerialFlashFn callback, gpointer user_data) { @@ -1049,12 +1041,22 @@ mm_serial_port_flash (MMSerialPort *self, MMSerialPortPrivate *priv; speed_t cur_speed = 0; GError *error = NULL; + gboolean success; g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); g_return_val_if_fail (callback != NULL, FALSE); priv = MM_SERIAL_PORT_GET_PRIVATE (self); + if (!mm_serial_port_is_open (self)) { + error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_ERROR_NOT_OPEN, + "The serial port is not open."); + callback (self, error, user_data); + g_error_free (error); + return FALSE; + } + if (priv->flash_id > 0) { error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_IN_PROGRESS, @@ -1064,11 +1066,13 @@ mm_serial_port_flash (MMSerialPort *self, return FALSE; } - if (!get_speed (self, &cur_speed, &error)) { + success = get_speed (self, &cur_speed, &error); + if (!success && !ignore_errors) { callback (self, error, user_data); g_error_free (error); return FALSE; } + g_clear_error (&error); info = g_slice_new0 (FlashInfo); info->port = self; @@ -1076,7 +1080,8 @@ mm_serial_port_flash (MMSerialPort *self, info->callback = callback; info->user_data = user_data; - if (!set_speed (self, B0, &error)) { + success = set_speed (self, B0, &error); + if (!success && !ignore_errors) { callback (self, error, user_data); g_error_free (error); return FALSE; @@ -1113,12 +1118,54 @@ mm_serial_port_new (const char *name, MMPortType ptype) NULL)); } +static gboolean +ba_equal (gconstpointer v1, gconstpointer v2) +{ + const GByteArray *a = v1; + const GByteArray *b = v2; + + if (!a && b) + return -1; + else if (a && !b) + return 1; + else if (!a && !b) + return 0; + + g_assert (a && b); + if (a->len < b->len) + return -1; + else if (a->len > b->len) + return 1; + + g_assert (a->len == b->len); + return !memcmp (a->data, b->data, a->len); +} + +static guint +ba_hash (gconstpointer v) +{ + /* 31 bit hash function */ + const GByteArray *array = v; + guint32 i, h = (const signed char) array->data[0]; + + for (i = 1; i < array->len; i++) + h = (h << 5) - h + (const signed char) array->data[i]; + + return h; +} + +static void +ba_free (gpointer v) +{ + g_byte_array_free ((GByteArray *) v, TRUE); +} + static void mm_serial_port_init (MMSerialPort *self) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->reply_cache = g_hash_table_new_full (ba_hash, ba_equal, ba_free, ba_free); priv->fd = -1; priv->baud = 57600; @@ -1128,8 +1175,7 @@ mm_serial_port_init (MMSerialPort *self) priv->send_delay = 1000; priv->queue = g_queue_new (); - priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE); - priv->response = g_string_sized_new (SERIAL_BUF_SIZE); + priv->response = g_byte_array_sized_new (500); } static void @@ -1191,7 +1237,10 @@ get_property (GObject *object, guint prop_id, static void dispose (GObject *object) { - mm_serial_port_close (MM_SERIAL_PORT (object)); + if (mm_serial_port_is_open (MM_SERIAL_PORT (object))) + mm_serial_port_close_force (MM_SERIAL_PORT (object)); + + mm_serial_port_flash_cancel (MM_SERIAL_PORT (object)); G_OBJECT_CLASS (mm_serial_port_parent_class)->dispose (object); } @@ -1203,24 +1252,8 @@ finalize (GObject *object) MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); g_hash_table_destroy (priv->reply_cache); + g_byte_array_free (priv->response, TRUE); g_queue_free (priv->queue); - g_string_free (priv->command, TRUE); - g_string_free (priv->response, TRUE); - - while (priv->unsolicited_msg_handlers) { - MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data; - - if (handler->notify) - handler->notify (handler->user_data); - - g_regex_unref (handler->regex); - g_slice_free (MMUnsolicitedMsgHandler, handler); - priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers, - priv->unsolicited_msg_handlers); - } - - if (priv->response_parser_notify) - priv->response_parser_notify (priv->response_parser_user_data); G_OBJECT_CLASS (mm_serial_port_parent_class)->finalize (object); } @@ -1238,6 +1271,9 @@ mm_serial_port_class_init (MMSerialPortClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; + klass->config_fd = real_config_fd; + klass->handle_response = real_handle_response; + /* Properties */ g_object_class_install_property (object_class, PROP_BAUD, @@ -1278,4 +1314,14 @@ mm_serial_port_class_init (MMSerialPortClass *klass) "Send delay", 0, G_MAXUINT64, 0, G_PARAM_READWRITE)); + + /* Signals */ + g_signal_new ("buffer-full", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMSerialPortClass, buffer_full), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); } + |