X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fgeneric_serial.c;h=e2a0b6f3d336317ccc842e1ea88a4f3743f08c95;hb=refs%2Fheads%2Fvserver;hp=1027eb7c6e4c8355faa20b4e1016336a040676ff;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 1027eb7c6..e2a0b6f3d 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -26,14 +26,13 @@ #include #include #include +#include +#include #include #include #define DEBUG -static char * tmp_buf; -static DECLARE_MUTEX(tmp_buf_sem); - static int gs_debug; #ifdef DEBUG @@ -44,11 +43,11 @@ static int gs_debug; #define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter %s\n", __FUNCTION__) #define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __FUNCTION__) - -#ifdef NEW_WRITE_LOCKING +#define NEW_WRITE_LOCKING 1 +#if NEW_WRITE_LOCKING #define DECL /* Nothing */ -#define LOCKIT down (& port->port_write_sem); -#define RELEASEIT up (&port->port_write_sem); +#define LOCKIT mutex_lock(& port->port_write_mutex); +#define RELEASEIT mutex_unlock(&port->port_write_mutex); #else #define DECL unsigned long flags; #define LOCKIT save_flags (flags);cli () @@ -57,7 +56,7 @@ static int gs_debug; #define RS_EVENT_WRITE_WAKEUP 1 -MODULE_PARM(gs_debug, "i"); +module_param(gs_debug, int, 0644); void gs_put_char(struct tty_struct * tty, unsigned char ch) @@ -102,7 +101,7 @@ void gs_put_char(struct tty_struct * tty, unsigned char ch) > -3- Other processes that are also trying to do a "write". */ -int gs_write(struct tty_struct * tty, int from_user, +int gs_write(struct tty_struct * tty, const unsigned char *buf, int count) { struct gs_port *port; @@ -123,14 +122,14 @@ int gs_write(struct tty_struct * tty, int from_user, /* get exclusive "write" access to this port (problem 3) */ /* This is not a spinlock because we can have a disk access (page fault) in copy_from_user */ - down (& port->port_write_sem); + mutex_lock(& port->port_write_mutex); while (1) { c = count; /* This is safe because we "OWN" the "head". Noone else can - change the "head": we own the port_write_sem. */ + change the "head": we own the port_write_mutex. */ /* Don't overrun the end of the buffer */ t = SERIAL_XMIT_SIZE - port->xmit_head; if (t < c) c = t; @@ -143,15 +142,8 @@ int gs_write(struct tty_struct * tty, int from_user, /* Can't copy more? break out! */ if (c <= 0) break; - if (from_user) { - if (copy_from_user (port->xmit_buf + port->xmit_head, - buf, c)) { - up (& port->port_write_sem); - return -EFAULT; - } - } else - memcpy (port->xmit_buf + port->xmit_head, buf, c); + memcpy (port->xmit_buf + port->xmit_head, buf, c); port -> xmit_cnt += c; port -> xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE -1); @@ -159,7 +151,7 @@ int gs_write(struct tty_struct * tty, int from_user, count -= c; total += c; } - up (& port->port_write_sem); + mutex_unlock(& port->port_write_mutex); gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n", (port->flags & GS_TX_INTEN)?"enabled": "disabled"); @@ -182,7 +174,7 @@ int gs_write(struct tty_struct * tty, int from_user, > -3- Other processes that are also trying to do a "write". */ -int gs_write(struct tty_struct * tty, int from_user, +int gs_write(struct tty_struct * tty, const unsigned char *buf, int count) { struct gs_port *port; @@ -211,83 +203,39 @@ int gs_write(struct tty_struct * tty, int from_user, if (!tty) return -EIO; port = tty->driver_data; - if (!port || !port->xmit_buf || !tmp_buf) + if (!port || !port->xmit_buf) return -EIO; - save_flags(flags); - if (from_user) { - down(&tmp_buf_sem); - while (1) { - c = count; - - /* This is safe because we "OWN" the "head". Noone else can - change the "head": we own the port_write_sem. */ - /* Don't overrun the end of the buffer */ - t = SERIAL_XMIT_SIZE - port->xmit_head; - if (t < c) c = t; - - /* This is safe because the xmit_cnt can only decrease. This - would increase "t", so we might copy too little chars. */ - /* Don't copy past the "head" of the buffer */ - t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; - if (t < c) c = t; - - /* Can't copy more? break out! */ - if (c <= 0) break; - - c -= copy_from_user(tmp_buf, buf, c); - if (!c) { - if (!total) - total = -EFAULT; - break; - } - cli(); - t = SERIAL_XMIT_SIZE - port->xmit_head; - if (t < c) c = t; - t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; - if (t < c) c = t; - - memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); - port->xmit_head = ((port->xmit_head + c) & - (SERIAL_XMIT_SIZE-1)); - port->xmit_cnt += c; - restore_flags(flags); - buf += c; - count -= c; - total += c; - } - up(&tmp_buf_sem); - } else { - while (1) { - cli(); - c = count; - - /* This is safe because we "OWN" the "head". Noone else can - change the "head": we own the port_write_sem. */ - /* Don't overrun the end of the buffer */ - t = SERIAL_XMIT_SIZE - port->xmit_head; - if (t < c) c = t; - - /* This is safe because the xmit_cnt can only decrease. This - would increase "t", so we might copy too little chars. */ - /* Don't copy past the "head" of the buffer */ - t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; - if (t < c) c = t; - - /* Can't copy more? break out! */ - if (c <= 0) { - restore_flags(flags); - break; - } - memcpy(port->xmit_buf + port->xmit_head, buf, c); - port->xmit_head = ((port->xmit_head + c) & - (SERIAL_XMIT_SIZE-1)); - port->xmit_cnt += c; - restore_flags(flags); - buf += c; - count -= c; - total += c; + local_save_flags(flags); + while (1) { + cli(); + c = count; + + /* This is safe because we "OWN" the "head". Noone else can + change the "head": we own the port_write_mutex. */ + /* Don't overrun the end of the buffer */ + t = SERIAL_XMIT_SIZE - port->xmit_head; + if (t < c) c = t; + + /* This is safe because the xmit_cnt can only decrease. This + would increase "t", so we might copy too little chars. */ + /* Don't copy past the "head" of the buffer */ + t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; + if (t < c) c = t; + + /* Can't copy more? break out! */ + if (c <= 0) { + local_restore_flags(flags); + break; } + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = ((port->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + port->xmit_cnt += c; + local_restore_flags(flags); + buf += c; + count -= c; + total += c; } if (port->xmit_cnt && @@ -329,7 +277,7 @@ int gs_chars_in_buffer(struct tty_struct *tty) } -int gs_real_chars_in_buffer(struct tty_struct *tty) +static int gs_real_chars_in_buffer(struct tty_struct *tty) { struct gs_port *port; func_enter (); @@ -345,7 +293,7 @@ int gs_real_chars_in_buffer(struct tty_struct *tty) } -static int gs_wait_tx_flushed (void * ptr, int timeout) +static int gs_wait_tx_flushed (void * ptr, unsigned long timeout) { struct gs_port *port = ptr; unsigned long end_jiffies; @@ -399,8 +347,7 @@ static int gs_wait_tx_flushed (void * ptr, int timeout) gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies " "(%d chars).\n", jiffies_to_transmit, charsleft); - set_current_state (TASK_INTERRUPTIBLE); - schedule_timeout(jiffies_to_transmit); + msleep_interruptible(jiffies_to_msecs(jiffies_to_transmit)); if (signal_pending (current)) { gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: "); rv = -EINTR; @@ -431,14 +378,12 @@ void gs_flush_buffer(struct tty_struct *tty) if (!port) return; /* XXX Would the write semaphore do? */ - save_flags(flags); cli(); + spin_lock_irqsave (&port->driver_lock, flags); port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - restore_flags(flags); + spin_unlock_irqrestore (&port->driver_lock, 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); func_exit (); } @@ -510,7 +455,7 @@ void gs_start(struct tty_struct * tty) } -void gs_shutdown_port (struct gs_port *port) +static void gs_shutdown_port (struct gs_port *port) { unsigned long flags; @@ -521,8 +466,7 @@ void gs_shutdown_port (struct gs_port *port) if (!(port->flags & ASYNC_INITIALIZED)) return; - save_flags (flags); - cli (); + spin_lock_irqsave(&port->driver_lock, flags); if (port->xmit_buf) { free_page((unsigned long) port->xmit_buf); @@ -535,7 +479,7 @@ void gs_shutdown_port (struct gs_port *port) port->rd->shutdown_port (port); port->flags &= ~ASYNC_INITIALIZED; - restore_flags (flags); + spin_unlock_irqrestore(&port->driver_lock, flags); func_exit(); } @@ -564,29 +508,6 @@ void gs_hangup(struct tty_struct *tty) } -void gs_do_softint(void *private_) -{ - struct gs_port *port = private_; - struct tty_struct *tty; - - func_enter (); - - if (!port) return; - - tty = port->tty; - - if (!tty) return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } - func_exit (); -} - - int gs_block_til_ready(void *port_, struct file * filp) { struct gs_port *port = port_; @@ -595,6 +516,7 @@ int gs_block_til_ready(void *port_, struct file * filp) int do_clocal = 0; int CD; struct tty_struct *tty; + unsigned long flags; func_enter (); @@ -646,10 +568,11 @@ int gs_block_til_ready(void *port_, struct file * filp) add_wait_queue(&port->open_wait, &wait); gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); - cli(); - if (!tty_hung_up_p(filp)) + spin_lock_irqsave(&port->driver_lock, flags); + if (!tty_hung_up_p(filp)) { port->count--; - sti(); + } + spin_unlock_irqrestore(&port->driver_lock, flags); port->blocked_open++; while (1) { CD = port->rd->get_CD (port); @@ -678,8 +601,9 @@ int gs_block_til_ready(void *port_, struct file * filp) port->blocked_open); set_current_state (TASK_RUNNING); remove_wait_queue(&port->open_wait, &wait); - if (!tty_hung_up_p(filp)) + if (!tty_hung_up_p(filp)) { port->count++; + } port->blocked_open--; if (retval) return retval; @@ -694,7 +618,7 @@ void gs_close(struct tty_struct * tty, struct file * filp) { unsigned long flags; struct gs_port *port; - + func_enter (); if (!tty) return; @@ -709,27 +633,29 @@ void gs_close(struct tty_struct * tty, struct file * filp) port->tty = tty; } - save_flags(flags); cli(); + spin_lock_irqsave(&port->driver_lock, flags); if (tty_hung_up_p(filp)) { - restore_flags(flags); - port->rd->hungup (port); + spin_unlock_irqrestore(&port->driver_lock, flags); + if (port->rd->hungup) + port->rd->hungup (port); func_exit (); return; } if ((tty->count == 1) && (port->count != 1)) { - printk(KERN_ERR "gs: gs_close: bad port count;" - " tty->count is 1, port count is %d\n", port->count); + printk(KERN_ERR "gs: gs_close port %p: bad port count;" + " tty->count is 1, port count is %d\n", port, port->count); port->count = 1; } if (--port->count < 0) { - printk(KERN_ERR "gs: gs_close: bad port count: %d\n", port->count); + printk(KERN_ERR "gs: gs_close port %p: bad port count: %d\n", port, port->count); port->count = 0; } + if (port->count) { - gs_dprintk(GS_DEBUG_CLOSE, "gs_close: count: %d\n", port->count); - restore_flags(flags); + gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->count); + spin_unlock_irqrestore(&port->driver_lock, flags); func_exit (); return; } @@ -751,17 +677,18 @@ void gs_close(struct tty_struct * tty, struct file * filp) */ port->rd->disable_rx_interrupts (port); + spin_unlock_irqrestore(&port->driver_lock, flags); /* close has no way of returning "EINTR", so discard return value */ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - gs_wait_tx_flushed (port, port->closing_wait); + gs_wait_tx_flushed (port, port->closing_wait); port->flags &= ~GS_ACTIVE; 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; port->event = 0; @@ -771,31 +698,25 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (port->blocked_open) { if (port->close_delay) { - set_current_state (TASK_INTERRUPTIBLE); - schedule_timeout(port->close_delay); + spin_unlock_irqrestore(&port->driver_lock, flags); + msleep_interruptible(jiffies_to_msecs(port->close_delay)); + spin_lock_irqsave(&port->driver_lock, flags); } wake_up_interruptible(&port->open_wait); } port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED); wake_up_interruptible(&port->close_wait); - restore_flags(flags); func_exit (); } -static unsigned int gs_baudrates[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 -}; - - void gs_set_termios (struct tty_struct * tty, - struct termios * old_termios) + struct ktermios * old_termios) { struct gs_port *port; int baudrate, tmp, rv; - struct termios *tiosp; + struct ktermios *tiosp; func_enter(); @@ -804,6 +725,12 @@ void gs_set_termios (struct tty_struct * tty, port = tty->driver_data; if (!port) return; + if (!port->tty) { + /* This seems to happen when this is called after gs_close. */ + gs_dprintk (GS_DEBUG_TERMIOS, "gs: Odd: port->tty is NULL\n"); + port->tty = tty; + } + tiosp = tty->termios; @@ -811,11 +738,9 @@ void gs_set_termios (struct tty_struct * tty, gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp); } -#if 0 /* This is an optimization that is only allowed for dumb cards */ /* Smart cards require knowledge of iflags and oflags too: that might change hardware cooking mode.... */ -#endif if (old_termios) { if( (tiosp->c_iflag == old_termios->c_iflag) && (tiosp->c_oflag == old_termios->c_oflag) @@ -839,16 +764,8 @@ void gs_set_termios (struct tty_struct * tty, if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n"); } - baudrate = tiosp->c_cflag & CBAUD; - if (baudrate & CBAUDEX) { - baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 4)) - tiosp->c_cflag &= ~CBAUDEX; - else - baudrate += 15; - } + baudrate = tty_get_baud_rate(tty); - baudrate = gs_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { if ( (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; @@ -898,7 +815,7 @@ void gs_set_termios (struct tty_struct * tty, if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&port->gs.open_wait); #endif func_exit(); @@ -911,58 +828,43 @@ void gs_set_termios (struct tty_struct * tty, int gs_init_port(struct gs_port *port) { unsigned long flags; - unsigned long page; - - save_flags (flags); - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - - cli (); /* Don't expect this to make a difference. */ - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - restore_flags (flags); - if (!tmp_buf) { - return -ENOMEM; - } - } + func_enter (); - if (port->flags & ASYNC_INITIALIZED) + if (port->flags & ASYNC_INITIALIZED) { + func_exit (); return 0; - + } if (!port->xmit_buf) { /* We may sleep in get_zeroed_page() */ unsigned long tmp; tmp = get_zeroed_page(GFP_KERNEL); - - /* Spinlock? */ - cli (); + spin_lock_irqsave (&port->driver_lock, flags); if (port->xmit_buf) free_page (tmp); else port->xmit_buf = (unsigned char *) tmp; - restore_flags (flags); - - if (!port->xmit_buf) + spin_unlock_irqrestore(&port->driver_lock, flags); + if (!port->xmit_buf) { + func_exit (); return -ENOMEM; + } } - cli(); - + spin_lock_irqsave (&port->driver_lock, flags); if (port->tty) clear_bit(TTY_IO_ERROR, &port->tty->flags); - + mutex_init(&port->port_write_mutex); port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - + spin_unlock_irqrestore(&port->driver_lock, flags); gs_set_termios(port->tty, NULL); - + spin_lock_irqsave (&port->driver_lock, flags); port->flags |= ASYNC_INITIALIZED; port->flags &= ~GS_TX_INTEN; - restore_flags(flags); + spin_unlock_irqrestore(&port->driver_lock, flags); + func_exit (); return 0; } @@ -1033,13 +935,15 @@ int gs_getserial(struct gs_port *port, struct serial_struct __user *sp) void gs_got_break(struct gs_port *port) { + func_enter (); + + tty_insert_flip_char(port->tty, 0, TTY_BREAK); + tty_schedule_flip(port->tty); if (port->flags & ASYNC_SAK) { do_SAK (port->tty); } - *(port->tty->flip.flag_buf_ptr) = TTY_BREAK; - port->tty->flip.flag_buf_ptr++; - port->tty->flip.char_buf_ptr++; - port->tty->flip.count++; + + func_exit (); } @@ -1052,7 +956,6 @@ EXPORT_SYMBOL(gs_flush_chars); EXPORT_SYMBOL(gs_stop); EXPORT_SYMBOL(gs_start); EXPORT_SYMBOL(gs_hangup); -EXPORT_SYMBOL(gs_do_softint); EXPORT_SYMBOL(gs_block_til_ready); EXPORT_SYMBOL(gs_close); EXPORT_SYMBOL(gs_set_termios);