#include "QMIThread.h"
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
#include <net/if.h>
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037

static size_t fibo_fread(const char *filename, void *buf, size_t size)
{
    FILE *fp = fopen(filename, "r");
    size_t n = 0;

    memset(buf, 0x00, size);

    if (fp) {
	n = fread(buf, 1, size, fp);
	if (n <= 0 || n == size) {
	    dbg_time(
		"warnning: fail to fread(%s), fread=%zd, buf_size=%zd, "
		"errno: %d (%s)",
		__func__, filename, n, size, errno, strerror(errno));
	}
	fclose(fp);
    }

    return n > 0 ? n : 0;
}

static size_t fibo_fwrite(const char *filename, const void *buf, size_t size)
{
    FILE *fp = fopen(filename, "w");
    size_t n = 0;

    if (fp) {
	n = fwrite(buf, 1, size, fp);
	if (n != size) {
	    dbg_time(
		"warnning: fail to fwrite(%s), fwrite=%zd, buf_size=%zd, "
		"errno: %d (%s)",
		__func__, filename, n, size, errno, strerror(errno));
	}
	fclose(fp);
    }

    return n > 0 ? n : 0;
}

static int fibo_iface_is_in_bridge(const char *iface)
{
    char filename[256];

    snprintf(filename, sizeof(filename), "/sys/class/net/%s/brport", iface);
    return (access(filename, F_OK) == 0 || errno != ENOENT);
}

int fibo_bridge_mode_detect(PROFILE_T *profile)
{
    const char *ifname = profile->qmapnet_adapter ? profile->qmapnet_adapter
						  : profile->usbnet_adapter;
    const char *driver;
    char bridge_mode[128];
    char bridge_ipv4[128];
    char ipv4[128];
    char buf[64];
    size_t n;
    int in_bridge;

    driver = profile->driver_name;
    snprintf(bridge_mode, sizeof(bridge_mode), "/sys/class/net/%s/bridge_mode",
	     ifname);
    snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/class/net/%s/bridge_ipv4",
	     ifname);

    if (access(bridge_mode, F_OK) && errno == ENOENT) {
	snprintf(bridge_mode, sizeof(bridge_mode),
		 "/sys/module/%s/parameters/bridge_mode", driver);
	snprintf(bridge_ipv4, sizeof(bridge_ipv4),
		 "/sys/module/%s/parameters/bridge_ipv4", driver);

	if (access(bridge_mode, F_OK) && errno == ENOENT) {
	    bridge_mode[0] = '\0';
	}
    }

    in_bridge = fibo_iface_is_in_bridge(ifname);
    if (in_bridge) {
	dbg_time("notice: iface %s had add to bridge\n", ifname);
    }

    if (in_bridge && bridge_mode[0] == '\0') {
	dbg_time("warnning: can not find bride_mode file for %s\n", ifname);
	return 1;
    }

    n = fibo_fread(bridge_mode, buf, sizeof(buf));

    if (in_bridge) {
	if (n <= 0 || buf[0] == '0') {
	    dbg_time("warnning: should set 1 to bride_mode file for %s\n",
		     ifname);
	    return 1;
	}
    } else {
	if (n <= 0 || buf[0] == '0') {
	    return 0;
	}
    }

    memset(ipv4, 0, sizeof(ipv4));

    if (strstr(bridge_ipv4, "/sys/class/net/") || profile->qmap_mode == 0 ||
	profile->qmap_mode == 1) {
	snprintf(ipv4, sizeof(ipv4), "0x%x", profile->ipv4.Address);
	dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
	fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
    } else {
	snprintf(ipv4, sizeof(ipv4), "0x%x:%d", profile->ipv4.Address,
		 profile->muxid);
	dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
	fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
    }

    return 1;
}
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
void fibo_get_driver_info(PROFILE_T *profile, RMNET_INFO *rmnet_info) {
    int ifc_ctl_sock;
    struct ifreq ifr;
    int rc;
    int request = 0x89F3;
    unsigned char data[512];

    memset(rmnet_info, 0x00, sizeof(*rmnet_info));

    ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (ifc_ctl_sock <= 0) {
        dbg_time("socket() failed: %s\n", strerror(errno));
        return;
    }

    memset(&ifr, 0, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, profile->usbnet_adapter, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
    ifr.ifr_ifru.ifru_data = (void *)data;
    rc = ioctl(ifc_ctl_sock, request, &ifr);
    if (rc < 0) {
        dbg_time("ioctl(0x%x, qmap_settings) failed: %s, rc=%d", request, strerror(errno), rc);
    }
    else {
        memcpy(rmnet_info, data, sizeof(*rmnet_info));
    }

    close(ifc_ctl_sock);
}
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
int fibo_qmap_mode_detect(PROFILE_T *profile)
{
    int n;
    char buf[128];
    char qmap_netcard[128];
    struct {
	char filename[255 * 2];
	char linkname[255 * 2];
    } * pl;

    pl = (typeof(pl))malloc(sizeof(*pl));

    snprintf(pl->linkname, sizeof(pl->linkname),
	     "/sys/class/net/%s/device/driver", profile->usbnet_adapter);
    n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
    pl->filename[n] = '\0';
    while (pl->filename[n] != '/') n--;
    profile->driver_name = strdup(&pl->filename[n + 1]);

//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
    fibo_get_driver_info(profile, &profile->rmnet_info);
    if (profile->rmnet_info.size) {
        profile->qmap_mode = profile->rmnet_info.qmap_mode;
        if (profile->qmap_mode) {
            int offset_id = profile->pdp - 1;

            if (profile->qmap_mode == 1)
                offset_id = 0;
            profile->muxid = profile->rmnet_info.mux_id[offset_id];
            profile->qmapnet_adapter = strdup( profile->rmnet_info.ifname[offset_id]);
            profile->qmap_size = profile->rmnet_info.rx_urb_size;
            profile->qmap_version = profile->rmnet_info.qmap_version;
        }

        goto _out;
    }
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037

    if (qmidev_is_pciemhi(profile->qmichannel)) {
	profile->qmap_mode = 1;
    
        if (profile->muxid == 0 || profile->muxid  == 0x81)
        {
            profile->muxid = 0x81;
        }
        else
        {
            if (profile->muxid  < 0x80)
                profile->muxid += 0x81;
            profile->qmap_mode = 2;
        }
        
	profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
       goto _final_process;
    }

    snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num",
	     profile->usbnet_adapter);

//2021-01-27 willa.liu@fibocom.com changed begin for support mantis 0068849
    if (access(pl->filename, F_OK) == 0) {
        dbg_time("access %s", pl->filename);
	if (errno != ENOENT) {
	    dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
		     strerror(errno));
	    goto _out;
	}
//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837
/*
	snprintf(pl->filename, sizeof(pl->filename),
		 "/sys/module/%s/parameters/qmap_num", profile->driver_name);
	if (access(pl->filename, R_OK)) {
	    if (errno != ENOENT) {
		dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
			 errno, strerror(errno));
		goto _out;
	    }

	    snprintf(
		pl->filename, sizeof(pl->filename),
		"/sys/class/net/%s/device/driver/module/parameters/qmap_num",
		profile->usbnet_adapter);
	    if (access(pl->filename, R_OK)) {
		if (errno != ENOENT) {
		    dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
			     errno, strerror(errno));
		    goto _out;
		}
	    }
	}
*/
//2021-02-01 willa.liu@fibocom.com changed end for support mantis 0069837
    }
    else
    {
    snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode",
        profile->usbnet_adapter);

        if (access(pl->filename, F_OK) == 0) {
        dbg_time("access %s", pl->filename);
            if (errno != ENOENT) {
                dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
                    strerror(errno));
                goto _out;
            }
        }
    }

    if(!access(pl->filename, R_OK))
    {
        n = fibo_fread(pl->filename, buf, sizeof(buf));
        if(n > 0)
        {
            profile->qmap_mode = atoi(buf);
            if(profile->qmap_mode >= 1 && qmidev_is_pciemhi(profile->qmichannel))
            {
                profile->muxid =
                    profile->pdp + 0x80;
                sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
                profile->qmapnet_adapter = strdup(qmap_netcard);
            }
            if(qmidev_is_gobinet(profile->qmichannel) || qmidev_is_qmiwwan(profile->qmichannel))
            {
                if(profile->qmap_mode > 1)
                {
                    profile->muxid =
                        profile->pdp + 0x80;
                    sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
                    profile->qmapnet_adapter = strdup(qmap_netcard);
                }
                if(profile->qmap_mode == 1)
                {
                    profile->muxid = 0x81;
                    profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
                }
            }
        }


    if (0) {
        profile->qmap_mode = atoi(buf);

        if (profile->qmap_mode > 1 && qmidev_is_gobinet(profile->qmichannel)) {
        profile->muxid =
            profile->pdp + 0x80;  // muxis is 0x8X for PDN-X
        sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
        profile->qmapnet_adapter = strdup(qmap_netcard);
        }
        if (profile->qmap_mode >= 1 && !qmidev_is_gobinet(profile->qmichannel)) {
        profile->muxid =
            profile->pdp + 0x80;  // muxis is 0x8X for PDN-X
        sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
        profile->qmapnet_adapter = strdup(qmap_netcard);
        }
        if (profile->qmap_mode == 1 && qmidev_is_gobinet(profile->qmichannel)) {
            profile->muxid = 0x81;
            profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
//2021-01-27 willa.liu@fibocom.com changed end for support mantis 0068849

	    }
	}
    } else if (qmidev_is_qmiwwan(profile->qmichannel)) {
	snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/qmimux%d",
		 profile->pdp - 1);
	if (access(pl->filename, R_OK)) {
	    if (errno != ENOENT) {
		dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
			 errno, strerror(errno));
	    }
	    goto _out;
	}

	// upstream Kernel Style QMAP qmi_wwan.c
	snprintf(pl->filename, sizeof(pl->filename),
		 "/sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter);
	n = fibo_fread(pl->filename, buf, sizeof(buf));
	if (n >= 5) {
	    profile->qmap_mode = n / 5;	 // 0x81\n0x82\n0x83\n
	    if (profile->qmap_mode > 1) {
		// PDN-X map to qmimux-X
		profile->muxid = (buf[5 * (profile->pdp - 1) + 2] - '0') * 16 +
				 (buf[5 * (profile->pdp - 1) + 3] - '0');
		sprintf(qmap_netcard, "qmimux%d", profile->pdp - 1);
		profile->qmapnet_adapter = strdup(qmap_netcard);
	    } else if (profile->qmap_mode == 1) {
		profile->muxid =
		    (buf[5 * 0 + 2] - '0') * 16 + (buf[5 * 0 + 3] - '0');
		sprintf(qmap_netcard, "qmimux%d", 0);
		profile->qmapnet_adapter = strdup(qmap_netcard);
	    }
	}
    }

_out:
    if (profile->qmap_mode) {
	profile->qmap_size = 16 * 1024;
	snprintf(pl->filename, sizeof(pl->filename),
		 "/sys/class/net/%s/qmap_size", profile->usbnet_adapter);
	if (!access(pl->filename, R_OK)) {
	    size_t n;
	    char buf[32];
	    n = fibo_fread(pl->filename, buf, sizeof(buf));
	    if (n > 0) {
		profile->qmap_size = atoi(buf);
	    }
	}
    }

_final_process:
    if (profile->qmap_mode) 
        dbg_time("qmap_mode = %d, muxid = 0x%02x, qmap_netcard = %s",
        	 profile->qmap_mode, profile->muxid, profile->qmapnet_adapter);

    free(pl);

    return 0;
}

int fibo_qmap_mode_set(PROFILE_T *profile)
{
    int n;
    char buf[128];
    struct {
	char filename[255 * 2];
	char linkname[255 * 2];
    } * pl;

    if (qmidev_is_pciemhi(profile->qmichannel))
    {
        dbg_time("pcie mode exit fibo_qmap_mode_set ");
        return 0;
    }

    pl = (typeof(pl))malloc(sizeof(*pl));

    snprintf(pl->linkname, sizeof(pl->linkname),
	     "/sys/class/net/%s/device/driver", profile->usbnet_adapter);
    n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
    pl->filename[n] = '\0';
    while (pl->filename[n] != '/') n--;
    profile->driver_name = strdup(&pl->filename[n + 1]);

    snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num",
	     profile->usbnet_adapter);
    if (access(pl->filename, R_OK)) {
	if (errno != ENOENT) {
	    dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
		     strerror(errno));
	    goto _out;
	}

	snprintf(pl->filename, sizeof(pl->filename),
		 "/sys/module/%s/parameters/qmap_mnum", profile->driver_name);
	if (access(pl->filename, R_OK)) {
	    if (errno != ENOENT) {
		dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
			 errno, strerror(errno));
		goto _out;
	    }

	    snprintf(
		pl->filename, sizeof(pl->filename),
		"/sys/class/net/%s/device/driver/module/parameters/qmap_num",
		profile->usbnet_adapter);
	    if (access(pl->filename, R_OK)) {
		if (errno != ENOENT) {
		    dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
			     errno, strerror(errno));
		    goto _out;
		}
	    }
	}
    }

    if (!access(pl->filename, R_OK)) {
	snprintf(buf, sizeof(buf), "%d", profile->pdpnum);
	n = fibo_fwrite(pl->filename, buf, strlen(buf));
    }

_out:

    free(pl);

    return 0;
}
