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