diff --git a/appliance_setup/configuration.py b/appliance_setup/configuration.py index 401ff88f3b987d339f2b906bbbb68b8eaa6450e8..d103922530b045e5bb9c03bb226fc8119d2662da 100644 --- a/appliance_setup/configuration.py +++ b/appliance_setup/configuration.py @@ -112,7 +112,10 @@ class Config: if (not cfg): cfg = self.cfgdata for (l, key, value) in cfg: - res += "%s %s:%s\n" % (l, key if verbose else "", value) + if (key != None): + res += "%s %s:%s\n" % (l, key if verbose else "", value) + else: + res += "%s:%s\n" % (l, value) return res def save (self, filename): diff --git a/appliance_setup/hp2101nw_connect.py b/appliance_setup/hp2101nw_connect.py new file mode 100755 index 0000000000000000000000000000000000000000..21b2bc2aa8552be4fac3f31a48869b16f420d4b9 --- /dev/null +++ b/appliance_setup/hp2101nw_connect.py @@ -0,0 +1,413 @@ +#!/usr/bin/python +## hp2101nw_connect.py - version 0.01 +## +## Linux connection utility for the HP 2101nw wireless G USB print server. +## +## (C) 2012 Reinhold Kainhofer <reinhold@kainhofer.com> +## License: GPL v2 or later + +## This script (hp2101nw_connect.py) provides a relatively easy way to identify and +## connect to the HP 2101nw wireless USB print server on linux. +## The device is not a real print server, but rather a USB port forwarder +## over TCP/UDP. On Windows it creates a virtual USB port, so for Linux +## support, we'll need a proper kernel module doing the USB port forwarding... +## +## This script "only" detects the device on the network, retrieves information and +## optionally lock it. Once a kernel module is ready, this is the user-space +## part of the driver. +## +## For the device protocol see: +## http://wiki.kainhofer.com/hardware/hp2101nw_wlan_printserver + +## Supported devices: +## -) HP 2101nw wireless G USB print server (USB-id 03f0:cd02) + + +#from device_types import * +from user_interface import * +from helpers import * +from configuration import * +import socket +import struct +import select +import time + + +VERSION=0.01; +print """ +hp2101nw_connect.py - version %.2f +Linux connection utility for the HP 2101nw wireless G USB print server. + +(C) 2012 Reinhold Kainhofer <reinhold@kainhofer.com> +License: GPL v2 or later +""" % (VERSION); + + + +#use IO::Socket; +#use IO::Select; + +# Wait a maximum of 5 seconds for data: +SOCK_TIMEOUT = 5 + + +# The PC always identifies itself as XXXXXXXX. +thisboxname = "XXXXXXXX" +port_information = 34444 +port_command = 34447 +port_usbnet = 34448 + +SBSU_STATUS = 1 +SBSU_LOCK = 2 +SBSU_UNLOCK = 3 +SBSU_UNKNOWN = 7 + +# USB vendor / product ids and descriptions of all supported devices +supported_devices=[ + {'vendor': 0x03f0, 'device': 0xcd02, 'desc': "HP 2101nw wireless G USB print server"} +] + + + + + +############################################################################### +## USB DEVICE MANAGEMENT / COMMUNICATION FUNCTIONS +############################################################################### + +def net_send_data (ui, sock, addr, data): + ui.debug ("Sending packet to : %s" % (addr,)) + return sock.sendto(data, addr) \ + or ui.error ("sending data to socket. Data was:\n%s" % data) + + +def net_read_data (ui, sock): + s = select.select ([sock], [], [], SOCK_TIMEOUT) + if (len(s)>0): + return sock.recv (10240) + else: + ui.debug ("Unable to read data from socket %s." % sock) + return None + +def SBSU_create_buffer (cmd, data=""): + return "SBSU%s%s" % (struct.pack ("<h", cmd), data) + + +## Read data into the second argument (output argument!), return +## size of data +#sub SBSU_receive_data ($$$) { + #my $sock = shift; + #my $cmd = shift; + +#} + +def info_data_write (ui, dev, cfg): + sock = dev.get('socket-info') + cfgstring = cfg.to_string () + l = len(cfgstring) + # Add the header (12 bytes): '@\0', packet length, boxname + request = "@\0%s%s%s" % (struct.pack ("<H", l), dev.get('device-name', ''), cfgstring) + return net_send_data (ui, sock, (dev.get('ip'), port_information), request); + + +## Read USB bulk data into the second argument (output argument!), return +## size of data +def info_data_read (ui, dev): + sock = dev.get('socket-info') + # TODO + + data = net_read_data (ui, sock) + ui.debug ("Data read: %s" % data) + datalen = len(data) + if (not data.startswith ("@\0\0")): + ui.error ("reading data: Wrong header '%s', expected @\\0\\0\n" % data[0:3]) + return None + + ## data length as given in the header: + (header_datalen,) = struct.unpack ("<H", data[3:5]) + datalen = datalen-13 # ignore 13 bytes header + ui.debug ("datalen: %d, header_datalen: %d" % (datalen, header_datalen)) + if (header_datalen != datalen): + ui.error ("reading data: %d data bytes read, headers says %d bytes" % (datalen, header_datalen)) + return None + boxname = data[5:13] + ## FIXME: Check the name of the box... + ## cut off the header: + return data[13:] + +def zerotrim(s): + return s.rstrip(' \t\n\x00') + + +def SBSU_status_parse (ui, data): + # Check the first 6 header bytes (signature) + if (not data.startswith ("SBSU\1\0")): + ui.error ("reading data: Wrong header '%s', expected SBSU\\0\\1\n" % data[0:6]); + return None + #result = {'data': data} + result = {} + # TODO: what does byte 6 mean? + result['device-name'] = zerotrim (data[7:15]) + result['printer-name'] = zerotrim (data[22:86]) + result['locked-ip'] = zerotrim (data[86:102]) # TODO: Shall we call inet_aton on the string here? + result['locked-host'] = zerotrim (data[102:110]) + # TODO: Bytes 112-168??? + (serial, vid, pid) = struct.unpack ("<LHH", data[168:176]) + result['serial'] = serial + result['vendor-id'] = vid + result['device-id'] = pid + #result['usb-descriptors'] = data[176:] + result['printer-plugged'] = (vid>0) and (pid>0) + usbcfg = {} + (iConf, bConfVal, bDescType, bNumInt) = struct.unpack ("<BBBB", data[176:180]) + usbcfg['iConfiguration'] = iConf + usbcfg['bConfigurationValue'] = bConfVal + usbcfg['bDescriptorType'] = bDescType + usbcfg['bNumInterfaces'] = bNumInt + interfaces = {} + startindex = 180 + for i in range(bNumInt): + (ifNr, ifClass, ifSubclass, ifProto) = struct.unpack ("<BBBB", data[startindex:startindex+4]) + startindex += 4 + interfaces[ifNr] = {'bInterfaceNumber': ifNr, 'bInterfaceClass': ifClass, + 'bInterfaceSubClass': ifSubclass, 'bInterfaceProtocol': ifProto} + + usbcfg['interfaces'] = interfaces + result['usb-configuration'] = usbcfg + return result + + + +def device_detect (ui): + request = SBSU_create_buffer (SBSU_STATUS) + result = {} + loop = 0 + ui.debug ("Sending device detection request (UDP broadcast on port 34447)") + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) + s.settimeout(1) + + while (loop <= 2 and len(result)<5): + loop += 1 + ui.progress (".", False) + try: + s.sendto(request, ("<broadcast>", port_command)) + while True: # Listen for all responses + data, (addr, port) = s.recvfrom (8096) + found = SBSU_status_parse (ui, data); + if found: + found['ip'] = addr + result[found['device-name']] = found + + except socket.timeout, ValueError: + pass + s.close() + + # setup the sockets + for i in result.keys (): + device = result.get(i,{}) + peer = device.get('ip') + info_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + info_s.settimeout(1) + device['socket-info'] = info_s + device['connected'] = False + info_data_write (ui, device, Config (ui, cfg=[("9801", None, "")])) + device['mac'] = info_data_read (ui, device) + ui.debug (" Device %s, MAC %s" % (device.get('ip'), device.get('mac'))) + + return result + + + +def device_list (ui, devs): + d = [] + dn = [] + fmt = "%-18s%-15s%-6s%-30s" + for dd in devs.keys (): + devinfo = devs.get (dd) + d.append (fmt % (devinfo.get('device-name'), devinfo.get('ip'), 'X' if devinfo.get('connected') else '', devinfo.get('printer-name'))) + dn.append (dd) + + text = "The following devices were found:\n\n\t " +\ + fmt % ("Device", "IP", "Conn.", "Detected printer") + "\n" +\ + "\t ------------------------------------------------------------------------" + + ui.progress ("\n%s" % text) + i = 1 + for dd in d: + ui.progress ("\t%d) %s" % (i, dd)) + i += 1 + +# if (len (d) == 1): +# ui.progress ("\n%s\n\t %s" % (text, d[0])) +# choice = d[0] +# else: +# choice = ui.ask_choice (text, d, dn, dn[0]) + +# print "Selected device: ", devs.get(choice) +# return devs.get(choice) + + +def pluggedin_info (ui, device): + info_data_write (ui, device, Config (ui, cfg=[("9010", None, "")])) + device['hardware-info'] = info_data_read (ui, device) + info_data_write (ui, device, Config (ui, cfg=[("9106", None, "")])) + device['wireless-info'] = info_data_read (ui, device) + info_data_write (ui, device, Config (ui, cfg=[("9971", None, "")])) + device['status'] = info_data_read (ui, device) + + del device['hardware-info'] + del device['wireless-info'] + +def update_connection_info (ui, device): + request = SBSU_create_buffer (SBSU_STATUS) + ui.debug ("Sending device information request (UDP port 34447)") + try: + s = device.get ('socket-info'); + s.sendto(request, (device.get('ip'), port_command)) + data = s.recv (8096) + found = SBSU_status_parse (ui, data) + device.update (found) + if (device.get('printer-plugged') and not device.get('hardware-info')): + # Printer was newly plugged in, so retrieve the hardware info + pluggedin_info (ui, device) + elif (not device.get('printer-plugged') and device.get('hardware-info')): + # Printer was unplugged, clean hardware info + device['hardware-info'] = None + del device['hardware-info'] + device['wireless-info'] = None + del device['wireless-info'] + device['status'] = None + del device['status'] + except socket.timeout, ValueError: + pass + + +def device_unconnect (ui, device): + if (not device.get('connected')): + ui.error("trying to unconnect already unconnected device %s" % device.get('devicename')) + + # TODO: Unregister socket from server + # Close the USB/IP socket: + s = device.get ('socket-usbip') + if s: + s.close () + del device['socket-usbip'] + + # and unlock the device: + request = SBSU_create_buffer (SBSU_UNLOCK) + ui.debug ("Unlocking device (TCP port 34447)") + s = device.get ('socket-commands'); + try: + s.send(request) # , (device.get('ip'), port_command) + data = s.recv (8096) + # TODO: Check status response + update_connection_info (ui, device) + except socket.timeout, ValueError: + pass + s.close () + del device['socket-commands'] + + update_connection_info (ui, device) + device['connected'] = False + + +def device_connect (ui, device): + # Create the socket for TCP port 34447 + sock_cmd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock_cmd.connect((device.get('ip'), port_command)) + sock_cmd.settimeout(1) + device['socket-commands'] = sock_cmd + # send the locking command, read the device status to check for success + local_ip = sock_cmd.getsockname()[0] + local_hostname = socket.gethostname() + unknown_data="\x7C\xF2\xF0\x00\xBE\xB0\x01\x10\x68\xF3" + \ + "\xf0\x00\x20\xe9\x91\x7c\x60\x00\x92\x7c\xff\xff\xff\xff\x5d\x00" + \ + "\x92\x7c\x67\xa4\x01\x10\x00\x00\xb6\x00\x00\x00\x00\x00\x86\xa4" + \ + "\x01\x10\xf9\x85\xbe\x9b\x30\x2f\x3a\x00\x30\xf4\x00\x00\x00\x00" + \ + "\x00\x00" + + print local_ip + print local_hostname + + # data is: 14 bytes IP, 0xf438 (UNKNOWN), 10 bytes hostname, + bufferfmt = "<14sH10s" + request = SBSU_create_buffer (SBSU_LOCK, struct.pack (bufferfmt, local_ip, 0xf438, local_hostname) + unknown_data) + ui.debug ("Locking device (TCP port 34447)") + try: + s = device.get ('socket-commands'); + s.send(request)#, (device.get('ip'), port_command)) + data = s.recv (8096) + #found = SBSU_status_parse (ui, data) + #device.update (found) + update_connection_info (ui, device) + if (device.get('locked-ip')): + device['connected'] = True + except socket.timeout, ValueError: + pass + + # Create the socket for TCP port 34448 + sock_usbip = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock_usbip.connect((device.get('ip'), port_usbnet)) + sock_usbip.settimeout(1) + device['socket-usbip'] = sock_usbip + + + +############################################################################### +## MAIN OPERATIONS: ASK FOR OPERATION AND HANDLE IT +############################################################################### + + +def main (): + UI = ConsoleInterface (debug=True) + devs = device_detect (UI) + if (len(devs) == 0): + UI.progress ("No supported devices detected on the network. Supported devices are:") + for i in supported_devices: + UI.progress ("\t-) %s" % i.get('desc')) + UI.progress ("") + exit (0) + print devs + device_list (UI, devs) + + time.sleep (3) + print "=====\nConnecting now\n\n" + # Loop in detected devices + # update plugged-in status ( + i = 0 + while i<2: + i += 1 + for d in devs: + device = devs[d] + update_connection_info (UI,device) + if (not device.get('locked-ip')): + device_connect (UI, device) + device_list (UI, devs) + print devs + time.sleep (3) + print "=====\n" + + print "=====\nJust waiting now\n\n" + for d in devs: + device = devs[d] + #if (device.get('connected') or device.get('locked-ip')==device.get('socket-info').getsockname()[0]): + device_unconnect (UI, device) + update_connection_info (UI,device) + + print devs + time.sleep (5) + print "=====\nUnconnecting now\n\n" + + for d in devs: + device = devs[d] + update_connection_info (UI,device) + device_list (UI, devs) + + + +############################################################################### +## MAIN FUNCTION +############################################################################### + +main () diff --git a/appliance_setup/user_interface.py b/appliance_setup/user_interface.py index 76fc90300fe22e5b528e49ebb81e6c3aafe9b2d5..b93a5dfebe9334f19ff833f1bf9686e32e3fe9ee 100644 --- a/appliance_setup/user_interface.py +++ b/appliance_setup/user_interface.py @@ -107,10 +107,9 @@ class ConsoleInterface (UserInterface): def __init__ (self, debug): UserInterface.__init__ (self, debug); def progress (self, str, newline=True): + sys.stdout.write(str) if newline: - print str - else: - print str, + sys.stdout.write("\n") sys.stdout.flush() def debug (self, str): if self.is_debug: