1 /* 2 * linux/arch/alpha/kernel/srmcons.c 3 * 4 * Callback based driver for SRM Console console device. 5 * (TTY driver and console driver) 6 */ 7 8 #include <linux/config.h> 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 <asm/uaccess.h> 23 24 25 static DEFINE_SPINLOCK(srmcons_callback_lock); 26 static int srm_is_registered_console = 0; 27 28 /* 29 * The TTY driver 30 */ 31 #define MAX_SRM_CONSOLE_DEVICES 1 /* only support 1 console device */ 32 33 struct srmcons_private { 34 struct tty_struct *tty; 35 struct timer_list timer; 36 spinlock_t lock; 37 }; 38 39 typedef union _srmcons_result { 40 struct { 41 unsigned long c :61; 42 unsigned long status :3; 43 } bits; 44 long as_long; 45 } srmcons_result; 46 47 /* called with callback_lock held */ 48 static int 49 srmcons_do_receive_chars(struct tty_struct *tty) 50 { 51 srmcons_result result; 52 int count = 0, loops = 0; 53 54 do { 55 result.as_long = callback_getc(0); 56 if (result.bits.status < 2) { 57 tty_insert_flip_char(tty, (char)result.bits.c, 0); 58 count++; 59 } 60 } while((result.bits.status & 1) && (++loops < 10)); 61 62 if (count) 63 tty_schedule_flip(tty); 64 65 return count; 66 } 67 68 static void 69 srmcons_receive_chars(unsigned long data) 70 { 71 struct srmcons_private *srmconsp = (struct srmcons_private *)data; 72 unsigned long flags; 73 int incr = 10; 74 75 local_irq_save(flags); 76 if (spin_trylock(&srmcons_callback_lock)) { 77 if (!srmcons_do_receive_chars(srmconsp->tty)) 78 incr = 100; 79 spin_unlock(&srmcons_callback_lock); 80 } 81 82 spin_lock(&srmconsp->lock); 83 if (srmconsp->tty) { 84 srmconsp->timer.expires = jiffies + incr; 85 add_timer(&srmconsp->timer); 86 } 87 spin_unlock(&srmconsp->lock); 88 89 local_irq_restore(flags); 90 } 91 92 /* called with callback_lock held */ 93 static int 94 srmcons_do_write(struct tty_struct *tty, const char *buf, int count) 95 { 96 static char str_cr[1] = "\r"; 97 long c, remaining = count; 98 srmcons_result result; 99 char *cur; 100 int need_cr; 101 102 for (cur = (char *)buf; remaining > 0; ) { 103 need_cr = 0; 104 /* 105 * Break it up into reasonable size chunks to allow a chance 106 * for input to get in 107 */ 108 for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++) 109 if (cur[c] == '\n') 110 need_cr = 1; 111 112 while (c > 0) { 113 result.as_long = callback_puts(0, cur, c); 114 c -= result.bits.c; 115 remaining -= result.bits.c; 116 cur += result.bits.c; 117 118 /* 119 * Check for pending input iff a tty was provided 120 */ 121 if (tty) 122 srmcons_do_receive_chars(tty); 123 } 124 125 while (need_cr) { 126 result.as_long = callback_puts(0, str_cr, 1); 127 if (result.bits.c > 0) 128 need_cr = 0; 129 } 130 } 131 return count; 132 } 133 134 static int 135 srmcons_write(struct tty_struct *tty, 136 const unsigned char *buf, int count) 137 { 138 unsigned long flags; 139 140 spin_lock_irqsave(&srmcons_callback_lock, flags); 141 srmcons_do_write(tty, (const char *) buf, count); 142 spin_unlock_irqrestore(&srmcons_callback_lock, flags); 143 144 return count; 145 } 146 147 static int 148 srmcons_write_room(struct tty_struct *tty) 149 { 150 return 512; 151 } 152 153 static int 154 srmcons_chars_in_buffer(struct tty_struct *tty) 155 { 156 return 0; 157 } 158 159 static int 160 srmcons_get_private_struct(struct srmcons_private **ps) 161 { 162 static struct srmcons_private *srmconsp = NULL; 163 static DEFINE_SPINLOCK(srmconsp_lock); 164 unsigned long flags; 165 int retval = 0; 166 167 if (srmconsp == NULL) { 168 spin_lock_irqsave(&srmconsp_lock, flags); 169 170 srmconsp = kmalloc(sizeof(*srmconsp), GFP_KERNEL); 171 if (srmconsp == NULL) 172 retval = -ENOMEM; 173 else { 174 srmconsp->tty = NULL; 175 spin_lock_init(&srmconsp->lock); 176 init_timer(&srmconsp->timer); 177 } 178 179 spin_unlock_irqrestore(&srmconsp_lock, flags); 180 } 181 182 *ps = srmconsp; 183 return retval; 184 } 185 186 static int 187 srmcons_open(struct tty_struct *tty, struct file *filp) 188 { 189 struct srmcons_private *srmconsp; 190 unsigned long flags; 191 int retval; 192 193 retval = srmcons_get_private_struct(&srmconsp); 194 if (retval) 195 return retval; 196 197 spin_lock_irqsave(&srmconsp->lock, flags); 198 199 if (!srmconsp->tty) { 200 tty->driver_data = srmconsp; 201 202 srmconsp->tty = tty; 203 srmconsp->timer.function = srmcons_receive_chars; 204 srmconsp->timer.data = (unsigned long)srmconsp; 205 srmconsp->timer.expires = jiffies + 10; 206 add_timer(&srmconsp->timer); 207 } 208 209 spin_unlock_irqrestore(&srmconsp->lock, flags); 210 211 return 0; 212 } 213 214 static void 215 srmcons_close(struct tty_struct *tty, struct file *filp) 216 { 217 struct srmcons_private *srmconsp = tty->driver_data; 218 unsigned long flags; 219 220 spin_lock_irqsave(&srmconsp->lock, flags); 221 222 if (tty->count == 1) { 223 srmconsp->tty = NULL; 224 del_timer(&srmconsp->timer); 225 } 226 227 spin_unlock_irqrestore(&srmconsp->lock, flags); 228 } 229 230 231 static struct tty_driver *srmcons_driver; 232 233 static struct tty_operations srmcons_ops = { 234 .open = srmcons_open, 235 .close = srmcons_close, 236 .write = srmcons_write, 237 .write_room = srmcons_write_room, 238 .chars_in_buffer= srmcons_chars_in_buffer, 239 }; 240 241 static int __init 242 srmcons_init(void) 243 { 244 if (srm_is_registered_console) { 245 struct tty_driver *driver; 246 int err; 247 248 driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES); 249 if (!driver) 250 return -ENOMEM; 251 driver->driver_name = "srm"; 252 driver->name = "srm"; 253 driver->major = 0; /* dynamic */ 254 driver->minor_start = 0; 255 driver->type = TTY_DRIVER_TYPE_SYSTEM; 256 driver->subtype = SYSTEM_TYPE_SYSCONS; 257 driver->init_termios = tty_std_termios; 258 tty_set_operations(driver, &srmcons_ops); 259 err = tty_register_driver(driver); 260 if (err) { 261 put_tty_driver(driver); 262 return err; 263 } 264 srmcons_driver = driver; 265 } 266 267 return -ENODEV; 268 } 269 270 module_init(srmcons_init); 271 272 273 /* 274 * The console driver 275 */ 276 static void 277 srm_console_write(struct console *co, const char *s, unsigned count) 278 { 279 unsigned long flags; 280 281 spin_lock_irqsave(&srmcons_callback_lock, flags); 282 srmcons_do_write(NULL, s, count); 283 spin_unlock_irqrestore(&srmcons_callback_lock, flags); 284 } 285 286 static struct tty_driver * 287 srm_console_device(struct console *co, int *index) 288 { 289 *index = co->index; 290 return srmcons_driver; 291 } 292 293 static int __init 294 srm_console_setup(struct console *co, char *options) 295 { 296 return 0; 297 } 298 299 static struct console srmcons = { 300 .name = "srm", 301 .write = srm_console_write, 302 .device = srm_console_device, 303 .setup = srm_console_setup, 304 .flags = CON_PRINTBUFFER, 305 .index = -1, 306 }; 307 308 void __init 309 register_srm_console(void) 310 { 311 if (!srm_is_registered_console) { 312 callback_open_console(); 313 register_console(&srmcons); 314 srm_is_registered_console = 1; 315 } 316 } 317 318 void __init 319 unregister_srm_console(void) 320 { 321 if (srm_is_registered_console) { 322 callback_close_console(); 323 unregister_console(&srmcons); 324 srm_is_registered_console = 0; 325 } 326 } 327