Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DLCVCAM_BUILD_VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2026072202
58 changes: 56 additions & 2 deletions DLCVCAM内核编译与更新操作手册.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,54 @@ which aarch64-linux-gnu-gcc

## 2. 编译内核

### 2.1 初次配置(仅需执行一次)
### 2.1 日期构建编号规则

DLCVCAM 发布内核使用仓库根目录的 `DLCVCAM_BUILD_VERSION` 作为构建编号,
格式固定为:

```text
YYYYMMDDNN
```

其中 `YYYYMMDD` 是发布日期,`NN` 是当天两位序号,从 `01` 开始。例如:

```text
2026072201
```

合入 `master` 前必须把该文件更新为实际合入日期;同一天发布多个内核时依次使用
`01`、`02`、`03`。不要通过修改 `VERSION/PATCHLEVEL/SUBLEVEL` 记录发布日期,
这样可以保持 `uname -r` 和 `/lib/modules/6.1.99-rk3576` 路径稳定。

开发机查看仓库默认构建编号:

```bash
cat DLCVCAM_BUILD_VERSION
make -s dlcvcam-build-version
```

板卡刷入对应内核后查看:

```bash
uname -v
cat /proc/version
```

输出示例:

```text
#2026072201 SMP Wed Jul 22 10:46:10 CST 2026
```

正常发布构建不需要再手工传入 `KBUILD_BUILD_VERSION`。CI 或临时构建仍可显式传入
该变量覆盖仓库值,但交付镜像必须使用 `DLCVCAM_BUILD_VERSION` 中已提交的编号。
建议镜像文件名同时记录构建编号和 Git 短提交,例如:

```text
boot-rk3576-6.1.99-2026072201-e8c10e0f.img
```

### 2.2 初次配置(仅需执行一次)

```bash
cd /home/ypw/kernel
Expand All @@ -36,7 +83,7 @@ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3576_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig
```

### 2.2 增量编译(日常修改后)
### 2.3 增量编译(日常修改后)

```bash
cd /home/ypw/kernel
Expand Down Expand Up @@ -67,6 +114,13 @@ BOOT_ITS=boot.its ./scripts/mkimg --dtb dlcvcam-rk3576.dtb
mkimage -l boot.img
```

同时确认本次编译使用的日期构建编号:

```bash
make -s ARCH=arm64 O=/path/to/kernel-out dlcvcam-build-version
# 2026072201
```

---

## 3. 烧录到板子
Expand Down
24 changes: 22 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,21 @@ endif
this-makefile := $(lastword $(MAKEFILE_LIST))
abs_srctree := $(realpath $(dir $(this-makefile)))

# DLCVCAM release build identifier. Keep the Linux release and module path
# stable (for example 6.1.99-rk3576), while exposing a date-based identifier
# through UTS_VERSION (`uname -v`) on the target board. A command-line
# KBUILD_BUILD_VERSION still takes precedence for temporary/CI builds.
DLCVCAM_BUILD_VERSION_FILE := $(abs_srctree)/DLCVCAM_BUILD_VERSION
DLCVCAM_BUILD_VERSION := $(strip $(shell cat $(DLCVCAM_BUILD_VERSION_FILE) 2>/dev/null))
DLCVCAM_BUILD_VERSION_VALID := $(shell printf '%s\n' '$(DLCVCAM_BUILD_VERSION)' | \
grep -Eq '^[0-9]{8}(0[1-9]|[1-9][0-9])$$' && echo y)
ifneq ($(DLCVCAM_BUILD_VERSION_VALID),y)
$(error DLCVCAM_BUILD_VERSION must use YYYYMMDDNN with NN in 01..99)
endif
KBUILD_BUILD_VERSION ?= $(DLCVCAM_BUILD_VERSION)

export DLCVCAM_BUILD_VERSION KBUILD_BUILD_VERSION

ifneq ($(words $(subst :, ,$(abs_srctree))), 1)
$(error source directory cannot contain spaces or colons)
endif
Expand Down Expand Up @@ -286,7 +301,8 @@ no-dot-config-targets := $(clean-targets) \
cscope gtags TAGS tags help% %docs check% coccicheck \
$(version_h) headers headers_% archheaders archscripts \
%asm-generic kernelversion %src-pkg dt_binding_check \
outputmakefile rustavailable rustfmt rustfmtcheck
outputmakefile rustavailable rustfmt rustfmtcheck \
dlcvcam-build-version
# Installation targets should not require compiler. Unfortunately, vdso_install
# is an exception where build artifacts may be updated. This must be fixed.
no-compiler-targets := $(no-dot-config-targets) install dtbs_install \
Expand Down Expand Up @@ -1687,6 +1703,7 @@ help:
@echo ' gtags - Generate GNU GLOBAL index'
@echo ' kernelrelease - Output the release version string (use with make -s)'
@echo ' kernelversion - Output the version stored in Makefile (use with make -s)'
@echo ' dlcvcam-build-version - Output the date-based DLCVCAM build identifier'
@echo ' image_name - Output the image name (use with make -s)'
@echo ' headers_install - Install sanitised kernel headers to INSTALL_HDR_PATH'; \
echo ' (default: $(INSTALL_HDR_PATH))'; \
Expand Down Expand Up @@ -2095,7 +2112,7 @@ coccicheck:
export_report:
$(PERL) $(srctree)/scripts/export_report.pl

PHONY += checkstack kernelrelease kernelversion image_name
PHONY += checkstack kernelrelease kernelversion dlcvcam-build-version image_name

# UML needs a little special treatment here. It wants to use the host
# toolchain, so needs $(SUBARCH) passed to checkstack.pl. Everyone
Expand All @@ -2116,6 +2133,9 @@ kernelrelease:
kernelversion:
@echo $(KERNELVERSION)

dlcvcam-build-version:
@echo $(KBUILD_BUILD_VERSION)

image_name:
@echo $(KBUILD_IMAGE)

Expand Down
117 changes: 110 additions & 7 deletions drivers/input/misc/trigger-dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* 2. trigger-path: write to sysfs "echo 1 > ..." - fallback for software trigger
* - Optional result LEDs: ok-led-gpios / ng-led-gpios; sysfs "result" (ok/ng/off),
* "result_led_enable"; each external trigger clears LEDs before camera action.
* - Runtime gate: sysfs "enable" (0/1). Disabling masks the input IRQ, cancels
* queued work and drops pending triggers before returning.
*
* When trigger-output-gpios is set, keep a single owner for that output GPIO.
* If another node already claims the same pin, remove one side in DT.
Expand Down Expand Up @@ -87,6 +89,11 @@ struct trigger_dev {
bool mode_use_gpio;
struct mutex mode_mutex;

/* 外部触发总门闩;默认开启,模式/ROI 切换期间由 sysfs enable 关闭 */
bool enabled;
/* Serializes IRQ masking and pending-work cancellation. */
struct mutex enable_mutex;

/* 结果指示灯:DT ok-led-gpios / ng-led-gpios;逻辑 0=inactive(灭) 1=active(亮) */
struct gpio_desc *ok_led_gpiod;
struct gpio_desc *ng_led_gpiod;
Expand All @@ -101,6 +108,7 @@ struct trigger_dev {
atomic64_t irq_count;
atomic64_t trigger_ok_count;
atomic64_t trigger_fail_count;
atomic64_t trigger_suppressed_count;
u32 max_pending;
u64 last_irq_ns;
};
Expand Down Expand Up @@ -163,9 +171,13 @@ static int trigger_dev_set_input_mode(struct trigger_dev *tdev,
enum trigger_dev_input_mode mode)
{
unsigned int irq_type = trigger_dev_irq_type_for_mode(tdev, mode);
bool enabled;
int ret;

disable_irq(tdev->irq);
mutex_lock(&tdev->enable_mutex);
enabled = READ_ONCE(tdev->enabled);
if (enabled)
disable_irq(tdev->irq);
cancel_delayed_work_sync(&tdev->debounce_work);

ret = irq_set_irq_type(tdev->irq, irq_type);
Expand All @@ -175,7 +187,51 @@ static int trigger_dev_set_input_mode(struct trigger_dev *tdev,
WRITE_ONCE(tdev->input_mode, mode);
}

enable_irq(tdev->irq);
if (enabled)
enable_irq(tdev->irq);
mutex_unlock(&tdev->enable_mutex);
return ret;
}

static int trigger_dev_set_enabled(struct trigger_dev *tdev, bool enabled)
{
int dropped;
int ret = 0;

mutex_lock(&tdev->enable_mutex);
if (enabled == READ_ONCE(tdev->enabled))
goto out;

if (!enabled) {
/* Publish the closed gate before waiting for any in-flight handler. */
WRITE_ONCE(tdev->enabled, false);
disable_irq(tdev->irq);
cancel_delayed_work_sync(&tdev->debounce_work);
cancel_work_sync(&tdev->trigger_work);

dropped = atomic_xchg(&tdev->trigger_pending, 0);
if (dropped > 0)
atomic64_add(dropped, &tdev->trigger_suppressed_count);

if (tdev->output_gpiod)
gpiod_set_value_cansleep(tdev->output_gpiod, 0);

if (tdev->pressed) {
tdev->pressed = false;
input_report_key(tdev->input, tdev->key_code, 0);
input_sync(tdev->input);
}
} else {
atomic_set(&tdev->trigger_pending, 0);
ret = trigger_dev_refresh_pressed_state(tdev);
if (ret)
goto out;
WRITE_ONCE(tdev->enabled, true);
enable_irq(tdev->irq);
}

out:
mutex_unlock(&tdev->enable_mutex);
return ret;
}

Expand Down Expand Up @@ -283,6 +339,11 @@ static void trigger_dev_trigger_work(struct work_struct *work)

/* Drain pending triggers (presses) */
while (atomic_dec_if_positive(&tdev->trigger_pending) >= 0) {
if (!READ_ONCE(tdev->enabled)) {
atomic64_inc(&tdev->trigger_suppressed_count);
continue;
}

mutex_lock(&tdev->led_mutex);
if (tdev->result_led_enable)
trigger_dev_leds_off_locked(tdev);
Expand Down Expand Up @@ -327,6 +388,9 @@ static void trigger_dev_trigger_work(struct work_struct *work)

static void trigger_dev_handle_state(struct trigger_dev *tdev, bool pressed_now)
{
if (!READ_ONCE(tdev->enabled))
return;

if (pressed_now == tdev->pressed)
return;

Expand Down Expand Up @@ -375,6 +439,10 @@ static irqreturn_t trigger_dev_irq(int irq, void *dev_id)

tdev->last_irq_ns = ktime_get_ns();
atomic64_inc(&tdev->irq_count);
if (!READ_ONCE(tdev->enabled)) {
atomic64_inc(&tdev->trigger_suppressed_count);
return IRQ_HANDLED;
}

/*
* Fast edge mode:
Expand Down Expand Up @@ -516,7 +584,9 @@ static int trigger_dev_probe(struct platform_device *pdev)
tdev->dev = dev;
platform_set_drvdata(pdev, tdev);
mutex_init(&tdev->mode_mutex);
mutex_init(&tdev->enable_mutex);
mutex_init(&tdev->led_mutex);
WRITE_ONCE(tdev->enabled, true);

/*
* Primary DT property: input-gpios (con_id = "input")
Expand Down Expand Up @@ -555,6 +625,7 @@ static int trigger_dev_probe(struct platform_device *pdev)
atomic64_set(&tdev->irq_count, 0);
atomic64_set(&tdev->trigger_ok_count, 0);
atomic64_set(&tdev->trigger_fail_count, 0);
atomic64_set(&tdev->trigger_suppressed_count, 0);
tdev->max_pending = 0;
tdev->last_irq_ns = 0;

Expand Down Expand Up @@ -590,15 +661,16 @@ static int trigger_dev_probe(struct platform_device *pdev)

gpio = desc_to_gpio(tdev->input_gpiod);
active_low = gpiod_is_active_low(tdev->input_gpiod);
dev_info(dev, "ready: gpio=%d irq=%d active_low=%d input_mode=%s debounce_ms=%u key_code=%u mode=%s\n",
dev_info(dev, "ready: gpio=%d irq=%d active_low=%d input_mode=%s debounce_ms=%u key_code=%u mode=%s enabled=%d\n",
gpio,
tdev->irq,
active_low,
trigger_dev_input_mode_name(tdev->input_mode),
tdev->debounce_ms,
tdev->key_code,
tdev->output_gpiod ?
"direct-gpio-pulse" : "sysfs-write");
"direct-gpio-pulse" : "sysfs-write",
READ_ONCE(tdev->enabled));
if (tdev->input_mode == TRIGGER_DEV_INPUT_MODE_BUTTON &&
!tdev->debounce_ms)
dev_warn(dev, "button input mode is selected with debounce-ms=0; mechanical keys may still bounce\n");
Expand Down Expand Up @@ -666,6 +738,36 @@ static ssize_t input_mode_store(struct device *dev,
}
static DEVICE_ATTR_RW(input_mode);

/* Sysfs: enable (0=屏蔽外部触发并清空 pending, 1=允许外部触发) */
static ssize_t enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct trigger_dev *tdev = dev_get_drvdata(dev);

return sysfs_emit(buf, "%d\n", READ_ONCE(tdev->enabled));
}

static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct trigger_dev *tdev = dev_get_drvdata(dev);
bool enabled;
int ret;

ret = kstrtobool(buf, &enabled);
if (ret)
return ret;

ret = trigger_dev_set_enabled(tdev, enabled);
if (ret)
return ret;

dev_info(dev, "external trigger %s\n", enabled ? "enabled" : "disabled");
return count;
}
static DEVICE_ATTR_RW(enable);

/* Sysfs: mode (gpio|sysfs) */
static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
Expand Down Expand Up @@ -874,10 +976,12 @@ static ssize_t stats_show(struct device *dev, struct device_attribute *attr, cha
u64 ago_us = tdev->last_irq_ns ? div_u64(now - tdev->last_irq_ns, 1000) : 0;

return sysfs_emit(buf,
"irq=%lld ok=%lld fail=%lld pending=%d max_pending=%u last_irq_ago_us=%llu input_mode=%s debounce_ms=%u\n",
"enabled=%d irq=%lld ok=%lld fail=%lld suppressed=%lld pending=%d max_pending=%u last_irq_ago_us=%llu input_mode=%s debounce_ms=%u\n",
READ_ONCE(tdev->enabled),
atomic64_read(&tdev->irq_count),
atomic64_read(&tdev->trigger_ok_count),
atomic64_read(&tdev->trigger_fail_count),
atomic64_read(&tdev->trigger_suppressed_count),
atomic_read(&tdev->trigger_pending),
tdev->max_pending,
ago_us,
Expand All @@ -888,6 +992,7 @@ static DEVICE_ATTR_RO(stats);

static struct attribute *trigger_dev_attrs[] = {
&dev_attr_input_mode.attr,
&dev_attr_enable.attr,
&dev_attr_mode.attr,
&dev_attr_pulse_count.attr,
&dev_attr_pulse_interval_us.attr,
Expand Down Expand Up @@ -930,5 +1035,3 @@ module_platform_driver(trigger_dev_driver);
MODULE_DESCRIPTION("Generic GPIO input trigger device (camera trigger + optional result LEDs)");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);


Loading