18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 28c2ecf20Sopenharmony_ci/* generic HDLC line discipline for Linux 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Written by Paul Fulghum paulkf@microgate.com 58c2ecf20Sopenharmony_ci * for Microgate Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Microgate and SyncLink are registered trademarks of Microgate Corporation 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Adapted from ppp.c, written by Michael Callahan <callahan@maths.ox.ac.uk>, 108c2ecf20Sopenharmony_ci * Al Longyear <longyear@netcom.com>, 118c2ecf20Sopenharmony_ci * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Original release 01/11/99 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This module implements the tty line discipline N_HDLC for use with 168c2ecf20Sopenharmony_ci * tty device drivers that support bit-synchronous HDLC communications. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * All HDLC data is frame oriented which means: 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * 1. tty write calls represent one complete transmit frame of data 218c2ecf20Sopenharmony_ci * The device driver should accept the complete frame or none of 228c2ecf20Sopenharmony_ci * the frame (busy) in the write method. Each write call should have 238c2ecf20Sopenharmony_ci * a byte count in the range of 2-65535 bytes (2 is min HDLC frame 248c2ecf20Sopenharmony_ci * with 1 addr byte and 1 ctrl byte). The max byte count of 65535 258c2ecf20Sopenharmony_ci * should include any crc bytes required. For example, when using 268c2ecf20Sopenharmony_ci * CCITT CRC32, 4 crc bytes are required, so the maximum size frame 278c2ecf20Sopenharmony_ci * the application may transmit is limited to 65531 bytes. For CCITT 288c2ecf20Sopenharmony_ci * CRC16, the maximum application frame size would be 65533. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * 2. receive callbacks from the device driver represents 328c2ecf20Sopenharmony_ci * one received frame. The device driver should bypass 338c2ecf20Sopenharmony_ci * the tty flip buffer and call the line discipline receive 348c2ecf20Sopenharmony_ci * callback directly to avoid fragmenting or concatenating 358c2ecf20Sopenharmony_ci * multiple frames into a single receive callback. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * The HDLC line discipline queues the receive frames in separate 388c2ecf20Sopenharmony_ci * buffers so complete receive frames can be returned by the 398c2ecf20Sopenharmony_ci * tty read calls. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * 3. tty read calls returns an entire frame of data or nothing. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * 4. all send and receive data is considered raw. No processing 448c2ecf20Sopenharmony_ci * or translation is performed by the line discipline, regardless 458c2ecf20Sopenharmony_ci * of the tty flags 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * 5. When line discipline is queried for the amount of receive 488c2ecf20Sopenharmony_ci * data available (FIOC), 0 is returned if no data available, 498c2ecf20Sopenharmony_ci * otherwise the count of the next available frame is returned. 508c2ecf20Sopenharmony_ci * (instead of the sum of all received frame counts). 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * These conventions allow the standard tty programming interface 538c2ecf20Sopenharmony_ci * to be used for synchronous HDLC applications when used with 548c2ecf20Sopenharmony_ci * this line discipline (or another line discipline that is frame 558c2ecf20Sopenharmony_ci * oriented such as N_PPP). 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * The SyncLink driver (synclink.c) implements both asynchronous 588c2ecf20Sopenharmony_ci * (using standard line discipline N_TTY) and synchronous HDLC 598c2ecf20Sopenharmony_ci * (using N_HDLC) communications, with the latter using the above 608c2ecf20Sopenharmony_ci * conventions. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * This implementation is very basic and does not maintain 638c2ecf20Sopenharmony_ci * any statistics. The main point is to enforce the raw data 648c2ecf20Sopenharmony_ci * and frame orientation of HDLC communications. 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 678c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 688c2ecf20Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 698c2ecf20Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 708c2ecf20Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 718c2ecf20Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 728c2ecf20Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 738c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 748c2ecf20Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 758c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 768c2ecf20Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define HDLC_MAGIC 0x239e 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#include <linux/module.h> 828c2ecf20Sopenharmony_ci#include <linux/init.h> 838c2ecf20Sopenharmony_ci#include <linux/kernel.h> 848c2ecf20Sopenharmony_ci#include <linux/sched.h> 858c2ecf20Sopenharmony_ci#include <linux/types.h> 868c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 878c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 888c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#include <linux/poll.h> 918c2ecf20Sopenharmony_ci#include <linux/in.h> 928c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 938c2ecf20Sopenharmony_ci#include <linux/slab.h> 948c2ecf20Sopenharmony_ci#include <linux/tty.h> 958c2ecf20Sopenharmony_ci#include <linux/errno.h> 968c2ecf20Sopenharmony_ci#include <linux/string.h> /* used in new tty drivers */ 978c2ecf20Sopenharmony_ci#include <linux/signal.h> /* used in new tty drivers */ 988c2ecf20Sopenharmony_ci#include <linux/if.h> 998c2ecf20Sopenharmony_ci#include <linux/bitops.h> 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#include <asm/termios.h> 1028c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1038c2ecf20Sopenharmony_ci#include "tty.h" 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * Buffers for individual HDLC frames 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci#define MAX_HDLC_FRAME_SIZE 65535 1098c2ecf20Sopenharmony_ci#define DEFAULT_RX_BUF_COUNT 10 1108c2ecf20Sopenharmony_ci#define MAX_RX_BUF_COUNT 60 1118c2ecf20Sopenharmony_ci#define DEFAULT_TX_BUF_COUNT 3 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistruct n_hdlc_buf { 1148c2ecf20Sopenharmony_ci struct list_head list_item; 1158c2ecf20Sopenharmony_ci int count; 1168c2ecf20Sopenharmony_ci char buf[]; 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistruct n_hdlc_buf_list { 1208c2ecf20Sopenharmony_ci struct list_head list; 1218c2ecf20Sopenharmony_ci int count; 1228c2ecf20Sopenharmony_ci spinlock_t spinlock; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/** 1268c2ecf20Sopenharmony_ci * struct n_hdlc - per device instance data structure 1278c2ecf20Sopenharmony_ci * @magic: magic value for structure 1288c2ecf20Sopenharmony_ci * @tbusy: reentrancy flag for tx wakeup code 1298c2ecf20Sopenharmony_ci * @woke_up: tx wakeup needs to be run again as it was called while @tbusy 1308c2ecf20Sopenharmony_ci * @tx_buf_list: list of pending transmit frame buffers 1318c2ecf20Sopenharmony_ci * @rx_buf_list: list of received frame buffers 1328c2ecf20Sopenharmony_ci * @tx_free_buf_list: list unused transmit frame buffers 1338c2ecf20Sopenharmony_ci * @rx_free_buf_list: list unused received frame buffers 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cistruct n_hdlc { 1368c2ecf20Sopenharmony_ci int magic; 1378c2ecf20Sopenharmony_ci bool tbusy; 1388c2ecf20Sopenharmony_ci bool woke_up; 1398c2ecf20Sopenharmony_ci struct n_hdlc_buf_list tx_buf_list; 1408c2ecf20Sopenharmony_ci struct n_hdlc_buf_list rx_buf_list; 1418c2ecf20Sopenharmony_ci struct n_hdlc_buf_list tx_free_buf_list; 1428c2ecf20Sopenharmony_ci struct n_hdlc_buf_list rx_free_buf_list; 1438c2ecf20Sopenharmony_ci struct work_struct write_work; 1448c2ecf20Sopenharmony_ci struct tty_struct *tty_for_write_work; 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * HDLC buffer list manipulation functions 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_cistatic void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, 1518c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf); 1528c2ecf20Sopenharmony_cistatic void n_hdlc_buf_put(struct n_hdlc_buf_list *list, 1538c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf); 1548c2ecf20Sopenharmony_cistatic struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* Local functions */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic struct n_hdlc *n_hdlc_alloc(void); 1598c2ecf20Sopenharmony_cistatic void n_hdlc_tty_write_work(struct work_struct *work); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* max frame size for memory allocations */ 1628c2ecf20Sopenharmony_cistatic int maxframe = 4096; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void flush_rx_queue(struct tty_struct *tty) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 1678c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci while ((buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list))) 1708c2ecf20Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, buf); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void flush_tx_queue(struct tty_struct *tty) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 1768c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) 1798c2ecf20Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void n_hdlc_free_buf_list(struct n_hdlc_buf_list *list) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci do { 1878c2ecf20Sopenharmony_ci buf = n_hdlc_buf_get(list); 1888c2ecf20Sopenharmony_ci kfree(buf); 1898c2ecf20Sopenharmony_ci } while (buf); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/** 1938c2ecf20Sopenharmony_ci * n_hdlc_tty_close - line discipline close 1948c2ecf20Sopenharmony_ci * @tty: pointer to tty info structure 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * Called when the line discipline is changed to something 1978c2ecf20Sopenharmony_ci * else, the tty is closed, or the tty detects a hangup. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void n_hdlc_tty_close(struct tty_struct *tty) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (n_hdlc->magic != HDLC_MAGIC) { 2048c2ecf20Sopenharmony_ci pr_warn("n_hdlc: trying to close unopened tty!\n"); 2058c2ecf20Sopenharmony_ci return; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci#if defined(TTY_NO_WRITE_SPLIT) 2088c2ecf20Sopenharmony_ci clear_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 2098c2ecf20Sopenharmony_ci#endif 2108c2ecf20Sopenharmony_ci tty->disc_data = NULL; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Ensure that the n_hdlcd process is not hanging on select()/poll() */ 2138c2ecf20Sopenharmony_ci wake_up_interruptible(&tty->read_wait); 2148c2ecf20Sopenharmony_ci wake_up_interruptible(&tty->write_wait); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci cancel_work_sync(&n_hdlc->write_work); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->rx_free_buf_list); 2198c2ecf20Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->tx_free_buf_list); 2208c2ecf20Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->rx_buf_list); 2218c2ecf20Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->tx_buf_list); 2228c2ecf20Sopenharmony_ci kfree(n_hdlc); 2238c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_close() */ 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/** 2268c2ecf20Sopenharmony_ci * n_hdlc_tty_open - called when line discipline changed to n_hdlc 2278c2ecf20Sopenharmony_ci * @tty: pointer to tty info structure 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci * Returns 0 if success, otherwise error code 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_cistatic int n_hdlc_tty_open(struct tty_struct *tty) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci pr_debug("%s() called (device=%s)\n", __func__, tty->name); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* There should not be an existing table for this slot. */ 2388c2ecf20Sopenharmony_ci if (n_hdlc) { 2398c2ecf20Sopenharmony_ci pr_err("%s: tty already associated!\n", __func__); 2408c2ecf20Sopenharmony_ci return -EEXIST; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci n_hdlc = n_hdlc_alloc(); 2448c2ecf20Sopenharmony_ci if (!n_hdlc) { 2458c2ecf20Sopenharmony_ci pr_err("%s: n_hdlc_alloc failed\n", __func__); 2468c2ecf20Sopenharmony_ci return -ENFILE; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci INIT_WORK(&n_hdlc->write_work, n_hdlc_tty_write_work); 2508c2ecf20Sopenharmony_ci n_hdlc->tty_for_write_work = tty; 2518c2ecf20Sopenharmony_ci tty->disc_data = n_hdlc; 2528c2ecf20Sopenharmony_ci tty->receive_room = 65536; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* change tty_io write() to not split large writes into 8K chunks */ 2558c2ecf20Sopenharmony_ci set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* flush receive data from driver */ 2588c2ecf20Sopenharmony_ci tty_driver_flush_buffer(tty); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci} /* end of n_tty_hdlc_open() */ 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/** 2658c2ecf20Sopenharmony_ci * n_hdlc_send_frames - send frames on pending send buffer list 2668c2ecf20Sopenharmony_ci * @n_hdlc: pointer to ldisc instance data 2678c2ecf20Sopenharmony_ci * @tty: pointer to tty instance data 2688c2ecf20Sopenharmony_ci * 2698c2ecf20Sopenharmony_ci * Send frames on pending send buffer list until the driver does not accept a 2708c2ecf20Sopenharmony_ci * frame (busy) this function is called after adding a frame to the send buffer 2718c2ecf20Sopenharmony_ci * list and by the tty wakeup callback. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci register int actual; 2768c2ecf20Sopenharmony_ci unsigned long flags; 2778c2ecf20Sopenharmony_ci struct n_hdlc_buf *tbuf; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cicheck_again: 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); 2828c2ecf20Sopenharmony_ci if (n_hdlc->tbusy) { 2838c2ecf20Sopenharmony_ci n_hdlc->woke_up = true; 2848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci n_hdlc->tbusy = true; 2888c2ecf20Sopenharmony_ci n_hdlc->woke_up = false; 2898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); 2928c2ecf20Sopenharmony_ci while (tbuf) { 2938c2ecf20Sopenharmony_ci pr_debug("sending frame %p, count=%d\n", tbuf, tbuf->count); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Send the next block of data to device */ 2968c2ecf20Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 2978c2ecf20Sopenharmony_ci actual = tty->ops->write(tty, tbuf->buf, tbuf->count); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* rollback was possible and has been done */ 3008c2ecf20Sopenharmony_ci if (actual == -ERESTARTSYS) { 3018c2ecf20Sopenharmony_ci n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci /* if transmit error, throw frame away by */ 3058c2ecf20Sopenharmony_ci /* pretending it was accepted by driver */ 3068c2ecf20Sopenharmony_ci if (actual < 0) 3078c2ecf20Sopenharmony_ci actual = tbuf->count; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (actual == tbuf->count) { 3108c2ecf20Sopenharmony_ci pr_debug("frame %p completed\n", tbuf); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* free current transmit buffer */ 3138c2ecf20Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* wait up sleeping writers */ 3168c2ecf20Sopenharmony_ci wake_up_interruptible(&tty->write_wait); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* get next pending transmit buffer */ 3198c2ecf20Sopenharmony_ci tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); 3208c2ecf20Sopenharmony_ci } else { 3218c2ecf20Sopenharmony_ci pr_debug("frame %p pending\n", tbuf); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * the buffer was not accepted by driver, 3258c2ecf20Sopenharmony_ci * return it back into tx queue 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (!tbuf) 3338c2ecf20Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Clear the re-entry flag */ 3368c2ecf20Sopenharmony_ci spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); 3378c2ecf20Sopenharmony_ci n_hdlc->tbusy = false; 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (n_hdlc->woke_up) 3418c2ecf20Sopenharmony_ci goto check_again; 3428c2ecf20Sopenharmony_ci} /* end of n_hdlc_send_frames() */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/** 3458c2ecf20Sopenharmony_ci * n_hdlc_tty_write_work - Asynchronous callback for transmit wakeup 3468c2ecf20Sopenharmony_ci * @work: pointer to work_struct 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * Called when low level device driver can accept more send data. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_cistatic void n_hdlc_tty_write_work(struct work_struct *work) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = container_of(work, struct n_hdlc, write_work); 3538c2ecf20Sopenharmony_ci struct tty_struct *tty = n_hdlc->tty_for_write_work; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci n_hdlc_send_frames(n_hdlc, tty); 3568c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_write_work() */ 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/** 3598c2ecf20Sopenharmony_ci * n_hdlc_tty_wakeup - Callback for transmit wakeup 3608c2ecf20Sopenharmony_ci * @tty: pointer to associated tty instance data 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * Called when low level device driver can accept more send data. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_cistatic void n_hdlc_tty_wakeup(struct tty_struct *tty) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci schedule_work(&n_hdlc->write_work); 3698c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_wakeup() */ 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/** 3728c2ecf20Sopenharmony_ci * n_hdlc_tty_receive - Called by tty driver when receive data is available 3738c2ecf20Sopenharmony_ci * @tty: pointer to tty instance data 3748c2ecf20Sopenharmony_ci * @data: pointer to received data 3758c2ecf20Sopenharmony_ci * @flags: pointer to flags for data 3768c2ecf20Sopenharmony_ci * @count: count of received data in bytes 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * Called by tty low level driver when receive data is available. Data is 3798c2ecf20Sopenharmony_ci * interpreted as one HDLC frame. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_cistatic void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, 3828c2ecf20Sopenharmony_ci char *flags, int count) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci register struct n_hdlc *n_hdlc = tty->disc_data; 3858c2ecf20Sopenharmony_ci register struct n_hdlc_buf *buf; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci pr_debug("%s() called count=%d\n", __func__, count); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* verify line is using HDLC discipline */ 3908c2ecf20Sopenharmony_ci if (n_hdlc->magic != HDLC_MAGIC) { 3918c2ecf20Sopenharmony_ci pr_err("line not using HDLC discipline\n"); 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (count > maxframe) { 3968c2ecf20Sopenharmony_ci pr_debug("rx count>maxframesize, data discarded\n"); 3978c2ecf20Sopenharmony_ci return; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* get a free HDLC buffer */ 4018c2ecf20Sopenharmony_ci buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); 4028c2ecf20Sopenharmony_ci if (!buf) { 4038c2ecf20Sopenharmony_ci /* 4048c2ecf20Sopenharmony_ci * no buffers in free list, attempt to allocate another rx 4058c2ecf20Sopenharmony_ci * buffer unless the maximum count has been reached 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT) 4088c2ecf20Sopenharmony_ci buf = kmalloc(struct_size(buf, buf, maxframe), 4098c2ecf20Sopenharmony_ci GFP_ATOMIC); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!buf) { 4138c2ecf20Sopenharmony_ci pr_debug("no more rx buffers, data discarded\n"); 4148c2ecf20Sopenharmony_ci return; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* copy received data to HDLC buffer */ 4188c2ecf20Sopenharmony_ci memcpy(buf->buf, data, count); 4198c2ecf20Sopenharmony_ci buf->count = count; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* add HDLC buffer to list of received frames */ 4228c2ecf20Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->rx_buf_list, buf); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* wake up any blocked reads and perform async signalling */ 4258c2ecf20Sopenharmony_ci wake_up_interruptible(&tty->read_wait); 4268c2ecf20Sopenharmony_ci if (tty->fasync != NULL) 4278c2ecf20Sopenharmony_ci kill_fasync(&tty->fasync, SIGIO, POLL_IN); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_receive() */ 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/** 4328c2ecf20Sopenharmony_ci * n_hdlc_tty_read - Called to retrieve one frame of data (if available) 4338c2ecf20Sopenharmony_ci * @tty: pointer to tty instance data 4348c2ecf20Sopenharmony_ci * @file: pointer to open file object 4358c2ecf20Sopenharmony_ci * @buf: pointer to returned data buffer 4368c2ecf20Sopenharmony_ci * @nr: size of returned data buffer 4378c2ecf20Sopenharmony_ci * 4388c2ecf20Sopenharmony_ci * Returns the number of bytes returned or error code. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_cistatic ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, 4418c2ecf20Sopenharmony_ci __u8 *kbuf, size_t nr, 4428c2ecf20Sopenharmony_ci void **cookie, unsigned long offset) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 4458c2ecf20Sopenharmony_ci int ret = 0; 4468c2ecf20Sopenharmony_ci struct n_hdlc_buf *rbuf; 4478c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Is this a repeated call for an rbuf we already found earlier? */ 4508c2ecf20Sopenharmony_ci rbuf = *cookie; 4518c2ecf20Sopenharmony_ci if (rbuf) 4528c2ecf20Sopenharmony_ci goto have_rbuf; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci add_wait_queue(&tty->read_wait, &wait); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci for (;;) { 4578c2ecf20Sopenharmony_ci if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { 4588c2ecf20Sopenharmony_ci ret = -EIO; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci if (tty_hung_up_p(file)) 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); 4678c2ecf20Sopenharmony_ci if (rbuf) 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* no data */ 4718c2ecf20Sopenharmony_ci if (tty_io_nonblock(tty, file)) { 4728c2ecf20Sopenharmony_ci ret = -EAGAIN; 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci schedule(); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (signal_pending(current)) { 4798c2ecf20Sopenharmony_ci ret = -EINTR; 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci remove_wait_queue(&tty->read_wait, &wait); 4858c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (!rbuf) 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci *cookie = rbuf; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cihave_rbuf: 4928c2ecf20Sopenharmony_ci /* Have we used it up entirely? */ 4938c2ecf20Sopenharmony_ci if (offset >= rbuf->count) 4948c2ecf20Sopenharmony_ci goto done_with_rbuf; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* More data to go, but can't copy any more? EOVERFLOW */ 4978c2ecf20Sopenharmony_ci ret = -EOVERFLOW; 4988c2ecf20Sopenharmony_ci if (!nr) 4998c2ecf20Sopenharmony_ci goto done_with_rbuf; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Copy as much data as possible */ 5028c2ecf20Sopenharmony_ci ret = rbuf->count - offset; 5038c2ecf20Sopenharmony_ci if (ret > nr) 5048c2ecf20Sopenharmony_ci ret = nr; 5058c2ecf20Sopenharmony_ci memcpy(kbuf, rbuf->buf+offset, ret); 5068c2ecf20Sopenharmony_ci offset += ret; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* If we still have data left, we leave the rbuf in the cookie */ 5098c2ecf20Sopenharmony_ci if (offset < rbuf->count) 5108c2ecf20Sopenharmony_ci return ret; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cidone_with_rbuf: 5138c2ecf20Sopenharmony_ci *cookie = NULL; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT) 5168c2ecf20Sopenharmony_ci kfree(rbuf); 5178c2ecf20Sopenharmony_ci else 5188c2ecf20Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return ret; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_read() */ 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/** 5258c2ecf20Sopenharmony_ci * n_hdlc_tty_write - write a single frame of data to device 5268c2ecf20Sopenharmony_ci * @tty: pointer to associated tty device instance data 5278c2ecf20Sopenharmony_ci * @file: pointer to file object data 5288c2ecf20Sopenharmony_ci * @data: pointer to transmit data (one frame) 5298c2ecf20Sopenharmony_ci * @count: size of transmit frame in bytes 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * Returns the number of bytes written (or error code). 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_cistatic ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, 5348c2ecf20Sopenharmony_ci const unsigned char *data, size_t count) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 5378c2ecf20Sopenharmony_ci int error = 0; 5388c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 5398c2ecf20Sopenharmony_ci struct n_hdlc_buf *tbuf; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci pr_debug("%s() called count=%zd\n", __func__, count); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (n_hdlc->magic != HDLC_MAGIC) 5448c2ecf20Sopenharmony_ci return -EIO; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* verify frame size */ 5478c2ecf20Sopenharmony_ci if (count > maxframe) { 5488c2ecf20Sopenharmony_ci pr_debug("%s: truncating user packet from %zu to %d\n", 5498c2ecf20Sopenharmony_ci __func__, count, maxframe); 5508c2ecf20Sopenharmony_ci count = maxframe; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci add_wait_queue(&tty->write_wait, &wait); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci for (;;) { 5568c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); 5598c2ecf20Sopenharmony_ci if (tbuf) 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (tty_io_nonblock(tty, file)) { 5638c2ecf20Sopenharmony_ci error = -EAGAIN; 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci schedule(); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (signal_pending(current)) { 5698c2ecf20Sopenharmony_ci error = -EINTR; 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 5758c2ecf20Sopenharmony_ci remove_wait_queue(&tty->write_wait, &wait); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (!error) { 5788c2ecf20Sopenharmony_ci /* Retrieve the user's buffer */ 5798c2ecf20Sopenharmony_ci memcpy(tbuf->buf, data, count); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Send the data */ 5828c2ecf20Sopenharmony_ci tbuf->count = error = count; 5838c2ecf20Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->tx_buf_list, tbuf); 5848c2ecf20Sopenharmony_ci n_hdlc_send_frames(n_hdlc, tty); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return error; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_write() */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci/** 5928c2ecf20Sopenharmony_ci * n_hdlc_tty_ioctl - process IOCTL system call for the tty device. 5938c2ecf20Sopenharmony_ci * @tty: pointer to tty instance data 5948c2ecf20Sopenharmony_ci * @file: pointer to open file object for device 5958c2ecf20Sopenharmony_ci * @cmd: IOCTL command code 5968c2ecf20Sopenharmony_ci * @arg: argument for IOCTL call (cmd dependent) 5978c2ecf20Sopenharmony_ci * 5988c2ecf20Sopenharmony_ci * Returns command dependent result. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_cistatic int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, 6018c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 6048c2ecf20Sopenharmony_ci int error = 0; 6058c2ecf20Sopenharmony_ci int count; 6068c2ecf20Sopenharmony_ci unsigned long flags; 6078c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf = NULL; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci pr_debug("%s() called %d\n", __func__, cmd); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* Verify the status of the device */ 6128c2ecf20Sopenharmony_ci if (n_hdlc->magic != HDLC_MAGIC) 6138c2ecf20Sopenharmony_ci return -EBADF; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci switch (cmd) { 6168c2ecf20Sopenharmony_ci case FIONREAD: 6178c2ecf20Sopenharmony_ci /* report count of read data available */ 6188c2ecf20Sopenharmony_ci /* in next available frame (if any) */ 6198c2ecf20Sopenharmony_ci spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock, flags); 6208c2ecf20Sopenharmony_ci buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list, 6218c2ecf20Sopenharmony_ci struct n_hdlc_buf, list_item); 6228c2ecf20Sopenharmony_ci if (buf) 6238c2ecf20Sopenharmony_ci count = buf->count; 6248c2ecf20Sopenharmony_ci else 6258c2ecf20Sopenharmony_ci count = 0; 6268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock, flags); 6278c2ecf20Sopenharmony_ci error = put_user(count, (int __user *)arg); 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci case TIOCOUTQ: 6318c2ecf20Sopenharmony_ci /* get the pending tx byte count in the driver */ 6328c2ecf20Sopenharmony_ci count = tty_chars_in_buffer(tty); 6338c2ecf20Sopenharmony_ci /* add size of next output frame in queue */ 6348c2ecf20Sopenharmony_ci spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); 6358c2ecf20Sopenharmony_ci buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list, 6368c2ecf20Sopenharmony_ci struct n_hdlc_buf, list_item); 6378c2ecf20Sopenharmony_ci if (buf) 6388c2ecf20Sopenharmony_ci count += buf->count; 6398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 6408c2ecf20Sopenharmony_ci error = put_user(count, (int __user *)arg); 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci case TCFLSH: 6448c2ecf20Sopenharmony_ci switch (arg) { 6458c2ecf20Sopenharmony_ci case TCIOFLUSH: 6468c2ecf20Sopenharmony_ci case TCOFLUSH: 6478c2ecf20Sopenharmony_ci flush_tx_queue(tty); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci fallthrough; /* to default */ 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci default: 6528c2ecf20Sopenharmony_ci error = n_tty_ioctl_helper(tty, file, cmd, arg); 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci return error; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_ioctl() */ 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci/** 6608c2ecf20Sopenharmony_ci * n_hdlc_tty_poll - TTY callback for poll system call 6618c2ecf20Sopenharmony_ci * @tty: pointer to tty instance data 6628c2ecf20Sopenharmony_ci * @filp: pointer to open file object for device 6638c2ecf20Sopenharmony_ci * @wait: wait queue for operations 6648c2ecf20Sopenharmony_ci * 6658c2ecf20Sopenharmony_ci * Determine which operations (read/write) will not block and return info 6668c2ecf20Sopenharmony_ci * to caller. 6678c2ecf20Sopenharmony_ci * Returns a bit mask containing info on which ops will not block. 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_cistatic __poll_t n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, 6708c2ecf20Sopenharmony_ci poll_table *wait) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 6738c2ecf20Sopenharmony_ci __poll_t mask = 0; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (n_hdlc->magic != HDLC_MAGIC) 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* 6798c2ecf20Sopenharmony_ci * queue the current process into any wait queue that may awaken in the 6808c2ecf20Sopenharmony_ci * future (read and write) 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_ci poll_wait(filp, &tty->read_wait, wait); 6838c2ecf20Sopenharmony_ci poll_wait(filp, &tty->write_wait, wait); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* set bits for operations that won't block */ 6868c2ecf20Sopenharmony_ci if (!list_empty(&n_hdlc->rx_buf_list.list)) 6878c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; /* readable */ 6888c2ecf20Sopenharmony_ci if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) 6898c2ecf20Sopenharmony_ci mask |= EPOLLHUP; 6908c2ecf20Sopenharmony_ci if (tty_hung_up_p(filp)) 6918c2ecf20Sopenharmony_ci mask |= EPOLLHUP; 6928c2ecf20Sopenharmony_ci if (!tty_is_writelocked(tty) && 6938c2ecf20Sopenharmony_ci !list_empty(&n_hdlc->tx_free_buf_list.list)) 6948c2ecf20Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; /* writable */ 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return mask; 6978c2ecf20Sopenharmony_ci} /* end of n_hdlc_tty_poll() */ 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic void n_hdlc_alloc_buf(struct n_hdlc_buf_list *list, unsigned int count, 7008c2ecf20Sopenharmony_ci const char *name) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf; 7038c2ecf20Sopenharmony_ci unsigned int i; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 7068c2ecf20Sopenharmony_ci buf = kmalloc(struct_size(buf, buf, maxframe), GFP_KERNEL); 7078c2ecf20Sopenharmony_ci if (!buf) { 7088c2ecf20Sopenharmony_ci pr_debug("%s(), kmalloc() failed for %s buffer %u\n", 7098c2ecf20Sopenharmony_ci __func__, name, i); 7108c2ecf20Sopenharmony_ci return; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci n_hdlc_buf_put(list, buf); 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci/** 7178c2ecf20Sopenharmony_ci * n_hdlc_alloc - allocate an n_hdlc instance data structure 7188c2ecf20Sopenharmony_ci * 7198c2ecf20Sopenharmony_ci * Returns a pointer to newly created structure if success, otherwise %NULL 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_cistatic struct n_hdlc *n_hdlc_alloc(void) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct n_hdlc *n_hdlc = kzalloc(sizeof(*n_hdlc), GFP_KERNEL); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (!n_hdlc) 7268c2ecf20Sopenharmony_ci return NULL; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock); 7298c2ecf20Sopenharmony_ci spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock); 7308c2ecf20Sopenharmony_ci spin_lock_init(&n_hdlc->rx_buf_list.spinlock); 7318c2ecf20Sopenharmony_ci spin_lock_init(&n_hdlc->tx_buf_list.spinlock); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list); 7348c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list); 7358c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list); 7368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci n_hdlc_alloc_buf(&n_hdlc->rx_free_buf_list, DEFAULT_RX_BUF_COUNT, "rx"); 7398c2ecf20Sopenharmony_ci n_hdlc_alloc_buf(&n_hdlc->tx_free_buf_list, DEFAULT_TX_BUF_COUNT, "tx"); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* Initialize the control block */ 7428c2ecf20Sopenharmony_ci n_hdlc->magic = HDLC_MAGIC; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci return n_hdlc; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci} /* end of n_hdlc_alloc() */ 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/** 7498c2ecf20Sopenharmony_ci * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list 7508c2ecf20Sopenharmony_ci * @buf_list: pointer to the buffer list 7518c2ecf20Sopenharmony_ci * @buf: pointer to the buffer 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_cistatic void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, 7548c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci unsigned long flags; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci spin_lock_irqsave(&buf_list->spinlock, flags); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci list_add(&buf->list_item, &buf_list->list); 7618c2ecf20Sopenharmony_ci buf_list->count++; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&buf_list->spinlock, flags); 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/** 7678c2ecf20Sopenharmony_ci * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list 7688c2ecf20Sopenharmony_ci * @buf_list: pointer to buffer list 7698c2ecf20Sopenharmony_ci * @buf: pointer to buffer 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_cistatic void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list, 7728c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci unsigned long flags; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci spin_lock_irqsave(&buf_list->spinlock, flags); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci list_add_tail(&buf->list_item, &buf_list->list); 7798c2ecf20Sopenharmony_ci buf_list->count++; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&buf_list->spinlock, flags); 7828c2ecf20Sopenharmony_ci} /* end of n_hdlc_buf_put() */ 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci/** 7858c2ecf20Sopenharmony_ci * n_hdlc_buf_get - remove and return an HDLC buffer from list 7868c2ecf20Sopenharmony_ci * @buf_list: pointer to HDLC buffer list 7878c2ecf20Sopenharmony_ci * 7888c2ecf20Sopenharmony_ci * Remove and return an HDLC buffer from the head of the specified HDLC buffer 7898c2ecf20Sopenharmony_ci * list. 7908c2ecf20Sopenharmony_ci * Returns a pointer to HDLC buffer if available, otherwise %NULL. 7918c2ecf20Sopenharmony_ci */ 7928c2ecf20Sopenharmony_cistatic struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci unsigned long flags; 7958c2ecf20Sopenharmony_ci struct n_hdlc_buf *buf; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci spin_lock_irqsave(&buf_list->spinlock, flags); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci buf = list_first_entry_or_null(&buf_list->list, 8008c2ecf20Sopenharmony_ci struct n_hdlc_buf, list_item); 8018c2ecf20Sopenharmony_ci if (buf) { 8028c2ecf20Sopenharmony_ci list_del(&buf->list_item); 8038c2ecf20Sopenharmony_ci buf_list->count--; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&buf_list->spinlock, flags); 8078c2ecf20Sopenharmony_ci return buf; 8088c2ecf20Sopenharmony_ci} /* end of n_hdlc_buf_get() */ 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops n_hdlc_ldisc = { 8118c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8128c2ecf20Sopenharmony_ci .magic = TTY_LDISC_MAGIC, 8138c2ecf20Sopenharmony_ci .name = "hdlc", 8148c2ecf20Sopenharmony_ci .open = n_hdlc_tty_open, 8158c2ecf20Sopenharmony_ci .close = n_hdlc_tty_close, 8168c2ecf20Sopenharmony_ci .read = n_hdlc_tty_read, 8178c2ecf20Sopenharmony_ci .write = n_hdlc_tty_write, 8188c2ecf20Sopenharmony_ci .ioctl = n_hdlc_tty_ioctl, 8198c2ecf20Sopenharmony_ci .poll = n_hdlc_tty_poll, 8208c2ecf20Sopenharmony_ci .receive_buf = n_hdlc_tty_receive, 8218c2ecf20Sopenharmony_ci .write_wakeup = n_hdlc_tty_wakeup, 8228c2ecf20Sopenharmony_ci .flush_buffer = flush_rx_queue, 8238c2ecf20Sopenharmony_ci}; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic int __init n_hdlc_init(void) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci int status; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* range check maxframe arg */ 8308c2ecf20Sopenharmony_ci maxframe = clamp(maxframe, 4096, MAX_HDLC_FRAME_SIZE); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc); 8338c2ecf20Sopenharmony_ci if (!status) 8348c2ecf20Sopenharmony_ci pr_info("N_HDLC line discipline registered with maxframe=%d\n", 8358c2ecf20Sopenharmony_ci maxframe); 8368c2ecf20Sopenharmony_ci else 8378c2ecf20Sopenharmony_ci pr_err("N_HDLC: error registering line discipline: %d\n", 8388c2ecf20Sopenharmony_ci status); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return status; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci} /* end of init_module() */ 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic void __exit n_hdlc_exit(void) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci /* Release tty registration of line discipline */ 8478c2ecf20Sopenharmony_ci int status = tty_unregister_ldisc(N_HDLC); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (status) 8508c2ecf20Sopenharmony_ci pr_err("N_HDLC: can't unregister line discipline (err = %d)\n", 8518c2ecf20Sopenharmony_ci status); 8528c2ecf20Sopenharmony_ci else 8538c2ecf20Sopenharmony_ci pr_info("N_HDLC: line discipline unregistered\n"); 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cimodule_init(n_hdlc_init); 8578c2ecf20Sopenharmony_cimodule_exit(n_hdlc_exit); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Fulghum paulkf@microgate.com"); 8618c2ecf20Sopenharmony_cimodule_param(maxframe, int, 0); 8628c2ecf20Sopenharmony_ciMODULE_ALIAS_LDISC(N_HDLC); 863