/**************************************************************************** * 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 #include #include #include #include #include #include #include #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 */ NULL, /* mmap */ NULL, /* truncate */ 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) { nxmutex_init(&dev->waitlist.lock); nxmutex_init(&dev->replylist.lock); nxmutex_init(&dev->evtmaplock); nxmutex_init(&dev->pfdlock); nxmutex_init(&dev->senddisablelock); nxmutex_init(&dev->select_inst.stat_lock); 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; nxmutex_destroy(&dev->waitlist.lock); nxmutex_destroy(&dev->replylist.lock); nxmutex_destroy(&dev->evtmaplock); nxmutex_destroy(&dev->pfdlock); nxmutex_destroy(&dev->senddisablelock); nxmutex_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) { nxmutex_destroy(&dev->waitlist.lock); nxmutex_destroy(&dev->replylist.lock); nxmutex_destroy(&dev->evtmaplock); nxmutex_destroy(&dev->pfdlock); nxmutex_destroy(&dev->senddisablelock); nxmutex_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; nxmutex_init(&priv->refslock); 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; }