summaryrefslogtreecommitdiff
path: root/common/pd_log.c
blob: f0e9312117da5ebea88b9f7ad50cb748a54fc04f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* Copyright (c) 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 int 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 int 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:
		charge_manager_save_log(port);
		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 */