~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/drivers/net/ppp_synctty.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * PPP synchronous tty channel driver for Linux.
  3  *
  4  * This is a ppp channel driver that can be used with tty device drivers
  5  * that are frame oriented, such as synchronous HDLC devices.
  6  *
  7  * Complete PPP frames without encoding/decoding are exchanged between
  8  * the channel driver and the device driver.
  9  * 
 10  * The async map IOCTL codes are implemented to keep the user mode
 11  * applications happy if they call them. Synchronous PPP does not use
 12  * the async maps.
 13  *
 14  * Copyright 1999 Paul Mackerras.
 15  *
 16  * Also touched by the grubby hands of Paul Fulghum paulkf@microgate.com
 17  *
 18  *  This program is free software; you can redistribute it and/or
 19  *  modify it under the terms of the GNU General Public License
 20  *  as published by the Free Software Foundation; either version
 21  *  2 of the License, or (at your option) any later version.
 22  *
 23  * This driver provides the encapsulation and framing for sending
 24  * and receiving PPP frames over sync serial lines.  It relies on
 25  * the generic PPP layer to give it frames to send and to process
 26  * received frames.  It implements the PPP line discipline.
 27  *
 28  * Part of the code in this driver was inspired by the old sync-only
 29  * PPP driver, written by Michael Callahan and Al Longyear, and
 30  * subsequently hacked by Paul Mackerras.
 31  *
 32  * ==FILEVERSION 20000322==
 33  */
 34 
 35 #include <linux/module.h>
 36 #include <linux/kernel.h>
 37 #include <linux/skbuff.h>
 38 #include <linux/tty.h>
 39 #include <linux/netdevice.h>
 40 #include <linux/poll.h>
 41 #include <linux/ppp_defs.h>
 42 #include <linux/if_ppp.h>
 43 #include <linux/ppp_channel.h>
 44 #include <linux/init.h>
 45 #include <asm/uaccess.h>
 46 
 47 #ifndef spin_trylock_bh
 48 #define spin_trylock_bh(lock)   ({ int __r; local_bh_disable(); \
 49                                    __r = spin_trylock(lock);    \
 50                                    if (!__r) local_bh_enable(); \
 51                                    __r; })
 52 #endif
 53 
 54 #define PPP_VERSION     "2.4.1"
 55 
 56 /* Structure for storing local state. */
 57 struct syncppp {
 58         struct tty_struct *tty;
 59         unsigned int    flags;
 60         unsigned int    rbits;
 61         int             mru;
 62         spinlock_t      xmit_lock;
 63         spinlock_t      recv_lock;
 64         unsigned long   xmit_flags;
 65         u32             xaccm[8];
 66         u32             raccm;
 67         unsigned int    bytes_sent;
 68         unsigned int    bytes_rcvd;
 69 
 70         struct sk_buff  *tpkt;
 71         unsigned long   last_xmit;
 72 
 73         struct sk_buff  *rpkt;
 74 
 75         struct ppp_channel chan;        /* interface to generic ppp layer */
 76 };
 77 
 78 /* Bit numbers in xmit_flags */
 79 #define XMIT_WAKEUP     0
 80 #define XMIT_FULL       1
 81 
 82 /* Bits in rbits */
 83 #define SC_RCV_BITS     (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
 84 
 85 #define PPPSYNC_MAX_RQLEN       32      /* arbitrary */
 86 
 87 /*
 88  * Prototypes.
 89  */
 90 static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *);
 91 static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
 92 static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
 93                           unsigned long arg);
 94 static int ppp_sync_push(struct syncppp *ap);
 95 static void ppp_sync_flush_output(struct syncppp *ap);
 96 static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
 97                            char *flags, int count);
 98 
 99 struct ppp_channel_ops sync_ops = {
100         ppp_sync_send,
101         ppp_sync_ioctl
102 };
103 
104 /*
105  * Utility procedures to print a buffer in hex/ascii
106  */
107 static void
108 ppp_print_hex (register __u8 * out, const __u8 * in, int count)
109 {
110         register __u8 next_ch;
111         static char hex[] = "0123456789ABCDEF";
112 
113         while (count-- > 0) {
114                 next_ch = *in++;
115                 *out++ = hex[(next_ch >> 4) & 0x0F];
116                 *out++ = hex[next_ch & 0x0F];
117                 ++out;
118         }
119 }
120 
121 static void
122 ppp_print_char (register __u8 * out, const __u8 * in, int count)
123 {
124         register __u8 next_ch;
125 
126         while (count-- > 0) {
127                 next_ch = *in++;
128 
129                 if (next_ch < 0x20 || next_ch > 0x7e)
130                         *out++ = '.';
131                 else {
132                         *out++ = next_ch;
133                         if (next_ch == '%')   /* printk/syslogd has a bug !! */
134                                 *out++ = '%';
135                 }
136         }
137         *out = '\0';
138 }
139 
140 static void
141 ppp_print_buffer (const char *name, const __u8 *buf, int count)
142 {
143         __u8 line[44];
144 
145         if (name != NULL)
146                 printk(KERN_DEBUG "ppp_synctty: %s, count = %d\n", name, count);
147 
148         while (count > 8) {
149                 memset (line, 32, 44);
150                 ppp_print_hex (line, buf, 8);
151                 ppp_print_char (&line[8 * 3], buf, 8);
152                 printk(KERN_DEBUG "%s\n", line);
153                 count -= 8;
154                 buf += 8;
155         }
156 
157         if (count > 0) {
158                 memset (line, 32, 44);
159                 ppp_print_hex (line, buf, count);
160                 ppp_print_char (&line[8 * 3], buf, count);
161                 printk(KERN_DEBUG "%s\n", line);
162         }
163 }
164 
165 
166 /*
167  * Routines implementing the synchronous PPP line discipline.
168  */
169 
170 /*
171  * Called when a tty is put into line discipline.
172  */
173 static int
174 ppp_sync_open(struct tty_struct *tty)
175 {
176         struct syncppp *ap;
177         int err;
178 
179         MOD_INC_USE_COUNT;
180         ap = kmalloc(sizeof(*ap), GFP_KERNEL);
181         err = -ENOMEM;
182         if (ap == 0)
183                 goto out;
184 
185         /* initialize the syncppp structure */
186         memset(ap, 0, sizeof(*ap));
187         ap->tty = tty;
188         ap->mru = PPP_MRU;
189         spin_lock_init(&ap->xmit_lock);
190         spin_lock_init(&ap->recv_lock);
191         ap->xaccm[0] = ~0U;
192         ap->xaccm[3] = 0x60000000U;
193         ap->raccm = ~0U;
194 
195         ap->chan.private = ap;
196         ap->chan.ops = &sync_ops;
197         ap->chan.mtu = PPP_MRU;
198         ap->chan.hdrlen = 2;    /* for A/C bytes */
199         err = ppp_register_channel(&ap->chan);
200         if (err)
201                 goto out_free;
202 
203         tty->disc_data = ap;
204 
205         return 0;
206 
207  out_free:
208         kfree(ap);
209  out:
210         MOD_DEC_USE_COUNT;
211         return err;
212 }
213 
214 /*
215  * Called when the tty is put into another line discipline
216  * (or it hangs up).
217  */
218 static void
219 ppp_sync_close(struct tty_struct *tty)
220 {
221         struct syncppp *ap = tty->disc_data;
222 
223         if (ap == 0)
224                 return;
225         tty->disc_data = 0;
226         ppp_unregister_channel(&ap->chan);
227         if (ap->rpkt != 0)
228                 kfree_skb(ap->rpkt);
229         if (ap->tpkt != 0)
230                 kfree_skb(ap->tpkt);
231         kfree(ap);
232         MOD_DEC_USE_COUNT;
233 }
234 
235 /*
236  * Read a PPP frame, for compatibility until pppd is updated.
237  */
238 static ssize_t
239 ppp_sync_read(struct tty_struct *tty, struct file *file,
240                unsigned char *buf, size_t count)
241 {
242         struct syncppp *ap = tty->disc_data;
243 
244         if (ap == 0)
245                 return -ENXIO;
246         return ppp_channel_read(&ap->chan, file, buf, count);
247 }
248 
249 /*
250  * Write a ppp frame, for compatibility until pppd is updated.
251  */
252 static ssize_t
253 ppp_sync_write(struct tty_struct *tty, struct file *file,
254                 const unsigned char *buf, size_t count)
255 {
256         struct syncppp *ap = tty->disc_data;
257 
258         if (ap == 0)
259                 return -ENXIO;
260         return ppp_channel_write(&ap->chan, buf, count);
261 }
262 
263 static int
264 ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
265                   unsigned int cmd, unsigned long arg)
266 {
267         struct syncppp *ap = tty->disc_data;
268         int err, val;
269 
270         err = -EFAULT;
271         switch (cmd) {
272         case PPPIOCGCHAN:
273                 err = -ENXIO;
274                 if (ap == 0)
275                         break;
276                 err = -EFAULT;
277                 if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
278                         break;
279                 err = 0;
280                 break;
281 
282         case PPPIOCGUNIT:
283                 err = -ENXIO;
284                 if (ap == 0)
285                         break;
286                 err = -EFAULT;
287                 if (put_user(ppp_unit_number(&ap->chan), (int *) arg))
288                         break;
289                 err = 0;
290                 break;
291 
292         case TCGETS:
293         case TCGETA:
294                 err = n_tty_ioctl(tty, file, cmd, arg);
295                 break;
296 
297         case TCFLSH:
298                 /* flush our buffers and the serial port's buffer */
299                 if (arg == TCIOFLUSH || arg == TCOFLUSH)
300                         ppp_sync_flush_output(ap);
301                 err = n_tty_ioctl(tty, file, cmd, arg);
302                 break;
303 
304         case FIONREAD:
305                 val = 0;
306                 if (put_user(val, (int *) arg))
307                         break;
308                 err = 0;
309                 break;
310 
311         /*
312          * Compatibility calls until pppd is updated.
313          */
314         case PPPIOCGFLAGS:
315         case PPPIOCSFLAGS:
316         case PPPIOCGASYNCMAP:
317         case PPPIOCSASYNCMAP:
318         case PPPIOCGRASYNCMAP:
319         case PPPIOCSRASYNCMAP:
320         case PPPIOCGXASYNCMAP:
321         case PPPIOCSXASYNCMAP:
322         case PPPIOCGMRU:
323         case PPPIOCSMRU:
324                 err = -EPERM;
325                 if (!capable(CAP_NET_ADMIN))
326                         break;
327                 err = ppp_sync_ioctl(&ap->chan, cmd, arg);
328                 break;
329 
330         case PPPIOCATTACH:
331         case PPPIOCDETACH:
332                 err = ppp_channel_ioctl(&ap->chan, cmd, arg);
333                 break;
334 
335         default:
336                 err = -ENOIOCTLCMD;
337         }
338 
339         return err;
340 }
341 
342 /* No kernel lock - fine */
343 static unsigned int
344 ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
345 {
346         struct syncppp *ap = tty->disc_data;
347         unsigned int mask;
348 
349         mask = POLLOUT | POLLWRNORM;
350         /* compatibility for old pppd */
351         if (ap != 0)
352                 mask |= ppp_channel_poll(&ap->chan, file, wait);
353         if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
354                 mask |= POLLHUP;
355         return mask;
356 }
357 
358 static int
359 ppp_sync_room(struct tty_struct *tty)
360 {
361         return 65535;
362 }
363 
364 static void
365 ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
366                   char *flags, int count)
367 {
368         struct syncppp *ap = tty->disc_data;
369 
370         if (ap == 0)
371                 return;
372         spin_lock_bh(&ap->recv_lock);
373         ppp_sync_input(ap, buf, flags, count);
374         spin_unlock_bh(&ap->recv_lock);
375         if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
376             && tty->driver.unthrottle)
377                 tty->driver.unthrottle(tty);
378 }
379 
380 static void
381 ppp_sync_wakeup(struct tty_struct *tty)
382 {
383         struct syncppp *ap = tty->disc_data;
384 
385         clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
386         if (ap == 0)
387                 return;
388         if (ppp_sync_push(ap))
389                 ppp_output_wakeup(&ap->chan);
390 }
391 
392 
393 static struct tty_ldisc ppp_sync_ldisc = {
394         magic:  TTY_LDISC_MAGIC,
395         name:   "pppsync",
396         open:   ppp_sync_open,
397         close:  ppp_sync_close,
398         read:   ppp_sync_read,
399         write:  ppp_sync_write,
400         ioctl:  ppp_synctty_ioctl,
401         poll:   ppp_sync_poll,
402         receive_room: ppp_sync_room,
403         receive_buf: ppp_sync_receive,
404         write_wakeup: ppp_sync_wakeup,
405 };
406 
407 int
408 ppp_sync_init(void)
409 {
410         int err;
411 
412         err = tty_register_ldisc(N_SYNC_PPP, &ppp_sync_ldisc);
413         if (err != 0)
414                 printk(KERN_ERR "PPP_sync: error %d registering line disc.\n",
415                        err);
416         return err;
417 }
418 
419 /*
420  * The following routines provide the PPP channel interface.
421  */
422 static int
423 ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
424 {
425         struct syncppp *ap = chan->private;
426         int err, val;
427         u32 accm[8];
428 
429         err = -EFAULT;
430         switch (cmd) {
431         case PPPIOCGFLAGS:
432                 val = ap->flags | ap->rbits;
433                 if (put_user(val, (int *) arg))
434                         break;
435                 err = 0;
436                 break;
437         case PPPIOCSFLAGS:
438                 if (get_user(val, (int *) arg))
439                         break;
440                 ap->flags = val & ~SC_RCV_BITS;
441                 spin_lock_bh(&ap->recv_lock);
442                 ap->rbits = val & SC_RCV_BITS;
443                 spin_unlock_bh(&ap->recv_lock);
444                 err = 0;
445                 break;
446 
447         case PPPIOCGASYNCMAP:
448                 if (put_user(ap->xaccm[0], (u32 *) arg))
449                         break;
450                 err = 0;
451                 break;
452         case PPPIOCSASYNCMAP:
453                 if (get_user(ap->xaccm[0], (u32 *) arg))
454                         break;
455                 err = 0;
456                 break;
457 
458         case PPPIOCGRASYNCMAP:
459                 if (put_user(ap->raccm, (u32 *) arg))
460                         break;
461                 err = 0;
462                 break;
463         case PPPIOCSRASYNCMAP:
464                 if (get_user(ap->raccm, (u32 *) arg))
465                         break;
466                 err = 0;
467                 break;
468 
469         case PPPIOCGXASYNCMAP:
470                 if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
471                         break;
472                 err = 0;
473                 break;
474         case PPPIOCSXASYNCMAP:
475                 if (copy_from_user(accm, (void *) arg, sizeof(accm)))
476                         break;
477                 accm[2] &= ~0x40000000U;        /* can't escape 0x5e */
478                 accm[3] |= 0x60000000U;         /* must escape 0x7d, 0x7e */
479                 memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
480                 err = 0;
481                 break;
482 
483         case PPPIOCGMRU:
484                 if (put_user(ap->mru, (int *) arg))
485                         break;
486                 err = 0;
487                 break;
488         case PPPIOCSMRU:
489                 if (get_user(val, (int *) arg))
490                         break;
491                 if (val < PPP_MRU)
492                         val = PPP_MRU;
493                 ap->mru = val;
494                 err = 0;
495                 break;
496 
497         default:
498                 err = -ENOTTY;
499         }
500         return err;
501 }
502 
503 /*
504  * Procedures for encapsulation and framing.
505  */
506 
507 struct sk_buff*
508 ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
509 {
510         int proto;
511         unsigned char *data;
512         int islcp;
513 
514         data  = skb->data;
515         proto = (data[0] << 8) + data[1];
516 
517         /* LCP packets with codes between 1 (configure-request)
518          * and 7 (code-reject) must be sent as though no options
519          * have been negotiated.
520          */
521         islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
522 
523         /* compress protocol field if option enabled */
524         if (data[0] == 0 && (ap->flags & SC_COMP_PROT) && !islcp)
525                 skb_pull(skb,1);
526 
527         /* prepend address/control fields if necessary */
528         if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
529                 if (skb_headroom(skb) < 2) {
530                         struct sk_buff *npkt = dev_alloc_skb(skb->len + 2);
531                         if (npkt == NULL) {
532                                 kfree_skb(skb);
533                                 return NULL;
534                         }
535                         skb_reserve(npkt,2);
536                         memcpy(skb_put(npkt,skb->len), skb->data, skb->len);
537                         kfree_skb(skb);
538                         skb = npkt;
539                 }
540                 skb_push(skb,2);
541                 skb->data[0] = PPP_ALLSTATIONS;
542                 skb->data[1] = PPP_UI;
543         }
544 
545         ap->last_xmit = jiffies;
546 
547         if (skb && ap->flags & SC_LOG_OUTPKT)
548                 ppp_print_buffer ("send buffer", skb->data, skb->len);
549 
550         return skb;
551 }
552 
553 /*
554  * Transmit-side routines.
555  */
556 
557 /*
558  * Send a packet to the peer over an sync tty line.
559  * Returns 1 iff the packet was accepted.
560  * If the packet was not accepted, we will call ppp_output_wakeup
561  * at some later time.
562  */
563 static int
564 ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb)
565 {
566         struct syncppp *ap = chan->private;
567 
568         ppp_sync_push(ap);
569 
570         if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
571                 return 0;       /* already full */
572         skb = ppp_sync_txmunge(ap, skb);
573         if (skb != NULL)
574                 ap->tpkt = skb;
575         else
576                 clear_bit(XMIT_FULL, &ap->xmit_flags);
577 
578         ppp_sync_push(ap);
579         return 1;
580 }
581 
582 /*
583  * Push as much data as possible out to the tty.
584  */
585 static int
586 ppp_sync_push(struct syncppp *ap)
587 {
588         int sent, done = 0;
589         struct tty_struct *tty = ap->tty;
590         int tty_stuffed = 0;
591 
592         set_bit(XMIT_WAKEUP, &ap->xmit_flags);
593         if (!spin_trylock_bh(&ap->xmit_lock))
594                 return 0;
595         for (;;) {
596                 if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
597                         tty_stuffed = 0;
598                 if (!tty_stuffed && ap->tpkt != 0) {
599                         set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
600                         sent = tty->driver.write(tty, 0, ap->tpkt->data, ap->tpkt->len);
601                         if (sent < 0)
602                                 goto flush;     /* error, e.g. loss of CD */
603                         if (sent < ap->tpkt->len) {
604                                 tty_stuffed = 1;
605                         } else {
606                                 kfree_skb(ap->tpkt);
607                                 ap->tpkt = 0;
608                                 clear_bit(XMIT_FULL, &ap->xmit_flags);
609                                 done = 1;
610                         }
611                         continue;
612                 }
613                 /* haven't made any progress */
614                 spin_unlock_bh(&ap->xmit_lock);
615                 if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
616                       || (!tty_stuffed && ap->tpkt != 0)))
617                         break;
618                 if (!spin_trylock_bh(&ap->xmit_lock))
619                         break;
620         }
621         return done;
622 
623 flush:
624         if (ap->tpkt != 0) {
625                 kfree_skb(ap->tpkt);
626                 ap->tpkt = 0;
627                 clear_bit(XMIT_FULL, &ap->xmit_flags);
628                 done = 1;
629         }
630         spin_unlock_bh(&ap->xmit_lock);
631         return done;
632 }
633 
634 /*
635  * Flush output from our internal buffers.
636  * Called for the TCFLSH ioctl.
637  */
638 static void
639 ppp_sync_flush_output(struct syncppp *ap)
640 {
641         int done = 0;
642 
643         spin_lock_bh(&ap->xmit_lock);
644         if (ap->tpkt != NULL) {
645                 kfree_skb(ap->tpkt);
646                 ap->tpkt = 0;
647                 clear_bit(XMIT_FULL, &ap->xmit_flags);
648                 done = 1;
649         }
650         spin_unlock_bh(&ap->xmit_lock);
651         if (done)
652                 ppp_output_wakeup(&ap->chan);
653 }
654 
655 /*
656  * Receive-side routines.
657  */
658 
659 static inline void
660 process_input_packet(struct syncppp *ap)
661 {
662         struct sk_buff *skb;
663         unsigned char *p;
664         int code = 0;
665 
666         skb = ap->rpkt;
667         ap->rpkt = 0;
668 
669         /* strip address/control field if present */
670         p = skb->data;
671         if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
672                 /* chop off address/control */
673                 if (skb->len < 3)
674                         goto err;
675                 p = skb_pull(skb, 2);
676         }
677 
678         /* decompress protocol field if compressed */
679         if (p[0] & 1) {
680                 /* protocol is compressed */
681                 skb_push(skb, 1)[0] = 0;
682         } else if (skb->len < 2)
683                 goto err;
684 
685         /* pass to generic layer */
686         ppp_input(&ap->chan, skb);
687         return;
688 
689  err:
690         kfree_skb(skb);
691         ppp_input_error(&ap->chan, code);
692 }
693 
694 /* called when the tty driver has data for us. 
695  *
696  * Data is frame oriented: each call to ppp_sync_input is considered
697  * a whole frame. If the 1st flag byte is non-zero then the whole
698  * frame is considered to be in error and is tossed.
699  */
700 static void
701 ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
702                 char *flags, int count)
703 {
704         struct sk_buff *skb;
705         unsigned char *sp;
706 
707         if (count == 0)
708                 return;
709 
710         /* if flag set, then error, ignore frame */
711         if (flags != 0 && *flags) {
712                 ppp_input_error(&ap->chan, *flags);
713                 return;
714         }
715 
716         if (ap->flags & SC_LOG_INPKT)
717                 ppp_print_buffer ("receive buffer", buf, count);
718 
719         /* stuff the chars in the skb */
720         if ((skb = ap->rpkt) == 0) {
721                 if ((skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2)) == 0) {
722                         printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
723                         ppp_input_error(&ap->chan, 0);
724                         return;
725                 }
726                 /* Try to get the payload 4-byte aligned */
727                 if (buf[0] != PPP_ALLSTATIONS)
728                         skb_reserve(skb, 2 + (buf[0] & 1));
729                 ap->rpkt = skb;
730         }
731         if (count > skb_tailroom(skb)) {
732                 /* packet overflowed MRU */
733                 ppp_input_error(&ap->chan, 1);
734         } else {
735                 sp = skb_put(skb, count);
736                 memcpy(sp, buf, count);
737                 process_input_packet(ap);
738         }
739 }
740 
741 void __exit
742 ppp_sync_cleanup(void)
743 {
744         if (tty_register_ldisc(N_SYNC_PPP, NULL) != 0)
745                 printk(KERN_ERR "failed to unregister Sync PPP line discipline\n");
746 }
747 
748 module_init(ppp_sync_init);
749 module_exit(ppp_sync_cleanup);
750 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.