Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /** |
2 | * Granch SBNI16 G.SHDSL Modem driver | |
3 | * Written by Denis I. Timofeev, 2002-2003. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * | |
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
24 | * SUCH DAMAGE. | |
25 | * | |
26 | * $FreeBSD: src/sys/dev/sbsh/if_sbsh.c,v 1.3.2.1 2003/04/15 18:15:07 fjoe Exp $ | |
dadab5e9 | 27 | * $DragonFly: src/sys/dev/netif/sbsh/if_sbsh.c,v 1.4 2003/06/25 03:55:48 dillon Exp $ |
984263bc MD |
28 | */ |
29 | ||
30 | #include <sys/param.h> | |
31 | #include <sys/systm.h> | |
32 | #include <sys/sockio.h> | |
33 | #include <sys/mbuf.h> | |
34 | #include <sys/malloc.h> | |
35 | #include <sys/kernel.h> | |
36 | #include <sys/proc.h> | |
37 | #include <sys/socket.h> | |
38 | #include <sys/random.h> | |
39 | #include <machine/clock.h> | |
40 | #include <machine/stdarg.h> | |
41 | ||
42 | #include <net/if.h> | |
43 | #include <net/if_arp.h> | |
44 | #include <net/ethernet.h> | |
45 | #include <net/if_media.h> | |
46 | ||
47 | #include <net/bpf.h> | |
48 | ||
49 | #include <vm/vm.h> | |
50 | #include <vm/pmap.h> | |
51 | #include <machine/bus.h> | |
52 | #include <machine/resource.h> | |
53 | #include <sys/bus.h> | |
54 | #include <sys/rman.h> | |
55 | ||
56 | #include <pci/pcireg.h> | |
57 | #include <pci/pcivar.h> | |
58 | ||
59 | #include <dev/sbsh/if_sbshreg.h> | |
60 | ||
61 | /* -------------------------------------------------------------------------- */ | |
62 | ||
63 | struct sbni16_hw_regs { | |
64 | u_int8_t CR, CRB, SR, IMR, CTDR, LTDR, CRDR, LRDR; | |
65 | }; | |
66 | ||
67 | struct hw_descr { | |
68 | u_int32_t address; | |
69 | u_int32_t length; | |
70 | }; | |
71 | ||
72 | struct cx28975_cmdarea { | |
73 | u_int8_t intr_host; | |
74 | u_int8_t intr_8051; | |
75 | u_int8_t map_version; | |
76 | ||
77 | u_int8_t in_dest; | |
78 | u_int8_t in_opcode; | |
79 | u_int8_t in_zero; | |
80 | u_int8_t in_length; | |
81 | u_int8_t in_csum; | |
82 | u_int8_t in_data[75]; | |
83 | u_int8_t in_datasum; | |
84 | ||
85 | u_int8_t out_dest; | |
86 | u_int8_t out_opcode; | |
87 | u_int8_t out_ack; | |
88 | u_int8_t out_length; | |
89 | u_int8_t out_csum; | |
90 | u_int8_t out_data[75]; | |
91 | u_int8_t out_datasum; | |
92 | }; | |
93 | ||
94 | #define XQLEN 8 | |
95 | #define RQLEN 8 | |
96 | ||
97 | struct sbsh_softc { | |
98 | struct arpcom arpcom; /* ethernet common */ | |
99 | ||
100 | struct resource *mem_res; | |
101 | struct resource *irq_res; | |
102 | void *intr_hand; | |
103 | ||
104 | void *mem_base; /* mapped memory address */ | |
105 | ||
106 | volatile struct sbni16_hw_regs *regs; | |
107 | volatile struct hw_descr *tbd; | |
108 | volatile struct hw_descr *rbd; | |
109 | volatile struct cx28975_cmdarea *cmdp; | |
110 | ||
111 | /* SBNI16 controller statistics */ | |
112 | struct sbni16_stats { | |
113 | u_int32_t sent_pkts, rcvd_pkts; | |
114 | u_int32_t crc_errs, ufl_errs, ofl_errs, attempts, last_time; | |
115 | } in_stats; | |
116 | ||
117 | /* transmit and reception queues */ | |
118 | struct mbuf *xq[XQLEN], *rq[RQLEN]; | |
119 | unsigned head_xq, tail_xq, head_rq, tail_rq; | |
120 | ||
121 | /* the descriptors mapped onto the first buffers in xq and rq */ | |
122 | unsigned head_tdesc, head_rdesc; | |
123 | u_int8_t state; | |
124 | }; | |
125 | ||
126 | struct cx28975_cfg { | |
127 | u_int8_t *firmw_image; | |
128 | u_int32_t firmw_len; | |
129 | u_int32_t lrate: 10; | |
130 | u_int32_t master: 1; | |
131 | u_int32_t mod: 2; | |
132 | u_int32_t crc16: 1; | |
133 | u_int32_t fill_7e: 1; | |
134 | u_int32_t inv: 1; | |
135 | u_int32_t rburst: 1; | |
136 | u_int32_t wburst: 1; | |
137 | u_int32_t : 14; | |
138 | }; | |
139 | ||
140 | /* SHDSL transceiver statistics */ | |
141 | struct dsl_stats { | |
142 | u_int8_t status_1, status_3; | |
143 | u_int8_t attenuat, nmr, tpbo, rpbo; | |
144 | u_int16_t losw, segd, crc, sega, losd; | |
145 | }; | |
146 | ||
147 | enum State { NOT_LOADED, DOWN, ACTIVATION, ACTIVE }; | |
148 | ||
149 | #define SIOCLOADFIRMW _IOWR('i', 67, struct ifreq) | |
150 | #define SIOCGETSTATS _IOWR('i', 68, struct ifreq) | |
151 | #define SIOCCLRSTATS _IOWR('i', 69, struct ifreq) | |
152 | ||
153 | static int sbsh_probe(device_t); | |
154 | static int sbsh_attach(device_t); | |
155 | static int sbsh_detach(device_t); | |
156 | static int sbsh_ioctl(struct ifnet *, u_long, caddr_t); | |
157 | static void sbsh_shutdown(device_t); | |
158 | static int sbsh_suspend(device_t); | |
159 | static int sbsh_resume(device_t); | |
160 | static void sbsh_watchdog(struct ifnet *); | |
161 | ||
162 | static void sbsh_start(struct ifnet *); | |
163 | static void sbsh_init(void *); | |
164 | static void sbsh_stop(struct sbsh_softc *); | |
165 | static void init_card(struct sbsh_softc *); | |
166 | static void sbsh_intr(void *); | |
167 | static void resume_tx(struct sbsh_softc *); | |
168 | static void start_xmit_frames(struct sbsh_softc *); | |
169 | static void encap_frame(struct sbsh_softc *, struct mbuf *); | |
170 | static struct mbuf * repack(struct sbsh_softc *, struct mbuf *); | |
171 | static void free_sent_buffers(struct sbsh_softc *); | |
172 | static void alloc_rx_buffers(struct sbsh_softc *); | |
173 | static void indicate_frames(struct sbsh_softc *); | |
174 | static void drop_queues(struct sbsh_softc *); | |
175 | static void activate(struct sbsh_softc *); | |
176 | static void deactivate(struct sbsh_softc *); | |
177 | static void cx28975_interrupt(struct sbsh_softc *); | |
178 | static int start_cx28975(struct sbsh_softc *, struct cx28975_cfg); | |
179 | static int download_firmware(struct sbsh_softc *, u_int8_t *, u_int32_t); | |
180 | static int issue_cx28975_cmd(struct sbsh_softc *, u_int8_t, | |
181 | u_int8_t *, u_int8_t); | |
182 | ||
183 | static device_method_t sbsh_methods[] = { | |
184 | /* Device interface */ | |
185 | DEVMETHOD(device_probe, sbsh_probe), | |
186 | DEVMETHOD(device_attach, sbsh_attach), | |
187 | DEVMETHOD(device_detach, sbsh_detach), | |
188 | DEVMETHOD(device_shutdown, sbsh_shutdown), | |
189 | DEVMETHOD(device_suspend, sbsh_suspend), | |
190 | DEVMETHOD(device_resume, sbsh_resume), | |
191 | ||
192 | { 0, 0 } | |
193 | }; | |
194 | ||
195 | static driver_t sbsh_driver = { | |
196 | "sbsh", | |
197 | sbsh_methods, | |
198 | sizeof(struct sbsh_softc) | |
199 | }; | |
200 | ||
201 | static devclass_t sbsh_devclass; | |
202 | ||
203 | DRIVER_MODULE(if_sbsh, pci, sbsh_driver, sbsh_devclass, 0, 0); | |
204 | ||
205 | static int | |
206 | sbsh_probe(device_t dev) | |
207 | { | |
208 | if (pci_get_vendor(dev) != SBNI16_VENDOR | |
209 | || pci_get_device(dev) != SBNI16_DEVICE | |
210 | || pci_get_subdevice(dev) != SBNI16_SUBDEV) | |
211 | return (ENXIO); | |
212 | ||
213 | device_set_desc(dev, "Granch SBNI16 G.SHDSL Modem"); | |
214 | return (0); | |
215 | } | |
216 | ||
217 | static int | |
218 | sbsh_attach(device_t dev) | |
219 | { | |
220 | struct sbsh_softc *sc; | |
221 | struct ifnet *ifp; | |
222 | int unit, error = 0, rid, s; | |
223 | ||
224 | s = splimp(); | |
225 | ||
226 | sc = device_get_softc(dev); | |
227 | unit = device_get_unit(dev); | |
228 | ||
229 | rid = PCIR_MAPS + 4; | |
230 | sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, | |
231 | 0, ~0, 4096, RF_ACTIVE); | |
232 | ||
233 | if (sc->mem_res == NULL) { | |
234 | printf ("sbsh%d: couldn't map memory\n", unit); | |
235 | error = ENXIO; | |
236 | goto fail; | |
237 | } | |
238 | ||
239 | rid = 0; | |
240 | sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, | |
241 | RF_SHAREABLE | RF_ACTIVE); | |
242 | ||
243 | if (sc->irq_res == NULL) { | |
244 | printf("sbsh%d: couldn't map interrupt\n", unit); | |
245 | bus_release_resource(dev, SYS_RES_MEMORY, | |
246 | PCIR_MAPS + 4, sc->mem_res); | |
247 | error = ENXIO; | |
248 | goto fail; | |
249 | } | |
250 | ||
251 | sc->mem_base = rman_get_virtual(sc->mem_res); | |
252 | init_card(sc); | |
253 | ||
254 | error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, | |
255 | sbsh_intr, sc, &sc->intr_hand); | |
256 | if (error) { | |
257 | bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); | |
258 | bus_release_resource(dev, SYS_RES_MEMORY, | |
259 | PCIR_MAPS + 4, sc->mem_res); | |
260 | printf("sbsh%d: couldn't set up irq\n", unit); | |
261 | goto fail; | |
262 | } | |
263 | ||
264 | /* generate ethernet MAC address */ | |
265 | *(u_int32_t *)sc->arpcom.ac_enaddr = htonl(0x00ff0192); | |
266 | read_random_unlimited(sc->arpcom.ac_enaddr + 4, 2); | |
267 | ||
268 | ifp = &sc->arpcom.ac_if; | |
269 | ifp->if_softc = sc; | |
270 | ifp->if_unit = unit; | |
271 | ifp->if_name = "sbsh"; | |
272 | ifp->if_mtu = ETHERMTU; | |
273 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | |
274 | ifp->if_ioctl = sbsh_ioctl; | |
275 | ifp->if_output = ether_output; | |
276 | ifp->if_start = sbsh_start; | |
277 | ifp->if_watchdog = sbsh_watchdog; | |
278 | ifp->if_init = sbsh_init; | |
279 | ifp->if_baudrate = 4600000; | |
280 | ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; | |
281 | ||
282 | ether_ifattach(ifp, ETHER_BPF_SUPPORTED); | |
283 | ||
284 | fail: | |
285 | splx(s); | |
286 | return (error); | |
287 | } | |
288 | ||
289 | static int | |
290 | sbsh_detach(device_t dev) | |
291 | { | |
292 | struct sbsh_softc *sc; | |
293 | struct ifnet *ifp; | |
294 | int s; | |
295 | ||
296 | s = splimp(); | |
297 | ||
298 | sc = device_get_softc(dev); | |
299 | ifp = &sc->arpcom.ac_if; | |
300 | ||
301 | sbsh_stop(sc); | |
302 | ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); | |
303 | ||
304 | bus_teardown_intr(dev, sc->irq_res, sc->intr_hand); | |
305 | bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); | |
306 | bus_release_resource(dev, SYS_RES_MEMORY, PCIR_MAPS + 4, sc->mem_res); | |
307 | ||
308 | splx(s); | |
309 | return (0); | |
310 | } | |
311 | ||
312 | ||
313 | static void | |
314 | sbsh_start(struct ifnet *ifp) | |
315 | { | |
316 | struct sbsh_softc *sc = ifp->if_softc; | |
317 | int s; | |
318 | ||
319 | if (sc->state != ACTIVE) | |
320 | return; | |
321 | ||
322 | s = splimp(); | |
323 | start_xmit_frames(ifp->if_softc); | |
324 | splx(s); | |
325 | } | |
326 | ||
327 | ||
328 | static void | |
329 | sbsh_init(void *xsc) | |
330 | { | |
331 | struct sbsh_softc *sc = xsc; | |
332 | struct ifnet *ifp = &sc->arpcom.ac_if; | |
333 | int s; | |
334 | u_int8_t t; | |
335 | ||
336 | if ((ifp->if_flags & IFF_RUNNING) || sc->state == NOT_LOADED) | |
337 | return; | |
338 | ||
339 | s = splimp(); | |
340 | ||
341 | bzero(&sc->in_stats, sizeof(struct sbni16_stats)); | |
342 | sc->head_xq = sc->tail_xq = sc->head_rq = sc->tail_rq = 0; | |
343 | sc->head_tdesc = sc->head_rdesc = 0; | |
344 | ||
345 | sc->regs->IMR = EXT; | |
346 | t = 2; | |
347 | issue_cx28975_cmd(sc, _DSL_CLEAR_ERROR_CTRS, &t, 1); | |
348 | if (issue_cx28975_cmd(sc, _DSL_ACTIVATION, &t, 1) == 0) { | |
349 | sc->state = ACTIVATION; | |
350 | ||
351 | ifp->if_flags |= IFF_RUNNING; | |
352 | ifp->if_flags &= ~IFF_OACTIVE; | |
353 | } | |
354 | ||
355 | splx(s); | |
356 | } | |
357 | ||
358 | ||
359 | static void | |
360 | sbsh_stop(struct sbsh_softc *sc) | |
361 | { | |
362 | int s; | |
363 | u_int8_t t; | |
364 | ||
365 | s = splimp(); | |
366 | sc->regs->IMR = EXT; | |
367 | ||
368 | t = 0; | |
369 | issue_cx28975_cmd(sc, _DSL_ACTIVATION, &t, 1); | |
370 | if (sc->state == ACTIVE) { | |
371 | t = 1; | |
372 | issue_cx28975_cmd(sc, _DSL_FORCE_DEACTIVATE, &t, 1); | |
373 | /* FIX! activation manager state */ | |
374 | ||
375 | /* Is it really must be done here? It calls from intr handler */ | |
376 | deactivate(sc); | |
377 | } | |
378 | ||
379 | sc->regs->IMR = 0; | |
380 | sc->state = DOWN; | |
381 | splx(s); | |
382 | } | |
383 | ||
384 | ||
385 | static void | |
386 | init_card(struct sbsh_softc *sc) | |
387 | { | |
388 | sc->state = NOT_LOADED; | |
389 | sc->tbd = (struct hw_descr *) sc->mem_base; | |
390 | sc->rbd = (struct hw_descr *) ((u_int8_t *)sc->mem_base + 0x400); | |
391 | sc->regs = (struct sbni16_hw_regs *) ((u_int8_t *)sc->mem_base + 0x800); | |
392 | sc->cmdp = (struct cx28975_cmdarea *) ((u_int8_t *)sc->mem_base + 0xc00); | |
393 | ||
394 | sc->regs->CR = 0; | |
395 | sc->regs->SR = 0xff; | |
396 | sc->regs->IMR = 0; | |
397 | } | |
398 | ||
399 | ||
400 | static int | |
401 | sbsh_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | |
402 | { | |
403 | struct sbsh_softc *sc = ifp->if_softc; | |
404 | struct ifreq *ifr = (struct ifreq *) data; | |
405 | struct cx28975_cfg cfg; | |
406 | struct dsl_stats ds; | |
dadab5e9 | 407 | struct thread *td = curthread; |
984263bc MD |
408 | int s, error = 0; |
409 | u_int8_t t; | |
410 | ||
411 | s = splimp(); | |
412 | ||
413 | switch(cmd) { | |
414 | case SIOCLOADFIRMW: | |
dadab5e9 | 415 | if ((error = suser(td)) != 0) |
984263bc MD |
416 | break; |
417 | if (ifp->if_flags & IFF_UP) | |
418 | error = EBUSY; | |
419 | ||
420 | bcopy((caddr_t)ifr->ifr_data, (caddr_t)&cfg, sizeof cfg); | |
421 | if (start_cx28975(sc, cfg) == 0) { | |
422 | static char *modstr[] = { | |
423 | "TCPAM32", "TCPAM16", "TCPAM8", "TCPAM4" }; | |
424 | if_printf(&sc->arpcom.ac_if, "%s, rate %d, %s\n", | |
425 | cfg.master ? "master" : "slave", | |
426 | cfg.lrate << 3, modstr[cfg.mod]); | |
427 | } else { | |
428 | if_printf(&sc->arpcom.ac_if, | |
429 | "unable to load firmware\n"); | |
430 | error = EIO; | |
431 | } | |
432 | break; | |
433 | ||
434 | case SIOCGETSTATS : | |
dadab5e9 | 435 | if ((error = suser(td)) != 0) |
984263bc MD |
436 | break; |
437 | ||
438 | t = 0; | |
439 | if (issue_cx28975_cmd(sc, _DSL_FAR_END_ATTEN, &t, 1)) | |
440 | error = EIO; | |
441 | ds.attenuat = sc->cmdp->out_data[0]; | |
442 | ||
443 | if (issue_cx28975_cmd(sc, _DSL_NOISE_MARGIN, &t, 1)) | |
444 | error = EIO; | |
445 | ds.nmr = sc->cmdp->out_data[0]; | |
446 | ||
447 | if (issue_cx28975_cmd(sc, _DSL_POWER_BACK_OFF_RESULT, &t, 1)) | |
448 | error = EIO; | |
449 | ds.tpbo = sc->cmdp->out_data[0]; | |
450 | ds.rpbo = sc->cmdp->out_data[1]; | |
451 | ||
452 | if (!issue_cx28975_cmd(sc, _DSL_HDSL_PERF_ERR_CTRS, &t, 1)) { | |
453 | int i; | |
454 | for (i = 0; i < 10; ++i) | |
455 | ((u_int8_t *) &ds.losw)[i] = | |
456 | sc->cmdp->out_data[i]; | |
457 | } else | |
458 | error = EIO; | |
459 | ||
460 | ds.status_1 = ((volatile u_int8_t *)sc->cmdp)[0x3c0]; | |
461 | ds.status_3 = ((volatile u_int8_t *)sc->cmdp)[0x3c2]; | |
462 | ||
463 | bcopy(&sc->in_stats, ifr->ifr_data, sizeof(struct sbni16_stats)); | |
464 | bcopy(&ds, ifr->ifr_data + sizeof(struct sbni16_stats), | |
465 | sizeof(struct dsl_stats)); | |
466 | break; | |
467 | ||
468 | case SIOCCLRSTATS : | |
dadab5e9 | 469 | if (!(error = suser(td))) { |
984263bc MD |
470 | bzero(&sc->in_stats, sizeof(struct sbni16_stats)); |
471 | t = 2; | |
472 | if (issue_cx28975_cmd(sc, _DSL_CLEAR_ERROR_CTRS, &t, 1)) | |
473 | error = EIO; | |
474 | } | |
475 | break; | |
476 | ||
477 | case SIOCSIFADDR: | |
478 | case SIOCGIFADDR: | |
479 | case SIOCSIFMTU: | |
480 | error = ether_ioctl(ifp, cmd, data); | |
481 | break; | |
482 | ||
483 | case SIOCSIFFLAGS: | |
484 | if (ifp->if_flags & IFF_UP) { | |
485 | if (!(ifp->if_flags & IFF_RUNNING)) { | |
486 | if (sc->state == NOT_LOADED) { | |
487 | if_printf(ifp, "firmware wasn't loaded\n"); | |
488 | error = EBUSY; | |
489 | } else | |
490 | sbsh_init(sc); | |
491 | } | |
492 | } else { | |
493 | if (ifp->if_flags & IFF_RUNNING) { | |
494 | sbsh_stop(sc); | |
495 | ifp->if_flags &= ~IFF_RUNNING; | |
496 | } | |
497 | } | |
498 | break; | |
499 | ||
500 | case SIOCADDMULTI: | |
501 | case SIOCDELMULTI: | |
502 | error = 0; | |
503 | break; | |
504 | ||
505 | default: | |
506 | error = EINVAL; | |
507 | break; | |
508 | } | |
509 | ||
510 | splx(s); | |
511 | return (error); | |
512 | } | |
513 | ||
514 | ||
515 | static void | |
516 | sbsh_shutdown(device_t dev) | |
517 | { | |
518 | struct sbsh_softc *sc = device_get_softc(dev); | |
519 | ||
520 | sbsh_stop(sc); | |
521 | } | |
522 | ||
523 | static int | |
524 | sbsh_suspend(device_t dev) | |
525 | { | |
526 | struct sbsh_softc *sc = device_get_softc(dev); | |
527 | int s; | |
528 | ||
529 | s = splimp(); | |
530 | sbsh_stop(sc); | |
531 | splx(s); | |
532 | ||
533 | return (0); | |
534 | } | |
535 | ||
536 | static int | |
537 | sbsh_resume(device_t dev) | |
538 | { | |
539 | struct sbsh_softc *sc = device_get_softc(dev); | |
540 | struct ifnet *ifp; | |
541 | int s; | |
542 | ||
543 | s = splimp(); | |
544 | ifp = &sc->arpcom.ac_if; | |
545 | ||
546 | if (ifp->if_flags & IFF_UP) | |
547 | sbsh_init(sc); | |
548 | ||
549 | splx(s); | |
550 | return (0); | |
551 | } | |
552 | ||
553 | ||
554 | static void | |
555 | sbsh_watchdog(struct ifnet *ifp) | |
556 | { | |
557 | struct sbsh_softc *sc = ifp->if_softc; | |
558 | ||
559 | if_printf(ifp, "transmit timeout\n"); | |
560 | ||
561 | if (sc->regs->SR & TXS) { | |
562 | sc->regs->SR = TXS; | |
563 | if_printf(ifp, "interrupt posted but not delivered\n"); | |
564 | } | |
565 | free_sent_buffers(sc); | |
566 | } | |
567 | ||
568 | /* -------------------------------------------------------------------------- */ | |
569 | ||
570 | static void | |
571 | sbsh_intr(void *arg) | |
572 | { | |
573 | struct sbsh_softc *sc = (struct sbsh_softc *)arg; | |
574 | u_int8_t status = sc->regs->SR; | |
575 | ||
576 | if (status == 0) | |
577 | return; | |
578 | ||
579 | if (status & EXT) { | |
580 | cx28975_interrupt(sc); | |
581 | sc->regs->SR = EXT; | |
582 | } | |
583 | ||
584 | if (status & UFL) { | |
585 | resume_tx(sc); | |
586 | sc->regs->SR = UFL; | |
587 | ++sc->in_stats.ufl_errs; | |
588 | ++sc->arpcom.ac_if.if_oerrors; | |
589 | } | |
590 | ||
591 | if (status & RXS) { | |
592 | sc->regs->SR = RXS; | |
593 | indicate_frames(sc); | |
594 | alloc_rx_buffers(sc); | |
595 | } | |
596 | ||
597 | if (status & TXS) { | |
598 | sc->regs->SR = TXS; | |
599 | free_sent_buffers(sc); | |
600 | } | |
601 | ||
602 | if (status & CRC) { | |
603 | ++sc->in_stats.crc_errs; | |
604 | ++sc->arpcom.ac_if.if_ierrors; | |
605 | sc->regs->SR = CRC; | |
606 | } | |
607 | ||
608 | if (status & OFL) { | |
609 | ++sc->in_stats.ofl_errs; | |
610 | ++sc->arpcom.ac_if.if_ierrors; | |
611 | sc->regs->SR = OFL; | |
612 | } | |
613 | } | |
614 | ||
615 | /* | |
616 | * Look for a first descriptor of a next packet, and write it's number | |
617 | * into CTDR. Then enable the transmitter. | |
618 | */ | |
619 | static void | |
620 | resume_tx(struct sbsh_softc *sc) | |
621 | { | |
622 | u_int32_t cur_tbd = sc->regs->CTDR; | |
623 | ||
624 | while (cur_tbd != sc->regs->LTDR | |
625 | && (sc->tbd[cur_tbd++].length & LAST_FRAG) == 0) | |
626 | ; | |
627 | sc->regs->CTDR = cur_tbd; | |
628 | sc->regs->CR |= TXEN; | |
629 | } | |
630 | ||
631 | static void | |
632 | start_xmit_frames(struct sbsh_softc *sc) | |
633 | { | |
634 | struct ifnet *ifp = &sc->arpcom.ac_if; | |
635 | struct mbuf *m; | |
636 | ||
637 | /* | |
638 | * Check if we have any free descriptor(s) and free space in | |
639 | * our transmit queue. | |
640 | */ | |
641 | while (sc->tail_xq != ((sc->head_xq - 1) & (XQLEN - 1)) | |
642 | && sc->regs->LTDR != ((sc->head_tdesc - 1) & 0x7f)) { | |
643 | ||
644 | IF_DEQUEUE(&ifp->if_snd, m); | |
645 | if (!m) | |
646 | break; | |
647 | if (m->m_pkthdr.len) { | |
648 | if (ifp->if_bpf) | |
649 | bpf_mtap(ifp, m); | |
650 | encap_frame(sc, m); | |
651 | } else | |
652 | m_freem(m); | |
653 | } | |
654 | ||
655 | if (sc->regs->CTDR != sc->regs->LTDR) | |
656 | ifp->if_flags |= IFF_OACTIVE; | |
657 | else | |
658 | ifp->if_flags &= ~IFF_OACTIVE; | |
659 | } | |
660 | ||
661 | ||
662 | /* | |
663 | * MUST be called at splimp | |
664 | */ | |
665 | static void | |
666 | encap_frame(struct sbsh_softc *sc, struct mbuf *m_head) | |
667 | { | |
668 | struct mbuf *m; | |
669 | u_int32_t cur_tbd; | |
670 | int done; | |
671 | ||
672 | look_for_nonzero: | |
673 | for (m = m_head; !m->m_len; m = m->m_next) | |
674 | ; | |
675 | ||
676 | cur_tbd = sc->regs->LTDR & 0x7f; | |
677 | done = 0; | |
678 | do { | |
679 | if (m->m_len < 5 || cur_tbd == ((sc->head_tdesc - 1) & 0x7f)) { | |
680 | if ((m_head = repack(sc, m_head)) != NULL) | |
681 | goto look_for_nonzero; | |
682 | else | |
683 | return; | |
684 | } | |
685 | ||
686 | sc->tbd[cur_tbd].address = vtophys(mtod(m, vm_offset_t)); | |
687 | sc->tbd[cur_tbd].length = m->m_len; | |
688 | ||
689 | do { | |
690 | m = m->m_next; | |
691 | } while (m && !m->m_len); | |
692 | ||
693 | if (!m) { /* last fragment has been reached */ | |
694 | sc->tbd[cur_tbd].length |= LAST_FRAG; | |
695 | done = 1; | |
696 | } | |
697 | ||
698 | ++cur_tbd; | |
699 | cur_tbd &= 0x7f; | |
700 | } while (!done); | |
701 | ||
702 | sc->xq[sc->tail_xq++] = m_head; | |
703 | sc->tail_xq &= (XQLEN - 1); | |
704 | ||
705 | sc->regs->LTDR = cur_tbd; | |
706 | ++sc->in_stats.sent_pkts; | |
707 | ++sc->arpcom.ac_if.if_opackets; | |
708 | } | |
709 | ||
710 | static struct mbuf * | |
711 | repack(struct sbsh_softc *sc, struct mbuf *m) | |
712 | { | |
713 | struct mbuf *m_new; | |
714 | ||
715 | MGETHDR(m_new, M_DONTWAIT, MT_DATA); | |
716 | if (!m_new) { | |
717 | if_printf (&sc->arpcom.ac_if, | |
718 | "unable to get mbuf.\n"); | |
719 | return (NULL); | |
720 | } | |
721 | ||
722 | if (m->m_pkthdr.len > MHLEN) { | |
723 | MCLGET(m_new, M_DONTWAIT); | |
724 | if (!(m_new->m_flags & M_EXT)) { | |
725 | m_freem(m_new); | |
726 | if_printf (&sc->arpcom.ac_if, | |
727 | "unable to get mbuf cluster.\n"); | |
728 | return (NULL); | |
729 | } | |
730 | } | |
731 | ||
732 | m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t)); | |
733 | m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len; | |
734 | m_freem(m); | |
735 | return (m_new); | |
736 | } | |
737 | ||
738 | static void | |
739 | free_sent_buffers(struct sbsh_softc *sc) | |
740 | { | |
741 | u_int32_t cur_tbd; | |
742 | ||
743 | cur_tbd = sc->regs->CTDR; | |
744 | ||
745 | while (sc->head_tdesc != cur_tbd) { | |
746 | /* | |
747 | * Be careful! one element in xq may correspond to | |
748 | * multiple descriptors. | |
749 | */ | |
750 | if (sc->tbd[sc->head_tdesc].length & LAST_FRAG) { | |
751 | m_freem(sc->xq[sc->head_xq++]); | |
752 | sc->head_xq &= (XQLEN - 1); | |
753 | } | |
754 | ||
755 | sc->tbd[sc->head_tdesc].length = 0; | |
756 | sc->head_tdesc = (sc->head_tdesc + 1) & 0x7f; | |
757 | } | |
758 | ||
759 | start_xmit_frames(sc); | |
760 | } | |
761 | ||
762 | /* | |
763 | * DON'T use free_sent_buffers to drop the queue! | |
764 | */ | |
765 | static void | |
766 | alloc_rx_buffers(struct sbsh_softc *sc) | |
767 | { | |
768 | unsigned cur_rbd = sc->regs->LRDR & 0x7f; | |
769 | struct mbuf *m; | |
770 | ||
771 | while (sc->tail_rq != ((sc->head_rq - 1) & (RQLEN - 1))) { | |
772 | MGETHDR(m, M_DONTWAIT, MT_DATA); | |
773 | if (!m) { | |
774 | if_printf (&sc->arpcom.ac_if, | |
775 | "unable to get mbuf.\n"); | |
776 | return; | |
777 | } | |
778 | ||
779 | if (SBNI16_MAX_FRAME > MHLEN) { | |
780 | MCLGET(m, M_DONTWAIT); | |
781 | if (!(m->m_flags & M_EXT)) { | |
782 | m_freem(m); | |
783 | if_printf (&sc->arpcom.ac_if, | |
784 | "unable to get mbuf cluster.\n"); | |
785 | return; | |
786 | } | |
787 | m->m_pkthdr.len = m->m_len = MCLBYTES; | |
788 | } | |
789 | ||
790 | m_adj(m, 2); /* align ip on longword boundaries */ | |
791 | ||
792 | sc->rq[sc->tail_rq++] = m; | |
793 | sc->tail_rq &= (RQLEN - 1); | |
794 | ||
795 | sc->rbd[cur_rbd].address = vtophys(mtod(m, vm_offset_t)); | |
796 | sc->rbd[cur_rbd].length = 0; | |
797 | sc->regs->LRDR = cur_rbd = (cur_rbd + 1) & 0x7f; | |
798 | } | |
799 | } | |
800 | ||
801 | static void | |
802 | indicate_frames(struct sbsh_softc *sc) | |
803 | { | |
804 | struct ether_header *eh; | |
805 | unsigned cur_rbd = sc->regs->CRDR & 0x7f; | |
806 | ||
807 | while (sc->head_rdesc != cur_rbd) { | |
808 | struct mbuf *m = sc->rq[sc->head_rq++]; | |
809 | sc->head_rq &= (RQLEN - 1); | |
810 | ||
811 | m->m_pkthdr.len = m->m_len = | |
812 | sc->rbd[sc->head_rdesc].length & 0x7ff; | |
813 | m->m_pkthdr.rcvif = &sc->arpcom.ac_if; | |
814 | ||
815 | eh = mtod(m, struct ether_header *); | |
816 | m_adj(m, sizeof(struct ether_header)); | |
817 | ether_input(&sc->arpcom.ac_if, eh, m); | |
818 | ++sc->in_stats.rcvd_pkts; | |
819 | ++sc->arpcom.ac_if.if_ipackets; | |
820 | ||
821 | sc->head_rdesc = (sc->head_rdesc + 1) & 0x7f; | |
822 | } | |
823 | } | |
824 | ||
825 | static void | |
826 | drop_queues(struct sbsh_softc *sc) | |
827 | { | |
828 | while (sc->head_rq != sc->tail_rq) { | |
829 | m_freem(sc->rq[sc->head_rq++]); | |
830 | sc->head_rq &= (RQLEN - 1); | |
831 | } | |
832 | ||
833 | while (sc->head_xq != sc->tail_xq) { | |
834 | m_freem(sc->xq[sc->head_xq++]); | |
835 | sc->head_xq &= (XQLEN - 1); | |
836 | } | |
837 | } | |
838 | ||
839 | /* -------------------------------------------------------------------------- */ | |
840 | ||
841 | static void | |
842 | activate(struct sbsh_softc *sc) | |
843 | { | |
844 | struct timeval tv; | |
845 | ||
846 | sc->regs->SR = 0xff; /* clear it! */ | |
847 | sc->regs->CTDR = sc->regs->LTDR = sc->regs->CRDR = sc->regs->LRDR = 0; | |
848 | ||
849 | sc->head_tdesc = sc->head_rdesc = 0; | |
850 | alloc_rx_buffers(sc); | |
851 | ||
852 | sc->regs->CRB &= ~RXDE; | |
853 | sc->regs->IMR = EXT | RXS | TXS | CRC | OFL | UFL; | |
854 | sc->regs->CR |= TXEN | RXEN; | |
855 | ||
856 | sc->state = ACTIVE; | |
857 | ++sc->in_stats.attempts; | |
858 | microtime(&tv); | |
859 | sc->in_stats.last_time = tv.tv_sec; | |
860 | start_xmit_frames(sc); | |
861 | } | |
862 | ||
863 | static void | |
864 | deactivate(struct sbsh_softc *sc) | |
865 | { | |
866 | sc->regs->CR &= ~(RXEN | TXEN); | |
867 | sc->regs->CRB |= RXDE; | |
868 | sc->regs->IMR = EXT; | |
869 | sc->regs->CTDR = sc->regs->LTDR; | |
870 | sc->regs->CRDR = sc->regs->LRDR; | |
871 | sc->state = ACTIVATION; | |
872 | ||
873 | drop_queues(sc); | |
874 | } | |
875 | ||
876 | /* -------------------------------------------------------------------------- */ | |
877 | ||
878 | static void | |
879 | cx28975_interrupt(struct sbsh_softc *sc) | |
880 | { | |
881 | volatile struct cx28975_cmdarea *p = sc->cmdp; | |
882 | u_int8_t t; | |
883 | ||
884 | if (p->intr_host != 0xfe) | |
885 | return; | |
886 | ||
887 | if (p->out_ack & 0x80) { | |
888 | if (*((volatile u_int8_t *)p + 0x3c7) & 2) { | |
889 | if (sc->state != ACTIVE | |
890 | && (*((volatile u_int8_t *)p + 0x3c0) & 0xc0) == 0x40) { | |
891 | activate(sc); | |
892 | if_printf(&sc->arpcom.ac_if, "connected to peer\n"); | |
893 | } else if (sc->state == ACTIVE | |
894 | && (*((volatile u_int8_t *)p + 0x3c0) & 0xc0) != 0x40) { | |
895 | deactivate(sc); | |
896 | if_printf(&sc->arpcom.ac_if, "carrier lost\n"); | |
897 | } | |
898 | } | |
899 | ||
900 | p->intr_host = 0; | |
901 | t = p->intr_host; | |
902 | p->out_ack = 0; | |
903 | } else { | |
904 | wakeup(sc); | |
905 | ||
906 | p->intr_host = 0; | |
907 | t = p->intr_host; | |
908 | } | |
909 | } | |
910 | ||
911 | /* -------------------------------------------------------------------------- */ | |
912 | ||
913 | static int | |
914 | start_cx28975(struct sbsh_softc *sc, struct cx28975_cfg cfg) | |
915 | { | |
916 | static char thresh[] = { +8, -4, -16, -40 }; | |
917 | ||
918 | volatile struct cx28975_cmdarea *p = sc->cmdp; | |
919 | u_int8_t t, parm[12]; | |
920 | ||
921 | p->intr_host = 0; | |
922 | t = p->intr_host; | |
923 | ||
924 | /* reset chip set */ | |
925 | sc->regs->IMR = EXT; | |
926 | sc->regs->CR = 0; | |
927 | sc->regs->SR = 0xff; | |
928 | DELAY(2); | |
929 | sc->regs->CR = XRST; | |
930 | if (cfg.crc16) | |
931 | sc->regs->CR |= CMOD; | |
932 | if (cfg.fill_7e) | |
933 | sc->regs->CR |= FMOD; | |
934 | if (cfg.inv) | |
935 | sc->regs->CR |= PMOD; | |
936 | ||
937 | sc->regs->CRB |= RODD | RXDE; | |
938 | if (cfg.rburst) | |
939 | sc->regs->CRB |= RDBE; | |
940 | if (cfg.wburst) | |
941 | sc->regs->CRB |= WTBE; | |
942 | ||
943 | tsleep(sc, PWAIT, "sbsh", 0); | |
944 | if ((p->out_ack & 0x1f) != _ACK_BOOT_WAKE_UP) | |
945 | return (-1); | |
946 | ||
947 | if (download_firmware(sc, cfg.firmw_image, cfg.firmw_len)) | |
948 | return (-1); | |
949 | ||
950 | tsleep(sc, PWAIT, "sbsh", 0); | |
951 | if ((p->out_ack & 0x1f) != _ACK_OPER_WAKE_UP) | |
952 | return (-1); | |
953 | ||
954 | t = cfg.master ? 1 : 9; | |
955 | if (issue_cx28975_cmd(sc, _DSL_SYSTEM_ENABLE, &t, 1)) | |
956 | return (-1); | |
957 | ||
958 | t = 0x63; | |
959 | if (issue_cx28975_cmd(sc, _DSL_SYSTEM_CONFIG, &t, 1)) | |
960 | return (-1); | |
961 | ||
962 | *(u_int16_t *)parm = cfg.lrate >> 3; | |
963 | parm[2] = parm[3] = parm[0]; | |
964 | parm[5] = cfg.lrate & 7; | |
965 | parm[4] = parm[7] = 1; | |
966 | parm[6] = 0; | |
967 | if (issue_cx28975_cmd(sc, _DSL_MULTI_RATE_CONFIG, parm, 8)) | |
968 | return (-1); | |
969 | ||
970 | parm[0] = 0x02 | (cfg.mod << 4); | |
971 | parm[1] = 0; | |
972 | if (issue_cx28975_cmd(sc, _DSL_TRAINING_MODE, parm, 2)) | |
973 | return (-1); | |
974 | ||
975 | bzero(parm, 12); | |
976 | parm[0] = 0x04; /* pre-activation: G.hs */ | |
977 | parm[4] = 0x04; /* no remote configuration */ | |
978 | parm[7] = 0x01; /* annex A (default) */ | |
979 | parm[8] = 0xff; /* i-bit mask (all bits) */ | |
980 | if (issue_cx28975_cmd(sc, _DSL_PREACTIVATION_CFG, parm, 12)) | |
981 | return (-1); | |
982 | ||
983 | parm[0] = 0x03; /* dying gasp time - 3 frames */ | |
984 | parm[1] = thresh[cfg.mod]; | |
985 | parm[2] = 0xff; /* attenuation */ | |
986 | parm[3] = 0x04; /* line probe NMR (+2 dB) */ | |
987 | parm[4] = 0x00; /* reserved */ | |
988 | parm[5] = 0x00; | |
989 | if (issue_cx28975_cmd(sc, _DSL_THRESHOLDS, parm, 6)) | |
990 | return (-1); | |
991 | ||
992 | t = cfg.master ? 0x23 : 0x21; | |
993 | if (issue_cx28975_cmd(sc, _DSL_FR_PCM_CONFIG, &t, 1)) | |
994 | return (-1); | |
995 | ||
996 | t = 0x02; | |
997 | if (issue_cx28975_cmd(sc, _DSL_INTR_HOST_MASK, &t, 1)) | |
998 | return (-1); | |
999 | ||
1000 | sc->state = DOWN; | |
1001 | return (0); | |
1002 | } | |
1003 | ||
1004 | static int | |
1005 | download_firmware(struct sbsh_softc *sc, u_int8_t *img, u_int32_t img_len) | |
1006 | { | |
1007 | u_int32_t t; | |
1008 | int i; | |
1009 | u_int8_t cksum = 0; | |
1010 | ||
1011 | for (i = 0; i < img_len; ++i) | |
1012 | cksum += img[i]; | |
1013 | ||
1014 | t = img_len; | |
1015 | if (issue_cx28975_cmd(sc, _DSL_DOWNLOAD_START, (u_int8_t *) &t, 4)) | |
1016 | return (-1); | |
1017 | ||
1018 | for (i = 0; img_len >= 75; i += 75, img_len -= 75) { | |
1019 | if (issue_cx28975_cmd(sc, _DSL_DOWNLOAD_DATA, img + i, 75)) | |
1020 | return (-1); | |
1021 | } | |
1022 | ||
1023 | if (img_len | |
1024 | && issue_cx28975_cmd(sc, _DSL_DOWNLOAD_DATA, img + i, img_len)) | |
1025 | return (-1); | |
1026 | ||
1027 | t = (cksum ^ 0xff) + 1; | |
1028 | if (issue_cx28975_cmd(sc, _DSL_DOWNLOAD_END, (u_int8_t *) &t, 1)) | |
1029 | return (-1); | |
1030 | ||
1031 | return (0); | |
1032 | } | |
1033 | ||
1034 | static int | |
1035 | issue_cx28975_cmd(struct sbsh_softc *sc, u_int8_t cmd, | |
1036 | u_int8_t *data, u_int8_t size) | |
1037 | { | |
1038 | volatile struct cx28975_cmdarea *p = sc->cmdp; | |
1039 | u_int8_t *databuf = p->in_data; | |
1040 | int i; | |
1041 | ||
1042 | u_int8_t cksum = 0; | |
1043 | ||
1044 | p->in_dest = 0xf0; | |
1045 | p->in_opcode = cmd; | |
1046 | p->in_zero = 0; | |
1047 | p->in_length = --size; | |
1048 | p->in_csum = 0xf0 ^ cmd ^ size ^ 0xaa; | |
1049 | ||
1050 | for (i = 0; i <= size; ++i) { | |
1051 | cksum ^= *data; | |
1052 | *databuf++ = *data++; /* only 1 byte per cycle! */ | |
1053 | } | |
1054 | ||
1055 | p->in_datasum = cksum ^ 0xaa; | |
1056 | p->out_ack = _ACK_NOT_COMPLETE; | |
1057 | p->intr_8051 = 0xfe; | |
1058 | ||
1059 | if (tsleep(sc, PWAIT, "sbsh", hz << 3)) | |
1060 | return (-1); | |
1061 | ||
1062 | while (p->out_ack == _ACK_NOT_COMPLETE) | |
1063 | ; /* FIXME ! */ | |
1064 | ||
1065 | if ((p->out_ack & 0x1f) == _ACK_PASS) { | |
1066 | p->out_ack = 0; | |
1067 | return (0); | |
1068 | } else { | |
1069 | p->out_ack = 0; | |
1070 | return (-1); | |
1071 | } | |
1072 | } |