diff --git a/README.md b/README.md index d09a6de3a..f30eb330e 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Switcam HS303 v2 HI3518EV200 OV9732 RTL8188EU_USB NOR_16M done Tiandy TC-C321N GK7205V200 OS02G10 - NOR_8M done Tiandy TC-C321N v2 SSC337 SC2336P - NOR_8M done Tiandy TC-C32QN GK7205V210 OS02G10 - NOR_8M done +TP-Link Kasa KC110 HI3518EV200 OV2735 RTL8188FU_USB NOR_16M ptz-yes, video-WIP (pending majestic MIPI fix) TP-Link Tapo C110 v1 SSC335 SC3335 ATBM6032i_USB NOR_8M done TP-Link Tapo C110 v1 SSC337 SC3335 SSW101B_USB NOR_8M done TP-Link Tapo C110 v2 SSC333 SC3338 SSW101B_USB NOR_8M done diff --git a/devices/hi3518ev200_ultimate_tplink-kasa-kc110/br-ext-chip-hisilicon/configs/hi3518ev200_ultimate_tplink-kasa-kc110_defconfig b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/br-ext-chip-hisilicon/configs/hi3518ev200_ultimate_tplink-kasa-kc110_defconfig new file mode 100644 index 000000000..34e9cf692 --- /dev/null +++ b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/br-ext-chip-hisilicon/configs/hi3518ev200_ultimate_tplink-kasa-kc110_defconfig @@ -0,0 +1,78 @@ +# Architecture +BR2_arm=y + +# Toolchain +BR2_TOOLCHAIN_EXTERNAL=y +BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y +BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y +BR2_TOOLCHAIN_EXTERNAL_URL="https://github.com/openipc/firmware/releases/download/$(OPENIPC_TOOLCHAIN).tgz" +BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="arm-openipc-linux-musleabi" +BR2_TOOLCHAIN_EXTERNAL_HEADERS_4_9=y +BR2_TOOLCHAIN_EXTERNAL_CUSTOM_MUSL=y +BR2_TOOLCHAIN_EXTERNAL_CXX=y + +# Kernel +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="https://github.com/openipc/linux/archive/$(OPENIPC_KERNEL).tar.gz" +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(EXTERNAL_VENDOR)/board/$(OPENIPC_SOC_FAMILY)/hi3518ev200.generic.config" +BR2_LINUX_KERNEL_UIMAGE=y +BR2_LINUX_KERNEL_XZ=y + +# Filesystem +BR2_PACKAGE_BUSYBOX_CONFIG="$(BR2_EXTERNAL)/package/busybox/busybox.config" +BR2_PACKAGE_UBOOT_TOOLS=y +# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set +BR2_PACKAGE_IPTABLES=y +BR2_PACKAGE_MOSQUITTO=y +# BR2_PACKAGE_MOSQUITTO_BROKER is not set +BR2_PACKAGE_WIREGUARD_LINUX_COMPAT=y +BR2_PACKAGE_WIREGUARD_TOOLS=y +BR2_PACKAGE_WIRELESS_TOOLS=y +BR2_PACKAGE_WPA_SUPPLICANT=y +BR2_PACKAGE_WPA_SUPPLICANT_CLI=y +BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y +BR2_TARGET_ROOTFS_CPIO=y +BR2_TARGET_ROOTFS_SQUASHFS=y +BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y +BR2_TARGET_ROOTFS_UBI=y +BR2_TARGET_ROOTFS_UBI_SUBSIZE=2048 +BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y +BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL)/scripts/ubifs/ubinize.cfg" +BR2_TARGET_ROOTFS_UBIFS_LEBSIZE=0x1f000 + +# OpenIPC +BR2_OPENIPC_SOC_VENDOR="hisilicon" +BR2_OPENIPC_SOC_MODEL="hi3518ev200" +BR2_OPENIPC_SOC_FAMILY="hi3516cv200" +BR2_OPENIPC_VARIANT="ultimate" +BR2_OPENIPC_FLASH_SIZE="16" + +# Packages +BR2_PACKAGE_DROPBEAR_OPENIPC=y +BR2_PACKAGE_HISILICON_OPENSDK=y +BR2_PACKAGE_HISILICON_OSDRV_HI3516CV200=y +BR2_PACKAGE_IPCTOOL=y +BR2_PACKAGE_JSONFILTER=y +BR2_PACKAGE_LAME_OPENIPC=y +BR2_PACKAGE_LIBCURL_OPENIPC_CURL=y +# BR2_PACKAGE_LIBCURL_OPENIPC_PROXY_SUPPORT is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_COOKIES_SUPPORT is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_EXTRA_PROTOCOLS_FEATURES is not set +BR2_PACKAGE_LIBEVENT_OPENIPC=y +BR2_PACKAGE_LIBOGG_OPENIPC=y +BR2_PACKAGE_LIBWEBSOCKETS_OPENIPC=y +BR2_PACKAGE_MAJESTIC_FONTS=y +BR2_PACKAGE_MAJESTIC_WEBUI=y +BR2_PACKAGE_MAJESTIC=y +BR2_PACKAGE_OPUS_OPENIPC=y +BR2_PACKAGE_QUIRC_OPENIPC=y +BR2_PACKAGE_RTL8188FU_OPENIPC=y +BR2_PACKAGE_UACME_OPENIPC=y +BR2_PACKAGE_VTUND_OPENIPC=y +BR2_PACKAGE_YAML_CLI=y +BR2_PACKAGE_ZEROTIER_ONE=y + +# Board support (PTZ, pinmux, IR-cut / IR-LED GPIO) +BR2_PACKAGE_KC110_BOARD_SUPPORT=y diff --git a/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/etc/sensors/ov2735_i2c_1080p.ini b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/etc/sensors/ov2735_i2c_1080p.ini new file mode 100644 index 000000000..ca65bc840 --- /dev/null +++ b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/etc/sensors/ov2735_i2c_1080p.ini @@ -0,0 +1,279 @@ +[sensor] +Sensor_type =ov2735 ;sensor name +Mode =0 ;WDR_MODE_NONE = 0 + ;WDR_MODE_BUILT_IN = 1 + ;WDR_MODE_2To1_LINE = 2 + ;WDR_MODE_2To1_FRAME = 3 + ;WDR_MODE_2To1_FRAME_FULL_RATE =4 ...etc +DllFile =/usr/lib/sensors/libsns_ov2735.so ;sensor lib path + + +[mode] +input_mode = 0 ;INPUT_MODE_MIPI = 0 (KC110 OV2735 = MIPI) + ;INPUT_MODE_SUBLVDS = 1 + ;INPUT_MODE_LVDS = 2 ...etc + +dev_attr = 0 ;mipi_dev_attr_t = 0 + ;lvds_dev_attr_t = 1 + ;NULL =2 + +[mipi] +;----------only for mipi_dev--------- +data_type = 3 ;raw data type: 8/10/12/14 bit + ;RAW_DATA_8BIT = 1 + ;RAW_DATA_10BIT = 2 + ;RAW_DATA_12BIT = 3 + ;RAW_DATA_14BIT = 4 +lane_id = 0|-1|-1|-1|-1|-1|-1|-1| ;lane_id: -1 - disable (KC110: OV2735 is 1-lane MIPI) + +[lvds] +;----------only for lvds_dev--------- +img_size_w = -1 ;oringnal sensor input image size W +img_size_h = -1 ;oringnal sensor input image size H +wdr_mode = -1 ;HI_WDR_MODE_NONE =0 + ;HI_WDR_MODE_2F = 1 + ;HI_WDR_MODE_3F = 2 + ;HI_WDR_MODE_4F =3 +sync_mode = -1 ;LVDS_SYNC_MODE_SOL = 0 + ;LVDS_SYNC_MODE_SAV = 1 +raw_data_type = -1 ;RAW_DATA_8BIT = 0 + ;RAW_DATA_10BIT = 1 + ;RAW_DATA_12BIT = 2 + ;RAW_DATA_14BIT = 3 +data_endian = -1 ;LVDS_ENDIAN_LITTLE = 0 + ;LVDS_ENDIAN_BIG = 1 +sync_code_endian =-1 ;LVDS_ENDIAN_LITTLE = 0 + ;LVDS_ENDIAN_BIG = 1 +lane_id = -1|-1|-1|-1|-1|-1|-1|-1| ;lane_id: -1 - disable +lvds_lane_num = -1 ;LVDS_LANE_NUM +wdr_vc_num = -1 ;WDR_VC_NUM +sync_code_num = -1 ;SYNC_CODE_NUM +sync_code_0 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| +sync_code_1 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| +sync_code_2 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| +sync_code_3 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| +sync_code_4 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| +sync_code_5 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| +sync_code_6 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| +sync_code_7 = -1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1|-1| + +[isp_image] +Isp_x =0 +Isp_y =0 +Isp_W =1920 +Isp_H =1080 +Isp_FrameRate=25 +Isp_Bayer =3 ;BAYER_RGGB=0, BAYER_GRBG=1, BAYER_GBRG=2, BAYER_BGGR=3 + + +[vi_dev] +Input_mod =2 ;VI_INPUT_MODE_BT656 = 0 + ;VI_INPUT_MODE_BT601, + ;VI_INPUT_MODE_DIGITAL_CAMERA +Work_mod =0 ;VI_WORK_MODE_1Multiplex = 0 + ;VI_WORK_MODE_2Multiplex, + ;VI_WORK_MODE_4Multiplex +Combine_mode =0 ;Y/C composite or separation mode + ;VI_COMBINE_COMPOSITE = 0 /*Composite mode */ + ;VI_COMBINE_SEPARATE, /*Separate mode */ +Comp_mode =0 ;Component mode (single-component or dual-component) + ;VI_COMP_MODE_SINGLE = 0, /*single component mode */ + ;VI_COMP_MODE_DOUBLE = 1, /*double component mode */ +Clock_edge =1 ;Clock edge mode (sampling on the rising or falling edge) + ;VI_CLK_EDGE_SINGLE_UP=0, /*rising edge */ + ;VI_CLK_EDGE_SINGLE_DOWN, /*falling edge */ +Mask_num =2 ;Component mask +Mask_0 =0x3FF0000 +Mask_1 =0x0 +Scan_mode = 1;VI_SCAN_INTERLACED = 0 + ;VI_SCAN_PROGRESSIVE, +Data_seq =2 ;data sequence (ONLY for YUV format) + ;----2th component U/V sequence in bt1120 + ; VI_INPUT_DATA_VUVU = 0, + ; VI_INPUT_DATA_UVUV, + ;----input sequence for yuv + ; VI_INPUT_DATA_UYVY = 0, + ; VI_INPUT_DATA_VYUY, + ; VI_INPUT_DATA_YUYV, + ; VI_INPUT_DATA_YVYU + +Vsync =0 ; vertical synchronization signal + ;VI_VSYNC_FIELD = 0, + ;VI_VSYNC_PULSE, +VsyncNeg=0 ;Polarity of the vertical synchronization signal + ;VI_VSYNC_NEG_HIGH = 0, + ;VI_VSYNC_NEG_LOW /*if VIU_VSYNC_E +Hsync =0 ;Attribute of the horizontal synchronization signal + ;VI_HSYNC_VALID_SINGNAL = 0, + ;VI_HSYNC_PULSE, +HsyncNeg =0 ;Polarity of the horizontal synchronization signal + ;VI_HSYNC_NEG_HIGH = 0, + ;VI_HSYNC_NEG_LOW +VsyncValid =1 ;Attribute of the valid vertical synchronization signal + ;VI_VSYNC_NORM_PULSE = 0, + ;VI_VSYNC_VALID_SINGAL, +VsyncValidNeg =0;Polarity of the valid vertical synchronization signal + ;VI_VSYNC_VALID_NEG_HIGH = 0, + ;VI_VSYNC_VALID_NEG_LOW +Timingblank_HsyncHfb =0 ;Horizontal front blanking width +Timingblank_HsyncAct =1920 ;Horizontal effetive width +Timingblank_HsyncHbb =0 ;Horizontal back blanking width +Timingblank_VsyncVfb =0 ;Vertical front blanking height +Timingblank_VsyncVact =1080 ;Vertical effetive width +Timingblank_VsyncVbb=0 ;Vertical back blanking height +Timingblank_VsyncVbfb =0 ;Even-field vertical front blanking height(interlace, invalid progressive) +Timingblank_VsyncVbact=0 ;Even-field vertical effetive width(interlace, invalid progressive) +Timingblank_VsyncVbbb =0 ;Even-field vertical back blanking height(interlace, invalid progressive) + +;----- only for bt656 ---------- +FixCode =0 ;BT656_FIXCODE_1 = 0, + ;BT656_FIXCODE_0 +FieldPolar=0 ;BT656_FIELD_POLAR_STD = 0 + ;BT656_FIELD_POLAR_NSTD +DataPath =1 ;ISP enable or bypass + ;VI_PATH_BYPASS = 0,/* ISP bypass */ + ;VI_PATH_ISP = 1,/* ISP enable */ + ;VI_PATH_RAW = 2,/* Capture raw data, for debug */ +InputDataType=1 ;VI_DATA_TYPE_YUV = 0,VI_DATA_TYPE_RGB = 1, +DataRev =FALSE ;Data reverse. FALSE = 0; TRUE = 1 +DevRect_x=0 ; +DevRect_y=0 ; +DevRect_w=1920 ; +DevRect_h=1080 ; + +[vi_chn] +CapRect_X =0 +CapRect_Y =0 +CapRect_Width=1920 +CapRect_Height=1080 +DestSize_Width=1920 +DestSize_Height=1080 +CapSel =2 ;Frame/field select. ONLY used in interlaced mode + ;VI_CAPSEL_TOP = 0, /* top field */ + ;VI_CAPSEL_BOTTOM, /* bottom field */ + ;VI_CAPSEL_BOTH, /* top and bottom field */ + +PixFormat =23;PIXEL_FORMAT_YUV_SEMIPLANAR_422 = 22 + ;PIXEL_FORMAT_YUV_SEMIPLANAR_420 = 23 ...etc +CompressMode =0 ;COMPRESS_MODE_NONE = 0 + ;COMPRESS_MODE_SEG =1 ...etc + +SrcFrameRate=-1 ;Source frame rate. -1: not controll +FrameRate =-1 ;Target frame rate. -1: not controll +[wdr] +;only for wdr mode +Compress =FALSE ;WDR Compress. FALSE = 0; TRUE = 1 + +[vpss_group] +Vpss_DciEn =FALSE +Vpss_IeEn =FALSE +Vpss_NrEn =TRUE +Vpss_HistEn =FALSE +Vpss_DieMode=1 ;Define de-interlace mode + ;VPSS_DIE_MODE_AUTO = 0, + ;VPSS_DIE_MODE_NODIE = 1, + ;VPSS_DIE_MODE_DIE = 2, + +[vpss_corp] +Crop_enable =FALSE +Coordinate =1 ;VPSS_CROP_RATIO_COOR = 0, /*Ratio coordinate*/ + ;VPSS_CROP_ABS_COOR = 1 /*Absolute coordinate*/ +Crop_X =128 +Crop_Y =128 +Crop_W =1664 +Crop_H =824 + +[vpss_chn] +Vpss_W =1920 +Vpss_H =1080 +CompressMode=0 ;COMPRESS_MODE_NONE = 0 + ;COMPRESS_MODE_SEG =1 ...etc +Mirror =FALSE;Whether to mirror +Flip =TRUE;Whether to flip + +[vb_conf] +VbCnt=10 +vbTimes=15 ;when raw=8bit vbTimes = 10 + ;when raw=10/12 bit vbTimes = 15 + ;when raw=14/16 bit vbTimes = 20 +[venc_comm] +venc_chn =1 ;create venc chn number;(0,2] +BufCnt = 1 ;network meida-trans bufcnt + +[venc_0] +PicWidth =1920 +PicHeight =1080 +Profile =2 +RcMode =VENC_RC_MODE_H264CBR + +Gop =50 +StatTime =2 +ViFrmRate =25 +TargetFrmRate=25 +;----- only for VENC_RC_MODE_H264CBR ---------- +BitRate=4096 +FluctuateLevel=0 +;----- only for VENC_RC_MODE_H264VBR ---------- +MaxBitRate =10000 + +MaxQp=32 +MinQp=24 +;----- only for VENC_RC_MODE_H264FIXQP ---------- +IQp=45 + +PQp=40 +;-------- for REF_EX IsliceEnable------ +IsliceEnable = FALSE ;IsliceEnable and ViEnable is mutual exclusion +IsRefreshEnable = FALSE ;IsliceEnable and bRefreshEnable both TRUE is effective +RefreshLineNum = 12 ;PicHeight/16/6 6 is empirical value,ask Fuyang +ReqIQp = 30 +;-------- for REF_EX ViEnable------ +ViEnable = TRUE +ViInterval = 50 ; 2s +ViQpDelta = 2 + +[venc_1] +PicWidth =1920 +PicHeight =1080 +Profile =2 +RcMode =VENC_RC_MODE_H264CBR + +Gop =50 +StatTime =2 +ViFrmRate =25 +TargetFrmRate=25 +;----- only for VENC_RC_MODE_H264CBR ---------- +BitRate=4096 +FluctuateLevel=0 +;----- only for VENC_RC_MODE_H264VBR ---------- +MaxBitRate =10000 + +MaxQp=32 + +MinQp=24 +;----- only for VENC_RC_MODE_H264FIXQP ---------- +IQp=40 + +PQp=45 +;-------- for REF_EX IsliceEnable------ +IsliceEnable = FALSE ;IsliceEnable and ViEnable is mutual exclusion +IsRefreshEnable = FALSE ;IsliceEnable and bRefreshEnable both TRUE is effective +RefreshLineNum = 12 ;PicHeight/16/6 6 is empirical value,ask Fuyang +ReqIQp = 30 +;-------- for REF_EX ViEnable------ +ViEnable = TRUE +ViInterval = 50 ; 2s +ViQpDelta = 2 + +[bind] +ViDev =0 +ViChn =0 +VpssGrp =0 +VpssChn = 0 +VoDev =0 +VoChn =0 +ViSnapChn =0 +VpssSnapGrp=0 +VpssSnapChn=1 +VencSnapGrp=1 +VencSnapChn=3 diff --git a/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/usr/bin/load_hisilicon b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/usr/bin/load_hisilicon new file mode 100755 index 000000000..cea807421 --- /dev/null +++ b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/usr/bin/load_hisilicon @@ -0,0 +1,449 @@ +#!/bin/sh +# +# This is part of OpenIPC.org project | 2022.02.17 +# +# + +# SoC detect +chipid=$(ipcinfo --chip-name) + +# MMZ config +mem_start=0x80000000 # phy mem start + +mem_total=$(fw_printenv -n totalmem | tr -d 'M') +mem_total=${mem_total:=64} + +# Prefer kernel cmdline mem=NM -- that's the authoritative kernel/MMZ split, +# but only when it's a strict subset of total RAM. With mmz_allocator=cma +# (V4 CMA layouts), bootargs typically pass mem= with the MMZ +# chunk CMA-reserved within that range; in that case mem= is no longer an +# OS/MMZ split and we must fall through to the U-Boot osmem env. +os_mem_size=$(awk 'BEGIN{RS=" "} /^mem=[0-9]+M/{gsub(/^mem=|M.*$/,""); print; exit}' /proc/cmdline) +if [ -n "$os_mem_size" ] && [ "$os_mem_size" -ge "$mem_total" ]; then + os_mem_size="" +fi +if [ -z "$os_mem_size" ]; then + os_mem_size=$(fw_printenv -n osmem | tr -d 'M') +fi +os_mem_size=${os_mem_size:=32} + +report_error() { + echo "******* Error: There's something wrong, please check! *****" + exit 1 +} + +insert_mmz() { + insmod mmz.ko mmz=anonymous,0,$mmz_start,$mmz_size anony=1 || report_error +} + +insert_detect() { + cd /lib/modules/4.9.37/hisilicon + insmod sys_config.ko vi_vpss_online=$b_arg_online sensor=unknown pin_mux_select=0 + insert_mmz + insmod hi_media.ko + insmod hi3518e_base.ko + insmod hi3518e_sys.ko vi_vpss_online=$b_arg_online sensor=unknown mem_total=$mem_total + insmod open_openipc_frame_ts.ko + insert_isp + insmod sensor_i2c.ko + insmod sensor_spi.ko + SENSOR=ar0130 insert_sns +} + +remove_detect() { + rmmod -w open_sensor_spi + rmmod -w open_sensor_i2c + rmmod -w open_isp + rmmod -w open_openipc_frame_ts + rmmod -w hi3518e_sys + rmmod -w hi3518e_base + rmmod -w open_himedia + rmmod -w open_mmz + rmmod -w open_sys_config +} + +insert_audio() { + insmod acodec.ko + insmod hi3518e_aio.ko + insmod hi3518e_ai.ko + insmod hi3518e_ao.ko + insmod hi3518e_aenc.ko + insmod hi3518e_adec.ko + #insmod extdrv/tlv_320aic31.ko + echo "insert audio" +} + +remove_audio() { + rmmod -w tlv_320aic31.ko + rmmod -w hi3518e_adec + rmmod -w hi3518e_aenc + rmmod -w hi3518e_ao + rmmod -w hi3518e_ai + rmmod -w hi3518e_aio + rmmod -w acodec + echo "remove audio" +} + +remove_sns() { + rmmod -w open_sensor_spi &>/dev/null + rmmod -w open_sensor_i2c &>/dev/null +} + +insert_isp() { + case $SENSOR in + ov9750) + insmod hi3518e_isp.ko update_pos=1 + ;; + *) + insmod hi3518e_isp.ko update_pos=0 proc_param=1 + ;; + esac +} + +insert_sns() { + case $SENSOR in + 9m034 | jxf23 | ov2718 | ov9732 | ov9750 | ov9752 | jxh62 | sc1235 | sc2235 | ov9712 | gc2023) + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x200f007c 32 0x1 # VI_DATA13 + devmem 0x200f0080 32 0x1 # VI_DATA10 + devmem 0x200f0084 32 0x1 # VI_DATA12 + devmem 0x200f0088 32 0x1 # VI_DATA11 + devmem 0x200f008c 32 0x2 # VI_VS + devmem 0x200f0090 32 0x2 # VI_HS + devmem 0x200f0094 32 0x1 # VI_DATA9 + + devmem 0x2003002c 32 0xc4001 # Sensor unreset, clk 24MHz, VI 99MHz + ;; + ar0130 | ar0237 | gc1034 | jxf22 | sc1135 | sc1145 | sc2135) + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x200f007c 32 0x1 # VI_DATA13 + devmem 0x200f0080 32 0x1 # VI_DATA10 + devmem 0x200f0084 32 0x1 # VI_DATA12 + devmem 0x200f0088 32 0x1 # VI_DATA11 + devmem 0x200f008c 32 0x2 # VI_VS + devmem 0x200f0090 32 0x2 # VI_HS + devmem 0x200f0094 32 0x1 # VI_DATA9 + devmem 0x2003002c 32 0xb4001 # clk 27MHz, VI 99MHz + ;; + sc2035) + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x200f007c 32 0x1 # VI_DATA13 + devmem 0x200f0080 32 0x1 # VI_DATA10 + devmem 0x200f0084 32 0x1 # VI_DATA12 + devmem 0x200f0088 32 0x1 # VI_DATA11 + devmem 0x200f008c 32 0x2 # VI_VS + devmem 0x200f0090 32 0x2 # VI_HS + devmem 0x200f0094 32 0x1 # VI_DATA9 + devmem 0x2003002c 32 0xc4003 # clk 24MHz + ;; + ar0230) + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x2003002c 32 0xb4005 # sensor unreset, clk 27MHz, VI 148.5MHz + devmem 0x20030104 32 0x1 # vpss 148.5MHz + ;; + imx222) + devmem 0x200f0040 32 0x1 # SPI0_SCLK + devmem 0x200f0044 32 0x1 # SPI0_SDO + devmem 0x200f0048 32 0x1 # SPI0_SDI + devmem 0x200f004c 32 0x1 # SPI0_CSN + + devmem 0x200f007c 32 0x1 # VI_DATA13 + devmem 0x200f0080 32 0x1 # VI_DATA10 + devmem 0x200f0084 32 0x1 # VI_DATA12 + devmem 0x200f0088 32 0x1 # VI_DATA11 + devmem 0x200f008c 32 0x2 # VI_VS + devmem 0x200f0090 32 0x2 # VI_HS + devmem 0x200f0094 32 0x1 # VI_DATA9 + + devmem 0x2003002c 32 0x94001 # sensor unreset, clk 37.125MHz, VI 99MHz + + insmod sensor_spi.ko + ;; + imx323) + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x200f007c 32 0x1 # VI_DATA13 + devmem 0x200f0080 32 0x1 # VI_DATA10 + devmem 0x200f0084 32 0x1 # VI_DATA12 + devmem 0x200f0088 32 0x1 # VI_DATA11 + devmem 0x200f008c 32 0x2 # VI_VS + devmem 0x200f0090 32 0x2 # VI_HS + devmem 0x200f0094 32 0x1 # VI_DATA9 + + devmem 0x2003002c 32 0x94001 # sensor unreset, clk 37.125MHz, VI 99MHz + ;; + mn34222 | imx291 | imx327 | imx307) + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x2003002c 32 0x94001 # sensor unreset, clk 37.125MHz, VI 99MHz + ;; + ov2710_mipi | ov2735_i2c_1080p | ov2735) + # MIPI variant — uses libsns_ov2710_mipi.so via ov2710_mipi_1080p.ini. + # KC110 ov2735 patch: same MIPI pinmux + clock; sensor differentiation in libsns/.ini. + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x2003002c 32 0xc4001 # sensor unreset, clk 24MHz, VI 99MHz + ;; + ov2710_dc) + # DVP/parallel variant — uses libsns_ov2710_dc.so via ov2710_dc_1080p.ini + devmem 0x200f0040 32 0x2 # I2C0_SCL + devmem 0x200f0044 32 0x2 # I2C0_SDA + + devmem 0x200f007c 32 0x1 # VI_DATA13 + devmem 0x200f0080 32 0x1 # VI_DATA10 + devmem 0x200f0084 32 0x1 # VI_DATA12 + devmem 0x200f0088 32 0x1 # VI_DATA11 + devmem 0x200f008c 32 0x2 # VI_VS + devmem 0x200f0090 32 0x2 # VI_HS + devmem 0x200f0094 32 0x1 # VI_DATA9 + + devmem 0x2003002c 32 0xc4001 # sensor unreset, clk 24MHz, VI 99MHz + ;; + *) + echo "xxxx Invalid sensor type $SENSOR xxxx" + report_error + ;; + esac +} +insert_ko() { + # sys config + insmod sys_config.ko vi_vpss_online=$b_arg_online sensor=$SENSOR pin_mux_select=0 + + # driver load + insert_mmz + #insmod mmz.ko mmz=anonymous,0,$mmz_start,$mmz_size anony=1 || report_error + insmod hi_media.ko + insmod hi3518e_base.ko + + insmod hi3518e_sys.ko vi_vpss_online=$b_arg_online sensor=$SENSOR mem_total=$mem_total + if [[ $? -ne 0 ]]; then + exit + fi + + insmod hi3518e_tde.ko + insmod hi3518e_region.ko + insmod hi3518e_vgs.ko + + insmod open_openipc_frame_ts.ko + insert_isp + insmod hi3518e_viu.ko detect_err_frame=10 + insmod hi3518e_vpss.ko rfr_frame_comp=1 + #insmod hi3518e_vou.ko + #insmod hi3518e_vou.ko transparentTransmit=1 #enable transparentTransmit + #insmod hifb.ko video="hifb:vram0_size:1620" # default pal + + insmod hi3518e_rc.ko + insmod hi3518e_venc.ko + insmod hi3518e_chnl.ko ChnlLowPower=1 + insmod hi3518e_h264e.ko H264eMiniBufMode=1 + insmod hi3518e_jpege.ko + insmod hi3518e_ive.ko save_power=0 + #insmod hi3518e_ive.ko + insmod sensor_i2c.ko + insmod pwm.ko + #insmod piris.ko + insert_sns + insert_audio + + insmod hi_mipi.ko + insmod wdt.ko #nodeamon=1 + echo "==== Your input Sensor type is $SENSOR ====" +} + +remove_ko() { + rmmod -w open_wdt + rmmod -w open_sys_config + remove_audio + remove_sns + + rmmod -w open_pwm + + rmmod -w hi3518e_ive + + rmmod -w hi3518e_rc + rmmod -w hi3518e_jpege + rmmod -w hi3518e_h264e + rmmod -w hi3518e_chnl + rmmod -w hi3518e_venc + + rmmod -w hifb + #rmmod -w hi3518e_vou + rmmod -w hi3518e_vpss + rmmod -w hi3518e_viu + rmmod -w open_mipi_rx + + rmmod -w hi3518e_vgs + rmmod -w hi3518e_region + rmmod -w hi3518e_tde + + #rmmod -w piris + rmmod -w open_isp + rmmod -w open_openipc_frame_ts + rmmod -w hi3518e_sys + rmmod -w hi3518e_base + rmmod -w open_himedia + rmmod -w open_mmz +} + +load_usage() { + echo "Usage: ./load_hisilicon [-option] [sensor_name]" + echo "options:" + echo " -i insert modules" + echo " -r remove modules" + echo " -a remove modules first, then insert modules" + echo " -sensor sensor_name config sensor type [default: imx307]" + echo " -h help information" + echo -e "Available sensors: gc2053 imx307 imx327 imx335 os05a sc2231 sc2235 sc4236 sc3235 sc4236 etc." + echo -e "for example: ./load_hisilicon -i -sensor imx307 -osmem 32M -board demo -yuv0 0\n" +} + +calc_mmz_info() { + mmz_start=$(echo "$mem_start $os_mem_size" | + awk 'BEGIN { temp = 0; } + { + temp = $1/1024/1024 + $2; + } + END { printf("0x%x00000\n", temp); }') + + mmz_size=$(echo "$mem_total $os_mem_size" | + awk 'BEGIN { temp = 0; } + { + temp = $1 - $2; + } + END { printf("%dM\n", temp); }') + echo "mmz_start: $mmz_start, mmz_size: $mmz_size" +} + +######################parse arg################################### +b_arg_os_mem=0 +b_arg_total_mem=0 +b_arg_sensor=0 +b_arg_insmod=0 +b_arg_remove=0 +b_arg_online=0 + +for arg in $@; do + if [ $b_arg_total_mem -eq 1 ]; then + b_arg_total_mem=0 + mem_total=$arg + + if [ -z $mem_total ]; then + echo "[error] mem_total is null" + exit + fi + fi + + if [ $b_arg_os_mem -eq 1 ]; then + b_arg_os_mem=0 + os_mem_size=$arg + + if [ -z $os_mem_size ]; then + echo "[error] os_mem_size is null" + exit + fi + fi + + if [ $b_arg_sensor -eq 1 ]; then + b_arg_sensor=0 + SENSOR=$arg + fi + + case $arg in + "-i") + b_arg_insmod=1 + ;; + "-r") + b_arg_remove=1 + ;; + "-a") + b_arg_insmod=1 + b_arg_remove=1 + ;; + "-h") + load_usage + ;; + "-sensor") + b_arg_sensor=1 + ;; + "-osmem") + b_arg_os_mem=1 + ;; + "-total") + b_arg_total_mem=1 + ;; + "-offline") + b_arg_online=0 + ;; + esac +done +#######################parse arg end######################## + +# HWRNG: load before any conditional exit (os_mem mismatch, sensor +# probe failure, etc.) so entropy is always available to userspace +# daemons (majestic, dropbear, openssl) that block on getrandom(). +if [ "$b_arg_insmod" = "1" ]; then + modprobe open_hwrng 2>/dev/null +fi + + + + + +if [ $os_mem_size -ge $mem_total ]; then + echo "[err] os_mem[$os_mem_size], over total_mem[$mem_total]" + exit +fi + +calc_mmz_info + +#######################Action############################### + +if [ $# -lt 1 ]; then + load_usage + exit 0 +fi + +# Sensor config +# SENSOR=${SENSOR:=imx307} +# + +if [ -n "$SENSOR" ]; then + logger -s -p daemon.info -t hisilicon "Manualy set SENSOR as ${SENSOR}" +else + if fw_printenv -n sensor >/dev/null; then + SENSOR_ENV=$(fw_printenv -n sensor) + export SENSOR=${SENSOR_ENV} + logger -s -p daemon.info -t hisilicon "Get data from environment and set SENSOR as ${SENSOR}" + else + insert_detect + SENSOR_DETECT=$(ipcinfo --short-sensor) + export SENSOR=${SENSOR_DETECT:=unknown} + remove_detect + logger -s -p daemon.info -t hisilicon "Get data from ipcinfo and set SENSOR as ${SENSOR}" + fw_setenv sensor $SENSOR && logger -s -p daemon.info -t hisilicon "Write detected ${SENSOR} to U-Boot ENV" + fi +fi + +if [ $b_arg_remove -eq 1 ]; then + remove_ko +fi + +if [ "$SENSOR" = "unknown" ]; then + exit 1 +else + if [ $b_arg_insmod -eq 1 ]; then + cd /lib/modules/4.9.37/hisilicon + insert_ko + fi +fi diff --git a/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/usr/share/openipc/customizer.sh b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/usr/share/openipc/customizer.sh new file mode 100755 index 000000000..d007b2cf1 --- /dev/null +++ b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/overlay/usr/share/openipc/customizer.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Perform basic settings on a known IP camera +# + +# +# Set custom upgrade url +# +fw_setenv upgrade 'https://github.com/OpenIPC/builder/releases/download/latest/hi3518ev200_ultimate_tplink-kasa-kc110-nor.tgz' +fw_setenv wlandev 'rtl8188fu-generic' + +# +# Set custom majestic settings +# +cli -s .isp.sensorConfig /etc/sensors/ov2735_i2c_1080p.ini +cli -s .nightMode.irCutPin1 64 +cli -s .nightMode.irCutSingleInvert false +cli -s .nightMode.backlightPin 63 +cli -s .nightMode.lightMonitor true + +exit 0 diff --git a/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/scripts/excludes/hi3518ev200_ultimate.list b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/scripts/excludes/hi3518ev200_ultimate.list new file mode 100644 index 000000000..6fb1069a3 --- /dev/null +++ b/devices/hi3518ev200_ultimate_tplink-kasa-kc110/general/scripts/excludes/hi3518ev200_ultimate.list @@ -0,0 +1,50 @@ +/etc/sensors/ar0130_i2c_720p.ini +/etc/sensors/gc1034_i2c_720p.ini +/etc/sensors/imx222_i2c_1080p.ini +/etc/sensors/imx222_i2c_720p.ini +/etc/sensors/imx291_i2c_lvds_1080p.ini +/etc/sensors/imx323_i2c_dc_1080p.ini +/etc/sensors/imx323_spi_dc_1080p.ini +/etc/sensors/jxf22_i2c_1080p.ini +/etc/sensors/jxf23_i2c_1080p.ini +/etc/sensors/jxh62_i2c_720p.ini +/etc/sensors/jxh65_i2c_960p.ini +/etc/sensors/ov9712_i2c_720p.ini +/etc/sensors/ov9732_i2c_720p.ini +/etc/sensors/ov9750_i2c_960p.ini +/etc/sensors/sc1135_i2c_720p.ini +/etc/sensors/sc1145_i2c_720p.ini +/etc/sensors/sc1235_i2c_dc_960p.ini +/etc/sensors/sc2135_i2c_1080p.ini +/etc/sensors/sc2235_i2c_dc_1080p.ini +/etc/sensors/ar0237_i2c_dc_1080p.ini +# +/etc/sensors/iq/ar0230.ini +/etc/sensors/iq/imx222.ini +/etc/sensors/iq/ov9732.ini +/etc/sensors/iq/ov9750.ini +# +/usr/lib/sensors/libsns_ar0130.so +/usr/lib/sensors/libsns_gc1034.so +/usr/lib/sensors/libsns_gc2023.so +/usr/lib/sensors/libsns_imx122.so +/usr/lib/sensors/libsns_imx222.so +/usr/lib/sensors/libsns_imx291.so +/usr/lib/sensors/libsns_imx323_i2c_dc.so +/usr/lib/sensors/libsns_imx323_i2c_dc_v1.so +/usr/lib/sensors/libsns_jxf22.so +/usr/lib/sensors/libsns_jxf23.so +/usr/lib/sensors/libsns_jxh62.so +/usr/lib/sensors/libsns_jxh65.so +/usr/lib/sensors/libsns_mn34222.so +/usr/lib/sensors/libsns_ov2718.so +/usr/lib/sensors/libsns_ov9712.so +/usr/lib/sensors/libsns_ov9732.so +/usr/lib/sensors/libsns_ov9750.so +/usr/lib/sensors/libsns_ov9752.so +/usr/lib/sensors/libsns_sc1135.so +/usr/lib/sensors/libsns_sc1145.so +/usr/lib/sensors/libsns_sc1235.so +/usr/lib/sensors/libsns_sc2135.so +/usr/lib/sensors/libsns_sc2235.so +# diff --git a/package/kc110-board-support/Config.in b/package/kc110-board-support/Config.in new file mode 100644 index 000000000..66d1b4f9b --- /dev/null +++ b/package/kc110-board-support/Config.in @@ -0,0 +1,28 @@ +config BR2_PACKAGE_KC110_BOARD_SUPPORT + bool "kc110-board-support" + depends on BR2_arm + help + Board support for the TP-Link Kasa KC110 PTZ camera (HiSilicon + Hi3518EV200 + OV2735 MIPI sensor + 24BYJ48 stepper motors via + ULN2803 darlington array). + + Installs: + - ptz: C binary driving pan/tilt steppers (mmap PL061) and IR-cut / + IR-LED via vendor-base-10 GPIO numbering. Replaces the upstream + gpio-motors sysfs approach which uses different pin numbers. + - gpio-motors: small shell shim that forwards web-GUI calls + (gpio-motors X Y N) to the ptz CLI. + - ir-control.sh: convenience wrapper for IR-LED + IR-cut filter. + - S94pinmux-pan-ir: boot-time init that writes 0x1 to pinmux + registers 0x200f00e8/ec/f0/fc/100/104 for chip7/8 pads. OpenIPC + kernel pinctrl doesn't auto-set this for the KC110's pan + IR-cut + + IR-LED pads (vendor's hn_config.sh writes them explicitly). + + KC110 GPIO map (vendor base-10 numbering = OpenIPC PL061 pins): + pan: 65 / 60 / 59 / 58 (vendor 81/74/73/72) + tilt: 0 / 1 / 2 / 36 (vendor 0/1/2/44) + ir-cut: 64 (vendor 80) + ir-led: 63 (vendor 77) + + Conflicts with the upstream gpio-motors package — this provides a + drop-in replacement at /usr/bin/gpio-motors that calls ptz. diff --git a/package/kc110-board-support/README.md b/package/kc110-board-support/README.md new file mode 100644 index 000000000..22f61fb4c --- /dev/null +++ b/package/kc110-board-support/README.md @@ -0,0 +1,59 @@ +# kc110-board-support + +Buildroot package providing KC110-specific hardware support for OpenIPC. Delivers: + +| File | Purpose | +|---|---| +| `/usr/bin/ptz` | C binary (mmap PL061) for pan/tilt + IR-cut + IR-LED via vendor-base-10 GPIO numbering | +| `/usr/bin/gpio-motors` | 451-byte shell shim that forwards calls to `ptz pan/tilt` (web-GUI compatible) | +| `/usr/bin/ir-control.sh` | Wrapper for IR-LED + IR-cut filter control | +| `/etc/init.d/S94pinmux-pan-ir` | Boot-time init that writes 0x1 to pinmux registers 0x200f00e8/ec/f0/fc/100/104 (KC110 chip7/8 pads) | + +## Layout + +``` +kc110-board-support/ +├── Config.in # BR2_PACKAGE_KC110_BOARD_SUPPORT +├── kc110-board-support.mk # generic-package build + install rules +├── src/ # source tree (compiled with target toolchain) +│ ├── Makefile +│ └── ptz.c # ARM-static-stripped via cross-compile +└── files/ # installed as-is (no build step) + ├── etc/init.d/S94pinmux-pan-ir + └── usr/bin/{gpio-motors,ir-control.sh} +``` + +## Use from upstream openipc-firmware + +The package lives at the repo root (`kc110-board-support/`) but must be discoverable by Buildroot inside the upstream tree via a symlink: + +``` +openipc/openipc-firmware/general/package/kc110-board-support → ../../../../kc110-board-support +``` + +The symlink is reconstructed by the setup step (below). + +## Setup after `git pull` of upstream + +Because `openipc/` is gitignored, the wire-in edits in the upstream tree are lost on each pull. Re-apply them from `patches/openipc-wire-in-kc110.patch`: + +```bash +cd openipc/openipc-firmware +git apply ../../patches/openipc-wire-in-kc110.patch + +# Re-create the symlink if it disappeared: +[ -L general/package/kc110-board-support ] || \ + ln -s ../../../../kc110-board-support general/package/kc110-board-support +``` + +The patch adds: +- A `source` statement for the package in `general/package/Config.in` +- `BR2_PACKAGE_KC110_BOARD_SUPPORT=y` to `hi3518ev200_ultimate_defconfig` and removes `BR2_PACKAGE_GPIO_MOTORS=y` (our shim replaces upstream's gpio-motors binary) + +## Build + +Picked up automatically by `BOARD=hi3518ev200_ultimate make all` once the wire-in is applied. The `ptz` binary builds reproducibly — md5 `e70f78d73c91248ba044082e3f21c54b` across rebuilds. + +## Conflict with upstream gpio-motors + +`BR2_PACKAGE_GPIO_MOTORS` (upstream) and `BR2_PACKAGE_KC110_BOARD_SUPPORT` both install `/usr/bin/gpio-motors` and cannot be enabled simultaneously. The wire-in patch removes `BR2_PACKAGE_GPIO_MOTORS=y` from the defconfig to avoid the collision. diff --git a/package/kc110-board-support/files/etc/init.d/S94pinmux-pan-ir b/package/kc110-board-support/files/etc/init.d/S94pinmux-pan-ir new file mode 100755 index 000000000..a0bdf05ee --- /dev/null +++ b/package/kc110-board-support/files/etc/init.d/S94pinmux-pan-ir @@ -0,0 +1,26 @@ +#!/bin/sh +# KC110 pad-pinmux: chip7/8 GPIOs used by pan motor + IR-cut + IR-LED +# need function=0x1 (per vendor hn_config.sh comments "GPIO8_0", "GPIO8_1"). +# OpenIPC kernel pinctrl leaves them at default (0x0 = alternate peripheral) +# which makes PL061 GPIODATA writes succeed silently — pad never toggles. +# Stock-OpenIPC builds (and prior boot residue) can also leave wrong state +# across soft-reboot, so we force-write here regardless of current value. + +start() { + # pan motor coils B/C/D (chip7 pin 2/3/4) + IR-LED (chip7 pin 7) + devmem 0x200f00e8 32 0x1 + devmem 0x200f00ec 32 0x1 + devmem 0x200f00f0 32 0x1 + devmem 0x200f00fc 32 0x1 + # IR-cut solenoid (chip8 pin 0) + pan motor coil A (chip8 pin 1) + devmem 0x200f0100 32 0x1 + devmem 0x200f0104 32 0x1 +} + +case "$1" in + start|"") start ;; + stop) ;; + restart|reload) start ;; + *) echo "usage: $0 {start|stop|restart}"; exit 1 ;; +esac +exit 0 diff --git a/package/kc110-board-support/files/usr/bin/gpio-motors b/package/kc110-board-support/files/usr/bin/gpio-motors new file mode 100755 index 000000000..b7282c82e --- /dev/null +++ b/package/kc110-board-support/files/usr/bin/gpio-motors @@ -0,0 +1,11 @@ +#!/bin/sh +# Shim: OpenIPC motor.cgi calls "gpio-motors " +# Translate to our ptz binary which knows the correct PL061 mapping. +pan="${1:-0}" +tilt="${2:-0}" +phase="${3:-3}" +log() { logger -t gpio-motors "$@"; } +log "called pan=$pan tilt=$tilt phase=$phase" +[ "$pan" != "0" ] && /usr/bin/ptz pan "$pan" "$phase" 2>&1 | logger -t gpio-motors +[ "$tilt" != "0" ] && /usr/bin/ptz tilt "$tilt" "$phase" 2>&1 | logger -t gpio-motors +exit 0 diff --git a/package/kc110-board-support/files/usr/bin/ir-control.sh b/package/kc110-board-support/files/usr/bin/ir-control.sh new file mode 100755 index 000000000..63ecd12b7 --- /dev/null +++ b/package/kc110-board-support/files/usr/bin/ir-control.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# KC110 IR-LED + IR-cut master via sysfs gpio export. +# GPIO 77 = IR-LED (active-high, vendor UCI: ledOnValue=1, ledOffValue=0) +# GPIO 80 = IR-cut master (active-low, vendor: set_ir_cut(1) writes 0) +# Modes: +# day - LED off, IR-cut filter IN (sees IR as colour-cast — bad, so cut blocks it) +# night - LED on, IR-cut filter OUT (lets IR illuminate the scene) + +GPIO_LED=77 +GPIO_CUT=80 + +export_pin() { + [ -d /sys/class/gpio/gpio$1 ] || echo $1 > /sys/class/gpio/export 2>/dev/null + echo out > /sys/class/gpio/gpio$1/direction 2>/dev/null +} + +set_pin() { + echo $2 > /sys/class/gpio/gpio$1/value +} + +case "$1" in + day) + export_pin $GPIO_LED + export_pin $GPIO_CUT + set_pin $GPIO_LED 0 + set_pin $GPIO_CUT 0 # active-low: 0 = filter IN + ;; + night) + export_pin $GPIO_LED + export_pin $GPIO_CUT + set_pin $GPIO_CUT 1 # active-low: 1 = filter OUT + sleep 1 # let mechanical filter settle + set_pin $GPIO_LED 1 + ;; + off) + set_pin $GPIO_LED 0 2>/dev/null + set_pin $GPIO_CUT 0 2>/dev/null + ;; + status) + echo "led: $(cat /sys/class/gpio/gpio$GPIO_LED/value 2>/dev/null || echo unset)" + echo "ircut: $(cat /sys/class/gpio/gpio$GPIO_CUT/value 2>/dev/null || echo unset)" + ;; + *) + echo "usage: $0 {day|night|off|status}" >&2 + exit 1 + ;; +esac diff --git a/package/kc110-board-support/kc110-board-support.mk b/package/kc110-board-support/kc110-board-support.mk new file mode 100644 index 000000000..925220c91 --- /dev/null +++ b/package/kc110-board-support/kc110-board-support.mk @@ -0,0 +1,19 @@ +################################################################################ +# +# kc110-board-support +# +################################################################################ + +KC110_BOARD_SUPPORT_SITE_METHOD = local +KC110_BOARD_SUPPORT_SITE = $(KC110_BOARD_SUPPORT_PKGDIR)/src + +define KC110_BOARD_SUPPORT_BUILD_CMDS + $(MAKE) CC="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS)" -C $(@D) +endef + +define KC110_BOARD_SUPPORT_INSTALL_TARGET_CMDS + $(INSTALL) -D -m 0755 $(@D)/ptz $(TARGET_DIR)/usr/bin/ptz + cp -a $(KC110_BOARD_SUPPORT_PKGDIR)/files/. $(TARGET_DIR)/ +endef + +$(eval $(generic-package)) diff --git a/package/kc110-board-support/src/Makefile b/package/kc110-board-support/src/Makefile new file mode 100644 index 000000000..479600e2a --- /dev/null +++ b/package/kc110-board-support/src/Makefile @@ -0,0 +1,12 @@ +CC ?= gcc +CFLAGS ?= -O2 + +all: ptz + +ptz: ptz.c + $(CC) $(CFLAGS) -o $@ $< + +clean: + rm -f ptz + +.PHONY: all clean diff --git a/package/kc110-board-support/src/ptz.c b/package/kc110-board-support/src/ptz.c new file mode 100644 index 000000000..b28bac67a --- /dev/null +++ b/package/kc110-board-support/src/ptz.c @@ -0,0 +1,281 @@ +/* ptz — KC110 pan/tilt/IR controller via PL061 /dev/mem (no sysfs overhead). + * + * Pin map (OpenIPC numbering on Hi3518EV200 PL061 chips 0-8). + * Derived from vendor kernel disasm of gpio_set_value @ 0xc01b854c: + * bank = vendor_gpio / 10, pin = vendor_gpio % 10 + * PL061_phys = 0x20140000 + (bank << 16) + (1 << (pin + 2)) + * + * pan A=8/1 B=7/4 C=7/3 D=7/2 (vendor 81 / 74 / 73 / 72) + * tilt A=0/0 B=0/1 C=0/2 D=4/4 (vendor 0 / 1 / 2 / 44) + * IR-LED bank 7 pin 7 (vendor 77, active-high) + * IR-cut bank 8 pin 0 (vendor 80, edge-triggers solenoid) + * + * State persisted in /etc/ptz_state across invocations so phase continuity + * across CLI calls keeps the motor smooth. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PL061_BASE 0x20140000u +#define PL061_STRIDE 0x10000u +#define GPIODIR_OFF 0x400u +#define STATE_PATH "/etc/ptz_state" + +#define PAN_STEPS_MAX 1934 +#define PAN_DEG 170 +#define TILT_STEPS_MAX 387 +#define TILT_DEG 68 + +struct pin { uint8_t bank, pin; }; + +static const struct pin PAN[4] = { {8,1}, {7,4}, {7,3}, {7,2} }; /* A B C D */ +static const struct pin TILT[4] = { {0,0}, {0,1}, {0,2}, {4,4} }; +static const struct pin IRLED = {7,7}; +static const struct pin IRCUT = {8,0}; + +/* Full-step (2-phase-on) sequence: AB, BC, CD, DA. */ +static const uint8_t SEQ[4][4] = { + {1,1,0,0}, {0,1,1,0}, {0,0,1,1}, {1,0,0,1}, +}; + +static void *bank_map[9]; + +static void *map_bank(uint8_t bank) { + if (bank_map[bank]) return bank_map[bank]; + int fd = open("/dev/mem", O_RDWR | O_SYNC); + if (fd < 0) { perror("/dev/mem"); exit(1); } + off_t phys = PL061_BASE + (off_t)bank * PL061_STRIDE; + void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, phys); + close(fd); + if (p == MAP_FAILED) { fprintf(stderr,"mmap bank %u: %s\n",bank,strerror(errno)); exit(1); } + bank_map[bank] = p; + return p; +} + +static void pin_set_output(struct pin p) { + volatile uint32_t *dir = (uint32_t *)((uint8_t *)map_bank(p.bank) + GPIODIR_OFF); + *dir |= (1u << p.pin); +} + +static void pin_write(struct pin p, int v) { + /* PL061 GPIODATA: address = base + (mask << 2). Only bits in mask change. */ + uint8_t *base = (uint8_t *)map_bank(p.bank); + uint32_t mask = 1u << p.pin; + volatile uint32_t *reg = (uint32_t *)(base + (mask << 2)); + *reg = v ? mask : 0; +} + +static void msleep(long ms) { + struct timespec ts = { ms / 1000, (ms % 1000) * 1000000L }; + nanosleep(&ts, NULL); +} + +/* State -------------------------------------------------------------------- */ + +struct state { + int pan_pos; /* 0 .. PAN_STEPS_MAX */ + int tilt_pos; /* 0 .. TILT_STEPS_MAX */ + int pan_phase; /* 0..3 — last SEQ index applied */ + int tilt_phase; + int ircut; /* 0 or 1 */ + int irled; /* 0 or 1 */ +}; + +static void state_load(struct state *s) { + memset(s, 0, sizeof(*s)); + FILE *f = fopen(STATE_PATH, "r"); + if (!f) return; + fscanf(f, "pan_pos=%d tilt_pos=%d pan_phase=%d tilt_phase=%d ircut=%d irled=%d", + &s->pan_pos, &s->tilt_pos, &s->pan_phase, &s->tilt_phase, + &s->ircut, &s->irled); + fclose(f); +} + +static void state_save(const struct state *s) { + FILE *f = fopen(STATE_PATH, "w"); + if (!f) { perror(STATE_PATH); return; } + fprintf(f, "pan_pos=%d tilt_pos=%d pan_phase=%d tilt_phase=%d ircut=%d irled=%d\n", + s->pan_pos, s->tilt_pos, s->pan_phase, s->tilt_phase, s->ircut, s->irled); + fclose(f); +} + +/* Motor drive -------------------------------------------------------------- */ + +static void apply_phase(const struct pin coils[4], int phase) { + const uint8_t *row = SEQ[phase & 3]; + for (int i = 0; i < 4; i++) pin_write(coils[i], row[i]); +} + +static void park(const struct pin coils[4]) { + for (int i = 0; i < 4; i++) pin_write(coils[i], 0); +} + +static void prep_motor(const struct pin coils[4]) { + for (int i = 0; i < 4; i++) pin_set_output(coils[i]); +} + +/* Drive `delta` steps. Positive = forward sequence direction. Returns new phase. */ +static int drive(const struct pin coils[4], int start_phase, int delta, long phase_ms) { + int phase = start_phase; + int dir = (delta > 0) ? 1 : -1; + int n = delta * dir; + while (n--) { + phase = (phase + dir + 4) & 3; + apply_phase(coils, phase); + msleep(phase_ms); + } + return phase; +} + +/* Commands ----------------------------------------------------------------- */ + +static int clamp(int v, int lo, int hi) { + return v < lo ? lo : (v > hi ? hi : v); +} + +static int cmd_pan(struct state *s, int delta, long phase_ms) { + int target = clamp(s->pan_pos + delta, 0, PAN_STEPS_MAX); + int real = target - s->pan_pos; + prep_motor(PAN); + s->pan_phase = drive(PAN, s->pan_phase, real, phase_ms); + park(PAN); + s->pan_pos = target; + return real; +} + +static int cmd_tilt(struct state *s, int delta, long phase_ms) { + int target = clamp(s->tilt_pos + delta, 0, TILT_STEPS_MAX); + int real = target - s->tilt_pos; + prep_motor(TILT); + s->tilt_phase = drive(TILT, s->tilt_phase, real, phase_ms); + park(TILT); + s->tilt_pos = target; + return real; +} + +/* Home: drive backwards beyond max range, hit mechanical stop, mark as 0. */ +static void cmd_home(struct state *s, long phase_ms) { + prep_motor(PAN); + drive(PAN, s->pan_phase, -(PAN_STEPS_MAX + 200), phase_ms); + park(PAN); + s->pan_phase = 0; + s->pan_pos = 0; + + prep_motor(TILT); + drive(TILT, s->tilt_phase, -(TILT_STEPS_MAX + 50), phase_ms); + park(TILT); + s->tilt_phase = 0; + s->tilt_pos = 0; +} + +static void cmd_irled(struct state *s, int on) { + pin_set_output(IRLED); + pin_write(IRLED, on ? 1 : 0); + s->irled = on ? 1 : 0; +} + +/* IR-cut: edge-triggered solenoid. Pulse the master GPIO to flip filter state. */ +static void cmd_daynight(struct state *s) { + pin_set_output(IRCUT); + int new_state = !s->ircut; + pin_write(IRCUT, new_state); + /* hold long enough for solenoid to actuate */ + msleep(80); + /* leave the pin in the new state; vendor pattern is to keep the bit set */ + s->ircut = new_state; +} + +static void cmd_status(const struct state *s) { + printf("pan_pos=%d/%d tilt_pos=%d/%d ircut=%d irled=%d\n", + s->pan_pos, PAN_STEPS_MAX, s->tilt_pos, TILT_STEPS_MAX, + s->ircut, s->irled); +} + +/* Argument parsing --------------------------------------------------------- */ + +static int parse_int(const char *s) { + char *end; long v = strtol(s, &end, 10); + if (*end) { fprintf(stderr,"bad integer: %s\n",s); exit(2); } + return (int)v; +} + +static void usage(void) { + fprintf(stderr, + "usage:\n" + " ptz pan <+N|-N|abs N> [phase_ms]\n" + " ptz tilt <+N|-N|abs N> [phase_ms]\n" + " ptz home [phase_ms]\n" + " ptz ir on|off\n" + " ptz daynight\n" + " ptz status\n" + " ptz raw set <0|1> (debug)\n" + "default phase_ms = 3\n"); + exit(2); +} + +int main(int argc, char **argv) { + if (argc < 2) usage(); + struct state s; state_load(&s); + + if (!strcmp(argv[1], "pan")) { + if (argc < 3) usage(); + long pms = (argc >= 4 && strcmp(argv[2],"abs")) ? parse_int(argv[3]) : 3; + int delta; + if (!strcmp(argv[2], "abs")) { + if (argc < 4) usage(); + delta = parse_int(argv[3]) - s.pan_pos; + pms = (argc >= 5) ? parse_int(argv[4]) : 3; + } else delta = parse_int(argv[2]); + int real = cmd_pan(&s, delta, pms); + printf("pan: %d steps (requested %d, clamped) → pos=%d\n", real, delta, s.pan_pos); + } + else if (!strcmp(argv[1], "tilt")) { + if (argc < 3) usage(); + long pms = (argc >= 4 && strcmp(argv[2],"abs")) ? parse_int(argv[3]) : 3; + int delta; + if (!strcmp(argv[2], "abs")) { + if (argc < 4) usage(); + delta = parse_int(argv[3]) - s.tilt_pos; + pms = (argc >= 5) ? parse_int(argv[4]) : 3; + } else delta = parse_int(argv[2]); + int real = cmd_tilt(&s, delta, pms); + printf("tilt: %d steps (requested %d, clamped) → pos=%d\n", real, delta, s.tilt_pos); + } + else if (!strcmp(argv[1], "home")) { + long pms = (argc >= 3) ? parse_int(argv[2]) : 4; + cmd_home(&s, pms); + puts("home: reset to (0,0)"); + } + else if (!strcmp(argv[1], "ir")) { + if (argc < 3) usage(); + cmd_irled(&s, !strcmp(argv[2], "on")); + printf("ir: %s\n", s.irled ? "on" : "off"); + } + else if (!strcmp(argv[1], "daynight")) { + cmd_daynight(&s); + printf("daynight: ircut=%d\n", s.ircut); + } + else if (!strcmp(argv[1], "status")) { + cmd_status(&s); + } + else if (!strcmp(argv[1], "raw") && argc >= 6 && !strcmp(argv[2],"set")) { + struct pin p = { (uint8_t)parse_int(argv[3]), (uint8_t)parse_int(argv[4]) }; + pin_set_output(p); + pin_write(p, parse_int(argv[5])); + printf("raw: bank=%u pin=%u val=%s\n", p.bank, p.pin, argv[5]); + } + else usage(); + + state_save(&s); + return 0; +}