本例程设计知识点:
- 读取hidraw1设备数据
- 子线程应用
- 动态库的制作,及动态加载
- 回调函数的注册和使用
opos.c文件源码:
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> // for close
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "opos.h"
#define OposDisplayTest 1
#define ReadOposDisplayTest 1
typedef void (*P_ShowBarcode)(char *str) ;//定义函数指针类型
P_ShowBarcode m_ShowBarcode = NULL;//定义一个函数指针 m_ShowBarcode
int fd=0, CloseFlag=0;
pthread_t id;//定义线程ID
/*******************************************************************************
* Function Name : reg_opos
* Description : opos注册函数,用于注册回调显示条码的函数
* Input : fun:回调显示的函数指针
* Output : None
* Return : None
*******************************************************************************/
void reg_opos(void * fun)
{
m_ShowBarcode = (P_ShowBarcode)fun;
}
/*******************************************************************************
* Function Name : open_opos
* Description : 打开opos函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void open_opos(void)
{
void *arg = NULL;//定义一个空指针
int ret;
fd = open("/dev/hidraw1", O_RDWR);
#ifdef OposDisplayTest
printf("fd = %d \n", fd);
#endif
if(fd<0)
{
#ifdef OposDisplayTest
printf("Open error2\n");
#endif
}
else
{
#ifdef OposDisplayTest
printf("Open ok\n");
#endif
ret=pthread_create(&id,NULL,(void *) ReadOposData,(void*)(&arg));
if(ret!=0)
{
#ifdef OposDisplayTest
printf ("Create pthread error!\n");
#endif
exit (1);
}
}
}
/*******************************************************************************
* Function Name : close_opos
* Description : 关闭opos函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void close_opos(void)
{
CloseFlag=1;//关闭标志置1
pthread_join(id,NULL);
#ifdef OposDisplayTest
printf("close\n");
#endif
close(fd);
}
/*******************************************************************************
* Function Name : ReadOposData
* Description : 读取opos数据的函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void ReadOposData(void *arg)
{
char barcode[2048];
int count=64, read_num=0;
int remain_len=0, save_len=0, code_len=0;
unsigned char buffer[64];
CloseFlag=0;//清空关闭标志
while(CloseFlag==0)
{
#ifdef ReadOposDisplayTest
printf("read...\n");
#endif
memset(barcode, 0, sizeof(barcode));//清零
code_len = 0;
remain_len = 0;
save_len = 0;
read_num = read(fd,buffer,count);//读取opos数据包
if (read_num<0)
{
#ifdef ReadOposDisplayTest
printf("read fail\n");
#endif
}
else
{
code_len = buffer[0] + (buffer[1]<<8);//获取整个数据包的长度
remain_len = code_len+2;//包头的长度不包含最后两个字节的校验码
#ifdef ReadOposDisplayTest
printf("remain_len = %d\n", remain_len);
#endif
if(remain_len <= read_num)//一个opos包接收完所有数据
{
memmove(barcode, buffer+4, remain_len-4);//解析出条码数据
save_len += remain_len-4;
remain_len = 0;
}
else
{
memmove(barcode, buffer+4, read_num-4);//解析出条码数据
save_len += read_num-4;
remain_len -= read_num;
while(remain_len)
{
memset(buffer, 0, sizeof(buffer));//清零
read_num = read(fd,buffer,count);//读取opos数据包
if (read_num<0)
{
#ifdef ReadOposDisplayTest
printf("read fail\n");
#endif
}
if(remain_len>=read_num)//数据包中不包含最后两个字节的校验符。
{
memmove(barcode+save_len, buffer, read_num);//解析出条码数据
save_len += read_num;
remain_len -= read_num;
}
else
{
memmove(barcode+save_len, buffer, remain_len);//解析出条码数据
save_len += remain_len;
remain_len = 0;
}
}
}
#ifdef ReadOposDisplayTest
printf("save_len = %d\n", remain_len);
#endif
barcode[save_len-2] = '\0';//设置字符串换行符
m_ShowBarcode(barcode);//回调main函数中的函数,用于显示条码
}
}
}
//说明:此函数涉及到从设备的打包格式中提出数据。
opos.h文件源码:
#ifndef OPOS_H
#define OPOS_H
void reg_opos(void * fun);
void open_opos(void);
void close_opos(void);
void ReadOposData(void *arg);
#endif
main.c文件源码:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <signal.h>
#include <errno.h>
#include "opos.h"
int listnum =0;
/*******************************************************************************
* Function Name : error_quit
* Description : 输出错误信息并退出
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void error_quit(const char *str)
{
fprintf(stderr, "%s\n", str);
exit(1);
}
/*******************************************************************************
* Function Name : ShowBarcode
* Description : 显示条码的函数
* Input : str需要显示的条码字符串的指针
* Output : None
* Return : None
*******************************************************************************/
void ShowBarcode(char *str)
{
listnum ++;
printf("%d: %s\n", listnum,str);
}
/*******************************************************************************
* Function Name : main
* Description : 主函数
* Input : argc:未调用,argv:未调用
* Output : None
* Return : 0
*******************************************************************************/
int main(int argc, char * argv [ ])
{
void *plib; //指向so文件的指针
typedef void (*FUN_REG_OPOS)(void*);
typedef void (*FUN_OPEN_OPOS)();
typedef void (*FUN_CLOSE_OPOS)();
FUN_REG_OPOS funRegOpos = NULL; //函数指针
FUN_OPEN_OPOS funOpenOpos = NULL; //函数指针
FUN_CLOSE_OPOS funCloseOpos = NULL; //函数指针
char command_data = '0';
//打开so文件
//为了方便演示,我将库文件和可执行文件放在同一个目录下
plib = dlopen("./libopos.so", RTLD_NOW | RTLD_GLOBAL);
if( NULL == plib )
error_quit("Can't open the libopos.so");
funRegOpos = dlsym(plib, "reg_opos");
if( NULL == funRegOpos )
error_quit("Can't load function 'reg_opos'");
funOpenOpos = dlsym(plib, "open_opos");
if( NULL == funOpenOpos )
error_quit("Can't load function 'open_opos'");
funCloseOpos = dlsym(plib, "close_opos");
if( NULL == funCloseOpos )
error_quit("Can't load function 'close_opos'");
//注册函数
funRegOpos((void*)ShowBarcode);
//打开opos
funOpenOpos();
while(1)
{
scanf("%c",&command_data);
if(command_data=='c')
{
printf("Close opos!\n");
break;
}
}
//关闭opos
funCloseOpos();
//关闭so文件
dlclose(plib);
printf("end!\n");
return 0;
}
ubuntu终端运行:
命令1:gcc opos.c -fPIC -shared -o libopos.so -lpthread
作用:生成动态库文件 opos.c。
命令2:gcc main.c -o main -ldl
作用:编译main.c文件,调用了动态库,增加“-ldl”。
命令3:sudo ./main
作用:使用管理员权限运行main函数。
展示效果: