]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/utopia/utopia.c
This commit was generated by cvs2svn to compensate for changes in r155832,
[FreeBSD/FreeBSD.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         free(val, M_TEMP);
448
449         if (error && req->newptr != NULL)
450                 bcopy(val, &utp->stats, sizeof(utp->stats));
451
452         /* ignore actual new value */
453
454         return (error);
455 }
456
457 /*
458  * Handle the loopback sysctl
459  */
460 static int
461 utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
462 {
463         struct utopia *utp = (struct utopia *)arg1;
464         int error;
465         u_int loopback;
466
467         error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
468         if (error != 0 || req->newptr == NULL)
469                 return (error);
470
471         error = SYSCTL_IN(req, &loopback, sizeof(u_int));
472         if (error)
473                 return (error);
474
475         UTP_LOCK(utp);
476         error = utopia_set_loopback(utp, loopback);
477         UTP_UNLOCK(utp);
478
479         return (error);
480 }
481
482 /*
483  * Handle the type sysctl
484  */
485 static int
486 utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
487 {
488         struct utopia *utp = (struct utopia *)arg1;
489
490         return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
491 }
492
493 /*
494  * Handle the name sysctl
495  */
496 static int
497 utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
498 {
499         struct utopia *utp = (struct utopia *)arg1;
500
501         return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
502 }
503
504 /*
505  * Initialize the state. This is called from the drivers attach
506  * function. The mutex must be already initialized.
507  */
508 int
509 utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
510     struct mtx *lock, struct sysctl_ctx_list *ctx,
511     struct sysctl_oid_list *children, const struct utopia_methods *m)
512 {
513
514         bzero(utp, sizeof(*utp));
515         utp->ifatm = ifatm;
516         utp->methods = m;
517         utp->media = media;
518         utp->lock = lock;
519         utp->chip = &utopia_chip_unknown;
520         utp->stats.version = 1;
521
522         ifmedia_init(media,
523             IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
524             utopia_media_change, utopia_media_status);
525
526         if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
527             CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
528             "phy registers") == NULL)
529                 return (-1);
530
531         if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
532             CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
533             "phy loopback mode") == NULL)
534                 return (-1);
535
536         if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
537             CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
538             "phy type") == NULL)
539                 return (-1);
540
541         if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
542             CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
543             "phy name") == NULL)
544                 return (-1);
545
546         if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats",
547             CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S",
548             "phy statistics") == NULL)
549                 return (-1);
550
551         if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_state",
552             CTLFLAG_RD, &utp->state, 0, "phy state") == NULL)
553                 return (-1);
554
555         if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_carrier",
556             CTLFLAG_RD, &utp->carrier, 0, "phy carrier") == NULL)
557                 return (-1);
558
559         UTP_WLOCK_LIST();
560         LIST_INSERT_HEAD(&utopia_list, utp, link);
561         UTP_WUNLOCK_LIST();
562
563         utp->state |= UTP_ST_ATTACHED;
564         return (0);
565 }
566
567 /*
568  * Detach. We set a flag here, wakeup the daemon and let him do it.
569  * Here we need the lock for synchronisation with the daemon.
570  */
571 void
572 utopia_detach(struct utopia *utp)
573 {
574
575         UTP_LOCK_ASSERT(utp);
576         if (utp->state & UTP_ST_ATTACHED) {
577                 utp->state |= UTP_ST_DETACH;
578                 while (utp->state & UTP_ST_DETACH) {
579                         wakeup(&utopia_list);
580                         msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
581                 }
582         }
583 }
584
585 /*
586  * The carrier state kernel proc for those adapters that do not interrupt.
587  *
588  * We assume, that utopia_attach can safely add a new utopia while we are going
589  * through the list without disturbing us (we lock the list while getting
590  * the address of the first element, adding is always done at the head).
591  * Removing is entirely handled here.
592  */
593 static void
594 utopia_daemon(void *arg __unused)
595 {
596         struct utopia *utp, *next;
597
598         UTP_RLOCK_LIST();
599         while (utopia_kproc != NULL) {
600                 utp = LIST_FIRST(&utopia_list);
601                 UTP_RUNLOCK_LIST();
602
603                 while (utp != NULL) {
604                         mtx_lock(&Giant);       /* XXX depend on MPSAFE */
605                         UTP_LOCK(utp);
606                         next = LIST_NEXT(utp, link);
607                         if (utp->state & UTP_ST_DETACH) {
608                                 LIST_REMOVE(utp, link);
609                                 utp->state &= ~UTP_ST_DETACH;
610                                 wakeup_one(utp);
611                         } else if (utp->state & UTP_ST_ACTIVE) {
612                                 if (utp->flags & UTP_FL_POLL_CARRIER)
613                                         utopia_update_carrier(utp);
614                                 utopia_update_stats(utp);
615                         }
616                         UTP_UNLOCK(utp);
617                         mtx_unlock(&Giant);     /* XXX depend on MPSAFE */
618                         utp = next;
619                 }
620
621                 UTP_RLOCK_LIST();
622                 msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz);
623         }
624         wakeup_one(&utopia_list);
625         UTP_RUNLOCK_LIST();
626         kthread_exit(0);
627 }
628
629 /*
630  * Module initialisation
631  */
632 static int
633 utopia_mod_init(module_t mod, int what, void *arg)
634 {
635         int err;
636         struct proc *kp;
637
638         switch (what) {
639
640           case MOD_LOAD:
641                 mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
642                 err = kthread_create(utopia_daemon, NULL, &utopia_kproc,
643                     RFHIGHPID, 0, "utopia");
644                 if (err != 0) {
645                         printf("cannot created utopia thread %d\n", err);
646                         return (err);
647                 }
648                 break;
649
650           case MOD_UNLOAD:
651                 UTP_WLOCK_LIST();
652                 if ((kp = utopia_kproc) != NULL) {
653                         utopia_kproc = NULL;
654                         wakeup_one(&utopia_list);
655                         PROC_LOCK(kp);
656                         UTP_WUNLOCK_LIST();
657                         msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
658                         PROC_UNLOCK(kp);
659                 } else
660                         UTP_WUNLOCK_LIST();
661                 mtx_destroy(&utopia_list_mtx);
662                 break;
663           default:
664                 return (EOPNOTSUPP);
665         }
666         return (0);
667 }
668
669 static moduledata_t utopia_mod = {
670         "utopia",
671         utopia_mod_init,
672         0
673 };
674                 
675 DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
676 MODULE_VERSION(utopia, 1);