blob: b1c548641873178e5a94ec4cfdc0bb3a414265bf [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.
#if defined(CONFIG_DM_INTERFACE)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <wchar.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
#include <linux/types.h>
#endif
#include <linux/if_packet.h>
#include <assert.h>
#include <pthread.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "cm.h"
#include "exitcb.h"
#if defined(CONFIG_DM_NET_DEVICE)
#define ETHERNET_DEVICE CONFIG_DM_NET_DEVICE
#else
#define ETHERNET_DEVICE "eth0"
#endif
#define DM_TOOL_PORT 9801
//#define DM_IF_DEBUG
typedef struct dm_sub_s {
unsigned short code;
unsigned short op;
unsigned short result;
} __attribute__((packed)) dm_sub_t;
/*HCI*/
#define WIMAX_MON_TOOL_REQUEST 0x0326
#define WIMAX_MON_TOOL_REPORT 0x8327
#define MANUAL_CONNECT 0x0014
#define CODE_CONNECT 0x0015
#define CODE_DISCONNECT 0x0016
#define OP_REQUEST 0x0000
#define OP_RESPONSE 0x0001
#define RET_SUCCESS 0x0000
#define RET_FAILURE 0x0001
extern int get_first_odev(void);
extern APIHAND cm_api_handle;
static pthread_t dm_thread;
static int dm_listener;
int dmif_init(void);
int dmif_deinit(void);
static int get_ip(const char *dev_name, char *ip)
{
#define sizeof_sa_sin_port 2
#define inaddrr(x) (*(struct in_addr *) &ifr.x[sizeof_sa_sin_port])
struct ifreq ifr;
int fd;
int ret = 0;
fd = socket(PF_PACKET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0) {
cm_printf("Socket error %s(%d)\n", strerror(errno), errno);
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev_name, IFNAMSIZ);
if ((ret = ioctl(fd, SIOCGIFADDR, &ifr)) < 0) {
cm_printf("ioctl SIOCGIFFLAGS %s(%d)\n", strerror(errno), errno);
goto out;
}
strcpy(ip, inet_ntoa(inaddrr(ifr_addr.sa_data)));
#if defined(DM_IF_DEBUG)
cm_printf("ip: %s\n", ip);
#endif
out:
close(fd);
return ret;
}
int send_dm_tool(void *data, int len)
{
cm_common_conf_t *pconf = &cm_common_conf;
#if defined(DM_IF_DEBUG)
hci_t *hci = (hci_t *) data;
#endif
int cfd = pconf->dm_interface_cfd;
int ret;
if (cfd <= 0) {
cm_printf("Invalied DM Tool's file descriptor\n");
return 0;
}
ret = send(cfd, data, len, 0);
#if defined(DM_IF_DEBUG)
cm_printf("Sent: 0x%04X, %d(%d)\n", _B2H(hci->cmd_evt), _B2H(hci->length), ret);
#endif
return ret;
}
int dm_ind_connetion(int connect, int success)
{
char buf[HCI_MAX_PACKET] __attribute__((aligned(4)));
hci_t *hci = (hci_t *) buf;
dm_sub_t *dsub = (dm_sub_t *) hci->data;
hci->cmd_evt = _H2B(WIMAX_MON_TOOL_REPORT);
hci->length = _H2B(sizeof(dm_sub_t));
if (connect)
dsub->code = _H2B(CODE_CONNECT);
else
dsub->code = _H2B(CODE_DISCONNECT);
dsub->op = _H2B(OP_RESPONSE);
if (success)
dsub->result = _H2B(RET_SUCCESS);
else
dsub->result = _H2B(RET_FAILURE);
return send_dm_tool(buf, HCI_HEADER_SIZE+sizeof(dm_sub_t));
}
static int dm_handle_host_cmd(GDEV_ID_P pID, hci_t *hci)
{
dm_sub_t *dsub = (dm_sub_t *) hci->data;
WIMAX_API_NSP_INFO NSPInfo;
int list_cnt = 1;
int ret = 0;
if (_B2H(hci->cmd_evt) == WIMAX_MON_TOOL_REQUEST) {
switch (_B2H(dsub->code)) {
case CODE_CONNECT:
if (GAPI_GetNetworkList(pID, &NSPInfo, (UINT32 *) &list_cnt)
!= GCT_API_RET_SUCCESS) {
cm_eprintf("Get network list failure\n");
ret = -1;
}
else if (!list_cnt) {
cm_eprintf("Network list is 0\n");
ret = -1;
}
else if (GAPI_CmdConnectToNetwork(pID, NSPInfo.NSPName, 0)
!= GCT_API_RET_SUCCESS) {
cm_eprintf("Connect failure\n");
ret = -1;
}
if (ret < 0)
dm_ind_connetion(1, 0);
break;
case CODE_DISCONNECT:
if (GAPI_CmdDisconnectFromNetwork(pID) != GCT_API_RET_SUCCESS) {
cm_eprintf("Disconnect failure\n");
dm_ind_connetion(0, 0);
}
break;
case MANUAL_CONNECT:
if (_B2H(dsub->op) == 0)
cm_enable_auto_connet(1, get_first_odev());
else
cm_enable_auto_connet(0, 0);
break;
}
ret = 1;
}
return ret;
}
static int accept_dm_hci(int sfd)
{
cm_common_conf_t *pconf = &cm_common_conf;
char buf[HCI_MAX_PACKET] __attribute__((aligned(4)));
hci_t *hci = (hci_t *) buf;
GDEV_ID ID;
struct sockaddr_in saddr_c;
int cfd, ret, len, hci_len;
unsigned int ulen = sizeof(struct sockaddr);
cm_printf("Wait for DM tool to be accepted...\n");
if ((pconf->dm_interface_cfd = cfd =
accept(sfd, (struct sockaddr *)&saddr_c, &ulen)) < 0) {
cm_printf("accept error %s(%d)\n", strerror(errno), errno);
return -1;
}
cm_printf("Accepted %s\n", inet_ntoa(saddr_c.sin_addr));
ID.apiHandle = cm_api_handle;
while (1) {
if ((len = ret = recv(cfd, buf, sizeof(buf), 0)) < 0) {
if (errno == ECONNRESET) {
ret = 0;
break;
}
cm_printf("read error %s(%d)\n", strerror(errno), errno);
break;
}
if (!ret)
break;
if (ret < HCI_HEADER_SIZE) {
cm_printf("Invalid length(%d)\n", ret);
continue;
}
if (!(ID.deviceIndex = get_first_odev())) {
cm_printf("There is no any attached device!!\n");
continue;
}
hci = (hci_t *) buf;
while (len >= HCI_HEADER_SIZE) {
hci_len = _B2H(hci->length);
#if defined(DM_IF_DEBUG)
cm_printf("DM: 0x%04X(%d), len=%d\n", _B2H(hci->cmd_evt), hci_len, len);
#endif
if (!dm_handle_host_cmd(&ID, hci)) {
if (GAPI_WriteHCIPacket(&ID, (char *) hci, HCI_HEADER_SIZE+hci_len)
!= GCT_API_RET_SUCCESS) {
cm_eprintf("Write HCI Failed\n");
goto out;
}
}
hci = (hci_t *) ((char *) hci + HCI_HEADER_SIZE + hci_len);
len -= HCI_HEADER_SIZE + hci_len;
}
if (len > 0)
cm_printf("Unhandled length is %d\n", len);
}
out:
pconf->dm_interface_cfd = -1;
close(cfd);
return ret;
}
static void *dm_receiver_thread(void *data)
{
const char *dev_name = (const char *) data;
struct sockaddr_in saddr_s;
char ip[32];
int port = DM_TOOL_PORT;
int ret;
if (get_ip(dev_name, ip) < 0)
return NULL;
memset(&saddr_s, 0, sizeof(saddr_s));
saddr_s.sin_family = AF_INET; /* AF_INET, PF_INET are same */
if (inet_aton(ip, &saddr_s.sin_addr) < 0) {
cm_printf("Fail: inet_aton %s\n", ip);
ret = -1;
goto out;
}
saddr_s.sin_port = htons(port);
if ((dm_listener = ret = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) {
cm_printf("DM socket error %s(%d)\n", strerror(errno), errno);
goto out;
}
if ((ret = bind(dm_listener, (struct sockaddr *) &saddr_s,
sizeof(struct sockaddr_in))) < 0) {
cm_printf("DM bind error %s(%d)\n", strerror(errno), errno);
goto out;
}
listen(dm_listener, 1);
while (1) {
if ((ret = accept_dm_hci(dm_listener)) < 0) {
cm_printf("accept_dm_hci error\n");
break;
}
}
out:
if (dm_listener > 0) {
close(dm_listener);
dm_listener = 0;
}
return NULL;
}
static int dm_receiver_create(void)
{
pthread_create(&dm_thread, NULL, dm_receiver_thread, ETHERNET_DEVICE);
return 0;
}
static int dm_stop_bmode(void)
{
const u8 stop_packet[] = {0x00, 0x04, 0x00, 0x02, 0xAD, 0xBC, 0x7E};
GDEV_ID ID;
char buf[HCI_MAX_PACKET] __attribute__((aligned(4)));
hci_t *hci = (hci_t *) buf;
int ret = 0, len;
ID.apiHandle = cm_api_handle;
if (!(ID.deviceIndex = get_first_odev())) {
cm_printf("There is no any attached device!!\n");
return -1;
}
len = sizeof(stop_packet);
hci->cmd_evt = _H2B(WIMAX_DM_CMD);
hci->length = _H2B(len);
memcpy(hci->data, stop_packet, len);
if (GAPI_WriteHCIPacket(&ID, buf, HCI_HEADER_SIZE+len) != GCT_API_RET_SUCCESS) {
cm_printf("Write HCI Failed\n");
ret = -1;
}
return ret;
}
static int dm_receiver_delete(void)
{
pthread_t thread;
int ret = 0;
if ((thread = dm_thread)) {
dm_thread = (pthread_t) NULL;
pthread_cancel(thread);
pthread_join(thread, NULL);
if (dm_listener > 0) {
close(dm_listener);
dm_listener = 0;
}
ret = dm_stop_bmode();
}
return ret;
}
static void cb_dm_deinit(int signal/*, void *info*/)
{
dmif_deinit();
}
int dmif_init(void)
{
int ret;
register_exit_cb(cb_dm_deinit);
ret = dm_receiver_create();
return ret;
}
int dmif_deinit(void)
{
int ret;
unregister_exit_cb(cb_dm_deinit);
ret = dm_receiver_delete();
return ret;
}
#endif