135 lines
3.9 KiB
C
135 lines
3.9 KiB
C
|
/* Copyright 2014 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*/
|
||
|
|
||
|
#include "charge_manager.h"
|
||
|
#include "console.h"
|
||
|
#include "event_log.h"
|
||
|
#include "host_command.h"
|
||
|
#include "timer.h"
|
||
|
#include "usb_pd.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
/*
|
||
|
* Ensure PD logging parameters are compatible with the generic logging
|
||
|
* framework that we're calling into.
|
||
|
*/
|
||
|
BUILD_ASSERT(sizeof(struct ec_response_pd_log) ==
|
||
|
sizeof(struct event_log_entry));
|
||
|
BUILD_ASSERT(PD_LOG_SIZE_MASK == EVENT_LOG_SIZE_MASK);
|
||
|
BUILD_ASSERT(PD_LOG_TIMESTAMP_SHIFT == EVENT_LOG_TIMESTAMP_SHIFT);
|
||
|
BUILD_ASSERT(PD_EVENT_NO_ENTRY == EVENT_LOG_NO_ENTRY);
|
||
|
|
||
|
void pd_log_event(uint8_t type, uint8_t size_port,
|
||
|
uint16_t data, void *payload)
|
||
|
{
|
||
|
uint32_t timestamp = get_time().val >> PD_LOG_TIMESTAMP_SHIFT;
|
||
|
|
||
|
log_add_event(type, size_port, data, payload, timestamp);
|
||
|
}
|
||
|
|
||
|
#ifdef HAS_TASK_HOSTCMD
|
||
|
|
||
|
/* number of accessory entries we have queued since last check */
|
||
|
static volatile int incoming_logs;
|
||
|
|
||
|
void pd_log_recv_vdm(int port, int cnt, uint32_t *payload)
|
||
|
{
|
||
|
struct ec_response_pd_log *r = (void *)&payload[1];
|
||
|
/* update port number from MCU point of view */
|
||
|
size_t size = PD_LOG_SIZE(r->size_port);
|
||
|
uint8_t size_port = PD_LOG_PORT_SIZE(port, size);
|
||
|
uint32_t timestamp;
|
||
|
|
||
|
if ((cnt < 2 + DIV_ROUND_UP(size, sizeof(uint32_t))) ||
|
||
|
!(payload[0] & VDO_SRC_RESPONDER))
|
||
|
/* Not a proper log entry, bail out */
|
||
|
return;
|
||
|
|
||
|
if (r->type != PD_EVENT_NO_ENTRY) {
|
||
|
timestamp = (get_time().val >> PD_LOG_TIMESTAMP_SHIFT)
|
||
|
- r->timestamp;
|
||
|
log_add_event(r->type, size_port, r->data, r->payload,
|
||
|
timestamp);
|
||
|
/* record that we have enqueued new content */
|
||
|
incoming_logs++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* we are a PD MCU/EC, send back the events to the host */
|
||
|
static enum ec_status hc_pd_get_log_entry(struct host_cmd_handler_args *args)
|
||
|
{
|
||
|
struct ec_response_pd_log *r = args->response;
|
||
|
|
||
|
dequeue_retry:
|
||
|
args->response_size = log_dequeue_event((struct event_log_entry *)r);
|
||
|
/* if the MCU log no longer has entries, try connected accessories */
|
||
|
if (r->type == PD_EVENT_NO_ENTRY) {
|
||
|
int i, res;
|
||
|
incoming_logs = 0;
|
||
|
for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; ++i) {
|
||
|
/* only accessories who knows Google logging format */
|
||
|
if (pd_get_identity_vid(i) != USB_VID_GOOGLE)
|
||
|
continue;
|
||
|
res = pd_fetch_acc_log_entry(i);
|
||
|
if (res == EC_RES_BUSY) /* host should retry */
|
||
|
return EC_RES_BUSY;
|
||
|
}
|
||
|
/* we have received new entries from an accessory */
|
||
|
if (incoming_logs)
|
||
|
goto dequeue_retry;
|
||
|
/* else the current entry is already "PD_EVENT_NO_ENTRY" */
|
||
|
}
|
||
|
|
||
|
return EC_RES_SUCCESS;
|
||
|
}
|
||
|
DECLARE_HOST_COMMAND(EC_CMD_PD_GET_LOG_ENTRY,
|
||
|
hc_pd_get_log_entry,
|
||
|
EC_VER_MASK(0));
|
||
|
|
||
|
static enum ec_status hc_pd_write_log_entry(struct host_cmd_handler_args *args)
|
||
|
{
|
||
|
const struct ec_params_pd_write_log_entry *p = args->params;
|
||
|
uint8_t type = p->type;
|
||
|
uint8_t port = p->port;
|
||
|
|
||
|
if (type < PD_EVENT_MCU_BASE || type >= PD_EVENT_ACC_BASE)
|
||
|
return EC_RES_INVALID_PARAM;
|
||
|
if (port > 0 && port >= CONFIG_USB_PD_PORT_COUNT)
|
||
|
return EC_RES_INVALID_PARAM;
|
||
|
|
||
|
switch (type) {
|
||
|
/* Charge event: Log data for all ports */
|
||
|
case PD_EVENT_MCU_CHARGE:
|
||
|
#ifdef CONFIG_CHARGE_MANAGER
|
||
|
charge_manager_save_log(port);
|
||
|
#endif
|
||
|
break;
|
||
|
|
||
|
/* Other events: no extra data, just log event type + port */
|
||
|
case PD_EVENT_MCU_CONNECT:
|
||
|
case PD_EVENT_MCU_BOARD_CUSTOM:
|
||
|
default:
|
||
|
pd_log_event(type, PD_LOG_PORT_SIZE(port, 0), 0, NULL);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return EC_RES_SUCCESS;
|
||
|
}
|
||
|
DECLARE_HOST_COMMAND(EC_CMD_PD_WRITE_LOG_ENTRY,
|
||
|
hc_pd_write_log_entry,
|
||
|
EC_VER_MASK(0));
|
||
|
#else /* !HAS_TASK_HOSTCMD */
|
||
|
/* we are a PD accessory, send back the events as a VDM (VDO_CMD_GET_LOG) */
|
||
|
int pd_vdm_get_log_entry(uint32_t *payload)
|
||
|
{
|
||
|
struct ec_response_pd_log *r = (void *)&payload[1];
|
||
|
int byte_size;
|
||
|
|
||
|
byte_size = log_dequeue_event((struct event_log_entry *)r);
|
||
|
|
||
|
return 1 + DIV_ROUND_UP(byte_size, sizeof(uint32_t));
|
||
|
}
|
||
|
#endif /* !HAS_TASK_HOSTCMD */
|