cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tty.c (14096B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * IPWireless 3G PCMCIA Network Driver
      4 *
      5 * Original code
      6 *   by Stephen Blackheath <stephen@blacksapphire.com>,
      7 *      Ben Martel <benm@symmetric.co.nz>
      8 *
      9 * Copyrighted as follows:
     10 *   Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
     11 *
     12 * Various driver changes and rewrites, port to new kernels
     13 *   Copyright (C) 2006-2007 Jiri Kosina
     14 *
     15 * Misc code cleanups and updates
     16 *   Copyright (C) 2007 David Sterba
     17 */
     18
     19#include <linux/kernel.h>
     20#include <linux/module.h>
     21#include <linux/mutex.h>
     22#include <linux/ppp_defs.h>
     23#include <linux/if.h>
     24#include <linux/ppp-ioctl.h>
     25#include <linux/sched.h>
     26#include <linux/serial.h>
     27#include <linux/slab.h>
     28#include <linux/tty.h>
     29#include <linux/tty_driver.h>
     30#include <linux/tty_flip.h>
     31#include <linux/uaccess.h>
     32
     33#include "tty.h"
     34#include "network.h"
     35#include "hardware.h"
     36#include "main.h"
     37
     38#define IPWIRELESS_PCMCIA_START 	(0)
     39#define IPWIRELESS_PCMCIA_MINORS	(24)
     40#define IPWIRELESS_PCMCIA_MINOR_RANGE	(8)
     41
     42#define TTYTYPE_MODEM    (0)
     43#define TTYTYPE_MONITOR  (1)
     44#define TTYTYPE_RAS_RAW  (2)
     45
     46struct ipw_tty {
     47	struct tty_port port;
     48	int index;
     49	struct ipw_hardware *hardware;
     50	unsigned int channel_idx;
     51	unsigned int secondary_channel_idx;
     52	int tty_type;
     53	struct ipw_network *network;
     54	unsigned int control_lines;
     55	struct mutex ipw_tty_mutex;
     56	int tx_bytes_queued;
     57};
     58
     59static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS];
     60
     61static struct tty_driver *ipw_tty_driver;
     62
     63static char *tty_type_name(int tty_type)
     64{
     65	static char *channel_names[] = {
     66		"modem",
     67		"monitor",
     68		"RAS-raw"
     69	};
     70
     71	return channel_names[tty_type];
     72}
     73
     74static struct ipw_tty *get_tty(int index)
     75{
     76	/*
     77	 * The 'ras_raw' channel is only available when 'loopback' mode
     78	 * is enabled.
     79	 * Number of minor starts with 16 (_RANGE * _RAS_RAW).
     80	 */
     81	if (!ipwireless_loopback && index >=
     82			 IPWIRELESS_PCMCIA_MINOR_RANGE * TTYTYPE_RAS_RAW)
     83		return NULL;
     84
     85	return ttys[index];
     86}
     87
     88static int ipw_open(struct tty_struct *linux_tty, struct file *filp)
     89{
     90	struct ipw_tty *tty = get_tty(linux_tty->index);
     91
     92	if (!tty)
     93		return -ENODEV;
     94
     95	mutex_lock(&tty->ipw_tty_mutex);
     96	if (tty->port.count == 0)
     97		tty->tx_bytes_queued = 0;
     98
     99	tty->port.count++;
    100
    101	tty->port.tty = linux_tty;
    102	linux_tty->driver_data = tty;
    103
    104	if (tty->tty_type == TTYTYPE_MODEM)
    105		ipwireless_ppp_open(tty->network);
    106
    107	mutex_unlock(&tty->ipw_tty_mutex);
    108
    109	return 0;
    110}
    111
    112static void do_ipw_close(struct ipw_tty *tty)
    113{
    114	tty->port.count--;
    115
    116	if (tty->port.count == 0) {
    117		struct tty_struct *linux_tty = tty->port.tty;
    118
    119		if (linux_tty != NULL) {
    120			tty->port.tty = NULL;
    121			linux_tty->driver_data = NULL;
    122
    123			if (tty->tty_type == TTYTYPE_MODEM)
    124				ipwireless_ppp_close(tty->network);
    125		}
    126	}
    127}
    128
    129static void ipw_hangup(struct tty_struct *linux_tty)
    130{
    131	struct ipw_tty *tty = linux_tty->driver_data;
    132
    133	if (!tty)
    134		return;
    135
    136	mutex_lock(&tty->ipw_tty_mutex);
    137	if (tty->port.count == 0) {
    138		mutex_unlock(&tty->ipw_tty_mutex);
    139		return;
    140	}
    141
    142	do_ipw_close(tty);
    143
    144	mutex_unlock(&tty->ipw_tty_mutex);
    145}
    146
    147static void ipw_close(struct tty_struct *linux_tty, struct file *filp)
    148{
    149	ipw_hangup(linux_tty);
    150}
    151
    152/* Take data received from hardware, and send it out the tty */
    153void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data,
    154			unsigned int length)
    155{
    156	int work = 0;
    157
    158	mutex_lock(&tty->ipw_tty_mutex);
    159
    160	if (!tty->port.count) {
    161		mutex_unlock(&tty->ipw_tty_mutex);
    162		return;
    163	}
    164	mutex_unlock(&tty->ipw_tty_mutex);
    165
    166	work = tty_insert_flip_string(&tty->port, data, length);
    167
    168	if (work != length)
    169		printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
    170				": %d chars not inserted to flip buffer!\n",
    171				length - work);
    172
    173	if (work)
    174		tty_flip_buffer_push(&tty->port);
    175}
    176
    177static void ipw_write_packet_sent_callback(void *callback_data,
    178					   unsigned int packet_length)
    179{
    180	struct ipw_tty *tty = callback_data;
    181
    182	/*
    183	 * Packet has been sent, so we subtract the number of bytes from our
    184	 * tally of outstanding TX bytes.
    185	 */
    186	tty->tx_bytes_queued -= packet_length;
    187}
    188
    189static int ipw_write(struct tty_struct *linux_tty,
    190		     const unsigned char *buf, int count)
    191{
    192	struct ipw_tty *tty = linux_tty->driver_data;
    193	int room, ret;
    194
    195	if (!tty)
    196		return -ENODEV;
    197
    198	mutex_lock(&tty->ipw_tty_mutex);
    199	if (!tty->port.count) {
    200		mutex_unlock(&tty->ipw_tty_mutex);
    201		return -EINVAL;
    202	}
    203
    204	room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
    205	if (room < 0)
    206		room = 0;
    207	/* Don't allow caller to write any more than we have room for */
    208	if (count > room)
    209		count = room;
    210
    211	if (count == 0) {
    212		mutex_unlock(&tty->ipw_tty_mutex);
    213		return 0;
    214	}
    215
    216	ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS,
    217			       buf, count,
    218			       ipw_write_packet_sent_callback, tty);
    219	if (ret < 0) {
    220		mutex_unlock(&tty->ipw_tty_mutex);
    221		return 0;
    222	}
    223
    224	tty->tx_bytes_queued += count;
    225	mutex_unlock(&tty->ipw_tty_mutex);
    226
    227	return count;
    228}
    229
    230static unsigned int ipw_write_room(struct tty_struct *linux_tty)
    231{
    232	struct ipw_tty *tty = linux_tty->driver_data;
    233	int room;
    234
    235	/* FIXME: Exactly how is the tty object locked here .. */
    236	if (!tty)
    237		return 0;
    238
    239	if (!tty->port.count)
    240		return 0;
    241
    242	room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
    243	if (room < 0)
    244		room = 0;
    245
    246	return room;
    247}
    248
    249static int ipwireless_get_serial_info(struct tty_struct *linux_tty,
    250				      struct serial_struct *ss)
    251{
    252	struct ipw_tty *tty = linux_tty->driver_data;
    253
    254	if (!tty)
    255		return -ENODEV;
    256
    257	if (!tty->port.count)
    258		return -EINVAL;
    259
    260	ss->type = PORT_UNKNOWN;
    261	ss->line = tty->index;
    262	ss->baud_base = 115200;
    263	return 0;
    264}
    265
    266static int ipwireless_set_serial_info(struct tty_struct *linux_tty,
    267				      struct serial_struct *ss)
    268{
    269	return 0;	/* Keeps the PCMCIA scripts happy. */
    270}
    271
    272static unsigned int ipw_chars_in_buffer(struct tty_struct *linux_tty)
    273{
    274	struct ipw_tty *tty = linux_tty->driver_data;
    275
    276	if (!tty)
    277		return 0;
    278
    279	if (!tty->port.count)
    280		return 0;
    281
    282	return tty->tx_bytes_queued;
    283}
    284
    285static int get_control_lines(struct ipw_tty *tty)
    286{
    287	unsigned int my = tty->control_lines;
    288	unsigned int out = 0;
    289
    290	if (my & IPW_CONTROL_LINE_RTS)
    291		out |= TIOCM_RTS;
    292	if (my & IPW_CONTROL_LINE_DTR)
    293		out |= TIOCM_DTR;
    294	if (my & IPW_CONTROL_LINE_CTS)
    295		out |= TIOCM_CTS;
    296	if (my & IPW_CONTROL_LINE_DSR)
    297		out |= TIOCM_DSR;
    298	if (my & IPW_CONTROL_LINE_DCD)
    299		out |= TIOCM_CD;
    300
    301	return out;
    302}
    303
    304static int set_control_lines(struct ipw_tty *tty, unsigned int set,
    305			     unsigned int clear)
    306{
    307	int ret;
    308
    309	if (set & TIOCM_RTS) {
    310		ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1);
    311		if (ret)
    312			return ret;
    313		if (tty->secondary_channel_idx != -1) {
    314			ret = ipwireless_set_RTS(tty->hardware,
    315					  tty->secondary_channel_idx, 1);
    316			if (ret)
    317				return ret;
    318		}
    319	}
    320	if (set & TIOCM_DTR) {
    321		ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1);
    322		if (ret)
    323			return ret;
    324		if (tty->secondary_channel_idx != -1) {
    325			ret = ipwireless_set_DTR(tty->hardware,
    326					  tty->secondary_channel_idx, 1);
    327			if (ret)
    328				return ret;
    329		}
    330	}
    331	if (clear & TIOCM_RTS) {
    332		ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0);
    333		if (tty->secondary_channel_idx != -1) {
    334			ret = ipwireless_set_RTS(tty->hardware,
    335					  tty->secondary_channel_idx, 0);
    336			if (ret)
    337				return ret;
    338		}
    339	}
    340	if (clear & TIOCM_DTR) {
    341		ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0);
    342		if (tty->secondary_channel_idx != -1) {
    343			ret = ipwireless_set_DTR(tty->hardware,
    344					  tty->secondary_channel_idx, 0);
    345			if (ret)
    346				return ret;
    347		}
    348	}
    349	return 0;
    350}
    351
    352static int ipw_tiocmget(struct tty_struct *linux_tty)
    353{
    354	struct ipw_tty *tty = linux_tty->driver_data;
    355	/* FIXME: Exactly how is the tty object locked here .. */
    356
    357	if (!tty)
    358		return -ENODEV;
    359
    360	if (!tty->port.count)
    361		return -EINVAL;
    362
    363	return get_control_lines(tty);
    364}
    365
    366static int
    367ipw_tiocmset(struct tty_struct *linux_tty,
    368	     unsigned int set, unsigned int clear)
    369{
    370	struct ipw_tty *tty = linux_tty->driver_data;
    371	/* FIXME: Exactly how is the tty object locked here .. */
    372
    373	if (!tty)
    374		return -ENODEV;
    375
    376	if (!tty->port.count)
    377		return -EINVAL;
    378
    379	return set_control_lines(tty, set, clear);
    380}
    381
    382static int ipw_ioctl(struct tty_struct *linux_tty,
    383		     unsigned int cmd, unsigned long arg)
    384{
    385	struct ipw_tty *tty = linux_tty->driver_data;
    386
    387	if (!tty)
    388		return -ENODEV;
    389
    390	if (!tty->port.count)
    391		return -EINVAL;
    392
    393	/* FIXME: Exactly how is the tty object locked here .. */
    394	if (tty->tty_type == TTYTYPE_MODEM) {
    395		switch (cmd) {
    396		case PPPIOCGCHAN:
    397			{
    398				int chan = ipwireless_ppp_channel_index(
    399							tty->network);
    400
    401				if (chan < 0)
    402					return -ENODEV;
    403				if (put_user(chan, (int __user *) arg))
    404					return -EFAULT;
    405			}
    406			return 0;
    407
    408		case PPPIOCGUNIT:
    409			{
    410				int unit = ipwireless_ppp_unit_number(
    411						tty->network);
    412
    413				if (unit < 0)
    414					return -ENODEV;
    415				if (put_user(unit, (int __user *) arg))
    416					return -EFAULT;
    417			}
    418			return 0;
    419
    420		case FIONREAD:
    421			{
    422				int val = 0;
    423
    424				if (put_user(val, (int __user *) arg))
    425					return -EFAULT;
    426			}
    427			return 0;
    428		case TCFLSH:
    429			return tty_perform_flush(linux_tty, arg);
    430		}
    431	}
    432	return -ENOIOCTLCMD;
    433}
    434
    435static int add_tty(int j,
    436		    struct ipw_hardware *hardware,
    437		    struct ipw_network *network, int channel_idx,
    438		    int secondary_channel_idx, int tty_type)
    439{
    440	ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL);
    441	if (!ttys[j])
    442		return -ENOMEM;
    443	ttys[j]->index = j;
    444	ttys[j]->hardware = hardware;
    445	ttys[j]->channel_idx = channel_idx;
    446	ttys[j]->secondary_channel_idx = secondary_channel_idx;
    447	ttys[j]->network = network;
    448	ttys[j]->tty_type = tty_type;
    449	mutex_init(&ttys[j]->ipw_tty_mutex);
    450	tty_port_init(&ttys[j]->port);
    451
    452	tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL);
    453	ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
    454
    455	if (secondary_channel_idx != -1)
    456		ipwireless_associate_network_tty(network,
    457						 secondary_channel_idx,
    458						 ttys[j]);
    459	/* check if we provide raw device (if loopback is enabled) */
    460	if (get_tty(j))
    461		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
    462		       ": registering %s device ttyIPWp%d\n",
    463		       tty_type_name(tty_type), j);
    464
    465	return 0;
    466}
    467
    468struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware,
    469				      struct ipw_network *network)
    470{
    471	int i, j;
    472
    473	for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) {
    474		int allfree = 1;
    475
    476		for (j = i; j < IPWIRELESS_PCMCIA_MINORS;
    477				j += IPWIRELESS_PCMCIA_MINOR_RANGE)
    478			if (ttys[j] != NULL) {
    479				allfree = 0;
    480				break;
    481			}
    482
    483		if (allfree) {
    484			j = i;
    485
    486			if (add_tty(j, hardware, network,
    487					IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS,
    488					TTYTYPE_MODEM))
    489				return NULL;
    490
    491			j += IPWIRELESS_PCMCIA_MINOR_RANGE;
    492			if (add_tty(j, hardware, network,
    493					IPW_CHANNEL_DIALLER, -1,
    494					TTYTYPE_MONITOR))
    495				return NULL;
    496
    497			j += IPWIRELESS_PCMCIA_MINOR_RANGE;
    498			if (add_tty(j, hardware, network,
    499					IPW_CHANNEL_RAS, -1,
    500					TTYTYPE_RAS_RAW))
    501				return NULL;
    502
    503			return ttys[i];
    504		}
    505	}
    506	return NULL;
    507}
    508
    509/*
    510 * Must be called before ipwireless_network_free().
    511 */
    512void ipwireless_tty_free(struct ipw_tty *tty)
    513{
    514	int j;
    515	struct ipw_network *network = ttys[tty->index]->network;
    516
    517	for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS;
    518			j += IPWIRELESS_PCMCIA_MINOR_RANGE) {
    519		struct ipw_tty *ttyj = ttys[j];
    520
    521		if (ttyj) {
    522			mutex_lock(&ttyj->ipw_tty_mutex);
    523			if (get_tty(j))
    524				printk(KERN_INFO IPWIRELESS_PCCARD_NAME
    525				       ": deregistering %s device ttyIPWp%d\n",
    526				       tty_type_name(ttyj->tty_type), j);
    527			if (ttyj->port.tty != NULL) {
    528				mutex_unlock(&ttyj->ipw_tty_mutex);
    529				tty_vhangup(ttyj->port.tty);
    530				/* FIXME: Exactly how is the tty object locked here
    531				   against a parallel ioctl etc */
    532				/* FIXME2: hangup does not mean all processes
    533				 * are gone */
    534				mutex_lock(&ttyj->ipw_tty_mutex);
    535			}
    536			while (ttyj->port.count)
    537				do_ipw_close(ttyj);
    538			ipwireless_disassociate_network_ttys(network,
    539							     ttyj->channel_idx);
    540			tty_unregister_device(ipw_tty_driver, j);
    541			tty_port_destroy(&ttyj->port);
    542			ttys[j] = NULL;
    543			mutex_unlock(&ttyj->ipw_tty_mutex);
    544			kfree(ttyj);
    545		}
    546	}
    547}
    548
    549static const struct tty_operations tty_ops = {
    550	.open = ipw_open,
    551	.close = ipw_close,
    552	.hangup = ipw_hangup,
    553	.write = ipw_write,
    554	.write_room = ipw_write_room,
    555	.ioctl = ipw_ioctl,
    556	.chars_in_buffer = ipw_chars_in_buffer,
    557	.tiocmget = ipw_tiocmget,
    558	.tiocmset = ipw_tiocmset,
    559	.set_serial = ipwireless_set_serial_info,
    560	.get_serial = ipwireless_get_serial_info,
    561};
    562
    563int ipwireless_tty_init(void)
    564{
    565	int result;
    566
    567	ipw_tty_driver = tty_alloc_driver(IPWIRELESS_PCMCIA_MINORS,
    568			TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
    569	if (IS_ERR(ipw_tty_driver))
    570		return PTR_ERR(ipw_tty_driver);
    571
    572	ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME;
    573	ipw_tty_driver->name = "ttyIPWp";
    574	ipw_tty_driver->major = 0;
    575	ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START;
    576	ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
    577	ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL;
    578	ipw_tty_driver->init_termios = tty_std_termios;
    579	ipw_tty_driver->init_termios.c_cflag =
    580	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;
    581	ipw_tty_driver->init_termios.c_ispeed = 9600;
    582	ipw_tty_driver->init_termios.c_ospeed = 9600;
    583	tty_set_operations(ipw_tty_driver, &tty_ops);
    584	result = tty_register_driver(ipw_tty_driver);
    585	if (result) {
    586		printk(KERN_ERR IPWIRELESS_PCCARD_NAME
    587		       ": failed to register tty driver\n");
    588		tty_driver_kref_put(ipw_tty_driver);
    589		return result;
    590	}
    591
    592	return 0;
    593}
    594
    595void ipwireless_tty_release(void)
    596{
    597	tty_unregister_driver(ipw_tty_driver);
    598	tty_driver_kref_put(ipw_tty_driver);
    599}
    600
    601int ipwireless_tty_is_modem(struct ipw_tty *tty)
    602{
    603	return tty->tty_type == TTYTYPE_MODEM;
    604}
    605
    606void
    607ipwireless_tty_notify_control_line_change(struct ipw_tty *tty,
    608					  unsigned int channel_idx,
    609					  unsigned int control_lines,
    610					  unsigned int changed_mask)
    611{
    612	unsigned int old_control_lines = tty->control_lines;
    613
    614	tty->control_lines = (tty->control_lines & ~changed_mask)
    615		| (control_lines & changed_mask);
    616
    617	/*
    618	 * If DCD is de-asserted, we close the tty so pppd can tell that we
    619	 * have gone offline.
    620	 */
    621	if ((old_control_lines & IPW_CONTROL_LINE_DCD)
    622			&& !(tty->control_lines & IPW_CONTROL_LINE_DCD)
    623			&& tty->port.tty) {
    624		tty_hangup(tty->port.tty);
    625	}
    626}
    627