#include <jni.h>  
 | 
#include <string>  
 | 
#include <termios.h>  
 | 
#include <unistd.h>  
 | 
#include <sys/types.h>  
 | 
#include <sys/stat.h>  
 | 
#include <fcntl.h>  
 | 
#include <errno.h>  
 | 
#include <android/log.h>  
 | 
#include "include/gpiod.h"  
 | 
  
 | 
#define TAG "SerialApp"  
 | 
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)  
 | 
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)  
 | 
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)  
 | 
  
 | 
typedef struct comport_s  
 | 
{  
 | 
    char            devname[12];  
 | 
    unsigned int    databit, parity, stopbit, flowctrl;  
 | 
    long            baudrate;  
 | 
  
 | 
    int             fd;  
 | 
    int             frag_size;  
 | 
}comport_t;  
 | 
  
 | 
comport_t *comport;  
 | 
  
 | 
// 将int类型的波特率转换成可以识别的波特率  
 | 
static speed_t getBaudRate(int baudRate){  
 | 
    switch (baudRate) {  
 | 
        case 0:  
 | 
            return B0;  
 | 
        case 50:  
 | 
            return B50;  
 | 
        case 75:  
 | 
            return B75;  
 | 
        case 110:  
 | 
            return B110;  
 | 
        case 134:  
 | 
            return B134;  
 | 
        case 150:  
 | 
            return B150;  
 | 
        case 200:  
 | 
            return B200;  
 | 
        case 300:  
 | 
            return B300;  
 | 
        case 600:  
 | 
            return B600;  
 | 
        case 1200:  
 | 
            return B1200;  
 | 
        case 1800:  
 | 
            return B1800;  
 | 
        case 2400:  
 | 
            return B2400;  
 | 
        case 4800:  
 | 
            return B4800;  
 | 
        case 9600:  
 | 
            return B9600;  
 | 
        case 19200:  
 | 
            return B19200;  
 | 
        case 38400:  
 | 
            return B38400;  
 | 
        case 57600:  
 | 
            return B57600;  
 | 
        case 115200:  
 | 
            return B115200;  
 | 
        case 230400:  
 | 
            return B230400;  
 | 
        case 460800:  
 | 
            return B460800;  
 | 
        case 500000:  
 | 
            return B500000;  
 | 
        case 576000:  
 | 
            return B576000;  
 | 
        case 921600:  
 | 
            return B921600;  
 | 
        case 1000000:  
 | 
            return B1000000;  
 | 
        case 1152000:  
 | 
            return B1152000;  
 | 
        case 1500000:  
 | 
            return B1500000;  
 | 
        case 2000000:  
 | 
            return B2000000;  
 | 
        case 2500000:  
 | 
            return B2500000;  
 | 
        case 3000000:  
 | 
            return B3000000;  
 | 
        case 3500000:  
 | 
            return B3500000;  
 | 
        case 4000000:  
 | 
            return B4000000;  
 | 
        default:  
 | 
            return -1;  
 | 
    }  
 | 
}  
 | 
  
 | 
extern "C"  
 | 
JNIEXPORT int JNICALL  
 | 
Java_com_example_serial_RS485Control_openSerialPort(JNIEnv *env, jclass clazz, jstring path,  
 | 
                                                    jlong baud_rate, jint data_bits, jint parity,  
 | 
                                                    jint stop_bits, jint flow_control) {  
 | 
    // TODO: implement openSerialPort()  
 | 
    speed_t        speed;  
 | 
    struct termios old_cfg, new_cfg;  
 | 
    int            old_flags;  
 | 
    long           temp;  
 | 
  
 | 
    comport = (comport_t *) malloc(sizeof(comport_t));  
 | 
    if (comport == NULL) {  
 | 
        LOGE("Failed to allocate memory for comport structure.");  
 | 
        return -1;  
 | 
    }  
 | 
    memset(comport, 0, sizeof(comport));  
 | 
  
 | 
//    mSend_len = max_len;  
 | 
  
 | 
    // 检查波特率是否合法  
 | 
    {  
 | 
        speed = getBaudRate(baud_rate);  
 | 
        if (speed == -1)  
 | 
        {  
 | 
            LOGE("Invalid buad rate");  
 | 
            return -1;  
 | 
        }  
 | 
    }  
 | 
  
 | 
    // 打开串口设备  
 | 
    {  
 | 
        jboolean iscopy;  
 | 
        // 获取String字符串path中的UTF8编码,并将其转换成C中的字符串  
 | 
        const char* devname = (*env).GetStringUTFChars(path, &iscopy);  
 | 
        LOGD("Opening serial port %s with flags 0x%x", devname, O_RDWR);  
 | 
        strncpy(comport->devname, devname, 12);  
 | 
        comport->baudrate = baud_rate;  
 | 
        comport->fd = -1;  
 | 
        comport->frag_size = 128;  
 | 
        comport->databit = data_bits;  
 | 
        comport->parity = parity;  
 | 
        comport->flowctrl = flow_control;  
 | 
        comport->stopbit = stop_bits;  
 | 
  
 | 
        if( !strstr(comport->devname, "tty") )  
 | 
        {  
 | 
            LOGE("Open Not a tty device \" %s\" \n", comport->devname);  
 | 
            return -2;  
 | 
        }  
 | 
  
 | 
        comport->fd = open(comport->devname, O_RDWR);  
 | 
        if ( comport->fd < 0 )  
 | 
        {  
 | 
            if( strstr(strerror(errno), "Permission denied") != nullptr )  
 | 
                return 1;  
 | 
            LOGE("open port failed:%s\n", strerror(errno));  
 | 
            return -3;  
 | 
        }  
 | 
        LOGD("Open device %s", comport->devname);  
 | 
        // 释放JNI字符串的UTF8编码  
 | 
        (*env).ReleaseStringUTFChars(path, devname);  
 | 
  
 | 
        if( (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)))  
 | 
        {  
 | 
            if(-1 == tcflush(comport->fd, TCIOFLUSH))  
 | 
            {  
 | 
                LOGE("open port failed:%s\n", strerror(errno));  
 | 
                return -4;  
 | 
            }  
 | 
        }  
 | 
        else  
 | 
        {  
 | 
            LOGE("open port failed:%s\n", strerror(errno));  
 | 
            return -5;  
 | 
        }  
 | 
  
 | 
        if ( 0 != tcgetattr(comport->fd, &old_cfg))  
 | 
        {  
 | 
            LOGE("open port failed:%s\n", strerror(errno));  
 | 
            return -6;  
 | 
        }  
 | 
    }  
 | 
  
 | 
    {  
 | 
        LOGD("starting configuring serial port");  
 | 
  
 | 
        old_cfg.c_cflag &= ~CSIZE;  
 | 
        old_cfg.c_lflag &= ~ICANON;  
 | 
        old_cfg.c_lflag &= ~ECHO;  
 | 
        old_cfg.c_lflag &= ~ECHOE;  
 | 
        old_cfg.c_lflag &= ~ECHONL;  
 | 
        old_cfg.c_lflag &= ~ISIG;  
 | 
        old_cfg.c_iflag &= ~(IXON | IXOFF | IXANY);  
 | 
        old_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);  
 | 
        old_cfg.c_oflag &= ~OPOST;  
 | 
        old_cfg.c_oflag &= ~ONLCR;  
 | 
        old_cfg.c_cflag |= CREAD | CLOCAL;  
 | 
  
 | 
        switch (comport->databit)  
 | 
        {  
 | 
            case 7:  
 | 
                old_cfg.c_cflag |= CS7;  
 | 
                break;  
 | 
            case 6:  
 | 
                old_cfg.c_cflag |= CS6;  
 | 
                break;  
 | 
            case 5:  
 | 
                old_cfg.c_cflag |= CS5;  
 | 
                break;  
 | 
            case 8:  
 | 
                old_cfg.c_cflag |= CS8;  
 | 
                break;  
 | 
        }  
 | 
  
 | 
        switch(comport->parity)  
 | 
        {  
 | 
            case 1:  
 | 
                old_cfg.c_cflag |= (PARENB | PARODD);  
 | 
                old_cfg.c_cflag |= (INPCK | ISTRIP);  
 | 
                break;  
 | 
            case 2:  
 | 
                old_cfg.c_cflag |= PARENB;  
 | 
                old_cfg.c_cflag &= ~PARODD;  
 | 
                old_cfg.c_cflag |= (INPCK | ISTRIP);  
 | 
                break;  
 | 
            case 0:  
 | 
                old_cfg.c_cflag &= ~PARENB;  
 | 
                break;  
 | 
        }  
 | 
  
 | 
        if(1 != comport->stopbit)  
 | 
        {  
 | 
            old_cfg.c_cflag |= CSTOPB;  
 | 
        }  
 | 
        else  
 | 
        {  
 | 
            old_cfg.c_cflag &= ~CSTOPB;  
 | 
        }  
 | 
  
 | 
        // set flow control  
 | 
        // 1: software control; 2:hardware control; 0: none  
 | 
        switch(comport->flowctrl)  
 | 
        {  
 | 
            case 1:  
 | 
                old_cfg.c_cflag &= ~(CRTSCTS);  
 | 
                old_cfg.c_iflag |= (IXON | IXOFF);  
 | 
                break;  
 | 
            case 2:  
 | 
                old_cfg.c_cflag |= CRTSCTS;  
 | 
                old_cfg.c_iflag &= ~(IXON | IXOFF);  
 | 
                break;  
 | 
            case 0:  
 | 
                old_cfg.c_cflag &= ~(CRTSCTS);  
 | 
                break;  
 | 
        }  
 | 
  
 | 
  
 | 
        // set baudRate  
 | 
        cfsetispeed(&old_cfg, speed);  
 | 
        cfsetospeed(&old_cfg, speed);  
 | 
  
 | 
        new_cfg.c_cc[VMIN] = 10;  
 | 
        new_cfg.c_cc[VTIME] = 0;  
 | 
  
 | 
  
 | 
        // 将设置的串口参数应用到串口上,TCSANOW表示立即生效,(TCSADRAIN:在所有输出都被传输后生效;TCSAFLUSH:在所有输出都被传输后生效,同时丢弃所有未读取的输入)  
 | 
        if(tcsetattr(comport->fd, TCSANOW, &old_cfg))  
 | 
        {  
 | 
            LOGE("tcsetattr() failed:%s", strerror(errno));  
 | 
            close(comport->fd);  
 | 
            return -1;  
 | 
        }  
 | 
        LOGD("Connected device \" %s \" successfully\n", comport->devname);  
 | 
        LOGD("port:%s, databit:%d, stopbit:%d, parity:%d, flowctl:%d", comport->devname, comport->databit, comport->stopbit, comport->parity, comport->flowctrl);  
 | 
    }  
 | 
    return 0;  
 | 
}  
 | 
extern "C"  
 | 
JNIEXPORT int JNICALL  
 | 
Java_com_example_serial_RS485Control_closeSerialPort(JNIEnv *env, jclass clazz) {  
 | 
    // TODO: implement closeSerialPort()  
 | 
    if ( !comport )  
 | 
    {  
 | 
        LOGE("%s() get invalid input arguments.\n", __FUNCTION__ );  
 | 
        return -1;  
 | 
    }  
 | 
  
 | 
    if (comport->fd >= 0)  
 | 
    {  
 | 
        close(comport->fd);  
 | 
        LOGD("close device \" %s \" successfully\n", comport->devname);  
 | 
    }  
 | 
    comport->fd = -1;  
 | 
    free(comport);  
 | 
    comport = NULL;  
 | 
    return 0;  
 | 
}  
 | 
extern "C"  
 | 
JNIEXPORT jint JNICALL  
 | 
Java_com_example_serial_RS485Control_sendToPort(JNIEnv *env, jclass clazz, jstring msg, jint len) {  
 | 
    // TODO: implement sendToPort()  
 | 
    // msg: 表示发送的数据;len: 发送的长度  
 | 
    int   rv;  
 | 
    char *ptr, left_bytes;  
 | 
    int  send = 0;  
 | 
    const char *sendMsg = env->GetStringUTFChars(msg, 0);  
 | 
    if ( sendMsg == nullptr )  
 | 
    {  
 | 
        return -1;  
 | 
    }  
 | 
  
 | 
    if( !comport || !msg || len <= 0 )  
 | 
    {  
 | 
        LOGE("Invalid parameter.\n");  
 | 
        return -1;  
 | 
    }  
 | 
    if (comport->fd == -1)  
 | 
    {  
 | 
        LOGE("Serail not connected.\n");  
 | 
        return -2;  
 | 
    }  
 | 
  
 | 
    left_bytes = len;  
 | 
    ptr = (char *)sendMsg;  
 | 
  
 | 
    while( left_bytes > 0 )  
 | 
    {  
 | 
        /* Large data, then slice them to frag and send */  
 | 
        send = left_bytes>comport->frag_size ? comport->frag_size : left_bytes;  
 | 
  
 | 
        rv = write(comport->fd, ptr, send);  
 | 
        if( rv<0 )  
 | 
        {  
 | 
            return -4;  
 | 
        }  
 | 
  
 | 
        left_bytes -= rv;  
 | 
        ptr += rv;  
 | 
    }  
 | 
    LOGD("send %s successfully\n", sendMsg);  
 | 
  
 | 
    env->ReleaseStringUTFChars(msg, sendMsg);  
 | 
    return rv;  
 | 
}  
 | 
  
 | 
  
 | 
extern "C"  
 | 
JNIEXPORT jstring JNICALL  
 | 
Java_com_example_serial_RS485Control_recvFromPort(JNIEnv *env, jclass clazz, jint len, jint timeout) {  
 | 
    int            rv = 0;  
 | 
    int            iRet;  
 | 
    fd_set         rdfds, exfds;  
 | 
    struct timeval stTime;  
 | 
  
 | 
    jstring result;  
 | 
    char           nativeMsg[len];  
 | 
    memset(nativeMsg, 0, len);  
 | 
  
 | 
    if( !comport  || len <= 0 )  
 | 
    {  
 | 
        LOGE("Invalid parameter.\n");  
 | 
        return nullptr;  
 | 
    }  
 | 
    if (comport->fd == -1)  
 | 
    {  
 | 
        LOGE("Serail not connected.\n");  
 | 
        return nullptr;  
 | 
    }  
 | 
  
 | 
    FD_ZERO(&rdfds);  
 | 
    FD_ZERO(&exfds);  
 | 
    FD_SET(comport->fd, &rdfds);  
 | 
    FD_SET(comport->fd, &exfds);  
 | 
  
 | 
    if (0xFFFFFFFF != timeout)  
 | 
    {  
 | 
        stTime.tv_sec = (time_t) (timeout / 1000);  
 | 
        stTime.tv_usec = (long)(1000 * (timeout % 1000));  
 | 
  
 | 
        iRet = select(comport->fd + 1, &rdfds, 0, &exfds, &stTime);  
 | 
        if (0 == iRet)  
 | 
        {  
 | 
            return nullptr;  
 | 
        }  
 | 
        else if (0 < iRet)  
 | 
        {  
 | 
            if (0 != FD_ISSET(comport->fd, &exfds))  
 | 
            {  
 | 
                LOGE("Error checking recv status.\n");  
 | 
                return nullptr;  
 | 
            }  
 | 
  
 | 
            if (0 == FD_ISSET(comport->fd, &rdfds))  
 | 
            {  
 | 
                LOGE("No incoming data.\n");  
 | 
                return nullptr;  
 | 
            }  
 | 
        }  
 | 
        else  
 | 
        {  
 | 
            if (EINTR == errno)  
 | 
            {  
 | 
                LOGE("catch interrupt signal.\n");  
 | 
//                rv = 0;  
 | 
            }  
 | 
            else  
 | 
            {  
 | 
                LOGE("Check recv status failure.\n");  
 | 
//                rv = -7;  
 | 
            }  
 | 
            return nullptr;  
 | 
        }  
 | 
    }  
 | 
  
 | 
    usleep(10000); /* sleep for 10ms for data incoming */  
 | 
  
 | 
    // Get data from Com port  
 | 
    iRet = read(comport->fd, nativeMsg, len);  
 | 
    if (0 > iRet)  
 | 
    {  
 | 
        if (EINTR == errno)  
 | 
            rv = 0;      // Interrupted signal catched  
 | 
        else  
 | 
            rv = -3;      // Failed to read comport  
 | 
  
 | 
        return nullptr;  
 | 
    }  
 | 
    LOGD("Receive {%s} successfully\n", nativeMsg);  
 | 
  
 | 
    return env->NewStringUTF(nativeMsg);  
 | 
}  
 | 
  
 | 
extern "C"  
 | 
JNIEXPORT jstring JNICALL  
 | 
Java_com_example_serial_RS485Control_saveToFile(JNIEnv *env, jclass clazz, jstring msg, jint len,  
 | 
                                                jstring file_name) {  
 | 
    // TODO: implement saveToFile()  
 | 
    int mFd = -1;  
 | 
    int rv;  
 | 
    mFd = open("dev/log.txt", O_CREAT | O_RDWR | O_APPEND, 0666);  
 | 
    rv = write(mFd, msg, len);  
 | 
    if (rv < 0) {  
 | 
        LOGE("Save data to file failed:%s\n" , strerror(errno));  
 | 
        return nullptr;  
 | 
    }  
 | 
    LOGD("save received data into file successfully!\n");  
 | 
    char buf[1024];  
 | 
    rv = read(mFd, buf, len);  
 | 
    if (rv < 0) {  
 | 
        LOGE("Read data failed:%s\n" , strerror(errno));  
 | 
        return nullptr;  
 | 
    }  
 | 
    strcat(reinterpret_cast<char *>(buf), "file");  
 | 
    close(mFd);  
 | 
    return reinterpret_cast<jstring>(buf);  
 | 
}  
 | 
  
 | 
// RS485是半双工通信的,由GPIO的高低电平来控制其是接收状态还是发送状态  
 | 
// 0: 接收(默认);1:发送  
 | 
// 切换状态成功返回0,失败返回-1  
 | 
extern "C"  
 | 
JNIEXPORT jint JNICALL  
 | 
Java_com_example_serial_RS485Control_changeState(JNIEnv *env, jclass clazz, jint state) {  
 | 
    // TODO: implement changeState()  
 | 
    struct gpiod_chip  *chip;  
 | 
    struct gpiod_line  *line;  
 | 
    const char         *chipname = "gpiochip4";  
 | 
    int                 rv = 0;  
 | 
    int                 gpio = 26;  
 | 
  
 | 
  
 | 
    chip = gpiod_chip_open_by_name(chipname);  
 | 
    if( !chip )  
 | 
    {  
 | 
        LOGE("Gpio open '%s' failed:%s\n", chipname, strerror(errno));  
 | 
        return -1;  
 | 
    }  
 | 
  
 | 
    line = gpiod_chip_get_line(chip, gpio);  
 | 
    if ( !line )  
 | 
    {  
 | 
        LOGE("Get gpio[%d] failed:%s\n", gpio, strerror(errno));  
 | 
        rv = -1;  
 | 
        goto Cleanup;  
 | 
    }  
 | 
  
 | 
    rv = gpiod_line_is_used(line);  
 | 
    if( rv )  
 | 
    {  
 | 
        LOGE("GPIO[%d] is used\n", gpio);  
 | 
        rv = -1;  
 | 
        goto Cleanup;  
 | 
    }  
 | 
  
 | 
    rv = gpiod_line_request_output(line, "rs485", 0);  
 | 
    if ( rv < 0 )  
 | 
    {  
 | 
        LOGE("Set GPIO[%d] as output failed:%s\n", gpio, strerror(errno));  
 | 
        rv = -1;  
 | 
        goto Cleanup;  
 | 
    }  
 | 
  
 | 
    rv = gpiod_line_set_value(line, state);  
 | 
    if ( rv < 0 )  
 | 
    {  
 | 
        LOGE("Set GPIO[%d]'s value failed:%s\n", gpio, strerror(errno));  
 | 
        rv = -1;  
 | 
        goto Cleanup;  
 | 
    }  
 | 
    LOGD("Set GPIO[%d]'s value as %d successfully\n", gpio, state);  
 | 
    rv = 0;  
 | 
  
 | 
    Cleanup:  
 | 
    gpiod_line_release(line);  
 | 
    gpiod_chip_close(chip);  
 | 
    return rv;  
 | 
} 
 |