Linux 下API的实现
作者: 韩大卫@ 吉林师范大学
驱动工程师工作内容之一就是向上层应用端提供API,这个API完成并封装了全部的与硬件芯片的I/O操作。
本问简单的说明了一个实现API函数的全部过程。
总体上看分为:
1,用户API
2,用户中间层(与底层通信)
3,底层中间层(寻找对应的驱动函数)
4, 驱动函数
5, CPU读写I/O端口。
我们主要的工作就是这个驱动部分
这个驱动函数功能是:将数据包装成kernel 中可操作的结构体, 按既有的通信方式发送给CPU,
这种通信方式就是通过总线了。可以是I2C等等。
最后,CPU返回执行结构,驱动函数再返回给用户层。
再将这些API 包装成库, 这样,用户层的程序就可以直接引用了。
下面的API功能是是: 统计交换芯片的接口速率。
Test/main.c 是一个测试程序, API 和API 的底层驱动的实现文件和头文件都在下面的代码中,
另外,用户层与底层之间的通信,使用的是socket ,这是相比与共享内存等最合适最高效的方式。
路径如下: 用户层API--- 中间层(socket)----| -----驱动中间层(socket)---(驱动函数)
这不是完整的程序,只是程序中的骨干部分。
如果需要全部的程序,请联系作者handawei@jusontech.com
***** ********************
转载请务必表明出处。
********* ***************************
在test/main.c 中:
#include "inc/jas.h"
#include <jas_sw_cmd_api.h>
#include <jas_cmd_cli_api.h>
#include <jas_frm_cmd_api.h>
在 inc/cmd_api/jas_sw_cmd_api.h 中:
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *cntr);
//这就是用户层使用的API
./libjascmd/jas_cmd_cli_api.h 中:
int jas_cmd_cli_api_send_msg(struct jas_cmd_api_cmd_msg tmsg);
int jas_cmd_cli_api_recv_msg(void *data, uint32_t size);
在Main.c 中的 jas_sw_intf_cntr_rate_cal()定义在:
jas_api/switch/sw_cmd_api.c 中:
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *data){
int ret = JAS_SUCCESS;
if(data == NULL)
return JAS_INV_ARG;
ret = jas_cmd_sw_api_intf_cntr_rate_cal(dev_id, intf_id, data);
if(ret < 0)
return ret;
return ret;
}
jas_cmd_sw_api_intf_cntr_rate_cal
这个函数是一个中间层,介于用户层和驱动层之间, 它的声明是在:
libjascmd/jas_cmd_sw_api.h 中:
int jas_cmd_sw_api_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data);
定义是在 ibjascmd/jas_cmd_sw_api.c 中:
int jas_cmd_sw_api_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
int ret = JAS_SUCCESS;
struct jas_cmd_api_cmd_msg msg;
msg.dev_id = dev_id;
msg.intf_id = intf_id;
msg.cmd = SW_INTF_CNTR_RATE_CAL;
msg.dev_type = JAS_CMD_SWITCH;
ret = jas_cmd_cli_api_send_msg(msg);
if (ret < 0) {
return JAS_SET_ERROR;
}
ret = jas_cmd_cli_api_recv_msg(jas_cntr_data, (uint32_t)sizeof(jas_intf_eth_rate_t));
if(ret < 0) {
return ret;
}
return ret;
}
通过使用socket ,发送和接受msg . 实现与驱动层的通信。
那么,socket的接受端是在哪里?
可以通过 grep -rn “SW_INTF_CNTR_RATE_CAL” 的方式,查找出相应的位置。
结果如下:
在libjascmd/jas_cmd_cli_api.c 中:
发现了 case SW_INTF_CNTR_RATE_CAL
进入这个文件中, 就可以找到这个socket 的接受端, 函数如下:
int jas_cmd_cli_serv_check_client(cmdStreamPTR streamP, char *buff, uint32_t length){
socketStreamPTR stream = (socketStreamPTR)streamP;
if(length == 0)
return JAS_SUCCESS;
if(strncmp(buff, "@@JAS_CMD_API", 12) == 0){
write(stream->socket, "Connected!", 10);
return JAS_SUCCESS;
}
if((length != sizeof(struct jas_cmd_api_cmd_msg)) || (strncmp(buff, "########", 8) != 0)){
sys_err("%s, %d, Get Error Buffer: %s, length: %d(%d)\n", __func__, __LINE__, buff, length, sizeof(struct jas_cmd_api_cmd_msg));
return JAS_FAILURE;
}
jas_cmd_cli_serv_run_cmd(streamP, buff);
return 0;
}
进入jas_cmd_cli_serv_run_cmd
int jas_cmd_cli_serv_run_cmd(cmdStreamPTR streamP, char *buff){
struct jas_cmd_api_cmd_msg cmsg;
struct jas_cmd_cli_msg msg;
socketStreamPTR stream = (socketStreamPTR)streamP;
memcpy(&cmsg, buff, sizeof(cmsg));
memset(&msg, 0, sizeof(struct jas_cmd_cli_msg));
memcpy(msg.flag, "########", 8);
msg.ret = JAS_SUCCESS;
msg.len = sizeof(struct jas_cmd_cli_msg) - 8;
switch(cmsg.dev_type){
case JAS_CMD_FRAMER:
__jas_cmd_api_run_frm_cmd(cmsg, &msg);
break;
case JAS_CMD_SWITCH:
__jas_cmd_api_run_sw_cmd(cmsg, &msg);
break;
default:
sys_err("%s, %d, Unknow Dev Type, buff: %s, cmsg.cmd: %d, cmsg.dev_id: %d.\n",
__func__, __LINE__, buff, cmsg.cmd, cmsg.dev_id);
break;
}
write(stream->socket, &msg, sizeof(struct jas_cmd_cli_msg));
return 0;
}
这样就清楚了,通过msg中的变量类型,进入
__jas_cmd_api_run_frm_cmd(cmsg, &msg);
或者
__jas_cmd_api_run_sw_cmd(cmsg, &msg);
处理msg, 最后将处理后的msg通过 write(stream->socket, &msg, sizeof(struct jas_cmd_cli_msg)) 发送出去,
这样就实现了用中间层的通信。
进入 static int __jas_cmd_api_run_sw_cmd()看一下,
static int __jas_cmd_api_run_sw_cmd(struct jas_cmd_api_cmd_msg cmsg, struct jas_cmd_cli_msg *msg){
switch(cmsg.cmd){
case SW_HW_REG_SET:
{
jas_reg_t *preg = (jas_reg_t *)cmsg.data;
msg->ret = jas_sw_drv_hw_reg_set(cmsg.dev_id, preg->regaddr, preg->value);
}
break;
….....
case SW_INTF_CNTR_RATE_CAL:
{
jas_intf_eth_cntr_t *cntr;
cntr = (jas_intf_eth_cntr_t *)msg->data;
break;
….. …
return 0;
}
这个jas_sw_drv_intf_cntr_rate_cal()就是驱动层的相应功能实现函数。
jas_sw_drv_intf_cntr_rate_cal() 的定于是在: libsw/sw_drv_api.c
int jas_sw_drv_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
jas_intf_eth_cntr_t sw_status_info1;
jas_intf_eth_cntr_t sw_status_info2;
int32_t result;
uint64_t *data;
time_t lt,lt1,lt2;
struct tm* ptr1;
struct tm* ptr2;
memset(&sw_status_info1, 0, sizeof(sw_status_info1));
memset(&sw_status_info2, 0, sizeof(sw_status_info2));
jas_sw_drv_intf_cntr_clear_on_read_set(dev_id, intf_id, JAS_FALSE);
jas_sw_drv_intf_cntr_get(dev_id, intf_id, &sw_status_info1);
lt1 = time(NULL);
sleep(1);
jas_sw_drv_intf_cntr_clear_on_read_set(dev_id,intf_id, JAS_FALSE);
jas_sw_drv_intf_cntr_get(dev_id, intf_id, &sw_status_info2);
lt2 = time(NULL);
lt = lt2 - lt1;
ptr1 = localtime(<1);
ptr2 = localtime(<2);
result = device_port_rate_cal(sw_status_info2.good_pkts.rx * 8,
sw_status_info1.good_pkts.rx * 8,
&(jas_cntr_data->good_pkts.rx),
lt);
result = device_port_rate_cal(sw_status_info2.good_pkts.tx * 8,
sw_status_info1.good_pkts.tx * 8,
&(jas_cntr_data->good_pkts.tx),
lt);
result = device_port_rate_cal(sw_status_info2.good_bytes.rx * 8,
sw_status_info1.good_bytes.rx * 8,
&(jas_cntr_data->good_bytes.rx),
lt);
result = device_port_rate_cal(sw_status_info2.good_bytes.tx * 8,
sw_status_info1.good_bytes.tx * 8,
&(jas_cntr_data->good_bytes.tx),
lt);
return 0;
}
现在有一个问题:
jas_sw_drv_intf_cntr_rate_cal()只是在sw_drv_api.c 中有定义,没有在任何.h文件中有声明,
那么在libjascmd/jas_cmd_cli_api.c 中的
case SW_INTF_CNTR_RATE_CAL:
{
jas_intf_eth_cntr_t *cntr;
cntr = (jas_intf_eth_cntr_t *)msg->data;
msg->ret = jas_sw_drv_intf_cntr_rate_cal(cmsg.dev_id, cmsg.intf_id, cntr);
}
break;
jas_sw_drv_intf_cntr_rate_cal()的声明是在 在inc/cmd_api/jas_sw_cmd_api.h 中
#include <jas_sw_cmd_api.h>
进而使用了这个函数。
这样就完成了API的实现
路径如下: 用户层API--- 中间层(socket)----| -----驱动中间层(socket)---(驱动函数)
详细说明如下:
用户层API : jas_sw_intf_cntr_rate_cal()
API的声明(头文件): inc/cmd_api/jas_sw_cmd_api.h
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *cntr);
API的定义(实现): jas_api/switch/sw_cmd_api.c
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *data){
int ret = JAS_SUCCESS;
if(data == NULL)
return JAS_INV_ARG;
ret = jas_cmd_sw_api_intf_cntr_rate_cal(dev_id, intf_id, data);
if(ret < 0)
return ret;
return ret;
}
用户中间层的函数: jas_cmd_sw_api_intf_cntr_rate_cal()
声明(头文件): libjascmd/jas_cmd_sw_api.h
定义: libjascmd/jas_cmd_sw_api.c
int jas_cmd_sw_api_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
int ret = JAS_SUCCESS;
struct jas_cmd_api_cmd_msg msg;
msg.dev_id = dev_id;
msg.intf_id = intf_id;
msg.cmd = SW_INTF_CNTR_RATE_CAL;
msg.dev_type = JAS_CMD_SWITCH;
ret = jas_cmd_cli_api_send_msg(msg);
if (ret < 0) {
return JAS_SET_ERROR;
}
ret = jas_cmd_cli_api_recv_msg(jas_cntr_data, (uint32_t)sizeof(jas_intf_eth_rate_t));
if(ret < 0) {
return ret;
}
return ret;
}
驱动层中间函数 : __jas_cmd_api_run_sw_cmd
位置: libjascmd/jas_cmd_cli_api.c
case SW_INTF_CNTR_RATE_CAL:
{
jas_intf_eth_cntr_t *cntr;
cntr = (jas_intf_eth_cntr_t *)msg->data;
msg->ret = jas_sw_drv_intf_cntr_rate_cal(cmsg.dev_id, cmsg.intf_id, cntr);
}
break;
驱动函数: jas_sw_drv_intf_cntr_rate_cal()
声明: inc/drv/sw_drv_api.h
定义: libsw/sw_drv_api.c
int jas_sw_drv_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
jas_intf_eth_cntr_t sw_status_info1;
jas_intf_eth_cntr_t sw_status_info2;
int32_t result;
uint64_t *data;
time_t lt,lt1,lt2;
struct tm* ptr1;
struct tm* ptr2;
memset(&sw_status_info1, 0, sizeof(sw_status_info1));
memset(&sw_status_info2, 0, sizeof(sw_status_info2));
jas_sw_drv_intf_cntr_clear_on_read_set(dev_id, intf_id, JAS_FALSE);
jas_sw_drv_intf_cntr_get(dev_id, intf_id, &sw_status_info1);
lt1 = time(NULL);
….
}
*************** ******************************************************
2012.7.30 14:00
遇到一个错误:
jascmd/jas_cmd_cli_api.c
libjascmd/jas_cmd_cli_api.c: In function '__jas_cmd_api_run_i2c_cmd':
libjascmd/jas_cmd_cli_api.c:335: error: 'I2C_INTF_I2C_TMP_GET' undeclared (first use in this function)
libjascmd/jas_cmd_cli_api.c:335: error: (Each undeclared identifier is reported only once
libjascmd/jas_cmd_cli_api.c:335: error: for each function it appears in.)
在jascmd/jas_cmd_cli_api.c 中明明有:
#include "jas_cmd_i2c_api.h"
在jascmd/jas_cmd_i2c_api.h 也有
typedef enum {
I2C_INTF_I2C_TMP_GET = 0,
I2C_INTF_I2C_RTC_GET,
I2C_INTF_I2C_EEPROM_GET,
I2C_INTF_I2C_SW_XFP_GET,
I2C_INTF_I2C_SW_SFP_GET,
}jas_cmd_i2c_api_cmd_type_t;
但为什么说没有定义呢?
结果是:#ifdef __JAS_CMD_I2C_API_H
应改为:#ifndef __JAS_CMD_I2C_API_H
下午17:00
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_tmp_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_sw_xfp_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_sw_sfp_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_rtc_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_eeprom_get'
在Makefile 也添加了libi2c 的信息,
为什么还是找不到?
原来是在test 中的Makefile 没有添加li2c
**** ************************************
int jas_cmd_frm_api_intf_status_get(int dev_id, int intf_id, jas_frm_intf_status_t *status){
int ret = JAS_SUCCESS;
struct jas_cmd_api_cmd_msg msg;
JAS_NULL_PTR_CHECK(status);
msg.dev_id = dev_id;
msg.intf_id = intf_id;
msg.cmd = FRM_INTF_STATUS_GET;
msg.dev_type = JAS_CMD_FRAMER;
ret = jas_cmd_cli_api_send_msg(msg);
if (ret < 0) {
return JAS_SET_ERROR;
}
ret = jas_cmd_cli_api_recv_msg(status, (uint32_t)sizeof(jas_frm_intf_status_t));
if(ret < 0) {
return JAS_SET_ERROR;
}
return ret;
}
*********** ********
jas_cmd_cli_api_send_msg 定义如下:
int jas_cmd_cli_api_send_msg(struct jas_cmd_api_cmd_msg cmsg){
jas_cmd_cli_api_handle_t *ctrl = (jas_cmd_cli_api_handle_t *)handle;
JAS_NULL_PTR_CHECK(handle);
memcpy(cmsg.flag, "########", 8);
if(__sock_check(ghandle[ctrl->id]->sockfd)){
sys_err("socket %d break off.\n", ghandle[ctrl->id]->sockfd);
close(ghandle[ctrl->id]->sockfd);
ghandle[ctrl->id]->sockfd = 0;
if(__reconnect(ctrl->id) != 0){
sys_err("Failed to reconnect server.\n");
return JAS_SOCK_ERROR;
}
}
return write(ctrl->sockfd, &cmsg, sizeof(struct jas_cmd_api_cmd_msg));
}
strcut jas_cmd_cli_api_handle_t 的定义是:
typedef struct jas_cmd_api_handle{
int id;
int sockfd;
char *ip_addr;
uint32_t port;
}jas_cmd_cli_api_handle_t;
__sock_check() 定义如下:
static int __sock_check(int sockfd){
fd_set fds;
int ret;
struct timeval tv_sel;
if(sockfd == 0){
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, "SOCK FD is 0");
return JAS_FAILURE;
}
tv_sel.tv_sec = 0;
tv_sel.tv_usec = 0;
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
if(ret != 0){
if(ret < 0)
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, strerror(errno));
} /*
else {
if(FD_ISSET(sockfd, &fds)) {
len = read(sockfd, buf, 100);
if(len == 0) {
sys_err("sockfd %d break off.\n", sockfd);
return -1;
}
sys_info("__sock_check buff: %s\n", buf);
}
}
*/
return 0;
}
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
sockfd +1 :是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1.
fds是一个文件描述符的集合,监视这些文件描述符的读变化的
第一个NULL 表示 不关心任何文件的写变化
第二个NULL 表示 不用监视文件错误异常。
时间值为0秒0毫秒,即将select()函数设置成一个纯粹的非阻塞函 数,不管文件描述符是否有变化,
都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
***************** ****
return write(ctrl->sockfd, &cmsg, sizeof(struct jas_cmd_api_cmd_msg));
向 ctr-> sockfd 发送 cmsg, 大小为sizeof(struct jas_cmd_api_cmd_msg);
************* **********
jas_cmd_cli_api_recv_msg() 函数定义是:
int jas_cmd_cli_api_recv_msg(void *data, uint32_t size){
jas_cmd_cli_api_handle_t *ctrl = (jas_cmd_cli_api_handle_t *)handle;
struct jas_cmd_cli_msg *msg = NULL;
int len = CMD_MAX_MSG;
int i, read_len, mlen=0;
struct timeval timeout;
JAS_NULL_PTR_CHECK(handle);
timeout.tv_sec = 900;
timeout.tv_usec = 0;
mlen = len;
read_len = __sock_read_data(ctrl->sockfd, buf_rd, &timeout, &mlen);
if(read_len < len){
sys_err("%s, %d, Read buffer error: \"%s\", len %d(%ld).\n", __func__, __LINE__, buf_rd, read_len, len);
return JAS_FAILURE;
}
/*get the right msg length*/
sys_debug("%s, %d, read buffer: \"%s\", len %d(%ld).\n", __func__, __LINE__, buf_rd, read_len, len);
for(i = 0; i < read_len; i++){
if(buf_rd[i] == '#'){
if(strncmp((char *)&buf_rd[i], "########", 8) == 0){
msg = (struct jas_cmd_cli_msg *)&buf_rd[i];
sys_debug("print msg:\n");
sys_debug("valid len: %d, msg ret: %d.\n",
msg->len, msg->ret);
if(msg->ret != JAS_SUCCESS){
sys_err("Operation failed!\n");
return msg->ret;
}
if(msg->len != (len - 8)){
sys_err("Get Wrong Length: %d(%d).\n",
(len - 8), msg->len);
return JAS_FAILURE;
}
break;
}
}
if(data != NULL)
memcpy(data, msg->data, size);
return 0;
}
__sock_read_data() 定义是:
/*return length of data*/
static int __sock_read_data(int sockfd, uint8_t *buf, struct timeval *timeout, int *len){
fd_set fds;
int ret, mlen=0;
struct timeval tv1, tv2, tv_sel;
if(sockfd == 0){
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, "SOCK FD is 0");
return JAS_INV_ARG;
}
gettimeofday(&tv1, NULL);
while(1){
gettimeofday(&tv2, NULL);
if(((tv2.tv_sec-tv1.tv_sec)*1000000 + (tv2.tv_usec-tv1.tv_usec)) >=
(timeout->tv_sec*1000000 + timeout->tv_usec)){
return 0;
}
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
tv_sel.tv_sec = 0;
tv_sel.tv_usec = 100000;
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
if(ret != 0){
if(ret < 0){
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, strerror(errno));
return JAS_FAILURE;
}
if(FD_ISSET(sockfd, &fds)){
mlen += read(sockfd, &buf[mlen], MSG_RD_BUF_LEN);
sys_debug("buff: %s, len: %d.\n", buf, mlen);
if((len != NULL) && (mlen < *len)){
continue;
}
return mlen;
}
return -1;
}
}
return 0;
}
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
FD_ZERO(&fds) 清空集合fds
FD_SET(sockfd, &fds); 将一个给定的文件描述符socket加入集合fds之中
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
集合最大文件描述符号范围 : sockfd +1
&fds , 监视fds 文件描述符集合。
NULL: 不关心文件写变化
NULL :不关心文件异常
&tv_sel : 0 : 秒 100000: 微秒/
即select在100000 微妙内内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回。
FD_ISSET(sockfd, &fds) fds中的文件描述符 sockfd 是否可以读。
mlen += read(sockfd, &buf[mlen], MSG_RD_BUF_LEN);
可以读的话, mlen 统计 read的字节数。
__sock_read_data(ctrl->sockfd, buf_rd, &timeout, &mlen);
函数作用是:
读取ctrl->sockfd 上的内容,放入buf_rd[] 中,&mlen 是要读取的最小长度,返回读取的字节数。
以上是用户层的API 发送和接收接口函数。
********* ******************************************
作者: 韩大卫@ 吉林师范大学
驱动工程师工作内容之一就是向上层应用端提供API,这个API完成并封装了全部的与硬件芯片的I/O操作。
本问简单的说明了一个实现API函数的全部过程。
总体上看分为:
1,用户API
2,用户中间层(与底层通信)
3,底层中间层(寻找对应的驱动函数)
4, 驱动函数
5, CPU读写I/O端口。
我们主要的工作就是这个驱动部分
这个驱动函数功能是:将数据包装成kernel 中可操作的结构体, 按既有的通信方式发送给CPU,
这种通信方式就是通过总线了。可以是I2C等等。
最后,CPU返回执行结构,驱动函数再返回给用户层。
再将这些API 包装成库, 这样,用户层的程序就可以直接引用了。
下面的API功能是是: 统计交换芯片的接口速率。
Test/main.c 是一个测试程序, API 和API 的底层驱动的实现文件和头文件都在下面的代码中,
另外,用户层与底层之间的通信,使用的是socket ,这是相比与共享内存等最合适最高效的方式。
路径如下: 用户层API--- 中间层(socket)----| -----驱动中间层(socket)---(驱动函数)
这不是完整的程序,只是程序中的骨干部分。
如果需要全部的程序,请联系作者handawei@jusontech.com
***** ********************
转载请务必表明出处。
********* ***************************
在test/main.c 中:
#include "inc/jas.h"
#include <jas_sw_cmd_api.h>
#include <jas_cmd_cli_api.h>
#include <jas_frm_cmd_api.h>
在 inc/cmd_api/jas_sw_cmd_api.h 中:
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *cntr);
//这就是用户层使用的API
./libjascmd/jas_cmd_cli_api.h 中:
int jas_cmd_cli_api_send_msg(struct jas_cmd_api_cmd_msg tmsg);
int jas_cmd_cli_api_recv_msg(void *data, uint32_t size);
在Main.c 中的 jas_sw_intf_cntr_rate_cal()定义在:
jas_api/switch/sw_cmd_api.c 中:
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *data){
int ret = JAS_SUCCESS;
if(data == NULL)
return JAS_INV_ARG;
ret = jas_cmd_sw_api_intf_cntr_rate_cal(dev_id, intf_id, data);
if(ret < 0)
return ret;
return ret;
}
jas_cmd_sw_api_intf_cntr_rate_cal
这个函数是一个中间层,介于用户层和驱动层之间, 它的声明是在:
libjascmd/jas_cmd_sw_api.h 中:
int jas_cmd_sw_api_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data);
定义是在 ibjascmd/jas_cmd_sw_api.c 中:
int jas_cmd_sw_api_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
int ret = JAS_SUCCESS;
struct jas_cmd_api_cmd_msg msg;
msg.dev_id = dev_id;
msg.intf_id = intf_id;
msg.cmd = SW_INTF_CNTR_RATE_CAL;
msg.dev_type = JAS_CMD_SWITCH;
ret = jas_cmd_cli_api_send_msg(msg);
if (ret < 0) {
return JAS_SET_ERROR;
}
ret = jas_cmd_cli_api_recv_msg(jas_cntr_data, (uint32_t)sizeof(jas_intf_eth_rate_t));
if(ret < 0) {
return ret;
}
return ret;
}
通过使用socket ,发送和接受msg . 实现与驱动层的通信。
那么,socket的接受端是在哪里?
可以通过 grep -rn “SW_INTF_CNTR_RATE_CAL” 的方式,查找出相应的位置。
结果如下:
在libjascmd/jas_cmd_cli_api.c 中:
发现了 case SW_INTF_CNTR_RATE_CAL
进入这个文件中, 就可以找到这个socket 的接受端, 函数如下:
int jas_cmd_cli_serv_check_client(cmdStreamPTR streamP, char *buff, uint32_t length){
socketStreamPTR stream = (socketStreamPTR)streamP;
if(length == 0)
return JAS_SUCCESS;
if(strncmp(buff, "@@JAS_CMD_API", 12) == 0){
write(stream->socket, "Connected!", 10);
return JAS_SUCCESS;
}
if((length != sizeof(struct jas_cmd_api_cmd_msg)) || (strncmp(buff, "########", 8) != 0)){
sys_err("%s, %d, Get Error Buffer: %s, length: %d(%d)\n", __func__, __LINE__, buff, length, sizeof(struct jas_cmd_api_cmd_msg));
return JAS_FAILURE;
}
jas_cmd_cli_serv_run_cmd(streamP, buff);
return 0;
}
进入jas_cmd_cli_serv_run_cmd
int jas_cmd_cli_serv_run_cmd(cmdStreamPTR streamP, char *buff){
struct jas_cmd_api_cmd_msg cmsg;
struct jas_cmd_cli_msg msg;
socketStreamPTR stream = (socketStreamPTR)streamP;
memcpy(&cmsg, buff, sizeof(cmsg));
memset(&msg, 0, sizeof(struct jas_cmd_cli_msg));
memcpy(msg.flag, "########", 8);
msg.ret = JAS_SUCCESS;
msg.len = sizeof(struct jas_cmd_cli_msg) - 8;
switch(cmsg.dev_type){
case JAS_CMD_FRAMER:
__jas_cmd_api_run_frm_cmd(cmsg, &msg);
break;
case JAS_CMD_SWITCH:
__jas_cmd_api_run_sw_cmd(cmsg, &msg);
break;
default:
sys_err("%s, %d, Unknow Dev Type, buff: %s, cmsg.cmd: %d, cmsg.dev_id: %d.\n",
__func__, __LINE__, buff, cmsg.cmd, cmsg.dev_id);
break;
}
write(stream->socket, &msg, sizeof(struct jas_cmd_cli_msg));
return 0;
}
这样就清楚了,通过msg中的变量类型,进入
__jas_cmd_api_run_frm_cmd(cmsg, &msg);
或者
__jas_cmd_api_run_sw_cmd(cmsg, &msg);
处理msg, 最后将处理后的msg通过 write(stream->socket, &msg, sizeof(struct jas_cmd_cli_msg)) 发送出去,
这样就实现了用中间层的通信。
进入 static int __jas_cmd_api_run_sw_cmd()看一下,
static int __jas_cmd_api_run_sw_cmd(struct jas_cmd_api_cmd_msg cmsg, struct jas_cmd_cli_msg *msg){
switch(cmsg.cmd){
case SW_HW_REG_SET:
{
jas_reg_t *preg = (jas_reg_t *)cmsg.data;
msg->ret = jas_sw_drv_hw_reg_set(cmsg.dev_id, preg->regaddr, preg->value);
}
break;
….....
case SW_INTF_CNTR_RATE_CAL:
{
jas_intf_eth_cntr_t *cntr;
cntr = (jas_intf_eth_cntr_t *)msg->data;
msg->ret = jas_sw_drv_intf_cntr_rate_cal(cmsg.dev_id, cmsg.intf_id, cntr);
// 这里使用了真正的驱动函数
break;
….. …
return 0;
}
这个jas_sw_drv_intf_cntr_rate_cal()就是驱动层的相应功能实现函数。
jas_sw_drv_intf_cntr_rate_cal() 的定于是在: libsw/sw_drv_api.c
int jas_sw_drv_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
jas_intf_eth_cntr_t sw_status_info1;
jas_intf_eth_cntr_t sw_status_info2;
int32_t result;
uint64_t *data;
time_t lt,lt1,lt2;
struct tm* ptr1;
struct tm* ptr2;
memset(&sw_status_info1, 0, sizeof(sw_status_info1));
memset(&sw_status_info2, 0, sizeof(sw_status_info2));
jas_sw_drv_intf_cntr_clear_on_read_set(dev_id, intf_id, JAS_FALSE);
jas_sw_drv_intf_cntr_get(dev_id, intf_id, &sw_status_info1);
lt1 = time(NULL);
sleep(1);
jas_sw_drv_intf_cntr_clear_on_read_set(dev_id,intf_id, JAS_FALSE);
jas_sw_drv_intf_cntr_get(dev_id, intf_id, &sw_status_info2);
lt2 = time(NULL);
lt = lt2 - lt1;
ptr1 = localtime(<1);
ptr2 = localtime(<2);
result = device_port_rate_cal(sw_status_info2.good_pkts.rx * 8,
sw_status_info1.good_pkts.rx * 8,
&(jas_cntr_data->good_pkts.rx),
lt);
result = device_port_rate_cal(sw_status_info2.good_pkts.tx * 8,
sw_status_info1.good_pkts.tx * 8,
&(jas_cntr_data->good_pkts.tx),
lt);
result = device_port_rate_cal(sw_status_info2.good_bytes.rx * 8,
sw_status_info1.good_bytes.rx * 8,
&(jas_cntr_data->good_bytes.rx),
lt);
result = device_port_rate_cal(sw_status_info2.good_bytes.tx * 8,
sw_status_info1.good_bytes.tx * 8,
&(jas_cntr_data->good_bytes.tx),
lt);
return 0;
}
现在有一个问题:
jas_sw_drv_intf_cntr_rate_cal()只是在sw_drv_api.c 中有定义,没有在任何.h文件中有声明,
那么在libjascmd/jas_cmd_cli_api.c 中的
case SW_INTF_CNTR_RATE_CAL:
{
jas_intf_eth_cntr_t *cntr;
cntr = (jas_intf_eth_cntr_t *)msg->data;
msg->ret = jas_sw_drv_intf_cntr_rate_cal(cmsg.dev_id, cmsg.intf_id, cntr);
}
break;
jas_sw_drv_intf_cntr_rate_cal()的声明是在 在inc/cmd_api/jas_sw_cmd_api.h 中
#include <jas_sw_cmd_api.h>
进而使用了这个函数。
这样就完成了API的实现
路径如下: 用户层API--- 中间层(socket)----| -----驱动中间层(socket)---(驱动函数)
详细说明如下:
用户层API : jas_sw_intf_cntr_rate_cal()
API的声明(头文件): inc/cmd_api/jas_sw_cmd_api.h
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *cntr);
API的定义(实现): jas_api/switch/sw_cmd_api.c
int jas_sw_intf_cntr_rate_cal(int dev_id, int intf_id, jas_intf_eth_rate_t *data){
int ret = JAS_SUCCESS;
if(data == NULL)
return JAS_INV_ARG;
ret = jas_cmd_sw_api_intf_cntr_rate_cal(dev_id, intf_id, data);
if(ret < 0)
return ret;
return ret;
}
用户中间层的函数: jas_cmd_sw_api_intf_cntr_rate_cal()
声明(头文件): libjascmd/jas_cmd_sw_api.h
定义: libjascmd/jas_cmd_sw_api.c
int jas_cmd_sw_api_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
int ret = JAS_SUCCESS;
struct jas_cmd_api_cmd_msg msg;
msg.dev_id = dev_id;
msg.intf_id = intf_id;
msg.cmd = SW_INTF_CNTR_RATE_CAL;
msg.dev_type = JAS_CMD_SWITCH;
ret = jas_cmd_cli_api_send_msg(msg);
if (ret < 0) {
return JAS_SET_ERROR;
}
ret = jas_cmd_cli_api_recv_msg(jas_cntr_data, (uint32_t)sizeof(jas_intf_eth_rate_t));
if(ret < 0) {
return ret;
}
return ret;
}
驱动层中间函数 : __jas_cmd_api_run_sw_cmd
位置: libjascmd/jas_cmd_cli_api.c
case SW_INTF_CNTR_RATE_CAL:
{
jas_intf_eth_cntr_t *cntr;
cntr = (jas_intf_eth_cntr_t *)msg->data;
msg->ret = jas_sw_drv_intf_cntr_rate_cal(cmsg.dev_id, cmsg.intf_id, cntr);
}
break;
驱动函数: jas_sw_drv_intf_cntr_rate_cal()
声明: inc/drv/sw_drv_api.h
定义: libsw/sw_drv_api.c
int jas_sw_drv_intf_cntr_rate_cal(uint8_t dev_id, uint8_t intf_id, jas_intf_eth_rate_t *jas_cntr_data){
jas_intf_eth_cntr_t sw_status_info1;
jas_intf_eth_cntr_t sw_status_info2;
int32_t result;
uint64_t *data;
time_t lt,lt1,lt2;
struct tm* ptr1;
struct tm* ptr2;
memset(&sw_status_info1, 0, sizeof(sw_status_info1));
memset(&sw_status_info2, 0, sizeof(sw_status_info2));
jas_sw_drv_intf_cntr_clear_on_read_set(dev_id, intf_id, JAS_FALSE);
jas_sw_drv_intf_cntr_get(dev_id, intf_id, &sw_status_info1);
lt1 = time(NULL);
….
}
*************** ******************************************************
2012.7.30 14:00
遇到一个错误:
jascmd/jas_cmd_cli_api.c
libjascmd/jas_cmd_cli_api.c: In function '__jas_cmd_api_run_i2c_cmd':
libjascmd/jas_cmd_cli_api.c:335: error: 'I2C_INTF_I2C_TMP_GET' undeclared (first use in this function)
libjascmd/jas_cmd_cli_api.c:335: error: (Each undeclared identifier is reported only once
libjascmd/jas_cmd_cli_api.c:335: error: for each function it appears in.)
在jascmd/jas_cmd_cli_api.c 中明明有:
#include "jas_cmd_i2c_api.h"
在jascmd/jas_cmd_i2c_api.h 也有
typedef enum {
I2C_INTF_I2C_TMP_GET = 0,
I2C_INTF_I2C_RTC_GET,
I2C_INTF_I2C_EEPROM_GET,
I2C_INTF_I2C_SW_XFP_GET,
I2C_INTF_I2C_SW_SFP_GET,
}jas_cmd_i2c_api_cmd_type_t;
但为什么说没有定义呢?
结果是:#ifdef __JAS_CMD_I2C_API_H
应改为:#ifndef __JAS_CMD_I2C_API_H
下午17:00
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_tmp_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_sw_xfp_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_sw_sfp_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_rtc_get'
..//libjascmd.so: undefined reference to `jas_i2c_drv_intf_i2c_eeprom_get'
在Makefile 也添加了libi2c 的信息,
为什么还是找不到?
原来是在test 中的Makefile 没有添加li2c
**** ************************************
int jas_cmd_frm_api_intf_status_get(int dev_id, int intf_id, jas_frm_intf_status_t *status){
int ret = JAS_SUCCESS;
struct jas_cmd_api_cmd_msg msg;
JAS_NULL_PTR_CHECK(status);
msg.dev_id = dev_id;
msg.intf_id = intf_id;
msg.cmd = FRM_INTF_STATUS_GET;
msg.dev_type = JAS_CMD_FRAMER;
ret = jas_cmd_cli_api_send_msg(msg);
if (ret < 0) {
return JAS_SET_ERROR;
}
ret = jas_cmd_cli_api_recv_msg(status, (uint32_t)sizeof(jas_frm_intf_status_t));
if(ret < 0) {
return JAS_SET_ERROR;
}
return ret;
}
*********** ********
jas_cmd_cli_api_send_msg 定义如下:
int jas_cmd_cli_api_send_msg(struct jas_cmd_api_cmd_msg cmsg){
jas_cmd_cli_api_handle_t *ctrl = (jas_cmd_cli_api_handle_t *)handle;
JAS_NULL_PTR_CHECK(handle);
memcpy(cmsg.flag, "########", 8);
if(__sock_check(ghandle[ctrl->id]->sockfd)){
sys_err("socket %d break off.\n", ghandle[ctrl->id]->sockfd);
close(ghandle[ctrl->id]->sockfd);
ghandle[ctrl->id]->sockfd = 0;
if(__reconnect(ctrl->id) != 0){
sys_err("Failed to reconnect server.\n");
return JAS_SOCK_ERROR;
}
}
return write(ctrl->sockfd, &cmsg, sizeof(struct jas_cmd_api_cmd_msg));
}
strcut jas_cmd_cli_api_handle_t 的定义是:
typedef struct jas_cmd_api_handle{
int id;
int sockfd;
char *ip_addr;
uint32_t port;
}jas_cmd_cli_api_handle_t;
__sock_check() 定义如下:
static int __sock_check(int sockfd){
fd_set fds;
int ret;
struct timeval tv_sel;
if(sockfd == 0){
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, "SOCK FD is 0");
return JAS_FAILURE;
}
tv_sel.tv_sec = 0;
tv_sel.tv_usec = 0;
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
if(ret != 0){
if(ret < 0)
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, strerror(errno));
} /*
else {
if(FD_ISSET(sockfd, &fds)) {
len = read(sockfd, buf, 100);
if(len == 0) {
sys_err("sockfd %d break off.\n", sockfd);
return -1;
}
sys_info("__sock_check buff: %s\n", buf);
}
}
*/
return 0;
}
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
sockfd +1 :是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1.
fds是一个文件描述符的集合,监视这些文件描述符的读变化的
第一个NULL 表示 不关心任何文件的写变化
第二个NULL 表示 不用监视文件错误异常。
时间值为0秒0毫秒,即将select()函数设置成一个纯粹的非阻塞函 数,不管文件描述符是否有变化,
都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
***************** ****
return write(ctrl->sockfd, &cmsg, sizeof(struct jas_cmd_api_cmd_msg));
向 ctr-> sockfd 发送 cmsg, 大小为sizeof(struct jas_cmd_api_cmd_msg);
************* **********
jas_cmd_cli_api_recv_msg() 函数定义是:
int jas_cmd_cli_api_recv_msg(void *data, uint32_t size){
jas_cmd_cli_api_handle_t *ctrl = (jas_cmd_cli_api_handle_t *)handle;
struct jas_cmd_cli_msg *msg = NULL;
int len = CMD_MAX_MSG;
int i, read_len, mlen=0;
struct timeval timeout;
JAS_NULL_PTR_CHECK(handle);
timeout.tv_sec = 900;
timeout.tv_usec = 0;
mlen = len;
read_len = __sock_read_data(ctrl->sockfd, buf_rd, &timeout, &mlen);
if(read_len < len){
sys_err("%s, %d, Read buffer error: \"%s\", len %d(%ld).\n", __func__, __LINE__, buf_rd, read_len, len);
return JAS_FAILURE;
}
/*get the right msg length*/
sys_debug("%s, %d, read buffer: \"%s\", len %d(%ld).\n", __func__, __LINE__, buf_rd, read_len, len);
for(i = 0; i < read_len; i++){
if(buf_rd[i] == '#'){
if(strncmp((char *)&buf_rd[i], "########", 8) == 0){
msg = (struct jas_cmd_cli_msg *)&buf_rd[i];
sys_debug("print msg:\n");
sys_debug("valid len: %d, msg ret: %d.\n",
msg->len, msg->ret);
if(msg->ret != JAS_SUCCESS){
sys_err("Operation failed!\n");
return msg->ret;
}
if(msg->len != (len - 8)){
sys_err("Get Wrong Length: %d(%d).\n",
(len - 8), msg->len);
return JAS_FAILURE;
}
break;
}
}
if(data != NULL)
memcpy(data, msg->data, size);
return 0;
}
__sock_read_data() 定义是:
/*return length of data*/
static int __sock_read_data(int sockfd, uint8_t *buf, struct timeval *timeout, int *len){
fd_set fds;
int ret, mlen=0;
struct timeval tv1, tv2, tv_sel;
if(sockfd == 0){
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, "SOCK FD is 0");
return JAS_INV_ARG;
}
gettimeofday(&tv1, NULL);
while(1){
gettimeofday(&tv2, NULL);
if(((tv2.tv_sec-tv1.tv_sec)*1000000 + (tv2.tv_usec-tv1.tv_usec)) >=
(timeout->tv_sec*1000000 + timeout->tv_usec)){
return 0;
}
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
tv_sel.tv_sec = 0;
tv_sel.tv_usec = 100000;
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
if(ret != 0){
if(ret < 0){
sys_err("%s, %d, error, %s.\n", __func__, __LINE__, strerror(errno));
return JAS_FAILURE;
}
if(FD_ISSET(sockfd, &fds)){
mlen += read(sockfd, &buf[mlen], MSG_RD_BUF_LEN);
sys_debug("buff: %s, len: %d.\n", buf, mlen);
if((len != NULL) && (mlen < *len)){
continue;
}
return mlen;
}
return -1;
}
}
return 0;
}
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
FD_ZERO(&fds) 清空集合fds
FD_SET(sockfd, &fds); 将一个给定的文件描述符socket加入集合fds之中
ret = select(sockfd + 1, &fds, NULL, NULL, &tv_sel);
集合最大文件描述符号范围 : sockfd +1
&fds , 监视fds 文件描述符集合。
NULL: 不关心文件写变化
NULL :不关心文件异常
&tv_sel : 0 : 秒 100000: 微秒/
即select在100000 微妙内内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回。
FD_ISSET(sockfd, &fds) fds中的文件描述符 sockfd 是否可以读。
mlen += read(sockfd, &buf[mlen], MSG_RD_BUF_LEN);
可以读的话, mlen 统计 read的字节数。
__sock_read_data(ctrl->sockfd, buf_rd, &timeout, &mlen);
函数作用是:
读取ctrl->sockfd 上的内容,放入buf_rd[] 中,&mlen 是要读取的最小长度,返回读取的字节数。
以上是用户层的API 发送和接收接口函数。
********* ******************************************