Linux Audio

Check our new training course

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