wakeup(s);
wakeup(&s->s_data);
wakeup(&s->s_iflag);
+ selwake_dev(4, 65, SELECT_IN);
}
/*
* the first one we find at the daemon. We should possibly round-robin
* these but it's not clear it's that important
*/
+static struct sockdata *netdev_findevent(void)
+{
+ struct sockdata *sd = sockdata;
+ while (sd != sockdata + NSOCKET) {
+ if (sd->event)
+ return sd;
+ sd++;
+ }
+ return NULL;
+}
+
int netdev_read(uint8_t flag)
{
if (net_ino == NULL || udata.u_count != sizeof(struct sockmsg)) {
return -1;
}
while(1) {
- struct sockdata *sd = sockdata;
- while (sd != sockdata + NSOCKET) {
- if (sd->event)
- return netdev_report(sd);
- sd++;
- }
+ struct sockdata *sd = netdev_findevent();
+ if (sd)
+ return netdev_report(sd);
if (psleep_flags(&ne, flag))
return -1;
}
if ((net_ino = getinode(fd)) == NULLINODE)
return -1;
i_ref(net_ino);
- return 0;
+ return 0;
+ case SELECT_BEGIN:
+ case SELECT_TEST:
+ /* We are always writable, but may not alway be readable */
+ if (*data & SELECT_IN) {
+ if (netdev_findevent() == NULL)
+ *data &= ~SELECT_IN;
+ }
+ *data &= SELECT_IN|SELECT_OUT;
}
udata.u_error = ENOTTY;
return -1;
sd->event |= NEV_STATE | NEVW_STATE;
sd->newstate = state;
wakeup(&ne);
+ selwake_dev(4, 65, SELECT_IN);
do {
if( s->s_state == SS_CLOSED )
struct sockdata *sd = s->s_priv;
sd->event |= event;
wakeup(&ne);
+ selwake_dev(4, 65, SELECT_IN);
}
/* General purpose ring buffer operator. Non re-entrant so use every
* by userspace.
*/
#define SELECT_BEGIN 0x8000
-#define SELECT_END 0x8001
+#define SELECT_TEST 0x8001
+#define SELECT_END 0x8002
#define IOCTL_NORMAL 0x0000 /* No special rules */
#define IOCTL_SUPER 0x4000 /* Superuser needed */
/* select.c */
+#ifdef CONFIG_LEVEL_2
extern void seladdwait(struct selmap *s);
extern void selrmwait(struct selmap *s);
extern void selwake(struct selmap *s);
-#ifdef CONFIG_LEVEL_2
extern void selwait_inode(inoptr i, uint8_t smask, uint8_t setit);
extern void selwake_inode(inoptr i, uint16_t mask);
extern void selwake_pipe(inoptr i, uint16_t mask);
+extern void selwake_dev(uint8_t major, uint8_t minor, uint16_t mask);
extern int _select(void);
#else
#define selwait_inode(i,smask,setit) do {} while(0)
#define selwake_inode(i,smask) do {} while(0)
#define selwake_pipe(i,smask) do {} while(0)
+#define selwake_dev(major,minor,smask) do {} while(0)
#endif
/* swap.c */
typedef enum {
TTY_READY_NOW=1, /* port is ready immediately */
TTY_READY_SOON=0, /* we'll be ready shortly, kernel should spin, polling the port repeatedly */
- TTY_READY_LATER=-1 /* we'll be a long time, put this process to sleep and schedule another */
+ TTY_READY_LATER=2 /* we'll be a long time, put this process to sleep and schedule another */
} ttyready_t;
/* provided by platform */
/* We have an RTC */
#define CONFIG_RTC
/* Enable to make ^Z dump the inode table for debug */
-#undef CONFIG_IDUMP
+#define CONFIG_IDUMP
/* Enable to make ^A drop back into the monitor */
#undef CONFIG_MONITOR
/* Profil syscall support (not yet complete) */
void selwake_inode(inoptr i, uint16_t mask)
{
struct selmap *s = (struct selmap *) (&i->c_node.i_addr[17]);
+ irqflags_t irq = di();
if (mask & SELECT_IN)
selwake(s);
s++;
s++;
if (mask & SELECT_EX)
selwake(s);
+ irqrestore(irq);
+}
+
+void selwake_dev(uint8_t major, uint8_t minor, uint16_t mask)
+{
+ irqflags_t irq = di();
+ uint16_t v = (major << 8) | minor;
+ inoptr i = i_tab;
+
+ while (i <= &i_tab[ITABSIZE - 1]) { /* Convoluted form to keep SDCC happy */
+ if (i->c_refs && i->c_node.i_addr[0] == v)
+ selwake_inode(i, mask);
+ i++;
+ }
+ irqrestore(irq);
}
static int pipesel_begin(inoptr i, uint8_t bits)
int _select(void)
{
+ irqflags_t irq;
+ uint16_t seltype = SELECT_BEGIN;
inoptr ino;
uint16_t sumo;
uint8_t i, m, n;
uint16_t inr = 0, outr = 0, exr = 0;
/* Second 16bits of each spare for expansion */
uint16_t in = ugetw(base);
- uint16_t out = ugetw(base + 4);
- uint16_t ex = ugetw(base + 8);
+ uint16_t out = ugetw(base + 2);
+ uint16_t ex = ugetw(base + 4);
uint16_t sum = in | out | ex;
/* Timeout in 1/10th of a second (BSD api mangling done by libc) */
- udata.u_ptab->p_timeout = ugetw(base + 12);
+ /* 0 means return immediately, need to sort out a 'forever' FIXME */
+ udata.u_ptab->p_timeout = ugetw(base + 6);
do {
m = 1;
+
+ irq = di();
+
for (i = 0; i < nfd; i++) {
if (sum & m) {
if (in & m)
/* If unsupported we report the device as read/write ready */
if (d_ioctl
(ino->c_node.i_addr[0],
- SELECT_BEGIN, &n) == -1) {
+ seltype, &n) == -1) {
udata.u_error = 0;
n = SELECT_IN | SELECT_OUT;
- }
+ } else if (seltype == SELECT_BEGIN)
+ selwait_inode(ino, n, 1);
setbits:
/* Set the outputs */
if (n & SELECT_IN)
inr &= in; /* Don't reply with bits not being selected */
outr &= out;
exr &= ex;
+ /* FIXME lock against time race */
sumo = inr | outr | exr; /* Are we there yet ? */
- if (!sumo
- && psleep_flags(&udata.u_ptab->p_timeout, 0) == -1)
+ /* No successes, wait requested, not timed out */
+ if (!sumo && udata.u_ptab->p_timeout > 1 &&
+ psleep_flags(&udata.u_ptab->p_timeout, 0) == -1)
break;
+ irqrestore(irq);
+ seltype = SELECT_TEST;
}
- while (!sumo && udata.u_ptab->p_timeout != 1);
+ while (!sumo && udata.u_ptab->p_timeout > 1);
+
+ irqrestore(irq);
udata.u_ptab->p_timeout = 0;
/* Return the values to user space */
uputw(inr, base);
- uputw(outr, base + 4);
- uputw(exr, base + 8);
+ uputw(outr, base + 2);
+ uputw(exr, base + 4);
/* Tell the device less people care, we may want to remove all this
and just select check. The 0 check we could do instead is cheap */
case MODE_R(F_CDEV):
d_ioctl(ino->c_node.i_addr[0], SELECT_END,
NULL);
+ /* Kill the wait */
+ selwait_inode(ino, SELECT_IN|SELECT_OUT|SELECT_EX, 0);
+ /* May be unknown in which case ignore */
+ udata.u_error = 0;
break;
case MODE_R(F_PIPE):
pipesel_end(ino);
struct tty ttydata[NUM_DEV_TTY + 1]; /* ttydata[0] is not used */
+#ifdef CONFIG_LEVEL_2
+static uint16_t tty_select; /* Fast path if no selects, could do with being per tty ? */
+
+/* Might be worth tracking tty minor <> inode for performance FIXME */
+static void tty_selwake(uint8_t minor, uint16_t event)
+{
+ if (tty_select) {
+ /* 2 is the tty devices */
+ selwake_dev(2, minor, event);
+ }
+}
+#else
+#define tty_selwake(a,b) do {} while(0)
+#endif
+
int tty_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
{
usize_t nread;
int tty_ioctl(uint8_t minor, uarg_t request, char *data)
{ /* Data in User Space */
struct tty *t;
+
if (minor > NUM_DEV_TTY + 1) {
udata.u_error = ENODEV;
return -1;
}
t = &ttydata[minor];
+
+ /* Special case select ending after a hangup */
+#ifdef CONFIG_LEVEL_2
+ if (request == SELECT_END) {
+ tty_select--;
+ return 0;
+ }
+#endif
+
if (jobcontrol_ioctl(minor, t, request))
return -1;
if (t->flag & TTYF_DEAD) {
if (uget(data, &t->termios, sizeof(struct termios)) == -1)
return -1;
tty_setup(minor);
+ tty_selwake(minor, SELECT_IN|SELECT_OUT);
break;
case TIOCINQ:
return uput(&ttyinq[minor].q_count, data, 2);
case TIOCFLUSH:
clrq(&ttyinq[minor]);
+ tty_selwake(minor, SELECT_OUT);
break;
case TIOCHANGUP:
tty_hangup(minor);
break;
case TIOCOSTART:
t->flag &= ~TTYF_STOP;
+ tty_selwake(minor, SELECT_OUT);
break;
case TIOCGWINSZ:
return uput(&t->winsize, data, sizeof(struct winsize));
return -1;
}
return tcsetpgrp(t, data);
+ case SELECT_BEGIN:
+ tty_select++;
+ /* Fall through */
+ case SELECT_TEST:
+ {
+ uint8_t n = *data;
+ *data = 0;
+ if (n & SELECT_EX) {
+ if (t->flag & TTYF_DEAD) {
+ *data = SELECT_IN|SELECT_EX;
+ return 0;
+ }
+ }
+ /* FIXME: IRQ race */
+ if (n & SELECT_IN) {
+ /* TODO - this one is hard, we need to peek down the queue to see if
+ a canonical input would succeed */
+ }
+ if (n & SELECT_OUT) {
+ if ((!(t->flag & TTYF_STOP)) &&
+ tty_writeready(minor) != TTY_READY_LATER)
+ *data |= SELECT_OUT;
+ }
+ return 0;
+ }
#endif
default:
udata.u_error = ENOTTY;
if (c == t->termios.c_cc[VSTART]) { /* ^Q */
t->flag &= ~TTYF_STOP;
wakeup(&t->flag);
+ tty_selwake(minor, SELECT_OUT);
return 1;
}
}
tty_putc(minor, '\007'); /* Beep if no more room */
if (!canon || c == t->termios.c_cc[VEOL] || c == '\n'
- || c == t->termios.c_cc[VEOF])
+ || c == t->termios.c_cc[VEOF]) {
wakeup(q);
+ tty_selwake(minor, SELECT_IN);
+ }
return wr;
eraseout:
void tty_outproc(uint8_t minor)
{
wakeup(&ttydata[minor]);
+ tty_selwake(minor, SELECT_OUT);
}
void tty_echo(uint8_t minor, unsigned char c)
/* Wake stopped stuff */
wakeup(&t->flag);
/* and deadflag will clear when the last user goes away */
+ tty_selwake(minor, SELECT_IN|SELECT_OUT|SELECT_EX);
}
void tty_carrier_drop(uint8_t minor)
void tty_carrier_raise(uint8_t minor)
{
wakeup(&ttydata[minor].termios.c_cflag);
+ tty_selwake(minor, SELECT_IN|SELECT_OUT);
}
/*
struct s_queue q = &ptyq[minor + PTY_OFFSET + PTY_PAIR];
/* tty output queue to pty */
insq(q, c);
+ /* FIXME: select */
wakeup(q);
}
#endif