1#!/bin/sh 2# Tcl magic -*- tcl -*- \ 3exec 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# 86proc 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 126proc 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# 145proc 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# 235proc 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# 311proc 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# 351proc 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# 384proc 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! 491proc 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# 641proc 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 673proc 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 713proc 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 753proc 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# 775proc 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# 838proc 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# 865proc 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# 909proc 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# 932proc 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# 964proc 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# 990proc 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################################################################################ 1081main; 1082