This is an automatic generated email to let you know that the following patch 
were queued at the 
http://git.linuxtv.org/cgit.cgi/v4l-utils.git tree:

Subject: cec-ctl: add a --phys-addr-from-edid option
Author:  Hans Verkuil <hans.verk...@cisco.com>
Date:    Fri Nov 15 13:28:08 2019 +0100

This option can parse the physical address from the given EDID.
This is useful in combination with a udev rule to automatically
update the physical address of e.g. a Pulse-Eight device.

Signed-off-by: Hans Verkuil <hans.verk...@cisco.com>

 utils/cec-ctl/cec-ctl.1.in |  5 +++
 utils/cec-ctl/cec-ctl.cpp  | 83 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 86 insertions(+), 2 deletions(-)

---

http://git.linuxtv.org/cgit.cgi/v4l-utils.git/commit/?id=7ead0e1856b89f2e19369af452bb03fd0cd16793
diff --git a/utils/cec-ctl/cec-ctl.1.in b/utils/cec-ctl/cec-ctl.1.in
index aacf0b9f86de..7f9740cb66b0 100644
--- a/utils/cec-ctl/cec-ctl.1.in
+++ b/utils/cec-ctl/cec-ctl.1.in
@@ -63,6 +63,11 @@ Use this physical address. The address can be a number (e.g. 
0 or 0x11b1)
 or formatted as a.b.c.d where each component is a hex value from 0-f
 (e.g. 0.0.0.0 or 1.1.b.1).
 .TP
+\fB\-e\fR, \fB\-\-phys\-addr\-from\-edid\fR \fI<path>\fR
+Parse the given EDID file (in raw binary format) and extract the physical
+address. If the EDID file does not exist or does not contain a physical
+address, then invalidate the physical address.
+.TP
 \fB\-o\fR, \fB\-\-osd\-name\fR \fI<name>\fR
 Use this OSD name. The maximum length is 14 characters.
 .TP
diff --git a/utils/cec-ctl/cec-ctl.cpp b/utils/cec-ctl/cec-ctl.cpp
index 7cad298511a8..94a49b62deca 100644
--- a/utils/cec-ctl/cec-ctl.cpp
+++ b/utils/cec-ctl/cec-ctl.cpp
@@ -66,6 +66,7 @@ enum Option {
        OptClear = 'C',
        OptSetDevice = 'd',
        OptSetDriver = 'D',
+       OptPhysAddrFromEDID = 'e',
        OptFrom = 'f',
        OptHelp = 'h',
        OptLogicalAddress = 'l',
@@ -157,6 +158,7 @@ static struct option long_options[] = {
        { "verbose", no_argument, 0, OptVerbose },
        { "wall-clock", no_argument, 0, OptWallClock },
        { "osd-name", required_argument, 0, OptOsdName },
+       { "phys-addr-from-edid", required_argument, 0, OptPhysAddrFromEDID },
        { "phys-addr", required_argument, 0, OptPhysAddr },
        { "vendor-id", required_argument, 0, OptVendorID },
        { "cec-version-1.4", no_argument, 0, OptCECVersion1_4 },
@@ -233,6 +235,7 @@ static void usage(void)
               "  -D, --driver <driver>    Use a cec device with this driver 
name\n"
               "  -a, --adapter <adapter>  Use a cec device with this adapter 
name\n"
               "  -p, --phys-addr <addr>   Use this physical address\n"
+              "  -e, --phys-addr-from-edid <path>   Set physical address from 
this EDID file\n"
               "  -o, --osd-name <name>    Use this OSD name\n"
               "  -V, --vendor-id <id>     Use this vendor ID\n"
               "  -l, --logical-address    Show first configured logical 
address\n"
@@ -1490,6 +1493,78 @@ static bool sort_on_device_name(const std::string &s1, 
const std::string &s2)
        return n1 < n2;
 }
 
+static unsigned int cec_get_edid_spa_location(const __u8 *edid,
+                                             unsigned int size)
+{
+       unsigned int blocks = size / 128;
+       unsigned int block;
+       __u8 d;
+
+       /* Sanity check: at least 2 blocks and a multiple of the block size */
+       if (blocks < 2 || size % 128)
+               return 0;
+
+       /*
+        * If there are fewer extension blocks than the size, then update
+        * 'blocks'. It is allowed to have more extension blocks than the size,
+        * since some hardware can only read e.g. 256 bytes of the EDID, even
+        * though more blocks are present. The first CEA-861 extension block
+        * should normally be in block 1 anyway.
+        */
+       if (edid[0x7e] + 1U < blocks)
+               blocks = edid[0x7e] + 1;
+
+       for (block = 1; block < blocks; block++) {
+               unsigned int offset = block * 128;
+
+               /* Skip any non-CEA-861 extension blocks */
+               if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
+                       continue;
+
+               /* search Vendor Specific Data Block (tag 3) */
+               d = edid[offset + 2] & 0x7f;
+               /* Check if there are Data Blocks */
+               if (d <= 4)
+                       continue;
+               if (d > 4) {
+                       unsigned int i = offset + 4;
+                       unsigned int end = offset + d;
+
+                       /* Note: 'end' is always < 'size' */
+                       do {
+                               __u8 tag = edid[i] >> 5;
+                               __u8 len = edid[i] & 0x1f;
+
+                               if (tag == 3 && len >= 5 && i + len <= end &&
+                                   edid[i + 1] == 0x03 &&
+                                   edid[i + 2] == 0x0c &&
+                                   edid[i + 3] == 0x00)
+                                       return i + 4;
+                               i += len + 1;
+                       } while (i < end);
+               }
+       }
+       return 0;
+}
+
+static __u16 parse_phys_addr_from_edid(const char *edid_path)
+{
+       FILE *f = fopen(edid_path, "r");
+       __u16 pa = CEC_PHYS_ADDR_INVALID;
+       __u8 edid[256];
+
+       if (f == NULL)
+               return pa;
+       if (fread(edid, sizeof(edid), 1, f) == 1) {
+               unsigned int loc = cec_get_edid_spa_location(edid, 
sizeof(edid));
+
+               if (loc)
+                       pa = (edid[loc] << 8) | edid[loc + 1];
+       }
+       fclose(f);
+       return pa;
+}
+
 typedef std::vector<std::string> dev_vec;
 typedef std::map<std::string, std::string> dev_map;
 
@@ -1709,6 +1784,9 @@ int main(int argc, char **argv)
                case OptPhysAddr:
                        phys_addr = cec_parse_phys_addr(optarg);
                        break;
+               case OptPhysAddrFromEDID:
+                       phys_addr = parse_phys_addr_from_edid(optarg);
+                       break;
                case OptOsdName:
                        osd_name = optarg;
                        break;
@@ -2083,7 +2161,9 @@ int main(int argc, char **argv)
        node.caps = caps.capabilities;
        node.available_log_addrs = caps.available_log_addrs;
 
-       if (options[OptPhysAddr] && !(node.caps & CEC_CAP_PHYS_ADDR))
+       bool set_phys_addr = options[OptPhysAddr] || 
options[OptPhysAddrFromEDID];
+
+       if (set_phys_addr && !(node.caps & CEC_CAP_PHYS_ADDR))
                fprintf(stderr, "The CEC adapter doesn't allow setting the 
physical address manually, ignore this option.\n\n");
 
        unsigned flags = 0;
@@ -2129,7 +2209,6 @@ int main(int argc, char **argv)
        }
 
        bool set_log_addrs = (node.caps & CEC_CAP_LOG_ADDRS) && flags;
-       bool set_phys_addr = (node.caps & CEC_CAP_PHYS_ADDR) && 
options[OptPhysAddr];
        bool clear_log_addrs = (node.caps & CEC_CAP_LOG_ADDRS) && 
options[OptClear];
 
        // When setting both PA and LA it is best to clear the LAs first.

_______________________________________________
linuxtv-commits mailing list
linuxtv-commits@linuxtv.org
https://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits

Reply via email to