tty.c (14096B)
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 46struct 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 59static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS]; 60 61static struct tty_driver *ipw_tty_driver; 62 63static 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 74static 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 88static 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 112static 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 129static 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 147static 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 */ 153void 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 177static 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 189static int ipw_write(struct tty_struct *linux_tty, 190 const unsigned char *buf, int 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 230static 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 249static 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 266static 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 272static 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 285static 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 304static 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 352static 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 366static int 367ipw_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 382static 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 435static 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 468struct 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 */ 512void 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 549static 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 563int 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 595void ipwireless_tty_release(void) 596{ 597 tty_unregister_driver(ipw_tty_driver); 598 tty_driver_kref_put(ipw_tty_driver); 599} 600 601int ipwireless_tty_is_modem(struct ipw_tty *tty) 602{ 603 return tty->tty_type == TTYTYPE_MODEM; 604} 605 606void 607ipwireless_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