Linux Phonet 協議族

簡介

Phonet 是一種資料包協議,諾基亞蜂窩調變解調器使用它進行 IPC 和 RPC。 使用 Linux Phonet 套接字系列,Linux 主機程序可以從/向調變解調器或連線到調變解調器的任何其他外部裝置接收和傳送訊息。 調變解調器負責路由。

Phonet 資料包可以透過各種硬體連線進行交換,具體取決於裝置,例如

  • 帶有 CDC Phonet 介面的 USB,

  • 紅外線,

  • 藍牙,

  • RS232 序列埠(帶有專用的 “FBUS” 行規程),

  • 帶有某些 TI OMAP 處理器的 SSI 匯流排。

資料包格式

Phonet 資料包具有以下通用標頭

struct phonethdr {
  uint8_t  pn_media;  /* Media type (link-layer identifier) */
  uint8_t  pn_rdev;   /* Receiver device ID */
  uint8_t  pn_sdev;   /* Sender device ID */
  uint8_t  pn_res;    /* Resource ID or function */
  uint16_t pn_length; /* Big-endian message byte length (minus 6) */
  uint8_t  pn_robj;   /* Receiver object ID */
  uint8_t  pn_sobj;   /* Sender object ID */
};

在 Linux 上,鏈路層標頭包括 pn_media 位元組(見下文)。 接下來的 7 個位元組是網路層標頭的一部分。

裝置 ID 被拆分:6 個高位構成裝置地址,而 2 個低位用於多路複用,8 位物件識別符號也是如此。 因此,Phonet 可以被認為是具有 6 位地址空間和 10 位傳輸協議的網路層(很像 IP 世界中的埠號)。

調變解調器的地址始終為零。 所有其他裝置都有自己的 6 位地址。

網路層

Phonet 套接字地址族對映 Phonet 資料包頭

struct sockaddr_pn {
  sa_family_t spn_family;    /* AF_PHONET */
  uint8_t     spn_obj;       /* Object ID */
  uint8_t     spn_dev;       /* Device ID */
  uint8_t     spn_resource;  /* Resource or function */
  uint8_t     spn_zero[...]; /* Padding */
};

資源欄位僅在傳送和接收時使用; bind() 和 getsockname() 會忽略它。

底層資料報協議

應用程式可以使用來自 PF_PHONET 系列的 Phonet 資料報套接字協議傳送 Phonet 訊息。 每個套接字都繫結到 2^10 個可用物件 ID 之一,並且可以與任何其他對等方傳送和接收資料包。

struct sockaddr_pn addr = { .spn_family = AF_PHONET, };
ssize_t len;
socklen_t addrlen = sizeof(addr);
int fd;

fd = socket(PF_PHONET, SOCK_DGRAM, 0);
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
/* ... */

sendto(fd, msg, msglen, 0, (struct sockaddr *)&addr, sizeof(addr));
len = recvfrom(fd, buf, sizeof(buf), 0,
               (struct sockaddr *)&addr, &addrlen);

此協議遵循 SOCK_DGRAM 無連線語義。 但是,connect() 和 getpeername() 不受支援,因為它們在 Phonet 用法中似乎沒有用處(可以很容易地新增)。

資源訂閱

一個 Phonet 資料報套接字可以訂閱任意數量的 8 位 Phonet 資源,如下所示

uint32_t res = 0xXX;
ioctl(fd, SIOCPNADDRESOURCE, &res);

使用 SIOCPNDELRESOURCE I/O 控制請求或在套接字關閉時類似地取消訂閱。

請注意,任何給定資源一次只能訂閱一個套接字。 如果不是,ioctl() 將返回 EBUSY。

Phonet Pipe 協議

Phonet Pipe 協議是一種簡單的排序資料包協議,具有端到端擁塞控制。 它使用被動監聽套接字範例。 監聽套接字繫結到唯一的空閒物件 ID。 每個監聽套接字最多可以處理 255 個併發連線,每個 accept()’d 套接字一個。

int lfd, cfd;

lfd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
listen (lfd, INT_MAX);

/* ... */
cfd = accept(lfd, NULL, NULL);
for (;;)
{
  char buf[...];
  ssize_t len = read(cfd, buf, sizeof(buf));

  /* ... */

  write(cfd, msg, msglen);
}

連線傳統上由“第三方”應用程式在兩個端點之間建立。 這意味著兩個端點都是被動的。

從 Linux 核心版本 2.6.39 開始,也可以直接連線兩個端點,在主動側使用 connect()。 這是為了支援更新的諾基亞無線調變解調器 API,例如在 ST-Ericsson U8500 平臺中的諾基亞超薄調變解調器中找到的 API

struct sockaddr_spn spn;
int fd;

fd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
memset(&spn, 0, sizeof(spn));
spn.spn_family = AF_PHONET;
spn.spn_obj = ...;
spn.spn_dev = ...;
spn.spn_resource = 0xD9;
connect(fd, (struct sockaddr *)&spn, sizeof(spn));
/* normal I/O here ... */
close(fd);

管道協議在 SOL_PNPIPE 級別提供兩個套接字選項

PNPIPE_ENCAP 接受一個整數值 (int),其值為

PNPIPE_ENCAP_NONE

套接字正常執行(預設)。

PNPIPE_ENCAP_IP

該套接字用作虛擬 IP 介面的後端。 這需要 CAP_NET_ADMIN 功能。 諾基亞調變解調器上的 GPRS 資料支援可以使用它。 請注意,在此模式下,套接字無法可靠地 poll() 或 read()。

PNPIPE_IFINDEX

是一個只讀整數值。 它包含由 PNPIPE_ENCAP 建立的網路介面的介面索引,如果封裝已關閉,則為零。

PNPIPE_HANDLE

是一個只讀整數值。 它包含管道的底層識別符號(“管道控制代碼”)。 這僅針對已連線或正在連線的套接字描述符定義。

作者

Linux Phonet 最初由 Sakari Ailus 編寫。

其他貢獻者包括 Mikä Liljeberg、Andras Domokos、Carlos Chinea 和 Rémi Denis-Courmont。

版權所有 © 2008 諾基亞公司。