diff -urN linux-2.2.20/Documentation/Configure.help linux/Documentation/Configure.help --- linux-2.2.20/Documentation/Configure.help Fri Nov 2 09:39:05 2001 +++ linux/Documentation/Configure.help Mon Dec 3 22:13:01 2001 @@ -9220,6 +9220,24 @@ This option will enlarge your kernel by about 18 KB. Several programs depend on this, so everyone should say Y here. +/dev filesystem support (EXPERIMENTAL) +CONFIG_DEVFS_FS + This is another virtual filesystem (like /proc) which provides the + filesystem interface to device drivers, normally found in /dev. + Devfs does not depend on major and minor number allocations. Device + drivers register entries in /dev which appear automagically. Without + devfs you need to populate /dev with hundreds, even thousands of + inodes. + This is work in progress. If you want to use this you *must* read + Documentation/filesystems/devfs/README + +Enable devfs debugging output +CONFIG_DEVFS_DEBUG + This option appears if you have CONFIG_DEVFS_FS enabled. Setting + this to 'Y' enables devfs debugging output. See the file + Documentation/filesystems/devfs/boot-options for more details. + The default is 'N'. + NFS filesystem support CONFIG_NFS_FS If you are connected to some other (usually local) Unix computer @@ -9587,6 +9605,8 @@ The GNU C library glibc 2.1 contains the requisite support for this mode of operation; you also need client programs that use the Unix98 API. + + Note that CONFIG_DEVFS_FS is a more general facility. UnixWare slices support (EXPERIMENTAL) CONFIG_UNIXWARE_DISKLABEL diff -urN linux-2.2.20/Documentation/filesystems/00-INDEX linux/Documentation/filesystems/00-INDEX --- linux-2.2.20/Documentation/filesystems/00-INDEX Sun Mar 25 09:31:56 2001 +++ linux/Documentation/filesystems/00-INDEX Mon Dec 3 22:13:01 2001 @@ -6,6 +6,8 @@ - info and mount options for the Amiga Fast File System. coda.txt - description of the CODA filesystem. +devfs + - directory containing devfs documentation. fat_cvf.txt - info on the Compressed Volume Files extension to the FAT filesystem hpfs.txt diff -urN linux-2.2.20/Documentation/filesystems/devfs/ChangeLog linux/Documentation/filesystems/devfs/ChangeLog --- linux-2.2.20/Documentation/filesystems/devfs/ChangeLog Wed Dec 31 17:00:00 1969 +++ linux/Documentation/filesystems/devfs/ChangeLog Mon Dec 3 23:23:32 2001 @@ -0,0 +1,1037 @@ +/* -*- auto-fill -*- */ +=============================================================================== +Changes for patch v1 + +- creation of devfs + +- modified miscellaneous character devices to support devfs +=============================================================================== +Changes for patch v2 + +- bug fix with manual inode creation +=============================================================================== +Changes for patch v3 + +- bugfixes + +- documentation improvements + +- created a couple of scripts (one to save&restore a devfs and the + other to set up compatibility symlinks) + +- devfs support for SCSI discs. New name format is: sd_hHcCiIlL +=============================================================================== +Changes for patch v4 + +- bugfix for the directory reading code + +- bugfix for compilation with kerneld + +- devfs support for generic hard discs + +- rationalisation of the various watchdog drivers +=============================================================================== +Changes for patch v5 + +- support for mounting directly from entries in the devfs (it doesn't + need to be mounted to do this), including the root filesystem. + Mounting of swap partitions also works. Hence, now if you set + CONFIG_DEVFS_ONLY to 'Y' then you won't be able to access your discs + via ordinary device nodes. Naturally, the default is 'N' so that you + can still use your old device nodes. If you want to mount from devfs + entries, make sure you use: append = "root=/dev/sd_..." in your + lilo.conf. It seems LILO looks for the device number (major&minor) + and writes that into the kernel image :-( + +- support for character memory devices (/dev/null, /dev/zero, /dev/full + and so on). Thanks to C. Scott Ananian +=============================================================================== +Changes for patch v6 + +- support for subdirectories + +- support for symbolic links (created by devfs_mk_symlink(), no + support yet for creation via symlink(2)) + +- SCSI disc naming now cast in stone, with the format: + /dev/sd/c0b1t2u3 controller=0, bus=1, ID=2, LUN=3, whole disc + /dev/sd/c0b1t2u3p4 controller=0, bus=1, ID=2, LUN=3, 4th partition + +- loop devices now appear in devfs + +- tty devices, console, serial ports, etc. now appear in devfs + Thanks to C. Scott Ananian + +- bugs with mounting devfs-only devices now fixed +=============================================================================== +Changes for patch v7 + +- SCSI CD-ROMS, tapes and generic devices now appear in devfs +=============================================================================== +Changes for patch v8 + +- bugfix with no-rewind SCSI tapes + +- RAMDISCs now appear in devfs + +- better cleaning up of devfs entries created by various modules + +- interface change to +=============================================================================== +Changes for patch v9 + +- the v8 patch was corrupted somehow, which would affect the patch for + linux/fs/filesystems.c + I've also fixed the v8 patch file on the WWW + +- MetaDevices (/dev/md*) should now appear in devfs +=============================================================================== +Changes for patch v10 + +- bugfix in meta device support for devfs + +- created this ChangeLog file + +- added devfs support to the floppy driver + +- added support for creating sockets in a devfs +=============================================================================== +Changes for patch v11 + +- added DEVFS_FL_HIDE_UNREG flag + +- incorporated better patch for ttyname() in libc 5.4.43 from H.J. Lu. + +- interface change to + +- support for creating symlinks with symlink(2) + +- parallel port printer (/dev/lp*) now appears in devfs +=============================================================================== +Changes for patch v12 + +- added inode check to function + +- improved devfs support when mounting from devfs + +- added call to <> operation when removing swap areas on + devfs devices + +- increased NR_SUPER to 128 to support large numbers of devfs mounts + (for chroot(2) gaols) + +- fixed bug in SCSI disc support: was generating incorrect minors if + SCSI ID's did not start at 0 and increase by 1 + +- support symlink traversal when mounting root +=============================================================================== +Changes for patch v13 + +- added devfs support to soundcard driver + Thanks to Eric Dumas and + C. Scott Ananian + +- added devfs support to the joystick driver + +- loop driver now has it's own subdirectory "/dev/loop/" + +- created and functions + +- fix problem with SCSI disc compatibility names (sd{a,b,c,d,e,f}) + which assumes ID's start at 0 and increase by 1. Also only create + devfs entries for SCSI disc partitions which actually exist + Show new names in partition check + Thanks to Jakub Jelinek +=============================================================================== +Changes for patch v14 + +- bug fix in floppy driver: would not compile without + CONFIG_DEVFS_FS='Y' + Thanks to Jurgen Botz + +- bug fix in loop driver + Thanks to C. Scott Ananian + +- do not create devfs entries for printers not configured + Thanks to C. Scott Ananian + +- do not create devfs entries for serial ports not present + Thanks to C. Scott Ananian + +- ensure is exported from tty_io.c + Thanks to C. Scott Ananian + +- allow unregistering of devfs symlink entries + +- fixed bug in SCSI disc naming introduced in last patch version +=============================================================================== +Changes for patch v15 + +- ported to kernel 2.1.81 +=============================================================================== +Changes for patch v16 + +- created function + +- moved DEVFS_SUPER_MAGIC into header file + +- added DEVFS_FL_HIDE flag + +- created + +- created + +- fixed bugs in searching by major&minor + +- changed interface to , and + + +- fixed inode times when symlink created with symlink(2) + +- change tty driver to do auto-creation of devfs entries + Thanks to C. Scott Ananian + +- fixed bug in genhd.c: whole disc (non-SCSI) was not registered to + devfs + +- updated libc 5.4.43 patch for ttyname() +=============================================================================== +Changes for patch v17 + +- added CONFIG_DEVFS_TTY_COMPAT + Thanks to C. Scott Ananian + +- bugfix in devfs support for drivers/char/lp.c + Thanks to C. Scott Ananian + +- clean up serial driver so that PCMCIA devices unregister correctly + Thanks to C. Scott Ananian + +- fixed bug in genhd.c: whole disc (non-SCSI) was not registered to + devfs [was missing in patch v16] + +- updated libc 5.4.43 patch for ttyname() [was missing in patch v16] + +- all SCSI devices now registered in /dev/sg + +- support removal of devfs entries via unlink(2) +=============================================================================== +Changes for patch v18 + +- added floppy/?u720 floppy entry + +- fixed kerneld support for entries in devfs subdirectories + +- incorporated latest patch for ttyname() in libc 5.4.43 from H.J. Lu. +=============================================================================== +Changes for patch v19 + +- bug fix when looking up unregistered entries: kerneld was not called + +- fixes for kernel 2.1.86 (now requires 2.1.86) +=============================================================================== +Changes for patch v20 + +- only create available floppy entries + Thanks to Andrzej Krzysztofowicz + +- new IDE naming scheme following SCSI format (i.e. /dev/id/c0b0t0u0p1 + instead of /dev/hda1) + Thanks to Andrzej Krzysztofowicz + +- new XT disc naming scheme following SCSI format (i.e. /dev/xd/c0t0p1 + instead of /dev/xda1) + Thanks to Andrzej Krzysztofowicz + +- new non-standard CD-ROM names (i.e. /dev/sbp/c#t#) + Thanks to Andrzej Krzysztofowicz + +- allow symlink traversal when mounting the root filesystem + +- Create entries for MD devices at MD init + Thanks to Christophe Leroy +=============================================================================== +Changes for patch v21 + +- ported to kernel 2.1.91 +=============================================================================== +Changes for patch v22 + +- SCSI host number patch ("scsihosts=" kernel option) + Thanks to Andrzej Krzysztofowicz +=============================================================================== +Changes for patch v23 + +- Fixed persistence bug with device numbers for manually created + device files + +- Fixed problem with recreating symlinks with different content + +- Added CONFIG_DEVFS_MOUNT (mount devfs on /dev at boot time) +=============================================================================== +Changes for patch v24 + +- Switched from CONFIG_KERNELD to CONFIG_KMOD: module autoloading + should now work again + +- Hide entries which are manually unlinked + +- Always invalidate devfs dentry cache when registering entries + +- Support removal of devfs directories via rmdir(2) + +- Ensure directories created by are visible + +- Default no access for "other" for floppy device +=============================================================================== +Changes for patch v25 + +- Updates to CREDITS file and minor IDE numbering change + Thanks to Andrzej Krzysztofowicz + +- Invalidate devfs dentry cache when making directories + +- Invalidate devfs dentry cache when removing entries + +- More informative message if root FS mount fails when devfs + configured + +- Fixed persistence bug with fifos +=============================================================================== +Changes for patch v26 + +- ported to kernel 2.1.97 + +- Changed serial directory from "/dev/serial" to "/dev/tts" and + "/dev/consoles" to "/dev/vc" to be more friendly to new procps +=============================================================================== +Changes for patch v27 + +- Added support for IDE4 and IDE5 + Thanks to Andrzej Krzysztofowicz + +- Documented "scsihosts=" boot parameter + +- Print process command when debugging kerneld/kmod + +- Added debugging for register/unregister/change operations + +- Added "devfs=" boot options + +- Hide unregistered entries by default +=============================================================================== +Changes for patch v28 + +- No longer lock/unlock superblock in (cope with + recent VFS interface change) + +- Do not automatically change ownership/protection of /dev/tty + +- Drop negative dentries when they are released + +- Manage dcache more efficiently +=============================================================================== +Changes for patch v29 + +- Added DEVFS_FL_AUTO_DEVNUM flag +=============================================================================== +Changes for patch v30 + +- No longer set unnecessary methods + +- Ported to kernel 2.1.99-pre3 +=============================================================================== +Changes for patch v31 + +- Added PID display to debugging message + +- Added "diread" and "diwrite" options + +- Ported to kernel 2.1.102 + +- Fixed persistence problem with permissions +=============================================================================== +Changes for patch v32 + +- Fixed devfs support in drivers/block/md.c +=============================================================================== +Changes for patch v33 + +- Support legacy device nodes + +- Fixed bug where recreated inodes were hidden + +- New IDE naming scheme: everything is under /dev/ide +=============================================================================== +Changes for patch v34 + +- Improved debugging in + +- Prevent duplicate calls to in SCSI layer + +- No longer free old dentries in + +- Free all dentries for a given entry when deleting inodes +=============================================================================== +Changes for patch v35 + +- Ported to kernel 2.1.105 (sound driver changes) +=============================================================================== +Changes for patch v36 + +- Fixed sound driver port +=============================================================================== +Changes for patch v37 + +- Minor documentation tweaks +=============================================================================== +Changes for patch v38 + +- More documentation tweaks + +- Fix for sound driver port + +- Removed ttyname-patch (grab libc 5.4.44 instead) + +- Ported to kernel 2.1.107-pre2 (loop driver fix) +=============================================================================== +Changes for patch v39 + +- Ported to kernel 2.1.107 (hd.c hunk broke due to spelling "fixes"). Sigh + +- Removed many #ifdef's, replaced with trickery in include/devfs_fs.h +=============================================================================== +Changes for patch v40 + +- Fix for sound driver port + +- Limit auto-device numbering to majors 128 to 239 +=============================================================================== +Changes for patch v41 + +- Fixed inode times persistence problem +=============================================================================== +Changes for patch v42 + +- Ported to kernel 2.1.108 (drivers/scsi/hosts.c hunk broke) +=============================================================================== +Changes for patch v43 + +- Fixed spelling in debug + +- Fixed bug in parsing "dilookup" + +- More #ifdef's removed + +- Supported Sparc keyboard (/dev/kbd) + +- Supported DSP56001 digital signal processor (/dev/dsp56k) + +- Supported Apple Desktop Bus (/dev/adb) + +- Supported Coda network file system (/dev/cfs*) +=============================================================================== +Changes for patch v44 + +- Fixed devfs inode leak when manually recreating inodes + +- Fixed permission persistence problem when recreating inodes +=============================================================================== +Changes for patch v45 + +- Ported to kernel 2.1.110 +=============================================================================== +Changes for patch v46 + +- Ported to kernel 2.1.112-pre1 + +- Removed harmless "unused variable" compiler warning + +- Fixed modes for manually recreated device nodes +=============================================================================== +Changes for patch v47 + +- Added NULL devfs inode warning in + +- Force all inode nlink values to 1 +=============================================================================== +Changes for patch v48 + +- Added "dimknod" option + +- Set inode nlink to 0 when freeing dentries + +- Added support for virtual console capture devices (/dev/vcs*) + Thanks to Dennis Hou + +- Fixed modes for manually recreated symlinks +=============================================================================== +Changes for patch v49 + +- Ported to kernel 2.1.113 +=============================================================================== +Changes for patch v50 + +- Fixed bugs in recreated directories and symlinks +=============================================================================== +Changes for patch v51 + +- Improved robustness of rc.devfs script + Thanks to Roderich Schupp + +- Fixed bugs in recreated device nodes + +- Fixed bug in currently unused + +- Defined new type + +- Improved debugging when getting entries + +- Fixed bug where directories could be emptied + +- Ported to kernel 2.1.115 +=============================================================================== +Changes for patch v52 + +- Replaced dummy .epoch inode with .devfsd character device + +- Modified rc.devfs to take acount of above change + +- Removed spurious driver warning messages when CONFIG_DEVFS_FS=n + +- Implemented devfsd protocol revision 0 +=============================================================================== +Changes for patch v53 + +- Ported to kernel 2.1.116 (kmod change broke hunk) + +- Updated Documentation/Configure.help + +- Test and tty pattern patch for rc.devfs script + Thanks to Roderich Schupp + +- Added soothing message to warning in +=============================================================================== +Changes for patch v54 + +- Ported to kernel 2.1.117 + +- Fixed default permissions in sound driver + +- Added support for frame buffer devices (/dev/fb*) +=============================================================================== +Changes for patch v55 + +- Ported to kernel 2.1.119 + +- Use GCC extensions for structure initialisations + +- Implemented async open notification + +- Incremented devfsd protocol revision to 1 +=============================================================================== +Changes for patch v56 + +- Ported to kernel 2.1.120-pre3 + +- Moved async open notification to end of +=============================================================================== +Changes for patch v57 + +- Ported to kernel 2.1.121 + +- Prepended "/dev/" to module load request + +- Renamed to + +- Created sample modules.conf file +=============================================================================== +Changes for patch v58 + +- Fixed typo "AYSNC" -> "ASYNC" +=============================================================================== +Changes for patch v59 + +- Added open flag for files +=============================================================================== +Changes for patch v60 + +- Ported to kernel 2.1.123-pre2 +=============================================================================== +Changes for patch v61 + +- Set i_blocks=0 and i_blksize=1024 in +=============================================================================== +Changes for patch v62 + +- Ported to kernel 2.1.123 +=============================================================================== +Changes for patch v63 + +- Ported to kernel 2.1.124-pre2 +=============================================================================== +Changes for patch v64 + +- Fixed Unix98 pty support + +- Increased buffer size in to avoid crash and + burn +=============================================================================== +Changes for patch v65 + +- More Unix98 pty support fixes + +- Added test for empty <> in + +- Renamed to and published + +- Created /dev/root symlink + Thanks to Roderich Schupp + with further modifications by me +=============================================================================== +Changes for patch v66 + +- Yet more Unix98 pty support fixes (now tested) + +- Created + +- Support media change checks when CONFIG_DEVFS_ONLY=y + +- Abolished Unix98-style PTY names for old PTY devices +=============================================================================== +Changes for patch v67 + +- Added inline declaration for dummy + +- Removed spurious "unable to register... in devfs" messages when + CONFIG_DEVFS_FS=n + +- Fixed misc. devices when CONFIG_DEVFS_FS=n + +- Limit auto-device numbering to majors 144 to 239 +=============================================================================== +Changes for patch v68 + +- Hide unopened virtual consoles from directory listings + +- Added support for video capture devices + +- Ported to kernel 2.1.125 +=============================================================================== +Changes for patch v69 + +- Fix for CONFIG_VT=n +=============================================================================== +Changes for patch v70 + +- Added support for non-OSS/Free sound cards +=============================================================================== +Changes for patch v71 + +- Ported to kernel 2.1.126-pre2 +=============================================================================== +Changes for patch v72 + +- #ifdef's for CONFIG_DEVFS_DISABLE_OLD_NAMES removed +=============================================================================== +Changes for patch v73 + +- CONFIG_DEVFS_DISABLE_OLD_NAMES replaced with "nocompat" boot option + +- CONFIG_DEVFS_BOOT_OPTIONS removed: boot options always available +=============================================================================== +Changes for patch v74 + +- Removed CONFIG_DEVFS_MOUNT and "mount" boot option and replaced with + "nomount" boot option + +- Documentation updates + +- Updated sample modules.conf +=============================================================================== +Changes for patch v75 + +- Updated sample modules.conf + +- Remount devfs after initrd finishes + +- Ported to kernel 2.1.127 + +- Added support for ISDN + Thanks to Christophe Leroy +=============================================================================== +Changes for patch v76 + +- Updated an email address in ChangeLog + +- CONFIG_DEVFS_ONLY replaced with "only" boot option +=============================================================================== +Changes for patch v77 + +- Added DEVFS_FL_REMOVABLE flag + +- Check for disc change when listing directories with removable media + devices + +- Use DEVFS_FL_REMOVABLE in sd.c + +- Ported to kernel 2.1.128 +=============================================================================== +Changes for patch v78 + +- Only call on first call to + +- Ported to kernel 2.1.129-pre5 + +- ISDN support improvements + Thanks to Christophe Leroy +=============================================================================== +Changes for patch v79 + +- Ported to kernel 2.1.130 + +- Renamed miscdevice "apm" to "apm_bios" to be consistent with + devices.txt +=============================================================================== +Changes for patch v80 + +- Ported to kernel 2.1.131 + +- Updated for VFS change in 2.1.131 +=============================================================================== +Changes for patch v81 + +- Fixed permissions on /dev/ptmx +=============================================================================== +Changes for patch v82 + +- Ported to kernel 2.1.132-pre4 + +- Changed initial permissions on /dev/pts/* + +- Created + +- Added "symlinks" boot option + +- Changed devfs_register_blkdev() back to register_blkdev() for IDE + +- Check for partitions on removable media in +=============================================================================== +Changes for patch v83 + +- Fixed support for ramdisc when using string-based root FS name + +- Ported to kernel 2.2.0-pre1 +=============================================================================== +Changes for patch v84 + +- Ported to kernel 2.2.0-pre7 +=============================================================================== +Changes for patch v85 + +- Compile fixes for driver/sound/sound_common.c (non-module) and + drivers/isdn/isdn_common.c + Thanks to Christophe Leroy + +- Added support for registering regular files + +- Created + +- Added /dev/cpu/mtrr as an alternative interface to /proc/mtrr + +- Update devfs inodes from entries if not changed through FS +=============================================================================== +Changes for patch v86 + +- Ported to kernel 2.2.0-pre9 +=============================================================================== +Changes for patch v87 + +- Fixed bug when mounting non-devfs devices in a devfs +=============================================================================== +Changes for patch v88 + +- Fixed to only initialise temporary inodes + +- Trap for NULL fops in + +- Return -ENODEV in for non-driver inodes + +- Fixed bug when unswapping non-devfs devices in a devfs +=============================================================================== +Changes for patch v89 + +- Switched to C data types in include/linux/devfs_fs.h + +- Switched from PATH_MAX to DEVFS_PATHLEN + +- Updated Documentation/filesystems/devfs/modules.conf to take account + of reverse scanning (!) by modprobe + +- Ported to kernel 2.2.0 +=============================================================================== +Changes for patch v90 + +- CONFIG_DEVFS_DISABLE_OLD_TTY_NAMES replaced with "nottycompat" boot + option + +- CONFIG_DEVFS_TTY_COMPAT removed: existing "symlinks" boot option now + controls this. This means you must have libc 5.4.44 or later, or a + recent version of libc 6 if you use the "symlinks" option +=============================================================================== +Changes for patch v91 + +- Switch from to in + drivers/char/vc_screen.c to fix problems with Midnight Commander +=============================================================================== +Changes for patch v92 + +- Ported to kernel 2.2.2-pre5 +=============================================================================== +Changes for patch v93 + +- Modified in drivers/scsi/sd.c to cope with devices that + don't exist (which happens with new RAID autostart code printk()s) +=============================================================================== +Changes for patch v94 + +- Fixed bug in joystick driver: only first joystick was registered +=============================================================================== +Changes for patch v95 + +- Fixed another bug in joystick driver + +- Fixed to not overrun event buffer +=============================================================================== +Changes for patch v96 + +- Ported to kernel 2.2.5-2 + +- Created + +- Fixed bugs: compatibility entries were not unregistered for: + loop driver + floppy driver + RAMDISC driver + IDE tape driver + SCSI CD-ROM driver + SCSI HDD driver +=============================================================================== +Changes for patch v97 + +- Fixed bugs: compatibility entries were not unregistered for: + ALSA sound driver + partitions in generic disc driver + +- Don't return unregistred entries in + +- Panic in if entry unregistered + +- Don't panic in for duplicates +=============================================================================== +Changes for patch v98 + +- Don't unregister already unregistered entries in + +- Register entry in + +- Unregister entry in + +- Changed to in drivers/char/tty_io.c + +- Ported to kernel 2.2.7 +=============================================================================== +Changes for patch v99 + +- Ported to kernel 2.2.8 + +- Fixed bug in drivers/scsi/sd.c when >16 SCSI discs + +- Disable warning messages when unable to read partition table for + removable media +=============================================================================== +Changes for patch v99.1 + +- Backported devfs-patch-v106 to kernel 2.2.9 + Thanks to BALATON Zoltan +=============================================================================== +Changes for patch v99.2 + +- Backported devfs-patch-v107 to kernel 2.2.9 + Thanks to BALATON Zoltan +=============================================================================== +Changes for patch v99.3 + +- Backported devfs-patch-v111 to kernel 2.2.10 + Thanks to BALATON Zoltan +=============================================================================== +Changes for patch v99.4 + +- Backported devfs-patch-v113 to kernel 2.2.10 + Thanks to BALATON Zoltan +=============================================================================== +Changes for patch v99.5 + +- Ported devfs-patch-v99.4 to kernel 2.2.12 + Thanks to Christian Czezatke +=============================================================================== +Changes for patch v99.6 + +- Ported devfs-patch-v99.5 to kernel 2.2.13 + Thanks to Martin Hoeller +=============================================================================== +Changes for patch v99.7 + +Work sponsored by SGI + +- Ported devfs-patch-v141 to kernel 2.2.13 +=============================================================================== +Changes for patch v99.8 + +Work sponsored by SGI + +- Bug fix in drivers/block/ide-probe.c (patch confusion) +=============================================================================== +Changes for patch v99.9 + +Work sponsored by SGI + +- Bug fix in drivers/block/md.c:partition_name() +=============================================================================== +Changes for patch v99.10 + +Work sponsored by SGI + +- Fixed bug in mounting root FS when initrd enabled + +- Support fifos when unregistering + +- Removed obsolete DEVFS_FL_COMPAT and DEVFS_FL_TOLERANT flags + +- Fixed compile problem with drivers/isdn/isdn_common.c + +- Removed kmod support: use devfsd instead + +- Moved miscellaneous character devices to /dev/misc + +- Ensure include/linux/joystick.h is OK for user-space + +- Improved debugging in + +- Ensure dentries created by devfsd will be cleaned up +=============================================================================== +Changes for patch v99.11 + +Work sponsored by SGI + +- Ported devfs-patch-v99.10 to kernel 2.2.14 + +- Moved /dev/tty0 to /dev/vc/0 +=============================================================================== +Changes for patch v99.12 + +Work sponsored by SGI + +- Ported devfs-patch-v99.11 to kernel 2.2.15 + +- Moved joystick devices to /dev/joysticks + +- Set inode->i_size to correct size for symlinks + +- Removed Documentation/filesystems/devfs/mk-devlinks + +- Don't create missing directories in + +- Updated sample modules.conf + +- Fixed bug in drivers/block/ide-probe.c when secondary but no primary + +- Updated Documentation/filesystems/devfs/README +=============================================================================== +Changes for patch v99.13 + +Work sponsored by SGI + +- Added support for network block devices + Thanks to Christian Czezatke +=============================================================================== +Changes for patch v99.14 + +Work sponsored by SGI + +- Ported devfs-patch-v99.13 to kernel 2.2.16 +=============================================================================== +Changes for patch v99.15 + +Work sponsored by SGI + +- Made drivers/block/genhd.c:disk_name() friendly to RAID 0.90 patches + Thanks to Arne Georg Gleditsch +=============================================================================== +Changes for patch v99.16 + +Work sponsored by SGI + +- Back-ported fixes for Unix98 pty handling from v161 and v162 + Thanks to Christian Czezatke (xS+S, Vienna ) + +- Back-ported removal of unnecessary call to + in + Thanks to Christian Czezatke (xS+S, Vienna ) +=============================================================================== +Changes for patch v99.17 + +Work sponsored by SGI + +- Register whole-disc entry even for invalid partition tables + +- Updated README from master HTML file + +- Fixed call to in drivers/sgi/char/shmiq.c:shmiq_init() + +- Changed interface to (same as 2.4.0-test2) +=============================================================================== +Changes for patch v99.18 + +Work sponsored by SGI + +- Ported devfs-patch-v99.17 to kernel 2.2.17-pre20 + +- Updated README from master HTML file +=============================================================================== +Changes for patch v99.19 + +- Ported devfs-patch-v99.18 to kernel 2.2.18 + +- Updated README from master HTML file +=============================================================================== +Changes for patch v99.20 + +- Ported devfs-patch-v99.19 to kernel 2.2.19 + +- Updated README from master HTML file +=============================================================================== +Changes for patch v99.21 + +- Ported devfs-patch-v99.20 to kernel 2.2.20 + +- Updated README from master HTML file + +- Added DEVFSD_NOTIFY_DELETE event + +- Removed unused DEVFS_FL_SHOW_UNREG flag + +- Send DEVFSD_NOTIFY_REGISTERED events in + +- Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from devfsd + or children + +- Switched to process group check for devfsd privileges + +- Fixed drivers/scsi/osst.c to account for change + +- Remove /dev/ide when IDE module unloaded + +- Made drivers/block/cpqarray.c devfs-friendly + +- Made drivers/block/cciss.c devfs-friendly diff -urN linux-2.2.20/Documentation/filesystems/devfs/README linux/Documentation/filesystems/devfs/README --- linux-2.2.20/Documentation/filesystems/devfs/README Wed Dec 31 17:00:00 1969 +++ linux/Documentation/filesystems/devfs/README Mon Dec 3 22:13:55 2001 @@ -0,0 +1,1796 @@ +Devfs (Device File System) FAQ + + +Linux Devfs (Device File System) FAQ +Richard Gooch +9-NOV-2001 + +----------------------------------------------------------------------------- + +NOTE: the master copy of this document is available online at: + +http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.html +and looks much better than the text version distributed with the +kernel sources. A mirror site is available at: + +http://www.ras.ucalgary.ca/~rgooch/linux/docs/devfs.html + +There is also an optional daemon that may be used with devfs. You can +find out more about it at: + +http://www.atnf.csiro.au/~rgooch/linux/ + +A mailing list is available which you may subscribe to. Send +email +to majordomo@oss.sgi.com with the following line in the +body of the message: +subscribe devfs +To unsubscribe, send the message body: +unsubscribe devfs +instead. The list is archived at + +http://oss.sgi.com/projects/devfs/archive/. + +----------------------------------------------------------------------------- + +Contents + + +What is it? + +Why do it? + +Who else does it? + +How it works + +Operational issues (essential reading) + +Instructions for the impatient +Permissions persistence accross reboots +Dealing with drivers without devfs support +All the way with Devfs +Other Issues +Kernel Naming Scheme +Devfsd Naming Scheme +Old Compatibility Names +SCSI Host Probing Issues + + + +Device drivers currently ported + +Allocation of Device Numbers + +Questions and Answers + +Making things work +Alternatives to devfs + + +Other resources + +Translations of this document + + +----------------------------------------------------------------------------- + + +What is it? + +Devfs is an alternative to "real" character and block special devices +on your root filesystem. Kernel device drivers can register devices by +name rather than major and minor numbers. These devices will appear in +devfs automatically, with whatever default ownership and +protection the driver specified. A daemon (devfsd) can be used to +override these defaults. Devfs has been in the kernel since 2.3.46. + +NOTE that devfs is entirely optional. If you prefer the old +disc-based device nodes, then simply leave CONFIG_DEVFS_FS=n (the +default). In this case, nothing will change. ALSO NOTE that if you do +enable devfs, the defaults are such that full compatibility is +maintained with the old devices names. + +There are two aspects to devfs: one is the underlying device +namespace, which is a namespace just like any mounted filesystem. The +other aspect is the filesystem code which provides a view of the +device namespace. The reason I make a distinction is because devfs +can be mounted many times, with each mount showing the same device +namespace. Changes made are global to all mounted devfs filesystems. +Also, because the devfs namespace exists without any devfs mounts, you +can easily mount the root filesystem by referring to an entry in the +devfs namespace. + + +The cost of devfs is a small increase in kernel code size and memory +usage. About 7 pages of code (some of that in __init sections) and 72 +bytes for each entry in the namespace. A modest system has only a +couple of hundred device entries, so this costs a few more +pages. Compare this with the suggestion to put /dev on a ramdisc. + +On a typical machine, the cost is under 0.2 percent. On a modest +system with 64 MBytes of RAM, the cost is under 0.1 percent. The +accusations of "bloatware" levelled at devfs are not justified. + +----------------------------------------------------------------------------- + + +Why do it? + +There are several problems that devfs addresses. Some of these +problems are more serious than others (depending on your point of +view), and some can be solved without devfs. However, the totality of +these problems really calls out for devfs. + +The choice is a patchwork of inefficient user space solutions, which +are complex and likely to be fragile, or to use a simple and efficient +devfs which is robust. + +There have been many counter-proposals to devfs, all seeking to +provide some of the benefits without actually implementing devfs. So +far there has been an absence of code and no proposed alternative has +been able to provide all the features that devfs does. Further, +alternative proposals require far more complexity in user-space (and +still deliver less functionality than devfs). Some people have the +mantra of reducing "kernel bloat", but don't consider the effects on +user-space. + +A good solution limits the total complexity of kernel-space and +user-space. + + +Major&minor allocation + +The existing scheme requires the allocation of major and minor device +numbers for each and every device. This means that a central +co-ordinating authority is required to issue these device numbers +(unless you're developing a "private" device driver), in order to +preserve uniqueness. Devfs shifts the burden to a namespace. This may +not seem like a huge benefit, but actually it is. Since driver authors +will naturally choose a device name which reflects the functionality +of the device, there is far less potential for namespace conflict. +Solving this requires a kernel change. + +/dev management + +Because you currently access devices through device nodes, these must +be created by the system administrator. For standard devices you can +usually find a MAKEDEV programme which creates all these (hundreds!) +of nodes. This means that changes in the kernel must be reflected by +changes in the MAKEDEV programme, or else the system administrator +creates device nodes by hand. + +The basic problem is that there are two separate databases of +major and minor numbers. One is in the kernel and one is in /dev (or +in a MAKEDEV programme, if you want to look at it that way). This is +duplication of information, which is not good practice. +Solving this requires a kernel change. + +/dev growth + +A typical /dev has over 1200 nodes! Most of these devices simply don't +exist because the hardware is not available. A huge /dev increases the +time to access devices (I'm just referring to the dentry lookup times +and the time taken to read inodes off disc: the next subsection shows +some more horrors). + +An example of how big /dev can grow is if we consider SCSI devices: + +host 6 bits (say up to 64 hosts on a really big machine) +channel 4 bits (say up to 16 SCSI buses per host) +id 4 bits +lun 3 bits +partition 6 bits +TOTAL 23 bits + + +This requires 8 Mega (1024*1024) inodes if we want to store all +possible device nodes. Even if we scrap everything but id,partition +and assume a single host adapter with a single SCSI bus and only one +logical unit per SCSI target (id), that's still 10 bits or 1024 +inodes. Each VFS inode takes around 256 bytes (kernel 2.1.78), so +that's 256 kBytes of inode storage on disc (assuming real inodes take +a similar amount of space as VFS inodes). This is actually not so bad, +because disc is cheap these days. Embedded systems would care about +256 kBytes of /dev inodes, but you could argue that embedded systems +would have hand-tuned /dev directories. I've had to do just that on my +embedded systems, but I would rather just leave it to devfs. + +Another issue is the time taken to lookup an inode when first +referenced. Not only does this take time in scanning through a list in +memory, but also the seek times to read the inodes off disc. +This could be solved in user-space using a clever programme which +scanned the kernel logs and deleted /dev entries which are not +available and created them when they were available. This programme +would need to be run every time a new module was loaded, which would +slow things down a lot. + +There is an existing programme called scsidev which will automatically +create device nodes for SCSI devices. It can do this by scanning files +in /proc/scsi. Unfortunately, to extend this idea to other device +nodes would require significant modifications to existing drivers (so +they too would provide information in /proc). This is a non-trivial +change (I should know: devfs has had to do something similar). Once +you go to this much effort, you may as well use devfs itself (which +also provides this information). Furthermore, such a system would +likely be implemented in an ad-hoc fashion, as different drivers will +provide their information in different ways. + +Devfs is much cleaner, because it (naturally) has a uniform mechanism +to provide this information: the device nodes themselves! + + +Node to driver file_operations translation + +There is an important difference between the way disc-based character +and block nodes and devfs entries make the connection between an entry +in /dev and the actual device driver. + +With the current 8 bit major and minor numbers the connection between +disc-based c&b nodes and per-major drivers is done through a +fixed-length table of 128 entries. The various filesystem types set +the inode operations for c&b nodes to {chr,blk}dev_inode_operations, +so when a device is opened a few quick levels of indirection bring us +to the driver file_operations. + +For miscellaneous character devices a second step is required: there +is a scan for the driver entry with the same minor number as the file +that was opened, and the appropriate minor open method is called. This +scanning is done *every time* you open a device node. Potentially, you +may be searching through dozens of misc. entries before you find your +open method. While not an enormous performance overhead, this does +seem pointless. + +Linux *must* move beyond the 8 bit major and minor barrier, +somehow. If we simply increase each to 16 bits, then the indexing +scheme used for major driver lookup becomes untenable, because the +major tables (one each for character and block devices) would need to +be 64 k entries long (512 kBytes on x86, 1 MByte for 64 bit +systems). So we would have to use a scheme like that used for +miscellaneous character devices, which means the search time goes up +linearly with the average number of major device drivers on your +system. Not all "devices" are hardware, some are higher-level drivers +like KGI, so you can get more "devices" without adding hardware +You can improve this by creating an ordered (balanced:-) +binary tree, in which case your search time becomes log(N). +Alternatively, you can use hashing to speed up the search. +But why do that search at all if you don't have to? Once again, it +seems pointless. + +Note that devfs doesn't use the major&minor system. For devfs +entries, the connection is done when you lookup the /dev entry. When +devfs_register() is called, an internal table is appended which has +the entry name and the file_operations. If the dentry cache doesn't +have the /dev entry already, this internal table is scanned to get the +file_operations, and an inode is created. If the dentry cache already +has the entry, there is *no lookup time* (other than the dentry scan +itself, but we can't avoid that anyway, and besides Linux dentries +cream other OS's which don't have them:-). Furthermore, the number of +node entries in a devfs is only the number of available device +entries, not the number of *conceivable* entries. Even if you remove +unnecessary entries in a disc-based /dev, the number of conceivable +entries remains the same: you just limit yourself in order to save +space. + +Devfs provides a fast connection between a VFS node and the device +driver, in a scalable way. + +/dev as a system administration tool + +Right now /dev contains a list of conceivable devices, most of which I +don't have. Devfs only shows those devices available on my +system. This means that listing /dev is a handy way of checking what +devices are available. + +Major&minor size + +Existing major and minor numbers are limited to 8 bits each. This is +now a limiting factor for some drivers, particularly the SCSI disc +driver, which consumes a single major number. Only 16 discs are +supported, and each disc may have only 15 partitions. Maybe this isn't +a problem for you, but some of us are building huge Linux systems with +disc arrays. With devfs an arbitrary pointer can be associated with +each device entry, which can be used to give an effective 32 bit +device identifier (i.e. that's like having a 32 bit minor +number). Since this is private to the kernel, there are no C library +compatibility issues which you would have with increasing major and +minor number sizes. See the section on "Allocation of Device Numbers" +for details on maintaining compatibility with userspace. + +Solving this requires a kernel change. + +Since writing this, the kernel has been modified so that the SCSI disc +driver has more major numbers allocated to it and now supports up to +128 discs. Since these major numbers are non-contiguous (a result of +unplanned expansion), the implementation is a little more cumbersome +than originally. + +Just like the changes to IPv4 to fix impending limitations in the +address space, people find ways around the limitations. In the long +run, however, solutions like IPv6 or devfs can't be put off forever. + +Read-only root filesystem + +Having your device nodes on the root filesystem means that you can't +operate properly with a read-only root filesystem. This is because you +want to change ownerships and protections of tty devices. Existing +practice prevents you using a CD-ROM as your root filesystem for a +*real* system. Sure, you can boot off a CD-ROM, but you can't change +tty ownerships, so it's only good for installing. + +Also, you can't use a shared NFS root filesystem for a cluster of +discless Linux machines (having tty ownerships changed on a common +/dev is not good). Nor can you embed your root filesystem in a +ROM-FS. + +You can get around this by creating a RAMDISC at boot time, making +an ext2 filesystem in it, mounting it somewhere and copying the +contents of /dev into it, then unmounting it and mounting it over +/dev. + +A devfs is a cleaner way of solving this. + +Non-Unix root filesystem + +Non-Unix filesystems (such as NTFS) can't be used for a root +filesystem because they variously don't support character and block +special files or symbolic links. You can't have a separate disc-based +or RAMDISC-based filesystem mounted on /dev because you need device +nodes before you can mount these. Devfs can be mounted without any +device nodes. Devlinks won't work because symlinks aren't supported. +An alternative solution is to use initrd to mount a RAMDISC initial +root filesystem (which is populated with a minimal set of device +nodes), and then construct a new /dev in another RAMDISC, and finally +switch to your non-Unix root filesystem. This requires clever boot +scripts and a fragile and conceptually complex boot procedure. + +Devfs solves this in a robust and conceptually simple way. + +PTY security + +Current pseudo-tty (pty) devices are owned by root and read-writable +by everyone. The user of a pty-pair cannot change +ownership/protections without being suid-root. + +This could be solved with a secure user-space daemon which runs as +root and does the actual creation of pty-pairs. Such a daemon would +require modification to *every* programme that wants to use this new +mechanism. It also slows down creation of pty-pairs. + +An alternative is to create a new open_pty() syscall which does much +the same thing as the user-space daemon. Once again, this requires +modifications to pty-handling programmes. + +The devfs solution allows a device driver to "tag" certain device +files so that when an unopened device is opened, the ownerships are +changed to the current euid and egid of the opening process, and the +protections are changed to the default registered by the driver. When +the device is closed ownership is set back to root and protections are +set back to read-write for everybody. No programme need be changed. +The devpts filesystem provides this auto-ownership feature for Unix98 +ptys. It doesn't support old-style pty devices, nor does it have all +the other features of devfs. + +Intelligent device management + +Devfs implements a simple yet powerful protocol for communication with +a device management daemon (devfsd) which runs in user space. It is +possible to send a message (either synchronously or asynchronously) to +devfsd on any event, such as registration/unregistration of device +entries, opening and closing devices, looking up inodes, scanning +directories and more. This has many possibilities. Some of these are +already implemented. See: + + +http://www.atnf.csiro.au/~rgooch/linux/ + +Device entry registration events can be used by devfsd to change +permissions of newly-created device nodes. This is one mechanism to +control device permissions. + +Device entry registration/unregistration events can be used to run +programmes or scripts. This can be used to provide automatic mounting +of filesystems when a new block device media is inserted into the +drive. + +Asynchronous device open and close events can be used to implement +clever permissions management. For example, the default permissions on +/dev/dsp do not allow everybody to read from the device. This is +sensible, as you don't want some remote user recording what you say at +your console. However, the console user is also prevented from +recording. This behaviour is not desirable. With asynchronous device +open and close events, you can have devfsd run a programme or script +when console devices are opened to change the ownerships for *other* +device nodes (such as /dev/dsp). On closure, you can run a different +script to restore permissions. An advantage of this scheme over +modifying the C library tty handling is that this works even if your +programme crashes (how many times have you seen the utmp database with +lingering entries for non-existent logins?). + +Synchronous device open events can be used to perform intelligent +device access protections. Before the device driver open() method is +called, the daemon must first validate the open attempt, by running an +external programme or script. This is far more flexible than access +control lists, as access can be determined on the basis of other +system conditions instead of just the UID and GID. + +Inode lookup events can be used to authenticate module autoload +requests. Instead of using kmod directly, the event is sent to +devfsd which can implement an arbitrary authentication before loading +the module itself. + +Inode lookup events can also be used to construct arbitrary +namespaces, without having to resort to populating devfs with symlinks +to devices that don't exist. + +Speculative Device Scanning + +Consider an application (like cdparanoia) that wants to find all +CD-ROM devices on the system (SCSI, IDE and other types), whether or +not their respective modules are loaded. The application must +speculatively open certain device nodes (such as /dev/sr0 for the SCSI +CD-ROMs) in order to make sure the module is loaded. This requires +that all Linux distributions follow the standard device naming scheme +(last time I looked RedHat did things differently). Devfs solves the +naming problem. + +The same application also wants to see which devices are actually +available on the system. With the existing system it needs to read the +/dev directory and speculatively open each /dev/sr* device to +determine if the device exists or not. With a large /dev this is an +inefficient operation, especially if there are many /dev/sr* nodes. A +solution like scsidev could reduce the number of /dev/sr* entries (but +of course that also requires all that inefficient directory scanning). + +With devfs, the application can open the /dev/sr directory +(which triggers the module autoloading if required), and proceed to +read /dev/sr. Since only the available devices will have +entries, there are no inefficencies in directory scanning or device +openings. + +----------------------------------------------------------------------------- + +Who else does it? + +FreeBSD has a devfs implementation. Solaris and AIX each have a +pseudo-devfs (something akin to scsidev but for all devices, with some +unspecified kernel support). BeOS, Plan9 and QNX also have it. SGI's +IRIX 6.4 and above also have a device filesystem. + +While we shouldn't just automatically do something because others do +it, we should not ignore the work of others either. FreeBSD has a lot +of competent people working on it, so their opinion should not be +blithely ignored. + +----------------------------------------------------------------------------- + + +How it works + +Registering device entries + +For every entry (device node) in a devfs-based /dev a driver must call +devfs_register(). This adds the name of the device entry, the +file_operations structure pointer and a few other things to an +internal table. Device entries may be added and removed at any +time. When a device entry is registered, it automagically appears in +any mounted devfs'. + +Inode lookup + +When a lookup operation on an entry is performed and if there is no +driver information for that entry devfs will attempt to call +devfsd. If still no driver information can be found then a negative +dentry is yielded and the next stage operation will be called by the +VFS (such as create() or mknod() inode methods). If driver information +can be found, an inode is created (if one does not exist already) and +all is well. + +Manually creating device nodes + +The mknod() method allows you to create an ordinary named pipe in the +devfs, or you can create a character or block special inode if one +does not already exist. You may wish to create a character or block +special inode so that you can set permissions and ownership. Later, if +a device driver registers an entry with the same name, the +permissions, ownership and times are retained. This is how you can set +the protections on a device even before the driver is loaded. Once you +create an inode it appears in the directory listing. + +Unregistering device entries + +A device driver calls devfs_unregister() to unregister an entry. + +Chroot() gaols + +2.2.x kernels + +The semantics of inode creation are different when devfs is mounted +with the "explicit" option. Now, when a device entry is registered, it +will not appear until you use mknod() to create the device. It doesn't +matter if you mknod() before or after the device is registered with +devfs_register(). The purpose of this behaviour is to support +chroot(2) gaols, where you want to mount a minimal devfs inside the +gaol. Only the devices you specifically want to be available (through +your mknod() setup) will be accessible. + +2.4.x kernels + +As of kernel 2.3.99, the VFS has had the ability to rebind parts of +the global filesystem namespace into another part of the namespace. +This now works even at the leaf-node level, which means that +individual files and device nodes may be bound into other parts of the +namespace. This is like making links, but better, because it works +across filesystems (unlike hard links) and works through chroot() +gaols (unlike symbolic links). + +Because of these improvements to the VFS, the multi-mount capability +in devfs is no longer needed. The administrator may create a minimal +device tree inside a chroot(2) gaol by using VFS bindings. As this +provides most of the features of the devfs multi-mount capability, I +removed the multi-mount support code (after issuing an RFC). This +yielded code size reductions and simplifications. + +If you want to construct a minimal chroot() gaol, the following +command should suffice: + +mount --bind /dev/null /gaol/dev/null + + +Repeat for other device nodes you want to expose. Simple! + +----------------------------------------------------------------------------- + + +Operational issues + + +Instructions for the impatient + +Nobody likes reading documentation. People just want to get in there +and play. So this section tells you quickly the steps you need to take +to run with devfs mounted over /dev. Skip these steps and you will end +up with a nearly unbootable system. Subsequent sections describe the +issues in more detail, and discuss non-essential configuration +options. + +Devfsd +OK, if you're reading this, I assume you want to play with +devfs. First you need to compile devfsd, the device management daemon, +available at +http://www.atnf.csiro.au/~rgooch/linux/. +Because the kernel has a naming scheme +which is quite different from the old naming scheme, you need to +install devfsd so that software and configuration files that use the +old naming scheme will not break. + +Compile and install devfsd. You will be provided with a default +configuration file /etc/devfsd.conf which will provide +compatibility symlinks for the old naming scheme. Don't change this +config file unless you know what you're doing. Even if you think you +do know what you're doing, don't change it until you've followed all +the steps below and booted a devfs-enabled system and verified that it +works. + +Now edit your main system boot script so that devfsd is started at the +very beginning (before any filesystem +checks). /etc/rc.d/rc.sysinit is often the main boot script +on systems with SysV-style boot scripts. On systems with BSD-style +boot scripts it is often /etc/rc. Also check +/sbin/rc. + +NOTE that the line you put into the boot +script should be exactly: + +/sbin/devfsd /dev + +DO NOT use some special daemon-launching +programme, otherwise the boot script may not wait for devfsd to finish +initialising. + +System Libraries +There may still be some problems because of broken software making +assumptions about device names. In particular, some software does not +handle devices which are symbolic links. If you are running a libc 5 +based system, install libc 5.4.44 (if you have libc 5.4.46, go back to +libc 5.4.44, which is actually correct). If you are running a glibc +based system, make sure you have glibc 2.1.3 or later. + +/etc/securetty +PAM (Pluggable Authentication Modules) is supposed to be a flexible +mechanism for providing better user authentication and access to +services. Unfortunately, it's also fragile, complex and undocumented +(check out RedHat 6.1, and probably other distributions as well). PAM +has problems with symbolic links. Append the following lines to your +/etc/securetty file: + +vc/1 +vc/2 +vc/3 +vc/4 +vc/5 +vc/6 +vc/7 +vc/8 + +This will not weaken security. If you have a version of util-linux +earlier than 2.10.h, please upgrade to 2.10.h or later. If you +absolutely cannot upgrade, then also append the following lines to +your /etc/securetty file: + +1 +2 +3 +4 +5 +6 +7 +8 + +This may potentially weaken security by allowing root logins over the +network (a password is still required, though). However, since there +are problems with dealing with symlinks, I'm suspicious of the level +of security offered in any case. + +XFree86 +While not essential, it's probably a good idea to upgrade to XFree86 +4.0, as patches went in to make it more devfs-friendly. If you don't, +you'll probably need to apply the following patch to +/etc/security/console.perms so that ordinary users can run +startx. Note that not all distributions have this file (e.g. Debian), +so if it's not present, don't worry about it. + +--- /etc/security/console.perms.orig Sat Apr 17 16:26:47 1999 ++++ /etc/security/console.perms Fri Feb 25 23:53:55 2000 +@@ -14,7 +14,7 @@ + # man 5 console.perms + + # file classes -- these are regular expressions +-=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9] ++=tty[0-9][0-9]* vc/[0-9][0-9]* :[0-9]\.[0-9] :[0-9] + + # device classes -- these are shell-style globs + =/dev/fd[0-1]* + +If the patch does not apply, then change the line: + +=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9] + +with: + +=tty[0-9][0-9]* vc/[0-9][0-9]* :[0-9]\.[0-9] :[0-9] + + +Disable devpts +I've had a report of devpts mounted on /dev/pts not working +correctly. Since devfs will also manage /dev/pts, there is no +need to mount devpts as well. You should either edit your +/etc/fstab so devpts is not mounted, or disable devpts from +your kernel configuration. + +Unsupported drivers +Not all drivers have devfs support. If you depend on one of these +drivers, you will need to create a script or tarfile that you can use +at boot time to create device nodes as appropriate. There is a +section which describes this. Another +section lists the drivers which have +devfs support. + +/dev/mouse + +Many disributions configure /dev/mouse to be the mouse device +for XFree86 and GPM. I actually think this is a bad idea, because it +adds another level of indirection. When looking at a config file, if +you see /dev/mouse you're left wondering which mouse +is being referred to. Hence I recommend putting the actual mouse +device (for example /dev/psaux) into your +/etc/X11/XF86Config file (and similarly for the GPM +configuration file). + +Alternatively, use the same technique used for unsupported drivers +described above. + +The Kernel +Finally, you need to make sure devfs is compiled into your kernel. Set +CONFIG_EXPERIMENTAL=y, CONFIG_DEVFS_FS=y and CONFIG_DEVFS_MOUNT=y by +using favourite configuration tool (i.e. make config or +make xconfig) and then make dep; make clean and then +recompile your kernel and modules. At boot, devfs will be mounted onto +/dev. + +If you encounter problems booting (for example if you forgot a +configuration step), you can pass devfs=nomount at the kernel +boot command line. This will prevent the kernel from mounting devfs at +boot time onto /dev. + +In general, a kernel built with CONFIG_DEVFS_FS=y but without mounting +devfs onto /dev is completely safe, and requires no +configuration changes. One exception to take note of is when +LABEL= directives are used in /etc/fstab. In this +case you will be unable to boot properly. This is because the +mount(8) programme uses /proc/partitions as part of +the volume label search process, and the device names it finds are not +available, because setting CONFIG_DEVFS_FS=y changes the names in +/proc/partitions, irrespective of whether devfs is mounted. + +Now you've finished all the steps required. You're now ready to boot +your shiny new kernel. Enjoy. + +Changing the configuration + +OK, you've now booted a devfs-enabled system, and everything works. +Now you may feel like changing the configuration (common targets are +/etc/fstab and /etc/devfsd.conf). Since you have a +system that works, if you make any changes and it doesn't work, you +now know that you only have to restore your configuration files to the +default and it will work again. + + +Permissions persistence across reboots + +If you don't use mknod(2) to create a device file, nor use chmod(2) or +chown(2) to change the ownerships/permissions, the inode ctime will +remain at 0 (the epoch, 12 am, 1-JAN-1970, GMT). Anything with a ctime +later than this has had it's ownership/permissions changed. Hence, a +simple script or programme may be used to tar up all changed inodes, +prior to shutdown. Although effective, many consider this approach a +kludge. + +A much better approach is to use devfsd to save and restore +permissions. It may be configured to record changes in permissions and +will save them in a database (in fact a directory tree), and restore +these upon boot. This is an efficient method and results in immediate +saving of current permissions (unlike the tar approach, which saves +permissions at some unspecified future time). + +The default configuration file supplied with devfsd has config entries +which you may uncomment to enable persistence management. + +If you decide to use the tar approach anyway, be aware that tar will +first unlink(2) an inode before creating a new device node. The +unlink(2) has the effect of breaking the connection between a devfs +entry and the device driver. If you use the "devfs=only" boot option, +you lose access to the device driver, requiring you to reload the +module. I consider this a bug in tar (there is no real need to +unlink(2) the inode first). + +Alternatively, you can use devfsd to provide more sophisticated +management of device permissions. You can use devfsd to store +permissions for whole groups of devices with a single configuration +entry, rather than the conventional single entry per device entry. + +Permissions database stored in mounted-over /dev + +If you wish to save and restore your device permissions into the +disc-based /dev while still mounting devfs onto /dev +you may do so. This requires a 2.4.x kernel (in fact, 2.3.99 or +later), which has the VFS binding facility. You need to do the +following to set this up: + + + +make sure the kernel does not mount devfs at boot time + + +create the /dev-state directory + + +add the following lines near the very beginning of your boot +scripts: + +mount --bind /dev /dev-state +mount -t devfs none /dev +devfsd /dev + + + + +add the following lines to your /etc/devfsd.conf file: + +REGISTER ^pt[sy]/.* IGNORE +CHANGE ^pt[sy]/.* IGNORE +REGISTER .* COPY /dev-state/$devname $devpath +CHANGE .* COPY $devpath /dev-state/$devname +CREATE .* COPY $devpath /dev-state/$devname + + + +reboot. + + + + +Permissions database stored in normal directory + +If you are using an older kernel which doesn't support VFS binding, +then you won't be able to have the permissions database in a +mounted-over /dev. However, you can still use a regular +directory to store the database. The sample /etc/devfsd.conf +file above may still be used. You will need to create the +/dev-state directory prior to installing devfsd. If you have +old permissions in /dev, then just copy (or move) the device +nodes over to the new directory. + +Which method is better? + +The best method is to have the permissions database stored in the +mounted-over /dev. This is because you will not need to copy +device nodes over to /dev-state, and because it allows you to +switch between devfs and non-devfs kernels, without requiring you to +copy permissions between /dev-state (for devfs) and +/dev (for non-devfs). + + +Dealing with drivers without devfs support + +Currently, not all device drivers in the kernel have been modified to +use devfs. Device drivers which do not yet have devfs support will not +automagically appear in devfs. The simplest way to create device nodes +for these drivers is to unpack a tarfile containing the required +device nodes. You can do this in your boot scripts. All your drivers +will now work as before. + +Hopefully for most people devfs will have enough support so that they +can mount devfs directly over /dev without loosing most functionality +(i.e. loosing access to various devices). As of 22-JAN-1998 (devfs +patch version 10) I am now running this way. All the devices I have +are available in devfs, so I don't lose anything. + +WARNING: if your configuration requires the old-style device names +(i.e. /dev/hda1 or /dev/sda1), you must install devfsd and configure +it to maintain compatibility entries. It is almost certain that you +will require this. Note that the kernel creates a compatibility entry +for the root device, so you don't need initrd. + +Note that you no longer need to mount devpts if you use Unix98 PTYs, +as devfs can manage /dev/pts itself. This saves you some RAM, as you +don't need to compile and install devpts. Note that some versions of +glibc have a bug with Unix98 pty handling on devfs systems. Contact +the glibc maintainers for a fix. Glibc 2.1.3 has the fix. + +Note also that apart from editing /etc/fstab, other things will need +to be changed if you *don't* install devfsd. Some software (like the X +server) hard-wire device names in their source. It really is much +easier to install devfsd so that compatibility entries are created. +You can then slowly migrate your system to using the new device names +(for example, by starting with /etc/fstab), and then limiting the +compatibility entries that devfsd creates. + +MAKE SURE YOU INSTALL DEVFSD BEFORE YOU BOOT A DEVFS-ENABLED KERNEL! + +Now that devfs has gone into the 2.3.46 kernel, I'm getting a lot of +reports back. Many of these are because people are trying to run +without devfsd, and hence some things break. Please just run devfsd if +things break. I want to concentrate on real bugs rather than +misconfiguration problems at the moment. If people are willing to fix +bugs/false assumptions in other code (i.e. glibc, X server) and submit +that to the respective maintainers, that would be great. + + +All the way with Devfs + +The devfs kernel patch creates a rationalised device tree. As stated +above, if you want to keep using the old /dev naming scheme, +you just need to configure devfsd appopriately (see the man +page). People who prefer the old names can ignore this section. For +those of us who like the rationalised names and an uncluttered +/dev, read on. + +If you don't run devfsd, or don't enable compatibility entry +management, then you will have to configure your system to use the new +names. For example, you will then need to edit your +/etc/fstab to use the new disc naming scheme. If you want to +be able to boot non-devfs kernels, you will need compatibility +symlinks in the underlying disc-based /dev pointing back to +the old-style names for when you boot a kernel without devfs. + +You can selectively decide which devices you want compatibility +entries for. For example, you may only want compatibility entries for +BSD pseudo-terminal devices (otherwise you'll have to patch you C +library or use Unix98 ptys instead). It's just a matter of putting in +the correct regular expression into /dev/devfsd.conf. + +There are other choices of naming schemes that you may prefer. For +example, I don't use the kernel-supplied +names, because they are too verbose. A common misconception is +that the kernel-supplied names are meant to be used directly in +configuration files. This is not the case. They are designed to +reflect the layout of the devices attached and to provide easy +classification. + +If you like the kernel-supplied names, that's fine. If you don't then +you should be using devfsd to construct a namespace more to your +liking. Devfsd has built-in code to construct a +namespace that is both logical and easy to +manage. In essence, it creates a convenient abbreviation of the +kernel-supplied namespace. + +You are of course free to build your own namespace. Devfsd has all the +infrastructure required to make this easy for you. All you need do is +write a script. You can even write some C code and devfsd can load the +shared object as a callable extension. + + +Other Issues + +The init programme +Another thing to take note of is whether your init programme +creates a Unix socket /dev/telinit. Some versions of init +create /dev/telinit so that the telinit programme can +communicate with the init process. If you have such a system you need +to make sure that devfs is mounted over /dev *before* init +starts. In other words, you can't leave the mounting of devfs to +/etc/rc, since this is executed after init. Other +versions of init require a named pipe /dev/initctl +which must exist *before* init starts. Once again, you need to +mount devfs and then create the named pipe *before* init +starts. + +The default behaviour now is not to mount devfs onto /dev at +boot time for 2.3.x and later kernels. You can correct this with the +"devfs=mount" boot option. This solves any problems with init, +and also prevents the dreaded: + +Cannot open initial console + +message. For 2.2.x kernels where you need to apply the devfs patch, +the default is to mount. + +If you have automatic mounting of devfs onto /dev then you +may need to create /dev/initctl in your boot scripts. The +following lines should suffice: + +mknod /dev/initctl p +kill -SIGUSR1 1 # tell init that /dev/initctl now exists + +Alternatively, if you don't want the kernel to mount devfs onto +/dev then you could use the following procedure is a +guideline for how to get around /dev/initctl problems: + +# cd /sbin +# mv init init.real +# cat > init +#! /bin/sh +mount -n -t devfs none /dev +mknod /dev/initctl p +exec /sbin/init.real $* +[control-D] +# chmod a+x init + +Note that newer versions of init create /dev/initctl +automatically, so you don't have to worry about this. + +Module autoloading +You will need to configure devfsd to enable module +autoloading. The following lines should be placed in your +/etc/devfsd.conf file: + +LOOKUP .* MODLOAD + + +As of devfsd-v1.3.10, a generic /etc/modules.devfs +configuration file is installed, which is used by the MODLOAD +action. This should be sufficient for most configurations. If you +require further configuration, edit your /etc/modules.conf +file. The way module autoloading work with devfs is: + + +a process attempts to lookup a device node (e.g. /dev/fred) + + +if that device node does not exist, the full pathname is passed to +devfsd as a string + + +devfsd will pass the string to the modprobe programme (provided the +configuration line shown above is present), and specifies that +/etc/modules.devfs is the configuration file + + +/etc/modules.devfs includes /etc/modules.conf to +access local configurations + +modprobe will search it's configuration files, looking for an alias +that translates the pathname into a module name + + +the translated pathname is then used to load the module. + + +If you wanted a lookup of /dev/fred to load the +mymod module, you would require the following configuration +line in /etc/modules.conf: + +alias /dev/fred mymod + +The /etc/modules.devfs configuration file provides many such +aliases for standard device names. If you look closely at this file, +you will note that some modules require multiple alias configuration +lines. This is required to support module autoloading for old and new +device names. + +Mounting root off a devfs device +If you wish to mount root off a devfs device when you pass the +"devfs=only" boot option, then you need to pass in the +"root=" option to the kernel when booting. If you use +LILO, then you must have this in lilo.conf: + +append = "root=" + +Surprised? Yep, so was I. It turns out if you have (as most people +do): + +root = + + +then LILO will determine the device number of and will +write that device number into a special place in the kernel image +before starting the kernel, and the kernel will use that device number +to mount the root filesystem. So, using the "append" variety ensures +that LILO passes the root filesystem device as a string, which devfs +can then use. + +Note that this isn't an issue if you don't pass "devfs=only". + +TTY issues +The ttyname(3) function in some versions of the C library makes +false assumptions about device entries which are symbolic links. The +tty(1) programme is one that depends on this function. I've +written a patch to libc 5.4.43 which fixes this. This has been +included in libc 5.4.44 and a similar fix is in glibc 2.1.3. + + +Kernel Naming Scheme + +The kernel provides a default naming scheme. This scheme is designed +to make it easy to search for specific devices or device types, and to +view the available devices. Some device types (such as hard discs), +have a directory of entries, making it easy to see what devices of +that class are available. Often, the entries are symbolic links into a +directory tree that reflects the topology of available devices. The +topological tree is useful for finding how your devices are arranged. + +Below is a list of the naming schemes for the most common drivers. A +list of reserved device names is +available for reference. Please send email to +rgooch@atnf.csiro.au to obtain an allocation. Please be +patient (the maintainer is busy). An alternative name may be allocated +instead of the requested name, at the discretion of the maintainer. + +Disc Devices + +All discs, whether SCSI, IDE or whatever, are placed under the +/dev/discs hierarchy: + + /dev/discs/disc0 first disc + /dev/discs/disc1 second disc + + +Each of these entries is a symbolic link to the directory for that +device. The device directory contains: + + disc for the whole disc + part* for individual partitions + + +CD-ROM Devices + +All CD-ROMs, whether SCSI, IDE or whatever, are placed under the +/dev/cdroms hierarchy: + + /dev/cdroms/cdrom0 first CD-ROM + /dev/cdroms/cdrom1 second CD-ROM + + +Each of these entries is a symbolic link to the real device entry for +that device. + +Tape Devices + +All tapes, whether SCSI, IDE or whatever, are placed under the +/dev/tapes hierarchy: + + /dev/tapes/tape0 first tape + /dev/tapes/tape1 second tape + + +Each of these entries is a symbolic link to the directory for that +device. The device directory contains: + + mt for mode 0 + mtl for mode 1 + mtm for mode 2 + mta for mode 3 + mtn for mode 0, no rewind + mtln for mode 1, no rewind + mtmn for mode 2, no rewind + mtan for mode 3, no rewind + + +SCSI Devices + +To uniquely identify any SCSI device requires the following +information: + + controller (host adapter) + bus (SCSI channel) + target (SCSI ID) + unit (Logical Unit Number) + + +All SCSI devices are placed under /dev/scsi (assuming devfs +is mounted on /dev). Hence, a SCSI device with the following +parameters: c=1,b=2,t=3,u=4 would appear as: + + /dev/scsi/host1/bus2/target3/lun4 device directory + + +Inside this directory, a number of device entries may be created, +depending on which SCSI device-type drivers were installed. + +See the section on the disc naming scheme to see what entries the SCSI +disc driver creates. + +See the section on the tape naming scheme to see what entries the SCSI +tape driver creates. + +The SCSI CD-ROM driver creates: + + cd + + +The SCSI generic driver creates: + + generic + + +IDE Devices + +To uniquely identify any IDE device requires the following +information: + + controller + bus (aka. primary/secondary) + target (aka. master/slave) + unit + + +All IDE devices are placed under /dev/ide, and uses a similar +naming scheme to the SCSI subsystem. + +XT Hard Discs + +All XT discs are placed under /dev/xd. The first XT disc has +the directory /dev/xd/disc0. + +TTY devices + +The tty devices now appear as: + + New name Old-name Device Type + -------- -------- ----------- + /dev/tts/{0,1,...} /dev/ttyS{0,1,...} Serial ports + /dev/cua/{0,1,...} /dev/cua{0,1,...} Call out devices + /dev/vc/0 /dev/tty Current virtual console + /dev/vc/{1,2,...} /dev/tty{1...63} Virtual consoles + /dev/vcc/{0,1,...} /dev/vcs{1...63} Virtual consoles + /dev/pty/m{0,1,...} /dev/ptyp?? PTY masters + /dev/pty/s{0,1,...} /dev/ttyp?? PTY slaves + + +RAMDISCS + +The RAMDISCS are placed in their own directory, and are named thus: + + /dev/rd/{0,1,2,...} + + +Meta Devices + +The meta devices are placed in their own directory, and are named +thus: + + /dev/md/{0,1,2,...} + + +Floppy discs + +Floppy discs are placed in the /dev/floppy directory. + +Loop devices + +Loop devices are placed in the /dev/loop directory. + +Sound devices + +Sound devices are placed in the /dev/sound directory +(audio, sequencer, ...). + + +Devfsd Naming Scheme + +Devfsd provides a naming scheme which is a convenient abbreviation of +the kernel-supplied namespace. In some +cases, the kernel-supplied naming scheme is quite convenient, so +devfsd does not provide another naming scheme. The convenience names +that devfsd creates are in fact the same names as the original devfs +kernel patch created (before Linus mandated the Big Name +Change). These are referred to as "new compatibility entries". + +In order to configure devfsd to create these convenience names, the +following lines should be placed in your /etc/devfsd.conf: + +REGISTER .* MKNEWCOMPAT +UNREGISTER .* RMNEWCOMPAT + +This will cause devfsd to create (and destroy) symbolic links which +point to the kernel-supplied names. + +SCSI Hard Discs + +All SCSI discs are placed under /dev/sd (assuming devfs is +mounted on /dev). Hence, a SCSI disc with the following +parameters: c=1,b=2,t=3,u=4 would appear as: + + /dev/sd/c1b2t3u4 for the whole disc + /dev/sd/c1b2t3u4p5 for the 5th partition + /dev/sd/c1b2t3u4p5s6 for the 6th slice in the 5th partition + + +SCSI Tapes + +All SCSI tapes are placed under /dev/st. A similar naming +scheme is used as for SCSI discs. A SCSI tape with the +parameters:c=1,b=2,t=3,u=4 would appear as: + + /dev/st/c1b2t3u4m0 for mode 0 + /dev/st/c1b2t3u4m1 for mode 1 + /dev/st/c1b2t3u4m2 for mode 2 + /dev/st/c1b2t3u4m3 for mode 3 + /dev/st/c1b2t3u4m0n for mode 0, no rewind + /dev/st/c1b2t3u4m1n for mode 1, no rewind + /dev/st/c1b2t3u4m2n for mode 2, no rewind + /dev/st/c1b2t3u4m3n for mode 3, no rewind + + +SCSI CD-ROMs + +All SCSI CD-ROMs are placed under /dev/sr. A similar naming +scheme is used as for SCSI discs. A SCSI CD-ROM with the +parameters:c=1,b=2,t=3,u=4 would appear as: + + /dev/sr/c1b2t3u4 + + +SCSI Generic Devices + +All SCSI CD-ROMs are placed under /dev/sg. A similar naming +scheme is used as for SCSI discs. A SCSI generic device with the +parameters:c=1,b=2,t=3,u=4 would appear as: + + /dev/sg/c1b2t3u4 + + +IDE Hard Discs + +All IDE discs are placed under /dev/ide/hd, using a similar +convention to SCSI discs. The following mappings exist between the new +and the old names: + + /dev/hda /dev/ide/hd/c0b0t0u0 + /dev/hdb /dev/ide/hd/c0b0t1u0 + /dev/hdc /dev/ide/hd/c0b1t0u0 + /dev/hdd /dev/ide/hd/c0b1t1u0 + + +IDE Tapes + +A similar naming scheme is used as for IDE discs. The entries will +appear in the /dev/ide/mt directory. + +IDE CD-ROM + +A similar naming scheme is used as for IDE discs. The entries will +appear in the /dev/ide/cd directory. + +IDE Floppies + +A similar naming scheme is used as for IDE discs. The entries will +appear in the /dev/ide/fd directory. + +XT Hard Discs + +All XT discs are placed under /dev/xd. The first XT disc +would appear as /dev/xd/c0t0. + + +Old Compatibility Names + +The old compatibility names are the legacy device names, such as +/dev/hda, /dev/sda, /dev/rtc and so on. +Devfsd can be configured to create compatibility symlinks so that you +may continue to use the old names in your configuration files and so +that old applications will continue to function correctly. + +In order to configure devfsd to create these legacy names, the +following lines should be placed in your /etc/devfsd.conf: + +REGISTER .* MKOLDCOMPAT +UNREGISTER .* RMOLDCOMPAT + +This will cause devfsd to create (and destroy) symbolic links which +point to the kernel-supplied names. + + +SCSI Host Probing Issues + +Devfs allows you to identify SCSI discs based in part on SCSI host +numbers. If you have only one SCSI host (card) in your computer, then +clearly it will be given host number 0. Life is not always that easy +is you have multiple SCSI hosts. Unfortunately, it can sometimes be +difficult to guess what the probing order of SCSI hosts is. You need +to know the probe order before you can use device names. To make this +easy, there is a kernel boot parameter called "scsihosts". This allows +you to specify the probe order for different types of SCSI hosts. The +syntax of this parameter is: + +scsihosts=:::...: + +where ,,..., are the names +of drivers used in the /proc filesystem. For example: + + scsihosts=aha1542:ppa:aha1542::ncr53c7xx + + +means that devices connected to + +- first aha1542 controller - will be /dev/scsi/host0/bus#/target#/lun# +- first parallel port ZIP - will be /dev/scsi/host1/bus#/target#/lun# +- second aha1542 controller - will be /dev/scsi/host2/bus#/target#/lun# +- first NCR53C7xx controller - will be /dev/scsi/host4/bus#/target#/lun# +- any extra controller - will be /dev/scsi/host5/bus#/target#/lun#, + /dev/scsi/host6/bus#/target#/lun#, etc +- if any of above controllers will not be found - the reserved names will + not be used by any other device. +- /dev/scsi/host3/bus#/target#/lun# names will never be used + + +You can use ',' instead of ':' as the separator character if you +wish. I have used the devfsd naming scheme +here. + +Note that this scheme does not address the SCSI host order if you have +multiple cards of the same type (such as NCR53c8xx). In this case you +need to use the driver-specific boot parameters to control this. + +----------------------------------------------------------------------------- + + +Device drivers currently ported + +- All miscellaneous character devices support devfs (this is done + transparently through misc_register()) + +- SCSI discs and generic hard discs + +- Character memory devices (null, zero, full and so on) + Thanks to C. Scott Ananian + +- Loop devices (/dev/loop?) + +- TTY devices (console, serial ports, terminals and pseudo-terminals) + Thanks to C. Scott Ananian + +- SCSI tapes (/dev/scsi and /dev/tapes) + +- SCSI CD-ROMs (/dev/scsi and /dev/cdroms) + +- SCSI generic devices (/dev/scsi) + +- RAMDISCS (/dev/ram?) + +- Meta Devices (/dev/md*) + +- Floppy discs (/dev/floppy) + +- Parallel port printers (/dev/printers) + +- Sound devices (/dev/sound) + Thanks to Eric Dumas and + C. Scott Ananian + +- Joysticks (/dev/joysticks) + +- Sparc keyboard (/dev/kbd) + +- DSP56001 digital signal processor (/dev/dsp56k) + +- Apple Desktop Bus (/dev/adb) + +- Coda network file system (/dev/cfs*) + +- Virtual console capture devices (/dev/vcc) + Thanks to Dennis Hou + +- Frame buffer devices (/dev/fb) + +- Video capture devices (/dev/v4l) + + +----------------------------------------------------------------------------- + + +Allocation of Device Numbers + +Devfs allows you to write a driver which doesn't need to allocate a +device number (major&minor numbers) for the internal operation of the +kernel. However, there are a number of userspace programmes that use +the device number as a unique handle for a device. An example is the +find programme, which uses device numbers to determine whether +an inode is on a different filesystem than another inode. The device +number used is the one for the block device which a filesystem is +using. To preserve compatibility with userspace programmes, block +devices using devfs need to have unique device numbers allocated to +them. Furthermore, POSIX specifies device numbers, so some kind of +device number needs to be presented to userspace. + +The simplest option (especially when porting drivers to devfs) is to +keep using the old major and minor numbers. Devfs will take whatever +values are given for major&minor and pass them onto userspace. + +Alternatively, you can have devfs choose unique device numbers for +you. When you register a character or block device using +devfs_register you can provide the optional +DEVFS_FL_AUTO_DEVNUM flag, which will then automatically allocate a +unique device number (the allocation is separated for the character +and block devices). + +This device number is a 16 bit number, so this leaves plenty of space +for large numbers of discs and partitions. This scheme can also be +used for character devices, in particular the tty devices, which are +currently limited to 256 pseudo-ttys (this limits the total number of +simultaneous xterms and remote logins). Note that the device number +is limited to the range 36864-61439 (majors 144-239), in order to +avoid any possible conflicts with existing official allocations. + +Please note that using dynamically allocated block device numbers may +break the NFS daemons (both user and kernel mode), which expect dev_t +for a given device to be constant over the lifetime of remote mounts. + +A final note on this scheme: since it doesn't increase the size of +device numbers, there are no compatibility issues with userspace. + +----------------------------------------------------------------------------- + + +Questions and Answers + + +Making things work +Alternatives to devfs +What I don't like about devfs + + + +Making things work + +Here are some common questions and answers. + + + +Devfsd is not managing all my permissions + +Make sure you are capturing the appropriate events. For example, +device entries created by the kernel generate REGISTER events, +but those created by devfsd generate CREATE events. + + +Devfsd is not capturing all REGISTER events + +See the previous entry: you may need to capture CREATE events. + + +X will not start + +Make sure you followed the steps +outlined above. + + +Why don't my network devices appear in devfs? + +This is not a bug. Network devices have their own, completely separate +namespace. They are accessed via socket(2) and +setsockopt(2) calls, and thus require no device nodes. I have +raised the possibilty of moving network devices into the device +namespace, but have had no response. + + +How can I test if I have devfs compiled into my kernel? + +All filesystems built-in or currently loaded are listed in +/proc/filesystems. If you see a devfs entry, then +you know that devfs was compiled into your kernel. If you have +correctly configured and rebuilt your kernel, then devfs will be +built-in. If you think you've configured it in, but +/proc/filesystems doesn't show it, you've made a mistake. +Common mistakes include: + +Using a 2.2.x kernel without applying the devfs patch (if you +don't know how to patch your kernel, use 2.4.x instead, don't bother +asking me how to patch) +Forgetting to set CONFIG_EXPERIMENTAL=y +Forgetting to set CONFIG_DEVFS_FS=y +Forgetting to set CONFIG_DEVFS_MOUNT=y (if you want devfs +to be automatically mounted at boot) +Editing your .config manually, instead of using make +config or make xconfig +Forgetting to run make dep; make clean after changing the +configuration and before compiling +Forgetting to compile your kernel and modules +Forgetting to install your kernel +Forgetting to install your modules + +Please check twice that you've done all these steps before sending in +a bug report. + + + +How can I test if devfs is mounted on /dev? + +The device filesystem will always create an entry called +".devfsd", which is used to communicate with the daemon. Even +if the daemon is not running, this entry will exist. Testing for the +existence of this entry is the approved method of determining if devfs +is mounted or not. Note that the type of entry (i.e. regular file, +character device, named pipe, etc.) may change without notice. Only +the existence of the entry should be relied upon. + + + + + +Alternatives to devfs + +I've attempted to collate all the anti-devfs proposals and explain +their limitations. Under construction. + + +Why not just pass device create/remove events to a daemon? + +Here the suggestion is to develop an API in the kernel so that devices +can register create and remove events, and a daemon listens for those +events. The daemon would then populate/depopulate /dev (which +resides on disc). + +This has several limitations: + + +it only works for modules loaded and unloaded (or devices inserted +and removed) after the kernel has finished booting. Without a database +of events, there is no way the daemon could fully populate +/dev + + +if you add a database to this scheme, the question is then how to +present that database to user-space. If you make it a list of strings +with embedded event codes which are passed through a pipe to the +daemon, then this is only of use to the daemon. I would argue that the +natural way to present this data is via a filesystem (since many of +the events will be of a hierarchical nature), such as devfs. +Presenting the data as a filesystem makes it easy for the user to see +what is available and also makes it easy to write scripts to scan the +"database" + + +the tight binding between device nodes and drivers is no longer +possible (requiring the otherwise perfectly avoidable +table lookups) + + +you cannot catch inode lookup events on /dev which means +that module autoloading requires device nodes to be created. This is a +problem, particularly for drivers where only a few inodes are created +from a potentially large set + + +this technique can't be used when the root FS is mounted +read-only + + + + +Just implement a better scsidev + +This suggestion involves taking the scsidev programme and +extending it to scan for all devices, not just SCSI devices. The +scsidev programme works by scanning /proc/scsi + +Problems: + + +the kernel does not currently provide a list of all devices +available. Not all drivers register entries in /proc or +generate kernel messages + + +there is no uniform mechanism to register devices other than the +devfs API + + +implementing such an API is then the same as the +proposal above + + + + +Put /dev on a ramdisc + +This suggestion involves creating a ramdisc and populating it with +device nodes and then mounting it over /dev. + +Problems: + + + +this doesn't help when mounting the root filesystem, since you +still need a device node to do that + + +if you want to use this technique for the root device node as +well, you need to use initrd. This complicates the booting sequence +and makes it significantly harder to administer and configure. The +initrd is essentially opaque, robbing the system administrator of easy +configuration + + +insufficient information is available to correctly populate the +ramdisc. So we come back to the +proposal above to "solve" this + + +a ramdisc-based solution would take more kernel memory, since the +backing store would be (at best) normal VFS inodes and dentries, which +take 284 bytes and 112 bytes, respectively, for each entry. Compare +that to 72 bytes for devfs + + + + +Do nothing: there's no problem + +Sometimes people can be heard to claim that the existing scheme is +fine. This is what they're ignoring: + + +device number size (8 bits each for major and minor) is a real +limitation, and must be fixed somehow. Systems with large numbers of +SCSI devices, for example, will continue to consume the remaining +unallocated major numbers. USB will also need to push beyond the 8 bit +minor limitation + + +simplying increasing the device number size is insufficient. Apart +from causing a lot of pain, it doesn't solve the management issues +of a /dev with thousands or more device nodes + + +ignoring the problem of a huge /dev will not make it go +away, and dismisses the legitimacy of a large number of people who +want a dynamic /dev + + +the standard response then becomes: "write a device management +daemon", which brings us back to the +proposal above + + + + +What I don't like about devfs + +Here are some common complaints about devfs, and some suggestions and +solutions that may make it more palatable for you. I can't please +everybody, but I do try :-) + +I hate the naming scheme + +First, remember that no naming scheme will please everybody. You hate +the scheme, others love it. Who's to say who's right and who's wrong? +Ultimately, the person who writes the code gets to choose, and what +exists now is a combination of the the choices made by the +devfs author and the +kernel maintainer (Linus). + +However, not all is lost. If you want to create your own naming +scheme, it is a simple matter to write a standalone script, hack +devfsd, or write a script called by devfsd. You can create whatever +naming scheme you like. + +Further, if you want to remove all traces of the devfs naming scheme +from /dev, you can mount devfs elsewhere (say +/devfs) and populate /dev with links into +/devfs. This population can be automated using devfsd if you +wish. + +You can even use the VFS binding facility to make the links, rather +than using symbolic links. This way, you don't even have to see the +"destination" of these symbolic links. + +Devfs puts policy into the kernel + +There's already policy in the kernel. Device numbers are in fact +policy (why should the kernel dictate what device numbers I use?). +Face it, some policy has to be in the kernel. The real difference +between device names as policy and device numbers as policy is that +no one will use device numbers directly, because device +numbers are devoid of meaning to humans and are ugly. At least with +the devfs device names, (even though you can add your own naming +scheme) some people will use the devfs-supplied names directly. This +offends some people :-) + +Devfs is bloatware + +This is not even remotely true. As shown above, +both code and data size are quite modest. + +----------------------------------------------------------------------------- + + +Other resources + + + +Douglas Gilbert has written a useful document at + +http://www.torque.net/sg/devfs_scsi.html which +explores the SCSI subsystem and how it interacts with devfs + + +Douglas Gilbert has written another useful document at + +http://www.torque.net/scsi/scsihosts.html which +discusses the scsihosts= boot option + + +Douglas Gilbert has written yet another useful document at + +http://www.torque.net/scsi/SCSI-2.4-HOWTO/ which +discusses the Linux SCSI subsystem in 2.4. + + +Johannes Erdfelt has started a discussion paper on Linux and +hot-swap devices, describing what the requirements are for a scalable +solution and how and why he's used devfs+devfsd. Note that this is an +early draft only, available in plain text form at: + +http://johannes.erdfelt.com/hotswap.txt. +Johannes has promised a HTML version will follow. + + +I presented an invited +paper +at the + +2nd Annual Storage Management Workshop held in Miamia, Florida, +U.S.A. in October 2000. + + + + +----------------------------------------------------------------------------- + + +Translations of this document + +This document has been translated into other languages. + + + + +A Korean translation by viatoris@nownuri.net is available at + +http://home.nownuri.net/~viatoris/devfs/devfs.html + + + diff -urN linux-2.2.20/Documentation/filesystems/devfs/ToDo linux/Documentation/filesystems/devfs/ToDo --- linux-2.2.20/Documentation/filesystems/devfs/ToDo Wed Dec 31 17:00:00 1969 +++ linux/Documentation/filesystems/devfs/ToDo Mon Dec 3 22:13:01 2001 @@ -0,0 +1,48 @@ + Device File System (devfs) ToDo List + + Richard Gooch + + 17-JUL-1999 + +This is a list of things to be done for better devfs support in the +Linux kernel. If you'd like to contribute to the devfs, please have a +look at this list for anything that is unallocated. Also, if there are +items missing (surely), please contact me so I can add them to the +list (preferably with your name attached to them:-). + + +- create /proc/devlist (maybe), with a format like: + name type perm user group class + (class should be "disk", "audio" and so on) + Me + +- >256 ptys + Thanks to C. Scott Ananian + +- Uniform CD-ROM support + Thanks to Erik Andersen + +- Amiga floppy driver (drivers/block/amiflop.c) + +- Atari floppy driver (drivers/block/ataflop.c) + +- Network block device (drivers/block/nbd.c) + +- SWIM3 (Super Woz Integrated Machine 3) floppy driver (drivers/block/swim3.c) + +- Amiga ZorroII ramdisc driver (drivers/block/z2ram.c) + +- Parallel port ATAPI CD-ROM (drivers/block/paride/pcd.c) + +- Parallel port ATAPI floppy (drivers/block/paride/pf.c) + +- AP1000 block driver (drivers/ap1000/ap.c, drivers/ap1000/ddv.c) + +- Archimedes floppy (drivers/acorn/block/fd1772.c) + +- MFM hard drive (drivers/acorn/block/mfmhd.c) + +- I2O block device (drivers/i2o/i2o_block.c) + +- ST-RAM device (arch/m68k/atari/stram.c) + diff -urN linux-2.2.20/Documentation/filesystems/devfs/boot-options linux/Documentation/filesystems/devfs/boot-options --- linux-2.2.20/Documentation/filesystems/devfs/boot-options Wed Dec 31 17:00:00 1969 +++ linux/Documentation/filesystems/devfs/boot-options Mon Dec 3 22:43:39 2001 @@ -0,0 +1,64 @@ +/* -*- auto-fill -*- */ + + Device File System (devfs) Boot Options + + Richard Gooch + + 3-DEC-2001 + + +When either CONFIG_DEVFS_DEBUG or CONFIG_DEVFS_BOOT_OPTIONS are +enabled, you can pass several boot options to the kernel to control +devfs behaviour. The boot options are prefixed by "devfs=", and are +separated by commas. Spaces are not allowed. The syntax looks like +this: + +devfs=,, + +and so on. For example, if you wanted to turn on debugging for module +load requests and device registration, you would do: + +devfs=dmod,dreg + + +Debugging Options +================= + +These requires CONFIG_DEVFS_DEBUG to be enabled. +Note that all debugging options have 'd' as the first character. By +default all options are off. All debugging output is sent to the +kernel logs. The debugging options do not take effect until the devfs +version message appears (just prior to the root filesystem being +mounted). + +These are the options: + +dmod print module load requests to + +dreg print device register requests to + +dunreg print device unregister requests to + +dchange print device change requests to + +dilookup print inode lookup requests + +diread print inode reads + +diunlink print inode unlinks + +diwrite print inode writes + +dimknod print calls to mknod(2) + +dall some debugging turned on + + +Other Options +============= + +These control the default behaviour of devfs. The options are: + +nomount do not mount devfs onto /dev at boot time + +only disable non-devfs device nodes for devfs-capable drivers diff -urN linux-2.2.20/Documentation/filesystems/devfs/modules.conf linux/Documentation/filesystems/devfs/modules.conf --- linux-2.2.20/Documentation/filesystems/devfs/modules.conf Wed Dec 31 17:00:00 1969 +++ linux/Documentation/filesystems/devfs/modules.conf Mon Dec 3 22:13:01 2001 @@ -0,0 +1,92 @@ +# Sample entries for /etc/modules.conf for devfs + +############################################################################### +# Configuration section: change to suit +# +alias /dev/sound sb +alias /dev/v4l bttv +alias gen-watchdog pcwd +alias gen-md raid0 +alias /dev/joysticks joystick +probeall scsi-hosts sym53c8xx + +############################################################################### +# Generic section: do not change +# +# All HDDs +probeall /dev/discs scsi-hosts sd_mod ide-probe-mod ide-disk DAC960 + +# All CD-ROMs +probeall /dev/cdroms scsi-hosts sr_mod ide-probe-mod ide-cd + +# All tapes +probeall /dev/tapes scsi-hosts st ide-probe-mod ide-tape + +# All SCSI devices +probeall /dev/scsi scsi-hosts sd_mod sr_mod st sg + +# All IDE devices +probeall /dev/ide ide-probe-mod ide-disk ide-cd ide-tape ide-floppy + +# SCSI HDDs +probeall /dev/sd scsi-hosts sd_mod +alias /dev/sd* /dev/sd + +# SCSI CD-ROMs +probeall /dev/sr scsi-hosts sr_mod +alias /dev/sr* /dev/sr + +# SCSI tapes +probeall /dev/st scsi-hosts st +alias /dev/st* /dev/st +alias /dev/nst* /dev/st + +# SCSI generic +probeall /dev/sg scsi-hosts sg +alias /dev/sg* /dev/sg + +# Floppies +alias /dev/floppy floppy +alias /dev/fd* floppy + +# RAMDISCs +alias /dev/rd rd +alias /dev/ram* rd + +# Loop devices +alias /dev/loop* loop + +# Meta devices +alias /dev/md* gen-md + +# Parallel port printers +alias /dev/printers lp +alias /dev/lp* lp + +# Soundcard +alias /dev/audio /dev/sound +alias /dev/mixer /dev/sound +alias /dev/dsp /dev/sound +alias /dev/dspW /dev/sound +alias /dev/midi /dev/sound + +# Joysticks +alias /dev/js* /dev/joysticks + +# Serial ports +alias /dev/tts serial +alias /dev/ttyS* /dev/tts +alias /dev/cua* /dev/tts + +# Miscellaneous devices +alias /dev/watchdog gen-watchdog # alias for gen-watchdog needed! +alias /dev/atibm atixlmouse +alias /dev/inportbm msbusmouse +alias /dev/logibm busmouse + +# PPP devices +alias /dev/ppp* ppp_generic + +# Video capture devices +alias /dev/video* /dev/v4l +alias /dev/vbi* /dev/v4l diff -urN linux-2.2.20/Documentation/filesystems/devfs/rc.devfs linux/Documentation/filesystems/devfs/rc.devfs --- linux-2.2.20/Documentation/filesystems/devfs/rc.devfs Wed Dec 31 17:00:00 1969 +++ linux/Documentation/filesystems/devfs/rc.devfs Mon Dec 3 22:13:01 2001 @@ -0,0 +1,104 @@ +#! /bin/sh +# +# /etc/rc.d/rc.devfs +# +# Linux Boot Scripts by Richard Gooch +# Copyright 1993-1999 under GNU Copyleft version 2.0. See /etc/rc for +# copyright notice. +# +# Save and restore devfs ownerships and permissions +# +# Written by Richard Gooch 11-JAN-1998 +# +# Updated by Richard Gooch 23-JAN-1998: Added "start" and "stop". +# +# Updated by Richard Gooch 5-AUG-1998: Robustness improvements by +# Roderich Schupp. +# +# Updated by Richard Gooch 9-AUG-1998: Took account of change from +# ".epoch" to ".devfsd". +# +# Updated by Richard Gooch 19-AUG-1998: Test and tty pattern patch +# by Roderich Schupp. +# +# Updated by Richard Gooch 24-MAY-1999: Use sed instead of tr. +# +# Last updated by Richard Gooch 25-MAY-1999: Don't save /dev/log. +# +# +# Usage: rc.devfs save|restore [savedir] [devfsdir] +# +# Note: "start" is a synonym for "restore" and "stop" is a synonym for "save". + +# Set VERBOSE to "no" if you would like a more quiet operation. +VERBOSE=yes + +# Set TAROPTS to "v" or even "vv" to see which files get saved/restored. +TAROPTS= + +option="$1" + +case "$option" in + save|restore) ;; + start) option=restore ;; + stop) option=save ;; + *) echo "No save or restore option given" ; exit 1 ;; +esac + +if [ "$2" = "" ]; then + savedir=/var/state +else + savedir=$2 +fi + +if [ ! -d $savedir ]; then + echo "Directory: $savedir does not exist" + exit 1 +fi + +if [ "$3" = "" ]; then + if [ -d /devfs ]; then + devfs=/devfs + else + devfs=/dev + fi +else + devfs=$3 +fi + +grep devfs /proc/filesystems >/dev/null || exit 0 + +if [ ! -d $devfs ]; then + echo "Directory: $devfs does not exist" + exit 1 +elif [ ! -c $devfs/.devfsd ]; then + echo "Directory: $devfs is not the root of a devfs filesystem" + exit 1 +fi + +savefile=`echo $devfs | sed 's*/*_*g'` +tarfile=${savedir}/devfssave.${savefile}.tar.gz + +cd $devfs + +case "$option" in + save) + [ "$VERBOSE" != no ] && echo "Saving $devfs permissions..." + + # You might want to adjust the pattern below to control + # which file's permissions will be saved. + # The sample pattern exludes all virtual consoles + # as well as old and new style pseudo terminals. + files=`find * -noleaf -cnewer .devfsd \ + ! -regex 'tty[0-9]+\|vc/.*\|vcsa?[0-9]+\|vcc/.*\|[pt]ty[a-z][0-9a-f]\|pt[ms]/.*\|log' -print` + rm -f $tarfile + [ -n "$files" ] && tar cz${TAROPTS}f $tarfile $files + ;; + + restore) + [ "$VERBOSE" != no ] && echo "Restoring $devfs permissions..." + [ -f $tarfile ] && tar xpz${TAROPTS}f $tarfile + ;; +esac + +exit 0 diff -urN linux-2.2.20/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c --- linux-2.2.20/arch/alpha/kernel/osf_sys.c Sun Mar 25 09:37:29 2001 +++ linux/arch/alpha/kernel/osf_sys.c Mon Dec 3 22:13:01 2001 @@ -42,9 +42,6 @@ extern int do_mount(kdev_t, const char *, const char *, char *, int, void *); extern int do_pipe(int *); -extern struct file_operations *get_blkfops(unsigned int); -extern struct file_operations *get_chrfops(unsigned int); - extern kdev_t get_unnamed_dev(void); extern void put_unnamed_dev(kdev_t); diff -urN linux-2.2.20/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c --- linux-2.2.20/arch/i386/kernel/apm.c Sun Mar 25 09:37:29 2001 +++ linux/arch/i386/kernel/apm.c Mon Dec 3 22:13:01 2001 @@ -1471,7 +1471,7 @@ static struct miscdevice apm_device = { APM_MINOR_DEV, - "apm", + "apm_bios", &apm_bios_fops }; diff -urN linux-2.2.20/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c --- linux-2.2.20/arch/i386/kernel/mtrr.c Sun Mar 25 09:31:45 2001 +++ linux/arch/i386/kernel/mtrr.c Mon Dec 3 22:13:01 2001 @@ -133,7 +133,7 @@ Fixed version numbering and history for v1.23 -> v1.24. v1.26 19990118 Richard Gooch - PLACEHOLDER. + Added devfs support. v1.27 19990123 Richard Gooch Changed locking to spin with reschedule. @@ -178,7 +178,6 @@ Moved to linux/arch/i386/kernel/setup.c and linux/include/asm-i386/bugs.h 19990228 Richard Gooch - Added #ifdef CONFIG_DEVFS_FS Added MTRRIOC_KILL_ENTRY ioctl(2) Trap for counter underflow in . Trap for 4 MiB aligned regions for PPro, stepping <= 7. @@ -230,6 +229,7 @@ #include #include #include +#include #include #include #define MTRR_NEED_STRINGS @@ -1296,8 +1296,6 @@ return reg; } /* End Function mtrr_del */ -#ifdef CONFIG_PROC_FS - static int mtrr_file_add (unsigned long base, unsigned long size, unsigned int type, char increment, struct file *file) { @@ -1502,6 +1500,8 @@ NULL, /* Lock */ }; +#ifdef CONFIG_PROC_FS + static struct inode_operations proc_mtrr_inode_operations = { &mtrr_fops, /* default property file-ops */ NULL, /* create */ @@ -1528,6 +1528,10 @@ 0, &proc_mtrr_inode_operations }; +#endif /* CONFIG_PROC_FS */ + +static devfs_handle_t devfs_handle = NULL; + static void compute_ascii (void) { char factor; @@ -1562,11 +1566,12 @@ ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes); } } + devfs_set_file_size (devfs_handle, ascii_buf_bytes); +#ifdef CONFIG_PROC_FS proc_root_mtrr.size = ascii_buf_bytes; +#endif } /* End Function compute_ascii */ -#endif /* CONFIG_PROC_FS */ - EXPORT_SYMBOL(mtrr_add); EXPORT_SYMBOL(mtrr_del); @@ -1893,6 +1898,9 @@ # ifdef CONFIG_PROC_FS proc_register (&proc_root, &proc_root_mtrr); # endif + devfs_handle = devfs_register (NULL, "cpu/mtrr", DEVFS_FL_DEFAULT, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, + &mtrr_fops, NULL); init_table (); return 0; diff -urN linux-2.2.20/arch/m68k/atari/joystick.c linux/arch/m68k/atari/joystick.c --- linux-2.2.20/arch/m68k/atari/joystick.c Sun Mar 25 09:31:50 2001 +++ linux/arch/m68k/atari/joystick.c Mon Dec 3 22:13:01 2001 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -132,12 +133,21 @@ __initfunc(int atari_joystick_init(void)) { + int i; + char devname[8]; + joystick[0].active = joystick[1].active = 0; joystick[0].ready = joystick[1].ready = 0; joystick[0].wait = joystick[1].wait = NULL; - if (register_chrdev(MAJOR_NR, "Joystick", &atari_joystick_fops)) + if (devfs_register_chrdev(MAJOR_NR, "Joystick", &atari_joystick_fops)) printk("unable to get major %d for joystick devices\n", MAJOR_NR); + for (i = 0; i < 2; i++) { + sprintf (devname, "djs%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, MAJOR_NR, i + 128, + S_IFCHR | S_IRUSR | S_IWUSR, + &atari_joystick_fops, NULL); + } return 0; } diff -urN linux-2.2.20/arch/m68k/mac/adb-bus.c linux/arch/m68k/mac/adb-bus.c --- linux-2.2.20/arch/m68k/mac/adb-bus.c Sun Mar 25 09:31:52 2001 +++ linux/arch/m68k/mac/adb-bus.c Mon Dec 3 22:13:01 2001 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -2703,10 +2704,16 @@ return 0; } +static devfs_handle_t devfs_handle = NULL; + void adbdev_init() { - if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) + if (devfs_register_chrdev(ADB_MAJOR, "adb", &adb_fops)) printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR); + devfs_handle = devfs_register (NULL, "adb", DEVFS_FL_DEFAULT, + ADB_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, + &adb_fops, NULL); } #if 0 /* old ADB device */ diff -urN linux-2.2.20/arch/sparc64/solaris/socksys.c linux/arch/sparc64/solaris/socksys.c --- linux-2.2.20/arch/sparc64/solaris/socksys.c Sun Mar 25 09:31:53 2001 +++ linux/arch/sparc64/solaris/socksys.c Mon Dec 3 22:13:01 2001 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,8 @@ socksys_release,/* release */ }; +static devfs_handle_t devfs_handle = NULL; + __initfunc(int init_socksys(void)) { @@ -184,7 +187,7 @@ int (*sys_close)(unsigned int) = (int (*)(unsigned int))SYS(close); - ret = register_chrdev (30, "socksys", &socksys_fops); + ret = devfs_register_chrdev (30, "socksys", &socksys_fops); if (ret < 0) { printk ("Couldn't register socksys character device\n"); return ret; @@ -194,6 +197,9 @@ printk ("Couldn't create socket\n"); return ret; } + devfs_handle = devfs_register (NULL, "socksys", DEVFS_FL_DEFAULT, + 30, 0, S_IFCHR | S_IRUSR | S_IWUSR, + &socksys_fops, NULL); file = fcheck(ret); /* N.B. Is this valid? Suppose the f_ops are in a module ... */ socksys_file_ops = *file->f_op; @@ -207,6 +213,7 @@ void cleanup_socksys(void) { - if (unregister_chrdev (30, "socksys")) + if (devfs_unregister_chrdev(30, "socksys")) printk ("Couldn't unregister socksys character device\n"); + devfs_unregister (devfs_handle); } diff -urN linux-2.2.20/drivers/block/acsi.c linux/drivers/block/acsi.c --- linux-2.2.20/drivers/block/acsi.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/acsi.c Mon Dec 3 22:13:01 2001 @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -1389,6 +1390,8 @@ ********************************************************************/ +extern struct file_operations acsi_fops; + static struct gendisk acsi_gendisk = { MAJOR_NR, /* Major number */ "ad", /* Major name */ @@ -1404,7 +1407,8 @@ acsi_sizes, /* block sizes */ 0, /* number */ (void *)acsi_info, /* internal */ - NULL /* next */ + NULL, /* next */ + &acsi_fops, /* file operations */ }; #define MAX_SCSI_DEVICE_CODE 10 @@ -1793,16 +1797,14 @@ { if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ACSI)) return 0; - - if (register_blkdev( MAJOR_NR, "ad", &acsi_fops )) { + if (devfs_register_blkdev( MAJOR_NR, "ad", &acsi_fops )) { printk( KERN_ERR "Unable to get major %d for ACSI\n", MAJOR_NR ); return -EBUSY; } - if (!(acsi_buffer = (char *)atari_stram_alloc( ACSI_BUFFER_SIZE, NULL, "acsi" ))) { printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" ); - unregister_blkdev( MAJOR_NR, "ad" ); + devfs_unregister_blkdev( MAJOR_NR, "ad" ); return -ENOMEM; } phys_acsi_buffer = virt_to_phys( acsi_buffer ); @@ -1841,7 +1843,7 @@ blk_dev[MAJOR_NR].request_fn = 0; atari_stram_free( acsi_buffer ); - if (unregister_blkdev( MAJOR_NR, "ad" ) != 0) + if (devfs_unregister_blkdev( MAJOR_NR, "ad" ) != 0) printk( KERN_ERR "acsi: cleanup_module failed\n"); for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) diff -urN linux-2.2.20/drivers/block/acsi_slm.c linux/drivers/block/acsi_slm.c --- linux-2.2.20/drivers/block/acsi_slm.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/acsi_slm.c Mon Dec 3 22:13:01 2001 @@ -65,6 +65,7 @@ #include #include #include +#include #include #include @@ -993,18 +994,31 @@ return( 1 ); } - int slm_init( void ) { - if (register_chrdev( MAJOR_NR, "slm", &slm_fops )) { + int i; + char devname[8]; + + if (devfs_register_chrdev( MAJOR_NR, "slm", &slm_fops )) { printk( KERN_ERR "Unable to get major %d for ACSI SLM\n", MAJOR_NR ); return -EBUSY; } + + for(i = 0; i < MAX_SLM; i++) { + sprintf (devname, "slm%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + MAJOR_NR, i, S_IFCHR | S_IRUSR | S_IWUSR, + &slm_fops, NULL); + } if (!(SLMBuffer = atari_stram_alloc( SLM_BUFFER_SIZE, NULL, "SLM" ))) { printk( KERN_ERR "Unable to get SLM ST-Ram buffer.\n" ); - unregister_chrdev( MAJOR_NR, "slm" ); + devfs_unregister_chrdev( MAJOR_NR, "slm" ); + for(i = 0; i < MAX_SLM; i++) { + sprintf(devname, "slm%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, MAJOR_NR, i, DEVFS_SPECIAL_CHR, 0)); + } return -ENOMEM; } BufferP = SLMBuffer; @@ -1032,8 +1046,15 @@ void cleanup_module(void) { - if (unregister_chrdev( MAJOR_NR, "slm" ) != 0) + int i; + char devname[8]; + + if (devfs_unregister_chrdev( MAJOR_NR, "slm" ) != 0) printk( KERN_ERR "acsi_slm: cleanup_module failed\n"); + for(i = 0; i < MAX_SLM; i++) { + sprintf(devname, "slm%i", i); + devfs_unregister((devfs_find_handle(NULL, devname, 0, MAJOR_NR, i, DEVFS_SPECIAL_CHR, 0)); + } atari_stram_free( SLMBuffer ); } #endif diff -urN linux-2.2.20/drivers/block/cciss.c linux/drivers/block/cciss.c --- linux-2.2.20/drivers/block/cciss.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/block/cciss.c Mon Dec 3 23:23:17 2001 @@ -1901,6 +1901,7 @@ hba[i]->gendisk.max_p = MAX_PART; hba[i]->gendisk.max_nr = NWD; hba[i]->gendisk.init = cciss_geninit; + hba[i]->gendisk.fops = &cciss_fops; hba[i]->gendisk.part = hba[i]->hd; hba[i]->gendisk.sizes = hba[i]->sizes; hba[i]->gendisk.nr_real = hba[i]->num_luns; diff -urN linux-2.2.20/drivers/block/cpqarray.c linux/drivers/block/cpqarray.c --- linux-2.2.20/drivers/block/cpqarray.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/block/cpqarray.c Mon Dec 3 23:21:43 2001 @@ -513,6 +513,7 @@ ida_gendisk[i].init = ida_geninit; ida_gendisk[i].part = ida + (i*256); ida_gendisk[i].sizes = ida_sizes + (i*256); + ida_gendisk[i].fops = &ida_fops; /* ida_gendisk[i].nr_real is handled by getgeometry */ blk_dev[MAJOR_NR+i].request_fn = request_fns[i]; diff -urN linux-2.2.20/drivers/block/floppy.c linux/drivers/block/floppy.c --- linux-2.2.20/drivers/block/floppy.c Sun Mar 25 09:31:23 2001 +++ linux/drivers/block/floppy.c Mon Dec 3 22:13:01 2001 @@ -144,6 +144,10 @@ #define FDPATCHES #include +/* + * 1998/1/21 -- Richard Gooch -- devfs support + */ + #include #include @@ -158,6 +162,7 @@ #include #include #include +#include /* * PS/2 floppies have much slower step rates than regular floppies. @@ -197,6 +202,9 @@ void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs); static int set_dor(int fdc, char mask, char data); static inline int __get_order(unsigned long size); +static void register_devfs_entries (int drive); +static devfs_handle_t devfs_handle = NULL; + #define K_64 0x10000 /* 64KB */ #include @@ -3641,6 +3649,7 @@ first = 0; } printk("%s fd%d is %s", prepend, drive, name); + register_devfs_entries (drive); } *UDP = *params; } @@ -3913,6 +3922,37 @@ floppy_revalidate, /* revalidate */ }; +static void register_devfs_entries (int drive) +{ + int base_minor, i; + static char *table[] = + {"", "d360", "h1200", "u360", "u720", "h360", "h720", + "u1440", "u2880", "CompaQ", "h1440", "u1680", "h410", + "u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743", + "h880", "u1040", "u1120", "h1600", "u1760", "u1920", + "u3200", "u3520", "u3840", "u1840", "u800", "u1600", + NULL + }; + static int t360[] = {1,0}, t1200[] = {2,5,6,10,12,14,16,18,20,23,0}, + t3in[] = {8,9,26,27,28, 7,11,15,19,24,25,29,31, 3,4,13,17,21,22,30,0}; + static int *table_sup[] = + {NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in}; + + base_minor = (drive < 4) ? drive : (124 + drive); + if (UDP->cmos <= NUMBER(default_drive_params)) { + i = 0; + do { + char name[16]; + + sprintf (name, "%d%s", drive, table[table_sup[UDP->cmos][i]]); + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, MAJOR_NR, + base_minor + (table_sup[UDP->cmos][i] << 2), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &floppy_fops, NULL); + } while (table_sup[UDP->cmos][i++]); + } +} + /* * Floppy Driver initialization * ============================= @@ -4132,6 +4172,7 @@ raw_cmd = 0; + devfs_handle = devfs_mk_dir (NULL, "floppy", 0, NULL); if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) { printk("Unable to get major %d for floppy\n",MAJOR_NR); return -EBUSY; @@ -4464,6 +4505,7 @@ { int dummy; + devfs_unregister (devfs_handle); unregister_blkdev(MAJOR_NR, "fd"); blk_dev[MAJOR_NR].request_fn = 0; diff -urN linux-2.2.20/drivers/block/genhd.c linux/drivers/block/genhd.c --- linux-2.2.20/drivers/block/genhd.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/block/genhd.c Mon Dec 3 22:13:01 2001 @@ -18,6 +18,9 @@ * Check partition table on IDE disks for common CHS translations * * Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl} + * + * devfs support - jj, rgooch, 980122 + * */ #include @@ -63,6 +66,7 @@ } struct gendisk *gendisk_head = NULL; +int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ static int current_minor = 0; extern int *blk_size[]; @@ -112,6 +116,15 @@ const char *maj = hd->major_name; int unit = (minor >> hd->minor_shift) + 'a'; + if (hd->part[minor].de) { + int pos, i; + + pos = devfs_generate_path (hd->part[minor].de, buf, 64); + if (pos >= 0) { + for (i = 0; (buf[i] = buf[pos + i]) != '\0'; i++); + return buf; + } + } #ifdef CONFIG_ARCH_S390 if ( strncmp ( hd->major_name,"dasd",4) == 0 ){ part = minor & ((1 << hd->minor_shift) - 1); @@ -176,14 +189,20 @@ static void add_partition (struct gendisk *hd, int minor, int start, int size, int type) { +#ifndef CONFIG_DEVFS_FS char buf[MAX_DISKNAME_LEN]; +#endif struct hd_struct *p = hd->part+minor; p->start_sect = start; p->nr_sects = size; p->type = type; +#ifdef CONFIG_DEVFS_FS + printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); +#else printk(" %s", disk_name(hd, minor, buf)); +#endif } static inline int is_extended_partition(struct partition *p) @@ -571,7 +590,7 @@ read_mbr: #endif if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { - printk(" unable to read partition table\n"); + if (warn_no_part) printk(" unable to read partition table\n"); return -1; } @@ -827,7 +846,7 @@ #define DISKLABELMAGIC (0x82564557UL) if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { - printk("unable to read partition table\n"); + if (warn_no_part) printk("unable to read partition table\n"); return -1; } label = (struct disklabel *) (bh->b_data+64); @@ -895,7 +914,7 @@ #define SUN_LABEL_MAGIC 0xDABE if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk("Dev %s: unable to read partition table\n", + if (warn_no_part) printk("Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } @@ -970,7 +989,7 @@ #define SGI_LABEL_MAGIC 0x0be5a941 if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk("Dev %s: unable to read partition table\n", kdevname(dev)); + if (warn_no_part) printk("Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } label = (struct sgi_disklabel *) bh->b_data; @@ -1053,7 +1072,7 @@ for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) { if(!(bh = bread(dev,blk,blocksize))) { - printk("Dev %s: unable to read RDB block %d\n", + if (warn_no_part) printk("Dev %s: unable to read RDB block %d\n", kdevname(dev),blk); goto rdb_done; } @@ -1079,7 +1098,7 @@ brelse(bh); for (part = 1; blk > 0 && part <= 16; part++) { if (!(bh = bread(dev,blk,blocksize))) { - printk("Dev %s: unable to read partition block %d\n", + if (warn_no_part) printk("Dev %s: unable to read partition block %d\n", kdevname(dev),blk); goto rdb_done; } @@ -1313,7 +1332,7 @@ bh = bread (dev, 0, get_ptable_blocksize(dev)); if (!bh) { - printk (" unable to read block 0\n"); + if (warn_no_part) printk (" unable to read block 0\n"); return -1; } @@ -1450,7 +1469,7 @@ bh = bread (dev, SBLOCK, get_ptable_blocksize(dev)); if (!bh) { - printk (" unable to read block 0x%lx\n", SBLOCK); + if (warn_no_part) printk (" unable to read block 0x%lx\n", SBLOCK); return -1; } @@ -1610,11 +1629,95 @@ } #endif +#ifdef CONFIG_DEVFS_FS +static void devfs_register_partition (struct gendisk *dev, int minor, int part) +{ + int devnum = minor >> dev->minor_shift; + devfs_handle_t dir; + unsigned int devfs_flags = DEVFS_FL_DEFAULT; + char devname[16]; + + if (dev->part[minor + part].de) return; + dir = devfs_get_parent (dev->part[minor].de); + if (!dir) return; + if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) ) + devfs_flags |= DEVFS_FL_REMOVABLE; + sprintf (devname, "part%d", part); + dev->part[minor + part].de = + devfs_register (dir, devname, devfs_flags, + dev->major, minor + part, + S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL); +} + +static void devfs_register_disc (struct gendisk *dev, int minor) +{ + int pos = 0; + int devnum = minor >> dev->minor_shift; + devfs_handle_t dir, slave; + unsigned int devfs_flags = DEVFS_FL_DEFAULT; + char dirname[64], symlink[16]; + static unsigned int disc_counter = 0; + static devfs_handle_t devfs_handle = NULL; + + if (dev->part[minor].de) return; + if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) ) + devfs_flags |= DEVFS_FL_REMOVABLE; + if (dev->de_arr) { + dir = dev->de_arr[devnum]; + if (!dir) /* Aware driver wants to block disc management */ + return; + pos = devfs_generate_path (dir, dirname + 3, sizeof dirname-3); + if (pos < 0) return; + strncpy (dirname + pos, "../", 3); + } + else { + /* Unaware driver: construct "real" directory */ + sprintf (dirname, "../%s/disc%d", dev->major_name, devnum); + dir = devfs_mk_dir (NULL, dirname + 3, 0, NULL); + } + if (!devfs_handle) + devfs_handle = devfs_mk_dir (NULL, "discs", 5, NULL); + sprintf (symlink, "disc%u", disc_counter++); + devfs_mk_symlink (devfs_handle, symlink, 0, DEVFS_FL_DEFAULT, + dirname + pos, 0, &slave, NULL); + dev->part[minor].de = + devfs_register (dir, "disc", devfs_flags, dev->major, minor, + S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL); + devfs_auto_unregister (dev->part[minor].de, slave); + if (!dev->de_arr) + devfs_auto_unregister (slave, dir); +} +#endif /* CONFIG_DEVFS_FS */ + +void devfs_register_partitions (struct gendisk *dev, int minor, int unregister) +{ +#ifdef CONFIG_DEVFS_FS + int part; + + if (!unregister) + devfs_register_disc (dev, minor); + for (part = 1; part < dev->max_p; part++) { + if ( unregister || (dev->part[part + minor].nr_sects < 1) ) { + devfs_unregister (dev->part[part + minor].de); + dev->part[part + minor].de = NULL; + continue; + } + devfs_register_partition (dev, minor, part); + } + if (unregister) { + devfs_unregister (dev->part[minor].de); + dev->part[minor].de = NULL; + } +#endif /* CONFIG_DEVFS_FS */ +} + static void check_partition(struct gendisk *hd, kdev_t dev) { + devfs_handle_t de = NULL; + int pos; static int first_time = 1; unsigned long first_sector; - char buf[MAX_DISKNAME_LEN]; + char buf[64]; if (first_time) printk(KERN_INFO "Partition check:\n"); @@ -1630,44 +1733,52 @@ return; } - printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); + if (hd->de_arr) + de = hd->de_arr[MINOR (dev) >> hd->minor_shift]; + pos = devfs_generate_path (de, buf, sizeof buf); + if (pos >= 0) + printk(KERN_INFO " /dev/%s:", buf + pos); + else + printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); #ifdef CONFIG_MSDOS_PARTITION if (msdos_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_OSF_PARTITION if (osf_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_SUN_PARTITION if(sun_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_AMIGA_PARTITION if(amiga_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_MAC_PARTITION if (mac_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_SGI_PARTITION if(sgi_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_ULTRIX_PARTITION if(ultrix_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_ATARI_PARTITION if(atari_partition(hd, dev, first_sector)) - return; + goto found_ptable; #endif #ifdef CONFIG_ARCH_S390 if (ibm_partition (hd, dev, first_sector)) - return; + goto found_ptable; #endif printk(" unknown partition table\n"); +found_ptable: + devfs_register_partitions (hd, MINOR (dev), hd->sizes ? 0 : 1); } /* This function is used to re-read partition tables for removable disks. @@ -1796,7 +1907,7 @@ int get_partition_list(char *page, char **start, off_t offset, int count) { struct gendisk *p; - char buf[MAX_DISKNAME_LEN]; + char buf[64]; int n, len; len = sprintf(page, "major minor #blocks name\n\n"); diff -urN linux-2.2.20/drivers/block/hd.c linux/drivers/block/hd.c --- linux-2.2.20/drivers/block/hd.c Sun Mar 25 09:37:30 2001 +++ linux/drivers/block/hd.c Mon Dec 3 22:13:01 2001 @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -664,6 +665,7 @@ } static void hd_geninit(struct gendisk *); +extern struct file_operations hd_fops; static struct gendisk hd_gendisk = { MAJOR_NR, /* Major number */ @@ -676,7 +678,8 @@ hd_sizes, /* block sizes */ 0, /* number */ NULL, /* internal use, not presently used */ - NULL /* next */ + NULL, /* next */ + &hd_fops, /* file operations */ }; static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -797,7 +800,7 @@ __initfunc(int hd_init(void)) { - if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) { + if (devfs_register_blkdev(MAJOR_NR,"hd",&hd_fops)) { printk("hd: unable to get major %d for hard disk\n",MAJOR_NR); return -1; } diff -urN linux-2.2.20/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- linux-2.2.20/drivers/block/ide-cd.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/block/ide-cd.c Mon Dec 3 22:13:01 2001 @@ -293,6 +293,7 @@ #include #include #include +#include #include #include #include @@ -2264,6 +2265,11 @@ devinfo->mask |= CDC_PLAY_AUDIO; if (!CDROM_CONFIG_FLAGS (drive)->close_tray) devinfo->mask |= CDC_CLOSE_TRAY; + + devinfo->de = devfs_register (drive->de, "cd", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFBLK | S_IRUGO | S_IWUGO, + ide_fops, NULL); return register_cdrom(devinfo); } diff -urN linux-2.2.20/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c --- linux-2.2.20/drivers/block/ide-disk.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/ide-disk.c Mon Dec 3 22:13:01 2001 @@ -746,6 +746,8 @@ static void idedisk_setup (ide_drive_t *drive) { + int i; + struct hd_driveid *id = drive->id; unsigned long capacity, check; @@ -765,6 +767,15 @@ if (id->model[0] != 'W' || id->model[1] != 'D') { drive->doorlocking = 1; } + } + for (i = 0; i < MAX_DRIVES; ++i) { + ide_hwif_t *hwif = HWIF(drive); + + if (drive != &hwif->drives[i]) continue; + hwif->gd->de_arr[i] = drive->de; + if (drive->removable) + hwif->gd->flags[i] |= GENHD_FL_REMOVABLE; + break; } /* Extract geometry if we did not already have one for the drive */ diff -urN linux-2.2.20/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c --- linux-2.2.20/drivers/block/ide-floppy.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/block/ide-floppy.c Mon Dec 3 22:13:01 2001 @@ -1590,6 +1590,15 @@ (void) idefloppy_get_capacity (drive); idefloppy_add_settings(drive); + for (i = 0; i < MAX_DRIVES; ++i) { + ide_hwif_t *hwif = HWIF(drive); + + if (drive != &hwif->drives[i]) continue; + hwif->gd->de_arr[i] = drive->de; + if (drive->removable) + hwif->gd->flags[i] |= GENHD_FL_REMOVABLE; + break; + } } static int idefloppy_cleanup (ide_drive_t *drive) diff -urN linux-2.2.20/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c --- linux-2.2.20/drivers/block/ide-probe.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/ide-probe.c Mon Dec 3 22:13:01 2001 @@ -699,13 +699,28 @@ gd->init = &ide_geninit; /* initialization function */ gd->real_devices= hwif; /* ptr to internal data */ gd->next = NULL; /* linked list of major devs */ + gd->fops = ide_fops; /* file operations */ + gd->de_arr = kmalloc (sizeof *gd->de_arr * units, GFP_KERNEL); + gd->flags = kmalloc (sizeof *gd->flags * units, GFP_KERNEL); + if (gd->de_arr) + memset (gd->de_arr, 0, sizeof *gd->de_arr * units); + if (gd->flags) + memset (gd->flags, 0, sizeof *gd->flags * units); for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ; hwif->gd = *gdp = gd; /* link onto tail of list */ for (unit = 0; unit < units; ++unit) { - if (hwif->drives[unit].present) + if (hwif->drives[unit].present) { + char name[64]; + ide_add_generic_settings(hwif->drives + unit); + sprintf (name, "ide/host%d/bus%d/target%d/lun%d", + (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index, + hwif->channel, unit, 0); + hwif->drives[unit].de = + devfs_mk_dir (NULL, name, 0, NULL); + } } } diff -urN linux-2.2.20/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c --- linux-2.2.20/drivers/block/ide-tape.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/ide-tape.c Mon Dec 3 22:13:01 2001 @@ -338,6 +338,7 @@ #include #include #include +#include #include #include #include @@ -630,6 +631,7 @@ */ typedef struct { ide_drive_t *drive; + devfs_handle_t de_r, de_n; /* * Since a typical character device operation requires more @@ -3848,11 +3850,13 @@ DRIVER(drive)->busy = 0; (void) ide_unregister_subdriver (drive); drive->driver_data = NULL; + devfs_unregister (tape->de_r); + devfs_unregister (tape->de_n); kfree (tape); for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) if (idetape_chrdevs[minor].drive != NULL) return 0; - unregister_chrdev (IDETAPE_MAJOR, "ht"); + devfs_unregister_chrdev (IDETAPE_MAJOR, "ht"); idetape_chrdev_present = 0; return 0; } @@ -3952,7 +3956,8 @@ MOD_DEC_USE_COUNT; return 0; } - if (!idetape_chrdev_present && register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) { + if (!idetape_chrdev_present && + devfs_register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) { printk (KERN_ERR "ide-tape: Failed to register character device interface\n"); MOD_DEC_USE_COUNT; return -EBUSY; @@ -3975,10 +3980,21 @@ for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++); idetape_setup (drive, tape, minor); idetape_chrdevs[minor].drive = drive; + tape->de_r = + devfs_register (drive->de, "mt", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + tape->de_n = + devfs_register (drive->de, "mtn", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + devfs_register_tape (tape->de_r); supported++; failed--; } while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL); if (!idetape_chrdev_present && !supported) { - unregister_chrdev (IDETAPE_MAJOR, "ht"); + devfs_unregister_chrdev (IDETAPE_MAJOR, "ht"); } else idetape_chrdev_present = 1; ide_register_module (&idetape_module); diff -urN linux-2.2.20/drivers/block/ide.c linux/drivers/block/ide.c --- linux-2.2.20/drivers/block/ide.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/block/ide.c Mon Dec 3 23:18:22 2001 @@ -1860,6 +1860,10 @@ d = hwgroup->drive; for (i = 0; i < MAX_DRIVES; ++i) { drive = &hwif->drives[i]; + if (drive->de) { + devfs_unregister (drive->de); + drive->de = NULL; + } if (!drive->present) continue; while (hwgroup->drive->next != drive) @@ -1908,6 +1912,10 @@ gd = *gdp; *gdp = gd->next; kfree(gd->sizes); kfree(gd->part); + if (gd->de_arr) + kfree (gd->de_arr); + if (gd->flags) + kfree (gd->flags); kfree(gd); } init_hwif_data (index); /* restore hwif data to pristine status */ @@ -3199,5 +3207,6 @@ #ifdef CONFIG_PROC_FS proc_ide_destroy(); #endif + devfs_unregister (devfs_find_handle(NULL, "ide", 0, 0, 0, 0, 0)); } #endif /* MODULE */ diff -urN linux-2.2.20/drivers/block/ide.h linux/drivers/block/ide.h --- linux-2.2.20/drivers/block/ide.h Sun Mar 25 09:31:23 2001 +++ linux/drivers/block/ide.h Mon Dec 3 23:56:55 2001 @@ -12,6 +12,7 @@ #include #include #include +#include #include /* @@ -144,7 +145,7 @@ /* * Some more useful definitions */ -#define IDE_MAJOR_NAME "ide" /* the same for all i/f; see also genhd.c */ +#define IDE_MAJOR_NAME "hd" /* the same for all i/f; see also genhd.c */ #define MAJOR_NAME IDE_MAJOR_NAME #define PARTN_BITS 6 /* number of minor dev bits for partitions */ #define PARTN_MASK ((1< 16-Jan-1998 + * * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998 * * Loadable modules and other fixes by AK, 1998 @@ -45,6 +47,7 @@ #include #include +#include #include @@ -69,6 +72,8 @@ #define FALSE 0 #define TRUE (!FALSE) +static devfs_handle_t devfs_handle = NULL; /* For the directory */ + /* Forward declaration of function to create missing blocks in the backing file (can happen if the backing file is sparse) */ static int create_missing_block(struct loop_device *lo, int block, int blksize); @@ -342,6 +347,8 @@ } } +extern struct file_operations lo_fops; + static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) { struct file *file; @@ -367,7 +374,13 @@ } if (S_ISBLK(inode->i_mode)) { +#ifdef CONFIG_DEVFS_FS + error = -ENODEV; + if (file->f_op->open) + error = file->f_op->open (inode, file); +#else error = blkdev_open(inode, file); +#endif lo->lo_device = inode->i_rdev; lo->lo_flags = 0; @@ -466,14 +479,23 @@ static int loop_clr_fd(struct loop_device *lo, kdev_t dev) { struct dentry *dentry = lo->lo_dentry; + struct inode *inode; if (!dentry) return -ENXIO; + inode = dentry->d_inode; + if (!inode) + return -ENXIO; if (lo->lo_refcnt > 1) /* we needed one fd for the ioctl */ return -EBUSY; - if (S_ISBLK(dentry->d_inode->i_mode)) - blkdev_release (dentry->d_inode); + if (S_ISBLK(inode->i_mode)) +#ifdef CONFIG_DEVFS_FS + if (inode->i_op->default_file_ops->release) + inode->i_op->default_file_ops->release (inode, NULL); +#else + blkdev_release (inode); +#endif lo->lo_dentry = NULL; if (lo->lo_backing_file != NULL) { @@ -708,11 +730,22 @@ { int i; - if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) { + if (devfs_register_blkdev(MAJOR_NR, "loop", &lo_fops)) { printk(KERN_WARNING "Unable to get major number %d for loop device\n", MAJOR_NR); return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "loop", 0, NULL); + for (i = 0; i < MAX_LOOP; ++i) + { + char devname[8]; + + sprintf (devname, "%d", i); + devfs_register (devfs_handle, devname, DEVFS_FL_DEFAULT, + MAJOR_NR, i, + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + &lo_fops, NULL); + } #ifndef MODULE printk(KERN_INFO "loop: registered device at major %d\n", MAJOR_NR); #endif @@ -733,7 +766,8 @@ #ifdef MODULE void cleanup_module(void) { - if (unregister_blkdev(MAJOR_NR, "loop") != 0) + devfs_unregister (devfs_handle); + if (devfs_unregister_blkdev(MAJOR_NR, "loop") != 0) printk(KERN_WARNING "loop: cannot unregister blkdev\n"); } #endif diff -urN linux-2.2.20/drivers/block/md.c linux/drivers/block/md.c --- linux-2.2.20/drivers/block/md.c Sun Mar 25 09:31:23 2001 +++ linux/drivers/block/md.c Mon Dec 3 22:13:01 2001 @@ -15,7 +15,9 @@ Changes for kmod by: Cyrus Durgin - + + Devfs support by Richard Gooch + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) @@ -51,6 +53,7 @@ #endif #include #include +#include #define __KERNEL_SYSCALLS__ #include @@ -77,6 +80,8 @@ int md_size[MAX_MD_DEV]={0, }; static void md_geninit (struct gendisk *); +extern struct file_operations md_fops; +static devfs_handle_t devfs_handle = NULL; static struct gendisk md_gendisk= { @@ -90,7 +95,8 @@ md_size, MAX_MD_DEV, NULL, - NULL + NULL, + &md_fops, }; static struct md_personality *pers[MAX_PERSONALITY]={NULL, }; @@ -115,7 +121,7 @@ char *partition_name (kdev_t dev) { - static char name[40]; /* This should be long + static char name[64]; /* This should be long enough for a device name ! */ struct gendisk *hd = find_gendisk (dev); @@ -612,7 +618,6 @@ } } else md_dev[minor].nb_dev++; - printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor); return (0); } @@ -1309,6 +1314,8 @@ __initfunc(int md_init (void)) { + int i; + printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION, MAX_MD_DEV, MAX_REAL); @@ -1317,6 +1324,16 @@ { printk ("Unable to get major %d for md\n", MD_MAJOR); return (-1); + } + devfs_handle = devfs_mk_dir (NULL, "md", 0, NULL); + + for (i = 0; i < MAX_MD_DEV; i++) + { + char name[8]; + + sprintf (name, "%d", i); + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, MAJOR_NR, i, + S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL); } blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST; diff -urN linux-2.2.20/drivers/block/nbd.c linux/drivers/block/nbd.c --- linux-2.2.20/drivers/block/nbd.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/nbd.c Mon Dec 3 22:13:01 2001 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,7 @@ static u64 nbd_bytesizes[MAX_NBD]; static struct nbd_device nbd_dev[MAX_NBD]; +static devfs_handle_t devfs_handle = NULL; #define DEBUG( s ) /* #define DEBUG( s ) printk( s ) @@ -483,6 +485,7 @@ MAJOR_NR); return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "nbd", 0, NULL); #ifdef MODULE printk("nbd: registered device at major %d\n", MAJOR_NR); #endif @@ -490,6 +493,8 @@ blk_size[MAJOR_NR] = nbd_sizes; blk_dev[MAJOR_NR].request_fn = do_nbd_request; for (i = 0; i < MAX_NBD; i++) { + char nbd_name[8]; + nbd_dev[i].refcnt = 0; nbd_dev[i].file = NULL; nbd_dev[i].magic = LO_MAGIC; @@ -498,6 +503,12 @@ nbd_blksize_bits[i] = 10; nbd_bytesizes[i] = 0x7ffffc00; /* 2GB */ nbd_sizes[i] = nbd_bytesizes[i] >> nbd_blksize_bits[i]; + + sprintf(nbd_name, "%d", i); + devfs_register(devfs_handle, nbd_name, DEVFS_FL_DEFAULT, + MAJOR_NR, i, + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &nbd_fops, NULL); } return 0; } @@ -505,6 +516,7 @@ #ifdef MODULE void cleanup_module(void) { + devfs_unregister (devfs_handle); if (unregister_blkdev(MAJOR_NR, "nbd") != 0) printk("nbd: cleanup_module failed\n"); else diff -urN linux-2.2.20/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c --- linux-2.2.20/drivers/block/paride/pd.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/paride/pd.c Mon Dec 3 22:13:01 2001 @@ -156,6 +156,7 @@ #include #include #include +#include #include #include #include @@ -339,20 +340,6 @@ /* kernel glue structures */ -static struct gendisk pd_gendisk = { - PD_MAJOR, /* Major number */ - PD_NAME, /* Major name */ - PD_BITS, /* Bits to shift to get real from partition */ - PD_PARTNS, /* Number of partitions per real */ - PD_UNITS, /* maximum number of real */ - pd_geninit, /* init function */ - pd_hd, /* hd struct */ - pd_sizes, /* block sizes */ - 0, /* number */ - NULL, /* internal */ - NULL /* next */ -}; - static struct file_operations pd_fops = { NULL, /* lseek - default */ block_read, /* read - general block-dev read */ @@ -370,6 +357,21 @@ pd_revalidate /* revalidate new media */ }; +static struct gendisk pd_gendisk = { + PD_MAJOR, /* Major number */ + PD_NAME, /* Major name */ + PD_BITS, /* Bits to shift to get real from partition */ + PD_PARTNS, /* Number of partitions per real */ + PD_UNITS, /* maximum number of real */ + pd_geninit, /* init function */ + pd_hd, /* hd struct */ + pd_sizes, /* block sizes */ + 0, /* number */ + NULL, /* internal */ + NULL, /* next */ + &pd_fops, /* file operations */ +}; + void pd_init_units( void ) { int unit, j; @@ -397,8 +399,7 @@ { int i; if (disable) return -1; - - if (register_blkdev(MAJOR_NR,name,&pd_fops)) { + if (devfs_register_blkdev(MAJOR_NR,name,&pd_fops)) { printk("%s: unable to get major number %d\n", name,major); return -1; @@ -638,8 +639,7 @@ { struct gendisk **gdp; int unit; - unregister_blkdev(MAJOR_NR,name); - + devfs_unregister_blkdev(MAJOR_NR,name); for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next)) if (*gdp == &pd_gendisk) break; if (*gdp) *gdp = (*gdp)->next; diff -urN linux-2.2.20/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c --- linux-2.2.20/drivers/block/paride/pg.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/block/paride/pg.c Mon Dec 3 22:13:01 2001 @@ -169,6 +169,7 @@ #include #include #include +#include #include @@ -297,7 +298,8 @@ int pg_init (void) /* preliminary initialisation */ -{ int unit; +{ int unit, i; + char devname[8]; if (disable) return -1; @@ -305,14 +307,19 @@ if (pg_detect()) return -1; - if (register_chrdev(major,name,&pg_fops)) { + if (devfs_register_chrdev(major,name,&pg_fops)) { printk("pg_init: unable to get major number %d\n", major); for (unit=0;unit #include #include +#include #include @@ -300,7 +301,8 @@ int pt_init (void) /* preliminary initialisation */ -{ int unit; +{ int unit, i; + char devname[8]; if (disable) return -1; @@ -308,7 +310,7 @@ if (pt_detect()) return -1; - if (register_chrdev(major,name,&pt_fops)) { + if (devfs_register_chrdev(major,name,&pt_fops)) { printk("pt_init: unable to get major number %d\n", major); for (unit=0;unit #include #include +#include #include #include #include @@ -171,7 +172,8 @@ ps2esdi_sizes, /* block sizes */ 0, /* number */ (void *) ps2esdi_info, /* internal */ - NULL /* next */ + NULL, /* next */ + &ps2esdi_fops, /* file operations */ }; /* initialization routine called by ll_rw_blk.c */ @@ -180,7 +182,7 @@ /* register the device - pass the name, major number and operations vector . */ - if (register_blkdev(MAJOR_NR, "ed", &ps2esdi_fops)) { + if (devfs_register_blkdev(MAJOR_NR, "ed", &ps2esdi_fops)) { printk("%s: Unable to get major number %d\n", DEVICE_NAME, MAJOR_NR); return -1; } @@ -236,7 +238,7 @@ release_region(io_base, 4); free_dma(dma_arb_level); free_irq(PS2ESDI_IRQ, NULL) - unregister_blkdev(MAJOR_NR, "ed"); + devfs_unregister_blkdev(MAJOR_NR, "ed"); } #endif /* MODULE */ diff -urN linux-2.2.20/drivers/block/rd.c linux/drivers/block/rd.c --- linux-2.2.20/drivers/block/rd.c Sun Mar 25 09:37:30 2001 +++ linux/drivers/block/rd.c Mon Dec 3 22:13:01 2001 @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,7 @@ static int rd_hardsec[NUM_RAMDISKS]; /* Size of real blocks in bytes */ static int rd_blocksizes[NUM_RAMDISKS]; /* Size of 1024 byte blocks :) */ static int rd_kbsize[NUM_RAMDISKS]; /* Size in blocks of 1024 bytes */ +static devfs_handle_t devfs_handle; /* * Parameters for the boot-loading of the RAM disk. These are set by @@ -130,6 +132,8 @@ * deleted, and make that my Ramdisk. If the request is outside of the * allocated size, we must get rid of it... * + * 19-JAN-1998 Richard Gooch Added devfs support + * */ static void rd_request(void) { @@ -285,7 +289,7 @@ return 0; } -static struct file_operations fd_fops = { +static struct file_operations rd_fops = { NULL, /* lseek - default */ block_read, /* read - block dev read */ block_write, /* write - block dev write */ @@ -304,19 +308,26 @@ { int i; - if (register_blkdev(MAJOR_NR, "ramdisk", &fd_fops)) { + if (register_blkdev(MAJOR_NR, "ramdisk", &rd_fops)) { printk("RAMDISK: Could not get major %d", MAJOR_NR); return -EIO; } blk_dev[MAJOR_NR].request_fn = &rd_request; + devfs_handle = devfs_mk_dir (NULL, "rd", 0, NULL); for (i = 0; i < NUM_RAMDISKS; i++) { + char name[8]; + /* rd_size is given in kB */ rd_length[i] = (rd_size << BLOCK_SIZE_BITS); rd_hardsec[i] = RDBLK_SIZE; rd_blocksizes[i] = BLOCK_SIZE; rd_kbsize[i] = (rd_length[i] >> BLOCK_SIZE_BITS); + sprintf (name, "%d", i); + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, + MAJOR_NR, i, S_IFBLK | S_IRUSR | S_IWUSR, + &rd_fops, NULL); } hardsect_size[MAJOR_NR] = rd_hardsec; /* Size of the RAM disk blocks */ @@ -352,6 +363,7 @@ for (i = 0 ; i < NUM_RAMDISKS; i++) destroy_buffers(MKDEV(MAJOR_NR, i)); + devfs_unregister (devfs_handle); unregister_blkdev( MAJOR_NR, "ramdisk" ); blk_dev[MAJOR_NR].request_fn = 0; } @@ -580,6 +592,7 @@ successful_load: invalidate_buffers(device); ROOT_DEV = MKDEV(MAJOR_NR, unit); + if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, "rd/0"); done: if (infile.f_op->release) diff -urN linux-2.2.20/drivers/block/xd.c linux/drivers/block/xd.c --- linux-2.2.20/drivers/block/xd.c Sun Mar 25 09:31:23 2001 +++ linux/drivers/block/xd.c Mon Dec 3 22:13:01 2001 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -129,6 +130,19 @@ static struct hd_struct xd_struct[XD_MAXDRIVES << 6]; static int xd_sizes[XD_MAXDRIVES << 6], xd_access[XD_MAXDRIVES] = { 0, 0 }; static int xd_blocksizes[XD_MAXDRIVES << 6]; +static struct file_operations xd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* poll */ + xd_ioctl, /* ioctl */ + NULL, /* mmap */ + xd_open, /* open */ + NULL, /* flush */ + xd_release, /* release */ + block_fsync /* fsync */ +}; static struct gendisk xd_gendisk = { MAJOR_NR, /* Major number */ "xd", /* Major name */ @@ -144,20 +158,8 @@ xd_sizes, /* block sizes */ 0, /* number */ (void *) xd_info, /* internal */ - NULL /* next */ -}; -static struct file_operations xd_fops = { - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* poll */ - xd_ioctl, /* ioctl */ - NULL, /* mmap */ - xd_open, /* open */ - NULL, /* flush */ - xd_release, /* release */ - block_fsync /* fsync */ + NULL, /* next */ + &xd_fops, /* file operations */ }; static struct wait_queue *xd_wait_int = NULL, *xd_wait_open = NULL; static u_char xd_valid[XD_MAXDRIVES] = { 0,0 }; @@ -176,13 +178,17 @@ static volatile u_char xd_error; static int nodma = XD_DONT_USE_DMA; +static devfs_handle_t devfs_handle = NULL; + + /* xd_init: register the block device number and set up pointer tables */ __initfunc(int xd_init (void)) { - if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) { + if (devfs_register_blkdev(MAJOR_NR,"xd",&xd_fops)) { printk("xd: Unable to get major number %d\n",MAJOR_NR); return -1; } + devfs_handle = devfs_mk_dir (NULL, xd_gendisk.major_name, 0, NULL); blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ xd_gendisk.next = gendisk_head; @@ -1172,7 +1178,7 @@ xd_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 }); if (!xd_drives) { /* no drives detected - unload module */ - unregister_blkdev(MAJOR_NR, "xd"); + devfs_unregister_blkdev(MAJOR_NR, "xd"); xd_done(); return (-1); } @@ -1187,7 +1193,7 @@ { int partition,dev,start; - unregister_blkdev(MAJOR_NR, "xd"); + devfs_unregister_blkdev(MAJOR_NR, "xd"); for (dev = 0; dev < xd_drives; dev++) { start = dev << xd_gendisk.minor_shift; for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) { @@ -1199,6 +1205,7 @@ } } xd_done(); + devfs_unregister (devfs_handle); if (xd_drives) { free_irq(xd_irq, NULL); free_dma(xd_dma); diff -urN linux-2.2.20/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c --- linux-2.2.20/drivers/cdrom/aztcd.c Sun Mar 25 09:31:36 2001 +++ linux/drivers/cdrom/aztcd.c Mon Dec 3 22:13:01 2001 @@ -178,6 +178,7 @@ #include #include #include +#include #ifndef AZT_KERNEL_PRIOR_2_1 #include @@ -1791,7 +1792,9 @@ return -EIO; } } - if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) + devfs_register (NULL, "aztcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &azt_fops, NULL); + if (devfs_register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) { printk("aztcd: Unable to get major %d for Aztech CD-ROM\n", MAJOR_NR); @@ -1823,7 +1826,9 @@ void cleanup_module(void) { - if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) + devfs_unregister(devfs_find_handle(NULL, "aztcd", 0, 0, 0, DEVFS_SPECIAL_BLK, + 0)); + if ((devfs_unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) { printk("What's that: can't unregister aztcd\n"); return; } diff -urN linux-2.2.20/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c --- linux-2.2.20/drivers/cdrom/cdrom.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/cdrom/cdrom.c Mon Dec 3 22:13:01 2001 @@ -322,6 +322,7 @@ static void cdrom_sysctl_register(void); #endif /* CONFIG_SYSCTL */ static struct cdrom_device_info *topCdromPtr = NULL; +static devfs_handle_t devfs_handle = NULL; struct file_operations cdrom_fops = { @@ -392,6 +393,25 @@ if (check_media_type==1) cdi->options |= (int) CDO_CHECK_TYPE; + if (cdi->de) { + int pos; + devfs_handle_t slave; + char devname[64], symlink[16]; + static unsigned int cdrom_counter = 0; + + if (!devfs_handle) + devfs_handle = devfs_mk_dir (NULL, "cdroms", 6, NULL); + pos = devfs_generate_path (cdi->de, devname + 3, + sizeof devname - 3); + if (pos >= 0) { + strncpy (devname + pos, "../", 3); + sprintf (symlink, "cdrom%u", cdrom_counter++); + devfs_mk_symlink (devfs_handle, symlink, 0, + DEVFS_FL_DEFAULT, + devname + pos, 0, &slave, NULL); + devfs_auto_unregister (cdi->de, slave); + } + } cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); cdi->next = topCdromPtr; topCdromPtr = cdi; @@ -2617,7 +2637,8 @@ printk(KERN_INFO "Uniform CD-ROM driver unloaded\n"); #ifdef CONFIG_SYSCTL cdrom_sysctl_unregister(); -#endif /* CONFIG_SYSCTL */ +#endif /* CONFIG_SYSCTL */ + devfs_unregister (devfs_handle); } #endif /* endif MODULE */ diff -urN linux-2.2.20/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c --- linux-2.2.20/drivers/cdrom/cdu31a.c Sun Mar 25 09:31:36 2001 +++ linux/drivers/cdrom/cdu31a.c Mon Dec 3 22:13:01 2001 @@ -157,6 +157,7 @@ #include #include #include +#include #include #include #include @@ -3427,7 +3428,7 @@ request_region(cdu31a_port, 4,"cdu31a"); - if (register_blkdev(MAJOR_NR,"cdu31a",&cdrom_fops)) + if (devfs_register_blkdev(MAJOR_NR,"cdu31a",&cdrom_fops)) { printk("Unable to get major %d for CDU-31a\n", MAJOR_NR); goto errout2; @@ -3510,6 +3511,8 @@ scd_info.mask = deficiency; strncpy(scd_info.name, "cdu31a", sizeof(scd_info.name)); + devfs_register (NULL, "cdu31a", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); if (register_cdrom(&scd_info)) { goto errout0; @@ -3529,7 +3532,9 @@ } errout0: printk("Unable to register CDU-31a with Uniform cdrom driver\n"); - if (unregister_blkdev(MAJOR_NR, "cdu31a")) + devfs_unregister(devfs_find_handle(NULL, "cdu31a", 0, 0, 0, + DEVFS_SPECIAL_BLK,0)); + if (devfs_unregister_blkdev(MAJOR_NR, "cdu31a")) { printk("Can't unregister block device for cdu31a\n"); } @@ -3555,7 +3560,9 @@ printk("Can't unregister cdu31a from Uniform cdrom driver\n"); return; } - if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) + devfs_unregister(devfs_find_handle(NULL, "cdu31a", 0, 0, 0, + DEVFS_SPECIAL_BLK,0)); + if ((devfs_unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) { printk("Can't unregister cdu31a\n"); return; diff -urN linux-2.2.20/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c --- linux-2.2.20/drivers/cdrom/cm206.c Sun Mar 25 09:31:36 2001 +++ linux/drivers/cdrom/cm206.c Mon Dec 3 22:13:01 2001 @@ -182,6 +182,7 @@ #include #include #include +#include #include #include #include @@ -1270,10 +1271,12 @@ printk("Can't unregister cdrom cm206\n"); return; } - if (unregister_blkdev(MAJOR_NR, "cm206")) { + if (devfs_unregister_blkdev(MAJOR_NR, "cm206")) { printk("Can't unregister major cm206\n"); return; } + devfs_unregister(devfs_find_handle(NULL, "cm206cd", 0, 0, 0, + DEVFS_SPECIAL_BLK,0)); case 3: free_irq(cm206_irq, NULL); case 2: @@ -1383,7 +1386,7 @@ return -EIO; } printk(".\n"); - if (register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 0) { + if (devfs_register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 0) { printk(KERN_INFO "Cannot register for major %d!\n", MAJOR_NR); cleanup(3); return -EIO; @@ -1394,6 +1397,8 @@ cleanup(3); return -EIO; } + devfs_register (NULL, "cm206cd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; blksize_size[MAJOR_NR] = cm206_blocksizes; read_ahead[MAJOR_NR] = 16; /* reads ahead what? */ diff -urN linux-2.2.20/drivers/cdrom/gscd.c linux/drivers/cdrom/gscd.c --- linux-2.2.20/drivers/cdrom/gscd.c Sun Mar 25 09:31:36 2001 +++ linux/drivers/cdrom/gscd.c Mon Dec 3 22:13:01 2001 @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -981,12 +982,13 @@ void cleanup_module (void) { - if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL)) + devfs_unregister(devfs_find_handle(NULL, "gscd", 0, 0, 0, DEVFS_SPECIAL_BLK, + 0)); + if ((devfs_unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL)) { printk("What's that: can't unregister GoldStar-module\n" ); return; } - release_region (gscd_port,4); printk(KERN_INFO "GoldStar-module released.\n" ); } @@ -1053,13 +1055,14 @@ i++; } - if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0) + if (devfs_register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0) { printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n", MAJOR_NR); return -EIO; } - + devfs_register (NULL, "gscd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &gscd_fops, NULL); blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; blksize_size[MAJOR_NR] = gscd_blocksizes; read_ahead[MAJOR_NR] = 4; diff -urN linux-2.2.20/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c --- linux-2.2.20/drivers/cdrom/mcd.c Sun Mar 25 09:31:36 2001 +++ linux/drivers/cdrom/mcd.c Mon Dec 3 22:13:01 2001 @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -1137,11 +1138,13 @@ printk(KERN_WARNING "Can't unregister cdrom mcd\n"); return; } + devfs_unregister(devfs_find_handle(NULL, "mcd", 0, 0, 0, DEVFS_SPECIAL_BLK, + 0)); free_irq(mcd_irq, NULL); case 2: release_region(mcd_port,4); case 1: - if (unregister_blkdev(MAJOR_NR, "mcd")) { + if (devfs_unregister_blkdev(MAJOR_NR, "mcd")) { printk(KERN_WARNING "Can't unregister major mcd\n"); return; } @@ -1166,7 +1169,7 @@ return -EIO; } - if (register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0) + if (devfs_register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0) { printk("Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR); @@ -1262,6 +1265,8 @@ mcd_info.dev = MKDEV(MAJOR_NR,0); + devfs_register (NULL, "mcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); if (register_cdrom(&mcd_info) != 0) { printk("Cannot register Mitsumi CD-ROM!\n"); cleanup(3); diff -urN linux-2.2.20/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c --- linux-2.2.20/drivers/cdrom/mcdx.c Sun Mar 25 09:31:36 2001 +++ linux/drivers/cdrom/mcdx.c Mon Dec 3 22:13:01 2001 @@ -70,6 +70,7 @@ #include #define MAJOR_NR MITSUMI_X_CDROM_MAJOR #include +#include /* for compatible parameter passing with "insmod" */ #define mcdx_drive_map mcdx @@ -984,7 +985,9 @@ for (i = 0; i < MCDX_NDRIVES; i++) { struct s_drive_stuff *stuffp; - if (unregister_cdrom(&mcdx_info)) { + devfs_unregister (devfs_find_handle(NULL, "mcdx", 0, 0, 0, + DEVFS_SPECIAL_BLK, 0)); + if (unregister_cdrom(&mcdx_info)) { printk(KERN_WARNING "Can't unregister cdrom mcdx\n"); return; } @@ -1001,7 +1004,7 @@ kfree(stuffp); } - if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) { + if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) { xwarn("cleanup() unregister_blkdev() failed\n"); } #if !MCDX_QUIET @@ -1102,16 +1105,14 @@ kfree(stuffp); return 0; /* next drive */ } - xtrace(INIT, "init() register blkdev\n"); - if (register_blkdev(MAJOR_NR, "mcdx", &cdrom_fops) != 0) { + if (devfs_register_blkdev(MAJOR_NR, "mcdx", &cdrom_fops) != 0) { xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n", MCDX, stuffp->wreg_data, stuffp->irq, MAJOR_NR); kfree(stuffp); return 1; } - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = READ_AHEAD; blksize_size[MAJOR_NR] = mcdx_blocksizes; @@ -1162,10 +1163,13 @@ MCDX_IO_SIZE); free_irq(stuffp->irq, NULL); kfree(stuffp); - if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) + if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) xwarn("cleanup() unregister_blkdev() failed\n"); return 2; } + devfs_register (NULL, "mcdx", DEVFS_FL_DEFAULT, + MAJOR_NR, 0, S_IFBLK | S_IRUGO | S_IWUGO, + &cdrom_fops, NULL); printk(msg); return 0; } diff -urN linux-2.2.20/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c --- linux-2.2.20/drivers/cdrom/optcd.c Sun Mar 25 09:31:36 2001 +++ linux/drivers/cdrom/optcd.c Mon Dec 3 22:13:01 2001 @@ -66,6 +66,8 @@ #include #include #include +#include + #include #define MAJOR_NR OPTICS_CDROM_MAJOR @@ -2059,12 +2061,13 @@ DEBUG((DEBUG_VFS, "exec_cmd COMINITDOUBLE: %02x", -status)); return -EIO; } - if (register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0) + if (devfs_register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0) { printk(KERN_ERR "optcd: unable to get major %d\n", MAJOR_NR); return -EIO; } - + devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &opt_fops, NULL); hardsect_size[MAJOR_NR] = &hsecsize; blksize_size[MAJOR_NR] = &blksize; blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; @@ -2085,7 +2088,9 @@ void cleanup_module(void) { - if (unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) { + devfs_unregister(devfs_find_handle(NULL, "optcd", 0, 0, 0, + DEVFS_SPECIAL_BLK, 0)); + if (devfs_unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) { printk(KERN_ERR "optcd: what's that: can't unregister\n"); return; } diff -urN linux-2.2.20/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c --- linux-2.2.20/drivers/cdrom/sbpcd.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/cdrom/sbpcd.c Mon Dec 3 22:13:01 2001 @@ -332,6 +332,7 @@ #include #include #include +#include #include #include #include @@ -5568,12 +5569,16 @@ * Test for presence of drive and initialize it. * Called once at boot or load time. */ + +static devfs_handle_t devfs_handle = NULL; + #ifdef MODULE int init_module(void) #else __initfunc(int SBPCD_INIT(void)) #endif MODULE { + char nbuff[16]; int i=0, j=0; int addr[2]={1, CDROM_PORT}; int port_index; @@ -5716,7 +5721,7 @@ OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */ #endif SOUND_BASE - if (register_blkdev(MAJOR_NR, major_name, &cdrom_fops) != 0) + if (devfs_register_blkdev(MAJOR_NR, major_name, &cdrom_fops) != 0) { msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR); #ifdef MODULE @@ -5751,7 +5756,7 @@ if (D_S[j].sbp_buf==NULL) { msg(DBG_INF,"data buffer (%d frames) not available.\n",D_S[j].sbp_bufsiz); - if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL)) + if ((devfs_unregister_blkdev(MAJOR_NR, major_name) == -EINVAL)) { printk("Can't unregister %s\n", major_name); } @@ -5782,7 +5787,12 @@ { printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n"); } - + if (!devfs_handle) + devfs_handle = devfs_mk_dir (NULL, "sbp", 0, NULL); + sprintf (nbuff, "c%dt%d", SBPCD_ISSUE - 1, D_S[j].drv_id); + devfs_register (devfs_handle, nbuff, DEVFS_FL_DEFAULT, + MAJOR_NR, j, S_IFBLK | S_IRUGO | S_IWUGO, + &cdrom_fops, NULL); /* * set the block size */ @@ -5812,7 +5822,7 @@ { int j; - if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL)) + if ((devfs_unregister_blkdev(MAJOR_NR, major_name) == -EINVAL)) { msg(DBG_INF, "What's that: can't unregister %s.\n", major_name); return; @@ -5831,6 +5841,7 @@ } vfree(D_S[j].sbpcd_infop); } + devfs_unregister (devfs_handle); msg(DBG_INF, "%s module released.\n", major_name); } #endif MODULE diff -urN linux-2.2.20/drivers/cdrom/sjcd.c linux/drivers/cdrom/sjcd.c --- linux-2.2.20/drivers/cdrom/sjcd.c Sun Mar 25 09:31:37 2001 +++ linux/drivers/cdrom/sjcd.c Mon Dec 3 22:13:01 2001 @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -1470,7 +1471,7 @@ hardsect_size[MAJOR_NR] = &secsize; blksize_size[MAJOR_NR] = &blksize; - if( register_blkdev( MAJOR_NR, "sjcd", &sjcd_fops ) != 0 ){ + if( devfs_register_blkdev( MAJOR_NR, "sjcd", &sjcd_fops ) != 0 ){ printk( "SJCD: Unable to get major %d for Sanyo CD-ROM\n", MAJOR_NR ); return( -EIO ); } @@ -1561,6 +1562,8 @@ } printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base); + devfs_register (NULL, "sjcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &sjcd_fops, NULL); sjcd_present++; return( 0 ); @@ -1569,7 +1572,7 @@ static int sjcd_cleanup(void) { - if( (unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL) ) + if( (devfs_unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL) ) printk( "SJCD: cannot unregister device.\n" ); else release_region( sjcd_base, 4 ); @@ -1586,6 +1589,8 @@ void cleanup_module(void) { + devfs_unregister(devfs_find_handle(NULL, "sjcd", 0, 0, 0, DEVFS_SPECIAL_BLK, + 0)); if ( sjcd_cleanup() ) printk( "SJCD: module: cannot be removed.\n" ); else diff -urN linux-2.2.20/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c --- linux-2.2.20/drivers/cdrom/sonycd535.c Sun Mar 25 09:31:37 2001 +++ linux/drivers/cdrom/sonycd535.c Mon Dec 3 22:13:01 2001 @@ -119,6 +119,7 @@ #include #include #include +#include #define REALLY_SLOW_IO #include @@ -1596,7 +1597,12 @@ printk("IRQ%d, ", tmp_irq); printk("using %d byte buffer\n", sony_buffer_size); - if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) { + devfs_register (NULL, CDU535_HANDLE, + DEVFS_FL_DEFAULT, + MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, + &cdu_fops, NULL); + if (devfs_register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) { printk("Unable to get major %d for %s\n", MAJOR_NR, CDU535_MESSAGE_NAME); return -EIO; @@ -1691,7 +1697,9 @@ kfree_s(sony_buffer, 4 * sony_buffer_sectors); kfree_s(last_sony_subcode, sizeof *last_sony_subcode); kfree_s(sony_toc, sizeof *sony_toc); - if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL) + devfs_unregister(devfs_find_handle(NULL, CDU535_HANDLE, 0, 0, 0, + DEVFS_SPECIAL_BLK, 0)); + if (devfs_unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL) printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n"); else printk(KERN_INFO CDU535_HANDLE " module released\n"); diff -urN linux-2.2.20/drivers/char/Makefile linux/drivers/char/Makefile --- linux-2.2.20/drivers/char/Makefile Fri Nov 2 09:39:06 2001 +++ linux/drivers/char/Makefile Mon Dec 3 22:13:01 2001 @@ -20,8 +20,8 @@ O_TARGET := char.o M_OBJS := -O_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o -OX_OBJS := pty.o misc.o +O_OBJS := n_tty.o tty_ioctl.o mem.o random.o +OX_OBJS := tty_io.o pty.o misc.o obj-y := obj-m := diff -urN linux-2.2.20/drivers/char/console.c linux/drivers/char/console.c --- linux-2.2.20/drivers/char/console.c Sun Mar 25 09:37:31 2001 +++ linux/drivers/char/console.c Mon Dec 3 23:34:09 2001 @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,8 @@ #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) +extern void vcs_make_devfs (unsigned int index, int unregister); + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -2249,13 +2252,18 @@ tty->winsize.ws_row = video_num_lines; tty->winsize.ws_col = video_num_columns; } + if (tty->count == 1) + vcs_make_devfs (currcons, 0); return 0; } static void con_close(struct tty_struct *tty, struct file * filp) { - if (tty->count == 1) - tty->driver_data = 0; + if (!tty) + return; + if (tty->count != 1) return; + vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1); + tty->driver_data = 0; } static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear) @@ -2305,7 +2313,7 @@ memset(&console_driver, 0, sizeof(struct tty_driver)); console_driver.magic = TTY_DRIVER_MAGIC; - console_driver.name = "tty"; + console_driver.name = "vc/"; console_driver.name_base = 1; console_driver.major = TTY_MAJOR; console_driver.minor_start = 1; @@ -2313,6 +2321,11 @@ console_driver.type = TTY_DRIVER_TYPE_CONSOLE; console_driver.init_termios = tty_std_termios; console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; + /* Tell tty_register_driver() to skip consoles because they are + * registered before kmalloc() is ready. We'll patch them in later. + * See comments at console_init(); see also con_init_devfs(). + */ + console_driver.flags |= TTY_DRIVER_NO_DEVFS; console_driver.refcount = &console_refcount; console_driver.table = console_table; console_driver.termios = console_termios; @@ -2473,6 +2486,19 @@ unsigned int mode; get_user(mode, argp); vesa_blank_mode = (mode < 4) ? mode : 0; +} + +/* We can't register the console with devfs during con_init(), because it + * is called before kmalloc() works. This function is called later to + * do the registration. + */ +void __init con_init_devfs (void) +{ + int i; + + for (i = 0; i < console_driver.num; i++) + tty_register_devfs (&console_driver, DEVFS_FL_AOPEN_NOTIFY, + console_driver.minor_start + i); } static void vesa_powerdown(void) diff -urN linux-2.2.20/drivers/char/dsp56k.c linux/drivers/char/dsp56k.c --- linux-2.2.20/drivers/char/dsp56k.c Sun Mar 25 09:31:26 2001 +++ linux/drivers/char/dsp56k.c Mon Dec 3 22:13:01 2001 @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -520,6 +521,9 @@ /****** Init and module functions ******/ +static devfs_handle_t devfs_handle = NULL; + + __initfunc(int dsp56k_init(void)) { if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) { @@ -527,10 +531,14 @@ return -ENODEV; } - if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) { + if(devfs_register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) { printk("DSP56k driver: Unable to register driver\n"); return -ENODEV; } + devfs_handle = devfs_register (NULL, "dsp56k", DEVFS_FL_DEFAULT, + DSP56K_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, + &dsp56k_fops, NULL); dsp56k.in_use = 0; @@ -545,6 +553,7 @@ void cleanup_module(void) { - unregister_chrdev(DSP56K_MAJOR, "dsp56k"); + devfs_unregister_chrdev(DSP56K_MAJOR, "dsp56k"); + devfs_unregister (devfs_handle); } #endif /* MODULE */ diff -urN linux-2.2.20/drivers/char/dtlk.c linux/drivers/char/dtlk.c --- linux-2.2.20/drivers/char/dtlk.c Sun Mar 25 09:31:27 2001 +++ linux/drivers/char/dtlk.c Mon Dec 3 22:13:01 2001 @@ -69,6 +69,7 @@ #include /* for __init */ #include /* for POLLIN, etc. */ #include /* local header file for DoubleTalk values */ +#include #ifdef TRACING #define TRACE_TEXT(str) printk(str); @@ -361,19 +362,25 @@ return 0; } +static devfs_handle_t devfs_handle; + int __init dtlk_init(void) { dtlk_port_lpc = 0; dtlk_port_tts = 0; dtlk_busy = 0; dtlk_timer_active = 0; - dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops); + dtlk_major = devfs_register_chrdev(0, "dtlk", &dtlk_fops); if (dtlk_major == 0) { printk(KERN_ERR "DoubleTalk PC - cannot register device\n"); return 0; } if (dtlk_dev_probe() == 0) printk(", MAJOR %d\n", dtlk_major); + devfs_handle = devfs_register (NULL, "dtlk", DEVFS_FL_DEFAULT, + dtlk_major, DTLK_MINOR, + S_IFCHR | S_IRUSR | S_IWUSR, + &dtlk_fops, NULL); init_timer(&dtlk_timer); dtlk_timer.function = dtlk_timer_tick; @@ -398,7 +405,8 @@ signals... */ dtlk_write_tts(DTLK_CLEAR); - unregister_chrdev(dtlk_major, "dtlk"); + devfs_unregister_chrdev(dtlk_major, "dtlk"); + devfs_unregister(devfs_handle); release_region(dtlk_port_lpc, DTLK_IO_EXTENT); } diff -urN linux-2.2.20/drivers/char/ftape/zftape/zftape-init.c linux/drivers/char/ftape/zftape/zftape-init.c --- linux-2.2.20/drivers/char/ftape/zftape/zftape-init.c Sun Mar 25 09:31:26 2001 +++ linux/drivers/char/ftape/zftape/zftape-init.c Mon Dec 3 22:13:01 2001 @@ -35,6 +35,7 @@ #endif #include #include +#include #include #if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16) @@ -416,6 +417,8 @@ */ __initfunc(int zft_init(void)) { + int i; + char devname[8]; TRACE_FUN(ft_t_flow); #ifdef MODULE @@ -444,7 +447,41 @@ TRACE(ft_t_info, "zft_init @ 0x%p", zft_init); TRACE(ft_t_info, "installing zftape VFS interface for ftape driver ..."); - TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),); + TRACE_CATCH(devfs_register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),); + + for (i = 0; i < 4; i++) { + sprintf (devname, "qft%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + QIC117_TAPE_MAJOR, i, + S_IFCHR | S_IRUSR | S_IWUSR, + &zft_cdev, NULL); + sprintf (devname, "nqft%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + QIC117_TAPE_MAJOR, i + 4, + S_IFCHR | S_IRUSR | S_IWUSR, + &zft_cdev, NULL); + sprintf (devname, "zqft%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + QIC117_TAPE_MAJOR, i + 16, + S_IFCHR | S_IRUSR | S_IWUSR, + &zft_cdev, NULL); + sprintf (devname, "nzqft%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + QIC117_TAPE_MAJOR, i + 20, + S_IFCHR | S_IRUSR | S_IWUSR, + &zft_cdev, NULL); + sprintf (devname, "rawqft%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + QIC117_TAPE_MAJOR, i + 32, + S_IFCHR | S_IRUSR | S_IWUSR, + &zft_cdev, NULL); + sprintf (devname, "nrawqft%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + QIC117_TAPE_MAJOR, i + 36, + S_IFCHR | S_IRUSR | S_IWUSR, + &zft_cdev, NULL); + } + #if LINUX_VERSION_CODE >= KERNEL_VER(1,2,0) # if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) register_symtab(&zft_symbol_table); /* add global zftape symbols */ @@ -489,12 +526,29 @@ */ void cleanup_module(void) { + int i; + char devname[8]; + TRACE_FUN(ft_t_flow); - if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) { + if (devfs_unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) { TRACE(ft_t_warn, "failed"); } else { TRACE(ft_t_info, "successful"); + } + for (i = 0; i < 4; i++) { + sprintf(devname, "qft%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i, DEVFS_SPECIAL_CHR, 0)); + sprintf(devname, "nqft%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 4, DEVFS_SPECIAL_CHR, 0)); + sprintf(devname, "zqft%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 16, DEVFS_SPECIAL_CHR, 0)); + sprintf(devname, "nzqft%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 20, DEVFS_SPECIAL_CHR, 0)); + sprintf(devname, "rawqft%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 32, DEVFS_SPECIAL_CHR, 0)); + sprintf(devname, "nrawqft%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 36, DEVFS_SPECIAL_CHR, 0)); } zft_uninit_mem(); /* release remaining memory, if any */ printk(KERN_INFO "zftape successfully unloaded.\n"); diff -urN linux-2.2.20/drivers/char/ip2main.c linux/drivers/char/ip2main.c --- linux-2.2.20/drivers/char/ip2main.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/char/ip2main.c Mon Dec 3 22:18:37 2001 @@ -929,19 +929,19 @@ if ( NULL != ( pB = i2BoardPtrTable[i] ) ) { sprintf( name, "ipl%d", i ); pB->devfs_ipl_handle = - devfs_register (devfs_handle, name, 0, - DEVFS_FL_NONE, + devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, IP2_IPL_MAJOR, 4 * i, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, - 0, 0, &ip2_ipl, NULL); + &ip2_ipl, NULL); sprintf( name, "stat%d", i ); pB->devfs_stat_handle = - devfs_register (devfs_handle, name, 0, - DEVFS_FL_NONE, + devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, IP2_IPL_MAJOR, 4 * i + 1, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, - 0, 0, &ip2_ipl, NULL); + &ip2_ipl, NULL); for ( box = 0; box < ABS_MAX_BOXES; ++box ) { diff -urN linux-2.2.20/drivers/char/istallion.c linux/drivers/char/istallion.c --- linux-2.2.20/drivers/char/istallion.c Sun Mar 25 09:31:25 2001 +++ linux/drivers/char/istallion.c Mon Dec 3 22:13:01 2001 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -845,7 +846,8 @@ stlibrd_t *brdp; stliport_t *portp; unsigned long flags; - int i, j; + int i, j, k; + char devname[16]; #if DEBUG printk("cleanup_module()\n"); @@ -874,10 +876,13 @@ restore_flags(flags); return; } - if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) + if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) printk("STALLION: failed to un-register serial memory device, " "errno=%d\n", -i); - + for (k = 0; k < 4; k++) { + sprintf(devname, "staliomem%i", k); + devfs_unregister(devfs_find_handle(NULL, devname, 0, STL_SIOMEMMAJOR, k, DEVFS_SPECIAL_CHR, 0)); + } if (stli_tmpwritebuf != (char *) NULL) kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE); if (stli_txcookbuf != (char *) NULL) @@ -5348,6 +5353,9 @@ __initfunc(int stli_init(void)) { + int i; + char devname[16]; + printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); stli_initbrds(); @@ -5368,8 +5376,16 @@ * Set up a character driver for the shared memory region. We need this * to down load the slave code image. Also it is a useful debugging tool. */ - if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem)) + if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem)) printk("STALLION: failed to register serial memory device\n"); + + for (i = 0; i < 4; i++) { + sprintf (devname, "staliomem%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + STL_SIOMEMMAJOR, i, + S_IFCHR | S_IRUSR | S_IWUSR, + &stli_fsiomem, NULL); + } /* * Set up the tty driver structure and register us as a driver. diff -urN linux-2.2.20/drivers/char/joystick/joystick.c linux/drivers/char/joystick/joystick.c --- linux-2.2.20/drivers/char/joystick/joystick.c Sun Mar 25 09:31:27 2001 +++ linux/drivers/char/joystick/joystick.c Mon Dec 3 22:13:01 2001 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -694,6 +695,10 @@ return prev; } +extern struct file_operations js_fops; + +static devfs_handle_t devfs_handle = NULL; + int js_register_device(struct js_port *port, int number, int axes, int buttons, char *name, js_ops_func open, js_ops_func close) { @@ -702,6 +707,7 @@ void *all; int i = 0; unsigned long flags; + char devfs_name[8]; if (!(all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + 2 * (((buttons - 1) >> 5) + 1) * sizeof(int) + @@ -745,6 +751,13 @@ spin_unlock_irqrestore(&js_lock, flags); + sprintf(devfs_name, "js%d", i); + curd->devfs_handle = devfs_register(devfs_handle, devfs_name, + DEVFS_FL_DEFAULT, + JOYSTICK_MAJOR, i, + S_IFCHR | S_IRUGO | S_IWUSR, + &js_fops, NULL); + return i; } @@ -760,6 +773,7 @@ spin_unlock_irqrestore(&js_lock, flags); + devfs_unregister(dev->devfs_handle); kfree(dev); } @@ -788,10 +802,11 @@ #endif { - if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { + if (devfs_register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); return -EBUSY; } + devfs_handle = devfs_mk_dir(NULL, "joysticks", 9, NULL); printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik \n", JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); @@ -871,7 +886,8 @@ void cleanup_module(void) { del_timer(&js_timer); - if (unregister_chrdev(JOYSTICK_MAJOR, "js")) + devfs_unregister(devfs_handle); + if (devfs_unregister_chrdev(JOYSTICK_MAJOR, "js")) printk(KERN_ERR "js: can't unregister device\n"); } #endif diff -urN linux-2.2.20/drivers/char/lp.c linux/drivers/char/lp.c --- linux-2.2.20/drivers/char/lp.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/char/lp.c Mon Dec 3 22:13:01 2001 @@ -16,6 +16,7 @@ * Parport sharing hacking by Andrea Arcangeli * Fixed kernel_(to/from)_user memory copy to check for errors * by Riccardo Facchetti + * 22-JAN-1998 Added support for devfs Richard Gooch * Redesigned interrupt handling for handle printers with buggy handshake * by Andrea Arcangeli, 11 May 1998 * Full efficient handling of printer with buggy irq handshake (now I have @@ -172,6 +173,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +189,8 @@ /* if you have more than 3 printers, remember to increase LP_NO */ #define LP_NO 3 +static devfs_handle_t devfs_handle = NULL; + struct lp_struct lp_table[LP_NO] = { [0 ... LP_NO-1] = {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, @@ -946,10 +950,22 @@ } if (count) { - if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) { + if (devfs_register_chrdev(LP_MAJOR, "lp", &lp_fops)) { printk("lp: unable to get major %d\n", LP_MAJOR); return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "printers", 0, NULL); + for (i = 0; i < LP_NO; ++i) { + char name[8]; + + if (!(lp_table[i].flags & LP_EXIST)) + continue; /*skip this entry: it doesn't exist*/ + sprintf (name, "%d", i); + devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, LP_MAJOR, i, + S_IFCHR | S_IRUGO | S_IWUGO, + &lp_fops, NULL); + } } else { printk(KERN_INFO "lp: no devices found\n"); return -ENODEV; @@ -991,11 +1007,12 @@ { unsigned int offset; - unregister_chrdev(LP_MAJOR, "lp"); + devfs_unregister_chrdev(LP_MAJOR, "lp"); for (offset = 0; offset < LP_NO; offset++) { if (lp_table[offset].dev == NULL) continue; parport_unregister_device(lp_table[offset].dev); } + devfs_unregister (devfs_handle); } #endif diff -urN linux-2.2.20/drivers/char/mem.c linux/drivers/char/mem.c --- linux-2.2.20/drivers/char/mem.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/char/mem.c Mon Dec 3 22:13:01 2001 @@ -2,6 +2,9 @@ * linux/drivers/char/mem.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * Added devfs support. + * Jan-11-1998, C. Scott Ananian */ #include @@ -589,6 +592,32 @@ return 0; } +void __init memory_devfs_register (void) +{ + /* These are never unregistered */ + static const struct { + unsigned short minor; + char *name; + umode_t mode; + struct file_operations *fops; + } list[] = { /* list of minor devices */ + {1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops}, + {2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops}, + {3, "null", S_IRUGO | S_IWUGO, &null_fops}, + {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops}, + {5, "zero", S_IRUGO | S_IWUGO, &zero_fops}, + {7, "full", S_IRUGO | S_IWUGO, &full_fops}, + {8, "random", S_IRUGO | S_IWUSR, &random_fops}, + {9, "urandom", S_IRUGO | S_IWUSR, &urandom_fops} + }; + int i; + + for (i=0; i<(sizeof(list)/sizeof(*list)); i++) + devfs_register (NULL, list[i].name, DEVFS_FL_DEFAULT, + MEM_MAJOR, list[i].minor, list[i].mode | S_IFCHR, + list[i].fops, NULL); +} + static struct file_operations memory_fops = { NULL, /* lseek */ NULL, /* read */ @@ -605,8 +634,9 @@ __initfunc(int chr_dev_init(void)) { - if (register_chrdev(MEM_MAJOR,"mem",&memory_fops)) + if (devfs_register_chrdev(MEM_MAJOR,"mem",&memory_fops)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); + memory_devfs_register(); rand_initialize(); #if defined (CONFIG_FB) fbmem_init(); diff -urN linux-2.2.20/drivers/char/misc.c linux/drivers/char/misc.c --- linux-2.2.20/drivers/char/misc.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/char/misc.c Mon Dec 3 22:13:01 2001 @@ -29,6 +29,8 @@ * * Changes for kmod (from kerneld): Cyrus Durgin + * + * Added devfs support. Richard Gooch 10-Jan-1998 */ #include @@ -41,6 +43,7 @@ #include #include #include +#include #include #include @@ -139,6 +142,7 @@ int misc_register(struct miscdevice * misc) { struct miscdevice *c; + static devfs_handle_t devfs_handle = NULL; if (misc->next || misc->prev) return -EBUSY; @@ -159,6 +163,13 @@ } if (misc->minor < DYNAMIC_MINORS) misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); + if (!devfs_handle) + devfs_handle = devfs_mk_dir (NULL, "misc", 4, NULL); + misc->devfs_handle = + devfs_register (devfs_handle, misc->name, DEVFS_FL_DEFAULT, + MISC_MAJOR, misc->minor, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + misc->fops, NULL); /* * Add it to the front, so that later devices can "override" @@ -180,6 +191,7 @@ misc->next->prev = misc->prev; misc->next = NULL; misc->prev = NULL; + devfs_unregister (misc->devfs_handle); if (i < DYNAMIC_MINORS && i>0) { misc_minors[i>>3] &= ~(1 << (misc->minor & 7)); } @@ -289,11 +301,10 @@ #ifdef CONFIG_TOSHIBA tosh_init(); #endif - if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { + if (devfs_register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); return -EIO; } - return 0; } diff -urN linux-2.2.20/drivers/char/pty.c linux/drivers/char/pty.c --- linux-2.2.20/drivers/char/pty.c Sun Mar 25 09:31:24 2001 +++ linux/drivers/char/pty.c Mon Dec 3 23:41:26 2001 @@ -10,6 +10,7 @@ #include #include /* For EXPORT_SYMBOL */ +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -96,6 +98,7 @@ } } #endif + tty_unregister_devfs (&tty->link->driver, MINOR (tty->device)); tty_vhangup(tty->link); } } @@ -329,6 +332,12 @@ clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); wake_up_interruptible(&pty->open_wait); set_bit(TTY_THROTTLED, &tty->flags); + /* register a slave for the master */ + if (tty->driver.major == PTY_MASTER_MAJOR) + tty_register_devfs(&tty->link->driver, + DEVFS_FL_AUTO_OWNER | DEVFS_FL_WAIT, + tty->link->driver.minor_start + + MINOR(tty->device)-tty->driver.minor_start); retval = 0; out: return retval; @@ -352,7 +361,7 @@ memset(&pty_driver, 0, sizeof(struct tty_driver)); pty_driver.magic = TTY_DRIVER_MAGIC; pty_driver.driver_name = "pty_master"; - pty_driver.name = "pty"; + pty_driver.name = "pty/m"; pty_driver.major = PTY_MASTER_MAJOR; pty_driver.minor_start = 0; pty_driver.num = NR_PTYS; @@ -383,12 +392,16 @@ pty_slave_driver = pty_driver; pty_slave_driver.driver_name = "pty_slave"; pty_slave_driver.proc_entry = 0; - pty_slave_driver.name = "ttyp"; + pty_slave_driver.name = "pty/s"; pty_slave_driver.subtype = PTY_TYPE_SLAVE; pty_slave_driver.major = PTY_SLAVE_MAJOR; pty_slave_driver.minor_start = 0; pty_slave_driver.init_termios = tty_std_termios; pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD; + /* Slave ptys are registered when their corresponding master pty + * is opened, and unregistered when the pair is closed. + */ + pty_slave_driver.flags |= TTY_DRIVER_NO_DEVFS; pty_slave_driver.table = ttyp_table; pty_slave_driver.termios = ttyp_termios; pty_slave_driver.termios_locked = ttyp_termios_locked; @@ -409,6 +422,7 @@ /* Unix98 devices */ #ifdef CONFIG_UNIX98_PTYS + devfs_mk_dir (NULL, "pts", 3, NULL); printk("pty: %d Unix98 ptys configured\n", UNIX98_NR_MAJORS*NR_PTYS); for ( i = 0 ; i < UNIX98_NR_MAJORS ; i++ ) { ptm_driver[i] = pty_driver; @@ -419,13 +433,14 @@ ptm_driver[i].name_base = i*NR_PTYS; ptm_driver[i].num = NR_PTYS; ptm_driver[i].other = &pts_driver[i]; + ptm_driver[i].flags |= TTY_DRIVER_NO_DEVFS; ptm_driver[i].table = ptm_table[i]; ptm_driver[i].termios = ptm_termios[i]; ptm_driver[i].termios_locked = ptm_termios_locked[i]; ptm_driver[i].driver_state = ptm_state[i]; pts_driver[i] = pty_slave_driver; - pts_driver[i].name = "pts"; + pts_driver[i].name = "pts/"; pts_driver[i].proc_entry = 0; pts_driver[i].major = UNIX98_PTY_SLAVE_MAJOR+i; pts_driver[i].minor_start = 0; diff -urN linux-2.2.20/drivers/char/serial.c linux/drivers/char/serial.c --- linux-2.2.20/drivers/char/serial.c Sun Mar 25 09:37:31 2001 +++ linux/drivers/char/serial.c Mon Dec 3 23:42:17 2001 @@ -3134,7 +3134,7 @@ memset(&serial_driver, 0, sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; serial_driver.driver_name = "serial"; - serial_driver.name = "ttyS"; + serial_driver.name = "tts/"; serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; serial_driver.num = NR_PORTS; @@ -3143,7 +3143,7 @@ serial_driver.init_termios = tty_std_termios; serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; serial_driver.refcount = &serial_refcount; serial_driver.table = serial_table; serial_driver.termios = serial_termios; @@ -3174,7 +3174,7 @@ * major number and the subtype code. */ callout_driver = serial_driver; - callout_driver.name = "cua"; + callout_driver.name = "cua/"; callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; callout_driver.read_proc = 0; @@ -3232,6 +3232,10 @@ (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); } return 0; } @@ -3287,6 +3291,10 @@ printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n", state->line + SERIAL_DEV_OFFSET, state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); return state->line + SERIAL_DEV_OFFSET; } @@ -3301,6 +3309,13 @@ tty_hangup(state->info->tty); state->type = PORT_UNKNOWN; printk(KERN_INFO "tty%02d unloaded\n", state->line + SERIAL_DEV_OFFSET); + /* These will be hidden, because they are devices that will no longer + * be available to the system. (ie, PCMCIA modems, once ejected) + */ + tty_unregister_devfs(&serial_driver, + serial_driver.minor_start + state->line); + tty_unregister_devfs(&callout_driver, + callout_driver.minor_start + state->line); restore_flags(flags); } diff -urN linux-2.2.20/drivers/char/stallion.c linux/drivers/char/stallion.c --- linux-2.2.20/drivers/char/stallion.c Sun Mar 25 09:31:25 2001 +++ linux/drivers/char/stallion.c Mon Dec 3 22:13:01 2001 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -797,7 +798,8 @@ stlpanel_t *panelp; stlport_t *portp; unsigned long flags; - int i, j, k; + int i, j, k, l; + char devname[16]; #if DEBUG printk("cleanup_module()\n"); @@ -823,9 +825,13 @@ restore_flags(flags); return; } - if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) + if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) printk("STALLION: failed to un-register serial memory device, " "errno=%d\n", -i); + for (l = 0; l < 4; l++) { + sprintf(devname, "staliomem%i", l); + devfs_unregister(devfs_find_handle(NULL, devname, 0, STL_SIOMEMMAJOR, l, DEVFS_SPECIAL_CHR, 0)); + } if (stl_tmpwritebuf != (char *) NULL) kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE); @@ -3250,6 +3256,9 @@ __initfunc(int stl_init(void)) { + int i; + char devname[16]; + printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); stl_initbrds(); @@ -3266,9 +3275,15 @@ * Set up a character driver for per board stuff. This is mainly used * to do stats ioctls on the ports. */ - if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) + if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) printk("STALLION: failed to register serial board device\n"); - + for (i = 0; i < 4; i++) { + sprintf (devname, "staliomem%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + STL_SIOMEMMAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, + &stl_fsiomem, NULL); + } /* * Set up the tty driver structure and register us as a driver. * Also setup the callout tty device. diff -urN linux-2.2.20/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c --- linux-2.2.20/drivers/char/tpqic02.c Sun Mar 25 09:31:25 2001 +++ linux/drivers/char/tpqic02.c Mon Dec 3 22:13:01 2001 @@ -89,7 +89,8 @@ #include #include #include - +#include + #include #include #include @@ -2927,7 +2928,7 @@ #endif printk(TPQIC02_NAME ": DMA buffers: %u blocks\n", NR_BLK_BUF); /* If we got this far, install driver functions */ - if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) + if (devfs_register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) { printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR); #ifndef CONFIG_QIC02_DYNCONF @@ -2935,6 +2936,38 @@ #endif return -ENODEV; } + devfs_register (NULL, "ntpqic11", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 2, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic11", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 3, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); + devfs_register (NULL, "ntpqic24", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 4, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic24", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 5, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); + devfs_register (NULL, "ntpqic120", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 6, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic120", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 7, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); + devfs_register (NULL, "ntpqic150", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 8, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic150", DEVFS_FL_DEFAULT, + QIC02_TAPE_MAJOR, 9, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + &qic02_tape_fops, NULL); /* prepare timer */ TIMEROFF; @@ -2981,7 +3014,15 @@ { qic02_release_resources(); } - unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); + devfs_unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); + devfs_unregister(devfs_find_handle(NULL, "ntpqic11", 0, QIC02_TAPE_MAJOR, 2, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic11", 0, QIC02_TAPE_MAJOR, 3, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "ntpqic24", 0, QIC02_TAPE_MAJOR, 4, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic24", 0, QIC02_TAPE_MAJOR, 5, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "ntpqic120", 0, QIC02_TAPE_MAJOR, 6, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic120", 0, QIC02_TAPE_MAJOR, 7, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "ntpqic150", 0, QIC02_TAPE_MAJOR, 8, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic150", 0, QIC02_TAPE_MAJOR, 9, DEVFS_SPECIAL_CHR, 0)); } int init_module(void) diff -urN linux-2.2.20/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- linux-2.2.20/drivers/char/tty_io.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/char/tty_io.c Mon Dec 3 23:38:00 2001 @@ -52,6 +52,9 @@ * Rewrote init_dev and release_dev to eliminate races. * -- Bill Hawes , June 97 * + * Added devfs support. + * -- C. Scott Ananian , 13-Jan-1998 + * * Added support for a Unix98-style ptmx device. * -- C. Scott Ananian , 14-Jan-1998 */ @@ -79,6 +82,7 @@ #include #include #include +#include #include #include @@ -88,9 +92,14 @@ #include #include #include +#include #include +#ifdef CONFIG_VT +extern void con_init_devfs (void); +#endif + #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) #define TTY_DEV MKDEV(TTYAUX_MAJOR,0) #define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1) @@ -107,6 +116,7 @@ #ifdef CONFIG_UNIX98_PTYS extern struct tty_driver ptm_driver[]; /* Unix98 pty masters; for /dev/ptmx */ +extern struct tty_driver pts_driver[]; /* Unix98 pty slaves; for /dev/ptmx */ #endif /* @@ -148,16 +158,28 @@ /* * This routine returns the name of tty. */ +static char * +_tty_make_name(struct tty_struct *tty, const char *name, char *buf) +{ + int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0; + + if (!tty) /* Hmm. NULL pointer. That's fun. */ + strcpy(buf, "NULL tty"); + else if (tty->driver.num == 1) /* Don't append a number */ + strcpy(buf, name); + else + sprintf(buf, "%s%d", name, + idx + tty->driver.name_base); + + return buf; +} + #define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \ (tty)->driver.name_base) - + char *tty_name(struct tty_struct *tty, char *buf) { - if (tty) - sprintf(buf, "%s%d", tty->driver.name, TTY_NUMBER(tty)); - else - strcpy(buf, "NULL tty"); - return buf; + return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf); } inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device, @@ -1315,6 +1337,8 @@ set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ minor -= driver->minor_start; devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start)); + tty_register_devfs(&pts_driver[major], DEVFS_FL_NO_PERSISTENCE, + pts_driver[major].minor_start + minor); noctty = 1; goto init_dev_done; @@ -1975,16 +1999,76 @@ } /* + * Register a tty device described by , with minor number . + */ +void tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned int minor) +{ +#ifdef CONFIG_DEVFS_FS + umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR; + struct tty_struct tty; + char buf[32]; + + tty.driver = *driver; + tty.device = MKDEV (driver->major, minor); + switch (tty.device) { + case TTY_DEV: + case PTMX_DEV: + mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + break; + default: + if (driver->major == PTY_MASTER_MAJOR) + flags |= DEVFS_FL_AUTO_OWNER; + break; + } + if ((minor < driver->minor_start) || + (minor >= driver->minor_start + driver->num)) { + printk(KERN_ERR "Attempt to register invalid minor number " + "with devfs (%d:%d).\n", (int)driver->major,(int)minor); + return; + } +# ifdef CONFIG_UNIX98_PTYS + if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) && + (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) + flags |= DEVFS_FL_CURRENT_OWNER; +# endif + devfs_register (NULL, tty_name (&tty, buf), flags | DEVFS_FL_DEFAULT, + driver->major, minor, mode, &tty_fops, NULL); +#endif /* CONFIG_DEVFS_FS */ +} + +void tty_unregister_devfs (struct tty_driver *driver, unsigned minor) +{ +#ifdef CONFIG_DEVFS_FS + void * handle; + struct tty_struct tty; + char buf[32]; + + tty.driver = *driver; + tty.device = MKDEV(driver->major, minor); + + handle = devfs_find_handle (NULL, tty_name (&tty, buf), 0, + driver->major, minor, + DEVFS_SPECIAL_CHR, 0); + devfs_unregister (handle); +#endif /* CONFIG_DEVFS_FS */ +} + +EXPORT_SYMBOL(tty_register_devfs); +EXPORT_SYMBOL(tty_unregister_devfs); + +/* * Called by a tty driver to register itself. */ int tty_register_driver(struct tty_driver *driver) { int error; + int i; if (driver->flags & TTY_DRIVER_INSTALLED) return 0; - error = register_chrdev(driver->major, driver->name, &tty_fops); + error = devfs_register_chrdev(driver->major, driver->name, &tty_fops); if (error < 0) return error; else if(driver->major == 0) @@ -1998,6 +2082,10 @@ if (tty_drivers) tty_drivers->prev = driver; tty_drivers = driver; + if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) { + for(i = 0; i < driver->num; i++) + tty_register_devfs(driver, 0, driver->minor_start + i); + } proc_tty_register_driver(driver); return error; } @@ -2027,11 +2115,11 @@ return -ENOENT; if (othername == NULL) { - retval = unregister_chrdev(driver->major, driver->name); + retval = devfs_unregister_chrdev(driver->major, driver->name); if (retval) return retval; } else - register_chrdev(driver->major, othername, &tty_fops); + devfs_register_chrdev(driver->major, othername, &tty_fops); if (driver->prev) driver->prev->next = driver->next; @@ -2057,6 +2145,7 @@ driver->termios_locked[i] = NULL; kfree_s(tp, sizeof(struct termios)); } + tty_unregister_devfs(driver, driver->minor_start + i); } proc_tty_unregister_driver(driver); return 0; @@ -2160,6 +2249,13 @@ if (tty_register_driver(&dev_syscons_driver)) panic("Couldn't register /dev/console driver\n"); + /* console calls tty_register_driver() before kmalloc() works. + * Thus, we can't devfs_register() then. Do so now, instead. + */ +#ifdef CONFIG_VT + con_init_devfs(); +#endif + #ifdef CONFIG_UNIX98_PTYS dev_ptmx_driver = dev_tty_driver; dev_ptmx_driver.driver_name = "/dev/ptmx"; @@ -2175,7 +2271,7 @@ #ifdef CONFIG_VT dev_console_driver = dev_tty_driver; - dev_console_driver.driver_name = "/dev/tty0"; + dev_console_driver.driver_name = "/dev/vc/0"; dev_console_driver.name = dev_console_driver.driver_name + 5; dev_console_driver.major = TTY_MAJOR; dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM; diff -urN linux-2.2.20/drivers/char/vc_screen.c linux/drivers/char/vc_screen.c --- linux-2.2.20/drivers/char/vc_screen.c Sun Mar 25 09:31:25 2001 +++ linux/drivers/char/vc_screen.c Mon Dec 3 22:13:01 2001 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -310,12 +311,46 @@ NULL /* fsync */ }; +static devfs_handle_t devfs_handle = NULL; + +void vcs_make_devfs (unsigned int index, int unregister) +{ +#ifdef CONFIG_DEVFS_FS + char name[8]; + + sprintf (name, "a%u", index + 1); + if (unregister) + { + devfs_unregister ( devfs_find_handle (devfs_handle, name + 1, 0, 0, 0, + DEVFS_SPECIAL_CHR, 0) ); + devfs_unregister ( devfs_find_handle (devfs_handle, name, 0, 0, 0, + DEVFS_SPECIAL_CHR, 0) ); + } + else + { + devfs_register (devfs_handle, name + 1, DEVFS_FL_DEFAULT, + VCS_MAJOR, index + 1, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, + VCS_MAJOR, index + 129, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); + } +#endif /* CONFIG_DEVFS_FS */ +} + __initfunc(int vcs_init(void)) { int error; - error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops); + error = devfs_register_chrdev(VCS_MAJOR, "vcs", &vcs_fops); if (error) printk("unable to get major %d for vcs device", VCS_MAJOR); + devfs_handle = devfs_mk_dir (NULL, "vcc", 3, NULL); + devfs_register (devfs_handle, "0", DEVFS_FL_DEFAULT, + VCS_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); + devfs_register (devfs_handle, "a", DEVFS_FL_DEFAULT, + VCS_MAJOR, 128, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); return error; } diff -urN linux-2.2.20/drivers/char/videodev.c linux/drivers/char/videodev.c --- linux-2.2.20/drivers/char/videodev.c Sun Mar 25 09:31:26 2001 +++ linux/drivers/char/videodev.c Mon Dec 3 22:13:01 2001 @@ -331,6 +331,8 @@ return -EINVAL; } +extern struct file_operations video_fops; + /* * Video For Linux device drivers request registration here. */ @@ -341,24 +343,29 @@ int base; int err; int end; + char *name_base; switch(type) { case VFL_TYPE_GRABBER: base=0; end=64; + name_base = "video"; break; case VFL_TYPE_VTX: base=192; end=224; + name_base = "vtx"; break; case VFL_TYPE_VBI: base=224; end=240; + name_base = "vbi"; break; case VFL_TYPE_RADIO: base=64; end=128; + name_base = "radio"; break; default: return -1; @@ -368,6 +375,8 @@ { if(video_device[i]==NULL) { + char name[16]; + video_device[i]=vfd; vfd->minor=i; /* The init call may sleep so we book the slot out @@ -383,6 +392,12 @@ return err; } } + sprintf (name, "v4l/%s%d", name_base, i - base); + vfd->devfs_handle = + devfs_register (NULL, name, DEVFS_FL_DEFAULT, + VIDEO_MAJOR, vfd->minor, + S_IFCHR | S_IRUGO | S_IWUGO, + &video_fops, NULL); return 0; } } @@ -397,6 +412,7 @@ { if(video_device[vfd->minor]!=vfd) panic("vfd: bad unregister"); + devfs_unregister (vfd->devfs_handle); video_device[vfd->minor]=NULL; MOD_DEC_USE_COUNT; } @@ -431,7 +447,7 @@ struct video_init *vfli = video_init_list; printk(KERN_INFO "Linux video capture interface: v1.00\n"); - if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops)) + if(devfs_register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops)) { printk("video_dev: unable to get major %d\n", VIDEO_MAJOR); return -EIO; @@ -457,7 +473,7 @@ void cleanup_module(void) { - unregister_chrdev(VIDEO_MAJOR, "video_capture"); + devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture"); } diff -urN linux-2.2.20/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c --- linux-2.2.20/drivers/isdn/avmb1/capi.c Fri Nov 2 09:39:06 2001 +++ linux/drivers/isdn/avmb1/capi.c Mon Dec 3 22:13:01 2001 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "capiutil.h" #include "capicmd.h" @@ -1814,6 +1815,8 @@ static int __init capi_init(void) { + int i; + char devname[16]; char *p; char *compileinfo; @@ -1827,7 +1830,7 @@ } else strcpy(rev, "1.0"); - if (register_chrdev(capi_major, "capi20", &capi_fops)) { + if (devfs_register_chrdev(capi_major, "capi20", &capi_fops)) { printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); MOD_DEC_USE_COUNT; return -EIO; @@ -1846,7 +1849,7 @@ if ((capifuncs = attach_capi_interface(&cuser)) == 0) { MOD_DEC_USE_COUNT; - unregister_chrdev(capi_major, "capi20"); + devfs_unregister_chrdev(capi_major, "capi20"); #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE unregister_chrdev(capi_rawmajor, "capi/r%d"); #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ @@ -1856,13 +1859,27 @@ #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE if (capinc_tty_init() < 0) { (void) detach_capi_interface(&cuser); - unregister_chrdev(capi_major, "capi20"); + devfs_unregister_chrdev(capi_major, "capi20"); unregister_chrdev(capi_rawmajor, "capi/r%d"); MOD_DEC_USE_COUNT; return -ENOMEM; } #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + devfs_register (NULL, "capi20", DEVFS_FL_DEFAULT, + capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, + &capi_fops, NULL); + for (i = 0; i < 10; i++) { + sprintf (devname, "capi20.0%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + capi_major, i + 1, S_IFCHR | S_IRUSR | S_IWUSR, + &capi_fops, NULL); + sprintf (devname, "capi20.1%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + capi_major, i + 11, + S_IFCHR | S_IRUSR | S_IWUSR, + &capi_fops, NULL); + } (void)proc_init(); @@ -1884,11 +1901,21 @@ static void capi_exit(void) { + int i; + char devname[16]; + #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE #endif - (void)proc_exit(); + devfs_unregister(devfs_find_handle(NULL, "capi20", 0, capi_major, 0, DEVFS_SPECIAL_CHR, 0)); + for (i = 0; i < 10; i++) { + sprintf (devname, "capi20.0%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 1, DEVFS_SPECIAL_CHR, 0)); + sprintf (devname, "capi20,1%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 11, DEVFS_SPECIAL_CHR, 0)); + } - unregister_chrdev(capi_major, "capi20"); + (void)proc_exit(); + devfs_unregister_chrdev(capi_major, "capi20"); #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE capinc_tty_exit(); diff -urN linux-2.2.20/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- linux-2.2.20/drivers/isdn/isdn_common.c Fri Nov 2 09:39:07 2001 +++ linux/drivers/isdn/isdn_common.c Mon Dec 3 22:13:01 2001 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,8 @@ static int isdn_writebuf_stub(int, int, const u_char *, int, int); static void set_global_features(void); static int isdn_wildmat(char *s, char *p); +static void isdn_register_devfs(int); +static void isdn_unregister_devfs(int); void isdn_lock_drivers(void) @@ -715,6 +718,7 @@ dev->drvmap[i] = -1; dev->chanmap[i] = -1; dev->usage[i] &= ~ISDN_USAGE_DISABLED; + isdn_unregister_devfs(i); } dev->drivers--; dev->channels -= dev->drv[di]->channels; @@ -2056,6 +2060,7 @@ if (dev->chanmap[k] < 0) { dev->chanmap[k] = j; dev->drvmap[k] = drvidx; + isdn_register_devfs(k); break; } restore_flags(flags); @@ -2195,6 +2200,96 @@ return 1; } +#ifdef CONFIG_DEVFS_FS + +static devfs_handle_t devfs_handle = NULL; + +static void isdn_register_devfs(int k) +{ + char buf[11]; + + sprintf (buf, "isdn%d", k); + dev->devfs_handle_isdnX[k] = + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_B + k, 0600 | S_IFCHR, + &isdn_fops, NULL); + sprintf (buf, "isdnctrl%d", k); + dev->devfs_handle_isdnctrlX[k] = + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR, + &isdn_fops, NULL); +} + +static void isdn_unregister_devfs(int k) +{ + devfs_unregister (dev->devfs_handle_isdnX[k]); + devfs_unregister (dev->devfs_handle_isdnctrlX[k]); +} + +static void isdn_init_devfs(void) +{ +# ifdef CONFIG_ISDN_PPP + int i; +# endif + + if (!devfs_handle) devfs_handle = devfs_mk_dir (NULL, "isdn", 4, NULL); +# ifdef CONFIG_ISDN_PPP + for (i = 0; i < ISDN_MAX_CHANNELS; i++) { + char buf[8]; + + sprintf (buf, "ippp%d", i); + dev->devfs_handle_ipppX[i] = + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_PPP + i, + 0600 | S_IFCHR, &isdn_fops, NULL); + } +# endif + + dev->devfs_handle_isdninfo = + devfs_register (devfs_handle, "isdninfo", DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR, + &isdn_fops, NULL); + dev->devfs_handle_isdnctrl = + devfs_register (devfs_handle, "isdnctrl", DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, + &isdn_fops, NULL); +} + +static void isdn_cleanup_devfs(void) +{ +# ifdef CONFIG_ISDN_PPP + int i; + for (i = 0; i < ISDN_MAX_CHANNELS; i++) + devfs_unregister (dev->devfs_handle_ipppX[i]); +# endif + devfs_unregister (dev->devfs_handle_isdninfo); + devfs_unregister (dev->devfs_handle_isdnctrl); + devfs_unregister (devfs_handle); +} + +#else /* CONFIG_DEVFS_FS */ +static void isdn_register_devfs(int dummy) +{ + return; +} + +static void isdn_unregister_devfs(int dummy) +{ + return; +} + +static void isdn_init_devfs(void) +{ + return; +} + +static void isdn_cleanup_devfs(void) +{ + return; +} + +#endif /* CONFIG_DEVFS_FS */ + /* ***************************************************************************** * And now the modules code. @@ -2242,11 +2337,12 @@ init_waitqueue_head(&dev->mdm.info[i].open_wait); init_waitqueue_head(&dev->mdm.info[i].close_wait); } - if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { + if (devfs_register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { printk(KERN_WARNING "isdn: Could not register control devices\n"); vfree(dev); return -EIO; } + isdn_init_devfs(); if ((i = isdn_tty_modem_init()) < 0) { printk(KERN_WARNING "isdn: Could not register tty devices\n"); if (i == -3) @@ -2254,7 +2350,8 @@ if (i <= -2) tty_unregister_driver(&dev->mdm.tty_modem); vfree(dev); - unregister_chrdev(ISDN_MAJOR, "isdn"); + isdn_cleanup_devfs(); + devfs_unregister_chrdev(ISDN_MAJOR, "isdn"); return -EIO; } #ifdef CONFIG_ISDN_PPP @@ -2264,7 +2361,8 @@ tty_unregister_driver(&dev->mdm.cua_modem); for (i = 0; i < ISDN_MAX_CHANNELS; i++) kfree(dev->mdm.info[i].xmit_buf - 4); - unregister_chrdev(ISDN_MAJOR, "isdn"); + isdn_cleanup_devfs(); + devfs_unregister_chrdev(ISDN_MAJOR, "isdn"); vfree(dev); return -EIO; } @@ -2327,10 +2425,11 @@ kfree(dev->mdm.info[i].fax); #endif } - if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { + if (devfs_unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); restore_flags(flags); } else { + isdn_cleanup_devfs(); del_timer(&dev->timer); restore_flags(flags); /* call vfree with interrupts enabled, else it will hang */ diff -urN linux-2.2.20/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c --- linux-2.2.20/drivers/macintosh/adb.c Sun Mar 25 09:31:41 2001 +++ linux/drivers/macintosh/adb.c Mon Dec 3 22:13:01 2001 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -605,8 +606,12 @@ { if (adb_controller == NULL) return 0; - if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) + if (devfs_register_chrdev(ADB_MAJOR, "adb", &adb_fops)) printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR); + else + devfs_register (NULL, "adb", DEVFS_FL_DEFAULT, + ADB_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, + &adb_fops, NULL); return 0; } diff -urN linux-2.2.20/drivers/net/cosa.c linux/drivers/net/cosa.c --- linux-2.2.20/drivers/net/cosa.c Sun Mar 25 09:37:34 2001 +++ linux/drivers/net/cosa.c Mon Dec 3 22:13:01 2001 @@ -84,6 +84,7 @@ #include #include #include +#include #include #include #include @@ -363,18 +364,20 @@ #endif { int i; + char devname[8]; + printk(KERN_INFO "cosa v1.06 (c) 1997-8 Jan Kasprzak \n"); #ifdef __SMP__ printk(KERN_INFO "cosa: SMP found. Please mail any success/failure reports to the author.\n"); #endif if (cosa_major > 0) { - if (register_chrdev(cosa_major, "cosa", &cosa_fops)) { + if (devfs_register_chrdev(cosa_major, "cosa", &cosa_fops)) { printk(KERN_WARNING "cosa: unable to get major %d\n", cosa_major); return -EIO; } } else { - if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) { + if (!(cosa_major=devfs_register_chrdev(0, "cosa", &cosa_fops))) { printk(KERN_WARNING "cosa: unable to register chardev\n"); return -EIO; } @@ -383,9 +386,15 @@ cosa_cards[i].num = -1; for (i=0; io[i] != 0 && i < MAX_CARDS; i++) cosa_probe(io[i], irq[i], dma[i]); + for (i = 0; i < nr_cards; i++) { + sprintf (devname, "cosa%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + cosa_major, i, S_IFCHR | S_IRUSR | S_IWUSR, + &cosa_fops, NULL); + } if (!nr_cards) { printk(KERN_WARNING "cosa: no devices found.\n"); - unregister_chrdev(cosa_major, "cosa"); + devfs_unregister_chrdev(cosa_major, "cosa"); return -ENODEV; } return 0; @@ -394,6 +403,8 @@ #ifdef MODULE void cleanup_module (void) { + int i; + char devname[8]; struct cosa_data *cosa; printk(KERN_INFO "Unloading the cosa module\n"); @@ -410,8 +421,10 @@ free_irq(cosa->irq, cosa); free_dma(cosa->dma); release_region(cosa->datareg,is_8bit(cosa)?2:4); + sprintf(devname, "cosa%i", nr_cards); + devfs_unregister((devfs_find_handle(NULL, devname, 0, cosa_major, nr_cards, DEVFS_SPECIAL_CHR, 0))); } - unregister_chrdev(cosa_major, "cosa"); + devfs_unregister_chrdev(cosa_major, "cosa"); } #endif diff -urN linux-2.2.20/drivers/sbus/audio/audio.c linux/drivers/sbus/audio/audio.c --- linux-2.2.20/drivers/sbus/audio/audio.c Sun Mar 25 09:31:39 2001 +++ linux/drivers/sbus/audio/audio.c Mon Dec 3 22:13:02 2001 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -2120,9 +2121,8 @@ #endif /* Register our character device driver with the VFS. */ - if (register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops)) + if (devfs_register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops)) return -EIO; - #ifdef CONFIG_SPARCAUDIO_AMD7930 amd7930_init(); @@ -2143,7 +2143,7 @@ #ifdef MODULE void cleanup_module(void) { - unregister_chrdev(SOUND_MAJOR, "sparcaudio"); + devfs_unregister_chrdev(SOUND_MAJOR, "sparcaudio"); } #endif diff -urN linux-2.2.20/drivers/sbus/char/bpp.c linux/drivers/sbus/char/bpp.c --- linux-2.2.20/drivers/sbus/char/bpp.c Sun Mar 25 09:31:39 2001 +++ linux/drivers/sbus/char/bpp.c Mon Dec 3 22:13:02 2001 @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -1056,18 +1057,23 @@ { int rc; unsigned idx; + char devname[8]; rc = collectLptPorts(); if (rc == 0) return -ENODEV; - rc = register_chrdev(BPP_MAJOR, dev_name, &bpp_fops); + rc = devfs_register_chrdev(BPP_MAJOR, dev_name, &bpp_fops); if (rc < 0) return rc; for (idx = 0; idx < BPP_NO; idx += 1) { instances[idx].opened = 0; probeLptPort(idx); + sprintf(devname, "%s%i", dev_name, idx); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + BPP_MAJOR, idx, S_IFCHR | S_IRUSR | S_IWUSR, + &bpp_fops, NULL); } return 0; @@ -1077,12 +1083,15 @@ void cleanup_module(void) { unsigned idx; + char devname[8]; - unregister_chrdev(BPP_MAJOR, dev_name); + devfs_unregister_chrdev(BPP_MAJOR, dev_name); for (idx = 0 ; idx < BPP_NO ; idx += 1) { if (instances[idx].present) freeLptPort(idx); + sprintf(devname, "%s%i", dev_name, idx); + devfs_unregister(devfs_find_handle(NULL, devname, 0, BPP_MAJOR, ipx, DEVFS_SPECIAL_CHR, 0)); } } #endif diff -urN linux-2.2.20/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c --- linux-2.2.20/drivers/sbus/char/sunkbd.c Sun Mar 25 09:31:38 2001 +++ linux/drivers/sbus/char/sunkbd.c Mon Dec 3 22:13:02 2001 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1558,7 +1559,11 @@ send_cmd(SKBDCMD_SETLED); send_cmd(0x0); /* All off */ /* Register the /dev/kbd interface */ - if (register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){ + devfs_register (NULL, "kbd", DEVFS_FL_DEFAULT, + KBD_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, + &kbd_fops, NULL); + if (devfs_register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){ printk ("Could not register /dev/kbd device\n"); return; } diff -urN linux-2.2.20/drivers/sbus/char/vfc_dev.c linux/drivers/sbus/char/vfc_dev.c --- linux-2.2.20/drivers/sbus/char/vfc_dev.c Sun Mar 25 09:31:39 2001 +++ linux/drivers/sbus/char/vfc_dev.c Mon Dec 3 22:13:02 2001 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -609,8 +610,9 @@ { struct linux_sbus *bus; struct linux_sbus_device *sdev = NULL; - int ret; + int ret, i; int instance=0,cards=0; + char devname[8]; for_all_sbusdev(sdev,bus) { if (strcmp(sdev->prom_name,"vfc") == 0) { @@ -630,12 +632,18 @@ memset(vfc_dev_lst,0,sizeof(struct vfc_dev *)*(cards+1)); vfc_dev_lst[cards]=NULL; - ret=register_chrdev(VFC_MAJOR,vfcstr,&vfc_fops); + ret=devfs_register_chrdev(VFC_MAJOR,vfcstr,&vfc_fops); if(ret) { printk(KERN_ERR "Unable to get major number %d\n",VFC_MAJOR); kfree(vfc_dev_lst); return -EIO; } + for (i = 0; i < VFC_SAA9051_NR; i++) { + sprintf(devname, "%s%i", vfcstr, i); + devfs_register (NULL, vfcstr, DEVFS_FL_DEFAULT, + VFC_MAJOR, i, S_IFCHR | S_IRUSR | S_IWUSR, + &vfc_fops, NULL); + } instance=0; for_all_sbusdev(sdev,bus) { @@ -680,7 +688,14 @@ void cleanup_module(void) { struct vfc_dev **devp; - unregister_chrdev(VFC_MAJOR,vfcstr); + int i; + char devname[8]; + + devfs_unregister_chrdev(VFC_MAJOR,vfcstr); + for (i = 0; i < VFC_SAA9051_NR; i++) { + sprintf(devname, "%s%i", vfcstr, VFC_SAA9051_NR); + devfs_unregister(devfs_find_handle(NULL, devname, 0, VFC_MAJOR, i, DEVFS_SPECIAL_CHR, 0)); + } for(devp=vfc_dev_lst;*devp;devp++) { deinit_vfc_device(*devp); } diff -urN linux-2.2.20/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- linux-2.2.20/drivers/scsi/hosts.c Sun Mar 25 09:37:36 2001 +++ linux/drivers/scsi/hosts.c Mon Dec 3 22:13:02 2001 @@ -661,6 +661,7 @@ * MAX_SCSI_HOSTS here. */ +Scsi_Host_Name * scsi_host_no_list = NULL; struct Scsi_Host * scsi_hostlist = NULL; struct Scsi_Device_Template * scsi_devicelist = NULL; @@ -670,7 +671,8 @@ void scsi_unregister(struct Scsi_Host * sh){ struct Scsi_Host * shpnt; - + Scsi_Host_Name *shn; + if(scsi_hostlist == sh) scsi_hostlist = sh->next; else { @@ -678,6 +680,16 @@ while(shpnt->next != sh) shpnt = shpnt->next; shpnt->next = shpnt->next->next; } + + /* + * We have to unregister the host from the scsi_host_no_list as well. + * Decide by the host_no not by the name because most host drivers are + * able to handle more than one adapters from the same kind (or family). + */ + for ( shn=scsi_host_no_list; shn && (sh->host_no != shn->host_no); + shn=shn->next); + if (shn) shn->host_registered = 0; + /* else {} : This should not happen, we should panic here... */ /* If we are removing the last host registered, it is safe to reuse * its host number (this avoids "holes" at boot time) (DB) @@ -704,8 +716,21 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){ struct Scsi_Host * retval, *shpnt; + Scsi_Host_Name *shn, *shn2; + int new = 1; retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j, (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC); + + /* trying to find a reserved entry (host_no) */ + for (shn = scsi_host_no_list;shn;shn = shn->next) + if (!(shn->host_registered) && shn->loaded_as_module && tpnt->proc_dir && + tpnt->proc_dir->name && !strncmp(tpnt->proc_dir->name, shn->name, strlen(tpnt->proc_dir->name))) { + new = 0; + retval->host_no = shn->host_no; + shn->host_registered = 1; + shn->loaded_as_module = scsi_loadable_module_flag; + break; + } atomic_set(&retval->host_active,0); retval->host_busy = 0; retval->host_failed = 0; @@ -714,7 +739,28 @@ if(j > 0xffff) panic("Too many extra bytes requested\n"); retval->extra_bytes = j; retval->loaded_as_module = scsi_loadable_module_flag; - retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */ + if (new) { + int len = 0; + shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC); + if (tpnt->proc_dir) + len = strlen(tpnt->proc_dir->name); + shn->name = kmalloc(len+1, GFP_ATOMIC); + if (tpnt->proc_dir) + strncpy(shn->name, tpnt->proc_dir->name, len); + shn->name[len] = 0; + shn->host_no = max_scsi_hosts++; + shn->host_registered = 1; + shn->loaded_as_module = scsi_loadable_module_flag; + shn->next = NULL; + if (scsi_host_no_list) { + for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next) + ; + shn2->next = shn; + } + else + scsi_host_no_list = shn; + retval->host_no = shn->host_no; + } next_scsi_host++; retval->host_queue = NULL; retval->host_wait = NULL; diff -urN linux-2.2.20/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h --- linux-2.2.20/drivers/scsi/hosts.h Sun Mar 25 09:37:36 2001 +++ linux/drivers/scsi/hosts.h Mon Dec 3 23:51:17 2001 @@ -403,6 +403,16 @@ __attribute__ ((aligned (sizeof(unsigned long)))); }; +typedef struct SHN + { + struct SHN * next; + char * name; + unsigned short host_no; + unsigned short host_registered; + unsigned loaded_as_module; + } Scsi_Host_Name; + +extern Scsi_Host_Name * scsi_host_no_list; extern struct Scsi_Host * scsi_hostlist; extern struct Scsi_Device_Template * scsi_devicelist; diff -urN linux-2.2.20/drivers/scsi/osst.c linux/drivers/scsi/osst.c --- linux-2.2.20/drivers/scsi/osst.c Fri Nov 2 09:39:08 2001 +++ linux/drivers/scsi/osst.c Mon Dec 3 23:10:32 2001 @@ -5217,17 +5217,17 @@ /* Rewind entry */ sprintf (name, "mt%s", formats[mode]); tpnt->de_r[mode] = - devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5), S_IFCHR | S_IRUGO | S_IWUGO, - 0, 0, &osst_fops, NULL); + &osst_fops, NULL); /* No-rewind entry */ sprintf (name, "mt%sn", formats[mode]); tpnt->de_n[mode] = - devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5) + 128, S_IFCHR | S_IRUGO | S_IWUGO, - 0, 0, &osst_fops, NULL); + &osst_fops, NULL); } devfs_register_tape (tpnt->de_r[0]); #endif diff -urN linux-2.2.20/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- linux-2.2.20/drivers/scsi/scsi.c Fri Nov 2 09:39:08 2001 +++ linux/drivers/scsi/scsi.c Mon Dec 3 22:13:02 2001 @@ -666,6 +666,8 @@ } } +static devfs_handle_t devfs_handle = NULL; + /* * The worker for scan_scsis. * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on. @@ -680,6 +682,7 @@ Scsi_Device * SDtail, *SDpnt=*SDpnt2; int bflags, type=-1; + char devname[64]; SDpnt->host = shpnt; SDpnt->id = dev; SDpnt->lun = lun; @@ -843,6 +846,12 @@ print_inquiry (scsi_result); + if (!devfs_handle) devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); + sprintf (devname, "host%d/bus%d/target%d/lun%d", + SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun); + if (SDpnt->de) printk ("DEBUG: dir: \"%s\" already exists\n", devname); + else SDpnt->de = devfs_mk_dir (devfs_handle, devname, 0, NULL); + for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) if (sdtpnt->detect) @@ -2047,7 +2056,39 @@ SDpnt->has_cmdblocks = 1; } +void __init scsi_host_no_insert(char *str, int n) +{ + Scsi_Host_Name *shn, *shn2; + int len; + + len = strlen(str); + if (len && (shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC))) { + if ((shn->name = kmalloc(len+1, GFP_ATOMIC))) { + strncpy(shn->name, str, len); + shn->name[len] = 0; + shn->host_no = n; + shn->host_registered = 0; + shn->loaded_as_module = 1; /* numbers shouldn't be freed in any case */ + shn->next = NULL; + if (scsi_host_no_list) { + for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next) + ; + shn2->next = shn; + } + else + scsi_host_no_list = shn; + max_scsi_hosts = n+1; + } + else + kfree((char *) shn); + } +} + #ifndef MODULE /* { */ + +char scsi_host_no_table[20][10] __initdata = {}; +int scsi_host_no_set __initdata = 0; + /* * scsi_dev_init() is our initialization routine, which in turn calls host * initialization, bus scanning, and sd/st initialization routines. @@ -2062,6 +2103,14 @@ return; #endif + /* Initialize list of host_no if kernel parameter set */ + if (scsi_host_no_set) { + int i; + + for (i = 0;i < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0]);i++) + scsi_host_no_insert(scsi_host_no_table[i], i); + } + /* Yes we're here... */ #if CONFIG_PROC_FS dispatch_scsi_info_ptr = dispatch_scsi_info; @@ -2475,6 +2524,7 @@ * Nobody is using this device any more. * Free all of the command structures. */ + devfs_unregister (scd->de); if (HBA_ptr->hostt->revoke) HBA_ptr->hostt->revoke(scd); for(SCpnt=scd->device_queue; SCpnt; SCpnt = scd->device_queue) @@ -3010,6 +3060,7 @@ printk("Attached usage count = %d\n", SDpnt->attached); return; } + devfs_unregister (SDpnt->de); } } @@ -3436,7 +3487,45 @@ #endif /* CONFIG_PROC_FS */ } +int scsi_host_no_init (char *str) +{ + static int next_no = 0; + char *temp; + +#ifndef MODULE + int len; + scsi_host_no_set = 1; + memset(scsi_host_no_table, 0, sizeof(scsi_host_no_table)); +#endif /* MODULE */ + + while (str) { + temp = str; + while (*temp && (*temp != ':') && (*temp != ',')) + temp++; + if (!*temp) + temp = NULL; + else + *temp++ = 0; #ifdef MODULE + scsi_host_no_insert(str, next_no); +#else + if (next_no < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0])) { + if ((len = strlen(str)) >= sizeof(scsi_host_no_table[0])) + len = sizeof(scsi_host_no_table[0])-1; + strncpy(scsi_host_no_table[next_no], str, len); + scsi_host_no_table[next_no][len] = 0; + } +#endif /* MODULE */ + str = temp; + next_no++; + } + return 1; +} + +#ifdef MODULE +static char *scsihosts; + +MODULE_PARM(scsihosts, "s"); MODULE_PARM(max_scsi_luns, "i"); @@ -3492,6 +3581,7 @@ printk("scsi::init_module: failed, out of memory\n"); return 1; } + scsi_host_no_init(scsihosts); /* * This is where the processing takes place for most everything @@ -3504,7 +3594,20 @@ void cleanup_module( void) { + Scsi_Host_Name *shn, *shn2 = NULL; + remove_bh(SCSI_BH); + + devfs_unregister (devfs_handle); + for (shn = scsi_host_no_list;shn;shn = shn->next) { + if (shn->name) + kfree(shn->name); + if (shn2) + kfree (shn2); + shn2 = shn; + } + if (shn2) + kfree (shn2); #if CONFIG_PROC_FS proc_scsi_unregister(0, PROC_SCSI_SCSI); diff -urN linux-2.2.20/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- linux-2.2.20/drivers/scsi/scsi.h Sun Mar 25 09:37:36 2001 +++ linux/drivers/scsi/scsi.h Mon Dec 3 23:51:18 2001 @@ -16,6 +16,7 @@ #define _SCSI_H #include /* for CONFIG_SCSI_LOGGING */ +#include /* * Some of the public constants are being moved to this file. @@ -440,6 +441,7 @@ int access_count; /* Count of open channels/mounts */ void *hostdata; /* available to low-level driver */ + devfs_handle_t de; /* directory for the device */ char type; char scsi_level; char vendor[8], model[16], rev[4]; diff -urN linux-2.2.20/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- linux-2.2.20/drivers/scsi/sd.c Sun Mar 25 09:31:30 2001 +++ linux/drivers/scsi/sd.c Mon Dec 3 22:13:02 2001 @@ -17,6 +17,8 @@ * * Modified by Jirka Hanika geo@ff.cuni.cz to support more * scsi disks using eight major numbers. + * + * Modified by Richard Gooch rgooch@atnf.csiro.au to support devfs. */ #include @@ -263,7 +265,8 @@ NULL, /* block sizes */ 0, /* number */ NULL, /* internal */ - NULL /* next */ + NULL, /* next */ + &sd_fops, /* file operations */ }; static struct gendisk *sd_gendisks = &sd_gendisk; @@ -1520,7 +1523,7 @@ if(!sd_registered) { for (i=0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) { - if (register_blkdev(SD_MAJOR(i),"sd",&sd_fops)) { + if (devfs_register_blkdev(SD_MAJOR(i),"sd",&sd_fops)) { printk("Unable to get major %d for SCSI disk\n", SD_MAJOR(i)); return 1; } @@ -1559,12 +1562,14 @@ sd_gendisks = (struct gendisk *) kmalloc(N_USED_SD_MAJORS * sizeof(struct gendisk), GFP_ATOMIC); for (i=0; i < N_USED_SD_MAJORS; i++) { + sd_gendisks[i] = sd_gendisk; + sd_gendisks[i].de_arr = scsi_init_malloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr, + GFP_ATOMIC); + sd_gendisks[i].flags = scsi_init_malloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags, + GFP_ATOMIC); sd_gendisks[i].major = SD_MAJOR(i); - sd_gendisks[i].major_name = "sd"; - sd_gendisks[i].minor_shift = 4; sd_gendisks[i].max_p = 1 << 4; sd_gendisks[i].max_nr = SCSI_DISKS_PER_MAJOR; - sd_gendisks[i].init = sd_geninit; sd_gendisks[i].part = sd + (i * SCSI_DISKS_PER_MAJOR << 4); sd_gendisks[i].sizes = sd_sizes + (i * SCSI_DISKS_PER_MAJOR << 4); sd_gendisks[i].nr_real = 0; @@ -1652,6 +1657,7 @@ } static int sd_attach(Scsi_Device * SDp){ + unsigned int devnum; Scsi_Disk * dpnt; int i; @@ -1672,6 +1678,10 @@ rscsi_disks[i].has_part_table = 0; sd_template.nr_dev++; SD_GENDISK(i).nr_real++; + devnum = i % SCSI_DISKS_PER_MAJOR; + SD_GENDISK(i).de_arr[devnum] = SDp->de; + if (SDp->removable) + SD_GENDISK(i).flags[devnum] |= GENHD_FL_REMOVABLE; return 0; } @@ -1771,6 +1781,8 @@ sd_sizes[index] = 0; } + devfs_register_partitions (&SD_GENDISK (i), + SD_MINOR_NUMBER (start), 1); dpnt->has_part_table = 0; dpnt->device = NULL; dpnt->capacity = 0; @@ -1802,7 +1814,7 @@ scsi_unregister_module(MODULE_SCSI_DEV, &sd_template); for (i=0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) - unregister_blkdev(SD_MAJOR(i),"sd"); + devfs_unregister_blkdev(SD_MAJOR(i),"sd"); sd_registered--; if( rscsi_disks != NULL ) diff -urN linux-2.2.20/drivers/scsi/sg.c linux/drivers/scsi/sg.c --- linux-2.2.20/drivers/scsi/sg.c Fri Nov 2 09:39:08 2001 +++ linux/drivers/scsi/sg.c Mon Dec 3 22:13:02 2001 @@ -9,6 +9,8 @@ * 2.x extensions to driver: * Copyright (C) 1998 - 2001 Douglas Gilbert * + * Modified 19-JAN-1998 Richard Gooch Devfs support + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) @@ -55,6 +57,7 @@ #include #include #include + #include #include #include @@ -156,6 +159,7 @@ struct wait_queue * o_excl_wait; /* queue open() when O_EXCL in use */ int sg_tablesize; /* adapter's max scatter-gather table size */ Sg_fd * headfp; /* first open fd belonging to this device */ + devfs_handle_t de; kdev_t i_rdev; /* holds device major+minor number */ char exclude; /* opened for exclusive access */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ @@ -987,7 +991,7 @@ if (sg_template.dev_noticed == 0) return 0; if(!sg_registered) { - if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops)) + if (devfs_register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops)) { printk("Unable to get major %d for generic SCSI device\n", SCSI_GENERIC_MAJOR); @@ -1036,6 +1040,10 @@ sdp->sgdebug = 0; sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0; sdp->i_rdev = MKDEV(SCSI_GENERIC_MAJOR, k); + sdp->de = devfs_register (scsidp->de, "generic", DEVFS_FL_DEFAULT, + SCSI_GENERIC_MAJOR, k, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &sg_fops, NULL); sg_template.nr_dev++; return 0; } @@ -1080,6 +1088,8 @@ SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); sdp->device = NULL; } + devfs_unregister (sdp->de); + sdp->de = NULL; scsidp->attached--; sg_template.nr_dev--; /* avoid associated device /dev/sg? being incremented @@ -1100,7 +1110,7 @@ void cleanup_module( void) { scsi_unregister_module(MODULE_SCSI_DEV, &sg_template); - unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); + devfs_unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); if(sg_dev_arr != NULL) { /* Really worrying situation of writes still pending and get here */ diff -urN linux-2.2.20/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- linux-2.2.20/drivers/scsi/sr.c Sun Mar 25 09:37:36 2001 +++ linux/drivers/scsi/sr.c Mon Dec 3 22:13:02 2001 @@ -26,6 +26,8 @@ * Modified by Jens Axboe - support DVD-RAM * transparently and loose the GHOST hack * + * Modified by Richard Gooch to support devfs + * * Modified by Arnaldo Carvalho de Melo * check resource allocation in sr_init and some cleanups - 2000/12/09 */ @@ -40,6 +42,8 @@ #include #include #include +#include + #include #include #include @@ -826,6 +830,9 @@ SDp->scsi_request_fn = do_sr_request; scsi_CDs[i].device = SDp; + cpnt->de = devfs_register (SDp->de, "cd", DEVFS_FL_DEFAULT, + MAJOR_NR, i, S_IFBLK | S_IRUGO | S_IWUGO, + &cdrom_fops, NULL); sr_template.nr_dev++; if (sr_template.nr_dev > sr_template.dev_max) @@ -1191,6 +1198,7 @@ sprintf(name, "sr%d", i); strcpy(scsi_CDs[i].cdi.name, name); + scsi_CDs[i].cdi.de = scsi_CDs[i].de; register_cdrom(&scsi_CDs[i].cdi); } @@ -1228,6 +1236,8 @@ * Reset things back to a sane state so that one can re-load a new * driver (perhaps the same one). */ + devfs_unregister (cpnt->de); + cpnt->de = NULL; unregister_cdrom(&(cpnt->cdi)); cpnt->device = NULL; cpnt->capacity = 0; diff -urN linux-2.2.20/drivers/scsi/sr.h linux/drivers/scsi/sr.h --- linux-2.2.20/drivers/scsi/sr.h Sun Mar 25 09:31:30 2001 +++ linux/drivers/scsi/sr.h Mon Dec 3 23:57:24 2001 @@ -18,12 +18,14 @@ #define _SR_H #include "scsi.h" +#include typedef struct { unsigned capacity; /* size in blocks */ unsigned sector_size; /* size in bytes */ Scsi_Device *device; + devfs_handle_t de; unsigned int vendor; /* vendor code, see sr_vendor.c */ unsigned long ms_offset; /* for reading multisession-CD's */ unsigned char sector_bit_size; /* sector size = 2^sector_bit_size */ diff -urN linux-2.2.20/drivers/scsi/st.c linux/drivers/scsi/st.c --- linux-2.2.20/drivers/scsi/st.c Fri Nov 2 09:39:08 2001 +++ linux/drivers/scsi/st.c Mon Dec 3 22:13:02 2001 @@ -13,6 +13,8 @@ Last modified: Wed Sep 12 09:16:22 2001 by makisara@abies.metla.fi Some small formal changes - aeb, 950809 + + Last modified: 18-JAN-1998 Richard Gooch Devfs support */ #include @@ -3454,7 +3456,7 @@ Scsi_Tape * tpnt; ST_mode * STm; ST_partstat * STps; - int i; + int i, mode; if (SDp->type != TYPE_TAPE) return 1; @@ -3471,6 +3473,26 @@ if(i >= st_template.dev_max) panic ("scsi_devices corrupt (st)"); + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + char name[8]; + static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"}; + + /* Rewind entry */ + sprintf (name, "mt%s", formats[mode]); + tpnt->de_r[mode] = + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5), + S_IFCHR | S_IRUGO | S_IWUGO, + &st_fops, NULL); + /* No-rewind entry */ + sprintf (name, "mt%sn", formats[mode]); + tpnt->de_n[mode] = + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5) + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + &st_fops, NULL); + } + devfs_register_tape (tpnt->de_r[0]); scsi_tapes[i].device = SDp; if (SDp->scsi_level <= 2) scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1; @@ -3559,7 +3581,7 @@ if (st_template.dev_noticed == 0) return 0; if(!st_registered) { - if (register_chrdev(SCSI_TAPE_MAJOR,"st",&st_fops)) { + if (devfs_register_chrdev(SCSI_TAPE_MAJOR,"st",&st_fops)) { printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",MAJOR_NR); return 1; } @@ -3577,7 +3599,7 @@ GFP_ATOMIC); if (scsi_tapes == NULL) { printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n"); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); return 1; } @@ -3602,7 +3624,7 @@ GFP_ATOMIC); if (st_buffers == NULL) { printk(KERN_ERR "Unable to allocate tape buffer pointers.\n"); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); st_free((char *) scsi_tapes, st_template.dev_max * sizeof(Scsi_Tape)); return 1; @@ -3646,11 +3668,17 @@ static void st_detach(Scsi_Device * SDp) { Scsi_Tape * tpnt; - int i; + int i, mode; for(tpnt = scsi_tapes, i=0; idevice == SDp) { tpnt->device = NULL; + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + devfs_unregister (tpnt->de_r[mode]); + tpnt->de_r[mode] = NULL; + devfs_unregister (tpnt->de_n[mode]); + tpnt->de_n[mode] = NULL; + } SDp->attached--; st_template.nr_dev--; st_template.dev_noticed--; @@ -3691,7 +3719,7 @@ int i, j; scsi_unregister_module(MODULE_SCSI_DEV, &st_template); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); st_registered--; if(scsi_tapes != NULL) { st_free((char *) scsi_tapes, diff -urN linux-2.2.20/drivers/scsi/st.h linux/drivers/scsi/st.h --- linux-2.2.20/drivers/scsi/st.h Fri Nov 2 09:39:08 2001 +++ linux/drivers/scsi/st.h Mon Dec 3 23:57:24 2001 @@ -8,6 +8,7 @@ #ifndef _SCSI_H #include "scsi.h" #endif +#include /* The tape buffer descriptor. */ typedef struct { @@ -87,6 +88,8 @@ /* Mode characteristics */ ST_mode modes[ST_NBR_MODES]; int current_mode; + devfs_handle_t de_r[ST_NBR_MODES]; /* Rewind entries */ + devfs_handle_t de_n[ST_NBR_MODES]; /* No-rewind entries */ /* Status variables */ int partition; diff -urN linux-2.2.20/drivers/sgi/char/shmiq.c linux/drivers/sgi/char/shmiq.c --- linux-2.2.20/drivers/sgi/char/shmiq.c Sun Mar 25 09:31:42 2001 +++ linux/drivers/sgi/char/shmiq.c Mon Dec 3 22:13:02 2001 @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -458,6 +459,19 @@ void shmiq_init (void) { + int i; + char devname[8]; + printk ("SHMIQ setup\n"); - register_chrdev (SHMIQ_MAJOR, "shmiq", &shmiq_fops); + devfs_register_chrdev(SHMIQ_MAJOR, "shmiq", &shmiq_fops); + devfs_register (NULL, "shmiq", DEVFS_FL_DEFAULT, + SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, + &shmiq_fops, NULL); + for (i = 0; i < 2; i++) { + sprintf (devname, "qcntl%i", i); + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, + SHMIQ_MAJOR, i + 1, + S_IFCHR | S_IRUSR | S_IWUSR, + &shmiq_fops, NULL); + } } diff -urN linux-2.2.20/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c --- linux-2.2.20/drivers/sound/sound_core.c Sun Mar 25 09:37:37 2001 +++ linux/drivers/sound/sound_core.c Mon Dec 3 22:13:02 2001 @@ -43,13 +43,17 @@ #include #include #include - - +#include + +#define SOUND_STEP 16 + + struct sound_unit { int unit_minor; struct file_operations *unit_fops; struct sound_unit *next; + devfs_handle_t de; }; extern int init_cmpci(void); @@ -89,7 +93,7 @@ if(*list==NULL || (*list)->unit_minor>n) break; list=&((*list)->next); - n+=16; + n+=SOUND_STEP; } if(n>=top) @@ -136,6 +140,7 @@ if(p->unit_minor==unit) { *list=p->next; + devfs_unregister (p->de); kfree(p); MOD_DEC_USE_COUNT; return; @@ -155,11 +160,15 @@ * Allocate the controlling structure and add it to the sound driver * list. Acquires locks as needed */ + +static devfs_handle_t devfs_handle = NULL; -static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top) +static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode) { int r; struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL); + char name_buf[16]; + if(s==NULL) return -ENOMEM; @@ -169,6 +178,13 @@ if(r<0) kfree(s); + if (r == low) + sprintf (name_buf, "%s", name); + else + sprintf (name_buf, "%s%d", name, (r - low) / SOUND_STEP); + s->de = devfs_register (devfs_handle, name_buf, + DEVFS_FL_DEFAULT, SOUND_MAJOR, s->unit_minor, + S_IFCHR | mode, fops, NULL); return r; } @@ -210,21 +226,76 @@ int register_sound_special(struct file_operations *fops, int unit) { - return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1); + char *name; + + switch (unit) { + case 0: + name = "mixer"; + break; + case 1: + name = "sequencer"; + break; + case 2: + name = "midi00"; + break; + case 3: + name = "dsp"; + break; + case 4: + name = "audio"; + break; + case 5: + name = "unknown5"; + break; + case 6: + name = "sndstat"; + break; + case 7: + name = "unknown7"; + break; + case 8: + name = "sequencer2"; + break; + case 9: + name = "dmmidi"; + break; + case 10: + name = "dmfm"; + break; + case 11: + name = "unknown11"; + break; + case 12: + name = "adsp"; + break; + case 13: + name = "amidi"; + break; + case 14: + name = "admmidi"; + break; + default: + name = "unknown"; + break; + } + return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1, + name, S_IRUGO | S_IWUGO); } EXPORT_SYMBOL(register_sound_special); int register_sound_mixer(struct file_operations *fops, int dev) { - return sound_insert_unit(&chains[0], fops, dev, 0, 128); + return sound_insert_unit(&chains[0], fops, dev, 0, 128, + "mixer", S_IRUGO | S_IWUGO); } EXPORT_SYMBOL(register_sound_mixer); int register_sound_midi(struct file_operations *fops, int dev) { - return sound_insert_unit(&chains[2], fops, dev, 2, 130); + return sound_insert_unit(&chains[2], fops, dev, 2, 130, + "midi", S_IRUGO | S_IWUGO); } EXPORT_SYMBOL(register_sound_midi); @@ -236,14 +307,16 @@ int register_sound_dsp(struct file_operations *fops, int dev) { - return sound_insert_unit(&chains[3], fops, dev, 3, 131); + return sound_insert_unit(&chains[3], fops, dev, 3, 131, + "dsp", S_IWUGO | S_IRUSR | S_IRGRP); } EXPORT_SYMBOL(register_sound_dsp); int register_sound_synth(struct file_operations *fops, int dev) { - return sound_insert_unit(&chains[9], fops, dev, 9, 137); + return sound_insert_unit(&chains[9], fops, dev, 9, 137, + "synth", S_IRUGO | S_IWUGO); } EXPORT_SYMBOL(register_sound_synth); @@ -380,7 +453,8 @@ { /* We have nothing to really do here - we know the lists must be empty */ - unregister_chrdev(SOUND_MAJOR, "sound"); + devfs_unregister_chrdev(SOUND_MAJOR, "sound"); + devfs_unregister (devfs_handle); } int init_module(void) @@ -388,11 +462,12 @@ int soundcore_init(void) #endif { - if(register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) + if(devfs_register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) { printk(KERN_ERR "soundcore: sound device already in use.\n"); return -EBUSY; } + devfs_handle = devfs_mk_dir (NULL, "sound", 0, NULL); /* * Now init non OSS drivers */ diff -urN linux-2.2.20/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c --- linux-2.2.20/drivers/sound/soundcard.c Sun Mar 25 09:31:34 2001 +++ linux/drivers/sound/soundcard.c Mon Dec 3 22:13:02 2001 @@ -15,6 +15,9 @@ * integrated sound_switch.c * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat, * which should disappear in the near future) + * Eric Dumas : devfs support (22-Jan-98) with fixups + * by C. Scott Ananian + * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c * * Rob Riggs Added persistent DMA buffers support (1998/10/17) */ @@ -36,6 +39,8 @@ #include #include #include +#include +#include #endif /* __KERNEL__ */ #include #include @@ -92,7 +97,6 @@ static mixer_vol_table mixer_vols[MAX_MIXER_DEV]; static int num_mixer_volumes = 0; - int *load_mixer_volumes(char *name, int *levels, int present) { int i, n; @@ -803,6 +807,66 @@ return -1; } + +/* These device names follow the official Linux device list, + * Documentation/devices.txt. Let us know if there are other + * common names we should support for compatibility. + * Only those devices not created by the generic code in sound_core.c are + * registered here. + */ +static const struct { + unsigned short minor; + char *name; + umode_t mode; + int *num; +} dev_list[] = { /* list of minor devices */ +#ifdef CONFIG_AUDIO +/* seems to be some confusion here -- this device is not in the device list */ + {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP, + &num_audiodevs}, + {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP, + &num_audiodevs}, +#endif /* CONFIG_AUDIO */ +}; + +static char * +soundcard_make_name(char *buf, char *name, int idx) { + if (idx==0) + sprintf(buf, "sound/%s", name); + else + sprintf(buf, "sound/%s%d", name, idx); + return buf; +} + +/* Register/unregister audio entries */ +static void soundcard_register_devfs (int do_register) +{ + char name_buf[32]; + int i, j, num; + + for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) + { + num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num; + for (j = 0; j < num || j == 0; j++) + { + soundcard_make_name (name_buf, dev_list[i].name, j); + if (do_register) + devfs_register (NULL, name_buf, DEVFS_FL_DEFAULT, + SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), + S_IFCHR | dev_list[i].mode, + &oss_sound_fops, NULL); + else + { + devfs_handle_t de; + + de = devfs_find_handle (NULL, name_buf, 0, 0, 0, + DEVFS_SPECIAL_CHR, 0); + devfs_unregister (de); + } + } + } +} + #ifdef MODULE static void #else @@ -841,6 +905,7 @@ if (proc_register(&proc_root, &proc_root_sound)) printk(KERN_ERR "sound: registering /proc/sound failed\n"); #endif + soundcard_register_devfs(1); /* register after we know # of devices */ } #ifdef MODULE @@ -910,6 +975,7 @@ if (proc_unregister(&proc_root, PROC_SOUND)) printk(KERN_ERR "sound: unregistering /proc/sound failed\n"); #endif + soundcard_register_devfs (0); if (chrdev_registered) destroy_special_devices(); diff -urN linux-2.2.20/drivers/video/fbmem.c linux/drivers/video/fbmem.c --- linux-2.2.20/drivers/video/fbmem.c Fri Nov 2 09:39:08 2001 +++ linux/drivers/video/fbmem.c Mon Dec 3 22:13:02 2001 @@ -26,6 +26,7 @@ #ifdef CONFIG_KMOD #include #endif +#include #if defined(__mc68000__) || defined(CONFIG_APUS) #include @@ -647,10 +648,13 @@ NULL /* fsync */ }; +static devfs_handle_t devfs_handle = NULL; + int register_framebuffer(struct fb_info *fb_info) { int i, j; + char name_buf[8]; static int fb_ever_opened[FB_MAX]; static int first = 1; @@ -677,12 +681,17 @@ first = 0; take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default); } + sprintf (name_buf, "%d", i); + fb_info->devfs_handle = + devfs_register (devfs_handle, name_buf, DEVFS_FL_DEFAULT, + FB_MAJOR, i << FB_MODES_SHIFT, + S_IFCHR | S_IRUGO | S_IWUGO, &fb_fops, NULL); return 0; } int -unregister_framebuffer(const struct fb_info *fb_info) +unregister_framebuffer(struct fb_info *fb_info) { int i, j; @@ -691,7 +700,11 @@ if (con2fb_map[j] == i) return -EBUSY; if (!registered_fb[i]) - return -EINVAL; + return -EINVAL; + devfs_unregister (fb_info->devfs_handle); + fb_info->devfs_handle = NULL; + devfs_unregister (fb_info->devfs_lhandle); + fb_info->devfs_lhandle = NULL; registered_fb[i]=NULL; num_registered_fb--; return 0; @@ -708,7 +721,8 @@ if (proc_fbmem) proc_fbmem->read_proc = fbmem_read_proc; - if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) + devfs_handle = devfs_mk_dir (NULL, "fb", 0, NULL); + if (devfs_register_chrdev(FB_MAJOR,"fb",&fb_fops)) printk("unable to get major %d for fb devs\n", FB_MAJOR); /* diff -urN linux-2.2.20/fs/Config.in linux/fs/Config.in --- linux-2.2.20/fs/Config.in Sun Mar 25 09:37:38 2001 +++ linux/fs/Config.in Mon Dec 3 22:13:02 2001 @@ -34,6 +34,12 @@ fi tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS bool '/proc filesystem support' CONFIG_PROC_FS +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool '/dev filesystem support (EXPERIMENTAL)' CONFIG_DEVFS_FS + if [ "$CONFIG_DEVFS_FS" = "y" ]; then + bool ' Debug devfs' CONFIG_DEVFS_DEBUG + fi +fi if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then # It compiles as a module for testing only. It should not be used # as a module in general. If we make this "tristate", a bunch of people diff -urN linux-2.2.20/fs/Makefile linux/fs/Makefile --- linux-2.2.20/fs/Makefile Sun Mar 25 09:30:58 2001 +++ linux/fs/Makefile Mon Dec 3 22:13:02 2001 @@ -18,7 +18,7 @@ MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd \ - nfsd nls devpts adfs qnx4 efs + nfsd nls devpts devfs adfs qnx4 efs ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -88,6 +88,10 @@ ifeq ($(CONFIG_ISO9660_FS),m) MOD_SUB_DIRS += isofs endif +endif + +ifdef CONFIG_DEVFS_FS +SUB_DIRS += devfs endif ifeq ($(CONFIG_HFS_FS),y) diff -urN linux-2.2.20/fs/coda/psdev.c linux/fs/coda/psdev.c --- linux-2.2.20/fs/coda/psdev.c Sun Mar 25 09:31:02 2001 +++ linux/fs/coda/psdev.c Mon Dec 3 22:13:02 2001 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,9 @@ struct coda_sb_info coda_super_info; struct venus_comm coda_upc_comm; - + +static devfs_handle_t devfs_handles[MAX_CODADEVS]; + /* * Device operations */ @@ -386,7 +389,19 @@ int init_coda_psdev(void) { - if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) { + int i; + char txt[8]; + + for (i = 0; i < MAX_CODADEVS; ++i) { + sprintf (txt, "cfs%d", i); + devfs_handles[i] = + devfs_register (NULL, txt, DEVFS_FL_DEFAULT, + CODA_PSDEV_MAJOR, i, + S_IFCHR | S_IRUSR | S_IWUSR, + &coda_psdev_fops, NULL); + } + if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", + &coda_psdev_fops)) { printk(KERN_ERR "coda_psdev: unable to get major %d\n", CODA_PSDEV_MAJOR); return -EIO; @@ -426,14 +441,16 @@ void cleanup_module(void) { - int err; + int err, i; ENTRY; if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) { printk("coda: failed to unregister filesystem\n"); } - unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); + for (i = 0; i < MAX_CODADEVS; ++i) + devfs_unregister (devfs_handles[i]); + devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); coda_sysctl_clean(); } diff -urN linux-2.2.20/fs/devfs/Makefile linux/fs/devfs/Makefile --- linux-2.2.20/fs/devfs/Makefile Wed Dec 31 17:00:00 1969 +++ linux/fs/devfs/Makefile Mon Dec 3 22:13:02 2001 @@ -0,0 +1,39 @@ +# +# Makefile for the linux devfs-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := devfs.o +OX_OBJS := base.o util.o + +# Special case to support building documentation +ifndef TOPDIR +TOPDIR = ../.. +endif + +include $(TOPDIR)/Rules.make + + +# Rule to build documentation +doc: base.c util.c + @echo '$$PACKAGE devfs' > devfs.doc + @echo '$$NAME Linux Kernel' >> devfs.doc + @echo '$$SUMMARY devfs (Device FileSystem) functions' >> devfs.doc + @echo '$$SYNOPSIS "#include "' >> devfs.doc + @echo '$$END' >> devfs.doc + c2doc base.c util.c >> devfs.doc + karma_doc2man -section 9 devfs.doc . + rm devfs.doc + gzip --best *.9 + mv *.9.gz /usr/man/man9 + + +# Rule for test compiling +test: + gcc -o /tmp/base.o -D__KERNEL__ -I../../include -Wall \ + -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe \ + -fno-strength-reduce -DCPU=686 -DEXPORT_SYMTAB -c base.c diff -urN linux-2.2.20/fs/devfs/base.c linux/fs/devfs/base.c --- linux-2.2.20/fs/devfs/base.c Wed Dec 31 17:00:00 1969 +++ linux/fs/devfs/base.c Mon Dec 3 23:54:54 2001 @@ -0,0 +1,3483 @@ +/* devfs (Device FileSystem) driver. + + Copyright (C) 1998-2001 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + ChangeLog + + 19980110 Richard Gooch + Original version. + v0.1 + 19980111 Richard Gooch + Created per-fs inode table rather than using inode->u.generic_ip + v0.2 + 19980111 Richard Gooch + Created .epoch inode which has a ctime of 0. + Fixed loss of named pipes when dentries lost. + Fixed loss of inode data when devfs_register() follows mknod(). + v0.3 + 19980111 Richard Gooch + Fix for when compiling with CONFIG_KERNELD. + 19980112 Richard Gooch + Fix for readdir() which sometimes didn't show entries. + Added <> option to . + v0.4 + 19980113 Richard Gooch + Created function. + v0.5 + 19980115 Richard Gooch + Added subdirectory support. Major restructuring. + 19980116 Richard Gooch + Fixed to not search major=0,minor=0. + Added symlink support. + v0.6 + 19980120 Richard Gooch + Created function and support directory unregister + 19980120 Richard Gooch + Auto-ownership uses real uid/gid rather than effective uid/gid. + v0.7 + 19980121 Richard Gooch + Supported creation of sockets. + v0.8 + 19980122 Richard Gooch + Added DEVFS_FL_HIDE_UNREG flag. + Interface change to . + Created to support symlink(2). + v0.9 + 19980123 Richard Gooch + Added check to to check inode is in devfs. + Added optional traversal of symlinks. + v0.10 + 19980124 Richard Gooch + Created and . + v0.11 + 19980125 C. Scott Ananian + Created . + 19980125 Richard Gooch + Allow removal of symlinks. + v0.12 + 19980125 Richard Gooch + Created . + 19980126 Richard Gooch + Moved DEVFS_SUPER_MAGIC into header file. + Added DEVFS_FL_HIDE flag. + Created . + Created . + Fixed minor bug in . + 19980127 Richard Gooch + Changed interface to , , + , and . + Fixed inode times when symlink created with symlink(2). + v0.13 + 19980129 C. Scott Ananian + Exported , + and . + 19980129 Richard Gooch + Created to support unlink(2). + v0.14 + 19980129 Richard Gooch + Fixed kerneld support for entries in devfs subdirectories. + 19980130 Richard Gooch + Bugfixes in . + v0.15 + 19980207 Richard Gooch + Call kerneld when looking up unregistered entries. + v0.16 + 19980326 Richard Gooch + Modified interface to for symlink traversal. + v0.17 + 19980331 Richard Gooch + Fixed persistence bug with device numbers for manually created + device files. + Fixed problem with recreating symlinks with different content. + v0.18 + 19980401 Richard Gooch + Changed to CONFIG_KMOD. + Hide entries which are manually unlinked. + Always invalidate devfs dentry cache when registering entries. + Created to support rmdir(2). + Ensure directories created by are visible. + v0.19 + 19980402 Richard Gooch + Invalidate devfs dentry cache when making directories. + Invalidate devfs dentry cache when removing entries. + Fixed persistence bug with fifos. + v0.20 + 19980421 Richard Gooch + Print process command when debugging kerneld/kmod. + Added debugging for register/unregister/change operations. + 19980422 Richard Gooch + Added "devfs=" boot options. + v0.21 + 19980426 Richard Gooch + No longer lock/unlock superblock in . + Drop negative dentries when they are released. + Manage dcache more efficiently. + v0.22 + 19980427 Richard Gooch + Added DEVFS_FL_AUTO_DEVNUM flag. + v0.23 + 19980430 Richard Gooch + No longer set unnecessary methods. + v0.24 + 19980504 Richard Gooch + Added PID display to debugging message. + Added "after" debugging message to . + 19980519 Richard Gooch + Added "diread" and "diwrite" boot options. + 19980520 Richard Gooch + Fixed persistence problem with permissions. + v0.25 + 19980602 Richard Gooch + Support legacy device nodes. + Fixed bug where recreated inodes were hidden. + v0.26 + 19980602 Richard Gooch + Improved debugging in . + 19980607 Richard Gooch + No longer free old dentries in . + Free all dentries for a given entry when deleting inodes. + v0.27 + 19980627 Richard Gooch + Limit auto-device numbering to majors 128 to 239. + v0.28 + 19980629 Richard Gooch + Fixed inode times persistence problem. + v0.29 + 19980704 Richard Gooch + Fixed spelling in debug. + Fixed bug in parsing "dilookup". + v0.30 + 19980705 Richard Gooch + Fixed devfs inode leak when manually recreating inodes. + Fixed permission persistence problem when recreating inodes. + v0.31 + 19980727 Richard Gooch + Removed harmless "unused variable" compiler warning. + Fixed modes for manually recreated device nodes. + v0.32 + 19980728 Richard Gooch + Added NULL devfs inode warning in . + Force all inode nlink values to 1. + v0.33 + 19980730 Richard Gooch + Added "dimknod" boot option. + Set inode nlink to 0 when freeing dentries. + Fixed modes for manually recreated symlinks. + v0.34 + 19980802 Richard Gooch + Fixed bugs in recreated directories and symlinks. + v0.35 + 19980806 Richard Gooch + Fixed bugs in recreated device nodes. + 19980807 Richard Gooch + Fixed bug in currently unused . + Defined new type. + Improved debugging when getting entries. + Fixed bug where directories could be emptied. + v0.36 + 19980809 Richard Gooch + Replaced dummy .epoch inode with .devfsd character device. + 19980810 Richard Gooch + Implemented devfsd protocol revision 0. + v0.37 + 19980819 Richard Gooch + Added soothing message to warning in . + v0.38 + 19980829 Richard Gooch + Use GCC extensions for structure initialisations. + Implemented async open notification. + Incremented devfsd protocol revision to 1. + v0.39 + 19980908 Richard Gooch + Moved async open notification to end of . + v0.40 + 19980910 Richard Gooch + Prepended "/dev/" to module load request. + Renamed to . + v0.41 + 19980910 Richard Gooch + Fixed typo "AYSNC" -> "ASYNC". + v0.42 + 19980910 Richard Gooch + Added open flag for files. + v0.43 + 19980927 Richard Gooch + Set i_blocks=0 and i_blksize=1024 in . + v0.44 + 19981005 Richard Gooch + Added test for empty <> in . + Renamed to and published. + v0.45 + 19981006 Richard Gooch + Created . + v0.46 + 19981007 Richard Gooch + Limit auto-device numbering to majors 144 to 239. + v0.47 + 19981010 Richard Gooch + Updated for VFS change in 2.1.125. + v0.48 + 19981022 Richard Gooch + Created DEVFS_ FL_COMPAT flag. + v0.49 + 19981023 Richard Gooch + Created "nocompat" boot option. + v0.50 + 19981025 Richard Gooch + Replaced "mount" boot option with "nomount". + v0.51 + 19981110 Richard Gooch + Created "only" boot option. + v0.52 + 19981112 Richard Gooch + Added DEVFS_FL_REMOVABLE flag. + v0.53 + 19981114 Richard Gooch + Only call on first call to + . + v0.54 + 19981205 Richard Gooch + Updated for VFS change in 2.1.131. + v0.55 + 19981218 Richard Gooch + Created . + 19981220 Richard Gooch + Check for partitions on removable media in . + v0.56 + 19990118 Richard Gooch + Added support for registering regular files. + Created . + Update devfs inodes from entries if not changed through FS. + v0.57 + 19990124 Richard Gooch + Fixed to only initialise temporary inodes. + Trap for NULL fops in . + Return -ENODEV in for non-driver inodes. + v0.58 + 19990126 Richard Gooch + Switched from PATH_MAX to DEVFS_PATHLEN. + v0.59 + 19990127 Richard Gooch + Created "nottycompat" boot option. + v0.60 + 19990318 Richard Gooch + Fixed to not overrun event buffer. + v0.61 + 19990329 Richard Gooch + Created . + v0.62 + 19990330 Richard Gooch + Don't return unregistred entries in . + Panic in if entry unregistered. + 19990401 Richard Gooch + Don't panic in for duplicates. + v0.63 + 19990402 Richard Gooch + Don't unregister already unregistered entries in . + v0.64 + 19990510 Richard Gooch + Disable warning messages when unable to read partition table for + removable media. + v0.65 + 19990512 Richard Gooch + Updated for VFS change in 2.3.1-pre1. + Created "oops-on-panic" boot option. + Improved debugging in and . + v0.66 + 19990519 Richard Gooch + Added documentation for some functions. + 19990525 Richard Gooch + Removed "oops-on-panic" boot option: now always Oops. + v0.67 + 19990531 Richard Gooch + Improved debugging in . + v0.68 + 19990604 Richard Gooch + Added "diunlink" and "nokmod" boot options. + Removed superfluous warning message in . + v0.69 + 19990611 Richard Gooch + Took account of change to . + v0.70 + 19990614 Richard Gooch + Created separate event queue for each mounted devfs. + Removed . + Created new ioctl()s. + Incremented devfsd protocol revision to 3. + Fixed bug when re-creating directories: contents were lost. + Block access to inodes until devfsd updates permissions. + 19990615 Richard Gooch + Support 2.2.x kernels. + v0.71 + 19990623 Richard Gooch + Switched to sending process uid/gid to devfsd. + Renamed to . + Added DEVFSD_NOTIFY_LOOKUP event. + 19990624 Richard Gooch + Added DEVFSD_NOTIFY_CHANGE event. + Incremented devfsd protocol revision to 4. + v0.72 + 19990713 Richard Gooch + Return EISDIR rather than EINVAL for read(2) on directories. + v0.73 + 19990809 Richard Gooch + Changed to new __init scheme. + v0.74 + 19990901 Richard Gooch + Changed remaining function declarations to new __init scheme. + v0.75 + 19991013 Richard Gooch + Created , , + and . + Added <> parameter to , , + and . + Work sponsored by SGI. + v0.76 + 19991017 Richard Gooch + Allow multiple unregistrations. + Work sponsored by SGI. + v0.77 + 19991026 Richard Gooch + Added major and minor number to devfsd protocol. + Incremented devfsd protocol revision to 5. + Work sponsored by SGI. + v0.78 + 19991030 Richard Gooch + Support info pointer for all devfs entry types. + Added <> parameter to and + . + Work sponsored by SGI. + v0.79 + 19991031 Richard Gooch + Support "../" when searching devfs namespace. + Work sponsored by SGI. + v0.80 + 19991101 Richard Gooch + Created . + Work sponsored by SGI. + v0.81 + 19991103 Richard Gooch + Exported . + Work sponsored by SGI. + v0.82 + 19991104 Richard Gooch + Removed unused . + 19991105 Richard Gooch + Do not hide entries from devfsd or children. + Removed DEVFS_ FL_TTY_COMPAT flag. + Removed "nottycompat" boot option. + Removed . + Work sponsored by SGI. + v0.83 + 19991107 Richard Gooch + Added DEVFS_ FL_WAIT flag. + Work sponsored by SGI. + v0.84 + 19991107 Richard Gooch + Support new "disc" naming scheme in . + Allow NULL fops in . + Work sponsored by SGI. + v0.85 + 19991110 Richard Gooch + Fall back to major table if NULL fops given to . + Work sponsored by SGI. + v0.86 + 19991204 Richard Gooch + Support fifos when unregistering. + Work sponsored by SGI. + v0.87 + 19991209 Richard Gooch + Removed obsolete DEVFS_ FL_COMPAT and DEVFS_ FL_TOLERANT flags. + Work sponsored by SGI. + v0.88 + 19991214 Richard Gooch + Removed kmod support. + Work sponsored by SGI. + v0.89 + 19991216 Richard Gooch + Improved debugging in . + Ensure dentries created by devfsd will be cleaned up. + Work sponsored by SGI. + v0.90 + 20000504 Richard Gooch + Set inode->i_size to correct size for symlinks. + Don't create missing directories in . + Work sponsored by SGI. + v0.90a + 20000609 Christian Czezatke + Ported Richard Gooch's DEVFS_FL_NO_PERSISTENCE option + (devfs v16[12]) back to 2.2 branch to fix Unix98 PTY handling. + Removed unnecessary call to in + . + v0.90b + 20000702 Richard Gooch + Changed interface to . + Work sponsored by SGI. + v0.90c + 20010202 Richard Gooch + Ported to kernel 2.2.18. + v0.90d + 20011203 Richard Gooch + Added DEVFSD_NOTIFY_DELETE event. + Removed unused DEVFS_ FL_SHOW_UNREG flag. + Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from + Switched to process group check for devfsd privileges. + v0.90e +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVFS_VERSION "0.90e (20011203)" + +#ifndef DEVFS_NAME +# define DEVFS_NAME "devfs" +#endif + +/* Compatibility for 2.2.x kernel series */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,6)) +# define D_ALLOC_ROOT(inode) d_alloc_root (inode, NULL) +#else +# define D_ALLOC_ROOT(inode) d_alloc_root (inode) +#endif + +#define INODE_TABLE_INC 250 +#define FIRST_INODE 1 + +#define STRING_LENGTH 256 + +#define MIN_DEVNUM 36864 /* Use major numbers 144 */ +#define MAX_DEVNUM 61439 /* through 239, inclusive */ + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#define IS_HIDDEN(de) ( (de)->hide && !is_devfsd_or_child(fs_info) ) + +#define DEBUG_NONE 0x00000 +#define DEBUG_MODULE_LOAD 0x00001 +#define DEBUG_REGISTER 0x00002 +#define DEBUG_UNREGISTER 0x00004 +#define DEBUG_SET_FLAGS 0x00008 +#define DEBUG_S_PUT 0x00010 +#define DEBUG_I_LOOKUP 0x00020 +#define DEBUG_I_CREATE 0x00040 +#define DEBUG_I_READ 0x00080 +#define DEBUG_I_WRITE 0x00100 +#define DEBUG_I_UNLINK 0x00200 +#define DEBUG_I_RLINK 0x00400 +#define DEBUG_I_FLINK 0x00800 +#define DEBUG_I_MKNOD 0x01000 +#define DEBUG_F_READDIR 0x02000 +#define DEBUG_D_DELETE 0x04000 +#define DEBUG_D_RELEASE 0x08000 +#define DEBUG_D_IPUT 0x10000 +#define DEBUG_ALL (DEBUG_MODULE_LOAD | DEBUG_REGISTER | \ + DEBUG_SET_FLAGS | DEBUG_I_LOOKUP | \ + DEBUG_I_UNLINK | DEBUG_I_MKNOD | \ + DEBUG_D_RELEASE | DEBUG_D_IPUT) +#define DEBUG_DISABLED DEBUG_NONE + +#define OPTION_NONE 0x00 +#define OPTION_NOMOUNT 0x01 +#define OPTION_ONLY 0x02 + +#define OOPS(format, args...) {printk (format, ## args); \ + printk ("Forcing Oops\n"); \ + *(int *) 0 = 0;} + +struct directory_type +{ + struct devfs_entry *first; + struct devfs_entry *last; + unsigned int num_removable; +}; + +struct file_type +{ + unsigned long size; +}; + +struct device_type +{ + unsigned short major; + unsigned short minor; +}; + +struct fcb_type /* File, char, block type */ +{ + uid_t default_uid; + gid_t default_gid; + struct file_operations *fops; + union + { + struct file_type file; + struct device_type device; + } + u; + unsigned char auto_owner:1; + unsigned char aopen_notify:1; + unsigned char removable:1; /* Belongs in device_type, but save space */ + unsigned char open:1; /* Not entirely correct */ +}; + +struct symlink_type +{ + unsigned int length; /* Not including the NULL-termimator */ + char *linkname; /* This is NULL-terminated */ +}; + +struct fifo_type +{ + uid_t uid; + gid_t gid; +}; + +struct devfs_entry +{ + void *info; + union + { + struct directory_type dir; + struct fcb_type fcb; + struct symlink_type symlink; + struct fifo_type fifo; + } + u; + struct devfs_entry *prev; /* Previous entry in the parent directory */ + struct devfs_entry *next; /* Next entry in the parent directory */ + struct devfs_entry *parent; /* The parent directory */ + struct devfs_entry *slave; /* Another entry to unregister */ + struct devfs_inode *first_inode; + struct devfs_inode *last_inode; + umode_t mode; + unsigned short namelen; /* I think 64k+ filenames are a way off... */ + unsigned char registered:1; + unsigned char hide:1; + unsigned char no_persistence:1; + char name[1]; /* This is just a dummy: the allocated array is + bigger. This is NULL-terminated */ +}; + +/* The root of the device tree */ +static struct devfs_entry *root_entry = NULL; + +struct devfs_inode /* This structure is for "persistent" inode storage */ +{ + time_t atime; + time_t mtime; + time_t ctime; + unsigned int ino; /* Inode number as seen in the VFS */ + struct devfs_entry *de; + struct fs_info *fs_info; + struct devfs_inode *prev; /* This pair are used to associate a list of */ + struct devfs_inode *next; /* inodes (one per FS) for a devfs entry */ + struct dentry *dentry; +#ifdef CONFIG_DEVFS_TUNNEL + struct dentry *covered; +#endif + umode_t mode; + uid_t uid; + gid_t gid; + nlink_t nlink; +}; + +struct devfsd_buf_entry +{ + void *data; + unsigned int type; + umode_t mode; + uid_t uid; + gid_t gid; +}; + +struct fs_info /* This structure is for each mounted devfs */ +{ + unsigned int num_inodes; /* Number of inodes created */ + unsigned int table_size; /* Size of the inode pointer table */ + struct devfs_inode **table; + struct super_block *sb; + volatile struct devfsd_buf_entry *devfsd_buffer; + volatile unsigned int devfsd_buf_in; + volatile unsigned int devfsd_buf_out; + volatile int devfsd_sleeping; + volatile int devfsd_buffer_in_use; + volatile struct task_struct *devfsd_task; + volatile pid_t devfsd_pgrp; + volatile struct file *devfsd_file; + volatile unsigned long devfsd_event_mask; + atomic_t devfsd_overrun_count; + wait_queue_head_t devfsd_wait_queue; + wait_queue_head_t revalidate_wait_queue; + struct fs_info *prev; + struct fs_info *next; + unsigned char require_explicit:1; +}; + +static struct fs_info *first_fs = NULL; +static struct fs_info *last_fs = NULL; +static unsigned int next_devnum_char = MIN_DEVNUM; +static unsigned int next_devnum_block = MIN_DEVNUM; +static const int devfsd_buf_size = PAGE_SIZE / sizeof(struct devfsd_buf_entry); +#ifdef CONFIG_DEVFS_DEBUG +# ifdef MODULE +unsigned int devfs_debug = DEBUG_NONE; +# else +static unsigned int devfs_debug_init __initdata = DEBUG_NONE; +static unsigned int devfs_debug = DEBUG_NONE; +# endif +#endif +static unsigned int boot_options = OPTION_NONE; + +/* Forward function declarations */ +static struct devfs_entry *search_for_entry (struct devfs_entry *dir, + const char *name, + unsigned int namelen, int mkdir, + int mkfile, int *is_new, + int traverse_symlink); +static ssize_t devfsd_read (struct file *file, char *buf, size_t len, + loff_t *ppos); +static int devfsd_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int devfsd_close (struct inode *inode, struct file *file); + + +/* Devfs daemon file operations */ +static struct file_operations devfsd_fops = +{ + read: devfsd_read, + ioctl: devfsd_ioctl, + release: devfsd_close, +}; + + +/* Support functions follow */ + +static struct devfs_entry *search_for_entry_in_dir (struct devfs_entry *parent, + const char *name, + unsigned int namelen, + int traverse_symlink) +/* [SUMMARY] Search for a devfs entry inside another devfs entry. + The parent devfs entry. + The name of the entry. + The number of characters in <>. + If TRUE then the entry is traversed if it is a symlink. + [RETURNS] A pointer to the entry on success, else NULL. +*/ +{ + struct devfs_entry *curr; + + if ( !S_ISDIR (parent->mode) ) + { + printk ("%s: entry is not a directory\n", DEVFS_NAME); + return NULL; + } + for (curr = parent->u.dir.first; curr != NULL; curr = curr->next) + { + if (curr->namelen != namelen) continue; + if (memcmp (curr->name, name, namelen) == 0) break; + /* Not found: try the next one */ + } + if (curr == NULL) return NULL; + if (!S_ISLNK (curr->mode) || !traverse_symlink) return curr; + /* Need to follow the link: this is a stack chomper */ + return search_for_entry (parent, + curr->u.symlink.linkname, curr->u.symlink.length, + FALSE, FALSE, NULL, TRUE); + return curr; +} /* End Function search_for_entry_in_dir */ + +static struct devfs_entry *create_entry (struct devfs_entry *parent, + const char *name,unsigned int namelen) +{ + struct devfs_entry *new; + + if ( name && (namelen < 1) ) namelen = strlen (name); + if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL ) + return NULL; + memset (new, 0, sizeof *new + namelen); + new->parent = parent; + if (name) memcpy (new->name, name, namelen); + new->namelen = namelen; + if (parent == NULL) return new; + new->prev = parent->u.dir.last; + /* Insert into the parent directory's list of children */ + if (parent->u.dir.first == NULL) parent->u.dir.first = new; + else parent->u.dir.last->next = new; + parent->u.dir.last = new; + return new; +} /* End Function create_entry */ + +static struct devfs_entry *get_root_entry (void) +/* [SUMMARY] Get the root devfs entry. + [RETURNS] The root devfs entry on success, else NULL. +*/ +{ + struct devfs_entry *new; + + /* Always ensure the root is created */ + if (root_entry != NULL) return root_entry; + if ( ( root_entry = create_entry (NULL, NULL, 0) ) == NULL ) return NULL; + root_entry->registered = TRUE; + root_entry->mode = S_IFDIR; + /* And create the entry for ".devfsd" */ + if ( ( new = create_entry (root_entry, ".devfsd", 0) ) == NULL ) + return NULL; + new->registered = TRUE; + new->u.fcb.u.device.major = next_devnum_char >> 8; + new->u.fcb.u.device.minor = next_devnum_char & 0xff; + ++next_devnum_char; + new->mode = S_IFCHR | S_IRUSR | S_IWUSR; + new->u.fcb.default_uid = 0; + new->u.fcb.default_gid = 0; + new->u.fcb.fops = &devfsd_fops; + return root_entry; +} /* End Function get_root_entry */ + +static struct devfs_entry *search_for_entry (struct devfs_entry *dir, + const char *name, + unsigned int namelen, int mkdir, + int mkfile, int *is_new, + int traverse_symlink) +/* [SUMMARY] Search for an entry in the devfs tree. + The parent directory to search from. If this is NULL the root is used + The name of the entry. + The number of characters in <>. + If TRUE intermediate directories are created as needed. + If TRUE the file entry is created if it doesn't exist. + If the returned entry was newly made, TRUE is written here. If + this is NULL nothing is written here. + If TRUE then symbolic links are traversed. + [NOTE] If the entry is created, then it will be in the unregistered state. + [RETURNS] A pointer to the entry on success, else NULL. +*/ +{ + int len; + const char *subname, *stop, *ptr; + struct devfs_entry *entry; + + if (is_new) *is_new = FALSE; + if (dir == NULL) dir = get_root_entry (); + if (dir == NULL) return NULL; + /* Extract one filename component */ + subname = name; + stop = name + namelen; + while (subname < stop) + { + /* Search for a possible '/' */ + for (ptr = subname; (ptr < stop) && (*ptr != '/'); ++ptr); + if (ptr >= stop) + { + /* Look for trailing component */ + len = stop - subname; + entry = search_for_entry_in_dir (dir, subname, len, + traverse_symlink); + if (entry != NULL) return entry; + if (!mkfile) return NULL; + entry = create_entry (dir, subname, len); + if (entry && is_new) *is_new = TRUE; + return entry; + } + /* Found '/': search for directory */ + if (strncmp (subname, "../", 3) == 0) + { + /* Going up */ + dir = dir->parent; + if (dir == NULL) return NULL; /* Cannot escape from devfs */ + subname += 3; + continue; + } + len = ptr - subname; + entry = search_for_entry_in_dir (dir, subname, len, traverse_symlink); + if (!entry && !mkdir) return NULL; + if (entry == NULL) + { + /* Make it */ + if ( ( entry = create_entry (dir, subname, len) ) == NULL ) + return NULL; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + if (is_new) *is_new = TRUE; + } + if ( !S_ISDIR (entry->mode) ) + { + printk ("%s: existing non-directory entry\n", DEVFS_NAME); + return NULL; + } + /* Ensure an unregistered entry is re-registered and visible */ + entry->registered = TRUE; + entry->hide = FALSE; + subname = ptr + 1; + dir = entry; + } + return NULL; +} /* End Function search_for_entry */ + +static struct devfs_entry *find_by_dev (struct devfs_entry *dir, + unsigned int major, unsigned int minor, + char type) +/* [SUMMARY] Find a devfs entry in a directory. + The major number to search for. + The minor number to search for. + The type of special file to search for. This may be either + DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. + [RETURNS] The devfs_entry pointer on success, else NULL. +*/ +{ + struct devfs_entry *entry, *de; + + if (dir == NULL) return NULL; + if ( !S_ISDIR (dir->mode) ) + { + printk ("%s: find_by_dev(): not a directory\n", DEVFS_NAME); + return NULL; + } + /* First search files in this directory */ + for (entry = dir->u.dir.first; entry != NULL; entry = entry->next) + { + if ( !S_ISCHR (entry->mode) && !S_ISBLK (entry->mode) ) continue; + if ( S_ISCHR (entry->mode) && (type != DEVFS_SPECIAL_CHR) ) continue; + if ( S_ISBLK (entry->mode) && (type != DEVFS_SPECIAL_BLK) ) continue; + if ( (entry->u.fcb.u.device.major == major) && + (entry->u.fcb.u.device.minor == minor) ) return entry; + /* Not found: try the next one */ + } + /* Now recursively search the subdirectories: this is a stack chomper */ + for (entry = dir->u.dir.first; entry != NULL; entry = entry->next) + { + if ( !S_ISDIR (entry->mode) ) continue; + de = find_by_dev (entry, major, minor, type); + if (de) return de; + } + return NULL; +} /* End Function find_by_dev */ + +static struct devfs_entry *find_entry (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int major, unsigned int minor, + char type, int traverse_symlink) +/* [SUMMARY] Find a devfs entry. + The handle to the parent devfs directory entry. If this is NULL the + name is relative to the root of the devfs. + The name of the entry. This is ignored if <> is not NULL. + The number of characters in <>, not including a NULL + terminator. If this is 0, then <> must be NULL-terminated and the + length is computed internally. + The major number. This is used if <> and <> are NULL. + The minor number. This is used if <> and <> are NULL. + [NOTE] If <> and <> are both 0, searching by major and minor + numbers is disabled. + The type of special file to search for. This may be either + DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. + If TRUE then symbolic links are traversed. + [RETURNS] The devfs_entry pointer on success, else NULL. +*/ +{ + struct devfs_entry *entry; + + if (name != NULL) + { + if (namelen < 1) namelen = strlen (name); + if (name[0] == '/') + { + /* Skip leading pathname component */ + if (namelen < 2) + { + printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name); + return NULL; + } + for (++name, --namelen; (*name != '/') && (namelen > 0); + ++name, --namelen); + if (namelen < 2) + { + printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name); + return NULL; + } + ++name; + --namelen; + } + entry = search_for_entry (dir, name, namelen, FALSE, FALSE, NULL, + traverse_symlink); + if (entry != NULL) return entry; + } + /* Have to search by major and minor: slow */ + if ( (major == 0) && (minor == 0) ) return NULL; + return find_by_dev (root_entry, major, minor, type); +} /* End Function find_entry */ + +static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode) +{ + struct fs_info *fs_info; + + if (inode == NULL) return NULL; + if (inode->i_ino < FIRST_INODE) return NULL; + fs_info = inode->i_sb->u.generic_sbp; + if (fs_info == NULL) return NULL; + if (inode->i_ino - FIRST_INODE >= fs_info->num_inodes) return NULL; + return fs_info->table[inode->i_ino - FIRST_INODE]; +} /* End Function get_devfs_inode_from_vfs_inode */ + +static void free_dentries (struct devfs_entry *de) +/* [SUMMARY] Free the dentries for a device entry and invalidate inodes. + The entry. + [RETURNS] Nothing. +*/ +{ + struct devfs_inode *di; + struct dentry *dentry; + + for (di = de->first_inode; di != NULL; di = di->next) + { + dentry = di->dentry; + if (dentry != NULL) + { + dget (dentry); + di->dentry = NULL; + /* Forcefully remove the inode */ + if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0; + d_drop (dentry); + dput (dentry); + } + } +} /* End Function free_dentries */ + +static int is_devfsd_or_child (struct fs_info *fs_info) +/* [SUMMARY] Test if the current process is devfsd or one of its children. + The filesystem information. + [RETURNS] TRUE if devfsd or child, else FALSE. +*/ +{ + if (current == fs_info->devfsd_task) return (TRUE); + if (current->pgrp == fs_info->devfsd_pgrp) return (TRUE); + return (FALSE); +} /* End Function is_devfsd_or_child */ + +static inline int devfsd_queue_empty (struct fs_info *fs_info) +/* [SUMMARY] Test if devfsd has work pending in its event queue. + The filesystem information. + [RETURNS] TRUE if the queue is empty, else FALSE. +*/ +{ + return (fs_info->devfsd_buf_out == fs_info->devfsd_buf_in) ? TRUE : FALSE; +} /* End Function devfsd_queue_empty */ + +static int wait_for_devfsd_finished (struct fs_info *fs_info) +/* [SUMMARY] Wait for devfsd to finish processing its event queue. + The filesystem information. + [RETURNS] TRUE if no more waiting will be required, else FALSE. +*/ +{ + DECLARE_WAITQUEUE (wait, current); + + if (fs_info->devfsd_task == NULL) return (TRUE); + if (devfsd_queue_empty (fs_info) && fs_info->devfsd_sleeping) return TRUE; + if ( is_devfsd_or_child (fs_info) ) return (FALSE); + add_wait_queue (&fs_info->revalidate_wait_queue, &wait); + current->state = TASK_UNINTERRUPTIBLE; + if (!devfsd_queue_empty (fs_info) || !fs_info->devfsd_sleeping) + if (fs_info->devfsd_task) schedule(); + remove_wait_queue (&fs_info->revalidate_wait_queue, &wait); + current->state = TASK_RUNNING; + return (TRUE); +} /* End Function wait_for_devfsd_finished */ + +static int devfsd_notify_one (void *data, unsigned int type, umode_t mode, + uid_t uid, gid_t gid, struct fs_info *fs_info) +/* [SUMMARY] Notify a single devfsd daemon of a change. + Data to be passed. + The type of change. + The mode of the entry. + The user ID. + The group ID. + The filesystem info. + [RETURNS] TRUE if an event was queued and devfsd woken up, else FALSE. +*/ +{ + unsigned int next_pos; + unsigned long flags; + struct devfsd_buf_entry *entry; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + + if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE); + next_pos = fs_info->devfsd_buf_in + 1; + if (next_pos >= devfsd_buf_size) next_pos = 0; + if (next_pos == fs_info->devfsd_buf_out) + { + /* Running up the arse of the reader: drop it */ + atomic_inc (&fs_info->devfsd_overrun_count); + return (FALSE); + } + spin_lock_irqsave (&lock, flags); + fs_info->devfsd_buffer_in_use = TRUE; + next_pos = fs_info->devfsd_buf_in + 1; + if (next_pos >= devfsd_buf_size) next_pos = 0; + entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer + + fs_info->devfsd_buf_in; + entry->data = data; + entry->type = type; + entry->mode = mode; + entry->uid = uid; + entry->gid = gid; + fs_info->devfsd_buf_in = next_pos; + fs_info->devfsd_buffer_in_use = FALSE; + spin_unlock_irqrestore (&lock, flags); + wake_up_interruptible (&fs_info->devfsd_wait_queue); + return (TRUE); +} /* End Function devfsd_notify_one */ + +static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) +/* [SUMMARY] Notify all devfsd daemons of a change. + The devfs entry that has changed. + The type of change event. + If TRUE, the functions waits for all daemons to finish processing + the event. + [RETURNS] Nothing. +*/ +{ + struct fs_info *fs_info; + + for (fs_info = first_fs; fs_info != NULL; fs_info = fs_info->next) + { + if (devfsd_notify_one (de, type, de->mode, current->euid, + current->egid, fs_info) && wait) + wait_for_devfsd_finished (fs_info); + } +} /* End Function devfsd_notify */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, + unsigned int flags, + unsigned int major, unsigned int minor, + umode_t mode, + struct file_operations *fops, void *info) +/* [SUMMARY] Register a device entry. + The handle to the parent devfs directory entry. If this is NULL the + new name is relative to the root of the devfs. + The name of the entry. + A set of bitwise-ORed flags (DEVFS_FL_*). + The major number. Not needed for regular files. + The minor number. Not needed for regular files. + The default file mode. + The <> structure. This must not be externally + deallocated. + An arbitrary pointer which will be written to the <> + field of the <> structure passed to the device driver. You can set + this to whatever you like, and change it once the file is opened (the next + file opened will not see this change). + [RETURNS] A handle which may later be used in a call to + []. On failure NULL is returned. +*/ +{ + int is_new; + struct devfs_entry *de; + + if (name == NULL) + { + printk ("%s: devfs_register(): NULL name pointer\n", DEVFS_NAME); + return NULL; + } + if (fops == NULL) + { + if ( S_ISCHR (mode) ) fops = get_chrfops (major, 0); + else if ( S_ISBLK (mode) ) fops = get_blkfops (major); + if (fops == NULL) + { + printk ("%s: devfs_register(%s): NULL fops pointer\n", + DEVFS_NAME, name); + return NULL; + } + printk ("%s: devfs_register(%s): NULL fops, got %p from major table\n", + DEVFS_NAME, name, fops); + } + if ( S_ISDIR (mode) ) + { + printk("%s: devfs_register(%s): creating directories is not allowed\n", + DEVFS_NAME, name); + return NULL; + } + if ( S_ISLNK (mode) ) + { + printk ("%s: devfs_register(%s): creating symlinks is not allowed\n", + DEVFS_NAME, name); + return NULL; + } + if ( S_ISCHR (mode) && (flags & DEVFS_FL_AUTO_DEVNUM) ) + { + if (next_devnum_char >= MAX_DEVNUM) + { + printk ("%s: devfs_register(%s): exhausted char device numbers\n", + DEVFS_NAME, name); + return NULL; + } + major = next_devnum_char >> 8; + minor = next_devnum_char & 0xff; + ++next_devnum_char; + } + if ( S_ISBLK (mode) && (flags & DEVFS_FL_AUTO_DEVNUM) ) + { + if (next_devnum_block >= MAX_DEVNUM) + { + printk ("%s: devfs_register(%s): exhausted block device numbers\n", + DEVFS_NAME, name); + return NULL; + } + major = next_devnum_block >> 8; + minor = next_devnum_block & 0xff; + ++next_devnum_block; + } + de = search_for_entry (dir, name, strlen (name), TRUE, TRUE, &is_new, + FALSE); + if (de == NULL) + { + printk ("%s: devfs_register(): could not create entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_REGISTER) + printk ("%s: devfs_register(%s): de: %p %s\n", + DEVFS_NAME, name, de, is_new ? "new" : "existing"); +#endif + if (!is_new) + { + /* Existing entry */ + if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && + !S_ISREG (de->mode) ) + { + printk ("%s: devfs_register(): existing non-device/file entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } + if (de->registered) + { + printk("%s: devfs_register(): device already registered: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } + /* If entry already exists free any dentries associated with it */ + if (de->registered) free_dentries (de); + } + de->registered = TRUE; + if ( S_ISCHR (mode) || S_ISBLK (mode) ) + { + de->u.fcb.u.device.major = major; + de->u.fcb.u.device.minor = minor; + } + else if ( S_ISREG (mode) ) de->u.fcb.u.file.size = 0; + else + { + printk ("%s: devfs_register(): illegal mode: %x\n", + DEVFS_NAME, mode); + return (NULL); + } + de->info = info; + de->mode = mode; + if (flags & DEVFS_FL_CURRENT_OWNER) + { + de->u.fcb.default_uid = current->uid; + de->u.fcb.default_gid = current->gid; + } + else + { + de->u.fcb.default_uid = 0; + de->u.fcb.default_gid = 0; + } + de->registered = TRUE; + de->u.fcb.fops = fops; + de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE; + de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE : FALSE; + if (flags & DEVFS_FL_REMOVABLE) + { + de->u.fcb.removable = TRUE; + ++de->parent->u.dir.num_removable; + } + de->u.fcb.open = FALSE; + de->no_persistence = (flags & DEVFS_FL_NO_PERSISTENCE) ? TRUE : FALSE; + de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE; + devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT); + return de; +} /* End Function devfs_register */ + +static void unregister (struct devfs_entry *de) +/* [SUMMARY] Unregister a device entry. + The entry to unregister. + [RETURNS] Nothing. +*/ +{ + struct devfs_entry *child; + + if ( (child = de->slave) != NULL ) + { + de->slave = NULL; /* Unhook first in case slave is parent directory */ + unregister (child); + } + if (de->registered) + { + devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED, 0); + free_dentries (de); + } + de->info = NULL; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + de->registered = FALSE; + de->u.fcb.fops = NULL; + return; + } + if ( S_ISLNK (de->mode) ) + { + de->registered = FALSE; + if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname); + de->u.symlink.linkname = NULL; + return; + } + if ( S_ISFIFO (de->mode) ) + { + de->registered = FALSE; + return; + } + if (!de->registered) return; + if ( !S_ISDIR (de->mode) ) + { + printk ("%s: unregister(): unsupported type\n", DEVFS_NAME); + return; + } + de->registered = FALSE; + /* Now recursively search the subdirectories: this is a stack chomper */ + for (child = de->u.dir.first; child != NULL; child = child->next) + { +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_UNREGISTER) + printk ("%s: unregister(): child->name: \"%s\" child: %p\n", + DEVFS_NAME, child->name, child); +#endif + unregister (child); + } +} /* End Function unregister */ + +/*PUBLIC_FUNCTION*/ +void devfs_unregister (devfs_handle_t de) +/* [SUMMARY] Unregister a device entry. + A handle previously created by [] or returned from + []. If this is NULL the routine does nothing. + [RETURNS] Nothing. +*/ +{ + if (de == NULL) return; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_UNREGISTER) + printk ("%s: devfs_unregister(): de->name: \"%s\" de: %p\n", + DEVFS_NAME, de->name, de); +#endif + unregister (de); +} /* End Function devfs_unregister */ + +/*PUBLIC_FUNCTION*/ +int devfs_mk_symlink (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int flags, + const char *link, unsigned int linklength, + devfs_handle_t *handle, void *info) +/* [SUMMARY] Create a symbolic link in the devfs namespace. + The handle to the parent devfs directory entry. If this is NULL the + new name is relative to the root of the devfs. + The name of the entry. + The number of characters in <>, not including a NULL + terminator. If this is 0, then <> must be NULL-terminated and the + length is computed internally. + A set of bitwise-ORed flags (DEVFS_FL_*). + The destination name. + The number of characters in <>, not including a NULL + terminator. If this is 0, then <> must be NULL-terminated and the + length is computed internally. + The handle to the symlink entry is written here. This may be NULL. + An arbitrary pointer which will be associated with the entry. + [RETURNS] 0 on success, else a negative error code is returned. +*/ +{ + int is_new; + char *newname; + struct devfs_entry *de; + + if (handle != NULL) *handle = NULL; + if (name == NULL) + { + printk ("%s: devfs_mk_symlink(): NULL name pointer\n", DEVFS_NAME); + return -EINVAL; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_REGISTER) + printk ("%s: devfs_mk_symlink(%s)\n", DEVFS_NAME, name); +#endif + if (namelen < 1) namelen = strlen (name); + if (link == NULL) + { + printk ("%s: devfs_mk_symlink(): NULL link pointer\n", DEVFS_NAME); + return -EINVAL; + } + if (linklength < 1) linklength = strlen (link); + de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE); + if (de == NULL) return -ENOMEM; + if (!S_ISLNK (de->mode) && de->registered) + { + printk ("%s: devfs_mk_symlink(): non-link entry already exists\n", + DEVFS_NAME); + return -EEXIST; + } + if (handle != NULL) *handle = de; + de->mode = S_IFLNK | S_IRUGO | S_IXUGO; + de->info = info; + de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE; + /* Note there is no need to fiddle the dentry cache if the symlink changes + as the symlink follow method is called every time it's needed */ + if ( de->registered && (linklength == de->u.symlink.length) ) + { + /* New link is same length as old link */ + if (memcmp (link, de->u.symlink.linkname, linklength) == 0) return 0; + return -EEXIST; /* Contents would change */ + } + /* Have to create/update */ + if (de->registered) return -EEXIST; + de->registered = TRUE; + if ( ( newname = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL ) + { + struct devfs_entry *parent = de->parent; + + if (!is_new) return -ENOMEM; + /* Have to clean up */ + if (de->prev == NULL) parent->u.dir.first = de->next; + else de->prev->next = de->next; + if (de->next == NULL) parent->u.dir.last = de->prev; + else de->next->prev = de->prev; + kfree (de); + return -ENOMEM; + } + if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname); + de->u.symlink.linkname = newname; + memcpy (de->u.symlink.linkname, link, linklength); + de->u.symlink.linkname[linklength] = '\0'; + de->u.symlink.length = linklength; + return 0; +} /* End Function devfs_mk_symlink */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, + unsigned int namelen, void *info) +/* [SUMMARY] Create a directory in the devfs namespace. + The handle to the parent devfs directory entry. If this is NULL the + new name is relative to the root of the devfs. + The name of the entry. + The number of characters in <>, not including a NULL + terminator. If this is 0, then <> must be NULL-terminated and the + length is computed internally. + An arbitrary pointer which will be associated with the entry. + [NOTE] Use of this function is optional. The [] function + will automatically create intermediate directories as needed. This function + is provided for efficiency reasons, as it provides a handle to a directory. + [RETURNS] A handle which may later be used in a call to + []. On failure NULL is returned. +*/ +{ + int is_new; + struct devfs_entry *de; + + if (name == NULL) + { + printk ("%s: devfs_mk_dir(): NULL name pointer\n", DEVFS_NAME); + return NULL; + } + if (namelen < 1) namelen = strlen (name); + de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE); + if (de == NULL) + { + printk ("%s: devfs_mk_dir(): could not create entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } + if (!S_ISDIR (de->mode) && de->registered) + { + printk ("%s: devfs_mk_dir(): existing non-directory entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_REGISTER) + printk ("%s: devfs_mk_dir(%s): de: %p %s\n", + DEVFS_NAME, name, de, is_new ? "new" : "existing"); +#endif + if (!S_ISDIR (de->mode) && !is_new) + { + /* Transmogrifying an old entry */ + de->u.dir.first = NULL; + de->u.dir.last = NULL; + } + de->mode = S_IFDIR | S_IRUGO | S_IXUGO; + de->info = info; + if (!de->registered) de->u.dir.num_removable = 0; + de->registered = TRUE; + de->hide = FALSE; + devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, 0); + return de; +} /* End Function devfs_mk_dir */ + +/*PUBLIC_FUNCTION*/ +int devfs_fill_file (struct inode *inode, struct file *file, devfs_handle_t de) +/* [SUMMARY] Fill a file structure. + The inode. If this is not NULL and has a non-NULL <> field + and <> is NULL, then this must refer to a valid, non-symlink inode + in a devfs. + The file. + A handle previously created by [] or returned from + []. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + if ( (de == NULL) && (inode != NULL) && (inode->i_sb != NULL) ) + { + /* Make sure this inode comes from the devfs */ + struct devfs_inode *di; + + if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return -ENODEV; + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENODEV; + de = di->de; + } + if (de == NULL) return -ENODEV; + if (!de->registered) return -ENODEV; + if (de->u.fcb.fops == NULL) return -ENODEV; + file->f_op = de->u.fcb.fops; + file->private_data = de->info; + /* Initialise temporary inodes (don't touch real inodes) */ + if ( (inode != NULL) && (inode->i_sb == NULL) ) + { + inode->i_mode = de->mode; + inode->i_rdev = MKDEV (de->u.fcb.u.device.major, + de->u.fcb.u.device.minor); + } + return 0; +} /* End Function devfs_fill_file */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_find_handle (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int major, unsigned int minor, + char type, int traverse_symlinks) +/* [SUMMARY] Find the handle of a devfs entry. + The handle to the parent devfs directory entry. If this is NULL the + name is relative to the root of the devfs. + The name of the entry. + The number of characters in <>, not including a NULL + terminator. If this is 0, then <> must be NULL-terminated and the + length is computed internally. + The major number. This is used if <> is NULL. + The minor number. This is used if <> is NULL. + The type of special file to search for. This may be either + DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. + If TRUE then symlink entries in the devfs namespace are + traversed. Symlinks pointing out of the devfs namespace will cause a + failure. Symlink traversal consumes stack space. + [RETURNS] A handle which may later be used in a call to + [], [], or []. + On failure NULL is returned. +*/ +{ + devfs_handle_t de; + + if ( (name != NULL) && (name[0] == '\0') ) name = NULL; + de = find_entry (dir, name, namelen, major, minor, type, + traverse_symlinks); + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de; +} /* End Function devfs_find_handle */ + +/*PUBLIC_FUNCTION*/ +int devfs_get_flags (devfs_handle_t de, unsigned int *flags) +/* [SUMMARY] Get the flags for a devfs entry. + The handle to the device entry. + The flags are written here. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + unsigned int fl = 0; + + if (de == NULL) return -EINVAL; + if (!de->registered) return -ENODEV; + if (de->hide) fl |= DEVFS_FL_HIDE; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + if (de->u.fcb.auto_owner) fl |= DEVFS_FL_AUTO_OWNER; + if (de->u.fcb.aopen_notify) fl |= DEVFS_FL_AOPEN_NOTIFY; + if (de->u.fcb.removable) fl |= DEVFS_FL_REMOVABLE; + } + *flags = fl; + return 0; +} /* End Function devfs_get_flags */ + +/*PUBLIC_FUNCTION*/ +int devfs_set_flags (devfs_handle_t de, unsigned int flags) +/* [SUMMARY] Set the flags for a devfs entry. + The handle to the device entry. + The flags to set. Unset flags are cleared. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + if (de == NULL) return -EINVAL; + if (!de->registered) return -ENODEV; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_SET_FLAGS) + printk ("%s: devfs_set_flags(): de->name: \"%s\"\n", + DEVFS_NAME, de->name); +#endif + de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE; + de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE:FALSE; + if ( de->u.fcb.removable && !(flags & DEVFS_FL_REMOVABLE) ) + { + de->u.fcb.removable = FALSE; + --de->parent->u.dir.num_removable; + } + else if ( !de->u.fcb.removable && (flags & DEVFS_FL_REMOVABLE) ) + { + de->u.fcb.removable = TRUE; + ++de->parent->u.dir.num_removable; + } + } + return 0; +} /* End Function devfs_set_flags */ + +/*PUBLIC_FUNCTION*/ +int devfs_get_maj_min (devfs_handle_t de, unsigned int *major, + unsigned int *minor) +/* [SUMMARY] Get the major and minor numbers for a devfs entry. + The handle to the device entry. + The major number is written here. This may be NULL. + The minor number is written here. This may be NULL. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + if (de == NULL) return -EINVAL; + if (!de->registered) return -ENODEV; + if ( S_ISDIR (de->mode) ) return -EISDIR; + if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) ) return -EINVAL; + if (major != NULL) *major = de->u.fcb.u.device.major; + if (minor != NULL) *minor = de->u.fcb.u.device.minor; + return 0; +} /* End Function devfs_get_maj_min */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) +/* [SUMMARY] Get the devfs handle for a VFS inode. + The VFS inode. + [RETURNS] The devfs handle on success, else NULL. +*/ +{ + struct devfs_inode *di; + + if (!inode || !inode->i_sb) return NULL; + if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return NULL; + di = get_devfs_inode_from_vfs_inode (inode); + if (!di) return NULL; + return di->de; +} /* End Function devfs_get_handle_from_inode */ + +/*PUBLIC_FUNCTION*/ +int devfs_generate_path (devfs_handle_t de, char *path, int buflen) +/* [SUMMARY] Generate a pathname for an entry, relative to the devfs root. + The devfs entry. + The buffer to write the pathname to. The pathname and '\0' + terminator will be written at the end of the buffer. + The length of the buffer. + [RETURNS] The offset in the buffer where the pathname starts on success, + else a negative error code. +*/ +{ + int pos; + + if (de == NULL) return -EINVAL; + if (de->namelen >= buflen) return -ENAMETOOLONG; /* Must be first */ + if (de->parent == NULL) return buflen; /* Don't prepend root */ + pos = buflen - de->namelen - 1; + memcpy (path + pos, de->name, de->namelen); + path[buflen - 1] = '\0'; + for (de = de->parent; de->parent != NULL; de = de->parent) + { + if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG; + path[--pos] = '/'; + pos -= de->namelen; + memcpy (path + pos, de->name, de->namelen); + } + return pos; +} /* End Function devfs_generate_path */ + +/*PUBLIC_FUNCTION*/ +struct file_operations *devfs_get_fops (devfs_handle_t de) +/* [SUMMARY] Get the file operations for a devfs entry. + The handle to the device entry. + [RETURNS] A pointer to the file operations on success, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + return de->u.fcb.fops; + return NULL; +} /* End Function devfs_get_fops */ + +/*PUBLIC_FUNCTION*/ +int devfs_set_file_size (devfs_handle_t de, unsigned long size) +/* [SUMMARY] Set the file size for a devfs regular file. + The handle to the device entry. + The new file size. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + struct devfs_inode *di; + + if (de == NULL) return -EINVAL; + if (!de->registered) return -EINVAL; + if ( !S_ISREG (de->mode) ) return -EINVAL; + if (de->u.fcb.u.file.size == size) return 0; + de->u.fcb.u.file.size = size; + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->dentry == NULL) continue; + if (di->dentry->d_inode == NULL) continue; + di->dentry->d_inode->i_size = size; + } + return 0; +} /* End Function devfs_set_file_size */ + +/*PUBLIC_FUNCTION*/ +void *devfs_get_info (devfs_handle_t de) +/* [SUMMARY] Get the info pointer written to <> upon open. + The handle to the device entry. + [RETURNS] The info pointer. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de->info; +} /* End Function devfs_get_info */ + +/*PUBLIC_FUNCTION*/ +int devfs_set_info (devfs_handle_t de, void *info) +/* [SUMMARY] Set the info pointer written to <> upon open. + The handle to the device entry. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + if (de == NULL) return -EINVAL; + if (!de->registered) return -EINVAL; + de->info = info; + return 0; +} /* End Function devfs_set_info */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_parent (devfs_handle_t de) +/* [SUMMARY] Get the parent device entry. + The handle to the device entry. + [RETURNS] The parent device entry if it exists, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de->parent; +} /* End Function devfs_get_parent */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_first_child (devfs_handle_t de) +/* [SUMMARY] Get the first leaf node in a directory. + The handle to the device entry. + [RETURNS] The leaf node device entry if it exists, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + if ( !S_ISDIR (de->mode) ) return NULL; + return de->u.dir.first; +} /* End Function devfs_get_first_child */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_next_sibling (devfs_handle_t de) +/* [SUMMARY] Get the next sibling leaf node. for a device entry. + The handle to the device entry. + [RETURNS] The leaf node device entry if it exists, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de->next; +} /* End Function devfs_get_next_sibling */ + +/*PUBLIC_FUNCTION*/ +void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave) +/* [SUMMARY] Configure a devfs entry to be automatically unregistered. + The master devfs entry. Only one slave may be registered. + The devfs entry which will be automatically unregistered when the + master entry is unregistered. It is illegal to call [] on + this entry. + [RETURNS] Nothing. +*/ +{ + if (master == NULL) return; + if (master->slave != NULL) + { + /* Because of the dumbness of the layers above, ignore duplicates */ + if (master->slave == slave) return; + printk ("%s: devfs_auto_unregister(): only one slave allowed\n", + DEVFS_NAME); + OOPS (" master: \"%s\" old slave: \"%s\" new slave: \"%s\"\n", + master->name, master->slave->name, slave->name); + } + master->slave = slave; +} /* End Function devfs_auto_unregister */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master) +/* [SUMMARY] Get the slave entry which will be automatically unregistered. + The master devfs entry. + [RETURNS] The slave which will be unregistered when <> is + unregistered. +*/ +{ + if (master == NULL) return NULL; + return master->slave; +} /* End Function devfs_get_unregister_slave */ + +/*PUBLIC_FUNCTION*/ +int devfs_register_chrdev (unsigned int major, const char *name, + struct file_operations *fops) +/* [SUMMARY] Optionally register a conventional character driver. + [PURPOSE] This function will register a character driver provided the + "devfs=only" option was not provided at boot time. + The major number for the driver. + The name of the driver (as seen in /proc/devices). + The file_operations structure pointer. + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return register_chrdev (major, name, fops); +} /* End Function devfs_register_chrdev */ + +/*PUBLIC_FUNCTION*/ +int devfs_register_blkdev (unsigned int major, const char *name, + struct file_operations *fops) +/* [SUMMARY] Optionally register a conventional block driver. + [PURPOSE] This function will register a block driver provided the + "devfs=only" option was not provided at boot time. + The major number for the driver. + The name of the driver (as seen in /proc/devices). + The file_operations structure pointer. + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return register_blkdev (major, name, fops); +} /* End Function devfs_register_blkdev */ + +/*PUBLIC_FUNCTION*/ +int devfs_unregister_chrdev (unsigned int major, const char *name) +/* [SUMMARY] Optionally unregister a conventional character driver. + [PURPOSE] This function will unregister a character driver provided the + "devfs=only" option was not provided at boot time. + The major number for the driver. + The name of the driver (as seen in /proc/devices). + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return unregister_chrdev (major, name); +} /* End Function devfs_unregister_chrdev */ + +/*PUBLIC_FUNCTION*/ +int devfs_unregister_blkdev (unsigned int major, const char *name) +/* [SUMMARY] Optionally unregister a conventional block driver. + [PURPOSE] This function will unregister a block driver provided the + "devfs=only" option was not provided at boot time. + The major number for the driver. + The name of the driver (as seen in /proc/devices). + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return unregister_blkdev (major, name); +} /* End Function devfs_unregister_blkdev */ + +#ifndef MODULE + +/*UNPUBLISHED_FUNCTION*/ +static int __init devfs_setup (char *str) +/* [SUMMARY] Process kernel boot options. + The boot options after the "devfs=". + Unused. + [RETURNS] Nothing. +*/ +{ + while ( (*str != '\0') && !isspace (*str) ) + { +# ifdef CONFIG_DEVFS_DEBUG + if (strncmp (str, "dall", 4) == 0) + { + devfs_debug_init |= DEBUG_ALL; + str += 4; + } + else if (strncmp (str, "dmod", 4) == 0) + { + devfs_debug_init |= DEBUG_MODULE_LOAD; + str += 4; + } + else if (strncmp (str, "dreg", 4) == 0) + { + devfs_debug_init |= DEBUG_REGISTER; + str += 4; + } + else if (strncmp (str, "dunreg", 6) == 0) + { + devfs_debug_init |= DEBUG_UNREGISTER; + str += 6; + } + else if (strncmp (str, "diread", 6) == 0) + { + devfs_debug_init |= DEBUG_I_READ; + str += 6; + } + else if (strncmp (str, "dchange", 7) == 0) + { + devfs_debug_init |= DEBUG_SET_FLAGS; + str += 7; + } + else if (strncmp (str, "diwrite", 7) == 0) + { + devfs_debug_init |= DEBUG_I_WRITE; + str += 7; + } + else if (strncmp (str, "dimknod", 7) == 0) + { + devfs_debug_init |= DEBUG_I_MKNOD; + str += 7; + } + else if (strncmp (str, "dilookup", 8) == 0) + { + devfs_debug_init |= DEBUG_I_LOOKUP; + str += 8; + } + else if (strncmp (str, "diunlink", 8) == 0) + { + devfs_debug_init |= DEBUG_I_UNLINK; + str += 8; + } + else +# endif /* CONFIG_DEVFS_DEBUG */ + if (strncmp (str, "only", 4) == 0) + { + boot_options |= OPTION_ONLY; + str += 4; + } + else if (strncmp (str, "nomount", 7) == 0) + { + boot_options |= OPTION_NOMOUNT; + str += 7; + } + else + return 0; + if (*str != ',') return 0; + ++str; + } + return 1; +} /* End Function devfs_setup */ + +__setup("devfs=", devfs_setup); + +#endif /* !MODULE */ + +EXPORT_SYMBOL(devfs_register); +EXPORT_SYMBOL(devfs_unregister); +EXPORT_SYMBOL(devfs_mk_symlink); +EXPORT_SYMBOL(devfs_mk_dir); +EXPORT_SYMBOL(devfs_fill_file); +EXPORT_SYMBOL(devfs_find_handle); +EXPORT_SYMBOL(devfs_get_flags); +EXPORT_SYMBOL(devfs_set_flags); +EXPORT_SYMBOL(devfs_get_maj_min); +EXPORT_SYMBOL(devfs_get_handle_from_inode); +EXPORT_SYMBOL(devfs_generate_path); +EXPORT_SYMBOL(devfs_get_fops); +EXPORT_SYMBOL(devfs_set_file_size); +EXPORT_SYMBOL(devfs_get_info); +EXPORT_SYMBOL(devfs_set_info); +EXPORT_SYMBOL(devfs_get_parent); +EXPORT_SYMBOL(devfs_get_first_child); +EXPORT_SYMBOL(devfs_get_next_sibling); +EXPORT_SYMBOL(devfs_auto_unregister); +EXPORT_SYMBOL(devfs_get_unregister_slave); +EXPORT_SYMBOL(devfs_register_chrdev); +EXPORT_SYMBOL(devfs_register_blkdev); +EXPORT_SYMBOL(devfs_unregister_chrdev); +EXPORT_SYMBOL(devfs_unregister_blkdev); + +#ifdef CONFIG_DEVFS_DEBUG +MODULE_PARM(devfs_debug, "i"); +#endif + +static void update_devfs_inode_from_entry (struct devfs_inode *di) +{ + if (di == NULL) return; + if (di->de == NULL) + { + printk ("%s: update_devfs_inode_from_entry(): NULL entry\n", + DEVFS_NAME); + return; + } + if ( S_ISDIR (di->de->mode) ) + { + di->mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + di->uid = 0; + di->gid = 0; + } + else if ( S_ISLNK (di->de->mode) ) + { + di->mode = S_IFLNK | S_IRUGO | S_IXUGO; + di->uid = 0; + di->gid = 0; + } + else if ( S_ISFIFO (di->de->mode) ) + { + di->mode = di->de->mode; + di->uid = di->de->u.fifo.uid; + di->gid = di->de->u.fifo.gid; + } + else + { + if (di->de->u.fcb.auto_owner) + di->mode = (di->de->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; + else di->mode = di->de->mode; + di->uid = di->de->u.fcb.default_uid; + di->gid = di->de->u.fcb.default_gid; + } +} /* End Function update_devfs_inode_from_entry */ + +static struct devfs_inode *create_devfs_inode (struct devfs_entry *entry, + struct fs_info *fs_info) +/* [SUMMARY] Create a devfs inode entry. + The devfs entry to associate the new inode with. + The FS info. + [RETURNS] A pointer to the devfs inode on success, else NULL. +*/ +{ + struct devfs_inode *di, **table; + + /* First ensure table size is enough */ + if (fs_info->num_inodes >= fs_info->table_size) + { + if ( ( table = kmalloc (sizeof *table * + (fs_info->table_size + INODE_TABLE_INC), + GFP_KERNEL) ) == NULL ) return NULL; + fs_info->table_size += INODE_TABLE_INC; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_CREATE) + printk ("%s: create_devfs_inode(): grew inode table to: %u entries\n", + DEVFS_NAME, fs_info->table_size); +#endif + if (fs_info->table) + { + memcpy (table, fs_info->table, sizeof *table *fs_info->num_inodes); + kfree (fs_info->table); + } + fs_info->table = table; + } + if ( ( di = kmalloc (sizeof *di, GFP_KERNEL) ) == NULL ) return NULL; + memset (di, 0, sizeof *di); + di->ino = fs_info->num_inodes + FIRST_INODE; + di->nlink = 1; + fs_info->table[fs_info->num_inodes] = di; + ++fs_info->num_inodes; + di->de = entry; + di->fs_info = fs_info; + di->prev = entry->last_inode; + if (entry->first_inode == NULL) entry->first_inode = di; + else entry->last_inode->next = di; + entry->last_inode = di; + update_devfs_inode_from_entry (di); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_CREATE) + printk ("%s: create_devfs_inode(): new di(%u): %p\n", + DEVFS_NAME, di->ino, di); +#endif + return di; +} /* End Function create_devfs_inode */ + +static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, + const char *name, unsigned namelen, + char buf[STRING_LENGTH]) +/* [SUMMARY] Notify devfsd of an inode lookup. + The parent devfs entry. + The filesystem info. + The device name. + The number of characters in <>. + A working area that will be used. This must not go out of scope until + devfsd is idle again. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + int pos; + + if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) ) + return -ENOENT; + if ( is_devfsd_or_child (fs_info) ) return -ENOENT; + if (namelen >= STRING_LENGTH) return -ENAMETOOLONG; + memcpy (buf + STRING_LENGTH - namelen - 1, name, namelen); + buf[STRING_LENGTH - 1] = '\0'; + pos = devfs_generate_path (parent, buf, STRING_LENGTH - namelen - 1); + if (pos < 0) return pos; + buf[STRING_LENGTH - namelen - 2] = '/'; + if ( !devfsd_notify_one (buf + pos, DEVFSD_NOTIFY_LOOKUP, 0, + current->euid, current->egid, fs_info) ) + return -ENOENT; + /* Possible success */ + return 0; +} /* End Function try_modload */ + +static void delete_fs (struct fs_info *fs_info) +{ + unsigned int count; + struct devfs_inode *di; + struct devfs_entry *de; + + if (fs_info == NULL) return; + for (count = 0; count < fs_info->num_inodes; ++count) + { + /* Unhook this inode from the devfs tree */ + di = fs_info->table[count]; + de = di->de; + if (di->prev == NULL) de->first_inode = di->next; + else di->prev->next = di->next; + if (di->next == NULL) de->last_inode = di->prev; + else di->next->prev = di->prev; + memset (di, 0, sizeof *di); + kfree (di); + } + if (fs_info->table) kfree (fs_info->table); + if (fs_info->prev == NULL) first_fs = fs_info->next; + else fs_info->prev->next = fs_info->next; + if (fs_info->next == NULL) last_fs = fs_info->prev; + else fs_info->next->prev = fs_info->prev; + memset (fs_info, 0, sizeof *fs_info); + kfree (fs_info); +} /* End Function delete_fs */ + +static int check_disc_changed (struct devfs_entry *de) +/* [SUMMARY] Check if a removable disc was changed. + The device. + [RETURNS] 1 if the media was changed, else 0. +*/ +{ + int tmp; + kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor); + struct file_operations *fops = de->u.fcb.fops; + struct super_block * sb; + extern int warn_no_part; + + if (fops == NULL) return 0; + if (fops->check_media_change == NULL) return 0; + if ( !fops->check_media_change (dev) ) return 0; + printk ( KERN_DEBUG "VFS: Disk change detected on device %s\n", + kdevname (dev) ); + sb = get_super (dev); + if ( sb && invalidate_inodes (sb) ) + printk("VFS: busy inodes on changed media..\n"); + invalidate_buffers (dev); + /* Ugly hack to disable messages about unable to read partition table */ + tmp = warn_no_part; + warn_no_part = 0; + if (fops->revalidate) fops->revalidate (dev); + warn_no_part = tmp; + return 1; +} /* End Function check_disc_changed */ + +static void scan_dir_for_removable (struct devfs_entry *dir) +/* [SUMMARY] Scan a directory for removable media devices and check media. + The directory. + [RETURNS] Nothing. +*/ +{ + struct devfs_entry *de; + + if (dir->u.dir.num_removable < 1) return; + for (de = dir->u.dir.first; de != NULL; de = de->next) + { + if (!de->registered) continue; + if ( !S_ISBLK (de->mode) ) continue; + if (!de->u.fcb.removable) continue; + check_disc_changed (de); + } +} /* End Function scan_dir_for_removable */ + +static int get_removable_partition (struct devfs_entry *dir, const char *name, + unsigned int namelen) +/* [SUMMARY] Get removable media partition. + The parent directory. + The name of the entry. + The number of characters in <>. + [RETURNS] 1 if the media was changed, else 0. +*/ +{ + struct devfs_entry *de; + + for (de = dir->u.dir.first; de != NULL; de = de->next) + { + if (!de->registered) continue; + if ( !S_ISBLK (de->mode) ) continue; + if (!de->u.fcb.removable) continue; + if (strcmp (de->name, "disc") == 0) return check_disc_changed (de); + /* Support for names where the partition is appended to the disc name + */ + if (de->namelen >= namelen) continue; + if (strncmp (de->name, name, de->namelen) != 0) continue; + return check_disc_changed (de); + } + return 0; +} /* End Function get_removable_partition */ + + +/* Superblock operations follow */ + +extern struct inode_operations devfs_iops; + +static void devfs_read_inode (struct inode *inode) +{ + struct devfs_inode *di; + + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) + { + printk ("%s: read_inode(%d): VFS inode: %p NO devfs_inode\n", + DEVFS_NAME, (int) inode->i_ino, inode); + return; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_READ) + printk ("%s: read_inode(%d): VFS inode: %p devfs_inode: %p\n", + DEVFS_NAME, (int) inode->i_ino, inode, di); +#endif + inode->i_size = 0; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_op = &devfs_iops; + inode->i_rdev = NODEV; + if ( S_ISCHR (di->mode) || S_ISBLK (di->mode) ) + { + inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major, + di->de->u.fcb.u.device.minor); + } + else if ( S_ISFIFO (di->mode) ) init_fifo (inode); + else if ( S_ISREG (di->mode) ) inode->i_size = di->de->u.fcb.u.file.size; + else if ( S_ISLNK (di->mode) ) inode->i_size = di->de->u.symlink.length; + inode->i_mode = di->mode; + inode->i_uid = di->uid; + inode->i_gid = di->gid; + inode->i_atime = di->atime; + inode->i_mtime = di->mtime; + inode->i_ctime = di->ctime; + inode->i_nlink = di->nlink; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_READ) + printk ("%s: mode: 0%o uid: %d gid: %d\n", + DEVFS_NAME, (int) inode->i_mode, + (int) inode->i_uid, (int) inode->i_gid); +#endif +} /* End Function devfs_read_inode */ + +static void devfs_write_inode (struct inode *inode) +{ + int index; + struct devfs_inode *di; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + if (inode->i_ino < FIRST_INODE) return; + index = inode->i_ino - FIRST_INODE; + if (index >= fs_info->num_inodes) + { + printk ("%s: writing inode: %lu for which there is no entry!\n", + DEVFS_NAME, inode->i_ino); + return; + } + di = fs_info->table[index]; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_WRITE) + { + printk ("%s: write_inode(%d): VFS inode: %p devfs_inode: %p\n", + DEVFS_NAME, (int) inode->i_ino, inode, di); + printk ("%s: mode: 0%o uid: %d gid: %d\n", + DEVFS_NAME, (int) inode->i_mode, + (int) inode->i_uid, (int) inode->i_gid); + } +#endif + di->mode = inode->i_mode; + di->uid = inode->i_uid; + di->gid = inode->i_gid; + di->atime = inode->i_atime; + di->mtime = inode->i_mtime; + di->ctime = inode->i_ctime; +} /* End Function devfs_write_inode */ + +static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr) +{ + int retval; + struct devfs_inode *di; + struct inode *inode = dentry->d_inode; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENODEV; + retval = inode_change_ok (inode, iattr); + if (retval != 0) return retval; + inode_setattr (inode, iattr); + if ( ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) ) && + !is_devfsd_or_child (fs_info) ) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CHANGE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_notify_change */ + +static void devfs_put_super (struct super_block *sb) +{ + struct fs_info *fs_info = sb->u.generic_sbp; + +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_S_PUT) + printk ("%s: put_super(): devfs ptr: %p\n", DEVFS_NAME, fs_info); +#endif + sb->s_dev = 0; +#ifdef CONFIG_DEVFS_TUNNEL + dput (fs_info->table[0]->covered); +#endif + delete_fs (fs_info); + MOD_DEC_USE_COUNT; +} /* End Function devfs_put_super */ + +static int devfs_statfs (struct super_block *sb, struct statfs *buf,int bufsiz) +{ + struct statfs tmp; + + tmp.f_type = DEVFS_SUPER_MAGIC; + tmp.f_bsize = PAGE_SIZE / sizeof (long); + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user (buf, &tmp, bufsiz) ? -EFAULT : 0; +} /* End Function devfs_statfs */ + +static struct super_operations devfs_sops = +{ + read_inode: devfs_read_inode, + write_inode: devfs_write_inode, + notify_change: devfs_notify_change, + put_super: devfs_put_super, + statfs: devfs_statfs, +}; + +static struct inode *get_vfs_inode (struct super_block *sb, + struct devfs_inode *di, + struct dentry *dentry) +/* [SUMMARY] Get a VFS inode. + The super block. + The devfs inode. + The dentry to register with the devfs inode. + [RETURNS] The inode on success, else NULL. +*/ +{ + struct inode *inode; + + if (di->dentry != NULL) + { + printk ("%s: get_vfs_inode(%u): old di->dentry: %p \"%s\" new dentry: %p \"%s\"\n", + DEVFS_NAME, di->ino, di->dentry, di->dentry->d_name.name, + dentry, dentry->d_name.name); + printk (" old inode: %p\n", di->dentry->d_inode); + return NULL; + } + if ( ( inode = iget (sb, di->ino) ) == NULL ) return NULL; + di->dentry = dentry; + return inode; +} /* End Function get_vfs_inode */ + + +/* File operations for device entries follow */ + +static int devfs_read (struct file *file, char *buf, size_t len, loff_t *ppos) +{ + if ( S_ISDIR (file->f_dentry->d_inode->i_mode) ) return -EISDIR; + return -EINVAL; +} /* End Function devfs_read */ + +static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) +{ + int err, count; + int stored = 0; + struct fs_info *fs_info; + struct devfs_inode *di; + struct devfs_entry *parent, *de; + struct inode *inode = file->f_dentry->d_inode; + + if (inode == NULL) + { + printk ("%s: readdir(): NULL inode\n", DEVFS_NAME); + return -EBADF; + } + if ( !S_ISDIR (inode->i_mode) ) + { + printk ("%s: readdir(): inode is not a directory\n", DEVFS_NAME); + return -ENOTDIR; + } + fs_info = inode->i_sb->u.generic_sbp; + di = get_devfs_inode_from_vfs_inode (file->f_dentry->d_inode); + parent = di->de; + if ( (long) file->f_pos < 0 ) return -EINVAL; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_F_READDIR) + printk ("%s: readdir(): fs_info: %p pos: %ld\n", DEVFS_NAME, + fs_info, (long) file->f_pos); +#endif + switch ( (long) file->f_pos ) + { + case 0: + scan_dir_for_removable (parent); + err = (*filldir) (dirent, "..", 2, file->f_pos, + file->f_dentry->d_parent->d_inode->i_ino); + if (err == -EINVAL) break; + if (err < 0) return err; + file->f_pos++; + ++stored; + /* Fall through */ + case 1: + err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino); + if (err == -EINVAL) break; + if (err < 0) return err; + file->f_pos++; + ++stored; + /* Fall through */ + default: + /* Skip entries */ + count = file->f_pos - 2; + for (de = parent->u.dir.first; (de != NULL) && (count > 0); + de = de->next) + { + if ( IS_HIDDEN (de) ) continue; + if (!fs_info->require_explicit) + { + --count; + continue; + } + /* Must search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (fs_info == di->fs_info) break; + } + if (di != NULL) --count; + } + /* Now add all remaining entries */ + for (; de != NULL; de = de->next) + { + if ( IS_HIDDEN (de) ) continue; + /* Must search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (fs_info == di->fs_info) break; + } + if (di == NULL) + { + if (fs_info->require_explicit) continue; + /* Have to create the inode right now */ + di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + } + err = (*filldir) (dirent, de->name, de->namelen, + file->f_pos, di->ino); + if (err == -EINVAL) break; + if (err < 0) return err; + file->f_pos++; + ++stored; + } + break; + } + return stored; +} /* End Function devfs_readdir */ + +static int devfs_open (struct inode *inode, struct file *file) +{ + int err; + struct fcb_type *df; + struct devfs_inode *di; + struct dentry *dentry = file->f_dentry; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENODEV; + if ( S_ISDIR (di->de->mode) ) return 0; + df = &di->de->u.fcb; + if (!di->de->registered) return -ENODEV; + file->f_op = df->fops; + file->private_data = di->de->info; + if (file->f_op) + err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0; + else + { + /* Fallback to legacy scheme */ + if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file); + else if ( S_ISBLK (inode->i_mode) ) err = blkdev_open (inode, file); + else err = -ENODEV; + } + if (err < 0) return err; + /* Open was successful */ + df->open = TRUE; + if (dentry->d_count != 1) return 0; /* No fancy operations */ + /* This is the first open */ + if (df->auto_owner) + { + /* Change the ownership/protection */ + di->mode = (di->mode & ~S_IALLUGO) | (di->de->mode & S_IRWXUGO); + di->uid = current->euid; + di->gid = current->egid; + inode->i_mode = di->mode; + inode->i_uid = di->uid; + inode->i_gid = di->gid; + } + if ( df->aopen_notify && !is_devfsd_or_child (fs_info) ) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode, + current->euid, current->egid, fs_info); + return 0; +} /* End Function devfs_open */ + +static struct file_operations devfs_fops = +{ + read: devfs_read, + readdir: devfs_readdir, + open: devfs_open, +}; + + +/* Dentry operations for device entries follow */ + +static void devfs_d_release (struct dentry *dentry) +/* [SUMMARY] Callback for when a dentry is freed. +*/ +{ +#ifdef CONFIG_DEVFS_DEBUG + struct inode *inode = dentry->d_inode; + + if (devfs_debug & DEBUG_D_RELEASE) + printk ("%s: d_release(): dentry: %p inode: %p\n", + DEVFS_NAME, dentry, inode); +#endif +} /* End Function devfs_d_release */ + +static void devfs_d_iput (struct dentry *dentry, struct inode *inode) +/* [SUMMARY] Callback for when a dentry loses its inode. +*/ +{ + struct devfs_inode *di; + + di = get_devfs_inode_from_vfs_inode (inode); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_D_IPUT) + printk ("%s: d_iput(): dentry: %p inode: %p di: %p di->dentry: %p\n", + DEVFS_NAME, dentry, inode, di, di->dentry); +#endif + if (di->dentry == dentry) + { + di->dentry = NULL; +#ifdef CONFIG_DEVFS_TUNNEL + dput (di->covered); + di->covered = NULL; +#endif + } + iput (inode); +} /* End Function devfs_d_iput */ + +static void devfs_d_delete (struct dentry *dentry); + +static struct dentry_operations devfs_dops = +{ + d_delete: devfs_d_delete, + d_release: devfs_d_release, + d_iput: devfs_d_iput, +}; + +static int devfs_d_revalidate (struct dentry *dentry, int flags); + +static struct dentry_operations devfs_revalidate_dops = +{ + d_delete: devfs_d_delete, + d_release: devfs_d_release, + d_iput: devfs_d_iput, + d_revalidate: devfs_d_revalidate, +}; + +static void devfs_d_delete (struct dentry *dentry) +/* [SUMMARY] Callback for when all files for a dentry are closed. +*/ +{ + struct inode *inode = dentry->d_inode; + struct devfs_inode *di; + struct fs_info *fs_info; + + if (dentry->d_op == &devfs_revalidate_dops) dentry->d_op = &devfs_dops; + /* Unhash dentry if negative (has no inode) */ + if (inode == NULL) + { +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_D_DELETE) + printk ("%s: d_delete(): dropping negative dentry: %p\n", + DEVFS_NAME, dentry); +#endif + d_drop (dentry); + return; + } + fs_info = inode->i_sb->u.generic_sbp; + di = get_devfs_inode_from_vfs_inode (inode); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_D_DELETE) + printk ("%s: d_delete(): dentry: %p inode: %p devfs_inode: %p\n", + DEVFS_NAME, dentry, inode, di); +#endif + if (di == NULL) return; + if (di->de == NULL) return; + if ( !S_ISCHR (di->mode) && !S_ISBLK (di->mode) && !S_ISREG (di->mode) ) + return; + if (!di->de->u.fcb.open) return; + di->de->u.fcb.open = FALSE; + if (di->de->u.fcb.aopen_notify) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CLOSE, inode->i_mode, + current->euid, current->egid, fs_info); + if (!di->de->u.fcb.auto_owner) return; + /* Change the ownership/protection back */ + di->mode = (di->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; + di->uid = di->de->u.fcb.default_uid; + di->gid = di->de->u.fcb.default_gid; + inode->i_mode = di->mode; + inode->i_uid = di->uid; + inode->i_gid = di->gid; +} /* End Function devfs_d_delete */ + +static int devfs_d_revalidate (struct dentry *dentry, int flags) +{ + devfs_handle_t de = dentry->d_fsdata; + struct inode *dir = dentry->d_parent->d_inode; + struct fs_info *fs_info = dir->i_sb->u.generic_sbp; + + if (!de || de->registered) + { + if ( !dentry->d_inode && is_devfsd_or_child (fs_info) ) + { + struct devfs_inode *di = NULL; + struct inode *inode; + +#ifdef CONFIG_DEVFS_DEBUG + char txt[STRING_LENGTH]; + + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, + (dentry->d_name.len >= STRING_LENGTH) ? + (STRING_LENGTH - 1) : dentry->d_name.len); + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: d_revalidate(): dentry: %p name: \"%s\" by: \"%s\"\n", + DEVFS_NAME, dentry, txt, current->comm); +#endif + if (de) + { + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + if (di->fs_info == fs_info) break; + } + if (de == NULL) + { + devfs_handle_t parent; + struct devfs_inode *pi; + + pi = get_devfs_inode_from_vfs_inode (dir); + parent = pi->de; + de = search_for_entry_in_dir (parent, dentry->d_name.name, + dentry->d_name.len, FALSE); + } + if (de == NULL) return 1; + /* Create an inode, now that the driver information is available + */ + if (di == NULL) di = create_devfs_inode (de, fs_info); + else if (de->no_persistence) update_devfs_inode_from_entry (di); + else if (di->ctime == 0) update_devfs_inode_from_entry (di); + else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO); + if (di == NULL) return 1; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return 1; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: d_revalidate(): new VFS inode(%u): %p devfs_inode: %p\n", + DEVFS_NAME, di->ino, inode, di); +#endif + d_instantiate (dentry, inode); + return 1; + } + } + if ( wait_for_devfsd_finished (fs_info) ) dentry->d_op = &devfs_dops; + return 1; +} /* End Function devfs_d_revalidate */ + + +/* Inode operations for device entries follow */ + +static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) +{ + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_inode *pi; + struct devfs_entry *parent, *de; + struct inode *inode; + char txt[STRING_LENGTH]; + + /* Set up the dentry operations before anything else, to ensure cleaning + up on any error */ + dentry->d_op = &devfs_dops; + if (dir == NULL) + { + printk ("%s: lookup(): NULL directory inode\n", DEVFS_NAME); + return ERR_PTR (-ENOTDIR); + } + if ( !S_ISDIR (dir->i_mode) ) return ERR_PTR (-ENOTDIR); + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, + (dentry->d_name.len >= STRING_LENGTH) ? + (STRING_LENGTH - 1) : dentry->d_name.len); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: lookup(%s): dentry: %p by: \"%s\"\n", + DEVFS_NAME, txt, dentry, current->comm); +#endif + fs_info = dir->i_sb->u.generic_sbp; + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return ERR_PTR (-EINVAL); + parent = pi->de; + if (!parent->registered) return ERR_PTR (-ENOENT); + /* Try to reclaim an existing devfs entry */ + de = search_for_entry_in_dir (parent, + dentry->d_name.name, dentry->d_name.len, + FALSE); + if (de) + { + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + if (di->fs_info == fs_info) break; + } + if (fs_info->require_explicit) + { + if (di == NULL) + { + /* Make the dentry negative so a subsequent operation can deal + with it (for the benefit of mknod()). Leaving the dentry + unhashed will cause to fail which in turns causes + to fail */ + d_add (dentry, NULL); + return NULL; + } + } + if ( ( (de == NULL) || !de->registered ) && + (parent->u.dir.num_removable > 0) && + get_removable_partition (parent, dentry->d_name.name, + dentry->d_name.len) ) + { + if (de == NULL) + de = search_for_entry_in_dir (parent, dentry->d_name.name, + dentry->d_name.len, FALSE); + } + if ( (de == NULL) || (!de->registered) ) + { + /* Try with devfsd. For any kind of failure, leave a negative dentry + so someone else can deal with it (in the case where the sysadmin + does a mknod()). It's important to do this before hashing the + dentry, so that the devfsd queue is filled before revalidates + can start */ + if (try_modload (parent, fs_info, + dentry->d_name.name, dentry->d_name.len, txt) < 0) + { + d_add (dentry, NULL); + return NULL; + } + /* devfsd claimed success */ + dentry->d_op = &devfs_revalidate_dops; + dentry->d_fsdata = de; + d_add (dentry, NULL); /* Open the floodgates */ + /* Unlock directory semaphore, which will release any waiters. They + will get the hashed dentry, and may be forced to wait for + revalidation */ + up (&dir->i_sem); + devfs_d_revalidate (dentry, 0); /* I might have to wait too */ + down (&dir->i_sem); /* Grab it again because them's the rules */ + /* If someone else has been so kind as to make the inode, we go home + early */ + if (dentry->d_inode) return NULL; + if (de && !de->registered) return NULL; + if (de == NULL) + de = search_for_entry_in_dir (parent, dentry->d_name.name, + dentry->d_name.len, FALSE); + if (de == NULL) return NULL; + /* OK, there's an entry now, but no VFS inode yet */ + } + else + { + dentry->d_op = &devfs_revalidate_dops; + d_add (dentry, NULL); /* Open the floodgates */ + } + /* Create an inode, now that the driver information is available */ + if (di == NULL) di = create_devfs_inode (de, fs_info); + else if (de->no_persistence) update_devfs_inode_from_entry (di); + else if (di->ctime == 0) update_devfs_inode_from_entry (di); + else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO); + if (di == NULL) return ERR_PTR (-ENOMEM); + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return ERR_PTR (-ENOMEM); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: lookup(): new VFS inode(%u): %p devfs_inode: %p\n", + DEVFS_NAME, di->ino, inode, di); +#endif + d_instantiate (dentry, inode); + /* Unlock directory semaphore, which will release any waiters. They will + get the hashed dentry, and may be forced to wait for revalidation */ + up (&dir->i_sem); + if (dentry->d_op == &devfs_revalidate_dops) + devfs_d_revalidate (dentry, 0); /* I might have to wait too */ + down (&dir->i_sem); /* Grab it again because them's the rules */ + return NULL; +} /* End Function devfs_lookup */ + +static int devfs_link (struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + /*struct inode *inode = old_dentry->d_inode;*/ + char txt[STRING_LENGTH]; + + memset (txt, 0, STRING_LENGTH); + memcpy (txt, old_dentry->d_name.name, old_dentry->d_name.len); + txt[STRING_LENGTH - 1] = '\0'; + printk ("%s: link of \"%s\"\n", DEVFS_NAME, txt); + return -EPERM; +} /* End Function devfs_link */ + +static int devfs_unlink (struct inode *dir, struct dentry *dentry) +{ + struct devfs_inode *di; + struct inode *inode = dentry->d_inode; + struct fs_info *fs_info = dir->i_sb->u.generic_sbp; + +#ifdef CONFIG_DEVFS_DEBUG + char txt[STRING_LENGTH]; + + if (devfs_debug & DEBUG_I_UNLINK) + { + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, dentry->d_name.len); + txt[STRING_LENGTH - 1] = '\0'; + printk ("%s: unlink(%s)\n", DEVFS_NAME, txt); + } +#endif + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + if (!dentry || !dentry->d_inode) return -ENOENT; + di = get_devfs_inode_from_vfs_inode (dentry->d_inode); + if (di == NULL) return -ENOENT; + if (!di->de->registered) return -ENOENT; + if ( !is_devfsd_or_child (fs_info) ) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_DELETE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + di->de->registered = FALSE; + di->de->hide = TRUE; + free_dentries (di->de); + return 0; +} /* End Function devfs_unlink */ + +static int devfs_symlink (struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err; + struct fs_info *fs_info; + struct devfs_inode *pi; + struct devfs_inode *di = NULL; + struct devfs_entry *parent, *de; + struct inode *inode; + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + fs_info = dir->i_sb->u.generic_sbp; + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return -EINVAL; + parent = pi->de; + if (!parent->registered) return -ENOENT; + err = devfs_mk_symlink (parent, dentry->d_name.name, dentry->d_name.len, + DEVFS_FL_NONE, symname, 0, &de, NULL); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: symlink(): errcode from : %d\n", + DEVFS_NAME, err); +#endif + if (err < 0) return err; + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->fs_info == fs_info) break; + } + if (di == NULL) di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + di->mode = de->mode; + di->atime = CURRENT_TIME; + di->mtime = CURRENT_TIME; + di->ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return -ENOMEM; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: symlink(): new VFS inode(%u): %p dentry: %p\n", + DEVFS_NAME, di->ino, inode, dentry); +#endif + de->hide = FALSE; + d_instantiate (dentry, inode); + if ( !is_devfsd_or_child (fs_info) ) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_symlink */ + +static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) +{ + int is_new; + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_inode *pi; + struct devfs_entry *parent, *de; + struct inode *inode; + + mode = (mode & ~S_IFMT) | S_IFDIR; + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + fs_info = dir->i_sb->u.generic_sbp; + /* We are allowed to create the directory */ + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return -EINVAL; + parent = pi->de; + if (!parent->registered) return -ENOENT; + /* Try to reclaim an existing devfs entry, create if there isn't one */ + de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len, + FALSE, TRUE, &is_new, FALSE); + if (de == NULL) return -ENOMEM; + if (de->registered) + { + printk ("%s: mkdir(): existing entry\n", DEVFS_NAME); + return -EEXIST; + } + de->registered = TRUE; + de->hide = FALSE; + if (!S_ISDIR (de->mode) && !is_new) + { + /* Transmogrifying an old entry */ + de->u.dir.first = NULL; + de->u.dir.last = NULL; + } + de->mode = mode; + de->u.dir.num_removable = 0; + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->fs_info == fs_info) break; + } + if (di == NULL) di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + di->mode = mode; + di->uid = current->euid; + di->gid = current->egid; + di->atime = CURRENT_TIME; + di->mtime = CURRENT_TIME; + di->ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return -ENOMEM; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: mkdir(): new VFS inode(%u): %p dentry: %p\n", + DEVFS_NAME, di->ino, inode, dentry); +#endif + d_instantiate (dentry, inode); + if ( !is_devfsd_or_child (fs_info) ) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_mkdir */ + +static int devfs_rmdir (struct inode *dir, struct dentry *dentry) +{ + int has_children = FALSE; + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_entry *de, *child; + struct inode *inode = dentry->d_inode; + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL; + if (inode == dir) return -EPERM; + fs_info = dir->i_sb->u.generic_sbp; + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENOENT; + de = di->de; + if (!de->registered) return -ENOENT; + if ( !S_ISDIR (de->mode) ) return -ENOTDIR; + for (child = de->u.dir.first; child != NULL; child = child->next) + { + if (child->registered) + { + has_children = TRUE; + break; + } + } + if (has_children) return -ENOTEMPTY; + if ( !is_devfsd_or_child (fs_info) ) + devfsd_notify_one (de, DEVFSD_NOTIFY_DELETE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + de->registered = FALSE; + de->hide = TRUE; + free_dentries (de); + return 0; +} /* End Function devfs_rmdir */ + +static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, + int rdev) +{ + int is_new; + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_inode *pi; + struct devfs_entry *parent, *de; + struct inode *inode; + +#ifdef CONFIG_DEVFS_DEBUG + char txt[STRING_LENGTH]; + + if (devfs_debug & DEBUG_I_MKNOD) + { + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, dentry->d_name.len); + txt[STRING_LENGTH - 1] = '\0'; + printk ("%s: mknod(%s): mode: 0%o dev: %d\n", + DEVFS_NAME, txt, mode, rdev); + } +#endif + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + fs_info = dir->i_sb->u.generic_sbp; + if ( !S_ISBLK (mode) && !S_ISCHR (mode) && !S_ISFIFO (mode) && + !S_ISSOCK (mode) ) return -EPERM; + /* We are allowed to create the node */ + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return -EINVAL; + parent = pi->de; + if (!parent->registered) return -ENOENT; + /* Try to reclaim an existing devfs entry, create if there isn't one */ + de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len, + FALSE, TRUE, &is_new, FALSE); + if (de == NULL) return -ENOMEM; + if (!de->registered) + { + /* Since we created the devfs entry we get to choose things */ + de->info = NULL; + de->mode = mode; + if ( S_ISBLK (mode) || S_ISCHR (mode) ) + { + de->u.fcb.u.device.major = MAJOR (rdev); + de->u.fcb.u.device.minor = MINOR (rdev); + de->u.fcb.default_uid = current->euid; + de->u.fcb.default_gid = current->egid; + de->u.fcb.fops = NULL; + de->u.fcb.auto_owner = FALSE; + de->u.fcb.aopen_notify = FALSE; + de->u.fcb.open = FALSE; + } + else if ( S_ISFIFO (mode) ) + { + de->u.fifo.uid = current->euid; + de->u.fifo.gid = current->egid; + } + } + de->registered = TRUE; + de->hide = FALSE; + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->fs_info == fs_info) break; + } + if (di == NULL) di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + di->mode = mode; + di->uid = current->euid; + di->gid = current->egid; + di->atime = CURRENT_TIME; + di->mtime = CURRENT_TIME; + di->ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return -ENOMEM; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_MKNOD) + printk ("%s: new VFS inode(%u): %p dentry: %p\n", + DEVFS_NAME, di->ino, inode, dentry); +#endif + d_instantiate (dentry, inode); + if ( !is_devfsd_or_child (fs_info) ) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_mknod */ + +static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct devfs_inode *di; + + if ( !inode || !S_ISLNK (inode->i_mode) ) return -EINVAL; + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENOENT; + if (!di->de->registered) return -ENOENT; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_RLINK) + printk ("%s: readlink(): dentry: %p\n", DEVFS_NAME, dentry); +#endif + if (buflen > di->de->u.symlink.length + 1) + buflen = di->de->u.symlink.length + 1; + if (copy_to_user (buffer, di->de->u.symlink.linkname, buflen) == 0) + return buflen; + return -EFAULT; +} /* End Function devfs_readlink */ + +static struct dentry *devfs_follow_link (struct dentry *dentry, + struct dentry *base, + unsigned int follow) +{ + struct inode *inode = dentry->d_inode; + struct devfs_inode *di; + + if ( !inode || !S_ISLNK (inode->i_mode) ) + { + dget (dentry); + dput (base); + return dentry; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_FLINK) + printk ("%s: follow_link(): dentry: %p\n", DEVFS_NAME, dentry); +#endif + di = get_devfs_inode_from_vfs_inode (inode); + if ( (di == NULL) || !di->de->registered ) + { + dput (base); + return ERR_PTR (-ENOENT); + } + base = lookup_dentry (di->de->u.symlink.linkname, base, follow); + return base; +} /* End Function devfs_follow_link */ + +static struct inode_operations devfs_iops = +{ + default_file_ops: &devfs_fops, + lookup: devfs_lookup, + link: devfs_link, + unlink: devfs_unlink, + symlink: devfs_symlink, + mkdir: devfs_mkdir, + rmdir: devfs_rmdir, + mknod: devfs_mknod, + readlink: devfs_readlink, + follow_link: devfs_follow_link, +}; + +static struct super_block *devfs_read_super (struct super_block *sb, + void *data, int silent) +{ + char *aopt = data; + struct fs_info *fs_info = NULL; + struct devfs_inode *di; + struct inode *root_inode = NULL; + + if (get_root_entry () == NULL) goto out_no_root; + if ( ( fs_info = kmalloc (sizeof *fs_info, GFP_KERNEL) ) == NULL ) + return NULL; + memset (fs_info, 0, sizeof *fs_info); + atomic_set (&fs_info->devfsd_overrun_count, 0); + init_waitqueue_head (&fs_info->devfsd_wait_queue); + init_waitqueue_head (&fs_info->revalidate_wait_queue); + fs_info->prev = last_fs; + if (first_fs == NULL) first_fs = fs_info; + else last_fs->next = fs_info; + last_fs = fs_info; + fs_info->sb = sb; + if (aopt) + { + if (strcmp (aopt, "explicit") == 0) fs_info->require_explicit = TRUE; + } + lock_super (sb); + sb->u.generic_sbp = fs_info; + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = DEVFS_SUPER_MAGIC; + sb->s_op = &devfs_sops; + di = create_devfs_inode (root_entry, fs_info); + if (di == NULL) goto out_no_root; + if (di->ino != 1) + { + printk ("%s: read_super: root inode number is: %d!\n", + DEVFS_NAME, di->ino); + goto out_no_root; + } + if ( ( root_inode = get_vfs_inode (sb, di, NULL) ) == NULL ) + goto out_no_root; + sb->s_root = D_ALLOC_ROOT (root_inode); + if (!sb->s_root) goto out_no_root; +#ifdef CONFIG_DEVFS_TUNNEL + di->covered = dget (sb->s_root->d_covered); +#endif + unlock_super (sb); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: read super, made devfs ptr: %p\n", + DEVFS_NAME, sb->u.generic_sbp); +#endif + MOD_INC_USE_COUNT; + return sb; + +out_no_root: + printk ("devfs_read_super: get root inode failed\n"); + delete_fs (fs_info); + if (root_inode) iput (root_inode); + sb->s_dev = 0; + unlock_super (sb); + return NULL; +} /* End Function devfs_read_super */ + + +static struct file_system_type devfs_fs_type = +{ + DEVFS_NAME, + 0, + devfs_read_super, + NULL, +}; + + +/* File operations for devfsd follow */ + +static ssize_t devfsd_read (struct file *file, char *buf, size_t len, + loff_t *ppos) +{ + int done = FALSE; + int ival; + loff_t pos, devname_offset, tlen, rpos; + struct devfsd_notify_struct info; + struct devfsd_buf_entry *entry; + struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->u.generic_sbp; + DECLARE_WAITQUEUE (wait, current); + + /* Can't seek (pread) on this device */ + if (ppos != &file->f_pos) return -ESPIPE; + /* Verify the task has grabbed the queue */ + if (fs_info->devfsd_task != current) return -EPERM; + info.major = 0; + info.minor = 0; + /* Block for a new entry */ + add_wait_queue (&fs_info->devfsd_wait_queue, &wait); + current->state = TASK_INTERRUPTIBLE; + while ( devfsd_queue_empty (fs_info) ) + { + fs_info->devfsd_sleeping = TRUE; + wake_up (&fs_info->revalidate_wait_queue); + schedule (); + fs_info->devfsd_sleeping = FALSE; + if ( signal_pending (current) ) + { + remove_wait_queue (&fs_info->devfsd_wait_queue, &wait); + current->state = TASK_RUNNING; + return -EINTR; + } + } + remove_wait_queue (&fs_info->devfsd_wait_queue, &wait); + current->state = TASK_RUNNING; + /* Now play with the data */ + ival = atomic_read (&fs_info->devfsd_overrun_count); + if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count); + info.overrun_count = ival; + entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer + + fs_info->devfsd_buf_out; + info.type = entry->type; + info.mode = entry->mode; + info.uid = entry->uid; + info.gid = entry->gid; + if (entry->type == DEVFSD_NOTIFY_LOOKUP) + { + info.namelen = strlen (entry->data); + pos = 0; + memcpy (info.devname, entry->data, info.namelen + 1); + } + else + { + devfs_handle_t de = entry->data; + + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + info.major = de->u.fcb.u.device.major; + info.minor = de->u.fcb.u.device.minor; + } + pos = devfs_generate_path (de, info.devname, DEVFS_PATHLEN); + if (pos < 0) return pos; + info.namelen = DEVFS_PATHLEN - pos - 1; + if (info.mode == 0) info.mode = de->mode; + } + devname_offset = info.devname - (char *) &info; + rpos = *ppos; + if (rpos < devname_offset) + { + /* Copy parts of the header */ + tlen = devname_offset - rpos; + if (tlen > len) tlen = len; + if ( copy_to_user (buf, (char *) &info + rpos, tlen) ) + { + return -EFAULT; + } + rpos += tlen; + buf += tlen; + len -= tlen; + } + if ( (rpos >= devname_offset) && (len > 0) ) + { + /* Copy the name */ + tlen = info.namelen + 1; + if (tlen > len) tlen = len; + else done = TRUE; + if ( copy_to_user (buf, info.devname + pos + rpos - devname_offset, + tlen) ) + { + return -EFAULT; + } + rpos += tlen; + } + tlen = rpos - *ppos; + if (done) + { + unsigned int next_pos = fs_info->devfsd_buf_out + 1; + + if (next_pos >= devfsd_buf_size) next_pos = 0; + fs_info->devfsd_buf_out = next_pos; + *ppos = 0; + } + else *ppos = rpos; + return tlen; +} /* End Function devfsd_read */ + +static int devfsd_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ival; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + switch (cmd) + { + case DEVFSDIOC_GET_PROTO_REV: + ival = DEVFSD_PROTOCOL_REVISION_KERNEL; + if ( copy_to_user ( (void *)arg, &ival, sizeof ival ) ) return -EFAULT; + break; + case DEVFSDIOC_SET_EVENT_MASK: + /* Ensure only one reader has access to the queue. This scheme will + work even if the global kernel lock were to be removed, because it + doesn't matter who gets in first, as long as only one gets it */ + if (fs_info->devfsd_task == NULL) + { +#ifdef __SMP__ + /* Looks like no-one has it: check again and grab, with interrupts + disabled */ + __cli (); + if (fs_info->devfsd_task == NULL) +#endif + { + fs_info->devfsd_event_mask = 0; /* Temporary disable */ + fs_info->devfsd_task = current; + } +#ifdef __SMP__ + __sti (); +#endif + } + /* Verify the task has grabbed the queue */ + if (fs_info->devfsd_task != current) return -EBUSY; + fs_info->devfsd_pgrp = (current->pgrp == current->pid) ? + current->pgrp : 0; + fs_info->devfsd_file = file; + fs_info->devfsd_buffer = (void *) __get_free_page (GFP_KERNEL); + if (fs_info->devfsd_buffer == NULL) + { + devfsd_close (inode, file); + return -ENOMEM; + } + fs_info->devfsd_buf_out = fs_info->devfsd_buf_in; + fs_info->devfsd_event_mask = arg; /* Let the masses come forth */ + break; + case DEVFSDIOC_RELEASE_EVENT_QUEUE: + if (fs_info->devfsd_file != file) return -EPERM; + return devfsd_close (inode, file); + /*break;*/ +#ifdef CONFIG_DEVFS_DEBUG + case DEVFSDIOC_SET_DEBUG_MASK: + if ( copy_from_user (&ival, (void *) arg, sizeof ival) )return -EFAULT; + devfs_debug = ival; + break; +#endif + default: + return -ENOIOCTLCMD; + } + return 0; +} /* End Function devfsd_ioctl */ + +static int devfsd_close (struct inode *inode, struct file *file) +{ + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + if (fs_info->devfsd_file != file) return 0; + fs_info->devfsd_event_mask = 0; + fs_info->devfsd_file = NULL; + if (fs_info->devfsd_buffer) + { + while (fs_info->devfsd_buffer_in_use) schedule (); + free_page ( (unsigned long) fs_info->devfsd_buffer ); + } + fs_info->devfsd_buffer = NULL; + fs_info->devfsd_pgrp = 0; + fs_info->devfsd_task = NULL; + wake_up (&fs_info->revalidate_wait_queue); + return 0; +} /* End Function devfsd_close */ + + +#ifdef MODULE +int init_module (void) +#else +int __init init_devfs_fs (void) +#endif +{ + printk ("%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", + DEVFS_NAME, DEVFS_VERSION); +#if defined(CONFIG_DEVFS_DEBUG) && !defined(MODULE) + devfs_debug = devfs_debug_init; + printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug); +#endif +#if !defined(MODULE) + printk ("%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options); +#endif + return register_filesystem (&devfs_fs_type); +} + +#ifndef MODULE +void __init mount_devfs_fs (void) +{ + int err; + kdev_t dev; + extern kdev_t get_unnamed_dev (void); + extern int do_mount (kdev_t dev, const char *dev_name, + const char *dir_name, const char *type, + int flags, void *data); + + if ( (boot_options & OPTION_NOMOUNT) ) return; + dev = get_unnamed_dev (); + err = do_mount (dev, "none", "/dev", "devfs", 0, ""); + if (err == 0) printk ("Mounted devfs on /dev\n"); + else printk ("Warning: unable to mount devfs, err: %d\n", err); +} /* End Function mount_devfs_fs */ +#endif + +#ifdef MODULE +static void free_entry (struct devfs_entry *parent) +{ + struct devfs_entry *de, *next; + + if (parent == NULL) return; + for (de = parent->u.dir.first; de != NULL; de = next) + { + next = de->next; + if (de->first_inode != NULL) + { + printk ("%s: free_entry(): unfreed inodes!\n", DEVFS_NAME); + } + if ( S_ISDIR (de->mode) ) + { + /* Recursively free the subdirectories: this is a stack chomper */ + free_entry (de); + } + else kfree (de); + } + kfree (parent); +} /* End Function free_entry */ + +void cleanup_module (void) +{ + unregister_filesystem (&devfs_fs_type); + if (first_fs != NULL) + { + printk ("%s: cleanup_module(): still mounted mounted filesystems!\n", + DEVFS_NAME); + } + free_entry (root_entry); +} +#endif /* MODULE */ diff -urN linux-2.2.20/fs/devfs/util.c linux/fs/devfs/util.c --- linux-2.2.20/fs/devfs/util.c Wed Dec 31 17:00:00 1969 +++ linux/fs/devfs/util.c Mon Dec 3 22:13:02 2001 @@ -0,0 +1,121 @@ +/* devfs (Device FileSystem) utilities. + + Copyright (C) 1999 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + ChangeLog + + 19991031 Richard Gooch + Created. + 19991103 Richard Gooch + Created <_devfs_convert_name> and supported SCSI and IDE CD-ROMs +*/ +#include +#include +#include +#include +#include + + +/* Private functions follow */ + +static void __init _devfs_convert_name (char *new, const char *old, int disc) +/* [SUMMARY] Convert from an old style location-based name to new style. + The new name will be written here. + The old name. + If true, disc partitioning information should be processed. + [RETURNS] Nothing. +*/ +{ + int host, bus, target, lun; + char *ptr; + char part[8]; + + /* Decode "c#b#t#u#" */ + if (old[0] != 'c') return; + host = simple_strtol (old + 1, &ptr, 10); + if (ptr[0] != 'b') return; + bus = simple_strtol (ptr + 1, &ptr, 10); + if (ptr[0] != 't') return; + target = simple_strtol (ptr + 1, &ptr, 10); + if (ptr[0] != 'u') return; + lun = simple_strtol (ptr + 1, &ptr, 10); + if (disc) + { + /* Decode "p#" */ + if (ptr[0] == 'p') sprintf (part, "part%s", ptr + 1); + else strcpy (part, "disc"); + } + else part[0] = '\0'; + sprintf (new, "/host%d/bus%d/target%d/lun%d/%s", + host, bus, target, lun, part); +} /* End Function _devfs_convert_name */ + + +/* Public functions follow */ + +/*PUBLIC_FUNCTION*/ +void __init devfs_make_root (const char *name) +/* [SUMMARY] Create the root FS device entry if required. + The name of the root FS device, as passed by "root=". + [RETURNS] Nothing. +*/ +{ + char dest[64]; + + if ( (strncmp (name, "sd/", 3) == 0) || (strncmp (name, "sr/", 3) == 0) ) + { + strcpy (dest, "../scsi"); + _devfs_convert_name (dest + 7, name + 3, (name[1] == 'd') ? 1 : 0); + } + else if ( (strncmp (name, "ide/hd/", 7) == 0) || + (strncmp (name, "ide/cd/", 7) == 0) ) + { + strcpy (dest, ".."); + _devfs_convert_name (dest + 2, name + 7, (name[4] == 'h') ? 1 : 0); + } + else return; + devfs_mk_symlink (NULL, name, 0, DEVFS_FL_DEFAULT, dest, 0, NULL,NULL); +} /* End Function devfs_make_root */ + +/*PUBLIC_FUNCTION*/ +void devfs_register_tape (devfs_handle_t de) +/* [SUMMARY] Register a tape device in the "/dev/tapes" hierarchy. + Any tape device entry in the device directory. + [RETURNS] Nothing. +*/ +{ + int pos; + devfs_handle_t parent, slave; + char name[16], dest[64]; + static unsigned int tape_counter = 0; + static devfs_handle_t tape_dir = NULL; + + if (tape_dir == NULL) tape_dir = devfs_mk_dir (NULL, "tapes", 5, NULL); + parent = devfs_get_parent (de); + pos = devfs_generate_path (parent, dest + 3, sizeof dest - 3); + if (pos < 0) return; + strncpy (dest + pos, "../", 3); + sprintf (name, "tape%u", tape_counter++); + devfs_mk_symlink (tape_dir, name, 0, DEVFS_FL_DEFAULT, dest + pos, 0, + &slave, NULL); + devfs_auto_unregister (de, slave); +} /* End Function devfs_register_tape */ +EXPORT_SYMBOL(devfs_register_tape); diff -urN linux-2.2.20/fs/devices.c linux/fs/devices.c --- linux-2.2.20/fs/devices.c Sun Mar 25 09:30:58 2001 +++ linux/fs/devices.c Mon Dec 3 22:13:02 2001 @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef CONFIG_KMOD #include @@ -198,11 +199,20 @@ int check_disk_change(kdev_t dev) { int i; - struct file_operations * fops; + struct file_operations * fops = NULL; struct super_block * sb; i = MAJOR(dev); - if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL) + if (i < MAX_BLKDEV) + fops = blkdevs[i].fops; + if (fops == NULL) { + devfs_handle_t de; + + de = devfs_find_handle (NULL, NULL, 0, i, MINOR (dev), + DEVFS_SPECIAL_BLK, 0); + if (de) fops = devfs_get_fops (de); + } + if (fops == NULL) return 0; if (fops->check_media_change == NULL) return 0; diff -urN linux-2.2.20/fs/filesystems.c linux/fs/filesystems.c --- linux-2.2.20/fs/filesystems.c Sun Mar 25 09:30:58 2001 +++ linux/fs/filesystems.c Mon Dec 3 22:13:02 2001 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +86,8 @@ #ifdef CONFIG_PROC_FS init_proc_fs(); #endif + + init_devfs_fs(); /* Header file may make this empty */ #ifdef CONFIG_LOCKD nlmxdr_init(); diff -urN linux-2.2.20/fs/super.c linux/fs/super.c --- linux-2.2.20/fs/super.c Fri Nov 2 09:39:08 2001 +++ linux/fs/super.c Mon Dec 3 22:13:02 2001 @@ -15,12 +15,14 @@ * * Added kerneld support: Jacques Gelinas and Bjorn Ekwall * Added change_root: Werner Almesberger & Hans Lermen, Feb '96 + * Added devfs support: Richard Gooch , 13-JAN-1998 */ #include #include #include #include +#include #include #include #include @@ -747,7 +749,17 @@ if (!retval) { fsync_dev(dev); if (dev != ROOT_DEV) { - blkdev_release(inode); + int err; + struct file file; + void *handle; + + handle = devfs_find_handle (NULL, NULL, 0, + MAJOR (dev), MINOR (dev), + DEVFS_SPECIAL_BLK, 0); + err = devfs_fill_file (inode, &file, handle); + if (!err && file.f_op->release) + file.f_op->release (inode, NULL); + if (err) blkdev_release(inode); put_unnamed_dev(dev); } } @@ -1092,7 +1104,8 @@ goto dput_and_out; retval = -ENOTBLK; - dummy.f_op = get_blkfops(MAJOR(dev)); + if ( devfs_fill_file (inode, &dummy, NULL) ) + dummy.f_op = get_blkfops(MAJOR(dev)); if (!dummy.f_op) goto dput_and_out; @@ -1146,6 +1159,9 @@ struct inode * d_inode = NULL; struct file filp; int retval; + void *handle; + char path[64]; + int path_start = -1; #ifdef CONFIG_ROOT_NFS if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { @@ -1208,20 +1224,46 @@ filp.f_mode = 1; /* read only */ else filp.f_mode = 3; /* read write */ - retval = blkdev_open(d_inode, &filp); + devfs_make_root (root_device_name); + handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME, 0, + MAJOR (ROOT_DEV), MINOR (ROOT_DEV), + DEVFS_SPECIAL_BLK, 1); + retval = devfs_fill_file (d_inode, &filp, handle); + if (retval == 0) + { + path_start = devfs_generate_path (handle, path + 5, + sizeof (path) - 5); + if (filp.f_op->open) retval = filp.f_op->open (d_inode, &filp); + if (retval == 0) ROOT_DEV = d_inode->i_rdev; + } + if (retval < 0) retval = blkdev_open(d_inode, &filp); if (retval == -EROFS) { root_mountflags |= MS_RDONLY; filp.f_mode = 1; - retval = blkdev_open(d_inode, &filp); + handle = devfs_find_handle (NULL, root_device_name, 0, + MAJOR (ROOT_DEV), MINOR (ROOT_DEV), + DEVFS_SPECIAL_BLK, 1); + retval = devfs_fill_file (d_inode, &filp, handle); + if (retval == 0) + { + path_start = devfs_generate_path (handle, path + 5, + sizeof (path) - 5); + if (filp.f_op->open) + retval = filp.f_op->open (d_inode, &filp); + if (retval == 0) ROOT_DEV = d_inode->i_rdev; + } + if (retval < 0) retval = blkdev_open(d_inode, &filp); } iput(d_inode); - if (retval) + if (retval) { /* * Allow the user to distinguish between failed open * and bad superblock on root device. */ - printk("VFS: Cannot open root device %s\n", - kdevname(ROOT_DEV)); + printk ("VFS: Cannot open root device \"%s\" or %s\n", + root_device_name, kdevname (ROOT_DEV)); + printk ("Please append a correct \"root=\" boot option\n"); + } else for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) { if (!(fs_type->fs_flags & FS_REQUIRES_DEV)) continue; @@ -1233,7 +1275,16 @@ printk ("VFS: Mounted root (%s filesystem)%s.\n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); - vfsmnt = add_vfsmnt(sb, "/dev/root", "/"); + if (path_start >= 0) { + devfs_mk_symlink (NULL, + "root", 0, DEVFS_FL_DEFAULT, + path + 5 + path_start, 0, + NULL, NULL); + memcpy (path + path_start, "/dev/", 5); + vfsmnt = add_vfsmnt (sb, path + path_start, + "/"); + } + else vfsmnt = add_vfsmnt (sb, "/dev/root", "/"); if (vfsmnt) return; panic("VFS: add_vfsmnt failed for root fs"); @@ -1260,6 +1311,18 @@ printk(KERN_CRIT "New root is busy. Staying in initrd.\n"); return -EBUSY; } + /* First unmount devfs if mounted */ + dir_d = lookup_dentry ("/dev", NULL, 1); + if (!IS_ERR(dir_d)) { + struct super_block *sb = dir_d->d_inode->i_sb; + + if (sb && (dir_d->d_inode == sb->s_root->d_inode) && + (sb->s_magic == DEVFS_SUPER_MAGIC)) { + dput (dir_d); + do_umount (sb->s_dev, 0, 0); + } + else dput (dir_d); + } ROOT_DEV = new_root_dev; mount_root(); dput(old_root); @@ -1268,6 +1331,7 @@ shrink_dcache(); printk("change_root: old root has d_count=%d\n", old_root->d_count); #endif + mount_devfs_fs (); /* * Get the new mount directory */ diff -urN linux-2.2.20/include/linux/cdrom.h linux/include/linux/cdrom.h --- linux-2.2.20/include/linux/cdrom.h Sun Mar 25 09:37:40 2001 +++ linux/include/linux/cdrom.h Mon Dec 3 23:50:57 2001 @@ -714,6 +714,7 @@ }; #ifdef __KERNEL__ +#include struct cdrom_write_settings { unsigned char fpacket; /* fixed/variable packets */ @@ -727,6 +728,7 @@ struct cdrom_device_ops *ops; /* link to device_ops */ struct cdrom_device_info *next; /* next device_info for this major */ void *handle; /* driver-dependent data */ + devfs_handle_t de; /* real driver creates this */ /* specifications */ kdev_t dev; /* device number */ int mask; /* mask of capability: disables them */ diff -urN linux-2.2.20/include/linux/devfs_fs.h linux/include/linux/devfs_fs.h --- linux-2.2.20/include/linux/devfs_fs.h Sun Mar 25 09:31:05 2001 +++ linux/include/linux/devfs_fs.h Mon Dec 3 23:51:41 2001 @@ -20,6 +20,7 @@ #define DEVFSD_NOTIFY_LOOKUP 4 #define DEVFSD_NOTIFY_CHANGE 5 #define DEVFSD_NOTIFY_CREATE 6 +#define DEVFSD_NOTIFY_DELETE 7 #define DEVFS_PATHLEN 1024 /* Never change this otherwise the binary interface will change */ diff -urN linux-2.2.20/include/linux/devfs_fs_kernel.h linux/include/linux/devfs_fs_kernel.h --- linux-2.2.20/include/linux/devfs_fs_kernel.h Sun Mar 25 09:31:05 2001 +++ linux/include/linux/devfs_fs_kernel.h Tue Dec 4 00:00:44 2001 @@ -25,17 +25,15 @@ is closed, ownership reverts back to <> and <> and the protection is set to read-write for all */ -#define DEVFS_FL_SHOW_UNREG 0x002 /* Show unregistered entries in - directory listings */ -#define DEVFS_FL_HIDE 0x004 /* Do not show entry in directory list */ -#define DEVFS_FL_AUTO_DEVNUM 0x008 /* Automatically generate device number +#define DEVFS_FL_HIDE 0x002 /* Do not show entry in directory list */ +#define DEVFS_FL_AUTO_DEVNUM 0x004 /* Automatically generate device number */ -#define DEVFS_FL_AOPEN_NOTIFY 0x010 /* Asynchronously notify devfsd on open +#define DEVFS_FL_AOPEN_NOTIFY 0x008 /* Asynchronously notify devfsd on open */ -#define DEVFS_FL_REMOVABLE 0x020 /* This is a removable media device */ -#define DEVFS_FL_WAIT 0x040 /* Wait for devfsd to finish */ -#define DEVFS_FL_NO_PERSISTENCE 0x080 /* Forget changes after unregister */ -#define DEVFS_FL_CURRENT_OWNER 0x100 /* Set initial ownership to current */ +#define DEVFS_FL_REMOVABLE 0x010 /* This is a removable media device */ +#define DEVFS_FL_WAIT 0x020 /* Wait for devfsd to finish */ +#define DEVFS_FL_NO_PERSISTENCE 0x040 /* Forget changes after unregister */ +#define DEVFS_FL_CURRENT_OWNER 0x080 /* Set initial ownership to current */ #define DEVFS_FL_DEFAULT DEVFS_FL_NONE @@ -243,5 +241,3 @@ #endif /* CONFIG_DEVFS_FS */ #endif /* _LINUX_DEVFS_FS_KERNEL_H */ -#define tty_register_devfs(driver,flags,minor) -#define tty_unregister_devfs(driver,minor) diff -urN linux-2.2.20/include/linux/fb.h linux/include/linux/fb.h --- linux-2.2.20/include/linux/fb.h Sun Mar 25 09:31:04 2001 +++ linux/include/linux/fb.h Mon Dec 3 22:13:02 2001 @@ -1,6 +1,7 @@ #ifndef _LINUX_FB_H #define _LINUX_FB_H +#include #include /* Definitions of frame buffers */ @@ -309,6 +310,8 @@ struct display *disp; /* initial display variable */ struct vc_data *display_fg; /* Console visible on this display */ char fontname[40]; /* default font name */ + devfs_handle_t devfs_handle; /* Devfs handle for new name */ + devfs_handle_t devfs_lhandle; /* Devfs handle for compat. symlink */ int (*changevar)(int); /* tell console var has changed */ int (*switch_con)(int, struct fb_info*); /* tell fb to switch consoles */ @@ -406,7 +409,7 @@ /* drivers/char/fbmem.c */ extern int register_framebuffer(struct fb_info *fb_info); -extern int unregister_framebuffer(const struct fb_info *fb_info); +extern int unregister_framebuffer(struct fb_info *fb_info); extern int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal, const struct fb_info *fb_info); extern int fbmon_dpms(const struct fb_info *fb_info); diff -urN linux-2.2.20/include/linux/fs.h linux/include/linux/fs.h --- linux-2.2.20/include/linux/fs.h Fri Nov 2 09:39:09 2001 +++ linux/include/linux/fs.h Mon Dec 3 23:50:57 2001 @@ -749,6 +749,8 @@ extern struct inode_operations blkdev_inode_operations; /* fs/devices.c */ +extern struct file_operations *get_blkfops(unsigned int); +extern struct file_operations *get_chrfops(unsigned int, unsigned int); extern int register_chrdev(unsigned int, const char *, struct file_operations *); extern int unregister_chrdev(unsigned int major, const char * name); extern int chrdev_open(struct inode * inode, struct file * filp); @@ -929,6 +931,8 @@ unsigned long generate_cluster(kdev_t dev, int b[], int size); unsigned long generate_cluster_swab32(kdev_t dev, int b[], int size); extern kdev_t ROOT_DEV; +extern char root_device_name[]; + extern void show_buffers(void); extern void mount_root(void); diff -urN linux-2.2.20/include/linux/genhd.h linux/include/linux/genhd.h --- linux-2.2.20/include/linux/genhd.h Sun Mar 25 09:31:03 2001 +++ linux/include/linux/genhd.h Mon Dec 3 23:50:57 2001 @@ -11,6 +11,7 @@ #include #include +#include #include #define CONFIG_MSDOS_PARTITION 1 @@ -64,6 +65,7 @@ long start_sect; long nr_sects; int type; /* RAID or normal */ + devfs_handle_t de; /* primary (master) devfs entry */ }; /* @@ -88,6 +90,7 @@ * the maximum length a given partition name can take (eg. "scd11") */ #define MAX_DISKNAME_LEN 32 +#define GENHD_FL_REMOVABLE 1 struct gendisk { int major; /* major number of driver */ @@ -104,6 +107,10 @@ void *real_devices; /* internal use */ struct gendisk *next; + struct file_operations *fops; + + devfs_handle_t *de_arr; /* one per physical disc */ + char *flags; /* one per physical disc */ }; #ifdef CONFIG_SOLARIS_X86_PARTITION @@ -275,5 +282,8 @@ * to that same buffer (for convenience). */ char *disk_name (struct gendisk *hd, int minor, char *buf); + +extern void devfs_register_partitions (struct gendisk *dev, int minor, + int unregister); #endif diff -urN linux-2.2.20/include/linux/isdn.h linux/include/linux/isdn.h --- linux-2.2.20/include/linux/isdn.h Fri Nov 2 09:39:09 2001 +++ linux/include/linux/isdn.h Mon Dec 3 22:13:02 2001 @@ -236,6 +236,10 @@ #endif +#ifdef CONFIG_DEVFS_FS +# include +#endif + #include #define ISDN_DRVIOCTL_MASK 0x7f /* Mask for Device-ioctl */ @@ -659,6 +663,15 @@ isdn_v110_stream *v110[ISDN_MAX_CHANNELS]; /* V.110 private data */ struct semaphore sem; /* serialize list access*/ unsigned long global_features; +#ifdef CONFIG_DEVFS_FS + devfs_handle_t devfs_handle_isdninfo; + devfs_handle_t devfs_handle_isdnctrl; + devfs_handle_t devfs_handle_isdnX[ISDN_MAX_CHANNELS]; + devfs_handle_t devfs_handle_isdnctrlX[ISDN_MAX_CHANNELS]; +# ifdef CONFIG_ISDN_PPP + devfs_handle_t devfs_handle_ipppX[ISDN_MAX_CHANNELS]; +# endif +#endif } isdn_dev; extern isdn_dev *dev; diff -urN linux-2.2.20/include/linux/joystick.h linux/include/linux/joystick.h --- linux-2.2.20/include/linux/joystick.h Sun Mar 25 09:31:04 2001 +++ linux/include/linux/joystick.h Mon Dec 3 23:51:24 2001 @@ -129,6 +129,7 @@ #define JS_BUFF_SIZE 64 /* output buffer size */ #include +#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) #error "You need to use at least v2.2 Linux kernel." @@ -213,6 +214,7 @@ int num_axes; int num_buttons; char *name; + devfs_handle_t devfs_handle; }; struct js_list { diff -urN linux-2.2.20/include/linux/miscdevice.h linux/include/linux/miscdevice.h --- linux-2.2.20/include/linux/miscdevice.h Sun Mar 25 09:37:40 2001 +++ linux/include/linux/miscdevice.h Mon Dec 3 23:51:06 2001 @@ -1,6 +1,8 @@ #ifndef _LINUX_MISCDEVICE_H #define _LINUX_MISCDEVICE_H +#include + #define BUSMOUSE_MINOR 0 #define PSMOUSE_MINOR 1 #define MS_BUSMOUSE_MINOR 2 @@ -36,6 +38,7 @@ const char *name; struct file_operations *fops; struct miscdevice * next, * prev; + devfs_handle_t devfs_handle; }; extern int misc_register(struct miscdevice * misc); diff -urN linux-2.2.20/include/linux/tty.h linux/include/linux/tty.h --- linux-2.2.20/include/linux/tty.h Mon Dec 3 23:50:57 2001 +++ linux/include/linux/tty.h Tue Dec 4 00:00:12 2001 @@ -417,5 +417,22 @@ extern int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_DEVFS_FS +extern void tty_register_devfs (struct tty_driver *driver, + unsigned int flags, unsigned minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); +#else /* CONFIG_DEVFS_FS */ +static inline void tty_register_devfs (struct tty_driver *driver, + unsigned int flags, unsigned minor) +{ + return; +} +static inline void tty_unregister_devfs (struct tty_driver *driver, + unsigned minor) +{ + return; +} +#endif /* CONFIG_DEVFS_FS */ + #endif /* __KERNEL__ */ #endif diff -urN linux-2.2.20/include/linux/tty_driver.h linux/include/linux/tty_driver.h --- linux-2.2.20/include/linux/tty_driver.h Sun Mar 25 09:31:03 2001 +++ linux/include/linux/tty_driver.h Mon Dec 3 23:50:57 2001 @@ -197,10 +197,15 @@ * optimize for this case if this flag is set. (Note that there * is also a promise, if the above case is true, not to signal * overruns, either.) + * + * TTY_DRIVER_NO_DEVFS --- if set, do not create devfs entries. This + * is only used by tty_register_driver(). + * */ #define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_RESET_TERMIOS 0x0002 #define TTY_DRIVER_REAL_RAW 0x0004 +#define TTY_DRIVER_NO_DEVFS 0x0008 /* tty driver types */ #define TTY_DRIVER_TYPE_SYSTEM 0x0001 diff -urN linux-2.2.20/include/linux/videodev.h linux/include/linux/videodev.h --- linux-2.2.20/include/linux/videodev.h Sun Mar 25 09:31:05 2001 +++ linux/include/linux/videodev.h Mon Dec 3 23:56:56 2001 @@ -9,6 +9,7 @@ #if LINUX_VERSION_CODE >= 0x020100 #include #endif +#include struct video_device { @@ -30,6 +31,7 @@ void *priv; /* Used to be 'private' but that upsets C++ */ int busy; int minor; + devfs_handle_t devfs_handle; }; extern int videodev_init(void); diff -urN linux-2.2.20/init/main.c linux/init/main.c --- linux-2.2.20/init/main.c Fri Nov 2 09:39:16 2001 +++ linux/init/main.c Mon Dec 3 22:13:02 2001 @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -243,6 +244,7 @@ extern void dc390_setup(char* str, int *ints); extern void scsi_luns_setup(char *str, int *ints); extern void scsi_logging_setup(char *str, int *ints); +extern void scsi_host_no_init(char *str, int *ints); extern void sound_setup(char *str, int *ints); extern void reboot_setup(char *str, int *ints); extern void video_setup(char *str, int *ints); @@ -434,6 +436,8 @@ int root_mountflags = MS_RDONLY; char *execute_command = NULL; +char root_device_name[64]; + static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; @@ -633,6 +637,7 @@ kdev_t __init name_to_kdev_t(char *line) { int base = 0; + if (strncmp(line,"/dev/",5) == 0) { struct dev_name_struct *dev = root_dev_names; line += 5; @@ -651,7 +656,18 @@ static void __init root_dev_setup(char *line, int *num) { + int i; + char ch; + ROOT_DEV = name_to_kdev_t(line); + memset (root_device_name, 0, sizeof root_device_name); + if (strncmp (line, "/dev/", 5) == 0) line += 5; + for (i = 0; i < sizeof root_device_name - 1; ++i) + { + ch = line[i]; + if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break; + root_device_name[i] = ch; + } } /* @@ -783,6 +799,7 @@ #ifdef CONFIG_SCSI { "max_scsi_luns=", scsi_luns_setup }, { "scsi_logging=", scsi_logging_setup }, + { "scsihosts=", scsi_host_no_init }, #endif #ifdef CONFIG_SCSI_ADVANSYS { "advansys=", advansys_setup }, @@ -1579,6 +1596,8 @@ /* Mount the root filesystem.. */ mount_root(); + + mount_devfs_fs (); #ifdef CONFIG_BLK_DEV_INITRD root_mountflags = real_root_mountflags; diff -urN linux-2.2.20/kernel/kmod.c linux/kernel/kmod.c --- linux-2.2.20/kernel/kmod.c Sun Mar 25 09:31:02 2001 +++ linux/kernel/kmod.c Mon Dec 3 22:13:02 2001 @@ -83,6 +83,9 @@ /* Allow execve args to be in kernel space. */ set_fs(KERNEL_DS); + /* Don't bother the parent with exit signals */ + current->exit_signal = 0; + /* Go, go, go... */ if (execve(program_path, argv, envp) < 0) return -errno; @@ -92,7 +95,11 @@ static int exec_modprobe(void * module_name) { static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; +#ifdef CONFIG_DEVFS_FS + char *argv[] = { modprobe_path, "-k", (char*)module_name, NULL}; +#else char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL }; +#endif int ret; ret = exec_usermodehelper(modprobe_path, argv, envp); diff -urN linux-2.2.20/kernel/ksyms.c linux/kernel/ksyms.c --- linux-2.2.20/kernel/ksyms.c Fri Nov 2 09:39:16 2001 +++ linux/kernel/ksyms.c Mon Dec 3 22:13:02 2001 @@ -244,6 +244,7 @@ EXPORT_SYMBOL(blkdev_open); EXPORT_SYMBOL(blkdev_release); EXPORT_SYMBOL(gendisk_head); +EXPORT_SYMBOL(devfs_register_partitions); EXPORT_SYMBOL(resetup_one_dev); EXPORT_SYMBOL(unplug_device); EXPORT_SYMBOL(make_request); diff -urN linux-2.2.20/mm/swapfile.c linux/mm/swapfile.c --- linux-2.2.20/mm/swapfile.c Fri Nov 2 09:39:16 2001 +++ linux/mm/swapfile.c Mon Dec 3 22:13:02 2001 @@ -3,6 +3,7 @@ * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * Swap reorganised 29.12.95, Stephen Tweedie + * Added devfs support 14.1.1998, Richard Gooch */ #include @@ -10,6 +11,7 @@ #include #include #include +#include #include /* for blk_size */ #include #include @@ -411,10 +413,18 @@ filp.f_dentry = dentry; filp.f_mode = 3; /* read write */ /* open it again to get fops */ - if( !blkdev_open(dentry->d_inode, &filp) && - filp.f_op && filp.f_op->release){ + if ( !devfs_fill_file (dentry->d_inode, &filp, NULL) ) + { + if (filp.f_op->release) + filp.f_op->release (dentry->d_inode, &filp); + } + else + { + if( !blkdev_open(dentry->d_inode, &filp) && + filp.f_op && filp.f_op->release){ filp.f_op->release(dentry->d_inode,&filp); filp.f_op->release(dentry->d_inode,&filp); + } } } dput(dentry); @@ -540,7 +550,10 @@ filp.f_dentry = swap_dentry; filp.f_mode = 3; /* read write */ - error = blkdev_open(swap_dentry->d_inode, &filp); + error = devfs_fill_file (swap_dentry->d_inode, &filp, NULL); + if (!error && filp.f_op->open) + error = filp.f_op->open (swap_dentry->d_inode, &filp); + if (error) error = blkdev_open(swap_dentry->d_inode, &filp); if (error) goto bad_swap_2; set_blocksize(dev, PAGE_SIZE); diff -urN linux-2.2.20/net/netlink/netlink_dev.c linux/net/netlink/netlink_dev.c --- linux-2.2.20/net/netlink/netlink_dev.c Sun Mar 25 09:31:14 2001 +++ linux/net/netlink/netlink_dev.c Mon Dec 3 22:13:02 2001 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -190,12 +191,37 @@ netlink_release }; +static devfs_handle_t devfs_handle = NULL; + +static void __init make_devfs_entries (const char *name, int minor) +{ + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, + NETLINK_MAJOR, minor, S_IFCHR | S_IRUSR | S_IWUSR, + &netlink_fops, NULL); +} + __initfunc(int init_netlink(void)) { - if (register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) { + int i; + char name[16]; + + if (devfs_register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) { printk(KERN_ERR "netlink: unable to get major %d\n", NETLINK_MAJOR); return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "netlink", 0, NULL); + /* Someone tell me the official names for the uppercase ones */ + make_devfs_entries ("route", 0); + make_devfs_entries ("skip", 1); + make_devfs_entries ("USERSOCK", 2); + make_devfs_entries ("fwmonitor", 3); + make_devfs_entries ("ARPD", 8); + make_devfs_entries ("ROUTE6", 11); + make_devfs_entries ("IP6_FW", 13); + for (i = 0; i < 16; ++i) { + sprintf (name, "tap%d", i); + make_devfs_entries (name, 16 + i); + } return 0; } @@ -209,7 +235,8 @@ void cleanup_module(void) { - unregister_chrdev(NET_MAJOR,"netlink"); + devfs_unregister_chrdev(NETLINK_MAJOR, "netlink"); + devfs_unregister (devfs_handle); } #endif