v2.5.0.4 -> v2.5.0.5
authorLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 07:58:17 +0000 (23:58 -0800)
committerLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 07:58:17 +0000 (23:58 -0800)
- Patrick Mochel: driver model infrastructure, part 1
- Jens Axboe: more bio fixes, cleanups
- Andrew Morton: release locking fixes
- Al Viro: superblock/mount handling
- Kai Germaschewski: AVM Fritz!Card ISDN driver
- Christoph Hellwig: make cramfs SMP-safe.

87 files changed:
Documentation/Changes
Documentation/Configure.help
Documentation/pm.txt
MAINTAINERS
Makefile
README
arch/i386/defconfig
arch/i386/kernel/apm.c
arch/i386/kernel/dmi_scan.c
arch/i386/kernel/semaphore.c
drivers/block/floppy.c
drivers/block/loop.c
drivers/block/paride/pcd.c
drivers/block/paride/pd.c
drivers/block/paride/pf.c
drivers/block/rd.c
drivers/char/agp/agpgart_be.c
drivers/char/ftape/zftape/zftape-init.c
drivers/char/pc_keyb.c
drivers/char/tty_io.c
drivers/ide/ide-floppy.c
drivers/ide/ide-pci.c
drivers/ide/piix.c
drivers/isdn/Config.in
drivers/isdn/divert/divert_procfs.c
drivers/isdn/hisax/Makefile
drivers/isdn/hisax/config.c
drivers/isdn/hisax/hisax_fcpcipnp.c [new file with mode: 0644]
drivers/isdn/hisax/hisax_fcpcipnp.h [new file with mode: 0644]
drivers/isdn/hisax/hisax_isac.c [new file with mode: 0644]
drivers/isdn/hisax/hisax_isac.h [new file with mode: 0644]
drivers/md/linear.c
drivers/md/raid0.c
drivers/net/irda/vlsi_ir.c
drivers/scsi/ide-scsi.c
drivers/scsi/qlogicfc.c
drivers/scsi/scsi.c
drivers/scsi/scsi_lib.c
drivers/sgi/char/shmiq.c
fs/Makefile
fs/bio.c
fs/block_dev.c
fs/buffer.c
fs/cramfs/inode.c
fs/dcache.c
fs/devfs/base.c
fs/driverfs/Makefile [new file with mode: 0644]
fs/driverfs/inode.c [new file with mode: 0644]
fs/fat/file.c
fs/inode.c
fs/minix/itree_common.c
fs/namei.c
fs/namespace.c
fs/reiserfs/journal.c
fs/super.c
include/asm-alpha/page.h
include/linux/bio.h
include/linux/blkdev.h
include/linux/cache.h
include/linux/device.h [new file with mode: 0644]
include/linux/driverfs_fs.h [new file with mode: 0644]
include/linux/fs.h
include/linux/highmem.h
include/linux/msdos_fs.h
include/linux/pci_ids.h
include/net/irda/irlmp.h
include/net/irda/irttp.h
init/main.c
kernel/Makefile
kernel/device.c [new file with mode: 0644]
kernel/exit.c
kernel/fork.c
kernel/softirq.c
mm/filemap.c
mm/highmem.c
mm/swapfile.c
net/irda/Config.in
net/irda/af_irda.c
net/irda/iriap.c
net/irda/irlap.c
net/irda/irlap_event.c
net/irda/irlmp.c
net/irda/irlmp_event.c
net/irda/irnet/irnet.h
net/irda/irnet/irnet_irda.c
net/irda/irsysctl.c
net/irda/qos.c

index 6dc50f5..8551fc7 100644 (file)
@@ -2,13 +2,13 @@ Intro
 =====
 
 This document is designed to provide a list of the minimum levels of
-software necessary to run the 2.4 kernels, as well as provide brief
+software necessary to run the 2.5 kernels, as well as provide brief
 instructions regarding any other "Gotchas" users may encounter when
-trying life on the Bleeding Edge.  If upgrading from a pre-2.2.x
-kernel, please consult the Changes file included with 2.2.x kernels for
+trying life on the Bleeding Edge.  If upgrading from a pre-2.4.x
+kernel, please consult the Changes file included with 2.4.x kernels for
 additional information; most of that information will not be repeated
 here.  Basically, this document assumes that your system is already
-functional and running at least 2.2.x kernels.
+functional and running at least 2.4.x kernels.
 
 This document is originally based on my "Changes" file for 2.0.x kernels
 and therefore owes credit to the same people as that file (Jared Mauch,
@@ -31,7 +31,7 @@ al espa
 Eine deutsche Version dieser Datei finden Sie unter
 <http://www.stefan-winter.de/Changes-2.4.0.txt>.
 
-Last updated: May 9, 2001
+Last updated: November 29, 2001
 
 Chris Ricker (kaboom@gatech.edu or chris.ricker@genetics.utah.edu).
 
@@ -43,14 +43,14 @@ encountered a bug!  If you're unsure what version you're currently
 running, the suggested command should tell you.
 
 Again, keep in mind that this list assumes you are already
-functionally running a Linux 2.2 kernel.  Also, not all tools are
+functionally running a Linux 2.4 kernel.  Also, not all tools are
 necessary on all systems; obviously, if you don't have any PCMCIA (PC
 Card) hardware, for example, you probably needn't concern yourself
 with pcmcia-cs.
 
 o  Gnu C                  2.95.3                  # gcc --version
 o  Gnu make               3.77                    # make --version
-o  binutils               2.9.1.0.25              # ld -v
+o  binutils               2.9.5.0.24              # ld -v
 o  util-linux             2.10o                   # fdformat --version
 o  modutils               2.4.2                   # insmod -V
 o  e2fsprogs              1.19                    # tune2fs
@@ -70,9 +70,9 @@ computer. The next paragraph applies to users of x86 CPUs, but not
 necessarily to users of other CPUs. Users of other CPUs should obtain
 information about their gcc version requirements from another source.
 
-The recommended compiler for the kernel is gcc 2.95.3 or .4, and it
+The recommended compiler for the kernel is gcc 2.95.x (x >= 3), and it
 should be used when you need absolute stability. You may use gcc 3.0.x
-instead if you wish, although it may cause problems. Later  versions of gcc 
+instead if you wish, although it may cause problems. Later versions of gcc 
 have not received much testing for Linux kernel compilation, and there are 
 almost certainly bugs (mainly, but not exclusively, in the kernel) that
 will need to be fixed in order to use these compilers. In any case, using
@@ -106,12 +106,6 @@ assembling the 16-bit boot code, removing the need for as86 to compile
 your kernel.  This change does, however, mean that you need a recent
 release of binutils.
 
-If you can, upgrade to the latest 2.9.5 or 2.10 binutils release.  Older
-releases such as 2.8, 2.8.xx, and the FSF's 2.9.1 should be avoided if
-at all possible.  The later releases of 2.9.1.0.x (anything where x >= 22)
-can and do compile the kernel properly, but there are many benefits in
-upgrading to 2.9.5 or 2.10 if you're up to it.
-
 System utils
 ============
 
@@ -276,7 +270,7 @@ o  <ftp://sourceware.cygnus.com/pub/gcc/releases/egcs-1.1.2/egcs-1.1.2.tar.bz2>
 
 gcc 2.95.3
 ----------
-o  <ftp://ftp.gnu.org/pub/gnu/gcc/gcc-2.95.3.tar.gz>
+o  <ftp://ftp.gnu.org/gnu/gcc/gcc-2.95.3.tar.gz>
 
 Gnu Make
 ********
@@ -287,21 +281,14 @@ o  <ftp://ftp.gnu.org/gnu/make/make-3.77.tar.gz>
 
 Binutils
 ********
-
-2.9.1 series
-------------
-o  <ftp://ftp.valinux.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz>
-
-2.9.5 and 2.10 series
----------------------
-o  <ftp://ftp.valinux.com/pub/support/hjl/binutils/>
+o  <ftp://ftp.kernel.org/pub/linux/devel/binutils/>
 
 System utilities
 ****************
 
 Util-linux
 ----------
-o  <ftp://ftp.kernel.org/pub/linux/utils/util-linux/util-linux-2.10o.tar.gz>
+o  <ftp://ftp.kernel.org/pub/linux/utils/util-linux/>
 
 Ksymoops
 --------
index d6d9fda..bd149af 100644 (file)
@@ -17004,8 +17004,8 @@ CONFIG_ACPI
   can be downloaded from:
   <http://developer.intel.com/technology/iapc/acpi/downloads.htm>.
 
-  The ACPI mailing list may also be of interest:
-  <http://phobos.fs.tum.de/acpi/index.html>.
+  The ACPI Sourceforge project may also be of interest:
+  <http://sf.net/projects/acpi/>
 
 Enable ACPI 2.0 with errata 1.3
 CONFIG_ACPI20
@@ -22281,11 +22281,6 @@ CONFIG_IRDA_ULTRA
   no management frames, simple fixed header).
   Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1);
 
-IrDA protocol options
-CONFIG_IRDA_OPTIONS
-  Say Y here if you want to configure any of the following IrDA
-  options.
-
 IrDA cache last LSAP
 CONFIG_IRDA_CACHE_LAST_LSAP
   Say Y here if you want IrLMP to cache the last LSAP used.  This
@@ -22297,47 +22292,35 @@ CONFIG_IRDA_CACHE_LAST_LSAP
 IrDA Fast RRs
 CONFIG_IRDA_FAST_RR
   Say Y here is you want IrLAP to send fast RR (Receive Ready) frames
-  when acting as a primary station. This will make IrLAP send out a RR
-  frame immediately when receiving a frame if its own transmit queue
-  is currently empty. This will give a lot of speed improvement when
-  receiving much data since the secondary station will not have to
-  wait the max. turn around time before it is allowed to transmit the
-  next time. If the transmit queue of the secondary is also empty the
-  primary will back off waiting longer for sending out the RR frame
-  until the timeout reaches the normal value. Enabling this option
-  will make the IR-diode burn more power and thus reduce your battery
-  life.
+  when acting as a primary station.
+  Disabling this option will make latency over IrDA very bad. Enabling
+  this option will make the IrDA stack send more packet than strictly
+  necessary, thus reduce your battery life (but not that much).
+
+  Fast RR will make IrLAP send out a RR frame immediately when
+  receiving a frame if its own transmit queue is currently empty. This
+  will give a lot of speed improvement when receiving much data since
+  the secondary station will not have to wait the max. turn around
+  time (usually 500ms) before it is allowed to transmit the next time.
+  If the transmit queue of the secondary is also empty, the primary will
+  start backing-off before sending another RR frame, waiting longer
+  each time until the back-off reaches the max. turn around time.
+  This back-off increase in controlled via
+  /proc/sys/net/irda/fast_poll_increase
 
-  If unsure, say N.
+  If unsure, say Y.
 
 IrDA debugging information
 CONFIG_IRDA_DEBUG
   Say Y here if you want the IrDA subsystem to write debug information
   to your syslog. You can change the debug level in
   /proc/sys/net/irda/debug .
+  When this option is enabled, the IrDA also perform many extra internal
+  verifications which will usually prevent the kernel to crash in case of
+  bugs.
 
   If unsure, say Y (since it makes it easier to find the bugs).
 
-IrLAP compression support
-CONFIG_IRDA_COMPRESSION
-  Compression is _not_ part of the IrDA(tm) protocol specification,
-  but it's working great! Linux is the first to try out compression
-  support at the IrLAP layer. This means that you will only benefit
-  from compression if you are running a Linux <-> Linux configuration.
-
-  If you say Y here, you also need to say Y or M to a compression
-  protocol below.
-
-IrLAP Deflate compression
-CONFIG_IRDA_DEFLATE
-  Say Y here if you want to build support for the Deflate compression
-  protocol. The deflate compression (GZIP) is exactly
-  the same as the one used by the PPP protocol.
-
-  If you want to compile this compression support as a module, say M
-  here and read <file:Documentation/modules.txt>.  The module will be
-  called irda_deflate.o.
-
 IrLAN protocol
 CONFIG_IRLAN
   Say Y here if you want to build support for the IrLAN protocol.  If
index 3913cd4..483d2a5 100644 (file)
@@ -34,7 +34,7 @@ Go ahead and start both.  If ACPI or APM is not available on your
 system the associated daemon will exit gracefully.
 
   apmd:   http://worldvisions.ca/~apenwarr/apmd/
-  acpid:  http://phobos.fs.tum.de/acpi/
+  acpid:  http://acpid.sf.net/
 
 Driver Interface
 ----------------
@@ -260,7 +260,7 @@ proceed in the opposite direction.
 Q: Who do I contact for additional information about
    enabling power management for my specific driver/device?
 
-ACPI4Linux mailing list: acpi@phobos.fs.tum.de
+ACPI Development mailing list: acpi-devel@lists.sourceforge.net
 
 System Interface
 ----------------
index 8b92617..5838519 100644 (file)
@@ -148,8 +148,8 @@ S:  Supported
 ACPI
 P:     Andy Grover
 M:     andrew.grover@intel.com
-L:     acpi@phobos.fs.tum.de
-W:     http://phobos.fs.tum.de/acpi/index.html
+L:     acpi-devel@lists.sourceforge.net
+W:     http://sf.net/projects/acpi/
 S:     Maintained
 
 AD1816 SOUND DRIVER
index 7babe19..5e0990b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 1
-EXTRAVERSION =-pre4
+EXTRAVERSION =-pre5
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff --git a/README b/README
index aabb049..1d42995 100644 (file)
--- a/README
+++ b/README
@@ -153,13 +153,12 @@ CONFIGURING the kernel:
 
 COMPILING the kernel:
 
- - Make sure you have gcc-2.91.66 (egcs-1.1.2) available.  gcc 2.95.2 may
+ - Make sure you have gcc 2.95.3 available.  gcc 2.91.66 (egcs-1.1.2) may
    also work but is not as safe, and *gcc 2.7.2.3 is no longer supported*.
    Also remember to upgrade your binutils package (for as/ld/nm and company)
    if necessary. For more information, refer to ./Documentation/Changes.
 
-   Please note that you can still run a.out user programs with this
-   kernel.
+   Please note that you can still run a.out user programs with this kernel.
 
  - Do a "make bzImage" to create a compressed kernel image.  If you want
    to make a boot disk (without root filesystem or LILO), insert a floppy
index c73c7bb..8b7fe15 100644 (file)
@@ -496,6 +496,9 @@ CONFIG_PCMCIA_RAYCS=y
 # IrDA (infrared) support
 #
 # CONFIG_IRDA is not set
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRDA_DEBUG=y
 
 #
 # ISDN subsystem
index 72dd9ae..02a1a5d 100644 (file)
@@ -387,7 +387,7 @@ static int                  broken_psr;
 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
 static struct apm_user *       user_list;
-static spinlock_t              user_list_lock;
+static spinlock_t              user_list_lock = SPIN_LOCK_UNLOCKED;
 
 static char                    driver_version[] = "1.15";      /* no spaces */
 
@@ -1054,9 +1054,9 @@ static void queue_event(apm_event_t event, struct apm_user *sender)
 {
        struct apm_user *       as;
 
-       spin_lock( &user_list_lock );
+       spin_lock(&user_list_lock);
        if (user_list == NULL)
-               return;
+               goto out;
        for (as = user_list; as != NULL; as = as->next) {
                if ((as == sender) || (!as->reader))
                        continue;
@@ -1085,8 +1085,9 @@ static void queue_event(apm_event_t event, struct apm_user *sender)
                        break;
                }
        }
-       spin_unlock( &user_list_lock );
        wake_up_interruptible(&apm_waitqueue);
+out:
+       spin_unlock(&user_list_lock);
 }
 
 static void set_time(void)
@@ -1182,12 +1183,12 @@ static int suspend(void)
        send_event(APM_NORMAL_RESUME);
        sti();
        queue_event(APM_NORMAL_RESUME, NULL);
-       spin_lock( &user_list_lock );
+       spin_lock(&user_list_lock);
        for (as = user_list; as != NULL; as = as->next) {
                as->suspend_wait = 0;
                as->suspend_result = ((err == APM_SUCCESS) ? 0 : -EIO);
        }
-       spin_unlock( &user_list_lock );
+       spin_unlock(&user_list_lock);
        ignore_normal_resume = 1;
        wake_up_interruptible(&apm_suspend_waitqueue);
        return err;
@@ -1534,7 +1535,7 @@ static int do_release(struct inode * inode, struct file * filp)
                if (suspends_pending <= 0)
                        (void) suspend();
        }
-       spin_lock( &user_list_lock );
+       spin_lock(&user_list_lock);
        if (user_list == as)
                user_list = as->next;
        else {
@@ -1549,7 +1550,7 @@ static int do_release(struct inode * inode, struct file * filp)
                else
                        as1->next = as->next;
        }
-       spin_unlock( &user_list_lock );
+       spin_unlock(&user_list_lock);
        kfree(as);
        return 0;
 }
@@ -1578,10 +1579,10 @@ static int do_open(struct inode * inode, struct file * filp)
        as->suser = capable(CAP_SYS_ADMIN);
        as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
        as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
-       spin_lock( &user_list_lock );
+       spin_lock(&user_list_lock);
        as->next = user_list;
        user_list = as;
-       spin_unlock( &user_list_lock );
+       spin_unlock(&user_list_lock);
        filp->private_data = as;
        return 0;
 }
index 227aa91..2431c35 100644 (file)
@@ -191,6 +191,7 @@ struct dmi_blacklist
  *     corruption problems
  */ 
  
+#if 0
 static __init int disable_ide_dma(struct dmi_blacklist *d)
 {
 #ifdef CONFIG_BLK_DEV_IDE
@@ -203,6 +204,7 @@ static __init int disable_ide_dma(struct dmi_blacklist *d)
 #endif 
        return 0;
 }
+#endif
 
 /* 
  * Reboot options and system auto-detection code provided by
index c2ff936..09c0852 100644 (file)
@@ -238,31 +238,30 @@ asm(
  */
 #if defined(CONFIG_SMP)
 asm(
-"
-.align 4
-.globl __write_lock_failed
-__write_lock_failed:
-       " LOCK "addl    $" RW_LOCK_BIAS_STR ",(%eax)
-1:     rep; nop
-       cmpl    $" RW_LOCK_BIAS_STR ",(%eax)
-       jne     1b
-
-       " LOCK "subl    $" RW_LOCK_BIAS_STR ",(%eax)
-       jnz     __write_lock_failed
-       ret
-
-
-.align 4
-.globl __read_lock_failed
-__read_lock_failed:
-       lock ; incl     (%eax)
-1:     rep; nop
-       cmpl    $1,(%eax)
-       js      1b
+".text\n"
+".align        4\n"
+".globl        __write_lock_failed\n"
+"__write_lock_failed:\n\t"
+       LOCK "addl      $" RW_LOCK_BIAS_STR ",(%eax)\n"
+"1:    rep; nop\n\t"
+       "cmpl   $" RW_LOCK_BIAS_STR ",(%eax)\n\t"
+       "jne    1b\n\t"
+       LOCK "subl      $" RW_LOCK_BIAS_STR ",(%eax)\n\t"
+       "jnz    __write_lock_failed\n\t"
+       "ret"
+);
 
-       lock ; decl     (%eax)
-       js      __read_lock_failed
-       ret
-"
+asm(
+".text\n"
+".align        4\n"
+".globl        __read_lock_failed\n"
+"__read_lock_failed:\n\t"
+       LOCK "incl      (%eax)\n"
+"1:    rep; nop\n\t"
+       "cmpl   $1,(%eax)\n\t"
+       "js     1b\n\t"
+       LOCK "decl      (%eax)\n\t"
+       "js     __read_lock_failed\n\t"
+       "ret"
 );
 #endif
index 31e22f2..978d5ac 100644 (file)
@@ -2542,7 +2542,7 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
                        break;
                }
 #endif
-               size = bio->bi_size;;
+               size = bio->bi_size;
                buffer = bio_data(bio);
        }
 #ifdef FLOPPY_SANITY_CHECK
index 0f17cbb..ec601cc 100644 (file)
@@ -315,10 +315,13 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio)
 
        pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset;
 
-       if (bio_rw(bio) == WRITE)
-               ret = lo_send(lo, bio, loop_get_bs(lo), pos);
-       else
-               ret = lo_receive(lo, bio, loop_get_bs(lo), pos);
+       do {
+               if (bio_rw(bio) == WRITE)
+                       ret = lo_send(lo, bio, loop_get_bs(lo), pos);
+               else
+                       ret = lo_receive(lo, bio, loop_get_bs(lo), pos);
+
+       } while (++bio->bi_idx < bio->bi_vcnt);
 
        return ret;
 }
@@ -489,6 +492,24 @@ inactive:
        goto out;
 }
 
+static int do_bio_blockbacked(struct loop_device *lo, struct bio *bio,
+                             struct bio *rbh)
+{
+       unsigned long IV = loop_get_iv(lo, rbh->bi_sector);
+       struct bio_vec *to;
+       char *vto, *vfrom;
+       int ret = 0, i;
+
+       bio_for_each_segment(to, bio, i) {
+               vfrom = page_address(rbh->bi_io_vec[i].bv_page) + rbh->bi_io_vec[i].bv_offset;
+               vto = page_address(to->bv_page) + to->bv_offset;
+               ret |= lo_do_transfer(lo, bio_data_dir(bio), vto, vfrom,
+                                       to->bv_len, IV);
+       }
+
+       return ret;
+}
+
 static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio)
 {
        int ret;
@@ -501,10 +522,8 @@ static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio)
                bio_endio(bio, !ret, bio_sectors(bio));
        } else {
                struct bio *rbh = bio->bi_private;
-               unsigned long IV = loop_get_iv(lo, rbh->bi_sector);
 
-               ret = lo_do_transfer(lo, READ, bio_data(bio), bio_data(rbh),
-                                    bio->bi_size, IV);
+               ret = do_bio_blockbacked(lo, bio, rbh);
 
                bio_endio(rbh, !ret, bio_sectors(bio));
                loop_put_buffer(bio);
index e3048d0..db034ed 100644 (file)
@@ -821,11 +821,11 @@ static void pcd_start( void )
 
        if (pcd_command(unit,rd_cmd,2048,"read block")) {
                pcd_bufblk = -1; 
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                pcd_busy = 0;
                end_request(0);
                do_pcd_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                return;
        }
 
@@ -845,11 +845,11 @@ static void do_pcd_read( void )
        pcd_retries = 0;
        pcd_transfer();
        if (!pcd_count) {
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                end_request(1);
                pcd_busy = 0;
                do_pcd_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                return;
        }
 
@@ -868,19 +868,19 @@ static void do_pcd_read_drq( void )
                        pi_do_claimed(PI,pcd_start);
                         return;
                         }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                pcd_busy = 0;
                pcd_bufblk = -1;
                end_request(0);
                do_pcd_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                return;
        }
 
        do_pcd_read();
-       spin_lock_irqsave(&io_request_lock,saved_flags);
+       spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
        do_pcd_request(NULL);
-       spin_unlock_irqrestore(&io_request_lock,saved_flags); 
+       spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags); 
 }
 
 /* the audio_ioctl stuff is adapted from sr_ioctl.c */
index 1d34d71..9efc4f8 100644 (file)
@@ -838,8 +838,7 @@ static int pd_ready( void )
 }
 
 static void do_pd_request (request_queue_t * q)
-
-{       struct buffer_head * bh;
+{
        int     unit;
 
         if (pd_busy) return;
@@ -853,8 +852,6 @@ repeat:
         pd_run = CURRENT->nr_sectors;
         pd_count = CURRENT->current_nr_sectors;
 
-       bh = CURRENT->bh;
-
         if ((pd_dev >= PD_DEVS) || 
            ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) {
                 end_request(0);
@@ -878,9 +875,9 @@ static void pd_next_buf( int unit )
 
 {      long    saved_flags;
 
-       spin_lock_irqsave(&io_request_lock,saved_flags);
+       spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
        end_request(1);
-       if (!pd_run) {  spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       if (!pd_run) {  spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                        return; 
        }
        
@@ -896,7 +893,7 @@ static void pd_next_buf( int unit )
 
        pd_count = CURRENT->current_nr_sectors;
        pd_buf = CURRENT->buffer;
-       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pd_read( void )
@@ -919,11 +916,11 @@ static void do_pd_read_start( void )
                         pi_do_claimed(PI,do_pd_read_start);
                        return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
                do_pd_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pd_ide_command(unit,IDE_READ,pd_block,pd_run);
@@ -943,11 +940,11 @@ static void do_pd_read_drq( void )
                         pi_do_claimed(PI,do_pd_read_start);
                         return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
                do_pd_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
             }
             pi_read_block(PI,pd_buf,512);
@@ -958,11 +955,11 @@ static void do_pd_read_drq( void )
            if (!pd_count) pd_next_buf(unit);
         }
         pi_disconnect(PI);
-       spin_lock_irqsave(&io_request_lock,saved_flags);
+       spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
         end_request(1);
         pd_busy = 0;
        do_pd_request(NULL);
-       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pd_write( void )
@@ -985,11 +982,11 @@ static void do_pd_write_start( void )
                        pi_do_claimed(PI,do_pd_write_start);
                         return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
                do_pd_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pd_ide_command(unit,IDE_WRITE,pd_block,pd_run);
@@ -1001,11 +998,11 @@ static void do_pd_write_start( void )
                         pi_do_claimed(PI,do_pd_write_start);
                         return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
                do_pd_request(NULL);
-                spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                return;
             }
             pi_write_block(PI,pd_buf,512);
@@ -1030,19 +1027,19 @@ static void do_pd_write_done( void )
                         pi_do_claimed(PI,do_pd_write_start);
                         return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
                do_pd_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pi_disconnect(PI);
-       spin_lock_irqsave(&io_request_lock,saved_flags);
+       spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
         end_request(1);
         pd_busy = 0;
        do_pd_request(NULL);
-       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 /* end of pd.c */
index 7b28276..7ca422e 100644 (file)
@@ -340,56 +340,6 @@ void pf_init_units( void )
         }
 } 
 
-static inline int pf_new_segment(request_queue_t *q, struct request *req, int max_segments)
-{
-       if (max_segments > cluster)
-               max_segments = cluster;
-
-       if (req->nr_segments < max_segments) {
-               req->nr_segments++;
-               return 1;
-       }
-       return 0;
-}
-
-static int pf_back_merge_fn(request_queue_t *q, struct request *req, 
-                           struct buffer_head *bh, int max_segments)
-{
-       if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data)
-               return 1;
-       return pf_new_segment(q, req, max_segments);
-}
-
-static int pf_front_merge_fn(request_queue_t *q, struct request *req, 
-                            struct buffer_head *bh, int max_segments)
-{
-       if (bh->b_data + bh->b_size == req->bh->b_data)
-               return 1;
-       return pf_new_segment(q, req, max_segments);
-}
-
-static int pf_merge_requests_fn(request_queue_t *q, struct request *req,
-                               struct request *next, int max_segments)
-{
-       int total_segments = req->nr_segments + next->nr_segments;
-       int same_segment;
-
-       if (max_segments > cluster)
-               max_segments = cluster;
-
-       same_segment = 0;
-       if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) {
-               total_segments--;
-               same_segment = 1;
-       }
-    
-       if (total_segments > max_segments)
-               return 0;
-
-       req->nr_segments = total_segments;
-       return 1;
-}
-
 int pf_init (void)      /* preliminary initialisation */
 
 {       int i;
@@ -409,9 +359,7 @@ int pf_init (void)      /* preliminary initialisation */
         }
        q = BLK_DEFAULT_QUEUE(MAJOR_NR);
        blk_init_queue(q, DEVICE_REQUEST);
-       q->back_merge_fn = pf_back_merge_fn;
-       q->front_merge_fn = pf_front_merge_fn;
-       q->merge_requests_fn = pf_merge_requests_fn;
+       blk_queue_max_segments(q, cluster);
         read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
         
        for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
@@ -893,8 +841,7 @@ static int pf_ready( void )
 }
 
 static void do_pf_request (request_queue_t * q)
-
-{       struct buffer_head * bh;
+{
        int unit;
 
         if (pf_busy) return;
@@ -907,8 +854,6 @@ repeat:
         pf_run = CURRENT->nr_sectors;
         pf_count = CURRENT->current_nr_sectors;
 
-       bh = CURRENT->bh;
-
         if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) {
                 end_request(0);
                 goto repeat;
@@ -931,9 +876,9 @@ static void pf_next_buf( int unit )
 
 {      long    saved_flags;
 
-       spin_lock_irqsave(&io_request_lock,saved_flags);
+       spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
        end_request(1);
-       if (!pf_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       if (!pf_run) { spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                       return; 
        }
        
@@ -949,7 +894,7 @@ static void pf_next_buf( int unit )
 
        pf_count = CURRENT->current_nr_sectors;
        pf_buf = CURRENT->buffer;
-       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pf_read( void )
@@ -973,11 +918,11 @@ static void do_pf_read_start( void )
                         pi_do_claimed(PI,do_pf_read_start);
                        return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
                do_pf_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
        pf_mask = STAT_DRQ;
@@ -999,11 +944,11 @@ static void do_pf_read_drq( void )
                         pi_do_claimed(PI,do_pf_read_start);
                         return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
                do_pf_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
             }
             pi_read_block(PI,pf_buf,512);
@@ -1014,11 +959,11 @@ static void do_pf_read_drq( void )
            if (!pf_count) pf_next_buf(unit);
         }
         pi_disconnect(PI);
-       spin_lock_irqsave(&io_request_lock,saved_flags); 
+       spin_lock_irqsave(&QUEUE->queue_lock,saved_flags); 
         end_request(1);
         pf_busy = 0;
        do_pf_request(NULL);
-       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pf_write( void )
@@ -1040,11 +985,11 @@ static void do_pf_write_start( void )
                         pi_do_claimed(PI,do_pf_write_start);
                        return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
                do_pf_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
 
@@ -1057,11 +1002,11 @@ static void do_pf_write_start( void )
                         pi_do_claimed(PI,do_pf_write_start);
                         return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
                do_pf_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
             }
             pi_write_block(PI,pf_buf,512);
@@ -1087,19 +1032,19 @@ static void do_pf_write_done( void )
                        pi_do_claimed(PI,do_pf_write_start);
                         return;
                 }
-               spin_lock_irqsave(&io_request_lock,saved_flags);
+               spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
                do_pf_request(NULL);
-               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pi_disconnect(PI);
-       spin_lock_irqsave(&io_request_lock,saved_flags);
+       spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
         end_request(1);
         pf_busy = 0;
        do_pf_request(NULL);
-       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+       spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 /* end of pf.c */
index bdb13ae..dcf1cd9 100644 (file)
@@ -228,18 +228,19 @@ static struct address_space_operations ramdisk_aops = {
        commit_write: ramdisk_commit_write,
 };
 
-static int rd_blkdev_pagecache_IO(int rw, struct bio *sbh, int minor)
+static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec,
+                                 sector_t sector, int minor)
 {
        struct address_space * mapping;
        unsigned long index;
        int offset, size, err;
 
-       err = -EIO;
+       err = 0;
        mapping = rd_bdev[minor]->bd_inode->i_mapping;
 
-       index = sbh->bi_sector >> (PAGE_CACHE_SHIFT - 9);
-       offset = (sbh->bi_sector << 9) & ~PAGE_CACHE_MASK;
-       size = bio_size(sbh);
+       index = sector >> (PAGE_CACHE_SHIFT - 9);
+       offset = (sector << 9) & ~PAGE_CACHE_MASK;
+       size = vec->bv_len;
 
        do {
                int count;
@@ -276,18 +277,18 @@ static int rd_blkdev_pagecache_IO(int rw, struct bio *sbh, int minor)
                if (rw == READ) {
                        src = kmap(page);
                        src += offset;
-                       dst = bio_kmap(sbh);
+                       dst = kmap(vec->bv_page) + vec->bv_offset;
                } else {
                        dst = kmap(page);
                        dst += offset;
-                       src = bio_kmap(sbh);
+                       src = kmap(vec->bv_page) + vec->bv_offset;
                }
                offset = 0;
 
                memcpy(dst, src, count);
 
                kunmap(page);
-               bio_kunmap(sbh);
+               kunmap(vec->bv_page);
 
                if (rw == READ) {
                        flush_dcache_page(page);
@@ -303,6 +304,22 @@ static int rd_blkdev_pagecache_IO(int rw, struct bio *sbh, int minor)
        return err;
 }
 
+static int rd_blkdev_bio_IO(struct bio *bio, unsigned int minor)
+{
+       struct bio_vec *bvec;
+       sector_t sector;
+       int ret = 0, i, rw;
+
+       sector = bio->bi_sector;
+       rw = bio_data_dir(bio);
+       bio_for_each_segment(bvec, bio, i) {
+               ret |= rd_blkdev_pagecache_IO(rw, bvec, sector, minor);
+               sector += bvec->bv_len >> 9;
+       }
+
+       return ret;
+}
+
 /*
  *  Basically, my strategy here is to set up a buffer-head which can't be
  *  deleted, and make that my Ramdisk.  If the request is outside of the
@@ -323,7 +340,7 @@ static int rd_make_request(request_queue_t * q, struct bio *sbh)
                goto fail;
 
        offset = sbh->bi_sector << 9;
-       len = bio_size(sbh);
+       len = sbh->bi_size;
 
        if ((offset + len) > rd_length[minor])
                goto fail;
@@ -335,7 +352,7 @@ static int rd_make_request(request_queue_t * q, struct bio *sbh)
                goto fail;
        }
 
-       if (rd_blkdev_pagecache_IO(rw, sbh, minor))
+       if (rd_blkdev_bio_IO(sbh, minor))
                goto fail;
 
        set_bit(BIO_UPTODATE, &sbh->bi_flags);
@@ -437,10 +454,11 @@ static int rd_open(struct inode * inode, struct file * filp)
 
 #ifdef CONFIG_BLK_DEV_INITRD
        if (unit == INITRD_MINOR) {
-               if (!initrd_start) return -ENODEV;
-               spin_lock( &initrd_users_lock );
+               spin_lock(&initrd_users_lock);
                initrd_users++;
-               spin_unlock( &initrd_users_lock );
+               spin_unlock(&initrd_users_lock);
+               if (!initrd_start) 
+                       return -ENODEV;
                filp->f_op = &initrd_fops;
                return 0;
        }
@@ -539,10 +557,8 @@ int __init rd_init (void)
        return 0;
 }
 
-#ifdef MODULE
 module_init(rd_init);
 module_exit(rd_cleanup);
-#endif
 
 /* loadable module support */
 MODULE_PARM     (rd_size, "1i");
index 85d115f..630ef9c 100644 (file)
@@ -1399,10 +1399,6 @@ static int __init intel_i830_setup(struct pci_dev *i830_dev)
 }
 
 #endif /* CONFIG_AGP_I810 */
- #ifdef CONFIG_AGP_INTEL
-
-#endif /* CONFIG_AGP_I810 */
 
 #ifdef CONFIG_AGP_INTEL
 
@@ -3564,12 +3560,6 @@ static struct {
                "AMD",
                "Irongate",
                amd_irongate_setup },
-       { PCI_DEVICE_ID_AMD_762_0,
-               PCI_VENDOR_ID_AMD,
-               AMD_IRONGATE,
-               "AMD",
-               "AMD 760MP",
-               amd_irongate_setup },
        { PCI_DEVICE_ID_AMD_761_0,
                PCI_VENDOR_ID_AMD,
                AMD_761,
index 3c81b65..ee5e51f 100644 (file)
@@ -114,7 +114,7 @@ static int zft_open(struct inode *ino, struct file *filep)
        TRACE_FUN(ft_t_flow);
 
        TRACE(ft_t_flow, "called for minor %d", MINOR(ino->i_rdev));
-       if ( test_and_set_bit(0,&busy_flag) )) {
+       if ( test_and_set_bit(0,&busy_flag) ) {
                TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy");
        }
        if ((MINOR(ino->i_rdev) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND))
index 76fef3f..657e448 100644 (file)
@@ -1074,13 +1074,18 @@ static int release_aux(struct inode * inode, struct file * file)
 static int open_aux(struct inode * inode, struct file * file)
 {
        unsigned long flags;
+       int ret;
+
        spin_lock_irqsave(&aux_count_lock, flags);
        if ( aux_count++ ) {
                spin_unlock_irqrestore(&aux_count_lock, flags);
                return 0;
        }
        queue->head = queue->tail = 0;          /* Flush input queue */
-       if (aux_request_irq(keyboard_interrupt, AUX_DEV)) {
+       spin_unlock_irqrestore(&aux_count_lock, flags);
+       ret = aux_request_irq(keyboard_interrupt, AUX_DEV);
+       spin_lock_irqsave(&aux_count_lock, flags);
+       if (ret) {
                aux_count--;
                spin_unlock_irqrestore(&aux_count_lock, flags);
                return -EBUSY;
index c26518b..e873ee6 100644 (file)
 
 #include <linux/kmod.h>
 
-#ifdef CONFIG_VT
-extern void con_init_devfs (void);
-#endif
-
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
 #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
 #define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
@@ -2246,6 +2242,8 @@ static struct tty_driver dev_tty_driver, dev_syscons_driver;
 static struct tty_driver dev_ptmx_driver;
 #endif
 #ifdef CONFIG_VT
+extern void con_init_devfs (void);
+extern void console_map_init(void);
 static struct tty_driver dev_console_driver;
 #endif
 
index 1cf1884..56e2173 100644 (file)
@@ -711,7 +711,7 @@ static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, uns
        int count;
 
        while (bcount) {
-               if (pc->b_count == bio_size(bio)) {
+               if (pc->b_count == bio->bi_size) {
                        rq->sector += rq->current_nr_sectors;
                        rq->nr_sectors -= rq->current_nr_sectors;
                        idefloppy_end_request (1, HWGROUP(drive));
@@ -723,7 +723,7 @@ static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, uns
                        idefloppy_discard_data (drive, bcount);
                        return;
                }
-               count = IDEFLOPPY_MIN (bio_size(bio) - pc->b_count, bcount);
+               count = IDEFLOPPY_MIN (bio->bi_size - pc->b_count, bcount);
                atapi_input_bytes (drive, bio_data(bio) + pc->b_count, count);
                bcount -= count; pc->b_count += count;
        }
@@ -742,7 +742,7 @@ static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, un
                        idefloppy_end_request (1, HWGROUP(drive));
                        if ((bio = rq->bio) != NULL) {
                                pc->b_data = bio_data(bio);
-                               pc->b_count = bio_size(bio);
+                               pc->b_count = bio->bi_size;
                        }
                }
                if (bio == NULL) {
@@ -1210,7 +1210,7 @@ static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t
        pc->callback = &idefloppy_rw_callback;
        pc->rq = rq;
        pc->b_data = rq->buffer;
-       pc->b_count = rq->cmd == READ ? 0 : bio_size(rq->bio);
+       pc->b_count = rq->cmd == READ ? 0 : rq->bio->bi_size;
        if (rq->cmd == WRITE)
                set_bit (PC_WRITING, &pc->flags);
        pc->buffer = NULL;
index c3fe1f7..e75d36b 100644 (file)
 #define DEVID_MPIIX    ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371MX})
 #define DEVID_PIIX3    ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371SB_1})
 #define DEVID_PIIX4    ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371AB})
-#define DEVID_PIIX4E   ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AB_1})
+#define DEVID_ICH0     ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AB_1})
 #define DEVID_PIIX4E2  ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443MX_1})
-#define DEVID_PIIX4U   ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AA_1})
+#define DEVID_ICH      ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AA_1})
 #define DEVID_PIIX4U2  ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82372FB_1})
 #define DEVID_PIIX4NX  ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82451NX})
-#define DEVID_PIIX4U3  ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_9})
-#define DEVID_PIIX4U4  ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_8})
-#define DEVID_PIIX4U5  ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801CA_10})
+#define DEVID_ICH2     ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_9})
+#define DEVID_ICH2M    ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_8})
+#define DEVID_ICH3     ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801CA_10})
 #define DEVID_VIA_IDE  ((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C561})
 #define DEVID_MR_IDE   ((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C576_1})
 #define DEVID_VP_IDE   ((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_1})
@@ -380,14 +380,14 @@ static ide_pci_device_t ide_pci_chipsets[] __initdata = {
        {DEVID_MPIIX,   "MPIIX",        NULL,           NULL,           INIT_PIIX,      NULL,           {{0x6D,0x80,0x80}, {0x6F,0x80,0x80}},   ON_BOARD,       0 },
        {DEVID_PIIX3,   "PIIX3",        PCI_PIIX,       NULL,           INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
        {DEVID_PIIX4,   "PIIX4",        PCI_PIIX,       NULL,           INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
-       {DEVID_PIIX4E,  "PIIX4",        PCI_PIIX,       NULL,           INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
+       {DEVID_ICH0,    "ICH0",         PCI_PIIX,       NULL,           INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
        {DEVID_PIIX4E2, "PIIX4",        PCI_PIIX,       NULL,           INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
-       {DEVID_PIIX4U,  "PIIX4",        PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
+       {DEVID_ICH,     "ICH",          PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
        {DEVID_PIIX4U2, "PIIX4",        PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
        {DEVID_PIIX4NX, "PIIX4",        PCI_PIIX,       NULL,           INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
-       {DEVID_PIIX4U3, "PIIX4",        PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
-       {DEVID_PIIX4U4, "PIIX4",        PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
-       {DEVID_PIIX4U5, "PIIX4",        PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
+       {DEVID_ICH2,    "ICH2",         PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
+       {DEVID_ICH2M,   "ICH2-M",       PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
+       {DEVID_ICH3,    "ICH3",         PCI_PIIX,       ATA66_PIIX,     INIT_PIIX,      NULL,           {{0x41,0x80,0x80}, {0x43,0x80,0x80}},   ON_BOARD,       0 },
        {DEVID_VIA_IDE, "VIA_IDE",      NULL,           NULL,           NULL,           NULL,           {{0x00,0x00,0x00}, {0x00,0x00,0x00}},   ON_BOARD,       0 },
        {DEVID_MR_IDE,  "VP_IDE",       PCI_VIA82CXXX,  ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX,  {{0x40,0x02,0x02}, {0x40,0x01,0x01}},   ON_BOARD,       0 },
        {DEVID_VP_IDE,  "VP_IDE",       PCI_VIA82CXXX,  ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX,  {{0x40,0x02,0x02}, {0x40,0x01,0x01}},   ON_BOARD,       0 },
@@ -842,7 +842,7 @@ static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_devic
 
        switch(class_rev) {
                case 4:
-               case 3: printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+               case 3: printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name);
                        ide_setup_pci_device(dev, d);
                        return;
                default:        break;
@@ -871,12 +871,12 @@ static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_devic
                        break;
                }
        }
-       printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+       printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name);
        ide_setup_pci_device(dev, d);
        if (!dev2)
                return;
        d2 = d;
-       printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn);
+       printk("%s: IDE controller on PCI slot %s\n", d2->name, dev2->slot_name);
        ide_setup_pci_device(dev2, d2);
 }
 
@@ -906,10 +906,10 @@ void __init ide_scan_pcidev (struct pci_dev *dev)
                hpt366_device_order_fixup(dev, d);
        else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
                if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
-                       printk("%s: unknown IDE controller on PCI bus %02x device %02x, VID=%04x, DID=%04x\n",
-                              d->name, dev->bus->number, dev->devfn, devid.vid, devid.did);
+                       printk("%s: unknown IDE controller on PCI slot %s, VID=%04x, DID=%04x\n",
+                              d->name, dev->slot_name, devid.vid, devid.did);
                else
-                       printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+                       printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name);
                ide_setup_pci_device(dev, d);
        }
 }
index 1fbac4c..0f64f4e 100644 (file)
@@ -88,33 +88,9 @@ static int piix_get_info (char *buffer, char **addr, off_t offset, int count)
        u8  c0 = 0, c1 = 0;
        u8  reg44 = 0, reg48 = 0, reg4a = 0, reg4b = 0, reg54 = 0, reg55 = 0;
 
-       switch(bmide_dev->device) {
-               case PCI_DEVICE_ID_INTEL_82801BA_8:
-               case PCI_DEVICE_ID_INTEL_82801BA_9:
-               case PCI_DEVICE_ID_INTEL_82801CA_10:
-                       p += sprintf(p, "\n                                Intel PIIX4 Ultra 100 Chipset.\n");
-                       break;
-               case PCI_DEVICE_ID_INTEL_82372FB_1:
-               case PCI_DEVICE_ID_INTEL_82801AA_1:
-                       p += sprintf(p, "\n                                Intel PIIX4 Ultra 66 Chipset.\n");
-                       break;
-               case PCI_DEVICE_ID_INTEL_82451NX:
-               case PCI_DEVICE_ID_INTEL_82801AB_1:
-               case PCI_DEVICE_ID_INTEL_82443MX_1:
-               case PCI_DEVICE_ID_INTEL_82371AB:
-                       p += sprintf(p, "\n                                Intel PIIX4 Ultra 33 Chipset.\n");
-                       break;
-               case PCI_DEVICE_ID_INTEL_82371SB_1:
-                       p += sprintf(p, "\n                                Intel PIIX3 Chipset.\n");
-                       break;
-               case PCI_DEVICE_ID_INTEL_82371MX:
-                       p += sprintf(p, "\n                                Intel MPIIX Chipset.\n");
-                       return p-buffer;        /* => must be less than 4k! */
-               case PCI_DEVICE_ID_INTEL_82371FB_1:
-               case PCI_DEVICE_ID_INTEL_82371FB_0:
-               default:
-                       p += sprintf(p, "\n                                Intel PIIX Chipset.\n");
-                       break;
+       if (bmide_dev->device == PCI_DEVICE_ID_INTEL_82371MX) {
+               p += sprintf(p, "\n                                Intel MPIIX Chipset.\n");
+               return p-buffer;        /* => must be less than 4k! */
        }
 
        pci_read_config_word(bmide_dev, 0x40, &reg40);
@@ -136,6 +112,7 @@ static int piix_get_info (char *buffer, char **addr, off_t offset, int count)
        c0 = inb_p((unsigned short)bibma + 0x02);
        c1 = inb_p((unsigned short)bibma + 0x0a);
 
+       p += sprintf(p, "\n                    %s Chipset.\n", bmide_dev->name);
        p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
        p += sprintf(p, "                %sabled                         %sabled\n",
                        (c0&0x80) ? "dis" : " en",
index 30ccb26..707940b 100644 (file)
@@ -81,6 +81,7 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
    dep_tristate 'Sedlbauer PCMCIA cards' CONFIG_HISAX_SEDLBAUER_CS $CONFIG_PCMCIA
    dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_PCMCIA
    dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL
+   dep_tristate 'Fritz!PCIv2 support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL
 fi
 endmenu
 
index 1bf9528..78122d1 100644 (file)
@@ -58,7 +58,6 @@ put_info_buffer(char *cp)
        else
                divert_info_tail->next = ib;    /* follows existing messages */
        divert_info_tail = ib;  /* new tail */
-       restore_flags(flags);
 
        /* delete old entrys */
        while (divert_info_head->next) {
@@ -70,7 +69,7 @@ put_info_buffer(char *cp)
                } else
                        break;
        }                       /* divert_info_head->next */
-       spin_lock_irqrestore( &divert_info_lock, flags );
+       spin_unlock_irqrestore( &divert_info_lock, flags );
        wake_up_interruptible(&(rd_queue));
 }                              /* put_info_buffer */
 
@@ -163,7 +162,6 @@ isdn_divert_close(struct inode *ino, struct file *filep)
                inf->usage_cnt--;
                inf = inf->next;
        }
-       restore_flags(flags);
        if (if_used <= 0)
                while (divert_info_head) {
                        inf = divert_info_head;
index 9f1a1f6..0b49772 100644 (file)
@@ -6,7 +6,7 @@ O_TARGET          := vmlinux-obj.o
 
 # Objects that export symbols.
 
-export-objs      := config.o fsm.o
+export-objs      := config.o fsm.o hisax_isac.o
 
 # Multipart objects.
 
@@ -58,6 +58,7 @@ obj-$(CONFIG_ISDN_DRV_HISAX)          += hisax.o
 obj-$(CONFIG_HISAX_SEDLBAUER_CS)       += sedlbauer_cs.o
 obj-$(CONFIG_HISAX_ELSA_CS)            += elsa_cs.o
 obj-$(CONFIG_HISAX_ST5481)             += hisax_st5481.o
+obj-$(CONFIG_HISAX_FRITZ_PCIPNP)        += hisax_isac.o hisax_fcpcipnp.o
 
 CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
 CFLAGS_cert.o := -DCERTIFICATION=$(CERT)
index 0af9a60..583fec9 100644 (file)
@@ -1505,15 +1505,9 @@ static int __init HiSax_init(void)
               nrcards, (nrcards > 1) ? "s" : "");
 
        /* Install only, if at least one card found */
-       if (!HiSax_inithardware(NULL)) {
-               retval = -EIO;
-               goto out_isdnl1;
-       }
-
+       HiSax_inithardware(NULL);
        return 0;
 
- out_isdnl1:
-       Isdnl1Free();
  out_tei:
        TeiFree();
  out_isdnl2:
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c
new file mode 100644 (file)
index 0000000..479e18f
--- /dev/null
@@ -0,0 +1,1001 @@
+/*
+ * Driver for AVM Fritz!PCI, Fritz!PCI v2, Fritz!PnP ISDN cards
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original avm_pci.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+
+/* TODO:
+ *
+ * o POWER PC
+ * o clean up debugging
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include "hisax_fcpcipnp.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver");
+
+static struct pci_device_id fcpci_ids[] __devinitdata = {
+       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1   , PCI_ANY_ID, PCI_ANY_ID,
+         0, 0, (unsigned long) "Fritz!Card PCI" },
+       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
+         0, 0, (unsigned long) "Fritz!Card PCI v2" },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+static struct isapnp_device_id fcpnp_ids[] __devinitdata = {
+       { ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900),
+         ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), 
+         (unsigned long) "Fritz!Card PnP" },
+       { }
+};
+MODULE_DEVICE_TABLE(isapnp, fcpnp_ids);
+
+static int protocol = 2;       /* EURO-ISDN Default */
+MODULE_PARM(protocol, "i");
+
+// ----------------------------------------------------------------------
+
+#define  AVM_INDEX              0x04
+#define  AVM_DATA               0x10
+
+#define         AVM_IDX_HDLC_1         0x00
+#define         AVM_IDX_HDLC_2         0x01
+#define         AVM_IDX_ISAC_FIFO      0x02
+#define         AVM_IDX_ISAC_REG_LOW   0x04
+#define         AVM_IDX_ISAC_REG_HIGH  0x06
+
+#define  AVM_STATUS0            0x02
+
+#define  AVM_STATUS0_IRQ_ISAC  0x01
+#define  AVM_STATUS0_IRQ_HDLC  0x02
+#define  AVM_STATUS0_IRQ_TIMER 0x04
+#define  AVM_STATUS0_IRQ_MASK  0x07
+
+#define  AVM_STATUS0_RESET     0x01
+#define  AVM_STATUS0_DIS_TIMER 0x02
+#define  AVM_STATUS0_RES_TIMER 0x04
+#define  AVM_STATUS0_ENA_IRQ   0x08
+#define  AVM_STATUS0_TESTBIT   0x10
+
+#define  AVM_STATUS1            0x03
+#define  AVM_STATUS1_ENA_IOM   0x80
+
+#define  HDLC_FIFO             0x0
+#define  HDLC_STATUS           0x4
+#define  HDLC_CTRL             0x4
+
+#define  HDLC_MODE_ITF_FLG     0x01
+#define  HDLC_MODE_TRANS       0x02
+#define  HDLC_MODE_CCR_7       0x04
+#define  HDLC_MODE_CCR_16      0x08
+#define  HDLC_MODE_TESTLOOP    0x80
+
+#define  HDLC_INT_XPR          0x80
+#define  HDLC_INT_XDU          0x40
+#define  HDLC_INT_RPR          0x20
+#define  HDLC_INT_MASK         0xE0
+
+#define  HDLC_STAT_RME         0x01
+#define  HDLC_STAT_RDO         0x10
+#define  HDLC_STAT_CRCVFRRAB   0x0E
+#define  HDLC_STAT_CRCVFR      0x06
+#define  HDLC_STAT_RML_MASK    0x3f00
+
+#define  HDLC_CMD_XRS          0x80
+#define  HDLC_CMD_XME          0x01
+#define  HDLC_CMD_RRS          0x20
+#define  HDLC_CMD_XML_MASK     0x3f00
+
+#define  AVM_HDLC_FIFO_1        0x10
+#define  AVM_HDLC_FIFO_2        0x18
+
+#define  AVM_HDLC_STATUS_1      0x14
+#define  AVM_HDLC_STATUS_2      0x1c
+
+#define  AVM_ISACSX_INDEX       0x04
+#define  AVM_ISACSX_DATA        0x08
+
+// ----------------------------------------------------------------------
+// Fritz!PCI
+
+static unsigned char fcpci_read_isac(struct isac *isac, unsigned char offset)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       unsigned char idx = (offset > 0x2f) ? 
+               AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+       unsigned char val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outb(idx, adapter->io + AVM_INDEX);
+       val = inb(adapter->io + AVM_DATA + (offset & 0xf));
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+       DBG(0x1000, " port %#x, value %#x",
+           offset, val);
+       return val;
+}
+
+static void fcpci_write_isac(struct isac *isac, unsigned char offset,
+                            unsigned char value)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       unsigned char idx = (offset > 0x2f) ? 
+               AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+       unsigned long flags;
+
+       DBG(0x1000, " port %#x, value %#x",
+           offset, value);
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outb(idx, adapter->io + AVM_INDEX);
+       outb(value, adapter->io + AVM_DATA + (offset & 0xf));
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_read_isac_fifo(struct isac *isac, unsigned char * data, 
+                                int size)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+       insb(adapter->io + AVM_DATA, data, size);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_write_isac_fifo(struct isac *isac, unsigned char * data, 
+                                 int size)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+       outsb(adapter->io + AVM_DATA, data, size);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+       u32 val;
+       int idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outl(idx, adapter->io + AVM_INDEX);
+       val = inl(adapter->io + AVM_DATA + HDLC_STATUS);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+       return val;
+}
+
+static void __fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       int idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+       DBG(0x40, "hdlc %c wr%x ctrl %x",
+           'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+       outl(idx, adapter->io + AVM_INDEX);
+       outl(bcs->ctrl.ctrl, adapter->io + AVM_DATA + HDLC_CTRL);
+}
+
+static void fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       __fcpci_write_ctrl(bcs, which);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PCI v2
+
+static unsigned char fcpci2_read_isac(struct isac *isac, unsigned char offset)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       unsigned char val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outl(offset, adapter->io + AVM_ISACSX_INDEX);
+       val = inl(adapter->io + AVM_ISACSX_DATA);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+       DBG(0x1000, " port %#x, value %#x",
+           offset, val);
+
+       return val;
+}
+
+static void fcpci2_write_isac(struct isac *isac, unsigned char offset, 
+                             unsigned char value)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       unsigned long flags;
+
+       DBG(0x1000, " port %#x, value %#x",
+           offset, value);
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outl(offset, adapter->io + AVM_ISACSX_INDEX);
+       outl(value, adapter->io + AVM_ISACSX_DATA);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_read_isac_fifo(struct isac *isac, unsigned char * data, 
+                                 int size)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outl(0, adapter->io + AVM_ISACSX_INDEX);
+       for (i = 0; i < size; i++)
+               data[i] = inl(adapter->io + AVM_ISACSX_DATA);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_write_isac_fifo(struct isac *isac, unsigned char * data, 
+                                  int size)
+{
+       struct fritz_adapter *adapter = isac->priv;
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outl(0, adapter->io + AVM_ISACSX_INDEX);
+       for (i = 0; i < size; i++)
+               outl(data[i], adapter->io + AVM_ISACSX_DATA);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci2_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+       int offset = nr ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+       return inl(adapter->io + offset);
+}
+
+static void fcpci2_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       int offset = bcs->channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+       DBG(0x40, "hdlc %c wr%x ctrl %x",
+           'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+       outl(bcs->ctrl.ctrl, adapter->io + offset);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PnP (ISAC access as for Fritz!PCI)
+
+static u32 fcpnp_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+       unsigned char idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       outb(idx, adapter->io + AVM_INDEX);
+       val = inb(adapter->io + AVM_DATA + HDLC_STATUS);
+       if (val & HDLC_INT_RPR)
+               val |= inb(adapter->io + AVM_DATA + HDLC_STATUS + 1) << 8;
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+       return val;
+}
+
+static void __fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+       DBG(0x40, "hdlc %c wr%x ctrl %x",
+           'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+       outb(idx, adapter->io + AVM_INDEX);
+       if (which & 4)
+               outb(bcs->ctrl.sr.mode, 
+                    adapter->io + AVM_DATA + HDLC_STATUS + 2);
+       if (which & 2)
+               outb(bcs->ctrl.sr.xml, 
+                    adapter->io + AVM_DATA + HDLC_STATUS + 1);
+       if (which & 1)
+               outb(bcs->ctrl.sr.cmd,
+                    adapter->io + AVM_DATA + HDLC_STATUS + 0);
+}
+
+static void fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+       __fcpnp_write_ctrl(bcs, which);
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+
+static inline void B_L1L2(struct fritz_bcs *bcs, int pr, void *arg)
+{
+       struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+       DBG(2, "pr %#x", pr);
+       ifc->l1l2(ifc, pr, arg);
+}
+
+static void hdlc_fill_fifo(struct fritz_bcs *bcs)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       struct sk_buff *skb = bcs->tx_skb;
+       int count;
+       int fifo_size = 32;
+       unsigned long flags;
+       unsigned char *p;
+
+       DBG(0x40, "hdlc_fill_fifo");
+
+       if (!skb)
+               BUG();
+
+       if (skb->len == 0)
+               BUG();
+
+       bcs->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+       if (bcs->tx_skb->len > fifo_size) {
+               count = fifo_size;
+       } else {
+               count = bcs->tx_skb->len;
+               if (bcs->mode != L1_MODE_TRANS)
+                       bcs->ctrl.sr.cmd |= HDLC_CMD_XME;
+       }
+       DBG(0x40, "hdlc_fill_fifo %d/%d", count, bcs->tx_skb->len);
+       p = bcs->tx_skb->data;
+       skb_pull(bcs->tx_skb, count);
+       bcs->tx_cnt += count;
+       bcs->ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+
+       switch (adapter->type) {
+       case AVM_FRITZ_PCI:
+               spin_lock_irqsave(&adapter->hw_lock, flags);
+               // sets the correct AVM_INDEX, too
+               __fcpci_write_ctrl(bcs, 3);
+               outsl(adapter->io + AVM_DATA + HDLC_FIFO,
+                     p, (count + 3) / 4);
+               spin_unlock_irqrestore(&adapter->hw_lock, flags);
+               break;
+       case AVM_FRITZ_PCIV2:
+               fcpci2_write_ctrl(bcs, 3);
+               outsl(adapter->io + 
+                     (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+                     p, (count + 3) / 4);
+               break;
+       case AVM_FRITZ_PNP:
+               spin_lock_irqsave(&adapter->hw_lock, flags);
+               // sets the correct AVM_INDEX, too
+               __fcpnp_write_ctrl(bcs, 3);
+               outsb(adapter->io + AVM_DATA, p, count);
+               spin_unlock_irqrestore(&adapter->hw_lock, flags);
+               break;
+       }
+}
+
+static inline void hdlc_empty_fifo(struct fritz_bcs *bcs, int count)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       unsigned char *p;
+       unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+       DBG(0x10, "hdlc_empty_fifo %d", count);
+       if (bcs->rcvidx + count > HSCX_BUFMAX) {
+               DBG(0x10, "hdlc_empty_fifo: incoming packet too large");
+               return;
+       }
+       p = bcs->rcvbuf + bcs->rcvidx;
+       bcs->rcvidx += count;
+       switch (adapter->type) {
+       case AVM_FRITZ_PCI:
+               spin_lock(&adapter->hw_lock);
+               outl(idx, adapter->io + AVM_INDEX);
+               insl(adapter->io + AVM_DATA + HDLC_FIFO, 
+                    p, (count + 3) / 4);
+               spin_unlock(&adapter->hw_lock);
+               break;
+       case AVM_FRITZ_PCIV2:
+               insl(adapter->io + 
+                    (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+                    p, (count + 3) / 4);
+               break;
+       case AVM_FRITZ_PNP:
+               spin_lock(&adapter->hw_lock);
+               outb(idx, adapter->io + AVM_INDEX);
+               insb(adapter->io + AVM_DATA, p, count);
+               spin_unlock(&adapter->hw_lock);
+               break;
+       }
+}
+
+static inline void hdlc_rpr_irq(struct fritz_bcs *bcs, u32 stat)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       struct sk_buff *skb;
+       int len;
+
+       if (stat & HDLC_STAT_RDO) {
+               DBG(0x10, "RDO");
+               bcs->ctrl.sr.xml = 0;
+               bcs->ctrl.sr.cmd |= HDLC_CMD_RRS;
+               adapter->write_ctrl(bcs, 1);
+               bcs->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+               adapter->write_ctrl(bcs, 1);
+               bcs->rcvidx = 0;
+               return;
+       }
+
+       len = (stat & HDLC_STAT_RML_MASK) >> 8;
+       if (len == 0)
+               len = 32;
+
+       hdlc_empty_fifo(bcs, len);
+
+       if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+               if (((stat & HDLC_STAT_CRCVFRRAB)== HDLC_STAT_CRCVFR) ||
+                   (bcs->mode == L1_MODE_TRANS)) {
+                       skb = dev_alloc_skb(bcs->rcvidx);
+                       if (!skb) {
+                               printk(KERN_WARNING "HDLC: receive out of memory\n");
+                       } else {
+                               memcpy(skb_put(skb, bcs->rcvidx), bcs->rcvbuf,
+                                      bcs->rcvidx);
+                               DBG_SKB(1, skb);
+                               B_L1L2(bcs, PH_DATA | INDICATION, skb);
+                       }
+                       bcs->rcvidx = 0;
+               } else {
+                       DBG(0x10, "ch%d invalid frame %#x",
+                           bcs->channel, stat);
+                       bcs->rcvidx = 0;
+               }
+       }
+}
+
+static inline void hdlc_xdu_irq(struct fritz_bcs *bcs)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+
+       /* Here we lost an TX interrupt, so
+        * restart transmitting the whole frame.
+        */
+       bcs->ctrl.sr.xml = 0;
+       bcs->ctrl.sr.cmd |= HDLC_CMD_XRS;
+       adapter->write_ctrl(bcs, 1);
+       bcs->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+       adapter->write_ctrl(bcs, 1);
+
+       if (!bcs->tx_skb) {
+               DBG(0x10, "XDU without skb");
+               return;
+       }
+       skb_push(bcs->tx_skb, bcs->tx_cnt);
+       bcs->tx_cnt = 0;
+}
+
+static inline void hdlc_xpr_irq(struct fritz_bcs *bcs)
+{
+       struct sk_buff *skb;
+
+       skb = bcs->tx_skb;
+       if (!skb)
+               return;
+
+       if (skb->len) {
+               hdlc_fill_fifo(bcs);
+               return;
+       }
+       bcs->tx_cnt = 0;
+       bcs->tx_skb = NULL;
+       B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+       dev_kfree_skb_irq(skb);
+}
+
+static void hdlc_irq(struct fritz_bcs *bcs, u32 stat)
+{
+       DBG(0x10, "ch%d stat %#x", bcs->channel, stat);
+       if (stat & HDLC_INT_RPR) {
+               DBG(0x10, "RPR");
+               hdlc_rpr_irq(bcs, stat);
+       }
+       if (stat & HDLC_INT_XDU) {
+               DBG(0x10, "XDU");
+               hdlc_xdu_irq(bcs);
+       }
+       if (stat & HDLC_INT_XPR) {
+               DBG(0x10, "XPR");
+               hdlc_xpr_irq(bcs);
+       }
+}
+
+static inline void hdlc_interrupt(struct fritz_adapter *adapter)
+{
+       int nr;
+       u32 stat;
+
+       for (nr = 0; nr < 2; nr++) {
+               stat = adapter->read_hdlc_status(adapter, nr);
+               DBG(0x10, "HDLC %c stat %#x", 'A' + nr, stat);
+               if (stat & HDLC_INT_MASK)
+                       hdlc_irq(&adapter->bcs[nr], stat);
+       }
+}
+
+static void modehdlc(struct fritz_bcs *bcs, int mode)
+{
+       struct fritz_adapter *adapter = bcs->adapter;
+       
+       DBG(0x40, "hdlc %c mode %d --> %d",
+           'A' + bcs->channel, bcs->mode, mode);
+
+       if (bcs->mode == mode)
+               return;
+
+       bcs->ctrl.ctrl = 0;
+       bcs->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+       switch (mode) {
+       case L1_MODE_NULL:
+               bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+               adapter->write_ctrl(bcs, 5);
+               break;
+       case L1_MODE_TRANS:
+               bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+               adapter->write_ctrl(bcs, 5);
+               bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
+               adapter->write_ctrl(bcs, 1);
+               bcs->ctrl.sr.cmd = 0;
+               break;
+       case L1_MODE_HDLC:
+               bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+               adapter->write_ctrl(bcs, 5);
+               bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
+               adapter->write_ctrl(bcs, 1);
+               bcs->ctrl.sr.cmd = 0;
+               break;
+       }
+       bcs->mode = mode;
+}
+
+static void fritz_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+       struct fritz_bcs *bcs = ifc->priv;
+       struct sk_buff *skb = arg;
+       int mode;
+
+       DBG(0x10, "pr %#x", pr);
+
+       switch (pr) {
+       case PH_DATA | REQUEST:
+               if (bcs->tx_skb)
+                       BUG();
+               
+               bcs->tx_skb = skb;
+               DBG_SKB(1, skb);
+               hdlc_fill_fifo(bcs);
+               break;
+       case PH_ACTIVATE | REQUEST:
+               mode = (int) arg;
+               DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+               modehdlc(bcs, mode);
+               B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+               break;
+       case PH_DEACTIVATE | REQUEST:
+               DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+               modehdlc(bcs, L1_MODE_NULL);
+               B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+               break;
+       }
+}
+
+// ----------------------------------------------------------------------
+
+static void fcpci2_irq(int intno, void *dev, struct pt_regs *regs)
+{
+       struct fritz_adapter *adapter = dev;
+       unsigned char val;
+
+       val = inb(adapter->io + AVM_STATUS0);
+       if (!(val & AVM_STATUS0_IRQ_MASK))
+               /* hopefully a shared  IRQ reqest */
+               return;
+       DBG(2, "STATUS0 %#x", val);
+       if (val & AVM_STATUS0_IRQ_ISAC)
+               isacsx_interrupt(&adapter->isac);
+
+       if (val & AVM_STATUS0_IRQ_HDLC)
+               hdlc_interrupt(adapter);
+}
+
+static void fcpci_irq(int intno, void *dev, struct pt_regs *regs)
+{
+       struct fritz_adapter *adapter = dev;
+       unsigned char sval;
+
+       sval = inb(adapter->io + 2);
+       if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+               /* possibly a shared  IRQ reqest */
+               return;
+       DBG(2, "sval %#x", sval);
+       if (!(sval & AVM_STATUS0_IRQ_ISAC))
+               isac_interrupt(&adapter->isac);
+
+       if (!(sval & AVM_STATUS0_IRQ_HDLC))
+               hdlc_interrupt(adapter);
+}
+
+// ----------------------------------------------------------------------
+
+static inline void fcpci2_init(struct fritz_adapter *adapter)
+{
+       outb(AVM_STATUS0_RES_TIMER, adapter->io + AVM_STATUS0);
+       outb(AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+}
+
+static inline void fcpci_init(struct fritz_adapter *adapter)
+{
+       outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | 
+            AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+       outb(AVM_STATUS1_ENA_IOM | adapter->irq, 
+            adapter->io + AVM_STATUS1);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(50*HZ / 1000); /* Timeout 50ms */
+}
+
+// ----------------------------------------------------------------------
+
+static int __devinit fcpcipnp_setup(struct fritz_adapter *adapter)
+{
+       u32 val = 0;
+       int retval;
+
+       DBG(1,"");
+
+       isac_init(&adapter->isac); // FIXME is this okay now
+
+       retval = -EBUSY;
+       if (!request_region(adapter->io, 32, "fcpcipnp"))
+               goto err;
+
+       switch (adapter->type) {
+       case AVM_FRITZ_PCIV2:
+               retval = request_irq(adapter->irq, fcpci2_irq, SA_SHIRQ, 
+                                    "fcpcipnp", adapter);
+               break;
+       case AVM_FRITZ_PCI:
+               retval = request_irq(adapter->irq, fcpci_irq, SA_SHIRQ,
+                                    "fcpcipnp", adapter);
+               break;
+       case AVM_FRITZ_PNP:
+               retval = request_irq(adapter->irq, fcpci_irq, 0,
+                                    "fcpcipnp", adapter);
+               break;
+       }
+       if (retval)
+               goto err_region;
+
+       switch (adapter->type) {
+       case AVM_FRITZ_PCIV2:
+       case AVM_FRITZ_PCI:
+               val = inl(adapter->io);
+               break;
+       case AVM_FRITZ_PNP:
+               val = inb(adapter->io);
+               val |= inb(adapter->io + 1) << 8;
+               break;
+       }
+
+       DBG(1, "stat %#x Class %X Rev %d",
+           val, val & 0xff, (val>>8) & 0xff);
+
+       spin_lock_init(&adapter->hw_lock);
+       adapter->isac.priv = adapter;
+       switch (adapter->type) {
+       case AVM_FRITZ_PCIV2:
+               adapter->isac.read_isac       = &fcpci2_read_isac;;
+               adapter->isac.write_isac      = &fcpci2_write_isac;
+               adapter->isac.read_isac_fifo  = &fcpci2_read_isac_fifo;
+               adapter->isac.write_isac_fifo = &fcpci2_write_isac_fifo;
+
+               adapter->read_hdlc_status     = &fcpci2_read_hdlc_status;
+               adapter->write_ctrl           = &fcpci2_write_ctrl;
+               break;
+       case AVM_FRITZ_PCI:
+               adapter->isac.read_isac       = &fcpci_read_isac;;
+               adapter->isac.write_isac      = &fcpci_write_isac;
+               adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+               adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+               adapter->read_hdlc_status     = &fcpci_read_hdlc_status;
+               adapter->write_ctrl           = &fcpci_write_ctrl;
+               break;
+       case AVM_FRITZ_PNP:
+               adapter->isac.read_isac       = &fcpci_read_isac;;
+               adapter->isac.write_isac      = &fcpci_write_isac;
+               adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+               adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+               adapter->read_hdlc_status     = &fcpnp_read_hdlc_status;
+               adapter->write_ctrl           = &fcpnp_write_ctrl;
+               break;
+       }
+
+       // Reset
+       outb(0, adapter->io + AVM_STATUS0);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(50 * HZ / 1000); // 50 msec
+       outb(AVM_STATUS0_RESET, adapter->io + AVM_STATUS0);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(50 * HZ / 1000); // 50 msec
+       outb(0, adapter->io + AVM_STATUS0);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(10 * HZ / 1000); // 10 msec
+
+       switch (adapter->type) {
+       case AVM_FRITZ_PCIV2:
+               fcpci2_init(adapter);
+               isacsx_setup(&adapter->isac);
+               break;
+       case AVM_FRITZ_PCI:
+       case AVM_FRITZ_PNP:
+               fcpci_init(adapter);
+               isac_setup(&adapter->isac);
+               break;
+       }
+       val = adapter->read_hdlc_status(adapter, 0);
+       DBG(0x20, "HDLC A STA %x", val);
+       val = adapter->read_hdlc_status(adapter, 1);
+       DBG(0x20, "HDLC B STA %x", val);
+
+       adapter->bcs[0].mode = -1;
+       adapter->bcs[1].mode = -1;
+       modehdlc(&adapter->bcs[0], L1_MODE_NULL);
+       modehdlc(&adapter->bcs[1], L1_MODE_NULL);
+
+       return 0;
+
+ err_region:
+       release_region(adapter->io, 32);
+ err:
+       return retval;
+}
+
+static void __devexit fcpcipnp_release(struct fritz_adapter *adapter)
+{
+       DBG(1,"");
+
+       outb(0, adapter->io + AVM_STATUS0);
+       free_irq(adapter->irq, adapter);
+       release_region(adapter->io, 32);
+}
+
+// ----------------------------------------------------------------------
+
+static struct fritz_adapter * __devinit 
+new_adapter(struct pci_dev *pdev)
+{
+       struct fritz_adapter *adapter;
+       struct hisax_b_if *b_if[2];
+       int i;
+
+       adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL);
+       if (!adapter)
+               return NULL;
+
+       memset(adapter, 0, sizeof(struct fritz_adapter));
+
+       SET_MODULE_OWNER(&adapter->isac.hisax_d_if);
+       adapter->isac.hisax_d_if.ifc.priv = &adapter->isac;
+       adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1;
+       
+       for (i = 0; i < 2; i++) {
+               adapter->bcs[i].adapter = adapter;
+               adapter->bcs[i].channel = i;
+               adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+               adapter->bcs[i].b_if.ifc.l2l1 = fritz_b_l2l1;
+       }
+
+       pci_set_drvdata(pdev, adapter);
+
+       for (i = 0; i < 2; i++)
+               b_if[i] = &adapter->bcs[i].b_if;
+
+       hisax_register(&adapter->isac.hisax_d_if, b_if, "fcpcipnp", protocol);
+
+       return adapter;
+}
+
+static void delete_adapter(struct fritz_adapter *adapter)
+{
+       hisax_unregister(&adapter->isac.hisax_d_if);
+       kfree(adapter);
+}
+
+static int __devinit fcpci_probe(struct pci_dev *pdev,
+                                const struct pci_device_id *ent)
+{
+       struct fritz_adapter *adapter;
+       int retval;
+
+       retval = -ENOMEM;
+       adapter = new_adapter(pdev);
+       if (!adapter)
+               goto err;
+
+       if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 
+               adapter->type = AVM_FRITZ_PCIV2;
+       else
+               adapter->type = AVM_FRITZ_PCI;
+
+       retval = pci_enable_device(pdev);
+       if (retval)
+               goto err_free;
+
+       adapter->io = pci_resource_start(pdev, 1);
+       adapter->irq = pdev->irq;
+
+       printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at %s\n",
+              (char *) ent->driver_data, pdev->slot_name);
+
+       retval = fcpcipnp_setup(adapter);
+       if (retval)
+               goto err_free;
+
+       return 0;
+       
+ err_free:
+       delete_adapter(adapter);
+ err:
+       return retval;
+}
+
+static int __devinit fcpnp_probe(struct pci_dev *pdev,
+                                const struct isapnp_device_id *ent)
+{
+       struct fritz_adapter *adapter;
+       int retval;
+
+       retval = -ENOMEM;
+       adapter = new_adapter(pdev);
+       if (!adapter)
+               goto err;
+
+       adapter->type = AVM_FRITZ_PNP;
+
+       pdev->prepare(pdev);
+       pdev->deactivate(pdev); // why?
+       pdev->activate(pdev);
+       adapter->io = pdev->resource[0].start;
+       adapter->irq = pdev->irq_resource[0].start;
+
+       printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at IO %#x irq %d\n",
+              (char *) ent->driver_data, adapter->io, adapter->irq);
+
+       retval = fcpcipnp_setup(adapter);
+       if (retval)
+               goto err_free;
+
+       return 0;
+       
+ err_free:
+       delete_adapter(adapter);
+ err:
+       return retval;
+}
+
+static void __devexit fcpci_remove(struct pci_dev *pdev)
+{
+       struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+       fcpcipnp_release(adapter);
+       pci_disable_device(pdev);
+       delete_adapter(adapter);
+}
+
+static void __devexit fcpnp_remove(struct pci_dev *pdev)
+{
+       struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+       fcpcipnp_release(adapter);
+       pdev->deactivate(pdev);
+       delete_adapter(adapter);
+}
+
+static struct pci_driver fcpci_driver = {
+       name:     "fcpci",
+       probe:    fcpci_probe,
+       remove:   fcpci_remove,
+       id_table: fcpci_ids,
+};
+
+static struct isapnp_driver fcpnp_driver = {
+       name:     "fcpnp",
+       probe:    fcpnp_probe,
+       remove:   fcpnp_remove,
+       id_table: fcpnp_ids,
+};
+
+static int __init hisax_fcpcipnp_init(void)
+{
+       int retval, pci_nr_found;
+
+       printk(KERN_INFO "hisax_fcpcipnp: Fritz!Card PCI/PCIv2/PnP ISDN driver v0.0.1\n");
+
+       retval = pci_register_driver(&fcpci_driver);
+       if (retval < 0)
+               goto out;
+       pci_nr_found = retval;
+
+       retval = isapnp_register_driver(&fcpnp_driver);
+       if (retval < 0)
+               goto out_unregister_pci;
+
+#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
+       if (pci_nr_found + retval == 0) {
+               retval = -ENODEV;
+               goto out_unregister_isapnp;
+       }
+#endif
+       return 0;
+
+#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
+ out_unregister_isapnp:
+       isapnp_unregister_driver(&fcpnp_driver);
+#endif
+ out_unregister_pci:
+       pci_unregister_driver(&fcpci_driver);
+ out:
+       return retval;
+}
+
+static void __exit hisax_fcpcipnp_exit(void)
+{
+       isapnp_unregister_driver(&fcpnp_driver);
+       pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(hisax_fcpcipnp_init);
+module_exit(hisax_fcpcipnp_exit);
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.h b/drivers/isdn/hisax/hisax_fcpcipnp.h
new file mode 100644 (file)
index 0000000..e0fb0b9
--- /dev/null
@@ -0,0 +1,57 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include <linux/pci.h>
+
+#define HSCX_BUFMAX    4096
+
+enum {
+       AVM_FRITZ_PCI,
+       AVM_FRITZ_PNP,
+       AVM_FRITZ_PCIV2,
+};
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+       u_char fill __attribute__((packed));
+       u_char mode __attribute__((packed));
+       u_char xml  __attribute__((packed));
+       u_char cmd  __attribute__((packed));
+#else
+       u_char cmd  __attribute__((packed));
+       u_char xml  __attribute__((packed));
+       u_char mode __attribute__((packed));
+       u_char fill __attribute__((packed));
+#endif
+};
+
+struct fritz_bcs {
+       struct hisax_b_if b_if;
+       struct fritz_adapter *adapter;
+       int mode;
+       int channel;
+
+       union {
+               u_int ctrl;
+               struct hdlc_stat_reg sr;
+       } ctrl;
+       u_int stat;
+       int rcvidx;
+       u_char rcvbuf[HSCX_BUFMAX]; /* B-Channel receive Buffer */
+
+       int tx_cnt;                 /* B-Channel transmit counter */
+       struct sk_buff *tx_skb;     /* B-Channel transmit Buffer */
+};
+
+struct fritz_adapter {
+       int type;
+       spinlock_t hw_lock;
+       unsigned int io;
+       unsigned int irq;
+       struct isac isac;
+
+       struct fritz_bcs bcs[2];
+
+       u32  (*read_hdlc_status) (struct fritz_adapter *adapter, int nr);
+       void (*write_ctrl) (struct fritz_bcs *bcs, int which);
+};
+
diff --git a/drivers/isdn/hisax/hisax_isac.c b/drivers/isdn/hisax/hisax_isac.c
new file mode 100644 (file)
index 0000000..9a6a506
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+ * Driver for ISAC-S and ISAC-SX 
+ * ISDN Subscriber Access Controller for Terminals
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original isac.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+/* TODO:
+ * specifically handle level vs edge triggered?
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "hisax_isac.h"
+
+// debugging cruft
+
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 1;
+MODULE_PARM(debug, "i");
+
+static char *ISACVer[] = {
+  "2086/2186 V1.1", 
+  "2085 B1", 
+  "2085 B2",
+  "2085 V2.3"
+};
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("ISAC/ISAC-SX driver");
+
+#define DBG_WARN      0x0001
+#define DBG_IRQ       0x0002
+#define DBG_L1M       0x0004
+#define DBG_PR        0x0008
+#define DBG_RFIFO     0x0100
+#define DBG_RPACKET   0x0200
+#define DBG_XFIFO     0x1000
+#define DBG_XPACKET   0x2000
+
+// we need to distinguish ISAC-S and ISAC-SX
+#define TYPE_ISAC        0x00
+#define TYPE_ISACSX      0x01
+
+// registers etc.
+#define ISAC_MASK        0x20
+#define ISAC_ISTA        0x20
+#define ISAC_ISTA_EXI    0x01
+#define ISAC_ISTA_SIN    0x02
+#define ISAC_ISTA_CISQ   0x04
+#define ISAC_ISTA_XPR    0x10
+#define ISAC_ISTA_RSC    0x20
+#define ISAC_ISTA_RPF    0x40
+#define ISAC_ISTA_RME    0x80
+
+#define ISAC_STAR        0x21
+#define ISAC_CMDR        0x21
+#define ISAC_CMDR_XRES   0x01
+#define ISAC_CMDR_XME    0x02
+#define ISAC_CMDR_XTF    0x08
+#define ISAC_CMDR_RRES   0x40
+#define ISAC_CMDR_RMC    0x80
+
+#define ISAC_EXIR        0x24
+#define ISAC_EXIR_MOS    0x04
+#define ISAC_EXIR_XDU    0x40
+#define ISAC_EXIR_XMR    0x80
+
+#define ISAC_ADF2        0x39
+#define ISAC_SPCR        0x30
+#define ISAC_ADF1        0x38
+
+#define ISAC_CIR0        0x31
+#define ISAC_CIX0        0x31
+#define ISAC_CIR0_CIC0   0x02
+#define ISAC_CIR0_CIC1   0x01
+
+#define ISAC_CIR1        0x33
+#define ISAC_CIX1        0x33
+#define ISAC_STCR        0x37
+#define ISAC_MODE        0x22
+
+#define ISAC_RSTA        0x27
+#define ISAC_RSTA_RDO    0x40
+#define ISAC_RSTA_CRC    0x20
+#define ISAC_RSTA_RAB    0x10
+
+#define ISAC_RBCL 0x25
+#define ISAC_RBCH 0x2A
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+#define ISAC_MOSR 0x3a
+#define ISAC_MOCR 0x3a
+#define ISAC_MOR0 0x32
+#define ISAC_MOX0 0x32
+#define ISAC_MOR1 0x34
+#define ISAC_MOX1 0x34
+
+#define ISAC_RBCH_XAC 0x80
+
+#define ISAC_CMD_TIM    0x0
+#define ISAC_CMD_RES    0x1
+#define ISAC_CMD_SSP    0x2
+#define ISAC_CMD_SCP    0x3
+#define ISAC_CMD_AR8    0x8
+#define ISAC_CMD_AR10   0x9
+#define ISAC_CMD_ARL    0xa
+#define ISAC_CMD_DI     0xf
+
+#define ISACSX_MASK       0x60
+#define ISACSX_ISTA       0x60
+#define ISACSX_ISTA_ICD   0x01
+#define ISACSX_ISTA_CIC   0x10
+
+#define ISACSX_MASKD      0x20
+#define ISACSX_ISTAD      0x20
+#define ISACSX_ISTAD_XDU  0x04
+#define ISACSX_ISTAD_XMR  0x08
+#define ISACSX_ISTAD_XPR  0x10
+#define ISACSX_ISTAD_RFO  0x20
+#define ISACSX_ISTAD_RPF  0x40
+#define ISACSX_ISTAD_RME  0x80
+
+#define ISACSX_CMDRD      0x21
+#define ISACSX_CMDRD_XRES 0x01
+#define ISACSX_CMDRD_XME  0x02
+#define ISACSX_CMDRD_XTF  0x08
+#define ISACSX_CMDRD_RRES 0x40
+#define ISACSX_CMDRD_RMC  0x80
+
+#define ISACSX_MODED      0x22
+
+#define ISACSX_RBCLD      0x26
+
+#define ISACSX_RSTAD      0x28
+#define ISACSX_RSTAD_RAB  0x10
+#define ISACSX_RSTAD_CRC  0x20
+#define ISACSX_RSTAD_RDO  0x40
+#define ISACSX_RSTAD_VFR  0x80
+
+#define ISACSX_CIR0       0x2e
+#define ISACSX_CIR0_CIC0  0x08
+#define ISACSX_CIX0       0x2e
+
+#define ISACSX_TR_CONF0   0x30
+
+#define ISACSX_TR_CONF2   0x32
+
+static struct Fsm l1fsm;
+
+enum {
+       ST_L1_RESET,
+       ST_L1_F3_PDOWN,
+       ST_L1_F3_PUP,
+       ST_L1_F3_PEND_DEACT,
+       ST_L1_F4,
+       ST_L1_F5,
+       ST_L1_F6,
+       ST_L1_F7,
+       ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1State[] =
+{
+       "ST_L1_RESET",
+       "ST_L1_F3_PDOWN",
+       "ST_L1_F3_PUP",
+       "ST_L1_F3_PEND_DEACT",
+       "ST_L1_F4",
+       "ST_L1_F5",
+       "ST_L1_F6",
+       "ST_L1_F7",
+       "ST_L1_F8",
+};
+
+enum {
+       EV_PH_DR,           // 0000
+       EV_PH_RES,          // 0001
+       EV_PH_TMA,          // 0010
+       EV_PH_SLD,          // 0011
+       EV_PH_RSY,          // 0100
+       EV_PH_DR6,          // 0101
+       EV_PH_EI,           // 0110
+       EV_PH_PU,           // 0111
+       EV_PH_AR,           // 1000
+       EV_PH_9,            // 1001
+       EV_PH_ARL,          // 1010
+       EV_PH_CVR,          // 1011
+       EV_PH_AI8,          // 1100
+       EV_PH_AI10,         // 1101
+       EV_PH_AIL,          // 1110
+       EV_PH_DC,           // 1111
+       EV_PH_ACTIVATE_REQ,
+       EV_PH_DEACTIVATE_REQ,
+       EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+       "EV_PH_DR",           // 0000
+       "EV_PH_RES",          // 0001
+       "EV_PH_TMA",          // 0010
+       "EV_PH_SLD",          // 0011
+       "EV_PH_RSY",          // 0100
+       "EV_PH_DR6",          // 0101
+       "EV_PH_EI",           // 0110
+       "EV_PH_PU",           // 0111
+       "EV_PH_AR",           // 1000
+       "EV_PH_9",            // 1001
+       "EV_PH_ARL",          // 1010
+       "EV_PH_CVR",          // 1011
+       "EV_PH_AI8",          // 1100
+       "EV_PH_AI10",         // 1101
+       "EV_PH_AIL",          // 1110
+       "EV_PH_DC",           // 1111
+       "EV_PH_ACTIVATE_REQ",
+       "EV_PH_DEACTIVATE_REQ",
+       "EV_TIMER3",
+};
+
+static inline void D_L1L2(struct isac *isac, int pr, void *arg)
+{
+       struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if;
+
+       DBG(DBG_PR, "pr %#x", pr);
+       ifc->l1l2(ifc, pr, arg);
+}
+
+static void ph_command(struct isac *isac, unsigned int command)
+{
+       DBG(DBG_L1M, "ph_command %#x", command);
+       switch (isac->type) {
+       case TYPE_ISAC:
+               isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3);
+               break;
+       case TYPE_ISACSX:
+               isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1));
+               break;
+       }
+}
+
+// ----------------------------------------------------------------------
+
+static void l1_di(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmChangeState(fi, ST_L1_RESET);
+       ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmChangeState(fi, ST_L1_RESET);
+       D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+       ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg)
+{
+       FsmChangeState(fi, ST_L1_F3_PDOWN);
+}
+
+static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+       D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+       ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+       ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f4(struct FsmInst *fi, int event, void *arg)
+{
+       FsmChangeState(fi, ST_L1_F4);
+}
+
+static void l1_go_f5(struct FsmInst *fi, int event, void *arg)
+{
+       FsmChangeState(fi, ST_L1_F5);
+}
+
+static void l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+       FsmChangeState(fi, ST_L1_F6);
+}
+
+static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmChangeState(fi, ST_L1_F6);
+       D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmDelTimer(&isac->timer, 0);
+       FsmChangeState(fi, ST_L1_F7);
+       ph_command(isac, ISAC_CMD_AR8);
+       D_L1L2(isac, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+       FsmChangeState(fi, ST_L1_F8);
+}
+
+static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmChangeState(fi, ST_L1_F8);
+       D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_ar8(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+       ph_command(isac, ISAC_CMD_AR8);
+}
+
+static void l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+       struct isac *isac = fi->userdata;
+
+       ph_command(isac, ISAC_CMD_DI);
+       D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+// state machines according to data sheet PSB 2186 / 3186
+
+static struct FsmNode L1FnList[] __initdata =
+{
+       {ST_L1_RESET,         EV_PH_RES,            l1_di},
+       {ST_L1_RESET,         EV_PH_EI,             l1_di},
+       {ST_L1_RESET,         EV_PH_DC,             l1_go_f3pdown},
+       {ST_L1_RESET,         EV_PH_AR,             l1_go_f6},
+       {ST_L1_RESET,         EV_PH_AI8,            l1_go_f7_act_ind},
+
+       {ST_L1_F3_PDOWN,      EV_PH_RES,            l1_di},
+       {ST_L1_F3_PDOWN,      EV_PH_EI,             l1_di},
+       {ST_L1_F3_PDOWN,      EV_PH_AR,             l1_go_f6},
+       {ST_L1_F3_PDOWN,      EV_PH_RSY,            l1_go_f5},
+       {ST_L1_F3_PDOWN,      EV_PH_PU,             l1_go_f4},
+       {ST_L1_F3_PDOWN,      EV_PH_AI8,            l1_go_f7_act_ind},
+       {ST_L1_F3_PDOWN,      EV_PH_ACTIVATE_REQ,   l1_ar8},
+       {ST_L1_F3_PDOWN,      EV_TIMER3,            l1_timer3},
+       
+       {ST_L1_F3_PEND_DEACT, EV_PH_RES,            l1_di},
+       {ST_L1_F3_PEND_DEACT, EV_PH_EI,             l1_di},
+       {ST_L1_F3_PEND_DEACT, EV_PH_DC,             l1_go_f3pdown},
+       {ST_L1_F3_PEND_DEACT, EV_PH_RSY,            l1_go_f5},
+       {ST_L1_F3_PEND_DEACT, EV_PH_AR,             l1_go_f6},
+       {ST_L1_F3_PEND_DEACT, EV_PH_AI8,            l1_go_f7_act_ind},
+
+       {ST_L1_F4,            EV_PH_RES,            l1_di},
+       {ST_L1_F4,            EV_PH_EI,             l1_di},
+       {ST_L1_F4,            EV_PH_RSY,            l1_go_f5},
+       {ST_L1_F4,            EV_PH_AI8,            l1_go_f7_act_ind},
+       {ST_L1_F4,            EV_TIMER3,            l1_timer3},
+       {ST_L1_F4,            EV_PH_DC,             l1_go_f3pdown},
+
+       {ST_L1_F5,            EV_PH_RES,            l1_di},
+       {ST_L1_F5,            EV_PH_EI,             l1_di},
+       {ST_L1_F5,            EV_PH_AR,             l1_go_f6},
+       {ST_L1_F5,            EV_PH_AI8,            l1_go_f7_act_ind},
+       {ST_L1_F5,            EV_TIMER3,            l1_timer3},
+       {ST_L1_F5,            EV_PH_DR,             l1_go_f3pend},
+       {ST_L1_F5,            EV_PH_DC,             l1_go_f3pdown},
+
+       {ST_L1_F6,            EV_PH_RES,            l1_di},
+       {ST_L1_F6,            EV_PH_EI,             l1_di},
+       {ST_L1_F6,            EV_PH_RSY,            l1_go_f8},
+       {ST_L1_F6,            EV_PH_AI8,            l1_go_f7_act_ind},
+       {ST_L1_F6,            EV_PH_DR6,            l1_go_f3pend},
+       {ST_L1_F6,            EV_TIMER3,            l1_timer3},
+       {ST_L1_F6,            EV_PH_DC,             l1_go_f3pdown},
+
+       {ST_L1_F7,            EV_PH_RES,            l1_di_deact_ind},
+       {ST_L1_F7,            EV_PH_EI,             l1_di_deact_ind},
+       {ST_L1_F7,            EV_PH_AR,             l1_go_f6_deact_ind},
+       {ST_L1_F7,            EV_PH_RSY,            l1_go_f8_deact_ind},
+       {ST_L1_F7,            EV_PH_DR,             l1_go_f3pend_deact_ind},
+
+       {ST_L1_F8,            EV_PH_RES,            l1_di},
+       {ST_L1_F8,            EV_PH_EI,             l1_di},
+       {ST_L1_F8,            EV_PH_AR,             l1_go_f6},
+       {ST_L1_F8,            EV_PH_DR,             l1_go_f3pend},
+       {ST_L1_F8,            EV_PH_AI8,            l1_go_f7_act_ind},
+       {ST_L1_F8,            EV_TIMER3,            l1_timer3},
+       {ST_L1_F8,            EV_PH_DC,             l1_go_f3pdown},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       va_list args;
+       char buf[256];
+       
+       va_start(args, fmt);
+       vsprintf(buf, fmt, args);
+       DBG(DBG_L1M, "%s", buf);
+       va_end(args);
+}
+
+static void isac_version(struct isac *cs)
+{
+       int val;
+
+       val = cs->read_isac(cs, ISAC_RBCH);
+       DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]);
+}
+
+static void isac_empty_fifo(struct isac *isac, int count)
+{
+       // this also works for isacsx, since
+       // CMDR(D) register works the same
+       u_char *ptr;
+
+       DBG(DBG_IRQ, "count %d", count);
+
+       if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+               DBG(DBG_WARN, "overrun %d", isac->rcvidx + count);
+               isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+               isac->rcvidx = 0;
+               return;
+       }
+       ptr = isac->rcvbuf + isac->rcvidx;
+       isac->rcvidx += count;
+       isac->read_isac_fifo(isac, ptr, count);
+       isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+       DBG_PACKET(DBG_RFIFO, ptr, count);
+}
+
+static void isac_fill_fifo(struct isac *isac)
+{
+       // this also works for isacsx, since
+       // CMDR(D) register works the same
+
+       int count;
+       unsigned char cmd;
+       u_char *ptr;
+
+       if (!isac->tx_skb)
+               BUG();
+
+       count = isac->tx_skb->len;
+       if (count <= 0)
+               BUG();
+
+       DBG(DBG_IRQ, "count %d", count);
+
+       if (count > 0x20) {
+               count = 0x20;
+               cmd = ISAC_CMDR_XTF;
+       } else {
+               cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME;
+       }
+
+       ptr = isac->tx_skb->data;
+       skb_pull(isac->tx_skb, count);
+       isac->tx_cnt += count;
+       DBG_PACKET(DBG_XFIFO, ptr, count);
+       isac->write_isac_fifo(isac, ptr, count);
+       isac->write_isac(isac, ISAC_CMDR, cmd);
+}
+
+static void isac_retransmit(struct isac *isac)
+{
+       if (!isac->tx_skb) {
+               DBG(DBG_WARN, "no skb");
+               return;
+       }
+       skb_push(isac->tx_skb, isac->tx_cnt);
+       isac->tx_cnt = 0;
+}
+
+
+static inline void isac_cisq_interrupt(struct isac *isac)
+{
+       unsigned char val;
+
+       val = isac->read_isac(isac, ISAC_CIR0);
+       DBG(DBG_IRQ, "CIR0 %#x", val);
+       if (val & ISAC_CIR0_CIC0) {
+               DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf);
+               FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+       }
+       if (val & ISAC_CIR0_CIC1) {
+               val = isac->read_isac(isac, ISAC_CIR1);
+               DBG(DBG_WARN, "ISAC CIR1 %#x", val );
+       }
+}
+
+static inline void isac_rme_interrupt(struct isac *isac)
+{
+       unsigned char val;
+       int count;
+       struct sk_buff *skb;
+       
+       val = isac->read_isac(isac, ISAC_RSTA);
+       if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB) )
+            != ISAC_RSTA_CRC) {
+               DBG(DBG_WARN, "RSTA %#x, dropped", val);
+               isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+               goto out;
+       }
+
+       count = isac->read_isac(isac, ISAC_RBCL) & 0x1f;
+       DBG(DBG_IRQ, "RBCL %#x", count);
+       if (count == 0)
+               count = 0x20;
+
+       isac_empty_fifo(isac, count);
+       count = isac->rcvidx;
+       if (count < 1) {
+               DBG(DBG_WARN, "count %d < 1", count);
+               goto out;
+       }
+
+       skb = alloc_skb(count, GFP_ATOMIC);
+       if (!skb) {
+               DBG(DBG_WARN, "no memory, dropping\n");
+               goto out;
+       }
+       memcpy(skb_put(skb, count), isac->rcvbuf, count);
+       DBG_SKB(DBG_RPACKET, skb);
+       D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+       isac->rcvidx = 0;
+}
+
+static inline void isac_xpr_interrupt(struct isac *isac)
+{
+       if (!isac->tx_skb)
+               return;
+
+       if (isac->tx_skb->len > 0) {
+               isac_fill_fifo(isac);
+               return;
+       }
+       dev_kfree_skb_irq(isac->tx_skb);
+       isac->tx_cnt = 0;
+       isac->tx_skb = NULL;
+       D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isac_exi_interrupt(struct isac *isac)
+{
+       unsigned char val;
+
+       val = isac->read_isac(isac, ISAC_EXIR);
+       DBG(2, "EXIR %#x", val);
+
+       if (val & ISAC_EXIR_XMR) {
+               DBG(DBG_WARN, "ISAC XMR");
+               isac_retransmit(isac);
+       }
+       if (val & ISAC_EXIR_XDU) {
+               DBG(DBG_WARN, "ISAC XDU");
+               isac_retransmit(isac);
+       }
+       if (val & ISAC_EXIR_MOS) {  /* MOS */
+               DBG(DBG_WARN, "MOS");
+               val = isac->read_isac(isac, ISAC_MOSR);
+               DBG(2, "ISAC MOSR %#x", val);
+       }
+}
+
+void isac_interrupt(struct isac *isac)
+{
+       unsigned char val;
+
+       val = isac->read_isac(isac, ISAC_ISTA);
+       DBG(DBG_IRQ, "ISTA %#x", val);
+
+       if (val & ISAC_ISTA_EXI) {
+               DBG(DBG_IRQ, "EXI");
+               isac_exi_interrupt(isac);
+       }
+       if (val & ISAC_ISTA_XPR) {
+               DBG(DBG_IRQ, "XPR");
+               isac_xpr_interrupt(isac);
+       }
+       if (val & ISAC_ISTA_RME) {
+               DBG(DBG_IRQ, "RME");
+               isac_rme_interrupt(isac);
+       }
+       if (val & ISAC_ISTA_RPF) {
+               DBG(DBG_IRQ, "RPF");
+               isac_empty_fifo(isac, 0x20);
+       }
+       if (val & ISAC_ISTA_CISQ) {
+               DBG(DBG_IRQ, "CISQ");
+               isac_cisq_interrupt(isac);
+       }
+       if (val & ISAC_ISTA_RSC) {
+               DBG(DBG_WARN, "RSC");
+       }
+       if (val & ISAC_ISTA_SIN) {
+               DBG(DBG_WARN, "SIN");
+       }
+       isac->write_isac(isac, ISAC_MASK, 0xff);
+       isac->write_isac(isac, ISAC_MASK, 0x00);
+}
+
+// ======================================================================
+
+static inline void isacsx_cic_interrupt(struct isac *isac)
+{
+       unsigned char val;
+
+       val = isac->read_isac(isac, ISACSX_CIR0);
+       DBG(DBG_IRQ, "CIR0 %#x", val);
+       if (val & ISACSX_CIR0_CIC0) {
+               DBG(DBG_IRQ, "CODR0 %#x", val >> 4);
+               FsmEvent(&isac->l1m, val >> 4, NULL);
+       }
+}
+
+static inline void isacsx_rme_interrupt(struct isac *isac)
+{
+       int count;
+       struct sk_buff *skb;
+       unsigned char val;
+
+       val = isac->read_isac(isac, ISACSX_RSTAD);
+       if ((val & (ISACSX_RSTAD_VFR | 
+                   ISACSX_RSTAD_RDO | 
+                   ISACSX_RSTAD_CRC | 
+                   ISACSX_RSTAD_RAB)) 
+           != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) {
+               DBG(DBG_WARN, "RSTAD %#x, dropped", val);
+               isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+               goto out;
+       }
+
+       count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f;
+       DBG(DBG_IRQ, "RBCLD %#x", count);
+       if (count == 0)
+               count = 0x20;
+
+       isac_empty_fifo(isac, count);
+       // strip trailing status byte
+       count = isac->rcvidx - 1;
+       if (count < 1) {
+               DBG(DBG_WARN, "count %d < 1", count);
+               goto out;
+       }
+
+       skb = dev_alloc_skb(count);
+       if (!skb) {
+               DBG(DBG_WARN, "no memory, dropping");
+               goto out;
+       }
+       memcpy(skb_put(skb, count), isac->rcvbuf, count);
+       DBG_SKB(DBG_RPACKET, skb);
+       D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+       isac->rcvidx = 0;
+}
+
+static inline void isacsx_xpr_interrupt(struct isac *isac)
+{
+       if (!isac->tx_skb)
+               return;
+
+       if (isac->tx_skb->len > 0) {
+               isac_fill_fifo(isac);
+               return;
+       }
+       dev_kfree_skb_irq(isac->tx_skb);
+       isac->tx_skb = NULL;
+       isac->tx_cnt = 0;
+       D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isacsx_icd_interrupt(struct isac *isac)
+{
+       unsigned char val;
+
+       val = isac->read_isac(isac, ISACSX_ISTAD);
+       DBG(DBG_IRQ, "ISTAD %#x", val);
+       if (val & ISACSX_ISTAD_XDU) {
+               DBG(DBG_WARN, "ISTAD XDU");
+               isac_retransmit(isac);
+       }
+       if (val & ISACSX_ISTAD_XMR) {
+               DBG(DBG_WARN, "ISTAD XMR");
+               isac_retransmit(isac);
+       }
+       if (val & ISACSX_ISTAD_XPR) {
+               DBG(DBG_IRQ, "ISTAD XPR");
+               isacsx_xpr_interrupt(isac);
+       }
+       if (val & ISACSX_ISTAD_RFO) {
+               DBG(DBG_WARN, "ISTAD RFO");
+               isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+       }
+       if (val & ISACSX_ISTAD_RME) {
+               DBG(DBG_IRQ, "ISTAD RME");
+               isacsx_rme_interrupt(isac);
+       }
+       if (val & ISACSX_ISTAD_RPF) {
+               DBG(DBG_IRQ, "ISTAD RPF");
+               isac_empty_fifo(isac, 0x20);
+       }
+}
+
+void isacsx_interrupt(struct isac *isac)
+{
+       unsigned char val;
+
+       val = isac->read_isac(isac, ISACSX_ISTA);
+       DBG(DBG_IRQ, "ISTA %#x", val);
+
+       if (val & ISACSX_ISTA_ICD)
+               isacsx_icd_interrupt(isac);
+       if (val & ISACSX_ISTA_CIC)
+               isacsx_cic_interrupt(isac);
+}
+
+void isac_init(struct isac *isac)
+{
+       isac->tx_skb = NULL;
+       isac->l1m.fsm = &l1fsm;
+       isac->l1m.state = ST_L1_RESET;
+#ifdef CONFIG_HISAX_DEBUG
+       isac->l1m.debug = 1;
+#else
+       isac->l1m.debug = 0;
+#endif
+       isac->l1m.userdata = isac;
+       isac->l1m.printdebug = l1m_debug;
+       FsmInitTimer(&isac->l1m, &isac->timer);
+}
+
+void isac_setup(struct isac *isac)
+{
+       int val, eval;
+
+       isac->type = TYPE_ISAC;
+       isac_version(isac);
+
+       ph_command(isac, ISAC_CMD_RES);
+
+       isac->write_isac(isac, ISAC_MASK, 0xff);
+       isac->mocr = 0xaa;
+       if (test_bit(ISAC_IOM1, &isac->flags)) {
+               /* IOM 1 Mode */
+               isac->write_isac(isac, ISAC_ADF2, 0x0);
+               isac->write_isac(isac, ISAC_SPCR, 0xa);
+               isac->write_isac(isac, ISAC_ADF1, 0x2);
+               isac->write_isac(isac, ISAC_STCR, 0x70);
+               isac->write_isac(isac, ISAC_MODE, 0xc9);
+       } else {
+               /* IOM 2 Mode */
+               if (!isac->adf2)
+                       isac->adf2 = 0x80;
+               isac->write_isac(isac, ISAC_ADF2, isac->adf2);
+               isac->write_isac(isac, ISAC_SQXR, 0x2f);
+               isac->write_isac(isac, ISAC_SPCR, 0x00);
+               isac->write_isac(isac, ISAC_STCR, 0x70);
+               isac->write_isac(isac, ISAC_MODE, 0xc9);
+               isac->write_isac(isac, ISAC_TIMR, 0x00);
+               isac->write_isac(isac, ISAC_ADF1, 0x00);
+       }
+       val = isac->read_isac(isac, ISAC_STAR);
+       DBG(2, "ISAC STAR %x", val);
+       val = isac->read_isac(isac, ISAC_MODE);
+       DBG(2, "ISAC MODE %x", val);
+       val = isac->read_isac(isac, ISAC_ADF2);
+       DBG(2, "ISAC ADF2 %x", val);
+       val = isac->read_isac(isac, ISAC_ISTA);
+       DBG(2, "ISAC ISTA %x", val);
+       if (val & 0x01) {
+               eval = isac->read_isac(isac, ISAC_EXIR);
+               DBG(2, "ISAC EXIR %x", eval);
+       }
+       val = isac->read_isac(isac, ISAC_CIR0);
+       DBG(2, "ISAC CIR0 %x", val);
+       FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+
+       isac->write_isac(isac, ISAC_MASK, 0x0);
+       // RESET Receiver and Transmitter
+       isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES);
+}
+
+void isacsx_setup(struct isac *isac)
+{
+       isac->type = TYPE_ISACSX;
+       // clear LDD
+       isac->write_isac(isac, ISACSX_TR_CONF0, 0x00);
+       // enable transmitter
+       isac->write_isac(isac, ISACSX_TR_CONF2, 0x00);
+       // transparent mode 0, RAC, stop/go
+       isac->write_isac(isac, ISACSX_MODED,    0xc9);
+       // all HDLC IRQ unmasked
+       isac->write_isac(isac, ISACSX_MASKD,    0x03);
+       // unmask ICD, CID IRQs
+       isac->write_isac(isac, ISACSX_MASK,            
+                        ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
+}
+
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+       struct isac *isac = hisax_d_if->priv;
+       struct sk_buff *skb = arg;
+
+       DBG(DBG_PR, "pr %#x", pr);
+
+       switch (pr) {
+       case PH_ACTIVATE | REQUEST:
+               FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL);
+               break;
+       case PH_DEACTIVATE | REQUEST:
+               FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+               break;
+       case PH_DATA | REQUEST:
+               DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len);
+               DBG_SKB(DBG_XPACKET, skb);
+               if (isac->l1m.state != ST_L1_F7) {
+                       DBG(1, "L1 wrong state %d\n", isac->l1m.state);
+                       dev_kfree_skb(skb);
+                       break;
+               }
+               if (isac->tx_skb)
+                       BUG();
+
+               isac->tx_skb = skb;
+               isac_fill_fifo(isac);
+               break;
+       }
+}
+
+static int __init hisax_isac_init(void)
+{
+       printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n");
+
+       l1fsm.state_count = L1_STATE_COUNT;
+       l1fsm.event_count = L1_EVENT_COUNT;
+       l1fsm.strState = strL1State;
+       l1fsm.strEvent = strL1Event;
+       return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+}
+
+static void __exit hisax_isac_exit(void)
+{
+       FsmFree(&l1fsm);
+}
+
+EXPORT_SYMBOL(isac_init);
+EXPORT_SYMBOL(isac_d_l2l1);
+
+EXPORT_SYMBOL(isacsx_setup);
+EXPORT_SYMBOL(isacsx_interrupt);
+
+EXPORT_SYMBOL(isac_setup);
+EXPORT_SYMBOL(isac_interrupt);
+
+module_init(hisax_isac_init);
+module_exit(hisax_isac_exit);
diff --git a/drivers/isdn/hisax/hisax_isac.h b/drivers/isdn/hisax/hisax_isac.h
new file mode 100644 (file)
index 0000000..6b8d2df
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef __HISAX_ISAC_H__
+#define __HISAX_ISAC_H__
+
+#include <linux/kernel.h>
+#include "fsm.h"
+#include "hisax_if.h"
+
+#define TIMER3_VALUE 7000
+#define MAX_DFRAME_LEN_L1 300
+
+#define ISAC_IOM1      0
+
+struct isac {
+       void *priv;
+
+       u_long flags;
+       struct hisax_d_if hisax_d_if;
+       struct FsmInst l1m;
+       struct FsmTimer timer;
+       u_char mocr;
+       u_char adf2;
+       int    type;
+
+       u_char rcvbuf[MAX_DFRAME_LEN_L1];
+       int rcvidx;
+
+       struct sk_buff *tx_skb;
+       int tx_cnt;
+
+       u_char (*read_isac)      (struct isac *, u_char);
+       void   (*write_isac)     (struct isac *, u_char, u_char);
+       void   (*read_isac_fifo) (struct isac *, u_char *, int);
+       void   (*write_isac_fifo)(struct isac *, u_char *, int);
+};
+
+void isac_init(struct isac *isac);
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+void isac_setup(struct isac *isac);
+void isac_interrupt(struct isac *isac);
+
+void isacsx_setup(struct isac *isac);
+void isacsx_interrupt(struct isac *isac);
+
+#endif
index 6f0a792..c40dd3a 100644 (file)
@@ -119,22 +119,21 @@ static int linear_stop (mddev_t *mddev)
        return 0;
 }
 
-static int linear_make_request (mddev_t *mddev,
-                       int rw, struct buffer_head * bh)
+static int linear_make_request (mddev_t *mddev, int rw, struct bio *bio)
 {
         linear_conf_t *conf = mddev_to_conf(mddev);
         struct linear_hash *hash;
         dev_info_t *tmp_dev;
         long block;
 
-       block = bh->b_rsector >> 1;
+       block = bio->bi_sector >> 1;
        hash = conf->hash_table + (block / conf->smallest->size);
   
        if (block >= (hash->dev0->size + hash->dev0->offset)) {
                if (!hash->dev1) {
                        printk ("linear_make_request : hash->dev1==NULL for block %ld\n",
                                                block);
-                       buffer_IO_error(bh);
+                       bio_io_error(bio);
                        return 0;
                }
                tmp_dev = hash->dev1;
@@ -144,11 +143,11 @@ static int linear_make_request (mddev_t *mddev,
        if (block >= (tmp_dev->size + tmp_dev->offset)
                                || block < tmp_dev->offset) {
                printk ("linear_make_request: Block %ld out of bounds on dev %s size %ld offset %ld\n", block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset);
-               buffer_IO_error(bh);
+               bio_io_error(bio);
                return 0;
        }
-       bh->b_rdev = tmp_dev->dev;
-       bh->b_rsector = bh->b_rsector - (tmp_dev->offset << 1);
+       bio->bi_dev = tmp_dev->dev;
+       bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1);
 
        return 1;
 }
index fed885b..8b21f61 100644 (file)
@@ -223,8 +223,7 @@ static int raid0_stop (mddev_t *mddev)
  * Of course, those facts may not be valid anymore (and surely won't...)
  * Hey guys, there's some work out there ;-)
  */
-static int raid0_make_request (mddev_t *mddev,
-                              int rw, struct buffer_head * bh)
+static int raid0_make_request (mddev_t *mddev, int rw, struct bio *bio)
 {
        unsigned int sect_in_chunk, chunksize_bits,  chunk_size;
        raid0_conf_t *conf = mddev_to_conf(mddev);
@@ -235,11 +234,11 @@ static int raid0_make_request (mddev_t *mddev,
 
        chunk_size = mddev->param.chunk_size >> 10;
        chunksize_bits = ffz(~chunk_size);
-       block = bh->b_rsector >> 1;
+       block = bio->bi_sector >> 1;
        hash = conf->hash_table + block / conf->smallest->size;
 
        /* Sanity check */
-       if (chunk_size < (block % chunk_size) + (bh->b_size >> 10))
+       if (chunk_size < (block % chunk_size) + (bio->bi_size >> 10))
                goto bad_map;
  
        if (!hash)
@@ -255,7 +254,7 @@ static int raid0_make_request (mddev_t *mddev,
        } else
                zone = hash->zone0;
     
-       sect_in_chunk = bh->b_rsector & ((chunk_size<<1) -1);
+       sect_in_chunk = bio->bi_sector & ((chunk_size<<1) -1);
        chunk = (block - zone->zone_offset) / (zone->nb_dev << chunksize_bits);
        tmp_dev = zone->dev[(block >> chunksize_bits) % zone->nb_dev];
        rsect = (((chunk << chunksize_bits) + zone->dev_offset)<<1)
@@ -265,8 +264,8 @@ static int raid0_make_request (mddev_t *mddev,
         * The new BH_Lock semantics in ll_rw_blk.c guarantee that this
         * is the only IO operation happening on this bh.
         */
-       bh->b_rdev = tmp_dev->dev;
-       bh->b_rsector = rsect;
+       bio->bi_dev = tmp_dev->dev;
+       bio->bi_sector = rsect;
 
        /*
         * Let the main block layer submit the IO and resolve recursion:
@@ -274,7 +273,7 @@ static int raid0_make_request (mddev_t *mddev,
        return 1;
 
 bad_map:
-       printk ("raid0_make_request bug: can't convert block across chunks or bigger than %dk %ld %d\n", chunk_size, bh->b_rsector, bh->b_size >> 10);
+       printk ("raid0_make_request bug: can't convert block across chunks or bigger than %dk %ld %d\n", chunk_size, bio->bi_sector, bio->bi_size >> 10);
        goto outerr;
 bad_hash:
        printk("raid0_make_request bug: hash==NULL for block %ld\n", block);
@@ -285,7 +284,7 @@ bad_zone0:
 bad_zone1:
        printk ("raid0_make_request bug: hash->zone1==NULL for block %ld\n", block);
  outerr:
-       buffer_IO_error(bh);
+       bio_io_error(bio);
        return 0;
 }
                           
index 0627bd8..e46fe52 100644 (file)
@@ -2,7 +2,7 @@
  *
  *     vlsi_ir.c:      VLSI82C147 PCI IrDA controller driver for Linux
  *
- *     Version:        0.3, Sep 30, 2001
+ *     Version:        0.3a, Nov 10, 2001
  *
  *     Copyright (c) 2001 Martin Diehl
  *
@@ -490,7 +490,7 @@ static int vlsi_set_baud(struct net_device *ndev)
        if (mode == IFF_FIR)
                config ^= IRENABLE_FIR_ON;
        else if (mode == IFF_MIR)
-               config ^= (IRENABLE_FIR_ON|IRENABLE_CRC16_ON);
+               config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON);
        else
                config ^= IRENABLE_SIR_ON;
 
@@ -877,6 +877,7 @@ static int vlsi_open(struct net_device *ndev)
        idev->irlap = irlap_open(ndev,&idev->qos,hwname);
 
        netif_start_queue(ndev);
+       outw(0, ndev->base_addr+VLSI_PIO_PROMPT);       /* kick hw state machine */
 
        printk(KERN_INFO "%s: device %s operational using (%d,%d) tx,rx-ring\n",
                __FUNCTION__, ndev->name, ringsize[0], ringsize[1]);
@@ -1200,7 +1201,6 @@ vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        int                     alloc_size;
 
 
-       vlsi_reg_debug(0x3000, "vlsi initial state");
        if (pci_enable_device(pdev))
                goto out;
 
index fee933c..5f91a87 100644 (file)
@@ -708,16 +708,25 @@ static inline struct bio *idescsi_dma_bio(ide_drive_t *drive, idescsi_pc_t *pc)
                printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10);
 #endif /* IDESCSI_DEBUG_LOG */
                while (segments--) {
-                       bh->bi_io_vec[0].bv_page = sg->page;
+                       struct page *page = sg->page;
+                       int offset = sg->offset;
+
+                       if (!page) {
+                               BUG_ON(!sg->address);
+                               page = virt_to_page(sg->address);
+                               offset = (unsigned long) sg->address & ~PAGE_MASK;
+                       }
+                               
+                       bh->bi_io_vec[0].bv_page = page;
                        bh->bi_io_vec[0].bv_len = sg->length;
-                       bh->bi_io_vec[0].bv_offset = sg->offset;
+                       bh->bi_io_vec[0].bv_offset = offset;
                        bh->bi_size = sg->length;
                        bh = bh->bi_next;
                        /*
                         * just until scsi_merge is fixed up...
                         */
-                       BUG_ON(PageHighMem(sg->page));
-                       sg->address = page_address(sg->page) + sg->offset;
+                       BUG_ON(PageHighMem(page));
+                       sg->address = page_address(page) + offset;
                        sg++;
                }
        } else {
index c8f98f5..efe6d60 100644 (file)
@@ -2042,10 +2042,6 @@ static int isp2x00_init(struct Scsi_Host *sh)
        }
 
        pci_set_master(pdev);
-       if (!(command & PCI_COMMAND_MASTER)) {
-               printk("qlogicfc%d : bus mastering is disabled\n", hostdata->host_id);
-               return 1;
-       }
        if (revision != ISP2100_REV_ID1 && revision != ISP2100_REV_ID3 && revision != ISP2200_REV_ID5)
                printk("qlogicfc%d : new isp2x00 revision ID (%d)\n", hostdata->host_id,  revision);
 
index 0758401..506f7d9 100644 (file)
@@ -741,11 +741,13 @@ void scsi_wait_req (Scsi_Request * SRpnt, const void *cmnd ,
                  int timeout, int retries)
 {
        DECLARE_COMPLETION(wait);
+       request_queue_t *q = &SRpnt->sr_device->request_queue;
        
        SRpnt->sr_request.waiting = &wait;
        SRpnt->sr_request.rq_status = RQ_SCSI_BUSY;
        scsi_do_req (SRpnt, (void *) cmnd,
                buffer, bufflen, scsi_wait_done, timeout, retries);
+       generic_unplug_device(q);
        wait_for_completion(&wait);
        SRpnt->sr_request.waiting = NULL;
        if( SRpnt->sr_command != NULL )
index 401f84a..d764ea2 100644 (file)
@@ -582,7 +582,7 @@ void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
         */
        if (good_sectors > 0) {
                SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d sectors done.\n",
-                                             req->nr_sectors good_sectors));
+                                             req->nr_sectors, good_sectors));
                SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n ", SCpnt->use_sg));
 
                req->errors = 0;
index 8aeee78..ec698d9 100644 (file)
@@ -330,12 +330,11 @@ shmiq_qcntl_mmap (struct file *file, struct vm_area_struct *vma)
 
        size  = vma->vm_end - vma->vm_start;
        start = vma->vm_start; 
-       spin_lock( &shmiqs [minor].shmiq_lock );
-       mem = (unsigned long) shmiqs [minor].shmiq_vaddr =  vmalloc_uncached (size);
-       if (!mem) {
-               spin_unlock( &shmiqs [minor].shmiq_lock );
+       mem = vmalloc_uncached (size);
+       if (!mem)
                return -EINVAL;
-       }
+       spin_lock( &shmiqs [minor].shmiq_lock );
+       hmiqs [minor].shmiq_vaddr = mem;
 
        /* Prevent the swapper from considering these pages for swap and touching them */
        vma->vm_flags    |= (VM_SHM  | VM_LOCKED | VM_IO);
@@ -344,12 +343,12 @@ shmiq_qcntl_mmap (struct file *file, struct vm_area_struct *vma)
        /* Uncache the pages */
        vma->vm_page_prot = PAGE_USERIO;
 
-       error = vmap_page_range (vma->vm_start, size, mem);
 
        shmiqs [minor].tail = 0;
        /* Init the shared memory input queue */
-       memset (shmiqs [minor].shmiq_vaddr, 0, size);
        spin_unlock( &shmiqs [minor].shmiq_lock );
+       memset (shmiqs [minor].shmiq_vaddr, 0, size);
+       error = vmap_page_range (vma->vm_start, size, mem);
        return error;
 }
 
@@ -435,8 +434,10 @@ shmiq_qcntl_close (struct inode *inode, struct file *filp)
                return -EINVAL;
 
        spin_lock( &shmiqs [minor].shmiq_lock );
-       if (shmiqs [minor].opened == 0)
+       if (shmiqs [minor].opened == 0) {
+               spin_unlock( &shmiqs [minor].shmiq_lock );
                return -EINVAL;
+       }
 
        shmiq_qcntl_fasync (-1, filp, 0);
        shmiqs [minor].opened      = 0;
index 56a7ebe..23cf361 100644 (file)
@@ -24,6 +24,7 @@ endif
 
 subdir-$(CONFIG_PROC_FS)       += proc
 subdir-y                       += partitions
+subdir-y                       += driverfs
 
 # Do not add any filesystems before this line
 subdir-$(CONFIG_EXT3_FS)       += ext3    # Before ext2 so root fs can be ext3
index 6a1c961..171a747 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -588,7 +588,6 @@ next_chunk:
        bio->bi_sector = sector;
        bio->bi_dev = dev;
        bio->bi_idx = 0;
-       bio->bi_flags |= 1 << BIO_PREBUILT;
        bio->bi_end_io = bio_end_io_kio;
        bio->bi_private = kio;
 
@@ -619,9 +618,13 @@ next_chunk:
                sector += nbytes >> 9;
                size -= nbytes;
                total_nr_pages--;
+               kio->offset += nbytes;
        }
 
 queue_io:
+       if (bio->bi_vcnt > 1)
+               bio->bi_flags |= 1 << BIO_PREBUILT;
+
        submit_bio(rw, bio);
 
        if (total_nr_pages)
index a01fd88..de4cb8a 100644 (file)
@@ -234,7 +234,7 @@ static struct vfsmount *bd_mnt;
 #define HASH_SIZE      (1UL << HASH_BITS)
 #define HASH_MASK      (HASH_SIZE-1)
 static struct list_head bdev_hashtable[HASH_SIZE];
-static spinlock_t bdev_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t bdev_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 static kmem_cache_t * bdev_cachep;
 
 #define alloc_bdev() \
index 2c8df73..5ede800 100644 (file)
@@ -74,7 +74,7 @@ static struct buffer_head **hash_table;
 static rwlock_t hash_table_lock = RW_LOCK_UNLOCKED;
 
 static struct buffer_head *lru_list[NR_LIST];
-static spinlock_t lru_list_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t lru_list_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 static int nr_buffers_type[NR_LIST];
 static unsigned long size_buffers_type[NR_LIST];
 
@@ -794,9 +794,10 @@ still_busy:
        return;
 }
 
-inline void set_buffer_async_io(struct buffer_head *bh) {
-    bh->b_end_io = end_buffer_io_async ;
-    mark_buffer_async(bh, 1);
+inline void set_buffer_async_io(struct buffer_head *bh)
+{
+       bh->b_end_io = end_buffer_io_async;
+       mark_buffer_async(bh, 1);
 }
 
 /*
@@ -1036,7 +1037,7 @@ static int balance_dirty_state(void)
 {
        unsigned long dirty, tot, hard_dirty_limit, soft_dirty_limit;
 
-       dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT;
+       dirty = (size_buffers_type[BUF_DIRTY] + size_buffers_type[BUF_LOCKED]) >> PAGE_SHIFT;
        tot = nr_free_buffer_pages();
 
        dirty *= 100;
index 3827163..7582c9d 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/locks.h>
 #include <linux/blkdev.h>
 #include <linux/cramfs_fs.h>
+#include <asm/semaphore.h>
 
 #include <asm/uaccess.h>
 
@@ -33,6 +34,9 @@ static struct inode_operations cramfs_dir_inode_operations;
 static struct file_operations cramfs_directory_operations;
 static struct address_space_operations cramfs_aops;
 
+static DECLARE_MUTEX(read_mutex);
+
+
 /* These two macros may change in future, to provide better st_ino
    semantics. */
 #define CRAMINO(x)     ((x)->offset?(x)->offset<<2:1)
@@ -199,8 +203,10 @@ static struct super_block * cramfs_read_super(struct super_block *sb, void *data
        for (i = 0; i < READ_BUFFERS; i++)
                buffer_blocknr[i] = -1;
 
+       down(&read_mutex);
        /* Read the first block and get the superblock from it */
        memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super));
+       up(&read_mutex);
 
        /* Do sanity checks on the superblock */
        if (super.magic != CRAMFS_MAGIC) {
@@ -291,7 +297,9 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                char *name;
                int namelen, error;
 
+               down(&read_mutex);
                de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+256);
+               up(&read_mutex);
                name = (char *)(de+1);
 
                /*
@@ -332,7 +340,9 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry)
                char *name;
                int namelen, retval;
 
+               down(&read_mutex);
                de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+256);
+               up(&read_mutex);
                name = (char *)(de+1);
 
                /* Try to take advantage of sorted directories */
@@ -384,18 +394,22 @@ static int cramfs_readpage(struct file *file, struct page * page)
                u32 start_offset, compr_len;
 
                start_offset = OFFSET(inode) + maxblock*4;
+               down(&read_mutex);
                if (page->index)
                        start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 4);
-               compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4)
-                            - start_offset);
+               compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - start_offset);
+               up(&read_mutex);
                pgdata = kmap(page);
                if (compr_len == 0)
                        ; /* hole */
-               else
+               else {
+                       down(&read_mutex);
                        bytes_filled = cramfs_uncompress_block(pgdata,
                                 PAGE_CACHE_SIZE,
                                 cramfs_read(sb, start_offset, compr_len),
                                 compr_len);
+                       up(&read_mutex);
+               }
        } else
                pgdata = kmap(page);
        memset(pgdata + bytes_filled, 0, PAGE_CACHE_SIZE - bytes_filled);
index 313b4fe..f049487 100644 (file)
@@ -29,7 +29,7 @@
 #define DCACHE_PARANOIA 1
 /* #define DCACHE_DEBUG 1 */
 
-spinlock_t dcache_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t dcache_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
 /* Right now the dcache depends on the kernel lock */
 #define check_lock()   if (!kernel_locked()) BUG()
index d93589c..2eeb1b7 100644 (file)
@@ -3358,6 +3358,9 @@ static int __init init_devfs_fs (void)
     printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
 #endif
     printk ("%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
+    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
+                                         sizeof (struct devfsd_buf_entry),
+                                         0, 0, NULL, NULL);
     err = register_filesystem (&devfs_fs_type);
     if (!err)
     {
@@ -3372,9 +3375,6 @@ void __init mount_devfs_fs (void)
 {
     int err;
 
-    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
-                                         sizeof (struct devfsd_buf_entry),
-                                         0, 0, NULL, NULL);
     if ( !(boot_options & OPTION_MOUNT) ) return;
     err = do_mount ("none", "/dev", "devfs", 0, "");
     if (err == 0) printk ("Mounted devfs on /dev\n");
diff --git a/fs/driverfs/Makefile b/fs/driverfs/Makefile
new file mode 100644 (file)
index 0000000..1fd6630
--- /dev/null
@@ -0,0 +1,8 @@
+O_TARGET := driverfs.o
+
+export-objs := inode.o
+
+obj-y := inode.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/driverfs/inode.c b/fs/driverfs/inode.c
new file mode 100644 (file)
index 0000000..1a37b22
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+ * driverfs.c - The device driver file system
+ *
+ * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
+ *
+ *  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.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This is a simple, ram-based filesystem, which allows kernel
+ * callbacks for read/write of files.
+ *
+ * Please see Documentation/filesystems/driverfs.txt for more information.
+ */
+
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/driverfs_fs.h>
+
+#include <asm/uaccess.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define DBG(x...) printk(x)
+#else
+# define DBG(x...)
+#endif
+
+/* Random magic number */
+#define DRIVERFS_MAGIC 0x42454552
+
+static struct super_operations driverfs_ops;
+static struct address_space_operations driverfs_aops;
+static struct file_operations driverfs_dir_operations;
+static struct file_operations driverfs_file_operations;
+static struct inode_operations driverfs_dir_inode_operations;
+static struct dentry_operations driverfs_dentry_dir_ops;
+static struct dentry_operations driverfs_dentry_file_ops;
+
+
+static struct vfsmount *driverfs_mount;
+static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED;
+static int mount_count = 0;
+
+static int driverfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+       buf->f_type = DRIVERFS_MAGIC;
+       buf->f_bsize = PAGE_CACHE_SIZE;
+       buf->f_namelen = 255;
+       return 0;
+}
+
+static struct dentry *driverfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+       d_add(dentry, NULL);
+       return NULL;
+}
+
+struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev)
+{
+       struct inode *inode = new_inode(sb);
+
+       if (inode) {
+               inode->i_mode = mode;
+               inode->i_uid = current->fsuid;
+               inode->i_gid = current->fsgid;
+               inode->i_blksize = PAGE_CACHE_SIZE;
+               inode->i_blocks = 0;
+               inode->i_rdev = NODEV;
+               inode->i_mapping->a_ops = &driverfs_aops;
+               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               switch (mode & S_IFMT) {
+               default:
+                       init_special_inode(inode, mode, dev);
+                       break;
+               case S_IFREG:
+                       inode->i_fop = &driverfs_file_operations;
+                       break;
+               case S_IFDIR:
+                       inode->i_op = &driverfs_dir_inode_operations;
+                       inode->i_fop = &driverfs_dir_operations;
+                       break;
+               }
+       }
+       return inode;
+}
+
+static int driverfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
+{
+       struct inode *inode = driverfs_get_inode(dir->i_sb, mode, dev);
+       int error = -ENOSPC;
+
+       if (inode) {
+               d_instantiate(dentry, inode);
+               dget(dentry);
+               error = 0;
+       }
+       return error;
+}
+
+static int driverfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       dentry->d_op = &driverfs_dentry_dir_ops;
+       return driverfs_mknod(dir, dentry, mode | S_IFDIR, 0);
+}
+
+static int driverfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+       dentry->d_op = &driverfs_dentry_file_ops;
+       return driverfs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+static int
+driverfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+{
+       struct inode *inode = old_dentry->d_inode;
+
+       if(S_ISDIR(inode->i_mode))
+               return -EPERM;
+
+       inode->i_nlink++;
+       atomic_inc(&inode->i_count);
+       dget(dentry);
+       d_instantiate(dentry, inode);
+       return 0;
+}
+
+static inline int driverfs_positive(struct dentry *dentry)
+{
+       return (dentry->d_inode && !d_unhashed(dentry));
+}
+
+static int driverfs_empty(struct dentry *dentry)
+{
+       struct list_head *list;
+
+       spin_lock(&dcache_lock);
+
+       list_for_each(list, &dentry->d_subdirs) {
+               struct dentry *de = list_entry(list, struct dentry, d_child);
+               if (driverfs_positive(de)) {
+                       spin_unlock(&dcache_lock);
+                       return 0;
+               }
+       }
+
+       spin_unlock(&dcache_lock);
+       return 1;
+}
+
+static int driverfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+       int error = -ENOTEMPTY;
+
+       if (driverfs_empty(dentry)) {
+               struct inode *inode = dentry->d_inode;
+
+               inode->i_nlink--;
+               dput(dentry);
+               error = 0;
+       }
+       return error;
+}
+
+static int
+driverfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+                struct inode *new_dir, struct dentry *new_dentry)
+{
+       int error = -ENOTEMPTY;
+
+       if (driverfs_empty(new_dentry)) {
+               struct inode *inode = new_dentry->d_inode;
+               if (inode) {
+                       inode->i_nlink--;
+                       dput(new_dentry);
+               }
+               error = 0;
+       }
+       return error;
+}
+
+static int driverfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+       int error;
+
+       error = driverfs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
+       if (!error) {
+               int l = strlen(symname) + 1;
+               struct inode *inode = dentry->d_inode;
+               error = block_symlink(inode,symname,l);
+       }
+       return error;
+}
+
+#define driverfs_rmdir driverfs_unlink
+
+/**
+ * driverfs_read_file - "read" data from a file.
+ * @file:      file pointer
+ * @buf:       buffer to fill
+ * @count:     number of bytes to read
+ * @ppos:      starting offset in file
+ *
+ * Userspace wants data from a file. It is up to the creator of the file to
+ * provide that data.
+ * There is a struct driver_file_entry embedded in file->private_data. We
+ * obtain that and check if the read callback is implemented. If so, we call
+ * it, passing the data field of the file entry.
+ * Said callback is responsible for filling the buffer and returning the number
+ * of bytes it put in it. We update @ppos correctly.
+ */
+static ssize_t
+driverfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+       struct driver_file_entry * entry;
+       unsigned char *page;
+       ssize_t retval = 0;
+
+       entry = (struct driver_file_entry *)file->private_data;
+       if (!entry) {
+               DBG("%s: file entry is NULL\n",__FUNCTION__);
+               return -ENOENT;
+       }
+
+       if (!entry->ops || !entry->ops->read) {
+               DBG("%s: no read callback\n",__FUNCTION__);
+               return -ENOENT;
+       }
+
+       page = (unsigned char*)__get_free_page(GFP_KERNEL);
+       if (!page) {
+               retval = -ENOMEM;
+               goto done;
+       }
+
+       while (count > 0) {
+               ssize_t len;
+
+               len = entry->ops->read(page,count,*ppos,entry->data);
+
+               if (len <= 0) {
+                       if (len < 0)
+                               retval = len;
+                       break;
+               }
+
+               if (copy_to_user(buf,page,len)) {
+                       retval = -EFAULT;
+                       break;
+               }
+
+               *ppos += len;
+               count -= len;
+               buf += len;
+               retval += len;
+       }
+       free_page((unsigned long)page);
+
+ done:
+       return retval;
+}
+
+/**
+ * driverfs_write_file - "write" to a file
+ * @file:      file pointer
+ * @buf:       data to write
+ * @count:     number of bytes
+ * @ppos:      starting offset
+ *
+ * Similarly to driverfs_read_file, we act essentially as a bit pipe.
+ * We check for a "write" callback in file->private_data, and pass
+ * @buffer, @count, @ppos, and the file entry's data to the callback.
+ * The number of bytes written is returned, and we handle updating
+ * @ppos properly.
+ */
+static ssize_t
+driverfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+       struct driver_file_entry * entry;
+       ssize_t retval = 0;
+
+       entry = (struct driver_file_entry *)file->private_data;
+       if (!entry) {
+               DBG("%s: file entry is NULL\n",__FUNCTION__);
+               return -ENOENT;
+       }
+
+       if (!entry->ops || !entry->ops->write) {
+               DBG("%s: no write callback\n",__FUNCTION__);
+               retval = -ENOENT;
+               goto done;
+       }
+
+       while (count > 0) {
+               ssize_t len;
+
+               len = entry->ops->write(buf,count,*ppos,entry->data);
+
+               if (len <= 0) {
+                       if (len < 0)
+                               retval = len;
+                       break;
+               }
+               retval += len;
+               count -= len;
+               *ppos += len;
+               buf += len;
+       }
+
+ done:
+       return retval;
+}
+
+static loff_t
+driverfs_file_lseek(struct file *file, loff_t offset, int orig)
+{
+       loff_t retval = -EINVAL;
+
+       switch(orig) {
+       case 0:
+               if (offset > 0) {
+                       file->f_pos = offset;
+                       retval = file->f_pos;
+               }
+               break;
+       case 1:
+               if ((offset + file->f_pos) > 0) {
+                       file->f_pos += offset;
+                       retval = file->f_pos;
+               }
+               break;
+       default:
+               break;
+       }
+       return retval;
+}
+
+static int driverfs_open_file(struct inode * inode, struct file * filp)
+{
+       if (filp && inode)
+               filp->private_data = inode->u.generic_ip;
+
+       return 0;
+}
+
+static int driverfs_sync_file (struct file *file, struct dentry *dentry, int datasync)
+{
+       return 0;
+}
+
+/* When the dentry goes away (the refcount hits 0), free the
+ * driver_file_entry for it.
+ */
+static int driverfs_d_delete_file (struct dentry * dentry)
+{
+       struct driver_file_entry * entry;
+
+       entry = (struct driver_file_entry *)dentry->d_fsdata;
+       if (entry)
+               kfree(dentry);
+       return 0;
+}
+
+/* Similar to above - if this dentry goes away, free the
+ * driver_dir_entry associated with it..
+ */
+static int driverfs_d_delete_dir (struct dentry * dentry)
+{
+       struct driver_dir_entry * entry;
+       entry = (struct driver_dir_entry *)dentry->d_fsdata;
+       if (entry)
+               kfree(entry);
+       return 0;
+}
+
+static struct address_space_operations driverfs_aops = {
+
+};
+
+static struct file_operations driverfs_file_operations = {
+       read:           driverfs_read_file,
+       write:          driverfs_write_file,
+       llseek:         driverfs_file_lseek,
+       mmap:           generic_file_mmap,
+       open:           driverfs_open_file,
+       fsync:          driverfs_sync_file,
+};
+
+static struct file_operations driverfs_dir_operations = {
+       read:           generic_read_dir,
+       readdir:        dcache_readdir,
+       fsync:          driverfs_sync_file,
+};
+
+static struct inode_operations driverfs_dir_inode_operations = {
+       create:         driverfs_create,
+       lookup:         driverfs_lookup,
+       link:           driverfs_link,
+       unlink:         driverfs_unlink,
+       symlink:        driverfs_symlink,
+       mkdir:          driverfs_mkdir,
+       rmdir:          driverfs_rmdir,
+       mknod:          driverfs_mknod,
+       rename:         driverfs_rename,
+};
+
+static struct dentry_operations driverfs_dentry_file_ops = {
+       d_delete:       driverfs_d_delete_file,
+};
+
+static struct dentry_operations driverfs_dentry_dir_ops = {
+       d_delete:       driverfs_d_delete_dir,
+};
+
+static struct super_operations driverfs_ops = {
+       statfs:         driverfs_statfs,
+       put_inode:      force_delete,
+};
+
+static struct super_block*
+driverfs_read_super(struct super_block *sb, void *data, int silent)
+{
+       struct inode *inode;
+       struct dentry *root;
+
+       sb->s_blocksize = PAGE_CACHE_SIZE;
+       sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+       sb->s_magic = DRIVERFS_MAGIC;
+       sb->s_op = &driverfs_ops;
+       inode = driverfs_get_inode(sb, S_IFDIR | 0755, 0);
+
+       if (!inode) {
+               DBG("%s: could not get inode!\n",__FUNCTION__);
+               return NULL;
+       }
+
+       root = d_alloc_root(inode);
+       if (!root) {
+               DBG("%s: could not get root dentry!\n",__FUNCTION__);
+               iput(inode);
+               return NULL;
+       }
+       sb->s_root = root;
+       return sb;
+}
+
+static DECLARE_FSTYPE(driverfs_fs_type, "driverfs", driverfs_read_super, FS_SINGLE | FS_LITTER);
+
+static int get_mount(void)
+{
+       struct vfsmount * mnt;
+
+       spin_lock(&mount_lock);
+       if (driverfs_mount) {
+               mntget(driverfs_mount);
+               ++mount_count;
+               spin_unlock(&mount_lock);
+               goto go_ahead;
+       }
+
+       spin_unlock(&mount_lock);
+       mnt = kern_mount(&driverfs_fs_type);
+
+       if (IS_ERR(mnt)) {
+               printk(KERN_ERR "driverfs: could not mount!\n");
+               return -ENODEV;
+       }
+
+       spin_lock(&mount_lock);
+       if (!driverfs_mount) {
+               driverfs_mount = mnt;
+               ++mount_count;
+               spin_unlock(&mount_lock);
+               goto go_ahead;
+       }
+
+       mntget(driverfs_mount);
+       ++mount_count;
+       spin_unlock(&mount_lock);
+
+ go_ahead:
+       DBG("driverfs: mount_count = %d\n",mount_count);
+       return 0;
+}
+
+static void put_mount(void)
+{
+       struct vfsmount * mnt;
+
+       spin_lock(&mount_lock);
+       mnt = driverfs_mount;
+       --mount_count;
+       if (!mount_count)
+               driverfs_mount = NULL;
+       spin_unlock(&mount_lock);
+       mntput(mnt);
+       DBG("driverfs: mount_count = %d\n",mount_count);
+}
+
+int __init init_driverfs_fs(void)
+{
+       int error;
+
+       DBG("%s: registering filesystem.\n",__FUNCTION__);
+       error = register_filesystem(&driverfs_fs_type);
+
+       if (error)
+               DBG(KERN_ERR "%s: register_filesystem failed with %d\n",
+                      __FUNCTION__,error);
+
+       return error;
+}
+
+static void __exit exit_driverfs_fs(void)
+{
+       unregister_filesystem(&driverfs_fs_type);
+}
+#if 0
+module_init(init_driverfs_fs);
+module_exit(exit_driverfs_fs);
+#endif
+
+EXPORT_SYMBOL(driverfs_create_file);
+EXPORT_SYMBOL(driverfs_create_dir);
+EXPORT_SYMBOL(driverfs_remove_file);
+EXPORT_SYMBOL(driverfs_remove_dir);
+
+MODULE_DESCRIPTION("The device driver filesystem");
+MODULE_LICENSE("GPL");
+
+/**
+ * driverfs_create_dir_entry - allocate and initialise directory entry
+ * @name:      name of the directory
+ * @mode:      permissions of the dir
+ */
+struct driver_dir_entry *
+driverfs_create_dir_entry(const char * name, mode_t mode)
+{
+       struct driver_dir_entry * entry;
+       int size = sizeof(struct driver_dir_entry) + strlen(name) + 1;
+
+
+       entry = kmalloc(size, GFP_KERNEL);
+       if (!entry)
+               return NULL;
+
+       memset(entry, 0, size);
+       strcpy((char *)entry + sizeof(struct driver_dir_entry), name);
+
+       entry->name = (char *)entry + sizeof(struct driver_dir_entry);
+
+       INIT_LIST_HEAD(&entry->files);
+       entry->mode = mode;
+
+       return entry;
+}
+
+/**
+ * driverfs_create_dir - create a directory in the filesystem
+ * @entry:     directory entry
+ * @parent:    parent directory entry
+ */
+int
+driverfs_create_dir(struct driver_dir_entry * entry,
+                   struct driver_dir_entry * parent)
+{
+       struct dentry * dentry = NULL;
+       struct dentry * parent_dentry;
+       struct qstr qstr;
+       int error = 0;
+
+       if (!entry)
+               return -EINVAL;
+
+       get_mount();
+
+       parent_dentry = parent ? parent->dentry : NULL;
+
+       if (!parent_dentry)
+               if (driverfs_mount && driverfs_mount->mnt_sb)
+                       parent_dentry = driverfs_mount->mnt_sb->s_root;
+
+       if (!parent_dentry) {
+               put_mount();
+               return -EFAULT;
+       }
+
+       dget(parent_dentry);
+       down(&parent_dentry->d_inode->i_sem);
+
+       qstr.name = entry->name;
+       qstr.len = strlen(entry->name);
+       qstr.hash = full_name_hash(entry->name,qstr.len);
+
+       dentry = lookup_hash(&qstr,parent_dentry);
+       if (IS_ERR(dentry)) {
+               error = PTR_ERR(dentry);
+               up(&parent_dentry->d_inode->i_sem);
+               dput(parent_dentry);
+       } else {
+               dentry->d_fsdata = (void *) entry;
+               entry->dentry = dentry;
+               error = vfs_mkdir(parent_dentry->d_inode,dentry,entry->mode);
+               up(&parent_dentry->d_inode->i_sem);
+       }
+
+       if (error)
+               put_mount();
+       return error;
+}
+
+/**
+ * driverfs_create_file - create a file
+ * @entry:     structure describing the file
+ * @parent:    directory to create it in
+ */
+int
+driverfs_create_file(struct driver_file_entry * entry,
+                    struct driver_dir_entry * parent)
+{
+       struct dentry * dentry;
+       struct dentry * parent_dentry;
+       struct qstr qstr;
+       int error = 0;
+
+       if (!entry || !parent)
+               return -EINVAL;
+
+       /* make sure we're mounted */
+       get_mount();
+
+       if (!parent->dentry) {
+               put_mount();
+               return -EINVAL;
+       }
+
+       /* make sure daddy doesn't run out on us again... */
+       parent_dentry = dget(parent->dentry);
+       down(&parent_dentry->d_inode->i_sem);
+
+       qstr.name = entry->name;
+       qstr.len = strlen(entry->name);
+       qstr.hash = full_name_hash(entry->name,qstr.len);
+
+       dentry = lookup_hash(&qstr,parent_dentry);
+       if (IS_ERR(dentry))
+               error = PTR_ERR(dentry);
+       else
+               error = vfs_create(parent_dentry->d_inode,dentry,entry->mode);
+
+
+       /* Still good? Ok, then fill in the blanks: */
+       if (!error) {
+               dentry->d_fsdata = (void *)entry;
+               dentry->d_inode->u.generic_ip = (void *)entry;
+
+               entry->dentry = dentry;
+
+               list_add_tail(&entry->node,&parent->files);
+       }
+       up(&parent_dentry->d_inode->i_sem);
+
+       if (error) {
+               dput(parent_dentry);
+               put_mount();
+       }
+       return error;
+}
+
+/**
+ * __remove_file - remove a regular file in the filesystem
+ * @dentry:    dentry of file to remove
+ *
+ * Call unlink to remove the file, and dput on the dentry to drop
+ * the refcount.
+ */
+static void __remove_file(struct dentry * dentry)
+{
+       dget(dentry);
+       down(&dentry->d_inode->i_sem);
+
+       vfs_unlink(dentry->d_parent->d_inode,dentry);
+
+       unlock_dir(dentry);
+
+       /* remove reference count from when file was created */
+       dput(dentry);
+
+       put_mount();
+}
+
+/**
+ * driverfs_remove_file - exported file removal
+ * @dir:       directory the file supposedly resides in
+ * @name:      name of the file
+ *
+ * Try and find the file in the dir's list.
+ * If it's there, call __remove_file() (above) for the dentry.
+ */
+void driverfs_remove_file(struct driver_dir_entry * dir, const char * name)
+{
+       struct dentry * dentry;
+       struct list_head * node;
+
+       if (!dir->dentry)
+               return;
+
+       dentry = dget(dir->dentry);
+       down(&dentry->d_inode->i_sem);
+
+       node = dir->files.next;
+       while (node != &dir->files) {
+               struct driver_file_entry * entry = NULL;
+
+               entry = list_entry(node,struct driver_file_entry,node);
+               if (!strcmp(entry->name,name)) {
+                       list_del_init(node);
+
+                       __remove_file(entry->dentry);
+                       break;
+               }
+               node = node->next;
+       }
+       unlock_dir(dentry);
+}
+
+/**
+ * driverfs_remove_dir - exportable directory removal
+ * @dir:       directory to remove
+ *
+ * To make sure we don't orphan anyone, first remove
+ * all the children in the list, then do vfs_rmdir() to remove it
+ * and decrement the refcount..
+ */
+void driverfs_remove_dir(struct driver_dir_entry * dir)
+{
+       struct list_head * node;
+       struct dentry * dentry;
+
+       if (!dir->dentry)
+               goto done;
+
+       /* lock the directory while we remove the files */
+       dentry = dget(dir->dentry);
+       down(&dentry->d_inode->i_sem);
+
+       node = dir->files.next;
+       while (node != &dir->files) {
+               struct driver_file_entry * entry;
+               entry = list_entry(node,struct driver_file_entry,node);
+
+               list_del_init(node);
+
+               __remove_file(entry->dentry);
+
+               node = dir->files.next;
+       }
+
+       /* now lock the parent, so we can remove this directory */
+       lock_parent(dentry);
+
+       vfs_rmdir(dentry->d_parent->d_inode,dentry);
+       double_unlock(dentry,dentry->d_parent);
+
+       /* remove reference count from when directory was created */
+       dput(dentry);
+ done:
+       put_mount();
+}
+
+/**
+ * driverfs_create_entry - allocate and initialise a struct driver_file_entry
+ * @name:      name of the file
+ * @mode:      permissions of the file
+ * @ops:       Operations for the file
+ * @data:      data that will be passed back to the callback
+ *
+ */
+struct driver_file_entry *
+driverfs_create_entry (const char * name, mode_t mode,
+                      struct driverfs_operations * ops, void * data)
+{
+       struct driver_file_entry * entry;
+       int size;
+
+       size = sizeof(struct driver_file_entry) + strlen(name) + 1;
+
+       entry = kmalloc(size,GFP_KERNEL);
+       if (!entry)
+               return NULL;
+
+       memset(entry, 0, size);
+       strcpy((char *)entry + sizeof(struct driver_file_entry), name);
+
+       entry->name = (char *)entry + sizeof(struct driver_file_entry);
+
+       INIT_LIST_HEAD(&entry->node);
+
+       entry->mode = mode;
+       entry->ops = ops;
+       entry->data = data;
+
+       return entry;
+}
+
index ade1a71..8071aaf 100644 (file)
@@ -48,7 +48,7 @@ ssize_t fat_file_read(
 }
 
 
-int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
+int fat_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
 {
        struct super_block *sb = inode->i_sb;
        unsigned long phys;
index 6eef0b3..50c58a5 100644 (file)
@@ -1176,6 +1176,16 @@ void __init inode_init(unsigned long mempages)
        unused_inodes_flush_task.routine = try_to_sync_unused_inodes;
 }
 
+static inline void do_atime_update(struct inode *inode)
+{
+       unsigned long time = CURRENT_TIME;
+       if (inode->i_atime != time) {
+               inode->i_atime = time;
+               mark_inode_dirty_sync(inode);
+       }
+}
+
+
 /**
  *     update_atime    -       update the access time
  *     @inode: inode accessed
@@ -1190,8 +1200,7 @@ void update_atime (struct inode *inode)
        if ( IS_NOATIME (inode) ) return;
        if ( IS_NODIRATIME (inode) && S_ISDIR (inode->i_mode) ) return;
        if ( IS_RDONLY (inode) ) return;
-       inode->i_atime = CURRENT_TIME;
-       mark_inode_dirty_sync (inode);
+       do_atime_update(inode);
 }   /*  End Function update_atime  */
 
 
index 061aad0..0aee59b 100644 (file)
@@ -140,7 +140,7 @@ changed:
        return -EAGAIN;
 }
 
-static inline int get_block(struct inode * inode, long block,
+static inline int get_block(struct inode * inode, sector_t block,
                        struct buffer_head *bh_result, int create)
 {
        int err = -EIO;
index c00e8c2..590d31c 100644 (file)
@@ -434,6 +434,8 @@ static inline void follow_dotdot(struct nameidata *nd)
                mntput(nd->mnt);
                nd->mnt = parent;
        }
+       while (d_mountpoint(nd->dentry) && __follow_down(&nd->mnt, &nd->dentry))
+               ;
 }
 
 /*
index 5c1abb8..327e314 100644 (file)
@@ -561,6 +561,67 @@ static int do_remount(struct nameidata *nd,int flags,int mnt_flags,void *data)
        return err;
 }
 
+static int do_move_mount(struct nameidata *nd, char *old_name)
+{
+       struct nameidata old_nd, parent_nd;
+       struct vfsmount *p;
+       int err = 0;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (!old_name || !*old_name)
+               return -EINVAL;
+       if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd))
+               err = path_walk(old_name, &old_nd);
+       if (err)
+               return err;
+
+       down(&mount_sem);
+       while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
+               ;
+       err = -EINVAL;
+       if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
+               goto out;
+
+       err = -ENOENT;
+       down(&nd->dentry->d_inode->i_zombie);
+       if (IS_DEADDIR(nd->dentry->d_inode))
+               goto out1;
+
+       spin_lock(&dcache_lock);
+       if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
+               goto out2;
+
+       err = -EINVAL;
+       if (old_nd.dentry != old_nd.mnt->mnt_root)
+               goto out2;
+
+       if (old_nd.mnt == old_nd.mnt->mnt_parent)
+               goto out2;
+
+       if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
+             S_ISDIR(old_nd.dentry->d_inode->i_mode))
+               goto out2;
+
+       err = -ELOOP;
+       for (p = nd->mnt; p->mnt_parent!=p; p = p->mnt_parent)
+               if (p == old_nd.mnt)
+                       goto out2;
+       err = 0;
+
+       detach_mnt(old_nd.mnt, &parent_nd);
+       attach_mnt(old_nd.mnt, nd);
+out2:
+       spin_unlock(&dcache_lock);
+out1:
+       up(&nd->dentry->d_inode->i_zombie);
+out:
+       up(&mount_sem);
+       if (!err)
+               path_release(&parent_nd);
+       path_release(&old_nd);
+       return err;
+}
+
 static int do_add_mount(struct nameidata *nd, char *type, int flags,
                        int mnt_flags, char *name, void *data)
 {
@@ -677,6 +738,8 @@ long do_mount(char * dev_name, char * dir_name, char *type_page,
                                    data_page);
        else if (flags & MS_BIND)
                retval = do_loopback(&nd, dev_name, flags & MS_REC);
+       else if (flags & MS_MOVE)
+               retval = do_move_mount(&nd, dev_name);
        else
                retval = do_add_mount(&nd, type_page, flags, mnt_flags,
                                      dev_name, data_page);
@@ -906,6 +969,8 @@ static void __init init_mount_tree(void)
        root_vfsmnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
        if (IS_ERR(root_vfsmnt))
                panic("can't allocate root vfsmount");
+       set_fs_pwd(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
+       set_fs_root(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
 }
 
 void __init mnt_init(unsigned long mempages)
@@ -992,6 +1057,8 @@ int __init change_root(kdev_t new_root_dev,const char *put_old)
                }
                path_release(&devfs_nd);
        }
+       set_fs_pwd(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
+       set_fs_root(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
        spin_lock(&dcache_lock);
        detach_mnt(old_rootmnt, &parent_nd);
        spin_unlock(&dcache_lock);
index 879d9fe..2c71fd4 100644 (file)
@@ -147,7 +147,7 @@ repeat:
   }
   bn = allocate_bitmap_node(p_s_sb) ;
   if (!bn) {
-    current->policy = SCHED_YIELD ;
+    current->policy |= SCHED_YIELD ;
     schedule() ;
     goto repeat ;
   }
index d8eb65f..6e48644 100644 (file)
@@ -374,6 +374,56 @@ static int grab_super(struct super_block *s)
        put_super(s);
        return 0;
 }
+/**
+ *     insert_super    -       put superblock on the lists
+ *     @s:     superblock in question
+ *     @type:  filesystem type it will belong to
+ *
+ *     Associates superblock with fs type and puts it on per-type and global
+ *     superblocks' lists.  Should be called with sb_lock held; drops it.
+ */
+static void insert_super(struct super_block *s, struct file_system_type *type)
+{
+       s->s_type = type;
+       list_add(&s->s_list, super_blocks.prev);
+       list_add(&s->s_instances, &type->fs_supers);
+       spin_unlock(&sb_lock);
+       get_filesystem(type);
+}
+
+void put_unnamed_dev(kdev_t dev);      /* should become static */
+
+/**
+ *     remove_super    -       makes superblock unreachable
+ *     @s:     superblock in question
+ *
+ *     Removes superblock from the lists, unlocks it, drop the reference
+ *     and releases the hosting device.  @s should have no active
+ *     references by that time and after remove_super() it's essentially
+ *     in rundown mode - all remaining references are temporary, no new
+ *     reference of any sort are going to appear and all holders of
+ *     temporary ones will eventually drop them.  At that point superblock
+ *     itself will be destroyed; all its contents is already gone.
+ */
+static void remove_super(struct super_block *s)
+{
+       kdev_t dev = s->s_dev;
+       struct block_device *bdev = s->s_bdev;
+       struct file_system_type *fs = s->s_type;
+
+       spin_lock(&sb_lock);
+       list_del(&s->s_list);
+       list_del(&s->s_instances);
+       spin_unlock(&sb_lock);
+       up_write(&s->s_umount);
+       put_super(s);
+       put_filesystem(fs);
+       if (bdev)
+               blkdev_put(bdev, BDEV_FS);
+       else
+               put_unnamed_dev(dev);
+}
 
 struct vfsmount *alloc_vfsmnt(void);
 void free_vfsmnt(struct vfsmount *mnt);
@@ -510,11 +560,8 @@ static struct super_block * read_super(kdev_t dev, struct block_device *bdev,
        s->s_dev = dev;
        s->s_bdev = bdev;
        s->s_flags = flags;
-       s->s_type = type;
        spin_lock(&sb_lock);
-       list_add (&s->s_list, super_blocks.prev);
-       list_add (&s->s_instances, &type->fs_supers);
-       spin_unlock(&sb_lock);
+       insert_super(s, type);
        lock_super(s);
        if (!type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
                goto out_fail;
@@ -527,17 +574,9 @@ out:
        return s;
 
 out_fail:
-       s->s_dev = 0;
-       s->s_bdev = 0;
-       s->s_type = NULL;
        unlock_super(s);
        deactivate_super(s);
-       spin_lock(&sb_lock);
-       list_del(&s->s_list);
-       list_del(&s->s_instances);
-       spin_unlock(&sb_lock);
-       up_write(&s->s_umount);
-       put_super(s);
+       remove_super(s);
        return NULL;
 }
 
@@ -609,13 +648,17 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type,
                goto out;
        check_disk_change(dev);
        error = -EACCES;
-       if (!(flags & MS_RDONLY) && is_read_only(dev))
-               goto out1;
+       if (!(flags & MS_RDONLY) && is_read_only(dev)) {
+               blkdev_put(bdev, BDEV_FS);
+               goto out;
+       }
 
        error = -ENOMEM;
        s = alloc_super();
-       if (!s)
-               goto out1;
+       if (!s) {
+               blkdev_put(bdev, BDEV_FS);
+               goto out;
+       }
 
        error = -EBUSY;
 restart:
@@ -629,7 +672,8 @@ restart:
                    ((flags ^ old->s_flags) & MS_RDONLY)) {
                        spin_unlock(&sb_lock);
                        destroy_super(s);
-                       goto out1;
+                       blkdev_put(bdev, BDEV_FS);
+                       goto out;
                }
                if (!grab_super(old))
                        goto restart;
@@ -641,10 +685,7 @@ restart:
        s->s_dev = dev;
        s->s_bdev = bdev;
        s->s_flags = flags;
-       s->s_type = fs_type;
-       list_add (&s->s_list, super_blocks.prev);
-       list_add (&s->s_instances, &fs_type->fs_supers);
-       spin_unlock(&sb_lock);
+       insert_super(s, fs_type);
 
        error = -EINVAL;
        lock_super(s);
@@ -652,24 +693,13 @@ restart:
                goto out_fail;
        s->s_flags |= MS_ACTIVE;
        unlock_super(s);
-       get_filesystem(fs_type);
        path_release(&nd);
        return s;
 
 out_fail:
-       s->s_dev = 0;
-       s->s_bdev = 0;
-       s->s_type = NULL;
        unlock_super(s);
        deactivate_super(s);
-       spin_lock(&sb_lock);
-       list_del(&s->s_list);
-       list_del(&s->s_instances);
-       spin_unlock(&sb_lock);
-       up_write(&s->s_umount);
-       put_super(s);
-out1:
-       blkdev_put(bdev, BDEV_FS);
+       remove_super(s);
 out:
        path_release(&nd);
        return ERR_PTR(error);
@@ -685,11 +715,8 @@ static struct super_block *get_sb_nodev(struct file_system_type *fs_type,
                struct super_block * sb;
                error = -EINVAL;
                sb = read_super(dev, NULL, fs_type, flags, data);
-               if (sb) {
-                       get_filesystem(fs_type);
+               if (sb)
                        return sb;
-               }
-               put_unnamed_dev(dev);
        }
        return ERR_PTR(error);
 }
@@ -724,39 +751,24 @@ retry:
                }
                s->s_dev = dev;
                s->s_flags = flags;
-               s->s_type = fs_type;
-               list_add (&s->s_list, super_blocks.prev);
-               list_add (&s->s_instances, &fs_type->fs_supers);
-               spin_unlock(&sb_lock);
+               insert_super(s, fs_type);
                lock_super(s);
                if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
                        goto out_fail;
                s->s_flags |= MS_ACTIVE;
                unlock_super(s);
-               get_filesystem(fs_type);
                return s;
 
        out_fail:
-               s->s_dev = 0;
-               s->s_bdev = 0;
-               s->s_type = NULL;
                unlock_super(s);
                deactivate_super(s);
-               spin_lock(&sb_lock);
-               list_del(&s->s_list);
-               list_del(&s->s_instances);
-               spin_unlock(&sb_lock);
-               up_write(&s->s_umount);
-               put_super(s);
-               put_unnamed_dev(dev);
+               remove_super(s);
                return ERR_PTR(-EINVAL);
        }
 }
 
 void kill_super(struct super_block *sb)
 {
-       struct block_device *bdev;
-       kdev_t dev;
        struct dentry *root = sb->s_root;
        struct file_system_type *fs = sb->s_type;
        struct super_operations *sop = sb->s_op;
@@ -789,24 +801,9 @@ void kill_super(struct super_block *sb)
                        "Self-destruct in 5 seconds.  Have a nice day...\n");
        }
 
-       dev = sb->s_dev;
-       sb->s_dev = 0;          /* Free the superblock */
-       bdev = sb->s_bdev;
-       sb->s_bdev = NULL;
-       put_filesystem(fs);
-       sb->s_type = NULL;
-       unlock_super(sb);
        unlock_kernel();
-       if (bdev)
-               blkdev_put(bdev, BDEV_FS);
-       else
-               put_unnamed_dev(dev);
-       spin_lock(&sb_lock);
-       list_del(&sb->s_list);
-       list_del(&sb->s_instances);
-       spin_unlock(&sb_lock);
-       up_write(&sb->s_umount);
-       put_super(sb);
+       unlock_super(sb);
+       remove_super(sb);
 }
 
 /*
@@ -957,7 +954,6 @@ void __init mount_root(void)
        int retval;
        void *handle;
        char path[64];
-       int path_start = -1;
        char *name = "/dev/root";
        char *fs_names, *p;
 #ifdef CONFIG_ROOT_NFS
@@ -1012,27 +1008,28 @@ skip_nfs:
        handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME,
                                    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);
+       if (handle) {
+               int n;
+               unsigned major, minor;
+
+               devfs_get_maj_min (handle, &major, &minor);
+               ROOT_DEV = MKDEV (major, minor);
+               if (!ROOT_DEV)
+                       panic("I have no root and I want to scream");
+               n = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
+               if (n >= 0) {
+                       name = path + n;
+                       devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
+                                         name + 5, NULL, NULL);
+                       memcpy (name, "/dev/", 5);
+               }
        }
 
-       /*
-        * Probably pure paranoia, but I'm less than happy about delving into
-        * devfs crap and checking it right now. Later.
-        */
-       if (!ROOT_DEV)
-               panic("I have no root and I want to scream");
-
 retry:
        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;
@@ -1046,6 +1043,7 @@ retry:
                 * Allow the user to distinguish between failed open
                 * and bad superblock on root device.
                 */
+Eio:
                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");
@@ -1068,11 +1066,17 @@ retry:
                struct file_system_type * fs_type = get_fs_type(p);
                if (!fs_type)
                        continue;
+               atomic_inc(&bdev->bd_count);
+               retval = blkdev_get(bdev, mode, 0, BDEV_FS);
+               if (retval)
+                       goto Eio;
                sb = read_super(ROOT_DEV, bdev, fs_type,
                                root_mountflags, root_mount_data);
-               if (sb) 
-                       goto mount_it;
                put_filesystem(fs_type);
+               if (sb) {
+                       blkdev_put(bdev, BDEV_FS);
+                       goto mount_it;
+               }
        }
        panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));
 
@@ -1082,12 +1086,6 @@ mount_it:
        printk ("VFS: Mounted root (%s filesystem)%s.\n", p,
                (sb->s_flags & MS_RDONLY) ? " readonly" : "");
        putname(fs_names);
-       if (path_start >= 0) {
-               name = path + path_start;
-               devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
-                                 name + 5, NULL, NULL);
-               memcpy (name, "/dev/", 5);
-       }
        vfsmnt = alloc_vfsmnt();
        if (!vfsmnt)
                panic("VFS: alloc_vfsmnt failed for root fs");
index 4d42294..3aed4d1 100644 (file)
@@ -73,6 +73,12 @@ do {                                                                         \
                        BUG();                  \
        } while (0)
 
+#define BUG_ON(condition)                      \
+       do {                                    \
+               if (unlikely((int)(condition))) \
+                       BUG();                  \
+       } while (0)
+
 /* Pure 2^n version of get_order */
 extern __inline__ int get_order(unsigned long size)
 {
index 99ae325..fc63356 100644 (file)
@@ -1,12 +1,11 @@
 /*
- * New 2.5 block I/O model
+ * 2.5 block I/O model
  *
  * Copyright (C) 2001 Jens Axboe <axboe@suse.de>
  *
- * This program is free software; you can redistribute it and/or mo
- * it under the terms of the GNU General Public License as publishe
- * the Free Software Foundation; either version 2 of the License, o
- * (at your option) any later version.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -121,7 +120,7 @@ struct bio {
  */
 #define __BVEC_END(bio) bio_iovec_idx((bio), (bio)->bi_idx - 1)
 #define BIO_CONTIG(bio, nxt) \
-       (bvec_to_phys(__BVEC_END((bio)) + (bio)->bi_size) ==bio_to_phys((nxt)))
+       (bvec_to_phys(__BVEC_END((bio)) + (bio)->bi_size) == bio_to_phys((nxt)))
 #define __BIO_SEG_BOUNDARY(addr1, addr2, mask) \
        (((addr1) | (mask)) == (((addr2) - 1) | (mask)))
 #define BIO_SEG_BOUNDARY(q, b1, b2) \
index 5f8d4e3..c061ce0 100644 (file)
@@ -100,11 +100,6 @@ struct request_queue
        unsigned long           bounce_pfn;
 
        /*
-        * for memory zoning (<= 4GB and > 4GB)
-        */
-       int                     bounce_gfp;
-
-       /*
         * This is used to remove the plug when tq_disk runs.
         */
        struct tq_struct        plug_tq;
@@ -177,11 +172,11 @@ extern inline struct request *elv_next_request(request_queue_t *q)
 
 #ifdef CONFIG_HIGHMEM
 
-extern void create_bounce(unsigned long pfn, struct bio **bio_orig, int gfp_mask);
+extern void create_bounce(unsigned long pfn, struct bio **bio_orig);
 
 extern inline void blk_queue_bounce(request_queue_t *q, struct bio **bio)
 {
-       create_bounce(q->bounce_pfn, bio, q->bounce_gfp);
+       create_bounce(q->bounce_pfn, bio);
 }
 
 #else /* CONFIG_HIGHMEM */
index 086acce..14e9c0b 100644 (file)
 #endif
 #endif /* __cacheline_aligned */
 
+#ifndef __cacheline_aligned_in_smp
+#ifdef CONFIG_SMP
+#define __cacheline_aligned_in_smp __cacheline_aligned
+#else
+#define __cacheline_aligned_in_smp
+#endif /* CONFIG_SMP */
+#endif
+
 #endif /* __LINUX_CACHE_H */
diff --git a/include/linux/device.h b/include/linux/device.h
new file mode 100644 (file)
index 0000000..df9e172
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * device.h - generic, centralized driver model
+ *
+ * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
+ *
+ * This is a relatively simple centralized driver model.
+ * The data structures were mainly lifted directly from the PCI
+ * driver model. These are thought to be the common fields that
+ * are relevant to all device buses.
+ *
+ * All the devices are arranged in a tree. All devices should
+ * have some sort of parent bus of whom they are children of.
+ * Devices should not be direct children of the system root.
+ *
+ * Device drivers should not directly call the device_* routines
+ * or access the contents of struct device directly. Instead,
+ * abstract that from the drivers and write bus-specific wrappers
+ * that do it for you.
+ *
+ * See Documentation/driver-model.txt for more information.
+ */
+
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/driverfs_fs.h>
+
+#define DEVICE_NAME_SIZE       80
+#define DEVICE_ID_SIZE         32
+#define BUS_ID_SIZE            16
+
+
+enum {
+       SUSPEND_NOTIFY,
+       SUSPEND_SAVE_STATE,
+       SUSPEND_DISABLE,
+       SUSPEND_POWER_DOWN,
+};
+
+enum {
+       RESUME_POWER_ON,
+       RESUME_RESTORE_STATE,
+       RESUME_ENABLE,
+};
+
+enum {
+       REMOVE_NOTIFY,
+       REMOVE_FREE_RESOURCES,
+};
+
+struct device;
+struct iobus;
+
+struct device_driver {
+       int     (*probe)        (struct device * dev);
+       int     (*remove)       (struct device * dev, u32 flags);
+
+       int     (*suspend)      (struct device * dev, u32 state, u32 stage);
+       int     (*resume)       (struct device * dev, u32 stage);
+};
+
+struct device {
+       struct list_head node;          /* node in sibling list */
+       struct iobus    *parent;        /* parent bus */
+
+       struct iobus    *subordinate;   /* only valid if this device is a
+                                          bridge device */
+
+       char    name[DEVICE_NAME_SIZE]; /* descriptive ascii string */
+       char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
+
+       spinlock_t      lock;           /* lock for the device to ensure two
+                                          different layers don't access it at
+                                          the same time. */
+       atomic_t        refcount;       /* refcount to make sure the device
+                                        * persists for the right amount of time */
+
+       struct driver_dir_entry * dir;
+
+       struct device_driver *driver;   /* which driver has allocated this
+                                          device */
+       void            *driver_data;   /* data private to the driver */
+       void            *platform_data; /* Platform specific data (e.g. ACPI,
+                                          BIOS data relevant to device */
+
+       u32             current_state;  /* Current operating state. In
+                                          ACPI-speak, this is D0-D3, D0
+                                          being fully functional, and D3
+                                          being off. */
+
+       unsigned char *saved_state;     /* saved device state */
+};
+
+/*
+ * struct bus_type - descriptor for a type of bus
+ * There are some instances when you need to know what type of bus a
+ * device is on. Instead of having some sort of enumerated integer type,
+ * each struct iobus will have a pointer to a struct bus_type that gives
+ * actually meaningful data.
+ * There should be one struct bus_type for each type of bus (one for PCI,
+ * one for USB, etc).
+ */
+struct iobus_driver {
+       char    name[16];       /* ascii descriptor of type of bus */
+       struct  list_head node; /* node in global list of bus drivers */
+
+       int     (*scan)         (struct iobus*);
+       int     (*add_device)   (struct iobus*, char*);
+};
+
+extern int iobus_register_driver(struct iobus_driver * driver);
+extern void iobus_unregister_driver(struct iobus_driver * driver);
+extern struct iobus_driver * iobus_find_driver(char *name);
+
+struct iobus {
+       spinlock_t      lock;           /* lock for bus */
+       atomic_t        refcount;
+
+       struct list_head node;          /* node in sibling list */
+       struct iobus *parent;           /* parent bus */
+       struct list_head children;      /* children buses */
+       struct list_head devices;       /* children devices */
+
+       struct device *self;            /* pointer to controlling device */
+       struct driver_dir_entry * dir;  /* driverfs directory */
+
+       char    name[DEVICE_NAME_SIZE];
+       char    bus_id[BUS_ID_SIZE];
+
+       struct  iobus_driver *driver;   /* bus operations */
+};
+
+static inline struct device *
+list_to_dev(struct list_head *node)
+{
+       return list_entry(node, struct device, node);
+}
+
+static inline struct iobus *
+list_to_iobus(const struct list_head *node)
+{
+       return list_entry(node, struct iobus, node);
+}
+
+/*
+ * High level routines for use by the bus drivers
+ */
+extern int device_register(struct device * dev);
+extern struct device * device_alloc(void);
+extern void device_init_dev(struct device * dev);
+
+extern int iobus_register(struct iobus * iobus);
+extern struct iobus * iobus_alloc(void);
+extern void iobus_init(struct iobus * iobus);
+
+extern int device_create_file(struct device *device, const char * name, mode_t mode,
+                             struct driverfs_operations * ops, void * data);
+extern void device_remove_file(struct device * dev, const char * name);
+
+extern int iobus_create_file(struct iobus *bus, const char * name, mode_t mode,
+                            struct driverfs_operations * ops, void * data);
+extern void iobus_remove_file(struct iobus * iobus, const char * name);
+
+
+/*
+ * Platform "fixup" functions - allow the platform to have their say
+ * about devices and actions that the general device layer doesn't
+ * know about.
+ */
+/* Notify platform of device discovery */
+extern int (*platform_notify)(struct device * dev);
+
+extern int (*platform_notify_remove)(struct device * dev);
+
+extern int device_driver_init(void);
+
+
+/* device and bus locking helpers.
+ *
+ * FIXME: Is there anything else we need to do?
+ */
+static inline void lock_device(struct device * dev)
+{
+       spin_lock(&dev->lock);
+}
+
+static inline void unlock_device(struct device * dev)
+{
+       spin_unlock(&dev->lock);
+}
+
+/**
+ * get_device - atomically increment the reference count for the device.
+ *
+ */
+static inline void get_device(struct device * dev)
+{
+       atomic_inc(&dev->refcount);
+}
+
+extern void put_device(struct device * dev);
+
+
+/**
+ * valid_device - check if device is valid
+ * @dev:       device in question
+ *
+ * Check whether or not a device can be operated on.
+ * If so, increment the reference count and carry on.
+ */
+static inline int valid_device(struct device * dev)
+{
+       int val;
+
+       lock_device(dev);
+       val = atomic_read(&dev->refcount);
+       if (val)
+               get_device(dev);
+       unlock_device(dev);
+       return (val > 0);
+}
+
+
+static inline void lock_iobus(struct iobus * iobus)
+{
+       spin_lock(&iobus->lock);
+}
+
+static inline void unlock_iobus(struct iobus * iobus)
+{
+       spin_unlock(&iobus->lock);
+}
+
+static inline void get_iobus(struct iobus * iobus)
+{
+       atomic_inc(&iobus->refcount);
+}
+
+static inline int valid_iobus(struct iobus * iobus)
+{
+       int val;
+       lock_iobus(iobus);
+       val = atomic_read(&iobus->refcount);
+       if (val)
+               get_iobus(iobus);
+       unlock_iobus(iobus);
+       return (val > 0);
+}
+
+extern void put_iobus(struct iobus * iobus);
+
+#endif /* _DEVICE_H_ */
diff --git a/include/linux/driverfs_fs.h b/include/linux/driverfs_fs.h
new file mode 100644 (file)
index 0000000..90a6034
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * driverfs_fs.h - definitions for the device driver filesystem
+ *
+ * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
+ *
+ *  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.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This is a simple, ram-based filesystem, which allows kernel
+ * callbacks for read/write of files.
+ *
+ * Please see Documentation/filesystems/driverfs.txt for more information.
+ */
+
+#ifndef _DRIVER_FS_H_
+#define _DRIVER_FS_H_
+
+struct driverfs_operations {
+       ssize_t (*read) (char *, size_t, loff_t, void *);
+       ssize_t (*write)(const char *, size_t, loff_t, void*);
+};
+
+struct driver_dir_entry {
+       char                    * name;
+       struct dentry           * dentry;
+       mode_t                  mode;
+       struct list_head        files;
+};
+
+struct driver_file_entry {
+       struct driver_dir_entry * parent;
+       struct list_head        node;
+       char                    * name;
+       mode_t                  mode;
+       struct dentry           * dentry;
+       void                    * data;
+       struct driverfs_operations      * ops;
+};
+
+extern struct driver_dir_entry *
+driverfs_create_dir_entry(const char * name, mode_t mode);
+
+extern int
+driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *);
+
+extern void
+driverfs_remove_dir(struct driver_dir_entry * entry);
+
+extern struct driver_file_entry *
+driverfs_create_entry (const char * name, mode_t mode,
+                      struct driverfs_operations * ops, void * data);
+
+extern int
+driverfs_create_file(struct driver_file_entry * entry,
+                    struct driver_dir_entry * parent);
+
+extern void
+driverfs_remove_file(struct driver_dir_entry *, const char * name);
+
+extern int init_driverfs_fs(void);
+
+#endif /* _DDFS_H_ */
index b18bf23..2bc05a6 100644 (file)
@@ -111,6 +111,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time;
 #define MS_NOATIME     1024    /* Do not update access times. */
 #define MS_NODIRATIME  2048    /* Do not update directory access times */
 #define MS_BIND                4096
+#define MS_MOVE                8192
 #define MS_REC         16384
 #define MS_VERBOSE     32768
 #define MS_ACTIVE      (1<<30)
index cffcb4b..157c3b6 100644 (file)
@@ -13,7 +13,7 @@ extern struct page *highmem_start_page;
 /* declarations for linux/mm/highmem.c */
 unsigned int nr_free_highpages(void);
 
-extern void create_bounce(unsigned long pfn, struct bio **bio_orig, int gfp_mask);
+extern void create_bounce(unsigned long pfn, struct bio **bio_orig);
 
 static inline char *bh_kmap(struct buffer_head *bh)
 {
index 1baa0a7..132b545 100644 (file)
@@ -273,7 +273,7 @@ extern struct file_operations fat_file_operations;
 extern struct inode_operations fat_file_inode_operations;
 extern ssize_t fat_file_read(struct file *filp, char *buf, size_t count,
                             loff_t *ppos);
-extern int fat_get_block(struct inode *inode, long iblock,
+extern int fat_get_block(struct inode *inode, sector_t iblock,
                         struct buffer_head *bh_result, int create);
 extern ssize_t fat_file_write(struct file *filp, const char *buf, size_t count,
                              loff_t *ppos);
index d5ab6ab..a8753be 100644 (file)
 #define PCI_DEVICE_ID_AVM_B1           0x0700
 #define PCI_DEVICE_ID_AVM_C4           0x0800
 #define PCI_DEVICE_ID_AVM_A1           0x0a00
+#define PCI_DEVICE_ID_AVM_A1_V2                0x0e00
 #define PCI_DEVICE_ID_AVM_C2           0x1100
 #define PCI_DEVICE_ID_AVM_T1           0x1200
 
index aebf005..248c1b8 100644 (file)
@@ -264,4 +264,16 @@ static inline int irlmp_get_lap_tx_queue_len(struct lsap_cb *self)
        return IRLAP_GET_TX_QUEUE_LEN(self->lap->irlap);
 }
 
+/* After doing a irlmp_dup(), this get one of the two socket back into
+ * a state where it's waiting incomming connections.
+ * Note : this can be used *only* if the socket is not yet connected
+ * (i.e. NO irlmp_connect_response() done on this socket).
+ * - Jean II */
+static inline void irlmp_listen(struct lsap_cb *self)
+{
+       self->dlsap_sel = LSAP_ANY;
+       self->lap = NULL;
+       self->lsap_state = LSAP_DISCONNECTED;
+}
+
 #endif
index a3193d7..d108741 100644 (file)
@@ -148,6 +148,17 @@ static __inline __u32 irttp_get_max_seg_size(struct tsap_cb *self)
        return self->max_seg_size;
 }
 
+/* After doing a irttp_dup(), this get one of the two socket back into
+ * a state where it's waiting incomming connections.
+ * Note : this can be used *only* if the socket is not yet connected
+ * (i.e. NO irttp_connect_response() done on this socket).
+ * - Jean II */
+static inline void irttp_listen(struct tsap_cb *self)
+{
+       irlmp_listen(self->lsap);
+       self->dtsap_sel = LSAP_ANY;
+}
+
 extern struct irttp_cb *irttp;
 
 #endif /* IRTTP_H */
index 8a1e432..b0e3304 100644 (file)
@@ -31,6 +31,8 @@
 #include <asm/io.h>
 #include <asm/bugs.h>
 
+#include <linux/device.h>
+
 #if defined(CONFIG_ARCH_S390)
 #include <asm/s390mach.h>
 #include <asm/ccwcache.h>
@@ -694,6 +696,9 @@ static void __init do_basic_setup(void)
        s390_init_machine_check();
 #endif
 
+       /* bring up the device tree */
+       device_driver_init();
+
 #ifdef CONFIG_PCI
        pci_init();
 #endif
index 02c4d3a..c118fff 100644 (file)
@@ -9,12 +9,13 @@
 
 O_TARGET := kernel.o
 
-export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o
+export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \
+               printk.o device.o
 
 obj-y     = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
            module.o exit.o itimer.o info.o time.o softirq.o resource.o \
            sysctl.o acct.o capability.o ptrace.o timer.o user.o \
-           signal.o sys.o kmod.o context.o
+           signal.o sys.o kmod.o context.o device.o
 
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += ksyms.o
diff --git a/kernel/device.c b/kernel/device.c
new file mode 100644 (file)
index 0000000..3534212
--- /dev/null
@@ -0,0 +1,1028 @@
+/*
+ * device.c
+ *
+ * Copyright (c) Patrick Mochel <mochel@osdl.org>
+ *
+ *  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.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Please see Documentation/driver-model.txt for more explanation.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/module.h>
+#include <linux/stat.h>
+#include <linux/driverfs_fs.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define DBG(x...) printk(x)
+#else
+# define DBG(x...)
+#endif
+
+static struct iobus device_root = {
+       bus_id: "root",
+       name:   "Logical System Root",
+};
+
+int (*platform_notify)(struct device * dev) = NULL;
+int (*platform_notify_remove)(struct device * dev) = NULL;
+
+static spinlock_t device_lock;
+static LIST_HEAD(device_gc_list);
+
+static int kdeviced_pid = 0;
+static DECLARE_WAIT_QUEUE_HEAD(kdeviced_wait);
+static DECLARE_COMPLETION(kdeviced_exited);
+
+static ssize_t device_read_status(char *, size_t, loff_t, void *);
+static ssize_t device_write_status(const char *, size_t, loff_t, void *);
+
+static struct driverfs_operations device_status_ops = {
+       read:   device_read_status,
+       write:  device_write_status,
+};
+
+static ssize_t device_read_power(char *, size_t, loff_t, void *);
+static ssize_t device_write_power(const char *, size_t, loff_t, void *);
+
+static struct driverfs_operations device_power_ops = {
+       read:   device_read_power,
+       write:  device_write_power,
+};
+
+static ssize_t iobus_read_status(char *, size_t, loff_t, void *);
+static ssize_t iobus_write_status(const char *, size_t, loff_t, void *);
+
+static struct driverfs_operations iobus_status_ops = {
+       read:   iobus_read_status,
+       write:  iobus_write_status,
+};
+
+
+/**
+ * device_create_file - create a driverfs file for a device
+ * @dev:       device requesting file
+ * @name:      name of file
+ * @mode:      permissions of file
+ * @ops:       operations for the file
+ * @data:      private data for the file
+ *
+ * Create a driverfs entry, then create the actual file the entry describes.
+ */
+int device_create_file(struct device * dev, const char * name, mode_t mode,
+                      struct driverfs_operations * ops, void * data)
+{
+       int error = -EFAULT;
+       struct driver_file_entry * entry;
+
+       if (!dev)
+               return -EINVAL;
+
+       if (!valid_device(dev))
+               return -EFAULT;
+
+       entry = driverfs_create_entry(name,mode,ops,data);
+       if (entry)
+               error = driverfs_create_file(entry,dev->dir);
+
+       put_device(dev);
+       return error;
+}
+
+/**
+ * device_remove_file - remove a device's file by name
+ * @dev:       device requesting removal
+ * @name:      name of the file
+ *
+ */
+void device_remove_file(struct device * dev, const char * name)
+{
+       if (!dev)
+               return;
+
+       if (!valid_device(dev))
+               return;
+
+       driverfs_remove_file(dev->dir,name);
+
+       put_device(dev);
+}
+
+/**
+ * device_remove_dir - remove a device's directory
+ * @dev:       device in question
+ */
+void device_remove_dir(struct device * dev)
+{
+       struct driver_dir_entry * dir;
+
+       if (!dev)
+               return;
+
+       lock_device(dev);
+       dir = dev->dir;
+       dev->dir = NULL;
+       unlock_device(dev);
+
+       if (dir)
+               driverfs_remove_dir(dir);
+}
+
+/**
+ * device_make_dir - create a driverfs directory
+ * @name:      name of directory
+ * @parent:    dentry for the parent directory
+ *
+ * Do the initial creation of the device's driverfs directory
+ * and populate it with the one default file.
+ *
+ * This is just a helper for device_register(), as we
+ * don't export this function. (Yes, that means we don't allow
+ * devices to create subdirectories).
+ */
+static int device_make_dir(struct device * dev)
+{
+       struct driver_dir_entry * entry;
+       int error;
+
+       entry = driverfs_create_dir_entry(dev->bus_id,(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO));
+       if (!entry)
+               return -EFAULT;
+
+       error = driverfs_create_dir(entry,dev->parent->dir);
+
+       if (error) {
+               kfree(entry);
+               return error;
+       }
+
+       lock_device(dev);
+       dev->dir = entry;
+       unlock_device(dev);
+
+       /* first the status file */
+       error = device_create_file(dev, "status", S_IRUGO | S_IWUSR,
+                                  &device_status_ops, (void *) dev);
+       if (error) {
+               device_remove_dir(dev);
+               goto done;
+       }
+
+       /* now the power file */
+       error = device_create_file(dev,"power",S_IRUGO | S_IWUSR,
+                                  &device_power_ops, (void *) dev);
+       if (error)
+               device_remove_dir(dev);
+
+ done:
+       return error;
+}
+
+/* iobus interface.
+ * For practical purposes, it's exactly the same as the device interface above.
+ * Even below, the two are almost identical, only taking different pointer
+ * types.
+ * I have fantasized about removing struct iobus completely. It would reduce
+ * this file by about 30%, and make life much easier. However, it will take some
+ * time to really work everything out..
+ */
+
+int iobus_create_file(struct iobus * iobus, const char * name, mode_t mode,
+                     struct driverfs_operations * ops, void * data)
+{
+       int error = -EFAULT;
+       struct driver_file_entry * entry;
+
+       if (!iobus)
+               return -EINVAL;
+
+       if (!valid_iobus(iobus))
+               return -EFAULT;
+
+       entry = driverfs_create_entry(name,mode,ops,data);
+       if (entry)
+               error = driverfs_create_file(entry,iobus->dir);
+
+       put_iobus(iobus);
+       return error;
+}
+
+void iobus_remove_file(struct iobus * iobus, const char * name)
+{
+       if (!iobus)
+               return;
+
+       if (!valid_iobus(iobus))
+               return;
+
+       driverfs_remove_file(iobus->dir,name);
+
+       put_iobus(iobus);
+}
+
+void iobus_remove_dir(struct iobus * iobus)
+{
+       struct driver_dir_entry * dir;
+
+       if (!iobus)
+               return;
+
+       lock_iobus(iobus);
+       dir = iobus->dir;
+       iobus->dir = NULL;
+       unlock_iobus(iobus);
+
+       if (dir)
+               driverfs_remove_dir(dir);
+}
+
+static int iobus_make_dir(struct iobus * iobus)
+{
+       struct driver_dir_entry * entry;
+       struct driver_dir_entry * parent = NULL;
+       int error;
+
+       entry = driverfs_create_dir_entry(iobus->bus_id,(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO));
+       if (!entry)
+               return -EFAULT;
+
+       if (iobus->parent)
+               parent = iobus->parent->dir;
+
+       error = driverfs_create_dir(entry,parent);
+       if (error) {
+               kfree(entry);
+               return error;
+       }
+
+       lock_iobus(iobus);
+       iobus->dir = entry;
+       unlock_iobus(iobus);
+
+       error = iobus_create_file(iobus, "status", S_IRUGO | S_IWUSR,
+                                 &iobus_status_ops, (void *)iobus);
+       if (error)
+               iobus_remove_dir(iobus);
+
+       return error;
+}
+
+/**
+ * device_register - register a device
+ * @dev:       pointer to the device structure
+ *
+ * First, make sure that the device has a parent, create
+ * a directory for it, then add it to the parent's list of
+ * children.
+ */
+int device_register(struct device *dev)
+{
+       struct iobus * parent;
+       int error = -EFAULT;
+
+       if (!dev)
+               return -EINVAL;
+
+       if (!dev->parent)
+               dev->parent = &device_root;
+       parent = dev->parent;
+
+       if (valid_iobus(parent)) {
+               if (!valid_device(dev)) {
+                       put_iobus(parent);
+                       goto register_done;
+               }
+       } else
+               return -EFAULT;
+
+       if (!strlen(dev->name)) {
+               error = -EINVAL;
+               goto register_done;
+       }
+
+       error = device_make_dir(dev);
+       if (error)
+               goto register_done;
+
+
+       /* finally add it to its parent's list */
+       lock_iobus(parent);
+       list_add_tail(&dev->node, &parent->devices);
+       unlock_iobus(parent);
+
+       DBG("DEV: registering device: ID = '%s', name = %s, parent = %s\n",
+           dev->bus_id, dev->name, parent->bus_id);
+
+       /* notify platform of device entry */
+       if (platform_notify)
+               platform_notify(dev);
+
+       return 0;
+
+ register_done:
+       put_device(dev);
+       put_iobus(parent);
+
+       return error;
+}
+
+/**
+ * put_device - clean up device
+ * @dev:       device in question
+ *
+ * Decrement reference count for device.
+ * If it hits 0, we need to clean it up.
+ * However, we may be here in interrupt context, and it may
+ * take some time to do proper clean up (removing files, calling
+ * back down to device to clean up everything it has).
+ * So, we remove it from its parent's list and add it to the list of
+ * devices to be cleaned up.
+ */
+void put_device(struct device * dev)
+{
+       struct iobus * parent;
+
+       if (!atomic_dec_and_lock(&dev->refcount,&dev->lock))
+               return;
+
+       parent = dev->parent;
+       dev->parent = NULL;
+       unlock_device(dev);
+
+       DBG("DEV: Unregistering device. ID = '%s', name = '%s'\n",
+           dev->bus_id,dev->name);
+
+       /* disavow parent's knowledge */
+       lock_iobus(parent);
+       list_del_init(&dev->node);
+       unlock_iobus(parent);
+
+       /* queue the device to be removed by the reaper. */
+       spin_lock(&device_lock);
+       list_add_tail(&dev->node,&device_gc_list);
+       spin_unlock(&device_lock);
+
+       wake_up(&kdeviced_wait);
+
+       put_iobus(parent);
+}
+
+int iobus_register(struct iobus *bus)
+{
+       struct iobus * parent;
+       int error = -EINVAL;
+
+       if (!bus)
+               return -EINVAL;
+
+       if (!bus->parent)
+               bus->parent = &device_root;
+       parent = bus->parent;
+
+       DBG("DEV: registering bus. ID = '%s' name = '%s' parent = %p\n",
+           bus->bus_id,bus->name,bus->parent);
+
+       if (valid_iobus(parent)) {
+               if (!valid_iobus(bus)) {
+                       put_iobus(parent);
+                       goto register_done;
+               }
+       } else
+               goto register_done;
+
+       if (!strlen(bus->bus_id))
+               goto register_done_put;
+
+       error = iobus_make_dir(bus);
+       if (error)
+               goto register_done_put;
+
+       lock_iobus(parent);
+       list_add_tail(&bus->node,&parent->children);
+       unlock_iobus(parent);
+
+       return 0;
+
+ register_done_put:
+       put_iobus(bus);
+       put_iobus(parent);
+ register_done:
+       return error;
+}
+
+/**
+ * iobus_unregister - remove bus and children from device tree
+ * @bus:       pointer to bus structure
+ *
+ * Remove device from parent's list of children and decrement
+ * reference count on controlling device. That should take care of
+ * the rest of the cleanup.
+ */
+void put_iobus(struct iobus * iobus)
+{
+       struct iobus * parent;
+
+       if (!atomic_dec_and_lock(&iobus->refcount,&iobus->lock))
+               return;
+
+       parent = iobus->parent;
+       iobus->parent = NULL;
+       unlock_iobus(iobus);
+
+       if (!list_empty(&iobus->devices) ||
+           !list_empty(&iobus->children))
+               BUG();
+
+       /* disavow parent's knowledge */
+       if (parent) {
+               lock_iobus(parent);
+               list_del(&iobus->node);
+               unlock_iobus(parent);
+
+               put_iobus(parent);
+       }
+
+       /* unregister itself */
+       put_device(iobus->self);
+
+       return;
+}
+
+/**
+ * device_init_dev - initialise a struct device
+ * @dev:       pointer to device struct
+ */
+void device_init_dev(struct device * dev)
+{
+       INIT_LIST_HEAD(&dev->node);
+       spin_lock_init(&dev->lock);
+       atomic_set(&dev->refcount,1);
+}
+
+/**
+ * device_alloc_dev - allocate and initialise a device structure
+ *
+ */
+struct device * device_alloc(void)
+{
+       struct device * dev;
+
+       dev = kmalloc(sizeof(struct device), GFP_KERNEL);
+
+       if (!dev)
+               return NULL;
+
+       memset(dev,0,sizeof(struct device));
+       device_init_dev(dev);
+
+       return dev;
+}
+
+void iobus_init(struct iobus *bus)
+{
+       spin_lock_init(&bus->lock);
+       atomic_set(&bus->refcount,1);
+
+       INIT_LIST_HEAD(&bus->node);
+       INIT_LIST_HEAD(&bus->children);
+       INIT_LIST_HEAD(&bus->devices);
+}
+
+struct iobus *iobus_alloc(void)
+{
+       struct iobus *bus;
+
+       bus = kmalloc(sizeof(struct iobus), GFP_KERNEL);
+
+       if (!bus)
+               return NULL;
+
+       memset(bus,0,sizeof(struct iobus));
+
+       iobus_init(bus);
+
+       return bus;
+}
+
+static int do_device_suspend(struct device * dev, u32 state)
+{
+       int error = 0;
+
+       if (!dev->driver->suspend)
+               return error;
+
+       error = dev->driver->suspend(dev,state,SUSPEND_NOTIFY);
+
+       if (error)
+               return error;
+
+       error = dev->driver->suspend(dev,state,SUSPEND_SAVE_STATE);
+       if (error) {
+               if (dev->driver->resume)
+                       dev->driver->resume(dev,RESUME_RESTORE_STATE);
+               return error;
+       }
+       error = dev->driver->suspend(dev,state,SUSPEND_POWER_DOWN);
+       if (error) {
+               if (dev->driver->resume)
+                       dev->driver->resume(dev,RESUME_RESTORE_STATE);
+       }
+       return error;
+}
+
+static int do_device_resume(struct device * dev)
+{
+       int error = 0;
+
+       if (!dev->driver->resume)
+               return 0;
+       error = dev->driver->resume(dev,RESUME_POWER_ON);
+       if (error)
+               return error;
+       error = dev->driver->resume(dev,RESUME_RESTORE_STATE);
+       return error;
+}
+
+/**
+ * device_read_status - report some device information
+ * @page:      page-sized buffer to write into
+ * @count:     number of bytes requested
+ * @off:       offset into buffer
+ * @data:      device-specific data
+ *
+ * Report some human-readable information about the device.
+ * This includes the name, the bus id, and the current power state.
+ */
+static ssize_t device_read_status(char * page, size_t count,
+                                 loff_t off, void * data)
+{
+       char *str = page;
+       struct device *dev = (struct device*)data;
+       ssize_t len = 0;
+
+       if (!dev)
+               return -EINVAL;
+
+       if (!valid_device(dev))
+               return -EFAULT;
+
+       if (off)
+               goto done;
+
+       str += sprintf(str,"Name:       %s\n",dev->name);
+       str += sprintf(str,"Bus ID:     %s\n",dev->bus_id);
+
+       len = str - page;
+
+       if (len > count)
+               len = count;
+
+       if (len < 0)
+               len = 0;
+
+ done:
+       put_device(dev);
+
+       return len;
+}
+
+/**
+ * device_write_status - forward a command to a driver
+ * @buf:       encoded command
+ * @count:     number of bytes in buffer
+ * @off:       offset into buffer to start with
+ * @data:      device-specific data
+ *
+ * Send a comamnd to a device driver.
+ * The following actions are supported:
+ * probe - scan slot for device
+ * remove - detach driver from slot
+ * suspend <state> <stage> - perform <stage> for entering <state>
+ * resume <stage> - perform <stage> for waking device up.
+ * (See Documentation/driver-model.txt for the theory of an n-stage
+ * suspend sequence).
+ */
+static ssize_t device_write_status(const char* buf, size_t count, loff_t off, void *data)
+{
+       char command[20];
+       struct device *dev = (struct device *)data;
+       int num;
+       int arg = 0;
+       int error = 0;
+
+       if (!dev)
+               return 0;
+
+       if (!valid_device(dev))
+               return -EFAULT;
+
+       if (off)
+               goto done_put;
+
+       /* everything involves dealing with the driver. */
+       if (!dev->driver)
+               goto done_put;
+
+       num = sscanf(buf,"%10s %d",command,&arg);
+
+       if (!num)
+               goto done_put;
+
+       if (!strcmp(command,"probe")) {
+               if (dev->driver->probe)
+                       error = dev->driver->probe(dev);
+
+       } else if (!strcmp(command,"remove")) {
+               if (dev->driver->remove)
+                       error = dev->driver->remove(dev,REMOVE_NOTIFY);
+       } else
+               error = -EFAULT;
+
+ done_put:
+       put_device(dev);
+       return error < 0 ? error : count;
+}
+
+static ssize_t
+device_read_power(char * page, size_t count, loff_t off, void * data)
+{
+       char    * str = page;
+       struct  device * dev = (struct device *)data;
+       ssize_t len = 0;
+
+       if (!dev)
+               return 0;
+
+       if (!valid_device(dev))
+               return 0;
+
+       str += sprintf(str,"State:      %d\n",dev->current_state);
+
+       len = str - page;
+
+       if (off) {
+               if (len < off) {
+                       len = 0;
+                       goto done;
+               }
+               str += off;
+               len -= off;
+       }
+
+       if (len > count)
+               len = count;
+
+ done:
+       put_device(dev);
+       return len;
+}
+
+static ssize_t
+device_write_power(const char * buf, size_t count, loff_t off, void * data)
+{
+       struct  device * dev = (struct device *)data;
+       char    str_command[20];
+       char    str_stage[20];
+       int     num_args;
+       u32     state;
+       u32     int_stage;
+       int     error = 0;
+
+       if (!dev)
+               return 0;
+
+       if (!valid_device(dev))
+               return -EFAULT;
+
+       if (off)
+               goto done;
+       if (!dev->driver)
+               goto done;
+
+       num_args = sscanf(buf,"%s %s %u",str_command,str_stage,&state);
+
+       error = -EINVAL;
+
+       if (!num_args) {
+               printk("have no arguments\n");
+               goto done;
+       }
+
+       if (!strnicmp(str_command,"suspend",7)) {
+
+               printk("%s: we know it's a suspend action\n",__FUNCTION__);
+
+               if (num_args != 3)
+                       goto done;
+               if (!strnicmp(str_stage,"notify",6))
+                       int_stage = SUSPEND_NOTIFY;
+               else if (!strnicmp(str_stage,"save",4))
+                       int_stage = SUSPEND_SAVE_STATE;
+               else if (!strnicmp(str_stage,"disable",7))
+                       int_stage = SUSPEND_DISABLE;
+               else if (!strnicmp(str_stage,"powerdown",8))
+                       int_stage = SUSPEND_POWER_DOWN;
+               else
+                       goto done;
+
+               if (dev->driver->suspend)
+                       error = dev->driver->suspend(dev,state,int_stage);
+               else
+                       error = 0;
+       } else if (!strnicmp(str_command,"resume",6)) {
+               if (num_args != 2)
+                       goto done;
+
+               if (!strnicmp(str_stage,"poweron",7))
+                       int_stage = RESUME_POWER_ON;
+               else if (!strnicmp(str_stage,"restore",7))
+                       int_stage = RESUME_RESTORE_STATE;
+               else if (!strnicmp(str_stage,"enable",6))
+                       int_stage = RESUME_ENABLE;
+               else
+                       goto done;
+
+               if (dev->driver->resume)
+                       error = dev->driver->resume(dev,int_stage);
+               else
+                       error = 0;
+       } else
+               printk("%s: couldn't find any thing to do\n",__FUNCTION__);
+ done:
+       put_device(dev);
+
+       DBG("%s: returning %d\n",__FUNCTION__,error);
+
+       return error < 0 ? error : count;
+}
+
+/**
+ * bus_read_status - report human readable information
+ * @page:      page-sized buffer to write into
+ * @count:     number of bytes requested
+ * @off:       offset into buffer to start at
+ * @data:      bus-specific data
+ */
+static ssize_t iobus_read_status(char *page, size_t count,
+                                loff_t off, void *data)
+{
+       char *str = page;
+       struct iobus *bus = (struct iobus*)data;
+       ssize_t len = 0;
+
+       if (!bus)
+               return -EINVAL;
+
+       if (!valid_iobus(bus))
+               return -EFAULT;
+
+       if (off)
+               goto done;
+
+       str += sprintf(str,"Name:       %s\n",bus->name);
+       str += sprintf(str,"Bus ID:     %s\n",bus->bus_id);
+
+       if (bus->driver)
+               str += sprintf(str,"Type:       %s\n",bus->driver->name);
+
+       len = str - page;
+       if (len < off)
+               len = 0;
+       if (len > count)
+               len = count;
+       if (len < 0)
+               len = 0;
+
+ done:
+       put_iobus(bus);
+       return len;
+}
+
+/**
+ * bus_write_status - forward a command to a bus
+ * @buf:       string encoded command
+ * @count:     number of bytes requested
+ * @off:       offset into buffer to start at
+ * @data:      bus-specific data
+ *
+ * Like device_write_status, this sends a command to a bus driver.
+ * Supported actions are:
+ * scan - scan a bus for devices
+ * add_device <id> - add a child device
+ */
+static ssize_t iobus_write_status(const char *buf, size_t count, loff_t off, void *data)
+{
+       char command[10];
+       char which[15];
+       char id[10];
+       struct iobus *bus = (struct iobus*)data;
+       int num;
+       int error = -EINVAL;
+
+       if (!bus)
+               return -EINVAL;
+
+       if (!valid_iobus(bus))
+               return -EFAULT;
+
+       if (!bus->driver)
+               goto done;
+
+       num = sscanf(buf,"%10s %15s %10s",command,which,id);
+
+       if (!num)
+               goto done;
+
+       if (!strnicmp(command,"scan",4)) {
+               if (bus->driver->scan)
+                       error = bus->driver->scan(bus);
+       } else if (!strnicmp(command,"add",3) && num == 2) {
+               error = bus->driver->add_device(bus,id);
+       } else if (!strnicmp(command, "suspend",7)) {
+               u32 state = simple_strtoul(which,NULL,0);
+               if (state > 0)
+                       error = do_device_suspend(bus->self,state);
+
+       } else if (!strnicmp(command,"resume",6)) {
+               error = do_device_resume(bus->self);
+
+       }
+
+ done:
+       put_iobus(bus);
+       return error < 0 ? error : count;
+}
+
+/* Device Garbage Collection
+ * When a device's reference count reaches 0, it is removed from it's
+ * parent's list and added to a list of devices waiting to be removed.
+ *
+ * We don't directly remove it ourselves, because someone could have an
+ * open file.
+ *
+ * We don't allocate an event for keventd, becuase we may be here from
+ * an interrupt; and how do those things get freed, anyway?
+ *
+ * Instead, when a device's reference count reaches 0, it is removed
+ * from its parent's list of children and added to the list of devices
+ * to be reaped.
+ *
+ * When we spawn a thread that gets woken up every time a device is added
+ * to the unused list.
+ */
+static inline void __reap_device(struct device * dev)
+{
+       /* FIXME: What do we do for a bridge? */
+
+       /* remove the driverfs directory */
+       device_remove_dir(dev);
+
+       if (dev->subordinate)
+               iobus_remove_dir(dev->subordinate);
+
+       /* Notify the platform of the removal, in case they
+        * need to do anything...
+        */
+       if (platform_notify_remove)
+               platform_notify_remove(dev);
+
+       /* Tell the driver to clean up after itself.
+        * Note that we likely didn't allocate the device,
+        * so this is the driver's chance to free that up...
+        */
+       if (dev->driver && dev->driver->remove)
+               dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
+}
+
+static int device_cleanup_thread(void * data)
+{
+       daemonize();
+
+       strcpy(current->comm,"kdeviced");
+
+       do {
+               struct list_head * node;
+
+               spin_lock(&device_lock);
+               node = device_gc_list.next;
+               while(node != &device_gc_list) {
+                       list_del_init(node);
+                       spin_unlock(&device_lock);
+                       __reap_device(list_to_dev(node));
+
+                       spin_lock(&device_lock);
+                       node = device_gc_list.next;
+               }
+               spin_unlock(&device_lock);
+
+               interruptible_sleep_on(&kdeviced_wait);
+       } while(!signal_pending(current));
+
+       DBG("kdeviced exiting\n");
+       complete_and_exit(&kdeviced_exited,0);
+       return 0;
+}
+
+static int __init device_init_root(void)
+{
+       /* initialize parent bus lists */
+       iobus_init(&device_root);
+
+       /* don't call iobus_register, as the only thing it really
+        * needs to do is create the root directory. Easier
+        * to just do it here than special case it elsewhere..
+        */
+       iobus_make_dir(&device_root);
+
+       return (device_root.dir ? 0 : -EFAULT);
+}
+
+int __init device_driver_init(void)
+{
+       int error = 0;
+       int pid;
+
+       DBG("DEV: Initialising Device Tree\n");
+
+       spin_lock_init(&device_lock);
+
+       error = init_driverfs_fs();
+
+       if (error) {
+               panic("DEV: could not initialise driverfs\n");
+               return error;
+       }
+
+       error = device_init_root();
+       if (error) {
+               printk(KERN_ERR "%s: device root init failed!\n", __FUNCTION__);
+               return error;
+       }
+
+       /* initialise the garbage collection */
+       pid = kernel_thread(device_cleanup_thread,NULL,
+                           (CLONE_FS | CLONE_FILES | CLONE_SIGHAND));
+       if (pid > 0)
+               kdeviced_pid = pid;
+       else {
+               DBG("DEV: Could not start cleanup thread\n");
+               return pid;
+       }
+
+       DBG("DEV: Done Initialising\n");
+       return error;
+}
+
+void __exit device_driver_exit(void)
+{
+       if (kdeviced_pid) {
+               kill_proc(kdeviced_pid,SIGTERM,1);
+               kdeviced_pid = 0;
+               wait_for_completion(&kdeviced_exited);
+       }
+}
+
+static int __init device_setup(char *str)
+{
+       return 0;
+}
+
+__setup("device=",device_setup);
+
+EXPORT_SYMBOL(device_register);
+EXPORT_SYMBOL(device_alloc);
+EXPORT_SYMBOL(device_init_dev);
+
+EXPORT_SYMBOL(device_create_file);
+EXPORT_SYMBOL(device_remove_file);
+
+EXPORT_SYMBOL(iobus_register);
+EXPORT_SYMBOL(iobus_alloc);
+EXPORT_SYMBOL(iobus_init);
+
+EXPORT_SYMBOL(iobus_create_file);
+EXPORT_SYMBOL(iobus_remove_file);
+
+EXPORT_SYMBOL(device_driver_init);
index 2650ac3..c2c97c1 100644 (file)
@@ -150,21 +150,34 @@ static inline int has_stopped_jobs(int pgrp)
 }
 
 /*
- * When we die, we re-parent all our children to
+ * When we die, we re-parent all our children.
+ * Try to give them to another thread in our process
+ * group, and if no such member exists, give it to
  * the global child reaper process (ie "init")
  */
 static inline void forget_original_parent(struct task_struct * father)
 {
-       struct task_struct * p;
+       struct task_struct * p, *reaper;
 
        read_lock(&tasklist_lock);
 
+       /* Next in our thread group */
+       reaper = next_thread(father);
+       if (reaper == father)
+               reaper = child_reaper;
+
        for_each_task(p) {
                if (p->p_opptr == father) {
                        /* We dont want people slaying init */
                        p->exit_signal = SIGCHLD;
                        p->self_exec_id++;
-                       p->p_opptr = child_reaper;
+
+                       /* Make sure we're not reparenting to ourselves */
+                       if (p == reaper)
+                               p->p_opptr = child_reaper;
+                       else
+                               p->p_opptr = reaper;
+
                        if (p->pdeath_signal) send_sig(p->pdeath_signal, p, 0);
                }
        }
index 3c28fc2..8a39c6a 100644 (file)
@@ -206,7 +206,7 @@ fail_nomem:
        return retval;
 }
 
-spinlock_t mmlist_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED;
+spinlock_t mmlist_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 int mmlist_nr;
 
 #define allocate_mm()  (kmem_cache_alloc(mm_cachep, SLAB_KERNEL))
index dd5fa5d..97b8eb8 100644 (file)
@@ -42,7 +42,7 @@
 
 irq_cpustat_t irq_stat[NR_CPUS];
 
-static struct softirq_action softirq_vec[32] __cacheline_aligned;
+static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
 
 /*
  * we cannot loop indefinitely here to avoid userspace starvation,
@@ -146,8 +146,8 @@ void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
 
 /* Tasklets */
 
-struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned;
-struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
+struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned_in_smp;
+struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned_in_smp;
 
 void __tasklet_schedule(struct tasklet_struct *t)
 {
index 5bd5e1e..4434dd0 100644 (file)
@@ -47,7 +47,7 @@ atomic_t page_cache_size = ATOMIC_INIT(0);
 unsigned int page_hash_bits;
 struct page **page_hash_table;
 
-spinlock_t pagecache_lock ____cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+spinlock_t pagecache_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 /*
  * NOTE: to avoid deadlocking you must never acquire the pagemap_lru_lock 
  *     with the pagecache_lock held.
@@ -57,7 +57,7 @@ spinlock_t pagecache_lock ____cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
  *             pagemap_lru_lock ->
  *                     pagecache_lock
  */
-spinlock_t pagemap_lru_lock ____cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+spinlock_t pagemap_lru_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
 #define CLUSTER_PAGES          (1 << page_cluster)
 #define CLUSTER_OFFSET(x)      (((x) >> page_cluster) << page_cluster)
index 8a06ab7..c353ab7 100644 (file)
@@ -343,7 +343,7 @@ repeat_alloc:
        goto repeat_alloc;
 }
 
-void create_bounce(unsigned long pfn, struct bio **bio_orig, int gfp_mask)
+void create_bounce(unsigned long pfn, struct bio **bio_orig)
 {
        struct page *page;
        struct bio *bio = NULL;
@@ -369,7 +369,7 @@ void create_bounce(unsigned long pfn, struct bio **bio_orig, int gfp_mask)
 
                to = &bio->bi_io_vec[i];
 
-               to->bv_page = alloc_bounce_page(gfp_mask);
+               to->bv_page = alloc_bounce_page(GFP_NOHIGHIO);
                to->bv_len = from->bv_len;
                to->bv_offset = from->bv_offset;
 
index facad15..5d8c5d5 100644 (file)
@@ -804,25 +804,17 @@ int get_swaparea_info(char *buf)
 {
        char * page = (char *) __get_free_page(GFP_KERNEL);
        struct swap_info_struct *ptr = swap_info;
-       int i, j, len = 0, usedswap;
+       int i, len;
 
        if (!page)
                return -ENOMEM;
 
-       len += sprintf(buf, "Filename\t\t\tType\t\tSize\tUsed\tPriority\n");
+       len = sprintf(buf, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
        for (i = 0 ; i < nr_swapfiles ; i++, ptr++) {
                if ((ptr->flags & SWP_USED) && ptr->swap_map) {
                        char * path = d_path(ptr->swap_file, ptr->swap_vfsmnt,
                                                page, PAGE_SIZE);
-
-                       len += sprintf(buf + len, "%-31s ", path);
-
-                       if (!ptr->swap_device)
-                               len += sprintf(buf + len, "file\t\t");
-                       else
-                               len += sprintf(buf + len, "partition\t");
-
-                       usedswap = 0;
+                       int j, usedswap = 0;
                        for (j = 0; j < ptr->max; ++j)
                                switch (ptr->swap_map[j]) {
                                        case SWAP_MAP_BAD:
@@ -831,8 +823,12 @@ int get_swaparea_info(char *buf)
                                        default:
                                                usedswap++;
                                }
-                       len += sprintf(buf + len, "%d\t%d\t%d\n", ptr->pages << (PAGE_SHIFT - 10), 
-                               usedswap << (PAGE_SHIFT - 10), ptr->prio);
+                       len += sprintf(buf + len, "%-39s %s\t%d\t%d\t%d\n",
+                                      path,
+                                      ptr->swap_device ? "partition" : "file\t",
+                                      ptr->pages << (PAGE_SHIFT - 10),
+                                      usedswap << (PAGE_SHIFT - 10),
+                                      ptr->prio);
                }
        }
        free_page((unsigned long) page);
index 5ab40b9..dbb0fa3 100644 (file)
@@ -14,13 +14,10 @@ if [ "$CONFIG_NET" != "n" ]; then
       source net/irda/irnet/Config.in
       source net/irda/ircomm/Config.in
       bool '  Ultra (connectionless) protocol' CONFIG_IRDA_ULTRA
-      bool '  IrDA protocol options' CONFIG_IRDA_OPTIONS
-      if [ "$CONFIG_IRDA_OPTIONS" != "n" ]; then
-        comment '  IrDA options'
-        bool '    Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
-        bool '    Fast RRs' CONFIG_IRDA_FAST_RR
-        bool '    Debug information' CONFIG_IRDA_DEBUG
-      fi
+      comment 'IrDA options'
+      bool '  Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
+      bool '  Fast RRs (low latency)' CONFIG_IRDA_FAST_RR
+      bool '  Debug information' CONFIG_IRDA_DEBUG
    fi
 
    if [ "$CONFIG_IRDA" != "n" ]; then
index 45ef5f7..da456b2 100644 (file)
@@ -914,8 +914,7 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags)
        memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info));
 
        /* Clean up the original one to keep it in listen state */
-       self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY;
-       self->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+       irttp_listen(self->tsap);
 
        skb->sk = NULL;
        skb->destructor = NULL;
@@ -1845,15 +1844,36 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
                        return -EFAULT;
                }
 
-               /* Find the object we target */
-               ias_obj = irias_find_object(ias_opt->irda_class_name);
+               /* Find the object we target.
+                * If the user gives us an empty string, we use the object
+                * associated with this socket. This will workaround
+                * duplicated class name - Jean II */
+               if(ias_opt->irda_class_name[0] == '\0') {
+                       if(self->ias_obj == NULL) {
+                               kfree(ias_opt);
+                               return -EINVAL;
+                       }
+                       ias_obj = self->ias_obj;
+               } else
+                       ias_obj = irias_find_object(ias_opt->irda_class_name);
+
+               /* Only ROOT can mess with the global IAS database.
+                * Users can only add attributes to the object associated
+                * with the socket they own - Jean II */
+               if((!capable(CAP_NET_ADMIN)) &&
+                  ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
+                       kfree(ias_opt);
+                       return -EPERM;
+               }
+
+               /* If the object doesn't exist, create it */
                if(ias_obj == (struct ias_object *) NULL) {
                        /* Create a new object */
                        ias_obj = irias_new_object(ias_opt->irda_class_name,
                                                   jiffies);
                }
 
-               /* Do we have it already ? */
+               /* Do we have the attribute already ? */
                if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) {
                        kfree(ias_opt);
                        return -EINVAL;
@@ -1927,13 +1947,28 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
                        return -EFAULT;
                }
 
-               /* Find the object we target */
-               ias_obj = irias_find_object(ias_opt->irda_class_name);
+               /* Find the object we target.
+                * If the user gives us an empty string, we use the object
+                * associated with this socket. This will workaround
+                * duplicated class name - Jean II */
+               if(ias_opt->irda_class_name[0] == '\0')
+                       ias_obj = self->ias_obj;
+               else
+                       ias_obj = irias_find_object(ias_opt->irda_class_name);
                if(ias_obj == (struct ias_object *) NULL) {
                        kfree(ias_opt);
                        return -EINVAL;
                }
 
+               /* Only ROOT can mess with the global IAS database.
+                * Users can only del attributes from the object associated
+                * with the socket they own - Jean II */
+               if((!capable(CAP_NET_ADMIN)) &&
+                  ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
+                       kfree(ias_opt);
+                       return -EPERM;
+               }
+
                /* Find the attribute (in the object) we target */
                ias_attr = irias_find_attrib(ias_obj,
                                             ias_opt->irda_attrib_name); 
@@ -2166,8 +2201,14 @@ bed:
                        return -EFAULT;
                }
 
-               /* Find the object we target */
-               ias_obj = irias_find_object(ias_opt->irda_class_name);
+               /* Find the object we target.
+                * If the user gives us an empty string, we use the object
+                * associated with this socket. This will workaround
+                * duplicated class name - Jean II */
+               if(ias_opt->irda_class_name[0] == '\0')
+                       ias_obj = self->ias_obj;
+               else
+                       ias_obj = irias_find_object(ias_opt->irda_class_name);
                if(ias_obj == (struct ias_object *) NULL) {
                        kfree(ias_opt);
                        return -EINVAL;
index 5be3ccb..7909b30 100644 (file)
@@ -800,9 +800,7 @@ static void iriap_connect_indication(void *instance, void *sap,
        new->max_header_size = max_header_size;
 
        /* Clean up the original one to keep it in listen state */
-       self->lsap->dlsap_sel = LSAP_ANY;
-       self->lsap->lsap_state = LSAP_DISCONNECTED;
-       /* FIXME: refcount in irlmp might get wrong */
+       irlmp_listen(self->lsap);
        
        iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, userdata);
 }
index b668545..aeb76ab 100644 (file)
 hashbin_t *irlap = NULL;
 int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;
 
+/* This is the delay of missed pf period before generating an event
+ * to the application. The spec mandate 3 seconds, but in some cases
+ * it's way too long. - Jean II */
+int sysctl_warn_noreply_time = 3;
+
 extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
 static void __irlap_close(struct irlap_cb *self);
 
@@ -527,22 +532,7 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
        self->discovery_cmd = discovery;
        info.discovery = discovery;
        
-       /* Check if the slot timeout is within limits */
-       if (sysctl_slot_timeout < 20) {
-               ERROR(__FUNCTION__ 
-                     "(), to low value for slot timeout!\n");
-               sysctl_slot_timeout = 20;
-       }
-       /* 
-        * Highest value is actually 8, but we allow higher since
-        * some devices seems to require it.
-        */
-       if (sysctl_slot_timeout > 160) {
-               ERROR(__FUNCTION__ 
-                     "(), to high value for slot timeout!\n");
-               sysctl_slot_timeout = 160;
-       }
-       
+       /* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */
        self->slot_timeout = sysctl_slot_timeout * HZ / 1000;
        
        irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info);
@@ -931,9 +921,6 @@ void irlap_init_qos_capabilities(struct irlap_cb *self,
        /* Set data size */
        /*self->qos_rx.data_size.bits &= 0x03;*/
 
-       /* Set disconnect time -> done properly in qos.c */
-       /*self->qos_rx.link_disc_time.bits &= 0x07;*/
-
        irda_qos_bits_to_value(&self->qos_rx);
 }
 
@@ -1070,8 +1057,11 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, int now)
        /*
         *  Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to 
         *  3 seconds otherwise. See page 71 in IrLAP for more details.
+        *  Actually, it's not always 3 seconds, as we allow to set
+        *  it via sysctl... Max maxtt is 500ms, and N1 need to be multiple
+        *  of 2, so 1 second is minimum we can allow. - Jean II
         */
-       if (self->qos_tx.link_disc_time.value == 3)
+       if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time)
                /* 
                 * If we set N1 to 0, it will trigger immediately, which is
                 * not what we want. What we really want is to disable it,
@@ -1079,7 +1069,8 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, int now)
                 */
                self->N1 = -2; /* Disable - Need to be multiple of 2*/
        else
-               self->N1 = 3000 / self->qos_rx.max_turn_time.value;
+               self->N1 = sysctl_warn_noreply_time * 1000 /
+                 self->qos_rx.max_turn_time.value;
        
        IRDA_DEBUG(4, "Setting N1 = %d\n", self->N1);
        
index 70967c6..847eb19 100644 (file)
@@ -1405,30 +1405,29 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
                }
                self->add_wait = FALSE;
 
-               if ((self->retry_count < self->N2) && 
-                   (self->retry_count != self->N1)) {
-                       
+               /* N2 is the disconnect timer. Until we reach it, we retry */
+               if (self->retry_count < self->N2) {
+                       /* Retry sending the pf bit to the secondary */
                        irlap_wait_min_turn_around(self, &self->qos_tx);
                        irlap_send_rr_frame(self, CMD_FRAME);
                        
                        irlap_start_final_timer(self, self->final_timeout);
                        self->retry_count++;
-
                        IRDA_DEBUG(4, "irlap_state_nrm_p: FINAL_TIMER_EXPIRED:"
                                   " retry_count=%d\n", self->retry_count);
-                       /* Keep state */
-               } else if (self->retry_count == self->N1) {
-                       irlap_status_indication(self, STATUS_NO_ACTIVITY);
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_rr_frame(self, CMD_FRAME);
-                       
-                       irlap_start_final_timer(self, self->final_timeout);
-                       self->retry_count++;
 
-                       IRDA_DEBUG(4, "retry count = N1; retry_count=%d\n", 
-                                  self->retry_count);
+                       /* Early warning event. I'm using a pretty liberal
+                        * interpretation of the spec and generate an event
+                        * every time the timer is multiple of N1 (and not
+                        * only the first time). This allow application
+                        * to know precisely if connectivity restart...
+                        * Jean II */
+                       if((self->retry_count % self->N1) == 0)
+                               irlap_status_indication(self,
+                                                       STATUS_NO_ACTIVITY);
+
                        /* Keep state */
-               } else if (self->retry_count >= self->N2) {
+               } else {
                        irlap_apply_default_connection_parameters(self);
 
                        /* Always switch state before calling upper layers */
@@ -1991,6 +1990,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
                 *  Wait until retry_count * n matches negotiated threshold/
                 *  disconnect time (note 2 in IrLAP p. 82)
                 *
+                * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED
                 * Note : self->wd_timeout = (self->final_timeout * 2),
                 *   which explain why we use (self->N2 / 2) here !!!
                 * Jean II
@@ -1998,16 +1998,15 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
                IRDA_DEBUG(1, __FUNCTION__ "(), retry_count = %d\n", 
                           self->retry_count);
 
-               if ((self->retry_count <  (self->N2 / 2))  && 
-                   (self->retry_count != (self->N1 / 2))) {
-                       
+               if (self->retry_count < (self->N2 / 2)) {
+                       /* No retry, just wait for primary */
                        irlap_start_wd_timer(self, self->wd_timeout);
                        self->retry_count++;
-               } else if (self->retry_count == (self->N1 / 2)) {
-                       irlap_status_indication(self, STATUS_NO_ACTIVITY);
-                       irlap_start_wd_timer(self, self->wd_timeout);
-                       self->retry_count++;
-               } else if (self->retry_count >= (self->N2 / 2)) {
+
+                       if((self->retry_count % (self->N1 / 2)) == 0)
+                               irlap_status_indication(self,
+                                                       STATUS_NO_ACTIVITY);
+               } else {
                        irlap_apply_default_connection_parameters(self);
                        
                        /* Always switch state before calling upper layers */
index 154b0e0..e056bf6 100644 (file)
@@ -469,7 +469,12 @@ void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb)
 
        IRDA_DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
                   self->slsap_sel, self->dlsap_sel);
-       
+
+       /* Note : self->lap is set in irlmp_link_data_indication(),
+        * (case CONNECT_CMD:) because we have no way to set it here.
+        * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap().
+        * Jean II */
+
        self->qos = *self->lap->qos;
 
        max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
@@ -577,7 +582,9 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
        /* Dup */
        memcpy(new, orig, sizeof(struct lsap_cb));
        new->notify.instance = instance;
-       
+       /* new->lap = orig->lap; => done in the memcpy() */
+       /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */
+
        init_timer(&new->watchdog_timer);
        
        hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, (int) new, 
index 37def77..3692a91 100644 (file)
@@ -407,12 +407,7 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
                 *  removed later and moved to the list of unconnected LSAPs
                 */
                if (HASHBIN_GET_SIZE(self->lsaps) > 0) {
-                       /* Make sure the timer has sensible value (the user
-                        * may have set it) - Jean II */
-                       if(sysctl_lap_keepalive_time < 100)     /* 100ms */
-                               sysctl_lap_keepalive_time = 100;
-                       if(sysctl_lap_keepalive_time > 10000)   /* 10s */
-                               sysctl_lap_keepalive_time = 10000;
+                       /* Timer value is checked in irsysctl - Jean II */
                        irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000);
                } else {
                        /* No more connections, so close IrLAP */
index 4afc417..08ef7b3 100644 (file)
  *     o Avoid leaking discovery log and skb
  *     o Replace "self" with "server" in irnet_connect_indication() to
  *       better detect cut'n'paste error ;-)
+ *
+ * v9 - 29.11.01 - Jean II
+ *     o Fix event generation in disconnect indication that I broke in v8
+ *       It was always generation "No-Answer" because I was testing ttp_open
+ *       just after clearing it. *blush*.
+ *     o Use newly created irttp_listen() to fix potential crash when LAP
+ *       destroyed before irnet module removed.
  */
 
 /***************************** INCLUDES *****************************/
index 9b85137..b5e5905 100644 (file)
@@ -830,8 +830,7 @@ irnet_connect_socket(irnet_socket * server,
 #endif /* STREAM_COMPAT */
 
   /* Clean up the original one to keep it in listen state */
-  server->tsap->dtsap_sel = server->tsap->lsap->dlsap_sel = LSAP_ANY;
-  server->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+  irttp_listen(server->tsap);
 
   /* Send a connection response on the new socket */
   irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL);
@@ -897,8 +896,7 @@ irnet_disconnect_server(irnet_socket *      self,
                   self->saddr, self->daddr, self->rname);
 
   /* Clean up the server to keep it in listen state */
-  self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY;
-  self->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+  irttp_listen(self->tsap);
 
   DEXIT(IRDA_SERV_TRACE, "\n");
   return;
@@ -1081,7 +1079,8 @@ irnet_disconnect_indication(void *        instance,
                            struct sk_buff *skb)
 {
   irnet_socket *       self = (irnet_socket *) instance;
-  int                  test = 0;
+  int                  test_open;
+  int                  test_connect;
 
   DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
   DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
@@ -1091,23 +1090,23 @@ irnet_disconnect_indication(void *      instance,
     dev_kfree_skb(skb);
 
   /* Prevent higher layer from accessing IrTTP */
-  test = test_and_clear_bit(0, &self->ttp_open);
+  test_open = test_and_clear_bit(0, &self->ttp_open);
   /* Not connecting anymore...
    * (note : TSAP is open, so IAP callbacks are no longer pending...) */
-  test |= test_and_clear_bit(0, &self->ttp_connect);
+  test_connect = test_and_clear_bit(0, &self->ttp_connect);
 
   /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we
    * have a race condition with irda_irnet_destroy() or
    * irnet_connect_indication(), so don't mess up tsap...
    */
-  if(!test)
+  if(!(test_open || test_connect))
     {
       DERROR(IRDA_CB_ERROR, "Race condition detected...\n");
       return;
     }
 
   /* If we were active, notify the control channel */
-  if(test_bit(0, &self->ttp_open))
+  if(test_open)
     irnet_post_event(self, IRNET_DISCONNECT_FROM,
                     self->saddr, self->daddr, self->rname);
   else
index 4a029a9..7a73ad8 100644 (file)
@@ -10,6 +10,7 @@
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
 #include <net/irda/irias_object.h>
 
 #define NET_IRDA 412 /* Random number */
-enum { DISCOVERY=1, DEVNAME, DEBUG, SLOTS, DISCOVERY_TIMEOUT, 
-       SLOT_TIMEOUT, MAX_BAUD_RATE, MAX_INACTIVE_TIME, LAP_KEEPALIVE_TIME, };
+enum { DISCOVERY=1, DEVNAME, DEBUG, FAST_POLL, DISCOVERY_SLOTS,
+       DISCOVERY_TIMEOUT, SLOT_TIMEOUT, MAX_BAUD_RATE, MIN_TX_TURN_TIME,
+       MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, LAP_KEEPALIVE_TIME };
 
 extern int  sysctl_discovery;
 extern int  sysctl_discovery_slots;
 extern int  sysctl_discovery_timeout;
-extern int  sysctl_slot_timeout;
+extern int  sysctl_slot_timeout;       /* Candidate */
 extern int  sysctl_fast_poll_increase;
 int         sysctl_compression = 0;
 extern char sysctl_devname[];
 extern int  sysctl_max_baud_rate;
-extern int  sysctl_max_inactive_time;
+extern int  sysctl_min_tx_turn_time;
+extern int  sysctl_max_noreply_time;
+extern int  sysctl_warn_noreply_time;
 extern int  sysctl_lap_keepalive_time;
 
 #ifdef CONFIG_IRDA_DEBUG
 extern unsigned int irda_debug;
 #endif
 
+/* this is needed for the proc_dointvec_minmax - Jean II */
+static int max_discovery_slots = 16;           /* ??? */
+static int min_discovery_slots = 1;
+/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices
+ * seems to require it. (from Dag's comment) */
+static int max_slot_timeout = 160;
+static int min_slot_timeout = 20;
+static int max_max_baud_rate = 16000000;       /* See qos.c - IrLAP spec */
+static int min_max_baud_rate = 2400;
+static int max_min_tx_turn_time = 10000;       /* See qos.c - IrLAP spec */
+static int min_min_tx_turn_time = 0;
+static int max_max_noreply_time = 40;          /* See qos.c - IrLAP spec */
+static int min_max_noreply_time = 3;
+static int max_warn_noreply_time = 3;          /* 3s == standard */
+static int min_warn_noreply_time = 1;          /* 1s == min WD_TIMER */
+static int max_lap_keepalive_time = 10000;     /* 10s */
+static int min_lap_keepalive_time = 100;       /* 100us */
+/* For other sysctl, I've no idea of the range. Maybe Dag could help
+ * us on that - Jean II */
+
 static int do_devname(ctl_table *table, int write, struct file *filp,
                      void *buffer, size_t *lenp)
 {
@@ -77,21 +101,32 @@ static ctl_table irda_table[] = {
          sizeof(int), 0644, NULL, &proc_dointvec },
 #endif
 #ifdef CONFIG_IRDA_FAST_RR
-        { SLOTS, "fast_poll_increase", &sysctl_fast_poll_increase,
+        { FAST_POLL, "fast_poll_increase", &sysctl_fast_poll_increase,
          sizeof(int), 0644, NULL, &proc_dointvec },
 #endif
-       { SLOTS, "discovery_slots", &sysctl_discovery_slots,
-         sizeof(int), 0644, NULL, &proc_dointvec },
+       { DISCOVERY_SLOTS, "discovery_slots", &sysctl_discovery_slots,
+         sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+         NULL, &min_discovery_slots, &max_discovery_slots },
        { DISCOVERY_TIMEOUT, "discovery_timeout", &sysctl_discovery_timeout,
          sizeof(int), 0644, NULL, &proc_dointvec },
        { SLOT_TIMEOUT, "slot_timeout", &sysctl_slot_timeout,
-         sizeof(int), 0644, NULL, &proc_dointvec },
+         sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+         NULL, &min_slot_timeout, &max_slot_timeout },
        { MAX_BAUD_RATE, "max_baud_rate", &sysctl_max_baud_rate,
-         sizeof(int), 0644, NULL, &proc_dointvec },
-       { MAX_INACTIVE_TIME, "max_inactive_time", &sysctl_max_inactive_time,
-         sizeof(int), 0644, NULL, &proc_dointvec },
+         sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+         NULL, &min_max_baud_rate, &max_max_baud_rate },
+       { MIN_TX_TURN_TIME, "min_tx_turn_time", &sysctl_min_tx_turn_time,
+         sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+         NULL, &min_min_tx_turn_time, &max_min_tx_turn_time },
+       { MAX_NOREPLY_TIME, "max_noreply_time", &sysctl_max_noreply_time,
+         sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+         NULL, &min_max_noreply_time, &max_max_noreply_time },
+       { WARN_NOREPLY_TIME, "warn_noreply_time", &sysctl_warn_noreply_time,
+         sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+         NULL, &min_warn_noreply_time, &max_warn_noreply_time },
        { LAP_KEEPALIVE_TIME, "lap_keepalive_time", &sysctl_lap_keepalive_time,
-         sizeof(int), 0644, NULL, &proc_dointvec },
+         sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+         NULL, &min_lap_keepalive_time, &max_lap_keepalive_time },
        { 0 }
 };
 
index c0422af..d597937 100644 (file)
@@ -51,7 +51,16 @@ int sysctl_max_baud_rate = 16000000;
  * may want to keep the LAP alive longuer or shorter in case of link failure.
  * Remember that the threshold time (early warning) is fixed to 3s...
  */
-int sysctl_max_inactive_time = 12;
+int sysctl_max_noreply_time = 12;
+/*
+ * Minimum turn time to be applied before transmitting to the peer.
+ * Nonzero values (usec) are used as lower limit to the per-connection
+ * mtt value which was announced by the other end during negotiation.
+ * Might be helpful if the peer device provides too short mtt.
+ * Default is 10 which means using the unmodified value given by the peer
+ * except if it's 0 (0 is likely a bug in the other stack).
+ */
+unsigned sysctl_min_tx_turn_time = 10;
 
 static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
 static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, 
@@ -184,7 +193,6 @@ static inline __u32 byte_value(__u8 byte, __u32 *array)
  * Function value_lower_bits (value, array)
  *
  *    Returns a bit field marking all possibility lower than value.
- *    We may need a "value_higher_bits" in the future...
  */
 static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field)
 {
@@ -207,6 +215,33 @@ static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *f
        return i;
 }
 
+/*
+ * Function value_highest_bit (value, array)
+ *
+ *    Returns a bit field marking the highest possibility lower than value.
+ */
+static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field)
+{
+       int     i;
+       __u16   mask = 0x1;
+       __u16   result = 0x0;
+
+       for (i=0; i < size; i++) {
+               /* Finished ? */
+               if (array[i] <= value)
+                       break;
+               /* Shift mask */
+               mask <<= 1;
+       }
+       /* Set the current value to the bit field */
+       result |= mask;
+       /* Send back a valid index */
+       if(i >= size)
+         i = size - 1; /* Last item */
+       *field = result;
+       return i;
+}
+
 /* -------------------------- MAIN CALLS -------------------------- */
 
 /*
@@ -254,9 +289,9 @@ void irda_init_max_qos_capabilies(struct qos_info *qos)
        sysctl_max_baud_rate = index_value(i, baud_rates);
 
        /* Set configured max disc time */
-       i = value_lower_bits(sysctl_max_inactive_time, link_disc_times, 8,
+       i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8,
                             &qos->link_disc_time.bits);
-       sysctl_max_inactive_time = index_value(i, link_disc_times);
+       sysctl_max_noreply_time = index_value(i, link_disc_times);
 
        /* LSB is first byte, MSB is second byte */
        qos->baud_rate.bits    &= 0x03ff;
@@ -282,6 +317,19 @@ void irlap_adjust_qos_settings(struct qos_info *qos)
 
        IRDA_DEBUG(2, __FUNCTION__ "()\n");
 
+       /*
+        * Make sure the mintt is sensible.
+        */
+       if (sysctl_min_tx_turn_time > qos->min_turn_time.value) {
+               int i;
+
+               /* We don't really need bits, but easier this way */
+               i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times,
+                                     8, &qos->min_turn_time.bits);
+               sysctl_min_tx_turn_time = index_value(i, min_turn_times);
+               qos->min_turn_time.value = sysctl_min_tx_turn_time;
+       }
+
        /* 
         * Not allowed to use a max turn time less than 500 ms if the baudrate
         * is less than 115200