📄 if_an.c
字号:
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
ifp->if_ierrors++;
return;
}
MCLGET(m, M_DONTWAIT);
if (!(m->m_flags & M_EXT)) {
m_freem(m);
ifp->if_ierrors++;
return;
}
m->m_pkthdr.rcvif = ifp;
/* Read Ethenet encapsulated packet */
#ifdef ANCACHE
/* Read NIC frame header */
if (an_read_data(sc, id, 0, (caddr_t) & rx_frame, sizeof(rx_frame))) {
ifp->if_ierrors++;
return;
}
#endif
/* Read in the 802_3 frame header */
if (an_read_data(sc, id, 0x34, (caddr_t) & rx_frame_802_3,
sizeof(rx_frame_802_3))) {
ifp->if_ierrors++;
return;
}
if (rx_frame_802_3.an_rx_802_3_status != 0) {
ifp->if_ierrors++;
return;
}
/* Check for insane frame length */
if (rx_frame_802_3.an_rx_802_3_payload_len > MCLBYTES) {
ifp->if_ierrors++;
return;
}
m->m_pkthdr.len = m->m_len =
rx_frame_802_3.an_rx_802_3_payload_len + 12;
eh = mtod(m, struct ether_header *);
bcopy((char *)&rx_frame_802_3.an_rx_dst_addr,
(char *)&eh->ether_dhost, ETHER_ADDR_LEN);
bcopy((char *)&rx_frame_802_3.an_rx_src_addr,
(char *)&eh->ether_shost, ETHER_ADDR_LEN);
/* in mbuf header type is just before payload */
error = an_read_data(sc, id, 0x44, (caddr_t)&(eh->ether_type),
rx_frame_802_3.an_rx_802_3_payload_len);
if (error) {
m_freem(m);
ifp->if_ierrors++;
return;
}
ifp->if_ipackets++;
/* Receive packet. */
m_adj(m, sizeof(struct ether_header));
#ifdef ANCACHE
an_cache_store(sc, eh, m, rx_frame.an_rx_signal_strength);
#endif
ether_input(ifp, eh, m);
}
}
static void
an_txeof(sc, status)
struct an_softc *sc;
int status;
{
struct ifnet *ifp;
int id, i;
ifp = &sc->arpcom.ac_if;
ifp->if_timer = 0;
ifp->if_flags &= ~IFF_OACTIVE;
id = CSR_READ_2(sc, AN_TX_CMP_FID);
if (status & AN_EV_TX_EXC) {
ifp->if_oerrors++;
} else
ifp->if_opackets++;
for (i = 0; i < AN_TX_RING_CNT; i++) {
if (id == sc->an_rdata.an_tx_ring[i]) {
sc->an_rdata.an_tx_ring[i] = 0;
break;
}
}
AN_INC(sc->an_rdata.an_tx_cons, AN_TX_RING_CNT);
return;
}
/*
* We abuse the stats updater to check the current NIC status. This
* is important because we don't want to allow transmissions until
* the NIC has synchronized to the current cell (either as the master
* in an ad-hoc group, or as a station connected to an access point).
*/
void
an_stats_update(xsc)
void *xsc;
{
struct an_softc *sc;
struct ifnet *ifp;
int s;
s = splimp();
sc = xsc;
ifp = &sc->arpcom.ac_if;
sc->an_status.an_type = AN_RID_STATUS;
sc->an_status.an_len = sizeof(struct an_ltv_status);
an_read_record(sc, (struct an_ltv_gen *)&sc->an_status);
if (sc->an_status.an_opmode & AN_STATUS_OPMODE_IN_SYNC)
sc->an_associated = 1;
else
sc->an_associated = 0;
/* Don't do this while we're transmitting */
if (ifp->if_flags & IFF_OACTIVE) {
sc->an_stat_ch = timeout(an_stats_update, sc, hz);
splx(s);
return;
}
sc->an_stats.an_len = sizeof(struct an_ltv_stats);
sc->an_stats.an_type = AN_RID_32BITS_CUM;
an_read_record(sc, (struct an_ltv_gen *)&sc->an_stats.an_len);
sc->an_stat_ch = timeout(an_stats_update, sc, hz);
splx(s);
return;
}
void
an_intr(xsc)
void *xsc;
{
struct an_softc *sc;
struct ifnet *ifp;
u_int16_t status;
sc = (struct an_softc*)xsc;
if (sc->an_gone)
return;
ifp = &sc->arpcom.ac_if;
/* Disable interrupts. */
CSR_WRITE_2(sc, AN_INT_EN, 0);
status = CSR_READ_2(sc, AN_EVENT_STAT);
CSR_WRITE_2(sc, AN_EVENT_ACK, ~AN_INTRS);
if (status & AN_EV_AWAKE) {
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_AWAKE);
}
if (status & AN_EV_LINKSTAT) {
if (CSR_READ_2(sc, AN_LINKSTAT) == AN_LINKSTAT_ASSOCIATED)
sc->an_associated = 1;
else
sc->an_associated = 0;
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_LINKSTAT);
}
if (status & AN_EV_RX) {
an_rxeof(sc);
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
}
if (status & AN_EV_TX) {
an_txeof(sc, status);
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX);
}
if (status & AN_EV_TX_EXC) {
an_txeof(sc, status);
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX_EXC);
}
if (status & AN_EV_ALLOC)
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC);
/* Re-enable interrupts. */
CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS);
if ((ifp->if_flags & IFF_UP) && (ifp->if_snd.ifq_head != NULL))
an_start(ifp);
return;
}
static int
an_cmd(sc, cmd, val)
struct an_softc *sc;
int cmd;
int val;
{
int i, s = 0;
CSR_WRITE_2(sc, AN_PARAM0, val);
CSR_WRITE_2(sc, AN_PARAM1, 0);
CSR_WRITE_2(sc, AN_PARAM2, 0);
CSR_WRITE_2(sc, AN_COMMAND, cmd);
for (i = 0; i < AN_TIMEOUT; i++) {
if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD)
break;
else {
if (CSR_READ_2(sc, AN_COMMAND) == cmd)
CSR_WRITE_2(sc, AN_COMMAND, cmd);
}
}
for (i = 0; i < AN_TIMEOUT; i++) {
CSR_READ_2(sc, AN_RESP0);
CSR_READ_2(sc, AN_RESP1);
CSR_READ_2(sc, AN_RESP2);
s = CSR_READ_2(sc, AN_STATUS);
if ((s & AN_STAT_CMD_CODE) == (cmd & AN_STAT_CMD_CODE))
break;
}
/* Ack the command */
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD);
if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY)
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY);
if (i == AN_TIMEOUT)
return(ETIMEDOUT);
return(0);
}
/*
* This reset sequence may look a little strange, but this is the
* most reliable method I've found to really kick the NIC in the
* head and force it to reboot correctly.
*/
static void
an_reset(sc)
struct an_softc *sc;
{
if (sc->an_gone)
return;
an_cmd(sc, AN_CMD_ENABLE, 0);
an_cmd(sc, AN_CMD_FW_RESTART, 0);
an_cmd(sc, AN_CMD_NOOP2, 0);
if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT)
printf("an%d: reset failed\n", sc->an_unit);
an_cmd(sc, AN_CMD_DISABLE, 0);
return;
}
/*
* Read an LTV record from the NIC.
*/
static int
an_read_record(sc, ltv)
struct an_softc *sc;
struct an_ltv_gen *ltv;
{
u_int16_t *ptr;
u_int8_t *ptr2;
int i, len;
if (ltv->an_len < 4 || ltv->an_type == 0)
return(EINVAL);
/* Tell the NIC to enter record read mode. */
if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) {
printf("an%d: RID access failed\n", sc->an_unit);
return(EIO);
}
/* Seek to the record. */
if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) {
printf("an%d: seek to record failed\n", sc->an_unit);
return(EIO);
}
/*
* Read the length and record type and make sure they
* match what we expect (this verifies that we have enough
* room to hold all of the returned data).
* Length includes type but not length.
*/
len = CSR_READ_2(sc, AN_DATA1);
if (len > (ltv->an_len - 2)) {
printf("an%d: record length mismatch -- expected %d, "
"got %d for Rid %x\n", sc->an_unit,
ltv->an_len - 2, len, ltv->an_type);
len = ltv->an_len - 2;
} else {
ltv->an_len = len + 2;
}
/* Now read the data. */
len -= 2; /* skip the type */
ptr = <v->an_val;
for (i = len; i > 1; i -= 2)
*ptr++ = CSR_READ_2(sc, AN_DATA1);
if (i) {
ptr2 = (u_int8_t *)ptr;
*ptr2 = CSR_READ_1(sc, AN_DATA1);
}
if (an_dump)
an_dump_record(sc, ltv, "Read");
return(0);
}
/*
* Same as read, except we inject data instead of reading it.
*/
static int
an_write_record(sc, ltv)
struct an_softc *sc;
struct an_ltv_gen *ltv;
{
u_int16_t *ptr;
u_int8_t *ptr2;
int i, len;
if (an_dump)
an_dump_record(sc, ltv, "Write");
if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type))
return(EIO);
if (an_seek(sc, ltv->an_type, 0, AN_BAP1))
return(EIO);
/*
* Length includes type but not length.
*/
len = ltv->an_len - 2;
CSR_WRITE_2(sc, AN_DATA1, len);
len -= 2; /* skip the type */
ptr = <v->an_val;
for (i = len; i > 1; i -= 2)
CSR_WRITE_2(sc, AN_DATA1, *ptr++);
if (i) {
ptr2 = (u_int8_t *)ptr;
CSR_WRITE_1(sc, AN_DATA0, *ptr2);
}
if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_WRITE, ltv->an_type))
return(EIO);
return(0);
}
static void
an_dump_record(sc, ltv, string)
struct an_softc *sc;
struct an_ltv_gen *ltv;
char *string;
{
u_int8_t *ptr2;
int len;
int i;
int count = 0;
char buf[17], temp;
len = ltv->an_len - 4;
printf("an%d: RID %4x, Length %4d, Mode %s\n",
sc->an_unit, ltv->an_type, ltv->an_len - 4, string);
if (an_dump == 1 || (an_dump == ltv->an_type)) {
printf("an%d:\t", sc->an_unit);
bzero(buf,sizeof(buf));
ptr2 = (u_int8_t *)<v->an_val;
for (i = len; i > 0; i--) {
printf("%02x ", *ptr2);
temp = *ptr2++;
if (temp >= ' ' && temp <= '~')
buf[count] = temp;
else if (temp >= 'A' && temp <= 'Z')
buf[count] = temp;
else
buf[count] = '.';
if (++count == 16) {
count = 0;
printf("%s\n",buf);
printf("an%d:\t", sc->an_unit);
bzero(buf,sizeof(buf));
}
}
for (; count != 16; count++) {
printf(" ");
}
printf(" %s\n",buf);
}
}
static int
an_seek(sc, id, off, chan)
struct an_softc *sc;
int id, off, chan;
{
int i;
int selreg, offreg;
switch (chan) {
case AN_BAP0:
selreg = AN_SEL0;
offreg = AN_OFF0;
break;
case AN_BAP1:
selreg = AN_SEL1;
offreg = AN_OFF1;
break;
default:
printf("an%d: invalid data path: %x\n", sc->an_unit, chan);
return(EIO);
}
CSR_WRITE_2(sc, selreg, id);
CSR_WRITE_2(sc, offreg, off);
for (i = 0; i < AN_TIMEOUT; i++) {
if (!(CSR_READ_2(sc, offreg) & (AN_OFF_BUSY|AN_OFF_ERR)))
break;
}
if (i == AN_TIMEOUT)
return(ETIMEDOUT);
return(0);
}
static int
an_read_data(sc, id, off, buf, len)
struct an_softc *sc;
int id, off;
caddr_t buf;
int len;
{
int i;
u_int16_t *ptr;
u_int8_t *ptr2;
if (off != -1) {
if (an_seek(sc, id, off, AN_BAP1))
return(EIO);
}
ptr = (u_int16_t *)buf;
for (i = len; i > 1; i -= 2)
*ptr++ = CSR_READ_2(sc, AN_DATA1);
if (i) {
ptr2 = (u_int8_t *)ptr;
*ptr2 = CSR_READ_1(sc, AN_DATA1);
}
return(0);
}
static int
an_write_data(sc, id, off, buf, len)
struct an_softc *sc;
int id, off;
caddr_t buf;
int len;
{
int i;
u_int16_t *ptr;
u_int8_t *ptr2;
if (off != -1) {
if (an_seek(sc, id, off, AN_BAP0))
return(EIO);
}
ptr = (u_int16_t *)buf;
for (i = len; i > 1; i -= 2)
CSR_WRITE_2(sc, AN_DATA0, *ptr++);
if (i) {
ptr2 = (u_int8_t *)ptr;
CSR_WRITE_1(sc, AN_DATA0, *ptr2);
}
return(0);
}
/*
* Allocate a region of memory inside the NIC and zero
* it out.
*/
static int
an_alloc_nicmem(sc, len, id)
struct an_softc *sc;
int len;
int *id;
{
int i;
if (an_cmd(sc, AN_CMD_ALLOC_MEM, len)) {
printf("an%d: failed to allocate %d bytes on NIC\n",
sc->an_unit, len);
return(ENOMEM);
}
for (i = 0; i < AN_TIMEOUT; i++) {
if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_ALLOC)
break;
}
if (i == AN_TIMEOUT)
return(ETIMEDOUT);
CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC);
*id = CSR_READ_2(sc, AN_ALLOC_FID);
if (an_seek(sc, *id, 0, AN_BAP0))
return(EIO);
for (i = 0; i < len / 2; i++)
CSR_WRITE_2(sc, AN_DATA0, 0);
return(0);
}
static void
an_setdef(sc, areq)
struct an_softc *sc;
struct an_req *areq;
{
struct sockaddr_dl *sdl;
struct ifaddr *ifa;
struct ifnet *ifp;
struct an_ltv_genconfig *cfg;
struct an_ltv_ssidlist *ssid;
struct an_ltv_aplist *ap;
struct an_ltv_gen *sp;
ifp = &sc->arpcom.ac_if;
switch (areq->an_type) {
case AN_RID_GENCONFIG:
cfg = (struct an_ltv_genconfig *)areq;
ifa = ifnet_addrs[ifp->if_index - 1];
sdl = (struct sockaddr_dl *)ifa->ifa_addr;
bcopy((char *)&cfg->an_macaddr, (char *)&sc->arpcom.ac_enaddr,
ETHER_ADDR_LEN);
bcopy((char *)&cfg->an_macaddr, LLADDR(sdl), ETHER_ADDR_LEN);
bcopy((char *)cfg, (char *)&sc->an_config,
sizeof(struct an_ltv_genconfig));
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -