TI-RTOS vxi11 儀器服務的實現_part2

差異列表

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 連結(待補)


  目錄