📄 mx21_16c2552.c
字号:
free_page(page);
else
info->xmit.buf = (unsigned char *) page;
/*
* Allocate the IRQ
*/
retval = request_irq(info->port->irq, ambauart_int, SA_SHIRQ, "mx21_16c2552", info);
if (retval) {
if (capable(CAP_SYS_ADMIN)) {
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
retval = 0;
}
goto errout;
}
info->mctrl = 0;
if (info->tty->termios->c_cflag & CBAUD)
info->mctrl = TIOCM_RTS | TIOCM_DTR;
info->port->set_mctrl(info->port, info->mctrl);
/*
* initialise the old status of the modem signals
*/
info->old_status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY;
/*
* Finally, enable interrupts
*/
UART_PUT_CR(info->port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE |
AMBA_UARTCR_RTIE);
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit.head = info->xmit.tail = 0;
/*
* Set up the tty->alt_speed kludge
*/
if (info->tty) {
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
info->tty->alt_speed = 57600;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
info->tty->alt_speed = 115200;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
info->tty->alt_speed = 230400;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
info->tty->alt_speed = 460800;
}
/*
* and set the speed of the serial port
*/
ambauart_change_speed(info, NULL);
info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
return 0;
errout:
restore_flags(flags);
return retval;
}
/*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
static void ambauart_shutdown(struct amba_info *info)
{
unsigned long flags;
if (!(info->flags & ASYNC_INITIALIZED))
return;
save_flags(flags); cli(); /* Disable interrupts */
/*
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
* here so the queue might never be woken up
*/
wake_up_interruptible(&info->delta_msr_wait);
/*
* Free the IRQ
*/
free_irq(info->port->irq, info);
if (info->xmit.buf) {
unsigned long pg = (unsigned long) info->xmit.buf;
info->xmit.buf = NULL;
free_page(pg);
}
/*
* disable all interrupts, disable the port
*/
UART_PUT_CR(info->port, 0);
/* disable break condition and fifos */
UART_PUT_LCRH(info->port, UART_GET_LCRH(info->port) &
~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN));
if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS);
info->port->set_mctrl(info->port, info->mctrl);
/* kill off our tasklet */
tasklet_kill(&info->tlet);
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
}
static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios)
{
unsigned int lcr_h, baud, quot, cflag, old_cr, bits;
unsigned long flags;
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
#if DEBUG
printk("ambauart_set_cflag(0x%x) called\n", cflag);
#endif
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5: lcr_h = AMBA_UARTLCR_H_WLEN_5; bits = 7; break;
case CS6: lcr_h = AMBA_UARTLCR_H_WLEN_6; bits = 8; break;
case CS7: lcr_h = AMBA_UARTLCR_H_WLEN_7; bits = 9; break;
default: lcr_h = AMBA_UARTLCR_H_WLEN_8; bits = 10; break; // CS8
}
if (cflag & CSTOPB) {
lcr_h |= AMBA_UARTLCR_H_STP2;
bits ++;
}
if (cflag & PARENB) {
lcr_h |= AMBA_UARTLCR_H_PEN;
bits++;
if (!(cflag & PARODD))
lcr_h |= AMBA_UARTLCR_H_EPS;
}
if (info->port->fifosize > 1)
lcr_h |= AMBA_UARTLCR_H_FEN;
do {
/* Determine divisor based on baud rate */
baud = tty_get_baud_rate(info->tty);
if (!baud)
baud = 9600;
if (baud == 38400 &&
((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
quot = info->state->custom_divisor;
else
quot = (info->port->uartclk / (16 * baud)) - 1;
if (!quot && old_termios) {
info->tty->termios->c_cflag &= ~CBAUD;
info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
old_termios = NULL;
}
} while (quot == 0 && old_termios);
/* As a last resort, if the quotient is zero, default to 9600 bps */
if (!quot)
quot = (info->port->uartclk / (16 * 9600)) - 1;
info->timeout = (info->port->fifosize * HZ * bits * quot) /
(info->port->uartclk / 16);
info->timeout += HZ/50; /* Add .02 seconds of slop */
if (cflag & CRTSCTS)
info->flags |= ASYNC_CTS_FLOW;
else
info->flags &= ~ASYNC_CTS_FLOW;
if (cflag & CLOCAL)
info->flags &= ~ASYNC_CHECK_CD;
else
info->flags |= ASYNC_CHECK_CD;
/*
* Set up parity check flag
*/
#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
info->read_status_mask = AMBA_UARTRSR_OE;
if (I_INPCK(info->tty))
info->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
info->read_status_mask |= AMBA_UARTRSR_BE;
/*
* Characters to ignore
*/
info->ignore_status_mask = 0;
if (I_IGNPAR(info->tty))
info->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
if (I_IGNBRK(info->tty)) {
info->ignore_status_mask |= AMBA_UARTRSR_BE;
/*
* If we're ignoring parity and break indicators,
* ignore overruns to (for real raw support).
*/
if (I_IGNPAR(info->tty))
info->ignore_status_mask |= AMBA_UARTRSR_OE;
}
/* first, disable everything */
save_flags(flags); cli();
// old_cr = UART_GET_CR(info->port) &= ~AMBA_UARTCR_MSIE;
old_cr = UART_GET_CR(info->port);
UART_PUT_CR(info->port, old_cr & ~AMBA_UARTCR_MSIE);
if ((info->flags & ASYNC_HARDPPS_CD) ||
(cflag & CRTSCTS) ||
!(cflag & CLOCAL))
old_cr |= AMBA_UARTCR_MSIE;
UART_PUT_CR(info->port, 0);
/* Set baud rate */
// MX1ADS, need to +1 for the division factor
// UART_PUT_LCRM(info->port, ((quot & 0xf00) >> 8));
// UART_PUT_LCRL(info->port, (quot & 0xff));
UART_PUT_LCRM(info->port, (((quot+1) & 0xf00) >> 8));
UART_PUT_LCRL(info->port, ((quot+1) & 0xff));
/*
* ----------v----------v----------v----------v-----
* NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
* ----------^----------^----------^----------^-----
*/
UART_PUT_LCRH(info->port, lcr_h);
UART_PUT_CR(info->port, old_cr);
restore_flags(flags);
}
static void ambauart_put_char(struct tty_struct *tty, u_char ch)
{
struct amba_info *info = tty->driver_data;
unsigned long flags;
// MX1ADS
//printk("(P)");
if (!tty || !info->xmit.buf)
return;
save_flags(flags); cli();
if (CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE) != 0) {
info->xmit.buf[info->xmit.head] = ch;
info->xmit.head = (info->xmit.head + 1) & (AMBA_XMIT_SIZE - 1);
}
restore_flags(flags);
}
static void ambauart_flush_chars(struct tty_struct *tty)
{
struct amba_info *info = tty->driver_data;
unsigned long flags;
if (info->xmit.head == info->xmit.tail
|| tty->stopped
|| tty->hw_stopped
|| !info->xmit.buf)
return;
save_flags(flags); cli();
ambauart_enable_tx_interrupt(info);
restore_flags(flags);
}
static int ambauart_write(struct tty_struct *tty, int from_user,
const u_char * buf, int count)
{
struct amba_info *info = tty->driver_data;
unsigned long flags;
int c, ret = 0;
// MX1ADS
//printk("(W)");
if (!tty || !info->xmit.buf || !tmp_buf)
return 0;
save_flags(flags);
if (from_user) {
down(&tmp_buf_sem);
while (1) {
int c1;
c = CIRC_SPACE_TO_END(info->xmit.head,
info->xmit.tail,
AMBA_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
c -= copy_from_user(tmp_buf, buf, c);
if (!c) {
if (!ret)
ret = -EFAULT;
break;
}
cli();
c1 = CIRC_SPACE_TO_END(info->xmit.head,
info->xmit.tail,
AMBA_XMIT_SIZE);
if (c1 < c)
c = c1;
memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
info->xmit.head = (info->xmit.head + c) &
(AMBA_XMIT_SIZE - 1);
restore_flags(flags);
buf += c;
count -= c;
ret += c;
}
up(&tmp_buf_sem);
} else {
cli();
while (1) {
c = CIRC_SPACE_TO_END(info->xmit.head,
info->xmit.tail,
AMBA_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
memcpy(info->xmit.buf + info->xmit.head, buf, c);
info->xmit.head = (info->xmit.head + c) &
(AMBA_XMIT_SIZE - 1);
buf += c;
count -= c;
ret += c;
}
restore_flags(flags);
}
if (info->xmit.head != info->xmit.tail
&& !tty->stopped
&& !tty->hw_stopped)
ambauart_enable_tx_interrupt(info);
return ret;
}
static int ambauart_write_room(struct tty_struct *tty)
{
struct amba_info *info = tty->driver_data;
return CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);
}
static int ambauart_chars_in_buffer(struct tty_struct *tty)
{
struct amba_info *info = tty->driver_data;
return CIRC_CNT(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);
}
static void ambauart_flush_buffer(struct tty_struct *tty)
{
struct amba_info *info = tty->driver_data;
unsigned long flags;
#if DEBUG
printk("ambauart_flush_buffer(%d) called\n",
MINOR(tty->device) - tty->driver.minor_start);
#endif
save_flags(flags); cli();
info->xmit.head = info->xmit.tail = 0;
restore_flags(flags);
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
}
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static void ambauart_send_xchar(struct tty_struct *tty, char ch)
{
struct amba_info *info = tty->driver_data;
info->x_char = ch;
if (ch)
ambauart_enable_tx_interrupt(info);
}
static void ambauart_throttle(struct tty_struct *tty)
{
struct amba_info *info = tty->driver_data;
unsigned long flags;
if (I_IXOFF(tty))
ambauart_send_xchar(tty, STOP_CHAR(tty));
if (tty->termios->c_cflag & CRTSCTS) {
save_flags(flags); cli();
info->mctrl &= ~TIOCM_RTS;
info->port->set_mctrl(info->port, info->mctrl);
restore_flags(flags);
}
}
static void ambauart_unthrottle(struct tty_struct *tty)
{
struct amba_info *info = (struct amba_info *) tty->driver_data;
unsigned long flags;
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
else
ambauart_send_xchar(tty, START_CHAR(tty));
}
if (tty->termios->c_cflag & CRTSCTS) {
save_flags(flags); cli();
info->mctrl |= TIOCM_RTS;
info->port->set_mctrl(info->port, info->mctrl);
restore_flags(flags);
}
}
static int get_serial_info(struct amba_info *info, struct serial_struct *retinfo)
{
struct amba_state *state = info->state;
struct amba_port *port = info->port;
struct serial_struct tmp;
memset(&tmp, 0, sizeof(tmp));
tmp.type = 0;
tmp.line = state->line;
tmp.port = port->uart_base;
if (HIGH_BITS_OFFSET)
tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET;
tmp.irq = port->irq;
tmp.flags = 0;
tmp.xmit_fifo_size = port->fifosize;
tmp.baud_base = port->uartclk / 16;
tmp.close_delay = state->close_delay;
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -