File: [local] / sys / dev / usb / ueagle.c (download)
Revision 1.1, Tue Mar 4 16:14:25 2008 UTC (16 years, 6 months ago) by nbrk
Branch point for: MAIN
Initial revision
|
/* $OpenBSD: ueagle.c,v 1.21 2007/06/14 10:11:15 mbalmer Exp $ */
/*-
* Copyright (c) 2003-2006
* Damien Bergamini <damien.bergamini@free.fr>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*-
* Driver for Analog Devices Eagle chipset.
* http://www.analog.com/
*/
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/kthread.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_atm.h>
#include <net/if_media.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_atm.h>
#include <netinet/if_ether.h>
#endif
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/ezload.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/ueaglereg.h>
#include <dev/usb/ueaglevar.h>
#ifdef USB_DEBUG
#define DPRINTF(x) do { if (ueagledebug > 0) printf x; } while (0)
#define DPRINTFN(n, x) do { if (ueagledebug >= (n)) printf x; } while (0)
int ueagledebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n, x)
#endif
/* various supported device vendors/products */
static const struct ueagle_type {
struct usb_devno dev;
const char *fw;
} ueagle_devs[] = {
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEI }, NULL },
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEI_NF }, "ueagleI" },
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEII }, NULL },
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEII_NF }, "ueagleII" },
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEIIC }, NULL },
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEIIC_NF }, "ueagleII" },
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEIII }, NULL },
{ { USB_VENDOR_ANALOG, USB_PRODUCT_ANALOG_EAGLEIII_NF }, "ueagleIII" },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_HEINEKEN_A }, NULL },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_HEINEKEN_A_NF }, "ueagleI" },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_HEINEKEN_B }, NULL },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_HEINEKEN_B_NF }, "ueagleI" },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_MILLER_A }, NULL },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_MILLER_A_NF }, "ueagleI" },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_MILLER_B }, NULL },
{ { USB_VENDOR_USR, USB_PRODUCT_USR_MILLER_B_NF }, "ueagleI" }
};
#define ueagle_lookup(v, p) \
((struct ueagle_type *)usb_lookup(ueagle_devs, v, p))
void ueagle_attachhook(void *);
int ueagle_getesi(struct ueagle_softc *, uint8_t *);
void ueagle_loadpage(void *);
void ueagle_request(struct ueagle_softc *, uint16_t, uint16_t,
void *, int);
#ifdef USB_DEBUG
void ueagle_dump_cmv(struct ueagle_softc *, struct ueagle_cmv *);
#endif
int ueagle_cr(struct ueagle_softc *, uint32_t, uint16_t,
uint32_t *);
int ueagle_cw(struct ueagle_softc *, uint32_t, uint16_t, uint32_t);
int ueagle_stat(struct ueagle_softc *);
void ueagle_stat_thread(void *);
int ueagle_boot(struct ueagle_softc *);
void ueagle_swap_intr(struct ueagle_softc *, struct ueagle_swap *);
void ueagle_cmv_intr(struct ueagle_softc *, struct ueagle_cmv *);
void ueagle_intr(usbd_xfer_handle, usbd_private_handle,
usbd_status);
uint32_t ueagle_crc_update(uint32_t, uint8_t *, int);
void ueagle_push_cell(struct ueagle_softc *, uint8_t *);
void ueagle_rxeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
void ueagle_txeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
int ueagle_encap(struct ueagle_softc *, struct mbuf *);
void ueagle_start(struct ifnet *);
int ueagle_open_vcc(struct ueagle_softc *,
struct atm_pseudoioctl *);
int ueagle_close_vcc(struct ueagle_softc *,
struct atm_pseudoioctl *);
int ueagle_ioctl(struct ifnet *, u_long, caddr_t);
int ueagle_open_pipes(struct ueagle_softc *);
void ueagle_close_pipes(struct ueagle_softc *);
int ueagle_init(struct ifnet *);
void ueagle_stop(struct ifnet *, int);
int ueagle_match(struct device *, void *, void *);
void ueagle_attach(struct device *, struct device *, void *);
int ueagle_detach(struct device *, int);
int ueagle_activate(struct device *, enum devact);
struct cfdriver ueagle_cd = {
NULL, "ueagle", DV_DULL
};
const struct cfattach ueagle_ca = {
sizeof(struct ueagle_softc),
ueagle_match,
ueagle_attach,
ueagle_detach,
ueagle_activate,
};
int
ueagle_match(struct device *parent, void *match, void *aux)
{
struct usb_attach_arg *uaa = aux;
if (uaa->iface != NULL)
return UMATCH_NONE;
return (ueagle_lookup(uaa->vendor, uaa->product) != NULL) ?
UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
}
void
ueagle_attachhook(void *xsc)
{
char *firmwares[2];
struct ueagle_softc *sc = xsc;
firmwares[0] = (char *)sc->fw;
firmwares[1] = NULL;
if (ezload_downloads_and_reset(sc->sc_udev, firmwares) != 0) {
printf("%s: could not download firmware\n",
sc->sc_dev.dv_xname);
return;
}
}
void
ueagle_attach(struct device *parent, struct device *self, void *aux)
{
struct ueagle_softc *sc = (struct ueagle_softc *)self;
struct usb_attach_arg *uaa = aux;
struct ifnet *ifp = &sc->sc_if;
char *devinfop;
uint8_t addr[ETHER_ADDR_LEN];
sc->sc_udev = uaa->device;
printf("\n");
/*
* Pre-firmware modems must be flashed and reset first. They will
* automatically detach themselves from the bus and reattach later
* with a new product Id.
*/
sc->fw = ueagle_lookup(uaa->vendor, uaa->product)->fw;
if (sc->fw != NULL) {
if (rootvp == NULL)
mountroothook_establish(ueagle_attachhook, sc);
else
ueagle_attachhook(sc);
/* processing of pre-firmware modems ends here */
return;
}
devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
printf("%s: %s\n", sc->sc_dev.dv_xname, devinfop);
usbd_devinfo_free(devinfop);
if (usbd_set_config_no(sc->sc_udev, UEAGLE_CONFIG_NO, 0) != 0) {
printf("%s: could not set configuration no\n",
sc->sc_dev.dv_xname);
return;
}
if (ueagle_getesi(sc, addr) != 0) {
printf("%s: could not read end system identifier\n",
sc->sc_dev.dv_xname);
return;
}
printf("%s: address: %02x:%02x:%02x:%02x:%02x:%02x\n",
sc->sc_dev.dv_xname, addr[0], addr[1], addr[2], addr[3],
addr[4], addr[5]);
usb_init_task(&sc->sc_swap_task, ueagle_loadpage, sc);
ifp->if_softc = sc;
ifp->if_flags = IFF_SIMPLEX;
ifp->if_init = ueagle_init;
ifp->if_ioctl = ueagle_ioctl;
ifp->if_start = ueagle_start;
IFQ_SET_READY(&ifp->if_snd);
memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
if_attach(ifp);
atm_ifattach(ifp);
/* override default MTU value (9180 is too large for us) */
ifp->if_mtu = UEAGLE_IFMTU;
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_RAW, 0);
#endif
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
&sc->sc_dev);
}
int
ueagle_detach(struct device *self, int flags)
{
struct ueagle_softc *sc = (struct ueagle_softc *)self;
struct ifnet *ifp = &sc->sc_if;
if (sc->fw != NULL)
return 0; /* shortcut for pre-firmware devices */
sc->gone = 1;
ueagle_stop(ifp, 1);
/* wait for stat thread to exit properly */
if (sc->stat_thread != NULL) {
DPRINTFN(3, ("%s: waiting for stat thread to exit\n",
sc->sc_dev.dv_xname));
tsleep(sc->stat_thread, PZERO, "ueaglestat", 0);
DPRINTFN(3, ("%s: stat thread exited properly\n",
sc->sc_dev.dv_xname));
}
if_detach(ifp);
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
&sc->sc_dev);
return 0;
}
/*
* Retrieve the device End System Identifier (MAC address).
*/
int
ueagle_getesi(struct ueagle_softc *sc, uint8_t *addr)
{
usb_string_descriptor_t us;
usbd_status error;
uint16_t c;
int i, len;
error = usbd_get_string_desc(sc->sc_udev, UEAGLE_ESISTR, 0, &us, &len);
if (error != 0)
return error;
if (us.bLength < (6 + 1) * 2)
return 1;
for (i = 0; i < 6 * 2; i++) {
if ((c = UGETW(us.bString[i])) & 0xff00)
return 1; /* not 8-bit clean */
if (i & 1)
addr[i / 2] <<= 4;
else
addr[i / 2] = 0;
if (c >= '0' && c <= '9')
addr[i / 2] |= c - '0';
else if (c >= 'a' && c <= 'f')
addr[i / 2] |= c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
addr[i / 2] |= c - 'A' + 10;
else
return 1;
}
return 0;
}
void
ueagle_loadpage(void *xsc)
{
struct ueagle_softc *sc = xsc;
usbd_xfer_handle xfer;
struct ueagle_block_info bi;
uint16_t pageno = sc->pageno;
uint16_t ovl = sc->ovl;
uint8_t pagecount, blockcount;
uint16_t blockaddr, blocksize;
uint32_t pageoffset;
uint8_t *p;
int i;
p = sc->dsp;
pagecount = *p++;
if (pageno >= pagecount) {
printf("%s: invalid page number %u requested\n",
sc->sc_dev.dv_xname, pageno);
return;
}
p += 4 * pageno;
pageoffset = UGETDW(p);
if (pageoffset == 0)
return;
p = sc->dsp + pageoffset;
blockcount = *p++;
DPRINTF(("%s: sending %u blocks for fw page %u\n",
sc->sc_dev.dv_xname, blockcount, pageno));
if ((xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
printf("%s: could not allocate xfer\n",
sc->sc_dev.dv_xname);
return;
}
USETW(bi.wHdr, UEAGLE_BLOCK_INFO_HDR);
USETW(bi.wOvl, ovl);
USETW(bi.wOvlOffset, ovl | 0x8000);
for (i = 0; i < blockcount; i++) {
blockaddr = UGETW(p); p += 2;
blocksize = UGETW(p); p += 2;
USETW(bi.wSize, blocksize);
USETW(bi.wAddress, blockaddr);
USETW(bi.wLast, (i == blockcount - 1) ? 1 : 0);
/* send block info through the IDMA pipe */
usbd_setup_xfer(xfer, sc->pipeh_idma, sc, &bi, sizeof bi, 0,
UEAGLE_IDMA_TIMEOUT, NULL);
if (usbd_sync_transfer(xfer) != 0) {
printf("%s: could not transfer block info\n",
sc->sc_dev.dv_xname);
break;
}
/* send block data through the IDMA pipe */
usbd_setup_xfer(xfer, sc->pipeh_idma, sc, p, blocksize, 0,
UEAGLE_IDMA_TIMEOUT, NULL);
if (usbd_sync_transfer(xfer) != 0) {
printf("%s: could not transfer block data\n",
sc->sc_dev.dv_xname);
break;
}
p += blocksize;
}
usbd_free_xfer(xfer);
}
void
ueagle_request(struct ueagle_softc *sc, uint16_t val, uint16_t index,
void *data, int len)
{
usb_device_request_t req;
usbd_status error;
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UEAGLE_REQUEST;
USETW(req.wValue, val);
USETW(req.wIndex, index);
USETW(req.wLength, len);
error = usbd_do_request_async(sc->sc_udev, &req, data);
if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS)
printf("%s: could not send request\n", sc->sc_dev.dv_xname);
}
#ifdef USB_DEBUG
void
ueagle_dump_cmv(struct ueagle_softc *sc, struct ueagle_cmv *cmv)
{
printf(" Preamble: 0x%04x\n", UGETW(cmv->wPreamble));
printf(" Destination: %s (0x%02x)\n",
(cmv->bDst == UEAGLE_HOST) ? "Host" : "Modem", cmv->bDst);
printf(" Type: %u\n", cmv->bFunction >> 4);
printf(" Subtype: %u\n", cmv->bFunction & 0xf);
printf(" Index: %u\n", UGETW(cmv->wIndex));
printf(" Address: %c%c%c%c.%u\n",
cmv->dwSymbolicAddress[1], cmv->dwSymbolicAddress[0],
cmv->dwSymbolicAddress[3], cmv->dwSymbolicAddress[2],
UGETW(cmv->wOffsetAddress));
printf(" Data: 0x%08x\n", UGETDATA(cmv->dwData));
}
#endif
int
ueagle_cr(struct ueagle_softc *sc, uint32_t address, uint16_t offset,
uint32_t *data)
{
struct ueagle_cmv cmv;
usbd_status error;
int s;
USETW(cmv.wPreamble, UEAGLE_CMV_PREAMBLE);
cmv.bDst = UEAGLE_MODEM;
cmv.bFunction = UEAGLE_CR;
USETW(cmv.wIndex, sc->index);
USETW(cmv.wOffsetAddress, offset);
USETDW(cmv.dwSymbolicAddress, address);
USETDATA(cmv.dwData, 0);
#ifdef USB_DEBUG
if (ueagledebug >= 15) {
printf("%s: reading CMV\n", sc->sc_dev.dv_xname);
ueagle_dump_cmv(sc, &cmv);
}
#endif
s = splusb();
ueagle_request(sc, UEAGLE_SETBLOCK, UEAGLE_MPTXSTART, &cmv, sizeof cmv);
/* wait at most 2 seconds for an answer */
error = tsleep(UEAGLE_COND_CMV(sc), PZERO, "cmv", 2 * hz);
if (error != 0) {
printf("%s: timeout waiting for CMV ack\n",
sc->sc_dev.dv_xname);
splx(s);
return error;
}
*data = sc->data;
splx(s);
return 0;
}
int
ueagle_cw(struct ueagle_softc *sc, uint32_t address, uint16_t offset,
uint32_t data)
{
struct ueagle_cmv cmv;
usbd_status error;
int s;
USETW(cmv.wPreamble, UEAGLE_CMV_PREAMBLE);
cmv.bDst = UEAGLE_MODEM;
cmv.bFunction = UEAGLE_CW;
USETW(cmv.wIndex, sc->index);
USETW(cmv.wOffsetAddress, offset);
USETDW(cmv.dwSymbolicAddress, address);
USETDATA(cmv.dwData, data);
#ifdef USB_DEBUG
if (ueagledebug >= 15) {
printf("%s: writing CMV\n", sc->sc_dev.dv_xname);
ueagle_dump_cmv(sc, &cmv);
}
#endif
s = splusb();
ueagle_request(sc, UEAGLE_SETBLOCK, UEAGLE_MPTXSTART, &cmv, sizeof cmv);
/* wait at most 2 seconds for an answer */
error = tsleep(UEAGLE_COND_CMV(sc), PZERO, "cmv", 2 * hz);
if (error != 0) {
printf("%s: timeout waiting for CMV ack\n",
sc->sc_dev.dv_xname);
splx(s);
return error;
}
splx(s);
return 0;
}
int
ueagle_stat(struct ueagle_softc *sc)
{
struct ifnet *ifp = &sc->sc_if;
uint32_t data;
usbd_status error;
#define CR(sc, address, offset, data) do { \
if ((error = ueagle_cr(sc, address, offset, data)) != 0) \
return error; \
} while (0)
CR(sc, UEAGLE_CMV_STAT, 0, &sc->stats.phy.status);
switch ((sc->stats.phy.status >> 8) & 0xf) {
case 0: /* idle */
DPRINTFN(3, ("%s: waiting for synchronization\n",
sc->sc_dev.dv_xname));
return ueagle_cw(sc, UEAGLE_CMV_CNTL, 0, 2);
case 1: /* initialization */
DPRINTFN(3, ("%s: initializing\n", sc->sc_dev.dv_xname));
return ueagle_cw(sc, UEAGLE_CMV_CNTL, 0, 2);
case 2: /* operational */
DPRINTFN(4, ("%s: operational\n", sc->sc_dev.dv_xname));
break;
default: /* fail ... */
DPRINTFN(3, ("%s: synchronization failed\n",
sc->sc_dev.dv_xname));
ueagle_init(ifp);
return 1;
}
CR(sc, UEAGLE_CMV_DIAG, 1, &sc->stats.phy.flags);
if (sc->stats.phy.flags & 0x10) {
DPRINTF(("%s: delineation LOSS\n", sc->sc_dev.dv_xname));
sc->stats.phy.status = 0;
ueagle_init(ifp);
return 1;
}
CR(sc, UEAGLE_CMV_RATE, 0, &data);
sc->stats.phy.dsrate = ((data >> 16) & 0x1ff) * 32;
sc->stats.phy.usrate = (data & 0xff) * 32;
CR(sc, UEAGLE_CMV_DIAG, 23, &data);
sc->stats.phy.attenuation = (data & 0xff) / 2;
CR(sc, UEAGLE_CMV_DIAG, 3, &sc->stats.atm.cells_crc_errors);
CR(sc, UEAGLE_CMV_DIAG, 22, &sc->stats.phy.dserror);
CR(sc, UEAGLE_CMV_DIAG, 25, &sc->stats.phy.dsmargin);
CR(sc, UEAGLE_CMV_DIAG, 46, &sc->stats.phy.userror);
CR(sc, UEAGLE_CMV_DIAG, 49, &sc->stats.phy.usmargin);
CR(sc, UEAGLE_CMV_DIAG, 51, &sc->stats.phy.rxflow);
CR(sc, UEAGLE_CMV_DIAG, 52, &sc->stats.phy.txflow);
CR(sc, UEAGLE_CMV_DIAG, 54, &sc->stats.phy.dsunc);
CR(sc, UEAGLE_CMV_DIAG, 58, &sc->stats.phy.usunc);
CR(sc, UEAGLE_CMV_INFO, 8, &sc->stats.phy.vidco);
CR(sc, UEAGLE_CMV_INFO, 14, &sc->stats.phy.vidcpe);
if (sc->pipeh_tx != NULL)
return 0;
return ueagle_open_pipes(sc);
#undef CR
}
void
ueagle_stat_thread(void *arg)
{
struct ueagle_softc *sc = arg;
for (;;) {
if (ueagle_stat(sc) != 0)
break;
usbd_delay_ms(sc->sc_udev, 5000);
}
wakeup(sc->stat_thread);
kthread_exit(0);
}
int
ueagle_boot(struct ueagle_softc *sc)
{
uint16_t zero = 0; /* ;-) */
usbd_status error;
#define CW(sc, address, offset, data) do { \
if ((error = ueagle_cw(sc, address, offset, data)) != 0) \
return error; \
} while (0)
ueagle_request(sc, UEAGLE_SETMODE, UEAGLE_BOOTIDMA, NULL, 0);
ueagle_request(sc, UEAGLE_SETMODE, UEAGLE_STARTRESET, NULL, 0);
usbd_delay_ms(sc->sc_udev, 200);
ueagle_request(sc, UEAGLE_SETMODE, UEAGLE_ENDRESET, NULL, 0);
ueagle_request(sc, UEAGLE_SET2183DATA, UEAGLE_MPTXMAILBOX, &zero, 2);
ueagle_request(sc, UEAGLE_SET2183DATA, UEAGLE_MPRXMAILBOX, &zero, 2);
ueagle_request(sc, UEAGLE_SET2183DATA, UEAGLE_SWAPMAILBOX, &zero, 2);
usbd_delay_ms(sc->sc_udev, 1000);
sc->pageno = 0;
sc->ovl = 0;
ueagle_loadpage(sc);
/* wait until modem reaches operationnal state */
error = tsleep(UEAGLE_COND_READY(sc), PZERO | PCATCH, "boot", 10 * hz);
if (error != 0) {
printf("%s: timeout waiting for operationnal state\n",
sc->sc_dev.dv_xname);
return error;
}
CW(sc, UEAGLE_CMV_CNTL, 0, 1);
/* send configuration options */
CW(sc, UEAGLE_CMV_OPTN, 0, UEAGLE_OPTN0);
CW(sc, UEAGLE_CMV_OPTN, 2, UEAGLE_OPTN2);
CW(sc, UEAGLE_CMV_OPTN, 7, UEAGLE_OPTN7);
/* continue with synchronization */
CW(sc, UEAGLE_CMV_CNTL, 0, 2);
return kthread_create(ueagle_stat_thread, sc, &sc->stat_thread,
sc->sc_dev.dv_xname);
#undef CW
}
void
ueagle_swap_intr(struct ueagle_softc *sc, struct ueagle_swap *swap)
{
#define rotbr(v, n) ((v) >> (n) | (v) << (8 - (n)))
sc->pageno = swap->bPageNo;
sc->ovl = rotbr(swap->bOvl, 4);
usb_add_task(sc->sc_udev, &sc->sc_swap_task);
#undef rotbr
}
/*
* This function handles spontaneous CMVs and CMV acknowledgements sent by the
* modem on the interrupt pipe.
*/
void
ueagle_cmv_intr(struct ueagle_softc *sc, struct ueagle_cmv *cmv)
{
#ifdef USB_DEBUG
if (ueagledebug >= 15) {
printf("%s: receiving CMV\n", sc->sc_dev.dv_xname);
ueagle_dump_cmv(sc, cmv);
}
#endif
if (UGETW(cmv->wPreamble) != UEAGLE_CMV_PREAMBLE) {
printf("%s: received CMV with invalid preamble\n",
sc->sc_dev.dv_xname);
return;
}
if (cmv->bDst != UEAGLE_HOST) {
printf("%s: received CMV with bad direction\n",
sc->sc_dev.dv_xname);
return;
}
/* synchronize our current CMV index with the modem */
sc->index = UGETW(cmv->wIndex) + 1;
switch (cmv->bFunction) {
case UEAGLE_MODEMREADY:
wakeup(UEAGLE_COND_READY(sc));
break;
case UEAGLE_CR_ACK:
sc->data = UGETDATA(cmv->dwData);
/* FALLTHROUGH */
case UEAGLE_CW_ACK:
wakeup(UEAGLE_COND_CMV(sc));
break;
}
}
void
ueagle_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct ueagle_softc *sc = priv;
struct ueagle_intr *intr;
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
printf("%s: abnormal interrupt status: %s\n",
sc->sc_dev.dv_xname, usbd_errstr(status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->pipeh_intr);
return;
}
intr = (struct ueagle_intr *)sc->ibuf;
switch (UGETW(intr->wInterrupt)) {
case UEAGLE_INTR_SWAP:
ueagle_swap_intr(sc, (struct ueagle_swap *)(intr + 1));
break;
case UEAGLE_INTR_CMV:
ueagle_cmv_intr(sc, (struct ueagle_cmv *)(intr + 1));
break;
default:
printf("%s: caught unknown interrupt\n",
sc->sc_dev.dv_xname);
}
}
static const uint32_t ueagle_crc32_table[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc,
0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f,
0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a,
0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8,
0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e,
0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84,
0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027,
0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022,
0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077,
0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c,
0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1,
0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb,
0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d,
0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f,
0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044,
0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689,
0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683,
0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59,
0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c,
0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e,
0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48,
0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2,
0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601,
0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604,
0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6,
0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad,
0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7,
0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd,
0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b,
0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679,
0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12,
0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af,
0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5,
0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06,
0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03,
0xb1f740b4
};
uint32_t
ueagle_crc_update(uint32_t crc, uint8_t *buf, int len)
{
for (; len != 0; len--, buf++)
crc = ueagle_crc32_table[(crc >> 24) ^ *buf] ^ (crc << 8);
return crc;
}
/*
* Reassembly part of the software ATM AAL5 SAR.
*/
void
ueagle_push_cell(struct ueagle_softc *sc, uint8_t *cell)
{
struct ueagle_vcc *vcc = &sc->vcc;
struct ifnet *ifp;
struct mbuf *m;
uint32_t crc;
uint16_t pdulen, totlen;
int s;
sc->stats.atm.cells_received++;
if (!(vcc->flags & UEAGLE_VCC_ACTIVE) ||
ATM_CH_GETVPI(cell) != vcc->vpi ||
ATM_CH_GETVCI(cell) != vcc->vci) {
sc->stats.atm.vcc_no_conn++;
return;
}
if (vcc->flags & UEAGLE_VCC_DROP) {
if (ATM_CH_ISLASTCELL(cell)) {
vcc->flags &= ~UEAGLE_VCC_DROP;
sc->stats.atm.cspdus_dropped++;
}
sc->stats.atm.cells_dropped++;
return;
}
if (vcc->m == NULL) {
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
vcc->flags |= UEAGLE_VCC_DROP;
return;
}
MCLGET(m, M_DONTWAIT);
if (!(m->m_flags & M_EXT)) {
vcc->flags |= UEAGLE_VCC_DROP;
m_freem(m);
return;
}
vcc->m = m;
vcc->dst = mtod(m, uint8_t *);
vcc->limit = vcc->dst + MCLBYTES - ATM_CELL_PAYLOAD_SIZE;
}
if (vcc->dst > vcc->limit) {
vcc->flags |= UEAGLE_VCC_DROP;
sc->stats.atm.cells_dropped++;
goto fail;
}
memcpy(vcc->dst, cell + ATM_CELL_HEADER_SIZE, ATM_CELL_PAYLOAD_SIZE);
vcc->dst += ATM_CELL_PAYLOAD_SIZE;
if (!ATM_CH_ISLASTCELL(cell))
return;
/*
* Handle the last cell of the AAL5 CPCS-PDU.
*/
m = vcc->m;
totlen = vcc->dst - mtod(m, uint8_t *);
pdulen = AAL5_TR_GETPDULEN(cell);
if (totlen < pdulen + AAL5_TRAILER_SIZE) {
sc->stats.atm.cspdus_dropped++;
goto fail;
}
if (totlen >= pdulen + ATM_CELL_PAYLOAD_SIZE + AAL5_TRAILER_SIZE) {
sc->stats.atm.cspdus_dropped++;
goto fail;
}
crc = ueagle_crc_update(CRC_INITIAL, mtod(m, uint8_t *), totlen);
if (crc != CRC_MAGIC) {
sc->stats.atm.cspdus_crc_errors++;
goto fail;
}
/* finalize mbuf */
ifp = &sc->sc_if;
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = pdulen;
sc->stats.atm.cspdus_received++;
s = splnet();
#if NBPFILTER > 0
if (ifp->if_bpf != NULL)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif
/* send the AAL5 CPCS-PDU to the ATM layer */
ifp->if_ipackets++;
atm_input(ifp, &vcc->aph, m, vcc->rxhand);
vcc->m = NULL;
splx(s);
return;
fail: m_freem(vcc->m);
vcc->m = NULL;
}
void
ueagle_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv,
usbd_status status)
{
struct ueagle_isoreq *req = priv;
struct ueagle_softc *sc = req->sc;
uint32_t count;
uint8_t *p;
int i;
if (status == USBD_CANCELLED)
return;
for (i = 0; i < UEAGLE_NISOFRMS; i++) {
count = req->frlengths[i];
p = req->offsets[i];
while (count >= ATM_CELL_SIZE) {
ueagle_push_cell(sc, p);
p += ATM_CELL_SIZE;
count -= ATM_CELL_SIZE;
}
#ifdef DIAGNOSTIC
if (count > 0) {
printf("%s: truncated cell (%u bytes)\n",
sc->sc_dev.dv_xname, count);
}
#endif
req->frlengths[i] = sc->isize;
}
usbd_setup_isoc_xfer(req->xfer, sc->pipeh_rx, req, req->frlengths,
UEAGLE_NISOFRMS, USBD_NO_COPY, ueagle_rxeof);
usbd_transfer(xfer);
}
void
ueagle_txeof(usbd_xfer_handle xfer, usbd_private_handle priv,
usbd_status status)
{
struct ueagle_txreq *req = priv;
struct ueagle_softc *sc = req->sc;
struct ifnet *ifp = &sc->sc_if;
int s;
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
printf("%s: could not transmit buffer: %s\n",
sc->sc_dev.dv_xname, usbd_errstr(status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->pipeh_tx);
ifp->if_oerrors++;
return;
}
s = splnet();
ifp->if_opackets++;
ifp->if_flags &= ~IFF_OACTIVE;
ueagle_start(ifp);
splx(s);
}
/*
* Segmentation part of the software ATM AAL5 SAR.
*/
int
ueagle_encap(struct ueagle_softc *sc, struct mbuf *m0)
{
struct ueagle_vcc *vcc = &sc->vcc;
struct ueagle_txreq *req;
struct mbuf *m;
uint8_t *src, *dst;
uint32_t crc;
int n, cellleft, mleft;
usbd_status error;
req = &sc->txreqs[0];
m_adj(m0, sizeof (struct atm_pseudohdr));
dst = req->buf;
cellleft = 0;
crc = CRC_INITIAL;
for (m = m0; m != NULL; m = m->m_next) {
src = mtod(m, uint8_t *);
mleft = m->m_len;
crc = ueagle_crc_update(crc, src, mleft);
if (cellleft != 0) {
n = min(mleft, cellleft);
memcpy(dst, src, n);
dst += n;
src += n;
cellleft -= n;
mleft -= n;
}
while (mleft >= ATM_CELL_PAYLOAD_SIZE) {
memcpy(dst, vcc->ch, ATM_CELL_HEADER_SIZE);
dst += ATM_CELL_HEADER_SIZE;
memcpy(dst, src, ATM_CELL_PAYLOAD_SIZE);
dst += ATM_CELL_PAYLOAD_SIZE;
src += ATM_CELL_PAYLOAD_SIZE;
mleft -= ATM_CELL_PAYLOAD_SIZE;
sc->stats.atm.cells_transmitted++;
}
if (mleft != 0) {
memcpy(dst, vcc->ch, ATM_CELL_HEADER_SIZE);
dst += ATM_CELL_HEADER_SIZE;
memcpy(dst, src, mleft);
dst += mleft;
cellleft = ATM_CELL_PAYLOAD_SIZE - mleft;
sc->stats.atm.cells_transmitted++;
}
}
/*
* If there is not enough space to put the AAL5 trailer into this cell,
* pad the content of this cell with zeros and create a new cell which
* will contain no data except the AAL5 trailer itself.
*/
if (cellleft < AAL5_TRAILER_SIZE) {
memset(dst, 0, cellleft);
crc = ueagle_crc_update(crc, dst, cellleft);
dst += cellleft;
memcpy(dst, vcc->ch, ATM_CELL_HEADER_SIZE);
dst += ATM_CELL_HEADER_SIZE;
cellleft = ATM_CELL_PAYLOAD_SIZE;
sc->stats.atm.cells_transmitted++;
}
/*
* Fill the AAL5 CPCS-PDU trailer.
*/
memset(dst, 0, cellleft - AAL5_TRAILER_SIZE);
/* src now points to the beginning of the last cell */
src = dst + cellleft - ATM_CELL_SIZE;
ATM_CH_SETPTFLAGS(src, 1);
AAL5_TR_SETCPSUU(src, 0);
AAL5_TR_SETCPI(src, 0);
AAL5_TR_SETPDULEN(src, m0->m_pkthdr.len);
crc = ~ueagle_crc_update(crc, dst, cellleft - 4);
AAL5_TR_SETCRC(src, crc);
usbd_setup_xfer(req->xfer, sc->pipeh_tx, req, req->buf,
dst + cellleft - req->buf, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
UEAGLE_TX_TIMEOUT, ueagle_txeof);
error = usbd_transfer(req->xfer);
if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS)
return error;
sc->stats.atm.cspdus_transmitted++;
return 0;
}
void
ueagle_start(struct ifnet *ifp)
{
struct ueagle_softc *sc = ifp->if_softc;
struct mbuf *m0;
/* nothing goes out until modem is synchronized and VCC is opened */
if (!(sc->vcc.flags & UEAGLE_VCC_ACTIVE))
return;
if (sc->pipeh_tx == NULL)
return;
IFQ_POLL(&ifp->if_snd, m0);
if (m0 == NULL)
return;
IFQ_DEQUEUE(&ifp->if_snd, m0);
if (ueagle_encap(sc, m0) != 0) {
m_freem(m0);
return;
}
#if NBPFILTER > 0
if (ifp->if_bpf != NULL)
bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
#endif
m_freem(m0);
ifp->if_flags |= IFF_OACTIVE;
}
int
ueagle_open_vcc(struct ueagle_softc *sc, struct atm_pseudoioctl *api)
{
struct ueagle_vcc *vcc = &sc->vcc;
DPRINTF(("%s: opening ATM VCC\n", sc->sc_dev.dv_xname));
vcc->vpi = ATM_PH_VPI(&api->aph);
vcc->vci = ATM_PH_VCI(&api->aph);
vcc->rxhand = api->rxhand;
vcc->m = NULL;
vcc->aph = api->aph;
vcc->flags = UEAGLE_VCC_ACTIVE;
/* pre-calculate cell headers (HEC field is set by hardware) */
ATM_CH_FILL(vcc->ch, 0, vcc->vpi, vcc->vci, 0, 0, 0);
return 0;
}
int
ueagle_close_vcc(struct ueagle_softc *sc, struct atm_pseudoioctl *api)
{
DPRINTF(("%s: closing ATM VCC\n", sc->sc_dev.dv_xname));
sc->vcc.flags &= ~UEAGLE_VCC_ACTIVE;
return 0;
}
int
ueagle_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ueagle_softc *sc = ifp->if_softc;
struct atm_pseudoioctl *api;
struct ifaddr *ifa;
struct ifreq *ifr;
int s, error = 0;
s = splnet();
switch (cmd) {
case SIOCSIFADDR:
ifa = (struct ifaddr *)data;
ifp->if_flags |= IFF_UP;
ueagle_init(ifp);
#ifdef INET
ifa->ifa_rtrequest = atm_rtrequest;
#endif
break;
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
if (!(ifp->if_flags & IFF_RUNNING))
ueagle_init(ifp);
} else {
if (ifp->if_flags & IFF_RUNNING)
ueagle_stop(ifp, 1);
}
break;
case SIOCSIFMTU:
ifr = (struct ifreq *)data;
if (ifr->ifr_mtu > UEAGLE_IFMTU)
error = EINVAL;
else
ifp->if_mtu = ifr->ifr_mtu;
break;
case SIOCATMENA:
api = (struct atm_pseudoioctl *)data;
error = ueagle_open_vcc(sc, api);
break;
case SIOCATMDIS:
api = (struct atm_pseudoioctl *)data;
error = ueagle_close_vcc(sc, api);
break;
default:
error = EINVAL;
}
splx(s);
return error;
}
int
ueagle_open_pipes(struct ueagle_softc *sc)
{
usb_endpoint_descriptor_t *edesc;
usbd_interface_handle iface;
struct ueagle_txreq *txreq;
struct ueagle_isoreq *isoreq;
usbd_status error;
uint8_t *buf;
int i, j;
error = usbd_device2interface_handle(sc->sc_udev, UEAGLE_US_IFACE_NO,
&iface);
if (error != 0) {
printf("%s: could not get tx interface handle\n",
sc->sc_dev.dv_xname);
goto fail;
}
error = usbd_open_pipe(iface, UEAGLE_TX_PIPE, USBD_EXCLUSIVE_USE,
&sc->pipeh_tx);
if (error != 0) {
printf("%s: could not open tx pipe\n", sc->sc_dev.dv_xname);
goto fail;
}
for (i = 0; i < UEAGLE_TX_LIST_CNT; i++) {
txreq = &sc->txreqs[i];
txreq->sc = sc;
txreq->xfer = usbd_alloc_xfer(sc->sc_udev);
if (txreq->xfer == NULL) {
printf("%s: could not allocate tx xfer\n",
sc->sc_dev.dv_xname);
error = ENOMEM;
goto fail;
}
txreq->buf = usbd_alloc_buffer(txreq->xfer, UEAGLE_TXBUFLEN);
if (txreq->buf == NULL) {
printf("%s: could not allocate tx buffer\n",
sc->sc_dev.dv_xname);
error = ENOMEM;
goto fail;
}
}
error = usbd_device2interface_handle(sc->sc_udev, UEAGLE_DS_IFACE_NO,
&iface);
if (error != 0) {
printf("%s: could not get rx interface handle\n",
sc->sc_dev.dv_xname);
goto fail;
}
/* XXX: alternative interface number sould depend on downrate */
error = usbd_set_interface(iface, 8);
if (error != 0) {
printf("%s: could not set rx alternative interface\n",
sc->sc_dev.dv_xname);
goto fail;
}
edesc = usbd_get_endpoint_descriptor(iface, UEAGLE_RX_PIPE);
if (edesc == NULL) {
printf("%s: could not get rx endpoint descriptor\n",
sc->sc_dev.dv_xname);
error = EIO;
goto fail;
}
sc->isize = UGETW(edesc->wMaxPacketSize);
error = usbd_open_pipe(iface, UEAGLE_RX_PIPE, USBD_EXCLUSIVE_USE,
&sc->pipeh_rx);
if (error != 0) {
printf("%s: could not open rx pipe\n", sc->sc_dev.dv_xname);
goto fail;
}
for (i = 0; i < UEAGLE_NISOREQS; i++) {
isoreq = &sc->isoreqs[i];
isoreq->sc = sc;
isoreq->xfer = usbd_alloc_xfer(sc->sc_udev);
if (isoreq->xfer == NULL) {
printf("%s: could not allocate rx xfer\n",
sc->sc_dev.dv_xname);
error = ENOMEM;
goto fail;
}
buf = usbd_alloc_buffer(isoreq->xfer,
sc->isize * UEAGLE_NISOFRMS);
if (buf == NULL) {
printf("%s: could not allocate rx buffer\n",
sc->sc_dev.dv_xname);
error = ENOMEM;
goto fail;
}
for (j = 0; j < UEAGLE_NISOFRMS; j++) {
isoreq->frlengths[j] = sc->isize;
isoreq->offsets[j] = buf + j * sc->isize;
}
usbd_setup_isoc_xfer(isoreq->xfer, sc->pipeh_rx, isoreq,
isoreq->frlengths, UEAGLE_NISOFRMS, USBD_NO_COPY,
ueagle_rxeof);
usbd_transfer(isoreq->xfer);
}
ueagle_request(sc, UEAGLE_SETMODE, UEAGLE_LOOPBACKOFF, NULL, 0);
return 0;
fail: ueagle_close_pipes(sc);
return error;
}
void
ueagle_close_pipes(struct ueagle_softc *sc)
{
int i;
ueagle_request(sc, UEAGLE_SETMODE, UEAGLE_LOOPBACKON, NULL, 0);
/* free Tx resources */
if (sc->pipeh_tx != NULL) {
usbd_abort_pipe(sc->pipeh_tx);
usbd_close_pipe(sc->pipeh_tx);
sc->pipeh_tx = NULL;
}
for (i = 0; i < UEAGLE_TX_LIST_CNT; i++) {
if (sc->txreqs[i].xfer != NULL) {
usbd_free_xfer(sc->txreqs[i].xfer);
sc->txreqs[i].xfer = NULL;
}
}
/* free Rx resources */
if (sc->pipeh_rx != NULL) {
usbd_abort_pipe(sc->pipeh_rx);
usbd_close_pipe(sc->pipeh_rx);
sc->pipeh_rx = NULL;
}
for (i = 0; i < UEAGLE_NISOREQS; i++) {
if (sc->isoreqs[i].xfer != NULL) {
usbd_free_xfer(sc->isoreqs[i].xfer);
sc->isoreqs[i].xfer = NULL;
}
}
}
int
ueagle_init(struct ifnet *ifp)
{
struct ueagle_softc *sc = ifp->if_softc;
usbd_interface_handle iface;
usbd_status error;
size_t len;
ueagle_stop(ifp, 0);
error = usbd_device2interface_handle(sc->sc_udev, UEAGLE_US_IFACE_NO,
&iface);
if (error != 0) {
printf("%s: could not get idma interface handle\n",
sc->sc_dev.dv_xname);
goto fail;
}
error = usbd_open_pipe(iface, UEAGLE_IDMA_PIPE, USBD_EXCLUSIVE_USE,
&sc->pipeh_idma);
if (error != 0) {
printf("%s: could not open idma pipe\n",
sc->sc_dev.dv_xname);
goto fail;
}
error = usbd_device2interface_handle(sc->sc_udev, UEAGLE_INTR_IFACE_NO,
&iface);
if (error != 0) {
printf("%s: could not get interrupt interface handle\n",
sc->sc_dev.dv_xname);
goto fail;
}
error = loadfirmware("ueagle-dsp", &sc->dsp, &len);
if (error != 0) {
printf("%s: could not load firmware\n", sc->sc_dev.dv_xname);
goto fail;
}
error = usbd_open_pipe_intr(iface, UEAGLE_INTR_PIPE, USBD_SHORT_XFER_OK,
&sc->pipeh_intr, sc, sc->ibuf, UEAGLE_INTR_MAXSIZE, ueagle_intr,
UEAGLE_INTR_INTERVAL);
if (error != 0) {
printf("%s: could not open interrupt pipe\n",
sc->sc_dev.dv_xname);
goto fail;
}
error = ueagle_boot(sc);
if (error != 0) {
printf("%s: could not boot modem\n", sc->sc_dev.dv_xname);
goto fail;
}
/*
* Opening of tx and rx pipes if deferred after synchronization is
* established.
*/
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
return 0;
fail: ueagle_stop(ifp, 1);
return error;
}
void
ueagle_stop(struct ifnet *ifp, int disable)
{
struct ueagle_softc *sc = ifp->if_softc;
/* stop any pending task */
usb_rem_task(sc->sc_udev, &sc->sc_swap_task);
/* free Tx and Rx resources */
ueagle_close_pipes(sc);
/* free firmware */
if (sc->dsp != NULL) {
free(sc->dsp, M_DEVBUF);
sc->dsp = NULL;
}
/* free interrupt resources */
if (sc->pipeh_intr != NULL) {
usbd_abort_pipe(sc->pipeh_intr);
usbd_close_pipe(sc->pipeh_intr);
sc->pipeh_intr = NULL;
}
/* free IDMA resources */
if (sc->pipeh_idma != NULL) {
usbd_abort_pipe(sc->pipeh_idma);
usbd_close_pipe(sc->pipeh_idma);
sc->pipeh_idma = NULL;
}
/* reset statistics */
memset(&sc->stats, 0, sizeof (struct ueagle_stats));
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
}
int
ueagle_activate(struct device *self, enum devact act)
{
struct ueagle_softc *sc = (struct ueagle_softc *)self;
switch (act) {
case DVACT_ACTIVATE:
break;
case DVACT_DEACTIVATE:
sc->gone = 1;
break;
}
return 0;
}