Linux Audio

Check our new training course

Yocto / OpenEmbedded training

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