#include #include #include #include #include #include #include #include #include #include #define NRTTY \ (sizeof(ttys) / sizeof(struct tty_struct*)) static struct tty_struct *ttys[] = { &tty_con, &tty_rs }; /* check if a buffer's empty and if so, go to sleep */ static int sleep_while_empty(struct tty_queue *q) { cli(); for(;;) { if(q->pread == q->pwrite) interruptible_sleep_on(&q->waiting); /* check again to see if anything was read, or we were interrupted */ if(q->pread == q->pwrite) { sti(); return -1; } sti(); return 0; } } /* check if a buffer's full and if so, go to sleep */ static int sleep_while_full(struct tty_queue *q, struct tty_struct *tty) { cli(); for(;;) { /* * if the buffer is full, first notify the * driver so it can try to remove some * data then, check again and if it's * still full, go to sleep. */ if(q->pwrite == ((q->pread - 1) % sizeof(q->buf))) if(tty->write) tty->write(); if(q->pwrite == ((q->pread - 1) % sizeof(q->buf))) interruptible_sleep_on(&q->waiting); /* check again to see if anything was removed, or we were interrupted */ if(q->pwrite == ((q->pread - 1) % sizeof(q->buf))) { sti(); return -1; } sti(); return 0; } } ssize_t tty_read(unsigned tty, void *buf, size_t len) { size_t i; char *b = buf; struct tty_queue *q = &ttys[tty]->rqueue; if(tty > NRTTY || ttys[tty] == NULL) return -1; if(len == 0) return 0; if(len > SSIZE_MAX) len = SSIZE_MAX; for(i = 0; i < len; i++) { /* check if the buffer's empty and if so, go to sleep */ if(sleep_while_empty(q) < 0) return i == 0 ? -EINTR : i; *b++ = q->buf[q->pread]; q->pread = (q->pread + 1) % TTY_BUF_SIZE; } return len; } ssize_t tty_write(unsigned tty, void *buf, size_t len) { size_t i; char *b = buf; struct tty_queue *q = &ttys[tty]->wqueue; if(tty > NRTTY || ttys[tty] == NULL) return -1; if(len == 0) return 0; if(len > SSIZE_MAX) len = SSIZE_MAX; for(i = 0; i < len; i++) { /* check if the buffer's full and if so, go to sleep */ if(sleep_while_full(q, ttys[tty]) < 0) return i == 0 ? -EINTR : i; q->buf[q->pwrite] = *b++; q->pwrite = (q->pwrite + 1) % TTY_BUF_SIZE; } /* tell the driver that new data is available */ cli(); if(ttys[tty]->write) ttys[tty]->write(); sti(); return len; } void tty_init(void) { int i; for(i = 0; i < NRTTY; i++) { if(!ttys[i]) continue; memset(&ttys[i]->rqueue, 0, sizeof(struct tty_queue)); memset(&ttys[i]->wqueue, 0, sizeof(struct tty_queue)); if(ttys[i]->init) ttys[i]->init(); } }