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