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