]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/patm/if_patm_ioctl.c
MFV r298691:
[FreeBSD/FreeBSD.git] / sys / dev / patm / if_patm_ioctl.c
1 /*-
2  * Copyright (c) 2003
3  *      Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * Author: Hartmut Brandt <harti@freebsd.org>
28  *
29  * Driver for IDT77252 based cards like ProSum's.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include "opt_inet.h"
36 #include "opt_natm.h"
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
42 #include <sys/kernel.h>
43 #include <sys/bus.h>
44 #include <sys/errno.h>
45 #include <sys/conf.h>
46 #include <sys/module.h>
47 #include <sys/lock.h>
48 #include <sys/mutex.h>
49 #include <sys/sysctl.h>
50 #include <sys/queue.h>
51 #include <sys/condvar.h>
52 #include <vm/uma.h>
53
54 #include <sys/sockio.h>
55 #include <sys/mbuf.h>
56 #include <sys/socket.h>
57
58 #include <net/if.h>
59 #include <net/if_var.h>
60 #include <net/if_media.h>
61 #include <net/if_atm.h>
62 #include <net/route.h>
63 #include <netinet/in.h>
64 #include <netinet/if_atm.h>
65
66 #include <machine/bus.h>
67 #include <machine/resource.h>
68 #include <sys/bus.h>
69 #include <sys/rman.h>
70 #include <sys/mbpool.h>
71
72 #include <dev/utopia/utopia.h>
73 #include <dev/patm/idt77252reg.h>
74 #include <dev/patm/if_patmvar.h>
75
76 /*
77  * Open the VCC with the given parameters
78  */
79 static int
80 patm_open_vcc(struct patm_softc *sc, struct atmio_openvcc *arg)
81 {
82         u_int cid;
83         struct patm_vcc *vcc;
84         int error = 0;
85
86         patm_debug(sc, VCC, "Open VCC: %u.%u flags=%#x", arg->param.vpi,
87             arg->param.vci, arg->param.flags);
88
89         if (!LEGAL_VPI(sc, arg->param.vpi) || !LEGAL_VCI(sc, arg->param.vci))
90                 return (EINVAL);
91         if (arg->param.vci == 0 && (arg->param.vpi != 0 ||
92             !(arg->param.flags & ATMIO_FLAG_NOTX) ||
93             arg->param.aal != ATMIO_AAL_RAW))
94                 return (EINVAL);
95         cid = PATM_CID(sc, arg->param.vpi, arg->param.vci);
96
97         if ((arg->param.flags & ATMIO_FLAG_NOTX) &&
98             (arg->param.flags & ATMIO_FLAG_NORX))
99                 return (EINVAL);
100
101         if ((arg->param.traffic == ATMIO_TRAFFIC_ABR) &&
102             (arg->param.flags & (ATMIO_FLAG_NOTX | ATMIO_FLAG_NORX)))
103                 return (EINVAL);
104
105         /* allocate vcc */
106         vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO);
107         if (vcc == NULL)
108                 return (ENOMEM);
109
110         mtx_lock(&sc->mtx);
111         if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
112                 /* stopped while we have analyzed the arguments */
113                 error = EIO;
114                 goto done;
115         }
116         if (sc->vccs[cid] != NULL) {
117                 /* ups, already open */
118                 error = EBUSY;
119                 goto done;
120         }
121
122         /* check some parameters */
123         vcc->cid = cid;
124         vcc->vcc = arg->param;
125         vcc->vflags = 0;
126         vcc->rxhand = arg->rxhand;
127         switch (vcc->vcc.aal) {
128
129           case ATMIO_AAL_0:
130           case ATMIO_AAL_34:
131           case ATMIO_AAL_5:
132                 break;
133
134           case ATMIO_AAL_RAW:
135                 if (arg->param.vci == 0 &&
136                     !(arg->param.flags & ATMIO_FLAG_NOTX)) {
137                         error = EINVAL;
138                         goto done;
139                 }
140                 break;
141
142           default:
143                 error = EINVAL;
144                 goto done;
145         }
146         switch (vcc->vcc.traffic) {
147
148           case ATMIO_TRAFFIC_VBR:
149           case ATMIO_TRAFFIC_UBR:
150           case ATMIO_TRAFFIC_CBR:
151           case ATMIO_TRAFFIC_ABR:
152                 break;
153
154           default:
155                 error = EINVAL;
156                 goto done;
157         }
158
159         /* initialize */
160         vcc->chain = NULL;
161         vcc->last = NULL;
162         vcc->ibytes = vcc->ipackets = 0;
163         vcc->obytes = vcc->opackets = 0;
164
165         /* ask the TX and RX sides */
166         patm_debug(sc, VCC, "Open VCC: asking Rx/Tx");
167         if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX) &&
168              (error = patm_tx_vcc_can_open(sc, vcc)) != 0)
169                 goto done;
170         if (!(vcc->vcc.flags & ATMIO_FLAG_NORX) &&
171              (error = patm_rx_vcc_can_open(sc, vcc)) != 0)
172                 goto done;
173
174         /* ok - go ahead */
175         sc->vccs[cid] = vcc;
176         patm_load_vc(sc, vcc, 0);
177
178         /* don't free below */
179         vcc = NULL;
180         sc->vccs_open++;
181
182         /* done */
183   done:
184         mtx_unlock(&sc->mtx);
185         if (vcc != NULL)
186                 uma_zfree(sc->vcc_zone, vcc);
187         return (error);
188 }
189
190 void
191 patm_load_vc(struct patm_softc *sc, struct patm_vcc *vcc, int reload)
192 {
193
194         patm_debug(sc, VCC, "Open VCC: opening");
195         if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX))
196                 patm_tx_vcc_open(sc, vcc);
197         if (!(vcc->vcc.flags & ATMIO_FLAG_NORX))
198                 patm_rx_vcc_open(sc, vcc);
199
200         if (!reload) {
201                 /* inform management about non-NG and NG-PVCs */
202                 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
203                      (vcc->vcc.flags & ATMIO_FLAG_PVC))
204                         ATMEV_SEND_VCC_CHANGED(IFP2IFATM(sc->ifp), vcc->vcc.vpi,
205                             vcc->vcc.vci, 1);
206         }
207
208         patm_debug(sc, VCC, "Open VCC: now open");
209 }
210
211 /*
212  * Try to close the given VCC
213  */
214 static int
215 patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg)
216 {
217         u_int cid;
218         struct patm_vcc *vcc;
219         int error = 0;
220
221         patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci);
222
223         if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci))
224                 return (EINVAL);
225         cid = PATM_CID(sc, arg->vpi, arg->vci);
226
227         mtx_lock(&sc->mtx);
228         if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
229                 /* stopped while we have analyzed the arguments */
230                 error = EIO;
231                 goto done;
232         }
233
234         vcc = sc->vccs[cid];
235         if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) {
236                 error = ENOENT;
237                 goto done;
238         }
239
240         if (vcc->vflags & PATM_VCC_TX_OPEN)
241                 patm_tx_vcc_close(sc, vcc);
242         if (vcc->vflags & PATM_VCC_RX_OPEN)
243                 patm_rx_vcc_close(sc, vcc);
244
245         if (vcc->vcc.flags & ATMIO_FLAG_ASYNC)
246                 goto done;
247
248         while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) {
249                 cv_wait(&sc->vcc_cv, &sc->mtx);
250                 if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
251                         /* ups, has been stopped */
252                         error = EIO;
253                         goto done;
254                 }
255         }
256
257         if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX))
258                 patm_tx_vcc_closed(sc, vcc);
259         if (!(vcc->vcc.flags & ATMIO_FLAG_NORX))
260                 patm_rx_vcc_closed(sc, vcc);
261
262         patm_vcc_closed(sc, vcc);
263
264   done:
265         mtx_unlock(&sc->mtx);
266
267         return (error);
268 }
269
270 /*
271  * VCC has been finally closed.
272  */
273 void
274 patm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc)
275 {
276
277         /* inform management about non-NG and NG-PVCs */
278         if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
279             (vcc->vcc.flags & ATMIO_FLAG_PVC))
280                 ATMEV_SEND_VCC_CHANGED(IFP2IFATM(sc->ifp), vcc->vcc.vpi,
281                     vcc->vcc.vci, 0);
282
283         sc->vccs_open--;
284         sc->vccs[vcc->cid] = NULL;
285         uma_zfree(sc->vcc_zone, vcc);
286 }
287
288 int
289 patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
290 {
291         struct ifreq *ifr = (struct ifreq *)data;
292         struct ifaddr *ifa = (struct ifaddr *)data;
293         struct patm_softc *sc = ifp->if_softc;
294         int error = 0;
295         uint32_t cfg;
296         struct atmio_vcctable *vtab;
297
298         switch (cmd) {
299
300           case SIOCSIFADDR:
301                 mtx_lock(&sc->mtx);
302                 ifp->if_flags |= IFF_UP;
303                 if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
304                         patm_initialize(sc);
305                 switch (ifa->ifa_addr->sa_family) {
306
307 #ifdef INET
308                   case AF_INET:
309                   case AF_INET6:
310                         ifa->ifa_rtrequest = atm_rtrequest;
311                         break;
312 #endif
313                   default:
314                         break;
315                 }
316                 mtx_unlock(&sc->mtx);
317                 break;
318
319           case SIOCSIFFLAGS:
320                 mtx_lock(&sc->mtx);
321                 if (ifp->if_flags & IFF_UP) {
322                         if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
323                                 patm_initialize(sc);
324                         }
325                 } else {
326                         if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
327                                 patm_stop(sc);
328                         }
329                 }
330                 mtx_unlock(&sc->mtx);
331                 break;
332
333           case SIOCGIFMEDIA:
334           case SIOCSIFMEDIA:
335                 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
336
337                 /*
338                  * We need to toggle unassigned/idle cells ourself because
339                  * the 77252 generates null cells for spacing. When switching
340                  * null cells of it gets the timing wrong.
341                  */
342                 mtx_lock(&sc->mtx);
343                 if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
344                         if (sc->utopia.state & UTP_ST_UNASS) {
345                                 if (!(sc->flags & PATM_UNASS)) {
346                                         cfg = patm_nor_read(sc, IDT_NOR_CFG);
347                                         cfg &= ~IDT_CFG_IDLECLP;
348                                         patm_nor_write(sc, IDT_NOR_CFG, cfg);
349                                         sc->flags |= PATM_UNASS;
350                                 }
351                         } else {
352                                 if (sc->flags & PATM_UNASS) {
353                                         cfg = patm_nor_read(sc, IDT_NOR_CFG);
354                                         cfg |= IDT_CFG_IDLECLP;
355                                         patm_nor_write(sc, IDT_NOR_CFG, cfg);
356                                         sc->flags &= ~PATM_UNASS;
357                                 }
358                         }
359                 } else {
360                         if (sc->utopia.state & UTP_ST_UNASS)
361                                 sc->flags |= PATM_UNASS;
362                         else
363                                 sc->flags &= ~PATM_UNASS;
364                 }
365                 mtx_unlock(&sc->mtx);
366                 break;
367
368           case SIOCSIFMTU:
369                 /*
370                  * Set the interface MTU.
371                  */
372                 if (ifr->ifr_mtu > ATMMTU)
373                         error = EINVAL;
374                 else
375                         ifp->if_mtu = ifr->ifr_mtu;
376                 break;
377
378           case SIOCATMOPENVCC:          /* kernel internal use */
379                 error = patm_open_vcc(sc, (struct atmio_openvcc *)data);
380                 break;
381
382           case SIOCATMCLOSEVCC:         /* kernel internal use */
383                 error = patm_close_vcc(sc, (struct atmio_closevcc *)data);
384                 break;
385
386           case SIOCATMGVCCS:    /* external use */
387                 /* return vcc table */
388                 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
389                     sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1);
390                 error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) +
391                     vtab->count * sizeof(vtab->vccs[0]));
392                 free(vtab, M_DEVBUF);
393                 break;
394
395           case SIOCATMGETVCCS:  /* netgraph internal use */
396                 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
397                     sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0);
398                 if (vtab == NULL) {
399                         error = ENOMEM;
400                         break;
401                 }
402                 *(void **)data = vtab;
403                 break;
404
405           default:
406                 patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data);
407                 error = EINVAL;
408                 break;
409         }
410
411         return (error);
412 }