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