/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. * Copyright (C) 2009 - 2011 Red Hat, Inc. * Copyright (C) 2011 Aleksander Morgado */ #include #include #include #include #include #include "mm-port-probe.h" #include "mm-log.h" #include "mm-at-serial-port.h" #include "mm-serial-port.h" #include "mm-serial-parsers.h" #include "mm-port-probe-at.h" #include "libqcdm/src/commands.h" #include "libqcdm/src/utils.h" #include "libqcdm/src/errors.h" #include "mm-qcdm-serial-port.h" #include "mm-daemon-enums-types.h" /* * Steps and flow of the Probing process: * ----> AT Serial Open * |----> Custom Init * |----> AT? * |----> Vendor * |----> Product * ----> QCDM Serial Open * |----> QCDM? */ G_DEFINE_TYPE (MMPortProbe, mm_port_probe, G_TYPE_OBJECT) typedef struct { /* ---- Generic task context ---- */ GSimpleAsyncResult *result; GCancellable *cancellable; GCancellable *at_probing_cancellable; guint32 flags; guint source_id; guint buffer_full_id; MMSerialPort *serial; /* ---- AT probing specific context ---- */ guint64 at_send_delay; /* Number of times we tried to open the AT port */ guint at_open_tries; /* Custom initialization commands for the AT port */ const MMPortProbeAtCommand *at_custom_init; /* Current group of AT commands to be sent */ const MMPortProbeAtCommand *at_commands; /* Current AT Result processor */ void (* at_result_processor) (MMPortProbe *self, GVariant *result); } PortProbeRunTask; struct _MMPortProbePrivate { /* Port and properties */ GUdevDevice *port; gchar *subsys; gchar *name; gchar *physdev_path; gchar *driver; /* Probing results */ guint32 flags; gboolean is_at; gboolean is_qcdm; gchar *vendor; gchar *product; /* Current probing task. Only one can be available at a time */ PortProbeRunTask *task; }; void mm_port_probe_set_result_at (MMPortProbe *self, gboolean at) { self->priv->is_at = at; self->priv->flags |= MM_PORT_PROBE_AT; if (self->priv->is_at) { mm_dbg ("(%s) port is AT-capable", self->priv->name); /* Also set as not a QCDM port */ self->priv->is_qcdm = FALSE; self->priv->flags |= MM_PORT_PROBE_QCDM; } else { mm_dbg ("(%s) port is not AT-capable", self->priv->name); self->priv->vendor = NULL; self->priv->product = NULL; self->priv->flags |= (MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT); } } void mm_port_probe_set_result_at_vendor (MMPortProbe *self, const gchar *at_vendor) { if (at_vendor) { mm_dbg ("(%s) vendor probing finished", self->priv->name); self->priv->vendor = g_utf8_casefold (at_vendor, -1); self->priv->flags |= MM_PORT_PROBE_AT_VENDOR; } else { mm_dbg ("(%s) couldn't probe for vendor string", self->priv->name); self->priv->vendor = NULL; self->priv->product = NULL; self->priv->flags |= (MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT); } } void mm_port_probe_set_result_at_product (MMPortProbe *self, const gchar *at_product) { if (at_product) { mm_dbg ("(%s) product probing finished", self->priv->name); self->priv->product = g_utf8_casefold (at_product, -1); self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT; } else { mm_dbg ("(%s) couldn't probe for product string", self->priv->name); self->priv->product = NULL; self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT; } } void mm_port_probe_set_result_qcdm (MMPortProbe *self, gboolean qcdm) { self->priv->is_qcdm = qcdm; self->priv->flags |= MM_PORT_PROBE_QCDM; if (self->priv->is_qcdm) { mm_dbg ("(%s) port is QCDM-capable", self->priv->name); /* Also set as not an AT port */ self->priv->is_at = FALSE; self->priv->vendor = NULL; self->priv->product = NULL; self->priv->flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT); } else mm_dbg ("(%s) port is not QCDM-capable", self->priv->name); } static gboolean serial_probe_at (MMPortProbe *self); static gboolean serial_probe_qcdm (MMPortProbe *self); static void serial_probe_schedule (MMPortProbe *self); static void port_probe_run_task_free (PortProbeRunTask *task) { if (task->source_id) g_source_remove (task->source_id); if (task->buffer_full_id) g_source_remove (task->buffer_full_id); if (task->serial) { if (mm_serial_port_is_open (task->serial)) mm_serial_port_close (task->serial); g_object_unref (task->serial); } if (task->cancellable) g_object_unref (task->cancellable); if (task->at_probing_cancellable) g_object_unref (task->at_probing_cancellable); g_object_unref (task->result); g_free (task); } static void port_probe_run_task_complete (PortProbeRunTask *task, gboolean result, GError *error) { /* As soon as we have the task completed, disable the buffer-full signal * handling, so that we do not get unwanted errors reported */ if (task->buffer_full_id) { g_source_remove (task->buffer_full_id); task->buffer_full_id = 0; } if (error) g_simple_async_result_take_error (task->result, error); else g_simple_async_result_set_op_res_gboolean (task->result, result); /* Always complete in idle */ g_simple_async_result_complete_in_idle (task->result); } static gboolean port_probe_run_is_cancelled (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; /* Manually check if cancelled. * TODO: Make the serial port response wait cancellable, * so that we can connect a callback to the cancellable and forget about * manually checking it. */ if (g_cancellable_is_cancelled (task->cancellable)) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "(%s) port probing cancelled", self->priv->name)); return TRUE; } return FALSE; } static void serial_probe_qcdm_parse_response (MMQcdmSerialPort *port, GByteArray *response, GError *error, MMPortProbe *self) { QcdmResult *result; gint err = QCDM_SUCCESS; gboolean is_qcdm = FALSE; /* Just the initial poke; ignore it */ if (!self) return; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return; if (!error) { /* Parse the response */ result = qcdm_cmd_version_info_result ((const gchar *) response->data, response->len, &err); if (!result) { mm_warn ("(%s) failed to parse QCDM version info command result: %d", self->priv->name, err); } else { /* yay, probably a QCDM port */ is_qcdm = TRUE; qcdm_result_unref (result); } } /* Set probing result */ mm_port_probe_set_result_qcdm (self, is_qcdm); /* Reschedule probing */ serial_probe_schedule (self); } static gboolean serial_probe_qcdm (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; GError *error = NULL; GByteArray *verinfo = NULL; GByteArray *verinfo2; gint len; task->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return FALSE; mm_dbg ("(%s) probing QCDM...", self->priv->name); /* If open, close the AT port */ if (task->serial) { mm_serial_port_close (task->serial); g_object_unref (task->serial); } /* Open the QCDM port */ task->serial = MM_SERIAL_PORT (mm_qcdm_serial_port_new (self->priv->name)); if (!task->serial) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s) Couldn't create QCDM port", self->priv->name)); return FALSE; } /* Try to open the port */ if (!mm_serial_port_open (task->serial, &error)) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s) Failed to open QCDM port: %s", self->priv->name, (error ? error->message : "unknown error"))); g_clear_error (&error); return FALSE; } /* Build up the probe command */ verinfo = g_byte_array_sized_new (50); len = qcdm_cmd_version_info_new ((gchar *) verinfo->data, 50); if (len <= 0) { g_byte_array_free (verinfo, TRUE); port_probe_run_task_complete ( task, FALSE, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s) Failed to create QCDM versin info command", self->priv->name)); return FALSE; } verinfo->len = len; /* Queuing the command takes ownership over it; dup it for the second try */ verinfo2 = g_byte_array_sized_new (verinfo->len); g_byte_array_append (verinfo2, verinfo->data, verinfo->len); /* Send the command twice; the ports often need to be woken up */ mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial), verinfo, 3, NULL, (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, NULL); mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial), verinfo2, 3, NULL, (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, self); return FALSE; } static void serial_probe_at_product_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a string */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_STRING)); mm_port_probe_set_result_at_product (self, g_variant_get_string (result, NULL)); return; } mm_port_probe_set_result_at_product (self, NULL); } static void serial_probe_at_vendor_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a string */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_STRING)); mm_port_probe_set_result_at_vendor (self, g_variant_get_string (result, NULL)); return; } mm_port_probe_set_result_at_vendor (self, NULL); } static void serial_probe_at_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a boolean */ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_BOOLEAN)); if (g_variant_get_boolean (result)) { mm_port_probe_set_result_at (self, TRUE); return; } } mm_port_probe_set_result_at (self, FALSE); } static void serial_probe_at_custom_init_result_processor (MMPortProbe *self, GVariant *result) { PortProbeRunTask *task = self->priv->task; /* No result is really expected here, but we could get a boolean to indicate * AT support */ if (result) serial_probe_at_result_processor (self, result); /* Reset so that it doesn't get scheduled again */ task->at_custom_init = NULL; } static void serial_probe_at_parse_response (MMAtSerialPort *port, GString *response, GError *error, MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; GVariant *result = NULL; GError *result_error = NULL; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return; /* If AT probing cancelled, end this partial probing */ if (g_cancellable_is_cancelled (task->at_probing_cancellable)) { mm_dbg ("(%s) no need to keep on probing the port for AT support", self->priv->name); task->at_result_processor (self, NULL); serial_probe_schedule (self); return; } if (!task->at_commands->response_processor (task->at_commands->command, response->str, !!task->at_commands[1].command, error, &result, &result_error)) { /* Were we told to abort the whole probing? */ if (result_error) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "(%s) error while probing AT features: %s", self->priv->name, result_error->message)); g_error_free (result_error); return; } /* Go on to next command */ task->at_commands++; if (!task->at_commands->command) { /* Was it the last command in the group? If so, * end this partial probing */ task->at_result_processor (self, NULL); /* Reschedule */ serial_probe_schedule (self); return; } /* Schedule the next command in the probing group */ task->source_id = g_idle_add ((GSourceFunc)serial_probe_at, self); return; } /* Run result processor. * Note that custom init commands are allowed to not return anything */ task->at_result_processor (self, result); if (result) g_variant_unref (result); /* Reschedule probing */ serial_probe_schedule (self); } static gboolean serial_probe_at (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; task->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return FALSE; /* If AT probing cancelled, end this partial probing */ if (g_cancellable_is_cancelled (task->at_probing_cancellable)) { mm_dbg ("(%s) no need to launch probing for AT support", self->priv->name); task->at_result_processor (self, NULL); serial_probe_schedule (self); return FALSE; } mm_at_serial_port_queue_command ( MM_AT_SERIAL_PORT (task->serial), task->at_commands->command, task->at_commands->timeout, task->at_probing_cancellable, (MMAtSerialResponseFn)serial_probe_at_parse_response, self); return FALSE; } static const MMPortProbeAtCommand at_probing[] = { { "AT", 3, mm_port_probe_response_processor_is_at }, { "AT", 3, mm_port_probe_response_processor_is_at }, { "AT", 3, mm_port_probe_response_processor_is_at }, { NULL } }; static const MMPortProbeAtCommand vendor_probing[] = { { "+CGMI", 3, mm_port_probe_response_processor_string }, { "+GMI", 3, mm_port_probe_response_processor_string }, { "I", 3, mm_port_probe_response_processor_string }, { NULL } }; static const MMPortProbeAtCommand product_probing[] = { { "+CGMM", 3, mm_port_probe_response_processor_string }, { "+GMM", 3, mm_port_probe_response_processor_string }, { "I", 3, mm_port_probe_response_processor_string }, { NULL } }; static void serial_probe_schedule (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return; /* Cleanup */ task->at_result_processor = NULL; task->at_commands = NULL; /* If we got some custom initialization commands requested, go on with them * first. */ if (task->at_custom_init) { task->at_result_processor = serial_probe_at_custom_init_result_processor; task->at_commands = task->at_custom_init; } /* AT check requested and not already probed? */ else if ((task->flags & MM_PORT_PROBE_AT) && !(self->priv->flags & MM_PORT_PROBE_AT)) { /* Prepare AT probing */ task->at_result_processor = serial_probe_at_result_processor; task->at_commands = at_probing; } /* Vendor requested and not already probed? */ else if ((task->flags & MM_PORT_PROBE_AT_VENDOR) && !(self->priv->flags & MM_PORT_PROBE_AT_VENDOR)) { /* Prepare AT vendor probing */ task->at_result_processor = serial_probe_at_vendor_result_processor; task->at_commands = vendor_probing; } /* Product requested and not already probed? */ else if ((task->flags & MM_PORT_PROBE_AT_PRODUCT) && !(self->priv->flags & MM_PORT_PROBE_AT_PRODUCT)) { /* Prepare AT product probing */ task->at_result_processor = serial_probe_at_product_result_processor; task->at_commands = product_probing; } /* If a next AT group detected, go for it */ if (task->at_result_processor && task->at_commands) { task->source_id = g_idle_add ((GSourceFunc)serial_probe_at, self); return; } /* QCDM requested and not already probed? */ if ((task->flags & MM_PORT_PROBE_QCDM) && !(self->priv->flags & MM_PORT_PROBE_QCDM)) { task->source_id = g_idle_add ((GSourceFunc)serial_probe_qcdm, self); return; } /* All done! Finish asynchronously */ port_probe_run_task_complete (task, TRUE, NULL); } static void serial_flash_done (MMSerialPort *port, GError *error, MMPortProbe *self) { /* Schedule probing */ serial_probe_schedule (self); } static const gchar *dq_strings[] = { /* Option Icera-based devices */ "option/faema_", "os_logids.h", /* Sierra CnS port */ "NETWORK SERVICE CHANGE", "/SRC/AMSS", NULL }; static const guint8 zerobuf[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static void serial_buffer_full (MMSerialPort *serial, GByteArray *buffer, PortProbeRunTask *task) { const gchar **iter; size_t iter_len; int i; /* Some devices (observed on a ZTE branded "QUALCOMM INCORPORATED" model * "154") spew NULLs from some ports. */ if ( (buffer->len >= sizeof (zerobuf)) && (memcmp (buffer->data, zerobuf, sizeof (zerobuf)) == 0)) { mm_serial_port_close (serial); port_probe_run_task_complete (task, FALSE, NULL); return; } /* Check for an immediate disqualification response. There are some * ports (Option Icera-based chipsets have them, as do Qualcomm Gobi * devices before their firmware is loaded) that just shouldn't be * probed if we get a certain response because we know they can't be * used. Kernel bugs (at least with 2.6.31 and 2.6.32) also trigger port * flow control kernel oopses if we read too much data for these ports. */ for (iter = &dq_strings[0]; iter && *iter; iter++) { /* Search in the response for the item; the response could have embedded * nulls so we can't use memcmp() or strstr() on the whole response. */ iter_len = strlen (*iter); for (i = 0; i < buffer->len - iter_len; i++) { if (!memcmp (&buffer->data[i], *iter, iter_len)) { /* Immediately close the port and complete probing */ mm_serial_port_close (serial); port_probe_run_task_complete (task, FALSE, NULL); return; } } } } static gboolean serial_open_at (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; GError *error = NULL; task->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return FALSE; /* Create AT serial port if not done before */ if (!task->serial) { task->serial = MM_SERIAL_PORT (mm_at_serial_port_new (self->priv->name)); if (!task->serial) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s) couldn't create AT port", self->priv->name)); return FALSE; } g_object_set (task->serial, MM_SERIAL_PORT_SEND_DELAY, task->at_send_delay, MM_PORT_CARRIER_DETECT, FALSE, MM_SERIAL_PORT_SPEW_CONTROL, TRUE, NULL); mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (task->serial), mm_serial_parser_v1_parse, mm_serial_parser_v1_new (), mm_serial_parser_v1_destroy); } /* Try to open the port */ if (!mm_serial_port_open (task->serial, &error)) { /* Abort if maximum number of open tries reached */ if (++task->at_open_tries > 4) { /* took too long to open the port; give up */ port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s) failed to open port after 4 tries", self->priv->name)); } else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE)) { /* this is nozomi being dumb; try again */ task->source_id = g_timeout_add_seconds (1, (GSourceFunc)serial_open_at, self); } else { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s) failed to open port: %s", self->priv->name, (error ? error->message : "unknown error"))); } g_clear_error (&error); return FALSE; } /* success, start probing */ task->buffer_full_id = g_signal_connect (task->serial, "buffer-full", G_CALLBACK (serial_buffer_full), self); mm_serial_port_flash (MM_SERIAL_PORT (task->serial), 100, TRUE, (MMSerialFlashFn)serial_flash_done, self); return FALSE; } gboolean mm_port_probe_run_cancel_at_probing (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); if (self->priv->task) { mm_dbg ("(%s) requested to cancel all AT probing", self->priv->name); g_cancellable_cancel (self->priv->task->at_probing_cancellable); return TRUE; } return FALSE; } gboolean mm_port_probe_run_cancel (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); if (self->priv->task) { mm_dbg ("(%s) requested to cancel the probing", self->priv->name); g_cancellable_cancel (self->priv->task->cancellable); return TRUE; } return FALSE; } gboolean mm_port_probe_run_finish (MMPortProbe *self, GAsyncResult *result, GError **error) { gboolean res; g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); /* Propagate error, if any */ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) res = FALSE; else res = g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)); /* Cleanup probing task */ if (self->priv->task) { port_probe_run_task_free (self->priv->task); self->priv->task = NULL; } return res; } void mm_port_probe_run (MMPortProbe *self, MMPortProbeFlag flags, guint64 at_send_delay, const MMPortProbeAtCommand *at_custom_init, GAsyncReadyCallback callback, gpointer user_data) { PortProbeRunTask *task; guint32 i; gchar *probe_list_str; g_return_if_fail (MM_IS_PORT_PROBE (self)); g_return_if_fail (flags != MM_PORT_PROBE_NONE); g_return_if_fail (callback != NULL); /* Shouldn't schedule more than one probing at a time */ g_assert (self->priv->task == NULL); task = g_new0 (PortProbeRunTask, 1); task->at_send_delay = at_send_delay; task->flags = MM_PORT_PROBE_NONE; task->at_custom_init = at_custom_init; task->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, mm_port_probe_run); /* Check if we already have the requested probing results. * We will fix here the 'task->flags' so that we only request probing * for the missing things. */ for (i = MM_PORT_PROBE_AT; i <= MM_PORT_PROBE_QCDM; i = (i << 1)) { if ((flags & i) && !(self->priv->flags & i)) { task->flags += i; } } /* Store as current task. We need to keep it internally, as it will be * freed during _finish() when the operation is completed. */ self->priv->task = task; /* All requested probings already available? If so, we're done */ if (!task->flags) { port_probe_run_task_complete (task, TRUE, NULL); return; } /* Setup internal cancellable */ task->cancellable = g_cancellable_new (); probe_list_str = mm_port_probe_flag_build_string_from_mask (task->flags); mm_info ("(%s) launching port probing: '%s'", self->priv->name, probe_list_str); g_free (probe_list_str); /* If any AT probing is needed, start by opening as AT port */ if (task->flags & MM_PORT_PROBE_AT || task->flags & MM_PORT_PROBE_AT_VENDOR || task->flags & MM_PORT_PROBE_AT_PRODUCT) { task->at_probing_cancellable = g_cancellable_new (); task->source_id = g_idle_add ((GSourceFunc)serial_open_at, self); return; } /* Otherwise, start by opening as QCDM port */ if (task->flags & MM_PORT_PROBE_QCDM) { task->source_id = g_idle_add ((GSourceFunc)serial_probe_qcdm, self); return; } /* Shouldn't happen */ g_assert_not_reached (); } gboolean mm_port_probe_is_at (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); if (g_str_equal (self->priv->subsys, "net")) return FALSE; /* Warn if it wasn't probed */ g_return_val_if_fail (self->priv->flags & MM_PORT_PROBE_AT, FALSE); return self->priv->is_at; } gboolean mm_port_probe_is_qcdm (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); if (g_str_equal (self->priv->subsys, "net")) return FALSE; /* Warn if it wasn't probed */ g_return_val_if_fail (self->priv->flags & MM_PORT_PROBE_QCDM, FALSE); return self->priv->is_qcdm; } MMPortType mm_port_probe_get_port_type (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE); if (g_str_equal (self->priv->subsys, "net")) return MM_PORT_TYPE_UNKNOWN; if (self->priv->flags & MM_PORT_PROBE_QCDM && self->priv->is_qcdm) return MM_PORT_TYPE_QCDM; if (self->priv->flags & MM_PORT_PROBE_AT && self->priv->is_at) return MM_PORT_TYPE_AT; return MM_PORT_TYPE_UNKNOWN; } GUdevDevice * mm_port_probe_get_port (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return self->priv->port; }; const gchar * mm_port_probe_get_vendor (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); if (g_str_equal (self->priv->subsys, "net")) return NULL; /* Warn if it wasn't probed */ g_return_val_if_fail (self->priv->flags & MM_PORT_PROBE_AT_VENDOR, NULL); return self->priv->vendor; } const gchar * mm_port_probe_get_product (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); if (g_str_equal (self->priv->subsys, "net")) return NULL; /* Warn if it wasn't probed */ g_return_val_if_fail (self->priv->flags & MM_PORT_PROBE_AT_PRODUCT, NULL); return self->priv->product; } const gchar * mm_port_probe_get_port_name (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return self->priv->name; } const gchar * mm_port_probe_get_port_subsys (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return self->priv->subsys; } const gchar * mm_port_probe_get_port_physdev (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return self->priv->physdev_path; } const gchar * mm_port_probe_get_port_driver (MMPortProbe *self) { g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL); return self->priv->driver; } MMPortProbe * mm_port_probe_new (GUdevDevice *port, const gchar *physdev_path, const gchar *driver) { MMPortProbe *self; self = MM_PORT_PROBE (g_object_new (MM_TYPE_PORT_PROBE, NULL)); self->priv->port = g_object_ref (port); self->priv->subsys = g_strdup (g_udev_device_get_subsystem (port)); self->priv->name = g_strdup (g_udev_device_get_name (port)); self->priv->physdev_path = g_strdup (physdev_path); self->priv->driver = g_strdup (driver); return self; } static void mm_port_probe_init (MMPortProbe *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), \ MM_TYPE_PORT_PROBE, \ MMPortProbePrivate); } static void finalize (GObject *object) { MMPortProbe *self = MM_PORT_PROBE (object); /* We should never have a task here */ g_assert (self->priv->task == NULL); g_free (self->priv->subsys); g_free (self->priv->name); g_free (self->priv->physdev_path); g_free (self->priv->driver); g_object_unref (self->priv->port); g_free (self->priv->vendor); g_free (self->priv->product); G_OBJECT_CLASS (mm_port_probe_parent_class)->finalize (object); } static void mm_port_probe_class_init (MMPortProbeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPortProbePrivate)); /* Virtual methods */ object_class->finalize = finalize; }