summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--device.c56
-rw-r--r--device.h29
-rw-r--r--log.c18
-rw-r--r--log.h14
-rw-r--r--main.c66
-rw-r--r--usb-linux.c251
-rw-r--r--usb.h18
-rw-r--r--utils.c6
-rw-r--r--utils.h16
10 files changed, 440 insertions, 36 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 81a6965..acbc779 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,6 +11,6 @@ set(LIBS ${LIBS} ${USB_LIBRARIES})
#set(CMAKE_VERBOSE_MAKEFILE ON)
add_definitions(-Wall -O2)
-add_executable(usbmuxd main.c usb-linux.c log.c utils.c)
+add_executable(usbmuxd main.c usb-linux.c log.c utils.c device.c)
target_link_libraries(usbmuxd ${LIBS})
diff --git a/device.c b/device.c
new file mode 100644
index 0000000..659f4ae
--- /dev/null
+++ b/device.c
@@ -0,0 +1,56 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include "device.h"
+#include "usb.h"
+#include "log.h"
+
+int device_id;
+/*
+int get_next_device_id(void)
+{
+ int i;
+ while(1) {
+ for(i=0; i<num_devs; i++) {
+ if(device_list[i].dev && device_list[i].id == device_id) {
+ device_id++;
+ break;
+ }
+ }
+ if(i < num_devs)
+ break;
+ }
+ return device_id++;
+}
+*/
+void device_add(struct usb_device *dev)
+{
+ usbmuxd_log(LL_NOTICE, "Connected to new device on location 0x%x with serial number %s", usb_get_location(dev), usb_get_serial(dev));
+}
+
+void device_remove(struct usb_device *dev)
+{
+ usbmuxd_log(LL_NOTICE, "Removed device on location 0x%x with serial number %s", usb_get_location(dev), usb_get_serial(dev));
+}
diff --git a/device.h b/device.h
new file mode 100644
index 0000000..11beaea
--- /dev/null
+++ b/device.h
@@ -0,0 +1,29 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef __DEVICE_H__
+#define __DEVICE_H__
+
+#include "usb.h"
+
+void device_add(struct usb_device *dev);
+void device_remove(struct usb_device *dev);
+
+#endif
diff --git a/log.c b/log.c
index d7f57df..29b3506 100644
--- a/log.c
+++ b/log.c
@@ -26,25 +26,33 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
+#include <time.h>
+#include <sys/time.h>
#include "log.h"
-int log_level = LOG_SPEW;
+int log_level = LL_SPEW;
void usbmuxd_log(enum loglevel level, const char *fmt, ...)
{
va_list ap;
char *fs;
+ struct timeval ts;
+ struct tm *tp;
- if(level < log_level)
+ gettimeofday(&ts, NULL);
+ tp = localtime(&ts.tv_sec);
+
+ if(level > log_level)
return;
- fs = malloc(10 + strlen(fmt));
- sprintf(fs, "[%d] %s\n", level, fmt);
+ fs = malloc(20 + strlen(fmt));
+ strftime(fs, 10, "[%H:%M:%S", tp);
+ sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
va_start(ap, fmt);
vfprintf(stderr, fs, ap);
va_end(ap);
free(fs);
-} \ No newline at end of file
+}
diff --git a/log.h b/log.h
index fda2e1a..56ecbf4 100644
--- a/log.h
+++ b/log.h
@@ -22,13 +22,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define __LOG_H__
enum loglevel {
- LOG_SPEW = 0,
- LOG_DEBUG,
- LOG_INFO,
- LOG_NOTICE,
- LOG_WARNING,
- LOG_ERROR,
- LOG_FATAL,
+ LL_FATAL = 0,
+ LL_ERROR,
+ LL_WARNING,
+ LL_NOTICE,
+ LL_INFO,
+ LL_DEBUG,
+ LL_SPEW,
};
extern int log_level;
diff --git a/main.c b/main.c
index c2467a2..6773d0e 100644
--- a/main.c
+++ b/main.c
@@ -42,13 +42,13 @@ int create_socket(void) {
int listenfd;
if(unlink(socket_path) == -1 && errno != ENOENT) {
- usbmuxd_log(LOG_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
+ usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
return -1;
}
listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (listenfd == -1) {
- usbmuxd_log(LOG_FATAL, "socket() failed: %s", strerror(errno));
+ usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
return -1;
}
@@ -56,37 +56,85 @@ int create_socket(void) {
bind_addr.sun_family = AF_UNIX;
strcpy(bind_addr.sun_path, socket_path);
if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
- usbmuxd_log(LOG_FATAL, "bind() failed: %s", strerror(errno));
+ usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
return -1;
}
// Start listening
if (listen(listenfd, 5) != 0) {
- usbmuxd_log(LOG_FATAL, "listen() failed: %s", strerror(errno));
+ usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
return -1;
}
return listenfd;
}
+int main_loop(int listenfd)
+{
+ int to, cnt, i;
+ struct fdlist pollfds;
+
+ while(1) {
+ usbmuxd_log(LL_SPEW, "main_loop iteration");
+ to = usb_get_timeout();
+ usbmuxd_log(LL_SPEW, "USB timeout is %d ms", to);
+
+ fdlist_create(&pollfds);
+ usb_get_fds(&pollfds);
+ usbmuxd_log(LL_SPEW, "fd count is %d", pollfds.count);
+
+ cnt = poll(pollfds.fds, pollfds.count, to);
+ usbmuxd_log(LL_SPEW, "poll() returned %d", cnt);
+
+ if(cnt == 0) {
+ if(usb_process() < 0) {
+ usbmuxd_log(LL_FATAL, "usb_process() failed");
+ return -1;
+ }
+ } else {
+ for(i=0; i<pollfds.count; i++) {
+ if(pollfds.fds[i].revents) {
+ if(pollfds.owners[i] == FD_USB) {
+ if(usb_process() < 0) {
+ usbmuxd_log(LL_FATAL, "usb_process() failed");
+ return -1;
+ }
+ }
+ }
+ }
+ }
+ fdlist_free(&pollfds);
+ }
+}
+
int main(int argc, char *argv[])
{
int listenfd;
int res;
- usbmuxd_log(LOG_NOTICE, "usbmux v0.1 starting up");
+ usbmuxd_log(LL_NOTICE, "usbmux v0.1 starting up");
- usbmuxd_log(LOG_INFO, "Creating socket");
+ usbmuxd_log(LL_INFO, "Creating socket");
listenfd = create_socket();
if(listenfd < 0)
return 1;
- usbmuxd_log(LOG_INFO, "Initializing USB");
+ usbmuxd_log(LL_INFO, "Initializing USB");
if((res = usb_init()) < 0)
return 2;
- usbmuxd_log(LOG_INFO, "%d device%s detected", res, (res==1)?"":"s");
+ usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
- usbmuxd_log(LOG_NOTICE, "initialization complete");
+ usbmuxd_log(LL_NOTICE, "Initialization complete");
+
+ res = main_loop(listenfd);
+ if(res < 0)
+ usbmuxd_log(LL_FATAL, "main_loop failed");
+
+ usbmuxd_log(LL_NOTICE, "usbmux shutting down");
+ usb_shutdown();
+ usbmuxd_log(LL_NOTICE, "Shutdown complete");
+ if(res < 0)
+ return -res;
return 0;
}
diff --git a/usb-linux.c b/usb-linux.c
index 221ce33..0820ed9 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -24,11 +24,258 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdio.h>
#include <stdlib.h>
-#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
#include <libusb.h>
-int usb_init(void)
+#include "usb.h"
+#include "log.h"
+#include "device.h"
+
+// interval for device connection/disconnection polling, in milliseconds
+// we need this because there is currently no asynchronous device discovery mechanism in libusb
+#define DEVICE_POLL_TIME 1000
+
+struct usb_device {
+ libusb_device_handle *dev;
+ uint8_t bus, address;
+ char serial[256];
+ int alive;
+};
+
+int num_devs;
+int device_id;
+struct usb_device *device_list;
+
+struct timeval next_dev_poll_time;
+
+static int alloc_device(void)
{
+ int i;
+ for(i=0; i<num_devs; i++) {
+ if(!device_list[i].dev)
+ return i;
+ }
+ num_devs++;
+ device_list = realloc(device_list, sizeof(*device_list) * num_devs);
+ memset(&device_list[num_devs-1], 0, sizeof(*device_list));
+ return num_devs - 1;
+}
+
+static void usb_disconnect(struct usb_device *dev)
+{
+ if(!dev->dev) {
+ return;
+ }
+ libusb_release_interface(dev->dev, USB_INTERFACE);
+ libusb_close(dev->dev);
+ dev->dev = NULL;
+}
+
+static int usb_discover(void)
+{
+ int cnt, i, j, res;
+ int valid_count = 0;
+ libusb_device **devs;
+
+ cnt = libusb_get_device_list(NULL, &devs);
+ if(cnt < 0) {
+ usbmuxd_log(LL_FATAL, "Could not get device list: %d", cnt);
+ return cnt;
+ }
+
+ usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
+
+ for(j=0; j<num_devs; j++) {
+ device_list[j].alive = 0;
+ }
+ for(i=0; i<cnt; i++) {
+ // the following are non-blocking operations on the device list
+ libusb_device *dev = devs[i];
+ uint8_t bus = libusb_get_bus_number(dev);
+ uint8_t address = libusb_get_device_address(dev);
+ struct libusb_device_descriptor devdesc;
+ for(j=0; j<num_devs; j++) {
+ if(device_list[j].dev && device_list[j].bus == bus && device_list[j].address == address) {
+ valid_count++;
+ device_list[j].alive = 1;
+ break;
+ }
+ }
+ if(j < num_devs)
+ continue; //device already found
+ if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res);
+ continue;
+ }
+ if(devdesc.idVendor != VID_APPLE)
+ continue;
+ if( (devdesc.idProduct != PID_IPHONE2G) &&
+ (devdesc.idProduct != PID_ITOUCH1G) &&
+ (devdesc.idProduct != PID_IPHONE3G))
+ continue;
+ libusb_device_handle *handle;
+ usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
+ // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable
+ if((res = libusb_open(dev, &handle)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res);
+ continue;
+ }
+ if((res = libusb_set_configuration(handle, USB_CONFIGURATION)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", USB_CONFIGURATION, bus, address, res);
+ libusb_close(handle);
+ continue;
+ }
+ if((res = libusb_claim_interface(handle, USB_INTERFACE)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", USB_INTERFACE, bus, address, res);
+ libusb_close(handle);
+ continue;
+ }
+ int idx = alloc_device();
+
+ if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)device_list[idx].serial, 256)) <= 0) {
+ usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", USB_INTERFACE, bus, address, res);
+ libusb_close(handle);
+ continue;
+ }
+ device_list[idx].serial[res] = 0;
+ device_list[idx].bus = bus;
+ device_list[idx].address = address;
+ device_list[idx].dev = handle;
+ device_list[idx].alive = 1;
+
+ device_add(&device_list[idx]);
+ valid_count++;
+ }
+ for(j=0; j<num_devs; j++) {
+ if(device_list[j].dev && !device_list[j].alive) {
+ device_remove(&device_list[j]);
+ usb_disconnect(&device_list[j]);
+ }
+ }
+ libusb_free_device_list(devs, 1);
+
+ gettimeofday(&next_dev_poll_time, NULL);
+ next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
+ next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
+ next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
+
+ return valid_count;
+}
+
+const char *usb_get_serial(struct usb_device *dev)
+{
+ if(!dev->dev) {
+ return NULL;
+ }
+ return dev->serial;
+}
+
+int usb_get_location(struct usb_device *dev)
+{
+ if(!dev->dev) {
+ return 0;
+ }
+ return (dev->bus << 16) | dev->address;
+}
+
+void usb_get_fds(struct fdlist *list)
+{
+ const struct libusb_pollfd **usbfds;
+ const struct libusb_pollfd **p;
+ usbfds = libusb_get_pollfds(NULL);
+ if(!usbfds) {
+ usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
+ return;
+ }
+ p = usbfds;
+ while(*p) {
+ fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
+ p++;
+ }
+ free(usbfds);
+}
+
+static int dev_poll_remain_ms(void)
+{
+ int msecs;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
+ msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
+ if(msecs < 0)
+ return 0;
+ return msecs;
+}
+
+int usb_get_timeout(void)
+{
+ struct timeval tv;
+ int msec;
+ int res;
+ int pollrem;
+ pollrem = dev_poll_remain_ms();
+ res = libusb_get_next_timeout(NULL, &tv);
+ if(res == 0)
+ return pollrem;
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res);
+ return pollrem;
+ }
+ msec = tv.tv_sec * 1000;
+ msec += tv.tv_usec / 1000;
+ if(msec > pollrem)
+ return pollrem;
+ return msec;
+}
+
+int usb_process(void)
+{
+ int res;
+ struct timeval tv;
+ tv.tv_sec = tv.tv_usec = 0;
+ res = libusb_handle_events_timeout(NULL, &tv);
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
+ return res;
+ }
+ if(dev_poll_remain_ms() <= 0) {
+ res = usb_discover();
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res);
+ return res;
+ }
+ }
return 0;
}
+
+int usb_init(void)
+{
+ int res;
+ usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
+
+ res = libusb_init(NULL);
+ if(res != 0) {
+ usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
+ return -1;
+ }
+
+ device_id = 1;
+ num_devs = 1;
+ device_list = malloc(sizeof(*device_list) * num_devs);
+ memset(device_list, 0, sizeof(*device_list) * num_devs);
+
+ return usb_discover();
+}
+
+void usb_shutdown(void)
+{
+ int i;
+ usbmuxd_log(LL_DEBUG, "usb_shutdown");
+ for(i=0; i<num_devs; i++)
+ usb_disconnect(&device_list[i]);
+ free(device_list);
+ device_list = NULL;
+ libusb_exit(NULL);
+}
diff --git a/usb.h b/usb.h
index cf25c3a..25243d8 100644
--- a/usb.h
+++ b/usb.h
@@ -21,9 +21,27 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#ifndef __USB_H__
#define __USB_H__
+#include "utils.h"
+
#define BULK_IN 0x85
#define BULK_OUT 0x04
+#define VID_APPLE 0x5ac
+#define PID_IPHONE2G 0x1290
+#define PID_ITOUCH1G 0x1291
+#define PID_IPHONE3G 0x1292
+
+#define USB_CONFIGURATION 3
+#define USB_INTERFACE 1
+
+struct usb_device;
+
int usb_init(void);
+void usb_shutdown(void);
+const char *usb_get_serial(struct usb_device *dev);
+int usb_get_location(struct usb_device *dev);
+void usb_get_fds(struct fdlist *list);
+int usb_get_timeout(void);
+int usb_process(void);
#endif
diff --git a/utils.c b/utils.c
index c3d5c50..41c3bcb 100644
--- a/utils.c
+++ b/utils.c
@@ -25,14 +25,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdlib.h>
#include "utils.h"
-void fdlist_create(fdlist *list)
+void fdlist_create(struct fdlist *list)
{
list->count = 0;
list->capacity = 4;
list->owners = malloc(sizeof(*list->owners) * list->capacity);
list->fds = malloc(sizeof(*list->fds) * list->capacity);
}
-void fdlist_add(fdlist *list, enum fdowner owner, int fd, short events)
+void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
{
if(list->count == list->capacity) {
list->capacity *= 2;
@@ -46,7 +46,7 @@ void fdlist_add(fdlist *list, enum fdowner owner, int fd, short events)
list->count++;
}
-void fdlist_free(fdlist *list)
+void fdlist_free(struct fdlist *list)
{
list->count = 0;
list->capacity = 0;
diff --git a/utils.h b/utils.h
index 465487d..9a6d566 100644
--- a/utils.h
+++ b/utils.h
@@ -18,8 +18,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef __LOG_H__
-#define __LOG_H__
+#ifndef __UTILS_H__
+#define __UTILS_H__
#include <poll.h>
@@ -29,19 +29,17 @@ enum fdowner {
FD_USB
};
-typedef struct {
+struct fdlist {
int count;
int capacity;
enum fdowner *owners;
struct pollfd *fds;
-} fdlist;
+};
-void fdlist_create(fdlist *list);
-void fdlist_add(fdlist *list, enum fdowner owner, int fd, short events);
-void fdlist_free(fdlist *list);
+void fdlist_create(struct fdlist *list);
+void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
+void fdlist_free(struct fdlist *list);
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
-
-
#endif