isdn_common.c
来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 2,372 行 · 第 1/4 页
C
2,372 行
retval = -ENODEV; goto out; } if (!dev->drv[drvidx]->stavail) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto out; } interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq)); } if (dev->drv[drvidx]->interface->readstat) { if (count > dev->drv[drvidx]->stavail) count = dev->drv[drvidx]->stavail; len = dev->drv[drvidx]->interface-> readstat(buf, count, 1, drvidx, isdn_minor2chan(minor)); } else { len = 0; } save_flags(flags); cli(); if (len) dev->drv[drvidx]->stavail -= len; else dev->drv[drvidx]->stavail = 0; restore_flags(flags); *off += len; retval = len; out: unlock_kernel(); return retval;}static ssize_tisdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off){ uint minor = minor(file->f_dentry->d_inode->i_rdev); int drvidx; int retval; if (off != &file->f_pos) return -ESPIPE; lock_kernel(); drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (drvidx < 0) { retval = -ENODEV; goto out; } if (!dev->drv[drvidx]->interface->writecmd) { retval = -EINVAL; goto out; } retval = dev->drv[drvidx]->interface-> writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor - ISDN_MINOR_CTRL)); out: unlock_kernel(); return retval;}static unsigned intisdn_ctrl_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; unsigned int minor = minor(file->f_dentry->d_inode->i_rdev); int drvidx; drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (drvidx < 0) /* driver deregistered while file open */ return POLLHUP; lock_kernel(); poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); mask = POLLOUT | POLLWRNORM; if (dev->drv[drvidx]->stavail) mask |= POLLIN | POLLRDNORM; unlock_kernel(); return mask;}static intisdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg){ isdn_ctrl c; int drvidx; int ret; int i; char *p; char *s; 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/* * 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 * service routines access user space and are not atomic. Therefor, ioctl's * manipulating the lists and ioctl's sleeping while accessing the lists * 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 (arg) { if (copy_from_user(name, (char *) arg, sizeof(name))) return -EFAULT; s = name; } else { s = NULL; } ret = down_interruptible(&dev->sem); if( ret ) return ret; if ((s = isdn_net_new(s, NULL))) { if (copy_to_user((char *) arg, s, strlen(s) + 1)){ ret = -EFAULT; } else { ret = 0; } } else ret = -ENODEV; up(&dev->sem); return ret; case IIOCNETASL: /* Add a slave to a network-interface */ if (arg) { if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1)) return -EFAULT; } else return -EINVAL; ret = down_interruptible(&dev->sem); if( ret ) return ret; if ((s = isdn_net_newslave(bname))) { if (copy_to_user((char *) arg, s, strlen(s) + 1)){ ret = -EFAULT; } else { ret = 0; } } else ret = -ENODEV; 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 */ case IIOCSETVER: dev->net_verbose = arg; printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose); return 0; case IIOCSETGST: if (arg) dev->global_flags |= ISDN_GLOBAL_STOPPED; else dev->global_flags &= ~ISDN_GLOBAL_STOPPED; printk(KERN_INFO "isdn: Global Mode %s\n", (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running"); return 0; case IIOCSETBRJ: drvidx = -1; if (arg) { int i; char *p; if (copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct))) return -EFAULT; if (strlen(iocts.drvid)) { if ((p = strchr(iocts.drvid, ','))) *p = 0; drvidx = -1; for (i = 0; i < ISDN_MAX_DRIVERS; i++) if (!(strcmp(dev->drvid[i], iocts.drvid))) { drvidx = i; break; } } } if (drvidx == -1) return -ENODEV; if (iocts.arg) dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS; else dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS; return 0; case IIOCSIGPRF: dev->profd = current; return 0; break; case IIOCGETPRF: /* Get all Modem-Profiles */ if (arg) { char *p = (char *) arg; int i; if ((ret = verify_area(VERIFY_WRITE, (void *) arg, (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS))) return ret; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if (copy_to_user(p, dev->mdm.info[i].emu.profile, ISDN_MODEM_NUMREG)) return -EFAULT; p += ISDN_MODEM_NUMREG; if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN)) return -EFAULT; p += ISDN_MSNLEN; if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN)) return -EFAULT; p += ISDN_LMSNLEN; } return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS; } else return -EINVAL; break; case IIOCSETPRF: /* Set all Modem-Profiles */ if (arg) { char *p = (char *) arg; int i; if ((ret = verify_area(VERIFY_READ, (void *) arg, (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS))) return ret; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if (copy_from_user(dev->mdm.info[i].emu.profile, p, ISDN_MODEM_NUMREG)) return -EFAULT; p += ISDN_MODEM_NUMREG; if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN)) return -EFAULT; p += ISDN_LMSNLEN; if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)) return -EFAULT; p += ISDN_MSNLEN; } return 0; } else return -EINVAL; break; case IIOCSETMAP: case IIOCGETMAP: /* Set/Get MSN->EAZ-Mapping for a driver */ if (arg) { if (copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct))) return -EFAULT; if (strlen(iocts.drvid)) { drvidx = -1; for (i = 0; i < ISDN_MAX_DRIVERS; i++) if (!(strcmp(dev->drvid[i], iocts.drvid))) { drvidx = i; break; } } else drvidx = 0; if (drvidx == -1) return -ENODEV; if (cmd == IIOCSETMAP) { int loop = 1; p = (char *) iocts.arg; i = 0; while (loop) { int j = 0; while (1) { if ((ret = verify_area(VERIFY_READ, p, 1))) return ret; get_user(bname[j], p++); switch (bname[j]) { case '\0': loop = 0; /* Fall through */ case ',': bname[j] = '\0'; strcpy(dev->drv[drvidx]->msn2eaz[i], bname); j = ISDN_MSNLEN; break; default: j++; } if (j >= ISDN_MSNLEN) break; } if (++i > 9) break; } } else { p = (char *) iocts.arg; for (i = 0; i < 10; i++) { sprintf(bname, "%s%s", strlen(dev->drv[drvidx]->msn2eaz[i]) ? dev->drv[drvidx]->msn2eaz[i] : "_", (i < 9) ? "," : "\0"); if (copy_to_user(p, bname, strlen(bname) + 1)) return -EFAULT; p += strlen(bname); } } return 0; } else return -EINVAL; case IIOCDBGVAR: if (arg) { if (copy_to_user((char *) arg, (char *) &dev, sizeof(ulong))) return -EFAULT; return 0; } else return -EINVAL; break; default: if ((cmd & IIOCDRVCTL) == IIOCDRVCTL) cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK; else return -EINVAL; if (arg) { int i; char *p; if (copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct))) return -EFAULT; if (strlen(iocts.drvid)) { if ((p = strchr(iocts.drvid, ','))) *p = 0; drvidx = -1; for (i = 0; i < ISDN_MAX_DRIVERS; i++) if (!(strcmp(dev->drvid[i], iocts.drvid))) { drvidx = i; break; } } else drvidx = 0; if (drvidx == -1) return -ENODEV; if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(isdn_ioctl_struct)))) return ret; c.driver = drvidx; c.command = ISDN_CMD_IOCTL; c.arg = cmd; memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong)); ret = isdn_command(&c); memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong)); if (copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct))) return -EFAULT; return ret; } else return -EINVAL; }#undef name#undef bname#undef iocts#undef phone#undef cfg}static struct file_operations isdn_ctrl_fops ={ owner: THIS_MODULE, llseek: no_llseek, read: isdn_ctrl_read, write: isdn_ctrl_write, poll: isdn_ctrl_poll, ioctl: isdn_ctrl_ioctl, open: isdn_ctrl_open, release: isdn_ctrl_release,};/* * file_operations for major 43, /dev/isdn* * stolen from drivers/char/misc.c */static intisdn_open(struct inode * inode, struct file * file){ int minor = minor(inode->i_rdev); int err = -ENODEV; struct file_operations *old_fops, *new_fops = NULL; if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) new_fops = fops_get(&isdn_ctrl_fops);#ifdef CONFIG_ISDN_PPP else if (minor >= ISDN_MINOR_PPP && minor <= ISDN_MINOR_PPPMAX) new_fops = fops_get(&isdn_ppp_fops);#endif else if (minor == ISDN_MINOR_STATUS) new_fops = fops_get(&isdn_status_fops); if (!new_fops) goto out; err = 0; old_fops = file->f_op; file->f_op = new_fops; if (file->f_op->open) { err = file->f_op->open(inode,file); if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } } fops_put(old_fops); out: return err;}static struct file_operations isdn_fops ={ owner: THIS_MODULE, open: isdn_open,};char *isdn_map_eaz2msn(char *msn, int di){ driver *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))intisdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev ,int pre_chan, char *msn){ int i; ulong flags; ulong features; ulong vfeatures; save_flags(flags); cli(); 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;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?