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};
  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
 628