nuttx/drivers/modem/alt1250/alt1250.c

1329 lines
36 KiB
C

/****************************************************************************
* drivers/modem/alt1250/alt1250.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <poll.h>
#include <errno.h>
#include <nuttx/wireless/lte/lte_ioctl.h>
#include <nuttx/modem/alt1250.h>
#include <assert.h>
#include "altcom_pkt.h"
#include "altcom_hdlr.h"
#include "altmdm.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define WRITE_OK 0
#define WRITE_NG 1
#define rel_evtbufinst(inst, dev) unlock_evtbufinst(inst, dev)
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Character driver methods. */
static int alt1250_open(FAR struct file *filep);
static int alt1250_close(FAR struct file *filep);
static ssize_t alt1250_read(FAR struct file *filep, FAR char *buffer,
size_t len);
static int alt1250_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
static int alt1250_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
parse_handler_t alt1250_additional_parsehdlr(uint16_t, uint8_t);
compose_handler_t alt1250_additional_composehdlr(uint32_t, FAR uint8_t *,
size_t);
/****************************************************************************
* Private Data
****************************************************************************/
/* This the vtable that supports the character driver interface. */
static const struct file_operations g_alt1250fops =
{
alt1250_open, /* open */
alt1250_close, /* close */
alt1250_read, /* read */
NULL, /* write */
NULL, /* seek */
alt1250_ioctl, /* ioctl */
alt1250_poll, /* poll */
};
static uint8_t g_recvbuff[ALTCOM_RX_PKT_SIZE_MAX];
static uint8_t g_sendbuff[ALTCOM_PKT_SIZE_MAX];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: add_list
****************************************************************************/
static void add_list(FAR struct alt_queue_s *head,
FAR struct alt_container_s *list)
{
FAR struct alt_container_s *next;
nxmutex_lock(&head->lock);
while (list != NULL)
{
next = (FAR struct alt_container_s *)sq_next(&list->node);
sq_next(&list->node) = NULL;
sq_addlast(&list->node, &head->queue);
list = next;
}
nxmutex_unlock(&head->lock);
}
/****************************************************************************
* Name: remove_list_all
****************************************************************************/
static FAR struct alt_container_s *remove_list_all(
FAR struct alt_queue_s *head)
{
FAR struct alt_container_s *list;
nxmutex_lock(&head->lock);
list = (FAR struct alt_container_s *)sq_peek(&head->queue);
sq_init(&head->queue);
nxmutex_unlock(&head->lock);
return list;
}
/****************************************************************************
* Name: remove_list
****************************************************************************/
static FAR struct alt_container_s *remove_list(FAR struct alt_queue_s *head,
uint16_t cmdid, uint16_t transid)
{
FAR struct alt_container_s *list;
nxmutex_lock(&head->lock);
list = (FAR struct alt_container_s *)sq_peek(&head->queue);
while (list != NULL)
{
if ((list->altcid == cmdid) && (list->alttid == transid))
{
sq_rem(&list->node, &head->queue);
sq_next(&list->node) = NULL;
break;
}
list = (FAR struct alt_container_s *)sq_next(&list->node);
}
nxmutex_unlock(&head->lock);
return list;
}
/****************************************************************************
* Name: set_senddisable
****************************************************************************/
static void set_senddisable(FAR struct alt1250_dev_s *dev, bool disable)
{
nxmutex_lock(&dev->senddisablelock);
dev->senddisable = disable;
nxmutex_unlock(&dev->senddisablelock);
}
/****************************************************************************
* Name: is_senddisable
****************************************************************************/
static bool is_senddisable(FAR struct alt1250_dev_s *dev)
{
bool disable;
nxmutex_lock(&dev->senddisablelock);
disable = dev->senddisable;
nxmutex_unlock(&dev->senddisablelock);
return disable;
}
/****************************************************************************
* Name: read_evtbitmap
****************************************************************************/
static ssize_t read_data(FAR struct alt1250_dev_s *dev,
FAR struct alt_readdata_s *rdata)
{
int idx;
nxmutex_lock(&dev->evtmaplock);
/* change status to NOT WRITABLE */
for (idx = 0; idx < (sizeof(uint64_t) * 8); idx++)
{
if ((dev->evtbitmap & (1ULL << idx)) != 0)
{
if (dev->evtbuff->ninst >= idx)
{
FAR alt_evtbuf_inst_t *inst = &dev->evtbuff->inst[idx];
nxmutex_lock(&inst->stat_lock);
inst->stat = ALTEVTBUF_ST_NOTWRITABLE;
nxmutex_unlock(&inst->stat_lock);
}
}
}
rdata->evtbitmap = dev->evtbitmap;
rdata->head = remove_list_all(&dev->replylist);
if (dev->evtbitmap & ALT1250_EVTBIT_RESET)
{
/* Resume sending because daemon has been notified of the reset
* reliably.
*/
set_senddisable(dev, false);
}
dev->evtbitmap = 0ULL;
nxmutex_unlock(&dev->evtmaplock);
return sizeof(struct alt_readdata_s);
}
/****************************************************************************
* Name: write_evtbitmap
****************************************************************************/
static void write_evtbitmap(FAR struct alt1250_dev_s *dev,
uint64_t bitmap)
{
nxmutex_lock(&dev->evtmaplock);
dev->evtbitmap |= bitmap;
if ((dev->evtbitmap & ALT1250_EVTBIT_RESET) != 0)
{
dev->evtbitmap = ALT1250_EVTBIT_RESET;
}
m_info("write bitmap: 0x%llx\n", bitmap);
nxmutex_unlock(&dev->evtmaplock);
}
/****************************************************************************
* Name: write_evtbitmapwithlist
****************************************************************************/
static void write_evtbitmapwithlist(FAR struct alt1250_dev_s *dev,
uint64_t bitmap, FAR struct alt_container_s *container)
{
nxmutex_lock(&dev->evtmaplock);
dev->evtbitmap |= bitmap;
if (dev->evtbitmap & ALT1250_EVTBIT_RESET)
{
dev->evtbitmap = ALT1250_EVTBIT_RESET;
}
add_list(&dev->replylist, container);
nxmutex_unlock(&dev->evtmaplock);
}
/****************************************************************************
* Name: is_evtbitmap_avail
****************************************************************************/
static bool is_evtbitmap_avail(FAR struct alt1250_dev_s *dev)
{
bool ret;
nxmutex_lock(&dev->evtmaplock);
/* 0 means it is not available, otherwise it is available. */
ret = (0ULL != dev->evtbitmap);
nxmutex_unlock(&dev->evtmaplock);
return ret;
}
/****************************************************************************
* Name: add_evtbuff
****************************************************************************/
static void add_evtbuff(FAR struct alt1250_dev_s *dev,
FAR struct alt_evtbuffer_s *buff)
{
dev->evtbuff = buff;
}
/****************************************************************************
* Name: write_evtbuff_byidx
****************************************************************************/
static int write_evtbuff_byidx(FAR struct alt1250_dev_s *dev,
uint64_t idx, CODE void(*write_func)(FAR void *outp[], FAR void *inp),
FAR void *inp)
{
int ret = WRITE_NG;
nxmutex_lock(&dev->evtmaplock);
if (dev->evtbuff)
{
if (dev->evtbuff->ninst >= idx)
{
FAR alt_evtbuf_inst_t *inst = &dev->evtbuff->inst[idx];
nxmutex_lock(&inst->stat_lock);
if (inst->stat == ALTEVTBUF_ST_WRITABLE)
{
write_func(inst->outparam, inp);
dev->evtbitmap |= (1ULL << idx);
ret = WRITE_OK;
}
nxmutex_unlock(&inst->stat_lock);
}
}
nxmutex_unlock(&dev->evtmaplock);
return ret;
}
/****************************************************************************
* Name: lock_evtbuffinst
****************************************************************************/
static void lock_evtbuffinst(FAR alt_evtbuf_inst_t *inst,
FAR struct alt1250_dev_s *dev)
{
nxmutex_lock(&dev->evtmaplock);
nxmutex_lock(&inst->stat_lock);
}
/****************************************************************************
* Name: unlock_evtbufinst
****************************************************************************/
static void unlock_evtbufinst(FAR alt_evtbuf_inst_t *inst,
FAR struct alt1250_dev_s *dev)
{
nxmutex_unlock(&inst->stat_lock);
nxmutex_unlock(&dev->evtmaplock);
}
/****************************************************************************
* Name: search_evtbufinst
****************************************************************************/
static FAR alt_evtbuf_inst_t *search_evtbufinst(uint16_t cid,
FAR uint64_t *bitmap, FAR struct alt1250_dev_s *dev)
{
FAR alt_evtbuf_inst_t *ret = NULL;
unsigned int i;
*bitmap = 0ULL;
for (i = 0; i < dev->evtbuff->ninst; i++)
{
ret = &dev->evtbuff->inst[i];
if (ret->altcid == cid)
{
*bitmap = 1ULL << i;
return ret;
}
}
return NULL;
}
/****************************************************************************
* Name: cid_to_searchable
****************************************************************************/
static uint16_t cid_to_searchable(uint16_t cid, uint8_t altver)
{
uint16_t cidv1;
cid &= ~ALTCOM_CMDID_REPLY_BIT;
if (altver == ALTCOM_VER4)
{
/* Change the command ID to Version 1
* Even if it cannot be converted, try to search the table
* using the original command ID.
*/
cidv1 = convert_cid2v1(cid);
if (cidv1 != APICMDID_UNKNOWN)
{
cid = cidv1;
}
}
return cid;
}
/****************************************************************************
* Name: get_bitmap
****************************************************************************/
static uint64_t get_bitmap(FAR struct alt1250_dev_s *dev, uint16_t cid,
uint8_t altver)
{
uint64_t bitmap = 0ULL;
cid = cid_to_searchable(cid, altver);
search_evtbufinst(cid, &bitmap, dev);
return bitmap;
}
/****************************************************************************
* Name: get_evtbuffinst_withlock
****************************************************************************/
static FAR alt_evtbuf_inst_t *get_evtbuffinst_withlock(
FAR struct alt1250_dev_s *dev, uint16_t cid, uint8_t altver,
FAR uint64_t *bitmap)
{
FAR alt_evtbuf_inst_t *inst = NULL;
FAR alt_evtbuf_inst_t *ret = NULL;
cid = cid_to_searchable(cid, altver);
if (cid == APICMDID_SOCK_SELECT)
{
ret = &dev->select_inst;
lock_evtbuffinst(ret, dev);
ret->outparam = dev->select_container->outparam;
ret->outparamlen = dev->select_container->outparamlen;
search_evtbufinst(cid, bitmap, dev);
}
else
{
inst = search_evtbufinst(cid, bitmap, dev);
if (inst)
{
lock_evtbuffinst(inst, dev);
if (inst->stat == ALTEVTBUF_ST_WRITABLE)
{
ret = inst;
}
else
{
unlock_evtbufinst(inst, dev);
}
}
}
return ret;
}
/****************************************************************************
* Name: write_restart_param
****************************************************************************/
static void write_restart_param(FAR void *outp[], FAR void *buff)
{
FAR int *out_reason = (FAR int *)outp[0];
FAR int *in_reason = (FAR int *)buff;
*out_reason = *in_reason;
}
/****************************************************************************
* Name: pollnotify
****************************************************************************/
static void pollnotify(FAR struct alt1250_dev_s *dev)
{
nxmutex_lock(&dev->pfdlock);
if (dev->pfd != NULL)
{
/* If poll() waits, notify */
poll_notify(&dev->pfd, 1, POLLIN);
}
nxmutex_unlock(&dev->pfdlock);
}
/****************************************************************************
* Name: get_composehdlr
****************************************************************************/
compose_handler_t get_composehdlr(uint32_t cmdid, FAR uint8_t *payload,
size_t size)
{
compose_handler_t ret;
ret = alt1250_composehdlr(cmdid);
#ifdef CONFIG_MODEM_ALT1250_ADDITIONAL_FUNC
if (ret == NULL)
{
ret = alt1250_additional_composehdlr(cmdid, payload, size);
}
#endif
return ret;
}
/****************************************************************************
* Name: get_parsehdlr
****************************************************************************/
parse_handler_t get_parsehdlr(uint16_t altcid, uint8_t altver)
{
parse_handler_t ret;
ret = alt1250_parsehdlr(altcid, altver);
#ifdef CONFIG_MODEM_ALT1250_ADDITIONAL_FUNC
if (ret == NULL)
{
ret = alt1250_additional_parsehdlr(altcid, altver);
}
#endif
return ret;
}
/****************************************************************************
* Name: alt1250_power_control
****************************************************************************/
static int alt1250_power_control(FAR struct alt1250_dev_s *dev,
FAR struct alt_power_s *req)
{
int ret = OK;
switch (req->cmdid)
{
case LTE_CMDID_POWERON:
ret = altmdm_poweron();
break;
case LTE_CMDID_POWEROFF:
ret = altmdm_poweroff();
break;
case LTE_CMDID_TAKEWLOCK:
ret = altmdm_take_wlock();
break;
case LTE_CMDID_GIVEWLOCK:
ret = altmdm_give_wlock();
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/****************************************************************************
* Name: make_altcomcmd_and_send
****************************************************************************/
static int make_altcomcmd_and_send(FAR struct alt1250_dev_s *dev,
FAR alt_container_t *req)
{
int ret = OK;
compose_handler_t handler;
uint8_t altver;
uint16_t cid;
uint16_t tid;
FAR uint8_t *payload;
int remainlen;
int pos;
m_info("send request: command ID=0x%08lx\n", req->cmdid);
payload = get_payload((FAR struct altcom_cmdhdr_s *)g_sendbuff);
handler = get_composehdlr(req->cmdid & ~LTE_CMDOPT_ASYNC_BIT, payload,
ALTCOM_PAYLOAD_SIZE_MAX);
if (handler)
{
altver = altmdm_get_protoversion();
if ((altver == ALTCOM_VERX) || is_senddisable(dev))
{
ret = -ENETDOWN;
}
else
{
ret = handler(req->inparam, req->inparamlen, altver, payload,
ALTCOM_PAYLOAD_SIZE_MAX, &cid);
ret = (ret > ALTCOM_PAYLOAD_SIZE_MAX) ? -ENOSPC : ret;
if (ret >= 0)
{
tid = altcom_make_header(
(FAR struct altcom_cmdhdr_s *)g_sendbuff,
altver, cid, ret);
req->altcid = cid | ALTCOM_CMDID_REPLY_BIT;
req->alttid = tid;
if (req->outparam != NULL)
{
add_list(&dev->waitlist, req);
}
remainlen = get_pktlen(altver, (uint16_t)ret);
pos = 0;
/* If the modem sleeps during the split transmission,
* the receive buffer of the modem will be cleared.
* Therefore, split packets sent before sleep will be
* discarded. To avoid this, do not enter sleep state
* until all the packets have been sent.
*/
altmdm_take_wlock();
do
{
ret = altmdm_write(&g_sendbuff[pos], remainlen);
if (ret < 0)
{
break;
}
else
{
m_info(
"write success: size=%d, cid=0x%04x tid=0x%04x\n",
ret, cid, tid);
remainlen -= ret;
pos += ret;
}
}
while (remainlen > 0);
altmdm_give_wlock();
if (ret < 0)
{
m_err("altmdm_write() failed: %d\n", ret);
ret = -ENETDOWN;
/* If the container is not left in the waitlist,
* it has already been processed by the recvthread.
* ENETRESET is returned to the caller to indicate that
* the container has been processed.
*/
if ((req->outparam != NULL) && (remove_list(&dev->waitlist,
req->altcid, req->alttid) == NULL))
{
ret = -ENETRESET;
}
}
else
{
m_info("write success: size=%d, cid=0x%04x tid=0x%04x\n",
ret, cid, tid);
ret = OK;
}
}
else
{
m_err("handler() failed: %d\n", ret);
}
}
}
else
{
ret = -ENOSYS;
}
return ret;
}
/****************************************************************************
* Name: exchange_selectcontainer
****************************************************************************/
static int exchange_selectcontainer(FAR struct alt1250_dev_s *dev,
FAR alt_container_t **container)
{
FAR alt_container_t *newcontainer;
if (container == NULL)
{
return -EINVAL;
}
nxmutex_lock(&dev->select_inst.stat_lock);
newcontainer = *container;
*container = dev->select_container;
dev->select_container = newcontainer;
nxmutex_unlock(&dev->select_inst.stat_lock);
return OK;
}
/****************************************************************************
* Name: altcom_recvthread
****************************************************************************/
static void altcom_recvthread(FAR void *arg)
{
int ret;
FAR struct alt1250_dev_s *dev = (FAR struct alt1250_dev_s *)arg;
bool is_running = true;
FAR struct alt_container_s *head;
FAR struct alt_container_s *container;
uint16_t cid;
uint16_t tid;
uint8_t altver;
parse_handler_t handler;
uint64_t bitmap = 0ULL;
int recvedlen = 0;
m_info("recv thread start\n");
altmdm_init(dev->spi, dev->lower);
while (is_running)
{
ret = altmdm_read(g_recvbuff + recvedlen,
ALTCOM_RX_PKT_SIZE_MAX - recvedlen);
/* Normal packet received */
if (ret >= 0)
{
m_info("read packet %d bytes\n", ret);
recvedlen += ret;
ret = altcom_is_pkt_ok(g_recvbuff, recvedlen);
if (ret > 0)
{
/* Cases in which fragmented packets are received.
* Therefore, the receive process is performed again.
*/
m_info("This is fragmented packet received. remain len: %d\n",
ret);
continue;
}
if (ret < 0)
{
/* Forced reset of modem due to packet format error detected */
m_err("[altcom] Forced modem reset due to parse failure\n");
altmdm_reset();
}
else
{
bool is_discard = false;
/* parse ALTCOM command ID and transaction ID from header */
cid = parse_cid((FAR struct altcom_cmdhdr_s *)g_recvbuff);
tid = parse_tid((FAR struct altcom_cmdhdr_s *)g_recvbuff);
altver = get_altver(
(FAR struct altcom_cmdhdr_s *)g_recvbuff);
m_info("receive cid:0x%04x tid:0x%04x\n", cid, tid);
/* Is error indication packet?
* This packet is a response to a command that is not supported
* by the ALT1250. The header of the request packet is included
* in the contents of the this packet.
*/
if (is_errind(cid))
{
/* Get ALTCOM command ID and transaction ID
* from error indication packet
*/
cid = parse_cid4errind(
(FAR struct altcom_cmdhdr_s *)g_recvbuff);
tid = parse_tid4errind(
(FAR struct altcom_cmdhdr_s *)g_recvbuff);
m_info("receive errind cid:0x%04x tid:0x%04x\n", cid, tid);
container = remove_list(&dev->waitlist, cid, tid);
if (container != NULL)
{
/* It means that requested command not implemented
* by modem
*/
container->result = -ENOSYS;
}
else
{
/* Discard the event packet */
is_discard = true;
m_warn("container is not found\n");
}
}
else
{
container = remove_list(&dev->waitlist, cid, tid);
handler = get_parsehdlr(cid, altver);
if (handler)
{
FAR uint8_t *payload = get_payload(
(FAR struct altcom_cmdhdr_s *)g_recvbuff);
if (container)
{
m_info("handler and container is found\n");
bitmap = get_bitmap(dev, cid, altver);
/* Perform parse handler */
container->result = handler(dev, payload,
get_payload_len(
(FAR struct altcom_cmdhdr_s *)g_recvbuff),
altver, container->outparam,
container->outparamlen, &bitmap);
}
else
{
FAR alt_evtbuf_inst_t *inst;
m_warn("container is not found\n");
/* If the state of the instance is NotWritable,
* instanse will be returned as NULL.
*/
inst = get_evtbuffinst_withlock(dev, cid, altver,
&bitmap);
if (inst)
{
/* Perform parse handler */
ret = handler(dev, payload, get_payload_len(
(FAR struct altcom_cmdhdr_s *)g_recvbuff),
altver, inst->outparam, inst->outparamlen,
&bitmap);
unlock_evtbufinst(inst, dev);
if (ret >= 0)
{
write_evtbitmap(dev, bitmap);
}
else
{
/* Discard the event packet */
is_discard = true;
}
}
else
{
/* Discard the event packet */
is_discard = true;
}
}
}
else if (container)
{
container->result = -ENOSYS;
m_warn("handler is not found\n");
}
else
{
/* Discard the event packet */
is_discard = true;
m_warn("container and handler is not found\n");
}
}
if (container)
{
if (container->cmdid & LTE_CMDOPT_ASYNC_BIT)
{
bitmap |= ALT1250_EVTBIT_REPLY;
}
else
{
bitmap = ALT1250_EVTBIT_REPLY;
}
write_evtbitmapwithlist(dev, bitmap, container);
}
if (is_discard)
{
dev->discardcnt++;
m_err("discard event %lu\n", dev->discardcnt);
}
else
{
pollnotify(dev);
}
}
}
else
{
switch (ret)
{
case ALTMDM_RETURN_RESET_PKT:
{
m_info("recieve ALTMDM_RETURN_RESET_PKT\n");
set_senddisable(dev, true);
}
break;
case ALTMDM_RETURN_RESET_V1:
case ALTMDM_RETURN_RESET_V4:
{
uint32_t reason = altmdm_get_reset_reason();
m_info("recieve ALTMDM_RETURN_RESET_V1/V4\n");
ret = write_evtbuff_byidx(dev, 0, write_restart_param,
(FAR void *)&reason);
/* If there is a waiting list,
* replace it with the replay list.
*/
head = remove_list_all(&dev->waitlist);
write_evtbitmapwithlist(dev, ALT1250_EVTBIT_RESET, head);
pollnotify(dev);
}
break;
case ALTMDM_RETURN_EXIT:
{
m_info("recieve ALTMDM_RETURN_EXIT\n");
is_running = false;
}
break;
default:
DEBUGASSERT(0);
break;
}
}
recvedlen = 0;
}
m_info("recv thread end\n");
pthread_exit(0);
}
/****************************************************************************
* Name: alt1250_open
****************************************************************************/
static int alt1250_open(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct alt1250_dev_s *dev;
int ret = OK;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
inode = filep->f_inode;
dev = (FAR struct alt1250_dev_s *)inode->i_private;
DEBUGASSERT(dev);
nxmutex_lock(&dev->refslock);
if (dev->crefs > 0)
{
ret = -EPERM;
}
/* Increment the count of open references on the driver */
dev->crefs++;
nxmutex_unlock(&dev->refslock);
if (ret == OK)
{
nxsem_init(&dev->waitlist.lock, 0, 1);
nxsem_init(&dev->replylist.lock, 0, 1);
nxsem_init(&dev->evtmaplock, 0, 1);
nxsem_init(&dev->pfdlock, 0, 1);
nxsem_init(&dev->senddisablelock, 0, 1);
nxsem_init(&dev->select_inst.stat_lock, 0, 1);
sq_init(&dev->waitlist.queue);
sq_init(&dev->replylist.queue);
dev->senddisable = true;
ret = pthread_create(&dev->recvthread, NULL,
(pthread_startroutine_t)altcom_recvthread,
(pthread_addr_t)dev);
if (ret < 0)
{
m_err("thread create failed: %d\n", errno);
ret = -errno;
nxsem_destroy(&dev->waitlist.lock);
nxsem_destroy(&dev->replylist.lock);
nxsem_destroy(&dev->evtmaplock);
nxsem_destroy(&dev->pfdlock);
nxsem_destroy(&dev->senddisablelock);
nxsem_destroy(&dev->select_inst.stat_lock);
nxmutex_lock(&dev->refslock);
dev->crefs--;
nxmutex_unlock(&dev->refslock);
}
else
{
pthread_setname_np(dev->recvthread, "altcom_recvthread");
}
}
return ret;
}
/****************************************************************************
* Name: alt1250_close
****************************************************************************/
static int alt1250_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct alt1250_dev_s *dev;
int ret = OK;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
inode = filep->f_inode;
dev = (FAR struct alt1250_dev_s *)inode->i_private;
DEBUGASSERT(dev);
nxmutex_lock(&dev->refslock);
if (dev->crefs == 0)
{
ret = -EPERM;
}
else
{
/* Decrement the count of open references on the driver */
dev->crefs--;
}
nxmutex_unlock(&dev->refslock);
if (ret == OK)
{
nxsem_destroy(&dev->waitlist.lock);
nxsem_destroy(&dev->replylist.lock);
nxsem_destroy(&dev->evtmaplock);
nxsem_destroy(&dev->pfdlock);
nxsem_destroy(&dev->senddisablelock);
nxsem_destroy(&dev->select_inst.stat_lock);
altmdm_fin();
pthread_join(dev->recvthread, NULL);
}
return ret;
}
/****************************************************************************
* Name: alt1250_read
****************************************************************************/
static ssize_t alt1250_read(FAR struct file *filep, FAR char *buffer,
size_t len)
{
FAR struct inode *inode;
FAR struct alt1250_dev_s *dev;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
inode = filep->f_inode;
dev = (FAR struct alt1250_dev_s *)inode->i_private;
DEBUGASSERT(dev);
if (len != sizeof(struct alt_readdata_s))
{
return -EINVAL;
}
return read_data(dev, (FAR struct alt_readdata_s *)buffer);
}
/****************************************************************************
* Name: alt1250_ioctl
****************************************************************************/
static int alt1250_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct alt1250_dev_s *dev;
int ret = OK;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
inode = filep->f_inode;
dev = (FAR struct alt1250_dev_s *)inode->i_private;
DEBUGASSERT(dev);
switch (cmd)
{
case ALT1250_IOC_POWER:
{
FAR struct alt_power_s *req = (FAR struct alt_power_s *)arg;
/* Performs power control or power consumption control
* of the modem.
*/
ret = alt1250_power_control(dev, req);
}
break;
case ALT1250_IOC_SEND:
{
FAR alt_container_t *req = (FAR alt_container_t *)arg;
ret = make_altcomcmd_and_send(dev, req);
}
break;
case ALT1250_IOC_SETEVTBUFF:
{
FAR struct alt_evtbuffer_s *buff =
(FAR struct alt_evtbuffer_s *)arg;
add_evtbuff(dev, buff);
}
break;
case ALT1250_IOC_EXCHGCONTAINER:
{
FAR alt_container_t **container = (FAR alt_container_t **)arg;
ret = exchange_selectcontainer(dev, container);
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/****************************************************************************
* Name: alt1250_poll
****************************************************************************/
static int alt1250_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
FAR struct inode *inode;
FAR struct alt1250_dev_s *dev;
int ret = OK;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
inode = filep->f_inode;
dev = (FAR struct alt1250_dev_s *)inode->i_private;
DEBUGASSERT(dev);
/* Are we setting up the poll? Or tearing it down? */
if (setup)
{
/* Ignore waits that do not include POLLIN */
if ((fds->events & POLLIN) == 0)
{
ret = -EDEADLK;
goto errout;
}
nxmutex_lock(&dev->pfdlock);
if (is_evtbitmap_avail(dev))
{
poll_notify(&fds, 1, POLLIN);
}
else
{
dev->pfd = fds;
}
nxmutex_unlock(&dev->pfdlock);
}
else
{
nxmutex_lock(&dev->pfdlock);
dev->pfd = NULL;
nxmutex_unlock(&dev->pfdlock);
}
errout:
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
FAR void *alt1250_register(FAR const char *devpath,
FAR struct spi_dev_s *dev,
FAR const struct alt1250_lower_s *lower)
{
FAR struct alt1250_dev_s *priv;
int ret;
priv = (FAR struct alt1250_dev_s *)
kmm_malloc(sizeof(struct alt1250_dev_s));
if (!priv)
{
m_err("Failed to allocate instance.\n");
return NULL;
}
memset(priv, 0, sizeof(struct alt1250_dev_s));
priv->spi = dev;
priv->lower = lower;
nxsem_init(&priv->refslock, 0, 1);
ret = register_driver(devpath, &g_alt1250fops, 0666, priv);
if (ret < 0)
{
m_err("Failed to register driver: %d\n", ret);
kmm_free(priv);
return NULL;
}
return priv;
}
uint64_t get_event_lapibuffer(FAR struct alt1250_dev_s *dev,
uint32_t lapicmdid,
alt_evtbuf_inst_t **inst)
{
FAR alt_evtbuf_inst_t *evtinst = NULL;
unsigned int i;
uint64_t ret = 0ULL;
for (i = 0; i < dev->evtbuff->ninst; i++)
{
evtinst = &dev->evtbuff->inst[i];
if (evtinst->cmdid == lapicmdid)
{
nxmutex_lock(&evtinst->stat_lock);
if (evtinst->stat == ALTEVTBUF_ST_WRITABLE)
{
*inst = evtinst;
ret = 1ULL << i;
}
nxmutex_unlock(&evtinst->stat_lock);
break;
}
}
return ret;
}