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