📄 irnet_ppp.c
字号:
else mask |= irnet_ctrl_poll(ap, file, wait); DEXIT(FS_TRACE, " - mask=0x%X\n", mask); return(mask);}/*------------------------------------------------------------------*//* * IOCtl : Called when someone does some ioctls on /dev/irnet * This is the way pppd configure us and control us while the PPP * instance is active. */static intdev_irnet_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ irnet_socket * ap = (struct irnet_socket *) file->private_data; int err; int val; DENTER(FS_TRACE, "(file=0x%X, ap=0x%X, cmd=0x%X)\n", (unsigned int) file, (unsigned int) ap, cmd); /* Basic checks... */ DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");#ifdef SECURE_DEVIRNET if(!capable(CAP_NET_ADMIN)) return -EPERM;#endif SECURE_DEVIRNET err = -EFAULT; switch(cmd) { /* Set discipline (should be N_SYNC_PPP or N_TTY) */ case TIOCSETD: if(get_user(val, (int *) arg)) break; if((val == N_SYNC_PPP) || (val == N_PPP)) { DEBUG(FS_INFO, "Entering PPP discipline.\n"); /* PPP channel setup */ ap->chan.private = ap; ap->chan.ops = &irnet_ppp_ops; ap->chan.mtu = PPP_MRU; err = ppp_register_channel(&ap->chan); if(err == 0) { /* Our ppp side is active */ ap->ppp_open = 1; DEBUG(FS_INFO, "Trying to establish a connection.\n"); /* Setup the IrDA link now - may fail... */ irda_irnet_connect(ap); } else DERROR(FS_ERROR, "Can't setup PPP channel...\n"); } else { /* In theory, should be N_TTY */ DEBUG(FS_INFO, "Exiting PPP discipline.\n"); /* Disconnect from the generic PPP layer */ if(ap->ppp_open) ppp_unregister_channel(&ap->chan); else DERROR(FS_ERROR, "Channel not registered !\n"); ap->ppp_open = 0; err = 0; } break; /* Attach this PPP instance to the PPP driver (set it active) */ case PPPIOCATTACH: case PPPIOCDETACH: if(ap->ppp_open) err = ppp_channel_ioctl(&ap->chan, cmd, arg); else DERROR(FS_ERROR, "Channel not registered !\n"); break; /* Query PPP channel and unit number */ case PPPIOCGCHAN: if(!ap->ppp_open) break; if(put_user(ppp_channel_index(&ap->chan), (int *) arg)) break; DEBUG(FS_INFO, "Query channel.\n"); err = 0; break; case PPPIOCGUNIT: if(!ap->ppp_open) break; if(put_user(ppp_unit_number(&ap->chan), (int *) arg)) break; DEBUG(FS_INFO, "Query unit number.\n"); err = 0; break; /* All these ioctls can be passed both directly and from ppp_generic, * so we just deal with them in one place... */ case PPPIOCGFLAGS: case PPPIOCSFLAGS: case PPPIOCGASYNCMAP: case PPPIOCSASYNCMAP: case PPPIOCGRASYNCMAP: case PPPIOCSRASYNCMAP: case PPPIOCGXASYNCMAP: case PPPIOCSXASYNCMAP: case PPPIOCGMRU: case PPPIOCSMRU: DEBUG(FS_INFO, "Standard PPP ioctl.\n"); if(!capable(CAP_NET_ADMIN)) err = -EPERM; else err = ppp_irnet_ioctl(&ap->chan, cmd, arg); break; /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */ /* Get termios */ case TCGETS: DEBUG(FS_INFO, "Get termios.\n"); if(kernel_termios_to_user_termios((struct termios *)arg, &ap->termios)) break; err = 0; break; /* Set termios */ case TCSETSF: DEBUG(FS_INFO, "Set termios.\n"); if(user_termios_to_kernel_termios(&ap->termios, (struct termios *) arg)) break; err = 0; break; /* Set DTR/RTS */ case TIOCMBIS: case TIOCMBIC: /* Set exclusive/non-exclusive mode */ case TIOCEXCL: case TIOCNXCL: DEBUG(FS_INFO, "TTY compatibility.\n"); err = 0; break; case TCGETA: DEBUG(FS_INFO, "TCGETA\n"); break; case TCFLSH: DEBUG(FS_INFO, "TCFLSH\n"); /* Note : this will flush buffers in PPP, so it *must* be done * We should also worry that we don't accept junk here and that * we get rid of our own buffers */#ifdef FLUSH_TO_PPP ppp_output_wakeup(&ap->chan);#endif FLUSH_TO_PPP err = 0; break; case FIONREAD: DEBUG(FS_INFO, "FIONREAD\n"); val = 0; if(put_user(val, (int *) arg)) break; err = 0; break; default: DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd); err = -ENOIOCTLCMD; } DEXIT(FS_TRACE, " - err = 0x%X\n", err); return err;}/************************** PPP CALLBACKS **************************//* * This are the functions that the generic PPP driver in the kernel * will call to communicate to us. *//*------------------------------------------------------------------*//* * Prepare the ppp frame for transmission over the IrDA socket. * We make sure that the header space is enough, and we change ppp header * according to flags passed by pppd. * This is not a callback, but just a helper function used in ppp_irnet_send() */static inline struct sk_buff *irnet_prepare_skb(irnet_socket * ap, struct sk_buff * skb){ unsigned char * data; int proto; /* PPP protocol */ int islcp; /* Protocol == LCP */ int needaddr; /* Need PPP address */ DENTER(PPP_TRACE, "(ap=0x%X, skb=0x%X)\n", (unsigned int) ap, (unsigned int) skb); /* Extract PPP protocol from the frame */ data = skb->data; proto = (data[0] << 8) + data[1]; /* LCP packets with codes between 1 (configure-request) * and 7 (code-reject) must be sent as though no options * have been negotiated. */ islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7); /* compress protocol field if option enabled */ if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp)) skb_pull(skb,1); /* Check if we need address/control fields */ needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp); /* Is the skb headroom large enough to contain all IrDA-headers? */ if((skb_headroom(skb) < (ap->max_header_size + needaddr)) || (skb_shared(skb))) { struct sk_buff * new_skb; DEBUG(PPP_INFO, "Reallocating skb\n"); /* Create a new skb */ new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr); /* We have to free the original skb anyway */ dev_kfree_skb(skb); /* Did the realloc succeed ? */ DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n"); /* Use the new skb instead */ skb = new_skb; } /* prepend address/control fields if necessary */ if(needaddr) { skb_push(skb,2); skb->data[0] = PPP_ALLSTATIONS; skb->data[1] = PPP_UI; } DEXIT(PPP_TRACE, "\n"); return skb;}/*------------------------------------------------------------------*//* * Send a packet to the peer over the IrTTP connection. * Returns 1 iff the packet was accepted. * Returns 0 iff packet was not consumed. * If the packet was not accepted, we will call ppp_output_wakeup * at some later time to reactivate flow control in ppp_generic. */static intppp_irnet_send(struct ppp_channel * chan, struct sk_buff * skb){ irnet_socket * self = (struct irnet_socket *) chan->private; int ret; DENTER(PPP_TRACE, "(channel=0x%X, ap/self=0x%X)\n", (unsigned int) chan, (unsigned int) self); /* Check if things are somewhat valid... */ DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n"); /* Check if we are connected */ if(self->ttp_open == 0) {#ifdef CONNECT_IN_SEND /* Let's try to connect one more time... */ /* Note : we won't connect fully yet, but we should be ready for * next packet... */ /* Note : we can't do that, we need to have a process context to * go through interruptible_sleep_on() in irnet_find_lsap_sel() * We need to find another way... */ irda_irnet_connect(self);#endif CONNECT_IN_SEND DEBUG(PPP_INFO, "IrTTP not ready ! (%d-0x%X)\n", self->ttp_open, (unsigned int) self->tsap); /* Note : we can either drop the packet or block the packet. * * Blocking the packet allow us a better connection time, * because by calling ppp_output_wakeup() we can have * ppp_generic resending the LCP request immediately to us, * rather than waiting for one of pppd periodic transmission of * LCP request. * * On the other hand, if we block all packet, all those periodic * transmissions of pppd accumulate in ppp_generic, creating a * backlog of LCP request. When we eventually connect later on, * we have to transmit all this backlog before we can connect * proper (if we don't timeout before). * * The current strategy is as follow : * While we are attempting to connect, we block packets to get * a better connection time. * If we fail to connect, we drain the queue and start dropping packets */#ifdef BLOCK_WHEN_CONNECT /* If we are attempting to connect */ if(self->tsap) { /* Blocking packet, ppp_generic will retry later */ return 0; }#endif BLOCK_WHEN_CONNECT /* Dropping packet, pppd will retry later */ dev_kfree_skb(skb); return 1; } /* Check if the queue can accept any packet, otherwise block */ if(self->tx_flow != FLOW_START) DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n", skb_queue_len(&self->tsap->tx_queue)); /* Prepare ppp frame for transmission */ skb = irnet_prepare_skb(self, skb); DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n"); /* Send the packet to IrTTP */ ret = irttp_data_request(self->tsap, skb); if(ret < 0) { /* * > IrTTPs tx queue is full, so we just have to * > drop the frame! You might think that we should * > just return -1 and don't deallocate the frame, * > but that is dangerous since it's possible that * > we have replaced the original skb with a new * > one with larger headroom, and that would really * > confuse do_dev_queue_xmit() in dev.c! I have * > tried :-) DB * Correction : we verify the flow control above (self->tx_flow), * so we come here only if IrTTP doesn't like the packet (empty, * too large, IrTTP not connected). In those rare cases, it's ok * to drop it, we don't want to see it here again... * Jean II */ DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret); dev_kfree_skb(skb); } DEXIT(PPP_TRACE, "\n"); return 1; /* Packet has been consumed */}/*------------------------------------------------------------------*//* * Take care of the ioctls that ppp_generic doesn't want to deal with... * Note : we are also called from dev_irnet_ioctl(). */static intppp_irnet_ioctl(struct ppp_channel * chan, unsigned int cmd, unsigned long arg){ irnet_socket * ap = (struct irnet_socket *) chan->private; int err; int val; u32 accm[8]; DENTER(PPP_TRACE, "(channel=0x%X, ap=0x%X, cmd=0x%X)\n", (unsigned int) chan, (unsigned int) ap, cmd); /* Basic checks... */ DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); err = -EFAULT; switch(cmd) { /* PPP flags */ case PPPIOCGFLAGS: val = ap->flags | ap->rbits; if(put_user(val, (int *) arg)) break; err = 0; break; case PPPIOCSFLAGS: if(get_user(val, (int *) arg)) break; ap->flags = val & ~SC_RCV_BITS; ap->rbits = val & SC_RCV_BITS; err = 0; break; /* Async map stuff - all dummy to please pppd */ case PPPIOCGASYNCMAP: if(put_user(ap->xaccm[0], (u32 *) arg)) break; err = 0; break; case PPPIOCSASYNCMAP: if(get_user(ap->xaccm[0], (u32 *) arg)) break; err = 0; break; case PPPIOCGRASYNCMAP: if(put_user(ap->raccm, (u32 *) arg)) break; err = 0; break; case PPPIOCSRASYNCMAP: if(get_user(ap->raccm, (u32 *) arg)) break; err = 0; break; case PPPIOCGXASYNCMAP: if(copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm))) break; err = 0; break; case PPPIOCSXASYNCMAP: if(copy_from_user(accm, (void *) arg, sizeof(accm))) break; accm[2] &= ~0x40000000U; /* can't escape 0x5e */ accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); err = 0; break; /* Max PPP frame size */ case PPPIOCGMRU: if(put_user(ap->mru, (int *) arg)) break; err = 0; break; case PPPIOCSMRU: if(get_user(val, (int *) arg)) break; if(val < PPP_MRU) val = PPP_MRU; ap->mru = val; err = 0; break; default: DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd); err = -ENOIOCTLCMD; } DEXIT(PPP_TRACE, " - err = 0x%X\n", err); return err;}/************************** INITIALISATION **************************//* * Module initialisation and all that jazz... *//*------------------------------------------------------------------*//* * Hook our device callbacks in the filesystem, to connect our code * to /dev/irnet */intppp_irnet_init(void){ int err = 0; DENTER(MODULE_TRACE, "()\n"); /* Allocate ourselves as a minor in the misc range */ err = misc_register(&irnet_misc_device); DEXIT(MODULE_TRACE, "\n"); return err;}/*------------------------------------------------------------------*//* * Cleanup at exit... */voidppp_irnet_cleanup(void){ DENTER(MODULE_TRACE, "()\n"); /* De-allocate /dev/irnet minor in misc range */ misc_deregister(&irnet_misc_device); DEXIT(MODULE_TRACE, "\n");}#ifdef MODULE/*------------------------------------------------------------------*//* * Module main entry point */intinit_module(void){ int err; /* Initialise both parts... */ err = irda_irnet_init(); if(!err) err = ppp_irnet_init(); return err;}/*------------------------------------------------------------------*//* * Module exit */voidcleanup_module(void){ irda_irnet_cleanup(); return ppp_irnet_cleanup();}#endif /* MODULE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -