mISDNuser/capi20/module/capi_mod_misdn.c

563 lines
13 KiB
C

/*
* capi_mod_misdn.c
*
* Author Karsten Keil <kkeil@linux-pingi.de>
*
* Copyright 2011 by Karsten Keil <kkeil@linux-pingi.de>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* version 2.1 as published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU LESSER GENERAL PUBLIC LICENSE for more details.
*
*/
/**
* \file capi_mod_misdn.c
* CAPI 2.0 module for mISDN
*/
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/capi.h>
#include <errno.h>
#include <unistd.h>
#include <sys/un.h>
#include <poll.h>
#include <capi20.h>
#include <capi_mod.h>
#include <capiutils.h>
#include "../m_capi_sock.h"
#ifdef MISDND_CAPI_MODULE_DEBUG
static FILE *mIm_debug = NULL;
static char mIm_debug_file[128];
#define mId_print(fmt, ...) do { \
if (mIm_debug) { \
fprintf(mIm_debug, fmt, ##__VA_ARGS__); \
fflush(mIm_debug); \
} \
} while(0)
#else
#define mId_print(fmt, ...) do {} while(0)
#endif
/**
* \brief Create a socket to mISDNcapid
* \return socket number
*/
static int misdnOpenSocket(void)
{
struct sockaddr_un mcaddr;
int nHandle;
/* Create new socket */
nHandle = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (nHandle < 0) {
return -1;
}
mcaddr.sun_family = AF_UNIX;
sprintf(mcaddr.sun_path, "%s/%s", MISDN_CAPI_SOCKET_DIR, MISDN_CAPI_SOCKET_NAME);
/* Connect socket to address */
if (!connect(nHandle, (struct sockaddr *)&mcaddr, sizeof(mcaddr))) {
/* no errors, return handle */
return nHandle;
}
close(nHandle);
return -1;
}
/**
* \brief Send message to socket and wait for response
* \param nHandle socket handle
* \param pnBuffer data buffer pointer
* \param nLen number of bytes to write from pnBuffer
* \param nConf current configuration id
* \return number of bytes read
*/
static int misdnRemoteCommand(int nHandle, unsigned char *pnBuffer, int nLen, int nConf)
{
struct pollfd mypoll;
int ret;
/* write message to socket */
ret = send(nHandle, pnBuffer, nLen, 0);
if (ret != nLen)
return -1;
mypoll.fd = nHandle;
mypoll.events = POLLIN | POLLPRI;
/* wait max 1 sec for a answer */
ret = poll(&mypoll, 1, 1000);
if (ret < 1)
return -2;
/* read data */
ret = recv(nHandle, pnBuffer, 1024, 0);
return ret;
}
/**
* \brief Add standard misdn header to buffer pointer
* \param ppnPtr data buffer pointer
* \param nLen length of message
* \param nCmd command id
*/
static void misdnSetHeader(unsigned char *p, _cword nLen, _cword AppId, _cword nCmd, _cword Contr)
{
CAPIMSG_SETLEN(p, nLen);
CAPIMSG_SETAPPID(p, AppId);
capimsg_setu8(p, 4, nCmd >> 8);
capimsg_setu8(p, 5, nCmd & 0xff);
capimsg_setu16(p, 8, Contr);
}
#if 0
/**
* \brief Debug purpose, write capi data to file
* \param nSend send
* \param pnBuffer data buffer
* \param nLength length of buffer
* \param nDataMsg data message len
*/
static void misdnWriteCapiTrace(int nSend, unsigned char *pnBuffer, int nLength, int nDataMsg)
{
int nHandle;
_cdword nTime;
unsigned char anHeader[7];
char *pnTraceFile = getTraceFile();
if (strlen(pnTraceFile) <= 0) {
return;
}
if (getTraceLevel() < (nDataMsg + 1)) {
return;
}
nHandle = open(pnTraceFile, O_WRONLY | O_CREAT | O_APPEND, 0644);
if (nHandle >= 0) {
nTime = (_cdword) time(NULL);
capimsg_setu16(anHeader, 0, nLength + sizeof(anHeader));
capimsg_setu32(anHeader, 2, nTime);
anHeader[6] = (nSend) ? 0x80 : 0x81;
close(nHandle);
}
}
#endif
#if HAVE_OLDCAPIMOD
#define ISINSTALLTYPE unsigned
#else
#define ISINSTALLTYPE int
#endif
/**
* \brief Check if misdn interface is available
* \return file descriptor of socket, or error code
*/
static ISINSTALLTYPE misdnIsInstalled(void)
{
ISINSTALLTYPE nHandle;
nHandle = misdnOpenSocket();
#ifdef MISDND_CAPI_MODULE_DEBUG
if (nHandle >= 0 && !mIm_debug) {
int pid;
pid = getpid();
sprintf(mIm_debug_file, "/tmp/mIm_debug_%05d.log", pid);
mIm_debug = fopen(mIm_debug_file, "wt");
}
#endif
return nHandle;
}
/**
* \brief Register at misdn
* \param nMaxB3Connection maximum b3 connection
* \param nMaxB3Blks maximum b3 blocks
* \param nMaxSizeB3 maximum b3 size
* \param pnApplId pointer where we store the new application id
* \return new socket handle
*/
static unsigned misdnRegister(unsigned nMaxB3Connection, unsigned nMaxB3Blks, unsigned nMaxSizeB3, unsigned *pnApplId)
{
unsigned char anBuf[100];
int nSock, ret;
uint16_t ApplId;
*pnApplId = -1;
/* open a new socket for communication */
nSock = misdnOpenSocket();
if (nSock < 0)
return nSock;
ApplId = capi_alloc_applid(nSock);
misdnSetHeader(anBuf, 20, ApplId, MIC_REGISTER_REQ, 0);
capimsg_setu32(anBuf, 8, nMaxB3Connection);
capimsg_setu32(anBuf, 12, nMaxB3Blks);
capimsg_setu32(anBuf, 16, nMaxSizeB3);
/* Send message to socket and wait for answer */
ret = misdnRemoteCommand(nSock, anBuf, 20, MIC_REGISTER_REQ);
if (ret != 10) {
close(nSock);
return -2;
}
ret = CAPIMSG_U16(anBuf, 8);
if (ret == CapiNoError) {
/* No error set it to pnApplId */
*pnApplId = ApplId;
mId_print("%s: fd=%d ApplId=%d\n", __func__, nSock, ApplId);
} else {
/* error occured, close socket give back ApplId and return -1 */
capi_freeapplid(ApplId);
close(nSock);
nSock = -1;
}
return nSock;
}
/**
* \brief Put capi message to misdn
* \param nSock socket handle
* \param nApplId application id
* \param pnMsg message pointer
* \return error code
*/
static unsigned misdnPutMessage(int nSock, unsigned nApplId, unsigned char *pnMsg)
{
int nLen = CAPIMSG_LEN(pnMsg);
int nCommand = CAPIMSG_COMMAND(pnMsg);
int nSubCommand = CAPIMSG_SUBCOMMAND(pnMsg);
int ret = -1, tot = 0, dlen = 0;
#ifdef MISDND_CAPI_MODULE_DEBUG
uint8_t d = 0;
#endif
uint16_t dh;
void *dp = NULL;
struct msghdr msg;
struct iovec iv[2];
if (nCommand == CAPI_DATA_B3) {
if (nSubCommand == CAPI_REQ) {
dlen = CAPIMSG_DATALEN(pnMsg);
if (sizeof(dp) == 4)
dp = (void *)((unsigned long)CAPIMSG_U32(pnMsg, 12));
else
dp = (void *)((unsigned long)CAPIMSG_U64(pnMsg, 22));
#ifdef MISDND_CAPI_MODULE_DEBUG
d = *((unsigned char *)dp);
#endif
iv[0].iov_base = pnMsg;
iv[0].iov_len = nLen;
iv[1].iov_base = dp;
iv[1].iov_len = dlen;
tot = dlen + nLen;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iv;
msg.msg_iovlen = 2;
ret = sendmsg(nSock, &msg, 0);
} else if (CAPI_RESP) {
dh = CAPIMSG_U16(pnMsg, 12);
dh = capi_return_buffer(nApplId, dh);
capimsg_setu16(pnMsg, 12, dh);
ret = send(nSock, pnMsg, nLen, 0);
tot = nLen;
}
} else {
ret = send(nSock, pnMsg, nLen, 0);
tot = nLen;
}
mId_print("%s: %s fd=%d len=%d dp=%p dlen=%d tot=%d d=%02x ret=%d (%d - %s)\n", __func__, capi20_cmd2str(nCommand, nSubCommand),
nSock, nLen, dp, dlen, tot, d, ret, errno, strerror(errno));
if (tot != ret) {
ret = CapiMsgOSResourceErr;
} else
ret = CapiNoError;
return ret;
}
/**
* \brief Get message from misdn
* \param nSock socket handle
* \param nApplId application id
* \param ppnBuffer pointer to data buffer pointer (where we store the data)
* \return error code
*/
static unsigned misdnGetMessage(int nSock, unsigned nApplId, unsigned char **ppnBuffer)
{
unsigned char *pnBuffer;
unsigned nOffset;
size_t nBufSize;
int nRet;
uint16_t ml;
unsigned long nData;
/* try to get a new buffer from queue */
if ((*ppnBuffer = pnBuffer = (unsigned char *)capi_get_buffer(nApplId, &nBufSize, &nOffset)) == 0) {
mId_print("%s: no pnBuffer\n", __func__);
return CapiMsgOSResourceErr;
}
/* Get message */
nRet = recv(nSock, pnBuffer, nBufSize, MSG_DONTWAIT);
if (nRet > 0) {
/* DATA_B3? Then set buffer address */
if ((CAPIMSG_COMMAND(pnBuffer) == CAPI_DATA_B3) && (CAPIMSG_SUBCOMMAND(pnBuffer) == CAPI_IND)) {
capi_save_datahandle(nApplId, nOffset, CAPIMSG_U16(pnBuffer, 18), CAPIMSG_U32(pnBuffer, 8));
/* patch datahandle */
capimsg_setu16(pnBuffer, 18, nOffset);
ml = CAPIMSG_LEN(pnBuffer);
nData = (unsigned long) pnBuffer + ml;
if (sizeof(void *) == 4) {
pnBuffer[12] = nData & 0xFF;
pnBuffer[13] = (nData >> 8) & 0xFF;
pnBuffer[14] = (nData >> 16) & 0xFF;
pnBuffer[15] = (nData >> 24) & 0xFF;
ml = 22;
} else {
capimsg_setu32(pnBuffer, 12, 0);
capimsg_setu64(pnBuffer, 22, nData);
ml = 30;
}
CAPIMSG_SETLEN(pnBuffer, ml);
/* keep buffer */
return CapiNoError;
}
/* buffer is not needed, return it */
capi_return_buffer(nApplId, nOffset);
return CapiNoError;
}
capi_return_buffer(nApplId, nOffset);
if (nRet == 0) {
return CapiReceiveQueueEmpty;
}
switch (errno) {
case EMSGSIZE:
nRet = CapiIllCmdOrSubcmdOrMsgToSmall;
break;
case EAGAIN:
nRet = CapiReceiveQueueEmpty;
break;
default:
nRet = CapiMsgOSResourceErr;
break;
}
return nRet;
}
/**
* \brief Get manufactor informations
* \param nHandle socket handle
* \param nController controller id
* \param pnBuffer buffer pointer we write our informations to
* \return pnBuffer
*/
static unsigned char *misdnGetManufactor(int nHandle, unsigned nController, unsigned char *pnBuffer)
{
unsigned char anBuf[100];
int ret;
misdnSetHeader(anBuf, 10, 0, MIC_GET_MANUFACTURER_REQ, nController);
ret = misdnRemoteCommand(nHandle, anBuf, 10, MIC_GET_MANUFACTURER_REQ);
if (ret == 74)
memcpy(pnBuffer, &anBuf[10], 64);
else
memset(pnBuffer, 0, 64);
return pnBuffer;
}
/**
* \brief Get version informations
* \param nHandle socket handle
* \param nController controller id
* \param pnBuffer buffer pointer we write our informations to
* \return pnBuffer
*/
static unsigned char *misdnGetVersion(int nHandle, unsigned nController, unsigned char *pnBuffer)
{
unsigned char anBuf[100];
int ret;
misdnSetHeader(anBuf, 10, 0, MIC_VERSION_REQ, nController);
ret = misdnRemoteCommand(nHandle, anBuf, 10, MIC_VERSION_REQ);
if (ret == 26)
memcpy(pnBuffer, &anBuf[10], 16);
else
memset(pnBuffer, 0, 16);
return pnBuffer;
}
/**
* \brief Get serial number informations
* \param nHandle socket handle
* \param nController controller id
* \param pnBuffer buffer pointer we write our informations to
* \return pnBuffer
*/
static unsigned char *misdnGetSerialNumber(int nHandle, unsigned nController, unsigned char *pnBuffer)
{
unsigned char anBuf[100];
int ret;
misdnSetHeader(anBuf, 10, 0, MIC_SERIAL_NUMBER_REQ, nController);
ret = misdnRemoteCommand(nHandle, anBuf, 10, MIC_SERIAL_NUMBER_REQ);
*pnBuffer = 0;
if (ret == 18)
memcpy(pnBuffer, &anBuf[10], 8);
return pnBuffer;
}
/**
* \brief Get profile from fritzbox
* \param nHandle socket handle
* \param nControllerId controller
* \param pnBuf buffer
* \return error code
*/
static unsigned misdnGetProfile(int nHandle, unsigned nController, unsigned char *pnBuf)
{
unsigned char anBuf[100];
uint16_t err;
int ret;
misdnSetHeader(anBuf, 10, 0, MIC_GET_PROFILE_REQ, nController);
ret = misdnRemoteCommand(nHandle, anBuf, 10, MIC_GET_PROFILE_REQ);
if (ret != 74)
return CapiMsgOSResourceErr;
err = CAPIMSG_U16(anBuf, 8);
if (err == CapiNoError) {
/* Important !!! Only copy 2 bytes if the number of controllers is requested */
if (nController)
memcpy(pnBuf, &anBuf[10], 64);
else
memcpy(pnBuf, &anBuf[10], 2);
}
return err;
}
static int misdnFlagReq(uint16_t ApplId, uint32_t set_f, uint32_t clr_f)
{
unsigned char anBuf[100];
int ret, fd;
fd = capi_applid2fd(ApplId);
if (fd < 0)
return -1;
misdnSetHeader(anBuf, 16, ApplId, MIC_USERFLAG_REQ, 0);
capimsg_setu32(anBuf, 8, set_f);
capimsg_setu32(anBuf, 12, clr_f);
ret = misdnRemoteCommand(fd, anBuf, 16, MIC_USERFLAG_REQ);
if (ret == 12)
ret = CAPIMSG_U32(anBuf, 8);
else
ret = -1;
return ret;
}
static int misdnGetFlags(unsigned nApplId, unsigned *pnFlagsPtr)
{
int ret;
ret = misdnFlagReq(nApplId, 0, 0);
if (ret < 0)
*pnFlagsPtr = 0;
else {
*pnFlagsPtr = ret;
ret = 0;
}
return ret;
}
static int misdnSetFlags(unsigned nApplId, unsigned nFlags)
{
int ret;
ret = misdnFlagReq(nApplId, nFlags, 0);
if (ret >= 0)
ret = 0;
return ret;
}
static int misdnClearFlags(unsigned nApplId, unsigned nFlags)
{
int ret;
ret = misdnFlagReq(nApplId, 0, nFlags);
if (ret >= 0)
ret = 0;
return ret;
}
static char *misdnGetTtyDeviceName(unsigned nApplId,unsigned nNcci, char *pnBuffer, size_t nSize)
{
unsigned char *anBuf;
int ret, fd;
fd = capi_applid2fd(nApplId);
if (fd < 0)
return NULL;
if (nSize > 64)
nSize = 64;
anBuf = malloc(nSize + 12);
if (!anBuf)
return NULL;
misdnSetHeader(anBuf, 16, nApplId, MIC_TTYNAME_REQ, 0);
capimsg_setu32(anBuf, 8, nNcci);
capimsg_setu32(anBuf, 12, nSize & 0xff);
ret = misdnRemoteCommand(fd, anBuf, 16, MIC_TTYNAME_REQ);
if (ret > 8) {
ret = ret - 8;
memcpy(pnBuffer, &anBuf[8], ret);
pnBuffer[ret] = 0;
} else
return NULL;
free(anBuf);
return pnBuffer;
}
/** Module operations structure */
static struct sModuleOperations sRemoteCapi = {
misdnIsInstalled,
misdnRegister,
NULL,
misdnPutMessage,
misdnGetMessage,
misdnGetManufactor,
misdnGetVersion,
misdnGetSerialNumber,
misdnGetProfile,
NULL,
misdnGetFlags,
misdnSetFlags,
misdnClearFlags,
misdnGetTtyDeviceName,
NULL,
NULL
};
MODULE_INIT("misdn", &sRemoteCapi);