From a6fb4b16d62b04c52ae60fbaaa102aa6f62c26d6 Mon Sep 17 00:00:00 2001 From: dmlb Date: Wed, 8 Mar 2000 23:28:06 +0000 Subject: Mega additions of NetBSD code - most of the NetBSD code is now in here, if not actually used yet. This created a lot of prototypes. I moved code around so that the functions are in the same order as NetBSD - this helps diff a lot. Things that are missing are the TX routine and ifmedia stuff and ioctls Not all of the stuff is enabled - we are missing calls in ccs_done and ioctl. Promiscious mode is working. Am almost ready to use the NetBSD start up routine - essentially all I need to do is not use a seperate timer to call download_timo. Other misc. things: callout_stop is a newish feature for cancelling a timer without argument checking it. tried to add a sysctl knob but it doesn't work in the module enabled infrastructure code to call netbsd bits. --- sys/dev/ray/if_ray.c | 3127 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 2040 insertions(+), 1087 deletions(-) (limited to 'sys/dev/ray/if_ray.c') diff --git a/sys/dev/ray/if_ray.c b/sys/dev/ray/if_ray.c index 9833061..7a96b0d 100644 --- a/sys/dev/ray/if_ray.c +++ b/sys/dev/ray/if_ray.c @@ -28,10 +28,40 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: if_ray.c,v 1.7 2000/03/05 22:24:30 dmlb Exp $ + * $Id: if_ray.c,v 1.8 2000/03/08 08:53:36 dmlb Exp $ * */ +/* $NetBSD: if_ray.c,v 1.12 2000/02/07 09:36:27 augustss Exp $ */ +/* + * Copyright (c) 2000 Christian E. Hopps + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + /* * * Card configuration @@ -117,10 +147,10 @@ * TODO * * _stop - mostly done - * would be nice to understand shutdown/power save to prevent RX + * would be nice to understand shutdown/or power save to prevent RX * _reset - done * just needs calling in the right places - * converted most panic to resets + * converted panics to resets - when tx packets are the wrong length * may be needed in a couple of other places when I do more commands * havenet - mostly done * i think i've got all the places to set it right, but not so sure @@ -128,13 +158,25 @@ * _unload - done * recreated most of stop but as card is unplugged don't try and * access it + * TX bpf - done + * RX bpf - done + * I would much prefer to have the complete 802.11 packet dropped to + * the bpf tap and then have a user land program parse the headers + * as needed. This way, tcpdump -w can be used to grab the raw data. If + * needed the 802.11 aware program can "translate" the .11 to ethernet + * for tcpdump -r + * + * XXX use std timeout code for download? should only be a move to ccs_done + * when checked can remove some of the stuff in download_timo as it + * duplicates check_ccs and ccs_done + * XXX and changing mode etc. + * XXX add the start_join_net - i needed it anyway - what about the update * - * TX bpf - * RX bpf - * shutdown * promisoius * multicast + * shutdown * ifp->if_hdr length + * antennas and rxlevel * * apm * @@ -142,19 +184,32 @@ * ioctls - translation, BSS_ID, countrycode * faster TX routine * more translations - * infrastructure mode + * infrastructure mode - maybe need some of the old stuff for checking? * differeniate between parameters set in attach and init * start_join_done needs a restart in download_done * spinning in ray_issue_cmd + * fix the XXX code in start_join_done * * command tracking - really needed? if not remove SCP_ stuff * will simplify ray_issue_cmd away + * + * callout handles need rationalising. can probably remove timerh and + * use ccs_timerh for download and sj_timerh + * + * ray_update_params_done needs work + * + * make RAY_DEBUG a knob somehow - either sysctl or IFF_DEBUG + * + * might need the mode changing and update stuff */ #define XXX 0 -#define XXX_TRACKING 0 -#define XXX_INFRA 0 -#define XXX_MCASTPROM 0 +#define XXX_DOWNLOAD_STD_TIMEOUT 0 +#define XXX_MCAST 0 +#define XXX_NETBSDTX 0 +#define XXX_PROM 0 +#define XXX_IOCTL 0 +#define XXX_NETBSD_SJ_NET 0 /* * XXX build options - move to LINT @@ -172,10 +227,12 @@ * 51 MBUFs dumped/packet types reported */ #ifndef RAY_DEBUG -#define RAY_DEBUG 2 +#define RAY_DEBUG 21 #endif -#define RAY_DOWNLOAD_TIMEOUT (hz/2) /* Timeout for CCS commands - only used for downloading startup parameters */ +#define RAY_DOWNLOAD_TIMEOUT (hz/2) /* Timeout for downloading startup parameters */ +#define RAY_CCS_TIMEOUT (hz/2) /* Timeout for CCS commands */ +#define RAY_CHECK_SCHED_TIMEOUT (hz) /* Time to wait until command retry, should be > RAY_CCS_TIMEOUT */ #define RAY_NEED_STARTJOIN_TIMO 0 /* Might be needed with build 4 */ #define RAY_SJ_TIMEOUT (90*hz) /* Timeout for failing STARTJOIN commands - only used with RAY_NEED_STARTJOIN_TIMO */ @@ -188,6 +245,8 @@ #define RAY_RESET_TIMEOUT (5*hz) /* Timeout for resetting the card */ +#define RAY_USE_CALLOUT_STOP 0 /* Set for kernels with callout_stop function - 3.3 and above */ + #define RAY_SIMPLE_TX 1 /* Simple TX routine */ #define RAY_DECENT_TX 0 /* Decent TX routine - tbd */ /* @@ -255,10 +314,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -297,6 +358,14 @@ #endif /* NAPM */ /* + * Sysctl knobs + */ +static int ray_debug = RAY_DEBUG; + +SYSCTL_NODE(_hw, OID_AUTO, ray, CTLFLAG_RW, 0, "Raylink Driver"); +SYSCTL_INT(_hw_ray, OID_AUTO, debug, CTLFLAG_RW, &ray_debug, RAY_DEBUG, ""); + +/* * One of these structures per allocated device */ struct ray_softc { @@ -305,6 +374,8 @@ struct ray_softc { struct ifmedia ifmedia; /* Ifnet common */ struct callout_handle \ timerh; /* Handle for timer */ + struct callout_handle \ + ccs_timerh; /* Handle for ccs timeouts */ #if RAY_NEED_STARTJOIN_TIMO struct callout_handle \ sj_timerh; /* Handle for start_join timer */ @@ -330,22 +401,39 @@ struct ray_softc { struct ray_ecf_startup_v5 \ sc_ecf_startup; /* Startup info from card */ - u_int8_t sc_ccsinuse[64];/* ccss' in use -- not for tx */ - size_t sc_ccs; /* ccs used by non-scheduled, */ - /* non-overlapping procedures */ - struct ray_cmd_net sc_cnet_1; /* current network params from */ - struct ray_net_params sc_cnet_2; /* starting/joining a network */ + struct ray_net_params \ + sc_cnet_2; /* starting/joining a network */ + u_int8_t sc_ap_status; /* Current operating mode */ + int sc_havenet; /* true if we have a network */ #if 0 + u_int8_t sc_oap_status; /* Old operating mode */ u_int8_t sc_cnwid[IEEE80211_NWID_LEN]; /* Last nwid */ u_int8_t sc_dnwid[IEEE80211_NWID_LEN]; /* Desired nwid */ - u_int8_t sc_omode; /* Old operating mode SC_MODE_xx */ - u_int8_t sc_mode; /* Current operating mode SC_MODE_xx */ u_int8_t sc_countrycode; /* Current country code */ u_int8_t sc_dcountrycode;/* Desired country code */ #endif - int sc_havenet; /* true if we have aquired a network */ + + int sc_promisc; /* current set value */ + int sc_running; /* things we are doing */ + int sc_scheduled; /* things we need to do */ + int sc_timoneed; /* set if timeout is sched */ + int sc_timocheck; /* set if timeout is sched */ + u_int8_t sc_ccsinuse[64];/* ccss' in use -- not for tx */ + size_t sc_startccs; /* ccs of start/join */ + u_int sc_startcmd; /* cmd (start | join) */ + + int sc_checkcounters; + u_int64_t sc_rxoverflow; /* Number of rx overflows */ + u_int64_t sc_rxcksum; /* Number of checksum errors */ + u_int64_t sc_rxhcksum; /* Number of header checksum errors */ + u_int8_t sc_rxnoise; /* Average receiver level */ + + struct ray_param_req \ + *sc_repreq; /* used to return values */ + struct ray_param_req \ + *sc_updreq; /* to the user */ }; static struct ray_softc ray_softc[NRAY]; @@ -364,7 +452,6 @@ static struct ray_softc ray_softc[NRAY]; #define sc_priv_join sc_cnet_2.p_privacy_can_join /* Remember to add to the debug macro and ioctl*/ -/* XXX might not need these */ /* Commands -- priority given to LSB */ #define SCP_FIRST 0x0001 #define SCP_UPDATESUBCMD 0x0001 @@ -374,8 +461,13 @@ static struct ray_softc ray_softc[NRAY]; /* Update sub commands -- issues are serialized priority to LSB */ #define SCP_UPD_FIRST 0x0100 +#if XXX_DOWNLOAD_STD_TIMEOUT #define SCP_UPD_STARTUP 0x0100 #define SCP_UPD_STARTJOIN 0x0200 +#else +#define SCP_UPD_STARTUP 0 +#define SCP_UPD_STARTJOIN 0 +#endif /* XXX_DOWNLOAD_STD_TIMEOUT */ #define SCP_UPD_PROMISC 0x0400 #define SCP_UPD_MCAST 0x0800 #define SCP_UPD_UPDATEPARAMS 0x1000 @@ -386,7 +478,6 @@ static struct ray_softc ray_softc[NRAY]; #define SCP_TIMOCHECK_CMD_MASK \ (SCP_UPD_UPDATEPARAMS | SCP_UPD_STARTUP | SCP_UPD_MCAST | \ SCP_UPD_PROMISC) -/*XXX might not need these */ /* * Translation types @@ -395,58 +486,102 @@ static struct ray_softc ray_softc[NRAY]; #define SC_TRANSLATE_WEBGEAR 0 /* - * PCMCIA driver definition - */ -static int ray_pccard_init __P((struct pccard_devinfo *dev_p)); -static void ray_pccard_unload __P((struct pccard_devinfo *dev_p)); -static int ray_pccard_intr __P((struct pccard_devinfo *dev_p)); - -PCCARD_MODULE(ray, ray_pccard_init, ray_pccard_unload, ray_pccard_intr, 0, net_imask); - -/* - * ISA driver definition - */ -static int ray_probe __P((struct isa_device *dev)); -static int ray_attach __P((struct isa_device *dev)); -struct isa_driver raydriver = { - ray_probe, - ray_attach, - "ray", - 1 -}; - -/* - * Network driver definition - */ -static void ray_start __P((struct ifnet *ifp)); -static int ray_ioctl __P((struct ifnet *ifp, u_long command, caddr_t data)); -static void ray_watchdog __P((struct ifnet *ifp)); -static void ray_init __P((void *xsc)); -static void ray_stop __P((struct ray_softc *sc)); - -/* - * Internal utilites + * Prototyping */ +static int ray_attach __P((struct isa_device *dev)); static int ray_alloc_ccs __P((struct ray_softc *sc, size_t *ccsp, u_int cmd, u_int track)); static void ray_ccs_done __P((struct ray_softc *sc, size_t ccs)); +static void ray_check_ccs __P((void *arg)); +static void ray_check_scheduled __P((void *arg)); +static void ray_cmd_cancel __P((struct ray_softc *sc, int cmdf)); +static void ray_cmd_done __P((struct ray_softc *sc, int cmdf)); +static int ray_cmd_is_running __P((struct ray_softc *sc, int cmdf)); +static int ray_cmd_is_scheduled __P((struct ray_softc *sc, int cmdf)); +static void ray_cmd_ran __P((struct ray_softc *sc, int cmdf)); +static void ray_cmd_schedule __P((struct ray_softc *sc, int cmdf)); static void ray_download_params __P((struct ray_softc *sc)); static void ray_download_timo __P((void *xsc)); +#if RAY_DEBUG > 50 +static void ray_dump_mbuf __P((struct ray_softc *sc, struct mbuf *m, char *s)); +#endif /* RAY_DEBUG > 50 */ static u_int8_t ray_free_ccs __P((struct ray_softc *sc, size_t ccs)); +#if XXX_NETBSDTX +static void ray_free_ccs_chain __P((struct ray_softc *sc, u_int ni)); +#endif /* XXX_NETBSDTX */ +static int ray_intr __P((struct pccard_devinfo *dev_p)); +static int ray_ioctl __P((struct ifnet *ifp, u_long command, caddr_t data)); +static void ray_init __P((void *xsc)); static int ray_issue_cmd __P((struct ray_softc *sc, size_t ccs, u_int track)); +static int ray_pccard_init __P((struct pccard_devinfo *dev_p)); +static int ray_pccard_intr __P((struct pccard_devinfo *dev_p)); +static void ray_pccard_unload __P((struct pccard_devinfo *dev_p)); +static int ray_probe __P((struct isa_device *dev)); +static void ray_rcs_intr __P((struct ray_softc *sc, size_t ccs)); + +static void ray_report_params __P((struct ray_softc *sc)); static void ray_reset __P((struct ray_softc *sc)); static void ray_reset_timo __P((void *xsc)); -static void ray_rcs_intr __P((struct ray_softc *sc, size_t ccs)); static void ray_rx __P((struct ray_softc *sc, size_t rcs)); +static void ray_set_pending __P((struct ray_softc *sc, u_int cmdf)); +static int ray_simple_cmd __P((struct ray_softc *sc, u_int cmd, u_int track)); +static void ray_start __P((struct ifnet *ifp)); +static void ray_start_assoc __P((struct ray_softc *sc)); static void ray_start_done __P((struct ray_softc *sc, size_t ccs, u_int8_t status)); +static void ray_start_sc __P((struct ray_softc *sc)); static void ray_start_timo __P((void *xsc)); static size_t ray_start_wrhdr __P((struct ray_softc *sc, struct ether_header *eh, size_t bufp)); static void ray_start_join_done __P((struct ray_softc *sc, size_t ccs, u_int8_t status)); +static void ray_start_join_net __P((struct ray_softc *sc)); #if RAY_NEED_STARTJOIN_TIMO static void ray_start_join_timo __P((void *xsc)); #endif /* RAY_NEED_STARTJOIN_TIMO */ -#if RAY_DEBUG > 50 -static void ray_dump_mbuf __P((struct ray_softc *sc, struct mbuf *m, char *s)); -#endif /* RAY_DEBUG > 50 */ +static void ray_stop __P((struct ray_softc *sc)); +static void ray_update_error_counters \ + __P((struct ray_softc *sc)); +static void ray_update_mcast __P((struct ray_softc *sc)); +static void ray_update_params __P((struct ray_softc *sc)); +static void ray_update_params_done __P((struct ray_softc *sc, size_t ccs, u_int stat)); +static void ray_update_promisc __P((struct ray_softc *sc)); +static void ray_update_subcmd __P((struct ray_softc *sc)); +static int ray_user_update_params __P((struct ray_softc *sc, struct ray_param_req *pr)); +static int ray_user_report_params __P((struct ray_softc *sc, struct ray_param_req *pr)); +static void ray_watchdog __P((struct ifnet *ifp)); + +/* + * PCMCIA driver definition + */ +PCCARD_MODULE(ray, ray_pccard_init, ray_pccard_unload, ray_pccard_intr, 0, net_imask); + +/* + * ISA driver definition + */ +struct isa_driver raydriver = { + ray_probe, + ray_attach, + "ray", + 1 +}; + +/* + * Command function tables - based on bit index in SCP_xx + */ +typedef void (*ray_cmd_func_t)(struct ray_softc *); +static ray_cmd_func_t ray_cmdtab[] = { + ray_update_subcmd, /* SCP_UPDATESUBCMD */ + ray_start_assoc, /* SCP_STARTASSOC */ + ray_report_params, /* SCP_REPORTPARAMS */ + ray_start_sc /* SCP_IFSTART */ +}; +static int ray_ncmdtab = sizeof(ray_cmdtab) / sizeof(*ray_cmdtab); + +static ray_cmd_func_t ray_subcmdtab[] = { + ray_download_params, /* SCP_UPD_STARTUP */ + ray_start_join_net, /* SCP_UPD_STARTJOIN */ + ray_update_promisc, /* SCP_UPD_PROMISC */ + ray_update_mcast, /* SCP_UPD_MCAST */ + ray_update_params /* SCP_UPD_UPDATEPARAMS */ +}; +static int ray_nsubcmdtab = sizeof(ray_subcmdtab) / sizeof(*ray_subcmdtab); /* * Indirections for reading/writing shared memory - from NetBSD/if_ray.c @@ -494,15 +629,21 @@ static void ray_dump_mbuf __P((struct ray_softc *sc, struct mbuf *m, char *s)); /* * Macro's and constants */ +#ifndef RAY_CCS_TIMEOUT +#define RAY_CCS_TIMEOUT (hz / 2) +#endif +#ifndef RAY_CHECK_SCHED_TIMEOUT +#define RAY_CHECK_SCHED_TIMEOUT (hz) +#endif #ifndef RAY_DOWNLOAD_TIMEOUT #define RAY_DOWNLOAD_TIMEOUT (hz / 2) #endif -#ifndef RAY_START_TIMEOUT -#define RAY_START_TIMEOUT (hz / 2) -#endif #ifndef RAY_RESET_TIMEOUT #define RAY_RESET_TIMEOUT (10 * hz) #endif +#ifndef RAY_START_TIMEOUT +#define RAY_START_TIMEOUT (hz / 2) +#endif #if RAY_SIMPLE_TX #define RAY_IFQ_MAXLEN (2) #else if RAY_DECENT_TX @@ -516,11 +657,9 @@ static void ray_dump_mbuf __P((struct ray_softc *sc, struct mbuf *m, char *s)); #define RAY_HCS_INTR(sc) (ray_read_reg(sc, RAY_HCSIR) & RAY_HCSIR_IRQ) /* - * XXX * As described in if_xe.c... * * Horrid stuff for accessing CIS tuples and remapping common memory... - * XXX */ #define CARD_MAJOR 50 static int ray_attr_write __P((struct ray_softc *sc, off_t offset, u_int8_t byte)); @@ -548,7 +687,7 @@ ray_pccard_init (dev_p) RAY_DPRINTFN(5, ("ray%d: PCCard probe\n", dev_p->isahd.id_unit)); if (dev_p->isahd.id_unit >= NRAY) - return(ENODEV); + return (ENODEV); sc = &ray_softc[dev_p->isahd.id_unit]; @@ -562,7 +701,7 @@ ray_pccard_init (dev_p) doRemap = 0; if (sc->md.start == 0x0) { printf("ray%d: pccardd did not map CM - giving up\n", sc->unit); - return(ENXIO); + return (ENXIO); } if (sc->md.flags != MDF_ACTIVE) { printf("ray%d: Fixing up CM flags from 0x%x to 0x40\n", @@ -601,9 +740,9 @@ ray_pccard_init (dev_p) sc->slotnum); if (ray_attach(&dev_p->isahd)) - return(ENXIO); + return (ENXIO); - return(0); + return (0); } /* @@ -628,9 +767,19 @@ ray_pccard_unload (dev_p) /* * Clear out timers and sort out driver state + * + * We use callout_stop to unconditionally kill the ccs and general + * timers as they are used with multiple arguments. */ +#if RAY_USE_CALLOUT_STOP + callout_stop(sc->ccs_timerh); + callout_stop(sc->timerh); +#else + untimeout(ray_check_ccs, sc, sc->ccs_timerh); + untimeout(ray_check_scheduled, sc, sc->ccs_timerh); untimeout(ray_download_timo, sc, sc->timerh); untimeout(ray_reset_timo, sc, sc->timerh); +#endif /* RAY_USE_CALLOUT_STOP */ #if RAY_NEED_STARTJOIN_TIMO untimeout(ray_start_join_timo, sc, sc->sj_timerh); #endif /* RAY_NEED_STARTJOIN_TIMO */ @@ -658,59 +807,13 @@ ray_pccard_unload (dev_p) } /* - * PCCard interrupt. + * process an interrupt */ static int ray_pccard_intr (dev_p) struct pccard_devinfo *dev_p; { - struct ray_softc *sc; - struct ifnet *ifp; - int ccsi, handled; - - RAY_DPRINTFN(5, ("ray%d: PCCard intr\n", dev_p->isahd.id_unit)); - - sc = &ray_softc[dev_p->isahd.id_unit]; - ifp = &sc->arpcom.ac_if; - RAY_MAP_CM(sc); - - if (sc->gone) { - printf("ray%d: unloaded before interrupt!\n", sc->unit); - return(0); - } - - /* - * Check that the interrupt was for us, if so get the rcs/ccs and vector - * on the command contained within it. - */ - if (!RAY_HCS_INTR(sc)) { - - handled = 0; - - } else { - - handled = 1; - ccsi = SRAM_READ_1(sc, RAY_SCB_RCSI); - if (ccsi <= RAY_CCS_LAST) - ray_ccs_done(sc, RAY_CCS_ADDRESS(ccsi)); - else if (ccsi <= RAY_RCS_LAST) - ray_rcs_intr(sc, RAY_CCS_ADDRESS(ccsi)); - else - printf("ray%d: ray_intr bad ccs index %d\n", sc->unit, ccsi); - - } - - if (handled) - RAY_HCS_CLEAR_INTR(sc); - - RAY_DPRINTFN(10, ("ray%d: interrupt %s handled\n", - sc->unit, handled?"was":"not")); - - /* Send any packets lying around */ - if (!(ifp->if_flags & IFF_OACTIVE) && (ifp->if_snd.ifq_head != NULL)) - ray_start(ifp); - - return(handled); + return (ray_intr(dev_p)); } /* @@ -723,7 +826,7 @@ ray_probe (dev_p) RAY_DPRINTFN(5, ("ray%d: ISA probe\n", dev_p->id_unit)); - return(0); + return (0); } /* @@ -745,7 +848,7 @@ ray_attach (dev_p) if (sc->gone) { printf("ray%d: unloaded before attach!\n", sc->unit); - return(1); + return (1); } /* @@ -767,14 +870,14 @@ ray_attach (dev_p) "\007RERSERVED1" "\008TEST_COMPLETE" ); - return(1); + return (1); } if (sc->sc_version != RAY_ECFS_BUILD_4 && sc->sc_version != RAY_ECFS_BUILD_5 ) { printf("ray%d: unsupported firmware version 0x%0x\n", sc->unit, ep->e_fw_build_string); - return(1); + return (1); } if (bootverbose || RAY_DEBUG) { @@ -847,6 +950,7 @@ ray_attach (dev_p) if (ifunit(ifname) == NULL) { callout_handle_init(&sc->timerh); + callout_handle_init(&sc->ccs_timerh); #if RAY_NEED_STARTJOIN_TIMO callout_handle_init(&sc->sj_timerh); #endif /* RAY_NEED_STARTJOIN_TIMO */ @@ -863,392 +967,280 @@ ray_attach (dev_p) #endif /* XXX */ } - return(0); + return (0); } /* - * Network start. + * Network initialisation. * - * Start sending a packet. + * Start up flow is as follows. + * The kernel calls ray_init when the interface is assigned an address. + * + * ray_init does a bit of house keeping before calling ray_download_params. * - * We make two assumptions here: - * 1) That the current priority is set to splimp _before_ this code - * is called *and* is returned to the appropriate priority after - * return - * 2) That the IFF_OACTIVE flag is checked before this code is called - * (i.e. that the output part of the interface is idle) + * ray_download_params fills the startup parameter structure out and + * sends it to the card. The download command simply completes so we + * use schedule a timeout function call to ray_download_timo instead + * of spin locking. We pass the ccs in use via sc->sc_startcss. + * + * ray_download_timo checks the ccs for command completion and/or + * errors. Then it tells the card to start an adhoc network or join a + * managed network. This should complete via the interrupt mechanism, + * but the NetBSD driver includes a timeout for some buggy stuff + * somewhere - I've left the hooks in but don't use them. The interrupt + * handler passes control to ray_start_join_done - the ccs is handled + * by the interrupt mechanism. + * + * Once ray_start_join_done has checked the ccs and uploaded/updated + * the network parameters we are ready to process packets. It is then + * safe to call ray_start which is done by the interrupt handler. */ static void -ray_start (ifp) - register struct ifnet *ifp; +ray_init (xsc) + void *xsc; { - struct ray_softc *sc; - struct mbuf *m0, *m; - struct ether_header *eh; - size_t ccs, bufp; - int i, pktlen, len; - u_int8_t status; - - RAY_DPRINTFN(5, ("ray%d: Network start\n", ifp->if_unit)); + struct ray_softc *sc = xsc; + struct ray_ecf_startup_v5 *ep; + struct ifnet *ifp; + size_t ccs; + int i; - sc = ifp->if_softc; + RAY_DPRINTFN(5, ("ray%d: Network init\n", sc->unit)); RAY_MAP_CM(sc); - /* - * Some simple checks first - */ if (sc->gone) { - printf("ray%d: unloaded before start!\n", sc->unit); + printf("ray%d: unloaded before init!\n", sc->unit); return; } - if ((ifp->if_flags & IFF_RUNNING) == 0 || !sc->sc_havenet) - return; - if (!RAY_ECF_READY(sc)) { - RAY_DPRINTFN(1, ("ray%d: ray_start busy, schedule a timeout\n", - sc->unit)); - sc->start_timerh = timeout(ray_start_timo, sc, RAY_START_TIMEOUT); - return; - } else - untimeout(ray_start_timo, sc, sc->start_timerh); + + ifp = &sc->arpcom.ac_if; + + if ((ifp->if_flags & IFF_RUNNING)) + ray_stop(sc); /* - * Simple one packet at a time TX routine - probably appaling performance - * and we certainly chew CPU. However bing to windows boxes shows - * a reliance on the far end too: + * Reset instance variables * - * Libretto 50CT (75MHz Pentium) with FreeBSD-3.1 to - * Nonname box Windows 95C (133MHz AMD 5x86) 996109bps - * AST J30 Windows 95A (100MHz Pentium) 1307791bps + * The first set are network parameters that are fully initialised + * when the card starts or joins the network. * - * Flow is - * get a ccs - * build the packet - * set IFF_OACTIVE - * interrupt the card to send the packet - * exit + * The second set are network parameters that are downloaded to + * the card. * - * wait for interrupt telling us the packet has been sent - * clear IFF_OACTIVE - * get called by the interrupt routine if any packets left + * The third set are driver parameters. + * + * All of the variables in these sets can be updated by the card or ioctls. */ +#if XXX + see the ray_attach section for stuff to move +#endif + sc->sc_upd_param = 0; + bzero(sc->sc_bss_id, sizeof(sc->sc_bss_id)); + sc->sc_inited = 0; + sc->sc_def_txrate = 0; + sc->sc_encrypt = 0; + + sc->sc_net_type = RAY_MIB_NET_TYPE_DEFAULT; + sc->sc_ap_status = RAY_MIB_AP_STATUS_DEFAULT; + bzero(sc->sc_ssid, sizeof(sc->sc_ssid)); + strncpy(sc->sc_ssid, RAY_MIB_SSID_DEFAULT, RAY_MAXSSIDLEN); + sc->sc_priv_start = RAY_MIB_PRIVACY_MUST_START_DEFAULT; + sc->sc_priv_join = RAY_MIB_PRIVACY_CAN_JOIN_DEFAULT; + sc->sc_promisc = !!(ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)); + + sc->sc_havenet = 0; + sc->translation = SC_TRANSLATE_WEBGEAR; + + /* Set all ccs to be free */ + bzero(sc->sc_ccsinuse, sizeof(sc->sc_ccsinuse)); + sc->sc_startccs = RAY_CCS_LAST + 1; + ccs = RAY_CCS_ADDRESS(0); + for (i = 0; i < RAY_CCS_LAST; ccs += RAY_CCS_SIZE, i++) + RAY_CCS_FREE(sc, ccs); + /* Clear any pending interrupts */ + RAY_HCS_CLEAR_INTR(sc); + +#if XXX + Not sure why I really need this - maybe best to deal with + this when resets are requested by me? +#endif /* XXX */ /* - * Find a free ccs; if none available wave good bye and exit. - * - * We find a ccs before we process the mbuf so that we are sure it - * is worthwhile processing the packet. All errors in the mbuf - * processing are either errors in the mbuf or gross configuration - * errors and the packet wouldn't get through anyway. - * - * Don't forget to clear the ccs on errors. + * Get startup results - the card may have been reset */ - i = RAY_CCS_TX_FIRST; - do { - status = SRAM_READ_FIELD_1(sc, RAY_CCS_ADDRESS(i), ray_cmd, c_status); - if (status == RAY_CCS_STATUS_FREE) - break; - i++; - } while (i <= RAY_CCS_TX_LAST); - if (i > RAY_CCS_TX_LAST) { - ifp->if_flags |= IFF_OACTIVE; - return; + ep = &sc->sc_ecf_startup; + ray_read_region(sc, RAY_ECF_TO_HOST_BASE, ep, sizeof(sc->sc_ecf_startup)); + if (ep->e_status != RAY_ECFS_CARD_OK) { + printf("ray%d: card failed self test: status 0x%b\n", sc->unit, + ep->e_status, + "\020" /* print in hex */ + "\001RESERVED0" + "\002PROC_SELF_TEST" + "\003PROG_MEM_CHECKSUM" + "\004DATA_MEM_TEST" + "\005RX_CALIBRATION" + "\006FW_VERSION_COMPAT" + "\007RERSERVED1" + "\008TEST_COMPLETE" + ); + return; /* XXX This doesn't mark the interface as down */ } - RAY_DPRINTFN(20, ("ray%d: ray_start using ccs 0x%02x\n", sc->unit, i)); /* - * Reserve and fill the ccs - must do the length later. - * - * Even though build 4 and build 5 have different fields all these - * are common apart from tx_rate. This will be overwritten later if - * needed. + * Fixup tib size to be correct - on build 4 it is garbage */ - ccs = RAY_CCS_ADDRESS(i); - bufp = RAY_TX_BASE + i * RAY_TX_BUF_SIZE; - bufp += sc->sc_tibsize; - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_status, RAY_CCS_STATUS_BUSY); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_cmd, RAY_CMD_TX_REQ); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_link, RAY_CCS_LINK_NULL); - SRAM_WRITE_FIELD_2(sc, ccs, ray_cmd_tx, c_bufp, bufp); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_tx_rate, sc->sc_def_txrate); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_apm_mode, 0); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_antenna, 0); - bufp += sizeof(struct ray_tx_phy_header); - + if (sc->sc_version == RAY_ECFS_BUILD_4 && sc->sc_tibsize == 0x55) + sc->sc_tibsize = sizeof(struct ray_tx_tib); + /* - * Get the mbuf and process it - we have to remember to free the - * ccs if there are any errors + * We are now up and running. Next we have to download network + * configuration into the card. We are busy until download is done. */ - IF_DEQUEUE(&ifp->if_snd, m0); - if (m0 == NULL) { - RAY_CCS_FREE(sc, ccs); - return; - } + ifp->if_flags |= IFF_RUNNING | IFF_OACTIVE; - for (pktlen = 0, m = m0; m != NULL; m = m->m_next) { - pktlen += m->m_len; - } - if (pktlen > ETHER_MAX_LEN - ETHER_CRC_LEN) { - RAY_DPRINTFN(1, ("ray%d: mbuf too long %d\n", sc->unit, pktlen)); - RAY_CCS_FREE(sc, ccs); - ifp->if_oerrors++; - m_freem(m0); - return; - } + ray_download_params(sc); - /* - * Translation - capability as described earlier - * - * Each case must write the 802.11 header using ray_start_wrhdr, - * passing a pointer to the ethernet header in and getting a new - * tc buffer pointer. Next remove/modify/addto the 802.3 and 802.2 - * headers as needed. - * - * We've pulled up the mbuf for you. - */ - if (m0->m_len < sizeof(struct ether_header)) - m = m_pullup(m, sizeof(struct ether_header)); - if (m0 == NULL) { - RAY_DPRINTFN(1, ("ray%d: ray_start could not pullup ether\n", sc->unit)); - RAY_CCS_FREE(sc, ccs); - ifp->if_oerrors++; - return; - } - eh = mtod(m0, struct ether_header *); - switch (sc->translation) { + return; +} - case SC_TRANSLATE_WEBGEAR: - bufp = ray_start_wrhdr(sc, eh, bufp); - break; +/* + * Network stop. + * + * Assumes that a ray_init is used to restart the card. + * + */ +static void +ray_stop (sc) + struct ray_softc *sc; +{ + struct ifnet *ifp; + int s; - default: - printf("ray%d: ray_start unknown translation type 0x%x - why?\n", - sc->unit, sc->translation); - RAY_CCS_FREE(sc, ccs); - ifp->if_oerrors++; - m0 = m_free(m0); - return; + RAY_DPRINTFN(5, ("ray%d: Network stop\n", sc->unit)); + RAY_MAP_CM(sc); - } - if (m0 == NULL) { - RAY_DPRINTFN(1, ("ray%d: ray_start could not translate mbuf\n", sc->unit)); - RAY_CCS_FREE(sc, ccs); - ifp->if_oerrors++; + if (sc->gone) { + printf("ray%d: unloaded before stop!\n", sc->unit); return; } - pktlen = sizeof(struct ieee80211_header); + + ifp = &sc->arpcom.ac_if; /* - * Copy the mbuf to the buffer in common memory - * - * We panic and don't bother wrapping as ethernet packets are 1518 - * bytes, we checked the mbuf earlier, and our TX buffers are 2048 - * bytes. We don't have 530 bytes of headers etc. so something - * must be fubar. + * Clear out timers and sort out driver state */ - for (m = m0; m != NULL; m = m->m_next) { - pktlen += m->m_len; - if ((len = m->m_len) == 0) - continue; - if ((bufp + len) < RAY_TX_END) - ray_write_region(sc, bufp, mtod(m, u_int8_t *), len); - else - panic("ray%d: ray_start tx buffer overflow\n", sc->unit); - bufp += len; - } - RAY_DMBUF_DUMP(sc, m0, "ray_start"); - m_free(m0); + untimeout(ray_download_timo, sc, sc->timerh); + untimeout(ray_reset_timo, sc, sc->timerh); +#if RAY_NEED_STARTJOIN_TIMO + untimeout(ray_start_join_timo, sc, sc->sj_timerh); +#endif /* RAY_NEED_STARTJOIN_TIMO */ + untimeout(ray_start_timo, sc, sc->start_timerh); + sc->sc_havenet = 0; /* - * Fill in a few loose ends and kick the card to send the packet + * Inhibit card - if we can't prevent reception then do not worry; + * stopping a NIC only guarantees no TX. */ - if (!RAY_ECF_READY(sc)) { - /* - * From NetBSD code: - * - * If this can really happen perhaps we need to save - * the chain and use it later. I think this might - * be a confused state though because we check above - * and don't issue any commands between. - */ - printf("ray%d: ray_tx device busy\n", sc->unit); - RAY_CCS_FREE(sc, ccs); - ifp->if_oerrors++; - return; - } - SRAM_WRITE_FIELD_2(sc, ccs, ray_cmd_tx, c_len, pktlen); - SRAM_WRITE_1(sc, RAY_SCB_CCSI, ccs); - ifp->if_opackets++; - ifp->if_flags |= IFF_OACTIVE; - RAY_ECF_START_CMD(sc); + s = splimp(); + /* XXX what does the SHUTDOWN command do? Or power saving in COR */ + splx(s); -#if XXX -#if NBPFILTER > 0 - /* XXX but the translation code dicks around with the mbuf structure sigh */ - /* XXX and I've freed the mbuf already */ - if (ifp->if_bpf) - bpf_mtap(ifp, m0); -#endif -#endif /* XXX */ + /* + * Mark as not running + */ + ifp->if_flags &= ~IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; return; } -#if XXX -netbsd - -driver uses a loop - repeat - get a ccs - get a mbuf - translate and send packet to shared ram - until (no more ccs's) || (no more mbuf's) - - send ccs chain to card - - exit - -Linux - -driver is simple single shot packet (with a lot of spinlocks!) - -general - -the tx space is 0x7000 = 28kB, and TX buffer size is 2048 so there -can be 14 requests at 2kB each - -from this 2k we have to remove the TIB - whatever that is - for data - - -netbsd: - we need to call _start after receiveing a packet to see - if any packets were queued whilst in the interrupt - - there is a potential race in obtaining ccss for the tx, in that - we might be in _start synchronously and then an rx interrupt - occurs. the rx will call _start and steal tx ccs from underneath - the interrupted entry. - - toptions - dont call _start from rx interrupt - - find a safe way of locking - - find a better way of obtaining ccs using next free avilable? - - look at other drivers - - use tsleep/wakeup - - some form of ring to hold ccs - - free lsit - - rework calling - -#endif /* - * TX completion routine. + * Reset the card * - * Clear ccs and network flags. + * I'm using the soft reset command in the COR register. I'm not sure + * if the sequence is right but it does seem to do the right thing. A + * nano second after reset is written the flashing light goes out, and + * a few seconds after the default is written the main card light goes + * out. We wait a while and then re-init the card. */ static void -ray_start_done (sc, ccs, status) +ray_reset (sc) struct ray_softc *sc; - size_t ccs; - u_int8_t status; { struct ifnet *ifp; - char *status_string[] = RAY_CCS_STATUS_STRINGS; - RAY_DPRINTFN(5, ("ray%d: ray_start_done\n", sc->unit)); + RAY_DPRINTFN(5, ("ray%d: ray_reset\n", sc->unit)); RAY_MAP_CM(sc); ifp = &sc->arpcom.ac_if; - if (status != RAY_CCS_STATUS_COMPLETE) { - printf("ray%d: ray_start tx completed but status is %s.\n", - sc->unit, status_string[status]); - ifp->if_oerrors++; - } + if (ifp->if_flags & IFF_RUNNING) + ray_stop(sc); - RAY_CCS_FREE(sc, ccs); - ifp->if_timer = 0; - if (ifp->if_flags & IFF_OACTIVE) - ifp->if_flags &= ~IFF_OACTIVE; + printf("ray%d: resetting card\n", sc->unit); + ray_attr_write((sc), RAY_COR, RAY_COR_RESET); + ray_attr_write((sc), RAY_COR, RAY_COR_DEFAULT); + sc->timerh = timeout(ray_reset_timo, sc, RAY_RESET_TIMEOUT); return; } /* - * Start timeout routine. - * - * Used when card was busy but we needed to send a packet. + * Finishing resetting and restarting the card */ static void -ray_start_timo (xsc) +ray_reset_timo (xsc) void *xsc; { struct ray_softc *sc = xsc; - struct ifnet *ifp; - int s; - RAY_DPRINTFN(5, ("ray%d: ray_start_timo\n", sc->unit)); + RAY_DPRINTFN(5, ("ray%d: ray_reset_timo\n", sc->unit)); RAY_MAP_CM(sc); - ifp = &sc->arpcom.ac_if; - - if (!(ifp->if_flags & IFF_OACTIVE) && (ifp->if_snd.ifq_head != NULL)) { - s = splimp(); - ray_start(ifp); - splx(s); + if (!RAY_ECF_READY(sc)) { + RAY_DPRINTFN(1, ("ray%d: ray_reset_timo still busy, re-schedule\n", + sc->unit)); + sc->timerh = timeout(ray_reset_timo, sc, RAY_RESET_TIMEOUT); + return; } + RAY_HCS_CLEAR_INTR(sc); + ray_init(sc); + return; } -/* - * Write an 802.11 header into the TX buffer and return the - * adjusted buffer pointer. - */ -static size_t -ray_start_wrhdr (sc, eh, bufp) - struct ray_softc *sc; - struct ether_header *eh; - size_t bufp; +static void +ray_watchdog (ifp) + register struct ifnet *ifp; { - struct ieee80211_header header; + struct ray_softc *sc; - RAY_DPRINTFN(5, ("ray%d: ray_start_wrhdr\n", sc->unit)); - RAY_MAP_CM(sc); + RAY_DPRINTFN(5, ("ray%d: Network watchdog\n", ifp->if_unit)); - bzero(&header, sizeof(struct ieee80211_header)); + sc = ifp->if_softc; + RAY_MAP_CM(sc); - header.i_fc[0] = (IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA); - if (sc->sc_net_type == RAY_MIB_NET_TYPE_ADHOC) { + if (sc->gone) { + printf("ray%d: unloaded before watchdog!\n", sc->unit); + return; + } - header.i_fc[1] = IEEE80211_FC1_STA_TO_STA; - bcopy(eh->ether_dhost, header.i_addr1, ETHER_ADDR_LEN); - bcopy(eh->ether_shost, header.i_addr2, ETHER_ADDR_LEN); - bcopy(sc->sc_bss_id, header.i_addr3, ETHER_ADDR_LEN); + printf("ray%d: watchdog timeout\n", sc->unit); - } else { -#if XXX_INFRA - if (sc->sc_ap_status == RAY_MIB_AP_STATUS_TERMINAL) { - - header.i_fc[1] = IEEE80211_FC1_STA_TO_AP; - bcopy(sc->sc_bss_id, header.i_addr1, ETHER_ADDR_LEN); - bcopy(eh->ether_shost, header.i_addr2, ETHER_ADDR_LEN); - bcopy(eh->ether_dhost, header.i_addr3, ETHER_ADDR_LEN); +/* XXX may need to have remedial action here + for example + ray_reset + ray_stop + ... + ray_init - } else - panic("ray%d: ray_start can't be an AP yet\n", sc-.unit); -#endif /* XXX_INFRA */ - } + do we only use on TX? + if so then we should clear OACTIVE etc. - ray_write_region(sc, bufp, (u_int8_t *)&header, - sizeof(struct ieee80211_header)); +*/ - return(bufp + sizeof(struct ieee80211_header)); + return; } /* @@ -1271,7 +1263,7 @@ ray_ioctl (ifp, command, data) if (sc->gone) { printf("ray%d: unloaded before ioctl!\n", sc->unit); ifp->if_flags &= ~IFF_RUNNING; - return(ENXIO); + return (ENXIO); } s = splimp(); @@ -1294,24 +1286,19 @@ ray_ioctl (ifp, command, data) if (ifp->if_flags & IFF_UP) { if (!(ifp->if_flags & IFF_RUNNING)) ray_init(sc); + ray_update_promisc(sc); } else { if (ifp->if_flags & IFF_RUNNING) ray_stop(sc); } - /* DROP THROUGH */ + /* XXX DROP THROUGH or not? */ -#if XXX case SIOCADDMULTI: case SIOCDELMULTI: - RAY_DPRINTFN(30, ("ray%d: ioctl called for ADDMULTI/DELMULTI\n, sc->unit")); - /* - * Multicast list has (maybe) changed; set the hardware filter - * accordingly. This also serves to deal with promiscuous mode - * if we have a BPF listener active. - */ + RAY_DPRINTFN(30, ("ray%d: ioctl called for ADDMULTI/DELMULTI\n", sc->unit)); +#if XXX_MCAST ray_setmulti(sc); -#endif /* XXX */ - +#endif /* XXX_MCAST */ error = 0; break; @@ -1352,283 +1339,860 @@ case SIOCGIFMEDIA: splx(s); - return(error); + return (error); } +/* + * Network start. + * + * Start sending a packet. + * + * We make two assumptions here: + * 1) That the current priority is set to splimp _before_ this code + * is called *and* is returned to the appropriate priority after + * return + * 2) That the IFF_OACTIVE flag is checked before this code is called + * (i.e. that the output part of the interface is idle) + */ static void -ray_watchdog (ifp) - register struct ifnet *ifp; +ray_start (ifp) + struct ifnet *ifp; { - struct ray_softc *sc; - - RAY_DPRINTFN(5, ("ray%d: Network watchdog\n", ifp->if_unit)); - - sc = ifp->if_softc; - RAY_MAP_CM(sc); - - if (sc->gone) { - printf("ray%d: unloaded before watchdog!\n", sc->unit); - return; - } - - printf("ray%d: watchdog timeout\n", sc->unit); - -/* XXX may need to have remedial action here - for example - ray_reset - ray_stop - ... - ray_init - - do we only use on TX? - if so then we should clear OACTIVE etc. - -*/ + RAY_DPRINTFN(5, ("ray%d: ray_start\n", ifp->if_unit)); - return; + ray_start_sc(ifp->if_softc); } -/* - * Network initialisation. - * - * Start up flow is as follows. - * The kernel calls ray_init when the interface is assigned an address. - * - * ray_init does a bit of house keeping before calling ray_download_params. - * - * ray_download_params fills the startup parameter structure out and - * sends it to the card. The download command simply completes so we - * use schedule a timeout function call to ray_download_timo instead - * of spin locking. We pass the ccs in use via sc->sc_css. - * - * ray_download_timo checks the ccs for command completion and/or - * errors. Then it tells the card to start an adhoc network or join a - * managed network. This should complete via the interrupt mechanism, - * but the NetBSD driver includes a timeout for some buggy stuff - * somewhere - I've left the hooks in but don't use them. The interrupt - * handler passes control to ray_start_join_done - the ccs is handled - * by the interrupt mechanism. - * - * Once ray_start_join_done has checked the ccs and uploaded/updated - * the network parameters we are ready to process packets. It is then - * safe to call ray_start which is done by the interrupt handler. - */ static void -ray_init (xsc) - void *xsc; +ray_start_sc (sc) + struct ray_softc *sc; { - struct ray_softc *sc = xsc; - struct ray_ecf_startup_v5 *ep; struct ifnet *ifp; - size_t ccs; - int i; + struct mbuf *m0, *m; + struct ether_header *eh; + size_t ccs, bufp; + int i, pktlen, len; + u_int8_t status; - RAY_DPRINTFN(5, ("ray%d: Network init\n", sc->unit)); + RAY_DPRINTFN(5, ("ray%d: ray_start_sc\n", sc->unit)); RAY_MAP_CM(sc); + ifp = &sc->arpcom.ac_if; + + /* + * Some simple checks first + */ if (sc->gone) { - printf("ray%d: unloaded before init!\n", sc->unit); + printf("ray%d: unloaded before start!\n", sc->unit); return; } - - ifp = &sc->arpcom.ac_if; - - if ((ifp->if_flags & IFF_RUNNING)) - ray_stop(sc); + if ((ifp->if_flags & IFF_RUNNING) == 0 || !sc->sc_havenet) + return; + if (!RAY_ECF_READY(sc)) { + RAY_DPRINTFN(1, ("ray%d: ray_start busy, schedule a timeout\n", + sc->unit)); + sc->start_timerh = timeout(ray_start_timo, sc, RAY_START_TIMEOUT); + return; + } else + untimeout(ray_start_timo, sc, sc->start_timerh); /* - * Reset instance variables + * Simple one packet at a time TX routine - probably appaling performance + * and we certainly chew CPU. However bing to windows boxes shows + * a reliance on the far end too: * - * The first set are network parameters that are fully initialised - * when the card starts or joins the network. + * 1500k default rate * - * The second set are network parameters that are downloaded to - * the card. + * Libretto 50CT (75MHz Pentium) with FreeBSD-3.1 to + * Nonname box Windows 95C (133MHz AMD 5x86) 996109bps + * AST J30 Windows 95A (100MHz Pentium) 1307791bps * - * The third set are driver parameters. + * 2000k default rate * - * All of the variables in these sets can be updated by the card or ioctls. + * Libretto 50CT (75MHz Pentium) with FreeBSD-3.1 to + * Nonname box Windows 95C (133MHz AMD 5x86) 1087049bps + * AST J30 Windows 95A (100MHz Pentium) 1307791bps + * + * Flow is + * get a ccs + * build the packet + * set IFF_OACTIVE + * interrupt the card to send the packet + * exit + * + * wait for interrupt telling us the packet has been sent + * clear IFF_OACTIVE + * get called by the interrupt routine if any packets left */ -#if XXX - see the ray_attach section for stuff to move -#endif - sc->sc_upd_param = 0; - bzero(sc->sc_bss_id, sizeof(sc->sc_bss_id)); - sc->sc_inited = 0; - sc->sc_def_txrate = 0; - sc->sc_encrypt = 0; - sc->sc_net_type = RAY_MIB_NET_TYPE_DEFAULT; - bzero(sc->sc_ssid, sizeof(sc->sc_ssid)); - strncpy(sc->sc_ssid, RAY_MIB_SSID_DEFAULT, RAY_MAXSSIDLEN); - sc->sc_priv_start = RAY_MIB_PRIVACY_MUST_START_DEFAULT; - sc->sc_priv_join = RAY_MIB_PRIVACY_CAN_JOIN_DEFAULT; -#if XXX_MCASTPROM - sc->sc_promisc = !!(sc->sc_if.if_flags & (IFF_PROMISC|IFF_ALLMULTI)); -#endif /* XXX_MCASTPROM */ - - sc->sc_havenet = 0; - sc->translation = SC_TRANSLATE_WEBGEAR; - - /* Set all ccs to be free */ - bzero(sc->sc_ccsinuse, sizeof(sc->sc_ccsinuse)); - sc->sc_ccs = RAY_CCS_LAST + 1; - ccs = RAY_CCS_ADDRESS(0); - for (i = 0; i < RAY_CCS_LAST; ccs += RAY_CCS_SIZE, i++) - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd, c_status, RAY_CCS_STATUS_FREE); - - /* Clear any pending interrupts */ - RAY_HCS_CLEAR_INTR(sc); - -#if XXX - Not sure why I really need this - maybe best to deal with - this when resets are requested by me? -#endif /* XXX */ /* - * Get startup results - the card may have been reset + * Find a free ccs; if none available wave good bye and exit. + * + * We find a ccs before we process the mbuf so that we are sure it + * is worthwhile processing the packet. All errors in the mbuf + * processing are either errors in the mbuf or gross configuration + * errors and the packet wouldn't get through anyway. + * + * Don't forget to clear the ccs on errors. */ - ep = &sc->sc_ecf_startup; - ray_read_region(sc, RAY_ECF_TO_HOST_BASE, ep, sizeof(sc->sc_ecf_startup)); - if (ep->e_status != RAY_ECFS_CARD_OK) { - printf("ray%d: card failed self test: status 0x%b\n", sc->unit, - ep->e_status, - "\020" /* print in hex */ - "\001RESERVED0" - "\002PROC_SELF_TEST" - "\003PROG_MEM_CHECKSUM" - "\004DATA_MEM_TEST" - "\005RX_CALIBRATION" - "\006FW_VERSION_COMPAT" - "\007RERSERVED1" - "\008TEST_COMPLETE" - ); - return; /* XXX This doesn't mark the interface as down */ + i = RAY_CCS_TX_FIRST; + do { + status = SRAM_READ_FIELD_1(sc, RAY_CCS_ADDRESS(i), ray_cmd, c_status); + if (status == RAY_CCS_STATUS_FREE) + break; + i++; + } while (i <= RAY_CCS_TX_LAST); + if (i > RAY_CCS_TX_LAST) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + RAY_DPRINTFN(20, ("ray%d: ray_start using ccs 0x%02x\n", sc->unit, i)); + + /* + * Reserve and fill the ccs - must do the length later. + * + * Even though build 4 and build 5 have different fields all these + * are common apart from tx_rate. This will be overwritten later if + * needed. + */ + ccs = RAY_CCS_ADDRESS(i); + bufp = RAY_TX_BASE + i * RAY_TX_BUF_SIZE; + bufp += sc->sc_tibsize; + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_status, RAY_CCS_STATUS_BUSY); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_cmd, RAY_CMD_TX_REQ); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_link, RAY_CCS_LINK_NULL); + SRAM_WRITE_FIELD_2(sc, ccs, ray_cmd_tx, c_bufp, bufp); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_tx_rate, sc->sc_def_txrate); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_apm_mode, 0); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_tx, c_antenna, 0); + bufp += sizeof(struct ray_tx_phy_header); + + /* + * Get the mbuf and process it - we have to remember to free the + * ccs if there are any errors + */ + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) { + RAY_CCS_FREE(sc, ccs); + return; + } + + for (pktlen = 0, m = m0; m != NULL; m = m->m_next) { + pktlen += m->m_len; + } + if (pktlen > ETHER_MAX_LEN - ETHER_CRC_LEN) { + RAY_DPRINTFN(1, ("ray%d: mbuf too long %d\n", sc->unit, pktlen)); + RAY_CCS_FREE(sc, ccs); + ifp->if_oerrors++; + m_freem(m0); + return; + } + + /* XXX + * I would much prefer to have the complete 802.11 packet dropped to + * the bpf tap and then have a user land program parse the headers + * as needed. This way, tcpdump -w can be used to grab the raw data. If + * needed the 802.11 aware program can "translate" the .11 to ethernet + * for tcpdump -r. + */ +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp, m0); +#endif /* NBPFILTER */ + + /* + * Translation - capability as described earlier + * + * Each case must write the 802.11 header using ray_start_wrhdr, + * passing a pointer to the ethernet header in and getting a new + * tc buffer pointer. Next remove/modify/addto the 802.3 and 802.2 + * headers as needed. + * + * We've pulled up the mbuf for you. + * + */ + if (m0->m_len < sizeof(struct ether_header)) + m = m_pullup(m, sizeof(struct ether_header)); + if (m0 == NULL) { + RAY_DPRINTFN(1, ("ray%d: ray_start could not pullup ether\n", sc->unit)); + RAY_CCS_FREE(sc, ccs); + ifp->if_oerrors++; + return; + } + eh = mtod(m0, struct ether_header *); + switch (sc->translation) { + + case SC_TRANSLATE_WEBGEAR: + bufp = ray_start_wrhdr(sc, eh, bufp); + break; + + default: + printf("ray%d: ray_start unknown translation type 0x%x - why?\n", + sc->unit, sc->translation); + RAY_CCS_FREE(sc, ccs); + ifp->if_oerrors++; + m0 = m_free(m0); + return; + + } + if (m0 == NULL) { + RAY_DPRINTFN(1, ("ray%d: ray_start could not translate mbuf\n", sc->unit)); + RAY_CCS_FREE(sc, ccs); + ifp->if_oerrors++; + return; + } + pktlen = sizeof(struct ieee80211_header); + + /* + * Copy the mbuf to the buffer in common memory + * + * We panic and don't bother wrapping as ethernet packets are 1518 + * bytes, we checked the mbuf earlier, and our TX buffers are 2048 + * bytes. We don't have 530 bytes of headers etc. so something + * must be fubar. + */ + for (m = m0; m != NULL; m = m->m_next) { + pktlen += m->m_len; + if ((len = m->m_len) == 0) + continue; + if ((bufp + len) < RAY_TX_END) + ray_write_region(sc, bufp, mtod(m, u_int8_t *), len); + else + panic("ray%d: ray_start tx buffer overflow\n", sc->unit); + bufp += len; + } + RAY_DMBUF_DUMP(sc, m0, "ray_start"); + + m_free(m0); + + /* + * Fill in a few loose ends and kick the card to send the packet + */ + if (!RAY_ECF_READY(sc)) { + /* + * From NetBSD code: + * + * If this can really happen perhaps we need to save + * the chain and use it later. I think this might + * be a confused state though because we check above + * and don't issue any commands between. + */ + printf("ray%d: ray_tx device busy\n", sc->unit); + RAY_CCS_FREE(sc, ccs); + ifp->if_oerrors++; + return; + } + SRAM_WRITE_FIELD_2(sc, ccs, ray_cmd_tx, c_len, pktlen); + SRAM_WRITE_1(sc, RAY_SCB_CCSI, ccs); + ifp->if_opackets++; + ifp->if_flags |= IFF_OACTIVE; + RAY_ECF_START_CMD(sc); + + return; +} +#if XXX_NETBSDTX +netbsd + +driver uses a loop + repeat + get a ccs + get a mbuf + translate and send packet to shared ram + until (no more ccs's) || (no more mbuf's) + + send ccs chain to card + + exit + +Linux + +driver is simple single shot packet (with a lot of spinlocks!) + +general + +the tx space is 0x7000 = 28kB, and TX buffer size is 2048 so there +can be 14 requests at 2kB each + +from this 2k we have to remove the TIB - whatever that is - for data + + +netbsd: + we need to call _start after receiveing a packet to see + if any packets were queued whilst in the interrupt + + there is a potential race in obtaining ccss for the tx, in that + we might be in _start synchronously and then an rx interrupt + occurs. the rx will call _start and steal tx ccs from underneath + the interrupted entry. + + toptions + dont call _start from rx interrupt + + find a safe way of locking + + find a better way of obtaining ccs using next free avilable? + + look at other drivers + + use tsleep/wakeup + + some form of ring to hold ccs + + free lsit + + rework calling +#endif XXX_NETBSDTX + +/* + * TX completion routine. + * + * Clear ccs and network flags. + */ +static void +ray_start_done (sc, ccs, status) + struct ray_softc *sc; + size_t ccs; + u_int8_t status; +{ + struct ifnet *ifp; + char *status_string[] = RAY_CCS_STATUS_STRINGS; + + RAY_DPRINTFN(5, ("ray%d: ray_start_done\n", sc->unit)); + RAY_MAP_CM(sc); + + ifp = &sc->arpcom.ac_if; + + if (status != RAY_CCS_STATUS_COMPLETE) { + printf("ray%d: ray_start tx completed but status is %s.\n", + sc->unit, status_string[status]); + ifp->if_oerrors++; + } + + RAY_CCS_FREE(sc, ccs); + ifp->if_timer = 0; + if (ifp->if_flags & IFF_OACTIVE) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +/* + * Start timeout routine. + * + * Used when card was busy but we needed to send a packet. + */ +static void +ray_start_timo (xsc) + void *xsc; +{ + struct ray_softc *sc = xsc; + struct ifnet *ifp; + int s; + + RAY_DPRINTFN(5, ("ray%d: ray_start_timo\n", sc->unit)); + RAY_MAP_CM(sc); + + ifp = &sc->arpcom.ac_if; + + if (!(ifp->if_flags & IFF_OACTIVE) && (ifp->if_snd.ifq_head != NULL)) { + s = splimp(); + ray_start(ifp); + splx(s); + } + + return; +} + +/* + * Write an 802.11 header into the TX buffer and return the + * adjusted buffer pointer. + */ +static size_t +ray_start_wrhdr (sc, eh, bufp) + struct ray_softc *sc; + struct ether_header *eh; + size_t bufp; +{ + struct ieee80211_header header; + + RAY_DPRINTFN(5, ("ray%d: ray_start_wrhdr\n", sc->unit)); + RAY_MAP_CM(sc); + + bzero(&header, sizeof(struct ieee80211_header)); + + header.i_fc[0] = (IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA); + if (sc->sc_net_type == RAY_MIB_NET_TYPE_ADHOC) { + + header.i_fc[1] = IEEE80211_FC1_STA_TO_STA; + bcopy(eh->ether_dhost, header.i_addr1, ETHER_ADDR_LEN); + bcopy(eh->ether_shost, header.i_addr2, ETHER_ADDR_LEN); + bcopy(sc->sc_bss_id, header.i_addr3, ETHER_ADDR_LEN); + + } else { + if (sc->sc_ap_status == RAY_MIB_AP_STATUS_TERMINAL) { + + header.i_fc[1] = IEEE80211_FC1_STA_TO_AP; + bcopy(sc->sc_bss_id, header.i_addr1, ETHER_ADDR_LEN); + bcopy(eh->ether_shost, header.i_addr2, ETHER_ADDR_LEN); + bcopy(eh->ether_dhost, header.i_addr3, ETHER_ADDR_LEN); + + } else + printf("ray%d: ray_start can't be an AP yet\n", sc->unit); } + ray_write_region(sc, bufp, (u_int8_t *)&header, + sizeof(struct ieee80211_header)); + + return (bufp + sizeof(struct ieee80211_header)); +} + +/* + * recevice a packet from the card + */ +static void +ray_rx (sc, rcs) + struct ray_softc *sc; + size_t rcs; +{ + struct ieee80211_header *header; + struct ether_header *eh; + struct ifnet *ifp; + struct mbuf *m0; + size_t pktlen, fraglen, readlen, tmplen; + size_t bufp, ebufp; + u_int8_t *dst, *src; + u_int8_t fc; + u_int first, ni, i; + + RAY_DPRINTFN(5, ("ray%d: ray_rx\n", sc->unit)); + RAY_MAP_CM(sc); + + RAY_DPRINTFN(20, ("ray%d: rcs chain - using rcs 0x%x\n", sc->unit, rcs)); + + ifp = &sc->arpcom.ac_if; + m0 = NULL; + readlen = 0; + /* - * Fixup tib size to be correct - on build 4 it is garbage + * Get first part of packet and the length. Do some sanity checks + * and get a mbuf. */ - if (sc->sc_version == RAY_ECFS_BUILD_4 && sc->sc_tibsize == 0x55) - sc->sc_tibsize = sizeof(struct ray_tx_tib); + first = RAY_CCS_INDEX(rcs); + pktlen = SRAM_READ_FIELD_2(sc, rcs, ray_cmd_rx, c_pktlen); + + if ((pktlen > MCLBYTES) || (pktlen < sizeof(struct ieee80211_header))) { + RAY_DPRINTFN(1, ("ray%d: ray_rx packet is too big or too small\n", + sc->unit)); + ifp->if_ierrors++; + goto skip_read; + } + + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 == NULL) { + RAY_DPRINTFN(1, ("ray%d: ray_rx MGETHDR failed\n", sc->unit)); + ifp->if_ierrors++; + goto skip_read; + } + if (pktlen > MHLEN) { + MCLGET(m0, M_DONTWAIT); + if ((m0->m_flags & M_EXT) == 0) { + RAY_DPRINTFN(1, ("ray%d: ray_rx MCLGET failed\n", sc->unit)); + ifp->if_ierrors++; + m_freem(m0); + m0 = NULL; + goto skip_read; + } + } + m0->m_pkthdr.rcvif = ifp; + m0->m_pkthdr.len = pktlen; + m0->m_len = pktlen; + dst = mtod(m0, u_int8_t *); /* - * We are now up and running. Next we have to download network - * configuration into the card. We are busy until download is done. + * Walk the fragment chain to build the complete packet. + * + * The use of two index variables removes a race with the + * hardware. If one index were used the clearing of the CCS would + * happen before reading the next pointer and the hardware can get in. + * Not my idea but verbatim from the NetBSD driver. */ - ifp->if_flags |= IFF_RUNNING | IFF_OACTIVE; + i = ni = first; + while ((i = ni) && (i != RAY_CCS_LINK_NULL)) { + rcs = RAY_CCS_ADDRESS(i); + ni = SRAM_READ_FIELD_1(sc, rcs, ray_cmd_rx, c_nextfrag); + bufp = SRAM_READ_FIELD_2(sc, rcs, ray_cmd_rx, c_bufp); + fraglen = SRAM_READ_FIELD_2(sc, rcs, ray_cmd_rx, c_len); + RAY_DPRINTFN(50, ("ray%d: ray_rx frag index %d len %d bufp 0x%x ni %d\n", + sc->unit, i, fraglen, (int)bufp, ni)); - ray_download_params(sc); + if (fraglen + readlen > pktlen) { + RAY_DPRINTFN(1, ("ray%d: ray_rx bad length current 0x%x pktlen 0x%x\n", + sc->unit, fraglen + readlen, pktlen)); + ifp->if_ierrors++; + m_freem(m0); + m0 = NULL; + goto skip_read; + } + if ((i < RAY_RCS_FIRST) || (i > RAY_RCS_LAST)) { + printf("ray%d: ray_rx bad rcs index 0x%x\n", sc->unit, i); + ifp->if_ierrors++; + m_freem(m0); + m0 = NULL; + goto skip_read; + } + + ebufp = bufp + fraglen; + if (ebufp <= RAY_RX_END) + ray_read_region(sc, bufp, dst, fraglen); + else { + ray_read_region(sc, bufp, dst, (tmplen = RAY_RX_END - bufp)); + ray_read_region(sc, RAY_RX_BASE, dst + tmplen, ebufp - RAY_RX_END); + } + dst += fraglen; + readlen += fraglen; + } + +skip_read: + + /* + * Walk the chain again to free the rcss. + */ + i = ni = first; + while ((i = ni) && (i != RAY_CCS_LINK_NULL)) { + rcs = RAY_CCS_ADDRESS(i); + ni = SRAM_READ_FIELD_1(sc, rcs, ray_cmd_rx, c_nextfrag); + RAY_CCS_FREE(sc, rcs); + } + + if (m0 == NULL) + return; + + RAY_DMBUF_DUMP(sc, m0, "ray_rx"); + + /* + * Check the 802.11 packet type and obtain the .11 src addresses. + * + * XXX CTL and MGT packets will have separate functions, DATA with here + * + * XXX This needs some work for INFRA mode + */ + header = mtod(m0, struct ieee80211_header *); + fc = header->i_fc[0]; + if ((fc & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { + RAY_DPRINTFN(1, ("ray%d: header not version 0 fc 0x%x\n", sc->unit, fc)); + ifp->if_ierrors++; + m_freem(m0); + return; + } + switch (fc & IEEE80211_FC0_TYPE_MASK) { + + case IEEE80211_FC0_TYPE_MGT: + printf("ray%d: ray_rx got a MGT packet - why?\n", sc->unit); + ifp->if_ierrors++; + m_freem(m0); + return; + + case IEEE80211_FC0_TYPE_CTL: + printf("ray%d: ray_rx got a CTL packet - why?\n", sc->unit); + ifp->if_ierrors++; + m_freem(m0); + return; + + case IEEE80211_FC0_TYPE_DATA: + RAY_DPRINTFN(50, ("ray%d: ray_rx got a DATA packet\n", sc->unit)); + break; + + default: + printf("ray%d: ray_rx got a unknown packet fc0 0x%x - why?\n", + sc->unit, fc); + ifp->if_ierrors++; + m_freem(m0); + return; + + } + fc = header->i_fc[1]; + switch (fc & IEEE80211_FC1_DS_MASK) { + + case IEEE80211_FC1_STA_TO_STA: + src = header->i_addr2; + RAY_DPRINTFN(50, ("ray%d: ray_rx packet from sta %6D\n", + sc->unit, src, ":")); + break; + + case IEEE80211_FC1_STA_TO_AP: + RAY_DPRINTFN(1, ("ray%d: ray_rx packet from sta %6D to ap %6D\n", + sc->unit, + header->i_addr2, ":", header->i_addr3, ":")); + ifp->if_ierrors++; + m_freem(m0); + break; + + case IEEE80211_FC1_AP_TO_STA: + RAY_DPRINTFN(1, ("ray%d: ray_rx packet from ap %6D\n", + sc->unit, + header->i_addr3, ":")); + ifp->if_ierrors++; + m_freem(m0); + break; + + case IEEE80211_FC1_AP_TO_AP: + RAY_DPRINTFN(1, ("ray%d: ray_rx saw packet between aps %6D %6D\n", + sc->unit, + header->i_addr1, ":", header->i_addr2, ":")); + ifp->if_ierrors++; + m_freem(m0); + return; + + default: + printf("ray%d: ray_rx packet type unknown fc1 0x%x - why?\n", + sc->unit, fc); + ifp->if_ierrors++; + m_freem(m0); + return; + } + + /* + * Translation - capability as described earlier + * + * Each case must remove the 802.11 header and leave an 802.3 + * header in the mbuf copy addresses as needed. + */ + switch (sc->translation) { + + case SC_TRANSLATE_WEBGEAR: + /* Nice and easy - just trim the 802.11 header */ + m_adj(m0, sizeof(struct ieee80211_header)); + break; + + default: + printf("ray%d: ray_rx unknown translation type 0x%x - why?\n", + sc->unit, sc->translation); + ifp->if_ierrors++; + m_freem(m0); + return; + + } + + /* + * Finally, do a bit of house keeping before sending the packet + * up the stack. + */ + ifp->if_ipackets++; +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp, m0); +#endif /* NBPFILTER */ +#if XXX_PROM +if_wi.c - might be needed if we hear our own broadcasts in promiscuous mode +but will not be if we dont see them + if ((ifp->if_flags & IFF_PROMISC) && + (bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN) && + (eh->ether_dhost[0] & 1) == 0) + ) { + m_freem(m0); + return; + } +#endif /* XXX_PROM */ + eh = mtod(m0, struct ether_header *); + m_adj(m0, sizeof(struct ether_header)); + ether_input(ifp, eh, m0); return; } +/****************************************************************************** + * XXX NOT KNF FROM HERE UP + ******************************************************************************/ /* - * Network stop. - * - * Assumes that a ray_init is used to restart the card. + * an update params command has completed lookup which command and + * the status * + * XXX this isn't finished yet, we need to grok the command used */ static void -ray_stop (sc) - struct ray_softc *sc; +ray_update_params_done(struct ray_softc *sc, size_t ccs, u_int stat) { - struct ifnet *ifp; - int s; - - RAY_DPRINTFN(5, ("ray%d: Network stop\n", sc->unit)); - RAY_MAP_CM(sc); - - if (sc->gone) { - printf("ray%d: unloaded before stop!\n", sc->unit); - return; - } - - ifp = &sc->arpcom.ac_if; - - /* - * Clear out timers and sort out driver state - */ - untimeout(ray_download_timo, sc, sc->timerh); - untimeout(ray_reset_timo, sc, sc->timerh); -#if RAY_NEED_STARTJOIN_TIMO - untimeout(ray_start_join_timo, sc, sc->sj_timerh); -#endif /* RAY_NEED_STARTJOIN_TIMO */ - untimeout(ray_start_timo, sc, sc->start_timerh); - sc->sc_havenet = 0; + RAY_DPRINTFN(5, ("ray%d: ray_update_params_done\n", sc->unit)); + RAY_MAP_CM(sc); - /* - * Inhibit card - if we can't prevent reception then do not worry; - * stopping a NIC only guarantees no TX. - */ - s = splimp(); - /* XXX what does the SHUTDOWN command do? Or power saving in COR */ - splx(s); + RAY_DPRINTFN(20, ("ray%d: ray_update_params_done stat %d\n", + sc->unit, stat)); - /* - * Mark as not running - */ - ifp->if_flags &= ~IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; + /* this will get more complex as we add commands */ + if (stat == RAY_CCS_STATUS_FAIL) { + printf("ray%d: failed to update a promisc\n", sc->unit); + /* XXX should probably reset */ + /* rcmd = ray_reset; */ + } - return; + if (sc->sc_running & SCP_UPD_PROMISC) { + ray_cmd_done(sc, SCP_UPD_PROMISC); + sc->sc_promisc = SRAM_READ_1(sc, RAY_HOST_TO_ECF_BASE); + RAY_DPRINTFN(20, ("ray%d: new promisc value %d\n", sc->unit, + sc->sc_promisc)); + } else if (sc->sc_updreq) { + ray_cmd_done(sc, SCP_UPD_UPDATEPARAMS); + /* get the update parameter */ + sc->sc_updreq->r_failcause = + SRAM_READ_FIELD_1(sc, ccs, ray_cmd_update, c_failcause); + sc->sc_updreq = 0; + wakeup(ray_update_params); + ray_start_join_net(sc); + } } /* - * Reset the card - * - * I'm using the soft reset command in the COR register. I'm not sure - * if the sequence is right but it does seem to do the right thing. A - * nano second after reset is written the flashing light goes out, and - * a few seconds after the default is written the main card light goes - * out. We wait a while and then re-init the card. + * check too see if we have any pending commands. */ static void -ray_reset (sc) - struct ray_softc *sc; +ray_check_scheduled(void *arg) { - struct ifnet *ifp; + struct ray_softc *sc; + int s, i, mask; - RAY_DPRINTFN(5, ("ray%d: ray_reset\n", sc->unit)); - RAY_MAP_CM(sc); + s = splnet(); + sc = arg; - ifp = &sc->arpcom.ac_if; + RAY_DPRINTFN(5, ("ray%d: ray_check_scheduled\n", sc->unit)); + RAY_MAP_CM(sc); - if (ifp->if_flags & IFF_RUNNING) - ray_stop(sc); + RAY_DPRINTFN(20, ( + "ray%d: ray_check_scheduled schd 0x%x running 0x%x ready %d\n", + sc->unit, sc->sc_scheduled, sc->sc_running, RAY_ECF_READY(sc))); - printf("ray%d: resetting card\n", sc->unit); - ray_attr_write((sc), RAY_COR, RAY_COR_RESET); - ray_attr_write((sc), RAY_COR, RAY_COR_DEFAULT); - sc->timerh = timeout(ray_reset_timo, sc, RAY_RESET_TIMEOUT); + if (sc->sc_timoneed) { + untimeout(ray_check_scheduled, sc, sc->ccs_timerh); + sc->sc_timoneed = 0; + } - return; + /* if update subcmd is running -- clear it in scheduled */ + if (sc->sc_running & SCP_UPDATESUBCMD) + sc->sc_scheduled &= ~SCP_UPDATESUBCMD; + + mask = SCP_FIRST; + for (i = 0; i < ray_ncmdtab; mask <<= 1, i++) { + if ((sc->sc_scheduled & ~SCP_UPD_MASK) == 0) + break; + if (!RAY_ECF_READY(sc)) + break; + if (sc->sc_scheduled & mask) + (*ray_cmdtab[i])(sc); + } + + RAY_DPRINTFN(20, ( + "ray%d: ray_check_scheduled sched 0x%x running 0x%x ready %d\n", + sc->unit, sc->sc_scheduled, sc->sc_running, RAY_ECF_READY(sc))); + + if (sc->sc_scheduled & ~SCP_UPD_MASK) + ray_set_pending(sc, sc->sc_scheduled); + + splx(s); } /* - * Finishing resetting and restarting the card + * check for unreported returns + * + * this routine is coded to only expect one outstanding request for the + * timed out requests at a time, but thats all that can be outstanding + * per hardware limitations */ static void -ray_reset_timo (xsc) - void *xsc; +ray_check_ccs(void *arg) { - struct ray_softc *sc = xsc; - - RAY_DPRINTFN(5, ("ray%d: ray_reset_timo\n", sc->unit)); - RAY_MAP_CM(sc); + struct ray_softc *sc; + u_int i, cmd, stat; + size_t ccs; + int s; + + s = splnet(); + sc = arg; + + RAY_DPRINTFN(5, ("ray%d: ray_check_ccs\n", sc->unit)); + RAY_MAP_CM(sc); + + ccs = 0; + stat = RAY_CCS_STATUS_FAIL; + sc->sc_timocheck = 0; + for (i = RAY_CCS_CMD_FIRST; i <= RAY_CCS_CMD_LAST; i++) { + if (!sc->sc_ccsinuse[i]) + continue; + ccs = RAY_CCS_ADDRESS(i); + cmd = SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_cmd); + switch (cmd) { + case RAY_CMD_START_PARAMS: + case RAY_CMD_UPDATE_MCAST: + case RAY_CMD_UPDATE_PARAMS: + stat = SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_status); + RAY_DPRINTFN(20, ("ray%d: check ccs idx %d ccs 0x%x " + "cmd 0x%x stat %d\n", sc->unit, i, + ccs, cmd, stat)); + goto breakout; + } + } +breakout: + /* see if we got one of the commands we are looking for */ + if (i > RAY_CCS_CMD_LAST) + ; /* nothign */ + else if (stat == RAY_CCS_STATUS_FREE) { + stat = RAY_CCS_STATUS_COMPLETE; + ray_ccs_done(sc, ccs); + } else if (stat != RAY_CCS_STATUS_BUSY) { + if (sc->sc_ccsinuse[i] == 1) { + /* give a chance for the interrupt to occur */ + sc->sc_ccsinuse[i] = 2; + if (!sc->sc_timocheck) { + sc->ccs_timerh = timeout(ray_check_ccs, sc, 1); + sc->sc_timocheck = 1; + } + } else + ray_ccs_done(sc, ccs); + } else { + sc->ccs_timerh = timeout(ray_check_ccs, sc, RAY_CCS_TIMEOUT); + sc->sc_timocheck = 1; + } + splx(s); +} - if (!RAY_ECF_READY(sc)) { - RAY_DPRINTFN(1, ("ray%d: ray_reset_timo still busy, re-schedule\n", - sc->unit)); - sc->timerh = timeout(ray_reset_timo, sc, RAY_RESET_TIMEOUT); - return; - } +/* + * read the counters, the card implements the following protocol + * to keep the values from being changed while read: It checks + * the `own' bit and if zero writes the current internal counter + * value, it then sets the `own' bit to 1. If the `own' bit was 1 it + * incremenets its internal counter. The user thus reads the counter + * if the `own' bit is one and then sets the own bit to 0. + */ +static void +ray_update_error_counters(struct ray_softc *sc) +{ + size_t csc; - RAY_HCS_CLEAR_INTR(sc); - ray_init(sc); + RAY_DPRINTFN(5, ("ray%d: ray_update_error_counters\n", sc->unit)); + RAY_MAP_CM(sc); - return; + /* try and update the error counters */ + csc = RAY_STATUS_BASE; + if (SRAM_READ_FIELD_1(sc, csc, ray_csc, csc_mrxo_own)) { + sc->sc_rxoverflow += + SRAM_READ_FIELD_2(sc, csc, ray_csc, csc_mrx_overflow); + SRAM_WRITE_FIELD_1(sc, csc, ray_csc, csc_mrxo_own, 0); + } + if (SRAM_READ_FIELD_1(sc, csc, ray_csc, csc_mrxc_own)) { + sc->sc_rxcksum += + SRAM_READ_FIELD_2(sc, csc, ray_csc, csc_mrx_overflow); + SRAM_WRITE_FIELD_1(sc, csc, ray_csc, csc_mrxc_own, 0); + } + if (SRAM_READ_FIELD_1(sc, csc, ray_csc, csc_rxhc_own)) { + sc->sc_rxhcksum += + SRAM_READ_FIELD_2(sc, csc, ray_csc, csc_rx_hcksum); + SRAM_WRITE_FIELD_1(sc, csc, ray_csc, csc_rxhc_own, 0); + } + sc->sc_rxnoise = SRAM_READ_FIELD_1(sc, csc, ray_csc, csc_rx_noise); } +/****************************************************************************** + * XXX NOT KNF FROM HERE DOWN * + ******************************************************************************/ + /* * Process CCS command completion - called from ray_intr */ @@ -1637,25 +2201,33 @@ ray_ccs_done (sc, ccs) struct ray_softc *sc; size_t ccs; { - u_int cmd, status; + u_int cmd, stat; RAY_DPRINTFN(5, ("ray%d: ray_ccs_done\n", sc->unit)); RAY_MAP_CM(sc); cmd = SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_cmd); - status = SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_status); + stat = SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_status); + RAY_DPRINTFN(20, ("ray%d: ccs idx %d ccs 0x%x cmd 0x%x status %d\n", - sc->unit, RAY_CCS_INDEX(ccs), ccs, cmd, status)); + sc->unit, RAY_CCS_INDEX(ccs), ccs, cmd, stat)); switch (cmd) { case RAY_CMD_START_PARAMS: +#if XXX_DOWNLOAD_STD_TIMEOUT + RAY_DPRINTFN(20, "ray%d: ray_ccs_done got START_PARAMS - why?\n", + sc->unit); + ray_cmd_done(sc, SCP_UPD_STARTUP); + ray_download_timo(sc); +#else printf("ray%d: ray_ccs_done got START_PARAMS - why?\n", sc->unit); +#endif /* XXX_DOWNLOAD_STD_TIMEOUT */ break; case RAY_CMD_UPDATE_PARAMS: RAY_DPRINTFN(20, ("ray%d: ray_ccs_done got UPDATE_PARAMS\n", sc->unit)); - XXX; + ray_update_params_done(sc, ccs, stat); break; case RAY_CMD_REPORT_PARAMS: @@ -1670,19 +2242,19 @@ ray_ccs_done (sc, ccs) XXX; break; - case RAY_CMD_UPDATE_APM: - RAY_DPRINTFN(20, ("ray%d: ray_ccs_done got UPDATE_APM\n", - sc->unit)); - XXX; - break; - case RAY_CMD_START_NET: case RAY_CMD_JOIN_NET: RAY_DPRINTFN(20, ("ray%d: ray_ccs_done got START|JOIN_NET\n", sc->unit)); - ray_start_join_done(sc, ccs, status); + ray_start_join_done(sc, ccs, stat); break; + case RAY_CMD_TX_REQ: + RAY_DPRINTFN(20, ("ray%d: ray_ccs_done got TX_REQ\n", + sc->unit)); + ray_start_done(sc, ccs, stat); + goto done; + case RAY_CMD_START_ASSOC: RAY_DPRINTFN(20, ("ray%d: ray_ccs_done got START_ASSOC\n", sc->unit)); @@ -1690,11 +2262,11 @@ ray_ccs_done (sc, ccs) XXX; break; - case RAY_CMD_TX_REQ: - RAY_DPRINTFN(20, ("ray%d: ray_ccs_done got TX_REQ\n", + case RAY_CMD_UPDATE_APM: + RAY_DPRINTFN(20, ("ray%d: ray_ccs_done got UPDATE_APM\n", sc->unit)); - ray_start_done(sc, ccs, status); - return; + XXX; + break; case RAY_CMD_TEST_MEM: printf("ray%d: ray_ccs_done got TEST_MEM - why?\n", sc->unit); @@ -1712,326 +2284,523 @@ ray_ccs_done (sc, ccs) printf("ray%d: ray_ccs_done got START_TIMER - why?\n", sc->unit); break; - default: - printf("ray%d: ray_ccs_done unknown command 0x%x\n", sc->unit, cmd); - break; - } + default: + printf("ray%d: ray_ccs_done unknown command 0x%x\n", sc->unit, cmd); + break; + } + + ray_free_ccs(sc, ccs); +done: + /* + * see if needed things can be done now that a command + * has completed + */ + ray_check_scheduled(sc); + + return; +} + +/* + * Process ECF command request - called from ray_intr + */ +static void +ray_rcs_intr (sc, rcs) + struct ray_softc *sc; + size_t rcs; +{ + struct ifnet *ifp; + u_int cmd, status; + + RAY_DPRINTFN(5, ("ray%d: ray_rcs_intr\n", sc->unit)); + RAY_MAP_CM(sc); + + ifp = &sc->arpcom.ac_if; + + cmd = SRAM_READ_FIELD_1(sc, rcs, ray_cmd, c_cmd); + status = SRAM_READ_FIELD_1(sc, rcs, ray_cmd, c_status); + RAY_DPRINTFN(20, ("ray%d: rcs idx %d rcs 0x%x cmd 0x%x status %d\n", + sc->unit, RAY_CCS_INDEX(rcs), rcs, cmd, status)); + + switch (cmd) { + case RAY_ECMD_RX_DONE: + RAY_DPRINTFN(20, ("ray%d: ray_rcs_intr got RX_DONE\n", sc->unit)); + ray_rx(sc, rcs); + break; + + case RAY_ECMD_REJOIN_DONE: + RAY_DPRINTFN(20, ("ray%d: ray_rcs_intr got REJOIN_DONE\n", + sc->unit)); + sc->sc_havenet = 1; /* Should not be here but in function */ + XXX; + break; + + case RAY_ECMD_ROAM_START: + RAY_DPRINTFN(20, ("ray%d: ray_rcs_intr got ROAM_START\n", + sc->unit)); + sc->sc_havenet = 0; /* Should not be here but in function */ + XXX; + break; + + case RAY_ECMD_JAPAN_CALL_SIGNAL: + printf("ray%d: ray_rcs_intr got JAPAN_CALL_SIGNAL - why?\n", + sc->unit); + break; + + default: + printf("ray%d: ray_rcs_intr unknown command 0x%x\n", sc->unit, cmd); + break; + } + + RAY_CCS_FREE(sc, rcs); + + return; +} +/****************************************************************************** + * XXX NOT KNF FROM HERE UP + ******************************************************************************/ + +/* + * process an interrupt + */ +static int +ray_intr(struct pccard_devinfo *dev_p) +{ + struct ray_softc *sc; + struct ifnet *ifp; + int i, count; + + sc = &ray_softc[dev_p->isahd.id_unit]; + + RAY_DPRINTFN(5, ("ray%d: ray_intr\n", sc->unit)); + RAY_MAP_CM(sc); + + ifp = &sc->arpcom.ac_if; + + if (sc->gone) { + printf("ray%d: unloaded before interrupt!\n", sc->unit); + return (0); + } + + if ((++sc->sc_checkcounters % 32) == 0) + ray_update_error_counters(sc); + + /* + * Check that the interrupt was for us, if so get the rcs/ccs + * and vector on the command contained within it. + */ + if (!RAY_HCS_INTR(sc)) + count = 0; + else { + count = 1; + i = SRAM_READ_1(sc, RAY_SCB_RCSI); + if (i <= RAY_CCS_LAST) + ray_ccs_done(sc, RAY_CCS_ADDRESS(i)); + else if (i <= RAY_RCS_LAST) + ray_rcs_intr(sc, RAY_CCS_ADDRESS(i)); + else + printf("ray%d: ray_intr bad ccs index %d\n", sc->unit, i); + } + + if (count) + RAY_HCS_CLEAR_INTR(sc); + + RAY_DPRINTFN(10, ("ray%d: interrupt %s handled\n", + sc->unit, count?"was":"not")); + + /* Send any packets lying around */ + if (!(ifp->if_flags & IFF_OACTIVE) && (ifp->if_snd.ifq_head != NULL)) + ray_start(ifp); + + return (count); +} + +/* + * Generic CCS handling + */ + +#if XXX_NETBSDTX +/* + * free the chain of descriptors -- used for freeing allocated tx chains + */ +static void +ray_free_ccs_chain(struct ray_softc *sc, u_int ni) +{ + u_int i; + + RAY_DPRINTFN(5, ("ray%d: ray_free_ccs_chain\n", sc->unit)); + RAY_MAP_CM(sc); + + while ((i = ni) != RAY_CCS_LINK_NULL) { + ni = SRAM_READ_FIELD_1(sc, RAY_CCS_ADDRESS(i), ray_cmd, c_link); + RAY_CCS_FREE(sc, RAY_CCS_ADDRESS(i)); + } +} +#endif XXX_NETBSDTX + +/* + * free up a cmd and return the old status. + * this routine is only used for commands. + */ +static u_int8_t +ray_free_ccs(struct ray_softc *sc, size_t ccs) +{ + u_int8_t stat; + + RAY_DPRINTFN(5, ("ray%d: ray_free_ccs\n", sc->unit)); + RAY_MAP_CM(sc); + + stat = SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_status); + RAY_CCS_FREE(sc, ccs); + if (ccs <= RAY_CCS_ADDRESS(RAY_CCS_LAST)) + sc->sc_ccsinuse[RAY_CCS_INDEX(ccs)] = 0; + RAY_DPRINTFN(20, ("ray%d: ray_free_ccs freed 0x%02x\n", + sc->unit, RAY_CCS_INDEX(ccs))); + + return (stat); +} + +/* + * Obtain a free ccs buffer. + * + * returns 1 and in `ccsp' the bus offset of the free ccs + * or 0 if none are free + * + * If `track' is not zero, handles tracking this command + * possibly indicating a callback is needed and setting a timeout + * also if ECF isn't ready we terminate earlier to avoid overhead. + * + * this routine is only used for commands + */ +static int +ray_alloc_ccs(struct ray_softc *sc, size_t *ccsp, u_int cmd, u_int track) +{ + size_t ccs; + u_int i; + + RAY_DPRINTFN(5, ("ray%d: ray_alloc_ccs\n", sc->unit)); + RAY_MAP_CM(sc); - ray_free_ccs(sc, ccs); + /* for tracked commands, if not ready just set pending */ + if (track && !RAY_ECF_READY(sc)) { + ray_cmd_schedule(sc, track); + return (0); + } - return; + for (i = RAY_CCS_CMD_FIRST; i <= RAY_CCS_CMD_LAST; i++) { + /* probe here to make the card go */ + (void)SRAM_READ_FIELD_1(sc, RAY_CCS_ADDRESS(i), ray_cmd, + c_status); + if (!sc->sc_ccsinuse[i]) + break; + } + if (i > RAY_CCS_CMD_LAST) { + if (track) + ray_cmd_schedule(sc, track); + return (0); + } + sc->sc_ccsinuse[i] = 1; + ccs = RAY_CCS_ADDRESS(i); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd, c_status, RAY_CCS_STATUS_BUSY); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd, c_cmd, cmd); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd, c_link, RAY_CCS_LINK_NULL); + + *ccsp = ccs; + return (1); } /* - * Process ECF command request - called from ray_intr + * this function sets the pending bit for the command given in 'need' + * and schedules a timeout if none is scheduled already. Any command + * that uses the `host to ecf' region must be serialized. */ static void -ray_rcs_intr (sc, rcs) - struct ray_softc *sc; - size_t rcs; +ray_set_pending(struct ray_softc *sc, u_int cmdf) { - struct ifnet *ifp; - u_int cmd, status; - - RAY_DPRINTFN(5, ("ray%d: ray_rcs_intr\n", sc->unit)); - RAY_MAP_CM(sc); - - ifp = &sc->arpcom.ac_if; - - cmd = SRAM_READ_FIELD_1(sc, rcs, ray_cmd, c_cmd); - status = SRAM_READ_FIELD_1(sc, rcs, ray_cmd, c_status); - RAY_DPRINTFN(20, ("ray%d: rcs idx %d rcs 0x%x cmd 0x%x status %d\n", - sc->unit, RAY_CCS_INDEX(rcs), rcs, cmd, status)); - - switch (cmd) { - case RAY_ECMD_RX_DONE: - RAY_DPRINTFN(20, ("ray%d: ray_rcs_intr got RX_DONE\n", sc->unit)); - ray_rx(sc, rcs); - break; - - case RAY_ECMD_REJOIN_DONE: - RAY_DPRINTFN(20, ("ray%d: ray_rcs_intr got REJOIN_DONE\n", - sc->unit)); - sc->sc_havenet = 1; /* Should not be here but in function */ - XXX; - break; + RAY_DPRINTFN(5, ("ray%d: ray_set_pending\n", sc->unit)); + RAY_MAP_CM(sc); + RAY_DPRINTFN(20, ("ray%d: ray_set_pending 0x%0x\n", sc->unit, cmdf)); - case RAY_ECMD_ROAM_START: - RAY_DPRINTFN(20, ("ray%d: ray_rcs_intr got ROAM_START\n", + sc->sc_scheduled |= cmdf; + if (!sc->sc_timoneed) { + RAY_DPRINTFN(20, ("ray%d: ray_set_pending new timo\n", sc->unit)); - sc->sc_havenet = 0; /* Should not be here but in function */ - XXX; - break; - - case RAY_ECMD_JAPAN_CALL_SIGNAL: - printf("ray%d: ray_rcs_intr got JAPAN_CALL_SIGNAL - why?\n", - sc->unit); - break; - - default: - printf("ray%d: ray_rcs_intr unknown command 0x%x\n", sc->unit, cmd); - break; - } - - SRAM_WRITE_FIELD_1(sc, rcs, ray_cmd, c_status, RAY_CCS_STATUS_FREE); - - return; + sc->ccs_timerh = timeout(ray_check_scheduled, sc, + RAY_CHECK_SCHED_TIMEOUT); + sc->sc_timoneed = 1; + } } /* - * Receive a packet + * schedule the `cmdf' for completion later */ static void -ray_rx (sc, rcs) - struct ray_softc *sc; - size_t rcs; +ray_cmd_schedule(struct ray_softc *sc, int cmdf) { - struct ieee80211_header *header; - struct ether_header *eh; - struct ifnet *ifp; - struct mbuf *m0; - size_t pktlen, fraglen, readlen, tmplen; - size_t bufp, ebufp; - u_int8_t *dst, *src; - u_int8_t fc; - u_int first, ni, i; - - RAY_DPRINTFN(5, ("ray%d: ray_rx\n", sc->unit)); - RAY_MAP_CM(sc); - - RAY_DPRINTFN(20, ("ray%d: rcs chain - using rcs 0x%x\n", sc->unit, rcs)); - - ifp = &sc->arpcom.ac_if; - m0 = NULL; - readlen = 0; + int track; + + RAY_DPRINTFN(5, ("ray%d: ray_cmd_schedule\n", sc->unit)); + RAY_MAP_CM(sc); + RAY_DPRINTFN(20, ("ray%d: ray_cmd_schedule 0x%x\n", sc->unit, cmdf)); + + track = cmdf; + if ((cmdf & SCP_UPD_MASK) == 0) + ray_set_pending(sc, track); + else if (ray_cmd_is_running(sc, SCP_UPDATESUBCMD)) { + /* don't do timeout mechaniscm if subcmd already going */ + sc->sc_scheduled |= cmdf; + } else + ray_set_pending(sc, cmdf | SCP_UPDATESUBCMD); +} - /* - * Get first part of packet and the length. Do some sanity checks - * and get a mbuf. - */ - first = RAY_CCS_INDEX(rcs); - pktlen = SRAM_READ_FIELD_2(sc, rcs, ray_cmd_rx, c_pktlen); +/* + * check to see if `cmdf' has been scheduled + */ +static int +ray_cmd_is_scheduled(struct ray_softc *sc, int cmdf) +{ + RAY_DPRINTFN(5, ("ray%d: ray_cmd_is_scheduled\n", sc->unit)); + RAY_MAP_CM(sc); + RAY_DPRINTFN(20, ("ray%d: ray_cmd_is_scheduled 0x%x\n", + sc->unit, cmdf)); - if ((pktlen > MCLBYTES) || (pktlen < sizeof(struct ieee80211_header))) { - RAY_DPRINTFN(1, ("ray%d: ray_rx packet is too big or too small\n", - sc->unit)); - ifp->if_ierrors++; - goto skip_read; - } + return ((sc->sc_scheduled & cmdf) ? 1 : 0); +} - MGETHDR(m0, M_DONTWAIT, MT_DATA); - if (m0 == NULL) { - RAY_DPRINTFN(1, ("ray%d: ray_rx MGETHDR failed\n", sc->unit)); - ifp->if_ierrors++; - goto skip_read; - } - if (pktlen > MHLEN) { - MCLGET(m0, M_DONTWAIT); - if ((m0->m_flags & M_EXT) == 0) { - RAY_DPRINTFN(1, ("ray%d: ray_rx MCLGET failed\n", sc->unit)); - ifp->if_ierrors++; - m_freem(m0); - m0 = NULL; - goto skip_read; +/* + * cancel a scheduled command (not a running one though!) + */ +static void +ray_cmd_cancel(struct ray_softc *sc, int cmdf) +{ + RAY_DPRINTFN(5, ("ray%d: ray_cmd_cancel\n", sc->unit)); + RAY_MAP_CM(sc); + RAY_DPRINTFN(20, ("ray%d: ray_cmd_cancel 0x%x\n", sc->unit, cmdf)); + + sc->sc_scheduled &= ~cmdf; + if ((cmdf & SCP_UPD_MASK) && (sc->sc_scheduled & SCP_UPD_MASK) == 0) + sc->sc_scheduled &= ~SCP_UPDATESUBCMD; + + /* if nothing else needed cancel the timer */ + if (sc->sc_scheduled == 0 && sc->sc_timoneed) { + untimeout(ray_check_scheduled, sc, sc->ccs_timerh); + sc->sc_timoneed = 0; } - } - m0->m_pkthdr.rcvif = ifp; - m0->m_pkthdr.len = pktlen; - m0->m_len = pktlen; - dst = mtod(m0, u_int8_t *); +} - /* - * Walk the fragment chain to build the complete packet. - * - * The use of two index variables removes a race with the - * hardware. If one index were used the clearing of the CCS would - * happen before reading the next pointer and the hardware can get in. - * Not my idea but verbatim from the NetBSD driver. - */ - i = ni = first; - while ((i = ni) && (i != RAY_CCS_LINK_NULL)) { - rcs = RAY_CCS_ADDRESS(i); - ni = SRAM_READ_FIELD_1(sc, rcs, ray_cmd_rx, c_nextfrag); - bufp = SRAM_READ_FIELD_2(sc, rcs, ray_cmd_rx, c_bufp); - fraglen = SRAM_READ_FIELD_2(sc, rcs, ray_cmd_rx, c_len); - RAY_DPRINTFN(50, ("ray%d: ray_rx frag index %d len %d bufp 0x%x ni %d\n", - sc->unit, i, fraglen, (int)bufp, ni)); +/* + * called to indicate the 'cmdf' has been issued + */ +static void +ray_cmd_ran(struct ray_softc *sc, int cmdf) +{ + RAY_DPRINTFN(5, ("ray%d: ray_cmd_ran\n", sc->unit)); + RAY_MAP_CM(sc); + RAY_DPRINTFN(20, ("ray%d: ray_cmd_ran 0x%x\n", sc->unit, cmdf)); - if (fraglen + readlen > pktlen) { - RAY_DPRINTFN(1, ("ray%d: ray_rx bad length current 0x%x pktlen 0x%x\n", - sc->unit, fraglen + readlen, pktlen)); - ifp->if_ierrors++; - m_freem(m0); - m0 = NULL; - goto skip_read; - } - if ((i < RAY_RCS_FIRST) || (i > RAY_RCS_LAST)) { - printf("ray%d: ray_rx bad rcs index 0x%x\n", sc->unit, i); - ifp->if_ierrors++; - m_freem(m0); - m0 = NULL; - goto skip_read; - } + if (cmdf & SCP_UPD_MASK) + sc->sc_running |= cmdf | SCP_UPDATESUBCMD; + else + sc->sc_running |= cmdf; - ebufp = bufp + fraglen; - if (ebufp <= RAY_RX_END) - ray_read_region(sc, bufp, dst, fraglen); - else { - ray_read_region(sc, bufp, dst, (tmplen = RAY_RX_END - bufp)); - ray_read_region(sc, RAY_RX_BASE, dst + tmplen, ebufp - RAY_RX_END); + if ((cmdf & SCP_TIMOCHECK_CMD_MASK) && !sc->sc_timocheck) { + sc->ccs_timerh = timeout(ray_check_ccs, sc, RAY_CCS_TIMEOUT); + sc->sc_timocheck = 1; } - dst += fraglen; - readlen += fraglen; - } +} -skip_read: +/* + * check to see if `cmdf' has been issued + */ +static int +ray_cmd_is_running(struct ray_softc *sc, int cmdf) +{ + RAY_DPRINTFN(5, ("ray%d: ray_cmd_is_running\n", sc->unit)); + RAY_MAP_CM(sc); + RAY_DPRINTFN(20, ("ray%d: ray_cmd_is_running 0x%x\n", sc->unit, cmdf)); - /* - * Walk the chain again to free the rcss. - */ - i = ni = first; - while ((i = ni) && (i != RAY_CCS_LINK_NULL)) { - rcs = RAY_CCS_ADDRESS(i); - ni = SRAM_READ_FIELD_1(sc, rcs, ray_cmd_rx, c_nextfrag); - SRAM_WRITE_FIELD_1(sc, rcs, ray_cmd, c_status, RAY_CCS_STATUS_FREE); - } + return ((sc->sc_running & cmdf) ? 1 : 0); +} - if (m0 == NULL) - return; +/* + * the given `cmdf' that was issued has completed + */ +static void +ray_cmd_done(struct ray_softc *sc, int cmdf) +{ + RAY_DPRINTFN(5, ("ray%d: ray_cmd_done\n", sc->unit)); + RAY_MAP_CM(sc); + RAY_DPRINTFN(20, ("ray%d: ray_cmd_done 0x%x\n", sc->unit, cmdf)); + + sc->sc_running &= ~cmdf; + if (cmdf & SCP_UPD_MASK) { + sc->sc_running &= ~SCP_UPDATESUBCMD; + if (sc->sc_scheduled & SCP_UPD_MASK) + ray_cmd_schedule(sc, sc->sc_scheduled & SCP_UPD_MASK); + } + if ((sc->sc_running & SCP_TIMOCHECK_CMD_MASK) == 0 && sc->sc_timocheck){ + untimeout(ray_check_ccs, sc, sc->ccs_timerh); + sc->sc_timocheck = 0; + } +} - RAY_DMBUF_DUMP(sc, m0, "ray_rx"); +/* + * issue the command + * only used for commands not tx + */ +static int +ray_issue_cmd(struct ray_softc *sc, size_t ccs, u_int track) +{ + u_int i; - /* - * Check the 802.11 packet type and obtain the .11 src addresses. - * - * XXX CTL and MGT packets will have separate functions, DATA with here - * - * XXX This needs some work for INFRA mode - */ - header = mtod(m0, struct ieee80211_header *); - fc = header->i_fc[0]; - if ((fc & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { - RAY_DPRINTFN(1, ("ray%d: header not version 0 fc 0x%x\n", sc->unit, fc)); - ifp->if_ierrors++; - m_freem(m0); - return; - } - switch (fc & IEEE80211_FC0_TYPE_MASK) { + RAY_DPRINTFN(5, ("ray%d: ray_cmd_issue\n", sc->unit)); + RAY_MAP_CM(sc); - case IEEE80211_FC0_TYPE_MGT: - printf("ray%d: ray_rx got a MGT packet - why?\n", sc->unit); - ifp->if_ierrors++; - m_freem(m0); - return; + /* + * XXX other drivers did this, but I think + * what we really want to do is just make sure we don't + * get here or that spinning is ok + */ + i = 0; + while (!RAY_ECF_READY(sc)) + if (++i > 50) { + printf("\n"); + (void)ray_free_ccs(sc, ccs); + if (track) + ray_cmd_schedule(sc, track); + return (0); + } else if (i == 1) + printf("ray%d: ray_issue_cmd spinning", sc->unit); + else + printf("."); + + SRAM_WRITE_1(sc, RAY_SCB_CCSI, RAY_CCS_INDEX(ccs)); + RAY_ECF_START_CMD(sc); + ray_cmd_ran(sc, track); + + return (1); +} - case IEEE80211_FC0_TYPE_CTL: - printf("ray%d: ray_rx got a CTL packet - why?\n", sc->unit); - ifp->if_ierrors++; - m_freem(m0); - return; +/* + * send a simple command if we can + */ +static int +ray_simple_cmd(struct ray_softc *sc, u_int cmd, u_int track) +{ + size_t ccs; - case IEEE80211_FC0_TYPE_DATA: - RAY_DPRINTFN(50, ("ray%d: ray_rx got a DATA packet\n", sc->unit)); - break; + RAY_DPRINTFN(5, ("ray%d: ray_simple_cmd\n", sc->unit)); + RAY_MAP_CM(sc); - default: - printf("ray%d: ray_rx got a unknown packet fc0 0x%x - why?\n", - sc->unit, fc); - ifp->if_ierrors++; - m_freem(m0); - return; + return (ray_alloc_ccs(sc, &ccs, cmd, track) && + ray_issue_cmd(sc, ccs, track)); +} - } - fc = header->i_fc[1]; - switch (fc & IEEE80211_FC1_DS_MASK) { +/* + * Functions based on CCS commands + */ - case IEEE80211_FC1_STA_TO_STA: - src = header->i_addr2; - RAY_DPRINTFN(50, ("ray%d: ray_rx packet from sta %6D\n", - sc->unit, src, ":")); - break; +/* + * run a update subcommand + */ +static void +ray_update_subcmd(struct ray_softc *sc) +{ + struct ifnet *ifp; + int submask, i; + + RAY_DPRINTFN(5, ("ray%d: ray_update_subcmd\n", sc->unit)); + RAY_MAP_CM(sc); + + ray_cmd_cancel(sc, SCP_UPDATESUBCMD); + + ifp = &sc->arpcom.ac_if; + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + submask = SCP_UPD_FIRST; + for (i = 0; i < ray_nsubcmdtab; submask <<= 1, i++) { + if ((sc->sc_scheduled & SCP_UPD_MASK) == 0) + break; + /* when done the next command will be scheduled */ + if (ray_cmd_is_running(sc, SCP_UPDATESUBCMD)) + break; + if (!RAY_ECF_READY(sc)) + break; + /* + * give priority to LSB -- e.g., if previous loop reschuled + * doing this command after calling the function won't catch + * if a later command sets an earlier bit + */ + if (sc->sc_scheduled & ((submask - 1) & SCP_UPD_MASK)) + break; + if (sc->sc_scheduled & submask) + (*ray_subcmdtab[i])(sc); + } +} - case IEEE80211_FC1_STA_TO_AP: - RAY_DPRINTFN(1, ("ray%d: ray_rx packet from sta %6D to ap %6D\n", - sc->unit, - header->i_addr2, ":", header->i_addr3, ":")); - ifp->if_ierrors++; - m_freem(m0); - break; +/* + * report a parameter + */ +static void +ray_report_params(struct ray_softc *sc) +{ + struct ifnet *ifp; + size_t ccs; - case IEEE80211_FC1_AP_TO_STA: - RAY_DPRINTFN(1, ("ray%d: ray_rx packet from ap %6D\n", - sc->unit, - header->i_addr3, ":")); - ifp->if_ierrors++; - m_freem(m0); - break; + RAY_DPRINTFN(5, ("ray%d: ray_report_params\n", sc->unit)); + RAY_MAP_CM(sc); - case IEEE80211_FC1_AP_TO_AP: - RAY_DPRINTFN(1, ("ray%d: ray_rx saw packet between aps %6D %6D\n", - sc->unit, - header->i_addr1, ":", header->i_addr2, ":")); - ifp->if_ierrors++; - m_freem(m0); - return; + ray_cmd_cancel(sc, SCP_REPORTPARAMS); - default: - printf("ray%d: ray_rx packet type unknown fc1 0x%x - why?\n", - sc->unit, fc); - ifp->if_ierrors++; - m_freem(m0); - return; - } + ifp = &sc->arpcom.ac_if; - /* - * Translation - capability as described earlier - * - * Each case must remove the 802.11 header and leave an 802.3 - * header in the mbuf copy addresses as needed. - */ - switch (sc->translation) { + if (!sc->sc_repreq) + return; - case SC_TRANSLATE_WEBGEAR: - /* Nice and easy - just trim the 802.11 header */ - m_adj(m0, sizeof(struct ieee80211_header)); - break; + /* do the issue check before equality check */ + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + else if (ray_cmd_is_running(sc, SCP_REPORTPARAMS)) { + ray_cmd_schedule(sc, SCP_REPORTPARAMS); + return; + } else if (!ray_alloc_ccs(sc, &ccs, RAY_CMD_REPORT_PARAMS, + SCP_REPORTPARAMS)) + return; - default: - printf("ray%d: ray_rx unknown translation type 0x%x - why?\n", - sc->unit, sc->translation); - ifp->if_ierrors++; - m_freem(m0); - return; + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_report, c_paramid, + sc->sc_repreq->r_paramid); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_report, c_nparam, 1); + (void)ray_issue_cmd(sc, ccs, SCP_REPORTPARAMS); +} - } +/* + * start an association + */ +static void +ray_start_assoc(struct ray_softc *sc) +{ + struct ifnet *ifp; - /* - * Finally, do a bit of house keeping before sending the packet - * up the stack. - */ - ifp->if_ipackets++; -#if NBPFILTER > 0 - if (ifp->if_bpf) - bpf_mtap(ifp, m0); -#endif /* NBPFILTER */ -#if XXX -if_wi.c - might be needed if we hear our own broadcasts in promiscuous mode -but will not be if we dont see them - if ((ifp->if_flags & IFF_PROMISC) && - (bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN) && - (eh->ether_dhost[0] & 1) == 0) - ) { - m_freem(m0); - return; - } -#endif /* XXX */ - eh = mtod(m0, struct ether_header *); - m_adj(m0, sizeof(struct ether_header)); - ether_input(ifp, eh, m0); + RAY_DPRINTFN(5, ("ray%d: ray_start_assoc\n", sc->unit)); + RAY_MAP_CM(sc); - return; + ifp = &sc->arpcom.ac_if; + + ray_cmd_cancel(sc, SCP_STARTASSOC); + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + else if (ray_cmd_is_running(sc, SCP_STARTASSOC)) + return; + (void)ray_simple_cmd(sc, RAY_CMD_START_ASSOC, SCP_STARTASSOC); } +/****************************************************************************** + * XXX NOT KNF FROM HERE DOWN * + ******************************************************************************/ +/* + * Subcommand functions that use the SCP_UPDATESUBCMD command + * (and are serialized with respect to other update sub commands + */ + /* * Download start up structures to card. * @@ -2047,9 +2816,11 @@ ray_download_params (sc) RAY_DPRINTFN(5, ("ray%d: Downloading startup parameters\n", sc->unit)); RAY_MAP_CM(sc); -#if XXX_TRACKING +#if XXX_DOWNLOAD_STD_TIMEOUT ray_cmd_cancel(sc, SCP_UPD_STARTUP); -#endif /* XXX_TRACKING */ +#else + /* XXX cancel timeouts ? */ +#endif /* XXX_DOWNLOAD_STD_TIMEOUT */ #define MIB4(m) ray_mib_4_default.##m #define MIB5(m) ray_mib_5_default.##m @@ -2060,7 +2831,7 @@ ray_download_params (sc) * Firmware version 4 defaults - see if_raymib.h for details */ MIB4(mib_net_type) = sc->sc_net_type; - MIB4(mib_ap_status) = RAY_MIB_AP_STATUS_DEFAULT; + MIB4(mib_ap_status) = sc->sc_ap_status; strncpy(MIB4(mib_ssid), sc->sc_ssid, RAY_MAXSSIDLEN); MIB4(mib_scan_mode) = RAY_MIB_SCAN_MODE_DEFAULT; MIB4(mib_apm_mode) = RAY_MIB_APM_MODE_DEFAULT; @@ -2106,7 +2877,7 @@ PUT2(MIB4(mib_uniq_word), RAY_MIB_UNIQ_WORD_DEFAULT); * Firmware version 5 defaults - see if_raymib.h for details */ MIB5(mib_net_type) = sc->sc_net_type; - MIB5(mib_ap_status) = RAY_MIB_AP_STATUS_DEFAULT; + MIB4(mib_ap_status) = sc->sc_ap_status; strncpy(MIB5(mib_ssid), sc->sc_ssid, RAY_MAXSSIDLEN); MIB5(mib_scan_mode) = RAY_MIB_SCAN_MODE_DEFAULT; MIB5(mib_apm_mode) = RAY_MIB_APM_MODE_DEFAULT; @@ -2171,18 +2942,26 @@ PUT2(MIB5(mib_cw_min), RAY_MIB_CW_MIN_V5); * command just gets serviced, so we use a timeout to complete the * sequence. */ - if (!ray_alloc_ccs(sc, &sc->sc_ccs, +#if XXX_DOWNLOAD_STD_TIMEOUT + if (!ray_simple_cmd(sc, RAY_CMD_START_PARAMS, SCP_UPD_STARTUP)) + printf("ray%d: ray_download_params can't issue command\n", sc->unit); +#else +/* XXX do we go back to using the std. timeout code? */ +/* XXX use ray_simple_cmd */ + if (!ray_alloc_ccs(sc, &sc->sc_startccs, RAY_CMD_START_PARAMS, SCP_UPD_STARTUP)) { printf("ray%d: ray_download_params can't get a CCS\n", sc->unit); ray_reset(sc); } - - if (!ray_issue_cmd(sc, sc->sc_ccs, SCP_UPD_STARTUP)) { + if (!ray_issue_cmd(sc, sc->sc_startccs, SCP_UPD_STARTUP)) { printf("ray%d: ray_download_params can't issue command\n", sc->unit); ray_reset(sc); } +/* XXX use ray_simple_cmd */ +/* XXX do we go back to using the std. timeout code? */ sc->timerh = timeout(ray_download_timo, sc, RAY_DOWNLOAD_TIMEOUT); +#endif /* XXX_DOWNLOAD_STD_TIMEOUT */ RAY_DPRINTFN(15, ("ray%d: Download now awaiting timeout\n", sc->unit)); return; @@ -2204,10 +2983,11 @@ ray_download_timo (xsc) RAY_DPRINTFN(5, ("ray%d: ray_download_timo\n", sc->unit)); RAY_MAP_CM(sc); - status = SRAM_READ_FIELD_1(sc, sc->sc_ccs, ray_cmd, c_status); - cmd = SRAM_READ_FIELD_1(sc, sc->sc_ccs, ray_cmd, c_cmd); + status = SRAM_READ_FIELD_1(sc, sc->sc_startccs, ray_cmd, c_status); + cmd = SRAM_READ_FIELD_1(sc, sc->sc_startccs, ray_cmd, c_cmd); RAY_DPRINTFN(20, ("ray%d: check rayidx %d ccs 0x%x cmd 0x%x status %d\n", - sc->unit, RAY_CCS_INDEX(sc->sc_ccs), sc->sc_ccs, cmd, status)); + sc->unit, RAY_CCS_INDEX(sc->sc_startccs), sc->sc_startccs, + cmd, status)); if ((cmd != RAY_CMD_START_PARAMS) || ((status != RAY_CCS_STATUS_FREE) && (status != RAY_CCS_STATUS_BUSY)) ) { @@ -2227,8 +3007,8 @@ ray_download_timo (xsc) } /* Clear the ccs */ - (void)ray_free_ccs(sc, sc->sc_ccs); - sc->sc_ccs = RAY_CCS_LAST + 1; + (void)ray_free_ccs(sc, sc->sc_startccs); + sc->sc_startccs = RAY_CCS_LAST + 1; /* * Grab a ccs and don't bother updating the network parameters. @@ -2263,6 +3043,83 @@ ray_download_timo (xsc) } /* + * start or join a network + */ +static void +ray_start_join_net(sc) + struct ray_softc *sc; +{ +#if XXX_NETBSD_SJ_NET + struct ray_net_params np; + struct ifnet *ifp; + size_t ccs; + int cmd; +#endif /* XXX_NETBSD_SJ_NET */ + + RAY_DPRINTFN(5, ("ray%d: ray_start_join_net\n", sc->unit)); + RAY_MAP_CM(sc); +#if XXX_NETBSD_SJ_NET + + ifp = &sc->arpcom.ac_if; + + ray_cmd_cancel(sc, SCP_UPD_STARTJOIN); + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + /* XXX check we may not want to re-issue */ + if (ray_cmd_is_running(sc, SCP_UPDATESUBCMD)) { + ray_cmd_schedule(sc, SCP_UPD_STARTJOIN); + return; + } + + if (sc->sc_mode == SC_MODE_ADHOC) + cmd = RAY_CMD_START_NET; + else + cmd = RAY_CMD_JOIN_NET; + + if (!ray_alloc_ccs(sc, &ccs, cmd, SCP_UPD_STARTJOIN)) + return; + sc->sc_startccs = ccs; + sc->sc_startcmd = cmd; + if (!memcmp(sc->sc_cnwid, sc->sc_dnwid, sizeof(sc->sc_cnwid)) + && sc->sc_omode == sc->sc_mode) + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_net, c_upd_param, 0); + else { + sc->sc_havenet = 0; + memset(&np, 0, sizeof(np)); + np.p_net_type = sc->sc_mode; + memcpy(np.p_ssid, sc->sc_dnwid, sizeof(np.p_ssid)); + ray_write_region(sc, RAY_HOST_TO_ECF_BASE, &np, sizeof(np)); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_net, c_upd_param, 1); + } + if (ray_issue_cmd(sc, ccs, SCP_UPD_STARTJOIN)) + timeout(ray_start_join_timo, sc, RAY_START_TIMEOUT); +#endif /* XXX_NETBSD_SJ_NET */ +} + +#if RAY_NEED_STARTJOIN_TIMO +/* + * Back stop catcher for start_join command. The NetBSD driver + * suggests that they need it to catch a bug in the firmware or the + * parameters they use - they are not sure. I'll just panic as I seem + * to get interrupts back fine and I have version 4 firmware. + */ +static void +ray_start_join_timo (xsc) + void *xsc; +{ + struct ray_softc *sc = xsc; + + RAY_DPRINTFN(5, ("ray%d: ray_start_join_timo\n", sc->unit)); + RAY_MAP_CM(sc); + + panic("ray%d: ray-start_join_timo occured\n", sc->unit); + + return; +} +#endif /* RAY_NEED_STARTJOIN_TIMO */ + +/* * Complete start or join command. * * Part of ray_init, download, start_join control flow. @@ -2285,9 +3142,11 @@ ray_start_join_done (sc, ccs, status) untimeout(ray_start_join_timo, sc, sc->sj_timerh); #endif /* RAY_NEED_STARTJOIN_TIMO */ -#if XXX_TRACKING +#if XXX_DOWNLOAD_STD_TIMEOUT ray_cmd_done(sc, SCP_UPD_STARTJOIN); -#endif /* XXX_TRACKING */ +#else + /* XXX cancel timeouts ? */ +#endif /* XXX_DOWNLOAD_STD_TIMEOUT */ /* * XXX This switch and the following test are badly done. I @@ -2336,7 +3195,7 @@ ray_start_join_done (sc, ccs, status) /* adjust values for buggy build 4 */ if (sc->sc_def_txrate == 0x55) - sc->sc_def_txrate = RAY_MIB_BASIC_RATE_SET_1500K; + sc->sc_def_txrate = RAY_MIB_BASIC_RATE_SET_2000K; if (sc->sc_encrypt == 0x55) sc->sc_encrypt = 0; @@ -2365,15 +3224,12 @@ ray_start_join_done (sc, ccs, status) * Just before we return from the interrupt context we check to * see if packets have been queued. */ -#if XXX_MCASTPROM + ray_cmd_schedule(sc, SCP_UPD_PROMISC); +#if XXX_MCAST ray_cmd_schedule(sc, SCP_UPD_MCAST|SCP_UPD_PROMISC); -#endif /* XXX_MCASTPROM */ +#endif /* XXX_MCAST */ if (SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_cmd) == RAY_CMD_JOIN_NET) -#if XXX_INFRA ray_start_assoc(sc); -#else - RAY_DPRINTFN(1, ("wanted to join a NET!\n")); -#endif /* XXX_INFRA */ else { sc->sc_havenet = 1; ifp->if_flags &= ~IFF_OACTIVE; @@ -2382,182 +3238,246 @@ ray_start_join_done (sc, ccs, status) return; } -#if RAY_NEED_STARTJOIN_TIMO +/****************************************************************************** + * XXX NOT KNF FROM HERE UP + ******************************************************************************/ + /* - * Back stop catcher for start_join command. The NetBSD driver - * suggests that they need it to catch a bug in the firmware or the - * parameters they use - they are not sure. I'll just panic as I seem - * to get interrupts back fine and I have version 4 firmware. + * set the card in/out of promiscuous mode */ static void -ray_start_join_timo (xsc) - void *xsc; +ray_update_promisc(struct ray_softc *sc) { - struct ray_softc *sc = xsc; - - RAY_DPRINTFN(5, ("ray%d: ray_start_join_timo\n", sc->unit)); - RAY_MAP_CM(sc); - - panic("ray%d: ray-start_join_timo occured\n", sc->unit); - - return; + struct ifnet *ifp; + size_t ccs; + int promisc; + + RAY_DPRINTFN(5, ("ray%d: ray_update_promisc\n", sc->unit)); + RAY_MAP_CM(sc); + + ifp = &sc->arpcom.ac_if; + ray_cmd_cancel(sc, SCP_UPD_PROMISC); + + /* do the issue check before equality check */ + promisc = !!(ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)); + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + else if (ray_cmd_is_running(sc, SCP_UPDATESUBCMD)) { + ray_cmd_schedule(sc, SCP_UPD_PROMISC); + return; + } else if (promisc == sc->sc_promisc) + return; + else if (!ray_alloc_ccs(sc,&ccs,RAY_CMD_UPDATE_PARAMS, SCP_UPD_PROMISC)) + return; + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_update, c_paramid, RAY_MIB_PROMISC); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_update, c_nparam, 1); + SRAM_WRITE_1(sc, RAY_HOST_TO_ECF_BASE, promisc); + (void)ray_issue_cmd(sc, ccs, SCP_UPD_PROMISC); } -#endif /* RAY_NEED_STARTJOIN_TIMO */ /* - * Obtain a free ccs buffer. - * - * Returns 1 and in `ccsp' the bus offset of the free ccs - * or 0 if none are free - * - * If `track' is not zero, handles tracking this command - * possibly indicating a callback is needed and setting a timeout - * also if ECF isn't ready we terminate earlier to avoid overhead. - * - * This routine is only used for commands + * update the parameter based on what the user passed in */ -static int -ray_alloc_ccs (sc, ccsp, cmd, track) - struct ray_softc *sc; - size_t *ccsp; - u_int cmd, track; +static void +ray_update_params(struct ray_softc *sc) { - size_t ccs; - u_int i; + struct ifnet *ifp; + size_t ccs; - RAY_DPRINTFN(5, ("ray%d: ray_alloc_ccs\n", sc->unit)); - RAY_MAP_CM(sc); + RAY_DPRINTFN(5, ("ray%d: ray_update_params\n", sc->unit)); + RAY_MAP_CM(sc); -#if XXX_TRACKING - /* for tracked commands, if not ready just set pending */ - if (track && !RAY_ECF_READY(sc)) { - ray_cmd_schedule(sc, track); - return(0); - } -#endif /* XXX_TRACKING */ + ifp = &sc->arpcom.ac_if; - for (i = RAY_CCS_CMD_FIRST; i <= RAY_CCS_CMD_LAST; i++) { - /* probe here to make the card go */ - (void)SRAM_READ_FIELD_1(sc, RAY_CCS_ADDRESS(i), ray_cmd, c_status); - if (!sc->sc_ccsinuse[i]) - break; - } - if (i > RAY_CCS_CMD_LAST) { -#if XXX_TRACKING - if (track) - ray_cmd_schedule(sc, track); -#endif /* XXX_TRACKING */ - return(0); - } - sc->sc_ccsinuse[i] = 1; - ccs = RAY_CCS_ADDRESS(i); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd, c_status, RAY_CCS_STATUS_BUSY); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd, c_cmd, cmd); - SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd, c_link, RAY_CCS_LINK_NULL); + ray_cmd_cancel(sc, SCP_UPD_UPDATEPARAMS); + if (!sc->sc_updreq) { + /* XXX do we need to wakeup here? */ + return; + } - *ccsp = ccs; - return(1); + /* do the issue check before equality check */ + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + else if (ray_cmd_is_running(sc, SCP_UPDATESUBCMD)) { + ray_cmd_schedule(sc, SCP_UPD_UPDATEPARAMS); + return; + } else if (!ray_alloc_ccs(sc, &ccs, RAY_CMD_UPDATE_PARAMS, + SCP_UPD_UPDATEPARAMS)) + return; + + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_update, c_paramid, + sc->sc_updreq->r_paramid); + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_update, c_nparam, 1); + ray_write_region(sc, RAY_HOST_TO_ECF_BASE, sc->sc_updreq->r_data, + sc->sc_updreq->r_len); + + (void)ray_issue_cmd(sc, ccs, SCP_UPD_UPDATEPARAMS); } /* - * Free up a ccs/cmd and return the old status. - * This routine is only used for commands. + * set the multicast filter list */ -static u_int8_t -ray_free_ccs (sc, ccs) - struct ray_softc *sc; - size_t ccs; +static void +ray_update_mcast(struct ray_softc *sc) { - u_int8_t status; +#if XXX_MCAST + size_t ccs; + struct ether_multistep step; + struct ether_multi *enm; + struct ethercom *ec; + size_t bufp; + int count; +#endif - RAY_DPRINTFN(5, ("ray%d: ray_free_ccs\n", sc->unit)); - RAY_MAP_CM(sc); + RAY_DPRINTFN(5, ("ray%d: ray_update_mcast\n", sc->unit)); + RAY_MAP_CM(sc); +#if XXX_MCAST + + ec = &sc->sc_ec; + ray_cmd_cancel(sc, SCP_UPD_MCAST); + + /* see if we have any ranges */ + if ((count = sc->sc_ec.ec_multicnt) < 17) { + ETHER_FIRST_MULTI(step, ec, enm); + while (enm) { + /* see if this is a range */ + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, + ETHER_ADDR_LEN)) { + count = 17; + break; + } + ETHER_NEXT_MULTI(step, enm); + } + } - status = SRAM_READ_FIELD_1(sc, ccs, ray_cmd, c_status); - RAY_CCS_FREE(sc, ccs); - if (ccs <= RAY_CCS_ADDRESS(RAY_CCS_LAST)) - sc->sc_ccsinuse[RAY_CCS_INDEX(ccs)] = 0; - RAY_DPRINTFN(20, ("ray%d: ray_free_ccs freed 0x%02x\n", - sc->unit, RAY_CCS_INDEX(ccs))); + /* track this stuff even when not running */ + if (count > 16) { + sc->sc_if.if_flags |= IFF_ALLMULTI; + ray_update_promisc(sc); + return; + } else if (sc->sc_if.if_flags & IFF_ALLMULTI) { + sc->sc_if.if_flags &= ~IFF_ALLMULTI; + ray_update_promisc(sc); + } - return(status); + if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) + return; + else if (ray_cmd_is_running(sc, SCP_UPDATESUBCMD)) { + ray_cmd_schedule(sc, SCP_UPD_MCAST); + return; + } else if (!ray_alloc_ccs(sc,&ccs, RAY_CMD_UPDATE_MCAST, SCP_UPD_MCAST)) + return; + SRAM_WRITE_FIELD_1(sc, ccs, ray_cmd_update_mcast, c_nmcast, count); + bufp = RAY_HOST_TO_ECF_BASE; + ETHER_FIRST_MULTI(step, ec, enm); + while (enm) { + ray_write_region(sc, bufp, enm->enm_addrlo, ETHER_ADDR_LEN); + bufp += ETHER_ADDR_LEN; + ETHER_NEXT_MULTI(step, enm); + } + (void)ray_issue_cmd(sc, ccs, SCP_UPD_MCAST); +#endif XXX_MCAST } /* - * Issue a command by writing the mailbox and tickling the card. - * Only used for commands not transmitted packets. + * User issued commands + */ + +/* + * issue a update params + * + * expected to be called in sleapable context -- intended for user stuff */ static int -ray_issue_cmd(sc, ccs, track) - struct ray_softc *sc; - size_t ccs; - u_int track; +ray_user_update_params(struct ray_softc *sc, struct ray_param_req *pr) { - u_int i; + struct ifnet *ifp; + int rv; - RAY_DPRINTFN(5, ("ray%d: ray_cmd_issue\n", sc->unit)); - RAY_MAP_CM(sc); + RAY_DPRINTFN(5, ("ray%d: ray_user_update_params\n", sc->unit)); + RAY_MAP_CM(sc); - /* - * XXX other drivers did this, but I think - * what we really want to do is just make sure we don't - * get here or that spinning is ok - */ - i = 0; - while (!RAY_ECF_READY(sc)) - if (++i > 50) { - printf("\n"); - (void)ray_free_ccs(sc, ccs); -#if XXX_TRACKING - if (track) - ray_cmd_schedule(sc, track); -#endif /* XXX_TRACKING */ - return(0); - } else if (i == 1) - printf("ray%d: ray_issue_cmd spinning", sc->unit); - else - printf("."); + ifp = &sc->arpcom.ac_if; - SRAM_WRITE_1(sc, RAY_SCB_CCSI, RAY_CCS_INDEX(ccs)); - RAY_ECF_START_CMD(sc); -#if XXX_TRACKING - ray_cmd_ran(sc, track); -#endif /* XXX_TRACKING */ + if ((ifp->if_flags & IFF_RUNNING) == 0) { + pr->r_failcause = RAY_FAILCAUSE_EDEVSTOP; + return (EIO); + } + + /* wait to be able to issue the command */ + rv = 0; + while (ray_cmd_is_running(sc, SCP_UPD_UPDATEPARAMS) || + ray_cmd_is_scheduled(sc, SCP_UPD_UPDATEPARAMS)) { + rv = tsleep(ray_update_params, 0|PCATCH, "cmd in use", 0); + if (rv) + return (rv); + if ((ifp->if_flags & IFF_RUNNING) == 0) { + pr->r_failcause = RAY_FAILCAUSE_EDEVSTOP; + return (EIO); + } + } + + pr->r_failcause = RAY_FAILCAUSE_WAITING; + sc->sc_updreq = pr; + ray_cmd_schedule(sc, SCP_UPD_UPDATEPARAMS); + ray_check_scheduled(sc); - return(1); + while (pr->r_failcause == RAY_FAILCAUSE_WAITING) + (void)tsleep(ray_update_params, 0, "waiting cmd", 0); + wakeup(ray_update_params); + + return (0); } -#if RAY_DEBUG > 50 -static void -ray_dump_mbuf(sc, m, s) - struct ray_softc *sc; - struct mbuf *m; - char *s; +/* + * issue a report params + * + * expected to be called in sleapable context -- intended for user stuff + */ +static int +ray_user_report_params(struct ray_softc *sc, struct ray_param_req *pr) { - u_int8_t *d, *ed; - u_int i; - char p[17]; + struct ifnet *ifp; + int rv; - printf("ray%d: %s mbuf dump:", sc->unit, s); - i = 0; - bzero(p, 17); - for (; m; m = m->m_next) { - d = mtod(m, u_int8_t *); - ed = d + m->m_len; + RAY_DPRINTFN(5, ("ray%d: ray_user_report_params\n", sc->unit)); + RAY_MAP_CM(sc); - for (; d < ed; i++, d++) { - if ((i % 16) == 0) { - printf(" %s\n\t", p); - } else if ((i % 8) == 0) - printf(" "); - printf(" %02x", *d); - p[i % 16] = ((*d >= 0x20) && (*d < 0x80)) ? *d : '.'; + ifp = &sc->arpcom.ac_if; + + if ((ifp->if_flags & IFF_RUNNING) == 0) { + pr->r_failcause = RAY_FAILCAUSE_EDEVSTOP; + return (EIO); } - } - if ((i - 1) % 16) - printf("%s\n", p); + + /* wait to be able to issue the command */ + rv = 0; + while (ray_cmd_is_running(sc, SCP_REPORTPARAMS) + || ray_cmd_is_scheduled(sc, SCP_REPORTPARAMS)) { + rv = tsleep(ray_report_params, 0|PCATCH, "cmd in use", 0); + if (rv) + return (rv); + if ((ifp->if_flags & IFF_RUNNING) == 0) { + pr->r_failcause = RAY_FAILCAUSE_EDEVSTOP; + return (EIO); + } + } + + pr->r_failcause = RAY_FAILCAUSE_WAITING; + sc->sc_repreq = pr; + ray_cmd_schedule(sc, SCP_REPORTPARAMS); + ray_check_scheduled(sc); + + while (pr->r_failcause == RAY_FAILCAUSE_WAITING) + (void)tsleep(ray_report_params, 0, "waiting cmd", 0); + wakeup(ray_report_params); + + return (0); } -#endif /* RAY_DEBUG > 50 */ + +/****************************************************************************** + * XXX NOT KNF FROM HERE DOWN + ******************************************************************************/ /* * Routines to read from/write to the attribute memory. @@ -2646,7 +3566,7 @@ ray_attr_write (struct ray_softc *sc, off_t offset, u_int8_t byte) ray_attr_cm(sc); #endif /* RAY_NEED_CM_REMAPPING */ - return(err); + return (err); } static int @@ -2673,7 +3593,7 @@ ray_attr_read (struct ray_softc *sc, off_t offset, u_int8_t *buf, int size) ray_attr_cm(sc); #endif /* RAY_NEED_CM_REMAPPING */ - return(err); + return (err); } static u_int8_t @@ -2685,7 +3605,40 @@ ray_read_reg (sc, reg) ray_attr_read(sc, reg, &byte, 1); - return(byte); + return (byte); +} + +#if RAY_DEBUG > 50 +static void +ray_dump_mbuf(sc, m, s) + struct ray_softc *sc; + struct mbuf *m; + char *s; +{ + u_int8_t *d, *ed; + u_int i; + char p[17]; + + printf("ray%d: %s mbuf dump:", sc->unit, s); + i = 0; + bzero(p, 17); + for (; m; m = m->m_next) { + d = mtod(m, u_int8_t *); + ed = d + m->m_len; + + for (; d < ed; i++, d++) { + if ((i % 16) == 0) { + printf(" %s\n\t", p); + } else if ((i % 8) == 0) + printf(" "); + printf(" %02x", *d); + p[i % 16] = ((*d >= 0x20) && (*d < 0x80)) ? *d : '.'; + } + } + if ((i - 1) % 16) + printf("%s\n", p); } +#endif /* RAY_DEBUG > 50 */ + #endif /* NRAY */ \ No newline at end of file -- cgit v1.1