static gboolean wdm_probe_qmi (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return FALSE; #if defined WITH_QMI mm_dbg ("(%s/%s) probing QMI...", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port)); /* Create a port and try to open it */ task->qmi_port = mm_qmi_port_new (g_udev_device_get_name (self->priv->port)); mm_qmi_port_open (task->qmi_port, FALSE, NULL, (GAsyncReadyCallback)qmi_port_open_ready, self); #else /* If not compiled with QMI support, just assume we won't have any QMI port */ mm_port_probe_set_result_qmi (self, FALSE); port_probe_run_task_complete (task, TRUE, NULL); #endif /* WITH_QMI */ return FALSE; }
static void qmi_port_open_ready (MMQmiPort *qmi_port, GAsyncResult *res, MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; GError *error = NULL; gboolean is_qmi; is_qmi = mm_qmi_port_open_finish (qmi_port, res, &error); if (!is_qmi) { mm_dbg ("(%s/%s) error checking QMI support: '%s'", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port), error ? error->message : "unknown error"); g_clear_error (&error); } /* Set probing result */ mm_port_probe_set_result_qmi (self, is_qmi); mm_qmi_port_close (qmi_port); /* All done! Finish asynchronously */ port_probe_run_task_complete (task, TRUE, 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_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 void at_custom_init_ready (MMPortProbe *self, GAsyncResult *res) { PortProbeRunTask *task = self->priv->task; GError *error = NULL; if (!task->at_custom_init_finish (self, res, &error)) { /* All errors propagated up end up forcing an UNSUPPORTED result */ port_probe_run_task_complete (task, FALSE, error); return; } /* Keep on with remaining probings */ task->at_custom_init_run = TRUE; serial_probe_schedule (self); }
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; }
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 (); }
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; }
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_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 gboolean serial_probe_qcdm (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; GError *error = NULL; GByteArray *verinfo = NULL; GByteArray *verinfo2; gint len; guint8 marker = 0x7E; task->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return FALSE; mm_dbg ("(%s/%s) probing QCDM...", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port)); /* 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 (g_udev_device_get_name (self->priv->port))); if (!task->serial) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s/%s) Couldn't create QCDM port", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port))); 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/%s) Failed to open QCDM port: %s", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port), (error ? error->message : "unknown error"))); g_clear_error (&error); return FALSE; } /* Build up the probe command; 0x7E is the frame marker, so put one at the * beginning of the buffer to ensure that the device discards any AT * commands that probing might have sent earlier. Should help devices * respond more quickly and speed up QCDM probing. */ verinfo = g_byte_array_sized_new (10); g_byte_array_append (verinfo, &marker, 1); len = qcdm_cmd_version_info_new ((char *) (verinfo->data + 1), 9); 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/%s) Failed to create QCDM versin info command", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port))); return FALSE; } verinfo->len = len + 1; /* Queuing the command takes ownership over it; save it for the second try */ verinfo2 = g_byte_array_sized_new (verinfo->len); g_byte_array_append (verinfo2, verinfo->data, verinfo->len); g_object_set_data_full (G_OBJECT (self), "cmd2", verinfo2, (GDestroyNotify) g_byte_array_unref); mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial), verinfo, 3, NULL, (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, self); return FALSE; }