summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChinh Tran <chinht@codeaurora.org>2011-01-28 16:53:08 -0800
committerNiranjan Pendharkar <npendhar@codeaurora.org>2011-02-18 17:43:25 -0800
commit38af2cb01b5928ad0cb3a5ed6ab8b694172d6f2e (patch)
tree546ccad58eeb997c11eea5b6319d128fb2eae1ec
parentd6f024d0e13b993ca1841f5518c4983f1402510e (diff)
downloadandroid_external_connectivity-38af2cb01b5928ad0cb3a5ed6ab8b694172d6f2e.tar.gz
android_external_connectivity-38af2cb01b5928ad0cb3a5ed6ab8b694172d6f2e.tar.bz2
android_external_connectivity-38af2cb01b5928ad0cb3a5ed6ab8b694172d6f2e.zip
external/connectivity: Add target file descriptor
Specify which target the request and message are from using target file descriptor. Added commands to process dormancy change, default network Reference CnE change to handle target file descriptor Change-Id: Ic85083625e32c80946b7f42b7cb41b1c51e05398
-rw-r--r--[-rwxr-xr-x]cnd/inc/cnd.h2
-rw-r--r--cnd/inc/cnd_commands.h10
-rw-r--r--cnd/inc/cnd_event.h6
-rw-r--r--cnd/inc/cnd_unsol_messages.h5
-rw-r--r--[-rwxr-xr-x]cnd/src/Android.mk0
-rw-r--r--cnd/src/cnd_event.cpp38
-rw-r--r--cnd/src/cnd_process.cpp98
-rwxr-xr-x[-rw-r--r--]include/cne/cne.h54
-rw-r--r--reference-cne/src/CneSvc.cpp3
9 files changed, 151 insertions, 65 deletions
diff --git a/cnd/inc/cnd.h b/cnd/inc/cnd.h
index 1d6bec3..fff057b 100755..100644
--- a/cnd/inc/cnd.h
+++ b/cnd/inc/cnd.h
@@ -1,6 +1,6 @@
/*
** Copyright 2006, The Android Open Source Project
-** Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+** Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/cnd/inc/cnd_commands.h b/cnd/inc/cnd_commands.h
index 0db8812..4e8c968 100644
--- a/cnd/inc/cnd_commands.h
+++ b/cnd/inc/cnd_commands.h
@@ -1,6 +1,6 @@
/*
** Copyright 2006, The Android Open Source Project
-** Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+** Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -31,6 +31,8 @@
{CNE_REQUEST_CONFIG_IPROUTE2_CMD, dispatchIproute2Cmd, responseVoid},
{CNE_NOTIFY_TIMER_EXPIRED_CMD, dispatchVoid, responseVoid},
{CNE_REQUEST_START_FMC_CMD, dispatchString, responseVoid},
- {CNE_REQUEST_STOP_FMC_CMD, dispatchVoid, responseVoid}
-
-
+ {CNE_REQUEST_STOP_FMC_CMD, dispatchVoid, responseVoid},
+ {CNE_REQUEST_UPDATE_WWAN_DORMANCY_INFO_CMD, dispatchInt, responseVoid},
+ {CNE_REQUEST_UPDATE_DEFAULT_NETWORK_INFO_CMD, dispatchInt, responseVoid},
+ {CNE_NOTIFY_SOCKET_CLOSED_CMD, dispatchVoid, responseVoid},
+ {CNE_REQUEST_VENDOR_CMD, dispatchRaw, responseVoid}
diff --git a/cnd/inc/cnd_event.h b/cnd/inc/cnd_event.h
index 8c47ee1..07b4fff 100644
--- a/cnd/inc/cnd_event.h
+++ b/cnd/inc/cnd_event.h
@@ -2,7 +2,7 @@
#define CNE_EVENT_H
/*
** Copyright 2006, The Android Open Source Project
-** Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+** Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -19,11 +19,11 @@
#include "cne.h"
// Max number of fd's we watch at any one time. Increase if necessary.
-#define MAX_FD_EVENTS 8
+#define MAX_FD_EVENTS 128
typedef void (*cnd_event_cb)(int fd, void *userdata);
typedef int (*cneIntFnType)(void);
-typedef void (*cneProcessCmdFnType)(int, void*,size_t);
+typedef void (*cneProcessCmdFnType)(int, int, void*,size_t);
typedef void (*cneRegMsgCbFnType)(cne_messageCbType);
struct cnd_event {
diff --git a/cnd/inc/cnd_unsol_messages.h b/cnd/inc/cnd_unsol_messages.h
index 4d809f2..6d41074 100644
--- a/cnd/inc/cnd_unsol_messages.h
+++ b/cnd/inc/cnd_unsol_messages.h
@@ -1,6 +1,6 @@
/*
** Copyright 2006, The Android Open Source Project
-** Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+** Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -27,4 +27,5 @@
{CNE_REQUEST_START_SCAN_WLAN_MSG, responseVoid},
{CNE_NOTIFY_INFLIGHT_STATUS_MSG, responseInts},
{CNE_NOTIFY_FMC_STATUS_MSG, responseInts},
- {CNE_NOTIFY_HOST_ROUTING_IP_MSG, responseString}
+ {CNE_NOTIFY_HOST_ROUTING_IP_MSG, responseString},
+ {CNE_NOTIFY_VENDOR_MSG, responseRaw}
diff --git a/cnd/src/Android.mk b/cnd/src/Android.mk
index aa73d21..aa73d21 100755..100644
--- a/cnd/src/Android.mk
+++ b/cnd/src/Android.mk
diff --git a/cnd/src/cnd_event.cpp b/cnd/src/cnd_event.cpp
index 5e52e2e..e6a7823 100644
--- a/cnd/src/cnd_event.cpp
+++ b/cnd/src/cnd_event.cpp
@@ -1,6 +1,6 @@
/*
** Copyright 2006, The Android Open Source Project
-** Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+** Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -18,6 +18,10 @@
#define LOG_TAG "CND_EVENT"
#define LOCAL_TAG "CND_EVENT_DEBUG"
+#define LOG_NDEBUG 0
+#define LOG_NDDEBUG 0
+#define LOG_NIDEBUG 0
+
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
@@ -103,22 +107,22 @@ static void removeWatch(struct cnd_event * ev, int index)
static void processReadReadyEvent(fd_set * rfds, int n)
{
- CNE_LOGV("processReadReadyEvent: n=%d, rfds0=%ld", n, rfds->fds_bits[0]);
+ CNE_LOGV("processReadReadyEvent: num of fd set=%d, rfds0=%ld", n, rfds->fds_bits[0]);
+
MUTEX_ACQUIRE();
for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) {
-
struct cnd_event * rev = watch_table[i];
-
- if (rev != NULL)
- CNE_LOGV("processReadReadyEvent: i=%d, fd=%d, rfds0=%ld", i, rev->fd, rfds->fds_bits[0]);
- else
- CNE_LOGV("processReadReadyEvent: i=%d, rev is NULL", i);
-
+ CNE_LOGD("processReadReadyEvent: watch_table index=%d", i);
+ if (rev == NULL) {
+ CNE_LOGD("processReadReadyEvent: REV is NULL at i=%d", i);
+ }
if (rev != NULL && FD_ISSET(rev->fd, rfds)) {
addToList(rev, &pending_list);
- CNE_LOGV("processReadReadyEvent: add to pendingList fd=%d", rev->fd);
+ CNE_LOGV("processReadReadyEvent: add to pendingList at watch_table"
+ "index=%d, fd=%d", i, rev->fd);
if (rev->persist == 0) {
+ CNE_LOGV("processReadReadyEvent: Should not get here, fd=%d", rev->fd);
removeWatch(rev, i);
}
n--;
@@ -214,20 +218,21 @@ void cnd_event_set(struct cnd_event * ev, int fd, int persist, cnd_event_cb func
void cnd_event_add(struct cnd_event * ev)
{
+ CNE_LOGV("cnd_event_add-called:fd=%d, readFds0=%ld", ev->fd, readFds.fds_bits[0]);
+
MUTEX_ACQUIRE();
for (int i = 0; i < MAX_FD_EVENTS; i++) {
if (watch_table[i] == NULL) {
watch_table[i] = ev;
ev->index = i;
- CNE_LOGV("cnd_event_add-before: add at i=%d for fd=%d, readFds0=%ld", i, ev->fd, readFds.fds_bits[0]);
-
+ CNE_LOGV("cnd_event_add-before: add at index=%d for fd=%d, readFds0=%ld",
+ i, ev->fd, readFds.fds_bits[0]);
FD_SET(ev->fd, &readFds);
-
if (ev->fd >= nfds)
- nfds = ev->fd+1;
- CNE_LOGV("cnd_event_add-after: add at i=%d for fd=%d, readFds0=%ld", i, ev->fd, readFds.fds_bits[0]);
-
+ nfds = ev->fd+1;
+ CNE_LOGV("cnd_event_add-after: add at index=%d for fd=%d, readFds0=%ld",
+ i, ev->fd, readFds.fds_bits[0]);
break;
}
}
@@ -241,7 +246,6 @@ void cnd_event_add(struct cnd_event * ev)
void cnd_event_del(struct cnd_event * ev)
{
-
CNE_LOGV("cnd_event_del: index=%d", ev->index);
MUTEX_ACQUIRE();
diff --git a/cnd/src/cnd_process.cpp b/cnd/src/cnd_process.cpp
index 731d1d0..819b3b9 100644
--- a/cnd/src/cnd_process.cpp
+++ b/cnd/src/cnd_process.cpp
@@ -1,6 +1,6 @@
/*
** Copyright 2006, The Android Open Source Project
-** Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+** Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
*/
#define LOG_TAG "CND_PROCESS"
#define LOCAL_TAG "CND_PROCESS_DEBUG"
+#define LOG_NDEBUG 0
+#define LOG_NDDEBUG 0
+#define LOG_NIDEBUG 0
#include <cutils/sockets.h>
#include <cutils/jstring.h>
@@ -27,7 +30,6 @@
#include <cutils/jstring.h>
#include <sys/types.h>
-#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -98,9 +100,6 @@ static pthread_t s_tid_dispatch;
static int s_started = 0;
static int s_fdListen = -1;
-static int s_fdCommand = -1;
-
-static int cnmSvcFd = -1;
static struct cnd_event s_commands_event[MAX_FD_EVENTS];
static struct cnd_event s_listen_event;
@@ -125,6 +124,7 @@ static RequestInfo *s_toDispatchTail = NULL;
static void dispatchVoid (Parcel& p, RequestInfo *pRI);
static void dispatchString (Parcel& p, RequestInfo *pRI);
static void dispatchStrings (Parcel& p, RequestInfo *pRI);
+static void dispatchInt (Parcel& p, RequestInfo *pRI);
static void dispatchInts (Parcel& p, RequestInfo *pRI);
static void dispatchWlanInfo(Parcel &p, RequestInfo *pRI);
static void dispatchWwanInfo(Parcel &p, RequestInfo *pRI);
@@ -177,15 +177,11 @@ void cnd_sendUnsolicitedMsg(int targetFd, int msgType, int dataLen, void *data)
{
int fd;
- if (targetFd == 0) // TODO find the correct fd, who keeps track of it?
- fd = cnmSvcFd;
- else
- fd = targetFd;
-
CNE_LOGV ("cnd_sendUnsolicitedMsg: Fd=%d, msgType=%d, datalen=%d\n",
targetFd, msgType, dataLen);
- unsolicitedMessage(msgType, data, dataLen, fd);
+ //unsolicitedMessage(msgType, data, dataLen, fd);
+ unsolicitedMessage(msgType, data, dataLen, targetFd);
}
@@ -194,7 +190,10 @@ static void
processCommand (int command, void *data, size_t datalen, CND_Token t)
{
- CNE_LOGV ("processCommand: command=%d, datalen=%d", command, datalen);
+
+ RequestInfo *reqInfo = (RequestInfo *)t;
+ CNE_LOGV ("processCommand: command=%d, datalen=%d, fd=%d", command, datalen, reqInfo->fd);
+
/* Special handling for iproute2 command to setup iproute2 table */
if ((command == CNE_REQUEST_CONFIG_IPROUTE2_CMD) && (cneServiceEnabled))
@@ -256,7 +255,7 @@ processCommand (int command, void *data, size_t datalen, CND_Token t)
if(cne_processCommand != NULL)
{
- cne_processCommand(command, data, datalen);
+ cne_processCommand(reqInfo->fd, command, data, datalen);
}
else
{
@@ -379,6 +378,24 @@ dispatchStrings (Parcel &p, RequestInfo *pRI)
/** Callee expects const int * */
static void
+dispatchInt (Parcel &p, RequestInfo *pRI)
+{
+ status_t status;
+ int32_t value;
+
+ status = p.readInt32 (&value);
+ if (status != NO_ERROR) {
+ CNE_LOGD ("dispatchInt: invalid block");
+ return;
+ }
+
+ CNE_LOGV ("dispatchInt: int32_t=%d", value);
+
+ processCommand(pRI->pCI->commandNumber, const_cast<int *>(&value), sizeof(int32_t), pRI);
+}
+
+/** Callee expects const int * */
+static void
dispatchInts (Parcel &p, RequestInfo *pRI)
{
int32_t count;
@@ -636,8 +653,9 @@ writeData(int fd, const void *buffer, size_t len)
toWrite = (const uint8_t *)buffer;
- CNE_LOGV ("writeData: len=%d",len);
+ CNE_LOGV ("writeData: fd=%d, len=%d, offset=%d", fd, len, writeOffset);
while (writeOffset < len) {
+ CNE_LOGV ("writeData in loop: fd=%d, len=%d, offset=%d", fd, len, writeOffset);
ssize_t written;
do {
written = write (fd, toWrite + writeOffset,
@@ -659,7 +677,6 @@ writeData(int fd, const void *buffer, size_t len)
static int
sendResponseRaw (const void *data, size_t dataSize, int fdCommand)
{
- int fd = fdCommand;
int ret;
uint32_t header;
@@ -678,13 +695,16 @@ sendResponseRaw (const void *data, size_t dataSize, int fdCommand)
header = htonl(dataSize);
- ret = writeData(fd, (void *)&header, sizeof(header));
+
+ CNE_LOGD("sendResponseRaw: fd=%d, datasize=%d, header=%d",
+ fdCommand, dataSize, header);
+ ret = writeData(fdCommand, (void *)&header, sizeof(header));
if (ret < 0) {
return ret;
}
- writeData(fd, data, dataSize);
+ writeData(fdCommand, data, dataSize);
if (ret < 0) {
pthread_mutex_unlock(&s_writeMutex);
@@ -800,6 +820,7 @@ static int responseRaw(Parcel &p, void *response, size_t responselen)
if (response == NULL) {
p.writeInt32(-1);
} else {
+ CNE_LOGD("responseRaw len=%d\n", responselen);
p.writeInt32(responselen);
p.write(response, responselen);
}
@@ -966,6 +987,9 @@ processCommandBuffer(void *buffer, size_t buflen, int fd)
status = p.readInt32(&request);
status = p.readInt32 (&token);
+ CNE_LOGD("processCommandBuffer: fd=%d, requestcode=%d, token=%d",
+ fd, request, token);
+
if (status != NO_ERROR) {
CNE_LOGD("processCommandBuffer: invalid request block");
return -1;
@@ -973,7 +997,7 @@ processCommandBuffer(void *buffer, size_t buflen, int fd)
if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) {
CNE_LOGD("processCommandBuffer: unsupported request code %d token %d",
- request, token);
+ request, token);
return -1;
}
@@ -1014,7 +1038,7 @@ static void processCommandsCallback(int fd, void *param)
for (;;) {
/* loop until EAGAIN/EINTR, end of stream, or other error */
ret = record_stream_get_next(p_rs, &p_record, &recordlen);
- CNE_LOGV ("processCommandsCallback: len=%d, ret=%d", recordlen, ret);
+ CNE_LOGV ("processCommandsCallback: len=%d, ret=%d, fd=%d", recordlen, ret, fd);
if (ret == 0 && p_record == NULL) {
CNE_LOGV ("processCommandsCallback: end of stream");
break;
@@ -1025,7 +1049,8 @@ static void processCommandsCallback(int fd, void *param)
}
}
-
+ CNE_LOGV ("processCommandsCallback: exit loop, ret=%d, errno=%d, fd=%d",
+ ret, errno, fd);
if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) {
/* fatal error or end-of-stream */
if (ret != 0) {
@@ -1033,12 +1058,22 @@ static void processCommandsCallback(int fd, void *param)
} else {
CNE_LOGD("EOS. Closing command socket.");
}
- close(s_fdCommand);
- s_fdCommand = -1;
- command_index = 0;
+ close(fd);
+ /* remove from watch list */
+ for(int i=0; i<MAX_FD_EVENTS; i++) {
+ CNE_LOGD("processCommandsCallback: fd=%d, commandFd=%d",
+ fd, s_commands_event[i].fd);
+ if (s_commands_event[i].fd == fd) {
+ CNE_LOGD("processCommandsCallback: matched fd=%d for index=%d",
+ fd, i);
+ cnd_event_del(&s_commands_event[i]);
+ command_index--;
+ break;
+ }
+ }
record_stream_free(p_rs);
- /* start listening for new connections again */
- cnd_event_add(&s_listen_event);
+ /* notify CNE of the socket closed */
+ cne_processCommand(fd, CNE_NOTIFY_SOCKET_CLOSED_CMD, NULL, 0);
onCommandsSocketClosed();
}
@@ -1048,18 +1083,14 @@ static void listenCallback (int fd, void *param)
{
int ret;
int err;
- int is_cnm_svc_socket;
RecordStream *p_rs;
- int i;
- char tmpBuf[10];
- int32_t pid, pid2, pid3, pid4;
struct sockaddr_un peeraddr;
socklen_t socklen = sizeof (peeraddr);
struct ucred creds;
socklen_t szCreds = sizeof(creds);
- struct passwd *pwd = NULL;
+ int s_fdCommand;
- assert (s_fdCommand < 0);
+ CNE_LOGD("listenCallback: fd=%d, s_fdListen=%d", fd, s_fdListen);
assert (fd == s_fdListen);
s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
@@ -1072,7 +1103,6 @@ static void listenCallback (int fd, void *param)
errno = 0;
err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
- cnmSvcFd = s_fdCommand; // save command descriptor to be used for communication
ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);
if (ret < 0) {
@@ -1089,6 +1119,10 @@ static void listenCallback (int fd, void *param)
CNE_LOGD ("Error: exceeding number of supported connection");
return;
}
+
+ CNE_LOGV("listenCallback: command_index=%d, fd=%d",
+ command_index, s_fdCommand);
+
cnd_event_set (&s_commands_event[command_index], s_fdCommand, 1,
processCommandsCallback, p_rs);
cnd_event_add (&s_commands_event[command_index]);
diff --git a/include/cne/cne.h b/include/cne/cne.h
index cd63cad..e58e745 100644..100755
--- a/include/cne/cne.h
+++ b/include/cne/cne.h
@@ -32,7 +32,7 @@
-----------------------------------------------------------------------------*/
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -132,10 +132,17 @@ typedef enum
CNE_NOTIFY_TIMER_EXPIRED_CMD,
CNE_REQUEST_START_FMC_CMD,
- CNE_REQUEST_STOP_FMC_CMD
- /* Add other commands here, note these should match with the ones in the
- * java layer.
+ CNE_REQUEST_STOP_FMC_CMD,
+ CNE_REQUEST_UPDATE_WWAN_DORMANCY_INFO_CMD,
+ CNE_REQUEST_UPDATE_DEFAULT_NETWORK_INFO_CMD,
+ CNE_NOTIFY_SOCKET_CLOSED_CMD,
+ /**
+ Add other commands here, note these should match with the ones in the
+ java layer.
+
+ CNE_REQUEST_VENDOR_CMD should always be last cmd in this enum
*/
+ CNE_REQUEST_VENDOR_CMD
} cne_cmd_enum_type;
@@ -165,7 +172,11 @@ typedef enum
CNE_REQUEST_START_SCAN_WLAN_MSG,
CNE_NOTIFY_INFLIGHT_STATUS_MSG,
CNE_NOTIFY_FMC_STATUS_MSG,
- CNE_NOTIFY_HOST_ROUTING_IP_MSG
+ CNE_NOTIFY_HOST_ROUTING_IP_MSG,
+ /**
+ CNE_NOTIFY_VENDOR_MSG should always be last msg in this enum
+ */
+ CNE_NOTIFY_VENDOR_MSG
} cne_msg_enum_type;
@@ -260,6 +271,39 @@ typedef enum
}cne_rat_type;
/**
+ * represents battery status, values should match
+ * BatteryManager.java
+ */
+typedef enum
+{
+ CNE_BATTERY_STATUS_UNKNOWN = 1,
+ CNE_BATTERY_STATUS_CHARGING,
+ CNE_BATTERY_STATUS_DISCHARGING,
+ CNE_BATTERY_STATUS_NOT_CHARGING,
+ CNE_BATTERY_STATUS_FULL
+} cne_battery_status;
+
+/**
+ * represets battery level
+ */
+typedef enum
+{
+ CNE_BATTERY_LEVEL_MIN = 0,
+ CNE_BATTERY_LEVEL_MAX = 100
+} cne_battery_level;
+
+/**
+ * represets charger type, values should match
+ * BatteryManager.java
+ */
+typedef enum
+{
+ CNE_BATTERY_PLUGGED_NONE,
+ CNE_BATTERY_PLUGGED_AC,
+ CNE_BATTERY_PLUGGED_USB
+} cne_battery_charger_type;
+
+/**
This is a type representing the list of possible subRATs
*/
typedef enum
diff --git a/reference-cne/src/CneSvc.cpp b/reference-cne/src/CneSvc.cpp
index 91b1baf..1db5f6b 100644
--- a/reference-cne/src/CneSvc.cpp
+++ b/reference-cne/src/CneSvc.cpp
@@ -14,7 +14,7 @@
the system belong here.
============================================================================*/
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010, 2011 Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -107,6 +107,7 @@ extern "C" void cne_init
extern "C" void
cne_processCommand
(
+ int fd,
int cmd,
void *cmd_data, /* event data depends on the type of event */
int cmd_len