if_pcnet.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,329 行 · 第 1/3 页

C
1,329
字号
//==========================================================================
//
//      dev/if_pcnet.c
//
//      Ethernet device driver for AMD PCNET compatible controllers
//
//==========================================================================
//####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####
//####BSDCOPYRIGHTBEGIN####
//
// -------------------------------------------
//
// Portions of this software may have been derived from OpenBSD or other sources,
// and are covered by the appropriate copyright disclaimers included herein.
//
// -------------------------------------------
//
//####BSDCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    jskov, based on lan91cxx driver by hmt & jskov
// Contributors: gthomas, jskov, hmt
// Date:         2001-04-02
// Purpose:      
// Description:  hardware driver for AMD PCNet (and possibly Lance) ethernet
// Notes:        The controller is used in its 16bit mode. That means that
//               all addresses are 24bit only - and that all controller
//               accessed memory must be within the same 16MB region
//               (starting at 0 on older controllers).
//
//               The KEEP_STATISTICS code is not implemented yet. Look
//               for FIXME macro.
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/system.h>
#include <pkgconf/devs_eth_amd_pcnet.h>
#include <pkgconf/io_eth_drivers.h>

#include <cyg/infra/cyg_type.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_if.h>             // delays
#include <string.h>
#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
#ifdef CYGPKG_NET
#include <pkgconf/net.h>
#include <cyg/kernel/kapi.h>
#include <net/if.h>  /* Needed for struct ifnet */
#include <pkgconf/io_eth_drivers.h>
#endif
#include CYGHWR_MEMORY_LAYOUT_H

#ifdef CYGPKG_IO_PCI
#include <cyg/io/pci.h>
#else
#error "Need PCI package here"
#endif

#define FIXME 0

#define _BUF_SIZE 1544

#ifdef CYGPKG_INFRA_DEBUG
// Then we log, OOI, the number of times we get a bad packet number
// from the tx done fifo.
int pcnet_txfifo_good = 0;
int pcnet_txfifo_bad = 0;
#endif

#include "amd_pcnet.h"
#define __WANT_DEVS
#include CYGDAT_DEVS_ETH_AMD_PCNET_INL
#undef  __WANT_DEVS

#if defined(CYGPKG_REDBOOT) && DEBUG

static void db_printf( char *fmt, ... )
{
    extern int start_console(void);
    extern void end_console(int);
    va_list a;
    int old_console;
    va_start( a, fmt );
    old_console = start_console();  
    diag_vprintf( fmt, a );
    end_console(old_console);
    va_end( a );
}

#else

#define db_printf diag_printf

#endif

static void pcnet_poll(struct eth_drv_sc *sc);

// This ISR is called when the ethernet interrupt occurs
static cyg_uint32
pcnet_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    struct pcnet_priv_data *cpd = (struct pcnet_priv_data *)data;

    DEBUG_FUNCTION();

    INCR_STAT( interrupts );

    cyg_drv_interrupt_mask(cpd->interrupt);
    cyg_drv_interrupt_acknowledge(cpd->interrupt);
    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
}

static void
pcnet_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    // This conditioning out is necessary because of explicit calls to this
    // DSR - which would not ever be called in the case of a polled mode
    // usage ie. in RedBoot.
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    struct pcnet_priv_data* cpd = (struct pcnet_priv_data *)data;
    struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(cpd->ndp);
    struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);

    // but here, it must be a *sc:
    eth_drv_dsr( vector, count, (cyg_addrword_t)sc );
#else
# ifndef CYGPKG_REDBOOT
#  error Empty PCnet ethernet DSR is compiled.  Is this what you want?
# endif
#endif
}


// The deliver function (ex-DSR)  handles the ethernet [logical] processing
static void
pcnet_deliver(struct eth_drv_sc *sc)
{
    struct pcnet_priv_data *cpd =
        (struct pcnet_priv_data *)sc->driver_private;

    DEBUG_FUNCTION();

    // Service the interrupt:
    pcnet_poll(sc);
    // Allow interrupts to happen again
    cyg_drv_interrupt_unmask(cpd->interrupt);
}

static int
pcnet_int_vector(struct eth_drv_sc *sc)
{
    struct pcnet_priv_data *cpd =
        (struct pcnet_priv_data *)sc->driver_private;

    return (cpd->interrupt);
}

// ------------------------------------------------------------------------
// Memory management
//
// Simply carve off from the front of the PCI mapped window into real memory
static cyg_uint32 pcnet_heap_size;
static cyg_uint8 *pcnet_heap_base;
static cyg_uint8 *pcnet_heap_free;

static void*
pciwindow_mem_alloc(int size)
{
    void *p_memory;
    int _size = size;

    CYG_ASSERT(
        (CYGHWR_AMD_PCNET_PCI_MEM_MAP_BASE <= (int)pcnet_heap_free)
        &&
        ((CYGHWR_AMD_PCNET_PCI_MEM_MAP_BASE + 
          CYGHWR_AMD_PCNET_PCI_MEM_MAP_SIZE) > (int)pcnet_heap_free)
        &&
        (0 < pcnet_heap_size)
        &&
        (CYGHWR_AMD_PCNET_PCI_MEM_MAP_SIZE >= pcnet_heap_size)
        &&
        (CYGHWR_AMD_PCNET_PCI_MEM_MAP_BASE == (int)pcnet_heap_base),
        "Heap variables corrupted" );

    p_memory = (void *)0;
    size = (size + 3) & ~3;
    if ( (pcnet_heap_free+size) < (pcnet_heap_base+pcnet_heap_size) ) {
        cyg_uint32 *p;
        p_memory = (void *)pcnet_heap_free;
        pcnet_heap_free += size;
        for ( p = (cyg_uint32 *)p_memory; _size > 0; _size -= 4 )
            *p++ = 0;
    }

#if DEBUG & 9
    db_printf("Allocated %d bytes at 0x%08x\n", size, p_memory);
#endif

    return p_memory;
}

static cyg_pci_match_func find_pcnet_match_func;

static cyg_bool
find_pcnet_match_func( cyg_uint16 v, cyg_uint16 d, cyg_uint32 c, void *p )
{
#if DEBUG & 9
    db_printf("PCI match vendor 0x%04x device 0x%04x\n", v, d);
#endif
    return (0x1022 == v) && (0x2000 == d);
}

static int
pci_init_find_pcnet( void )
{
    cyg_pci_device_id devid;
    cyg_pci_device dev_info;
    cyg_uint16 cmd;
    int device_index;
    int found_devices = 0;

    DEBUG_FUNCTION();

#ifdef CYGARC_UNCACHED_ADDRESS
    CYG_ASSERT( CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) ==
                CYGHWR_AMD_PCNET_PCI_MEM_MAP_BASE,
      "PCI window configured does not match PCI memory section base" );
#else
    CYG_ASSERT( (CYG_ADDRWORD)CYGMEM_SECTION_pci_window ==
                CYGHWR_AMD_PCNET_PCI_MEM_MAP_BASE,
      "PCI window configured does not match PCI memory section base" );
#endif
    CYG_ASSERT( CYGMEM_SECTION_pci_window_SIZE ==
                CYGHWR_AMD_PCNET_PCI_MEM_MAP_SIZE,
        "PCI window configured does not match PCI memory section size" );

    if (
#ifdef CYGARC_UNCACHED_ADDRESS
         CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) !=
#else
         (CYG_ADDRWORD)CYGMEM_SECTION_pci_window !=
#endif
         CYGHWR_AMD_PCNET_PCI_MEM_MAP_BASE
         ||
         CYGMEM_SECTION_pci_window_SIZE !=
         CYGHWR_AMD_PCNET_PCI_MEM_MAP_SIZE ) {
#if DEBUG & 8
        db_printf("pci_init_find_pcnets(): PCI window misconfigured\n");
#endif
        return 0;
    }

    // First initialize the heap in PCI window'd memory
    pcnet_heap_size = CYGHWR_AMD_PCNET_PCI_MEM_MAP_SIZE;
    pcnet_heap_base = (cyg_uint8 *)CYGHWR_AMD_PCNET_PCI_MEM_MAP_BASE;
    pcnet_heap_free = pcnet_heap_base;
#if DEBUG & 9
    db_printf("pcimem : 0x%08x size: 0x%08x\n", pcnet_heap_base, pcnet_heap_size);
#endif

    cyg_pci_init();
#if DEBUG & 8
    db_printf("Finished cyg_pci_init();\n");
#endif

    devid = CYG_PCI_NULL_DEVID;

    for (device_index = 0; 
         device_index < CYGNUM_DEVS_ETH_AMD_PCNET_DEV_COUNT;
         device_index++) {
        struct pcnet_priv_data* cpd = pcnet_priv_array[device_index];

        cpd->index = device_index;

        // See above for find_pcnet_match_func - it selects any of several
        // variants.  This is necessary in case we have multiple mixed-type
        // devices on one board in arbitrary orders.
        if (cyg_pci_find_matching( &find_pcnet_match_func, NULL, &devid )) {
#if DEBUG & 8
            db_printf("eth%d = pcnet\n", device_index);
#endif
            cyg_pci_get_device_info(devid, &dev_info);

            cpd->interrupt_handle = 0; // Flag not attached.
            if (cyg_pci_translate_interrupt(&dev_info, &cpd->interrupt)) {
#if DEBUG & 8
                db_printf(" Wired to HAL vector %d\n", cpd->interrupt);
#endif
                cyg_drv_interrupt_create(
                    cpd->interrupt,
                    1,                  // Priority - unused
                    (cyg_addrword_t)cpd,// Data item passed to ISR & DSR
                    pcnet_isr,          // ISR
                    pcnet_dsr,          // DSR
                    &cpd->interrupt_handle, // handle to intr obj
                    &cpd->interrupt_object ); // space for int obj

                cyg_drv_interrupt_attach(cpd->interrupt_handle);

                // Don't unmask the interrupt yet, that could get us into a
                // race.
            }
            else {
                cpd->interrupt = 0;
#if DEBUG & 8
                db_printf(" Does not generate interrupts.\n");
#endif
            }

            if (cyg_pci_configure_device(&dev_info)) {
#if DEBUG & 8
                int i;
                db_printf("Found device on bus %d, devfn 0x%02x:\n",
                          CYG_PCI_DEV_GET_BUS(devid),
                          CYG_PCI_DEV_GET_DEVFN(devid));

                if (dev_info.command & CYG_PCI_CFG_COMMAND_ACTIVE) {
                    db_printf(" Note that board is active. Probed"
                              " sizes and CPU addresses invalid!\n");
                }
                db_printf(" Vendor    0x%04x", dev_info.vendor);
                db_printf("\n Device    0x%04x", dev_info.device);
                db_printf("\n Command   0x%04x, Status 0x%04x\n",
                          dev_info.command, dev_info.status);
                
                db_printf(" Class/Rev 0x%08x", dev_info.class_rev);
                db_printf("\n Header 0x%02x\n", dev_info.header_type);

                db_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n",
                          dev_info.header.normal.sub_vendor, 
                          dev_info.header.normal.sub_id);

                for(i = 0; i < CYG_PCI_MAX_BAR; i++) {
                    db_printf(" BAR[%d]    0x%08x /", i, dev_info.base_address[i]);
                    db_printf(" probed size 0x%08x / CPU addr 0x%08x\n",
                              dev_info.base_size[i], dev_info.base_map[i]);
                }
                db_printf(" eth%d configured\n", device_index);
#endif
                found_devices++;
                cpd->found = 1;
                cpd->active = 0;
                cpd->devid = devid;
                cpd->base = (unsigned char*) dev_info.base_map[0];
#if DEBUG & 8
                db_printf(" I/O address = 0x%08x\n", cpd->base);
#endif

                // Don't use cyg_pci_set_device_info since it clears
                // some of the fields we want to print out below.
                cyg_pci_read_config_uint16(dev_info.devid,
                                           CYG_PCI_CFG_COMMAND, &cmd);
                cmd |= (CYG_PCI_CFG_COMMAND_IO         // enable I/O space
                        | CYG_PCI_CFG_COMMAND_MEMORY   // enable memory space
                        | CYG_PCI_CFG_COMMAND_MASTER); // enable bus master
                cyg_pci_write_config_uint16(dev_info.devid,
                                            CYG_PCI_CFG_COMMAND, cmd);

                // This is the indicator for "uses an interrupt"
                if (cpd->interrupt_handle != 0) {
                    cyg_drv_interrupt_acknowledge(cpd->interrupt);
                    cyg_drv_interrupt_unmask(cpd->interrupt);
#if DEBUG & 8
                    db_printf(" Enabled interrupt %d\n", cpd->interrupt);
#endif
                }
#if DEBUG & 8
                db_printf(" **** Device enabled for I/O and Memory "
                            "and Bus Master\n");
#endif
            }
            else {
                cpd->found = 0;
                cpd->active = 0;
#if DEBUG & 8
                db_printf("Failed to configure device %d\n", device_index);
#endif
            }
        }
        else {
            cpd->found = 0;
            cpd->active = 0;
#if DEBUG & 8
            db_printf("eth%d not found\n", device_index);
#endif
        }
    }

    if (0 == found_devices)
        return 0;

    return 1;
}


static bool 
amd_pcnet_init(struct cyg_netdevtab_entry *tab)
{
    static int initialized = 0; // only probe PCI et al *once*
    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
    struct pcnet_priv_data *cpd =
        (struct pcnet_priv_data *)sc->driver_private;
    cyg_uint16 val;
    cyg_uint32 b;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?