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