* ever possible.
*/
-#include <linux/config.h>
+#include <linux/delay.h>
#undef SERIAL_PARANOIA_CHECK
#define SERIAL_DO_RESTART
/* 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", \
tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s)
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/bitops.h>
#include <asm/setup.h>
#include <asm/system.h>
#include <asm/irq.h>
-#include <asm/bitops.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
-#ifdef SERIAL_INLINE
-#define _INLINE_ inline
-#endif
-
+#define custom amiga_custom
static char *serial_name = "Amiga-builtin serial driver";
static struct tty_driver *serial_driver;
static unsigned char current_ctl_bits;
-static void change_speed(struct async_struct *info, struct termios *old);
+static void change_speed(struct async_struct *info, struct ktermios *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))
-
-#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);
+#define NR_PORTS ARRAY_SIZE(rs_table)
#include <asm/uaccess.h>
* 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)
+static void rs_sched_event(struct async_struct *info,
+ int event)
{
info->event |= 1 << event;
tasklet_schedule(&info->tlet);
}
-static _INLINE_ void receive_chars(struct async_struct *info)
+static void receive_chars(struct async_struct *info)
{
int status;
int serdatr;
struct tty_struct *tty = info->tty;
- unsigned char ch;
+ unsigned char ch, flag;
struct async_icount *icount;
+ int oe = 0;
icount = &info->state->icount;
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;
+ flag = TTY_NORMAL;
/*
* We don't handle parity or frame errors - but I have left
* should be ignored.
*/
if (status & info->ignore_status_mask)
- goto ignore_char;
+ goto out;
status &= info->read_status_mask;
#ifdef SERIAL_DEBUG_INTR
printk("handling break....");
#endif
- *tty->flip.flag_buf_ptr = TTY_BREAK;
+ flag = TTY_BREAK;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
} else if (status & UART_LSR_PE)
- *tty->flip.flag_buf_ptr = TTY_PARITY;
+ flag = TTY_PARITY;
else if (status & UART_LSR_FE)
- *tty->flip.flag_buf_ptr = TTY_FRAME;
+ flag = 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;
- }
+ oe = 1;
}
}
- tty->flip.flag_buf_ptr++;
- tty->flip.char_buf_ptr++;
- tty->flip.count++;
- ignore_char:
-
+ tty_insert_flip_char(tty, ch, flag);
+ if (oe == 1)
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
tty_flip_buffer_push(tty);
+out:
+ return;
}
-static _INLINE_ void transmit_chars(struct async_struct *info)
+static void transmit_chars(struct async_struct *info)
{
custom.intreq = IF_TBE;
mb();
}
}
-static _INLINE_ void check_modem_status(struct async_struct *info)
+static void check_modem_status(struct async_struct *info)
{
unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
unsigned char dstatus;
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,
+ printk("ttyS%d CD now %s...", info->line,
(!(status & SER_DCD)) ? "on" : "off");
#endif
if (!(status & SER_DCD))
}
}
-static irqreturn_t ser_vbl_int( int irq, void *data, struct pt_regs *regs)
+static irqreturn_t ser_vbl_int( int irq, void *data)
{
/* vbl is just a periodic interrupt we tie into to update modem status */
struct async_struct * info = IRQ_ports;
return IRQ_HANDLED;
}
-static irqreturn_t ser_rx_int(int irq, void *dev_id, struct pt_regs * regs)
+static irqreturn_t ser_rx_int(int irq, void *dev_id)
{
struct async_struct * info;
return IRQ_HANDLED;
}
-static irqreturn_t ser_tx_int(int irq, void *dev_id, struct pt_regs * regs)
+static irqreturn_t ser_tx_int(int irq, void *dev_id)
{
struct async_struct * info;
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);
+ tty_wakeup(tty);
wake_up_interruptible(&tty->write_wait);
}
}
/*
* and set the speed of the serial port
*/
- change_speed(info, 0);
+ change_speed(info, NULL);
info->flags |= ASYNC_INITIALIZED;
local_irq_restore(flags);
if (info->xmit.buf) {
free_page((unsigned long) info->xmit.buf);
- info->xmit.buf = 0;
+ info->xmit.buf = NULL;
}
info->IER = 0;
* the specified baud rate for a serial port.
*/
static void change_speed(struct async_struct *info,
- struct termios *old_termios)
+ struct ktermios *old_termios)
{
int quot = 0, baud_base, baud;
unsigned cflag, cval = 0;
static void rs_put_char(struct tty_struct *tty, unsigned char ch)
{
- struct async_struct *info = (struct async_struct *)tty->driver_data;
+ struct async_struct *info;
unsigned long flags;
+ if (!tty)
+ return;
+
+ info = tty->driver_data;
+
if (serial_paranoia_check(info, tty->name, "rs_put_char"))
return;
- if (!tty || !info->xmit.buf)
+ if (!info->xmit.buf)
return;
local_irq_save(flags);
local_irq_restore(flags);
}
-static int rs_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
+static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count)
{
int c, ret = 0;
- struct async_struct *info = (struct async_struct *)tty->driver_data;
+ struct async_struct *info;
unsigned long flags;
+ if (!tty)
+ return 0;
+
+ info = tty->driver_data;
+
if (serial_paranoia_check(info, tty->name, "rs_write"))
return 0;
- if (!tty || !info->xmit.buf || !tmp_buf)
+ if (!info->xmit.buf)
return 0;
local_save_flags(flags);
- if (from_user) {
- down(&tmp_buf_sem);
- while (1) {
- int c1;
- c = CIRC_SPACE_TO_END(info->xmit.head,
- info->xmit.tail,
- SERIAL_XMIT_SIZE);
- if (count < c)
- c = count;
-
- c -= copy_from_user(tmp_buf, buf, c);
- if (!c) {
- if (!ret)
- ret = -EFAULT;
- break;
- }
- local_irq_disable();
- c1 = CIRC_SPACE_TO_END(info->xmit.head,
- info->xmit.tail,
- SERIAL_XMIT_SIZE);
- if (c1 < c)
- c = c1;
- memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
- info->xmit.head = ((info->xmit.head + c) &
- (SERIAL_XMIT_SIZE-1));
- local_irq_restore(flags);
- buf += c;
- count -= c;
- ret += c;
- }
- up(&tmp_buf_sem);
- } else {
- local_irq_disable();
- while (1) {
- c = CIRC_SPACE_TO_END(info->xmit.head,
- info->xmit.tail,
- SERIAL_XMIT_SIZE);
- if (count < c)
- c = count;
- if (c <= 0) {
- break;
- }
- memcpy(info->xmit.buf + info->xmit.head, buf, c);
- info->xmit.head = ((info->xmit.head + c) &
- (SERIAL_XMIT_SIZE-1));
- buf += c;
- count -= c;
- ret += c;
+ local_irq_disable();
+ while (1) {
+ c = CIRC_SPACE_TO_END(info->xmit.head,
+ info->xmit.tail,
+ SERIAL_XMIT_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0) {
+ break;
}
- local_irq_restore(flags);
+ memcpy(info->xmit.buf + info->xmit.head, buf, c);
+ info->xmit.head = ((info->xmit.head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ buf += c;
+ count -= c;
+ ret += c;
}
+ local_irq_restore(flags);
+
if (info->xmit.head != info->xmit.tail
&& !tty->stopped
&& !tty->hw_stopped
info->xmit.head = info->xmit.tail = 0;
local_irq_restore(flags);
wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
+ tty_wakeup(tty);
}
/*
*/
static int get_serial_info(struct async_struct * info,
- struct serial_struct * retinfo)
+ struct serial_struct __user * retinfo)
{
struct serial_struct tmp;
struct serial_state *state = info->state;
}
static int set_serial_info(struct async_struct * info,
- struct serial_struct * new_info)
+ struct serial_struct __user * new_info)
{
struct serial_struct new_serial;
struct serial_state old_state, *state;
info->tty->alt_speed = 230400;
if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
info->tty->alt_speed = 460800;
- change_speed(info, 0);
+ change_speed(info, NULL);
}
} else
retval = startup(info);
* 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)
+static int get_lsr_info(struct async_struct * info, unsigned int __user *value)
{
unsigned char status;
unsigned int result;
struct async_struct * info = (struct async_struct *)tty->driver_data;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct icount;
+ void __user *argp = (void __user *)arg;
unsigned long flags;
if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
switch (cmd) {
case TIOCGSERIAL:
- return get_serial_info(info,
- (struct serial_struct *) arg);
+ return get_serial_info(info, argp);
case TIOCSSERIAL:
- return set_serial_info(info,
- (struct serial_struct *) arg);
+ return set_serial_info(info, argp);
case TIOCSERCONFIG:
return 0;
case TIOCSERGETLSR: /* Get line status register */
- return get_lsr_info(info, (unsigned int *) arg);
+ return get_lsr_info(info, argp);
case TIOCSERGSTRUCT:
- if (copy_to_user((struct async_struct *) arg,
+ if (copy_to_user(argp,
info, sizeof(struct async_struct)))
return -EFAULT;
return 0;
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun;
- if (copy_to_user((void *)arg, &icount, sizeof(icount)))
+ if (copy_to_user(argp, &icount, sizeof(icount)))
return -EFAULT;
return 0;
case TIOCSERGWILD:
return 0;
}
-static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
shutdown(info);
if (tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
- if (tty->ldisc.flush_buffer)
- tty->ldisc.flush_buffer(tty);
+
+ tty_ldisc_flush(tty);
tty->closing = 0;
info->event = 0;
- info->tty = 0;
+ info->tty = NULL;
if (info->blocked_open) {
if (info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
if (char_time == 0)
char_time = 1;
if (timeout)
- char_time = MIN(char_time, timeout);
+ char_time = min_t(unsigned long, 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
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
#endif
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
info->event = 0;
state->count = 0;
info->flags &= ~ASYNC_NORMAL_ACTIVE;
- info->tty = 0;
+ info->tty = NULL;
wake_up_interruptible(&info->open_wait);
}
{
struct async_struct *info;
int retval, line;
- unsigned long page;
line = tty->index;
if ((line < 0) || (line >= NR_PORTS)) {
#endif
info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
- if (!tmp_buf) {
- page = get_zeroed_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
*/
info->magic = SERIAL_MAGIC;
info->flags = state->flags;
info->quot = 0;
- info->tty = 0;
+ info->tty = NULL;
}
local_irq_save(flags);
status = ciab.pra;
* number, and identifies which options were configured into this
* driver.
*/
-static _INLINE_ void show_serial_version(void)
+static 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);
-
-
-static struct tty_operations serial_ops = {
+static const struct tty_operations serial_ops = {
.open = rs_open,
.close = rs_close,
.write = rs_write,
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",
+ printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n",
state->line);
/* Hardware set up */
/* 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);
+ request_irq(IRQ_AMIGA_RBF, ser_rx_int, IRQF_DISABLED, "serial RX", state);
/* turn off Rx and Tx interrupts */
custom.intena = IF_RBF | IF_TBE;
kfree(info);
}
- if (tmp_buf) {
- free_page((unsigned long) tmp_buf);
- tmp_buf = NULL;
- }
-
release_mem_region(CUSTOM_PHYSADDR+0x30, 4);
}