diff --git a/CLAUDE.md b/CLAUDE.md index 7542cff..32009f9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -217,11 +217,15 @@ is also what lets detection survive a kernel that will not hand over Ambarella (`src/hal/ambarella.c`) stops at the machine string on purpose. The exact part (S3L33M, S3L55M, S3L66, ...) is a word the DSP firmware writes, -reachable only through `/dev/iav`, so the report says `S3L`. Total RAM is the +reachable only through `/dev/iav`, so the report says `S3L`, plus `family: S3Lm` +when `iav.ko` is loaded. Total RAM is the top of the DSP carve-out, because `mem=` keeps the kernel far below it: 118 MiB of 512 MiB on the camera in #266, where the DRAM marking confirmed the figure. The carve-out comes from `/proc/device-tree/iavmem/reg`, or from the boot log's `IAVMEM` line on kernels with no `/proc/device-tree`, which Dahua's 3.10.73 is. +Only its end is trusted: Hikvision's kernel moves the split at boot and leaves +`/iavmem` stale, so media is what lies above Linux's RAM in `/proc/iomem`, and +with no such RAM listed it is left out, not guessed. That line is printed once, so if the ring buffer has dropped it the report falls back to what the kernel sees. Stock Dahua firmware has no `/dev/i2c-*` either: its own `sensor.ko` owns the bus, so no sensor is reported there, and reaching diff --git a/src/hal/ambarella.c b/src/hal/ambarella.c index 0bd0622..98954e1 100644 --- a/src/hal/ambarella.c +++ b/src/hal/ambarella.c @@ -11,9 +11,10 @@ /* Defaults of the `addr` module parameter across the S3L SDK's VIN drivers, * the 8-bit form ipctool's tables use. AR0521 is the one part left out: it * answers on 0x6C, and the onsemi probe runs before the OmniVision one, so - * listing it would have that probe reading an OV4689. */ + * listing it would have that probe reading an OV4689. 0x30 is not a default: + * the Hikvision DS-2CD2423G0-I strings its AR0237 there. */ static unsigned char sony_addrs[] = {0x34, 0}; -static unsigned char onsemi_addrs[] = {0x20, 0}; +static unsigned char onsemi_addrs[] = {0x20, 0x30, 0}; static unsigned char omni_addrs[] = {0x6c, 0}; static sensor_addr_t ambarella_possible_i2c_addrs[] = { @@ -88,6 +89,52 @@ bool ambarella_iavmem_from_log(const char *log, uint32_t *start, end == '\n'; } +/* The carve-out's end is the top of DRAM on every board seen, but its start + * cannot be trusted: Hikvision's kernel moves the split at boot, and leaves + * /iavmem saying 0x04000000 where it gave Linux up to 0x07000000. So media is + * what lies between the top of Linux's RAM and that end, and with no RAM listed + * to measure from it is unknown: 0, which the report leaves out. */ +unsigned long ambarella_media_kb(const char *iomem, uint64_t end) { + FILE *f = fopen(iomem, "r"); + uint64_t top = 0; + char line[128]; + + if (!f) + return 0; + + /* Top level only: "Kernel code" and the like are indented inside it, + * and %llx would skip that indent */ + while (fgets(line, sizeof(line), f)) { + unsigned long long from, to; + int name = 0; + + if (*line != ' ' && + sscanf(line, "%llx-%llx : %n", &from, &to, &name) == 2 && name && + !strncmp(line + name, "System RAM", 10) && to + 1 > top) + top = to + 1; + } + fclose(f); + + return top && top < end ? (end - top) / 1024 : 0; +} + +/* iav's own view of the part, there once iav.ko is loaded: "S3Lm", which the + * machine string cannot tell from S3L. */ +bool ambarella_family(const char *iav, char *out, size_t outlen) { + if (!outlen || !line_from_file(iav, "AMBARELLA_CHIP=(\\w+)", out, outlen)) + return false; + + out[outlen - 1] = '\0'; + return true; +} + +static void ambarella_chip_properties(cJSON *j_inner) { + char family[32]; + + if (ambarella_family("/proc/ambarella/iav", family, sizeof(family))) + ADD_PARAM("family", family); +} + static unsigned long ambarella_totalmem(unsigned long *media_mem) { uint32_t start = 0, size = 0; bool found = ambarella_iavmem_from_dt("/proc/device-tree/iavmem/reg", @@ -113,8 +160,8 @@ static unsigned long ambarella_totalmem(unsigned long *media_mem) { if (!found || !size || start + size < start) return kernel_mem(); - *media_mem = size / 1024; - return (unsigned long)(start / 1024) + *media_mem; + *media_mem = ambarella_media_kb("/proc/iomem", (uint64_t)start + size); + return (unsigned long)(((uint64_t)start + size) / 1024); } #endif @@ -122,5 +169,6 @@ void ambarella_setup_hal() { possible_i2c_addrs = ambarella_possible_i2c_addrs; #ifndef STANDALONE_LIBRARY hal_totalmem = ambarella_totalmem; + hal_chip_properties = ambarella_chip_properties; #endif } diff --git a/src/hal/ambarella.h b/src/hal/ambarella.h index 0b76ad6..56018fa 100644 --- a/src/hal/ambarella.h +++ b/src/hal/ambarella.h @@ -16,5 +16,7 @@ bool ambarella_iavmem_from_dt(const char *path, uint32_t *start, uint32_t *size); bool ambarella_iavmem_from_log(const char *log, uint32_t *start, uint32_t *size); +unsigned long ambarella_media_kb(const char *iomem, uint64_t end); +bool ambarella_family(const char *iav, char *out, size_t outlen); #endif /* HAL_AMBARELLA_H */ diff --git a/src/hal/ambarella_test.c b/src/hal/ambarella_test.c index 63ca1c7..7480d02 100644 --- a/src/hal/ambarella_test.c +++ b/src/hal/ambarella_test.c @@ -1,6 +1,7 @@ /* Hardware-free cover for the Ambarella machine-string parser. The strings * are the DT_MACHINE_START names in the vendor kernel, and the S3L cpuinfo is * the one from issue #266. */ +#include #include #include #include @@ -101,6 +102,65 @@ int main() { CHECK(rounded_num(513) == 1024); CHECK(rounded_num(1) == 1); + /* Two boards. On the DS-2CD2423G0-I /iavmem is stale: the DT says the + * DSP starts at 64 MiB and Linux runs to 112 MiB. On the IP4M-1026E the + * two agree, Linux ending exactly where the DSP starts. */ + { + char iomem[] = "/tmp/ambarella_test.XXXXXX"; + const char *hik = "00200000-06ffffff : System RAM\n" + " 00208000-00719667 : Kernel code\n"; + const char *dahua = "00200000-077fffff : System RAM\n"; + /* Split, the higher range last, and an indented line that must not + * count however high it goes */ + const char *split = "00200000-03ffffff : System RAM\n" + " 00208000-00719667 : Kernel code\n" + "04100000-06ffffff : System RAM\n" + " 0ff00000-0fffffff : System RAM\n"; + const char *none = "e8005000-e8005fff : uart\n"; + const uint64_t top = 0x10000000; + int ifd = mkstemp(iomem); + + CHECK(ifd >= 0); + close(ifd); +#define IOMEM(text) \ + do { \ + int w = open(iomem, O_WRONLY | O_TRUNC); \ + CHECK(w >= 0); \ + CHECK(write(w, text, strlen(text)) == (ssize_t)strlen(text)); \ + close(w); \ + } while (0) + IOMEM(hik); + CHECK(ambarella_media_kb(iomem, top) == 144 * 1024); + IOMEM(dahua); + CHECK(ambarella_media_kb(iomem, 0x20000000) == 392 * 1024); + IOMEM(split); + CHECK(ambarella_media_kb(iomem, top) == 144 * 1024); + + /* Nothing to measure from, or Linux past the end: unknown */ + IOMEM(none); + CHECK(ambarella_media_kb(iomem, top) == 0); + IOMEM(hik); + CHECK(ambarella_media_kb(iomem, 0x07000000) == 0); + unlink(iomem); + CHECK(ambarella_media_kb(iomem, top) == 0); +#undef IOMEM + } + + { + char iav[] = "/tmp/ambarella_test.XXXXXX"; + const char *text = "AMBARELLA_CHIP=S3Lm\ndsp_op_mode: 1\n"; + char family[16]; + int ifd = mkstemp(iav); + + CHECK(ifd >= 0); + CHECK(write(ifd, text, strlen(text)) == (ssize_t)strlen(text)); + close(ifd); + CHECK(ambarella_family(iav, family, sizeof(family))); + CHECK(!strcmp(family, "S3Lm")); + unlink(iav); + CHECK(!ambarella_family(iav, family, sizeof(family))); + } + if (failures) return EXIT_FAILURE; puts("ambarella_test: all cases passed"); diff --git a/src/sensors.c b/src/sensors.c index 02e3535..5c85c6d 100644 --- a/src/sensors.c +++ b/src/sensors.c @@ -451,6 +451,12 @@ static int detect_onsemi_sensor(sensor_ctx_t *ctx, int fd, case 0x256: sid = 0x0237; break; + /* Register 0x3000 reads 0x0950 on the DS-2CD2423G0-I, whose firmware + * drives that sensor with its ar0237 module. No datasheet to hand: the + * vendor driver reads this register but does not check it. */ + case 0x5009: + sid = 0x0237; + break; case 0x2602: sid = 0x0331; break; diff --git a/src/sensors_golden.txt b/src/sensors_golden.txt index cba0f7f..ef39b9a 100644 --- a/src/sensors_golden.txt +++ b/src/sensors_golden.txt @@ -1102,6 +1102,7 @@ onsemi 0256 AR0237 onsemi 2402 AR0130 onsemi 2602 AR0331 onsemi 2604 AR0330 +onsemi 5009 AR0237 omni 2710 OV2710 omni 2715 OV2715 omni 2718 OV2718