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