]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/dev/patm/if_patm_ioctl.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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_media.h>
60 #include <net/if_atm.h>
61 #include <net/route.h>
62 #include <netinet/in.h>
63 #include <netinet/if_atm.h>
64
65 #include <machine/bus.h>
66 #include <machine/resource.h>
67 #include <sys/bus.h>
68 #include <sys/rman.h>
69 #include <sys/mbpool.h>
70
71 #include <dev/utopia/utopia.h>
72 #include <dev/patm/idt77252reg.h>
73 #include <dev/patm/if_patmvar.h>
74
75 /*
76  * Open the VCC with the given parameters
77  */
78 static int
79 patm_open_vcc(struct patm_softc *sc, struct atmio_openvcc *arg)
80 {
81         u_int cid;
82         struct patm_vcc *vcc;
83         int error = 0;
84
85         patm_debug(sc, VCC, "Open VCC: %u.%u flags=%#x", arg->param.vpi,
86             arg->param.vci, arg->param.flags);
87
88         if (!LEGAL_VPI(sc, arg->param.vpi) || !LEGAL_VCI(sc, arg->param.vci))
89                 return (EINVAL);
90         if (arg->param.vci == 0 && (arg->param.vpi != 0 ||
91             !(arg->param.flags & ATMIO_FLAG_NOTX) ||
92             arg->param.aal != ATMIO_AAL_RAW))
93                 return (EINVAL);
94         cid = PATM_CID(sc, arg->param.vpi, arg->param.vci);
95
96         if ((arg->param.flags & ATMIO_FLAG_NOTX) &&
97             (arg->param.flags & ATMIO_FLAG_NORX))
98                 return (EINVAL);
99
100         if ((arg->param.traffic == ATMIO_TRAFFIC_ABR) &&
101             (arg->param.flags & (ATMIO_FLAG_NOTX | ATMIO_FLAG_NORX)))
102                 return (EINVAL);
103
104         /* allocate vcc */
105         vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO);
106         if (vcc == NULL)
107                 return (ENOMEM);
108
109         mtx_lock(&sc->mtx);
110         if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
111                 /* stopped while we have analyzed the arguments */
112                 error = EIO;
113                 goto done;
114         }
115         if (sc->vccs[cid] != NULL) {
116                 /* ups, already open */
117                 error = EBUSY;
118                 goto done;
119         }
120
121         /* check some parameters */
122         vcc->cid = cid;
123         vcc->vcc = arg->param;
124         vcc->vflags = 0;
125         vcc->rxhand = arg->rxhand;
126         switch (vcc->vcc.aal) {
127
128           case ATMIO_AAL_0:
129           case ATMIO_AAL_34:
130           case ATMIO_AAL_5:
131                 break;
132
133           case ATMIO_AAL_RAW:
134                 if (arg->param.vci == 0 &&
135                     !(arg->param.flags & ATMIO_FLAG_NOTX)) {
136                         error = EINVAL;
137                         goto done;
138                 }
139                 break;
140
141           default:
142                 error = EINVAL;
143                 goto done;
144         }
145         switch (vcc->vcc.traffic) {
146
147           case ATMIO_TRAFFIC_VBR:
148           case ATMIO_TRAFFIC_UBR:
149           case ATMIO_TRAFFIC_CBR:
150           case ATMIO_TRAFFIC_ABR:
151                 break;
152
153           default:
154                 error = EINVAL;
155                 goto done;
156         }
157
158         /* initialize */
159         vcc->chain = NULL;
160         vcc->last = NULL;
161         vcc->ibytes = vcc->ipackets = 0;
162         vcc->obytes = vcc->opackets = 0;
163
164         /* ask the TX and RX sides */
165         patm_debug(sc, VCC, "Open VCC: asking Rx/Tx");
166         if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX) &&
167              (error = patm_tx_vcc_can_open(sc, vcc)) != 0)
168                 goto done;
169         if (!(vcc->vcc.flags & ATMIO_FLAG_NORX) &&
170              (error = patm_rx_vcc_can_open(sc, vcc)) != 0)
171                 goto done;
172
173         /* ok - go ahead */
174         sc->vccs[cid] = vcc;
175         patm_load_vc(sc, vcc, 0);
176
177         /* don't free below */
178         vcc = NULL;
179         sc->vccs_open++;
180
181         /* done */
182   done:
183         mtx_unlock(&sc->mtx);
184         if (vcc != NULL)
185                 uma_zfree(sc->vcc_zone, vcc);
186         return (error);
187 }
188
189 void
190 patm_load_vc(struct patm_softc *sc, struct patm_vcc *vcc, int reload)
191 {
192
193         patm_debug(sc, VCC, "Open VCC: opening");
194         if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX))
195                 patm_tx_vcc_open(sc, vcc);
196         if (!(vcc->vcc.flags & ATMIO_FLAG_NORX))
197                 patm_rx_vcc_open(sc, vcc);
198
199         if (!reload) {
200                 /* inform management about non-NG and NG-PVCs */
201                 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
202                      (vcc->vcc.flags & ATMIO_FLAG_PVC))
203                         ATMEV_SEND_VCC_CHANGED(IFP2IFATM(sc->ifp), vcc->vcc.vpi,
204                             vcc->vcc.vci, 1);
205         }
206
207         patm_debug(sc, VCC, "Open VCC: now open");
208 }
209
210 /*
211  * Try to close the given VCC
212  */
213 static int
214 patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg)
215 {
216         u_int cid;
217         struct patm_vcc *vcc;
218         int error = 0;
219
220         patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci);
221
222         if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci))
223                 return (EINVAL);
224         cid = PATM_CID(sc, arg->vpi, arg->vci);
225
226         mtx_lock(&sc->mtx);
227         if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
228                 /* stopped while we have analyzed the arguments */
229                 error = EIO;
230                 goto done;
231         }
232
233         vcc = sc->vccs[cid];
234         if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) {
235                 error = ENOENT;
236                 goto done;
237         }
238
239         if (vcc->vflags & PATM_VCC_TX_OPEN)
240                 patm_tx_vcc_close(sc, vcc);
241         if (vcc->vflags & PATM_VCC_RX_OPEN)
242                 patm_rx_vcc_close(sc, vcc);
243
244         if (vcc->vcc.flags & ATMIO_FLAG_ASYNC)
245                 goto done;
246
247         while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) {
248                 cv_wait(&sc->vcc_cv, &sc->mtx);
249                 if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
250                         /* ups, has been stopped */
251                         error = EIO;
252                         goto done;
253                 }
254         }
255
256         if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX))
257                 patm_tx_vcc_closed(sc, vcc);
258         if (!(vcc->vcc.flags & ATMIO_FLAG_NORX))
259                 patm_rx_vcc_closed(sc, vcc);
260
261         patm_vcc_closed(sc, vcc);
262
263   done:
264         mtx_unlock(&sc->mtx);
265
266         return (error);
267 }
268
269 /*
270  * VCC has been finally closed.
271  */
272 void
273 patm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc)
274 {
275
276         /* inform management about non-NG and NG-PVCs */
277         if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
278             (vcc->vcc.flags & ATMIO_FLAG_PVC))
279                 ATMEV_SEND_VCC_CHANGED(IFP2IFATM(sc->ifp), vcc->vcc.vpi,
280                     vcc->vcc.vci, 0);
281
282         sc->vccs_open--;
283         sc->vccs[vcc->cid] = NULL;
284         uma_zfree(sc->vcc_zone, vcc);
285 }
286
287 int
288 patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
289 {
290         struct ifreq *ifr = (struct ifreq *)data;
291         struct ifaddr *ifa = (struct ifaddr *)data;
292         struct patm_softc *sc = ifp->if_softc;
293         int error = 0;
294         uint32_t cfg;
295         struct atmio_vcctable *vtab;
296
297         switch (cmd) {
298
299           case SIOCSIFADDR:
300                 mtx_lock(&sc->mtx);
301                 ifp->if_flags |= IFF_UP;
302                 if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
303                         patm_initialize(sc);
304                 switch (ifa->ifa_addr->sa_family) {
305
306 #ifdef INET
307                   case AF_INET:
308                   case AF_INET6:
309                         ifa->ifa_rtrequest = atm_rtrequest;
310                         break;
311 #endif
312                   default:
313                         break;
314                 }
315                 mtx_unlock(&sc->mtx);
316                 break;
317
318           case SIOCSIFFLAGS:
319                 mtx_lock(&sc->mtx);
320                 if (ifp->if_flags & IFF_UP) {
321                         if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
322                                 patm_initialize(sc);
323                         }
324                 } else {
325                         if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
326                                 patm_stop(sc);
327                         }
328                 }
329                 mtx_unlock(&sc->mtx);
330                 break;
331
332           case SIOCGIFMEDIA:
333           case SIOCSIFMEDIA:
334                 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
335
336                 /*
337                  * We need to toggle unassigned/idle cells ourself because
338                  * the 77252 generates null cells for spacing. When switching
339                  * null cells of it gets the timing wrong.
340                  */
341                 mtx_lock(&sc->mtx);
342                 if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
343                         if (sc->utopia.state & UTP_ST_UNASS) {
344                                 if (!(sc->flags & PATM_UNASS)) {
345                                         cfg = patm_nor_read(sc, IDT_NOR_CFG);
346                                         cfg &= ~IDT_CFG_IDLECLP;
347                                         patm_nor_write(sc, IDT_NOR_CFG, cfg);
348                                         sc->flags |= PATM_UNASS;
349                                 }
350                         } else {
351                                 if (sc->flags & PATM_UNASS) {
352                                         cfg = patm_nor_read(sc, IDT_NOR_CFG);
353                                         cfg |= IDT_CFG_IDLECLP;
354                                         patm_nor_write(sc, IDT_NOR_CFG, cfg);
355                                         sc->flags &= ~PATM_UNASS;
356                                 }
357                         }
358                 } else {
359                         if (sc->utopia.state & UTP_ST_UNASS)
360                                 sc->flags |= PATM_UNASS;
361                         else
362                                 sc->flags &= ~PATM_UNASS;
363                 }
364                 mtx_unlock(&sc->mtx);
365                 break;
366
367           case SIOCSIFMTU:
368                 /*
369                  * Set the interface MTU.
370                  */
371                 if (ifr->ifr_mtu > ATMMTU)
372                         error = EINVAL;
373                 else
374                         ifp->if_mtu = ifr->ifr_mtu;
375                 break;
376
377           case SIOCATMOPENVCC:          /* kernel internal use */
378                 error = patm_open_vcc(sc, (struct atmio_openvcc *)data);
379                 break;
380
381           case SIOCATMCLOSEVCC:         /* kernel internal use */
382                 error = patm_close_vcc(sc, (struct atmio_closevcc *)data);
383                 break;
384
385           case SIOCATMGVCCS:    /* external use */
386                 /* return vcc table */
387                 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
388                     sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1);
389                 error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) +
390                     vtab->count * sizeof(vtab->vccs[0]));
391                 free(vtab, M_DEVBUF);
392                 break;
393
394           case SIOCATMGETVCCS:  /* netgraph internal use */
395                 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
396                     sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0);
397                 if (vtab == NULL) {
398                         error = ENOMEM;
399                         break;
400                 }
401                 *(void **)data = vtab;
402                 break;
403
404           default:
405                 patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data);
406                 error = EINVAL;
407                 break;
408         }
409
410         return (error);
411 }