root/drivers/tty/ipwireless/tty.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tty_type_name
  2. get_tty
  3. ipw_open
  4. do_ipw_close
  5. ipw_hangup
  6. ipw_close
  7. ipwireless_tty_received
  8. ipw_write_packet_sent_callback
  9. ipw_write
  10. ipw_write_room
  11. ipwireless_get_serial_info
  12. ipwireless_set_serial_info
  13. ipw_chars_in_buffer
  14. get_control_lines
  15. set_control_lines
  16. ipw_tiocmget
  17. ipw_tiocmset
  18. ipw_ioctl
  19. add_tty
  20. ipwireless_tty_create
  21. ipwireless_tty_free
  22. ipwireless_tty_init
  23. ipwireless_tty_release
  24. ipwireless_tty_is_modem
  25. ipwireless_tty_notify_control_line_change

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

/* [<][>][^][v][top][bottom][index][help] */