OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [net/] [wan/] [dlci.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * DLCI         Implementation of Frame Relay protocol for Linux, according to
3
 *              RFC 1490.  This generic device provides en/decapsulation for an
4
 *              underlying hardware driver.  Routes & IPs are assigned to these
5
 *              interfaces.  Requires 'dlcicfg' program to create usable
6
 *              interfaces, the initial one, 'dlci' is for IOCTL use only.
7
 *
8
 * Version:     @(#)dlci.c      0.35    4 Jan 1997
9
 *
10
 * Author:      Mike McLagan <mike.mclagan@linux.org>
11
 *
12
 * Changes:
13
 *
14
 *              0.15    Mike Mclagan    Packet freeing, bug in kmalloc call
15
 *                                      DLCI_RET handling
16
 *              0.20    Mike McLagan    More conservative on which packets
17
 *                                      are returned for retry and whic are
18
 *                                      are dropped.  If DLCI_RET_DROP is
19
 *                                      returned from the FRAD, the packet is
20
 *                                      sent back to Linux for re-transmission
21
 *              0.25    Mike McLagan    Converted to use SIOC IOCTL calls
22
 *              0.30    Jim Freeman     Fixed to allow IPX traffic
23
 *              0.35    Michael Elizabeth       Fixed incorrect memcpy_fromfs
24
 *
25
 *              This program is free software; you can redistribute it and/or
26
 *              modify it under the terms of the GNU General Public License
27
 *              as published by the Free Software Foundation; either version
28
 *              2 of the License, or (at your option) any later version.
29
 */
30
 
31
#include <linux/config.h> /* for CONFIG_DLCI_COUNT */
32
#include <linux/module.h>
33
 
34
#include <linux/kernel.h>
35
#include <linux/sched.h>
36
#include <linux/types.h>
37
#include <linux/fcntl.h>
38
#include <linux/interrupt.h>
39
#include <linux/ptrace.h>
40
#include <linux/ioport.h>
41
#include <linux/in.h>
42
#include <linux/slab.h>
43
#include <linux/string.h>
44
#include <linux/init.h>
45
#include <asm/system.h>
46
#include <asm/bitops.h>
47
#include <asm/io.h>
48
#include <asm/dma.h>
49
#include <asm/uaccess.h>
50
 
51
#include <linux/errno.h>
52
 
53
#include <linux/netdevice.h>
54
#include <linux/skbuff.h>
55
#include <linux/if_arp.h>
56
#include <linux/if_frad.h>
57
 
58
#include <net/sock.h>
59
 
60
static const char devname[] = "dlci";
61
static const char version[] = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
62
 
63
static struct net_device *open_dev[CONFIG_DLCI_COUNT];
64
 
65
static char *basename[16];
66
 
67
int dlci_init(struct net_device *dev);
68
 
69
/* allow FRAD's to register their name as a valid FRAD */
70
int register_frad(const char *name)
71
{
72
        int i;
73
 
74
        if (!name)
75
                return(-EINVAL);
76
 
77
        for (i=0;i<sizeof(basename) / sizeof(char *);i++)
78
        {
79
                if (!basename[i])
80
                        break;
81
 
82
                /* take care of multiple registrations */
83
                if (strcmp(basename[i], name) == 0)
84
                        return(0);
85
        }
86
 
87
        if (i == sizeof(basename) / sizeof(char *))
88
                return(-EMLINK);
89
 
90
        basename[i] = kmalloc(strlen(name) + 1, GFP_KERNEL);
91
        if (!basename[i])
92
                return(-ENOMEM);
93
 
94
        strcpy(basename[i], name);
95
 
96
        return(0);
97
}
98
 
99
int unregister_frad(const char *name)
100
{
101
        int i;
102
 
103
        if (!name)
104
                return(-EINVAL);
105
 
106
        for (i=0;i<sizeof(basename) / sizeof(char *);i++)
107
                if (basename[i] && (strcmp(basename[i], name) == 0))
108
                        break;
109
 
110
        if (i == sizeof(basename) / sizeof(char *))
111
                return(-EINVAL);
112
 
113
        kfree(basename[i]);
114
        basename[i] = NULL;
115
 
116
        return(0);
117
}
118
 
119
/*
120
 * these encapsulate the RFC 1490 requirements as well as
121
 * deal with packet transmission and reception, working with
122
 * the upper network layers
123
 */
124
 
125
static int dlci_header(struct sk_buff *skb, struct net_device *dev,
126
                           unsigned short type, void *daddr, void *saddr,
127
                           unsigned len)
128
{
129
        struct frhdr            hdr;
130
        struct dlci_local       *dlp;
131
        unsigned int            hlen;
132
        char                    *dest;
133
 
134
        dlp = dev->priv;
135
 
136
        hdr.control = FRAD_I_UI;
137
        switch(type)
138
        {
139
                case ETH_P_IP:
140
                        hdr.IP_NLPID = FRAD_P_IP;
141
                        hlen = sizeof(hdr.control) + sizeof(hdr.IP_NLPID);
142
                        break;
143
 
144
                /* feel free to add other types, if necessary */
145
 
146
                default:
147
                        hdr.pad = FRAD_P_PADDING;
148
                        hdr.NLPID = FRAD_P_SNAP;
149
                        memset(hdr.OUI, 0, sizeof(hdr.OUI));
150
                        hdr.PID = htons(type);
151
                        hlen = sizeof(hdr);
152
                        break;
153
        }
154
 
155
        dest = skb_push(skb, hlen);
156
        if (!dest)
157
                return(0);
158
 
159
        memcpy(dest, &hdr, hlen);
160
 
161
        return(hlen);
162
}
163
 
164
static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
165
{
166
        struct dlci_local *dlp;
167
        struct frhdr            *hdr;
168
        int                                     process, header;
169
 
170
        dlp = dev->priv;
171
        hdr = (struct frhdr *) skb->data;
172
        process = 0;
173
        header = 0;
174
        skb->dev = dev;
175
 
176
        if (hdr->control != FRAD_I_UI)
177
        {
178
                printk(KERN_NOTICE "%s: Invalid header flag 0x%02X.\n", dev->name, hdr->control);
179
                dlp->stats.rx_errors++;
180
        }
181
        else
182
                switch(hdr->IP_NLPID)
183
                {
184
                        case FRAD_P_PADDING:
185
                                if (hdr->NLPID != FRAD_P_SNAP)
186
                                {
187
                                        printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->NLPID);
188
                                        dlp->stats.rx_errors++;
189
                                        break;
190
                                }
191
 
192
                                if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0)
193
                                {
194
                                        printk(KERN_NOTICE "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.\n", dev->name, hdr->OUI[0], hdr->OUI[1], hdr->OUI[2]);
195
                                        dlp->stats.rx_errors++;
196
                                        break;
197
                                }
198
 
199
                                /* at this point, it's an EtherType frame */
200
                                header = sizeof(struct frhdr);
201
                                /* Already in network order ! */
202
                                skb->protocol = hdr->PID;
203
                                process = 1;
204
                                break;
205
 
206
                        case FRAD_P_IP:
207
                                header = sizeof(hdr->control) + sizeof(hdr->IP_NLPID);
208
                                skb->protocol = htons(ETH_P_IP);
209
                                process = 1;
210
                                break;
211
 
212
                        case FRAD_P_SNAP:
213
                        case FRAD_P_Q933:
214
                        case FRAD_P_CLNP:
215
                                printk(KERN_NOTICE "%s: Unsupported NLPID 0x%02X.\n", dev->name, hdr->pad);
216
                                dlp->stats.rx_errors++;
217
                                break;
218
 
219
                        default:
220
                                printk(KERN_NOTICE "%s: Invalid pad byte 0x%02X.\n", dev->name, hdr->pad);
221
                                dlp->stats.rx_errors++;
222
                                break;
223
                }
224
 
225
        if (process)
226
        {
227
                /* we've set up the protocol, so discard the header */
228
                skb->mac.raw = skb->data;
229
                skb_pull(skb, header);
230
                dlp->stats.rx_bytes += skb->len;
231
                netif_rx(skb);
232
                dlp->stats.rx_packets++;
233
                dev->last_rx = jiffies;
234
        }
235
        else
236
                dev_kfree_skb(skb);
237
}
238
 
239
static int dlci_transmit(struct sk_buff *skb, struct net_device *dev)
240
{
241
        struct dlci_local *dlp;
242
        int                                     ret;
243
 
244
        ret = 0;
245
 
246
        if (!skb || !dev)
247
                return(0);
248
 
249
        dlp = dev->priv;
250
 
251
        netif_stop_queue(dev);
252
 
253
        ret = dlp->slave->hard_start_xmit(skb, dlp->slave);
254
        switch (ret)
255
        {
256
                case DLCI_RET_OK:
257
                        dlp->stats.tx_packets++;
258
                        ret = 0;
259
                        break;
260
                        case DLCI_RET_ERR:
261
                        dlp->stats.tx_errors++;
262
                        ret = 0;
263
                        break;
264
                        case DLCI_RET_DROP:
265
                        dlp->stats.tx_dropped++;
266
                        ret = 1;
267
                        break;
268
        }
269
        /* Alan Cox recommends always returning 0, and always freeing the packet */
270
        /* experience suggest a slightly more conservative approach */
271
 
272
        if (!ret)
273
        {
274
                dev_kfree_skb(skb);
275
                netif_wake_queue(dev);
276
        }
277
        return(ret);
278
}
279
 
280
int dlci_config(struct net_device *dev, struct dlci_conf *conf, int get)
281
{
282
        struct dlci_conf        config;
283
        struct dlci_local       *dlp;
284
        struct frad_local       *flp;
285
        int                     err;
286
 
287
        dlp = dev->priv;
288
 
289
        flp = dlp->slave->priv;
290
 
291
        if (!get)
292
        {
293
                if(copy_from_user(&config, conf, sizeof(struct dlci_conf)))
294
                        return -EFAULT;
295
                if (config.flags & ~DLCI_VALID_FLAGS)
296
                        return(-EINVAL);
297
                memcpy(&dlp->config, &config, sizeof(struct dlci_conf));
298
                dlp->configured = 1;
299
        }
300
 
301
        err = (*flp->dlci_conf)(dlp->slave, dev, get);
302
        if (err)
303
                return(err);
304
 
305
        if (get)
306
        {
307
                if(copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf)))
308
                        return -EFAULT;
309
        }
310
 
311
        return(0);
312
}
313
 
314
int dlci_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
315
{
316
        struct dlci_local *dlp;
317
 
318
        if (!capable(CAP_NET_ADMIN))
319
                return(-EPERM);
320
 
321
        dlp = dev->priv;
322
 
323
        switch(cmd)
324
        {
325
                case DLCI_GET_SLAVE:
326
                        if (!*(short *)(dev->dev_addr))
327
                                return(-EINVAL);
328
 
329
                        strncpy(ifr->ifr_slave, dlp->slave->name, sizeof(ifr->ifr_slave));
330
                        break;
331
 
332
                case DLCI_GET_CONF:
333
                case DLCI_SET_CONF:
334
                        if (!*(short *)(dev->dev_addr))
335
                                return(-EINVAL);
336
 
337
                        return(dlci_config(dev, (struct dlci_conf *) ifr->ifr_data, cmd == DLCI_GET_CONF));
338
                        break;
339
 
340
                default:
341
                        return(-EOPNOTSUPP);
342
        }
343
        return(0);
344
}
345
 
346
static int dlci_change_mtu(struct net_device *dev, int new_mtu)
347
{
348
        struct dlci_local *dlp;
349
 
350
        dlp = dev->priv;
351
 
352
        return((*dlp->slave->change_mtu)(dlp->slave, new_mtu));
353
}
354
 
355
static int dlci_open(struct net_device *dev)
356
{
357
        struct dlci_local       *dlp;
358
        struct frad_local       *flp;
359
        int                     err;
360
 
361
        dlp = dev->priv;
362
 
363
        if (!*(short *)(dev->dev_addr))
364
                return(-EINVAL);
365
 
366
        if (!netif_running(dlp->slave))
367
                return(-ENOTCONN);
368
 
369
        flp = dlp->slave->priv;
370
        err = (*flp->activate)(dlp->slave, dev);
371
        if (err)
372
                return(err);
373
 
374
        netif_start_queue(dev);
375
 
376
        return 0;
377
}
378
 
379
static int dlci_close(struct net_device *dev)
380
{
381
        struct dlci_local       *dlp;
382
        struct frad_local       *flp;
383
        int                     err;
384
 
385
        netif_stop_queue(dev);
386
 
387
        dlp = dev->priv;
388
 
389
        flp = dlp->slave->priv;
390
        err = (*flp->deactivate)(dlp->slave, dev);
391
 
392
        return 0;
393
}
394
 
395
static struct net_device_stats *dlci_get_stats(struct net_device *dev)
396
{
397
        struct dlci_local *dlp;
398
 
399
        dlp = dev->priv;
400
 
401
        return(&dlp->stats);
402
}
403
 
404
int dlci_add(struct dlci_add *dlci)
405
{
406
        struct net_device               *master, *slave;
407
        struct dlci_local       *dlp;
408
        struct frad_local       *flp;
409
        int                     err, i;
410
        char                    buf[10];
411
 
412
        /* validate slave device */
413
        slave = __dev_get_by_name(dlci->devname);
414
        if (!slave)
415
                return(-ENODEV);
416
 
417
        if (slave->type != ARPHRD_FRAD)
418
                return(-EINVAL);
419
 
420
        /* check for registration */
421
        for (i=0;i<sizeof(basename) / sizeof(char *); i++)
422
                if ((basename[i]) &&
423
                         (strncmp(dlci->devname, basename[i], strlen(basename[i])) == 0) &&
424
                         (strlen(dlci->devname) > strlen(basename[i])))
425
                        break;
426
 
427
        if (i == sizeof(basename) / sizeof(char *))
428
                return(-EINVAL);
429
 
430
        /* check for too many open devices : should this be dynamic ? */
431
        for(i=0;i<CONFIG_DLCI_COUNT;i++)
432
                if (!open_dev[i])
433
                        break;
434
 
435
        if (i == CONFIG_DLCI_COUNT)
436
                return(-ENOSPC);  /*  #### Alan: Comments on this?? */
437
 
438
        /* create device name */
439
        sprintf(buf, "%s%02i", devname, i);
440
 
441
        master = kmalloc(sizeof(*master), GFP_KERNEL);
442
        if (!master)
443
                return(-ENOMEM);
444
 
445
        memset(master, 0, sizeof(*master));
446
 
447
        strcpy(master->name, buf);
448
        master->init = dlci_init;
449
        master->flags = 0;
450
 
451
        err = register_netdev(master);
452
        if (err < 0)
453
        {
454
                kfree(master);
455
                return(err);
456
        }
457
 
458
        *(short *)(master->dev_addr) = dlci->dlci;
459
 
460
        dlp = (struct dlci_local *) master->priv;
461
        dlp->slave = slave;
462
 
463
        flp = slave->priv;
464
        err = flp ? (*flp->assoc)(slave, master) : -EINVAL;
465
        if (err < 0)
466
        {
467
                unregister_netdev(master);
468
                kfree(master->priv);
469
                kfree(master);
470
                return(err);
471
        }
472
 
473
        strcpy(dlci->devname, buf);
474
        open_dev[i] = master;
475
        MOD_INC_USE_COUNT;
476
        return(0);
477
}
478
 
479
int dlci_del(struct dlci_add *dlci)
480
{
481
        struct dlci_local       *dlp;
482
        struct frad_local       *flp;
483
        struct net_device               *master, *slave;
484
        int                     i, err;
485
 
486
        /* validate slave device */
487
        master = __dev_get_by_name(dlci->devname);
488
        if (!master)
489
                return(-ENODEV);
490
 
491
        if (netif_running(master))
492
                return(-EBUSY);
493
 
494
        dlp = master->priv;
495
        slave = dlp->slave;
496
        flp = slave->priv;
497
 
498
        err = (*flp->deassoc)(slave, master);
499
        if (err)
500
                return(err);
501
 
502
        unregister_netdev(master);
503
 
504
        for(i=0;i<CONFIG_DLCI_COUNT;i++)
505
                if (master == open_dev[i])
506
                        break;
507
 
508
        if (i<CONFIG_DLCI_COUNT)
509
                open_dev[i] = NULL;
510
 
511
        kfree(master->priv);
512
        kfree(master);
513
 
514
        MOD_DEC_USE_COUNT;
515
 
516
        return(0);
517
}
518
 
519
int dlci_ioctl(unsigned int cmd, void *arg)
520
{
521
        struct dlci_add add;
522
        int err;
523
 
524
        if (!capable(CAP_NET_ADMIN))
525
                return(-EPERM);
526
 
527
        if(copy_from_user(&add, arg, sizeof(struct dlci_add)))
528
                return -EFAULT;
529
 
530
        switch (cmd)
531
        {
532
                case SIOCADDDLCI:
533
                        err = dlci_add(&add);
534
 
535
                        if (!err)
536
                                if(copy_to_user(arg, &add, sizeof(struct dlci_add)))
537
                                        return -EFAULT;
538
                        break;
539
 
540
                case SIOCDELDLCI:
541
                        err = dlci_del(&add);
542
                        break;
543
 
544
                default:
545
                        err = -EINVAL;
546
        }
547
 
548
        return(err);
549
}
550
 
551
int dlci_init(struct net_device *dev)
552
{
553
        struct dlci_local *dlp;
554
 
555
        dev->priv = kmalloc(sizeof(struct dlci_local), GFP_KERNEL);
556
        if (!dev->priv)
557
                return(-ENOMEM);
558
 
559
        memset(dev->priv, 0, sizeof(struct dlci_local));
560
        dlp = dev->priv;
561
 
562
        dev->flags              = 0;
563
        dev->open               = dlci_open;
564
        dev->stop               = dlci_close;
565
        dev->do_ioctl           = dlci_dev_ioctl;
566
        dev->hard_start_xmit    = dlci_transmit;
567
        dev->hard_header        = dlci_header;
568
        dev->get_stats          = dlci_get_stats;
569
        dev->change_mtu         = dlci_change_mtu;
570
 
571
        dlp->receive            = dlci_receive;
572
 
573
        dev->type               = ARPHRD_DLCI;
574
        dev->hard_header_len    = sizeof(struct frhdr);
575
        dev->addr_len           = sizeof(short);
576
        memset(dev->dev_addr, 0, sizeof(dev->dev_addr));
577
 
578
        return(0);
579
}
580
 
581
int __init dlci_setup(void)
582
{
583
        int i;
584
 
585
        printk("%s.\n", version);
586
 
587
        for(i=0;i<CONFIG_DLCI_COUNT;i++)
588
                open_dev[i] = NULL;
589
 
590
        for(i=0;i<sizeof(basename) / sizeof(char *);i++)
591
                basename[i] = NULL;
592
 
593
        return(0);
594
}
595
 
596
#ifdef MODULE
597
 
598
extern int (*dlci_ioctl_hook)(unsigned int, void *);
599
 
600
int init_module(void)
601
{
602
        dlci_ioctl_hook = dlci_ioctl;
603
 
604
        return(dlci_setup());
605
}
606
 
607
void cleanup_module(void)
608
{
609
        dlci_ioctl_hook = NULL;
610
}
611
#endif /* MODULE */
612
 
613
MODULE_AUTHOR("Mike McLagan");
614
MODULE_DESCRIPTION("Frame Relay DLCI layer");
615
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.