diff --git a/devicemodel/hw/platform/ioc.c b/devicemodel/hw/platform/ioc.c index 7ee78a9d8..928f2b5df 100644 --- a/devicemodel/hw/platform/ioc.c +++ b/devicemodel/hw/platform/ioc.c @@ -767,14 +767,33 @@ cbc_request_dequeue(struct ioc_dev *ioc, enum cbc_queue_type qtype) return free; } +/* + * Send a cbc_request to TX handler + */ +static int +send_tx_request(struct ioc_dev *ioc, enum cbc_request_type type) +{ + struct cbc_request *req; + + req = cbc_request_dequeue(ioc, CBC_QUEUE_T_FREE); + if (!req) { + DPRINTF("%s", "ioc sends a tx request failed\r\n"); + return -1; + } + + req->rtype = type; + cbc_request_enqueue(ioc, req, CBC_QUEUE_T_TX, true); + return 0; +} + /* * Process hb active event before transfer to next state */ static int process_hb_active_event(struct ioc_dev *ioc) { - /* TODO: Need implementation */ - return 0; + /* Enable wakeup reason bit 23 that indicating UOS is active */ + return send_tx_request(ioc, CBC_REQ_T_UOS_ACTIVE); } /* @@ -783,8 +802,22 @@ process_hb_active_event(struct ioc_dev *ioc) static int process_ram_refresh_event(struct ioc_dev *ioc) { - /* TODO: Need implementation */ - return 0; + int rc; + + /* Rx and Tx threads discard all CBC protocol packets */ + ioc->cbc_enable = false; + + /* + * Tx handler sents shutdown wakeup reason, + * Then enter suspended state. + */ + rc = send_tx_request(ioc, CBC_REQ_T_UOS_INACTIVE); + + /* + * TODO: set suspend to PM DM + */ + + return rc; } /* @@ -793,8 +826,22 @@ process_ram_refresh_event(struct ioc_dev *ioc) static int process_hb_inactive_event(struct ioc_dev *ioc) { - /* TODO: Need implementation */ - return 0; + int rc; + + /* Rx and Tx threads discard all CBC protocol packets */ + ioc->cbc_enable = false; + + /* + * Tx sents shutdown wakeup reason, + * Then enter shutdown state. + */ + rc = send_tx_request(ioc, CBC_REQ_T_UOS_INACTIVE); + + /* + * TODO: set shutdown to PM DM + */ + + return rc; } /* @@ -803,7 +850,26 @@ process_hb_inactive_event(struct ioc_dev *ioc) static int process_shutdown_event(struct ioc_dev *ioc) { - /* TODO: Need implementation */ + int i; + struct ioc_ch_info *chl; + + /* + * Due to native CBC driver buffer will be full if the native CBC char + * devices are opened, but not keep reading. So close the native devices + * when removing them from epoll. + */ + for (i = 0, chl = ioc_ch_tbl; i < ARRAY_SIZE(ioc_ch_tbl); i++, chl++) { + switch (i) { + case IOC_NATIVE_PMT ... IOC_NATIVE_RAW11: + if (chl->stat == IOC_CH_OFF || chl->fd < 0) + continue; + epoll_ctl(ioc->epfd, EPOLL_CTL_DEL, chl->fd, NULL); + close(chl->fd); + chl->fd = IOC_INIT_FD; + break; + } + } + return 0; } @@ -813,7 +879,29 @@ process_shutdown_event(struct ioc_dev *ioc) static int process_resume_event(struct ioc_dev *ioc) { - /* TODO: Need implementation */ + int i; + struct ioc_ch_info *chl; + + /* Rx and Tx threads begin to process CBC protocol packets */ + ioc->cbc_enable = true; + + /* re-open native CBC char devices and add them into epoll */ + for (i = 0, chl = ioc_ch_tbl; i < ARRAY_SIZE(ioc_ch_tbl); i++, chl++) { + switch (i) { + case IOC_NATIVE_PMT ... IOC_NATIVE_RAW11: + if (chl->stat == IOC_CH_OFF || chl->fd != IOC_INIT_FD) + continue; + + chl->fd = ioc_open_native_ch(chl->name); + if (chl->fd > 0) + epoll_ctl(ioc->epfd, EPOLL_CTL_ADD, chl->fd, + &ioc->evts[i]); + else + DPRINTF("ioc open failed, channel:%s\r\n", + chl->name); + break; + } + } return 0; } @@ -846,6 +934,21 @@ ioc_process_events(struct ioc_dev *ioc, enum ioc_ch_id id) } } +/* + * For a new event update. + * The interface is for mult-threads, currently write one byte to core thread, + * So if write more types, needs to add protection for thread safety. + */ +void +ioc_update_event(int fd, enum ioc_event_type evt) +{ + uint8_t val = evt; + + if (write(fd, &val, sizeof(val)) < 0) + DPRINTF("ioc update event failed, error:%s\r\n", + strerror(errno)); +} + /* * Build a cbc_request with CBC link frame and add the cbc_request to * the rx queue tail. @@ -1027,7 +1130,7 @@ ioc_rx_thread(void *arg) memset(&packet, 0, sizeof(packet)); packet.cfg = &ioc->rx_config; - packet.boot_reason = ioc_boot_reason; + packet.ioc = ioc; for (;;) { pthread_mutex_lock(&ioc->rx_mtx); @@ -1080,7 +1183,7 @@ ioc_tx_thread(void *arg) memset(&packet, 0, sizeof(packet)); packet.cfg = &ioc->tx_config; - packet.boot_reason = ioc_boot_reason; + packet.ioc = ioc; for (;;) { pthread_mutex_lock(&ioc->tx_mtx); @@ -1250,6 +1353,12 @@ ioc_init(struct vmctx *ctx) /* Set event fd to default value */ ioc->evt_fd = IOC_INIT_FD; + /* Enable CBC packet processing by default */ + ioc->cbc_enable = true; + + /* Set boot reason from IOC mediator boot command line */ + ioc->boot_reason = ioc_boot_reason; + /* * Initialize native CBC cdev and virtual UART. */ diff --git a/devicemodel/hw/platform/ioc_cbc.c b/devicemodel/hw/platform/ioc_cbc.c index 07a37db0d..2eda8b755 100644 --- a/devicemodel/hw/platform/ioc_cbc.c +++ b/devicemodel/hw/platform/ioc_cbc.c @@ -586,31 +586,30 @@ cbc_send_pkt(struct cbc_pkt *pkt) static void cbc_update_heartbeat(struct cbc_pkt *pkt, uint8_t cmd, uint8_t sus_action) { - uint8_t stat; + enum ioc_event_type evt; - (void) sus_action; - - /* - * If heartbeat state switches(active/inactive), update the new state - * value based on heartbeat commands. - * The default state is inactive. - */ - if (cmd == CBC_HB_ACTIVE || cmd == CBC_HB_STANDBY || - cmd == CBC_HB_INITIAL) - stat = 1; + if (cmd == CBC_HB_INITIAL || cmd == CBC_HB_ACTIVE || + cmd == CBC_HB_STANDBY || + cmd == CBC_HB_SD_DLY) + evt = IOC_E_HB_ACTIVE; + else if (cmd == CBC_HB_SD_PREP) + evt = (sus_action == CBC_SS_REFRESH) ? IOC_E_RAM_REFRESH : + IOC_E_HB_INACTIVE; else - stat = 0; + return; - /* Default heartbeat state is zero, that means not active */ - if (stat != pkt->hb_state) { - /* - * Route the cbc request to the tx thread - * and request type is SOC state update - */ + /* Update IOC state with a new event */ + if (evt != pkt->evt) { + ioc_update_event(pkt->ioc->evt_fd, evt); + pkt->evt = evt; + } + + /* Tigger a wakeup reason immediately */ + if (cmd == CBC_HB_INITIAL) { + + /* Route the cbc request to the tx thread */ pkt->qtype = CBC_QUEUE_T_TX; - pkt->req->rtype = CBC_REQ_T_SOC; - pkt->req->buf[0] = stat; - pkt->hb_state = stat; + pkt->req->rtype = CBC_REQ_T_HB_INIT; } } @@ -670,6 +669,11 @@ cbc_process_wakeup_reason(struct cbc_pkt *pkt) */ pkt->reason = reason; + if (pkt->uos_active) + reason |= CBC_WK_RSN_SOC; + else + reason &= ~CBC_WK_RSN_SOC; + /* Update periodic wakeup reason */ cbc_update_wakeup_reason(pkt, reason); @@ -795,11 +799,18 @@ cbc_rx_handler(struct cbc_pkt *pkt) uint8_t mux, prio; /* - * FIXME: need to check CBC request type in the rx handler - * currently simply check is enough, expand the check in the further + * Rx only handle CBC protocol packet currently. + * Drop the packet when the CBC protocl status is not enable. */ - if (pkt->req->rtype != CBC_REQ_T_PROT) + if (pkt->req->rtype != CBC_REQ_T_PROT || + pkt->ioc->cbc_enable == false) { + + /* Discard the packet */ + DPRINTF("ioc rx discard the packet, type:%d\r\n", + pkt->req->rtype); return; + } + /* * TODO: use this prio to enable dynamic cbc priority configuration * feature in the future, currently ignore it. @@ -834,7 +845,7 @@ cbc_rx_handler(struct cbc_pkt *pkt) void cbc_tx_handler(struct cbc_pkt *pkt) { - if (pkt->req->rtype == CBC_REQ_T_PROT) { + if (pkt->req->rtype == CBC_REQ_T_PROT && pkt->ioc->cbc_enable) { switch (pkt->req->id) { case IOC_NATIVE_LFCC: cbc_process_wakeup_reason(pkt); @@ -850,20 +861,39 @@ cbc_tx_handler(struct cbc_pkt *pkt) pkt->req->id); break; } - } else if (pkt->req->rtype == CBC_REQ_T_SOC) { - /* - * Update wakeup reasons with SoC new state - * the new state update by heartbeat state change - * (active/inactive) in rx thread - */ - pkt->soc_active = pkt->req->buf[0]; - cbc_update_wakeup_reason(pkt, pkt->reason); + } else if (pkt->req->rtype == CBC_REQ_T_HB_INIT) { - /* Send wakeup reason */ + /* + * Boot reason represents the wakeup reason from IOC mediator + * boot command line or resume callback. + */ + cbc_update_wakeup_reason(pkt, pkt->ioc->boot_reason | + CBC_WK_RSN_SOC); cbc_send_pkt(pkt); + + /* Heartbeat init also indicates UOS enter active state */ + pkt->uos_active = true; + } else if (pkt->req->rtype == CBC_REQ_T_UOS_ACTIVE) { + cbc_update_wakeup_reason(pkt, pkt->reason | CBC_WK_RSN_SOC); + cbc_send_pkt(pkt); + + /* Enable UOS active flag */ + pkt->uos_active = true; + } else if (pkt->req->rtype == CBC_REQ_T_UOS_INACTIVE) { + cbc_update_wakeup_reason(pkt, CBC_WK_RSN_SHUTDOWN); + cbc_send_pkt(pkt); + + /* Disable UOS active flag */ + pkt->uos_active = false; + + /* + * After sending shutdown wakeup reason, then trigger shutdown + * IOC event that IOC mediator can enter suspended. + */ + ioc_update_event(pkt->ioc->evt_fd, IOC_E_SHUTDOWN); } else { - /* TODO: others request types process */ - DPRINTF("ioc invalid cbc_request type in tx:%d\r\n", + /* Discard the packet */ + DPRINTF("ioc tx discard the packet, type:%d\r\n", pkt->req->rtype); } } diff --git a/devicemodel/include/ioc.h b/devicemodel/include/ioc.h index 449cf1830..b5a0f38ec 100644 --- a/devicemodel/include/ioc.h +++ b/devicemodel/include/ioc.h @@ -103,6 +103,9 @@ #define CBC_WK_RSN_DOR (1 << 11) /* CBC wakeup reason field cardoor */ #define CBC_WK_RSN_SOC (1 << 23) /* CBC wakeup reason field soc */ +/* CBC wakeup reason filed suspend or shutdown */ +#define CBC_WK_RSN_SHUTDOWN (0) + /* * IOC mediator permits button, rtc and cardoor wakeup reasons which comes from * IOC firmware, others will be masked. @@ -578,7 +581,9 @@ enum cbc_request_type { CBC_REQ_T_PROT, /* CBC protocol request */ CBC_REQ_T_SUSPEND, /* CBC suspend request */ CBC_REQ_T_SHUTDOWN, /* CBC shutdown request */ - CBC_REQ_T_SOC /* SOC state update request */ + CBC_REQ_T_HB_INIT, /* CBC Heartbeat init request */ + CBC_REQ_T_UOS_ACTIVE, /* CBC UOS active request */ + CBC_REQ_T_UOS_INACTIVE /* CBC UOS inactive request */ }; /* @@ -683,13 +688,13 @@ enum ioc_event_type { * CBC packet is mainly structure for CBC protocol process. */ struct cbc_pkt { - uint8_t soc_active; /* Record soc state */ - uint8_t hb_state; /* Record Heartbeat state */ + bool uos_active; /* Mark UOS active status */ uint32_t reason; /* Record current wakeup reason */ - uint32_t boot_reason; /* Record boot up wakeup reason */ struct cbc_request *req; /* CBC packet data */ struct cbc_config *cfg; /* CBC and whitelist configurations */ enum cbc_queue_type qtype; /* Routes cbc_request to queue */ + enum ioc_event_type evt; /* Record last event */ + struct ioc_dev *ioc; /* IOC device */ }; /* @@ -705,9 +710,11 @@ SIMPLEQ_HEAD(cbc_qhead, cbc_request); */ struct ioc_dev { char name[16]; /* Core thread name */ + bool cbc_enable; /* Tx and Rx protocol enable flag */ int closing; /* Close IOC mediator device flag */ int epfd; /* Epoll fd */ int32_t evt_fd; /* Pipe write fd to trigger one event */ + uint32_t boot_reason; /* Boot or resume wakeup reason */ enum ioc_state_type state; /* IOC state type */ struct epoll_event *evts; /* Epoll events table */ struct cbc_request *pool; /* CBC requests pool */ @@ -776,4 +783,7 @@ void wlist_init_group(struct cbc_group *cbc_tbl, size_t cbc_size, /* Set CBC log file */ void cbc_set_log_file(FILE *f); + +/* Update IOC state by the event */ +void ioc_update_event(int fd, enum ioc_event_type evt); #endif