xref: /openbmc/linux/drivers/tty/ipwireless/tty.c (revision abe9af53)
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 
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 	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 
114 static 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 
131 static 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 
149 static 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 */
155 void 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 
179 static 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 
191 static 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 < 0) {
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 
232 static 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 
251 static 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 
268 static 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 
274 static 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 
287 static 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 
306 static 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 
354 static 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 
368 static int
369 ipw_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 
384 static 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 
437 static 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 
470 struct 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  */
514 void 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 
552 static 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 
566 int 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 
598 void 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 
609 int ipwireless_tty_is_modem(struct ipw_tty *tty)
610 {
611 	return tty->tty_type == TTYTYPE_MODEM;
612 }
613 
614 void
615 ipwireless_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 
636