1 | /* $NetBSD: if_awi_pcmcia.c,v 1.46 2016/07/14 04:19:27 msaitoh Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1999, 2004 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Bill Sommerfeld |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * PCMCIA attachment for BayStack 650 802.11FH PCMCIA card, |
34 | * based on the AMD 79c930 802.11 controller chip. |
35 | * |
36 | * This attachment can probably be trivially adapted for other FH and |
37 | * DS cards based on the same chipset. |
38 | */ |
39 | |
40 | #include <sys/cdefs.h> |
41 | __KERNEL_RCSID(0, "$NetBSD: if_awi_pcmcia.c,v 1.46 2016/07/14 04:19:27 msaitoh Exp $" ); |
42 | |
43 | #include <sys/param.h> |
44 | #include <sys/systm.h> |
45 | #include <sys/mbuf.h> |
46 | #include <sys/socket.h> |
47 | #include <sys/ioctl.h> |
48 | #include <sys/errno.h> |
49 | #include <sys/syslog.h> |
50 | #include <sys/select.h> |
51 | #include <sys/device.h> |
52 | |
53 | #include <net/if.h> |
54 | #include <net/if_dl.h> |
55 | #include <net/if_ether.h> |
56 | #include <net/if_media.h> |
57 | |
58 | #include <net80211/ieee80211_netbsd.h> |
59 | #include <net80211/ieee80211_var.h> |
60 | |
61 | #include <sys/cpu.h> |
62 | #include <sys/bus.h> |
63 | #include <sys/intr.h> |
64 | |
65 | #include <dev/ic/am79c930reg.h> |
66 | #include <dev/ic/am79c930var.h> |
67 | #include <dev/ic/awireg.h> |
68 | #include <dev/ic/awivar.h> |
69 | |
70 | #include <dev/pcmcia/pcmciareg.h> |
71 | #include <dev/pcmcia/pcmciavar.h> |
72 | #include <dev/pcmcia/pcmciadevs.h> |
73 | |
74 | static int awi_pcmcia_match(device_t, cfdata_t, void *); |
75 | static int awi_pcmcia_validate_config(struct pcmcia_config_entry *); |
76 | static void awi_pcmcia_attach(device_t, device_t, void *); |
77 | static int awi_pcmcia_detach(device_t, int); |
78 | static int awi_pcmcia_enable(struct awi_softc *); |
79 | static void awi_pcmcia_disable(struct awi_softc *); |
80 | |
81 | struct awi_pcmcia_softc { |
82 | struct awi_softc sc_awi; /* real "awi" softc */ |
83 | |
84 | /* PCMCIA-specific goo */ |
85 | struct pcmcia_function *sc_pf; /* our PCMCIA function */ |
86 | void *sc_ih; /* interrupt handler */ |
87 | |
88 | int sc_state; |
89 | #define AWI_PCMCIA_ATTACHED 3 |
90 | }; |
91 | |
92 | CFATTACH_DECL_NEW(awi_pcmcia, sizeof(struct awi_pcmcia_softc), |
93 | awi_pcmcia_match, awi_pcmcia_attach, awi_pcmcia_detach, awi_activate); |
94 | |
95 | static const struct pcmcia_product awi_pcmcia_products[] = { |
96 | { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_STACK_650, |
97 | PCMCIA_CIS_BAY_STACK_650 }, |
98 | |
99 | { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_STACK_660, |
100 | PCMCIA_CIS_BAY_STACK_660 }, |
101 | |
102 | { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_SURFER_PRO, |
103 | PCMCIA_CIS_BAY_SURFER_PRO }, |
104 | |
105 | { PCMCIA_VENDOR_AMD, PCMCIA_PRODUCT_AMD_AM79C930, |
106 | PCMCIA_CIS_AMD_AM79C930 }, |
107 | |
108 | { PCMCIA_VENDOR_ICOM, PCMCIA_PRODUCT_ICOM_SL200, |
109 | PCMCIA_CIS_ICOM_SL200 }, |
110 | |
111 | { PCMCIA_VENDOR_NOKIA, PCMCIA_PRODUCT_NOKIA_C020_WLAN, |
112 | PCMCIA_CIS_NOKIA_C020_WLAN }, |
113 | |
114 | { PCMCIA_VENDOR_FARALLON, PCMCIA_PRODUCT_FARALLON_SKYLINE, |
115 | PCMCIA_CIS_FARALLON_SKYLINE }, |
116 | }; |
117 | static const size_t awi_pcmcia_nproducts = |
118 | sizeof(awi_pcmcia_products) / sizeof(awi_pcmcia_products[0]); |
119 | |
120 | static int |
121 | awi_pcmcia_enable(struct awi_softc *sc) |
122 | { |
123 | struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)sc; |
124 | struct pcmcia_function *pf = psc->sc_pf; |
125 | int error; |
126 | |
127 | /* establish the interrupt. */ |
128 | psc->sc_ih = pcmcia_intr_establish(pf, IPL_NET, awi_intr, sc); |
129 | if (!psc->sc_ih) |
130 | return (EIO); |
131 | |
132 | error = pcmcia_function_enable(pf); |
133 | if (error) { |
134 | pcmcia_intr_disestablish(pf, psc->sc_ih); |
135 | psc->sc_ih = 0; |
136 | } |
137 | |
138 | return (error); |
139 | } |
140 | |
141 | static void |
142 | awi_pcmcia_disable(struct awi_softc *sc) |
143 | { |
144 | struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)sc; |
145 | struct pcmcia_function *pf = psc->sc_pf; |
146 | |
147 | pcmcia_function_disable(pf); |
148 | pcmcia_intr_disestablish(pf, psc->sc_ih); |
149 | psc->sc_ih = 0; |
150 | } |
151 | |
152 | static int |
153 | awi_pcmcia_match(device_t parent, cfdata_t match, void *aux) |
154 | { |
155 | struct pcmcia_attach_args *pa = aux; |
156 | |
157 | if (pcmcia_product_lookup(pa, awi_pcmcia_products, awi_pcmcia_nproducts, |
158 | sizeof(awi_pcmcia_products[0]), NULL)) |
159 | return (1); |
160 | return (0); |
161 | } |
162 | |
163 | static int |
164 | awi_pcmcia_validate_config(struct pcmcia_config_entry *cfe) |
165 | { |
166 | if (cfe->iftype != PCMCIA_IFTYPE_IO || |
167 | cfe->num_iospace < 1 || |
168 | cfe->iospace[0].length < AM79C930_IO_SIZE) |
169 | return (EINVAL); |
170 | if (cfe->num_memspace < 1) { |
171 | cfe->memspace[0].length = AM79C930_MEM_SIZE; |
172 | cfe->memspace[0].cardaddr = 0; |
173 | cfe->memspace[0].hostaddr = 0; |
174 | } else if (cfe->memspace[0].length < AM79C930_MEM_SIZE) |
175 | return (EINVAL); |
176 | return (0); |
177 | } |
178 | |
179 | static void |
180 | awi_pcmcia_attach(device_t parent, device_t self, void *aux) |
181 | { |
182 | struct awi_pcmcia_softc *psc = device_private(self); |
183 | struct awi_softc *sc = &psc->sc_awi; |
184 | struct pcmcia_attach_args *pa = aux; |
185 | struct pcmcia_config_entry *cfe; |
186 | int error; |
187 | |
188 | sc->sc_dev = self; |
189 | psc->sc_pf = pa->pf; |
190 | |
191 | error = pcmcia_function_configure(pa->pf, awi_pcmcia_validate_config); |
192 | if (error) { |
193 | aprint_error_dev(self, "configure failed, error=%d\n" , error); |
194 | return; |
195 | } |
196 | |
197 | cfe = pa->pf->cfe; |
198 | sc->sc_chip.sc_bustype = AM79C930_BUS_PCMCIA; |
199 | sc->sc_chip.sc_iot = cfe->iospace[0].handle.iot; |
200 | sc->sc_chip.sc_ioh = cfe->iospace[0].handle.ioh; |
201 | if (cfe->num_memspace > 0) { |
202 | sc->sc_chip.sc_memt = cfe->memspace[0].handle.memt; |
203 | sc->sc_chip.sc_memh = cfe->memspace[0].handle.memh; |
204 | am79c930_chip_init(&sc->sc_chip, 1); |
205 | } else |
206 | am79c930_chip_init(&sc->sc_chip, 0); |
207 | |
208 | error = awi_pcmcia_enable(sc); |
209 | if (error) |
210 | goto fail; |
211 | sc->sc_enabled = 1; |
212 | |
213 | awi_read_bytes(sc, AWI_BANNER, sc->sc_banner, AWI_BANNER_LEN); |
214 | if (memcmp(sc->sc_banner, "PCnetMobile:" , 12)) |
215 | goto fail2; |
216 | |
217 | sc->sc_enable = awi_pcmcia_enable; |
218 | sc->sc_disable = awi_pcmcia_disable; |
219 | |
220 | sc->sc_cansleep = 1; |
221 | |
222 | if (awi_attach(sc) != 0) { |
223 | aprint_error_dev(self, "failed to attach controller\n" ); |
224 | goto fail2; |
225 | } |
226 | |
227 | sc->sc_enabled = 0; |
228 | awi_pcmcia_disable(sc); |
229 | psc->sc_state = AWI_PCMCIA_ATTACHED; |
230 | return; |
231 | |
232 | fail2: |
233 | sc->sc_enabled = 0; |
234 | awi_pcmcia_disable(sc); |
235 | fail: |
236 | pcmcia_function_unconfigure(pa->pf); |
237 | } |
238 | |
239 | static int |
240 | awi_pcmcia_detach(device_t self, int flags) |
241 | { |
242 | struct awi_pcmcia_softc *psc = device_private(self); |
243 | int error; |
244 | |
245 | if (psc->sc_state != AWI_PCMCIA_ATTACHED) |
246 | return (0); |
247 | |
248 | error = awi_detach(&psc->sc_awi); |
249 | if (error) |
250 | return (error); |
251 | |
252 | pcmcia_function_unconfigure(psc->sc_pf); |
253 | |
254 | return (0); |
255 | } |
256 | |