brintos

brintos / linux-shallow public Read only

0
0
Text · 13.8 KiB · b6de408 Raw
628 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * IPWireless 3G PCMCIA Network Driver4 *5 * Original code6 *   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 kernels13 *   Copyright (C) 2006-2007 Jiri Kosina14 *15 * Misc code cleanups and updates16 *   Copyright (C) 2007 David Sterba17 */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' mode78	 * 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_NAME170				": %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 our184	 * tally of outstanding TX bytes.185	 */186	tty->tx_bytes_queued -= packet_length;187}188 189static ssize_t ipw_write(struct tty_struct *linux_tty, const u8 *buf,190			 size_t 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 int367ipw_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_NAME462		       ": 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_NAME525				       ": 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 here531				   against a parallel ioctl etc */532				/* FIXME2: hangup does not mean all processes533				 * 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_NAME587		       ": 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 606void607ipwireless_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 we619	 * 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 628