1 /*-
2 * Copyright (c) 2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Hartmut Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD: releng/9.0/sys/dev/utopia/utopia.c 174318 2007-12-05 19:32:59Z philip $");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/unistd.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/proc.h>
39 #include <sys/bus.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/sysctl.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/socket.h>
46
47 #include <net/if.h>
48 #include <net/if_var.h>
49 #include <net/if_media.h>
50 #include <net/if_atm.h>
51
52 #include <dev/utopia/suni.h>
53 #include <dev/utopia/idtphy.h>
54 #include <dev/utopia/utopia.h>
55 #include <dev/utopia/utopia_priv.h>
56
57 /* known chips */
58 extern const struct utopia_chip utopia_chip_idt77155;
59 extern const struct utopia_chip utopia_chip_idt77105;
60 extern const struct utopia_chip utopia_chip_lite;
61 extern const struct utopia_chip utopia_chip_ultra;
62 extern const struct utopia_chip utopia_chip_622;
63
64 /*
65 * Global list of all registered interfaces
66 */
67 static struct mtx utopia_list_mtx;
68 static LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list);
69
70 #define UTP_RLOCK_LIST() mtx_lock(&utopia_list_mtx)
71 #define UTP_RUNLOCK_LIST() mtx_unlock(&utopia_list_mtx)
72 #define UTP_WLOCK_LIST() mtx_lock(&utopia_list_mtx)
73 #define UTP_WUNLOCK_LIST() mtx_unlock(&utopia_list_mtx)
74
75 #define UTP_LOCK(UTP) mtx_lock((UTP)->lock)
76 #define UTP_UNLOCK(UTP) mtx_unlock((UTP)->lock)
77 #define UTP_LOCK_ASSERT(UTP) mtx_assert((UTP)->lock, MA_OWNED)
78
79 static struct proc *utopia_kproc;
80
81 static void utopia_dump(struct utopia *) __unused;
82
83 /*
84 * Read a multi-register value.
85 */
86 uint32_t
87 utopia_update(struct utopia *utp, u_int reg, u_int nreg, uint32_t mask)
88 {
89 int err;
90 u_int n;
91 uint8_t regs[4];
92 uint32_t val;
93
94 n = nreg;
95 if ((err = UTP_READREGS(utp, reg, regs, &n)) != 0) {
96 #ifdef DIAGNOSTIC
97 printf("%s: register read error %s(%u,%u): %d\n", __func__,
98 utp->chip->name, reg, nreg, err);
99 #endif
100 return (0);
101 }
102 if (n < nreg) {
103 #ifdef DIAGNOSTIC
104 printf("%s: got only %u regs %s(%u,%u): %d\n", __func__, n,
105 utp->chip->name, reg, nreg, err);
106 #endif
107 return (0);
108 }
109 val = 0;
110 for (n = nreg; n > 0; n--) {
111 val <<= 8;
112 val |= regs[n - 1];
113 }
114 return (val & mask);
115 }
116
117 /*
118 * Debugging - dump all registers.
119 */
120 static void
121 utopia_dump(struct utopia *utp)
122 {
123 uint8_t regs[256];
124 u_int n = 256, i;
125 int err;
126
127 if ((err = UTP_READREGS(utp, 0, regs, &n)) != 0) {
128 printf("UTOPIA reg read error %d\n", err);
129 return;
130 }
131 for (i = 0; i < n; i++) {
132 if (i % 16 == 0)
133 printf("%02x:", i);
134 if (i % 16 == 8)
135 printf(" ");
136 printf(" %02x", regs[i]);
137 if (i % 16 == 15)
138 printf("\n");
139 }
140 if (i % 16 != 0)
141 printf("\n");
142 }
143
144 /*
145 * Update the carrier status
146 */
147 void
148 utopia_check_carrier(struct utopia *utp, u_int carr_ok)
149 {
150 int old;
151
152 old = utp->carrier;
153 if (carr_ok) {
154 /* carrier */
155 utp->carrier = UTP_CARR_OK;
156 if (old != UTP_CARR_OK) {
157 if_printf(utp->ifatm->ifp, "carrier detected\n");
158 ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 1);
159 }
160 } else {
161 /* no carrier */
162 utp->carrier = UTP_CARR_LOST;
163 if (old == UTP_CARR_OK) {
164 if_printf(utp->ifatm->ifp, "carrier lost\n");
165 ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 0);
166 }
167 }
168 }
169
170 static int
171 unknown_inval(struct utopia *utp, int what __unused)
172 {
173
174 return (EINVAL);
175 }
176
177 static int
178 unknown_reset(struct utopia *utp __unused)
179 {
180 return (EIO);
181 }
182
183 static int
184 unknown_update_carrier(struct utopia *utp)
185 {
186 utp->carrier = UTP_CARR_UNKNOWN;
187 return (0);
188 }
189
190 static int
191 unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused)
192 {
193 return (EINVAL);
194 }
195
196 static void
197 unknown_intr(struct utopia *utp __unused)
198 {
199 }
200
201 static void
202 unknown_update_stats(struct utopia *utp __unused)
203 {
204 }
205
206 static const struct utopia_chip utopia_chip_unknown = {
207 UTP_TYPE_UNKNOWN,
208 "unknown",
209 0,
210 unknown_reset,
211 unknown_inval,
212 unknown_inval,
213 unknown_inval,
214 unknown_update_carrier,
215 unknown_set_loopback,
216 unknown_intr,
217 unknown_update_stats,
218 };
219
220 /*
221 * Callbacks for the ifmedia infrastructure.
222 */
223 static int
224 utopia_media_change(struct ifnet *ifp)
225 {
226 struct ifatm *ifatm = IFP2IFATM(ifp);
227 struct utopia *utp = ifatm->phy;
228 int error = 0;
229
230 UTP_LOCK(utp);
231 if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
232 if (utp->media->ifm_media & IFM_ATM_SDH) {
233 if (!(utp->state & UTP_ST_SDH))
234 error = utopia_set_sdh(utp, 1);
235 } else {
236 if (utp->state & UTP_ST_SDH)
237 error = utopia_set_sdh(utp, 0);
238 }
239 if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) {
240 if (!(utp->state & UTP_ST_UNASS))
241 error = utopia_set_unass(utp, 1);
242 } else {
243 if (utp->state & UTP_ST_UNASS)
244 error = utopia_set_unass(utp, 0);
245 }
246 if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) {
247 if (!(utp->state & UTP_ST_NOSCRAMB))
248 error = utopia_set_noscramb(utp, 1);
249 } else {
250 if (utp->state & UTP_ST_NOSCRAMB)
251 error = utopia_set_noscramb(utp, 0);
252 }
253 } else
254 error = EIO;
255 UTP_UNLOCK(utp);
256 return (error);
257 }
258
259 /*
260 * Look at the carrier status.
261 */
262 static void
263 utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
264 {
265 struct utopia *utp = IFP2IFATM(ifp)->phy;
266
267 UTP_LOCK(utp);
268 if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
269 ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media;
270
271 switch (utp->carrier) {
272
273 case UTP_CARR_OK:
274 ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
275 break;
276
277 case UTP_CARR_LOST:
278 ifmr->ifm_status = IFM_AVALID;
279 break;
280
281 default:
282 ifmr->ifm_status = 0;
283 break;
284 }
285 if (utp->state & UTP_ST_SDH) {
286 ifmr->ifm_active |= IFM_ATM_SDH;
287 ifmr->ifm_current |= IFM_ATM_SDH;
288 }
289 if (utp->state & UTP_ST_UNASS) {
290 ifmr->ifm_active |= IFM_ATM_UNASSIGNED;
291 ifmr->ifm_current |= IFM_ATM_UNASSIGNED;
292 }
293 if (utp->state & UTP_ST_NOSCRAMB) {
294 ifmr->ifm_active |= IFM_ATM_NOSCRAMB;
295 ifmr->ifm_current |= IFM_ATM_NOSCRAMB;
296 }
297 } else {
298 ifmr->ifm_active = 0;
299 ifmr->ifm_status = 0;
300 }
301 UTP_UNLOCK(utp);
302 }
303
304 /*
305 * Initialize media from the mib
306 */
307 void
308 utopia_init_media(struct utopia *utp)
309 {
310
311 ifmedia_removeall(utp->media);
312 ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL);
313 ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media);
314 }
315
316 /*
317 * Reset all media
318 */
319 void
320 utopia_reset_media(struct utopia *utp)
321 {
322
323 ifmedia_removeall(utp->media);
324 }
325
326 /*
327 * This is called by the driver as soon as the SUNI registers are accessible.
328 * This may be either in the attach routine or the init routine of the driver.
329 */
330 int
331 utopia_start(struct utopia *utp)
332 {
333 uint8_t reg;
334 int err;
335 u_int n = 1;
336
337 /*
338 * Try to find out what chip we have
339 */
340 if ((err = UTP_READREGS(utp, SUNI_REGO_MRESET, ®, &n)) != 0)
341 return (err);
342
343 switch (reg & SUNI_REGM_MRESET_TYPE) {
344
345 case SUNI_REGM_MRESET_TYPE_622:
346 utp->chip = &utopia_chip_622;
347 break;
348
349 case SUNI_REGM_MRESET_TYPE_LITE:
350 /* this may be either a SUNI LITE or a IDT77155 *
351 * Read register 0x70. The SUNI doesn't have it */
352 n = 1;
353 if ((err = UTP_READREGS(utp, IDTPHY_REGO_RBER, ®, &n)) != 0)
354 return (err);
355 if ((reg & ~IDTPHY_REGM_RBER_RESV) ==
356 (IDTPHY_REGM_RBER_FAIL | IDTPHY_REGM_RBER_WARN))
357 utp->chip = &utopia_chip_idt77155;
358 else
359 utp->chip = &utopia_chip_lite;
360 break;
361
362 case SUNI_REGM_MRESET_TYPE_ULTRA:
363 utp->chip = &utopia_chip_ultra;
364 break;
365
366 default:
367 if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI))
368 utp->chip = &utopia_chip_idt77105;
369 else {
370 if_printf(utp->ifatm->ifp,
371 "unknown ATM-PHY chip %#x\n", reg);
372 utp->chip = &utopia_chip_unknown;
373 }
374 break;
375 }
376 utp->state |= UTP_ST_ACTIVE;
377 return (0);
378 }
379
380 /*
381 * Stop the chip
382 */
383 void
384 utopia_stop(struct utopia *utp)
385 {
386 utp->state &= ~UTP_ST_ACTIVE;
387 }
388
389 /*
390 * Handle the sysctls
391 */
392 static int
393 utopia_sysctl_regs(SYSCTL_HANDLER_ARGS)
394 {
395 struct utopia *utp = (struct utopia *)arg1;
396 int error;
397 u_int n;
398 uint8_t *val;
399 uint8_t new[3];
400
401 if ((n = utp->chip->nregs) == 0)
402 return (EIO);
403 val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK);
404
405 UTP_LOCK(utp);
406 error = UTP_READREGS(utp, 0, val, &n);
407 UTP_UNLOCK(utp);
408
409 if (error) {
410 free(val, M_TEMP);
411 return (error);
412 }
413
414 error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n);
415 free(val, M_TEMP);
416 if (error != 0 || req->newptr == NULL)
417 return (error);
418
419 error = SYSCTL_IN(req, new, sizeof(new));
420 if (error)
421 return (error);
422
423 UTP_LOCK(utp);
424 error = UTP_WRITEREG(utp, new[0], new[1], new[2]);
425 UTP_UNLOCK(utp);
426
427 return (error);
428 }
429
430 static int
431 utopia_sysctl_stats(SYSCTL_HANDLER_ARGS)
432 {
433 struct utopia *utp = (struct utopia *)arg1;
434 void *val;
435 int error;
436
437 val = malloc(sizeof(utp->stats), M_TEMP, M_WAITOK);
438
439 UTP_LOCK(utp);
440 bcopy(&utp->stats, val, sizeof(utp->stats));
441 if (req->newptr != NULL)
442 bzero((char *)&utp->stats + sizeof(utp->stats.version),
443 sizeof(utp->stats) - sizeof(utp->stats.version));
444 UTP_UNLOCK(utp);
445
446 error = SYSCTL_OUT(req, val, sizeof(utp->stats));
447 if (error && req->newptr != NULL)
448 bcopy(val, &utp->stats, sizeof(utp->stats));
449 free(val, M_TEMP);
450
451 /* ignore actual new value */
452
453 return (error);
454 }
455
456 /*
457 * Handle the loopback sysctl
458 */
459 static int
460 utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
461 {
462 struct utopia *utp = (struct utopia *)arg1;
463 int error;
464 u_int loopback;
465
466 error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
467 if (error != 0 || req->newptr == NULL)
468 return (error);
469
470 error = SYSCTL_IN(req, &loopback, sizeof(u_int));
471 if (error)
472 return (error);
473
474 UTP_LOCK(utp);
475 error = utopia_set_loopback(utp, loopback);
476 UTP_UNLOCK(utp);
477
478 return (error);
479 }
480
481 /*
482 * Handle the type sysctl
483 */
484 static int
485 utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
486 {
487 struct utopia *utp = (struct utopia *)arg1;
488
489 return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
490 }
491
492 /*
493 * Handle the name sysctl
494 */
495 static int
496 utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
497 {
498 struct utopia *utp = (struct utopia *)arg1;
499
500 return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
501 }
502
503 /*
504 * Initialize the state. This is called from the drivers attach
505 * function. The mutex must be already initialized.
506 */
507 int
508 utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
509 struct mtx *lock, struct sysctl_ctx_list *ctx,
510 struct sysctl_oid_list *children, const struct utopia_methods *m)
511 {
512
513 bzero(utp, sizeof(*utp));
514 utp->ifatm = ifatm;
515 utp->methods = m;
516 utp->media = media;
517 utp->lock = lock;
518 utp->chip = &utopia_chip_unknown;
519 utp->stats.version = 1;
520
521 ifmedia_init(media,
522 IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
523 utopia_media_change, utopia_media_status);
524
525 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
526 CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
527 "phy registers") == NULL)
528 return (-1);
529
530 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
531 CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
532 "phy loopback mode") == NULL)
533 return (-1);
534
535 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
536 CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
537 "phy type") == NULL)
538 return (-1);
539
540 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
541 CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
542 "phy name") == NULL)
543 return (-1);
544
545 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats",
546 CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S",
547 "phy statistics") == NULL)
548 return (-1);
549
550 if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_state",
551 CTLFLAG_RD, &utp->state, 0, "phy state") == NULL)
552 return (-1);
553
554 if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_carrier",
555 CTLFLAG_RD, &utp->carrier, 0, "phy carrier") == NULL)
556 return (-1);
557
558 UTP_WLOCK_LIST();
559 LIST_INSERT_HEAD(&utopia_list, utp, link);
560 UTP_WUNLOCK_LIST();
561
562 utp->state |= UTP_ST_ATTACHED;
563 return (0);
564 }
565
566 /*
567 * Detach. We set a flag here, wakeup the daemon and let him do it.
568 * Here we need the lock for synchronisation with the daemon.
569 */
570 void
571 utopia_detach(struct utopia *utp)
572 {
573
574 UTP_LOCK_ASSERT(utp);
575 if (utp->state & UTP_ST_ATTACHED) {
576 utp->state |= UTP_ST_DETACH;
577 while (utp->state & UTP_ST_DETACH) {
578 wakeup(&utopia_list);
579 msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
580 }
581 }
582 }
583
584 /*
585 * The carrier state kernel proc for those adapters that do not interrupt.
586 *
587 * We assume, that utopia_attach can safely add a new utopia while we are going
588 * through the list without disturbing us (we lock the list while getting
589 * the address of the first element, adding is always done at the head).
590 * Removing is entirely handled here.
591 */
592 static void
593 utopia_daemon(void *arg __unused)
594 {
595 struct utopia *utp, *next;
596
597 UTP_RLOCK_LIST();
598 while (utopia_kproc != NULL) {
599 utp = LIST_FIRST(&utopia_list);
600 UTP_RUNLOCK_LIST();
601
602 while (utp != NULL) {
603 mtx_lock(&Giant); /* XXX depend on MPSAFE */
604 UTP_LOCK(utp);
605 next = LIST_NEXT(utp, link);
606 if (utp->state & UTP_ST_DETACH) {
607 LIST_REMOVE(utp, link);
608 utp->state &= ~UTP_ST_DETACH;
609 wakeup_one(utp);
610 } else if (utp->state & UTP_ST_ACTIVE) {
611 if (utp->flags & UTP_FL_POLL_CARRIER)
612 utopia_update_carrier(utp);
613 utopia_update_stats(utp);
614 }
615 UTP_UNLOCK(utp);
616 mtx_unlock(&Giant); /* XXX depend on MPSAFE */
617 utp = next;
618 }
619
620 UTP_RLOCK_LIST();
621 msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz);
622 }
623 wakeup_one(&utopia_list);
624 UTP_RUNLOCK_LIST();
625 kproc_exit(0);
626 }
627
628 /*
629 * Module initialisation
630 */
631 static int
632 utopia_mod_init(module_t mod, int what, void *arg)
633 {
634 int err;
635 struct proc *kp;
636
637 switch (what) {
638
639 case MOD_LOAD:
640 mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
641 err = kproc_create(utopia_daemon, NULL, &utopia_kproc,
642 RFHIGHPID, 0, "utopia");
643 if (err != 0) {
644 printf("cannot created utopia thread %d\n", err);
645 return (err);
646 }
647 break;
648
649 case MOD_UNLOAD:
650 UTP_WLOCK_LIST();
651 if ((kp = utopia_kproc) != NULL) {
652 utopia_kproc = NULL;
653 wakeup_one(&utopia_list);
654 PROC_LOCK(kp);
655 UTP_WUNLOCK_LIST();
656 msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
657 PROC_UNLOCK(kp);
658 } else
659 UTP_WUNLOCK_LIST();
660 mtx_destroy(&utopia_list_mtx);
661 break;
662 default:
663 return (EOPNOTSUPP);
664 }
665 return (0);
666 }
667
668 static moduledata_t utopia_mod = {
669 "utopia",
670 utopia_mod_init,
671 0
672 };
673
674 DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
675 MODULE_VERSION(utopia, 1);
Cache object: 989a53bdf857ff1efebac4e2747c59fb
|