Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <bio.h>

#include "modem.h"

typedef struct {
        char    *terse;
        char    *verbose;
        int     result;
        int     (*f)(Modem*);
} ResultCode;

static ResultCode results[] = {
        { "0",   "OK",           Rok,            0, },
        { "1",  "CONNECT",      Rconnect,       0, },
        { "2",  "RING",         Rring,          0, },
        { "3",  "NO CARRIER",   Rfailure,       0, },
        { "4",  "ERROR",        Rrerror,        0, },
        { "5",  "CONNECT 1200", Rconnect,       0, },
        { "6",  "NO DIALTONE",  Rfailure,       0, },
        { "7",  "BUSY",         Rfailure,       0, },
        { "8",  "NO ANSWER",    Rfailure,       0, },
        { "9",  "CONNECT 2400", Rconnect,       0, },           /* MT1432BA */
        { "10", "CONNECT 2400", Rconnect,       0, },           /* Hayes */
        { "11", "CONNECT 4800", Rconnect,       0, },
        { "12", "CONNECT 9600", Rconnect,       0, },
        { "13", "CONNECT 14400",Rconnect,       0, },
        { "23", "CONNECT 1275", Rconnect,       0, },           /* MT1432BA */

        { "-1", "+FCON",        Rcontinue,      fcon, },
        { "-1", "+FTSI",        Rcontinue,      ftsi, },
        { "-1", "+FDCS",        Rcontinue,      fdcs, },
        { "-1", "+FCFR",        Rcontinue,      fcfr, },
        { "-1", "+FPTS",        Rcontinue,      fpts, },
        { "-1", "+FET",         Rcontinue,      fet, },
        { "-1", "+FHNG",        Rcontinue,      fhng, },

        { 0 },
};

void
initmodem(Modem *m, int fd, int cfd, char *type, char *id)
{
        m->fd = fd;
        m->cfd = cfd;
        if(id == 0)
                id = "Plan 9";
        m->id = id;
        m->t = type;
}

int
rawmchar(Modem *m, char *p)
{
        Dir *d;
        int n;

        if(m->icount == 0)
                m->iptr = m->ibuf;

        if(m->icount){
                *p = *m->iptr++;
                m->icount--;
                return Eok;
        }

        m->iptr = m->ibuf;

        if((d = dirfstat(m->fd)) == nil){
                verbose("rawmchar: dirfstat: %r");
                return seterror(m, Esys);
        }
        n = d->length;
        free(d);
        if(n == 0)
                return Enoresponse;

        if(n > sizeof(m->ibuf)-1)
                n = sizeof(m->ibuf)-1;
        if((m->icount = read(m->fd, m->ibuf, n)) <= 0){
                verbose("rawmchar: read: %r");
                m->icount = 0;
                return seterror(m, Esys);
        }
        *p = *m->iptr++;
        m->icount--;

        return Eok;
}

int
getmchar(Modem *m, char *buf, long timeout)
{
        int r, t;

        timeout += time(0);
        while((t = time(0)) <= timeout){
                switch(r = rawmchar(m, buf)){

                case Eok:
                        return Eok;

                case Enoresponse:
                        sleep(100);
                        continue;

                default:
                        return r;
                }
        }
        verbose("getmchar: time %ud, timeout %ud", t, timeout);

        return seterror(m, Enoresponse);
}

int
putmchar(Modem *m, char *p)
{
        if(write(m->fd, p, 1) < 0)
                return seterror(m, Esys);
        return Eok;
}

/*
 *  lines terminate with cr-lf
 */
static int
getmline(Modem *m, char *buf, int len, long timeout)
{
        int r, t;
        char *e = buf+len-1;
        char last = 0;

        timeout += time(0);
        while((t = time(0)) <= timeout){
                switch(r = rawmchar(m, buf)){

                case Eok:
                        /* ignore ^s ^q which are used for flow */
                        if(*buf == '\021' || *buf == '\023')
                                continue;
                        if(*buf == '\n'){
                                /* ignore nl if its not with a cr */
                                if(last == '\r'){
                                        *buf = 0;
                                        return Eok;
                                }
                                continue;
                        }
                        last = *buf;
                        if(*buf == '\r')
                                continue;
                        buf++;
                        if(buf == e){
                                *buf = 0;
                                return Eok;
                        }
                        continue;

                case Enoresponse:
                        sleep(100);
                        continue;

                default:
                        return r;
                }
        }
        verbose("getmline: time %ud, timeout %ud", t, timeout);

        return seterror(m, Enoresponse);
}

int
command(Modem *m, char *s)
{
        verbose("m->: %s", s);
        if(fprint(m->fd, "%s\r", s) < 0)
                return seterror(m, Esys);
        return Eok;
}

/*
 * Read till we see a message or we time out.
 * BUG: line lengths not checked;
 *      newlines
 */
int
response(Modem *m, int timeout)
{
        int r;
        ResultCode *rp;

        while(getmline(m, m->response, sizeof(m->response), timeout) == Eok){
                if(m->response[0] == 0)
                        continue;
                verbose("<-m: %s", m->response);
                for(rp = results; rp->terse; rp++){
                        if(strncmp(rp->verbose, m->response, strlen(rp->verbose)))
                                continue;
                        r = rp->result;
                        if(rp->f && (r = (*rp->f)(m)) == Rcontinue)
                                break;
                        return r;
                }
        }

        m->response[0] = 0;
        return Rnoise;
}

void
xonoff(Modem *m, int i)
{
        char buf[8];

        sprint(buf, "x%d", i);
        i = strlen(buf);
        write(m->cfd, buf, i);
}