📄 isdn_common.c
字号:
if (minor <= ISDN_MINOR_PPPMAX) isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);#endif out: unlock_kernel(); return 0;}static struct file_operations isdn_fops ={ .owner = THIS_MODULE, .llseek = no_llseek, .read = isdn_read, .write = isdn_write, .poll = isdn_poll, .ioctl = isdn_ioctl, .open = isdn_open, .release = isdn_close,};char *isdn_map_eaz2msn(char *msn, int di){ isdn_driver_t *this = dev->drv[di]; int i; if (strlen(msn) == 1) { i = msn[0] - '0'; if ((i >= 0) && (i <= 9)) if (strlen(this->msn2eaz[i])) return (this->msn2eaz[i]); } return (msn);}/* * Find an unused ISDN-channel, whose feature-flags match the * given L2- and L3-protocols. */#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))/* * This function must be called with holding the dev->lock. */intisdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev ,int pre_chan, char *msn){ int i; ulong features; ulong vfeatures; features = ((1 << l2_proto) | (0x10000 << l3_proto)); vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) & ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038)); /* If Layer-2 protocol is V.110, accept drivers with * transparent feature even if these don't support V.110 * because we can emulate this in linklevel. */ for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (USG_NONE(dev->usage[i]) && (dev->drvmap[i] != -1)) { int d = dev->drvmap[i]; if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) && ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) continue; if (!strcmp(isdn_map_eaz2msn(msn, d), "-")) continue; if (dev->usage[i] & ISDN_USAGE_DISABLED) continue; /* usage not allowed */ if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { if (((dev->drv[d]->interface->features & features) == features) || (((dev->drv[d]->interface->features & vfeatures) == vfeatures) && (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) { if ((pre_dev < 0) || (pre_chan < 0)) { dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; dev->usage[i] |= usage; isdn_info_update(); return i; } else { if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) { dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; dev->usage[i] |= usage; isdn_info_update(); return i; } } } } } return -1;}/* * Set state of ISDN-channel to 'unused' */voidisdn_free_channel(int di, int ch, int usage){ int i; for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) && (dev->drvmap[i] == di) && (dev->chanmap[i] == ch)) { dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE); strcpy(dev->num[i], "???"); dev->ibytes[i] = 0; dev->obytes[i] = 0;// 20.10.99 JIM, try to reinitialize v110 ! dev->v110emu[i] = 0; atomic_set(&(dev->v110use[i]), 0); isdn_v110_close(dev->v110[i]); dev->v110[i] = NULL;// 20.10.99 JIM, try to reinitialize v110 ! isdn_info_update(); skb_queue_purge(&dev->drv[di]->rpqueue[ch]); }}/* * Cancel Exclusive-Flag for ISDN-channel */voidisdn_unexclusive_channel(int di, int ch){ int i; for (i = 0; i < ISDN_MAX_CHANNELS; i++) if ((dev->drvmap[i] == di) && (dev->chanmap[i] == ch)) { dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE; isdn_info_update(); return; }}/* * writebuf replacement for SKB_ABLE drivers */static intisdn_writebuf_stub(int drvidx, int chan, const u_char __user * buf, int len){ int ret; int hl = dev->drv[drvidx]->interface->hl_hdrlen; struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC); if (!skb) return 0; skb_reserve(skb, hl); copy_from_user(skb_put(skb, len), buf, len); ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb); if (ret <= 0) dev_kfree_skb(skb); if (ret > 0) dev->obytes[isdn_dc2minor(drvidx, chan)] += ret; return ret;}/* * Return: length of data on success, -ERRcode on failure. */intisdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb){ int ret; struct sk_buff *nskb = NULL; int v110_ret = skb->len; int idx = isdn_dc2minor(drvidx, chan); if (dev->v110[idx]) { atomic_inc(&dev->v110use[idx]); nskb = isdn_v110_encode(dev->v110[idx], skb); atomic_dec(&dev->v110use[idx]); if (!nskb) return 0; v110_ret = *((int *)nskb->data); skb_pull(nskb, sizeof(int)); if (!nskb->len) { dev_kfree_skb(nskb); return v110_ret; } /* V.110 must always be acknowledged */ ack = 1; ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb); } else { int hl = dev->drv[drvidx]->interface->hl_hdrlen; if( skb_headroom(skb) < hl ){ /* * This should only occur when new HL driver with * increased hl_hdrlen was loaded after netdevice * was created and connected to the new driver. * * The V.110 branch (re-allocates on its own) does * not need this */ struct sk_buff * skb_tmp; skb_tmp = skb_realloc_headroom(skb, hl); printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed"); if (!skb_tmp) return -ENOMEM; /* 0 better? */ ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp); if( ret > 0 ){ dev_kfree_skb(skb); } else { dev_kfree_skb(skb_tmp); } } else { ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb); } } if (ret > 0) { dev->obytes[idx] += ret; if (dev->v110[idx]) { atomic_inc(&dev->v110use[idx]); dev->v110[idx]->skbuser++; atomic_dec(&dev->v110use[idx]); /* For V.110 return unencoded data length */ ret = v110_ret; /* if the complete frame was send we free the skb; if not upper function will requeue the skb */ if (ret == skb->len) dev_kfree_skb(skb); } } else if (dev->v110[idx]) dev_kfree_skb(nskb); return ret;}static intisdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding){ int j, k, m; init_waitqueue_head(&d->st_waitq); if (d->flags & DRV_FLAG_RUNNING) return -1; if (n < 1) return 0; m = (adding) ? d->channels + n : n; if (dev->channels + n > ISDN_MAX_CHANNELS) { printk(KERN_WARNING "register_isdn: Max. %d channels supported\n", ISDN_MAX_CHANNELS); return -1; } if ((adding) && (d->rcverr)) kfree(d->rcverr); if (!(d->rcverr = kmalloc(sizeof(int) * m, GFP_ATOMIC))) { printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n"); return -1; } memset((char *) d->rcverr, 0, sizeof(int) * m); if ((adding) && (d->rcvcount)) kfree(d->rcvcount); if (!(d->rcvcount = kmalloc(sizeof(int) * m, GFP_ATOMIC))) { printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n"); if (!adding) kfree(d->rcverr); return -1; } memset((char *) d->rcvcount, 0, sizeof(int) * m); if ((adding) && (d->rpqueue)) { for (j = 0; j < d->channels; j++) skb_queue_purge(&d->rpqueue[j]); kfree(d->rpqueue); } if (!(d->rpqueue = kmalloc(sizeof(struct sk_buff_head) * m, GFP_ATOMIC))) { printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n"); if (!adding) { kfree(d->rcvcount); kfree(d->rcverr); } return -1; } for (j = 0; j < m; j++) { skb_queue_head_init(&d->rpqueue[j]); } if ((adding) && (d->rcv_waitq)) kfree(d->rcv_waitq); d->rcv_waitq = kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_ATOMIC); if (!d->rcv_waitq) { printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n"); if (!adding) { kfree(d->rpqueue); kfree(d->rcvcount); kfree(d->rcverr); } return -1; } d->snd_waitq = d->rcv_waitq + m; for (j = 0; j < m; j++) { init_waitqueue_head(&d->rcv_waitq[j]); init_waitqueue_head(&d->snd_waitq[j]); } dev->channels += n; for (j = d->channels; j < m; j++) for (k = 0; k < ISDN_MAX_CHANNELS; k++) if (dev->chanmap[k] < 0) { dev->chanmap[k] = j; dev->drvmap[k] = drvidx; break; } d->channels = m; return 0;}/* * Low-level-driver registration */static voidset_global_features(void){ int drvidx; dev->global_features = 0; for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) { if (!dev->drv[drvidx]) continue; if (dev->drv[drvidx]->interface) dev->global_features |= dev->drv[drvidx]->interface->features; }}#ifdef CONFIG_ISDN_DIVERSIONstatic char *map_drvname(int di){ if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) return(NULL); return(dev->drvid[di]); /* driver name */} /* map_drvname */static int map_namedrv(char *id){ int i; for (i = 0; i < ISDN_MAX_DRIVERS; i++) { if (!strcmp(dev->drvid[i],id)) return(i); } return(-1);} /* map_namedrv */int DIVERT_REG_NAME(isdn_divert_if *i_div){ if (i_div->if_magic != DIVERT_IF_MAGIC) return(DIVERT_VER_ERR); switch (i_div->cmd) { case DIVERT_CMD_REL: if (divert_if != i_div) return(DIVERT_REL_ERR); divert_if = NULL; /* free interface */ return(DIVERT_NO_ERR); case DIVERT_CMD_REG: if (divert_if) return(DIVERT_REG_ERR); i_div->ll_cmd = isdn_command; /* set command function */ i_div->drv_to_name = map_drvname; i_div->name_to_drv = map_namedrv; divert_if = i_div; /* remember interface */ return(DIVERT_NO_ERR); default: return(DIVERT_CMD_ERR); }} /* DIVERT_REG_NAME */EXPORT_SYMBOL(DIVERT_REG_NAME);#endif /* CONFIG_ISDN_DIVERSION */EXPORT_SYMBOL(register_isdn);#ifdef CONFIG_ISDN_PPPEXPORT_SYMBOL(isdn_ppp_register_compressor);EXPORT_SYMBOL(isdn_ppp_unregister_compressor);#endifintregister_isdn(isdn_if * i){ isdn_driver_t *d; int j; ulong flags; int drvidx; if (dev->drivers >= ISDN_MAX_DRIVERS) { printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n", ISDN_MAX_DRIVERS); return 0; } if (!i->writebuf_skb) { printk(KERN_WARNING "register_isdn: No write routine given.\n"); return 0; } if (!(d = kmalloc(sizeof(isdn_driver_t), GFP_KERNEL))) { printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n"); return 0; } memset((char *) d, 0, sizeof(isdn_driver_t)); d->maxbufsize = i->maxbufsize; d->pktcount = 0; d->stavail = 0; d->flags = DRV_FLAG_LOADED; d->online = 0; d->interface = i; d->channels = 0; spin_lock_irqsave(&dev->lock, flags); for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) if (!dev->drv[drvidx]) break; if (isdn_add_channels(d, drvidx, i->channels, 0)) { spin_unlock_irqrestore(&dev->lock, flags); kfree(d); return 0; } i->channels = drvidx; i->rcvcallb_skb = isdn_receive_skb_callback; i->statcallb = isdn_status_callback; if (!strlen(i->id)) sprintf(i->id, "line%d", drvidx); for (j = 0; j < drvidx; j++) if (!strcmp(i->id, dev->drvid[j])) sprintf(i->id, "line%d", drvidx); dev->drv[drvidx] = d; strcpy(dev->drvid[drvidx], i->id); isdn_info_update(); dev->drivers++; set_global_features(); spin_unlock_irqrestore(&dev->lock, flags); return 1;}/* ***************************************************************************** * And now the modules code. ***************************************************************************** */static char *isdn_getrev(const char *revision){ char *rev; char *p; if ((p = strchr(revision, ':'))) { rev = p + 2; p = strchr(rev, '$'); *--p = 0; } else rev = "???"; return rev;}/* * Allocate and initialize all data, register modem-devices */static int __init isdn_init(void){ int i; char tmprev[50]; if (!(dev = (isdn_dev *) vmalloc(sizeof(isdn_dev)))) { printk(KERN_WARNING "isdn: Could not allocate device-struct.\n"); return -EIO; } memset((char *) dev, 0, sizeof(isdn_dev)); init_timer(&dev->timer); dev->timer.function = isdn_timer_funct; spin_lock_init(&dev->lock); spin_lock_init(&dev->timerlock);#ifdef MODULE dev->owner = THIS_MODULE;#endif init_MUTEX(&dev->sem); init_waitqueue_head(&dev->info_waitq); for (i = 0; i < ISDN_MAX_CHANNELS; i++) { dev->drvmap[i] = -1; dev->chanmap[i] = -1; dev->m_idx[i] = -1; strcpy(dev->num[i], "???"); init_waitqueue_head(&dev->mdm.info[i].open_wait); init_waitqueue_head(&dev->mdm.info[i].close_wait); } if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { printk(KERN_WARNING "isdn: Could not register control devices\n"); vfree(dev); return -EIO; } if ((isdn_tty_modem_init()) < 0) { printk(KERN_WARNING "isdn: Could not register tty devices\n"); vfree(dev); unregister_chrdev(ISDN_MAJOR, "isdn"); return -EIO; }#ifdef CONFIG_ISDN_PPP if (isdn_ppp_init() < 0) { printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n"); isdn_tty_exit(); unregister_chrdev(ISDN_MAJOR, "isdn"); vfree(dev); return -EIO; }#endif /* CONFIG_ISDN_PPP */ strcpy(tmprev, isdn_revision); printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev)); strcpy(tmprev, isdn_tty_revision); printk("%s/", isdn_getrev(tmprev)); strcpy(tmprev, isdn_net_revision); printk("%s/", isdn_getrev(tmprev)); strcpy(tmprev, isdn_ppp_revision); printk("%s/", isdn_getrev(tmprev)); strcpy(tmprev, isdn_audio_revision); printk("%s/", isdn_getrev(tmprev)); strcpy(tmprev, isdn_v110_revision); printk("%s", isdn_getrev(tmprev));#ifdef MODULE printk(" loaded\n");#else printk("\n");#endif isdn_info_update(); return 0;}/* * Unload module */static void __exit isdn_exit(void){#ifdef CONFIG_ISDN_PPP isdn_ppp_cleanup();#endif if (isdn_net_rmall() < 0) { printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n"); return; } isdn_tty_exit(); unregister_chrdev(ISDN_MAJOR, "isdn"); del_timer(&dev->timer); /* call vfree with interrupts enabled, else it will hang */ vfree(dev); printk(KERN_NOTICE "ISDN-subsystem unloaded\n");}module_init(isdn_init);module_exit(isdn_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -