/* Task that listens for incomming IPC messages from Host and initiate host * command processing. */ void ipc_comm_task(void) { int ret = 0; uint32_t out_drbl, pkt_len; for (;;) { ret = task_wait_event_mask(EVENT_FLAG_BIT_READ_IPC | EVENT_FLAG_BIT_WRITE_IPC, -1); if ((ret & EVENT_FLAG_BIT_WRITE_IPC)) continue; else if (!(ret & EVENT_FLAG_BIT_READ_IPC)) continue; /* Read the command byte. This clears the FRMH bit in * the status byte. */ out_drbl = REG32(IPC_HOST2ISH_DOORBELL); pkt_len = out_drbl & IPC_HEADER_LENGTH_MASK; ret = ipc_read(IPC_PEER_HOST_ID, ipc_host_args, pkt_len); host_cmd_args.command = EC_COMMAND_PROTOCOL_3; host_cmd_args.result = EC_RES_SUCCESS; host_cmd_flags = ipc_host_args->flags; /* We only support new style command (v3) now */ if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { ipc_packet.send_response = ipc_send_response_packet; ipc_packet.request = (const void *)ipc_get_hostcmd_data_range(); ipc_packet.request_temp = params_copy; ipc_packet.request_max = sizeof(params_copy); /* Don't know the request size so pass in * the entire buffer */ ipc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; ipc_packet.response = (void *)ipc_get_hostcmd_data_range(); ipc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; ipc_packet.response_size = 0; ipc_packet.driver_result = EC_RES_SUCCESS; host_packet_receive(&ipc_packet); usleep(10); /* To force yield */ continue; } else { /* Old style command unsupported */ host_cmd_args.result = EC_RES_INVALID_COMMAND; } /* Hand off to host command handler */ host_command_received(&host_cmd_args); } }
/* Process the command in the i2c host buffer */ static void i2c_process_command(void) { struct host_cmd_handler_args *args = &host_cmd_args; char *buff = host_buffer; args->command = *buff; args->result = EC_RES_SUCCESS; if (args->command >= EC_CMD_VERSION0) { int csum, i; /* Read version and data size */ args->version = args->command - EC_CMD_VERSION0; args->command = buff[1]; args->params_size = buff[2]; /* Verify checksum */ for (csum = i = 0; i < args->params_size + 3; i++) csum += buff[i]; if ((uint8_t)csum != buff[i]) args->result = EC_RES_INVALID_CHECKSUM; buff += 3; i2c_old_response = 0; } else { /* * Old style (version 1) command. * * TODO(crosbug.com/p/23765): Nothing sends these anymore, * since this was superseded by version 2 before snow launched. * This code should be safe to remove. */ args->version = 0; args->params_size = EC_PROTO2_MAX_PARAM_SIZE; /* unknown */ buff++; i2c_old_response = 1; } /* we have an available command : execute it */ args->send_response = i2c_send_response; args->params = buff; /* skip room for error code, arglen */ args->response = host_buffer + 2; args->response_max = EC_PROTO2_MAX_PARAM_SIZE; args->response_size = 0; host_command_received(args); }
void acpi_1_interrupt(void) { uint8_t st = MEC1322_ACPI_EC_STATUS(1); if (!(st & EC_LPC_STATUS_FROM_HOST) || !(st & EC_LPC_STATUS_LAST_CMD)) return; /* Set the busy bit */ MEC1322_ACPI_EC_STATUS(1) |= EC_LPC_STATUS_PROCESSING; /* * Read the command byte. This clears the FRMH bit in * the status byte. */ host_cmd_args.command = MEC1322_ACPI_EC_OS2EC(1, 0); host_cmd_args.result = EC_RES_SUCCESS; host_cmd_flags = lpc_host_args->flags; /* We only support new style command (v3) now */ if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { lpc_packet.send_response = lpc_send_response_packet; lpc_packet.request = (const void *)lpc_get_hostcmd_data_range(); lpc_packet.request_temp = params_copy; lpc_packet.request_max = sizeof(params_copy); /* Don't know the request size so pass in the entire buffer */ lpc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; lpc_packet.response = (void *)lpc_get_hostcmd_data_range(); lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; lpc_packet.response_size = 0; lpc_packet.driver_result = EC_RES_SUCCESS; host_packet_receive(&lpc_packet); return; } else { /* Old style command unsupported */ host_cmd_args.result = EC_RES_INVALID_COMMAND; } /* Hand off to host command handler */ host_command_received(&host_cmd_args); }
/** * Handle write to host command I/O ports. * * @param is_cmd Is write command (1) or data (0)? */ static void handle_host_write(int is_cmd) { /* Set processing flag before reading command byte */ SET_BIT(NPCX_HIPMST(PMC_HOST_CMD), 2); /* * Read the command byte. This clears the FRMH bit in * the status byte. */ host_cmd_args.command = NPCX_HIPMDI(PMC_HOST_CMD); host_cmd_args.result = EC_RES_SUCCESS; host_cmd_args.send_response = lpc_send_response; host_cmd_flags = lpc_host_args->flags; /* See if we have an old or new style command */ if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { lpc_packet.send_response = lpc_send_response_packet; lpc_packet.request = (const void *)shm_mem_host_cmd; lpc_packet.request_temp = params_copy; lpc_packet.request_max = sizeof(params_copy); /* Don't know the request size so pass in the entire buffer */ lpc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; lpc_packet.response = (void *)shm_mem_host_cmd; lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; lpc_packet.response_size = 0; lpc_packet.driver_result = EC_RES_SUCCESS; host_packet_receive(&lpc_packet); return; } else if (host_cmd_flags & EC_HOST_ARGS_FLAG_FROM_HOST) { /* Version 2 (link) style command */ int size = lpc_host_args->data_size; int csum, i; /* Clear processing flag */ CLEAR_BIT(NPCX_HIPMST(PMC_HOST_CMD), 2); host_cmd_args.version = lpc_host_args->command_version; host_cmd_args.params = params_copy; host_cmd_args.params_size = size; host_cmd_args.response = cmd_params; host_cmd_args.response_max = EC_PROTO2_MAX_PARAM_SIZE; host_cmd_args.response_size = 0; /* Verify params size */ if (size > EC_PROTO2_MAX_PARAM_SIZE) { host_cmd_args.result = EC_RES_INVALID_PARAM; } else { const uint8_t *src = cmd_params; uint8_t *copy = params_copy; /* * Verify checksum and copy params out of LPC space. * This ensures the data acted on by the host command * handler can't be changed by host writes after the * checksum is verified. */ csum = host_cmd_args.command + host_cmd_flags + host_cmd_args.version + host_cmd_args.params_size; for (i = 0; i < size; i++) { csum += *src; *(copy++) = *(src++); } if ((uint8_t)csum != lpc_host_args->checksum) host_cmd_args.result = EC_RES_INVALID_CHECKSUM; } } else { /* Old style command, now unsupported */ host_cmd_args.result = EC_RES_INVALID_COMMAND; /* Clear processing flag */ CLEAR_BIT(NPCX_HIPMST(PMC_HOST_CMD), 2); } /* Hand off to host command handler */ host_command_received(&host_cmd_args); }
void pm2_ibf_interrupt(void) { uint8_t value __attribute__((unused)) = 0; uint8_t status; status = pm_get_status(LPC_HOST_CMD); /* IBE */ if (!(status & EC_LPC_STATUS_FROM_HOST)) { task_clear_pending_irq(IT83XX_IRQ_PMC2_IN); return; } /* IBF and data port */ if (!(status & EC_LPC_STATUS_LAST_CMD)) { /* R/C IBF*/ value = pm_get_data_in(LPC_HOST_CMD); task_clear_pending_irq(IT83XX_IRQ_PMC2_IN); return; } /* Set the busy bit */ pm_set_status(LPC_HOST_CMD, EC_LPC_STATUS_PROCESSING, 1); /* * Read the command byte. This clears the FRMH bit in * the status byte. */ host_cmd_args.command = pm_get_data_in(LPC_HOST_CMD); host_cmd_args.result = EC_RES_SUCCESS; if (host_cmd_args.command != EC_COMMAND_PROTOCOL_3) host_cmd_args.send_response = lpc_send_response; host_cmd_flags = lpc_host_args->flags; /* We only support new style command (v3) now */ if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { lpc_packet.send_response = lpc_send_response_packet; lpc_packet.request = (const void *)host_cmd_memmap; lpc_packet.request_temp = params_copy; lpc_packet.request_max = sizeof(params_copy); /* Don't know the request size so pass in the entire buffer */ lpc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; lpc_packet.response = (void *)host_cmd_memmap; lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; lpc_packet.response_size = 0; lpc_packet.driver_result = EC_RES_SUCCESS; host_packet_receive(&lpc_packet); task_clear_pending_irq(IT83XX_IRQ_PMC2_IN); return; } else { /* Old style command, now unsupported */ host_cmd_args.result = EC_RES_INVALID_COMMAND; } /* Hand off to host command handler */ host_command_received(&host_cmd_args); task_clear_pending_irq(IT83XX_IRQ_PMC2_IN); }
void host_packet_receive(struct host_packet *pkt) { const struct ec_host_request *r = (const struct ec_host_request *)pkt->request; const uint8_t *in = (const uint8_t *)pkt->request; uint8_t *itmp = (uint8_t *)pkt->request_temp; int csum = 0; int i; /* Track the packet we're handling */ pkt0 = pkt; /* If driver indicates error, don't even look at the data */ if (pkt->driver_result) { args0.result = pkt->driver_result; goto host_packet_bad; } if (pkt->request_size < sizeof(*r)) { /* Packet too small for even a header */ args0.result = EC_RES_REQUEST_TRUNCATED; goto host_packet_bad; } if (pkt->request_size > pkt->request_max) { /* Got a bigger request than the interface can handle */ args0.result = EC_RES_REQUEST_TRUNCATED; goto host_packet_bad; } /* * Response buffer needs to be big enough for a header. If it's not * we can't even return an error packet. */ ASSERT(pkt->response_max >= sizeof(struct ec_host_response)); /* Start checksum and copy request header if necessary */ if (pkt->request_temp) { /* Copy to temp buffer and checksum */ for (i = sizeof(*r); i > 0; i--) { *itmp = *in++; csum += *itmp++; } r = (const struct ec_host_request *)pkt->request_temp; } else { /* Just checksum */ for (i = sizeof(*r); i > 0; i--) csum += *in++; } if (r->struct_version != EC_HOST_REQUEST_VERSION) { /* Request header we don't know how to handle */ args0.result = EC_RES_INVALID_HEADER; goto host_packet_bad; } if (pkt->request_size < sizeof(*r) + r->data_len) { /* * Packet too small for expected params. Note that it's ok if * the received packet data is too big; some interfaces may pad * the data at the end (SPI) or may not know how big the * received data is (LPC). */ args0.result = EC_RES_REQUEST_TRUNCATED; goto host_packet_bad; } /* Copy request data and validate checksum */ if (pkt->request_temp) { /* Params go in temporary buffer */ args0.params = itmp; /* Copy request data and checksum */ for (i = r->data_len; i > 0; i--) { *itmp = *in++; csum += *itmp++; } } else { /* Params read directly from request */ args0.params = in; /* Just checksum */ for (i = r->data_len; i > 0; i--) csum += *in++; } /* Validate checksum */ if ((uint8_t)csum) { args0.result = EC_RES_INVALID_CHECKSUM; goto host_packet_bad; } /* Set up host command handler args */ args0.send_response = host_packet_respond; args0.command = r->command; args0.version = r->command_version; args0.params_size = r->data_len; args0.response = (struct ec_host_response *)(pkt->response) + 1; args0.response_max = pkt->response_max - sizeof(struct ec_host_response); args0.response_size = 0; args0.result = EC_RES_SUCCESS; /* Chain to host command received */ host_command_received(&args0); return; host_packet_bad: /* * TODO (crosbug.com/p/29315): This is typically running in interrupt * context, so it woud be better not to send the response here, and to * let the host command task send the response. */ /* Improperly formed packet from host, so send an error response */ host_packet_respond(&args0); }
/** * Handle an event on the NSS pin * * A falling edge of NSS indicates that the master is starting a new * transaction. A rising edge indicates that we have finsihed * * @param signal GPIO signal for the NSS pin */ void spi_event(enum gpio_signal signal) { stm32_dma_chan_t *rxdma; uint16_t *nss_reg; uint32_t nss_mask; uint16_t i; /* If not enabled, ignore glitches on NSS */ if (!enabled) return; /* Check chip select. If it's high, the AP ended a transaction. */ nss_reg = gpio_get_level_reg(GPIO_SPI1_NSS, &nss_mask); if (REG16(nss_reg) & nss_mask) { enable_sleep(SLEEP_MASK_SPI); /* * If the buffer is still used by the host command, postpone * the DMA rx setup. */ if (state == SPI_STATE_PROCESSING) { setup_transaction_later = 1; return; } /* Set up for the next transaction */ spi_init(); /* Fix for bug chrome-os-partner:31390 */ return; } disable_sleep(SLEEP_MASK_SPI); /* Chip select is low = asserted */ if (state != SPI_STATE_READY_TO_RX) { /* * AP started a transaction but we weren't ready for it. * Tell AP we weren't ready, and ignore the received data. */ CPRINTS("SPI not ready"); tx_status(EC_SPI_NOT_READY); state = SPI_STATE_RX_BAD; return; } /* We're now inside a transaction */ state = SPI_STATE_RECEIVING; tx_status(EC_SPI_RECEIVING); rxdma = dma_get_channel(STM32_DMAC_SPI1_RX); /* Wait for version, command, length bytes */ if (wait_for_bytes(rxdma, 3, nss_reg, nss_mask)) goto spi_event_error; if (in_msg[0] == EC_HOST_REQUEST_VERSION) { /* Protocol version 3 */ struct ec_host_request *r = (struct ec_host_request *)in_msg; int pkt_size; /* Wait for the rest of the command header */ if (wait_for_bytes(rxdma, sizeof(*r), nss_reg, nss_mask)) goto spi_event_error; /* * Check how big the packet should be. We can't just wait to * see how much data the host sends, because it will keep * sending dummy data until we respond. */ pkt_size = host_request_expected_size(r); if (pkt_size == 0 || pkt_size > sizeof(in_msg)) goto spi_event_error; /* Wait for the packet data */ if (wait_for_bytes(rxdma, pkt_size, nss_reg, nss_mask)) goto spi_event_error; spi_packet.send_response = spi_send_response_packet; spi_packet.request = in_msg; spi_packet.request_temp = NULL; spi_packet.request_max = sizeof(in_msg); spi_packet.request_size = pkt_size; /* Response must start with the preamble */ memcpy(out_msg, out_preamble, sizeof(out_preamble)); spi_packet.response = out_msg + sizeof(out_preamble); /* Reserve space for the preamble and trailing past-end byte */ spi_packet.response_max = sizeof(out_msg) - sizeof(out_preamble) - EC_SPI_PAST_END_LENGTH; spi_packet.response_size = 0; spi_packet.driver_result = EC_RES_SUCCESS; /* Move to processing state */ state = SPI_STATE_PROCESSING; tx_status(EC_SPI_PROCESSING); host_packet_receive(&spi_packet); return; } else if (in_msg[0] >= EC_CMD_VERSION0) { /* * Protocol version 2 * * TODO(crosbug.com/p/20257): Remove once kernel supports * version 3. */ #ifdef CHIP_FAMILY_STM32F0 CPRINTS("WARNING: Protocol version 2 is not supported on the F0" " line due to crbug.com/31390"); #endif args.version = in_msg[0] - EC_CMD_VERSION0; args.command = in_msg[1]; args.params_size = in_msg[2]; /* Wait for parameters */ if (wait_for_bytes(rxdma, 3 + args.params_size, nss_reg, nss_mask)) goto spi_event_error; /* * Params are not 32-bit aligned in protocol version 2. As a * workaround, move them to the beginning of the input buffer * so they are aligned. */ if (args.params_size) memmove(in_msg, in_msg + 3, args.params_size); args.params = in_msg; args.send_response = spi_send_response; /* Allow room for the header bytes */ args.response = out_msg + SPI_PROTO2_OFFSET; args.response_max = sizeof(out_msg) - SPI_PROTO2_OVERHEAD; args.response_size = 0; args.result = EC_RES_SUCCESS; /* Move to processing state */ state = SPI_STATE_PROCESSING; tx_status(EC_SPI_PROCESSING); host_command_received(&args); return; } spi_event_error: /* Error, timeout, or protocol we can't handle. Ignore data. */ tx_status(EC_SPI_RX_BAD_DATA); state = SPI_STATE_RX_BAD; CPRINTS("SPI rx bad data"); CPRINTF("in_msg=["); for (i = 0; i < dma_bytes_done(rxdma, sizeof(in_msg)); i++) CPRINTF("%02x ", in_msg[i]); CPRINTF("]\n"); }