Linux Audio

Check our new training course

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