Skip to content
Snippets Groups Projects
e1000.c 32 KiB
Newer Older
/****************************************************************************
 * drivers/net/e1000.c
 *
 *   Copyright (C) 2011 Yu Qiang. All rights reserved.
 *   Author: Yu Qiang <yuq825@gmail.com>
 *
 * This file is a part of NuttX:
 *
 *   Copyright (C) 2011 Gregory Nutt. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <nuttx/kmalloc.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <debug.h>
#include <wdog.h>
#include <errno.h>

#include <nuttx/irq.h>
#include <nuttx/arch.h>

#include <net/uip/uip.h>
#include <net/uip/uip-arp.h>
#include <net/uip/uip-arch.h>

#include <rgmp/pmap.h>
#include <rgmp/pci.h>
#include <rgmp/string.h>
#include <rgmp/x86.h>
#include <rgmp/stdio.h>
#include "e1000.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */

#define E1000_WDDELAY   (1*CLK_TCK)
#define E1000_POLLHSEC  (1*2)

/* TX timeout = 1 minute */

#define E1000_TXTIMEOUT (60*CLK_TCK)

/* This is a helper pointer for accessing the contents of the Ethernet header */

#define BUF ((struct uip_eth_hdr *)e1000->uip_dev.d_buf)

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct tx_ring {
    struct tx_desc *desc;
    char *buf;
    int tail;      // where to write desc
};

struct rx_ring {
    struct rx_desc *desc;
    char *buf;
    int head;      // where to read
    int tail;      // where to release free desc
    int free;      // number of freed desc
};

struct e1000_dev {
    uint32_t phy_mem_base;
    uint32_t io_mem_base;
    uint32_t mem_size;
    int pci_dev_id;
    unsigned char src_mac[6];
    unsigned char dst_mac[6];
    int irq;
    struct irq_action int_desc;
    struct tx_ring tx_ring;
    struct rx_ring rx_ring;
    struct e1000_dev *next;

    // NuttX net data
    bool bifup;               /* true:ifup false:ifdown */
    WDOG_ID txpoll;           /* TX poll timer */
    WDOG_ID txtimeout;        /* TX timeout timer */

    /* This holds the information visible to uIP/NuttX */

    struct uip_driver_s uip_dev;  /* Interface understood by uIP */
};

struct e1000_dev_head {
    struct e1000_dev *next;
};

/****************************************************************************
 * Private Data
 ****************************************************************************/

static struct e1000_dev_head e1000_list = {0};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Common TX logic */

static int  e1000_transmit(struct e1000_dev *e1000);
static int  e1000_uiptxpoll(struct uip_driver_s *dev);

/* Interrupt handling */

static void e1000_receive(struct e1000_dev *e1000);

/* Watchdog timer expirations */

static void e1000_polltimer(int argc, uint32_t arg, ...);
static void e1000_txtimeout(int argc, uint32_t arg, ...);

/* NuttX callback functions */

static int e1000_ifup(struct uip_driver_s *dev);
static int e1000_ifdown(struct uip_driver_s *dev);
static int e1000_txavail(struct uip_driver_s *dev);
#ifdef CONFIG_NET_IGMP
static int e1000_addmac(struct uip_driver_s *dev, const uint8_t *mac);
static int e1000_rmmac(struct uip_driver_s *dev, const uint8_t *mac);
#endif

/****************************************************************************
 * Private Functions
 ****************************************************************************/
 
static inline void e1000_outl(struct e1000_dev *dev, int reg, uint32_t val)
{
    writel(dev->io_mem_base+reg, val);
}

static inline uint32_t e1000_inl(struct e1000_dev *dev, int reg)
{
    return readl(dev->io_mem_base+reg);
}

/****************************** e1000 driver ********************************/

patacongo's avatar
patacongo committed
void e1000_reset(struct e1000_dev *dev)
{
    uint32_t dev_control;

    // Reset the network controller hardware
    dev_control = 0;
    dev_control |= (1<<0);   // FD-bit (Full Duplex)
    dev_control |= (0<<2);   // GIOMD-bit (GIO Master Disable)
    dev_control |= (1<<3);   // LRST-bit (Link Reset)
    dev_control |= (1<<6);   // SLU-bit (Set Link Up)	
patacongo's avatar
patacongo committed
    dev_control |= (2<<8);   // SPEED=2 (1000Mbps)
    dev_control |= (0<<11);  // FRCSPD-bit (Force Speed)
    dev_control |= (0<<12);  // FRCDPLX-bit (Force Duplex)
    dev_control |= (0<<20);  // ADVD3WUC-bit (Advertise D3 Wake Up Cap)
    dev_control |= (1<<26);  // RST-bit (Device Reset)
    dev_control |= (1<<27);  // RFCE-bit (Receive Flow Control Enable)
    dev_control |= (1<<28);  // TFCE-bit (Transmit Flow Control Enable) 
    dev_control |= (0<<30);  // VME-bit (VLAN Mode Enable) 
    dev_control |= (0<<31);  // PHY_RST-bit (PHY Reset)

    e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
    e1000_outl(dev, E1000_STATUS, 0x00000000);
    e1000_outl(dev, E1000_CTRL, dev_control);
    dev_control &= ~(1<<26);  // clear RST-bit (Device Reset)
    e1000_outl(dev, E1000_CTRL, dev_control);	
patacongo's avatar
patacongo committed
    up_mdelay(10);
    e1000_outl(dev, E1000_CTRL_EXT, 0x001401C0);
patacongo's avatar
patacongo committed
    e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
patacongo's avatar
patacongo committed
void e1000_turn_on(struct e1000_dev *dev)
{
    int	tx_control, rx_control;
    uint32_t ims = 0;

    // turn on the controller's receive engine
    rx_control = e1000_inl(dev, E1000_RCTL);
    rx_control |= (1<<1);
    e1000_outl(dev, E1000_RCTL, rx_control);	

    // turn on the controller's transmit engine
    tx_control = e1000_inl(dev, E1000_TCTL);
    tx_control |= (1<<1);
    e1000_outl(dev, E1000_TCTL, tx_control);	

    // enable the controller's interrupts
    e1000_outl(dev, E1000_ICR, 0xFFFFFFFF);
    e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);

    ims |= 1<<0;      // TXDW
    ims |= 1<<1;      // TXQE
    ims |= 1<<2;      // LSC
    ims |= 1<<4;      // RXDMT0 
    ims |= 1<<7;      // RXT0 
    e1000_outl(dev, E1000_IMS, ims);
}

patacongo's avatar
patacongo committed
void e1000_turn_off(struct e1000_dev *dev)
{
    int	tx_control, rx_control;

    // turn off the controller's receive engine
    rx_control = e1000_inl(dev, E1000_RCTL);
    rx_control &= ~(1<<1);
    e1000_outl(dev, E1000_RCTL, rx_control);	

    // turn off the controller's transmit engine
    tx_control = e1000_inl(dev, E1000_TCTL);
    tx_control &= ~(1<<1);
    e1000_outl(dev, E1000_TCTL, tx_control);	

    // turn off the controller's interrupts
    e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
}

patacongo's avatar
patacongo committed
void e1000_init(struct e1000_dev *dev)
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
{
    uint32_t rxd_phys, txd_phys, kmem_phys;
    uint32_t rx_control, tx_control;
    uint32_t pba;
    int i;

    e1000_reset(dev);

    // configure the controller's 'receive' engine
    rx_control = 0;
    rx_control |= (0<<1);	  // EN-bit (Enable)
    rx_control |= (0<<2);	  // SPB-bit (Store Bad Packets) 	
    rx_control |= (0<<3);	  // UPE-bit (Unicast Promiscuous Mode)
    rx_control |= (1<<4);	  // MPE-bit (Multicast Promiscuous Mode)
    rx_control |= (0<<5);	  // LPE-bit (Long Packet Enable)
    rx_control |= (0<<6);	  // LBM=0 (Loop-Back Mode)
    rx_control |= (0<<8);	  // RDMTS=0 (Rx Descriptor Min Threshold Size)
    rx_control |= (0<<10);  // DTYPE=0 (Descriptor Type)
    rx_control |= (0<<12);  // MO=0 (Multicast Offset)
    rx_control |= (1<<15);  // BAM-bit (Broadcast Address Mode)
    rx_control |= (0<<16);  // BSIZE=0 (Buffer Size = 2048) 	
    rx_control |= (0<<18);  // VLE-bit (VLAN filter Enable)
    rx_control |= (0<<19);  // CFIEN-bit (Canonical Form Indicator Enable)	
    rx_control |= (0<<20);  // CFI-bit (Canonical Form Indicator)
    rx_control |= (1<<22);  // DPF-bit (Discard Pause Frames)	
    rx_control |= (0<<23);  // PMCF-bit (Pass MAC Control Frames)
    rx_control |= (0<<25);  // BSEX=0 (Buffer Size EXtension)
    rx_control |= (1<<26);  // SECRC-bit (Strip Ethernet CRC)
    rx_control |= (0<<27);  // FLEXBUF=0 (Flexible Buffer size)	
    e1000_outl(dev, E1000_RCTL, rx_control);

    // configure the controller's 'transmit' engine
    tx_control = 0;
    tx_control |= (0<<1);	   // EN-bit (Enable)
    tx_control |= (1<<3);	   // PSP-bit (Pad Short Packets)
    tx_control |= (15<<4);   // CT=15 (Collision Threshold)
    tx_control |= (63<<12);  // COLD=63 (Collision Distance)
    tx_control |= (0<<22);   // SWXOFF-bit (Software XOFF)
    tx_control |= (1<<24);   // RTLC-bit (Re-Transmit on Late Collision)
    tx_control |= (0<<25);   // UNORTX-bit (Underrun No Re-Transmit)
    tx_control |= (0<<26);   // TXCSCMT=0 (TxDesc Mininum Threshold)
    tx_control |= (0<<28);   // MULR-bit (Multiple Request Support)
    e1000_outl(dev, E1000_TCTL, tx_control);

    // hardware flow control
    pba = e1000_inl(dev, E1000_PBA);
    // get receive FIFO size
    pba = (pba & 0x000000ff)<<10;
    e1000_outl(dev, E1000_FCAL, 0x00C28001);
    e1000_outl(dev, E1000_FCAH, 0x00000100);
    e1000_outl(dev, E1000_FCT, 0x00008808);
    e1000_outl(dev, E1000_FCTTV, 0x00000680);
    e1000_outl(dev, E1000_FCRTL, (pba*8/10)|0x80000000);
    e1000_outl(dev, E1000_FCRTH, pba*9/10);

    // setup tx rings
    txd_phys = PADDR(dev->tx_ring.desc);
    kmem_phys = PADDR(dev->tx_ring.buf);
    for (i=0; i<CONFIG_E1000_N_TX_DESC; i++,kmem_phys+=CONFIG_E1000_BUFF_SIZE) {
	dev->tx_ring.desc[i].base_address = kmem_phys;
	dev->tx_ring.desc[i].packet_length = 0;
	dev->tx_ring.desc[i].cksum_offset = 0;
	dev->tx_ring.desc[i].cksum_origin = 0;
	dev->tx_ring.desc[i].desc_status = 1;
	dev->tx_ring.desc[i].desc_command = (1<<0)|(1<<1)|(1<<3);
	dev->tx_ring.desc[i].special_info = 0;
    }
    dev->tx_ring.tail = 0;
    e1000_outl(dev, E1000_TDT, 0);
    e1000_outl(dev, E1000_TDH, 0);
    // tell controller the location, size, and fetch-policy for Tx queue
    e1000_outl(dev, E1000_TDBAL, txd_phys);
    e1000_outl(dev, E1000_TDBAH, 0x00000000);
    e1000_outl(dev, E1000_TDLEN, CONFIG_E1000_N_TX_DESC*16);
    e1000_outl(dev, E1000_TXDCTL, 0x01010000);

    // setup rx rings
    rxd_phys = PADDR(dev->rx_ring.desc);
    kmem_phys = PADDR(dev->rx_ring.buf);
    for (i=0; i<CONFIG_E1000_N_RX_DESC; i++,kmem_phys+=CONFIG_E1000_BUFF_SIZE) {
	dev->rx_ring.desc[i].base_address = kmem_phys;
	dev->rx_ring.desc[i].packet_length = 0;
	dev->rx_ring.desc[i].packet_cksum = 0;
	dev->rx_ring.desc[i].desc_status = 0;
	dev->rx_ring.desc[i].desc_errors = 0;
	dev->rx_ring.desc[i].vlan_tag = 0;
    }
    dev->rx_ring.head = 0;
    dev->rx_ring.tail = CONFIG_E1000_N_RX_DESC-1;
    dev->rx_ring.free = 0;
    // give the controller ownership of all receive descriptors
    e1000_outl(dev, E1000_RDH, 0);
    e1000_outl(dev, E1000_RDT, CONFIG_E1000_N_RX_DESC-1);
    // tell controller the location, size, and fetch-policy for RX queue
    e1000_outl(dev, E1000_RDBAL, rxd_phys);
    e1000_outl(dev, E1000_RDBAH, 0x00000000);
    e1000_outl(dev, E1000_RDLEN, CONFIG_E1000_N_RX_DESC*16);
    e1000_outl(dev, E1000_RXDCTL, 0x01010000);

    e1000_turn_on(dev);
}

/****************************************************************************
 * Function: e1000_transmit
 *
 * Description:
 *   Start hardware transmission.  Called either from the txdone interrupt
 *   handling or from watchdog based polling.
 *
 * Parameters:
 *   e1000  - Reference to the driver state structure
 *
 * Returned Value:
 *   OK on success; a negated errno on failure
 *
 * Assumptions:
 *   May or may not be called from an interrupt handler.  In either case,
 *   global interrupts are disabled, either explicitly or indirectly through
 *   interrupt handling logic.
 *
 ****************************************************************************/

static int e1000_transmit(struct e1000_dev *e1000)
{
    int tail = e1000->tx_ring.tail;
    unsigned char *cp = (unsigned char *)
	(e1000->tx_ring.buf + tail * CONFIG_E1000_BUFF_SIZE);
    int count = e1000->uip_dev.d_len;

    /* Verify that the hardware is ready to send another packet.  If we get
     * here, then we are committed to sending a packet; Higher level logic
     * must have assured that there is not transmission in progress.
     */

    if (!e1000->tx_ring.desc[tail].desc_status)
	return -1;

    /* Increment statistics */

    /* Send the packet: address=skel->sk_dev.d_buf, length=skel->sk_dev.d_len */
    memcpy(cp, e1000->uip_dev.d_buf, e1000->uip_dev.d_len);

    // prepare the transmit-descriptor
    e1000->tx_ring.desc[tail].packet_length = count<60 ? 60:count;
    e1000->tx_ring.desc[tail].desc_status = 0;

    // give ownership of this descriptor to the network controller
    tail = (tail + 1) % CONFIG_E1000_N_TX_DESC;
    e1000->tx_ring.tail = tail;
    e1000_outl(e1000, E1000_TDT, tail);

    /* Enable Tx interrupts */

    /* Setup the TX timeout watchdog (perhaps restarting the timer) */

    wd_start(e1000->txtimeout, E1000_TXTIMEOUT, e1000_txtimeout, 1, (uint32_t)e1000);
    return OK;
}

/****************************************************************************
 * Function: e1000_uiptxpoll
 *
 * Description:
 *   The transmitter is available, check if uIP has any outgoing packets ready
 *   to send.  This is a callback from uip_poll().  uip_poll() may be called:
 *
 *   1. When the preceding TX packet send is complete,
 *   2. When the preceding TX packet send timesout and the interface is reset
 *   3. During normal TX polling
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   OK on success; a negated errno on failure
 *
 * Assumptions:
 *   May or may not be called from an interrupt handler.  In either case,
 *   global interrupts are disabled, either explicitly or indirectly through
 *   interrupt handling logic.
 *
 ****************************************************************************/

static int e1000_uiptxpoll(struct uip_driver_s *dev)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
    int tail = e1000->tx_ring.tail;

    /* If the polling resulted in data that should be sent out on the network,
     * the field d_len is set to a value > 0.
     */

    if (e1000->uip_dev.d_len > 0) {
	uip_arp_out(&e1000->uip_dev);
	e1000_transmit(e1000);

	/* Check if there is room in the device to hold another packet. If not,
	 * return a non-zero value to terminate the poll.
	 */
	if (!e1000->tx_ring.desc[tail].desc_status)
	    return -1;
    }

    /* If zero is returned, the polling will continue until all connections have
     * been examined.
     */

    return 0;
}

/****************************************************************************
 * Function: e1000_receive
 *
 * Description:
 *   An interrupt was received indicating the availability of a new RX packet
 *
 * Parameters:
 *   e1000  - Reference to the driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   Global interrupts are disabled by interrupt handling logic.
 *
 ****************************************************************************/

static void e1000_receive(struct e1000_dev *e1000)
{
    int head = e1000->rx_ring.head;
    unsigned char *cp = (unsigned char *)
	(e1000->rx_ring.buf + head * CONFIG_E1000_BUFF_SIZE);
    int cnt;

    while (e1000->rx_ring.desc[head].desc_status) {

	/* Check for errors and update statistics */
	
	// Here we do not handle packets that exceed packet-buffer size
	if ((e1000->rx_ring.desc[head].desc_status & 3) == 1) {
	    cprintf("NIC READ: Oversized packet\n");
	    goto next;
	}
	
	/* Check if the packet is a valid size for the uIP buffer configuration */
	
	// get the number of actual data-bytes in this packet
	cnt = e1000->rx_ring.desc[head].packet_length;
	
	if (cnt > CONFIG_NET_BUFSIZE || cnt < 14) {
	    cprintf("NIC READ: invalid package size\n");
	    goto next;
	}
    
	/* Copy the data data from the hardware to e1000->uip_dev.d_buf.  Set
	 * amount of data in e1000->uip_dev.d_len
	 */
    
	// now we try to copy these data-bytes to the UIP buffer
	memcpy(e1000->uip_dev.d_buf, cp, cnt);
	e1000->uip_dev.d_len = cnt;

	/* We only accept IP packets of the configured type and ARP packets */

#ifdef CONFIG_NET_IPv6
	if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
#else
	if (BUF->type == HTONS(UIP_ETHTYPE_IP))
#endif
	{
	    uip_arp_ipin(&e1000->uip_dev);
	    uip_input(&e1000->uip_dev);

	    /* If the above function invocation resulted in data that should be
	     * sent out on the network, the field  d_len will set to a value > 0.
	     */

	    if (e1000->uip_dev.d_len > 0) {
		uip_arp_out(&e1000->uip_dev);
		e1000_transmit(e1000);
	    }
	}
	else if (BUF->type == htons(UIP_ETHTYPE_ARP)) {
	    uip_arp_arpin(&e1000->uip_dev);

	    /* If the above function invocation resulted in data that should be
	     * sent out on the network, the field  d_len will set to a value > 0.
	     */

	    if (e1000->uip_dev.d_len > 0) {
		e1000_transmit(e1000);
	    }
	}

    next:
	e1000->rx_ring.desc[head].desc_status = 0;
	e1000->rx_ring.head = (head + 1) % CONFIG_E1000_N_RX_DESC;
	e1000->rx_ring.free++;
	head = e1000->rx_ring.head;
	cp = (unsigned char *)(e1000->rx_ring.buf + head * CONFIG_E1000_BUFF_SIZE);
    }
}

/****************************************************************************
 * Function: e1000_txtimeout
 *
 * Description:
 *   Our TX watchdog timed out.  Called from the timer interrupt handler.
 *   The last TX never completed.  Reset the hardware and start again.
 *
 * Parameters:
 *   argc - The number of available arguments
 *   arg  - The first argument
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   Global interrupts are disabled by the watchdog logic.
 *
 ****************************************************************************/

static void e1000_txtimeout(int argc, uint32_t arg, ...)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)arg;

    /* Increment statistics and dump debug info */

    /* Then reset the hardware */
    e1000_init(e1000);

    /* Then poll uIP for new XMIT data */

    (void)uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
}

/****************************************************************************
 * Function: e1000_polltimer
 *
 * Description:
 *   Periodic timer handler.  Called from the timer interrupt handler.
 *
 * Parameters:
 *   argc - The number of available arguments
 *   arg  - The first argument
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   Global interrupts are disabled by the watchdog logic.
 *
 ****************************************************************************/

static void e1000_polltimer(int argc, uint32_t arg, ...)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)arg;
    int tail = e1000->tx_ring.tail;

    /* Check if there is room in the send another TX packet.  We cannot perform
     * the TX poll if he are unable to accept another packet for transmission.
     */
    if (!e1000->tx_ring.desc[tail].desc_status)
	return;

    /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
     * might be bug here.  Does this mean if there is a transmit in progress,
     * we will missing TCP time state updates?
     */

    (void)uip_timer(&e1000->uip_dev, e1000_uiptxpoll, E1000_POLLHSEC);

    /* Setup the watchdog poll timer again */

    (void)wd_start(e1000->txpoll, E1000_WDDELAY, e1000_polltimer, 1, arg);
}

/****************************************************************************
 * Function: e1000_ifup
 *
 * Description:
 *   NuttX Callback: Bring up the Ethernet interface when an IP address is
 *   provided 
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

static int e1000_ifup(struct uip_driver_s *dev)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;

    ndbg("Bringing up: %d.%d.%d.%d\n",
	 dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
	 (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );

    /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
    e1000_init(e1000);

    /* Set and activate a timer process */

    (void)wd_start(e1000->txpoll, E1000_WDDELAY, e1000_polltimer, 1, (uint32_t)e1000);

    if (e1000_inl(e1000, E1000_STATUS) & 2)
	e1000->bifup = true;
    else
	e1000->bifup = false;

    return OK;
}

/****************************************************************************
 * Function: e1000_ifdown
 *
 * Description:
 *   NuttX Callback: Stop the interface.
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

static int e1000_ifdown(struct uip_driver_s *dev)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
    irqstate_t flags;

    /* Disable the Ethernet interrupt */

    flags = irqsave();

    e1000_turn_off(e1000);

    /* Cancel the TX poll timer and TX timeout timers */

    wd_cancel(e1000->txpoll);
    wd_cancel(e1000->txtimeout);

    /* Put the the EMAC is its reset, non-operational state.  This should be
     * a known configuration that will guarantee the skel_ifup() always
     * successfully brings the interface back up.
     */
    //e1000_reset(e1000);

    /* Mark the device "down" */

    e1000->bifup = false;
    irqrestore(flags);

    return OK;
}

/****************************************************************************
 * Function: e1000_txavail
 *
 * Description:
 *   Driver callback invoked when new TX data is available.  This is a 
 *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
 *   latency.
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   Called in normal user mode
 *
 ****************************************************************************/

static int e1000_txavail(struct uip_driver_s *dev)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
    int tail = e1000->tx_ring.tail;
    irqstate_t flags;

    /* Disable interrupts because this function may be called from interrupt
     * level processing.
     */

    flags = irqsave();

    /* Ignore the notification if the interface is not yet up */

    if (e1000->bifup) {
	/* Check if there is room in the hardware to hold another outgoing packet. */
	if (e1000->tx_ring.desc[tail].desc_status)
	    (void)uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
    }

    irqrestore(flags);
    return OK;
}

/****************************************************************************
 * Function: e1000_addmac
 *
 * Description:
 *   NuttX Callback: Add the specified MAC address to the hardware multicast
 *   address filtering
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *   mac  - The MAC address to be added 
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef CONFIG_NET_IGMP
static int e1000_addmac(struct uip_driver_s *dev, const uint8_t *mac)
{
  struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;

  /* Add the MAC address to the hardware multicast routing table */

  return OK;
}
#endif

/****************************************************************************
 * Function: e1000_rmmac
 *
 * Description:
 *   NuttX Callback: Remove the specified MAC address from the hardware multicast
 *   address filtering
 *
 * Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *   mac  - The MAC address to be removed 
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *
 ****************************************************************************/

#ifdef CONFIG_NET_IGMP
static int e1000_rmmac(struct uip_driver_s *dev, const uint8_t *mac)
{
  struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;

  /* Add the MAC address to the hardware multicast routing table */

  return OK;
}
#endif

irqreturn_t  e1000_interrupt_handler(struct Trapframe *tf, void *dev_id)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)dev_id;
    
    /* Get and clear interrupt status bits */
    int intr_cause = e1000_inl(e1000, E1000_ICR);
    e1000_outl(e1000, E1000_ICR, intr_cause);

    // not for me
    if (intr_cause == 0) 
	return IRQ_NONE;

    /* Handle interrupts according to status bit settings */

    // Link status change
    if (intr_cause & (1<<2)) {
	if (e1000_inl(e1000, E1000_STATUS) & 2)
	    e1000->bifup = true;
	else
	    e1000->bifup = false;
    }
    
    /* Check if we received an incoming packet, if so, call skel_receive() */

    // Rx-descriptor Timer expired
    if (intr_cause & (1<<7))
	e1000_receive(e1000);

    // Tx queue empty
    if (intr_cause & (1<<1))
	wd_cancel(e1000->txtimeout);

    /* Check is a packet transmission just completed.  If so, call skel_txdone.
     * This may disable further Tx interrupts if there are no pending
     * tansmissions.
     */

    // Tx-descriptor Written back
    if (intr_cause & (1<<0))
	uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
  

    // Rx-Descriptors Low
    if (intr_cause & (1<<4)) {
	int tail;
	tail = e1000->rx_ring.tail + e1000->rx_ring.free;
	tail %= CONFIG_E1000_N_RX_DESC;
	e1000->rx_ring.tail = tail;
	e1000->rx_ring.free = 0;
	e1000_outl(e1000, E1000_RDT, tail);
    }

    return IRQ_HANDLED;
}

/******************************* PCI driver *********************************/

static pci_id_t e1000_id_table[] = {
    {.sep = {INTEL_VENDERID, E1000_82573L}},
    {.sep = {INTEL_VENDERID, E1000_82540EM}},
    {.sep = {INTEL_VENDERID, E1000_82574L}},
    {.sep = {INTEL_VENDERID, E1000_82567LM}},
    {.sep = {INTEL_VENDERID, E1000_82541PI}},
    {.sep = {0,0}}	
};

static int e1000_probe(uint16_t addr, pci_id_t id)
{
    uint32_t mmio_base, mmio_size;
    uint32_t pci_cmd, size;
patacongo's avatar
patacongo committed
    int err, irq, flags;
    void *kmem, *omem;
    struct e1000_dev *dev;

    // alloc e1000_dev memory
    dev = kzalloc(sizeof(struct e1000_dev));
    if (dev == NULL)
	return -1;

    // enable device
    err = pci_enable_device(addr, PCI_RESOURCE_MEM);
    if (err)
	goto error;

    // get e1000 device type
    dev->pci_dev_id = id.join;

    // remap the controller's i/o-memory into kernel's address-space
    mmio_base = pci_resource_start(addr, 0);
    mmio_size = pci_resource_len(addr, 0);
    err = rgmp_memmap_nocache(mmio_base, mmio_size, mmio_base);
    if (err) 
	goto error;
    dev->phy_mem_base = mmio_base;
    dev->io_mem_base = mmio_base;
    dev->mem_size = mmio_size;

    // make sure the controller's Bus Master capability is enabled
    pci_cmd = pci_config_readl(addr, PCI_COMMAND);
    pci_cmd |= (1<<2);
    pci_config_writel(addr, PCI_COMMAND, pci_cmd);

    // MAC address
    memset(dev->dst_mac, 0xFF, 6);
    memcpy(dev->src_mac, (void *)(dev->io_mem_base+E1000_RA), 6);

    // get e1000 IRQ
patacongo's avatar
patacongo committed
    flags = 0;
    irq = pci_enable_msi(addr);
    if (irq == 0) {
	irq = pci_read_irq(addr);
	flags |= IDC_SHARE;
    }
    dev->irq = irq;
    dev->int_desc.handler = e1000_interrupt_handler;
    dev->int_desc.dev_id = dev;
patacongo's avatar
patacongo committed
    err = rgmp_request_irq(irq, &dev->int_desc, flags);
    if (err)
	goto err0;

    // Here we alloc a big block of memory once and make it
    // aligned to page boundary and multiple of page size. This
    // is because the memory can be modified by E1000 DMA and
    // should be mapped no-cache which will hugely reduce memory 
    // access performance. The page size alloc will restrict
    // this bad effect only within the memory we alloc here.
    size = CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc) +
	   CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE +
	   CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc) + 
	   CONFIG_E1000_N_RX_DESC * CONFIG_E1000_BUFF_SIZE;
    size = ROUNDUP(size, PGSIZE);
    omem = kmem = memalign(PGSIZE, size);
    if (kmem == NULL) {
	err = -ENOMEM;
	goto err1;
    }
    rgmp_memremap_nocache((uintptr_t)kmem, size);

    // alloc memory for tx ring
    dev->tx_ring.desc = (struct tx_desc*)kmem;
    kmem += CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc);
    dev->tx_ring.buf = kmem;
    kmem += CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE;

    // alloc memory for rx rings
    dev->rx_ring.desc = (struct rx_desc*)kmem;
    kmem += CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc);
    dev->rx_ring.buf = kmem;

    /* Initialize the driver structure */

    dev->uip_dev.d_ifup    = e1000_ifup;     /* I/F up (new IP address) callback */
    dev->uip_dev.d_ifdown  = e1000_ifdown;   /* I/F down callback */
    dev->uip_dev.d_txavail = e1000_txavail;  /* New TX data callback */
#ifdef CONFIG_NET_IGMP
    dev->uip_dev.d_addmac  = e1000_addmac;   /* Add multicast MAC address */
    dev->uip_dev.d_rmmac   = e1000_rmmac;    /* Remove multicast MAC address */
#endif
    dev->uip_dev.d_private = dev;            /* Used to recover private state from dev */

    /* Create a watchdog for timing polling for and timing of transmisstions */

    dev->txpoll       = wd_create();         /* Create periodic poll timer */
    dev->txtimeout    = wd_create();         /* Create TX timeout timer */

    // Put the interface in the down state.
    // e1000 reset
    e1000_reset(dev);

    /* Read the MAC address from the hardware */
    memcpy(dev->uip_dev.d_mac.ether_addr_octet, (void *)(dev->io_mem_base+E1000_RA), 6);

    /* Register the device with the OS so that socket IOCTLs can be performed */
    err = netdev_register(&dev->uip_dev);
    if (err)
	goto err2;

    // insert into e1000_list
    dev->next = e1000_list.next;
    e1000_list.next = dev;
    cprintf("bring up e1000 device: %04x %08x\n", addr, id.join);