Skip to content

KISS Utilities

KISS mode is popular for communicating to and from TNCs. Traditionally, it is used over serial connections, but in principle it could be used anywhere a datastream might be used to transfer frames or commands, such as over TCP.

Pax25 comes with utilities for reading from a KISS stream and for sending commands to a KISS-aware device. The most pertinent application of this is the serial interface.

If you are interested in how KISS as a protocol works, we recommend reading up on its specification.

Warning

Pax25 does not currently have good semantics for using KISS to manage TNCs that have multiple ports. This is expected to change in a future release, but for now, assume that only one port can be used at a time.

pax25.interfaces.kiss.protocol

Data structures and functions used for ingesting and exporting KISS frames.

read_from_kiss(read_byte: Callable[[], Awaitable[bytes]], get_disassemblers: Callable[[], CommandToDisassembler] = default_frame_disassemblers) -> AsyncIterator[tuple[int, Assembler]] async

Generator loop for reading KISS frames from a source.

Source code in pax25/interfaces/kiss/protocol.py
async def read_from_kiss(
    read_byte: Callable[[], Awaitable[bytes]],
    get_disassemblers: Callable[
        [], CommandToDisassembler
    ] = default_frame_disassemblers,
) -> AsyncIterator[tuple[int, Assembler]]:
    """
    Generator loop for reading KISS frames from a source.
    """
    disassemblers = get_disassemblers()
    state = ReaderState()
    while byte := await read_byte():
        if state.packet_started and not state.reading and byte != KISS_FEND:
            # This is the command byte.
            state.command = byte
            state.reading = True
            continue
        modifier: ReaderUpdater = COMMAND_FUNCS.get(byte, handle_new_char)
        modifier(byte, state)
        if state.ready:
            try:
                disassembler = disassemblers.get(state.command, None)
                if disassembler is None:
                    raise DisassemblyError(
                        "No existing frame disassembler for this command."
                    )
                frame = disassembler(state.packet)
                yield int.from_bytes(state.command, KISS_ENDIAN), frame
            except Exception as err:
                logger.debug("Packet decoding error: %s", (err,))
            state = ReaderState()

ax25_frames_from_kiss(read_byte: Callable[[], Awaitable[bytes]]) -> AsyncIterator[Frame] async

Generator loop for reading KISS from a source. Drops all frames except for AX.25 frames.

Source code in pax25/interfaces/kiss/protocol.py
async def ax25_frames_from_kiss(
    read_byte: Callable[[], Awaitable[bytes]],
) -> AsyncIterator[Frame]:
    """
    Generator loop for reading KISS from a source. Drops all frames except for AX.25
    frames.
    """
    async for frame in filter_kiss_frames(read_byte, frame_types={Frame}):
        yield frame