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