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        memset(&tmp, 0, sizeof(tmp));
 256        tmp.type = PORT_UNKNOWN;
 257        tmp.line = tty->index;
 258        tmp.baud_base = 115200;
 259
 260        if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
 261                return -EFAULT;
 262
 263        return 0;
 264}
 265
 266static int ipw_chars_in_buffer(struct tty_struct *linux_tty)
 267{
 268        struct ipw_tty *tty = linux_tty->driver_data;
 269
 270        if (!tty)
 271                return 0;
 272
 273        if (!tty->port.count)
 274                return 0;
 275
 276        return tty->tx_bytes_queued;
 277}
 278
 279static int get_control_lines(struct ipw_tty *tty)
 280{
 281        unsigned int my = tty->control_lines;
 282        unsigned int out = 0;
 283
 284        if (my & IPW_CONTROL_LINE_RTS)
 285                out |= TIOCM_RTS;
 286        if (my & IPW_CONTROL_LINE_DTR)
 287                out |= TIOCM_DTR;
 288        if (my & IPW_CONTROL_LINE_CTS)
 289                out |= TIOCM_CTS;
 290        if (my & IPW_CONTROL_LINE_DSR)
 291                out |= TIOCM_DSR;
 292        if (my & IPW_CONTROL_LINE_DCD)
 293                out |= TIOCM_CD;
 294
 295        return out;
 296}
 297
 298static int set_control_lines(struct ipw_tty *tty, unsigned int set,
 299                             unsigned int clear)
 300{
 301        int ret;
 302
 303        if (set & TIOCM_RTS) {
 304                ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1);
 305                if (ret)
 306                        return ret;
 307                if (tty->secondary_channel_idx != -1) {
 308                        ret = ipwireless_set_RTS(tty->hardware,
 309                                          tty->secondary_channel_idx, 1);
 310                        if (ret)
 311                                return ret;
 312                }
 313        }
 314        if (set & TIOCM_DTR) {
 315                ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1);
 316                if (ret)
 317                        return ret;
 318                if (tty->secondary_channel_idx != -1) {
 319                        ret = ipwireless_set_DTR(tty->hardware,
 320                                          tty->secondary_channel_idx, 1);
 321                        if (ret)
 322                                return ret;
 323                }
 324        }
 325        if (clear & TIOCM_RTS) {
 326                ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0);
 327                if (tty->secondary_channel_idx != -1) {
 328                        ret = ipwireless_set_RTS(tty->hardware,
 329                                          tty->secondary_channel_idx, 0);
 330                        if (ret)
 331                                return ret;
 332                }
 333        }
 334        if (clear & TIOCM_DTR) {
 335                ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0);
 336                if (tty->secondary_channel_idx != -1) {
 337                        ret = ipwireless_set_DTR(tty->hardware,
 338                                          tty->secondary_channel_idx, 0);
 339                        if (ret)
 340                                return ret;
 341                }
 342        }
 343        return 0;
 344}
 345
 346static int ipw_tiocmget(struct tty_struct *linux_tty)
 347{
 348        struct ipw_tty *tty = linux_tty->driver_data;
 349        /* FIXME: Exactly how is the tty object locked here .. */
 350
 351        if (!tty)
 352                return -ENODEV;
 353
 354        if (!tty->port.count)
 355                return -EINVAL;
 356
 357        return get_control_lines(tty);
 358}
 359
 360static int
 361ipw_tiocmset(struct tty_struct *linux_tty,
 362             unsigned int set, unsigned int clear)
 363{
 364        struct ipw_tty *tty = linux_tty->driver_data;
 365        /* FIXME: Exactly how is the tty object locked here .. */
 366
 367        if (!tty)
 368                return -ENODEV;
 369
 370        if (!tty->port.count)
 371                return -EINVAL;
 372
 373        return set_control_lines(tty, set, clear);
 374}
 375
 376static int ipw_ioctl(struct tty_struct *linux_tty,
 377                     unsigned int cmd, unsigned long arg)
 378{
 379        struct ipw_tty *tty = linux_tty->driver_data;
 380
 381        if (!tty)
 382                return -ENODEV;
 383
 384        if (!tty->port.count)
 385                return -EINVAL;
 386
 387        /* FIXME: Exactly how is the tty object locked here .. */
 388
 389        switch (cmd) {
 390        case TIOCGSERIAL:
 391                return ipwireless_get_serial_info(tty, (void __user *) arg);
 392
 393        case TIOCSSERIAL:
 394                return 0;       /* Keeps the PCMCIA scripts happy. */
 395        }
 396
 397        if (tty->tty_type == TTYTYPE_MODEM) {
 398                switch (cmd) {
 399                case PPPIOCGCHAN:
 400                        {
 401                                int chan = ipwireless_ppp_channel_index(
 402                                                        tty->network);
 403
 404                                if (chan < 0)
 405                                        return -ENODEV;
 406                                if (put_user(chan, (int __user *) arg))
 407                                        return -EFAULT;
 408                        }
 409                        return 0;
 410
 411                case PPPIOCGUNIT:
 412                        {
 413                                int unit = ipwireless_ppp_unit_number(
 414                                                tty->network);
 415
 416                                if (unit < 0)
 417                                        return -ENODEV;
 418                                if (put_user(unit, (int __user *) arg))
 419                                        return -EFAULT;
 420                        }
 421                        return 0;
 422
 423                case FIONREAD:
 424                        {
 425                                int val = 0;
 426
 427                                if (put_user(val, (int __user *) arg))
 428                                        return -EFAULT;
 429                        }
 430                        return 0;
 431                case TCFLSH:
 432                        return tty_perform_flush(linux_tty, arg);
 433                }
 434        }
 435        return -ENOIOCTLCMD;
 436}
 437
 438static int add_tty(int j,
 439                    struct ipw_hardware *hardware,
 440                    struct ipw_network *network, int channel_idx,
 441                    int secondary_channel_idx, int tty_type)
 442{
 443        ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL);
 444        if (!ttys[j])
 445                return -ENOMEM;
 446        ttys[j]->index = j;
 447        ttys[j]->hardware = hardware;
 448        ttys[j]->channel_idx = channel_idx;
 449        ttys[j]->secondary_channel_idx = secondary_channel_idx;
 450        ttys[j]->network = network;
 451        ttys[j]->tty_type = tty_type;
 452        mutex_init(&ttys[j]->ipw_tty_mutex);
 453        tty_port_init(&ttys[j]->port);
 454
 455        tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL);
 456        ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
 457
 458        if (secondary_channel_idx != -1)
 459                ipwireless_associate_network_tty(network,
 460                                                 secondary_channel_idx,
 461                                                 ttys[j]);
 462        /* check if we provide raw device (if loopback is enabled) */
 463        if (get_tty(j))
 464                printk(KERN_INFO IPWIRELESS_PCCARD_NAME
 465                       ": registering %s device ttyIPWp%d\n",
 466                       tty_type_name(tty_type), j);
 467
 468        return 0;
 469}
 470
 471struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware,
 472                                      struct ipw_network *network)
 473{
 474        int i, j;
 475
 476        for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) {
 477                int allfree = 1;
 478
 479                for (j = i; j < IPWIRELESS_PCMCIA_MINORS;
 480                                j += IPWIRELESS_PCMCIA_MINOR_RANGE)
 481                        if (ttys[j] != NULL) {
 482                                allfree = 0;
 483                                break;
 484                        }
 485
 486                if (allfree) {
 487                        j = i;
 488
 489                        if (add_tty(j, hardware, network,
 490                                        IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS,
 491                                        TTYTYPE_MODEM))
 492                                return NULL;
 493
 494                        j += IPWIRELESS_PCMCIA_MINOR_RANGE;
 495                        if (add_tty(j, hardware, network,
 496                                        IPW_CHANNEL_DIALLER, -1,
 497                                        TTYTYPE_MONITOR))
 498                                return NULL;
 499
 500                        j += IPWIRELESS_PCMCIA_MINOR_RANGE;
 501                        if (add_tty(j, hardware, network,
 502                                        IPW_CHANNEL_RAS, -1,
 503                                        TTYTYPE_RAS_RAW))
 504                                return NULL;
 505
 506                        return ttys[i];
 507                }
 508        }
 509        return NULL;
 510}
 511
 512/*
 513 * Must be called before ipwireless_network_free().
 514 */
 515void ipwireless_tty_free(struct ipw_tty *tty)
 516{
 517        int j;
 518        struct ipw_network *network = ttys[tty->index]->network;
 519
 520        for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS;
 521                        j += IPWIRELESS_PCMCIA_MINOR_RANGE) {
 522                struct ipw_tty *ttyj = ttys[j];
 523
 524                if (ttyj) {
 525                        mutex_lock(&ttyj->ipw_tty_mutex);
 526                        if (get_tty(j))
 527                                printk(KERN_INFO IPWIRELESS_PCCARD_NAME
 528                                       ": deregistering %s device ttyIPWp%d\n",
 529                                       tty_type_name(ttyj->tty_type), j);
 530                        ttyj->closing = 1;
 531                        if (ttyj->port.tty != NULL) {
 532                                mutex_unlock(&ttyj->ipw_tty_mutex);
 533                                tty_vhangup(ttyj->port.tty);
 534                                /* FIXME: Exactly how is the tty object locked here
 535                                   against a parallel ioctl etc */
 536                                /* FIXME2: hangup does not mean all processes
 537                                 * are gone */
 538                                mutex_lock(&ttyj->ipw_tty_mutex);
 539                        }
 540                        while (ttyj->port.count)
 541                                do_ipw_close(ttyj);
 542                        ipwireless_disassociate_network_ttys(network,
 543                                                             ttyj->channel_idx);
 544                        tty_unregister_device(ipw_tty_driver, j);
 545                        tty_port_destroy(&ttyj->port);
 546                        ttys[j] = NULL;
 547                        mutex_unlock(&ttyj->ipw_tty_mutex);
 548                        kfree(ttyj);
 549                }
 550        }
 551}
 552
 553static const struct tty_operations tty_ops = {
 554        .open = ipw_open,
 555        .close = ipw_close,
 556        .hangup = ipw_hangup,
 557        .write = ipw_write,
 558        .write_room = ipw_write_room,
 559        .ioctl = ipw_ioctl,
 560        .chars_in_buffer = ipw_chars_in_buffer,
 561        .tiocmget = ipw_tiocmget,
 562        .tiocmset = ipw_tiocmset,
 563};
 564
 565int ipwireless_tty_init(void)
 566{
 567        int result;
 568
 569        ipw_tty_driver = alloc_tty_driver(IPWIRELESS_PCMCIA_MINORS);
 570        if (!ipw_tty_driver)
 571                return -ENOMEM;
 572
 573        ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME;
 574        ipw_tty_driver->name = "ttyIPWp";
 575        ipw_tty_driver->major = 0;
 576        ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START;
 577        ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 578        ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL;
 579        ipw_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 580        ipw_tty_driver->init_termios = tty_std_termios;
 581        ipw_tty_driver->init_termios.c_cflag =
 582            B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 583        ipw_tty_driver->init_termios.c_ispeed = 9600;
 584        ipw_tty_driver->init_termios.c_ospeed = 9600;
 585        tty_set_operations(ipw_tty_driver, &tty_ops);
 586        result = tty_register_driver(ipw_tty_driver);
 587        if (result) {
 588                printk(KERN_ERR IPWIRELESS_PCCARD_NAME
 589                       ": failed to register tty driver\n");
 590                put_tty_driver(ipw_tty_driver);
 591                return result;
 592        }
 593
 594        return 0;
 595}
 596
 597void ipwireless_tty_release(void)
 598{
 599        int ret;
 600
 601        ret = tty_unregister_driver(ipw_tty_driver);
 602        put_tty_driver(ipw_tty_driver);
 603        if (ret != 0)
 604                printk(KERN_ERR IPWIRELESS_PCCARD_NAME
 605                        ": tty_unregister_driver failed with code %d\n", ret);
 606}
 607
 608int ipwireless_tty_is_modem(struct ipw_tty *tty)
 609{
 610        return tty->tty_type == TTYTYPE_MODEM;
 611}
 612
 613void
 614ipwireless_tty_notify_control_line_change(struct ipw_tty *tty,
 615                                          unsigned int channel_idx,
 616                                          unsigned int control_lines,
 617                                          unsigned int changed_mask)
 618{
 619        unsigned int old_control_lines = tty->control_lines;
 620
 621        tty->control_lines = (tty->control_lines & ~changed_mask)
 622                | (control_lines & changed_mask);
 623
 624        /*
 625         * If DCD is de-asserted, we close the tty so pppd can tell that we
 626         * have gone offline.
 627         */
 628        if ((old_control_lines & IPW_CONTROL_LINE_DCD)
 629                        && !(tty->control_lines & IPW_CONTROL_LINE_DCD)
 630                        && tty->port.tty) {
 631                tty_hangup(tty->port.tty);
 632        }
 633}
 634
 635