hpoj reference: PTAL API

This document explains the C-language application programming interface to PTAL, the Peripheral Transport Abstraction Library, which is one of the major components of the hpoj software.

See the Device protocols page for background information.

You need to include the line "#include <ptal.h>" in your program to include the necessary #defines, typedefs, and function prototypes.

Macros and other #defines

Unless otherwise specified, functions with an int return type return the symbolic constant PTAL_ERROR (-1) on failure. On success, they either return PTAL_OK (0), or any integer value other than PTAL_ERROR, depending on the function. Functions with a pointer return type return NULL (zero) on error, although NULL is not defined in ptal.h.

For your convenience, the following macros are provided for converting between native integers and shorts/longs stored in "unsigned char" arrays as big-endian or little-endian:

The "s" parameter is the character array. The "GET" macros return the native integer value, and the "SET" macros take the native integer value as the "x" parameter.


The following opaque pointer types are used:

Logging and debugging

The following macros may be called for error-reporting or debugging purposes: The variable argument list is the same as is passed to the standard printf() function.

What actually gets logged (to standard error) depends on the setting of the PTAL_DEBUG environment variable. Setting it to -1 or lower disables all logging, setting it to 0 (or not setting it at all) logs only errors, setting it to 1 logs errors and warnings, and setting it to 2 or higher logs everything.

Initialization and deinitialization

int ptalInit(void);
int ptalDone(void);
Call ptalInit() when your application starts up before using PTAL; this reads from /etc/ptal information on devices which have been registed using "ptal-init setup". Call ptalDone() when finished with PTAL before exiting.

Opening, closing, and deleting devices

ptalDevice_t ptalDeviceOpen(char *name);
int ptalDeviceClose(ptalDevice_t dev);
int ptalDeviceDelete(ptalDevice_t dev);
char *ptalDeviceGetName(ptalDevice_t dev);
Given a PTAL device name string, such as "mlc:usb:OfficeJet_G85", ptalDeviceOpen() creates if necessary and returns a ptalDevice_t pointer, which you pass to other functions that need to refer to that device. If you pass a NULL device-name pointer, then the default device is returned. If the return value was NULL, then the specified or default device is not valid.

ptalDeviceClose() closes/deallocates all channels and PML objects associated with the device, but leaves the ptalDevice_t pointer intact. On the other hand, ptalDeviceDelete() also deletes the ptalDevice_t pointer.

ptalDeviceGetName() returns a pointer to the PTAL device name string associated with the given device. Don't modify the string!

Device ID string retrieval and parsing

int ptalDeviceGetDeviceIDString(ptalDevice_t dev,char *buffer,int maxlen);
int ptalDeviceGetPreviousDeviceIDString(ptalDevice_t dev,
    char *buffer,int maxlen);
ptalDeviceGetDeviceIDString() attempts to retrieve the (null-terminated) device ID string for the given device into buffer, up to maxlen characters. ptalDeviceGetPreviousDeviceIDString() is almost the same, except it attempts to retrieve the previous (as opposed to current) device ID string. If successful, then you can pass buffer as the first parameter to the following parse functions:

int ptalDeviceIDGetField(char *_devID,char *field,
    char **pValue,int *pLenValue);
int ptalDeviceIDGetEitherField(char *devID,char *field1,char *field2,
    char **pValue,int *pLenValue);
void ptalDeviceIDPruneField(char **pValue,int *pLenValue);
ptalDeviceIDGetField() and ptalDeviceIDGetEitherField() search for a specific field (or either of two fields). If successful, *pValue is set to the start of the field, and *pLenValue is set to the field length, including the leading field name and colon and the trailing semicolon (if any). ptalDeviceIDPruneField() alters the field start and length to "prune" the leading and trailing stuff, leaving you with just the value part of the field between the colon and semicolon.

The following functions may be called to search for specific common fields:

int ptalDeviceIDGetManufacturer(char *devID,char **pValue,int *pLenValue);
int ptalDeviceIDGetModel(char *devID,char **pValue,int *pLenValue);
int ptalDeviceIDGetCommandSet(char *devID,char **pValue,int *pLenValue);
int ptalDeviceIDGetSerialNumber(char *devID,char **pValue,int *pLenValue);

Channel allocate and deallocate functions

ptalChannel_t ptalChannelAllocate(ptalDevice_t dev);
ptalChannel_t ptalChannelFindOrAllocate(ptalDevice_t dev,
    int serviceType,int socketID,char *serviceName);
int ptalChannelDeallocate(ptalChannel_t chan);
ptalChannelAllocate() creates and returns a new ptalChannel_t pointer, which you pass to other functions that need to refer to that channel.

ptalChannelFindOrAllocate() searches for a channel that has already been allocated on the given device and set to the given remote service parameters (see ptalChannelSetRemoteService() below). If found, it returns the previously-allocated channel, but if not found, then it allocates it and sets the given remote service parameters.

ptalChannelDeallocate() closes (if necessary) and deallocates the channel pointer.

Channel setup functions

int ptalChannelGetRemoteService(ptalChannel_t chan,
    int *pServiceType,int *pSocketID,char **pServiceName);
int ptalChannelSetRemoteService(ptalChannel_t chan,
    int serviceType,int socketID,char *serviceName);
ptalChannelSetRemoteService() specifies what socket/service to connect to on the peripheral. The serviceType parameter may be one of the following:
int ptalChannelSetPacketSizes(ptalChannel_t chan,
    int desiredHPSize,int desiredPHSize);
int ptalChannelAdjustPacketSizes(ptalChannel_t chan,
    int *pDesiredHPSize,int *pDesiredPHSize);
ptalChannelSetPacketSizes() requests the given host-to-peripheral and peripheral-to-host packet sizes, not including any added protocol overhead, such as the MLC/1284.4 6-byte header. ptalChannelAdjustPacketSizes() may be called after opening the channel, passing pointers to the same requested packet sizes which were passed to ptalChannelSetPacketSizes(). If either actual packet size ended up being lower than the requested packet size, then the value is reduced accordingly. Note that it's not guaranteed that your requested packet sizes will be honored or that it's possible to even determine the actual packet sizes.

int ptalChannelSetErrorHandling(ptalChannel_t chan,
    int retryCount,int retryDelay);
ptalChannelSetErrorHandling() sets retryCount and retryDelay (in seconds) that may be used to retry certain types of open failures, depending on the particular provider. A negative retryCount means retry forever.

Channel open and close functions

int ptalChannelIsOpen(ptalChannel_t chan);
int ptalChannelOpen(ptalChannel_t chan);
int ptalChannelOpenOrReopen(ptalChannel_t chan);
int ptalChannelClose(ptalChannel_t chan);
ptalChannelIsOpen() returns nonzero if the channel is already open or zero if not.

ptalChannelOpen() attempts to open the channel if it's not already open. ptalChannelOpenOrReopen() also first closes the channel if it happens to be "stale" (closed remotely, see ptalChannelIsStale() below).

ptalChannelClose() closes the channel if it is currently open.

Channel data transfer functions

int ptalFdPrepareForSelect(int fd,int *pn,
    fd_set *prset,fd_set *pwset,fd_set *pxset);
int ptalChannelPrepareForSelect(ptalChannel_t chan,
    int *pfd,int *pn,fd_set *prset,fd_set *pwset,fd_set *pxset);
int ptalChannelSelect(ptalChannel_t chan,int *pr,int *pw,int *px,
    struct timeval *timeout);
ptalFdPrepareForSelect() and ptalChannelPrepareForSelect() take an arbitrary file descriptor or a channel, respectively, and add the (corresponding) file descriptor to each of the file descriptor sets with a non-NULL pointer. In addition, the *pn parameter is set to the maximum of the current value or the file descriptor if pn is non-NULL. For ptalChannelPrepareForSelect(), *pfd is set to the corresponding file descriptor if pfd is non-NULL.

ptalChannelSelect() is a somewhat simpler select() wrapper for a single channel. Instead of file descriptor sets, the "int *pX" parameters may be set to nonzero if select() should check for read/write/exception activity, or zero (or NULL pointer) if not; the resulting values of these variables are nonzero if the channel is ready for the corresponding activity and zero if not. As with select(), the "struct timeval *timeout" parameter is NULL for an infinite timeout.

int ptalChannelRead(ptalChannel_t chan,char *buffer,int count);
int ptalChannelWrite(ptalChannel_t chan,char *buffer,int count);
ptalChannelRead() and ptalChannelWrite() read/write data on the channel, generally using standard read() and write() semantics. They return the number of bytes read or written, or PTAL_ERROR on an error.

int ptalChannelIsStale(ptalChannel_t chan);
int ptalChannelReadTimeout(ptalChannel_t chan,char *buffer,int countdown,
    struct timeval *startTimeout,struct timeval *continueTimeout);
int ptalChannelFlush(ptalChannel_t chan,
    struct timeval *startTimeout,struct timeval *continueTimeout);
ptalChannelIsStale() returns nonzero if the channel is "stale", or in other words, has been closed remotely. It may consume a byte of data, so don't call it if you intend to read afterwards data that may be waiting.

ptalChannelReadTimeout() and ptalChannelFlush() read data in a loop until the given timeout runs out, and so are less sensitive to the "partial read" phenomenon that you may experience with ptalChannelRead() due to packet boundaries on the underlying link. ptalChannelReadTimeout() reads into the buffer a maximum of countdown bytes, and ptalChannelFlush() discards all data received within the given timeout constraints. startTimeout is the timeout for reading the first packet of data, and continueTimeout is the timeout for reading successive packets of data.

PML open and close functions

int ptalPmlOpen(ptalDevice_t dev);
int ptalPmlClose(ptalDevice_t dev);
ptalPmlOpen() is called to open the PML channel before you use it, and ptalPmlClose() is called to close the PML channel when you're done.

PML object allocate and deallocate functions

ptalPmlObject_t ptalPmlAllocate(ptalDevice_t dev);
ptalPmlObject_t ptalPmlAllocateID(ptalDevice_t dev,char *oid);
int ptalPmlDeallocate(ptalPmlObject_t obj);
int ptalPmlDeallocateAll(ptalDevice_t dev);
ptalPmlAllocate() allocates and returns a pointer to a new PML object tied to the specified device. ptalPmlAllocateID() also initializes the object ID to the given binary object ID (see ptalPmlSetID() below).

ptalPmlDeallocate() deallocates the previously-allocated PML object, and ptalPmlDeallocateAll() deallocates all PML objects allocated on the given device.

PML object ID functions

int ptalPmlSetID(ptalPmlObject_t obj,char *oid);
int ptalPmlSetAsciiID(ptalPmlObject_t obj,char *s);
int ptalPmlGetID(ptalPmlObject_t obj,char *buffer,int maxlen);
ptalPmlSetID() sets the given binary object ID, and ptalPmlSetAsciiID() sets the given ASCII (dotted-decimal) object ID. ptalPmlGetID() retrieves the object ID in binary form into the given buffer up to maxlen bytes.

For example, to set the object ID "", you could call one of the following functions:

	ptalPmlSetID(obj,"\x1\x2\x2\x1\xC");        /* binary */
	ptalPmlSetAsciiID(obj,"");        /* ASCII */

PML data types

The following symbolic constants are defined for PML data types.

"Basically" integer types:

"Basically" character-array types:

PML SET functions

To set a PML object on the peripheral, you must:
  1. Allocate a ptalPmlObject_t and set the object ID, if you haven't already.
  2. Set the value into the ptalPmlObject_t.
  3. Call ptalPmlRequestSet() to request the SET operation.
  4. Check the status (see below).
int ptalPmlSetValue(ptalPmlObject_t obj,int type,char *value,int len);
int ptalPmlSetStringValue(ptalPmlObject_t obj,int symbolSet,
    char *value,int len);
int ptalPmlSetIntegerValue(ptalPmlObject_t obj,int type,int value);

int ptalPmlRequestSet(ptalPmlObject_t obj);
ptalPmlSetValue() sets a PTAL_PML_TYPE_BINARY value.

ptalPmlSetStringValue() sets a PTAL_PML_TYPE_STRING value. It's probably safe to specify zero for the symbol set, or you can pass one of PTAL_PML_SYMSET_0E, PTAL_PML_SYMSET_ROMAN8, or another constant defined in ptal.h. Pass zero for the len parameter if the string is null-terminated.


PML GET functions

To get a PML object on the peripheral, you must:
  1. Allocate a ptalPmlObject_t and set the object ID, if you haven't already.
  2. Call ptalPmlRequestGet() to request the GET operation.
  3. Check the status (see below).
  4. Get the value out of the ptalPmlObject_t if the operation was successful.
int ptalPmlRequestGet(ptalPmlObject_t obj,ptalPmlObject_t next);

int ptalPmlGetValue(ptalPmlObject_t obj,int *pType,char *buffer,int maxlen);
int ptalPmlGetStringValue(ptalPmlObject_t obj,int *pSymbolSet,
    char *buffer,int maxlen);
int ptalPmlGetIntegerValue(ptalPmlObject_t obj,int *pType,int *pValue);
int ptalPmlDoLastValuesDiffer(ptalPmlObject_t obj);
ptalPmlGetValue() gets the binary value for any type, which probably is not what you want for any type other than binary. However, it can be a useful starting point for determine what data type the value is.

ptalPmlGetStringValue() and ptalPmlGetIntegerValue() get string and integer data types, respectively. They succeed even if the actual value has an incompatible data type, but the result may not be meaningful.

ptalPmlDoLastValuesDiffer() returns nonzero if the last two cached values of the PML object differ, for example, between two GETs.

Checking status from a PML GET or SET

int ptalPmlGetStatus(ptalPmlObject_t obj);
ptalPmlGetStatus() returns that PML status code from the last ptalPmlRequestSet() or ptalPmlRequestGet() call. The following successful status values are defined: The following unsuccessful status values are defined: