wok view tazndis/stuff/tazndis @ rev 1942

tazndis, don't modify system files in debug mode.
author Eric Joseph-Alexandre <erjo@slitaz.org>
date Sat Dec 27 22:17:15 2008 +0100 (2008-12-27)
parents f9f8efc8b0a8
children 5117bf553562
line source
1 #!/usr/bin/perl
3 #/*
4 #* tazndis, install, remove or list of NDIS drivers.
5 #*
6 #* This program is a replacement for ndiswrapper utility written by
7 #* Pontus Fuchs and Giridhar Pemmasani.
8 #* Most part of code come from the original ndiswrapper PERL script.
9 #*
10 #* If you need more complexe commands consider to use the original ndiswrapper
11 #* instead.
12 #*
13 #* Copyright (C) 2008 Eric Joseph-Alexandre
14 #* Copyright (C) 2005-2006 Pontus Fuchs, Giridhar Pemmasani
15 #*
16 #*
17 #* This program is free software; you can redistribute it and/or modify
18 #* it under the terms of the GNU General Public License as published by
19 #* the Free Software Foundation; either version 2 of the License, or
20 #* (at your option) any later version.
21 #*
22 #* This program is distributed in the hope that it will be useful,
23 #* but WITHOUT ANY WARRANTY; without even the implied warranty of
24 #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 #* GNU General Public License for more details.
26 #*
27 #*/
29 $VERSION="1.53.1";
30 $ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin";
32 my $WRAP_PCI_BUS = 5;
33 my $WRAP_PCMCIA_BUS = 8;
34 my $WRAP_USB_BUS = 15;
36 my %sections;
37 my %parsed_sections;
38 my $confdir = "/etc/ndiswrapper";
39 my $src_dir;
40 my $driver_name;
41 my @source_disks_files;
43 my $re_dev_id = "([[:xdigit:]]{4})";
44 my $re_sub_dev_conf = "$re_dev_id:$re_dev_id:$re_dev_id:$re_dev_id" .
45 "\.([[:xdigit:]]+)\.conf";
46 my $re_dev_conf = "$re_dev_id:$re_dev_id\.([[:xdigit:]]+)\.conf";
48 my $rcs_config = "/etc/rcS.conf";
49 my $net_config = "/etc/network.conf";
50 my $config_change = 1;
51 my $dbg = 1;
54 # fixup list for parameters.
55 my %param_fixlist = ("EnableRadio|0" => "1",
56 "IBSSGMode|0" => "2",
57 "PrivacyMode|0" => "2",
58 "MapRegisters|256" => "64",
59 "AdhocGMode|1" => "0");
61 if (@ARGV < 1) {
62 usage();
63 exit(1);
64 }
66 my $res;
67 my $dbg_file;
69 $dbg_file = "/dev/null";
71 # "-D" is for development/debugging only
72 if ($ARGV[0] eq "-D") {
73 my $dbg = 0;
74 $dbg_file = "/tmp/ndiswrapper.dbg";
75 $confdir = "/tmp/ndiswrapper";
76 open(DBG, "> $dbg_file") or die "couldn't open $dbg_file: $!";
77 shift;
78 }
80 if ($ARGV[0] eq "-i" and @ARGV == 2) {
81 $res = install($ARGV[1]);
82 } elsif (($ARGV[0] eq "-r") and @ARGV == 2) {
83 $res = remove_driver($ARGV[1]);
84 } elsif ($ARGV[0] eq "-l" and @ARGV == 1) {
85 $res = list_drivers();
86 } elsif ($ARGV[0] eq "-v" and @ARGV == 1) {
87 $res = check_version();
88 } else {
89 usage();
90 }
91 close(DBG);
92 exit($res);
94 sub usage() {
95 print "install/manage Windows drivers for ndiswrapper kernel module\n\n" .
96 "usage: tazndis OPTION\n" .
97 "-i inffile install driver described by 'inffile'\n" .
98 "-r driver remove 'driver'\n" .
99 "-l list installed drivers\n" .
100 "-v report version information\n\n" .
101 "If you need more complex operation, you may install full ndiswrapper package instead.\n" ;
102 }
104 #/*
105 #* Begin of PERL modules substition
106 #*
107 #* Replaced File.pm
109 sub basename(@_){
110 ($a = shift) =~ s#.*/(.*)$#$1#;
111 return $a;
112 }
115 # delete given single tree with no sub directories.
116 sub rmtree {
117 local $dir = $_[0];
118 opendir(DIR, $dir) or die "couldn't delete $dir: $!\n";
119 @dirs = grep(!/^.{1,2}$/, readdir(DIR));
120 foreach $file (@dirs){
121 unlink "$dir/$file" or die "couldn't delete file $file $!";
122 }
123 if(rmdir "$dir"){
124 return 1;
125 }else{
126 return 0;
127 }
128 }
130 # return current path.
131 sub cwd {
132 local $var;
133 chomp($var = `pwd`);
134 return $var;
135 }
137 #
138 sub copy {
139 local ($file1, $file2) = @_;
140 open (F1, "$file1") or
141 die "couldn't open file $file1 for reading $!\n";
142 open (F2, ">$file2") or
143 die "couldn't open file $file2 for writing $!\n";
144 if ($file1 =~ /\.((bin)|(sys)|(cat))$/) {
145 binmode F1;
146 binmode F2;
147 while (read(F1,$buffer,1)) {
148 print(F2 $buffer);
149 }
150 } else {
151 while (<F1>) {
152 print F2 $_;
153 }
154 }
155 close F1;
156 close F2;
157 }
159 ##
160 ## End of PERL modules substition
161 ##
163 sub remove_driver {
164 my $driver = shift;
165 if (!rmtree("$confdir/$driver", 0, 1)) {
166 warn "couldn't delete $confdir/$driver: $!\n";
167 }
168 return 0;
169 }
171 sub abort {
172 remove_driver($driver_name);
173 exit 1;
174 }
176 sub check_version {
177 my ($utils_version, $module_utils_version, $res);
178 $res = 0;
179 $utils_version = `loadndisdriver -v`;
180 chomp($utils_version);
181 $utils_version =~ s/^version: //;
182 if (length($utils_version) eq 0) {
183 printf "utils version is too old!\n";
184 $res = -1;
185 }
186 $module_utils_version = 0;
187 open(MODINFO, "modinfo ndiswrapper |");
188 while (my $line = <MODINFO>) {
189 if ($line =~ /utils_version:.*read only:\s([0-9\.]+)/) {
190 $module_utils_version = $1;
191 last;
192 }
193 }
194 if ($module_utils_version eq 0) {
195 printf "module version is too old!\n";
196 $res = -1;
197 } elsif ($utils_version ne $module_utils_version) {
198 printf "utils version '%s' is incompatible with utils version needed" .
199 " by driver ('%s')!\n", $utils_version, $module_utils_version;
200 $res = -1;
201 }
202 printf "tazndis version: %s\n ", $VERSION;
203 printf "utils version: '%s', utils version needed by module: '%s'\n",
204 $utils_version, $module_utils_version;
205 printf "module details:\n";
206 system("modinfo ndiswrapper | grep -E '^(version|vermagic|filename)'");
208 if ($res) {
209 printf "\nYou may need to upgrade driver and/or utils to latest " .
210 "versions available at\n" .
211 "http://ndiswrapper.sourceforge.net\n";
212 }
213 return $res;
214 }
216 sub set_rcs_config {
217 # Add ndiswrapper to LOAD_MODULES if needed.
218 open (CONFIG, "< $rcs_config") or die "couldn't open $rcs_config:$!";
219 open (CONFIG1, "> $rcs_config.tmp") or die "couldn't open $rcs_config.tmp for writting:$!";
220 LINE: while(<CONFIG>){
221 if(/^LOAD_MODULES/){
222 if (!/^LOAD_MODULES=.*ndiswrapper/){
223 print "Add ndiswrapper to module list...\n";
224 $_ =~ s/(.*)\"$/$1 ndiswrapper\"/;
225 $file_change = 0;
226 }
227 }
228 chomp;
229 print CONFIG1 "$_\n";
230 }
231 close CONFIG, CONFIG1;
233 if($file_change == 0){
234 rename "$rcs_config.tmp","$rcs_config" or die "couldn't update $rcs_config: $!";
235 $config_change=1;
236 } else {
237 unlink "$rcs_config.tmp";
238 }
239 }
241 sub set_net_config {
242 #
243 open (CONFIG, "< $net_config") or die "couldn't open $net_config:$!";
244 open (CONFIG1, "> $net_config.tmp") or die "couldn't open $net_config.tmp for writting:$!";
245 print "Set WiFi to start at boot time...\n";
246 LINE: while(<CONFIG>){
247 if(/^WIFI=/){
248 if (/^WIFI="no"$/){
249 $_ =~ s/^WIFI="no"$/WIFI="yes"/;
250 $config_change = 0;
251 }
252 }
253 chomp;
254 print CONFIG1 "$_\n";
255 }
256 close CONFIG, CONFIG1;
258 if($rcs_config_change == 0){
259 rename "$net_config.tmp","$net_config" or die "couldn't update $rcs_config: $!";
260 $config_change=1;
261 } else {
262 unlink "$net_config.tmp";
263 }
264 }
266 sub load_ndiswrapper {
267 open (LSMOD, " lsmod |") or die "couldn't get loaded module list: $!";
268 if(!/^ndiswrapper/){
269 print "Loading ndiswrapper...\n";
270 `modprobe ndiswrapper`;
271 }
272 }
274 sub install {
275 my $inf = shift;
276 chomp($inf);
277 chop($src_dir = `dirname $inf`);
278 $driver_name = lc(basename($inf));
280 unless ($driver_name =~ s/\.inf$//) {
281 die "install argument must be .inf file\n";
282 }
284 if (! -d $confdir) {
285 mkdir($confdir) or die "couldn't create $confdir: $!";
286 }
287 (-d "$confdir/$driver_name") and
288 die "driver $driver_name is already installed\n";
290 read_sections($inf);
291 parse_section("Strings");
292 parse_section("Version");
293 parse_source_disks_files();
294 mkdir("$confdir/$driver_name") or
295 die "couldn't create $confdir/$driver_name: $!";
296 print "installing $driver_name ...\n";
297 parse_mfr();
298 copy_file(basename($inf), basename($inf));
299 create_fuzzy_conf($driver_name);
301 #Update LOAD_MODULES and load ndiswrapper.
302 # Only if we are not in debug mode.
303 if (!$dbg) {
304 set_rcs_config();
305 load_ndiswrapper();
307 # Ask for WiFi at boot time.
308 print "Would you to start WiFi at boot time [y/N]? ";
309 chomp(my $answer = <STDIN>);
310 if (lc($answer) eq "y") {
311 set_net_config();
312 }
313 }
315 return 0;
316 }
318 # return lines in section
319 sub get_section {
320 my $name = shift;
321 foreach my $key (keys %sections) {
322 if (lc($key) eq lc($name)) {
323 printf DBG "section: $key\n";
324 return \@{$sections{$key}};
325 }
326 }
327 printf DBG "couldn't find section \"$name\"\n";
328 return 0;
329 }
331 # load inf and split into different sections.
332 sub read_sections {
333 my $filename = shift;
334 open(INF, $filename) or die "couldn't open $filename: $!";
336 my $name = "none";
337 @{$sections{$name}} = ();
338 while (my $line = <INF>) {
339 # convert from unicode
340 $line =~ s/\xff\xfe//;
341 $line =~ s/\0//g;
343 chomp($line);
344 $line = trim($line);
345 next if ($line =~ /^$/);
346 if ($line =~ /^\[(.+)\]/) {
347 $name = $1;
348 @{$sections{$name}} = ();
349 } else {
350 push(@{$sections{$name}}, $line);
351 }
352 }
353 close(INF);
354 foreach $name (keys %sections) {
355 printf DBG "section: %s\n", $name;
356 foreach my $line (@{$sections{$name}}) {
357 printf DBG "%s: %s\n", $name, $line;
358 }
359 }
360 }
362 sub parse_section {
363 my $name = shift;
364 my $lines = get_section($name);
365 if (!$lines) {
366 return;
367 }
368 $parsed_sections{$name} = ();
369 foreach my $line (@{$lines}) {
370 (my $key, my $val) = parse_key_value($line);
371 if ($key) {
372 $val = strip_quotes($val);
373 $parsed_sections{$name}->{$key} = $val;
374 printf DBG "$name: %s = %s\n", $key, $val;
375 }
376 }
377 }
379 sub parse_mfr() {
380 my $lines = get_section("Manufacturer");
381 $lines or die "couldn't get manufacturer section - " .
382 "installation may be incomplete\n";
383 foreach my $line (@{$lines}) {
384 my ($strkey, $val) = parse_key_value($line);
385 if ($strkey) {
386 my ($models, @targets) = split(",", $val);
387 if ($models) {
388 printf DBG "mfr: %s, %s\n", $line, $models;
389 my $target = choose_target_os(@targets);
390 printf DBG "target: '%s'\n", $target;
391 parse_models($models, $target);
392 }
393 }
394 }
395 }
397 sub parse_models {
398 my ($models, $target) = @_;
399 printf DBG "models: target: '%s'.'%s'\n", $models, $target;
400 my $lines = get_target_os_section($models, $target);
401 if (!$lines) {
402 warn "couldn't find models section \"$models\" -\n" .
403 "installation may be incomplete\n";
404 return -1;
405 }
406 foreach my $line (@{$lines}) {
407 $line = del_comment($line);
408 next if (length($line) eq 0);
409 (my $dev_desc, my $val) = parse_key_value($line);
410 my @fields = split(",", $val);
411 if (@fields le 1) {
412 printf "couldn't find install directive: %s\n", $line;
413 next;
414 }
415 my $section = trim($fields[0]);
416 my $hwid = trim($fields[1]);
417 if ($hwid =~ /^%.+%$/) {
418 $hwid = get_string_value($hwid);
419 }
420 # TODO: deal with compatible IDs as hwid?
421 my ($bus_type, $vendor, $device, $subvendor, $subdevice) =
422 parse_hwid($hwid);
423 next if (!$vendor);
424 printf DBG "models: %s, %s, %s\n", $section, $hwid, $vendor;
425 parse_install($section, $target, $bus_type, $vendor, $device,
426 $subvendor, $subdevice);
427 }
428 }
430 sub parse_install {
431 my ($section, $target, $bus_type, $vendor, $device,
432 $subvendor, $subdevice) = @_;
433 my $lines = get_target_os_section($section, $target);
434 if (!$lines) {
435 warn "couldn't find install section \"$section\" -\n" .
436 "installation may be incomplete\n";
437 return -1;
438 }
440 my $filename = "$vendor:$device";
441 if ($subvendor) {
442 $filename .= ":$subvendor:$subdevice"
443 }
444 $filename .= sprintf(".%X.conf", $bus_type);
446 my (@addregs, @copyfiles);
447 foreach my $line (@{$lines}) {
448 $line =~ s/^;\s*//;
449 $line = trim(del_comment($line));
450 my ($key, $val) = parse_key_value($line);
451 my @array;
452 if ($key) {
453 if (lc($key) eq "addreg") {
454 @array = split(",", $val);
455 foreach my $reg (@array) {
456 push @addregs, trim($reg);
457 }
458 } elsif (lc($key) eq "copyfiles") {
459 printf DBG "copyfiles: %s\n", $val;
460 @array = split(",", $val);
461 foreach my $copy_file_dirs (@array) {
462 my @copy_sec = split(",", $copy_file_dirs);
463 foreach my $file (@copy_sec) {
464 push @copyfiles, trim($file);
465 }
466 }
467 } elsif (lc($key) eq "bustype") {
468 printf DBG "bustype: %s\n", $val;
469 $bus_type = $val;
470 }
471 }
472 }
474 open(CONF, ">$confdir/$driver_name/$filename") or
475 die "couldn't create file $confdir/$driver_name/$filename: $!";
477 printf CONF "sys_files|";
478 foreach my $file (@copyfiles) {
479 parse_copy_file($file);
480 }
481 printf CONF "\n";
483 my $version = get_section_value("Version", "DriverVer");
484 my $provider = get_section_value("Version", "Provider");
485 my $classguid = get_section_value("Version", "ClassGUID");
486 my $providerstring = trim(strip_quotes(get_string_value(trim($provider))));
487 $classguid =~ s/^\s*{//;
488 $classguid =~ s/}\s*$//;
490 printf CONF "NdisVersion|0x50001\n";
491 printf CONF "Environment|1\n";
492 printf CONF "class_guid|%s\n", $classguid;
493 printf CONF "driver_version|%s,%s\n", $providerstring, $version;
494 printf CONF "BusType|%s\n", $bus_type;
495 printf CONF "SlotNumber|01\n";
496 printf CONF "NetCfgInstanceId|{28022A01-1234-5678-ABCDE-123813291A00}\n";
497 printf CONF "\n";
498 close(CONF);
500 open(CONF, "|sort|uniq >>$confdir/$driver_name/$filename") or
501 die "couldn't create file $confdir/$driver_name/$filename: $!";
503 foreach my $reg (@addregs) {
504 parse_registry($reg);
505 }
506 close(CONF);
507 }
509 sub parse_registry {
510 my ($reg, $conf) = @_;
511 my $lines = get_section($reg);
512 if (!$lines) {
513 warn "couldn't find section \"$reg\" -\n" .
514 "installation may be incomplete\n";
515 return -1;
516 }
518 my $driver_desc = 0;
519 foreach my $line (@{$lines}) {
520 $line = del_comment($line);
521 my @fields = split(",", $line);
522 next if (@fields lt 4);
523 my $value;
524 my $param = trim($fields[1]);
525 if ($param =~ /^ndi\\/i) {
526 if ($param =~ /^ndi\\params\\(.+)/i) {
527 $param = strip_quotes(trim($1));
528 $param =~ s/\\.*$//;
529 next if (lc(trim($fields[2])) ne "default");
530 $value = strip_quotes(trim($fields[4]));
531 } else {
532 printf DBG "ignoring parameter $line\n";
533 next;
534 }
535 } else {
536 $param = strip_quotes(trim($fields[2]));
537 next if (length($param) eq 0);
538 $value = strip_quotes(trim($fields[4]));
539 }
540 $value = get_string_value($value);
541 if (length($param) gt 0) {
542 if ($param_fixlist{"$param|$value"}) {
543 my $orig_value = $value;
544 $value = $param_fixlist{"$param|$value"};
545 printf "forcing parameter $param from $orig_value to $value\n";
546 }
547 printf CONF "%s|%s\n", $param, $value;
548 if ($param =~ /^DriverDesc$/) {
549 $driver_desc = 1;
550 }
551 }
552 }
553 if ($driver_desc == 0) {
554 printf CONF "DriverDesc|NDIS Network Adapter\n";
555 }
556 }
558 sub parse_copy_file {
559 my $copy_name = shift;
561 if ($copy_name =~ /^\@/) {
562 $copy_name =~ s/^\@//;
563 $copy_name = trim($copy_name);
564 if (valid_copy_file_name($copy_name)) {
565 return copy_file($copy_name, $copy_name);
566 }
567 }
569 my $lines = get_section($copy_name);
570 if (!$lines) {
571 warn "couldn't find section \"$copy_name\" -\n" .
572 "installation may be incomplete\n";
573 return -1;
574 }
575 foreach my $line (@{$lines}) {
576 $line = trim($line);
578 # some inf files have file names commented out; get file names from them
579 $line =~ s/^\s*;//;
580 my @files = split(",", $line);
581 if (@files == 0) {
582 printf DBG "copyfiles section $copy_name has no files\n";
583 return -1;
584 }
585 my $src, my $dst;
586 if (@files > 1 and length(trim($files[1])) > 0) {
587 $src = $files[1];
588 if (length(trim($files[0])) > 0) {
589 $dst = $files[0];
590 } else {
591 $dst = $src;
592 }
593 } else {
594 $src = $files[0];
595 $dst = $src;
596 }
597 $src =~ s/^.*\\//;
598 $dst =~ s/^.*\\//;
599 printf DBG "src: '%s', dst: '%s'\n", $src, $dst;
600 $src = trim(del_comment($src));
601 next if (length($src) eq 0);
602 if (valid_copy_file_name($src)) {
603 $dst = trim(del_comment($dst));
604 printf DBG "src: '%s', dst: '%s'\n", $src, $dst;
605 copy_file($src, $dst);
606 } else {
607 printf DBG "invalid file '%s' ignored\n", $src;
608 }
609 }
610 return 0;
611 }
613 sub parse_hwid {
614 my $hwid = uc(shift);
615 if ($hwid =~ /(PCI\\)?VEN_(\w+)&DEV_(\w+)&SUBSYS_(\w{4})(\S{4})/) {
616 return ($WRAP_PCI_BUS, $2, $3, $4, $5);
617 } elsif ($hwid =~ /(PCI\\)?VEN_(\w+)&DEV_(\w+)/) {
618 return ($WRAP_PCI_BUS, $2, $3, 0, 0);
619 } elsif ($hwid =~ /(USB\\)?VID_(\w+)&PID_(\w+)/) {
620 return ($WRAP_USB_BUS, $2, $3, 0, 0);
621 } else {
622 return 0;
623 }
624 }
626 sub parse_key_value {
627 my $line = shift;
629 $line = del_comment($line);
630 if ($line =~ /([^=]+)=(.+)/) {
631 return (trim($1), trim($2));
632 } else {
633 return 0;
634 }
635 }
637 sub choose_target_os {
638 my @targets = @_;
639 my $arch = `uname -m`;
640 chomp($arch);
641 printf DBG "arch: %s\n", $arch;
642 if ($arch =~ /64$/) {
643 $arch = "amd64";
644 } else {
645 $arch = "x86";
646 }
647 printf DBG "arch: %s\n", $arch;
648 my @prefs = ("NT($arch)\.5\.1", "NT($arch)\.5", "NT($arch)",
649 "NT\.5\.1", "NT\.5", "NT");
650 foreach my $pref (@prefs) {
651 foreach my $target (@targets) {
652 $target = trim($target);
653 printf DBG "target: '%s', pref: '%s'\n", $target, $pref;
654 if ($target =~ /NT((amd64)|(x86))/i) {
655 printf DBG "target arch: '%s'\n", $1;
656 next if ($1 !~ /$arch/i);
657 }
658 if ($target =~ /$pref/i) {
659 return $target;
660 }
661 }
662 }
663 return "";
664 }
666 sub get_target_os_section {
667 my ($section, $target) = @_;
668 my $lines;
670 chomp($section);
671 $section =~ s/^\s*"\s*//;
672 $section =~ s/\s*"\s*$//;
673 printf DBG "section: \"%s\", target: \"%s\"\n", $section, $target;
675 if (length($target) gt 0) {
676 $lines = get_section($section . "." . $target);
677 return $lines if ($lines);
678 }
680 my $arch = `uname -m`;
681 chomp($arch);
682 printf DBG "arch: %s\n", $arch;
683 if ($arch =~ /64$/) {
684 $arch = "AMD64";
685 } else {
686 $arch = "X86";
687 }
688 printf DBG "arch: %s\n", $arch;
690 my @prefs = ("NT$arch.5.1", "NT$arch.5", "NT$arch",
691 "NT.5.1", "NT.5", "NT");
692 foreach my $pref (@prefs) {
693 $lines = get_section($section . "." . $pref);
694 return $lines if ($lines);
695 }
696 $lines = get_section($section);
697 return $lines if ($lines);
699 printf DBG "couldn't find section \"$section\" for \"$arch\"\n";
700 return 0;
701 }
703 sub get_section_value {
704 (my $section, my $name) = @_;
705 return $parsed_sections{$section}->{$name};
706 }
708 sub get_string_value {
709 my $key = shift;
710 if ($key =~ /%(.+)%/) {
711 $key = $1;
712 return get_section_value("Strings", $key);
713 } else {
714 return $key;
715 }
716 }
718 sub copy_file {
719 my ($src, $dst) = @_;
721 # ignore files not needed
722 return 0 if (lc($src) =~ /\.((exe)|(dll)|(cpl)|(hlp))$/);
723 my $real_file = get_file($src);
724 if (length($real_file) gt 0) {
725 $dst = lc($dst);
726 printf DBG "copying \"$src_dir/$real_file\" to " .
727 "\"$confdir/$driver_name/$dst\"\n";
728 copy("$src_dir/$real_file", "$confdir/$driver_name/$dst") or
729 warn "couldn't copy \"$src_dir/$real_file\" to " .
730 "\"$confdir/$driver_name\": $! -\n" .
731 "installation may be incomplete\n";
732 printf DBG "chmod: $confdir/$driver_name/$dst\n";
733 chmod(0644, "$confdir/$driver_name/$dst");
734 if ($dst =~ /\.sys$/) {
735 printf CONF "%s ", $dst;
736 }
737 } else {
738 warn "couldn't find \"$src\" in \"$src_dir\"; make sure " .
739 "all driver files, including .inf, .sys (and any firmware files) " .
740 "are in \"$src_dir\" -\n" .
741 "installation may be incomplete\n";
742 }
743 }
746 # for conf files with subvendor and subdevice, create conf files with just
747 # vendor and device
748 sub create_fuzzy_conf {
749 my $driver = shift;
750 my $cwd = cwd();
751 chdir("$confdir/$driver") or die "couldn't chdir to $confdir/$driver: $!";
752 open(LS, "ls -1 . |") or die "couldn't open $confdir/$driver: $!";
753 while (my $file = <LS>) {
754 chomp($file);
755 if ($file =~ /$re_sub_dev_conf/) {
756 my $fuzzy_file = "$1:$2.$5.conf";
757 printf DBG "file: $file, fuzzy file: $fuzzy_file\n";
758 if (! -e "$confdir/$driver/$fuzzy_file") {
759 symlink("$file", "$fuzzy_file") or
760 warn "couldn't link $confdir/$driver/$file " .
761 "to $confdir/$driver/$fuzzy_file: $!\n";
762 }
763 }
764 }
765 close(LS);
766 chdir($cwd) or warn "couldn't chdir to $cwd: $!";
767 return 0;
768 }
770 # find a file in a case-insensitive way.
771 sub get_file {
772 my $file = lc(shift);
773 if (opendir(DIR, "$src_dir")) {
774 my @allfiles = readdir(DIR);
775 foreach my $real_file (@allfiles) {
776 if (lc($real_file) eq $file) {
777 closedir(DIR);
778 return $real_file;
779 }
780 }
781 closedir(DIR);
782 } else {
783 warn "couldn't open \"$src_dir\": $! -\n" .
784 "installation may be incomplete\n";
785 }
786 return "";
787 }
789 sub strip_quotes {
790 my $s = shift;
791 $s =~ s/"(.*)"/$1/;
792 return $s;
793 }
795 sub del_comment {
796 my $s = shift;
797 $s =~ s/;.*//;
798 return $s;
799 }
801 # remove whitsepace at front and end.
802 sub trim {
803 my $s = shift;
804 $s =~ s/^\s*//;
805 $s =~ s/\s*$//;
806 return $s;
807 }
809 sub valid_copy_file_name {
810 my $file = shift;
811 $file = lc($file);
812 printf DBG "file name: %s\n", $file;
813 foreach my $disk_file (@source_disks_files) {
814 return 1 if ($file eq $disk_file);
815 }
816 # some inf files may not have SourceDisksFiles section, so use
817 # known file names
818 return 1 if ($file =~ /\.((sys)|(bin)|(out))$/);
819 return 0;
820 }
822 sub parse_source_disks_files {
823 my $lines = get_source_disks_files();
824 if ($lines) {
825 foreach my $line (@{$lines}) {
826 $line = del_comment($line);
827 next if (length($line) eq 0);
828 my @file = split("=", $line);
829 next if (@file eq 0 or length($file[0] eq 0));
830 printf DBG "source disk file: \"%s\"\n", trim($file[0]);
831 push (@source_disks_files, lc(trim($file[0])));
832 }
833 } else {
834 warn "couldn't find SourceDisksFiles section - " .
835 "continuing anyway...\n";
836 }
837 }
839 sub get_source_disks_files {
840 my $arch = `uname -m`;
841 chomp($arch);
842 if ($arch =~ /64$/) {
843 $arch = "AMD64";
844 } else {
845 $arch = "X86";
846 }
848 my $lines = get_section("SourceDisksFiles." . $arch);
849 return $lines if ($lines);
851 $lines = get_section("SourceDisksFiles");
852 return $lines if ($lines);
854 return 0;
855 }
857 sub device_driver_alias {
858 my ($devid, $driver) = @_;
859 my $done = 0;
861 $devid = uc($devid);
862 if (!($devid =~ /^$re_dev_id:$re_dev_id$/)) {
863 printf "'$devid' is not a valid device ID\n";
864 return 1;
865 }
866 open(LS, "ls -1 $confdir/$driver/ |") or
867 die "couldn't open $confdir/$driver: $!";
869 while (my $f = <LS>) {
870 chomp($f);
871 if ($f =~ /\.([[:xdigit:]]+)\.conf$/) {
872 if (stat("$confdir/$driver/$devid.$1.conf")) {
873 printf "Driver '$driver' is already used for '$devid'\n";
874 $done = 1;
875 last;
876 }
877 if (symlink("$f", "$confdir/$driver/$devid.$1.conf")) {
878 printf "WARNING: Driver '$driver' will be used for '$devid'\n" .
879 "This is safe _only_ if driver $driver is meant for " .
880 "chip in device $devid\n";
881 $done = 1;
882 last;
883 } else {
884 warn "couldn't create symlink for \"$f\": $! -\n" .
885 "installation may be incomplete\n";
886 }
887 }
888 }
889 close(LS);
890 if ($done == 0) {
891 printf "driver '$driver' is not installed (properly)!\n";
892 return 1;
893 }
894 return 0;
895 }
897 sub list_drivers {
898 my $cards = get_cards();
899 open(LS, "/bin/ls -1 $confdir|") or die "couldn't open $confdir: $!";
900 while (my $driver = <LS>) {
901 chomp($driver);
902 if (-e "$confdir/$driver") {
903 printf "%s : %s\n", $driver, install_status($cards, $driver);
904 }
905 }
906 close(LS);
907 return 0;
908 }
911 sub get_cards {
912 #01:00.0 Class 0300: 1002:4c66 (rev 01)
913 # Subsystem: 1043:1732
914 my @cards = ();
915 if (open(LSPCI, "/usr/bin/lspci -vn|")) {
916 my $card;
917 while (my $line = <LSPCI>) {
918 if ($line =~ /^[0-9a-f]+.+\s$re_dev_id:$re_dev_id/) {
919 $card = {vendor => uc($1), device => uc($2)};
920 printf DBG "card: %s, %s\n", $1, $2;
921 } elsif ($line =~ /.+Subsystem:\s$re_dev_id:$re_dev_id/) {
922 $card->{subvendor} = uc($1);
923 $card->{subdevice} = uc($2);
924 printf DBG "sub: %s, %s\n", $1, $2;
925 push(@cards, $card);
926 }
927 }
928 close(LSPCI);
929 }
931 if (open(LSUSB, "lsusb |")) {
932 my $card;
933 while (my $line = <LSUSB>) {
934 if ($line =~ /.+: ID\s$re_dev_id:$re_dev_id/) {
935 $card = {vendor => uc($1), device => uc($2)};
936 push(@cards, $card);
937 }
938 }
939 close(LSUSB);
940 }
941 return \@cards;
942 }
944 sub install_status {
945 my ($cards, $driver) = @_;
947 if (!$cards or !$driver) {
948 return;
949 }
951 my ($sys, $conf, $inf);
952 my ($vendor, $device, $subvendor, $subdevice, $busid, $ret);
954 $sys = $conf = $inf = 0;
955 open(LS2, "/bin/ls -1 $confdir/$driver|") or
956 die "couldn't open $confdir/$driver: $!";
957 while (my $file = <LS2>) {
958 chomp($file);
959 if ($file =~ /\.sys$/) {
960 $sys = 1;
961 } elsif ($file =~ /\.inf$/) {
962 $inf = 1;
963 } elsif ($file =~ /^$re_sub_dev_conf$/) {
964 $busid = hex($5);
965 $conf = 1 if ($busid eq $WRAP_PCI_BUS);
966 } elsif ($file =~ /^$re_dev_conf$/) {
967 $busid = hex($3);
968 $conf = 1 if ($busid eq $WRAP_USB_BUS or $busid eq 0 or
969 $busid eq $WRAP_PCI_BUS);
970 }
971 }
972 close(LS2);
973 printf DBG "status: $sys, $inf, $conf\n";
974 if ($sys eq 0 or $inf eq 0 or $conf eq 0) {
975 $ret = "invalid driver!";
976 return $ret;
977 }
978 $ret = "driver installed";
979 open(LS2, "/bin/ls -1 $confdir/$driver|") or
980 die "couldn't open $confdir/$driver: $!";
982 while (my $file = <LS2>) {
983 chomp($file);
984 next if ($file !~ /\.conf$/);
985 $conf = 0;
986 if ($file =~ /^$re_sub_dev_conf$/) {
987 ($vendor, $device, $subvendor, $subdevice, $busid) =
988 (uc($1), uc($2), uc($3), uc($4), hex($5));
989 $conf = 1;
990 foreach my $card (@{$cards}) {
991 if ($card->{vendor} eq $vendor and
992 $card->{device} eq $device and
993 $card->{subvendor} eq $subvendor and
994 $card->{subdevice} eq $subdevice and
995 $busid eq $WRAP_PCI_BUS) {
996 $ret .= "\n\tdevice ($vendor:$device" .
997 ":$subvendor:$subdevice) present";
998 $conf = 2;
999 last;
1002 } elsif ($file =~ /^$re_dev_conf/) {
1003 ($vendor, $device, $subvendor, $subdevice, $busid) =
1004 (uc($1), uc($2), "\\*", "\\*", hex($3));
1005 $conf = 1;
1006 foreach my $card (@{$cards}) {
1007 if ($card->{vendor} eq $vendor and
1008 $card->{device} eq $device and
1009 ($busid eq $WRAP_USB_BUS or $busid eq 0 or
1010 $busid eq $WRAP_PCI_BUS)) {
1011 $ret .= "\n\tdevice ($vendor:$device) present";
1012 $conf = 2;
1013 last;
1017 next if ($conf le 1);
1018 # find if kernel knows of an alternate driver for this device
1019 my $devstring;
1020 if ($busid eq $WRAP_USB_BUS or $busid eq 0) {
1021 $devstring = sprintf("usb:v%sp%sd", $vendor, $device);
1022 } elsif ($busid eq $WRAP_PCI_BUS) {
1023 $devstring = sprintf("pci:v0000%sd0000%ssv", $vendor, $device);
1024 } else {
1025 next;
1027 open(MODPROBE, "modprobe -c|") or next;
1028 while (my $line = <MODPROBE>) {
1029 my $alt;
1030 chomp($line);
1031 next if $line !~ /$devstring/;
1032 $alt = (split(' ', $line))[-1];
1033 chomp($alt);
1034 if (length($alt) gt 0 and $alt ne "ndiswrapper") {
1035 $ret .= " (alternate driver: $alt)";
1036 last;
1039 close(MODPROBE);
1041 close(LS2);
1042 printf DBG "driver: $driver, $ret\n";
1043 return $ret;
1046 ## Local Variables: ##
1047 ## cperl-indent-level: 4 ##
1048 ## End: ##