PREV NEXT INDEX



Appendix A. Guidelines for Writing Custom Subsystems

Customized subsystems may be added to the DeviceMate software. This appendix discusses the general strategy used in writing the subsystems provided by Z-World.

A.1 Software Overview

The DeviceMate communicates with the target processor using thin layers of service calls. Each feature in the DeviceMate feature set is identified by a unique packet type and communicates by sending and receiving buffers to the same packet type on the other processor. The low-level Target Communications (TC) makes a best attempt to deliver the buffers intact, but the buffer is only passed to the destination subsystem if the checksum and packet format are correct.

A.1.1 Packet Type

Packet types 0 through 15 are reserved and should not be used. The following packet type values are pre-defined:


#define TC_TYPE_SYSTEM 0x00   // System messages
#define TC_TYPE_DEBUG  0x01   // Debugging messages
#define TC_TYPE_WD     0x02   // Watchdogs
#define TC_TYPE_TCPIP  0x03   // TCP/IP tunneling over TC/XTC
#define TC_TYPE_FS     0x04   // File system
#define TC_TYPE_VAR    0x05   // Web-page variable
#define TC_TYPE_LOG    0x06   // Logging

A.1.1.1 Configuration Macro

The macro TC_MAX_APPLICATIONS is the maximum number of subsystems (read: callbacks) that are supported. Therefore, the largest packet type value supported is TC_MAX_APPLICATIONS - 1. This macro defaults to 10.

A.2 Multitasking Environment

Subsystems must be safe in a preemptive multitasking environment such as µC/OS-II. This is done with a generic implementation of binary semaphores to serialize access, or the disabling of interrupts to enter critical sections of code.

These locking macros can be redefined by the user to match the target environment.

A.2.1 Locking Macros

In dm_app.lib, are the following macros:

TC_LOCKING

Locking is enabled if this macro is defined in the subsystem code. Locking is automatically defined when µC/OS-II is being used.

TC_LOCKTYPE

This macro specifies the data type of the lock. If it is not defined in the subsystem and µC/OS-II is being used, TC_LOCKTYPE will be defined as OS_EVENT*: a pointer able to access an OS_EVENT data structure that will identify a binary semaphore.

TC_CREATELOCK()

This macro takes no parameters. It returns a lock handle of type TC_LOCKTYPE. For µC/OS-II, this will use the function OSSemCreate(), with 1 as the parameter (the initial count of the semaphore).

TC_LOCK(lock)

This macro takes a lock handle of type TC_LOCKTYPE as a parameter. It will block until the given semaphore can be acquired (unless locking is not being used, in which case it will do nothing). Under µC/OS-II, this will wrap the function OSSemPend(), with the time-out as zero (infinity) and the error status being read into a dummy global variable.

TC_UNLOCK(lock)

This macro takes a lock handle of type TC_LOCKTYPE as a parameter. It will release the given semaphore (unless locking is not being used, in which case it will do nothing). Under µC/OS-II, this will wrap the function OSSemPost().

A.2.1.1 Initialization

The initialization function of a subsystem should have something like the following:


#ifdef TC_LOCKING
TC_LOCKTYPE _myapp_lock;
#endif

_myapp_init() {
// ...

#ifdef TC_LOCKING
_myapp_lock = TC_CREATELOCK();
#endif

// ...
}

A.2.1.2 µC/OS-II Support

The maximum number of events supported by µC/OS-II is defined by the macro OS_MAX_EVENTS. This macro includes the number of semaphores, message mailboxes and any message queues being used by µC/OS-II. Two must be added to OS_MAX_EVENTS for each of the DeviceMate subsystems that are used; also, one must be added if any of the included subsystems use XTC.

In the subsystem code, follow the define of OS_MAX_EVENTS with a #use "ucos2.lib" statement. This ensures that the definition supplied outside of the library is used, rather than the default in the library. Also the inclusion of ucos2.lib must happen before the inclusion of any other libraries. For more information on the Dynamic C implementation of µC/OS-II please see the Dynamic C User's Manual.

The µC/OS-II lock function, OSSemPend(), takes as a parameter a pointer to an error status integer. This is a global dummy variable in dm_app.lib.

A.2.1.3 Locking Use

In general, the easiest way to make a subsystem safe for preemptive multitasking environments is to lock the tick and API functions on entry, and unlock them on exit. This way will be safe unless:

By default, the lock and unlock macros will expand to nothing.

If MCOS is defined (which it is if you are using µC/OS-II in Dynamic C), then the lock and unlock macros will expand to a default set of macros that use the native µC/OS-II semaphores. Note that these macros will provide binary semaphores only! If you expect to be able to lock multiple times from the same task and have it succeed, then you will have unpleasant surprises!

A.2.2 Critical Sections

A critical section of code is a part of the program that accesses shared data.

The critical section macros are: TC_ENTER_CRITICAL() and TC_EXIT_CRITICAL(). They take no parameters. Under µC/OS-II they map to OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL().

They should be used with caution as they disable interrupts.

A.2.3 Data Flow

The following diagram shows normal data flow between DeviceMate and target subsystems.

Figure 1. Data Flow Initiated by Rabbit Target

Typically, the target subsystem kicks things off by queuing a buffer to be sent to the DeviceMate. After the buffer has been transmitted, it is returned to the target subsystem by the subsystem-supplied callback. On the other side, the DeviceMate begins receiving the data into a DeviceMate-supplied buffer that was previously registered by a call to targetproc_recvbuf(). After the data has been completely received and verified, the callback supplied by the DeviceMate subsystem is called on the buffer.

This also works in the opposite direction, with the subsystem on the DeviceMate queuing a buffer to send to the target by calling targetproc_send() and the target receiving the data via the callback using the buffer that was previously registered by a call to devmate_recvbuf().

A.3 The Callback Function

Each of the subsystems, on both the target and the DeviceMate, will have a callback function associated with it that must be defined as follows:


int callback (uint16 flags, uint8 type, unint8 subtype, uint16 length, uint32 buffer, uint32 userdata);

Parameters

flags

A bitfield with the following values:

#define TC_RECEIVE       0x0001
#define TC_TXDONE        0x0002
#define TC_UNSUPPORTED   0x0004
#define TC_NOBUFFER      0x0008
#define TC_RESET         0x0010
#define TC_SYSBUF        0x8000

TC_RECEIVE and TC_TXDONE inform the subsystem what type of callback was made. If TC_RECEIVE is set, it is a receive callback: meaning that the packet in buffer is valid and should be handled quickly. It TC_TXDONE is set, it is a transmit callback and the packet in buffer was sent successfully.

If TC_UNSUPPORTED is set, the other side has not registered a callback for the requested type.

If TC_NOBUFFER is set, the other side has a registered callback, but has no free receive buffers at the moment. This implies that the packet referenced in the last TC_TXDONE callback was not received successfully.

If TC_RESET is set, it is requested that the subsystem reset itself to an initial state.

If TC_SYSBUF is set, the buffer given in the callback is a system buffer and will be returned automatically once the callback is finished.

type

Identifies the subsystem with a unique value.

subtype

On a receive callback, this value is the subtype of the packet. On txdone, this parameter is undefined.

length

On a receive callback, this is the length of the received data in the buffer. On txdone, this is undefined.

buffer

The physical address of the beginning of the buffer's header. Note that on a receive callback, the user data starts sizeof(_TCHeader) bytes after the location pointed to by buffer.

userdata

The data that the subsystem specified in devmate_send() or devmate_recvbuf().

Return Value

The callback function should return 0 for now--other return codes may be defined later.

A.3.1 Callback Registration

At startup, each subsystem must register their callback function with the following API call:


int devmate_registercallback ( uint8 type, uint16 (*call_back)());

devmate_registercallback() will return TC_SUCCESS or TC_ERROR. Passing a NULL value as the function pointer will un-register the callback and shut down handling of the subsystem identified by type.

Subsystems that use XTC do not call devmate_registercallback() since XTC does it for them.

A.4 Buffer Management

There are two general classes of buffers. One set is owned by the system. These system buffers are used to notify a subsystem (running on either the target or DeviceMate) that there is a problem transmitting the data. The subsystem should copy out any important values and then return from the callback function as quickly as possible.

A.4.1 Subsystem Buffers

The second set of buffers is owned by the subsystems. When a subsystem on DeviceMate wants to send data, it places the data in a buffer and queues it to be sent by calling targetproc_send(). A subsystem running on the target would call devmate_send().

Before a subsystem can receive data from the target handler it must register the buffer by calling devmate_recvbuf(). This must be done for each receive buffer that will be used.

Subsystems running on DeviceMate register buffers with the DeviceMate handler.

A.4.2 Queue and Buffer Routines

These routines will be used by subsystems that do not use XTC services.


_tc_get_buffer



extern faraddr _tc_get_buffer(faraddr_t *chain);

Description

Removes a buffer from the specified chain, and returns the long pointer to it, or 0 if there was no buffer available on that chain.

Parameters

chain

Identifies the buffer queue.

Return Value

0: The queue is empty
long *: Address of the buffer that was removed from the queue

Library

tc.lib


_tc_queue_buffer



extern int _tc_queue_buffer(faraddr_t *chain, faraddr_t buffer);

Description

This function adds a buffer to the end of a queue.

Parameters

chain

A pointer to the queue where you want to add the buffer

buffer

Address of the buffer to add to the queue

Return Value

0: For all cases

Library

tc.lib


_tc_create_queue



int _tc_create_queue(faraddr_t* chain, faraddr_t buffer, long bufsize, int number);

Description

This function creates a new queue and may add buffers to it.

Parameters

chain

The pointer to the queue

buffer

The address of the first buffer to add to the newly created queue.

bufsize

The number of bytes in the buffers that will be added to the queue

number

The number of buffers to add to the queue

Return Value

 0: The queue is empty
!0: Pointer to last buffer in the queue

Library

tc.lib


_tc_empty



int _tc_queue_empty(faraddr_t* chain);

Description

This function checks the queue to see if anything is there.

Parameters

chain

Pointer to a queue

Return Value

 0: The queue is empty
!0: The queue is not empty

Library

tc.lib

A.5 Transmitting Packets API

A valid callback function, that can deal with TC_TXDONE callbacks, must be registered for the subsystem before calling devmate_send().


devmate_send



int devmate_send(char type, char subtype, int length, long buffer, long userdata);

Description

This function will queue a packet for transmission. If the function returns success, the specified buffer should not be used until the TC_TXDONE callback is issued for it.

Parameters

type

The subsystem type of this packet. This type must have a valid callback function registered or an error will result.

subtype

The subtype of this packet

length

The length of the user data in the packet. Note that this does not include any headers in the buffer.

buffer

The physical address of the buffer to send. This is the beginning of the buffer, not the user data area. The user data should start sizeof(_TCHeader) bytes after this physical address.

userdata

This is an area where a subsystem can store any useful data. This area is stored in the buffer's header when devmate_send() is called, and is returned in the callback.

Return Value

TC_SUCCESS: Packet was successfully queued
TC_ERROR: General error
TC_NOCALLBACK: No callback function was registered for this subsystem

Library

dm_app.lib

A.6 Receiving Packets API

A valid callback function, that can deal with TC_RECEIVE callbacks, must be registered for the subsystem before calling devmate_recvbuf().


devmate_recvbuf



int devmate_recvbuf(char type, int length, long buffer, long userdata);

Description

This function adds the given buffer to the end of type's receive queue. Note that when a TC_RECEIVE callback gives a packet to the subsystem, that buffer has been removed from the receive queue. To add the buffer back to the receive queue so that another packet can be received into it, call devmate_recvbuf().

Parameters

type

The subsystem type of this packet.

length

The length of the user area in the buffer. This does NOT include the sizeof(_TCHeader) bytes at the beginning of the buffer. If zero is specified as the length, no data will be saved on the incoming packets, and the callback will only specify the subtype and such. No matther

buffer

The physical address of the buffer to receive into. This is the beginning of the buffer, not the user data area. The user data should start sizeof(_TCHeader) bytes after this physical address.

userdata

This is an area where a subsystem can store any useful data. This area is stored in the buffer's header when devmate_recvbuf() is called, and is returned in the callback.

Return Value

TC_SUCCESS: Packet was successfully queued
TC_ERROR: General error
TC_NOCALLBACK: No callback function was registered for this subsystem

Library

dm_app.lib


Z-World
http://www.zworld.com
Voice: (530) 757-3737
FAX: (530) 757-3792
sales@zworld.com
PREV NEXT INDEX