1*cbab9cadSchs /* $NetBSD: if_an_pcmcia.c,v 1.41 2012/10/27 17:18:36 chs Exp $ */
29c25e920Sonoe
39c25e920Sonoe /*-
4adbea351Smycroft * Copyright (c) 2000, 2004 The NetBSD Foundation, Inc.
59c25e920Sonoe * All rights reserved.
69c25e920Sonoe *
79c25e920Sonoe * This code is derived from software contributed to The NetBSD Foundation
89c25e920Sonoe * by Atsushi Onoe
99c25e920Sonoe *
109c25e920Sonoe * Redistribution and use in source and binary forms, with or without
119c25e920Sonoe * modification, are permitted provided that the following conditions
129c25e920Sonoe * are met:
139c25e920Sonoe * 1. Redistributions of source code must retain the above copyright
149c25e920Sonoe * notice, this list of conditions and the following disclaimer.
159c25e920Sonoe * 2. Redistributions in binary form must reproduce the above copyright
169c25e920Sonoe * notice, this list of conditions and the following disclaimer in the
179c25e920Sonoe * documentation and/or other materials provided with the distribution.
189c25e920Sonoe *
199c25e920Sonoe * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
209c25e920Sonoe * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219c25e920Sonoe * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
229c25e920Sonoe * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
239c25e920Sonoe * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
249c25e920Sonoe * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
259c25e920Sonoe * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
269c25e920Sonoe * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279c25e920Sonoe * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
289c25e920Sonoe * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
299c25e920Sonoe * POSSIBILITY OF SUCH DAMAGE.
309c25e920Sonoe */
319c25e920Sonoe
32ab5d9d2bSlukem #include <sys/cdefs.h>
33*cbab9cadSchs __KERNEL_RCSID(0, "$NetBSD: if_an_pcmcia.c,v 1.41 2012/10/27 17:18:36 chs Exp $");
34ab5d9d2bSlukem
359c25e920Sonoe #include <sys/param.h>
369c25e920Sonoe #include <sys/systm.h>
379c25e920Sonoe #include <sys/callout.h>
389c25e920Sonoe #include <sys/mbuf.h>
399c25e920Sonoe #include <sys/socket.h>
409c25e920Sonoe #include <sys/ioctl.h>
419c25e920Sonoe #include <sys/errno.h>
429c25e920Sonoe #include <sys/syslog.h>
439c25e920Sonoe #include <sys/select.h>
449c25e920Sonoe #include <sys/device.h>
459c25e920Sonoe
469c25e920Sonoe #include <net/if.h>
479c25e920Sonoe #include <net/if_dl.h>
489c25e920Sonoe #include <net/if_ether.h>
499c25e920Sonoe #include <net/if_media.h>
502459b026Sdyoung
5190634029Sdyoung #include <net80211/ieee80211_netbsd.h>
522459b026Sdyoung #include <net80211/ieee80211_var.h>
539c25e920Sonoe
54a2a38285Sad #include <sys/cpu.h>
55a2a38285Sad #include <sys/bus.h>
56a2a38285Sad #include <sys/intr.h>
579c25e920Sonoe
589c25e920Sonoe #include <dev/ic/anreg.h>
5999675e3dSonoe #include <dev/ic/anvar.h>
609c25e920Sonoe
619c25e920Sonoe #include <dev/pcmcia/pcmciareg.h>
629c25e920Sonoe #include <dev/pcmcia/pcmciavar.h>
639c25e920Sonoe #include <dev/pcmcia/pcmciadevs.h>
649c25e920Sonoe
657cf29912Scegger static int an_pcmcia_match(device_t, cfdata_t, void *);
6618db93c7Sperry static int an_pcmcia_validate_config(struct pcmcia_config_entry *);
677cf29912Scegger static void an_pcmcia_attach(device_t, device_t, void *);
687cf29912Scegger static int an_pcmcia_detach(device_t, int);
6918db93c7Sperry static int an_pcmcia_enable(struct an_softc *);
7018db93c7Sperry static void an_pcmcia_disable(struct an_softc *);
719c25e920Sonoe
729c25e920Sonoe struct an_pcmcia_softc {
739c25e920Sonoe struct an_softc sc_an; /* real "an" softc */
749c25e920Sonoe
759c25e920Sonoe /* PCMCIA-specific goo */
769c25e920Sonoe struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o space info */
779c25e920Sonoe int sc_io_window; /* our i/o window */
789c25e920Sonoe struct pcmcia_function *sc_pf; /* our PCMCIA function */
79a7125e43Sonoe void *sc_ih; /* interrupt handle */
808355db5eSmycroft
818355db5eSmycroft int sc_state;
828355db5eSmycroft #define AN_PCMCIA_ATTACHED 3
839c25e920Sonoe };
849c25e920Sonoe
85363285caSdrochner CFATTACH_DECL_NEW(an_pcmcia, sizeof(struct an_pcmcia_softc),
86b75a007dSthorpej an_pcmcia_match, an_pcmcia_attach, an_pcmcia_detach, an_activate);
879c25e920Sonoe
8896221eb5Smycroft static const struct pcmcia_product an_pcmcia_products[] = {
899c25e920Sonoe { PCMCIA_VENDOR_AIRONET, PCMCIA_PRODUCT_AIRONET_PC4800,
90b88b233eSmycroft PCMCIA_CIS_AIRONET_PC4800 },
91aa513e63Sonoe { PCMCIA_VENDOR_AIRONET, PCMCIA_PRODUCT_AIRONET_PC4500,
92b88b233eSmycroft PCMCIA_CIS_AIRONET_PC4500 },
935653ae73Sonoe { PCMCIA_VENDOR_AIRONET, PCMCIA_PRODUCT_AIRONET_350,
94b88b233eSmycroft PCMCIA_CIS_AIRONET_350 },
959c25e920Sonoe };
96adbea351Smycroft static const size_t an_pcmcia_nproducts =
97adbea351Smycroft sizeof(an_pcmcia_products) / sizeof(an_pcmcia_products[0]);
989c25e920Sonoe
999c25e920Sonoe static int
an_pcmcia_match(device_t parent,cfdata_t match,void * aux)100*cbab9cadSchs an_pcmcia_match(device_t parent, cfdata_t match, void *aux)
1019c25e920Sonoe {
1029c25e920Sonoe struct pcmcia_attach_args *pa = aux;
1039c25e920Sonoe
104adbea351Smycroft if (pcmcia_product_lookup(pa, an_pcmcia_products, an_pcmcia_nproducts,
105adbea351Smycroft sizeof(an_pcmcia_products[0]), NULL))
106adbea351Smycroft return (1);
107adbea351Smycroft return (0);
1089c25e920Sonoe }
1099c25e920Sonoe
1108355db5eSmycroft static int
an_pcmcia_validate_config(struct pcmcia_config_entry * cfe)111454af1c0Sdsl an_pcmcia_validate_config(struct pcmcia_config_entry *cfe)
1128355db5eSmycroft {
1138355db5eSmycroft if (cfe->iftype != PCMCIA_IFTYPE_IO ||
1148355db5eSmycroft cfe->num_iospace < 1)
1158355db5eSmycroft return (EINVAL);
1168355db5eSmycroft return (0);
1178355db5eSmycroft }
1188355db5eSmycroft
1199c25e920Sonoe static void
an_pcmcia_attach(device_t parent,device_t self,void * aux)120*cbab9cadSchs an_pcmcia_attach(device_t parent, device_t self, void *aux)
1219c25e920Sonoe {
122363285caSdrochner struct an_pcmcia_softc *psc = device_private(self);
1239c25e920Sonoe struct an_softc *sc = &psc->sc_an;
1249c25e920Sonoe struct pcmcia_attach_args *pa = aux;
1259c25e920Sonoe struct pcmcia_config_entry *cfe;
1268355db5eSmycroft int error;
1274ee7a7cdSonoe
128363285caSdrochner sc->sc_dev = self;
1299c25e920Sonoe psc->sc_pf = pa->pf;
1308355db5eSmycroft
1318355db5eSmycroft error = pcmcia_function_configure(pa->pf, an_pcmcia_validate_config);
1328355db5eSmycroft if (error) {
133bebbf57bScegger aprint_error_dev(self, "configure failed, error=%d\n",
1348355db5eSmycroft error);
1358355db5eSmycroft return;
1369c25e920Sonoe }
1379c25e920Sonoe
1388355db5eSmycroft cfe = pa->pf->cfe;
1398355db5eSmycroft sc->sc_iot = cfe->iospace[0].handle.iot;
1408355db5eSmycroft sc->sc_ioh = cfe->iospace[0].handle.ioh;
1419c25e920Sonoe
1428355db5eSmycroft error = an_pcmcia_enable(sc);
1438355db5eSmycroft if (error)
1448355db5eSmycroft goto fail;
145684fe561Sonoe
146684fe561Sonoe sc->sc_enabled = 1;
1479c25e920Sonoe sc->sc_enable = an_pcmcia_enable;
1489c25e920Sonoe sc->sc_disable = an_pcmcia_disable;
1499c25e920Sonoe
1508355db5eSmycroft error = an_attach(sc);
1518355db5eSmycroft if (error) {
152bebbf57bScegger aprint_error_dev(self, "failed to attach controller\n");
1538355db5eSmycroft goto fail2;
1549c25e920Sonoe }
1558355db5eSmycroft
156edeaa5ddStsutsui if (pmf_device_register(self, NULL, NULL))
1574c1d81b2Sjmcneill pmf_class_network_register(self, &sc->sc_if);
158edeaa5ddStsutsui else
159edeaa5ddStsutsui aprint_error_dev(self, "couldn't establish power handler\n");
1609c25e920Sonoe
1619c25e920Sonoe an_pcmcia_disable(sc);
1628355db5eSmycroft sc->sc_enabled = 0;
1638355db5eSmycroft psc->sc_state = AN_PCMCIA_ATTACHED;
1649c25e920Sonoe return;
1659c25e920Sonoe
166684fe561Sonoe fail2:
1678355db5eSmycroft an_pcmcia_disable(sc);
1688355db5eSmycroft sc->sc_enabled = 0;
1698355db5eSmycroft fail:
1708355db5eSmycroft pcmcia_function_unconfigure(pa->pf);
1719c25e920Sonoe }
1729c25e920Sonoe
1739c25e920Sonoe
1749c25e920Sonoe static int
an_pcmcia_detach(device_t self,int flags)1757cf29912Scegger an_pcmcia_detach(device_t self, int flags)
1769c25e920Sonoe {
177363285caSdrochner struct an_pcmcia_softc *psc = device_private(self);
1789c25e920Sonoe int error;
1799c25e920Sonoe
1808355db5eSmycroft if (psc->sc_state != AN_PCMCIA_ATTACHED)
1819c25e920Sonoe return (0);
1829c25e920Sonoe
1834c1d81b2Sjmcneill pmf_device_deregister(self);
1849c25e920Sonoe
1859c25e920Sonoe error = an_detach(&psc->sc_an);
1868355db5eSmycroft if (error)
1879c25e920Sonoe return (error);
1889c25e920Sonoe
1898355db5eSmycroft pcmcia_function_unconfigure(psc->sc_pf);
1909c25e920Sonoe
1919c25e920Sonoe return (0);
1929c25e920Sonoe }
1938355db5eSmycroft
1948355db5eSmycroft static int
an_pcmcia_enable(struct an_softc * sc)195454af1c0Sdsl an_pcmcia_enable(struct an_softc *sc)
1968355db5eSmycroft {
1978355db5eSmycroft struct an_pcmcia_softc *psc = (void *)sc;
1988355db5eSmycroft int error;
1998355db5eSmycroft
2008355db5eSmycroft /* establish the interrupt. */
2018355db5eSmycroft psc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, an_intr, sc);
2028355db5eSmycroft if (!psc->sc_ih)
2038355db5eSmycroft return (EIO);
2048355db5eSmycroft
2058355db5eSmycroft error = pcmcia_function_enable(psc->sc_pf);
2068355db5eSmycroft if (error) {
2078355db5eSmycroft pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih);
2088355db5eSmycroft psc->sc_ih = 0;
2098355db5eSmycroft }
2108355db5eSmycroft
2118355db5eSmycroft return (error);
2128355db5eSmycroft }
2138355db5eSmycroft
2148355db5eSmycroft static void
an_pcmcia_disable(struct an_softc * sc)215454af1c0Sdsl an_pcmcia_disable(struct an_softc *sc)
2168355db5eSmycroft {
2178355db5eSmycroft struct an_pcmcia_softc *psc = (void *)sc;
2188355db5eSmycroft
2198355db5eSmycroft pcmcia_function_disable(psc->sc_pf);
2208355db5eSmycroft pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih);
2218355db5eSmycroft psc->sc_ih = 0;
2228355db5eSmycroft }
223