]> git.neil.brown.name Git - history.git/commitdiff
ISDN: New file for net interface config and basic setup
authorKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
Sat, 5 Oct 2002 13:25:11 +0000 (08:25 -0500)
committerKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
Sat, 5 Oct 2002 13:25:11 +0000 (08:25 -0500)
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.

drivers/isdn/i4l/Makefile
drivers/isdn/i4l/isdn_common.c
drivers/isdn/i4l/isdn_common.h
drivers/isdn/i4l/isdn_net.c
drivers/isdn/i4l/isdn_net.h
drivers/isdn/i4l/isdn_net_lib.c [new file with mode: 0644]
include/linux/isdn.h

index 65055e42faf22ae8a06f3d25876e2938801195a0..c98022e5becbaf3029082663e6f5db5eed93a8c8 100644 (file)
@@ -11,9 +11,10 @@ obj-$(CONFIG_ISDN_PPP_BSDCOMP)               += isdn_bsdcomp.o
 
 # Multipart objects.
 
-isdn-objs                              := isdn_net.o isdn_tty.o \
-                                          isdn_v110.o isdn_common.o \
-                                          isdn_ciscohdlck.o
+isdn-objs                              := isdn_net.o isdn_net_lib.o \
+                                          isdn_ciscohdlck.o \
+                                          isdn_tty.o isdn_v110.o \
+                                          isdn_common.o \
 
 # Optional parts of multipart objects.
 
index 5ccf21779ce80d23b297540094d16ea587b78417..7fec01fe0f64198027c1afec1f8902552fa03df7 100644 (file)
@@ -1010,19 +1010,6 @@ static int
 isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 {
        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) {
        case IIOCGETDVR:
@@ -1044,26 +1031,11 @@ isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                } else
                        return -EINVAL;
                break;
-#ifdef CONFIG_NETDEVICES
        case IIOCNETGPN:
-               /* Get peer phone number of a connected 
-                * 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
+               return isdn_net_ioctl(inode, file, cmd, arg);
        default:
                return -EINVAL;
        }
-
-#undef name
-#undef bname
-#undef iocts
-#undef phone
-#undef cfg
 }
 
 static struct file_operations isdn_status_fops =
@@ -1221,19 +1193,15 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
        int ret;
        int i;
        char *p;
-       union iocpar {
-               char name[10];
+       /* save stack space */
+       union {
                char bname[20];
                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
+#define bname iocpar.bname
+
 /*
  * 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
@@ -1242,134 +1210,19 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
  * are serialized by means of a semaphore.
  */
        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:
-               /* 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:
-               /* 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:
-               /* 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:
-               /* 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:
-               /* 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:
-               /* 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:
-               /* 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:
-               /* 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:
-               /* 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:
-               if (!arg)
-                       return -EINVAL;
-               if (copy_from_user(name, (char *) arg, sizeof(name)))
-                       return -EFAULT;
-               return isdn_ppp_dial_slave(name);
        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:
-               /* Force hangup of a network-interface */
-               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 */
+               return isdn_net_ioctl(inode, file, cmd, arg);
        case IIOCSETVER:
                dev->net_verbose = arg;
                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)
                } else
                        return -EINVAL;
        }
-
-#undef name
-#undef bname
 #undef iocts
-#undef phone
-#undef cfg
+#undef bname
 }
 
 static struct file_operations isdn_ctrl_fops =
@@ -2430,7 +2279,7 @@ static int __init isdn_init(void)
        printk("\n");
 #endif
        isdn_info_update();
-       isdn_net_init_module();
+       isdn_net_init();
        return 0;
 
  err_tty_modem:
@@ -2456,8 +2305,7 @@ static void __exit isdn_exit(void)
 #endif
        save_flags(flags);
        cli();
-       if (isdn_net_rmall() < 0)
-               BUG();
+       isdn_net_exit();
 
        isdn_tty_exit();
        if (unregister_chrdev(ISDN_MAJOR, "isdn"))
index c3ae7d57c49486dfe56eb4f54f8da38f56ffcdf2..f151fcc0268528e57830f3d79d25f5c8113358e9 100644 (file)
@@ -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__)
 
+extern struct list_head isdn_net_devs;
+
 /* Prototypes */
 extern void isdn_MOD_INC_USE_COUNT(void);
 extern void isdn_MOD_DEC_USE_COUNT(void);
@@ -82,8 +84,6 @@ struct dial_info {
        unsigned char *phone;
 };
 
-extern struct list_head isdn_net_devs;
-
 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_all_eaz(int slot);
index fb07f6bbd7015f3bad503866dff7244d2b42ec2d..71a7d94b466673980f43d1b9126a889c3f5add49 100644 (file)
@@ -43,12 +43,6 @@ enum {
        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_* */
 enum {
        EV_NET_DIAL            = 0x200,
@@ -59,8 +53,6 @@ enum {
        EV_NET_TIMER_CB        = 0x205,
 };
 
-LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */
-
 /*
  * Outline of new tbusy handling: 
  *
@@ -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...
  */
 
-#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)
 {
        return idev->dialstate == ST_ACTIVE;
@@ -193,10 +167,8 @@ int isdn_net_online(isdn_net_dev *idev)
 
 /* Prototypes */
 
-static int isdn_net_force_dial_idev(isdn_net_dev *);
 static void do_dialout(isdn_net_dev *idev);
-static 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);
+int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
 
 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)
        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)
  */
@@ -298,66 +249,6 @@ isdn_net_bind_channel(isdn_net_dev *idev, int idx)
        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)
 {
        isdn_net_rm_from_bundle(idev);
@@ -413,14 +304,6 @@ isdn_net_stat_callback(int idx, isdn_ctrl *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
  * of interface.
  */
@@ -450,7 +333,6 @@ do_dialout(isdn_net_dev *idev)
 {
        isdn_net_local *mlp = idev->mlp;
        int i;
-       unsigned long flags;
        struct isdn_net_phone *phone;
        struct dial_info dial = {
                .l2_proto = mlp->l2_proto,
@@ -463,9 +345,7 @@ do_dialout(isdn_net_dev *idev)
        if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF)
                return;
        
-       spin_lock_irqsave(&mlp->lock, flags);
        if (list_empty(&mlp->phone[1])) {
-               spin_unlock_irqrestore(&mlp->lock, flags);
                return;
        }
        i = 0;
@@ -481,7 +361,6 @@ do_dialout(isdn_net_dev *idev)
  found:
        idev->dial++;
        dial.phone = phone->num;
-       spin_unlock_irqrestore(&mlp->lock, flags);
 
        if (idev->dialretry > mlp->dialmax) {
                if (mlp->dialtimeout == 0) {
@@ -524,7 +403,7 @@ do_dialout(isdn_net_dev *idev)
 /* For EV_NET_DIAL, returns 1 if timer callback is needed 
  * 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_local *mlp = idev->mlp;
@@ -714,17 +593,6 @@ isdn_net_hangup(isdn_net_dev *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 {
        unsigned short source;
        unsigned short dest;
@@ -843,21 +711,6 @@ isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
        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
  * 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)
                                if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
                                        sdev = idev->slave;
                                        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)
        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
 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;
        }
 
-       if (isdn_net_force_dial_idev(idev) < 0)
+       if (isdn_net_dev_dial(idev) < 0)
                goto discard;
 
        /* Log packet, which triggered dialing */
@@ -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.
  */
@@ -1095,24 +907,6 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb)
        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
 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)
                }
                dbg_net_icall("n_fi: match2\n");
                if (mlp->flags & ISDN_NET_SECURE) {
-                       spin_lock_irqsave(&mlp->lock, flags);
                        list_for_each_entry(n, &mlp->phone[0], list) {
                                if (!isdn_msncmp(nr, n->num)) {
-                                       spin_unlock_irqrestore(&mlp->lock, flags);
                                        goto found;
                                }
                        }
-                       spin_unlock_irqrestore(&mlp->lock, flags);
                        continue;
                }
        found:
@@ -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.
- */
-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().
+ * Trigger dialing out
  */
-static int
-isdn_net_force_dial_idev(isdn_net_dev *idev)
+int
+isdn_net_dev_dial(isdn_net_dev *idev)
 {
        int slot;
-       unsigned long flags;
        isdn_net_local *mlp = idev->mlp;
 
        if (isdn_net_bound(idev))
                return -EBUSY;
 
-       save_flags(flags);
-       cli();
-
        if (idev->exclusive >= 0)
                slot = idev->exclusive;
        else
@@ -1379,13 +1149,11 @@ isdn_net_force_dial_idev(isdn_net_dev *idev)
                goto err;;
 
        /* Initiate dialing */
-       restore_flags(flags);
        init_dialout(idev);
 
        return 0;
 
  err:
-       restore_flags(flags);
        return -EAGAIN;
 }
 
@@ -1402,665 +1170,7 @@ isdn_net_dial_req(isdn_net_dev *idev)
        if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
                return -EBUSY;
 
-       return isdn_net_force_dial_idev(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;
+       return isdn_net_dev_dial(idev);
 }
 
 // ISDN_NET_ENCAP_IPTYP
@@ -2203,7 +1313,7 @@ static struct isdn_netif_ops ether_ops = {
 // ======================================================================
 
 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_RAWIP,      &rawip_ops);
index a4d898f749978d7bc1888154593a60eaa950c5ff..3b628d928e4966daccb6ab651559d8de97be0bac 100644 (file)
 #define CISCO_SLARP_REPLY     1
 #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_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_dev_dial(isdn_net_dev *idev);
 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_dial_req(isdn_net_dev *);
 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 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
 
diff --git a/drivers/isdn/i4l/isdn_net_lib.c b/drivers/isdn/i4l/isdn_net_lib.c
new file mode 100644 (file)
index 0000000..3185a86
--- /dev/null
@@ -0,0 +1,1102 @@
+/*
+ * 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);
+}
+
index e1d2a910400c692e61c9db7c4d685e64672592c6..2571ec195463535099c6a3796f3c8ccd09a30830 100644 (file)
@@ -312,7 +312,6 @@ struct isdn_netif_ops {
 /* Local interface-data */
 typedef struct isdn_net_local_s {
   ulong                  magic;
-  spinlock_t             lock;
   struct net_device_stats stats;       /* Ethernet Statistics              */
   int                    flags;        /* Connection-flags                 */
   int                    dialmax;      /* Max. Number of Dial-retries      */
@@ -402,7 +401,7 @@ typedef struct isdn_net_dev_s {
   struct tasklet_struct  tlet;
 
   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       online;       /* Members of local->online         */