int i3c_hci_parse_ext_caps()

in master/mipi-i3c-hci/ext_caps.c [256:308]


int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
{
	void __iomem *curr_cap = hci->EXTCAPS_regs;
	void __iomem *end = curr_cap + 0x1000; /* some arbitrary limit */
	u32 cap_header, cap_id, cap_length;
	const struct hci_ext_caps *cap_entry;
	int i, err = 0;

	if (!curr_cap)
		return 0;

	for (; !err && curr_cap < end; curr_cap += cap_length * 4) {
		cap_header = readl(curr_cap);
		cap_id = FIELD_GET(CAP_HEADER_ID, cap_header);
		cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header);
		DBG("id=0x%02x length=%d", cap_id, cap_length);
		if (!cap_length)
			break;
		if (curr_cap + cap_length * 4 >= end) {
			dev_err(&hci->master.dev,
				"ext_cap 0x%02x has size %d (too big)\n",
				cap_id, cap_length);
			err = -EINVAL;
			break;
		}

		if (cap_id >= 0xc0 && cap_id <= 0xcf) {
			err = hci_extcap_vendor_specific(hci, curr_cap,
							 cap_id, cap_length);
			continue;
		}

		cap_entry = NULL;
		for (i = 0; i < ARRAY_SIZE(ext_capabilities); i++) {
			if (ext_capabilities[i].id == cap_id) {
				cap_entry = &ext_capabilities[i];
				break;
			}
		}
		if (!cap_entry) {
			dev_notice(&hci->master.dev,
				   "unknown ext_cap 0x%02x\n", cap_id);
		} else if (cap_length < cap_entry->min_length) {
			dev_err(&hci->master.dev,
				"ext_cap 0x%02x has size %d (expecting >= %d)\n",
				cap_id, cap_length, cap_entry->min_length);
			err = -EINVAL;
		} else {
			err = cap_entry->parser(hci, curr_cap);
		}
	}
	return err;
}