if_patm_ioctl.c revision 117632
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 117632 2003-07-15 11:57:24Z 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#ifdef notyet
182	/* inform management about non-NG and NG-PVCs */
183	if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
184	     (vcc->vcc.flags & ATMIO_FLAG_PVC))
185		atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
186		   (1 << 24) | (vcc->vcc.vpi << 16) | vcc->vcc.vci);
187#endif
188
189	patm_debug(sc, VCC, "Open VCC: now open");
190
191	/* don't free below */
192	vcc = NULL;
193
194	sc->vccs_open++;
195
196	/* done */
197  done:
198	mtx_unlock(&sc->mtx);
199	if (vcc != NULL)
200		uma_zfree(sc->vcc_zone, vcc);
201	return (error);
202}
203
204/*
205 * Enable ioctl for NATM. Map to an open ioctl.
206 */
207static int
208patm_open_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph)
209{
210	struct atmio_openvcc v;
211
212	bzero(&v, sizeof(v));
213	v.param.flags = ATM_PH_FLAGS(&ph->aph) & (ATM_PH_AAL5 | ATM_PH_LLCSNAP);
214	v.param.vpi = ATM_PH_VPI(&ph->aph);
215	v.param.vci = ATM_PH_VCI(&ph->aph);
216	v.param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5)
217	    ? ATMIO_AAL_5 : ATMIO_AAL_0;
218	v.param.traffic = ATMIO_TRAFFIC_UBR;;
219	v.param.tparam.pcr = sc->ifatm.mib.pcr;
220	v.rxhand = ph->rxhand;
221
222	return (patm_open_vcc(sc, &v, PATM_VCC_ASYNC));
223}
224
225/*
226 * Try to close the given VCC
227 */
228static int
229patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg)
230{
231	u_int cid;
232	struct patm_vcc *vcc;
233	int error = 0;
234
235	patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci);
236
237	if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci))
238		return (EINVAL);
239	cid = PATM_CID(sc, arg->vpi, arg->vci);
240
241	mtx_lock(&sc->mtx);
242	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
243		/* stopped while we have analyzed the arguments */
244		error = EIO;
245		goto done;
246	}
247
248	vcc = sc->vccs[cid];
249	if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) {
250		error = ENOENT;
251		goto done;
252	}
253
254	if (vcc->vflags & PATM_VCC_TX_OPEN)
255		patm_tx_vcc_close(sc, vcc);
256	if (vcc->vflags & PATM_VCC_RX_OPEN)
257		patm_rx_vcc_close(sc, vcc);
258
259	if (vcc->vflags & PATM_VCC_ASYNC)
260		goto done;
261
262	while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) {
263		cv_wait(&sc->vcc_cv, &sc->mtx);
264		if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
265			/* ups, has been stopped */
266			error = EIO;
267			goto done;
268		}
269	}
270
271	if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX))
272		patm_tx_vcc_closed(sc, vcc);
273	if (!(vcc->vcc.flags & ATMIO_FLAG_NORX))
274		patm_rx_vcc_closed(sc, vcc);
275
276	patm_vcc_closed(sc, vcc);
277
278  done:
279	mtx_unlock(&sc->mtx);
280
281	return (error);
282}
283
284/*
285 * Close a VCC asynchronuosly
286 */
287static int
288patm_close_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph)
289{
290	struct atmio_closevcc v;
291
292	v.vpi = ATM_PH_VPI(&ph->aph);
293	v.vci = ATM_PH_VCI(&ph->aph);
294
295	return (patm_close_vcc(sc, &v));
296}
297
298/*
299 * VCC has been finally closed.
300 */
301void
302patm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc)
303{
304
305#ifdef notyet
306	/* inform management about non-NG and NG-PVCs */
307	if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
308	    (vcc->vcc.flags & ATMIO_FLAG_PVC))
309		atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
310		   (0 << 24) | (vcc->vcc.vpi << 16) | vcc->vcc.vci);
311#endif
312
313	sc->vccs_open--;
314	sc->vccs[vcc->cid] = NULL;
315	uma_zfree(sc->vcc_zone, vcc);
316}
317
318int
319patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
320{
321	struct ifreq *ifr = (struct ifreq *)data;
322	struct ifaddr *ifa = (struct ifaddr *)data;
323	struct patm_softc *sc = ifp->if_softc;
324	int error = 0;
325	uint32_t cfg;
326	struct atmio_vcctable *vtab;
327
328	switch (cmd) {
329
330	  case SIOCSIFADDR:
331		mtx_lock(&sc->mtx);
332		ifp->if_flags |= IFF_UP;
333		if (!(ifp->if_flags & IFF_RUNNING))
334			patm_initialize(sc);
335		switch (ifa->ifa_addr->sa_family) {
336
337#ifdef INET
338		  case AF_INET:
339		  case AF_INET6:
340			ifa->ifa_rtrequest = atm_rtrequest;
341			break;
342#endif
343		  default:
344			break;
345		}
346		mtx_unlock(&sc->mtx);
347		break;
348
349	  case SIOCSIFFLAGS:
350		mtx_lock(&sc->mtx);
351		if (ifp->if_flags & IFF_UP) {
352			if (!(ifp->if_flags & IFF_RUNNING)) {
353				patm_initialize(sc);
354			}
355		} else {
356			if (ifp->if_flags & IFF_RUNNING) {
357				patm_stop(sc);
358			}
359		}
360		mtx_unlock(&sc->mtx);
361		break;
362
363	  case SIOCGIFMEDIA:
364	  case SIOCSIFMEDIA:
365		error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
366
367		/*
368		 * We need to toggle unassigned/idle cells ourself because
369		 * the 77252 generates null cells for spacing. When switching
370		 * null cells of it gets the timing wrong.
371		 */
372		mtx_lock(&sc->mtx);
373		if (ifp->if_flags & IFF_RUNNING) {
374			if (sc->utopia.state & UTP_ST_UNASS) {
375				if (!(sc->flags & PATM_UNASS)) {
376					cfg = patm_nor_read(sc, IDT_NOR_CFG);
377					cfg &= ~IDT_CFG_IDLECLP;
378					patm_nor_write(sc, IDT_NOR_CFG, cfg);
379					sc->flags |= PATM_UNASS;
380				}
381			} else {
382				if (sc->flags & PATM_UNASS) {
383					cfg = patm_nor_read(sc, IDT_NOR_CFG);
384					cfg |= IDT_CFG_IDLECLP;
385					patm_nor_write(sc, IDT_NOR_CFG, cfg);
386					sc->flags &= ~PATM_UNASS;
387				}
388			}
389		} else {
390			if (sc->utopia.state & UTP_ST_UNASS)
391				sc->flags |= PATM_UNASS;
392			else
393				sc->flags &= ~PATM_UNASS;
394		}
395		mtx_unlock(&sc->mtx);
396		break;
397
398	  case SIOCSIFMTU:
399		/*
400		 * Set the interface MTU.
401		 */
402		if (ifr->ifr_mtu > ATMMTU)
403			error = EINVAL;
404		else
405			ifp->if_mtu = ifr->ifr_mtu;
406		break;
407
408	  case SIOCATMOPENVCC:		/* netgraph/harp internal use */
409		error = patm_open_vcc(sc, (struct atmio_openvcc *)data, 0);
410		break;
411
412	  case SIOCATMCLOSEVCC:		/* netgraph and HARP internal use */
413		error = patm_close_vcc(sc, (struct atmio_closevcc *)data);
414		break;
415
416	  case SIOCATMENA:	/* NATM internal use */
417		error = patm_open_vcc1(sc, (struct atm_pseudoioctl *)data);
418		break;
419
420	  case SIOCATMDIS:	/* NATM internal use */
421		error = patm_close_vcc1(sc, (struct atm_pseudoioctl *)data);
422		break;
423
424	  case SIOCATMGVCCS:	/* external use */
425		/* return vcc table */
426		vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
427		    sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1);
428		error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) +
429		    vtab->count * sizeof(vtab->vccs[0]));
430		free(vtab, M_DEVBUF);
431		break;
432
433	  case SIOCATMGETVCCS:	/* netgraph internal use */
434		vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
435		    sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0);
436		if (vtab == NULL) {
437			error = ENOMEM;
438			break;
439		}
440		*(void **)data = vtab;
441		break;
442
443	  default:
444		patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data);
445		error = EINVAL;
446		break;
447	}
448
449	return (error);
450}
451