1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-1.0+
296fd7ce5SGreg Kroah-Hartman /* generic HDLC line discipline for Linux
396fd7ce5SGreg Kroah-Hartman *
496fd7ce5SGreg Kroah-Hartman * Written by Paul Fulghum paulkf@microgate.com
596fd7ce5SGreg Kroah-Hartman * for Microgate Corporation
696fd7ce5SGreg Kroah-Hartman *
796fd7ce5SGreg Kroah-Hartman * Microgate and SyncLink are registered trademarks of Microgate Corporation
896fd7ce5SGreg Kroah-Hartman *
996fd7ce5SGreg Kroah-Hartman * Adapted from ppp.c, written by Michael Callahan <callahan@maths.ox.ac.uk>,
1096fd7ce5SGreg Kroah-Hartman * Al Longyear <longyear@netcom.com>,
1196fd7ce5SGreg Kroah-Hartman * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
1296fd7ce5SGreg Kroah-Hartman *
1396fd7ce5SGreg Kroah-Hartman * Original release 01/11/99
1496fd7ce5SGreg Kroah-Hartman *
1596fd7ce5SGreg Kroah-Hartman * This module implements the tty line discipline N_HDLC for use with
1696fd7ce5SGreg Kroah-Hartman * tty device drivers that support bit-synchronous HDLC communications.
1796fd7ce5SGreg Kroah-Hartman *
1896fd7ce5SGreg Kroah-Hartman * All HDLC data is frame oriented which means:
1996fd7ce5SGreg Kroah-Hartman *
2096fd7ce5SGreg Kroah-Hartman * 1. tty write calls represent one complete transmit frame of data
2196fd7ce5SGreg Kroah-Hartman * The device driver should accept the complete frame or none of
2296fd7ce5SGreg Kroah-Hartman * the frame (busy) in the write method. Each write call should have
2396fd7ce5SGreg Kroah-Hartman * a byte count in the range of 2-65535 bytes (2 is min HDLC frame
2496fd7ce5SGreg Kroah-Hartman * with 1 addr byte and 1 ctrl byte). The max byte count of 65535
2596fd7ce5SGreg Kroah-Hartman * should include any crc bytes required. For example, when using
2696fd7ce5SGreg Kroah-Hartman * CCITT CRC32, 4 crc bytes are required, so the maximum size frame
2796fd7ce5SGreg Kroah-Hartman * the application may transmit is limited to 65531 bytes. For CCITT
2896fd7ce5SGreg Kroah-Hartman * CRC16, the maximum application frame size would be 65533.
2996fd7ce5SGreg Kroah-Hartman *
3096fd7ce5SGreg Kroah-Hartman *
3196fd7ce5SGreg Kroah-Hartman * 2. receive callbacks from the device driver represents
3296fd7ce5SGreg Kroah-Hartman * one received frame. The device driver should bypass
3396fd7ce5SGreg Kroah-Hartman * the tty flip buffer and call the line discipline receive
3496fd7ce5SGreg Kroah-Hartman * callback directly to avoid fragmenting or concatenating
3596fd7ce5SGreg Kroah-Hartman * multiple frames into a single receive callback.
3696fd7ce5SGreg Kroah-Hartman *
3796fd7ce5SGreg Kroah-Hartman * The HDLC line discipline queues the receive frames in separate
3896fd7ce5SGreg Kroah-Hartman * buffers so complete receive frames can be returned by the
3996fd7ce5SGreg Kroah-Hartman * tty read calls.
4096fd7ce5SGreg Kroah-Hartman *
4196fd7ce5SGreg Kroah-Hartman * 3. tty read calls returns an entire frame of data or nothing.
4296fd7ce5SGreg Kroah-Hartman *
4396fd7ce5SGreg Kroah-Hartman * 4. all send and receive data is considered raw. No processing
4496fd7ce5SGreg Kroah-Hartman * or translation is performed by the line discipline, regardless
4596fd7ce5SGreg Kroah-Hartman * of the tty flags
4696fd7ce5SGreg Kroah-Hartman *
4796fd7ce5SGreg Kroah-Hartman * 5. When line discipline is queried for the amount of receive
4896fd7ce5SGreg Kroah-Hartman * data available (FIOC), 0 is returned if no data available,
4996fd7ce5SGreg Kroah-Hartman * otherwise the count of the next available frame is returned.
5096fd7ce5SGreg Kroah-Hartman * (instead of the sum of all received frame counts).
5196fd7ce5SGreg Kroah-Hartman *
5296fd7ce5SGreg Kroah-Hartman * These conventions allow the standard tty programming interface
5396fd7ce5SGreg Kroah-Hartman * to be used for synchronous HDLC applications when used with
5496fd7ce5SGreg Kroah-Hartman * this line discipline (or another line discipline that is frame
5596fd7ce5SGreg Kroah-Hartman * oriented such as N_PPP).
5696fd7ce5SGreg Kroah-Hartman *
5796fd7ce5SGreg Kroah-Hartman * The SyncLink driver (synclink.c) implements both asynchronous
5896fd7ce5SGreg Kroah-Hartman * (using standard line discipline N_TTY) and synchronous HDLC
5996fd7ce5SGreg Kroah-Hartman * (using N_HDLC) communications, with the latter using the above
6096fd7ce5SGreg Kroah-Hartman * conventions.
6196fd7ce5SGreg Kroah-Hartman *
6296fd7ce5SGreg Kroah-Hartman * This implementation is very basic and does not maintain
6396fd7ce5SGreg Kroah-Hartman * any statistics. The main point is to enforce the raw data
6496fd7ce5SGreg Kroah-Hartman * and frame orientation of HDLC communications.
6596fd7ce5SGreg Kroah-Hartman *
6696fd7ce5SGreg Kroah-Hartman * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
6796fd7ce5SGreg Kroah-Hartman * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
6896fd7ce5SGreg Kroah-Hartman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
6996fd7ce5SGreg Kroah-Hartman * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
7096fd7ce5SGreg Kroah-Hartman * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
7196fd7ce5SGreg Kroah-Hartman * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
7296fd7ce5SGreg Kroah-Hartman * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
7396fd7ce5SGreg Kroah-Hartman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
7496fd7ce5SGreg Kroah-Hartman * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
7596fd7ce5SGreg Kroah-Hartman * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
7696fd7ce5SGreg Kroah-Hartman * OF THE POSSIBILITY OF SUCH DAMAGE.
7796fd7ce5SGreg Kroah-Hartman */
7896fd7ce5SGreg Kroah-Hartman
7996fd7ce5SGreg Kroah-Hartman #include <linux/module.h>
8096fd7ce5SGreg Kroah-Hartman #include <linux/init.h>
8196fd7ce5SGreg Kroah-Hartman #include <linux/kernel.h>
8296fd7ce5SGreg Kroah-Hartman #include <linux/sched.h>
8396fd7ce5SGreg Kroah-Hartman #include <linux/types.h>
8496fd7ce5SGreg Kroah-Hartman #include <linux/fcntl.h>
8596fd7ce5SGreg Kroah-Hartman #include <linux/interrupt.h>
8696fd7ce5SGreg Kroah-Hartman #include <linux/ptrace.h>
8796fd7ce5SGreg Kroah-Hartman
8896fd7ce5SGreg Kroah-Hartman #include <linux/poll.h>
8996fd7ce5SGreg Kroah-Hartman #include <linux/in.h>
9096fd7ce5SGreg Kroah-Hartman #include <linux/ioctl.h>
9196fd7ce5SGreg Kroah-Hartman #include <linux/slab.h>
9296fd7ce5SGreg Kroah-Hartman #include <linux/tty.h>
9396fd7ce5SGreg Kroah-Hartman #include <linux/errno.h>
9496fd7ce5SGreg Kroah-Hartman #include <linux/string.h> /* used in new tty drivers */
9596fd7ce5SGreg Kroah-Hartman #include <linux/signal.h> /* used in new tty drivers */
9696fd7ce5SGreg Kroah-Hartman #include <linux/if.h>
9796fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h>
9896fd7ce5SGreg Kroah-Hartman
997c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
1005ffa6e34SGreg Kroah-Hartman #include "tty.h"
10196fd7ce5SGreg Kroah-Hartman
10296fd7ce5SGreg Kroah-Hartman /*
10396fd7ce5SGreg Kroah-Hartman * Buffers for individual HDLC frames
10496fd7ce5SGreg Kroah-Hartman */
10596fd7ce5SGreg Kroah-Hartman #define MAX_HDLC_FRAME_SIZE 65535
10696fd7ce5SGreg Kroah-Hartman #define DEFAULT_RX_BUF_COUNT 10
10796fd7ce5SGreg Kroah-Hartman #define MAX_RX_BUF_COUNT 60
10896fd7ce5SGreg Kroah-Hartman #define DEFAULT_TX_BUF_COUNT 3
10996fd7ce5SGreg Kroah-Hartman
11096fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf {
11182f2341cSAlexander Popov struct list_head list_item;
11296fd7ce5SGreg Kroah-Hartman int count;
11385f4c951SGustavo A. R. Silva char buf[];
11496fd7ce5SGreg Kroah-Hartman };
11596fd7ce5SGreg Kroah-Hartman
11696fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf_list {
11782f2341cSAlexander Popov struct list_head list;
11896fd7ce5SGreg Kroah-Hartman int count;
11996fd7ce5SGreg Kroah-Hartman spinlock_t spinlock;
12096fd7ce5SGreg Kroah-Hartman };
12196fd7ce5SGreg Kroah-Hartman
12296fd7ce5SGreg Kroah-Hartman /**
12396fd7ce5SGreg Kroah-Hartman * struct n_hdlc - per device instance data structure
124724ac070SJiri Slaby * @tbusy: reentrancy flag for tx wakeup code
125724ac070SJiri Slaby * @woke_up: tx wakeup needs to be run again as it was called while @tbusy
126724ac070SJiri Slaby * @tx_buf_list: list of pending transmit frame buffers
127724ac070SJiri Slaby * @rx_buf_list: list of received frame buffers
128724ac070SJiri Slaby * @tx_free_buf_list: list unused transmit frame buffers
129724ac070SJiri Slaby * @rx_free_buf_list: list unused received frame buffers
13096fd7ce5SGreg Kroah-Hartman */
13196fd7ce5SGreg Kroah-Hartman struct n_hdlc {
1320f238298SJiri Slaby bool tbusy;
1330f238298SJiri Slaby bool woke_up;
13496fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf_list tx_buf_list;
13596fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf_list rx_buf_list;
13696fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf_list tx_free_buf_list;
13796fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf_list rx_free_buf_list;
1381ee33b1cSTetsuo Handa struct work_struct write_work;
1391ee33b1cSTetsuo Handa struct tty_struct *tty_for_write_work;
14096fd7ce5SGreg Kroah-Hartman };
14196fd7ce5SGreg Kroah-Hartman
14296fd7ce5SGreg Kroah-Hartman /*
14396fd7ce5SGreg Kroah-Hartman * HDLC buffer list manipulation functions
14496fd7ce5SGreg Kroah-Hartman */
14582f2341cSAlexander Popov static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
14682f2341cSAlexander Popov struct n_hdlc_buf *buf);
14796fd7ce5SGreg Kroah-Hartman static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
14896fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *buf);
14996fd7ce5SGreg Kroah-Hartman static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list);
15096fd7ce5SGreg Kroah-Hartman
15196fd7ce5SGreg Kroah-Hartman /* Local functions */
15296fd7ce5SGreg Kroah-Hartman
15396fd7ce5SGreg Kroah-Hartman static struct n_hdlc *n_hdlc_alloc(void);
1541ee33b1cSTetsuo Handa static void n_hdlc_tty_write_work(struct work_struct *work);
15596fd7ce5SGreg Kroah-Hartman
15696fd7ce5SGreg Kroah-Hartman /* max frame size for memory allocations */
15796fd7ce5SGreg Kroah-Hartman static int maxframe = 4096;
15896fd7ce5SGreg Kroah-Hartman
flush_rx_queue(struct tty_struct * tty)15996fd7ce5SGreg Kroah-Hartman static void flush_rx_queue(struct tty_struct *tty)
16096fd7ce5SGreg Kroah-Hartman {
16175011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
16296fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *buf;
16396fd7ce5SGreg Kroah-Hartman
16496fd7ce5SGreg Kroah-Hartman while ((buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list)))
16596fd7ce5SGreg Kroah-Hartman n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, buf);
16696fd7ce5SGreg Kroah-Hartman }
16796fd7ce5SGreg Kroah-Hartman
flush_tx_queue(struct tty_struct * tty)16896fd7ce5SGreg Kroah-Hartman static void flush_tx_queue(struct tty_struct *tty)
16996fd7ce5SGreg Kroah-Hartman {
17075011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
17196fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *buf;
17296fd7ce5SGreg Kroah-Hartman
17396fd7ce5SGreg Kroah-Hartman while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list)))
17496fd7ce5SGreg Kroah-Hartman n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf);
17596fd7ce5SGreg Kroah-Hartman }
17696fd7ce5SGreg Kroah-Hartman
n_hdlc_free_buf_list(struct n_hdlc_buf_list * list)17730fafd92SJiri Slaby static void n_hdlc_free_buf_list(struct n_hdlc_buf_list *list)
17830fafd92SJiri Slaby {
17930fafd92SJiri Slaby struct n_hdlc_buf *buf;
18030fafd92SJiri Slaby
18130fafd92SJiri Slaby do {
18230fafd92SJiri Slaby buf = n_hdlc_buf_get(list);
18330fafd92SJiri Slaby kfree(buf);
18430fafd92SJiri Slaby } while (buf);
18530fafd92SJiri Slaby }
18630fafd92SJiri Slaby
18796fd7ce5SGreg Kroah-Hartman /**
18896fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_close - line discipline close
189724ac070SJiri Slaby * @tty: pointer to tty info structure
19096fd7ce5SGreg Kroah-Hartman *
19196fd7ce5SGreg Kroah-Hartman * Called when the line discipline is changed to something
19296fd7ce5SGreg Kroah-Hartman * else, the tty is closed, or the tty detects a hangup.
19396fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_close(struct tty_struct * tty)19496fd7ce5SGreg Kroah-Hartman static void n_hdlc_tty_close(struct tty_struct *tty)
19596fd7ce5SGreg Kroah-Hartman {
19675011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
19796fd7ce5SGreg Kroah-Hartman
19896fd7ce5SGreg Kroah-Hartman #if defined(TTY_NO_WRITE_SPLIT)
19996fd7ce5SGreg Kroah-Hartman clear_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
20096fd7ce5SGreg Kroah-Hartman #endif
20196fd7ce5SGreg Kroah-Hartman tty->disc_data = NULL;
20243e784ecSJiri Slaby
20343e784ecSJiri Slaby /* Ensure that the n_hdlcd process is not hanging on select()/poll() */
20443e784ecSJiri Slaby wake_up_interruptible(&tty->read_wait);
20543e784ecSJiri Slaby wake_up_interruptible(&tty->write_wait);
20643e784ecSJiri Slaby
2071ee33b1cSTetsuo Handa cancel_work_sync(&n_hdlc->write_work);
2081ee33b1cSTetsuo Handa
20943e784ecSJiri Slaby n_hdlc_free_buf_list(&n_hdlc->rx_free_buf_list);
21043e784ecSJiri Slaby n_hdlc_free_buf_list(&n_hdlc->tx_free_buf_list);
21143e784ecSJiri Slaby n_hdlc_free_buf_list(&n_hdlc->rx_buf_list);
21243e784ecSJiri Slaby n_hdlc_free_buf_list(&n_hdlc->tx_buf_list);
21343e784ecSJiri Slaby kfree(n_hdlc);
21496fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_tty_close() */
21596fd7ce5SGreg Kroah-Hartman
21696fd7ce5SGreg Kroah-Hartman /**
21796fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_open - called when line discipline changed to n_hdlc
218724ac070SJiri Slaby * @tty: pointer to tty info structure
21996fd7ce5SGreg Kroah-Hartman *
22096fd7ce5SGreg Kroah-Hartman * Returns 0 if success, otherwise error code
22196fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_open(struct tty_struct * tty)22296fd7ce5SGreg Kroah-Hartman static int n_hdlc_tty_open(struct tty_struct *tty)
22396fd7ce5SGreg Kroah-Hartman {
22475011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
22596fd7ce5SGreg Kroah-Hartman
226b18d1c2eSJiri Slaby pr_debug("%s() called (device=%s)\n", __func__, tty->name);
22796fd7ce5SGreg Kroah-Hartman
22896fd7ce5SGreg Kroah-Hartman /* There should not be an existing table for this slot. */
22996fd7ce5SGreg Kroah-Hartman if (n_hdlc) {
230d86b05cbSJiri Slaby pr_err("%s: tty already associated!\n", __func__);
23196fd7ce5SGreg Kroah-Hartman return -EEXIST;
23296fd7ce5SGreg Kroah-Hartman }
23396fd7ce5SGreg Kroah-Hartman
23496fd7ce5SGreg Kroah-Hartman n_hdlc = n_hdlc_alloc();
23596fd7ce5SGreg Kroah-Hartman if (!n_hdlc) {
236d86b05cbSJiri Slaby pr_err("%s: n_hdlc_alloc failed\n", __func__);
23796fd7ce5SGreg Kroah-Hartman return -ENFILE;
23896fd7ce5SGreg Kroah-Hartman }
23996fd7ce5SGreg Kroah-Hartman
2401ee33b1cSTetsuo Handa INIT_WORK(&n_hdlc->write_work, n_hdlc_tty_write_work);
2411ee33b1cSTetsuo Handa n_hdlc->tty_for_write_work = tty;
24296fd7ce5SGreg Kroah-Hartman tty->disc_data = n_hdlc;
24396fd7ce5SGreg Kroah-Hartman tty->receive_room = 65536;
24496fd7ce5SGreg Kroah-Hartman
24596fd7ce5SGreg Kroah-Hartman /* change tty_io write() to not split large writes into 8K chunks */
24696fd7ce5SGreg Kroah-Hartman set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
24796fd7ce5SGreg Kroah-Hartman
24896fd7ce5SGreg Kroah-Hartman /* flush receive data from driver */
24996fd7ce5SGreg Kroah-Hartman tty_driver_flush_buffer(tty);
25096fd7ce5SGreg Kroah-Hartman
25196fd7ce5SGreg Kroah-Hartman return 0;
25296fd7ce5SGreg Kroah-Hartman
25396fd7ce5SGreg Kroah-Hartman } /* end of n_tty_hdlc_open() */
25496fd7ce5SGreg Kroah-Hartman
25596fd7ce5SGreg Kroah-Hartman /**
25696fd7ce5SGreg Kroah-Hartman * n_hdlc_send_frames - send frames on pending send buffer list
257724ac070SJiri Slaby * @n_hdlc: pointer to ldisc instance data
258724ac070SJiri Slaby * @tty: pointer to tty instance data
25996fd7ce5SGreg Kroah-Hartman *
26096fd7ce5SGreg Kroah-Hartman * Send frames on pending send buffer list until the driver does not accept a
26196fd7ce5SGreg Kroah-Hartman * frame (busy) this function is called after adding a frame to the send buffer
26296fd7ce5SGreg Kroah-Hartman * list and by the tty wakeup callback.
26396fd7ce5SGreg Kroah-Hartman */
n_hdlc_send_frames(struct n_hdlc * n_hdlc,struct tty_struct * tty)26496fd7ce5SGreg Kroah-Hartman static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
26596fd7ce5SGreg Kroah-Hartman {
26696fd7ce5SGreg Kroah-Hartman register int actual;
26796fd7ce5SGreg Kroah-Hartman unsigned long flags;
26896fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *tbuf;
26996fd7ce5SGreg Kroah-Hartman
27096fd7ce5SGreg Kroah-Hartman check_again:
27196fd7ce5SGreg Kroah-Hartman
27296fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
27396fd7ce5SGreg Kroah-Hartman if (n_hdlc->tbusy) {
2740f238298SJiri Slaby n_hdlc->woke_up = true;
27596fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
27696fd7ce5SGreg Kroah-Hartman return;
27796fd7ce5SGreg Kroah-Hartman }
2780f238298SJiri Slaby n_hdlc->tbusy = true;
2790f238298SJiri Slaby n_hdlc->woke_up = false;
28096fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
28196fd7ce5SGreg Kroah-Hartman
28296fd7ce5SGreg Kroah-Hartman tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
28396fd7ce5SGreg Kroah-Hartman while (tbuf) {
284b18d1c2eSJiri Slaby pr_debug("sending frame %p, count=%d\n", tbuf, tbuf->count);
28596fd7ce5SGreg Kroah-Hartman
28696fd7ce5SGreg Kroah-Hartman /* Send the next block of data to device */
2877962fce9SIlya Zykov set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
28896fd7ce5SGreg Kroah-Hartman actual = tty->ops->write(tty, tbuf->buf, tbuf->count);
28996fd7ce5SGreg Kroah-Hartman
29096fd7ce5SGreg Kroah-Hartman /* rollback was possible and has been done */
29196fd7ce5SGreg Kroah-Hartman if (actual == -ERESTARTSYS) {
29282f2341cSAlexander Popov n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
29396fd7ce5SGreg Kroah-Hartman break;
29496fd7ce5SGreg Kroah-Hartman }
29596fd7ce5SGreg Kroah-Hartman /* if transmit error, throw frame away by */
29696fd7ce5SGreg Kroah-Hartman /* pretending it was accepted by driver */
29796fd7ce5SGreg Kroah-Hartman if (actual < 0)
29896fd7ce5SGreg Kroah-Hartman actual = tbuf->count;
29996fd7ce5SGreg Kroah-Hartman
30096fd7ce5SGreg Kroah-Hartman if (actual == tbuf->count) {
301b18d1c2eSJiri Slaby pr_debug("frame %p completed\n", tbuf);
30296fd7ce5SGreg Kroah-Hartman
30396fd7ce5SGreg Kroah-Hartman /* free current transmit buffer */
30496fd7ce5SGreg Kroah-Hartman n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf);
30596fd7ce5SGreg Kroah-Hartman
30696fd7ce5SGreg Kroah-Hartman /* wait up sleeping writers */
30796fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&tty->write_wait);
30896fd7ce5SGreg Kroah-Hartman
30996fd7ce5SGreg Kroah-Hartman /* get next pending transmit buffer */
31096fd7ce5SGreg Kroah-Hartman tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
31196fd7ce5SGreg Kroah-Hartman } else {
312b18d1c2eSJiri Slaby pr_debug("frame %p pending\n", tbuf);
31396fd7ce5SGreg Kroah-Hartman
31482f2341cSAlexander Popov /*
31582f2341cSAlexander Popov * the buffer was not accepted by driver,
31682f2341cSAlexander Popov * return it back into tx queue
31782f2341cSAlexander Popov */
31882f2341cSAlexander Popov n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
31996fd7ce5SGreg Kroah-Hartman break;
32096fd7ce5SGreg Kroah-Hartman }
32196fd7ce5SGreg Kroah-Hartman }
32296fd7ce5SGreg Kroah-Hartman
32396fd7ce5SGreg Kroah-Hartman if (!tbuf)
3247962fce9SIlya Zykov clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
32596fd7ce5SGreg Kroah-Hartman
32696fd7ce5SGreg Kroah-Hartman /* Clear the re-entry flag */
32796fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
3280f238298SJiri Slaby n_hdlc->tbusy = false;
32996fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
33096fd7ce5SGreg Kroah-Hartman
33196fd7ce5SGreg Kroah-Hartman if (n_hdlc->woke_up)
33296fd7ce5SGreg Kroah-Hartman goto check_again;
33396fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_send_frames() */
33496fd7ce5SGreg Kroah-Hartman
33596fd7ce5SGreg Kroah-Hartman /**
3361ee33b1cSTetsuo Handa * n_hdlc_tty_write_work - Asynchronous callback for transmit wakeup
3371ee33b1cSTetsuo Handa * @work: pointer to work_struct
3381ee33b1cSTetsuo Handa *
3391ee33b1cSTetsuo Handa * Called when low level device driver can accept more send data.
3401ee33b1cSTetsuo Handa */
n_hdlc_tty_write_work(struct work_struct * work)3411ee33b1cSTetsuo Handa static void n_hdlc_tty_write_work(struct work_struct *work)
3421ee33b1cSTetsuo Handa {
3431ee33b1cSTetsuo Handa struct n_hdlc *n_hdlc = container_of(work, struct n_hdlc, write_work);
3441ee33b1cSTetsuo Handa struct tty_struct *tty = n_hdlc->tty_for_write_work;
3451ee33b1cSTetsuo Handa
3461ee33b1cSTetsuo Handa n_hdlc_send_frames(n_hdlc, tty);
3471ee33b1cSTetsuo Handa } /* end of n_hdlc_tty_write_work() */
3481ee33b1cSTetsuo Handa
3491ee33b1cSTetsuo Handa /**
35096fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_wakeup - Callback for transmit wakeup
351724ac070SJiri Slaby * @tty: pointer to associated tty instance data
35296fd7ce5SGreg Kroah-Hartman *
35396fd7ce5SGreg Kroah-Hartman * Called when low level device driver can accept more send data.
35496fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_wakeup(struct tty_struct * tty)35596fd7ce5SGreg Kroah-Hartman static void n_hdlc_tty_wakeup(struct tty_struct *tty)
35696fd7ce5SGreg Kroah-Hartman {
35775011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
35896fd7ce5SGreg Kroah-Hartman
3591ee33b1cSTetsuo Handa schedule_work(&n_hdlc->write_work);
36096fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_tty_wakeup() */
36196fd7ce5SGreg Kroah-Hartman
36296fd7ce5SGreg Kroah-Hartman /**
36396fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_receive - Called by tty driver when receive data is available
364724ac070SJiri Slaby * @tty: pointer to tty instance data
365724ac070SJiri Slaby * @data: pointer to received data
366724ac070SJiri Slaby * @flags: pointer to flags for data
367724ac070SJiri Slaby * @count: count of received data in bytes
36896fd7ce5SGreg Kroah-Hartman *
36996fd7ce5SGreg Kroah-Hartman * Called by tty low level driver when receive data is available. Data is
37096fd7ce5SGreg Kroah-Hartman * interpreted as one HDLC frame.
37196fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_receive(struct tty_struct * tty,const u8 * data,const u8 * flags,size_t count)372a8d9cd23SJiri Slaby (SUSE) static void n_hdlc_tty_receive(struct tty_struct *tty, const u8 *data,
373892bc209SJiri Slaby (SUSE) const u8 *flags, size_t count)
37496fd7ce5SGreg Kroah-Hartman {
37575011682SJiri Slaby register struct n_hdlc *n_hdlc = tty->disc_data;
37696fd7ce5SGreg Kroah-Hartman register struct n_hdlc_buf *buf;
37796fd7ce5SGreg Kroah-Hartman
378e8161447SJiri Slaby (SUSE) pr_debug("%s() called count=%zu\n", __func__, count);
37996fd7ce5SGreg Kroah-Hartman
38096fd7ce5SGreg Kroah-Hartman if (count > maxframe) {
381b18d1c2eSJiri Slaby pr_debug("rx count>maxframesize, data discarded\n");
38255db4c64SLinus Torvalds return;
38396fd7ce5SGreg Kroah-Hartman }
38496fd7ce5SGreg Kroah-Hartman
38596fd7ce5SGreg Kroah-Hartman /* get a free HDLC buffer */
38696fd7ce5SGreg Kroah-Hartman buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list);
38796fd7ce5SGreg Kroah-Hartman if (!buf) {
38880967ff2SJiri Slaby /*
38980967ff2SJiri Slaby * no buffers in free list, attempt to allocate another rx
39080967ff2SJiri Slaby * buffer unless the maximum count has been reached
39180967ff2SJiri Slaby */
39296fd7ce5SGreg Kroah-Hartman if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT)
39385f4c951SGustavo A. R. Silva buf = kmalloc(struct_size(buf, buf, maxframe),
39485f4c951SGustavo A. R. Silva GFP_ATOMIC);
39596fd7ce5SGreg Kroah-Hartman }
39696fd7ce5SGreg Kroah-Hartman
39796fd7ce5SGreg Kroah-Hartman if (!buf) {
398b18d1c2eSJiri Slaby pr_debug("no more rx buffers, data discarded\n");
39955db4c64SLinus Torvalds return;
40096fd7ce5SGreg Kroah-Hartman }
40196fd7ce5SGreg Kroah-Hartman
40296fd7ce5SGreg Kroah-Hartman /* copy received data to HDLC buffer */
40396fd7ce5SGreg Kroah-Hartman memcpy(buf->buf, data, count);
40496fd7ce5SGreg Kroah-Hartman buf->count = count;
40596fd7ce5SGreg Kroah-Hartman
40696fd7ce5SGreg Kroah-Hartman /* add HDLC buffer to list of received frames */
40796fd7ce5SGreg Kroah-Hartman n_hdlc_buf_put(&n_hdlc->rx_buf_list, buf);
40896fd7ce5SGreg Kroah-Hartman
40996fd7ce5SGreg Kroah-Hartman /* wake up any blocked reads and perform async signalling */
41096fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&tty->read_wait);
411df6de639SJiri Slaby if (tty->fasync != NULL)
412df6de639SJiri Slaby kill_fasync(&tty->fasync, SIGIO, POLL_IN);
41396fd7ce5SGreg Kroah-Hartman
41496fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_tty_receive() */
41596fd7ce5SGreg Kroah-Hartman
41696fd7ce5SGreg Kroah-Hartman /**
41796fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_read - Called to retrieve one frame of data (if available)
418724ac070SJiri Slaby * @tty: pointer to tty instance data
419724ac070SJiri Slaby * @file: pointer to open file object
420ef80f77bSLee Jones * @kbuf: pointer to returned data buffer
421724ac070SJiri Slaby * @nr: size of returned data buffer
422ef80f77bSLee Jones * @cookie: stored rbuf from previous run
423ef80f77bSLee Jones * @offset: offset into the data buffer
42496fd7ce5SGreg Kroah-Hartman *
42596fd7ce5SGreg Kroah-Hartman * Returns the number of bytes returned or error code.
42696fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_read(struct tty_struct * tty,struct file * file,u8 * kbuf,size_t nr,void ** cookie,unsigned long offset)42796fd7ce5SGreg Kroah-Hartman static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
428*49b8220cSJiri Slaby (SUSE) u8 *kbuf, size_t nr, void **cookie,
429*49b8220cSJiri Slaby (SUSE) unsigned long offset)
43096fd7ce5SGreg Kroah-Hartman {
43175011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
4321035b63dSPaul Fulghum int ret = 0;
43396fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *rbuf;
4341035b63dSPaul Fulghum DECLARE_WAITQUEUE(wait, current);
43596fd7ce5SGreg Kroah-Hartman
4363b830a9cSLinus Torvalds /* Is this a repeated call for an rbuf we already found earlier? */
4373b830a9cSLinus Torvalds rbuf = *cookie;
4383b830a9cSLinus Torvalds if (rbuf)
4393b830a9cSLinus Torvalds goto have_rbuf;
4403b830a9cSLinus Torvalds
4411035b63dSPaul Fulghum add_wait_queue(&tty->read_wait, &wait);
44296fd7ce5SGreg Kroah-Hartman
44396fd7ce5SGreg Kroah-Hartman for (;;) {
4440f40fbbcSBrian Bloniarz if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
4451035b63dSPaul Fulghum ret = -EIO;
4461035b63dSPaul Fulghum break;
44796fd7ce5SGreg Kroah-Hartman }
4481035b63dSPaul Fulghum if (tty_hung_up_p(file))
44996fd7ce5SGreg Kroah-Hartman break;
45096fd7ce5SGreg Kroah-Hartman
4511035b63dSPaul Fulghum set_current_state(TASK_INTERRUPTIBLE);
45296fd7ce5SGreg Kroah-Hartman
4531035b63dSPaul Fulghum rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
4543b830a9cSLinus Torvalds if (rbuf)
4551035b63dSPaul Fulghum break;
4561035b63dSPaul Fulghum
4571035b63dSPaul Fulghum /* no data */
458c96cf923SDmitry Safonov if (tty_io_nonblock(tty, file)) {
4591035b63dSPaul Fulghum ret = -EAGAIN;
4601035b63dSPaul Fulghum break;
4611035b63dSPaul Fulghum }
4621035b63dSPaul Fulghum
4631035b63dSPaul Fulghum schedule();
4641035b63dSPaul Fulghum
4651035b63dSPaul Fulghum if (signal_pending(current)) {
4661035b63dSPaul Fulghum ret = -EINTR;
4671035b63dSPaul Fulghum break;
4681035b63dSPaul Fulghum }
4691035b63dSPaul Fulghum }
4701035b63dSPaul Fulghum
4711035b63dSPaul Fulghum remove_wait_queue(&tty->read_wait, &wait);
4721035b63dSPaul Fulghum __set_current_state(TASK_RUNNING);
4731035b63dSPaul Fulghum
4743b830a9cSLinus Torvalds if (!rbuf)
4753b830a9cSLinus Torvalds return ret;
4763b830a9cSLinus Torvalds *cookie = rbuf;
4773b830a9cSLinus Torvalds
4783b830a9cSLinus Torvalds have_rbuf:
4793b830a9cSLinus Torvalds /* Have we used it up entirely? */
4803b830a9cSLinus Torvalds if (offset >= rbuf->count)
4813b830a9cSLinus Torvalds goto done_with_rbuf;
4823b830a9cSLinus Torvalds
4833b830a9cSLinus Torvalds /* More data to go, but can't copy any more? EOVERFLOW */
4843b830a9cSLinus Torvalds ret = -EOVERFLOW;
4853b830a9cSLinus Torvalds if (!nr)
4863b830a9cSLinus Torvalds goto done_with_rbuf;
4873b830a9cSLinus Torvalds
4883b830a9cSLinus Torvalds /* Copy as much data as possible */
4893b830a9cSLinus Torvalds ret = rbuf->count - offset;
4903b830a9cSLinus Torvalds if (ret > nr)
4913b830a9cSLinus Torvalds ret = nr;
4923b830a9cSLinus Torvalds memcpy(kbuf, rbuf->buf+offset, ret);
4933b830a9cSLinus Torvalds offset += ret;
4943b830a9cSLinus Torvalds
4953b830a9cSLinus Torvalds /* If we still have data left, we leave the rbuf in the cookie */
4963b830a9cSLinus Torvalds if (offset < rbuf->count)
4973b830a9cSLinus Torvalds return ret;
4983b830a9cSLinus Torvalds
4993b830a9cSLinus Torvalds done_with_rbuf:
5003b830a9cSLinus Torvalds *cookie = NULL;
5013b830a9cSLinus Torvalds
5023b830a9cSLinus Torvalds if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT)
5033b830a9cSLinus Torvalds kfree(rbuf);
5043b830a9cSLinus Torvalds else
5053b830a9cSLinus Torvalds n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf);
5063b830a9cSLinus Torvalds
50796fd7ce5SGreg Kroah-Hartman return ret;
50896fd7ce5SGreg Kroah-Hartman
50996fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_tty_read() */
51096fd7ce5SGreg Kroah-Hartman
51196fd7ce5SGreg Kroah-Hartman /**
51296fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_write - write a single frame of data to device
513724ac070SJiri Slaby * @tty: pointer to associated tty device instance data
514724ac070SJiri Slaby * @file: pointer to file object data
515724ac070SJiri Slaby * @data: pointer to transmit data (one frame)
516724ac070SJiri Slaby * @count: size of transmit frame in bytes
51796fd7ce5SGreg Kroah-Hartman *
51896fd7ce5SGreg Kroah-Hartman * Returns the number of bytes written (or error code).
51996fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_write(struct tty_struct * tty,struct file * file,const u8 * data,size_t count)52096fd7ce5SGreg Kroah-Hartman static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
521*49b8220cSJiri Slaby (SUSE) const u8 *data, size_t count)
52296fd7ce5SGreg Kroah-Hartman {
52375011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
52496fd7ce5SGreg Kroah-Hartman int error = 0;
52596fd7ce5SGreg Kroah-Hartman DECLARE_WAITQUEUE(wait, current);
52696fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *tbuf;
52796fd7ce5SGreg Kroah-Hartman
528b18d1c2eSJiri Slaby pr_debug("%s() called count=%zd\n", __func__, count);
52996fd7ce5SGreg Kroah-Hartman
53096fd7ce5SGreg Kroah-Hartman /* verify frame size */
53196fd7ce5SGreg Kroah-Hartman if (count > maxframe) {
532f3c2e277SJiri Slaby pr_debug("%s: truncating user packet from %zu to %d\n",
533f3c2e277SJiri Slaby __func__, count, maxframe);
53496fd7ce5SGreg Kroah-Hartman count = maxframe;
53596fd7ce5SGreg Kroah-Hartman }
53696fd7ce5SGreg Kroah-Hartman
53796fd7ce5SGreg Kroah-Hartman add_wait_queue(&tty->write_wait, &wait);
5381035b63dSPaul Fulghum
5391035b63dSPaul Fulghum for (;;) {
54096fd7ce5SGreg Kroah-Hartman set_current_state(TASK_INTERRUPTIBLE);
54196fd7ce5SGreg Kroah-Hartman
5421035b63dSPaul Fulghum tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list);
5431035b63dSPaul Fulghum if (tbuf)
5441035b63dSPaul Fulghum break;
5451035b63dSPaul Fulghum
546c96cf923SDmitry Safonov if (tty_io_nonblock(tty, file)) {
54796fd7ce5SGreg Kroah-Hartman error = -EAGAIN;
54896fd7ce5SGreg Kroah-Hartman break;
54996fd7ce5SGreg Kroah-Hartman }
55096fd7ce5SGreg Kroah-Hartman schedule();
55196fd7ce5SGreg Kroah-Hartman
55296fd7ce5SGreg Kroah-Hartman if (signal_pending(current)) {
55396fd7ce5SGreg Kroah-Hartman error = -EINTR;
55496fd7ce5SGreg Kroah-Hartman break;
55596fd7ce5SGreg Kroah-Hartman }
55696fd7ce5SGreg Kroah-Hartman }
55796fd7ce5SGreg Kroah-Hartman
5581035b63dSPaul Fulghum __set_current_state(TASK_RUNNING);
55996fd7ce5SGreg Kroah-Hartman remove_wait_queue(&tty->write_wait, &wait);
56096fd7ce5SGreg Kroah-Hartman
56196fd7ce5SGreg Kroah-Hartman if (!error) {
56296fd7ce5SGreg Kroah-Hartman /* Retrieve the user's buffer */
56396fd7ce5SGreg Kroah-Hartman memcpy(tbuf->buf, data, count);
56496fd7ce5SGreg Kroah-Hartman
56596fd7ce5SGreg Kroah-Hartman /* Send the data */
56696fd7ce5SGreg Kroah-Hartman tbuf->count = error = count;
56796fd7ce5SGreg Kroah-Hartman n_hdlc_buf_put(&n_hdlc->tx_buf_list, tbuf);
56896fd7ce5SGreg Kroah-Hartman n_hdlc_send_frames(n_hdlc, tty);
56996fd7ce5SGreg Kroah-Hartman }
5701035b63dSPaul Fulghum
57196fd7ce5SGreg Kroah-Hartman return error;
57296fd7ce5SGreg Kroah-Hartman
57396fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_tty_write() */
57496fd7ce5SGreg Kroah-Hartman
57596fd7ce5SGreg Kroah-Hartman /**
57696fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_ioctl - process IOCTL system call for the tty device.
577724ac070SJiri Slaby * @tty: pointer to tty instance data
578724ac070SJiri Slaby * @cmd: IOCTL command code
579724ac070SJiri Slaby * @arg: argument for IOCTL call (cmd dependent)
58096fd7ce5SGreg Kroah-Hartman *
58196fd7ce5SGreg Kroah-Hartman * Returns command dependent result.
58296fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)583d78328bcSJiri Slaby static int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
584d78328bcSJiri Slaby unsigned long arg)
58596fd7ce5SGreg Kroah-Hartman {
58675011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
58796fd7ce5SGreg Kroah-Hartman int error = 0;
58896fd7ce5SGreg Kroah-Hartman int count;
58996fd7ce5SGreg Kroah-Hartman unsigned long flags;
59082f2341cSAlexander Popov struct n_hdlc_buf *buf = NULL;
59196fd7ce5SGreg Kroah-Hartman
592b18d1c2eSJiri Slaby pr_debug("%s() called %d\n", __func__, cmd);
59396fd7ce5SGreg Kroah-Hartman
59496fd7ce5SGreg Kroah-Hartman switch (cmd) {
59596fd7ce5SGreg Kroah-Hartman case FIONREAD:
59696fd7ce5SGreg Kroah-Hartman /* report count of read data available */
59796fd7ce5SGreg Kroah-Hartman /* in next available frame (if any) */
59896fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock, flags);
59982f2341cSAlexander Popov buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
60082f2341cSAlexander Popov struct n_hdlc_buf, list_item);
60182f2341cSAlexander Popov if (buf)
60282f2341cSAlexander Popov count = buf->count;
60396fd7ce5SGreg Kroah-Hartman else
60496fd7ce5SGreg Kroah-Hartman count = 0;
60596fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock, flags);
60696fd7ce5SGreg Kroah-Hartman error = put_user(count, (int __user *)arg);
60796fd7ce5SGreg Kroah-Hartman break;
60896fd7ce5SGreg Kroah-Hartman
60996fd7ce5SGreg Kroah-Hartman case TIOCOUTQ:
61096fd7ce5SGreg Kroah-Hartman /* get the pending tx byte count in the driver */
61196fd7ce5SGreg Kroah-Hartman count = tty_chars_in_buffer(tty);
61296fd7ce5SGreg Kroah-Hartman /* add size of next output frame in queue */
61396fd7ce5SGreg Kroah-Hartman spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
61482f2341cSAlexander Popov buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
61582f2341cSAlexander Popov struct n_hdlc_buf, list_item);
61682f2341cSAlexander Popov if (buf)
61782f2341cSAlexander Popov count += buf->count;
61896fd7ce5SGreg Kroah-Hartman spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
61996fd7ce5SGreg Kroah-Hartman error = put_user(count, (int __user *)arg);
62096fd7ce5SGreg Kroah-Hartman break;
62196fd7ce5SGreg Kroah-Hartman
62296fd7ce5SGreg Kroah-Hartman case TCFLSH:
62396fd7ce5SGreg Kroah-Hartman switch (arg) {
62496fd7ce5SGreg Kroah-Hartman case TCIOFLUSH:
62596fd7ce5SGreg Kroah-Hartman case TCOFLUSH:
62696fd7ce5SGreg Kroah-Hartman flush_tx_queue(tty);
62796fd7ce5SGreg Kroah-Hartman }
628df561f66SGustavo A. R. Silva fallthrough; /* to default */
62996fd7ce5SGreg Kroah-Hartman
63096fd7ce5SGreg Kroah-Hartman default:
6317c783601SJiri Slaby error = n_tty_ioctl_helper(tty, cmd, arg);
63296fd7ce5SGreg Kroah-Hartman break;
63396fd7ce5SGreg Kroah-Hartman }
63496fd7ce5SGreg Kroah-Hartman return error;
63596fd7ce5SGreg Kroah-Hartman
63696fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_tty_ioctl() */
63796fd7ce5SGreg Kroah-Hartman
63896fd7ce5SGreg Kroah-Hartman /**
63996fd7ce5SGreg Kroah-Hartman * n_hdlc_tty_poll - TTY callback for poll system call
640724ac070SJiri Slaby * @tty: pointer to tty instance data
641724ac070SJiri Slaby * @filp: pointer to open file object for device
642724ac070SJiri Slaby * @wait: wait queue for operations
64396fd7ce5SGreg Kroah-Hartman *
64496fd7ce5SGreg Kroah-Hartman * Determine which operations (read/write) will not block and return info
64596fd7ce5SGreg Kroah-Hartman * to caller.
64696fd7ce5SGreg Kroah-Hartman * Returns a bit mask containing info on which ops will not block.
64796fd7ce5SGreg Kroah-Hartman */
n_hdlc_tty_poll(struct tty_struct * tty,struct file * filp,poll_table * wait)648afc9a42bSAl Viro static __poll_t n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
64996fd7ce5SGreg Kroah-Hartman poll_table *wait)
65096fd7ce5SGreg Kroah-Hartman {
65175011682SJiri Slaby struct n_hdlc *n_hdlc = tty->disc_data;
652afc9a42bSAl Viro __poll_t mask = 0;
65396fd7ce5SGreg Kroah-Hartman
6545f289514SJiri Slaby /*
6555f289514SJiri Slaby * queue the current process into any wait queue that may awaken in the
6565f289514SJiri Slaby * future (read and write)
6575f289514SJiri Slaby */
65896fd7ce5SGreg Kroah-Hartman poll_wait(filp, &tty->read_wait, wait);
65996fd7ce5SGreg Kroah-Hartman poll_wait(filp, &tty->write_wait, wait);
66096fd7ce5SGreg Kroah-Hartman
66196fd7ce5SGreg Kroah-Hartman /* set bits for operations that won't block */
66282f2341cSAlexander Popov if (!list_empty(&n_hdlc->rx_buf_list.list))
663a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; /* readable */
6640f40fbbcSBrian Bloniarz if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
665a9a08845SLinus Torvalds mask |= EPOLLHUP;
66696fd7ce5SGreg Kroah-Hartman if (tty_hung_up_p(filp))
667a9a08845SLinus Torvalds mask |= EPOLLHUP;
66896fd7ce5SGreg Kroah-Hartman if (!tty_is_writelocked(tty) &&
66982f2341cSAlexander Popov !list_empty(&n_hdlc->tx_free_buf_list.list))
670a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM; /* writable */
6715f289514SJiri Slaby
67296fd7ce5SGreg Kroah-Hartman return mask;
67396fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_tty_poll() */
67496fd7ce5SGreg Kroah-Hartman
n_hdlc_alloc_buf(struct n_hdlc_buf_list * list,unsigned int count,const char * name)675740708abSJiri Slaby static void n_hdlc_alloc_buf(struct n_hdlc_buf_list *list, unsigned int count,
676740708abSJiri Slaby const char *name)
677740708abSJiri Slaby {
678740708abSJiri Slaby struct n_hdlc_buf *buf;
679740708abSJiri Slaby unsigned int i;
680740708abSJiri Slaby
681740708abSJiri Slaby for (i = 0; i < count; i++) {
682740708abSJiri Slaby buf = kmalloc(struct_size(buf, buf, maxframe), GFP_KERNEL);
683740708abSJiri Slaby if (!buf) {
684b18d1c2eSJiri Slaby pr_debug("%s(), kmalloc() failed for %s buffer %u\n",
685b18d1c2eSJiri Slaby __func__, name, i);
686740708abSJiri Slaby return;
687740708abSJiri Slaby }
688740708abSJiri Slaby n_hdlc_buf_put(list, buf);
689740708abSJiri Slaby }
690740708abSJiri Slaby }
691740708abSJiri Slaby
69296fd7ce5SGreg Kroah-Hartman /**
69396fd7ce5SGreg Kroah-Hartman * n_hdlc_alloc - allocate an n_hdlc instance data structure
69496fd7ce5SGreg Kroah-Hartman *
69596fd7ce5SGreg Kroah-Hartman * Returns a pointer to newly created structure if success, otherwise %NULL
69696fd7ce5SGreg Kroah-Hartman */
n_hdlc_alloc(void)69796fd7ce5SGreg Kroah-Hartman static struct n_hdlc *n_hdlc_alloc(void)
69896fd7ce5SGreg Kroah-Hartman {
6998e25f8ceSFabian Frederick struct n_hdlc *n_hdlc = kzalloc(sizeof(*n_hdlc), GFP_KERNEL);
70096fd7ce5SGreg Kroah-Hartman
70196fd7ce5SGreg Kroah-Hartman if (!n_hdlc)
70296fd7ce5SGreg Kroah-Hartman return NULL;
70396fd7ce5SGreg Kroah-Hartman
704e9b736d8SJiri Slaby spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock);
705e9b736d8SJiri Slaby spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock);
706e9b736d8SJiri Slaby spin_lock_init(&n_hdlc->rx_buf_list.spinlock);
707e9b736d8SJiri Slaby spin_lock_init(&n_hdlc->tx_buf_list.spinlock);
70896fd7ce5SGreg Kroah-Hartman
70982f2341cSAlexander Popov INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list);
71082f2341cSAlexander Popov INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list);
71182f2341cSAlexander Popov INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list);
71282f2341cSAlexander Popov INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list);
71382f2341cSAlexander Popov
714740708abSJiri Slaby n_hdlc_alloc_buf(&n_hdlc->rx_free_buf_list, DEFAULT_RX_BUF_COUNT, "rx");
715740708abSJiri Slaby n_hdlc_alloc_buf(&n_hdlc->tx_free_buf_list, DEFAULT_TX_BUF_COUNT, "tx");
71696fd7ce5SGreg Kroah-Hartman
71796fd7ce5SGreg Kroah-Hartman return n_hdlc;
71896fd7ce5SGreg Kroah-Hartman
71996fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_alloc() */
72096fd7ce5SGreg Kroah-Hartman
72196fd7ce5SGreg Kroah-Hartman /**
72282f2341cSAlexander Popov * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list
723724ac070SJiri Slaby * @buf_list: pointer to the buffer list
724724ac070SJiri Slaby * @buf: pointer to the buffer
72596fd7ce5SGreg Kroah-Hartman */
n_hdlc_buf_return(struct n_hdlc_buf_list * buf_list,struct n_hdlc_buf * buf)72682f2341cSAlexander Popov static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
72796fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *buf)
72896fd7ce5SGreg Kroah-Hartman {
72996fd7ce5SGreg Kroah-Hartman unsigned long flags;
73096fd7ce5SGreg Kroah-Hartman
73182f2341cSAlexander Popov spin_lock_irqsave(&buf_list->spinlock, flags);
73296fd7ce5SGreg Kroah-Hartman
73382f2341cSAlexander Popov list_add(&buf->list_item, &buf_list->list);
73482f2341cSAlexander Popov buf_list->count++;
73596fd7ce5SGreg Kroah-Hartman
73682f2341cSAlexander Popov spin_unlock_irqrestore(&buf_list->spinlock, flags);
73782f2341cSAlexander Popov }
73882f2341cSAlexander Popov
73982f2341cSAlexander Popov /**
74082f2341cSAlexander Popov * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list
741724ac070SJiri Slaby * @buf_list: pointer to buffer list
742724ac070SJiri Slaby * @buf: pointer to buffer
74382f2341cSAlexander Popov */
n_hdlc_buf_put(struct n_hdlc_buf_list * buf_list,struct n_hdlc_buf * buf)74482f2341cSAlexander Popov static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
74582f2341cSAlexander Popov struct n_hdlc_buf *buf)
74682f2341cSAlexander Popov {
74782f2341cSAlexander Popov unsigned long flags;
74882f2341cSAlexander Popov
74982f2341cSAlexander Popov spin_lock_irqsave(&buf_list->spinlock, flags);
75082f2341cSAlexander Popov
75182f2341cSAlexander Popov list_add_tail(&buf->list_item, &buf_list->list);
75282f2341cSAlexander Popov buf_list->count++;
75382f2341cSAlexander Popov
75482f2341cSAlexander Popov spin_unlock_irqrestore(&buf_list->spinlock, flags);
75596fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_buf_put() */
75696fd7ce5SGreg Kroah-Hartman
75796fd7ce5SGreg Kroah-Hartman /**
75896fd7ce5SGreg Kroah-Hartman * n_hdlc_buf_get - remove and return an HDLC buffer from list
759724ac070SJiri Slaby * @buf_list: pointer to HDLC buffer list
76096fd7ce5SGreg Kroah-Hartman *
76196fd7ce5SGreg Kroah-Hartman * Remove and return an HDLC buffer from the head of the specified HDLC buffer
76296fd7ce5SGreg Kroah-Hartman * list.
76396fd7ce5SGreg Kroah-Hartman * Returns a pointer to HDLC buffer if available, otherwise %NULL.
76496fd7ce5SGreg Kroah-Hartman */
n_hdlc_buf_get(struct n_hdlc_buf_list * buf_list)76582f2341cSAlexander Popov static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
76696fd7ce5SGreg Kroah-Hartman {
76796fd7ce5SGreg Kroah-Hartman unsigned long flags;
76896fd7ce5SGreg Kroah-Hartman struct n_hdlc_buf *buf;
76996fd7ce5SGreg Kroah-Hartman
77082f2341cSAlexander Popov spin_lock_irqsave(&buf_list->spinlock, flags);
77182f2341cSAlexander Popov
77282f2341cSAlexander Popov buf = list_first_entry_or_null(&buf_list->list,
77382f2341cSAlexander Popov struct n_hdlc_buf, list_item);
77496fd7ce5SGreg Kroah-Hartman if (buf) {
77582f2341cSAlexander Popov list_del(&buf->list_item);
77682f2341cSAlexander Popov buf_list->count--;
77796fd7ce5SGreg Kroah-Hartman }
77896fd7ce5SGreg Kroah-Hartman
77982f2341cSAlexander Popov spin_unlock_irqrestore(&buf_list->spinlock, flags);
78096fd7ce5SGreg Kroah-Hartman return buf;
78196fd7ce5SGreg Kroah-Hartman } /* end of n_hdlc_buf_get() */
78296fd7ce5SGreg Kroah-Hartman
783edee649fSJiri Slaby static struct tty_ldisc_ops n_hdlc_ldisc = {
784edee649fSJiri Slaby .owner = THIS_MODULE,
785fbadf70aSJiri Slaby .num = N_HDLC,
786edee649fSJiri Slaby .name = "hdlc",
787edee649fSJiri Slaby .open = n_hdlc_tty_open,
788edee649fSJiri Slaby .close = n_hdlc_tty_close,
789edee649fSJiri Slaby .read = n_hdlc_tty_read,
790edee649fSJiri Slaby .write = n_hdlc_tty_write,
791edee649fSJiri Slaby .ioctl = n_hdlc_tty_ioctl,
792edee649fSJiri Slaby .poll = n_hdlc_tty_poll,
793edee649fSJiri Slaby .receive_buf = n_hdlc_tty_receive,
794edee649fSJiri Slaby .write_wakeup = n_hdlc_tty_wakeup,
795edee649fSJiri Slaby .flush_buffer = flush_rx_queue,
796edee649fSJiri Slaby };
797edee649fSJiri Slaby
n_hdlc_init(void)79896fd7ce5SGreg Kroah-Hartman static int __init n_hdlc_init(void)
79996fd7ce5SGreg Kroah-Hartman {
80096fd7ce5SGreg Kroah-Hartman int status;
80196fd7ce5SGreg Kroah-Hartman
80296fd7ce5SGreg Kroah-Hartman /* range check maxframe arg */
803c549725fSJiri Slaby maxframe = clamp(maxframe, 4096, MAX_HDLC_FRAME_SIZE);
80496fd7ce5SGreg Kroah-Hartman
805fbadf70aSJiri Slaby status = tty_register_ldisc(&n_hdlc_ldisc);
80696fd7ce5SGreg Kroah-Hartman if (!status)
807cda3756cSJiri Slaby pr_info("N_HDLC line discipline registered with maxframe=%d\n",
808cda3756cSJiri Slaby maxframe);
80996fd7ce5SGreg Kroah-Hartman else
810cda3756cSJiri Slaby pr_err("N_HDLC: error registering line discipline: %d\n",
811cda3756cSJiri Slaby status);
81296fd7ce5SGreg Kroah-Hartman
81396fd7ce5SGreg Kroah-Hartman return status;
81496fd7ce5SGreg Kroah-Hartman
81596fd7ce5SGreg Kroah-Hartman } /* end of init_module() */
81696fd7ce5SGreg Kroah-Hartman
n_hdlc_exit(void)81796fd7ce5SGreg Kroah-Hartman static void __exit n_hdlc_exit(void)
81896fd7ce5SGreg Kroah-Hartman {
819357a6a87SJiri Slaby tty_unregister_ldisc(&n_hdlc_ldisc);
82096fd7ce5SGreg Kroah-Hartman }
82196fd7ce5SGreg Kroah-Hartman
82296fd7ce5SGreg Kroah-Hartman module_init(n_hdlc_init);
82396fd7ce5SGreg Kroah-Hartman module_exit(n_hdlc_exit);
82496fd7ce5SGreg Kroah-Hartman
82596fd7ce5SGreg Kroah-Hartman MODULE_LICENSE("GPL");
82696fd7ce5SGreg Kroah-Hartman MODULE_AUTHOR("Paul Fulghum paulkf@microgate.com");
82796fd7ce5SGreg Kroah-Hartman module_param(maxframe, int, 0);
82896fd7ce5SGreg Kroah-Hartman MODULE_ALIAS_LDISC(N_HDLC);
829