tty/procfs patches

tytso@MIT.EDU
Fri, 28 Feb 1997 21:45:38 GMT


Hi Linus,

Unfortunately, this set of patches are a bit more involved, but
it wasn't really possible to break them apart into smaller patches very
easily. It makes the following enhancements to 2.1.27:

* Add configuration options to the serial driver so that people
can only compile in the features that they need. This
allows a simple COM 1/2/3/4 driver to only take 18k,
saving 16k over a full-featured serial driver.

* Fix bugs in the serial driver:
- Removed memory leak when unloading as a module.
- Fixed TI 16750 support
- Fixed overrun UART handling so that a character
doesn't get unnecessarily thrown away.
- Don't try to autoconfigure a port at boot time if the
IO range that it uses is already reserved.

* Enhancements to the serial driver:
- Support for manipulating the OUT1/OUT2 lines
directly. (Needed for boards that support
half-duplex operation)
- Support for Mark and Space parity
- Added statistics reporting (using /proc/tty --- see below)

* Enhancements to the /proc filesystem
- Added code to handle generic /proc support. (What was
present before was incomplete, and I extended
it). The generic code which I added should be
good enough to eventually subsume the special
case code currently used to support /proc/net
and /proc/scsi. I avoided doing this for now
since it would require touching a huge number of
files in the network and/or SCSI layer.
- Use the generic /proc code to implement the /proc/tty
hierarchy.
- Removed dead code in fs/proc/inode.c that was no
longer needed after the first set of generic
changes were done. (It looks like whoever did
the first set of generic /proc fixes stopped
before the work was completely done.)

* Updated various routines to use the 2.1 user memory access
routines.

I've been running with these patches for a while now, and I'm quite
confident that they're stable. If you could apply them to your kernel
sources, I'd appreciate it. Thanks!!!

- Ted

Patch generated: on Thu Feb 27 19:34:43 EST 1997 by tytso@rsts-11.mit.edu
against Linux version 2.1.27

===================================================================
RCS file: drivers/char/RCS/ChangeLog,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/ChangeLog
--- drivers/char/ChangeLog 1997/02/27 03:41:00 1.1
+++ drivers/char/ChangeLog 1997/02/27 06:54:00
@@ -1,3 +1,52 @@
+Thu Feb 27 01:53:08 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c (change_speed): Add support for the termios flag
+ CMSPAR, which allows the user to select stick parity.
+ (i.e, if PARODD is set, the the parity bit is always 1; if
+ PARRODD is not set, then the parity bit is always 0).
+
+Wed Feb 26 19:03:10 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c (cleanup_module): Fix memory leak when using the serial
+ driver as a module; make sure tmp_buf gets freed!
+
+Tue Feb 25 11:01:59 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c (set_modem_info): Add support for setting and clearing
+ the OUT1 and OUT2 bits. (For special case UART's, usually
+ for half-duplex.)
+ (autoconfig, change_speed): Fix TI 16750 support.
+
+Sun Feb 16 00:14:43 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * tty_io.c (release_dev): Add sanity check to make sure there are
+ no waiters on tty->read_wait or tty->write_wait.
+
+ * serial.c (rs_init): Don't autoconfig a device if the I/O region
+ is already reserved.
+
+ * serial.c (serial_proc_info): Add support for /proc/serial.
+
+Thu Feb 13 00:49:10 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c (receive_chars): When the UART repotrs an overrun
+ condition, it does so with a valid character. Changed to
+ not throw away the valid character, but instead report the
+ overrun after the valid character.
+
+ * serial.c: Added new #ifdef's for some of the advanced serial
+ driver features. A minimal driver that only supports COM
+ 1/2/3/4 without sharing serial interrupts only takes 17k;
+ the full driver takes 32k.
+
+Wed Feb 12 14:50:44 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * vt.c:
+ * pty.c:
+ * tty_ioctl.c:
+ * serial.c: Update routines to use the new 2.1 memory access
+ routines.
+
Wed Dec 4 07:51:52 1996 Theodre Ts'o <tytso@localhost.mit.edu>

* serial.c (change_speed): Use save_flags(); cli() and
===================================================================
RCS file: Documentation/RCS/Configure.help,v
retrieving revision 1.1
diff -u -r1.1 Documentation/Configure.help
--- Documentation/Configure.help 1997/02/27 03:41:00 1.1
+++ Documentation/Configure.help 1997/02/27 03:41:54
@@ -595,6 +595,47 @@
corresponds to a serial port; this could be useful if you attached
a terminal or printer to that port.

+Non-standard serial port support
+CONFIG_SERIAL_NONSTANDARD
+ Say Y here if you have any non-standard serial boards --- boards
+ which aren't supported using the standard "dumb" serial driver.
+ This includes intelligent serial boards such as Cyclades,
+ Digiboards, etc.
+
+Extended dumb serial driver options
+CONFIG_SERIAL_EXTENDED
+ If you wish to use any non-standard features of the standard "dumb"
+ driver, say Y here. This includes HUB6 support, shared serial
+ interrupts, special multiport support, support for more than the
+ four COM 1/2/3/4 boards, etc.
+
+Support more than 4 serial ports
+CONFIG_SERIAL_MANY_PORTS
+ Enable this option if you have dumb serial boards other than the
+ four standard COM 1/2/3/4 ports. This may happen if you have an AST
+ FourPort, Accent Async, Boca, or other custom serial port hardware
+ which acts similar to standard serial port hardware. If you only
+ use the standard COM 1/2/3/4 ports, you can say N here to save some
+ memory.
+
+Support for sharing serial interrupts
+CONFIG_SERIAL_SHARE_IRQ
+ Some serial boards have hardware support which allows multiple dumb
+ serial ports on the same board to share a single IRQ. To enable
+ support for this in the serial driver, say Y here.
+
+Support special multiport boards
+CONFIG_SERIAL_MULTIPORT
+ Some multiport serial ports have special ports which are used to
+ signal when there are any serial ports on the board which need
+ servicing. Say Y here to enable the serial driver to take advantage
+ of those special I/O ports.
+
+Support the Bell Technologies HUB6 card
+CONFIG_HUB6
+ Say Y here to enable support in the dumb serial driver to support
+ the HUB6 card.
+
TGA Console Support
CONFIG_TGA_CONSOLE
Many Alpha systems (e.g the Multia) are shipped with a graphics card
===================================================================
RCS file: drivers/char/RCS/Config.in,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/Config.in
--- drivers/char/Config.in 1997/02/27 03:41:01 1.1
+++ drivers/char/Config.in 1997/02/27 03:41:54
@@ -4,19 +4,29 @@
mainmenu_option next_comment
comment 'Character devices'

-tristate 'Standard/generic serial support' CONFIG_SERIAL
-bool 'Digiboard PC/Xx Support' CONFIG_DIGI
-tristate 'Cyclades async mux support' CONFIG_CYCLADES
-bool 'Stallion multiport serial support' CONFIG_STALDRV
-if [ "$CONFIG_STALDRV" = "y" ]; then
- tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION
- tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
+tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL
+bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED
+if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
+ bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
+ bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ
+ bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT
+ bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6
fi
-tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
-tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
-if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then
- int ' DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1
- int ' FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
+bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
+if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
+ bool 'Digiboard PC/Xx Support' CONFIG_DIGI
+ tristate 'Cyclades async mux support' CONFIG_CYCLADES
+ bool 'Stallion multiport serial support' CONFIG_STALDRV
+ if [ "$CONFIG_STALDRV" = "y" ]; then
+ tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION
+ tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
+ fi
+ tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
+ tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
+ if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then
+ int ' DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1
+ int ' FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
+ fi
fi
tristate 'Parallel printer support' CONFIG_PRINTER

===================================================================
RCS file: include/linux/RCS/serial.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/serial.h
--- include/linux/serial.h 1997/02/27 03:41:01 1.1
+++ include/linux/serial.h 1997/02/27 06:56:15
@@ -123,7 +123,10 @@
*/
struct serial_icounter_struct {
int cts, dsr, rng, dcd;
- int reserved[16];
+ int rx, tx;
+ int frame, overrun, parity, brk;
+ int buf_overrun;
+ int reserved[9];
};


@@ -144,7 +147,9 @@
* Counters of the input lines (CTS, DSR, RI, CD) interrupts
*/
struct async_icount {
- __u32 cts, dsr, rng, dcd;
+ __u32 cts, dsr, rng, dcd, tx, rx;
+ __u32 frame, parity, overrun, brk;
+ __u32 buf_overrun;
};

struct serial_state {
@@ -178,6 +183,7 @@
int read_status_mask;
int ignore_status_mask;
int timeout;
+ int quot;
int x_char; /* xon/xoff character */
int close_delay;
unsigned short closing_wait;
@@ -235,5 +241,6 @@
/* Export to allow PCMCIA to use this - Dave Hinds */
extern int register_serial(struct serial_struct *req);
extern void unregister_serial(int line);
+
#endif /* __KERNEL__ */
#endif /* _LINUX_SERIAL_H */
===================================================================
RCS file: include/linux/RCS/tty.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty.h
--- include/linux/tty.h 1997/02/27 03:41:01 1.1
+++ include/linux/tty.h 1997/02/27 06:55:58
@@ -328,13 +328,8 @@
extern int n_tty_ioctl(struct tty_struct * tty, struct file * file,
unsigned int cmd, unsigned long arg);

-/* serial.c */
+/* pcxx.c */

-extern int rs_open(struct tty_struct * tty, struct file * filp);
-
-/* pty.c */
-
-extern int pty_open(struct tty_struct * tty, struct file * filp);
extern int pcxe_open(struct tty_struct *tty, struct file *filp);

/* console.c */
===================================================================
RCS file: include/linux/RCS/tty_ldisc.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty_ldisc.h
--- include/linux/tty_ldisc.h 1997/02/27 03:41:01 1.1
+++ include/linux/tty_ldisc.h 1997/02/27 03:45:14
@@ -102,6 +102,7 @@

struct tty_ldisc {
int magic;
+ char *name;
int num;
int flags;
/*
===================================================================
RCS file: include/linux/RCS/tty_driver.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty_driver.h
--- include/linux/tty_driver.h 1997/02/27 03:41:01 1.1
+++ include/linux/tty_driver.h 1997/02/27 03:45:14
@@ -107,6 +107,7 @@

struct tty_driver {
int magic; /* magic number for this structure */
+ const char *driver_name;
const char *name;
int name_base; /* offset of printed name */
short major; /* major device number */
@@ -117,6 +118,7 @@
struct termios init_termios; /* Initial termios */
int flags; /* tty driver flags */
int *refcount; /* for loadable tty drivers */
+ struct proc_dir_entry *proc_entry; /* /proc fs entry */
struct tty_driver *other; /* only used for the PTY driver */

/*
@@ -150,6 +152,10 @@
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, void *data);
+ int (*write_proc)(struct file *file, const char *buffer,
+ unsigned long count, void *data);

/*
* linked list pointers
@@ -196,5 +202,9 @@
/* pty subtypes (magic, used by tty_io.c) */
#define PTY_TYPE_MASTER 0x0001
#define PTY_TYPE_SLAVE 0x0002
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2

#endif /* #ifdef _LINUX_TTY_DRIVER_H */
===================================================================
RCS file: include/asm-i386/RCS/termios.h,v
retrieving revision 1.1
diff -u -r1.1 include/asm-i386/termios.h
--- include/asm-i386/termios.h 1997/02/27 03:41:01 1.1
+++ include/asm-i386/termios.h 1997/02/27 06:55:58
@@ -33,6 +33,8 @@
#define TIOCM_DSR 0x100
#define TIOCM_CD TIOCM_CAR
#define TIOCM_RI TIOCM_RNG
+#define TIOCM_OUT1 0x2000
+#define TIOCM_OUT2 0x4000

/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */

===================================================================
RCS file: include/asm-i386/RCS/termbits.h,v
retrieving revision 1.1
diff -u -r1.1 include/asm-i386/termbits.h
--- include/asm-i386/termbits.h 1997/02/27 06:54:43 1.1
+++ include/asm-i386/termbits.h 1997/02/27 06:55:03
@@ -122,6 +122,7 @@
#define B230400 0010003
#define B460800 0010004
#define CIBAUD 002003600000 /* input baud rate (not used) */
+#define CMSPAR 010000000000 /* mark or space (stick) parity */
#define CRTSCTS 020000000000 /* flow control */

/* c_lflag bits */
===================================================================
RCS file: drivers/char/RCS/tty_io.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/tty_io.c
--- drivers/char/tty_io.c 1997/02/27 03:41:01 1.1
+++ drivers/char/tty_io.c 1997/02/27 03:41:54
@@ -63,6 +63,9 @@
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/poll.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif

#include <asm/uaccess.h>
#include <asm/system.h>
@@ -1135,6 +1138,25 @@
return;

/*
+ * Sanity check --- if tty->count is zero, there shouldn't be
+ * any waiters on tty->read_wait or tty->write_wait. But just
+ * in case....
+ */
+ while (1) {
+ if (waitqueue_active(&tty->read_wait)) {
+ printk("release_dev: %s: read_wait active?!?\n",
+ tty_name(tty));
+ wake_up(&tty->read_wait);
+ } else if (waitqueue_active(&tty->write_wait)) {
+ printk("release_dev: %s: write_wait active?!?\n",
+ tty_name(tty));
+ wake_up(&tty->write_wait);
+ } else
+ break;
+ schedule();
+ }
+
+ /*
* We're committed; at this point, we must not block!
*/
if (o_tty) {
@@ -1232,7 +1254,9 @@
int minor;
int noctty, retval;
kdev_t device;
+ unsigned short saved_flags;

+ saved_flags = filp->f_flags;
retry_open:
noctty = filp->f_flags & O_NOCTTY;
device = inode->i_rdev;
@@ -1240,6 +1264,7 @@
if (!current->tty)
return -ENXIO;
device = current->tty->device;
+ filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
}
if (device == CONSOLE_DEV) {
@@ -1263,6 +1288,7 @@
retval = tty->driver.open(tty, filp);
else
retval = -ENODEV;
+ filp->f_flags = saved_flags;

if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
retval = -EBUSY;
@@ -1830,6 +1856,10 @@
driver->next = tty_drivers;
if (tty_drivers) tty_drivers->prev = driver;
tty_drivers = driver;
+
+#ifdef CONFIG_PROC_FS
+ proc_tty_register_driver(driver);
+#endif
return error;
}

@@ -1871,6 +1901,9 @@
if (driver->next)
driver->next->prev = driver->prev;

+#ifdef CONFIG_PROC_FS
+ proc_tty_unregister_driver(driver);
+#endif
return 0;
}

@@ -1925,18 +1958,24 @@
*/
memset(&dev_tty_driver, 0, sizeof(struct tty_driver));
dev_tty_driver.magic = TTY_DRIVER_MAGIC;
- dev_tty_driver.name = "tty";
+ dev_tty_driver.driver_name = "/dev/tty";
+ dev_tty_driver.name = dev_tty_driver.driver_name + 5;
dev_tty_driver.name_base = 0;
dev_tty_driver.major = TTY_MAJOR;
dev_tty_driver.minor_start = 0;
dev_tty_driver.num = 1;
+ dev_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+ dev_tty_driver.subtype = SYSTEM_TYPE_TTY;

if (tty_register_driver(&dev_tty_driver))
panic("Couldn't register /dev/tty driver\n");

dev_console_driver = dev_tty_driver;
- dev_console_driver.name = "console";
+ dev_console_driver.driver_name = "/dev/console";
+ dev_console_driver.name = dev_console_driver.driver_name + 5;
dev_console_driver.major = TTYAUX_MAJOR;
+ dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+ dev_console_driver.subtype = SYSTEM_TYPE_CONSOLE;

if (tty_register_driver(&dev_console_driver))
panic("Couldn't register /dev/console driver\n");
===================================================================
RCS file: drivers/char/RCS/n_tty.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/n_tty.c
--- drivers/char/n_tty.c 1997/02/27 03:41:01 1.1
+++ drivers/char/n_tty.c 1997/02/27 03:41:54
@@ -1010,6 +1010,7 @@

struct tty_ldisc tty_ldisc_N_TTY = {
TTY_LDISC_MAGIC, /* magic */
+ "n_tty", /* name */
0, /* num */
0, /* flags */
n_tty_open, /* open */
===================================================================
RCS file: drivers/char/RCS/serial.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/serial.c
--- drivers/char/serial.c 1997/02/27 03:41:02 1.1
+++ drivers/char/serial.c 1997/02/27 20:54:09
@@ -18,11 +18,13 @@
* rs_set_termios fixed to look also for changes of the input
* flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
* Bernd Anhdupl 05/17/96.
+ *
+ * 1/97: Extended dumb serial ports are a config option now.
+ * Saves 4k. Michael A. Griffith <grif@acm.org>
*
* This module exports the following rs232 io functions:
*
* int rs_init(void);
- * int rs_open(struct tty_struct * tty, struct file * filp)
*/

#include <linux/config.h>
@@ -50,17 +52,13 @@
#include <asm/bitops.h>

static char *serial_name = "Serial driver";
-static char *serial_version = "4.22";
+static char *serial_version = "4.23";

-DECLARE_TASK_QUEUE(tq_serial);
+static DECLARE_TASK_QUEUE(tq_serial);

-struct tty_driver serial_driver, callout_driver;
+static struct tty_driver serial_driver, callout_driver;
static int serial_refcount;

-/* serial subtype definitions */
-#define SERIAL_TYPE_NORMAL 1
-#define SERIAL_TYPE_CALLOUT 2
-
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256

@@ -71,6 +69,16 @@
* Enables support for the venerable Bell Technologies
* HUB6 card.
*
+ * CONFIG_SERIAL_MANY_PORTS
+ * Enables support for ports beyond the standard, stupid
+ * COM 1/2/3/4.
+ *
+ * CONFIG_SERIAL_MULTIPORT
+ * Enables support for special multiport board support.
+ *
+ * CONFIG_SERIAL_SHARE_IRQ
+ * Enables support for multiple serial ports on one IRQ
+ *
* SERIAL_PARANOIA_CHECK
* Check the magic number for the async_structure where
* ever possible.
@@ -80,6 +88,34 @@
#define CONFIG_SERIAL_NOPAUSE_IO
#define SERIAL_DO_RESTART

+#if 0
+/* Normally these defines are controlled by the autoconf.h */
+
+#define CONFIG_SERIAL_MANY_PORTS
+#define CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_MULTIPORT
+#define CONFIG_HUB6
+#endif
+
+/* Sanity checks */
+
+#ifdef CONFIG_SERIAL_MULTIPORT
+#ifndef CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_SHARE_IRQ
+#endif
+#endif
+
+#ifdef CONFIG_HUB6
+#ifndef CONFIG_SERIAL_MANY_PORTS
+#define CONFIG_SERIAL_MANY_PORTS
+#endif
+#ifndef CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_SHARE_IRQ
+#endif
+#endif
+
+/* Set of debugging defines */
+
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
@@ -105,7 +141,9 @@
*/

static struct async_struct *IRQ_ports[16];
+#ifdef CONFIG_SERIAL_MULTIPORT
static struct rs_multiport_struct rs_multiport[16];
+#endif
static int IRQ_timeout[16];
static volatile int rs_irq_triggered;
static volatile int rs_triggered;
@@ -146,10 +184,13 @@
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF

+
+#ifdef CONFIG_SERIAL_MANY_PORTS
#define FOURPORT_FLAGS ASYNC_FOURPORT
#define ACCENT_FLAGS 0
#define BOCA_FLAGS 0
#define HUB6_FLAGS 0
+#endif

/*
* The following define the access methods for the HUB6 card. All
@@ -167,13 +208,13 @@

#define C_P(card,port) (((card)<<6|(port)<<3) + 1)

-struct serial_state rs_table[] = {
+static struct serial_state rs_table[] = {
/* UART CLK PORT IRQ FLAGS */
{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */
{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */
{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */
{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
-
+#ifdef CONFIG_SERIAL_MANY_PORTS
{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */
{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */
{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */
@@ -223,6 +264,7 @@
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */
#endif
+#endif /* CONFIG_SERIAL_MANY_PORTS */
#ifdef CONFIG_MCA
{ 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },
{ 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },
@@ -426,43 +468,79 @@
struct tty_struct *tty = info->tty;
unsigned char ch;
int ignored = 0;
+ struct async_icount *icount;

+ icount = &info->state->icount;
do {
ch = serial_inp(info, UART_RX);
- if (*status & UART_LSR_BI)
- *status &= ~(UART_LSR_FE | UART_LSR_PE);
- if (*status & info->ignore_status_mask) {
- if (++ignored > 100)
- break;
- goto ignore_char;
- }
- *status &= info->read_status_mask;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
- tty->flip.count++;
- if (*status & (UART_LSR_BI)) {
+ *tty->flip.char_buf_ptr = ch;
+ icount->rx++;
+
#ifdef SERIAL_DEBUG_INTR
- printk("handling break....");
+ printk("DR%02x:%02x...", ch, *status);
#endif
- *tty->flip.flag_buf_ptr++ = TTY_BREAK;
- if (info->flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (*status & UART_LSR_PE)
- *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- else if (*status & UART_LSR_FE)
- *tty->flip.flag_buf_ptr++ = TTY_FRAME;
- else if (*status & UART_LSR_OE)
- *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- else
- *tty->flip.flag_buf_ptr++ = 0;
- *tty->flip.char_buf_ptr++ = ch;
+ *tty->flip.flag_buf_ptr = 0;
+ if (*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)) {
+ /*
+ * For statistics only
+ */
+ if (*status & UART_LSR_BI) {
+ *status &= ~(UART_LSR_FE | UART_LSR_PE);
+ icount->brk++;
+ } else if (*status & UART_LSR_PE)
+ icount->parity++;
+ else if (*status & UART_LSR_FE)
+ icount->frame++;
+ if (*status & UART_LSR_OE)
+ icount->overrun++;
+
+ /*
+ * Now check to see if character should be
+ * ignored, and mask off conditions which
+ * should be ignored.
+ */
+ if (*status & info->ignore_status_mask) {
+ if (++ignored > 100)
+ break;
+ goto ignore_char;
+ }
+ *status &= info->read_status_mask;
+
+ if (*status & (UART_LSR_BI)) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("handling break....");
+#endif
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ if (info->flags & ASYNC_SAK)
+ do_SAK(tty);
+ } else if (*status & UART_LSR_PE)
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ else if (*status & UART_LSR_FE)
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ if (*status & UART_LSR_OE) {
+ /*
+ * Overrun is special, since it's
+ * reported immediately, and doesn't
+ * affect the current character
+ */
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ }
+ }
+ }
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
ignore_char:
*status = serial_inp(info, UART_LSR);
} while (*status & UART_LSR_DR);
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#ifdef SERIAL_DEBUG_INTR
- printk("DR...");
-#endif
}

static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -471,6 +549,7 @@

if (info->x_char) {
serial_outp(info, UART_TX, info->x_char);
+ info->state->icount.tx++;
info->x_char = 0;
if (intr_done)
*intr_done = 0;
@@ -487,6 +566,7 @@
do {
serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->state->icount.tx++;
if (--info->xmit_cnt <= 0)
break;
} while (--count > 0);
@@ -574,6 +654,7 @@
}
}

+#ifdef CONFIG_SERIAL_SHARE_IRQ
/*
* This is the serial driver's generic interrupt routine
*/
@@ -583,8 +664,10 @@
struct async_struct * info;
int pass_counter = 0;
struct async_struct *end_mark = 0;
+#ifdef CONFIG_SERIAL_MULTIPORT
int first_multi = 0;
struct rs_multiport_struct *multi;
+#endif

#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt(%d)...", irq);
@@ -594,9 +677,11 @@
if (!info)
return;

+#ifdef CONFIG_SERIAL_MULTIPORT
multi = &rs_multiport[irq];
if (multi->port_monitor)
first_multi = inb(multi->port_monitor);
+#endif

do {
if (!info->tty ||
@@ -632,14 +717,18 @@
continue;
}
} while (end_mark != info);
+#ifdef CONFIG_SERIAL_MULTIPORT
if (multi->port_monitor)
printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
info->state->irq, first_multi,
inb(multi->port_monitor));
+#endif
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
+#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */
+

/*
* This is the serial driver's interrupt routine for a single port
@@ -648,9 +737,11 @@
{
int status;
int pass_counter = 0;
- int first_multi = 0;
struct async_struct * info;
+#ifdef CONFIG_SERIAL_MULTIPORT
+ int first_multi = 0;
struct rs_multiport_struct *multi;
+#endif

#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt_single(%d)...", irq);
@@ -660,9 +751,11 @@
if (!info || !info->tty)
return;

+#ifdef CONFIG_SERIAL_MULTIPORT
multi = &rs_multiport[irq];
if (multi->port_monitor)
first_multi = inb(multi->port_monitor);
+#endif

do {
status = serial_inp(info, UART_LSR);
@@ -682,15 +775,18 @@
}
} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
info->last_active = jiffies;
+#ifdef CONFIG_SERIAL_MULTIPORT
if (multi->port_monitor)
printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
info->state->irq, first_multi,
inb(multi->port_monitor));
+#endif
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}

+#ifdef CONFIG_SERIAL_MULTIPORT
/*
* This is the serial driver's for multiport boards
*/
@@ -771,7 +867,7 @@
printk("end.\n");
#endif
}
-
+#endif

/*
* -------------------------------------------------------------------
@@ -851,6 +947,7 @@
if (!info)
continue;
cli();
+#ifdef CONFIG_SERIAL_SHARE_IRQ
if (info->next_port) {
do {
serial_out(info, UART_IER, 0);
@@ -858,11 +955,14 @@
serial_out(info, UART_IER, info->IER);
info = info->next_port;
} while (info);
+#ifdef CONFIG_SERIAL_MULTIPORT
if (rs_multiport[i].port1)
rs_interrupt_multi(i, NULL, NULL);
else
+#endif
rs_interrupt(i, NULL, NULL);
} else
+#endif /* CONFIG_SERIAL_SHARE_IRQ */
rs_interrupt_single(i, NULL, NULL);
sti();
}
@@ -873,7 +973,11 @@

if (IRQ_ports[0]) {
cli();
+#ifdef CONFIG_SERIAL_SHARE_IRQ
rs_interrupt(0, NULL, NULL);
+#else
+ rs_interrupt_single(0, NULL, NULL);
+#endif
sti();

timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
@@ -948,12 +1052,15 @@

static int startup(struct async_struct * info)
{
- unsigned short ICP;
unsigned long flags;
int retval;
void (*handler)(int, void *, struct pt_regs *);
struct serial_state *state= info->state;
unsigned long page;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+ unsigned short ICP;
+#endif
+

page = get_free_page(GFP_KERNEL);
if (!page)
@@ -1026,11 +1133,17 @@
if (state->irq && (!IRQ_ports[state->irq] ||
!IRQ_ports[state->irq]->next_port)) {
if (IRQ_ports[state->irq]) {
+#ifdef CONFIG_SERIAL_SHARE_IRQ
free_irq(state->irq, NULL);
+#ifdef CONFIG_SERIAL_MULTIPORT
if (rs_multiport[state->irq].port1)
handler = rs_interrupt_multi;
else
+#endif
handler = rs_interrupt;
+#else
+ return -EBUSY;
+#endif /* CONFIG_SERIAL_SHARE_IRQ */
} else
handler = rs_interrupt_single;

@@ -1074,10 +1187,13 @@
info->MCR = 0;
if (info->tty->termios->c_cflag & CBAUD)
info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
if (state->irq == 0)
info->MCR |= UART_MCR_OUT1;
- } else {
+ } else
+#endif
+ {
if (state->irq != 0)
info->MCR |= UART_MCR_OUT2;
}
@@ -1095,12 +1211,14 @@
info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
serial_outp(info, UART_IER, info->IER); /* enable interrupts */

+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
/* Enable interrupts on the AST Fourport board */
ICP = (info->port & 0xFE0) | 0x01F;
outb_p(0x80, ICP);
(void) inb_p(ICP);
}
+#endif

/*
* And clear the interrupt registers again for luck.
@@ -1193,11 +1311,13 @@

info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
/* reset interrupts on the AST Fourport board */
(void) inb((info->port & 0xFE0) | 0x01F);
info->MCR |= UART_MCR_OUT1;
} else
+#endif
info->MCR &= ~UART_MCR_OUT2;
#if defined(__alpha__) && !defined(CONFIG_PCI)
/*
@@ -1270,6 +1390,10 @@
}
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif

/* Determine divisor based on baud rate */
i = cflag & CBAUD;
@@ -1303,6 +1427,7 @@
if (!quot)
quot = baud_base / 9600;
}
+ info->quot = quot;
info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
info->timeout += HZ/50; /* Add .02 seconds of slop */

@@ -1376,7 +1501,8 @@
if (info->state->type == PORT_16750)
serial_outp(info, UART_FCR, fcr); /* set fcr */
serial_outp(info, UART_LCR, cval); /* reset DLAB */
- serial_outp(info, UART_FCR, fcr); /* set fcr */
+ if (info->state->type != PORT_16750)
+ serial_outp(info, UART_FCR, fcr); /* set fcr */
restore_flags(flags);
}

@@ -1424,7 +1550,7 @@
static int rs_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
- int c, total = 0;
+ int c, ret = 0;
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;

@@ -1445,7 +1571,12 @@
break;

if (from_user) {
- copy_from_user(tmp_buf, buf, c);
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
@@ -1456,7 +1587,7 @@
restore_flags(flags);
buf += c;
count -= c;
- total += c;
+ ret += c;
}
if (from_user)
up(&tmp_buf_sem);
@@ -1466,7 +1597,7 @@
serial_out(info, UART_IER, info->IER);
}
restore_flags(flags);
- return total;
+ return ret;
}

static int rs_write_room(struct tty_struct *tty)
@@ -1510,7 +1641,7 @@
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
-void rs_send_xchar(struct tty_struct *tty, char ch)
+static void rs_send_xchar(struct tty_struct *tty, char ch)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;

@@ -1609,7 +1740,8 @@
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
tmp.hub6 = state->hub6;
- copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
return 0;
}

@@ -1621,9 +1753,8 @@
unsigned int i,change_irq,change_port;
int retval = 0;

- if (!new_info)
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
- copy_from_user(&new_serial,new_info,sizeof(new_serial));
state = info->state;
old_state = *state;

@@ -1734,8 +1865,7 @@
status = serial_in(info, UART_LSR);
sti();
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- put_user(result,value);
- return 0;
+ return put_user(result,value);
}


@@ -1750,12 +1880,15 @@
sti();
result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+#ifdef TIOCM_OUT1
+ | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0)
+ | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)
+#endif
| ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
| ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
| ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
| ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
- put_user(result,value);
- return 0;
+ return put_user(result,value);
}

static int set_modem_info(struct async_struct * info, unsigned int cmd,
@@ -1773,16 +1906,37 @@
info->MCR |= UART_MCR_RTS;
if (arg & TIOCM_DTR)
info->MCR |= UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+ if (arg & TIOCM_OUT1)
+ info->MCR |= UART_MCR_OUT1;
+ if (arg & TIOCM_OUT2)
+ info->MCR |= UART_MCR_OUT2;
+#endif
break;
case TIOCMBIC:
if (arg & TIOCM_RTS)
info->MCR &= ~UART_MCR_RTS;
if (arg & TIOCM_DTR)
info->MCR &= ~UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+ if (arg & TIOCM_OUT1)
+ info->MCR &= ~UART_MCR_OUT1;
+ if (arg & TIOCM_OUT2)
+ info->MCR &= ~UART_MCR_OUT2;
+#endif
break;
case TIOCMSET:
- info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+ info->MCR = ((info->MCR & ~(UART_MCR_RTS |
+#ifdef TIOCM_OUT1
+ UART_MCR_OUT1 |
+ UART_MCR_OUT2 |
+#endif
+ UART_MCR_DTR))
| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+#ifdef TIOCM_OUT1
+ | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
+ | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
+#endif
| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
break;
default:
@@ -1883,6 +2037,7 @@
return wild_interrupts;
}

+#ifdef CONFIG_SERIAL_MULTIPORT
static int get_multiport_struct(struct async_struct * info,
struct serial_multiport_struct *retinfo)
{
@@ -1911,9 +2066,9 @@

ret.irq = info->state->irq;

- copy_to_user(retinfo,&ret,sizeof(*retinfo));
+ if (copy_to_user(retinfo,&ret,sizeof(*retinfo)))
+ return -EFAULT;
return 0;
-
}

static int set_multiport_struct(struct async_struct * info,
@@ -1928,13 +2083,12 @@

if (!suser())
return -EPERM;
- if (!in_multi)
- return -EFAULT;
state = info->state;

- copy_from_user(&new_multi, in_multi,
- sizeof(struct serial_multiport_struct));
-
+ if (copy_from_user(&new_multi, in_multi,
+ sizeof(struct serial_multiport_struct)))
+ return -EFAULT;
+
if (new_multi.irq != state->irq || state->irq == 0 ||
!IRQ_ports[state->irq])
return -EINVAL;
@@ -1996,6 +2150,7 @@

return 0;
}
+#endif

static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
@@ -2053,82 +2208,52 @@
(arg ? CLOCAL : 0));
return 0;
case TIOCMGET:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
- if (error)
- return error;
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
return set_modem_info(info, cmd, (unsigned int *) arg);
case TIOCGSERIAL:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_struct));
- if (error)
- return error;
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
- error = verify_area(VERIFY_READ, (void *) arg,
- sizeof(struct serial_struct));
- if (error)
- return error;
return set_serial_info(info,
(struct serial_struct *) arg);
case TIOCSERCONFIG:
return do_autoconfig(info);

case TIOCSERGWILD:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(int));
- if (error)
- return error;
- put_user(rs_wild_int_mask, (unsigned int *) arg);
- return 0;
-
+ return put_user(rs_wild_int_mask,
+ (unsigned int *) arg);
case TIOCSERGETLSR: /* Get line status register */
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
- if (error)
- return error;
- else
- return get_lsr_info(info, (unsigned int *) arg);
+ return get_lsr_info(info, (unsigned int *) arg);

case TIOCSERSWILD:
if (!suser())
return -EPERM;
- error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
+ error = get_user(rs_wild_int_mask,
+ (unsigned int *) arg);
if (error)
return error;
- get_user(rs_wild_int_mask, (unsigned int *) arg);
if (rs_wild_int_mask < 0)
rs_wild_int_mask = check_wild_interrupts(0);
return 0;

case TIOCSERGSTRUCT:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct async_struct));
- if (error)
- return error;
- copy_to_user((struct async_struct *) arg,
- info, sizeof(struct async_struct));
+ if (copy_to_user((struct async_struct *) arg,
+ info, sizeof(struct async_struct)))
+ return -EFAULT;
return 0;
-
+
+#ifdef CONFIG_SERIAL_MULTIPORT
case TIOCSERGETMULTI:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_multiport_struct));
- if (error)
- return error;
return get_multiport_struct(info,
(struct serial_multiport_struct *) arg);
case TIOCSERSETMULTI:
- error = verify_area(VERIFY_READ, (void *) arg,
- sizeof(struct serial_multiport_struct));
- if (error)
- return error;
return set_multiport_struct(info,
(struct serial_multiport_struct *) arg);
+#endif
+
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
@@ -2168,18 +2293,18 @@
* RI where only 0->1 is counted.
*/
case TIOCGICOUNT:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_icounter_struct));
- if (error)
- return error;
cli();
cnow = info->state->icount;
sti();
p_cuser = (struct serial_icounter_struct *) arg;
- put_user(cnow.cts, &p_cuser->cts);
- put_user(cnow.dsr, &p_cuser->dsr);
- put_user(cnow.rng, &p_cuser->rng);
- put_user(cnow.dcd, &p_cuser->dcd);
+ error = put_user(cnow.cts, &p_cuser->cts);
+ if (error) return error;
+ error = put_user(cnow.dsr, &p_cuser->dsr);
+ if (error) return error;
+ error = put_user(cnow.rng, &p_cuser->rng);
+ if (error) return error;
+ error = put_user(cnow.dcd, &p_cuser->dcd);
+ if (error) return error;
return 0;

default:
@@ -2409,7 +2534,7 @@
/*
* rs_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
-void rs_hangup(struct tty_struct *tty)
+static void rs_hangup(struct tty_struct *tty)
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
struct serial_state *state = info->state;
@@ -2567,7 +2692,7 @@
return 0;
}

-int get_async_struct(int line, struct async_struct **ret_info)
+static int get_async_struct(int line, struct async_struct **ret_info)
{
struct async_struct *info;
struct serial_state *sstate;
@@ -2609,7 +2734,7 @@
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
-int rs_open(struct tty_struct *tty, struct file * filp)
+static int rs_open(struct tty_struct *tty, struct file * filp)
{
struct async_struct *info;
int retval, line;
@@ -2677,6 +2802,106 @@
}

/*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct serial_state *state)
+{
+ struct async_struct *info = state->info, scr_info;
+ char stat_buf[30], control, status;
+ int ret;
+
+ ret = sprintf(buf, "%d: uart:%s port:%X irq:%d",
+ state->line, uart_config[state->type].name,
+ state->port, state->irq);
+
+ if (!state->port || (state->type == PORT_UNKNOWN)) {
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+ }
+
+ /*
+ * Figure out the current RS-232 lines
+ */
+ if (!info) {
+ info = &scr_info; /* This is just for serial_{in,out} */
+
+ info->magic = SERIAL_MAGIC;
+ info->port = state->port;
+ info->flags = state->flags;
+ info->quot = 0;
+ info->tty = 0;
+ }
+ cli();
+ status = serial_in(info, UART_MSR);
+ control = info ? info->MCR : serial_in(info, UART_MCR);
+ sti();
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if (control & UART_MCR_RTS)
+ strcat(stat_buf, "|RTS");
+ if (status & UART_MSR_CTS)
+ strcat(stat_buf, "|CTS");
+ if (control & UART_MCR_DTR)
+ strcat(stat_buf, "|DTR");
+ if (status & UART_MSR_DSR)
+ strcat(stat_buf, "|DSR");
+ if (status & UART_MSR_DCD)
+ strcat(stat_buf, "|CD");
+ if (status & UART_MSR_RI)
+ strcat(stat_buf, "|RI");
+
+ if (info->quot) {
+ ret += sprintf(buf+ret, " baud:%d",
+ state->baud_base / info->quot);
+ }
+
+ ret += sprintf(buf+ret, " tx:%d rx:%d",
+ state->icount.tx, state->icount.rx);
+
+ if (state->icount.frame)
+ ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
+
+ if (state->icount.parity)
+ ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
+
+ if (state->icount.brk)
+ ret += sprintf(buf+ret, " brk:%d", state->icount.brk);
+
+ if (state->icount.overrun)
+ ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
+
+ /*
+ * Last thing is the RS-232 status lines
+ */
+ ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+ return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count, void
+ *data)
+{
+ int i, len = 0;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+ for (i = 0; i < NR_PORTS && len < 4000; i++) {
+ len += line_info(page + len, &rs_table[i]);
+ if (len+begin > off+count)
+ break;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
* ---------------------------------------------------------------------
* rs_init() and friends
*
@@ -2711,9 +2936,14 @@
*/
static int get_auto_irq(struct async_struct *info)
{
- unsigned char save_MCR, save_IER, save_ICP=0;
- unsigned short ICP=0, port = info->port;
+
+ unsigned char save_MCR, save_IER;
unsigned long timeout;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+ unsigned char save_ICP=0;
+ unsigned short ICP=0, port = info->port;
+#endif
+

/*
* Enable interrupts and see who answers
@@ -2722,6 +2952,7 @@
cli();
save_IER = serial_inp(info, UART_IER);
save_MCR = serial_inp(info, UART_MCR);
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
@@ -2729,7 +2960,9 @@
save_ICP = inb_p(ICP);
outb_p(0x80, ICP);
(void) inb_p(ICP);
- } else {
+ } else
+#endif
+ {
serial_outp(info, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
@@ -2754,8 +2987,10 @@
cli();
serial_outp(info, UART_IER, save_IER);
serial_outp(info, UART_MCR, save_MCR);
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT)
outb_p(save_ICP, ICP);
+#endif
sti();
return(rs_irq_triggered);
}
@@ -2912,7 +3147,7 @@
serial_outp(info, UART_LCR, 0);
serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 5;
- if (scratch == 7)
+ if (scratch == 6)
state->type = PORT_16750;
}
serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
@@ -2981,7 +3216,10 @@
for (i = 0; i < 16; i++) {
IRQ_ports[i] = 0;
IRQ_timeout[i] = 0;
- memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
+#ifdef CONFIG_SERIAL_MULTIPORT
+ memset(&rs_multiport[i], 0,
+ sizeof(struct rs_multiport_struct));
+#endif
}

show_serial_version();
@@ -2990,6 +3228,7 @@

memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "serial";
serial_driver.name = "ttyS";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
@@ -3022,7 +3261,8 @@
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
serial_driver.wait_until_sent = rs_wait_until_sent;
-
+ serial_driver.read_proc = rs_read_proc;
+
/*
* The callout device is just like normal device except for
* major number and the subtype code.
@@ -3031,6 +3271,8 @@
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 serial driver\n");
@@ -3048,22 +3290,26 @@
state->normal_termios = serial_driver.init_termios;
state->icount.cts = state->icount.dsr =
state->icount.rng = state->icount.dcd = 0;
+ state->icount.rx = state->icount.tx = 0;
+ state->icount.frame = state->icount.parity = 0;
+ state->icount.overrun = state->icount.brk = 0;
if (state->irq == 2)
state->irq = 9;
if (state->type == PORT_UNKNOWN) {
if (!(state->flags & ASYNC_BOOT_AUTOCONF))
continue;
+ if (check_region(state->port,8))
+ continue;
autoconfig(state);
if (state->type == PORT_UNKNOWN)
continue;
}
- printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d) is a %s\n",
+ printk(KERN_INFO "ttyS%02d%s at 0x%04x (irq = %d) is a %s\n",
state->line,
(state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
state->port, state->irq,
uart_config[state->type].name);
}
-
return 0;
}

@@ -3161,5 +3407,10 @@
if (rs_table[i].type != PORT_UNKNOWN)
release_region(rs_table[i].port, 8);
}
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = NULL;
+ }
}
#endif /* MODULE */
+
===================================================================
RCS file: drivers/char/RCS/pty.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/pty.c
--- drivers/char/pty.c 1997/02/27 03:41:02 1.1
+++ drivers/char/pty.c 1997/02/27 03:41:54
@@ -4,14 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/

-/*
- * pty.c
- *
- * This module exports the following pty function:
- *
- * int pty_open(struct tty_struct * tty, struct file * filp);
- */
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
@@ -47,8 +39,8 @@
static unsigned char *tmp_buf;
static struct semaphore tmp_buf_sem = MUTEX;

-struct tty_driver pty_driver, pty_slave_driver;
-struct tty_driver old_pty_driver, old_pty_slave_driver;
+static struct tty_driver pty_driver, pty_slave_driver;
+static struct tty_driver old_pty_driver, old_pty_slave_driver;
static int pty_refcount;

static struct tty_struct *pty_table[NR_PTYS];
@@ -74,8 +66,10 @@
}
wake_up_interruptible(&tty->read_wait);
wake_up_interruptible(&tty->write_wait);
+ tty->packet = 0;
if (!tty->link)
return;
+ tty->link->packet = 0;
wake_up_interruptible(&tty->link->read_wait);
wake_up_interruptible(&tty->link->write_wait);
set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
@@ -125,7 +119,12 @@
((tty->driver.subtype-1) * PTY_BUF_SIZE);
while (count > 0) {
n = MIN(count, PTY_BUF_SIZE);
- copy_from_user(temp_buffer, buf, n);
+ n -= copy_from_user(temp_buffer, buf, n);
+ if (!n) {
+ if (!c)
+ c = -EFAULT;
+ break;
+ }
r = to->ldisc.receive_room(to);
if (r <= 0)
break;
@@ -179,7 +178,7 @@
}
}

-int pty_open(struct tty_struct *tty, struct file * filp)
+static int pty_open(struct tty_struct *tty, struct file * filp)
{
int retval;
int line;
@@ -230,6 +229,7 @@
memset(&pty_state, 0, sizeof(pty_state));
memset(&pty_driver, 0, sizeof(struct tty_driver));
pty_driver.magic = TTY_DRIVER_MAGIC;
+ pty_driver.driver_name = "pty_master";
pty_driver.name = "pty";
pty_driver.major = PTY_MASTER_MAJOR;
pty_driver.minor_start = 0;
@@ -258,6 +258,8 @@
pty_driver.set_termios = pty_set_termios;

pty_slave_driver = pty_driver;
+ pty_slave_driver.driver_name = "pty_slave";
+ pty_slave_driver.proc_entry = 0;
pty_slave_driver.name = "ttyp";
pty_slave_driver.subtype = PTY_TYPE_SLAVE;
pty_slave_driver.major = PTY_SLAVE_MAJOR;
@@ -270,12 +272,16 @@
pty_slave_driver.other = &pty_driver;

old_pty_driver = pty_driver;
+ old_pty_driver.driver_name = "compat_pty_master";
+ old_pty_driver.proc_entry = 0;
old_pty_driver.major = TTY_MAJOR;
old_pty_driver.minor_start = 128;
old_pty_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
old_pty_driver.other = &old_pty_slave_driver;

old_pty_slave_driver = pty_slave_driver;
+ old_pty_slave_driver.driver_name = "compat_pty_slave";
+ old_pty_slave_driver.proc_entry = 0;
old_pty_slave_driver.major = TTY_MAJOR;
old_pty_slave_driver.minor_start = 192;
old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
===================================================================
RCS file: include/linux/RCS/proc_fs.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/proc_fs.h
--- include/linux/proc_fs.h 1997/02/27 03:41:02 1.1
+++ include/linux/proc_fs.h 1997/02/27 06:56:15
@@ -223,6 +223,10 @@
void (*fill_inode)(struct inode *);
struct proc_dir_entry *next, *parent, *subdir;
void *data;
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, void *data);
+ int (*write_proc)(struct file *file, const char *buffer,
+ unsigned long count, void *data);
};

extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
@@ -327,6 +331,7 @@
extern int proc_openprom_unregdev(struct openpromfs_dev *);

extern struct inode_operations proc_dir_inode_operations;
+extern struct inode_operations proc_file_inode_operations;
extern struct inode_operations proc_net_inode_operations;
extern struct inode_operations proc_netdir_inode_operations;
extern struct inode_operations proc_scsi_inode_operations;
@@ -344,3 +349,16 @@
extern struct inode_operations proc_ringbuf_inode_operations;
#endif
#endif
+
+/*
+ * generic.c
+ */
+struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+ struct proc_dir_entry *parent);
+
+/*
+ * proc_tty.c
+ */
+extern void proc_tty_init(void);
+extern void proc_tty_register_driver(struct tty_driver *driver);
+extern void proc_tty_unregister_driver(struct tty_driver *driver);
===================================================================
RCS file: drivers/net/RCS/slip.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/net/slip.c
--- drivers/net/slip.c 1997/02/27 03:41:02 1.1
+++ drivers/net/slip.c 1997/02/27 03:41:54
@@ -1120,6 +1120,7 @@
/* Fill in our line protocol discipline, and register it */
memset(&sl_ldisc, 0, sizeof(sl_ldisc));
sl_ldisc.magic = TTY_LDISC_MAGIC;
+ sl_ldisc.name = "slip";
sl_ldisc.flags = 0;
sl_ldisc.open = slip_open;
sl_ldisc.close = slip_close;
===================================================================
RCS file: drivers/net/RCS/ppp.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/net/ppp.c
--- drivers/net/ppp.c 1997/02/27 03:41:02 1.1
+++ drivers/net/ppp.c 1997/02/27 03:41:54
@@ -358,6 +358,7 @@
*/
(void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
ppp_ldisc.magic = TTY_LDISC_MAGIC;
+ ppp_ldisc.name = "ppp";
ppp_ldisc.open = ppp_tty_open;
ppp_ldisc.close = ppp_tty_close;
ppp_ldisc.read = ppp_tty_read;
===================================================================
RCS file: fs/proc/RCS/Makefile,v
retrieving revision 1.1
diff -u -r1.1 fs/proc/Makefile
--- fs/proc/Makefile 1997/02/27 03:41:02 1.1
+++ fs/proc/Makefile 1997/02/27 03:41:54
@@ -8,7 +8,8 @@
# Note 2! The CFLAGS definitions are now in the main makefile...

O_TARGET := proc.o
-O_OBJS := inode.o root.o base.o mem.o link.o fd.o array.o kmsg.o net.o scsi.o
+O_OBJS := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \
+ kmsg.o net.o scsi.o proc_tty.o
OX_OBJS := procfs_syms.o
M_OBJS :=

===================================================================
RCS file: fs/proc/RCS/root.c,v
retrieving revision 1.1
diff -u -r1.1 fs/proc/root.c
--- fs/proc/root.c 1997/02/27 03:41:02 1.1
+++ fs/proc/root.c 1997/02/27 03:41:54
@@ -299,13 +299,41 @@
extern void openpromfs_init (void);
#endif /* CONFIG_SUN_OPENPROMFS */

+static int make_inode_number(void)
+{
+ int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
+ if (i<0 || i>=PROC_NDYNAMIC)
+ return -1;
+ set_bit(i, (void *) proc_alloc_map);
+ return PROC_DYNAMIC_FIRST + i;
+}
+
int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
{
+ int i;
+
+ if (dp->low_ino == 0) {
+ i = make_inode_number();
+ if (i < 0)
+ return -EAGAIN;
+ dp->low_ino = i;
+ }
dp->next = dir->subdir;
dp->parent = dir;
dir->subdir = dp;
- if (S_ISDIR(dp->mode))
+ if (S_ISDIR(dp->mode)) {
+ if (dp->ops == NULL)
+ dp->ops = &proc_dir_inode_operations;
dir->nlink++;
+ } else {
+ if (dp->ops == NULL)
+ dp->ops = &proc_file_inode_operations;
+ }
+ /*
+ * kludge until we fixup the md device driver
+ */
+ if (dp->low_ino == PROC_MD)
+ dp->ops = &proc_array_inode_operations;
return 0;
}

@@ -330,28 +358,16 @@
return -EINVAL;
}

-static int make_inode_number(void)
-{
- int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
- if (i<0 || i>=PROC_NDYNAMIC)
- return -1;
- set_bit(i, (void *) proc_alloc_map);
- return PROC_DYNAMIC_FIRST + i;
-}
-
int proc_register_dynamic(struct proc_dir_entry * dir,
struct proc_dir_entry * dp)
{
- int i = make_inode_number();
- if (i < 0)
- return -EAGAIN;
- dp->low_ino = i;
- dp->next = dir->subdir;
- dp->parent = dir;
- dir->subdir = dp;
- if (S_ISDIR(dp->mode))
- dir->nlink++;
- return 0;
+ /*
+ * Make sure we use a dynamically allocated inode.
+ * In the future, all procedures should just call
+ * proc_register....
+ */
+ dp->low_ino = 0;
+ return proc_register(dir, dp);
}

/*
@@ -404,38 +420,46 @@
static struct proc_dir_entry proc_root_loadavg = {
PROC_LOADAVG, 7, "loadavg",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_uptime = {
PROC_UPTIME, 6, "uptime",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_meminfo = {
PROC_MEMINFO, 7, "meminfo",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_kmsg = {
PROC_KMSG, 4, "kmsg",
S_IFREG | S_IRUSR, 1, 0, 0,
+ 0, &proc_kmsg_inode_operations
};
static struct proc_dir_entry proc_root_version = {
PROC_VERSION, 7, "version",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#ifdef CONFIG_PCI
static struct proc_dir_entry proc_root_pci = {
PROC_PCI, 3, "pci",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#endif
#ifdef CONFIG_ZORRO
static struct proc_dir_entry proc_root_zorro = {
PROC_ZORRO, 5, "zorro",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#endif
static struct proc_dir_entry proc_root_cpuinfo = {
PROC_CPUINFO, 7, "cpuinfo",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_self = {
PROC_SELF, 4, "self",
@@ -446,81 +470,99 @@
static struct proc_dir_entry proc_root_malloc = {
PROC_MALLOC, 6, "malloc",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#endif
static struct proc_dir_entry proc_root_kcore = {
PROC_KCORE, 5, "kcore",
S_IFREG | S_IRUSR, 1, 0, 0,
+ 0, &proc_kcore_inode_operations
};
#ifdef CONFIG_MODULES
static struct proc_dir_entry proc_root_modules = {
PROC_MODULES, 7, "modules",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_ksyms = {
PROC_KSYMS, 5, "ksyms",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#endif
static struct proc_dir_entry proc_root_stat = {
PROC_STAT, 4, "stat",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_devices = {
PROC_DEVICES, 7, "devices",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_interrupts = {
PROC_INTERRUPTS, 10,"interrupts",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#ifdef __SMP_PROF__
static struct proc_dir_entry proc_root_smp = {
PROC_SMP_PROF, 3,"smp",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#endif
static struct proc_dir_entry proc_root_filesystems = {
PROC_FILESYSTEMS, 11,"filesystems",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_dma = {
PROC_DMA, 3, "dma",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_ioports = {
PROC_IOPORTS, 7, "ioports",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_cmdline = {
PROC_CMDLINE, 7, "cmdline",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#ifdef CONFIG_RTC
static struct proc_dir_entry proc_root_rtc = {
PROC_RTC, 3, "rtc",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
#endif
static struct proc_dir_entry proc_root_locks = {
PROC_LOCKS, 5, "locks",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_mounts = {
PROC_MTAB, 6, "mounts",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_swaps = {
PROC_SWAP, 5, "swaps",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};
static struct proc_dir_entry proc_root_profile = {
PROC_PROFILE, 7, "profile",
S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
+ 0, &proc_profile_inode_operations
};
static struct proc_dir_entry proc_root_slab = {
PROC_SLABINFO, 8, "slabinfo",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
};

void proc_root_init(void)
@@ -555,6 +597,7 @@
proc_register(&proc_root, &proc_root_malloc);
#endif
proc_register(&proc_root, &proc_root_kcore);
+ proc_root_kcore.size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;

#ifdef CONFIG_MODULES
proc_register(&proc_root, &proc_root_modules);
@@ -589,7 +632,10 @@

if (prof_shift) {
proc_register(&proc_root, &proc_root_profile);
+ proc_root_profile.size = (1+prof_len) * sizeof(unsigned long);
}
+
+ proc_tty_init();
}


@@ -666,8 +712,17 @@
{
unsigned int pid, c;
int i, ino, retval;
+ struct task_struct *p;

dir->i_count++;
+
+ if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */
+ dir->i_nlink = proc_root.nlink;
+ for_each_task(p)
+ if (p && p->pid)
+ dir->i_nlink++;
+ }
+
retval = proc_lookup(dir, name, len, result);
if (retval != -ENOENT) {
iput(dir);
===================================================================
RCS file: fs/proc/RCS/inode.c,v
retrieving revision 1.1
diff -u -r1.1 fs/proc/inode.c
--- fs/proc/inode.c 1997/02/27 03:41:02 1.1
+++ fs/proc/inode.c 1997/02/27 03:41:54
@@ -81,6 +81,7 @@
struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de)
{
struct inode * inode = iget(s, ino);
+ struct task_struct *p;

#ifdef CONFIG_SUN_OPENPROMFS_MODULE
if ((inode->i_ino >= PROC_OPENPROM_FIRST)
@@ -106,6 +107,14 @@
de->fill_inode(inode);
}
}
+ /*
+ * Fixup the root inode's nlink value
+ */
+ if (inode->i_ino == PROC_ROOT_INO) {
+ for_each_task(p)
+ if (p && p->pid)
+ inode->i_nlink++;
+ }
return inode;
}

@@ -162,92 +171,19 @@
inode->i_nlink = 1;
inode->i_size = 0;
pid = ino >> 16;
+ if (!pid)
+ return;
p = task[0];
for (i = 0; i < NR_TASKS ; i++)
if ((p = task[i]) && (p->pid == pid))
break;
if (!p || i >= NR_TASKS)
return;
- if (ino == PROC_ROOT_INO) {
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
- inode->i_nlink = 2;
- for (i = 1 ; i < NR_TASKS ; i++)
- if (task[i])
- inode->i_nlink++;
- return;
- }

- if (!pid) {
- switch (ino) {
- case PROC_KMSG:
- inode->i_mode = S_IFREG | S_IRUSR;
- inode->i_op = &proc_kmsg_inode_operations;
- break;
- case PROC_NET:
- inode->i_nlink = 2;
- break;
- case PROC_SCSI:
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
- inode->i_nlink = 2;
- inode->i_op = &proc_scsi_inode_operations;
- break;
- case PROC_KCORE:
- inode->i_mode = S_IFREG | S_IRUSR;
- inode->i_op = &proc_kcore_inode_operations;
- inode->i_size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;
- break;
- case PROC_PROFILE:
- inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
- inode->i_op = &proc_profile_inode_operations;
- inode->i_size = (1+prof_len) * sizeof(unsigned long);
- break;
- default:
- inode->i_mode = S_IFREG | S_IRUGO;
- inode->i_op = &proc_array_inode_operations;
- break;
- }
- return;
- }
ino &= 0x0000ffff;
if (ino == PROC_PID_INO || p->dumpable) {
inode->i_uid = p->euid;
inode->i_gid = p->egid;
- }
- switch (ino) {
- case PROC_PID_INO:
- inode->i_nlink = 4;
- return;
- case PROC_PID_MEM:
- inode->i_op = &proc_mem_inode_operations;
- inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
- return;
- case PROC_PID_CWD:
- case PROC_PID_ROOT:
- case PROC_PID_EXE:
- inode->i_op = &proc_link_inode_operations;
- inode->i_size = 64;
- inode->i_mode = S_IFLNK | S_IRWXU;
- return;
- case PROC_PID_FD:
- inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
- inode->i_op = &proc_fd_inode_operations;
- inode->i_nlink = 2;
- return;
- case PROC_PID_ENVIRON:
- inode->i_mode = S_IFREG | S_IRUSR;
- inode->i_op = &proc_array_inode_operations;
- return;
- case PROC_PID_CMDLINE:
- case PROC_PID_STATUS:
- case PROC_PID_STAT:
- case PROC_PID_STATM:
- inode->i_mode = S_IFREG | S_IRUGO;
- inode->i_op = &proc_array_inode_operations;
- return;
- case PROC_PID_MAPS:
- inode->i_mode = S_IFIFO | S_IRUGO;
- inode->i_op = &proc_arraylong_inode_operations;
- return;
}
switch (ino >> 8) {
case PROC_PID_FD_DIR:
--- /dev/null Mon Dec 31 23:00:00 1979
+++ fs/proc/generic.c Wed Feb 26 22:41:54 1997
@@ -0,0 +1,195 @@
+/*
+ * proc/fs/generic.c --- generic routines for the proc-fs
+ *
+ * This file contains generic proc-fs routines for handling
+ * directories and files.
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds.
+ * Copyright (C) 1997 Theodore Ts'o
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <asm/bitops.h>
+
+static long proc_file_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long nbytes);
+static long proc_file_write(struct inode * inode, struct file * file,
+ const char * buffer, unsigned long count);
+static long long proc_file_lseek(struct inode * inode, struct file * file,
+ long long offset, int orig);
+
+static struct file_operations proc_file_operations = {
+ proc_file_lseek, /* lseek */
+ proc_file_read, /* read */
+ proc_file_write, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/*
+ * proc files can do almost nothing..
+ */
+struct inode_operations proc_file_inode_operations = {
+ &proc_file_operations, /* default scsi directory file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+static long proc_file_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long nbytes)
+{
+ char *page;
+ int retval=0;
+ int n;
+ char *start;
+ struct proc_dir_entry * dp;
+
+ if (nbytes < 0)
+ return -EINVAL;
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ while (nbytes > 0)
+ {
+ n = MIN(PROC_BLOCK_SIZE, nbytes);
+
+ if (dp->get_info) {
+ /*
+ * Handle backwards compatibility with the old net
+ * routines.
+ *
+ * XXX What gives with the file->f_flags & O_ACCMODE
+ * test? Seems stupid to me....
+ */
+ n = dp->get_info(page, &start, file->f_pos, n,
+ (file->f_flags & O_ACCMODE) == O_RDWR);
+ } else if (dp->read_proc) {
+ n = dp->read_proc(page, &start, file->f_pos,
+ n, dp->data);
+ } else
+ break;
+
+ if (n == 0)
+ break; /* End of file */
+ if (n < 0) {
+ if (retval == 0)
+ retval = n;
+ break;
+ }
+
+ n -= copy_to_user(buf, start, n);
+ if (n == 0) {
+ if (retval == 0)
+ retval = -EFAULT;
+ break;
+ }
+
+ file->f_pos += n; /* Move down the file */
+ nbytes -= n;
+ buf += n;
+ retval += n;
+ }
+ free_page((unsigned long) page);
+ return retval;
+}
+
+static long
+proc_file_write(struct inode * inode, struct file * file,
+ const char * buffer, unsigned long count)
+{
+ struct proc_dir_entry * dp;
+ char *page;
+
+ if (count < 0)
+ return -EINVAL;
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (!dp->write_proc)
+ return -EIO;
+
+ return dp->write_proc(file, buffer, count, dp->data);
+}
+
+
+
+static long long proc_file_lseek(struct inode * inode, struct file * file,
+ long long offset, int orig)
+{
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ return(file->f_pos);
+ case 1:
+ file->f_pos += offset;
+ return(file->f_pos);
+ case 2:
+ return(-EINVAL);
+ default:
+ return(-EINVAL);
+ }
+}
+
+struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+ struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *ent;
+
+ ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+ if (!ent)
+ return NULL;
+ memset(ent, 0, sizeof(struct proc_dir_entry));
+
+ if (mode == S_IFDIR)
+ mode |= S_IRUGO | S_IXUGO;
+ else if (mode == 0)
+ mode = S_IFREG | S_IRUGO;
+
+ ent->name = name;
+ ent->namelen = strlen(ent->name);
+ ent->mode = mode;
+ if (S_ISDIR(mode))
+ ent->nlink = 2;
+ else
+ ent->nlink = 1;
+
+ if (parent)
+ proc_register(parent, ent);
+
+ return ent;
+}
+
--- /dev/null Mon Dec 31 23:00:00 1979
+++ fs/proc/proc_tty.c Wed Feb 26 22:41:54 1997
@@ -0,0 +1,183 @@
+/*
+ * proc_tty.c -- handles /proc/tty
+ *
+ * Copyright 1997, Theodore Ts'o
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/config.h>
+#include <asm/bitops.h>
+
+extern struct tty_driver *tty_drivers; /* linked list of tty drivers */
+extern struct tty_ldisc ldiscs[];
+
+
+static int tty_drivers_read_proc(char *page, char **start, off_t off,
+ int count, void *data);
+static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
+ int count, void *data);
+
+/*
+ * The /proc/tty directory inodes...
+ */
+static struct proc_dir_entry *proc_tty, *proc_tty_ldisc, *proc_tty_driver;
+
+/*
+ * This is the handler for /proc/tty/drivers
+ */
+static int tty_drivers_read_proc(char *page, char **start, off_t off,
+ int count, void *data)
+{
+ int len = 0;
+ off_t begin = 0;
+ struct tty_driver *p;
+ char range[20], deftype[20];
+ char *type;
+
+ for (p = tty_drivers; p; p = p->next) {
+ if (p->num > 1)
+ sprintf(range, "%d-%d", p->minor_start,
+ p->minor_start + p->num - 1);
+ else
+ sprintf(range, "%d", p->minor_start);
+ switch (p->type) {
+ case TTY_DRIVER_TYPE_SYSTEM:
+ if (p->subtype == SYSTEM_TYPE_TTY)
+ type = "system:/dev/tty";
+ else if (p->subtype == SYSTEM_TYPE_CONSOLE)
+ type = "system:console";
+ else
+ type = "system";
+ break;
+ case TTY_DRIVER_TYPE_CONSOLE:
+ type = "console";
+ break;
+ case TTY_DRIVER_TYPE_SERIAL:
+ if (p->subtype == 2)
+ type = "serial:callout";
+ else
+ type = "serial";
+ break;
+ case TTY_DRIVER_TYPE_PTY:
+ if (p->subtype == PTY_TYPE_MASTER)
+ type = "pty:master";
+ else if (p->subtype == PTY_TYPE_SLAVE)
+ type = "pty:slave";
+ else
+ type = "pty";
+ break;
+ default:
+ sprintf(deftype, "type:%d.%d", p->type, p->subtype);
+ type = deftype;
+ break;
+ }
+ len += sprintf(page+len, "%-20s /dev/%-8s %3d %7s %s\n",
+ p->driver_name ? p->driver_name : "",
+ p->name, p->major, range, type);
+ if (len+begin > off+count)
+ break;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * This is the handler for /proc/tty/ldiscs
+ */
+static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
+ int count, void *data)
+{
+ int i;
+ int len = 0;
+ off_t begin = 0;
+
+ for (i=0; i < NR_LDISCS; i++) {
+ if (!(ldiscs[i].flags & LDISC_FLAG_DEFINED))
+ continue;
+ len += sprintf(page+len, "%-10s %2d\n",
+ ldiscs[i].name ? ldiscs[i].name : "???", i);
+ if (len+begin > off+count)
+ break;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * Thsi function is called by register_tty_driver() to handle
+ * registering the driver's /proc handler into /proc/tty/driver/<foo>
+ */
+void proc_tty_register_driver(struct tty_driver *driver)
+{
+ struct proc_dir_entry *ent;
+
+ if ((!driver->read_proc && !driver->write_proc) ||
+ !driver->driver_name ||
+ driver->proc_entry)
+ return;
+
+ ent = create_proc_entry(driver->driver_name, 0, proc_tty_driver);
+ if (!ent)
+ return;
+ ent->read_proc = driver->read_proc;
+ ent->write_proc = driver->write_proc;
+ ent->data = driver;
+
+ driver->proc_entry = ent;
+}
+
+/*
+ * This function is called by unregister_tty_driver()
+ */
+void proc_tty_unregister_driver(struct tty_driver *driver)
+{
+ struct proc_dir_entry *ent;
+
+ ent = driver->proc_entry;
+ if (!ent)
+ return;
+
+ proc_unregister(proc_tty_driver, ent->low_ino);
+
+ driver->proc_entry = 0;
+ kfree(ent);
+}
+
+/*
+ * Called by proc_root_init() to initialize the /proc/tty subtree
+ */
+void proc_tty_init(void)
+{
+ struct proc_dir_entry *ent;
+
+ proc_tty = create_proc_entry("tty", S_IFDIR, &proc_root);
+ if (!proc_tty)
+ return;
+ proc_tty_ldisc = create_proc_entry("ldisc", S_IFDIR, proc_tty);
+ proc_tty_driver = create_proc_entry("driver", S_IFDIR, proc_tty);
+
+ ent = create_proc_entry("ldiscs", 0, proc_tty);
+ ent->read_proc = tty_ldiscs_read_proc;
+
+ ent = create_proc_entry("drivers", 0, proc_tty);
+ ent->read_proc = tty_drivers_read_proc;
+}
+