diff --git a/hp2101nw_setup.pl b/hp2101nw_setup.pl index 4b964c11577194c827561d60f669e31312e19e6d..16a3a8c0535d08648fc7d3b6124344a818452290 100755 --- a/hp2101nw_setup.pl +++ b/hp2101nw_setup.pl @@ -7,18 +7,17 @@ use strict; use warnings; my $debug=1; +# my $debug=0; +our $VERSION=0.01; +print "hp2101nw_setup.pl - 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"; -=head1 NAME - -hp2101nw_setup.pl - Configure the HP 2101nw wireless G USB print server - -=head1 VERSION - -Version 0.01 -=cut -our $VERSION=0.01; +our $thisboxname = "XXXXXXXX"; +$| = 1; =head1 SYNOPSIS @@ -34,39 +33,51 @@ http://wiki.kainhofer.com/hardware/hp2101nw_wlan_printserver =cut -print "hp2101nw_setup.pl - 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"; - $Data::Dumper::Indent = 1; ## no critic(ProhibitPackageVars) # data structure: VendorID, ProductID, BULK_IN_EP, BULK_OUT_EP my @supported_devices=( [0x03f0, 0xcd02] ); +my $var_names = {}; +sub progress { +# my $str = shift; + printf (@_); +} +sub debug { + if ($debug) { + printf (@_); + } +} +sub trim($) { + my $string = shift; + $string =~ s/^\s+//; + $string =~ s/\s+$//; + return $string; +} sub device_detect () { my $usb = Device::USB->new(); my $dev; - print "Detected devices:\n"; + progress "Detected devices:\n"; foreach (@supported_devices) { $dev = $usb->find_device( @$_ ); if ($dev) { - printf "\t%s / ID %04x:%04x (%s: %s)\n", $dev->filename(), + progress ("\t%s / ID %04x:%04x (%s: %s)\n", $dev->filename(), $dev->idVendor(), $dev->idProduct(), - $dev->manufacturer(), $dev->product(); + $dev->manufacturer(), $dev->product()); } } if ($dev) { - printf "\nUsing device: %04x:%04x (%s: %s)\n\n", - $dev->idVendor(), $dev->idProduct(), - $dev->manufacturer(), $dev->product(); + progress "\n"; + debug ("Using device: %04x:%04x (%s: %s)\n\n", + $dev->idVendor(), $dev->idProduct(), + $dev->manufacturer(), $dev->product() ); } else { - print "\tNONE\n\n"; + progress "\tNONE\n\n"; print "ERROR: No supported device was found...\n\n"; print "Please connect your HP wireless USB print server to \n"; print "the computer with the black USB cable (micro USB plug) \n"; @@ -101,26 +112,213 @@ sub device_open ($) { } - -sub request_device_config ($) { +sub write_bulk_data ($$$) { my $dev = shift; - my $request = "@\0\x06\0XXXXXXXX9100:\n"; + 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); if ($debug) { print "Request: ", $request, "\n"; } - my $res = $dev->bulk_write (1, $request, 18, 1000); + my $res = $dev->bulk_write (1, $request, 18, 500); if ($res<0) { printf "ERROR write bulk data (%d): %s\n", $res, $!; } - $res = $dev->bulk_read (2, my $result="", 25600, 1000); + return $res; +} + +# Read bulk data, maybe in loops until there is nothing left to read? +sub read_bulk_data ($$$) { + my $dev = shift; + 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, $!; } - if ($debug) { printf "read %d bytes: \n%s\n\n", $res, $result; } - return $result; + 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 security2string { + my $sec = shift; + if ($sec == 0) { + return "None"; + } elsif ($sec == 1) { + return "WEP"; + } elsif ($sec == 2) { + return "WPA-PSK"; + } elsif ($sec == 4) { + return "WPA2-PSK"; # WPA2-PSK TKIP + } elsif ($sec == 6) { + return "WPA/WPA2-PSK"; # WPA/WPA2 and WPA2-PSK CCMP + } elsif ($sec == 7) { + return "WPA-EAP"; + } elsif ($sec == 8) { + return "WPA2-EAP"; + } else { + return sprintf ("Unknown (%d)", $sec); + } +} + +sub val { + my $config = shift; + my $var = shift; + foreach my $e (@$config) { + if ($e->[0] eq $var) { + return $e->[2]; + } + } + return undef; +} + +sub print_current_configuration ($) { + my $config = shift; + print "Current configuration of the device:\n"; + 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; + } + printf "\tSSID: %s\n", val($config,'7000'); + printf "\tSecurity: %s\n", security2string (val($config,'7003')); + printf "\tChannel: %s\n", val($config,'7002'); + + 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) { + printf "\tLink state: %s\n", val($config,'7014'); + } else { + # Not connected + printf "\tWireless not connected\n"; + } + # TODO: Print ad-hoc network setting if configured/enabled + print "\n\n"; +} + + +sub request_device_config ($) { + my $dev = shift; + my $cfg = ""; + progress "Reading current configuration...\n"; + my $res = write_bulk_data ($dev, "9100:\n", 6); + $res = read_bulk_data ($dev, $cfg, 25600); + return $cfg; } sub parse_device_config ($) { 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]); +# print "key: ", $key, ", name: ", $name, ", value: ", $value, "\n"; + } + return @config; +} +# Convert a list of the form [ ["OPTIONID", "OPTIONNAME", "VALUE], ...] +# into a string to be sent to the device +sub create_config ($) { + my $cfg = shift; + my $result = ""; + foreach my $o (@$cfg) { + $result .= sprintf ("%s %s:%s\n", $o->[0], $o->[1], $o->[2]); + } + return $result; +} + +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] + }; +# "SSID: ", $ssid, "settings: ", @settings, "\n"; +# print "entry: |", $_, "|\n"; + } + debug Dumper ($networks); + return $networks; +} + +sub scan_for_aps ($) { + my $dev = shift; + progress "Scanning for access points: "; + my $loop = 0; + my $found; + my $survey; + while (!defined($found) && $loop <= 4) { + ++$loop; + debug ("loop=", $loop, "\n"); + my $res = write_bulk_data ($dev, "9107:\n", 6); + $res = read_bulk_data ($dev, my $d="", 0); + foreach (1, 2, 3, 4, 5) { + sleep(1); + progress "."; + } + my $cfg = ""; + $res = write_bulk_data ($dev, "9100:\n", 6); + $res = read_bulk_data ($dev, $cfg, 25600); + my @config = parse_device_config ($cfg); + $survey = val(\@config, '7021'); + $found = (defined ($survey) && length ($survey) > 0); + } + progress "\n"; + if ($found) { +# progress "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"; +# print Dumper ($aps); + 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"; } @@ -131,12 +329,53 @@ sub device_close ($) { $dev->reset; } +sub ask_change { + my $exit = ""; + until ($exit =~ /^[yn]/i ) { + print "Do you want to change these settings? (y,n) "; + chomp($exit = <STDIN>); + $exit = trim($exit); + } + return ($exit =~ /^y/i); +} +sub ask_ap { + my $aps = shift; + 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 "Enter '0', 'hidden' or 'h' to connect to a hidden network.\n"; + do { + print "Desired wireless network: "; + chomp($newssid = <STDIN>); + $newssid = trim ($newssid); + return $newssid if (exists $aps->{$newssid}); + if ($newssid =~ /^[1-9]\d*$/ && ($newssid <= scalar(@ssids))) { + return $ssids[$newssid-1]; + } + if ($newssid eq "0" || $newssid eq "h" || $newssid eq "hidden") { + $newssid = ""; + while (length($newssid) < 1) { + print "Please enter the SSID: "; + chomp ($newssid = <STDIN>); + } + return $newssid; + } + } while 1; # We'll jump out of the loop via return! +} my $dev = device_detect (); if ($dev) { device_open ($dev); my $cfg = request_device_config ($dev); - my $cfg_array = parse_device_config ($cfg); + my @config = parse_device_config ($cfg); + print_current_configuration (\@config); + # TODO: Ask whether configuration shall be changed + exit unless (ask_change ()); + my $aps = scan_for_aps ($dev); + print_wireless_networks ($aps); + my $newap = ask_ap ($aps); + debug ("\tNew wireless network: |%s|\n", $newap); + device_close ($dev); }