xref: /openbmc/linux/drivers/tty/ipwireless/tty.c (revision 82e6fdd6)
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 == -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 
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 ipw_tty *tty,
252 				      struct serial_struct __user *retinfo)
253 {
254 	struct serial_struct tmp;
255 
256 	memset(&tmp, 0, sizeof(tmp));
257 	tmp.type = PORT_UNKNOWN;
258 	tmp.line = tty->index;
259 	tmp.baud_base = 115200;
260 
261 	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
262 		return -EFAULT;
263 
264 	return 0;
265 }
266 
267 static int ipw_chars_in_buffer(struct tty_struct *linux_tty)
268 {
269 	struct ipw_tty *tty = linux_tty->driver_data;
270 
271 	if (!tty)
272 		return 0;
273 
274 	if (!tty->port.count)
275 		return 0;
276 
277 	return tty->tx_bytes_queued;
278 }
279 
280 static int get_control_lines(struct ipw_tty *tty)
281 {
282 	unsigned int my = tty->control_lines;
283 	unsigned int out = 0;
284 
285 	if (my & IPW_CONTROL_LINE_RTS)
286 		out |= TIOCM_RTS;
287 	if (my & IPW_CONTROL_LINE_DTR)
288 		out |= TIOCM_DTR;
289 	if (my & IPW_CONTROL_LINE_CTS)
290 		out |= TIOCM_CTS;
291 	if (my & IPW_CONTROL_LINE_DSR)
292 		out |= TIOCM_DSR;
293 	if (my & IPW_CONTROL_LINE_DCD)
294 		out |= TIOCM_CD;
295 
296 	return out;
297 }
298 
299 static int set_control_lines(struct ipw_tty *tty, unsigned int set,
300 			     unsigned int clear)
301 {
302 	int ret;
303 
304 	if (set & TIOCM_RTS) {
305 		ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1);
306 		if (ret)
307 			return ret;
308 		if (tty->secondary_channel_idx != -1) {
309 			ret = ipwireless_set_RTS(tty->hardware,
310 					  tty->secondary_channel_idx, 1);
311 			if (ret)
312 				return ret;
313 		}
314 	}
315 	if (set & TIOCM_DTR) {
316 		ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1);
317 		if (ret)
318 			return ret;
319 		if (tty->secondary_channel_idx != -1) {
320 			ret = ipwireless_set_DTR(tty->hardware,
321 					  tty->secondary_channel_idx, 1);
322 			if (ret)
323 				return ret;
324 		}
325 	}
326 	if (clear & TIOCM_RTS) {
327 		ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0);
328 		if (tty->secondary_channel_idx != -1) {
329 			ret = ipwireless_set_RTS(tty->hardware,
330 					  tty->secondary_channel_idx, 0);
331 			if (ret)
332 				return ret;
333 		}
334 	}
335 	if (clear & TIOCM_DTR) {
336 		ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0);
337 		if (tty->secondary_channel_idx != -1) {
338 			ret = ipwireless_set_DTR(tty->hardware,
339 					  tty->secondary_channel_idx, 0);
340 			if (ret)
341 				return ret;
342 		}
343 	}
344 	return 0;
345 }
346 
347 static int ipw_tiocmget(struct tty_struct *linux_tty)
348 {
349 	struct ipw_tty *tty = linux_tty->driver_data;
350 	/* FIXME: Exactly how is the tty object locked here .. */
351 
352 	if (!tty)
353 		return -ENODEV;
354 
355 	if (!tty->port.count)
356 		return -EINVAL;
357 
358 	return get_control_lines(tty);
359 }
360 
361 static int
362 ipw_tiocmset(struct tty_struct *linux_tty,
363 	     unsigned int set, unsigned int clear)
364 {
365 	struct ipw_tty *tty = linux_tty->driver_data;
366 	/* FIXME: Exactly how is the tty object locked here .. */
367 
368 	if (!tty)
369 		return -ENODEV;
370 
371 	if (!tty->port.count)
372 		return -EINVAL;
373 
374 	return set_control_lines(tty, set, clear);
375 }
376 
377 static int ipw_ioctl(struct tty_struct *linux_tty,
378 		     unsigned int cmd, unsigned long arg)
379 {
380 	struct ipw_tty *tty = linux_tty->driver_data;
381 
382 	if (!tty)
383 		return -ENODEV;
384 
385 	if (!tty->port.count)
386 		return -EINVAL;
387 
388 	/* FIXME: Exactly how is the tty object locked here .. */
389 
390 	switch (cmd) {
391 	case TIOCGSERIAL:
392 		return ipwireless_get_serial_info(tty, (void __user *) arg);
393 
394 	case TIOCSSERIAL:
395 		return 0;	/* Keeps the PCMCIA scripts happy. */
396 	}
397 
398 	if (tty->tty_type == TTYTYPE_MODEM) {
399 		switch (cmd) {
400 		case PPPIOCGCHAN:
401 			{
402 				int chan = ipwireless_ppp_channel_index(
403 							tty->network);
404 
405 				if (chan < 0)
406 					return -ENODEV;
407 				if (put_user(chan, (int __user *) arg))
408 					return -EFAULT;
409 			}
410 			return 0;
411 
412 		case PPPIOCGUNIT:
413 			{
414 				int unit = ipwireless_ppp_unit_number(
415 						tty->network);
416 
417 				if (unit < 0)
418 					return -ENODEV;
419 				if (put_user(unit, (int __user *) arg))
420 					return -EFAULT;
421 			}
422 			return 0;
423 
424 		case FIONREAD:
425 			{
426 				int val = 0;
427 
428 				if (put_user(val, (int __user *) arg))
429 					return -EFAULT;
430 			}
431 			return 0;
432 		case TCFLSH:
433 			return tty_perform_flush(linux_tty, arg);
434 		}
435 	}
436 	return -ENOIOCTLCMD;
437 }
438 
439 static int add_tty(int j,
440 		    struct ipw_hardware *hardware,
441 		    struct ipw_network *network, int channel_idx,
442 		    int secondary_channel_idx, int tty_type)
443 {
444 	ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL);
445 	if (!ttys[j])
446 		return -ENOMEM;
447 	ttys[j]->index = j;
448 	ttys[j]->hardware = hardware;
449 	ttys[j]->channel_idx = channel_idx;
450 	ttys[j]->secondary_channel_idx = secondary_channel_idx;
451 	ttys[j]->network = network;
452 	ttys[j]->tty_type = tty_type;
453 	mutex_init(&ttys[j]->ipw_tty_mutex);
454 	tty_port_init(&ttys[j]->port);
455 
456 	tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL);
457 	ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
458 
459 	if (secondary_channel_idx != -1)
460 		ipwireless_associate_network_tty(network,
461 						 secondary_channel_idx,
462 						 ttys[j]);
463 	/* check if we provide raw device (if loopback is enabled) */
464 	if (get_tty(j))
465 		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
466 		       ": registering %s device ttyIPWp%d\n",
467 		       tty_type_name(tty_type), j);
468 
469 	return 0;
470 }
471 
472 struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware,
473 				      struct ipw_network *network)
474 {
475 	int i, j;
476 
477 	for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) {
478 		int allfree = 1;
479 
480 		for (j = i; j < IPWIRELESS_PCMCIA_MINORS;
481 				j += IPWIRELESS_PCMCIA_MINOR_RANGE)
482 			if (ttys[j] != NULL) {
483 				allfree = 0;
484 				break;
485 			}
486 
487 		if (allfree) {
488 			j = i;
489 
490 			if (add_tty(j, hardware, network,
491 					IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS,
492 					TTYTYPE_MODEM))
493 				return NULL;
494 
495 			j += IPWIRELESS_PCMCIA_MINOR_RANGE;
496 			if (add_tty(j, hardware, network,
497 					IPW_CHANNEL_DIALLER, -1,
498 					TTYTYPE_MONITOR))
499 				return NULL;
500 
501 			j += IPWIRELESS_PCMCIA_MINOR_RANGE;
502 			if (add_tty(j, hardware, network,
503 					IPW_CHANNEL_RAS, -1,
504 					TTYTYPE_RAS_RAW))
505 				return NULL;
506 
507 			return ttys[i];
508 		}
509 	}
510 	return NULL;
511 }
512 
513 /*
514  * Must be called before ipwireless_network_free().
515  */
516 void ipwireless_tty_free(struct ipw_tty *tty)
517 {
518 	int j;
519 	struct ipw_network *network = ttys[tty->index]->network;
520 
521 	for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS;
522 			j += IPWIRELESS_PCMCIA_MINOR_RANGE) {
523 		struct ipw_tty *ttyj = ttys[j];
524 
525 		if (ttyj) {
526 			mutex_lock(&ttyj->ipw_tty_mutex);
527 			if (get_tty(j))
528 				printk(KERN_INFO IPWIRELESS_PCCARD_NAME
529 				       ": deregistering %s device ttyIPWp%d\n",
530 				       tty_type_name(ttyj->tty_type), j);
531 			ttyj->closing = 1;
532 			if (ttyj->port.tty != NULL) {
533 				mutex_unlock(&ttyj->ipw_tty_mutex);
534 				tty_vhangup(ttyj->port.tty);
535 				/* FIXME: Exactly how is the tty object locked here
536 				   against a parallel ioctl etc */
537 				/* FIXME2: hangup does not mean all processes
538 				 * are gone */
539 				mutex_lock(&ttyj->ipw_tty_mutex);
540 			}
541 			while (ttyj->port.count)
542 				do_ipw_close(ttyj);
543 			ipwireless_disassociate_network_ttys(network,
544 							     ttyj->channel_idx);
545 			tty_unregister_device(ipw_tty_driver, j);
546 			tty_port_destroy(&ttyj->port);
547 			ttys[j] = NULL;
548 			mutex_unlock(&ttyj->ipw_tty_mutex);
549 			kfree(ttyj);
550 		}
551 	}
552 }
553 
554 static const struct tty_operations tty_ops = {
555 	.open = ipw_open,
556 	.close = ipw_close,
557 	.hangup = ipw_hangup,
558 	.write = ipw_write,
559 	.write_room = ipw_write_room,
560 	.ioctl = ipw_ioctl,
561 	.chars_in_buffer = ipw_chars_in_buffer,
562 	.tiocmget = ipw_tiocmget,
563 	.tiocmset = ipw_tiocmset,
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