#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
+#include <linux/sched.h>
#include <linux/serial.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
char tmp[P_BUF_SIZE]; \
snprintf(tmp, sizeof(tmp), ##args); \
printk(_err_flag_ "[%d] %s(): %s\n", __LINE__, \
- __FUNCTION__, tmp); \
+ __func__, tmp); \
} while (0)
#define DBG1(args...) D_(0x01, ##args)
F32_8 = 8192, /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */
};
+/* Initialization states a card can be in */
+enum card_state {
+ NOZOMI_STATE_UKNOWN = 0,
+ NOZOMI_STATE_ENABLED = 1, /* pci device enabled */
+ NOZOMI_STATE_ALLOCATED = 2, /* config setup done */
+ NOZOMI_STATE_READY = 3, /* flowcontrols received */
+};
+
/* Two different toggle channels exist */
enum channel_type {
CH_A = 0,
/* This holds all information that is needed regarding a port */
struct port {
+ struct tty_port port;
u8 update_flow_control;
struct ctrl_ul ctrl_ul;
struct ctrl_dl ctrl_dl;
u8 toggle_ul;
u16 token_dl;
- struct tty_struct *tty;
- int tty_open_count;
/* mutex to ensure one access patch to this port */
struct mutex tty_sem;
wait_queue_head_t tty_wait;
spinlock_t spin_mutex; /* secures access to registers and tty */
unsigned int index_start;
+ enum card_state state;
u32 open_ttys;
};
u32 size_bytes)
{
u32 i = 0;
- const u32 *ptr = (__force u32 *) mem_addr_start;
+ const u32 __iomem *ptr = mem_addr_start;
u16 *buf16;
if (unlikely(!ptr || !buf))
switch (size_bytes) {
case 2: /* 2 bytes */
buf16 = (u16 *) buf;
- *buf16 = __le16_to_cpu(readw((void __iomem *)ptr));
+ *buf16 = __le16_to_cpu(readw(ptr));
goto out;
break;
case 4: /* 4 bytes */
- *(buf) = __le32_to_cpu(readl((void __iomem *)ptr));
+ *(buf) = __le32_to_cpu(readl(ptr));
goto out;
break;
}
if (size_bytes - i == 2) {
/* Handle 2 bytes in the end */
buf16 = (u16 *) buf;
- *(buf16) = __le16_to_cpu(readw((void __iomem *)ptr));
+ *(buf16) = __le16_to_cpu(readw(ptr));
i += 2;
} else {
/* Read 4 bytes */
- *(buf) = __le32_to_cpu(readl((void __iomem *)ptr));
+ *(buf) = __le32_to_cpu(readl(ptr));
i += 4;
}
buf++;
u32 size_bytes)
{
u32 i = 0;
- u32 *ptr = (__force u32 *) mem_addr_start;
+ u32 __iomem *ptr = mem_addr_start;
const u16 *buf16;
if (unlikely(!ptr || !buf))
switch (size_bytes) {
case 2: /* 2 bytes */
buf16 = (const u16 *)buf;
- writew(__cpu_to_le16(*buf16), (void __iomem *)ptr);
+ writew(__cpu_to_le16(*buf16), ptr);
return 2;
break;
case 1: /*
* so falling through..
*/
case 4: /* 4 bytes */
- writel(__cpu_to_le32(*buf), (void __iomem *)ptr);
+ writel(__cpu_to_le32(*buf), ptr);
return 4;
break;
}
if (size_bytes - i == 2) {
/* 2 bytes */
buf16 = (const u16 *)buf;
- writew(__cpu_to_le16(*buf16), (void __iomem *)ptr);
+ writew(__cpu_to_le16(*buf16), ptr);
i += 2;
} else {
/* 4 bytes */
- writel(__cpu_to_le32(*buf), (void __iomem *)ptr);
+ writel(__cpu_to_le32(*buf), ptr);
i += 4;
}
buf++;
dc->last_ier = dc->last_ier | CTRL_DL;
writew(dc->last_ier, dc->reg_ier);
+ dc->state = NOZOMI_STATE_ALLOCATED;
dev_info(&dc->pdev->dev, "Initialization OK!\n");
return 1;
}
* Return 1 - send buffer to card and ack.
* Return 0 - don't ack, don't send buffer to card.
*/
-static int send_data(enum port_type index, const struct nozomi *dc)
+static int send_data(enum port_type index, struct nozomi *dc)
{
u32 size = 0;
- const struct port *port = &dc->port[index];
+ struct port *port = &dc->port[index];
const u8 toggle = port->toggle_ul;
void __iomem *addr = port->ul_addr[toggle];
const u32 ul_size = port->ul_size[toggle];
- struct tty_struct *tty = port->tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
/* Get data from tty and place in buf for now */
size = __kfifo_get(port->fifo_ul, dc->send_buf,
if (size == 0) {
DBG4("No more data to send, disable link:");
+ tty_kref_put(tty);
return 0;
}
if (tty)
tty_wakeup(tty);
+ tty_kref_put(tty);
return 1;
}
u32 offset = 4;
struct port *port = &dc->port[index];
void __iomem *addr = port->dl_addr[port->toggle_dl];
- struct tty_struct *tty = port->tty;
- int i;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ int i, ret;
if (unlikely(!tty)) {
DBG1("tty not open for port: %d?", index);
/* disable interrupt in downlink... */
disable_transmit_dl(index, dc);
- return 0;
+ ret = 0;
+ goto put;
}
if (unlikely(size == 0)) {
dev_err(&dc->pdev->dev, "size == 0?\n");
- return 1;
+ ret = 1;
+ goto put;
}
tty_buffer_request_room(tty, size);
}
set_bit(index, &dc->flip);
-
- return 1;
+ ret = 1;
+put:
+ tty_kref_put(tty);
+ return ret;
}
/* Debug for interrupts */
case CTRL_APP2:
port = PORT_APP2;
enable_ier = APP2_DL;
+ if (dc->state == NOZOMI_STATE_ALLOCATED) {
+ /*
+ * After card initialization the flow control
+ * received for APP2 is always the last
+ */
+ dc->state = NOZOMI_STATE_READY;
+ dev_info(&dc->pdev->dev, "Device READY!\n");
+ }
break;
default:
dev_err(&dc->pdev->dev,
exit_handler:
spin_unlock(&dc->spin_mutex);
- for (a = 0; a < NOZOMI_MAX_PORTS; a++)
- if (test_and_clear_bit(a, &dc->flip))
- tty_flip_buffer_push(dc->port[a].tty);
+ for (a = 0; a < NOZOMI_MAX_PORTS; a++) {
+ struct tty_struct *tty;
+ if (test_and_clear_bit(a, &dc->flip)) {
+ tty = tty_port_tty_get(&dc->port[a].port);
+ if (tty)
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
+ }
return IRQ_HANDLED;
none:
spin_unlock(&dc->spin_mutex);
dc->pdev = pdev;
- /* Find out what card type it is */
- nozomi_get_card_type(dc);
-
ret = pci_enable_device(dc->pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to enable PCI Device\n");
goto err_free;
}
- start = pci_resource_start(dc->pdev, 0);
- if (start == 0) {
- dev_err(&pdev->dev, "No I/O address for card detected\n");
- ret = -ENODEV;
- goto err_disable_device;
- }
-
ret = pci_request_regions(dc->pdev, NOZOMI_NAME);
if (ret) {
dev_err(&pdev->dev, "I/O address 0x%04x already in use\n",
goto err_disable_device;
}
- dc->base_addr = ioremap(start, dc->card_type);
+ start = pci_resource_start(dc->pdev, 0);
+ if (start == 0) {
+ dev_err(&pdev->dev, "No I/O address for card detected\n");
+ ret = -ENODEV;
+ goto err_rel_regs;
+ }
+
+ /* Find out what card type it is */
+ nozomi_get_card_type(dc);
+
+ dc->base_addr = ioremap_nocache(start, dc->card_type);
if (!dc->base_addr) {
dev_err(&pdev->dev, "Unable to map card MMIO\n");
ret = -ENODEV;
dc->index_start = ndev_idx * MAX_PORT;
ndevs[ndev_idx] = dc;
+ pci_set_drvdata(pdev, dc);
+
+ /* Enable RESET interrupt */
+ dc->last_ier = RESET;
+ iowrite16(dc->last_ier, dc->reg_ier);
+
+ dc->state = NOZOMI_STATE_ENABLED;
+
for (i = 0; i < MAX_PORT; i++) {
mutex_init(&dc->port[i].tty_sem);
- dc->port[i].tty_open_count = 0;
- dc->port[i].tty = NULL;
+ tty_port_init(&dc->port[i].port);
tty_register_device(ntty_driver, dc->index_start + i,
&pdev->dev);
}
-
- /* Enable RESET interrupt. */
- dc->last_ier = RESET;
- writew(dc->last_ier, dc->reg_ier);
-
- pci_set_drvdata(pdev, dc);
-
return 0;
err_free_sbuf:
flush_scheduled_work();
- for (i = 0; i < MAX_PORT; ++i)
- if (dc->port[i].tty && \
- list_empty(&dc->port[i].tty->hangup_work.entry))
- tty_hangup(dc->port[i].tty);
-
+ for (i = 0; i < MAX_PORT; ++i) {
+ struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port);
+ if (tty && list_empty(&tty->hangup_work.entry))
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
+ /* Racy below - surely should wait for scheduled work to be done or
+ complete off a hangup method ? */
while (dc->open_ttys)
msleep(1);
-
for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
tty_unregister_device(ntty_driver, i);
}
struct nozomi *dc = get_dc_by_tty(tty);
unsigned long flags;
- if (!port || !dc)
+ if (!port || !dc || dc->state != NOZOMI_STATE_READY)
return -ENODEV;
if (mutex_lock_interruptible(&port->tty_sem))
return -ERESTARTSYS;
- port->tty_open_count++;
+ port->port.count++;
dc->open_ttys++;
/* Enable interrupt downlink for channel */
- if (port->tty_open_count == 1) {
- tty->low_latency = 1;
+ if (port->port.count == 1) {
tty->driver_data = port;
- port->tty = tty;
+ tty_port_tty_set(&port->port, tty);
DBG1("open: %d", port->token_dl);
spin_lock_irqsave(&dc->spin_mutex, flags);
dc->last_ier = dc->last_ier | port->token_dl;
writew(dc->last_ier, dc->reg_ier);
spin_unlock_irqrestore(&dc->spin_mutex, flags);
}
-
mutex_unlock(&port->tty_sem);
-
return 0;
}
-/* Called when the userspace process close the tty, /dev/noz*. */
+/* Called when the userspace process close the tty, /dev/noz*. Also
+ called immediately if ntty_open fails in which case tty->driver_data
+ will be NULL an we exit by the first return */
+
static void ntty_close(struct tty_struct *tty, struct file *file)
{
struct nozomi *dc = get_dc_by_tty(tty);
- struct port *port = tty->driver_data;
+ struct port *nport = tty->driver_data;
+ struct tty_port *port = &nport->port;
unsigned long flags;
- if (!dc || !port)
+ if (!dc || !nport)
return;
- if (mutex_lock_interruptible(&port->tty_sem))
- return;
+ /* Users cannot interrupt a close */
+ mutex_lock(&nport->tty_sem);
- if (!port->tty_open_count)
- goto exit;
+ WARN_ON(!port->count);
dc->open_ttys--;
- port->tty_open_count--;
+ port->count--;
+ tty_port_tty_set(port, NULL);
- if (port->tty_open_count == 0) {
- DBG1("close: %d", port->token_dl);
+ if (port->count == 0) {
+ DBG1("close: %d", nport->token_dl);
spin_lock_irqsave(&dc->spin_mutex, flags);
- dc->last_ier &= ~(port->token_dl);
+ dc->last_ier &= ~(nport->token_dl);
writew(dc->last_ier, dc->reg_ier);
spin_unlock_irqrestore(&dc->spin_mutex, flags);
}
-
-exit:
- mutex_unlock(&port->tty_sem);
+ mutex_unlock(&nport->tty_sem);
}
/*
return -EAGAIN;
}
- if (unlikely(!port->tty_open_count)) {
+ if (unlikely(!port->port.count)) {
DBG1(" ");
goto exit;
}
if (!mutex_trylock(&port->tty_sem))
return 0;
- if (!port->tty_open_count)
+ if (!port->port.count)
goto exit;
room = port->fifo_ul->size - __kfifo_len(port->fifo_ul);
const struct ctrl_dl *ctrl_dl = &port->ctrl_dl;
const struct ctrl_ul *ctrl_ul = &port->ctrl_ul;
+ /* Note: these could change under us but it is not clear this
+ matters if so */
return (ctrl_ul->RTS ? TIOCM_RTS : 0) |
(ctrl_ul->DTR ? TIOCM_DTR : 0) |
(ctrl_dl->DCD ? TIOCM_CAR : 0) |
static int ntty_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear)
{
+ struct nozomi *dc = get_dc_by_tty(tty);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dc->spin_mutex, flags);
if (set & TIOCM_RTS)
set_rts(tty, 1);
else if (clear & TIOCM_RTS)
set_dtr(tty, 1);
else if (clear & TIOCM_DTR)
set_dtr(tty, 0);
+ spin_unlock_irqrestore(&dc->spin_mutex, flags);
return 0;
}
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun;
- return copy_to_user(argp, &icount, sizeof(icount));
+ return copy_to_user(argp, &icount, sizeof(icount)) ? -EFAULT : 0;
}
static int ntty_ioctl(struct tty_struct *tty, struct file *file,
spin_unlock_irqrestore(&dc->spin_mutex, flags);
}
-/* just to discard single character writes */
-static void ntty_put_char(struct tty_struct *tty, unsigned char c)
-{
- /*
- * card does not react correct when we write single chars
- * to the card, so we discard them
- */
- DBG2("PUT CHAR Function: %c", c);
-}
-
/* Returns number of chars in buffer, called by tty layer */
static s32 ntty_chars_in_buffer(struct tty_struct *tty)
{
struct port *port = tty->driver_data;
struct nozomi *dc = get_dc_by_tty(tty);
- s32 rval;
+ s32 rval = 0;
if (unlikely(!dc || !port)) {
- rval = -ENODEV;
goto exit_in_buffer;
}
- if (unlikely(!port->tty_open_count)) {
+ if (unlikely(!port->port.count)) {
dev_err(&dc->pdev->dev, "No tty open?\n");
- rval = -ENODEV;
goto exit_in_buffer;
}
.unthrottle = ntty_unthrottle,
.throttle = ntty_throttle,
.chars_in_buffer = ntty_chars_in_buffer,
- .put_char = ntty_put_char,
.tiocmget = ntty_tiocmget,
.tiocmset = ntty_tiocmset,
};