From 8879de6cdb01afc344b67140542b27403f9fac28 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:31:37 -0500 Subject: [PATCH] Import 2.3.46pre5 --- CREDITS | 10 + Documentation/Configure.help | 39 + Documentation/filesystems/00-INDEX | 2 + Documentation/filesystems/devfs/ChangeLog | 1440 ++++++++ Documentation/filesystems/devfs/README | 853 +++++ Documentation/filesystems/devfs/ToDo | 40 + Documentation/filesystems/devfs/boot-options | 66 + Documentation/filesystems/devfs/mk-devlinks | 123 + Documentation/filesystems/devfs/modules.conf | 89 + Documentation/filesystems/devfs/rc.devfs | 104 + MAINTAINERS | 11 + arch/i386/config.in | 4 + arch/i386/defconfig | 1 + arch/i386/kernel/Makefile | 10 + arch/i386/kernel/apm.c | 2 +- arch/i386/kernel/microcode.c | 205 ++ arch/i386/kernel/mtrr.c | 34 +- arch/m68k/atari/joystick.c | 6 +- arch/sparc64/solaris/socksys.c | 12 +- drivers/block/DAC960.c | 7 +- drivers/block/acsi.c | 14 +- drivers/block/acsi_slm.c | 13 +- drivers/block/floppy.c | 52 +- drivers/block/genhd.c | 2 + drivers/block/hd.c | 8 +- drivers/block/ide-cd.c | 8 +- drivers/block/ide-disk.c | 11 + drivers/block/ide-floppy.c | 9 + drivers/block/ide-probe.c | 19 +- drivers/block/ide-tape.c | 22 +- drivers/block/ide.c | 8 + drivers/block/ll_rw_blk.c | 60 +- drivers/block/loop.c | 14 +- drivers/block/md.c | 14 +- drivers/block/paride/pd.c | 12 +- drivers/block/paride/pg.c | 13 +- drivers/block/paride/pt.c | 17 +- drivers/block/ps2esdi.c | 8 +- drivers/block/rd.c | 11 + drivers/block/xd.c | 17 +- drivers/cdrom/aztcd.c | 9 +- drivers/cdrom/cdrom.c | 33 +- drivers/cdrom/cdu31a.c | 7 +- drivers/cdrom/cm206.c | 5 +- drivers/cdrom/gscd.c | 10 +- drivers/cdrom/mcd.c | 5 +- drivers/cdrom/mcdx.c | 9 +- drivers/cdrom/optcd.c | 11 +- drivers/cdrom/sbpcd.c | 19 +- drivers/cdrom/sjcd.c | 9 +- drivers/cdrom/sonycd535.c | 12 +- drivers/char/Makefile | 3 +- drivers/char/console.c | 34 +- drivers/char/dsp56k.c | 13 +- drivers/char/dtlk.c | 12 +- drivers/char/ftape/zftape/zftape-init.c | 59 +- drivers/char/istallion.c | 17 +- drivers/char/joystick/joystick.c | 21 +- drivers/char/lp.c | 25 +- drivers/char/mem.c | 33 +- drivers/char/misc.c | 16 +- drivers/char/ppdev.c | 14 +- drivers/char/pty.c | 24 +- drivers/char/serial.c | 28 +- drivers/char/stallion.c | 15 +- drivers/char/tpqic02.c | 48 +- drivers/char/tty_io.c | 127 +- drivers/char/vc_screen.c | 485 +-- drivers/char/videodev.c | 20 +- drivers/isdn/avmb1/capi.c | 39 +- drivers/isdn/isdn_common.c | 107 +- drivers/macintosh/adb.c | 8 +- drivers/net/ppp_generic.c | 12 +- drivers/net/wan/cosa.c | 19 +- drivers/sbus/audio/audio.c | 6 +- drivers/sbus/char/bpp.c | 13 +- drivers/sbus/char/sunkbd.c | 7 +- drivers/sbus/char/vfc.h | 3 + drivers/sbus/char/vfc_dev.c | 15 +- drivers/scsi/hosts.c | 50 +- drivers/scsi/hosts.h | 10 + drivers/scsi/scsi.c | 101 + drivers/scsi/scsi.h | 2 + drivers/scsi/scsi_scan.c | 7 + drivers/scsi/sd.c | 26 +- drivers/scsi/sg.c | 14 +- drivers/scsi/sr.c | 11 +- drivers/scsi/st.c | 42 +- drivers/scsi/st.h | 3 + drivers/sgi/char/shmiq.c | 10 +- drivers/sound/sound_core.c | 97 +- drivers/sound/soundcard.c | 68 +- drivers/video/fbmem.c | 20 +- fs/Config.in | 7 + fs/Makefile | 8 +- fs/block_dev.c | 16 +- fs/coda/psdev.c | 16 +- fs/devfs/Makefile | 39 + fs/devfs/base.c | 3467 ++++++++++++++++++ fs/devfs/util.c | 160 + fs/filesystems.c | 3 + fs/nfsd/nfs3xdr.c | 4 +- fs/nfsd/nfsfh.c | 2 +- fs/nfsd/nfssvc.c | 105 +- fs/nfsd/vfs.c | 24 +- fs/partitions/acorn.c | 2 +- fs/partitions/amiga.c | 4 +- fs/partitions/atari.c | 2 +- fs/partitions/check.c | 118 +- fs/partitions/check.h | 2 + fs/partitions/msdos.c | 2 +- fs/partitions/osf.c | 2 +- fs/partitions/sgi.c | 2 +- fs/partitions/sun.c | 2 +- fs/partitions/ultrix.c | 2 +- fs/super.c | 54 +- fs/tunnel.c | 50 + include/asm-i386/processor.h | 12 + include/linux/cdrom.h | 2 + include/linux/devfs_fs.h | 42 + include/linux/devfs_fs_kernel.h | 258 ++ include/linux/fb.h | 5 +- include/linux/fs.h | 4 + include/linux/genhd.h | 17 +- include/linux/ide.h | 4 +- include/linux/isdn.h | 13 + include/linux/joystick.h | 2 + include/linux/miscdevice.h | 3 + include/linux/nfsd/nfsd.h | 7 +- include/linux/raid/md_k.h | 2 +- include/linux/tty_driver.h | 5 + include/linux/videodev.h | 2 + init/main.c | 17 + kernel/ksyms.c | 1 + mm/swapfile.c | 4 + net/netlink/netlink_dev.c | 29 +- 136 files changed, 9199 insertions(+), 526 deletions(-) create mode 100644 Documentation/filesystems/devfs/ChangeLog create mode 100644 Documentation/filesystems/devfs/README create mode 100644 Documentation/filesystems/devfs/ToDo create mode 100644 Documentation/filesystems/devfs/boot-options create mode 100644 Documentation/filesystems/devfs/mk-devlinks create mode 100644 Documentation/filesystems/devfs/modules.conf create mode 100644 Documentation/filesystems/devfs/rc.devfs create mode 100644 arch/i386/kernel/microcode.c create mode 100644 fs/devfs/Makefile create mode 100644 fs/devfs/base.c create mode 100644 fs/devfs/util.c create mode 100644 fs/tunnel.c create mode 100644 include/linux/devfs_fs.h create mode 100644 include/linux/devfs_fs_kernel.h diff --git a/CREDITS b/CREDITS index 7db905aebfc8..70fa8c15c13d 100644 --- a/CREDITS +++ b/CREDITS @@ -41,6 +41,7 @@ N: Tigran A. Aivazian E: tigran@ocston.org W: http://www.ocston.org/~tigran D: BFS filesystem +D: Intel P6 CPU microcode update support S: United Kingdom N: Werner Almesberger @@ -66,6 +67,10 @@ W: http://www.pdos.lcs.mit.edu/~cananian P: 1024/85AD9EED AD C0 49 08 91 67 DF D7 FA 04 1A EE 09 E8 44 B0 D: Unix98 pty support. D: APM update to 1.2 spec. +D: /devfs hacking. +S: 322 N. Riverside Dr. +S: Neptune, NJ 07753 +S: USA N: Erik Andersen E: andersee@debian.org @@ -784,6 +789,7 @@ E: rgooch@atnf.csiro.au D: parent process death signal to children D: prctl() syscall D: /proc/mtrr support to manipulate MTRRs on Intel P6 family +D: Device FileSystem (devfs) S: CSIRO Australia Telescope National Facility S: P.O. Box 76, Epping S: New South Wales, 2121 @@ -1265,6 +1271,10 @@ S: Markham, Ontario S: L3R 8B2 S: Canada +N: Andrzej M. Krzysztofowicz +E: ankry@mif.pg.gda.pl +D: Some 8-bit XT disk driver and /devfs hacking + N: Andreas S. Krebs E: akrebs@altavista.net D: CYPRESS CY82C693 chipset IDE, Digital's PC-Alpha 164SX boards diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 0bd8d16927d6..208c67d9a2d2 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -8673,6 +8673,24 @@ CONFIG_PROC_FS This option will enlarge your kernel by about 67 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 @@ -9091,6 +9109,8 @@ CONFIG_DEVPTS_FS 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 Like some systems, UnixWare uses its own slice table inside a @@ -10752,6 +10772,25 @@ CONFIG_ACQUIRE_WDT module, say M here and read Documentation/modules.txt. Most people will say N. +Intel P6 CPU Microcode Update Support +CONFIG_MICROCODE + If you say Y here you will be able to update microcode on + Intel processors in P6 family, e.g. Pentium Pro, Pentium II, + Pentium III, Xeon etc. You will obviously need the actual microcode + binary data itself which is not shipped with the Linux kernel. + Contact Intel to obtain the latest revision of microcode for + your CPU(s). With this support compiled you can use dd(1) to write + microcode, for example: + + # dd if=/etc/microcode of=/proc/driver/microcode bs=98304 count=1 + + You need to be superuser to do that. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called microcode.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + Enhanced Real Time Clock Support CONFIG_RTC If you say Y here and create a character special file /dev/rtc with diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX index a5312faaf7e2..ec53a9501009 100644 --- a/Documentation/filesystems/00-INDEX +++ b/Documentation/filesystems/00-INDEX @@ -8,6 +8,8 @@ bfs.txt - info for the SCO UnixWare Boot Filesystem (BFS). 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 --git a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog new file mode 100644 index 000000000000..684ef07e0f6d --- /dev/null +++ b/Documentation/filesystems/devfs/ChangeLog @@ -0,0 +1,1440 @@ +/* -*- 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 v100 + +- Ported to kernel 2.3.1-pre5 + +- Added "oops-on-panic" boot option + +- Improved debugging in and + +- Register entry in + +- Unregister entry in + +- Register entry in + +- Unregister entry in + +- Added support for ALSA drivers +=============================================================================== +Changes for patch v101 + +- Ported to kernel 2.3.2 +=============================================================================== +Changes for patch v102 + +- Update serial driver to register PCMCIA entries + Thanks to Roch-Alexandre Nomine-Beguin + +- Updated an email address in ChangeLog + +- Hide virtual console capture entries from directory listings when + corresponding console device is not open +=============================================================================== +Changes for patch v103 + +- Ported to kernel 2.3.3 +=============================================================================== +Changes for patch v104 + +- Added documentation for some functions + +- Added "doc" target to fs/devfs/Makefile + +- Added "v4l" directory for video4linux devices + +- Replaced call to in with call to + + +- Moved registration for sr and sg drivers from detect() to attach() + methods + +- Register entries in and unregister in + +- Work around IDE driver treating CD-ROM as gendisk + +- Use instead of in rc.devfs + +- Updated ToDo list + +- Removed "oops-on-panic" boot option: now always Oops +=============================================================================== +Changes for patch v105 + +- Unregister SCSI host from in + Thanks to Zoltan BOSZORMENYI + +- Don't save /dev/log in rc.devfs + +- Ported to kernel 2.3.4-pre1 +=============================================================================== +Changes for patch v106 + +- Fixed silly typo in drivers/scsi/st.c + +- Improved debugging in +=============================================================================== +Changes for patch v107 + +- Added "diunlink" and "nokmod" boot options + +- Removed superfluous warning message in +=============================================================================== +Changes for patch v108 + +- Remove entries when unloading sound module +=============================================================================== +Changes for patch v109 + +- Ported to kernel 2.3.6-pre2 +=============================================================================== +Changes for patch v110 + +- Took account of change to +=============================================================================== +Changes for patch v111 + +- Created separate event queue for each mounted devfs + +- Removed + +- Created new ioctl()s for devfsd + +- Incremented devfsd protocol revision to 3 + +- Fixed bug when re-creating directories: contents were lost + +- Block access to inodes until devfsd updates permissions +=============================================================================== +Changes for patch v112 + +- Modified patch so it applies against 2.3.5 and 2.3.6 + +- Updated an email address in ChangeLog + +- Do not automatically change ownership/protection of /dev/tty + +- Updated sample modules.conf + +- Switched to sending process uid/gid to devfsd + +- Renamed to + +- Added DEVFSD_NOTIFY_LOOKUP event + +- Added DEVFSD_NOTIFY_CHANGE event + +- Added DEVFSD_NOTIFY_CREATE event + +- Incremented devfsd protocol revision to 4 + +- Moved kernel-specific stuff to include/linux/devfs_fs_kernel.h +=============================================================================== +Changes for patch v113 + +- Ported to kernel 2.3.9 + +- Restricted permissions on some block devices +=============================================================================== +Changes for patch v114 + +- Added support for /dev/netlink + Thanks to Dennis Hou + +- Return EISDIR rather than EINVAL for read(2) on directories + +- Ported to kernel 2.3.10 +=============================================================================== +Changes for patch v115 + +- Added support for all remaining character devices + Thanks to Dennis Hou + +- Cleaned up netlink support +=============================================================================== +Changes for patch v116 + +- Added support for /dev/parport%d + Thanks to Tim Waugh + +- Fixed parallel port ATAPI tape driver + +- Fixed Atari SLM laser printer driver +=============================================================================== +Changes for patch v117 + +- Added support for COSA card + Thanks to Dennis Hou + +- Fixed drivers/char/ppdev.c: missing #include + +- Fixed drivers/char/ftape/zftape/zftape-init.c + Thanks to Vladimir Popov +=============================================================================== +Changes for patch v118 + +- Ported to kernel 2.3.15-pre3 + +- Fixed bug in loop driver + +- Unregister /dev/lp%d entries in drivers/char/lp.c + Thanks to Maciej W. Rozycki +=============================================================================== +Changes for patch v119 + +- Ported to kernel 2.3.16 +=============================================================================== +Changes for patch v120 + +- Fixed bug in drivers/scsi/scsi.c + +- Added /dev/ppp + Thanks to Dennis Hou + +- Ported to kernel 2.3.17 +=============================================================================== +Changes for patch v121 + +- Fixed bug in drivers/block/loop.c + +- Ported to kernel 2.3.18 +=============================================================================== +Changes for patch v122 + +- Ported to kernel 2.3.19 +=============================================================================== +Changes for patch v123 + +- Ported to kernel 2.3.20 +=============================================================================== +Changes for patch v124 + +- Ported to kernel 2.3.21 +=============================================================================== +Changes for patch v125 + +- Created , , + and + Added <> parameter to , , + and + Work sponsored by SGI + +- Fixed apparent bug in COSA driver + +- Re-instated "scsihosts=" boot option +=============================================================================== +Changes for patch v126 + +- Always create /dev/pts if CONFIG_UNIX98_PTYS=y + +- Fixed call to in drivers/block/ide-disk.c + Thanks to Dennis Hou + +- Allow multiple unregistrations + +- Created /dev/scsi hierarchy + Work sponsored by SGI +=============================================================================== +Changes for patch v127 + +Work sponsored by SGI + +- No longer disable devpts if devfs enabled (caveat emptor) + +- Added flags array to struct gendisk and removed code from + drivers/scsi/sd.c + +- Created /dev/discs hierarchy +=============================================================================== +Changes for patch v128 + +Work sponsored by SGI + +- Created /dev/cdroms hierarchy +=============================================================================== +Changes for patch v129 + +Work sponsored by SGI + +- Removed compatibility entries for sound devices + +- Removed compatibility entries for printer devices + +- Removed compatibility entries for video4linux devices + +- Removed compatibility entries for parallel port devices + +- Removed compatibility entries for frame buffer devices +=============================================================================== +Changes for patch v130 + +Work sponsored by SGI + +- Added major and minor number to devfsd protocol + +- Incremented devfsd protocol revision to 5 + +- Removed compatibility entries for SoundBlaster CD-ROMs + +- Removed compatibility entries for netlink devices + +- Removed compatibility entries for SCSI generic devices + +- Removed compatibility entries for SCSI tape devices +=============================================================================== +Changes for patch v131 + +Work sponsored by SGI + +- Support info pointer for all devfs entry types + +- Added <> parameter to and + +- Removed /dev/st hierarchy + +- Removed /dev/sg hierarchy + +- Removed compatibility entries for loop devices + +- Removed compatibility entries for IDE tape devices + +- Removed compatibility entries for SCSI CD-ROMs + +- Removed /dev/sr hierarchy +=============================================================================== +Changes for patch v132 + +Work sponsored by SGI + +- Removed compatibility entries for floppy devices + +- Removed compatibility entries for RAMDISCs + +- Removed compatibility entries for meta-devices + +- Removed compatibility entries for SCSI discs + +- Created + +- Removed /dev/sd hierarchy + +- Support "../" when searching devfs namespace + +- Created /dev/ide/host* hierarchy + +- Supported IDE hard discs in /dev/ide/host* hierarchy + +- Removed compatibility entries for IDE discs + +- Removed /dev/ide/hd hierarchy + +- Supported IDE CD-ROMs in /dev/ide/host* hierarchy + +- Removed compatibility entries for IDE CD-ROMs + +- Removed /dev/ide/cd hierarchy +=============================================================================== +Changes for patch v133 + +Work sponsored by SGI + +- Created + +- Fixed bug in fs/partitions/check.c when rescanning +=============================================================================== +Changes for patch v134 + +Work sponsored by SGI + +- Removed /dev/sd, /dev/sr, /dev/st and /dev/sg directories + +- Removed /dev/ide/hd directory + +- Exported + +- Created and /dev/tapes hierarchy + +- Removed /dev/ide/mt hierarchy + +- Removed /dev/ide/fd hierarchy + +- Ported to kernel 2.3.25 +=============================================================================== +Changes for patch v135 + +Work sponsored by SGI + +- Removed compatibility entries for virtual console capture devices + +- Removed unused + +- Removed compatibility entries for serial devices + +- Removed compatibility entries for console devices + +- Do not hide entries from devfsd or children + +- Removed DEVFS_FL_TTY_COMPAT flag + +- Removed "nottycompat" boot option + +- Removed +=============================================================================== +Changes for patch v136 + +Work sponsored by SGI + +- Moved BSD pty devices to /dev/pty + +- Added DEVFS_FL_WAIT flag +=============================================================================== +Changes for patch v137 + +Work sponsored by SGI + +- Really fixed bug in fs/partitions/check.c when rescanning + +- Support new "disc" naming scheme in + +- Allow NULL fops in + +- Removed redundant name functions in SCSI disc and IDE drivers +=============================================================================== +Changes for patch v138 + +Work sponsored by SGI + +- Fixed old bugs in drivers/block/paride/pt.c, drivers/char/tpqic02.c, + drivers/net/wan/cosa.c and drivers/scsi/scsi.c + Thanks to Sergey Kubushin + +- Fall back to major table if NULL fops given to +=============================================================================== +Changes for patch v139 + +Work sponsored by SGI + +- Corrected and moved and declarations + from arch/alpha/kernel/osf_sys.c to include/linux/fs.h + +- Removed name function from struct gendisk + +- Updated devfs FAQ +=============================================================================== +Changes for patch v140 + +Work sponsored by SGI + +- Ported to kernel 2.3.27 +=============================================================================== +Changes for patch v141 + +Work sponsored by SGI + +- Bug fix in arch/m68k/atari/joystick.c + +- Moved ISDN and capi devices to /dev/isdn +=============================================================================== +Changes for patch v142 + +Work sponsored by SGI + +- Bug fix in drivers/block/ide-probe.c (patch confusion) +=============================================================================== +Changes for patch v143 + +Work sponsored by SGI + +- Bug fix in drivers/block/blkpg.c:partition_name() +=============================================================================== +Changes for patch v144 + +Work sponsored by SGI + +- Ported to kernel 2.3.29 + +- Removed calls to from cdu31a, cm206, mcd and mcdx + CD-ROM drivers: generic driver handles this now + +- Moved joystick devices to /dev/joysticks +=============================================================================== +Changes for patch v145 + +Work sponsored by SGI + +- Ported to kernel 2.3.30-pre3 + +- Register whole-disc entry even for invalid partition tables + +- Fixed bug in mounting root FS when initrd enabled + +- Fixed device entry leak with IDE CD-ROMs + +- Fixed compile problem with drivers/isdn/isdn_common.c + +- Moved COSA devices to /dev/cosa + +- Support fifos when unregistering + +- Created and used in many drivers + +- Moved Coda devices to /dev/coda + +- Moved parallel port IDE tapes to /dev/pt + +- Moved parallel port IDE generic devices to /dev/pg +=============================================================================== +Changes for patch v146 + +Work sponsored by SGI + +- Removed obsolete DEVFS_FL_COMPAT and DEVFS_FL_TOLERANT flags + +- Fixed compile problem with fs/coda/psdev.c + +- Reinstate change to in + drivers/block/ide-probe.c now that fs/isofs/inode.c is fixed + +- Switched to in drivers/block/floppy.c, + drivers/scsi/sr.c and drivers/block/md.c + +- Moved DAC960 devices to /dev/dac960 +=============================================================================== +Changes for patch v147 + +Work sponsored by SGI + +- Ported to kernel 2.3.32-pre4 +=============================================================================== +Changes for patch v148 + +Work sponsored by SGI + +- Removed kmod support: use devfsd instead + +- Moved miscellaneous character devices to /dev/misc +=============================================================================== +Changes for patch v149 + +Work sponsored by SGI + +- 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 v150 + +Work sponsored by SGI + +- Ported to kernel 2.3.34 +=============================================================================== +Changes for patch v151 + +Work sponsored by SGI + +- Ported to kernel 2.3.35-pre1 + +- Created +=============================================================================== +Changes for patch v152 + +Work sponsored by SGI + +- Updated sample modules.conf + +- Ported to kernel 2.3.36-pre1 +=============================================================================== +Changes for patch v153 + +Work sponsored by SGI + +- Ported to kernel 2.3.42 + +- Removed +=============================================================================== +Changes for patch v154 + +Work sponsored by SGI + +- Took account of device number changes for /dev/fb* +=============================================================================== +Changes for patch v155 + +Work sponsored by SGI + +- Ported to kernel 2.3.43-pre8 + +- Moved /dev/tty0 to /dev/vc/0 + +- Moved sequence number formatting from <_tty_make_name> to drivers +=============================================================================== +Changes for patch v156 + +Work sponsored by SGI + +- Fixed breakage in drivers/scsi/sd.c due to recent SCSI changes +=============================================================================== +Changes for patch v157 + +Work sponsored by SGI + +- Ported to kernel 2.3.45 +=============================================================================== +Changes for patch v158 + +Work sponsored by SGI + +- Ported to kernel 2.3.46-pre2 diff --git a/Documentation/filesystems/devfs/README b/Documentation/filesystems/devfs/README new file mode 100644 index 000000000000..a4fc3f9088b2 --- /dev/null +++ b/Documentation/filesystems/devfs/README @@ -0,0 +1,853 @@ +/* -*- auto-fill -*- */ + + Device File System (devfs) Overview + + Richard Gooch + + 11-NOV-1999 + + +Conventions used in this document
+================================= + +Each section in this document will have the string "
" at the +right-hand side of the section title. Each subsection will have +"" at the right-hand side. These strings are meant to make +it easier to search through the document. + +NOTE that the master copy of this document is available online at: +http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.txt + +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/ + + +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 +the devfs automatically, with whatever default ownership and +protection the driver specified. + +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 the 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. 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. + +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 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 (natually) 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 c&b 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 the 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. A devfs would only show those devices available on my +system. This means that listing /dev would be a handy way of checking +what devices were 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 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. + +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 can be 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-current now has a devfs implementation. Solaris 2 has a +pseudo-devfs (something akin to scsidev but for all devices, with some +unspecified kernel support). BeOS and Plan9 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 or +kmod. 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 +-------------- +The semantics of inode creation are different when the 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. + + +Persistence of ownership/permissions 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. + +Upon bootup, simply untar the previously created tarfile, and all your +ownerships/permissions will be retained. For the paranoid, you can +save your permissions periodically using a cron job. + +NOTE: 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). + +I've provided a script called "rc.devfs" in this directory which you +can use to save and restore permissions. + +Alternatively, you can use the device management daemon (devfsd) to +control the permissions of device nodes. This has the advantage of +being able to store permissions for whole groups of devices with a +single configuration entry, rather than one (tar) entry per device +node. Devfsd also receives inode change events, so you could easily +implement a simple permissions database. + + +Installation during the transition phase
+======================================== + +Currently, not all device drivers in the kernel have been modified to +use devfs. If you want to boot between kernels with and without devfs +support, this section discusses some options. Either way is safe, +although some people will have different preferences. + +Note that your old-style (i.e. node-based) chroot /gaol/dev +directories which you manually created will still work, unless you +pass the "devfs=only" boot option. + +Fail-safe Approach with devfs mounted on /dev +--------------------------------------------- +The default is for devfs to be mounted onto /dev at boot time. +Device drivers which do not yet have devfs support will not +automagically appear in the 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 the 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. + +Fail-safe Approach with real /dev inodes +---------------------------------------- +This method involves more work, and is no longer recommended now that +a large number of drivers have devfs support. You will need to use the +"devfs=nomount" boot option. + +Copy the contents of /dev to /devfs. Then remove entries in /dev +which are now available in devfs and make them symbolic links to the +entries in /devfs. Finally, edit your /etc/fstab or boot scripts so +that devfs is mounted over /devfs on bootup. If devfs is supported, +accessing devices supported by devfs will follow the symlinks to +devfs. If devfs is not supported, accessing those same devices will +follow the symlinks to /devfs which contains only old-style device +nodes. Devices not supported by devfs will be found directly on /dev. +Simple! You can also follow this principle for chroot gaols. + +I've provided a demonstration script called "mk-devlinks" in this +directory which you can use to create symlinks in /dev and +/devfs. Note that this script is out of date and should not be run +without modification. + + +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. + + +Other Issues
+============ + +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 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 to mount devfs onto /dev at boot time. +You can disable this with the "devfs=nomount" boot option, but you can +then get harmless but annoying messages about not being able to open +an initial console. + +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. + +Using kmod (module autoloading) +------------------------------- +Another thing to note is that if you are using kmod then you need to +edit your /etc/modules.conf so that things work properly. You should +include the sample modules.conf file in the +Documentation/filesystems/devfs directory into your /etc/modules.conf +to ensure correct operation. + +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 +---------- +You may replace your tty devices in /dev with symbolic links to /devfs +however you will then find that programmes which depend on ttyname(3) +will no longer work properly. The programme is a good +example. I've written a patch to libc 5.4.43 which fixes this. This +has been included in libc 5.4.44 and glibc 2.1.? + + +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/st*) + +- SCSI CD-ROMs (/dev/sr*) + +- SCSI generic devices (/dev/sg*) + +- RAMDISCS (/dev/ram?) + +- Meta Devices (/dev/md*) + +- Floppy discs (/dev/fd?*) + +- Parallel port printers (/dev/lp*) + +- Sound devices + Thanks to Eric Dumas and + C. Scott Ananian + +- Joysticks (/dev/js*) + +- 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/vcs*) + Thanks to Dennis Hou + +- Frame buffer devices (/dev/fb*) + +- Video capture devices (/dev/video? /dev/vbi?) + + +Naming Scheme
+============= + +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 (assuming devfs is mounted +on /dev), and uses a similar naming scheme to the SCSI subsystem. + + +XT Hard Discs +------------- +All XT discs are placed under /dev/xd (assuming devfs is mounted on +/dev). 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,1,...} /dev/tty{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, ...). + + +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 +/proc filesystem. For example: + + scsihosts=aha1542:ppa:aha1542::ncr53c7xx + +means that devices connected to +- first aha1542 controller - will be c0b#t#u# +- first parallel port ZIP - will be c1b#t#u# +- second aha1542 controller - will be c2b#t#u# +- first NCR53C7xx controller - will be c4b#t#u# +- any extra controller - will be c5b#t#u#, c6b#t#u#, etc +- if any of above controllers will not be found - the reserved names will + not be used by any other device. +- c3b#t#u# names will never be used + +You can use ',' instead of ':' as the separator character if you +wish. + +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. + + +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 + 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 + 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. + +A final note on this scheme: since it doesn't increase the size of +device numbers, there are no compatibility issues with userspace. diff --git a/Documentation/filesystems/devfs/ToDo b/Documentation/filesystems/devfs/ToDo new file mode 100644 index 000000000000..7a36369a4b10 --- /dev/null +++ b/Documentation/filesystems/devfs/ToDo @@ -0,0 +1,40 @@ + Device File System (devfs) ToDo List + + Richard Gooch + + 5-DEC-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:-). + + +- >256 ptys + Thanks to C. Scott Ananian + +- 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 --git a/Documentation/filesystems/devfs/boot-options b/Documentation/filesystems/devfs/boot-options new file mode 100644 index 000000000000..83acf91684b0 --- /dev/null +++ b/Documentation/filesystems/devfs/boot-options @@ -0,0 +1,66 @@ +/* -*- auto-fill -*- */ + + Device File System (devfs) Boot Options + + Richard Gooch + + 14-DEC-1999 + + +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: + +show show unregistered devices by default + +nomount do not mount devfs onto /dev at boot time + +only disable non-devfs device nodes for devfs-capable drivers diff --git a/Documentation/filesystems/devfs/mk-devlinks b/Documentation/filesystems/devfs/mk-devlinks new file mode 100644 index 000000000000..885ebdfffc43 --- /dev/null +++ b/Documentation/filesystems/devfs/mk-devlinks @@ -0,0 +1,123 @@ +#! /bin/csh -f + +# WARNING: make sure /devfs is not a mounted devfs + +cd /devfs +if (-e .devfsd) then + echo "/devfs must not be a mounted devfs" + exit 1 +endif +if ( ! -e null ) then + echo "Cannot find null device" + exit 1 +endif + + +# Make SCSI disc links. +# WARNING: this assumes your SCSI discs are numbered ID=0, ID=1, ID=2 and so on +set discs = (a b c d e f) +if ( ! -d sd ) mkdir sd +ln -sf /devfs/sd /dev + +@ scsi_id = 0 +while ($scsi_id < $#discs) + @ discnum = $scsi_id + 1 + ls -lF sd${discs[$discnum]} + rm /dev/sd${discs[$discnum]} + ln -s /devfs/sd${discs[$discnum]} /dev + ln -s ../sd${discs[$discnum]} sd/c0b0t${scsi_id}u0 + @ partition = 1 + while ($partition < 16) + ls -lF sd${discs[$discnum]}${partition} + rm /dev/sd${discs[$discnum]}${partition} + ln -s /devfs/sd${discs[$discnum]}${partition} /dev + ln -s ../sd${discs[$discnum]}${partition} sd/c0b0t${scsi_id}u0p$partition + @ partition ++ + end + @ scsi_id ++ +end + + +# Make IDE disc links +foreach i (hd*) + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make miscellaneous character devices links (character, major=10) +foreach i (`ls -l * | grep '^c' | fgrep '10,' | awk '{print $10}'`) + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make memory devices links (character, major=1) +foreach i (`ls -l * | grep '^c' | fgrep ' 1,' | awk '{print $10}'`) + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make loop device links +foreach i (loop*) + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make various tty device links +foreach i (tty* pty* cua* console) + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make SCSI CD-ROM, tapes and generic links +ln -s /devfs/sr /dev +ln -s /devfs/st /dev +ln -s /devfs/sg /dev +foreach i (sr* st* nst* sg*) + if ("$i" == "stderr") continue + if ("$i" == "stdin") continue + if ("$i" == "stdout") continue + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make RAMDISC device links +ln -s /devfs/rd /dev +foreach i (ram*) + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make floppy device links +ln -s /devfs/floppy /dev +foreach i (fd?*) + rm /dev/$i + ln -s /devfs/$i /dev +end + + +# Make line printer device links +foreach i (lp*) + rm /dev/$i + ln -s /devfs/$i /dev +end + +# Make sound devices links +if ( ! -d sound ) mkdir sound +ln -sf /devfs/sound /dev +foreach i (mixer sequencer midi dsp audio sequencer2 mixer1 patmgr0 midi1\ + dsp1 audio1 patmgr1 midi2 midi3) + + if ( -f /dev/$i ) then + rm /dev/$i + ln -s /devfs/$i /dev + endif +end + + diff --git a/Documentation/filesystems/devfs/modules.conf b/Documentation/filesystems/devfs/modules.conf new file mode 100644 index 000000000000..273568c3fcf5 --- /dev/null +++ b/Documentation/filesystems/devfs/modules.conf @@ -0,0 +1,89 @@ +# 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/sr +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 + +# Video capture devices +alias /dev/video* /dev/v4l +alias /dev/vbi* /dev/v4l diff --git a/Documentation/filesystems/devfs/rc.devfs b/Documentation/filesystems/devfs/rc.devfs new file mode 100644 index 000000000000..73bbb3ac2d74 --- /dev/null +++ b/Documentation/filesystems/devfs/rc.devfs @@ -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 --git a/MAINTAINERS b/MAINTAINERS index ba186c8c7e6c..a99d29c36f58 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -274,6 +274,12 @@ M: hpa@zytor.com L: linux-kernel@vger.rutgers.edu S: Maintained +DEVICE FILESYSTEM +P: Richard Gooch +M: rgooch@atnf.csiro.au +L: linux-kernel@vger.rutgers.edu +S: Maintained + DIGI INTL. EPCA DRIVER P: Chad Schwartz M: support@dgii.com @@ -496,6 +502,11 @@ P: Ingo Molnar M: mingo@redhat.com S: Maintained +INTEL P6 MICROCODE UPDATE SUPPORT +P: Tigran Aivazian +M: tigran@sco.com +S: Maintained + IP MASQUERADING: P: Juanjo Ciarlante M: jjciarla@raiz.uncu.edu.ar diff --git a/arch/i386/config.in b/arch/i386/config.in index 2377c24b705d..6aef1222c9d1 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -49,6 +49,10 @@ if [ "$CONFIG_MK7" = "y" ]; then define_bool CONFIG_X86_USE_3DNOW y fi +if [ "$CONFIG_M686" = "y" -a "$CONFIG_PROC_FS" = "y" ]; then + tristate '/proc/driver/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE +fi + choice 'High Memory Support' \ "off CONFIG_NOHIGHMEM \ 4GB CONFIG_HIGHMEM4G \ diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 712698adba15..c071ab505c67 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -392,6 +392,7 @@ CONFIG_PSMOUSE=y # CONFIG_WATCHDOG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set +# CONFIG_MICROCODE is not set # # Video For Linux diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 96be4dff61dc..3b7bb1258a6d 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -40,6 +40,16 @@ else endif endif +ifeq ($(CONFIG_PROC_FS),y) +ifeq ($(CONFIG_MICROCODE),y) +OX_OBJS += microcode.o +else + ifeq ($(CONFIG_MICROCODE),m) + MX_OBJS += microcode.o + endif +endif +endif + ifeq ($(CONFIG_ACPI),y) O_OBJS += acpi.o else diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 81a813b0579a..4b90347c48c2 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -1394,7 +1394,7 @@ static struct file_operations apm_bios_fops = { static struct miscdevice apm_device = { APM_MINOR_DEV, - "apm", + "apm_bios", &apm_bios_fops }; diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c new file mode 100644 index 000000000000..eb7fbca534a0 --- /dev/null +++ b/arch/i386/kernel/microcode.c @@ -0,0 +1,205 @@ +/* + * CPU Microcode Update interface for Linux + * + * Copyright (C) 2000 Tigran Aivazian + * + * This driver allows to upgrade microcode on Intel processors + * belonging to P6 family - PentiumPro, Pentium II, Pentium III etc. + * + * Reference: Section 8.10 of Volume III, Intel Pentium III Manual, + * Order Number 243192 or download from: + * + * http://developer.intel.com/design/pentiumii/manuals/243192.htm + * + * 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 of the License, or (at your option) any later version. + * + * 1.0 16 February 2000, Tigran Aivazian + * Initial release. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MICROCODE_VERSION "1.0" + +MODULE_DESCRIPTION("CPU (P6) microcode update driver"); +MODULE_AUTHOR("Tigran Aivazian "); +EXPORT_NO_SYMBOLS; + +/* VFS interface */ +static int microcode_open(struct inode *, struct file *); +static int microcode_release(struct inode *, struct file *); +static ssize_t microcode_write(struct file *, const char *, size_t, loff_t *); + + +/* internal helpers to do the work */ +static void do_microcode_update(void); +static void do_update_one(void *); + +/* + * Bits in microcode_status. (31 bits of room for future expansion) + */ +#define MICROCODE_IS_OPEN 0 /* set if /dev/microcode is in use */ +static unsigned long microcode_status = 0; + +/* the actual array of microcode blocks, each 2048 bytes */ +static struct microcode * microcode = NULL; +static unsigned int microcode_num = 0; + +static struct file_operations microcode_fops = { + write: microcode_write, + open: microcode_open, + release: microcode_release, +}; + +static struct inode_operations microcode_inops = { + default_file_ops: µcode_fops, +}; + +static struct proc_dir_entry *proc_microcode; + +static int __init microcode_init(void) +{ + /* write-only /proc/driver/microcode file, one day may become read-write.. */ + proc_microcode = create_proc_entry("microcode", S_IWUSR, proc_root_driver); + if (!proc_microcode) { + printk(KERN_ERR "microcode: can't create /proc/driver/microcode entry\n"); + return -ENOMEM; + } + proc_microcode->ops = µcode_inops; + printk(KERN_ERR "P6 Microcode Update Driver v%s registered\n", MICROCODE_VERSION); + return 0; +} + +static void __exit microcode_exit(void) +{ + remove_proc_entry("microcode", proc_root_driver); + printk(KERN_ERR "P6 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION); +} + +module_init(microcode_init); +module_exit(microcode_exit); + +/* + * We enforce only one user at a time here with open/close. + */ +static int microcode_open(struct inode *inode, struct file *file) +{ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* one at a time, please */ + if (test_and_set_bit(MICROCODE_IS_OPEN, µcode_status)) + return -EBUSY; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int microcode_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + + clear_bit(MICROCODE_IS_OPEN, µcode_status); + return 0; +} + + +static void do_microcode_update(void) +{ + if (smp_call_function(do_update_one, NULL, 1, 0) != 0) + panic("do_microcode_update(): timed out waiting for other CPUs\n"); + do_update_one(NULL); +} + +static void do_update_one(void *unused) +{ + struct cpuinfo_x86 * c; + unsigned int pf = 0, val[2], rev, sig; + int i, id; + + id = smp_processor_id(); + c = cpu_data + id; + + + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6) + return; + + sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8); + + if (c->x86_model >= 5) { + /* get processor flags from BBL_CR_OVRD MSR (0x17) */ + rdmsr(0x17, val[0], val[1]); + pf = 1 << ((val[1] >> 18) & 7); + } + + for (i=0; i= (unsigned int *)m) + sum += *sump; + if (sum != 0) { + printk(KERN_ERR "microcode: CPU%d aborting, " + "bad checksum\n", id); + break; + } + wrmsr(0x79, (unsigned int)(m->bits), 0); + __asm__ __volatile__ ("cpuid"); + rdmsr(0x8B, val[0], val[1]); + printk(KERN_ERR "microcode: CPU%d microcode updated " + "from revision %d to %d\n", id, rev, val[1]); + } + break; + } +} + +static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos) +{ + if (len % sizeof(struct microcode) != 0) { + printk(KERN_ERR "microcode: can only write in N*%d bytes units\n", + sizeof(struct microcode)); + return -EINVAL; + return -EINVAL; + } + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + lock_kernel(); + microcode_num = len/sizeof(struct microcode); + microcode = vmalloc(len); + if (!microcode) { + unlock_kernel(); + return -ENOMEM; + } + if (copy_from_user(microcode, buf, len)) { + vfree(microcode); + unlock_kernel(); + return -EFAULT; + } + do_microcode_update(); + vfree(microcode); + unlock_kernel(); + return len; +} diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 5caa4e477072..dc9a69ea6e77 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -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. @@ -241,6 +240,7 @@ #include #include #include +#include #include #include #define MTRR_NEED_STRINGS @@ -305,11 +305,15 @@ typedef u8 mtrr_type; TRUE) #endif -#ifndef CONFIG_PROC_FS +#if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS) +# define USERSPACE_INTERFACE +#endif + +#ifndef USERSPACE_INTERFACE # define compute_ascii() while (0) #endif -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE static char *ascii_buffer = NULL; static unsigned int ascii_buf_bytes = 0; #endif @@ -317,7 +321,7 @@ static unsigned int *usage_table = NULL; static DECLARE_MUTEX(main_lock); /* Private functions */ -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE static void compute_ascii (void); #endif @@ -1010,7 +1014,7 @@ static void init_table (void) return; } for (i = 0; i < max; i++) usage_table[i] = 1; -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL ) { printk ("mtrr: could not allocate\n"); @@ -1286,7 +1290,7 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) return reg; } /* End Function mtrr_del */ -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE static int mtrr_file_add (unsigned long base, unsigned long size, unsigned int type, char increment, struct file *file) @@ -1475,12 +1479,18 @@ static struct file_operations mtrr_fops = release: mtrr_close, }; +# ifdef CONFIG_PROC_FS + static struct inode_operations proc_mtrr_inode_operations = { &mtrr_fops, /* default property file-ops */ }; static struct proc_dir_entry *proc_root_mtrr; +# endif /* CONFIG_PROC_FS */ + +static devfs_handle_t devfs_handle = NULL; + static void compute_ascii (void) { char factor; @@ -1515,10 +1525,13 @@ static void compute_ascii (void) 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 /* CONFIG_PROC_FS */ } /* End Function compute_ascii */ -#endif /* CONFIG_PROC_FS */ +#endif /* USERSPACE_INTERFACE */ EXPORT_SYMBOL(mtrr_add); EXPORT_SYMBOL(mtrr_del); @@ -1786,9 +1799,12 @@ int __init mtrr_init(void) # endif /* !__SMP__ */ # ifdef CONFIG_PROC_FS - proc_root_mtrr = create_proc_entry("mtrr", S_IWUSR|S_IRUGO, &proc_root); + proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root); proc_root_mtrr->ops = &proc_mtrr_inode_operations; #endif + devfs_handle = devfs_register (NULL, "cpu/mtrr", 0, DEVFS_FL_DEFAULT, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, 0, 0, + &mtrr_fops, NULL); init_table (); return 0; } /* End Function mtrr_init */ diff --git a/arch/m68k/atari/joystick.c b/arch/m68k/atari/joystick.c index bf82e67eadf4..86bd24c73a13 100644 --- a/arch/m68k/atari/joystick.c +++ b/arch/m68k/atari/joystick.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -132,8 +133,11 @@ int __init atari_joystick_init(void) init_waitqueue_head(&joystick[0].wait); init_waitqueue_head(&joystick[1].wait); - 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); + devfs_register_series (NULL, "joysticks/digital%u", 2, DEVFS_FL_DEFAULT, + MAJOR_NR, 128, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &atari_joystick_fops, NULL); return 0; } diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index b5f76d400ff3..a437e2214e94 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -157,6 +158,8 @@ static struct file_operations socksys_fops = { release: socksys_release, }; +static devfs_handle_t devfs_handle = NULL; + int __init init_socksys(void) { @@ -167,7 +170,7 @@ init_socksys(void) 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; @@ -177,6 +180,10 @@ init_socksys(void) printk ("Couldn't create socket\n"); return ret; } + devfs_handle = devfs_register (NULL, "socksys", 0, DEVFS_FL_NONE, + 30, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &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; @@ -190,6 +197,7 @@ init_socksys(void) 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 --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 5a9f2216250c..d4e44bb07bd8 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -1089,7 +1089,7 @@ static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller) /* Register the Block Device Major Number for this DAC960 Controller. */ - if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0) + if (devfs_register_blkdev(MajorNumber, "dac960", &DAC960_FileOperations) < 0) { DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n", Controller, MajorNumber); @@ -1130,12 +1130,13 @@ static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller) Complete initialization of the Generic Disk Information structure. */ Controller->GenericDiskInfo.major = MajorNumber; - Controller->GenericDiskInfo.major_name = "rd"; + Controller->GenericDiskInfo.major_name = "dac960"; Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits; Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions; Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount; Controller->GenericDiskInfo.real_devices = Controller; Controller->GenericDiskInfo.next = NULL; + Controller->GenericDiskInfo.fops = &DAC960_FileOperations; /* Install the Generic Disk Information structure at the end of the list. */ @@ -1164,7 +1165,7 @@ static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller) /* Unregister the Block Device Major Number for this DAC960 Controller. */ - unregister_blkdev(MajorNumber, "rd"); + devfs_unregister_blkdev(MajorNumber, "dac960"); /* Remove the I/O Request Function. */ diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c index 41578b7b2c1e..f2a102cf2412 100644 --- a/drivers/block/acsi.c +++ b/drivers/block/acsi.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -1385,6 +1386,8 @@ static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd ) ********************************************************************/ +extern struct block_device_operations acsi_fops; + static struct gendisk acsi_gendisk = { MAJOR_NR, /* Major number */ "ad", /* Major name */ @@ -1394,7 +1397,8 @@ static struct gendisk acsi_gendisk = { acsi_sizes, /* block sizes */ 0, /* number */ (void *)acsi_info, /* internal */ - NULL /* next */ + NULL, /* next */ + &acsi_fops, /* file operations */ }; #define MAX_SCSI_DEVICE_CODE 10 @@ -1776,16 +1780,14 @@ int acsi_init( void ) int err = 0; 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 ); @@ -1824,7 +1826,7 @@ void cleanup_module(void) blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); 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 --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c index e4d343be31a6..88fa04ac668e 100644 --- a/drivers/block/acsi_slm.c +++ b/drivers/block/acsi_slm.c @@ -65,6 +65,7 @@ not be guaranteed. There are several ways to assure this: #include #include #include +#include #include #include @@ -987,23 +988,28 @@ int attach_slm( int target, int lun ) return( 1 ); } +static devfs_handle_t devfs_handle = NULL; int slm_init( void ) { - if (register_chrdev( MAJOR_NR, "slm", &slm_fops )) { + 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; } 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" ); return -ENOMEM; } BufferP = SLMBuffer; SLMState = IDLE; + devfs_handle = devfs_mk_dir (NULL, "slm", 3, NULL); + devfs_register_series (devfs_handle, "%u", MAX_SLM, DEVFS_FL_DEFAULT, + MAJOR_NR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &slm_fops, NULL); return 0; } @@ -1026,7 +1032,8 @@ int init_module(void) void cleanup_module(void) { - if (unregister_chrdev( MAJOR_NR, "slm" ) != 0) + devfs_unregister (devfs_handle); + if (devfs_unregister_chrdev( MAJOR_NR, "slm" ) != 0) printk( KERN_ERR "acsi_slm: cleanup_module failed\n"); atari_stram_free( SLMBuffer ); } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 44abae8779c4..ade376b5118e 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -144,6 +144,10 @@ static int irqdma_allocated = 0; #define FDPATCHES #include +/* + * 1998/1/21 -- Richard Gooch -- devfs support + */ + #include #include @@ -158,6 +162,7 @@ static int irqdma_allocated = 0; #include #include #include +#include /* * PS/2 floppies have much slower step rates than regular floppies. @@ -196,6 +201,9 @@ static int use_virtual_dma=0; static unsigned short virtual_dma_port=0x3f0; void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs); static int set_dor(int fdc, char mask, char data); +static void register_devfs_entries (int drive); +static devfs_handle_t devfs_handle = NULL; + #define K_64 0x10000 /* 64KB */ #include @@ -3614,6 +3622,7 @@ static void config_types(void) first = 0; } printk("%s fd%d is %s", prepend, drive, name); + register_devfs_entries (drive); } *UDP = *params; } @@ -3827,6 +3836,37 @@ static struct block_device_operations floppy_fops = { revalidate: floppy_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, 0, DEVFS_FL_DEFAULT, MAJOR_NR, + base_minor + (table_sup[UDP->cmos][i] << 2), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP, + 0, 0, &floppy_fops, NULL); + } while (table_sup[UDP->cmos][i++]); + } +} + /* * Floppy Driver initialization * ============================= @@ -4049,7 +4089,8 @@ int __init floppy_init(void) raw_cmd = 0; - if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) { + devfs_handle = devfs_mk_dir (NULL, "floppy", 0, NULL); + if (devfs_register_blkdev(MAJOR_NR,"fd",&floppy_fops)) { printk("Unable to get major %d for floppy\n",MAJOR_NR); return -EBUSY; } @@ -4080,7 +4121,7 @@ int __init floppy_init(void) use_virtual_dma = can_use_virtual_dma & 1; fdc_state[0].address = FDC1; if (fdc_state[0].address == -1) { - unregister_blkdev(MAJOR_NR,"fd"); + devfs_unregister_blkdev(MAJOR_NR,"fd"); del_timer(&fd_timeout); return -ENODEV; } @@ -4092,7 +4133,7 @@ int __init floppy_init(void) if (floppy_grab_irq_and_dma()){ del_timer(&fd_timeout); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - unregister_blkdev(MAJOR_NR,"fd"); + devfs_unregister_blkdev(MAJOR_NR,"fd"); del_timer(&fd_timeout); return -EBUSY; } @@ -4158,7 +4199,7 @@ int __init floppy_init(void) if (usage_count) floppy_release_irq_and_dma(); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - unregister_blkdev(MAJOR_NR,"fd"); + devfs_unregister_blkdev(MAJOR_NR,"fd"); } for (drive = 0; drive < N_DRIVE; drive++) { @@ -4395,7 +4436,8 @@ void cleanup_module(void) { int dummy; - unregister_blkdev(MAJOR_NR, "fd"); + devfs_unregister (devfs_handle); + devfs_unregister_blkdev(MAJOR_NR, "fd"); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); /* eject disk, if any */ diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 95999e273376..3f3237e8730e 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -4,6 +4,8 @@ * * Copyright (C) 1991-1998 Linus Torvalds * + * devfs support - jj, rgooch, 980122 + * * Moved partition checking code to fs/partitions* - Russell King * (linux@arm.uk.linux.org) */ diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 111b899fe54e..5520c17b00ea 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -662,6 +663,8 @@ static int hd_release(struct inode * inode, struct file * file) return 0; } +extern struct block_device_operations hd_fops; + static struct gendisk hd_gendisk = { MAJOR_NR, /* Major number */ "hd", /* Major name */ @@ -671,7 +674,8 @@ static struct gendisk hd_gendisk = { 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) @@ -800,7 +804,7 @@ static void hd_geninit(void) int __init 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 --git a/drivers/block/ide-cd.c b/drivers/block/ide-cd.c index 48cf87c81ce0..0f032ac8c072 100644 --- a/drivers/block/ide-cd.c +++ b/drivers/block/ide-cd.c @@ -299,7 +299,6 @@ * Generic packet command support and error handling routines. */ - /* Mark that we've seen a media change, and invalidate our internal buffers. */ static void cdrom_saw_media_change (ide_drive_t *drive) @@ -2270,7 +2269,12 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots) devinfo->mask |= CDC_PLAY_AUDIO; if (!CDROM_CONFIG_FLAGS (drive)->close_tray) devinfo->mask |= CDC_CLOSE_TRAY; - + + devinfo->de = devfs_register (drive->de, "cd", 2, DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + ide_fops, NULL); + return register_cdrom (devinfo); } diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c index 1209aa82a2d3..e62295241861 100644 --- a/drivers/block/ide-disk.c +++ b/drivers/block/ide-disk.c @@ -744,6 +744,8 @@ static int idedisk_cleanup (ide_drive_t *drive) static void idedisk_setup (ide_drive_t *drive) { + int i; + struct hd_driveid *id = drive->id; unsigned long capacity; @@ -764,6 +766,15 @@ static void idedisk_setup (ide_drive_t *drive) 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 */ if (!drive->cyl || !drive->head || !drive->sect) { diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c index b24933637f4a..e2977c754032 100644 --- a/drivers/block/ide-floppy.c +++ b/drivers/block/ide-floppy.c @@ -1549,6 +1549,15 @@ static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy) (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 --git a/drivers/block/ide-probe.c b/drivers/block/ide-probe.c index 33ca2900b2c8..77e8983049de 100644 --- a/drivers/block/ide-probe.c +++ b/drivers/block/ide-probe.c @@ -699,13 +699,28 @@ static void init_gendisk (ide_hwif_t *hwif) gd->nr_real = units; /* current num real drives */ 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->index : hwif->index, + hwif->channel, unit, 0); + hwif->drives[unit].de = + devfs_mk_dir (NULL, name, 0, NULL); + } } } @@ -764,7 +779,7 @@ static int hwif_init (ide_hwif_t *hwif) printk("%s: request_fn NOT DEFINED\n", hwif->name); return (hwif->present = 0); } - if (register_blkdev (hwif->major, hwif->name, ide_fops)) { + if (devfs_register_blkdev (hwif->major, hwif->name, ide_fops)) { printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major); return (hwif->present = 0); } diff --git a/drivers/block/ide-tape.c b/drivers/block/ide-tape.c index 9d2bc216ffa2..1e1b6e44eaa7 100644 --- a/drivers/block/ide-tape.c +++ b/drivers/block/ide-tape.c @@ -396,6 +396,7 @@ #include #include #include +#include #include #include #include @@ -794,6 +795,7 @@ typedef struct { */ typedef struct { ide_drive_t *drive; + devfs_handle_t de_r, de_n; /* * Since a typical character device operation requires more @@ -5770,11 +5772,13 @@ static int idetape_cleanup (ide_drive_t *drive) 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; } @@ -5871,7 +5875,8 @@ int idetape_init (void) #endif 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; #if ONSTREAM_DEBUG @@ -5905,10 +5910,21 @@ int idetape_init (void) 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", 2, DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + &idetape_fops, NULL); + tape->de_n = + devfs_register (drive->de, "mtn", 3, DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor + 128, + S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + &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 --git a/drivers/block/ide.c b/drivers/block/ide.c index 13e2c481924c..93da9bea20a9 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -1978,6 +1978,10 @@ void ide_unregister (unsigned int index) 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) @@ -2026,6 +2030,10 @@ void ide_unregister (unsigned int index) 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); } old_hwif = *hwif; diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 976803e2bbbb..c4e9542e96a2 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -213,8 +213,7 @@ static int ll_merge_requests_fn(request_queue_t *q, struct request *req, { int total_segments = req->nr_segments + next->nr_segments; - if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) - { + if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) { total_segments--; q->nr_segments--; } @@ -226,29 +225,6 @@ static int ll_merge_requests_fn(request_queue_t *q, struct request *req, return 1; } -void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) -{ - INIT_LIST_HEAD(&q->queue_head); - q->elevator = ELEVATOR_DEFAULTS; - q->request_fn = rfn; - q->back_merge_fn = ll_back_merge_fn; - q->front_merge_fn = ll_front_merge_fn; - q->merge_requests_fn = ll_merge_requests_fn; - q->make_request_fn = NULL; - q->plug_tq.sync = 0; - q->plug_tq.routine = &generic_unplug_device; - q->plug_tq.data = q; - q->plugged = 0; - /* - * These booleans describe the queue properties. We set the - * default (and most common) values here. Other drivers can - * use the appropriate functions to alter the queue properties. - * as appropriate. - */ - q->plug_device_fn = NULL; - q->head_active = 1; -} - /* * "plug" the device if there are no outstanding requests: this will * force the transfer to start only after we have put all the requests @@ -257,7 +233,7 @@ void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) * This is called with interrupts off and no requests on the queue. * (and with the request spinlock aquired) */ -inline void generic_plug_device (request_queue_t *q, kdev_t dev) +static void generic_plug_device (request_queue_t *q, kdev_t dev) { #ifdef CONFIG_BLK_DEV_MD if (MAJOR(dev) == MD_MAJOR) { @@ -272,6 +248,29 @@ inline void generic_plug_device (request_queue_t *q, kdev_t dev) queue_task(&q->plug_tq, &tq_disk); } +void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) +{ + INIT_LIST_HEAD(&q->queue_head); + q->elevator = ELEVATOR_DEFAULTS; + q->request_fn = rfn; + q->back_merges_fn = ll_back_merge_fn; + q->front_merge_fn = ll_front_merge_fn; + q->merge_requests_fn = ll_merge_requests_fn; + q->make_request_fn = NULL; + q->plug_tq.sync = 0; + q->plug_tq.routine = &generic_unplug_device; + q->plug_tq.data = q; + q->plugged = 0; + /* + * These booleans describe the queue properties. We set the + * default (and most common) values here. Other drivers can + * use the appropriate functions to alter the queue properties. + * as appropriate. + */ + q->plug_device_fn = generic_plug_device; + q->head_active = 1; +} + /* * remove the plug and let it rip.. */ @@ -439,8 +438,7 @@ static inline struct list_head * seek_to_not_starving_chunk(request_queue_t * q, do { struct request * req = blkdev_entry_to_request(entry); - if (elevator_sequence_before(req->elevator_sequence, sequence)) - { + if (elevator_sequence_before(req->elevator_sequence, sequence)) { *lat -= q->nr_segments - pos; *starving = 1; return entry; @@ -780,11 +778,7 @@ static inline void __make_request(request_queue_t * q, int rw, empty = 0; if (list_empty(&q->queue_head)) { empty = 1; - /* MD and loop can't handle plugging without deadlocking */ - if (q->plug_device_fn) - q->plug_device_fn(q, bh->b_rdev); /* is atomic */ - else - generic_plug_device(q, bh->b_rdev); /* is atomic */ + q->plug_device_fn(q, bh->b_rdev); /* is atomic */ goto get_rq; } diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 122c7a9c5fca..ab47b380e952 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -14,6 +14,8 @@ * * Fixed do_loop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997 * + * Added devfs support - Richard Gooch 16-Jan-1998 + * * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998 * * Loadable modules and other fixes by AK, 1998 @@ -56,6 +58,7 @@ #include #include +#include #include @@ -77,6 +80,7 @@ static int max_loop = 8; static struct loop_device *loop_dev; static int *loop_sizes; static int *loop_blksizes; +static devfs_handle_t devfs_handle = NULL; /* For the directory */ #define FALSE 0 #define TRUE (!FALSE) @@ -750,11 +754,16 @@ int __init loop_init(void) { 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); + devfs_register_series (devfs_handle, "%u", max_loop, DEVFS_FL_DEFAULT, + MAJOR_NR, 0, + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + &lo_fops, NULL); if ((max_loop < 1) || (max_loop > 255)) { printk (KERN_WARNING "loop: invalid max_loop (must be between 1 and 255), using default (8)\n"); @@ -804,7 +813,8 @@ int __init loop_init(void) #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"); kfree (loop_dev); diff --git a/drivers/block/md.c b/drivers/block/md.c index 752c7b0ab33f..da79af48d672 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -11,6 +11,7 @@ - kerneld support by Boris Tobotras - kmod support by: Cyrus Durgin - RAID0 bugfixes: Mark Anthony Lisher + - 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 @@ -24,6 +25,7 @@ #include #include +#include #ifdef CONFIG_KMOD #include @@ -68,6 +70,9 @@ static mdk_thread_t *md_recovery_thread = NULL; int md_size[MAX_MD_DEVS] = {0, }; +extern struct block_device_operations md_fops; +static devfs_handle_t devfs_handle = NULL; + static struct gendisk md_gendisk= { MD_MAJOR, @@ -78,7 +83,8 @@ static struct gendisk md_gendisk= md_size, MAX_MD_DEVS, NULL, - NULL + NULL, + &md_fops, }; void md_plug_device (request_queue_t *mdqueue, kdev_t dev) @@ -3302,11 +3308,15 @@ int md__init md_init (void) MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION, MAX_MD_DEVS, MAX_REAL); - if (register_blkdev (MD_MAJOR, "md", &md_fops)) + if (devfs_register_blkdev (MD_MAJOR, "md", &md_fops)) { printk (KERN_ALERT "Unable to get major %d for md\n", MD_MAJOR); return (-1); } + devfs_handle = devfs_mk_dir (NULL, "md", 0, NULL); + devfs_register_series (devfs_handle, "%u", MAX_MD_DEV,DEVFS_FL_DEFAULT, + MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + &md_fops, NULL); blk_dev[MD_MAJOR].queue = md_get_queue; diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index ffd5c1c3f37e..f40958ecd96d 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -156,6 +156,7 @@ static int pd_drive_count; #include #include #include +#include #include #include #include @@ -339,6 +340,8 @@ static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR", /* kernel glue structures */ +extern struct block_device_operations pd_fops; + static struct gendisk pd_gendisk = { PD_MAJOR, /* Major number */ PD_NAME, /* Major name */ @@ -348,7 +351,8 @@ static struct gendisk pd_gendisk = { pd_sizes, /* block sizes */ 0, /* number */ NULL, /* internal */ - NULL /* next */ + NULL, /* next */ + &pd_fops, /* block device operations */ }; static struct block_device_operations pd_fops = { @@ -386,8 +390,7 @@ int pd_init (void) { 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; @@ -592,8 +595,7 @@ void cleanup_module(void) { 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 --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 434fac0296f4..b4e36726c49f 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -164,6 +164,7 @@ static int pg_drive_count; #include #include #include +#include #include #include #include @@ -286,6 +287,8 @@ void pg_init_units( void ) } } +static devfs_handle_t devfs_handle = NULL; + int pg_init (void) /* preliminary initialisation */ { int unit; @@ -296,14 +299,17 @@ int pg_init (void) /* preliminary initialisation */ 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 #include #include @@ -290,6 +291,8 @@ void pt_init_units( void ) } } +static devfs_handle_t devfs_handle = NULL; + int pt_init (void) /* preliminary initialisation */ { int unit; @@ -300,7 +303,7 @@ int pt_init (void) /* preliminary initialisation */ 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 @@ -164,7 +165,8 @@ static struct gendisk ps2esdi_gendisk = 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 */ @@ -173,7 +175,7 @@ int __init ps2esdi_init(void) /* 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; } @@ -229,7 +231,7 @@ cleanup_module(void) 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 --git a/drivers/block/rd.c b/drivers/block/rd.c index 7c8e75b59ca1..19f485df0aca 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -97,6 +98,7 @@ static unsigned long rd_length[NUM_RAMDISKS]; /* Size of RAM disks in bytes */ 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 = NULL; /* * Parameters for the boot-loading of the RAM disk. These are set by @@ -180,6 +182,8 @@ __setup("ramdisk_size=", ramdisk_size2); * 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(request_queue_t * q) { @@ -388,6 +392,7 @@ static void __exit rd_cleanup (void) for (i = 0 ; i < NUM_RAMDISKS; i++) destroy_buffers(MKDEV(MAJOR_NR, i)); + devfs_unregister (devfs_handle); unregister_blkdev( MAJOR_NR, "ramdisk" ); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); } @@ -419,6 +424,11 @@ int __init rd_init (void) rd_blocksizes[i] = rd_blocksize; rd_kbsize[i] = rd_size; } + devfs_handle = devfs_mk_dir (NULL, "rd", 0, NULL); + devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS, + DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + &fd_fops, NULL); hardsect_size[MAJOR_NR] = rd_hardsec; /* Size of the RAM disk blocks */ blksize_size[MAJOR_NR] = rd_blocksizes; /* Avoid set_blocksize() check */ @@ -681,6 +691,7 @@ static void __init rd_load_image(kdev_t device, int offset, int unit) 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 --git a/drivers/block/xd.c b/drivers/block/xd.c index 2e074338976e..fde487ecd7f2 100644 --- a/drivers/block/xd.c +++ b/drivers/block/xd.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -117,6 +118,9 @@ static unsigned int xd_bases[] __initdata = 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]; + +extern struct block_device_operations xd_fops; + static struct gendisk xd_gendisk = { MAJOR_NR, /* Major number */ "xd", /* Major name */ @@ -126,7 +130,8 @@ static struct gendisk xd_gendisk = { xd_sizes, /* block sizes */ 0, /* number */ (void *) xd_info, /* internal */ - NULL /* next */ + NULL, /* next */ + &xd_fops, /* file operations */ }; static struct block_device_operations xd_fops = { open: xd_open, @@ -151,13 +156,16 @@ static struct timer_list xd_timer = { NULL, NULL, 0, 0, (timeout_fn) xd_wakeup } 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 */ int __init 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_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ xd_gendisk.next = gendisk_head; @@ -1149,7 +1157,7 @@ int init_module(void) printk(KERN_INFO "XD: Loaded as a module.\n"); if (!xd_drives) { /* no drives detected - unload module */ - unregister_blkdev(MAJOR_NR, "xd"); + devfs_unregister_blkdev(MAJOR_NR, "xd"); xd_done(); return (-1); } @@ -1161,7 +1169,7 @@ void cleanup_module(void) { 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--) { @@ -1173,6 +1181,7 @@ void cleanup_module(void) } } xd_done(); + devfs_unregister (devfs_handle); if (xd_drives) { free_irq(xd_irq, NULL); free_dma(xd_dma); diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c index 74f24070b1ee..e7df93ac6bf5 100644 --- a/drivers/cdrom/aztcd.c +++ b/drivers/cdrom/aztcd.c @@ -183,6 +183,7 @@ #include #include #include +#include #ifndef AZT_KERNEL_PRIOR_2_1 #include @@ -1785,7 +1786,9 @@ int __init aztcd_init(void) return -EIO; } } - if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) + devfs_register (NULL, "aztcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &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); @@ -1811,7 +1814,9 @@ int __init aztcd_init(void) void __exit aztcd_exit(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 --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index c40e0ae28dea..c20ce1b93982 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -291,6 +291,7 @@ int cdrom_get_next_writable(kdev_t dev, long *next_writable); static void cdrom_sysctl_register(void); #endif /* CONFIG_SYSCTL */ static struct cdrom_device_info *topCdromPtr = NULL; +static devfs_handle_t devfs_handle = NULL; struct block_device_operations cdrom_fops = { @@ -313,6 +314,8 @@ int register_cdrom(struct cdrom_device_info *cdi) int major = MAJOR(cdi->dev); struct cdrom_device_ops *cdo = cdi->ops; int *change_capability = (int *)&cdo->capability; /* hack */ + char vname[16]; + static unsigned int cdrom_counter = 0; cdinfo(CD_OPEN, "entering register_cdrom\n"); @@ -351,6 +354,31 @@ int register_cdrom(struct cdrom_device_info *cdi) if (check_media_type==1) cdi->options |= (int) CDO_CHECK_TYPE; + if (!devfs_handle) + devfs_handle = devfs_mk_dir (NULL, "cdroms", 6, NULL); + sprintf (vname, "cdrom%u", cdrom_counter++); + if (cdi->de) { + int pos; + devfs_handle_t slave; + char rname[64]; + + pos = devfs_generate_path (cdi->de, rname + 3, + sizeof rname - 3); + if (pos >= 0) { + strncpy (rname + pos, "../", 3); + devfs_mk_symlink (devfs_handle, vname, 0, + DEVFS_FL_DEFAULT, + rname + pos, 0, &slave, NULL); + devfs_auto_unregister (cdi->de, slave); + } + } + else { + cdi->de = + devfs_register (devfs_handle, vname, 0, DEVFS_FL_DEFAULT, + MAJOR (cdi->dev), MINOR (cdi->dev), + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + &cdrom_fops, NULL); + } cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); cdi->next = topCdromPtr; topCdromPtr = cdi; @@ -382,6 +410,7 @@ int unregister_cdrom(struct cdrom_device_info *unreg) else topCdromPtr = cdi->next; cdi->ops->n_minors--; + devfs_unregister (cdi->de); cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); return 0; } @@ -2483,6 +2512,7 @@ int init_module(void) #ifdef CONFIG_SYSCTL cdrom_sysctl_register(); #endif + devfs_handle = devfs_mk_dir (NULL, "cdroms", 6, NULL); return 0; } @@ -2491,7 +2521,8 @@ void cleanup_module(void) 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 --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c index d22cbd92185b..0b8d942b4344 100644 --- a/drivers/cdrom/cdu31a.c +++ b/drivers/cdrom/cdu31a.c @@ -162,6 +162,7 @@ #include #include #include +#include #include #include #include @@ -3441,7 +3442,7 @@ cdu31a_init(void) 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; @@ -3543,7 +3544,7 @@ cdu31a_init(void) } errout0: printk("Unable to register CDU-31a with Uniform cdrom driver\n"); - if (unregister_blkdev(MAJOR_NR, "cdu31a")) + if (devfs_unregister_blkdev(MAJOR_NR, "cdu31a")) { printk("Can't unregister block device for cdu31a\n"); } @@ -3562,7 +3563,7 @@ cdu31a_exit(void) printk("Can't unregister cdu31a from Uniform cdrom driver\n"); return; } - if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) + if ((devfs_unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) { printk("Can't unregister cdu31a\n"); return; diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c index 9a4d6dcf6bc5..05aff4b598d4 100644 --- a/drivers/cdrom/cm206.c +++ b/drivers/cdrom/cm206.c @@ -187,6 +187,7 @@ History: #include #include #include +#include #include #include #include @@ -1277,7 +1278,7 @@ static void cleanup(int level) 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; } @@ -1390,7 +1391,7 @@ int __init cm206_init(void) 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; diff --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c index b5da453f30da..f563a16c0313 100644 --- a/drivers/cdrom/gscd.c +++ b/drivers/cdrom/gscd.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include @@ -991,12 +992,13 @@ long err; void __exit exit_gscd(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" ); } @@ -1067,12 +1069,14 @@ int result; 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", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &gscd_fops, NULL); blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); blksize_size[MAJOR_NR] = gscd_blocksizes; diff --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c index 88f57c3ce1f8..44cde3738ca9 100644 --- a/drivers/cdrom/mcd.c +++ b/drivers/cdrom/mcd.c @@ -86,6 +86,7 @@ #include #include #include +#include #include #include #include @@ -1156,7 +1157,7 @@ static void cleanup(int level) 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; } @@ -1181,7 +1182,7 @@ int __init mcd_init(void) 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); diff --git a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c index 38247218332d..805044841396 100644 --- a/drivers/cdrom/mcdx.c +++ b/drivers/cdrom/mcdx.c @@ -74,6 +74,7 @@ static const char *mcdx_c_version #include #define MAJOR_NR MITSUMI_X_CDROM_MAJOR #include +#include /* for compatible parameter passing with "insmod" */ #define mcdx_drive_map mcdx @@ -995,7 +996,7 @@ void __exit mcdx_exit(void) for (i = 0; i < MCDX_NDRIVES; i++) { struct s_drive_stuff *stuffp; - if (unregister_cdrom(&mcdx_info)) { + if (unregister_cdrom(&mcdx_info)) { printk(KERN_WARNING "Can't unregister cdrom mcdx\n"); return; } @@ -1012,7 +1013,7 @@ void __exit mcdx_exit(void) 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 @@ -1123,7 +1124,7 @@ int __init mcdx_init_drive(int 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); @@ -1181,7 +1182,7 @@ int __init mcdx_init_drive(int drive) 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; } diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c index 3bd1ce6db347..9a167203748d 100644 --- a/drivers/cdrom/optcd.c +++ b/drivers/cdrom/optcd.c @@ -71,6 +71,8 @@ #include #include #include +#include + #include #define MAJOR_NR OPTICS_CDROM_MAJOR @@ -2061,12 +2063,13 @@ int __init optcd_init(void) 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", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &opt_fops, NULL); hardsect_size[MAJOR_NR] = &hsecsize; blksize_size[MAJOR_NR] = &blksize; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); @@ -2081,7 +2084,9 @@ int __init optcd_init(void) void __exit optcd_exit(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 --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index d8127febf9b5..4c88dacc2cfd 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -338,6 +338,7 @@ #include #include #include +#include #include #include #include @@ -5584,12 +5585,16 @@ static int __init config_spea(void) * 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 __SBPCD_INIT(void) #else int __init SBPCD_INIT(void) #endif MODULE { + char nbuff[16]; int i=0, j=0; int addr[2]={1, CDROM_PORT}; int port_index; @@ -5732,7 +5737,7 @@ int __init SBPCD_INIT(void) 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 @@ -5747,6 +5752,7 @@ int __init SBPCD_INIT(void) request_region(CDo_command,4,major_name); + devfs_handle = devfs_mk_dir (NULL, "sbp", 0, NULL); for (j=0;jdev = MKDEV(MAJOR_NR, j); strncpy(sbpcd_infop->name,major_name, sizeof(sbpcd_infop->name)); + sprintf (nbuff, "c%dt%d/cd", SBPCD_ISSUE - 1, D_S[j].drv_id); + sbpcd_infop->de = + devfs_register (devfs_handle, nbuff, 0, DEVFS_FL_DEFAULT, + MAJOR_NR, j, S_IFBLK | S_IRUGO | S_IWUGO, + 0, 0, &cdrom_fops, NULL); if (register_cdrom(sbpcd_infop)) { printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n"); } - /* * set the block size */ @@ -5829,13 +5839,14 @@ void sbpcd_exit(void) { 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; } release_region(CDo_command,4); + devfs_unregister (devfs_handle); for (j=0;j #include #include +#include #include #include @@ -1471,7 +1472,7 @@ int __init sjcd_init( void ){ 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 ); } @@ -1563,6 +1564,8 @@ int __init sjcd_init( void ){ } printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base); + devfs_register (NULL, "sjcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &sjcd_fops, NULL); sjcd_present++; return( 0 ); @@ -1571,7 +1574,7 @@ int __init sjcd_init( void ){ 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 ); @@ -1582,6 +1585,8 @@ sjcd_cleanup(void) void __exit sjcd_exit(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 --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c index 689b26a74547..1f5fea7b9392 100644 --- a/drivers/cdrom/sonycd535.c +++ b/drivers/cdrom/sonycd535.c @@ -124,6 +124,7 @@ #include #include #include +#include #define REALLY_SLOW_IO #include @@ -1586,7 +1587,12 @@ sony535_init(void) 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, 0, + DEVFS_FL_DEFAULT, + MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, + 0, 0, &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; @@ -1684,7 +1690,9 @@ sony535_exit(void) 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 --git a/drivers/char/Makefile b/drivers/char/Makefile index 88d28c2ade03..bde31a4ba491 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -37,7 +37,8 @@ obj-y += tty_io.o n_tty.o tty_ioctl.o mem.o raw.o pty.o misc.o random.o # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. export-objs := busmouse.o console.o i2c-old.o keyboard.o \ - misc.o pty.o random.o selection.o serial.o videodev.o + misc.o pty.o random.o selection.o serial.o videodev.o \ + tty_io.o KEYMAP =defkeymap.o KEYBD =pc_keyb.o diff --git a/drivers/char/console.c b/drivers/char/console.c index 49c7b17fdeb2..89c83427e26a 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,10 @@ struct consw *conswitchp = NULL; #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) +extern int tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void vcs_make_devfs (unsigned int index, int unregister); + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -2294,13 +2299,18 @@ static int con_open(struct tty_struct *tty, struct file * filp) 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) @@ -2352,7 +2362,7 @@ void __init con_init(void) memset(&console_driver, 0, sizeof(struct tty_driver)); console_driver.magic = TTY_DRIVER_MAGIC; - console_driver.name = "tty"; + console_driver.name = "vc/%d"; console_driver.name_base = 1; console_driver.major = TTY_MAJOR; console_driver.minor_start = 1; @@ -2360,6 +2370,11 @@ void __init con_init(void) 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; @@ -2520,6 +2535,19 @@ static void set_vesa_blanking(unsigned long arg) 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, 0, + console_driver.minor_start + i); +} + static void vesa_powerdown(void) { struct vc_data *c = vc_cons[fg_console].d; diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index ff41c0fdd29a..8ed17fd712cf 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -510,6 +511,9 @@ static struct file_operations dsp56k_fops = { /****** Init and module functions ******/ + +static devfs_handle_t devfs_handle = NULL; + int __init dsp56k_init(void) { if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) { @@ -517,10 +521,14 @@ int __init dsp56k_init(void) 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", 0, DEVFS_FL_NONE, + DSP56K_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &dsp56k_fops, NULL); dsp56k.in_use = 0; @@ -537,6 +545,7 @@ int init_module(void) void cleanup_module(void) { - unregister_chrdev(DSP56K_MAJOR, "dsp56k"); + devfs_unregister_chrdev(DSP56K_MAJOR, "dsp56k"); + devfs_unregister (devfs_handle); } #endif /* MODULE */ diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index ac913935ba6b..58ce08712c04 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -64,6 +64,7 @@ #include /* for __init, module_{init,exit} */ #include /* for POLLIN, etc. */ #include /* local header file for DoubleTalk values */ +#include #ifdef TRACING #define TRACE_TEXT(str) printk(str); @@ -352,19 +353,25 @@ static int dtlk_release(struct inode *inode, struct file *file) return 0; } +static devfs_handle_t devfs_handle; + static 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", 0, DEVFS_FL_NONE, + dtlk_major, DTLK_MINOR, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &dtlk_fops, NULL); init_timer(&dtlk_timer); dtlk_timer.function = dtlk_timer_tick; @@ -383,7 +390,8 @@ static void __exit dtlk_cleanup (void) 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 --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c index 4795eafdc534..5f181aa31d1b 100644 --- a/drivers/char/ftape/zftape/zftape-init.c +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -35,6 +35,7 @@ #endif #include #include +#include #include #if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16) @@ -403,6 +404,7 @@ extern int zft_compressor_init(void); */ int __init zft_init(void) { + int i; TRACE_FUN(ft_t_flow); #ifdef MODULE @@ -431,7 +433,43 @@ KERN_INFO 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++) { + char devname[8]; + + sprintf (devname, "qft%i", i); + devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + QIC117_TAPE_MAJOR, i, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &zft_cdev, NULL); + sprintf (devname, "nqft%i", i); + devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + QIC117_TAPE_MAJOR, i + 4, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &zft_cdev, NULL); + sprintf (devname, "zqft%i", i); + devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + QIC117_TAPE_MAJOR, i + 16, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &zft_cdev, NULL); + sprintf (devname, "nzqft%i", i); + devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + QIC117_TAPE_MAJOR, i + 20, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &zft_cdev, NULL); + sprintf (devname, "rawqft%i", i); + devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + QIC117_TAPE_MAJOR, i + 32, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &zft_cdev, NULL); + sprintf (devname, "nrawqft%i", i); + devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + QIC117_TAPE_MAJOR, i + 36, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &zft_cdev, NULL); + } + #if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) register_symtab(&zft_symbol_table); /* add global zftape symbols */ #endif @@ -471,13 +509,30 @@ int init_module(void) */ 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"); TRACE_EXIT; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 494fbc260bd0..0b1ab10e1f98 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -829,12 +830,14 @@ int init_module() /*****************************************************************************/ +static devfs_handle_t devfs_handle = NULL; + void cleanup_module() { stlibrd_t *brdp; stliport_t *portp; unsigned long flags; - int i, j; + int i, j, k; #if DEBUG printk("cleanup_module()\n"); @@ -863,10 +866,10 @@ void cleanup_module() restore_flags(flags); return; } - if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) + devfs_unregister (devfs_handle); + if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) printk("STALLION: failed to un-register serial memory device, " "errno=%d\n", -i); - if (stli_tmpwritebuf != (char *) NULL) kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE); if (stli_txcookbuf != (char *) NULL) @@ -5321,9 +5324,15 @@ int __init stli_init(void) * 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"); + devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); + devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, + STL_SIOMEMMAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &stli_fsiomem, NULL); + /* * Set up the tty driver structure and register us as a driver. * Also setup the callout tty device. diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c index 48af4c713bbb..854c6131c4b8 100644 --- a/drivers/char/joystick/joystick.c +++ b/drivers/char/joystick/joystick.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -694,6 +695,10 @@ struct js_port *js_unregister_port(struct js_port *port) 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 @@ int js_register_device(struct js_port *port, int number, int axes, int buttons, 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) + @@ -732,6 +738,12 @@ int js_register_device(struct js_port *port, int number, int axes, int buttons, curd->name = all += axes * sizeof(struct js_corr); strcpy(curd->name, name); + sprintf (devfs_name, "analogue%d", number); + curd->devfs_handle = devfs_register (devfs_handle, devfs_name, 0, + DEVFS_FL_DEFAULT, + JOYSTICK_MAJOR, number, + S_IFCHR | S_IRUGO | S_IWUSR, 0, 0, + &js_fops, NULL); port->devs[number] = curd; port->axes[number] = curd->new.axes; @@ -760,6 +772,7 @@ void js_unregister_device(struct js_dev *dev) spin_unlock_irqrestore(&js_lock, flags); + devfs_unregister (dev->devfs_handle); kfree(dev); } @@ -788,10 +801,11 @@ int __init js_init(void) #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,8 +885,9 @@ int __init js_init(void) void cleanup_module(void) { del_timer(&js_timer); - if (unregister_chrdev(JOYSTICK_MAJOR, "js")) - printk(KERN_ERR "js: can't unregister device\n"); + devfs_unregister (devfs_handle); + if (devfs_unregister_chrdev(JOYSTICK_MAJOR, "js")) + printk(KERN_ERR "js: can't unregister device\n"); } #endif diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 0a0288410be7..b3ab9d489f44 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -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 @@ -118,6 +119,7 @@ #include #include #include +#include #include #include #include @@ -138,6 +140,8 @@ /* ROUND_UP macro from fs/select.c */ #define ROUND_UP(x,y) (((x)+(y)-1)/(y)) +static devfs_handle_t devfs_handle = NULL; + struct lp_struct lp_table[LP_NO]; static unsigned int lp_count = 0; @@ -772,7 +776,7 @@ int __init lp_init (void) lp_table[i].timeout = 10 * HZ; } - 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; } @@ -782,6 +786,22 @@ int __init lp_init (void) return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "printers", 0, NULL); + if (lp_count) { + 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, 0, + DEVFS_FL_DEFAULT, LP_MAJOR, i, + S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + &lp_fops, NULL); + } + } + if (!lp_count) { printk (KERN_INFO "lp: driver loaded but no devices found\n"); #ifndef CONFIG_PARPORT_12843 @@ -832,7 +852,8 @@ void cleanup_module(void) unregister_console (&lpcons); #endif - unregister_chrdev(LP_MAJOR, "lp"); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(LP_MAJOR, "lp"); for (offset = 0; offset < LP_NO; offset++) { if (lp_table[offset].dev == NULL) continue; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 70cfa12eef63..e239cf555283 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -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 @@ -570,14 +573,42 @@ static int memory_open(struct inode * inode, struct file * filp) 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, 0, DEVFS_FL_NONE, + MEM_MAJOR, list[i].minor, + list[i].mode | S_IFCHR, 0, 0, + list[i].fops, NULL); +} + static struct file_operations memory_fops = { open: memory_open, /* just a selector for the real open */ }; int __init 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(); raw_init(); #ifdef CONFIG_I2C diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 48b2eac66729..53c7078e3033 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -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 @@ -121,6 +124,8 @@ static struct file_operations misc_fops = { int misc_register(struct miscdevice * misc) { + static devfs_handle_t devfs_handle = NULL; + if (misc->next || misc->prev) return -EBUSY; if (misc->minor == MISC_DYNAMIC_MINOR) { @@ -133,6 +138,13 @@ int misc_register(struct miscdevice * misc) } 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, 0, DEVFS_FL_NONE, + MISC_MAJOR, misc->minor, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + misc->fops, NULL); /* * Add it to the front, so that later devices can "override" @@ -154,6 +166,7 @@ int misc_deregister(struct miscdevice * misc) 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)); } @@ -217,11 +230,10 @@ int __init misc_init(void) #ifdef CONFIG_SGI streamable_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 --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 6a6ff8081478..0d43916aaab0 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include #include #include @@ -579,13 +581,20 @@ static struct file_operations pp_fops = { release: pp_release, }; +static devfs_handle_t devfs_handle = NULL; + static int __init ppdev_init (void) { - if (register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) { + if (devfs_register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) { printk (KERN_WARNING CHRDEV ": unable to get major %d\n", PP_MAJOR); return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "parports", 0, NULL); + devfs_register_series (devfs_handle, "%u", PARPORT_MAX, + DEVFS_FL_DEFAULT, PP_MAJOR, 0, + S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + &pp_fops, NULL); printk (KERN_INFO PP_VERSION "\n"); return 0; @@ -594,7 +603,8 @@ static int __init ppdev_init (void) static void __exit ppdev_cleanup (void) { /* Clean up all parport stuff */ - unregister_chrdev (PP_MAJOR, CHRDEV); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev (PP_MAJOR, CHRDEV); } module_init(ppdev_init); diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 5f35b24ad66c..b4b2c71bc410 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -10,6 +10,7 @@ #include #include /* For EXPORT_SYMBOL */ +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -28,6 +30,10 @@ #define BUILDING_PTY_C 1 #include +extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + struct pty_struct { int magic; wait_queue_head_t open_wait; @@ -94,6 +100,7 @@ static void pty_close(struct tty_struct * tty, struct file * filp) } } #endif + tty_unregister_devfs (&tty->link->driver, MINOR (tty->device)); tty_vhangup(tty->link); } } @@ -323,6 +330,11 @@ static int pty_open(struct tty_struct *tty, struct file * filp) 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_WAIT, + tty->link->driver.minor_start + + MINOR(tty->device)-tty->driver.minor_start); retval = 0; out: return retval; @@ -346,7 +358,7 @@ int __init pty_init(void) 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%d"; pty_driver.major = PTY_MASTER_MAJOR; pty_driver.minor_start = 0; pty_driver.num = NR_PTYS; @@ -377,12 +389,16 @@ int __init pty_init(void) 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%d"; 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; @@ -403,6 +419,7 @@ int __init pty_init(void) /* 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++ ) { int j; @@ -415,6 +432,7 @@ int __init pty_init(void) 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]; @@ -424,7 +442,7 @@ int __init pty_init(void) init_waitqueue_head(&ptm_state[i][j].open_wait); pts_driver[i] = pty_slave_driver; - pts_driver[i].name = "pts"; + pts_driver[i].name = "pts/%d"; pts_driver[i].proc_entry = 0; pts_driver[i].major = UNIX98_PTY_SLAVE_MAJOR+i; pts_driver[i].minor_start = 0; diff --git a/drivers/char/serial.c b/drivers/char/serial.c index e728197e7013..19aaa2c38dd9 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -215,7 +215,12 @@ static char *serial_revdate = "2000-1-27"; #else #define _INLINE_ #endif - + +extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + + static char *serial_name = "Serial driver"; static DECLARE_TASK_QUEUE(tq_serial); @@ -4394,7 +4399,7 @@ int __init rs_init(void) #if (LINUX_VERSION_CODE > 0x20100) serial_driver.driver_name = "serial"; #endif - serial_driver.name = "ttyS"; + serial_driver.name = "tts/%d"; serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; serial_driver.num = NR_PORTS; @@ -4403,7 +4408,7 @@ int __init rs_init(void) 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; @@ -4438,7 +4443,7 @@ int __init rs_init(void) * major number and the subtype code. */ callout_driver = serial_driver; - callout_driver.name = "cua"; + callout_driver.name = "cua/%d"; callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; #if (LINUX_VERSION_CODE >= 131343) @@ -4485,6 +4490,10 @@ int __init rs_init(void) (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); } #ifdef ENABLE_SERIAL_PCI probe_serial_pci(); @@ -4562,6 +4571,10 @@ int register_serial(struct serial_struct *req) state->iomem_base ? (unsigned long)state->iomem_base : (unsigned long)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; } @@ -4576,6 +4589,13 @@ void unregister_serial(int line) tty_hangup(state->info->tty); state->type = PORT_UNKNOWN; printk(KERN_INFO "tty%02d unloaded\n", state->line); + /* 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 --git a/drivers/char/stallion.c b/drivers/char/stallion.c index d936eef41d55..340ecb7dda21 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -774,13 +775,15 @@ int init_module() /*****************************************************************************/ +static devfs_handle_t devfs_handle = NULL; + void cleanup_module() { stlbrd_t *brdp; stlpanel_t *panelp; stlport_t *portp; unsigned long flags; - int i, j, k; + int i, j, k, l; #if DEBUG printk("cleanup_module()\n"); @@ -806,7 +809,8 @@ void cleanup_module() restore_flags(flags); return; } - if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) + devfs_unregister (devfs_handle); + if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) printk("STALLION: failed to un-register serial memory device, " "errno=%d\n", -i); @@ -3213,8 +3217,13 @@ int __init stl_init(void) * 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"); + devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); + devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, + STL_SIOMEMMAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &stl_fsiomem, NULL); /* * Set up the tty driver structure and register us as a driver. diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 4f6a1cef3ecd..d29c63407983 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -89,7 +89,8 @@ #include #include #include - +#include + #include #include #include @@ -2902,7 +2903,7 @@ int __init qic02_tape_init(void) #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 @@ -2910,7 +2911,38 @@ int __init qic02_tape_init(void) #endif return -ENODEV; } - + devfs_register (NULL, "ntpqic11", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 2, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic11", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 3, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); + devfs_register (NULL, "ntpqic24", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 4, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic24", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 5, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); + devfs_register (NULL, "ntpqic120", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 6, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic120", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 7, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); + devfs_register (NULL, "ntpqic150", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 8, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); + devfs_register (NULL, "tpqic150", 0, DEVFS_FL_NONE, + QIC02_TAPE_MAJOR, 9, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + &qic02_tape_fops, NULL); init_waitqueue_head(&qic02_tape_transfer); /* prepare timer */ TIMEROFF; @@ -2957,7 +2989,15 @@ void cleanup_module(void) { 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 --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index abc27bb910d8..705d876b32e9 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -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,17 @@ #include #include #include +#include #include +#ifdef CONFIG_VT +extern void con_init_devfs (void); +#endif +void tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned minor); +void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) #define TTY_DEV MKDEV(TTYAUX_MAJOR,0) #define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1) @@ -107,6 +119,7 @@ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */ #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 /* @@ -149,16 +162,26 @@ extern int rs_8xx_init(void); /* * 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 + sprintf(buf, 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, @@ -1298,6 +1321,8 @@ retry_open: 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], 0, + pts_driver[major].minor_start + minor); noctty = 1; goto init_dev_done; @@ -1965,17 +1990,87 @@ void tty_default_put_char(struct tty_struct *tty, unsigned char ch) tty->driver.write(tty, 0, &ch, 1); } +/* + * 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; + uid_t uid = 0; + gid_t gid = 0; + struct tty_struct tty; + char buf[32]; + + flags |= DEVFS_FL_DEFAULT; + 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: + 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; + } + if (driver->type == TTY_DRIVER_TYPE_CONSOLE) { + flags |= DEVFS_FL_AOPEN_NOTIFY; + flags &= ~DEVFS_FL_AUTO_OWNER; + } +# ifdef CONFIG_UNIX98_PTYS + if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) && + (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) { + flags &= ~DEVFS_FL_AUTO_OWNER; + uid = current->uid; + gid = current->gid; + } +# endif + devfs_register (NULL, tty_name (&tty, buf), 0, flags, + driver->major, minor, mode, uid, gid, + &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) @@ -1989,6 +2084,10 @@ int tty_register_driver(struct tty_driver *driver) 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; } @@ -2018,11 +2117,11 @@ int tty_unregister_driver(struct tty_driver *driver) 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; @@ -2048,6 +2147,7 @@ int tty_unregister_driver(struct tty_driver *driver) 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; @@ -2148,6 +2248,13 @@ void __init tty_init(void) 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"; @@ -2163,7 +2270,7 @@ void __init tty_init(void) #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 --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 2aa610a9cef8..194bee13d52c 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -95,12 +96,11 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; unsigned int currcons = MINOR(inode->i_rdev); - long p = *ppos; - long viewed, attr, size, read; - char *con_buf0; + long pos = *ppos; + long viewed, attr, read; int col, maxcol; unsigned short *org = NULL; - ssize_t ret, orig_count; + ssize_t ret; down(&con_buf_sem); @@ -122,129 +122,150 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) if (!vc_cons_allocated(currcons)) goto unlock_out; - size = vcs_size(inode); ret = -EINVAL; - if (p < 0 || p > size) + if (pos < 0) goto unlock_out; - if (count > size - p) - count = size - p; - if (count > CON_BUF_SIZE) - count = CON_BUF_SIZE; - - /* Perform the whole read into the local con_buf. - * Then we can drop the console spinlock and safely - * attempt to move it to userspace. - */ - - con_buf0 = con_buf; - orig_count = count; - maxcol = video_num_columns; - if (!attr) { - org = screen_pos(currcons, p, viewed); - col = p % maxcol; - p += maxcol - col; - while (count-- > 0) { - *con_buf0++ = (vcs_scr_readw(currcons, org++) & 0xff); - if (++col == maxcol) { - org = screen_pos(currcons, p, viewed); - col = 0; - p += maxcol; + read = 0; + ret = 0; + while (count) { + char *con_buf0, *con_buf_start; + long this_round, size; + ssize_t orig_count; + long p = pos; + + /* Check whether we are above size each round, + * as copy_to_user at the end of this loop + * could sleep. + */ + size = vcs_size(inode); + if (pos >= size) + break; + if (count > size - pos) + count = size - pos; + + this_round = count; + if (this_round > CON_BUF_SIZE) + this_round = CON_BUF_SIZE; + + /* Perform the whole read into the local con_buf. + * Then we can drop the console spinlock and safely + * attempt to move it to userspace. + */ + + con_buf_start = con_buf0 = con_buf; + orig_count = this_round; + maxcol = video_num_columns; + if (!attr) { + org = screen_pos(currcons, p, viewed); + col = p % maxcol; + p += maxcol - col; + while (this_round-- > 0) { + *con_buf0++ = (vcs_scr_readw(currcons, org++) & 0xff); + if (++col == maxcol) { + org = screen_pos(currcons, p, viewed); + col = 0; + p += maxcol; + } } - } - } else { - if (p < HEADER_SIZE) { - size_t tmp_count; - - con_buf0[0] = (char) video_num_lines; - con_buf0[1] = (char) video_num_columns; - getconsxy(currcons, con_buf0 + 2); - - tmp_count = HEADER_SIZE - p; - if (tmp_count > count) - tmp_count = count; - - /* Advance state pointers and move on. */ - count -= tmp_count; - buf += tmp_count; - p += tmp_count; - con_buf0 += tmp_count; - } - p -= HEADER_SIZE; - col = (p/2) % maxcol; - if (count > 0) { - char tmp_byte; + } else { + if (p < HEADER_SIZE) { + size_t tmp_count; + + con_buf0[0] = (char) video_num_lines; + con_buf0[1] = (char) video_num_columns; + getconsxy(currcons, con_buf0 + 2); + + tmp_count = HEADER_SIZE - p; + if (tmp_count > this_round) + tmp_count = this_round; + + /* Advance state pointers and move on. */ + this_round -= tmp_count; + con_buf_start += p; + orig_count -= p; + p += tmp_count; + con_buf0 = con_buf + p; + } + p -= HEADER_SIZE; + col = (p/2) % maxcol; + if (this_round > 0) { + char tmp_byte; - org = screen_pos(currcons, p/2, viewed); - if ((p & 1) && count > 0) { + org = screen_pos(currcons, p/2, viewed); + if ((p & 1) && this_round > 0) { #ifdef __BIG_ENDIAN - tmp_byte = vcs_scr_readw(currcons, org++) & 0xff; + tmp_byte = vcs_scr_readw(currcons, org++) & 0xff; #else - tmp_byte = vcs_scr_readw(currcons, org++) >> 8; + tmp_byte = vcs_scr_readw(currcons, org++) >> 8; #endif - *con_buf0++ = tmp_byte; + *con_buf0++ = tmp_byte; - count--; - p++; - if (++col == maxcol) { - org = screen_pos(currcons, p/2, viewed); - col = 0; + this_round--; + p++; + if (++col == maxcol) { + org = screen_pos(currcons, p/2, viewed); + col = 0; + } } + p /= 2; + p += maxcol - col; } - p /= 2; - p += maxcol - col; - } - if (count > 1) { - size_t tmp_count = count; - unsigned short *tmp_buf = (unsigned short *)con_buf0; - - while (tmp_count > 1) { - *tmp_buf++ = vcs_scr_readw(currcons, org++); - tmp_count -= 2; - if (++col == maxcol) { - org = screen_pos(currcons, p, viewed); - col = 0; - p += maxcol; + if (this_round > 1) { + size_t tmp_count = this_round; + unsigned short *tmp_buf = (unsigned short *)con_buf0; + + while (tmp_count > 1) { + *tmp_buf++ = vcs_scr_readw(currcons, org++); + tmp_count -= 2; + if (++col == maxcol) { + org = screen_pos(currcons, p, viewed); + col = 0; + p += maxcol; + } } - } - /* Advance pointers, and move on. */ - count -= (char *)tmp_buf - con_buf0; - con_buf0 += (char *)tmp_buf - con_buf0; - } - if (count > 0) { - char tmp_byte; + /* Advance pointers, and move on. */ + this_round = tmp_count; + con_buf0 = (char*)tmp_buf; + } + if (this_round > 0) { + char tmp_byte; #ifdef __BIG_ENDIAN - tmp_byte = vcs_scr_readw(currcons, org) >> 8; + tmp_byte = vcs_scr_readw(currcons, org) >> 8; #else - tmp_byte = vcs_scr_readw(currcons, org) & 0xff; + tmp_byte = vcs_scr_readw(currcons, org) & 0xff; #endif - *con_buf0++ = tmp_byte; + *con_buf0++ = tmp_byte; + } } - } - /* Finally, temporarily drop the console lock and push - * all the data to userspace from our temporary buffer. - */ + /* Finally, temporarily drop the console lock and push + * all the data to userspace from our temporary buffer. + */ - spin_unlock_irq(&console_lock); - ret = copy_to_user(buf, con_buf0, orig_count); - spin_lock_irq(&console_lock); + spin_unlock_irq(&console_lock); + ret = copy_to_user(buf, con_buf_start, orig_count); + spin_lock_irq(&console_lock); - if (ret) { - ret = -EFAULT; - } else { - read = orig_count; - *ppos += read; - ret = read; + if (ret) { + read += (orig_count - ret); + ret = -EFAULT; + break; + } + buf += orig_count; + pos += orig_count; + read += orig_count; + count -= orig_count; } - + *ppos += read; + if (read) + ret = read; unlock_out: spin_unlock_irq(&console_lock); - up(&con_buf_sem); return ret; } @@ -254,12 +275,12 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; unsigned int currcons = MINOR(inode->i_rdev); - long p = *ppos; + long pos = *ppos; long viewed, attr, size, written; char *con_buf0; int col, maxcol; u16 *org0 = NULL, *org = NULL; - size_t ret, orig_count; + size_t ret; down(&con_buf_sem); @@ -284,125 +305,144 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) size = vcs_size(inode); ret = -EINVAL; - if (p < 0 || p > size) - goto unlock_out; - if (count > size - p) - count = size - p; - if (count > CON_BUF_SIZE) - count = CON_BUF_SIZE; - - /* Temporarily drop the console lock so that we can read - * in the write data from userspace safely. - */ - spin_unlock_irq(&console_lock); - ret = copy_from_user(con_buf, buf, count); - spin_lock_irq(&console_lock); - - if (ret) { - ret = -EFAULT; + if (pos < 0 || pos > size) goto unlock_out; - } + if (count > size - pos) + count = size - pos; + written = 0; + while (count) { + long this_round = count; + size_t orig_count; + long p; + + if (this_round > CON_BUF_SIZE) + this_round = CON_BUF_SIZE; + + /* Temporarily drop the console lock so that we can read + * in the write data from userspace safely. + */ + spin_unlock_irq(&console_lock); + ret = copy_from_user(con_buf, buf, this_round); + spin_lock_irq(&console_lock); + + if (ret) { + this_round -= ret; + if (!this_round) { + /* Abort loop if no data were copied. Otherwise + * fail with -EFAULT. + */ + if (written) + break; + ret = -EFAULT; + goto unlock_out; + } + } - /* The vcs_size might have changed while we slept to grab - * the user buffer, so recheck. - */ - size = vcs_size(inode); - ret = -EINVAL; - if (p > size) - goto unlock_out; - if (count > size - p) - count = size - p; + /* The vcs_size might have changed while we slept to grab + * the user buffer, so recheck. + * Return data written up to now on failure. + */ + size = vcs_size(inode); + if (pos >= size) + break; + if (this_round > size - pos) + this_round = size - pos; + + /* OK, now actually push the write to the console + * under the lock using the local kernel buffer. + */ + + con_buf0 = con_buf; + orig_count = this_round; + maxcol = video_num_columns; + p = pos; + if (!attr) { + org0 = org = screen_pos(currcons, p, viewed); + col = p % maxcol; + p += maxcol - col; - /* OK, now actually push the write to the console - * under the lock using the local kernel buffer. - */ + while (this_round > 0) { + unsigned char c = *con_buf0++; - con_buf0 = con_buf; - orig_count = count; - maxcol = video_num_columns; - if (!attr) { - org0 = org = screen_pos(currcons, p, viewed); - col = p % maxcol; - p += maxcol - col; - - while (count > 0) { - unsigned char c = *con_buf0++; - - count--; - vcs_scr_writew(currcons, - (vcs_scr_readw(currcons, org) & 0xff00) | c, org); - org++; - if (++col == maxcol) { - org = screen_pos(currcons, p, viewed); - col = 0; - p += maxcol; + this_round--; + vcs_scr_writew(currcons, + (vcs_scr_readw(currcons, org) & 0xff00) | c, org); + org++; + if (++col == maxcol) { + org = screen_pos(currcons, p, viewed); + col = 0; + p += maxcol; + } } - } - } else { - if (p < HEADER_SIZE) { - char header[HEADER_SIZE]; - - getconsxy(currcons, header + 2); - while (p < HEADER_SIZE && count > 0) { - count--; - header[p++] = *con_buf0++; + } else { + if (p < HEADER_SIZE) { + char header[HEADER_SIZE]; + + getconsxy(currcons, header + 2); + while (p < HEADER_SIZE && this_round > 0) { + this_round--; + header[p++] = *con_buf0++; + } + if (!viewed) + putconsxy(currcons, header + 2); } - if (!viewed) - putconsxy(currcons, header + 2); - } - p -= HEADER_SIZE; - col = (p/2) % maxcol; - if (count > 0) { - org0 = org = screen_pos(currcons, p/2, viewed); - if ((p & 1) && count > 0) { - char c; - - count--; - c = *con_buf0++; + p -= HEADER_SIZE; + col = (p/2) % maxcol; + if (this_round > 0) { + org0 = org = screen_pos(currcons, p/2, viewed); + if ((p & 1) && this_round > 0) { + char c; + + this_round--; + c = *con_buf0++; #ifdef __BIG_ENDIAN - vcs_scr_writew(currcons, c | - (vcs_scr_readw(currcons, org) & 0xff00), org); + vcs_scr_writew(currcons, c | + (vcs_scr_readw(currcons, org) & 0xff00), org); #else - vcs_scr_writew(currcons, (c << 8) | - (vcs_scr_readw(currcons, org) & 0xff), org); + vcs_scr_writew(currcons, (c << 8) | + (vcs_scr_readw(currcons, org) & 0xff), org); #endif - org++; - p++; + org++; + p++; + if (++col == maxcol) { + org = screen_pos(currcons, p/2, viewed); + col = 0; + } + } + p /= 2; + p += maxcol - col; + } + while (this_round > 1) { + unsigned short w; + + w = *((const unsigned short *)con_buf0); + vcs_scr_writew(currcons, w, org++); + con_buf0 += 2; + this_round -= 2; if (++col == maxcol) { - org = screen_pos(currcons, p/2, viewed); + org = screen_pos(currcons, p, viewed); col = 0; + p += maxcol; } } - p /= 2; - p += maxcol - col; - } - while (count > 1) { - unsigned short w; - - w = *((const unsigned short *)con_buf0); - vcs_scr_writew(currcons, w, org++); - con_buf0 += 2; - count -= 2; - if (++col == maxcol) { - org = screen_pos(currcons, p, viewed); - col = 0; - p += maxcol; - } - } - if (count > 0) { - unsigned char c; + if (this_round > 0) { + unsigned char c; - c = *con_buf0++; + c = *con_buf0++; #ifdef __BIG_ENDIAN - vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org); + vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org); #else - vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org); + vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org); #endif + } } + count -= orig_count; + written += orig_count; + buf += orig_count; + pos += orig_count; + if (org0) + update_region(currcons, (unsigned long)(org0), org-org0); } - if (org0) - update_region(currcons, (unsigned long)(org0), org-org0); - written = orig_count; *ppos += written; ret = written; @@ -430,12 +470,49 @@ static struct file_operations vcs_fops = { open: vcs_open, }; +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, 0, DEVFS_FL_DEFAULT, + VCS_MAJOR, index + 1, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, + VCS_MAJOR, index + 129, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + } +#endif /* CONFIG_DEVFS_FS */ +} + int __init 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", 1, DEVFS_FL_DEFAULT, + VCS_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + devfs_register (devfs_handle, "a", 1, DEVFS_FL_DEFAULT, + VCS_MAJOR, 128, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + return error; } diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index d10182d294ab..cfcdbfcdeaa9 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -229,6 +229,8 @@ static int video_mmap(struct inode * ino, struct file * file, return -EINVAL; } +extern struct file_operations video_fops; + /* * Video For Linux device drivers request registration here. */ @@ -239,24 +241,29 @@ int video_register_device(struct video_device *vfd, int type) 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; @@ -266,6 +273,8 @@ int video_register_device(struct video_device *vfd, int type) { 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 @@ -281,6 +290,12 @@ int video_register_device(struct video_device *vfd, int type) return err; } } + sprintf (name, "v4l/%s%d", name_base, i - base); + vfd->devfs_handle = + devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT, + VIDEO_MAJOR, vfd->minor, + S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + &video_fops, NULL); return 0; } } @@ -295,6 +310,7 @@ void video_unregister_device(struct video_device *vfd) { if(video_device[vfd->minor]!=vfd) panic("vfd: bad unregister"); + devfs_unregister (vfd->devfs_handle); video_device[vfd->minor]=NULL; MOD_DEC_USE_COUNT; } @@ -323,7 +339,7 @@ int videodev_init(void) 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; @@ -349,7 +365,7 @@ int init_module(void) void cleanup_module(void) { - unregister_chrdev(VIDEO_MAJOR, "video_capture"); + devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture"); } diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index c214b0b28dab..62f246407053 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -128,6 +128,7 @@ #include #include #include +#include #include "capiutil.h" #include "capicmd.h" @@ -621,14 +622,36 @@ int capi_init(void) init_waitqueue_head(&capidevs[j].recv_wait); } - 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); return -EIO; } + devfs_register (NULL, "isdn/capi20", 0, DEVFS_FL_DEFAULT, + capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &capi_fops, NULL); + devfs_register_series (NULL, "isdn/capi20.0%u", 10, DEVFS_FL_DEFAULT, + capi_major, 1, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &capi_fops, NULL); + devfs_register_series (NULL, "isdn/capi20.1%u", 10, DEVFS_FL_DEFAULT, + capi_major, 11, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &capi_fops, NULL); printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major); if ((capifuncs = attach_capi_interface(&cuser)) == 0) { - unregister_chrdev(capi_major, "capi20"); + devfs_unregister_chrdev(capi_major, "capi20"); + devfs_unregister(devfs_find_handle(NULL, "capi20", 0, + capi_major, 0, + DEVFS_SPECIAL_CHR, 0)); + for (j = 0; j < 10; j++) { + char devname[32]; + + sprintf(devname, "isdn/capi20.0%i", j); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, j + 1, DEVFS_SPECIAL_CHR, 0)); + sprintf (devname, "isdn/capi20.1%i", j); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, j + 11, DEVFS_SPECIAL_CHR, 0)); + } return -EIO; } (void)proc_init(); @@ -638,8 +661,18 @@ int capi_init(void) #ifdef MODULE void cleanup_module(void) { + int i; + char devname[32]; + (void)proc_exit(); - unregister_chrdev(capi_major, "capi20"); + devfs_unregister_chrdev(capi_major, "capi20"); + devfs_unregister(devfs_find_handle(NULL, "isdn/capi20", 0, capi_major, 0, DEVFS_SPECIAL_CHR, 0)); + for (i = 0; i < 10; i++) { + sprintf (devname, "isdn/capi20.0%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 1, DEVFS_SPECIAL_CHR, 0)); + sprintf (devname, "isdn/capi20.1%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 11, DEVFS_SPECIAL_CHR, 0)); + } (void) detach_capi_interface(&cuser); } diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index 21cc2e67d567..f1df11f5510c 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -436,6 +436,7 @@ #endif CONFIG_ISDN_DIVERSION #include "isdn_v110.h" #include "isdn_cards.h" +#include /* Debugflags */ #undef ISDN_DEBUG_STATCALLB @@ -465,6 +466,8 @@ isdn_divert_if *divert_if = NULL; /* interface to diversion module */ static int isdn_writebuf_stub(int, int, const u_char *, int, int); static void set_global_features(void); +static void isdn_register_devfs(int); +static void isdn_unregister_devfs(int); void isdn_MOD_INC_USE_COUNT(void) @@ -1097,6 +1100,7 @@ isdn_status_callback(isdn_ctrl * c) 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; @@ -2391,6 +2395,7 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) if (dev->chanmap[k] < 0) { dev->chanmap[k] = j; dev->drvmap[k] = drvidx; + isdn_register_devfs(k); break; } restore_flags(flags); @@ -2560,6 +2565,96 @@ isdn_getrev(const char *revision) return rev; } +#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, 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, 0, 0, + &isdn_fops, NULL); + sprintf (buf, "isdnctrl%d", k); + dev->devfs_handle_isdnctrlX[k] = + devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR, + 0, 0, &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 + + 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, 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_PPP + i, + 0600 | S_IFCHR, 0, 0, &isdn_fops, NULL); + } +# endif + + dev->devfs_handle_isdninfo = + devfs_register (devfs_handle, "isdninfo", 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR, + 0, 0, &isdn_fops, NULL); + dev->devfs_handle_isdnctrl = + devfs_register (devfs_handle, "isdnctrl", 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, 0, 0, + &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 */ + /* * Allocate and initialize all data, register modem-devices */ @@ -2586,11 +2681,12 @@ isdn_init(void) 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) @@ -2598,7 +2694,8 @@ isdn_init(void) 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 @@ -2608,7 +2705,8 @@ isdn_init(void) 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; } @@ -2674,9 +2772,10 @@ cleanup_module(void) 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"); } else { + isdn_cleanup_devfs(); del_timer(&dev->timer); vfree(dev); printk(KERN_NOTICE "ISDN-subsystem unloaded\n"); diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 9ed259a2060f..fd8c1645aac2 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -664,6 +665,11 @@ void adbdev_init() return; #endif - 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", 0, DEVFS_FL_NONE, + ADB_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &adb_fops, NULL); } diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index fad5da9fe6e6..ddfed868e8c5 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -549,6 +550,8 @@ static struct file_operations ppp_device_fops = { #define PPP_MAJOR 108 +static devfs_handle_t devfs_handle = NULL; + /* Called at boot time if ppp is compiled into the kernel, or at module load time (from init_module) if compiled as a module. */ int __init ppp_init(void) @@ -561,9 +564,13 @@ int __init ppp_init(void) #endif printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); - err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); + err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); if (err) printk(KERN_ERR "failed to register PPP device (%d)\n", err); + devfs_handle = devfs_register (NULL, "ppp", 0, DEVFS_FL_NONE, + PPP_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &ppp_device_fops, NULL); #ifndef MODULE #ifdef CONFIG_PPP_ASYNC ppp_async_init(); @@ -1613,7 +1620,8 @@ cleanup_module(void) /* should never happen */ if (!list_empty(&all_ppp_units)) printk(KERN_ERR "PPP: removing module but units remain!\n"); - if (unregister_chrdev(PPP_MAJOR, "ppp") != 0) + if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0) printk(KERN_ERR "PPP: failed to unregister PPP device\n"); + devfs_unregister (devfs_handle); } #endif /* MODULE */ diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 6883aa69f3e9..873a561ef4dc 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -84,6 +84,7 @@ #include #include #include +#include #include #include #include @@ -359,6 +360,8 @@ static void debug_status_out(struct cosa_data *cosa, int status); /* ---------- Initialization stuff ---------- */ +static devfs_handle_t devfs_handle = NULL; + #ifdef MODULE int init_module(void) #else @@ -366,18 +369,19 @@ static int __init cosa_init(void) #endif { int i; + 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; } @@ -386,9 +390,14 @@ static int __init cosa_init(void) cosa_cards[i].num = -1; for (i=0; io[i] != 0 && i < MAX_CARDS; i++) cosa_probe(io[i], irq[i], dma[i]); + devfs_handle = devfs_mk_dir (NULL, "cosa", 4, NULL); + devfs_register_series (devfs_handle, "%u", nr_cards, DEVFS_FL_DEFAULT, + cosa_major, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &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; @@ -397,9 +406,11 @@ static int __init cosa_init(void) #ifdef MODULE void cleanup_module (void) { + int i; struct cosa_data *cosa; printk(KERN_INFO "Unloading the cosa module\n"); + devfs_unregister (devfs_handle); for (cosa=cosa_cards; nr_cards--; cosa++) { int i; /* Clean up the per-channel data */ @@ -414,7 +425,7 @@ void cleanup_module (void) free_dma(cosa->dma); release_region(cosa->datareg,is_8bit(cosa)?2:4); } - unregister_chrdev(cosa_major, "cosa"); + devfs_unregister_chrdev(cosa_major, "cosa"); } #endif diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index 62d295a8edad..ec9a2da5667b 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -2198,9 +2199,8 @@ int __init sparcaudio_init(void) #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(); @@ -2221,7 +2221,7 @@ int __init sparcaudio_init(void) #ifdef MODULE void cleanup_module(void) { - unregister_chrdev(SOUND_MAJOR, "sparcaudio"); + devfs_unregister_chrdev(SOUND_MAJOR, "sparcaudio"); } #endif diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index 1d2bee01aab6..694eac86cee9 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -1004,6 +1005,8 @@ static inline void freeLptPort(int idx) #endif +static devfs_handle_t devfs_handle = NULL; + #ifdef MODULE int init_module(void) #else @@ -1017,14 +1020,19 @@ int __init bpp_init(void) 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_handle = devfs_mk_dir (NULL, "bpp", 3, NULL); + devfs_register_series (devfs_handle, "%u", BPP_NO, DEVFS_FL_DEFAULT, + BPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &bpp_fops, NULL); return 0; } @@ -1034,7 +1042,8 @@ void cleanup_module(void) { unsigned idx; - unregister_chrdev(BPP_MAJOR, dev_name); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(BPP_MAJOR, dev_name); for (idx = 0 ; idx < BPP_NO ; idx += 1) { if (instances[idx].present) diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index e66f3cedece1..91bf24a1852b 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1549,7 +1550,11 @@ void __init keyboard_zsinit(void (*put_char)(unsigned char)) 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", 0, DEVFS_FL_NONE, + KBD_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0, 0, + &kbd_fops, NULL); + if (devfs_register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){ printk ("Could not register /dev/kbd device\n"); return; } diff --git a/drivers/sbus/char/vfc.h b/drivers/sbus/char/vfc.h index b9ed039ce59d..c22f835c0cf4 100644 --- a/drivers/sbus/char/vfc.h +++ b/drivers/sbus/char/vfc.h @@ -1,6 +1,8 @@ #ifndef _LINUX_VFC_H_ #define _LINUX_VFC_H_ +#include + /* * The control register for the vfc is at offset 0x4000 * The first field ram bank is located at offset 0x5000 @@ -126,6 +128,7 @@ struct vfc_dev { volatile struct vfc_regs *regs; struct vfc_regs *phys_regs; unsigned int control_reg; + devfs_handle_t de; struct semaphore device_lock_sem; struct timer_list poll_timer; wait_queue_head_t poll_wait; diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 5f7af8642afa..a5e61bb9a367 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -41,6 +41,7 @@ #include "vfc.h" #include +static devfs_handle_t devfs_handle = NULL; /* For the directory */ struct vfc_dev **vfc_dev_lst; static char vfcstr[]="vfc"; static unsigned char saa9051_init_array[VFC_SAA9051_NR] = { @@ -140,6 +141,8 @@ int init_vfc_devstruct(struct vfc_dev *dev, int instance) int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) { + char devname[8]; + if(dev == NULL) { printk(KERN_ERR "VFC: Bogus pointer passed\n"); return -ENOMEM; @@ -162,6 +165,11 @@ int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) if (init_vfc_hw(dev)) return -EIO; + sprintf (devname, "%d", instance); + dev->de = devfs_register (devfs_handle, devname, 0, DEVFS_FL_DEFAULT, + VFC_MAJOR, instance, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &vfc_fops, NULL); return 0; } @@ -659,12 +667,13 @@ static int vfc_probe(void) 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; } + devfs_handle = devfs_mk_dir (NULL, "vfc", 3, NULL); instance = 0; for_all_sbusdev(sdev, sbus) { @@ -705,6 +714,7 @@ static void deinit_vfc_device(struct vfc_dev *dev) { if(dev == NULL) return; + devfs_unregister (dev->de); sbus_iounmap((unsigned long)dev->regs, sizeof(struct vfc_regs)); kfree(dev); } @@ -713,11 +723,12 @@ void cleanup_module(void) { struct vfc_dev **devp; - unregister_chrdev(VFC_MAJOR,vfcstr); + devfs_unregister_chrdev(VFC_MAJOR,vfcstr); for (devp = vfc_dev_lst; *devp; devp++) deinit_vfc_device(*devp); + devfs_unregister (devfs_handle); kfree(vfc_dev_lst); return; } diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 0f8454516fbb..82a1f2ef1bde 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -665,6 +665,7 @@ static Scsi_Host_Template builtin_scsi_hosts[] = * 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; @@ -674,7 +675,8 @@ int next_scsi_host = 0; void scsi_unregister(struct Scsi_Host * sh){ struct Scsi_Host * shpnt; - + Scsi_Host_Name *shn; + if(scsi_hostlist == sh) scsi_hostlist = sh->next; else { @@ -682,6 +684,16 @@ scsi_unregister(struct Scsi_Host * sh){ 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) @@ -708,16 +720,50 @@ scsi_unregister(struct Scsi_Host * sh){ 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 *)kmalloc(sizeof(struct Scsi_Host) + j, (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC); memset(retval, 0, sizeof(struct Scsi_Host) + j); + + /* 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; 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; init_waitqueue_head(&retval->host_wait); diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index e18b2e6d0452..e40381e7a67f 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -413,6 +413,16 @@ struct Scsi_Host extern void scsi_free_host_dev(Scsi_Device * SDpnt); extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host * SHpnt); +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 --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index fece6484d33d..33b517a2c078 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -636,6 +636,8 @@ int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt) return rtn; } +devfs_handle_t scsi_devfs_handle = NULL; + /* * scsi_do_cmd sends all the commands out to the low-level driver. It * handles the specifics required for each low level driver - ie queued @@ -1168,7 +1170,39 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt) static int proc_scsi_gen_write(struct file * file, const char * buf, unsigned long length, void *data); +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. @@ -1184,8 +1218,16 @@ int __init scsi_dev_init(void) 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... */ + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); /* * This makes /proc/scsi and /proc/scsi/scsi visible. */ @@ -1535,6 +1577,7 @@ static int proc_scsi_gen_write(struct file * file, const char * buf, * Nobody is using this device any more. * Free all of the command structures. */ + devfs_unregister (scd->de); scsi_release_commandblocks(scd); /* Now we can remove the device structure */ @@ -1834,6 +1877,7 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt) printk("Attached usage count = %d\n", SDpnt->attached); return; } + devfs_unregister (SDpnt->de); } } @@ -2221,7 +2265,49 @@ static void scsi_dump_status(int level) } #endif /* CONFIG_PROC_FS */ +static 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; +} + +#ifndef MODULE +__setup("scsihosts=", scsi_host_no_init); +#endif + +#ifdef MODULE +static char *scsihosts; + +MODULE_PARM(scsihosts, "s"); int init_module(void) { @@ -2252,6 +2338,8 @@ int init_module(void) scsi_loadable_module_flag = 1; + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); + scsi_host_no_init (scsihosts); /* * This is where the processing takes place for most everything * when commands are completed. @@ -2263,8 +2351,21 @@ int init_module(void) void cleanup_module(void) { + Scsi_Host_Name *shn, *shn2 = NULL; + remove_bh(SCSI_BH); + devfs_unregister (scsi_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); + #ifdef CONFIG_PROC_FS /* No, we're not here anymore. Don't show the /proc/scsi files. */ remove_proc_entry ("scsi/scsi", 0); diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index df294f2e482c..764ea6e608d7 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -16,6 +16,7 @@ #define _SCSI_H #include /* for CONFIG_SCSI_LOGGING */ +#include #include /* @@ -512,6 +513,7 @@ struct scsi_device { 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 --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 645edb67cb76..fe80f8ec8040 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -455,6 +455,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, int *sparse_lun, Scsi_Device ** SDpnt2, struct Scsi_Host *shpnt, char *scsi_result) { + char devname[64]; unsigned char scsi_cmd[MAX_COMMAND_SIZE]; struct Scsi_Device_Template *sdtpnt; Scsi_Device *SDtail, *SDpnt = *SDpnt2; @@ -462,6 +463,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, int bflags, type = -1; static int ghost_channel=-1, ghost_dev=-1; int org_lun = lun; + extern devfs_handle_t scsi_devfs_handle; SDpnt->host = shpnt; SDpnt->id = dev; @@ -637,6 +639,11 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, print_inquiry(scsi_result); + 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 (scsi_devfs_handle, devname, 0, NULL); + for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) if (sdtpnt->detect) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index ff3ea3cf5a91..71d84637a119 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -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 @@ -489,7 +491,8 @@ static struct gendisk sd_gendisk = NULL, /* block sizes */ 0, /* number */ NULL, /* internal */ - NULL /* next */ + NULL, /* next */ + &sd_fops, /* file operations */ }; static struct gendisk *sd_gendisks = &sd_gendisk; @@ -948,7 +951,7 @@ static int sd_init() 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; } @@ -988,6 +991,15 @@ static int sd_init() 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 = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr, + GFP_ATOMIC); + memset (sd_gendisks[i].de_arr, 0, + SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr); + sd_gendisks[i].flags = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags, + GFP_ATOMIC); + memset (sd_gendisks[i].flags, 0, + SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags); sd_gendisks[i].major = SD_MAJOR(i); sd_gendisks[i].major_name = "sd"; sd_gendisks[i].minor_shift = 4; @@ -1061,8 +1073,10 @@ static int sd_detect(Scsi_Device * SDp) return 1; } + static int sd_attach(Scsi_Device * SDp) { + unsigned int devnum; Scsi_Disk *dpnt; int i; @@ -1084,6 +1098,10 @@ static int sd_attach(Scsi_Device * SDp) 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; } @@ -1182,6 +1200,8 @@ static void sd_detach(Scsi_Device * SDp) sd_gendisks->part[index].nr_sects = 0; sd_sizes[index] = 0; } + devfs_register_partitions (&SD_GENDISK (i), + SD_MINOR_NUMBER (start), 1); /* unregister_disk() */ dpnt->has_part_table = 0; dpnt->device = NULL; @@ -1212,7 +1232,7 @@ void cleanup_module(void) 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 --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index e092f40a1147..2ccf9ac095b1 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -9,6 +9,8 @@ * Version 2 and 3 extensions to driver: * Copyright (C) 1998, 1999 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) @@ -50,6 +52,7 @@ #include #include #include + #include #include #include @@ -179,6 +182,7 @@ typedef struct sg_device /* holds the state of each scsi generic device */ wait_queue_head_t 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 */ @@ -1075,7 +1079,7 @@ static int sg_init() 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); @@ -1150,6 +1154,10 @@ static int sg_attach(Scsi_Device * scsidp) 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", 7, DEVFS_FL_NONE, + SCSI_GENERIC_MAJOR, k, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + &sg_fops, NULL); sg_template.nr_dev++; return 0; } @@ -1194,6 +1202,8 @@ static void sg_detach(Scsi_Device * scsidp) 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 @@ -1219,7 +1229,7 @@ int init_module(void) { 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"); #ifdef CONFIG_PROC_FS sg_proc_cleanup(); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 365c906607fd..3a792807ea37 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -23,6 +23,8 @@ * Modified by Jens Axboe - Uniform sr_packet() * interface, capabilities probe additions, ioctl cleanups, etc. * + * Modified by Richard Gooch to support devfs + * */ #include @@ -693,7 +695,7 @@ static int sr_init() return 0; if (!sr_registered) { - if (register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) { + if (devfs_register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) { printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR); return 1; } @@ -767,6 +769,11 @@ void sr_finish() sprintf(name, "sr%d", i); strcpy(scsi_CDs[i].cdi.name, name); + scsi_CDs[i].cdi.de = + devfs_register (scsi_CDs[i].device->de, "cd", 2, + DEVFS_FL_DEFAULT, MAJOR_NR, i, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + &cdrom_fops, NULL); register_cdrom(&scsi_CDs[i].cdi); } @@ -828,7 +835,7 @@ int init_module(void) void cleanup_module(void) { scsi_unregister_module(MODULE_SCSI_DEV, &sr_template); - unregister_blkdev(MAJOR_NR, "sr"); + devfs_unregister_blkdev(MAJOR_NR, "sr"); sr_registered--; if (scsi_CDs != NULL) { kfree((char *) scsi_CDs); diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 7b04411631f6..33e38b187855 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -13,6 +13,8 @@ Last modified: Fri Feb 11 19:43:57 2000 by makisara@kai.makisara.local Some small formal changes - aeb, 950809 + + Last modified: 18-JAN-1998 Richard Gooch Devfs support */ #include @@ -3221,7 +3223,7 @@ static int st_attach(Scsi_Device * SDp) Scsi_Tape *tpnt; ST_mode *STm; ST_partstat *STps; - int i; + int i, mode; if (SDp->type != TYPE_TAPE) return 1; @@ -3238,6 +3240,26 @@ static int st_attach(Scsi_Device * SDp) 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, 0, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5), + S_IFCHR | S_IRUGO | S_IWUGO, + 0, 0, &st_fops, NULL); + /* No-rewind entry */ + sprintf (name, "mt%sn", formats[mode]); + tpnt->de_n[mode] = + devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5) + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + 0, 0, &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; @@ -3327,7 +3349,7 @@ static int st_init() st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs); 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; @@ -3346,7 +3368,7 @@ static int st_init() 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; } @@ -3363,7 +3385,7 @@ static int st_init() for (j=0; j < i; j++) kfree(scsi_tapes[j].mt_status); kfree(scsi_tapes); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); return 1; } /* Initialize status */ @@ -3376,7 +3398,7 @@ static int st_init() 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"); for (i=0; i < st_template.dev_max; i++) kfree(scsi_tapes[i].mt_status); kfree(scsi_tapes); @@ -3407,11 +3429,17 @@ static int st_init() static void st_detach(Scsi_Device * SDp) { Scsi_Tape *tpnt; - int i; + int i, mode; for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++) if (tpnt->device == 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--; @@ -3436,7 +3464,7 @@ void cleanup_module(void) int i; 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) { for (i=0; i < st_template.dev_max; ++i) diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 479dca0798b5..2736349ae760 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -8,6 +8,7 @@ #ifndef _SCSI_H #include "scsi.h" #endif +#include /* The tape buffer descriptor. */ typedef struct { @@ -85,6 +86,8 @@ typedef struct { /* 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 --git a/drivers/sgi/char/shmiq.c b/drivers/sgi/char/shmiq.c index b49ca3f76429..af230c463031 100644 --- a/drivers/sgi/char/shmiq.c +++ b/drivers/sgi/char/shmiq.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -443,5 +444,12 @@ void shmiq_init (void) { printk ("SHMIQ setup\n"); - register_chrdev (SHMIQ_MAJOR, "shmiq", &shmiq_fops); + devfs_register_chrdev(SHMIQ_MAJOR, "shmiq", &shmiq_fops); + devfs_register (NULL, "shmiq", 0, DEVFS_FL_DEFAULT, + SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &shmiq_fops, NULL); + devfs_register_series (NULL, "qcntl%u", 2, DEVFS_FL_DEFAULT, + SHMIQ_MAJOR, 1, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &shmiq_fops, NULL); } diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index a8bc739cc99d..923b46e909b7 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -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; }; #ifdef CONFIG_SOUND_MSNDCLAS @@ -82,7 +86,7 @@ static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, if(*list==NULL || (*list)->unit_minor>n) break; list=&((*list)->next); - n+=16; + n+=SOUND_STEP; } if(n>=top) @@ -129,6 +133,7 @@ static void __sound_remove_unit(struct sound_unit **list, int unit) if(p->unit_minor==unit) { *list=p->next; + devfs_unregister (p->de); kfree(p); MOD_DEC_USE_COUNT; return; @@ -148,11 +153,15 @@ spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED; * 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; @@ -162,6 +171,13 @@ static int sound_insert_unit(struct sound_unit **list, struct file_operations *f 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, 0, + DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, + S_IFCHR | mode, 0, 0, fops, NULL); return r; } @@ -203,21 +219,76 @@ static struct sound_unit *chains[16]; 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); @@ -229,14 +300,16 @@ EXPORT_SYMBOL(register_sound_midi); 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); @@ -359,7 +432,8 @@ void cleanup_module(void) { /* 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) @@ -367,11 +441,12 @@ int init_module(void) 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 --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index 5593cf5ddb7a..a1d6829690b4 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -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 @@ -93,7 +98,6 @@ unsigned long seq_time = 0; /* Time for /dev/sequencer */ 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; @@ -811,6 +815,66 @@ bad1: 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, 0, DEVFS_FL_NONE, + SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), + S_IFCHR | dev_list[i].mode, 0, 0, + &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 @@ -849,6 +913,7 @@ soundcard_init(void) if (!create_proc_info_entry("sound", 0, NULL, sound_proc_get_info)) printk(KERN_ERR "sound: registering /proc/sound failed\n"); #endif + soundcard_register_devfs(1); /* register after we know # of devices */ } #ifdef MODULE @@ -931,6 +996,7 @@ void cleanup_module(void) return; } remove_proc_entry("sound", NULL); + soundcard_register_devfs (0); if (chrdev_registered) destroy_special_devices(); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 00d21faad9f2..717cc94f094e 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -25,6 +25,7 @@ #ifdef CONFIG_KMOD #include #endif +#include #if defined(__mc68000__) || defined(CONFIG_APUS) #include @@ -567,10 +568,13 @@ static struct file_operations fb_fops = { release: fb_release, }; +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; @@ -597,12 +601,17 @@ register_framebuffer(struct fb_info *fb_info) 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, 0, DEVFS_FL_NONE, + FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + &fb_fops, NULL); return 0; } int -unregister_framebuffer(const struct fb_info *fb_info) +unregister_framebuffer(struct fb_info *fb_info) { int i, j; @@ -611,7 +620,11 @@ unregister_framebuffer(const struct fb_info *fb_info) 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; @@ -624,7 +637,8 @@ fbmem_init(void) create_proc_read_entry("fb", 0, 0, fbmem_read_proc, NULL); - 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 --git a/fs/Config.in b/fs/Config.in index f93a799df436..dcbb13d1c49b 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -43,6 +43,13 @@ tristate 'OS/2 HPFS filesystem support' 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 + # 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 # who don't know what they are doing turn it on and complain when it diff --git a/fs/Makefile b/fs/Makefile index e79d69c1b0f7..74fc394ed233 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -18,8 +18,8 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \ - nfsd nls devpts adfs partitions qnx4 udf bfs cramfs openpromfs \ - autofs4 + nfsd nls devpts devfs adfs partitions qnx4 udf bfs cramfs \ + openpromfs autofs4 SUB_DIRS := partitions @@ -105,6 +105,10 @@ else endif endif +ifdef CONFIG_DEVFS_FS +SUB_DIRS += devfs +endif + ifeq ($(CONFIG_HFS_FS),y) SUB_DIRS += hfs else diff --git a/fs/block_dev.c b/fs/block_dev.c index b451332ed3d9..b5d665c29cb9 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -454,7 +455,7 @@ int get_blkdev_list(char * p) Return the function table of a device. Load the driver if needed. */ -static const struct block_device_operations * get_blkfops(unsigned int major) +const struct block_device_operations * get_blkfops(unsigned int major) { const struct block_device_operations *ret = NULL; @@ -518,11 +519,20 @@ int unregister_blkdev(unsigned int major, const char * name) int check_disk_change(kdev_t dev) { int i; - const struct block_device_operations * bdops; + const struct block_device_operations * bdops = NULL; struct super_block * sb; i = MAJOR(dev); - if (i >= MAX_BLKDEV || (bdops = blkdevs[i].bdops) == NULL) + if (i < MAX_BLKDEV) + bdops = blkdevs[i].bdops; + if (bdops == NULL) { + devfs_handle_t de; + + de = devfs_find_handle (NULL, NULL, 0, i, MINOR (dev), + DEVFS_SPECIAL_BLK, 0); + if (de) bdops = devfs_get_ops (de); + } + if (bdops == NULL) return 0; if (bdops->check_media_change == NULL) return 0; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 07ba223352ac..c00a6945c696 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,7 @@ unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ struct coda_sb_info coda_super_info; struct venus_comm coda_upc_comm; - + /* * Device operations */ @@ -358,13 +359,21 @@ int __init init_coda(void) return status; } +static devfs_handle_t devfs_handle = NULL; + int init_coda_psdev(void) { - if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) { + 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; } + devfs_handle = devfs_mk_dir (NULL, "coda", 4, NULL); + devfs_register_series (devfs_handle, "%u", MAX_CODADEVS, DEVFS_FL_NONE, + CODA_PSDEV_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &coda_psdev_fops, NULL); memset(&coda_upc_comm, 0, sizeof(coda_upc_comm)); memset(&coda_super_info, 0, sizeof(coda_super_info)); init_waitqueue_head(&coda_upc_comm.vc_waitq); @@ -407,7 +416,8 @@ void cleanup_module(void) if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) { printk("coda: failed to unregister filesystem\n"); } - unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); coda_sysctl_clean(); } diff --git a/fs/devfs/Makefile b/fs/devfs/Makefile new file mode 100644 index 000000000000..2b301b37afee --- /dev/null +++ b/fs/devfs/Makefile @@ -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 --git a/fs/devfs/base.c b/fs/devfs/base.c new file mode 100644 index 000000000000..ed9e04d69f1e --- /dev/null +++ b/fs/devfs/base.c @@ -0,0 +1,3467 @@ +/* devfs (Device FileSystem) driver. + + Copyright (C) 1998-2000 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 + 19991223 Richard Gooch + Created . + Work sponsored by SGI. + v0.91 + 20000203 Richard Gooch + Ported to kernel 2.3.42. + Removed . + Work sponsored by SGI. + v0.92 +*/ +#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.92 (20000203)" + +#ifndef DEVFS_NAME +# define DEVFS_NAME "devfs" +#endif + +/* Compatibility for 2.2.x kernel series */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)) +# define init_waitqueue_head(p) init_waitqueue(p) +# define DECLARE_WAITQUEUE(wait, p) struct wait_queue wait = {p, NULL} +typedef struct wait_queue *wait_queue_head_t; +#endif +#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 +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)) +# define SETUP_STATIC +# define __setup(a,b) +#else +# define SETUP_STATIC static +#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)) || (!(de)->registered&& !(de)->show_unreg))) + +#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_SHOW 0x01 +#define OPTION_NOMOUNT 0x02 +#define OPTION_ONLY 0x04 + +#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; + void *ops; + 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 show_unreg:1; + unsigned char hide: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 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.ops = &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, TRUE, 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. +*/ +{ + struct task_struct *p; + + for (p = current; p != &init_task; p = p->p_opptr) + { + if (p == fs_info->devfsd_task) 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 namelen, + unsigned int flags, + unsigned int major, unsigned int minor, + umode_t mode, uid_t uid, gid_t gid, + void *ops, 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. + 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 major number. Not needed for regular files. + The minor number. Not needed for regular files. + The default file mode. + The default UID of the file. + The default GID of the file. + The <> or <> 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 (ops == NULL) + { + if ( S_ISCHR (mode) ) ops = get_chrfops (major, 0); + else if ( S_ISBLK (mode) ) ops = (void *) get_blkfops (major); + if (ops == NULL) + { + printk ("%s: devfs_register(%s): NULL ops pointer\n", + DEVFS_NAME, name); + return NULL; + } + printk ("%s: devfs_register(%s): NULL ops, got %p from major table\n", + DEVFS_NAME, name, ops); + } + 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 (namelen < 1) namelen = strlen (name); + 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, namelen, 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; + de->u.fcb.default_uid = uid; + de->u.fcb.default_gid = gid; + de->registered = TRUE; + de->u.fcb.ops = ops; + 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->show_unreg = ( (boot_options & OPTION_SHOW) + || (flags & DEVFS_FL_SHOW_UNREG) ) ? 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.ops = 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->show_unreg = ( (boot_options & OPTION_SHOW) + || (flags & DEVFS_FL_SHOW_UNREG) ) ? TRUE : FALSE; + 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->show_unreg = (boot_options & OPTION_SHOW) ? TRUE : FALSE; + de->hide = FALSE; + return de; +} /* End Function devfs_mk_dir */ + +/*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->show_unreg) fl |= DEVFS_FL_SHOW_UNREG; + 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->show_unreg = (flags & DEVFS_FL_SHOW_UNREG) ? TRUE : FALSE; + 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*/ +void *devfs_get_ops (devfs_handle_t de) +/* [SUMMARY] Get the device operations for a devfs entry. + The handle to the device entry. + [RETURNS] A pointer to the device 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.ops; + return NULL; +} /* End Function devfs_get_ops */ + +/*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*/ +const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen) +/* [SUMMARY] Get the name for a device entry in its parent directory. + The handle to the device entry. + The length of the name is written here. This may be NULL. + [RETURNS] The name on success, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + if (namelen != NULL) *namelen = de->namelen; + return de->name; +} /* End Function devfs_get_name */ + +/*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 block_device_operations *bdops) +/* [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 block_device_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, bdops); +} /* 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*/ +SETUP_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, "show", 4) == 0) + { + boot_options |= OPTION_SHOW; + str += 4; + } + else 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_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_ops); +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) + { + mode_t mode = di->de->mode; + + di->mode = (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 block_device_operations *bdops = de->u.fcb.ops; + struct super_block * sb; + extern int warn_no_part; + + if ( !S_ISBLK (de->mode) ) return 0; + if (bdops == NULL) return 0; + if (bdops->check_media_change == NULL) return 0; + if ( !bdops->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 (bdops->revalidate) bdops->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) ) + inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major, + di->de->u.fcb.u.device.minor); + else if ( S_ISBLK (di->mode) ) + { + inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major, + di->de->u.fcb.u.device.minor); + inode->i_bdev = bdget (inode->i_rdev); + if (inode->i_bdev) inode->i_bdev->bd_op = di->de->u.fcb.ops; + else printk ("%s: read_inode(%d): no block device from bdget()\n", + DEVFS_NAME, (int) inode->i_ino); + } + else if ( S_ISFIFO (di->mode) ) inode->i_op = &fifo_inode_operations; + else if ( S_ISREG (di->mode) ) inode->i_size = di->de->u.fcb.u.file.size; + 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) ) + 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; + } + else if (di->ctime == 0) update_devfs_inode_from_entry (di); + 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->private_data = di->de->info; + if ( S_ISBLK (inode->i_mode) ) + { + file->f_op = &def_blk_fops; + if (df->ops) inode->i_bdev->bd_op = df->ops; + } + else file->f_op = df->ops; + 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 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) + 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_wait (struct dentry *dentry, int flags); + +static struct dentry_operations devfs_wait_dops = +{ + d_delete: devfs_d_delete, + d_release: devfs_d_release, + d_iput: devfs_d_iput, + d_revalidate: devfs_d_revalidate_wait, +}; + +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_wait_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_wait (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 (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_wait */ + + +/* 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_wait_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_wait (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_wait_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 (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_wait_dops) + devfs_d_revalidate_wait (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; + +#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; + 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); + 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); + 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; + 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.ops = 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->show_unreg = FALSE; + 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); + 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_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_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; + extern int do_mount (struct block_device *bdev, const char *dev_name, + const char *dir_name, const char * type, int flags, + void * data); + + if ( (boot_options & OPTION_NOMOUNT) ) return; + err = do_mount (NULL, "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 --git a/fs/devfs/util.c b/fs/devfs/util.c new file mode 100644 index 000000000000..7e22ae7cb932 --- /dev/null +++ b/fs/devfs/util.c @@ -0,0 +1,160 @@ +/* devfs (Device FileSystem) utilities. + + Copyright (C) 1999-2000 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 + 20000203 Richard Gooch + Changed operations pointer type to void *. +*/ +#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); + +/*PUBLIC_FUNCTION*/ +void devfs_register_series (devfs_handle_t dir, const char *format, + unsigned int num_entries, unsigned int flags, + unsigned int major, unsigned int minor_start, + umode_t mode, uid_t uid, gid_t gid, + void *ops, void *info) +/* [SUMMARY] Register a sequence of device entries. + The handle to the parent devfs directory entry. If this is NULL the + new names are relative to the root of the devfs. + The printf-style format string. A single "%u" is allowed. + A set of bitwise-ORed flags (DEVFS_FL_*). + The major number. Not needed for regular files. + The starting minor number. Not needed for regular files. + The default file mode. + The default UID of the file. + The default GID of the file. + The <> or <> 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] Nothing. +*/ +{ + unsigned int count; + char devname[128]; + + for (count = 0; count < num_entries; ++count) + { + sprintf (devname, format, count); + devfs_register (dir, devname, 0, flags, major, minor_start + count, + mode, uid, gid, ops, info); + } +} /* End Function devfs_register_series */ +EXPORT_SYMBOL(devfs_register_series); diff --git a/fs/filesystems.c b/fs/filesystems.c index 10c8f8bd3b71..d0a446c6eff4 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,8 @@ void __init filesystem_setup(void) init_proc_fs(); #endif + init_devfs_fs(); /* Header file may make this empty */ + #ifdef CONFIG_LOCKD nlmxdr_init(); #endif diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 95e8e84243e7..165f385162dd 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -181,7 +181,7 @@ encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry) } else { p = enc64(p, (u64) inode->i_size); } - p = enc64(p, inode->i_blksize * inode->i_blocks); + p = enc64(p, ((u64)inode->i_blocks) << 9); *p++ = htonl((u32) MAJOR(inode->i_rdev)); *p++ = htonl((u32) MINOR(inode->i_rdev)); p = enc64(p, (u64) inode->i_dev); @@ -211,7 +211,7 @@ encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) } else { p = enc64(p, (u64) fhp->fh_post_size); } - p = enc64(p, fhp->fh_post_blksize * fhp->fh_post_blocks); + p = enc64(p, ((u64)fhp->fh_post_blocks) << 9); *p++ = htonl((u32) MAJOR(fhp->fh_post_rdev)); *p++ = htonl((u32) MINOR(fhp->fh_post_rdev)); p = enc64(p, (u64) inode->i_dev); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 2532104cc991..f7b441ab1c0b 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -301,7 +301,7 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath) struct dentry *dentry, *result = NULL; struct dentry *tmp; int found =0; - long err; + int err; /* This semaphore is needed to make sure that only one unconnected (free) * dcache path ever exists, as otherwise two partial paths might get * joined together, which would be very confusing. diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 940460a1b20d..d69bba8d0ff8 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -42,7 +42,13 @@ extern struct svc_program nfsd_program; static void nfsd(struct svc_rqst *rqstp); struct timeval nfssvc_boot = { 0, 0 }; -static int nfsd_active = 0; +static struct svc_serv *nfsd_serv = NULL; + +struct nfsd_list { + struct list_head list; + struct task_struct *task; +}; +struct list_head nfsd_list = LIST_HEAD_INIT(nfsd_list); /* * Maximum number of nfsd processes @@ -52,45 +58,60 @@ static int nfsd_active = 0; int nfsd_svc(unsigned short port, int nrservs) { - struct svc_serv * serv; - int error; + int error; + int none_left; + struct list_head *victim; dprintk("nfsd: creating service\n"); error = -EINVAL; if (nrservs <= 0) - goto out; + nrservs = 0; if (nrservs > NFSD_MAXSERVS) nrservs = NFSD_MAXSERVS; - nfsd_nservers = nrservs; - - error = -ENOMEM; - nfsd_racache_init(); /* Readahead param cache */ - if (nfsd_nservers == 0) - goto out; - - serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE); - if (serv == NULL) + + /* Readahead param cache - will no-op if it already exists */ + error = nfsd_racache_init(2*nrservs); + if (error<0) goto out; - - error = svc_makesock(serv, IPPROTO_UDP, port); - if (error < 0) - goto failure; + if (!nfsd_serv) { + nfsd_serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE); + if (nfsd_serv == NULL) + goto out; + error = svc_makesock(nfsd_serv, IPPROTO_UDP, port); + if (error < 0) + goto failure; #if 0 /* Don't even pretend that TCP works. It doesn't. */ - error = svc_makesock(serv, IPPROTO_TCP, port); - if (error < 0) - goto failure; + error = svc_makesock(nfsd_serv, IPPROTO_TCP, port); + if (error < 0) + goto failure; #endif - - while (nrservs--) { - error = svc_create_thread(nfsd, serv); + get_fast_time(&nfssvc_boot); /* record boot time */ + } else + nfsd_serv->sv_nrthreads++; + nrservs -= (nfsd_serv->sv_nrthreads-1); + while (nrservs > 0) { + nrservs--; + error = svc_create_thread(nfsd, nfsd_serv); if (error < 0) break; } - -failure: - svc_destroy(serv); /* Release server */ -out: + victim = nfsd_list.next; + while (nrservs < 0 && victim != &nfsd_list) { + struct nfsd_list *nl = + list_entry(victim,struct nfsd_list, list); + victim = victim->next; + send_sig(SIGKILL, nl->task, 1); + nrservs++; + } + failure: + none_left = (nfsd_serv->sv_nrthreads == 1); + svc_destroy(nfsd_serv); /* Release server */ + if (none_left) { + nfsd_serv = NULL; + nfsd_racache_shutdown(); + } + out: return error; } @@ -101,7 +122,8 @@ static void nfsd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; - int oldumask, err; + int err; + struct nfsd_list me; /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; @@ -109,17 +131,17 @@ nfsd(struct svc_rqst *rqstp) exit_mm(current); current->session = 1; current->pgrp = 1; + sprintf(current->comm, "nfsd"); + current->fs->umask = 0; + /* Let svc_process check client's authentication. */ rqstp->rq_auth = 1; - sprintf(current->comm, "nfsd"); - oldumask = current->fs->umask; /* Set umask to 0. */ - current->fs->umask = 0; - if (!nfsd_active++) { - nfssvc_boot = xtime; /* record boot time */ - } lockd_up(); /* start lockd */ + me.task = current; + list_add(&me.list, &nfsd_list); + /* * The main request loop */ @@ -173,17 +195,16 @@ nfsd(struct svc_rqst *rqstp) /* Release lockd */ lockd_down(); - if (!--nfsd_active) { - printk("nfsd: last server exiting\n"); - /* revoke all exports */ - nfsd_export_shutdown(); - /* release read-ahead cache */ - nfsd_racache_shutdown(); + + /* Check if this is last thread */ + if (serv->sv_nrthreads==1) { + nfsd_serv = NULL; + nfsd_racache_shutdown(); /* release read-ahead cache */ } + list_del(&me.list); - /* Destroy the thread */ + /* Release the thread */ svc_exit_thread(rqstp); - current->fs->umask = oldumask; /* Release module */ MOD_DEC_USE_COUNT; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 15fd77b22411..2b4cb0c7db65 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -73,8 +73,6 @@ struct raparms { p_rawin; }; -int nfsd_nservers = 0; -#define FILECACHE_MAX (2 * nfsd_nservers) static struct raparms * raparml = NULL; static struct raparms * raparm_cache = NULL; @@ -374,7 +372,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access) int error; error = fh_verify(rqstp, fhp, 0, MAY_NOP); - if (error < 0) + if (error) goto out; export = fhp->fh_export; @@ -1698,34 +1696,34 @@ nfsd_racache_shutdown(void) { if (!raparm_cache) return; - dprintk("nfsd: freeing %d readahead buffers.\n", FILECACHE_MAX); + dprintk("nfsd: freeing readahead buffers.\n"); kfree(raparml); - nfsd_nservers = 0; raparm_cache = raparml = NULL; } /* * Initialize readahead param cache */ -void -nfsd_racache_init(void) +int +nfsd_racache_init(int cache_size) { int i; if (raparm_cache) - return; - raparml = kmalloc(sizeof(struct raparms) * FILECACHE_MAX, GFP_KERNEL); + return 0; + raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL); if (raparml != NULL) { dprintk("nfsd: allocating %d readahead buffers.\n", - FILECACHE_MAX); - memset(raparml, 0, sizeof(struct raparms) * FILECACHE_MAX); - for (i = 0; i < FILECACHE_MAX - 1; i++) { + cache_size); + memset(raparml, 0, sizeof(struct raparms) * cache_size); + for (i = 0; i < cache_size - 1; i++) { raparml[i].p_next = raparml + i + 1; } raparm_cache = raparml; } else { printk(KERN_WARNING "nfsd: Could not allocate memory read-ahead cache.\n"); - nfsd_nservers = 0; + return -ENOMEM; } + return 0; } diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index 34aab5f44061..4ffc43b0b9f2 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -466,7 +466,7 @@ int acorn_partition(struct gendisk *hd, kdev_t dev, r = adfspart_check_POWERTEC(hd, dev, first_sector, first_part_minor); #endif if (r < 0) - printk(" unable to read boot sectors / partition sectors\n"); + if (warn_no_part) printk(" unable to read boot sectors / partition sectors\n"); else if (r) printk("\n"); return r; diff --git a/fs/partitions/amiga.c b/fs/partitions/amiga.c index aa8605315508..4717a1b5f0a0 100644 --- a/fs/partitions/amiga.c +++ b/fs/partitions/amiga.c @@ -54,7 +54,7 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int 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; } @@ -80,7 +80,7 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int 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; } diff --git a/fs/partitions/atari.c b/fs/partitions/atari.c index ae8596e6a1ca..be7028e14f50 100644 --- a/fs/partitions/atari.c +++ b/fs/partitions/atari.c @@ -48,7 +48,7 @@ int atari_partition (struct gendisk *hd, kdev_t dev, bh = bread (dev, 0, get_ptable_blocksize(dev)); if (!bh) { - printk (" unable to read block 0 (partition table)\n"); + if (warn_no_part) printk (" unable to read block 0 (partition table)\n"); return -1; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index f7442e6eb1b0..b9e5ba70b121 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -38,6 +38,7 @@ extern void rd_load(void); extern void initrd_load(void); struct gendisk *gendisk_head; +int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ static int (*check_part[])(struct gendisk *hd, kdev_t dev, unsigned long first_sect, int first_minor) = { #ifdef CONFIG_ACORN_PARTITION @@ -82,6 +83,14 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) const char *maj = hd->major_name; int unit = (minor >> hd->minor_shift) + 'a'; + part = minor & ((1 << hd->minor_shift) - 1); + if (hd->part[minor].de) { + int pos; + + pos = devfs_generate_path (hd->part[minor].de, buf, 64); + if (pos >= 0) + return buf + pos; + } /* * IDE devices use multiple major numbers, but the drives * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}.. @@ -110,7 +119,6 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) maj = "hd"; break; } - part = minor & ((1 << hd->minor_shift) - 1); if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) { unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16; if (unit > 'z') { @@ -153,13 +161,20 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) */ void add_gd_partition(struct gendisk *hd, int minor, int start, int size) { +#ifndef CONFIG_DEVFS_FS char buf[40]; +#endif + hd->part[minor].start_sect = start; hd->part[minor].nr_sects = size; +#ifdef CONFIG_DEVFS_FS + printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); +#else if (hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); else printk(" %s", disk_name(hd, minor, buf)); +#endif } int get_hardsect_size(kdev_t dev) @@ -217,7 +232,7 @@ unsigned int get_ptable_blocksize(kdev_t dev) int get_partition_list(char * page) { struct gendisk *p; - char buf[40]; + char buf[64]; int n, len; len = sprintf(page, "major minor #blocks name\n\n"); @@ -237,9 +252,10 @@ int get_partition_list(char * page) static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor) { + devfs_handle_t de = NULL; static int first_time = 1; unsigned long first_sector; - char buf[40]; + char buf[64]; int i; if (first_time) @@ -256,12 +272,104 @@ static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor return; } - printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); + if (hd->de_arr) + de = hd->de_arr[MINOR(dev) >> hd->minor_shift]; + i = devfs_generate_path (de, buf, sizeof buf); + if (i >= 0) + printk(KERN_INFO " /dev/%s:", buf + i); + else + printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); for (i = 0; check_part[i]; i++) if (check_part[i](hd, dev, first_sector, first_part_minor)) - return; + goto setup_devfs; printk(" unknown partition table\n"); +setup_devfs: + i = first_part_minor - 1; + devfs_register_partitions (hd, i, hd->sizes ? 0 : 1); +} + +#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, 0, devfs_flags, + dev->major, minor + part, + S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + 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", 4, devfs_flags, dev->major, minor, + S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, 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 */ } /* diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 804d5451788a..c2dde3a97edf 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h @@ -8,3 +8,5 @@ void add_gd_partition(struct gendisk *hd, int minor, int start, int size); * Get the default block size for this device */ unsigned int get_ptable_blocksize(kdev_t dev); + +extern int warn_no_part; diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c index 8ab3f071acb6..904b6b0ed8a9 100644 --- a/fs/partitions/msdos.c +++ b/fs/partitions/msdos.c @@ -353,7 +353,7 @@ int msdos_partition(struct gendisk *hd, kdev_t dev, 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; } data = bh->b_data; diff --git a/fs/partitions/osf.c b/fs/partitions/osf.c index 6cd1540c0f4b..7982302a36b2 100644 --- a/fs/partitions/osf.c +++ b/fs/partitions/osf.c @@ -57,7 +57,7 @@ int osf_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, struct d_partition * partition; 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); diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c index db9c1ae391b9..8027abda95b9 100644 --- a/fs/partitions/sgi.c +++ b/fs/partitions/sgi.c @@ -44,7 +44,7 @@ int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, in struct sgi_partition *p; if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); + if (warn_no_part) printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } label = (struct sgi_disklabel *) bh->b_data; diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c index 5f31cde4bccc..f48bc5cd8c5c 100644 --- a/fs/partitions/sun.c +++ b/fs/partitions/sun.c @@ -48,7 +48,7 @@ int sun_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, in unsigned long spc; if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk(KERN_WARNING "Dev %s: unable to read partition table\n", + if (warn_no_part) printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } diff --git a/fs/partitions/ultrix.c b/fs/partitions/ultrix.c index fcfaf0d22790..a0017350d011 100644 --- a/fs/partitions/ultrix.c +++ b/fs/partitions/ultrix.c @@ -35,7 +35,7 @@ static int ultrix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_ 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; } diff --git a/fs/super.c b/fs/super.c index 4e7146fcffe6..8f9d6ddac44e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -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 @@ -1080,6 +1082,8 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto out; if (fstype->fs_flags & FS_REQUIRES_DEV) { + struct block_device_operations *bdops; + dentry = namei(dev_name); retval = PTR_ERR(dentry); if (IS_ERR(dentry)) @@ -1095,6 +1099,8 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto dput_and_out; bdev = inode->i_bdev; + bdops = devfs_get_ops ( devfs_get_handle_from_inode (inode) ); + if (bdops) bdev->bd_op = bdops; } page = 0; @@ -1123,6 +1129,9 @@ void __init mount_root(void) struct block_device *bdev = NULL; mode_t mode; int retval; + void *handle; + char path[64]; + int path_start = -1; #ifdef CONFIG_ROOT_NFS if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { @@ -1178,9 +1187,22 @@ void __init mount_root(void) } #endif + 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); + if (handle) /* Sigh: bd*() functions only paper over the cracks */ + { + unsigned major, minor; + + devfs_get_maj_min (handle, &major, &minor); + ROOT_DEV = MKDEV (major, minor); + } bdev = bdget(kdev_t_to_nr(ROOT_DEV)); if (!bdev) panic(__FUNCTION__ ": unable to allocate root device"); + bdev->bd_op = devfs_get_ops (handle); + path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5); mode = FMODE_READ; if (!(root_mountflags & MS_RDONLY)) mode |= FMODE_WRITE; @@ -1189,13 +1211,15 @@ void __init mount_root(void) root_mountflags |= MS_RDONLY; retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS); } - 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; @@ -1207,7 +1231,16 @@ void __init mount_root(void) 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) { bdput(bdev); /* sb holds a reference */ return; @@ -1343,6 +1376,18 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) 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); @@ -1351,6 +1396,7 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) 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 --git a/fs/tunnel.c b/fs/tunnel.c new file mode 100644 index 000000000000..8fee49a00c71 --- /dev/null +++ b/fs/tunnel.c @@ -0,0 +1,50 @@ +/* fs/tunnel.c: utility functions to support VFS tunnelling + + 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 + + 19991121 Richard Gooch + Created. +*/ +#include +#include +#include +#include +#include + + +/*PUBLIC_FUNCTION*/ +struct dentry *vfs_tunnel_lookup (const struct dentry *dentry, + const struct dentry *parent, + const struct dentry *covered) +/* [SUMMARY] Lookup the corresponding dentry in the mounted-over FS. + The dentry which is in the overmounting FS. + The parent of the dentry in the mounted-over FS. This may be NULL. + The dentry covered by the root dentry of the overmounting FS. + [RETURNS] A dentry on success, else NULL. +*/ +{ + struct dentry *root = dentry->d_sb->s_root; + + if (covered == root) return NULL; + if (parent) return lookup_dentry (dentry->d_name.name, parent, 0); +} /* End Function vfs_tunnel_lookup */ diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index e5ccac281a02..1a4c151cdcdd 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -419,4 +419,16 @@ unsigned long get_wchan(struct task_struct *p); #define init_task (init_task_union.task) #define init_stack (init_task_union.stack) +struct microcode { + unsigned int hdrver; + unsigned int rev; + unsigned int date; + unsigned int sig; + unsigned int cksum; + unsigned int ldrver; + unsigned int pf; + unsigned int reserved[5]; + unsigned int bits[500]; +}; + #endif /* __ASM_I386_PROCESSOR_H */ diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index 78f7d426b473..3f15c3063b5e 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -705,6 +705,7 @@ struct request_sense { }; #ifdef __KERNEL__ +#include struct cdrom_write_settings { unsigned char fpacket; /* fixed/variable packets */ @@ -718,6 +719,7 @@ struct cdrom_device_info { 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 --git a/include/linux/devfs_fs.h b/include/linux/devfs_fs.h new file mode 100644 index 000000000000..9c4cf54c2342 --- /dev/null +++ b/include/linux/devfs_fs.h @@ -0,0 +1,42 @@ +#ifndef _LINUX_DEVFS_FS_H +#define _LINUX_DEVFS_FS_H + +#include + +#define DEVFSD_PROTOCOL_REVISION_KERNEL 5 + +#define DEVFSD_IOCTL_BASE 'd' + +/* These are the various ioctls */ +#define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int) +#define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int) +#define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int) +#define DEVFSDIOC_SET_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int) + +#define DEVFSD_NOTIFY_REGISTERED 0 +#define DEVFSD_NOTIFY_UNREGISTERED 1 +#define DEVFSD_NOTIFY_ASYNC_OPEN 2 +#define DEVFSD_NOTIFY_CLOSE 3 +#define DEVFSD_NOTIFY_LOOKUP 4 +#define DEVFSD_NOTIFY_CHANGE 5 +#define DEVFSD_NOTIFY_CREATE 6 + +#define DEVFS_PATHLEN 1024 /* Never change this otherwise the + binary interface will change */ + +struct devfsd_notify_struct +{ + unsigned int type; /* DEVFSD_NOTIFY_* value */ + unsigned int mode; /* Mode of the inode or device entry */ + unsigned int major; /* Major number of device entry */ + unsigned int minor; /* Minor number of device entry */ + unsigned int uid; /* Uid of process, inode or device entry */ + unsigned int gid; /* Gid of process, inode or device entry */ + unsigned int overrun_count; /* Number of lost events */ + unsigned int namelen; /* Number of characters not including '\0' */ + /* The device name MUST come last */ + char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */ +}; + + +#endif /* _LINUX_DEVFS_FS_H */ diff --git a/include/linux/devfs_fs_kernel.h b/include/linux/devfs_fs_kernel.h new file mode 100644 index 000000000000..b3abeacd47e1 --- /dev/null +++ b/include/linux/devfs_fs_kernel.h @@ -0,0 +1,258 @@ +#ifndef _LINUX_DEVFS_FS_KERNEL_H +#define _LINUX_DEVFS_FS_KERNEL_H + +#include +#include + +#define DEVFS_SUPER_MAGIC 0x1373 + +#define IS_DEVFS_INODE(inode) (DEVFS_SUPER_MAGIC == (inode)->i_sb->s_magic) + +#define DEVFS_MINOR(inode) \ + ({unsigned int m; /* evil GCC trickery */ \ + ((inode)->i_sb && \ + ((inode)->i_sb->s_magic==DEVFS_SUPER_MAGIC) && \ + (devfs_get_maj_min(devfs_get_handle_from_inode((inode)),NULL,&m)==0) \ + ) ? m : MINOR((inode)->r_dev); }) + + +#define DEVFS_FL_NONE 0x000 /* This helps making code more readable */ +#define DEVFS_FL_AUTO_OWNER 0x001 /* When a closed inode is opened the + ownerships are set to the opening + process and the protection is set to + that given in <>. When the inode + 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_AOPEN_NOTIFY 0x010 /* 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_DEFAULT DEVFS_FL_NONE + + +#define DEVFS_SPECIAL_CHR 0 +#define DEVFS_SPECIAL_BLK 1 + +typedef struct devfs_entry * devfs_handle_t; + + +#ifdef CONFIG_BLK_DEV_INITRD +# define ROOT_DEVICE_NAME ((real_root_dev ==ROOT_DEV) ? root_device_name:NULL) +#else +# define ROOT_DEVICE_NAME root_device_name +#endif + + +#ifdef CONFIG_DEVFS_FS +extern devfs_handle_t devfs_register (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int flags, + unsigned int major, unsigned int minor, + umode_t mode, uid_t uid, gid_t gid, + void *ops, void *info); +extern void devfs_unregister (devfs_handle_t de); +extern 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); +extern devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, + unsigned int namelen, void *info); +extern 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); +extern int devfs_get_flags (devfs_handle_t de, unsigned int *flags); +extern int devfs_set_flags (devfs_handle_t de, unsigned int flags); +extern int devfs_get_maj_min (devfs_handle_t de, + unsigned int *major, unsigned int *minor); +extern devfs_handle_t devfs_get_handle_from_inode (struct inode *inode); +extern int devfs_generate_path (devfs_handle_t de, char *path, int buflen); +extern void *devfs_get_ops (devfs_handle_t de); +extern int devfs_set_file_size (devfs_handle_t de, unsigned long size); +extern void *devfs_get_info (devfs_handle_t de); +extern int devfs_set_info (devfs_handle_t de, void *info); +extern devfs_handle_t devfs_get_parent (devfs_handle_t de); +extern devfs_handle_t devfs_get_first_child (devfs_handle_t de); +extern devfs_handle_t devfs_get_next_sibling (devfs_handle_t de); +extern void devfs_auto_unregister (devfs_handle_t master,devfs_handle_t slave); +extern devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master); +extern const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen); +extern int devfs_register_chrdev (unsigned int major, const char *name, + struct file_operations *fops); +extern int devfs_register_blkdev (unsigned int major, const char *name, + struct block_device_operations *bdops); +extern int devfs_unregister_chrdev (unsigned int major, const char *name); +extern int devfs_unregister_blkdev (unsigned int major, const char *name); + +extern void devfs_register_tape (devfs_handle_t de); +extern void devfs_register_series (devfs_handle_t dir, const char *format, + unsigned int num_entries, + unsigned int flags, unsigned int major, + unsigned int minor_start, + umode_t mode, uid_t uid, gid_t gid, + void *ops, void *info); + +extern int init_devfs_fs (void); +extern void mount_devfs_fs (void); +extern void devfs_make_root (const char *name); +#else /* CONFIG_DEVFS_FS */ +static inline devfs_handle_t devfs_register (devfs_handle_t dir, + const char *name, + unsigned int namelen, + unsigned int flags, + unsigned int major, + unsigned int minor, + umode_t mode, + uid_t uid, gid_t gid, + void *ops, void *info) +{ + return NULL; +} +static inline void devfs_unregister (devfs_handle_t de) +{ + return; +} +static inline 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) +{ + return 0; +} +static inline devfs_handle_t devfs_mk_dir (devfs_handle_t dir, + const char *name, + unsigned int namelen, void *info) +{ + return NULL; +} +static inline 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) +{ + return NULL; +} +static inline int devfs_get_flags (devfs_handle_t de, unsigned int *flags) +{ + return 0; +} +static inline int devfs_set_flags (devfs_handle_t de, unsigned int flags) +{ + return 0; +} +static inline int devfs_get_maj_min (devfs_handle_t de, + unsigned int *major, unsigned int *minor) +{ + return 0; +} +static inline devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) +{ + return NULL; +} +static inline int devfs_generate_path (devfs_handle_t de, char *path, + int buflen) +{ + return -ENOSYS; +} +static inline void *devfs_get_ops (devfs_handle_t de) +{ + return NULL; +} +static inline int devfs_set_file_size (devfs_handle_t de, unsigned long size) +{ + return -ENOSYS; +} +static inline void *devfs_get_info (devfs_handle_t de, unsigned long size) +{ + return NULL; +} +static inline int devfs_set_info (devfs_handle_t de, void *info) +{ + return 0; +} +static inline devfs_handle_t devfs_get_parent (devfs_handle_t de) +{ + return NULL; +} +static inline devfs_handle_t devfs_get_first_child (devfs_handle_t de) +{ + return NULL; +} +static inline devfs_handle_t devfs_get_next_sibling (devfs_handle_t de) +{ + return NULL; +} +static inline void devfs_auto_unregister (devfs_handle_t master, + devfs_handle_t slave) +{ + return; +} +static inline devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master) +{ + return NULL; +} +static inline const char *devfs_get_name (devfs_handle_t de, + unsigned int *namelen) +{ + return NULL; +} +static inline int devfs_register_chrdev (unsigned int major, const char *name, + struct file_operations *fops) +{ + return register_chrdev (major, name, fops); +} +static inline int devfs_register_blkdev (unsigned int major, const char *name, + struct block_device_operations *bdops) +{ + return register_blkdev (major, name, bdops); +} +static inline int devfs_unregister_chrdev (unsigned int major,const char *name) +{ + return unregister_chrdev (major, name); +} +static inline int devfs_unregister_blkdev (unsigned int major,const char *name) +{ + return unregister_blkdev (major, name); +} + +static inline void devfs_register_tape (devfs_handle_t de) +{ + return; +} + +static inline void devfs_register_series (devfs_handle_t dir, + const char *format, + unsigned int num_entries, + unsigned int flags, + unsigned int major, + unsigned int minor_start, + umode_t mode, uid_t uid, gid_t gid, + void *ops, void *info) +{ + return; +} + +static inline int init_devfs_fs (void) +{ + return 0; +} +static inline void mount_devfs_fs (void) +{ + return; +} +static inline void devfs_make_root (const char *name) +{ + return; +} +#endif /* CONFIG_DEVFS_FS */ + +#endif /* _LINUX_DEVFS_FS_KERNEL_H */ diff --git a/include/linux/fb.h b/include/linux/fb.h index 56a0f03b6389..2f80133182b0 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -221,6 +221,7 @@ extern int GET_FB_IDX(kdev_t rdev); #include #include +#include struct fb_info; @@ -277,6 +278,8 @@ struct fb_info { 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 */ @@ -371,7 +374,7 @@ extern void fbgen_blank(int blank, struct fb_info *info); /* drivers/video/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 num_registered_fb; extern struct fb_info *registered_fb[FB_MAX]; diff --git a/include/linux/fs.h b/include/linux/fs.h index 4e607707c8b9..dc35928e7aa7 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -787,6 +787,8 @@ extern int blkdev_get(struct block_device *, mode_t, unsigned, int); extern int blkdev_put(struct block_device *, int); /* fs/devices.c */ +extern const struct block_device_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, const char *); extern int chrdev_open(struct inode *, struct file *); @@ -1016,6 +1018,8 @@ extern void put_super(kdev_t); unsigned long generate_cluster(kdev_t, int b[], int); unsigned long generate_cluster_swab32(kdev_t, int b[], int); extern kdev_t ROOT_DEV; +extern char root_device_name[]; + extern void show_buffers(void); extern void mount_root(void); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 9e228d0d70e2..e7bf84240460 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -29,7 +29,7 @@ #define EZD_PARTITION 0x55 /* EZ-DRIVE */ #define DM6_AUX1PARTITION 0x51 /* no DDO: use xlated geom */ #define DM6_AUX3PARTITION 0x53 /* no DDO: use xlated geom */ - + struct partition { unsigned char boot_ind; /* 0x80 - active */ unsigned char head; /* starting head */ @@ -43,12 +43,18 @@ struct partition { unsigned int nr_sects; /* nr of sectors in partition */ } __attribute__((packed)); +#ifdef __KERNEL__ +# include + struct hd_struct { long start_sect; long nr_sects; int type; /* currently RAID or normal */ + devfs_handle_t de; /* primary (master) devfs entry */ }; +#define GENHD_FL_REMOVABLE 1 + struct gendisk { int major; /* major number of driver */ const char *major_name; /* name of major driver */ @@ -62,7 +68,12 @@ struct gendisk { void *real_devices; /* internal use */ struct gendisk *next; + struct block_device_operations *fops; + + devfs_handle_t *de_arr; /* one per physical disc */ + char *flags; /* one per physical disc */ }; +#endif /* __KERNEL__ */ #ifdef CONFIG_SOLARIS_X86_PARTITION @@ -217,7 +228,11 @@ extern struct gendisk *gendisk_head; /* linked list of disks */ char *disk_name (struct gendisk *hd, int minor, char *buf); +extern void devfs_register_partitions (struct gendisk *dev, int minor, + int unregister); + int get_hardsect_size(kdev_t dev); + #endif #endif diff --git a/include/linux/ide.h b/include/linux/ide.h index 49ce1c179540..6d95ad2df605 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -13,6 +13,7 @@ #include #include #include +#include #include /* @@ -142,7 +143,7 @@ typedef unsigned char byte; /* used everywhere */ /* * 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< #endif +#ifdef CONFIG_DEVFS_FS +# include +#endif + #include #define ISDN_DRVIOCTL_MASK 0x7f /* Mask for Device-ioctl */ @@ -976,6 +980,15 @@ typedef struct isdn_devt { struct semaphore sem; /* serialize list access*/ isdn_module *modules; 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 --git a/include/linux/joystick.h b/include/linux/joystick.h index b5b39d0f59e3..8cd5cbccb6c8 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -129,6 +129,7 @@ struct JS_DATA_SAVE_TYPE { #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." @@ -217,6 +218,7 @@ struct js_dev { int num_axes; int num_buttons; char *name; + devfs_handle_t devfs_handle; }; struct js_list { diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 9e2a1e4ffe22..9cf4b9a148a6 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -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 @@ struct miscdevice const char *name; struct file_operations *fops; struct miscdevice * next, * prev; + devfs_handle_t devfs_handle; }; extern int misc_register(struct miscdevice * misc); diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 67456e4d1d04..5b3c3860c437 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -75,7 +75,7 @@ int nfsd_svc(unsigned short port, int nrservs); /* nfsd/vfs.c */ int fh_lock_parent(struct svc_fh *, struct dentry *); -void nfsd_racache_init(void); +int nfsd_racache_init(int); void nfsd_racache_shutdown(void); int nfsd_lookup(struct svc_rqst *, struct svc_fh *, const char *, int, struct svc_fh *); @@ -182,11 +182,6 @@ extern u32 nfs_ok, */ extern struct timeval nfssvc_boot; -/* - * The number of nfsd threads. - */ -extern int nfsd_nservers; - #endif /* __KERNEL__ */ #endif /* LINUX_NFSD_NFSD_H */ diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index d0056335799d..9e457818f06e 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -329,7 +329,7 @@ typedef struct mdk_thread_s { #define THREAD_WAKEUP 0 -#define MAX_DISKNAME_LEN 32 +#define MAX_DISKNAME_LEN 64 typedef struct dev_name_s { struct md_list_head list; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 46ace283a207..325e7f64df7f 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -197,10 +197,15 @@ struct tty_driver { * 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 --git a/include/linux/videodev.h b/include/linux/videodev.h index e1ba268a72a7..5919a894e685 100644 --- a/include/linux/videodev.h +++ b/include/linux/videodev.h @@ -9,6 +9,7 @@ #if LINUX_VERSION_CODE >= 0x020100 #include #endif +#include struct video_device { @@ -30,6 +31,7 @@ struct video_device 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 --git a/init/main.c b/init/main.c index 9056cc1d0fe5..98484283fc47 100644 --- a/init/main.c +++ b/init/main.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -122,6 +123,8 @@ kdev_t real_root_dev; 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, }; @@ -249,6 +252,7 @@ static struct dev_name_struct { 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; @@ -267,7 +271,18 @@ kdev_t __init name_to_kdev_t(char *line) static int __init root_dev_setup(char *line) { + 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; + } return 1; } @@ -677,6 +692,8 @@ static void __init do_basic_setup(void) /* Mount the root filesystem.. */ mount_root(); + mount_devfs_fs (); + #ifdef CONFIG_BLK_DEV_INITRD root_mountflags = real_root_mountflags; if (mount_initrd && ROOT_DEV != real_root_dev diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 614fd3a58ba2..88f5fd63f111 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -261,6 +261,7 @@ EXPORT_SYMBOL(is_read_only); EXPORT_SYMBOL(set_device_ro); EXPORT_SYMBOL(bmap); EXPORT_SYMBOL(sync_dev); +EXPORT_SYMBOL(devfs_register_partitions); EXPORT_SYMBOL(blkdev_open); EXPORT_SYMBOL(blkdev_get); EXPORT_SYMBOL(blkdev_put); diff --git a/mm/swapfile.c b/mm/swapfile.c index dc647f2b0520..e8a2a0b2fc72 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -634,11 +634,15 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) if (S_ISBLK(swap_dentry->d_inode->i_mode)) { kdev_t dev = swap_dentry->d_inode->i_rdev; + struct block_device_operations *bdops; p->swap_device = dev; set_blocksize(dev, PAGE_SIZE); bdev = swap_dentry->d_inode->i_bdev; + bdops = devfs_get_ops ( devfs_get_handle_from_inode + (swap_dentry->d_inode) ); + if (bdops) bdev->bd_op = bdops; error = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_SWAP); if (error) diff --git a/net/netlink/netlink_dev.c b/net/netlink/netlink_dev.c index 31b0bd8902ea..d63e1f678fde 100644 --- a/net/netlink/netlink_dev.c +++ b/net/netlink/netlink_dev.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -175,12 +176,35 @@ static struct file_operations netlink_fops = { release: 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, 0, DEVFS_FL_DEFAULT, + NETLINK_MAJOR, minor, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &netlink_fops, NULL); +} + int __init init_netlink(void) { - if (register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) { + 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", 7, 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); + devfs_register_series (devfs_handle, "tap%u", 16, DEVFS_FL_DEFAULT, + NETLINK_MAJOR, 16, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &netlink_fops, NULL); return 0; } @@ -194,7 +218,8 @@ int init_module(void) void cleanup_module(void) { - unregister_chrdev(NET_MAJOR,"netlink"); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(NETLINK_MAJOR, "netlink"); } #endif -- 2.39.5