Hi everybody, I think that a useful feature for QEMU would be to expose the USB interface through TCP.
It would allow quick USB device development in high level languages without recompiling QEMU. We could have an instance of QEMU running all the time while we create our device and hot plug/unplug it whenever we want to. This could also attract people interested in hardware emulation, but scared of learning QEMU internals just to create a simple new device. I think USB is quite suited for this, as it is designed for pluggable external devices, but something similar could be made for serial and parallel devices too. The attached patch is a quick hack derived from the VNC server just to show the idea, not intended for commiting. A dummy protocol is used for message interchange between server and client. It adds the new command line option: -usbtcp port It starts a socket listening on port for incoming connections. A sample USB mouse in python is also provided that moves the cursor in circles. Would such a feature be of any interest for QEMU? Regards, Eduardo Felipe
usb_over_tcp.diff
Description: Binary data
# 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