diff options
Diffstat (limited to 'drivers/char')
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 } } |