> ## 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:

> - 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 `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