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