📄 ecos_usbeth.c
字号:
//==========================================================================
//
// ecos_usbeth.c
//
// Linux device driver for eCos-based USB ethernet peripherals.
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// Contributors: bartv
// Date: 2000-11-12
//
//####DESCRIPTIONEND####
//==========================================================================
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#ifdef MODULE
MODULE_AUTHOR("Bart Veer <bartv@redhat.com>");
MODULE_DESCRIPTION("USB ethernet driver for eCos-based peripherals");
#endif
// This array identifies specific implementations of eCos USB-ethernet
// devices. All implementations should add their vendor and device
// details.
typedef struct ecos_usbeth_impl {
const char* name;
__u16 vendor;
__u16 id;
} ecos_usbeth_impl;
const static ecos_usbeth_impl ecos_usbeth_implementations[] = {
{ "eCos ether", 0x4242, 0x4242 },
{ (const char*) 0, 0, 0 }
};
// Constants. These have to be kept in sync with the target-side
// code.
#define ECOS_USBETH_MAXTU 1516
#define ECOS_USBETH_MAX_CONTROL_TU 8
#define ECOS_USBETH_CONTROL_GET_MAC_ADDRESS 0x01
#define ECOS_USBETH_CONTROL_SET_PROMISCUOUS_MODE 0x02
// The main data structure. It keeps track of both the USB
// and network side of things, and provides buffers for
// the various operations.
//
// NOTE: currently this driver only supports a single
// plugged-in device. Theoretically multiple eCos-based
// USB ethernet devices could be plugged in to a single
// host and each one would require an ecos_usbeth
// structure.
typedef struct ecos_usbeth {
spinlock_t usb_lock;
int target_promiscuous;
struct usb_device* usb_dev;
struct net_device* net_dev;
struct net_device_stats stats;
struct urb rx_urb;
struct urb tx_urb;
unsigned char rx_buffer[ECOS_USBETH_MAXTU];
unsigned char tx_buffer[ECOS_USBETH_MAXTU];
} ecos_usbeth;
// open()
// Invoked by the TCP/IP stack when the interface is brought up.
// This just starts a receive operation.
static int
ecos_usbeth_open(struct net_device* net)
{
ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
int res;
netif_start_queue(net);
res = usb_submit_urb(&(usbeth->rx_urb));
if (0 != res) {
printk("ecos_usbeth: failed to start USB receives, %d\n", res);
}
MOD_INC_USE_COUNT;
return 0;
}
// close()
// Invoked by the TCP/IP stack when the interface is taken down.
// Any active USB operations need to be cancelled. During
// a disconnect this may get called twice, once for the
// disconnect and once for the network interface being
// brought down.
static int
ecos_usbeth_close(struct net_device* net)
{
ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
if (0 != netif_running(net)) {
netif_stop_queue(net);
net->start = 0;
if (-EINPROGRESS == usbeth->rx_urb.status) {
usb_unlink_urb(&(usbeth->rx_urb));
}
if (-EINPROGRESS == usbeth->tx_urb.status) {
usb_unlink_urb(&(usbeth->tx_urb));
}
MOD_DEC_USE_COUNT;
}
return 0;
}
// Reception.
// probe() fills in an rx_urb. When the net device is brought up
// the urb is activated, and this callback gets run for incoming
// data.
static void
ecos_usbeth_rx_callback(struct urb* urb)
{
ecos_usbeth* usbeth = (ecos_usbeth*) urb->context;
struct net_device* net = usbeth->net_dev;
struct sk_buff* skb;
int len;
int res;
if (0 != urb->status) {
// This happens numerous times during a disconnect. Do not
// issue a warning, but do clear the status field or things
// get confused when resubmitting.
//
// Some host hardware does not distinguish between CRC errors
// (very rare) and timeouts (perfectly normal). Do not
// increment the error count if it might have been a timeout.
if (USB_ST_CRC != urb->status) {
usbeth->stats.rx_errors++;
}
urb->status = 0;
} else if (2 > urb->actual_length) {
// With some hardware the target may have to send a bogus
// first packet. Just ignore those.
} else {
len = usbeth->rx_buffer[0] + (usbeth->rx_buffer[1] << 8);
if (len > (urb->actual_length - 2)) {
usbeth->stats.rx_errors++;
usbeth->stats.rx_length_errors++;
printk("ecos_usbeth: warning, packet size mismatch, got %d bytes, expected %d\n",
urb->actual_length, len);
} else {
skb = dev_alloc_skb(len + 2);
if ((struct sk_buff*)0 == skb) {
printk("ecos_usbeth: failed to alloc skb, dropping packet\n");
usbeth->stats.rx_dropped++;
} else {
#if 0
{
int i;
printk("--------------------------------------------------------------\n");
printk("ecos_usbeth RX: total size %d\n", len);
for (i = 0; (i < len) && (i < 128); i+= 8) {
printk("rx %x %x %x %x %x %x %x %x\n",
usbeth->rx_buffer[i+0], usbeth->rx_buffer[i+1], usbeth->rx_buffer[i+2], usbeth->rx_buffer[i+3],
usbeth->rx_buffer[i+4], usbeth->rx_buffer[i+5], usbeth->rx_buffer[i+6], usbeth->rx_buffer[i+7]);
}
printk("--------------------------------------------------------------\n");
}
#endif
skb->dev = net;
eth_copy_and_sum(skb, &(usbeth->rx_buffer[2]), len, 0);
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, net);
netif_rx(skb);
usbeth->stats.rx_packets++;
usbeth->stats.rx_bytes += len;
}
}
}
if (0 != netif_running(net)) {
res = usb_submit_urb(&(usbeth->rx_urb));
if (0 != res) {
printk("ecos_usbeth: failed to restart USB receives after packet, %d\n", res);
}
}
}
// start_tx().
// Transmit a single packet. The relevant USB protocol requires a
// 2-byte length field at the start, the incoming buffer has no space
// for this, and the URB API does not support any form of
// scatter/gather. Therefore unfortunately the whole packet has to be
// copied. The callback function is specified when the URB is filled
// in by probe().
static void
ecos_usbeth_tx_callback(struct urb* urb)
{
ecos_usbeth* usbeth = (ecos_usbeth*) urb->context;
spin_lock(&usbeth->usb_lock);
if (0 != netif_running(usbeth->net_dev)) {
netif_wake_queue(usbeth->net_dev);
}
spin_unlock(&usbeth->usb_lock);
}
static int
ecos_usbeth_start_tx(struct sk_buff* skb, struct net_device* net)
{
ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
int res;
if ((skb->len + 2) > ECOS_USBETH_MAXTU) {
printk("ecos_usbeth: oversized packet of %d bytes\n", skb->len);
return 0;
}
if (netif_queue_stopped(net)) {
// Another transmission already in progress.
// USB bulk operations should complete within 5s.
int current_delay = jiffies - net->trans_start;
if (current_delay < (5 * HZ)) {
return 1;
} else {
// There has been a timeout. Discard this message.
//printk("transmission timed out\n");
usbeth->stats.tx_errors++;
dev_kfree_skb(skb);
return 0;
}
}
spin_lock(&usbeth->usb_lock);
usbeth->tx_buffer[0] = skb->len & 0x00FF;
usbeth->tx_buffer[1] = (skb->len >> 8) & 0x00FF;
memcpy(&(usbeth->tx_buffer[2]), skb->data, skb->len);
usbeth->tx_urb.transfer_buffer_length = skb->len + 2;
// Some targets are unhappy about receiving 0-length packets, not
// just sending them.
if (0 == (usbeth->tx_urb.transfer_buffer_length % 64)) {
usbeth->tx_urb.transfer_buffer_length++;
}
#if 0
{
int i;
printk("--------------------------------------------------------------\n");
printk("ecos_usbeth start_tx: len %d\n", skb->len + 2);
for (i = 0; (i < (skb->len + 2)) && (i < 128); i+= 8) {
printk("tx %x %x %x %x %x %x %x %x\n",
usbeth->tx_buffer[i], usbeth->tx_buffer[i+1], usbeth->tx_buffer[i+2], usbeth->tx_buffer[i+3],
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -