#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>

#include <linux/can.h>
#include <linux/can/raw.h>

#include "dy_common.h"
#define NDPRINTF 1

#ifndef NDPRINTF
    #define dprintf(...) dy_syslog(LOG_DEBUG, __VA_ARGS__)
#else
    #define dprintf(...)
#endif

int can_send(int socket, struct can_frame *pframe)
{
    int ret = 0;
    int retry_cnt = 0;

retry:
    ret = write(socket, pframe, sizeof(struct can_frame));
    if (ret < 0)
    {
        if (errno == ENOBUFS)
        {
            retry_cnt++;
            if (retry_cnt < 10)
            {
                usleep(20 * 1000);
                goto retry;
            }
        }
    }
    return ret;
}

int can_rcv(int socket, struct can_frame *pframe)
{
    if (sizeof(struct can_frame) != read(socket, pframe, sizeof(struct can_frame)))
    {
        return -1;
    }
    dprintf("can recv header:%08x len:%d", pframe->can_id, pframe->can_dlc);
    return 0;
}

/*
setnonblocking - 设置句柄为非阻塞方式
*/
static int setnonblocking(int fd)
{
    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK) == -1)
    {
        return -1;
    }
    return 0;
}


int can_open(char *dev)
{
    int socked = -1;
    struct sockaddr_can can_addr;
    struct ifreq ifr;

    if ((socked = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
    {
        dprintf("open can socket error.");
        return -1;
    }

    memset(&ifr, 0, sizeof(ifr));
    strcpy(ifr.ifr_name, dev);
    if (0 != ioctl(socked, SIOCGIFINDEX, &ifr))
    {
        dprintf("ioctl SIOCGIFINDEX failed.");
        close(socked);
        return -1;
    }

    setnonblocking(socked);
    memset(&can_addr, 0, sizeof(can_addr));
    can_addr.can_family  = AF_CAN;
    can_addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(socked, (struct sockaddr *)&can_addr, sizeof(can_addr)) < 0)
    {
        dprintf("bind can socket failed.");
        close(socked);
        return -1;
    }

    return socked;
}

void can_exit(int socketfd)
{
    close(socketfd);
}
