#include <klibc/string.h>
#include <lunaix/fs.h>
#include <lunaix/mm/valloc.h>
+#include <lunaix/process.h>
#include <lunaix/spike.h>
#include <lunaix/status.h>
+#include <lunaix/syslog.h>
#include <usr/lunaix/ioctl_defs.h>
-#define ANSI_LCNTL 0
+LOG_MODULE("term");
+
+#define termdev(dev) ((struct term*)(dev)->underlay)
-extern struct term_lcntl ansi_line_controller;
-static struct term_lcntl* line_controls[] = { [ANSI_LCNTL] =
- &ansi_line_controller };
#define LCNTL_TABLE_LEN (sizeof(line_controls) / sizeof(struct term_lcntl*))
+static struct devclass termdev_class = DEVCLASS(LUNAIX, TTY, VTERM);
+
+struct device* sysconsole = NULL;
+
+extern struct termport_pot_ops default_termport_pot_ops;
+
static int
term_exec_cmd(struct device* dev, u32_t req, va_list args)
{
- struct term* term = (struct term*)dev->underlay;
+ struct term* term = termdev(dev);
int err = 0;
device_lock(dev);
}
case TIOCGPGRP:
return term->fggrp;
- case TIOCPUSH: {
- u32_t lcntl_idx = va_arg(args, u32_t);
- struct term_lcntl* lcntl = term_get_lcntl(lcntl_idx);
-
- if (!lcntl) {
- err = EINVAL;
- goto done;
- }
-
- term_push_lcntl(term, lcntl);
- break;
- }
- case TIOCPOP:
- term_pop_lcntl(term);
- break;
- case TIOCSCDEV: {
+ case TDEV_TCSETCHDEV: {
int fd = va_arg(args, int);
struct v_fd* vfd;
goto done;
}
- struct device* cdev = device_cast(vfd->file->inode->data);
+ struct device* cdev = resolve_device(vfd->file->inode->data);
if (!cdev) {
err = ENOTDEV;
goto done;
term_bind(term, cdev);
break;
}
- case TIOCGCDEV: {
+ case TDEV_TCGETCHDEV: {
struct dev_info* devinfo = va_arg(args, struct dev_info*);
if (!term->chdev) {
}
break;
}
+ case TDEV_TCGETATTR: {
+ struct termios* tios = va_arg(args, struct termios*);
+ *tios = (struct termios){.c_oflag = term->oflags,
+ .c_iflag = term->iflags,
+ .c_lflag = term->lflags,
+ .c_cflag = term->cflags};
+ memcpy(tios->c_cc, term->cc, _NCCS * sizeof(cc_t));
+ tios->c_baud = term->iospeed;
+ } break;
+ case TDEV_TCSETATTR: {
+ struct termport_pot_ops* pot_ops;
+ struct termios* tios = va_arg(args, struct termios*);
+
+ term->iflags = tios->c_iflag;
+ term->oflags = tios->c_oflag;
+ term->lflags = tios->c_lflag;
+ memcpy(term->cc, tios->c_cc, _NCCS * sizeof(cc_t));
+
+ tcflag_t old_cf = term->cflags;
+ term->cflags = tios->c_cflag;
+
+ if (!term->tp_cap) {
+ goto done;
+ }
+
+ pot_ops = term->tp_cap->ops;
+
+ if (tios->c_baud != term->iospeed) {
+ term->iospeed = tios->c_baud;
+
+ pot_ops->set_speed(term->chdev, tios->c_baud);
+ }
+
+ if (old_cf != tios->c_cflag) {
+ pot_ops->set_cntrl_mode(term->chdev, tios->c_cflag);
+ }
+ } break;
default:
err = EINVAL;
goto done;
}
static int
-term_write(struct device* dev, void* buf, size_t offset, size_t len)
+tdev_do_write(struct device* dev, void* buf, off_t fpos, size_t len)
{
- struct term* term = (struct term*)dev->underlay;
- size_t sz = MIN(len, term->line.sz_hlf);
-
- if (!term->chdev) {
- return ENODEV;
+ struct term* tdev = termdev(dev);
+ lbuf_ref_t current = ref_current(&tdev->line_out);
+ size_t wrsz = 0;
+ while (wrsz < len) {
+ wrsz += rbuffer_puts(
+ deref(current), &((char*)buf)[wrsz], len - wrsz);
+
+ if (rbuffer_full(deref(current))) {
+ term_flush(tdev);
+ }
}
-
- device_lock(term->dev);
-
- memcpy(term->line.current, &((char*)buf)[offset], sz);
-
- struct term_lcntl *lcntl, *n;
- llist_for_each(lcntl, n, &term->lcntl_stack, lcntls)
- {
- sz = lcntl->apply(term, term->line.current, sz);
- line_flip(&term->line);
+
+ if (!rbuffer_empty(deref(current))) {
+ term_flush(tdev);
}
-
- int errcode = term_sendline(term, sz);
-
- device_unlock(term->dev);
-
- return errcode;
+ return wrsz;
}
static int
-term_read(struct device* dev, void* buf, size_t offset, size_t len)
+tdev_do_read(struct device* dev, void* buf, off_t fpos, size_t len)
{
- struct term* term = (struct term*)dev->underlay;
- size_t sz = MIN(len, term->line.sz_hlf);
-
- if (!term->chdev) {
- return ENODEV;
- }
-
- device_lock(term->dev);
-
- sz = term_readline(term, sz);
- if (!sz) {
- device_unlock(term->dev);
- return 0;
- }
-
- struct term_lcntl *pos, *n;
- llist_for_each(pos, n, &term->lcntl_stack, lcntls)
- {
- sz = pos->apply(term, term->line.current, sz);
- line_flip(&term->line);
+ struct term* tdev = termdev(dev);
+ lbuf_ref_t current = ref_current(&tdev->line_in);
+ bool cont = true;
+ size_t rdsz = 0;
+
+ while (cont && rdsz < len) {
+ cont = term_read(tdev);
+ rdsz += rbuffer_gets(
+ deref(current), &((char*)buf)[rdsz], len - rdsz);
}
+
+ tdev->line_in.sflags = 0;
+ return rdsz;
+}
- memcpy(&((char*)buf)[offset], term->line.current, sz);
+static cc_t default_cc[_NCCS] = {4, '\n', 0x7f, 3, 1, 24, 22, 0, 0, 1, 1};
- device_unlock(term->dev);
+static void
+load_default_setting(struct term* tdev)
+{
+ tdev->lflags = _ICANON | _IEXTEN | _ISIG | _ECHO | _ECHOE | _ECHONL;
+ tdev->iflags = _ICRNL | _IGNBRK;
+ tdev->oflags = _ONLCR | _OPOST;
+ tdev->iospeed = _B9600;
+ memcpy(tdev->cc, default_cc, _NCCS * sizeof(cc_t));
+}
- return sz;
+static void
+alloc_term_buffer(struct term* terminal, size_t sz_hlf)
+{
+ line_alloc(&terminal->line_in, sz_hlf);
+ line_alloc(&terminal->line_out, sz_hlf);
+ terminal->scratch_pad = valloc(sz_hlf);
}
-struct term*
-term_create()
+struct termport_potens*
+term_attach_potens(struct device* chardev,
+ struct termport_pot_ops* ops, char* suffix)
{
- struct term* terminal = valloc(sizeof(struct term));
+ struct term* terminal;
+ struct device* tdev;
+ struct termport_potens* tp_cap;
+ terminal = vzalloc(sizeof(struct term));
if (!terminal) {
return NULL;
}
- terminal->dev = device_allocseq(NULL, terminal);
-
- terminal->dev->ops.read = term_read;
- terminal->dev->ops.write = term_write;
-
- llist_init_head(&terminal->lcntl_stack);
- line_alloc(&terminal->line, 1024);
-
- return terminal;
-}
+ tdev = device_allocseq(NULL, terminal);
+ terminal->dev = tdev;
+ terminal->chdev = chardev;
-int
-term_bind(struct term* term, struct device* chdev)
-{
- device_lock(term->dev);
-
- term->chdev = chdev;
+ tdev->ops.read = tdev_do_read;
+ tdev->ops.write = tdev_do_write;
+ tdev->ops.exec_cmd = term_exec_cmd;
- device_unlock(term->dev);
+ waitq_init(&terminal->line_in_event);
- return 0;
-}
+ alloc_term_buffer(terminal, 1024);
-struct term_lcntl*
-term_get_lcntl(u32_t lcntl_index)
-{
- if (lcntl_index >= LCNTL_TABLE_LEN) {
- return NULL;
+ if (chardev) {
+ int cdev_var = DEV_VAR_FROM(chardev->ident.unique);
+ register_device(tdev, &termdev_class, "tty%s%d", suffix, cdev_var);
+ } else {
+ register_device_var(tdev, &termdev_class, "tty");
}
- struct term_lcntl* lcntl_template = line_controls[lcntl_index];
- struct term_lcntl* lcntl_instance = valloc(sizeof(struct term_lcntl));
+ INFO("spawned: %s", tdev->name_val);
- if (!lcntl_instance) {
- return NULL;
- }
-
- lcntl_instance->apply = lcntl_template->apply;
-
- return lcntl_instance;
-}
+ tp_cap = new_potens(potens(TERMPORT), struct termport_potens);
+ tp_cap->ops = ops ?: &default_termport_pot_ops;
-int
-term_push_lcntl(struct term* term, struct term_lcntl* lcntl)
-{
- device_lock(term->dev);
+ terminal->tp_cap = tp_cap;
+ tp_cap->term = terminal;
- llist_append(&term->lcntl_stack, &lcntl->lcntls);
+ device_grant_potens(tdev, potens_meta(tp_cap));
- device_unlock(term->dev);
+ load_default_setting(terminal);
- return 0;
+ return tp_cap;
}
int
-term_pop_lcntl(struct term* term)
+term_bind(struct term* term, struct device* chdev)
{
- if (term->lcntl_stack.prev == &term->lcntl_stack) {
- return 0;
- }
-
device_lock(term->dev);
- struct term_lcntl* lcntl =
- list_entry(term->lcntl_stack.prev, struct term_lcntl, lcntls);
- llist_delete(term->lcntl_stack.prev);
-
- vfree(lcntl);
+ term->chdev = chdev;
device_unlock(term->dev);
- return 1;
-}
-
-void
-line_flip(struct linebuffer* lbf)
-{
- char* tmp = lbf->current;
- lbf->current = lbf->next;
- lbf->next = tmp;
+ return 0;
}
void
line_alloc(struct linebuffer* lbf, size_t sz_hlf)
{
char* buffer = valloc(sz_hlf * 2);
- lbf->current = buffer;
- lbf->next = &buffer[sz_hlf];
+ memset(lbf, 0, sizeof(*lbf));
+ lbf->current = rbuffer_create(buffer, sz_hlf);
+ lbf->next = rbuffer_create(&buffer[sz_hlf], sz_hlf);
lbf->sz_hlf = sz_hlf;
}
void
line_free(struct linebuffer* lbf, size_t sz_hlf)
{
- void* ptr = (void*)MIN((ptr_t)lbf->current, (ptr_t)lbf->next);
+ void* ptr =
+ (void*)MIN((ptr_t)lbf->current->buffer, (ptr_t)lbf->next->buffer);
vfree(ptr);
}
-int
-term_sendline(struct term* tdev, size_t len)
-{
- struct device* chdev = tdev->chdev;
-
- device_lock(chdev);
-
- int code = chdev->ops.write(chdev, tdev->line.current, 0, len);
-
- device_unlock(chdev);
-
- return code;
-}
-
-int
-term_readline(struct term* tdev, size_t len)
-{
- struct device* chdev = tdev->chdev;
-
- device_lock(chdev);
-
- int code = chdev->ops.read(chdev, tdev->line.current, 0, len);
-
- device_unlock(chdev);
-
- return code;
-}
-
-int
-term_init(struct device_def* devdef)
+void
+term_sendsig(struct term* tdev, int signal)
{
- struct term* terminal = term_create();
- struct device* tdev = device_allocseq(NULL, terminal);
- tdev->ops.read = term_read;
- tdev->ops.write = term_write;
- tdev->ops.exec_cmd = term_exec_cmd;
-
- devdef->class.variant++;
- device_register(tdev, &devdef->class, "tty%d", devdef->class.variant);
-
- return 0;
-}
-
-static struct device_def vterm_def = {
- .class = DEVCLASS(DEVIF_NON, DEVFN_TTY, DEV_VTERM),
- .name = "virtual terminal",
- .init = term_init
-};
-EXPORT_DEVICE(vterm, &vterm_def, load_on_demand);
\ No newline at end of file
+ if ((tdev->lflags & _ISIG)) {
+ signal_send(-tdev->fggrp, signal);
+ pwake_all(&tdev->line_in_event);
+ }
+}
\ No newline at end of file