1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * linux/arch/alpha/kernel/srmcons.c 4 * 5 * Callback based driver for SRM Console console device. 6 * (TTY driver and console driver) 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/init.h> 11 #include <linux/console.h> 12 #include <linux/delay.h> 13 #include <linux/mm.h> 14 #include <linux/slab.h> 15 #include <linux/spinlock.h> 16 #include <linux/timer.h> 17 #include <linux/tty.h> 18 #include <linux/tty_driver.h> 19 #include <linux/tty_flip.h> 20 21 #include <asm/console.h> 22 #include <linux/uaccess.h> 23 24 #include "proto.h" 25 26 27 static DEFINE_SPINLOCK(srmcons_callback_lock); 28 static int srm_is_registered_console = 0; 29 30 /* 31 * The TTY driver 32 */ 33 #define MAX_SRM_CONSOLE_DEVICES 1 /* only support 1 console device */ 34 35 struct srmcons_private { 36 struct tty_port port; 37 struct timer_list timer; 38 } srmcons_singleton; 39 40 typedef union _srmcons_result { 41 struct { 42 unsigned long c :61; 43 unsigned long status :3; 44 } bits; 45 long as_long; 46 } srmcons_result; 47 48 /* called with callback_lock held */ 49 static int 50 srmcons_do_receive_chars(struct tty_port *port) 51 { 52 srmcons_result result; 53 int count = 0, loops = 0; 54 55 do { 56 result.as_long = callback_getc(0); 57 if (result.bits.status < 2) { 58 tty_insert_flip_char(port, (u8)result.bits.c, 0); 59 count++; 60 } 61 } while((result.bits.status & 1) && (++loops < 10)); 62 63 if (count) 64 tty_flip_buffer_push(port); 65 66 return count; 67 } 68 69 static void 70 srmcons_receive_chars(struct timer_list *t) 71 { 72 struct srmcons_private *srmconsp = from_timer(srmconsp, t, timer); 73 struct tty_port *port = &srmconsp->port; 74 unsigned long flags; 75 int incr = 10; 76 77 local_irq_save(flags); 78 if (spin_trylock(&srmcons_callback_lock)) { 79 if (!srmcons_do_receive_chars(port)) 80 incr = 100; 81 spin_unlock(&srmcons_callback_lock); 82 } 83 84 spin_lock(&port->lock); 85 if (port->tty) 86 mod_timer(&srmconsp->timer, jiffies + incr); 87 spin_unlock(&port->lock); 88 89 local_irq_restore(flags); 90 } 91 92 /* called with callback_lock held */ 93 static void 94 srmcons_do_write(struct tty_port *port, const u8 *buf, size_t count) 95 { 96 size_t c; 97 srmcons_result result; 98 99 while (count > 0) { 100 bool need_cr = false; 101 /* 102 * Break it up into reasonable size chunks to allow a chance 103 * for input to get in 104 */ 105 for (c = 0; c < min_t(size_t, 128U, count) && !need_cr; c++) 106 if (buf[c] == '\n') 107 need_cr = true; 108 109 while (c > 0) { 110 result.as_long = callback_puts(0, buf, c); 111 c -= result.bits.c; 112 count -= result.bits.c; 113 buf += result.bits.c; 114 115 /* 116 * Check for pending input iff a tty port was provided 117 */ 118 if (port) 119 srmcons_do_receive_chars(port); 120 } 121 122 while (need_cr) { 123 result.as_long = callback_puts(0, "\r", 1); 124 if (result.bits.c > 0) 125 need_cr = false; 126 } 127 } 128 } 129 130 static ssize_t 131 srmcons_write(struct tty_struct *tty, const u8 *buf, size_t count) 132 { 133 unsigned long flags; 134 135 spin_lock_irqsave(&srmcons_callback_lock, flags); 136 srmcons_do_write(tty->port, buf, count); 137 spin_unlock_irqrestore(&srmcons_callback_lock, flags); 138 139 return count; 140 } 141 142 static unsigned int 143 srmcons_write_room(struct tty_struct *tty) 144 { 145 return 512; 146 } 147 148 static int 149 srmcons_open(struct tty_struct *tty, struct file *filp) 150 { 151 struct srmcons_private *srmconsp = &srmcons_singleton; 152 struct tty_port *port = &srmconsp->port; 153 unsigned long flags; 154 155 spin_lock_irqsave(&port->lock, flags); 156 157 if (!port->tty) { 158 tty->driver_data = srmconsp; 159 tty->port = port; 160 port->tty = tty; /* XXX proper refcounting */ 161 mod_timer(&srmconsp->timer, jiffies + 10); 162 } 163 164 spin_unlock_irqrestore(&port->lock, flags); 165 166 return 0; 167 } 168 169 static void 170 srmcons_close(struct tty_struct *tty, struct file *filp) 171 { 172 struct srmcons_private *srmconsp = tty->driver_data; 173 struct tty_port *port = &srmconsp->port; 174 unsigned long flags; 175 176 spin_lock_irqsave(&port->lock, flags); 177 178 if (tty->count == 1) { 179 port->tty = NULL; 180 del_timer(&srmconsp->timer); 181 } 182 183 spin_unlock_irqrestore(&port->lock, flags); 184 } 185 186 187 static struct tty_driver *srmcons_driver; 188 189 static const struct tty_operations srmcons_ops = { 190 .open = srmcons_open, 191 .close = srmcons_close, 192 .write = srmcons_write, 193 .write_room = srmcons_write_room, 194 }; 195 196 static int __init 197 srmcons_init(void) 198 { 199 timer_setup(&srmcons_singleton.timer, srmcons_receive_chars, 0); 200 if (srm_is_registered_console) { 201 struct tty_driver *driver; 202 int err; 203 204 driver = tty_alloc_driver(MAX_SRM_CONSOLE_DEVICES, 0); 205 if (IS_ERR(driver)) 206 return PTR_ERR(driver); 207 208 tty_port_init(&srmcons_singleton.port); 209 210 driver->driver_name = "srm"; 211 driver->name = "srm"; 212 driver->major = 0; /* dynamic */ 213 driver->minor_start = 0; 214 driver->type = TTY_DRIVER_TYPE_SYSTEM; 215 driver->subtype = SYSTEM_TYPE_SYSCONS; 216 driver->init_termios = tty_std_termios; 217 tty_set_operations(driver, &srmcons_ops); 218 tty_port_link_device(&srmcons_singleton.port, driver, 0); 219 err = tty_register_driver(driver); 220 if (err) { 221 tty_driver_kref_put(driver); 222 tty_port_destroy(&srmcons_singleton.port); 223 return err; 224 } 225 srmcons_driver = driver; 226 } 227 228 return -ENODEV; 229 } 230 device_initcall(srmcons_init); 231 232 233 /* 234 * The console driver 235 */ 236 static void 237 srm_console_write(struct console *co, const char *s, unsigned count) 238 { 239 unsigned long flags; 240 241 spin_lock_irqsave(&srmcons_callback_lock, flags); 242 srmcons_do_write(NULL, s, count); 243 spin_unlock_irqrestore(&srmcons_callback_lock, flags); 244 } 245 246 static struct tty_driver * 247 srm_console_device(struct console *co, int *index) 248 { 249 *index = co->index; 250 return srmcons_driver; 251 } 252 253 static int 254 srm_console_setup(struct console *co, char *options) 255 { 256 return 0; 257 } 258 259 static struct console srmcons = { 260 .name = "srm", 261 .write = srm_console_write, 262 .device = srm_console_device, 263 .setup = srm_console_setup, 264 .flags = CON_PRINTBUFFER | CON_BOOT, 265 .index = -1, 266 }; 267 268 void __init 269 register_srm_console(void) 270 { 271 if (!srm_is_registered_console) { 272 callback_open_console(); 273 register_console(&srmcons); 274 srm_is_registered_console = 1; 275 } 276 } 277 278 void __init 279 unregister_srm_console(void) 280 { 281 if (srm_is_registered_console) { 282 callback_close_console(); 283 unregister_console(&srmcons); 284 srm_is_registered_console = 0; 285 } 286 } 287
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.