#include #include #include #include #include static struct char_device_ops tty_ops = { .read = tty_read, .write = tty_write, }; static spin_lock_t foreground_lock; static struct device *foreground = NULL; static struct device *foreground_input = NULL; static void tty_input_hook_callback(struct device *dev, struct input_event *ev, enum input_event_hook_flags *flags, void *arg) { struct device *fg_tty = foreground; if (!fg_tty) { return; } tty_report_event(fg_tty, ev); *flags = 0; } static struct input_event_hook foreground_input_hook = { .hook_callback = tty_input_hook_callback, }; static void tty_console_write(struct console *con, const char *s, unsigned int len) { if (foreground) { size_t nr_written; tty_write(foreground, s, len, &nr_written, 0); } } static struct console tty_console = { .c_name = "tty", .c_write = tty_console_write, }; struct device *tty_device_create(void) { struct char_device *cdev = char_device_create(); if (!cdev) { return NULL; } struct tty_device *tty_dev = kmalloc(sizeof *tty_dev, VM_NORMAL); if (!tty_dev) { object_deref(char_device_object(cdev)); return NULL; } kern_status_t status = ringbuffer_init(&tty_dev->tty_input, TTY_INPUT_QUEUE_SIZE * sizeof(char)); if (status != KERN_OK) { kfree(tty_dev); object_deref(char_device_object(cdev)); return NULL; } tty_dev->tty_ldisc = tty_default_line_discipline(); cdev->c_ops = &tty_ops; cdev->c_tty = tty_dev; return char_device_base(cdev); } static kern_status_t generate_name(struct tty_driver *owner, struct device *dev, char out[DEV_NAME_MAX]) { /* minor numbers start at 1. subtract 1 to start at 0 instead */ snprintf(out, DEV_NAME_MAX, "%s%u", owner->tty_name, dev->dev_minor - 1); return KERN_OK; } kern_status_t tty_device_register(struct device *dev, struct tty_driver *owner, struct device *parent) { kern_status_t status = device_register(dev, tty_driver_base(owner), parent); if (status != KERN_OK) { return status; } char link_name[DEV_NAME_MAX]; generate_name(owner, dev, link_name); char link_path[OBJECT_PATH_MAX]; snprintf(link_path, sizeof link_path, "/dev/tty/%s", link_name); return object_namespace_create_link(global_namespace(), link_path, &dev->dev_base); } void tty_set_foreground(struct device *tty) { bool console_init = false; if (foreground) { console_init = true; device_deref(foreground); } foreground = device_ref(tty); if (!console_init) { console_register(&tty_console); } } kern_status_t tty_connect_foreground_input_device(struct device *input) { struct input_device *inputdev = INPUT_DEVICE(input); if (!inputdev) { return KERN_INVALID_ARGUMENT; } unsigned long flags; spin_lock_irqsave(&foreground_lock, &flags); if (foreground_input) { struct device *prev = foreground_input; foreground_input = NULL; device_lock(prev); input_device_remove_hook(prev, &foreground_input_hook); device_unlock(prev); device_deref(prev); object_deref(&prev->dev_base); } foreground_input = device_ref(input); device_lock(input); input_device_add_hook(input, &foreground_input_hook); device_unlock(input); spin_unlock_irqrestore(&foreground_lock, flags); return KERN_OK; }