root/drivers/staging/isdn/gigaset/interface.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. if_lock
  2. if_version
  3. if_config
  4. if_open
  5. if_close
  6. if_ioctl
  7. if_compat_ioctl
  8. if_tiocmget
  9. if_tiocmset
  10. if_write
  11. if_write_room
  12. if_chars_in_buffer
  13. if_throttle
  14. if_unthrottle
  15. if_set_termios
  16. if_wake
  17. gigaset_if_init
  18. gigaset_if_free
  19. gigaset_if_receive
  20. gigaset_if_initdriver
  21. gigaset_if_freedriver

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * interface to user space for the gigaset driver
   4  *
   5  * Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
   6  *
   7  * =====================================================================
   8  * =====================================================================
   9  */
  10 
  11 #include "gigaset.h"
  12 #include <linux/gigaset_dev.h>
  13 #include <linux/tty_flip.h>
  14 #include <linux/module.h>
  15 
  16 /*** our ioctls ***/
  17 
  18 static int if_lock(struct cardstate *cs, int *arg)
  19 {
  20         int cmd = *arg;
  21 
  22         gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
  23 
  24         if (cmd > 1)
  25                 return -EINVAL;
  26 
  27         if (cmd < 0) {
  28                 *arg = cs->mstate == MS_LOCKED;
  29                 return 0;
  30         }
  31 
  32         if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
  33                 cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
  34                 cs->ops->baud_rate(cs, B115200);
  35                 cs->ops->set_line_ctrl(cs, CS8);
  36                 cs->control_state = TIOCM_DTR | TIOCM_RTS;
  37         }
  38 
  39         cs->waiting = 1;
  40         if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
  41                                NULL, cmd, NULL)) {
  42                 cs->waiting = 0;
  43                 return -ENOMEM;
  44         }
  45         gigaset_schedule_event(cs);
  46 
  47         wait_event(cs->waitqueue, !cs->waiting);
  48 
  49         if (cs->cmd_result >= 0) {
  50                 *arg = cs->cmd_result;
  51                 return 0;
  52         }
  53 
  54         return cs->cmd_result;
  55 }
  56 
  57 static int if_version(struct cardstate *cs, unsigned arg[4])
  58 {
  59         static const unsigned version[4] = GIG_VERSION;
  60         static const unsigned compat[4] = GIG_COMPAT;
  61         unsigned cmd = arg[0];
  62 
  63         gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
  64 
  65         switch (cmd) {
  66         case GIGVER_DRIVER:
  67                 memcpy(arg, version, sizeof version);
  68                 return 0;
  69         case GIGVER_COMPAT:
  70                 memcpy(arg, compat, sizeof compat);
  71                 return 0;
  72         case GIGVER_FWBASE:
  73                 cs->waiting = 1;
  74                 if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
  75                                        NULL, 0, arg)) {
  76                         cs->waiting = 0;
  77                         return -ENOMEM;
  78                 }
  79                 gigaset_schedule_event(cs);
  80 
  81                 wait_event(cs->waitqueue, !cs->waiting);
  82 
  83                 if (cs->cmd_result >= 0)
  84                         return 0;
  85 
  86                 return cs->cmd_result;
  87         default:
  88                 return -EINVAL;
  89         }
  90 }
  91 
  92 static int if_config(struct cardstate *cs, int *arg)
  93 {
  94         gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
  95 
  96         if (*arg != 1)
  97                 return -EINVAL;
  98 
  99         if (cs->mstate != MS_LOCKED)
 100                 return -EBUSY;
 101 
 102         if (!cs->connected) {
 103                 pr_err("%s: not connected\n", __func__);
 104                 return -ENODEV;
 105         }
 106 
 107         *arg = 0;
 108         return gigaset_enterconfigmode(cs);
 109 }
 110 
 111 /*** the terminal driver ***/
 112 
 113 static int if_open(struct tty_struct *tty, struct file *filp)
 114 {
 115         struct cardstate *cs;
 116 
 117         gig_dbg(DEBUG_IF, "%d+%d: %s()",
 118                 tty->driver->minor_start, tty->index, __func__);
 119 
 120         cs = gigaset_get_cs_by_tty(tty);
 121         if (!cs || !try_module_get(cs->driver->owner))
 122                 return -ENODEV;
 123 
 124         if (mutex_lock_interruptible(&cs->mutex)) {
 125                 module_put(cs->driver->owner);
 126                 return -ERESTARTSYS;
 127         }
 128         tty->driver_data = cs;
 129 
 130         ++cs->port.count;
 131 
 132         if (cs->port.count == 1) {
 133                 tty_port_tty_set(&cs->port, tty);
 134                 cs->port.low_latency = 1;
 135         }
 136 
 137         mutex_unlock(&cs->mutex);
 138         return 0;
 139 }
 140 
 141 static void if_close(struct tty_struct *tty, struct file *filp)
 142 {
 143         struct cardstate *cs = tty->driver_data;
 144 
 145         if (!cs) { /* happens if we didn't find cs in open */
 146                 gig_dbg(DEBUG_IF, "%s: no cardstate", __func__);
 147                 return;
 148         }
 149 
 150         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 151 
 152         mutex_lock(&cs->mutex);
 153 
 154         if (!cs->connected)
 155                 gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 156         else if (!cs->port.count)
 157                 dev_warn(cs->dev, "%s: device not opened\n", __func__);
 158         else if (!--cs->port.count)
 159                 tty_port_tty_set(&cs->port, NULL);
 160 
 161         mutex_unlock(&cs->mutex);
 162 
 163         module_put(cs->driver->owner);
 164 }
 165 
 166 static int if_ioctl(struct tty_struct *tty,
 167                     unsigned int cmd, unsigned long arg)
 168 {
 169         struct cardstate *cs = tty->driver_data;
 170         int retval = -ENODEV;
 171         int int_arg;
 172         unsigned char buf[6];
 173         unsigned version[4];
 174 
 175         gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
 176 
 177         if (mutex_lock_interruptible(&cs->mutex))
 178                 return -ERESTARTSYS;
 179 
 180         if (!cs->connected) {
 181                 gig_dbg(DEBUG_IF, "not connected");
 182                 retval = -ENODEV;
 183         } else {
 184                 retval = 0;
 185                 switch (cmd) {
 186                 case GIGASET_REDIR:
 187                         retval = get_user(int_arg, (int __user *) arg);
 188                         if (retval >= 0)
 189                                 retval = if_lock(cs, &int_arg);
 190                         if (retval >= 0)
 191                                 retval = put_user(int_arg, (int __user *) arg);
 192                         break;
 193                 case GIGASET_CONFIG:
 194                         retval = get_user(int_arg, (int __user *) arg);
 195                         if (retval >= 0)
 196                                 retval = if_config(cs, &int_arg);
 197                         if (retval >= 0)
 198                                 retval = put_user(int_arg, (int __user *) arg);
 199                         break;
 200                 case GIGASET_BRKCHARS:
 201                         retval = copy_from_user(&buf,
 202                                                 (const unsigned char __user *) arg, 6)
 203                                 ? -EFAULT : 0;
 204                         if (retval >= 0) {
 205                                 gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
 206                                                    6, buf);
 207                                 retval = cs->ops->brkchars(cs, buf);
 208                         }
 209                         break;
 210                 case GIGASET_VERSION:
 211                         retval = copy_from_user(version,
 212                                                 (unsigned __user *) arg, sizeof version)
 213                                 ? -EFAULT : 0;
 214                         if (retval >= 0)
 215                                 retval = if_version(cs, version);
 216                         if (retval >= 0)
 217                                 retval = copy_to_user((unsigned __user *) arg,
 218                                                       version, sizeof version)
 219                                         ? -EFAULT : 0;
 220                         break;
 221                 default:
 222                         gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
 223                                 __func__, cmd);
 224                         retval = -ENOIOCTLCMD;
 225                 }
 226         }
 227 
 228         mutex_unlock(&cs->mutex);
 229 
 230         return retval;
 231 }
 232 
 233 #ifdef CONFIG_COMPAT
 234 static long if_compat_ioctl(struct tty_struct *tty,
 235                     unsigned int cmd, unsigned long arg)
 236 {
 237         return if_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
 238 }
 239 #endif
 240 
 241 static int if_tiocmget(struct tty_struct *tty)
 242 {
 243         struct cardstate *cs = tty->driver_data;
 244         int retval;
 245 
 246         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 247 
 248         if (mutex_lock_interruptible(&cs->mutex))
 249                 return -ERESTARTSYS;
 250 
 251         retval = cs->control_state & (TIOCM_RTS | TIOCM_DTR);
 252 
 253         mutex_unlock(&cs->mutex);
 254 
 255         return retval;
 256 }
 257 
 258 static int if_tiocmset(struct tty_struct *tty,
 259                        unsigned int set, unsigned int clear)
 260 {
 261         struct cardstate *cs = tty->driver_data;
 262         int retval;
 263         unsigned mc;
 264 
 265         gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
 266                 cs->minor_index, __func__, set, clear);
 267 
 268         if (mutex_lock_interruptible(&cs->mutex))
 269                 return -ERESTARTSYS;
 270 
 271         if (!cs->connected) {
 272                 gig_dbg(DEBUG_IF, "not connected");
 273                 retval = -ENODEV;
 274         } else {
 275                 mc = (cs->control_state | set) & ~clear & (TIOCM_RTS | TIOCM_DTR);
 276                 retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
 277                 cs->control_state = mc;
 278         }
 279 
 280         mutex_unlock(&cs->mutex);
 281 
 282         return retval;
 283 }
 284 
 285 static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
 286 {
 287         struct cardstate *cs = tty->driver_data;
 288         struct cmdbuf_t *cb;
 289         int retval;
 290 
 291         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 292 
 293         if (mutex_lock_interruptible(&cs->mutex))
 294                 return -ERESTARTSYS;
 295 
 296         if (!cs->connected) {
 297                 gig_dbg(DEBUG_IF, "not connected");
 298                 retval = -ENODEV;
 299                 goto done;
 300         }
 301         if (cs->mstate != MS_LOCKED) {
 302                 dev_warn(cs->dev, "can't write to unlocked device\n");
 303                 retval = -EBUSY;
 304                 goto done;
 305         }
 306         if (count <= 0) {
 307                 /* nothing to do */
 308                 retval = 0;
 309                 goto done;
 310         }
 311 
 312         cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
 313         if (!cb) {
 314                 dev_err(cs->dev, "%s: out of memory\n", __func__);
 315                 retval = -ENOMEM;
 316                 goto done;
 317         }
 318 
 319         memcpy(cb->buf, buf, count);
 320         cb->len = count;
 321         cb->offset = 0;
 322         cb->next = NULL;
 323         cb->wake_tasklet = &cs->if_wake_tasklet;
 324         retval = cs->ops->write_cmd(cs, cb);
 325 done:
 326         mutex_unlock(&cs->mutex);
 327         return retval;
 328 }
 329 
 330 static int if_write_room(struct tty_struct *tty)
 331 {
 332         struct cardstate *cs = tty->driver_data;
 333         int retval;
 334 
 335         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 336 
 337         if (mutex_lock_interruptible(&cs->mutex))
 338                 return -ERESTARTSYS;
 339 
 340         if (!cs->connected) {
 341                 gig_dbg(DEBUG_IF, "not connected");
 342                 retval = -ENODEV;
 343         } else if (cs->mstate != MS_LOCKED) {
 344                 dev_warn(cs->dev, "can't write to unlocked device\n");
 345                 retval = -EBUSY;
 346         } else
 347                 retval = cs->ops->write_room(cs);
 348 
 349         mutex_unlock(&cs->mutex);
 350 
 351         return retval;
 352 }
 353 
 354 static int if_chars_in_buffer(struct tty_struct *tty)
 355 {
 356         struct cardstate *cs = tty->driver_data;
 357         int retval = 0;
 358 
 359         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 360 
 361         mutex_lock(&cs->mutex);
 362 
 363         if (!cs->connected)
 364                 gig_dbg(DEBUG_IF, "not connected");
 365         else if (cs->mstate != MS_LOCKED)
 366                 dev_warn(cs->dev, "can't write to unlocked device\n");
 367         else
 368                 retval = cs->ops->chars_in_buffer(cs);
 369 
 370         mutex_unlock(&cs->mutex);
 371 
 372         return retval;
 373 }
 374 
 375 static void if_throttle(struct tty_struct *tty)
 376 {
 377         struct cardstate *cs = tty->driver_data;
 378 
 379         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 380 
 381         mutex_lock(&cs->mutex);
 382 
 383         if (!cs->connected)
 384                 gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 385         else
 386                 gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 387 
 388         mutex_unlock(&cs->mutex);
 389 }
 390 
 391 static void if_unthrottle(struct tty_struct *tty)
 392 {
 393         struct cardstate *cs = tty->driver_data;
 394 
 395         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 396 
 397         mutex_lock(&cs->mutex);
 398 
 399         if (!cs->connected)
 400                 gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 401         else
 402                 gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 403 
 404         mutex_unlock(&cs->mutex);
 405 }
 406 
 407 static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
 408 {
 409         struct cardstate *cs = tty->driver_data;
 410         unsigned int iflag;
 411         unsigned int cflag;
 412         unsigned int old_cflag;
 413         unsigned int control_state, new_state;
 414 
 415         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 416 
 417         mutex_lock(&cs->mutex);
 418 
 419         if (!cs->connected) {
 420                 gig_dbg(DEBUG_IF, "not connected");
 421                 goto out;
 422         }
 423 
 424         iflag = tty->termios.c_iflag;
 425         cflag = tty->termios.c_cflag;
 426         old_cflag = old ? old->c_cflag : cflag;
 427         gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
 428                 cs->minor_index, iflag, cflag, old_cflag);
 429 
 430         /* get a local copy of the current port settings */
 431         control_state = cs->control_state;
 432 
 433         /*
 434          * Update baud rate.
 435          * Do not attempt to cache old rates and skip settings,
 436          * disconnects screw such tricks up completely.
 437          * Premature optimization is the root of all evil.
 438          */
 439 
 440         /* reassert DTR and (maybe) RTS on transition from B0 */
 441         if ((old_cflag & CBAUD) == B0) {
 442                 new_state = control_state | TIOCM_DTR;
 443                 /* don't set RTS if using hardware flow control */
 444                 if (!(old_cflag & CRTSCTS))
 445                         new_state |= TIOCM_RTS;
 446                 gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
 447                         cs->minor_index,
 448                         (new_state & TIOCM_RTS) ? " only" : "/RTS");
 449                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
 450                 control_state = new_state;
 451         }
 452 
 453         cs->ops->baud_rate(cs, cflag & CBAUD);
 454 
 455         if ((cflag & CBAUD) == B0) {
 456                 /* Drop RTS and DTR */
 457                 gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
 458                 new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
 459                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
 460                 control_state = new_state;
 461         }
 462 
 463         /*
 464          * Update line control register (LCR)
 465          */
 466 
 467         cs->ops->set_line_ctrl(cs, cflag);
 468 
 469         /* save off the modified port settings */
 470         cs->control_state = control_state;
 471 
 472 out:
 473         mutex_unlock(&cs->mutex);
 474 }
 475 
 476 static const struct tty_operations if_ops = {
 477         .open =                 if_open,
 478         .close =                if_close,
 479         .ioctl =                if_ioctl,
 480 #ifdef CONFIG_COMPAT
 481         .compat_ioctl =         if_compat_ioctl,
 482 #endif
 483         .write =                if_write,
 484         .write_room =           if_write_room,
 485         .chars_in_buffer =      if_chars_in_buffer,
 486         .set_termios =          if_set_termios,
 487         .throttle =             if_throttle,
 488         .unthrottle =           if_unthrottle,
 489         .tiocmget =             if_tiocmget,
 490         .tiocmset =             if_tiocmset,
 491 };
 492 
 493 
 494 /* wakeup tasklet for the write operation */
 495 static void if_wake(unsigned long data)
 496 {
 497         struct cardstate *cs = (struct cardstate *)data;
 498 
 499         tty_port_tty_wakeup(&cs->port);
 500 }
 501 
 502 /*** interface to common ***/
 503 
 504 void gigaset_if_init(struct cardstate *cs)
 505 {
 506         struct gigaset_driver *drv;
 507 
 508         drv = cs->driver;
 509         if (!drv->have_tty)
 510                 return;
 511 
 512         tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
 513 
 514         mutex_lock(&cs->mutex);
 515         cs->tty_dev = tty_port_register_device(&cs->port, drv->tty,
 516                         cs->minor_index, NULL);
 517 
 518         if (!IS_ERR(cs->tty_dev))
 519                 dev_set_drvdata(cs->tty_dev, cs);
 520         else {
 521                 pr_warning("could not register device to the tty subsystem\n");
 522                 cs->tty_dev = NULL;
 523         }
 524         mutex_unlock(&cs->mutex);
 525 }
 526 
 527 void gigaset_if_free(struct cardstate *cs)
 528 {
 529         struct gigaset_driver *drv;
 530 
 531         drv = cs->driver;
 532         if (!drv->have_tty)
 533                 return;
 534 
 535         tasklet_disable(&cs->if_wake_tasklet);
 536         tasklet_kill(&cs->if_wake_tasklet);
 537         cs->tty_dev = NULL;
 538         tty_unregister_device(drv->tty, cs->minor_index);
 539 }
 540 
 541 /**
 542  * gigaset_if_receive() - pass a received block of data to the tty device
 543  * @cs:         device descriptor structure.
 544  * @buffer:     received data.
 545  * @len:        number of bytes received.
 546  *
 547  * Called by asyncdata/isocdata if a block of data received from the
 548  * device must be sent to userspace through the ttyG* device.
 549  */
 550 void gigaset_if_receive(struct cardstate *cs,
 551                         unsigned char *buffer, size_t len)
 552 {
 553         tty_insert_flip_string(&cs->port, buffer, len);
 554         tty_flip_buffer_push(&cs->port);
 555 }
 556 EXPORT_SYMBOL_GPL(gigaset_if_receive);
 557 
 558 /* gigaset_if_initdriver
 559  * Initialize tty interface.
 560  * parameters:
 561  *      drv             Driver
 562  *      procname        Name of the driver (e.g. for /proc/tty/drivers)
 563  *      devname         Name of the device files (prefix without minor number)
 564  */
 565 void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
 566                            const char *devname)
 567 {
 568         int ret;
 569         struct tty_driver *tty;
 570 
 571         drv->have_tty = 0;
 572 
 573         drv->tty = tty = alloc_tty_driver(drv->minors);
 574         if (tty == NULL)
 575                 goto enomem;
 576 
 577         tty->type =             TTY_DRIVER_TYPE_SERIAL;
 578         tty->subtype =          SERIAL_TYPE_NORMAL;
 579         tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 580 
 581         tty->driver_name =      procname;
 582         tty->name =             devname;
 583         tty->minor_start =      drv->minor;
 584 
 585         tty->init_termios          = tty_std_termios;
 586         tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 587         tty_set_operations(tty, &if_ops);
 588 
 589         ret = tty_register_driver(tty);
 590         if (ret < 0) {
 591                 pr_err("error %d registering tty driver\n", ret);
 592                 goto error;
 593         }
 594         gig_dbg(DEBUG_IF, "tty driver initialized");
 595         drv->have_tty = 1;
 596         return;
 597 
 598 enomem:
 599         pr_err("out of memory\n");
 600 error:
 601         if (drv->tty)
 602                 put_tty_driver(drv->tty);
 603 }
 604 
 605 void gigaset_if_freedriver(struct gigaset_driver *drv)
 606 {
 607         if (!drv->have_tty)
 608                 return;
 609 
 610         drv->have_tty = 0;
 611         tty_unregister_driver(drv->tty);
 612         put_tty_driver(drv->tty);
 613 }

/* [<][>][^][v][top][bottom][index][help] */