差異列表
num | 平台/參數 | windows/Unix | TI-RTOS(lwip1.4.1) |
---|---|---|---|
1 | socket | unsigned short | void * |
2 | sizeof(enum) | 4 (int) | 1(packed) |
3 | endian | big-endian | little-endian |
4 | htonl | function | macro |
5 | ntohl | function | macro |
6 | htons | function | macro |
7 | ntohs | function | macro |
8 | select() | select(a,b,c,d,e) | fdSelect(a,(fdset )b,(fdset *)c,(fd_set \)d,e) |
9 | getpid() | 有 | 無 |
10 | errno.h | 有 | 有(但缺一些宣告) |
11 | FD_SETSIZE | 64 | NDK_FD_SETSIZE 16 |
12 | abort | 有 | 有但不可以使用,會讓程式整個退出 |
13 | 關閉 socket | closesocket | fdclose |
14 | struct hostent | 有 | 無 |
15 | struct hostent | 有 | 無 |
16 | _rpc_dtablesize | 有 | 無 |
17 | gettimeofday | 有 | 無 |
18 | register 數量 | 夠 | 不夠 |
19 | get_myad.c | 有 | 自行實作 |
移植開始
1.socket 間的差異,
看到 TI-RTOS 是地址,Windos/Unix 是單純的變數,在 rpc 某些部分文件是有很大的影響,因為 rpc 有一個部分會用 socket 的值拿來當作陣列的 index 所以, 很明顯 TI-RTOS 會讓整個陣列空間爆掉所以我們必須要替換他的演算法。
像是 svc.c
原本的 code 長這樣
xports[sock] = xprt;
但很明顯 TI-RTOS 的 socket 放進去 陣列一定會爆掉,所以我們必須換掉成
ndk_socket_to_sock_set(sock,idx);
xports[idx] = xprt;
svc.c 上面相關 function 長這樣
//
static SOCKET ysocks[YSOCKS_SIZE]={0};
// added by yuyan
/*
* ndk_socket_to_sock_find() function for xports
* if find sock in ysocks ,then return idx; otherwise return free room idx
*/
int ndk_socket_to_sock_find(sock)
SOCKET sock;
{
int i;
int free_latest = -1;
for(i = 0;i < YSOCKS_SIZE;i++)
{
if(ysocks[i] == sock) //find room
return i;
if(free_latest == -1 && ysocks[i] == 0)
free_latest = i;
}
return free_latest;
}
int ndk_socket_to_sock_release(int idx)
{
ysocks[idx] = 0;
}
int ndk_socket_to_sock_set(SOCKET sock,int idx)
{
ysocks[idx] = sock;
}
基本上解決的個大致上就先完成了一半。
2.sizeof(enum) 的差異
很不巧的是在 rpc 模組中他設定的 sizeof(enum)必須要是 4,否則會影響送出去封包的大小,當然在 TI-RTOS 也可以在 CCS 專案 properties 設定成 4,但因為在我那個板子上設定成 4 會有其他問題,所以只能改變 rpc 模組,讓他不使用 enum,簡單來說,我用一般 define + int 型態去替換掉,就可以解決這個問題。
以這個做例子 enum clnt_stat
enum clnt_stat {
RPC_SUCCESS=0, /* call succeeded */
/*
* local errors
*/
RPC_CANTENCODEARGS=1, /* can't encode arguments */
RPC_CANTDECODERES=2, /* can't decode results */
RPC_CANTSEND=3, /* failure in sending call */
RPC_CANTRECV=4, /* failure in receiving result */
RPC_TIMEDOUT=5, /* call timed out */
/*
* remote errors
*/
RPC_VERSMISMATCH=6, /* rpc versions not compatible */
RPC_AUTHERROR=7, /* authentication error */
RPC_PROGUNAVAIL=8, /* program not available */
RPC_PROGVERSMISMATCH=9, /* program version mismatched */
RPC_PROCUNAVAIL=10, /* procedure unavailable */
RPC_CANTDECODEARGS=11, /* decode arguments error */
RPC_SYSTEMERROR=12, /* generic "other problem" */
/*
* callrpc & clnt_create errors
*/
RPC_UNKNOWNHOST=13, /* unknown host name */
RPC_UNKNOWNPROTO=17, /* unkown protocol */
/*
* _ create errors
*/
RPC_PMAPFAILURE=14, /* the pmapper failed in its call */
RPC_PROGNOTREGISTERED=15, /* remote program is not registered */
/*
* unspecified error
*/
RPC_FAILED=16
};
改成這樣
#define RPC_SUCCESS 0 /* call succeeded */
/*
* local errors
*/
#define RPC_CANTENCODEARGS 1 /* can't encode arguments */
#define RPC_CANTDECODERES 2 /* can't decode results */
#define RPC_CANTSEND 3 /* failure in sending call */
#define RPC_CANTRECV 4 /* failure in receiving result */
#define RPC_TIMEDOUT 5 /* call timed out */
/*
* remote errors
*/
#define RPC_VERSMISMATCH 6 /* rpc versions not compatible */
#define RPC_AUTHERROR 7 /* authentication error */
#define RPC_PROGUNAVAIL 8 /* program not available */
#define RPC_PROGVERSMISMATCH 9 /* program version mismatched */
#define RPC_PROCUNAVAIL 10 /* procedure unavailable */
#define RPC_CANTDECODEARGS 11 /* decode arguments error */
#define RPC_SYSTEMERROR 12 /* generic "other problem" */
/*
* callrpc & clnt_create errors
*/
#define RPC_UNKNOWNHOST 13 /* unknown host name */
#define RPC_UNKNOWNPROTO 17 /* unkown protocol */
/*
* _ create errors
*/
#define RPC_PMAPFAILURE 14 /* the pmapper failed in its call */
#define RPC_PROGNOTREGISTERED 15 /* remote program is not registered */
/*
* unspecified error
*/
#define RPC_FAILED 16
#define clnt_stat int // enum clnt_stat
3.big-endian and little-endian 的差異
經過我的測試,沒有影響。
4-7.htonl、ntohl、htons、ntohs
這個東西主要是在替換高低位元的值,在建置封包時很容易看到,但宣告成 macro 或用 function 在 RPC 上有著極大的差異,讓我們看一段 code。
socketndk.h
#define htonl(a) ((((a) & 0xff000000) >> 24) | (((a) & 0x00ff0000) >> 8) | \
(((a) & 0x0000ff00) << 8) | (((a) & 0x000000ff) << 24) )
#define ntohl(a) htonl(a)
#define ntohs(a) htons(a)
在 xdr.h 的檔案中
#define IXDR_GET_LONG(buf) ((long)ntohl((u_long)*(buf)++))
...
// 當使用者呼叫 IXDR_GET_LONG(buf)
// macro 的ntohl 會被展開成
((long)ntohl((((((u_long)*(buf)++))) & 0xff000000) >> 24) | ((((u_long)*(buf)++))) & 0x00ff0000) >> 8) | \
((((u_long)*(buf)++))) & 0x0000ff00) << 8) | ((((u_long)*(buf)++))) & 0x000000ff) << 24) )
可以看到 buf 的指標被++了 4 次,很明顯會有問題,所以我改成自己寫 NTOHL、HTONL、NTOHS、HTONS 用 function 的方式 替換掉所有的 ntohl、htonl、ntohs、htons
vxi11-rtos.h
static unsigned short HTONS(unsigned short a)
{
return ( (((a)>>8)&0xff) + (((a)<<8)&0xff00) );
}
static unsigned int HTONL(unsigned int n)
{
return (((((unsigned long)(n) & 0xFF)) << 24) | \
((((unsigned long)(n) & 0xFF00)) << 8) | \
((((unsigned long)(n) & 0xFF0000)) >> 8) | \
((((unsigned long)(n) & 0xFF000000)) >> 24));
}
#define NTOHL(a) HTONL(a)
#define NTOHS(a) HTONS(a)
接下來就是把 rpc 內所有檔案有用到 ntohl、htonl、ntohs、htons 替換成 NTOHL、HTONL、NTOHS、HTONS 上面自己實作的 function
8.select()差異
這個東西好辦,TI-RTOS 跟 Windows/Unix 下的 select 長得差不多。所以就
vxi-rtos.h
#define select(a,b,c,d,e) fdSelect(a,(fd_set *)b,(fd_set *)c,(fd_set *)d,e)
9. getpid()
這個東西在 TI-RTOS 裡面是沒有的,但是我仔細的看了 rpc 使用他做了什麼式,發現只是為了產生隨機 port 的範圍,為了能夠隨機所以才使用他,所以隨便給他訂個數字就可以了
vxi-rtos.h
#define getpid() 123 //隨意
10.errno.h
TI-RTOS 沒有 EAFNOSUPPORT 所以就隨意宣告給他。
vxi-rtos.h
#include <errno.h>
#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */
TI-RTOS 沒有 errlist,所以就隨意宣告給他。
vxi-rtos.c
char *sys_errlist[10]={0};
11.NDK_FD_SETSIZE
這個東西是只 select 可監控的 socket 最大數是多少,ti-rtos 有 16,所以夠用
vxi-rtos.h
#define YSOCKS_SIZE NDK_FD_SETSIZE
#define _rpc_dtablesize() YSOCKS_SIZE
另外,portmap 會用掉 2 個(udp+tcp),vxi 會用掉 2 個(udp+tcp),所以可以得知 vxi 的連線量最多可達到 16-4=12 條。
12.abort
TI-RTOS 有但不可以使用,會讓程式整個退出,所以把 rpc 所有檔案看到的 abort 換成自己寫的 yabort
vxi-rtos.c
void yabort()
{
UARTprintf("yuyan abort()..... \n");
}
13.關閉 socket
這個簡單,直接
vxi-rtos.h
#define closesocket fdClose
14-15.struct hostent、struct hostent
因為 TI-RTOS 的 socketndk.h 沒有這個結構,所以自己補上
vxi-rtos.h
struct hostent {
char * h_name; /* official name of host */
char ** h_aliases; /* alias list */
short h_addrtype; /* host address type */
short h_length; /* length of address */
char ** h_addr_list; /* list of addresses */
#define h_addr h_addr_list[0] /* address, for backward compat */
};
struct protoent {
char * p_name; /* official protocol name */
char ** p_aliases; /* alias list */
short p_proto; /* protocol # */
};
16. _rpc_dtablesize
這個東西是 unix 裡才有的東西,但實際上跟 FD_SETSIZE 是一樣的東西所以就
vxi-rtos.h
#define YSOCKS_SIZE NDK_FD_SETSIZE
#define _rpc_dtablesize() YSOCKS_SIZE
17.gettimeofday
TI-RTOS 並沒有時間這個函數,於是我看 rpc 用這個函數在做甚麼咚咚,原來只是要取亂數值,所以替換方法就是有用到他的地方用亂數取代,像是
先宣告的 tick
vxi-rtos.c
int vxi11_tick()
{
unsigned long t = (unsigned long)Clock_getTicks();
return (int64_t) t;
}
原本的 auth_uni.c
(void)gettimeofday(&now, (struct timezone *)0);
aup.aup_time = now.tv_sec;
改成
//(void)gettimeofday(&now, (struct timezone *)0); // changed by yuyan
aup.aup_time = vxi11_tick()/1000000;//now.tv_sec;
18.register 數量(可選)
這個東西我比較不確定 TI-RTOS 的東西夠不夠用,rpc 原本有使用目的是希望存在記憶體,能夠更快數的存取 socket list 結構,所以我懶的驗證就把所有有關的 register 全部先幹掉。
19.get_myad.c
最後就是這個檔案啦,裡面有 get_myaddress(addr)函式,這個東西就是 portmap 要知道自己的 local ip 是多少,所以內容就自行實作,
原本為
static char sccsid[] = "@(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro";
#endif
/*
* get_myaddress.c
*
* Get client's IP address via ioctl. This avoids using the yellowpages.
* Copyright (C) 1984, Sun Microsystems, Inc.
*/
#ifdef WIN32
#include <rpc/rpc.h>
#include <rpc/pmap_pro.h>
#include <stdio.h>
#define MAX_NAME_LEN 255
#else
#include <rpc/types.h>
#include <rpc/pmap_prot.h>
#include <sys/socket.h>
#include <stdio.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/*
* don't use gethostbyname, which would invoke yellow pages
*/
#endif
get_myaddress(addr)
struct sockaddr_in *addr;
{
#ifdef WIN32
struct hostent *Hostent;
char my_name[MAX_NAME_LEN];
gethostname(my_name, MAX_NAME_LEN);
Hostent = gethostbyname(my_name);
if (Hostent == NULL) {
errno;
perror("Can not get host info");
exit (1);
}
addr->sin_family = AF_INET;
addr->sin_port = htons(PMAPPORT);
bcopy((char *)Hostent->h_addr, (char *)&addr->sin_addr,
Hostent->h_length);
#else
int s;
char buf[BUFSIZ];
struct ifconf ifc;
struct ifreq ifreq, *ifr;
int len;
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("get_myaddress: socket");
exit(1);
}
ifc.ifc_len = sizeof (buf);
ifc.ifc_buf = buf;
if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
perror("get_myaddress: ioctl (get interface configuration)");
exit(1);
}
ifr = ifc.ifc_req;
for (len = ifc.ifc_len; len; len -= sizeof ifreq) {
ifreq = *ifr;
if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
perror("get_myaddress: ioctl");
exit(1);
}
if ((ifreq.ifr_flags & IFF_UP) &&
ifr->ifr_addr.sa_family == AF_INET) {
*addr = *((struct sockaddr_in *)&ifr->ifr_addr);
addr->sin_port = htons(PMAPPORT);
break;
}
ifr++;
}
(void) close(s);
#endif
被我改為
static char sccsid[] = "@(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro";
#endif
/*
* get_myaddress.c
*
* Get client's IP address via ioctl. This avoids using the yellowpages.
* Copyright (C) 1984, Sun Microsystems, Inc.
*/
#include "../vxi11-rtos.h"
#include <stdio.h>
#include <rpc/types.h>
#include <rpc/pmap_pro.h>
//#include <sys/socket.h>
//
//#include <net/if.h>
//#include <sys/ioctl.h>
//#include <arpa/inet.h>
//#include <netinet/in.h>
/*
* don't use gethostbyname, which would invoke yellow pages
*/
#include "menu/data/dataMenu.h"
get_myaddress(addr)
struct sockaddr_in *addr;
{
//UARTprintf("get_myaddress()\n");
unsigned char *local_ip = NULL;
if (Conf.intf_sel.LAN.dhcp) {
local_ip = Dhcp.ip;
}
else {
local_ip = Conf.intf_sel.LAN.ip;
}
unsigned int ip4 = 0;
if ((local_ip[3] < 256) && (local_ip[2] < 256) && (local_ip[1] < 256) && (local_ip[0] < 256)) {
ip4 = (local_ip[3] << 24)
+ (local_ip[2] << 16)
+ (local_ip[1] << 8)
+ local_ip[0];
}
addr->sin_family = AF_INET;
addr->sin_port = HTONS(PMAPPORT);
addr->sin_addr.s_addr = ip4;
//inet_pton(AF_INET, "192.168.31.239", &addr->sin_addr);
return;
}
就是這麼的樸實無華且枯燥~。
結語
本篇只大概講了大概的轉換方向,畢竟修改的東西相當繁多,當然我也會將轉換前後的代碼放在我的 github,一個是可以在 windows 上執行的專案,另一個是在 TI-RTOS 上執行的,完整的專案就交給前公司了,如果有需要修正的地方歡迎來信指教。
github 連結(待補)
gitlab 連結(待補)