1/*-
2 * Copyright (c) 2017 Broadcom. All rights reserved.
3 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 *    this list of conditions and the following disclaimer in the documentation
13 *    and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $FreeBSD: stable/11/sys/dev/ocs_fc/ocs_vpd.h 331766 2018-03-30 15:28:25Z ken $
32 */
33
34/**
35 * @file
36 * OCS VPD parser
37 */
38
39#if !defined(__OCS_VPD_H__)
40#define __OCS_VPD_H__
41
42/**
43 * @brief VPD buffer structure
44 */
45
46typedef struct {
47	uint8_t *buffer;
48	uint32_t length;
49	uint32_t offset;
50	uint8_t checksum;
51	} vpdbuf_t;
52
53/**
54 * @brief return next VPD byte
55 *
56 * Returns next VPD byte and updates accumulated checksum
57 *
58 * @param vpd pointer to vpd buffer
59 *
60 * @return returns next byte for success, or a negative error code value for failure.
61 *
62 */
63
64static inline int
65vpdnext(vpdbuf_t *vpd)
66{
67	int rc = -1;
68	if (vpd->offset < vpd->length) {
69		rc = vpd->buffer[vpd->offset++];
70		vpd->checksum += rc;
71	}
72	return rc;
73}
74
75/**
76 * @brief return true if no more vpd buffer data
77 *
78 * return true if the vpd buffer data has been completely consumed
79 *
80 * @param vpd pointer to vpd buffer
81 *
82 * @return returns true if no more data
83 *
84 */
85static inline int
86vpddone(vpdbuf_t *vpd)
87{
88	return vpd->offset >= vpd->length;
89}
90/**
91 * @brief return pointer to current VPD data location
92 *
93 * Returns a pointer to the current location in the VPD data
94 *
95 * @param vpd pointer to vpd buffer
96 *
97 * @return pointer to current VPD data location
98 */
99
100static inline uint8_t *
101vpdref(vpdbuf_t *vpd)
102{
103	return &vpd->buffer[vpd->offset];
104}
105
106#define VPD_LARGE_RESOURCE_TYPE_ID_STRING_TAG	0x82
107#define VPD_LARGE_RESOURCE_TYPE_R_TAG		0x90
108#define VPD_LARGE_RESOURCE_TYPE_W_TAG		0x91
109#define VPD_SMALL_RESOURCE_TYPE_END_TAG		0x78
110
111/**
112 * @brief find a VPD entry
113 *
114 * Finds a VPD entry given the two character code
115 *
116 * @param vpddata pointer to raw vpd data buffer
117 * @param vpddata_length length of vpddata buffer in bytes
118 * @param key key to look up
119
120 * @return returns a pointer to the key location or NULL if not found or checksum error
121 */
122
123static inline uint8_t *
124ocs_find_vpd(uint8_t *vpddata, uint32_t vpddata_length, const char *key)
125{
126	vpdbuf_t vpdbuf;
127	uint8_t *pret = NULL;
128	uint8_t c0 = key[0];
129	uint8_t c1 = key[1];
130
131	vpdbuf.buffer = (uint8_t*) vpddata;
132	vpdbuf.length = vpddata_length;
133	vpdbuf.offset = 0;
134	vpdbuf.checksum = 0;
135
136	while (!vpddone(&vpdbuf)) {
137		int type = vpdnext(&vpdbuf);
138		int len_lo;
139		int len_hi;
140		int len;
141		int i;
142
143		if (type == VPD_SMALL_RESOURCE_TYPE_END_TAG) {
144			break;
145		}
146
147		len_lo = vpdnext(&vpdbuf);
148		len_hi = vpdnext(&vpdbuf);
149		len = len_lo + (len_hi << 8);
150
151		if ((type == VPD_LARGE_RESOURCE_TYPE_R_TAG) || (type == VPD_LARGE_RESOURCE_TYPE_W_TAG)) {
152			while (len > 0) {
153				int rc0;
154				int rc1;
155				int sublen;
156				uint8_t *pstart;
157
158				rc0 = vpdnext(&vpdbuf);
159				rc1 = vpdnext(&vpdbuf);
160
161				/* Mark this location */
162				pstart = vpdref(&vpdbuf);
163
164				sublen = vpdnext(&vpdbuf);
165
166				/* Adjust remaining len */
167				len -= (sublen + 3);
168
169				/* check for match with request */
170				if ((c0 == rc0) && (c1 == rc1)) {
171					pret = pstart;
172					for (i = 0; i < sublen; i++) {
173						vpdnext(&vpdbuf);
174					}
175				/* check for "RV" end */
176				} else if ('R' == rc0 && 'V' == rc1) {
177
178					/* Read the checksum */
179					for (i = 0; i < sublen; i++) {
180						vpdnext(&vpdbuf);
181					}
182
183					/* The accumulated checksum should be zero here */
184					if (vpdbuf.checksum != 0) {
185						ocs_log_test(NULL, "checksum error\n");
186						return NULL;
187					}
188				}
189				else
190					for (i = 0; i < sublen; i++) {
191						vpdnext(&vpdbuf);
192					}
193			}
194		}
195
196		for (i = 0; i < len; i++) {
197			vpdnext(&vpdbuf);
198		}
199	}
200
201	return pret;
202}
203#endif
204