linux/drivers/tty/ipwireless/tty.c
<<
>>
Prefs
   1/*
   2 * IPWireless 3G PCMCIA Network Driver
   3 *
   4 * Original code
   5 *   by Stephen Blackheath <stephen@blacksapphire.com>,
   6 *      Ben Martel <benm@symmetric.co.nz>
   7 *
   8 * Copyrighted as follows:
   9 *   Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
  10 *
  11 * Various driver changes and rewrites, port to new kernels
  12 *   Copyright (C) 2006-2007 Jiri Kosina
  13 *
  14 * Misc code cleanups and updates
  15 *   Copyright (C) 2007 David Sterba
  16 */
  17
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/mutex.h>
  21#include <linux/ppp_defs.h>
  22#include <linux/if.h>
  23#include <linux/ppp-ioctl.h>
  24#include <linux/sched.h>
  25#include <linux/serial.h>
  26#include <linux/slab.h>
  27#include <linux/tty.h>
  28#include <linux/tty_driver.h>
  29#include <linux/tty_flip.h>
  30#include <linux/uaccess.h>
  31
  32#include "tty.h"
  33#include "network.h"
  34#include "hardware.h"
  35#include "main.h"
  36
  37#define IPWIRELESS_PCMCIA_START         (0)
  38#define IPWIRELESS_PCMCIA_MINORS        (24)
  39#define IPWIRELESS_PCMCIA_MINOR_RANGE   (8)
  40
  41#define TTYTYPE_MODEM    (0)
  42#define TTYTYPE_MONITOR  (1)
  43#define TTYTYPE_RAS_RAW  (2)
  44
  45struct ipw_tty {
  46        struct tty_port port;
  47        int index;
  48        struct ipw_hardware *hardware;
  49        unsigned int channel_idx;
  50        unsigned int secondary_channel_idx;
  51        int tty_type;
  52        struct ipw_network *network;
  53        unsigned int control_lines;
  54        struct mutex ipw_tty_mutex;
  55        int tx_bytes_queued;
  56        int closing;
  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        tty->port.low_latency = 1;
 104
 105        if (tty->tty_type == TTYTYPE_MODEM)
 106                ipwireless_ppp_open(tty->network);
 107
 108        mutex_unlock(&tty->ipw_tty_mutex);
 109
 110        return 0;
 111}
 112
 113static void do_ipw_close(struct ipw_tty *tty)
 114{
 115        tty->port.count--;
 116
 117        if (tty->port.count == 0) {
 118                struct tty_struct *linux_tty = tty->port.tty;
 119
 120                if (linux_tty != NULL) {
 121                        tty->port.tty = NULL;
 122                        linux_tty->driver_data = NULL;
 123
 124                        if (tty->tty_type == TTYTYPE_MODEM)
 125                                ipwireless_ppp_close(tty->network);
 126                }
 127        }
 128}
 129
 130static void ipw_hangup(struct tty_struct *linux_tty)
 131{
 132        struct ipw_tty *tty = linux_tty->driver_data;
 133
 134        if (!tty)
 135                return;
 136
 137        mutex_lock(&tty->ipw_tty_mutex);
 138        if (tty->port.count == 0) {
 139                mutex_unlock(&tty->ipw_tty_mutex);
 140                return;
 141        }
 142
 143        do_ipw_close(tty);
 144
 145        mutex_unlock(&tty->ipw_tty_mutex);
 146}
 147
 148static void ipw_close(struct tty_struct *linux_tty, struct file *filp)
 149{
 150        ipw_hangup(linux_tty);
 151}
 152
 153/* Take data received from hardware, and send it out the tty */
 154void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data,
 155                        unsigned int length)
 156{
 157        int work = 0;
 158
 159        mutex_lock(&tty->ipw_tty_mutex);
 160
 161        if (!tty->port.count) {
 162                mutex_unlock(&tty->ipw_tty_mutex);
 163                return;
 164        }
 165        mutex_unlock(&tty->ipw_tty_mutex);
 166
 167        work = tty_insert_flip_string(&tty->port, data, length);
 168
 169        if (work != length)
 170                printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
 171                                ": %d chars not inserted to flip buffer!\n",
 172                                length - work);
 173
 174        if (work)
 175                tty_flip_buffer_push(&tty->port);
 176}
 177
 178static void ipw_write_packet_sent_callback(void *callback_data,
 179                                           unsigned int packet_length)
 180{
 181        struct ipw_tty *tty = callback_data;
 182
 183        /*
 184         * Packet has been sent, so we subtract the number of bytes from our
 185         * tally of outstanding TX bytes.
 186         */
 187        tty->tx_bytes_queued -= packet_length;
 188}
 189
 190static int ipw_write(struct tty_struct *linux_tty,
 191                     const unsigned char *buf, int count)
 192{
 193        struct ipw_tty *tty = linux_tty->driver_data;
 194        int room, ret;
 195
 196        if (!tty)
 197                return -ENODEV;
 198
 199        mutex_lock(&tty->ipw_tty_mutex);
 200        if (!tty->port.count) {
 201                mutex_unlock(&tty->ipw_tty_mutex);
 202                return -EINVAL;
 203        }
 204
 205        room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
 206        if (room < 0)
 207                room = 0;
 208        /* Don't allow caller to write any more than we have room for */
 209        if (count > room)
 210                count = room;
 211
 212        if (count == 0) {
 213                mutex_unlock(&tty->ipw_tty_mutex);
 214                return 0;
 215        }
 216
 217        ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS,
 218                               buf, count,
 219                               ipw_write_packet_sent_callback, tty);
 220        if (ret == -1) {
 221                mutex_unlock(&tty->ipw_tty_mutex);
 222                return 0;
 223        }
 224
 225        tty->tx_bytes_queued += count;
 226        mutex_unlock(&tty->ipw_tty_mutex);
 227
 228        return count;
 229}
 230
 231static int ipw_write_room(struct tty_struct *linux_tty)
 232{
 233        struct ipw_tty *tty = linux_tty->driver_data;
 234        int room;
 235
 236        /* FIXME: Exactly how is the tty object locked here .. */
 237        if (!tty)
 238                return -ENODEV;
 239
 240        if (!tty->port.count)
 241                return -EINVAL;
 242
 243        room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
 244        if (room < 0)
 245                room = 0;
 246
 247        return room;
 248}
 249
 250static int ipwireless_get_serial_info(struct ipw_tty *tty,
 251                                      struct serial_struct __user *retinfo)
 252{
 253        struct serial_struct tmp;
 254
 255        if (!retinfo)
 256                return (-EFAULT);
 257
 258        memset(&tmp, 0, sizeof(tmp));
 259        tmp.type = PORT_UNKNOWN;
 260        tmp.line = tty->index;
 261        tmp.port = 0;
 262        tmp.irq = 0;
 263        tmp.flags = 0;
 264        tmp.baud_base = 115200;
 265        tmp.close_delay = 0;
 266        tmp.closing_wait = 0;
 267        tmp.custom_divisor = 0;
 268        tmp.hub6 = 0;
 269        if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
 270                return -EFAULT;
 271
 272        return 0;
 273}
 274
 275static int ipw_chars_in_buffer(struct tty_struct *linux_tty)
 276{
 277        struct ipw_tty *tty = linux_tty->driver_data;
 278
 279        if (!tty)
 280                return 0;
 281
 282        if (!tty->port.count)
 283                return 0;
 284
 285        return tty->tx_bytes_queued;
 286}
 287
 288static int get_control_lines(struct ipw_tty *tty)
 289{
 290        unsigned int my = tty->control_lines;
 291        unsigned int out = 0;
 292
 293        if (my & IPW_CONTROL_LINE_RTS)
 294                out |= TIOCM_RTS;
 295        if (my & IPW_CONTROL_LINE_DTR)
 296                out |= TIOCM_DTR;
 297        if (my & IPW_CONTROL_LINE_CTS)
 298                out |= TIOCM_CTS;
 299        if (my & IPW_CONTROL_LINE_DSR)
 300                out |= TIOCM_DSR;
 301        if (my & IPW_CONTROL_LINE_DCD)
 302                out |= TIOCM_CD;
 303
 304        return out;
 305}
 306
 307static int set_control_lines(struct ipw_tty *tty, unsigned int set,
 308                             unsigned int clear)
 309{
 310        int ret;
 311
 312        if (set & TIOCM_RTS) {
 313                ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1);
 314                if (ret)
 315                        return ret;
 316                if (tty->secondary_channel_idx != -1) {
 317                        ret = ipwireless_set_RTS(tty->hardware,
 318                                          tty->secondary_channel_idx, 1);
 319                        if (ret)
 320                                return ret;
 321                }
 322        }
 323        if (set & TIOCM_DTR) {
 324                ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1);
 325                if (ret)
 326                        return ret;
 327                if (tty->secondary_channel_idx != -1) {
 328                        ret = ipwireless_set_DTR(tty->hardware,
 329                                          tty->secondary_channel_idx, 1);
 330                        if (ret)
 331                                return ret;
 332                }
 333        }
 334        if (clear & TIOCM_RTS) {
 335                ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0);
 336                if (tty->secondary_channel_idx != -1) {
 337                        ret = ipwireless_set_RTS(tty->hardware,
 338                                          tty->secondary_channel_idx, 0);
 339                        if (ret)
 340                                return ret;
 341                }
 342        }
 343        if (clear & TIOCM_DTR) {
 344                ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0);
 345                if (tty->secondary_channel_idx != -1) {
 346                        ret = ipwireless_set_DTR(tty->hardware,
 347                                          tty->secondary_channel_idx, 0);
 348                        if (ret)
 349                                return ret;
 350                }
 351        }
 352        return 0;
 353}
 354
 355static int ipw_tiocmget(struct tty_struct *linux_tty)
 356{
 357        struct ipw_tty *tty = linux_tty->driver_data;
 358        /* FIXME: Exactly how is the tty object locked here .. */
 359
 360        if (!tty)
 361                return -ENODEV;
 362
 363        if (!tty->port.count)
 364                return -EINVAL;
 365
 366        return get_control_lines(tty);
 367}
 368
 369static int
 370ipw_tiocmset(struct tty_struct *linux_tty,
 371             unsigned int set, unsigned int clear)
 372{
 373        struct ipw_tty *tty = linux_tty->driver_data;
 374        /* FIXME: Exactly how is the tty object locked here .. */
 375
 376        if (!tty)
 377                return -ENODEV;
 378
 379        if (!tty->port.count)
 380                return -EINVAL;
 381
 382        return set_control_lines(tty, set, clear);
 383}
 384
 385static int ipw_ioctl(struct tty_struct *linux_tty,
 386                     unsigned int cmd, unsigned long arg)
 387{
 388        struct ipw_tty *tty = linux_tty->driver_data;
 389
 390        if (!tty)
 391                return -ENODEV;
 392
 393        if (!tty->port.count)
 394                return -EINVAL;
 395
 396        /* FIXME: Exactly how is the tty object locked here .. */
 397
 398        switch (cmd) {
 399        case TIOCGSERIAL:
 400                return ipwireless_get_serial_info(tty, (void __user *) arg);
 401
 402        case TIOCSSERIAL:
 403                return 0;       /* Keeps the PCMCIA scripts happy. */
 404        }
 405
 406        if (tty->tty_type == TTYTYPE_MODEM) {
 407                switch (cmd) {
 408                case PPPIOCGCHAN:
 409                        {
 410                                int chan = ipwireless_ppp_channel_index(
 411                                                        tty->network);
 412
 413                                if (chan < 0)
 414                                        return -ENODEV;
 415                                if (put_user(chan, (int __user *) arg))
 416                                        return -EFAULT;
 417                        }
 418                        return 0;
 419
 420                case PPPIOCGUNIT:
 421                        {
 422                                int unit = ipwireless_ppp_unit_number(
 423                                                tty->network);
 424
 425                                if (unit < 0)
 426                                        return -ENODEV;
 427                                if (put_user(unit, (int __user *) arg))
 428                                        return -EFAULT;
 429                        }
 430                        return 0;
 431
 432                case FIONREAD:
 433                        {
 434                                int val = 0;
 435
 436                                if (put_user(val, (int __user *) arg))
 437                                        return -EFAULT;
 438                        }
 439                        return 0;
 440                case TCFLSH:
 441                        return tty_perform_flush(linux_tty, arg);
 442                }
 443        }
 444        return -ENOIOCTLCMD;
 445}
 446
 447static int add_tty(int j,
 448                    struct ipw_hardware *hardware,
 449                    struct ipw_network *network, int channel_idx,
 450                    int secondary_channel_idx, int tty_type)
 451{
 452        ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL);
 453        if (!ttys[j])
 454                return -ENOMEM;
 455        ttys[j]->index = j;
 456        ttys[j]->hardware = hardware;
 457        ttys[j]->channel_idx = channel_idx;
 458        ttys[j]->secondary_channel_idx = secondary_channel_idx;
 459        ttys[j]->network = network;
 460        ttys[j]->tty_type = tty_type;
 461        mutex_init(&ttys[j]->ipw_tty_mutex);
 462        tty_port_init(&ttys[j]->port);
 463
 464        tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL);
 465        ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
 466
 467        if (secondary_channel_idx != -1)
 468                ipwireless_associate_network_tty(network,
 469                                                 secondary_channel_idx,
 470                                                 ttys[j]);
 471        /* check if we provide raw device (if loopback is enabled) */
 472        if (get_tty(j))
 473                printk(KERN_INFO IPWIRELESS_PCCARD_NAME
 474                       ": registering %s device ttyIPWp%d\n",
 475                       tty_type_name(tty_type), j);
 476
 477        return 0;
 478}
 479
 480struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware,
 481                                      struct ipw_network *network)
 482{
 483        int i, j;
 484
 485        for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) {
 486                int allfree = 1;
 487
 488                for (j = i; j < IPWIRELESS_PCMCIA_MINORS;
 489                                j += IPWIRELESS_PCMCIA_MINOR_RANGE)
 490                        if (ttys[j] != NULL) {
 491                                allfree = 0;
 492                                break;
 493                        }
 494
 495                if (allfree) {
 496                        j = i;
 497
 498                        if (add_tty(j, hardware, network,
 499                                        IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS,
 500                                        TTYTYPE_MODEM))
 501                                return NULL;
 502
 503                        j += IPWIRELESS_PCMCIA_MINOR_RANGE;
 504                        if (add_tty(j, hardware, network,
 505                                        IPW_CHANNEL_DIALLER, -1,
 506                                        TTYTYPE_MONITOR))
 507                                return NULL;
 508
 509                        j += IPWIRELESS_PCMCIA_MINOR_RANGE;
 510                        if (add_tty(j, hardware, network,
 511                                        IPW_CHANNEL_RAS, -1,
 512                                        TTYTYPE_RAS_RAW))
 513                                return NULL;
 514
 515                        return ttys[i];
 516                }
 517        }
 518        return NULL;
 519}
 520
 521/*
 522 * Must be called before ipwireless_network_free().
 523 */
 524void ipwireless_tty_free(struct ipw_tty *tty)
 525{
 526        int j;
 527        struct ipw_network *network = ttys[tty->index]->network;
 528
 529        for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS;
 530                        j += IPWIRELESS_PCMCIA_MINOR_RANGE) {
 531                struct ipw_tty *ttyj = ttys[j];
 532
 533                if (ttyj) {
 534                        mutex_lock(&ttyj->ipw_tty_mutex);
 535                        if (get_tty(j))
 536                                printk(KERN_INFO IPWIRELESS_PCCARD_NAME
 537                                       ": deregistering %s device ttyIPWp%d\n",
 538                                       tty_type_name(ttyj->tty_type), j);
 539                        ttyj->closing = 1;
 540                        if (ttyj->port.tty != NULL) {
 541                                mutex_unlock(&ttyj->ipw_tty_mutex);
 542                                tty_vhangup(ttyj->port.tty);
 543                                /* FIXME: Exactly how is the tty object locked here
 544                                   against a parallel ioctl etc */
 545                                /* FIXME2: hangup does not mean all processes
 546                                 * are gone */
 547                                mutex_lock(&ttyj->ipw_tty_mutex);
 548                        }
 549                        while (ttyj->port.count)
 550                                do_ipw_close(ttyj);
 551                        ipwireless_disassociate_network_ttys(network,
 552                                                             ttyj->channel_idx);
 553                        tty_unregister_device(ipw_tty_driver, j);
 554                        tty_port_destroy(&ttyj->port);
 555                        ttys[j] = NULL;
 556                        mutex_unlock(&ttyj->ipw_tty_mutex);
 557                        kfree(ttyj);
 558                }
 559        }
 560}
 561
 562static const struct tty_operations tty_ops = {
 563        .open = ipw_open,
 564        .close = ipw_close,
 565        .hangup = ipw_hangup,
 566        .write = ipw_write,
 567        .write_room = ipw_write_room,
 568        .ioctl = ipw_ioctl,
 569        .chars_in_buffer = ipw_chars_in_buffer,
 570        .tiocmget = ipw_tiocmget,
 571        .tiocmset = ipw_tiocmset,
 572};
 573
 574int ipwireless_tty_init(void)
 575{
 576        int result;
 577
 578        ipw_tty_driver = alloc_tty_driver(IPWIRELESS_PCMCIA_MINORS);
 579        if (!ipw_tty_driver)
 580                return -ENOMEM;
 581
 582        ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME;
 583        ipw_tty_driver->name = "ttyIPWp";
 584        ipw_tty_driver->major = 0;
 585        ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START;
 586        ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 587        ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL;
 588        ipw_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 589        ipw_tty_driver->init_termios = tty_std_termios;
 590        ipw_tty_driver->init_termios.c_cflag =
 591            B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 592        ipw_tty_driver->init_termios.c_ispeed = 9600;
 593        ipw_tty_driver->init_termios.c_ospeed = 9600;
 594        tty_set_operations(ipw_tty_driver, &tty_ops);
 595        result = tty_register_driver(ipw_tty_driver);
 596        if (result) {
 597                printk(KERN_ERR IPWIRELESS_PCCARD_NAME
 598                       ": failed to register tty driver\n");
 599                put_tty_driver(ipw_tty_driver);
 600                return result;
 601        }
 602
 603        return 0;
 604}
 605
 606void ipwireless_tty_release(void)
 607{
 608        int ret;
 609
 610        ret = tty_unregister_driver(ipw_tty_driver);
 611        put_tty_driver(ipw_tty_driver);
 612        if (ret != 0)
 613                printk(KERN_ERR IPWIRELESS_PCCARD_NAME
 614                        ": tty_unregister_driver failed with code %d\n", ret);
 615}
 616
 617int ipwireless_tty_is_modem(struct ipw_tty *tty)
 618{
 619        return tty->tty_type == TTYTYPE_MODEM;
 620}
 621
 622void
 623ipwireless_tty_notify_control_line_change(struct ipw_tty *tty,
 624                                          unsigned int channel_idx,
 625                                          unsigned int control_lines,
 626                                          unsigned int changed_mask)
 627{
 628        unsigned int old_control_lines = tty->control_lines;
 629
 630        tty->control_lines = (tty->control_lines & ~changed_mask)
 631                | (control_lines & changed_mask);
 632
 633        /*
 634         * If DCD is de-asserted, we close the tty so pppd can tell that we
 635         * have gone offline.
 636         */
 637        if ((old_control_lines & IPW_CONTROL_LINE_DCD)
 638                        && !(tty->control_lines & IPW_CONTROL_LINE_DCD)
 639                        && tty->port.tty) {
 640                tty_hangup(tty->port.tty);
 641        }
 642}
 643
 644