收藏 分销(赏)

PPP驱动程序的基本原理.doc

上传人:仙人****88 文档编号:9411546 上传时间:2025-03-25 格式:DOC 页数:17 大小:47KB
下载 相关 举报
PPP驱动程序的基本原理.doc_第1页
第1页 / 共17页
PPP驱动程序的基本原理.doc_第2页
第2页 / 共17页
点击查看更多>>
资源描述
PPP驱动程序的基本原理 ===================== 1) ppp设备是指在点对点的物理链路之间使用PPP帧进行分组交换的内核网络接口设备, 由于Linux内核将串行设备作为终端设备来驱动, 于是引入PPP终端规程来实现终端设备与PPP设备的接口. 根据终端设备的物理传输特性的不同, PPP规程分为异步规程(N_PPP)和同步规程(N_SYNC_PPP)两种, 对于普通串口设备使用异步PPP规程. 2) 在PPP驱动程序中, 每一tty终端设备对应于一条PPP传输通道(chanell), 每一ppp网络设备对应于一个PPP接口单元(unit). 从终端设备上接收到的数据流通过PPP传输通道解码后转换成PPP帧传递到PPP网络接口单元, PPP接口单元再将PPP帧转换为PPP设备的接收帧. 反之, 当PPP设备发射数据帧时, 发射帧通过PPP接口单元转换成PPP帧传递给PPP通道, PPP通道负责将PPP帧编码后写入终端设备. 在配置了多链路PPP时(CONFIG_PPP_MULTILINK), 多个PPP传输通道可连接到同一PPP接口单元. PPP接口单元将PPP帧分割成若干个片段传递给不同的PPP传输通道, 反之, PPP传输通道接收到的PPP帧片段被PPP接口单元重组成完整的PPP帧. 3) 在Linux-2.4中, 应用程序可通过字符设备/dev/ppp监控内核PPP驱动程序. 用户可以用ioctl(PPPIOCATTACH)将文件绑定到PPP接口单元上, 来读写PPP接口单元的输出帧, 也可以用ioctl(PPPIOCATTCHAN)将文件绑定到PPP传输通道上, 来读写PPP传输通道的输入帧. 4) PPP传输通道用channel结构描述, 系统中所有打开的传输通道在all_channels链表中. PPP接口单元用ppp结构描述, 系统中所有建立的接口单元在all_ppp_units链表中. 当终端设备的物理链路连接成功后, 用户使用ioctl(TIOCSETD)将终端切换到PPP规程. PPP规程初始化时, 将建立终端设备的传输通道和通道驱动结构. 对于异步PPP规程来说, 通道驱动结构为asyncppp, 它包含通道操作表async_ops. 传输通道和接口单元各自包含自已的设备文件(/dev/ppp)参数结构(ppp_file). ; drivers/char/tty_io.c: int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) { if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; if (new_ldisc) { ldiscs[disc] = *new_ldisc; ldiscs[disc].flags |= LDISC_FLAG_DEFINED; ldiscs[disc].num = disc; } else memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc)); return 0; } int tty_ioctl(struct inode * inode, struct file * file,       unsigned int cmd, unsigned long arg) { struct tty_struct *tty, *real_tty; int retval; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) return -EINVAL; real_tty = tty; if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&     tty->driver.subtype == PTY_TYPE_MASTER) real_tty = tty->link; ... switch (cmd) { ... case TIOCGETD: return put_user(tty->ldisc.num, (int *) arg); case TIOCSETD: return tiocsetd(tty, (int *) arg); ... } if (tty->driver.ioctl) { int retval = (tty->driver.ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } if (tty->ldisc.ioctl) { int retval = (tty->ldisc.ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } return -EINVAL; } static int tiocsetd(struct tty_struct *tty, int *arg) { int retval, ldisc; retval = get_user(ldisc, arg); if (retval) return retval; return tty_set_ldisc(tty, ldisc); } /* Set the discipline of a tty line. */ static int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval = 0; struct tty_ldisc o_ldisc; char buf[64]; if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) return -EINVAL; /* Eduardo Blanco */ /* Cyrus Durgin */ if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) { 如果设定的规程不存在 char modname [20]; sprintf(modname, "tty-ldisc-%d", ldisc); request_module (modname); 尝试加载模块 } if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) return -EINVAL; if (tty->ldisc.num == ldisc) return 0; /* We are already in the desired discipline */ o_ldisc = tty->ldisc; tty_wait_until_sent(tty, 0); 等待终端输出设备的数据发送完 /* Shutdown the current discipline. */ if (tty->ldisc.close) (tty->ldisc.close)(tty); 关闭原来的规程 /* Now set up the new line discipline. */ tty->ldisc = ldiscs[ldisc]; tty->termios->c_line = ldisc; if (tty->ldisc.open) retval = (tty->ldisc.open)(tty); 打开新规程 if (retval < 0) { 如果打开失败, 恢复原来的规程 tty->ldisc = o_ldisc; tty->termios->c_line = tty->ldisc.num; if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) { tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (tty->ldisc.open) { int r = tty->ldisc.open(tty); if (r < 0) panic("Couldn't open N_TTY ldisc for "       "%s --- error %d.",       tty_name(tty, buf), r); } } } if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc) tty->driver.set_ldisc(tty); return retval; } ; drivers/char/tty_ioctl.c: /* * Internal flag options for termios setting behavior */ #define TERMIOS_FLUSH 1 #define TERMIOS_WAIT 2 #define TERMIOS_TERMIO 4 void tty_wait_until_sent(struct tty_struct * tty, long timeout) { DECLARE_WAITQUEUE(wait, current); #ifdef TTY_DEBUG_WAIT_UNTIL_SENT char buf[64]; printk("%s wait until sent...\n", tty_name(tty, buf)); #endif if (!tty->driver.chars_in_buffer) return; add_wait_queue(&tty->write_wait, &wait); if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; do { #ifdef TTY_DEBUG_WAIT_UNTIL_SENT printk("waiting %s...(%d)\n", tty_name(tty, buf),        tty->driver.chars_in_buffer(tty)); #endif set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) goto stop_waiting; if (!tty->driver.chars_in_buffer(tty)) break; timeout = schedule_timeout(timeout); } while (timeout); if (tty->driver.wait_until_sent) tty->driver.wait_until_sent(tty, timeout); stop_waiting: current->state = TASK_RUNNING; remove_wait_queue(&tty->write_wait, &wait); } ; drivers/net/ppp_async.c: /* * The basic PPP frame. */ #define PPP_HDRLEN 4 /* octets for standard ppp header */ #define PPP_FCSLEN 2 /* octets for FCS */ #define PPP_MRU 1500 /* default MRU = max length of info field */ #define OBUFSIZE 256 /* Structure for storing local state. */ struct asyncppp { 异步PPP通道的驱动结构 struct tty_struct *tty; unsigned int flags; unsigned int state; unsigned int rbits; int mru; spinlock_t xmit_lock; spinlock_t recv_lock; unsigned long xmit_flags; u32 xaccm[8]; 终端字符转换位图 u32 raccm; unsigned int bytes_sent; unsigned int bytes_rcvd; struct sk_buff *tpkt; int tpkt_pos; u16 tfcs; unsigned char *optr; unsigned char *olim; unsigned long last_xmit; struct sk_buff *rpkt; int lcp_fcs; struct ppp_channel chan; /* interface to generic ppp layer */ unsigned char obuf[OBUFSIZE]; }; static struct tty_ldisc ppp_ldisc = { 异步PPP规程操作表 magic: TTY_LDISC_MAGIC, name: "ppp", open: ppp_asynctty_open, close: ppp_asynctty_close, read: ppp_asynctty_read, write: ppp_asynctty_write, ioctl: ppp_asynctty_ioctl, poll: ppp_asynctty_poll, receive_room: ppp_asynctty_room, receive_buf: ppp_asynctty_receive, write_wakeup: ppp_asynctty_wakeup, }; struct ppp_channel_ops async_ops = { PPP通道驱动操作表 ppp_async_send, 发送PPP帧到终端设备 ppp_async_ioctl }; int ppp_async_init(void) 模块初始化 { int err; err = tty_register_ldisc(N_PPP, &ppp_ldisc); 注册N_PPP规程 if (err != 0) printk(KERN_ERR "PPP_async: error %d registering line disc.\n",        err); return err; } /* * Called when a tty is put into PPP line discipline. */ static int ppp_asynctty_open(struct tty_struct *tty) 打开异步PPP规程 { struct asyncppp *ap; int err; MOD_INC_USE_COUNT; err = -ENOMEM; ap = kmalloc(sizeof(*ap), GFP_KERNEL); if (ap == 0) goto out; /* initialize the asyncppp structure */ memset(ap, 0, sizeof(*ap)); ap->tty = tty; 在驱动结构上设置打开终端指针 ap->mru = PPP_MRU; spin_lock_init(&ap->xmit_lock); spin_lock_init(&ap->recv_lock); ap->xaccm[0] = ~0U; ap->xaccm[3] = 0x60000000U; ap->raccm = ~0U; ap->optr = ap->obuf; ap->olim = ap->obuf; ap->lcp_fcs = -1; ap->chan.private = ap; 在一般的PPP驱动结构上设置异步驱动结构指针 ap->chan.ops = &async_ops; 异步通道操作表 ap->chan.mtu = PPP_MRU; err = ppp_register_channel(&ap->chan); 建立通道驱动程序的传输通道结构 if (err) goto out_free; tty->disc_data = ap; 在打开终端结构上设置驱动结构指针 return 0; out_free: kfree(ap); out: MOD_DEC_USE_COUNT; return err; } /* * Called when the tty is put into another line discipline * or it hangs up. * We assume that while we are in this routine, the tty layer * won't call any of the other line discipline entries for the * same tty. */ static void ppp_asynctty_close(struct tty_struct *tty) { struct asyncppp *ap = tty->disc_data; if (ap == 0) return; tty->disc_data = 0; ppp_unregister_channel(&ap->chan); if (ap->rpkt != 0) kfree_skb(ap->rpkt); if (ap->tpkt != 0) kfree_skb(ap->tpkt); kfree(ap); MOD_DEC_USE_COUNT; } /* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead. */ static ssize_t ppp_asynctty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t count) { return -EAGAIN; } /* * Write on the tty does nothing, the packets all come in * from the ppp generic stuff. */ static ssize_t ppp_asynctty_write(struct tty_struct *tty, struct file *file,    const unsigned char *buf, size_t count) { return -EAGAIN; } static int ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,    unsigned int cmd, unsigned long arg) { struct asyncppp *ap = tty->disc_data; int err, val; err = -EFAULT; switch (cmd) { case PPPIOCGCHAN: 取通道号 err = -ENXIO; if (ap == 0) break; err = -EFAULT; if (put_user(ppp_channel_index(&ap->chan), (int *) arg)) break; err = 0; break; case PPPIOCGUNIT: 取接口单元号 err = -ENXIO; if (ap == 0) break; err = -EFAULT; if (put_user(ppp_unit_number(&ap->chan), (int *) arg)) break; err = 0; break; case TCGETS: case TCGETA: err = n_tty_ioctl(tty, file, cmd, arg); break; case TCFLSH: /* flush our buffers and the serial port's buffer */ if (arg == TCIOFLUSH || arg == TCOFLUSH) ppp_async_flush_output(ap); err = n_tty_ioctl(tty, file, cmd, arg); break; case FIONREAD: val = 0; if (put_user(val, (int *) arg)) break; err = 0; break; case PPPIOCATTACH: 将传输通道连接到接口单元 case PPPIOCDETACH: 将传输通道与接口单元脱离 err = ppp_channel_ioctl(&ap->chan, cmd, arg); break; default: err = -ENOIOCTLCMD; } return err; } /* * Flush output from our internal buffers. * Called for the TCFLSH ioctl. */ static void ppp_async_flush_output(struct asyncppp *ap) { int done = 0; spin_lock_bh(&ap->xmit_lock); ap->optr = ap->olim; if (ap->tpkt != NULL) { kfree_skb(ap->tpkt); ap->tpkt = 0; clear_bit(XMIT_FULL, &ap->xmit_flags); done = 1; } spin_unlock_bh(&ap->xmit_lock); if (done) ppp_output_wakeup(&ap->chan); } /* No kernel lock - fine */ static unsigned int ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { return 0; } static int ppp_asynctty_room(struct tty_struct *tty) { return 65535; } ; drivers/net/ppp_generic.c: /* * Private data structure for each channel. * This includes the data structure used for multilink. */ struct channel { 传输通道结构 struct ppp_file file; /* stuff for read/write/poll */ struct ppp_channel *chan; /* public channel data structure */ spinlock_t downl; /* protects `chan', file.xq dequeue */ struct ppp *ppp; /* ppp unit we're connected to */ struct list_head clist; /* link in list of channels per unit */ rwlock_t upl; /* protects `ppp' and `ulist' */ #ifdef CONFIG_PPP_MULTILINK u8 avail; /* flag used in multilink stuff */ u8 had_frag; /* >= 1 fragments have been sent */ u32 lastseq; /* MP: last sequence # received */ #endif /* CONFIG_PPP_MULTILINK */ }; struct ppp_channel { 传输通道的通用驱动结构 void *private; /* channel private data */ struct ppp_channel_ops *ops; /* operations for this channel */ int mtu; /* max transmit packet size */ int hdrlen; /* amount of headroom channel needs */ void *ppp; /* opaque to channel */ /* the following are not used at present */ int speed; /* transfer rate (bytes/second) */ int latency; /* overhead time in milliseconds */ }; struct ppp_channel_ops { 驱动操作表 /* Send a packet (or multilink fragment) on this channel.    Returns 1 if it was accepted, 0 if not. */ int (*start_xmit)(struct ppp_channel *, struct sk_buff *); /* Handle an ioctl call that has come in via /dev/ppp. */ int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long); }; /* * Data structure describing one ppp unit. * A ppp unit corresponds to a ppp network interface device * and represents a multilink bundle. * It can have 0 or more ppp channels connected to it. */ struct ppp { struct ppp_file file; /* stuff for read/write/poll */ struct list_head channels; /* list of attached channels */ int n_channels; /* how many channels are attached */ spinlock_t rlock; /* lock for receive side */ spinlock_t wlock; /* lock for transmit side */ int mru; /* max receive unit */ unsigned int flags; /* control bits */ unsigned int xstate; /* transmit state bits */ unsigned int rstate; /* receive state bits */ int debug; /* debug flags */ struct slcompress *vj; /* state for VJ header compression */ enum NPmode npmode[NUM_NP]; /* what to do with each net proto */ struct sk_buff *xmit_pending; /* a packet ready to go out */ struct compressor *xcomp; /* transmit packet compressor */ void *xc_state; /* its internal state */ struct compressor *rcomp; /* receive decompressor */ void *rc_state; /* its internal state */ unsigned long last_xmit; /* jiffies when last pkt sent */ unsigned long last_recv; /* jiffies when last pkt rcvd */ struct net_device *dev; /* network interface device */ #ifdef CONFIG_PPP_MULTILINK int nxchan; /* next channel to send something on */ u32 nxseq; /* next sequence number to send */ int mrru; /* MP: max reconst. receive unit */ u32 nextseq; /* MP: seq no of next packet */ u32 minseq; /* MP: min of most recent seqnos */ struct sk_buff_head mrq; /* MP: receive reconstruction queue */ #endif /* CONFIG_PPP_MULTILINK */ struct net_device_stats stats; /* statistics */ }; /* * An instance of /dev/ppp can be associated with either a ppp * interface unit or a ppp channel. In both cases, file->private_data * points to one of these. */ struct ppp_file { 监控文件结构参数 enum { INTERFACE=1, CHANNEL } kind; struct sk_buff_head xq; /* pppd transmit queue */ struct sk_buff_head rq; /* receive queue for pppd */ wait_queue_head_t rwait; /* for poll on reading /dev/ppp */ atomic_t refcnt; /* # refs (incl /dev/ppp attached) */ int hdrlen; /* space to leave for headers */ struct list_head list; /* link in all_* list */ int index; /* interface unit / channel number */ }; /* * all_ppp_lock protects the all_ppp_units. * It also ensures that finding a ppp unit in the all_ppp_units list * and updating its file.refcnt field is atomic. */ static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED; static LIST_HEAD(all_ppp_units); /* * all_channels_lock protects all_channels and last_channel_index, * and the atomicity of find a channel and updating its file.refcnt * field. */ static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED; static LIST_HEAD(all_channels); static int last_channel_index; /* * Create a new, unattached ppp channel. */ int ppp_register_channel(struct ppp_channel *chan) { struct channel *pch; pch = kmalloc(sizeof(struct channel), GFP_ATOMIC); if (pch == 0) return -ENOMEM; memset(pch, 0, sizeof(struct channel)); pch->ppp = NULL; pch->chan = chan; chan->ppp = pch; init_ppp_file(&pch->file, CHANNEL); pch->file.hdrlen = chan->hdrlen; #ifdef CONFIG_PPP_MULTILINK pch->lastseq = -1; #endif /* CONFIG_PPP_MULTILINK */ spin_lock_init(&pch->downl); pch->upl = RW_LOCK_UNLOCKED; spin_lock_bh(&all_channels_lock); pch->file.index = ++last_channel_index; list_add(&pch->file.list, &all_channels); spin_unlock_bh(&all_channels_lock); MOD_INC_USE_COUNT; return
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 教育专区 > 小学其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服