]> git.neil.brown.name Git - history.git/commitdiff
Make main-title more concise.
authorDavid Mosberger <davidm@wailua.hpl.hp.com>
Wed, 13 Mar 2002 16:28:41 +0000 (08:28 -0800)
committerDavid Mosberger <davidm@wailua.hpl.hp.com>
Wed, 13 Mar 2002 16:28:41 +0000 (08:28 -0800)
Rename "General setup" to "Processor type and features".
Move ACPI types after the point where HP_SIM gets defined.
Pick up HP Ski configuration options from arch/ia64/hp/Config.in.

16 files changed:
arch/ia64/config.in
arch/ia64/defconfig
arch/ia64/hp/Makefile
arch/ia64/hp/simeth.c [new file with mode: 0644]
arch/ia64/hp/simscsi.c [new file with mode: 0644]
arch/ia64/hp/simscsi.h [new file with mode: 0644]
arch/ia64/hp/simserial.c [new file with mode: 0644]
arch/ia64/kernel/smp.c
arch/ia64/kernel/unaligned.c
arch/ia64/mm/fault.c
arch/ia64/mm/init.c
arch/ia64/vmlinux.lds.S
include/asm-ia64/bitops.h
include/asm-ia64/mman.h
include/asm-ia64/system.h
include/asm-ia64/uaccess.h

index c3405697cced613e1899d9adef0206187966c00f..7baef59260a39f1f977ad92ce05796894556f51e 100644 (file)
@@ -1,9 +1,9 @@
-mainmenu_name "Kernel configuration of Linux for IA-64 machines"
+mainmenu_name "IA-64 Linux Kernel Configuration"
 
 source init/Config.in
 
 mainmenu_option next_comment
-comment 'General setup'
+comment 'Processor type and features'
 
 define_bool CONFIG_IA64 y
 
@@ -14,13 +14,6 @@ define_bool CONFIG_SBUS n
 define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
 define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
 
-if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then
-  define_bool CONFIG_ACPI y
-  define_bool CONFIG_ACPI_EFI y
-  define_bool CONFIG_ACPI_INTERPRETER y
-  define_bool CONFIG_ACPI_KERNEL_CONFIG y
-fi
-
 choice 'IA-64 processor type' \
        "Itanium                        CONFIG_ITANIUM \
         McKinley                       CONFIG_MCKINLEY" Itanium
@@ -38,6 +31,13 @@ choice 'Kernel page size'                                            \
         16KB                   CONFIG_IA64_PAGE_SIZE_16KB              \
         64KB                   CONFIG_IA64_PAGE_SIZE_64KB" 16KB
 
+if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then
+  define_bool CONFIG_ACPI y
+  define_bool CONFIG_ACPI_EFI y
+  define_bool CONFIG_ACPI_INTERPRETER y
+  define_bool CONFIG_ACPI_KERNEL_CONFIG y
+fi
+
 if [ "$CONFIG_ITANIUM" = "y" ]; then
        define_bool CONFIG_IA64_BRL_EMU y
        bool '  Enable Itanium B-step specific code' CONFIG_ITANIUM_BSTEP_SPECIFIC
@@ -239,15 +239,7 @@ fi
 fi # !HP_SIM
 
 if [ "$CONFIG_IA64_HP_SIM" != "n" -o "$CONFIG_IA64_GENERIC" != "n" ]; then
-  mainmenu_option next_comment
-  comment 'Simulated drivers'
-
-  bool 'Simulated Ethernet ' CONFIG_SIMETH
-  bool 'Simulated serial driver support' CONFIG_SIM_SERIAL
-  if [ "$CONFIG_SCSI" != "n" ]; then
-    bool 'Simulated SCSI disk' CONFIG_SCSI_SIM
-  fi
-  endmenu
+  source arch/ia64/hp/Config.in
 fi
 
 
@@ -269,15 +261,6 @@ if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then
    bool '  Spinlock debugging' CONFIG_DEBUG_SPINLOCK
    bool '  Turn on compare-and-exchange bug checking (slow!)' CONFIG_IA64_DEBUG_CMPXCHG
    bool '  Turn on irq debug checks (slow!)' CONFIG_IA64_DEBUG_IRQ
-   bool '  Built-in Kernel Debugger support' CONFIG_KDB
-   dep_tristate '    KDB modules' CONFIG_KDB_MODULES $CONFIG_KDB
-   if [ "$CONFIG_KDB" = "y" ]; then
-      bool '    KDB off by default' CONFIG_KDB_OFF
-      comment '  Load all symbols for debugging is required for KDB'
-      define_bool CONFIG_KALLSYMS y
-   else
-      bool '  Load all symbols for debugging' CONFIG_KALLSYMS
-   fi
 fi
 
 endmenu
index 552271dc325cdb43b2333bbd63b82dacddf72a6a..2544fd6f7881bce4672c3e47c59f2d33976de3bb 100644 (file)
@@ -183,7 +183,7 @@ CONFIG_BLK_DEV_LOOP=y
 CONFIG_IDE=y
 
 #
-# IDE, ATA and ATAPI Block devices
+# ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
 
@@ -218,16 +218,14 @@ CONFIG_BLK_DEV_IDESCSI=y
 # CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
 CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
 CONFIG_IDEPCI_SHARE_IRQ=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
-# CONFIG_BLK_DEV_OFFBOARD is not set
 # CONFIG_IDEDMA_PCI_AUTO is not set
 # CONFIG_IDEDMA_ONLYDISK is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_PCI_WIP is not set
-# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set
 # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
-CONFIG_BLK_DEV_ADMA=y
 # CONFIG_BLK_DEV_AEC62XX is not set
 # CONFIG_AEC62XX_TUNING is not set
 # CONFIG_BLK_DEV_ALI15X3 is not set
@@ -254,8 +252,8 @@ CONFIG_BLK_DEV_PIIX=y
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
 # CONFIG_IDE_CHIPSETS is not set
-# CONFIG_IDEDMA_AUTO is not set
 # CONFIG_IDEDMA_IVB is not set
+# CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
 CONFIG_BLK_DEV_IDE_MODES=y
 # CONFIG_BLK_DEV_ATARAID is not set
@@ -817,6 +815,7 @@ CONFIG_USB_UHCI=m
 # USB Device Class drivers
 #
 # CONFIG_USB_AUDIO is not set
+# CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
index e151251ce77421e1b5f71f9a763c983ea7045cda..642a03c2d488fb316c4a0c84cd70e56097a96524 100644 (file)
@@ -1,6 +1,8 @@
 #
 # ia64/platform/hp/Makefile
 #
+# Copyright (C) 2002 Hewlett-Packard Co.
+#      David Mosberger-Tang <davidm@hpl.hp.com>
 # Copyright (C) 1999 Silicon Graphics, Inc.
 # Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com)
 #
@@ -12,6 +14,10 @@ O_TARGET := hp.a
 obj-y := hpsim_console.o hpsim_irq.o hpsim_setup.o
 obj-$(CONFIG_IA64_GENERIC) += hpsim_machvec.o
 
+obj-$(CONFIG_SIMETH)   += simeth.o
+obj-$(CONFIG_SIM_SERIAL) += simserial.o
+obj-$(CONFIG_SCSI_SIM) += simscsi.o
+
 clean::
 
 include $(TOPDIR)/Rules.make
diff --git a/arch/ia64/hp/simeth.c b/arch/ia64/hp/simeth.c
new file mode 100644 (file)
index 0000000..742631f
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Simulated Ethernet Driver
+ *
+ * Copyright (C) 1999-2001 Hewlett-Packard Co
+ *     Stephane Eranian <eranian@hpl.hp.com>
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/notifier.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#define SIMETH_RECV_MAX        10
+
+/*
+ * Maximum possible received frame for Ethernet.
+ * We preallocate an sk_buff of that size to avoid costly
+ * memcpy for temporary buffer into sk_buff. We do basically
+ * what's done in other drivers, like eepro with a ring.
+ * The difference is, of course, that we don't have real DMA !!!
+ */
+#define SIMETH_FRAME_SIZE      ETH_FRAME_LEN
+
+
+#define SSC_NETDEV_PROBE               100
+#define SSC_NETDEV_SEND                        101
+#define SSC_NETDEV_RECV                        102
+#define SSC_NETDEV_ATTACH              103
+#define SSC_NETDEV_DETACH              104
+
+#define NETWORK_INTR                   8
+
+struct simeth_local {
+       struct net_device_stats stats;
+       int                     simfd;   /* descriptor in the simulator */
+};
+
+static int simeth_probe1(void);
+static int simeth_open(struct net_device *dev);
+static int simeth_close(struct net_device *dev);
+static int simeth_tx(struct sk_buff *skb, struct net_device *dev);
+static int simeth_rx(struct net_device *dev);
+static struct net_device_stats *simeth_get_stats(struct net_device *dev);
+static void simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+static void set_multicast_list(struct net_device *dev);
+static int simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr);
+
+static char *simeth_version="0.3";
+
+/*
+ * This variable is used to establish a mapping between the Linux/ia64 kernel
+ * and the host linux kernel.
+ *
+ * As of today, we support only one card, even though most of the code
+ * is ready for many more. The mapping is then:
+ *     linux/ia64 -> linux/x86
+ *        eth0    -> eth1
+ *
+ * In the future, we some string operations, we could easily support up
+ * to 10 cards (0-9).
+ *
+ * The default mapping can be changed on the kernel command line by
+ * specifying simeth=ethX (or whatever string you want).
+ */
+static char *simeth_device="eth0";      /* default host interface to use */
+
+
+
+static volatile unsigned int card_count; /* how many cards "found" so far */
+static int simeth_debug;               /* set to 1 to get debug information */
+
+/*
+ * Used to catch IFF_UP & IFF_DOWN events
+ */
+static struct notifier_block simeth_dev_notifier = {
+       simeth_device_event,
+       0
+};
+
+
+/*
+ * Function used when using a kernel command line option.
+ *
+ * Format: simeth=interface_name (like eth0)
+ */
+static int __init
+simeth_setup(char *str)
+{
+       simeth_device = str;
+       return 1;
+}
+
+__setup("simeth=", simeth_setup);
+
+/*
+ * Function used to probe for simeth devices when not installed
+ * as a loadable module
+ */
+
+int __init
+simeth_probe (void)
+{
+       int r;
+
+       printk("simeth: v%s\n", simeth_version);
+
+       r = simeth_probe1();
+
+       if (r == 0) register_netdevice_notifier(&simeth_dev_notifier);
+
+       return r;
+}
+
+extern long ia64_ssc (long, long, long, long, int);
+extern void ia64_ssc_connect_irq (long intr, long irq);
+
+static inline int
+netdev_probe(char *name, unsigned char *ether)
+{
+       return ia64_ssc(__pa(name), __pa(ether), 0,0, SSC_NETDEV_PROBE);
+}
+
+
+static inline int
+netdev_connect(int irq)
+{
+       /* XXX Fix me
+        * this does not support multiple cards
+        * also no return value
+        */
+       ia64_ssc_connect_irq(NETWORK_INTR, irq);
+       return 0;
+}
+
+static inline int
+netdev_attach(int fd, int irq, unsigned int ipaddr)
+{
+       /* this puts the host interface in the right mode (start interupting) */
+       return ia64_ssc(fd, ipaddr, 0,0, SSC_NETDEV_ATTACH);
+}
+
+
+static inline int
+netdev_detach(int fd)
+{
+       /*
+        * inactivate the host interface (don't interrupt anymore) */
+       return ia64_ssc(fd, 0,0,0, SSC_NETDEV_DETACH);
+}
+
+static inline int
+netdev_send(int fd, unsigned char *buf, unsigned int len)
+{
+       return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_SEND);
+}
+
+static inline int
+netdev_read(int fd, unsigned char *buf, unsigned int len)
+{
+       return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_RECV);
+}
+
+/*
+ * Function shared with module code, so cannot be in init section
+ *
+ * So far this function "detects" only one card (test_&_set) but could
+ * be extended easily.
+ *
+ * Return:
+ *     - -ENODEV is no device found
+ *     - -ENOMEM is no more memory
+ *     - 0 otherwise
+ */
+static int
+simeth_probe1(void)
+{
+       unsigned char mac_addr[ETH_ALEN];
+       struct simeth_local *local;
+       struct net_device *dev;
+       int fd, i;
+
+       /*
+        * XXX Fix me
+        * let's support just one card for now
+        */
+       if (test_and_set_bit(0, &card_count))
+               return -ENODEV;
+
+       /*
+        * check with the simulator for the device
+        */
+       fd = netdev_probe(simeth_device, mac_addr);
+       if (fd == -1)
+               return -ENODEV;
+
+       dev = init_etherdev(NULL, sizeof(struct simeth_local));
+       if (!dev)
+               return -ENOMEM;
+
+       memcpy(dev->dev_addr, mac_addr, sizeof(mac_addr));
+
+       dev->irq = ia64_alloc_irq();
+
+       /*
+        * attach the interrupt in the simulator, this does enable interrupts
+        * until a netdev_attach() is called
+        */
+       netdev_connect(dev->irq);
+
+       memset(dev->priv, 0, sizeof(struct simeth_local));
+
+       local = dev->priv;
+       local->simfd = fd; /* keep track of underlying file descriptor */
+
+       dev->open               = simeth_open;
+       dev->stop               = simeth_close;
+       dev->hard_start_xmit    = simeth_tx;
+       dev->get_stats          = simeth_get_stats;
+       dev->set_multicast_list = set_multicast_list; /* no yet used */
+
+       /* Fill in the fields of the device structure with ethernet-generic values. */
+       ether_setup(dev);
+
+       printk("%s: hosteth=%s simfd=%d, HwAddr", dev->name, simeth_device, local->simfd);
+       for(i = 0; i < ETH_ALEN; i++) {
+               printk(" %2.2x", dev->dev_addr[i]);
+       }
+       printk(", IRQ %d\n", dev->irq);
+
+               return 0;
+}
+
+/*
+ * actually binds the device to an interrupt vector
+ */
+static int
+simeth_open(struct net_device *dev)
+{
+       if (request_irq(dev->irq, simeth_interrupt, 0, "simeth", dev)) {
+               printk ("simeth: unable to get IRQ %d.\n", dev->irq);
+               return -EAGAIN;
+       }
+
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+/* copied from lapbether.c */
+static __inline__ int dev_is_ethdev(struct net_device *dev)
+{
+       return ( dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5));
+}
+
+
+/*
+ * Handler for IFF_UP or IFF_DOWN
+ *
+ * The reason for that is that we don't want to be interrupted when the
+ * interface is down. There is no way to unconnect in the simualtor. Instead
+ * we use this function to shutdown packet processing in the frame filter
+ * in the simulator. Thus no interrupts are generated
+ *
+ *
+ * That's also the place where we pass the IP address of this device to the
+ * simulator so that that we can start filtering packets for it
+ *
+ * There may be a better way of doing this, but I don't know which yet.
+ */
+static int
+simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+       struct net_device *dev = (struct net_device *)ptr;
+       struct simeth_local *local;
+       struct in_device *in_dev;
+       struct in_ifaddr **ifap = NULL;
+       struct in_ifaddr *ifa = NULL;
+       int r;
+
+
+       if ( ! dev ) {
+               printk(KERN_WARNING "simeth_device_event dev=0\n");
+               return NOTIFY_DONE;
+       }
+
+       if ( event != NETDEV_UP && event != NETDEV_DOWN ) return NOTIFY_DONE;
+
+       /*
+        * Check whether or not it's for an ethernet device
+        *
+        * XXX Fixme: This works only as long as we support one
+        * type of ethernet device.
+        */
+       if ( !dev_is_ethdev(dev) ) return NOTIFY_DONE;
+
+       if ((in_dev=dev->ip_ptr) != NULL) {
+               for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)
+                       if (strcmp(dev->name, ifa->ifa_label) == 0) break;
+       }
+       if ( ifa == NULL ) {
+               printk("simeth_open: can't find device %s's ifa\n", dev->name);
+               return NOTIFY_DONE;
+       }
+
+       printk("simeth_device_event: %s ipaddr=0x%x\n", dev->name, htonl(ifa->ifa_local));
+
+       /*
+        * XXX Fix me
+        * if the device was up, and we're simply reconfiguring it, not sure
+        * we get DOWN then UP.
+        */
+
+       local = dev->priv;
+       /* now do it for real */
+       r = event == NETDEV_UP ?
+               netdev_attach(local->simfd, dev->irq, htonl(ifa->ifa_local)):
+               netdev_detach(local->simfd);
+
+       printk("simeth: netdev_attach/detach: event=%s ->%d\n", event == NETDEV_UP ? "attach":"detach", r);
+
+       return NOTIFY_DONE;
+}
+
+static int
+simeth_close(struct net_device *dev)
+{
+       netif_stop_queue(dev);
+
+       free_irq(dev->irq, dev);
+
+       return 0;
+}
+
+/*
+ * Only used for debug
+ */
+static void
+frame_print(unsigned char *from, unsigned char *frame, int len)
+{
+       int i;
+
+       printk("%s: (%d) %02x", from, len, frame[0] & 0xff);
+       for(i=1; i < 6; i++ ) {
+               printk(":%02x", frame[i] &0xff);
+       }
+       printk(" %2x", frame[6] &0xff);
+       for(i=7; i < 12; i++ ) {
+               printk(":%02x", frame[i] &0xff);
+       }
+       printk(" [%02x%02x]\n", frame[12], frame[13]);
+
+       for(i=14; i < len; i++ ) {
+               printk("%02x ", frame[i] &0xff);
+               if ( (i%10)==0) printk("\n");
+       }
+       printk("\n");
+}
+
+
+/*
+ * Function used to transmit of frame, very last one on the path before
+ * going to the simulator.
+ */
+static int
+simeth_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct simeth_local *local = (struct simeth_local *)dev->priv;
+
+#if 0
+       /* ensure we have at least ETH_ZLEN bytes (min frame size) */
+       unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+       /* Where do the extra padding bytes comes from inthe skbuff ? */
+#else
+       /* the real driver in the host system is going to take care of that
+        * or maybe it's the NIC itself.
+        */
+       unsigned int length = skb->len;
+#endif
+
+       local->stats.tx_bytes += skb->len;
+       local->stats.tx_packets++;
+
+
+       if (simeth_debug > 5) frame_print("simeth_tx", skb->data, length);
+
+       netdev_send(local->simfd, skb->data, length);
+
+       /*
+        * we are synchronous on write, so we don't simulate a
+        * trasnmit complete interrupt, thus we don't need to arm a tx
+        */
+
+       dev_kfree_skb(skb);
+       return 0;
+}
+
+static inline struct sk_buff *
+make_new_skb(struct net_device *dev)
+{
+       struct sk_buff *nskb;
+
+       /*
+        * The +2 is used to make sure that the IP header is nicely
+        * aligned (on 4byte boundary I assume 14+2=16)
+        */
+       nskb = dev_alloc_skb(SIMETH_FRAME_SIZE + 2);
+       if ( nskb == NULL ) {
+               printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
+               return NULL;
+       }
+       nskb->dev = dev;
+
+       skb_reserve(nskb, 2);   /* Align IP on 16 byte boundaries */
+
+       skb_put(nskb,SIMETH_FRAME_SIZE);
+
+       return nskb;
+}
+
+/*
+ * called from interrupt handler to process a received frame
+ */
+static int
+simeth_rx(struct net_device *dev)
+{
+       struct simeth_local     *local;
+       struct sk_buff          *skb;
+       int                     len;
+       int                     rcv_count = SIMETH_RECV_MAX;
+
+       local = (struct simeth_local *)dev->priv;
+       /*
+        * the loop concept has been borrowed from other drivers
+        * looks to me like it's a throttling thing to avoid pushing to many
+        * packets at one time into the stack. Making sure we can process them
+        * upstream and make forward progress overall
+        */
+       do {
+               if ( (skb=make_new_skb(dev)) == NULL ) {
+                       printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
+                       local->stats.rx_dropped++;
+                       return 0;
+               }
+               /*
+                * Read only one frame at a time
+                */
+               len = netdev_read(local->simfd, skb->data, SIMETH_FRAME_SIZE);
+               if ( len == 0 ) {
+                       if ( simeth_debug > 0 ) printk(KERN_WARNING "%s: count=%d netdev_read=0\n", dev->name, SIMETH_RECV_MAX-rcv_count);
+                       break;
+               }
+#if 0
+               /*
+                * XXX Fix me
+                * Should really do a csum+copy here
+                */
+               memcpy(skb->data, frame, len);
+#endif
+               skb->protocol = eth_type_trans(skb, dev);
+
+               if ( simeth_debug > 6 ) frame_print("simeth_rx", skb->data, len);
+
+               /*
+                * push the packet up & trigger software interrupt
+                */
+               netif_rx(skb);
+
+               local->stats.rx_packets++;
+               local->stats.rx_bytes += len;
+
+       } while ( --rcv_count );
+
+       return len; /* 0 = nothing left to read, otherwise, we can try again */
+}
+
+/*
+ * Interrupt handler (Yes, we can do it too !!!)
+ */
+static void
+simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct net_device *dev = dev_id;
+
+       if ( dev == NULL ) {
+               printk(KERN_WARNING "simeth: irq %d for unknown device\n", irq);
+               return;
+       }
+
+       /*
+        * very simple loop because we get interrupts only when receving
+        */
+       while (simeth_rx(dev));
+}
+
+static struct net_device_stats *
+simeth_get_stats(struct net_device *dev)
+{
+       struct simeth_local  *local = (struct simeth_local *) dev->priv;
+
+       return &local->stats;
+}
+
+/* fake multicast ability */
+static void
+set_multicast_list(struct net_device *dev)
+{
+       printk(KERN_WARNING "%s: set_multicast_list called\n", dev->name);
+}
+
+#ifdef CONFIG_NET_FASTROUTE
+static int
+simeth_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
+{
+       printk(KERN_WARNING "%s: simeth_accept_fastpath called\n", dev->name);
+       return -1;
+}
+#endif
+
+__initcall(simeth_probe);
diff --git a/arch/ia64/hp/simscsi.c b/arch/ia64/hp/simscsi.c
new file mode 100644 (file)
index 0000000..e387c19
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Simulated SCSI driver.
+ *
+ * Copyright (C) 1999, 2001-2002 Hewlett-Packard Co
+ *     David Mosberger-Tang <davidm@hpl.hp.com>
+ *     Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * 02/01/15 David Mosberger    Updated for v2.5.1
+ * 99/12/18 David Mosberger    Added support for READ10/WRITE10 needed by linux v2.3.33
+ */
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+
+#include <scsi/scsi.h>
+
+#include <asm/irq.h>
+
+#include "../drivers/scsi/scsi.h"
+#include "../drivers/scsi/sd.h"
+#include "../drivers/scsi/hosts.h"
+#include "simscsi.h"
+
+#define DEBUG_SIMSCSI  1
+
+/* Simulator system calls: */
+
+#define SSC_OPEN                       50
+#define SSC_CLOSE                      51
+#define SSC_READ                       52
+#define SSC_WRITE                      53
+#define SSC_GET_COMPLETION             54
+#define SSC_WAIT_COMPLETION            55
+
+#define SSC_WRITE_ACCESS               2
+#define SSC_READ_ACCESS                        1
+
+#if DEBUG_SIMSCSI
+  int simscsi_debug;
+# define DBG   simscsi_debug
+#else
+# define DBG   0
+#endif
+
+static struct Scsi_Host *host;
+
+static void simscsi_interrupt (unsigned long val);
+DECLARE_TASKLET(simscsi_tasklet, simscsi_interrupt, 0);
+
+struct disk_req {
+       unsigned long addr;
+       unsigned len;
+};
+
+struct disk_stat {
+       int fd;
+       unsigned count;
+};
+
+extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr);
+
+static int desc[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static struct queue_entry {
+       Scsi_Cmnd *sc;
+} queue[SIMSCSI_REQ_QUEUE_LEN];
+
+static int rd, wr;
+static atomic_t num_reqs = ATOMIC_INIT(0);
+
+/* base name for default disks */
+static char *simscsi_root = DEFAULT_SIMSCSI_ROOT;
+
+#define MAX_ROOT_LEN   128
+
+/*
+ * used to setup a new base for disk images
+ * to use /foo/bar/disk[a-z] as disk images
+ * you have to specify simscsi=/foo/bar/disk on the command line
+ */
+static int __init
+simscsi_setup (char *s)
+{
+       /* XXX Fix me we may need to strcpy() ? */
+       if (strlen(s) > MAX_ROOT_LEN) {
+               printk("simscsi_setup: prefix too long---using default %s\n", simscsi_root);
+       }
+       simscsi_root = s;
+       return 1;
+}
+
+__setup("simscsi=", simscsi_setup);
+
+static void
+simscsi_interrupt (unsigned long val)
+{
+       Scsi_Cmnd *sc;
+
+       while ((sc = queue[rd].sc) != 0) {
+               atomic_dec(&num_reqs);
+               queue[rd].sc = 0;
+               if (DBG)
+                       printk("simscsi_interrupt: done with %ld\n", sc->serial_number);
+               (*sc->scsi_done)(sc);
+               rd = (rd + 1) % SIMSCSI_REQ_QUEUE_LEN;
+       }
+}
+
+int
+simscsi_detect (Scsi_Host_Template *templ)
+{
+       templ->proc_name = "simscsi";
+       host = scsi_register(templ, 0);
+       return 1;       /* fake one SCSI host adapter */
+}
+
+int
+simscsi_release (struct Scsi_Host *host)
+{
+       return 0;       /* this is easy...  */
+}
+
+const char *
+simscsi_info (struct Scsi_Host *host)
+{
+       return "simulated SCSI host adapter";
+}
+
+int
+simscsi_abort (Scsi_Cmnd *cmd)
+{
+       printk ("simscsi_abort: unimplemented\n");
+       return SCSI_ABORT_SUCCESS;
+}
+
+int
+simscsi_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+       printk ("simscsi_reset: unimplemented\n");
+       return SCSI_RESET_SUCCESS;
+}
+
+int
+simscsi_biosparam (Disk *disk, kdev_t n, int ip[])
+{
+       int size = disk->capacity;
+
+       ip[0] = 64;
+       ip[1] = 32;
+       ip[2] = size >> 11;
+       return 0;
+}
+
+static void
+simscsi_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset, unsigned long len)
+{
+       struct disk_stat stat;
+       struct disk_req req;
+
+       req.addr = __pa(sc->request_buffer);
+       req.len  = len;                 /* # of bytes to transfer */
+
+       if (sc->request_bufflen < req.len)
+               return;
+
+       stat.fd = desc[sc->target];
+       if (DBG)
+               printk("simscsi_%s @ %lx (off %lx)\n",
+                      mode == SSC_READ ? "read":"write", req.addr, offset);
+       ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
+       ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
+
+       if (stat.count == req.len) {
+               sc->result = GOOD;
+       } else {
+               sc->result = DID_ERROR << 16;
+       }
+}
+
+static void
+simscsi_sg_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset)
+{
+       int list_len = sc->use_sg;
+       struct scatterlist *sl = (struct scatterlist *)sc->buffer;
+       struct disk_stat stat;
+       struct disk_req req;
+
+       stat.fd = desc[sc->target];
+
+       while (list_len) {
+               req.addr = __pa(page_address(sl->page) + sl->offset);
+               req.len  = sl->length;
+               if (DBG)
+                       printk("simscsi_sg_%s @ %lx (off %lx) use_sg=%d len=%d\n",
+                              mode == SSC_READ ? "read":"write", req.addr, offset,
+                              list_len, sl->length);
+               ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
+               ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
+
+               /* should not happen in our case */
+               if (stat.count != req.len) {
+                       sc->result = DID_ERROR << 16;
+                       return;
+               }
+               offset +=  sl->length;
+               sl++;
+               list_len--;
+       }
+       sc->result = GOOD;
+}
+
+/*
+ * function handling both READ_6/WRITE_6 (non-scatter/gather mode)
+ * commands.
+ * Added 02/26/99 S.Eranian
+ */
+static void
+simscsi_readwrite6 (Scsi_Cmnd *sc, int mode)
+{
+       unsigned long offset;
+
+       offset = (((sc->cmnd[1] & 0x1f) << 16) | (sc->cmnd[2] << 8) | sc->cmnd[3])*512;
+       if (sc->use_sg > 0)
+               simscsi_sg_readwrite(sc, mode, offset);
+       else
+               simscsi_readwrite(sc, mode, offset, sc->cmnd[4]*512);
+}
+
+
+static void
+simscsi_readwrite10 (Scsi_Cmnd *sc, int mode)
+{
+       unsigned long offset;
+
+       offset = (  (sc->cmnd[2] << 24) | (sc->cmnd[3] << 16)
+                 | (sc->cmnd[4] <<  8) | (sc->cmnd[5] <<  0))*512;
+       if (sc->use_sg > 0)
+               simscsi_sg_readwrite(sc, mode, offset);
+       else
+               simscsi_readwrite(sc, mode, offset, ((sc->cmnd[7] << 8) | sc->cmnd[8])*512);
+}
+
+int
+simscsi_queuecommand (Scsi_Cmnd *sc, void (*done)(Scsi_Cmnd *))
+{
+       char fname[MAX_ROOT_LEN+16];
+       char *buf;
+#if DEBUG_SIMSCSI
+       register long sp asm ("sp");
+
+       if (DBG)
+               printk("simscsi_queuecommand: target=%d,cmnd=%u,sc=%lu,sp=%lx,done=%p\n",
+                      sc->target, sc->cmnd[0], sc->serial_number, sp, done);
+#endif
+
+       sc->result = DID_BAD_TARGET << 16;
+       sc->scsi_done = done;
+       if (sc->target <= 7 && sc->lun == 0) {
+               switch (sc->cmnd[0]) {
+                     case INQUIRY:
+                       if (sc->request_bufflen < 35) {
+                               break;
+                       }
+                       sprintf (fname, "%s%c", simscsi_root, 'a' + sc->target);
+                       desc[sc->target] = ia64_ssc (__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS,
+                                                    0, 0, SSC_OPEN);
+                       if (desc[sc->target] < 0) {
+                               /* disk doesn't exist... */
+                               break;
+                       }
+                       buf = sc->request_buffer;
+                       buf[0] = 0;     /* magnetic disk */
+                       buf[1] = 0;     /* not a removable medium */
+                       buf[2] = 2;     /* SCSI-2 compliant device */
+                       buf[3] = 2;     /* SCSI-2 response data format */
+                       buf[4] = 31;    /* additional length (bytes) */
+                       buf[5] = 0;     /* reserved */
+                       buf[6] = 0;     /* reserved */
+                       buf[7] = 0;     /* various flags */
+                       memcpy(buf + 8, "HP      SIMULATED DISK  0.00",  28);
+                       sc->result = GOOD;
+                       break;
+
+                     case TEST_UNIT_READY:
+                       sc->result = GOOD;
+                       break;
+
+                     case READ_6:
+                       if (desc[sc->target] < 0 )
+                               break;
+                       simscsi_readwrite6(sc, SSC_READ);
+                       break;
+
+                     case READ_10:
+                       if (desc[sc->target] < 0 )
+                               break;
+                       simscsi_readwrite10(sc, SSC_READ);
+                       break;
+
+                     case WRITE_6:
+                       if (desc[sc->target] < 0)
+                               break;
+                       simscsi_readwrite6(sc, SSC_WRITE);
+                       break;
+
+                     case WRITE_10:
+                       if (desc[sc->target] < 0)
+                               break;
+                       simscsi_readwrite10(sc, SSC_WRITE);
+                       break;
+
+
+                     case READ_CAPACITY:
+                       if (desc[sc->target] < 0 || sc->request_bufflen < 8) {
+                               break;
+                       }
+                       buf = sc->request_buffer;
+
+                       /* pretend to be a 1GB disk (partition table contains real stuff): */
+                       buf[0] = 0x00;
+                       buf[1] = 0x1f;
+                       buf[2] = 0xff;
+                       buf[3] = 0xff;
+                       /* set block size of 512 bytes: */
+                       buf[4] = 0;
+                       buf[5] = 0;
+                       buf[6] = 2;
+                       buf[7] = 0;
+                       sc->result = GOOD;
+                       break;
+
+                     case MODE_SENSE:
+                       printk("MODE_SENSE\n");
+                       break;
+
+                     case START_STOP:
+                       printk("START_STOP\n");
+                       break;
+
+                     default:
+                       panic("simscsi: unknown SCSI command %u\n", sc->cmnd[0]);
+               }
+       }
+       if (sc->result == DID_BAD_TARGET) {
+               sc->result |= DRIVER_SENSE << 24;
+               sc->sense_buffer[0] = 0x70;
+               sc->sense_buffer[2] = 0x00;
+       }
+       if (atomic_read(&num_reqs) >= SIMSCSI_REQ_QUEUE_LEN) {
+               panic("Attempt to queue command while command is pending!!");
+       }
+       atomic_inc(&num_reqs);
+       queue[wr].sc = sc;
+       wr = (wr + 1) % SIMSCSI_REQ_QUEUE_LEN;
+
+       tasklet_schedule(&simscsi_tasklet);
+       return 0;
+}
+
+
+static Scsi_Host_Template driver_template = SIMSCSI;
+
+#define __initcall(fn) late_initcall(fn)
+
+#include "../drivers/scsi/scsi_module.c"
diff --git a/arch/ia64/hp/simscsi.h b/arch/ia64/hp/simscsi.h
new file mode 100644 (file)
index 0000000..4104540
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Simulated SCSI driver.
+ *
+ * Copyright (C) 1999 Hewlett-Packard Co
+ *     David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+#ifndef SIMSCSI_H
+#define SIMSCSI_H
+
+#define SIMSCSI_REQ_QUEUE_LEN  64
+
+#define DEFAULT_SIMSCSI_ROOT   "/var/ski-disks/sd"
+
+extern int simscsi_detect (Scsi_Host_Template *);
+extern int simscsi_release (struct Scsi_Host *);
+extern const char *simscsi_info (struct Scsi_Host *);
+extern int simscsi_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int simscsi_abort (Scsi_Cmnd *);
+extern int simscsi_reset (Scsi_Cmnd *, unsigned int);
+extern int simscsi_biosparam (Disk *, kdev_t, int[]);
+
+#define SIMSCSI {                                      \
+       detect:                 simscsi_detect,         \
+       release:                simscsi_release,        \
+       info:                   simscsi_info,           \
+       queuecommand:           simscsi_queuecommand,   \
+       abort:                  simscsi_abort,          \
+       reset:                  simscsi_reset,          \
+       bios_param:             simscsi_biosparam,      \
+       can_queue:              SIMSCSI_REQ_QUEUE_LEN,  \
+       this_id:                -1,                     \
+       sg_tablesize:           SG_ALL,                 \
+       cmd_per_lun:            SIMSCSI_REQ_QUEUE_LEN,  \
+       present:                0,                      \
+       unchecked_isa_dma:      0,                      \
+       use_clustering:         DISABLE_CLUSTERING      \
+}
+
+#endif /* SIMSCSI_H */
diff --git a/arch/ia64/hp/simserial.c b/arch/ia64/hp/simserial.c
new file mode 100644 (file)
index 0000000..d7573b6
--- /dev/null
@@ -0,0 +1,1104 @@
+/*
+ * Simulated Serial Driver (fake serial)
+ *
+ * This driver is mostly used for bringup purposes and will go away.
+ * It has a strong dependency on the system console. All outputs
+ * are rerouted to the same facility as the one used by printk which, in our
+ * case means sys_sim.c console (goes via the simulator). The code hereafter
+ * is completely leveraged from the serial.c driver.
+ *
+ * Copyright (C) 1999-2000 Hewlett-Packard Co
+ * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * 02/04/00 D. Mosberger       Merged in serial.c bug fixes in rs_close().
+ * 02/25/00 D. Mosberger       Synced up with 2.3.99pre-5 version of serial.c.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_KDB
+# include <linux/kdb.h>
+#endif
+
+#undef SIMSERIAL_DEBUG /* define this to get some debug information */
+
+#define KEYBOARD_INTR  3       /* must match with simulator! */
+
+#define NR_PORTS       1       /* only one port for now */
+#define SERIAL_INLINE  1
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+
+#ifndef MIN
+#define MIN(a,b)       ((a) < (b) ? (a) : (b))
+#endif
+
+#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
+
+#define SSC_GETCHAR    21
+
+extern long ia64_ssc (long, long, long, long, int);
+extern void ia64_ssc_connect_irq (long intr, long irq);
+
+static char *serial_name = "SimSerial driver";
+static char *serial_version = "0.6";
+
+/*
+ * This has been extracted from asm/serial.h. We need one eventually but
+ * I don't know exactly what we're going to put in it so just fake one
+ * for now.
+ */
+#define BASE_BAUD ( 1843200 / 16 )
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+/*
+ * Most of the values here are meaningless to this particular driver.
+ * However some values must be preserved for the code (leveraged from serial.c
+ * to work correctly).
+ * port must not be 0
+ * type must not be UNKNOWN
+ * So I picked arbitrary (guess from where?) values instead
+ */
+static struct serial_state rs_table[NR_PORTS]={
+  /* UART CLK   PORT IRQ     FLAGS        */
+  { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 }  /* ttyS0 */
+};
+
+/*
+ * Just for the fun of it !
+ */
+static struct serial_uart_config uart_config[] = {
+       { "unknown", 1, 0 },
+       { "8250", 1, 0 },
+       { "16450", 1, 0 },
+       { "16550", 1, 0 },
+       { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
+       { "cirrus", 1, 0 },
+       { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },
+       { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+                 UART_STARTECH },
+       { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+       { 0, 0}
+};
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+static struct async_struct *IRQ_ports[NR_IRQS];
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+static struct console *console;
+
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+extern struct console *console_drivers; /* from kernel/printk.c */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+#ifdef SIMSERIAL_DEBUG
+       printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
+               tty->stopped, tty->hw_stopped, tty->flow_stopped);
+#endif
+
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+#if SIMSERIAL_DEBUG
+       printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
+               tty->stopped, tty->hw_stopped, tty->flow_stopped);
+#endif
+}
+
+static  void receive_chars(struct tty_struct *tty, struct pt_regs *regs)
+{
+       unsigned char ch;
+       static unsigned char seen_esc = 0;
+
+       while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
+               if ( ch == 27 && seen_esc == 0 ) {
+                       seen_esc = 1;
+                       continue;
+               } else {
+                       if ( seen_esc==1 && ch == 'O' ) {
+                               seen_esc = 2;
+                               continue;
+                       } else if ( seen_esc == 2 ) {
+                               if ( ch == 'P' ) show_state();          /* F1 key */
+                               if ( ch == 'Q' ) show_buffers();        /* F2 key */
+#ifdef CONFIG_KDB
+                               if ( ch == 'S' )
+                                       kdb(KDB_REASON_KEYBOARD, 0, (kdb_eframe_t) regs);
+#endif
+
+                               seen_esc = 0;
+                               continue;
+                       }
+               }
+               seen_esc = 0;
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;
+
+               *tty->flip.char_buf_ptr = ch;
+
+               *tty->flip.flag_buf_ptr = 0;
+
+               tty->flip.flag_buf_ptr++;
+               tty->flip.char_buf_ptr++;
+               tty->flip.count++;
+       }
+       tty_flip_buffer_push(tty);
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct async_struct * info;
+
+       /*
+        * I don't know exactly why they don't use the dev_id opaque data
+        * pointer instead of this extra lookup table
+        */
+       info = IRQ_ports[irq];
+       if (!info || !info->tty) {
+               printk("simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
+               return;
+       }
+       /*
+        * pretty simple in our case, because we only get interrupts
+        * on inbound traffic
+        */
+       receive_chars(info->tty, regs);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+#if 0
+/*
+ * not really used in our situation so keep them commented out for now
+ */
+static DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */
+static void do_serial_bh(void)
+{
+       run_task_queue(&tq_serial);
+       printk("do_serial_bh: called\n");
+}
+#endif
+
+static void do_softint(void *private_)
+{
+       printk("simserial: do_softint called\n");
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+
+       if (!tty || !info->xmit.buf) return;
+
+       save_flags(flags); cli();
+       if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
+               restore_flags(flags);
+               return;
+       }
+       info->xmit.buf[info->xmit.head] = ch;
+       info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
+       restore_flags(flags);
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
+{
+       int count;
+       unsigned long flags;
+
+       save_flags(flags); cli();
+
+       if (info->x_char) {
+               char c = info->x_char;
+
+               console->write(console, &c, 1);
+
+               info->state->icount.tx++;
+               info->x_char = 0;
+
+               goto out;
+       }
+
+       if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {
+#ifdef SIMSERIAL_DEBUG
+               printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
+                      info->xmit.head, info->xmit.tail, info->tty->stopped);
+#endif
+               goto out;
+       }
+       /*
+        * We removed the loop and try to do it in to chunks. We need
+        * 2 operations maximum because it's a ring buffer.
+        *
+        * First from current to tail if possible.
+        * Then from the beginning of the buffer until necessary
+        */
+
+       count = MIN(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
+                   SERIAL_XMIT_SIZE - info->xmit.tail);
+       console->write(console, info->xmit.buf+info->xmit.tail, count);
+
+       info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
+
+       /*
+        * We have more at the beginning of the buffer
+        */
+       count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+       if (count) {
+               console->write(console, info->xmit.buf, count);
+               info->xmit.tail += count;
+       }
+out:
+       restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+       if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped ||
+           !info->xmit.buf)
+               return;
+
+       transmit_chars(info, NULL);
+}
+
+
+static int rs_write(struct tty_struct * tty, int from_user,
+                   const unsigned char *buf, int count)
+{
+       int     c, ret = 0;
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+
+       if (!tty || !info->xmit.buf || !tmp_buf) return 0;
+
+       save_flags(flags);
+       if (from_user) {
+               down(&tmp_buf_sem);
+               while (1) {
+                       int c1;
+                       c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+                       if (count < c)
+                               c = count;
+                       if (c <= 0)
+                               break;
+
+                       c -= copy_from_user(tmp_buf, buf, c);
+                       if (!c) {
+                               if (!ret)
+                                       ret = -EFAULT;
+                               break;
+                       }
+                       cli();
+                       c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+                       if (c1 < c)
+                               c = c1;
+                       memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+                       info->xmit.head = ((info->xmit.head + c) &
+                                          (SERIAL_XMIT_SIZE-1));
+                       restore_flags(flags);
+                       buf += c;
+                       count -= c;
+                       ret += c;
+               }
+               up(&tmp_buf_sem);
+       } else {
+               cli();
+               while (1) {
+                       c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+                       if (count < c)
+                               c = count;
+                       if (c <= 0) {
+                               break;
+                       }
+                       memcpy(info->xmit.buf + info->xmit.head, buf, c);
+                       info->xmit.head = ((info->xmit.head + c) &
+                                          (SERIAL_XMIT_SIZE-1));
+                       buf += c;
+                       count -= c;
+                       ret += c;
+               }
+               restore_flags(flags);
+       }
+       /*
+        * Hey, we transmit directly from here in our case
+        */
+       if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)
+           && !tty->stopped && !tty->hw_stopped) {
+               transmit_chars(info, NULL);
+       }
+       return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+       return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+       return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+
+       save_flags(flags); cli();
+       info->xmit.head = info->xmit.tail = 0;
+       restore_flags(flags);
+
+       wake_up_interruptible(&tty->write_wait);
+
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+       info->x_char = ch;
+       if (ch) {
+               /*
+                * I guess we could call console->write() directly but
+                * let's do that for now.
+                */
+               transmit_chars(info, NULL);
+       }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+       if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty));
+
+       printk("simrs_throttle called\n");
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       rs_send_xchar(tty, START_CHAR(tty));
+       }
+       printk("simrs_unthrottle called\n");
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+                   unsigned int cmd, unsigned long arg)
+{
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                   return -EIO;
+       }
+
+       switch (cmd) {
+               case TIOCMGET:
+                       printk("rs_ioctl: TIOCMGET called\n");
+                       return -EINVAL;
+               case TIOCMBIS:
+               case TIOCMBIC:
+               case TIOCMSET:
+                       printk("rs_ioctl: TIOCMBIS/BIC/SET called\n");
+                       return -EINVAL;
+               case TIOCGSERIAL:
+                       printk("simrs_ioctl TIOCGSERIAL called\n");
+                       return 0;
+               case TIOCSSERIAL:
+                       printk("simrs_ioctl TIOCSSERIAL called\n");
+                       return 0;
+               case TIOCSERCONFIG:
+                       printk("rs_ioctl: TIOCSERCONFIG called\n");
+                       return -EINVAL;
+
+               case TIOCSERGETLSR: /* Get line status register */
+                       printk("rs_ioctl: TIOCSERGETLSR called\n");
+                       return  -EINVAL;
+
+               case TIOCSERGSTRUCT:
+                       printk("rs_ioctl: TIOCSERGSTRUCT called\n");
+#if 0
+                       if (copy_to_user((struct async_struct *) arg,
+                                        info, sizeof(struct async_struct)))
+                               return -EFAULT;
+#endif
+                       return 0;
+
+               /*
+                * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+                * - mask passed in arg for lines of interest
+                *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+                * Caller should use TIOCGICOUNT to see which one it was
+                */
+               case TIOCMIWAIT:
+                       printk("rs_ioctl: TIOCMIWAIT: called\n");
+                       return 0;
+               /*
+                * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+                * Return: write counters to the user passed counter struct
+                * NB: both 1->0 and 0->1 transitions are counted except for
+                *     RI where only 0->1 is counted.
+                */
+               case TIOCGICOUNT:
+                       printk("rs_ioctl: TIOCGICOUNT called\n");
+                       return 0;
+
+               case TIOCSERGWILD:
+               case TIOCSERSWILD:
+                       /* "setserial -W" is called in Debian boot */
+                       printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+                       return 0;
+
+               default:
+                       return -ENOIOCTLCMD;
+               }
+       return 0;
+}
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+       unsigned int cflag = tty->termios->c_cflag;
+
+       if (   (cflag == old_termios->c_cflag)
+           && (   RELEVANT_IFLAG(tty->termios->c_iflag)
+               == RELEVANT_IFLAG(old_termios->c_iflag)))
+         return;
+
+
+       /* Handle turning off CRTSCTS */
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               rs_start(tty);
+       }
+}
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+       unsigned long   flags;
+       struct serial_state *state;
+       int             retval;
+
+       if (!(info->flags & ASYNC_INITIALIZED)) return;
+
+       state = info->state;
+
+#ifdef SIMSERIAL_DEBUG
+       printk("Shutting down serial port %d (irq %d)....", info->line,
+              state->irq);
+#endif
+
+       save_flags(flags); cli(); /* Disable interrupts */
+
+       /*
+        * First unlink the serial port from the IRQ chain...
+        */
+       if (info->next_port)
+               info->next_port->prev_port = info->prev_port;
+       if (info->prev_port)
+               info->prev_port->next_port = info->next_port;
+       else
+               IRQ_ports[state->irq] = info->next_port;
+
+       /*
+        * Free the IRQ, if necessary
+        */
+       if (state->irq && (!IRQ_ports[state->irq] ||
+                         !IRQ_ports[state->irq]->next_port)) {
+               if (IRQ_ports[state->irq]) {
+                       free_irq(state->irq, NULL);
+                       retval = request_irq(state->irq, rs_interrupt_single,
+                                            IRQ_T(info), "serial", NULL);
+
+                       if (retval)
+                               printk("serial shutdown: request_irq: error %d"
+                                      "  Couldn't reacquire IRQ.\n", retval);
+               } else
+                       free_irq(state->irq, NULL);
+       }
+
+       if (info->xmit.buf) {
+               free_page((unsigned long) info->xmit.buf);
+               info->xmit.buf = 0;
+       }
+
+       if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~ASYNC_INITIALIZED;
+       restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       struct serial_state *state;
+       unsigned long flags;
+
+       if (!info ) return;
+
+       state = info->state;
+
+       save_flags(flags); cli();
+
+       if (tty_hung_up_p(filp)) {
+#ifdef SIMSERIAL_DEBUG
+               printk("rs_close: hung_up\n");
+#endif
+               MOD_DEC_USE_COUNT;
+               restore_flags(flags);
+               return;
+       }
+#ifdef SIMSERIAL_DEBUG
+       printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+       if ((tty->count == 1) && (state->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  state->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk("rs_close: bad serial port count; tty->count is 1, "
+                      "state->count is %d\n", state->count);
+               state->count = 1;
+       }
+       if (--state->count < 0) {
+               printk("rs_close: bad serial port count for ttys%d: %d\n",
+                      info->line, state->count);
+               state->count = 0;
+       }
+       if (state->count) {
+               MOD_DEC_USE_COUNT;
+               restore_flags(flags);
+               return;
+       }
+       info->flags |= ASYNC_CLOSING;
+       restore_flags(flags);
+
+       /*
+        * Now we wait for the transmit buffer to clear; and we notify
+        * the line discipline to only process XON/XOFF characters.
+        */
+       shutdown(info);
+       if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty);
+       if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty);
+       info->event = 0;
+       info->tty = 0;
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       schedule_timeout(info->close_delay);
+               }
+               wake_up_interruptible(&info->open_wait);
+       }
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING);
+       wake_up_interruptible(&info->close_wait);
+       MOD_DEC_USE_COUNT;
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+}
+
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       struct serial_state *state = info->state;
+
+#ifdef SIMSERIAL_DEBUG
+       printk("rs_hangup: called\n");
+#endif
+
+       state = info->state;
+
+       rs_flush_buffer(tty);
+       if (info->flags & ASYNC_CLOSING)
+               return;
+       shutdown(info);
+
+       info->event = 0;
+       state->count = 0;
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+       info->tty = 0;
+       wake_up_interruptible(&info->open_wait);
+}
+
+
+static int get_async_struct(int line, struct async_struct **ret_info)
+{
+       struct async_struct *info;
+       struct serial_state *sstate;
+
+       sstate = rs_table + line;
+       sstate->count++;
+       if (sstate->info) {
+               *ret_info = sstate->info;
+               return 0;
+       }
+       info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+       if (!info) {
+               sstate->count--;
+               return -ENOMEM;
+       }
+       memset(info, 0, sizeof(struct async_struct));
+       init_waitqueue_head(&info->open_wait);
+       init_waitqueue_head(&info->close_wait);
+       init_waitqueue_head(&info->delta_msr_wait);
+       info->magic = SERIAL_MAGIC;
+       info->port = sstate->port;
+       info->flags = sstate->flags;
+       info->xmit_fifo_size = sstate->xmit_fifo_size;
+       info->line = line;
+       info->tqueue.routine = do_softint;
+       info->tqueue.data = info;
+       info->state = sstate;
+       if (sstate->info) {
+               kfree(info);
+               *ret_info = sstate->info;
+               return 0;
+       }
+       *ret_info = sstate->info = info;
+       return 0;
+}
+
+static int
+startup(struct async_struct *info)
+{
+       unsigned long flags;
+       int     retval=0;
+       void (*handler)(int, void *, struct pt_regs *);
+       struct serial_state *state= info->state;
+       unsigned long page;
+
+       page = get_free_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+
+       save_flags(flags); cli();
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               free_page(page);
+               goto errout;
+       }
+
+       if (!state->port || !state->type) {
+               if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+               free_page(page);
+               goto errout;
+       }
+       if (info->xmit.buf)
+               free_page(page);
+       else
+               info->xmit.buf = (unsigned char *) page;
+
+#ifdef SIMSERIAL_DEBUG
+       printk("startup: ttys%d (irq %d)...", info->line, state->irq);
+#endif
+
+       /*
+        * Allocate the IRQ if necessary
+        */
+       if (state->irq && (!IRQ_ports[state->irq] ||
+                         !IRQ_ports[state->irq]->next_port)) {
+               if (IRQ_ports[state->irq]) {
+                       retval = -EBUSY;
+                       goto errout;
+               } else
+                       handler = rs_interrupt_single;
+
+               retval = request_irq(state->irq, handler, IRQ_T(info),
+                                    "simserial", NULL);
+               if (retval) {
+                       if (capable(CAP_SYS_ADMIN)) {
+                               if (info->tty)
+                                       set_bit(TTY_IO_ERROR,
+                                               &info->tty->flags);
+                               retval = 0;
+                       }
+                       goto errout;
+               }
+       }
+
+       /*
+        * Insert serial port into IRQ chain.
+        */
+       info->prev_port = 0;
+       info->next_port = IRQ_ports[state->irq];
+       if (info->next_port)
+               info->next_port->prev_port = info;
+       IRQ_ports[state->irq] = info;
+
+       if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->xmit.head = info->xmit.tail = 0;
+
+#if 0
+       /*
+        * Set up serial timers...
+        */
+       timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
+       timer_active |= 1 << RS_TIMER;
+#endif
+
+       /*
+        * Set up the tty->alt_speed kludge
+        */
+       if (info->tty) {
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       info->tty->alt_speed = 57600;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       info->tty->alt_speed = 115200;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       info->tty->alt_speed = 230400;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       info->tty->alt_speed = 460800;
+       }
+
+       info->flags |= ASYNC_INITIALIZED;
+       restore_flags(flags);
+       return 0;
+
+errout:
+       restore_flags(flags);
+       return retval;
+}
+
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+       struct async_struct     *info;
+       int                     retval, line;
+       unsigned long           page;
+
+       MOD_INC_USE_COUNT;
+       line = minor(tty->device) - tty->driver.minor_start;
+       if ((line < 0) || (line >= NR_PORTS)) {
+               MOD_DEC_USE_COUNT;
+               return -ENODEV;
+       }
+       retval = get_async_struct(line, &info);
+       if (retval) {
+               MOD_DEC_USE_COUNT;
+               return retval;
+       }
+       tty->driver_data = info;
+       info->tty = tty;
+
+#ifdef SIMSERIAL_DEBUG
+       printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+              info->state->count);
+#endif
+       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       if (!tmp_buf) {
+               page = get_free_page(GFP_KERNEL);
+               if (!page) {
+                       /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+                       return -ENOMEM;
+               }
+               if (tmp_buf)
+                       free_page(page);
+               else
+                       tmp_buf = (unsigned char *) page;
+       }
+
+       /*
+        * If the port is the middle of closing, bail out now
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+               /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+#ifdef SERIAL_DO_RESTART
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * Start up serial port
+        */
+       retval = startup(info);
+       if (retval) {
+               /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+               return retval;
+       }
+
+       if ((info->state->count == 1) &&
+           (info->flags & ASYNC_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+                       *tty->termios = info->state->normal_termios;
+               else
+                       *tty->termios = info->state->callout_termios;
+       }
+
+       /*
+        * figure out which console to use (should be one already)
+        */
+       console = console_drivers;
+       while (console) {
+               if ((console->flags & CON_ENABLED) && console->write) break;
+               console = console->next;
+       }
+
+       info->session = current->session;
+       info->pgrp = current->pgrp;
+
+#ifdef SIMSERIAL_DEBUG
+       printk("rs_open ttys%d successful\n", info->line);
+#endif
+       return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct serial_state *state)
+{
+       return sprintf(buf, "%d: uart:%s port:%lX irq:%d\n",
+                      state->line, uart_config[state->type].name,
+                      state->port, state->irq);
+}
+
+static int rs_read_proc(char *page, char **start, off_t off, int count,
+                int *eof, void *data)
+{
+       int i, len = 0, l;
+       off_t   begin = 0;
+
+       len += sprintf(page, "simserinfo:1.0 driver:%s\n", serial_version);
+       for (i = 0; i < NR_PORTS && len < 4000; i++) {
+               l = line_info(page + len, &rs_table[i]);
+               len += l;
+               if (len+begin > off+count)
+                       goto done;
+               if (len+begin < off) {
+                       begin += len;
+                       len = 0;
+               }
+       }
+       *eof = 1;
+done:
+       if (off >= len+begin)
+               return 0;
+       *start = page + (begin-off);
+       return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static inline void show_serial_version(void)
+{
+       printk(KERN_INFO "%s version %s with", serial_name, serial_version);
+       printk(" no serial options enabled\n");
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+static int __init
+simrs_init (void)
+{
+       int                     i;
+       struct serial_state     *state;
+
+       show_serial_version();
+
+       /* Initialize the tty_driver structure */
+
+       memset(&serial_driver, 0, sizeof(struct tty_driver));
+       serial_driver.magic = TTY_DRIVER_MAGIC;
+       serial_driver.driver_name = "simserial";
+       serial_driver.name = "ttyS";
+       serial_driver.major = TTY_MAJOR;
+       serial_driver.minor_start = 64;
+       serial_driver.num = 1;
+       serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+       serial_driver.subtype = SERIAL_TYPE_NORMAL;
+       serial_driver.init_termios = tty_std_termios;
+       serial_driver.init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver.flags = TTY_DRIVER_REAL_RAW;
+       serial_driver.refcount = &serial_refcount;
+       serial_driver.table = serial_table;
+       serial_driver.termios = serial_termios;
+       serial_driver.termios_locked = serial_termios_locked;
+
+       serial_driver.open = rs_open;
+       serial_driver.close = rs_close;
+       serial_driver.write = rs_write;
+       serial_driver.put_char = rs_put_char;
+       serial_driver.flush_chars = rs_flush_chars;
+       serial_driver.write_room = rs_write_room;
+       serial_driver.chars_in_buffer = rs_chars_in_buffer;
+       serial_driver.flush_buffer = rs_flush_buffer;
+       serial_driver.ioctl = rs_ioctl;
+       serial_driver.throttle = rs_throttle;
+       serial_driver.unthrottle = rs_unthrottle;
+       serial_driver.send_xchar = rs_send_xchar;
+       serial_driver.set_termios = rs_set_termios;
+       serial_driver.stop = rs_stop;
+       serial_driver.start = rs_start;
+       serial_driver.hangup = rs_hangup;
+       serial_driver.break_ctl = rs_break;
+       serial_driver.wait_until_sent = rs_wait_until_sent;
+       serial_driver.read_proc = rs_read_proc;
+
+       /*
+        * Let's have a little bit of fun !
+        */
+       for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+
+               if (state->type == PORT_UNKNOWN) continue;
+
+               if (!state->irq) {
+                       state->irq = ia64_alloc_irq();
+                       ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq);
+               }
+
+               printk(KERN_INFO "ttyS%02d at 0x%04lx (irq = %d) is a %s\n",
+                      state->line,
+                      state->port, state->irq,
+                      uart_config[state->type].name);
+       }
+       /*
+        * The callout device is just like normal device except for
+        * major number and the subtype code.
+        */
+       callout_driver = serial_driver;
+       callout_driver.name = "cua";
+       callout_driver.major = TTYAUX_MAJOR;
+       callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+       callout_driver.read_proc = 0;
+       callout_driver.proc_entry = 0;
+
+       if (tty_register_driver(&serial_driver))
+               panic("Couldn't register simserial driver\n");
+
+       if (tty_register_driver(&callout_driver))
+               panic("Couldn't register callout driver\n");
+
+       return 0;
+}
+
+#ifndef MODULE
+__initcall(simrs_init);
+#endif
index 008d732bafac9f00e9ba3291ef5b4bcab8b1965a..f6d2038d545cdb58d6bf2f5b9da7f5cd0bfdc692 100644 (file)
@@ -98,7 +98,7 @@ void
 handle_IPI (int irq, void *dev_id, struct pt_regs *regs)
 {
        int this_cpu = smp_processor_id();
-       unsigned long *pending_ipis = &ipi_operation;
+       unsigned long *pending_ipis = &this_cpu(ipi_operation);
        unsigned long ops;
 
        /* Count this now; we may make a call that never returns. */
@@ -158,7 +158,7 @@ handle_IPI (int irq, void *dev_id, struct pt_regs *regs)
 static inline void
 send_IPI_single (int dest_cpu, int op)
 {
-       set_bit(op, &ipi_operation);
+       set_bit(op, &per_cpu(ipi_operation, dest_cpu));
        platform_send_ipi(dest_cpu, IA64_IPI_VECTOR, IA64_IPI_DM_INT, 0);
 }
 
index 3f2671ad215917da0cfbc2a1dbdef791e20cab8d..5a85013e893678485952d4b455faf125febd2bf2 100644 (file)
@@ -1305,11 +1305,7 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs)
         * handler into reading an arbitrary kernel addresses...
         */
        if (!user_mode(regs)) {
-#ifdef GAS_HAS_LOCAL_TAGS
-               fix = search_exception_table(regs->cr_iip + ia64_psr(regs)->ri);
-#else
-               fix = search_exception_table(regs->cr_iip);
-#endif
+               fix = SEARCH_EXCEPTION_TABLE(regs);
        }
        if (user_mode(regs) || fix.cont) {
                if ((current->thread.flags & IA64_THREAD_UAC_SIGBUS) != 0)
index 6f95fc97137c0f40881e215fd1bb455320605f7b..b3c75b7fea6f793c75806d99247deee19f70ae04 100644 (file)
@@ -49,7 +49,6 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re
        int signal = SIGSEGV, code = SEGV_MAPERR;
        struct vm_area_struct *vma, *prev_vma;
        struct mm_struct *mm = current->mm;
-       struct exception_fixup fix;
        struct siginfo si;
        unsigned long mask;
 
@@ -167,15 +166,8 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re
                return;
        }
 
-#ifdef GAS_HAS_LOCAL_TAGS
-       fix = search_exception_table(regs->cr_iip + ia64_psr(regs)->ri);
-#else
-       fix = search_exception_table(regs->cr_iip);
-#endif
-       if (fix.cont) {
-               handle_exception(regs, fix);
+       if (done_with_exception(regs))
                return;
-       }
 
        /*
         * Oops. The kernel tried to access some bad page. We'll have to terminate things
index d7cc0ec5ed8a7ba8424d7790b14c3913b12c566b..a5d26ac2df2e771ec9edbe8914c0193ca582fd8c 100644 (file)
@@ -445,4 +445,4 @@ mem_init (void)
 #ifdef CONFIG_IA32_SUPPORT
        ia32_gdt_init();
 #endif
-}
\ No newline at end of file
+}
index ba6939954526433a4d6b7448c7b09c74538b604a..a634192326efa1ce62ff2ff2ce8fbc7d127b761e 100644 (file)
@@ -66,6 +66,11 @@ SECTIONS
   machvec_end = .;
 #endif
 
+  __start___ksymtab = .;       /* Kernel symbol table */
+  __ksymtab : AT(ADDR(__ksymtab) - PAGE_OFFSET)
+       { *(__ksymtab) }
+  __stop___ksymtab = .;
+
   /* Unwind info & table: */
   . = ALIGN(8);
   .IA_64.unwind_info : AT(ADDR(.IA_64.unwind_info) - PAGE_OFFSET)
index 67e2c61b0f43ea4996043cc5962878569c085eb4..340b70543401e9755bc1d7f0d72d1cfe4054ab08 100644 (file)
@@ -9,6 +9,8 @@
  *         scheduler patch
  */
 
+#include <linux/types.h>
+
 #include <asm/system.h>
 
 /**
@@ -97,7 +99,7 @@ clear_bit (int nr, volatile void *addr)
 static __inline__ void
 __clear_bit (int nr, volatile void *addr)
 {
-       volatile __u32 *p = (__u32 *) addr + (nr >> 5);;
+       volatile __u32 *p = (__u32 *) addr + (nr >> 5);
        __u32 m = 1 << (nr & 31);
        *p &= ~m;
 }
index 58db5609867e3eb0ff40beff3b76089e9c6062c6..88c81a1fcfba9476b9c4b807257018e61783c870 100644 (file)
@@ -9,6 +9,7 @@
 #define PROT_READ      0x1             /* page can be read */
 #define PROT_WRITE     0x2             /* page can be written */
 #define PROT_EXEC      0x4             /* page can be executed */
+#define PROT_SEM       0x8             /* page may be used for atomic ops */
 #define PROT_NONE      0x0             /* page can not be accessed */
 
 #define MAP_SHARED     0x01            /* Share changes */
index 5cbf1dac47b37ddfbc1c1a4880088ee7406ec893..8ec8655902791326b90c4832d1a9be038d765d1a 100644 (file)
@@ -397,13 +397,17 @@ extern void ia64_load_extra (struct task_struct *task);
 } while (0)
 
 #ifdef CONFIG_SMP
-  /*
-   * In the SMP case, we save the fph state when context-switching
-   * away from a thread that modified fph.  This way, when the thread
-   * gets scheduled on another CPU, the CPU can pick up the state from
-   * task->thread.fph, avoiding the complication of having to fetch
-   * the latest fph state from another CPU.
-   */
+
+/* Return true if this CPU can call the console drivers in printk() */
+#define arch_consoles_callable() (cpu_online_map & (1UL << smp_processor_id()))
+
+/*
+ * In the SMP case, we save the fph state when context-switching
+ * away from a thread that modified fph.  This way, when the thread
+ * gets scheduled on another CPU, the CPU can pick up the state from
+ * task->thread.fph, avoiding the complication of having to fetch
+ * the latest fph state from another CPU.
+ */
 # define switch_to(prev,next) do {                                             \
        if (ia64_psr(ia64_task_regs(prev))->mfh) {                              \
                ia64_psr(ia64_task_regs(prev))->mfh = 0;                        \
index b53b244d80e2b88c7a1aed509348b6716cffb0f0..965aecc95e66d09a53efb2393186fa41f8547b16 100644 (file)
@@ -320,4 +320,22 @@ struct exception_fixup {
 extern struct exception_fixup search_exception_table (unsigned long addr);
 extern void handle_exception (struct pt_regs *regs, struct exception_fixup fixup);
 
+#ifdef GAS_HAS_LOCAL_TAGS
+#define SEARCH_EXCEPTION_TABLE(regs) search_exception_table(regs->cr_iip + ia64_psr(regs)->ri);
+#else
+#define SEARCH_EXCEPTION_TABLE(regs) search_exception_table(regs->cr_iip);
+#endif
+
+static inline int
+done_with_exception (struct pt_regs *regs)
+{
+       struct exception_fixup fix;
+       fix = SEARCH_EXCEPTION_TABLE(regs);
+       if (fix.cont) {
+               handle_exception(regs, fix);
+               return 1;
+       }
+       return 0;
+}
+
 #endif /* _ASM_IA64_UACCESS_H */