18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/irqreturn.h> 78c2ecf20Sopenharmony_ci#include <linux/kd.h> 88c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "chan.h" 128c2ecf20Sopenharmony_ci#include <irq_kern.h> 138c2ecf20Sopenharmony_ci#include <irq_user.h> 148c2ecf20Sopenharmony_ci#include <kern_util.h> 158c2ecf20Sopenharmony_ci#include <os.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define LINE_BUFSIZE 4096 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic irqreturn_t line_interrupt(int irq, void *data) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci struct chan *chan = data; 228c2ecf20Sopenharmony_ci struct line *line = chan->line; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (line) 258c2ecf20Sopenharmony_ci chan_interrupt(line, irq); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci return IRQ_HANDLED; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * Returns the free space inside the ring buffer of this line. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Should be called while holding line->lock (this does not modify data). 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic int write_room(struct line *line) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci int n; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (line->buffer == NULL) 408c2ecf20Sopenharmony_ci return LINE_BUFSIZE - 1; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* This is for the case where the buffer is wrapped! */ 438c2ecf20Sopenharmony_ci n = line->head - line->tail; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (n <= 0) 468c2ecf20Sopenharmony_ci n += LINE_BUFSIZE; /* The other case */ 478c2ecf20Sopenharmony_ci return n - 1; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciint line_write_room(struct tty_struct *tty) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 538c2ecf20Sopenharmony_ci unsigned long flags; 548c2ecf20Sopenharmony_ci int room; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci spin_lock_irqsave(&line->lock, flags); 578c2ecf20Sopenharmony_ci room = write_room(line); 588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&line->lock, flags); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return room; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint line_chars_in_buffer(struct tty_struct *tty) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 668c2ecf20Sopenharmony_ci unsigned long flags; 678c2ecf20Sopenharmony_ci int ret; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci spin_lock_irqsave(&line->lock, flags); 708c2ecf20Sopenharmony_ci /* write_room subtracts 1 for the needed NULL, so we readd it.*/ 718c2ecf20Sopenharmony_ci ret = LINE_BUFSIZE - (write_room(line) + 1); 728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&line->lock, flags); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return ret; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * This copies the content of buf into the circular buffer associated with 798c2ecf20Sopenharmony_ci * this line. 808c2ecf20Sopenharmony_ci * The return value is the number of characters actually copied, i.e. the ones 818c2ecf20Sopenharmony_ci * for which there was space: this function is not supposed to ever flush out 828c2ecf20Sopenharmony_ci * the circular buffer. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Must be called while holding line->lock! 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic int buffer_data(struct line *line, const char *buf, int len) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int end, room; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (line->buffer == NULL) { 918c2ecf20Sopenharmony_ci line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC); 928c2ecf20Sopenharmony_ci if (line->buffer == NULL) { 938c2ecf20Sopenharmony_ci printk(KERN_ERR "buffer_data - atomic allocation " 948c2ecf20Sopenharmony_ci "failed\n"); 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci line->head = line->buffer; 988c2ecf20Sopenharmony_ci line->tail = line->buffer; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci room = write_room(line); 1028c2ecf20Sopenharmony_ci len = (len > room) ? room : len; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci end = line->buffer + LINE_BUFSIZE - line->tail; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (len < end) { 1078c2ecf20Sopenharmony_ci memcpy(line->tail, buf, len); 1088c2ecf20Sopenharmony_ci line->tail += len; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci else { 1118c2ecf20Sopenharmony_ci /* The circular buffer is wrapping */ 1128c2ecf20Sopenharmony_ci memcpy(line->tail, buf, end); 1138c2ecf20Sopenharmony_ci buf += end; 1148c2ecf20Sopenharmony_ci memcpy(line->buffer, buf, len - end); 1158c2ecf20Sopenharmony_ci line->tail = line->buffer + len - end; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return len; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * Flushes the ring buffer to the output channels. That is, write_chan is 1238c2ecf20Sopenharmony_ci * called, passing it line->head as buffer, and an appropriate count. 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * On exit, returns 1 when the buffer is empty, 1268c2ecf20Sopenharmony_ci * 0 when the buffer is not empty on exit, 1278c2ecf20Sopenharmony_ci * and -errno when an error occurred. 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Must be called while holding line->lock!*/ 1308c2ecf20Sopenharmony_cistatic int flush_buffer(struct line *line) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int n, count; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if ((line->buffer == NULL) || (line->head == line->tail)) 1358c2ecf20Sopenharmony_ci return 1; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (line->tail < line->head) { 1388c2ecf20Sopenharmony_ci /* line->buffer + LINE_BUFSIZE is the end of the buffer! */ 1398c2ecf20Sopenharmony_ci count = line->buffer + LINE_BUFSIZE - line->head; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci n = write_chan(line->chan_out, line->head, count, 1428c2ecf20Sopenharmony_ci line->driver->write_irq); 1438c2ecf20Sopenharmony_ci if (n < 0) 1448c2ecf20Sopenharmony_ci return n; 1458c2ecf20Sopenharmony_ci if (n == count) { 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * We have flushed from ->head to buffer end, now we 1488c2ecf20Sopenharmony_ci * must flush only from the beginning to ->tail. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci line->head = line->buffer; 1518c2ecf20Sopenharmony_ci } else { 1528c2ecf20Sopenharmony_ci line->head += n; 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci count = line->tail - line->head; 1588c2ecf20Sopenharmony_ci n = write_chan(line->chan_out, line->head, count, 1598c2ecf20Sopenharmony_ci line->driver->write_irq); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (n < 0) 1628c2ecf20Sopenharmony_ci return n; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci line->head += n; 1658c2ecf20Sopenharmony_ci return line->head == line->tail; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_civoid line_flush_buffer(struct tty_struct *tty) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 1718c2ecf20Sopenharmony_ci unsigned long flags; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci spin_lock_irqsave(&line->lock, flags); 1748c2ecf20Sopenharmony_ci flush_buffer(line); 1758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&line->lock, flags); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * We map both ->flush_chars and ->put_char (which go in pair) onto 1808c2ecf20Sopenharmony_ci * ->flush_buffer and ->write. Hope it's not that bad. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_civoid line_flush_chars(struct tty_struct *tty) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci line_flush_buffer(tty); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciint line_write(struct tty_struct *tty, const unsigned char *buf, int len) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 1908c2ecf20Sopenharmony_ci unsigned long flags; 1918c2ecf20Sopenharmony_ci int n, ret = 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci spin_lock_irqsave(&line->lock, flags); 1948c2ecf20Sopenharmony_ci if (line->head != line->tail) 1958c2ecf20Sopenharmony_ci ret = buffer_data(line, buf, len); 1968c2ecf20Sopenharmony_ci else { 1978c2ecf20Sopenharmony_ci n = write_chan(line->chan_out, buf, len, 1988c2ecf20Sopenharmony_ci line->driver->write_irq); 1998c2ecf20Sopenharmony_ci if (n < 0) { 2008c2ecf20Sopenharmony_ci ret = n; 2018c2ecf20Sopenharmony_ci goto out_up; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci len -= n; 2058c2ecf20Sopenharmony_ci ret += n; 2068c2ecf20Sopenharmony_ci if (len > 0) 2078c2ecf20Sopenharmony_ci ret += buffer_data(line, buf + n, len); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ciout_up: 2108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&line->lock, flags); 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_civoid line_set_termios(struct tty_struct *tty, struct ktermios * old) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci /* nothing */ 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_civoid line_throttle(struct tty_struct *tty) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci deactivate_chan(line->chan_in, line->driver->read_irq); 2248c2ecf20Sopenharmony_ci line->throttled = 1; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid line_unthrottle(struct tty_struct *tty) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci line->throttled = 0; 2328c2ecf20Sopenharmony_ci chan_interrupt(line, line->driver->read_irq); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic irqreturn_t line_write_interrupt(int irq, void *data) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct chan *chan = data; 2388c2ecf20Sopenharmony_ci struct line *line = chan->line; 2398c2ecf20Sopenharmony_ci int err; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* 2428c2ecf20Sopenharmony_ci * Interrupts are disabled here because genirq keep irqs disabled when 2438c2ecf20Sopenharmony_ci * calling the action handler. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci spin_lock(&line->lock); 2478c2ecf20Sopenharmony_ci err = flush_buffer(line); 2488c2ecf20Sopenharmony_ci if (err == 0) { 2498c2ecf20Sopenharmony_ci spin_unlock(&line->lock); 2508c2ecf20Sopenharmony_ci return IRQ_NONE; 2518c2ecf20Sopenharmony_ci } else if ((err < 0) && (err != -EAGAIN)) { 2528c2ecf20Sopenharmony_ci line->head = line->buffer; 2538c2ecf20Sopenharmony_ci line->tail = line->buffer; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci spin_unlock(&line->lock); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci tty_port_tty_wakeup(&line->port); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciint line_setup_irq(int fd, int input, int output, struct line *line, void *data) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci const struct line_driver *driver = line->driver; 2658c2ecf20Sopenharmony_ci int err = 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (input) 2688c2ecf20Sopenharmony_ci err = um_request_irq(driver->read_irq, fd, IRQ_READ, 2698c2ecf20Sopenharmony_ci line_interrupt, IRQF_SHARED, 2708c2ecf20Sopenharmony_ci driver->read_irq_name, data); 2718c2ecf20Sopenharmony_ci if (err) 2728c2ecf20Sopenharmony_ci return err; 2738c2ecf20Sopenharmony_ci if (output) 2748c2ecf20Sopenharmony_ci err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, 2758c2ecf20Sopenharmony_ci line_write_interrupt, IRQF_SHARED, 2768c2ecf20Sopenharmony_ci driver->write_irq_name, data); 2778c2ecf20Sopenharmony_ci return err; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int line_activate(struct tty_port *port, struct tty_struct *tty) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci int ret; 2838c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = enable_chan(line); 2868c2ecf20Sopenharmony_ci if (ret) 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (!line->sigio) { 2908c2ecf20Sopenharmony_ci chan_enable_winch(line->chan_out, port); 2918c2ecf20Sopenharmony_ci line->sigio = 1; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci chan_window_size(line, &tty->winsize.ws_row, 2958c2ecf20Sopenharmony_ci &tty->winsize.ws_col); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void unregister_winch(struct tty_struct *tty); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void line_destruct(struct tty_port *port) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct tty_struct *tty = tty_port_tty_get(port); 3058c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (line->sigio) { 3088c2ecf20Sopenharmony_ci unregister_winch(tty); 3098c2ecf20Sopenharmony_ci line->sigio = 0; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic const struct tty_port_operations line_port_ops = { 3148c2ecf20Sopenharmony_ci .activate = line_activate, 3158c2ecf20Sopenharmony_ci .destruct = line_destruct, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ciint line_open(struct tty_struct *tty, struct file *filp) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return tty_port_open(&line->port, tty, filp); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciint line_install(struct tty_driver *driver, struct tty_struct *tty, 3268c2ecf20Sopenharmony_ci struct line *line) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci int ret; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ret = tty_standard_install(driver, tty); 3318c2ecf20Sopenharmony_ci if (ret) 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci tty->driver_data = line; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_civoid line_close(struct tty_struct *tty, struct file * filp) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci tty_port_close(&line->port, tty, filp); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_civoid line_hangup(struct tty_struct *tty) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct line *line = tty->driver_data; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci tty_port_hangup(&line->port); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_civoid close_lines(struct line *lines, int nlines) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci int i; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci for(i = 0; i < nlines; i++) 3588c2ecf20Sopenharmony_ci close_chan(&lines[i]); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ciint setup_one_line(struct line *lines, int n, char *init, 3628c2ecf20Sopenharmony_ci const struct chan_opts *opts, char **error_out) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct line *line = &lines[n]; 3658c2ecf20Sopenharmony_ci struct tty_driver *driver = line->driver->driver; 3668c2ecf20Sopenharmony_ci int err = -EINVAL; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (line->port.count) { 3698c2ecf20Sopenharmony_ci *error_out = "Device is already open"; 3708c2ecf20Sopenharmony_ci goto out; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (!strcmp(init, "none")) { 3748c2ecf20Sopenharmony_ci if (line->valid) { 3758c2ecf20Sopenharmony_ci line->valid = 0; 3768c2ecf20Sopenharmony_ci kfree(line->init_str); 3778c2ecf20Sopenharmony_ci tty_unregister_device(driver, n); 3788c2ecf20Sopenharmony_ci parse_chan_pair(NULL, line, n, opts, error_out); 3798c2ecf20Sopenharmony_ci err = 0; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci } else { 3828c2ecf20Sopenharmony_ci char *new = kstrdup(init, GFP_KERNEL); 3838c2ecf20Sopenharmony_ci if (!new) { 3848c2ecf20Sopenharmony_ci *error_out = "Failed to allocate memory"; 3858c2ecf20Sopenharmony_ci return -ENOMEM; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci if (line->valid) { 3888c2ecf20Sopenharmony_ci tty_unregister_device(driver, n); 3898c2ecf20Sopenharmony_ci kfree(line->init_str); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci line->init_str = new; 3928c2ecf20Sopenharmony_ci line->valid = 1; 3938c2ecf20Sopenharmony_ci err = parse_chan_pair(new, line, n, opts, error_out); 3948c2ecf20Sopenharmony_ci if (!err) { 3958c2ecf20Sopenharmony_ci struct device *d = tty_port_register_device(&line->port, 3968c2ecf20Sopenharmony_ci driver, n, NULL); 3978c2ecf20Sopenharmony_ci if (IS_ERR(d)) { 3988c2ecf20Sopenharmony_ci *error_out = "Failed to register device"; 3998c2ecf20Sopenharmony_ci err = PTR_ERR(d); 4008c2ecf20Sopenharmony_ci parse_chan_pair(NULL, line, n, opts, error_out); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci if (err) { 4048c2ecf20Sopenharmony_ci line->init_str = NULL; 4058c2ecf20Sopenharmony_ci line->valid = 0; 4068c2ecf20Sopenharmony_ci kfree(new); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ciout: 4108c2ecf20Sopenharmony_ci return err; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* 4148c2ecf20Sopenharmony_ci * Common setup code for both startup command line and mconsole initialization. 4158c2ecf20Sopenharmony_ci * @lines contains the array (of size @num) to modify; 4168c2ecf20Sopenharmony_ci * @init is the setup string; 4178c2ecf20Sopenharmony_ci * @error_out is an error string in the case of failure; 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ciint line_setup(char **conf, unsigned int num, char **def, 4218c2ecf20Sopenharmony_ci char *init, char *name) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci char *error; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (*init == '=') { 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * We said con=/ssl= instead of con#=, so we are configuring all 4288c2ecf20Sopenharmony_ci * consoles at once. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci *def = init + 1; 4318c2ecf20Sopenharmony_ci } else { 4328c2ecf20Sopenharmony_ci char *end; 4338c2ecf20Sopenharmony_ci unsigned n = simple_strtoul(init, &end, 0); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (*end != '=') { 4368c2ecf20Sopenharmony_ci error = "Couldn't parse device number"; 4378c2ecf20Sopenharmony_ci goto out; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci if (n >= num) { 4408c2ecf20Sopenharmony_ci error = "Device number out of range"; 4418c2ecf20Sopenharmony_ci goto out; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci conf[n] = end + 1; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ciout: 4488c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to set up %s with " 4498c2ecf20Sopenharmony_ci "configuration string \"%s\" : %s\n", name, init, error); 4508c2ecf20Sopenharmony_ci return -EINVAL; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciint line_config(struct line *lines, unsigned int num, char *str, 4548c2ecf20Sopenharmony_ci const struct chan_opts *opts, char **error_out) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci char *end; 4578c2ecf20Sopenharmony_ci int n; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (*str == '=') { 4608c2ecf20Sopenharmony_ci *error_out = "Can't configure all devices from mconsole"; 4618c2ecf20Sopenharmony_ci return -EINVAL; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci n = simple_strtoul(str, &end, 0); 4658c2ecf20Sopenharmony_ci if (*end++ != '=') { 4668c2ecf20Sopenharmony_ci *error_out = "Couldn't parse device number"; 4678c2ecf20Sopenharmony_ci return -EINVAL; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci if (n >= num) { 4708c2ecf20Sopenharmony_ci *error_out = "Device number out of range"; 4718c2ecf20Sopenharmony_ci return -EINVAL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return setup_one_line(lines, n, end, opts, error_out); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ciint line_get_config(char *name, struct line *lines, unsigned int num, char *str, 4788c2ecf20Sopenharmony_ci int size, char **error_out) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct line *line; 4818c2ecf20Sopenharmony_ci char *end; 4828c2ecf20Sopenharmony_ci int dev, n = 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dev = simple_strtoul(name, &end, 0); 4858c2ecf20Sopenharmony_ci if ((*end != '\0') || (end == name)) { 4868c2ecf20Sopenharmony_ci *error_out = "line_get_config failed to parse device number"; 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if ((dev < 0) || (dev >= num)) { 4918c2ecf20Sopenharmony_ci *error_out = "device number out of range"; 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci line = &lines[dev]; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!line->valid) 4988c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, "none", 1); 4998c2ecf20Sopenharmony_ci else { 5008c2ecf20Sopenharmony_ci struct tty_struct *tty = tty_port_tty_get(&line->port); 5018c2ecf20Sopenharmony_ci if (tty == NULL) { 5028c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, line->init_str, 1); 5038c2ecf20Sopenharmony_ci } else { 5048c2ecf20Sopenharmony_ci n = chan_config_string(line, str, size, error_out); 5058c2ecf20Sopenharmony_ci tty_kref_put(tty); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return n; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ciint line_id(char **str, int *start_out, int *end_out) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci char *end; 5158c2ecf20Sopenharmony_ci int n; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci n = simple_strtoul(*str, &end, 0); 5188c2ecf20Sopenharmony_ci if ((*end != '\0') || (end == *str)) 5198c2ecf20Sopenharmony_ci return -1; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci *str = end; 5228c2ecf20Sopenharmony_ci *start_out = n; 5238c2ecf20Sopenharmony_ci *end_out = n; 5248c2ecf20Sopenharmony_ci return n; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciint line_remove(struct line *lines, unsigned int num, int n, char **error_out) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci if (n >= num) { 5308c2ecf20Sopenharmony_ci *error_out = "Device number out of range"; 5318c2ecf20Sopenharmony_ci return -EINVAL; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci return setup_one_line(lines, n, "none", NULL, error_out); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ciint register_lines(struct line_driver *line_driver, 5378c2ecf20Sopenharmony_ci const struct tty_operations *ops, 5388c2ecf20Sopenharmony_ci struct line *lines, int nlines) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct tty_driver *driver = alloc_tty_driver(nlines); 5418c2ecf20Sopenharmony_ci int err; 5428c2ecf20Sopenharmony_ci int i; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (!driver) 5458c2ecf20Sopenharmony_ci return -ENOMEM; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci driver->driver_name = line_driver->name; 5488c2ecf20Sopenharmony_ci driver->name = line_driver->device_name; 5498c2ecf20Sopenharmony_ci driver->major = line_driver->major; 5508c2ecf20Sopenharmony_ci driver->minor_start = line_driver->minor_start; 5518c2ecf20Sopenharmony_ci driver->type = line_driver->type; 5528c2ecf20Sopenharmony_ci driver->subtype = line_driver->subtype; 5538c2ecf20Sopenharmony_ci driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 5548c2ecf20Sopenharmony_ci driver->init_termios = tty_std_termios; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for (i = 0; i < nlines; i++) { 5578c2ecf20Sopenharmony_ci tty_port_init(&lines[i].port); 5588c2ecf20Sopenharmony_ci lines[i].port.ops = &line_port_ops; 5598c2ecf20Sopenharmony_ci spin_lock_init(&lines[i].lock); 5608c2ecf20Sopenharmony_ci lines[i].driver = line_driver; 5618c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lines[i].chan_list); 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci tty_set_operations(driver, ops); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci err = tty_register_driver(driver); 5668c2ecf20Sopenharmony_ci if (err) { 5678c2ecf20Sopenharmony_ci printk(KERN_ERR "register_lines : can't register %s driver\n", 5688c2ecf20Sopenharmony_ci line_driver->name); 5698c2ecf20Sopenharmony_ci put_tty_driver(driver); 5708c2ecf20Sopenharmony_ci for (i = 0; i < nlines; i++) 5718c2ecf20Sopenharmony_ci tty_port_destroy(&lines[i].port); 5728c2ecf20Sopenharmony_ci return err; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci line_driver->driver = driver; 5768c2ecf20Sopenharmony_ci mconsole_register_dev(&line_driver->mc); 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(winch_handler_lock); 5818c2ecf20Sopenharmony_cistatic LIST_HEAD(winch_handlers); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistruct winch { 5848c2ecf20Sopenharmony_ci struct list_head list; 5858c2ecf20Sopenharmony_ci int fd; 5868c2ecf20Sopenharmony_ci int tty_fd; 5878c2ecf20Sopenharmony_ci int pid; 5888c2ecf20Sopenharmony_ci struct tty_port *port; 5898c2ecf20Sopenharmony_ci unsigned long stack; 5908c2ecf20Sopenharmony_ci struct work_struct work; 5918c2ecf20Sopenharmony_ci}; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic void __free_winch(struct work_struct *work) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct winch *winch = container_of(work, struct winch, work); 5968c2ecf20Sopenharmony_ci um_free_irq(WINCH_IRQ, winch); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (winch->pid != -1) 5998c2ecf20Sopenharmony_ci os_kill_process(winch->pid, 1); 6008c2ecf20Sopenharmony_ci if (winch->stack != 0) 6018c2ecf20Sopenharmony_ci free_stack(winch->stack, 0); 6028c2ecf20Sopenharmony_ci kfree(winch); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic void free_winch(struct winch *winch) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci int fd = winch->fd; 6088c2ecf20Sopenharmony_ci winch->fd = -1; 6098c2ecf20Sopenharmony_ci if (fd != -1) 6108c2ecf20Sopenharmony_ci os_close_file(fd); 6118c2ecf20Sopenharmony_ci list_del(&winch->list); 6128c2ecf20Sopenharmony_ci __free_winch(&winch->work); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic irqreturn_t winch_interrupt(int irq, void *data) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct winch *winch = data; 6188c2ecf20Sopenharmony_ci struct tty_struct *tty; 6198c2ecf20Sopenharmony_ci struct line *line; 6208c2ecf20Sopenharmony_ci int fd = winch->fd; 6218c2ecf20Sopenharmony_ci int err; 6228c2ecf20Sopenharmony_ci char c; 6238c2ecf20Sopenharmony_ci struct pid *pgrp; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (fd != -1) { 6268c2ecf20Sopenharmony_ci err = generic_read(fd, &c, NULL); 6278c2ecf20Sopenharmony_ci if (err < 0) { 6288c2ecf20Sopenharmony_ci if (err != -EAGAIN) { 6298c2ecf20Sopenharmony_ci winch->fd = -1; 6308c2ecf20Sopenharmony_ci list_del(&winch->list); 6318c2ecf20Sopenharmony_ci os_close_file(fd); 6328c2ecf20Sopenharmony_ci printk(KERN_ERR "winch_interrupt : " 6338c2ecf20Sopenharmony_ci "read failed, errno = %d\n", -err); 6348c2ecf20Sopenharmony_ci printk(KERN_ERR "fd %d is losing SIGWINCH " 6358c2ecf20Sopenharmony_ci "support\n", winch->tty_fd); 6368c2ecf20Sopenharmony_ci INIT_WORK(&winch->work, __free_winch); 6378c2ecf20Sopenharmony_ci schedule_work(&winch->work); 6388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci goto out; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci tty = tty_port_tty_get(winch->port); 6448c2ecf20Sopenharmony_ci if (tty != NULL) { 6458c2ecf20Sopenharmony_ci line = tty->driver_data; 6468c2ecf20Sopenharmony_ci if (line != NULL) { 6478c2ecf20Sopenharmony_ci chan_window_size(line, &tty->winsize.ws_row, 6488c2ecf20Sopenharmony_ci &tty->winsize.ws_col); 6498c2ecf20Sopenharmony_ci pgrp = tty_get_pgrp(tty); 6508c2ecf20Sopenharmony_ci if (pgrp) 6518c2ecf20Sopenharmony_ci kill_pgrp(pgrp, SIGWINCH, 1); 6528c2ecf20Sopenharmony_ci put_pid(pgrp); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci tty_kref_put(tty); 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci out: 6578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_civoid register_winch_irq(int fd, int tty_fd, int pid, struct tty_port *port, 6618c2ecf20Sopenharmony_ci unsigned long stack) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct winch *winch; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci winch = kmalloc(sizeof(*winch), GFP_KERNEL); 6668c2ecf20Sopenharmony_ci if (winch == NULL) { 6678c2ecf20Sopenharmony_ci printk(KERN_ERR "register_winch_irq - kmalloc failed\n"); 6688c2ecf20Sopenharmony_ci goto cleanup; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci *winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list), 6728c2ecf20Sopenharmony_ci .fd = fd, 6738c2ecf20Sopenharmony_ci .tty_fd = tty_fd, 6748c2ecf20Sopenharmony_ci .pid = pid, 6758c2ecf20Sopenharmony_ci .port = port, 6768c2ecf20Sopenharmony_ci .stack = stack }); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, 6798c2ecf20Sopenharmony_ci IRQF_SHARED, "winch", winch) < 0) { 6808c2ecf20Sopenharmony_ci printk(KERN_ERR "register_winch_irq - failed to register " 6818c2ecf20Sopenharmony_ci "IRQ\n"); 6828c2ecf20Sopenharmony_ci goto out_free; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci spin_lock(&winch_handler_lock); 6868c2ecf20Sopenharmony_ci list_add(&winch->list, &winch_handlers); 6878c2ecf20Sopenharmony_ci spin_unlock(&winch_handler_lock); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci out_free: 6928c2ecf20Sopenharmony_ci kfree(winch); 6938c2ecf20Sopenharmony_ci cleanup: 6948c2ecf20Sopenharmony_ci os_kill_process(pid, 1); 6958c2ecf20Sopenharmony_ci os_close_file(fd); 6968c2ecf20Sopenharmony_ci if (stack != 0) 6978c2ecf20Sopenharmony_ci free_stack(stack, 0); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic void unregister_winch(struct tty_struct *tty) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci struct list_head *ele, *next; 7038c2ecf20Sopenharmony_ci struct winch *winch; 7048c2ecf20Sopenharmony_ci struct tty_struct *wtty; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci spin_lock(&winch_handler_lock); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci list_for_each_safe(ele, next, &winch_handlers) { 7098c2ecf20Sopenharmony_ci winch = list_entry(ele, struct winch, list); 7108c2ecf20Sopenharmony_ci wtty = tty_port_tty_get(winch->port); 7118c2ecf20Sopenharmony_ci if (wtty == tty) { 7128c2ecf20Sopenharmony_ci free_winch(winch); 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci tty_kref_put(wtty); 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci spin_unlock(&winch_handler_lock); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic void winch_cleanup(void) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct list_head *ele, *next; 7238c2ecf20Sopenharmony_ci struct winch *winch; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci spin_lock(&winch_handler_lock); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci list_for_each_safe(ele, next, &winch_handlers) { 7288c2ecf20Sopenharmony_ci winch = list_entry(ele, struct winch, list); 7298c2ecf20Sopenharmony_ci free_winch(winch); 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci spin_unlock(&winch_handler_lock); 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci__uml_exitcall(winch_cleanup); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cichar *add_xterm_umid(char *base) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci char *umid, *title; 7398c2ecf20Sopenharmony_ci int len; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci umid = get_umid(); 7428c2ecf20Sopenharmony_ci if (*umid == '\0') 7438c2ecf20Sopenharmony_ci return base; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci len = strlen(base) + strlen(" ()") + strlen(umid) + 1; 7468c2ecf20Sopenharmony_ci title = kmalloc(len, GFP_KERNEL); 7478c2ecf20Sopenharmony_ci if (title == NULL) { 7488c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to allocate buffer for xterm title\n"); 7498c2ecf20Sopenharmony_ci return base; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci snprintf(title, len, "%s (%s)", base, umid); 7538c2ecf20Sopenharmony_ci return title; 7548c2ecf20Sopenharmony_ci} 755