From 1f6282ffddec7012df82fa929dfe72cfc74b063a Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Thu, 10 Sep 2009 13:28:13 +0200 Subject: Public API rework, extension and adaption to latest libusbmuxd-1.0 API. --- src/iphone.c | 371 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 305 insertions(+), 66 deletions(-) (limited to 'src/iphone.c') diff --git a/src/iphone.c b/src/iphone.c index e694373..80e796b 100644 --- a/src/iphone.c +++ b/src/iphone.c @@ -1,8 +1,9 @@ /* * iphone.c - * Functions for creating and initializing iPhone structures. + * Device discovery and communication interface. * * Copyright (c) 2008 Zach C. All Rights Reserved. + * Copyright (c) 2009 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 @@ -19,104 +20,161 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include #include +#include +#include #include "iphone.h" #include "utils.h" +static iphone_event_cb_t event_cb = NULL; + +static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) +{ + iphone_event_t ev; + + ev.event = event->event; + ev.uuid = event->device.uuid; + ev.conn_type = CONNECTION_USBMUXD; + + if (event_cb) { + event_cb(&ev, user_data); + } +} + /** - * Retrieves a list of connected devices from usbmuxd and matches their - * UUID with the given UUID. If the given UUID is NULL then the first - * device reported by usbmuxd is used. + * Register a callback function that will be called when device add/remove + * events occur. * - * @param device Upon calling this function, a pointer to a location of type - * iphone_device_t, which must have the value NULL. On return, this location - * will be filled with a handle to the device. - * @param uuid The UUID to match. + * @param callback Callback function to call. + * @param user_data Application-specific data passed as parameter + * to the registered callback function. * - * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + * @return IPHONE_E_SUCCESS on success or an error value when an error occured. */ -iphone_error_t iphone_get_device_by_uuid(iphone_device_t * device, const char *uuid) +iphone_error_t iphone_event_subscribe(iphone_event_cb_t callback, void *user_data) { - iphone_device_t phone; - uint32_t handle = 0; - char *serial_number = malloc(41); - usbmuxd_scan_result *dev_list = NULL; - int i; - - if (usbmuxd_scan(&dev_list) < 0) { - log_debug_msg("%s: usbmuxd_scan returned an error, is usbmuxd running?\n", __func__); - } - if (dev_list && dev_list[0].handle > 0) { - if (!uuid) { - /* select first device found if no UUID specified */ - handle = dev_list[0].handle; - strcpy(serial_number, dev_list[0].serial_number); - } else { - /* otherwise walk through the list */ - for (i = 0; dev_list[i].handle > 0; i++) { - log_debug_msg("%s: device handle=%d, uuid=%s\n", __func__, dev_list[i].handle, dev_list[i].serial_number); - if (strcasecmp(uuid, dev_list[i].serial_number) == 0) { - handle = dev_list[i].handle; - strcpy(serial_number, dev_list[i].serial_number); - break; - } - } - } - free(dev_list); - - if (handle > 0) { - phone = (iphone_device_t) malloc(sizeof(struct iphone_device_int)); - phone->handle = handle; - phone->serial_number = serial_number; - *device = phone; - return IPHONE_E_SUCCESS; - } + event_cb = callback; + int res = usbmuxd_subscribe(usbmux_event_cb, user_data); + if (res != 0) { + event_cb = NULL; + log_debug_msg("%s: Error %d when subscribing usbmux event callback!\n", __func__, res); + return IPHONE_E_UNKNOWN_ERROR; } + return IPHONE_E_SUCCESS; +} - return IPHONE_E_NO_DEVICE; +/** + * Release the event callback function that has been registered with + * iphone_event_subscribe(). + * + * @return IPHONE_E_SUCCESS on success or an error value when an error occured. + */ +iphone_error_t iphone_event_unsubscribe() +{ + event_cb = NULL; + int res = usbmuxd_unsubscribe(); + if (res != 0) { + log_debug_msg("%s: Error %d when unsubscribing usbmux event callback!\n", __func__, res); + return IPHONE_E_UNKNOWN_ERROR; + } + return IPHONE_E_SUCCESS; } /** - * This function has the purpose to retrieve a handle to the first - * attached iPhone/iPod reported by usbmuxd. + * Get a list of currently available devices. * - * @param Upon calling this function, a pointer to a location of type - * iphone_device_t, which must have the value NULL. On return, this location - * will be filled with a handle to the device. + * @param devices List of uuids of devices that are currently available. + * This list is terminated by a NULL pointer. + * @param count Number of devices found. * - * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + * @return IPHONE_E_SUCCESS on success or an error value when an error occured. */ -iphone_error_t iphone_get_device(iphone_device_t * device) +iphone_error_t iphone_get_device_list(char ***devices, int *count) { - return iphone_get_device_by_uuid(device, NULL); + usbmuxd_device_info_t *dev_list; + + *devices = NULL; + *count = 0; + + if (usbmuxd_get_device_list(&dev_list) < 0) { + log_debug_msg("%s: ERROR: usbmuxd is not running!\n", __func__); + return IPHONE_E_NO_DEVICE; + } + + char **newlist = NULL; + int i, newcount = 0; + + for (i = 0; dev_list[i].handle > 0; i++) { + newlist = realloc(*devices, sizeof(char*) * (newcount+1)); + newlist[newcount++] = strdup(dev_list[i].uuid); + *devices = newlist; + } + usbmuxd_free_device_list(dev_list); + + *count = newcount; + newlist = realloc(*devices, sizeof(char*) * (newcount+1)); + newlist[newcount] = NULL; + *devices = newlist; + + return IPHONE_E_SUCCESS; } -iphone_error_t iphone_device_get_handle(iphone_device_t device, uint32_t *handle) +/** + * Free a list of device uuids. + * + * @param devices List of uuids to free. + * + * @return Always returnes IPHONE_E_SUCCESS. + */ +iphone_error_t iphone_free_device_list(char **devices) { - if (!device) - return IPHONE_E_INVALID_ARG; - - *handle = device->handle; + if (devices) { + int i = 0; + while (devices[i++]) { + free(devices[i]); + } + free(devices); + } return IPHONE_E_SUCCESS; } -iphone_error_t iphone_device_get_uuid(iphone_device_t device, char **uuid) +/** + * Creates an iphone_device_t structure for the device specified by uuid, + * if the device is available. + * + * @note The resulting iphone_device_t structure has to be freed with + * iphone_device_free() if it is no longer used. + * + * @param device Upon calling this function, a pointer to a location of type + * iphone_device_t. On successful return, this location will be populated. + * @param uuid The UUID to match. + * + * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + */ +iphone_error_t iphone_device_new(iphone_device_t * device, const char *uuid) { - if (!device) - return IPHONE_E_INVALID_ARG; + usbmuxd_device_info_t muxdev; + int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); + if (res > 0) { + iphone_device_t phone = (iphone_device_t) malloc(sizeof(struct iphone_device_int)); + phone->uuid = strdup(muxdev.uuid); + phone->conn_type = CONNECTION_USBMUXD; + phone->conn_data = (void*)muxdev.handle; + *device = phone; + return IPHONE_E_SUCCESS; + } + /* other connection types could follow here */ - *uuid = strdup(device->serial_number); - return IPHONE_E_SUCCESS; + return IPHONE_E_NO_DEVICE; } /** Cleans up an iPhone structure, then frees the structure itself. * This is a library-level function; deals directly with the iPhone to tear * down relations, but otherwise is mostly internal. * - * @param phone A pointer to an iPhone structure. + * @param device A pointer to an iPhone structure. */ iphone_error_t iphone_device_free(iphone_device_t device) { @@ -126,8 +184,189 @@ iphone_error_t iphone_device_free(iphone_device_t device) ret = IPHONE_E_SUCCESS; - free(device->serial_number); + free(device->uuid); + + if (device->conn_type == CONNECTION_USBMUXD) { + device->conn_data = 0; + } + if (device->conn_data) { + free(device->conn_data); + } free(device); return ret; } +/** + * Set up a connection to the given device. + * + * @param device The device to connect to. + * @param dst_port The destination port to connect to. + * @param connection Pointer to an iphone_connection_t that will be filled + * with the necessary data of the connection. + * + * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + */ +iphone_error_t iphone_device_connect(iphone_device_t device, uint16_t dst_port, iphone_connection_t *connection) +{ + if (!device) { + return IPHONE_E_INVALID_ARG; + } + + if (device->conn_type == CONNECTION_USBMUXD) { + int sfd = usbmuxd_connect((uint32_t)(device->conn_data), dst_port); + if (sfd < 0) { + log_debug_msg("%s: ERROR: Connecting to usbmuxd failed: %d (%s)\n", __func__, sfd, strerror(-sfd)); + return IPHONE_E_UNKNOWN_ERROR; + } + iphone_connection_t new_connection = (iphone_connection_t)malloc(sizeof(struct iphone_connection_int)); + new_connection->type = CONNECTION_USBMUXD; + new_connection->data = (void*)sfd; + *connection = new_connection; + return IPHONE_E_SUCCESS; + } else { + log_debug_msg("%s: Unknown connection type %d\n", __func__, device->conn_type); + } + + return IPHONE_E_UNKNOWN_ERROR; +} + +/** + * Disconnect from the device and clean up the connection structure. + * + * @param connection The connection to close. + * + * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + */ +iphone_error_t iphone_device_disconnect(iphone_connection_t connection) +{ + if (!connection) { + return IPHONE_E_INVALID_ARG; + } + iphone_error_t result = IPHONE_E_UNKNOWN_ERROR; + if (connection->type == CONNECTION_USBMUXD) { + usbmuxd_disconnect((int)(connection->data)); + result = IPHONE_E_SUCCESS; + } else { + log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); + } + free(connection); + return result; +} + +/** + * Send data to a device via the given connection. + * + * @param connection The connection to send data over. + * @param data Buffer with data to send. + * @param len Size of the buffer to send. + * @param sent_bytes Pointer to an uint32_t that will be filled + * with the number of bytes actually sent. + * + * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + */ +iphone_error_t iphone_device_send(iphone_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) +{ + if (!connection || !data) { + return IPHONE_E_INVALID_ARG; + } + + if (connection->type == CONNECTION_USBMUXD) { + int res = usbmuxd_send((int)(connection->data), data, len, sent_bytes); + if (res < 0) { + log_debug_msg("%s: ERROR: usbmuxd_send returned %d (%s)\n", __func__, res, strerror(-res)); + return IPHONE_E_UNKNOWN_ERROR; + } + return IPHONE_E_SUCCESS; + } else { + log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); + } + return IPHONE_E_UNKNOWN_ERROR; +} + +/** + * Receive data from a device via the given connection. + * This function will return after the given timeout even if no data has been + * received. + * + * @param connection The connection to receive data from. + * @param data Buffer that will be filled with the received data. + * This buffer has to be large enough to hold len bytes. + * @param len Buffer size or number of bytes to receive. + * @param recv_bytes Number of bytes actually received. + * @param timeout Timeout in milliseconds after which this function should + * return even if no data has been received. + * + * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + */ +iphone_error_t iphone_device_recv_timeout(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) +{ + if (!connection) { + return IPHONE_E_INVALID_ARG; + } + + if (connection->type == CONNECTION_USBMUXD) { + int res = usbmuxd_recv_timeout((int)(connection->data), data, len, recv_bytes, timeout); + if (res < 0) { + log_debug_msg("%s: ERROR: usbmuxd_recv_timeout returned %d (%s)\n", __func__, res, strerror(-res)); + return IPHONE_E_UNKNOWN_ERROR; + } + } else { + log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); + } + return IPHONE_E_UNKNOWN_ERROR; +} + +/** + * Receive data from a device via the given connection. + * This function is like iphone_device_recv_timeout, but with a predefined + * reasonable timeout. + * + * @param connection The connection to receive data from. + * @param data Buffer that will be filled with the received data. + * This buffer has to be large enough to hold len bytes. + * @param len Buffer size or number of bytes to receive. + * @param recv_bytes Number of bytes actually received. + * + * @return IPHONE_E_SUCCESS if ok, otherwise an error code. + */ +iphone_error_t iphone_device_recv(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) +{ + if (!connection) { + return -EINVAL; + } + + if (connection->type == CONNECTION_USBMUXD) { + int res = usbmuxd_recv((int)(connection->data), data, len, recv_bytes); + if (res < 0) { + log_debug_msg("%s: ERROR: usbmuxd_recv returned %d (%s)\n", __func__, res, strerror(-res)); + return IPHONE_E_UNKNOWN_ERROR; + } + } else { + log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); + } + return IPHONE_E_UNKNOWN_ERROR; +} + +iphone_error_t iphone_device_get_handle(iphone_device_t device, uint32_t *handle) +{ + if (!device) + return IPHONE_E_INVALID_ARG; + + if (device->conn_type == CONNECTION_USBMUXD) { + *handle = (uint32_t)device->conn_data; + return IPHONE_E_SUCCESS; + } else { + log_debug_msg("%s: Unknown connection type %d\n", __func__, device->conn_type); + } + return IPHONE_E_UNKNOWN_ERROR; +} + +iphone_error_t iphone_device_get_uuid(iphone_device_t device, char **uuid) +{ + if (!device) + return IPHONE_E_INVALID_ARG; + + *uuid = strdup(device->uuid); + return IPHONE_E_SUCCESS; +} + -- cgit v1.1-32-gdbae