Messages in this thread Patch in this message | | | Date | Fri, 28 Feb 1997 21:45:38 GMT | Subject | tty/procfs patches | From | tytso@MIT ... |
| |
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; +} +
| |