summaryrefslogtreecommitdiffstats
path: root/src/device_link_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/device_link_service.c')
-rw-r--r--src/device_link_service.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/device_link_service.c b/src/device_link_service.c
new file mode 100644
index 0000000..e1155a5
--- /dev/null
+++ b/src/device_link_service.c
@@ -0,0 +1,299 @@
+ /*
+ * device_link_service.c
+ * DeviceLink service implementation.
+ *
+ * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <string.h>
+#include <stdlib.h>
+#include "device_link_service.h"
+#include "property_list_service.h"
+#include "debug.h"
+
+/**
+ * Internally used function to extract the message string from a DLMessage*
+ * plist.
+ *
+ * @param dl_msg The DeviceLink property list to parse.
+ *
+ * @return An allocated char* with the DLMessage from the given plist,
+ * or NULL when the plist does not contain any DLMessage. It is up to
+ * the caller to free the allocated memory.
+ */
+static char *device_link_service_get_message(plist_t dl_msg)
+{
+ uint32_t cnt = 0;
+ plist_t cmd = 0;
+ char *cmd_str = NULL;
+
+ /* sanity check */
+ if ((plist_get_node_type(dl_msg) != PLIST_ARRAY) || ((cnt = plist_array_get_size(dl_msg)) < 1)) {
+ return NULL;
+ }
+
+ /* get dl command */
+ cmd = plist_array_get_item(dl_msg, 0);
+ if (!cmd || (plist_get_node_type(cmd) != PLIST_STRING)) {
+ return NULL;
+ }
+
+ plist_get_string_val(cmd, &cmd_str);
+ if (!cmd_str) {
+ return NULL;
+ }
+
+ if ((strlen(cmd_str) < (strlen("DLMessage")+1))
+ || (strncmp(cmd_str, "DLMessage", strlen("DLMessage")))) {
+ free(cmd_str);
+ return NULL;
+ }
+
+ /* we got a DLMessage* command */
+ return cmd_str;
+}
+
+/**
+ * Creates a new device link service client.
+ *
+ * @param device The device to connect to.
+ * @param port Port on device to connect to.
+ * @param client Reference that will point to a newly allocated
+ * device_link_service_client_t upon successful return.
+ *
+ * @return DEVICE_LINK_SERVICE_E_SUCCESS on success,
+ * DEVICE_LINK_SERVICE_E_INVALID_ARG when one of the parameters is invalid,
+ * or DEVICE_LINK_SERVICE_E_MUX_ERROR when the connection failed.
+ */
+device_link_service_error_t device_link_service_client_new(iphone_device_t device, uint16_t port, device_link_service_client_t *client)
+{
+ if (!device || port == 0 || !client || *client) {
+ return DEVICE_LINK_SERVICE_E_INVALID_ARG;
+ }
+
+ property_list_service_client_t plistclient = NULL;
+ if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ return DEVICE_LINK_SERVICE_E_MUX_ERROR;
+ }
+
+ /* create client object */
+ device_link_service_client_t client_loc = (device_link_service_client_t) malloc(sizeof(struct device_link_service_client_int));
+ client_loc->parent = plistclient;
+
+ /* all done, return success */
+ *client = client_loc;
+ return DEVICE_LINK_SERVICE_E_SUCCESS;
+}
+
+/**
+ * Frees a device link service client.
+ *
+ * @param client The device_link_service_client_t to free.
+ *
+ * @return DEVICE_LINK_SERVICE_E_SUCCESS on success,
+ * DEVICE_LINK_SERVICE_E_INVALID_ARG when one of client or client->parent
+ * is invalid, or DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR when the was an error
+ * freeing the parent property_list_service client.
+ */
+device_link_service_error_t device_link_service_client_free(device_link_service_client_t client)
+{
+ if (!client)
+ return DEVICE_LINK_SERVICE_E_INVALID_ARG;
+
+ if (property_list_service_client_free(client->parent) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ return DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
+ }
+ return DEVICE_LINK_SERVICE_E_SUCCESS;
+}
+
+/**
+ * Performs the DLMessageVersionExchange with the connected device.
+ * This should be the first operation to be executed by an implemented
+ * device link service client.
+ *
+ * @param client The device_link_service client to use.
+ * @param version_major The major version number to check.
+ * @param version_minor The minor version number to check.
+ *
+ * @return DEVICE_LINK_SERVICE_E_SUCCESS on success,
+ * DEVICE_LINK_SERVICE_E_INVALID_ARG when client is NULL,
+ * DEVICE_LINK_SERVICE_E_MUX_ERROR when a communication error occurs,
+ * DEVICE_LINK_SERVICE_E_PLIST_ERROR when the received plist has not the
+ * expected contents, DEVICE_LINK_SERVICE_E_BAD_VERSION when the version
+ * given by the device is larger than the given version,
+ * or DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR otherwise.
+ */
+device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor)
+{
+ if (!client)
+ return DEVICE_LINK_SERVICE_E_INVALID_ARG;
+
+ device_link_service_error_t err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
+
+ /* perform version exchange */
+ plist_t array = NULL;
+ char *msg = NULL;
+
+ /* receive DLMessageVersionExchange from device */
+ if (property_list_service_receive_plist(client->parent, &array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ debug_info("Did not receive initial message from device!");
+ err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
+ goto leave;
+ }
+ msg = device_link_service_get_message(array);
+ if (!msg || strcmp(msg, "DLMessageVersionExchange")) {
+ debug_info("Did not receive DLMessageVersionExchange from device!");
+ err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
+ goto leave;
+ }
+ free(msg);
+ msg = NULL;
+
+ /* get major and minor version number */
+ if (plist_array_get_size(array) < 3) {
+ debug_info("DLMessageVersionExchange has unexpected format!");
+ err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
+ goto leave;
+ }
+ plist_t maj = plist_array_get_item(array, 1);
+ plist_t min = plist_array_get_item(array, 2);
+ uint64_t vmajor = 0;
+ uint64_t vminor = 0;
+ if (maj) {
+ plist_get_uint_val(maj, &vmajor);
+ }
+ if (min) {
+ plist_get_uint_val(min, &vminor);
+ }
+ if (vmajor > version_major) {
+ debug_info("Version mismatch: device=(%lld,%lld) > expected=(%lld,%lld)", vmajor, vminor, version_major, version_minor);
+ err = DEVICE_LINK_SERVICE_E_BAD_VERSION;
+ goto leave;
+ } else if ((vmajor == version_major) && (vminor > version_minor)) {
+ debug_info("WARNING: Version mismatch: device=(%lld,%lld) > expected=(%lld,%lld)", vmajor, vminor, version_major, version_minor);
+ err = DEVICE_LINK_SERVICE_E_BAD_VERSION;
+ goto leave;
+ }
+ plist_free(array);
+
+ /* version is ok, send reply */
+ array = plist_new_array();
+ plist_array_append_item(array, plist_new_string("DLMessageVersionExchange"));
+ plist_array_append_item(array, plist_new_string("DLVersionsOk"));
+ if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ debug_info("Error when sending DLVersionsOk");
+ err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
+ goto leave;
+ }
+ plist_free(array);
+
+ /* receive DeviceReady message */
+ array = NULL;
+ if (property_list_service_receive_plist(client->parent, &array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ debug_info("Error when receiving DLMessageDeviceReady!");
+ err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
+ goto leave;
+ }
+ msg = device_link_service_get_message(array);
+ if (!msg || strcmp(msg, "DLMessageDeviceReady")) {
+ debug_info("Did not get DLMessageDeviceReady!");
+ err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
+ goto leave;
+ }
+ err = DEVICE_LINK_SERVICE_E_SUCCESS;
+
+leave:
+ if (msg) {
+ free(msg);
+ }
+ if (array) {
+ plist_free(array);
+ }
+ return err;
+}
+
+/**
+ * Performs a disconnect with the connected device link service client.
+ *
+ * @param client The device link service client to disconnect.
+ *
+ * @return DEVICE_LINK_SERVICE_E_SUCCESS on success,
+ * DEVICE_LINK_SERVICE_E_INVALID_ARG if client is NULL,
+ * or DEVICE_LINK_SERVICE_E_MUX_ERROR when there's an error when sending
+ * the the disconnect message.
+ */
+device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client)
+{
+ if (!client)
+ return DEVICE_LINK_SERVICE_E_INVALID_ARG;
+
+ plist_t array = plist_new_array();
+ plist_array_append_item(array, plist_new_string("DLMessageDisconnect"));
+ plist_array_append_item(array, plist_new_string("All done, thanks for the memories"));
+
+ device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS;
+ if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
+ }
+ plist_free(array);
+ return err;
+}
+
+/**
+ * Generic device link service send function.
+ *
+ * @param client The device link service client to use for sending
+ * @param plist The property list to send
+ *
+ * @return DEVICE_LINK_SERVICE_E_SUCCESS on success,
+ * DEVICE_LINK_SERVICE_E_INVALID_ARG when client or plist is NULL,
+ * or DEVICE_LINK_SERVICE_E_MUX_ERROR when the given property list could
+ * not be sent.
+ */
+device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist)
+{
+ if (!client || !plist) {
+ return DEVICE_LINK_SERVICE_E_INVALID_ARG;
+ }
+ if (property_list_service_send_binary_plist(client->parent, plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ return DEVICE_LINK_SERVICE_E_MUX_ERROR;
+ }
+ return DEVICE_LINK_SERVICE_E_SUCCESS;
+}
+
+/* Generic device link service receive function.
+ *
+ * @param client The device link service client to use for sending
+ * @param plist Pointer that will point to the property list received upon
+ * successful return.
+ *
+ * @return DEVICE_LINK_SERVICE_E_SUCCESS on success,
+ * DEVICE_LINK_SERVICE_E_INVALID_ARG when client or plist is NULL,
+ * or DEVICE_LINK_SERVICE_E_MUX_ERROR when no property list could be
+ * received.
+ */
+device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist)
+{
+ if (!client || !plist || (plist && *plist)) {
+ return DEVICE_LINK_SERVICE_E_INVALID_ARG;
+ }
+
+ if (property_list_service_receive_plist(client->parent, plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ return DEVICE_LINK_SERVICE_E_MUX_ERROR;
+ }
+ return DEVICE_LINK_SERVICE_E_SUCCESS;
+}
+