]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/kdrv/KernelDriver
stress2: Added a few regression tests
[FreeBSD/FreeBSD.git] / tools / tools / kdrv / KernelDriver
1 #!/bin/sh
2 # Tcl magic -*- tcl -*- \
3 exec tclsh $0 $*
4 ################################################################################
5 #
6 # KernelDriver - FreeBSD driver source installer
7 #
8 ################################################################################
9 #
10 # Copyright (C) 1997
11 #      Michael Smith.  All rights reserved.
12 #
13 # Redistribution and use in source and binary forms, with or without
14 # modification, are permitted provided that the following conditions
15 # are met:
16 # 1. Redistributions of source code must retain the above copyright
17 #    notice, this list of conditions and the following disclaimer.
18 # 2. Redistributions in binary form must reproduce the above copyright
19 #    notice, this list of conditions and the following disclaimer in the
20 #    documentation and/or other materials provided with the distribution.
21 # 3. Neither the name of the author nor the names of any co-contributors
22 #    may be used to endorse or promote products derived from this software
23 #    without specific prior written permission.
24 #
25 # THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND
26 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 # ARE DISCLAIMED.  IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE
29 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 # SUCH DAMAGE.
36 #
37 ################################################################################
38 #
39 # KernelDriver provides a means for installing source-form drivers into FreeBSD 
40 # kernel source trees in an automated fashion.  It can also remove drivers it 
41 # has installed.
42 #
43 # Driver information is read from a control file, with the following syntax :
44 #
45 # description {<text>}          Driver description; used in comments inserted into
46 #                               files.
47 # driver <name>                 The name of the driver. (Note that this can't end in .drvinfo :)
48 # filei386 <path> <name>        The file <name> in the driver package is installed into
49 #                               <path> in the kernel source tree.  Files whose names
50 #                               end in '.c' have an entry added to i386/conf/files.i386.
51 # fileconf <path> <name>        The file <name> in the driver package is installed into
52 #                               <path> in the kernel source tree.  Files whose names
53 #                               end in '.c' have an entry added to conf/files.
54 # optioni386 <name> <hdr>       Adds an entry to i386/conf/options.i386, such that
55 #                               the option <name> will be placed in the header <hdr>.
56 # optionconf <name> <hdr>       Adds an entry to conf/options, such that
57 #                               the option <name> will be placed in the header <hdr>.
58 # linttext                      Lines between this and a subsequent 'end' line are added
59 #                               to the LINT file to provide configuration examples,
60 #                               comments, etc.
61 # end                           Ends a text region.
62
63 # Possible additions :
64 #
65 # patch <name>          Applies the patch contained in <name>; patch is invoked
66 #                       at the top level of the kernel source tree, and the 
67 #                       patch must apply cleanly (this is checked).
68 #
69 # option <name> <file>  Adds an entry to i386/conf/options.i386
70 #
71 # Lines beginning with '#' or blanks are considered comments, except in
72 # 'linttext' regions.
73 #
74 ################################################################################
75 #
76 #
77 ################################################################################
78
79 ################################################################################
80 # findDrvFile
81 #
82 # Given (hint), use it to locate a driver information file.
83 # (Possible extension; support drivers in gzipped tarballs...)
84 #
85 proc findDrvFile_try {hint} {
86
87     # points to something already
88     if {[file exists $hint]} {
89         # unwind symbolic links
90         while {[file type $hint] == "link"} {
91             set hint [file readlink $hint];
92         }
93         switch [file type $hint] {
94             file {
95                 # run with it as it is
96                 return $hint;
97             }
98             directory {
99                 # look for a drvinfo file in the directory
100                 set candidate [glob -nocomplain "$hint/*.drvinfo"];
101                 switch [llength $candidate] {
102                     0 {
103                         # nothing there
104                     }
105                     1 {
106                         return $candidate;
107                     }
108                     default {
109                         error "multiple driver info files in directory : $hint";
110                     }
111                 }
112             }
113             default {
114                 error "driver info file may be a typewriter : $hint";
115             }
116         }
117     }
118     # maybe we need an extension
119     if {[file exists $hint.drvinfo]} {
120         return $hint.drvinfo;
121     }
122     error "can't find a driver info file using '$hint'";
123 }
124
125 proc findDrvFile {hint} {
126
127     set result [findDrvFile_try $hint];
128     if {$result != ""} {
129         return $result;
130     }
131     set result [findDrvFile_try ${hint}.drvinfo];
132     if {$result != ""} {
133         return $result;
134     }
135     error "can't find driver information file using : $hint";
136 }
137
138 ################################################################################
139 # readDrvFile
140 #
141 # Reads the contents of (fname), which are expected to be in the format 
142 # described above, and fill in the global Drv array.
143 #
144 proc readDrvFile {fname} {
145
146     global Drv Options;
147
148     if {$Options(verbose)} {puts "+ read options from '$fname'";}
149     set fh [open $fname r];
150     
151     # set defaults
152     set Drv(description) "";
153     set Drv(driver) "";
154     set Drv(filesi386) "";
155     set Drv(filesconf) "";
156     set Drv(optionsi386) "";
157     set Drv(optionsconf) "";
158     set Drv(patches) "";
159     set Drv(linttext) "";
160
161     while {[gets $fh line] >= 0} {
162         
163         # blank lines/comments
164         if {([llength $line] == 0) ||
165             ([string index $line 0] == "\#")} {
166             continue ;
167         }
168
169         # get keyword, process
170         switch -- [lindex $line 0] {
171             description {
172                 set Drv(description) [lindex $line 1];
173             }
174             driver {
175                 set Drv(driver) [lindex $line 1];
176             }
177             filei386 {
178                 set path [lindex $line 1];
179                 set plast [expr [string length $path] -1];
180                 if {[string index $path $plast] != "/"} {
181                     append path "/";
182                 }
183                 set name [lindex $line 2];
184                 set Drv(filei386:$name) $path;
185                 lappend Drv(filesi386) $name;
186             }
187             fileconf {
188                 set path [lindex $line 1];
189                 set plast [expr [string length $path] -1];
190                 if {[string index $path $plast] != "/"} {
191                     append path "/";
192                 }
193                 set name [lindex $line 2];
194                 set Drv(fileconf:$name) $path;
195                 lappend Drv(filesconf) $name;
196             }
197             optioni386 {
198                 set opt [lindex $line 1];
199                 set hdr [lindex $line 2];
200                 lappend Drv(optionsi386) $opt;
201                 set Drv(optioni386:$opt) $hdr;
202             }
203             optionconf {
204                 set opt [lindex $line 1];
205                 set hdr [lindex $line 2];
206                 lappend Drv(optionsconf) $opt;
207                 set Drv(optionconf:$opt) $hdr;
208             }
209             patch {
210                 lappend Drv(patches) [lindex $line 1];
211             }
212             linttext {
213                 while {[gets $fh line] >= 0} {
214                     if {$line == "end"} {
215                         break ;
216                     }
217                     lappend Drv(linttext) $line;
218                 }
219             }
220         }
221     }
222     close $fh;
223     if {$Options(verbose)} {
224         printDrv;
225     }
226 }
227                 
228 ################################################################################
229 # validateDrvPackage
230 #
231 # With the global Drv filled in, check that the files required are all in
232 # (dir), and that the kernel config at (kpath) can be written.
233 #
234 proc validateDrvPackage {dir kpath} {
235
236     global Drv Options;
237
238     if {$Options(verbose)} {puts "+ checking driver package...";}
239     set missing "";
240     set unwritable "";
241
242     # check files, patches
243     foreach f $Drv(filesi386) {
244         if {![file readable $dir$f]} {
245             lappend missing $f;
246         }
247     }
248     foreach f $Drv(filesconf) {
249         if {![file readable $dir$f]} {
250             lappend missing $f;
251         }
252     }
253     foreach f $Drv(patches) {
254         if {![file readable $dir$f]} {
255             lappend missing $f;
256         }
257     }
258     if {$missing != ""} {
259         error "missing files : $missing";
260     }
261
262     # check writability
263     if {$Options(verbose)} {puts "+ checking kernel source writability...";}
264     foreach f $Drv(filesi386) {
265         set p $Drv(filei386:$f);
266         if {![file isdirectory $kpath$p]} {
267             lappend missing $p;
268         } else {
269             if {![file writable $kpath$p]} {
270                 if {[lsearch -exact $unwritable $p] == -1} {
271                     lappend unwritable $p;
272                 }
273             }
274         }
275     }
276     foreach f $Drv(filesconf) {
277         set p $Drv(fileconf:$f);
278         if {![file isdirectory $kpath$p]} {
279             lappend missing $p;
280         } else {
281             if {![file writable $kpath$p]} {
282                 if {[lsearch -exact $unwritable $p] == -1} {
283                     lappend unwritable $p;
284                 }
285             }
286         }
287     }
288     foreach f [list \
289                    "conf/files" \
290                    "i386/conf/files.i386" \
291                    "i386/conf/options.i386" \
292                    "i386/conf/LINT"] {
293         if {![file writable $kpath$f]} {
294             lappend unwritable $f;
295         }
296     }
297     if {$missing != ""} {
298         error "missing directories : $missing";
299     }
300     if {$unwritable != ""} {
301         error "can't write to : $unwritable";
302     }
303 }
304
305 ################################################################################
306 # installDrvFiles
307 #
308 # Install the files listed in the global Drv into (kpath) from (dir)
309 #
310 proc installDrvFiles {dir kpath} {
311
312     global Drv Options;
313
314     # clear 'installed' record
315     set Drv(installedi386) "";
316     set Drv(installedconf) "";
317     set failed "";
318
319     if {$Options(verbose)} {puts "+ installing driver files...";}
320     foreach f $Drv(filesi386) {
321         if {$Options(verbose)} {puts "$f -> $kpath$Drv(filei386:$f)";}
322         if {$Options(real)} {
323             if {[catch {exec cp $dir$f $kpath$Drv(filei386:$f)} msg]} {
324                 lappend failed $f;
325             } else {
326                 lappend Drv(installedi386) $f;
327             }
328         }
329     }
330     foreach f $Drv(filesconf) {
331         if {$Options(verbose)} {puts "$f -> $kpath$Drv(fileconf:$f)";}
332         if {$Options(real)} {
333             if {[catch {exec cp $dir$f $kpath$Drv(fileconf:$f)} msg]} {
334                 lappend failed $f;
335             } else {
336                 lappend Drv(installedconf) $f;
337             }
338         }
339     }
340     if {$failed != ""} {
341         error "failed to install files : $failed";
342     }
343 }
344
345 ################################################################################
346 # backoutDrvChanges
347 #
348 # Remove files from a failed installation in (kpath)
349 #
350 proc backoutDrvChanges {kpath} {
351
352     global Drv Options;
353
354     if {$Options(verbose)} {puts "+ backing out installed files...";}
355     # delete installed files
356     foreach f $Drv(installedi386) {
357         exec rm -f $kpath$Drv(filei386:$f)$f;
358     }
359     foreach f $Drv(installedconf) {
360         exec rm -f $kpath$Drv(fileconf:$f)$f;
361     }
362 }
363
364 ################################################################################
365 # registerDrvFiles
366 #
367 # Adds an entry to i386/conf/files.i386 and conf/files for the .c files in the driver.  
368 # (kpath) points to the kernel.
369 #
370 # A comment is added to the file preceding the new entries :
371 #
372 #  ## driver: <drivername>
373 #  # <description>
374 #  # filei386: <path><file>
375 #  <file spec (.c files only)>
376 #  ## enddriver
377 #
378 # We only append to the end of the file.
379 #
380 # Add linttext to the LINT file.
381 # Add options to i386/conf/options.i386 if any are specified
382 #
383 proc registerDrvFiles {kpath} {
384
385     global Drv Options;
386
387     if {$Options(verbose)} {puts "+ registering installed files...";}
388
389 # Add stuff to LINT
390     if {$Drv(linttext) != ""} {
391
392         if {$Options(verbose)} {puts "+ updating LINT...";}
393         if {$Options(real)} {
394             set fname [format "%si386/conf/LINT" $kpath];
395             set fh [open $fname a];
396
397             # header
398             puts $fh "\#\# driver: $Drv(driver)";
399             puts $fh "\# $Drv(description)";
400             foreach l $Drv(linttext) {
401                 puts $fh $l;
402             }
403             puts $fh "\#\# enddriver";
404             close $fh;
405         }
406     }
407
408 # Do filesi386 stuff
409     if {$Options(real)} {
410         set fname [format "%si386/conf/files.i386" $kpath];
411         set fh [open $fname a];
412
413         # header
414         puts $fh "\#\# driver: $Drv(driver)";
415         puts $fh "\# $Drv(description)";
416         # file information
417         foreach f $Drv(filesi386) {
418             puts $fh "\# file: $Drv(filei386:$f)$f";
419             # is it a compilable object?
420             if {[string match "*.c" $f]} {
421                 puts $fh "$Drv(filei386:$f)$f\t\toptional\t$Drv(driver)\tdevice-driver";
422             }
423         }
424         puts $fh "\#\# enddriver";
425         close $fh;
426     }
427     if {$Drv(optionsi386) != ""} {
428         if {$Options(verbose)} {puts "+ adding options...";}
429         if {$Options(real)} {
430             set fname [format "%si386/conf/options.i386" $kpath];
431             set fh [open $fname a];
432
433             # header
434             puts $fh "\#\# driver: $Drv(driver)";
435             puts $fh "\# $Drv(description)";
436             # options
437             foreach opt $Drv(optionsi386) {
438                 puts $fh "$opt\t$Drv(optioni386:$opt)";
439             }
440             puts $fh "\#\# enddriver";
441             close $fh;
442         }
443     }
444
445 # Do filesconf stuff
446     if {$Options(real)} {
447         set fname [format "%sconf/files" $kpath];
448         set fh [open $fname a];
449
450         # header
451         puts $fh "\#\# driver: $Drv(driver)";
452         puts $fh "\# $Drv(description)";
453         # file information
454         foreach f $Drv(filesconf) {
455             puts $fh "\# file: $Drv(fileconf:$f)$f";
456             # is it a compilable object?
457             if {[string match "*.c" $f]} {
458                 puts $fh "$Drv(fileconf:$f)$f\t\toptional\t$Drv(driver)\tdevice-driver";
459             }
460         }
461         puts $fh "\#\# enddriver";
462         close $fh;
463     }
464      if {$Drv(optionsconf) != ""} {
465         if {$Options(verbose)} {puts "+ adding options...";}
466         if {$Options(real)} {
467             set fname [format "%sconf/options" $kpath];
468             set fh [open $fname a];
469
470             # header
471             puts $fh "\#\# driver: $Drv(driver)";
472             puts $fh "\# $Drv(description)";
473             # options
474             foreach opt $Drv(optionsconf) {
475                 puts $fh "$opt\t$Drv(optionconf:$opt)";
476             }
477             puts $fh "\#\# enddriver";
478             close $fh;
479         }
480      }
481
482 }
483
484 ################################################################################
485 # listInstalledDrv
486 #
487 # List all drivers recorded as installed, in the kernel at (kpath)
488 #
489 # XXX : fix me so I understand conf/{options,files} stuff!
490 proc listInstalledDrv {kpath} {
491
492     global Drv;
493
494     # pick up all the i386 options information first
495     set fname [format "%si386/conf/options.i386" $kpath];
496     if {![file readable $fname]} {
497         error "not a kernel directory";
498     }
499     set fh [open $fname r];
500
501     while {[gets $fh line] >= 0} {
502     
503         # got a driver?
504         if {[scan $line "\#\# driver: %s" driver] == 1} {
505             # read driver details, ignore
506             gets $fh line;
507             # loop reading option details
508             while {[gets $fh line] >= 0} {
509                 # end of driver info
510                 if {$line == "\#\# enddriver"} {
511                     break ;
512                 }
513                 # parse option/header tuple
514                 if {[scan $line "%s %s" opt hdr] == 2} {
515                     # remember that this driver uses this option
516                     lappend drivers($driver:optionsi386) $opt;
517                     # remember that this option goes in this header
518                     set optionsi386($opt) $hdr;
519                 }
520             }
521         }
522     }
523     close $fh;
524
525     # pick up all the conf options information first
526     set fname [format "%sconf/options" $kpath];
527     if {![file readable $fname]} {
528         error "not a kernel directory";
529     }
530     set fh [open $fname r];
531
532     while {[gets $fh line] >= 0} {
533     
534         # got a driver?
535         if {[scan $line "\#\# driver: %s" driver] == 1} {
536             # read driver details, ignore
537             gets $fh line;
538             # loop reading option details
539             while {[gets $fh line] >= 0} {
540                 # end of driver info
541                 if {$line == "\#\# enddriver"} {
542                     break ;
543                 }
544                 # parse option/header tuple
545                 if {[scan $line "%s %s" opt hdr] == 2} {
546                     # remember that this driver uses this option
547                     lappend drivers($driver:optionsconf) $opt;
548                     # remember that this option goes in this header
549                     set optionsconf($opt) $hdr;
550                 }
551             }
552         }
553     }
554     close $fh;
555
556     set fname [format "%si386/conf/files.i386" $kpath];
557     set fh [open $fname r];
558     
559     while {[gets $fh line] >= 0} {
560
561         # got a driver? 
562         if {[scan $line "\#\# driver: %s" driver] == 1} {
563             # clear global and reset
564             catch {unset Drv};
565             set Drv(driver) $driver;
566             # read driver details
567             gets $fh line;
568             set Drv(description) [string range $line 2 end];
569             set Drv(filesi386) "";
570             # options?
571             if {[info exists drivers($Drv(driver):optionsi386)]} {
572                 set Drv(optionsi386) $drivers($Drv(driver):optionsi386);
573                 # get pathnames
574                 foreach opt $Drv(optionsi386) {
575                     set Drv(optioni386:$opt) $optionsi386($opt);
576                 }
577             }
578             # loop reading file details
579             while {[gets $fh line] >= 0} {
580                 if {$line == "\#\# enddriver"} {
581                     # print this driver and loop
582                     printDrv;
583                     break ;
584                 }
585                 if {[scan $line "\# filei386: %s" fpath] == 1} {
586                     set f [file tail $fpath];   
587                     set Drv(filei386:$f) "[file dirname $fpath]/";
588                     lappend Drv(filesi386) $f;
589                 }
590             }
591         }
592     }
593     close $fh;
594
595     set fname [format "%sconf/files" $kpath];
596     set fh [open $fname r];
597     
598     while {[gets $fh line] >= 0} {
599
600         # got a driver? 
601         if {[scan $line "\#\# driver: %s" driver] == 1} {
602             # clear global and reset
603             catch {unset Drv};
604             set Drv(driver) $driver;
605             # read driver details
606             gets $fh line;
607             set Drv(description) [string range $line 2 end];
608             set Drv(filesconf) "";
609             # options?
610             if {[info exists drivers($Drv(driver):optionsconf)]} {
611                 set Drv(optionsconf) $drivers($Drv(driver):optionsconf);
612                 # get pathnames
613                 foreach opt $Drv(optionsconf) {
614                     set Drv(optionconf:$opt) $optionsconf($opt);
615                 }
616             }
617             # loop reading file details
618             while {[gets $fh line] >= 0} {
619                 if {$line == "\#\# enddriver"} {
620                     # print this driver and loop
621                     printDrv;
622                     break ;
623                 }
624                 if {[scan $line "\# fileconf: %s" fpath] == 1} {
625                     set f [file tail $fpath];   
626                     set Drv(fileconf:$f) "[file dirname $fpath]/";
627                     lappend Drv(filesconf) $f;
628                 }
629             }
630         }
631     }
632     close $fh;
633 }
634
635 ################################################################################
636 # printDrv
637 #
638 # Print the contents of the global Drv.
639 #
640 proc printDrv {} {
641
642     global Drv Options;
643
644     puts "$Drv(driver) : $Drv(description)";
645     if {$Options(verbose)} {
646         foreach f $Drv(filesi386) {
647             puts " $Drv(filei386:$f)$f"
648         }
649         foreach f $Drv(filesconf) {
650             puts " $Drv(fileconf:$f)$f"
651         }
652         if {[info exists Drv(optionsi386)]} {
653             foreach opt $Drv(optionsi386) {
654                 puts " $opt in $Drv(optioni386:$opt)";
655             }
656         }
657         if {[info exists Drv(optionsconf)]} {
658             foreach opt $Drv(optionsconf) {
659                 puts " $opt in $Drv(optionconf:$opt)";
660             }
661         }
662     }
663 }
664
665 ################################################################################
666 # findInstalledDrv
667 #
668 # Given a kernel tree at (kpath), get driver details about an installed
669 # driver (drvname)
670 #
671
672 proc findInstalledDrvi386 {drvname kpath} {
673
674     global Drv;
675
676     set fname [format "%si386/conf/files.i386" $kpath];
677     set fh [open $fname r];
678     
679     puts "checking i386/conf/files.i386";
680
681     while {[gets $fh line] >= 0} {
682         if {[scan $line "\#\# driver: %s" name] == 1} {
683             if {$name != $drvname} {
684                 continue ;              # not us
685             }
686             # read information
687             set Drv(driver) $drvname;
688             set line [gets $fh];
689             set Drv(description) [string range $line 2 end];
690             set Drv(filesi386) "";
691             # loop reading file details
692             while {[gets $fh line] >= 0} {
693                 if {$line == "\#\# enddriver"} {
694                     close $fh;
695                     return 1;           # all done
696                 }
697                 if {[scan $line "\# file: %s" fpath] == 1} {
698                     set f [file tail $fpath];   
699                     set Drv(filei386:$f) "[file dirname $fpath]/";
700                     lappend Drv(filesi386) $f;
701                 }
702             }
703             close $fh;
704             error "unexpected EOF reading '$fname'";
705         }
706     }
707     close $fh
708
709     return 0;
710 }
711
712 proc findInstalledDrvconf {drvname kpath} {
713
714     global Drv;
715
716     set fname [format "%sconf/files" $kpath];
717     set fh [open $fname r];
718
719     puts "checking conf/files";
720     
721     while {[gets $fh line] >= 0} {
722         if {[scan $line "\#\# driver: %s" name] == 1} {
723             if {$name != $drvname} {
724                 continue ;              # not us
725             }
726             # read information
727             set Drv(driver) $drvname;
728             set line [gets $fh];
729             set Drv(description) [string range $line 2 end];
730             set Drv(filesconf) "";
731             # loop reading file details
732             while {[gets $fh line] >= 0} {
733                 if {$line == "\#\# enddriver"} {
734                     close $fh;
735                     return 1;           # all done
736                 }
737                 if {[scan $line "\# file: %s" fpath] == 1} {
738                     set f [file tail $fpath];   
739                     set Drv(fileconf:$f) "[file dirname $fpath]/";
740                     lappend Drv(filesconf) $f;
741                 }
742             }
743             close $fh;
744             error "unexpected EOF reading '$fname'";
745         }
746     }
747     close $fh
748
749     return 0;
750 }
751
752 proc findInstalledDrv {drvname kpath} {
753
754     global Drv Options;
755
756     if {$Options(verbose)} {puts "+ look for driver '$drvname' in '$kpath'";}
757
758 # Whoops... won't work in a single if statement due to expression shortcircuiting
759     set a [findInstalledDrvi386 $drvname $kpath];
760     set b [findInstalledDrvconf $drvname $kpath];
761     if {$a || $b} {
762         return;
763     }
764
765     error "driver '$drvname' not recorded as installed";
766 }
767
768 ################################################################################
769 # validateDrvRemoval
770 #
771 # Verify that we can remove the driver described in the global Drv installed
772 # at (kpath).
773 #
774 proc validateDrvRemoval {kpath} {
775
776     global Drv Options;
777
778     set missing "";
779     set unwritable "";
780
781     if {$Options(verbose)} {puts "+ checking for removabilty...";}
782
783     # admin files?
784     foreach f [list \
785                    "i386/conf/files.i386" \
786                    "i386/conf/options.i386" \
787                    "i386/conf/LINT" \
788                    "conf/files" \
789                    "conf/options" ] { 
790         if {![file exists $kpath$f]} {
791             lappend missing $kpath$f;
792         } else {
793             if {![file writable $kpath$f]} {
794                 lappend unwritable $f;
795             }
796         }
797     }
798     # driver components?
799     foreach f $Drv(filesi386) {
800         set p $Drv(filei386:$f);
801         if {![file isdirectory $kpath$p]} {
802             lappend missing $p;
803         } else {
804             if {![file writable $kpath$p]} {
805                 if {[lsearch -exact $unwritable $p] == -1} {
806                     lappend unwritable $p;
807                 }
808             }
809         }
810     }
811     foreach f $Drv(filesconf) {
812         set p $Drv(fileconf:$f);
813         if {![file isdirectory $kpath$p]} {
814             lappend missing $p;
815         } else {
816             if {![file writable $kpath$p]} {
817                 if {[lsearch -exact $unwritable $p] == -1} {
818                     lappend unwritable $p;
819                 }
820             }
821         }
822     }
823     if {$missing != ""} {
824         error "files/directories missing : $missing";
825     }
826     if {$unwritable != ""} {
827         error "can't write to : $unwritable";
828     }
829 }
830
831 ################################################################################
832 # deleteDrvFiles
833 #
834 # Delete the files belonging to the driver devfined in the global Drv in
835 # the kernel tree at (kpath)
836 #
837 proc deleteDrvFiles {kpath} {
838
839     global Drv Options;
840
841     if {$Options(verbose)} {puts "+ delete driver files...";}
842
843     # loop deleting files
844     foreach f $Drv(filesi386) {
845         if {$Options(verbose)} {puts "- $Drv(filei386:$f)$f";}
846         if {$Options(real)} {
847             exec rm $kpath$Drv(filei386:$f)$f;
848         }
849     }
850     foreach f $Drv(filesconf) {
851         if {$Options(verbose)} {puts "- $Drv(fileconf:$f)$f";}
852         if {$Options(real)} {
853             exec rm $kpath$Drv(fileconf:$f)$f;
854         }
855     }
856 }    
857
858 ################################################################################
859 # unregisterDrvFiles
860 #
861 # Remove any mention of the current driver from the files.i386 and LINT
862 # files in (ksrc)
863 #
864 proc unregisterDrvFiles {ksrc} {
865
866     global Drv Options;
867
868     if {$Options(verbose)} {puts "+ deregister driver files...";}
869
870     # don't really do it?
871     if {!$Options(real)} { return ; }
872
873     foreach f [list \
874                    "i386/conf/files.i386" \
875                    "i386/conf/options.i386" \
876                    "i386/conf/LINT" \
877                    "conf/files" \
878                    "conf/options" ] {
879         set ifh [open $ksrc$f r];
880         set ofh [open $ksrc$f.new w];
881         set copying 1;
882
883         while {[gets $ifh line] >= 0} {
884
885             if {[scan $line "\#\# driver: %s" name] == 1} {
886                 if {$name == $Drv(driver)} {
887                     set copying 0;                      # don't copy this one
888                 }
889             }
890             if {$copying} {
891                 puts $ofh $line;                # copy through
892             }
893             if {$line == "\#\# enddriver"} {    # end of driver detail
894                 set copying 1;
895             }
896         }
897         close $ifh;
898         close $ofh;
899         exec mv $ksrc$f.new $ksrc$f;            # move new over old
900     }
901 }
902
903 ################################################################################
904 # usage
905 #
906 # Remind the user what goes where
907 #
908 proc usage {} {
909
910     global argv0;
911
912     set progname [file tail $argv0];
913
914     puts stderr "Usage is :";
915     puts stderr "  $progname \[-v -n\] add <drvinfo> \[<kpath>\]";
916     puts stderr "  $progname \[-v -n\] delete <drvname> \[<kpath>\]";
917     puts stderr "  $progname \[-v\] list \[<kpath>\]";
918     puts stderr "  <drvinfo> is a driver info file";
919     puts stderr "  <drvname> is a driver name";
920     puts stderr "  <kpath> is the path to the kernel source (default /sys/)";
921     puts stderr "  -v  be verbose";
922     puts stderr "  -n  don't actually do anything";
923     exit ;
924 }
925
926 ################################################################################
927 # getOptions
928 #
929 # Parse commandline options, return anything that doesn't look like an option
930 #
931 proc getOptions {} {
932
933     global argv Options;
934
935     set Options(real) 1;
936     set Options(verbose) 0;
937     set ret "";
938     
939     for {set index 0} {$index < [llength $argv]} {incr index} {
940         
941         switch -- [lindex $argv $index] {
942
943             -n {
944                 set Options(real) 0;            # 'do-nothing' mode
945             }
946             -v {
947                 set Options(verbose) 1;         # brag
948             }
949             default {
950                 lappend ret [lindex $argv $index];
951             }
952         }
953     }
954     return $ret;
955 }
956
957 ################################################################################
958 # getKpath
959 #
960 # Given (hint), return the kernel path.  If (hint) is empty, return /sys.
961 # If the kernel path is not a directory, complain and dump the usage.
962 #
963 proc getKpath {hint} {
964
965     set kpath "";
966
967     # check the kernel path
968     if {$hint == ""} {
969         set kpath "/sys/";
970     } else {
971         set kpath $hint;
972     }
973     if {![file isdirectory $kpath]} {
974         puts "not a directory : $kpath";
975         usage ;
976     }
977     set plast [expr [string length $kpath] -1];
978     if {[string index $kpath $plast] != "/"} {
979         append kpath "/";
980     }
981     return $kpath;
982 }
983
984 ################################################################################
985 # main
986 #
987 # Start somewhere here.
988 #
989 proc main {} {
990
991     global Options;
992
993     # Work out what we're trying to do
994     set cmdline [getOptions];
995     set mode [lindex $cmdline 0];
996
997     # do stuff
998     switch -- $mode {
999         add {
1000             set hint [lindex $cmdline 1];
1001             set kpath [getKpath [lindex $cmdline 2]];
1002
1003             # check driver file argument
1004             if {[catch {set drv [findDrvFile $hint]} msg]} {
1005                 puts stderr $msg;
1006                 usage ;
1007             }
1008             if {([file type $drv] != "file") ||
1009                 ![file readable $drv]} {
1010                 puts "can't read driver file : $drv";
1011                 usage ; 
1012             }
1013             set drvdir "[file dirname $drv]/";
1014
1015             # read driver file
1016             if {[catch {readDrvFile $drv} msg]} {
1017                 puts stderr $msg;
1018                 exit ;
1019             }
1020             # validate driver
1021             if {[catch {validateDrvPackage $drvdir $kpath} msg]} {
1022                 puts stderr $msg;
1023                 exit ;
1024             }
1025             # install new files
1026             if {[catch {installDrvFiles $drvdir $kpath} msg]} {
1027                 backoutDrvChanges $kpath;               # oops, unwind
1028                 puts stderr $msg;
1029                 exit ;
1030             }
1031             # register files in config
1032             if {[catch {registerDrvFiles $kpath} msg]} {
1033                 backoutDrvChanges $kpath;               # oops, unwind
1034                 puts stderr $msg;
1035                 exit ;
1036             }
1037         }
1038         delete {
1039             set drv [lindex $cmdline 1];
1040             set kpath [getKpath [lindex $cmdline 2]];
1041
1042             if {[string last ".drvinfo" $drv] != -1} {
1043                 set drv [string range $drv 0 [expr [string length $drv] - 9]];
1044                 puts "Driver name ends in .drvinfo, removing, is now $drv";
1045             }
1046
1047             if {[catch {findInstalledDrv $drv $kpath} msg]} {
1048                 puts stderr $msg;
1049                 exit ;
1050             }
1051             if {[catch {validateDrvRemoval $kpath} msg]} {
1052                 puts stderr $msg;
1053                 exit ;
1054             }
1055             if {[catch {unregisterDrvFiles $kpath} msg]} {
1056                 puts stderr $msg;
1057                 exit ;
1058             }
1059             if {[catch {deleteDrvFiles $kpath} msg]} {
1060                 puts stderr $msg;
1061                 exit ;
1062             }
1063         }
1064         list { 
1065             set kpath [getKpath [lindex $cmdline 1]];
1066             if {[catch {listInstalledDrv $kpath} msg]} {
1067                 puts stderr "can't list drivers in '$kpath' : $msg";
1068             }
1069         }
1070         default {
1071             puts stderr "unknown command '$mode'";
1072             usage ;
1073         }
1074     }
1075 }
1076
1077
1078
1079 ################################################################################
1080 main;