]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/utopia/utopia.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / utopia / utopia.c
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$");
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, &reg, &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, &reg, &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);