# Copyright (c) 2007 Eduardo Felipe # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import sys, socket, struct, math USB_DIR_OUT = 0 USB_DIR_IN = 0x80 USB_TYPE_MASK = (0x03 << 5) USB_TYPE_STANDARD = (0x00 << 5) USB_TYPE_CLASS = (0x01 << 5) USB_TYPE_VENDOR = (0x02 << 5) USB_TYPE_RESERVED = (0x03 << 5) USB_RECIP_MASK = 0x1f USB_RECIP_DEVICE = 0x00 USB_RECIP_INTERFACE = 0x01 USB_RECIP_ENDPOINT = 0x02 USB_RECIP_OTHER = 0x03 DeviceRequest = ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) DeviceOutRequest = ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) InterfaceRequest =((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) InterfaceOutRequest = ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) EndpointRequest = ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) EndpointOutRequest = ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) USB_REQ_GET_STATUS = 0x00 USB_REQ_CLEAR_FEATURE = 0x01 USB_REQ_SET_FEATURE = 0x03 USB_REQ_SET_ADDRESS = 0x05 USB_REQ_GET_DESCRIPTOR = 0x06 USB_REQ_SET_DESCRIPTOR = 0x07 USB_REQ_GET_CONFIGURATION = 0x08 USB_REQ_SET_CONFIGURATION = 0x09 USB_REQ_GET_INTERFACE = 0x0A USB_REQ_SET_INTERFACE = 0x0B USB_REQ_SYNCH_FRAME = 0x0C USB_DEVICE_SELF_POWERED = 0 USB_DEVICE_REMOTE_WAKEUP = 1 USB_DT_DEVICE = 0x01 USB_DT_CONFIG = 0x02 USB_DT_STRING = 0x03 USB_DT_INTERFACE = 0x04 USB_DT_ENDPOINT = 0x05 # HID interface requests GET_REPORT = 0xa101 GET_IDLE = 0xa102 GET_PROTOCOL= 0xa103 SET_IDLE = 0x210a SET_PROTOCOL= 0x210b class USBdevice: def __init__(self): self.dev_descriptor = "\x12\x01\x00\x01\x00\x00\x00\x08\x27\x06\x01\x00\x00\x00\x03\x02\x01\x01" self.conf_descriptor = '\x09\x02\x22\x00\x01\x01\x04\xA0\x32' \ '\x09\x04\x00\x00\x01\x03\x01\x02\x05' \ '\x09\x21\x01\x00\x00\x01\x22\x32\x00' \ '\x07\x05\x81\x03\x03\x00\x0A' self.hid_report_descriptor = '\x05\x01\x09\x02\xA1\x01\x09\x01' \ '\xA1\x00\x05\x09\x19\x01\x29\x03' \ '\x15\x00\x25\x01\x95\x03\x75\x01' \ '\x81\x02\x95\x01\x75\x05\x81\x01' \ '\x05\x01\x09\x30\x09\x31\x15\x81' \ '\x25\x7F\x75\x08\x95\x02\x81\x06' \ '\xC0\xC0' self.reset() def usbstr(self,s): return chr(len(s)*2+2)+'\x03' + ''.join(['%c\x00'%x for x in s]) def reset(self): self.angle = 0.0 self.angleinc = 2.0 * math.pi / 360.0 self.radius = 150.0 self.px = int(self.radius) self.py = 0 def get_string(self,stridx): data = '' if stridx == 0: # language id data = '\x04\x03\x09\x04' # English (US) elif stridx == 1: # serial number data = self.usbstr('1') elif stridx == 2: # product description data = self.usbstr('Python USB mouse') elif stridx == 3: # vendor description data = self.usbstr('QEMU') return data def get_data(self,packetlen): self.angle = ( self.angle + self.angleinc ) % (2.0 * math.pi) nx = int(round(math.cos(self.angle) * self.radius)) ny = int(round(math.sin(self.angle) * self.radius)) dx = nx - self.px dy = - (ny - self.py) self.px = nx self.py = ny b = 0 data = '%c%c%c'%(b,dx&0xff,dy&0xff) return data def get_control(self, request, value, index, length): data = 0 if request == DeviceRequest | USB_REQ_GET_DESCRIPTOR: if value>>8 == USB_DT_DEVICE: data = self.dev_descriptor elif value>>8 == USB_DT_CONFIG: data = self.conf_descriptor elif value>>8 == USB_DT_STRING: data = self.get_string(value&0xff) elif request == DeviceOutRequest | USB_REQ_SET_ADDRESS: data = 0 elif request == DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if value == USB_DEVICE_REMOTE_WAKEUP: data = 0 elif request == DeviceOutRequest | USB_REQ_SET_CONFIGURATION: data = 0 elif request == InterfaceRequest | USB_REQ_GET_DESCRIPTOR: if value >> 8 == 0x22: data = self.hid_report_descriptor elif request == GET_REPORT: data = self.get_data(length) elif request == SET_IDLE: data = 0 else: print 'unhandled request: 0x%04x value: 0x%04x'%(req,value) return data def qemu_ret(data): if isinstance(data,int): return struct.pack('>H',data) elif isinstance(data,basestring): return struct.pack('>H',len(data)) + data # ------------------------------------------------------------------------- if len(sys.argv)>1: localhost,port = sys.argv[1].split(':') else: host='localhost' port = 5555 outsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) outsocket.connect((host,port)) usb = USBdevice() while 1: sdata = outsocket.recv(16) if not sdata: print 'connection reset by peer' break if sdata == 'USBTCP 000.001\n': # Handshake outsocket.send('USBTCP 000.001\n') else: msg = sdata[:2] param = sdata[2:] if msg == 'CT': request, value, index, length = struct.unpack('>HHHH',param) outsocket.send( qemu_ret( usb.get_control(request, value, index, length) ) ) elif msg == 'RS': usb.reset() outsocket.send( qemu_ret( 0 ) ) elif msg == 'DI': value = struct.unpack('>H',param) outsocket.send( qemu_ret( usb.get_data(value) ) ) else: print 'unknown message: %s\n%s'%(msg,param.encode('hex')) break