diff options
| -rw-r--r-- | client.c | 1 | ||||
| -rw-r--r-- | client.h | 7 | ||||
| -rw-r--r-- | device.c | 225 | ||||
| -rw-r--r-- | device.h | 6 | ||||
| -rw-r--r-- | log.h | 4 | ||||
| -rw-r--r-- | main.c | 15 | ||||
| -rw-r--r-- | usb-linux.c | 152 | ||||
| -rw-r--r-- | usb.h | 3 | 
8 files changed, 384 insertions, 29 deletions
| @@ -24,4 +24,5 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  #include "log.h"  #include "usb.h" +#include "client.h" @@ -18,7 +18,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  */ -#ifndef __MUX_H__ -#define __MUX_H__ +#ifndef __CLIENT_H__ +#define __CLIENT_H__  #endif + +void client_accept(int fd); +void client_get_fds(struct fdlist *list); @@ -18,39 +18,242 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  */ +#define _BSD_SOURCE +  #ifdef HAVE_CONFIG_H  #include <config.h>  #endif +#include <netinet/in.h> +#include <netinet/tcp.h>  #include <stdlib.h> +#include <string.h>  #include "device.h"  #include "usb.h"  #include "log.h" -int device_id; -/* -int get_next_device_id(void) +int next_device_id; + +enum mux_protocol { +	MUX_PROTO_VERSION = 0, +	MUX_PROTO_TCP = IPPROTO_TCP, +}; + +enum mux_dev_state { +	MUXDEV_INIT, +	MUXDEV_ACTIVE, +	MUXDEV_DEAD +}; + +struct mux_header +{ +	uint32_t protocol; +	uint32_t length; +}; + +struct version_header +{ +	uint32_t major; +	uint32_t minor; +	uint32_t padding; +}; + +struct mux_device +{ +	struct usb_device *usbdev; +	int id; +	enum mux_dev_state state; +}; + +static int num_devs; +static struct mux_device *device_list; + +static int alloc_device(void) +{ +	int i; +	for(i=0; i<num_devs; i++) { +		if(!device_list[i].usbdev) +			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 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++; +			if(device_list[i].usbdev && device_list[i].id == next_device_id) { +				next_device_id++;  				break;  			}  		} -		if(i < num_devs) +		if(i >= num_devs) +			return next_device_id++; +	} +} + +int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, void *data, int length) +{ +	unsigned char *buffer; +	int hdrlen; +	int res; +	 +	switch(proto) { +		case MUX_PROTO_VERSION: +			hdrlen = sizeof(struct version_header);  			break; +		case MUX_PROTO_TCP: +			hdrlen = sizeof(struct tcphdr); +			break; +		default: +			usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length); +			return -1; +	} +	usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length); +	 +	int total = sizeof(struct mux_header) + hdrlen + length; +	 +	if(total > USB_MTU) { +		usbmuxd_log(LL_ERROR, "Tried to send packet larger than USB MTU (hdr %d data %d total %d) to device %d", hdrlen, length, total, dev->id); +		return -1;  	} -	return device_id++; +	 +	buffer = malloc(total); +	struct mux_header *mhdr = (struct mux_header *)buffer; +	mhdr->protocol = htonl(proto); +	mhdr->length = htonl(total);; +	memcpy(buffer + sizeof(struct mux_header), header, hdrlen); +	if(data && length) +		memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length); +	 +	if((res = usb_send(dev->usbdev, buffer, total)) < 0) { +		usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res); +		free(buffer); +		return res; +	} +	return mhdr->length;  } -*/ -void device_add(struct usb_device *dev) + +static void device_version_input(struct mux_device *dev, struct version_header *vh) +{ +	if(dev->state != MUXDEV_INIT) { +		usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id); +		return; +	} +	vh->major = ntohl(vh->major); +	vh->minor = ntohl(vh->minor); +	if(vh->major != 1 || vh->minor != 0) { +		usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor); +		return; +	} +	usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", vh->major, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev)); +} + +static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, int payload_length) +{ + +} + + +void device_data_input(struct usb_device *usbdev, unsigned char *buffer, int length) +{ +	int i; +	struct mux_device *dev; +	for(i=0; i<num_devs; i++) { +		if(device_list[i].usbdev == usbdev) { +			dev = &device_list[i]; +			break; +		} +	} +	if(i >= num_devs) { +		usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); +		return; +	} +	 +	usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length); +	 +	struct mux_header *mhdr = (struct mux_header *)buffer; +	 +	if(ntohl(mhdr->length) != length) { +		usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length); +		return; +	} +	 +	struct tcphdr *th; +	unsigned char *payload; +	int payload_length; +	 +	switch(ntohl(mhdr->protocol)) { +		case MUX_PROTO_VERSION: +			device_version_input(dev, (struct version_header *)(mhdr+1)); +			break; +		case MUX_PROTO_TCP: +			th = (struct tcphdr *)(mhdr+1); +			payload = (unsigned char *)(th+1); +			payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header); +			device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length); +			break; +		default: +			usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol)); +			break; +	} +	 +} + +int 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)); +	int res; +	int id = get_next_device_id(); +	int idx = alloc_device(); +	usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(dev), id); +	device_list[idx].id = id; +	device_list[idx].usbdev = dev; +	device_list[idx].state = MUXDEV_INIT; +	struct version_header vh; +	vh.major = htonl(1); +	vh.minor = htonl(0); +	vh.padding = 0; +	if((res = send_packet(&device_list[idx], MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) { +		usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d\n", id); +		device_list[idx].usbdev = NULL; +		device_list[idx].state = MUXDEV_DEAD; +		return res; +	} +	return 0;  }  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)); +	int i; +	for(i=0; i<num_devs; i++) { +		if(device_list[i].usbdev == dev) { +			usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", device_list[i].id, usb_get_location(dev)); +			device_list[i].usbdev = NULL; +			return; +		} +	} +	usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", dev, usb_get_location(dev)); +} + +void device_init(void) +{ +	usbmuxd_log(LL_DEBUG, "device_init"); +	num_devs = 1; +	device_list = malloc(sizeof(*device_list) * num_devs); +	memset(device_list, 0, sizeof(*device_list) * num_devs); +	next_device_id = 1; +} + +void device_shutdown(void) +{ +	int i; +	usbmuxd_log(LL_DEBUG, "device_shutdown"); +	for(i=0; i<num_devs; i++) +		device_remove(device_list[i].usbdev); +	free(device_list); +	device_list = NULL;  } @@ -23,7 +23,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  #include "usb.h" -void device_add(struct usb_device *dev); +void device_data_input(struct usb_device *dev, unsigned char *buf, int length); + +int device_add(struct usb_device *dev);  void device_remove(struct usb_device *dev); +void device_init(void); +void device_shutdown(void);  #endif @@ -29,10 +29,12 @@ enum loglevel {  	LL_INFO,  	LL_DEBUG,  	LL_SPEW, +	LL_FLOOD,  };  extern int log_level; -void usbmuxd_log(enum loglevel level, const char *fmt, ...); +void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +  #endif @@ -34,8 +34,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  #include "log.h"  #include "usb.h" +#include "device.h" -const char *socket_path = "/tmp/usbmuxd"; //TODO: CHANGEME +static const char *socket_path = "/tmp/usbmuxd"; //TODO: CHANGEME  int create_socket(void) {  	struct sockaddr_un bind_addr; @@ -75,17 +76,17 @@ int main_loop(int listenfd)  	struct fdlist pollfds;  	while(1) { -		usbmuxd_log(LL_SPEW, "main_loop iteration"); +		usbmuxd_log(LL_FLOOD, "main_loop iteration");  		to = usb_get_timeout(); -		usbmuxd_log(LL_SPEW, "USB timeout is %d ms", to); +		usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);  		fdlist_create(&pollfds);  		fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);  		usb_get_fds(&pollfds); -		usbmuxd_log(LL_SPEW, "fd count is %d", pollfds.count); +		usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);  		cnt = poll(pollfds.fds, pollfds.count, to); -		usbmuxd_log(LL_SPEW, "poll() returned %d", cnt); +		usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);  		if(cnt == 0) {  			if(usb_process() < 0) { @@ -122,6 +123,7 @@ int main(int argc, char *argv[])  	if(listenfd < 0)  		return 1; +	device_init();  	usbmuxd_log(LL_INFO, "Initializing USB");  	if((res = usb_init()) < 0)  		return 2; @@ -135,8 +137,9 @@ int main(int argc, char *argv[])  	usbmuxd_log(LL_NOTICE, "usbmux shutting down");  	usb_shutdown(); +	device_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 0820ed9..27a7bb1 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -42,13 +42,13 @@ struct usb_device {  	uint8_t bus, address;  	char serial[256];  	int alive; +	struct libusb_transfer *rx_xfer;  }; -int num_devs; -int device_id; -struct usb_device *device_list; +static int num_devs; +static struct usb_device *device_list; -struct timeval next_dev_poll_time; +static struct timeval next_dev_poll_time;  static int alloc_device(void)  { @@ -68,11 +68,130 @@ static void usb_disconnect(struct usb_device *dev)  	if(!dev->dev) {  		return;  	} +	if(dev->rx_xfer) { +		// kill the rx xfer and try to make sure the rx callback gets called before we free the device +		struct timeval tv; +		int res; +		// TODO: BUG: outstanding TX xfers are not listed but we need to free them +		libusb_cancel_transfer(dev->rx_xfer); +		tv.tv_sec = tv.tv_usec = 0; +		if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) { +			usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for device removal failed: %d", res); +		} +	}  	libusb_release_interface(dev->dev, USB_INTERFACE);  	libusb_close(dev->dev);  	dev->dev = NULL;  } +static void tx_callback(struct libusb_transfer *xfer) +{ +	struct usb_device *dev = xfer->user_data; +	usbmuxd_log(LL_SPEW, "TX callback dev %d-%d len %d -> %d status %d", dev->bus, dev->address, xfer->length, xfer->actual_length, xfer->status); +	if(xfer->status != LIBUSB_TRANSFER_COMPLETED) { +		switch(xfer->status) { +			case LIBUSB_TRANSFER_COMPLETED: //shut up compiler +			case LIBUSB_TRANSFER_ERROR: +				// funny, this happens when we disconnect the device while waiting for a transfer, sometimes +				usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_TIMED_OUT: +				usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_CANCELLED: +				usbmuxd_log(LL_ERROR, "TX transfer cancelled for device %d-%d", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_STALL: +				usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_NO_DEVICE: +				// other times, this happens, and also even when we abort the transfer after device removal +				usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_OVERFLOW: +				usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address); +				break; +			// and nothing happens (this never gets called) if the device is freed after a disconnect! (bad) +		} +		// we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events +		// we'll do device_remove there too +		dev->alive = 0; +	} +	free(xfer->buffer); +	libusb_free_transfer(xfer); +} + +int usb_send(struct usb_device *dev, const unsigned char *buf, int length) +{ +	int res; +	struct libusb_transfer *xfer = libusb_alloc_transfer(0); +	libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, (void*)buf, length, tx_callback, dev, 0); +	xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK; +	if((res = libusb_submit_transfer(xfer)) < 0) { +		usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res); +		libusb_free_transfer(xfer); +		return res; +	} +	return 0; +} + +static void rx_callback(struct libusb_transfer *xfer) +{ +	struct usb_device *dev = xfer->user_data; +	usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status); +	if(xfer->status == LIBUSB_TRANSFER_COMPLETED) { +		device_data_input(dev, xfer->buffer, xfer->actual_length); +		libusb_submit_transfer(xfer); +	} else { +		switch(xfer->status) { +			case LIBUSB_TRANSFER_COMPLETED: //shut up compiler +			case LIBUSB_TRANSFER_ERROR: +				// funny, this happens when we disconnect the device while waiting for a transfer, sometimes +				usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_TIMED_OUT: +				usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_CANCELLED: +				usbmuxd_log(LL_ERROR, "RX transfer cancelled for device %d-%d", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_STALL: +				usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_NO_DEVICE: +				// other times, this happens, and also even when we abort the transfer after device removal +				usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address); +				break; +			case LIBUSB_TRANSFER_OVERFLOW: +				usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address); +				break; +			// and nothing happens (this never gets called) if the device is freed after a disconnect! (bad) +		} +		free(xfer->buffer); +		dev->rx_xfer = NULL; +		libusb_free_transfer(xfer); +		// we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events +		// we'll do device_remove there too +		dev->alive = 0; +	} +} + +static int start_rx(struct usb_device *dev) +{ +	int res; +	void *buf; +	dev->rx_xfer = libusb_alloc_transfer(0); +	buf = malloc(USB_MTU); +	libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, BULK_IN, buf, USB_MTU, rx_callback, dev, 0); +	if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) { +		usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res); +		libusb_free_transfer(dev->rx_xfer); +		dev->rx_xfer = NULL; +		return res; +	} +	return 0; +} +  static int usb_discover(void)  {  	int cnt, i, j, res; @@ -135,7 +254,8 @@ static int usb_discover(void)  		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); +			usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res); +			libusb_release_interface(handle, USB_INTERFACE);  			libusb_close(handle);  			continue;  		} @@ -145,7 +265,15 @@ static int usb_discover(void)  		device_list[idx].dev = handle;  		device_list[idx].alive = 1; -		device_add(&device_list[idx]); +		if(device_add(&device_list[idx]) < 0) { +			usb_disconnect(&device_list[j]); +			continue; +		} +		if(start_rx(&device_list[idx]) < 0) { +			device_remove(&device_list[j]); +			usb_disconnect(&device_list[j]); +			continue; +		}  		valid_count++;  	}  	for(j=0; j<num_devs; j++) { @@ -232,7 +360,7 @@ int usb_get_timeout(void)  int usb_process(void)  { -	int res; +	int i, res;  	struct timeval tv;  	tv.tv_sec = tv.tv_usec = 0;  	res = libusb_handle_events_timeout(NULL, &tv); @@ -240,6 +368,14 @@ int usb_process(void)  		usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);  		return res;  	} +	// reap devices marked dead due to an RX error +	for(i=0; i<num_devs; i++) { +		if(device_list[i].dev && !device_list[i].alive) { +			device_remove(&device_list[i]); +			usb_disconnect(&device_list[i]); +		} +	} +  	if(dev_poll_remain_ms() <= 0) {  		res = usb_discover();  		if(res < 0) { @@ -256,12 +392,12 @@ int usb_init(void)  	usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");  	res = libusb_init(NULL); +	//libusb_set_debug(NULL, 3);  	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); @@ -26,6 +26,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  #define BULK_IN 0x85  #define BULK_OUT 0x04 +#define USB_MTU 65536 +  #define VID_APPLE 0x5ac  #define PID_IPHONE2G 0x1290  #define PID_ITOUCH1G 0x1291 @@ -42,6 +44,7 @@ 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_send(struct usb_device *dev, const unsigned char *buf, int length);  int usb_process(void);  #endif | 
