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