+EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
+
+int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id)
+{
+ struct gb_svc_intf_eject_request request;
+
+ request.intf_id = intf_id;
+
+ /*
+ * The pulse width for module release in svc is long so we need to
+ * increase the timeout so the operation will not return to soon.
+ */
+ return gb_operation_sync_timeout(svc->connection,
+ GB_SVC_TYPE_INTF_EJECT, &request,
+ sizeof(request), NULL, 0,
+ GB_SVC_EJECT_TIME);
+}
+EXPORT_SYMBOL_GPL(gb_svc_intf_eject);
+
+int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector,
+ u32 *value)
+{
+ struct gb_svc_dme_peer_get_request request;
+ struct gb_svc_dme_peer_get_response response;
+ u16 result;
+ int ret;
+
+ request.intf_id = intf_id;
+ request.attr = cpu_to_le16(attr);
+ request.selector = cpu_to_le16(selector);
+
+ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_GET,
+ &request, sizeof(request),
+ &response, sizeof(response));
+ if (ret) {
+ dev_err(&svc->dev, "failed to get DME attribute (%u 0x%04x %u): %d\n",
+ intf_id, attr, selector, ret);
+ return ret;
+ }
+
+ result = le16_to_cpu(response.result_code);
+ if (result) {
+ dev_err(&svc->dev, "UniPro error while getting DME attribute (%u 0x%04x %u): %u\n",
+ intf_id, attr, selector, result);
+ return -EIO;
+ }
+
+ if (value)
+ *value = le32_to_cpu(response.attr_value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gb_svc_dme_peer_get);
+
+int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector,
+ u32 value)
+{
+ struct gb_svc_dme_peer_set_request request;
+ struct gb_svc_dme_peer_set_response response;
+ u16 result;
+ int ret;
+
+ request.intf_id = intf_id;
+ request.attr = cpu_to_le16(attr);
+ request.selector = cpu_to_le16(selector);
+ request.value = cpu_to_le32(value);
+
+ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_SET,
+ &request, sizeof(request),
+ &response, sizeof(response));
+ if (ret) {
+ dev_err(&svc->dev, "failed to set DME attribute (%u 0x%04x %u %u): %d\n",
+ intf_id, attr, selector, value, ret);
+ return ret;
+ }
+
+ result = le16_to_cpu(response.result_code);
+ if (result) {
+ dev_err(&svc->dev, "UniPro error while setting DME attribute (%u 0x%04x %u %u): %u\n",
+ intf_id, attr, selector, value, result);
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set);
+
+/*
+ * T_TstSrcIncrement is written by the module on ES2 as a stand-in for boot
+ * status attribute ES3_INIT_STATUS. AP needs to read and clear it, after
+ * reading a non-zero value from it.
+ *
+ * FIXME: This is module-hardware dependent and needs to be extended for every
+ * type of module we want to support.
+ */
+static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf)
+{
+ struct gb_host_device *hd = intf->hd;
+ int ret;
+ u32 value;
+ u16 attr;
+ u8 init_status;
+
+ /*
+ * Check if the module is ES2 or ES3, and choose attr number
+ * appropriately.
+ * FIXME: Remove ES2 support from the kernel entirely.
+ */
+ if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID &&
+ intf->ddbl1_product_id == ES2_DDBL1_PROD_ID)
+ attr = DME_ATTR_T_TST_SRC_INCREMENT;
+ else
+ attr = DME_ATTR_ES3_INIT_STATUS;
+
+ /* Read and clear boot status in ES3_INIT_STATUS */
+ ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr,
+ DME_ATTR_SELECTOR_INDEX, &value);
+
+ if (ret)
+ return ret;
+
+ /*
+ * A nonzero boot status indicates the module has finished
+ * booting. Clear it.
+ */
+ if (!value) {
+ dev_err(&intf->dev, "Module not ready yet\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Check if the module needs to boot from UniPro.
+ * For ES2: We need to check lowest 8 bits of 'value'.
+ * For ES3: We need to check highest 8 bits out of 32 of 'value'.
+ * FIXME: Remove ES2 support from the kernel entirely.
+ */
+ if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID &&
+ intf->ddbl1_product_id == ES2_DDBL1_PROD_ID)
+ init_status = value;
+ else
+ init_status = value >> 24;
+
+ if (init_status == DME_DIS_UNIPRO_BOOT_STARTED ||
+ init_status == DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED)
+ intf->boot_over_unipro = true;
+
+ return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr,
+ DME_ATTR_SELECTOR_INDEX, 0);
+}