/*
 *
 * ===================================
 * HARP  |  Host ATM Research Platform
 * ===================================
 *
 *
 * This Host ATM Research Platform ("HARP") file (the "Software") is
 * made available by Network Computing Services, Inc. ("NetworkCS")
 * "AS IS".  NetworkCS does not provide maintenance, improvements or
 * support of any kind.
 *
 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
 * In no event shall NetworkCS be responsible for any damages, including
 * but not limited to consequential damages, arising from or relating to
 * any use of the Software or related support.
 *
 * Copyright 1994-1998 Network Computing Services, Inc.
 *
 * Copies of this Software may be made, however, the above copyright
 * notice must be reproduced on all copies.
 *
 *	@(#) $FreeBSD: src/sys/netatm/atm_if.c,v 1.5 1999/08/28 00:48:35 peter Exp $
 *
 */

/*
 * Core ATM Services
 * -----------------
 *
 * ATM interface management
 *
 */

#include <netatm/kern_include.h>

#ifndef lint
__RCSID("@(#) $FreeBSD: src/sys/netatm/atm_if.c,v 1.5 1999/08/28 00:48:35 peter Exp $");
#endif


#if (defined(BSD) && (BSD < 199506))
extern int		ifqmaxlen;
#endif

/*
 * Local functions
 */
static int	atm_physif_ioctl __P((int, caddr_t, caddr_t));
#if (defined(BSD) && (BSD >= 199306))
static int	atm_netif_rtdel __P((struct radix_node *, void *));
#endif
static int	atm_if_ioctl __P((struct ifnet *, u_long, caddr_t));
static int	atm_ifparse __P((char *, char *, int, int *));

/*
 * Local variables
 */
static int	(*atm_ifouttbl[AF_MAX+1])
			__P((struct ifnet *, KBuffer *, struct sockaddr *))
				= {NULL};


/*
 * Register an ATM physical interface
 * 
 * Each ATM device interface must register itself here upon completing
 * its internal initialization.  This applies to both linked and loaded
 * device drivers.  The interface must be registered before a signalling
 * manager can be attached.
 *
 * Arguments:
 *	cup	pointer to interface's common unit structure
 *	name	pointer to device name string
 *	sdp	pointer to interface's stack services
 *
 * Returns:
 *	0	registration successful
 *	errno	registration failed - reason indicated
 *
 */
int
atm_physif_register(cup, name, sdp)
	Cmn_unit		*cup;
	char			*name;
	struct stack_defn	*sdp;
{
	struct atm_pif	*pip;
	int		s;

	/*
	 * See if we need to be initialized
	 */
	if (!atm_init)
		atm_initialize();

	/*
	 * Make sure we're not already registered
	 */
	if (cup->cu_flags & CUF_REGISTER) {
		return (EALREADY);
	}

	s = splnet();

	/*
	 * Make sure an interface is only registered once
	 */
	for (pip = atm_interface_head; pip != NULL; pip = pip->pif_next) {
		if ((cup->cu_unit == pip->pif_unit) && 
		    (strcmp(name, pip->pif_name) == 0)) {
			(void) splx(s);
			return (EEXIST);
		}
	}

	/*
	 * Fill in physical interface parameters
	 */
	pip = &cup->cu_pif;
	pip->pif_name = name;
	pip->pif_unit = cup->cu_unit;
	pip->pif_flags = PIF_UP;
	pip->pif_services = sdp;
	pip->pif_ioctl = atm_physif_ioctl;

	/*
	 * Link in the interface and mark us registered
	 */
	LINK2TAIL(pip, struct atm_pif, atm_interface_head, pif_next);
	cup->cu_flags |= CUF_REGISTER;

	(void) splx(s);
	return (0);
}


/*
 * De-register an ATM physical interface
 * 
 * Each ATM interface must de-register itself before downing the interface.  
 * The interface's signalling manager will be detached and any network
 * interface and VCC control blocks will be freed.  
 *
 * Arguments:
 *	cup	pointer to interface's common unit structure
 *
 * Returns:
 *	0	de-registration successful
 *	errno	de-registration failed - reason indicated
 *
 */
int
atm_physif_deregister(cup)
	Cmn_unit	*cup;
{
	struct atm_pif	*pip = (struct atm_pif *)&cup->cu_pif;
	Cmn_vcc		*cvp;
	int	err;
	int	s = splnet();

	/*
	 * Detach and deregister, if needed
	 */
	if ((cup->cu_flags & CUF_REGISTER)) {

		/*
		 * Detach from signalling manager
		 */
		if (pip->pif_sigmgr != NULL) {
			err = atm_sigmgr_detach(pip);
			if (err && (err != ENOENT)) {
				(void) splx(s);
				return (err);
			}
		}

		/*
		 * Make sure signalling manager is detached
		 */
		if (pip->pif_sigmgr != NULL) {
			(void) splx(s);
			return (EBUSY);
		}

		/*
		 * Unlink interface
		 */
		UNLINK(pip, struct atm_pif, atm_interface_head, pif_next);

		cup->cu_flags &= ~CUF_REGISTER;
	}

	/*
	 * Free all of our network interfaces
	 */
	atm_physif_freenifs(pip);

	/*
	 * Free unit's vcc information
	 */
	cvp = cup->cu_vcc;
	while (cvp) {
		atm_free(cvp);
		cvp = cvp->cv_next;
	}
	cup->cu_vcc = (Cmn_vcc *)NULL;

	(void) splx(s);

	return (0);
}


/*
 * Free all network interfaces on a physical interface
 *
 * Arguments
 *	pip		pointer to physical interface structure
 *
 * Returns
 *	none
 *
 */
void
atm_physif_freenifs(pip)
	struct atm_pif	*pip;
{
	struct atm_nif	*nip = pip->pif_nif;
	int	s = splnet();

	while ( nip ) 
	{
		/*
		 * atm_nif_detach zeros pointers - save so we can
		 * walk the chain.
		 */
		struct atm_nif	*nipp = nip->nif_pnext;

		/*
		 * Clean up network i/f trails
		 */
		atm_nif_detach ( nip );
		atm_free ((caddr_t)nip);
		nip = nipp;
	}
	pip->pif_nif = (struct atm_nif *)NULL;

	(void) splx(s);

	return;
}


/*
 * Handle physical interface ioctl's
 *
 * See <netatm/atm_ioctl.h> for definitions.
 *
 * Called at splnet.
 *
 * Arguments:
 *	code			Ioctl function (sub)code
 *	data			Data block. On input contains command,
 *					on output, contains results
 *	arg			Optional code specific arguments
 *
 * Returns:
 *	0			Request processed successfully
 *	errno			Request failed - reason code
 *
 */
static int
atm_physif_ioctl(code, data, arg)
	int	code;
	caddr_t	data;
	caddr_t	arg;
{
	struct atminfreq	*aip = (struct atminfreq *)data;
	struct atmsetreq	*asr = (struct atmsetreq *)data;
	struct atm_pif		*pip;
	struct atm_nif		*nip;
	struct sigmgr		*smp;
	struct siginst		*sip;
	struct ifnet		*ifp;
	Cmn_unit		*cup;
	Atm_config		*acp;
	caddr_t			buf = aip->air_buf_addr;
	struct air_phy_stat_rsp	*apsp;
	struct air_int_rsp	apr;
	struct air_netif_rsp	anr;
	struct air_cfg_rsp	acr;
	int			count, len, buf_len = aip->air_buf_len;
	int			err = 0;
	char			ifname[2*IFNAMSIZ];
#if (defined(BSD) && (BSD >= 199103))
	struct ifaddr		*ifa;
	struct in_ifaddr	*ia;
	struct sockaddr_dl	*sdl;
#endif
 

	switch ( aip->air_opcode ) {

	case AIOCS_INF_INT:
		/*
		 * Get physical interface information
		 */
		aip = (struct atminfreq *)data;
		pip = (struct atm_pif *)arg;

		/*
		 * Make sure there's room in user buffer
		 */
		if (aip->air_buf_len < sizeof(apr)) {
			err = ENOSPC;
			break;
		}

		/*
		 * Fill in info to be returned
		 */
		KM_ZERO((caddr_t)&apr, sizeof(apr));
		smp = pip->pif_sigmgr;
		sip = pip->pif_siginst;
		(void) snprintf(apr.anp_intf, sizeof(apr.anp_intf),
			"%s%d", pip->pif_name, pip->pif_unit );
		if ( pip->pif_nif )
		{
			strcpy(apr.anp_nif_pref, pip->pif_nif->nif_if.if_name);

			nip = pip->pif_nif;
			while ( nip ) {
				apr.anp_nif_cnt++;
				nip = nip->nif_pnext;
			}
		}
		if (sip) {
			ATM_ADDR_COPY(&sip->si_addr, &apr.anp_addr);
			ATM_ADDR_COPY(&sip->si_subaddr, &apr.anp_subaddr);
			apr.anp_sig_proto = smp->sm_proto;
			apr.anp_sig_state = sip->si_state;
		}

		/*
		 * Copy data to user buffer
		 */
		err = copyout((caddr_t)&apr, aip->air_buf_addr, sizeof(apr));
		if (err)
			break;

		/*
		 * Update buffer pointer/count
		 */
		aip->air_buf_addr += sizeof(apr);
		aip->air_buf_len -= sizeof(apr);
		break;

	case AIOCS_INF_NIF:
		/*
		 * Get network interface information
		 */
		aip = (struct atminfreq *)data;
		nip = (struct atm_nif *)arg;
		ifp = &nip->nif_if;
		pip = nip->nif_pif;

		/*
		 * Make sure there's room in user buffer
		 */
		if (aip->air_buf_len < sizeof(anr)) {
			err = ENOSPC;
			break;
		}

		/*
		 * Fill in info to be returned
		 */
		KM_ZERO((caddr_t)&anr, sizeof(anr));
		(void) snprintf(anr.anp_intf, sizeof(anr.anp_intf),
		    "%s%d", ifp->if_name, ifp->if_unit);
		IFP_TO_IA(ifp, ia);
		if (ia) {
			anr.anp_proto_addr = *ia->ia_ifa.ifa_addr;
		}
		(void) snprintf(anr.anp_phy_intf, sizeof(anr.anp_phy_intf),
		    "%s%d", pip->pif_name, pip->pif_unit);

		/*
		 * Copy data to user buffer
		 */
		err = copyout((caddr_t)&anr, aip->air_buf_addr, sizeof(anr));
		if (err)
			break;

		/*
		 * Update buffer pointer/count
		 */
		aip->air_buf_addr += sizeof(anr);
		aip->air_buf_len -= sizeof(anr);
		break;

	case AIOCS_INF_PIS:
		/*
		 * Get per interface statistics
		 */
		pip = (struct atm_pif *)arg;
		if ( pip == NULL )
			return ( ENXIO );
		snprintf ( ifname, sizeof(ifname),
		    "%s%d", pip->pif_name, pip->pif_unit );

		/*
		 * Cast response into users buffer
		 */
		apsp = (struct air_phy_stat_rsp *)buf;

		/*
		 * Sanity check
		 */
		len = sizeof ( struct air_phy_stat_rsp );
		if ( buf_len < len )
			return ( ENOSPC );

		/*
		 * Copy interface name into response
		 */
		if ((err = copyout ( ifname, apsp->app_intf, IFNAMSIZ)) != 0)
			break;

		/*
		 * Copy counters
		 */
		if ((err = copyout(&pip->pif_ipdus, &apsp->app_ipdus,
		    len - sizeof(apsp->app_intf))) != 0)
			break;

		/*
		 * Adjust buffer elements
		 */
		buf += len;
		buf_len -= len;

		aip->air_buf_addr = buf;
		aip->air_buf_len = buf_len;
		break;

	case AIOCS_SET_NIF:
		/*
		 * Set NIF - allow user to configure 1 or more logical
		 *	interfaces per physical interface.
		 */

		/*
		 * Get pointer to physical interface structure from
		 * ioctl argument.
		 */
		pip = (struct atm_pif *)arg;
		cup = (Cmn_unit *)pip;

		/*
		 * Sanity check - are we already connected to something?
		 */
		if ( pip->pif_sigmgr )
		{
			err = EBUSY;
			break;
		}

		/*
		 * Free any previously allocated NIFs
		 */
		atm_physif_freenifs(pip);

		/*
		 * Add list of interfaces
		 */
		for ( count = 0; count < asr->asr_nif_cnt; count++ )
		{
			nip = (struct atm_nif *)atm_allocate(cup->cu_nif_pool);
			if ( nip == NULL )
			{
				/*
				 * Destroy any successful nifs
				 */
				atm_physif_freenifs(pip);
				err = ENOMEM;
				break;
			}

			nip->nif_pif = pip;
			ifp = &nip->nif_if;

			strcpy ( nip->nif_name, asr->asr_nif_pref );
			nip->nif_sel = count;

			ifp->if_name = nip->nif_name;
			ifp->if_unit = count;
			ifp->if_mtu = ATM_NIF_MTU;
			ifp->if_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
			ifp->if_output = atm_ifoutput;
			ifp->if_ioctl = atm_if_ioctl;
			ifp->if_snd.ifq_maxlen = ifqmaxlen;
#if (defined(BSD) && (BSD >= 199103))
			/*
			 * Set if_type and if_baudrate
			 */
			ifp->if_type = IFT_ATM;
			switch ( cup->cu_config.ac_media ) {
			case MEDIA_TAXI_100:
				ifp->if_baudrate = 100000000;
				break;
			case MEDIA_TAXI_140:
				ifp->if_baudrate = 140000000;
				break;
			case MEDIA_OC3C:
			case MEDIA_OC12C:
			case MEDIA_UTP155:
				ifp->if_baudrate = 155000000;
				break;
			}
#endif

			if ((err = atm_nif_attach(nip)) != 0) {
				atm_free ( (caddr_t)nip );

				/*
				 * Destroy any successful nifs
				 */
				atm_physif_freenifs(pip);
				break;
			}
#if (defined(BSD) && (BSD >= 199103))
			/*
			 * Set macaddr in <Link> address
			 */
			ifp->if_addrlen = 6;
			ifa = ifnet_addrs[ifp->if_index - 1];
			if ( ifa ) {
				sdl = (struct sockaddr_dl *)
					ifa->ifa_addr;
				sdl->sdl_type = IFT_ETHER;
				sdl->sdl_alen = ifp->if_addrlen;
				bcopy ( (caddr_t)&cup->cu_config.ac_macaddr,
					LLADDR(sdl), ifp->if_addrlen );
			}
#endif
		}
		break;

	case AIOCS_INF_CFG:
		/*
		 * Get adapter configuration information
		 */
		aip = (struct atminfreq *)data;
		pip = (struct atm_pif *)arg;
		cup = (Cmn_unit *)pip;
		acp = &cup->cu_config;

		/*
		 * Make sure there's room in user buffer
		 */
		if (aip->air_buf_len < sizeof(acr)) {
			err = ENOSPC;
			break;
		}

		/*
		 * Fill in info to be returned
		 */
		KM_ZERO((caddr_t)&acr, sizeof(acr));
		(void) snprintf(acr.acp_intf, sizeof(acr.acp_intf),
		    "%s%d", pip->pif_name, pip->pif_unit);
		KM_COPY((caddr_t)acp, (caddr_t)&acr.acp_cfg,
				sizeof(Atm_config));

		/*
		 * Copy data to user buffer
		 */
		err = copyout((caddr_t)&acr, aip->air_buf_addr,
				sizeof(acr));
		if (err)
			break;

		/*
		 * Update buffer pointer/count
		 */
		aip->air_buf_addr += sizeof(acr);
		aip->air_buf_len -= sizeof(acr);
		break;

	case AIOCS_INF_VST:
		/*
		 * Pass off to device-specific handler
		 */
		cup = (Cmn_unit *)arg;
		if (cup == NULL)
			err = ENXIO;
		else
			err = (*cup->cu_ioctl)(code, data, arg);
		break;

	default:
		err = ENOSYS;
	}

	return ( err );
}


/*
 * Register a Network Convergence Module
 * 
 * Each ATM network convergence module must register itself here before
 * it will receive network interface status notifications. 
 *
 * Arguments:
 *	ncp	pointer to network convergence definition structure
 *
 * Returns:
 *	0	registration successful
 *	errno	registration failed - reason indicated
 *
 */
int
atm_netconv_register(ncp)
	struct atm_ncm	*ncp;
{
	struct atm_ncm	*tdp;
	int		s = splnet();

	/*
	 * See if we need to be initialized
	 */
	if (!atm_init)
		atm_initialize();

	/*
	 * Validate protocol family
	 */
	if (ncp->ncm_family > AF_MAX) {
		(void) splx(s);
		return (EINVAL);
	}

	/*
	 * Ensure no duplicates
	 */
	for (tdp = atm_netconv_head; tdp != NULL; tdp = tdp->ncm_next) {
		if (tdp->ncm_family == ncp->ncm_family) {
			(void) splx(s);
			return (EEXIST);
		}
	}

	/*
	 * Add module to list
	 */
	LINK2TAIL(ncp, struct atm_ncm, atm_netconv_head, ncm_next);

	/*
	 * Add new interface output function
	 */
	atm_ifouttbl[ncp->ncm_family] = ncp->ncm_ifoutput;

	(void) splx(s);
	return (0);
}


/*
 * De-register an ATM Network Convergence Module
 * 
 * Each ATM network convergence provider must de-register its registered 
 * service(s) before terminating.  Specifically, loaded kernel modules
 * must de-register their services before unloading themselves.
 *
 * Arguments:
 *	ncp	pointer to network convergence definition structure
 *
 * Returns:
 *	0	de-registration successful 
 *	errno	de-registration failed - reason indicated
 *
 */
int
atm_netconv_deregister(ncp)
	struct atm_ncm	*ncp;
{
	int	found, s = splnet();

	/*
	 * Remove module from list
	 */
	UNLINKF(ncp, struct atm_ncm, atm_netconv_head, ncm_next, found);

	if (!found) {
		(void) splx(s);
		return (ENOENT);
	}

	/*
	 * Remove module's interface output function
	 */
	atm_ifouttbl[ncp->ncm_family] = NULL;

	(void) splx(s);
	return (0);
}


/*
 * Attach an ATM Network Interface
 * 
 * Before an ATM network interface can be used by the system, the owning
 * device interface must attach the network interface using this function.
 * The physical interface for this network interface must have been previously
 * registered (using atm_interface_register).  The network interface will be
 * added to the kernel's interface list and to the physical interface's list.
 * The caller is responsible for initializing the control block fields.
 *
 * Arguments:
 *	nip	pointer to atm network interface control block
 *
 * Returns:
 *	0	attach successful
 *	errno	attach failed - reason indicated
 *
 */
int
atm_nif_attach(nip)
	struct atm_nif	*nip;
{
	struct atm_pif	*pip, *pip2;
	struct ifnet	*ifp;
	struct atm_ncm	*ncp;
	int		s;

	ifp = &nip->nif_if;
	pip = nip->nif_pif;

	s = splimp();

	/*
	 * Verify physical interface is registered
	 */
	for (pip2 = atm_interface_head; pip2 != NULL; pip2 = pip2->pif_next) {
		if (pip == pip2)
			break;
	}
	if ((pip == NULL) || (pip2 == NULL)) {
		(void) splx(s);
		return (EFAULT);
	}

	/*
	 * Add to system interface list 
	 */
	if_attach(ifp);

	/*
	 * Add to physical interface list
	 */
	LINK2TAIL(nip, struct atm_nif, pip->pif_nif, nif_pnext);

	/*
	 * Notify network convergence modules of new network i/f
	 */
	for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) {
		int	err;

		err = (*ncp->ncm_stat)(NCM_ATTACH, nip, 0);
		if (err) {
			atm_nif_detach(nip);
			(void) splx(s);
			return (err);
		}
	}

	(void) splx(s);
	return (0);
}


/*
 * Detach an ATM Network Interface
 * 
 * Before an ATM network interface control block can be freed, all kernel
 * references to/from this block must be released.  This function will delete
 * all routing references to the interface and free all interface addresses
 * for the interface.  The network interface will then be removed from the
 * kernel's interface list and from the owning physical interface's list.
 * The caller is responsible for free'ing the control block.
 *
 * Arguments:
 *	nip	pointer to atm network interface control block
 *
 * Returns:
 *	none
 *
 */
void
atm_nif_detach(nip)
	struct atm_nif	*nip;
{
	struct atm_ncm	*ncp;
	int		s, i;
	struct ifnet	*ifp = &nip->nif_if;
	struct ifaddr	*ifa;
	struct in_ifaddr	*ia;
	struct radix_node_head	*rnh;


	s = splimp();

	/*
	 * Notify convergence modules of network i/f demise
	 */
	for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) {
		(void) (*ncp->ncm_stat)(NCM_DETACH, nip, 0);
	}

	/*
	 * Mark interface down
	 */
	if_down(ifp);

	/*
	 * Free all interface routes and addresses
	 */
	while (1) {
		IFP_TO_IA(ifp, ia);
		if (ia == NULL)
			break;

		/* Delete interface route */
		in_ifscrub(ifp, ia);

		/* Remove interface address from queues */
		ifa = &ia->ia_ifa;
		TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
		TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link);

		/* Free interface address */
		IFAFREE(ifa);
	}

	/*
	 * Delete all remaining routes using this interface
	 * Unfortuneatly the only way to do this is to slog through
	 * the entire routing table looking for routes which point
	 * to this interface...oh well...
	 */
	for (i = 1; i <= AF_MAX; i++) {
		if ((rnh = rt_tables[i]) == NULL)
			continue;
		(void) rnh->rnh_walktree(rnh, atm_netif_rtdel, ifp);
	}

	/*
	 * Remove from system interface list (ie. if_detach())
	 */
	TAILQ_REMOVE(&ifnet, ifp, if_link);

	/*
	 * Remove from physical interface list
	 */
	UNLINK(nip, struct atm_nif, nip->nif_pif->pif_nif, nif_pnext);

	(void) splx(s);
}


/*
 * Delete Routes for a Network Interface
 * 
 * Called for each routing entry via the rnh->rnh_walktree() call above
 * to delete all route entries referencing a detaching network interface.
 *
 * Arguments:
 *	rn	pointer to node in the routing table
 *	arg	argument passed to rnh->rnh_walktree() - detaching interface
 *
 * Returns:
 *	0	successful
 *	errno	failed - reason indicated
 *
 */
static int
atm_netif_rtdel(rn, arg)
	struct radix_node	*rn;
	void			*arg;
{
	struct rtentry	*rt = (struct rtentry *)rn;
	struct ifnet	*ifp = arg;
	int		err;

	if (rt->rt_ifp == ifp) {

		/*
		 * Protect (sorta) against walktree recursion problems
		 * with cloned routes
		 */
		if ((rt->rt_flags & RTF_UP) == 0)
			return (0);

		err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
				rt_mask(rt), rt->rt_flags,
				(struct rtentry **) NULL);
		if (err) {
			log(LOG_WARNING, "atm_netif_rtdel: error %d\n", err);
		}
	}

	return (0);
}


/*
 * Set an ATM Network Interface address
 * 
 * This is called from a device interface when processing an SIOCSIFADDR
 * ioctl request.  We just notify all convergence modules of the new address
 * and hope everyone has non-overlapping interests, since if someone reports
 * an error we don't go back and tell everyone to undo the change.
 *
 * Arguments:
 *	nip	pointer to atm network interface control block
 *	ifa	pointer to new interface address
 *
 * Returns:
 *	0	set successful
 *	errno	set failed - reason indicated
 *
 */
int
atm_nif_setaddr(nip, ifa)
	struct atm_nif	*nip;
	struct ifaddr	*ifa;
{
	struct atm_ncm	*ncp;
	int	err = 0, s = splnet();

	/*
	 * Notify convergence modules of network i/f change
	 */
	for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) {
		err = (*ncp->ncm_stat)(NCM_SETADDR, nip, (int)ifa);
		if (err)
			break;
	}
	(void) splx(s);

	return (err);
}


/*
 * ATM Interface Packet Output
 * 
 * All ATM network interfaces must have their ifnet if_output address set to
 * this function.  Since no existing network layer code is to be modified 
 * for ATM support, this function serves as the hook to allow network output
 * packets to be assigned to their proper outbound VCC.  Each network address
 * family which is to be supported over ATM must be assigned an output
 * packet processing function via atm_netconv_register().
 *
 * Arguments:
 *	ifp	pointer to ifnet structure
 *	m	pointer to packet buffer chain to be output
 *	dst	pointer to packet's network destination address
 *
 * Returns:
 *	0	packet queued to interface
 *	errno	output failed - reason indicated
 *
 */
int
#if (defined(BSD) && (BSD >= 199103))
atm_ifoutput(ifp, m, dst, rt)
#else
atm_ifoutput(ifp, m, dst)
#endif
	struct ifnet	*ifp;
	KBuffer		*m;
	struct sockaddr	*dst;
#if (defined(BSD) && (BSD >= 199103))
	struct rtentry	*rt;
#endif
{
	u_short		fam = dst->sa_family;
	int		(*func)__P((struct ifnet *, KBuffer *,
					struct sockaddr *));

	/*
	 * Validate address family
	 */
	if (fam > AF_MAX) {
		KB_FREEALL(m);
		return (EAFNOSUPPORT);
	}

	/*
	 * Hand packet off for dst-to-VCC mapping
	 */
	func = atm_ifouttbl[fam];
	if (func == NULL) {
		KB_FREEALL(m);
		return (EAFNOSUPPORT);
	}
	return ((*func)(ifp, m, dst));
}


/*
 * Handle interface ioctl requests. 
 *
 * Arguments:
 *	ifp		pointer to network interface structure
 *	cmd		IOCTL cmd
 *	data		arguments to/from ioctl
 *
 * Returns:
 *	error		errno value
 */
static int
atm_if_ioctl(ifp, cmd, data)
	struct ifnet *ifp;
	u_long	cmd;
	caddr_t data;
{
	register struct ifreq *ifr = (struct ifreq *)data;
	struct atm_nif	*nip = (struct atm_nif *)ifp;
	int	error = 0;
	int	s = splnet();

	switch ( cmd )
	{
	case SIOCGIFADDR:
		KM_COPY ( (caddr_t)&(nip->nif_pif->pif_macaddr),
			(caddr_t)ifr->ifr_addr.sa_data, 
			sizeof(struct mac_addr) );
		break;

	case SIOCSIFADDR:
		error = atm_nif_setaddr ( nip, (struct ifaddr *)data);
		ifp->if_flags |= IFF_UP | IFF_RUNNING | IFF_BROADCAST;
		break;

	case SIOCGIFFLAGS:
		*(short *)data = ifp->if_flags;
		break;

	case SIOCSIFFLAGS:
		break;

	default:
		error = EINVAL;
		break;
	}

	(void) splx(s);
	return ( error );
}


/*
 * Parse interface name
 * 
 * Parses an interface name string into a name and a unit component.
 *
 * Arguments:
 *	name	pointer to interface name string
 *	namep	address to store interface name
 *	size	size available at namep
 *	unitp	address to store interface unit number
 *
 * Returns:
 *	0 	name parsed
 *	else	parse error
 *
 */
static int
atm_ifparse(name, namep, size, unitp)
	char		*name;
	char		*namep;
	int		size;
	int		*unitp;
{
	char		*cp, *np;
	int		len = 0, unit = 0;

	/*
	 * Separate supplied string into name and unit parts.
	 */
	cp = name;
	np = namep;
	while (*cp) {
		if (*cp >= '0' && *cp <= '9')
			break;
		if (++len >= size)
			return (-1);
		*np++ = *cp++;
	}
	*np = '\0';
	while (*cp && *cp >= '0' && *cp <= '9')
		unit = 10 * unit + *cp++ - '0';

	*unitp = unit;

	return (0);
}


/*
 * Locate ATM physical interface via name
 * 
 * Uses the supplied interface name string to locate a registered
 * ATM physical interface.
 *
 * Arguments:
 *	name	pointer to interface name string
 *
 * Returns:
 *	0 	interface not found
 *	else	pointer to atm physical interface structure
 *
 */
struct atm_pif *
atm_pifname(name)
	char		*name;
{
	struct atm_pif	*pip;
	char		n[IFNAMSIZ];
	int		unit;

	/*
	 * Break down name
	 */
	if (atm_ifparse(name, n, sizeof(n), &unit))
		return ((struct atm_pif *)0);

	/*
	 * Look for the physical interface
	 */
	for (pip = atm_interface_head; pip; pip = pip->pif_next) {
		if ((pip->pif_unit == unit) && (strcmp(pip->pif_name, n) == 0))
			break;
	}

	return (pip);
}


/*
 * Locate ATM network interface via name
 * 
 * Uses the supplied interface name string to locate an ATM network interface.
 *
 * Arguments:
 *	name	pointer to interface name string
 *
 * Returns:
 *	0 	interface not found
 *	else	pointer to atm network interface structure
 *
 */
struct atm_nif *
atm_nifname(name)
	char		*name;
{
	struct atm_pif	*pip;
	struct atm_nif	*nip;
	char		n[IFNAMSIZ];
	int		unit;

	/*
	 * Break down name
	 */
	if (atm_ifparse(name, n, sizeof(n), &unit))
		return ((struct atm_nif *)0);

	/*
	 * Search thru each physical interface
	 */
	for (pip = atm_interface_head; pip; pip = pip->pif_next) {
		/*
		 * Looking for network interface
		 */
		for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) {
			struct ifnet	*ifp = (struct ifnet *)nip;
			if ((ifp->if_unit == unit) && 
			    (strcmp(ifp->if_name, n) == 0))
				return (nip);
		}
	}
	return (NULL);
}

