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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [io/] [usb/] [eth/] [slave/] [current/] [host/] [ecos_usbeth.c] - Blame information for rev 786

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 786 skrzyp
//==========================================================================
2
//
3
//      ecos_usbeth.c
4
//
5
//      Linux device driver for eCos-based USB ethernet peripherals.
6
//
7
//==========================================================================
8
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
9
// -------------------------------------------                              
10
// This file is part of eCos, the Embedded Configurable Operating System.   
11
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
12
//
13
// eCos is free software; you can redistribute it and/or modify it under    
14
// the terms of the GNU General Public License as published by the Free     
15
// Software Foundation; either version 2 or (at your option) any later      
16
// version.                                                                 
17
//
18
// eCos is distributed in the hope that it will be useful, but WITHOUT      
19
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
20
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
21
// for more details.                                                        
22
//
23
// You should have received a copy of the GNU General Public License        
24
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
25
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
26
//
27
// As a special exception, if other files instantiate templates or use      
28
// macros or inline functions from this file, or you compile this file      
29
// and link it with other works to produce a work based on this file,       
30
// this file does not by itself cause the resulting work to be covered by   
31
// the GNU General Public License. However the source code for this file    
32
// must still be made available in accordance with section (3) of the GNU   
33
// General Public License v2.                                               
34
//
35
// This exception does not invalidate any other reasons why a work based    
36
// on this file might be covered by the GNU General Public License.         
37
// -------------------------------------------                              
38
// ####ECOSGPLCOPYRIGHTEND####                                              
39
//==========================================================================
40
//#####DESCRIPTIONBEGIN####                                             
41
//
42
// Author(s):           bartv
43
// Contributors:        bartv
44
// Date:                2000-11-12
45
//
46
//####DESCRIPTIONEND####
47
//==========================================================================
48
 
49
#include <linux/module.h>
50
#include <linux/init.h>
51
#include <linux/usb.h>
52
#include <linux/netdevice.h>
53
#include <linux/etherdevice.h>
54
 
55
#ifdef MODULE
56
MODULE_AUTHOR("Bart Veer <bartv@redhat.com>");
57
MODULE_DESCRIPTION("USB ethernet driver for eCos-based peripherals");
58
#endif
59
 
60
// This array identifies specific implementations of eCos USB-ethernet
61
// devices. All implementations should add their vendor and device
62
// details.
63
 
64
typedef struct ecos_usbeth_impl {
65
    const char* name;
66
    __u16       vendor;
67
    __u16       id;
68
} ecos_usbeth_impl;
69
 
70
const static ecos_usbeth_impl ecos_usbeth_implementations[] = {
71
    { "eCos ether",     0x4242, 0x4242 },
72
    { (const char*) 0,       0,      0 }
73
};
74
 
75
 
76
// Constants. These have to be kept in sync with the target-side
77
// code.
78
#define ECOS_USBETH_MAXTU                           1516
79
#define ECOS_USBETH_MAX_CONTROL_TU                     8
80
#define ECOS_USBETH_CONTROL_GET_MAC_ADDRESS         0x01
81
#define ECOS_USBETH_CONTROL_SET_PROMISCUOUS_MODE    0x02
82
 
83
// The main data structure. It keeps track of both the USB
84
// and network side of things, and provides buffers for
85
// the various operations.
86
//
87
// NOTE: currently this driver only supports a single
88
// plugged-in device. Theoretically multiple eCos-based
89
// USB ethernet devices could be plugged in to a single
90
// host and each one would require an ecos_usbeth
91
// structure.
92
typedef struct ecos_usbeth {
93
    spinlock_t                  usb_lock;
94
    int                         target_promiscuous;
95
    struct usb_device*          usb_dev;
96
    struct net_device*          net_dev;
97
    struct net_device_stats     stats;
98
    struct urb                  rx_urb;
99
    struct urb                  tx_urb;
100
    unsigned char               rx_buffer[ECOS_USBETH_MAXTU];
101
    unsigned char               tx_buffer[ECOS_USBETH_MAXTU];
102
} ecos_usbeth;
103
 
104
 
105
// open()
106
// Invoked by the TCP/IP stack when the interface is brought up.
107
// This just starts a receive operation.
108
static int
109
ecos_usbeth_open(struct net_device* net)
110
{
111
    ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
112
    int          res;
113
 
114
    netif_start_queue(net);
115
    res = usb_submit_urb(&(usbeth->rx_urb));
116
    if (0 != res) {
117
        printk("ecos_usbeth: failed to start USB receives, %d\n", res);
118
    }
119
    MOD_INC_USE_COUNT;
120
    return 0;
121
}
122
 
123
// close()
124
// Invoked by the TCP/IP stack when the interface is taken down.
125
// Any active USB operations need to be cancelled. During
126
// a disconnect this may get called twice, once for the
127
// disconnect and once for the network interface being
128
// brought down.
129
static int
130
ecos_usbeth_close(struct net_device* net)
131
{
132
    ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
133
 
134
    if (0 != netif_running(net)) {
135
        netif_stop_queue(net);
136
        net->start = 0;
137
 
138
        if (-EINPROGRESS == usbeth->rx_urb.status) {
139
            usb_unlink_urb(&(usbeth->rx_urb));
140
        }
141
        if (-EINPROGRESS == usbeth->tx_urb.status) {
142
            usb_unlink_urb(&(usbeth->tx_urb));
143
        }
144
        MOD_DEC_USE_COUNT;
145
    }
146
 
147
    return 0;
148
}
149
 
150
// Reception.
151
// probe() fills in an rx_urb. When the net device is brought up
152
// the urb is activated, and this callback gets run for incoming
153
// data.
154
static void
155
ecos_usbeth_rx_callback(struct urb* urb)
156
{
157
    ecos_usbeth*        usbeth  = (ecos_usbeth*) urb->context;
158
    struct net_device*  net     = usbeth->net_dev;
159
    struct sk_buff*     skb;
160
    int                 len;
161
    int                 res;
162
 
163
    if (0 != urb->status) {
164
        // This happens numerous times during a disconnect. Do not
165
        // issue a warning, but do clear the status field or things
166
        // get confused when resubmitting.
167
        //
168
        // Some host hardware does not distinguish between CRC errors
169
        // (very rare) and timeouts (perfectly normal). Do not
170
        // increment the error count if it might have been a timeout.
171
        if (USB_ST_CRC != urb->status) {
172
            usbeth->stats.rx_errors++;
173
        }
174
        urb->status = 0;
175
    } else if (2 > urb->actual_length) {
176
        // With some hardware the target may have to send a bogus
177
        // first packet. Just ignore those.
178
 
179
    } else {
180
        len = usbeth->rx_buffer[0] + (usbeth->rx_buffer[1] << 8);
181
        if (len > (urb->actual_length - 2)) {
182
            usbeth->stats.rx_errors++;
183
            usbeth->stats.rx_length_errors++;
184
            printk("ecos_usbeth: warning, packet size mismatch, got %d bytes, expected %d\n",
185
                   urb->actual_length, len);
186
        } else {
187
            skb = dev_alloc_skb(len + 2);
188
            if ((struct sk_buff*)0 == skb) {
189
                printk("ecos_usbeth: failed to alloc skb, dropping packet\n");
190
                usbeth->stats.rx_dropped++;
191
            } else {
192
#if 0
193
                {
194
                    int i;
195
                    printk("--------------------------------------------------------------\n");
196
                    printk("ecos_usbeth RX: total size %d\n", len);
197
                    for (i = 0; (i < len) && (i < 128); i+= 8) {
198
                        printk("rx  %x %x %x %x %x %x %x %x\n",
199
                               usbeth->rx_buffer[i+0], usbeth->rx_buffer[i+1], usbeth->rx_buffer[i+2], usbeth->rx_buffer[i+3],
200
                               usbeth->rx_buffer[i+4], usbeth->rx_buffer[i+5], usbeth->rx_buffer[i+6], usbeth->rx_buffer[i+7]);
201
                    }
202
                    printk("--------------------------------------------------------------\n");
203
                }
204
#endif
205
                skb->dev        = net;
206
                eth_copy_and_sum(skb, &(usbeth->rx_buffer[2]), len, 0);
207
                skb_put(skb, len);
208
                skb->protocol   = eth_type_trans(skb, net);
209
                netif_rx(skb);
210
                usbeth->stats.rx_packets++;
211
                usbeth->stats.rx_bytes += len;
212
            }
213
        }
214
    }
215
 
216
    if (0 != netif_running(net)) {
217
        res = usb_submit_urb(&(usbeth->rx_urb));
218
        if (0 != res) {
219
            printk("ecos_usbeth: failed to restart USB receives after packet, %d\n", res);
220
        }
221
    }
222
}
223
 
224
// start_tx().
225
// Transmit a single packet. The relevant USB protocol requires a
226
// 2-byte length field at the start, the incoming buffer has no space
227
// for this, and the URB API does not support any form of
228
// scatter/gather. Therefore unfortunately the whole packet has to be
229
// copied. The callback function is specified when the URB is filled
230
// in by probe().
231
static void
232
ecos_usbeth_tx_callback(struct urb* urb)
233
{
234
    ecos_usbeth* usbeth = (ecos_usbeth*) urb->context;
235
    spin_lock(&usbeth->usb_lock);
236
    if (0 != netif_running(usbeth->net_dev)) {
237
        netif_wake_queue(usbeth->net_dev);
238
    }
239
    spin_unlock(&usbeth->usb_lock);
240
}
241
 
242
static int
243
ecos_usbeth_start_tx(struct sk_buff* skb, struct net_device* net)
244
{
245
    ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
246
    int          res;
247
 
248
    if ((skb->len + 2) > ECOS_USBETH_MAXTU) {
249
        printk("ecos_usbeth: oversized packet of %d bytes\n", skb->len);
250
        return 0;
251
    }
252
 
253
    if (netif_queue_stopped(net)) {
254
        // Another transmission already in progress.
255
        // USB bulk operations should complete within 5s.
256
        int current_delay = jiffies - net->trans_start;
257
        if (current_delay < (5 * HZ)) {
258
            return 1;
259
        } else {
260
            // There has been a timeout. Discard this message.
261
            //printk("transmission timed out\n");
262
            usbeth->stats.tx_errors++;
263
            dev_kfree_skb(skb);
264
            return 0;
265
        }
266
    }
267
 
268
    spin_lock(&usbeth->usb_lock);
269
    usbeth->tx_buffer[0]        = skb->len & 0x00FF;
270
    usbeth->tx_buffer[1]        = (skb->len >> 8) & 0x00FF;
271
    memcpy(&(usbeth->tx_buffer[2]), skb->data, skb->len);
272
    usbeth->tx_urb.transfer_buffer_length = skb->len + 2;
273
 
274
    // Some targets are unhappy about receiving 0-length packets, not
275
    // just sending them.
276
    if (0 == (usbeth->tx_urb.transfer_buffer_length % 64)) {
277
        usbeth->tx_urb.transfer_buffer_length++;
278
    }
279
#if 0
280
    {
281
        int i;
282
        printk("--------------------------------------------------------------\n");
283
        printk("ecos_usbeth start_tx: len %d\n", skb->len + 2);
284
        for (i = 0; (i < (skb->len + 2)) && (i < 128); i+= 8) {
285
            printk("tx  %x %x %x %x %x %x %x %x\n",
286
                   usbeth->tx_buffer[i], usbeth->tx_buffer[i+1], usbeth->tx_buffer[i+2], usbeth->tx_buffer[i+3],
287
                   usbeth->tx_buffer[i+4], usbeth->tx_buffer[i+5], usbeth->tx_buffer[i+6], usbeth->tx_buffer[i+7]);
288
        }
289
        printk("--------------------------------------------------------------\n");
290
    }
291
#endif
292
    res = usb_submit_urb(&(usbeth->tx_urb));
293
    if (0 == res) {
294
        netif_stop_queue(net);
295
        net->trans_start        = jiffies;
296
        usbeth->stats.tx_packets++;
297
        usbeth->stats.tx_bytes  += skb->len;
298
    } else {
299
        printk("ecos_usbeth: failed to start USB packet transmission, %d\n", res);
300
        usbeth->stats.tx_errors++;
301
    }
302
 
303
    spin_unlock(&usbeth->usb_lock);
304
    dev_kfree_skb(skb);
305
    return 0;
306
}
307
 
308
 
309
// set_rx_mode()
310
// Invoked by the network stack to enable/disable promiscuous mode or
311
// for multicasting. The latter is not yet supported on the target
312
// side. The former involves a USB control message. The main call
313
// is not allowed to block.
314
static void
315
ecos_usbeth_set_rx_mode_callback(struct urb* urb)
316
{
317
    kfree(urb->setup_packet);
318
    usb_free_urb(urb);
319
}
320
 
321
static void
322
ecos_usbeth_set_rx_mode(struct net_device* net)
323
{
324
    ecos_usbeth* usbeth         = (ecos_usbeth*) net->priv;
325
    __u16        promiscuous    = net->flags & IFF_PROMISC;
326
    int          res;
327
 
328
    if (promiscuous != usbeth->target_promiscuous) {
329
        devrequest*     req;
330
        urb_t*          urb;
331
 
332
        urb = usb_alloc_urb(0);
333
        if ((urb_t*)0 == urb) {
334
            return;
335
        }
336
        req = kmalloc(sizeof(devrequest), GFP_KERNEL);
337
        if ((devrequest*)0 == req) {
338
            usb_free_urb(urb);
339
            return;
340
        }
341
        req->requesttype        = USB_TYPE_CLASS | USB_RECIP_DEVICE;
342
        req->request            = ECOS_USBETH_CONTROL_SET_PROMISCUOUS_MODE;
343
        req->value              = cpu_to_le16p(&promiscuous);
344
        req->index              = 0;
345
        req->length             = 0;
346
 
347
        FILL_CONTROL_URB(urb,
348
                         usbeth->usb_dev,
349
                         usb_sndctrlpipe(usbeth->usb_dev, 0),
350
                         (unsigned char*) req,
351
                         (void*) 0,
352
                         0,
353
                         &ecos_usbeth_set_rx_mode_callback,
354
                         (void*) usbeth);
355
        res = usb_submit_urb(urb);
356
        if (0 != res) {
357
            kfree(req);
358
            usb_free_urb(urb);
359
        } else {
360
            usbeth->target_promiscuous = promiscuous;
361
        }
362
    }
363
}
364
 
365
// netdev_stats()
366
// Supply the current network statistics. These are held in
367
// the stats field of the ecos_usbeth structure
368
static struct net_device_stats*
369
ecos_usbeth_netdev_stats(struct net_device* net)
370
{
371
    ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
372
    return &(usbeth->stats);
373
}
374
 
375
// ioctl()
376
// Currently none of the network ioctl()'s are supported
377
static int
378
ecos_usbeth_ioctl(struct net_device* net, struct ifreq* request, int command)
379
{
380
    return -EINVAL;
381
}
382
 
383
// probe().
384
// This is invoked by the generic USB code when a new device has
385
// been detected and its configuration details have been extracted
386
// and stored in the usbdev structure. The interface_id specifies
387
// a specific USB interface, to cope with multifunction peripherals.
388
//
389
// FIXME; right now this code only copes with simple enumeration data.
390
// OK, to be really honest it just looks for the vendor and device ids
391
// in the simple test cases and ignores everything else.
392
//
393
// On success it should return a non-NULL pointer, which happens to be
394
// a newly allocated ecos_usbeth structure. This will get passed to
395
// the disconnect function. Filling in the ecos_usbeth structure will,
396
// amongst other things, register this as a network device driver.
397
// The MAC address is obtained from the peripheral via a control
398
// request.
399
 
400
static void*
401
ecos_usbeth_probe(struct usb_device* usbdev, unsigned int interface_id)
402
{
403
    struct net_device*  net;
404
    ecos_usbeth*        usbeth;
405
    int                 res;
406
    unsigned char       MAC[6];
407
    unsigned char       dummy[1];
408
    int                 tx_endpoint = -1;
409
    int                 rx_endpoint = -1;
410
    const ecos_usbeth_impl*   impl;
411
    int                 found_impl = 0;
412
 
413
    // See if this is the correct driver for this USB peripheral.
414
    impl = ecos_usbeth_implementations;
415
    while (impl->name != NULL) {
416
        if ((usbdev->descriptor.idVendor  != impl->vendor) ||
417
            (usbdev->descriptor.idProduct != impl->id)) {
418
            found_impl = 1;
419
            break;
420
        }
421
        impl++;
422
    }
423
    if (! found_impl) {
424
        return (void*) 0;
425
    }
426
 
427
    // For now only support USB-ethernet peripherals consisting of a single
428
    // configuration, with a single interface, with two bulk endpoints.
429
    if ((1 != usbdev->descriptor.bNumConfigurations)  ||
430
        (1 != usbdev->config[0].bNumInterfaces) ||
431
        (2 != usbdev->config[0].interface[0].altsetting->bNumEndpoints)) {
432
        return (void*) 0;
433
    }
434
    if ((0 == (usbdev->config[0].interface[0].altsetting->endpoint[0].bEndpointAddress & USB_DIR_IN)) &&
435
        (0 != (usbdev->config[0].interface[0].altsetting->endpoint[1].bEndpointAddress & USB_DIR_IN))) {
436
        tx_endpoint = usbdev->config[0].interface[0].altsetting->endpoint[0].bEndpointAddress;
437
        rx_endpoint = usbdev->config[0].interface[0].altsetting->endpoint[1].bEndpointAddress & ~USB_DIR_IN;
438
    }
439
    if ((0 != (usbdev->config[0].interface[0].altsetting->endpoint[0].bEndpointAddress & USB_DIR_IN)) &&
440
        (0 == (usbdev->config[0].interface[0].altsetting->endpoint[1].bEndpointAddress & USB_DIR_IN))) {
441
        tx_endpoint = usbdev->config[0].interface[0].altsetting->endpoint[1].bEndpointAddress;
442
        rx_endpoint = usbdev->config[0].interface[0].altsetting->endpoint[0].bEndpointAddress & ~USB_DIR_IN;
443
    }
444
    if (-1 == tx_endpoint) {
445
        return (void*) 0;
446
    }
447
 
448
    res = usb_set_configuration(usbdev, usbdev->config[0].bConfigurationValue);
449
    if (0 != res) {
450
        printk("ecos_usbeth: failed to set configuration, %d\n", res);
451
        return (void*) 0;
452
    }
453
    res = usb_control_msg(usbdev,
454
                          usb_rcvctrlpipe(usbdev, 0),
455
                          ECOS_USBETH_CONTROL_GET_MAC_ADDRESS,
456
                          USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN,
457
                          0,
458
                          0,
459
                          (void*) MAC,
460
                          6,
461
                          5 * HZ);
462
    if (6 != res) {
463
        printk("ecos_usbeth: failed to get MAC address, %d\n", res);
464
        return (void*) 0;
465
    }
466
 
467
    res = usb_control_msg(usbdev,
468
                          usb_sndctrlpipe(usbdev, 0),                           // pipe
469
                          ECOS_USBETH_CONTROL_SET_PROMISCUOUS_MODE,             // request
470
                          USB_TYPE_CLASS | USB_RECIP_DEVICE,                    // requesttype
471
                          0,                                                    // value
472
                          0,                                                    // index
473
                          (void*) dummy,                                        // data
474
                          0,                                                    // size
475
                          5 * HZ);                                              // timeout
476
    if (0 != res) {
477
        printk("ecos_usbeth: failed to disable promiscous mode, %d\n", res);
478
    }
479
 
480
    usbeth = (ecos_usbeth*) kmalloc(sizeof(ecos_usbeth), GFP_KERNEL);
481
    if ((ecos_usbeth*)0 == usbeth) {
482
        printk("ecos_usbeth: failed to allocate memory for usbeth data structure\n");
483
        return (void*) 0;
484
    }
485
    memset(usbeth, 0, sizeof(ecos_usbeth));
486
 
487
    net                 = init_etherdev(0, 0);
488
    if ((struct net_device*) 0 == net) {
489
        kfree(usbeth);
490
        printk("ecos_usbeth: failed to allocate memory for net data structure\n");
491
        return (void*) 0;
492
    }
493
 
494
    usbeth->usb_lock    = SPIN_LOCK_UNLOCKED;
495
    usbeth->usb_dev     = usbdev;
496
    FILL_BULK_URB(&(usbeth->tx_urb), usbdev, usb_sndbulkpipe(usbdev, tx_endpoint),
497
                  usbeth->tx_buffer, ECOS_USBETH_MAXTU, &ecos_usbeth_tx_callback, (void*) usbeth);
498
    FILL_BULK_URB(&(usbeth->rx_urb), usbdev, usb_rcvbulkpipe(usbdev, rx_endpoint),
499
                  usbeth->rx_buffer, ECOS_USBETH_MAXTU, &ecos_usbeth_rx_callback, (void*) usbeth);
500
 
501
    usbeth->net_dev             = net;
502
    usbeth->target_promiscuous  = 0;
503
 
504
    net->priv                   = (void*) usbeth;
505
    net->open                   = &ecos_usbeth_open;
506
    net->stop                   = &ecos_usbeth_close;
507
    net->do_ioctl               = &ecos_usbeth_ioctl;
508
    net->hard_start_xmit        = &ecos_usbeth_start_tx;
509
    net->set_multicast_list     = &ecos_usbeth_set_rx_mode;
510
    net->get_stats              = &ecos_usbeth_netdev_stats;
511
    net->mtu                    = 1500; // ECOS_USBETH_MAXTU - 2;
512
    memcpy(net->dev_addr, MAC, 6);
513
 
514
    printk("eCos-based USB ethernet peripheral active at %s\n", net->name);
515
    MOD_INC_USE_COUNT;
516
    return (void*) usbeth;
517
}
518
 
519
// disconnect().
520
// Invoked after probe() has recognized a device but that device
521
// has gone away. 
522
static void
523
ecos_usbeth_disconnect(struct usb_device* usbdev, void* data)
524
{
525
    ecos_usbeth* usbeth = (ecos_usbeth*) data;
526
    if (!usbeth) {
527
        printk("ecos_usbeth: warning, disconnecting unconnected device\n");
528
        return;
529
    }
530
    if (0 != netif_running(usbeth->net_dev)) {
531
        ecos_usbeth_close(usbeth->net_dev);
532
    }
533
    unregister_netdev(usbeth->net_dev);
534
    if (-EINPROGRESS == usbeth->rx_urb.status) {
535
        usb_unlink_urb(&(usbeth->rx_urb));
536
    }
537
    if (-EINPROGRESS == usbeth->tx_urb.status) {
538
        usb_unlink_urb(&(usbeth->tx_urb));
539
    }
540
    kfree(usbeth);
541
    MOD_DEC_USE_COUNT;
542
}
543
 
544
static struct usb_driver ecos_usbeth_driver = {
545
    name:       "ecos_usbeth",
546
    probe:      ecos_usbeth_probe,
547
    disconnect: ecos_usbeth_disconnect,
548
};
549
 
550
// init()
551
// Called when the module is loaded. It just registers the device with
552
// the generic USB code. Nothing else can really be done until
553
// the USB code detects that a device has been attached.
554
int __init
555
ecos_usbeth_init(void)
556
{
557
    printk("eCos USB-ethernet device driver\n");
558
    return usb_register(&ecos_usbeth_driver);
559
}
560
 
561
// exit()
562
// Called when the module is unloaded. disconnect() will be
563
// invoked if appropriate.
564
void __exit
565
ecos_usbeth_exit(void)
566
{
567
    usb_deregister(&ecos_usbeth_driver);
568
}
569
 
570
module_init(ecos_usbeth_init);
571
module_exit(ecos_usbeth_exit);

powered by: WebSVN 2.1.0

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