18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/************************************************************************ 38c2ecf20Sopenharmony_ci * Copyright 2003 Digi International (www.digi.com) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 IBM Corporation. All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact Information: 88c2ecf20Sopenharmony_ci * Scott H Kilau <Scott_Kilau@digi.com> 98c2ecf20Sopenharmony_ci * Ananda Venkatarman <mansarov@us.ibm.com> 108c2ecf20Sopenharmony_ci * Modifications: 118c2ecf20Sopenharmony_ci * 01/19/06: changed jsm_input routine to use the dynamically allocated 128c2ecf20Sopenharmony_ci * tty_buffer changes. Contributors: Scott Kilau and Ananda V. 138c2ecf20Sopenharmony_ci ***********************************************************************/ 148c2ecf20Sopenharmony_ci#include <linux/tty.h> 158c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 168c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> /* For udelay */ 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "jsm.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic DECLARE_BITMAP(linemap, MAXLINES); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void jsm_carrier(struct jsm_channel *ch); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic inline int jsm_get_mstat(struct jsm_channel *ch) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned char mstat; 308c2ecf20Sopenharmony_ci int result; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "start\n"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci mstat = (ch->ch_mostat | ch->ch_mistat); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci result = 0; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (mstat & UART_MCR_DTR) 398c2ecf20Sopenharmony_ci result |= TIOCM_DTR; 408c2ecf20Sopenharmony_ci if (mstat & UART_MCR_RTS) 418c2ecf20Sopenharmony_ci result |= TIOCM_RTS; 428c2ecf20Sopenharmony_ci if (mstat & UART_MSR_CTS) 438c2ecf20Sopenharmony_ci result |= TIOCM_CTS; 448c2ecf20Sopenharmony_ci if (mstat & UART_MSR_DSR) 458c2ecf20Sopenharmony_ci result |= TIOCM_DSR; 468c2ecf20Sopenharmony_ci if (mstat & UART_MSR_RI) 478c2ecf20Sopenharmony_ci result |= TIOCM_RI; 488c2ecf20Sopenharmony_ci if (mstat & UART_MSR_DCD) 498c2ecf20Sopenharmony_ci result |= TIOCM_CD; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); 528c2ecf20Sopenharmony_ci return result; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic unsigned int jsm_tty_tx_empty(struct uart_port *port) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return TIOCSER_TEMT; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Return modem signals to ld. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic unsigned int jsm_tty_get_mctrl(struct uart_port *port) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int result; 668c2ecf20Sopenharmony_ci struct jsm_channel *channel = 678c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci result = jsm_get_mstat(channel); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (result < 0) 748c2ecf20Sopenharmony_ci return -ENXIO; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return result; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * jsm_set_modem_info() 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Set modem signals, called by ld. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct jsm_channel *channel = 898c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (mctrl & TIOCM_RTS) 948c2ecf20Sopenharmony_ci channel->ch_mostat |= UART_MCR_RTS; 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci channel->ch_mostat &= ~UART_MCR_RTS; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (mctrl & TIOCM_DTR) 998c2ecf20Sopenharmony_ci channel->ch_mostat |= UART_MCR_DTR; 1008c2ecf20Sopenharmony_ci else 1018c2ecf20Sopenharmony_ci channel->ch_mostat &= ~UART_MCR_DTR; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->assert_modem_signals(channel); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 1068c2ecf20Sopenharmony_ci udelay(10); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* 1108c2ecf20Sopenharmony_ci * jsm_tty_write() 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * Take data from the user or kernel and send it out to the FEP. 1138c2ecf20Sopenharmony_ci * In here exists all the Transparent Print magic as well. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic void jsm_tty_write(struct uart_port *port) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct jsm_channel *channel; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci channel = container_of(port, struct jsm_channel, uart_port); 1208c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void jsm_tty_start_tx(struct uart_port *port) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct jsm_channel *channel = 1268c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci channel->ch_flags &= ~(CH_STOP); 1318c2ecf20Sopenharmony_ci jsm_tty_write(port); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void jsm_tty_stop_tx(struct uart_port *port) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct jsm_channel *channel = 1398c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci channel->ch_flags |= (CH_STOP); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void jsm_tty_send_xchar(struct uart_port *port, char ch) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci unsigned long lock_flags; 1518c2ecf20Sopenharmony_ci struct jsm_channel *channel = 1528c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 1538c2ecf20Sopenharmony_ci struct ktermios *termios; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 1568c2ecf20Sopenharmony_ci termios = &port->state->port.tty->termios; 1578c2ecf20Sopenharmony_ci if (ch == termios->c_cc[VSTART]) 1588c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->send_start_character(channel); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (ch == termios->c_cc[VSTOP]) 1618c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->send_stop_character(channel); 1628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void jsm_tty_stop_rx(struct uart_port *port) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct jsm_channel *channel = 1688c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->disable_receiver(channel); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void jsm_tty_break(struct uart_port *port, int break_state) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci unsigned long lock_flags; 1768c2ecf20Sopenharmony_ci struct jsm_channel *channel = 1778c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 1808c2ecf20Sopenharmony_ci if (break_state == -1) 1818c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->send_break(channel); 1828c2ecf20Sopenharmony_ci else 1838c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->clear_break(channel); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int jsm_tty_open(struct uart_port *port) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned long lock_flags; 1918c2ecf20Sopenharmony_ci struct jsm_board *brd; 1928c2ecf20Sopenharmony_ci struct jsm_channel *channel = 1938c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 1948c2ecf20Sopenharmony_ci struct ktermios *termios; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Get board pointer from our array of majors we have allocated */ 1978c2ecf20Sopenharmony_ci brd = channel->ch_bd; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * Allocate channel buffers for read/write/error. 2018c2ecf20Sopenharmony_ci * Set flag, so we don't get trounced on. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci channel->ch_flags |= (CH_OPENING); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Drop locks, as malloc with GFP_KERNEL can sleep */ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!channel->ch_rqueue) { 2088c2ecf20Sopenharmony_ci channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL); 2098c2ecf20Sopenharmony_ci if (!channel->ch_rqueue) { 2108c2ecf20Sopenharmony_ci jsm_dbg(INIT, &channel->ch_bd->pci_dev, 2118c2ecf20Sopenharmony_ci "unable to allocate read queue buf\n"); 2128c2ecf20Sopenharmony_ci return -ENOMEM; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci if (!channel->ch_equeue) { 2168c2ecf20Sopenharmony_ci channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!channel->ch_equeue) { 2188c2ecf20Sopenharmony_ci jsm_dbg(INIT, &channel->ch_bd->pci_dev, 2198c2ecf20Sopenharmony_ci "unable to allocate error queue buf\n"); 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci channel->ch_flags &= ~(CH_OPENING); 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * Initialize if neither terminal is open. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci jsm_dbg(OPEN, &channel->ch_bd->pci_dev, 2298c2ecf20Sopenharmony_ci "jsm_open: initializing channel in open...\n"); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* 2328c2ecf20Sopenharmony_ci * Flush input queues. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci channel->ch_r_head = channel->ch_r_tail = 0; 2358c2ecf20Sopenharmony_ci channel->ch_e_head = channel->ch_e_tail = 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci brd->bd_ops->flush_uart_write(channel); 2388c2ecf20Sopenharmony_ci brd->bd_ops->flush_uart_read(channel); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci channel->ch_flags = 0; 2418c2ecf20Sopenharmony_ci channel->ch_cached_lsr = 0; 2428c2ecf20Sopenharmony_ci channel->ch_stops_sent = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 2458c2ecf20Sopenharmony_ci termios = &port->state->port.tty->termios; 2468c2ecf20Sopenharmony_ci channel->ch_c_cflag = termios->c_cflag; 2478c2ecf20Sopenharmony_ci channel->ch_c_iflag = termios->c_iflag; 2488c2ecf20Sopenharmony_ci channel->ch_c_oflag = termios->c_oflag; 2498c2ecf20Sopenharmony_ci channel->ch_c_lflag = termios->c_lflag; 2508c2ecf20Sopenharmony_ci channel->ch_startc = termios->c_cc[VSTART]; 2518c2ecf20Sopenharmony_ci channel->ch_stopc = termios->c_cc[VSTOP]; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Tell UART to init itself */ 2548c2ecf20Sopenharmony_ci brd->bd_ops->uart_init(channel); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * Run param in case we changed anything 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci brd->bd_ops->param(channel); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci jsm_carrier(channel); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci channel->ch_open_count++; 2648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n"); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void jsm_tty_close(struct uart_port *port) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct jsm_board *bd; 2738c2ecf20Sopenharmony_ci struct jsm_channel *channel = 2748c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n"); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci bd = channel->ch_bd; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci channel->ch_flags &= ~(CH_STOPI); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci channel->ch_open_count--; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * If we have HUPCL set, lower DTR and RTS 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci if (channel->ch_c_cflag & HUPCL) { 2888c2ecf20Sopenharmony_ci jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, 2898c2ecf20Sopenharmony_ci "Close. HUPCL set, dropping DTR/RTS\n"); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Drop RTS/DTR */ 2928c2ecf20Sopenharmony_ci channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS); 2938c2ecf20Sopenharmony_ci bd->bd_ops->assert_modem_signals(channel); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Turn off UART interrupts for this port */ 2978c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->uart_off(channel); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "finish\n"); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void jsm_tty_set_termios(struct uart_port *port, 3038c2ecf20Sopenharmony_ci struct ktermios *termios, 3048c2ecf20Sopenharmony_ci struct ktermios *old_termios) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci unsigned long lock_flags; 3078c2ecf20Sopenharmony_ci struct jsm_channel *channel = 3088c2ecf20Sopenharmony_ci container_of(port, struct jsm_channel, uart_port); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, lock_flags); 3118c2ecf20Sopenharmony_ci channel->ch_c_cflag = termios->c_cflag; 3128c2ecf20Sopenharmony_ci channel->ch_c_iflag = termios->c_iflag; 3138c2ecf20Sopenharmony_ci channel->ch_c_oflag = termios->c_oflag; 3148c2ecf20Sopenharmony_ci channel->ch_c_lflag = termios->c_lflag; 3158c2ecf20Sopenharmony_ci channel->ch_startc = termios->c_cc[VSTART]; 3168c2ecf20Sopenharmony_ci channel->ch_stopc = termios->c_cc[VSTOP]; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci channel->ch_bd->bd_ops->param(channel); 3198c2ecf20Sopenharmony_ci jsm_carrier(channel); 3208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, lock_flags); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic const char *jsm_tty_type(struct uart_port *port) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci return "jsm"; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void jsm_tty_release_port(struct uart_port *port) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int jsm_tty_request_port(struct uart_port *port) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic void jsm_config_port(struct uart_port *port, int flags) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci port->type = PORT_JSM; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const struct uart_ops jsm_ops = { 3438c2ecf20Sopenharmony_ci .tx_empty = jsm_tty_tx_empty, 3448c2ecf20Sopenharmony_ci .set_mctrl = jsm_tty_set_mctrl, 3458c2ecf20Sopenharmony_ci .get_mctrl = jsm_tty_get_mctrl, 3468c2ecf20Sopenharmony_ci .stop_tx = jsm_tty_stop_tx, 3478c2ecf20Sopenharmony_ci .start_tx = jsm_tty_start_tx, 3488c2ecf20Sopenharmony_ci .send_xchar = jsm_tty_send_xchar, 3498c2ecf20Sopenharmony_ci .stop_rx = jsm_tty_stop_rx, 3508c2ecf20Sopenharmony_ci .break_ctl = jsm_tty_break, 3518c2ecf20Sopenharmony_ci .startup = jsm_tty_open, 3528c2ecf20Sopenharmony_ci .shutdown = jsm_tty_close, 3538c2ecf20Sopenharmony_ci .set_termios = jsm_tty_set_termios, 3548c2ecf20Sopenharmony_ci .type = jsm_tty_type, 3558c2ecf20Sopenharmony_ci .release_port = jsm_tty_release_port, 3568c2ecf20Sopenharmony_ci .request_port = jsm_tty_request_port, 3578c2ecf20Sopenharmony_ci .config_port = jsm_config_port, 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/* 3618c2ecf20Sopenharmony_ci * jsm_tty_init() 3628c2ecf20Sopenharmony_ci * 3638c2ecf20Sopenharmony_ci * Init the tty subsystem. Called once per board after board has been 3648c2ecf20Sopenharmony_ci * downloaded and init'ed. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ciint jsm_tty_init(struct jsm_board *brd) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci int i; 3698c2ecf20Sopenharmony_ci void __iomem *vaddr; 3708c2ecf20Sopenharmony_ci struct jsm_channel *ch; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (!brd) 3738c2ecf20Sopenharmony_ci return -ENXIO; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "start\n"); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* 3788c2ecf20Sopenharmony_ci * Initialize board structure elements. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci brd->nasync = brd->maxports; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * Allocate channel memory that might not have been allocated 3858c2ecf20Sopenharmony_ci * when the driver was first loaded. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci for (i = 0; i < brd->nasync; i++) { 3888c2ecf20Sopenharmony_ci if (!brd->channels[i]) { 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * Okay to malloc with GFP_KERNEL, we are not at 3928c2ecf20Sopenharmony_ci * interrupt context, and there are no locks held. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL); 3958c2ecf20Sopenharmony_ci if (!brd->channels[i]) { 3968c2ecf20Sopenharmony_ci jsm_dbg(CORE, &brd->pci_dev, 3978c2ecf20Sopenharmony_ci "%s:%d Unable to allocate memory for channel struct\n", 3988c2ecf20Sopenharmony_ci __FILE__, __LINE__); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ch = brd->channels[0]; 4048c2ecf20Sopenharmony_ci vaddr = brd->re_map_membase; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Set up channel variables */ 4078c2ecf20Sopenharmony_ci for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (!brd->channels[i]) 4108c2ecf20Sopenharmony_ci continue; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci spin_lock_init(&ch->ch_lock); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (brd->bd_uart_offset == 0x200) 4158c2ecf20Sopenharmony_ci ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i); 4168c2ecf20Sopenharmony_ci else 4178c2ecf20Sopenharmony_ci ch->ch_cls_uart = vaddr + (brd->bd_uart_offset * i); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ch->ch_bd = brd; 4208c2ecf20Sopenharmony_ci ch->ch_portnum = i; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* .25 second delay */ 4238c2ecf20Sopenharmony_ci ch->ch_close_delay = 250; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci init_waitqueue_head(&ch->ch_flags_wait); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "finish\n"); 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ciint jsm_uart_port_init(struct jsm_board *brd) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci int i, rc; 4358c2ecf20Sopenharmony_ci unsigned int line; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (!brd) 4388c2ecf20Sopenharmony_ci return -ENXIO; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "start\n"); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * Initialize board structure elements. 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci brd->nasync = brd->maxports; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Set up channel variables */ 4498c2ecf20Sopenharmony_ci for (i = 0; i < brd->nasync; i++) { 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (!brd->channels[i]) 4528c2ecf20Sopenharmony_ci continue; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.irq = brd->irq; 4558c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.uartclk = 14745600; 4568c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.type = PORT_JSM; 4578c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.iotype = UPIO_MEM; 4588c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.membase = brd->re_map_membase; 4598c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.fifosize = 16; 4608c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.ops = &jsm_ops; 4618c2ecf20Sopenharmony_ci line = find_first_zero_bit(linemap, MAXLINES); 4628c2ecf20Sopenharmony_ci if (line >= MAXLINES) { 4638c2ecf20Sopenharmony_ci printk(KERN_INFO "jsm: linemap is full, added device failed\n"); 4648c2ecf20Sopenharmony_ci continue; 4658c2ecf20Sopenharmony_ci } else 4668c2ecf20Sopenharmony_ci set_bit(line, linemap); 4678c2ecf20Sopenharmony_ci brd->channels[i]->uart_port.line = line; 4688c2ecf20Sopenharmony_ci rc = uart_add_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); 4698c2ecf20Sopenharmony_ci if (rc) { 4708c2ecf20Sopenharmony_ci printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i); 4718c2ecf20Sopenharmony_ci return rc; 4728c2ecf20Sopenharmony_ci } else 4738c2ecf20Sopenharmony_ci printk(KERN_INFO "jsm: Port %d added\n", i); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "finish\n"); 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciint jsm_remove_uart_port(struct jsm_board *brd) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci int i; 4838c2ecf20Sopenharmony_ci struct jsm_channel *ch; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (!brd) 4868c2ecf20Sopenharmony_ci return -ENXIO; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "start\n"); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * Initialize board structure elements. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci brd->nasync = brd->maxports; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Set up channel variables */ 4978c2ecf20Sopenharmony_ci for (i = 0; i < brd->nasync; i++) { 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (!brd->channels[i]) 5008c2ecf20Sopenharmony_ci continue; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ch = brd->channels[i]; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci clear_bit(ch->uart_port.line, linemap); 5058c2ecf20Sopenharmony_ci uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci jsm_dbg(INIT, &brd->pci_dev, "finish\n"); 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_civoid jsm_input(struct jsm_channel *ch) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct jsm_board *bd; 5158c2ecf20Sopenharmony_ci struct tty_struct *tp; 5168c2ecf20Sopenharmony_ci struct tty_port *port; 5178c2ecf20Sopenharmony_ci u32 rmask; 5188c2ecf20Sopenharmony_ci u16 head; 5198c2ecf20Sopenharmony_ci u16 tail; 5208c2ecf20Sopenharmony_ci int data_len; 5218c2ecf20Sopenharmony_ci unsigned long lock_flags; 5228c2ecf20Sopenharmony_ci int len = 0; 5238c2ecf20Sopenharmony_ci int s = 0; 5248c2ecf20Sopenharmony_ci int i = 0; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci port = &ch->uart_port.state->port; 5298c2ecf20Sopenharmony_ci tp = port->tty; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci bd = ch->ch_bd; 5328c2ecf20Sopenharmony_ci if (!bd) 5338c2ecf20Sopenharmony_ci return; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci spin_lock_irqsave(&ch->ch_lock, lock_flags); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* 5388c2ecf20Sopenharmony_ci *Figure the number of characters in the buffer. 5398c2ecf20Sopenharmony_ci *Exit immediately if none. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci rmask = RQUEUEMASK; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci head = ch->ch_r_head & rmask; 5458c2ecf20Sopenharmony_ci tail = ch->ch_r_tail & rmask; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci data_len = (head - tail) & rmask; 5488c2ecf20Sopenharmony_ci if (data_len == 0) { 5498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 5508c2ecf20Sopenharmony_ci return; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci *If the device is not open, or CREAD is off, flush 5578c2ecf20Sopenharmony_ci *input data and return immediately. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci if (!tp || !C_CREAD(tp)) { 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 5628c2ecf20Sopenharmony_ci "input. dropping %d bytes on port %d...\n", 5638c2ecf20Sopenharmony_ci data_len, ch->ch_portnum); 5648c2ecf20Sopenharmony_ci ch->ch_r_head = tail; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Force queue flow control to be released, if needed */ 5678c2ecf20Sopenharmony_ci jsm_check_queue_flow_control(ch); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 5708c2ecf20Sopenharmony_ci return; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* 5748c2ecf20Sopenharmony_ci * If we are throttled, simply don't read any data. 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci if (ch->ch_flags & CH_STOPI) { 5778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 5788c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 5798c2ecf20Sopenharmony_ci "Port %d throttled, not reading any data. head: %x tail: %x\n", 5808c2ecf20Sopenharmony_ci ch->ch_portnum, head, tail); 5818c2ecf20Sopenharmony_ci return; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci len = tty_buffer_request_room(port, data_len); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* 5898c2ecf20Sopenharmony_ci * len now contains the most amount of data we can copy, 5908c2ecf20Sopenharmony_ci * bounded either by the flip buffer size or the amount 5918c2ecf20Sopenharmony_ci * of data the card actually has pending... 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci while (len) { 5948c2ecf20Sopenharmony_ci s = ((head >= tail) ? head : RQUEUESIZE) - tail; 5958c2ecf20Sopenharmony_ci s = min(s, len); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (s <= 0) 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* 6018c2ecf20Sopenharmony_ci * If conditions are such that ld needs to see all 6028c2ecf20Sopenharmony_ci * UART errors, we will have to walk each character 6038c2ecf20Sopenharmony_ci * and error byte and send them to the buffer one at 6048c2ecf20Sopenharmony_ci * a time. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { 6088c2ecf20Sopenharmony_ci for (i = 0; i < s; i++) { 6098c2ecf20Sopenharmony_ci /* 6108c2ecf20Sopenharmony_ci * Give the Linux ld the flags in the 6118c2ecf20Sopenharmony_ci * format it likes. 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ci if (*(ch->ch_equeue +tail +i) & UART_LSR_BI) 6148c2ecf20Sopenharmony_ci tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_BREAK); 6158c2ecf20Sopenharmony_ci else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE) 6168c2ecf20Sopenharmony_ci tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_PARITY); 6178c2ecf20Sopenharmony_ci else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE) 6188c2ecf20Sopenharmony_ci tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_FRAME); 6198c2ecf20Sopenharmony_ci else 6208c2ecf20Sopenharmony_ci tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_NORMAL); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci } else { 6238c2ecf20Sopenharmony_ci tty_insert_flip_string(port, ch->ch_rqueue + tail, s); 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci tail += s; 6268c2ecf20Sopenharmony_ci len -= s; 6278c2ecf20Sopenharmony_ci /* Flip queue if needed */ 6288c2ecf20Sopenharmony_ci tail &= rmask; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci ch->ch_r_tail = tail & rmask; 6328c2ecf20Sopenharmony_ci ch->ch_e_tail = tail & rmask; 6338c2ecf20Sopenharmony_ci jsm_check_queue_flow_control(ch); 6348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->ch_lock, lock_flags); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Tell the tty layer its okay to "eat" the data now */ 6378c2ecf20Sopenharmony_ci tty_flip_buffer_push(port); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic void jsm_carrier(struct jsm_channel *ch) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct jsm_board *bd; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci int virt_carrier = 0; 6478c2ecf20Sopenharmony_ci int phys_carrier = 0; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "start\n"); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci bd = ch->ch_bd; 6528c2ecf20Sopenharmony_ci if (!bd) 6538c2ecf20Sopenharmony_ci return; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (ch->ch_mistat & UART_MSR_DCD) { 6568c2ecf20Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "mistat: %x D_CD: %x\n", 6578c2ecf20Sopenharmony_ci ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD); 6588c2ecf20Sopenharmony_ci phys_carrier = 1; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (ch->ch_c_cflag & CLOCAL) 6628c2ecf20Sopenharmony_ci virt_carrier = 1; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "DCD: physical: %d virt: %d\n", 6658c2ecf20Sopenharmony_ci phys_carrier, virt_carrier); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* 6688c2ecf20Sopenharmony_ci * Test for a VIRTUAL carrier transition to HIGH. 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_ci if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* 6738c2ecf20Sopenharmony_ci * When carrier rises, wake any threads waiting 6748c2ecf20Sopenharmony_ci * for carrier in the open routine. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: virt DCD rose\n"); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (waitqueue_active(&(ch->ch_flags_wait))) 6808c2ecf20Sopenharmony_ci wake_up_interruptible(&ch->ch_flags_wait); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* 6848c2ecf20Sopenharmony_ci * Test for a PHYSICAL carrier transition to HIGH. 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* 6898c2ecf20Sopenharmony_ci * When carrier rises, wake any threads waiting 6908c2ecf20Sopenharmony_ci * for carrier in the open routine. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci jsm_dbg(CARR, &ch->ch_bd->pci_dev, 6948c2ecf20Sopenharmony_ci "carrier: physical DCD rose\n"); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (waitqueue_active(&(ch->ch_flags_wait))) 6978c2ecf20Sopenharmony_ci wake_up_interruptible(&ch->ch_flags_wait); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * Test for a PHYSICAL transition to low, so long as we aren't 7028c2ecf20Sopenharmony_ci * currently ignoring physical transitions (which is what "virtual 7038c2ecf20Sopenharmony_ci * carrier" indicates). 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * The transition of the virtual carrier to low really doesn't 7068c2ecf20Sopenharmony_ci * matter... it really only means "ignore carrier state", not 7078c2ecf20Sopenharmony_ci * "make pretend that carrier is there". 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) 7108c2ecf20Sopenharmony_ci && (phys_carrier == 0)) { 7118c2ecf20Sopenharmony_ci /* 7128c2ecf20Sopenharmony_ci * When carrier drops: 7138c2ecf20Sopenharmony_ci * 7148c2ecf20Sopenharmony_ci * Drop carrier on all open units. 7158c2ecf20Sopenharmony_ci * 7168c2ecf20Sopenharmony_ci * Flush queues, waking up any task waiting in the 7178c2ecf20Sopenharmony_ci * line discipline. 7188c2ecf20Sopenharmony_ci * 7198c2ecf20Sopenharmony_ci * Send a hangup to the control terminal. 7208c2ecf20Sopenharmony_ci * 7218c2ecf20Sopenharmony_ci * Enable all select calls. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ci if (waitqueue_active(&(ch->ch_flags_wait))) 7248c2ecf20Sopenharmony_ci wake_up_interruptible(&ch->ch_flags_wait); 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* 7288c2ecf20Sopenharmony_ci * Make sure that our cached values reflect the current reality. 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_ci if (virt_carrier == 1) 7318c2ecf20Sopenharmony_ci ch->ch_flags |= CH_FCAR; 7328c2ecf20Sopenharmony_ci else 7338c2ecf20Sopenharmony_ci ch->ch_flags &= ~CH_FCAR; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (phys_carrier == 1) 7368c2ecf20Sopenharmony_ci ch->ch_flags |= CH_CD; 7378c2ecf20Sopenharmony_ci else 7388c2ecf20Sopenharmony_ci ch->ch_flags &= ~CH_CD; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_civoid jsm_check_queue_flow_control(struct jsm_channel *ch) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct board_ops *bd_ops = ch->ch_bd->bd_ops; 7458c2ecf20Sopenharmony_ci int qleft; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* Store how much space we have left in the queue */ 7488c2ecf20Sopenharmony_ci if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0) 7498c2ecf20Sopenharmony_ci qleft += RQUEUEMASK + 1; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* 7528c2ecf20Sopenharmony_ci * Check to see if we should enforce flow control on our queue because 7538c2ecf20Sopenharmony_ci * the ld (or user) isn't reading data out of our queue fast enuf. 7548c2ecf20Sopenharmony_ci * 7558c2ecf20Sopenharmony_ci * NOTE: This is done based on what the current flow control of the 7568c2ecf20Sopenharmony_ci * port is set for. 7578c2ecf20Sopenharmony_ci * 7588c2ecf20Sopenharmony_ci * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt. 7598c2ecf20Sopenharmony_ci * This will cause the UART's FIFO to back up, and force 7608c2ecf20Sopenharmony_ci * the RTS signal to be dropped. 7618c2ecf20Sopenharmony_ci * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to 7628c2ecf20Sopenharmony_ci * the other side, in hopes it will stop sending data to us. 7638c2ecf20Sopenharmony_ci * 3) NONE - Nothing we can do. We will simply drop any extra data 7648c2ecf20Sopenharmony_ci * that gets sent into us when the queue fills up. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ci if (qleft < 256) { 7678c2ecf20Sopenharmony_ci /* HWFLOW */ 7688c2ecf20Sopenharmony_ci if (ch->ch_c_cflag & CRTSCTS) { 7698c2ecf20Sopenharmony_ci if (!(ch->ch_flags & CH_RECEIVER_OFF)) { 7708c2ecf20Sopenharmony_ci bd_ops->disable_receiver(ch); 7718c2ecf20Sopenharmony_ci ch->ch_flags |= (CH_RECEIVER_OFF); 7728c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 7738c2ecf20Sopenharmony_ci "Internal queue hit hilevel mark (%d)! Turning off interrupts\n", 7748c2ecf20Sopenharmony_ci qleft); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci /* SWFLOW */ 7788c2ecf20Sopenharmony_ci else if (ch->ch_c_iflag & IXOFF) { 7798c2ecf20Sopenharmony_ci if (ch->ch_stops_sent <= MAX_STOPS_SENT) { 7808c2ecf20Sopenharmony_ci bd_ops->send_stop_character(ch); 7818c2ecf20Sopenharmony_ci ch->ch_stops_sent++; 7828c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 7838c2ecf20Sopenharmony_ci "Sending stop char! Times sent: %x\n", 7848c2ecf20Sopenharmony_ci ch->ch_stops_sent); 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* 7908c2ecf20Sopenharmony_ci * Check to see if we should unenforce flow control because 7918c2ecf20Sopenharmony_ci * ld (or user) finally read enuf data out of our queue. 7928c2ecf20Sopenharmony_ci * 7938c2ecf20Sopenharmony_ci * NOTE: This is done based on what the current flow control of the 7948c2ecf20Sopenharmony_ci * port is set for. 7958c2ecf20Sopenharmony_ci * 7968c2ecf20Sopenharmony_ci * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt. 7978c2ecf20Sopenharmony_ci * This will cause the UART's FIFO to raise RTS back up, 7988c2ecf20Sopenharmony_ci * which will allow the other side to start sending data again. 7998c2ecf20Sopenharmony_ci * 2) SWFLOW (IXOFF) - Send a start character to 8008c2ecf20Sopenharmony_ci * the other side, so it will start sending data to us again. 8018c2ecf20Sopenharmony_ci * 3) NONE - Do nothing. Since we didn't do anything to turn off the 8028c2ecf20Sopenharmony_ci * other side, we don't need to do anything now. 8038c2ecf20Sopenharmony_ci */ 8048c2ecf20Sopenharmony_ci if (qleft > (RQUEUESIZE / 2)) { 8058c2ecf20Sopenharmony_ci /* HWFLOW */ 8068c2ecf20Sopenharmony_ci if (ch->ch_c_cflag & CRTSCTS) { 8078c2ecf20Sopenharmony_ci if (ch->ch_flags & CH_RECEIVER_OFF) { 8088c2ecf20Sopenharmony_ci bd_ops->enable_receiver(ch); 8098c2ecf20Sopenharmony_ci ch->ch_flags &= ~(CH_RECEIVER_OFF); 8108c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 8118c2ecf20Sopenharmony_ci "Internal queue hit lowlevel mark (%d)! Turning on interrupts\n", 8128c2ecf20Sopenharmony_ci qleft); 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci /* SWFLOW */ 8168c2ecf20Sopenharmony_ci else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) { 8178c2ecf20Sopenharmony_ci ch->ch_stops_sent = 0; 8188c2ecf20Sopenharmony_ci bd_ops->send_start_character(ch); 8198c2ecf20Sopenharmony_ci jsm_dbg(READ, &ch->ch_bd->pci_dev, 8208c2ecf20Sopenharmony_ci "Sending start char!\n"); 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci} 824