1 /* 2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 3 * Licensed under the GPL 4 */ 5 6 #include <linux/slab.h> 7 #include <linux/tty.h> 8 #include <linux/tty_flip.h> 9 #include "chan.h" 10 #include <os.h> 11 #include <irq_kern.h> 12 13 #ifdef CONFIG_NOCONFIG_CHAN 14 static void *not_configged_init(char *str, int device, 15 const struct chan_opts *opts) 16 { 17 printk(KERN_ERR "Using a channel type which is configured out of " 18 "UML\n"); 19 return NULL; 20 } 21 22 static int not_configged_open(int input, int output, int primary, void *data, 23 char **dev_out) 24 { 25 printk(KERN_ERR "Using a channel type which is configured out of " 26 "UML\n"); 27 return -ENODEV; 28 } 29 30 static void not_configged_close(int fd, void *data) 31 { 32 printk(KERN_ERR "Using a channel type which is configured out of " 33 "UML\n"); 34 } 35 36 static int not_configged_read(int fd, char *c_out, void *data) 37 { 38 printk(KERN_ERR "Using a channel type which is configured out of " 39 "UML\n"); 40 return -EIO; 41 } 42 43 static int not_configged_write(int fd, const char *buf, int len, void *data) 44 { 45 printk(KERN_ERR "Using a channel type which is configured out of " 46 "UML\n"); 47 return -EIO; 48 } 49 50 static int not_configged_console_write(int fd, const char *buf, int len) 51 { 52 printk(KERN_ERR "Using a channel type which is configured out of " 53 "UML\n"); 54 return -EIO; 55 } 56 57 static int not_configged_window_size(int fd, void *data, unsigned short *rows, 58 unsigned short *cols) 59 { 60 printk(KERN_ERR "Using a channel type which is configured out of " 61 "UML\n"); 62 return -ENODEV; 63 } 64 65 static void not_configged_free(void *data) 66 { 67 printk(KERN_ERR "Using a channel type which is configured out of " 68 "UML\n"); 69 } 70 71 static const struct chan_ops not_configged_ops = { 72 .init = not_configged_init, 73 .open = not_configged_open, 74 .close = not_configged_close, 75 .read = not_configged_read, 76 .write = not_configged_write, 77 .console_write = not_configged_console_write, 78 .window_size = not_configged_window_size, 79 .free = not_configged_free, 80 .winch = 0, 81 }; 82 #endif /* CONFIG_NOCONFIG_CHAN */ 83 84 static int open_one_chan(struct chan *chan) 85 { 86 int fd, err; 87 88 if (chan->opened) 89 return 0; 90 91 if (chan->ops->open == NULL) 92 fd = 0; 93 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary, 94 chan->data, &chan->dev); 95 if (fd < 0) 96 return fd; 97 98 err = os_set_fd_block(fd, 0); 99 if (err) { 100 (*chan->ops->close)(fd, chan->data); 101 return err; 102 } 103 104 chan->fd = fd; 105 106 chan->opened = 1; 107 return 0; 108 } 109 110 static int open_chan(struct list_head *chans) 111 { 112 struct list_head *ele; 113 struct chan *chan; 114 int ret, err = 0; 115 116 list_for_each(ele, chans) { 117 chan = list_entry(ele, struct chan, list); 118 ret = open_one_chan(chan); 119 if (chan->primary) 120 err = ret; 121 } 122 return err; 123 } 124 125 void chan_enable_winch(struct chan *chan, struct tty_port *port) 126 { 127 if (chan && chan->primary && chan->ops->winch) 128 register_winch(chan->fd, port); 129 } 130 131 static void line_timer_cb(struct work_struct *work) 132 { 133 struct line *line = container_of(work, struct line, task.work); 134 135 if (!line->throttled) 136 chan_interrupt(line, line->driver->read_irq); 137 } 138 139 int enable_chan(struct line *line) 140 { 141 struct list_head *ele; 142 struct chan *chan; 143 int err; 144 145 INIT_DELAYED_WORK(&line->task, line_timer_cb); 146 147 list_for_each(ele, &line->chan_list) { 148 chan = list_entry(ele, struct chan, list); 149 err = open_one_chan(chan); 150 if (err) { 151 if (chan->primary) 152 goto out_close; 153 154 continue; 155 } 156 157 if (chan->enabled) 158 continue; 159 err = line_setup_irq(chan->fd, chan->input, chan->output, line, 160 chan); 161 if (err) 162 goto out_close; 163 164 chan->enabled = 1; 165 } 166 167 return 0; 168 169 out_close: 170 close_chan(line); 171 return err; 172 } 173 174 static void close_one_chan(struct chan *chan, int delay_free_irq) 175 { 176 if (!chan->opened) 177 return; 178 179 /* we can safely call free now - it will be marked 180 * as free and freed once the IRQ stopped processing 181 */ 182 if (chan->input && chan->enabled) 183 um_free_irq(chan->line->driver->read_irq, chan); 184 if (chan->output && chan->enabled) 185 um_free_irq(chan->line->driver->write_irq, chan); 186 chan->enabled = 0; 187 if (chan->ops->close != NULL) 188 (*chan->ops->close)(chan->fd, chan->data); 189 190 chan->opened = 0; 191 chan->fd = -1; 192 } 193 194 void close_chan(struct line *line) 195 { 196 struct chan *chan; 197 198 /* Close in reverse order as open in case more than one of them 199 * refers to the same device and they save and restore that device's 200 * state. Then, the first one opened will have the original state, 201 * so it must be the last closed. 202 */ 203 list_for_each_entry_reverse(chan, &line->chan_list, list) { 204 close_one_chan(chan, 0); 205 } 206 } 207 208 void deactivate_chan(struct chan *chan, int irq) 209 { 210 if (chan && chan->enabled) 211 deactivate_fd(chan->fd, irq); 212 } 213 214 void reactivate_chan(struct chan *chan, int irq) 215 { 216 if (chan && chan->enabled) 217 reactivate_fd(chan->fd, irq); 218 } 219 220 int write_chan(struct chan *chan, const char *buf, int len, 221 int write_irq) 222 { 223 int n, ret = 0; 224 225 if (len == 0 || !chan || !chan->ops->write) 226 return 0; 227 228 n = chan->ops->write(chan->fd, buf, len, chan->data); 229 if (chan->primary) { 230 ret = n; 231 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len))) 232 reactivate_fd(chan->fd, write_irq); 233 } 234 return ret; 235 } 236 237 int console_write_chan(struct chan *chan, const char *buf, int len) 238 { 239 int n, ret = 0; 240 241 if (!chan || !chan->ops->console_write) 242 return 0; 243 244 n = chan->ops->console_write(chan->fd, buf, len); 245 if (chan->primary) 246 ret = n; 247 return ret; 248 } 249 250 int console_open_chan(struct line *line, struct console *co) 251 { 252 int err; 253 254 err = open_chan(&line->chan_list); 255 if (err) 256 return err; 257 258 printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name, 259 co->index); 260 return 0; 261 } 262 263 int chan_window_size(struct line *line, unsigned short *rows_out, 264 unsigned short *cols_out) 265 { 266 struct chan *chan; 267 268 chan = line->chan_in; 269 if (chan && chan->primary) { 270 if (chan->ops->window_size == NULL) 271 return 0; 272 return chan->ops->window_size(chan->fd, chan->data, 273 rows_out, cols_out); 274 } 275 chan = line->chan_out; 276 if (chan && chan->primary) { 277 if (chan->ops->window_size == NULL) 278 return 0; 279 return chan->ops->window_size(chan->fd, chan->data, 280 rows_out, cols_out); 281 } 282 return 0; 283 } 284 285 static void free_one_chan(struct chan *chan) 286 { 287 list_del(&chan->list); 288 289 close_one_chan(chan, 0); 290 291 if (chan->ops->free != NULL) 292 (*chan->ops->free)(chan->data); 293 294 if (chan->primary && chan->output) 295 ignore_sigio_fd(chan->fd); 296 kfree(chan); 297 } 298 299 static void free_chan(struct list_head *chans) 300 { 301 struct list_head *ele, *next; 302 struct chan *chan; 303 304 list_for_each_safe(ele, next, chans) { 305 chan = list_entry(ele, struct chan, list); 306 free_one_chan(chan); 307 } 308 } 309 310 static int one_chan_config_string(struct chan *chan, char *str, int size, 311 char **error_out) 312 { 313 int n = 0; 314 315 if (chan == NULL) { 316 CONFIG_CHUNK(str, size, n, "none", 1); 317 return n; 318 } 319 320 CONFIG_CHUNK(str, size, n, chan->ops->type, 0); 321 322 if (chan->dev == NULL) { 323 CONFIG_CHUNK(str, size, n, "", 1); 324 return n; 325 } 326 327 CONFIG_CHUNK(str, size, n, ":", 0); 328 CONFIG_CHUNK(str, size, n, chan->dev, 0); 329 330 return n; 331 } 332 333 static int chan_pair_config_string(struct chan *in, struct chan *out, 334 char *str, int size, char **error_out) 335 { 336 int n; 337 338 n = one_chan_config_string(in, str, size, error_out); 339 str += n; 340 size -= n; 341 342 if (in == out) { 343 CONFIG_CHUNK(str, size, n, "", 1); 344 return n; 345 } 346 347 CONFIG_CHUNK(str, size, n, ",", 1); 348 n = one_chan_config_string(out, str, size, error_out); 349 str += n; 350 size -= n; 351 CONFIG_CHUNK(str, size, n, "", 1); 352 353 return n; 354 } 355 356 int chan_config_string(struct line *line, char *str, int size, 357 char **error_out) 358 { 359 struct chan *in = line->chan_in, *out = line->chan_out; 360 361 if (in && !in->primary) 362 in = NULL; 363 if (out && !out->primary) 364 out = NULL; 365 366 return chan_pair_config_string(in, out, str, size, error_out); 367 } 368 369 struct chan_type { 370 char *key; 371 const struct chan_ops *ops; 372 }; 373 374 static const struct chan_type chan_table[] = { 375 { "fd", &fd_ops }, 376 377 #ifdef CONFIG_NULL_CHAN 378 { "null", &null_ops }, 379 #else 380 { "null", ¬_configged_ops }, 381 #endif 382 383 #ifdef CONFIG_PORT_CHAN 384 { "port", &port_ops }, 385 #else 386 { "port", ¬_configged_ops }, 387 #endif 388 389 #ifdef CONFIG_PTY_CHAN 390 { "pty", &pty_ops }, 391 { "pts", &pts_ops }, 392 #else 393 { "pty", ¬_configged_ops }, 394 { "pts", ¬_configged_ops }, 395 #endif 396 397 #ifdef CONFIG_TTY_CHAN 398 { "tty", &tty_ops }, 399 #else 400 { "tty", ¬_configged_ops }, 401 #endif 402 403 #ifdef CONFIG_XTERM_CHAN 404 { "xterm", &xterm_ops }, 405 #else 406 { "xterm", ¬_configged_ops }, 407 #endif 408 }; 409 410 static struct chan *parse_chan(struct line *line, char *str, int device, 411 const struct chan_opts *opts, char **error_out) 412 { 413 const struct chan_type *entry; 414 const struct chan_ops *ops; 415 struct chan *chan; 416 void *data; 417 int i; 418 419 ops = NULL; 420 data = NULL; 421 for(i = 0; i < ARRAY_SIZE(chan_table); i++) { 422 entry = &chan_table[i]; 423 if (!strncmp(str, entry->key, strlen(entry->key))) { 424 ops = entry->ops; 425 str += strlen(entry->key); 426 break; 427 } 428 } 429 if (ops == NULL) { 430 *error_out = "No match for configured backends"; 431 return NULL; 432 } 433 434 data = (*ops->init)(str, device, opts); 435 if (data == NULL) { 436 *error_out = "Configuration failed"; 437 return NULL; 438 } 439 440 chan = kmalloc(sizeof(*chan), GFP_ATOMIC); 441 if (chan == NULL) { 442 *error_out = "Memory allocation failed"; 443 return NULL; 444 } 445 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list), 446 .free_list = 447 LIST_HEAD_INIT(chan->free_list), 448 .line = line, 449 .primary = 1, 450 .input = 0, 451 .output = 0, 452 .opened = 0, 453 .enabled = 0, 454 .fd = -1, 455 .ops = ops, 456 .data = data }); 457 return chan; 458 } 459 460 int parse_chan_pair(char *str, struct line *line, int device, 461 const struct chan_opts *opts, char **error_out) 462 { 463 struct list_head *chans = &line->chan_list; 464 struct chan *new; 465 char *in, *out; 466 467 if (!list_empty(chans)) { 468 line->chan_in = line->chan_out = NULL; 469 free_chan(chans); 470 INIT_LIST_HEAD(chans); 471 } 472 473 if (!str) 474 return 0; 475 476 out = strchr(str, ','); 477 if (out != NULL) { 478 in = str; 479 *out = '\0'; 480 out++; 481 new = parse_chan(line, in, device, opts, error_out); 482 if (new == NULL) 483 return -1; 484 485 new->input = 1; 486 list_add(&new->list, chans); 487 line->chan_in = new; 488 489 new = parse_chan(line, out, device, opts, error_out); 490 if (new == NULL) 491 return -1; 492 493 list_add(&new->list, chans); 494 new->output = 1; 495 line->chan_out = new; 496 } 497 else { 498 new = parse_chan(line, str, device, opts, error_out); 499 if (new == NULL) 500 return -1; 501 502 list_add(&new->list, chans); 503 new->input = 1; 504 new->output = 1; 505 line->chan_in = line->chan_out = new; 506 } 507 return 0; 508 } 509 510 void chan_interrupt(struct line *line, int irq) 511 { 512 struct tty_port *port = &line->port; 513 struct chan *chan = line->chan_in; 514 int err; 515 char c; 516 517 if (!chan || !chan->ops->read) 518 goto out; 519 520 do { 521 if (!tty_buffer_request_room(port, 1)) { 522 schedule_delayed_work(&line->task, 1); 523 goto out; 524 } 525 err = chan->ops->read(chan->fd, &c, chan->data); 526 if (err > 0) 527 tty_insert_flip_char(port, c, TTY_NORMAL); 528 } while (err > 0); 529 530 if (err == 0) 531 reactivate_fd(chan->fd, irq); 532 if (err == -EIO) { 533 if (chan->primary) { 534 tty_port_tty_hangup(&line->port, false); 535 if (line->chan_out != chan) 536 close_one_chan(line->chan_out, 1); 537 } 538 close_one_chan(chan, 1); 539 if (chan->primary) 540 return; 541 } 542 out: 543 tty_flip_buffer_push(port); 544 } 545