diff --git a/appliance_setup/appliance_setup.py b/appliance_setup/appliance_setup.py
new file mode 100755
index 0000000000000000000000000000000000000000..78e724b8e41ec9841691793859e57d46c494e113
--- /dev/null
+++ b/appliance_setup/appliance_setup.py
@@ -0,0 +1,711 @@
+#!/usr/bin/python
+## appliance_setup.py - version 0.01
+## 
+## Linux configuration utility for several small network appliances.
+##
+## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
+## License: GPL v2 or later
+
+## This script (appliance_setup.pl) provides a relatively easy way to configure
+## several small (wireless or wired) network appliances like print servers,
+## USB network servers and wireless bridges.
+##
+## The devices need to use the config-string based protocol described in:
+##    http://wiki.kainhofer.com/hardware/hp2101nw_wlan_printserver
+##    ftp://94.145.20.132/Public/P/print-serv100/091001/FPS-5P-M/Manual/PDF/
+## Supported devices:
+##    -) HP 2101nw wireless G USB print server (USB-id 03f0:cd02), via USB
+##    -) VAP11G wireless bridge, via Ethernet II proto 0x8888
+##
+## Other devices that use the same config format, but are not yet supported:
+##    -) Linksys WPS54G-DE Wireless-G printserver 802.11g USB
+##    -) NetHawk RF-200A
+##    -) SMC2621W-U EZ Connect Wireless Print Server
+##
+## A note to the HP2101nw wireless USB print server:
+## 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...
+
+import device_types
+import user_interface
+import helpers
+
+#use Data::Dumper;
+
+our $debug=0;
+
+our $VERSION=0.01;
+print "appliance_setup.py - version $VERSION\n";
+print "Linux configuration utility for the HP 2101nw wireless G USB print server.\n\n";
+print "(C) 2011 Reinhold Kainhofer <reinhold\@kainhofer.com>\n";
+print "License: GPL v2 or later\n\n";
+
+
+
+# The PC always identifies itself as XXXXXXXX.
+our $thisboxname = "XXXXXXXX";
+
+
+
+###############################################################################
+##  USB DEVICE MANAGEMENT / COMMUNICATION FUNCTIONS
+###############################################################################
+
+
+sub device_detect () {
+  my $usb = Device::USB->new();
+  my @devices;
+  my @devicedescriptions;
+
+  foreach (@supported_devices) {
+    foreach my $dev ($usb->list_devices (@$_)) {
+      push @devices, $dev;
+      push @devicedescriptions, sprintf ("%s / ID %04x:%04x (%s: %s)", $dev->filename(),
+             $dev->idVendor(), $dev->idProduct(),
+             $dev->manufacturer(), $dev->product());
+    }
+  }
+  my $dev;
+  if (scalar (@devices) > 1) {
+    $dev = ask_choice ("Multiple supported devices detected:", \@devicedescriptions, \@devices, $devices[0]);
+  } elsif (scalar (@devices) == 1) {
+    $dev = @devices[0];
+  }
+  if ($dev) {
+    progress ("Using device: %04x:%04x (%s: %s)\n\n",
+             $dev->idVendor(), $dev->idProduct(),
+             $dev->manufacturer(), $dev->product() );
+  } else {
+    progress "\tNo supported devices found\n\n";
+    print "ERROR: No supported device was found...\n\n";
+    print "Supported devices are (USB IDs): \n";
+    foreach (@supported_devices) {
+      printf "\t%04x:%04x (%s)\n", @$_;
+    }
+    print "\n";
+    print "Please connect your HP wireless USB print server to \n";
+    print "the computer with the black USB cable (micro USB plug) \n";
+    print "and run this script again.\n\n";
+  }
+  return $dev;
+}
+
+sub device_open ($) {
+  my $dev = shift;
+  my $res = $dev->open;
+  if ($res<0) {
+    printf "ERROR opening device (%d): %s\n", $res, $!;
+  }
+  $res = $dev->detach_kernel_driver_np(0);
+  # No error check, Linux always gives an error!
+  $res = $dev->set_configuration(1);
+  if ($res<0) {
+    printf "ERROR setting configuration 1 (%d): %s\n", $res, $!;
+  }
+  $res = $dev->claim_interface(0);
+  if ($res<0) {
+    printf "ERROR claiming interface 0 (%d): %s\n", $res, $!;
+  }
+}
+
+sub device_close ($) {
+  my $dev = shift;
+  $dev->release_interface(0);
+  $dev->reset;
+}
+
+
+
+sub bulk_data_write ($$$) {
+  my $dev = shift;
+  my $data = shift;
+  my $len = shift;
+  # Add the header (12 bytes): '@\0', packet length, boxname (="XXXXXXXX") 
+  my $request = sprintf ("@\0%s%s%s", pack ("v", $len), $thisboxname, $data);
+  debug "Request: $request\n";
+  my $res = $dev->bulk_write (1, $request, length($request), 500);
+  if ($res<0) {
+    printf "ERROR write bulk data (%d): %s\n", $res, $!;
+  }
+  return $res;
+}
+
+# Read USB bulk data into the second argument (output argument!), return 
+# size of data
+sub bulk_data_read ($$$) {
+  my $dev = shift;
+  # TODO: Get rid of the length argument, simply read all that is sent
+  # FIXME: Read bulk data in loops until there is nothing left to read?
+  my $len = $_[1];
+  my $readdata = "";
+  my $res = $dev->bulk_read (2, $readdata, $len+13, 500);
+  if ($res<0) {
+    printf "ERROR reading bulk data (%d): %s\n", $res, $!;
+    return;
+  }
+  if ($res == 0) {
+    debug "\tEmpty response received\n";
+    return 0;
+  }
+  debug ("read %d bytes: \n%s\n\n", $res, $readdata); 
+  # Check and cut off the header after some sanity checks:
+  if (substr ($readdata, 0, 3) ne "\@\0\0") {
+    printf "ERROR reading data: Wrong header %s\n", substr ($readdata, 0, 3);
+  }
+  my $datalen = unpack ("v", substr ($readdata, 3, 2));
+  $res = $res-13;
+  if ($datalen != $res) {
+    printf "ERROR reading data: Expected %d bytes of data, got %d\n", $datalen, $res;
+  }
+  my $boxname = substr ($readdata, 5, 8);
+  # FIXME: Check the name of the box...
+  
+  # cut off the header:
+  $_[0]=substr ($readdata, 13);
+  return $res;
+}
+
+sub device_reset ($) {
+  my $dev = shift;
+  progress "Resetting the device, please wait";
+  my $res = bulk_data_write ($dev, "9002:\n", 6);
+  debug "\tSent 9002 reset command: %d\n", $res;
+  my $reset_done = 0;
+  # Poll the device until it doesn't respond. Then sleep for ~15 seconds
+  do {
+    my $readdata = "";
+    $res = $dev->bulk_read (2, $readdata, 13, 100);
+    debug "\tData received: $readdata (len: $res)\n";
+    sleep (1);
+  } while ($res > 0);
+  sleep_progress (20);
+  device_close ($dev);
+  progress "Device was reset. Please press Return.";
+  <STDIN>;
+}
+
+
+###############################################################################
+##  DEVICE CONFIGURATION HANDLING
+###############################################################################
+
+
+sub print_current_configuration ($$) {
+  my $config = shift;
+  my $text = shift;
+  sub val ($$) {
+    my $config = shift;
+    my $var = shift;
+    foreach my $e (@$config) {
+      return $e->[2] if ($e->[0] eq $var);
+    }
+    return undef;
+  }
+  progress $text;
+  printf "\tDevice name:   %s\n", val($config, '0001');
+  if (val($config,'0012') ne "Enable") {
+    printf "\tTCP/IP not yet configured and/or enabled.\n\n";
+    return;
+  }
+  my $adhoc = val($config,'7024') == '1';
+  if ($adhoc) {
+    # ad-hoc network:
+    my $sec = val($config,'7103');
+    printf "\tWireless mode: Ad-hoc network\n";
+    printf "\tSSID:          %s\n", val($config,'7100');
+    printf "\tChannel:       %s\n", val($config,'7102');
+    printf "\tSecurity:      %s\n", security2string ($sec);
+    if ($sec == 0) {
+      # None => nothing to display
+    } elsif ($sec == 1) {
+      # WEP => Print passphrase
+      printf "\tWEP Key:       %s\n", val($config,'7106');
+    } else {
+      # WPA 1/2 or mixed
+      printf "\tEncryption:    %s\n", encryption2string(val($config,'7118'));
+      printf "\tPassphrase:    %s\n", val($config,'7119');
+    }
+  } else {
+    # infrastructure network
+    my $sec = val($config,'7003');
+    printf "\tSSID:          %s\n", val($config,'7000');
+    printf "\tChannel:       %s\n", val($config,'7002');
+    printf "\tSecurity:      %s\n", security2string ($sec);
+    if ($sec == 0) {
+      # None => nothing to display
+    } elsif ($sec == 1) {
+      # WEP => Print passphrase
+      printf "\tAuthentication:  %s\n", authentication2string(val($config,'7012'));
+      printf "\tWEP Key:       %s\n", val($config,'7006');
+    } else {
+      # WPA 1/2 or mixed
+      printf "\tEncryption:    %s\n", encryption2string(val($config,'7018'));
+      printf "\tPassphrase:    %s\n", val($config,'7019');
+    }
+  }
+  
+  my $dhcp = (val($config,'4020') eq "Enable");
+  printf "\tIPv4 method:   %s\n", $dhcp?"DHCP":"manual";
+  my $associated = val($config,'7014') =~ m/STATE:Associated/;
+  if ($associated || !$dhcp) {
+    printf "\tIP address:    %s\n", val($config,'4000');
+    printf "\tGateway:       %s\n", val($config,'4001');
+    printf "\tNetmask:       %s\n", val($config,'4002');
+  }
+  if ($associated) {
+    my $assinfo = val($config,'7014');
+    printf "\tLink state:    %s\n", $assinfo;
+  } else {
+    # Not connected
+    printf "\tWireless not connected\n";
+  }
+  print "\n\n";
+}
+
+
+sub device_config_request ($) {
+  my $dev = shift;
+  my $cfg = "";
+  debug "Reading current configuration...\n";
+  my $res = bulk_data_write ($dev, "9100:\n", 6);
+  $res = bulk_data_read ($dev, $cfg, 25600);
+  return $cfg;
+}
+
+sub device_config_send ($$) {
+  my $dev = shift;
+  my $cfg = shift;
+  my $tmp = "";
+  progress "Sending new configuation to the device. Please wait";
+  debug "\tNew device config: %s\n", $cfg;
+  my $res = bulk_data_write ($dev, $cfg, length($cfg));
+  sleep_progress (10);
+  # Now reset the device:
+  return device_reset ($dev);
+}
+
+sub config_parse ($) {
+  my $cfg = shift;
+  my @entries = split ('\n', $cfg);
+  my @config = ();
+  foreach (@entries) {
+    my $key = substr ($_, 0, 4);
+    (my $name, my $value) = split (/:/, substr ($_, 5), 2);
+    push (@config, [$key, $name, $value]);
+  }
+  return @config;
+}
+
+# Convert a list of the form [ ["OPTIONID", "OPTIONNAME", VALUE], ...]
+# into a string to be sent to the device
+sub config_create ($) {
+  my $cfg = shift;
+  my $result = "";
+  foreach my $o (@$cfg) {
+    $result .= sprintf ("%s %s:%s\n", $o->[0], $o->[1], $o->[2]);
+  }
+  return $result;
+}
+
+sub set_hostname ($) {
+  my $dev = shift;
+  my $hostname = ask_input ("New hostname: ", \&is_hostname,
+      "\tA hostname can only contain A-Z, a-z, 0-9 and -, and may have 1 to 64 characters\n");;
+  my $config = config_create ([["0001", "BOX_NAME", $hostname]]);
+  debug "\tChanging hostname using config\n\t%s\n", $config;
+  return device_config_send ($dev, $config);
+}
+
+sub device_config_save ($$) {
+  my $filename = shift;
+  my $config = shift;
+  my $status = open FILE, ">$filename";
+  if (!$status) {
+    printf "ERROR: %s\n", $!;
+    return;
+  }
+  print FILE $config;
+  progress "Current print server configuration saved to file '%s'\n", $filename;
+}
+
+sub device_config_restore ($$) {
+  my $dev = shift;
+  my $filename = shift;
+  my $status = open FILE, "<$filename";
+  if (!$status) {
+    printf "ERROR: %s\n", $!;
+    return;
+  }
+  my $config = do { local $/; <FILE> };
+  debug "Configuration file '%s' contains the data:\n", $filename, $config;
+  progress "Loaded configuration data from file '%s', sending it to the device.\n";
+  return device_config_send ($dev, $config);
+}
+
+
+
+
+
+
+###############################################################################
+##  WIRELESS NETWORK DETECTION
+###############################################################################
+
+
+sub parse_survey_data ($) {
+  my $survey = shift;
+  my $networks = {};
+  my @entries = split ("\x0b", $survey);
+  foreach (@entries) {
+    (my $ssid, my $settings) = split ("\x0c", $_);
+    my @settings = split (",", $settings);
+    $networks->{$ssid} = {
+      'mac' => $settings[0],
+      'channel' => $settings[1],
+      'wifi_ver' => $settings[2],
+      'secmode' => $settings[3],
+      'signal' => $settings[4],
+      'adhoc' => $settings[5]
+    };
+  }
+  debug Dumper ($networks);
+  return $networks;
+}
+
+sub scan_for_aps ($) {
+  my $dev = shift;
+  progress "Scanning for access points: ";
+  my $loop = 0;
+  my $found = 0;
+  my $survey;
+  while (!$found && $loop <= 4) {
+    ++$loop;
+    debug ("loop=", $loop, "\n");
+    my $res = bulk_data_write ($dev, "9107:\n", 6);
+    $res = bulk_data_read ($dev, my $d="", 0);
+    sleep_progress (6);
+    my $cfg = "";
+    $res = bulk_data_write ($dev, "9100:\n", 6);
+    $res = bulk_data_read ($dev, $cfg, 25600);
+    my @config = config_parse ($cfg);
+    $survey = val(\@config, '7021');
+    $found = (defined ($survey) && length ($survey) > 0);
+  }
+  progress "\n";
+  if ($found) {
+    debug "Found wireless networks: \n", $survey;
+  } else {
+    print "ERROR: No wireless networks detected.\n";
+  }
+  return parse_survey_data ($survey);
+}
+
+sub print_wireless_networks ($) {
+  my $aps = shift;
+  print "Detected wireless networks:\n";
+  my $format = "    %3s %-25s%-9s%-13s%-17s%-8s\n";
+  printf $format, "   ", "SSID", "Signal", "Security", "Type", "channel";
+  print "\t------------------------------------------------------------------------\n";
+  my $i = 0;
+  foreach my $ssid (sort {lc $a cmp lc $b} keys (%$aps)) {
+    ++$i;
+    my $network = $aps->{$ssid};
+    printf $format, $i.")", $ssid, $network->{'signal'}, 
+                    security2string ($network->{'secmode'}),
+                    $network->{'adhoc'}?"Ad-Hoc":"Infrastructure",
+                    $network->{'channel'};
+  }
+  print "\n";
+}
+
+
+
+###############################################################################
+##  INFRASTRUCTURE WIRELESS SETUP (i.e. wlan through access point / router)
+###############################################################################
+
+
+sub wifi_ask_ssid ($) {
+  my $dev = shift;
+  my $aps = scan_for_aps ($dev);
+  print_wireless_networks ($aps);
+  my @ssids = (sort {lc $a cmp lc $b} keys (%$aps));
+  my $newssid;
+  print "Please enter the number or the SSID of the desired wireless network.\n";
+  print "\tEnter '0', 'hidden' or 'h' to connect to a hidden network.\n";
+  print "\tEnter 'r' or 'rescan' to rescan for wireless networks.\n";
+  do {
+    $newssid = input ("Desired wireless network: ");
+    return $newssid if (exists $aps->{$newssid});
+    if ($newssid =~ /^[1-9]\d*$/ && ($newssid <= scalar(@ssids))) {
+      return ($aps,$ssids[$newssid-1]);
+    }
+    if ($newssid eq "0" || $newssid eq "h" || $newssid eq "hidden") {
+      $newssid = "";
+      while (length($newssid) < 1) {
+        $newssid = input ("Please enter the SSID: ");
+      }
+      return ($aps,$newssid);
+    }
+    if ($newssid eq "r" || $newssid eq "rescan") {
+      return wifi_ask_ssid ($dev);
+    }
+  } while 1; # We'll jump out of the loop via return!
+}
+
+sub wifi_ask_address ($$) {
+  my $ap = shift;
+  my $aps = shift;
+  my $dhcp = ask_choice ("IP-address assignment:", ["DHCP", "Manual"], ["Enable", "Disable"], "Enable");
+  my %result = ("dhcp" => $dhcp);
+  if ($dhcp ne "Enable") {
+    $result{'ip_addr'} = ask_ip_address ("IP-address of the device:\t");
+    $result{'mask'}    = ask_ip_address ("Netmask:\t");
+    $result{'gateway'} = ask_ip_address ("IP-address of the gateway:\t");
+  } else {
+    $result{'ip_addr'} = "0.0.0.0";
+    $result{'mask'}    = "0.0.0.0";
+    $result{'gateway'} = "0.0.0.0";
+  }
+  return %result;
+}
+
+sub wifi_ask_security ($$) {
+  my $ap = shift;
+  my $aps = shift;
+  my %result = ();
+  my $sec = 0;
+  if (defined $aps->{$ap}) {
+    $sec = $aps->{$ap}->{'secmode'};
+    $result{'channel'} = $aps->{$ap}->{'channel'};
+  }
+  $sec = ask_choice ("Security: ", ["None", -1, "WEP", "WPA-PSK", "WPA2-PSK", "WPA/WPA2-PSK"], ['0', '1', '2', '4', '6'], $sec);
+  $result{'secmode'} = $sec;
+
+  if ($sec == 0) {
+    # No encryption, nothing to ask the user
+  } elsif ($sec == 1) {
+    # WEP
+    progress "Using WEP security\n";
+    $result{'authen'} = ask_choice ("Type of authentication:", ["Open System","Shared Key"], ['1','2'], '1');
+    $result{'key'} = ask_wep_key ($ap);
+    $result{'key_len'} = length ($result{'key'})/2;
+  } elsif ($sec == 2 || $sec == 4 || $sec == 6 || $sec == 7) {
+    # WPA1 / WPA2
+    progress "Using %s security\n", security2string($sec);
+    $result{'pskalset'} = ask_choice ("Type of encryption:", ["TKIP", "AES"], ['0','1'], '0');
+    $result{'pskkey'} = ask_input ("Please enter the WPA passphrase for network '$ap': ",
+        \&is_wpa_passphrase, "\tEnter a passprase with 1-63 characters!\n");
+  } else {
+    printf "ERROR: security type %s not supported\n", security2string($sec);
+    return wifi_ask_security ($ap, $aps);
+  }
+  return %result;
+}
+
+sub wifi_config_create ($) {
+  my $newconfig = shift;
+  my $config = [
+    ["4000", "IP_ADDR",   $newconfig->{'ip_addr'}],  # IP-address
+    ["4001", "GATEWAY",   $newconfig->{'gateway'}],
+    ["4002", "MASK   ",   $newconfig->{'mask'}],
+    ["4020", "DHCP_MODE", $newconfig->{'dhcp'}],
+    ["4021", "",          $newconfig->{'dhcp'}], # Unknown, == 4020
+    ["4022", "",          $newconfig->{'dhcp'}], # Unknown, == 4020
+    ["5000", "",          ""], # Unknown, always empty
+    ["5003", "",          ""], # Unknown, always empty
+    ["5101", "",          ""], # Unknown, always empty
+    ["5102", "",          ""], # Unknown, always empty
+    ["5104", "",          ""], # Unknown, always empty
+    ["5103", "",          ""], # Unknown, always empty
+    ["7000", "SSID",      $newconfig->{'SSID'}],
+    ["7001", "DOMAIN",    ""],
+    ["7002", "CHANNEL",   $newconfig->{'channel'}],
+    ["7003", "SECMODE",   $newconfig->{'secmode'}],
+    ["7004", "KEY_LEN",   $newconfig->{'key_len'}],
+    ["7005", "DEFAULTKEY","0"],
+    ["7006", "KEY0",      $newconfig->{'key'}],
+    ["7007", "KEY1",      ""],
+    ["7008", "KEY2",      ""],
+    ["7009", "KEY3",      ""],
+    ["7012", "AUTHEN",    $newconfig->{'authen'}],
+    ["7013", "MODE",      "0"],
+    ["7018", "PSKALSET",  $newconfig->{'pskalset'}],
+    ["7019", "PSKKEY",    $newconfig->{'pskkey'}],
+    ["7024", "STAMODE",   "0"],
+    ["7025", "APCHANNEL", "5"],
+    ["7026", "CHMODE",    "1"],
+    ["7030", "WLMODE",    "0"],
+  ];
+  return config_create ($config);
+}
+
+
+
+###############################################################################
+##  AD-HOC WIRELESS SETUP (i.e. direct connection with print server and PC)
+###############################################################################
+
+
+sub adhoc_send_unknown_command ($) {
+  my $dev = shift;
+  bulk_data_write ($dev, "9971:\n", 6);
+  my $val = "";
+  bulk_data_read ($dev, $val, 1);
+  debug "\tResponse to 9971 Ad-hoc setting command (unknown purpose): %s\n", $val;
+  return $val;
+}
+
+sub adhoc_ask_ssid ($) {
+  my $dev = shift;
+  my $ssid = "";
+  print "\n";
+  do {
+    $ssid = input ("SSID of the ad-hoc wireless connection:");
+    if (length ($ssid) > 0) {
+      # scan for wireless networks to check for name clashes
+      my $aps = scan_for_aps ($dev);
+      if (defined ($aps->{$ssid})) {
+        print "\tERROR: wireless network '$ssid' already exists, please choose a different name.\n\n";
+      } else {
+        return $ssid;
+      }
+    }
+  } while 1; # We'll jump out of the loop via return!
+}
+
+def adhoc_ask_channel (ap):
+  channel = False
+  while (!channel):
+    c = input ("Wireless channel for network '%s' [auto or 0-13]: " % ap);
+    if (c == "auto"):
+      channel = "6"   # FIXME
+    elif (re.match ('^([0-9]|1[0-3])$', c)):
+      channel = c
+  debug ("\tNew channel: %s\n" % channel)
+  return channel
+
+sub adhoc_config_create ($) {
+  my $newconfig = shift;
+  my $config = [
+    ["7024", "STAMODE",    "1"],
+    ["7025", "APCHANNEL",  $newconfig->{'channel'}],
+    ["7026", "CHMODE",     "1"], # FIXME: Shall we change this?
+    ["7030", "WLMODE",     "1"], # FIXME: Shall we change this?
+    ["7100", "APSSID",     $newconfig->{'SSID'}],
+    ["7103", "APSECMODE",  $newconfig->{'secmode'}],
+    ["7104", "APKEY_LEN",  $newconfig->{'key_len'}],
+    ["7105", "APDEFAULTKE","0"],
+    ["7106", "APKEY0",     $newconfig->{'key'}],
+    ["7107", "APKEY1",     ""],
+    ["7108", "APKEY2",     ""],
+    ["7109", "APKEY3",     ""],
+    ["7112", "APAUTHEN",   $newconfig->{'authen'}],
+    ["7118", "APPSKALSET", $newconfig->{'pskalset'}],
+    ["7119", "APPSKKEY",   $newconfig->{'pskkey'}]
+  ];
+  return config_create ($config);
+}
+
+
+
+###############################################################################
+##  MAIN OPERATIONS: ASK FOR OPERATION AND HANDLE IT
+###############################################################################
+
+
+sub ask_operation () {
+  return ask_choice (
+    "What do you want to do?", 
+    ["Setup a wireless connection through a wifi router or access point ('infrastructure')",
+     "Setup a direct wireless connection with the printer ('ad-hoc')",
+     -1,
+     "Change the name of the device",
+     -1,
+     "Save the device configuration to a file",
+     "Restore the device configuration from a file",
+     -1,
+     "Reload the configuration",
+     "Exit"
+    ],
+    ["wifi_infrastructure",
+     "wifi_adhoc",
+     "change_hostname",
+     "config_save",
+     "config_restore",
+     "reload",
+     "exit"
+    ],
+    "exit");
+}
+
+sub main () {
+  my $reconfigured = 0;
+  my $dev = device_detect () or exit;
+  device_open ($dev);
+  my $cfg = device_config_request ($dev);
+  my @config = config_parse ($cfg);
+  print_current_configuration (\@config, "Current configuration of the device:\n");
+  # Ask what to do (set wifi AP, ad-hoc, save, restore)
+  my $operation = ask_operation ();
+  debug ("\tSelected operation: %s\n", $operation);
+  if ($operation eq "wifi_infrastructure") {
+    # Infrastructure, scan for APs
+    my ($aps,$newap) = wifi_ask_ssid ($dev);
+    my %newconfig = ('SSID' => $newap);
+    debug ("\tSelected wireless network: %s\n", $newap);
+    my %security = wifi_ask_security ($newap, $aps);
+    my %ip = wifi_ask_address ($newap, $aps);
+    %newconfig = (%newconfig, %security, %ip);
+    my $newcfg = wifi_config_create (\%newconfig);
+    device_config_send ($dev, $newcfg);
+    $reconfigured = 1;
+  } elsif ($operation eq "wifi_adhoc") {
+    # Ad-hoc connection to print server
+    my $adhocssid = adhoc_ask_ssid ($dev);
+    adhoc_send_unknown_command ($dev);
+    my $channel = adhoc_ask_channel ($dev, $adhocssid);
+    my %newconfig = ('SSID' => $adhocssid,
+                     'channel' => $channel,
+                     adhoc_ask_security ($adhocssid)
+                    );
+    my $newcfg = adhoc_config_create (\%newconfig);
+    device_config_send ($dev, $newcfg);
+    $reconfigured = 1;
+  } elsif ($operation eq "change_hostname") {
+    set_hostname ($dev);
+    $reconfigured = 1;
+  } elsif ($operation eq "config_save") {
+    my $filename = ask_input ("Filename to save to: ", \&is_filename, "");
+    device_config_save ($filename, $cfg);
+  } elsif ($operation eq "config_restore") {
+    my $filename = ask_input ("Filename to load from: ", \&is_filename, "");
+    device_config_restore ($dev, $filename);
+  } elsif ($operation eq "reload") {
+    # do nothing, we'll call main below...
+  } elsif ($operation eq "exit") {
+    device_close ($dev);
+    exit;
+  } else {
+    printf "ERROR: unknown operation %s\n", $operation;
+  }
+  device_close ($dev);
+  # if the configuration was changed, print the new config after the device was reset
+  $dev = device_detect () or exit;
+  device_open ($dev);
+  @config = config_parse (device_config_request ($dev));
+  print_current_configuration (\@config, "New configuration of the device:\n");
+  
+  # Loop in main if "reload" is selected...
+  main () if ($operation eq "reload");
+}
+
+
+
+
+###############################################################################
+##  MAIN FUNCTION
+###############################################################################
+
+main ();
diff --git a/appliance_setup/configuration.py b/appliance_setup/configuration.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab58c0d388792bcd4931d8c90eb079da4fbcca95
--- /dev/null
+++ b/appliance_setup/configuration.py
@@ -0,0 +1,510 @@
+#!/usr/bin/python
+## configuration.py - version 0.01
+## 
+## Linux configuration utility for several small network appliances.
+##
+## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
+## License: GPL v2 or later
+
+
+class Config:
+  self.cfgdata = [];
+  self.cfgdesc = [];
+  def __init__ (self, ui):
+    self.ui = ui
+    
+  def val (self, line):
+    return self.cfgdata.get (line, None)
+
+  def print_configuration (self):
+    # TODO: ui.progress (text);
+    ui.print ("\tDevice name:   %(0001)s\n" % self.cfgdata);
+    if (val('0012') != "Enable"):
+      ui.print "\tTCP/IP not yet configured and/or enabled.\n\n";
+      return
+    adhoc = (val('7024') == '1');
+    if adhoc:
+      # ad-hoc network:
+      my $sec = val($config,'7103');
+      printf "\tWireless mode: Ad-hoc network\n";
+      printf "\tSSID:          %s\n", val($config,'7100');
+      printf "\tChannel:       %s\n", val($config,'7102');
+      printf "\tSecurity:      %s\n", security2string ($sec);
+      if ($sec == 0) {
+        # None => nothing to display
+      } elsif ($sec == 1) {
+        # WEP => Print passphrase
+        printf "\tWEP Key:       %s\n", val($config,'7106');
+      } else {
+        # WPA 1/2 or mixed
+        printf "\tEncryption:    %s\n", encryption2string(val($config,'7118'));
+        printf "\tPassphrase:    %s\n", val($config,'7119');
+      }
+    } else {
+      # infrastructure network
+      my $sec = val($config,'7003');
+      printf "\tSSID:          %s\n", val($config,'7000');
+      printf "\tChannel:       %s\n", val($config,'7002');
+      printf "\tSecurity:      %s\n", security2string ($sec);
+      if ($sec == 0) {
+        # None => nothing to display
+      } elsif ($sec == 1) {
+        # WEP => Print passphrase
+        printf "\tAuthentication:  %s\n", authentication2string(val($config,'7012'));
+        printf "\tWEP Key:       %s\n", val($config,'7006');
+      } else {
+        # WPA 1/2 or mixed
+        printf "\tEncryption:    %s\n", encryption2string(val($config,'7018'));
+        printf "\tPassphrase:    %s\n", val($config,'7019');
+      }
+    }
+  
+    my $dhcp = (val($config,'4020') eq "Enable");
+    printf "\tIPv4 method:   %s\n", $dhcp?"DHCP":"manual";
+    my $associated = val($config,'7014') =~ m/STATE:Associated/;
+    if ($associated || !$dhcp) {
+      printf "\tIP address:    %s\n", val($config,'4000');
+      printf "\tGateway:       %s\n", val($config,'4001');
+      printf "\tNetmask:       %s\n", val($config,'4002');
+    }
+    if ($associated) {
+      my $assinfo = val($config,'7014');
+      printf "\tLink state:    %s\n", $assinfo;
+    } else {
+      # Not connected
+      printf "\tWireless not connected\n";
+    }
+    print "\n\n";
+    
+  pass
+
+
+###############################################################################
+##  DEVICE CONFIGURATION HANDLING
+###############################################################################
+
+
+sub config_parse ($) {
+  my $cfg = shift;
+  my @entries = split ('\n', $cfg);
+  my @config = ();
+  foreach (@entries) {
+    my $key = substr ($_, 0, 4);
+    (my $name, my $value) = split (/:/, substr ($_, 5), 2);
+    push (@config, [$key, $name, $value]);
+  }
+  return @config;
+}
+
+# Convert a list of the form [ ["OPTIONID", "OPTIONNAME", VALUE], ...]
+# into a string to be sent to the device
+sub config_create ($) {
+  my $cfg = shift;
+  my $result = "";
+  foreach my $o (@$cfg) {
+    $result .= sprintf ("%s %s:%s\n", $o->[0], $o->[1], $o->[2]);
+  }
+  return $result;
+}
+
+sub set_hostname ($) {
+  my $dev = shift;
+  my $hostname = ask_input ("New hostname: ", \&is_hostname,
+      "\tA hostname can only contain A-Z, a-z, 0-9 and -, and may have 1 to 64 characters\n");;
+  my $config = config_create ([["0001", "BOX_NAME", $hostname]]);
+  debug "\tChanging hostname using config\n\t%s\n", $config;
+  return device_config_send ($dev, $config);
+}
+
+sub device_config_save ($$) {
+  my $filename = shift;
+  my $config = shift;
+  my $status = open FILE, ">$filename";
+  if (!$status) {
+    printf "ERROR: %s\n", $!;
+    return;
+  }
+  print FILE $config;
+  progress "Current print server configuration saved to file '%s'\n", $filename;
+}
+
+sub device_config_restore ($$) {
+  my $dev = shift;
+  my $filename = shift;
+  my $status = open FILE, "<$filename";
+  if (!$status) {
+    printf "ERROR: %s\n", $!;
+    return;
+  }
+  my $config = do { local $/; <FILE> };
+  debug "Configuration file '%s' contains the data:\n", $filename, $config;
+  progress "Loaded configuration data from file '%s', sending it to the device.\n";
+  return device_config_send ($dev, $config);
+}
+
+
+
+
+
+
+###############################################################################
+##  WIRELESS NETWORK DETECTION
+###############################################################################
+
+
+sub parse_survey_data ($) {
+  my $survey = shift;
+  my $networks = {};
+  my @entries = split ("\x0b", $survey);
+  foreach (@entries) {
+    (my $ssid, my $settings) = split ("\x0c", $_);
+    my @settings = split (",", $settings);
+    $networks->{$ssid} = {
+      'mac' => $settings[0],
+      'channel' => $settings[1],
+      'wifi_ver' => $settings[2],
+      'secmode' => $settings[3],
+      'signal' => $settings[4],
+      'adhoc' => $settings[5]
+    };
+  }
+  debug Dumper ($networks);
+  return $networks;
+}
+
+sub scan_for_aps ($) {
+  my $dev = shift;
+  progress "Scanning for access points: ";
+  my $loop = 0;
+  my $found = 0;
+  my $survey;
+  while (!$found && $loop <= 4) {
+    ++$loop;
+    debug ("loop=", $loop, "\n");
+    my $res = bulk_data_write ($dev, "9107:\n", 6);
+    $res = bulk_data_read ($dev, my $d="", 0);
+    sleep_progress (6);
+    my $cfg = "";
+    $res = bulk_data_write ($dev, "9100:\n", 6);
+    $res = bulk_data_read ($dev, $cfg, 25600);
+    my @config = config_parse ($cfg);
+    $survey = val(\@config, '7021');
+    $found = (defined ($survey) && length ($survey) > 0);
+  }
+  progress "\n";
+  if ($found) {
+    debug "Found wireless networks: \n", $survey;
+  } else {
+    print "ERROR: No wireless networks detected.\n";
+  }
+  return parse_survey_data ($survey);
+}
+
+sub print_wireless_networks ($) {
+  my $aps = shift;
+  print "Detected wireless networks:\n";
+  my $format = "    %3s %-25s%-9s%-13s%-17s%-8s\n";
+  printf $format, "   ", "SSID", "Signal", "Security", "Type", "channel";
+  print "\t------------------------------------------------------------------------\n";
+  my $i = 0;
+  foreach my $ssid (sort {lc $a cmp lc $b} keys (%$aps)) {
+    ++$i;
+    my $network = $aps->{$ssid};
+    printf $format, $i.")", $ssid, $network->{'signal'}, 
+                    security2string ($network->{'secmode'}),
+                    $network->{'adhoc'}?"Ad-Hoc":"Infrastructure",
+                    $network->{'channel'};
+  }
+  print "\n";
+}
+
+
+
+###############################################################################
+##  INFRASTRUCTURE WIRELESS SETUP (i.e. wlan through access point / router)
+###############################################################################
+
+
+sub wifi_ask_ssid ($) {
+  my $dev = shift;
+  my $aps = scan_for_aps ($dev);
+  print_wireless_networks ($aps);
+  my @ssids = (sort {lc $a cmp lc $b} keys (%$aps));
+  my $newssid;
+  print "Please enter the number or the SSID of the desired wireless network.\n";
+  print "\tEnter '0', 'hidden' or 'h' to connect to a hidden network.\n";
+  print "\tEnter 'r' or 'rescan' to rescan for wireless networks.\n";
+  do {
+    $newssid = input ("Desired wireless network: ");
+    return $newssid if (exists $aps->{$newssid});
+    if ($newssid =~ /^[1-9]\d*$/ && ($newssid <= scalar(@ssids))) {
+      return ($aps,$ssids[$newssid-1]);
+    }
+    if ($newssid eq "0" || $newssid eq "h" || $newssid eq "hidden") {
+      $newssid = "";
+      while (length($newssid) < 1) {
+        $newssid = input ("Please enter the SSID: ");
+      }
+      return ($aps,$newssid);
+    }
+    if ($newssid eq "r" || $newssid eq "rescan") {
+      return wifi_ask_ssid ($dev);
+    }
+  } while 1; # We'll jump out of the loop via return!
+}
+
+sub wifi_ask_address ($$) {
+  my $ap = shift;
+  my $aps = shift;
+  my $dhcp = ask_choice ("IP-address assignment:", ["DHCP", "Manual"], ["Enable", "Disable"], "Enable");
+  my %result = ("dhcp" => $dhcp);
+  if ($dhcp ne "Enable") {
+    $result{'ip_addr'} = ask_ip_address ("IP-address of the device:\t");
+    $result{'mask'}    = ask_ip_address ("Netmask:\t");
+    $result{'gateway'} = ask_ip_address ("IP-address of the gateway:\t");
+  } else {
+    $result{'ip_addr'} = "0.0.0.0";
+    $result{'mask'}    = "0.0.0.0";
+    $result{'gateway'} = "0.0.0.0";
+  }
+  return %result;
+}
+
+sub wifi_ask_security ($$) {
+  my $ap = shift;
+  my $aps = shift;
+  my %result = ();
+  my $sec = 0;
+  if (defined $aps->{$ap}) {
+    $sec = $aps->{$ap}->{'secmode'};
+    $result{'channel'} = $aps->{$ap}->{'channel'};
+  }
+  $sec = ask_choice ("Security: ", ["None", -1, "WEP", "WPA-PSK", "WPA2-PSK", "WPA/WPA2-PSK"], ['0', '1', '2', '4', '6'], $sec);
+  $result{'secmode'} = $sec;
+
+  if ($sec == 0) {
+    # No encryption, nothing to ask the user
+  } elsif ($sec == 1) {
+    # WEP
+    progress "Using WEP security\n";
+    $result{'authen'} = ask_choice ("Type of authentication:", ["Open System","Shared Key"], ['1','2'], '1');
+    $result{'key'} = ask_wep_key ($ap);
+    $result{'key_len'} = length ($result{'key'})/2;
+  } elsif ($sec == 2 || $sec == 4 || $sec == 6 || $sec == 7) {
+    # WPA1 / WPA2
+    progress "Using %s security\n", security2string($sec);
+    $result{'pskalset'} = ask_choice ("Type of encryption:", ["TKIP", "AES"], ['0','1'], '0');
+    $result{'pskkey'} = ask_input ("Please enter the WPA passphrase for network '$ap': ",
+        \&is_wpa_passphrase, "\tEnter a passprase with 1-63 characters!\n");
+  } else {
+    printf "ERROR: security type %s not supported\n", security2string($sec);
+    return wifi_ask_security ($ap, $aps);
+  }
+  return %result;
+}
+
+sub wifi_config_create ($) {
+  my $newconfig = shift;
+  my $config = [
+    ["4000", "IP_ADDR",   $newconfig->{'ip_addr'}],  # IP-address
+    ["4001", "GATEWAY",   $newconfig->{'gateway'}],
+    ["4002", "MASK   ",   $newconfig->{'mask'}],
+    ["4020", "DHCP_MODE", $newconfig->{'dhcp'}],
+    ["4021", "",          $newconfig->{'dhcp'}], # Unknown, == 4020
+    ["4022", "",          $newconfig->{'dhcp'}], # Unknown, == 4020
+    ["5000", "",          ""], # Unknown, always empty
+    ["5003", "",          ""], # Unknown, always empty
+    ["5101", "",          ""], # Unknown, always empty
+    ["5102", "",          ""], # Unknown, always empty
+    ["5104", "",          ""], # Unknown, always empty
+    ["5103", "",          ""], # Unknown, always empty
+    ["7000", "SSID",      $newconfig->{'SSID'}],
+    ["7001", "DOMAIN",    ""],
+    ["7002", "CHANNEL",   $newconfig->{'channel'}],
+    ["7003", "SECMODE",   $newconfig->{'secmode'}],
+    ["7004", "KEY_LEN",   $newconfig->{'key_len'}],
+    ["7005", "DEFAULTKEY","0"],
+    ["7006", "KEY0",      $newconfig->{'key'}],
+    ["7007", "KEY1",      ""],
+    ["7008", "KEY2",      ""],
+    ["7009", "KEY3",      ""],
+    ["7012", "AUTHEN",    $newconfig->{'authen'}],
+    ["7013", "MODE",      "0"],
+    ["7018", "PSKALSET",  $newconfig->{'pskalset'}],
+    ["7019", "PSKKEY",    $newconfig->{'pskkey'}],
+    ["7024", "STAMODE",   "0"],
+    ["7025", "APCHANNEL", "5"],
+    ["7026", "CHMODE",    "1"],
+    ["7030", "WLMODE",    "0"],
+  ];
+  return config_create ($config);
+}
+
+
+
+###############################################################################
+##  AD-HOC WIRELESS SETUP (i.e. direct connection with print server and PC)
+###############################################################################
+
+
+sub adhoc_send_unknown_command ($) {
+  my $dev = shift;
+  bulk_data_write ($dev, "9971:\n", 6);
+  my $val = "";
+  bulk_data_read ($dev, $val, 1);
+  debug "\tResponse to 9971 Ad-hoc setting command (unknown purpose): %s\n", $val;
+  return $val;
+}
+
+sub adhoc_ask_ssid ($) {
+  my $dev = shift;
+  my $ssid = "";
+  print "\n";
+  do {
+    $ssid = input ("SSID of the ad-hoc wireless connection:");
+    if (length ($ssid) > 0) {
+      # scan for wireless networks to check for name clashes
+      my $aps = scan_for_aps ($dev);
+      if (defined ($aps->{$ssid})) {
+        print "\tERROR: wireless network '$ssid' already exists, please choose a different name.\n\n";
+      } else {
+        return $ssid;
+      }
+    }
+  } while 1; # We'll jump out of the loop via return!
+}
+
+def adhoc_ask_channel (ap):
+  channel = False
+  while (!channel):
+    c = input ("Wireless channel for network '%s' [auto or 0-13]: " % ap);
+    if (c == "auto"):
+      channel = "6"   # FIXME
+    elif (re.match ('^([0-9]|1[0-3])$', c)):
+      channel = c
+  debug ("\tNew channel: %s\n" % channel)
+  return channel
+
+sub adhoc_config_create ($) {
+  my $newconfig = shift;
+  my $config = [
+    ["7024", "STAMODE",    "1"],
+    ["7025", "APCHANNEL",  $newconfig->{'channel'}],
+    ["7026", "CHMODE",     "1"], # FIXME: Shall we change this?
+    ["7030", "WLMODE",     "1"], # FIXME: Shall we change this?
+    ["7100", "APSSID",     $newconfig->{'SSID'}],
+    ["7103", "APSECMODE",  $newconfig->{'secmode'}],
+    ["7104", "APKEY_LEN",  $newconfig->{'key_len'}],
+    ["7105", "APDEFAULTKE","0"],
+    ["7106", "APKEY0",     $newconfig->{'key'}],
+    ["7107", "APKEY1",     ""],
+    ["7108", "APKEY2",     ""],
+    ["7109", "APKEY3",     ""],
+    ["7112", "APAUTHEN",   $newconfig->{'authen'}],
+    ["7118", "APPSKALSET", $newconfig->{'pskalset'}],
+    ["7119", "APPSKKEY",   $newconfig->{'pskkey'}]
+  ];
+  return config_create ($config);
+}
+
+
+
+###############################################################################
+##  MAIN OPERATIONS: ASK FOR OPERATION AND HANDLE IT
+###############################################################################
+
+
+sub ask_operation () {
+  return ask_choice (
+    "What do you want to do?", 
+    ["Setup a wireless connection through a wifi router or access point ('infrastructure')",
+     "Setup a direct wireless connection with the printer ('ad-hoc')",
+     -1,
+     "Change the name of the device",
+     -1,
+     "Save the device configuration to a file",
+     "Restore the device configuration from a file",
+     -1,
+     "Reload the configuration",
+     "Exit"
+    ],
+    ["wifi_infrastructure",
+     "wifi_adhoc",
+     "change_hostname",
+     "config_save",
+     "config_restore",
+     "reload",
+     "exit"
+    ],
+    "exit");
+}
+
+sub main () {
+  my $reconfigured = 0;
+  my $dev = device_detect () or exit;
+  device_open ($dev);
+  my $cfg = device_config_request ($dev);
+  my @config = config_parse ($cfg);
+  print_current_configuration (\@config, "Current configuration of the device:\n");
+  # Ask what to do (set wifi AP, ad-hoc, save, restore)
+  my $operation = ask_operation ();
+  debug ("\tSelected operation: %s\n", $operation);
+  if ($operation eq "wifi_infrastructure") {
+    # Infrastructure, scan for APs
+    my ($aps,$newap) = wifi_ask_ssid ($dev);
+    my %newconfig = ('SSID' => $newap);
+    debug ("\tSelected wireless network: %s\n", $newap);
+    my %security = wifi_ask_security ($newap, $aps);
+    my %ip = wifi_ask_address ($newap, $aps);
+    %newconfig = (%newconfig, %security, %ip);
+    my $newcfg = wifi_config_create (\%newconfig);
+    device_config_send ($dev, $newcfg);
+    $reconfigured = 1;
+  } elsif ($operation eq "wifi_adhoc") {
+    # Ad-hoc connection to print server
+    my $adhocssid = adhoc_ask_ssid ($dev);
+    adhoc_send_unknown_command ($dev);
+    my $channel = adhoc_ask_channel ($dev, $adhocssid);
+    my %newconfig = ('SSID' => $adhocssid,
+                     'channel' => $channel,
+                     adhoc_ask_security ($adhocssid)
+                    );
+    my $newcfg = adhoc_config_create (\%newconfig);
+    device_config_send ($dev, $newcfg);
+    $reconfigured = 1;
+  } elsif ($operation eq "change_hostname") {
+    set_hostname ($dev);
+    $reconfigured = 1;
+  } elsif ($operation eq "config_save") {
+    my $filename = ask_input ("Filename to save to: ", \&is_filename, "");
+    device_config_save ($filename, $cfg);
+  } elsif ($operation eq "config_restore") {
+    my $filename = ask_input ("Filename to load from: ", \&is_filename, "");
+    device_config_restore ($dev, $filename);
+  } elsif ($operation eq "reload") {
+    # do nothing, we'll call main below...
+  } elsif ($operation eq "exit") {
+    device_close ($dev);
+    exit;
+  } else {
+    printf "ERROR: unknown operation %s\n", $operation;
+  }
+  device_close ($dev);
+  # if the configuration was changed, print the new config after the device was reset
+  $dev = device_detect () or exit;
+  device_open ($dev);
+  @config = config_parse (device_config_request ($dev));
+  print_current_configuration (\@config, "New configuration of the device:\n");
+  
+  # Loop in main if "reload" is selected...
+  main () if ($operation eq "reload");
+}
+
+
+
+
+###############################################################################
+##  MAIN FUNCTION
+###############################################################################
+
+main ();
diff --git a/appliance_setup/device.py b/appliance_setup/device.py
new file mode 100644
index 0000000000000000000000000000000000000000..18fd6f393aea768d9726e74750db30b2c8aaa3f7
--- /dev/null
+++ b/appliance_setup/device.py
@@ -0,0 +1,2 @@
+class Device:
+    def __init__ (
\ No newline at end of file
diff --git a/appliance_setup/device_types.py b/appliance_setup/device_types.py
new file mode 100755
index 0000000000000000000000000000000000000000..2d443560682a5e9c592a51c33086c1ab5fb0f2e7
--- /dev/null
+++ b/appliance_setup/device_types.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+## appliance_setup/device_types.py - version 0.01
+##
+## Device type definitions for appliance_setup.py
+##
+## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
+## License: GPL v2 or later
+
+import usb
+
+## Base class
+class DeviceType:
+    def __init__ (self):
+      pass
+
+    def detect_devices (self):
+      pass
+
+    def device_open (self):
+      pass
+    def device_close (self):
+      pass
+    def config_send (self, config):
+      pass
+    def config_retrieve (self):
+      pass
+    def device_reset (self):
+      pass
+    def scan_for_aps (self):
+      pass
+    def print_supported_devices (self):
+      pass
+
+## USB-based devices like the HP2101nw
+class USBDevice (DeviceType):
+    supported_devices = (
+      {'vendor_id':0x03f0, 'product_id':0xcd02, 'desc':"HP 2101nw wireless G USB print server"}
+    );
+    dev = None;
+    def __init__ (self):
+      DeviceType.__init__ ()
+      
+    def detect_devices ():
+      devices = ();
+      for d in self.supported_devices:
+        for dev in (usb.list_devices (d['vendor_id'], d['product_id'])):
+          devices.append (dev);
+          devicedescriptions.append ("%s / ID %04x:%04x (%s: %s)" % (dev.filename(),
+             dev.idVendor(), dev.idProduct(),
+             dev.manufacturer(), dev.product());
+      
+      if len(devices) > 1:
+        dev = ask_choice ("Multiple supported devices detected:", devicedescriptions, devices, devices[0]);
+      else:
+        def = devices[0];
+
+      if dev:
+        progress ("Using device: %04x:%04x (%s: %s)\n\n",
+               dev.idVendor(), dev.idProduct(),
+               dev.manufacturer(), dev.product() );
+      else:
+        progress "\tNo supported devices found\n\n";
+        print "ERROR: No supported device was found...\n\n";
+        print "Supported devices are (USB IDs): \n";
+        for d in (self.supported_devices):
+          print "\t%(vendor_id)04x:%(product_id)04x (%(desc)s)\n" % d;
+        print "\n";
+        print "Please connect your HP wireless USB print server to \n";
+        print "the computer with the black USB cable (micro USB plug) \n";
+        print "and run this script again.\n\n";
+      return dev;
+
+    def device_open () {
+      res = self.dev->open;
+      if (res<0) {
+    printf "ERROR opening device (%d): %s\n", $res, $!;
+  }
+  $res = $dev->detach_kernel_driver_np(0);
+  # No error check, Linux always gives an error!
+  $res = $dev->set_configuration(1);
+  if ($res<0) {
+    printf "ERROR setting configuration 1 (%d): %s\n", $res, $!;
+  }
+  $res = $dev->claim_interface(0);
+  if ($res<0) {
+    printf "ERROR claiming interface 0 (%d): %s\n", $res, $!;
+  }
+}
+
+sub device_close ($) {
+  my $dev = shift;
+  $dev->release_interface(0);
+  $dev->reset;
+}
+
+
+## ethernet devices (using ethernet II proto 0x8888), like the VAP11G
+class Ether8888Device (DeviceType):
+    def __init__ (self):
+      DeviceType.__init__ ();
+
+## FTP-based devices like ???
+class FTPDevice (DeviceType):
+    def __init__ (self):
+      DeviceType.__init__ ();
diff --git a/appliance_setup/helpers.py b/appliance_setup/helpers.py
new file mode 100755
index 0000000000000000000000000000000000000000..222811432f33aea5a316f02f0711ec15145673d5
--- /dev/null
+++ b/appliance_setup/helpers.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+## appliance_setup/helpers.py - version 0.01
+##
+## Helper functions or appliance_setup
+##
+## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
+## License: GPL v2 or later
+
+
+def trim (str):
+  return str.strip ()
+
+def is_ip_address (ip):
+  return re.match ('((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$', ip)
+
+def security2string (sec):
+  return {
+    0 => "None",
+    1 => "WEP",
+    2 => "WPA-PSK",
+    4 => "WPA2-PSK", # WPA2-PSK TKIP
+    6 => "WPA/WPA2-PSK", # WPA/WPA2 and WPA2-PSK CCMP
+    7 => "WPA-EAP",
+    8 => "WPA2-EAP"
+  }.get (sec, "Unknown (%d)" % sec)
+
+def encryption2string (enc):
+  return {0: "TKIP", 1: "AES"}.get (enc, "Unknown (%d)" % enc)
+
+def authentication2string (auth):
+  return {1: "Open System", 2: "Shared Key"}.get (auth, "Unknown (%d)" % auth)
+
+def is_wep_key (key):
+  return (re.match("[0-9a-fA-F]{10}$", key) || re.match('[0-9a-fA-F]{26}$', key))
+
+def is_wpa_passphrase (passphrase):
+  return (0 < len (passphrase) && len (passphrase) < 64)
+
+def is_filename (fn):
+  return (len(fn)>0)
+
+def is_hostname (hostname):
+  return re.match("[-A-Za-z0-9]{1,63}$", hostname)
diff --git a/appliance_setup/user_interface.py b/appliance_setup/user_interface.py
new file mode 100755
index 0000000000000000000000000000000000000000..52d397867fae131460adf6c82f2741788035fa89
--- /dev/null
+++ b/appliance_setup/user_interface.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+## appliance_setup/user_interface.py - version 0.01
+##
+## User interface definitions for appliance_setup.py. Currently only a 
+## console-based user interface is implementd.
+##
+## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>
+## License: GPL v2 or later
+
+class UserInterface:
+    def __init__ (self, debug):
+      self.debug = debug;
+    def progress (self, str):
+      pass
+    def debug (self, str):
+      pass
+    def input (self, str):
+      pass
+    ## Sleep for dur seconds, show some kind of progress bar / dots
+    def sleep_progress (self, dur):
+      pass
+    ## Give the user a number of choices to select from.
+    ##   text ...    text to print before the options
+    ##   options ... array ref containing human-readable option values (-1 for separator)
+    ##   values ...  array ref with option values (-1 in $options are ignored!)
+    ##   default ... default value if empty input is given
+    def ask_choice (self, text, options, values, default):
+      pass
+    # Ask the user for some text input, which will be validated (if invalid input
+    # is given, the user is notified and asked again.
+    #     text ...      text to ask the user
+    #     validator ... callback function to validate the input
+    #     message ...   error message printed if validation fails.
+    def ask_input (self, text, validator, message):
+      pass
+    def display_device_settings (self):
+      pass
+    def print_wireless_networks (self):
+      pass
+    def ask_operation (self):
+      pass
+    def ask_ip_address (self, string):
+      pass
+    def ask_wep_key (self, ap):
+      pass
+
+class ConsoleInterface (UserInterface):
+    def __init__ (self, debug):
+      UserInterface.__init__ (debug);
+    def progress (self, str):
+      print str;
+    def debug (self, str):
+      if (self.debug):
+        print str;
+    def input (self, str):
+      s = raw_input (str).strip ();
+      return s
+
+    def sleep_progress (self, dur):
+      for i in range(dur):
+        progress "."
+        sleep (1);
+      progress "\n"
+
+    def ask_choice (self, text, options, values, default):
+      print "%s\n" % text
+      i = 1
+      defindex = 1
+      for o in options:
+        if (o == -1):
+          print "\t---\n"
+        else:
+          print "\t%d) %s\n" % (i, o)
+          if (values[i-1] == default):
+            defindex = i
+          i += 1
+      while (retval == undefined):
+        op = self.input ("Your choice (default: %d): " % i);
+        if (op == ""):
+          retval = default;
+        elif (re.match ('[1-9][0-9]*$', op) && ($op <= len (values))):
+          retval = values.get (op-1);
+        else:
+          print "\tInvalid input. Please enter a number from 1 to %d, or simply press Return to use the default.\n" % len(values);
+      return retval;
+
+    # Ask the user for some text input, which will be validated (if invalid input
+    # is given, the user is notified and asked again.
+    #     text ...      text to ask the user
+    #     validator ... callback function to validate the input
+    #     message ...   error message printed if validation fails.
+    def ask_input (self, text, validator, message):
+      retval = ""
+      valid = False
+      while (!valid):
+        retval = self.input (text)
+        valid = validator (retval)
+        if (!valid):
+          print message % retval
+      return retval
+
+    def display_device_settings (self):
+      pass
+    def print_wireless_networks (self):
+      pass
+    def ask_operation (self):
+      pass
+
+    def ask_ip_address (self, string):
+      return self.ask_input (string, self.is_ip_address, "\tInvalid input %s. Please enter a string of the form xxx.xxx.xxx.xxx, where each xxx is a number from 0 to 255.\n")
+
+    def ask_wep_key (self, ap):
+      return self.ask_input (
+        "Please enter the WEP key (10 or 26 characters, 0-9 and a-f) for network '%s': " % ap,
+        helpers.is_wep_key,
+        "\tInvalid key %s. Length can only be 10 or 26, use only digits 0-9 and letters a-f.\n\n")