USB Ethernet Adapter for Windows - Based on the LPC2148 Education Board

 

Final Project for Embedded Systems Course at Tel Aviv University

 

Ido Kasher & Ron Eyal

 

Winter Semester 2009/2010

---

Description

 

The purpose of the project is to implement an Ethernet adapter over USB for Windows. An LPC2148 microcontroller will connect to a host computer (Running Microsoft Windows) via USB, and will identify itself as an Ethernet adapter (According to RNDIS specification, to allow the host to load an existing generic network driver). On the other end, it will connect over an SPI interface to an Ethernet controller (Which implements both the MAC and PHY layers of the Ethernet standard). The microcontroller will run an application that configures each interface, receives packets on one interface and sends it over the other while buffering the packets as necessary, adding and removing the necessary headers, etc.

 

Hardware

·         LPC2148 Education Board – connected to the host computer via USB on two interfaces.

o   USB interface – for acting as an Ethernet adapter and transferring data over the network.

o   UART interface – for downloading the firmware and printing debug traces to the host.

·         ENC28J60 Ethernet Controller – connected to the LPC board via the board's expansion pins and to the network via an Ethernet cable.

 

 

IMG_4628

Figure 1. LPC2148 Education Board (On the left, connected to a USB cable)

connected via the expansion pins to the ENC28J60 Ethernet Controller

(On the right, connected to an Ethernet cable).                                                              

 

Software

The firmware consists of several software modules:

·         USB driver providing APIs to handle the USB.

·         High-level USB driver handling the RNDIS logical interface.

·         Ethernet driver providing APIs to handle the Ethernet controller.

·         Transport module that handles the buffers for Tx/Rx packets.

·         Main module that controls the chip and the two interfaces.

 

In addition we used drivers for the UART and SPI interfaces.

 

Transport module (transfer.c)

This module is in charge of defining and maintaining cyclic packet queues which we use for receiving, buffering and transmitting data on both interfaces. We use an Rx queue for packets received by the Ethernet adapter and sent to the USB interface, and a Tx queue for the opposite direction.

Each queue is implemented as a cyclic buffer of a constant length and two pointers, pointing to the start and end of the valid data. When adding a packet to the queue, the data is written at the end pointer position, updating the end pointer to the new end. When reading data and removing a packet from the queue, data is read from the head pointer position, which is then advanced to the start of the next packet. The RNDIS protocol header of each packet is utilized to describe the length of each packet.

The packets are written to the buffer in a cyclic manner – when the end of the buffer is reached the remaining data is written to the start of the buffer (making sure not to overrun the start of the valid data).

 

Ethernet Module (enc28j60.c / enc28j60.h)

Our Ethernet module is based on code that we received from Michael from our course, which is in turn based on an open source code (see appendix B for license info).

The module uses the SPI interface to communicate with the ENC28J60 Ethernet expansion board.

This module contains both a low level driver to control the Ethernet expansion board, such as initialization and reading/writing commands and buffers of data, and a high level driver. The high level driver uses the low level functions for commands such as getting / setting the MAC address and sending and receiving packets. We rewrote the send/receive packet functions to support our packet queues mechanism instead of sending a simple (non-cyclic) buffer.

When receiving a packet from the Ethernet interface, we attach an RNDIS header to it and insert it to the Rx queue. When retrieving a packet from the Tx queue, we remove the RNDIS header before sending it over the Ethernet.

 

USB Module

We used a low level USB driver taken from the labs in the course (usb.c / usb.h).

The higher level functionality of the RNDIS protocol utilizing the USB communication was implemented in rndis.c.

This module implements the transfer of packets over the USB interface, in 64 byte chunks. When sending a packet to the host, it is taken directly from the Rx queue, and after all the chunks are sent, the start pointer of the queue is updated. When receiving a packet from the host, each chunk is copied to a buffer, and when the entire packet is received, it is inserted in the queue.

Sending and receiving chunks is done in interrupt context when receiving an interrupt on the appropriate endpoint.

 

Main flow of operation

·         The ENC28J60 Ethernet Controller was connected to the LPC2148 Board's expansion pins. Four of the pins were used as an SPI bus to connect the Ethernet controller with the main board. A fifth pin was used as a reset pin for the Ethernet Controller.

·         The device declares itself as a Remote NDIS device, so Windows can load the generic RNDIS driver upon detecting it.

·         The main control loop (main.c) first initializes the microcontroller and drivers:

o   Clocks (Both PCLK and CCLK are set at 60MHz)

o   GPIOs to control the debug LEDs

o   VIC for handling the interrupts

o   UART for allowing to print debug messages

o   USB

o   Ethernet

 

It then turns to run the main control loop:

o   Rx path

·         Receive a packet over the Ethernet interface (if there is a packet ready)

o   Tx path

·         Add packets received over the USB interface to the Tx queue (if the packet has been received in full)

·         Send a packet over the Ethernet interface (if there is at least one in the queue)

 

Sending packets to the host over the USB interface is done in interrupt context, when the relevant endpoint handler is called.

 

 

Development Process

 

Ethernet: We first concentrated on the Ethernet interface, trying to communicate with the Ethernet expansion board by toggling its LEDs, setting and getting the MAC address and transmitting ARP packets. At this phase we connected the device in a loopback manner (Only the serial port interface of the USB was used – for downloading the firmware and receiving the debug messages over the UART. The device was connected the computer via an Ethernet interface), and tried to sniff the ARP packets generated by the device and received by the host on its native Ethernet card.

We ran into many problems at this stage because the different source codes we were trying to use were developed for other versions of the LPC board, and had minor misalignments with our board – for example, the IO pins used for the SPI were different. After we had tried to fix all the misalignments, we still could not communicate with the Ethernet expansion board. At this point we consulted with Michael from our course, who was working on a similar project. We looked at his code and noticed that the main difference was that his code holds down the ENC28J60 hard reset pin while initializing the SPI interface. When we tried to do this, all of a sudden things started working!

 

USB (Enumeration): Our next effort was to communicate over the USB interface, identifying ourselves as an Ethernet adapter to the operating system and communicating using the RNDIS protocol. We've passed the enumeration process immediately, and after pointing the New Hardware Found wizard to the relevant .inf file, the device was added to the Device Manager. Next, we went on to implement the RNDIS protocol.

 

USB (Remote NDIS): At first, we failed to initialize the RNDIS stack. We received the RNDIS Initialize message and replied it, but the communication went no further, failing to receive the next RNDIS message (RNDIS Query message). We've used a USB sniffer to debug the RNDIS initialization process. This proved us that we indeed send the reply to the initialization message as intended. We figured that there must be something in the reply that the host RNDIS's stack dislike. We've then used a Windows kernel debugger (Following closely after the assembler instructions, as the sources of the drivers were unavailable to us) to try and understand what happens after the host receives the response to the initialization message, and what causes it to take the branch that fails the initialization process rather than continuing it. Before we had a chance to go deeper in that direction, we've tried some more different responses (There was a contradiction in the newly published RNDIS specification that we've used, regarding the length of the message and its fields), and fortunately, one worked. From that moment on, the rest of the RNDIS protocol went smoothly, and the device received and replies to RNDIS messages (And NDIS OIDs). See appendix A for snapshots of the USB sniffer (For one of the failed attempt, where the length of the RNDIS Initialize Complete message is 48 bytes instead of 52).

 

Data communication: After implementing both interfaces, it seemed that everything was supposed to work, yet we still could only send packets to the network, and packets that were received over the Ethernet interface were sent to the host, but were filtered by the OS. We could still see the packets using WireShark so we knew that we were transferring them correctly.

After a long debug process that did not lead to great results, we tried changing the MAC address just for the heck of it. Until that point we had been using a "bogus" MAC address (01:02:03:04:05:06)… When we changed it to an address that was similar to a real MAC address of another Ethernet card (00:1C:XX…), the OS stopped filtering our packets and we were able to obtain an IP address from the router and pass pings to Google! After consulting with our professor, Sivan Toledo, we came to the conclusion that when the first byte of the MAC address is odd, it is a multicast address. When we changed it to an even number, it made the difference J

After improving our transfer module to use packet queues, we were able to actually do some surfing to sites like Google and YNET. Below you can see a few DNS, TCP and HTTP packets from our first session:

 

Source Code

Our source code can be downloaded from here as a visual studio solution.

The attached rndis.inf file is used for the loading the relevant RNDIS drivers when the device is attached to the computer.

 

Appendix A: USB sniffer logs

 

·         Enumeration Overview

 

 

 

·         Get Device Descriptors - Request

 

 

 

·         Get Device Descriptors – Response

 

 

 

 

·         RNDIS Initialize Message

 

 

 

·         RNDIS Message Ready Notification followed by RNDIS Initialization Complete Message - Request

 

 

 

·         RNDIS Initialization Complete Message – Response

 

 

 

Appendix B: Licenses

We are attaching the licenses of the drivers that we used. Our source code is provided under these licenses. 

USB code:

  LPCUSB, an USB device driver for LPC microcontrollers

  Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)

 

  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. The name of the author may not be used to endorse or promote products

  derived from this software without specific prior written permission.

 

  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.

 

  Modified by Sivan Toledo.

 

ENC28J60 driver:

* Copyright (c) 2009, Manish Shakya,Real Time Solutions Pvt. Ltd.

 * 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 of the Institute 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 INSTITUTE 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 INSTITUTE 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.

 *

 *

 */

/**

 

Based upon

 

Advairtizer V1.0

www.braintechnology.de

*/

 

RNDIS code:

*

             LUFA Library

     Copyright (C) Dean Camera, 2009.

             

  dean [at] fourwalledcubicle [dot] com

      www.fourwalledcubicle.com

*/

 

/*

  Copyright 2009  Dean Camera (dean [at] fourwalledcubicle [dot] com)

 

  Permission to use, copy, modify, and distribute this software

  and its documentation for any purpose and without fee is hereby

  granted, provided that the above copyright notice appear in all

  copies and that both that the copyright notice and this

  permission notice and warranty disclaimer appear in supporting

  documentation, and that the name of the author not be used in

  advertising or publicity pertaining to distribution of the

  software without specific, written prior permission.

 

  The author disclaim all warranties with regard to this

  software, including all implied warranties of merchantability

  and fitness.  In no event shall the author be liable for any

  special, indirect or consequential damages or any damages

  whatsoever resulting from loss of use, data or profits, whether

  in an action of contract, negligence or other tortious action,

  arising out of or in connection with the use or performance of

  this software.

*/

 

---

 

Thanks

 

We would like to thank Prof. Sivan Toledo for guiding us in this project

Thanks also to Michael Gendelman from the embedded systems course for helping us debug the Ethernet code!