diff options
| author | 2009-09-10 13:28:13 +0200 | |
|---|---|---|
| committer | 2009-09-12 11:41:38 +0200 | |
| commit | 1f6282ffddec7012df82fa929dfe72cfc74b063a (patch) | |
| tree | 452c927c07806855f360ee9803111a1f72e05d3c /src/iphone.c | |
| parent | 26ce10634d277df51c4e9c2bd61b409df3f5b060 (diff) | |
| download | libimobiledevice-1f6282ffddec7012df82fa929dfe72cfc74b063a.tar.gz libimobiledevice-1f6282ffddec7012df82fa929dfe72cfc74b063a.tar.bz2 | |
Public API rework, extension and adaption to latest libusbmuxd-1.0 API.
Diffstat (limited to 'src/iphone.c')
| -rw-r--r-- | src/iphone.c | 371 |
1 files changed, 305 insertions, 66 deletions
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 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * iphone.c | 2 | * iphone.c |
| 3 | * Functions for creating and initializing iPhone structures. | 3 | * Device discovery and communication interface. |
| 4 | * | 4 | * |
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
| 6 | * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. | ||
| 6 | * | 7 | * |
| 7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public | 9 | * modify it under the terms of the GNU Lesser General Public |
| @@ -19,104 +20,161 @@ | |||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | */ | 21 | */ |
| 21 | 22 | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
| 24 | #include <string.h> | 24 | #include <string.h> |
| 25 | #include <errno.h> | ||
| 25 | 26 | ||
| 27 | #include <usbmuxd.h> | ||
| 26 | #include "iphone.h" | 28 | #include "iphone.h" |
| 27 | #include "utils.h" | 29 | #include "utils.h" |
| 28 | 30 | ||
| 31 | static iphone_event_cb_t event_cb = NULL; | ||
| 32 | |||
| 33 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) | ||
| 34 | { | ||
| 35 | iphone_event_t ev; | ||
| 36 | |||
| 37 | ev.event = event->event; | ||
| 38 | ev.uuid = event->device.uuid; | ||
| 39 | ev.conn_type = CONNECTION_USBMUXD; | ||
| 40 | |||
| 41 | if (event_cb) { | ||
| 42 | event_cb(&ev, user_data); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 29 | /** | 46 | /** |
| 30 | * Retrieves a list of connected devices from usbmuxd and matches their | 47 | * Register a callback function that will be called when device add/remove |
| 31 | * UUID with the given UUID. If the given UUID is NULL then the first | 48 | * events occur. |
| 32 | * device reported by usbmuxd is used. | ||
| 33 | * | 49 | * |
| 34 | * @param device Upon calling this function, a pointer to a location of type | 50 | * @param callback Callback function to call. |
| 35 | * iphone_device_t, which must have the value NULL. On return, this location | 51 | * @param user_data Application-specific data passed as parameter |
| 36 | * will be filled with a handle to the device. | 52 | * to the registered callback function. |
| 37 | * @param uuid The UUID to match. | ||
| 38 | * | 53 | * |
| 39 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | 54 | * @return IPHONE_E_SUCCESS on success or an error value when an error occured. |
| 40 | */ | 55 | */ |
| 41 | iphone_error_t iphone_get_device_by_uuid(iphone_device_t * device, const char *uuid) | 56 | iphone_error_t iphone_event_subscribe(iphone_event_cb_t callback, void *user_data) |
| 42 | { | 57 | { |
| 43 | iphone_device_t phone; | 58 | event_cb = callback; |
| 44 | uint32_t handle = 0; | 59 | int res = usbmuxd_subscribe(usbmux_event_cb, user_data); |
| 45 | char *serial_number = malloc(41); | 60 | if (res != 0) { |
| 46 | usbmuxd_scan_result *dev_list = NULL; | 61 | event_cb = NULL; |
| 47 | int i; | 62 | log_debug_msg("%s: Error %d when subscribing usbmux event callback!\n", __func__, res); |
| 48 | 63 | return IPHONE_E_UNKNOWN_ERROR; | |
| 49 | if (usbmuxd_scan(&dev_list) < 0) { | ||
| 50 | log_debug_msg("%s: usbmuxd_scan returned an error, is usbmuxd running?\n", __func__); | ||
| 51 | } | ||
| 52 | if (dev_list && dev_list[0].handle > 0) { | ||
| 53 | if (!uuid) { | ||
| 54 | /* select first device found if no UUID specified */ | ||
| 55 | handle = dev_list[0].handle; | ||
| 56 | strcpy(serial_number, dev_list[0].serial_number); | ||
| 57 | } else { | ||
| 58 | /* otherwise walk through the list */ | ||
| 59 | for (i = 0; dev_list[i].handle > 0; i++) { | ||
| 60 | log_debug_msg("%s: device handle=%d, uuid=%s\n", __func__, dev_list[i].handle, dev_list[i].serial_number); | ||
| 61 | if (strcasecmp(uuid, dev_list[i].serial_number) == 0) { | ||
| 62 | handle = dev_list[i].handle; | ||
| 63 | strcpy(serial_number, dev_list[i].serial_number); | ||
| 64 | break; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | free(dev_list); | ||
| 69 | |||
| 70 | if (handle > 0) { | ||
| 71 | phone = (iphone_device_t) malloc(sizeof(struct iphone_device_int)); | ||
| 72 | phone->handle = handle; | ||
| 73 | phone->serial_number = serial_number; | ||
| 74 | *device = phone; | ||
| 75 | return IPHONE_E_SUCCESS; | ||
| 76 | } | ||
| 77 | } | 64 | } |
| 65 | return IPHONE_E_SUCCESS; | ||
| 66 | } | ||
| 78 | 67 | ||
| 79 | return IPHONE_E_NO_DEVICE; | 68 | /** |
| 69 | * Release the event callback function that has been registered with | ||
| 70 | * iphone_event_subscribe(). | ||
| 71 | * | ||
| 72 | * @return IPHONE_E_SUCCESS on success or an error value when an error occured. | ||
| 73 | */ | ||
| 74 | iphone_error_t iphone_event_unsubscribe() | ||
| 75 | { | ||
| 76 | event_cb = NULL; | ||
| 77 | int res = usbmuxd_unsubscribe(); | ||
| 78 | if (res != 0) { | ||
| 79 | log_debug_msg("%s: Error %d when unsubscribing usbmux event callback!\n", __func__, res); | ||
| 80 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 81 | } | ||
| 82 | return IPHONE_E_SUCCESS; | ||
| 80 | } | 83 | } |
| 81 | 84 | ||
| 82 | /** | 85 | /** |
| 83 | * This function has the purpose to retrieve a handle to the first | 86 | * Get a list of currently available devices. |
| 84 | * attached iPhone/iPod reported by usbmuxd. | ||
| 85 | * | 87 | * |
| 86 | * @param Upon calling this function, a pointer to a location of type | 88 | * @param devices List of uuids of devices that are currently available. |
| 87 | * iphone_device_t, which must have the value NULL. On return, this location | 89 | * This list is terminated by a NULL pointer. |
| 88 | * will be filled with a handle to the device. | 90 | * @param count Number of devices found. |
| 89 | * | 91 | * |
| 90 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | 92 | * @return IPHONE_E_SUCCESS on success or an error value when an error occured. |
| 91 | */ | 93 | */ |
| 92 | iphone_error_t iphone_get_device(iphone_device_t * device) | 94 | iphone_error_t iphone_get_device_list(char ***devices, int *count) |
| 93 | { | 95 | { |
| 94 | return iphone_get_device_by_uuid(device, NULL); | 96 | usbmuxd_device_info_t *dev_list; |
| 97 | |||
| 98 | *devices = NULL; | ||
| 99 | *count = 0; | ||
| 100 | |||
| 101 | if (usbmuxd_get_device_list(&dev_list) < 0) { | ||
| 102 | log_debug_msg("%s: ERROR: usbmuxd is not running!\n", __func__); | ||
| 103 | return IPHONE_E_NO_DEVICE; | ||
| 104 | } | ||
| 105 | |||
| 106 | char **newlist = NULL; | ||
| 107 | int i, newcount = 0; | ||
| 108 | |||
| 109 | for (i = 0; dev_list[i].handle > 0; i++) { | ||
| 110 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | ||
| 111 | newlist[newcount++] = strdup(dev_list[i].uuid); | ||
| 112 | *devices = newlist; | ||
| 113 | } | ||
| 114 | usbmuxd_free_device_list(dev_list); | ||
| 115 | |||
| 116 | *count = newcount; | ||
| 117 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | ||
| 118 | newlist[newcount] = NULL; | ||
| 119 | *devices = newlist; | ||
| 120 | |||
| 121 | return IPHONE_E_SUCCESS; | ||
| 95 | } | 122 | } |
| 96 | 123 | ||
| 97 | iphone_error_t iphone_device_get_handle(iphone_device_t device, uint32_t *handle) | 124 | /** |
| 125 | * Free a list of device uuids. | ||
| 126 | * | ||
| 127 | * @param devices List of uuids to free. | ||
| 128 | * | ||
| 129 | * @return Always returnes IPHONE_E_SUCCESS. | ||
| 130 | */ | ||
| 131 | iphone_error_t iphone_free_device_list(char **devices) | ||
| 98 | { | 132 | { |
| 99 | if (!device) | 133 | if (devices) { |
| 100 | return IPHONE_E_INVALID_ARG; | 134 | int i = 0; |
| 101 | 135 | while (devices[i++]) { | |
| 102 | *handle = device->handle; | 136 | free(devices[i]); |
| 137 | } | ||
| 138 | free(devices); | ||
| 139 | } | ||
| 103 | return IPHONE_E_SUCCESS; | 140 | return IPHONE_E_SUCCESS; |
| 104 | } | 141 | } |
| 105 | 142 | ||
| 106 | iphone_error_t iphone_device_get_uuid(iphone_device_t device, char **uuid) | 143 | /** |
| 144 | * Creates an iphone_device_t structure for the device specified by uuid, | ||
| 145 | * if the device is available. | ||
| 146 | * | ||
| 147 | * @note The resulting iphone_device_t structure has to be freed with | ||
| 148 | * iphone_device_free() if it is no longer used. | ||
| 149 | * | ||
| 150 | * @param device Upon calling this function, a pointer to a location of type | ||
| 151 | * iphone_device_t. On successful return, this location will be populated. | ||
| 152 | * @param uuid The UUID to match. | ||
| 153 | * | ||
| 154 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 155 | */ | ||
| 156 | iphone_error_t iphone_device_new(iphone_device_t * device, const char *uuid) | ||
| 107 | { | 157 | { |
| 108 | if (!device) | 158 | usbmuxd_device_info_t muxdev; |
| 109 | return IPHONE_E_INVALID_ARG; | 159 | int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); |
| 160 | if (res > 0) { | ||
| 161 | iphone_device_t phone = (iphone_device_t) malloc(sizeof(struct iphone_device_int)); | ||
| 162 | phone->uuid = strdup(muxdev.uuid); | ||
| 163 | phone->conn_type = CONNECTION_USBMUXD; | ||
| 164 | phone->conn_data = (void*)muxdev.handle; | ||
| 165 | *device = phone; | ||
| 166 | return IPHONE_E_SUCCESS; | ||
| 167 | } | ||
| 168 | /* other connection types could follow here */ | ||
| 110 | 169 | ||
| 111 | *uuid = strdup(device->serial_number); | 170 | return IPHONE_E_NO_DEVICE; |
| 112 | return IPHONE_E_SUCCESS; | ||
| 113 | } | 171 | } |
| 114 | 172 | ||
| 115 | /** Cleans up an iPhone structure, then frees the structure itself. | 173 | /** Cleans up an iPhone structure, then frees the structure itself. |
| 116 | * This is a library-level function; deals directly with the iPhone to tear | 174 | * This is a library-level function; deals directly with the iPhone to tear |
| 117 | * down relations, but otherwise is mostly internal. | 175 | * down relations, but otherwise is mostly internal. |
| 118 | * | 176 | * |
| 119 | * @param phone A pointer to an iPhone structure. | 177 | * @param device A pointer to an iPhone structure. |
| 120 | */ | 178 | */ |
| 121 | iphone_error_t iphone_device_free(iphone_device_t device) | 179 | iphone_error_t iphone_device_free(iphone_device_t device) |
| 122 | { | 180 | { |
| @@ -126,8 +184,189 @@ iphone_error_t iphone_device_free(iphone_device_t device) | |||
| 126 | 184 | ||
| 127 | ret = IPHONE_E_SUCCESS; | 185 | ret = IPHONE_E_SUCCESS; |
| 128 | 186 | ||
| 129 | free(device->serial_number); | 187 | free(device->uuid); |
| 188 | |||
| 189 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
| 190 | device->conn_data = 0; | ||
| 191 | } | ||
| 192 | if (device->conn_data) { | ||
| 193 | free(device->conn_data); | ||
| 194 | } | ||
| 130 | free(device); | 195 | free(device); |
| 131 | return ret; | 196 | return ret; |
| 132 | } | 197 | } |
| 133 | 198 | ||
| 199 | /** | ||
| 200 | * Set up a connection to the given device. | ||
| 201 | * | ||
| 202 | * @param device The device to connect to. | ||
| 203 | * @param dst_port The destination port to connect to. | ||
| 204 | * @param connection Pointer to an iphone_connection_t that will be filled | ||
| 205 | * with the necessary data of the connection. | ||
| 206 | * | ||
| 207 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 208 | */ | ||
| 209 | iphone_error_t iphone_device_connect(iphone_device_t device, uint16_t dst_port, iphone_connection_t *connection) | ||
| 210 | { | ||
| 211 | if (!device) { | ||
| 212 | return IPHONE_E_INVALID_ARG; | ||
| 213 | } | ||
| 214 | |||
| 215 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
| 216 | int sfd = usbmuxd_connect((uint32_t)(device->conn_data), dst_port); | ||
| 217 | if (sfd < 0) { | ||
| 218 | log_debug_msg("%s: ERROR: Connecting to usbmuxd failed: %d (%s)\n", __func__, sfd, strerror(-sfd)); | ||
| 219 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 220 | } | ||
| 221 | iphone_connection_t new_connection = (iphone_connection_t)malloc(sizeof(struct iphone_connection_int)); | ||
| 222 | new_connection->type = CONNECTION_USBMUXD; | ||
| 223 | new_connection->data = (void*)sfd; | ||
| 224 | *connection = new_connection; | ||
| 225 | return IPHONE_E_SUCCESS; | ||
| 226 | } else { | ||
| 227 | log_debug_msg("%s: Unknown connection type %d\n", __func__, device->conn_type); | ||
| 228 | } | ||
| 229 | |||
| 230 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * Disconnect from the device and clean up the connection structure. | ||
| 235 | * | ||
| 236 | * @param connection The connection to close. | ||
| 237 | * | ||
| 238 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 239 | */ | ||
| 240 | iphone_error_t iphone_device_disconnect(iphone_connection_t connection) | ||
| 241 | { | ||
| 242 | if (!connection) { | ||
| 243 | return IPHONE_E_INVALID_ARG; | ||
| 244 | } | ||
| 245 | iphone_error_t result = IPHONE_E_UNKNOWN_ERROR; | ||
| 246 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 247 | usbmuxd_disconnect((int)(connection->data)); | ||
| 248 | result = IPHONE_E_SUCCESS; | ||
| 249 | } else { | ||
| 250 | log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); | ||
| 251 | } | ||
| 252 | free(connection); | ||
| 253 | return result; | ||
| 254 | } | ||
| 255 | |||
| 256 | /** | ||
| 257 | * Send data to a device via the given connection. | ||
| 258 | * | ||
| 259 | * @param connection The connection to send data over. | ||
| 260 | * @param data Buffer with data to send. | ||
| 261 | * @param len Size of the buffer to send. | ||
| 262 | * @param sent_bytes Pointer to an uint32_t that will be filled | ||
| 263 | * with the number of bytes actually sent. | ||
| 264 | * | ||
| 265 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 266 | */ | ||
| 267 | iphone_error_t iphone_device_send(iphone_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) | ||
| 268 | { | ||
| 269 | if (!connection || !data) { | ||
| 270 | return IPHONE_E_INVALID_ARG; | ||
| 271 | } | ||
| 272 | |||
| 273 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 274 | int res = usbmuxd_send((int)(connection->data), data, len, sent_bytes); | ||
| 275 | if (res < 0) { | ||
| 276 | log_debug_msg("%s: ERROR: usbmuxd_send returned %d (%s)\n", __func__, res, strerror(-res)); | ||
| 277 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 278 | } | ||
| 279 | return IPHONE_E_SUCCESS; | ||
| 280 | } else { | ||
| 281 | log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); | ||
| 282 | } | ||
| 283 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 284 | } | ||
| 285 | |||
| 286 | /** | ||
| 287 | * Receive data from a device via the given connection. | ||
| 288 | * This function will return after the given timeout even if no data has been | ||
| 289 | * received. | ||
| 290 | * | ||
| 291 | * @param connection The connection to receive data from. | ||
| 292 | * @param data Buffer that will be filled with the received data. | ||
| 293 | * This buffer has to be large enough to hold len bytes. | ||
| 294 | * @param len Buffer size or number of bytes to receive. | ||
| 295 | * @param recv_bytes Number of bytes actually received. | ||
| 296 | * @param timeout Timeout in milliseconds after which this function should | ||
| 297 | * return even if no data has been received. | ||
| 298 | * | ||
| 299 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 300 | */ | ||
| 301 | iphone_error_t iphone_device_recv_timeout(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) | ||
| 302 | { | ||
| 303 | if (!connection) { | ||
| 304 | return IPHONE_E_INVALID_ARG; | ||
| 305 | } | ||
| 306 | |||
| 307 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 308 | int res = usbmuxd_recv_timeout((int)(connection->data), data, len, recv_bytes, timeout); | ||
| 309 | if (res < 0) { | ||
| 310 | log_debug_msg("%s: ERROR: usbmuxd_recv_timeout returned %d (%s)\n", __func__, res, strerror(-res)); | ||
| 311 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 312 | } | ||
| 313 | } else { | ||
| 314 | log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); | ||
| 315 | } | ||
| 316 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * Receive data from a device via the given connection. | ||
| 321 | * This function is like iphone_device_recv_timeout, but with a predefined | ||
| 322 | * reasonable timeout. | ||
| 323 | * | ||
| 324 | * @param connection The connection to receive data from. | ||
| 325 | * @param data Buffer that will be filled with the received data. | ||
| 326 | * This buffer has to be large enough to hold len bytes. | ||
| 327 | * @param len Buffer size or number of bytes to receive. | ||
| 328 | * @param recv_bytes Number of bytes actually received. | ||
| 329 | * | ||
| 330 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 331 | */ | ||
| 332 | iphone_error_t iphone_device_recv(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) | ||
| 333 | { | ||
| 334 | if (!connection) { | ||
| 335 | return -EINVAL; | ||
| 336 | } | ||
| 337 | |||
| 338 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 339 | int res = usbmuxd_recv((int)(connection->data), data, len, recv_bytes); | ||
| 340 | if (res < 0) { | ||
| 341 | log_debug_msg("%s: ERROR: usbmuxd_recv returned %d (%s)\n", __func__, res, strerror(-res)); | ||
| 342 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 343 | } | ||
| 344 | } else { | ||
| 345 | log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); | ||
| 346 | } | ||
| 347 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 348 | } | ||
| 349 | |||
| 350 | iphone_error_t iphone_device_get_handle(iphone_device_t device, uint32_t *handle) | ||
| 351 | { | ||
| 352 | if (!device) | ||
| 353 | return IPHONE_E_INVALID_ARG; | ||
| 354 | |||
| 355 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
| 356 | *handle = (uint32_t)device->conn_data; | ||
| 357 | return IPHONE_E_SUCCESS; | ||
| 358 | } else { | ||
| 359 | log_debug_msg("%s: Unknown connection type %d\n", __func__, device->conn_type); | ||
| 360 | } | ||
| 361 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 362 | } | ||
| 363 | |||
| 364 | iphone_error_t iphone_device_get_uuid(iphone_device_t device, char **uuid) | ||
| 365 | { | ||
| 366 | if (!device) | ||
| 367 | return IPHONE_E_INVALID_ARG; | ||
| 368 | |||
| 369 | *uuid = strdup(device->uuid); | ||
| 370 | return IPHONE_E_SUCCESS; | ||
| 371 | } | ||
| 372 | |||
