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