User specific TCPIP Device driver - SC12 @CHIP-RTOS V1.10
IPC@CHIP Documentation Index
Introduction
Adding a user specific device driver/linklayer interface to the TCP/IP stack
The @CHIP-RTOS of the IPC@CHIP provides four internal TCP/IP device interfaces:
1. Ethernet controller
2. PPP server
3. PPP client (PPP uses one of the serial ports of the IPC@CHIP)
4. Internal loopback (Virtual loopback device with IP address 127.0.0.1)
For each of these internal devices the necessary specific driver functions are implemented inside of the @CHIP-RTOS.
The provided TCP/IP API calls 0xA0 - 0xA7 allow the application developer
to install an additional TCP/IP driver interface for a connected hardware device (e.g. an
additional UART or an Ethernet controller). This new interface then has its own IP configuration
and is used by the TCP/IP stack for IP communication in the same way as with the pre-installed
internal devices.
The following sections explain how to implement and install a user specific device driver for TCP/IP.
The generic example code shown here uses C-library functions provided for the TCP/IP API, which can
be found in source file TCPIP.C.
We are also using several functions from the C-library files HWAPI.C and RTOS.C.
The C-library files are available at
www.beck-ipc.com in the Internet download area of the IPC@CHIP.
All needed TCP/IP related types and constants are declared in the C-library header files
TCPIPAPI.H and TCPIP.H.
Important notes:
1. IP configuration of the user device is not adjustable with the settings in the IP section of chip.ini configuration file.
You can create your own section in chip.ini for storing IP configuration of the new device interface
with BIOSINT 0xA0 functions 0x23/0x24.
For possible IP configuration via the @CHIP-RTOS UDP config server see
Install UDP Config Server Callback.
2. Setting the default gateway (reachable via the installed interface) is now possible with
the expanded ADD_DEFAULT_GATEWAY API.
In the following sections we describe the set of driver specific functions you may have to provide.
Possible implementations of an interrupt service function and receiver task for the
device interface are provided.
These device driver functions must be installed with the API call
DEVOPENIFACE. These a callback functions
which are invoked by the internal TCP/IP stack of the @CHIP-RTOS. Do not call these
functions directly from within your application!
The driver functions are internally locked by semaphores and block every other device driver function.
Because of this behavior, it's not advisable to wait (sleep) for long periods of time as this can
lead to deadlock situations (primarily between the send and recv calls).
Device Open function
- The TCP/IP stack calls this (optional) function to initialize the hardware and (optional) to install an
Interrupt Service handler. If Borland C compilers are used the driver functions must be
declared as huge (see below).
Microsoft C users must declare driver functions as far _saveregs _loadds .
The function should return 0 if initialization was successful.
If initialization failed, return -1.
Generic Example:
| int huge myDevOpen(DevUserIfaceHandle ifaceHandle)
{
// Install (if necessary) a RTOS Interrupt Service function with
// HWAPI handler function 0xA1 service 0x84
return 0;
} |
Top of list Index page
Device Close function
- The TCP/IP stack will execute this (optional) function when the device driver
interface is closed with the
DEV_CLOSE_IFACE API call.
This callback function should return 0 if the closing of the device was
successful, otherwise -1 on failure.
Generic example:
| int huge myDevClose(DevUserIfaceHandle ifaceHandle)
{
// DeInitialize the hardware
// Remove the ISR handler
return 0;
} |
Top of list Index page
Device Send function
- This callback function is used by the TCP/IP stack to send the data out the device.
The TCP/IP stack does not call this function from within a separate transmit task.
This callback executes in the thread which made the send call, e.g.
API_SEND API.
This callback function should return 0 if the sending of data was successful,
otherwise -1 on error.
Important:
If the input parameter flag has value 1 (this indicates this
is last frame in block) you must call
DEV_SND_COMPLETE
to tell the TCP/IP stack that the send buffer is no longer in use.
Generic example:
| int huge myDevSend(DevUserIfaceHandle ifaceHandle,
unsigned char far * dataPtr,
int dataLength,
int flag )
{
int errorcode;
// Hardware specific: Send the data (dataPtr) out the device
// Do not wait(sleep) here for indefinite times,
// to avoid blocking of other function calls.
// Is this now the last frame in message block?
if (flag & 0x1) // Bit0 flag set?
{
// Inform TCP/IP stack that transmit buffer is now free.
Dev_Send_Complete(ifaceHandle, &errorcode); // C-Lib
}
return 0;
} |
Related Topics
-
- DEV_SND_COMPLETE API - Dev_Send_Complete's implementation
-
- DevUserIfaceHandle type definition
Top of list Index page
Device Receive function
In this function, a received packet is passed back up into the protocol stack.
The TCP/IP stack calls this function to receive a data frame from the device.
TCP/IP calls this function from within your separate receiver task, which you are
required to create (see final example).
The receive callback should return 0 if receiving of data was successful, otherwise
-1 on failure.
Important
:
It's optional but recommended to store incoming data in a buffer from the TCP/IP
pre-allocated memory pool (see TCPIPMEM).
API call DEV_GET_BUF returns you a
buffer pointer for storing the incoming data (see example below).
If you are using your own buffer allocation for storing the incoming data, you must
null out the location referenced by the input parameter bufferHandle
(see example). In this case, you should also implement and install the device
driver callback function:
int (far * DevFreeRecvFunc)(DevUserIfaceHandle ifaceHandle,
unsigned char far * dataPtr);
The TCP/IP stack calls this function to indicate that the receive buffer is no
longer used by TCP/IP. The vector to this callback is placed in the
DevFreeRecv member of the
DevUserDriver structure
at the DEV_OPEN_IFACE call.
Two generic examples for receiver functions follow:
myDevReceive1 : Using buffer from the TCP/IP memory pool
myDevReceive2 : Using your own receive buffer
| // Generic example using TCP/IP memory pool receive buffer:
int huge myDevReceive1(DevUserIfaceHandle ifaceHandle,
unsigned char far * far * dataPtr,
int far * dataLength,
DevUserBufferHandle bufferHandle)
{
int errorCode;
unsigned int rcvdLength;
unsigned char far *tcp_buffer ;
// Hardware specific: Check how many incoming data bytes are available.
rcvdLength = .....; // =byte count
// Get a buffer from TCP/IP by calling API service 0xA5 and save at dataPtr
Dev_Get_Buffer(bufferHandle, dataPtr, rcvdLength); // C-Lib call
tcp_buffer = *dataPtr ; // Check if memory allocation successful
if (tcp_buffer != (unsigned char far *)0)
{
// Hardware specific: Move received data from device to tcp_buffer
// Do not wait (sleep) here for indefinite times,
// to avoid blocking of other function calls.
*dataLength = recvdLength; // Report number of bytes now in tcp_buffer
return 0; // success
}
else
{
return -1; // out of memory
}
}
// Generic example for using your own receive buffer:
int huge myDevReceive2(DevUserIfaceHandle ifaceHandle,
unsigned char far * far * dataPtr,
int far * dataLength,
DevUserBufferHandle bufferHandle)
{
// Save the pointer to the beginning of the data
*dataPtr = myBuffer; // myBuffer somehow allocated by the user
// Hardware specific: Read data from your device and store in myBuffer
// Save the length (in bytes) of received data
*dataLength = deviceDataLength;
// IMPORTANT: Null out the bufferhandle pointer
*bufferHandle = (DevUserBuffer)0;
return 0;
} |
Related Topics
-
- DEV_GET_BUF API - Dev_Get_Buffer's implementation
-
- DevUserIfaceHandle type definition
Top of list Index page
Device FreeReceive function
- Implementation of this function is necessary if you decide to use your own buffers
for receiving incoming data.
The TCP/IP stack will call this function to inform you that the receive buffer
(input parameter dataPtr) is no longer used by TCP/IP.
This callback should return 0 if ok, else -1 on failure.
Generic example:
| int huge myDevFreeRecv(DevUserIfaceHandle ifaceHandle, unsigned char far *dataPtr )
{
// Somehow free your allocated buffer at dataPtr
my_free(dataPtr);
return 0;
} |
Related Topics
-
- DevUserIfaceHandle type definition
Top of list Index page
Device Get PhysicalAddress function
- This function applies only to Ethernet controllers.
The 6 byte array referenced by the PhysicalAddress input parameter
should be filled with the MAC address of your connected Ethernet controller.
Generic example:
| int huge myDevGetPhysAddr(DevUserIfaceHandle ifaceHandle,
unsigned char far * physicalAddress)
{
// Hardware specific: copy MAC address into physicalAddress
_fmemcpy(physicalAddress, myEthernet_MAC, 6) ;
return 0;
} |
Related Topics
-
- DevUserIfaceHandle type definition
Top of list Index page
Implementation of an interrupt service routine (ISR) and receiver task
- The implementation of a device specific ISR is optional. If your hardware device is able to generate interrupts on
device events (e.g. incoming data available), you can implement an ISR like the example below.
The CPU time spent within an ISR must be keep to a minimum, as the length of this interrupts
masked period impacts the interrupt latency of the other critical system ISR's. Consequently,
your ISR should only notify events (incoming data, error,..) at the device and not directly
handle device events itself (e.g. retrieve incoming data) immediately within the ISR.
With API call DEV_NOTIFY_ISR the ISR should wakeup
a user provided task, which receives the incoming data from the device and moves the data into the
TCP/IP stack. This task should use API call DEV_RECV_WAIT
and DEV_RECV_IFACE (see example below).
Instead of a creating a new task, it is also possible to use your program's main thread
for receiving by having it perform the MyReceiveTask() actions shown below.
If your device doesn't support interrupts, you could create a polling task (or again, simply
use your program's main thread for this purpose) which periodically
checks your device for incoming data as illustrated
in the MyReceiveTask_Polling example below.
Important
: An ISR must be installed as a RTOS ISR with the
Install Interrupt Service Routine
of the Hardware API.
Generic examples for an ISR and a two forms of receiver task functions follow.
| // Interrupt Service Routine
void interrupt MyDeviceISRHandler(void)
{
int receivedFrames;
int errorCode;
// Hardware specific: Check if there are incoming data packets available
// Wakeup receiver task
Dev_Notify_ISR(MyDevHandle, receivedFrames, 0, &errorCode); // C-Lib call
// Note: Issue no EOI here.
// (EOI for the ISR is issued inside of the CHIP-RTOS.)
}
// Generic example for a receiver task function, which waits for an event from ISR:
void huge MyReceiveTask(void)
{
int errorCode;
int statRecv;
// Optional: do some initialization
while(1)
{
// Wait for a wakeup from ISR
Dev_Recv_Wait(mydevdriver.IfaceHandle, &errorCode); // C-Lib call
// After wakeup received and move incoming data into the stack
do
{ // C-Lib call
statRecv = Dev_Recv_Interface(mydevdriver.IfaceHandle, &errorCode);
} while (statRecv != -1);
}
}
// Generic example for receiver task, polling for incoming data:
void huge MyReceiveTask_Polling(void)
{
int errorCode;
// Optional: do some initialization
// Wait for completion of interface installation (Intr 0xAC 0xA0)
while (install_done == 0)
{
RTX_Sleep_Time(10); // Go to sleep for a defined time.
}
while(1)
{
// Check if there is data available inside of your device.
if (myDeviceDataAvail())
{
// Receive and move incoming data into the stack
Dev_Recv_Interface(mydevdriver.IfaceHandle, &errorCode);
}
RTX_Sleep_Time(10); // Go to sleep for a defined time.
}
} |
Related Topics
-
- DEV_NOTIFY_ISR API - Dev_Notify_ISR's implementation
-
- DEV_RECV_WAIT API - Dev_Recv_Wait's implementation
-
- DEV_RECV_IFACE API - Dev_Recv_Interface's implementation
-
- RTX_SLEEP_TIME API - RTX_Sleep_Time's implementation
Top of list Index page
Install the device driver
- Based on the previous sections of this document, the following generic example
should make clear the main steps required to install a user implemented device driver:
| #include "tcpip.h"
#include "rtos.h"
int huge myDevOpen(DevUserIfaceHandle ifaceHandle);
int huge myDevClose(DevUserIfaceHandle ifaceHandle);
int huge myDevSend(DevUserIfaceHandle ifaceHandle,
unsigned char far * dataPtr,
int dataLength, int flag);
int huge myDevReceive1(DevUserIfaceHandle ifaceHandle,
unsigned char far * far * dataPtr,
int far * dataLength,
DevUserBufferHandle bufferHandle);
int huge myDevGetPhysAddr(DevUserIfaceHandle ifaceHandle,
unsigned char far * physicalAddress);
void interrupt MyDeviceISRHandler(void);
void huge MyReceiveTask(void);
unsigned int recvID; // task ID
unsigned int myrecv_stack[1024]; // stack for receiver task
unsigned char install_done = 0; // waiting flag for receiver task
unsigned int errorCode;
DevUserIfaceHandle MyDevHandle;
TaskDefBlock myrecv_defblock =
{
MyReceiveTask, // task function
{'D','E','V',' '}, // a name: 4 chars
&myrecv_stack[1024], // top of stack
1024*sizeof(int), // size of stack
0, // attributes, not supported
20, // priority 20(high) ... 127(low)
0, // no time slicing
0,0,0,0 // mailbox depth,
};
char far * mydevicename = "MyDev";
char far * IPString = "192.168.200.020";
char far * NetmaskString = "255.255.255.000";
DevUserDriver mydevdriver;
int main(void)
{
//***********************************************************************
// Initialize struct mydevdriver;
//***********************************************************************
mydevdriver.DevName = mydevicename; // Unique device name,
// max. 13 chars + 0.
inet_addr(IPString , &mydevdriver.IpAddr); // IP address
inet_addr(NetmaskString, &mydevdriver.Netmask); // Netmask
mydevdriver.iface_type = 1; // Ethernet device
mydevdriver.use_dhcp = 0; // no DHCP
//Important:
// At the first DEV_OPEN_IFACE call for a device, IfaceHandle must be NULL.
mydevdriver.IfaceHandle = 0 ;
// Note: If the interface should be restarted by calling DEV_CLOSE_IFACE
// and DEV_OPEN_IFACE (e.g. for changing IP configuration) the
// IfaceHandle must contain at DEV_OPEN_IFACE the valid IfaceHandle handle
// from the first DEV_OPEN_IFACE call.
// Install your driver functions
mydevdriver.DevOpen = (void far *)mydevOpen;
mydevdriver.DevOpen = (void far *)mydevClose;
mydevdriver.DevSend = (void far *)mydevSend;
mydevdriver.DevRecv = (void far *)myDevReceive1;
mydevdriver.DevFreeRecv = (void far *)0; // Since using TCP/IP buffers
mydevdriver.DevGetPhysAddr = (void far *)myDevGetPhysAddr;
mydevdriver.DevIoctl = (void far *)0; // Currently not supported,
// pass a Null pointer.
//***********************************************************************
// Install the device driver interface
//***********************************************************************
result = Dev_Open_Interface(&mydevdriver, &errorCode); // C-Lib
// if(result).....
//***********************************************************************
// Create the receiver task
//***********************************************************************
result = RTX_Create_Task(&recvID, &myrecv_defblock); // C-Lib
// if(result).....
// Optional, but recommended: Change priority of receiver task to high prio 4
RTX_Change_TaskPrio(recvID, 4, &errorCode);
//***********************************************************************
//If device interface should be configured by DHCP, wait for completion
//of the DHCP configuration process
//***********************************************************************
if (mydevdriver.use_dhcp == 1)
{
result= Dev_Wait_DHCP_Complete(&mydevdriver, 20, &errorCode); // C-Lib
// if(result)....
}
// Wait forever, or stay resident with int21h call 31h.
// It is possible to close and restart the interface inside of an application.
// But due to the internal architecture of the TCP/IP stack, it is not
// possible to exit the driver program and restart the interface with the
// same unique name.
// Your driver program should run forever. Avoid killing the receiver task.
while(1) RTX_Sleep_Time(100);
}// End of main(void) |
Related Topics
-
- API_INETADDR API - inet_addr's implementation
-
- DEV_OPEN_IFACE API - Dev_Open_Interface's implementation
-
- DEV_WAIT_DHCP_COMPLETE API - Dev_Wait_DHCP_Complete's implementation
-
- RTX_TASK_CREATE API - RTX_Create_Task's implementation
-
- RTX_SLEEP_TIME API - RTX_Sleep_Time's implementation
-
- DevUserDriver data structure type definition
Top of list Index page
End of document
|