Commit 32683578 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

staging: dgnc: delete the driver

Digi does not support it, no one has hardware for it, and no one is
working on it, so let's drop it for now.  If anyone wants to pick it
back up, then can revert this patch.
Reported-by: default avatarLidza Louina <lidza.louina@gmail.com>
Reported-by: default avatarMark Hounschell <markh@compro.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f3583dcd
......@@ -4359,13 +4359,6 @@ L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-gpio-mm.c
DIGI NEO AND CLASSIC PCI PRODUCTS
M: Lidza Louina <lidza.louina@gmail.com>
M: Mark Hounschell <markh@compro.net>
L: driverdev-devel@linuxdriverproject.org
S: Maintained
F: drivers/staging/dgnc/
DIOLAN U2C-12 I2C DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-i2c@vger.kernel.org
......
......@@ -80,8 +80,6 @@ source "drivers/staging/netlogic/Kconfig"
source "drivers/staging/mt29f_spinand/Kconfig"
source "drivers/staging/dgnc/Kconfig"
source "drivers/staging/gs_fpgaboot/Kconfig"
source "drivers/staging/unisys/Kconfig"
......
......@@ -29,7 +29,6 @@ obj-$(CONFIG_STAGING_BOARD) += board/
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_DGNC) += dgnc/
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
obj-$(CONFIG_UNISYSSPAR) += unisys/
......
config DGNC
tristate "Digi Neo and Classic PCI Products"
default n
depends on TTY && PCI
help
Say Y here to enable support for the Digi International Neo and
Classic PCI based product line.
To compile this driver as a module, say M here: the module will be
called dgnc
obj-$(CONFIG_DGNC) += dgnc.o
dgnc-objs := dgnc_cls.o dgnc_driver.o\
dgnc_tty.o
* remove unnecessary comments
* there is a lot of unnecessary code in the driver. It was
originally a standalone driver. Remove unneeded code.
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
Cc: Lidza Louina <lidza.louina@gmail.com>
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/pci.h>
#include "dgnc_driver.h"
#include "dgnc_cls.h"
#include "dgnc_tty.h"
static inline void cls_set_cts_flow_control(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
unsigned char ier = readb(&ch->ch_cls_uart->ier);
unsigned char isr_fcr = 0;
/*
* The Enhanced Register Set may only be accessed when
* the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on CTS flow control, turn off IXON flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_CTSDSR);
isr_fcr &= ~(UART_EXAR654_EFR_IXON);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/*
* Enable interrupts for CTS flow, turn off interrupts for
* received XOFF chars
*/
ier |= (UART_EXAR654_IER_CTSDSR);
ier &= ~(UART_EXAR654_IER_XOFF);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 |
UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
&ch->ch_cls_uart->isr_fcr);
ch->ch_t_tlevel = 16;
}
static inline void cls_set_ixon_flow_control(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
unsigned char ier = readb(&ch->ch_cls_uart->ier);
unsigned char isr_fcr = 0;
/*
* The Enhanced Register Set may only be accessed when
* the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on IXON flow control, turn off CTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXON);
isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Now set our current start/stop chars while in enhanced mode */
writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
writeb(0, &ch->ch_cls_uart->lsr);
writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
writeb(0, &ch->ch_cls_uart->spr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/*
* Disable interrupts for CTS flow, turn on interrupts for
* received XOFF chars
*/
ier &= ~(UART_EXAR654_IER_CTSDSR);
ier |= (UART_EXAR654_IER_XOFF);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
&ch->ch_cls_uart->isr_fcr);
}
static inline void cls_set_no_output_flow_control(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
unsigned char ier = readb(&ch->ch_cls_uart->ier);
unsigned char isr_fcr = 0;
/*
* The Enhanced Register Set may only be accessed when
* the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn off IXON flow control, turn off CTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB);
isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR | UART_EXAR654_EFR_IXON);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/*
* Disable interrupts for CTS flow, turn off interrupts for
* received XOFF chars
*/
ier &= ~(UART_EXAR654_IER_CTSDSR);
ier &= ~(UART_EXAR654_IER_XOFF);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
&ch->ch_cls_uart->isr_fcr);
ch->ch_r_watermark = 0;
ch->ch_t_tlevel = 16;
ch->ch_r_tlevel = 16;
}
static inline void cls_set_rts_flow_control(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
unsigned char ier = readb(&ch->ch_cls_uart->ier);
unsigned char isr_fcr = 0;
/*
* The Enhanced Register Set may only be accessed when
* the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on RTS flow control, turn off IXOFF flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_RTSDTR);
isr_fcr &= ~(UART_EXAR654_EFR_IXOFF);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* Enable interrupts for RTS flow */
ier |= (UART_EXAR654_IER_RTSDTR);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 |
UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
&ch->ch_cls_uart->isr_fcr);
ch->ch_r_watermark = 4;
ch->ch_r_tlevel = 8;
}
static inline void cls_set_ixoff_flow_control(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
unsigned char ier = readb(&ch->ch_cls_uart->ier);
unsigned char isr_fcr = 0;
/*
* The Enhanced Register Set may only be accessed when
* the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on IXOFF flow control, turn off RTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXOFF);
isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Now set our current start/stop chars while in enhanced mode */
writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
writeb(0, &ch->ch_cls_uart->lsr);
writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
writeb(0, &ch->ch_cls_uart->spr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* Disable interrupts for RTS flow */
ier &= ~(UART_EXAR654_IER_RTSDTR);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
&ch->ch_cls_uart->isr_fcr);
}
static inline void cls_set_no_input_flow_control(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
unsigned char ier = readb(&ch->ch_cls_uart->ier);
unsigned char isr_fcr = 0;
/*
* The Enhanced Register Set may only be accessed when
* the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn off IXOFF flow control, turn off RTS flow control */
isr_fcr |= (UART_EXAR654_EFR_ECB);
isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR | UART_EXAR654_EFR_IXOFF);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* Disable interrupts for RTS flow */
ier &= ~(UART_EXAR654_IER_RTSDTR);
writeb(ier, &ch->ch_cls_uart->ier);
/* Set the usual FIFO values */
writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
&ch->ch_cls_uart->isr_fcr);
ch->ch_t_tlevel = 16;
ch->ch_r_tlevel = 16;
}
/*
* Determines whether its time to shut off break condition.
*
* No locks are assumed to be held when calling this function.
* channel lock is held and released in this function.
*/
static inline void cls_clear_break(struct channel_t *ch, int force)
{
unsigned long flags;
if (!ch)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
if (!ch->ch_stop_sending_break) {
spin_unlock_irqrestore(&ch->ch_lock, flags);
return;
}
/* Turn break off, and unset some variables */
if (ch->ch_flags & CH_BREAK_SENDING) {
if (time_after(jiffies, ch->ch_stop_sending_break) || force) {
unsigned char temp = readb(&ch->ch_cls_uart->lcr);
writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr);
ch->ch_flags &= ~(CH_BREAK_SENDING);
ch->ch_stop_sending_break = 0;
}
}
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
static void cls_copy_data_from_uart_to_queue(struct channel_t *ch)
{
int qleft = 0;
unsigned char linestatus = 0;
unsigned char error_mask = 0;
ushort head;
ushort tail;
unsigned long flags;
if (!ch)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
head = ch->ch_r_head;
tail = ch->ch_r_tail;
qleft = tail - head - 1;
if (qleft < 0)
qleft += RQUEUEMASK + 1;
/*
* Create a mask to determine whether we should
* insert the character (if any) into our queue.
*/
if (ch->ch_c_iflag & IGNBRK)
error_mask |= UART_LSR_BI;
while (1) {
linestatus = readb(&ch->ch_cls_uart->lsr);
if (!(linestatus & (UART_LSR_DR)))
break;
/* Discard character if we are ignoring the error mask. */
if (linestatus & error_mask) {
linestatus = 0;
readb(&ch->ch_cls_uart->txrx);
continue;
}
/*
* If our queue is full, we have no choice but to drop some
* data. The assumption is that HWFLOW or SWFLOW should have
* stopped things way way before we got to this point.
*/
while (qleft < 1) {
tail = (tail + 1) & RQUEUEMASK;
ch->ch_r_tail = tail;
ch->ch_err_overrun++;
qleft++;
}
ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE
| UART_LSR_FE);
ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx);
qleft--;
if (ch->ch_equeue[head] & UART_LSR_PE)
ch->ch_err_parity++;
if (ch->ch_equeue[head] & UART_LSR_BI)
ch->ch_err_break++;
if (ch->ch_equeue[head] & UART_LSR_FE)
ch->ch_err_frame++;
head = (head + 1) & RQUEUEMASK;
ch->ch_rxcount++;
}
ch->ch_r_head = head & RQUEUEMASK;
ch->ch_e_head = head & EQUEUEMASK;
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
/* Make the UART raise any of the output signals we want up */
static void cls_assert_modem_signals(struct channel_t *ch)
{
unsigned char out;
if (!ch)
return;
out = ch->ch_mostat;
if (ch->ch_flags & CH_LOOPBACK)
out |= UART_MCR_LOOP;
writeb(out, &ch->ch_cls_uart->mcr);
/* Give time for the UART to actually drop the signals */
udelay(20);
}
static void cls_copy_data_from_queue_to_uart(struct channel_t *ch)
{
ushort head;
ushort tail;
int n;
int qlen;
uint len_written = 0;
unsigned long flags;
if (!ch)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
if (ch->ch_w_tail == ch->ch_w_head)
goto exit_unlock;
/* If port is "stopped", don't send any data to the UART */
if ((ch->ch_flags & CH_FORCED_STOP) ||
(ch->ch_flags & CH_BREAK_SENDING))
goto exit_unlock;
if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
goto exit_unlock;
n = 32;
head = ch->ch_w_head & WQUEUEMASK;
tail = ch->ch_w_tail & WQUEUEMASK;
qlen = (head - tail) & WQUEUEMASK;
n = min(n, qlen);
while (n > 0) {
/*
* If RTS Toggle mode is on, turn on RTS now if not already set,
* and make sure we get an event when the data transfer has
* completed.
*/
if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
if (!(ch->ch_mostat & UART_MCR_RTS)) {
ch->ch_mostat |= (UART_MCR_RTS);
cls_assert_modem_signals(ch);
}
ch->ch_tun.un_flags |= (UN_EMPTY);
}
/*
* If DTR Toggle mode is on, turn on DTR now if not already set,
* and make sure we get an event when the data transfer has
* completed.
*/
if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
if (!(ch->ch_mostat & UART_MCR_DTR)) {
ch->ch_mostat |= (UART_MCR_DTR);
cls_assert_modem_signals(ch);
}
ch->ch_tun.un_flags |= (UN_EMPTY);
}
writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx);
ch->ch_w_tail++;
ch->ch_w_tail &= WQUEUEMASK;
ch->ch_txcount++;
len_written++;
n--;
}
if (len_written > 0)
ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
exit_unlock:
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
static void cls_parse_modem(struct channel_t *ch, unsigned char signals)
{
unsigned char msignals = signals;
unsigned long flags;
if (!ch)
return;
/*
* Do altpin switching. Altpin switches DCD and DSR.
* This prolly breaks DSRPACE, so we should be more clever here.
*/
spin_lock_irqsave(&ch->ch_lock, flags);
if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
unsigned char mswap = signals;
if (mswap & UART_MSR_DDCD) {
msignals &= ~UART_MSR_DDCD;
msignals |= UART_MSR_DDSR;
}
if (mswap & UART_MSR_DDSR) {
msignals &= ~UART_MSR_DDSR;
msignals |= UART_MSR_DDCD;
}
if (mswap & UART_MSR_DCD) {
msignals &= ~UART_MSR_DCD;
msignals |= UART_MSR_DSR;
}
if (mswap & UART_MSR_DSR) {
msignals &= ~UART_MSR_DSR;
msignals |= UART_MSR_DCD;
}
}
spin_unlock_irqrestore(&ch->ch_lock, flags);
/* Scrub off lower bits. They signify delta's */
signals &= 0xf0;
spin_lock_irqsave(&ch->ch_lock, flags);
if (msignals & UART_MSR_DCD)
ch->ch_mistat |= UART_MSR_DCD;
else
ch->ch_mistat &= ~UART_MSR_DCD;
if (msignals & UART_MSR_DSR)
ch->ch_mistat |= UART_MSR_DSR;
else
ch->ch_mistat &= ~UART_MSR_DSR;
if (msignals & UART_MSR_RI)
ch->ch_mistat |= UART_MSR_RI;
else
ch->ch_mistat &= ~UART_MSR_RI;
if (msignals & UART_MSR_CTS)
ch->ch_mistat |= UART_MSR_CTS;
else
ch->ch_mistat &= ~UART_MSR_CTS;
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
/* Parse the ISR register for the specific port */
static inline void cls_parse_isr(struct dgnc_board *brd, uint port)
{
struct channel_t *ch;
unsigned char isr = 0;
unsigned long flags;
/*
* No need to verify board pointer, it was already
* verified in the interrupt routine.
*/
if (port >= brd->nasync)
return;
ch = brd->channels[port];
/* Here we try to figure out what caused the interrupt to happen */
while (1) {
isr = readb(&ch->ch_cls_uart->isr_fcr);
if (isr & UART_IIR_NO_INT)
break;
/* Receive Interrupt pending */
if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) {
cls_copy_data_from_uart_to_queue(ch);
dgnc_check_queue_flow_control(ch);
}
/* Transmit Hold register empty pending */
if (isr & UART_IIR_THRI) {
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
spin_unlock_irqrestore(&ch->ch_lock, flags);
cls_copy_data_from_queue_to_uart(ch);
}
cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
}
}
/* Channel lock MUST be held before calling this function! */
static void cls_flush_uart_write(struct channel_t *ch)
{
if (!ch)
return;
writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT),
&ch->ch_cls_uart->isr_fcr);
/* Must use *delay family functions in atomic context */
udelay(10);
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
}
/* Channel lock MUST be held before calling this function! */
static void cls_flush_uart_read(struct channel_t *ch)
{
if (!ch)
return;
/*
* For complete POSIX compatibility, we should be purging the
* read FIFO in the UART here.
*
* However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also
* incorrectly flushes write data as well as just basically trashing the
* FIFO.
*
* Presumably, this is a bug in this UART.
*/
udelay(10);
}
/* Send any/all changes to the line to the UART. */
static void cls_param(struct tty_struct *tty)
{
unsigned char lcr = 0;
unsigned char uart_lcr = 0;
unsigned char ier = 0;
unsigned char uart_ier = 0;
uint baud = 9600;
int quot = 0;
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
if (!tty)
return;
un = (struct un_t *)tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
/* If baud rate is zero, flush queues, and set mval to drop DTR. */
if ((ch->ch_c_cflag & (CBAUD)) == 0) {
ch->ch_r_head = 0;
ch->ch_r_tail = 0;
ch->ch_e_head = 0;
ch->ch_e_tail = 0;
ch->ch_w_head = 0;
ch->ch_w_tail = 0;
cls_flush_uart_write(ch);
cls_flush_uart_read(ch);
/* The baudrate is B0 so all modem lines are to be dropped. */
ch->ch_flags |= (CH_BAUD0);
ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
cls_assert_modem_signals(ch);
ch->ch_old_baud = 0;
return;
} else if (ch->ch_custom_speed) {
baud = ch->ch_custom_speed;
/* Handle transition from B0 */
if (ch->ch_flags & CH_BAUD0) {
ch->ch_flags &= ~(CH_BAUD0);
/*
* Bring back up RTS and DTR...
* Also handle RTS or DTR toggle if set.
*/
if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
ch->ch_mostat |= (UART_MCR_RTS);
if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
ch->ch_mostat |= (UART_MCR_DTR);
}
} else {
int iindex = 0;
int jindex = 0;
ulong bauds[4][16] = {
{ /* slowbaud */
0, 50, 75, 110,
134, 150, 200, 300,
600, 1200, 1800, 2400,
4800, 9600, 19200, 38400 },
{ /* slowbaud & CBAUDEX */
0, 57600, 115200, 230400,
460800, 150, 200, 921600,
600, 1200, 1800, 2400,
4800, 9600, 19200, 38400 },
{ /* fastbaud */
0, 57600, 76800, 115200,
131657, 153600, 230400, 460800,
921600, 1200, 1800, 2400,
4800, 9600, 19200, 38400 },
{ /* fastbaud & CBAUDEX */
0, 57600, 115200, 230400,
460800, 150, 200, 921600,
600, 1200, 1800, 2400,
4800, 9600, 19200, 38400 }
};
/*
* Only use the TXPrint baud rate if the terminal
* unit is NOT open
*/
if (!(ch->ch_tun.un_flags & UN_ISOPEN) &&
(un->un_type == DGNC_PRINT))
baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
else
baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
if (ch->ch_c_cflag & CBAUDEX)
iindex = 1;
if (ch->ch_digi.digi_flags & DIGI_FAST)
iindex += 2;
jindex = baud;
if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) &&
(jindex < 16)) {
baud = bauds[iindex][jindex];
} else {
baud = 0;
}
if (baud == 0)
baud = 9600;
/* Handle transition from B0 */
if (ch->ch_flags & CH_BAUD0) {
ch->ch_flags &= ~(CH_BAUD0);
/*
* Bring back up RTS and DTR...
* Also handle RTS or DTR toggle if set.
*/
if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
ch->ch_mostat |= (UART_MCR_RTS);
if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
ch->ch_mostat |= (UART_MCR_DTR);
}
}
if (ch->ch_c_cflag & PARENB)
lcr |= UART_LCR_PARITY;
if (!(ch->ch_c_cflag & PARODD))
lcr |= UART_LCR_EPAR;
#ifdef CMSPAR
if (ch->ch_c_cflag & CMSPAR)
lcr |= UART_LCR_SPAR;
#endif
if (ch->ch_c_cflag & CSTOPB)
lcr |= UART_LCR_STOP;
switch (ch->ch_c_cflag & CSIZE) {
case CS5:
lcr |= UART_LCR_WLEN5;
break;
case CS6:
lcr |= UART_LCR_WLEN6;
break;
case CS7:
lcr |= UART_LCR_WLEN7;
break;
case CS8:
default:
lcr |= UART_LCR_WLEN8;
break;
}
uart_ier = readb(&ch->ch_cls_uart->ier);
ier = uart_ier;
uart_lcr = readb(&ch->ch_cls_uart->lcr);
if (baud == 0)
baud = 9600;
quot = ch->ch_bd->bd_dividend / baud;
if (quot != 0 && ch->ch_old_baud != baud) {
ch->ch_old_baud = baud;
writeb(UART_LCR_DLAB, &ch->ch_cls_uart->lcr);
writeb((quot & 0xff), &ch->ch_cls_uart->txrx);
writeb((quot >> 8), &ch->ch_cls_uart->ier);
writeb(lcr, &ch->ch_cls_uart->lcr);
}
if (uart_lcr != lcr)
writeb(lcr, &ch->ch_cls_uart->lcr);
if (ch->ch_c_cflag & CREAD)
ier |= (UART_IER_RDI | UART_IER_RLSI);
else
ier &= ~(UART_IER_RDI | UART_IER_RLSI);
/*
* Have the UART interrupt on modem signal changes ONLY when
* we are in hardware flow control mode, or CLOCAL/FORCEDCD is not set.
*/
if ((ch->ch_digi.digi_flags & CTSPACE) ||
(ch->ch_digi.digi_flags & RTSPACE) ||
(ch->ch_c_cflag & CRTSCTS) ||
!(ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
!(ch->ch_c_cflag & CLOCAL))
ier |= UART_IER_MSI;
else
ier &= ~UART_IER_MSI;
ier |= UART_IER_THRI;
if (ier != uart_ier)
writeb(ier, &ch->ch_cls_uart->ier);
if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) {
cls_set_cts_flow_control(ch);
} else if (ch->ch_c_iflag & IXON) {
if ((ch->ch_startc == _POSIX_VDISABLE) ||
(ch->ch_stopc == _POSIX_VDISABLE))
cls_set_no_output_flow_control(ch);
else
cls_set_ixon_flow_control(ch);
} else {
cls_set_no_output_flow_control(ch);
}
if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) {
cls_set_rts_flow_control(ch);
} else if (ch->ch_c_iflag & IXOFF) {
if ((ch->ch_startc == _POSIX_VDISABLE) ||
(ch->ch_stopc == _POSIX_VDISABLE))
cls_set_no_input_flow_control(ch);
else
cls_set_ixoff_flow_control(ch);
} else {
cls_set_no_input_flow_control(ch);
}
cls_assert_modem_signals(ch);
cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
}
/* Board poller function. */
static void cls_tasklet(unsigned long data)
{
struct dgnc_board *bd = (struct dgnc_board *)data;
struct channel_t *ch;
unsigned long flags;
int i;
int state = 0;
int ports = 0;
if (!bd)
return;
spin_lock_irqsave(&bd->bd_lock, flags);
state = bd->state;
ports = bd->nasync;
spin_unlock_irqrestore(&bd->bd_lock, flags);
/*
* Do NOT allow the interrupt routine to read the intr registers
* Until we release this lock.
*/
spin_lock_irqsave(&bd->bd_intr_lock, flags);
if ((state == BOARD_READY) && (ports > 0)) {
for (i = 0; i < ports; i++) {
ch = bd->channels[i];
/*
* NOTE: Remember you CANNOT hold any channel
* locks when calling input.
* During input processing, its possible we
* will call ld, which might do callbacks back
* into us.
*/
dgnc_input(ch);
/*
* Channel lock is grabbed and then released
* inside this routine.
*/
cls_copy_data_from_queue_to_uart(ch);
dgnc_wakeup_writes(ch);
dgnc_carrier(ch);
/*
* The timing check of turning off the break is done
* inside clear_break()
*/
if (ch->ch_stop_sending_break)
cls_clear_break(ch, 0);
}
}
spin_unlock_irqrestore(&bd->bd_intr_lock, flags);
}
/* Classic specific interrupt handler. */
static irqreturn_t cls_intr(int irq, void *voidbrd)
{
struct dgnc_board *brd = voidbrd;
uint i = 0;
unsigned char poll_reg;
unsigned long flags;
if (!brd)
return IRQ_NONE;
spin_lock_irqsave(&brd->bd_intr_lock, flags);
poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
if (!poll_reg) {
spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
return IRQ_NONE;
}
for (i = 0; i < brd->nasync; i++)
cls_parse_isr(brd, i);
tasklet_schedule(&brd->helper_tasklet);
spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
return IRQ_HANDLED;
}
static void cls_disable_receiver(struct channel_t *ch)
{
unsigned char tmp = readb(&ch->ch_cls_uart->ier);
tmp &= ~(UART_IER_RDI);
writeb(tmp, &ch->ch_cls_uart->ier);
}
static void cls_enable_receiver(struct channel_t *ch)
{
unsigned char tmp = readb(&ch->ch_cls_uart->ier);
tmp |= (UART_IER_RDI);
writeb(tmp, &ch->ch_cls_uart->ier);
}
/*
* This function basically goes to sleep for seconds, or until
* it gets signalled that the port has fully drained.
*/
static int cls_drain(struct tty_struct *tty, uint seconds)
{
unsigned long flags;
struct channel_t *ch;
struct un_t *un;
if (!tty)
return -ENXIO;
un = (struct un_t *)tty->driver_data;
if (!un)
return -ENXIO;
ch = un->un_ch;
if (!ch)
return -ENXIO;
spin_lock_irqsave(&ch->ch_lock, flags);
un->un_flags |= UN_EMPTY;
spin_unlock_irqrestore(&ch->ch_lock, flags);
/* NOTE: Do something with time passed in. */
/* If ret is non-zero, user ctrl-c'ed us */
return wait_event_interruptible(un->un_flags_wait,
((un->un_flags & UN_EMPTY) == 0));
}
static void cls_send_start_character(struct channel_t *ch)
{
if (!ch)
return;
if (ch->ch_startc != _POSIX_VDISABLE) {
ch->ch_xon_sends++;
writeb(ch->ch_startc, &ch->ch_cls_uart->txrx);
}
}
static void cls_send_stop_character(struct channel_t *ch)
{
if (!ch)
return;
if (ch->ch_stopc != _POSIX_VDISABLE) {
ch->ch_xoff_sends++;
writeb(ch->ch_stopc, &ch->ch_cls_uart->txrx);
}
}
static void cls_uart_init(struct channel_t *ch)
{
unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
unsigned char isr_fcr = 0;
writeb(0, &ch->ch_cls_uart->ier);
/*
* The Enhanced Register Set may only be accessed when
* the Line Control Register is set to 0xBFh.
*/
writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
/* Turn on Enhanced/Extended controls */
isr_fcr |= (UART_EXAR654_EFR_ECB);
writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
/* Write old LCR value back out, which turns enhanced access off */
writeb(lcrb, &ch->ch_cls_uart->lcr);
/* Clear out UART and FIFO */
readb(&ch->ch_cls_uart->txrx);
writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
&ch->ch_cls_uart->isr_fcr);
usleep_range(10, 20);
ch->ch_flags |= (CH_FIFO_ENABLED | CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
readb(&ch->ch_cls_uart->lsr);
readb(&ch->ch_cls_uart->msr);
}
static void cls_uart_off(struct channel_t *ch)
{
writeb(0, &ch->ch_cls_uart->ier);
}
/*
* The channel lock MUST be held by the calling function.
* Returns 0 is nothing left in the FIFO, returns 1 otherwise.
*/
static uint cls_get_uart_bytes_left(struct channel_t *ch)
{
unsigned char left = 0;
unsigned char lsr = 0;
if (!ch)
return 0;
lsr = readb(&ch->ch_cls_uart->lsr);
/* Determine whether the Transmitter is empty or not */
if (!(lsr & UART_LSR_TEMT)) {
if (ch->ch_flags & CH_TX_FIFO_EMPTY)
tasklet_schedule(&ch->ch_bd->helper_tasklet);
left = 1;
} else {
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
left = 0;
}
return left;
}
/*
* Starts sending a break thru the UART.
* The channel lock MUST be held by the calling function.
*/
static void cls_send_break(struct channel_t *ch, int msecs)
{
if (!ch)
return;
/* If we receive a time of 0, this means turn off the break. */
if (msecs == 0) {
if (ch->ch_flags & CH_BREAK_SENDING) {
unsigned char temp = readb(&ch->ch_cls_uart->lcr);
writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr);
ch->ch_flags &= ~(CH_BREAK_SENDING);
ch->ch_stop_sending_break = 0;
}
return;
}
/*
* Set the time we should stop sending the break.
* If we are already sending a break, toss away the existing
* time to stop, and use this new value instead.
*/
ch->ch_stop_sending_break = jiffies + dgnc_jiffies_from_ms(msecs);
/* Tell the UART to start sending the break */
if (!(ch->ch_flags & CH_BREAK_SENDING)) {
unsigned char temp = readb(&ch->ch_cls_uart->lcr);
writeb((temp | UART_LCR_SBC), &ch->ch_cls_uart->lcr);
ch->ch_flags |= (CH_BREAK_SENDING);
}
}
/*
* Sends a specific character as soon as possible to the UART,
* jumping over any bytes that might be in the write queue.
*
* The channel lock MUST be held by the calling function.
*/
static void cls_send_immediate_char(struct channel_t *ch, unsigned char c)
{
if (!ch)
return;
writeb(c, &ch->ch_cls_uart->txrx);
}
struct board_ops dgnc_cls_ops = {
.tasklet = cls_tasklet,
.intr = cls_intr,
.uart_init = cls_uart_init,
.uart_off = cls_uart_off,
.drain = cls_drain,
.param = cls_param,
.assert_modem_signals = cls_assert_modem_signals,
.flush_uart_write = cls_flush_uart_write,
.flush_uart_read = cls_flush_uart_read,
.disable_receiver = cls_disable_receiver,
.enable_receiver = cls_enable_receiver,
.send_break = cls_send_break,
.send_start_character = cls_send_start_character,
.send_stop_character = cls_send_stop_character,
.copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart,
.get_uart_bytes_left = cls_get_uart_bytes_left,
.send_immediate_char = cls_send_immediate_char
};
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*/
#ifndef _DGNC_CLS_H
#define _DGNC_CLS_H
/**
* struct cls_uart_struct - Per channel/port Classic UART.
*
* key - W = read write
* - R = read only
* - U = unused
*
* @txrx: (WR) Holding Register.
* @ier: (WR) Interrupt Enable Register.
* @isr_fcr: (WR) Interrupt Status Register/Fifo Control Register.
* @lcr: (WR) Line Control Register.
* @mcr: (WR) Modem Control Register.
* @lsr: (WR) Line Status Register.
* @msr: (WR) Modem Status Register.
* @spr: (WR) Scratch Pad Register.
*/
struct cls_uart_struct {
u8 txrx;
u8 ier;
u8 isr_fcr;
u8 lcr;
u8 mcr;
u8 lsr;
u8 msr;
u8 spr;
};
/* Where to read the interrupt register (8bits) */
#define UART_CLASSIC_POLL_ADDR_OFFSET 0x40
#define UART_EXAR654_ENHANCED_REGISTER_SET 0xBF
#define UART_16654_FCR_TXTRIGGER_16 0x10
#define UART_16654_FCR_RXTRIGGER_16 0x40
#define UART_16654_FCR_RXTRIGGER_56 0x80
/* Received CTS/RTS change of state */
#define UART_IIR_CTSRTS 0x20
/* Receiver data TIMEOUT */
#define UART_IIR_RDI_TIMEOUT 0x0C
/*
* These are the EXTENDED definitions for the Exar 654's Interrupt
* Enable Register.
*/
#define UART_EXAR654_EFR_ECB 0x10 /* Enhanced control bit */
#define UART_EXAR654_EFR_IXON 0x2 /* Receiver compares Xon1/Xoff1 */
#define UART_EXAR654_EFR_IXOFF 0x8 /* Transmit Xon1/Xoff1 */
#define UART_EXAR654_EFR_RTSDTR 0x40 /* Auto RTS/DTR Flow Control Enable */
#define UART_EXAR654_EFR_CTSDSR 0x80 /* Auto CTS/DSR Flow Control Enable */
#define UART_EXAR654_IER_XOFF 0x20 /* Xoff Interrupt Enable */
#define UART_EXAR654_IER_RTSDTR 0x40 /* Output Interrupt Enable */
#define UART_EXAR654_IER_CTSDSR 0x80 /* Input Interrupt Enable */
extern struct board_ops dgnc_cls_ops;
#endif /* _DGNC_CLS_H */
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include "dgnc_driver.h"
#include "dgnc_tty.h"
#include "dgnc_cls.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Digi International, http://www.digi.com");
MODULE_DESCRIPTION("Driver for the Digi International Neo and Classic PCI based product line");
MODULE_SUPPORTED_DEVICE("dgnc");
static unsigned int dgnc_num_boards;
struct dgnc_board *dgnc_board[MAXBOARDS];
static DEFINE_SPINLOCK(dgnc_poll_lock); /* Poll scheduling lock */
static int dgnc_poll_tick = 20; /* Poll interval - 20 ms */
static ulong dgnc_poll_time; /* Time of next poll */
static uint dgnc_poll_stop; /* Used to tell poller to stop */
static struct timer_list dgnc_poll_timer;
#define DIGI_VID 0x114F
#define PCI_DEVICE_CLASSIC_4_DID 0x0028
#define PCI_DEVICE_CLASSIC_8_DID 0x0029
#define PCI_DEVICE_CLASSIC_4_422_DID 0x00D0
#define PCI_DEVICE_CLASSIC_8_422_DID 0x00D1
#define PCI_DEVICE_CLASSIC_4_PCI_NAME "ClassicBoard 4 PCI"
#define PCI_DEVICE_CLASSIC_8_PCI_NAME "ClassicBoard 8 PCI"
#define PCI_DEVICE_CLASSIC_4_422_PCI_NAME "ClassicBoard 4 422 PCI"
#define PCI_DEVICE_CLASSIC_8_422_PCI_NAME "ClassicBoard 8 422 PCI"
static const struct pci_device_id dgnc_pci_tbl[] = {
{PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_4_DID), .driver_data = 0},
{PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_4_422_DID), .driver_data = 1},
{PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_8_DID), .driver_data = 2},
{PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_8_422_DID), .driver_data = 3},
{0,}
};
MODULE_DEVICE_TABLE(pci, dgnc_pci_tbl);
struct board_id {
unsigned char *name;
uint maxports;
unsigned int is_pci_express;
};
static const struct board_id dgnc_ids[] = {
{ PCI_DEVICE_CLASSIC_4_PCI_NAME, 4, 0 },
{ PCI_DEVICE_CLASSIC_4_422_PCI_NAME, 4, 0 },
{ PCI_DEVICE_CLASSIC_8_PCI_NAME, 8, 0 },
{ PCI_DEVICE_CLASSIC_8_422_PCI_NAME, 8, 0 },
{ NULL, 0, 0 }
};
/* Remap PCI memory. */
static int dgnc_do_remap(struct dgnc_board *brd)
{
brd->re_map_membase = ioremap(brd->membase, 0x1000);
if (!brd->re_map_membase)
return -ENOMEM;
return 0;
}
/* A board has been found, initialize it. */
static struct dgnc_board *dgnc_found_board(struct pci_dev *pdev, int id)
{
struct dgnc_board *brd;
unsigned int pci_irq;
int rc = 0;
brd = kzalloc(sizeof(*brd), GFP_KERNEL);
if (!brd)
return ERR_PTR(-ENOMEM);
/* store the info for the board we've found */
brd->boardnum = dgnc_num_boards;
brd->device = dgnc_pci_tbl[id].device;
brd->pdev = pdev;
brd->name = dgnc_ids[id].name;
brd->maxports = dgnc_ids[id].maxports;
init_waitqueue_head(&brd->state_wait);
spin_lock_init(&brd->bd_lock);
spin_lock_init(&brd->bd_intr_lock);
brd->state = BOARD_FOUND;
pci_irq = pdev->irq;
brd->irq = pci_irq;
switch (brd->device) {
case PCI_DEVICE_CLASSIC_4_DID:
case PCI_DEVICE_CLASSIC_8_DID:
case PCI_DEVICE_CLASSIC_4_422_DID:
case PCI_DEVICE_CLASSIC_8_422_DID:
/*
* For PCI ClassicBoards
* PCI Local Address (i.e. "resource" number) space
* 0 PLX Memory Mapped Config
* 1 PLX I/O Mapped Config
* 2 I/O Mapped UARTs and Status
* 3 Memory Mapped VPD
* 4 Memory Mapped UARTs and Status
*/
brd->membase = pci_resource_start(pdev, 4);
if (!brd->membase) {
dev_err(&brd->pdev->dev,
"Card has no PCI IO resources, failing.\n");
rc = -ENODEV;
goto failed;
}
brd->membase_end = pci_resource_end(pdev, 4);
if (brd->membase & 1)
brd->membase &= ~3;
else
brd->membase &= ~15;
brd->iobase = pci_resource_start(pdev, 1);
brd->iobase_end = pci_resource_end(pdev, 1);
brd->iobase = ((unsigned int)(brd->iobase)) & 0xFFFE;
brd->bd_ops = &dgnc_cls_ops;
brd->bd_uart_offset = 0x8;
brd->bd_dividend = 921600;
rc = dgnc_do_remap(brd);
if (rc < 0)
goto failed;
/*
* Enable Local Interrupt 1 (0x1),
* Local Interrupt 1 Polarity Active high (0x2),
* Enable PCI interrupt (0x40)
*/
outb(0x43, brd->iobase + 0x4c);
break;
default:
dev_err(&brd->pdev->dev,
"Didn't find any compatible Neo/Classic PCI boards.\n");
rc = -ENXIO;
goto failed;
}
tasklet_init(&brd->helper_tasklet,
brd->bd_ops->tasklet,
(unsigned long)brd);
wake_up_interruptible(&brd->state_wait);
return brd;
failed:
kfree(brd);
return ERR_PTR(rc);
}
static int dgnc_request_irq(struct dgnc_board *brd)
{
if (brd->irq) {
int rc = request_irq(brd->irq, brd->bd_ops->intr,
IRQF_SHARED, "DGNC", brd);
if (rc) {
dev_err(&brd->pdev->dev,
"Failed to hook IRQ %d\n", brd->irq);
brd->state = BOARD_FAILED;
return -ENODEV;
}
}
return 0;
}
static void dgnc_free_irq(struct dgnc_board *brd)
{
if (brd->irq)
free_irq(brd->irq, brd);
}
/*
* As each timer expires, it determines (a) whether the "transmit"
* waiter needs to be woken up, and (b) whether the poller needs to
* be rescheduled.
*/
static void dgnc_poll_handler(struct timer_list *unused)
{
struct dgnc_board *brd;
unsigned long flags;
int i;
unsigned long new_time;
for (i = 0; i < dgnc_num_boards; i++) {
brd = dgnc_board[i];
spin_lock_irqsave(&brd->bd_lock, flags);
if (brd->state == BOARD_FAILED) {
spin_unlock_irqrestore(&brd->bd_lock, flags);
continue;
}
tasklet_schedule(&brd->helper_tasklet);
spin_unlock_irqrestore(&brd->bd_lock, flags);
}
/* Schedule ourself back at the nominal wakeup interval. */
spin_lock_irqsave(&dgnc_poll_lock, flags);
dgnc_poll_time += dgnc_jiffies_from_ms(dgnc_poll_tick);
new_time = dgnc_poll_time - jiffies;
if ((ulong)new_time >= 2 * dgnc_poll_tick)
dgnc_poll_time = jiffies + dgnc_jiffies_from_ms(dgnc_poll_tick);
timer_setup(&dgnc_poll_timer, dgnc_poll_handler, 0);
dgnc_poll_timer.expires = dgnc_poll_time;
spin_unlock_irqrestore(&dgnc_poll_lock, flags);
if (!dgnc_poll_stop)
add_timer(&dgnc_poll_timer);
}
/* returns count (>= 0), or negative on error */
static int dgnc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc;
struct dgnc_board *brd;
rc = pci_enable_device(pdev);
if (rc)
return -EIO;
brd = dgnc_found_board(pdev, ent->driver_data);
if (IS_ERR(brd))
return PTR_ERR(brd);
rc = dgnc_tty_register(brd);
if (rc < 0) {
pr_err(DRVSTR ": Can't register tty devices (%d)\n", rc);
goto failed;
}
rc = dgnc_request_irq(brd);
if (rc < 0) {
pr_err(DRVSTR ": Can't finalize board init (%d)\n", rc);
goto unregister_tty;
}
rc = dgnc_tty_init(brd);
if (rc < 0) {
pr_err(DRVSTR ": Can't init tty devices (%d)\n", rc);
goto free_irq;
}
brd->state = BOARD_READY;
dgnc_board[dgnc_num_boards++] = brd;
return 0;
free_irq:
dgnc_free_irq(brd);
unregister_tty:
dgnc_tty_unregister(brd);
failed:
kfree(brd);
return rc;
}
static struct pci_driver dgnc_driver = {
.name = "dgnc",
.probe = dgnc_init_one,
.id_table = dgnc_pci_tbl,
};
static int dgnc_start(void)
{
unsigned long flags;
/* Start the poller */
spin_lock_irqsave(&dgnc_poll_lock, flags);
timer_setup(&dgnc_poll_timer, dgnc_poll_handler, 0);
dgnc_poll_time = jiffies + dgnc_jiffies_from_ms(dgnc_poll_tick);
dgnc_poll_timer.expires = dgnc_poll_time;
spin_unlock_irqrestore(&dgnc_poll_lock, flags);
add_timer(&dgnc_poll_timer);
return 0;
}
/* Free all the memory associated with a board */
static void dgnc_cleanup_board(struct dgnc_board *brd)
{
int i = 0;
if (!brd)
return;
switch (brd->device) {
case PCI_DEVICE_CLASSIC_4_DID:
case PCI_DEVICE_CLASSIC_8_DID:
case PCI_DEVICE_CLASSIC_4_422_DID:
case PCI_DEVICE_CLASSIC_8_422_DID:
/* Tell card not to interrupt anymore. */
outb(0, brd->iobase + 0x4c);
break;
default:
break;
}
if (brd->irq)
free_irq(brd->irq, brd);
tasklet_kill(&brd->helper_tasklet);
if (brd->re_map_membase) {
iounmap(brd->re_map_membase);
brd->re_map_membase = NULL;
}
for (i = 0; i < MAXPORTS ; i++) {
if (brd->channels[i]) {
kfree(brd->channels[i]->ch_rqueue);
kfree(brd->channels[i]->ch_equeue);
kfree(brd->channels[i]->ch_wqueue);
kfree(brd->channels[i]);
brd->channels[i] = NULL;
}
}
dgnc_board[brd->boardnum] = NULL;
kfree(brd);
}
/* Driver load/unload functions */
static void cleanup(void)
{
int i;
unsigned long flags;
spin_lock_irqsave(&dgnc_poll_lock, flags);
dgnc_poll_stop = 1;
spin_unlock_irqrestore(&dgnc_poll_lock, flags);
/* Turn off poller right away. */
del_timer_sync(&dgnc_poll_timer);
for (i = 0; i < dgnc_num_boards; ++i) {
dgnc_cleanup_tty(dgnc_board[i]);
dgnc_cleanup_board(dgnc_board[i]);
}
}
static void __exit dgnc_cleanup_module(void)
{
cleanup();
pci_unregister_driver(&dgnc_driver);
}
static int __init dgnc_init_module(void)
{
int rc;
/* Initialize global stuff */
rc = dgnc_start();
if (rc < 0)
return rc;
/* Find and configure all the cards */
rc = pci_register_driver(&dgnc_driver);
if (rc) {
pr_warn("WARNING: dgnc driver load failed. No Digi Neo or Classic boards found.\n");
cleanup();
return rc;
}
return 0;
}
module_init(dgnc_init_module);
module_exit(dgnc_cleanup_module);
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*/
#ifndef _DGNC_DRIVER_H
#define _DGNC_DRIVER_H
#include <linux/types.h>
#include <linux/tty.h>
#include <linux/interrupt.h>
#include "digi.h" /* Digi specific ioctl header */
/* Driver identification and error statements */
#define PROCSTR "dgnc" /* /proc entries */
#define DEVSTR "/dev/dg/dgnc" /* /dev entries */
#define DRVSTR "dgnc" /* Driver name string */
#define DG_PART "40002369_F" /* RPM part number */
#define TRC_TO_CONSOLE 1
/* Number of boards we support at once. */
#define MAXBOARDS 20
#define MAXPORTS 8
#define MAXTTYNAMELEN 200
/* Serial port types */
#define DGNC_SERIAL 0
#define DGNC_PRINT 1
#define SERIAL_TYPE_NORMAL 1
#define PORT_NUM(dev) ((dev) & 0x7f)
#define IS_PRINT(dev) (((dev) & 0xff) >= 0x80)
/* MAX number of stop characters sent when our read queue is getting full */
#define MAX_STOPS_SENT 5
/* 4 extra for alignment play space */
#define WRITEBUFLEN ((4096) + 4)
#define dgnc_jiffies_from_ms(a) (((a) * HZ) / 1000)
#ifndef _POSIX_VDISABLE
#define _POSIX_VDISABLE '\0'
#endif
/* All the possible states the driver can be while being loaded. */
enum {
DRIVER_INITIALIZED = 0,
DRIVER_READY
};
/* All the possible states the board can be while booting up. */
enum {
BOARD_FAILED = 0,
BOARD_FOUND,
BOARD_READY
};
struct dgnc_board;
struct channel_t;
/**
* struct board_ops - Per board operations.
*/
struct board_ops {
void (*tasklet)(unsigned long data);
irqreturn_t (*intr)(int irq, void *voidbrd);
void (*uart_init)(struct channel_t *ch);
void (*uart_off)(struct channel_t *ch);
int (*drain)(struct tty_struct *tty, uint seconds);
void (*param)(struct tty_struct *tty);
void (*assert_modem_signals)(struct channel_t *ch);
void (*flush_uart_write)(struct channel_t *ch);
void (*flush_uart_read)(struct channel_t *ch);
void (*disable_receiver)(struct channel_t *ch);
void (*enable_receiver)(struct channel_t *ch);
void (*send_break)(struct channel_t *ch, int msec);
void (*send_start_character)(struct channel_t *ch);
void (*send_stop_character)(struct channel_t *ch);
void (*copy_data_from_queue_to_uart)(struct channel_t *ch);
uint (*get_uart_bytes_left)(struct channel_t *ch);
void (*send_immediate_char)(struct channel_t *ch, unsigned char c);
};
/**
* struct dgnc_board - Per board information.
* @boardnum: Board number (0 - 32).
*
* @name: Product name.
* @pdev: Pointer to the pci_dev structure.
* @device: PCI device ID.
* @maxports: Maximum ports this board can handle.
* @bd_lock: Used to protect board.
* @bd_intr_lock: Protect poller tasklet and interrupt routine from each other.
* @state: State of the card.
* @state_wait: Queue to sleep on for state change.
* @helper_tasklet: Poll helper tasklet.
* @nasync: Number of ports on card.
* @irq: Interrupt request number.
* @membase: Start of base memory of the card.
* @membase_end: End of base memory of the card.
* @iobase: Start of IO base of the card.
* @iobase_end: End of IO base of the card.
* @bd_uart_offset: Space between each UART.
* @channels: array of pointers to our channels.
* @serial_driver: Pointer to the serial driver.
* @serial_name: Serial driver name.
* @print_dirver: Pointer to the print driver.
* @print_name: Print driver name.
* @bd_dividend: Board/UART's specific dividend.
* @bd_ops: Pointer to board operations structure.
*/
struct dgnc_board {
int boardnum;
char *name;
struct pci_dev *pdev;
u16 device;
uint maxports;
/* used to protect the board */
spinlock_t bd_lock;
/* Protect poller tasklet and interrupt routine from each other. */
spinlock_t bd_intr_lock;
uint state;
wait_queue_head_t state_wait;
struct tasklet_struct helper_tasklet;
uint nasync;
uint irq;
ulong membase;
ulong membase_end;
u8 __iomem *re_map_membase;
ulong iobase;
ulong iobase_end;
uint bd_uart_offset;
struct channel_t *channels[MAXPORTS];
struct tty_driver *serial_driver;
char serial_name[200];
struct tty_driver *print_driver;
char print_name[200];
uint bd_dividend;
struct board_ops *bd_ops;
};
/* Unit flag definitions for un_flags. */
#define UN_ISOPEN 0x0001 /* Device is open */
#define UN_CLOSING 0x0002 /* Line is being closed */
#define UN_IMM 0x0004 /* Service immediately */
#define UN_BUSY 0x0008 /* Some work this channel */
#define UN_BREAKI 0x0010 /* Input break received */
#define UN_PWAIT 0x0020 /* Printer waiting for terminal */
#define UN_TIME 0x0040 /* Waiting on time */
#define UN_EMPTY 0x0080 /* Waiting output queue empty */
#define UN_LOW 0x0100 /* Waiting output low water mark*/
#define UN_EXCL_OPEN 0x0200 /* Open for exclusive use */
#define UN_WOPEN 0x0400 /* Device waiting for open */
#define UN_WIOCTL 0x0800 /* Device waiting for open */
#define UN_HANGUP 0x8000 /* Carrier lost */
struct device;
/**
* struct un_t - terminal or printer unit
* @un_open_count: Counter of opens to port.
* @un_tty: Pointer to unit tty structure.
* @un_flags: Unit flags.
* @un_flags_wait: Place to sleep to wait on unit.
* @un_dev: Minor device number.
*/
struct un_t {
struct channel_t *un_ch;
uint un_type;
uint un_open_count;
struct tty_struct *un_tty;
uint un_flags;
wait_queue_head_t un_flags_wait;
uint un_dev;
struct device *un_sysfs;
};
/* Device flag definitions for ch_flags. */
#define CH_PRON 0x0001 /* Printer on string */
#define CH_STOP 0x0002 /* Output is stopped */
#define CH_STOPI 0x0004 /* Input is stopped */
#define CH_CD 0x0008 /* Carrier is present */
#define CH_FCAR 0x0010 /* Carrier forced on */
#define CH_HANGUP 0x0020 /* Hangup received */
#define CH_RECEIVER_OFF 0x0040 /* Receiver is off */
#define CH_OPENING 0x0080 /* Port in fragile open state */
#define CH_CLOSING 0x0100 /* Port in fragile close state */
#define CH_FIFO_ENABLED 0x0200 /* Port has FIFOs enabled */
#define CH_TX_FIFO_EMPTY 0x0400 /* TX Fifo is completely empty */
#define CH_TX_FIFO_LWM 0x0800 /* TX Fifo is below Low Water */
#define CH_BREAK_SENDING 0x1000 /* Break is being sent */
#define CH_LOOPBACK 0x2000 /* Channel is in lookback mode */
#define CH_BAUD0 0x08000 /* Used for checking B0 transitions */
#define CH_FORCED_STOP 0x20000 /* Output is forcibly stopped */
#define CH_FORCED_STOPI 0x40000 /* Input is forcibly stopped */
/* Our Read/Error/Write queue sizes */
#define RQUEUEMASK 0x1FFF /* 8 K - 1 */
#define EQUEUEMASK 0x1FFF /* 8 K - 1 */
#define WQUEUEMASK 0x0FFF /* 4 K - 1 */
#define RQUEUESIZE (RQUEUEMASK + 1)
#define EQUEUESIZE RQUEUESIZE
#define WQUEUESIZE (WQUEUEMASK + 1)
/**
* struct channel_t - Channel information.
* @dgnc_board: Pointer to board structure.
* @ch_bd: Transparent print structure.
* @ch_tun: Terminal unit information.
* @ch_pun: Printer unit information.
* @ch_lock: Provide for serialization.
* @ch_flags_wait: Channel flags wait queue.
* @ch_portnum: Port number, 0 offset.
* @ch_open_count: Open count.
* @ch_flags: Channel flags.
* @ch_close_delay: How long we should drop RTS/DTR for.
* @ch_cpstime: Time for CPS calculations.
* @ch_c_iflag: Channel iflags.
* @ch_c_cflag: Channel cflags.
* @ch_c_oflag: Channel oflags.
* @ch_c_lflag: Channel lflags.
* @ch_stopc: Stop character.
* @ch_startc: Start character.
* @ch_old_baud: Cache of the current baud rate.
* @ch_custom_speed: Custom baud rate, if set.
* @ch_wopen: Waiting for open process count.
* @ch_mostat: FEP output modem status.
* @ch_mistat: FEP input modem status.
* @ch_cls_uart: Pointer to the mapped cls UART struct
* @ch_cached_lsr: Cached value of the LSR register.
* @ch_rqueue: Read queue buffer, malloc'ed.
* @ch_r_head: Head location of the read queue.
* @ch_r_tail: Tail location of the read queue.
* @ch_equeue: Error queue buffer, malloc'ed.
* @ch_e_head: Head location of the error queue.
* @ch_e_tail: Tail location of the error queue.
* @ch_wqueue: Write queue buffer, malloc'ed.
* @ch_w_head: Head location of the write queue.
* @ch_w_tail: Tail location of the write queue.
* @ch_rxcount: Total of data received so far.
* @ch_txcount: Total of data transmitted so far.
* @ch_r_tlevel: Receive trigger level.
* @ch_t_tlevel: Transmit trigger level.
* @ch_r_watermark: Receive water mark.
* @ch_stop_sending_break: Time we should STOP sending a break.
* @ch_stops_sent: How many times I have send a stop character to try
* to stop the other guy sending.
* @ch_err_parity: Count of parity
* @ch_err_frame: Count of framing errors on channel.
* @ch_err_break: Count of breaks on channel.
* @ch_err_overrun: Count of overruns on channel.
* @ch_xon_sends: Count of xons transmitted.
* @ch_xoff_sends: Count of xoffs transmitted.
*/
struct channel_t {
struct dgnc_board *ch_bd;
struct digi_t ch_digi;
struct un_t ch_tun;
struct un_t ch_pun;
spinlock_t ch_lock; /* provide for serialization */
wait_queue_head_t ch_flags_wait;
uint ch_portnum;
uint ch_open_count;
uint ch_flags;
ulong ch_close_delay;
ulong ch_cpstime;
tcflag_t ch_c_iflag;
tcflag_t ch_c_cflag;
tcflag_t ch_c_oflag;
tcflag_t ch_c_lflag;
unsigned char ch_stopc;
unsigned char ch_startc;
uint ch_old_baud;
uint ch_custom_speed;
uint ch_wopen;
unsigned char ch_mostat;
unsigned char ch_mistat;
struct cls_uart_struct __iomem *ch_cls_uart;
unsigned char ch_cached_lsr;
unsigned char *ch_rqueue;
ushort ch_r_head;
ushort ch_r_tail;
unsigned char *ch_equeue;
ushort ch_e_head;
ushort ch_e_tail;
unsigned char *ch_wqueue;
ushort ch_w_head;
ushort ch_w_tail;
ulong ch_rxcount;
ulong ch_txcount;
unsigned char ch_r_tlevel;
unsigned char ch_t_tlevel;
unsigned char ch_r_watermark;
ulong ch_stop_sending_break;
uint ch_stops_sent;
ulong ch_err_parity;
ulong ch_err_frame;
ulong ch_err_break;
ulong ch_err_overrun;
ulong ch_xon_sends;
ulong ch_xoff_sends;
};
extern struct dgnc_board *dgnc_board[MAXBOARDS];/* Array of boards */
#endif /* _DGNC_DRIVER_H */
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*/
/*
* This file implements the tty driver functionality for the
* Neo and ClassicBoard PCI based product lines.
*/
#include <linux/kernel.h>
#include <linux/sched/signal.h> /* For jiffies, task states, etc. */
#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/types.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
#include <linux/delay.h> /* For udelay */
#include <linux/uaccess.h> /* For copy_from_user/copy_to_user */
#include <linux/pci.h>
#include "dgnc_driver.h"
#include "dgnc_tty.h"
#include "dgnc_cls.h"
/* Default transparent print information. */
static const struct digi_t dgnc_digi_init = {
.digi_flags = DIGI_COOK, /* Flags */
.digi_maxcps = 100, /* Max CPS */
.digi_maxchar = 50, /* Max chars in print queue */
.digi_bufsize = 100, /* Printer buffer size */
.digi_onlen = 4, /* size of printer on string */
.digi_offlen = 4, /* size of printer off string */
.digi_onstr = "\033[5i", /* ANSI printer on string ] */
.digi_offstr = "\033[4i", /* ANSI printer off string ] */
.digi_term = "ansi" /* default terminal type */
};
static int dgnc_tty_open(struct tty_struct *tty, struct file *file);
static void dgnc_tty_close(struct tty_struct *tty, struct file *file);
static int dgnc_block_til_ready(struct tty_struct *tty, struct file *file,
struct channel_t *ch);
static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg);
static int dgnc_tty_digigeta(struct tty_struct *tty,
struct digi_t __user *retinfo);
static int dgnc_tty_digiseta(struct tty_struct *tty,
struct digi_t __user *new_info);
static int dgnc_tty_write_room(struct tty_struct *tty);
static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c);
static int dgnc_tty_chars_in_buffer(struct tty_struct *tty);
static void dgnc_tty_start(struct tty_struct *tty);
static void dgnc_tty_stop(struct tty_struct *tty);
static void dgnc_tty_throttle(struct tty_struct *tty);
static void dgnc_tty_unthrottle(struct tty_struct *tty);
static void dgnc_tty_flush_chars(struct tty_struct *tty);
static void dgnc_tty_flush_buffer(struct tty_struct *tty);
static void dgnc_tty_hangup(struct tty_struct *tty);
static int dgnc_set_modem_info(struct channel_t *ch, unsigned int command,
unsigned int __user *value);
static int dgnc_get_modem_info(struct channel_t *ch,
unsigned int __user *value);
static int dgnc_tty_tiocmget(struct tty_struct *tty);
static int dgnc_tty_tiocmset(struct tty_struct *tty, unsigned int set,
unsigned int clear);
static int dgnc_tty_send_break(struct tty_struct *tty, int msec);
static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout);
static int dgnc_tty_write(struct tty_struct *tty, const unsigned char *buf,
int count);
static void dgnc_tty_set_termios(struct tty_struct *tty,
struct ktermios *old_termios);
static void dgnc_tty_send_xchar(struct tty_struct *tty, char ch);
static void dgnc_set_signal_low(struct channel_t *ch, const unsigned char line);
static void dgnc_wake_up_unit(struct un_t *unit);
static const struct tty_operations dgnc_tty_ops = {
.open = dgnc_tty_open,
.close = dgnc_tty_close,
.write = dgnc_tty_write,
.write_room = dgnc_tty_write_room,
.flush_buffer = dgnc_tty_flush_buffer,
.chars_in_buffer = dgnc_tty_chars_in_buffer,
.flush_chars = dgnc_tty_flush_chars,
.ioctl = dgnc_tty_ioctl,
.set_termios = dgnc_tty_set_termios,
.stop = dgnc_tty_stop,
.start = dgnc_tty_start,
.throttle = dgnc_tty_throttle,
.unthrottle = dgnc_tty_unthrottle,
.hangup = dgnc_tty_hangup,
.put_char = dgnc_tty_put_char,
.tiocmget = dgnc_tty_tiocmget,
.tiocmset = dgnc_tty_tiocmset,
.break_ctl = dgnc_tty_send_break,
.wait_until_sent = dgnc_tty_wait_until_sent,
.send_xchar = dgnc_tty_send_xchar
};
/* TTY Initialization/Cleanup Functions */
static struct tty_driver *dgnc_tty_create(char *serial_name, uint maxports,
int major, int minor)
{
int rc;
struct tty_driver *drv;
drv = tty_alloc_driver(maxports,
TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV |
TTY_DRIVER_HARDWARE_BREAK);
if (IS_ERR(drv))
return drv;
drv->name = serial_name;
drv->name_base = 0;
drv->major = major;
drv->minor_start = minor;
drv->type = TTY_DRIVER_TYPE_SERIAL;
drv->subtype = SERIAL_TYPE_NORMAL;
drv->init_termios = tty_std_termios;
drv->init_termios.c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL);
drv->init_termios.c_ispeed = 9600;
drv->init_termios.c_ospeed = 9600;
drv->driver_name = DRVSTR;
/*
* Entry points for driver. Called by the kernel from
* tty_io.c and n_tty.c.
*/
tty_set_operations(drv, &dgnc_tty_ops);
rc = tty_register_driver(drv);
if (rc < 0) {
put_tty_driver(drv);
return ERR_PTR(rc);
}
return drv;
}
static void dgnc_tty_free(struct tty_driver *drv)
{
tty_unregister_driver(drv);
put_tty_driver(drv);
}
/**
* dgnc_tty_register() - Init the tty subsystem for this board.
*/
int dgnc_tty_register(struct dgnc_board *brd)
{
int rc;
snprintf(brd->serial_name, MAXTTYNAMELEN, "tty_dgnc_%d_",
brd->boardnum);
brd->serial_driver = dgnc_tty_create(brd->serial_name,
brd->maxports, 0, 0);
if (IS_ERR(brd->serial_driver)) {
rc = PTR_ERR(brd->serial_driver);
dev_dbg(&brd->pdev->dev, "Can't register tty device (%d)\n",
rc);
return rc;
}
snprintf(brd->print_name, MAXTTYNAMELEN, "pr_dgnc_%d_", brd->boardnum);
brd->print_driver = dgnc_tty_create(brd->print_name, brd->maxports,
0x80,
brd->serial_driver->major);
if (IS_ERR(brd->print_driver)) {
rc = PTR_ERR(brd->print_driver);
dev_dbg(&brd->pdev->dev,
"Can't register Transparent Print device(%d)\n", rc);
dgnc_tty_free(brd->serial_driver);
return rc;
}
return 0;
}
void dgnc_tty_unregister(struct dgnc_board *brd)
{
dgnc_tty_free(brd->print_driver);
dgnc_tty_free(brd->serial_driver);
}
/**
* dgnc_tty_init() - Initialize the tty subsystem.
*
* Called once per board after board has been downloaded and initialized.
*/
int dgnc_tty_init(struct dgnc_board *brd)
{
int i;
int rc;
void __iomem *vaddr;
struct channel_t *ch;
if (!brd)
return -ENXIO;
/* Initialize board structure elements. */
vaddr = brd->re_map_membase;
brd->nasync = brd->maxports;
for (i = 0; i < brd->nasync; i++) {
brd->channels[i] = kzalloc(sizeof(*brd->channels[i]),
GFP_KERNEL);
if (!brd->channels[i]) {
rc = -ENOMEM;
goto err_free_channels;
}
}
ch = brd->channels[0];
vaddr = brd->re_map_membase;
/* Set up channel variables */
for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
spin_lock_init(&ch->ch_lock);
ch->ch_tun.un_ch = ch;
ch->ch_tun.un_type = DGNC_SERIAL;
ch->ch_tun.un_dev = i;
ch->ch_pun.un_ch = ch;
ch->ch_pun.un_type = DGNC_PRINT;
ch->ch_pun.un_dev = i + 128;
ch->ch_cls_uart = vaddr + (brd->bd_uart_offset * i);
ch->ch_bd = brd;
ch->ch_portnum = i;
ch->ch_digi = dgnc_digi_init;
/* .25 second delay */
ch->ch_close_delay = 250;
init_waitqueue_head(&ch->ch_flags_wait);
init_waitqueue_head(&ch->ch_tun.un_flags_wait);
init_waitqueue_head(&ch->ch_pun.un_flags_wait);
{
struct device *classp;
classp = tty_register_device(brd->serial_driver, i,
&ch->ch_bd->pdev->dev);
ch->ch_tun.un_sysfs = classp;
classp = tty_register_device(brd->print_driver, i,
&ch->ch_bd->pdev->dev);
ch->ch_pun.un_sysfs = classp;
}
}
return 0;
err_free_channels:
for (i = i - 1; i >= 0; --i) {
kfree(brd->channels[i]);
brd->channels[i] = NULL;
}
return rc;
}
/**
* dgnc_cleanup_tty() - Cleanup driver.
*
* Uninitialize the TTY portion of this driver. Free all memory and
* resources.
*/
void dgnc_cleanup_tty(struct dgnc_board *brd)
{
int i = 0;
for (i = 0; i < brd->nasync; i++)
tty_unregister_device(brd->serial_driver, i);
tty_unregister_driver(brd->serial_driver);
for (i = 0; i < brd->nasync; i++)
tty_unregister_device(brd->print_driver, i);
tty_unregister_driver(brd->print_driver);
put_tty_driver(brd->serial_driver);
put_tty_driver(brd->print_driver);
}
/**
* dgnc_wmove() - Write data to transmit queue.
* @ch: Pointer to channel structure.
* @buf: Pointer to characters to be moved.
* @n: Number of characters to move.
*/
static void dgnc_wmove(struct channel_t *ch, char *buf, uint n)
{
int remain;
uint head;
if (!ch)
return;
head = ch->ch_w_head & WQUEUEMASK;
/*
* If the write wraps over the top of the circular buffer,
* move the portion up to the wrap point, and reset the
* pointers to the bottom.
*/
remain = WQUEUESIZE - head;
if (n >= remain) {
n -= remain;
memcpy(ch->ch_wqueue + head, buf, remain);
head = 0;
buf += remain;
}
if (n > 0) {
/* Move rest of data. */
remain = n;
memcpy(ch->ch_wqueue + head, buf, remain);
head += remain;
}
head &= WQUEUEMASK;
ch->ch_w_head = head;
}
/**
* dgnc_input() - Process received data.
* @ch: Pointer to channel structure.
*/
void dgnc_input(struct channel_t *ch)
{
struct dgnc_board *bd;
struct tty_struct *tp;
struct tty_ldisc *ld = NULL;
uint rmask;
ushort head;
ushort tail;
int data_len;
unsigned long flags;
int flip_len;
int len = 0;
int n = 0;
int s = 0;
int i = 0;
if (!ch)
return;
tp = ch->ch_tun.un_tty;
bd = ch->ch_bd;
if (!bd)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
rmask = RQUEUEMASK;
head = ch->ch_r_head & rmask;
tail = ch->ch_r_tail & rmask;
data_len = (head - tail) & rmask;
if (data_len == 0)
goto exit_unlock;
/*
* If the device is not open, or CREAD is off,
* flush input data and return immediately.
*/
if (!tp ||
!(ch->ch_tun.un_flags & UN_ISOPEN) ||
!C_CREAD(tp) ||
(ch->ch_tun.un_flags & UN_CLOSING)) {
ch->ch_r_head = tail;
/* Force queue flow control to be released, if needed */
dgnc_check_queue_flow_control(ch);
goto exit_unlock;
}
if (ch->ch_flags & CH_FORCED_STOPI)
goto exit_unlock;
flip_len = TTY_FLIPBUF_SIZE;
len = min(data_len, flip_len);
len = min(len, (N_TTY_BUF_SIZE - 1));
ld = tty_ldisc_ref(tp);
if (!ld) {
len = 0;
} else {
if (!ld->ops->receive_buf) {
ch->ch_r_head = ch->ch_r_tail;
len = 0;
}
}
if (len <= 0)
goto exit_unlock;
/*
* The tty layer in the kernel has changed in 2.6.16+.
*
* The flip buffers in the tty structure are no longer exposed,
* and probably will be going away eventually.
*
* If we are completely raw, we don't need to go through a lot
* of the tty layers that exist.
* In this case, we take the shortest and fastest route we
* can to relay the data to the user.
*
* On the other hand, if we are not raw, we need to go through
* the new 2.6.16+ tty layer, which has its API more well defined.
*/
len = tty_buffer_request_room(tp->port, len);
n = len;
/*
* n now contains the most amount of data we can copy,
* bounded either by how much the Linux tty layer can handle,
* or the amount of data the card actually has pending...
*/
while (n) {
unsigned char *ch_pos = ch->ch_equeue + tail;
s = ((head >= tail) ? head : RQUEUESIZE) - tail;
s = min(s, n);
if (s <= 0)
break;
/*
* If conditions are such that ld needs to see all
* UART errors, we will have to walk each character
* and error byte and send them to the buffer one at
* a time.
*/
if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
for (i = 0; i < s; i++) {
unsigned char ch = *(ch_pos + i);
char flag = TTY_NORMAL;
if (ch & UART_LSR_BI)
flag = TTY_BREAK;
else if (ch & UART_LSR_PE)
flag = TTY_PARITY;
else if (ch & UART_LSR_FE)
flag = TTY_FRAME;
tty_insert_flip_char(tp->port, ch, flag);
}
} else {
tty_insert_flip_string(tp->port, ch_pos, s);
}
tail += s;
n -= s;
/* Flip queue if needed */
tail &= rmask;
}
ch->ch_r_tail = tail & rmask;
ch->ch_e_tail = tail & rmask;
dgnc_check_queue_flow_control(ch);
spin_unlock_irqrestore(&ch->ch_lock, flags);
/* Tell the tty layer its okay to "eat" the data now */
tty_flip_buffer_push(tp->port);
if (ld)
tty_ldisc_deref(ld);
return;
exit_unlock:
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (ld)
tty_ldisc_deref(ld);
}
/**
* dgnc_carrier()
*
* Determines when CARRIER changes state and takes appropriate
* action.
*/
void dgnc_carrier(struct channel_t *ch)
{
int virt_carrier = 0;
int phys_carrier = 0;
if (!ch)
return;
if (ch->ch_mistat & UART_MSR_DCD)
phys_carrier = 1;
if (ch->ch_digi.digi_flags & DIGI_FORCEDCD)
virt_carrier = 1;
if (ch->ch_c_cflag & CLOCAL)
virt_carrier = 1;
/* Test for a VIRTUAL carrier transition to HIGH. */
if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
/*
* When carrier rises, wake any threads waiting
* for carrier in the open routine.
*/
if (waitqueue_active(&ch->ch_flags_wait))
wake_up_interruptible(&ch->ch_flags_wait);
}
/* Test for a PHYSICAL carrier transition to HIGH. */
if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
/*
* When carrier rises, wake any threads waiting
* for carrier in the open routine.
*/
if (waitqueue_active(&ch->ch_flags_wait))
wake_up_interruptible(&ch->ch_flags_wait);
}
/*
* Test for a PHYSICAL transition to low, so long as we aren't
* currently ignoring physical transitions (which is what "virtual
* carrier" indicates).
*
* The transition of the virtual carrier to low really doesn't
* matter... it really only means "ignore carrier state", not
* "make pretend that carrier is there".
*/
if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) &&
(phys_carrier == 0)) {
/*
* When carrier drops:
*
* Drop carrier on all open units.
*
* Flush queues, waking up any task waiting in the
* line discipline.
*
* Send a hangup to the control terminal.
*
* Enable all select calls.
*/
if (waitqueue_active(&ch->ch_flags_wait))
wake_up_interruptible(&ch->ch_flags_wait);
if (ch->ch_tun.un_open_count > 0)
tty_hangup(ch->ch_tun.un_tty);
if (ch->ch_pun.un_open_count > 0)
tty_hangup(ch->ch_pun.un_tty);
}
/* Make sure that our cached values reflect the current reality. */
if (virt_carrier == 1)
ch->ch_flags |= CH_FCAR;
else
ch->ch_flags &= ~CH_FCAR;
if (phys_carrier == 1)
ch->ch_flags |= CH_CD;
else
ch->ch_flags &= ~CH_CD;
}
/* Assign the custom baud rate to the channel structure */
static void dgnc_set_custom_speed(struct channel_t *ch, uint newrate)
{
int testdiv;
int testrate_high;
int testrate_low;
int deltahigh;
int deltalow;
if (newrate <= 0) {
ch->ch_custom_speed = 0;
return;
}
/*
* Since the divisor is stored in a 16-bit integer, we make sure
* we don't allow any rates smaller than a 16-bit integer would allow.
* And of course, rates above the dividend won't fly.
*/
if (newrate && newrate < ((ch->ch_bd->bd_dividend / 0xFFFF) + 1))
newrate = (ch->ch_bd->bd_dividend / 0xFFFF) + 1;
if (newrate && newrate > ch->ch_bd->bd_dividend)
newrate = ch->ch_bd->bd_dividend;
if (newrate > 0) {
testdiv = ch->ch_bd->bd_dividend / newrate;
/*
* If we try to figure out what rate the board would use
* with the test divisor, it will be either equal or higher
* than the requested baud rate. If we then determine the
* rate with a divisor one higher, we will get the next lower
* supported rate below the requested.
*/
testrate_high = ch->ch_bd->bd_dividend / testdiv;
testrate_low = ch->ch_bd->bd_dividend / (testdiv + 1);
/*
* If the rate for the requested divisor is correct, just
* use it and be done.
*/
if (testrate_high != newrate) {
/*
* Otherwise, pick the rate that is closer
* (i.e. whichever rate has a smaller delta).
*/
deltahigh = testrate_high - newrate;
deltalow = newrate - testrate_low;
if (deltahigh < deltalow)
newrate = testrate_high;
else
newrate = testrate_low;
}
}
ch->ch_custom_speed = newrate;
}
void dgnc_check_queue_flow_control(struct channel_t *ch)
{
int qleft;
qleft = ch->ch_r_tail - ch->ch_r_head - 1;
if (qleft < 0)
qleft += RQUEUEMASK + 1;
/*
* Check to see if we should enforce flow control on our queue because
* the ld (or user) isn't reading data out of our queue fast enuf.
*
* NOTE: This is done based on what the current flow control of the
* port is set for.
*
* 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
* This will cause the UART's FIFO to back up, and force
* the RTS signal to be dropped.
* 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
* the other side, in hopes it will stop sending data to us.
* 3) NONE - Nothing we can do. We will simply drop any extra data
* that gets sent into us when the queue fills up.
*/
if (qleft < 256) {
/* HWFLOW */
if (ch->ch_digi.digi_flags & CTSPACE ||
ch->ch_c_cflag & CRTSCTS) {
if (!(ch->ch_flags & CH_RECEIVER_OFF)) {
ch->ch_bd->bd_ops->disable_receiver(ch);
ch->ch_flags |= (CH_RECEIVER_OFF);
}
}
/* SWFLOW */
else if (ch->ch_c_iflag & IXOFF) {
if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
ch->ch_bd->bd_ops->send_stop_character(ch);
ch->ch_stops_sent++;
}
}
}
/*
* Check to see if we should unenforce flow control because
* ld (or user) finally read enuf data out of our queue.
*
* NOTE: This is done based on what the current flow control of the
* port is set for.
*
* 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
* This will cause the UART's FIFO to raise RTS back up,
* which will allow the other side to start sending data again.
* 2) SWFLOW (IXOFF) - Send a start character to
* the other side, so it will start sending data to us again.
* 3) NONE - Do nothing. Since we didn't do anything to turn off the
* other side, we don't need to do anything now.
*/
if (qleft > (RQUEUESIZE / 2)) {
/* HWFLOW */
if (ch->ch_digi.digi_flags & RTSPACE ||
ch->ch_c_cflag & CRTSCTS) {
if (ch->ch_flags & CH_RECEIVER_OFF) {
ch->ch_bd->bd_ops->enable_receiver(ch);
ch->ch_flags &= ~(CH_RECEIVER_OFF);
}
}
/* SWFLOW */
else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
ch->ch_stops_sent = 0;
ch->ch_bd->bd_ops->send_start_character(ch);
}
}
}
static void dgnc_set_signal_low(struct channel_t *ch, const unsigned char sig)
{
ch->ch_mostat &= ~(sig);
ch->ch_bd->bd_ops->assert_modem_signals(ch);
}
void dgnc_wakeup_writes(struct channel_t *ch)
{
int qlen = 0;
unsigned long flags;
if (!ch)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
/* If channel now has space, wake up anyone waiting on the condition. */
qlen = ch->ch_w_head - ch->ch_w_tail;
if (qlen < 0)
qlen += WQUEUESIZE;
if (qlen >= (WQUEUESIZE - 256)) {
spin_unlock_irqrestore(&ch->ch_lock, flags);
return;
}
if (ch->ch_tun.un_flags & UN_ISOPEN) {
tty_wakeup(ch->ch_tun.un_tty);
/*
* If unit is set to wait until empty, check to make sure
* the queue AND FIFO are both empty.
*/
if (ch->ch_tun.un_flags & UN_EMPTY) {
if ((qlen == 0) &&
(ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) {
ch->ch_tun.un_flags &= ~(UN_EMPTY);
/*
* If RTS Toggle mode is on, whenever
* the queue and UART is empty, keep RTS low.
*/
if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)
dgnc_set_signal_low(ch, UART_MCR_RTS);
/*
* If DTR Toggle mode is on, whenever
* the queue and UART is empty, keep DTR low.
*/
if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)
dgnc_set_signal_low(ch, UART_MCR_DTR);
}
}
wake_up_interruptible(&ch->ch_tun.un_flags_wait);
}
if (ch->ch_pun.un_flags & UN_ISOPEN) {
tty_wakeup(ch->ch_pun.un_tty);
/*
* If unit is set to wait until empty, check to make sure
* the queue AND FIFO are both empty.
*/
if (ch->ch_pun.un_flags & UN_EMPTY) {
if ((qlen == 0) &&
(ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0))
ch->ch_pun.un_flags &= ~(UN_EMPTY);
}
wake_up_interruptible(&ch->ch_pun.un_flags_wait);
}
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
static struct dgnc_board *find_board_by_major(unsigned int major)
{
int i;
for (i = 0; i < MAXBOARDS; i++) {
struct dgnc_board *brd = dgnc_board[i];
if (!brd)
return NULL;
if (major == brd->serial_driver->major ||
major == brd->print_driver->major)
return brd;
}
return NULL;
}
/* TTY Entry points and helper functions */
static int dgnc_tty_open(struct tty_struct *tty, struct file *file)
{
struct dgnc_board *brd;
struct channel_t *ch;
struct un_t *un;
uint major = 0;
uint minor = 0;
int rc = 0;
unsigned long flags;
rc = 0;
major = MAJOR(tty_devnum(tty));
minor = MINOR(tty_devnum(tty));
if (major > 255)
return -ENXIO;
brd = find_board_by_major(major);
if (!brd)
return -ENXIO;
rc = wait_event_interruptible(brd->state_wait,
(brd->state & BOARD_READY));
if (rc)
return rc;
spin_lock_irqsave(&brd->bd_lock, flags);
if (PORT_NUM(minor) >= brd->nasync) {
rc = -ENXIO;
goto err_brd_unlock;
}
ch = brd->channels[PORT_NUM(minor)];
if (!ch) {
rc = -ENXIO;
goto err_brd_unlock;
}
spin_unlock_irqrestore(&brd->bd_lock, flags);
spin_lock_irqsave(&ch->ch_lock, flags);
/* Figure out our type */
if (!IS_PRINT(minor)) {
un = &brd->channels[PORT_NUM(minor)]->ch_tun;
un->un_type = DGNC_SERIAL;
} else if (IS_PRINT(minor)) {
un = &brd->channels[PORT_NUM(minor)]->ch_pun;
un->un_type = DGNC_PRINT;
} else {
rc = -ENXIO;
goto err_ch_unlock;
}
/*
* If the port is still in a previous open, and in a state
* where we simply cannot safely keep going, wait until the
* state clears.
*/
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = wait_event_interruptible(ch->ch_flags_wait,
((ch->ch_flags & CH_OPENING) == 0));
/* If ret is non-zero, user ctrl-c'ed us */
if (rc)
return -EINTR;
/*
* If either unit is in the middle of the fragile part of close,
* we just cannot touch the channel safely.
* Go to sleep, knowing that when the channel can be
* touched safely, the close routine will signal the
* ch_flags_wait to wake us back up.
*/
rc = wait_event_interruptible(ch->ch_flags_wait,
!((ch->ch_tun.un_flags |
ch->ch_pun.un_flags) & UN_CLOSING));
/* If ret is non-zero, user ctrl-c'ed us */
if (rc)
return -EINTR;
spin_lock_irqsave(&ch->ch_lock, flags);
tty->driver_data = un;
/* Initialize tty's */
if (!(un->un_flags & UN_ISOPEN)) {
un->un_tty = tty;
/* Maybe do something here to the TTY struct as well? */
}
/*
* Allocate channel buffers for read/write/error.
* Set flag, so we don't get trounced on.
*/
ch->ch_flags |= (CH_OPENING);
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (!ch->ch_rqueue)
ch->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL);
if (!ch->ch_equeue)
ch->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL);
if (!ch->ch_wqueue)
ch->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL);
if (!ch->ch_rqueue || !ch->ch_equeue || !ch->ch_wqueue) {
kfree(ch->ch_rqueue);
kfree(ch->ch_equeue);
kfree(ch->ch_wqueue);
return -ENOMEM;
}
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags &= ~(CH_OPENING);
wake_up_interruptible(&ch->ch_flags_wait);
/* Initialize if neither terminal or printer is open. */
if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) {
/* Flush input queues. */
ch->ch_r_head = 0;
ch->ch_r_tail = 0;
ch->ch_e_head = 0;
ch->ch_e_tail = 0;
ch->ch_w_head = 0;
ch->ch_w_tail = 0;
brd->bd_ops->flush_uart_write(ch);
brd->bd_ops->flush_uart_read(ch);
ch->ch_flags = 0;
ch->ch_cached_lsr = 0;
ch->ch_stop_sending_break = 0;
ch->ch_stops_sent = 0;
ch->ch_c_cflag = tty->termios.c_cflag;
ch->ch_c_iflag = tty->termios.c_iflag;
ch->ch_c_oflag = tty->termios.c_oflag;
ch->ch_c_lflag = tty->termios.c_lflag;
ch->ch_startc = tty->termios.c_cc[VSTART];
ch->ch_stopc = tty->termios.c_cc[VSTOP];
/*
* Bring up RTS and DTR...
* Also handle RTS or DTR toggle if set.
*/
if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
ch->ch_mostat |= (UART_MCR_RTS);
if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
ch->ch_mostat |= (UART_MCR_DTR);
/* Tell UART to init itself */
brd->bd_ops->uart_init(ch);
}
brd->bd_ops->param(tty);
dgnc_carrier(ch);
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = dgnc_block_til_ready(tty, file, ch);
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_open_count++;
un->un_open_count++;
un->un_flags |= (UN_ISOPEN);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return rc;
err_brd_unlock:
spin_unlock_irqrestore(&brd->bd_lock, flags);
return rc;
err_ch_unlock:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return rc;
}
/* Wait for DCD, if needed. */
static int dgnc_block_til_ready(struct tty_struct *tty,
struct file *file,
struct channel_t *ch)
{
int rc = 0;
struct un_t *un = tty->driver_data;
unsigned long flags;
uint old_flags = 0;
int sleep_on_un_flags = 0;
if (!file)
return -ENXIO;
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_wopen++;
while (1) {
sleep_on_un_flags = 0;
if (ch->ch_bd->state == BOARD_FAILED) {
rc = -ENXIO;
break;
}
if (tty_hung_up_p(file)) {
rc = -EAGAIN;
break;
}
/*
* If either unit is in the middle of the fragile part of close,
* we just cannot touch the channel safely.
* Go back to sleep, knowing that when the channel can be
* touched safely, the close routine will signal the
* ch_wait_flags to wake us back up.
*/
if (!((ch->ch_tun.un_flags |
ch->ch_pun.un_flags) &
UN_CLOSING)) {
/*
* Our conditions to leave cleanly and happily:
* 1) NONBLOCKING on the tty is set.
* 2) CLOCAL is set.
* 3) DCD (fake or real) is active.
*/
if (file->f_flags & O_NONBLOCK)
break;
if (tty_io_error(tty)) {
rc = -EIO;
break;
}
if (ch->ch_flags & CH_CD)
break;
if (ch->ch_flags & CH_FCAR)
break;
} else {
sleep_on_un_flags = 1;
}
/*
* If there is a signal pending, the user probably
* interrupted (ctrl-c) us.
*/
if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
if (sleep_on_un_flags)
old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags;
else
old_flags = ch->ch_flags;
/*
* Let go of channel lock before calling schedule.
* Our poller will get any FEP events and wake us up when DCD
* eventually goes active.
*/
spin_unlock_irqrestore(&ch->ch_lock, flags);
/*
* Wait for something in the flags to change
* from the current value.
*/
if (sleep_on_un_flags)
rc = wait_event_interruptible
(un->un_flags_wait,
(old_flags != (ch->ch_tun.un_flags |
ch->ch_pun.un_flags)));
else
rc = wait_event_interruptible(
ch->ch_flags_wait,
(old_flags != ch->ch_flags));
/*
* We got woken up for some reason.
* Before looping around, grab our channel lock.
*/
spin_lock_irqsave(&ch->ch_lock, flags);
}
ch->ch_wopen--;
spin_unlock_irqrestore(&ch->ch_lock, flags);
return rc;
}
/* Hangup the port. Like a close, but don't wait for output to drain. */
static void dgnc_tty_hangup(struct tty_struct *tty)
{
if (!tty)
return;
/* flush the transmit queues */
dgnc_tty_flush_buffer(tty);
}
static void dgnc_tty_close(struct tty_struct *tty, struct file *file)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
/*
* Determine if this is the last close or not - and if we agree about
* which type of close it is with the Line Discipline
*/
if ((tty->count == 1) && (un->un_open_count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. un_open_count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
dev_dbg(tty->dev,
"tty->count is 1, un open count is %d\n",
un->un_open_count);
un->un_open_count = 1;
}
if (un->un_open_count)
un->un_open_count--;
else
dev_dbg(tty->dev,
"bad serial port open count of %d\n",
un->un_open_count);
ch->ch_open_count--;
if (ch->ch_open_count && un->un_open_count) {
spin_unlock_irqrestore(&ch->ch_lock, flags);
return;
}
/* OK, its the last close on the unit */
un->un_flags |= UN_CLOSING;
tty->closing = 1;
/*
* Only officially close channel if count is 0 and
* DIGI_PRINTER bit is not set.
*/
if ((ch->ch_open_count == 0) &&
!(ch->ch_digi.digi_flags & DIGI_PRINTER)) {
ch->ch_flags &= ~(CH_STOPI | CH_FORCED_STOPI);
/* turn off print device when closing print device. */
if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
dgnc_wmove(ch, ch->ch_digi.digi_offstr,
(int)ch->ch_digi.digi_offlen);
ch->ch_flags &= ~CH_PRON;
}
spin_unlock_irqrestore(&ch->ch_lock, flags);
/* wait for output to drain */
/* This will also return if we take an interrupt */
bd->bd_ops->drain(tty, 0);
dgnc_tty_flush_buffer(tty);
tty_ldisc_flush(tty);
spin_lock_irqsave(&ch->ch_lock, flags);
tty->closing = 0;
/* If we have HUPCL set, lower DTR and RTS */
if (ch->ch_c_cflag & HUPCL) {
/* Drop RTS/DTR */
ch->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
bd->bd_ops->assert_modem_signals(ch);
/*
* Go to sleep to ensure RTS/DTR
* have been dropped for modems to see it.
*/
if (ch->ch_close_delay) {
spin_unlock_irqrestore(&ch->ch_lock,
flags);
msleep_interruptible(ch->ch_close_delay);
spin_lock_irqsave(&ch->ch_lock, flags);
}
}
ch->ch_old_baud = 0;
/* Turn off UART interrupts for this port */
ch->ch_bd->bd_ops->uart_off(ch);
} else {
/* turn off print device when closing print device. */
if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
dgnc_wmove(ch, ch->ch_digi.digi_offstr,
(int)ch->ch_digi.digi_offlen);
ch->ch_flags &= ~CH_PRON;
}
}
un->un_tty = NULL;
un->un_flags &= ~(UN_ISOPEN | UN_CLOSING);
wake_up_interruptible(&ch->ch_flags_wait);
wake_up_interruptible(&un->un_flags_wait);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
/*
* Return number of characters that have not been transmitted yet.
*
* This routine is used by the line discipline to determine if there
* is data waiting to be transmitted/drained/flushed or not.
*/
static int dgnc_tty_chars_in_buffer(struct tty_struct *tty)
{
struct channel_t *ch = NULL;
struct un_t *un = NULL;
ushort thead;
ushort ttail;
uint tmask;
uint chars;
unsigned long flags;
if (!tty)
return 0;
un = tty->driver_data;
if (!un)
return 0;
ch = un->un_ch;
if (!ch)
return 0;
spin_lock_irqsave(&ch->ch_lock, flags);
tmask = WQUEUEMASK;
thead = ch->ch_w_head & tmask;
ttail = ch->ch_w_tail & tmask;
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (ttail == thead)
chars = 0;
else if (thead > ttail)
chars = thead - ttail;
else
chars = thead - ttail + WQUEUESIZE;
return chars;
}
/*
* Reduces bytes_available to the max number of characters
* that can be sent currently given the maxcps value, and
* returns the new bytes_available. This only affects printer
* output.
*/
static int dgnc_maxcps_room(struct channel_t *ch, int bytes_available)
{
int rc = bytes_available;
if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0) {
int cps_limit = 0;
unsigned long current_time = jiffies;
unsigned long buffer_time = current_time +
(HZ * ch->ch_digi.digi_bufsize) /
ch->ch_digi.digi_maxcps;
if (ch->ch_cpstime < current_time) {
/* buffer is empty */
ch->ch_cpstime = current_time; /* reset ch_cpstime */
cps_limit = ch->ch_digi.digi_bufsize;
} else if (ch->ch_cpstime < buffer_time) {
/* still room in the buffer */
cps_limit = ((buffer_time - ch->ch_cpstime) *
ch->ch_digi.digi_maxcps) / HZ;
} else {
/* no room in the buffer */
cps_limit = 0;
}
rc = min(cps_limit, bytes_available);
}
return rc;
}
/* Return room available in Tx buffer */
static int dgnc_tty_write_room(struct tty_struct *tty)
{
struct channel_t *ch = NULL;
struct un_t *un = NULL;
ushort head;
ushort tail;
ushort tmask;
int room = 0;
unsigned long flags;
if (!tty)
return 0;
un = tty->driver_data;
if (!un)
return 0;
ch = un->un_ch;
if (!ch)
return 0;
spin_lock_irqsave(&ch->ch_lock, flags);
tmask = WQUEUEMASK;
head = (ch->ch_w_head) & tmask;
tail = (ch->ch_w_tail) & tmask;
room = tail - head - 1;
if (room < 0)
room += WQUEUESIZE;
/* Limit printer to maxcps */
if (un->un_type != DGNC_PRINT)
room = dgnc_maxcps_room(ch, room);
/*
* If we are printer device, leave room for
* possibly both the on and off strings.
*/
if (un->un_type == DGNC_PRINT) {
if (!(ch->ch_flags & CH_PRON))
room -= ch->ch_digi.digi_onlen;
room -= ch->ch_digi.digi_offlen;
} else {
if (ch->ch_flags & CH_PRON)
room -= ch->ch_digi.digi_offlen;
}
if (room < 0)
room = 0;
spin_unlock_irqrestore(&ch->ch_lock, flags);
return room;
}
/*
* Put a character into ch->ch_buf
* Used by the line discipline for OPOST processing
*/
static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c)
{
dgnc_tty_write(tty, &c, 1);
return 1;
}
/*
* Take data from the user or kernel and send it out to the FEP.
* In here exists all the Transparent Print magic as well.
*/
static int dgnc_tty_write(struct tty_struct *tty,
const unsigned char *buf, int count)
{
struct channel_t *ch = NULL;
struct un_t *un = NULL;
int bufcount = 0, n = 0;
unsigned long flags;
ushort head;
ushort tail;
ushort tmask;
uint remain;
if (!tty)
return 0;
un = tty->driver_data;
if (!un)
return 0;
ch = un->un_ch;
if (!ch)
return 0;
if (!count)
return 0;
/*
* Store original amount of characters passed in.
* This helps to figure out if we should ask the FEP
* to send us an event when it has more space available.
*/
spin_lock_irqsave(&ch->ch_lock, flags);
tmask = WQUEUEMASK;
head = (ch->ch_w_head) & tmask;
tail = (ch->ch_w_tail) & tmask;
bufcount = tail - head - 1;
if (bufcount < 0)
bufcount += WQUEUESIZE;
/*
* Limit printer output to maxcps overall, with bursts allowed
* up to bufsize characters.
*/
if (un->un_type != DGNC_PRINT)
bufcount = dgnc_maxcps_room(ch, bufcount);
count = min(count, bufcount);
if (count <= 0)
goto exit_retry;
/*
* Output the printer ON string, if we are in terminal mode, but
* need to be in printer mode.
*/
if ((un->un_type == DGNC_PRINT) && !(ch->ch_flags & CH_PRON)) {
dgnc_wmove(ch, ch->ch_digi.digi_onstr,
(int)ch->ch_digi.digi_onlen);
head = (ch->ch_w_head) & tmask;
ch->ch_flags |= CH_PRON;
}
/*
* On the other hand, output the printer OFF string, if we are
* currently in printer mode, but need to output to the terminal.
*/
if ((un->un_type != DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
dgnc_wmove(ch, ch->ch_digi.digi_offstr,
(int)ch->ch_digi.digi_offlen);
head = (ch->ch_w_head) & tmask;
ch->ch_flags &= ~CH_PRON;
}
n = count;
/*
* If the write wraps over the top of the circular buffer,
* move the portion up to the wrap point, and reset the
* pointers to the bottom.
*/
remain = WQUEUESIZE - head;
if (n >= remain) {
n -= remain;
memcpy(ch->ch_wqueue + head, buf, remain);
head = 0;
buf += remain;
}
if (n > 0) {
/* Move rest of data. */
remain = n;
memcpy(ch->ch_wqueue + head, buf, remain);
head += remain;
}
if (count) {
head &= tmask;
ch->ch_w_head = head;
}
/* Update printer buffer empty time. */
if ((un->un_type == DGNC_PRINT) && (ch->ch_digi.digi_maxcps > 0) &&
(ch->ch_digi.digi_bufsize > 0)) {
ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps;
}
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (count)
ch->ch_bd->bd_ops->copy_data_from_queue_to_uart(ch);
return count;
exit_retry:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
/* Return modem signals to ld. */
static int dgnc_tty_tiocmget(struct tty_struct *tty)
{
struct channel_t *ch;
struct un_t *un;
int rc;
unsigned char mstat = 0;
unsigned long flags;
if (!tty)
return -EIO;
un = tty->driver_data;
if (!un)
return -EIO;
ch = un->un_ch;
if (!ch)
return -EIO;
spin_lock_irqsave(&ch->ch_lock, flags);
mstat = ch->ch_mostat | ch->ch_mistat;
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = 0;
if (mstat & UART_MCR_DTR)
rc |= TIOCM_DTR;
if (mstat & UART_MCR_RTS)
rc |= TIOCM_RTS;
if (mstat & UART_MSR_CTS)
rc |= TIOCM_CTS;
if (mstat & UART_MSR_DSR)
rc |= TIOCM_DSR;
if (mstat & UART_MSR_RI)
rc |= TIOCM_RI;
if (mstat & UART_MSR_DCD)
rc |= TIOCM_CD;
return rc;
}
/* Set modem signals, called by ld. */
static int dgnc_tty_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return -EIO;
un = tty->driver_data;
if (!un)
return -EIO;
ch = un->un_ch;
if (!ch)
return -EIO;
bd = ch->ch_bd;
if (!bd)
return -EIO;
spin_lock_irqsave(&ch->ch_lock, flags);
if (set & TIOCM_RTS)
ch->ch_mostat |= UART_MCR_RTS;
if (set & TIOCM_DTR)
ch->ch_mostat |= UART_MCR_DTR;
if (clear & TIOCM_RTS)
ch->ch_mostat &= ~(UART_MCR_RTS);
if (clear & TIOCM_DTR)
ch->ch_mostat &= ~(UART_MCR_DTR);
bd->bd_ops->assert_modem_signals(ch);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
/* Send a Break, called by ld. */
static int dgnc_tty_send_break(struct tty_struct *tty, int msec)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return -EIO;
un = tty->driver_data;
if (!un)
return -EIO;
ch = un->un_ch;
if (!ch)
return -EIO;
bd = ch->ch_bd;
if (!bd)
return -EIO;
if (msec < 0)
msec = 0xFFFF;
spin_lock_irqsave(&ch->ch_lock, flags);
bd->bd_ops->send_break(ch, msec);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
/* wait until data has been transmitted, called by ld. */
static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
bd->bd_ops->drain(tty, 0);
}
/* send a high priority character, called by ld. */
static void dgnc_tty_send_xchar(struct tty_struct *tty, char c)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
bd->bd_ops->send_immediate_char(ch, c);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
/* Return modem signals to ld. */
static inline int dgnc_get_mstat(struct channel_t *ch)
{
unsigned char mstat;
unsigned long flags;
int rc;
if (!ch)
return -ENXIO;
spin_lock_irqsave(&ch->ch_lock, flags);
mstat = ch->ch_mostat | ch->ch_mistat;
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = 0;
if (mstat & UART_MCR_DTR)
rc |= TIOCM_DTR;
if (mstat & UART_MCR_RTS)
rc |= TIOCM_RTS;
if (mstat & UART_MSR_CTS)
rc |= TIOCM_CTS;
if (mstat & UART_MSR_DSR)
rc |= TIOCM_DSR;
if (mstat & UART_MSR_RI)
rc |= TIOCM_RI;
if (mstat & UART_MSR_DCD)
rc |= TIOCM_CD;
return rc;
}
/* Return modem signals to ld. */
static int dgnc_get_modem_info(struct channel_t *ch,
unsigned int __user *value)
{
return put_user(dgnc_get_mstat(ch), value);
}
/* Set modem signals, called by ld. */
static int dgnc_set_modem_info(struct channel_t *ch,
unsigned int command,
unsigned int __user *value)
{
int rc;
unsigned int arg = 0;
unsigned long flags;
rc = get_user(arg, value);
if (rc)
return rc;
switch (command) {
case TIOCMBIS:
if (arg & TIOCM_RTS)
ch->ch_mostat |= UART_MCR_RTS;
if (arg & TIOCM_DTR)
ch->ch_mostat |= UART_MCR_DTR;
break;
case TIOCMBIC:
if (arg & TIOCM_RTS)
ch->ch_mostat &= ~(UART_MCR_RTS);
if (arg & TIOCM_DTR)
ch->ch_mostat &= ~(UART_MCR_DTR);
break;
case TIOCMSET:
if (arg & TIOCM_RTS)
ch->ch_mostat |= UART_MCR_RTS;
else
ch->ch_mostat &= ~(UART_MCR_RTS);
if (arg & TIOCM_DTR)
ch->ch_mostat |= UART_MCR_DTR;
else
ch->ch_mostat &= ~(UART_MCR_DTR);
break;
default:
return -EINVAL;
}
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_bd->bd_ops->assert_modem_signals(ch);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
/* Ioctl to get the information for ditty. */
static int dgnc_tty_digigeta(struct tty_struct *tty,
struct digi_t __user *retinfo)
{
struct channel_t *ch;
struct un_t *un;
struct digi_t tmp;
unsigned long flags;
if (!retinfo)
return -EFAULT;
if (!tty)
return -EFAULT;
un = tty->driver_data;
if (!un)
return -EFAULT;
ch = un->un_ch;
if (!ch)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
spin_lock_irqsave(&ch->ch_lock, flags);
memcpy(&tmp, &ch->ch_digi, sizeof(tmp));
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
/* Ioctl to set the information for ditty. */
static int dgnc_tty_digiseta(struct tty_struct *tty,
struct digi_t __user *new_info)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
struct digi_t new_digi;
unsigned long flags;
if (!tty)
return -EFAULT;
un = tty->driver_data;
if (!un)
return -EFAULT;
ch = un->un_ch;
if (!ch)
return -EFAULT;
bd = ch->ch_bd;
if (!bd)
return -EFAULT;
if (copy_from_user(&new_digi, new_info, sizeof(new_digi)))
return -EFAULT;
spin_lock_irqsave(&ch->ch_lock, flags);
/* Handle transitions to and from RTS Toggle. */
if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) &&
(new_digi.digi_flags & DIGI_RTS_TOGGLE))
ch->ch_mostat &= ~(UART_MCR_RTS);
if ((ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) &&
!(new_digi.digi_flags & DIGI_RTS_TOGGLE))
ch->ch_mostat |= (UART_MCR_RTS);
/* Handle transitions to and from DTR Toggle. */
if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) &&
(new_digi.digi_flags & DIGI_DTR_TOGGLE))
ch->ch_mostat &= ~(UART_MCR_DTR);
if ((ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) &&
!(new_digi.digi_flags & DIGI_DTR_TOGGLE))
ch->ch_mostat |= (UART_MCR_DTR);
memcpy(&ch->ch_digi, &new_digi, sizeof(new_digi));
if (ch->ch_digi.digi_maxcps < 1)
ch->ch_digi.digi_maxcps = 1;
if (ch->ch_digi.digi_maxcps > 10000)
ch->ch_digi.digi_maxcps = 10000;
if (ch->ch_digi.digi_bufsize < 10)
ch->ch_digi.digi_bufsize = 10;
if (ch->ch_digi.digi_maxchar < 1)
ch->ch_digi.digi_maxchar = 1;
if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize)
ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize;
if (ch->ch_digi.digi_onlen > DIGI_PLEN)
ch->ch_digi.digi_onlen = DIGI_PLEN;
if (ch->ch_digi.digi_offlen > DIGI_PLEN)
ch->ch_digi.digi_offlen = DIGI_PLEN;
bd->bd_ops->param(tty);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
static void dgnc_tty_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_c_cflag = tty->termios.c_cflag;
ch->ch_c_iflag = tty->termios.c_iflag;
ch->ch_c_oflag = tty->termios.c_oflag;
ch->ch_c_lflag = tty->termios.c_lflag;
ch->ch_startc = tty->termios.c_cc[VSTART];
ch->ch_stopc = tty->termios.c_cc[VSTOP];
bd->bd_ops->param(tty);
dgnc_carrier(ch);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
static void dgnc_tty_throttle(struct tty_struct *tty)
{
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags |= (CH_FORCED_STOPI);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
static void dgnc_tty_unthrottle(struct tty_struct *tty)
{
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags &= ~(CH_FORCED_STOPI);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
static void dgnc_tty_start(struct tty_struct *tty)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags &= ~(CH_FORCED_STOP);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
static void dgnc_tty_stop(struct tty_struct *tty)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags |= (CH_FORCED_STOP);
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
/*
* Flush the cook buffer
*
* Note to self, and any other poor souls who venture here:
*
* flush in this case DOES NOT mean dispose of the data.
* instead, it means "stop buffering and send it if you
* haven't already." Just guess how I figured that out... SRW 2-Jun-98
*
* It is also always called in interrupt context - JAR 8-Sept-99
*/
static void dgnc_tty_flush_chars(struct tty_struct *tty)
{
struct dgnc_board *bd;
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
bd = ch->ch_bd;
if (!bd)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
/* Do something maybe here */
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
/* Flush Tx buffer (make in == out) */
static void dgnc_tty_flush_buffer(struct tty_struct *tty)
{
struct channel_t *ch;
struct un_t *un;
unsigned long flags;
if (!tty)
return;
un = tty->driver_data;
if (!un)
return;
ch = un->un_ch;
if (!ch)
return;
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags &= ~CH_STOP;
/* Flush our write queue */
ch->ch_w_head = ch->ch_w_tail;
/* Flush UARTs transmit FIFO */
ch->ch_bd->bd_ops->flush_uart_write(ch);
if (ch->ch_tun.un_flags & (UN_LOW | UN_EMPTY)) {
ch->ch_tun.un_flags &= ~(UN_LOW | UN_EMPTY);
wake_up_interruptible(&ch->ch_tun.un_flags_wait);
}
if (ch->ch_pun.un_flags & (UN_LOW | UN_EMPTY)) {
ch->ch_pun.un_flags &= ~(UN_LOW | UN_EMPTY);
wake_up_interruptible(&ch->ch_pun.un_flags_wait);
}
spin_unlock_irqrestore(&ch->ch_lock, flags);
}
/* Wakes up processes waiting in the unit's (teminal/printer) wait queue */
static void dgnc_wake_up_unit(struct un_t *unit)
{
unit->un_flags &= ~(UN_LOW | UN_EMPTY);
wake_up_interruptible(&unit->un_flags_wait);
}
/* The IOCTL function and all of its helpers */
/* The usual assortment of ioctl's */
static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
struct dgnc_board *bd;
struct board_ops *ch_bd_ops;
struct channel_t *ch;
struct un_t *un;
int rc;
unsigned long flags;
void __user *uarg = (void __user *)arg;
if (!tty)
return -ENODEV;
un = tty->driver_data;
if (!un)
return -ENODEV;
ch = un->un_ch;
if (!ch)
return -ENODEV;
bd = ch->ch_bd;
if (!bd)
return -ENODEV;
ch_bd_ops = bd->bd_ops;
spin_lock_irqsave(&ch->ch_lock, flags);
if (un->un_open_count <= 0) {
rc = -EIO;
goto err_unlock;
}
switch (cmd) {
/* Here are all the standard ioctl's that we MUST implement */
case TCSBRK:
/*
* TCSBRK is SVID version: non-zero arg --> no break
* this behaviour is exploited by tcdrain().
*
* According to POSIX.1 spec (7.2.2.1.2) breaks should be
* between 0.25 and 0.5 seconds so we'll ask for something
* in the middle: 0.375 seconds.
*/
rc = tty_check_change(tty);
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (rc)
return rc;
rc = ch_bd_ops->drain(tty, 0);
if (rc)
return -EINTR;
spin_lock_irqsave(&ch->ch_lock, flags);
if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP))
ch_bd_ops->send_break(ch, 250);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
case TCSBRKP:
/*
* support for POSIX tcsendbreak()
* According to POSIX.1 spec (7.2.2.1.2) breaks should be
* between 0.25 and 0.5 seconds so we'll ask for something
* in the middle: 0.375 seconds.
*/
rc = tty_check_change(tty);
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (rc)
return rc;
rc = ch_bd_ops->drain(tty, 0);
if (rc)
return -EINTR;
spin_lock_irqsave(&ch->ch_lock, flags);
ch_bd_ops->send_break(ch, 250);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
case TIOCSBRK:
rc = tty_check_change(tty);
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (rc)
return rc;
rc = ch_bd_ops->drain(tty, 0);
if (rc)
return -EINTR;
spin_lock_irqsave(&ch->ch_lock, flags);
ch_bd_ops->send_break(ch, 250);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
case TIOCCBRK:
/* Do Nothing */
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
case TIOCGSOFTCAR:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return put_user(C_CLOCAL(tty) ? 1 : 0,
(unsigned long __user *)arg);
case TIOCSSOFTCAR:
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = get_user(arg, (unsigned long __user *)arg);
if (rc)
return rc;
spin_lock_irqsave(&ch->ch_lock, flags);
tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
ch_bd_ops->param(tty);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
case TIOCMGET:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return dgnc_get_modem_info(ch, uarg);
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return dgnc_set_modem_info(ch, cmd, uarg);
/* Here are any additional ioctl's that we want to implement */
case TCFLSH:
/*
* The linux tty driver doesn't have a flush
* input routine for the driver, assuming all backed
* up data is in the line disc. buffers. However,
* we all know that's not the case. Here, we
* act on the ioctl, but then lie and say we didn't
* so the line discipline will process the flush
* also.
*/
rc = tty_check_change(tty);
if (rc)
goto err_unlock;
if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) {
ch->ch_r_head = ch->ch_r_tail;
ch_bd_ops->flush_uart_read(ch);
/* Force queue flow control to be released, if needed */
dgnc_check_queue_flow_control(ch);
}
if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) {
if (!(un->un_type == DGNC_PRINT)) {
ch->ch_w_head = ch->ch_w_tail;
ch_bd_ops->flush_uart_write(ch);
if (ch->ch_tun.un_flags & (UN_LOW | UN_EMPTY))
dgnc_wake_up_unit(&ch->ch_tun);
if (ch->ch_pun.un_flags & (UN_LOW | UN_EMPTY))
dgnc_wake_up_unit(&ch->ch_pun);
}
}
/* pretend we didn't recognize this IOCTL */
spin_unlock_irqrestore(&ch->ch_lock, flags);
return -ENOIOCTLCMD;
case TCSETSF:
case TCSETSW:
/*
* The linux tty driver doesn't have a flush
* input routine for the driver, assuming all backed
* up data is in the line disc. buffers. However,
* we all know that's not the case. Here, we
* act on the ioctl, but then lie and say we didn't
* so the line discipline will process the flush
* also.
*/
if (cmd == TCSETSF) {
/* flush rx */
ch->ch_flags &= ~CH_STOP;
ch->ch_r_head = ch->ch_r_tail;
ch_bd_ops->flush_uart_read(ch);
/* Force queue flow control to be released, if needed */
dgnc_check_queue_flow_control(ch);
}
/* now wait for all the output to drain */
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = ch_bd_ops->drain(tty, 0);
if (rc)
return -EINTR;
/* pretend we didn't recognize this */
return -ENOIOCTLCMD;
case TCSETAW:
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = ch_bd_ops->drain(tty, 0);
if (rc)
return -EINTR;
/* pretend we didn't recognize this */
return -ENOIOCTLCMD;
case TCXONC:
spin_unlock_irqrestore(&ch->ch_lock, flags);
/* Make the ld do it */
return -ENOIOCTLCMD;
case DIGI_GETA:
/* get information for ditty */
spin_unlock_irqrestore(&ch->ch_lock, flags);
return dgnc_tty_digigeta(tty, uarg);
case DIGI_SETAW:
case DIGI_SETAF:
/* set information for ditty */
if (cmd == (DIGI_SETAW)) {
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = ch_bd_ops->drain(tty, 0);
if (rc)
return -EINTR;
spin_lock_irqsave(&ch->ch_lock, flags);
} else {
tty_ldisc_flush(tty);
}
/* fall thru */
case DIGI_SETA:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return dgnc_tty_digiseta(tty, uarg);
case DIGI_LOOPBACK:
{
uint loopback = 0;
/*
* Let go of locks when accessing user space,
* could sleep
*/
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = get_user(loopback, (unsigned int __user *)arg);
if (rc)
return rc;
spin_lock_irqsave(&ch->ch_lock, flags);
/* Enable/disable internal loopback for this port */
if (loopback)
ch->ch_flags |= CH_LOOPBACK;
else
ch->ch_flags &= ~(CH_LOOPBACK);
ch_bd_ops->param(tty);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
case DIGI_GETCUSTOMBAUD:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return put_user(ch->ch_custom_speed,
(unsigned int __user *)arg);
case DIGI_SETCUSTOMBAUD:
{
int new_rate;
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = get_user(new_rate, (int __user *)arg);
if (rc)
return rc;
spin_lock_irqsave(&ch->ch_lock, flags);
dgnc_set_custom_speed(ch, new_rate);
ch_bd_ops->param(tty);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
/*
* This ioctl allows insertion of a character into the front
* of any pending data to be transmitted.
*
* This ioctl is to satisfy the "Send Character Immediate"
* call that the RealPort protocol spec requires.
*/
case DIGI_REALPORT_SENDIMMEDIATE:
{
unsigned char c;
spin_unlock_irqrestore(&ch->ch_lock, flags);
rc = get_user(c, (unsigned char __user *)arg);
if (rc)
return rc;
spin_lock_irqsave(&ch->ch_lock, flags);
ch_bd_ops->send_immediate_char(ch, c);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return 0;
}
/*
* This ioctl returns all the current counts for the port.
*
* This ioctl is to satisfy the "Line Error Counters"
* call that the RealPort protocol spec requires.
*/
case DIGI_REALPORT_GETCOUNTERS:
{
struct digi_getcounter buf;
buf.norun = ch->ch_err_overrun;
buf.noflow = 0; /* The driver doesn't keep this stat */
buf.nframe = ch->ch_err_frame;
buf.nparity = ch->ch_err_parity;
buf.nbreak = ch->ch_err_break;
buf.rbytes = ch->ch_rxcount;
buf.tbytes = ch->ch_txcount;
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (copy_to_user(uarg, &buf, sizeof(buf)))
return -EFAULT;
return 0;
}
/*
* This ioctl returns all current events.
*
* This ioctl is to satisfy the "Event Reporting"
* call that the RealPort protocol spec requires.
*/
case DIGI_REALPORT_GETEVENTS:
{
unsigned int events = 0;
/* NOTE: MORE EVENTS NEEDS TO BE ADDED HERE */
if (ch->ch_flags & CH_BREAK_SENDING)
events |= EV_TXB;
if ((ch->ch_flags & CH_STOP) ||
(ch->ch_flags & CH_FORCED_STOP))
events |= (EV_OPU | EV_OPS);
if ((ch->ch_flags & CH_STOPI) ||
(ch->ch_flags & CH_FORCED_STOPI))
events |= (EV_IPU | EV_IPS);
spin_unlock_irqrestore(&ch->ch_lock, flags);
return put_user(events, (unsigned int __user *)arg);
}
/*
* This ioctl returns TOUT and TIN counters based
* upon the values passed in by the RealPort Server.
* It also passes back whether the UART Transmitter is
* empty as well.
*/
case DIGI_REALPORT_GETBUFFERS:
{
struct digi_getbuffer buf;
int tdist;
int count;
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (copy_from_user(&buf, uarg, sizeof(buf)))
return -EFAULT;
spin_lock_irqsave(&ch->ch_lock, flags);
/* Figure out how much data is in our RX and TX queues. */
buf.rxbuf = (ch->ch_r_head - ch->ch_r_tail) & RQUEUEMASK;
buf.txbuf = (ch->ch_w_head - ch->ch_w_tail) & WQUEUEMASK;
/*
* Is the UART empty?
* Add that value to whats in our TX queue.
*/
count = buf.txbuf + ch_bd_ops->get_uart_bytes_left(ch);
/*
* Figure out how much data the RealPort Server believes should
* be in our TX queue.
*/
tdist = (buf.tx_in - buf.tx_out) & 0xffff;
/*
* If we have more data than the RealPort Server believes we
* should have, reduce our count to its amount.
*
* This count difference CAN happen because the Linux LD can
* insert more characters into our queue for OPOST processing
* that the RealPort Server doesn't know about.
*/
if (buf.txbuf > tdist)
buf.txbuf = tdist;
/* Report whether our queue and UART TX are completely empty. */
if (count)
buf.txdone = 0;
else
buf.txdone = 1;
spin_unlock_irqrestore(&ch->ch_lock, flags);
if (copy_to_user(uarg, &buf, sizeof(buf)))
return -EFAULT;
return 0;
}
default:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return -ENOIOCTLCMD;
}
err_unlock:
spin_unlock_irqrestore(&ch->ch_lock, flags);
return rc;
}
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*/
#ifndef _DGNC_TTY_H
#define _DGNC_TTY_H
#include "dgnc_driver.h"
int dgnc_tty_register(struct dgnc_board *brd);
void dgnc_tty_unregister(struct dgnc_board *brd);
int dgnc_tty_init(struct dgnc_board *brd);
void dgnc_cleanup_tty(struct dgnc_board *brd);
void dgnc_input(struct channel_t *ch);
void dgnc_carrier(struct channel_t *ch);
void dgnc_wakeup_writes(struct channel_t *ch);
void dgnc_check_queue_flow_control(struct channel_t *ch);
#endif /* _DGNC_TTY_H */
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*/
#ifndef _DIGI_H
#define _DIGI_H
#define DIGI_GETA (('e' << 8) | 94) /* Read params */
#define DIGI_SETA (('e' << 8) | 95) /* Set params */
#define DIGI_SETAW (('e' << 8) | 96) /* Drain & set params */
#define DIGI_SETAF (('e' << 8) | 97) /* Drain, flush & set params */
#define DIGI_LOOPBACK (('d' << 8) | 252) /* Enable/disable UART
* internal loopback
*/
#define DIGI_FAST 0x0002 /* Fast baud rates */
#define RTSPACE 0x0004 /* RTS input flow control */
#define CTSPACE 0x0008 /* CTS output flow control */
#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */
#define DIGI_FORCEDCD 0x0100 /* Force carrier */
#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl*/
#define DIGI_DTR_TOGGLE 0x2000 /* Support DTR Toggle */
#define DIGI_RTS_TOGGLE 0x8000 /* Support RTS Toggle */
#define DIGI_PLEN 28 /* String length */
#define DIGI_TSIZ 10 /* Terminal string len */
/*
* Structure used with ioctl commands for DIGI parameters.
*/
/**
* struct digi_t - Ioctl commands for DIGI parameters.
* @digi_flags: Flags.
* @digi_maxcps: Maximum printer CPS.
* @digi_maxchar: Maximum characters in the print queue.
* @digi_bufsize: Buffer size.
* @digi_onlen: Length of ON string.
* @digi_offlen: Length of OFF string.
* @digi_onstr: Printer ON string.
* @digi_offstr: Printer OFF string.
* @digi_term: Terminal string.
*/
struct digi_t {
unsigned short digi_flags;
unsigned short digi_maxcps;
unsigned short digi_maxchar;
unsigned short digi_bufsize;
unsigned char digi_onlen;
unsigned char digi_offlen;
char digi_onstr[DIGI_PLEN];
char digi_offstr[DIGI_PLEN];
char digi_term[DIGI_TSIZ];
};
/**
* struct digi_getbuffer - Holds buffer use counts.
*/
struct digi_getbuffer {
unsigned long tx_in;
unsigned long tx_out;
unsigned long rxbuf;
unsigned long txbuf;
unsigned long txdone;
};
/**
* struct digi_getcounter
* @norun: Number of UART overrun errors.
* @noflow: Number of buffer overflow errors.
* @nframe: Number of framing errors.
* @nparity: Number of parity errors.
* @nbreak: Number of breaks received.
* @rbytes: Number of received bytes.
* @tbytes: Number of transmitted bytes.
*/
struct digi_getcounter {
unsigned long norun;
unsigned long noflow;
unsigned long nframe;
unsigned long nparity;
unsigned long nbreak;
unsigned long rbytes;
unsigned long tbytes;
};
#define DIGI_SETCUSTOMBAUD _IOW('e', 106, int) /* Set integer baud rate */
#define DIGI_GETCUSTOMBAUD _IOR('e', 107, int) /* Get integer baud rate */
#define DIGI_REALPORT_GETBUFFERS (('e' << 8) | 108)
#define DIGI_REALPORT_SENDIMMEDIATE (('e' << 8) | 109)
#define DIGI_REALPORT_GETCOUNTERS (('e' << 8) | 110)
#define DIGI_REALPORT_GETEVENTS (('e' << 8) | 111)
#define EV_OPU 0x0001 /* Output paused by client */
#define EV_OPS 0x0002 /* Output paused by regular sw flowctrl */
#define EV_IPU 0x0010 /* Input paused unconditionally by user */
#define EV_IPS 0x0020 /* Input paused by high/low water marks */
#define EV_TXB 0x0040 /* Transmit break pending */
/**
* struct ni_info - intelligent <--> non-intelligent DPA translation.
*/
struct ni_info {
int board;
int channel;
int dtr;
int rts;
int cts;
int dsr;
int ri;
int dcd;
int curtx;
int currx;
unsigned short iflag;
unsigned short oflag;
unsigned short cflag;
unsigned short lflag;
unsigned int mstat;
unsigned char hflow;
unsigned char xmit_stopped;
unsigned char recv_stopped;
unsigned int baud;
};
#define TTY_FLIPBUF_SIZE 512
#endif /* _DIGI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment