1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2021 Intel Corporation 4 */ 5 6 #include <drm/drm_displayid.h> 7 #include <drm/drm_edid.h> 8 #include <drm/drm_print.h> 9 10 static int validate_displayid(const u8 *displayid, int length, int idx) 11 { 12 int i, dispid_length; 13 u8 csum = 0; 14 const struct displayid_header *base; 15 16 base = (const struct displayid_header *)&displayid[idx]; 17 18 DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", 19 base->rev, base->bytes, base->prod_id, base->ext_count); 20 21 /* +1 for DispID checksum */ 22 dispid_length = sizeof(*base) + base->bytes + 1; 23 if (dispid_length > length - idx) 24 return -EINVAL; 25 26 for (i = 0; i < dispid_length; i++) 27 csum += displayid[idx + i]; 28 if (csum) { 29 DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum); 30 return -EINVAL; 31 } 32 33 return 0; 34 } 35 36 static const u8 *drm_find_displayid_extension(const struct edid *edid, 37 int *length, int *idx, 38 int *ext_index) 39 { 40 const u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT, ext_index); 41 const struct displayid_header *base; 42 int ret; 43 44 if (!displayid) 45 return NULL; 46 47 /* EDID extensions block checksum isn't for us */ 48 *length = EDID_LENGTH - 1; 49 *idx = 1; 50 51 ret = validate_displayid(displayid, *length, *idx); 52 if (ret) 53 return NULL; 54 55 base = (const struct displayid_header *)&displayid[*idx]; 56 *length = *idx + sizeof(*base) + base->bytes; 57 58 return displayid; 59 } 60 61 void displayid_iter_edid_begin(const struct edid *edid, 62 struct displayid_iter *iter) 63 { 64 memset(iter, 0, sizeof(*iter)); 65 66 iter->edid = edid; 67 } 68 69 static const struct displayid_block * 70 displayid_iter_block(const struct displayid_iter *iter) 71 { 72 const struct displayid_block *block; 73 74 if (!iter->section) 75 return NULL; 76 77 block = (const struct displayid_block *)&iter->section[iter->idx]; 78 79 if (iter->idx + sizeof(*block) <= iter->length && 80 iter->idx + sizeof(*block) + block->num_bytes <= iter->length) 81 return block; 82 83 return NULL; 84 } 85 86 const struct displayid_block * 87 __displayid_iter_next(struct displayid_iter *iter) 88 { 89 const struct displayid_block *block; 90 91 if (!iter->edid) 92 return NULL; 93 94 if (iter->section) { 95 /* current block should always be valid */ 96 block = displayid_iter_block(iter); 97 if (WARN_ON(!block)) { 98 iter->section = NULL; 99 iter->edid = NULL; 100 return NULL; 101 } 102 103 /* next block in section */ 104 iter->idx += sizeof(*block) + block->num_bytes; 105 106 block = displayid_iter_block(iter); 107 if (block) 108 return block; 109 } 110 111 for (;;) { 112 iter->section = drm_find_displayid_extension(iter->edid, 113 &iter->length, 114 &iter->idx, 115 &iter->ext_index); 116 if (!iter->section) { 117 iter->edid = NULL; 118 return NULL; 119 } 120 121 iter->idx += sizeof(struct displayid_header); 122 123 block = displayid_iter_block(iter); 124 if (block) 125 return block; 126 } 127 } 128 129 void displayid_iter_end(struct displayid_iter *iter) 130 { 131 memset(iter, 0, sizeof(*iter)); 132 } 133