if_hatm_ioctl.c revision 118170
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 118170 2003-07-29 14:07:19Z 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	/* inform management about non-NG and NG-PVCs */
221	if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
222	     (vcc->param.flags & ATMIO_FLAG_PVC))
223		ATMEV_SEND_VCC_CHANGED(&sc->ifatm, arg->param.vpi,
224		    arg->param.vci, 1);
225
226	/* don't free below */
227	vcc = NULL;
228
229	sc->open_vccs++;
230
231  done:
232	mtx_unlock(&sc->mtx);
233	if (vcc != NULL)
234		uma_zfree(sc->vcc_zone, vcc);
235	return (error);
236}
237
238/*
239 * Enable ioctl for NATM. Map to an open ioctl.
240 */
241static int
242hatm_open_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
243{
244	struct atmio_openvcc *v;
245	int error;
246
247	if ((v = malloc(sizeof(*v), M_TEMP, M_NOWAIT | M_ZERO)) == NULL)
248		return (ENOMEM);
249
250	v->param.flags = ATM_PH_FLAGS(&ph->aph) &
251	    (ATM_PH_AAL5 | ATM_PH_LLCSNAP);
252	v->param.vpi = ATM_PH_VPI(&ph->aph);
253	v->param.vci = ATM_PH_VCI(&ph->aph);
254	v->param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5)
255	    ? ATMIO_AAL_5 : ATMIO_AAL_0;
256	v->param.traffic = hatm_natm_traffic;
257	v->rxhand = ph->rxhand;
258	if ((v->param.tparam.pcr = hatm_natm_pcr) == 0 ||
259	    hatm_natm_pcr > sc->ifatm.mib.pcr)
260		v->param.tparam.pcr = sc->ifatm.mib.pcr;
261	v->param.tparam.mcr = 0;
262
263	error = hatm_open_vcc(sc, v);
264	if (error == 0)
265		sc->vccs[HE_CID(v->param.vpi, v->param.vci)]->vflags |=
266		    HE_VCC_ASYNC;
267
268	free(v, M_TEMP);
269
270	return (error);
271}
272
273/*
274 * VCC has been finally closed.
275 */
276void
277hatm_vcc_closed(struct hatm_softc *sc, u_int cid)
278{
279	struct hevcc *vcc = sc->vccs[cid];
280
281	/* inform management about non-NG and NG-PVCs */
282	if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
283	    (vcc->param.flags & ATMIO_FLAG_PVC))
284		ATMEV_SEND_VCC_CHANGED(&sc->ifatm, HE_VPI(cid), HE_VCI(cid), 0);
285
286	sc->open_vccs--;
287	uma_zfree(sc->vcc_zone, vcc);
288	sc->vccs[cid] = NULL;
289}
290
291/*
292 * Try to close the given VCC
293 */
294static int
295hatm_close_vcc(struct hatm_softc *sc, struct atmio_closevcc *arg)
296{
297	u_int cid;
298	struct hevcc *vcc;
299	int error = 0;
300
301	DBG(sc, VCC, ("Close VCC: %u.%u", arg->vpi, arg->vci));
302
303	if((arg->vpi & ~HE_VPI_MASK) ||
304	   (arg->vci & ~HE_VCI_MASK) ||
305	   (arg->vci == 0))
306		return (EINVAL);
307	cid = HE_CID(arg->vpi, arg->vci);
308
309	mtx_lock(&sc->mtx);
310	vcc = sc->vccs[cid];
311	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
312		error = EIO;
313		goto done;
314	}
315
316	if (vcc == NULL || !(vcc->vflags & HE_VCC_OPEN)) {
317		error = ENOENT;
318		goto done;
319	}
320
321	if (vcc->vflags & HE_VCC_TX_OPEN)
322		hatm_tx_vcc_close(sc, cid);
323	if (vcc->vflags & HE_VCC_RX_OPEN)
324		hatm_rx_vcc_close(sc, cid);
325
326	if (vcc->vflags & HE_VCC_ASYNC)
327		goto done;
328
329	while ((sc->ifatm.ifnet.if_flags & IFF_RUNNING) &&
330	       (vcc->vflags & (HE_VCC_TX_CLOSING | HE_VCC_RX_CLOSING)))
331		cv_wait(&sc->vcc_cv, &sc->mtx);
332
333	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
334		error = EIO;
335		goto done;
336	}
337
338	if (!(vcc->vflags & ATMIO_FLAG_NOTX))
339		hatm_tx_vcc_closed(sc, cid);
340
341	hatm_vcc_closed(sc, cid);
342
343  done:
344	mtx_unlock(&sc->mtx);
345	return (error);
346}
347
348static int
349hatm_close_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
350{
351	struct atmio_closevcc v;
352
353	v.vpi = ATM_PH_VPI(&ph->aph);
354	v.vci = ATM_PH_VCI(&ph->aph);
355
356	return (hatm_close_vcc(sc, &v));
357}
358
359/*
360 * IOCTL handler
361 */
362int
363hatm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
364{
365	struct ifreq *ifr = (struct ifreq *)data;
366	struct ifaddr *ifa = (struct ifaddr *)data;
367	struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc;
368	struct atmio_vcctable *vtab;
369	int error = 0;
370
371	switch (cmd) {
372
373	  case SIOCSIFADDR:
374		mtx_lock(&sc->mtx);
375		ifp->if_flags |= IFF_UP;
376		if (!(ifp->if_flags & IFF_RUNNING))
377			hatm_initialize(sc);
378		switch (ifa->ifa_addr->sa_family) {
379
380#ifdef INET
381		  case AF_INET:
382		  case AF_INET6:
383			ifa->ifa_rtrequest = atm_rtrequest;
384			break;
385#endif
386		  default:
387			break;
388		}
389		mtx_unlock(&sc->mtx);
390		break;
391
392	  case SIOCSIFFLAGS:
393		mtx_lock(&sc->mtx);
394		if (ifp->if_flags & IFF_UP) {
395			if (!(ifp->if_flags & IFF_RUNNING)) {
396				hatm_initialize(sc);
397			}
398		} else {
399			if (ifp->if_flags & IFF_RUNNING) {
400				hatm_stop(sc);
401			}
402		}
403		mtx_unlock(&sc->mtx);
404		break;
405
406	  case SIOCGIFMEDIA:
407	  case SIOCSIFMEDIA:
408		error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
409		break;
410
411	case SIOCSIFMTU:
412		/*
413		 * Set the interface MTU.
414		 */
415		if (ifr->ifr_mtu > ATMMTU)
416			error = EINVAL;
417		else
418			ifp->if_mtu = ifr->ifr_mtu;
419		break;
420
421	  case SIOCATMGVCCS:
422		/* return vcc table */
423		vtab = hatm_getvccs(sc);
424		if (vtab == NULL) {
425			error = ENOMEM;
426			break;
427		}
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 SIOCATMENA:	/* NATM internal use */
434		error = hatm_open_vcc1(sc, (struct atm_pseudoioctl *)data);
435		break;
436
437	  case SIOCATMDIS:	/* NATM internal use */
438		error = hatm_close_vcc1(sc, (struct atm_pseudoioctl *)data);
439		break;
440
441	  case SIOCATMGETVCCS:	/* netgraph internal use */
442		if ((vtab = hatm_getvccs(sc)) == NULL) {
443			error = ENOMEM;
444			break;
445		}
446		*(void **)data = vtab;
447		break;
448
449	  case SIOCATMOPENVCC:		/* netgraph/harp internal use */
450		error = hatm_open_vcc(sc, (struct atmio_openvcc *)data);
451		break;
452
453	  case SIOCATMCLOSEVCC:		/* netgraph and HARP internal use */
454		error = hatm_close_vcc(sc, (struct atmio_closevcc *)data);
455		break;
456
457	  default:
458		DBG(sc, IOCTL, ("cmd=%08lx arg=%p", cmd, data));
459		error = EINVAL;
460		break;
461	}
462
463	return (error);
464}
465
466static int
467hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS)
468{
469	int error;
470	int tmp;
471
472	tmp = hatm_natm_traffic;
473	error = sysctl_handle_int(oidp, &tmp, 0, req);
474	if (error != 0 || req->newptr == NULL)
475		return (error);
476
477	if (tmp != ATMIO_TRAFFIC_UBR && tmp != ATMIO_TRAFFIC_CBR)
478		return (EINVAL);
479
480	hatm_natm_traffic = tmp;
481	return (0);
482}
483