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 // LoggingA.1.1.1 Configuration Macro
The macro
TC_MAX_APPLICATIONSis the maximum number of subsystems (read: callbacks) that are supported. Therefore, the largest packet type value supported isTC_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_LOCKTYPEwill be defined asOS_EVENT*: a pointer able to access anOS_EVENTdata 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 functionOSSemCreate(), with 1 as the parameter (the initial count of the semaphore).TC_LOCK(lock)
This macro takes a lock handle of type
TC_LOCKTYPEas 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 functionOSSemPend(), 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_LOCKTYPEas 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 functionOSSemPost().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 toOS_MAX_EVENTSfor 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_EVENTSwith 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 ofucos2.libmust 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 indm_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:
- the tick function is accessible from something other than the tick function chain (such as from one of the API functions)
- one of the API functions calls another of the API functions
- some other obscure way that is probably really, really bad
By default, the lock and unlock macros will expand to nothing.
If
MCOSis 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()andTC_EXIT_CRITICAL(). They take no parameters. Under µC/OS-II they map toOS_ENTER_CRITICAL()andOS_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.
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 todevmate_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_RECEIVEandTC_TXDONEinform the subsystem what type of callback was made. IfTC_RECEIVEis set, it is a receive callback: meaning that the packet inbufferis valid and should be handled quickly. ItTC_TXDONEis set, it is a transmit callback and the packet inbufferwas sent successfully.If
TC_UNSUPPORTEDis set, the other side has not registered a callback for the requested type.If
TC_NOBUFFERis 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 lastTC_TXDONEcallback was not received successfully.If
TC_RESETis set, it is requested that the subsystem reset itself to an initial state.If
TC_SYSBUFis 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 bybuffer.userdata
The data that the subsystem specified in
devmate_send()ordevmate_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 returnTC_SUCCESSorTC_ERROR. Passing aNULLvalue as the function pointer will un-register the callback and shut down handling of the subsystem identified bytype.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 calldevmate_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
Return Value
0: The queue is emptylong *: Address of the buffer that was removed from the queueLibrary
_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
Library
_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
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 queueLibrary
_tc_empty
int _tc_queue_empty(faraddr_t* chain);Description
This function checks the queue to see if anything is there.
Parameters
chain
Return Value
0: The queue is empty!0: The queue is not emptyLibrary
A.5 Transmitting Packets API
A valid callback function, that can deal with
TC_TXDONEcallbacks, must be registered for the subsystem before callingdevmate_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_TXDONEcallback 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
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 queuedTC_ERROR: General errorTC_NOCALLBACK: No callback function was registered for this subsystemLibrary
A.6 Receiving Packets API
A valid callback function, that can deal with
TC_RECEIVEcallbacks, must be registered for the subsystem before callingdevmate_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 aTC_RECEIVEcallback 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, calldevmate_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 mattherbuffer
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 queuedTC_ERROR: General errorTC_NOCALLBACK: No callback function was registered for this subsystemLibrary
| Z-World http://www.zworld.com Voice: (530) 757-3737 FAX: (530) 757-3792 sales@zworld.com |