Commit 74408ad7 authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN: New file for net interface config and basic setup

Add a new file isdn_net_lib.c, where code which is shared among different
kind of network interface will gradually migrate to.

For now, move the ioctl config code out of isdn_{common,net}.c there,
and the basic register_netdev() + associated methods.
parent e2d5b146
...@@ -11,9 +11,10 @@ obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o ...@@ -11,9 +11,10 @@ obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
# Multipart objects. # Multipart objects.
isdn-objs := isdn_net.o isdn_tty.o \ isdn-objs := isdn_net.o isdn_net_lib.o \
isdn_v110.o isdn_common.o \ isdn_ciscohdlck.o \
isdn_ciscohdlck.o isdn_tty.o isdn_v110.o \
isdn_common.o \
# Optional parts of multipart objects. # Optional parts of multipart objects.
......
...@@ -1010,19 +1010,6 @@ static int ...@@ -1010,19 +1010,6 @@ static int
isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{ {
int ret; int ret;
union iocpar {
char name[10];
char bname[22];
isdn_ioctl_struct iocts;
isdn_net_ioctl_phone phone;
isdn_net_ioctl_cfg cfg;
} iocpar;
#define name iocpar.name
#define bname iocpar.bname
#define iocts iocpar.iocts
#define phone iocpar.phone
#define cfg iocpar.cfg
switch (cmd) { switch (cmd) {
case IIOCGETDVR: case IIOCGETDVR:
...@@ -1044,26 +1031,11 @@ isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ...@@ -1044,26 +1031,11 @@ isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
} else } else
return -EINVAL; return -EINVAL;
break; break;
#ifdef CONFIG_NETDEVICES
case IIOCNETGPN: case IIOCNETGPN:
/* Get peer phone number of a connected return isdn_net_ioctl(inode, file, cmd, arg);
* isdn network interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
} else
return -EINVAL;
#endif
default: default:
return -EINVAL; return -EINVAL;
} }
#undef name
#undef bname
#undef iocts
#undef phone
#undef cfg
} }
static struct file_operations isdn_status_fops = static struct file_operations isdn_status_fops =
...@@ -1221,19 +1193,15 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ...@@ -1221,19 +1193,15 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
int ret; int ret;
int i; int i;
char *p; char *p;
union iocpar { /* save stack space */
char name[10]; union {
char bname[20]; char bname[20];
isdn_ioctl_struct iocts; isdn_ioctl_struct iocts;
isdn_net_ioctl_phone phone;
isdn_net_ioctl_cfg cfg;
} iocpar; } iocpar;
#define name iocpar.name
#define bname iocpar.bname
#define iocts iocpar.iocts #define iocts iocpar.iocts
#define phone iocpar.phone #define bname iocpar.bname
#define cfg iocpar.cfg
/* /*
* isdn net devices manage lots of configuration variables as linked lists. * isdn net devices manage lots of configuration variables as linked lists.
* Those lists must only be manipulated from user space. Some of the ioctl's * Those lists must only be manipulated from user space. Some of the ioctl's
...@@ -1242,134 +1210,19 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ...@@ -1242,134 +1210,19 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
* are serialized by means of a semaphore. * are serialized by means of a semaphore.
*/ */
switch (cmd) { switch (cmd) {
case IIOCNETDWRSET:
printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
return(-EINVAL);
case IIOCNETLCR:
printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
return -ENODEV;
#ifdef CONFIG_NETDEVICES
case IIOCNETAIF: case IIOCNETAIF:
/* Add a network-interface */
if (copy_from_user(name, (char *) arg, sizeof(name) - 1))
return -EFAULT;
name[sizeof(name)-1] = 0;
ret = down_interruptible(&dev->sem);
if (ret)
return ret;
ret = isdn_net_new(name, NULL);
up(&dev->sem);
return ret;
case IIOCNETASL: case IIOCNETASL:
/* Add a slave to a network-interface */
if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1))
return -EFAULT;
bname[sizeof(bname)-1] = 0;
ret = down_interruptible(&dev->sem);
if (ret)
return ret;
ret = isdn_net_newslave(bname);
up(&dev->sem);
return ret;
case IIOCNETDIF: case IIOCNETDIF:
/* Delete a network-interface */
if (arg) {
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_rm(name);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETSCF: case IIOCNETSCF:
/* Set configurable parameters of a network-interface */
if (arg) {
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
return -EFAULT;
return isdn_net_setcfg(&cfg);
} else
return -EINVAL;
case IIOCNETGCF: case IIOCNETGCF:
/* Get configurable parameters of a network-interface */
if (arg) {
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
return -EFAULT;
if (!(ret = isdn_net_getcfg(&cfg))) {
if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
return -EFAULT;
}
return ret;
} else
return -EINVAL;
case IIOCNETANM: case IIOCNETANM:
/* Add a phone-number to a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_addphone(&phone);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETGNM: case IIOCNETGNM:
/* Get list of phone-numbers of a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_getphones(&phone, (char *) arg);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETDNM: case IIOCNETDNM:
/* Delete a phone-number of a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_delphone(&phone);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETDIL: case IIOCNETDIL:
/* Force dialing of a network-interface */
if (arg) {
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_net_force_dial(name);
} else
return -EINVAL;
#ifdef CONFIG_ISDN_PPP
case IIOCNETALN: case IIOCNETALN:
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_ppp_dial_slave(name);
case IIOCNETDLN: case IIOCNETDLN:
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_ppp_hangup_slave(name);
#endif
case IIOCNETHUP: case IIOCNETHUP:
/* Force hangup of a network-interface */ return isdn_net_ioctl(inode, file, cmd, arg);
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_net_force_hangup(name);
break;
#endif /* CONFIG_NETDEVICES */
case IIOCSETVER: case IIOCSETVER:
dev->net_verbose = arg; dev->net_verbose = arg;
printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose); printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
...@@ -1577,12 +1430,8 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ...@@ -1577,12 +1430,8 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
} else } else
return -EINVAL; return -EINVAL;
} }
#undef name
#undef bname
#undef iocts #undef iocts
#undef phone #undef bname
#undef cfg
} }
static struct file_operations isdn_ctrl_fops = static struct file_operations isdn_ctrl_fops =
...@@ -2430,7 +2279,7 @@ static int __init isdn_init(void) ...@@ -2430,7 +2279,7 @@ static int __init isdn_init(void)
printk("\n"); printk("\n");
#endif #endif
isdn_info_update(); isdn_info_update();
isdn_net_init_module(); isdn_net_init();
return 0; return 0;
err_tty_modem: err_tty_modem:
...@@ -2456,8 +2305,7 @@ static void __exit isdn_exit(void) ...@@ -2456,8 +2305,7 @@ static void __exit isdn_exit(void)
#endif #endif
save_flags(flags); save_flags(flags);
cli(); cli();
if (isdn_net_rmall() < 0) isdn_net_exit();
BUG();
isdn_tty_exit(); isdn_tty_exit();
if (unregister_chrdev(ISDN_MAJOR, "isdn")) if (unregister_chrdev(ISDN_MAJOR, "isdn"))
......
...@@ -52,6 +52,8 @@ do { printk(KERN_WARNING "ISDN Bug at %s:%d\n", __FILE__, __LINE__); \ ...@@ -52,6 +52,8 @@ do { printk(KERN_WARNING "ISDN Bug at %s:%d\n", __FILE__, __LINE__); \
#define HERE printk("%s:%d (%s)\n", __FILE__, __LINE__, __FUNCTION__) #define HERE printk("%s:%d (%s)\n", __FILE__, __LINE__, __FUNCTION__)
extern struct list_head isdn_net_devs;
/* Prototypes */ /* Prototypes */
extern void isdn_MOD_INC_USE_COUNT(void); extern void isdn_MOD_INC_USE_COUNT(void);
extern void isdn_MOD_DEC_USE_COUNT(void); extern void isdn_MOD_DEC_USE_COUNT(void);
...@@ -82,8 +84,6 @@ struct dial_info { ...@@ -82,8 +84,6 @@ struct dial_info {
unsigned char *phone; unsigned char *phone;
}; };
extern struct list_head isdn_net_devs;
extern int isdn_get_free_slot(int, int, int, int, int, char *); extern int isdn_get_free_slot(int, int, int, int, int, char *);
extern void isdn_slot_free(int slot, int usage); extern void isdn_slot_free(int slot, int usage);
extern void isdn_slot_all_eaz(int slot); extern void isdn_slot_all_eaz(int slot);
......
...@@ -43,12 +43,6 @@ enum { ...@@ -43,12 +43,6 @@ enum {
ST_WAIT_BEFORE_CB, ST_WAIT_BEFORE_CB,
}; };
enum {
ST_CHARGE_NULL,
ST_CHARGE_GOT_CINF, /* got a first charge info */
ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
};
/* keep clear of ISDN_CMD_* and ISDN_STAT_* */ /* keep clear of ISDN_CMD_* and ISDN_STAT_* */
enum { enum {
EV_NET_DIAL = 0x200, EV_NET_DIAL = 0x200,
...@@ -59,8 +53,6 @@ enum { ...@@ -59,8 +53,6 @@ enum {
EV_NET_TIMER_CB = 0x205, EV_NET_TIMER_CB = 0x205,
}; };
LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */
/* /*
* Outline of new tbusy handling: * Outline of new tbusy handling:
* *
...@@ -168,24 +160,6 @@ void isdn_net_zero_frame_cnt(isdn_net_dev *idev) ...@@ -168,24 +160,6 @@ void isdn_net_zero_frame_cnt(isdn_net_dev *idev)
* which might rely on the tx timeout. If so, we'll find out this way... * which might rely on the tx timeout. If so, we'll find out this way...
*/ */
#define ISDN_NET_TX_TIMEOUT (20*HZ)
static struct isdn_netif_ops *netif_ops[ISDN_NET_ENCAP_NR];
int
register_isdn_netif(int encap, struct isdn_netif_ops *ops)
{
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR)
return -EINVAL;
if (netif_ops[encap])
return -EBUSY;
netif_ops[encap] = ops;
return 0;
}
int isdn_net_online(isdn_net_dev *idev) int isdn_net_online(isdn_net_dev *idev)
{ {
return idev->dialstate == ST_ACTIVE; return idev->dialstate == ST_ACTIVE;
...@@ -193,10 +167,8 @@ int isdn_net_online(isdn_net_dev *idev) ...@@ -193,10 +167,8 @@ int isdn_net_online(isdn_net_dev *idev)
/* Prototypes */ /* Prototypes */
static int isdn_net_force_dial_idev(isdn_net_dev *);
static void do_dialout(isdn_net_dev *idev); static void do_dialout(isdn_net_dev *idev);
static int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg); int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
static int isdn_net_set_encap(isdn_net_local *mlp, int encap);
char *isdn_net_revision = "$Revision: 1.140.6.11 $"; char *isdn_net_revision = "$Revision: 1.140.6.11 $";
...@@ -217,27 +189,6 @@ isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason) ...@@ -217,27 +189,6 @@ isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
dst_link_failure(skb); dst_link_failure(skb);
} }
/* Open/initialize the board. */
static int
isdn_net_open(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
int retval = 0;
if (!lp->ops)
return -ENODEV;
if (lp->ops->open)
retval = lp->ops->open(lp);
if (!retval)
return retval;
netif_start_queue(dev);
isdn_MOD_INC_USE_COUNT();
return 0;
}
/* /*
* unbind a net-interface (resets interface after an error) * unbind a net-interface (resets interface after an error)
*/ */
...@@ -298,66 +249,6 @@ isdn_net_bind_channel(isdn_net_dev *idev, int idx) ...@@ -298,66 +249,6 @@ isdn_net_bind_channel(isdn_net_dev *idev, int idx)
return retval; return retval;
} }
/*
* Perform auto-hangup for net-interfaces.
*
* auto-hangup:
* Increment idle-counter (this counter is reset on any incoming or
* outgoing packet), if counter exceeds configured limit either do a
* hangup immediately or - if configured - wait until just before the next
* charge-info.
*/
static void isdn_net_hup_timer(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_local *mlp = idev->mlp;
if (!isdn_net_online(idev)) {
isdn_BUG();
return;
}
dbg_net_dial("%s: huptimer %d, onhtime %d, chargetime %ld, chargeint %d\n",
idev->name, idev->huptimer, mlp->onhtime, idev->chargetime, idev->chargeint);
if (mlp->onhtime == 0)
return;
if (idev->huptimer++ <= mlp->onhtime)
goto mod_timer;
if ((mlp->hupflags & (ISDN_MANCHARGE | ISDN_CHARGEHUP)) == (ISDN_MANCHARGE | ISDN_CHARGEHUP)) {
while (time_after(jiffies, idev->chargetime + idev->chargeint))
idev->chargetime += idev->chargeint;
if (time_after(jiffies, idev->chargetime + idev->chargeint - 2 * HZ)) {
if (idev->outgoing || mlp->hupflags & ISDN_INHUP) {
isdn_net_hangup(idev);
return;
}
}
} else if (idev->outgoing) {
if (mlp->hupflags & ISDN_CHARGEHUP) {
if (idev->charge_state != ST_CHARGE_HAVE_CINT) {
dbg_net_dial("%s: did not get CINT\n", idev->name);
isdn_net_hangup(idev);
return;
} else if (time_after(jiffies, idev->chargetime + idev->chargeint)) {
dbg_net_dial("%s: chtime = %lu, chint = %d\n",
idev->name, idev->chargetime, idev->chargeint);
isdn_net_hangup(idev);
return;
}
}
} else if (mlp->hupflags & ISDN_INHUP) {
isdn_net_hangup(idev);
return;
}
mod_timer:
mod_timer(&idev->hup_timer, idev->hup_timer.expires + HZ);
}
static void isdn_net_lp_disconnected(isdn_net_dev *idev) static void isdn_net_lp_disconnected(isdn_net_dev *idev)
{ {
isdn_net_rm_from_bundle(idev); isdn_net_rm_from_bundle(idev);
...@@ -413,14 +304,6 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) ...@@ -413,14 +304,6 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c)
return isdn_net_handle_event(idev, c->command, c); return isdn_net_handle_event(idev, c->command, c);
} }
static void
isdn_net_dial_timer(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_handle_event(idev, idev->dial_event, NULL);
}
/* Initiate dialout. Set phone-number-pointer to first number /* Initiate dialout. Set phone-number-pointer to first number
* of interface. * of interface.
*/ */
...@@ -450,7 +333,6 @@ do_dialout(isdn_net_dev *idev) ...@@ -450,7 +333,6 @@ do_dialout(isdn_net_dev *idev)
{ {
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
int i; int i;
unsigned long flags;
struct isdn_net_phone *phone; struct isdn_net_phone *phone;
struct dial_info dial = { struct dial_info dial = {
.l2_proto = mlp->l2_proto, .l2_proto = mlp->l2_proto,
...@@ -463,9 +345,7 @@ do_dialout(isdn_net_dev *idev) ...@@ -463,9 +345,7 @@ do_dialout(isdn_net_dev *idev)
if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF)
return; return;
spin_lock_irqsave(&mlp->lock, flags);
if (list_empty(&mlp->phone[1])) { if (list_empty(&mlp->phone[1])) {
spin_unlock_irqrestore(&mlp->lock, flags);
return; return;
} }
i = 0; i = 0;
...@@ -481,7 +361,6 @@ do_dialout(isdn_net_dev *idev) ...@@ -481,7 +361,6 @@ do_dialout(isdn_net_dev *idev)
found: found:
idev->dial++; idev->dial++;
dial.phone = phone->num; dial.phone = phone->num;
spin_unlock_irqrestore(&mlp->lock, flags);
if (idev->dialretry > mlp->dialmax) { if (idev->dialretry > mlp->dialmax) {
if (mlp->dialtimeout == 0) { if (mlp->dialtimeout == 0) {
...@@ -524,7 +403,7 @@ do_dialout(isdn_net_dev *idev) ...@@ -524,7 +403,7 @@ do_dialout(isdn_net_dev *idev)
/* For EV_NET_DIAL, returns 1 if timer callback is needed /* For EV_NET_DIAL, returns 1 if timer callback is needed
* For ISDN_STAT_*, returns 1 if event was for us * For ISDN_STAT_*, returns 1 if event was for us
*/ */
static int int
isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg) isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
{ {
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
...@@ -714,17 +593,6 @@ isdn_net_hangup(isdn_net_dev *idev) ...@@ -714,17 +593,6 @@ isdn_net_hangup(isdn_net_dev *idev)
isdn_net_unbind_channel(idev); isdn_net_unbind_channel(idev);
} }
void
isdn_net_hangup_all()
{
struct list_head *l;
list_for_each(l, &isdn_net_devs) {
isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
isdn_net_hangup(p);
}
}
typedef struct { typedef struct {
unsigned short source; unsigned short source;
unsigned short dest; unsigned short dest;
...@@ -843,21 +711,6 @@ isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb) ...@@ -843,21 +711,6 @@ isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
spin_unlock_bh(&idev->xmit_lock); spin_unlock_bh(&idev->xmit_lock);
} }
static void isdn_net_tasklet(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
struct sk_buff *skb;
spin_lock_bh(&idev->xmit_lock);
while (!isdn_net_dev_busy(idev)) {
skb = skb_dequeue(&idev->super_tx_queue);
if (!skb)
break;
isdn_net_writebuf_skb(idev, skb);
}
spin_unlock_bh(&idev->xmit_lock);
}
/* /*
* all frames sent from the (net) LL to a HL driver should go via this function * all frames sent from the (net) LL to a HL driver should go via this function
* it's serialized by the caller holding the idev->xmit_lock spinlock * it's serialized by the caller holding the idev->xmit_lock spinlock
...@@ -953,7 +806,7 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -953,7 +806,7 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) { if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
sdev = idev->slave; sdev = idev->slave;
if (!isdn_net_bound(sdev)) { if (!isdn_net_bound(sdev)) {
isdn_net_force_dial_idev(sdev); isdn_net_dev_dial(sdev);
} }
} }
} }
...@@ -970,14 +823,6 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -970,14 +823,6 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
return 0; return 0;
} }
static void
isdn_net_tx_timeout(struct net_device *dev)
{
printk(KERN_WARNING "isdn_tx_timeout dev %s\n", dev->name);
netif_wake_queue(dev);
}
int int
isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev) isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
{ {
...@@ -1005,7 +850,7 @@ isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev) ...@@ -1005,7 +850,7 @@ isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
idev->dialwait_timer = 0; idev->dialwait_timer = 0;
} }
if (isdn_net_force_dial_idev(idev) < 0) if (isdn_net_dev_dial(idev) < 0)
goto discard; goto discard;
/* Log packet, which triggered dialing */ /* Log packet, which triggered dialing */
...@@ -1022,39 +867,6 @@ isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev) ...@@ -1022,39 +867,6 @@ isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
} }
/*
* Shutdown a net-interface.
*/
static int
isdn_net_close(struct net_device *dev)
{
isdn_net_local *mlp = dev->priv;
struct list_head *l, *n;
isdn_net_dev *sdev;
if (mlp->ops->close)
mlp->ops->close(mlp);
netif_stop_queue(dev);
list_for_each_safe(l, n, &mlp->online) {
sdev = list_entry(l, isdn_net_dev, online);
isdn_net_hangup(sdev);
}
isdn_MOD_DEC_USE_COUNT();
return 0;
}
/*
* Get statistics
*/
static struct net_device_stats *
isdn_net_get_stats(struct net_device *dev)
{
isdn_net_local *lp = (isdn_net_local *) dev->priv;
return &lp->stats;
}
/* /*
* Got a packet from ISDN-Channel. * Got a packet from ISDN-Channel.
*/ */
...@@ -1095,24 +907,6 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb) ...@@ -1095,24 +907,6 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb)
return 0; return 0;
} }
/*
* Interface-setup. (just after registering a new interface)
*/
static int
isdn_net_init(struct net_device *ndev)
{
/* Setup the generic properties */
ndev->mtu = 1500;
ndev->tx_queue_len = 10;
ndev->open = &isdn_net_open;
ndev->hard_header_len = ETH_HLEN + isdn_hard_header_len();
ndev->stop = &isdn_net_close;
ndev->get_stats = &isdn_net_get_stats;
return 0;
}
static int static int
isdn_net_do_callback(isdn_net_dev *idev) isdn_net_do_callback(isdn_net_dev *idev)
{ {
...@@ -1260,14 +1054,11 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) ...@@ -1260,14 +1054,11 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
} }
dbg_net_icall("n_fi: match2\n"); dbg_net_icall("n_fi: match2\n");
if (mlp->flags & ISDN_NET_SECURE) { if (mlp->flags & ISDN_NET_SECURE) {
spin_lock_irqsave(&mlp->lock, flags);
list_for_each_entry(n, &mlp->phone[0], list) { list_for_each_entry(n, &mlp->phone[0], list) {
if (!isdn_msncmp(nr, n->num)) { if (!isdn_msncmp(nr, n->num)) {
spin_unlock_irqrestore(&mlp->lock, flags);
goto found; goto found;
} }
} }
spin_unlock_irqrestore(&mlp->lock, flags);
continue; continue;
} }
found: found:
...@@ -1334,38 +1125,17 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) ...@@ -1334,38 +1125,17 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
} }
/* /*
* Search list of net-interfaces for an interface with given name. * Trigger dialing out
*/
isdn_net_dev *
isdn_net_findif(char *name)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &isdn_net_devs, global_list) {
if (!strcmp(idev->name, name))
return idev;
}
return NULL;
}
/*
* Force a net-interface to dial out.
* This is called from the userlevel-routine below or
* from isdn_net_start_xmit().
*/ */
static int int
isdn_net_force_dial_idev(isdn_net_dev *idev) isdn_net_dev_dial(isdn_net_dev *idev)
{ {
int slot; int slot;
unsigned long flags;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
if (isdn_net_bound(idev)) if (isdn_net_bound(idev))
return -EBUSY; return -EBUSY;
save_flags(flags);
cli();
if (idev->exclusive >= 0) if (idev->exclusive >= 0)
slot = idev->exclusive; slot = idev->exclusive;
else else
...@@ -1379,13 +1149,11 @@ isdn_net_force_dial_idev(isdn_net_dev *idev) ...@@ -1379,13 +1149,11 @@ isdn_net_force_dial_idev(isdn_net_dev *idev)
goto err;; goto err;;
/* Initiate dialing */ /* Initiate dialing */
restore_flags(flags);
init_dialout(idev); init_dialout(idev);
return 0; return 0;
err: err:
restore_flags(flags);
return -EAGAIN; return -EAGAIN;
} }
...@@ -1402,665 +1170,7 @@ isdn_net_dial_req(isdn_net_dev *idev) ...@@ -1402,665 +1170,7 @@ isdn_net_dial_req(isdn_net_dev *idev)
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO) if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
return -EBUSY; return -EBUSY;
return isdn_net_force_dial_idev(idev); return isdn_net_dev_dial(idev);
}
/*
* Force a net-interface to dial out.
* This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
*/
int
isdn_net_force_dial(char *name)
{
isdn_net_dev *p = isdn_net_findif(name);
if (!p)
return -ENODEV;
return isdn_net_force_dial_idev(p);
}
/*
* Allocate a new network-interface and initialize its data structures.
*/
int
isdn_net_new(char *name, isdn_net_local *mlp)
{
int retval;
isdn_net_dev *netdev;
/* Avoid creating an existing interface */
if (isdn_net_findif(name)) {
printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
return -EEXIST;
}
if (!(netdev = kmalloc(sizeof(*netdev), GFP_KERNEL))) {
printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
return -ENOMEM;
}
memset(netdev, 0, sizeof(*netdev));
strcpy(netdev->name, name);
if (!mlp) {
/* Device shall be a master */
mlp = kmalloc(sizeof(*mlp), GFP_KERNEL);
if (!mlp)
return -ENOMEM;
memset(mlp, 0, sizeof(*mlp));
mlp->magic = ISDN_NET_MAGIC;
mlp->dev.tx_timeout = isdn_net_tx_timeout;
mlp->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT;
strcpy(mlp->dev.name, name);
mlp->dev.priv = mlp;
mlp->dev.init = isdn_net_init;
INIT_LIST_HEAD(&mlp->slaves);
INIT_LIST_HEAD(&mlp->online);
spin_lock_init(&mlp->lock);
mlp->p_encap = -1;
mlp->l2_proto = ISDN_PROTO_L2_X75I;
mlp->l3_proto = ISDN_PROTO_L3_TRANS;
mlp->triggercps = 6000;
mlp->slavedelay = 10 * HZ;
mlp->hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */
mlp->onhtime = 10; /* Default hangup-time for saving costs
of those who forget configuring this */
mlp->dialmax = 1;
mlp->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL; /* Hangup before Callback, manual dial */
mlp->cbdelay = 5 * HZ; /* Wait 5 secs before Callback */
mlp->dialtimeout = -1; /* Infinite Dial-Timeout */
mlp->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
spin_lock_init(&mlp->lock);
INIT_LIST_HEAD(&mlp->phone[0]);
INIT_LIST_HEAD(&mlp->phone[1]);
isdn_net_set_encap(mlp, ISDN_NET_ENCAP_RAWIP);
retval = register_netdev(&mlp->dev);
if (retval) {
printk(KERN_WARNING "isdn_net: Could not register net-device\n");
kfree(netdev);
return retval;
}
}
netdev->mlp = mlp;
list_add_tail(&netdev->slaves, &mlp->slaves);
tasklet_init(&netdev->tlet, isdn_net_tasklet, (unsigned long) netdev);
spin_lock_init(&netdev->xmit_lock);
skb_queue_head_init(&netdev->super_tx_queue);
netdev->isdn_slot = -1;
netdev->pre_device = -1;
netdev->pre_channel = -1;
netdev->exclusive = -1;
netdev->ppp_slot = -1;
netdev->pppbind = -1;
netdev->dialstarted = 0; /* Jiffies of last dial-start */
netdev->dialwait_timer = 0; /* Jiffies of earliest next dial-start */
init_timer(&netdev->dial_timer);
netdev->dial_timer.data = (unsigned long) netdev;
netdev->dial_timer.function = isdn_net_dial_timer;
init_timer(&netdev->hup_timer);
netdev->hup_timer.data = (unsigned long) netdev;
netdev->hup_timer.function = isdn_net_hup_timer;
/* Put into to netdev-chain */
list_add(&netdev->global_list, &isdn_net_devs);
return 0;
}
int
isdn_net_newslave(char *parm)
{
char *p = strchr(parm, ',');
isdn_net_dev *m;
/* Slave-Name MUST not be empty */
if (!p || !p[1])
return -EINVAL;
*p = 0;
/* Master must already exist */
if (!(m = isdn_net_findif(parm)))
return -ESRCH;
/* Master must not be started yet */
if (isdn_net_device_started(m))
return -EBUSY;
return isdn_net_new(p+1, m->mlp);
}
static int
isdn_net_set_encap(isdn_net_local *lp, int encap)
{
int retval = 0;
if (lp->p_encap == encap){
/* nothing to do */
retval = 0;
goto out;
}
if (netif_running(&lp->dev)) {
retval = -EBUSY;
goto out;
}
if (lp->ops && lp->ops->cleanup)
lp->ops->cleanup(lp);
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR) {
lp->p_encap = -1;
lp->ops = NULL;
retval = -EINVAL;
goto out;
}
lp->p_encap = encap;
lp->ops = netif_ops[encap];
lp->dev.hard_start_xmit = lp->ops->hard_start_xmit;
lp->dev.hard_header = lp->ops->hard_header;
lp->dev.do_ioctl = lp->ops->do_ioctl;
lp->dev.flags = lp->ops->flags;
lp->dev.type = lp->ops->type;
lp->dev.addr_len = lp->ops->addr_len;
if (lp->ops->init)
retval = lp->ops->init(lp);
if (retval != 0) {
lp->p_encap = -1;
lp->ops = NULL;
}
out:
return retval;
}
static int
isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg)
{
isdn_net_local *mlp = idev->mlp;
int i, retval;
int drvidx = -1;
int chidx = -1;
char drvid[25];
strncpy(drvid, cfg->drvid, 24);
drvid[24] = 0;
if (cfg->exclusive && !strlen(drvid)) {
/* If we want to bind exclusively, need to specify drv/chan */
retval = -ENODEV;
goto out;
}
if (strlen(drvid)) {
/* A bind has been requested ... */
char *c = strchr(drvid, ',');
if (!c) {
retval = -ENODEV;
goto out;
}
/* The channel-number is appended to the driver-Id with a comma */
*c = 0;
chidx = simple_strtol(c + 1, NULL, 10);
for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
/* Lookup driver-Id in array */
if (!strcmp(dev->drvid[i], drvid)) {
drvidx = i;
break;
}
}
if (drvidx == -1 || chidx == -1) {
/* Either driver-Id or channel-number invalid */
retval = -ENODEV;
goto out;
}
}
if (cfg->exclusive == (idev->exclusive >= 0) &&
drvidx == idev->pre_device && chidx == idev->pre_channel) {
/* no change */
retval = 0;
goto out;
}
if (idev->exclusive >= 0) {
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
isdn_free_channel(idev->pre_device, idev->pre_channel, ISDN_USAGE_NET);
idev->exclusive = -1;
}
if (cfg->exclusive) {
/* If binding is exclusive, try to grab the channel */
idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto,
mlp->l3_proto, drvidx, chidx, cfg->eaz);
if (idev->exclusive < 0) {
/* Grab failed, because desired channel is in use */
retval = -EBUSY;
goto out;
}
/* All went ok, so update isdninfo */
isdn_slot_set_usage(idev->exclusive, ISDN_USAGE_EXCLUSIVE);
}
idev->pre_device = drvidx;
idev->pre_channel = chidx;
retval = 0;
out:
return retval;
}
/*
* Set interface-parameters.
* Always set all parameters, so the user-level application is responsible
* for not overwriting existing setups. It has to get the current
* setup first, if only selected parameters are to be changed.
*/
int
isdn_net_setcfg(isdn_net_ioctl_cfg *cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *mlp = idev->mlp;
ulong features;
int i, retval;
if (!idev) {
retval = -ENODEV;
goto out;
}
/* See if any registered driver supports the features we want */
features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
for (i = 0; i < ISDN_MAX_DRIVERS; i++)
if (dev->drv[i] &&
(dev->drv[i]->interface->features & features) == features)
break;
if (i == ISDN_MAX_DRIVERS) {
printk(KERN_WARNING "isdn_net: No driver with selected features\n");
retval = -ENODEV;
goto out;
}
retval = isdn_net_set_encap(mlp, cfg->p_encap);
if (retval)
goto out;
retval = isdn_net_bind(idev, cfg);
if (retval)
goto out;
strncpy(mlp->msn, cfg->eaz, ISDN_MSNLEN-1);
mlp->msn[ISDN_MSNLEN-1] = 0;
mlp->onhtime = cfg->onhtime;
idev->charge = cfg->charge;
mlp->l2_proto = cfg->l2_proto;
mlp->l3_proto = cfg->l3_proto;
mlp->cbdelay = cfg->cbdelay * HZ / 5;
mlp->dialmax = cfg->dialmax;
mlp->triggercps = cfg->triggercps;
mlp->slavedelay = cfg->slavedelay * HZ;
idev->pppbind = cfg->pppbind;
mlp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
mlp->dialwait = cfg->dialwait * HZ;
if (cfg->secure)
mlp->flags |= ISDN_NET_SECURE;
else
mlp->flags &= ~ISDN_NET_SECURE;
if (cfg->cbhup)
mlp->flags |= ISDN_NET_CBHUP;
else
mlp->flags &= ~ISDN_NET_CBHUP;
switch (cfg->callback) {
case 0:
mlp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
break;
case 1:
mlp->flags |= ISDN_NET_CALLBACK;
mlp->flags &= ~ISDN_NET_CBOUT;
break;
case 2:
mlp->flags |= ISDN_NET_CBOUT;
mlp->flags &= ~ISDN_NET_CALLBACK;
break;
}
mlp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */
if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
retval = -EINVAL;
goto out;
}
mlp->flags |= cfg->dialmode; /* turn on selected bits */
if (mlp->flags & ISDN_NET_DM_OFF)
isdn_net_hangup(idev);
if (cfg->chargehup)
mlp->hupflags |= ISDN_CHARGEHUP;
else
mlp->hupflags &= ~ISDN_CHARGEHUP;
if (cfg->ihup)
mlp->hupflags |= ISDN_INHUP;
else
mlp->hupflags &= ~ISDN_INHUP;
if (cfg->chargeint > 10) {
idev->chargeint = cfg->chargeint * HZ;
idev->charge_state = ST_CHARGE_HAVE_CINT;
mlp->hupflags |= ISDN_MANCHARGE;
}
retval = 0;
out:
return retval;
}
/*
* Perform get-interface-parameters.ioctl
*/
int
isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *mlp;
if (!idev)
return -ENODEV;
mlp = idev->mlp;
strcpy(cfg->eaz, mlp->msn);
cfg->exclusive = idev->exclusive >= 0;
if (idev->pre_device >= 0) {
sprintf(cfg->drvid, "%s,%d", dev->drvid[idev->pre_device],
idev->pre_channel);
} else
cfg->drvid[0] = '\0';
cfg->onhtime = mlp->onhtime;
cfg->charge = idev->charge;
cfg->l2_proto = mlp->l2_proto;
cfg->l3_proto = mlp->l3_proto;
cfg->p_encap = mlp->p_encap;
cfg->secure = (mlp->flags & ISDN_NET_SECURE) ? 1 : 0;
cfg->callback = 0;
if (mlp->flags & ISDN_NET_CALLBACK)
cfg->callback = 1;
if (mlp->flags & ISDN_NET_CBOUT)
cfg->callback = 2;
cfg->cbhup = (mlp->flags & ISDN_NET_CBHUP) ? 1 : 0;
cfg->dialmode = mlp->flags & ISDN_NET_DIALMODE_MASK;
cfg->chargehup = (mlp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
cfg->ihup = (mlp->hupflags & ISDN_INHUP) ? 1 : 0;
cfg->cbdelay = mlp->cbdelay * 5 / HZ;
cfg->dialmax = mlp->dialmax;
cfg->triggercps = mlp->triggercps;
cfg->slavedelay = mlp->slavedelay / HZ;
cfg->chargeint = (mlp->hupflags & ISDN_CHARGEHUP) ?
(idev->chargeint / HZ) : 0;
cfg->pppbind = idev->pppbind;
cfg->dialtimeout = mlp->dialtimeout >= 0 ? mlp->dialtimeout / HZ : -1;
cfg->dialwait = mlp->dialwait / HZ;
if (idev->slaves.next != &mlp->slaves)
strcpy(cfg->slave, list_entry(idev->slaves.next, isdn_net_dev, slaves)->name);
else
cfg->slave[0] = '\0';
if (strcmp(mlp->dev.name, idev->name))
strcpy(cfg->master, mlp->dev.name);
else
cfg->master[0] = '\0';
return 0;
}
/*
* Add a phone-number to an interface.
*/
int
isdn_net_addphone(isdn_net_ioctl_phone * phone)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
isdn_net_local *mlp;
unsigned long flags;
struct isdn_net_phone *n;
if (!idev)
return -ENODEV;
n = kmalloc(sizeof(*n), GFP_KERNEL);
if (!n)
return -ENOMEM;
strcpy(n->num, phone->phone);
mlp = idev->mlp;
spin_lock_irqsave(&mlp->lock, flags);
list_add_tail(&n->list, &mlp->phone[phone->outgoing & 1]);
spin_unlock_irqrestore(&mlp->lock, flags);
return 0;
}
/*
* Copy a string of all phone-numbers of an interface to user space.
* This might sleep and must be called with the isdn semaphore down.
*/
int
isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
isdn_net_local *mlp;
unsigned long flags;
int inout = phone->outgoing & 1;
int count = 0;
char *buf = (char *)__get_free_page(GFP_KERNEL);
struct isdn_net_phone *n;
if (!idev)
return -ENODEV;
if (!buf)
return -ENOMEM;
mlp = idev->mlp;
inout &= 1;
spin_lock_irqsave(&mlp->lock, flags);
list_for_each_entry(n, &mlp->phone[inout], list) {
strcpy(&buf[count], n->num);
count += strlen(n->num);
buf[count++] = ' ';
if (count > PAGE_SIZE - ISDN_MSNLEN - 1)
break;
}
spin_unlock_irqrestore(&mlp->lock, flags);
if (!count)
count++;
buf[count-1] = 0;
if (copy_to_user(phones, buf, count))
count = -EFAULT;
free_page((unsigned long)buf);
return count;
}
/*
* Copy a string containing the peer's phone number of a connected interface
* to user space.
*/
int
isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
{
isdn_net_dev *p = isdn_net_findif(phone->name);
int idx;
if (!p) return -ENODEV;
/*
* Theoretical race: while this executes, the remote number might
* become invalid (hang up) or change (new connection), resulting
* in (partially) wrong number copied to user. This race
* currently ignored.
*/
idx = p->isdn_slot;
if (idx<0) return -ENOTCONN;
/* for pre-bound channels, we need this extra check */
if (strncmp(isdn_slot_num(idx),"???",3) == 0 ) return -ENOTCONN;
strncpy(phone->phone,isdn_slot_num(idx),ISDN_MSNLEN);
phone->outgoing=USG_OUTGOING(isdn_slot_usage(idx));
if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
return 0;
}
/*
* Delete a phone-number from an interface.
*/
int
isdn_net_delphone(isdn_net_ioctl_phone * phone)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
isdn_net_local *mlp;
int inout = phone->outgoing & 1;
struct isdn_net_phone *n;
unsigned long flags;
int retval;
if (!idev)
return -ENODEV;
mlp = idev->mlp;
retval = -EINVAL;
spin_lock_irqsave(&mlp->lock, flags);
list_for_each_entry(n, &mlp->phone[inout], list) {
if (!strcmp(n->num, phone->phone)) {
list_del(&n->list);
kfree(n);
retval = 0;
break;
}
}
spin_unlock_irqrestore(&mlp->lock, flags);
return retval;
}
/*
* Delete all phone-numbers of an interface.
*/
static int
isdn_net_rmallphone(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
struct isdn_net_phone *n;
unsigned long flags;
int i;
spin_lock_irqsave(&mlp->lock, flags);
for (i = 0; i < 2; i++) {
while (!list_empty(&mlp->phone[i])) {
n = list_entry(mlp->phone[i].next, struct isdn_net_phone, list);
list_del(&n->list);
kfree(n);
}
}
spin_lock_irqsave(&mlp->lock, flags);
return 0;
}
/*
* Force a hangup of a network-interface.
*/
int
isdn_net_force_hangup(char *name)
{
isdn_net_dev *idev = isdn_net_findif(name);
isdn_net_dev *p;
if (!idev)
return -ENODEV;
if (idev->isdn_slot < 0)
return -ENOTCONN;
p = idev->slave;
/* If this interface has slaves, do a hangup for them also. */
while (p) {
isdn_net_hangup(p);
p = p->slave;
}
isdn_net_hangup(idev);
return 0;
}
/*
* Helper-function for isdn_net_rm: Do the real work.
*/
static int
isdn_net_realrm(isdn_net_dev *p)
{
unsigned long flags;
save_flags(flags);
cli();
if (isdn_net_device_started(p)) {
restore_flags(flags);
return -EBUSY;
}
isdn_net_set_encap(p->mlp, -1);
/* Free all phone-entries */
isdn_net_rmallphone(p);
/* If interface is bound exclusive, free channel-usage */
if (p->exclusive >= 0)
isdn_unexclusive_channel(p->pre_device, p->pre_channel);
/* Unlink device from chain */
list_del(&p->global_list);
list_del(&p->slaves);
if (list_empty(&p->mlp->slaves)) {
unregister_netdev(&p->mlp->dev);
kfree(p->mlp);
}
restore_flags(flags);
kfree(p);
return 0;
}
/*
* Remove a single network-interface.
*/
int
isdn_net_rm(char *name)
{
/* FIXME: For compatibility, if a master isdn_net_dev is rm'ed,
* kill all slaves, too */
isdn_net_dev *idev = isdn_net_findif(name);
if (!idev)
return -ENODEV;
return isdn_net_realrm(idev);
}
/*
* Remove all network-interfaces
*/
int
isdn_net_rmall(void)
{
unsigned long flags;
int ret = 0;
/* Walk through netdev-chain */
save_flags(flags);
cli();
while (!list_empty(&isdn_net_devs)) {
isdn_net_dev *idev = list_entry(isdn_net_devs.next, isdn_net_dev, global_list);
ret = isdn_net_realrm(idev);
if (ret)
break;
}
restore_flags(flags);
return ret;
} }
// ISDN_NET_ENCAP_IPTYP // ISDN_NET_ENCAP_IPTYP
...@@ -2203,7 +1313,7 @@ static struct isdn_netif_ops ether_ops = { ...@@ -2203,7 +1313,7 @@ static struct isdn_netif_ops ether_ops = {
// ====================================================================== // ======================================================================
void void
isdn_net_init_module(void) isdn_net_init(void)
{ {
register_isdn_netif(ISDN_NET_ENCAP_ETHER, &ether_ops); register_isdn_netif(ISDN_NET_ENCAP_ETHER, &ether_ops);
register_isdn_netif(ISDN_NET_ENCAP_RAWIP, &rawip_ops); register_isdn_netif(ISDN_NET_ENCAP_RAWIP, &rawip_ops);
......
...@@ -32,32 +32,30 @@ ...@@ -32,32 +32,30 @@
#define CISCO_SLARP_REPLY 1 #define CISCO_SLARP_REPLY 1
#define CISCO_SLARP_KEEPALIVE 2 #define CISCO_SLARP_KEEPALIVE 2
extern void isdn_net_init_module(void); extern void isdn_net_init(void);
extern void isdn_net_exit(void);
extern void isdn_net_hangup_all(void);
extern int isdn_net_ioctl(struct inode *, struct file *, uint, ulong);
extern int register_isdn_netif(int encap, struct isdn_netif_ops *ops);
extern int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev);
extern int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev);
extern int isdn_net_new(char *, isdn_net_local *);
extern int isdn_net_newslave(char *);
extern int isdn_net_rm(char *);
extern int isdn_net_rmall(void);
extern int isdn_net_stat_callback(int, isdn_ctrl *); extern int isdn_net_stat_callback(int, isdn_ctrl *);
extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
extern int isdn_net_addphone(isdn_net_ioctl_phone *);
extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
extern int isdn_net_delphone(isdn_net_ioctl_phone *);
extern int isdn_net_find_icall(int, int, int, setup_parm *); extern int isdn_net_find_icall(int, int, int, setup_parm *);
extern int isdn_net_dev_dial(isdn_net_dev *idev);
extern void isdn_net_hangup(isdn_net_dev *); extern void isdn_net_hangup(isdn_net_dev *);
extern void isdn_net_hangup_all(void);
extern int isdn_net_force_hangup(char *);
extern int isdn_net_force_dial(char *);
extern isdn_net_dev *isdn_net_findif(char *);
extern int isdn_net_rcv_skb(int, struct sk_buff *); extern int isdn_net_rcv_skb(int, struct sk_buff *);
extern int isdn_net_dial_req(isdn_net_dev *); extern int isdn_net_dial_req(isdn_net_dev *);
extern void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb); extern void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb);
extern void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb); extern void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb);
extern int isdn_net_online(isdn_net_dev *); extern int isdn_net_online(isdn_net_dev *);
extern int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev);
extern int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev); enum {
ST_CHARGE_NULL,
ST_CHARGE_GOT_CINF, /* got a first charge info */
ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
};
#define ISDN_NET_MAX_QUEUE_LENGTH 2 #define ISDN_NET_MAX_QUEUE_LENGTH 2
......
/*
* Linux ISDN subsystem, Network interface configuration
*
* Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de)
* 1995,96 by Thinking Objects Software GmbH Wuerzburg
* 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
* 1999-2002 by Kai Germaschewski <kai@germaschewski.name>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/capability.h>
#include <linux/rtnetlink.h>
#include "isdn_common.h"
#include "isdn_net.h"
#include "isdn_ppp.h"
#define ISDN_NET_TX_TIMEOUT (20*HZ)
/* All of this configuration code is globally serialized */
static DECLARE_MUTEX(sem);
LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */ // FIXME static
int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg); /* FIXME */
static void isdn_net_tasklet(unsigned long data);
static void isdn_net_dial_timer(unsigned long data);
static void isdn_net_hup_timer(unsigned long data);
static int isdn_init_netif(struct net_device *ndev);
/* ====================================================================== */
/* Registration of ISDN network interface types */
/* ====================================================================== */
static struct isdn_netif_ops *isdn_netif_ops[ISDN_NET_ENCAP_NR];
int
register_isdn_netif(int encap, struct isdn_netif_ops *ops)
{
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR)
return -EINVAL;
if (isdn_netif_ops[encap])
return -EBUSY;
isdn_netif_ops[encap] = ops;
return 0;
}
/* ====================================================================== */
/* Helpers */
/* ====================================================================== */
/* Search list of net-interfaces for an interface with given name. */
static isdn_net_dev *
isdn_net_findif(char *name)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &isdn_net_devs, global_list) {
if (!strcmp(idev->name, name))
return idev;
}
return NULL;
}
/* Set up a certain encapsulation */
static int
isdn_net_set_encap(isdn_net_local *lp, int encap)
{
int retval = 0;
if (lp->p_encap == encap){
/* nothing to do */
retval = 0;
goto out;
}
if (netif_running(&lp->dev)) {
retval = -EBUSY;
goto out;
}
if (lp->ops && lp->ops->cleanup)
lp->ops->cleanup(lp);
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR) {
lp->p_encap = -1;
lp->ops = NULL;
retval = -EINVAL;
goto out;
}
lp->p_encap = encap;
lp->ops = isdn_netif_ops[encap];
lp->dev.hard_start_xmit = lp->ops->hard_start_xmit;
lp->dev.hard_header = lp->ops->hard_header;
lp->dev.do_ioctl = lp->ops->do_ioctl;
lp->dev.flags = lp->ops->flags;
lp->dev.type = lp->ops->type;
lp->dev.addr_len = lp->ops->addr_len;
if (lp->ops->init)
retval = lp->ops->init(lp);
if (retval != 0) {
lp->p_encap = -1;
lp->ops = NULL;
}
out:
return retval;
}
static int
isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg)
{
isdn_net_local *mlp = idev->mlp;
int i, retval;
int drvidx = -1;
int chidx = -1;
char drvid[25];
strncpy(drvid, cfg->drvid, 24);
drvid[24] = 0;
if (cfg->exclusive && !strlen(drvid)) {
/* If we want to bind exclusively, need to specify drv/chan */
retval = -ENODEV;
goto out;
}
if (strlen(drvid)) {
/* A bind has been requested ... */
char *c = strchr(drvid, ',');
if (!c) {
retval = -ENODEV;
goto out;
}
/* The channel-number is appended to the driver-Id with a comma */
*c = 0;
chidx = simple_strtol(c + 1, NULL, 10);
for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
/* Lookup driver-Id in array */
if (!strcmp(dev->drvid[i], drvid)) {
drvidx = i;
break;
}
}
if (drvidx == -1 || chidx == -1) {
/* Either driver-Id or channel-number invalid */
retval = -ENODEV;
goto out;
}
}
if (cfg->exclusive == (idev->exclusive >= 0) &&
drvidx == idev->pre_device && chidx == idev->pre_channel) {
/* no change */
retval = 0;
goto out;
}
if (idev->exclusive >= 0) {
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
isdn_free_channel(idev->pre_device, idev->pre_channel, ISDN_USAGE_NET);
idev->exclusive = -1;
}
if (cfg->exclusive) {
/* If binding is exclusive, try to grab the channel */
idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto,
mlp->l3_proto, drvidx, chidx, cfg->eaz);
if (idev->exclusive < 0) {
/* Grab failed, because desired channel is in use */
retval = -EBUSY;
goto out;
}
/* All went ok, so update isdninfo */
isdn_slot_set_usage(idev->exclusive, ISDN_USAGE_EXCLUSIVE);
}
idev->pre_device = drvidx;
idev->pre_channel = chidx;
retval = 0;
out:
return retval;
}
/*
* Delete all phone-numbers of an interface.
*/
static void
isdn_net_rmallphone(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
struct isdn_net_phone *n;
int i;
for (i = 0; i < 2; i++) {
while (!list_empty(&mlp->phone[i])) {
n = list_entry(mlp->phone[i].next, struct isdn_net_phone, list);
list_del(&n->list);
kfree(n);
}
}
}
/* ====================================================================== */
/* /dev/isdnctrl net ioctl interface */
/* ====================================================================== */
/*
* Allocate a new network-interface and initialize its data structures
*/
static int
isdn_net_addif(char *name, isdn_net_local *mlp)
{
int retval;
struct net_device *dev = NULL;
isdn_net_dev *idev;
/* Avoid creating an existing interface */
if (isdn_net_findif(name))
return -EEXIST;
idev = kmalloc(sizeof(*idev), GFP_KERNEL);
if (!idev)
return -ENOMEM;
memset(idev, 0, sizeof(*idev));
strcpy(idev->name, name);
tasklet_init(&idev->tlet, isdn_net_tasklet, (unsigned long) idev);
spin_lock_init(&idev->xmit_lock);
skb_queue_head_init(&idev->super_tx_queue);
idev->isdn_slot = -1;
idev->pre_device = -1;
idev->pre_channel = -1;
idev->exclusive = -1;
idev->ppp_slot = -1;
idev->pppbind = -1;
idev->dialstarted = 0; /* Jiffies of last dial-start */
idev->dialwait_timer = 0; /* Jiffies of earliest next dial-start */
init_timer(&idev->dial_timer);
idev->dial_timer.data = (unsigned long) idev;
idev->dial_timer.function = isdn_net_dial_timer;
init_timer(&idev->hup_timer);
idev->hup_timer.data = (unsigned long) idev;
idev->hup_timer.function = isdn_net_hup_timer;
if (!mlp) {
/* Device shall be a master */
mlp = kmalloc(sizeof(*mlp), GFP_KERNEL);
if (!mlp)
return -ENOMEM;
memset(mlp, 0, sizeof(*mlp));
mlp->magic = ISDN_NET_MAGIC;
INIT_LIST_HEAD(&mlp->slaves);
INIT_LIST_HEAD(&mlp->online);
mlp->p_encap = -1;
isdn_net_set_encap(mlp, ISDN_NET_ENCAP_RAWIP);
mlp->l2_proto = ISDN_PROTO_L2_X75I;
mlp->l3_proto = ISDN_PROTO_L3_TRANS;
mlp->triggercps = 6000;
mlp->slavedelay = 10 * HZ;
mlp->hupflags = ISDN_INHUP;
mlp->onhtime = 10;
mlp->dialmax = 1;
mlp->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;
mlp->cbdelay = 5 * HZ; /* Wait 5 secs before Callback */
mlp->dialtimeout = -1; /* Infinite Dial-Timeout */
mlp->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
INIT_LIST_HEAD(&mlp->phone[0]);
INIT_LIST_HEAD(&mlp->phone[1]);
dev = &mlp->dev;
}
idev->mlp = mlp;
list_add_tail(&idev->slaves, &mlp->slaves);
if (dev) {
strcpy(dev->name, name);
dev->priv = mlp;
dev->init = isdn_init_netif;
SET_MODULE_OWNER(dev);
retval = register_netdev(dev);
if (retval) {
kfree(mlp);
kfree(idev);
return retval;
}
}
list_add(&idev->global_list, &isdn_net_devs);
return 0;
}
/*
* Add a new slave interface to an existing one
*/
static int
isdn_net_addslave(char *parm)
{
char *p = strchr(parm, ',');
isdn_net_dev *idev;
isdn_net_local *mlp;
int retval;
/* get slave name */
if (!p || !p[1])
return -EINVAL;
*p++ = 0;
/* find master */
idev = isdn_net_findif(parm);
if (!idev)
return -ESRCH;
mlp = idev->mlp;
rtnl_lock();
if (netif_running(&mlp->dev))
return -EBUSY;
retval = isdn_net_addif(p, mlp);
rtnl_unlock();
return retval;
}
/*
* Delete a single network-interface
*/
static int
isdn_net_dev_delete(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
int retval;
rtnl_lock();
if (netif_running(&mlp->dev)) {
retval = -EBUSY;
goto unlock;
}
isdn_net_set_encap(mlp, -1);
isdn_net_rmallphone(idev);
if (idev->exclusive >= 0)
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
list_del(&idev->slaves);
rtnl_unlock();
if (list_empty(&mlp->slaves)) {
unregister_netdev(&mlp->dev);
kfree(mlp);
}
list_del(&idev->global_list);
kfree(idev);
return 0;
unlock:
rtnl_unlock();
return retval;
}
/*
* Delete a single network-interface
*/
static int
isdn_net_delif(char *name)
{
/* FIXME: For compatibility, if a master isdn_net_dev is rm'ed,
* kill all slaves, too */
isdn_net_dev *idev = isdn_net_findif(name);
if (!idev)
return -ENODEV;
return isdn_net_dev_delete(idev);
}
/*
* Set interface-parameters.
* Always set all parameters, so the user-level application is responsible
* for not overwriting existing setups. It has to get the current
* setup first, if only selected parameters are to be changed.
*/
static int
isdn_net_setcfg(isdn_net_ioctl_cfg *cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *mlp;
ulong features;
int i, retval;
if (!idev)
return -ENODEV;
mlp = idev->mlp;
rtnl_lock();
if (netif_running(&mlp->dev)) {
retval = -EBUSY;
goto out;
}
/* See if any registered driver supports the features we want */
features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
for (i = 0; i < ISDN_MAX_DRIVERS; i++)
if (dev->drv[i] &&
(dev->drv[i]->interface->features & features) == features)
break;
if (i == ISDN_MAX_DRIVERS) {
printk(KERN_WARNING "isdn_net: No driver with selected features\n");
retval = -ENODEV;
goto out;
}
retval = isdn_net_set_encap(mlp, cfg->p_encap);
if (retval)
goto out;
retval = isdn_net_bind(idev, cfg);
if (retval)
goto out;
strncpy(mlp->msn, cfg->eaz, ISDN_MSNLEN-1);
mlp->msn[ISDN_MSNLEN-1] = 0;
mlp->onhtime = cfg->onhtime;
idev->charge = cfg->charge;
mlp->l2_proto = cfg->l2_proto;
mlp->l3_proto = cfg->l3_proto;
mlp->cbdelay = cfg->cbdelay * HZ / 5;
mlp->dialmax = cfg->dialmax;
mlp->triggercps = cfg->triggercps;
mlp->slavedelay = cfg->slavedelay * HZ;
idev->pppbind = cfg->pppbind;
mlp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
mlp->dialwait = cfg->dialwait * HZ;
if (cfg->secure)
mlp->flags |= ISDN_NET_SECURE;
else
mlp->flags &= ~ISDN_NET_SECURE;
if (cfg->cbhup)
mlp->flags |= ISDN_NET_CBHUP;
else
mlp->flags &= ~ISDN_NET_CBHUP;
switch (cfg->callback) {
case 0:
mlp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
break;
case 1:
mlp->flags |= ISDN_NET_CALLBACK;
mlp->flags &= ~ISDN_NET_CBOUT;
break;
case 2:
mlp->flags |= ISDN_NET_CBOUT;
mlp->flags &= ~ISDN_NET_CALLBACK;
break;
}
mlp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */
if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
retval = -EINVAL;
goto out;
}
mlp->flags |= cfg->dialmode; /* turn on selected bits */
if (mlp->flags & ISDN_NET_DM_OFF)
isdn_net_hangup(idev);
if (cfg->chargehup)
mlp->hupflags |= ISDN_CHARGEHUP;
else
mlp->hupflags &= ~ISDN_CHARGEHUP;
if (cfg->ihup)
mlp->hupflags |= ISDN_INHUP;
else
mlp->hupflags &= ~ISDN_INHUP;
if (cfg->chargeint > 10) {
idev->chargeint = cfg->chargeint * HZ;
idev->charge_state = ST_CHARGE_HAVE_CINT;
mlp->hupflags |= ISDN_MANCHARGE;
}
retval = 0;
out:
rtnl_unlock();
return retval;
}
/*
* Perform get-interface-parameters.ioctl
*/
static int
isdn_net_getcfg(isdn_net_ioctl_cfg *cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *mlp;
if (!idev)
return -ENODEV;
mlp = idev->mlp;
strcpy(cfg->eaz, mlp->msn);
cfg->exclusive = idev->exclusive >= 0;
if (idev->pre_device >= 0) {
sprintf(cfg->drvid, "%s,%d", dev->drvid[idev->pre_device],
idev->pre_channel);
} else {
cfg->drvid[0] = '\0';
}
cfg->onhtime = mlp->onhtime;
cfg->charge = idev->charge;
cfg->l2_proto = mlp->l2_proto;
cfg->l3_proto = mlp->l3_proto;
cfg->p_encap = mlp->p_encap;
cfg->secure = (mlp->flags & ISDN_NET_SECURE) ? 1 : 0;
cfg->callback = 0;
if (mlp->flags & ISDN_NET_CALLBACK)
cfg->callback = 1;
if (mlp->flags & ISDN_NET_CBOUT)
cfg->callback = 2;
cfg->cbhup = (mlp->flags & ISDN_NET_CBHUP) ? 1 : 0;
cfg->dialmode = mlp->flags & ISDN_NET_DIALMODE_MASK;
cfg->chargehup = (mlp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
cfg->ihup = (mlp->hupflags & ISDN_INHUP) ? 1 : 0;
cfg->cbdelay = mlp->cbdelay * 5 / HZ;
cfg->dialmax = mlp->dialmax;
cfg->triggercps = mlp->triggercps;
cfg->slavedelay = mlp->slavedelay / HZ;
cfg->chargeint = (mlp->hupflags & ISDN_CHARGEHUP) ?
(idev->chargeint / HZ) : 0;
cfg->pppbind = idev->pppbind;
cfg->dialtimeout = mlp->dialtimeout >= 0 ? mlp->dialtimeout / HZ : -1;
cfg->dialwait = mlp->dialwait / HZ;
if (idev->slaves.next != &mlp->slaves)
strcpy(cfg->slave, list_entry(idev->slaves.next, isdn_net_dev, slaves)->name);
else
cfg->slave[0] = '\0';
if (strcmp(mlp->dev.name, idev->name))
strcpy(cfg->master, mlp->dev.name);
else
cfg->master[0] = '\0';
return 0;
}
/*
* Add a phone-number to an interface.
*/
static int
isdn_net_addphone(isdn_net_ioctl_phone *phone)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
struct isdn_net_phone *n;
int retval = 0;
if (!idev)
return -ENODEV;
rtnl_lock();
if (netif_running(&idev->mlp->dev)) {
retval = -EBUSY;
goto out;
}
n = kmalloc(sizeof(*n), GFP_KERNEL);
if (!n) {
retval = -ENOMEM;
goto out;
}
strcpy(n->num, phone->phone);
list_add_tail(&n->list, &idev->mlp->phone[phone->outgoing & 1]);
out:
rtnl_unlock();
return retval;
}
/*
* Delete a phone-number from an interface.
*/
static int
isdn_net_delphone(isdn_net_ioctl_phone *phone)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
struct isdn_net_phone *n;
int retval;
if (!idev)
return -ENODEV;
rtnl_lock();
if (netif_running(&idev->mlp->dev)) {
retval = -EBUSY;
goto out;
}
retval = -EINVAL;
list_for_each_entry(n, &idev->mlp->phone[phone->outgoing & 1], list) {
if (!strcmp(n->num, phone->phone)) {
list_del(&n->list);
kfree(n);
retval = 0;
break;
}
}
out:
rtnl_unlock();
return retval;
}
/*
* Copy a string of all phone-numbers of an interface to user space.
*/
static int
isdn_net_getphone(isdn_net_ioctl_phone * phone, char *phones)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
int count = 0;
char *buf = (char *)__get_free_page(GFP_KERNEL);
struct isdn_net_phone *n;
if (!buf)
return -ENOMEM;
if (!idev) {
count = -ENODEV;
goto free;
}
list_for_each_entry(n, &idev->mlp->phone[phone->outgoing & 1], list) {
strcpy(&buf[count], n->num);
count += strlen(n->num);
buf[count++] = ' ';
if (count > PAGE_SIZE - ISDN_MSNLEN - 1)
break;
}
if (!count) /* list was empty? */
count++;
buf[count-1] = 0;
if (copy_to_user(phones, buf, count))
count = -EFAULT;
free:
free_page((unsigned long)buf);
return count;
}
/*
* Force a net-interface to dial out.
*/
static int
isdn_net_dial(char *name)
{
isdn_net_dev *idev = isdn_net_findif(name);
if (!idev)
return -ENODEV;
return isdn_net_dev_dial(idev);
}
/*
* Force a hangup of a network-interface.
*/
static int
isdn_net_force_hangup(char *name) // FIXME rename?
{
isdn_net_dev *idev = isdn_net_findif(name);
isdn_net_dev *p;
if (!idev)
return -ENODEV;
if (idev->isdn_slot < 0)
return -ENOTCONN;
p = idev->slave;
/* If this interface has slaves, do a hangup for them also. */
while (p) {
isdn_net_hangup(p);
p = p->slave;
}
isdn_net_hangup(idev);
return 0;
}
/*
* Copy a string containing the peer's phone number of a connected interface
* to user space.
*/
static int
isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
int idx;
if (!idev)
return -ENODEV;
/* FIXME
* Theoretical race: while this executes, the remote number might
* become invalid (hang up) or change (new connection), resulting
* in (partially) wrong number copied to user. This race
* currently ignored.
*/
idx = idev->isdn_slot;
if (idx < 0)
return -ENOTCONN;
/* for pre-bound channels, we need this extra check */
if (strncmp(isdn_slot_num(idx), "???", 3) == 0 )
return -ENOTCONN;
strncpy(phone->phone, isdn_slot_num(idx), ISDN_MSNLEN);
phone->outgoing = USG_OUTGOING(isdn_slot_usage(idx));
if (copy_to_user(peer, phone, sizeof(*peer)))
return -EFAULT;
return 0;
}
/*
* ioctl on /dev/isdnctrl, used to configure ISDN net interfaces
*/
int
isdn_net_ioctl(struct inode *ino, struct file *file, uint cmd, ulong arg)
{
/* Save stack space */
union {
char name[10];
char bname[20];
isdn_net_ioctl_phone phone;
isdn_net_ioctl_cfg cfg;
} iocpar;
int retval;
#define name iocpar.name
#define bname iocpar.bname
#define phone iocpar.phone
#define cfg iocpar.cfg
name[sizeof(name)-1] = 0;
bname[sizeof(bname)-1] = 0;
down(&sem);
switch (cmd) {
case IIOCNETAIF: /* add an interface */
if (copy_from_user(name, (char *) arg, sizeof(name) - 1)) {
retval = -EFAULT;
break;
}
retval = isdn_net_addif(name, NULL);
break;
case IIOCNETASL: /* add slave to an interface */
if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1)) {
retval = -EFAULT;
break;
}
retval = isdn_net_addslave(bname);
break;
case IIOCNETDIF: /* delete an interface */
if (copy_from_user(name, (char *) arg, sizeof(name) - 1)) {
retval = -EFAULT;
break;
}
retval = isdn_net_delif(name);
break;
case IIOCNETSCF: /* set config */
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))) {
retval = -EFAULT;
break;
}
retval = isdn_net_setcfg(&cfg);
break;
case IIOCNETGCF: /* get config */
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))) {
retval = -EFAULT;
break;
}
retval = isdn_net_getcfg(&cfg);
if (retval)
break;
if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
retval = -EFAULT;
break;
case IIOCNETANM: /* add a phone number */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
break;
}
retval = isdn_net_addphone(&phone);
break;
case IIOCNETGNM: /* get list of phone numbers */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
break;
}
retval = isdn_net_getphone(&phone, (char *) arg);
break;
case IIOCNETDNM: /* delete a phone number */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
break;
}
retval = isdn_net_delphone(&phone);
break;
case IIOCNETDIL: /* trigger dial-out */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_net_dial(name);
break;
case IIOCNETHUP: /* hangup */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_net_force_hangup(name);
break;
case IIOCNETGPN: /* Get peer phone number of a connected interface */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
}
retval = isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
break;
#ifdef CONFIG_ISDN_PPP
case IIOCNETALN: /* Add link */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_ppp_dial_slave(name);
break;
case IIOCNETDLN: /* Delete link */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_ppp_hangup_slave(name);
break;
#endif
default:
retval = -ENOTTY;
}
up(&sem);
return retval;
#undef name
#undef bname
#undef iocts
#undef phone
#undef cfg
}
/*
* Hang up all network-interfaces
*/
void
isdn_net_hangup_all(void)
{
isdn_net_dev *idev;
down(&sem);
list_for_each_entry(idev, &isdn_net_devs, global_list)
isdn_net_hangup(idev);
up(&sem);
}
/*
* Remove all network-interfaces
*/
void
isdn_net_exit(void)
{
isdn_net_dev *idev;
int retval;
down(&sem);
while (!list_empty(&isdn_net_devs)) {
idev = list_entry(isdn_net_devs.next, isdn_net_dev, global_list);
retval = isdn_net_dev_delete(idev);
/* can only fail if an interface is still running.
* In this case, an elevated module use count should
* have prevented this function from being called in
* the first place */
if (retval)
isdn_BUG();
}
up(&sem);
}
/* ====================================================================== */
/* interface to network layer */
/* ====================================================================== */
/*
* Open/initialize the board.
*/
static int
isdn_net_open(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
int retval = 0;
if (!lp->ops)
return -ENODEV;
if (lp->ops->open)
retval = lp->ops->open(lp);
if (!retval)
return retval;
netif_start_queue(dev);
return 0;
}
/*
* Shutdown a net-interface.
*/
// FIXME share?
static int
isdn_net_close(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
struct list_head *l, *n;
isdn_net_dev *sdev;
if (lp->ops->close)
lp->ops->close(lp);
netif_stop_queue(dev);
list_for_each_safe(l, n, &lp->online) {
sdev = list_entry(l, isdn_net_dev, online);
isdn_net_hangup(sdev);
}
return 0;
}
/*
* Get statistics
*/
static struct net_device_stats *
isdn_net_get_stats(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
return &lp->stats;
}
/*
* Transmit timeout
*/
static void
isdn_net_tx_timeout(struct net_device *dev)
{
printk(KERN_WARNING "isdn_tx_timeout dev %s\n", dev->name);
netif_wake_queue(dev);
}
/*
* Interface-setup. (just after registering a new interface)
*/
static int
isdn_init_netif(struct net_device *ndev)
{
/* Setup the generic properties */
ndev->mtu = 1500;
ndev->tx_queue_len = 10;
ndev->open = &isdn_net_open;
ndev->hard_header_len = ETH_HLEN + isdn_hard_header_len();
ndev->stop = &isdn_net_close;
ndev->get_stats = &isdn_net_get_stats;
ndev->tx_timeout = isdn_net_tx_timeout;
ndev->watchdog_timeo = ISDN_NET_TX_TIMEOUT;
return 0;
}
/* ====================================================================== */
static void
isdn_net_tasklet(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
struct sk_buff *skb;
spin_lock_bh(&idev->xmit_lock);
while (!isdn_net_dev_busy(idev)) {
skb = skb_dequeue(&idev->super_tx_queue);
if (!skb)
break;
isdn_net_writebuf_skb(idev, skb);
}
spin_unlock_bh(&idev->xmit_lock);
}
/* ====================================================================== */
static void
isdn_net_dial_timer(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_handle_event(idev, idev->dial_event, NULL);
}
/*
* Perform auto-hangup for net-interfaces.
*
* auto-hangup:
* Increment idle-counter (this counter is reset on any incoming or
* outgoing packet), if counter exceeds configured limit either do a
* hangup immediately or - if configured - wait until just before the next
* charge-info.
*/
static void
isdn_net_hup_timer(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_local *mlp = idev->mlp;
if (!isdn_net_online(idev)) {
isdn_BUG();
return;
}
dbg_net_dial("%s: huptimer %d, onhtime %d, chargetime %ld, chargeint %d\n",
idev->name, idev->huptimer, mlp->onhtime, idev->chargetime, idev->chargeint);
if (mlp->onhtime == 0)
return;
if (idev->huptimer++ <= mlp->onhtime)
goto mod_timer;
if ((mlp->hupflags & (ISDN_MANCHARGE | ISDN_CHARGEHUP)) == (ISDN_MANCHARGE | ISDN_CHARGEHUP)) {
while (time_after(jiffies, idev->chargetime + idev->chargeint))
idev->chargetime += idev->chargeint;
if (time_after(jiffies, idev->chargetime + idev->chargeint - 2 * HZ)) {
if (idev->outgoing || mlp->hupflags & ISDN_INHUP) {
isdn_net_hangup(idev);
return;
}
}
} else if (idev->outgoing) {
if (mlp->hupflags & ISDN_CHARGEHUP) {
if (idev->charge_state != ST_CHARGE_HAVE_CINT) {
dbg_net_dial("%s: did not get CINT\n", idev->name);
isdn_net_hangup(idev);
return;
} else if (time_after(jiffies, idev->chargetime + idev->chargeint)) {
dbg_net_dial("%s: chtime = %lu, chint = %d\n",
idev->name, idev->chargetime, idev->chargeint);
isdn_net_hangup(idev);
return;
}
}
} else if (mlp->hupflags & ISDN_INHUP) {
isdn_net_hangup(idev);
return;
}
mod_timer:
mod_timer(&idev->hup_timer, idev->hup_timer.expires + HZ);
}
...@@ -312,7 +312,6 @@ struct isdn_netif_ops { ...@@ -312,7 +312,6 @@ struct isdn_netif_ops {
/* Local interface-data */ /* Local interface-data */
typedef struct isdn_net_local_s { typedef struct isdn_net_local_s {
ulong magic; ulong magic;
spinlock_t lock;
struct net_device_stats stats; /* Ethernet Statistics */ struct net_device_stats stats; /* Ethernet Statistics */
int flags; /* Connection-flags */ int flags; /* Connection-flags */
int dialmax; /* Max. Number of Dial-retries */ int dialmax; /* Max. Number of Dial-retries */
...@@ -402,7 +401,7 @@ typedef struct isdn_net_dev_s { ...@@ -402,7 +401,7 @@ typedef struct isdn_net_dev_s {
struct tasklet_struct tlet; struct tasklet_struct tlet;
isdn_net_local *mlp; /* Ptr to master device for all devs*/ isdn_net_local *mlp; /* Ptr to master device for all devs*/
struct isdn_net_dev_s *slave; /* Ptr to Slave device for masters */ struct isdn_net_dev_s *slave; /* Ptr to Slave device for masters */ // FIXME kill
struct list_head slaves; /* Members of local->slaves */ struct list_head slaves; /* Members of local->slaves */
struct list_head online; /* Members of local->online */ struct list_head online; /* Members of local->online */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment