xref: /openbmc/linux/drivers/tty/ipwireless/tty.c (revision d0b73b48)
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 
46 struct 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 
60 static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS];
61 
62 static struct tty_driver *ipw_tty_driver;
63 
64 static 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 
75 static 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 
89 static 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 
119 static 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 
136 static 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 
154 static 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 */
160 void 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 
193 static 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 
205 static 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 
246 static 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 
265 static 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 
290 static 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 
303 static 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 
322 static 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 
370 static 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 
384 static int
385 ipw_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 
400 static 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 
462 static 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_port_register_device(&ttys[j]->port, 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 
495 struct 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  */
539 void 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 			tty_port_destroy(&ttyj->port);
570 			ttys[j] = NULL;
571 			mutex_unlock(&ttyj->ipw_tty_mutex);
572 			kfree(ttyj);
573 		}
574 	}
575 }
576 
577 static const struct tty_operations tty_ops = {
578 	.open = ipw_open,
579 	.close = ipw_close,
580 	.hangup = ipw_hangup,
581 	.write = ipw_write,
582 	.write_room = ipw_write_room,
583 	.ioctl = ipw_ioctl,
584 	.chars_in_buffer = ipw_chars_in_buffer,
585 	.tiocmget = ipw_tiocmget,
586 	.tiocmset = ipw_tiocmset,
587 };
588 
589 int ipwireless_tty_init(void)
590 {
591 	int result;
592 
593 	ipw_tty_driver = alloc_tty_driver(IPWIRELESS_PCMCIA_MINORS);
594 	if (!ipw_tty_driver)
595 		return -ENOMEM;
596 
597 	ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME;
598 	ipw_tty_driver->name = "ttyIPWp";
599 	ipw_tty_driver->major = 0;
600 	ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START;
601 	ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
602 	ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL;
603 	ipw_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
604 	ipw_tty_driver->init_termios = tty_std_termios;
605 	ipw_tty_driver->init_termios.c_cflag =
606 	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;
607 	ipw_tty_driver->init_termios.c_ispeed = 9600;
608 	ipw_tty_driver->init_termios.c_ospeed = 9600;
609 	tty_set_operations(ipw_tty_driver, &tty_ops);
610 	result = tty_register_driver(ipw_tty_driver);
611 	if (result) {
612 		printk(KERN_ERR IPWIRELESS_PCCARD_NAME
613 		       ": failed to register tty driver\n");
614 		put_tty_driver(ipw_tty_driver);
615 		return result;
616 	}
617 
618 	return 0;
619 }
620 
621 void ipwireless_tty_release(void)
622 {
623 	int ret;
624 
625 	ret = tty_unregister_driver(ipw_tty_driver);
626 	put_tty_driver(ipw_tty_driver);
627 	if (ret != 0)
628 		printk(KERN_ERR IPWIRELESS_PCCARD_NAME
629 			": tty_unregister_driver failed with code %d\n", ret);
630 }
631 
632 int ipwireless_tty_is_modem(struct ipw_tty *tty)
633 {
634 	return tty->tty_type == TTYTYPE_MODEM;
635 }
636 
637 void
638 ipwireless_tty_notify_control_line_change(struct ipw_tty *tty,
639 					  unsigned int channel_idx,
640 					  unsigned int control_lines,
641 					  unsigned int changed_mask)
642 {
643 	unsigned int old_control_lines = tty->control_lines;
644 
645 	tty->control_lines = (tty->control_lines & ~changed_mask)
646 		| (control_lines & changed_mask);
647 
648 	/*
649 	 * If DCD is de-asserted, we close the tty so pppd can tell that we
650 	 * have gone offline.
651 	 */
652 	if ((old_control_lines & IPW_CONTROL_LINE_DCD)
653 			&& !(tty->control_lines & IPW_CONTROL_LINE_DCD)
654 			&& tty->port.tty) {
655 		tty_hangup(tty->port.tty);
656 	}
657 }
658 
659