# 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.
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:
} 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 =
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
* 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);
} else
return -EINVAL;
}
-
-#undef name
-#undef bname
#undef iocts
-#undef phone
-#undef cfg
+#undef bname
}
static struct file_operations isdn_ctrl_fops =
printk("\n");
#endif
isdn_info_update();
- isdn_net_init_module();
+ isdn_net_init();
return 0;
err_tty_modem:
#endif
save_flags(flags);
cli();
- if (isdn_net_rmall() < 0)
- BUG();
+ isdn_net_exit();
isdn_tty_exit();
if (unregister_chrdev(ISDN_MAJOR, "isdn"))
#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);
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);
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,
EV_NET_TIMER_CB = 0x205,
};
-LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */
-
/*
* Outline of new tbusy handling:
*
* 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;
/* 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 $";
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)
*/
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);
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.
*/
{
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,
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;
found:
idev->dial++;
dial.phone = phone->num;
- spin_unlock_irqrestore(&mlp->lock, flags);
if (idev->dialretry > mlp->dialmax) {
if (mlp->dialtimeout == 0) {
/* 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;
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;
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
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);
}
}
}
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)
{
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 */
}
-/*
- * 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.
*/
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)
{
}
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:
}
/*
- * 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
goto err;;
/* Initiate dialing */
- restore_flags(flags);
init_dialout(idev);
return 0;
err:
- restore_flags(flags);
return -EAGAIN;
}
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
// ======================================================================
void
-isdn_net_init_module(void)
+isdn_net_init(void)
{
register_isdn_netif(ISDN_NET_ENCAP_ETHER, ðer_ops);
register_isdn_netif(ISDN_NET_ENCAP_RAWIP, &rawip_ops);
#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
--- /dev/null
+/*
+ * 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);
+}
+
/* 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 */
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 */