162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* generic HDLC line discipline for Linux 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Written by Paul Fulghum paulkf@microgate.com 562306a36Sopenharmony_ci * for Microgate Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Microgate and SyncLink are registered trademarks of Microgate Corporation 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Adapted from ppp.c, written by Michael Callahan <callahan@maths.ox.ac.uk>, 1062306a36Sopenharmony_ci * Al Longyear <longyear@netcom.com>, 1162306a36Sopenharmony_ci * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Original release 01/11/99 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * This module implements the tty line discipline N_HDLC for use with 1662306a36Sopenharmony_ci * tty device drivers that support bit-synchronous HDLC communications. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * All HDLC data is frame oriented which means: 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * 1. tty write calls represent one complete transmit frame of data 2162306a36Sopenharmony_ci * The device driver should accept the complete frame or none of 2262306a36Sopenharmony_ci * the frame (busy) in the write method. Each write call should have 2362306a36Sopenharmony_ci * a byte count in the range of 2-65535 bytes (2 is min HDLC frame 2462306a36Sopenharmony_ci * with 1 addr byte and 1 ctrl byte). The max byte count of 65535 2562306a36Sopenharmony_ci * should include any crc bytes required. For example, when using 2662306a36Sopenharmony_ci * CCITT CRC32, 4 crc bytes are required, so the maximum size frame 2762306a36Sopenharmony_ci * the application may transmit is limited to 65531 bytes. For CCITT 2862306a36Sopenharmony_ci * CRC16, the maximum application frame size would be 65533. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * 2. receive callbacks from the device driver represents 3262306a36Sopenharmony_ci * one received frame. The device driver should bypass 3362306a36Sopenharmony_ci * the tty flip buffer and call the line discipline receive 3462306a36Sopenharmony_ci * callback directly to avoid fragmenting or concatenating 3562306a36Sopenharmony_ci * multiple frames into a single receive callback. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * The HDLC line discipline queues the receive frames in separate 3862306a36Sopenharmony_ci * buffers so complete receive frames can be returned by the 3962306a36Sopenharmony_ci * tty read calls. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * 3. tty read calls returns an entire frame of data or nothing. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * 4. all send and receive data is considered raw. No processing 4462306a36Sopenharmony_ci * or translation is performed by the line discipline, regardless 4562306a36Sopenharmony_ci * of the tty flags 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * 5. When line discipline is queried for the amount of receive 4862306a36Sopenharmony_ci * data available (FIOC), 0 is returned if no data available, 4962306a36Sopenharmony_ci * otherwise the count of the next available frame is returned. 5062306a36Sopenharmony_ci * (instead of the sum of all received frame counts). 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * These conventions allow the standard tty programming interface 5362306a36Sopenharmony_ci * to be used for synchronous HDLC applications when used with 5462306a36Sopenharmony_ci * this line discipline (or another line discipline that is frame 5562306a36Sopenharmony_ci * oriented such as N_PPP). 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * The SyncLink driver (synclink.c) implements both asynchronous 5862306a36Sopenharmony_ci * (using standard line discipline N_TTY) and synchronous HDLC 5962306a36Sopenharmony_ci * (using N_HDLC) communications, with the latter using the above 6062306a36Sopenharmony_ci * conventions. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * This implementation is very basic and does not maintain 6362306a36Sopenharmony_ci * any statistics. The main point is to enforce the raw data 6462306a36Sopenharmony_ci * and frame orientation of HDLC communications. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 6762306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 6862306a36Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 6962306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 7062306a36Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7162306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7262306a36Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 7362306a36Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 7462306a36Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 7562306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 7662306a36Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#include <linux/module.h> 8062306a36Sopenharmony_ci#include <linux/init.h> 8162306a36Sopenharmony_ci#include <linux/kernel.h> 8262306a36Sopenharmony_ci#include <linux/sched.h> 8362306a36Sopenharmony_ci#include <linux/types.h> 8462306a36Sopenharmony_ci#include <linux/fcntl.h> 8562306a36Sopenharmony_ci#include <linux/interrupt.h> 8662306a36Sopenharmony_ci#include <linux/ptrace.h> 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#include <linux/poll.h> 8962306a36Sopenharmony_ci#include <linux/in.h> 9062306a36Sopenharmony_ci#include <linux/ioctl.h> 9162306a36Sopenharmony_ci#include <linux/slab.h> 9262306a36Sopenharmony_ci#include <linux/tty.h> 9362306a36Sopenharmony_ci#include <linux/errno.h> 9462306a36Sopenharmony_ci#include <linux/string.h> /* used in new tty drivers */ 9562306a36Sopenharmony_ci#include <linux/signal.h> /* used in new tty drivers */ 9662306a36Sopenharmony_ci#include <linux/if.h> 9762306a36Sopenharmony_ci#include <linux/bitops.h> 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#include <linux/uaccess.h> 10062306a36Sopenharmony_ci#include "tty.h" 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Buffers for individual HDLC frames 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci#define MAX_HDLC_FRAME_SIZE 65535 10662306a36Sopenharmony_ci#define DEFAULT_RX_BUF_COUNT 10 10762306a36Sopenharmony_ci#define MAX_RX_BUF_COUNT 60 10862306a36Sopenharmony_ci#define DEFAULT_TX_BUF_COUNT 3 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct n_hdlc_buf { 11162306a36Sopenharmony_ci struct list_head list_item; 11262306a36Sopenharmony_ci int count; 11362306a36Sopenharmony_ci char buf[]; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct n_hdlc_buf_list { 11762306a36Sopenharmony_ci struct list_head list; 11862306a36Sopenharmony_ci int count; 11962306a36Sopenharmony_ci spinlock_t spinlock; 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/** 12362306a36Sopenharmony_ci * struct n_hdlc - per device instance data structure 12462306a36Sopenharmony_ci * @tbusy: reentrancy flag for tx wakeup code 12562306a36Sopenharmony_ci * @woke_up: tx wakeup needs to be run again as it was called while @tbusy 12662306a36Sopenharmony_ci * @tx_buf_list: list of pending transmit frame buffers 12762306a36Sopenharmony_ci * @rx_buf_list: list of received frame buffers 12862306a36Sopenharmony_ci * @tx_free_buf_list: list unused transmit frame buffers 12962306a36Sopenharmony_ci * @rx_free_buf_list: list unused received frame buffers 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistruct n_hdlc { 13262306a36Sopenharmony_ci bool tbusy; 13362306a36Sopenharmony_ci bool woke_up; 13462306a36Sopenharmony_ci struct n_hdlc_buf_list tx_buf_list; 13562306a36Sopenharmony_ci struct n_hdlc_buf_list rx_buf_list; 13662306a36Sopenharmony_ci struct n_hdlc_buf_list tx_free_buf_list; 13762306a36Sopenharmony_ci struct n_hdlc_buf_list rx_free_buf_list; 13862306a36Sopenharmony_ci struct work_struct write_work; 13962306a36Sopenharmony_ci struct tty_struct *tty_for_write_work; 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * HDLC buffer list manipulation functions 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistatic void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, 14662306a36Sopenharmony_ci struct n_hdlc_buf *buf); 14762306a36Sopenharmony_cistatic void n_hdlc_buf_put(struct n_hdlc_buf_list *list, 14862306a36Sopenharmony_ci struct n_hdlc_buf *buf); 14962306a36Sopenharmony_cistatic struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* Local functions */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct n_hdlc *n_hdlc_alloc(void); 15462306a36Sopenharmony_cistatic void n_hdlc_tty_write_work(struct work_struct *work); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* max frame size for memory allocations */ 15762306a36Sopenharmony_cistatic int maxframe = 4096; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void flush_rx_queue(struct tty_struct *tty) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 16262306a36Sopenharmony_ci struct n_hdlc_buf *buf; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci while ((buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list))) 16562306a36Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, buf); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void flush_tx_queue(struct tty_struct *tty) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 17162306a36Sopenharmony_ci struct n_hdlc_buf *buf; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) 17462306a36Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void n_hdlc_free_buf_list(struct n_hdlc_buf_list *list) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct n_hdlc_buf *buf; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci do { 18262306a36Sopenharmony_ci buf = n_hdlc_buf_get(list); 18362306a36Sopenharmony_ci kfree(buf); 18462306a36Sopenharmony_ci } while (buf); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/** 18862306a36Sopenharmony_ci * n_hdlc_tty_close - line discipline close 18962306a36Sopenharmony_ci * @tty: pointer to tty info structure 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * Called when the line discipline is changed to something 19262306a36Sopenharmony_ci * else, the tty is closed, or the tty detects a hangup. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_cistatic void n_hdlc_tty_close(struct tty_struct *tty) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci#if defined(TTY_NO_WRITE_SPLIT) 19962306a36Sopenharmony_ci clear_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 20062306a36Sopenharmony_ci#endif 20162306a36Sopenharmony_ci tty->disc_data = NULL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Ensure that the n_hdlcd process is not hanging on select()/poll() */ 20462306a36Sopenharmony_ci wake_up_interruptible(&tty->read_wait); 20562306a36Sopenharmony_ci wake_up_interruptible(&tty->write_wait); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci cancel_work_sync(&n_hdlc->write_work); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->rx_free_buf_list); 21062306a36Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->tx_free_buf_list); 21162306a36Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->rx_buf_list); 21262306a36Sopenharmony_ci n_hdlc_free_buf_list(&n_hdlc->tx_buf_list); 21362306a36Sopenharmony_ci kfree(n_hdlc); 21462306a36Sopenharmony_ci} /* end of n_hdlc_tty_close() */ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/** 21762306a36Sopenharmony_ci * n_hdlc_tty_open - called when line discipline changed to n_hdlc 21862306a36Sopenharmony_ci * @tty: pointer to tty info structure 21962306a36Sopenharmony_ci * 22062306a36Sopenharmony_ci * Returns 0 if success, otherwise error code 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic int n_hdlc_tty_open(struct tty_struct *tty) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci pr_debug("%s() called (device=%s)\n", __func__, tty->name); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* There should not be an existing table for this slot. */ 22962306a36Sopenharmony_ci if (n_hdlc) { 23062306a36Sopenharmony_ci pr_err("%s: tty already associated!\n", __func__); 23162306a36Sopenharmony_ci return -EEXIST; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci n_hdlc = n_hdlc_alloc(); 23562306a36Sopenharmony_ci if (!n_hdlc) { 23662306a36Sopenharmony_ci pr_err("%s: n_hdlc_alloc failed\n", __func__); 23762306a36Sopenharmony_ci return -ENFILE; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci INIT_WORK(&n_hdlc->write_work, n_hdlc_tty_write_work); 24162306a36Sopenharmony_ci n_hdlc->tty_for_write_work = tty; 24262306a36Sopenharmony_ci tty->disc_data = n_hdlc; 24362306a36Sopenharmony_ci tty->receive_room = 65536; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* change tty_io write() to not split large writes into 8K chunks */ 24662306a36Sopenharmony_ci set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* flush receive data from driver */ 24962306a36Sopenharmony_ci tty_driver_flush_buffer(tty); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci} /* end of n_tty_hdlc_open() */ 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * n_hdlc_send_frames - send frames on pending send buffer list 25762306a36Sopenharmony_ci * @n_hdlc: pointer to ldisc instance data 25862306a36Sopenharmony_ci * @tty: pointer to tty instance data 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * Send frames on pending send buffer list until the driver does not accept a 26162306a36Sopenharmony_ci * frame (busy) this function is called after adding a frame to the send buffer 26262306a36Sopenharmony_ci * list and by the tty wakeup callback. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci register int actual; 26762306a36Sopenharmony_ci unsigned long flags; 26862306a36Sopenharmony_ci struct n_hdlc_buf *tbuf; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cicheck_again: 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); 27362306a36Sopenharmony_ci if (n_hdlc->tbusy) { 27462306a36Sopenharmony_ci n_hdlc->woke_up = true; 27562306a36Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci n_hdlc->tbusy = true; 27962306a36Sopenharmony_ci n_hdlc->woke_up = false; 28062306a36Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); 28362306a36Sopenharmony_ci while (tbuf) { 28462306a36Sopenharmony_ci pr_debug("sending frame %p, count=%d\n", tbuf, tbuf->count); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Send the next block of data to device */ 28762306a36Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 28862306a36Sopenharmony_ci actual = tty->ops->write(tty, tbuf->buf, tbuf->count); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* rollback was possible and has been done */ 29162306a36Sopenharmony_ci if (actual == -ERESTARTSYS) { 29262306a36Sopenharmony_ci n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci /* if transmit error, throw frame away by */ 29662306a36Sopenharmony_ci /* pretending it was accepted by driver */ 29762306a36Sopenharmony_ci if (actual < 0) 29862306a36Sopenharmony_ci actual = tbuf->count; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (actual == tbuf->count) { 30162306a36Sopenharmony_ci pr_debug("frame %p completed\n", tbuf); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* free current transmit buffer */ 30462306a36Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* wait up sleeping writers */ 30762306a36Sopenharmony_ci wake_up_interruptible(&tty->write_wait); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* get next pending transmit buffer */ 31062306a36Sopenharmony_ci tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); 31162306a36Sopenharmony_ci } else { 31262306a36Sopenharmony_ci pr_debug("frame %p pending\n", tbuf); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * the buffer was not accepted by driver, 31662306a36Sopenharmony_ci * return it back into tx queue 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!tbuf) 32462306a36Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* Clear the re-entry flag */ 32762306a36Sopenharmony_ci spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); 32862306a36Sopenharmony_ci n_hdlc->tbusy = false; 32962306a36Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (n_hdlc->woke_up) 33262306a36Sopenharmony_ci goto check_again; 33362306a36Sopenharmony_ci} /* end of n_hdlc_send_frames() */ 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/** 33662306a36Sopenharmony_ci * n_hdlc_tty_write_work - Asynchronous callback for transmit wakeup 33762306a36Sopenharmony_ci * @work: pointer to work_struct 33862306a36Sopenharmony_ci * 33962306a36Sopenharmony_ci * Called when low level device driver can accept more send data. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_cistatic void n_hdlc_tty_write_work(struct work_struct *work) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct n_hdlc *n_hdlc = container_of(work, struct n_hdlc, write_work); 34462306a36Sopenharmony_ci struct tty_struct *tty = n_hdlc->tty_for_write_work; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci n_hdlc_send_frames(n_hdlc, tty); 34762306a36Sopenharmony_ci} /* end of n_hdlc_tty_write_work() */ 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/** 35062306a36Sopenharmony_ci * n_hdlc_tty_wakeup - Callback for transmit wakeup 35162306a36Sopenharmony_ci * @tty: pointer to associated tty instance data 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * Called when low level device driver can accept more send data. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic void n_hdlc_tty_wakeup(struct tty_struct *tty) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci schedule_work(&n_hdlc->write_work); 36062306a36Sopenharmony_ci} /* end of n_hdlc_tty_wakeup() */ 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/** 36362306a36Sopenharmony_ci * n_hdlc_tty_receive - Called by tty driver when receive data is available 36462306a36Sopenharmony_ci * @tty: pointer to tty instance data 36562306a36Sopenharmony_ci * @data: pointer to received data 36662306a36Sopenharmony_ci * @flags: pointer to flags for data 36762306a36Sopenharmony_ci * @count: count of received data in bytes 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * Called by tty low level driver when receive data is available. Data is 37062306a36Sopenharmony_ci * interpreted as one HDLC frame. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistatic void n_hdlc_tty_receive(struct tty_struct *tty, const u8 *data, 37362306a36Sopenharmony_ci const u8 *flags, size_t count) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci register struct n_hdlc *n_hdlc = tty->disc_data; 37662306a36Sopenharmony_ci register struct n_hdlc_buf *buf; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci pr_debug("%s() called count=%zu\n", __func__, count); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (count > maxframe) { 38162306a36Sopenharmony_ci pr_debug("rx count>maxframesize, data discarded\n"); 38262306a36Sopenharmony_ci return; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* get a free HDLC buffer */ 38662306a36Sopenharmony_ci buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); 38762306a36Sopenharmony_ci if (!buf) { 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * no buffers in free list, attempt to allocate another rx 39062306a36Sopenharmony_ci * buffer unless the maximum count has been reached 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT) 39362306a36Sopenharmony_ci buf = kmalloc(struct_size(buf, buf, maxframe), 39462306a36Sopenharmony_ci GFP_ATOMIC); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!buf) { 39862306a36Sopenharmony_ci pr_debug("no more rx buffers, data discarded\n"); 39962306a36Sopenharmony_ci return; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* copy received data to HDLC buffer */ 40362306a36Sopenharmony_ci memcpy(buf->buf, data, count); 40462306a36Sopenharmony_ci buf->count = count; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* add HDLC buffer to list of received frames */ 40762306a36Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->rx_buf_list, buf); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* wake up any blocked reads and perform async signalling */ 41062306a36Sopenharmony_ci wake_up_interruptible(&tty->read_wait); 41162306a36Sopenharmony_ci if (tty->fasync != NULL) 41262306a36Sopenharmony_ci kill_fasync(&tty->fasync, SIGIO, POLL_IN); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci} /* end of n_hdlc_tty_receive() */ 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/** 41762306a36Sopenharmony_ci * n_hdlc_tty_read - Called to retrieve one frame of data (if available) 41862306a36Sopenharmony_ci * @tty: pointer to tty instance data 41962306a36Sopenharmony_ci * @file: pointer to open file object 42062306a36Sopenharmony_ci * @kbuf: pointer to returned data buffer 42162306a36Sopenharmony_ci * @nr: size of returned data buffer 42262306a36Sopenharmony_ci * @cookie: stored rbuf from previous run 42362306a36Sopenharmony_ci * @offset: offset into the data buffer 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * Returns the number of bytes returned or error code. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_cistatic ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, 42862306a36Sopenharmony_ci u8 *kbuf, size_t nr, void **cookie, 42962306a36Sopenharmony_ci unsigned long offset) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 43262306a36Sopenharmony_ci int ret = 0; 43362306a36Sopenharmony_ci struct n_hdlc_buf *rbuf; 43462306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Is this a repeated call for an rbuf we already found earlier? */ 43762306a36Sopenharmony_ci rbuf = *cookie; 43862306a36Sopenharmony_ci if (rbuf) 43962306a36Sopenharmony_ci goto have_rbuf; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci add_wait_queue(&tty->read_wait, &wait); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (;;) { 44462306a36Sopenharmony_ci if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { 44562306a36Sopenharmony_ci ret = -EIO; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci if (tty_hung_up_p(file)) 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); 45462306a36Sopenharmony_ci if (rbuf) 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* no data */ 45862306a36Sopenharmony_ci if (tty_io_nonblock(tty, file)) { 45962306a36Sopenharmony_ci ret = -EAGAIN; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci schedule(); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (signal_pending(current)) { 46662306a36Sopenharmony_ci ret = -EINTR; 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci remove_wait_queue(&tty->read_wait, &wait); 47262306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (!rbuf) 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci *cookie = rbuf; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cihave_rbuf: 47962306a36Sopenharmony_ci /* Have we used it up entirely? */ 48062306a36Sopenharmony_ci if (offset >= rbuf->count) 48162306a36Sopenharmony_ci goto done_with_rbuf; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* More data to go, but can't copy any more? EOVERFLOW */ 48462306a36Sopenharmony_ci ret = -EOVERFLOW; 48562306a36Sopenharmony_ci if (!nr) 48662306a36Sopenharmony_ci goto done_with_rbuf; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Copy as much data as possible */ 48962306a36Sopenharmony_ci ret = rbuf->count - offset; 49062306a36Sopenharmony_ci if (ret > nr) 49162306a36Sopenharmony_ci ret = nr; 49262306a36Sopenharmony_ci memcpy(kbuf, rbuf->buf+offset, ret); 49362306a36Sopenharmony_ci offset += ret; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* If we still have data left, we leave the rbuf in the cookie */ 49662306a36Sopenharmony_ci if (offset < rbuf->count) 49762306a36Sopenharmony_ci return ret; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cidone_with_rbuf: 50062306a36Sopenharmony_ci *cookie = NULL; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT) 50362306a36Sopenharmony_ci kfree(rbuf); 50462306a36Sopenharmony_ci else 50562306a36Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return ret; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci} /* end of n_hdlc_tty_read() */ 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/** 51262306a36Sopenharmony_ci * n_hdlc_tty_write - write a single frame of data to device 51362306a36Sopenharmony_ci * @tty: pointer to associated tty device instance data 51462306a36Sopenharmony_ci * @file: pointer to file object data 51562306a36Sopenharmony_ci * @data: pointer to transmit data (one frame) 51662306a36Sopenharmony_ci * @count: size of transmit frame in bytes 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * Returns the number of bytes written (or error code). 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_cistatic ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, 52162306a36Sopenharmony_ci const u8 *data, size_t count) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 52462306a36Sopenharmony_ci int error = 0; 52562306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 52662306a36Sopenharmony_ci struct n_hdlc_buf *tbuf; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci pr_debug("%s() called count=%zd\n", __func__, count); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* verify frame size */ 53162306a36Sopenharmony_ci if (count > maxframe) { 53262306a36Sopenharmony_ci pr_debug("%s: truncating user packet from %zu to %d\n", 53362306a36Sopenharmony_ci __func__, count, maxframe); 53462306a36Sopenharmony_ci count = maxframe; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci add_wait_queue(&tty->write_wait, &wait); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci for (;;) { 54062306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); 54362306a36Sopenharmony_ci if (tbuf) 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (tty_io_nonblock(tty, file)) { 54762306a36Sopenharmony_ci error = -EAGAIN; 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci schedule(); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (signal_pending(current)) { 55362306a36Sopenharmony_ci error = -EINTR; 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 55962306a36Sopenharmony_ci remove_wait_queue(&tty->write_wait, &wait); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (!error) { 56262306a36Sopenharmony_ci /* Retrieve the user's buffer */ 56362306a36Sopenharmony_ci memcpy(tbuf->buf, data, count); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Send the data */ 56662306a36Sopenharmony_ci tbuf->count = error = count; 56762306a36Sopenharmony_ci n_hdlc_buf_put(&n_hdlc->tx_buf_list, tbuf); 56862306a36Sopenharmony_ci n_hdlc_send_frames(n_hdlc, tty); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return error; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci} /* end of n_hdlc_tty_write() */ 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/** 57662306a36Sopenharmony_ci * n_hdlc_tty_ioctl - process IOCTL system call for the tty device. 57762306a36Sopenharmony_ci * @tty: pointer to tty instance data 57862306a36Sopenharmony_ci * @cmd: IOCTL command code 57962306a36Sopenharmony_ci * @arg: argument for IOCTL call (cmd dependent) 58062306a36Sopenharmony_ci * 58162306a36Sopenharmony_ci * Returns command dependent result. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_cistatic int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd, 58462306a36Sopenharmony_ci unsigned long arg) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 58762306a36Sopenharmony_ci int error = 0; 58862306a36Sopenharmony_ci int count; 58962306a36Sopenharmony_ci unsigned long flags; 59062306a36Sopenharmony_ci struct n_hdlc_buf *buf = NULL; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci pr_debug("%s() called %d\n", __func__, cmd); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci switch (cmd) { 59562306a36Sopenharmony_ci case FIONREAD: 59662306a36Sopenharmony_ci /* report count of read data available */ 59762306a36Sopenharmony_ci /* in next available frame (if any) */ 59862306a36Sopenharmony_ci spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock, flags); 59962306a36Sopenharmony_ci buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list, 60062306a36Sopenharmony_ci struct n_hdlc_buf, list_item); 60162306a36Sopenharmony_ci if (buf) 60262306a36Sopenharmony_ci count = buf->count; 60362306a36Sopenharmony_ci else 60462306a36Sopenharmony_ci count = 0; 60562306a36Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock, flags); 60662306a36Sopenharmony_ci error = put_user(count, (int __user *)arg); 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci case TIOCOUTQ: 61062306a36Sopenharmony_ci /* get the pending tx byte count in the driver */ 61162306a36Sopenharmony_ci count = tty_chars_in_buffer(tty); 61262306a36Sopenharmony_ci /* add size of next output frame in queue */ 61362306a36Sopenharmony_ci spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); 61462306a36Sopenharmony_ci buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list, 61562306a36Sopenharmony_ci struct n_hdlc_buf, list_item); 61662306a36Sopenharmony_ci if (buf) 61762306a36Sopenharmony_ci count += buf->count; 61862306a36Sopenharmony_ci spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 61962306a36Sopenharmony_ci error = put_user(count, (int __user *)arg); 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci case TCFLSH: 62362306a36Sopenharmony_ci switch (arg) { 62462306a36Sopenharmony_ci case TCIOFLUSH: 62562306a36Sopenharmony_ci case TCOFLUSH: 62662306a36Sopenharmony_ci flush_tx_queue(tty); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci fallthrough; /* to default */ 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci default: 63162306a36Sopenharmony_ci error = n_tty_ioctl_helper(tty, cmd, arg); 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci return error; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci} /* end of n_hdlc_tty_ioctl() */ 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/** 63962306a36Sopenharmony_ci * n_hdlc_tty_poll - TTY callback for poll system call 64062306a36Sopenharmony_ci * @tty: pointer to tty instance data 64162306a36Sopenharmony_ci * @filp: pointer to open file object for device 64262306a36Sopenharmony_ci * @wait: wait queue for operations 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * Determine which operations (read/write) will not block and return info 64562306a36Sopenharmony_ci * to caller. 64662306a36Sopenharmony_ci * Returns a bit mask containing info on which ops will not block. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic __poll_t n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, 64962306a36Sopenharmony_ci poll_table *wait) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct n_hdlc *n_hdlc = tty->disc_data; 65262306a36Sopenharmony_ci __poll_t mask = 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * queue the current process into any wait queue that may awaken in the 65662306a36Sopenharmony_ci * future (read and write) 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci poll_wait(filp, &tty->read_wait, wait); 65962306a36Sopenharmony_ci poll_wait(filp, &tty->write_wait, wait); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* set bits for operations that won't block */ 66262306a36Sopenharmony_ci if (!list_empty(&n_hdlc->rx_buf_list.list)) 66362306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; /* readable */ 66462306a36Sopenharmony_ci if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) 66562306a36Sopenharmony_ci mask |= EPOLLHUP; 66662306a36Sopenharmony_ci if (tty_hung_up_p(filp)) 66762306a36Sopenharmony_ci mask |= EPOLLHUP; 66862306a36Sopenharmony_ci if (!tty_is_writelocked(tty) && 66962306a36Sopenharmony_ci !list_empty(&n_hdlc->tx_free_buf_list.list)) 67062306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; /* writable */ 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return mask; 67362306a36Sopenharmony_ci} /* end of n_hdlc_tty_poll() */ 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic void n_hdlc_alloc_buf(struct n_hdlc_buf_list *list, unsigned int count, 67662306a36Sopenharmony_ci const char *name) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct n_hdlc_buf *buf; 67962306a36Sopenharmony_ci unsigned int i; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci for (i = 0; i < count; i++) { 68262306a36Sopenharmony_ci buf = kmalloc(struct_size(buf, buf, maxframe), GFP_KERNEL); 68362306a36Sopenharmony_ci if (!buf) { 68462306a36Sopenharmony_ci pr_debug("%s(), kmalloc() failed for %s buffer %u\n", 68562306a36Sopenharmony_ci __func__, name, i); 68662306a36Sopenharmony_ci return; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci n_hdlc_buf_put(list, buf); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/** 69362306a36Sopenharmony_ci * n_hdlc_alloc - allocate an n_hdlc instance data structure 69462306a36Sopenharmony_ci * 69562306a36Sopenharmony_ci * Returns a pointer to newly created structure if success, otherwise %NULL 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_cistatic struct n_hdlc *n_hdlc_alloc(void) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct n_hdlc *n_hdlc = kzalloc(sizeof(*n_hdlc), GFP_KERNEL); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (!n_hdlc) 70262306a36Sopenharmony_ci return NULL; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock); 70562306a36Sopenharmony_ci spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock); 70662306a36Sopenharmony_ci spin_lock_init(&n_hdlc->rx_buf_list.spinlock); 70762306a36Sopenharmony_ci spin_lock_init(&n_hdlc->tx_buf_list.spinlock); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list); 71062306a36Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list); 71162306a36Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list); 71262306a36Sopenharmony_ci INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci n_hdlc_alloc_buf(&n_hdlc->rx_free_buf_list, DEFAULT_RX_BUF_COUNT, "rx"); 71562306a36Sopenharmony_ci n_hdlc_alloc_buf(&n_hdlc->tx_free_buf_list, DEFAULT_TX_BUF_COUNT, "tx"); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return n_hdlc; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci} /* end of n_hdlc_alloc() */ 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/** 72262306a36Sopenharmony_ci * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list 72362306a36Sopenharmony_ci * @buf_list: pointer to the buffer list 72462306a36Sopenharmony_ci * @buf: pointer to the buffer 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_cistatic void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, 72762306a36Sopenharmony_ci struct n_hdlc_buf *buf) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci unsigned long flags; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci spin_lock_irqsave(&buf_list->spinlock, flags); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci list_add(&buf->list_item, &buf_list->list); 73462306a36Sopenharmony_ci buf_list->count++; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci spin_unlock_irqrestore(&buf_list->spinlock, flags); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/** 74062306a36Sopenharmony_ci * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list 74162306a36Sopenharmony_ci * @buf_list: pointer to buffer list 74262306a36Sopenharmony_ci * @buf: pointer to buffer 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_cistatic void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list, 74562306a36Sopenharmony_ci struct n_hdlc_buf *buf) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci unsigned long flags; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci spin_lock_irqsave(&buf_list->spinlock, flags); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci list_add_tail(&buf->list_item, &buf_list->list); 75262306a36Sopenharmony_ci buf_list->count++; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci spin_unlock_irqrestore(&buf_list->spinlock, flags); 75562306a36Sopenharmony_ci} /* end of n_hdlc_buf_put() */ 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci/** 75862306a36Sopenharmony_ci * n_hdlc_buf_get - remove and return an HDLC buffer from list 75962306a36Sopenharmony_ci * @buf_list: pointer to HDLC buffer list 76062306a36Sopenharmony_ci * 76162306a36Sopenharmony_ci * Remove and return an HDLC buffer from the head of the specified HDLC buffer 76262306a36Sopenharmony_ci * list. 76362306a36Sopenharmony_ci * Returns a pointer to HDLC buffer if available, otherwise %NULL. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_cistatic struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci unsigned long flags; 76862306a36Sopenharmony_ci struct n_hdlc_buf *buf; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci spin_lock_irqsave(&buf_list->spinlock, flags); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci buf = list_first_entry_or_null(&buf_list->list, 77362306a36Sopenharmony_ci struct n_hdlc_buf, list_item); 77462306a36Sopenharmony_ci if (buf) { 77562306a36Sopenharmony_ci list_del(&buf->list_item); 77662306a36Sopenharmony_ci buf_list->count--; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci spin_unlock_irqrestore(&buf_list->spinlock, flags); 78062306a36Sopenharmony_ci return buf; 78162306a36Sopenharmony_ci} /* end of n_hdlc_buf_get() */ 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic struct tty_ldisc_ops n_hdlc_ldisc = { 78462306a36Sopenharmony_ci .owner = THIS_MODULE, 78562306a36Sopenharmony_ci .num = N_HDLC, 78662306a36Sopenharmony_ci .name = "hdlc", 78762306a36Sopenharmony_ci .open = n_hdlc_tty_open, 78862306a36Sopenharmony_ci .close = n_hdlc_tty_close, 78962306a36Sopenharmony_ci .read = n_hdlc_tty_read, 79062306a36Sopenharmony_ci .write = n_hdlc_tty_write, 79162306a36Sopenharmony_ci .ioctl = n_hdlc_tty_ioctl, 79262306a36Sopenharmony_ci .poll = n_hdlc_tty_poll, 79362306a36Sopenharmony_ci .receive_buf = n_hdlc_tty_receive, 79462306a36Sopenharmony_ci .write_wakeup = n_hdlc_tty_wakeup, 79562306a36Sopenharmony_ci .flush_buffer = flush_rx_queue, 79662306a36Sopenharmony_ci}; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic int __init n_hdlc_init(void) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci int status; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* range check maxframe arg */ 80362306a36Sopenharmony_ci maxframe = clamp(maxframe, 4096, MAX_HDLC_FRAME_SIZE); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci status = tty_register_ldisc(&n_hdlc_ldisc); 80662306a36Sopenharmony_ci if (!status) 80762306a36Sopenharmony_ci pr_info("N_HDLC line discipline registered with maxframe=%d\n", 80862306a36Sopenharmony_ci maxframe); 80962306a36Sopenharmony_ci else 81062306a36Sopenharmony_ci pr_err("N_HDLC: error registering line discipline: %d\n", 81162306a36Sopenharmony_ci status); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return status; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci} /* end of init_module() */ 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic void __exit n_hdlc_exit(void) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci tty_unregister_ldisc(&n_hdlc_ldisc); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cimodule_init(n_hdlc_init); 82362306a36Sopenharmony_cimodule_exit(n_hdlc_exit); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 82662306a36Sopenharmony_ciMODULE_AUTHOR("Paul Fulghum paulkf@microgate.com"); 82762306a36Sopenharmony_cimodule_param(maxframe, int, 0); 82862306a36Sopenharmony_ciMODULE_ALIAS_LDISC(N_HDLC); 829