* Original driver code supplied by Multi-Tech
*
* Changes
- * 1/9/98 alan@redhat.com Merge to 2.0.x kernel tree
+ * 1/9/98 alan@lxorguk.ukuu.org.uk
+ * Merge to 2.0.x kernel tree
* Obtain and use official major/minors
* Loader switched to a misc device
* (fixed range check bug as a side effect)
* Printk clean up
- * 9/12/98 alan@redhat.com Rough port to 2.1.x
+ * 9/12/98 alan@lxorguk.ukuu.org.uk
+ * Rough port to 2.1.x
*
* 10/6/99 sameer Merged the ISA and PCI drivers to
* a new unified driver.
#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 inline void raise_dtr_rts(struct isi_port *port)
+
+static void isicom_dtr_rts(struct tty_port *port, int on)
{
- struct isi_board *card = port->card;
+ struct isi_port *ip = container_of(port, struct isi_port, port);
+ struct isi_board *card = ip->card;
unsigned long base = card->base;
- u16 channel = port->channel;
+ u16 channel = ip->channel;
if (!lock_card(card))
return;
- outw(0x8000 | (channel << card->shift_count) | 0x02, base);
- outw(0x0f04, base);
- InterruptTheCard(base);
- port->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);
}
return 0;
}
-static int block_til_ready(struct tty_struct *tty, struct file *filp,
- struct isi_port *port)
+static int isicom_carrier_raised(struct tty_port *port)
{
- struct isi_board *card = port->card;
- int do_clocal = 0, retval;
- unsigned long flags;
- DECLARE_WAITQUEUE(wait, current);
-
- /* block if port is in the process of being closed */
-
- if (tty_hung_up_p(filp) || port->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)
- 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->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->port.open_wait, &wait);
-
- spin_lock_irqsave(&card->card_lock, flags);
- if (!tty_hung_up_p(filp))
- port->port.count--;
- port->port.blocked_open++;
- spin_unlock_irqrestore(&card->card_lock, flags);
-
- while (1) {
- raise_dtr_rts(port);
-
- set_current_state(TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp) || !(port->port.flags & ASYNC_INITIALIZED)) {
- if (port->port.flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
- break;
- }
- if (!(port->port.flags & ASYNC_CLOSING) &&
- (do_clocal || (port->status & ISI_DCD))) {
- break;
- }
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- schedule();
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->port.open_wait, &wait);
- spin_lock_irqsave(&card->card_lock, flags);
- if (!tty_hung_up_p(filp))
- port->port.count++;
- port->port.blocked_open--;
- spin_unlock_irqrestore(&card->card_lock, flags);
- if (retval)
- return retval;
- port->port.flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
+ struct isi_port *ip = container_of(port, struct isi_port, port);
+ return (ip->status & ISI_DCD)?1 : 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;
+ return &port->port;
+}
+
+static int isicom_open(struct tty_struct *tty, struct file *filp)
+{
+ struct isi_port *port;
+ struct isi_board *card;
+ struct tty_port *tport;
+ int error = 0;
+
+ tport = isicom_find_port(tty);
+ if (tport == NULL)
+ return -ENODEV;
+ port = container_of(tport, struct isi_port, port);
+ card = &isi_card[BOARD(tty->index)];
isicom_setup_board(card);
+ /* FIXME: locking on port.count etc */
port->port.count++;
tty->driver_data = port;
tty_port_tty_set(&port->port, tty);
- error = isicom_setup_port(tty);
+ /* FIXME: Locking on Initialized flag */
+ if (!test_bit(ASYNCB_INITIALIZED, &tport->flags))
+ error = isicom_setup_port(tty);
if (error == 0)
- error = block_til_ready(tty, filp, port);
+ error = tty_port_block_til_ready(&port->port, tty, filp);
return error;
}
if (!card->count)
isicom_shutdown_board(card);
}
+ tty_kref_put(tty);
}
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_close_port(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(&card->card_lock, flags);
- if (tty_hung_up_p(filp)) {
- spin_unlock_irqrestore(&card->card_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(&card->card_lock, flags);
- return;
- }
- port->port.flags |= ASYNC_CLOSING;
- tty->closing = 1;
- spin_unlock_irqrestore(&card->card_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);
+ if (port->flags & ASYNC_INITIALIZED) {
+ card->port_status &= ~(1 << ip->channel);
outw(card->port_status, card->base + 0x02);
}
- isicom_shutdown_port(port);
+ isicom_shutdown_port(ip);
spin_unlock_irqrestore(&card->card_lock, flags);
+}
- isicom_flush_buffer(tty);
- tty_ldisc_flush(tty);
-
- spin_lock_irqsave(&card->card_lock, flags);
- tty->closing = 0;
+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;
- if (port->port.blocked_open) {
- spin_unlock_irqrestore(&card->card_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(&card->card_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(&card->card_lock, flags);
+ if (tty_port_close_start(port, tty, filp) == 0)
+ return;
+ isicom_close_port(port);
+ isicom_flush_buffer(tty);
+ tty_port_close_end(port, tty);
}
/* write et all */
isicom_shutdown_port(port);
spin_unlock_irqrestore(&port->card->card_lock, flags);
- port->port.count = 0;
- port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- tty_port_tty_set(&port->port, NULL);
- wake_up_interruptible(&port->port.open_wait);
+ tty_port_hangup(&port->port);
}
.break_ctl = isicom_send_break,
};
+static const struct tty_port_operations isicom_port_ops = {
+ .carrier_raised = isicom_carrier_raised,
+ .dtr_rts = isicom_dtr_rts,
+};
+
static int __devinit reset_card(struct pci_dev *pdev,
const unsigned int card, unsigned int *signature)
{
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;
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;