diff options
| author | 2009-05-05 01:40:22 +0200 | |
|---|---|---|
| committer | 2009-05-05 01:40:22 +0200 | |
| commit | dce2546afadd9185b7e65f1db6b127d6293b178c (patch) | |
| tree | 956f7d1adbba68ca2db4ce7d8740e10be8f5f53b | |
| parent | 78577f36b4fd92b052300e0b4d3b369d45b40e67 (diff) | |
| download | usbmuxd-dce2546afadd9185b7e65f1db6b127d6293b178c.tar.gz usbmuxd-dce2546afadd9185b7e65f1db6b127d6293b178c.tar.bz2 | |
Add python stuff and patches
| -rw-r--r-- | patches/libusb-add-zlp-flag.patch | 24 | ||||
| -rw-r--r-- | patches/valgrind-usbdevfs.patch | 133 | ||||
| -rw-r--r-- | python-client/tcprelay.py | 146 | ||||
| -rw-r--r-- | python-client/usbmux.py | 240 |
4 files changed, 543 insertions, 0 deletions
diff --git a/patches/libusb-add-zlp-flag.patch b/patches/libusb-add-zlp-flag.patch new file mode 100644 index 0000000..add9f97 --- /dev/null +++ b/patches/libusb-add-zlp-flag.patch | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | diff -ur libusb-1.0.0/libusb/os/linux_usbfs.c libusb-1.0.0-mod/libusb/os/linux_usbfs.c | ||
| 2 | --- libusb-1.0.0/libusb/os/linux_usbfs.c 2008-12-13 20:43:20.000000000 +0100 | ||
| 3 | +++ libusb-1.0.0-mod/libusb/os/linux_usbfs.c 2009-04-30 04:25:12.000000000 +0200 | ||
| 4 | @@ -1295,7 +1295,8 @@ | ||
| 5 | urb->buffer_length = transfer->length % MAX_BULK_BUFFER_LENGTH; | ||
| 6 | else | ||
| 7 | urb->buffer_length = MAX_BULK_BUFFER_LENGTH; | ||
| 8 | - | ||
| 9 | + if ((!(transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK)) && (i == num_urbs - 1)) | ||
| 10 | + urb->flags = USBFS_URB_ZLP; | ||
| 11 | r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb); | ||
| 12 | if (r < 0) { | ||
| 13 | int j; | ||
| 14 | diff -ur libusb-1.0.0/libusb/os/linux_usbfs.h libusb-1.0.0-mod/libusb/os/linux_usbfs.h | ||
| 15 | --- libusb-1.0.0/libusb/os/linux_usbfs.h 2008-07-16 16:17:57.000000000 +0200 | ||
| 16 | +++ libusb-1.0.0-mod/libusb/os/linux_usbfs.h 2009-04-30 04:08:27.000000000 +0200 | ||
| 17 | @@ -63,6 +63,7 @@ | ||
| 18 | #define USBFS_URB_DISABLE_SPD 1 | ||
| 19 | #define USBFS_URB_ISO_ASAP 2 | ||
| 20 | #define USBFS_URB_QUEUE_BULK 0x10 | ||
| 21 | +#define USBFS_URB_ZLP 0x40 | ||
| 22 | |||
| 23 | enum usbfs_urb_type { | ||
| 24 | USBFS_URB_TYPE_ISO = 0, | ||
diff --git a/patches/valgrind-usbdevfs.patch b/patches/valgrind-usbdevfs.patch new file mode 100644 index 0000000..9287573 --- /dev/null +++ b/patches/valgrind-usbdevfs.patch | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | diff -ur -x '*~' valgrind-3.4.1/coregrind/m_syswrap/syswrap-generic.c valgrind-3.4.1-mod/coregrind/m_syswrap/syswrap-generic.c | ||
| 2 | --- valgrind-3.4.1/coregrind/m_syswrap/syswrap-generic.c 2009-03-01 23:01:09.000000000 +0100 | ||
| 3 | +++ valgrind-3.4.1-mod/coregrind/m_syswrap/syswrap-generic.c 2009-04-29 15:44:08.000000000 +0200 | ||
| 4 | @@ -4160,23 +4160,56 @@ | ||
| 5 | struct vki_usbdevfs_urb *vkuu = (struct vki_usbdevfs_urb *)ARG3; | ||
| 6 | |||
| 7 | /* Not the whole struct needs to be initialized */ | ||
| 8 | - PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).ep", (Addr)&vkuu->endpoint, sizeof(vkuu->endpoint)); | ||
| 9 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).endpoint", (Addr)&vkuu->endpoint, sizeof(vkuu->endpoint)); | ||
| 10 | PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).type", (Addr)&vkuu->type, sizeof(vkuu->type)); | ||
| 11 | PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).flags", (Addr)&vkuu->flags, sizeof(vkuu->flags)); | ||
| 12 | PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer", (Addr)&vkuu->buffer, sizeof(vkuu->buffer)); | ||
| 13 | - PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer_length", (Addr)&vkuu->buffer_length, sizeof(vkuu->buffer_length)); | ||
| 14 | - PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).usercontext", (Addr)&vkuu->usercontext, sizeof(vkuu->usercontext)); | ||
| 15 | - if (vkuu->endpoint & 0x80) | ||
| 16 | - PRE_MEM_WRITE( "ioctl(USBDEVFS_URB).buffer", (Addr)vkuu->buffer, vkuu->buffer_length); | ||
| 17 | - else | ||
| 18 | - PRE_MEM_READ( "ioctl(USBDEVFS_URB).buffer", (Addr)vkuu->buffer, vkuu->buffer_length); | ||
| 19 | - /* FIXME: Does not handle all cases this ioctl can do, ISOs are missing. */ | ||
| 20 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).signr", (Addr)&vkuu->signr, sizeof(vkuu->signr)); | ||
| 21 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).status", (Addr)&vkuu->status, sizeof(vkuu->status)); | ||
| 22 | + if (vkuu->type == VKI_USBDEVFS_URB_TYPE_CONTROL) { | ||
| 23 | + struct vki_usbdevfs_setuppacket *vkusp = (struct vki_usbdevfs_setuppacket *)vkuu->buffer; | ||
| 24 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer_length", (Addr)&vkuu->buffer_length, sizeof(vkuu->buffer_length)); | ||
| 25 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer.setup_packet", (Addr)vkusp, sizeof(*vkusp)); | ||
| 26 | + if (vkusp->bRequestType & 0x80) | ||
| 27 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).buffer.data", (Addr)(vkusp+1), vkuu->buffer_length - sizeof(*vkusp)); | ||
| 28 | + else | ||
| 29 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer.data", (Addr)(vkusp+1), vkuu->buffer_length - sizeof(*vkusp)); | ||
| 30 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).actual_length", (Addr)&vkuu->actual_length, sizeof(vkuu->actual_length)); | ||
| 31 | + } else if (vkuu->type == VKI_USBDEVFS_URB_TYPE_ISO) { | ||
| 32 | + int total_length = 0; | ||
| 33 | + int i; | ||
| 34 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).number_of_packets", (Addr)&vkuu->number_of_packets, sizeof(vkuu->number_of_packets)); | ||
| 35 | + for(i=0; i<vkuu->number_of_packets; i++) { | ||
| 36 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).iso_frame_desc[].length", (Addr)&vkuu->iso_frame_desc[i].length, sizeof(vkuu->iso_frame_desc[i].length)); | ||
| 37 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).iso_frame_desc[].actual_length", (Addr)&vkuu->iso_frame_desc[i].actual_length, sizeof(vkuu->iso_frame_desc[i].actual_length)); | ||
| 38 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).iso_frame_desc[].status", (Addr)&vkuu->iso_frame_desc[i].status, sizeof(vkuu->iso_frame_desc[i].status)); | ||
| 39 | + total_length += vkuu->iso_frame_desc[i].length; | ||
| 40 | + } | ||
| 41 | + if (vkuu->endpoint & 0x80) | ||
| 42 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).buffer", (Addr)vkuu->buffer, total_length); | ||
| 43 | + else | ||
| 44 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer", (Addr)vkuu->buffer, total_length); | ||
| 45 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).error_count", (Addr)&vkuu->error_count, sizeof(vkuu->error_count)); | ||
| 46 | + } else { | ||
| 47 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer_length", (Addr)&vkuu->buffer_length, sizeof(vkuu->buffer_length)); | ||
| 48 | + if (vkuu->endpoint & 0x80) | ||
| 49 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).buffer", (Addr)vkuu->buffer, vkuu->buffer_length); | ||
| 50 | + else | ||
| 51 | + PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB).buffer", (Addr)vkuu->buffer, vkuu->buffer_length); | ||
| 52 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_SUBMITURB).actual_length", (Addr)&vkuu->actual_length, sizeof(vkuu->actual_length)); | ||
| 53 | + } | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | + case VKI_USBDEVFS_DISCARDURB: | ||
| 57 | + break; | ||
| 58 | case VKI_USBDEVFS_REAPURB: | ||
| 59 | + if ( ARG3 ) { | ||
| 60 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_REAPURB)", ARG3, sizeof(struct vki_usbdevfs_urb **)); | ||
| 61 | + break; | ||
| 62 | + } | ||
| 63 | case VKI_USBDEVFS_REAPURBNDELAY: | ||
| 64 | if ( ARG3 ) { | ||
| 65 | - PRE_MEM_READ( "ioctl(USBDEVFS_SUBMITURB)", ARG3, sizeof(struct vki_usbdevfs_urb *)); | ||
| 66 | + PRE_MEM_WRITE( "ioctl(USBDEVFS_REAPURBNDELAY)", ARG3, sizeof(struct vki_usbdevfs_urb **)); | ||
| 67 | break; | ||
| 68 | } | ||
| 69 | case VKI_USBDEVFS_CONNECTINFO: | ||
| 70 | @@ -4986,11 +5019,31 @@ | ||
| 71 | case VKI_USBDEVFS_REAPURBNDELAY: | ||
| 72 | if ( ARG3 ) { | ||
| 73 | struct vki_usbdevfs_urb **vkuu = (struct vki_usbdevfs_urb**)ARG3; | ||
| 74 | + POST_MEM_WRITE((Addr)vkuu, sizeof(*vkuu)); | ||
| 75 | if (!*vkuu) | ||
| 76 | break; | ||
| 77 | POST_MEM_WRITE((Addr) &((*vkuu)->status),sizeof((*vkuu)->status)); | ||
| 78 | - if ((*vkuu)->endpoint & 0x80) | ||
| 79 | - POST_MEM_WRITE((Addr)(*vkuu)->buffer, (*vkuu)->actual_length); | ||
| 80 | + if ((*vkuu)->type == VKI_USBDEVFS_URB_TYPE_CONTROL) { | ||
| 81 | + struct vki_usbdevfs_setuppacket *vkusp = (struct vki_usbdevfs_setuppacket *)(*vkuu)->buffer; | ||
| 82 | + if (vkusp->bRequestType & 0x80) | ||
| 83 | + POST_MEM_WRITE((Addr)(vkusp+1), (*vkuu)->buffer_length - sizeof(*vkusp)); | ||
| 84 | + POST_MEM_WRITE((Addr)&(*vkuu)->actual_length, sizeof((*vkuu)->actual_length)); | ||
| 85 | + } else if ((*vkuu)->type == VKI_USBDEVFS_URB_TYPE_ISO) { | ||
| 86 | + char *bp = (*vkuu)->buffer; | ||
| 87 | + int i; | ||
| 88 | + for(i=0; i<(*vkuu)->number_of_packets; i++) { | ||
| 89 | + POST_MEM_WRITE((Addr)&(*vkuu)->iso_frame_desc[i].actual_length, sizeof((*vkuu)->iso_frame_desc[i].actual_length)); | ||
| 90 | + POST_MEM_WRITE((Addr)&(*vkuu)->iso_frame_desc[i].status, sizeof((*vkuu)->iso_frame_desc[i].status)); | ||
| 91 | + if ((*vkuu)->endpoint & 0x80) | ||
| 92 | + POST_MEM_WRITE((Addr)bp, (*vkuu)->iso_frame_desc[i].actual_length); | ||
| 93 | + bp += (*vkuu)->iso_frame_desc[i].length; // FIXME: or actual_length?? | ||
| 94 | + } | ||
| 95 | + POST_MEM_WRITE((Addr)&(*vkuu)->error_count, sizeof((*vkuu)->error_count)); | ||
| 96 | + } else { | ||
| 97 | + if ((*vkuu)->endpoint & 0x80) | ||
| 98 | + POST_MEM_WRITE((Addr)(*vkuu)->buffer, (*vkuu)->actual_length); | ||
| 99 | + POST_MEM_WRITE((Addr)&(*vkuu)->actual_length, sizeof((*vkuu)->actual_length)); | ||
| 100 | + } | ||
| 101 | break; | ||
| 102 | } | ||
| 103 | case VKI_USBDEVFS_CONNECTINFO: | ||
| 104 | diff -ur -x '*~' valgrind-3.4.1/include/vki/vki-linux.h valgrind-3.4.1-mod/include/vki/vki-linux.h | ||
| 105 | --- valgrind-3.4.1/include/vki/vki-linux.h 2009-03-01 22:46:45.000000000 +0100 | ||
| 106 | +++ valgrind-3.4.1-mod/include/vki/vki-linux.h 2009-04-29 15:42:05.000000000 +0200 | ||
| 107 | @@ -2325,11 +2325,26 @@ | ||
| 108 | #define VKI_USBDEVFS_BULK _VKI_IOWR('U', 2, struct vki_usbdevfs_bulktransfer) | ||
| 109 | #define VKI_USBDEVFS_GETDRIVER _VKI_IOW('U', 8, struct vki_usbdevfs_getdriver) | ||
| 110 | #define VKI_USBDEVFS_SUBMITURB _VKI_IOR('U', 10, struct vki_usbdevfs_urb) | ||
| 111 | +#define VKI_USBDEVFS_DISCARDURB _VKI_IO('U', 11) | ||
| 112 | #define VKI_USBDEVFS_REAPURB _VKI_IOW('U', 12, void *) | ||
| 113 | #define VKI_USBDEVFS_REAPURBNDELAY _VKI_IOW('U', 13, void *) | ||
| 114 | #define VKI_USBDEVFS_CONNECTINFO _VKI_IOW('U', 17, struct vki_usbdevfs_connectinfo) | ||
| 115 | #define VKI_USBDEVFS_IOCTL _VKI_IOWR('U', 18, struct vki_usbdevfs_ioctl) | ||
| 116 | |||
| 117 | +#define VKI_USBDEVFS_URB_TYPE_ISO 0 | ||
| 118 | +#define VKI_USBDEVFS_URB_TYPE_INTERRUPT 1 | ||
| 119 | +#define VKI_USBDEVFS_URB_TYPE_CONTROL 2 | ||
| 120 | +#define VKI_USBDEVFS_URB_TYPE_BULK 3 | ||
| 121 | + | ||
| 122 | +// this is missing in usbdevice_fs.h | ||
| 123 | +struct vki_usbdevfs_setuppacket { | ||
| 124 | + __vki_u8 bRequestType; | ||
| 125 | + __vki_u8 bRequest; | ||
| 126 | + __vki_u16 wValue; | ||
| 127 | + __vki_u16 wIndex; | ||
| 128 | + __vki_u16 wLength; | ||
| 129 | +}; | ||
| 130 | + | ||
| 131 | //---------------------------------------------------------------------- | ||
| 132 | // From linux-2.6.20.1/include/linux/i2c.h | ||
| 133 | //---------------------------------------------------------------------- | ||
diff --git a/python-client/tcprelay.py b/python-client/tcprelay.py new file mode 100644 index 0000000..3075fcb --- /dev/null +++ b/python-client/tcprelay.py | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #!/usr/bin/python | ||
| 2 | # | ||
| 3 | # tcprelay.py - TCP connection relay for usbmuxd | ||
| 4 | # | ||
| 5 | # Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com> | ||
| 6 | # | ||
| 7 | # This program is free software; you can redistribute it and/or modify | ||
| 8 | # it under the terms of the GNU General Public License as published by | ||
| 9 | # the Free Software Foundation, either version 2 or version 3. | ||
| 10 | # | ||
| 11 | # This program is distributed in the hope that it will be useful, | ||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | # GNU General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU General Public License | ||
| 17 | # along with this program; if not, write to the Free Software | ||
| 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | |||
| 20 | import usbmux | ||
| 21 | import SocketServer | ||
| 22 | import select | ||
| 23 | from optparse import OptionParser | ||
| 24 | import sys | ||
| 25 | import threading | ||
| 26 | |||
| 27 | class SocketRelay(object): | ||
| 28 | def __init__(self, a, b, maxbuf=65535): | ||
| 29 | self.a = a | ||
| 30 | self.b = b | ||
| 31 | self.atob = "" | ||
| 32 | self.btoa = "" | ||
| 33 | self.maxbuf = maxbuf | ||
| 34 | def handle(self): | ||
| 35 | while True: | ||
| 36 | rlist = [] | ||
| 37 | wlist = [] | ||
| 38 | xlist = [self.a, self.b] | ||
| 39 | if self.atob: | ||
| 40 | wlist.append(self.b) | ||
| 41 | if self.btoa: | ||
| 42 | wlist.append(self.a) | ||
| 43 | if len(self.atob) < self.maxbuf: | ||
| 44 | rlist.append(self.a) | ||
| 45 | if len(self.btoa) < self.maxbuf: | ||
| 46 | rlist.append(self.b) | ||
| 47 | rlo, wlo, xlo = select.select(rlist, wlist, xlist) | ||
| 48 | if xlo: | ||
| 49 | return | ||
| 50 | if self.a in wlo: | ||
| 51 | n = self.a.send(self.btoa) | ||
| 52 | self.btoa = self.btoa[n:] | ||
| 53 | if self.b in wlo: | ||
| 54 | n = self.b.send(self.atob) | ||
| 55 | self.atob = self.atob[n:] | ||
| 56 | if self.a in rlo: | ||
| 57 | s = self.a.recv(self.maxbuf - len(self.atob)) | ||
| 58 | if not s: | ||
| 59 | return | ||
| 60 | self.atob += s | ||
| 61 | if self.b in rlo: | ||
| 62 | s = self.b.recv(self.maxbuf - len(self.btoa)) | ||
| 63 | if not s: | ||
| 64 | return | ||
| 65 | self.btoa += s | ||
| 66 | #print "Relay iter: %8d atob, %8d btoa, lists: %r %r %r"%(len(self.atob), len(self.btoa), rlo, wlo, xlo) | ||
| 67 | |||
| 68 | class TCPRelay(SocketServer.BaseRequestHandler): | ||
| 69 | def handle(self): | ||
| 70 | print "Incoming connection to %d"%self.server.server_address[1] | ||
| 71 | mux = usbmux.USBMux("/tmp/usbmuxd") | ||
| 72 | print "Waiting for devices..." | ||
| 73 | if not mux.devices: | ||
| 74 | mux.process(1.0) | ||
| 75 | if not mux.devices: | ||
| 76 | print "No device found" | ||
| 77 | self.request.close() | ||
| 78 | return | ||
| 79 | dev = mux.devices[0] | ||
| 80 | print "Connecting to device %s"%str(dev) | ||
| 81 | dsock = mux.connect(dev, self.server.rport) | ||
| 82 | lsock = self.request | ||
| 83 | print "Connection established, relaying data" | ||
| 84 | try: | ||
| 85 | fwd = SocketRelay(dsock, lsock, self.server.bufsize * 1024) | ||
| 86 | fwd.handle() | ||
| 87 | finally: | ||
| 88 | dsock.close() | ||
| 89 | lsock.close() | ||
| 90 | print "Connection closed" | ||
| 91 | |||
| 92 | class TCPServer(SocketServer.TCPServer): | ||
| 93 | allow_reuse_address = True | ||
| 94 | |||
| 95 | class ThreadedTCPServer(SocketServer.ThreadingMixIn, TCPServer): | ||
| 96 | pass | ||
| 97 | |||
| 98 | HOST = "localhost" | ||
| 99 | |||
| 100 | parser = OptionParser(usage="usage: %prog [OPTIONS] RemotePort[:LocalPort] [RemotePort[:LocalPort]]...") | ||
| 101 | parser.add_option("-t", "--threaded", dest='threaded', action='store_true', default=False, help="use threading to handle multiple connections at once") | ||
| 102 | parser.add_option("-b", "--bufsize", dest='bufsize', action='store', metavar='KILOBYTES', type='int', default=128, help="specify buffer size for socket forwarding") | ||
| 103 | |||
| 104 | options, args = parser.parse_args() | ||
| 105 | |||
| 106 | serverclass = TCPServer | ||
| 107 | if options.threaded: | ||
| 108 | serverclass = ThreadedTCPServer | ||
| 109 | |||
| 110 | if len(args) == 0: | ||
| 111 | parser.print_help() | ||
| 112 | sys.exit(1) | ||
| 113 | |||
| 114 | ports = [] | ||
| 115 | |||
| 116 | for arg in args: | ||
| 117 | try: | ||
| 118 | if ':' in arg: | ||
| 119 | rport, lport = arg.split(":") | ||
| 120 | rport = int(rport) | ||
| 121 | lport = int(lport) | ||
| 122 | ports.append((rport, lport)) | ||
| 123 | else: | ||
| 124 | ports.append((int(arg), int(arg))) | ||
| 125 | except: | ||
| 126 | parser.print_help() | ||
| 127 | sys.exit(1) | ||
| 128 | |||
| 129 | servers=[] | ||
| 130 | |||
| 131 | for rport, lport in ports: | ||
| 132 | print "Forwarding local port %d to remote port %d"%(lport, rport) | ||
| 133 | server = serverclass((HOST, lport), TCPRelay) | ||
| 134 | server.rport = rport | ||
| 135 | server.bufsize = options.bufsize | ||
| 136 | servers.append(server) | ||
| 137 | |||
| 138 | alive = True | ||
| 139 | |||
| 140 | while alive: | ||
| 141 | try: | ||
| 142 | rl, wl, xl = select.select(servers, [], []) | ||
| 143 | for server in rl: | ||
| 144 | server.handle_request() | ||
| 145 | except: | ||
| 146 | alive = False | ||
diff --git a/python-client/usbmux.py b/python-client/usbmux.py new file mode 100644 index 0000000..172f326 --- /dev/null +++ b/python-client/usbmux.py | |||
| @@ -0,0 +1,240 @@ | |||
| 1 | #!/usr/bin/python | ||
| 2 | # | ||
| 3 | # usbmux.py - usbmux client library for Python | ||
| 4 | # | ||
| 5 | # Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com> | ||
| 6 | # | ||
| 7 | # This program is free software; you can redistribute it and/or modify | ||
| 8 | # it under the terms of the GNU General Public License as published by | ||
| 9 | # the Free Software Foundation, either version 2 or version 3. | ||
| 10 | # | ||
| 11 | # This program is distributed in the hope that it will be useful, | ||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | # GNU General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU General Public License | ||
| 17 | # along with this program; if not, write to the Free Software | ||
| 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | |||
| 20 | import socket, struct, select, sys | ||
| 21 | |||
| 22 | try: | ||
| 23 | import plistlib | ||
| 24 | haveplist = True | ||
| 25 | except: | ||
| 26 | haveplist = False | ||
| 27 | |||
| 28 | class MuxError(Exception): | ||
| 29 | pass | ||
| 30 | |||
| 31 | class MuxVersionError(MuxError): | ||
| 32 | pass | ||
| 33 | |||
| 34 | class SafeStreamSocket: | ||
| 35 | def __init__(self, address, family): | ||
| 36 | self.sock = socket.socket(family, socket.SOCK_STREAM) | ||
| 37 | self.sock.connect(address) | ||
| 38 | def send(self, msg): | ||
| 39 | totalsent = 0 | ||
| 40 | while totalsent < len(msg): | ||
| 41 | sent = self.sock.send(msg[totalsent:]) | ||
| 42 | if sent == 0: | ||
| 43 | raise MuxError("socket connection broken") | ||
| 44 | totalsent = totalsent + sent | ||
| 45 | def recv(self, size): | ||
| 46 | msg = '' | ||
| 47 | while len(msg) < size: | ||
| 48 | chunk = self.sock.recv(size-len(msg)) | ||
| 49 | if chunk == '': | ||
| 50 | raise MuxError("socket connection broken") | ||
| 51 | msg = msg + chunk | ||
| 52 | return msg | ||
| 53 | |||
| 54 | class MuxDevice(object): | ||
| 55 | def __init__(self, devid, usbprod, serial, location): | ||
| 56 | self.devid = devid | ||
| 57 | self.usbprod = usbprod | ||
| 58 | self.serial = serial | ||
| 59 | self.location = location | ||
| 60 | def __str__(self): | ||
| 61 | return "<MuxDevice: ID %d ProdID 0x%04x Serial '%s' Location 0x%x>"%(self.devid, self.usbprod, self.serial, self.location) | ||
| 62 | |||
| 63 | class BinaryProtocol(object): | ||
| 64 | TYPE_RESULT = 1 | ||
| 65 | TYPE_CONNECT = 2 | ||
| 66 | TYPE_LISTEN = 3 | ||
| 67 | TYPE_DEVICE_ADD = 4 | ||
| 68 | TYPE_DEVICE_REMOVE = 5 | ||
| 69 | VERSION = 0 | ||
| 70 | def __init__(self, socket): | ||
| 71 | self.socket = socket | ||
| 72 | self.connected = False | ||
| 73 | |||
| 74 | def _pack(self, req, payload): | ||
| 75 | if req == self.TYPE_CONNECT: | ||
| 76 | return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + "\x00\x00" | ||
| 77 | elif req == self.TYPE_LISTEN: | ||
| 78 | return "" | ||
| 79 | else: | ||
| 80 | raise ValueError("Invalid outgoing request type %d"%req) | ||
| 81 | |||
| 82 | def _unpack(self, resp, payload): | ||
| 83 | if resp == self.TYPE_RESULT: | ||
| 84 | return {'Number':struct.unpack("I", payload)[0]} | ||
| 85 | elif resp == self.TYPE_DEVICE_ADD: | ||
| 86 | devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload) | ||
| 87 | serial = serial.split("\0")[0] | ||
| 88 | return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}} | ||
| 89 | elif resp == self.TYPE_DEVICE_REMOVE: | ||
| 90 | devid = struct.unpack("I", payload)[0] | ||
| 91 | return {'DeviceID': devid} | ||
| 92 | else: | ||
| 93 | raise MuxError("Invalid incoming request type %d"%req) | ||
| 94 | |||
| 95 | def sendpacket(self, req, tag, payload={}): | ||
| 96 | payload = self._pack(req, payload) | ||
| 97 | if self.connected: | ||
| 98 | raise MuxError("Mux is connected, cannot issue control packets") | ||
| 99 | length = 16 + len(payload) | ||
| 100 | data = struct.pack("IIII", length, self.VERSION, req, tag) + payload | ||
| 101 | self.socket.send(data) | ||
| 102 | def getpacket(self): | ||
| 103 | if self.connected: | ||
| 104 | raise MuxError("Mux is connected, cannot issue control packets") | ||
| 105 | dlen = self.socket.recv(4) | ||
| 106 | dlen = struct.unpack("I", dlen)[0] | ||
| 107 | body = self.socket.recv(dlen - 4) | ||
| 108 | version, resp, tag = struct.unpack("III",body[:0xc]) | ||
| 109 | if version != self.VERSION: | ||
| 110 | raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version)) | ||
| 111 | payload = self._unpack(resp, body[0xc:]) | ||
| 112 | return (resp, tag, payload) | ||
| 113 | |||
| 114 | class PlistProtocol(BinaryProtocol): | ||
| 115 | TYPE_RESULT = "Result" | ||
| 116 | TYPE_CONNECT = "Connect" | ||
| 117 | TYPE_LISTEN = "Listen" | ||
| 118 | TYPE_DEVICE_ADD = "Attached" | ||
| 119 | TYPE_DEVICE_REMOVE = "Detached" #??? | ||
| 120 | TYPE_PLIST = 8 | ||
| 121 | VERSION = 1 | ||
| 122 | def __init__(self, socket): | ||
| 123 | if not haveplist: | ||
| 124 | raise Exception("You need the plistlib module") | ||
| 125 | BinaryProtocol.__init__(self, socket) | ||
| 126 | |||
| 127 | def _pack(self, req, payload): | ||
| 128 | return payload | ||
| 129 | |||
| 130 | def _unpack(self, resp, payload): | ||
| 131 | return payload | ||
| 132 | |||
| 133 | def sendpacket(self, req, tag, payload={}): | ||
| 134 | payload['ClientVersionString'] = 'usbmux.py by marcan' | ||
| 135 | if isinstance(req, int): | ||
| 136 | req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2] | ||
| 137 | payload['MessageType'] = req | ||
| 138 | payload['ProgName'] = 'tcprelay' | ||
| 139 | BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.writePlistToString(payload)) | ||
| 140 | def getpacket(self): | ||
| 141 | resp, tag, payload = BinaryProtocol.getpacket(self) | ||
| 142 | if resp != self.TYPE_PLIST: | ||
| 143 | raise MuxError("Received non-plist type %d"%resp) | ||
| 144 | payload = plistlib.readPlistFromString(payload) | ||
| 145 | return payload['MessageType'], tag, payload | ||
| 146 | |||
| 147 | class MuxConnection(object): | ||
| 148 | def __init__(self, socketpath, protoclass): | ||
| 149 | self.socketpath = socketpath | ||
| 150 | if sys.platform in ['win32', 'cygwin']: | ||
| 151 | family = socket.AF_INET | ||
| 152 | address = ('127.0.0.1', 27015) | ||
| 153 | else: | ||
| 154 | family = socket.AF_UNIX | ||
| 155 | address = self.socketpath | ||
| 156 | self.socket = SafeStreamSocket(address, family) | ||
| 157 | self.proto = protoclass(self.socket) | ||
| 158 | self.pkttag = 1 | ||
| 159 | self.devices = [] | ||
| 160 | |||
| 161 | def _getreply(self): | ||
| 162 | while True: | ||
| 163 | resp, tag, data = self.proto.getpacket() | ||
| 164 | if resp == self.proto.TYPE_RESULT: | ||
| 165 | return tag, data | ||
| 166 | else: | ||
| 167 | raise MuxError("Invalid packet type received: %d"%resp) | ||
| 168 | def _processpacket(self): | ||
| 169 | resp, tag, data = self.proto.getpacket() | ||
| 170 | if resp == self.proto.TYPE_DEVICE_ADD: | ||
| 171 | self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID'])) | ||
| 172 | elif resp == self.proto.TYPE_DEVICE_REMOVE: | ||
| 173 | for dev in self.devices: | ||
| 174 | if dev.devid == data['DeviceID']: | ||
| 175 | self.devices.remove(dev) | ||
| 176 | elif resp == self.proto.TYPE_RESULT: | ||
| 177 | raise MuxError("Unexpected result: %d"%resp) | ||
| 178 | else: | ||
| 179 | raise MuxError("Invalid packet type received: %d"%resp) | ||
| 180 | def _exchange(self, req, payload={}): | ||
| 181 | mytag = self.pkttag | ||
| 182 | self.pkttag += 1 | ||
| 183 | self.proto.sendpacket(req, mytag, payload) | ||
| 184 | recvtag, data = self._getreply() | ||
| 185 | if recvtag != mytag: | ||
| 186 | raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag)) | ||
| 187 | return data['Number'] | ||
| 188 | |||
| 189 | def listen(self): | ||
| 190 | ret = self._exchange(self.proto.TYPE_LISTEN) | ||
| 191 | if ret != 0: | ||
| 192 | raise MuxError("Listen failed: error %d"%ret) | ||
| 193 | def process(self, timeout=None): | ||
| 194 | if self.proto.connected: | ||
| 195 | raise MuxError("Socket is connected, cannot process listener events") | ||
| 196 | rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout) | ||
| 197 | if xlo: | ||
| 198 | self.socket.sock.close() | ||
| 199 | raise MuxError("Exception in listener socket") | ||
| 200 | if rlo: | ||
| 201 | self._processpacket() | ||
| 202 | def connect(self, device, port): | ||
| 203 | ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)}) | ||
| 204 | if ret != 0: | ||
| 205 | raise MuxError("Connect failed: error %d"%ret) | ||
| 206 | self.proto.connected = True | ||
| 207 | return self.socket.sock | ||
| 208 | def close(self): | ||
| 209 | self.socket.sock.close() | ||
| 210 | |||
| 211 | class USBMux(object): | ||
| 212 | def __init__(self, socketpath="/tmp/usbmuxd"): | ||
| 213 | self.socketpath = socketpath | ||
| 214 | self.listener = MuxConnection(socketpath, BinaryProtocol) | ||
| 215 | try: | ||
| 216 | self.listener.listen() | ||
| 217 | self.version = 0 | ||
| 218 | self.protoclass = BinaryProtocol | ||
| 219 | except MuxVersionError: | ||
| 220 | self.listener = MuxConnection(socketpath, PlistProtocol) | ||
| 221 | self.listener.listen() | ||
| 222 | self.protoclass = PlistProtocol | ||
| 223 | self.version = 1 | ||
| 224 | self.devices = self.listener.devices | ||
| 225 | def process(self, timeout=None): | ||
| 226 | self.listener.process(timeout) | ||
| 227 | def connect(self, device, port): | ||
| 228 | connector = MuxConnection(self.socketpath, self.protoclass) | ||
| 229 | return connector.connect(device, port) | ||
| 230 | |||
| 231 | if __name__ == "__main__": | ||
| 232 | mux = USBMux() | ||
| 233 | print "Waiting for devices..." | ||
| 234 | if not mux.devices: | ||
| 235 | mux.process(0.1) | ||
| 236 | while True: | ||
| 237 | print "Devices:" | ||
| 238 | for dev in mux.devices: | ||
| 239 | print dev | ||
| 240 | mux.process() | ||
