📄 capi.c
字号:
if (mp->tty && mp->ttyoutstop) {#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: send: tty stopped\n");#endif return 0; } while ((skb = skb_dequeue(&mp->outqueue)) != 0) { datahandle = mp->datahandle; len = (u16)skb->len; skb_push(skb, CAPI_DATA_B3_REQ_LEN); memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); capimsg_setu16(skb->data, 2, mp->ap->applid); capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); capimsg_setu8 (skb->data, 5, CAPI_REQ); capimsg_setu16(skb->data, 6, mp->msgid++); capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */ capimsg_setu16(skb->data, 16, len); /* Data length */ capimsg_setu16(skb->data, 18, datahandle); capimsg_setu16(skb->data, 20, 0); /* Flags */ if (capincci_add_ack(mp, datahandle) < 0) { skb_pull(skb, CAPI_DATA_B3_REQ_LEN); skb_queue_head(&mp->outqueue, skb); return count; } errcode = capi20_put_message(mp->ap, skb); if (errcode == CAPI_NOERROR) { mp->datahandle++; count++; mp->outbytes -= len;#ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n", datahandle, len);#endif continue; } capiminor_del_ack(mp, datahandle); if (errcode == CAPI_SENDQUEUEFULL) { skb_pull(skb, CAPI_DATA_B3_REQ_LEN); skb_queue_head(&mp->outqueue, skb); break; } /* ups, drop packet */ printk(KERN_ERR "capi: put_message = %x\n", errcode); mp->outbytes -= len; kfree_skb(skb); } return count;}#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE *//* -------- function called by lower level -------------------------- */static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb){ struct capidev *cdev = ap->private;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE struct capiminor *mp; u16 datahandle;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ struct capincci *np; u32 ncci; if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { u16 info = CAPIMSG_U16(skb->data, 12); // Info field if (info == 0) { down(&cdev->ncci_list_sem); capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); up(&cdev->ncci_list_sem); } } if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) { down(&cdev->ncci_list_sem); capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); up(&cdev->ncci_list_sem); } if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); return; } ncci = CAPIMSG_CONTROL(skb->data); for (np = cdev->nccis; np && np->ncci != ncci; np = np->next) ; if (!np) { printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); return; }#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait);#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ mp = np->minorp; if (!mp) { skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); return; } if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2);#ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n", datahandle, skb->len-CAPIMSG_LEN(skb->data));#endif skb_queue_tail(&mp->inqueue, skb); mp->inbytes += skb->len; handle_minor_recv(mp); } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) { datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4);#ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n", datahandle, CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));#endif kfree_skb(skb); (void)capiminor_del_ack(mp, datahandle); if (mp->tty) tty_wakeup(mp->tty); (void)handle_minor_send(mp); } else { /* ups, let capi application handle it :-) */ skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); }#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */}/* -------- file_operations for capidev ----------------------------- */static ssize_tcapi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ struct capidev *cdev = (struct capidev *)file->private_data; struct sk_buff *skb; size_t copied; if (!cdev->ap.applid) return -ENODEV; if ((skb = skb_dequeue(&cdev->recvqueue)) == 0) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; for (;;) { interruptible_sleep_on(&cdev->recvwait); if ((skb = skb_dequeue(&cdev->recvqueue)) != 0) break; if (signal_pending(current)) break; } if (skb == 0) return -ERESTARTNOHAND; } if (skb->len > count) { skb_queue_head(&cdev->recvqueue, skb); return -EMSGSIZE; } if (copy_to_user(buf, skb->data, skb->len)) { skb_queue_head(&cdev->recvqueue, skb); return -EFAULT; } copied = skb->len; kfree_skb(skb); return copied;}static ssize_tcapi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ struct capidev *cdev = (struct capidev *)file->private_data; struct sk_buff *skb; u16 mlen; if (!cdev->ap.applid) return -ENODEV; skb = alloc_skb(count, GFP_USER); if (!skb) return -ENOMEM; if (copy_from_user(skb_put(skb, count), buf, count)) { kfree_skb(skb); return -EFAULT; } mlen = CAPIMSG_LEN(skb->data); if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { if ((size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) { kfree_skb(skb); return -EINVAL; } } else { if (mlen != count) { kfree_skb(skb); return -EINVAL; } } CAPIMSG_SETAPPID(skb->data, cdev->ap.applid); if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) { down(&cdev->ncci_list_sem); capincci_free(cdev, CAPIMSG_NCCI(skb->data)); up(&cdev->ncci_list_sem); } cdev->errcode = capi20_put_message(&cdev->ap, skb); if (cdev->errcode) { kfree_skb(skb); return -EIO; } return count;}static unsigned intcapi_poll(struct file *file, poll_table * wait){ struct capidev *cdev = (struct capidev *)file->private_data; unsigned int mask = 0; if (!cdev->ap.applid) return POLLERR; poll_wait(file, &(cdev->recvwait), wait); mask = POLLOUT | POLLWRNORM; if (!skb_queue_empty(&cdev->recvqueue)) mask |= POLLIN | POLLRDNORM; return mask;}static intcapi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct capidev *cdev = file->private_data; struct capi20_appl *ap = &cdev->ap; capi_ioctl_struct data; int retval = -EINVAL; void __user *argp = (void __user *)arg; switch (cmd) { case CAPI_REGISTER: { if (ap->applid) return -EEXIST; if (copy_from_user(&cdev->ap.rparam, argp, sizeof(struct capi_register_params))) return -EFAULT; cdev->ap.private = cdev; cdev->ap.recv_message = capi_recv_message; cdev->errcode = capi20_register(ap); if (cdev->errcode) { ap->applid = 0; return -EIO; } } return (int)ap->applid; case CAPI_GET_VERSION: { if (copy_from_user(&data.contr, argp, sizeof(data.contr))) return -EFAULT; cdev->errcode = capi20_get_version(data.contr, &data.version); if (cdev->errcode) return -EIO; if (copy_to_user(argp, &data.version, sizeof(data.version))) return -EFAULT; } return 0; case CAPI_GET_SERIAL: { if (copy_from_user(&data.contr, argp, sizeof(data.contr))) return -EFAULT; cdev->errcode = capi20_get_serial (data.contr, data.serial); if (cdev->errcode) return -EIO; if (copy_to_user(argp, data.serial, sizeof(data.serial))) return -EFAULT; } return 0; case CAPI_GET_PROFILE: { if (copy_from_user(&data.contr, argp, sizeof(data.contr))) return -EFAULT; if (data.contr == 0) { cdev->errcode = capi20_get_profile(data.contr, &data.profile); if (cdev->errcode) return -EIO; retval = copy_to_user(argp, &data.profile.ncontroller, sizeof(data.profile.ncontroller)); } else { cdev->errcode = capi20_get_profile(data.contr, &data.profile); if (cdev->errcode) return -EIO; retval = copy_to_user(argp, &data.profile, sizeof(data.profile)); } if (retval) return -EFAULT; } return 0; case CAPI_GET_MANUFACTURER: { if (copy_from_user(&data.contr, argp, sizeof(data.contr))) return -EFAULT; cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer); if (cdev->errcode) return -EIO; if (copy_to_user(argp, data.manufacturer, sizeof(data.manufacturer))) return -EFAULT; } return 0; case CAPI_GET_ERRCODE: data.errcode = cdev->errcode; cdev->errcode = CAPI_NOERROR; if (arg) { if (copy_to_user(argp, &data.errcode, sizeof(data.errcode))) return -EFAULT; } return data.errcode; case CAPI_INSTALLED: if (capi20_isinstalled() == CAPI_NOERROR) return 0; return -ENXIO; case CAPI_MANUFACTURER_CMD: { struct capi_manufacturer_cmd mcmd; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (copy_from_user(&mcmd, argp, sizeof(mcmd))) return -EFAULT; return capi20_manufacturer(mcmd.cmd, mcmd.data); } return 0; case CAPI_SET_FLAGS: case CAPI_CLR_FLAGS: { unsigned userflags; if (copy_from_user(&userflags, argp, sizeof(userflags))) return -EFAULT; if (cmd == CAPI_SET_FLAGS) cdev->userflags |= userflags; else cdev->userflags &= ~userflags; } return 0; case CAPI_GET_FLAGS: if (copy_to_user(argp, &cdev->userflags, sizeof(cdev->userflags))) return -EFAULT; return 0; case CAPI_NCCI_OPENCOUNT: { struct capincci *nccip;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE struct capiminor *mp;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ unsigned ncci; int count = 0; if (copy_from_user(&ncci, argp, sizeof(ncci))) return -EFAULT; down(&cdev->ncci_list_sem); if ((nccip = capincci_find(cdev, (u32) ncci)) == 0) { up(&cdev->ncci_list_sem); return 0; }#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE if ((mp = nccip->minorp) != 0) { count += atomic_read(&mp->ttyopencount); }#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ up(&cdev->ncci_list_sem); return count; } return 0;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE case CAPI_NCCI_GETUNIT: { struct capincci *nccip; struct capiminor *mp; unsigned ncci; int unit = 0; if (copy_from_user(&ncci, argp, sizeof(ncci))) return -EFAULT; down(&cdev->ncci_list_sem); nccip = capincci_find(cdev, (u32) ncci); if (!nccip || (mp = nccip->minorp) == 0) { up(&cdev->ncci_list_sem); return -ESRCH; } unit = mp->minor; up(&cdev->ncci_list_sem); return unit; } return 0;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ } return -EINVAL;}static intcapi_open(struct inode *inode, struct file *file){ if (file->private_data) return -EEXIST; if ((file->private_data = capidev_alloc()) == 0) return -ENOMEM; return nonseekable_open(inode, file);}static intcapi_release(struct inode *inode, struct file *file){ struct capidev *cdev = (struct capidev *)file->private_data; capidev_free(cdev); file->private_data = NULL; return 0;}static struct file_operations capi_fops ={ .owner = THIS_MODULE, .llseek = no_llseek, .read = capi_read, .write = capi_write, .poll = capi_poll, .ioctl = capi_ioctl, .open = capi_open, .release = capi_release,};#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE/* -------- tty_operations for capincci ----------------------------- */static int capinc_tty_open(struct tty_struct * tty, struct file * file){ struct capiminor *mp; if ((mp = capiminor_find(iminor(file->f_dentry->d_inode))) == 0) return -ENXIO; if (mp->nccip == 0) return -ENXIO; tty->driver_data = (void *)mp; if (atomic_read(&mp->ttyopencount) == 0) mp->tty = tty; atomic_inc(&mp->ttyopencount);#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));#endif handle_minor_recv(mp); return 0;}static void capinc_tty_close(struct tty_struct * tty, struct file * file){ struct capiminor *mp; mp = (struct capiminor *)tty->driver_data; if (mp) { if (atomic_dec_and_test(&mp->ttyopencount)) {#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "capinc_tty_close lastclose\n");#endif tty->driver_data = NULL; mp->tty = NULL; }#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount));#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -