modemu2k 0.2.4
Library that provides telnet capability to a comm program
Loading...
Searching...
No Matches
modemu2k.h File Reference

Public API for the modemu2k PTY-based modem emulator library. More...

#include <poll.h>
#include <stddef.h>
#include "modemu2k_version.h"

Go to the source code of this file.

Macros

#define M2K_API
 Public-symbol visibility/export marker.
#define M2K_DEPRECATED(msg)
#define M2K_ERROR_BUFFER_SIZE   256
#define M2K_MAX_POLLFDS   3

Typedefs

typedef struct m2k_s m2k_t
 Opaque modem emulator context. Create with m2k_new(), destroy with m2k_free().
typedef void(* m2k_log_fn) (const char *msg, void *userdata)
 Log callback type.

Enumerations

enum  m2k_log_level_t {
  M2K_LOG_ERROR = 0 , M2K_LOG_WARN , M2K_LOG_INFO , M2K_LOG_DEBUG ,
  M2K_LOG_TRACE
}
 Log severity levels, ordered from most to least severe. More...
enum  m2k_err_t {
  M2K_OK = 0 , M2K_ERR_NOMEM , M2K_ERR_PTY , M2K_ERR_SOCKET ,
  M2K_ERR_TIMEOUT , M2K_ERR_CANCELED , M2K_ERR_BUG , M2K_ERR_WOULDBLOCK ,
  M2K_ERR_AT
}
 Return codes used by all m2k_* functions. More...

Functions

M2K_API const char * m2k_version (void)
 Runtime version string of the linked libmodemu2k.
M2K_API m2k_tm2k_new (void)
 Allocate and initialise a new modem context.
M2K_API void m2k_free (m2k_t *ctx)
 Release all resources held by ctx.
M2K_API void m2k_set_log_fn (m2k_t *ctx, m2k_log_fn fn, void *userdata)
 Install a log callback.
M2K_API void m2k_set_error_buffer (m2k_t *ctx, char *buf, size_t size)
 Install a buffer that receives a detailed message for the most recent error.
M2K_API m2k_err_t m2k_atcmd (m2k_t *ctx, const char *cmd)
 Feed a Hayes AT command string to the modem.
M2K_API M2K_DEPRECATED ("use m2k_run() or the step API instead") m2k_err_t m2k_dial(m2k_t *ctx
 Open a TCP connection to host : port.
M2K_API m2k_err_t m2k_hangup (m2k_t *ctx)
 Hang up: abandon whatever connection activity is in progress.
M2K_API m2k_err_t m2k_escape (m2k_t *ctx)
 Request an immediate return to command mode from online mode.
M2K_API const char * m2k_strerror (m2k_err_t err)
 Return a human-readable string for err.
M2K_API m2k_err_t m2k_setup_stdin (m2k_t *ctx)
 Use stdin/stdout as the TTY (standalone mode).
M2K_API m2k_err_t m2k_setup_pty (m2k_t *ctx, const char **slave_out)
 Allocate a PTY master and return the slave device path.
M2K_API m2k_err_t m2k_setup_comm_program (m2k_t *ctx, const char *cmd)
 Allocate a PTY and fork/exec a comm program on the slave.
M2K_API m2k_err_t m2k_setup_dev (m2k_t *ctx, const char *dev)
 Open an existing PTY device as the TTY.
M2K_API m2k_err_t m2k_setup_listen (m2k_t *ctx, const char *port)
 Bind a TCP listening socket on port.
M2K_API m2k_err_t m2k_setup_app_io (m2k_t *ctx)
 Embed mode: no real TTY fd; the host application supplies the bytes a TTY would normally produce, via m2k_write_from_app(), and consumes the bytes modemu2k would normally send to the TTY, via m2k_read_to_app().
M2K_API m2k_err_t m2k_write_from_app (m2k_t *ctx, const void *buf, size_t len, size_t *consumed)
 Push bytes into the modem as if they had been read from the TTY.
M2K_API m2k_err_t m2k_read_to_app (m2k_t *ctx, void *buf, size_t max, size_t *len_out)
 Drain bytes from the modem that would normally have been written to the TTY.
M2K_API int m2k_has_pending_output (const m2k_t *ctx)
 Test whether the modem has TTY-bound bytes still buffered.
M2K_API int m2k_get_listen_fd (const m2k_t *ctx)
 Expose the listening socket's fd (after m2k_setup_listen, before m2k_listen_accept).
M2K_API m2k_err_t m2k_listen_accept (m2k_t *ctx)
 Accept a single incoming connection on the listening socket opened by m2k_setup_listen() and adopt it as the TTY.
M2K_API m2k_err_t m2k_setup_answer (m2k_t *ctx, const char *port)
 Bind the line-side "answer" listener on port.
M2K_API int m2k_get_answer_fd (const m2k_t *ctx)
 Expose the answer listener's fd (after m2k_setup_answer()).
M2K_API m2k_err_t m2k_run (m2k_t *ctx)
 Run the modem command/online loop until the PTY closes.
M2K_API m2k_err_t m2k_get_pollfds (m2k_t *ctx, struct pollfd *fds, size_t *nfds_inout, int *timeout_ms)
 Describe the fds and timeout the caller's event loop should watch.
M2K_API m2k_err_t m2k_step (m2k_t *ctx, struct pollfd *fds, size_t nfds)
 Run one non-blocking iteration of the state machine.
M2K_API int m2k_run_done (const m2k_t *ctx)
 Test whether the session has ended.
M2K_API int m2k_is_online (const m2k_t *ctx)
 Test whether the modem is currently in online mode.
M2K_API int m2k_describe_state (const m2k_t *ctx, char *buf, size_t cap)
 Write a human-readable snapshot of the modem's state into buf.
M2K_API int m2k_has_carrier (const m2k_t *ctx)
 Test whether the modem has an active carrier (live TCP socket).
M2K_API void m2k_set_dtr (m2k_t *ctx, int on)
 Set the host's DTR (Data Terminal Ready) signal state.
M2K_API void m2k_set_rts (m2k_t *ctx, int on)
 Set the host's RTS (Request to Send) signal state.
M2K_API int m2k_get_dtr (const m2k_t *ctx)
M2K_API int m2k_get_rts (const m2k_t *ctx)
M2K_API void m2k_set_force_verbose (m2k_t *ctx, int on)
 Bypass the ATV verbose category mask.
M2K_API int m2k_get_force_verbose (const m2k_t *ctx)
M2K_API void m2k_set_log_level (m2k_t *ctx, m2k_log_level_t level)
 Set the severity threshold for log delivery.
M2K_API m2k_log_level_t m2k_get_log_level (const m2k_t *ctx)

Variables

M2K_API const char * host
M2K_API const char const char * port

Detailed Description

Public API for the modemu2k PTY-based modem emulator library.

Typical usage

The pattern used by the bundled modemu2k executable (see src/main.c) shows the recommended way to use the library:

m2k_t *ctx = m2k_new();
if (!ctx) return EXIT_FAILURE;
// Choose one setup mode:
m2k_setup_stdin(ctx); // read/write stdin/stdout
// m2k_setup_comm_program(ctx, "minicom -p %s"); // fork a comm program on a PTY
// m2k_setup_listen(ctx, "5000"); // bind a TCP port, then
// m2k_listen_accept(ctx); // wait for a client to connect
// m2k_setup_app_io(ctx); // or: host supplies TTY bytes itself
// Optional, combines with any mode: answer incoming "calls" (RING/ATA/S0)
// m2k_setup_answer(ctx, "2400");
// Optional: apply AT commands before entering the command loop
m2k_atcmd(ctx, "ATS7=30");
// Run the full command/online state machine (blocks until session ends)
m2k_run(ctx);
m2k_free(ctx);
M2K_API m2k_err_t m2k_run(m2k_t *ctx)
Run the modem command/online loop until the PTY closes.
M2K_API void m2k_free(m2k_t *ctx)
Release all resources held by ctx.
M2K_API m2k_err_t m2k_setup_stdin(m2k_t *ctx)
Use stdin/stdout as the TTY (standalone mode).
struct m2k_s m2k_t
Opaque modem emulator context. Create with m2k_new(), destroy with m2k_free().
Definition modemu2k.h:99
M2K_API m2k_err_t m2k_atcmd(m2k_t *ctx, const char *cmd)
Feed a Hayes AT command string to the modem.
M2K_API m2k_t * m2k_new(void)
Allocate and initialise a new modem context.

For finer control — driving the state machine yourself rather than letting m2k_run() block — replace m2k_run() with a poll loop built around m2k_get_pollfds() + m2k_step(), exiting when m2k_run_done() returns nonzero. Dialing from code is done by feeding the ATD string through the CMD-mode line buffer (write to the TTY in standalone mode, or m2k_write_from_app() in embed mode); m2k_step() then walks the DIAL → ONLINE transition. m2k_escape() and m2k_hangup() drive the online→cmd and disconnect transitions programmatically.

Macro Definition Documentation

◆ M2K_API

#define M2K_API

Public-symbol visibility/export marker.

On platforms where it matters (Windows DLLs, ELF builds with -fvisibility=hidden) this macro decorates every documented public function below. Defaults to nothing on Unix-default builds, where symbols are visible already. Define M2K_BUILDING_DLL before including this header inside the modemu2k source tree on Windows to flip the dllimport/dllexport direction; consumers leave it undefined.

Borrowed from libuv (UV_EXTERN) / libcurl (CURL_EXTERN) / libssh2 (LIBSSH2_API).

◆ M2K_ERROR_BUFFER_SIZE

#define M2K_ERROR_BUFFER_SIZE   256

Recommended minimum size for the m2k_set_error_buffer() buffer.

◆ M2K_MAX_POLLFDS

#define M2K_MAX_POLLFDS   3

Maximum number of pollfds modemu2k will ever ask the caller to watch.

Typedef Documentation

◆ m2k_log_fn

typedef void(* m2k_log_fn) (const char *msg, void *userdata)

Log callback type.

Parameters
msgNUL-terminated log message.
userdataCaller-supplied pointer passed unchanged from m2k_set_log_fn().

Enumeration Type Documentation

◆ m2k_err_t

enum m2k_err_t

Return codes used by all m2k_* functions.

Values are stable across releases: new codes are appended and existing values are never reordered, so callers can safely cache the numbers.

Enumerator
M2K_OK 

Success.

M2K_ERR_NOMEM 

Memory allocation failed.

M2K_ERR_PTY 

PTY open or allocation failed.

M2K_ERR_SOCKET 

TCP connect or I/O error.

M2K_ERR_TIMEOUT 

Operation timed out. (Reserved; not currently returned — dial timeouts surface as NO CARRIER through the step machine.)

M2K_ERR_CANCELED 

Operation canceled (e.g., +++ escape sequence).

M2K_ERR_BUG 

Internal assertion failure — should not happen.

M2K_ERR_WOULDBLOCK 

Operation would block; call m2k_step() (or wait for the relevant fd to become ready) and retry. Mirrors libssh2's LIBSSH2_ERROR_EAGAIN.

M2K_ERR_AT 

AT command rejected by the lexer (malformed input).

◆ m2k_log_level_t

Log severity levels, ordered from most to least severe.

Every message the library emits carries one of these levels; only messages at or above the threshold set by m2k_set_log_level() reach the log callback (or stderr). DEBUG opens the state-machine and telnet-negotiation narration; TRACE additionally opens byte-level buffer traffic. The Hayes-side ATV mask can still open individual DEBUG/TRACE categories independently of the level.

Since
0.2.3
Enumerator
M2K_LOG_ERROR 

Failures: syscall errors, rejected commands.

M2K_LOG_WARN 

Abnormal but survivable: peer resets, overruns.

M2K_LOG_INFO 

Notable lifecycle events (default threshold).

M2K_LOG_DEBUG 

State transitions, AT dispatch, telnet options.

M2K_LOG_TRACE 

Byte-level buffer traffic. Very noisy.

Function Documentation

◆ m2k_atcmd()

M2K_API m2k_err_t m2k_atcmd ( m2k_t * ctx,
const char * cmd )

Feed a Hayes AT command string to the modem.

Intended for one-shot configuration commands (S-register sets, mode toggles, etc.). Action commands like ATD/ATO/ATA are recognised by the lexer but do not drive a connection from this entry point; feed them through the CMD-mode line buffer instead (see

Returns
below).
Parameters
ctxModem context.
cmdNUL-terminated AT command (e.g. "ATZ" or "ATS0=1"). A string with no AT prefix is silently ignored.
Returns
M2K_OK on successful execution or no-op (missing AT prefix); M2K_ERR_AT if the lexer rejected the command as malformed, or if the command was an action verb (ATD, ATO, or ATA) — those drive a connection and so are not actionable from this one-shot configuration entry. To dial, answer, or resume online mode from code, feed the command string through the CMD-mode line buffer (m2k_write_from_app() in embed mode, or just write to the TTY in standalone mode) and let m2k_step() dispatch it. M2K_ERR_BUG only for unexpected lexer states.
m2k_atcmd(ctx, "ATZ"); // reset to defaults
m2k_atcmd(ctx, "ATS0=1"); // auto-answer on first ring
Examples
main.c.

◆ M2K_DEPRECATED()

M2K_API M2K_DEPRECATED ( "use m2k_run() or the step API instead" )

Open a TCP connection to host : port.

Enter online mode and relay data between the PTY and the socket.

Deprecated
Slated for removal in 0.3.0. Drive the state machine via m2k_run() or the step API (m2k_get_pollfds + m2k_step) and let the AT lexer dispatch the dial.

Transitions the modem from idle to connected state. Call m2k_online() after this to start relaying data.

Parameters
ctxModem context.
hostHostname or numeric IP address.
portService name or decimal port number.
Returns
M2K_OK on success, M2K_ERR_SOCKET on failure.
/* ATS7 sets the dial timeout (seconds); AT%D1 disables non-TTY dial
cancel so a stray stdin byte can't abort the dial. */
static int run_dial(m2k_t *ctx, const char *host, const char *port)
{
m2k_atcmd(ctx, "ATS7=20");
m2k_atcmd(ctx, "AT%D1");
m2k_err_t err = m2k_dial(ctx, host, port);
if (err != M2K_OK) {
fprintf(stderr, "dial: %s\n", m2k_strerror(err));
return 1;
}
sleep(1);
m2k_hangup(ctx);
return 0;
}
Deprecated
Slated for removal in 0.3.0. Use m2k_run() or the step API (m2k_get_pollfds + m2k_step) instead — they share one state-machine implementation with the rest of the library.

Blocks until the connection closes or the +++ escape sequence is detected. Returns M2K_ERR_CANCELED when the caller escapes back to command mode.

Parameters
ctxModem context (must have an active connection from m2k_dial()).
Returns
M2K_OK when the remote end closes, M2K_ERR_CANCELED on +++ escape.

◆ m2k_describe_state()

M2K_API int m2k_describe_state ( const m2k_t * ctx,
char * buf,
size_t cap )

Write a human-readable snapshot of the modem's state into buf.

Four newline-separated lines covering the step-machine state, carrier, TTY fds and buffer fill, socket and buffer fill, listeners, ring count, dial progress, control lines, and verbose mask. Intended for bug reports and host-side debug dumps; the exact format may evolve between versions and is not a parsing API.

snprintf contract: returns the length the full description requires (excluding the terminating NUL) regardless of cap; writes at most cap - 1 bytes plus a NUL. cap == 0 (with buf NULL or not) is a legal size probe.

Parameters
ctxModem context.
bufDestination buffer (may be NULL when cap is 0).
capCapacity of buf in bytes.
Returns
Length of the full description, excluding the NUL.
char state[512];
m2k_describe_state(ctx, state, sizeof state);
fprintf(stderr, "modem state:\n%s\n", state); /* paste into bug reports */
Since
0.2.3

◆ m2k_escape()

M2K_API m2k_err_t m2k_escape ( m2k_t * ctx)

Request an immediate return to command mode from online mode.

Drives the same transition that the +++ escape sequence would — but without the silence-guard timing or sending the literal +++ bytes through the I/O path. Useful from a host application that embeds modemu2k and wants programmatic control of the mode.

The transition takes effect on the next m2k_step() / m2k_run() iteration. The underlying TCP connection is left intact; use m2k_hangup() afterward if you also want to drop it.

A no-op when the context is not currently in online mode.

Parameters
ctxModem context.
Returns
M2K_OK.

◆ m2k_free()

M2K_API void m2k_free ( m2k_t * ctx)

Release all resources held by ctx.

Parameters
ctxContext to destroy. Safe to call with NULL.
Examples
main.c.

◆ m2k_get_answer_fd()

M2K_API int m2k_get_answer_fd ( const m2k_t * ctx)

Expose the answer listener's fd (after m2k_setup_answer()).

Lets an embedding host poll for the incoming-call event itself (the RING indication also flows through the TTY output).

Parameters
ctxModem context.
Returns
The bound answer listener fd, or -1 when none is bound.
Since
0.2.3

◆ m2k_get_dtr()

M2K_API int m2k_get_dtr ( const m2k_t * ctx)

Return the most recent value passed to m2k_set_dtr() (1 by default).

◆ m2k_get_force_verbose()

M2K_API int m2k_get_force_verbose ( const m2k_t * ctx)

Return the most recent value passed to m2k_set_force_verbose() (0 by default).

Since
0.2.2

◆ m2k_get_listen_fd()

M2K_API int m2k_get_listen_fd ( const m2k_t * ctx)

Expose the listening socket's fd (after m2k_setup_listen, before m2k_listen_accept).

Lets a host event loop poll the fd for POLLIN — i.e. detect an incoming TCP connection (the "RING" event in modem terms) so it can call m2k_listen_accept() at the right moment instead of blocking unconditionally on it.

Parameters
ctxModem context.
Returns
The fd of a bound-but-not-yet-accepted listener, or -1 when no listener is open (either never set up, or already accepted).

◆ m2k_get_log_level()

M2K_API m2k_log_level_t m2k_get_log_level ( const m2k_t * ctx)

Return the current log level threshold (M2K_LOG_INFO by default).

Since
0.2.3

◆ m2k_get_pollfds()

M2K_API m2k_err_t m2k_get_pollfds ( m2k_t * ctx,
struct pollfd * fds,
size_t * nfds_inout,
int * timeout_ms )

Describe the fds and timeout the caller's event loop should watch.

Fills fds with the fds modemu2k currently wants to monitor and stores the longest acceptable wait in *timeout_ms (-1 = wait indefinitely, 0 = poll immediately). Each pollfd's .fd and .events are set; the caller is responsible for the .revents field after calling poll().

Parameters
ctxModem context.
fdsCaller-provided array of at least M2K_MAX_POLLFDS entries.
nfds_inoutIn: capacity of fds (must be >= M2K_MAX_POLLFDS). Out: number of entries actually populated. May be zero while the session is still alive (app-IO command mode with no listener: the host's own fds drive progress) — use m2k_run_done() to decide when to exit the loop.
timeout_msOut: maximum wait in milliseconds before m2k_step() needs to be called again. -1 means no deadline.
Returns
M2K_OK on success, M2K_ERR_BUG if *nfds_inout is too small.

◆ m2k_get_rts()

M2K_API int m2k_get_rts ( const m2k_t * ctx)

Return the most recent value passed to m2k_set_rts() (1 by default).

◆ m2k_hangup()

M2K_API m2k_err_t m2k_hangup ( m2k_t * ctx)

Hang up: abandon whatever connection activity is in progress.

Covers all three connection phases: aborts a dial in progress (M2K_STATE_DIAL), cancels an ATA/auto-answer wait (M2K_STATE_ANSWER), or tears down a live TCP connection. In each case the modem emits NO CARRIER and returns to command mode. A no-op when idle. The answer listener from m2k_setup_answer() stays bound.

Parameters
ctxModem context.
Returns
M2K_OK (always).

◆ m2k_has_carrier()

M2K_API int m2k_has_carrier ( const m2k_t * ctx)

Test whether the modem has an active carrier (live TCP socket).

Maps to the DCD (Data Carrier Detect) signal real serial modems expose. Stays true after a +++ escape back to command mode as long as the TCP connection is still up; goes false on hangup, NO CARRIER, or before the first dial.

Parameters
ctxModem context.
Returns
Nonzero when a connection is alive, zero otherwise.

◆ m2k_has_pending_output()

M2K_API int m2k_has_pending_output ( const m2k_t * ctx)

Test whether the modem has TTY-bound bytes still buffered.

Returns nonzero whenever a subsequent m2k_read_to_app() would deliver at least one byte without first needing another m2k_step()/poll cycle. See m2k_read_to_app() for the back-pressure pattern this predicate exists to support.

Always returns 0 when the context isn't in app-I/O mode.

Parameters
ctxModem context.
Returns
Nonzero if buffered output is pending, zero otherwise.

◆ m2k_is_online()

M2K_API int m2k_is_online ( const m2k_t * ctx)

Test whether the modem is currently in online mode.

Useful for embed-mode hosts that paint mode-specific UI (status bar, local echo toggle, etc.) and need to know whether m2k is relaying bytes to/from a connected socket vs. consuming AT commands.

Parameters
ctxModem context.
Returns
Nonzero in online mode, zero in command mode or after the session has ended.

◆ m2k_listen_accept()

M2K_API m2k_err_t m2k_listen_accept ( m2k_t * ctx)

Accept a single incoming connection on the listening socket opened by m2k_setup_listen() and adopt it as the TTY.

Blocks until a client connects. The listening socket is closed once a connection is accepted (modemu2k handles one client per session). Call before m2k_run().

Parameters
ctxModem context (must have an active listener from m2k_setup_listen()).
Returns
M2K_OK on success, M2K_ERR_SOCKET on accept failure (including interruption by a signal) or if no listener has been set up. On failure the listener is closed regardless; a fresh m2k_setup_listen() is required to retry.
Examples
main.c.

◆ m2k_new()

M2K_API m2k_t * m2k_new ( void )

Allocate and initialise a new modem context.

Returns
Newly allocated context, or NULL on memory exhaustion.
Examples
main.c.

◆ m2k_read_to_app()

M2K_API m2k_err_t m2k_read_to_app ( m2k_t * ctx,
void * buf,
size_t max,
size_t * len_out )

Drain bytes from the modem that would normally have been written to the TTY.

Only valid after m2k_setup_app_io(). Copies up to max bytes from the internal TTY write buffer into buf and stores the actual length in *len_out (which may be zero if there is nothing pending).

Back-pressure shape
After m2k_step() processes an incoming socket burst, the internal TTY-bound buffer may hold more bytes than a single read_to_app call can return. The host must not block in poll() while bytes remain pending — none of modemu2k's fds will fire to wake it. Use one of:
  • Loop on read_to_app until m2k_has_pending_output() returns 0; or
  • On a partial drain (got < max OR you only wanted a small chunk), check m2k_has_pending_output() and use a zero timeout on the next poll() so the next read_to_app runs immediately.

Skipping this collapses throughput to roughly "your buffer size per poll timeout."

Parameters
ctxModem context.
bufDestination buffer. Must be non-NULL when max > 0.
maxMaximum bytes to copy. May be 0.
len_outOut: number of bytes actually copied (0 means no data). Must be non-NULL.
Returns
M2K_OK on success, M2K_ERR_PTY (overloaded as "wrong I/O mode") if the context is not in app-I/O mode — call m2k_setup_app_io() first.

◆ m2k_run()

M2K_API m2k_err_t m2k_run ( m2k_t * ctx)

Run the modem command/online loop until the PTY closes.

Handles the full state machine: reads Hayes AT commands in command mode, dials on ATD, relays data in online mode, and returns to command mode on +++ escape or disconnection. Call one of the m2k_setup_*() functions before calling m2k_run().

Equivalent (modulo poll-error handling) to:

while (!m2k_run_done(ctx)) {
struct pollfd fds[M2K_MAX_POLLFDS];
size_t nfds = M2K_MAX_POLLFDS;
int timeout_ms;
m2k_get_pollfds(ctx, fds, &nfds, &timeout_ms);
poll(fds, nfds, timeout_ms);
m2k_step(ctx, fds, nfds);
}
M2K_API int m2k_run_done(const m2k_t *ctx)
Test whether the session has ended.
#define M2K_MAX_POLLFDS
Definition modemu2k.h:614
M2K_API m2k_err_t m2k_step(m2k_t *ctx, struct pollfd *fds, size_t nfds)
Run one non-blocking iteration of the state machine.
M2K_API m2k_err_t m2k_get_pollfds(m2k_t *ctx, struct pollfd *fds, size_t *nfds_inout, int *timeout_ms)
Describe the fds and timeout the caller's event loop should watch.
Parameters
ctxModem context.
Returns
M2K_OK when the session ends normally; otherwise the first non-OK code from m2k_get_pollfds()/m2k_step() (realistically only M2K_ERR_BUG).
Examples
main.c.

◆ m2k_run_done()

M2K_API int m2k_run_done ( const m2k_t * ctx)

Test whether the session has ended.

Parameters
ctxModem context.
Returns
Nonzero when the state machine has reached a terminal state (PTY closed, etc.) and the caller's loop should stop.

◆ m2k_set_dtr()

M2K_API void m2k_set_dtr ( m2k_t * ctx,
int on )

Set the host's DTR (Data Terminal Ready) signal state.

On the 1→0 transition while a connection is live, modemu2k hangs the connection up — matching the typical real-modem AT&D2 default of "DTR drop terminates the call". Drop is otherwise a no-op (no carrier to drop). The DTR &D register is not (yet) lexable from AT command strings, so this function is the sole way to drive DTR-related behavior. Default state is asserted (1).

Parameters
ctxModem context.
onNon-zero to assert DTR, zero to drop it.

◆ m2k_set_error_buffer()

M2K_API void m2k_set_error_buffer ( m2k_t * ctx,
char * buf,
size_t size )

Install a buffer that receives a detailed message for the most recent error.

When buf is non-NULL, internal error paths write a human-readable, contextual message into buf (NUL-terminated, truncated to fit) in addition to any logging via m2k_set_log_fn(). The caller can then present the message after a function returns a non-OK m2k_err_t — curl-style CURLOPT_ERRORBUFFER semantics.

The buffer is only updated when an error actually occurs; successful calls leave it untouched, so callers should check the function's return code first.

Parameters
ctxModem context.
bufCaller-owned buffer that outlives the context, or NULL to detach. Recommended size: M2K_ERROR_BUFFER_SIZE.
sizeCapacity of buf in bytes (including the trailing NUL). Ignored when buf is NULL. Passing size = 0 with a non-NULL buf is equivalent to detaching — the pointer is stored but never written through.
char errbuf[M2K_ERROR_BUFFER_SIZE];
m2k_set_error_buffer(ctx, errbuf, sizeof(errbuf));
if (m2k_atcmd(ctx, "AT~not~a~real~command") != M2K_OK)
fprintf(stderr, "modemu2k: %s\n", errbuf);

◆ m2k_set_force_verbose()

M2K_API void m2k_set_force_verbose ( m2k_t * ctx,
int on )

Bypass the ATV verbose category mask.

Narration sites are normally gated by the category mask the Hayes ATV command sets. ATZ resets that mask to its saved default of 0, so a host that wanted log output for the whole run can be silenced by any user-issued ATZ. This flag is OR'd into the gate, so narration survives regardless of the ATV mask. Default is off. Hosts should prefer m2k_set_log_level(), which the standalone CLI's -v flag now uses; this flag remains as a plain mask bypass.

Parameters
ctxModem context.
onNon-zero to force narration on, zero to defer to ATV.
Since
0.2.2

◆ m2k_set_log_fn()

M2K_API void m2k_set_log_fn ( m2k_t * ctx,
m2k_log_fn fn,
void * userdata )

Install a log callback.

When fn is non-NULL, all diagnostic messages are routed to it instead of stderr. Pass fn = NULL to restore the default stderr output.

Parameters
ctxModem context.
fnCallback to receive log messages, or NULL.
userdataOpaque pointer forwarded to every fn invocation.
/* Userdata is passed through unchanged to the callback. */
static void my_logger(const char *msg, void *userdata)
{
FILE *log = userdata;
fputs(msg, log);
}
m2k_set_log_fn(ctx, my_logger, stderr);
Examples
main.c.

◆ m2k_set_log_level()

M2K_API void m2k_set_log_level ( m2k_t * ctx,
m2k_log_level_t level )

Set the severity threshold for log delivery.

Messages less severe than level (numerically greater m2k_log_level_t values) are dropped before they reach the log callback (or stderr). The default is M2K_LOG_INFO, which matches the library's historical always-on output: errors, warnings, and lifecycle notices flow; DEBUG narration and TRACE byte traffic stay quiet until asked for.

The threshold lives outside the Hayes-visible S/% registers, so an ATZ from the far side cannot reset it — this supersedes m2k_set_force_verbose() as the host-side knob (that flag remains as a plain ATV-mask bypass). The ATV mask itself keeps working: it can open individual DEBUG/TRACE categories even when the level threshold would hide them.

The standalone CLI maps -v to M2K_LOG_DEBUG and -vv to M2K_LOG_TRACE.

Parameters
ctxModem context.
levelNew threshold; one of m2k_log_level_t.
Since
0.2.3
Examples
main.c.

◆ m2k_set_rts()

M2K_API void m2k_set_rts ( m2k_t * ctx,
int on )

Set the host's RTS (Request to Send) signal state.

Tracked for symmetry with real Hayes modems; modemu2k does not act on RTS state (hardware flow control is meaningless for a TCP-based virtual modem — the kernel's socket buffers and TCP windowing do that job). Default state is asserted (1).

Parameters
ctxModem context.
onNon-zero to assert RTS, zero to drop it.

◆ m2k_setup_answer()

M2K_API m2k_err_t m2k_setup_answer ( m2k_t * ctx,
const char * port )

Bind the line-side "answer" listener on port.

Incoming TCP connections on this port are treated as modem calls: while one is pending, command mode emits RING (S-register 1 counts the rings); the ATA command — or S0 auto-answer when S0 > 0 — accepts the call and goes online, exactly as if ATD had connected outward. The listener persists across hangups (a real modem's hangup does not remove the phone line) and is closed by m2k_free().

Not to be confused with m2k_setup_listen(), which accepts a TCP connection to use as the TTY (DTE side).

Parameters
ctxModem context.
portService name or decimal port number to listen on.
Returns
M2K_OK on success; M2K_ERR_SOCKET if a listener is already bound or on bind / listen / getaddrinfo failure.
/* Bind TCP port 2030 as the "phone line": while a caller waits, command
mode emits RING; ATA (or ATS0=1 auto-answer) accepts the call. Pair
with -a/--answer on the CLI for the same flow. */
static int run_answerer(m2k_t *ctx, const char *port)
{
m2k_err_t err = m2k_setup_answer(ctx, port);
if (err != M2K_OK) {
fprintf(stderr, "setup_answer: %s\n", m2k_strerror(err));
return 1;
}
m2k_run(ctx); /* callers now RING; answer with ATA or S0 */
return 0;
}
Since
0.2.3
Examples
main.c.

◆ m2k_setup_app_io()

M2K_API m2k_err_t m2k_setup_app_io ( m2k_t * ctx)

Embed mode: no real TTY fd; the host application supplies the bytes a TTY would normally produce, via m2k_write_from_app(), and consumes the bytes modemu2k would normally send to the TTY, via m2k_read_to_app().

Intended for embedding modemu2k inside a host program that already owns the terminal/serial interface (e.g. a comm program linking libmodemu2k instead of forking the standalone binary). The socket side still uses real fds and integrates with the host's event loop via m2k_get_pollfds() as usual.

Parameters
ctxModem context.
Returns
M2K_OK.

◆ m2k_setup_comm_program()

M2K_API m2k_err_t m2k_setup_comm_program ( m2k_t * ctx,
const char * cmd )

Allocate a PTY and fork/exec a comm program on the slave.

The slave device path is substituted for s in cmd. Call before m2k_run().

Parameters
ctxModem context.
cmdShell command; s is replaced by the slave device path.
Returns
M2K_OK on success, M2K_ERR_PTY or M2K_ERR_NOMEM on failure.
/* Fork minicom on the PTY slave; %s is substituted with the slave path. */
m2k_err_t err = m2k_setup_comm_program(ctx, "minicom -l -tansi -con -p %s");
if (err != M2K_OK) {
fprintf(stderr, "setup_comm_program failed: %s\n", m2k_strerror(err));
m2k_free(ctx);
return 1;
}
m2k_run(ctx); /* blocks until the comm program exits */
Examples
main.c.

◆ m2k_setup_dev()

M2K_API m2k_err_t m2k_setup_dev ( m2k_t * ctx,
const char * dev )

Open an existing PTY device as the TTY.

Parameters
ctxModem context.
devPath to the PTY master device (e.g. "/dev/ptyp0").
Returns
M2K_OK on success, M2K_ERR_PTY if the device cannot be opened.
Examples
main.c.

◆ m2k_setup_listen()

M2K_API m2k_err_t m2k_setup_listen ( m2k_t * ctx,
const char * port )

Bind a TCP listening socket on port.

Binds and listens on all interfaces (dual-stack IPv4/IPv6); returns immediately without waiting for a connection. Pair with m2k_listen_accept() to accept one connection and use it as the TTY, or integrate the listener fd with the caller's own event loop.

Suitable for use as a virtual-modem backend for programs that connect to modemu2k over TCP, such as dosemu2.

Parameters
ctxModem context.
portService name or decimal port number to listen on.
Returns
M2K_OK on success, M2K_ERR_SOCKET on failure. All bind / listen / getaddrinfo failures (port in use, address family unavailable, etc.) funnel through M2K_ERR_SOCKET; pair with m2k_set_error_buffer() to recover the specific cause.
/* Bind TCP port 5000, accept one connection, then drive the modem state
machine over the accepted socket. Pair with -l/--listen on the CLI for
the same flow. */
static int run_listener(m2k_t *ctx, const char *port)
{
m2k_err_t err = m2k_setup_listen(ctx, port);
if (err != M2K_OK) {
fprintf(stderr, "setup_listen: %s\n", m2k_strerror(err));
return 1;
}
err = m2k_listen_accept(ctx); /* blocks until a client connects */
if (err != M2K_OK) {
fprintf(stderr, "listen_accept: %s\n", m2k_strerror(err));
return 1;
}
m2k_run(ctx);
return 0;
}
Examples
main.c.

◆ m2k_setup_pty()

M2K_API m2k_err_t m2k_setup_pty ( m2k_t * ctx,
const char ** slave_out )

Allocate a PTY master and return the slave device path.

The returned pointer is owned by the context — do NOT free() it. It remains valid until the next m2k_setup_*() call or m2k_free(ctx).

Use this when you need the slave path itself (e.g. to print it with –show). To fork a comm program on the slave, prefer m2k_setup_comm_program().

Parameters
ctxModem context.
slave_outOut: pointer to a NUL-terminated slave path inside ctx. Must be non-NULL.
Returns
M2K_OK on success, M2K_ERR_PTY on failure.
Examples
main.c.

◆ m2k_setup_stdin()

M2K_API m2k_err_t m2k_setup_stdin ( m2k_t * ctx)

Use stdin/stdout as the TTY (standalone mode).

Puts the terminal into raw mode. Call before m2k_run().

Parameters
ctxModem context.
Returns
M2K_OK.
Examples
main.c.

◆ m2k_step()

M2K_API m2k_err_t m2k_step ( m2k_t * ctx,
struct pollfd * fds,
size_t nfds )

Run one non-blocking iteration of the state machine.

Consumes the events the caller's poll() reported (via the .revents fields of fds) and advances the cmd-mode / online-mode state once. Does no poll() of its own. Safe to call with all .revents == 0 (the call is then a no-op).

Parameters
ctxModem context.
fdsThe pollfd array previously filled by m2k_get_pollfds(), with the OS's .revents values populated by the caller.
nfdsNumber of entries in fds (the value m2k_get_pollfds returned in *nfds_inout).
Returns
M2K_OK on success, M2K_ERR_BUG on a corrupted state value.

◆ m2k_strerror()

M2K_API const char * m2k_strerror ( m2k_err_t err)

Return a human-readable string for err.

Parameters
errError code.
Returns
Static string; never NULL. Codes outside the m2k_err_t range return a generic "Unknown error".

◆ m2k_version()

M2K_API const char * m2k_version ( void )

Runtime version string of the linked libmodemu2k.

Lets a caller detect a header/library version mismatch by comparing M2K_VERSION (the value at the time the caller's translation unit was compiled) against m2k_version() (the value the linked library was built with).

Returns
Static "MAJOR.MINOR.PATCH" string; never NULL, never owned by the caller.

◆ m2k_write_from_app()

M2K_API m2k_err_t m2k_write_from_app ( m2k_t * ctx,
const void * buf,
size_t len,
size_t * consumed )

Push bytes into the modem as if they had been read from the TTY.

Only valid after m2k_setup_app_io(). Accepts as many bytes from buf as the internal TTY read buffer has room for (partial accept; the actual count is returned via *consumed). The accepted bytes are processed by the next m2k_step() call.

If the buffer is completely full, *consumed is set to 0 and the function returns M2K_ERR_WOULDBLOCK — call m2k_step() to drain, then retry.

Parameters
ctxModem context.
bufBytes to inject. Must be non-NULL when len > 0.
lenLength of buf in bytes. May be 0.
consumedOut: bytes actually accepted (0 .. len). Must be non-NULL.
Returns
M2K_OK if any bytes were accepted (a call with len == 0 is a no-op returning M2K_OK), M2K_ERR_WOULDBLOCK if none were, M2K_ERR_PTY (overloaded as "wrong I/O mode") if the context is not in app-I/O mode — call m2k_setup_app_io() first.