예제 #1
0
static unsigned char add_switch_cap(dterm_t* dt, int fd)
{
    dterm_mark_t prop;
    dterm_mark_t c_list;
#ifndef SW_CNT
#define SW_CNT SW_MAX
#endif
    unsigned char bitmask[SW_CNT / 8 + 1];
    int i = 0;

    if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(bitmask)), bitmask) < 0) {
        return IEDRV_RES_IO_ERROR;
    }

    dterm_tuple_begin(dt, &prop);
    dterm_atom(dt, ie_switch);
    dterm_list_begin(dt, &c_list);

    // Add all capabilities present
    for(i = 0; i < sizeof(sw_cap_map) / sizeof(sw_cap_map[0]); ++i)  {
        if (t_bit(bitmask, sw_cap_map[i].bit)) {
            dterm_mark_t cap_tuple;
            dterm_tuple_begin(dt, &cap_tuple);
            dterm_atom(dt, sw_cap_map[i].atom);
            dterm_tuple_end(dt, &cap_tuple);
        }
    }

    dterm_list_end(dt, &c_list);
    dterm_tuple_end(dt, &prop);

    return IEDRV_RES_OK;
}
예제 #2
0
static unsigned char add_sync_cap(dterm_t* dt, int fd)
{
    dterm_mark_t prop;
    dterm_mark_t c_list;
    unsigned char bitmask[1];  // 4 SYN events.
    int i = 0;

    if (ioctl(fd, EVIOCGBIT(EV_SYN, sizeof(bitmask)), bitmask) < 0) {
        return IEDRV_RES_IO_ERROR;
    }

    dterm_tuple_begin(dt, &prop);
    dterm_atom(dt, ie_sync);
    dterm_list_begin(dt, &c_list);

    // Add all capabilities present
    for(i = 0; i < sizeof(sync_cap_map) / sizeof(sync_cap_map[0]); ++i)  {
        if (t_bit(bitmask, sync_cap_map[i].bit)) {
            dterm_mark_t cap_tuple;
            dterm_tuple_begin(dt, &cap_tuple);
            dterm_atom(dt, sync_cap_map[i].atom);
            dterm_tuple_end(dt, &cap_tuple);
        }
    }

    dterm_list_end(dt, &c_list);
    dterm_tuple_end(dt, &prop);

    return IEDRV_RES_OK;
}
예제 #3
0
static unsigned char add_key_cap(dterm_t* dt, int fd)
{
    dterm_mark_t prop;
    dterm_mark_t c_list;
#ifndef KEY_CNT
#define KEY_CNT KEY_UNKNOWN
#endif
    unsigned char bitmask[KEY_CNT / 8 + 1];

    int i = 0;


    if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask) < 0) {
        return IEDRV_RES_IO_ERROR;
    }

    dterm_tuple_begin(dt, &prop);
    dterm_atom(dt, ie_key);
    dterm_list_begin(dt, &c_list);

    // Add all capabilities present
    for(i = 0; i < sizeof(key_cap_map) / sizeof(key_cap_map[0]); ++i)
        if (t_bit(bitmask, key_cap_map[i].bit)) {
            dterm_mark_t cap_tuple;
            dterm_tuple_begin(dt, &cap_tuple);
            dterm_atom(dt, key_cap_map[i].atom);
            dterm_int(dt, key_cap_map[i].bit);
            dterm_tuple_end(dt, &cap_tuple);
        }

    dterm_list_end(dt, &c_list);
    dterm_tuple_end(dt, &prop);

    return IEDRV_RES_OK;
}
예제 #4
0
static unsigned char add_abs_cap(dterm_t* dt, int fd)
{
    dterm_mark_t prop;
    dterm_mark_t c_list;
    unsigned char bitmask[KEY_CNT / 8 + 1];

    int i = 0;


    if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bitmask)), bitmask) < 0) {
        return IEDRV_RES_IO_ERROR;
    }

    dterm_tuple_begin(dt, &prop);
    dterm_atom(dt, ie_abs);
    dterm_list_begin(dt, &c_list);

    // Add all capabilities present.
    // Each element will have the format
    // { Cap (abs_x), { CurrentVal, Min, Max, Fuzz, Flat, Resolution }}
    for(i = 0; i < sizeof(abs_cap_map) / sizeof(abs_cap_map[0]); ++i) {
        struct input_absinfo abs_info;
        dterm_mark_t a_prop;

        if (!t_bit(bitmask, abs_cap_map[i].bit))
            continue;

        if (ioctl(fd, EVIOCGABS(abs_cap_map[i].bit), &abs_info) < 0) {
            continue;
        }

        dterm_tuple_begin(dt, &a_prop);{
            dterm_mark_t cap_tuple;
            dterm_tuple_begin(dt, &cap_tuple);
            dterm_atom(dt, abs_cap_map[i].atom);
            dterm_atom(dt, ie_absinfo);
            dterm_int(dt, abs_info.value);
            dterm_int(dt, abs_info.minimum);
            dterm_int(dt, abs_info.maximum);
            dterm_int(dt, abs_info.fuzz);
            dterm_int(dt, abs_info.flat);
//            dterm_int(dt, abs_info.resolution);  // Not available in ARM7
            dterm_tuple_end(dt, &cap_tuple);
        }
        dterm_tuple_end(dt, &a_prop);
    }

    dterm_list_end(dt, &c_list);
    dterm_tuple_end(dt, &prop);

    return IEDRV_RES_OK;
}
예제 #5
0
파일: dterm.c 프로젝트: relabsoss/uart
void dterm_kv_bool(dterm_t* t,ErlDrvTermData key, int value)
{
    dterm_mark_t m;
    dterm_tuple_begin(t, &m); {
	dterm_atom(t, key);
	dterm_atom(t, value ? am_true : am_false);
    }
    dterm_tuple_end(t, &m);
}
예제 #6
0
파일: dterm.c 프로젝트: relabsoss/uart
void dterm_kv_atom(dterm_t* t,ErlDrvTermData key, ErlDrvTermData value)
{
    dterm_mark_t m;
    dterm_tuple_begin(t, &m); {
	dterm_atom(t, key);
	dterm_atom(t, value);
    }
    dterm_tuple_end(t, &m);
}
예제 #7
0
파일: dterm.c 프로젝트: relabsoss/uart
void dterm_kv_string(dterm_t* t,ErlDrvTermData key, char* value)
{
    dterm_mark_t m;
    size_t len = strlen(value);
    char* dst = (char*) dterm_link_copy_data(t, value, len);

    dterm_tuple_begin(t, &m); {
	dterm_atom(t, key);
	dterm_string(t, dst, len);
    }
    dterm_tuple_end(t, &m);
}
예제 #8
0
파일: dthread.c 프로젝트: relabsoss/uart
// send {Ref, {error,Reason}}
int dthread_port_send_error(dthread_t* thr, dthread_t* source, 
			    ErlDrvTermData target,
			    ErlDrvTermData ref, int error)
{
    dterm_t t;
    dterm_mark_t m,e;
    int r;

    dterm_init(&t);
    dterm_tuple_begin(&t, &m); {
	dterm_int(&t, ref);
	dterm_tuple_begin(&t, &e); {
	    dterm_atom(&t, am_error);
	    dterm_atom(&t, error_atom(error));
	}
	dterm_tuple_end(&t,&e);
    }
    dterm_tuple_end(&t,&m);
	
    r = dthread_port_send_term(thr, source, target,
			       dterm_data(&t), dterm_used_size(&t));
    dterm_finish(&t);
    return r;    
}
예제 #9
0
static void inpevt_ready_input(ErlDrvData drv_data, ErlDrvEvent event)
{
    IEContext* ctx = 0;
    struct input_event buf[32];
    ssize_t rd_res = 0;

    ctx = (IEContext*) drv_data;

    if (ctx->mDescriptor == -1) {
        return;
    }


    while((rd_res = read(ctx->mDescriptor, (char*) buf, sizeof(buf))) > 0) {
        int i = 0;
        for (i = 0; i < rd_res / sizeof(buf[0]); ++i) {
            dterm_t dt;
            dterm_mark_t ev_mark;

            dterm_init(&dt);
            dterm_tuple_begin(&dt, &ev_mark);
            dterm_atom(&dt, ie_input_event);
            dterm_port(&dt, ctx->mDport);
            dterm_int(&dt, buf[i].time.tv_sec);
            dterm_int(&dt, buf[i].time.tv_usec);
            dterm_atom(&dt, *type_atoms[buf[i].type].atom);

            if (type_atoms[buf[i].type].code)
                dterm_atom(&dt, *type_atoms[buf[i].type].code[buf[i].code]);
            else
                dterm_atom(&dt, ie_unknown);

            dterm_int(&dt, buf[i].code);

            dterm_int(&dt, buf[i].value);

            dterm_tuple_end(&dt, &ev_mark);

            driver_output_term(ctx->mPort,
                               dterm_data(&dt),
                               dterm_used_size(&dt));

            dterm_finish(&dt);
        }
    }
    return;
}
예제 #10
0
static unsigned char send_device_info(IEContext* ctx, unsigned int reply_id)
{
    dterm_mark_t msg;
    dterm_t dt;
    int len = 0;
    char name[256];
    char topology[256];
    char uniq_id[256];

    struct input_id id;

    // Get device id.
    if (ioctl(ctx->mDescriptor, EVIOCGID, &id) < 0)
    {
        return IEDRV_RES_IO_ERROR;
    }

    if ((len = ioctl(ctx->mDescriptor, EVIOCGNAME(sizeof(name) - 1), name)) < 0) {
        return IEDRV_RES_IO_ERROR;
    }
    name[len] = 0;

    if ((len = ioctl(ctx->mDescriptor, EVIOCGPHYS(sizeof(topology) - 1), topology)) < 0) {
        return IEDRV_RES_IO_ERROR;
    }
    topology[len] = 0;

    if ((len = ioctl(ctx->mDescriptor, EVIOCGUNIQ(sizeof(uniq_id) - 1), uniq_id)) < 0)
        uniq_id[0] = 0;

    uniq_id[len] = 0;

    dterm_init(&dt);

    dterm_tuple_begin(&dt, &msg); {
        dterm_mark_t prop;

        dterm_atom(&dt, ie_device_info);
        dterm_port(&dt, ctx->mDport);
        dterm_int(&dt, reply_id);


        //
        // Setup { id, Bustype, Vendor, Product, Version, Name}
        //
        dterm_tuple_begin(&dt, &prop); {
            dterm_atom(&dt, ie_drv_dev_id);
            dterm_string(&dt, uniq_id, strlen(uniq_id));
            dterm_string(&dt, name, strlen(name));
            dterm_atom(&dt, *bus_atoms[id.bustype]);
            dterm_int(&dt, id.vendor);
            dterm_int(&dt, id.product);
            dterm_int(&dt, id.version);
            dterm_string(&dt, topology, strlen(topology));

            //
            // Setup [{ capability, [ { Cap, [X] }, { Cap, [Y] }, ...}, ...]
            //
            add_cap(&dt,  ctx->mDescriptor);
            dterm_tuple_end(&dt, &prop);
        }
    }
    dterm_tuple_end(&dt, &msg);
    driver_output_term(ctx->mPort, dterm_data(&dt), dterm_used_size(&dt));
    dterm_finish(&dt);


    return IEDRV_RES_OK;
}
예제 #11
0
파일: uart_unix.c 프로젝트: GENIVI/rvi_core
// thread main!
int uart_unix_main(void* arg)
{
    dthread_t* self = (dthread_t*) arg;
    dthread_t* other = (dthread_t*) self->arg;
    dmessage_t* mp = NULL;
    dthread_poll_event_t ev, *evp;
    size_t nev;
    dterm_t term;
    uart_ctx_t ctx;
    ErlDrvTermData mp_from;
    ErlDrvTermData mp_ref;
    dthread_t*     mp_source;
    int tmo;
    int r;

    DEBUGF("uart_unix: thread started");

    uart_init(&ctx, self, other);

    dterm_init(&term);

again_tmo:
    tmo = next_timeout(&ctx);
again:
    nev = 0;
    evp = NULL;
    if (ctx.fd >= 0) {
	ev.event = (ErlDrvEvent) ((long)ctx.fd);
	ev.events = 0;
	if ((ctx.option.active != UART_PASSIVE) || ctx.recv) {
	    ev.events |= ERL_DRV_READ;
	    if (ctx.option.ptypkt && (ctx.fd != ctx.tty_fd))
		ev.events |= ERL_DRV_EXCEP;
	}
	if (ctx.oq.mesg)
	    ev.events |= ERL_DRV_WRITE;
	if (ev.events) {
	    evp = &ev;
	    nev = 1;
	}
	DEBUGF("ctx.fd=%d, ev.events=%d", ctx.fd, ev.events);
    }

    DEBUGF("uart_unix_main: nev=%d, events=%x, timeout = %d", 
	   nev, ev.events, tmo);
    r = dthread_poll(self, evp, &nev, tmo);

    if (r < 0) {
	DEBUGF("uart_unix_main: dthread_poll failed=%d", r);
	goto again_tmo;
    }
    else {
	DEBUGF("uart_unix_main: nev=%d, r=%d", nev, r);

	if (evp && (nev == 1)) {
	    if (evp->revents & ERL_DRV_WRITE)
		process_output(&ctx, self);
	    if (evp->revents & (ERL_DRV_READ|ERL_DRV_EXCEP)) {
		while((process_input(&ctx, self, 0) == 1) && 
		      (ctx.option.active != UART_PASSIVE))
		    ;
	    }
	}
	tmo = next_timeout(&ctx);
	DEBUGF("uart_unix_main: timeout = %d", tmo);
	if (ctx.recv) {
	    if (tmo == 0) {
		uart_async_error_am(&ctx, ctx.dport, ctx.caller, am_timeout);
		clear_timeout(&ctx);
		ctx.remain = 0;
	    }
	}
	if (r == 0)
	    goto again;

	// r>0 (number of messages)
	DEBUGF("about to receive message r=%d", r);
	if ((mp = dthread_recv(self, NULL)) == NULL) {
	    DEBUGF("uart_unix_main: message was NULL");
	    goto again;
	}
	mp_from = mp->from;
	mp_ref  = mp->ref;
	mp_source = mp->source;

	switch (mp->cmd) {
	case DTHREAD_STOP:
	    DEBUGF("uart_unix_main: STOP");
	    close_device(&ctx);
	    uart_final(&ctx);
	    dmessage_free(mp);
	    DEBUGF("uart_unix_main: EXIT");
	    dthread_exit(0);
	    break;

	case DTHREAD_OUTPUT: // async send!
	    DEBUGF("uart_unix_main: OUTPUT");
	    if (ctx.fd < 0) {
		dmessage_free(mp);
		goto again;
	    }
	    if (enq_output(&ctx, self, mp, 0) < 0) {
		mp = NULL;
		goto error;
	    }
	    goto again;

	case UART_CMD_CONNECT: {
	    ErlDrvTermData owner;
	    if (mp->used != 0) goto badarg;
	    owner = driver_connected(self->port);
	    self->owner   = owner;
	    other->owner  = owner;
	    goto ok;
	}

	case UART_CMD_CLOSE:
	    DEBUGF("uart_unix_main: CLOSE");
	    close_device(&ctx);
	    goto ok;

	case UART_CMD_SEND: // sync send
	    DEBUGF("uart_unix_main: SEND");
	    if (ctx.fd < 0) goto ebadf;
	    if (enq_output(&ctx, self, mp, mp_from) < 0) {
		mp = NULL;
		goto error;
	    }
	    goto again;
	    
	case UART_CMD_SENDCHAR: // sync send
	    DEBUGF("uart_unix_main: SENDCHAR");
	    if (ctx.fd < 0) goto ebadf;
	    if (enq_output(&ctx, self, mp, mp_from) < 0) {
		mp = NULL;
		goto error;
	    }
	    goto again;

	case UART_CMD_RECV: {  // <<Time:32, Length:32>> Time=0xffffffff=inf
	    uint32_t tm;
	    int len;
	    DEBUGF("uart_unix_main: RECV");
	    if (ctx.fd < 0) goto ebadf;
	    if (ctx.recv) goto ealready;
	    if (mp->used != 8) goto badarg;
	    if (ctx.option.active != UART_PASSIVE) goto badarg;
	    tm = get_uint32((uint8_t*) mp->buffer);
	    len = (int) get_uint32((uint8_t*) (mp->buffer+4));
	    if ((len < 0) || (len > UART_MAX_PACKET_SIZE)) goto badarg;
	    ctx.ref = mp_ref;
	    ctx.caller = mp_from;
	    set_timeout(&ctx, tm);
	    ctx.recv = 1;
	    DEBUGF("recv timeout %lu", tm);
	    process_input(&ctx, self, len);
	    dmessage_free(mp);
	    goto again_tmo;
	}

	case UART_CMD_UNRECV: {  // argument is data to push back
	    uart_buf_push(&ctx.ib, mp->buffer, mp->used);
	    DEBUGF("unrecived %d bytes", ctx.ib.ptr - ctx.ib.ptr_start);
	    if (ctx.option.active != UART_PASSIVE) {
		while((process_input(&ctx, self, 0) == 1) && 
		      (ctx.option.active != UART_PASSIVE))
		    ;
	    }
	    goto ok;
	}

	case UART_CMD_SETOPTS: {
	    uart_com_state_t state  = ctx.state;
	    uart_opt_t       option = ctx.option;
	    uint32_t         sflags = ctx.sflags;

	    // parse & update options in state,option and sflag
	    if (uart_parse_opts(mp->buffer, mp->used, 
				&state, &option, &sflags) < 0)
		goto badarg;

	    //  apply the changed values
	    if ((r=apply_opts(&ctx, &state, &option, sflags)) < 0)
		goto error;

	    if (r == 1) {
		while((process_input(&ctx, self, 0) == 1) && 
		      (ctx.option.active != UART_PASSIVE))
		    ;
	    }
	    goto ok;
	}

	case UART_CMD_GETOPTS: {
	    dterm_mark_t m1;
	    dterm_mark_t m2;
	    // {Ref, {ok,List}} || {Ref, {error,Reason}}
	    dterm_tuple_begin(&term, &m1); {
		dterm_uint(&term, mp_ref);
		dterm_tuple_begin(&term, &m2); {
		    dterm_atom(&term, am_ok);
		    if (uart_get_opts(&term, &ctx,(uint8_t*)mp->buffer,mp->used) < 0) {
			dterm_reset(&term);
			goto badarg;
		    }
		}
		dterm_tuple_end(&term, &m2);
	    }
	    dterm_tuple_end(&term, &m1);
	    dthread_port_send_dterm(mp_source, self, mp_from, &term);
	    dterm_reset(&term);
	    dmessage_free(mp);
	    goto again;
	}

	case UART_CMD_GET_MODEM: {
	    dterm_mark_t m1;
	    dterm_mark_t m2;
	    uart_modem_state_t mstate;
	    if (ctx.tty_fd < 0) goto ebadf;
	    if (get_modem_state(ctx.tty_fd, &mstate) < 0) goto error;

	    dterm_tuple_begin(&term, &m1); {
		dterm_uint(&term, mp_ref);
		dterm_tuple_begin(&term, &m2); {
		    dterm_atom(&term, am_ok);
		    modem_state_dterm(&term, mstate);
		}
		dterm_tuple_end(&term, &m2);
	    }
	    dterm_tuple_end(&term, &m1);
	    dthread_port_send_dterm(mp_source, self, mp_from, &term);
	    dterm_reset(&term);
	    dmessage_free(mp);
	    goto again;
	}

	case UART_CMD_SET_MODEM: {
	    uart_modem_state_t mstate;	    
	    if (ctx.tty_fd < 0) goto ebadf;
	    if (mp->used != 4) goto badarg;
	    mstate = (uart_modem_state_t) get_uint32((uint8_t*) mp->buffer);
	    if (set_modem_state(ctx.tty_fd, mstate, 1) < 0) goto error;
	    goto ok;
	}

	case UART_CMD_CLR_MODEM: {
	    uart_modem_state_t mstate;
	    if (ctx.tty_fd < 0) goto ebadf;
	    if (mp->used != 4) goto badarg;
	    mstate = (uart_modem_state_t) get_uint32((uint8_t*) mp->buffer);
	    if (set_modem_state(ctx.tty_fd, mstate, 0) < 0) goto error;
	    goto ok;
	}
		
	case UART_CMD_HANGUP: {
	    struct termios tio;
	    int r;
	    if (ctx.tty_fd < 0) goto ebadf;
	    if (mp->used != 0) goto badarg;
	    if ((r = tcgetattr(ctx.tty_fd, &tio)) < 0) {
		INFOF("tcgetattr: error=%s\n", strerror(errno));
		goto badarg;
	    }
	    cfsetispeed(&tio, B0);
	    cfsetospeed(&tio, B0);
	    if ((r = tcsetattr(ctx.tty_fd, TCSANOW, &tio)) < 0) {
		INFOF("tcsetattr: error=%s\n", strerror(errno));
		goto badarg;		
	    }
	    goto ok;
	}
		
	case UART_CMD_BREAK: {
	    int duration;
	    if (ctx.tty_fd < 0) goto ebadf;
	    if (mp->used != 4) goto badarg;
	    duration = (int) get_uint32((uint8_t*) mp->buffer);
	    if (tcsendbreak(ctx.tty_fd, duration) < 0)
		goto error;
	    goto ok;
	}
	    
	case UART_CMD_FLOW:
	    if (ctx.tty_fd < 0) goto ebadf;
	    if (mp->used != 1) goto badarg;
	    switch(mp->buffer[0]) {
	    case 0: r = tcflow(ctx.tty_fd, TCIOFF); break;
	    case 1: r = tcflow(ctx.tty_fd, TCION); break;
	    case 2: r = tcflow(ctx.tty_fd, TCOOFF); break;
	    case 3: r = tcflow(ctx.tty_fd, TCOON); break;
	    default: goto badarg; break;
	    }
	    if (r < 0)
		goto error;
	    goto ok;

	default:
	    goto badarg;
	}
    }

ok:
    dthread_port_send_ok(mp_source, self,  mp_from, mp_ref);
    if (mp) dmessage_free(mp);
    goto again;

ebadf:
    errno = EBADF;
    goto error;
badarg:
    errno = EINVAL;
    goto error;
ealready:
    errno = EALREADY;
    goto error;

error:
    dthread_port_send_error(mp_source, self, mp_from, mp_ref,
			    uart_errno(&ctx));
    if (mp) dmessage_free(mp);
    goto again;
}
예제 #12
0
// thread main!
int uart_win32_main(void* arg)
{
    dthread_t* self = (dthread_t*) arg;
    dthread_t* other = (dthread_t*) self->arg;
    dmessage_t* mp = NULL;
    dthread_poll_event_t ev[3];
    dthread_poll_event_t* evp;
    size_t nev;
    dterm_t term;
    uart_ctx_t ctx;
    ErlDrvTermData mp_from;
    ErlDrvTermData mp_ref;
    dthread_t*     mp_source;
    int tmo;
    int r;

    DEBUGF("uart_win32: thread started");

    uart_init(&ctx, self, other);

    dterm_init(&term);

again_tmo:
    tmo = next_timeout(&ctx);
again:
    nev = 0;

    if (ctx.writing) {
	ev[nev].event = (ErlDrvEvent) ctx.out.hEvent;
	ev[nev].events = ERL_DRV_READ; // yepp, even for write
	nev++;
    }

    while(!ctx.reading && (ctx.recv || (ctx.option.active != UART_PASSIVE)))
	process_input(&ctx, self, 0);

    if (ctx.reading) {
	ev[nev].event = (ErlDrvEvent) ctx.in.hEvent;
	ev[nev].events = ERL_DRV_READ;
	nev++;
    }

    evp = nev ? &ev[0] : NULL;

    DEBUGF("uart_win32_main: ctx.fh=%d, nev=%u, timeout = %d", 
	   ctx.fh, nev, tmo);
    r = dthread_poll(self, evp, &nev, tmo);

    if (r < 0) {
	DEBUGF("uart_win32_main: dthread_poll failed=%d", r);
	goto again_tmo;
    }
    else {
	DWORD i;
	DEBUGF("uart_win32_main: nev=%u, r=%d", nev, r);
	for (i = 0; i < nev; i++) {
	    if (ev[i].revents & ERL_DRV_READ) {
		if (ev[i].event == (ErlDrvEvent) ctx.in.hEvent) {
		    while((process_input(&ctx, self, 0) == 1) && 
			  (ctx.option.active != UART_PASSIVE))
			;
		}
		else if (ev[i].event == (ErlDrvEvent) ctx.out.hEvent) {
		    process_output(&ctx, self);
		}
	    }
	}
	tmo = next_timeout(&ctx);
	DEBUGF("uart_win32_main: timeout = %d", tmo);
	if (ctx.recv) {
	    if (tmo == 0) {
		uart_async_error_am(&ctx, ctx.dport, ctx.caller, am_timeout);
		clear_timeout(&ctx);
		ctx.remain = 0;
	    }
	}
	if (r == 0)
	    goto again;

	// r>0 (number of messages)
	DEBUGF("about to receive message r=%d", r);
	if ((mp = dthread_recv(self, NULL)) == NULL) {
	    DEBUGF("uart_win32_main: message was NULL");
	    goto again;
	}
	mp_from = mp->from;
	mp_ref  = mp->ref;
	mp_source = mp->source;

	switch (mp->cmd) {
	case DTHREAD_STOP:
	    DEBUGF("uart_win32_main: STOP");
	    close_device(&ctx);
	    uart_final(&ctx);
	    dmessage_free(mp);
	    DEBUGF("uart_win32_main: EXIT");
	    dthread_exit(0);
	    break;

	case DTHREAD_OUTPUT: // async send!
	    DEBUGF("uart_win32_main: OUTPUT");
	    if (ctx.fh == INVALID_HANDLE_VALUE) {
		dmessage_free(mp);
		goto again;
	    }
	    if (enq_output(&ctx, self, mp, 0) < 0) {
		mp = NULL;
		goto error;
	    }
	    goto again;

	case UART_CMD_CONNECT: {
	    ErlDrvTermData owner;
	    if (mp->used != 0) goto badarg;
	    owner = driver_connected(self->port);
	    self->owner   = owner;
	    other->owner  = owner;
	    goto ok;
	}

	case UART_CMD_CLOSE:
	    DEBUGF("uart_win32_main: CLOSE");
	    close_device(&ctx);
	    goto ok;

	case UART_CMD_SEND: // sync send
	    DEBUGF("uart_win32_main: SEND");
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (enq_output(&ctx, self, mp, mp_from) < 0) {
		mp = NULL;
		goto error;
	    }
	    goto again;
	    
	case UART_CMD_SENDCHAR: // sync send
	    DEBUGF("uart_win32_main: SENDCHAR");
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (enq_output(&ctx, self, mp, mp_from) < 0) {
		mp = NULL;
		goto error;
	    }
	    goto again;

	case UART_CMD_RECV: {  // <<Time:32, Length:32>> Time=0xffffffff=inf
	    uint32_t tm;
	    int len;
	    DEBUGF("uart_win32_main: RECV");
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (ctx.recv) goto ealready;
	    if (mp->used != 8) goto badarg;
	    if (ctx.option.active != UART_PASSIVE) goto badarg;
	    tm = get_uint32((uint8_t*) mp->buffer);
	    len = (int) get_uint32((uint8_t*) (mp->buffer+4));
	    if ((len < 0) || (len > UART_MAX_PACKET_SIZE)) goto badarg;
	    ctx.ref = mp_ref;
	    ctx.caller = mp_from;
	    set_timeout(&ctx, tm);
	    ctx.recv = 1;
	    DEBUGF("recv timeout %lu", tm);
	    process_input(&ctx, self, len);
	    dmessage_free(mp);
	    goto again_tmo;
	}

	case UART_CMD_UNRECV: {  // argument is data to push back
	    uart_buf_push(&ctx.ib, mp->buffer, mp->used);
	    DEBUGF("unrecived %d bytes", ctx.ib.ptr - ctx.ib.ptr_start);
	    if (ctx.option.active != UART_PASSIVE) {
		while((process_input(&ctx, self, 0) == 1) && 
		      (ctx.option.active != UART_PASSIVE))
		    ;
	    }
	    goto ok;
	}

	case UART_CMD_SETOPTS: {
	    uart_com_state_t state  = ctx.state;
	    uart_opt_t       option = ctx.option;
	    uint32_t         sflags = ctx.sflags;

	    // parse & update options in state,option and sflag
	    if (uart_parse_opts(mp->buffer, mp->used, 
				&state, &option, &sflags) < 0)
		goto badarg;

	    //  apply the changed values
	    if ((r=apply_opts(&ctx, &state, &option, sflags)) < 0) {
		goto error;
	    }
	    goto ok;
	}

	case UART_CMD_GETOPTS: {
	    dterm_mark_t m1;
	    dterm_mark_t m2;
	    // {Ref, {ok,List}} || {Ref, {error,Reason}}
	    dterm_tuple_begin(&term, &m1); {
		dterm_uint(&term, mp_ref);
		dterm_tuple_begin(&term, &m2); {
		    dterm_atom(&term, am_ok);
		    if (uart_get_opts(&term, &ctx,(uint8_t*)mp->buffer,mp->used) < 0) {
			dterm_reset(&term);
			goto badarg;
		    }
		}
		dterm_tuple_end(&term, &m2);
	    }
	    dterm_tuple_end(&term, &m1);
	    dthread_port_send_dterm(mp_source, self, mp_from, &term);
	    dterm_reset(&term);
	    dmessage_free(mp);
	    goto again;
	}

	case UART_CMD_GET_MODEM: {
	    dterm_mark_t m1;
	    dterm_mark_t m2;
	    uart_modem_state_t mstate;
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (get_modem_state(ctx.fh, &mstate) < 0) goto error;

	    dterm_tuple_begin(&term, &m1); {
		dterm_uint(&term, mp_ref);
		dterm_tuple_begin(&term, &m2); {
		    dterm_atom(&term, am_ok);
		    modem_state_dterm(&term, mstate);
		}
		dterm_tuple_end(&term, &m2);
	    }
	    dterm_tuple_end(&term, &m1);
	    dthread_port_send_dterm(mp_source, self, mp_from, &term);
	    dterm_reset(&term);
	    dmessage_free(mp);
	    goto again;
	}

	case UART_CMD_SET_MODEM: {
	    uart_modem_state_t mstate;	    
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (mp->used != 4) goto badarg;
	    mstate = (uart_modem_state_t) get_uint32((uint8_t*) mp->buffer);
	    if (set_modem_state(ctx.fh, mstate, 1) < 0) goto error;
	    goto ok;
	}

	case UART_CMD_CLR_MODEM: {
	    uart_modem_state_t mstate;
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (mp->used != 4) goto badarg;
	    mstate = (uart_modem_state_t) get_uint32((uint8_t*) mp->buffer);
	    if (set_modem_state(ctx.fh, mstate, 0) < 0) goto error;
	    goto ok;
	}
		
	case UART_CMD_HANGUP: {
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (mp->used != 0) goto badarg;
	    // FIXME?
	    goto ok;
	}
		
	case UART_CMD_BREAK: {
	    int duration;
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (mp->used != 4) goto badarg;
	    duration = (int) get_uint32((uint8_t*) mp->buffer);
	    if (!EscapeCommFunction(ctx.fh, SETBREAK)) {
		DEBUG_ERROR("EscapeCommFunction: error %d", GetLastError());
		goto error;
	    }
	    Sleep(duration);
	    if (!EscapeCommFunction(ctx.fh, CLRBREAK)) {
		DEBUG_ERROR("EscapeCommFunction: error %d", GetLastError());
		goto error;
	    }
	    goto ok;
	}

	case UART_CMD_FLOW:
	    if (ctx.fh == INVALID_HANDLE_VALUE) goto ebadf;
	    if (mp->used != 1) goto badarg;
	    switch(mp->buffer[0]) {
	    case 0:
		if (!EscapeCommFunction(ctx.fh, SETXOFF)) {
		    DEBUG_ERROR("EscapeCommFunction: error %d", GetLastError());
		    goto error;
		}
		break;
	    case 1:
		if (!EscapeCommFunction(ctx.fh, SETXON)) {
		    DEBUG_ERROR("EscapeCommFunction: error %d", GetLastError());
		    goto error;
		}
		break;
	    case 2:
		// TransmitCommChar(ctx.fh, XOFF);
		break;
	    case 3:
		// TransmitCommChar(ctx.fh, XON);
		break;
	    default: 
		goto badarg;
	    }
	    goto ok;

	default:
	    goto badarg;
	}
    }

ok:
    dthread_port_send_ok(mp_source, self,  mp_from, mp_ref);
    if (mp) dmessage_free(mp);
    goto again;

ebadf:
    errno = EBADF;
    goto error;
badarg:
    errno = EINVAL;
    goto error;
ealready:
    errno = EBUSY;
    goto error;

error:
    dthread_port_send_error(mp_source, self, mp_from, mp_ref,
			    uart_errno(&ctx));
    if (mp) dmessage_free(mp);
    goto again;
}