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