#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <sys/mbuf.h>
#include <sys/queue.h>
#include <sys/rman.h>
#include <machine/clock.h>
#include <bus/pci/pcireg.h>
#include <bus/pci/pcivar.h>
#include "pci_if.h"
#include <netgraph/ng_message.h>
#include <netgraph/netgraph.h>
#include <vm/vm.h>
#include <vm/pmap.h>
static MALLOC_DEFINE(M_MUSYCC, "musycc", "MUSYCC related");
static int maxlatency = 250;
SYSCTL_INT(_debug, OID_AUTO, musycc_maxlatency, CTLFLAG_RW, &maxlatency, 0,
"The number of milliseconds a packet is allowed to spend in the output queue. "
"If the output queue is longer than this number of milliseconds when the packet "
"arrives for output, the packet will be dropped."
);
static int debug = 0;
SYSCTL_INT(_debug, OID_AUTO, musycc_debug, CTLFLAG_RW, &debug, 0, "");
struct softc;
static void init_8370(struct softc *sc);
static u_int32_t parse_ts(const char *s, int *nbit);
static devclass_t musycc_devclass;
struct groupr {
u_int32_t thp[32];
u_int32_t tmp[32];
u_int32_t rhp[32];
u_int32_t rmp[32];
u_int8_t ttsm[128];
u_int8_t tscm[256];
u_int32_t tcct[32];
u_int8_t rtsm[128];
u_int8_t rscm[256];
u_int32_t rcct[32];
u_int32_t __glcd;
u_int32_t __iqp;
u_int32_t __iql;
u_int32_t grcd;
u_int32_t mpd;
u_int32_t mld;
u_int32_t pcd;
u_int32_t __rbist;
u_int32_t __tbist;
};
struct globalr {
u_int32_t gbp;
u_int32_t dacbp;
u_int32_t srd;
u_int32_t isd;
u_int32_t __thp[28];
u_int32_t __tmp[32];
u_int32_t __rhp[32];
u_int32_t __rmp[32];
u_int8_t ttsm[128];
u_int8_t tscm[256];
u_int32_t tcct[32];
u_int8_t rtsm[128];
u_int8_t rscm[256];
u_int32_t rcct[32];
u_int32_t glcd;
u_int32_t iqp;
u_int32_t iql;
u_int32_t grcd;
u_int32_t mpd;
u_int32_t mld;
u_int32_t pcd;
u_int32_t rbist;
u_int32_t tbist;
};
struct mycg {
struct groupr cg;
};
struct mdesc {
u_int32_t status;
u_int32_t data;
u_int32_t next;
struct mbuf *m;
struct mdesc *snext;
};
#define NPORT 8
#define NHDLC 32
#define NIQD 32
struct softc;
struct schan {
enum {DOWN, UP} state;
struct softc *sc;
int chan;
u_int32_t ts;
char hookname[8];
hook_p hook;
u_long rx_drop;
u_long tx_limit;
u_long tx_pending;
struct mdesc *tx_next_md;
struct mdesc *tx_last_md;
int rx_last_md;
int nmd;
time_t last_recv;
time_t last_rdrop;
time_t last_rxerr;
u_long crc_error;
u_long dribble_error;
u_long long_error;
u_long abort_error;
u_long short_error;
u_long txn, rxn;
time_t last_xmit;
time_t last_txerr;
time_t last_txdrop;
u_long tx_drop;
#if 0
u_long rx_error;
u_long overflow_error;
int last_error;
int prev_error;
#endif
};
enum framing {WHOKNOWS, E1, E1U, T1, T1U};
enum clocksource {EXT, INT};
struct softc {
enum framing framing;
enum clocksource clocksource;
int nhooks;
u_int32_t last;
struct csoftc *csc;
u_int32_t *ds8370;
void *ds847x;
struct globalr *reg;
struct groupr *ram;
struct mycg *mycg;
struct mdesc *mdt[NHDLC];
struct mdesc *mdr[NHDLC];
node_p node;
char nodename[NG_NODESIZ];
struct schan *chan[NHDLC];
u_long cnt_ferr;
u_long cnt_cerr;
u_long cnt_lcv;
u_long cnt_febe;
u_long cnt_berr;
u_long cnt_fred;
u_long cnt_cofa;
u_long cnt_sef;
};
struct csoftc {
enum { C_IDLE, C_INIT, C_RUNNING, C_FAULT } state;
int unit, bus, slot;
LIST_ENTRY(csoftc) list;
device_t f[2];
struct resource *irq[2];
void *intrhand[2];
vm_offset_t physbase[2];
u_char *virbase[2];
u_int creg, *cregp;
int nchan;
struct softc serial[NPORT];
struct globalr *reg;
struct globalr *ram;
u_int32_t iqd[NIQD];
};
#define NG_NODETYPE "lmc1504"
static ng_constructor_t musycc_constructor;
static ng_rcvmsg_t musycc_rcvmsg;
static ng_shutdown_t musycc_shutdown;
static ng_newhook_t musycc_newhook;
static ng_connect_t musycc_connect;
static ng_rcvdata_t musycc_rcvdata;
static ng_disconnect_t musycc_disconnect;
static struct ng_type ngtypestruct = {
NG_VERSION,
NG_NODETYPE,
NULL,
musycc_constructor,
musycc_rcvmsg,
musycc_shutdown,
musycc_newhook,
NULL,
musycc_connect,
musycc_rcvdata,
musycc_rcvdata,
musycc_disconnect,
NULL
};
static u_int32_t
parse_ts(const char *s, int *nbit)
{
unsigned r;
int i, j;
char *p;
r = 0;
j = -1;
*nbit = 0;
while(*s) {
i = strtol(s, &p, 0);
if (i < 0 || i > 31)
return (0);
while (j != -1 && j < i) {
r |= 1 << j++;
(*nbit)++;
}
j = -1;
r |= 1 << i;
(*nbit)++;
if (*p == ',') {
s = p + 1;
continue;
} else if (*p == '-') {
j = i + 1;
s = p + 1;
continue;
} else if (!*p) {
break;
} else {
return (0);
}
}
return (r);
}
static LIST_HEAD(, csoftc) sc_list = LIST_HEAD_INITIALIZER(&sc_list);
#if 0
static void
poke_847x(void *dummy)
{
static int count;
int i;
struct csoftc *csc;
timeout(poke_847x, NULL, 1);
LIST_FOREACH(csc, &sc_list, list) {
count++;
i = (csc->creg >> 24 & 0xf);
csc->creg &= ~0xf000000;
i++;
csc->creg |= (i & 0xf) << 24;
*csc->cregp = csc->creg;
#if 0
for (i = 0; i < sc->nchan; i++) {
if (sc->serial[i].last == 0xffffffff) {
sc->serial[i].reg->srd = 0;
sc->serial[i].last = 0;
return;
}
}
#endif
}
}
#endif
static void
init_card(struct csoftc *csc)
{
kprintf("init_card(%p)\n", csc);
csc->state = C_INIT;
csc->reg->srd = 0x100;
tsleep(csc, PCATCH, "icard", hz / 10);
csc->reg->gbp = vtophys(csc->ram);
csc->ram->glcd = 0x3f30;
csc->ram->iqp = vtophys(csc->iqd);
csc->ram->iql = NIQD - 1;
csc->ram->dacbp = 0;
csc->reg->srd = csc->serial[0].last = 0x400;
tsleep(&csc->serial[0].last, PCATCH, "con1", hz);
#if 0
DELAY(20000);
#endif
csc->state = C_RUNNING;
}
static void
init_ctrl(struct softc *sc)
{
int i;
kprintf("init_ctrl(%p) [%s] [%08x]\n", sc, sc->nodename, sc->csc->reg->glcd);
init_8370(sc);
tsleep(sc, PCATCH, "ds8370", hz);
kprintf("%s: glcd: [%08x]\n", sc->nodename, sc->csc->reg->glcd);
sc->reg->gbp = vtophys(sc->ram);
sc->ram->grcd = 0x00000001;
sc->ram->grcd |= 0x00000002;
sc->ram->grcd |= 0x00000004;
if (sc->framing == E1 || sc->framing == T1)
sc->ram->grcd |= 0x00000008;
else
sc->ram->grcd |= 0x00000000;
sc->ram->grcd |= 0x00000020;
sc->ram->grcd |= 0x00000440;
sc->ram->mpd = 0;
sc->ram->pcd = 0x0000001;
sc->ram->pcd |= 1 << 5;
sc->ram->pcd |= 1 << 9;
sc->ram->mld = 1600;
sc->ram->mld |= (1600 << 16);
for (i = 0; i < NHDLC; i++) {
sc->ram->ttsm[i] = 0;
sc->ram->rtsm[i] = 0;
}
sc->reg->srd = sc->last = 0x500;
tsleep(&sc->last, PCATCH, "con1", hz);
sc->reg->srd = sc->last = 0x520;
tsleep(&sc->last, PCATCH, "con1", hz);
}
static void
status_chans(struct softc *sc, char *s)
{
int i;
struct schan *scp;
s += strlen(s);
for (i = 0; i < NHDLC; i++) {
scp = sc->chan[i];
if (scp == NULL)
continue;
ksprintf(s + strlen(s), "c%2d:", i);
ksprintf(s + strlen(s), " ts %08x", scp->ts);
ksprintf(s + strlen(s), " RX %lus/%lus",
time_uptime - scp->last_recv, time_uptime - scp->last_rxerr);
ksprintf(s + strlen(s), " TX %lus/%lus/%lus",
time_uptime - scp->last_xmit,
time_uptime - scp->last_txerr,
time_uptime - scp->last_txdrop);
ksprintf(s + strlen(s), " TXdrop %lu Pend %lu",
scp->tx_drop,
scp->tx_pending);
ksprintf(s + strlen(s), " CRC %lu Dribble %lu Long %lu Short %lu Abort %lu",
scp->crc_error,
scp->dribble_error,
scp->long_error,
scp->short_error,
scp->abort_error);
ksprintf(s + strlen(s), "\n TX: %lu RX: %lu\n",
scp->txn, scp->rxn);
}
}
static void
status_8370(struct softc *sc, char *s)
{
u_int32_t *p = sc->ds8370;
s += strlen(s);
ksprintf(s, "Framer: "); s += strlen(s);
switch (sc->framing) {
case WHOKNOWS: ksprintf(s, "(unconfigured)\n"); break;
case E1: ksprintf(s, "(e1)\n"); break;
case E1U: ksprintf(s, "(e1u)\n"); break;
case T1: ksprintf(s, "(t1)\n"); break;
case T1U: ksprintf(s, "(t1u)\n"); break;
default: ksprintf(s, "(mode %d XXX?)\n", sc->framing); break;
}
s += strlen(s);
ksprintf(s, " Red alarms:"); s += strlen(s);
if (p[0x47] & 0x08) { ksprintf(s, " ALOS"); s += strlen(s); }
if (p[0x47] & 0x04) { ksprintf(s, " LOS"); s += strlen(s); }
if (sc->framing == E1 || sc->framing == T1) {
if (p[0x47] & 0x02) { ksprintf(s, " LOF"); s += strlen(s); }
}
ksprintf(s, "\n Yellow alarms:"); s += strlen(s);
if (p[0x47] & 0x80) { ksprintf(s, " RMYEL"); s += strlen(s); }
if (p[0x47] & 0x40) { ksprintf(s, " RYEL"); s += strlen(s); }
ksprintf(s, "\n Blue alarms:"); s += strlen(s);
if (p[0x47] & 0x10) { ksprintf(s, " AIS"); s += strlen(s); }
ksprintf(s, "\n"); s += strlen(s);
ksprintf(s, "\n Various alarms:"); s += strlen(s);
if (p[0x48] & 0x10) { ksprintf(s, " TSHORT"); s += strlen(s); }
ksprintf(s, "\n Counters:"); s += strlen(s);
if (sc->framing == E1) {
ksprintf(s, " FERR=%lu", sc->cnt_ferr); s += strlen(s);
}
ksprintf(s, " CERR=%lu", sc->cnt_cerr); s += strlen(s);
ksprintf(s, " LCV=%lu", sc->cnt_lcv); s += strlen(s);
ksprintf(s, " FEBE=%lu", sc->cnt_febe); s += strlen(s);
ksprintf(s, " BERR=%lu", sc->cnt_berr); s += strlen(s);
ksprintf(s, " FRED=%lu", sc->cnt_fred); s += strlen(s);
ksprintf(s, " COFA=%lu", sc->cnt_cofa); s += strlen(s);
ksprintf(s, " SEF=%lu", sc->cnt_sef); s += strlen(s);
ksprintf(s, "\n"); s += strlen(s);
}
static void
dump_8370(struct softc *sc, char *s, int offset)
{
int i, j;
u_int32_t *p = sc->ds8370;
s += strlen(s);
for (i = 0; i < 0x100; i += 16) {
ksprintf(s, "%03x: ", i + offset);
s += strlen(s);
for (j = 0; j < 0x10; j ++) {
ksprintf(s, " %02x", p[i + j + offset] & 0xff);
s += strlen(s);
}
ksprintf(s, "\n");
s += strlen(s);
}
}
static void
init_8370(struct softc *sc)
{
int i;
u_int32_t *p = sc->ds8370;
p[0x001] = 0x80;
DELAY(20);
p[0x001] = 0x00;
DELAY(20);
if (sc->clocksource == INT)
p[0x002] = 0x40;
else
p[0x002] = 0x20;
p[0x00D] = 0x01;
p[0x014] = 0x00;
p[0x015] = 0x00;
p[0x016] = 0x00;
p[0x017] = 0x00;
p[0x018] = 0xFF;
p[0x019] = 0x3c;
if (sc->clocksource == INT)
p[0x01A] = 0x37;
else
p[0x01A] = 0x37;
p[0x020] = 0x41;
p[0x022] = 0xb1;
p[0x024] = 0x1d;
p[0x027] = 0xba;
p[0x028] = 0xda;
p[0x02a] = 0xa6;
if (sc->framing == E1U || sc->framing == T1U)
p[0x040] = 0x49;
else
p[0x040] = 0x09;
p[0x041] = 0x00;
p[0x045] = 0x00;
p[0x046] = 0x05;
p[0x068] = 0x4c;
p[0x070] = 0x04;
if (sc->framing == E1U || sc->framing == T1U)
p[0x071] = 0x41;
else
p[0x071] = 0x51;
if (sc->framing == E1U || sc->framing == T1U)
p[0x072] = 0x00;
else
p[0x072] = 0x1b;
p[0x073] = 0x00;
p[0x074] = 0x00;
if (sc->framing == E1U || sc->framing == T1U)
p[0x075] = 0x0;
else
p[0x075] = 0x10;
p[0x076] = 0x00;
p[0x077] = 0x00;
p[0x090] = 0x05;
p[0x091] = 0x01;
if (sc->framing == E1U || sc->framing == T1U) {
p[0x0a0] = 0x00;
p[0x0a6] = 0x00;
p[0x0b1] = 0x00;
}
p[0x0d0] = 0x46;
p[0x0d1] = 0x70;
p[0x0d2] = 0x00;
p[0x0d3] = 0x00;
p[0x0d4] = 0x30;
p[0x0d5] = 0x00;
p[0x0d6] = 0x00;
if (sc->framing == E1U || sc->framing == T1U)
p[0x0d7] = 0x05;
else
p[0x0d7] = 0x01;
p[0x0d8] = 0x00;
for (i = 0; i < 32; i ++) {
p[0x0e0 + i] = 0x0d;
p[0x100 + i] = 0x00;
p[0x180 + i] = 0x00;
}
}
static void
musycc_intr0_tx_eom(struct softc *sc, int ch)
{
struct schan *sch;
struct mdesc *md;
sch = sc->chan[ch];
if (sch == NULL || sch->state != UP) {
kprintf("Xmit packet on uninitialized channel %d\n", ch);
}
if (sc->mdt[ch] == NULL)
return;
for (;;) {
md = sch->tx_last_md;
if (md->status == 0)
break;
if (md->status & 0x80000000)
break;
sch->tx_last_md = md->snext;
md->data = 0;
if (md->m != NULL) {
sch->tx_pending -= md->m->m_pkthdr.len;
m_freem(md->m);
md->m = NULL;
}
md->status = 0;
}
}
static void
musycc_intr0_rx_eom(struct softc *sc, int ch)
{
u_int32_t status, error;
struct schan *sch;
struct mbuf *m, *m2;
struct mdesc *md;
sch = sc->chan[ch];
if (sch == NULL || sch->state != UP) {
kprintf("Received packet on uninitialized channel %d\n", ch);
return;
}
if (sc->mdr[ch] == NULL)
return;
for (;;) {
md = &sc->mdr[ch][sch->rx_last_md];
status = md->status;
if (!(status & 0x80000000))
break;
m = md->m;
m->m_len = m->m_pkthdr.len = status & 0x3fff;
error = (status >> 16) & 0xf;
if (error == 0) {
MGETHDR(m2, M_NOWAIT, MT_DATA);
if (m2 != NULL) {
MCLGET(m2, M_NOWAIT);
if((m2->m_flags & M_EXT) != 0) {
md->m = m2;
md->data = vtophys(m2->m_data);
sch->last_recv = time_uptime;
ng_queue_data(sch->hook, m, NULL);
} else {
m_freem(m2);
sch->last_rdrop = time_uptime;
sch->rx_drop++;
}
} else {
sch->last_rdrop = time_uptime;
sch->rx_drop++;
}
} else if (error == 9) {
sch->last_rxerr = time_uptime;
sch->crc_error++;
} else if (error == 10) {
sch->last_rxerr = time_uptime;
sch->dribble_error++;
} else if (error == 11) {
sch->last_rxerr = time_uptime;
sch->abort_error++;
} else if (error == 12) {
sch->last_rxerr = time_uptime;
sch->long_error++;
} else {
sch->last_rxerr = time_uptime;
kprintf("%s %s: RX 0x%08x ", sch->sc->nodename,
sch->hookname, status);
if (m->m_len > 16)
m->m_len = m->m_pkthdr.len = 16;
m_print(m);
kprintf("\n");
}
md->status = 1600;
if (++sch->rx_last_md >= sch->nmd)
sch->rx_last_md = 0;
}
}
static void
musycc_intr0(void *arg)
{
int i, j, g, ch, ev, er;
struct csoftc *csc;
u_int32_t u, u1, n, c;
struct softc *sc;
csc = arg;
for (;;) {
u = csc->reg->isd;
c = u & 0x7fff;
n = u >> 16;
if (c == 0)
return;
if (debug & 1)
kprintf("%s: IRQ: %08x n = %d c = %d\n", csc->serial[0].nodename, u, n, c);
for (i = 0; i < c; i++) {
j = (n + i) % NIQD;
u1 = csc->iqd[j];
g = (u1 >> 29) & 0x3;
g |= (u1 >> (14-2)) & 0x4;
ch = (u1 >> 24) & 0x1f;
ev = (u1 >> 20) & 0xf;
er = (u1 >> 16) & 0xf;
sc = &csc->serial[g];
if ((debug & 2) || er) {
kprintf("%08x %d", u1, g);
kprintf("/%s", u1 & 0x80000000 ? "T" : "R");
kprintf("/%02d", ch);
kprintf(" %02d", ev);
kprintf(":%02d", er);
kprintf("\n");
}
switch (ev) {
case 1:
#if 0
kprintf("%s: SACK: %08x group=%d", sc->nodename, csc->iqd[j], g);
kprintf("/%s", csc->iqd[j] & 0x80000000 ? "T" : "R");
kprintf(" cmd %08x (%08x) \n", sc->last, sc->reg->srd);
#endif
sc->last = 0xffffffff;
wakeup(&sc->last);
break;
case 5:
case 6:
break;
case 3:
if (csc->iqd[j] & 0x80000000)
musycc_intr0_tx_eom(sc, ch);
else
musycc_intr0_rx_eom(sc, ch);
break;
case 0:
if (er == 13) {
sc->chan[ch]->last_rxerr = time_uptime;
sc->chan[ch]->short_error++;
break;
}
default:
musycc_intr0_tx_eom(sc, ch);
musycc_intr0_rx_eom(sc, ch);
#if 1
kprintf("huh ? %08x %d", u1, g);
kprintf("/%s", u1 & 0x80000000 ? "T" : "R");
kprintf("/%02d", ch);
kprintf(" %02d", ev);
kprintf(":%02d", er);
kprintf("\n");
#endif
}
csc->iqd[j] = 0xffffffff;
j++;
j %= NIQD;
csc->reg->isd = j << 16;
}
}
}
static void
musycc_intr1(void *arg)
{
int i;
struct csoftc *csc;
struct softc *sc;
u_int32_t *u;
u_int8_t irr;
csc = arg;
for (i = 0; i < csc->nchan; i++) {
sc = &csc->serial[i];
u = sc->ds8370;
irr = u[3];
if (irr == 0)
continue;
if (u[0x5] & 1) {
sc->cnt_ferr += u[0x50] & 0xff;
sc->cnt_ferr += (u[0x51] & 0xff) << 8;
sc->cnt_cerr += u[0x52] & 0xff;
sc->cnt_cerr += (u[0x53] & 0xff) << 8;
sc->cnt_lcv += u[0x54] & 0xff;
sc->cnt_lcv += (u[0x55] & 0xff) << 8;
sc->cnt_febe += u[0x56] & 0xff;
sc->cnt_febe += (u[0x57] & 0xff) << 8;
sc->cnt_berr += u[0x58] & 0xff;
sc->cnt_berr += (u[0x59] & 0xff) << 8;
sc->cnt_fred += (u[0x5a] & 0xf0) >> 4;
sc->cnt_cofa += (u[0x5a] & 0x0c) >> 2;
sc->cnt_sef += u[0x5a] & 0x03;
}
if (debug & 4) {
int j;
kprintf("musycc_intr1:%d %02x", i, irr);
for (j = 4; j < 0x14; j++)
kprintf(" %02x", u[j] & 0xff);
kprintf("\n");
}
}
}
static int
musycc_constructor(node_p *nodep)
{
return (EINVAL);
}
static int
musycc_shutdown(node_p nodep)
{
return (EINVAL);
}
static void
musycc_config(node_p node, char *set, char *ret)
{
struct softc *sc;
struct csoftc *csc;
enum framing wframing;
int i;
sc = node->private;
csc = sc->csc;
if (csc->state == C_IDLE)
init_card(csc);
while (csc->state != C_RUNNING)
tsleep(&csc->state, PCATCH, "crun", hz/10);
if (set != NULL) {
if (!strncmp(set, "line ", 5)) {
wframing = sc->framing;
if (!strcmp(set, "line e1")) {
wframing = E1;
} else if (!strcmp(set, "line e1u")) {
wframing = E1U;
} else {
strcat(ret, "ENOGROK\n");
return;
}
if (wframing == sc->framing)
return;
if (sc->nhooks > 0) {
ksprintf(ret, "Cannot change line when %d hooks open\n", sc->nhooks);
return;
}
sc->framing = wframing;
init_ctrl(sc);
return;
}
if (!strcmp(set, "clock source internal")) {
sc->clocksource = INT;
init_ctrl(sc);
} else if (!strcmp(set, "clock source line")) {
sc->clocksource = EXT;
init_ctrl(sc);
} else if (!strcmp(set, "show 8370 0")) {
dump_8370(sc, ret, 0);
} else if (!strcmp(set, "show 8370 1")) {
dump_8370(sc, ret, 0x100);
} else if (!strncmp(set, "creg", 4)) {
i = strtol(set + 5, 0, 0);
kprintf("set creg %d\n", i);
csc->creg = 0xfe | (i << 24);
*csc->cregp = csc->creg;
} else {
kprintf("%s CONFIG SET [%s]\n", sc->nodename, set);
goto barf;
}
return;
}
if (sc->framing == E1)
strcat(ret, "line e1\n");
else if (sc->framing == E1U)
strcat(ret, "line e1u\n");
if (sc->clocksource == INT)
strcat(ret, "clock source internal\n");
else
strcat(ret, "clock source line\n");
return;
barf:
strcpy(ret, "Syntax Error\n");
strcat(ret, "\tline {e1|e1u}\n");
strcat(ret, "\tshow 8370 {0|1}\n");
return;
}
static int
musycc_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp)
{
struct softc *sc;
char *s, *r;
sc = node->private;
if (msg->header.typecookie != NGM_GENERIC_COOKIE)
goto out;
if (msg->header.cmd == NGM_TEXT_STATUS) {
NG_MKRESPONSE(*resp, msg,
sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT);
if (*resp == NULL) {
kfree(msg, M_NETGRAPH);
return (ENOMEM);
}
s = (char *)(*resp)->data;
status_8370(sc, s);
status_chans(sc,s);
(*resp)->header.arglen = strlen(s) + 1;
kfree(msg, M_NETGRAPH);
return (0);
} else if (msg->header.cmd == NGM_TEXT_CONFIG) {
if (msg->header.arglen) {
s = (char *)msg->data;
} else {
s = NULL;
}
NG_MKRESPONSE(*resp, msg,
sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT);
if (*resp == NULL) {
kfree(msg, M_NETGRAPH);
return (ENOMEM);
}
r = (char *)(*resp)->data;
*r = '\0';
musycc_config(node, s, r);
(*resp)->header.arglen = strlen(r) + 1;
kfree(msg, M_NETGRAPH);
return (0);
}
out:
if (resp)
*resp = NULL;
kfree(msg, M_NETGRAPH);
return (EINVAL);
}
static int
musycc_newhook(node_p node, hook_p hook, const char *name)
{
struct softc *sc;
struct csoftc *csc;
struct schan *sch;
u_int32_t ts, chan;
int nbit;
sc = node->private;
csc = sc->csc;
while (csc->state != C_RUNNING)
tsleep(&csc->state, PCATCH, "crun", hz/10);
if (sc->framing == WHOKNOWS)
return (EINVAL);
if (name[0] != 't' || name[1] != 's')
return (EINVAL);
ts = parse_ts(name + 2, &nbit);
if (ts == 0)
return (EINVAL);
chan = ffs(ts) - 1;
if (sc->framing == E1U && nbit == 32)
;
else if (sc->framing == T1U && nbit == 24)
;
else if (ts & 1)
return (EINVAL);
if (sc->chan[chan] == NULL) {
sch = kmalloc(sizeof(*sch), M_MUSYCC, M_WAITOK | M_ZERO);
sch->sc = sc;
sch->state = DOWN;
sch->chan = chan;
ksprintf(sch->hookname, name);
sc->chan[chan] = sch;
} else if (sc->chan[chan]->state == UP) {
return (EBUSY);
}
sc->nhooks++;
sch = sc->chan[chan];
sch->ts = ts;
sch->hook = hook;
sch->tx_limit = nbit * 8;
hook->private = sch;
return(0);
}
static int
musycc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
{
struct softc *sc;
struct csoftc *csc;
struct schan *sch;
struct mdesc *md, *md0;
u_int32_t ch, u, u0, len;
struct mbuf *m2;
sch = hook->private;
sc = sch->sc;
csc = sc->csc;
ch = sch->chan;
if (csc->state != C_RUNNING) {
kprintf("csc->state = %d\n", csc->state);
NG_FREE_DATA(m, meta);
return (0);
}
NG_FREE_META(meta);
meta = NULL;
if (sch->state != UP) {
kprintf("sch->state = %d\n", sch->state);
NG_FREE_DATA(m, meta);
return (0);
}
if (sch->tx_pending + m->m_pkthdr.len > sch->tx_limit * maxlatency) {
sch->tx_drop++;
sch->last_txdrop = time_uptime;
NG_FREE_DATA(m, meta);
return (0);
}
m2 = m;
md = sch->tx_next_md;
for (len = m2->m_pkthdr.len; len; m2 = m2->m_next) {
if (m2->m_len == 0)
continue;
if (md->status != 0) {
sch->tx_drop++;
sch->last_txdrop = time_uptime;
NG_FREE_DATA(m, meta);
return (0);
}
len -= m2->m_len;
md = md->snext;
}
m2 = m;
md = md0 = sch->tx_next_md;
u0 = 0;
for (len = m->m_pkthdr.len; len > 0; m = m->m_next) {
if (m->m_len == 0)
continue;
if (md->status != 0) {
kprintf("Out of tx md(2)\n");
sch->last_txerr = time_uptime;
sch->tx_drop++;
sch->last_txdrop = time_uptime;
NG_FREE_DATA(m, meta);
break;
}
md->data = vtophys(m->m_data);
if (md == md0)
u = 0x00000000;
else
u = 0x80000000;
u |= m->m_len;
len -= m->m_len;
if (len > 0) {
md->m = NULL;
if (md == md0)
u0 = u;
else
md->status = u;
md = md->snext;
continue;
}
u |= 0x20000000;
md->m = m2;
sch->tx_pending += m2->m_pkthdr.len;
if (md == md0) {
u |= 0x80000000;
md->status = u;
} else {
md->status = u;
md0->status = u0 | 0x80000000;
}
sch->last_xmit = time_uptime;
sch->tx_next_md = md->snext;
}
sch->txn++;
return (0);
}
static int
musycc_connect(hook_p hook)
{
struct softc *sc;
struct csoftc *csc;
struct schan *sch;
int nts, nbuf, i, nmd, ch;
struct mbuf *m;
sch = hook->private;
sc = sch->sc;
csc = sc->csc;
ch = sch->chan;
while (csc->state != C_RUNNING)
tsleep(&csc->state, PCATCH, "crun", hz/10);
if (sch->state == UP)
return (0);
sch->state = UP;
nts = 0;
for (i = ch; i < 32; i++) {
if (sch->ts & (1 << i)) {
sc->ram->rtsm[i] = ch | (4 << 5);
sc->ram->ttsm[i] = ch | (4 << 5);
nts++;
}
}
nbuf = 0;
for (i = ch; i < 32; i++) {
if (sch->ts & (1 << i))
nbuf++;
else
break;
}
kprintf("Connect ch= %d ts= %08x nts= %d nbuf = %d\n",
ch, sch->ts, nts, nbuf);
sc->reg->srd = sc->last = 0x1800;
tsleep(&sc->last, PCATCH, "con1", hz);
sc->reg->srd = sc->last = 0x1820;
tsleep(&sc->last, PCATCH, "con2", hz);
sc->ram->tcct[ch] = 0x2800;
sc->ram->rcct[ch] = 0x2800;
sc->ram->tcct[ch] |= (1 + 2 * (nbuf - 1)) << 16;
sc->ram->rcct[ch] |= (1 + 2 * (nbuf - 1)) << 16;
sc->ram->tcct[ch] |= ((ch * 2) << 24);
sc->ram->rcct[ch] |= ((ch * 2) << 24);
sc->reg->srd = sc->last = 0x0b00 + ch;
tsleep(&sc->last, PCATCH, "con3", hz);
sc->reg->srd = sc->last = 0x0b20 + ch;
tsleep(&sc->last, PCATCH, "con4", hz);
sch->nmd = nmd = 200 + nts * 4;
sch->rx_last_md = 0;
sc->mdt[ch] = kmalloc(sizeof(struct mdesc) * nmd, M_MUSYCC, M_WAITOK);
sc->mdr[ch] = kmalloc(sizeof(struct mdesc) * nmd, M_MUSYCC, M_WAITOK);
for (i = 0; i < nmd; i++) {
if (i == nmd - 1) {
sc->mdt[ch][i].snext = &sc->mdt[ch][0];
sc->mdt[ch][i].next = vtophys(sc->mdt[ch][i].snext);
sc->mdr[ch][i].snext = &sc->mdr[ch][0];
sc->mdr[ch][i].next = vtophys(sc->mdr[ch][i].snext);
} else {
sc->mdt[ch][i].snext = &sc->mdt[ch][i + 1];
sc->mdt[ch][i].next = vtophys(sc->mdt[ch][i].snext);
sc->mdr[ch][i].snext = &sc->mdr[ch][i + 1];
sc->mdr[ch][i].next = vtophys(sc->mdr[ch][i].snext);
}
sc->mdt[ch][i].status = 0;
sc->mdt[ch][i].m = NULL;
sc->mdt[ch][i].data = 0;
MGETHDR(m, M_WAITOK, MT_DATA);
MCLGET(m, M_WAITOK);
sc->mdr[ch][i].m = m;
sc->mdr[ch][i].data = vtophys(m->m_data);
sc->mdr[ch][i].status = 1600;
}
sch->tx_last_md = sch->tx_next_md = &sc->mdt[ch][0];
sc->ram->thp[ch] = vtophys(&sc->mdt[ch][0]);
sc->ram->tmp[ch] = vtophys(&sc->mdt[ch][0]);
sc->ram->rhp[ch] = vtophys(&sc->mdr[ch][0]);
sc->ram->rmp[ch] = vtophys(&sc->mdr[ch][0]);
sc->reg->srd = sc->last = 0x0800 + ch;
tsleep(&sc->last, PCATCH, "con4", hz);
sc->reg->srd = sc->last = 0x0820 + ch;
tsleep(&sc->last, PCATCH, "con3", hz);
return (0);
}
static int
musycc_disconnect(hook_p hook)
{
struct softc *sc;
struct csoftc *csc;
struct schan *sch;
int i, ch;
sch = hook->private;
sc = sch->sc;
csc = sc->csc;
ch = sch->chan;
while (csc->state != C_RUNNING)
tsleep(&csc->state, PCATCH, "crun", hz/10);
sc->reg->srd = sc->last = 0x0900 + sch->chan;
tsleep(&sc->last, PCATCH, "con3", hz);
sc->reg->srd = sc->last = 0x0920 + sch->chan;
tsleep(&sc->last, PCATCH, "con4", hz);
if (sch->state == DOWN)
return (0);
sch->state = DOWN;
sc->ram->thp[ch] = 0;
sc->ram->tmp[ch] = 0;
sc->ram->rhp[ch] = 0;
sc->ram->rmp[ch] = 0;
for (i = 0; i < sch->nmd; i++) {
if (sc->mdt[ch][i].m != NULL)
m_freem(sc->mdt[ch][i].m);
if (sc->mdr[ch][i].m != NULL)
m_freem(sc->mdr[ch][i].m);
}
kfree(sc->mdt[ch], M_MUSYCC);
sc->mdt[ch] = NULL;
kfree(sc->mdr[ch], M_MUSYCC);
sc->mdr[ch] = NULL;
for (i = 0; i < 32; i++) {
if (sch->ts & (1 << i)) {
sc->ram->rtsm[i] = 0;
sc->ram->ttsm[i] = 0;
}
}
sc->nhooks--;
sch->tx_pending = 0;
return (0);
}
static int
musycc_probe(device_t self)
{
char desc[40];
if (sizeof(struct groupr) != 1572) {
kprintf("sizeof(struct groupr) = %zu, should be 1572\n",
sizeof(struct groupr));
return(ENXIO);
}
if (sizeof(struct globalr) != 1572) {
kprintf("sizeof(struct globalr) = %zu, should be 1572\n",
sizeof(struct globalr));
return(ENXIO);
}
if (sizeof(struct mycg) > 2048) {
kprintf("sizeof(struct mycg) = %zu, should be <= 2048\n",
sizeof(struct mycg));
return(ENXIO);
}
switch (pci_get_devid(self)) {
case 0x8471109e: strcpy(desc, "CN8471 MUSYCC"); break;
case 0x8472109e: strcpy(desc, "CN8472 MUSYCC"); break;
case 0x8474109e: strcpy(desc, "CN8474 MUSYCC"); break;
case 0x8478109e: strcpy(desc, "CN8478 MUSYCC"); break;
default:
return (ENXIO);
}
switch (pci_get_function(self)) {
case 0: strcat(desc, " Network controller"); break;
case 1: strcat(desc, " Ebus bridge"); break;
default:
return (ENXIO);
}
device_set_desc_copy(self, desc);
return 0;
}
static int
musycc_attach(device_t self)
{
struct csoftc *csc;
struct resource *res;
struct softc *sc;
int rid, i, error;
int f;
u_int32_t *u32p, u;
static int once;
if (!once) {
once++;
error = ng_newtype(&ngtypestruct);
if (error != 0)
kprintf("ng_newtype() failed %d\n", error);
}
kprintf("We have %zu pad bytes in mycg\n", 2048 - sizeof(struct mycg));
f = pci_get_function(self);
if (f == 0) {
csc = kmalloc(sizeof(*csc), M_MUSYCC, M_WAITOK | M_ZERO);
csc->bus = pci_get_bus(self);
csc->slot = pci_get_slot(self);
LIST_INSERT_HEAD(&sc_list, csc, list);
} else {
LIST_FOREACH(csc, &sc_list, list) {
if (csc->bus != pci_get_bus(self))
continue;
if (csc->slot != pci_get_slot(self))
continue;
break;
}
}
csc->f[f] = self;
device_set_softc(self, csc);
rid = PCIR_MAPS;
res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid,
0, ~0, 1, RF_ACTIVE);
if (res == NULL) {
device_printf(self, "Could not map memory\n");
return ENXIO;
}
csc->virbase[f] = (u_char *)rman_get_virtual(res);
csc->physbase[f] = rman_get_start(res);
rid = 0;
csc->irq[f] = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0,
1, RF_SHAREABLE | RF_ACTIVE);
if (csc->irq[f] == NULL) {
kprintf("couldn't map interrupt\n");
return(ENXIO);
}
error = bus_setup_intr(self, csc->irq[f], 0,
(f == 0 ? musycc_intr0 : musycc_intr1), csc,
&csc->intrhand[f], NULL);
if (error) {
kprintf("couldn't set up irq\n");
return(ENXIO);
}
if (f == 0)
return (0);
for (i = 0; i < 2; i++)
kprintf("f%d: device %p virtual %p physical %08lx\n",
i, csc->f[i], csc->virbase[i], csc->physbase[i]);
csc->reg = (struct globalr *)csc->virbase[0];
csc->reg->glcd = 0x3f30;
u32p = (u_int32_t *)csc->virbase[1];
u = u32p[0x1200];
if ((u & 0xffff0000) != 0x13760000) {
kprintf("Not a LMC1504 (ID is 0x%08x). Bailing out.\n", u);
return(ENXIO);
}
csc->nchan = (u >> 8) & 0xf;
kprintf("Found <LanMedia LMC1504 Rev %d Chan %d>\n", (u >> 12) & 0xf, csc->nchan);
csc->creg = 0xfe;
csc->cregp = &u32p[0x1000];
*csc->cregp = csc->creg;
for (i = 0; i < csc->nchan; i++) {
sc = &csc->serial[i];
sc->csc = csc;
sc->last = 0xffffffff;
sc->ds8370 = (u_int32_t *)
(csc->virbase[1] + i * 0x800);
sc->ds847x = csc->virbase[0] + i * 0x800;
sc->reg = (struct globalr *)
(csc->virbase[0] + i * 0x800);
sc->mycg = kmalloc(sizeof(struct mycg), M_MUSYCC,
M_WAITOK | M_ZERO);
sc->ram = &sc->mycg->cg;
error = ng_make_node_common(&ngtypestruct, &sc->node);
if (error) {
kprintf("ng_make_node_common() failed %d\n", error);
continue;
}
sc->node->private = sc;
ksprintf(sc->nodename, "sync-%d-%d-%d",
csc->bus,
csc->slot,
i);
error = ng_name_node(sc->node, sc->nodename);
}
csc->ram = (struct globalr *)&csc->serial[0].mycg->cg;
sc = &csc->serial[0];
sc->reg->srd = sc->last = 0x100;
csc->state = C_IDLE;
return 0;
}
static device_method_t musycc_methods[] = {
DEVMETHOD(device_probe, musycc_probe),
DEVMETHOD(device_attach, musycc_attach),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD_END
};
static driver_t musycc_driver = {
"musycc",
musycc_methods,
0
};
DRIVER_MODULE(musycc, pci, musycc_driver, musycc_devclass, NULL, NULL);