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