if_hatm_ioctl.c revision 116519
1/*
2 * Copyright (c) 2001-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 * ForeHE driver.
30 *
31 * Ioctl handler.
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/dev/hatm/if_hatm_ioctl.c 116519 2003-06-18 09:31:37Z harti $");
36
37#include "opt_inet.h"
38#include "opt_natm.h"
39
40#include <sys/types.h>
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/malloc.h>
44#include <sys/kernel.h>
45#include <sys/bus.h>
46#include <sys/errno.h>
47#include <sys/conf.h>
48#include <sys/module.h>
49#include <sys/queue.h>
50#include <sys/syslog.h>
51#include <sys/condvar.h>
52#include <sys/sysctl.h>
53#include <vm/uma.h>
54
55#include <sys/sockio.h>
56#include <sys/mbuf.h>
57#include <sys/socket.h>
58
59#include <net/if.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 <pci/pcireg.h>
71#include <pci/pcivar.h>
72
73#include <dev/utopia/utopia.h>
74#include <dev/hatm/if_hatmconf.h>
75#include <dev/hatm/if_hatmreg.h>
76#include <dev/hatm/if_hatmvar.h>
77
78static u_int hatm_natm_traffic = ATMIO_TRAFFIC_UBR;
79static u_int hatm_natm_pcr = 0;
80
81static int hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS);
82
83SYSCTL_DECL(_hw_atm);
84
85SYSCTL_PROC(_hw_atm, OID_AUTO, natm_traffic, CTLTYPE_UINT | CTLFLAG_RW,
86    &hatm_natm_traffic, sizeof(hatm_natm_traffic), hatm_sysctl_natm_traffic,
87    "IU", "traffic type for NATM connections");
88SYSCTL_UINT(_hw_atm, OID_AUTO, natm_pcr, CTLFLAG_RW,
89    &hatm_natm_pcr, 0, "PCR for NATM connections");
90
91/*
92 * Return a table of VCCs in a freshly allocated memory area.
93 * Here we have a problem: we first count, how many vccs we need
94 * to return. The we allocate the memory and finally fill it in.
95 * Because we cannot lock while calling malloc, the number of active
96 * vccs may change while we're in malloc. So we allocate a couple of
97 * vccs more and if space anyway is not enough re-iterate.
98 */
99static struct atmio_vcctable *
100hatm_getvccs(struct hatm_softc *sc)
101{
102	u_int cid, alloc;
103	size_t len;
104	struct atmio_vcctable *vccs;
105	struct atmio_vcc *v;
106
107	alloc = sc->open_vccs + 10;
108	vccs = NULL;
109
110  again:
111	len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]);
112	vccs = reallocf(vccs, len, M_DEVBUF, M_WAITOK);
113	bzero(vccs, len);
114
115	/*
116	 * Fill in
117	 */
118	vccs->count = 0;
119	v = vccs->vccs;
120
121	mtx_lock(&sc->mtx);
122	for (cid = 0; cid < HE_MAX_VCCS; cid++)
123		if (sc->vccs[cid] != NULL &&
124		    (sc->vccs[cid]->vflags & (HE_VCC_RX_OPEN |
125		    HE_VCC_TX_OPEN))) {
126			if (++vccs->count == alloc) {
127				/*
128				 * too many - try again
129				 */
130				break;
131			}
132			*v++ = sc->vccs[cid]->param;
133		}
134	mtx_unlock(&sc->mtx);
135
136	if (cid == HE_MAX_VCCS)
137		return (vccs);
138
139	alloc *= 2;
140	goto again;
141}
142
143/*
144 * Try to open the given VCC.
145 */
146static int
147hatm_open_vcc(struct hatm_softc *sc, struct atmio_openvcc *arg)
148{
149	u_int cid;
150	struct hevcc *vcc;
151	int error = 0;
152
153	DBG(sc, VCC, ("Open VCC: %u.%u flags=%#x", arg->param.vpi,
154	    arg->param.vci, arg->param.flags));
155
156	if ((arg->param.vpi & ~HE_VPI_MASK) ||
157	    (arg->param.vci & ~HE_VCI_MASK) ||
158	    (arg->param.vci == 0))
159		return (EINVAL);
160	cid = HE_CID(arg->param.vpi, arg->param.vci);
161
162	if ((arg->param.flags & ATMIO_FLAG_NOTX) &&
163	    (arg->param.flags & ATMIO_FLAG_NORX))
164		return (EINVAL);
165
166	vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO);
167	if (vcc == NULL)
168		return (ENOMEM);
169
170	mtx_lock(&sc->mtx);
171	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
172		error = EIO;
173		goto done;
174	}
175	if (sc->vccs[cid] != NULL) {
176		error = EBUSY;
177		goto done;
178	}
179	vcc->param = arg->param;
180	vcc->rxhand = arg->rxhand;
181	switch (vcc->param.aal) {
182
183	  case ATMIO_AAL_0:
184	  case ATMIO_AAL_5:
185	  case ATMIO_AAL_RAW:
186		break;
187
188	  default:
189		error = EINVAL;
190		goto done;
191	}
192	switch (vcc->param.traffic) {
193
194	  case ATMIO_TRAFFIC_UBR:
195	  case ATMIO_TRAFFIC_CBR:
196	  case ATMIO_TRAFFIC_ABR:
197		break;
198
199	  default:
200		error = EINVAL;
201		goto done;
202	}
203	vcc->ntpds = 0;
204	vcc->chain = vcc->last = NULL;
205	vcc->ibytes = vcc->ipackets = 0;
206	vcc->obytes = vcc->opackets = 0;
207
208	if (!(vcc->param.flags & ATMIO_FLAG_NOTX) &&
209	     (error = hatm_tx_vcc_can_open(sc, cid, vcc)) != 0)
210		goto done;
211
212	/* ok - go ahead */
213	sc->vccs[cid] = vcc;
214
215	if (!(vcc->param.flags & ATMIO_FLAG_NOTX))
216		hatm_tx_vcc_open(sc, cid);
217	if (!(vcc->param.flags & ATMIO_FLAG_NORX))
218		hatm_rx_vcc_open(sc, cid);
219
220#ifdef notyet
221	/* inform management about non-NG and NG-PVCs */
222	if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
223	     (vcc->param.flags & ATMIO_FLAG_PVC))
224		atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
225		   (1 << 24) | (arg->vpi << 16) | arg->vci);
226#endif
227
228	/* don't free below */
229	vcc = NULL;
230
231	sc->open_vccs++;
232
233  done:
234	mtx_unlock(&sc->mtx);
235	if (vcc != NULL)
236		uma_zfree(sc->vcc_zone, vcc);
237	return (error);
238}
239
240/*
241 * Enable ioctl for NATM. Map to an open ioctl.
242 */
243static int
244hatm_open_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
245{
246	struct atmio_openvcc *v;
247	int error;
248
249	if ((v = malloc(sizeof(*v), M_TEMP, M_NOWAIT | M_ZERO)) == NULL)
250		return (ENOMEM);
251
252	v->param.flags = ATM_PH_FLAGS(&ph->aph) &
253	    (ATM_PH_AAL5 | ATM_PH_LLCSNAP);
254	v->param.vpi = ATM_PH_VPI(&ph->aph);
255	v->param.vci = ATM_PH_VCI(&ph->aph);
256	v->param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5)
257	    ? ATMIO_AAL_5 : ATMIO_AAL_0;
258	v->param.traffic = hatm_natm_traffic;
259	v->rxhand = ph->rxhand;
260	if ((v->param.tparam.pcr = hatm_natm_pcr) == 0 ||
261	    hatm_natm_pcr > sc->ifatm.mib.pcr)
262		v->param.tparam.pcr = sc->ifatm.mib.pcr;
263	v->param.tparam.mcr = 0;
264
265	error = hatm_open_vcc(sc, v);
266	if (error == 0)
267		sc->vccs[HE_CID(v->param.vpi, v->param.vci)]->vflags |=
268		    HE_VCC_ASYNC;
269
270	free(v, M_TEMP);
271
272	return (error);
273}
274
275/*
276 * VCC has been finally closed.
277 */
278void
279hatm_vcc_closed(struct hatm_softc *sc, u_int cid)
280{
281	struct hevcc *vcc = sc->vccs[cid];
282
283#ifdef notyet
284	/* inform management about non-NG and NG-PVCs */
285	if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
286	    (vcc->param.flags & ATMIO_FLAG_PVC))
287		atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
288		   (0 << 24) | (HE_VPI(cid) << 16) | HE_VCI(cid));
289#endif
290
291	sc->open_vccs--;
292	uma_zfree(sc->vcc_zone, vcc);
293	sc->vccs[cid] = NULL;
294}
295
296/*
297 * Try to close the given VCC
298 */
299static int
300hatm_close_vcc(struct hatm_softc *sc, struct atmio_closevcc *arg)
301{
302	u_int cid;
303	struct hevcc *vcc;
304	int error = 0;
305
306	DBG(sc, VCC, ("Close VCC: %u.%u", arg->vpi, arg->vci));
307
308	if((arg->vpi & ~HE_VPI_MASK) ||
309	   (arg->vci & ~HE_VCI_MASK) ||
310	   (arg->vci == 0))
311		return (EINVAL);
312	cid = HE_CID(arg->vpi, arg->vci);
313
314	mtx_lock(&sc->mtx);
315	vcc = sc->vccs[cid];
316	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
317		error = EIO;
318		goto done;
319	}
320
321	if (vcc == NULL || !(vcc->vflags & HE_VCC_OPEN)) {
322		error = ENOENT;
323		goto done;
324	}
325
326	if (vcc->vflags & HE_VCC_TX_OPEN)
327		hatm_tx_vcc_close(sc, cid);
328	if (vcc->vflags & HE_VCC_RX_OPEN)
329		hatm_rx_vcc_close(sc, cid);
330
331	if (vcc->vflags & HE_VCC_ASYNC)
332		goto done;
333
334	while ((sc->ifatm.ifnet.if_flags & IFF_RUNNING) &&
335	       (vcc->vflags & (HE_VCC_TX_CLOSING | HE_VCC_RX_CLOSING)))
336		cv_wait(&sc->vcc_cv, &sc->mtx);
337
338	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
339		error = EIO;
340		goto done;
341	}
342
343	if (!(vcc->vflags & ATMIO_FLAG_NOTX))
344		hatm_tx_vcc_closed(sc, cid);
345
346	hatm_vcc_closed(sc, cid);
347
348  done:
349	mtx_unlock(&sc->mtx);
350	return (error);
351}
352
353static int
354hatm_close_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
355{
356	struct atmio_closevcc v;
357
358	v.vpi = ATM_PH_VPI(&ph->aph);
359	v.vci = ATM_PH_VCI(&ph->aph);
360
361	return (hatm_close_vcc(sc, &v));
362}
363
364/*
365 * IOCTL handler
366 */
367int
368hatm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
369{
370	struct ifreq *ifr = (struct ifreq *)data;
371	struct ifaddr *ifa = (struct ifaddr *)data;
372	struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc;
373	struct atmio_vcctable *vtab;
374	int error = 0;
375
376	switch (cmd) {
377
378	  case SIOCSIFADDR:
379		mtx_lock(&sc->mtx);
380		ifp->if_flags |= IFF_UP;
381		if (!(ifp->if_flags & IFF_RUNNING))
382			hatm_initialize(sc);
383		switch (ifa->ifa_addr->sa_family) {
384
385#ifdef INET
386		  case AF_INET:
387		  case AF_INET6:
388			ifa->ifa_rtrequest = atm_rtrequest;
389			break;
390#endif
391		  default:
392			break;
393		}
394		mtx_unlock(&sc->mtx);
395		break;
396
397	  case SIOCSIFFLAGS:
398		mtx_lock(&sc->mtx);
399		if (ifp->if_flags & IFF_UP) {
400			if (!(ifp->if_flags & IFF_RUNNING)) {
401				hatm_initialize(sc);
402			}
403		} else {
404			if (ifp->if_flags & IFF_RUNNING) {
405				hatm_stop(sc);
406			}
407		}
408		mtx_unlock(&sc->mtx);
409		break;
410
411	  case SIOCGIFMEDIA:
412	  case SIOCSIFMEDIA:
413		error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
414		break;
415
416	case SIOCSIFMTU:
417		/*
418		 * Set the interface MTU.
419		 */
420		if (ifr->ifr_mtu > ATMMTU)
421			error = EINVAL;
422		else
423			ifp->if_mtu = ifr->ifr_mtu;
424		break;
425
426	  case SIOCATMGVCCS:
427		/* return vcc table */
428		vtab = hatm_getvccs(sc);
429		if (vtab == NULL) {
430			error = ENOMEM;
431			break;
432		}
433		error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) +
434		    vtab->count * sizeof(vtab->vccs[0]));
435		free(vtab, M_DEVBUF);
436		break;
437
438	  case SIOCATMENA:	/* NATM internal use */
439		error = hatm_open_vcc1(sc, (struct atm_pseudoioctl *)data);
440		break;
441
442	  case SIOCATMDIS:	/* NATM internal use */
443		error = hatm_close_vcc1(sc, (struct atm_pseudoioctl *)data);
444		break;
445
446	  case SIOCATMGETVCCS:	/* netgraph internal use */
447		if ((vtab = hatm_getvccs(sc)) == NULL) {
448			error = ENOMEM;
449			break;
450		}
451		*(void **)data = vtab;
452		break;
453
454	  case SIOCATMOPENVCC:		/* netgraph/harp internal use */
455		error = hatm_open_vcc(sc, (struct atmio_openvcc *)data);
456		break;
457
458	  case SIOCATMCLOSEVCC:		/* netgraph and HARP internal use */
459		error = hatm_close_vcc(sc, (struct atmio_closevcc *)data);
460		break;
461
462	  default:
463		DBG(sc, IOCTL, ("cmd=%08lx arg=%p", cmd, data));
464		error = EINVAL;
465		break;
466	}
467
468	return (error);
469}
470
471static int
472hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS)
473{
474	int error;
475	int tmp;
476
477	tmp = hatm_natm_traffic;
478	error = sysctl_handle_int(oidp, &tmp, 0, req);
479	if (error != 0 || req->newptr == NULL)
480		return (error);
481
482	if (tmp != ATMIO_TRAFFIC_UBR && tmp != ATMIO_TRAFFIC_CBR)
483		return (EINVAL);
484
485	hatm_natm_traffic = tmp;
486	return (0);
487}
488