if_hatm_ioctl.c revision 118205
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 118205 2003-07-30 11:32:42Z 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 * Try to open the given VCC.
93 */
94static int
95hatm_open_vcc(struct hatm_softc *sc, struct atmio_openvcc *arg)
96{
97	u_int cid;
98	struct hevcc *vcc;
99	int error = 0;
100
101	DBG(sc, VCC, ("Open VCC: %u.%u flags=%#x", arg->param.vpi,
102	    arg->param.vci, arg->param.flags));
103
104	if ((arg->param.vpi & ~HE_VPI_MASK) ||
105	    (arg->param.vci & ~HE_VCI_MASK) ||
106	    (arg->param.vci == 0))
107		return (EINVAL);
108	cid = HE_CID(arg->param.vpi, arg->param.vci);
109
110	if ((arg->param.flags & ATMIO_FLAG_NOTX) &&
111	    (arg->param.flags & ATMIO_FLAG_NORX))
112		return (EINVAL);
113
114	vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO);
115	if (vcc == NULL)
116		return (ENOMEM);
117
118	mtx_lock(&sc->mtx);
119	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
120		error = EIO;
121		goto done;
122	}
123	if (sc->vccs[cid] != NULL) {
124		error = EBUSY;
125		goto done;
126	}
127	vcc->param = arg->param;
128	vcc->rxhand = arg->rxhand;
129	switch (vcc->param.aal) {
130
131	  case ATMIO_AAL_0:
132	  case ATMIO_AAL_5:
133	  case ATMIO_AAL_RAW:
134		break;
135
136	  default:
137		error = EINVAL;
138		goto done;
139	}
140	switch (vcc->param.traffic) {
141
142	  case ATMIO_TRAFFIC_UBR:
143	  case ATMIO_TRAFFIC_CBR:
144	  case ATMIO_TRAFFIC_ABR:
145		break;
146
147	  default:
148		error = EINVAL;
149		goto done;
150	}
151	vcc->ntpds = 0;
152	vcc->chain = vcc->last = NULL;
153	vcc->ibytes = vcc->ipackets = 0;
154	vcc->obytes = vcc->opackets = 0;
155
156	if (!(vcc->param.flags & ATMIO_FLAG_NOTX) &&
157	     (error = hatm_tx_vcc_can_open(sc, cid, vcc)) != 0)
158		goto done;
159
160	/* ok - go ahead */
161	sc->vccs[cid] = vcc;
162
163	if (!(vcc->param.flags & ATMIO_FLAG_NOTX))
164		hatm_tx_vcc_open(sc, cid);
165	if (!(vcc->param.flags & ATMIO_FLAG_NORX))
166		hatm_rx_vcc_open(sc, cid);
167
168	/* inform management about non-NG and NG-PVCs */
169	if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
170	     (vcc->param.flags & ATMIO_FLAG_PVC))
171		ATMEV_SEND_VCC_CHANGED(&sc->ifatm, arg->param.vpi,
172		    arg->param.vci, 1);
173
174	/* don't free below */
175	vcc = NULL;
176
177	sc->open_vccs++;
178
179  done:
180	mtx_unlock(&sc->mtx);
181	if (vcc != NULL)
182		uma_zfree(sc->vcc_zone, vcc);
183	return (error);
184}
185
186/*
187 * Enable ioctl for NATM. Map to an open ioctl.
188 */
189static int
190hatm_open_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
191{
192	struct atmio_openvcc *v;
193	int error;
194
195	if ((v = malloc(sizeof(*v), M_TEMP, M_NOWAIT | M_ZERO)) == NULL)
196		return (ENOMEM);
197
198	v->param.flags = ATM_PH_FLAGS(&ph->aph) &
199	    (ATM_PH_AAL5 | ATM_PH_LLCSNAP);
200	v->param.vpi = ATM_PH_VPI(&ph->aph);
201	v->param.vci = ATM_PH_VCI(&ph->aph);
202	v->param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5)
203	    ? ATMIO_AAL_5 : ATMIO_AAL_0;
204	v->param.traffic = hatm_natm_traffic;
205	v->rxhand = ph->rxhand;
206	if ((v->param.tparam.pcr = hatm_natm_pcr) == 0 ||
207	    hatm_natm_pcr > sc->ifatm.mib.pcr)
208		v->param.tparam.pcr = sc->ifatm.mib.pcr;
209	v->param.tparam.mcr = 0;
210
211	error = hatm_open_vcc(sc, v);
212	if (error == 0)
213		sc->vccs[HE_CID(v->param.vpi, v->param.vci)]->vflags |=
214		    HE_VCC_ASYNC;
215
216	free(v, M_TEMP);
217
218	return (error);
219}
220
221/*
222 * VCC has been finally closed.
223 */
224void
225hatm_vcc_closed(struct hatm_softc *sc, u_int cid)
226{
227	struct hevcc *vcc = sc->vccs[cid];
228
229	/* inform management about non-NG and NG-PVCs */
230	if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
231	    (vcc->param.flags & ATMIO_FLAG_PVC))
232		ATMEV_SEND_VCC_CHANGED(&sc->ifatm, HE_VPI(cid), HE_VCI(cid), 0);
233
234	sc->open_vccs--;
235	uma_zfree(sc->vcc_zone, vcc);
236	sc->vccs[cid] = NULL;
237}
238
239/*
240 * Try to close the given VCC
241 */
242static int
243hatm_close_vcc(struct hatm_softc *sc, struct atmio_closevcc *arg)
244{
245	u_int cid;
246	struct hevcc *vcc;
247	int error = 0;
248
249	DBG(sc, VCC, ("Close VCC: %u.%u", arg->vpi, arg->vci));
250
251	if((arg->vpi & ~HE_VPI_MASK) ||
252	   (arg->vci & ~HE_VCI_MASK) ||
253	   (arg->vci == 0))
254		return (EINVAL);
255	cid = HE_CID(arg->vpi, arg->vci);
256
257	mtx_lock(&sc->mtx);
258	vcc = sc->vccs[cid];
259	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
260		error = EIO;
261		goto done;
262	}
263
264	if (vcc == NULL || !(vcc->vflags & HE_VCC_OPEN)) {
265		error = ENOENT;
266		goto done;
267	}
268
269	if (vcc->vflags & HE_VCC_TX_OPEN)
270		hatm_tx_vcc_close(sc, cid);
271	if (vcc->vflags & HE_VCC_RX_OPEN)
272		hatm_rx_vcc_close(sc, cid);
273
274	if (vcc->vflags & HE_VCC_ASYNC)
275		goto done;
276
277	while ((sc->ifatm.ifnet.if_flags & IFF_RUNNING) &&
278	       (vcc->vflags & (HE_VCC_TX_CLOSING | HE_VCC_RX_CLOSING)))
279		cv_wait(&sc->vcc_cv, &sc->mtx);
280
281	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
282		error = EIO;
283		goto done;
284	}
285
286	if (!(vcc->vflags & ATMIO_FLAG_NOTX))
287		hatm_tx_vcc_closed(sc, cid);
288
289	hatm_vcc_closed(sc, cid);
290
291  done:
292	mtx_unlock(&sc->mtx);
293	return (error);
294}
295
296static int
297hatm_close_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
298{
299	struct atmio_closevcc v;
300
301	v.vpi = ATM_PH_VPI(&ph->aph);
302	v.vci = ATM_PH_VCI(&ph->aph);
303
304	return (hatm_close_vcc(sc, &v));
305}
306
307/*
308 * IOCTL handler
309 */
310int
311hatm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
312{
313	struct ifreq *ifr = (struct ifreq *)data;
314	struct ifaddr *ifa = (struct ifaddr *)data;
315	struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc;
316	struct atmio_vcctable *vtab;
317	int error = 0;
318
319	switch (cmd) {
320
321	  case SIOCSIFADDR:
322		mtx_lock(&sc->mtx);
323		ifp->if_flags |= IFF_UP;
324		if (!(ifp->if_flags & IFF_RUNNING))
325			hatm_initialize(sc);
326		switch (ifa->ifa_addr->sa_family) {
327
328#ifdef INET
329		  case AF_INET:
330		  case AF_INET6:
331			ifa->ifa_rtrequest = atm_rtrequest;
332			break;
333#endif
334		  default:
335			break;
336		}
337		mtx_unlock(&sc->mtx);
338		break;
339
340	  case SIOCSIFFLAGS:
341		mtx_lock(&sc->mtx);
342		if (ifp->if_flags & IFF_UP) {
343			if (!(ifp->if_flags & IFF_RUNNING)) {
344				hatm_initialize(sc);
345			}
346		} else {
347			if (ifp->if_flags & IFF_RUNNING) {
348				hatm_stop(sc);
349			}
350		}
351		mtx_unlock(&sc->mtx);
352		break;
353
354	  case SIOCGIFMEDIA:
355	  case SIOCSIFMEDIA:
356		error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
357		break;
358
359	case SIOCSIFMTU:
360		/*
361		 * Set the interface MTU.
362		 */
363		if (ifr->ifr_mtu > ATMMTU)
364			error = EINVAL;
365		else
366			ifp->if_mtu = ifr->ifr_mtu;
367		break;
368
369	  case SIOCATMGVCCS:
370		/* return vcc table */
371		vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
372		    HE_MAX_VCCS, sc->open_vccs, &sc->mtx, 1);
373		error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) +
374		    vtab->count * sizeof(vtab->vccs[0]));
375		free(vtab, M_DEVBUF);
376		break;
377
378	  case SIOCATMENA:	/* NATM internal use */
379		error = hatm_open_vcc1(sc, (struct atm_pseudoioctl *)data);
380		break;
381
382	  case SIOCATMDIS:	/* NATM internal use */
383		error = hatm_close_vcc1(sc, (struct atm_pseudoioctl *)data);
384		break;
385
386	  case SIOCATMGETVCCS:	/* netgraph internal use */
387		vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
388		    HE_MAX_VCCS, sc->open_vccs, &sc->mtx, 0);
389		if (vtab == NULL) {
390			error = ENOMEM;
391			break;
392		}
393		*(void **)data = vtab;
394		break;
395
396	  case SIOCATMOPENVCC:		/* netgraph/harp internal use */
397		error = hatm_open_vcc(sc, (struct atmio_openvcc *)data);
398		break;
399
400	  case SIOCATMCLOSEVCC:		/* netgraph and HARP internal use */
401		error = hatm_close_vcc(sc, (struct atmio_closevcc *)data);
402		break;
403
404	  default:
405		DBG(sc, IOCTL, ("cmd=%08lx arg=%p", cmd, data));
406		error = EINVAL;
407		break;
408	}
409
410	return (error);
411}
412
413static int
414hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS)
415{
416	int error;
417	int tmp;
418
419	tmp = hatm_natm_traffic;
420	error = sysctl_handle_int(oidp, &tmp, 0, req);
421	if (error != 0 || req->newptr == NULL)
422		return (error);
423
424	if (tmp != ATMIO_TRAFFIC_UBR && tmp != ATMIO_TRAFFIC_CBR)
425		return (EINVAL);
426
427	hatm_natm_traffic = tmp;
428	return (0);
429}
430