#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/serial.h>
+#include <linux/smp_lock.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
/* card->lock MUST NOT be held */
-static void isicom_raise_dtr_rts(struct tty_port *port)
+static void isicom_dtr_rts(struct tty_port *port, int on)
{
struct isi_port *ip = container_of(port, struct isi_port, port);
struct isi_board *card = ip->card;
if (!lock_card(card))
return;
- outw(0x8000 | (channel << card->shift_count) | 0x02, base);
- outw(0x0f04, base);
- InterruptTheCard(base);
- ip->status |= (ISI_DTR | ISI_RTS);
+ if (on) {
+ outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+ outw(0x0f04, base);
+ InterruptTheCard(base);
+ ip->status |= (ISI_DTR | ISI_RTS);
+ } else {
+ outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+ outw(0x0C04, base);
+ InterruptTheCard(base);
+ ip->status &= ~(ISI_DTR | ISI_RTS);
+ }
unlock_card(card);
}
{
int channel;
struct isi_port *port;
- unsigned long flags;
- spin_lock_irqsave(&bp->card_lock, flags);
- if (bp->status & BOARD_ACTIVE) {
- spin_unlock_irqrestore(&bp->card_lock, flags);
- return;
+ bp->count++;
+ if (!(bp->status & BOARD_INIT)) {
+ port = bp->ports;
+ for (channel = 0; channel < bp->port_count; channel++, port++)
+ drop_dtr_rts(port);
}
- port = bp->ports;
- bp->status |= BOARD_ACTIVE;
- for (channel = 0; channel < bp->port_count; channel++, port++)
- drop_dtr_rts(port);
- spin_unlock_irqrestore(&bp->card_lock, flags);
+ bp->status |= BOARD_ACTIVE | BOARD_INIT;
}
-static int isicom_setup_port(struct tty_struct *tty)
+/* Activate and thus setup board are protected from races against shutdown
+ by the tty_port mutex */
+
+static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
{
- struct isi_port *port = tty->driver_data;
+ struct isi_port *port = container_of(tport, struct isi_port, port);
struct isi_board *card = port->card;
unsigned long flags;
- if (port->port.flags & ASYNC_INITIALIZED)
- return 0;
- if (tty_port_alloc_xmit_buf(&port->port) < 0)
+ if (tty_port_alloc_xmit_buf(tport) < 0)
return -ENOMEM;
spin_lock_irqsave(&card->card_lock, flags);
- clear_bit(TTY_IO_ERROR, &tty->flags);
- if (port->port.count == 1)
- card->count++;
+ isicom_setup_board(card);
port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);
InterruptTheCard(card->base);
}
-
isicom_config_port(tty);
- port->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&card->card_lock, flags);
return 0;
return (ip->status & ISI_DCD)?1 : 0;
}
-static int block_til_ready(struct tty_struct *tty, struct file *filp,
- struct isi_port *ip)
-{
- 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->flags & ASYNC_CLOSING) {
- pr_dbg("block_til_ready: close in progress.\n");
- interruptible_sleep_on(&port->close_wait);
- if (port->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
- }
-
- /* if non-blocking mode is set ... */
-
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- pr_dbg("block_til_ready: non-block mode.\n");
- port->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
- }
-
- if (C_CLOCAL(tty))
- do_clocal = 1;
-
- /* block waiting for DCD to be asserted, and while
- callout dev is busy */
- retval = 0;
- add_wait_queue(&port->open_wait, &wait);
-
- spin_lock_irqsave(&port->lock, flags);
- if (!tty_hung_up_p(filp))
- port->count--;
- port->blocked_open++;
- spin_unlock_irqrestore(&port->lock, flags);
-
- while (1) {
- tty_port_raise_dtr_rts(port);
-
- set_current_state(TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
- if (port->flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
- break;
- }
- 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->open_wait, &wait);
- spin_lock_irqsave(&port->lock, flags);
- if (!tty_hung_up_p(filp))
- port->count++;
- port->blocked_open--;
- if (retval == 0)
- port->flags |= ASYNC_NORMAL_ACTIVE;
- spin_unlock_irqrestore(&port->lock, flags);
- return 0;
-}
-
-static int isicom_open(struct tty_struct *tty, struct file *filp)
+static struct tty_port *isicom_find_port(struct tty_struct *tty)
{
struct isi_port *port;
struct isi_board *card;
unsigned int board;
- int error, line;
+ int line = tty->index;
- line = tty->index;
if (line < 0 || line > PORT_COUNT-1)
- return -ENODEV;
+ return NULL;
board = BOARD(line);
card = &isi_card[board];
if (!(card->status & FIRMWARE_LOADED))
- return -ENODEV;
+ return NULL;
/* open on a port greater than the port count for the card !!! */
if (line > ((board * 16) + card->port_count - 1))
- return -ENODEV;
+ return NULL;
port = &isi_ports[line];
if (isicom_paranoia_check(port, tty->name, "isicom_open"))
- return -ENODEV;
+ return NULL;
- isicom_setup_board(card);
-
- port->port.count++;
- tty->driver_data = port;
- tty_port_tty_set(&port->port, tty);
- error = isicom_setup_port(tty);
- if (error == 0)
- error = block_til_ready(tty, filp, port);
- return error;
+ return &port->port;
}
-/* close et all */
-
-static inline void isicom_shutdown_board(struct isi_board *bp)
+static int isicom_open(struct tty_struct *tty, struct file *filp)
{
- if (bp->status & BOARD_ACTIVE)
- bp->status &= ~BOARD_ACTIVE;
+ struct isi_port *port;
+ struct isi_board *card;
+ struct tty_port *tport;
+
+ tport = isicom_find_port(tty);
+ if (tport == NULL)
+ return -ENODEV;
+ port = container_of(tport, struct isi_port, port);
+ card = &isi_card[BOARD(tty->index)];
+
+ return tty_port_open(tport, tty, filp);
}
+/* close et all */
+
/* card->lock HAS to be held */
static void isicom_shutdown_port(struct isi_port *port)
{
struct isi_board *card = port->card;
- struct tty_struct *tty;
-
- tty = tty_port_tty_get(&port->port);
-
- if (!(port->port.flags & ASYNC_INITIALIZED)) {
- tty_kref_put(tty);
- return;
- }
-
- tty_port_free_xmit_buf(&port->port);
- port->port.flags &= ~ASYNC_INITIALIZED;
- /* 3rd October 2000 : Vinayak P Risbud */
- tty_port_tty_set(&port->port, NULL);
-
- /*Fix done by Anil .S on 30-04-2001
- remote login through isi port has dtr toggle problem
- due to which the carrier drops before the password prompt
- appears on the remote end. Now we drop the dtr only if the
- HUPCL(Hangup on close) flag is set for the tty*/
-
- if (C_HUPCL(tty))
- /* drop dtr on this port */
- drop_dtr(port);
-
- /* any other port uninits */
- if (tty)
- set_bit(TTY_IO_ERROR, &tty->flags);
if (--card->count < 0) {
pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n",
card->base, card->count);
card->count = 0;
}
-
- /* last port was closed, shutdown that boad too */
- if (C_HUPCL(tty)) {
- if (!card->count)
- isicom_shutdown_board(card);
- }
+ /* last port was closed, shutdown that board too */
+ if (!card->count)
+ card->status &= BOARD_ACTIVE;
}
static void isicom_flush_buffer(struct tty_struct *tty)
tty_wakeup(tty);
}
-static void isicom_close(struct tty_struct *tty, struct file *filp)
+static void isicom_shutdown(struct tty_port *port)
{
- struct isi_port *port = tty->driver_data;
- struct isi_board *card;
+ struct isi_port *ip = container_of(port, struct isi_port, port);
+ struct isi_board *card = ip->card;
unsigned long flags;
- if (!port)
- return;
- card = port->card;
- if (isicom_paranoia_check(port, tty->name, "isicom_close"))
- return;
-
- pr_dbg("Close start!!!.\n");
-
- spin_lock_irqsave(&port->port.lock, flags);
- if (tty_hung_up_p(filp)) {
- spin_unlock_irqrestore(&port->port.lock, flags);
- return;
- }
-
- if (tty->count == 1 && port->port.count != 1) {
- printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port "
- "count tty->count = 1 port count = %d.\n",
- card->base, port->port.count);
- port->port.count = 1;
- }
- if (--port->port.count < 0) {
- printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port "
- "count for channel%d = %d", card->base, port->channel,
- port->port.count);
- port->port.count = 0;
- }
-
- if (port->port.count) {
- spin_unlock_irqrestore(&port->port.lock, flags);
- return;
- }
- port->port.flags |= ASYNC_CLOSING;
- tty->closing = 1;
- spin_unlock_irqrestore(&port->port.lock, flags);
-
- if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, port->port.closing_wait);
/* indicate to the card that no more data can be received
on this port */
spin_lock_irqsave(&card->card_lock, flags);
- if (port->port.flags & ASYNC_INITIALIZED) {
- card->port_status &= ~(1 << port->channel);
- outw(card->port_status, card->base + 0x02);
- }
- isicom_shutdown_port(port);
+ card->port_status &= ~(1 << ip->channel);
+ outw(card->port_status, card->base + 0x02);
+ isicom_shutdown_port(ip);
spin_unlock_irqrestore(&card->card_lock, flags);
+ tty_port_free_xmit_buf(port);
+}
- isicom_flush_buffer(tty);
- tty_ldisc_flush(tty);
-
- spin_lock_irqsave(&port->port.lock, flags);
- tty->closing = 0;
-
- if (port->port.blocked_open) {
- spin_unlock_irqrestore(&port->port.lock, flags);
- if (port->port.close_delay) {
- pr_dbg("scheduling until time out.\n");
- msleep_interruptible(
- jiffies_to_msecs(port->port.close_delay));
- }
- spin_lock_irqsave(&port->port.lock, flags);
- wake_up_interruptible(&port->port.open_wait);
- }
- port->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
- wake_up_interruptible(&port->port.close_wait);
- spin_unlock_irqrestore(&port->port.lock, flags);
+static void isicom_close(struct tty_struct *tty, struct file *filp)
+{
+ struct isi_port *ip = tty->driver_data;
+ struct tty_port *port = &ip->port;
+ if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
+ return;
+ tty_port_close(port, tty, filp);
}
/* write et all */
static void isicom_hangup(struct tty_struct *tty)
{
struct isi_port *port = tty->driver_data;
- unsigned long flags;
if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
return;
-
- spin_lock_irqsave(&port->card->card_lock, flags);
- isicom_shutdown_port(port);
- spin_unlock_irqrestore(&port->card->card_lock, flags);
-
tty_port_hangup(&port->port);
}
static const struct tty_port_operations isicom_port_ops = {
.carrier_raised = isicom_carrier_raised,
- .raise_dtr_rts = isicom_raise_dtr_rts,
+ .dtr_rts = isicom_dtr_rts,
+ .activate = isicom_activate,
+ .shutdown = isicom_shutdown,
};
static int __devinit reset_card(struct pci_dev *pdev,
status = inw(base + 0x4);
if (status != 0) {
dev_warn(&pdev->dev, "Card%d rejected load header:\n"
- KERN_WARNING "Address:0x%x\n"
- KERN_WARNING "Count:0x%x\n"
- KERN_WARNING "Status:0x%x\n",
- index + 1, frame->addr, frame->count, status);
+ "Address:0x%x\n"
+ "Count:0x%x\n"
+ "Status:0x%x\n",
+ index + 1, frame->addr, frame->count, status);
goto errrelfw;
}
outsw(base, frame->data, word_count);
status = inw(base + 0x4);
if (status != 0) {
dev_warn(&pdev->dev, "Card%d rejected verify header:\n"
- KERN_WARNING "Address:0x%x\n"
- KERN_WARNING "Count:0x%x\n"
- KERN_WARNING "Status: 0x%x\n",
- index + 1, frame->addr, frame->count, status);
+ "Address:0x%x\n"
+ "Count:0x%x\n"
+ "Status: 0x%x\n",
+ index + 1, frame->addr, frame->count, status);
goto errrelfw;
}
static int __devinit isicom_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- unsigned int signature, index;
+ unsigned int uninitialized_var(signature), index;
int retval = -EPERM;
struct isi_board *board = NULL;