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