blob: 2203aea6b528b99dfcace4db9dad7bd00ac8b549 [file] [log] [blame]
// Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "global.h"
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "error.h"
#include "wimax.h"
#include "wm_ioctl.h"
#include "io.h"
#include "device.h"
#include "hci.h"
#include "log.h"
int get_iface_index(int fd, const char *device)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, device, IFNAMSIZ);
ifr.ifr_name[strlen(ifr.ifr_name)] = '\0';
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
xprintf(SDK_STD_ERR, "SIOCGIFINDEX(%d)\n", fd);
return sdk_set_errno(ERR_STD);
}
return ifr.ifr_ifindex;
}
static int net_open(device_t *dev)
{
int fd;
char *ifname = dev->name;
int ifindex;
fd = socket(PF_PACKET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0) {
xprintf(SDK_STD_ERR, "socket\n");
return sdk_set_errno(ERR_STD);
}
ifindex = get_iface_index(fd, ifname);
if (ifindex < 0) {
xprintf(SDK_ERR, "Cannot find the interface\n");
return sdk_set_errno(ERR_ENV);
}
xprintf(SDK_DBG, "net open, fd=%d, ifindex=%d, name=%s\n", fd, ifindex, ifname);
dev->ifindex = ifindex;
dev->net_fd = fd;
return 0;
}
int net_updown(int dev_idx, bool up)
{
device_t *dev;
struct ifreq ifr;
int fd;
int ret = 0;
xfunc_in();
if (!(dev = dm_get_dev(dev_idx)))
return -1;
if (!dev->inserted) {
xprintf(SDK_DBG, "device(%s) has been removed\n", dev->name);
goto out;
}
fd = dev->net_fd;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
xprintf(SDK_STD_ERR, "ioctl SIOCGIFFLAGS(%d) %s\n", fd, up ? "Up" : "Down");
ret = sdk_set_errno(ERR_STD);
goto out;
}
if (up)
ifr.ifr_flags |= (IFF_UP|IFF_RUNNING);
else
ifr.ifr_flags &= ~IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
xprintf(SDK_STD_ERR, "ioctl SIOCSIFFLAGS(%d) %s\n", fd, up ? "Up" : "Down");
ret = sdk_set_errno(ERR_STD);
}
out:
dm_put_dev(dev_idx);
xfunc_out();
return ret;
}
#define RESOLV_CONF_PATH "/etc/resolv.conf"
unsigned int net_get_dns_info_mtime(void)
{
const char *file = RESOLV_CONF_PATH;
struct stat stat_buf;
if (stat(RESOLV_CONF_PATH, &stat_buf) < 0) {
if (errno == ENOENT)
xprintf(SDK_DBG, "%s has been removed!\n", RESOLV_CONF_PATH);
else {
xprintf(SDK_STD_ERR, "stat(%s)\n", file);
return 0;
}
}
xprintf(SDK_DBG, "%s: st_mtime=0x%08X\n", file, (int)stat_buf.st_mtime);
return stat_buf.st_mtime;
}
int net_get_dns_info(dns_info_t *dns_info)
{
FILE *file;
int ret, i, svr_cnt, df;
char name[16], domain[128];
xfunc_in();
file = fopen(RESOLV_CONF_PATH, "r");
if (!file) {
xprintf(SDK_ERR, "fopen fail = %s(%d)\n", strerror(errno), errno);
return -errno;
}
i = svr_cnt = df = 0;
while ((ret = fscanf(file, "%s %s", name, domain)) != EOF) {
if (ret < 2) {
xprintf(SDK_STD_ERR, "fscanf ret=%d\n", ret);
ret = -1;
break;
}
if (svr_cnt < MAX_DNS_SERVERS && !strcmp(name, "nameserver")) {
strcpy(dns_info->dns_svr[svr_cnt++], domain);
xprintf(SDK_INFO, "dns_svr=%s\n", domain);
}
else if (!strcmp(name, "search")) {
strcpy(dns_info->domain, domain);
xprintf(SDK_INFO, "domain=%s\n", domain);
df++;
}
}
fclose(file);
xprintf(SDK_INFO, "svr_cnt=%d, df=%d\n", svr_cnt, df);
if (svr_cnt && df)
ret = 0;
else
ret = -1;
xfunc_out("ret=%d", ret);
return ret;
}
int net_get_dhcp_info(int dev_idx, dhcp_info_t *dhcp_info)
{
#define sizeof_sa_sin_port 2
#define p_inaddrr(x) (&ifr.x[sizeof_sa_sin_port])
device_t *dev;
struct ifreq ifr;
struct in_addr inaddr;
char *dev_name;
int fd;
int ret = 0;
if (!(dev = dm_get_dev(dev_idx)))
return -1;
dev_name = dev->name;
if (!dev->inserted) {
xprintf(SDK_ERR, "device(%s) has been removed\n", dev_name);
goto out;
}
fd = dev->net_fd;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev_name, IFNAMSIZ);
if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
xprintf(SDK_DBG, "ioctl SIOCGIFFLAGS(%d)\n", fd);
ret = sdk_set_errno(ERR_STD);
goto out;
}
memcpy(&inaddr, p_inaddrr(ifr_addr.sa_data), sizeof(inaddr));
strcpy(dhcp_info->ip, inet_ntoa(inaddr));
xprintf(SDK_INFO, "ip: %s\n", dhcp_info->ip);
ret = ioctl(fd, SIOCGIFNETMASK, &ifr);
memcpy(&inaddr, p_inaddrr(ifr_addr.sa_data), sizeof(inaddr));
if (ret < 0) {
xprintf(SDK_STD_ERR, "ioctl SIOCGIFNETMASK(%d): netmask(%s)\n",
fd, inet_ntoa(inaddr));
ret = sdk_set_errno(ERR_STD);
goto out;
}
strcpy(dhcp_info->netmask, inet_ntoa(inaddr));
xprintf(SDK_INFO, "netmask: %s\n", dhcp_info->netmask);
out:
xprintf(SDK_DBG, "net_get_dhcp_info: ret=%d\n", ret);
dm_put_dev(dev_idx);
return ret;
}
int net_ioctl(int dev_idx, wm_req_t *wmr, int cmd)
{
device_t *dev;
int ret = 0;
if (!(dev = dm_get_dev(dev_idx)))
return -1;
wmr->cmd = cmd;
xfunc_in("cmd=0x%X", wmr->cmd);
if (dev->inserted && ioctl(dev->net_fd, SIOCWMIOCTL, wmr) < 0) {
xprintf(SDK_STD_ERR, "[%d] cmd=%d\n", dev_idx, wmr->cmd);
ret = sdk_set_errno(ERR_STD);
}
xfunc_out("ret=%d", ret);
dm_put_dev(dev_idx);
return ret;
}
int net_ioctl_data(int dev_idx, bool get, u16 data_id, void *buf, int size)
{
device_t *dev;
wm_req_t wmr;
int ret = -1, cmd;
xfunc_in("dev=%d", dev_idx);
if (!(dev = dm_get_dev(dev_idx)))
return -1;
if (data_id >= SIOC_DATA_MAX) {
xprintf(SDK_ERR, "invalid data_id=%d\n", data_id);
goto out;
}
if (!buf) {
xprintf(SDK_ERR, "buffer is null\n");
goto out;
}
if (size <= 0) {
xprintf(SDK_ERR, "invalid size=%d\n", size);
goto out;
}
cmd = get ? SIOCG_DATA : SIOCS_DATA;
strncpy(wmr.ifr_name, dev->name, sizeof(wmr.ifr_name)-1);
wmr.data_id = data_id;
wmr.data.buf = buf;
wmr.data.size = size;
xprintf(SDK_INFO, "net ioctl: cmd=%s, id=%d\n", get ? "Get" : "Set", data_id);
if ((ret = net_ioctl(dev_idx, &wmr, cmd)) < 0) {
xprintf(SDK_STD_ERR, "[%d] cmd(0x%04x) data id=%d\n", dev_idx, cmd, data_id);
sdk_set_errno(ERR_STD);
}
out:
dm_put_dev(dev_idx);
xfunc_out("ret=%d", ret);
return ret < 0 ? -1 : wmr.data.size;
}
static void net_close(device_t *dev)
{
xfunc_in();
if (dev->net_fd > 0) {
xprintf(SDK_DBG, "close(%d)\n", dev->net_fd);
close(dev->net_fd);
dev->net_fd = 0;
}
xfunc_out();
}
static int io_open(device_t *dev)
{
int idx, ret;
sscanf(dev->name, "wm%d", &idx);
ret = nl_open(&dev->io_nl, NETLINK_WIMAX, dev->ifindex, idx);
if (ret < 0) {
xprintf(SDK_ERR, "Open net-link failure\n");
return sdk_set_errno(ERR_ENV);
}
xprintf(SDK_DBG, "io open\n");
return ret;
}
static void io_close(device_t *dev)
{
int ret;
xfunc_in("dev=%s", dev->name);
ret = nl_close(&dev->io_nl);
xfunc_out("ret=%d", ret);
}
int io_send(int dev_idx, void *buf, int len)
{
device_t *dev;
int ret;
if (!(dev = dm_get_dev(dev_idx)))
return -1;
ret = nl_send(&dev->io_nl, 0, buf, len);
if (ret < 0) {
if (!dev->inserted)
xprintf(SDK_DBG, "device[%d] has been removed\n", dev_idx);
else {
xprintf(SDK_STD_ERR, "[%d] nl_send\n", dev_idx);
sdk_set_errno(ERR_STD);
}
}
else if (!ret) {
xprintf(SDK_ERR, "nl_send length is 0.\n");
ret = -1;
}
else
ret = len;
dm_put_dev(dev_idx);
return ret;
}
static int io_recv(device_t *dev, char *buf, int len, int flags)
{
hnetlink_t *hnl = &dev->io_nl;
int ret;
ret = nl_recv(hnl, buf, len, flags);
if (ret < 0) {
if (!dev->inserted) {
xprintf(SDK_DBG, "device(%s) has been removed\n", dev->name);
return -1;
}
xprintf(SDK_STD_ERR, "nl_recv\n");
sdk_set_errno(ERR_STD);
}
else if (!ret) {
xprintf(SDK_ERR, "Received length is 0.\n");
ret = -1;
}
return ret;
}
void *io_receiver_thread(void *data)
{
int dev_idx = (int) data;
device_t *dev;
hci_msg_t *msg;
int fd, len = 0;
if (!(dev = dm_get_dev(dev_idx)))
goto end;
xfunc_in("name=%s", dev->name);
if ((fd = net_open(dev)) < 0)
goto out;
if ((fd = io_open(dev)) < 0)
goto out;
pthread_sem_signal(&dev->sync_lock);
while (1) {
msg = (hci_msg_t *) sdk_malloc(sizeof(hci_msg_t));
len = io_recv(dev, msg->buf, sizeof(msg->buf), 0);
xprintf(SDK_DBG, "io_recv: %02x%02x %02x%02x\n",
(u8)msg->buf[0], (u8)msg->buf[1], (u8)msg->buf[2], (u8)msg->buf[3]);
if (len < 0) {
if (errno == ENOBUFS) {
/*
If rx-netlink is full, ENOBUFS is returned,
thus, we will try to receive non-overflowed data,
but overflowed data cannot be received.
*/
xprintf(SDK_NOTICE, "ENOBUFS has been returned and try to receive again!");
continue;
}
sdk_free(msg);
break;
}
msg->len = len;
hci_send_msg(dev_idx, msg);
xprintf(SDK_DBG, "[%d] msg sent, len=%d\n", dev_idx, len);
}
out:
io_close(dev);
net_close(dev);
xfunc_out("name=%s", dev->name);
dm_put_dev(dev_idx);
end:
#if 0
if (!len)
pthread_sem_signal(&dev->sync_lock);
#endif
return NULL;
}
int io_receiver_create(int dev_idx)
{
device_t *dev;
if (!(dev = dm_get_dev(dev_idx)))
return -1;
xfunc_in("dev=%d", dev_idx);
pthread_create(&dev->ind_thr, NULL, io_receiver_thread, (void *)dev_idx);
pthread_sem_wait(&dev->sync_lock);
xfunc_out();
dm_put_dev(dev_idx);
return dev->io_nl.fd ? 0 : -1;
}
int io_receiver_delete(int dev_idx)
{
device_t *dev;
pthread_t thread;
int ret;
if (!(dev = dm_get_dev(dev_idx)))
return -1;
xfunc_in("name=%s", dev->name);
if ((thread = dev->ind_thr)) {
dev->ind_thr = (pthread_t) NULL;
pthread_cancel(thread);
xprintf(SDK_DBG, "+IO(%d) pthread_join\n", dev_idx);
ret = pthread_join(thread, NULL);
xprintf(SDK_DBG, "+IO(%d) pthread_join, ret=%d\n", dev_idx, ret);
net_close(dev);
io_close(dev);
}
xfunc_out("name=%s, thread=0x%08X", dev->name, (int) thread);
dm_put_dev(dev_idx);
return 0;
}