/***
|
*
|
* rtmac_disc.c
|
*
|
* rtmac - real-time networking media access control subsystem
|
* Copyright (C) 2002 Marc Kleine-Budde <kleine-budde@gmx.de>,
|
* 2003, 2004 Jan Kiszka <Jan.Kiszka@web.de>
|
*
|
* This program is free software; you can redistribute it and/or modify
|
* it under the terms of the GNU General Public License as published by
|
* the Free Software Foundation; either version 2 of the License, or
|
* (at your option) any later version.
|
*
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
*
|
* You should have received a copy of the GNU General Public License
|
* along with this program; if not, write to the Free Software
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
*
|
*/
|
|
#include <linux/module.h>
|
#include <linux/errno.h>
|
#include <linux/slab.h>
|
#include <linux/netdevice.h>
|
#include <linux/mutex.h>
|
|
#include <rtnet_internal.h>
|
#include <rtmac/rtmac_disc.h>
|
#include <rtmac/rtmac_proc.h>
|
#include <rtmac/rtmac_vnic.h>
|
|
static DEFINE_MUTEX(disc_list_lock);
|
static LIST_HEAD(disc_list);
|
|
/***
|
* rtmac_disc_attach
|
*
|
* @rtdev attaches a discipline to a device
|
* @disc discipline to attach
|
*
|
* 0 success
|
* -EBUSY other discipline active
|
* -ENOMEM could not allocate memory
|
*
|
* Note: must be called with rtdev->nrt_lock acquired
|
*/
|
int rtmac_disc_attach(struct rtnet_device *rtdev, struct rtmac_disc *disc)
|
{
|
int ret;
|
struct rtmac_priv *priv;
|
|
RTNET_ASSERT(rtdev != NULL, return -EINVAL;);
|
RTNET_ASSERT(disc != NULL, return -EINVAL;);
|
RTNET_ASSERT(disc->attach != NULL, return -EINVAL;);
|
|
if (rtdev->mac_disc) {
|
printk("RTmac: another discipline for rtdev '%s' active.\n",
|
rtdev->name);
|
return -EBUSY;
|
}
|
|
if (rtdev->flags & IFF_LOOPBACK)
|
return -EINVAL;
|
|
if (!try_module_get(disc->owner))
|
return -EIDRM;
|
|
if (!rtdev_reference(rtdev)) {
|
ret = -EIDRM;
|
goto err_module_put;
|
}
|
|
/* alloc memory */
|
priv = kmalloc(sizeof(struct rtmac_priv) + disc->priv_size, GFP_KERNEL);
|
if (!priv) {
|
printk("RTmac: kmalloc returned NULL for rtmac!\n");
|
return -ENOMEM;
|
}
|
priv->orig_start_xmit = rtdev->start_xmit;
|
|
/* call attach function of discipline */
|
ret = disc->attach(rtdev, priv->disc_priv);
|
if (ret < 0)
|
goto err_kfree_priv;
|
|
/* now attach RTmac to device */
|
rtdev->mac_disc = disc;
|
rtdev->mac_priv = priv;
|
rtdev->start_xmit = disc->rt_packet_tx;
|
if (disc->get_mtu)
|
rtdev->get_mtu = disc->get_mtu;
|
rtdev->mac_detach = rtmac_disc_detach;
|
|
/* create the VNIC */
|
ret = rtmac_vnic_add(rtdev, disc->vnic_xmit);
|
if (ret < 0) {
|
printk("RTmac: Warning, VNIC creation failed for rtdev %s.\n",
|
rtdev->name);
|
goto err_disc_detach;
|
}
|
|
return 0;
|
|
err_disc_detach:
|
disc->detach(rtdev, priv->disc_priv);
|
err_kfree_priv:
|
kfree(priv);
|
rtdev_dereference(rtdev);
|
err_module_put:
|
module_put(disc->owner);
|
return ret;
|
}
|
|
/***
|
* rtmac_disc_detach
|
*
|
* @rtdev detaches a discipline from a device
|
*
|
* 0 success
|
* -1 discipline has no detach function
|
* -EINVAL called with rtdev=NULL
|
* -ENODEV no discipline active on dev
|
*
|
* Note: must be called with rtdev->nrt_lock acquired
|
*/
|
int rtmac_disc_detach(struct rtnet_device *rtdev)
|
{
|
int ret;
|
struct rtmac_disc *disc;
|
struct rtmac_priv *priv;
|
|
RTNET_ASSERT(rtdev != NULL, return -EINVAL;);
|
|
disc = rtdev->mac_disc;
|
if (!disc)
|
return -ENODEV;
|
|
RTNET_ASSERT(disc->detach != NULL, return -EINVAL;);
|
|
priv = rtdev->mac_priv;
|
RTNET_ASSERT(priv != NULL, return -EINVAL;);
|
|
ret = rtmac_vnic_unregister(rtdev);
|
if (ret < 0)
|
return ret;
|
|
/* call release function of discipline */
|
ret = disc->detach(rtdev, priv->disc_priv);
|
if (ret < 0)
|
return ret;
|
|
rtmac_vnic_cleanup(rtdev);
|
|
/* restore start_xmit and get_mtu */
|
rtdev->start_xmit = priv->orig_start_xmit;
|
rtdev->get_mtu = rt_hard_mtu;
|
|
/* remove pointers from rtdev */
|
rtdev->mac_disc = NULL;
|
rtdev->mac_priv = NULL;
|
rtdev->mac_detach = NULL;
|
|
rtdev_dereference(rtdev);
|
|
kfree(priv);
|
|
module_put(disc->owner);
|
|
return 0;
|
}
|
|
static struct rtmac_disc *rtmac_get_disc_by_name(const char *name)
|
{
|
struct list_head *disc;
|
|
mutex_lock(&disc_list_lock);
|
|
list_for_each (disc, &disc_list) {
|
if (strcmp(((struct rtmac_disc *)disc)->name, name) == 0) {
|
mutex_unlock(&disc_list_lock);
|
return (struct rtmac_disc *)disc;
|
}
|
}
|
|
mutex_unlock(&disc_list_lock);
|
|
return NULL;
|
}
|
|
int __rtmac_disc_register(struct rtmac_disc *disc, struct module *module)
|
{
|
int ret;
|
|
RTNET_ASSERT(disc != NULL, return -EINVAL;);
|
RTNET_ASSERT(disc->name != NULL, return -EINVAL;);
|
RTNET_ASSERT(disc->rt_packet_tx != NULL, return -EINVAL;);
|
RTNET_ASSERT(disc->nrt_packet_tx != NULL, return -EINVAL;);
|
RTNET_ASSERT(disc->attach != NULL, return -EINVAL;);
|
RTNET_ASSERT(disc->detach != NULL, return -EINVAL;);
|
|
disc->owner = module;
|
|
if (rtmac_get_disc_by_name(disc->name) != NULL) {
|
printk("RTmac: discipline '%s' already registered!\n",
|
disc->name);
|
return -EBUSY;
|
}
|
|
ret = rtnet_register_ioctls(&disc->ioctls);
|
if (ret < 0)
|
return ret;
|
|
#ifdef CONFIG_XENO_OPT_VFILE
|
ret = rtmac_disc_proc_register(disc);
|
if (ret < 0) {
|
rtnet_unregister_ioctls(&disc->ioctls);
|
return ret;
|
}
|
#endif /* CONFIG_XENO_OPT_VFILE */
|
|
mutex_lock(&disc_list_lock);
|
|
list_add(&disc->list, &disc_list);
|
|
mutex_unlock(&disc_list_lock);
|
|
return 0;
|
}
|
|
void rtmac_disc_deregister(struct rtmac_disc *disc)
|
{
|
RTNET_ASSERT(disc != NULL, return;);
|
|
mutex_lock(&disc_list_lock);
|
|
list_del(&disc->list);
|
|
mutex_unlock(&disc_list_lock);
|
|
rtnet_unregister_ioctls(&disc->ioctls);
|
|
#ifdef CONFIG_XENO_OPT_VFILE
|
rtmac_disc_proc_unregister(disc);
|
#endif /* CONFIG_XENO_OPT_VFILE */
|
}
|
|
#ifdef CONFIG_XENO_OPT_VFILE
|
int rtnet_rtmac_disciplines_show(struct xnvfile_regular_iterator *it, void *d)
|
{
|
struct rtmac_disc *disc;
|
int err;
|
|
err = mutex_lock_interruptible(&disc_list_lock);
|
if (err < 0)
|
return err;
|
|
xnvfile_printf(it, "Name\t\tID\n");
|
|
list_for_each_entry (disc, &disc_list, list)
|
xnvfile_printf(it, "%-15s %04X\n", disc->name,
|
ntohs(disc->disc_type));
|
|
mutex_unlock(&disc_list_lock);
|
|
return 0;
|
}
|
#endif /* CONFIG_XENO_OPT_VFILE */
|