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