Skip to content

Commit

Permalink
tty_port: Add a port level carrier detect operation
Browse files Browse the repository at this point in the history
This is the first step to generalising the various pieces of waiting logic
duplicated in all sorts of serial drivers.

Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Alan Cox authored and Linus Torvalds committed Jan 2, 2009
1 parent c9b3976 commit 31f3593
Show file tree
Hide file tree
Showing 19 changed files with 427 additions and 239 deletions.
61 changes: 37 additions & 24 deletions drivers/char/esp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2054,6 +2054,15 @@ static void esp_hangup(struct tty_struct *tty)
wake_up_interruptible(&info->port.open_wait);
}

static int esp_carrier_raised(struct tty_port *port)
{
struct esp_struct *info = container_of(port, struct esp_struct, port);
serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD)
return 1;
return 0;
}

/*
* ------------------------------------------------------------
* esp_open() and friends
Expand All @@ -2066,17 +2075,19 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
int retval;
int do_clocal = 0;
unsigned long flags;
int cd;
struct tty_port *port = &info->port;

/*
* 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->port.flags & ASYNC_CLOSING)) {
if (info->port.flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->port.close_wait);
(port->flags & ASYNC_CLOSING)) {
if (port->flags & ASYNC_CLOSING)
interruptible_sleep_on(&port->close_wait);
#ifdef SERIAL_DO_RESTART
if (info->port.flags & ASYNC_HUP_NOTIFY)
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
Expand All @@ -2091,7 +2102,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
info->port.flags |= ASYNC_NORMAL_ACTIVE;
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

Expand All @@ -2101,20 +2112,20 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
/*
* 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, info->port.count is dropped by one, so that
* this loop, port->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->port.open_wait, &wait);
add_wait_queue(&port->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk(KERN_DEBUG "block_til_ready before block: ttys%d, count = %d\n",
info->line, info->port.count);
info->line, port->count);
#endif
spin_lock_irqsave(&info->lock, flags);
if (!tty_hung_up_p(filp))
info->port.count--;
info->port.blocked_open++;
port->count--;
port->blocked_open++;
while (1) {
if ((tty->termios->c_cflag & CBAUD)) {
unsigned int scratch;
Expand All @@ -2129,9 +2140,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
}
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->port.flags & ASYNC_INITIALIZED)) {
!(port->flags & ASYNC_INITIALIZED)) {
#ifdef SERIAL_DO_RESTART
if (info->port.flags & ASYNC_HUP_NOTIFY)
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
Expand All @@ -2141,11 +2152,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
break;
}

serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD)
do_clocal = 1;
cd = tty_port_carrier_raised(port);

if (!(info->port.flags & ASYNC_CLOSING) &&
if (!(port->flags & ASYNC_CLOSING) &&
(do_clocal))
break;
if (signal_pending(current)) {
Expand All @@ -2154,25 +2163,25 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
}
#ifdef SERIAL_DEBUG_OPEN
printk(KERN_DEBUG "block_til_ready blocking: ttys%d, count = %d\n",
info->line, info->port.count);
info->line, port->count);
#endif
spin_unlock_irqrestore(&info->lock, flags);
schedule();
spin_lock_irqsave(&info->lock, flags);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->port.open_wait, &wait);
remove_wait_queue(&port->open_wait, &wait);
if (!tty_hung_up_p(filp))
info->port.count++;
info->port.blocked_open--;
port->count++;
port->blocked_open--;
spin_unlock_irqrestore(&info->lock, flags);
#ifdef SERIAL_DEBUG_OPEN
printk(KERN_DEBUG "block_til_ready after blocking: ttys%d, count = %d\n",
info->line, info->port.count);
info->line, port->count);
#endif
if (retval)
return retval;
info->port.flags |= ASYNC_NORMAL_ACTIVE;
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

Expand Down Expand Up @@ -2329,6 +2338,10 @@ static const struct tty_operations esp_ops = {
.tiocmset = esp_tiocmset,
};

static const struct tty_port_operations esp_port_ops = {
.esp_carrier_raised,
};

/*
* The serial driver boot-time initialization code!
*/
Expand Down Expand Up @@ -2415,6 +2428,8 @@ static int __init espserial_init(void)
offset = 0;

do {
tty_port_init(&info->port);
info->port.ops = &esp_port_ops;
info->io_port = esp[i] + offset;
info->irq = irq[i];
info->line = (i * 8) + (offset / 8);
Expand All @@ -2437,8 +2452,6 @@ static int __init espserial_init(void)
info->config.flow_off = flow_off;
info->config.pio_threshold = pio_threshold;
info->next_port = ports;
init_waitqueue_head(&info->port.open_wait);
init_waitqueue_head(&info->port.close_wait);
init_waitqueue_head(&info->delta_msr_wait);
init_waitqueue_head(&info->break_wait);
ports = info;
Expand Down
43 changes: 22 additions & 21 deletions drivers/char/generic_serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ void gs_hangup(struct tty_struct *tty)

int gs_block_til_ready(void *port_, struct file * filp)
{
struct gs_port *port = port_;
struct gs_port *gp = port_;
struct tty_port *port = &gp->port;
DECLARE_WAITQUEUE(wait, current);
int retval;
int do_clocal = 0;
Expand All @@ -409,16 +410,16 @@ int gs_block_til_ready(void *port_, struct file * filp)

if (!port) return 0;

tty = port->port.tty;
tty = port->tty;

gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n");
/*
* 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) || port->port.flags & ASYNC_CLOSING) {
interruptible_sleep_on(&port->port.close_wait);
if (port->port.flags & ASYNC_HUP_NOTIFY)
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
interruptible_sleep_on(&port->close_wait);
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
Expand All @@ -432,7 +433,7 @@ int gs_block_til_ready(void *port_, struct file * filp)
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
port->port.flags |= ASYNC_NORMAL_ACTIVE;
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

Expand All @@ -444,34 +445,34 @@ int gs_block_til_ready(void *port_, struct file * filp)
/*
* 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, port->port.count is dropped by one, so that
* this loop, port->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(&port->port.open_wait, &wait);
add_wait_queue(&port->open_wait, &wait);

gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n");
spin_lock_irqsave(&port->driver_lock, flags);
spin_lock_irqsave(&gp->driver_lock, flags);
if (!tty_hung_up_p(filp)) {
port->port.count--;
port->count--;
}
spin_unlock_irqrestore(&port->driver_lock, flags);
port->port.blocked_open++;
spin_unlock_irqrestore(&gp->driver_lock, flags);
port->blocked_open++;
while (1) {
CD = port->rd->get_CD (port);
CD = tty_port_carrier_raised(port);
gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD);
set_current_state (TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(port->port.flags & ASYNC_INITIALIZED)) {
if (port->port.flags & ASYNC_HUP_NOTIFY)
!(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
if (!(port->port.flags & ASYNC_CLOSING) &&
if (!(port->flags & ASYNC_CLOSING) &&
(do_clocal || CD))
break;
gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n",
Expand All @@ -483,17 +484,17 @@ int gs_block_til_ready(void *port_, struct file * filp)
schedule();
}
gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n",
port->port.blocked_open);
port->blocked_open);
set_current_state (TASK_RUNNING);
remove_wait_queue(&port->port.open_wait, &wait);
remove_wait_queue(&port->open_wait, &wait);
if (!tty_hung_up_p(filp)) {
port->port.count++;
port->count++;
}
port->port.blocked_open--;
port->blocked_open--;
if (retval)
return retval;

port->port.flags |= ASYNC_NORMAL_ACTIVE;
port->flags |= ASYNC_NORMAL_ACTIVE;
func_exit ();
return 0;
}
Expand Down
51 changes: 32 additions & 19 deletions drivers/char/isicom.c
Original file line number Diff line number Diff line change
Expand Up @@ -830,20 +830,28 @@ static int isicom_setup_port(struct tty_struct *tty)
return 0;
}

static int isicom_carrier_raised(struct tty_port *port)
{
struct isi_port *ip = container_of(port, struct isi_port, port);
return (ip->status & ISI_DCD)?1 : 0;
}

static int block_til_ready(struct tty_struct *tty, struct file *filp,
struct isi_port *port)
struct isi_port *ip)
{
struct isi_board *card = port->card;
struct isi_board *card = ip->card;
struct tty_port *port = &ip->port;
int do_clocal = 0, retval;
unsigned long flags;
DECLARE_WAITQUEUE(wait, current);
int cd;

/* block if port is in the process of being closed */

if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
pr_dbg("block_til_ready: close in progress.\n");
interruptible_sleep_on(&port->port.close_wait);
if (port->port.flags & ASYNC_HUP_NOTIFY)
interruptible_sleep_on(&port->close_wait);
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
Expand All @@ -854,7 +862,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
pr_dbg("block_til_ready: non-block mode.\n");
port->port.flags |= ASYNC_NORMAL_ACTIVE;
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

Expand All @@ -864,45 +872,45 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
/* block waiting for DCD to be asserted, and while
callout dev is busy */
retval = 0;
add_wait_queue(&port->port.open_wait, &wait);
add_wait_queue(&port->open_wait, &wait);

spin_lock_irqsave(&card->card_lock, flags);
if (!tty_hung_up_p(filp))
port->port.count--;
port->port.blocked_open++;
port->count--;
port->blocked_open++;
spin_unlock_irqrestore(&card->card_lock, flags);

while (1) {
raise_dtr_rts(port);
raise_dtr_rts(ip);

set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) || !(port->port.flags & ASYNC_INITIALIZED)) {
if (port->port.flags & ASYNC_HUP_NOTIFY)
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
if (!(port->port.flags & ASYNC_CLOSING) &&
(do_clocal || (port->status & ISI_DCD))) {
cd = tty_port_carrier_raised(port);
if (!(port->flags & ASYNC_CLOSING) &&
(do_clocal || cd))
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->port.open_wait, &wait);
remove_wait_queue(&port->open_wait, &wait);
spin_lock_irqsave(&card->card_lock, flags);
if (!tty_hung_up_p(filp))
port->port.count++;
port->port.blocked_open--;
port->count++;
port->blocked_open--;
spin_unlock_irqrestore(&card->card_lock, flags);
if (retval)
return retval;
port->port.flags |= ASYNC_NORMAL_ACTIVE;
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

Expand Down Expand Up @@ -1452,6 +1460,10 @@ static const struct tty_operations isicom_ops = {
.break_ctl = isicom_send_break,
};

static const struct tty_port_operations isicom_port_ops = {
.carrier_raised = isicom_carrier_raised,
};

static int __devinit reset_card(struct pci_dev *pdev,
const unsigned int card, unsigned int *signature)
{
Expand Down Expand Up @@ -1794,6 +1806,7 @@ static int __init isicom_init(void)
spin_lock_init(&isi_card[idx].card_lock);
for (channel = 0; channel < 16; channel++, port++) {
tty_port_init(&port->port);
port->port.ops = &isicom_port_ops;
port->magic = ISICOM_MAGIC;
port->card = &isi_card[idx];
port->channel = channel;
Expand Down
Loading

0 comments on commit 31f3593

Please sign in to comment.