summaryrefslogtreecommitdiffstats
path: root/lib/network/ndiswrapper.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/network/ndiswrapper.pm')
-rw-r--r--lib/network/ndiswrapper.pm160
1 files changed, 160 insertions, 0 deletions
diff --git a/lib/network/ndiswrapper.pm b/lib/network/ndiswrapper.pm
new file mode 100644
index 0000000..22af6d6
--- /dev/null
+++ b/lib/network/ndiswrapper.pm
@@ -0,0 +1,160 @@
+package network::ndiswrapper;
+
+use strict;
+use common;
+use modules;
+use detect_devices;
+
+#- using bsd_glob() since glob("/DONT_EXIST") return "/DONT_EXIST" instead of () (and we don't want this)
+use File::Glob ':glob';
+
+my $ndiswrapper_root = "/etc/ndiswrapper";
+
+sub installed_drivers() {
+ grep { -d $::prefix . "$ndiswrapper_root/$_" } all($::prefix . $ndiswrapper_root);
+}
+
+sub present_devices {
+ my ($driver) = @_;
+ my @supported_devices;
+ foreach (all($::prefix . "$ndiswrapper_root/$driver")) {
+ my ($ids) = /^([0-9A-F]{4}:[0-9A-F]{4})\.[0-9A-F]\.conf$/;
+ $ids and push @supported_devices, $ids;
+ }
+ grep { member(uc(sprintf("%04x:%04x", $_->{vendor}, $_->{id})), @supported_devices) } detect_devices::probeall();
+}
+
+sub get_devices {
+ my ($in, $driver) = @_;
+ my @devices = present_devices($driver);
+ @devices or $in->ask_warn(N("Error"), N("No device supporting the %s ndiswrapper driver is present!", $driver));
+ @devices;
+}
+
+sub ask_driver {
+ my ($in) = @_;
+ if (my $inf_file = $in->ask_fileW({ title => N("Please select the correct driver"), message => N("Please select the Windows driver description (.inf) file, or corresponding driver file (.dll or .o files). Note that only drivers up to Windows XP are supported."), directory => $::prefix . "/media" })) {
+ my $driver = basename(lc($inf_file));
+ $driver =~ s/\.inf$//;
+
+ #- first uninstall the driver if present, may solve issues if it is corrupted
+ require run_program;
+ -d $::prefix . "$ndiswrapper_root/$driver" and run_program::rooted($::prefix, 'ndiswrapper', '-e', $driver);
+
+ unless (run_program::rooted($::prefix, 'ndiswrapper', '-i', $inf_file)) {
+ $in->ask_warn(N("Error"), N("Unable to install the %s ndiswrapper driver!", $driver));
+ return undef;
+ }
+
+ return $driver;
+ }
+ undef;
+}
+
+sub find_matching_devices {
+ my ($device) = @_;
+ my $net_path = '/sys/class/net';
+ my @devices;
+
+ my $is_driver_listed = sub { my ($driver) = @_; any { member($driver, @{$_->{drivers}}) } @devices };
+
+ require network::connection::ethernet;
+ foreach my $interface (all($net_path)) {
+ if (network::connection::ethernet::device_matches_interface($device, $interface)) {
+ my $driver = network::connection::ethernet::interface_to_driver($interface);
+ push @devices, { interface => $interface, drivers => [ $driver ] } if $driver;
+ }
+ }
+
+ #- find drivers with no net interface
+ my $sysfs_driver = $device->{sysfs_device} && basename(readlink($device->{sysfs_device} . "/driver/module"));
+ if ($sysfs_driver) {
+ my @sysfs_drivers = $sysfs_driver;
+ if ($sysfs_drivers[0] eq 'ssb') {
+ push @sysfs_drivers, map { basename(readlink($_)) } bsd_glob($device->{sysfs_device} . "/ssb*/driver/module");
+ }
+ @sysfs_drivers = grep { !$is_driver_listed->($_) } @sysfs_drivers;
+ push @devices, { interface => undef, drivers => \@sysfs_drivers } if @sysfs_drivers;
+ }
+
+ #- add original driver
+ push @devices, { interface => undef, drivers => [ $device->{driver} ] }
+ if !$is_driver_listed->($device->{driver}) && any { $_ eq $device->{driver} } modules::loaded_modules();
+
+ @devices;
+}
+
+sub find_conflicting_devices {
+ my ($device) = @_;
+ grep { !member("ndiswrapper", @{$_->{drivers}}) } find_matching_devices($device);
+}
+
+sub find_interface {
+ my ($device) = @_;
+ my $dev = find { member("ndiswrapper", @{$_->{drivers}}) } find_matching_devices($device);
+ $dev->{interface};
+}
+
+sub setup_device {
+ my ($in, $device) = @_;
+
+ my @conflicts = find_conflicting_devices($device);
+ if (@conflicts) {
+ $in->ask_yesorno(N("Warning"), N("The selected device has already been configured with the %s driver.
+Do you really want to use a ndiswrapper driver?", $conflicts[0]->{drivers}[0])) or return;
+ #- stop old interfaces
+ network::tools::stop_interface($_->{interface}, 0) foreach grep { defined $_->{interface} } @conflicts;
+ #- unload old modules before trying to load ndiswrapper
+ #- (sorted according to /proc/modules to handle deps nicely)
+ my @drivers = intersection([ modules::loaded_modules() ], [ map { @{$_->{drivers}} } @conflicts ]);
+ eval { modules::unload($_) } foreach @drivers;
+ }
+
+ #- unload ndiswrapper first so that the newly installed .inf files will be read
+ eval { modules::unload("ndiswrapper") };
+ eval { modules::load("ndiswrapper") };
+
+ if ($@) {
+ $in->ask_warn(N("Error"), N("Unable to load the ndiswrapper module!"));
+ return;
+ }
+
+ my $interface = find_interface($device);
+ unless ($interface) {
+ $in->ask_warn(N("Error"), N("Unable to find the ndiswrapper interface!"));
+ return;
+ }
+
+ $interface;
+}
+
+sub select_device {
+ my ($in) = @_;
+ my $driver;
+ my @drivers = installed_drivers();
+ if (@drivers) {
+ $driver ||= first(@drivers);
+ $in->ask_from('', N("Choose an ndiswrapper driver"), [
+ { type => "list", val => \$driver, allow_empty_list => 1,
+ list => [ undef, @drivers ],
+ format => sub { defined $_[0] ? N("Use the ndiswrapper driver %s", $_[0]) : N("Install a new driver") } }
+ ]) or return;
+ }
+ $driver ||= ask_driver($in) or return;
+
+ my @devices = get_devices($in, $driver) or return;
+ my $device;
+ if (@devices == 1) {
+ #- only one device matches installed driver
+ $device = $devices[0];
+ } else {
+ $in->ask_from('', N("Select a device:"), [
+ { type => "list", val => \$device, allow_empty_list => 1,
+ list => [ present_devices($driver) ],
+ format => sub { $_[0]{description} } }
+ ]) or return;
+ }
+ return $device;
+}
+
+1;