^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) .. SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) .. include:: <isonum.txt>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) ============================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) Linux Phonet protocol family
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) ============================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) Introduction
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) ------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) Phonet is a packet protocol used by Nokia cellular modems for both IPC
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) and RPC. With the Linux Phonet socket family, Linux host processes can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) receive and send messages from/to the modem, or any other external
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) device attached to the modem. The modem takes care of routing.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) Phonet packets can be exchanged through various hardware connections
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) depending on the device, such as:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) - USB with the CDC Phonet interface,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) - infrared,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) - Bluetooth,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) - an RS232 serial port (with a dedicated "FBUS" line discipline),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) - the SSI bus with some TI OMAP processors.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) Packets format
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) --------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) Phonet packets have a common header as follows::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct phonethdr {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) uint8_t pn_media; /* Media type (link-layer identifier) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) uint8_t pn_rdev; /* Receiver device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) uint8_t pn_sdev; /* Sender device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) uint8_t pn_res; /* Resource ID or function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) uint16_t pn_length; /* Big-endian message byte length (minus 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) uint8_t pn_robj; /* Receiver object ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) uint8_t pn_sobj; /* Sender object ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) On Linux, the link-layer header includes the pn_media byte (see below).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) The next 7 bytes are part of the network-layer header.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) The device ID is split: the 6 higher-order bits constitute the device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) address, while the 2 lower-order bits are used for multiplexing, as are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) the 8-bit object identifiers. As such, Phonet can be considered as a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) network layer with 6 bits of address space and 10 bits for transport
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) protocol (much like port numbers in IP world).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) The modem always has address number zero. All other device have a their
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) own 6-bit address.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) Link layer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) ----------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) Phonet links are always point-to-point links. The link layer header
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) consists of a single Phonet media type byte. It uniquely identifies the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) link through which the packet is transmitted, from the modem's
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) perspective. Each Phonet network device shall prepend and set the media
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) type byte as appropriate. For convenience, a common phonet_header_ops
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) link-layer header operations structure is provided. It sets the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) media type according to the network device hardware address.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) Linux Phonet network interfaces support a dedicated link layer packets
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) type (ETH_P_PHONET) which is out of the Ethernet type range. They can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) only send and receive Phonet packets.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) The virtual TUN tunnel device driver can also be used for Phonet. This
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) requires IFF_TUN mode, _without_ the IFF_NO_PI flag. In this case,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) there is no link-layer header, so there is no Phonet media type byte.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) Note that Phonet interfaces are not allowed to re-order packets, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) only the (default) Linux FIFO qdisc should be used with them.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) Network layer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) -------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) The Phonet socket address family maps the Phonet packet header::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct sockaddr_pn {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) sa_family_t spn_family; /* AF_PHONET */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) uint8_t spn_obj; /* Object ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) uint8_t spn_dev; /* Device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) uint8_t spn_resource; /* Resource or function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) uint8_t spn_zero[...]; /* Padding */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) The resource field is only used when sending and receiving;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) It is ignored by bind() and getsockname().
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) Low-level datagram protocol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) ---------------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) Applications can send Phonet messages using the Phonet datagram socket
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) protocol from the PF_PHONET family. Each socket is bound to one of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) 2^10 object IDs available, and can send and receive packets with any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) other peer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) ::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct sockaddr_pn addr = { .spn_family = AF_PHONET, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) ssize_t len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) socklen_t addrlen = sizeof(addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) int fd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) fd = socket(PF_PHONET, SOCK_DGRAM, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) bind(fd, (struct sockaddr *)&addr, sizeof(addr));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /* ... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) sendto(fd, msg, msglen, 0, (struct sockaddr *)&addr, sizeof(addr));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) len = recvfrom(fd, buf, sizeof(buf), 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) (struct sockaddr *)&addr, &addrlen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) This protocol follows the SOCK_DGRAM connection-less semantics.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) However, connect() and getpeername() are not supported, as they did
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) not seem useful with Phonet usages (could be added easily).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) Resource subscription
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) ---------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) A Phonet datagram socket can be subscribed to any number of 8-bits
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) Phonet resources, as follow::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) uint32_t res = 0xXX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) ioctl(fd, SIOCPNADDRESOURCE, &res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) Subscription is similarly cancelled using the SIOCPNDELRESOURCE I/O
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) control request, or when the socket is closed.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) Note that no more than one socket can be subcribed to any given
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) resource at a time. If not, ioctl() will return EBUSY.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) Phonet Pipe protocol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) --------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) The Phonet Pipe protocol is a simple sequenced packets protocol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) with end-to-end congestion control. It uses the passive listening
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) socket paradigm. The listening socket is bound to an unique free object
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) ID. Each listening socket can handle up to 255 simultaneous
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) connections, one per accept()'d socket.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) ::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) int lfd, cfd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) lfd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) listen (lfd, INT_MAX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) /* ... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) cfd = accept(lfd, NULL, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) for (;;)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) char buf[...];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) ssize_t len = read(cfd, buf, sizeof(buf));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) /* ... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) write(cfd, msg, msglen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) Connections are traditionally established between two endpoints by a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) "third party" application. This means that both endpoints are passive.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) As of Linux kernel version 2.6.39, it is also possible to connect
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) two endpoints directly, using connect() on the active side. This is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) intended to support the newer Nokia Wireless Modem API, as found in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) e.g. the Nokia Slim Modem in the ST-Ericsson U8500 platform::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) struct sockaddr_spn spn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) int fd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) fd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) memset(&spn, 0, sizeof(spn));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) spn.spn_family = AF_PHONET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) spn.spn_obj = ...;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) spn.spn_dev = ...;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) spn.spn_resource = 0xD9;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) connect(fd, (struct sockaddr *)&spn, sizeof(spn));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) /* normal I/O here ... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) close(fd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) .. Warning:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) When polling a connected pipe socket for writability, there is an
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) intrinsic race condition whereby writability might be lost between the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) polling and the writing system calls. In this case, the socket will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) block until write becomes possible again, unless non-blocking mode
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) is enabled.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) The pipe protocol provides two socket options at the SOL_PNPIPE level:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) PNPIPE_ENCAP accepts one integer value (int) of:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) PNPIPE_ENCAP_NONE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) The socket operates normally (default).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) PNPIPE_ENCAP_IP:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) The socket is used as a backend for a virtual IP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) interface. This requires CAP_NET_ADMIN capability. GPRS data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) support on Nokia modems can use this. Note that the socket cannot
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) be reliably poll()'d or read() from while in this mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) PNPIPE_IFINDEX
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) is a read-only integer value. It contains the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) interface index of the network interface created by PNPIPE_ENCAP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) or zero if encapsulation is off.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) PNPIPE_HANDLE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) is a read-only integer value. It contains the underlying
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) identifier ("pipe handle") of the pipe. This is only defined for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) socket descriptors that are already connected or being connected.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) Authors
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) -------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) Linux Phonet was initially written by Sakari Ailus.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) Other contributors include Mikä Liljeberg, Andras Domokos,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) Carlos Chinea and Rémi Denis-Courmont.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) Copyright |copy| 2008 Nokia Corporation.