static void smd_net_notify(void *_dev, unsigned event) { struct rmnet_private *p = netdev_priv((struct net_device *)_dev); switch (event) { case SMD_EVENT_DATA: spin_lock(&p->lock); if (p->skb && (smd_write_avail(p->ch) >= p->skb->len)) { smd_disable_read_intr(p->ch); tasklet_hi_schedule(&p->tsklt); } spin_unlock(&p->lock); if (smd_read_avail(p->ch) && (smd_read_avail(p->ch) >= smd_cur_packet_size(p->ch))) { smd_net_data_tasklet.data = (unsigned long) _dev; tasklet_schedule(&smd_net_data_tasklet); } break; case SMD_EVENT_OPEN: DBG0("%s: opening SMD port\n", __func__); netif_carrier_on(_dev); if (netif_queue_stopped(_dev)) { DBG0("%s: re-starting if queue\n", __func__); netif_wake_queue(_dev); } break; case SMD_EVENT_CLOSE: DBG0("%s: closing SMD port\n", __func__); netif_carrier_off(_dev); break; } }
static int smd_tty_open(struct tty_struct *tty, struct file *f) { int res = 0; unsigned int n = tty->index; struct smd_tty_info *info; const char *peripheral = NULL; if (n >= MAX_SMD_TTYS || !smd_tty[n].smd) return -ENODEV; info = smd_tty + n; mutex_lock(&smd_tty_lock); tty->driver_data = info; if (info->open_count++ == 0) { peripheral = smd_edge_to_subsystem(smd_tty[n].smd->edge); if (peripheral) { info->pil = pil_get(peripheral); if (IS_ERR(info->pil)) { res = PTR_ERR(info->pil); goto out; } /* Wait for the modem SMSM to be inited for the SMD * Loopback channel to be allocated at the modem. Since * the wait need to be done atmost once, using msleep * doesn't degrade the performance. */ if (n == LOOPBACK_IDX) { if (!is_modem_smsm_inited()) msleep(5000); smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK); msleep(100); } /* * Wait for a channel to be allocated so we know * the modem is ready enough. */ if (smd_tty_modem_wait) { res = wait_for_completion_interruptible_timeout( &info->ch_allocated, msecs_to_jiffies(smd_tty_modem_wait * 1000)); if (res == 0) { pr_err("Timed out waiting for SMD" " channel\n"); res = -ETIMEDOUT; goto release_pil; } else if (res < 0) { pr_err("Error waiting for SMD channel:" " %d\n", res); goto release_pil; } res = 0; } } info->tty = tty; tasklet_init(&info->tty_tsklt, smd_tty_read, (unsigned long)info); wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, smd_tty[n].smd->port_name); if (!info->ch) { res = smd_named_open_on_edge(smd_tty[n].smd->port_name, smd_tty[n].smd->edge, &info->ch, info, smd_tty_notify); if (res < 0) { pr_err("%s: %s open failed %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_pil; } res = wait_event_interruptible_timeout( info->ch_opened_wait_queue, info->is_open, (2 * HZ)); if (res == 0) res = -ETIMEDOUT; if (res < 0) { pr_err("%s: wait for %s smd_open failed %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_pil; } res = 0; } } release_pil: if (res < 0) pil_put(info->pil); else smd_disable_read_intr(info->ch); out: mutex_unlock(&smd_tty_lock); return res; }
static void smd_tty_notify(void *priv, unsigned event) { struct smd_tty_info *info = priv; unsigned long flags; unsigned char *ptr; switch (event) { case SMD_EVENT_DATA: spin_lock_irqsave(&info->reset_lock, flags); if (!info->is_open) { spin_unlock_irqrestore(&info->reset_lock, flags); break; } spin_unlock_irqrestore(&info->reset_lock, flags); /* There may be clients (tty framework) that are blocked * waiting for space to write data, so if a possible read * interrupt came in wake anyone waiting and disable the * interrupts */ if (smd_write_avail(info->ch)) { smd_disable_read_intr(info->ch); if (info->tty) { unsigned int n = info->tty->index; wake_up_interruptible(&info->tty->write_wait); /* use pm_qos for BT performance */ if (n == BT_ACL_IDX || n == BT_CMD_IDX) schedule_work(&pm_qos_set_work); } } tasklet_hi_schedule(&info->tty_tsklt); break; case SMD_EVENT_OPEN: if (is_in_reset(info)) { unsigned int n = info->tty->index; if (n == BT_CMD_IDX) { pr_err("%s: BT_CMD_IDX Sending hardware error event to stack\n", __func__); tty_prepare_flip_string(info->tty, &ptr, 0x03); ptr[0] = 0x10; ptr[1] = 0x01; ptr[2] = 0x0A; tty_flip_buffer_push(info->tty); } } spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 0; info->in_reset_updated = 1; info->is_open = 1; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); break; case SMD_EVENT_CLOSE: spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 1; info->in_reset_updated = 1; info->is_open = 0; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); /* schedule task to send TTY_BREAK */ tasklet_hi_schedule(&info->tty_tsklt); if (info->tty->index == LOOPBACK_IDX) schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000)); break; } }
struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, uint32_t dl, apr_svc_cb_fn func, void *priv) { int rc; if ((svc >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) || (dl >= APR_DL_MAX)) { pr_err("apr_tal: Invalid params\n"); return NULL; } if (apr_svc_ch[dl][dest][svc].ch) { pr_err("apr_tal: This channel alreday openend\n"); return NULL; } mutex_lock(&apr_svc_ch[dl][dest][svc].m_lock); if (!apr_svc_ch[dl][dest][svc].dest_state) { rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].dest, apr_svc_ch[dl][dest][svc].dest_state, msecs_to_jiffies(APR_OPEN_TIMEOUT_MS)); if (rc == 0) { pr_err("apr_tal:open timeout\n"); mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); return NULL; } pr_debug("apr_tal:Wakeup done\n"); apr_svc_ch[dl][dest][svc].dest_state = 0; } rc = smd_named_open_on_edge(svc_names[dest][svc], dest, &apr_svc_ch[dl][dest][svc].ch, &apr_svc_ch[dl][dest][svc], apr_tal_notify); if (rc < 0) { pr_err("apr_tal: smd_open failed %s\n", svc_names[dest][svc]); mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); return NULL; } rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait, (apr_svc_ch[dl][dest][svc].smd_state == 1), 5 * HZ); if (rc == 0) { pr_err("apr_tal:TIMEOUT for OPEN event\n"); mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); apr_tal_close(&apr_svc_ch[dl][dest][svc]); return NULL; } smd_disable_read_intr(apr_svc_ch[dl][dest][svc].ch); if (!apr_svc_ch[dl][dest][svc].dest_state) { apr_svc_ch[dl][dest][svc].dest_state = 1; pr_debug("apr_tal:Waiting for apr svc init\n"); msleep(200); pr_debug("apr_tal:apr svc init done\n"); } apr_svc_ch[dl][dest][svc].smd_state = 0; apr_svc_ch[dl][dest][svc].func = func; apr_svc_ch[dl][dest][svc].priv = priv; mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); return &apr_svc_ch[dl][dest][svc]; }
static void smd_tty_notify(void *priv, unsigned event) { struct smd_tty_info *info = priv; unsigned long flags; switch (event) { case SMD_EVENT_DATA: spin_lock_irqsave(&info->reset_lock, flags); if (!info->is_open) { spin_unlock_irqrestore(&info->reset_lock, flags); break; } spin_unlock_irqrestore(&info->reset_lock, flags); /* There may be clients (tty framework) that are blocked * waiting for space to write data, so if a possible read * interrupt came in wake anyone waiting and disable the * interrupts */ if (smd_write_avail(info->ch)) { smd_disable_read_intr(info->ch); if (info->tty) wake_up_interruptible(&info->tty->write_wait); } tasklet_hi_schedule(&info->tty_tsklt); break; case SMD_EVENT_OPEN: spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 0; info->in_reset_updated = 1; info->is_open = 1; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); break; case SMD_EVENT_CLOSE: spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 1; info->in_reset_updated = 1; info->is_open = 0; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); /* schedule task to send TTY_BREAK */ tasklet_hi_schedule(&info->tty_tsklt); if (info->tty->index == LOOPBACK_IDX) schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000)); break; #ifdef CONFIG_LGE_USES_SMD_DS_TTY /* */ case SMD_EVENT_REOPEN_READY: /* smd channel is closed completely */ spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 1; info->in_reset_updated = 1; info->is_open = 0; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); break; #endif } }
/** @brief This function is used by the DAL Core to initialize the Control Transport for processing. It must be called prior to calling any other APIs of the Control Transport. @param szName: unique name for the channel that is to be opened uSize: size of the channel that must be opened (should fit the largest size of packet that the Dal Core wishes to send) wctsCBs: a list of callbacks that the CT needs to use to send notification and messages back to DAL @see @return A handle that must be used for further communication with the CTS. This is an opaque structure for the caller and it will be used in all communications to and from the CTS. */ WCTS_HandleType WCTS_OpenTransport ( const wpt_uint8* szName, wpt_uint32 uSize, WCTS_TransportCBsType* wctsCBs ) { WCTS_ControlBlockType* pWCTSCb; wpt_status status; int smdstatus; /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /*--------------------------------------------------------------------- Sanity check ---------------------------------------------------------------------*/ if ((NULL == wctsCBs) || (NULL == szName) || (NULL == wctsCBs->wctsNotifyCB) || (NULL == wctsCBs->wctsRxMsgCB)) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "WCTS_OpenTransport: Invalid parameters received."); return NULL; } /* This open is coming after a SSR, we don't need to reopen SMD, * the SMD port was never closed during SSR*/ if (gwctsHandle) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, "WCTS_OpenTransport port is already open\n"); pWCTSCb = gwctsHandle; if (WCTS_CB_MAGIC != pWCTSCb->wctsMagic) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_FATAL, "WCTS_OpenTransport: Invalid magic."); return NULL; } pWCTSCb->wctsState = WCTS_STATE_OPEN; pWCTSCb->wctsNotifyCB((WCTS_HandleType)pWCTSCb, WCTS_EVENT_OPEN, pWCTSCb->wctsNotifyCBData); /* we initially don't want read interrupts (we only want them if we get into deferred write mode) */ smd_disable_read_intr(pWCTSCb->wctsChannel); return (WCTS_HandleType)pWCTSCb; } #ifdef FEATURE_R33D if(port_open) { /* Port open before, not need to open again */ /* notified registered client that the channel is open */ ctsCB->wctsState = WCTS_STATE_OPEN; ctsCB->wctsNotifyCB((WCTS_HandleType)ctsCB, WCTS_EVENT_OPEN, ctsCB->wctsNotifyCBData); return (WCTS_HandleType)ctsCB; } #endif /* FEATURE_R33D */ /* allocate a ControlBlock to hold all context */ pWCTSCb = wpalMemoryAllocate(sizeof(*pWCTSCb)); if (NULL == pWCTSCb) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "WCTS_OpenTransport: Memory allocation failure."); return NULL; } /* make sure the control block is initialized. in particular we need to make sure the embedded event and list structures are initialized to prevent "magic number" tests from being run against uninitialized values */ wpalMemoryZero(pWCTSCb, sizeof(*pWCTSCb)); #ifdef FEATURE_R33D smd_init(0); port_open = 1; ctsCB = pWCTSCb; #endif /* FEATURE_R33D */ /*Initialise the event*/ wpalEventInit(&pWCTSCb->wctsEvent); /* save the user-supplied information */ pWCTSCb->wctsNotifyCB = wctsCBs->wctsNotifyCB; pWCTSCb->wctsNotifyCBData = wctsCBs->wctsNotifyCBData; pWCTSCb->wctsRxMsgCB = wctsCBs->wctsRxMsgCB; pWCTSCb->wctsRxMsgCBData = wctsCBs->wctsRxMsgCBData; /* initialize the remaining fields */ wpal_list_init(&pWCTSCb->wctsPendingQueue); pWCTSCb->wctsMagic = WCTS_CB_MAGIC; pWCTSCb->wctsState = WCTS_STATE_OPEN_PENDING; pWCTSCb->wctsChannel = NULL; /* since SMD will callback in interrupt context, we will used * canned messages to serialize the SMD events into a thread * context */ pWCTSCb->wctsOpenMsg.callback = WCTS_PALOpenCallback; pWCTSCb->wctsOpenMsg.pContext = pWCTSCb; pWCTSCb->wctsDataMsg.callback = WCTS_PALDataCallback; pWCTSCb->wctsDataMsg.pContext = pWCTSCb; /*--------------------------------------------------------------------- Open the SMD channel ---------------------------------------------------------------------*/ wpalEventReset(&pWCTSCb->wctsEvent); smdstatus = smd_named_open_on_edge(szName, SMD_APPS_WCNSS, &pWCTSCb->wctsChannel, pWCTSCb, WCTS_NotifyCallback); if (0 != smdstatus) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "%s: smd_named_open_on_edge failed with status %d", __func__, smdstatus); goto fail; } /* wait for the channel to be fully opened before we proceed */ status = wpalEventWait(&pWCTSCb->wctsEvent, WCTS_SMD_OPEN_TIMEOUT); if (eWLAN_PAL_STATUS_SUCCESS != status) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "%s: failed to receive SMD_EVENT_OPEN", __func__); /* since we opened one end of the channel, close it */ smdstatus = smd_close(pWCTSCb->wctsChannel); if (0 != smdstatus) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "%s: smd_close failed with status %d", __func__, smdstatus); } goto fail; } /* we initially don't want read interrupts (we only want them if we get into deferred write mode) */ smd_disable_read_intr(pWCTSCb->wctsChannel); /* we have successfully opened the SMD channel */ gwctsHandle = pWCTSCb; return (WCTS_HandleType)pWCTSCb; fail: /* we were unable to open the SMD channel */ pWCTSCb->wctsMagic = 0; wpalMemoryFree(pWCTSCb); return NULL; }/*WCTS_OpenTransport*/
/** @brief Callback function for serializing WCTS Write processing in the control context @param pWCTSCb WCTS Control Block @see @return void */ static void WCTS_PALWriteCallback ( WCTS_ControlBlockType* pWCTSCb ) { wpt_list_node* pNode; WCTS_BufferType* pBufferQueue; void* pBuffer; int len; int available; int written; /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*-------------------------------------------------------------------- Sanity check --------------------------------------------------------------------*/ if ((NULL == pWCTSCb) || (WCTS_CB_MAGIC != pWCTSCb->wctsMagic)) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "WCTS_PALWriteCallback: Invalid parameter received."); return; } /* if we are not deferred, then there are no pending packets */ if (WCTS_STATE_DEFERRED != pWCTSCb->wctsState) { return; } /* Keep sending deferred messages as long as there is room in the channel. Note that we initially peek at the head of the list to access the parameters for the next message; we don't actually remove the next message from the deferred list until we know the channel can handle it */ while (eWLAN_PAL_STATUS_SUCCESS == wpal_list_peek_front(&pWCTSCb->wctsPendingQueue, &pNode)) { pBufferQueue = container_of(pNode, WCTS_BufferType, node); pBuffer = pBufferQueue->pBuffer; len = pBufferQueue->bufferSize; available = smd_write_avail(pWCTSCb->wctsChannel); if (available < len) { /* channel has no room for the next packet so we are done */ return; } /* there is room for the next message, so we can now remove it from the deferred message queue and send it */ wpal_list_remove_front(&pWCTSCb->wctsPendingQueue, &pNode); /* note that pNode will be the same as when we peeked, so there is no need to update pBuffer or len */ written = smd_write(pWCTSCb->wctsChannel, pBuffer, len); if (written != len) { /* Something went wrong */ WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "WCTS_PALWriteCallback: channel write failure"); /* we were unable to send the message that was at the head of the deferred list. there is nothing else we can do other than drop it, so we will just fall through to the "success" processing. hopefully the client can recover from this since there is nothing else we can do here */ } /* whether we had success or failure, reclaim all memory */ wpalMemoryFree(pBuffer); wpalMemoryFree(pBufferQueue); /* we'll continue to iterate until the channel is full or all of the deferred messages have been sent */ } /* if we've exited the loop, then we have drained the deferred queue. set the state to indicate we are no longer deferred, and turn off the remote read interrupt */ pWCTSCb->wctsState = WCTS_STATE_OPEN; smd_disable_read_intr(pWCTSCb->wctsChannel); } /*WCTS_PALWriteCallback*/
static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) { int res = 0; unsigned int n = tty->index; struct smd_tty_info *info; const char *peripheral = NULL; if (n >= MAX_SMD_TTYS || !smd_tty[n].smd) return -ENODEV; info = smd_tty + n; mutex_lock(&smd_tty_lock); tty->driver_data = info; peripheral = smd_edge_to_subsystem(smd_tty[n].smd->edge); if (peripheral) { info->pil = subsystem_get(peripheral); if (IS_ERR(info->pil)) { SMD_TTY_INFO( "%s failed on smd_tty device :%s subsystem_get failed for %s", __func__, smd_tty[n].smd->port_name, peripheral); /* * Sleep, inorder to reduce the frequency of * retry by user-space modules and to avoid * possible watchdog bite. */ msleep((smd_tty[n].open_wait * 1000)); res = PTR_ERR(info->pil); goto out; } /* Wait for the modem SMSM to be inited for the SMD * Loopback channel to be allocated at the modem. Since * the wait need to be done atmost once, using msleep * doesn't degrade the performance. */ if (n == LOOPBACK_IDX) { if (!is_modem_smsm_inited()) msleep(5000); smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK); msleep(100); } /* * Wait for a channel to be allocated so we know * the modem is ready enough. */ if (smd_tty[n].open_wait) { res = wait_for_completion_interruptible_timeout( &info->ch_allocated, msecs_to_jiffies(smd_tty[n].open_wait * 1000)); if (res == 0) { SMD_TTY_INFO( "Timed out waiting for SMD channel %s", smd_tty[n].smd->port_name); res = -ETIMEDOUT; goto release_pil; } else if (res < 0) { SMD_TTY_INFO( "Error waiting for SMD channel %s : %d\n", smd_tty[n].smd->port_name, res); goto release_pil; } #ifdef CONFIG_MSM_SMD_TTY_DS_LEGACY /* * on boot, process tried to open smd0 sleeps until * modem is ready or timeout. */ if (n == DS_IDX) { /* wait for open ready status in seconds */ pr_info("%s: checking DS modem status\n", __func__); res = wait_event_interruptible_timeout( info->ch_opened_wait_queue, info->is_dsmodem_ready, (smd_tty_ds_modem_wait * HZ)); if (!res) { res = -ETIMEDOUT; pr_err("%s: timeout to wait for %s modem: %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_pil; } else if (res < 0) { pr_err("%s: Error waiting for %s modem: %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_pil; } pr_info("%s: DS modem is OK, open smd0..\n", __func__); } #endif } } tasklet_init(&info->tty_tsklt, smd_tty_read, (unsigned long)info); wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, smd_tty[n].smd->port_name); scnprintf(info->ra_wake_lock_name, MAX_RA_WAKE_LOCK_NAME_LEN, "SMD_TTY_%s_RA", smd_tty[n].smd->port_name); wake_lock_init(&info->ra_wake_lock, WAKE_LOCK_SUSPEND, info->ra_wake_lock_name); res = smd_named_open_on_edge(smd_tty[n].smd->port_name, smd_tty[n].smd->edge, &info->ch, info, smd_tty_notify); if (res < 0) { SMD_TTY_INFO("%s: %s open failed %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_wl_tl; } res = wait_event_interruptible_timeout(info->ch_opened_wait_queue, info->is_open, (2 * HZ)); if (res == 0) res = -ETIMEDOUT; if (res < 0) { SMD_TTY_INFO("%s: wait for %s smd_open failed %d\n", __func__, smd_tty[n].smd->port_name, res); goto close_ch; } SMD_TTY_INFO("%s with PID %u opened port %s", current->comm, current->pid, smd_tty[n].smd->port_name); smd_disable_read_intr(info->ch); mutex_unlock(&smd_tty_lock); return 0; close_ch: smd_close(info->ch); info->ch = NULL; release_wl_tl: tasklet_kill(&info->tty_tsklt); wake_lock_destroy(&info->wake_lock); wake_lock_destroy(&info->ra_wake_lock); release_pil: subsystem_put(info->pil); out: mutex_unlock(&smd_tty_lock); return res; }
static void smd_tty_notify(void *priv, unsigned event) { struct smd_tty_info *info = priv; struct tty_struct *tty; unsigned long flags; switch (event) { case SMD_EVENT_DATA: spin_lock_irqsave(&info->reset_lock, flags); if (!info->is_open) { spin_unlock_irqrestore(&info->reset_lock, flags); break; } spin_unlock_irqrestore(&info->reset_lock, flags); /* There may be clients (tty framework) that are blocked * waiting for space to write data, so if a possible read * interrupt came in wake anyone waiting and disable the * interrupts */ if (smd_write_avail(info->ch)) { smd_disable_read_intr(info->ch); tty = tty_port_tty_get(&info->port); if (tty) wake_up_interruptible(&tty->write_wait); tty_kref_put(tty); } spin_lock_irqsave(&info->ra_lock, flags); if (smd_read_avail(info->ch)) { wake_lock(&info->ra_wake_lock); tasklet_hi_schedule(&info->tty_tsklt); } spin_unlock_irqrestore(&info->ra_lock, flags); break; case SMD_EVENT_OPEN: spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 0; info->in_reset_updated = 1; info->is_open = 1; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); break; case SMD_EVENT_CLOSE: spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 1; info->in_reset_updated = 1; info->is_open = 0; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); /* schedule task to send TTY_BREAK */ tasklet_hi_schedule(&info->tty_tsklt); tty = tty_port_tty_get(&info->port); if (tty->index == LOOPBACK_IDX) schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000)); tty_kref_put(tty); break; #ifdef CONFIG_MSM_SMD_TTY_DS_LEGACY /* * At current smd_tty framework, if smd_tty_open() * is invoked by process before smd_tty_close() is * completely finished, smd_tty_open() may fail * because smd_tty_close() does not wait to close smd * channel from modem. To fix this situation, new SMD * notify status, SMD_EVENT_REOPEN_READY is used. * Until smd_tty receive this status, smd_tty_close() * will be wait(in fact, process will be wait). */ case SMD_EVENT_REOPEN_READY: /* smd channel is closed completely */ spin_lock_irqsave(&info->reset_lock, flags); info->in_reset = 1; info->in_reset_updated = 1; info->is_open = 0; wake_up_interruptible(&info->ch_opened_wait_queue); spin_unlock_irqrestore(&info->reset_lock, flags); break; #endif } }
static int hci_smd_register_dev(struct hci_smd_data *hsmd) { static struct hci_dev *hdev; int rc; /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); return -ENOMEM; } hsmd->hdev = hdev; hdev->bus = HCI_SMD; hdev->driver_data = NULL; hdev->open = hci_smd_open; hdev->close = hci_smd_close; hdev->send = hci_smd_send_frame; hdev->destruct = hci_smd_destruct; hdev->owner = THIS_MODULE; tasklet_init(&hsmd->hci_event_task, hci_smd_recv_event, (unsigned long) hsmd); tasklet_init(&hsmd->hci_data_task, hci_smd_recv_data, (unsigned long) hsmd); /* * Setup the timer to monitor whether the Rx queue is empty, * to control the wake lock release */ setup_timer(&hsmd->rx_q_timer, schedule_timer, (unsigned long) hsmd->hdev); /* Open the SMD Channel and device and register the callback function */ rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS, &hsmd->event_channel, hdev, hci_smd_notify_event); if (rc < 0) { BT_ERR("Cannot open the command channel"); hci_free_dev(hdev); hdev = NULL; return -ENODEV; } rc = smd_named_open_on_edge(DATA_CHANNEL, SMD_APPS_WCNSS, &hsmd->data_channel, hdev, hci_smd_notify_data); if (rc < 0) { BT_ERR("Failed to open the Data channel"); hci_free_dev(hdev); hdev = NULL; return -ENODEV; } /* Disable the read interrupts on the channel */ smd_disable_read_intr(hsmd->event_channel); smd_disable_read_intr(hsmd->data_channel); if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); return -ENODEV; } return 0; }
WCTS_HandleType WCTS_OpenTransport ( const wpt_uint8* szName, wpt_uint32 uSize, WCTS_TransportCBsType* wctsCBs ) { WCTS_ControlBlockType* pWCTSCb; wpt_status status; int smdstatus; if ((NULL == wctsCBs) || (NULL == szName) || (NULL == wctsCBs->wctsNotifyCB) || (NULL == wctsCBs->wctsRxMsgCB)) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "WCTS_OpenTransport: Invalid parameters received."); return NULL; } if (gwctsHandle) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, "WCTS_OpenTransport port is already open"); pWCTSCb = gwctsHandle; if (WCTS_CB_MAGIC != pWCTSCb->wctsMagic) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_FATAL, "WCTS_OpenTransport: Invalid magic."); return NULL; } pWCTSCb->wctsState = WCTS_STATE_OPEN; pWCTSCb->wctsNotifyCB((WCTS_HandleType)pWCTSCb, WCTS_EVENT_OPEN, pWCTSCb->wctsNotifyCBData); smd_disable_read_intr(pWCTSCb->wctsChannel); return (WCTS_HandleType)pWCTSCb; } #ifdef FEATURE_R33D if(port_open) { ctsCB->wctsState = WCTS_STATE_OPEN; ctsCB->wctsNotifyCB((WCTS_HandleType)ctsCB, WCTS_EVENT_OPEN, ctsCB->wctsNotifyCBData); return (WCTS_HandleType)ctsCB; } #endif pWCTSCb = wpalMemoryAllocate(sizeof(*pWCTSCb)); if (NULL == pWCTSCb) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "WCTS_OpenTransport: Memory allocation failure."); return NULL; } wpalMemoryZero(pWCTSCb, sizeof(*pWCTSCb)); #ifdef FEATURE_R33D smd_init(0); port_open = 1; ctsCB = pWCTSCb; #endif wpalEventInit(&pWCTSCb->wctsEvent); pWCTSCb->wctsNotifyCB = wctsCBs->wctsNotifyCB; pWCTSCb->wctsNotifyCBData = wctsCBs->wctsNotifyCBData; pWCTSCb->wctsRxMsgCB = wctsCBs->wctsRxMsgCB; pWCTSCb->wctsRxMsgCBData = wctsCBs->wctsRxMsgCBData; wpal_list_init(&pWCTSCb->wctsPendingQueue); pWCTSCb->wctsMagic = WCTS_CB_MAGIC; pWCTSCb->wctsState = WCTS_STATE_OPEN_PENDING; pWCTSCb->wctsChannel = NULL; pWCTSCb->wctsOpenMsg.callback = WCTS_PALOpenCallback; pWCTSCb->wctsOpenMsg.pContext = pWCTSCb; pWCTSCb->wctsOpenMsg.type= WPAL_MC_MSG_SMD_NOTIF_OPEN_SIG; pWCTSCb->wctsDataMsg.callback = WCTS_PALDataCallback; pWCTSCb->wctsDataMsg.pContext = pWCTSCb; pWCTSCb-> wctsDataMsg.type= WPAL_MC_MSG_SMD_NOTIF_DATA_SIG; wpalEventReset(&pWCTSCb->wctsEvent); smdstatus = smd_named_open_on_edge(szName, SMD_APPS_WCNSS, &pWCTSCb->wctsChannel, pWCTSCb, WCTS_NotifyCallback); if (0 != smdstatus) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "%s: smd_named_open_on_edge failed with status %d", __func__, smdstatus); goto fail; } status = wpalEventWait(&pWCTSCb->wctsEvent, WCTS_SMD_OPEN_TIMEOUT); if (eWLAN_PAL_STATUS_SUCCESS != status) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "%s: failed to receive SMD_EVENT_OPEN", __func__); smdstatus = smd_close(pWCTSCb->wctsChannel); if (0 != smdstatus) { WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, "%s: smd_close failed with status %d", __func__, smdstatus); } goto fail; } smd_disable_read_intr(pWCTSCb->wctsChannel); gwctsHandle = pWCTSCb; return (WCTS_HandleType)pWCTSCb; fail: pWCTSCb->wctsMagic = 0; wpalMemoryFree(pWCTSCb); return NULL; }
static int wcn36xx_start(struct ieee80211_hw *hw) { struct wcn36xx *wcn = hw->priv; int ret; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start"); /* SMD initialization */ ret = wcn36xx_smd_open(wcn); if (ret) { wcn36xx_error("Failed to open smd channel: %d", ret); goto out_err; } /* Not to receive INT until the whole buf from SMD is read */ smd_disable_read_intr(wcn->smd_ch); /* Allocate memory pools for Mgmt BD headers and Data BD headers */ ret = wcn36xx_dxe_allocate_mem_pools(wcn); if (ret) { wcn36xx_error("Failed to alloc DXE mempool: %d", ret); goto out_smd_close; } wcn36xx_dxe_alloc_ctl_blks(wcn); if (ret) { wcn36xx_error("Failed to alloc DXE ctl blocks: %d", ret); goto out_free_dxe_pool; } INIT_WORK(&wcn->rx_ready_work, wcn36xx_rx_ready_work); /* Maximum SMD message size is 4k */ wcn->smd_buf = kmalloc(4096, GFP_KERNEL); if (!wcn->smd_buf) { wcn36xx_error("Failed to allocate smd buf"); ret = -ENOMEM; goto out_free_dxe_ctl; } /* TODO pass configuration to FW */ ret = wcn36xx_smd_load_nv(wcn); if (ret) { wcn36xx_error("Failed to push NV to chip"); goto out_free_smd_buf; } ret = wcn36xx_smd_start(wcn); if (ret) { wcn36xx_error("Failed to start chip"); goto out_free_smd_buf; } /* DMA channel initialization */ ret = wcn36xx_dxe_init(wcn); if (ret) { wcn36xx_error("DXE init failed"); goto out_smd_stop; } wcn36xx_pmc_init(wcn); wcn36xx_debugfs_init(wcn); if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { ret = wcn36xx_smd_feature_caps_exchange(wcn); if (ret) wcn36xx_warn("Exchange feature caps failed"); } return 0; out_smd_stop: wcn36xx_smd_stop(wcn); out_free_smd_buf: kfree(wcn->smd_buf); out_free_dxe_pool: wcn36xx_dxe_free_mem_pools(wcn); out_free_dxe_ctl: wcn36xx_dxe_free_ctl_blks(wcn); out_smd_close: wcn36xx_smd_close(wcn); out_err: return ret; }