summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Config.in2
-rw-r--r--drivers/char/Makefile18
-rw-r--r--drivers/char/agp/agp.h2
-rw-r--r--drivers/char/amikeyb.c7
-rw-r--r--drivers/char/amiserial.c2264
-rw-r--r--drivers/char/console.c161
-rw-r--r--drivers/char/dsp56k.c13
-rw-r--r--drivers/char/dtlk.c12
-rw-r--r--drivers/char/dz.c1
-rw-r--r--drivers/char/esp.c22
-rw-r--r--drivers/char/ftape/lowlevel/ftape-buffer.c18
-rw-r--r--drivers/char/ftape/zftape/zftape-init.c59
-rw-r--r--drivers/char/h8.c10
-rw-r--r--drivers/char/isicom.c3
-rw-r--r--drivers/char/istallion.c17
-rw-r--r--drivers/char/joystick/joystick.c20
-rw-r--r--drivers/char/keyboard.c3
-rw-r--r--drivers/char/lp.c191
-rw-r--r--drivers/char/mac_SCC.c1529
-rw-r--r--drivers/char/mac_SCC.h321
-rw-r--r--drivers/char/mem.c48
-rw-r--r--drivers/char/misc.c16
-rw-r--r--drivers/char/nvram.c2
-rw-r--r--drivers/char/pc_keyb.c9
-rw-r--r--drivers/char/pcxx.c1
-rw-r--r--drivers/char/ppdev.c13
-rw-r--r--drivers/char/pty.c23
-rw-r--r--drivers/char/rtc.c30
-rw-r--r--drivers/char/serial.c30
-rw-r--r--drivers/char/serial167.c2
-rw-r--r--drivers/char/stallion.c15
-rw-r--r--drivers/char/sysrq.c3
-rw-r--r--drivers/char/tpqic02.c69
-rw-r--r--drivers/char/tty_io.c127
-rw-r--r--drivers/char/vc_screen.c473
-rw-r--r--drivers/char/videodev.c20
-rw-r--r--drivers/char/vme_scc.c1
-rw-r--r--drivers/char/vt.c12
-rw-r--r--drivers/char/zr36120_mem.c15
39 files changed, 3257 insertions, 2325 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index 42cbaab55..c34978479 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -228,7 +228,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
if [ "$CONFIG_AGP" != "n" ]; then
bool ' Intel 440LX/BX/GX support' CONFIG_AGP_INTEL
bool ' Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810
- bool ' VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA
+ bool ' VIA chipset support' CONFIG_AGP_VIA
bool ' AMD Irongate support' CONFIG_AGP_AMD
bool ' Generic SiS support' CONFIG_AGP_SIS
bool ' ALI M1541 support' CONFIG_AGP_ALI
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 42aef1cc1..4667e1fa3 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -36,8 +36,9 @@ obj-y += tty_io.o n_tty.o tty_ioctl.o mem.o raw.o pty.o misc.o random.o
# All of the (potential) objects that export symbols.
# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
-export-objs := busmouse.o console.o i2c-old.o keyboard.o \
- misc.o pty.o random.o selection.o serial.o videodev.o
+export-objs := busmouse.o console.o i2c-old.o keyboard.o sysrq.o \
+ misc.o pty.o random.o selection.o serial.o videodev.o \
+ tty_io.o
KEYMAP =defkeymap.o
KEYBD =pc_keyb.o
@@ -45,8 +46,12 @@ CONSOLE =console.o
SERIAL =serial.o
ifeq ($(ARCH),m68k)
- KEYBD =
- SERIAL =
+ ifdef CONFIG_AMIGA
+ KEYBD = amikeyb.o
+ else
+ KEYBD =
+ endif
+ SERIAL =
endif
ifeq ($(ARCH),arm)
@@ -70,6 +75,7 @@ ifneq ($(CONFIG_SUN_SERIAL),)
SERIAL =
endif
+
obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o
obj-$(CONFIG_SERIAL) += $(SERIAL)
obj-$(CONFIG_SERIAL_21285) += serial_21285.o
@@ -106,6 +112,7 @@ obj-$(CONFIG_ESPSERIAL) += esp.o
obj-$(CONFIG_SYNCLINK) += synclink.o
obj-$(CONFIG_N_HDLC) += n_hdlc.o
obj-$(CONFIG_SPECIALIX) += specialix.o
+obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
ifeq ($(CONFIG_SX),y)
obj-y += sx.o generic_serial.o
@@ -147,13 +154,14 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_MIXCOMWD) += mixcomwd.o
obj-$(CONFIG_AMIGAMOUSE) += amigamouse.o
obj-$(CONFIG_ATARIMOUSE) += atarimouse.o
-obj-$(CONFIG_ADBMOUSE) += adbmouse.o
+obj-$(CONFIG_ADBMOUSE) += adbmouse.o busmouse.o
obj-$(CONFIG_PC110_PAD) += pc110pad.o
obj-$(CONFIG_WDT) += wdt.o
obj-$(CONFIG_RTC) += rtc.o
ifeq ($(CONFIG_PPC),)
obj-$(CONFIG_NVRAM) += nvram.o
endif
+obj-$(CONFIG_I810_RNG) += i810_rng.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
index ca7f76aa3..31f481065 100644
--- a/drivers/char/agp/agp.h
+++ b/drivers/char/agp/agp.h
@@ -27,8 +27,6 @@
#ifndef _AGP_BACKEND_PRIV_H
#define _AGP_BACKEND_PRIV_H 1
-#include <linux/config.h>
-
enum aper_size_type {
U8_APER_SIZE,
U16_APER_SIZE,
diff --git a/drivers/char/amikeyb.c b/drivers/char/amikeyb.c
index 42e1ea7c8..41bb7ac15 100644
--- a/drivers/char/amikeyb.c
+++ b/drivers/char/amikeyb.c
@@ -17,13 +17,14 @@
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/keyboard.h>
+#include <linux/kd.h>
+#include <linux/kbd_ll.h>
#include <linux/delay.h>
#include <linux/timer.h>
-#include <linux/kd.h>
#include <linux/random.h>
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/kbd_ll.h>
+#include <linux/kbd_kern.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
@@ -230,7 +231,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp)
/* switch CIA serial port to input mode */
ciaa.cra &= ~0x40;
- mark_bh(KEYBOARD_BH);
+ tasklet_schedule(&keyboard_tasklet);
/* rotate scan code to get up/down bit in proper position */
scancode = ((scancode >> 1) & 0x7f) | ((scancode << 7) & 0x80);
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
new file mode 100644
index 000000000..ea828cc24
--- /dev/null
+++ b/drivers/char/amiserial.c
@@ -0,0 +1,2264 @@
+/*
+ * linux/drivers/char/amiserial.c
+ *
+ * Serial driver for the amiga builtin port.
+ *
+ * This code was created by taking serial.c version 4.30 from kernel
+ * release 2.3.22, replacing all hardware related stuff with the
+ * corresponding amiga hardware actions, and removing all irrelevant
+ * code. As a consequence, it uses many of the constants and names
+ * associated with the registers and bits of 16550 compatible UARTS -
+ * but only to keep track of status, etc in the state variables. It
+ * was done this was to make it easier to keep the code in line with
+ * (non hardware specific) changes to serial.c.
+ *
+ * The port is registered with the tty driver as minor device 64, and
+ * therefore other ports should should only use 65 upwards.
+ *
+ * Richard Lucock 28/12/99
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997,
+ * 1998, 1999 Theodore Ts'o
+ *
+ */
+
+/*
+ * Serial driver configuration section. Here are the various options:
+ *
+ * SERIAL_PARANOIA_CHECK
+ * Check the magic number for the async_structure where
+ * ever possible.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#undef SERIAL_PARANOIA_CHECK
+#define SERIAL_DO_RESTART
+
+/* Set of debugging defines */
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+
+/* Sanity checks */
+
+#define SERIAL_INLINE
+
+#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
+#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
+ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
+#else
+#define DBG_CNT(s)
+#endif
+
+/*
+ * End of serial driver configuration section.
+ */
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/serial_reg.h>
+static char *serial_version = "4.30";
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+
+static char *serial_name = "Amiga-builtin serial driver";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#ifndef SERIAL_TYPE_NORMAL
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+#endif
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+static struct async_struct *IRQ_ports;
+
+static unsigned char current_ctl_bits;
+
+static void change_speed(struct async_struct *info, struct termios *old);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+
+static struct serial_state rs_table[1];
+
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
+
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+#include <asm/uaccess.h>
+
+#define serial_isroot() (capable(CAP_SYS_ADMIN))
+
+
+static inline int serial_paranoia_check(struct async_struct *info,
+ kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%s) in %s\n";
+ static const char *badinfo =
+ "Warning: null async_struct for (%s) in %s\n";
+
+ if (!info) {
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/* some serial hardware definitions */
+#define SDR_OVRUN (1<<15)
+#define SDR_RBF (1<<14)
+#define SDR_TBE (1<<13)
+#define SDR_TSRE (1<<12)
+
+#define SERPER_PARENB (1<<15)
+
+#define AC_SETCLR (1<<15)
+#define AC_UARTBRK (1<<11)
+
+#define SER_DTR (1<<7)
+#define SER_RTS (1<<6)
+#define SER_DCD (1<<5)
+#define SER_CTS (1<<4)
+#define SER_DSR (1<<3)
+
+static __inline__ void rtsdtr_ctrl(int bits)
+{
+ ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR));
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_stop"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->IER & UART_IER_THRI) {
+ info->IER &= ~UART_IER_THRI;
+ /* disable Tx interrupt and remove any pending interrupts */
+ custom.intena = IF_TBE;
+ mb();
+ custom.intreq = IF_TBE;
+ mb();
+ }
+ restore_flags(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_start"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
+ info->IER |= UART_IER_THRI;
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ }
+ restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible. After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct async_struct *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(struct async_struct *info)
+{
+ int status;
+ int serdatr;
+ struct tty_struct *tty = info->tty;
+ unsigned char ch;
+ struct async_icount *icount;
+
+ icount = &info->state->icount;
+
+ status = UART_LSR_DR; /* We obviously have a character! */
+ serdatr = custom.serdatr;
+ mb();
+ custom.intreq = IF_RBF;
+ mb();
+
+ if((serdatr & 0x1ff) == 0)
+ status |= UART_LSR_BI;
+ if(serdatr & SDR_OVRUN)
+ status |= UART_LSR_OE;
+
+ ch = serdatr & 0xff;
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ goto ignore_char;
+ *tty->flip.char_buf_ptr = ch;
+ icount->rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, status);
+#endif
+ *tty->flip.flag_buf_ptr = 0;
+
+ /*
+ * We don't handle parity or frame errors - but I have left
+ * the code in, since I'm not sure that the errors can't be
+ * detected.
+ */
+
+ 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)
+ 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:
+
+ tty_flip_buffer_push(tty);
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info)
+{
+ custom.intreq = IF_TBE;
+ mb();
+ if (info->x_char) {
+ custom.serdat = info->x_char | 0x100;
+ mb();
+ info->state->icount.tx++;
+ info->x_char = 0;
+ return;
+ }
+ if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+ info->tty->hw_stopped) {
+ info->IER &= ~UART_IER_THRI;
+ custom.intena = IF_TBE;
+ mb();
+ return;
+ }
+
+ custom.serdat = info->xmit_buf[info->xmit_tail++] | 0x100;
+ mb();
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->state->icount.tx++;
+ --info->xmit_cnt;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+ if (info->xmit_cnt <= 0) {
+ custom.intena = IF_TBE;
+ mb();
+ info->IER &= ~UART_IER_THRI;
+ }
+}
+
+static _INLINE_ void check_modem_status(struct async_struct *info)
+{
+ unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+ unsigned char dstatus;
+ struct async_icount *icount;
+
+ /* Determine bits that have changed */
+ dstatus = status ^ current_ctl_bits;
+ current_ctl_bits = status;
+
+ if (dstatus) {
+ icount = &info->state->icount;
+ /* update input line counters */
+ if (dstatus & SER_DSR)
+ icount->dsr++;
+ if (dstatus & SER_DCD) {
+ icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+ if ((info->flags & ASYNC_HARDPPS_CD) &&
+ !(status & SER_DCD))
+ hardpps();
+#endif
+ }
+ if (dstatus & SER_CTS)
+ icount->cts++;
+ wake_up_interruptible(&info->delta_msr_wait);
+ }
+
+ if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttyS%02d CD now %s...", info->line,
+ (!(status & SER_DCD)) ? "on" : "off");
+#endif
+ if (!(status & SER_DCD))
+ wake_up_interruptible(&info->open_wait);
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("doing serial hangup...");
+#endif
+ if (info->tty)
+ tty_hangup(info->tty);
+ }
+ }
+ if (info->flags & ASYNC_CTS_FLOW) {
+ if (info->tty->hw_stopped) {
+ if (!(status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx start...");
+#endif
+ info->tty->hw_stopped = 0;
+ info->IER |= UART_IER_THRI;
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+ return;
+ }
+ } else {
+ if ((status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx stop...");
+#endif
+ info->tty->hw_stopped = 1;
+ info->IER &= ~UART_IER_THRI;
+ /* disable Tx interrupt and remove any pending interrupts */
+ custom.intena = IF_TBE;
+ mb();
+ custom.intreq = IF_TBE;
+ mb();
+ }
+ }
+ }
+}
+
+static void ser_vbl_int( int irq, void *data, struct pt_regs *regs)
+{
+ /* vbl is just a periodic interrupt we tie into to update modem status */
+ struct async_struct * info = IRQ_ports;
+ /*
+ * TBD - is it better to unregister from this interrupt or to
+ * ignore it if MSI is clear ?
+ */
+ if(info->IER & UART_IER_MSI)
+ check_modem_status(info);
+}
+
+static void ser_rx_int(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct async_struct * info;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_rx_int...");
+#endif
+
+ info = IRQ_ports;
+ if (!info || !info->tty)
+ return;
+
+ receive_chars(info);
+ info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+static void ser_tx_int(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct async_struct * info;
+
+ if (custom.serdatr & SDR_TBE) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_tx_int...");
+#endif
+
+ info = IRQ_ports;
+ if (!info || !info->tty)
+ return;
+
+ transmit_chars(info);
+ info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+ }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+ struct async_struct *info = (struct async_struct *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver: routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port. Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+static int startup(struct async_struct * info)
+{
+ unsigned long flags;
+ int retval=0;
+ unsigned long page;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ save_flags(flags); cli();
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ free_page(page);
+ goto errout;
+ }
+
+ if (info->xmit_buf)
+ free_page(page);
+ else
+ info->xmit_buf = (unsigned char *) page;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttys%d ...", info->line);
+#endif
+
+ /* Clear anything in the input buffer */
+
+ custom.intreq = IF_RBF;
+ mb();
+
+ retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);
+ if (retval) {
+ if (serial_isroot()) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR,
+ &info->tty->flags);
+ retval = 0;
+ }
+ goto errout;
+ }
+
+ /* enable both Rx and Tx interrupts */
+ custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
+ mb();
+ info->IER = UART_IER_MSI;
+
+ /* remember current state of the DCD and CTS bits */
+ current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+
+ IRQ_ports = info;
+
+ info->MCR = 0;
+ if (info->tty->termios->c_cflag & CBAUD)
+ info->MCR = SER_DTR | SER_RTS;
+ rtsdtr_ctrl(info->MCR);
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ /*
+ * Set up the tty->alt_speed kludge
+ */
+ if (info->tty) {
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
+ }
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info, 0);
+
+ info->flags |= ASYNC_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+
+errout:
+ restore_flags(flags);
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+ unsigned long flags;
+ struct serial_state *state;
+
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+ state = info->state;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d ....\n", info->line);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+ wake_up_interruptible(&info->delta_msr_wait);
+
+ IRQ_ports = NULL;
+
+ /*
+ * Free the IRQ, if necessary
+ */
+ free_irq(IRQ_AMIGA_VERTB, info);
+
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ info->IER = 0;
+ custom.intena = IF_RBF | IF_TBE;
+ mb();
+
+ /* disable break condition */
+ custom.adkcon = AC_UARTBRK;
+ mb();
+
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+ info->MCR &= ~(SER_DTR|SER_RTS);
+ rtsdtr_ctrl(info->MCR);
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
+ restore_flags(flags);
+}
+
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct async_struct *info,
+ struct termios *old_termios)
+{
+ int quot = 0, baud_base, baud;
+ unsigned cflag, cval = 0;
+ int bits;
+ unsigned long flags;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+
+ /* Byte size is always 8 bits plus parity bit if requested */
+
+ cval = 3; bits = 10;
+ if (cflag & CSTOPB) {
+ cval |= 0x04;
+ bits++;
+ }
+ if (cflag & PARENB) {
+ cval |= UART_LCR_PARITY;
+ bits++;
+ }
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
+
+ /* Determine divisor based on baud rate */
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600; /* B0 transition handled in rs_set_termios */
+ baud_base = info->state->baud_base;
+ if (baud == 38400 &&
+ ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+ quot = info->state->custom_divisor;
+ else {
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*baud_base / 269);
+ else if (baud)
+ quot = baud_base / baud;
+ }
+ /* If the quotient is zero refuse the change */
+ if (!quot && old_termios) {
+ info->tty->termios->c_cflag &= ~CBAUD;
+ info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+ if (baud == 38400 &&
+ ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+ quot = info->state->custom_divisor;
+ else {
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*baud_base / 269);
+ else if (baud)
+ quot = baud_base / baud;
+ }
+ }
+ /* As a last resort, if the quotient is zero, default to 9600 bps */
+ 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 */
+
+ /* CTS flow control flag and modem status interrupts */
+ info->IER &= ~UART_IER_MSI;
+ if (info->flags & ASYNC_HARDPPS_CD)
+ info->IER |= UART_IER_MSI;
+ if (cflag & CRTSCTS) {
+ info->flags |= ASYNC_CTS_FLOW;
+ info->IER |= UART_IER_MSI;
+ } else
+ info->flags &= ~ASYNC_CTS_FLOW;
+ if (cflag & CLOCAL)
+ info->flags &= ~ASYNC_CHECK_CD;
+ else {
+ info->flags |= ASYNC_CHECK_CD;
+ info->IER |= UART_IER_MSI;
+ }
+ /* TBD:
+ * Does clearing IER_MSI imply that we should disbale the VBL interrupt ?
+ */
+
+ /*
+ * Set up parity check flag
+ */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ info->read_status_mask = UART_LSR_OE | UART_LSR_DR;
+ if (I_INPCK(info->tty))
+ info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+ info->read_status_mask |= UART_LSR_BI;
+
+ /*
+ * Characters to ignore
+ */
+ info->ignore_status_mask = 0;
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (I_IGNBRK(info->tty)) {
+ info->ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignore parity and break indicators, ignore
+ * overruns too. (For real raw support).
+ */
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= UART_LSR_OE;
+ }
+ /*
+ * !!! ignore all characters if CREAD is not set
+ */
+ if ((cflag & CREAD) == 0)
+ info->ignore_status_mask |= UART_LSR_DR;
+ save_flags(flags); cli();
+
+ {
+ short serper;
+
+ /* Set up the baud rate */
+ serper = quot - 1;
+
+ /* Enable or disable parity bit */
+
+ if(cval & UART_LCR_PARITY)
+ serper |= (SERPER_PARENB);
+
+ custom.serper = serper;
+ mb();
+ }
+
+ info->LCR = cval; /* Save LCR */
+ restore_flags(flags);
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+ return;
+
+ if (!tty || !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+ restore_flags(flags);
+ return;
+ }
+
+ info->xmit_buf[info->xmit_head++] = ch;
+ info->xmit_head &= SERIAL_XMIT_SIZE-1;
+ info->xmit_cnt++;
+ restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ info->IER |= UART_IER_THRI;
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, ret = 0;
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write"))
+ return 0;
+
+ if (!tty || !info->xmit_buf || !tmp_buf)
+ return 0;
+
+ save_flags(flags);
+ if (from_user) {
+ down(&tmp_buf_sem);
+ while (1) {
+ c = MIN(count,
+ MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+ cli();
+ 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);
+ info->xmit_head = ((info->xmit_head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ up(&tmp_buf_sem);
+ } else {
+ while (1) {
+ cli();
+ c = MIN(count,
+ MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0) {
+ restore_flags(flags);
+ break;
+ }
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ info->xmit_head = ((info->xmit_head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ }
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+ !(info->IER & UART_IER_THRI)) {
+ info->IER |= UART_IER_THRI;
+ cli();
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ restore_flags(flags);
+ }
+ return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+ return;
+ save_flags(flags); cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ restore_flags(flags);
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_send_char"))
+ return;
+
+ info->x_char = ch;
+ if (ch) {
+ /* Make sure transmit interrupts are on */
+
+ /* Check this ! */
+ save_flags(flags);
+ cli();
+ if(!(custom.intenar & IF_TBE)) {
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ }
+ restore_flags(flags);
+
+ info->IER |= UART_IER_THRI;
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ rs_send_xchar(tty, STOP_CHAR(tty));
+
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->MCR &= ~SER_RTS;
+
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ rs_send_xchar(tty, START_CHAR(tty));
+ }
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->MCR |= SER_RTS;
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct async_struct * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+ struct serial_state *state = info->state;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = state->type;
+ tmp.line = state->line;
+ tmp.port = state->port;
+ tmp.irq = state->irq;
+ tmp.flags = state->flags;
+ tmp.xmit_fifo_size = state->xmit_fifo_size;
+ tmp.baud_base = state->baud_base;
+ tmp.close_delay = state->close_delay;
+ tmp.closing_wait = state->closing_wait;
+ tmp.custom_divisor = state->custom_divisor;
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int set_serial_info(struct async_struct * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct serial_state old_state, *state;
+ unsigned int change_irq,change_port;
+ int retval = 0;
+
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+ state = info->state;
+ old_state = *state;
+
+ change_irq = new_serial.irq != state->irq;
+ change_port = (new_serial.port != state->port);
+ if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size))
+ return -EINVAL;
+
+ if (!serial_isroot()) {
+ if ((new_serial.baud_base != state->baud_base) ||
+ (new_serial.close_delay != state->close_delay) ||
+ (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
+ ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ (state->flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ state->flags = ((state->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ state->custom_divisor = new_serial.custom_divisor;
+ goto check_and_exit;
+ }
+
+ if (new_serial.baud_base < 9600)
+ return -EINVAL;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ state->baud_base = new_serial.baud_base;
+ state->flags = ((state->flags & ~ASYNC_FLAGS) |
+ (new_serial.flags & ASYNC_FLAGS));
+ info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
+ (info->flags & ASYNC_INTERNAL_FLAGS));
+ state->custom_divisor = new_serial.custom_divisor;
+ state->close_delay = new_serial.close_delay * HZ/100;
+ state->closing_wait = new_serial.closing_wait * HZ/100;
+ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+check_and_exit:
+ if (info->flags & ASYNC_INITIALIZED) {
+ if (((old_state.flags & ASYNC_SPD_MASK) !=
+ (state->flags & ASYNC_SPD_MASK)) ||
+ (old_state.custom_divisor != state->custom_divisor)) {
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
+ change_speed(info, 0);
+ }
+ } else
+ retval = startup(info);
+ return retval;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct async_struct * info, unsigned int *value)
+{
+ unsigned char status;
+ unsigned int result;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ status = custom.serdatr;
+ mb();
+ restore_flags(flags);
+ result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0);
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+
+static int get_modem_info(struct async_struct * info, unsigned int *value)
+{
+ unsigned char control, status;
+ unsigned int result;
+ unsigned long flags;
+
+ control = info->MCR;
+ save_flags(flags); cli();
+ status = ciab.pra;
+ restore_flags(flags);
+ result = ((control & SER_RTS) ? TIOCM_RTS : 0)
+ | ((control & SER_DTR) ? TIOCM_DTR : 0)
+ | (!(status & SER_DCD) ? TIOCM_CAR : 0)
+ | (!(status & SER_DSR) ? TIOCM_DSR : 0)
+ | (!(status & SER_CTS) ? TIOCM_CTS : 0);
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+static int set_modem_info(struct async_struct * info, unsigned int cmd,
+ unsigned int *value)
+{
+ unsigned int arg;
+ unsigned long flags;
+
+ if (copy_from_user(&arg, value, sizeof(int)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ info->MCR |= SER_RTS;
+ if (arg & TIOCM_DTR)
+ info->MCR |= SER_DTR;
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ info->MCR &= ~SER_RTS;
+ if (arg & TIOCM_DTR)
+ info->MCR &= ~SER_DTR;
+ break;
+ case TIOCMSET:
+ info->MCR = ((info->MCR & ~(SER_RTS | SER_DTR))
+ | ((arg & TIOCM_RTS) ? SER_RTS : 0)
+ | ((arg & TIOCM_DTR) ? SER_DTR : 0));
+ break;
+ default:
+ return -EINVAL;
+ }
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_break"))
+ return;
+
+ save_flags(flags); cli();
+ if (break_state == -1)
+ custom.adkcon = AC_SETCLR | AC_UARTBRK;
+ else
+ custom.adkcon = AC_UARTBRK;
+ mb();
+ restore_flags(flags);
+}
+
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct async_icount cprev, cnow; /* kernel counter temps */
+ struct serial_icounter_struct icount;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+ (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TIOCMGET:
+ 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:
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSERCONFIG:
+ return 0;
+
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ if (copy_to_user((struct async_struct *) arg,
+ info, sizeof(struct async_struct)))
+ return -EFAULT;
+ return 0;
+
+ /*
+ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ */
+ case TIOCMIWAIT:
+ save_flags(flags); cli();
+ /* note the counters on entry */
+ cprev = info->state->icount;
+ restore_flags(flags);
+ while (1) {
+ interruptible_sleep_on(&info->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ save_flags(flags); cli();
+ cnow = info->state->icount; /* atomic copy */
+ restore_flags(flags);
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+ return 0;
+ }
+ cprev = cnow;
+ }
+ /* NOTREACHED */
+
+ /*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ * RI where only 0->1 is counted.
+ */
+ case TIOCGICOUNT:
+ save_flags(flags); cli();
+ cnow = info->state->icount;
+ restore_flags(flags);
+ icount.cts = cnow.cts;
+ icount.dsr = cnow.dsr;
+ icount.rng = cnow.rng;
+ icount.dcd = cnow.dcd;
+ icount.rx = cnow.rx;
+ icount.tx = cnow.tx;
+ icount.frame = cnow.frame;
+ icount.overrun = cnow.overrun;
+ icount.parity = cnow.parity;
+ icount.brk = cnow.brk;
+ icount.buf_overrun = cnow.buf_overrun;
+
+ if (copy_to_user((void *)arg, &icount, sizeof(icount)))
+ return -EFAULT;
+ return 0;
+ case TIOCSERGWILD:
+ case TIOCSERSWILD:
+ /* "setserial -W" is called in Debian boot */
+ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+ unsigned int cflag = tty->termios->c_cflag;
+
+ if ( (cflag == old_termios->c_cflag)
+ && ( RELEVANT_IFLAG(tty->termios->c_iflag)
+ == RELEVANT_IFLAG(old_termios->c_iflag)))
+ return;
+
+ change_speed(info, old_termios);
+
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(cflag & CBAUD)) {
+ info->MCR &= ~(SER_DTR|SER_RTS);
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ (cflag & CBAUD)) {
+ info->MCR |= SER_DTR;
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
+ info->MCR |= SER_RTS;
+ }
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+ }
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ rs_start(tty);
+ }
+
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct serial_state *state;
+ unsigned long flags;
+
+ if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+ return;
+
+ state = info->state;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ DBG_CNT("before DEC-hung");
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+ if ((tty->count == 1) && (state->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. state->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("rs_close: bad serial port count; tty->count is 1, "
+ "state->count is %d\n", state->count);
+ state->count = 1;
+ }
+ if (--state->count < 0) {
+ printk("rs_close: bad serial port count for ttys%d: %d\n",
+ info->line, state->count);
+ state->count = 0;
+ }
+ if (state->count) {
+ DBG_CNT("before DEC-2");
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->state->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->state->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ info->read_status_mask &= ~UART_LSR_DR;
+ if (info->flags & ASYNC_INITIALIZED) {
+ /* disable receive interrupts */
+ custom.intena = IF_RBF;
+ mb();
+ /* clear any pending receive interrupt */
+ custom.intreq = IF_RBF;
+ mb();
+
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ rs_wait_until_sent(tty, info->timeout);
+ }
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(info->close_delay);
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+ int lsr;
+
+ if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+ return;
+
+ if (info->xmit_fifo_size == 0)
+ return; /* Just in case.... */
+
+ orig_jiffies = jiffies;
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+ * interval should also be less than the timeout.
+ *
+ * Note: we have to use pretty tight timings here to satisfy
+ * the NIST-PCTS.
+ */
+ char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+ char_time = char_time / 5;
+ if (char_time == 0)
+ char_time = 1;
+ if (timeout)
+ char_time = MIN(char_time, timeout);
+ /*
+ * If the transmitter hasn't cleared in twice the approximate
+ * amount of time to send the entire FIFO, it probably won't
+ * ever clear. This assumes the UART isn't doing flow
+ * control, which is currently the case. Hence, if it ever
+ * takes longer than info->timeout, this is probably due to a
+ * UART bug of some kind. So, we clamp the timeout parameter at
+ * 2*info->timeout.
+ */
+ if (!timeout || timeout > 2*info->timeout)
+ timeout = 2*info->timeout;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+ printk("jiff=%lu...", jiffies);
+#endif
+ while(!((lsr = custom.serdatr) & SDR_TSRE)) {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(char_time);
+ if (signal_pending(current))
+ break;
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ break;
+ }
+ current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct serial_state *state = info->state;
+
+ if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+ return;
+
+ state = info->state;
+
+ rs_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ state->count = 0;
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct async_struct *info)
+{
+#ifdef DECLARE_WAITQUEUE
+ DECLARE_WAITQUEUE(wait, current);
+#else
+ struct wait_queue wait = { current, NULL };
+#endif
+ struct serial_state *state = info->state;
+ int retval;
+ int do_clocal = 0, extra_count = 0;
+ unsigned long flags;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (state->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, state->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttys%d, count = %d\n",
+ state->line, state->count);
+#endif
+ save_flags(flags); cli();
+ if (!tty_hung_up_p(filp)) {
+ extra_count = 1;
+ state->count--;
+ }
+ restore_flags(flags);
+ info->blocked_open++;
+ while (1) {
+ save_flags(flags); cli();
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD))
+ rtsdtr_ctrl(SER_DTR|SER_RTS);
+ restore_flags(flags);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ASYNC_CLOSING) &&
+ (do_clocal || (!(ciab.pra & SER_DCD)) ))
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (extra_count)
+ state->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+static int get_async_struct(int line, struct async_struct **ret_info)
+{
+ struct async_struct *info;
+ struct serial_state *sstate;
+
+ sstate = rs_table + line;
+ sstate->count++;
+ if (sstate->info) {
+ *ret_info = sstate->info;
+ return 0;
+ }
+ info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+ if (!info) {
+ sstate->count--;
+ return -ENOMEM;
+ }
+ memset(info, 0, sizeof(struct async_struct));
+#ifdef DECLARE_WAITQUEUE
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
+ init_waitqueue_head(&info->delta_msr_wait);
+#endif
+ info->magic = SERIAL_MAGIC;
+ info->port = sstate->port;
+ info->flags = sstate->flags;
+ info->xmit_fifo_size = sstate->xmit_fifo_size;
+ info->line = line;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->state = sstate;
+ if (sstate->info) {
+ kfree_s(info, sizeof(struct async_struct));
+ *ret_info = sstate->info;
+ return 0;
+ }
+ *ret_info = sstate->info = info;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct async_struct *info;
+ int retval, line;
+ unsigned long page;
+
+ MOD_INC_USE_COUNT;
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= NR_PORTS)) {
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+ retval = get_async_struct(line, &info);
+ if (retval) {
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+ tty->driver_data = info;
+ info->tty = tty;
+ if (serial_paranoia_check(info, tty->device, "rs_open"))
+ return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->state->count);
+#endif
+ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ if (!tmp_buf) {
+ page = get_free_page(GFP_KERNEL);
+ if (!page) {
+ return -ENOMEM;
+ }
+ if (tmp_buf)
+ free_page(page);
+ else
+ tmp_buf = (unsigned char *) page;
+ }
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval) {
+ return retval;
+ }
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->state->count == 1) &&
+ (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->state->normal_termios;
+ else
+ *tty->termios = info->state->callout_termios;
+ change_speed(info, 0);
+ }
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttys%d successful...", info->line);
+#endif
+ return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct serial_state *state)
+{
+ struct async_struct *info = state->info, scr_info;
+ char stat_buf[30], control, status;
+ int ret;
+ unsigned long flags;
+
+ ret = sprintf(buf, "%d: uart:amiga_builtin",state->line);
+
+ /*
+ * Figure out the current RS-232 lines
+ */
+ if (!info) {
+ info = &scr_info; /* This is just for serial_{in,out} */
+
+ info->magic = SERIAL_MAGIC;
+ info->flags = state->flags;
+ info->quot = 0;
+ info->tty = 0;
+ }
+ save_flags(flags); cli();
+ status = ciab.pra;
+ control = info ? info->MCR : status;
+ restore_flags(flags);
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if(!(control & SER_RTS))
+ strcat(stat_buf, "|RTS");
+ if(!(status & SER_CTS))
+ strcat(stat_buf, "|CTS");
+ if(!(control & SER_DTR))
+ strcat(stat_buf, "|DTR");
+ if(!(status & SER_DSR))
+ strcat(stat_buf, "|DSR");
+ if(!(status & SER_DCD))
+ strcat(stat_buf, "|CD");
+
+ 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,
+ int *eof, void *data)
+{
+ int len = 0, l;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+ l = line_info(page + len, &rs_table[0]);
+ len += l;
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ *eof = 1;
+done:
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static _INLINE_ void show_serial_version(void)
+{
+ printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+
+int register_serial(struct serial_struct *req);
+void unregister_serial(int line);
+
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int __init rs_init(void)
+{
+ unsigned long flags;
+ struct serial_state * state;
+
+ if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL))
+ return -ENODEV;
+
+ init_bh(SERIAL_BH, do_serial_bh);
+
+ IRQ_ports = NULL;
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "amiserial";
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = 1;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+ serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = rs_open;
+ serial_driver.close = rs_close;
+ serial_driver.write = rs_write;
+ serial_driver.put_char = rs_put_char;
+ serial_driver.flush_chars = rs_flush_chars;
+ serial_driver.write_room = rs_write_room;
+ serial_driver.chars_in_buffer = rs_chars_in_buffer;
+ serial_driver.flush_buffer = rs_flush_buffer;
+ serial_driver.ioctl = rs_ioctl;
+ serial_driver.throttle = rs_throttle;
+ serial_driver.unthrottle = rs_unthrottle;
+ serial_driver.set_termios = rs_set_termios;
+ serial_driver.stop = rs_stop;
+ serial_driver.start = rs_start;
+ serial_driver.hangup = rs_hangup;
+ serial_driver.break_ctl = rs_break;
+ serial_driver.send_xchar = rs_send_xchar;
+ 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.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+ callout_driver.read_proc = 0;
+ callout_driver.proc_entry = 0;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ state = rs_table;
+ state->magic = SSTATE_MAGIC;
+ state->port = (int)&custom.serdatr; /* Just to give it a value */
+ state->line = 0;
+ state->custom_divisor = 0;
+ state->close_delay = 5*HZ/10;
+ state->closing_wait = 30*HZ;
+ state->callout_termios = callout_driver.init_termios;
+ 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->port && check_region(state->port,REGION_LENGTH(state)))
+ continue;
+ */
+
+ printk(KERN_INFO "ttyS%02d is the amiga builtin serial port\n",
+ state->line);
+
+ /* Hardware set up */
+
+ state->baud_base = amiga_colorclock;
+ state->xmit_fifo_size = 1;
+
+ save_flags (flags);
+ cli();
+
+ /* set ISRs, and then disable the rx interrupts */
+ request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state);
+ request_irq(IRQ_AMIGA_RBF, ser_rx_int, SA_INTERRUPT, "serial RX", state);
+
+ /* turn off Rx and Tx interrupts */
+ custom.intena = IF_RBF | IF_TBE;
+ mb();
+
+ /* clear any pending interrupt */
+ custom.intreq = IF_RBF | IF_TBE;
+ mb();
+
+ restore_flags (flags);
+
+ /*
+ * set the appropriate directions for the modem control flags,
+ * and clear RTS and DTR
+ */
+ ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */
+ ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return rs_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned long flags;
+ int e1, e2;
+ struct async_struct *info;
+
+ /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
+ save_flags(flags);
+ cli();
+ remove_bh(SERIAL_BH);
+ if ((e1 = tty_unregister_driver(&serial_driver)))
+ printk("SERIAL: failed to unregister serial driver (%d)\n",
+ e1);
+ if ((e2 = tty_unregister_driver(&callout_driver)))
+ printk("SERIAL: failed to unregister callout driver (%d)\n",
+ e2);
+ restore_flags(flags);
+
+ info = rs_table[0].info;
+ if (info) {
+ rs_table[0].info = NULL;
+ kfree_s(info, sizeof(struct async_struct));
+ }
+
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = NULL;
+ }
+}
+#endif /* MODULE */
+
+/*
+ Local variables:
+ compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -DCPU=686 -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c"
+ End:
+*/
diff --git a/drivers/char/console.c b/drivers/char/console.c
index 85c638ef2..a1c9f3cf3 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -81,6 +81,7 @@
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/vt_kern.h>
#include <linux/selection.h>
#include <linux/console_struct.h>
@@ -120,6 +121,10 @@ struct consw *conswitchp = NULL;
#define DEFAULT_BELL_PITCH 750
#define DEFAULT_BELL_DURATION (HZ/8)
+extern int tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+ unsigned int minor);
+extern void vcs_make_devfs (unsigned int index, int unregister);
+
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
@@ -177,12 +182,13 @@ static struct vc_data *master_display_fg = NULL;
* Unfortunately, we need to delay tty echo when we're currently writing to the
* console since the code is (and always was) not re-entrant, so we insert
* all filp requests to con_task_queue instead of tq_timer and run it from
- * the console_bh.
+ * the console_tasklet. The console_tasklet is protected by the IRQ
+ * protected console_lock.
*/
DECLARE_TASK_QUEUE(con_task_queue);
/*
- * For the same reason, we defer scrollback to the console_bh.
+ * For the same reason, we defer scrollback to the console tasklet.
*/
static int scrollback_delta = 0;
@@ -224,7 +230,7 @@ static inline unsigned short *screenpos(int currcons, int offset, int viewed)
static inline void scrolldelta(int lines)
{
scrollback_delta += lines;
- mark_bh(CONSOLE_BH);
+ tasklet_schedule(&console_tasklet);
}
static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
@@ -546,16 +552,12 @@ void redraw_screen(int new_console, int is_switch)
{
int redraw = 1;
int currcons, old_console;
- static int lock = 0;
- if (lock)
- return;
if (!vc_cons_allocated(new_console)) {
/* strange ... */
- printk("redraw_screen: tty %d not allocated ??\n", new_console+1);
+ /* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */
return;
}
- lock = 1;
if (is_switch) {
currcons = fg_console;
@@ -591,7 +593,6 @@ void redraw_screen(int new_console, int is_switch)
set_leds();
compute_shiftstate();
}
- lock = 0;
}
/*
@@ -1785,6 +1786,19 @@ static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c)
}
}
+/* This is a temporary buffer used to prepare a tty console write
+ * so that we can easily avoid touching user space while holding the
+ * console spinlock. It is allocated in con_init and is shared by
+ * this code and the vc_screen read/write tty calls.
+ *
+ * We have to allocate this statically in the kernel data section
+ * since console_init (and thus con_init) are called before any
+ * kernel memory allocation is available.
+ */
+char con_buf[PAGE_SIZE];
+#define CON_BUF_SIZE PAGE_SIZE
+DECLARE_MUTEX(con_buf_sem);
+
static int do_con_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
@@ -1802,6 +1816,8 @@ static int do_con_write(struct tty_struct * tty, int from_user,
unsigned long draw_from = 0, draw_to = 0;
struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
u16 himask, charmask;
+ const unsigned char *orig_buf = NULL;
+ int orig_count;
currcons = vt->vc_num;
if (!vc_cons_allocated(currcons)) {
@@ -1814,12 +1830,32 @@ static int do_con_write(struct tty_struct * tty, int from_user,
return 0;
}
+ orig_buf = buf;
+ orig_count = count;
+
if (from_user) {
- /* just to make sure that noone lurks at places he shouldn't see. */
- if (verify_area(VERIFY_READ, buf, count))
- return 0; /* ?? are error codes legal here ?? */
+ down(&con_buf_sem);
+
+again:
+ if (count > CON_BUF_SIZE)
+ count = CON_BUF_SIZE;
+ if (copy_from_user(con_buf, buf, count)) {
+ n = 0; /* ?? are error codes legal here ?? */
+ goto out;
+ }
+
+ buf = con_buf;
}
+ /* At this point 'buf' is guarenteed to be a kernel buffer
+ * and therefore no access to userspace (and therefore sleeping)
+ * will be needed. The con_buf_sem serializes all tty based
+ * console rendering and vcs write/read operations. We hold
+ * the console spinlock during the entire write.
+ */
+
+ spin_lock_irq(&console_lock);
+
himask = hi_font_mask;
charmask = himask ? 0x1ff : 0xff;
@@ -1827,15 +1863,11 @@ static int do_con_write(struct tty_struct * tty, int from_user,
if (IS_FG)
hide_cursor(currcons);
- disable_bh(CONSOLE_BH);
while (!tty->stopped && count) {
- enable_bh(CONSOLE_BH);
- if (from_user)
- __get_user(c, buf);
- else
- c = *buf;
- buf++; n++; count--;
- disable_bh(CONSOLE_BH);
+ c = *buf;
+ buf++;
+ n++;
+ count--;
if (utf) {
/* Combine UTF-8 into Unicode */
@@ -1940,23 +1972,48 @@ static int do_con_write(struct tty_struct * tty, int from_user,
do_con_trol(tty, currcons, c);
}
FLUSH
- enable_bh(CONSOLE_BH);
+ spin_unlock_irq(&console_lock);
+
+out:
+ if (from_user) {
+ /* If the user requested something larger than
+ * the CON_BUF_SIZE, and the tty is not stopped,
+ * keep going.
+ */
+ if ((orig_count > CON_BUF_SIZE) && !tty->stopped) {
+ orig_count -= CON_BUF_SIZE;
+ orig_buf += CON_BUF_SIZE;
+ count = orig_count;
+ buf = orig_buf;
+ goto again;
+ }
+
+ up(&con_buf_sem);
+ }
+
return n;
#undef FLUSH
}
/*
- * This is the console switching bottom half handler.
+ * This is the console switching tasklet.
*
- * Doing console switching in a bottom half handler allows
+ * Doing console switching in a tasklet allows
* us to do the switches asynchronously (needed when we want
- * to switch due to a keyboard interrupt), while still giving
- * us the option to easily disable it to avoid races when we
- * need to write to the console.
+ * to switch due to a keyboard interrupt). Synchronization
+ * with other console code and prevention of re-entrancy is
+ * ensured with console_lock.
*/
-static void console_bh(void)
+static void console_softint(unsigned long ignored)
{
+ /* Runs the task queue outside of the console lock. These
+ * callbacks can come back into the console code and thus
+ * will perform their own locking.
+ */
run_task_queue(&con_task_queue);
+
+ spin_lock_irq(&console_lock);
+
if (want_console >= 0) {
if (want_console != fg_console && vc_cons_allocated(want_console)) {
hide_cursor(fg_console);
@@ -1978,6 +2035,8 @@ static void console_bh(void)
sw->con_scrolldelta(vc_cons[currcons].d, scrollback_delta);
scrollback_delta = 0;
}
+
+ spin_unlock_irq(&console_lock);
}
#ifdef CONFIG_VT_CONSOLE
@@ -1985,10 +2044,7 @@ static void console_bh(void)
/*
* Console on virtual terminal
*
- * NOTE NOTE NOTE! This code can do no global locking. In particular,
- * we can't disable interrupts or bottom half handlers globally, because
- * we can be called from contexts that hold critical spinlocks, and
- * trying do get a global lock at this point will lead to deadlocks.
+ * The console_lock must be held when we get here.
*/
void vt_console_print(struct console *co, const char * b, unsigned count)
@@ -2015,7 +2071,7 @@ void vt_console_print(struct console *co, const char * b, unsigned count)
if (!vc_cons_allocated(currcons)) {
/* impossible */
- printk("vt_console_print: tty %d not allocated ??\n", currcons+1);
+ /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
goto quit;
}
@@ -2263,13 +2319,18 @@ static int con_open(struct tty_struct *tty, struct file * filp)
tty->winsize.ws_row = video_num_lines;
tty->winsize.ws_col = video_num_columns;
}
+ if (tty->count == 1)
+ vcs_make_devfs (currcons, 0);
return 0;
}
static void con_close(struct tty_struct *tty, struct file * filp)
{
- if (tty->count == 1)
- tty->driver_data = 0;
+ if (!tty)
+ return;
+ if (tty->count != 1) return;
+ vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1);
+ tty->driver_data = 0;
}
static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear)
@@ -2305,6 +2366,8 @@ static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols,
struct tty_driver console_driver;
static int console_refcount;
+DECLARE_TASKLET_DISABLED(console_tasklet, console_softint, 0);
+
void __init con_init(void)
{
const char *display_desc = NULL;
@@ -2319,7 +2382,7 @@ void __init con_init(void)
memset(&console_driver, 0, sizeof(struct tty_driver));
console_driver.magic = TTY_DRIVER_MAGIC;
- console_driver.name = "tty";
+ console_driver.name = "vc/%d";
console_driver.name_base = 1;
console_driver.major = TTY_MAJOR;
console_driver.minor_start = 1;
@@ -2327,6 +2390,11 @@ void __init con_init(void)
console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
console_driver.init_termios = tty_std_termios;
console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
+ /* Tell tty_register_driver() to skip consoles because they are
+ * registered before kmalloc() is ready. We'll patch them in later.
+ * See comments at console_init(); see also con_init_devfs().
+ */
+ console_driver.flags |= TTY_DRIVER_NO_DEVFS;
console_driver.refcount = &console_refcount;
console_driver.table = console_table;
console_driver.termios = console_termios;
@@ -2393,7 +2461,8 @@ void __init con_init(void)
register_console(&vt_console_driver);
#endif
- init_bh(CONSOLE_BH, console_bh);
+ tasklet_enable(&console_tasklet);
+ tasklet_schedule(&console_tasklet);
}
#ifndef VT_SINGLE_DRIVER
@@ -2486,6 +2555,19 @@ static void set_vesa_blanking(unsigned long arg)
vesa_blank_mode = (mode < 4) ? mode : 0;
}
+/* We can't register the console with devfs during con_init(), because it
+ * is called before kmalloc() works. This function is called later to
+ * do the registration.
+ */
+void __init con_init_devfs (void)
+{
+ int i;
+
+ for (i = 0; i < console_driver.num; i++)
+ tty_register_devfs (&console_driver, 0,
+ console_driver.minor_start + i);
+}
+
static void vesa_powerdown(void)
{
struct vc_data *c = vc_cons[fg_console].d;
@@ -2744,9 +2826,11 @@ int con_font_op(int currcons, struct console_font_op *op)
}
op->data = temp;
}
- disable_bh(CONSOLE_BH);
+
+ spin_lock_irq(&console_lock);
rc = sw->con_font_op(vc_cons[currcons].d, op);
- enable_bh(CONSOLE_BH);
+ spin_unlock_irq(&console_lock);
+
op->data = old_op.data;
if (!rc && !set) {
int c = (op->width+7)/8 * 32 * op->charcount;
@@ -2847,6 +2931,7 @@ EXPORT_SYMBOL(video_font_height);
EXPORT_SYMBOL(video_scan_lines);
EXPORT_SYMBOL(vc_resize);
EXPORT_SYMBOL(fg_console);
+EXPORT_SYMBOL(console_blank_hook);
#ifndef VT_SINGLE_DRIVER
EXPORT_SYMBOL(take_over_console);
diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c
index ff41c0fdd..8ed17fd71 100644
--- a/drivers/char/dsp56k.c
+++ b/drivers/char/dsp56k.c
@@ -34,6 +34,7 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
#include <asm/segment.h>
#include <asm/atarihw.h>
@@ -510,6 +511,9 @@ static struct file_operations dsp56k_fops = {
/****** Init and module functions ******/
+
+static devfs_handle_t devfs_handle = NULL;
+
int __init dsp56k_init(void)
{
if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
@@ -517,10 +521,14 @@ int __init dsp56k_init(void)
return -ENODEV;
}
- if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
+ if(devfs_register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
printk("DSP56k driver: Unable to register driver\n");
return -ENODEV;
}
+ devfs_handle = devfs_register (NULL, "dsp56k", 0, DEVFS_FL_NONE,
+ DSP56K_MAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &dsp56k_fops, NULL);
dsp56k.in_use = 0;
@@ -537,6 +545,7 @@ int init_module(void)
void cleanup_module(void)
{
- unregister_chrdev(DSP56K_MAJOR, "dsp56k");
+ devfs_unregister_chrdev(DSP56K_MAJOR, "dsp56k");
+ devfs_unregister (devfs_handle);
}
#endif /* MODULE */
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c
index ac913935b..58ce08712 100644
--- a/drivers/char/dtlk.c
+++ b/drivers/char/dtlk.c
@@ -64,6 +64,7 @@
#include <linux/init.h> /* for __init, module_{init,exit} */
#include <linux/poll.h> /* for POLLIN, etc. */
#include <linux/dtlk.h> /* local header file for DoubleTalk values */
+#include <linux/devfs_fs_kernel.h>
#ifdef TRACING
#define TRACE_TEXT(str) printk(str);
@@ -352,19 +353,25 @@ static int dtlk_release(struct inode *inode, struct file *file)
return 0;
}
+static devfs_handle_t devfs_handle;
+
static int __init dtlk_init(void)
{
dtlk_port_lpc = 0;
dtlk_port_tts = 0;
dtlk_busy = 0;
dtlk_timer_active = 0;
- dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
+ dtlk_major = devfs_register_chrdev(0, "dtlk", &dtlk_fops);
if (dtlk_major == 0) {
printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
return 0;
}
if (dtlk_dev_probe() == 0)
printk(", MAJOR %d\n", dtlk_major);
+ devfs_handle = devfs_register (NULL, "dtlk", 0, DEVFS_FL_NONE,
+ dtlk_major, DTLK_MINOR,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &dtlk_fops, NULL);
init_timer(&dtlk_timer);
dtlk_timer.function = dtlk_timer_tick;
@@ -383,7 +390,8 @@ static void __exit dtlk_cleanup (void)
signals... */
dtlk_write_tts(DTLK_CLEAR);
- unregister_chrdev(dtlk_major, "dtlk");
+ devfs_unregister_chrdev(dtlk_major, "dtlk");
+ devfs_unregister(devfs_handle);
release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
}
diff --git a/drivers/char/dz.c b/drivers/char/dz.c
index 13b19bb6c..35f6b0379 100644
--- a/drivers/char/dz.c
+++ b/drivers/char/dz.c
@@ -1433,6 +1433,7 @@ static void dz_console_put_char (unsigned char ch)
* dz_console_print ()
*
* dz_console_print is registered for printk.
+ * The console_lock must be held when we get here.
* -------------------------------------------------------------------
*/
static void dz_console_print (struct console *cons,
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
index ff800e6c4..e3cea9e5d 100644
--- a/drivers/char/esp.c
+++ b/drivers/char/esp.c
@@ -203,20 +203,6 @@ static inline void serial_out(struct esp_struct *info, int offset,
outb(value, info->port+offset);
}
-static inline int __get_order(unsigned long size)
-{
- int order;
-
- size = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
- order = -1;
- do {
- size >>= 1;
- order++;
- } while (size);
-
- return order;
-}
-
/*
* ------------------------------------------------------------
* rs_stop() and rs_start()
@@ -965,14 +951,14 @@ static int startup(struct esp_struct * info)
if (!(info->stat_flags & ESP_STAT_USE_PIO) && !dma_buffer) {
dma_buffer = (char *)__get_dma_pages(
- GFP_KERNEL, __get_order(DMA_BUFFER_SZ));
+ GFP_KERNEL, get_order(DMA_BUFFER_SZ));
/* use PIO mode if DMA buf/chan cannot be allocated */
if (!dma_buffer)
info->stat_flags |= ESP_STAT_USE_PIO;
else if (request_dma(dma, "esp serial")) {
free_pages((unsigned int)dma_buffer,
- __get_order(DMA_BUFFER_SZ));
+ get_order(DMA_BUFFER_SZ));
dma_buffer = 0;
info->stat_flags |= ESP_STAT_USE_PIO;
}
@@ -1076,7 +1062,7 @@ static void shutdown(struct esp_struct * info)
if (!current_port) {
free_dma(dma);
free_pages((unsigned int)dma_buffer,
- __get_order(DMA_BUFFER_SZ));
+ get_order(DMA_BUFFER_SZ));
dma_buffer = 0;
}
}
@@ -2785,7 +2771,7 @@ void cleanup_module(void)
if (dma_buffer)
free_pages((unsigned int)dma_buffer,
- __get_order(DMA_BUFFER_SZ));
+ get_order(DMA_BUFFER_SZ));
if (tmp_buf)
free_page((unsigned long)tmp_buf);
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.c b/drivers/char/ftape/lowlevel/ftape-buffer.c
index 8de980562..d7d31dbbb 100644
--- a/drivers/char/ftape/lowlevel/ftape-buffer.c
+++ b/drivers/char/ftape/lowlevel/ftape-buffer.c
@@ -39,20 +39,6 @@
/* DMA'able memory allocation stuff.
*/
-/* Pure 2^n version of get_order */
-static inline int __get_order(size_t size)
-{
- unsigned long order;
-
- size = (size-1) >> (PAGE_SHIFT-1);
- order = -1;
- do {
- size >>= 1;
- order++;
- } while (size);
- return order;
-}
-
static inline void *dmaalloc(size_t size)
{
unsigned long addr;
@@ -60,7 +46,7 @@ static inline void *dmaalloc(size_t size)
if (size == 0) {
return NULL;
}
- addr = __get_dma_pages(GFP_KERNEL, __get_order(size));
+ addr = __get_dma_pages(GFP_KERNEL, get_order(size));
if (addr) {
int i;
@@ -80,7 +66,7 @@ static inline void dmafree(void *addr, size_t size)
i < MAP_NR((unsigned long)addr+size); i++) {
mem_map_unreserve (i);
}
- free_pages((unsigned long) addr, __get_order(size));
+ free_pages((unsigned long) addr, get_order(size));
}
}
diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c
index 4795eafdc..883f4a106 100644
--- a/drivers/char/ftape/zftape/zftape-init.c
+++ b/drivers/char/ftape/zftape/zftape-init.c
@@ -35,6 +35,7 @@
#endif
#include <linux/fcntl.h>
#include <linux/wrapper.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/zftape.h>
#if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16)
@@ -403,6 +404,7 @@ extern int zft_compressor_init(void);
*/
int __init zft_init(void)
{
+ int i;
TRACE_FUN(ft_t_flow);
#ifdef MODULE
@@ -431,7 +433,43 @@ KERN_INFO
TRACE(ft_t_info, "zft_init @ 0x%p", zft_init);
TRACE(ft_t_info,
"installing zftape VFS interface for ftape driver ...");
- TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),);
+ TRACE_CATCH(devfs_register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),);
+
+ for (i = 0; i < 4; i++) {
+ char devname[9];
+
+ sprintf (devname, "qft%i", i);
+ devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+ QIC117_TAPE_MAJOR, i,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &zft_cdev, NULL);
+ sprintf (devname, "nqft%i", i);
+ devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+ QIC117_TAPE_MAJOR, i + 4,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &zft_cdev, NULL);
+ sprintf (devname, "zqft%i", i);
+ devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+ QIC117_TAPE_MAJOR, i + 16,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &zft_cdev, NULL);
+ sprintf (devname, "nzqft%i", i);
+ devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+ QIC117_TAPE_MAJOR, i + 20,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &zft_cdev, NULL);
+ sprintf (devname, "rawqft%i", i);
+ devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+ QIC117_TAPE_MAJOR, i + 32,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &zft_cdev, NULL);
+ sprintf (devname, "nrawqft%i", i);
+ devfs_register (NULL, devname, 0, DEVFS_FL_NONE,
+ QIC117_TAPE_MAJOR, i + 36,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &zft_cdev, NULL);
+ }
+
#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
register_symtab(&zft_symbol_table); /* add global zftape symbols */
#endif
@@ -471,13 +509,30 @@ int init_module(void)
*/
void cleanup_module(void)
{
+ int i;
+ char devname[9];
+
TRACE_FUN(ft_t_flow);
- if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) {
+ if (devfs_unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) {
TRACE(ft_t_warn, "failed");
} else {
TRACE(ft_t_info, "successful");
}
+ for (i = 0; i < 4; i++) {
+ sprintf(devname, "qft%i", i);
+ devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i, DEVFS_SPECIAL_CHR, 0));
+ sprintf(devname, "nqft%i", i);
+ devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 4, DEVFS_SPECIAL_CHR, 0));
+ sprintf(devname, "zqft%i", i);
+ devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 16, DEVFS_SPECIAL_CHR, 0));
+ sprintf(devname, "nzqft%i", i);
+ devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 20, DEVFS_SPECIAL_CHR, 0));
+ sprintf(devname, "rawqft%i", i);
+ devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 32, DEVFS_SPECIAL_CHR, 0));
+ sprintf(devname, "nrawqft%i", i);
+ devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 36, DEVFS_SPECIAL_CHR, 0));
+ }
zft_uninit_mem(); /* release remaining memory, if any */
printk(KERN_INFO "zftape successfully unloaded.\n");
TRACE_EXIT;
diff --git a/drivers/char/h8.c b/drivers/char/h8.c
index 99b157833..0583923d9 100644
--- a/drivers/char/h8.c
+++ b/drivers/char/h8.c
@@ -260,7 +260,7 @@ static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
return;
} else if (data_reg == H8_SYNC_BYTE) {
h8_state = H8_IDLE;
- if (!QUEUE_EMPTY(&h8_actq, link))
+ if (!QUEUE_IS_EMPTY(&h8_actq, link))
h8_send_next_cmd_byte();
} else {
Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg);
@@ -279,7 +279,7 @@ static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
QUEUE_REMOVE(&h8_actq, qp, link);
h8_cmd_done (qp);
/* More commands to send over? */
- if (!QUEUE_EMPTY(&h8_cmdq, link))
+ if (!QUEUE_IS_EMPTY(&h8_cmdq, link))
h8_start_new_cmd();
}
return;
@@ -458,7 +458,7 @@ h8_q_cmd(u_char *cmd, int cmd_size, int resp_size)
/* get cmd buf */
save_flags(flags); cli();
- while (QUEUE_EMPTY(&h8_freeq, link)) {
+ while (QUEUE_IS_EMPTY(&h8_freeq, link)) {
Dprintk("H8: need to allocate more cmd buffers\n");
restore_flags(flags);
h8_alloc_queues();
@@ -500,13 +500,13 @@ h8_start_new_cmd(void)
return;
}
- if (!QUEUE_EMPTY(&h8_actq, link)) {
+ if (!QUEUE_IS_EMPTY(&h8_actq, link)) {
Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n");
restore_flags(flags);
return;
}
- if (QUEUE_EMPTY(&h8_cmdq, link)) {
+ if (QUEUE_IS_EMPTY(&h8_cmdq, link)) {
Dprintk("h8_start_new_cmd: no command to dequeue\n");
restore_flags(flags);
return;
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index a9634ad91..77dc4625b 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -2051,7 +2051,8 @@ void cleanup_module(void)
re_schedule = 0;
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ);
- disable_bh(ISICOM_BH);
+
+ remove_bh(ISICOM_BH);
#ifdef ISICOM_DEBUG
printk("ISICOM: isicom_tx tx_count = %ld.\n", tx_count);
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 494fbc260..0b1ab10e1 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -39,6 +39,7 @@
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -829,12 +830,14 @@ int init_module()
/*****************************************************************************/
+static devfs_handle_t devfs_handle = NULL;
+
void cleanup_module()
{
stlibrd_t *brdp;
stliport_t *portp;
unsigned long flags;
- int i, j;
+ int i, j, k;
#if DEBUG
printk("cleanup_module()\n");
@@ -863,10 +866,10 @@ void cleanup_module()
restore_flags(flags);
return;
}
- if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
+ devfs_unregister (devfs_handle);
+ if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
printk("STALLION: failed to un-register serial memory device, "
"errno=%d\n", -i);
-
if (stli_tmpwritebuf != (char *) NULL)
kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE);
if (stli_txcookbuf != (char *) NULL)
@@ -5321,9 +5324,15 @@ int __init stli_init(void)
* Set up a character driver for the shared memory region. We need this
* to down load the slave code image. Also it is a useful debugging tool.
*/
- if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem))
+ if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem))
printk("STALLION: failed to register serial memory device\n");
+ devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL);
+ devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT,
+ STL_SIOMEMMAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &stli_fsiomem, NULL);
+
/*
* Set up the tty driver structure and register us as a driver.
* Also setup the callout tty device.
diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c
index 48af4c713..cfd0f2e82 100644
--- a/drivers/char/joystick/joystick.c
+++ b/drivers/char/joystick/joystick.c
@@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/joystick.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/malloc.h>
@@ -694,6 +695,10 @@ struct js_port *js_unregister_port(struct js_port *port)
return prev;
}
+extern struct file_operations js_fops;
+
+static devfs_handle_t devfs_handle = NULL;
+
int js_register_device(struct js_port *port, int number, int axes, int buttons, char *name,
js_ops_func open, js_ops_func close)
{
@@ -702,6 +707,7 @@ int js_register_device(struct js_port *port, int number, int axes, int buttons,
void *all;
int i = 0;
unsigned long flags;
+ char devfs_name[8];
if (!(all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) +
2 * (((buttons - 1) >> 5) + 1) * sizeof(int) +
@@ -745,6 +751,13 @@ int js_register_device(struct js_port *port, int number, int axes, int buttons,
spin_unlock_irqrestore(&js_lock, flags);
+ sprintf(devfs_name, "js%d", i);
+ curd->devfs_handle = devfs_register(devfs_handle, devfs_name, 0,
+ DEVFS_FL_DEFAULT,
+ JOYSTICK_MAJOR, i,
+ S_IFCHR | S_IRUGO | S_IWUSR, 0, 0,
+ &js_fops, NULL);
+
return i;
}
@@ -760,6 +773,7 @@ void js_unregister_device(struct js_dev *dev)
spin_unlock_irqrestore(&js_lock, flags);
+ devfs_unregister(dev->devfs_handle);
kfree(dev);
}
@@ -788,10 +802,11 @@ int __init js_init(void)
#endif
{
- if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
+ if (devfs_register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
return -EBUSY;
}
+ devfs_handle = devfs_mk_dir(NULL, "joysticks", 9, NULL);
printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik <vojtech@suse.cz>\n",
JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff);
@@ -871,7 +886,8 @@ int __init js_init(void)
void cleanup_module(void)
{
del_timer(&js_timer);
- if (unregister_chrdev(JOYSTICK_MAJOR, "js"))
+ devfs_unregister(devfs_handle);
+ if (devfs_unregister_chrdev(JOYSTICK_MAJOR, "js"))
printk(KERN_ERR "js: can't unregister device\n");
}
#endif
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index c02a6db08..7e5a654c2 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -208,7 +208,7 @@ void handle_scancode(unsigned char scancode, int down)
pm_access(pm_kbd);
do_poke_blanked_console = 1;
- mark_bh(CONSOLE_BH);
+ tasklet_schedule(&console_tasklet);
add_keyboard_randomness(scancode | up_flag);
tty = ttytab? ttytab[fg_console]: NULL;
@@ -925,6 +925,7 @@ static void kbd_bh(unsigned long dummy)
}
}
+EXPORT_SYMBOL(keyboard_tasklet);
DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
int __init kbd_init(void)
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 4f6004f1c..bb00a32fa 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -16,6 +16,7 @@
* Parport sharing hacking by Andrea Arcangeli
* Fixed kernel_(to/from)_user memory copy to check for errors
* by Riccardo Facchetti <fizban@tin.it>
+ * 22-JAN-1998 Added support for devfs Richard Gooch <rgooch@atnf.csiro.au>
* Redesigned interrupt handling for handle printers with buggy handshake
* by Andrea Arcangeli, 11 May 1998
* Full efficient handling of printer with buggy irq handshake (now I have
@@ -118,6 +119,7 @@
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
@@ -138,6 +140,8 @@
/* ROUND_UP macro from fs/select.c */
#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
+static devfs_handle_t devfs_handle = NULL;
+
struct lp_struct lp_table[LP_NO];
static unsigned int lp_count = 0;
@@ -151,72 +155,6 @@ static unsigned int lp_count = 0;
#undef LP_DEBUG
-/* If you want to see if you can get lp_poll working, define this. */
-#undef SUPPORT_POLL
-
-/* --- parport support ----------------------------------------- */
-
-static int lp_preempt(void *handle)
-{
- struct lp_struct *lps = (struct lp_struct *)handle;
-
- if (!(lps->flags & LP_PORT_BUSY)) {
- /* Let the port go. */
- clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
- return 0;
- }
-
- if (!(lps->flags & LP_PORT_BUSY)) {
- /* Let the port go. */
- clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
- return 0;
- }
-
- /* Don't actually release the port now */
- return 1;
-}
-
-static void lp_check_data (struct lp_struct *lp)
-{
-#if !defined(CONFIG_PARPORT_1284) || !defined (SUPPORT_POLL)
- return;
-#else
- struct pardevice *dev = lp->dev;
- if (!(lp->flags & LP_NO_REVERSE)) {
- int err = parport_negotiate (dev->port, IEEE1284_MODE_NIBBLE);
- if (err)
- lp->flags |= LP_NO_REVERSE;
- else {
- unsigned char s = parport_read_status (dev->port);
- if (s & PARPORT_STATUS_ERROR)
- lp->flags &= ~LP_DATA_AVAIL;
- else {
- lp->flags |= LP_DATA_AVAIL;
- if (waitqueue_active (&lp->dataq))
- wake_up_interruptible (&lp->dataq);
- }
- }
- }
-#endif /* IEEE 1284 support */
-}
-
-static void lp_parport_release (int minor)
-{
- lp_check_data (&lp_table[minor]);
- if (test_and_clear_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
- parport_release (lp_table[minor].dev);
-
- lp_table[minor].flags &= ~LP_PORT_BUSY;
-}
-
-static void lp_parport_claim (int minor)
-{
- if (!test_and_set_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
- parport_claim_or_block (lp_table[minor].dev);
-
- lp_table[minor].flags |= LP_PORT_BUSY;
-}
-
/* --- low-level port access ----------------------------------- */
#define r_dtr(x) (parport_read_data(lp_table[(x)].dev->port))
@@ -227,42 +165,15 @@ static void lp_parport_claim (int minor)
static int lp_reset(int minor)
{
int retval;
- lp_parport_claim (minor);
+ parport_claim_or_block (lp_table[minor].dev);
w_ctr(minor, LP_PSELECP);
udelay (LP_DELAY);
w_ctr(minor, LP_PSELECP | LP_PINITP);
retval = r_str(minor);
- lp_parport_release (minor);
+ parport_release (lp_table[minor].dev);
return retval;
}
-static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct lp_struct *lp_dev = (struct lp_struct *) dev_id;
- if (!(lp_dev->flags & LP_PORT_BUSY))
- /* We must have the port since we got an interrupt. */
- lp_check_data (lp_dev);
- if (waitqueue_active (&lp_dev->waitq))
- wake_up_interruptible (&lp_dev->waitq);
-}
-
-static void lp_wakeup (void *handle)
-{
- struct lp_struct *lp_dev = handle;
-
- if (lp_dev->flags & LP_PORT_BUSY)
- return;
-
- /* Grab the port if it can help (i.e. reverse mode is possible). */
- if (!(lp_dev->flags & LP_NO_REVERSE)) {
- parport_claim (lp_dev->dev);
- set_bit (LP_HAVE_PORT_BIT, &lp_dev->flags);
- lp_check_data (lp_dev);
- if (waitqueue_active (&lp_dev->waitq))
- wake_up_interruptible (&lp_dev->waitq);
- }
-}
-
static void lp_error (int minor)
{
int polling;
@@ -271,10 +182,10 @@ static void lp_error (int minor)
return;
polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
- if (polling) lp_parport_release (minor);
+ if (polling) parport_release (lp_table[minor].dev);
interruptible_sleep_on_timeout (&lp_table[minor].waitq,
LP_TIMEOUT_POLLED);
- if (polling) lp_parport_claim (minor);
+ if (polling) parport_claim_or_block (lp_table[minor].dev);
else parport_yield_blocking (lp_table[minor].dev);
}
@@ -326,7 +237,6 @@ static ssize_t lp_write(struct file * file, const char * buf,
ssize_t retv = 0;
ssize_t written;
size_t copy_size = count;
- long old_to;
#ifdef LP_STATS
if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
@@ -347,13 +257,13 @@ static ssize_t lp_write(struct file * file, const char * buf,
/* Claim Parport or sleep until it becomes available
*/
- lp_parport_claim (minor);
+ parport_claim_or_block (lp_table[minor].dev);
/* Go to compatibility mode. */
parport_negotiate (port, IEEE1284_MODE_COMPAT);
- old_to = parport_set_timeout (lp_table[minor].dev,
- lp_table[minor].timeout);
+ parport_set_timeout (lp_table[minor].dev,
+ lp_table[minor].timeout);
do {
/* Write the data. */
@@ -399,21 +309,13 @@ static ssize_t lp_write(struct file * file, const char * buf,
}
} while (count > 0);
- /* Not really necessary, but polite. */
- parport_set_timeout (lp_table[minor].dev, old_to);
-
- lp_parport_release (minor);
+ parport_release (lp_table[minor].dev);
up (&lp_table[minor].port_mutex);
return retv;
}
-static long long lp_lseek(struct file * file, long long offset, int origin)
-{
- return -ESPIPE;
-}
-
#ifdef CONFIG_PARPORT_1284
/* Status readback conforming to ieee1284 */
@@ -431,7 +333,7 @@ static ssize_t lp_read(struct file * file, char * buf,
if (down_interruptible (&lp_table[minor].port_mutex))
return -EINTR;
- lp_parport_claim (minor);
+ parport_claim_or_block (lp_table[minor].dev);
for (;;) {
retval = parport_read (port, kbuf, count);
@@ -452,7 +354,7 @@ static ssize_t lp_read(struct file * file, char * buf,
}
}
- lp_parport_release (minor);
+ parport_release (lp_table[minor].dev);
if (retval > 0 && copy_to_user (buf, kbuf, retval))
retval = -EFAULT;
@@ -484,9 +386,9 @@ static int lp_open(struct inode * inode, struct file * file)
should most likely only ever be used by the tunelp application. */
if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
int status;
- lp_parport_claim (minor);
+ parport_claim_or_block (lp_table[minor].dev);
status = r_str(minor);
- lp_parport_release (minor);
+ parport_release (lp_table[minor].dev);
if (status & LP_POUTPA) {
printk(KERN_INFO "lp%d out of paper\n", minor);
MOD_DEC_USE_COUNT;
@@ -578,9 +480,9 @@ static int lp_ioctl(struct inode *inode, struct file *file,
return -EFAULT;
break;
case LPGETSTATUS:
- lp_parport_claim(minor);
+ parport_claim_or_block (lp_table[minor].dev);
status = r_str(minor);
- lp_parport_release(minor);
+ parport_release (lp_table[minor].dev);
if (copy_to_user((int *) arg, &status, sizeof(int)))
return -EFAULT;
@@ -629,21 +531,6 @@ static int lp_ioctl(struct inode *inode, struct file *file,
return retval;
}
-#ifdef CONFIG_PARPORT_1284
-static unsigned int lp_poll (struct file *filp, struct poll_table_struct *wait)
-{
- unsigned int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
- unsigned int mask = POLLOUT | POLLWRNORM; /* always writable */
-
- poll_wait (filp, &lp_table[minor].dataq, wait);
-
- if (lp_table[minor].flags & LP_DATA_AVAIL)
- mask |= POLLIN | POLLRDNORM;
-
- return mask;
-}
-#endif /* IEEE 1284 support */
-
static struct file_operations lp_fops = {
write: lp_write,
ioctl: lp_ioctl,
@@ -651,7 +538,6 @@ static struct file_operations lp_fops = {
release: lp_release,
#ifdef CONFIG_PARPORT_1284
read: lp_read,
- poll: lp_poll,
#endif
};
@@ -666,22 +552,20 @@ static struct file_operations lp_fops = {
* non-zero to get the latter behaviour. */
#define CONSOLE_LP_STRICT 1
+/* The console_lock must be held when we get here. */
+
static void lp_console_write (struct console *co, const char *s,
unsigned count)
{
struct pardevice *dev = lp_table[CONSOLE_LP].dev;
struct parport *port = dev->port;
ssize_t written;
- signed long old_to;
- if (!(lp_table[CONSOLE_LP].flags & (1<<LP_HAVE_PORT_BIT))) {
- if (parport_claim (dev))
- /* Nothing we can do. */
- return;
- set_bit (LP_HAVE_PORT_BIT, &lp_table[CONSOLE_LP].flags);
- }
+ if (parport_claim (dev))
+ /* Nothing we can do. */
+ return;
- old_to = parport_set_timeout (dev, 0);
+ parport_set_timeout (dev, 0);
/* Go to compatibility mode. */
parport_negotiate (port, IEEE1284_MODE_COMPAT);
@@ -719,7 +603,7 @@ static void lp_console_write (struct console *co, const char *s,
}
} while (count > 0 && (CONSOLE_LP_STRICT || written > 0));
- parport_set_timeout (dev, old_to);
+ parport_release (dev);
}
static kdev_t lp_console_device (struct console *c)
@@ -790,10 +674,10 @@ void __init lp_setup(char *str, int *ints)
static int lp_register(int nr, struct parport *port)
{
+ char name[8];
+
lp_table[nr].dev = parport_register_device(port, "lp",
- lp_preempt, lp_wakeup,
- lp_interrupt,
- 0,
+ NULL, NULL, NULL, 0,
(void *) &lp_table[nr]);
if (lp_table[nr].dev == NULL)
return 1;
@@ -802,6 +686,12 @@ static int lp_register(int nr, struct parport *port)
if (reset)
lp_reset(nr);
+ sprintf (name, "%d", nr);
+ devfs_register (devfs_handle, name, 0,
+ DEVFS_FL_DEFAULT, LP_MAJOR, nr,
+ S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+ &lp_fops, NULL);
+
printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
(port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
@@ -888,7 +778,7 @@ int __init lp_init (void)
lp_table[i].timeout = 10 * HZ;
}
- if (register_chrdev (LP_MAJOR, "lp", &lp_fops)) {
+ if (devfs_register_chrdev (LP_MAJOR, "lp", &lp_fops)) {
printk ("lp: unable to get major %d\n", LP_MAJOR);
return -EIO;
}
@@ -898,11 +788,13 @@ int __init lp_init (void)
return -EIO;
}
+ devfs_handle = devfs_mk_dir (NULL, "printers", 0, NULL);
+
if (!lp_count) {
printk (KERN_INFO "lp: driver loaded but no devices found\n");
-#ifndef CONFIG_PARPORT_12843
+#ifndef CONFIG_PARPORT_1284
if (parport_nr[0] == LP_PARPORT_AUTO)
- printk (KERN_INFO "lp: (is IEEE 1284.3 support enabled?)\n");
+ printk (KERN_INFO "lp: (is IEEE 1284 support enabled?)\n");
#endif
}
@@ -948,12 +840,11 @@ void cleanup_module(void)
unregister_console (&lpcons);
#endif
- unregister_chrdev(LP_MAJOR, "lp");
+ devfs_unregister (devfs_handle);
+ devfs_unregister_chrdev(LP_MAJOR, "lp");
for (offset = 0; offset < LP_NO; offset++) {
if (lp_table[offset].dev == NULL)
continue;
- if (lp_table[offset].flags & (1<<LP_HAVE_PORT_BIT))
- parport_release (lp_table[offset].dev);
parport_unregister_device(lp_table[offset].dev);
}
}
diff --git a/drivers/char/mac_SCC.c b/drivers/char/mac_SCC.c
deleted file mode 100644
index c20e7a9f0..000000000
--- a/drivers/char/mac_SCC.c
+++ /dev/null
@@ -1,1529 +0,0 @@
-/*
- * mac_SCC.c: m68k version of
- *
- * macserial.c: Serial port driver for Power Macintoshes.
- * Extended for the 68K mac by Alan Cox.
- * Rewritten to m68k serial design by Michael Schmitz
- *
- * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
- *
- * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-/*
- * Design note for the m68k rewrite:
- * The structure of the m68k serial code requires separation of the low-level
- * functions that talk directly to the hardware from the Linux serial driver
- * code interfacing to the tty layer. The reason for this separation is simply
- * the fact that the m68k serial hardware is, unlike the i386, based on a
- * variety of chips, and the rs_* serial routines need to be shared.
- *
- * I've tried to make consistent use of the async_struct info populated in the
- * midlevel code, and introduced an async_private struct to hold the Macintosh
- * SCC internals (this was added to the async_struct for the PowerMac driver).
- * Exception: the console and kgdb hooks still use the zs_soft[] data, and this
- * is still filled in by the probe_sccs() routine, which provides some data
- * for mac_SCC_init as well. Interrupts are registered in mac_SCC_init, so
- * the console/kgdb stuff probably won't work before proper serial init, and
- * I have to rely on keeping info and zs_soft consistent at least for the
- * console/kgdb port.
- *
- * Update (16-11-97): The SCC interrupt handling was suffering from the problem
- * that the autovector SCC interrupt was registered only once, hence only one
- * async_struct was passed to the interrupt function and only interrupts from
- * the corresponding channel could be handled (yes, major design flaw).
- * The autovector interrupt is now registered by the main interrupt initfunc,
- * and uses a handler that will call the registered SCC specific interrupts in
- * turn. The SCC init has to register these as machspec interrupts now, as is
- * done for the VIA interrupts elsewhere.
- */
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/config.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-
-#include <asm/uaccess.h>
-#include <asm/setup.h>
-#include <asm/bootinfo.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/macints.h>
-#ifndef CONFIG_MAC
-#include <asm/prom.h>
-#endif
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/bitops.h>
-#include <asm/hwtest.h>
-
-#include "mac_SCC.h"
-
-/*
- * It would be nice to dynamically allocate everything that
- * depends on NUM_SERIAL, so we could support any number of
- * Z8530s, but for now...
- */
-#define NUM_SERIAL 2 /* Max number of ZS chips supported */
-#define NUM_CHANNELS (NUM_SERIAL * 2) /* 2 channels per chip */
-
-#ifdef CONFIG_MAC
-/*
- * All the Macintosh 68K boxes that have an MMU also have hardware
- * recovery delays.
- */
-#define RECOVERY_DELAY
-#else
-/* On PowerMacs, the hardware takes care of the SCC recovery time,
- but we need the eieio to make sure that the accesses occur
- in the order we want. */
-#define RECOVERY_DELAY eieio()
-#endif
-
-struct mac_zschannel *zs_kgdbchan;
-struct mac_zschannel zs_channels[NUM_CHANNELS];
-
-struct m68k_async_struct zs_soft[NUM_CHANNELS];
-struct m68k_async_private zs_soft_private[NUM_CHANNELS];
-int zs_channels_found;
-struct m68k_async_struct *zs_chain; /* list of all channels */
-
-struct tty_struct zs_ttys[NUM_CHANNELS];
-/** struct tty_struct *zs_constty; **/
-
-/* Console hooks... */
-static int zs_cons_chanout = 0;
-static int zs_cons_chanin = 0;
-struct m68k_async_struct *zs_consinfo = 0;
-struct mac_zschannel *zs_conschan;
-
-static unsigned char kgdb_regs[16] = {
- 0, 0, 0, /* write 0, 1, 2 */
- (Rx8 | RxENABLE), /* write 3 */
- (X16CLK | SB1 | PAR_EVEN), /* write 4 */
- (Tx8 | TxENAB), /* write 5 */
- 0, 0, 0, /* write 6, 7, 8 */
- (NV), /* write 9 */
- (NRZ), /* write 10 */
- (TCBR | RCBR), /* write 11 */
- 1, 0, /* 38400 baud divisor, write 12 + 13 */
- (BRENABL), /* write 14 */
- (DCDIE) /* write 15 */
-};
-
-#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */
-
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
- */
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#define RS_STROBE_TIME 10
-#define RS_ISR_PASS_LIMIT 256
-
-#define _INLINE_ inline
-
-static void probe_sccs(void);
-
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-
-/***************************** Prototypes *****************************/
-
-static void SCC_init_port( struct m68k_async_struct *info, int type, int channel );
-#if 0
-#ifdef MODULE
-static void SCC_deinit_port( struct m68k_async_struct *info, int channel );
-#endif
-#endif
-
-/* FIXME !!! Currently, only autovector interrupt used! */
-#if 0
-static void SCC_rx_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_tx_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_stat_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_ri_int (int irq, void *data, struct pt_regs *fp);
-#endif
-
-static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct
- *tty, struct file *file );
-static void SCC_init( struct m68k_async_struct *info );
-static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr );
-static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
-static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base,
- int divisor );
-static void SCC_change_speed( struct m68k_async_struct *info );
-#if 0
-static int SCC_clocksrc( unsigned baud_base, unsigned channel );
-#endif
-static void SCC_throttle( struct m68k_async_struct *info, int status );
-static void SCC_set_break( struct m68k_async_struct *info, int break_flag );
-static void SCC_get_serial_info( struct m68k_async_struct *info, struct
- serial_struct *retinfo );
-static unsigned int SCC_get_modem_info( struct m68k_async_struct *info );
-static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int
- new_rts );
-static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct
- m68k_async_struct *info, unsigned int cmd, unsigned long arg );
-static void SCC_stop_receive (struct m68k_async_struct *info);
-static int SCC_trans_empty (struct m68k_async_struct *info);
-
-/************************* End of Prototypes **************************/
-
-
-static SERIALSWITCH SCC_switch = {
- SCC_init, SCC_deinit, SCC_enab_tx_int,
- SCC_check_custom_divisor, SCC_change_speed,
- SCC_throttle, SCC_set_break,
- SCC_get_serial_info, SCC_get_modem_info,
- SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty,
- SCC_check_open
-};
-
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 0 };
-
-/*
- * Reading and writing Z8530 registers.
- */
-static inline unsigned char read_zsreg(struct mac_zschannel *channel,
- unsigned char reg)
-{
- unsigned char retval;
-
- if (reg != 0) {
- *channel->control = reg;
- RECOVERY_DELAY;
- }
- retval = *channel->control;
- RECOVERY_DELAY;
- return retval;
-}
-
-static inline void write_zsreg(struct mac_zschannel *channel,
- unsigned char reg, unsigned char value)
-{
- if (reg != 0) {
- *channel->control = reg;
- RECOVERY_DELAY;
- }
- *channel->control = value;
- RECOVERY_DELAY;
- return;
-}
-
-static inline unsigned char read_zsdata(struct mac_zschannel *channel)
-{
- unsigned char retval;
-
- retval = *channel->data;
- RECOVERY_DELAY;
- return retval;
-}
-
-static inline void write_zsdata(struct mac_zschannel *channel,
- unsigned char value)
-{
- *channel->data = value;
- RECOVERY_DELAY;
- return;
-}
-
-static inline void load_zsregs(struct mac_zschannel *channel,
- unsigned char *regs)
-{
- ZS_CLEARERR(channel);
- ZS_CLEARFIFO(channel);
- /* Load 'em up */
- write_zsreg(channel, R4, regs[R4]);
- write_zsreg(channel, R10, regs[R10]);
- write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
- write_zsreg(channel, R5, regs[R5] & ~TxENAB);
- write_zsreg(channel, R1, regs[R1]);
- write_zsreg(channel, R9, regs[R9]);
- write_zsreg(channel, R11, regs[R11]);
- write_zsreg(channel, R12, regs[R12]);
- write_zsreg(channel, R13, regs[R13]);
- write_zsreg(channel, R14, regs[R14]);
- write_zsreg(channel, R15, regs[R15]);
- write_zsreg(channel, R3, regs[R3]);
- write_zsreg(channel, R5, regs[R5]);
- return;
-}
-
-/* Sets or clears DTR/RTS on the requested line */
-static inline void zs_rtsdtr(struct m68k_async_struct *ss, int set)
-{
- if (set)
- ss->private->curregs[5] |= (RTS | DTR);
- else
- ss->private->curregs[5] &= ~(RTS | DTR);
- write_zsreg(ss->private->zs_channel, 5, ss->private->curregs[5]);
- return;
-}
-
-static inline void kgdb_chaninit(struct m68k_async_struct *ss, int intson, int bps)
-{
- int brg;
-
- if (intson) {
- kgdb_regs[R1] = INT_ALL_Rx;
- kgdb_regs[R9] |= MIE;
- } else {
- kgdb_regs[R1] = 0;
- kgdb_regs[R9] &= ~MIE;
- }
- brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
- kgdb_regs[R12] = brg;
- kgdb_regs[R13] = brg >> 8;
- load_zsregs(ss->private->zs_channel, kgdb_regs);
-}
-
-/* Utility routines for the Zilog */
-static inline int get_zsbaud(struct m68k_async_struct *ss)
-{
- struct mac_zschannel *channel = ss->private->zs_channel;
- int brg;
-
- /* The baud rate is split up between two 8-bit registers in
- * what is termed 'BRG time constant' format in my docs for
- * the chip, it is a function of the clk rate the chip is
- * receiving which happens to be constant.
- */
- brg = (read_zsreg(channel, 13) << 8);
- brg |= read_zsreg(channel, 12);
- return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->private->clk_divisor)));
-}
-
-/* On receive, this clears errors and the receiver interrupts */
-static inline void SCC_recv_clear(struct mac_zschannel *zsc)
-{
- write_zsreg(zsc, 0, ERR_RES);
- write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines. All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt(). They were separated out for readability's sake.
- *
- * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-extern void breakpoint(void); /* For the KGDB frame character */
-
-static /*_INLINE_*/ void receive_chars(struct m68k_async_struct *info,
- struct pt_regs *regs)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat, flag;
-
- while ((read_zsreg(info->private->zs_channel, 0) & Rx_CH_AV) != 0) {
-
- stat = read_zsreg(info->private->zs_channel, R1);
- ch = read_zsdata(info->private->zs_channel);
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: receive_chars stat=%X char=%X \n", stat, ch);
-#endif
-
-#if 0 /* KGDB not yet supported */
- /* Look for kgdb 'stop' character, consult the gdb documentation
- * for remote target debugging and arch/sparc/kernel/sparc-stub.c
- * to see how all this works.
- */
- if ((info->kgdb_channel) && (ch =='\003')) {
- breakpoint();
- continue;
- }
-#endif
-
- if (!tty)
- continue;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- tty_flip_buffer_push(tty);
-
- if (stat & Rx_OVR) {
- flag = TTY_OVERRUN;
- /* reset the error indication */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- } else if (stat & FRM_ERR) {
- /* this error is not sticky */
- flag = TTY_FRAME;
- } else if (stat & PAR_ERR) {
- flag = TTY_PARITY;
- /* reset the error indication */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- } else
- flag = 0;
-
- if (tty->flip.buf_num
- && tty->flip.count >= TTY_FLIPBUF_SIZE) {
-#ifdef SCC_DEBUG_OVERRUN
- printk("mac_SCC: flip buffer overrun!\n");
-#endif
- return;
- }
-
- if (!tty->flip.buf_num
- && tty->flip.count >= 2*TTY_FLIPBUF_SIZE) {
- printk("mac_SCC: double flip buffer overrun!\n");
- return;
- }
-
- tty->flip.count++;
- *tty->flip.flag_buf_ptr++ = flag;
- *tty->flip.char_buf_ptr++ = ch;
- info->icount.rx++;
- tty_flip_buffer_push(tty);
- }
-#if 0
-clear_and_exit:
- SCC_recv_clear(info->private->zs_channel);
-#endif
-}
-
-/* that's SCC_enable_tx_int, basically */
-
-static void transmit_chars(struct m68k_async_struct *info)
-{
- if ((read_zsreg(info->private->zs_channel, 0) & Tx_BUF_EMP) == 0)
- return;
- info->private->tx_active = 0;
-
- if (info->x_char) {
- /* Send next char */
- write_zsdata(info->private->zs_channel, info->x_char);
- info->x_char = 0;
- info->private->tx_active = 1;
- return;
- }
-
- if ((info->xmit_cnt <= 0) || info->tty->stopped
- || info->private->tx_stopped) {
- write_zsreg(info->private->zs_channel, 0, RES_Tx_P);
- return;
- }
-
- /* Send char */
- write_zsdata(info->private->zs_channel, info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->icount.tx++;
- info->xmit_cnt--;
- info->private->tx_active = 1;
-
- if (info->xmit_cnt < WAKEUP_CHARS)
- rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-}
-
-static /*_INLINE_*/ void status_handle(struct m68k_async_struct *info)
-{
- unsigned char status;
-
- /* Get status from Read Register 0 */
- status = read_zsreg(info->private->zs_channel, 0);
-
- /* Check for DCD transitions */
- if (((status ^ info->private->read_reg_zero) & DCD) != 0
- && info->tty && C_CLOCAL(info->tty)) {
- if (status & DCD) {
- wake_up_interruptible(&info->open_wait);
- } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) {
- if (info->tty)
- tty_hangup(info->tty);
- }
- }
-
- /* Check for CTS transitions */
- if (info->tty && C_CRTSCTS(info->tty)) {
- /*
- * For some reason, on the Power Macintosh,
- * it seems that the CTS bit is 1 when CTS is
- * *negated* and 0 when it is asserted.
- * The DCD bit doesn't seem to be inverted
- * like this.
- */
- if ((status & CTS) == 0) {
- if (info->private->tx_stopped) {
- info->private->tx_stopped = 0;
- if (!info->private->tx_active)
- transmit_chars(info);
- }
- } else {
- info->private->tx_stopped = 1;
- }
- }
-
- /* Clear status condition... */
- write_zsreg(info->private->zs_channel, 0, RES_EXT_INT);
- info->private->read_reg_zero = status;
-}
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-void mac_SCC_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
- struct m68k_async_struct *info = (struct m68k_async_struct *) dev_id;
- unsigned char zs_intreg;
- int shift;
-
- /* NOTE: The read register 3, which holds the irq status,
- * does so for both channels on each chip. Although
- * the status value itself must be read from the A
- * channel and is only valid when read from channel A.
- * Yes... broken hardware...
- */
-#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: interrupt; port: %lx channel: %lx \n",
- info->port, info->private->zs_channel);
-#endif
-
- if (info->private->zs_chan_a == info->private->zs_channel)
- shift = 3; /* Channel A */
- else
- shift = 0; /* Channel B */
-
- for (;;) {
- zs_intreg = read_zsreg(info->private->zs_chan_a, 3);
-#ifdef SCC_DEBUG
- printk("mac_SCC: status %x shift %d shifted %x \n",
- zs_intreg, shift, zs_intreg >> shift);
-#endif
- zs_intreg = zs_intreg >> shift;
- if ((zs_intreg & CHAN_IRQMASK) == 0)
- break;
-
- if (zs_intreg & CHBRxIP)
- receive_chars(info, regs);
- if (zs_intreg & CHBTxIP)
- transmit_chars(info);
- if (zs_intreg & CHBEXT)
- status_handle(info);
- }
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * ------------------------------------------------------------
- */
-
-static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag )
-{
- unsigned long flags;
-
- if (enab_flag) {
-#if 0
- save_flags(flags); cli();
- if (info->private->curregs[5] & TxENAB) {
- info->private->curregs[5] &= ~TxENAB;
- info->private->pendregs[5] &= ~TxENAB;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
- restore_flags(flags);
-#endif
- /* FIXME: should call transmit_chars here ??? */
- transmit_chars(info);
- } else {
- save_flags(flags); cli();
-#if 0
- if ( info->xmit_cnt && info->xmit_buf &&
- !(info->private->curregs[5] & TxENAB)) {
- info->private->curregs[5] |= TxENAB;
- info->private->pendregs[5] = info->private->curregs[5];
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
-#else
- if ( info->xmit_cnt && info->xmit_buf &&
- !info->private->tx_active) {
- transmit_chars(info);
- }
-#endif
- restore_flags(flags);
- }
-
-}
-
-#if 0
-/*
- * leftover from original driver ...
- */
-static int SCC_startup(struct m68k_async_struct * info)
-{
- unsigned long flags;
-
- save_flags(flags); cli();
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("starting up ttyS%d (irq %d)...", info->line, info->irq);
-#endif
-
- /*
- * Clear the receive FIFO.
- */
- ZS_CLEARFIFO(info->private->zs_channel);
- info->xmit_fifo_size = 1;
-
- /*
- * Clear the interrupt registers.
- */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- write_zsreg(info->private->zs_channel, 0, RES_H_IUS);
-
- /*
- * Turn on RTS and DTR.
- */
- zs_rtsdtr(info, 1);
-
- /*
- * Finally, enable sequencing and interrupts
- */
- info->private->curregs[1] = (info->private->curregs[1] & ~0x18)
- | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
- info->private->pendregs[1] = info->private->curregs[1];
- info->private->curregs[3] |= (RxENABLE | Rx8);
- info->private->pendregs[3] = info->private->curregs[3];
- info->private->curregs[5] |= (TxENAB | Tx8);
- info->private->pendregs[5] = info->private->curregs[5];
- info->private->curregs[9] |= (NV | MIE);
- info->private->pendregs[9] = info->private->curregs[9];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]);
-
- /*
- * Set the speed of the serial port
- */
- SCC_change_speed(info);
-
- /* Save the current value of RR0 */
- info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0);
-
- restore_flags(flags);
- return 0;
-}
-#endif
-
-/* FIXME: are these required ?? */
-static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct *tty,
- struct file *file )
-{
- /* check on the basis of info->whatever ?? */
- if (info->private->kgdb_channel || info->private->is_cons)
- return -EBUSY;
- return( 0 );
-}
-
-static void SCC_init( struct m68k_async_struct *info )
-{
- /* FIXME: init currently done in probe_sccs() */
-
- /* BUT: startup part needs to be done here! */
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: init, info %lx, info->port %lx \n", info, info->port);
-#endif
- /*
- * Clear the receive FIFO.
- */
- ZS_CLEARFIFO(info->private->zs_channel);
- info->xmit_fifo_size = 1;
-
- /*
- * Clear the interrupt registers.
- */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- write_zsreg(info->private->zs_channel, 0, RES_H_IUS);
-
- /*
- * Turn on RTS and DTR.
- */
- zs_rtsdtr(info, 1);
-
- /*
- * Finally, enable sequencing and interrupts
- */
- info->private->curregs[1] = (info->private->curregs[1] & ~0x18)
- | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
- info->private->pendregs[1] = info->private->curregs[1];
- info->private->curregs[3] |= (RxENABLE | Rx8);
- info->private->pendregs[3] = info->private->curregs[3];
- info->private->curregs[5] |= (TxENAB | Tx8);
- info->private->pendregs[5] = info->private->curregs[5];
- info->private->curregs[9] |= (NV | MIE);
- info->private->pendregs[9] = info->private->curregs[9];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]);
-
- /*
- * Set the speed of the serial port - done in startup() !!
- */
-#if 0
- SCC_change_speed(info);
-#endif
-
- /* Save the current value of RR0 */
- info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0);
-
-}
-
-static void SCC_init_port( struct m68k_async_struct *info, int type, int channel )
-{
- static int got_autovector = 0;
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: init_port, info %x \n", info);
-#endif
- info->sw = &SCC_switch;
- info->private = &zs_soft_private[channel];
- info->private->zs_channel = &zs_channels[channel];
- info->irq = IRQ4;
- info->private->clk_divisor = 16;
- info->private->zs_baud = get_zsbaud(info);
- info->port = (int) info->private->zs_channel->control;
-
- /*
- * MSch: Extended interrupt scheme:
- * The generic m68k interrupt code can't use multiple handlers for
- * the same interrupt source (no chained interrupts).
- * We have to plug in a 'master' interrupt handler instead, calling
- * mac_SCC_interrupt with the proper arguments ...
- */
-
- if (!got_autovector) {
- if(sys_request_irq(IRQ4, mac_SCC_handler, 0, "SCC master", info))
- panic("macserial: can't get irq %d", IRQ4);
-#ifdef SCC_DEBUG
- printk("mac_SCC: got SCC master interrupt %d, channel %d info %p\n",
- IRQ4, channel, info);
-#endif
- got_autovector = 1;
- }
-
- if (info->private->zs_chan_a == info->private->zs_channel) {
- /* Channel A */
- if (request_irq(IRQ_SCCA, mac_SCC_interrupt, 0, "SCC A", info))
- panic("mac_SCC: can't get irq %d", IRQ_SCCA);
-#ifdef SCC_DEBUG
- printk("mac_SCC: got SCC A interrupt %d, channel %d info %p\n",
- IRQ_SCCA, channel, info);
-#endif
- } else {
- /* Channel B */
- if (request_irq(IRQ_SCCB, mac_SCC_interrupt, 0, "SCC B", info))
- panic("mac_SCC: can't get irq %d", IRQ_SCCB);
-#ifdef SCC_DEBUG
- printk("mac_SCC: got SCC B interrupt %d, channel %d info %p\n",
- IRQ_SCCB, channel, info);
-#endif
- }
-
- /* If console serial line, then enable interrupts. */
- if (info->private->is_cons) {
- printk("mac_SCC: console line %d; enabling interrupt!\n", info->line);
- write_zsreg(info->private->zs_channel, R1,
- (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
- write_zsreg(info->private->zs_channel, R9, (NV | MIE));
- write_zsreg(info->private->zs_channel, R10, (NRZ));
- write_zsreg(info->private->zs_channel, R3, (Rx8 | RxENABLE));
- write_zsreg(info->private->zs_channel, R5, (Tx8 | TxENAB));
- }
- /* If this is the kgdb line, enable interrupts because we
- * now want to receive the 'control-c' character from the
- * client attached to us asynchronously.
- */
- if (info->private->kgdb_channel) {
- printk("mac_SCC: kgdb line %d; enabling interrupt!\n", info->line);
- kgdb_chaninit(info, 1, info->private->zs_baud);
- }
- /* Report settings (in m68kserial.c) */
-#ifndef CONFIG_MAC
- printk("ttyS%d at 0x%08x (irq = %d)", info->line,
- info->port, info->irq);
- printk(" is a Z8530 SCC\n");
-#endif
-
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void SCC_deinit(struct m68k_async_struct * info, int leave_dtr)
-{
- unsigned long flags;
-
- save_flags(flags); cli(); /* Disable interrupts */
-
- info->private->pendregs[1] = info->private->curregs[1] = 0;
- write_zsreg(info->private->zs_channel, 1, 0); /* no interrupts */
-
- info->private->curregs[3] &= ~RxENABLE;
- info->private->pendregs[3] = info->private->curregs[3];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
-
- info->private->curregs[5] &= ~TxENAB;
-
- if (!leave_dtr)
- info->private->curregs[5] &= ~(DTR | RTS);
- else
- info->private->curregs[5] &= ~(RTS);
-
- info->private->pendregs[5] = info->private->curregs[5];
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
-
- restore_flags(flags);
-}
-
-/* FIXME !!! */
-static int SCC_check_custom_divisor( struct m68k_async_struct *info,
- int baud_base, int divisor )
-{
- return 0;
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void SCC_change_speed(struct m68k_async_struct *info)
-{
- unsigned short port;
- unsigned cflag;
- int i;
- int brg;
- unsigned long flags;
-
- if (!info->tty || !info->tty->termios)
- return;
- cflag = info->tty->termios->c_cflag;
- if (!(port = info->port))
- return;
- i = cflag & CBAUD;
-
- if (i == 0 && !(info->flags & ASYNC_SPD_MASK)) {
- /* speed == 0 -> drop DTR */
- save_flags(flags);
- cli();
- info->private->curregs[5] &= ~(DTR | RTS);
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- restore_flags(flags);
- return;
- }
-
-
- if (i & CBAUDEX) {
- /* XXX CBAUDEX is not obeyed.
- * It is impossible at a 32bits PPC. XXX??
- * But we have to report this to user ... someday.
- */
- i = B9600;
- }
-
- save_flags(flags); cli();
- info->private->zs_baud = baud_table[i];
- info->private->clk_divisor = 16;
-
- info->private->curregs[4] = X16CLK;
- info->private->curregs[11] = TCBR | RCBR;
- brg = BPS_TO_BRG(info->private->zs_baud,
- ZS_CLOCK/info->private->clk_divisor);
- info->private->curregs[12] = (brg & 255);
- info->private->curregs[13] = ((brg >> 8) & 255);
- info->private->curregs[14] = BRENABL;
-
- /* byte size and parity */
- info->private->curregs[3] &= ~RxNBITS_MASK;
- info->private->curregs[5] &= ~TxNBITS_MASK;
- switch (cflag & CSIZE) {
- case CS5:
- info->private->curregs[3] |= Rx5;
- info->private->curregs[5] |= Tx5;
- break;
- case CS6:
- info->private->curregs[3] |= Rx6;
- info->private->curregs[5] |= Tx6;
- break;
- case CS7:
- info->private->curregs[3] |= Rx7;
- info->private->curregs[5] |= Tx7;
- break;
- case CS8:
- default: /* defaults to 8 bits */
- info->private->curregs[3] |= Rx8;
- info->private->curregs[5] |= Tx8;
- break;
- }
- info->private->pendregs[3] = info->private->curregs[3];
- info->private->pendregs[5] = info->private->curregs[5];
-
- info->private->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
- if (cflag & CSTOPB) {
- info->private->curregs[4] |= SB2;
- } else {
- info->private->curregs[4] |= SB1;
- }
- if (cflag & PARENB) {
- info->private->curregs[4] |= PAR_ENA;
- }
- if (!(cflag & PARODD)) {
- info->private->curregs[4] |= PAR_EVEN;
- }
- info->private->pendregs[4] = info->private->curregs[4];
-
- info->private->curregs[15] &= ~(DCDIE | CTSIE);
- if (!(cflag & CLOCAL)) {
- info->private->curregs[15] |= DCDIE;
- }
- if (cflag & CRTSCTS) {
- info->private->curregs[15] |= CTSIE;
- if ((read_zsreg(info->private->zs_channel, 0) & CTS) != 0)
- info->private->tx_stopped = 1;
- } else
- info->private->tx_stopped = 0;
- info->private->pendregs[15] = info->private->curregs[15];
-
- /* Load up the new values */
- load_zsregs(info->private->zs_channel, info->private->curregs);
-
- restore_flags(flags);
-}
-
-/* This is for console output over ttya/ttyb */
-static void SCC_put_char(char ch)
-{
- struct mac_zschannel *chan = zs_conschan;
- int loops = 0;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0 && loops < 10000) {
- loops++;
- udelay(5);
- }
- write_zsdata(chan, ch);
- restore_flags(flags);
-}
-
-/* These are for receiving and sending characters under the kgdb
- * source level kernel debugger.
- */
-void putDebugChar(char kgdb_char)
-{
- struct mac_zschannel *chan = zs_kgdbchan;
-
- while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
- udelay(5);
- write_zsdata(chan, kgdb_char);
-}
-
-char getDebugChar(void)
-{
- struct mac_zschannel *chan = zs_kgdbchan;
-
- while ((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
- udelay(5);
- return read_zsdata(chan);
-}
-
-/*
- * Fair output driver allows a process to speak.
- */
-static void SCC_fair_output(void)
-{
- int left; /* Output no more than that */
- unsigned long flags;
- struct m68k_async_struct *info = zs_consinfo;
- char c;
-
- if (info == 0) return;
- if (info->xmit_buf == 0) return;
-
- save_flags(flags); cli();
- left = info->xmit_cnt;
- while (left != 0) {
- c = info->xmit_buf[info->xmit_tail];
- info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
- restore_flags(flags);
-
- SCC_put_char(c);
-
- save_flags(flags); cli();
- left = MIN(info->xmit_cnt, left-1);
- }
-
- restore_flags(flags);
- return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *p)
-{
- char c;
-
- while ((c = *(p++)) != 0) {
- if (c == '\n')
- SCC_put_char('\r');
- SCC_put_char(c);
- }
-
- /* Comment this if you want to have a strict interrupt-driven output */
- SCC_fair_output();
-}
-
-/* FIXME: check with SCC_enab_tx_int!! */
-#if 0
-static void rs_flush_chars(struct tty_struct *tty)
-{
- struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
- return;
-
- if (info->xmit_cnt <= 0 || tty->stopped || info->private->tx_stopped ||
- !info->xmit_buf)
- return;
-
- /* Enable transmitter */
- save_flags(flags); cli();
- transmit_chars(info);
- restore_flags(flags);
-}
-
-static int rs_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
-{
- int c, total = 0;
- struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_write"))
- return 0;
-
- if (!tty || !info->xmit_buf)
- return 0;
-
- save_flags(flags);
- while (1) {
- cli();
- c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
- if (c <= 0)
- break;
-
- if (from_user) {
- down(&tmp_buf_sem);
- memcpy_fromfs(tmp_buf, buf, c);
- 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);
- up(&tmp_buf_sem);
- } else
- memcpy(info->xmit_buf + info->xmit_head, buf, c);
- info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt += c;
- restore_flags(flags);
- buf += c;
- count -= c;
- total += c;
- }
- if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
- && !info->tx_active)
- transmit_chars(info);
- restore_flags(flags);
- return total;
-}
-#endif
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void SCC_throttle(struct m68k_async_struct *info, int status)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (status) {
- /*
- * Here we want to turn off the RTS line. On Macintoshes,
- * we only get the DTR line, which goes to both DTR and
- * RTS on the modem. RTS doesn't go out to the serial
- * port socket. So you should make sure your modem is
- * set to ignore DTR if you're using CRTSCTS.
- */
- info->private->curregs[5] &= ~(DTR | RTS);
- info->private->pendregs[5] &= ~(DTR | RTS);
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- } else {
- /* Assert RTS and DTR lines */
- info->private->curregs[5] |= DTR | RTS;
- info->private->pendregs[5] |= DTR | RTS;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
-
- restore_flags(flags);
-
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static void SCC_get_serial_info(struct m68k_async_struct * info,
- struct serial_struct * retinfo)
-{
- retinfo->baud_base = info->baud_base;
- retinfo->custom_divisor = info->custom_divisor;
-}
-
-/* FIXME: set_serial_info needs check_custom_divisor !!! */
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * is emptied. On bus types like RS485, the transmitter must
- * release the bus after transmitting. This must be done when
- * the transmit shift register is empty, not be done when the
- * transmit holding register is empty. This functionality
- * allows an RS485 driver to be written in user space.
- */
-static int SCC_get_lsr_info(struct m68k_async_struct * info, unsigned int *value)
-{
- unsigned char status;
-
- cli();
- status = read_zsreg(info->private->zs_channel, 0);
- sti();
- return status;
-}
-
-static unsigned int SCC_get_modem_info(struct m68k_async_struct *info)
-{
- unsigned char control, status;
- unsigned int result;
-
- cli();
- control = info->private->curregs[5];
- status = read_zsreg(info->private->zs_channel, 0);
- sti();
- result = ((control & RTS) ? TIOCM_RTS: 0)
- | ((control & DTR) ? TIOCM_DTR: 0)
- | ((status & DCD) ? TIOCM_CAR: 0)
- | ((status & CTS) ? 0: TIOCM_CTS);
- return result;
-}
-
-/* FIXME: zs_setdtr was used in rs_open ... */
-
-static int SCC_set_modem_info(struct m68k_async_struct *info,
- int new_dtr, int new_rts)
-{
- unsigned int bits;
-
- bits = (new_rts ? RTS: 0) + (new_dtr ? DTR: 0);
- info->private->curregs[5] = (info->private->curregs[5] & ~(DTR | RTS)) | bits;
- info->private->pendregs[5] = info->private->curregs[5];
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- sti();
- return 0;
-}
-
-/*
- * This routine sends a break character out the serial port.
- */
-static void SCC_set_break(struct m68k_async_struct * info, int break_flag)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (break_flag) {
- info->private->curregs[5] |= SND_BRK;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- } else {
- info->private->curregs[5] &= ~SND_BRK;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
-
- restore_flags(flags);
-}
-
-/* FIXME: these have to be enabled in rs_ioctl !! */
-
-static int SCC_ioctl(struct tty_struct *tty, struct file * file,
- struct m68k_async_struct * info, unsigned int cmd,
- unsigned long arg)
-{
- int error;
-
- switch (cmd) {
- case TIOCSERGETLSR: /* Get line status register */
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
- if (error)
- return error;
- else
- return SCC_get_lsr_info(info, (unsigned int *) arg);
-
- case TIOCSERGSTRUCT:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct m68k_async_struct));
- if (error)
- return error;
- copy_to_user((struct m68k_async_struct *) arg,
- info, sizeof(struct m68k_async_struct));
- return 0;
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static void SCC_stop_receive (struct m68k_async_struct *info)
-{
- /* disable Rx */
- info->private->curregs[3] &= ~RxENABLE;
- info->private->pendregs[3] = info->private->curregs[3];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
- /* disable Rx interrupts */
- info->private->curregs[1] &= ~(0x18); /* disable any rx ints */
- info->private->pendregs[1] = info->private->curregs[1];
- write_zsreg(info->private->zs_channel, 1, info->private->curregs[1]);
- ZS_CLEARFIFO(info->private->zs_channel);
-}
-
-static int SCC_trans_empty (struct m68k_async_struct *info)
-{
- return (read_zsreg(info->private->zs_channel, 1) & ALL_SNT) != 0;
-}
-
-/* Finally, routines used to initialize the serial driver. */
-
-#ifdef CONFIG_MAC
-
-/*
- * Mac: use boot_info data; assume 2 channels
- */
-
-static void probe_sccs(void)
-{
- int n;
-
-#define ZS_CONTROL 0x50F04000
-#define ZS_DATA (ZS_CONTROL+4)
-#define ZS_IRQ 5
-#define ZS_MOVE -2
-#define ZS_DATA_MOVE 4
-#define ZS_CH_A_FIRST 2
-
- /* last-ditch fixup for NetBSD booter case */
- if (mac_bi_data.sccbase == 0)
- mac_bi_data.sccbase = ZS_CONTROL;
-
- /* testing: fix up broken 24 bit addresses (ClassicII) */
- if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase)
- mac_bi_data.sccbase |= 0x50000000;
-
- if ( !hwreg_present((void *)mac_bi_data.sccbase))
- {
- printk(KERN_WARNING "z8530: Serial devices not accessible. Check serial switch.\n");
- return;
- }
-
- for(n=0;n<2;n++)
- {
-#if 0
- zs_channels[n].control = (volatile unsigned char *)
- ZS_CONTROL+ZS_MOVE*n;
- zs_channels[n].data = (volatile unsigned char *)ZS_DATA+ZS_MOVE*n;
-#else
- zs_channels[n].control = (volatile unsigned char *) /* 2, 0 */
- (mac_bi_data.sccbase+ZS_CH_A_FIRST)+ZS_MOVE*n;
- zs_channels[n].data = (volatile unsigned char *) /* 6, 4 */
- (mac_bi_data.sccbase+ZS_CH_A_FIRST+ZS_DATA_MOVE)+ZS_MOVE*n;
-#endif
- zs_soft[n].private = &zs_soft_private[n];
- zs_soft[n].private->zs_channel = &zs_channels[n];
- zs_soft[n].irq = IRQ4;
-#if 0
- if (request_irq(ch->intrs[0], rs_interrupt, 0,
- "SCC", &zs_soft[n]))
- panic("macserial: can't get irq %d",
- ch->intrs[0]);
-#endif
- if (n & 1)
- zs_soft[n].private->zs_chan_a = &zs_channels[n-1];
- else
- zs_soft[n].private->zs_chan_a = &zs_channels[n];
- }
-
- zs_channels_found=2;
-}
-
-#else
-
-/*
- * PowerMAC - query the PROM
- */
-
-static void show_serial_version(void)
-{
- printk("PowerMac Z8530 serial driver version 1.00\n");
-}
-
-/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
-static void
-probe_sccs()
-{
- struct device_node *dev, *ch;
- struct m68k_async_struct **pp;
- int n;
-
- n = 0;
- pp = &zs_chain;
- for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
- if (n >= NUM_CHANNELS) {
- printk("Sorry, can't use %s: no more channels\n",
- dev->full_name);
- continue;
- }
- for (ch = dev->child; ch != 0; ch = ch->sibling) {
- if (ch->n_addrs < 1 || ch ->n_intrs < 1) {
- printk("Can't use %s: %d addrs %d intrs\n",
- ch->full_name, ch->n_addrs, ch->n_intrs);
- continue;
- }
- zs_channels[n].control = (volatile unsigned char *)
- ch->addrs[0].address;
- zs_channels[n].data = zs_channels[n].control
- + ch->addrs[0].size / 2;
- zs_soft[n].private = &zs_soft_private[n];
- zs_soft[n].private->zs_channel = &zs_channels[n];
- zs_soft[n].irq = ch->intrs[0];
- if (request_irq(ch->intrs[0], mac_SCC_interrupt, 0,
- "SCC", &zs_soft[n]))
- panic("macserial: can't get irq %d",
- ch->intrs[0]);
- /* XXX this assumes the prom puts chan A before B */
- if (n & 1)
- zs_soft[n].private->zs_chan_a = &zs_channels[n-1];
- else
- zs_soft[n].private->zs_chan_a = &zs_channels[n];
-
- *pp = &zs_soft[n];
- pp = &zs_soft[n].private->zs_next;
- ++n;
- }
- }
- *pp = 0;
- zs_channels_found = n;
-}
-
-#endif
-
-extern void register_console(void (*proc)(const char *));
-
-static inline void
-rs_cons_check(struct m68k_async_struct *ss, int channel)
-{
- int i, o, io;
- static int consout_registered = 0;
- static int msg_printed = 0;
-
- i = o = io = 0;
-
- /* Is this one of the serial console lines? */
- if ((zs_cons_chanout != channel) &&
- (zs_cons_chanin != channel))
- return;
- zs_conschan = ss->private->zs_channel;
- zs_consinfo = ss;
-
- /* Register the console output putchar, if necessary */
- if (zs_cons_chanout == channel) {
- o = 1;
- /* double whee.. */
- if (!consout_registered) {
- register_console(zs_console_print);
- consout_registered = 1;
- }
- }
-
- if (zs_cons_chanin == channel) {
- i = 1;
- }
- if (o && i)
- io = 1;
- if (ss->private->zs_baud != 9600)
- panic("Console baud rate weirdness");
-
- /* Set flag variable for this port so that it cannot be
- * opened for other uses by accident.
- */
- ss->private->is_cons = 1;
-
- if (io) {
- if(!msg_printed) {
- printk("zs%d: console I/O\n", ((channel>>1)&1));
- msg_printed = 1;
- }
- } else {
- printk("zs%d: console %s\n", ((channel>>1)&1),
- (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
- }
-
- /* FIXME : register interrupt here??? */
-}
-
-volatile int test_done;
-
-/* rs_init inits the driver */
-int mac_SCC_init(void)
-{
- int channel, line, nr = 0;
- unsigned long flags;
- struct serial_struct req;
-
- printk("Mac68K Z8530 serial driver version 1.01\n");
-
- /* SCC present at all? */
- if (!MACH_IS_MAC)
- return( -ENODEV );
-
- if (zs_chain == 0)
- probe_sccs();
-
- save_flags(flags);
- cli();
-
- /*
- * FIXME: init of rs_table entry and register_serial now done,
- * but possible clash of zs_soft[channel] and rs_table[channel]!!
- * zs_soft initialized in probe_sccs(), some settings copied to
- * info = &rs_table[channel], which is used by the mid-level code.
- * The info->private part is shared among both!
- */
-
- for (channel = 0; channel < zs_channels_found; ++channel) {
- req.line = channel;
- req.type = SER_SCC_MAC;
- req.port = (int) zs_soft[channel].private->zs_channel->control;
-
- if ((line = register_serial( &req )) >= 0) {
- SCC_init_port( &rs_table[line], req.type, line );
- ++nr;
- }
- else
- printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
- }
-
- restore_flags(flags);
-
- return( nr > 0 ? 0 : -ENODEV );
-}
-
-/* Hooks for running a serial console. con_init() calls this if the
- * console is being run over one of the serial ports.
- * 'channel' is decoded as 0=modem 1=printer, 'chip' is ignored.
- */
-void
-rs_cons_hook(int chip, int out, int channel)
-{
- if (zs_chain == 0)
- probe_sccs();
- zs_soft[channel].private->clk_divisor = 16;
- zs_soft[channel].private->zs_baud = get_zsbaud(&zs_soft[channel]);
- rs_cons_check(&zs_soft[channel], channel);
- if (out)
- zs_cons_chanout = channel;
- else
- zs_cons_chanin = channel;
-
- /* FIXME : register interrupt here??? */
-}
-
-/* This is called at boot time to prime the kgdb serial debugging
- * serial line. The 'tty_num' argument is 0 for /dev/ttyS0 and 1
- * for /dev/ttyS1 which is determined in setup_arch() from the
- * boot command line flags.
- */
-void
-rs_kgdb_hook(int tty_num)
-{
- if (zs_chain == 0)
- probe_sccs();
- zs_kgdbchan = zs_soft[tty_num].private->zs_channel;
- zs_soft[tty_num].private->clk_divisor = 16;
- zs_soft[tty_num].private->zs_baud = get_zsbaud(&zs_soft[tty_num]);
- zs_soft[tty_num].private->kgdb_channel = 1; /* This runs kgdb */
- zs_soft[tty_num ^ 1].private->kgdb_channel = 0; /* This does not */
- /* Turn on transmitter/receiver at 8-bits/char */
- kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
- ZS_CLEARERR(zs_kgdbchan);
- ZS_CLEARFIFO(zs_kgdbchan);
-
- /* FIXME : register interrupt here??? */
-}
-
diff --git a/drivers/char/mac_SCC.h b/drivers/char/mac_SCC.h
deleted file mode 100644
index 5e903e1db..000000000
--- a/drivers/char/mac_SCC.h
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * macserial.h: Definitions for the Macintosh Z8530 serial driver.
- *
- * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
- *
- * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-#ifndef _MAC_SCC_H
-#define _MAC_SCC_H
-
-/*
- * For the close wait times, 0 means wait forever for serial port to
- * flush its output. 65535 means don't wait at all.
- */
-#define ZILOG_CLOSING_WAIT_INF 0
-#define ZILOG_CLOSING_WAIT_NONE 65535
-
-/*
- * Definitions for ZILOG_struct (and serial_struct) flags field
- */
-#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
- on the callout port */
-#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
-#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
-#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ZILOG_SPD_MASK 0x0030
-#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
-
-#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
-#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
-
-#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
-#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
-#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
-
-#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
-#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
- * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
-#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
-#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
-#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
-#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
-#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
-#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
- *
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-/* MSch: gone to <asm/serial.h> */
-
-#define SERIAL_MAGIC 0x5301
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP 0
-
-#endif /* __KERNEL__ */
-
-/* Conversion routines to/from brg time constants from/to bits
- * per second.
- */
-#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
-#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
-
-/* The Zilog register set */
-
-#define FLAG 0x7e
-
-/* Write Register 0 */
-#define R0 0 /* Register selects */
-#define R1 1
-#define R2 2
-#define R3 3
-#define R4 4
-#define R5 5
-#define R6 6
-#define R7 7
-#define R8 8
-#define R9 9
-#define R10 10
-#define R11 11
-#define R12 12
-#define R13 13
-#define R14 14
-#define R15 15
-
-#define NULLCODE 0 /* Null Code */
-#define POINT_HIGH 0x8 /* Select upper half of registers */
-#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
-#define SEND_ABORT 0x18 /* HDLC Abort */
-#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
-#define RES_Tx_P 0x28 /* Reset TxINT Pending */
-#define ERR_RES 0x30 /* Error Reset */
-#define RES_H_IUS 0x38 /* Reset highest IUS */
-
-#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
-#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
-#define RES_EOM_L 0xC0 /* Reset EOM latch */
-
-/* Write Register 1 */
-
-#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
-#define TxINT_ENAB 0x2 /* Tx Int Enable */
-#define PAR_SPEC 0x4 /* Parity is special condition */
-
-#define RxINT_DISAB 0 /* Rx Int Disable */
-#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
-#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
-#define INT_ERR_Rx 0x18 /* Int on error only */
-
-#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
-#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
-#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
-
-/* Write Register #2 (Interrupt Vector) */
-
-/* Write Register 3 */
-
-#define RxENABLE 0x1 /* Rx Enable */
-#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
-#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
-#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
-#define ENT_HM 0x10 /* Enter Hunt Mode */
-#define AUTO_ENAB 0x20 /* Auto Enables */
-#define Rx5 0x0 /* Rx 5 Bits/Character */
-#define Rx7 0x40 /* Rx 7 Bits/Character */
-#define Rx6 0x80 /* Rx 6 Bits/Character */
-#define Rx8 0xc0 /* Rx 8 Bits/Character */
-#define RxNBITS_MASK 0xc0
-
-/* Write Register 4 */
-
-#define PAR_ENA 0x1 /* Parity Enable */
-#define PAR_EVEN 0x2 /* Parity Even/Odd* */
-
-#define SYNC_ENAB 0 /* Sync Modes Enable */
-#define SB1 0x4 /* 1 stop bit/char */
-#define SB15 0x8 /* 1.5 stop bits/char */
-#define SB2 0xc /* 2 stop bits/char */
-#define SB_MASK 0xc
-
-#define MONSYNC 0 /* 8 Bit Sync character */
-#define BISYNC 0x10 /* 16 bit sync character */
-#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
-#define EXTSYNC 0x30 /* External Sync Mode */
-
-#define X1CLK 0x0 /* x1 clock mode */
-#define X16CLK 0x40 /* x16 clock mode */
-#define X32CLK 0x80 /* x32 clock mode */
-#define X64CLK 0xC0 /* x64 clock mode */
-#define XCLK_MASK 0xC0
-
-/* Write Register 5 */
-
-#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
-#define RTS 0x2 /* RTS */
-#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
-#define TxENAB 0x8 /* Tx Enable */
-#define SND_BRK 0x10 /* Send Break */
-#define Tx5 0x0 /* Tx 5 bits (or less)/character */
-#define Tx7 0x20 /* Tx 7 bits/character */
-#define Tx6 0x40 /* Tx 6 bits/character */
-#define Tx8 0x60 /* Tx 8 bits/character */
-#define TxNBITS_MASK 0x60
-#define DTR 0x80 /* DTR */
-
-/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
-
-/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
-
-/* Write Register 8 (transmit buffer) */
-
-/* Write Register 9 (Master interrupt control) */
-#define VIS 1 /* Vector Includes Status */
-#define NV 2 /* No Vector */
-#define DLC 4 /* Disable Lower Chain */
-#define MIE 8 /* Master Interrupt Enable */
-#define STATHI 0x10 /* Status high */
-#define NORESET 0 /* No reset on write to R9 */
-#define CHRB 0x40 /* Reset channel B */
-#define CHRA 0x80 /* Reset channel A */
-#define FHWRES 0xc0 /* Force hardware reset */
-
-/* Write Register 10 (misc control bits) */
-#define BIT6 1 /* 6 bit/8bit sync */
-#define LOOPMODE 2 /* SDLC Loop mode */
-#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
-#define MARKIDLE 8 /* Mark/flag on idle */
-#define GAOP 0x10 /* Go active on poll */
-#define NRZ 0 /* NRZ mode */
-#define NRZI 0x20 /* NRZI mode */
-#define FM1 0x40 /* FM1 (transition = 1) */
-#define FM0 0x60 /* FM0 (transition = 0) */
-#define CRCPS 0x80 /* CRC Preset I/O */
-
-/* Write Register 11 (Clock Mode control) */
-#define TRxCXT 0 /* TRxC = Xtal output */
-#define TRxCTC 1 /* TRxC = Transmit clock */
-#define TRxCBR 2 /* TRxC = BR Generator Output */
-#define TRxCDP 3 /* TRxC = DPLL output */
-#define TRxCOI 4 /* TRxC O/I */
-#define TCRTxCP 0 /* Transmit clock = RTxC pin */
-#define TCTRxCP 8 /* Transmit clock = TRxC pin */
-#define TCBR 0x10 /* Transmit clock = BR Generator output */
-#define TCDPLL 0x18 /* Transmit clock = DPLL output */
-#define RCRTxCP 0 /* Receive clock = RTxC pin */
-#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
-#define RCBR 0x40 /* Receive clock = BR Generator output */
-#define RCDPLL 0x60 /* Receive clock = DPLL output */
-#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
-
-/* Write Register 12 (lower byte of baud rate generator time constant) */
-
-/* Write Register 13 (upper byte of baud rate generator time constant) */
-
-/* Write Register 14 (Misc control bits) */
-#define BRENABL 1 /* Baud rate generator enable */
-#define BRSRC 2 /* Baud rate generator source */
-#define DTRREQ 4 /* DTR/Request function */
-#define AUTOECHO 8 /* Auto Echo */
-#define LOOPBAK 0x10 /* Local loopback */
-#define SEARCH 0x20 /* Enter search mode */
-#define RMC 0x40 /* Reset missing clock */
-#define DISDPLL 0x60 /* Disable DPLL */
-#define SSBR 0x80 /* Set DPLL source = BR generator */
-#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
-#define SFMM 0xc0 /* Set FM mode */
-#define SNRZI 0xe0 /* Set NRZI mode */
-
-/* Write Register 15 (external/status interrupt control) */
-#define ZCIE 2 /* Zero count IE */
-#define DCDIE 8 /* DCD IE */
-#define SYNCIE 0x10 /* Sync/hunt IE */
-#define CTSIE 0x20 /* CTS IE */
-#define TxUIE 0x40 /* Tx Underrun/EOM IE */
-#define BRKIE 0x80 /* Break/Abort IE */
-
-
-/* Read Register 0 */
-#define Rx_CH_AV 0x1 /* Rx Character Available */
-#define ZCOUNT 0x2 /* Zero count */
-#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
-#define DCD 0x8 /* DCD */
-#define SYNC_HUNT 0x10 /* Sync/hunt */
-#define CTS 0x20 /* CTS */
-#define TxEOM 0x40 /* Tx underrun */
-#define BRK_ABRT 0x80 /* Break/Abort */
-
-/* Read Register 1 */
-#define ALL_SNT 0x1 /* All sent */
-/* Residue Data for 8 Rx bits/char programmed */
-#define RES3 0x8 /* 0/3 */
-#define RES4 0x4 /* 0/4 */
-#define RES5 0xc /* 0/5 */
-#define RES6 0x2 /* 0/6 */
-#define RES7 0xa /* 0/7 */
-#define RES8 0x6 /* 0/8 */
-#define RES18 0xe /* 1/8 */
-#define RES28 0x0 /* 2/8 */
-/* Special Rx Condition Interrupts */
-#define PAR_ERR 0x10 /* Parity error */
-#define Rx_OVR 0x20 /* Rx Overrun Error */
-#define FRM_ERR 0x40 /* CRC/Framing Error */
-#define END_FR 0x80 /* End of Frame (SDLC) */
-
-/* Read Register 2 (channel b only) - Interrupt vector */
-
-/* Read Register 3 (interrupt pending register) ch a only */
-#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
-#define CHBTxIP 0x2 /* Channel B Tx IP */
-#define CHBRxIP 0x4 /* Channel B Rx IP */
-#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
-#define CHATxIP 0x10 /* Channel A Tx IP */
-#define CHARxIP 0x20 /* Channel A Rx IP */
-
-/* Read Register 8 (receive data register) */
-
-/* Read Register 10 (misc status bits) */
-#define ONLOOP 2 /* On loop */
-#define LOOPSEND 0x10 /* Loop sending */
-#define CLK2MIS 0x40 /* Two clocks missing */
-#define CLK1MIS 0x80 /* One clock missing */
-
-/* Read Register 12 (lower byte of baud rate generator constant) */
-
-/* Read Register 13 (upper byte of baud rate generator constant) */
-
-/* Read Register 15 (value of WR 15) */
-
-/* Misc macros */
-#define ZS_CLEARERR(channel) (write_zsreg(channel, 0, ERR_RES))
-#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
- garbage = read_zsdata(channel); \
- garbage = read_zsdata(channel); \
- garbage = read_zsdata(channel); \
- } while(0)
-
-#endif /* !(_MAC_SCC_H) */
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 09c12be26..e70860ea9 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -2,6 +2,9 @@
* linux/drivers/char/mem.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Added devfs support.
+ * Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
*/
#include <linux/config.h>
@@ -58,9 +61,6 @@ extern void mda_console_init(void);
#if defined(CONFIG_ADB)
extern void adbdev_init(void);
#endif
-#ifdef CONFIG_USB
-extern void usb_init(void);
-#endif
#ifdef CONFIG_PHONE
extern void telephony_init(void);
#endif
@@ -248,14 +248,16 @@ static ssize_t read_kmem(struct file *file, char *buf,
if (p < PAGE_SIZE && read > 0) {
size_t tmp = PAGE_SIZE - p;
if (tmp > read) tmp = read;
- clear_user(buf, tmp);
+ if (clear_user(buf, tmp))
+ return -EFAULT;
buf += tmp;
p += tmp;
read -= tmp;
count -= tmp;
}
#endif
- copy_to_user(buf, (char *)p, read);
+ if (copy_to_user(buf, (char *)p, read))
+ return -EFAULT;
p += read;
buf += read;
count -= read;
@@ -574,19 +576,47 @@ static int memory_open(struct inode * inode, struct file * filp)
return 0;
}
+void __init memory_devfs_register (void)
+{
+ /* These are never unregistered */
+ static const struct {
+ unsigned short minor;
+ char *name;
+ umode_t mode;
+ struct file_operations *fops;
+ } list[] = { /* list of minor devices */
+ {1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},
+ {2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},
+ {3, "null", S_IRUGO | S_IWUGO, &null_fops},
+#if (!defined(CONFIG_PPC) && !defined(__mc68000__) && !defined(__mips__)) || \
+ defined(CONFIG_HAVE_IO_PORTS)
+ {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},
+#endif
+ {5, "zero", S_IRUGO | S_IWUGO, &zero_fops},
+ {7, "full", S_IRUGO | S_IWUGO, &full_fops},
+ {8, "random", S_IRUGO | S_IWUSR, &random_fops},
+ {9, "urandom", S_IRUGO | S_IWUSR, &urandom_fops}
+ };
+ int i;
+
+ for (i=0; i<(sizeof(list)/sizeof(*list)); i++)
+ devfs_register (NULL, list[i].name, 0, DEVFS_FL_NONE,
+ MEM_MAJOR, list[i].minor,
+ list[i].mode | S_IFCHR, 0, 0,
+ list[i].fops, NULL);
+}
+
static struct file_operations memory_fops = {
open: memory_open, /* just a selector for the real open */
};
int __init chr_dev_init(void)
{
- if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
+ if (devfs_register_chrdev(MEM_MAJOR,"mem",&memory_fops))
printk("unable to get major %d for memory devs\n", MEM_MAJOR);
+ memory_devfs_register();
rand_initialize();
raw_init();
-#ifdef CONFIG_USB
- usb_init();
-#endif
#ifdef CONFIG_I2C
i2c_init_all();
#endif
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 0e3f17fe6..99884a00a 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -29,6 +29,8 @@
*
* Changes for kmod (from kerneld):
* Cyrus Durgin <cider@speakeasy.org>
+ *
+ * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au> 10-Jan-1998
*/
#include <linux/module.h>
@@ -41,6 +43,7 @@
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/stat.h>
#include <linux/init.h>
@@ -120,6 +123,8 @@ static struct file_operations misc_fops = {
int misc_register(struct miscdevice * misc)
{
+ static devfs_handle_t devfs_handle = NULL;
+
if (misc->next || misc->prev)
return -EBUSY;
if (misc->minor == MISC_DYNAMIC_MINOR) {
@@ -132,6 +137,13 @@ int misc_register(struct miscdevice * misc)
}
if (misc->minor < DYNAMIC_MINORS)
misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
+ if (!devfs_handle)
+ devfs_handle = devfs_mk_dir (NULL, "misc", 4, NULL);
+ misc->devfs_handle =
+ devfs_register (devfs_handle, misc->name, 0, DEVFS_FL_NONE,
+ MISC_MAJOR, misc->minor,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
+ misc->fops, NULL);
/*
* Add it to the front, so that later devices can "override"
@@ -153,6 +165,7 @@ int misc_deregister(struct miscdevice * misc)
misc->next->prev = misc->prev;
misc->next = NULL;
misc->prev = NULL;
+ devfs_unregister (misc->devfs_handle);
if (i < DYNAMIC_MINORS && i>0) {
misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
}
@@ -219,11 +232,10 @@ int __init misc_init(void)
#ifdef CONFIG_SGI
streamable_init ();
#endif
- if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
+ if (devfs_register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
printk("unable to get major %d for misc devices\n",
MISC_MAJOR);
return -EIO;
}
-
return 0;
}
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index e49144c1a..c61a6e6a9 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -38,7 +38,7 @@
/* select machine configuration */
#if defined(CONFIG_ATARI)
#define MACH ATARI
-#elif defined(__i386__) /* and others?? */
+#elif defined(__i386__) || defined(__arm__) /* and others?? */
#define MACH PC
#else
#error Cannot build nvram driver for this machine configuration.
diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c
index 68dbeff21..6ae0ab71c 100644
--- a/drivers/char/pc_keyb.c
+++ b/drivers/char/pc_keyb.c
@@ -413,9 +413,12 @@ static inline void handle_mouse_event(unsigned char scancode)
#endif
}
+static unsigned char kbd_exists = 1;
+
static inline void handle_keyboard_event(unsigned char scancode)
{
#ifdef CONFIG_VT
+ kbd_exists = 1;
if (do_acknowledge(scancode))
handle_scancode(scancode, !(scancode & 0x80));
#endif
@@ -512,8 +515,10 @@ static int send_data(unsigned char data)
void pckbd_leds(unsigned char leds)
{
- if (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))
- send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */
+ if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) {
+ send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */
+ kbd_exists = 0;
+ }
}
/*
diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c
index 75f27e830..2e451ce17 100644
--- a/drivers/char/pcxx.c
+++ b/drivers/char/pcxx.c
@@ -1198,7 +1198,6 @@ int __init pcxe_init(void)
memset(pcxe_termios_locked,0,sizeof(struct termios *)*nbdevs);
init_bh(DIGI_BH,do_pcxe_bh);
- enable_bh(DIGI_BH);
timer_table[DIGI_TIMER].fn = pcxxpoll;
timer_table[DIGI_TIMER].expires = 0;
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 6a6ff8081..d73c09695 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -45,6 +45,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/ioctl.h>
#include <linux/parport.h>
#include <linux/ctype.h>
@@ -579,13 +580,20 @@ static struct file_operations pp_fops = {
release: pp_release,
};
+static devfs_handle_t devfs_handle = NULL;
+
static int __init ppdev_init (void)
{
- if (register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) {
+ if (devfs_register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) {
printk (KERN_WARNING CHRDEV ": unable to get major %d\n",
PP_MAJOR);
return -EIO;
}
+ devfs_handle = devfs_mk_dir (NULL, "parports", 0, NULL);
+ devfs_register_series (devfs_handle, "%u", PARPORT_MAX,
+ DEVFS_FL_DEFAULT, PP_MAJOR, 0,
+ S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+ &pp_fops, NULL);
printk (KERN_INFO PP_VERSION "\n");
return 0;
@@ -594,7 +602,8 @@ static int __init ppdev_init (void)
static void __exit ppdev_cleanup (void)
{
/* Clean up all parport stuff */
- unregister_chrdev (PP_MAJOR, CHRDEV);
+ devfs_unregister (devfs_handle);
+ devfs_unregister_chrdev (PP_MAJOR, CHRDEV);
}
module_init(ppdev_init);
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 5f35b24ad..77cdb7a51 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -20,6 +20,7 @@
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -28,6 +29,10 @@
#define BUILDING_PTY_C 1
#include <linux/devpts_fs.h>
+extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+ unsigned int minor);
+extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
+
struct pty_struct {
int magic;
wait_queue_head_t open_wait;
@@ -94,6 +99,7 @@ static void pty_close(struct tty_struct * tty, struct file * filp)
}
}
#endif
+ tty_unregister_devfs (&tty->link->driver, MINOR (tty->device));
tty_vhangup(tty->link);
}
}
@@ -323,6 +329,11 @@ static int pty_open(struct tty_struct *tty, struct file * filp)
clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
wake_up_interruptible(&pty->open_wait);
set_bit(TTY_THROTTLED, &tty->flags);
+ /* register a slave for the master */
+ if (tty->driver.major == PTY_MASTER_MAJOR)
+ tty_register_devfs(&tty->link->driver, DEVFS_FL_WAIT,
+ tty->link->driver.minor_start +
+ MINOR(tty->device)-tty->driver.minor_start);
retval = 0;
out:
return retval;
@@ -346,7 +357,7 @@ int __init pty_init(void)
memset(&pty_driver, 0, sizeof(struct tty_driver));
pty_driver.magic = TTY_DRIVER_MAGIC;
pty_driver.driver_name = "pty_master";
- pty_driver.name = "pty";
+ pty_driver.name = "pty/m%d";
pty_driver.major = PTY_MASTER_MAJOR;
pty_driver.minor_start = 0;
pty_driver.num = NR_PTYS;
@@ -377,12 +388,16 @@ int __init pty_init(void)
pty_slave_driver = pty_driver;
pty_slave_driver.driver_name = "pty_slave";
pty_slave_driver.proc_entry = 0;
- pty_slave_driver.name = "ttyp";
+ pty_slave_driver.name = "pty/s%d";
pty_slave_driver.subtype = PTY_TYPE_SLAVE;
pty_slave_driver.major = PTY_SLAVE_MAJOR;
pty_slave_driver.minor_start = 0;
pty_slave_driver.init_termios = tty_std_termios;
pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
+ /* Slave ptys are registered when their corresponding master pty
+ * is opened, and unregistered when the pair is closed.
+ */
+ pty_slave_driver.flags |= TTY_DRIVER_NO_DEVFS;
pty_slave_driver.table = ttyp_table;
pty_slave_driver.termios = ttyp_termios;
pty_slave_driver.termios_locked = ttyp_termios_locked;
@@ -403,6 +418,7 @@ int __init pty_init(void)
/* Unix98 devices */
#ifdef CONFIG_UNIX98_PTYS
+ devfs_mk_dir (NULL, "pts", 3, NULL);
printk("pty: %d Unix98 ptys configured\n", UNIX98_NR_MAJORS*NR_PTYS);
for ( i = 0 ; i < UNIX98_NR_MAJORS ; i++ ) {
int j;
@@ -415,6 +431,7 @@ int __init pty_init(void)
ptm_driver[i].name_base = i*NR_PTYS;
ptm_driver[i].num = NR_PTYS;
ptm_driver[i].other = &pts_driver[i];
+ ptm_driver[i].flags |= TTY_DRIVER_NO_DEVFS;
ptm_driver[i].table = ptm_table[i];
ptm_driver[i].termios = ptm_termios[i];
ptm_driver[i].termios_locked = ptm_termios_locked[i];
@@ -424,7 +441,7 @@ int __init pty_init(void)
init_waitqueue_head(&ptm_state[i][j].open_wait);
pts_driver[i] = pty_slave_driver;
- pts_driver[i].name = "pts";
+ pts_driver[i].name = "pts/%d";
pts_driver[i].proc_entry = 0;
pts_driver[i].major = UNIX98_PTY_SLAVE_MAJOR+i;
pts_driver[i].minor_start = 0;
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 95c188e83..6296e789b 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -97,14 +97,18 @@ static ssize_t rtc_read(struct file *file, char *buf,
static int rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
+#ifndef __alpha__
static unsigned int rtc_poll(struct file *file, poll_table *wait);
+#endif
static void get_rtc_time (struct rtc_time *rtc_tm);
static void get_rtc_alm_time (struct rtc_time *alm_tm);
+#ifndef __alpha__
static void rtc_dropped_irq(unsigned long data);
static void set_rtc_irq_bit(unsigned char bit);
static void mask_rtc_irq_bit(unsigned char bit);
+#endif
static inline unsigned char rtc_is_updating(void);
@@ -132,6 +136,7 @@ static unsigned long epoch = 1900; /* year corresponding to 0x00 */
static const unsigned char days_in_mo[] =
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+#ifndef __alpha__
/*
* A very tiny interrupt handler. It runs with SA_INTERRUPT set,
* so that there is no possibility of conflicting with the
@@ -162,6 +167,7 @@ static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (atomic_read(&rtc_status) & RTC_TIMER_ON)
mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
}
+#endif
/*
* Now all the various file operations that we export.
@@ -175,6 +181,9 @@ static long long rtc_llseek(struct file *file, loff_t offset, int origin)
static ssize_t rtc_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
+#ifdef __alpha__
+ return -EIO;
+#else
DECLARE_WAITQUEUE(wait, current);
unsigned long data;
ssize_t retval;
@@ -206,6 +215,7 @@ static ssize_t rtc_read(struct file *file, char *buf,
remove_wait_queue(&rtc_wait, &wait);
return retval;
+#endif
}
static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
@@ -216,6 +226,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
struct rtc_time wtime;
switch (cmd) {
+#ifndef __alpha__
case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
{
mask_rtc_irq_bit(RTC_AIE);
@@ -265,6 +276,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
set_rtc_irq_bit(RTC_UIE);
return 0;
}
+#endif
case RTC_ALM_READ: /* Read the present alarm time */
{
/*
@@ -398,6 +410,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
+#ifndef __alpha__
case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
{
return put_user(rtc_freq, (unsigned long *)arg);
@@ -437,7 +450,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
-#if defined(__alpha__) || defined(__mips__)
case RTC_EPOCH_READ: /* Read the epoch. */
{
return put_user (epoch, (unsigned long *)arg);
@@ -494,13 +506,14 @@ static int rtc_fasync (int fd, struct file *filp, int on)
static int rtc_release(struct inode *inode, struct file *file)
{
+ unsigned long flags;
+#ifndef __alpha__
/*
* Turn off all interrupts once the device is no longer
* in use, and clear the data.
*/
unsigned char tmp;
- unsigned long flags;
spin_lock_irqsave(&rtc_lock, flags);
tmp = CMOS_READ(RTC_CONTROL);
@@ -520,6 +533,7 @@ static int rtc_release(struct inode *inode, struct file *file)
rtc_fasync (-1, file, 0);
}
+#endif
MOD_DEC_USE_COUNT;
spin_lock_irqsave (&rtc_lock, flags);
@@ -529,6 +543,7 @@ static int rtc_release(struct inode *inode, struct file *file)
return 0;
}
+#ifndef __alpha__
static unsigned int rtc_poll(struct file *file, poll_table *wait)
{
unsigned long l, flags;
@@ -543,6 +558,7 @@ static unsigned int rtc_poll(struct file *file, poll_table *wait)
return POLLIN | POLLRDNORM;
return 0;
}
+#endif
/*
* The various file operations we support.
@@ -551,7 +567,9 @@ static unsigned int rtc_poll(struct file *file, poll_table *wait)
static struct file_operations rtc_fops = {
llseek: rtc_llseek,
read: rtc_read,
+#ifndef __alpha__
poll: rtc_poll,
+#endif
ioctl: rtc_ioctl,
open: rtc_open,
release: rtc_release,
@@ -612,12 +630,14 @@ found:
return -EIO;
}
+#ifndef __alpha__
if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
{
/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
return -EIO;
}
+#endif
request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
#endif /* __sparc__ vs. others */
@@ -657,12 +677,14 @@ found:
#ifdef CONFIG_MIPS_JAZZ
epoch = 1980;
#endif
+#ifndef __alpha__
init_timer(&rtc_irq_timer);
rtc_irq_timer.function = rtc_dropped_irq;
spin_lock_irqsave(&rtc_lock, flags);
/* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
spin_unlock_irqrestore(&rtc_lock, flags);
+#endif
rtc_freq = 1024;
printk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n");
@@ -692,6 +714,7 @@ module_init(rtc_init);
module_exit(rtc_exit);
EXPORT_NO_SYMBOLS;
+#ifndef __alpha__
/*
* At IRQ rates >= 4096Hz, an interrupt may get lost altogether.
* (usually during an IDE disk interrupt, with IRQ unmasking off)
@@ -717,6 +740,7 @@ static void rtc_dropped_irq(unsigned long data)
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */
spin_unlock_irqrestore(&rtc_lock, flags);
}
+#endif
/*
* Info exported via "/proc/driver/rtc".
@@ -905,6 +929,7 @@ static void get_rtc_alm_time(struct rtc_time *alm_tm)
}
}
+#ifndef __alpha__
/*
* Used to disable/enable interrupts for any one of UIE, AIE, PIE.
* Rumour has it that if you frob the interrupt enable/disable
@@ -942,3 +967,4 @@ static void set_rtc_irq_bit(unsigned char bit)
rtc_irq_data = 0;
spin_unlock_irqrestore(&rtc_lock, flags);
}
+#endif
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index eeb82a033..3b9a71f9e 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -217,7 +217,12 @@ static char *serial_revdate = "2000-1-27";
#else
#define _INLINE_
#endif
-
+
+extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+ unsigned int minor);
+extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
+
+
static char *serial_name = "Serial driver";
static DECLARE_TASK_QUEUE(tq_serial);
@@ -4406,7 +4411,7 @@ int __init rs_init(void)
#if (LINUX_VERSION_CODE > 0x20100)
serial_driver.driver_name = "serial";
#endif
- serial_driver.name = "ttyS";
+ serial_driver.name = "tts/%d";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;
serial_driver.num = NR_PORTS;
@@ -4415,7 +4420,7 @@ int __init rs_init(void)
serial_driver.init_termios = tty_std_termios;
serial_driver.init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
serial_driver.refcount = &serial_refcount;
serial_driver.table = serial_table;
serial_driver.termios = serial_termios;
@@ -4450,7 +4455,7 @@ int __init rs_init(void)
* major number and the subtype code.
*/
callout_driver = serial_driver;
- callout_driver.name = "cua";
+ callout_driver.name = "cua/%d";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
#if (LINUX_VERSION_CODE >= 131343)
@@ -4497,6 +4502,10 @@ int __init rs_init(void)
(state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
state->port, state->irq,
uart_config[state->type].name);
+ tty_register_devfs(&serial_driver, 0,
+ serial_driver.minor_start + state->line);
+ tty_register_devfs(&callout_driver, 0,
+ callout_driver.minor_start + state->line);
}
#ifdef ENABLE_SERIAL_PCI
probe_serial_pci();
@@ -4574,6 +4583,10 @@ int register_serial(struct serial_struct *req)
state->iomem_base ? (unsigned long)state->iomem_base :
(unsigned long)state->port,
state->irq, uart_config[state->type].name);
+ tty_register_devfs(&serial_driver, 0,
+ serial_driver.minor_start + state->line);
+ tty_register_devfs(&callout_driver, 0,
+ callout_driver.minor_start + state->line);
return state->line + SERIAL_DEV_OFFSET;
}
@@ -4588,6 +4601,13 @@ void unregister_serial(int line)
tty_hangup(state->info->tty);
state->type = PORT_UNKNOWN;
printk(KERN_INFO "tty%02d unloaded\n", state->line);
+ /* These will be hidden, because they are devices that will no longer
+ * be available to the system. (ie, PCMCIA modems, once ejected)
+ */
+ tty_unregister_devfs(&serial_driver,
+ serial_driver.minor_start + state->line);
+ tty_unregister_devfs(&callout_driver,
+ callout_driver.minor_start + state->line);
restore_flags(flags);
}
@@ -4676,6 +4696,8 @@ static inline void wait_for_xmitr(struct async_struct *info)
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
+ *
+ * The console_lock must be held when we get here.
*/
static void serial_console_write(struct console *co, const char *s,
unsigned count)
diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c
index 885b236fd..35b480a1c 100644
--- a/drivers/char/serial167.c
+++ b/drivers/char/serial167.c
@@ -2729,6 +2729,8 @@ void console_setup(char *str, int *ints)
*
* Of course, once the console has been registered, we had better ensure
* that serial167_init() doesn't leave the chip non-functional.
+ *
+ * The console_lock must be held when we get here.
*/
void serial167_console_write(struct console *co, const char *str, unsigned count)
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index d936eef41..18fa8c645 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -40,6 +40,7 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -750,6 +751,8 @@ static struct file_operations stl_fsiomem = {
/*****************************************************************************/
+static devfs_handle_t devfs_handle = NULL;
+
#ifdef MODULE
/*
@@ -780,7 +783,7 @@ void cleanup_module()
stlpanel_t *panelp;
stlport_t *portp;
unsigned long flags;
- int i, j, k;
+ int i, j, k, l;
#if DEBUG
printk("cleanup_module()\n");
@@ -806,7 +809,8 @@ void cleanup_module()
restore_flags(flags);
return;
}
- if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
+ devfs_unregister (devfs_handle);
+ if ((i = devfs_unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
printk("STALLION: failed to un-register serial memory device, "
"errno=%d\n", -i);
@@ -3213,8 +3217,13 @@ int __init stl_init(void)
* Set up a character driver for per board stuff. This is mainly used
* to do stats ioctls on the ports.
*/
- if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
+ if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
printk("STALLION: failed to register serial board device\n");
+ devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL);
+ devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT,
+ STL_SIOMEMMAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &stl_fsiomem, NULL);
/*
* Set up the tty driver structure and register us as a driver.
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index 6fece9470..7fcbfbb52 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -21,6 +21,7 @@
#include <linux/kbd_kern.h>
#include <linux/quotaops.h>
#include <linux/smp_lock.h>
+#include <linux/module.h>
#include <asm/ptrace.h>
@@ -32,6 +33,8 @@ extern struct vfsmount *vfsmntlist;
/* Machine specific power off function */
void (*sysrq_power_off)(void) = NULL;
+EXPORT_SYMBOL(sysrq_power_off);
+
/* Send a signal to all user processes */
static void send_sig_all(int sig, int even_init)
diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c
index 7a9257ae9..d29c63407 100644
--- a/drivers/char/tpqic02.c
+++ b/drivers/char/tpqic02.c
@@ -89,7 +89,8 @@
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/init.h>
-
+#include <linux/devfs_fs_kernel.h>
+
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -2774,23 +2775,6 @@ static struct file_operations qic02_tape_fops = {
};
-/* Why is this not is one place? */
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
- int order;
-
- size = (size-1) >> (PAGE_SHIFT-1);
- order = -1;
- do
- {
- size >>= 1;
- order++;
- } while (size);
- return order;
-}
-
-
static void qic02_release_resources(void)
{
free_irq(QIC02_TAPE_IRQ, NULL);
@@ -2798,7 +2782,7 @@ static void qic02_release_resources(void)
release_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE);
if (buffaddr)
{
- free_pages(buffaddr, __get_order(TPQBUF_SIZE));
+ free_pages(buffaddr, get_order(TPQBUF_SIZE));
}
buffaddr = 0; /* Better to cause a panic than overwite someone else */
status_zombie = YES;
@@ -2850,7 +2834,7 @@ static int qic02_get_resources(void)
/* Setup the page-address for the dma transfer. */
/*** TODO: does _get_dma_pages() really return the physical address?? ****/
- buffaddr = __get_dma_pages(GFP_KERNEL,__get_order(TPQBUF_SIZE));
+ buffaddr = __get_dma_pages(GFP_KERNEL,get_order(TPQBUF_SIZE));
if (!buffaddr)
{
@@ -2919,7 +2903,7 @@ int __init qic02_tape_init(void)
#endif
printk(TPQIC02_NAME ": DMA buffers: %u blocks\n", NR_BLK_BUF);
/* If we got this far, install driver functions */
- if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops))
+ if (devfs_register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops))
{
printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
#ifndef CONFIG_QIC02_DYNCONF
@@ -2927,7 +2911,38 @@ int __init qic02_tape_init(void)
#endif
return -ENODEV;
}
-
+ devfs_register (NULL, "ntpqic11", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 2,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
+ devfs_register (NULL, "tpqic11", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 3,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
+ devfs_register (NULL, "ntpqic24", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 4,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
+ devfs_register (NULL, "tpqic24", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 5,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
+ devfs_register (NULL, "ntpqic120", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 6,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
+ devfs_register (NULL, "tpqic120", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 7,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
+ devfs_register (NULL, "ntpqic150", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 8,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
+ devfs_register (NULL, "tpqic150", 0, DEVFS_FL_NONE,
+ QIC02_TAPE_MAJOR, 9,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0,
+ &qic02_tape_fops, NULL);
init_waitqueue_head(&qic02_tape_transfer);
/* prepare timer */
TIMEROFF;
@@ -2974,7 +2989,15 @@ void cleanup_module(void)
{
qic02_release_resources();
}
- unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
+ devfs_unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
+ devfs_unregister(devfs_find_handle(NULL, "ntpqic11", 0, QIC02_TAPE_MAJOR, 2, DEVFS_SPECIAL_CHR, 0));
+ devfs_unregister(devfs_find_handle(NULL, "tpqic11", 0, QIC02_TAPE_MAJOR, 3, DEVFS_SPECIAL_CHR, 0));
+ devfs_unregister(devfs_find_handle(NULL, "ntpqic24", 0, QIC02_TAPE_MAJOR, 4, DEVFS_SPECIAL_CHR, 0));
+ devfs_unregister(devfs_find_handle(NULL, "tpqic24", 0, QIC02_TAPE_MAJOR, 5, DEVFS_SPECIAL_CHR, 0));
+ devfs_unregister(devfs_find_handle(NULL, "ntpqic120", 0, QIC02_TAPE_MAJOR, 6, DEVFS_SPECIAL_CHR, 0));
+ devfs_unregister(devfs_find_handle(NULL, "tpqic120", 0, QIC02_TAPE_MAJOR, 7, DEVFS_SPECIAL_CHR, 0));
+ devfs_unregister(devfs_find_handle(NULL, "ntpqic150", 0, QIC02_TAPE_MAJOR, 8, DEVFS_SPECIAL_CHR, 0));
+ devfs_unregister(devfs_find_handle(NULL, "tpqic150", 0, QIC02_TAPE_MAJOR, 9, DEVFS_SPECIAL_CHR, 0));
}
int init_module(void)
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index abc27bb91..705d876b3 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -52,6 +52,9 @@
* Rewrote init_dev and release_dev to eliminate races.
* -- Bill Hawes <whawes@star.net>, June 97
*
+ * Added devfs support.
+ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
+ *
* Added support for a Unix98-style ptmx device.
* -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
*/
@@ -79,6 +82,7 @@
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
@@ -88,9 +92,17 @@
#include <linux/kbd_kern.h>
#include <linux/vt_kern.h>
#include <linux/selection.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/kmod.h>
+#ifdef CONFIG_VT
+extern void con_init_devfs (void);
+#endif
+void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+ unsigned minor);
+void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
+
#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
@@ -107,6 +119,7 @@ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
#ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver ptm_driver[]; /* Unix98 pty masters; for /dev/ptmx */
+extern struct tty_driver pts_driver[]; /* Unix98 pty slaves; for /dev/ptmx */
#endif
/*
@@ -149,16 +162,26 @@ extern int rs_8xx_init(void);
/*
* This routine returns the name of tty.
*/
+static char *
+_tty_make_name(struct tty_struct *tty, const char *name, char *buf)
+{
+ int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;
+
+ if (!tty) /* Hmm. NULL pointer. That's fun. */
+ strcpy(buf, "NULL tty");
+ else
+ sprintf(buf, name,
+ idx + tty->driver.name_base);
+
+ return buf;
+}
+
#define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \
(tty)->driver.name_base)
-
+
char *tty_name(struct tty_struct *tty, char *buf)
{
- if (tty)
- sprintf(buf, "%s%d", tty->driver.name, TTY_NUMBER(tty));
- else
- strcpy(buf, "NULL tty");
- return buf;
+ return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);
}
inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
@@ -1298,6 +1321,8 @@ retry_open:
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
minor -= driver->minor_start;
devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start));
+ tty_register_devfs(&pts_driver[major], 0,
+ pts_driver[major].minor_start + minor);
noctty = 1;
goto init_dev_done;
@@ -1966,16 +1991,86 @@ void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
}
/*
+ * Register a tty device described by <driver>, with minor number <minor>.
+ */
+void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+ unsigned int minor)
+{
+#ifdef CONFIG_DEVFS_FS
+ umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ struct tty_struct tty;
+ char buf[32];
+
+ flags |= DEVFS_FL_DEFAULT;
+ tty.driver = *driver;
+ tty.device = MKDEV (driver->major, minor);
+ switch (tty.device) {
+ case TTY_DEV:
+ case PTMX_DEV:
+ mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ break;
+ default:
+ flags |= DEVFS_FL_AUTO_OWNER;
+ break;
+ }
+ if ((minor < driver->minor_start) ||
+ (minor >= driver->minor_start + driver->num)) {
+ printk(KERN_ERR "Attempt to register invalid minor number "
+ "with devfs (%d:%d).\n", (int)driver->major,(int)minor);
+ return;
+ }
+ if (driver->type == TTY_DRIVER_TYPE_CONSOLE) {
+ flags |= DEVFS_FL_AOPEN_NOTIFY;
+ flags &= ~DEVFS_FL_AUTO_OWNER;
+ }
+# ifdef CONFIG_UNIX98_PTYS
+ if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) &&
+ (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) {
+ flags &= ~DEVFS_FL_AUTO_OWNER;
+ uid = current->uid;
+ gid = current->gid;
+ }
+# endif
+ devfs_register (NULL, tty_name (&tty, buf), 0, flags,
+ driver->major, minor, mode, uid, gid,
+ &tty_fops, NULL);
+#endif /* CONFIG_DEVFS_FS */
+}
+
+void tty_unregister_devfs (struct tty_driver *driver, unsigned minor)
+{
+#ifdef CONFIG_DEVFS_FS
+ void * handle;
+ struct tty_struct tty;
+ char buf[32];
+
+ tty.driver = *driver;
+ tty.device = MKDEV(driver->major, minor);
+
+ handle = devfs_find_handle (NULL, tty_name (&tty, buf), 0,
+ driver->major, minor,
+ DEVFS_SPECIAL_CHR, 0);
+ devfs_unregister (handle);
+#endif /* CONFIG_DEVFS_FS */
+}
+
+EXPORT_SYMBOL(tty_register_devfs);
+EXPORT_SYMBOL(tty_unregister_devfs);
+
+/*
* Called by a tty driver to register itself.
*/
int tty_register_driver(struct tty_driver *driver)
{
int error;
+ int i;
if (driver->flags & TTY_DRIVER_INSTALLED)
return 0;
- error = register_chrdev(driver->major, driver->name, &tty_fops);
+ error = devfs_register_chrdev(driver->major, driver->name, &tty_fops);
if (error < 0)
return error;
else if(driver->major == 0)
@@ -1989,6 +2084,10 @@ int tty_register_driver(struct tty_driver *driver)
if (tty_drivers) tty_drivers->prev = driver;
tty_drivers = driver;
+ if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
+ for(i = 0; i < driver->num; i++)
+ tty_register_devfs(driver, 0, driver->minor_start + i);
+ }
proc_tty_register_driver(driver);
return error;
}
@@ -2018,11 +2117,11 @@ int tty_unregister_driver(struct tty_driver *driver)
return -ENOENT;
if (othername == NULL) {
- retval = unregister_chrdev(driver->major, driver->name);
+ retval = devfs_unregister_chrdev(driver->major, driver->name);
if (retval)
return retval;
} else
- register_chrdev(driver->major, othername, &tty_fops);
+ devfs_register_chrdev(driver->major, othername, &tty_fops);
if (driver->prev)
driver->prev->next = driver->next;
@@ -2048,6 +2147,7 @@ int tty_unregister_driver(struct tty_driver *driver)
driver->termios_locked[i] = NULL;
kfree_s(tp, sizeof(struct termios));
}
+ tty_unregister_devfs(driver, driver->minor_start + i);
}
proc_tty_unregister_driver(driver);
return 0;
@@ -2148,6 +2248,13 @@ void __init tty_init(void)
if (tty_register_driver(&dev_syscons_driver))
panic("Couldn't register /dev/console driver\n");
+ /* console calls tty_register_driver() before kmalloc() works.
+ * Thus, we can't devfs_register() then. Do so now, instead.
+ */
+#ifdef CONFIG_VT
+ con_init_devfs();
+#endif
+
#ifdef CONFIG_UNIX98_PTYS
dev_ptmx_driver = dev_tty_driver;
dev_ptmx_driver.driver_name = "/dev/ptmx";
@@ -2163,7 +2270,7 @@ void __init tty_init(void)
#ifdef CONFIG_VT
dev_console_driver = dev_tty_driver;
- dev_console_driver.driver_name = "/dev/tty0";
+ dev_console_driver.driver_name = "/dev/vc/0";
dev_console_driver.name = dev_console_driver.driver_name + 5;
dev_console_driver.major = TTY_MAJOR;
dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM;
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index e099b4ac3..1da60703e 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -21,10 +21,12 @@
* - making it shorter - scr_readw are macros which expand in PRETTY long code
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/tty.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
@@ -32,8 +34,11 @@
#include <linux/vt_kern.h>
#include <linux/console_struct.h>
#include <linux/selection.h>
+#include <linux/kbd_kern.h>
+#include <linux/console.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#undef attr
#undef org
@@ -80,21 +85,34 @@ static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
return file->f_pos;
}
-#define RETURN(x) { enable_bh(CONSOLE_BH); return x; }
+/* We share this temporary buffer with the console write code
+ * so that we can easily avoid touching user space while holding the
+ * console spinlock.
+ */
+extern char con_buf[PAGE_SIZE];
+#define CON_BUF_SIZE PAGE_SIZE
+extern struct semaphore con_buf_sem;
+
static ssize_t
vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
unsigned int currcons = MINOR(inode->i_rdev);
- long p = *ppos;
- long viewed, attr, size, read;
- char *buf0;
+ long pos = *ppos;
+ long viewed, attr, read;
int col, maxcol;
unsigned short *org = NULL;
+ ssize_t ret;
+
+ down(&con_buf_sem);
+
+ /* Select the proper current console and verify
+ * sanity of the situation under the console lock.
+ */
+ spin_lock_irq(&console_lock);
attr = (currcons & 128);
currcons = (currcons & 127);
- disable_bh(CONSOLE_BH);
if (currcons == 0) {
currcons = fg_console;
viewed = 1;
@@ -102,78 +120,143 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
currcons--;
viewed = 0;
}
+ ret = -ENXIO;
if (!vc_cons_allocated(currcons))
- RETURN( -ENXIO );
+ goto unlock_out;
- size = vcs_size(inode);
- if (p < 0 || p > size)
- RETURN( -EINVAL );
- if (count > size - p)
- count = size - p;
-
- buf0 = buf;
- maxcol = video_num_columns;
- if (!attr) {
- org = screen_pos(currcons, p, viewed);
- col = p % maxcol;
- p += maxcol - col;
- while (count-- > 0) {
- put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++);
- if (++col == maxcol) {
- org = screen_pos(currcons, p, viewed);
- col = 0;
- p += maxcol;
- }
- }
- } else {
- if (p < HEADER_SIZE) {
- char header[HEADER_SIZE];
- header[0] = (char) video_num_lines;
- header[1] = (char) video_num_columns;
- getconsxy(currcons, header+2);
- while (p < HEADER_SIZE && count > 0)
- { count--; put_user(header[p++], buf++); }
- }
- p -= HEADER_SIZE;
- col = (p/2) % maxcol;
- if (count > 0) {
- org = screen_pos(currcons, p/2, viewed);
- if ((p & 1) && count > 0) {
- count--;
-#ifdef __BIG_ENDIAN
- put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++);
-#else
- put_user(vcs_scr_readw(currcons, org++) >> 8, buf++);
-#endif
- p++;
+ ret = -EINVAL;
+ if (pos < 0)
+ goto unlock_out;
+ read = 0;
+ ret = 0;
+ while (count) {
+ char *con_buf0, *con_buf_start;
+ long this_round, size;
+ ssize_t orig_count;
+ long p = pos;
+
+ /* Check whether we are above size each round,
+ * as copy_to_user at the end of this loop
+ * could sleep.
+ */
+ size = vcs_size(inode);
+ if (pos >= size)
+ break;
+ if (count > size - pos)
+ count = size - pos;
+
+ this_round = count;
+ if (this_round > CON_BUF_SIZE)
+ this_round = CON_BUF_SIZE;
+
+ /* Perform the whole read into the local con_buf.
+ * Then we can drop the console spinlock and safely
+ * attempt to move it to userspace.
+ */
+
+ con_buf_start = con_buf0 = con_buf;
+ orig_count = this_round;
+ maxcol = video_num_columns;
+ if (!attr) {
+ org = screen_pos(currcons, p, viewed);
+ col = p % maxcol;
+ p += maxcol - col;
+ while (this_round-- > 0) {
+ *con_buf0++ = (vcs_scr_readw(currcons, org++) & 0xff);
if (++col == maxcol) {
- org = screen_pos(currcons, p/2, viewed);
+ org = screen_pos(currcons, p, viewed);
col = 0;
+ p += maxcol;
}
}
- p /= 2;
- p += maxcol - col;
- }
- while (count > 1) {
- put_user(vcs_scr_readw(currcons, org++), (unsigned short *) buf);
- buf += 2;
- count -= 2;
- if (++col == maxcol) {
+ } else {
+ if (p < HEADER_SIZE) {
+ size_t tmp_count;
+
+ con_buf0[0] = (char) video_num_lines;
+ con_buf0[1] = (char) video_num_columns;
+ getconsxy(currcons, con_buf0 + 2);
+
+ con_buf_start += p;
+ this_round += p;
+ if (this_round > CON_BUF_SIZE) {
+ this_round = CON_BUF_SIZE;
+ orig_count = this_round - p;
+ }
+
+ tmp_count = HEADER_SIZE;
+ if (tmp_count > this_round)
+ tmp_count = this_round;
+
+ /* Advance state pointers and move on. */
+ this_round -= tmp_count;
+ p = HEADER_SIZE;
+ con_buf0 = con_buf + HEADER_SIZE;
+ /* If this_round >= 0, then p is even... */
+ } else if (p & 1) {
+ /* Skip first byte for output if start address is odd
+ * Update region sizes up/down depending on free
+ * space in buffer.
+ */
+ con_buf_start++;
+ if (this_round < CON_BUF_SIZE)
+ this_round++;
+ else
+ orig_count--;
+ }
+ if (this_round > 0) {
+ unsigned short *tmp_buf = (unsigned short *)con_buf0;
+
+ p -= HEADER_SIZE;
+ p /= 2;
+ col = p % maxcol;
+
org = screen_pos(currcons, p, viewed);
- col = 0;
- p += maxcol;
+ p += maxcol - col;
+
+ /* Buffer has even length, so we can always copy
+ * character + attribute. We do not copy last byte
+ * to userspace if this_round is odd.
+ */
+ this_round = (this_round + 1) >> 1;
+
+ while (this_round) {
+ *tmp_buf++ = vcs_scr_readw(currcons, org++);
+ this_round --;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
+ }
}
}
- if (count > 0)
-#ifdef __BIG_ENDIAN
- put_user(vcs_scr_readw(currcons, org) >> 8, buf++);
-#else
- put_user(vcs_scr_readw(currcons, org) & 0xff, buf++);
-#endif
+
+ /* Finally, temporarily drop the console lock and push
+ * all the data to userspace from our temporary buffer.
+ */
+
+ spin_unlock_irq(&console_lock);
+ ret = copy_to_user(buf, con_buf_start, orig_count);
+ spin_lock_irq(&console_lock);
+
+ if (ret) {
+ read += (orig_count - ret);
+ ret = -EFAULT;
+ break;
+ }
+ buf += orig_count;
+ pos += orig_count;
+ read += orig_count;
+ count -= orig_count;
}
- read = buf - buf0;
*ppos += read;
- RETURN( read );
+ if (read)
+ ret = read;
+unlock_out:
+ spin_unlock_irq(&console_lock);
+ up(&con_buf_sem);
+ return ret;
}
static ssize_t
@@ -181,15 +264,23 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
unsigned int currcons = MINOR(inode->i_rdev);
- long p = *ppos;
+ long pos = *ppos;
long viewed, attr, size, written;
- const char *buf0;
+ char *con_buf0;
int col, maxcol;
u16 *org0 = NULL, *org = NULL;
+ size_t ret;
+
+ down(&con_buf_sem);
+
+ /* Select the proper current console and verify
+ * sanity of the situation under the console lock.
+ */
+ spin_lock_irq(&console_lock);
attr = (currcons & 128);
currcons = (currcons & 127);
- disable_bh(CONSOLE_BH);
+
if (currcons == 0) {
currcons = fg_console;
viewed = 1;
@@ -197,94 +288,159 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
currcons--;
viewed = 0;
}
+ ret = -ENXIO;
if (!vc_cons_allocated(currcons))
- RETURN( -ENXIO );
+ goto unlock_out;
size = vcs_size(inode);
- if (p < 0 || p > size)
- RETURN( -EINVAL );
- if (count > size - p)
- count = size - p;
-
- buf0 = buf;
- maxcol = video_num_columns;
- if (!attr) {
- org0 = org = screen_pos(currcons, p, viewed);
- col = p % maxcol;
- p += maxcol - col;
- while (count > 0) {
- unsigned char c;
- count--;
- get_user(c, (const unsigned char*)buf++);
- vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
- org++;
- if (++col == maxcol) {
- org = screen_pos(currcons, p, viewed);
- col = 0;
- p += maxcol;
+ ret = -EINVAL;
+ if (pos < 0 || pos > size)
+ goto unlock_out;
+ if (count > size - pos)
+ count = size - pos;
+ written = 0;
+ while (count) {
+ long this_round = count;
+ size_t orig_count;
+ long p;
+
+ if (this_round > CON_BUF_SIZE)
+ this_round = CON_BUF_SIZE;
+
+ /* Temporarily drop the console lock so that we can read
+ * in the write data from userspace safely.
+ */
+ spin_unlock_irq(&console_lock);
+ ret = copy_from_user(con_buf, buf, this_round);
+ spin_lock_irq(&console_lock);
+
+ if (ret) {
+ this_round -= ret;
+ if (!this_round) {
+ /* Abort loop if no data were copied. Otherwise
+ * fail with -EFAULT.
+ */
+ if (written)
+ break;
+ ret = -EFAULT;
+ goto unlock_out;
}
}
- } else {
- if (p < HEADER_SIZE) {
- char header[HEADER_SIZE];
- getconsxy(currcons, header+2);
- while (p < HEADER_SIZE && count > 0)
- { count--; get_user(header[p++], buf++); }
- if (!viewed)
- putconsxy(currcons, header+2);
- }
- p -= HEADER_SIZE;
- col = (p/2) % maxcol;
- if (count > 0) {
- org0 = org = screen_pos(currcons, p/2, viewed);
- if ((p & 1) && count > 0) {
- char c;
- count--;
- get_user(c,buf++);
+
+ /* The vcs_size might have changed while we slept to grab
+ * the user buffer, so recheck.
+ * Return data written up to now on failure.
+ */
+ size = vcs_size(inode);
+ if (pos >= size)
+ break;
+ if (this_round > size - pos)
+ this_round = size - pos;
+
+ /* OK, now actually push the write to the console
+ * under the lock using the local kernel buffer.
+ */
+
+ con_buf0 = con_buf;
+ orig_count = this_round;
+ maxcol = video_num_columns;
+ p = pos;
+ if (!attr) {
+ org0 = org = screen_pos(currcons, p, viewed);
+ col = p % maxcol;
+ p += maxcol - col;
+
+ while (this_round > 0) {
+ unsigned char c = *con_buf0++;
+
+ this_round--;
+ vcs_scr_writew(currcons,
+ (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
+ org++;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
+ }
+ } else {
+ if (p < HEADER_SIZE) {
+ char header[HEADER_SIZE];
+
+ getconsxy(currcons, header + 2);
+ while (p < HEADER_SIZE && this_round > 0) {
+ this_round--;
+ header[p++] = *con_buf0++;
+ }
+ if (!viewed)
+ putconsxy(currcons, header + 2);
+ }
+ p -= HEADER_SIZE;
+ col = (p/2) % maxcol;
+ if (this_round > 0) {
+ org0 = org = screen_pos(currcons, p/2, viewed);
+ if ((p & 1) && this_round > 0) {
+ char c;
+
+ this_round--;
+ c = *con_buf0++;
#ifdef __BIG_ENDIAN
- vcs_scr_writew(currcons, c |
- (vcs_scr_readw(currcons, org) & 0xff00), org);
+ vcs_scr_writew(currcons, c |
+ (vcs_scr_readw(currcons, org) & 0xff00), org);
#else
- vcs_scr_writew(currcons, (c << 8) |
- (vcs_scr_readw(currcons, org) & 0xff), org);
+ vcs_scr_writew(currcons, (c << 8) |
+ (vcs_scr_readw(currcons, org) & 0xff), org);
#endif
- org++;
- p++;
+ org++;
+ p++;
+ if (++col == maxcol) {
+ org = screen_pos(currcons, p/2, viewed);
+ col = 0;
+ }
+ }
+ p /= 2;
+ p += maxcol - col;
+ }
+ while (this_round > 1) {
+ unsigned short w;
+
+ w = get_unaligned(((const unsigned short *)con_buf0));
+ vcs_scr_writew(currcons, w, org++);
+ con_buf0 += 2;
+ this_round -= 2;
if (++col == maxcol) {
- org = screen_pos(currcons, p/2, viewed);
+ org = screen_pos(currcons, p, viewed);
col = 0;
+ p += maxcol;
}
}
- p /= 2;
- p += maxcol - col;
- }
- while (count > 1) {
- unsigned short w;
- get_user(w, (const unsigned short *) buf);
- vcs_scr_writew(currcons, w, org++);
- buf += 2;
- count -= 2;
- if (++col == maxcol) {
- org = screen_pos(currcons, p, viewed);
- col = 0;
- p += maxcol;
- }
- }
- if (count > 0) {
- unsigned char c;
- get_user(c, (const unsigned char*)buf++);
+ if (this_round > 0) {
+ unsigned char c;
+
+ c = *con_buf0++;
#ifdef __BIG_ENDIAN
- vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org);
+ vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org);
#else
- vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
+ vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
#endif
+ }
}
+ count -= orig_count;
+ written += orig_count;
+ buf += orig_count;
+ pos += orig_count;
+ if (org0)
+ update_region(currcons, (unsigned long)(org0), org-org0);
}
- if (org0)
- update_region(currcons, (unsigned long)(org0), org-org0);
- written = buf - buf0;
*ppos += written;
- RETURN( written );
+ ret = written;
+
+unlock_out:
+ spin_unlock_irq(&console_lock);
+
+ up(&con_buf_sem);
+
+ return ret;
}
static int
@@ -303,12 +459,49 @@ static struct file_operations vcs_fops = {
open: vcs_open,
};
+static devfs_handle_t devfs_handle = NULL;
+
+void vcs_make_devfs (unsigned int index, int unregister)
+{
+#ifdef CONFIG_DEVFS_FS
+ char name[8];
+
+ sprintf (name, "a%u", index + 1);
+ if (unregister)
+ {
+ devfs_unregister ( devfs_find_handle (devfs_handle, name + 1, 0, 0, 0,
+ DEVFS_SPECIAL_CHR, 0) );
+ devfs_unregister ( devfs_find_handle (devfs_handle, name, 0, 0, 0,
+ DEVFS_SPECIAL_CHR, 0) );
+ }
+ else
+ {
+ devfs_register (devfs_handle, name + 1, 0, DEVFS_FL_DEFAULT,
+ VCS_MAJOR, index + 1,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+ devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT,
+ VCS_MAJOR, index + 129,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+ }
+#endif /* CONFIG_DEVFS_FS */
+}
+
int __init vcs_init(void)
{
int error;
- error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
+ error = devfs_register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
+
if (error)
printk("unable to get major %d for vcs device", VCS_MAJOR);
+
+ devfs_handle = devfs_mk_dir (NULL, "vcc", 3, NULL);
+ devfs_register (devfs_handle, "0", 1, DEVFS_FL_DEFAULT,
+ VCS_MAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+ devfs_register (devfs_handle, "a", 1, DEVFS_FL_DEFAULT,
+ VCS_MAJOR, 128,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL);
+
return error;
}
diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c
index d10182d29..cfcdbfcde 100644
--- a/drivers/char/videodev.c
+++ b/drivers/char/videodev.c
@@ -229,6 +229,8 @@ static int video_mmap(struct inode * ino, struct file * file,
return -EINVAL;
}
+extern struct file_operations video_fops;
+
/*
* Video For Linux device drivers request registration here.
*/
@@ -239,24 +241,29 @@ int video_register_device(struct video_device *vfd, int type)
int base;
int err;
int end;
+ char *name_base;
switch(type)
{
case VFL_TYPE_GRABBER:
base=0;
end=64;
+ name_base = "video";
break;
case VFL_TYPE_VTX:
base=192;
end=224;
+ name_base = "vtx";
break;
case VFL_TYPE_VBI:
base=224;
end=240;
+ name_base = "vbi";
break;
case VFL_TYPE_RADIO:
base=64;
end=128;
+ name_base = "radio";
break;
default:
return -1;
@@ -266,6 +273,8 @@ int video_register_device(struct video_device *vfd, int type)
{
if(video_device[i]==NULL)
{
+ char name[16];
+
video_device[i]=vfd;
vfd->minor=i;
/* The init call may sleep so we book the slot out
@@ -281,6 +290,12 @@ int video_register_device(struct video_device *vfd, int type)
return err;
}
}
+ sprintf (name, "v4l/%s%d", name_base, i - base);
+ vfd->devfs_handle =
+ devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT,
+ VIDEO_MAJOR, vfd->minor,
+ S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+ &video_fops, NULL);
return 0;
}
}
@@ -295,6 +310,7 @@ void video_unregister_device(struct video_device *vfd)
{
if(video_device[vfd->minor]!=vfd)
panic("vfd: bad unregister");
+ devfs_unregister (vfd->devfs_handle);
video_device[vfd->minor]=NULL;
MOD_DEC_USE_COUNT;
}
@@ -323,7 +339,7 @@ int videodev_init(void)
struct video_init *vfli = video_init_list;
printk(KERN_INFO "Linux video capture interface: v1.00\n");
- if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops))
+ if(devfs_register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops))
{
printk("video_dev: unable to get major %d\n", VIDEO_MAJOR);
return -EIO;
@@ -349,7 +365,7 @@ int init_module(void)
void cleanup_module(void)
{
- unregister_chrdev(VIDEO_MAJOR, "video_capture");
+ devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture");
}
diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c
index 15232f832..b6b2263a0 100644
--- a/drivers/char/vme_scc.c
+++ b/drivers/char/vme_scc.c
@@ -1045,6 +1045,7 @@ static void scc_ch_write (char ch)
*p = ch;
}
+/* The console_lock must be held when we get here. */
static void scc_console_write (struct console *co, const char *str, unsigned count)
{
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 59b204554..8425e64da 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -22,6 +22,7 @@
#include <linux/malloc.h>
#include <linux/major.h>
#include <linux/fs.h>
+#include <linux/console.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -105,12 +106,13 @@ _kd_mksound(unsigned int hz, unsigned int ticks)
{
static struct timer_list sound_timer = { NULL, NULL, 0, 0,
kd_nosound };
-
unsigned int count = 0;
+ unsigned long flags;
if (hz > 20 && hz < 32767)
count = 1193180 / hz;
+ save_flags(flags);
cli();
del_timer(&sound_timer);
if (count) {
@@ -128,7 +130,7 @@ _kd_mksound(unsigned int hz, unsigned int ticks)
}
} else
kd_nosound(0);
- sti();
+ restore_flags(flags);
return;
}
@@ -804,12 +806,10 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* When we actually do the console switch,
* make sure we are atomic with respect to
* other console switches..
- *
- * Damn! Was it difficult to make this clean?
*/
- disable_bh(CONSOLE_BH);
+ spin_lock_irq(&console_lock);
complete_change_console(newvt);
- enable_bh(CONSOLE_BH);
+ spin_unlock_irq(&console_lock);
}
}
diff --git a/drivers/char/zr36120_mem.c b/drivers/char/zr36120_mem.c
index 5e81049eb..082cfee06 100644
--- a/drivers/char/zr36120_mem.c
+++ b/drivers/char/zr36120_mem.c
@@ -35,17 +35,6 @@
/* Memory management functions */
/*******************************/
-inline int __get_order(unsigned long size)
-{
- int order = 0;
- size = (size+PAGE_SIZE-1)/PAGE_SIZE;
- while (size) {
- size /= 2;
- order++;
- }
- return order;
-}
-
void* bmalloc(unsigned long size)
{
void* mem;
@@ -56,7 +45,7 @@ void* bmalloc(unsigned long size)
* The following function got a lot of memory at boottime,
* so we know its always there...
*/
- mem = (void*)__get_free_pages(GFP_USER|GFP_DMA,__get_order(size));
+ mem = (void*)__get_free_pages(GFP_USER|GFP_DMA,get_order(size));
#endif
if (mem) {
unsigned long adr = (unsigned long)mem;
@@ -82,7 +71,7 @@ void bfree(void* mem, unsigned long size)
#ifdef CONFIG_BIGPHYS_AREA
bigphysarea_free_pages(mem);
#else
- free_pages((unsigned long)mem,__get_order(size));
+ free_pages((unsigned long)mem,get_order(size));
#endif
}
}