【BBuf的cuda学习笔记十】Megatron-LM的gradient_accumulation_fusion优化

部署运行你感兴趣的模型镜像

0x0. 前言

这篇文章来解析一下Megaton-LM涉及到的一个优化gradient_accumulation_fusion。这里fusion的意思是在gemm接口中会将当前的结果累加到先前计算的梯度上,所有这些都在一个操作中完成,可以避免多次访问global memory提升算子的带宽。下面解析一下这个优化的调度逻辑和cuda实现。

0x1. 调度逻辑解析

gradient_accumulation_fusion的调度逻辑是和LinearWithGradAccumulationAndAsyncCommunication这个类的实现有关的,LinearWithGradAccumulationAndAsyncCommunication 这个类又被包了一层变成 linear_with_grad_accumulation_and_async_allreduce 这个函数,这个函数又给RowParallelLinearColumnParallelLinear这两个实现模型并行的Linear类使用。

下面解析一下linear_with_grad_accumulation_and_async_allreduce这个函数(https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/core/tensor_parallel/layers.py#L356-L446):

# 这部分定义了一个函数,名为linear_with_grad_accumulation_and_async_allreduce,
# 它接收七个参数:输入张量、权重张量、一个可选的偏置张量和3个布尔标志。
def linear_with_grad_accumulation_and_async_allreduce(
    input: torch.Tensor,
    weight: torch.Tensor,
    bias: Optional[torch.Tensor],
    gradient_accumulation_fusion: bool,
    async_grad_allreduce: bool,
    sequence_parallel_enabled: bool,
) -> torch.Tensor:
    """带有反向传播的异步通信和梯度累积融合的线性层实现.

    此函数提供了一个选项,可以将反向传播计算的结果累积到一个现有的梯度缓冲区中,
    从而避免在梯度计算后进行额外的加法核操作。

    此外,输入梯度的张量并行all reduce可以与权重梯度的计算异步进行。

    在使用序列并行的情况下,输入梯度的reduce scatter与权重梯度的计算异步进行。

    使用此模块需要环境变量CUDA_DEVICE_MAX_CONNECTIONS=1。代码中有一些集合操作,
    应该在计算核之前调度,以使通信与计算重叠,这对于加速是必要的,但对于正确性则不是必要的,
    因此调度器不会强制这种排序。将CUDA_DEVICE_MAX_CONNECTIONS设置为1会强制按照它们被调用的顺序调度内核。

    Arguments:

    input (torch.Tensor required): 输入,类似torch.nn.functional.linear

    weight (torch.Tensor required): 权重,类似torch.nn.functional.linear

    bias (torch.Tensor optional): 偏置,类似torch.nn.functional.linear

    gradient_accumulation_fusion (bool required): 执行梯度累积融合,
    需要自定义的CUDA扩展模块fused_weight_gradient_mlp_cuda。
    要使用gradient_accumulation_fusion,你必须使用--cpp_ext和--cuda_ext安装APEX。
    例如:"pip install --global-option="--cpp_ext" --global-option="--cuda_ext ." 
    注意,此扩展要求CUDA版本大于或等于11。否则,你必须关闭梯度累积融合。

    async_grad_allreduce (bool required): 异步地与权重梯度的计算进行输入梯度的allreduce。
    如果sequence_parallel_enabled为True,这必须为False,因为不执行allreduce。

    sequence_parallel_enabled (bool required): 表示使用了序列并行,
    因此在前向传播中,输入是add gather后的,在反向传播中,输入梯度是reduce scatter后的。
    """
    # 这部分创建了一个名为args的列表,它基本上是函数输入参数的集合。
    args = [
        input,
        weight,
        bias,
        gradient_accumulation_fusion,
        async_grad_allreduce,
        sequence_parallel_enabled,
    ]

    # 这部分检查是否已经发出警告。函数使用一个类级别变量warned来记住是否已经向用户显示了警告。
    if not linear_with_grad_accumulation_and_async_allreduce.warned:
        # 这部分检查环境变量CUDA_DEVICE_MAX_CONNECTIONS是否设置为"1"。
        # 如果没有,并且满足某些条件(sequence_parallel_enabled或async_grad_allreduce),
        # 它会发出警告。然后将warned标志设置为True,以便不会重复发出此警告。
        if os.environ.get('CUDA_DEVICE_MAX_CONNECTIONS') != "1":
            if sequence_parallel_enabled:
                warnings.warn(
                    "When using sequence parallelism it is recommended to set the "
                    "environment variable CUDA_DEVICE_MAX_CONNECTIONS to 1 for "
                    "maximum speedup")
                linear_with_grad_accumulation_and_async_allreduce.warned = True

            if async_grad_allreduce:
                warnings.warn(
                    "When using async grad allreduce it is recommended to set the "
                    "environment variable CUDA_DEVICE_MAX_CONNECTIONS to 1 for "
                    "maximum speedup")
                linear_with_grad_accumulation_and_async_allreduce.warned = True

    # 最后,函数调用另一个名为LinearWithGradAccumulationAndAsyncCommunication的类并返回其结果。
    return LinearWithGradAccumulationAndAsyncCommunication.apply(*args)

# 在函数外部,初始化属性warned为False。这用于检查是否已经向用户发出警告。
linear_with_grad_accumulation_and_async_allreduce.warned = False

解着解析一下LinearWithGradAccumulationAndAsyncCommunication这个类的实现(https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/core/tensor_parallel/layers.py#L232):

# 这定义了一个名为LinearWithGradAccumulationAndAsyncCommunication的类,
# 该类继承自torch.autograd.Function。
class LinearWithGradAccumulationAndAsyncCommunication(torch.autograd.Function):
    """See linear_with_grad_accumulation_and_async_allreduce"""

    # 使用两个装饰器标记forward方法。其中@staticmethod表示这是一个静态方法,
    # 而@custom_fwd是一个自定义装饰器,用于特定的前向传播操作。
    @staticmethod
    @custom_fwd
    def forward(
        ctx,
        input,
        weight,
        bias,
        gradient_accumulation_fusion,
        async_grad_allreduce,
        sequence_parallel,
    ):
        # 使用上下文对象ctx保存输入和权重,以便在后向传播中使用。
        ctx.save_for_backward(input, weight)
        # 在上下文对象ctx中存储其他变量和标志。
        ctx.use_bias = bias is not None
        ctx.gradient_accumulation_fusion = gradient_accumulation_fusion
        ctx.async_grad_allreduce = async_grad_allreduce
        ctx.sequence_parallel = sequence_parallel

        # 如果启用了序列并行,则进行以下操作:
        if sequence_parallel:
            # 获取模型并行的world_size(通常是参与并行处理的GPU数量)。
            world_size = get_tensor_model_parallel_world_size()
            # 更改输入的第一个维度以考虑模型并行的全部大小。
            dim_size = list(input.size())
            dim_size[0] = dim_size[0] * world_size

            # 收集所有GPU上的输入。
            all_gather_buffer = get_global_memory_buffer().get_tensor(dim_size, input.dtype, "mpu")
            torch.distributed._all_gather_base(
                all_gather_buffer, input, group=get_tensor_model_parallel_group()
            )
            # 更新total_input为收集的数据。
            total_input = all_gather_buffer
        else:
            # 如果不使用序列并行,则total_input仅仅是传入的输入。
            total_input = input

        # 对total_input和weight的转置进行矩阵乘法以计算输出。
        output = torch.matmul(total_input, weight.t())
        # 如果提供了偏置,则将其添加到输出中
        if bias is not None:
            output = output + bias
        return output

    @staticmethod
    @custom_bwd
    def backward(ctx, grad_output):
        # 从上下文对象中恢复前向传播保存的张量。
        input, weight = ctx.saved_tensors
        # 从上下文对象中恢复偏置使用的信息。
        use_bias = ctx.use_bias

        # 如果启用了序列并行,要如何获取完整的输入数据。
        # 它通过分布式的_all_gather_base函数来异步地聚集所有输入。
        if ctx.sequence_parallel:
            world_size = get_tensor_model_parallel_world_size()
            dim_size = list(input.size())
            dim_size[0] = dim_size[0] * world_size

            all_gather_buffer = get_global_memory_buffer().get_tensor(dim_size, input.dtype, "mpu")
            handle = torch.distributed._all_gather_base(
                all_gather_buffer, input, group=get_tensor_model_parallel_group(), async_op=True
            )

            # Here we rely on CUDA_DEVICE_MAX_CONNECTIONS=1 to ensure that the
            # gather is scheduled before the input gradient computation
            total_input = all_gather_buffer
        # 如果没有启用序列并行,那么完整的输入就是原始输入。
        else:
            total_input = input
        # 通过矩阵乘法计算关于输入的梯度。
        grad_input = grad_output.matmul(weight)

        # 如果启用了序列并行,则等待所有聚集操作完成。
        if ctx.sequence_parallel:
            handle.wait()

        # Doing gather + slicing during the NeMo forward pass can make this tensor
        # not be contiguous. PyTorch only checks if the tensor is contiguous, and only
        # clones it if it's not contiguous:
        # https://github.com/pytorch/pytorch/blob/c47cf9bc7f9e02f649ab4ed53fe4d35732c92ab6/torch/_refs/__init__.py#L2761
        # 这些是注释,提到在NeMo的前向传递中,执行gather和slicing操作可能会导致grad_output张量
        # 不是连续的。PyTorch只检查张量是否是连续的,并且只在不连续时克隆它。
        grad_output = grad_output.contiguous() # 确保grad_output是连续的
        # Convert the tensor shapes to 2D for execution compatibility
        # 将grad_output张量的形状转化为2D,以确保兼容性。
        grad_output = grad_output.view(
            grad_output.shape[0] * grad_output.shape[1], grad_output.shape[2]
        )
        # 同样地,将total_input张量也转化为2D。
        total_input = total_input.view(
            total_input.shape[0] * total_input.shape[1], total_input.shape[2]
        )

        # 如果启用了异步的梯度all-reduce,执行该操作。这是一个分布式操作,用于聚合所有工作节点上的梯度。
        if ctx.async_grad_allreduce:
            # Asynchronous all-reduce
            handle = torch.distributed.all_reduce(
                grad_input, group=get_tensor_model_parallel_group(), async_op=True
            )
            # Here we rely on CUDA_DEVICE_MAX_CONNECTIONS=1 to ensure that the
            # all-reduce is scheduled before the weight gradient computation

        # 如果启用了序列并行,则不应该在此处启用异步all-reduce(由assert语句确保)。
        # 接着,创建一个新的sub_grad_input张量,并执行一个reduce_scatter操作。
        # 这是一个分布式操作,它会将输入的梯度从所有工作节点上聚合到一个工作节点上。
        if ctx.sequence_parallel:
            assert not ctx.async_grad_allreduce
            dim_size = list(input.size())
            sub_grad_input = torch.empty(
                dim_size, dtype=input.dtype, device=torch.cuda.current_device(), requires_grad=False
            )
            # reduce_scatter
            handle = torch.distributed._reduce_scatter_base(
                sub_grad_input, grad_input, group=get_tensor_model_parallel_group(), async_op=True
            )
            # Here we rely on CUDA_DEVICE_MAX_CONNECTIONS=1 to ensure that the
            # reduce scatter is scheduled before the weight gradient computation

        # 根据是否启用了梯度累积融合,使用特定的CUDA操作或标准的矩阵乘法来计算权重的梯度。
        # 这个条件检查是否启用了梯度累积融合。梯度累积通常在小批量训练中用于累积梯度以在较大的有效批量上更新模型。
        if ctx.gradient_accumulation_fusion:
            if weight.main_grad.dtype == torch.float32:
                fused_weight_gradient_mlp_cuda.wgrad_gemm_accum_fp32(
                    total_input, grad_output, weight.main_grad
                )
            elif weight.main_grad.dtype in (torch.float16, torch.bfloat16):
                fused_weight_gradient_mlp_cuda.wgrad_gemm_accum_fp16(
                    total_input, grad_output, weight.main_grad
                )
            else:
                raise RuntimeError("Unsupported gradient type for gradient accumulation fusion")
            # 在梯度累积融合的情况下,设置grad_weight为None,
            # 这意味着梯度已经在前面的CUDA函数中直接更新了(weight.main_grad),所以在这里没有返回值。
            grad_weight = None
        else:
            grad_weight = grad_output.t().matmul(total_input)
        # 如果使用偏置,则计算关于偏置的梯度。
        grad_bias = grad_output.sum(dim=0) if use_bias else None

        # 如果启用了序列并行,等待上述操作完成,并返回计算得到的梯度。
        if ctx.sequence_parallel:
            handle.wait()
            return sub_grad_input, grad_weight, grad_bias, None, None, None

        # 如果启用了异步all-reduce,等待all-reduce操作完成。
        if ctx.async_grad_allreduce:
            handle.wait()

        return grad_input, grad_weight, grad_bias, None, None, None

可以看到gradient_accumulation_fusion这个优化作用于Linear层中对weight求梯度的时候,调用了apex库提供的2个fuse cuda kernel原地更新了weight的梯度。

0x2. fused_weight_gradient_mlp_cuda 实现

fused_weight_gradient_mlp_cuda接口分别为float32和float16/bfloat16提供了2个cuda kernel实现,我们先看一下上层的接口。(https://github.com/NVIDIA/apex/blob/master/csrc/megatron/fused_weight_gradient_dense.cpp)

// 定义了一个名为 wgrad_gemm_accum_fp32_cuda_stub 的函数原型。这是一个CUDA C++函数,
// 用于处理float32数据类型的权重梯度累积。该函数接受三个at::Tensor参数:
// input_2d, d_output_2d, 和 d_weight。
void wgrad_gemm_accum_fp32_cuda_stub(
  at::Tensor &input_2d,
  at::Tensor &d_output_2d,
  at::Tensor &d_weight
);

// 定义了一个名为 wgrad_gemm_accum_fp16_cuda_stub 的函数原型,与上面的函数类似,
// 但它是为float16数据类型设计的。
void wgrad_gemm_accum_fp16_cuda_stub(
  at::Tensor &input_2d,
  at::Tensor &d_output_2d,
  at::Tensor &d_weight
);

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
    m.def("wgrad_gemm_accum_fp32", &wgrad_gemm_accum_fp32_cuda_stub, "wgrad gemm accum in fp32");
    m.def("wgrad_gemm_accum_fp16", &wgrad_gemm_accum_fp16_cuda_stub, "wgrad gemm accum in fp16");
}

接下来解析一下wgrad_gemm_accum_fp32这个kernel,对应 https://github.com/NVIDIA/apex/blob/master/csrc/megatron/fused_weight_gradient_dense_cuda.cu 这个文件。


// 这个函数是一个封装了NVIDIA cuBLAS库中的cublasGemmEx函数的C++函数,
// 专门用于执行BFloat16(BF16)的矩阵乘法(GEMM)操作。
// 函数的名称为gemmex_wrapper,它的设计意图是提供一个简单的接口,
// 使得PyTorch可以方便地利用cuBLAS中的高效GEMM操作,特别是当使用BFloat16数据类型时。
// BF16 Tensor core wrapper around cublas GEMMEx
void gemmex_wrapper(
    cublasHandle_t handle, // cuBLAS库的句柄,用于管理cuBLAS调用。
    cublasOperation_t transa, 
    cublasOperation_t transb, // 这两个参数描述了两个输入矩阵A和B是否需要转置。
    // 定义了矩阵A, B和输出矩阵C的维度。具体来说,矩阵A的维度为m x k,
    // 矩阵B的维度为k x n,输出矩阵C的维度为m x n。
    int m,
    int n,
    int k,
    const float* alpha, // 标量系数,用于计算alpha * A * B。
    at::BFloat16* A, // 输入矩阵A,它们都是BFloat16数据类型。
    int lda, //  这个参数是矩阵A的leading dim,通常与矩阵的行数相同。
    at::BFloat16* B,
    int ldb,
    const float* beta, // 标量系数,用于计算beta * C。
    float* C, // 输出矩阵C,它是float数据类型。
    int ldc) { // 矩阵C的leading 维度,通常与矩阵C的行数相同。
  // 使用TORCH_CUDABLAS_CHECK宏调用了cublasGemmEx函数。这是cuBLAS库中用于执行混合精度矩阵乘法的函数。
  // cublasGemmEx函数的参数主要用于描述输入和输出矩阵的属性,以及要执行的具体操作。
  // 在这里,输入矩阵A和B都是BFloat16数据类型,而输出矩阵C是float数据类型。
  // CUDA_R_16BF和CUDA_R_32F是枚举值,用于描述矩阵的数据类型。
  // CUBLAS_GEMM_DEFAULT_TENSOR_OP是一个枚举值,指示cuBLAS使用默认的Tensor Core操作来执行GEMM。
  TORCH_CUDABLAS_CHECK(cublasGemmEx(
      handle,
      transa,
      transb,
      m,
      n,
      k,
      alpha,
      A,
      CUDA_R_16BF,
      lda,
      B,
      CUDA_R_16BF,
      ldb,
      beta,
      C,
      CUDA_R_32F,
      ldc,
      CUDA_R_32F,
      CUBLAS_GEMM_DEFAULT_TENSOR_OP));
}

// 类似上面的函数,用于执行FP16的矩阵乘法
// FP16 Tensor core wrapper around cublas GEMMEx
void gemmex_wrapper(
    cublasHandle_t handle,
    cublasOperation_t transa,
    cublasOperation_t transb,
    int m,
    int n,
    int k,
    const float* alpha,
    at::Half* A,
    int lda,
    at::Half* B,
    int ldb,
    const float* beta,
    float* C,
    int ldc) {
  TORCH_CUDABLAS_CHECK(cublasGemmEx(
      handle,
      transa,
      transb,
      m,
      n,
      k,
      alpha,
      A,
      CUDA_R_16F,
      lda,
      B,
      CUDA_R_16F,
      ldb,
      beta,
      C,
      CUDA_R_32F,
      ldc,
      CUDA_R_32F,
      CUBLAS_GEMM_DEFAULT_TENSOR_OP));
}

// 类似上面的函数,用于执行FP32的矩阵乘法
// FP32 wrapper around cublas GEMMEx
void gemmex_wrapper(
    cublasHandle_t handle,
    cublasOperation_t transa,
    cublasOperation_t transb,
    int m,
    int n,
    int k,
    const float *alpha,
    float *A,
    int lda,
    float *B,
    int ldb,
    const float *beta,
    float *C,
    int ldc) {
  TORCH_CUDABLAS_CHECK(cublasGemmEx(
      handle,
      transa,
      transb,
      m,
      n,
      k,
      alpha,
      A,
      CUDA_R_32F,
      lda,
      B,
      CUDA_R_32F,
      ldb,
      beta,
      C,
      CUDA_R_32F,
      ldc,
      CUDA_R_32F,
      CUBLAS_GEMM_DEFAULT_TENSOR_OP));
}

// 这个函数wgrad_gemm_accum_fp32_cuda是一个模板函数,用于在CUDA上执行累加的权重梯度计算(矩阵乘法)。
// 它使用了前面提到的gemmex_wrapper函数,该函数是NVIDIA cuBLAS库中的cublasGemmEx函数的封装,
// 用于执行高效的矩阵乘法。
template <typename T>
void wgrad_gemm_accum_fp32_cuda(T *input, T *d_output, float *d_weight, int in_dim, int hidden_dim, int out_dim) {
    // 获取当前CUDA cuBLAS句柄。
    cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
    // 获取CUDA Stream。
    cudaStream_t stream;
    // 从cuBLAS句柄获取当前CUDA流。
    cublasGetStream(handle, &stream);
    // 定义矩阵乘法的标量系数,用于计算alpha * A * B + beta * C。
    const float alpha = 1.0;
    const float beta  = 1.0;

    // 使用CUBLAS_OP_N和CUBLAS_OP_T作为参数,表示输入矩阵不需要转置,但d_output矩阵需要转置。
    // 使用输入矩阵input和输出矩阵的梯度d_output作为输入,将结果存储在权重梯度d_weight中。
    gemmex_wrapper(
        handle,
        CUBLAS_OP_N,
        CUBLAS_OP_T,
        in_dim,
        out_dim,
        hidden_dim,
        &alpha,
        input,
        in_dim,
        d_output,
        out_dim,
        &beta,
        d_weight,
        in_dim);
}

// 这是为数据类型at::Half(即半精度浮点型,也称为FP16)显式实例化的wgrad_gemm_accum_fp32_cuda函数。
// 使用此数据类型的版本,可以进行更快速的计算,尤其是在支持FP16计算的硬件上。
template void wgrad_gemm_accum_fp32_cuda<at::Half>(at::Half *input, at::Half *d_output, float *d_weight, int in_dim, int hidden_dim, int out_dim);
template void wgrad_gemm_accum_fp32_cuda<at::BFloat16>(at::BFloat16 *input, at::BFloat16 *d_output, float *d_weight, int in_dim, int hidden_dim, int out_dim);
template void wgrad_gemm_accum_fp32_cuda<float>(float *input, float *d_output, float *d_weight, int in_dim, int hidden_dim, int out_dim);

// 这个函数名为wgrad_gemm_accum_fp32_cuda_stub,从名字中可以看出这是一个为CUDA定义的存根函数。
// 它处理输入的张量,调整它们的维度,然后调用对应的CUDA模板函数来完成具体的操作。
void wgrad_gemm_accum_fp32_cuda_stub(
  at::Tensor &input,
  at::Tensor &d_output,
  at::Tensor &d_weight
) {
    at::Tensor input_2d, d_output_2d;
    // input tensor: collapse to the first dim
    auto in_sizes = input.sizes();
    // 如果input张量的维度大于2,它将最后一个维度以外的所有维度折叠为第一个维度,
    // 使其成为一个2D张量input_2d。否则,它将使用原始input张量。
    if (input.dim() > 2) {
        input_2d = input.view({-1, in_sizes[in_sizes.size() - 1]});
    } else {
        input_2d = input;
    }
    // d_output tensor: collapse to the first dim
    // 类似地,如果d_output张量的维度大于2,它也会进行同样的维度转换。
    // 否则,它会使用原始的d_output张量。
    auto d_out_sizes = d_output.sizes();
    if (d_output.dim() > 2) {
        d_output_2d = d_output.view({-1, d_out_sizes[d_out_sizes.size() - 1]});
    } else {
        d_output_2d = d_output;
    }

    // hidden_dim是input_2d的第一个维度的大小。
    const int hidden_dim = input_2d.size(0);
    // in_dim是input_2d的第二个维度的大小。
    const int in_dim = input_2d.size(1);
    // out_dim是d_weight的第一个维度的大小。
    const int out_dim = d_weight.size(0);

    // 使用DISPATCH_FLOAT_HALF_AND_BFLOAT宏来基于input_2d的数据类型调用相应的函数。
    // 这意味着,根据输入数据的数据类型(浮点、半精度或BFloat16),
    // 它将选择正确的版本的wgrad_gemm_accum_fp32_cuda函数进行调用。
    DISPATCH_FLOAT_HALF_AND_BFLOAT(input_2d.scalar_type(), 0, "wgrad_gemm_accum_fp32",
        wgrad_gemm_accum_fp32_cuda<scalar_t_0>(
            input_2d.data_ptr<scalar_t_0>(),
            d_output_2d.data_ptr<scalar_t_0>(),
            d_weight.data_ptr<float>(),
            in_dim,
            hidden_dim,
            out_dim);
    );
}

注意,在Kernel中这里会将当前的结果累加到先前计算的梯度上,所有这些都在一个操作中完成,这是fuse的思想,可以避免多次访问global memory提升算子的带宽。

0x3. 总结

不需要总结,文本很短。

您可能感兴趣的与本文相关的镜像

PyTorch 2.9

PyTorch 2.9

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

Transformer发轫于NLP(自然语言处理),并跨界应用到CV(计算机视觉)领域。 Swin Transformer是基于Transformer的计算机视觉骨干网,在图像分类、目标检测、实例分割、语义分割等多项下游CV应用中取得了SOTA的性能。该项工作也获得了ICCV 2021顶会最佳论文奖。 本课程将手把手地教大家使用labelImg标注和使用Swin Transformer训练自己的数据集。  本课程将介绍Transformer及在CV领域的应用、Swin Transformer的原理。 课程以多目标检测(足球和梅西同时检测)为例进行Swin Transformer实战演示。 课程在Windows和Ubuntu系统上分别做项目演示。包括:安装软件环境、安装Pytorch、安装Swin-Transformer-Object-Detection、标注自己的数据集、准备自己的数据集(自动划分训练集和验证集)、数据集格式转换(Python脚本完成)、修改配置文件、训练自己的数据集、测试训练出的网络模型、性能统计、日志分析。  相关课程: 《Transformer原理与代码精讲(PyTorch)》https://edu.youkuaiyun.com/course/detail/36697《Transformer原理与代码精讲(TensorFlow)》https://edu.youkuaiyun.com/course/detail/36699《ViT(Vision Transformer)原理与代码精讲》https://edu.youkuaiyun.com/course/detail/36719《DETR原理与代码精讲》https://edu.youkuaiyun.com/course/detail/36768《Swin Transformer实战目标检测:训练自己的数据集》https://edu.youkuaiyun.com/course/detail/36585《Swin Transformer实战实例分割:训练自己的数据集》https://edu.youkuaiyun.com/course/detail/36586《Swin Transformer原理与代码精讲》 https://download.youkuaiyun.com/course/detail/37045
/******************************************************************************* Function : device_general_list_config_add Description : add a new general device to device list config file Input : dev @ input general device pointer to a dev_node struct Output : N/A Return : DEVICE_OK @ success other @ failed *******************************************************************************/ int device_general_list_config_add(struct dev_node *dev) { DM_PRINT("save config [dev-manager]\n"); if (!dev) { DM_PRINT("param[*dev] is null\n"); return DEVICE_PARAM_ERR; } int err = DEVICE_OK; struct blob_buf b = {0}; blobmsg_buf_init(&b); /* search device item in config file */ void *section = blobmsg_open_table(&b, "device"); if (NULL == section) { DM_PRINT("create section failed\n"); err = DEVICE_OTHER_ERR; goto RTN; } blobmsg_add_string(&b, "deviceId", dev->devid); blobmsg_close_table(&b, section); char sname[64] = {}; if (!blobuci_get_sec_name_by_val(&b, "dev-manager", sname, sizeof(sname))) { strncpy(sname, "+device", sizeof(sname) - 1); } /* add item */ blobmsg_buf_init(&b); section = blobmsg_open_table(&b, sname); if (NULL == section) { DM_PRINT("create section failed\n"); err = DEVICE_OTHER_ERR; goto RTN; } blobmsg_add_string(&b, "model", dev->model); blobmsg_add_string(&b, "alias", dev->def_alias); blobmsg_add_string(&b, "avatar", dev->avatar); blobmsg_add_string(&b, "type", dev->type); blobmsg_add_string(&b, "category", dev->category); blobmsg_add_string(&b, "mac", dev->mac); blobmsg_add_string(&b, "devid", dev->devid); blobmsg_add_string(&b, "ip", dev->ip); blobmsg_add_u32(&b, "ai_camera_support", dev->ai_camera_support); blobmsg_add_u32(&b, "general_camera", dev->general_camera); blobmsg_add_string(&b, "audio_cfg", dev->audio_cfg); blobmsg_close_table(&b, section); blobuci_save(&b, "dev-manager"); RTN: blob_buf_free(&b); return err; } /******************************************************************************* Function : device_list_add_general Description : add a new general device to device list Input : in @ input data pointer to blob_attr struct Output : N/A Return : DEVICE_OK @ success other @ failed *******************************************************************************/ int device_list_add_general(struct blob_attr *in) { int err = DEVICE_OK; DEV_NODE *dev = NULL; char *resolution = NULL; int update_info = 0; int storage_media_status = -1; if (!in) { DBG_ERR("param[*in] is null\n"); err = DEVICE_PARAM_ERR; return err; } struct blobmsg ( blobmsg_table device_info, blobmsg_table config, ) (arg, in, false); if (!arg.device_info) { DBG_ERR("input[msg] no device_info\n"); err = DEVICE_PARAM_NOT_ENOUTH; return err; } struct blobmsg ( blobmsg_string model, blobmsg_string alias, blobmsg_string avatar, blobmsg_string type, blobmsg_string mac, blobmsg_string device_id, blobmsg_string ipaddr, blobmsg_string network_mode, blobmsg_string auth_type, blobmsg_int32 ai_camera_support, blobmsg_string storage_media_status, blobmsg_string resolution, blobmsg_int32 storage_schedule_status, blobmsg_string hub_storage_enabled, blobmsg_table record_plan, blobmsg_string wifi_backup_enabled, blobmsg_string backup, blobmsg_string audio_cfg, ) (info, arg.device_info, false); if (!info.model || !info.alias || !info.type || !info.mac || !info.device_id || !info.ipaddr || !info.ai_camera_support) { DBG_ERR("input[device_info] param error\n"); err = DEVICE_PARAM_NOT_ENOUTH; return err; } dev = device_list_search(arg.device_info); if (!dev) { /* check legal device number, including battery and general camera */ if (device_total_number_get() >= DEVMGR_MAX_DEV_NUM) { DBG_ERR("no more devices can be added, max:%d\n", DEVMGR_MAX_DEV_NUM); err = DEVICE_MAX_DEV_NUM; return err; } dev = malloc(sizeof(DEV_NODE)); memset(dev, 0, sizeof(DEV_NODE)); list_add_tail(&dev->list, &g_devlist.list); } else { if (dev->hub_storage_enable == 0) { if (device_total_number_get() >= DEVMGR_MAX_DEV_NUM) { DBG_ERR("no more devices can be added, max:%d\n", DEVMGR_MAX_DEV_NUM); err = DEVICE_MAX_DEV_NUM; return err; } } update_info = 1; } /* the request may be retransmited, update info */ strncpy(dev->model, blobmsg_get_string(info.model), sizeof(dev->model) - 1); strncpy(dev->def_alias, blobmsg_get_string(info.alias), sizeof(dev->def_alias) - 1); strncpy(dev->type, blobmsg_get_string(info.type), sizeof(dev->type) - 1); strncpy(dev->category, dev->type, sizeof(dev->category)); strncpy(dev->mac, blobmsg_get_string(info.mac), sizeof(dev->mac) - 1); strncpy(dev->devid, blobmsg_get_string(info.device_id),sizeof(dev->devid) - 1); strncpy(dev->ip, blobmsg_get_string(info.ipaddr), sizeof(dev->ip) - 1); dev->ai_camera_support = blobmsg_get_u32(info.ai_camera_support); if(info.avatar){ strncpy(dev->avatar, blobmsg_get_string(info.avatar), sizeof(dev->avatar) - 1); } if (info.network_mode) { strncpy(dev->network_mode, blobmsg_get_string(info.network_mode), sizeof(dev->network_mode) - 1); } if (info.auth_type) { strncpy(dev->auth_type, blobmsg_get_string(info.auth_type), sizeof(dev->auth_type) - 1); } if (info.storage_media_status) { storage_media_status = blobmsg_get_u32(info.storage_media_status); } if (info.resolution) { resolution = blobmsg_get_string(info.resolution); strncpy(dev->resolution, resolution, sizeof(dev->resolution) - 1); } if (info.storage_schedule_status) { dev->storage_schedule_status = blobmsg_get_u32(info.storage_schedule_status); } if (info.hub_storage_enabled) { if (!strncmp(blobmsg_get_string(info.hub_storage_enabled), "on", strlen("on"))) { dev->hub_storage_enable = 1; } else { dev->hub_storage_enable = 0; } } if (info.wifi_backup_enabled) { if (!strncmp(blobmsg_get_string(info.wifi_backup_enabled), "on", strlen("on"))) { dev->wifi_backup_enable = 1; } else { dev->wifi_backup_enable = 0; } } if (info.backup) { strncpy(dev->backup_wifi, blobmsg_get_string(info.backup), sizeof(dev->backup_wifi) - 1); } if (info.audio_cfg) { strncpy(dev->audio_cfg, blobmsg_get_string(info.audio_cfg), sizeof(dev->audio_cfg) - 1); } dev->general_camera = 1; if (update_info) { /* update config*/ if (info.wifi_backup_enabled) { devmgr_general_wifi_backup_set(dev, dev->wifi_backup_enable, dev->backup_wifi); } if (info.hub_storage_enabled) { devmgr_general_hub_storage_set(dev, storage_media_status, dev->hub_storage_enable, resolution, dev->storage_schedule_status); } } else { /* useless */ INIT_LIST_HEAD(&dev->wake_list); INIT_LIST_HEAD(&(dev->metry_evt.event.list)); blobmsg_buf_init(&dev->new_fw_info); devmgr_sub_dev_config_delete(dev); if (arg.config) { struct blob_attr *cur; int rem; blobmsg_for_each_attr(cur, arg.config, rem) { if (BLOBMSG_TYPE_TABLE != blobmsg_type(cur)) { continue; } char path[256] = {}; const char *module = blobmsg_name(cur); snprintf(path, sizeof(path), "/etc/config/devmgr/%s/%s/%s", dev->model, dev->devid, module); devmgr_config_path_prepare(dev->model, dev->devid, module); struct blob_buf b = {0}; struct blob_attr *section; int sec_rem; blobmsg_for_each_attr(section, cur, sec_rem) { if (BLOBMSG_TYPE_TABLE != blobmsg_type(section)) { continue; } blob_buf_init(&b, 0); blobmsg_add_blob(&b, section); blobuci_save(&b, path); } } } #ifdef AI_ENHANCE devmgr_AIEnhance_FD_default_info_set(dev); devmgr_AIEnhance_FD_default_usrcfg_set(dev); #endif } device_general_list_config_add(dev); master_send_add_general_device(dev); return err; } void device_general_list_get(struct blob_buf *out, struct blob_attr *msg) { if (!out) { DM_PRINT("param[*out] is null\n"); return; } struct dev_node *entry, *tmp; void *array = NULL; int child_num = 0; int plan_24h_rec_num = 0; struct blob_attr *dev_list_req = NULL, *cur = NULL; int rem = 0; int need_add = 0; int time_now = devmgr_time(NULL); blobmsg_add_u32(out, "max_bound", MAX_DEV_NUM); blobmsg_add_u32(out, "max_24h_record_dev", MAX_24H_RECORD_NUMBER); array = blobmsg_open_array(out, "system"); if (!array) { DM_PRINT("create array failed\n"); return; } if (msg) { struct blobmsg ( blobmsg_array dev_list, ) (req, msg, false); if (req.dev_list) { dev_list_req = req.dev_list; } } list_for_each_entry_safe(entry, tmp, &g_devlist.list, list) { if (!entry->general_camera) { if (entry->migrate_mode == MIGRATE_MODE_IN && entry->migrate_stat != MIGRATE_IN_STAT_DONE) { continue; } if (entry->device_state == DEV_STAT_ILLEGAL || entry->device_state == DEV_STAT_NOT_VERIFIED) { if (entry->onboard_time + NEW_DEV_FLUSH_TIME < time_now) { DBG_NOTICE("Device[%s] is illegal or unverified, more than 1800s, delete it\n", entry->devid); device_list_delete(entry); } continue; } if (entry->onboard_state != ONBOARD_FINISH) { if (entry->onboard_time + ONBOARDING_FLUSH_TIME < time_now) { DBG_NOTICE("Device[%s] onboarding step is not finished , more than 300s, delete it\n", entry->devid); device_list_delete(entry); } continue; } if (entry->device_state == DEV_STAT_DELETE) { continue; } child_num++; continue; } else if (entry->hub_storage_enable) { child_num++; if (entry->plan_24h_record || entry->stat_24h_record) { plan_24h_rec_num++; } } if (dev_list_req) { need_add = 0; blobmsg_for_each_attr(cur, dev_list_req, rem) { struct blobmsg ( blobmsg_string mac, ) (info, cur, false); if (info.mac && !strcmp(blobmsg_get_string(info.mac), entry->mac)) { need_add = 1; break; } } if (!need_add) { continue; } } void *table = blobmsg_open_table(out, NULL); if (!table) { DM_PRINT("create table failed\n"); continue; } blobmsg_add_string(out, "model", entry->model); blobmsg_add_string(out, "category", entry->category); blobmsg_add_string(out, "alias", entry->def_alias); blobmsg_add_string(out, "type", entry->type); blobmsg_add_string(out, "mac", entry->mac); blobmsg_add_string(out, "device_id", entry->devid); blobmsg_add_string(out, "ipaddr", entry->ip); blobmsg_add_u32(out, "general_camera", entry->general_camera); blobmsg_add_string(out, "network_mode", entry->network_mode); blobmsg_add_u8(out, "wifi_backup_enabled", entry->wifi_backup_enable); if (entry->wifi_backup_enable) { blobmsg_add_string(out, "backup_wifi", entry->backup_wifi); } blobmsg_add_u8(out, "hub_storage_enabled", entry->hub_storage_enable); blobmsg_add_u32(out,"ai_camera_support", entry->ai_camera_support); blobmsg_add_string(out, "avatar", entry->avatar); blobmsg_add_u32(out, "always_on", entry->always_on); #ifdef AI_ENHANCE devmgr_AIEnhance_Config_Load(entry, out); #endif /* for mediacenter init storage */ blobmsg_add_u32(out, "storage_enable", entry->hub_storage_enable); if (entry->resolution[0]) { blobmsg_add_string(out, "resolution", entry->resolution); } blobmsg_add_u32(out, "storage_schedule_status", entry->storage_schedule_status); blobmsg_add_string(out, "audio_cfg", entry->audio_cfg); if (entry->plan_24h_record || entry->stat_24h_record) { blobmsg_add_u8(out, "plan_24h_record", true); } else { blobmsg_add_u8(out, "plan_24h_record", false); } blobmsg_close_table(out, table); } blobmsg_close_array(out, array); /* including battery and general camera */ blobmsg_add_u32(out, "current_bound", child_num); blobmsg_add_u32(out, "cur_24h_record_dev", plan_24h_rec_num); return; } /******************************************************************************* Function : device_list_config_del Description : delete a device from device list config file Input : dev @ input device pointer to a dev_node struct Output : N/A Return : DEVICE_OK @ success other @ failed *******************************************************************************/ int device_list_config_del(struct dev_node *dev) { if (!dev || strlen(dev->devid) == 0) { DM_PRINT("dev or devid is null\n"); return DEVICE_PARAM_ERR; } int err = DEVICE_OK; struct blob_buf b = {0}; blob_buf_init(&b, 0); void *section = blobmsg_open_table(&b, "device"); if (NULL == section) { DM_PRINT("create section failed\n"); err = DEVICE_OTHER_ERR; goto RTN; } blobmsg_add_string(&b, "devid", dev->devid); blobmsg_close_table(&b, section); char sname[64] = {}; if (!blobuci_get_sec_name_by_val(&b, "dev-manager", sname, sizeof(sname))) { DM_PRINT("no such device item in config[dev-manager]\n"); err = DEVICE_NO_SUCH_CFG_ITEM; goto RTN; } DBG_NOTICE("delete device[%s] from dev-manager config\n", dev->devid); blob_buf_free(&b); memset(&b, 0, sizeof(b)); blobmsg_buf_init(&b); char sname_new[64 + 1] = {}; snprintf(sname_new, sizeof(sname_new), "-%s", sname); blobmsg_add_string(&b, "hello", sname_new); blobuci_save(&b, "dev-manager"); RTN: blob_buf_free(&b); return err; } void device_list_upgrade_msg_push_event(void *dev) { if (!dev) { DBG_ERR("params null\n"); return; } char path[256] = {0}; struct blob_buf b = {0}; struct blob_buf bBuf = {0}; void *table = NULL; void *array = NULL; DEV_NODE *device = (DEV_NODE *)dev; /*add msg_push_event_list*/ memset(path, 0, 256); snprintf(path, 256, "/etc/config/devmgr/%s/%s/motionDetect", device->model, device->devid); blob_buf_init(&b, 0); blob_buf_init(&bBuf, 0); table = blobmsg_open_table(&b, "msg_push_event"); array = blobmsg_open_array(&b, "msg_push_event_list"); devmgr_msg_push_event_check(dev, "motion_detection", &b); blobuci_load(&bBuf, path); struct blobmsg ( blobmsg_section person_detect, blobmsg_section vehicle_detect, blobmsg_section pet_detect, blobmsg_section package_detect, blobmsg_section glass_detect, blobmsg_section alarm_detect, blobmsg_section meow_detect, blobmsg_section bark_detect, blobmsg_section tamper_detect, blobmsg_section sound_detect, blobmsg_section hpr_detect, blobmsg_section linecrossing_detect, blobmsg_section expression_detect, blobmsg_section intrusion_detect, ) (msec, &bBuf); if (msec.person_detect) { devmgr_msg_push_event_check(dev, "people_detection", &b); } if (msec.vehicle_detect) { devmgr_msg_push_event_check(dev, "vehicle_detection", &b); } if (msec.pet_detect) { devmgr_msg_push_event_check(dev, "pet_detection", &b); } if (msec.package_detect) { devmgr_msg_push_event_check(dev, "package_detection", &b); } if (msec.glass_detect) { devmgr_msg_push_event_check(dev, "glass_detection", &b); } if (msec.alarm_detect) { devmgr_msg_push_event_check(dev, "alarm_detection", &b); } if (msec.meow_detect) { devmgr_msg_push_event_check(dev, "meow_detection", &b); } if (msec.bark_detect) { devmgr_msg_push_event_check(dev, "bark_detection", &b); } if (msec.tamper_detect) { devmgr_msg_push_event_check(dev, "tamper_detection", &b); } if (msec.sound_detect) { devmgr_msg_push_event_check(dev, "sound_detection", &b); } if (msec.hpr_detect) { devmgr_msg_push_event_check(dev, "hpr_detection", &b); } if (msec.linecrossing_detect) { devmgr_msg_push_event_check(dev, "linecrossing_detection", &b); } if (msec.expression_detect) { devmgr_msg_push_event_check(dev, "expression_detection", &b); } if (msec.intrusion_detect) { devmgr_msg_push_event_check(dev, "intrusion_detection", &b); } blobmsg_close_array(&b, array); blobmsg_close_table(&b, table); devmgr_config_update("msg_push", b.head, dev, 0, CFG_SYNC_SYNC); blob_buf_free(&bBuf); blob_buf_free(&b); return; } static void devmgr_battery_statistic_tmo_cb(struct uloop_timeout *tm) { /* in this timer, we should update bat_stat.next_id and start a new begin */ time_t cur_time = 0, next_time = 0; time_t sys_up_time = 0; struct tm *utc_tm = NULL, *local_tm = NULL; int remain = 0, usage = 0; DEV_NODE *dev = container_of(tm, DEV_NODE, bat_stat_tmr); struct bat_statistic_node *node = &dev->bat_stat.node[dev->bat_stat.id]; sys_up_time = devmgr_time(NULL); /* update last node info */ if (dev->bat_charge_start) { node->bat_charge_time += (sys_up_time - dev->bat_charge_start); dev->bat_charge_start = sys_up_time; } if (dev->bat_standby_start) { time_t run_time = sys_up_time - dev->bat_standby_start; device_battery_consumption_update(dev, DEV_BAT_CONSUMP_STANDBY, run_time); dev->bat_standby_start = sys_up_time; } if (dev->bat_active_start) { time_t run_time = sys_up_time - dev->bat_active_start; device_battery_consumption_update(dev, DEV_BAT_CONSUMP_ACTIVE, run_time); node->wakeup_time += run_time; dev->bat_active_start = sys_up_time; } remain = node->bat_remain; usage = (remain > dev->bat_percent) ? (remain - dev->bat_percent) : 0; node->bat_remain = dev->bat_percent; node->bat_usage += usage; node->statistic_time = sys_up_time - dev->bat_statistic_start; /* use a new node */ if (dev->bat_stat.num < (ONE_DAY_HOURS*DEV_MAX_STATISTIC_DAY)) { dev->bat_stat.num++; } dev->bat_stat.id = (dev->bat_stat.id+1)%(ONE_DAY_HOURS*DEV_MAX_STATISTIC_DAY); node = &dev->bat_stat.node[dev->bat_stat.id]; /* calc the seconds to the next hour, the next_time should be 3600 */ cur_time = time(NULL); utc_tm = gmtime(&cur_time); next_time = (60 - utc_tm->tm_sec) + (59 - utc_tm->tm_min) * 60 + 10; dev->bat_statistic_start = sys_up_time; memset(node, 0, sizeof(bat_statistic_node_t)); node->timestamp = cur_time; node->bat_remain = dev->bat_percent; uloop_timeout_set(&dev->bat_stat_tmr, next_time * 1000); /* save config */ local_tm = localtime(&cur_time); if (local_tm->tm_hour == 0) { DBG_NOTICE("save bat_statistic config\n"); devmgr_battery_statistic_info_config_set(dev); devmgr_sub_dev_config_backup(dev, "batt_statistic"); } } void devmgr_battery_statistic_start(DEV_NODE *dev) { time_t cur_time = 0, last_time = 0, next_time = 0; struct tm *utc_tm = NULL; struct tm tm1, tm2; struct bat_statistic_node *node = NULL; if (dev) { node = &dev->bat_stat.node[dev->bat_stat.id]; /* calc the seconds to the next hour */ cur_time = time(NULL); last_time = node->timestamp + node->statistic_time; if (cur_time >= last_time) { utc_tm = gmtime(&cur_time); next_time = (60-utc_tm->tm_sec) + (59-utc_tm->tm_min)*60 + 10; dev->bat_stat_ready = 1; DBG_NOTICE("bat statistic ready, next_time:%ld\n", next_time); } else { utc_tm = gmtime(&last_time); next_time = (60-utc_tm->tm_sec) + (59-utc_tm->tm_min)*60 + 10 + (last_time-cur_time); dev->bat_stat_ready = 0; DBG_NOTICE("bat statistic not ready, next_time:%ld\n", next_time); } localtime_r(&cur_time, &tm1); localtime_r(&last_time, &tm2); if (cur_time >= last_time && (tm1.tm_year!=tm2.tm_year || tm1.tm_mon!=tm2.tm_mon || tm1.tm_mday!=tm2.tm_mday || tm1.tm_hour!=tm2.tm_hour)) { DBG_NOTICE("current time is not in the range of last statistic hour, create new node\n"); /* use a new node */ dev->bat_stat.id = (dev->bat_stat.id+1) % (ONE_DAY_HOURS*DEV_MAX_STATISTIC_DAY); node = &dev->bat_stat.node[dev->bat_stat.id]; if (dev->bat_stat.num < (ONE_DAY_HOURS*DEV_MAX_STATISTIC_DAY)) { dev->bat_stat.num++; } /* a new start from current utc time */ dev->bat_statistic_start = devmgr_time(NULL); memset(node, 0, sizeof(bat_statistic_node_t)); node->timestamp = cur_time; if (dev->bat_percent) { node->bat_remain = dev->bat_percent; } } dev->bat_stat_tmr.cb = devmgr_battery_statistic_tmo_cb; uloop_timeout_cancel(&dev->bat_stat_tmr); uloop_timeout_set(&dev->bat_stat_tmr, next_time * 1000); } } void devmgr_battery_statistic_stop(void) { struct dev_node *entry; list_for_each_entry(entry, &g_devlist.list, list) { if (!entry->general_camera) { devmgr_battery_statistic_info_config_set(entry); devmgr_sub_dev_config_backup(entry, "batt_statistic"); } } }
09-18
/************************************************************ * Copyright(c) 2019-2021 TP-Link Systems Inc. * * file general_camera_manage.c * * Note * * author Li Xiangcun * date 28Aug23 * history V1.0 Create file ************************************************************/ #include <sys/stat.h> #include <sys/types.h> #include <lib-tpcom/lib_tpcom.h> #include "udsd.h" #include "general_camera_manage.h" #include "udsd_auth.h" #include "udsd_session.h" /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #define SESSION_CLIENT_PATH "/tmp/luci-client-sessions" #define LOGIN_RETRY_INTERVAL (3 * 1000) #define UDSD_LOGIN_POST_CAMERA_HEAD_REQ \ "POST %s HTTP/1.1\r\n"\ "Connection: keep-alive\r\n"\ "User-Agent: Hub\r\n"\ "Content-Type: application/json\r\n"\ "Content-Length: %d\r\n"\ "\r\n" #define UDSD_REQUEST_POST_CAMERA_HEAD_REQ \ "POST %s HTTP/1.1\r\n"\ "Connection: keep-alive\r\n"\ "User-Agent: Hub\r\n"\ "Content-Type: application/json\r\n"\ "Content-Length: %d\r\n"\ "tapo_tag: %s\r\n"\ "seq: %d\r\n"\ "\r\n" #define UDSD_CAMERA_REQUEST_CONTENT \ "{\"method\":\"multipleRequest\", \"params\":{\"requests\":[{\"method\":\"%s\",\"params\":{\"%s\":{\"%s\":%s}}}]}}\r\n" #ifndef MAX_24H_RECORD_NUMBER #define MAX_24H_RECORD_NUMBER 4 #endif /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ typedef enum { LOGIN_INIT = 0, LOGIN_FIRST_POST, LOGIN_FINISH, } LOGIN_STATE; struct udsd_camera_session { struct ubus_app *app; struct ubus_call *call; struct tpsocket_fd *sock; char *dev_ip; char *method; /* method name in interface */ char *domain; /* domain name in interface */ char *req; char *json_data; /* specific content of the request */ /* login_cnt: * record the number of POST times when sync to camera, limited to two. * not retry immediately if login failed to avoid entering a loop. */ int login_cnt; LOGIN_STATE login_state; bool send_req; /* if sent request already */ int response; /* response code from camera, -1:no response, 0:success, other:failed */ struct uloop_timeout retry_timer; /* if sync failed, retry later */ int retry_times; /* times of sycn retry */ struct long_conn_session *client_session; }; /**************************************************************************************************/ /* VARIABLES */ /**************************************************************************************************/ struct list_head client_auth_info_list; extern struct list_head g_auth_info_list; /**************************************************************************************************/ /* FUNCTIONS */ /**************************************************************************************************/ static bool tpsocket_event_config_to_camera_secure(struct tpsocket_handler *handler, struct list_head *buf, int event); static struct tpsocket_fd* udsd_create_session_sock(struct udsd_camera_session *session); #ifdef PROTOCOL_SECURE bool udsd_send_request_to_camera(struct udsd_camera_session *session, char *path); #endif static int mac_format_change(const char *in, char *out, int out_len, int upcase) { /* target: AABBCCDDEEFF or aabbccddeeff */ int i = 0, j = 0, len = 0; int need_split = 0; len = strlen(in); if (len == 17) { need_split = 1; } else if (len == 12) { need_split = 0; } else { return -1; } for (i = 0; i < len; i++) { if (need_split && (i + 1) % 3 == 0) { if (in[i] == ':' || in[i] == '-') { continue; } else { return -1; } } else if (j < out_len) { if (in[i] >= '0' && in[i] <= '9') { out[j++] = in[i]; } else if (in[i] >= 'A' && in[i] <= 'F') { if (!upcase) { out[j++] = in[i] + 'a' - 'A'; } else { out[j++] = in[i]; } } else if (in[i] >= 'a' && in[i] <= 'f') { if (upcase) { out[j++] = in[i] + 'A' - 'a'; } else { out[j++] = in[i]; } } else { return -1; } } else { return -1; } } out[out_len - 1] = 0; return 0; } static void ubus_get_general_sysinfo_cb(struct ubus_app* app, struct blob_attr *msg, int ret, void *priv) { struct blob_buf *bBuf = (struct blob_buf *)priv; struct blob_attr *cur = NULL; int rem = 0; void *array = NULL; if (NULL == priv) { return; } if (msg) { struct blobmsg ( blobmsg_array system, blobmsg_int32 max_bound, blobmsg_int32 current_bound, blobmsg_int32 max_24h_record_dev, blobmsg_int32 cur_24h_record_dev, ) (arg, msg, false); if (arg.max_bound) { blobmsg_add_u32(bBuf, "max_bound", blobmsg_get_u32(arg.max_bound)); } if (arg.current_bound) { blobmsg_add_u32(bBuf, "current_bound", blobmsg_get_u32(arg.current_bound)); } if (arg.max_24h_record_dev) { blobmsg_add_u32(bBuf, "max_24h_record_dev", blobmsg_get_u32(arg.max_24h_record_dev)); } if (arg.cur_24h_record_dev) { blobmsg_add_u32(bBuf, "cur_24h_record_dev", blobmsg_get_u32(arg.cur_24h_record_dev)); } if (arg.system) { array = blobmsg_open_array(bBuf, "paired_general_device_list"); if (array) { blobmsg_for_each_attr(cur, arg.system, rem) { blobmsg_add_blob(bBuf, cur); } blobmsg_close_array(bBuf, array); } } } return; } #ifdef AIENHANCE_SUPPORT static void ubus_get_ai_result_cb(struct ubus_app * app, struct blob_attr * msg, int ret, void * priv) { if (msg && priv) { int *ai_hub_support = (int*)priv; struct blobmsg ( blobmsg_table AIEnhance, ) (resp, msg, false); if (resp.AIEnhance) { struct blobmsg ( blobmsg_int32 AI_support, ) (info, resp.AIEnhance, false); if (info.AI_support) { *ai_hub_support = blobmsg_get_u32(info.AI_support); } } } } #endif static void ubus_get_scan_device_cb(struct ubus_app* app, struct blob_attr *msg, int ret, void *priv) { struct blob_buf *bBuf = (struct blob_buf *)priv; if (NULL == priv) { return; } if (msg) { struct blobmsg ( blobmsg_bool active, blobmsg_array general_device_list, blobmsg_array unsupported_device_list, ) (arg, msg, false); if (arg.active) { blobmsg_add_string(bBuf, "scan_status", blobmsg_get_bool(arg.active) ? "scanning" : "idle"); } if (arg.general_device_list) { blobmsg_add_blob(bBuf, arg.general_device_list); } if (arg.unsupported_device_list) { blobmsg_add_blob(bBuf, arg.unsupported_device_list); } } return; } static void ubus_sync_AIEnhance_cb(struct ubus_app* app, struct blob_attr *msg, int ret, void *priv) { struct blob_buf *bBuf = (struct blob_buf *)priv; struct blob_attr *cur = NULL; int rem = 0; if (NULL == priv) { return; } if (msg) { blobmsg_for_each_attr(cur, msg, rem) { blobmsg_add_blob(bBuf, cur); } } return; } static void udsd_camera_session_free(struct udsd_camera_session *session, bool close_sock) { struct blob_buf b = {0}; if (!session) { return; } if (close_sock && session->sock) { session->sock->handler.priv = NULL; tpsocket_close(session->sock); } if (session->method) { free(session->method); session->method = NULL; } if (session->domain) { free(session->domain); session->domain = NULL; } if (session->json_data) { free(session->json_data); session->json_data = NULL; } if (session->dev_ip) { free(session->dev_ip); session->dev_ip = NULL; } if (session->req) { free(session->req); session->req = NULL; } session->app = NULL; if (session->call) { blobmsg_buf_init(&b); blobmsg_add_u32(&b, "error_code", session->response); ubus_app_reply(session->call, b.head); blob_buf_free(&b); } uloop_timeout_cancel(&session->retry_timer); free(session); session = NULL; return; } static void udsd_retry_timeout(struct uloop_timeout *tmo) { struct udsd_camera_session *session = container_of(tmo, struct udsd_camera_session, retry_timer); if (session) { session->retry_times++; session->response = -1; session->login_state = LOGIN_FINISH; udsd_create_session_sock(session); } return; } static struct udsd_camera_session* udsd_camera_session_new(char *dev_ip, const char *method, const char *domain, char *req, struct blob_attr *msg, struct ubus_app *svr_ubus_app, struct ubus_call *call) { struct udsd_camera_session *session = malloc(sizeof(struct udsd_camera_session)); if (!session) { return NULL; } memset(session, 0, sizeof(struct udsd_camera_session)); session->app = svr_ubus_app; session->call = call; session->dev_ip = strdup(dev_ip); session->method = strdup(method); session->domain = strdup(domain); session->req = strdup(req); session->json_data = blobmsg_format_json(msg, true); session->response = -1; /* no response */ session->login_state = LOGIN_FINISH; /* read token from file by default */ session->retry_timer.cb = udsd_retry_timeout; session->client_session = NULL; return session; } static void update_camera_token_file(const char *ip, const char *user_name, const char *group, const char *token) { if (!ip || !user_name || !group || !token) { DBG_ERR("param error\n"); return; } if (!is_directory(SESSION_CLIENT_PATH)) { mkdir(SESSION_CLIENT_PATH, 0700); } char sess_data[SESSION_DATA_MAX_LEN + 1] = {0}; char path[PATH_MAX + 1] = {0}; snprintf(sess_data, sizeof(sess_data), "%ld %s %s %s", get_uptime(), user_name, group, token); snprintf(path, sizeof(path), "%s/%s", SESSION_CLIENT_PATH, ip); DBG_DBG("path:%s\n", path); write_file_data(path, sess_data); return; } static struct tpsocket_fd* udsd_create_session_sock(struct udsd_camera_session *session) { if (!session || !session->dev_ip || !session->dev_ip[0]) { goto err; } struct tpsocket_fd *sock = NULL; char url[128] = {0}; char file_path[PATH_MAX + 1] = {0}; int rlen = 0; char sess_data[SESSION_DATA_MAX_LEN + 1] = {0}; char *user_name = NULL, *group = NULL, *token = NULL; struct tpsocket_fd *client_sock = NULL; if (session->client_session) { client_sock = session->client_session->client_sock; } if (session->login_state == LOGIN_FINISH) { /* get token from file */ snprintf(file_path, sizeof(file_path), "%s/%s", client_sock ? SESSION_PATH : SESSION_CLIENT_PATH, session->dev_ip); rlen = read_file_data(file_path, sess_data, sizeof(sess_data)); if (rlen <= 0) { DBG_NOTICE("do login first: %s!", session->dev_ip); } else { user_name = strchr(sess_data, ' '); if (user_name) { user_name++; group = strchr(user_name, ' '); } if (group) { group++; token = strchr(group, ' '); if (token) { token++; } } } } if (client_sock) { DBG_NOTICE("use long conn to send request to %s\n", session->dev_ip); if (token) { #ifdef PROTOCOL_SECURE snprintf(url, sizeof(url), "/stok=%s/ds", token); session->sock = client_sock; if (true == udsd_send_request_to_camera(session, url)) { DBG_NOTICE("success to send request through long conn\n"); udsd_camera_session_free(session, false); return client_sock; } #endif } else { DBG_ERR("get no token of long conn\n"); } DBG_ERR("fail to send request through long conn and close socket\n"); long_session_free(session->client_session); session->client_session = NULL; if (session->login_state == LOGIN_FINISH) { session->login_state = LOGIN_INIT; } snprintf(url, sizeof(url), "https://%s:443", session->dev_ip); } else { DBG_NOTICE("use short conn to send request to %s\n", session->dev_ip); if (token) { snprintf(url, sizeof(url), "https://%s:443/stok=%s/ds", session->dev_ip, token); } else { if (session->login_state == LOGIN_FINISH) { session->login_state = LOGIN_INIT; } snprintf(url, sizeof(url), "https://%s:443", session->dev_ip); } } struct tpsocket_handler config_to_camera = { .cb = tpsocket_event_config_to_camera_secure, .timeout = 3000, }; DBG_DBG("short conn url:%s\n", url); if (!(sock = tpsocket_from_url(url, &config_to_camera))) { goto err; } sock->handler.priv = session; session->response = -1; session->sock = sock; return sock; err: udsd_camera_session_free(session, true); return NULL; } int udsd_connect_camera(char *dev_ip, const char *method, const char *domain, char *req, struct blob_attr *msg, struct long_conn_session *client_session, struct ubus_app *svr_ubus_app, struct ubus_call *call) { if (!dev_ip || !dev_ip[0] || !method || !domain || !req) { DBG_ERR("dev ip null\n"); return -1; } struct udsd_camera_session *session = NULL; session = udsd_camera_session_new(dev_ip, method, domain, req, msg, svr_ubus_app, call); if (!session) { DBG_ERR("create session error\n"); return -1; } session->client_session = client_session; if (NULL == udsd_create_session_sock(session)) { DBG_ERR("create session socket error\n"); return -1; } return 0; } #ifdef PROTOCOL_SECURE int init_secure_client_auth_info() { INIT_LIST_HEAD(&client_auth_info_list); return 0; } static struct auth_info_list_t *get_client_auth_info(char *ip, int long_conn) { struct auth_info_list_t *auth_info = NULL, *n_auth_info = NULL, *rtn_auth_info = NULL; struct list_head *auth_info_list = long_conn ? (&g_auth_info_list) : (&client_auth_info_list); long cur_time = get_uptime(); if (!ip || !ip[0]) { return NULL; } list_for_each_entry_safe(auth_info, n_auth_info, auth_info_list, list) { if (!strncmp(ip, auth_info->ip, sizeof(auth_info->ip)) && ((auth_info->login_success == 0 && cur_time - auth_info->time <= SESSION_TOKEN_UPDATE) || (auth_info->login_success == 1 && cur_time - auth_info->time <= (long_conn ? SESSION_TOKEN_LONG_TIMEOUT : SESSION_TOKEN_TIMEOUT)))) { if (auth_info->login_success == 0) { auth_info->time = cur_time; } DBG_NOTICE("find authinfo, login_success:%d, time:%ld, now time:%ld\n", auth_info->login_success, auth_info->time, cur_time); rtn_auth_info = auth_info; } if ((auth_info->login_success == 0 && cur_time - auth_info->time > SESSION_TOKEN_UPDATE) || (auth_info->login_success == 1 && cur_time - auth_info->time > (long_conn ? SESSION_TOKEN_LONG_TIMEOUT : SESSION_TOKEN_TIMEOUT))) { if (rtn_auth_info == auth_info) { rtn_auth_info = NULL; } DBG_DBG("delete auth info of host:%s\n", auth_info->ip); uloop_timeout_cancel(&auth_info->seq_timeout); list_del(&auth_info->list); free(auth_info); } } return rtn_auth_info; } static bool update_client_auth_info(char *ip, char *nonce, char *cnonce, unsigned char *lsk, unsigned char *ivb, int seq, bool login_success) { struct auth_info_list_t *auth_info = NULL, *n_auth_info = NULL; long cur_time = get_uptime(); int matched = 0; if (!ip || !ip[0]) { return false; } list_for_each_entry_safe(auth_info, n_auth_info, &client_auth_info_list, list) { if (!strncmp(ip, auth_info->ip, sizeof(auth_info->ip))) { matched = 1; auth_info->time = cur_time; if (login_success) { auth_info->login_success = 1; } else { auth_info->login_success = 0; } if (nonce && nonce[0]) { memcpy(auth_info->nonce, nonce, sizeof(auth_info->nonce) - 1); } if (cnonce && cnonce[0]) { memcpy(auth_info->cnonce, cnonce, sizeof(auth_info->cnonce) - 1); } if (lsk) { memcpy(auth_info->aes_lsk, lsk, sizeof(auth_info->aes_lsk)); } if (ivb) { memcpy(auth_info->aes_ivb, ivb, sizeof(auth_info->aes_ivb)); } if (seq >= 0) { auth_info->last_seq = seq; } } if ((auth_info->login_success == 0 && cur_time - auth_info->time > SESSION_TOKEN_UPDATE) || (auth_info->login_success == 1 && cur_time - auth_info->time > SESSION_TOKEN_TIMEOUT)) { DBG_DBG("delete auth info of host:%s\n", auth_info->ip); uloop_timeout_cancel(&auth_info->seq_timeout); list_del(&auth_info->list); free(auth_info); } } if (!matched && cnonce) { auth_info = (struct auth_info_list_t *)malloc(sizeof(struct auth_info_list_t)); if (!auth_info) { return false; } memset(auth_info, 0, sizeof(struct auth_info_list_t)); memcpy(auth_info->ip, ip, sizeof(auth_info->ip) - 1); memcpy(auth_info->cnonce, cnonce, sizeof(auth_info->cnonce) - 1); auth_info->time = cur_time; auth_info->login_success = 0; INIT_LIST_HEAD(&auth_info->list); list_add_tail(&auth_info->list, &client_auth_info_list); DBG_DBG("auth info not match, add host:%s\n", auth_info->ip); } return true; } /* protocol secure first login */ static bool udsd_secure_login_first_post(struct tpsocket_fd *sock) { struct tpsocket_buf *pbuf = NULL, *nbuf = NULL; char *post_json = NULL; int content_len = 0; struct list_head head; INIT_LIST_HEAD(&head); struct blob_buf b = {NULL}, b1 = {NULL}; void *table = NULL; char cnonce[ENCRYPT_NONCE_LEN_16 + 1] = {0}; char *user = NULL; /* build json */ blobmsg_buf_init(&b); blobmsg_add_string(&b, "method", "login"); table = blobmsg_open_table(&b, "params"); if (table) { blobmsg_buf_init(&b1); user = blobuci_get(account, account, user, blobmsg_get_string, &b1); if (user) { blobmsg_add_string(&b, "username", user); } blob_buf_free(&b1); blobmsg_add_string(&b, "encrypt_type", "3"); /* generate cnonce */ get_random_nonce(ENCRYPT_NONCE_LEN_16, cnonce); cnonce[ENCRYPT_NONCE_LEN_16] = '\0'; blobmsg_add_string(&b, "cnonce", cnonce); blobmsg_close_table(&b, table); } post_json = blobmsg_format_json(b.head, true); nbuf = tpbuf_snprintf(1024, "%s\r\n", post_json); content_len = tpbuf_data_len(nbuf); if (post_json) { free(post_json); } blob_buf_free(&b); /* build head */ pbuf = tpbuf_snprintf(512, UDSD_LOGIN_POST_CAMERA_HEAD_REQ, "/", content_len); list_add_tail(&pbuf->list, &head); list_add_tail(&nbuf->list, &head); if (false == tpsocket_write_list_force(sock, &head, false)) { tpsocket_free_buf(&head, NULL, 0); return false; } /* update auth info list */ update_client_auth_info(sock->addr, NULL, cnonce, NULL, NULL, -1, false); return true; } /* protocol secure second login */ static bool udsd_secure_login_second_post(struct tpsocket_fd *sock) { struct tpsocket_buf *pbuf = NULL, *nbuf = NULL; char *post_json = NULL; int content_len = 0; struct list_head head; INIT_LIST_HEAD(&head); struct blob_buf b = {NULL}, b1 = {NULL}; void *table = NULL; struct auth_info_list_t *auth_info = NULL; char *user = NULL; char *config_passwd = NULL; char app_confirm[CONFIRM_LEN + 1] = {0}; unsigned char aes_lsk[AES_KEY_LEN] = {0}; unsigned char aes_ivb[AES_KEY_LEN] = {0}; /* build json */ blobmsg_buf_init(&b); blobmsg_add_string(&b, "method", "login"); table = blobmsg_open_table(&b, "params"); if (table) { blobmsg_buf_init(&b1); user = blobuci_get(account, account, user, blobmsg_get_string, &b1); if (user) { blobmsg_add_string(&b, "username", user); } blob_buf_free(&b1); blobmsg_add_string(&b, "encrypt_type", "3"); auth_info = get_client_auth_info(sock->addr, 0); if (auth_info) { /* generate digest_password */ blobmsg_buf_init(&b1); config_passwd = blobuci_get(account, account, general_password, blobmsg_get_string, &b1); cal_app_confirm((char *)auth_info->cnonce, (char *)auth_info->nonce, config_passwd, app_confirm, sizeof(app_confirm)); blob_buf_free(&b1); blobmsg_add_string(&b, "cnonce", (char *)auth_info->cnonce); blobmsg_add_string(&b, "digest_passwd", app_confirm); } blobmsg_close_table(&b, table); } post_json = blobmsg_format_json(b.head, true); nbuf = tpbuf_snprintf(1024, "%s\r\n", post_json); content_len = tpbuf_data_len(nbuf); if (post_json) { free(post_json); } blob_buf_free(&b); /* build head */ pbuf = tpbuf_snprintf(512, UDSD_LOGIN_POST_CAMERA_HEAD_REQ, "/", content_len); list_add_tail(&pbuf->list, &head); list_add_tail(&nbuf->list, &head); if (false == tpsocket_write_list_force(sock, &head, false)) { tpsocket_free_buf(&head, NULL, 0); return false; } /* update lsk ivb of auth info list */ if (auth_info) { cal_aes_lsk_ivb(config_passwd, (char *)auth_info->nonce, (char *)auth_info->cnonce, aes_lsk, aes_ivb); update_client_auth_info(sock->addr, NULL, NULL, aes_lsk, aes_ivb, -1, false); } return true; } bool udsd_send_request_to_camera(struct udsd_camera_session *session, char *path) { struct auth_info_list_t *auth_info = NULL; unsigned char *content = NULL; int content_len = 0; int enc_len = 0, buf_size = 0; unsigned char *base64_req = NULL; int nbuf_len = 0; char tapo_tag[256] = {0}; char *config_passwd = NULL; struct list_head head; INIT_LIST_HEAD(&head); struct tpsocket_buf *nbuf = NULL, *pbuf = NULL; struct blob_buf b_tmp = {NULL}; char aes_lsk[AES_KEY_LEN] = {0}; char aes_ivb[AES_KEY_LEN] = {0}; int long_conn = 0; if (!session || !session->sock) { DBG_ERR("session or sock NULL\n"); return false; } if (session->client_session && session->client_session->client_sock) { DBG_NOTICE("use long conn to send\n"); long_conn = 1; } session->send_req = true; auth_info = get_client_auth_info(session->sock->addr, long_conn); if (!auth_info) { DBG_NOTICE("no auth_info, need login to %s\n", session->sock->addr); session->response = SLP_EUNAUTH; return false; } content_len = strlen(session->json_data) + 1024; content = malloc(content_len); if (!content) { DBG_ERR("malloc content %d failed\n", content_len); return false; } memset(content, 0, content_len); snprintf((char *)content, content_len, UDSD_CAMERA_REQUEST_CONTENT, session->method, session->domain, session->req, session->json_data); DBG_DBG("send %s length %d, content length %d\n", session->method, strlen((char *)content), content_len); /* aes encrypt */ memcpy(aes_lsk, auth_info->aes_lsk, AES_KEY_LEN); memcpy(aes_ivb, auth_info->aes_ivb, AES_KEY_LEN); enc_len = aes_cbc_128_encrypt(content, strlen((char *)content), content, strlen((char *)content) + 16, (unsigned char *)aes_lsk, (unsigned char *)aes_ivb); /* base64 encode */ buf_size = (enc_len + 2) / 3 * 4 + 1; base64_req = (unsigned char *)malloc(buf_size); if (base64_req) { memset(base64_req, 0, buf_size); enc_len = tpsocket_base64_encode(content, enc_len, base64_req); buf_size = enc_len + 128; nbuf = tpbuf_snprintf(buf_size, "{\"method\":\"securePassthrough\",\"params\":{\"request\":\"%s\"}}", base64_req); nbuf_len = tpbuf_data_len(nbuf); auth_info->last_seq++; free(base64_req); } /* build head */ if (nbuf) { blobmsg_buf_init(&b_tmp); config_passwd = blobuci_get(account, account, general_password, blobmsg_get_string, &b_tmp); cal_tapo_tag(config_passwd, auth_info->cnonce, tpbuf_data(nbuf), auth_info->last_seq, tapo_tag, sizeof(tapo_tag)); pbuf = tpbuf_snprintf(512, UDSD_REQUEST_POST_CAMERA_HEAD_REQ, path ? path : "/", nbuf_len, tapo_tag, auth_info->last_seq); blob_buf_free(&b_tmp); list_add_tail(&pbuf->list, &head); list_add_tail(&nbuf->list, &head); } if (content) { free(content); } if (false == tpsocket_write_list_force(session->sock, &head, false)) { tpsocket_free_buf(&head, NULL, 0); return false; } return true; } #endif static bool tpsocket_event_config_to_camera_secure(struct tpsocket_handler *handler, struct list_head *buf, int event) { struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler); struct udsd_camera_session *session = NULL; if (sock) { session = (struct udsd_camera_session *)sock->handler.priv; } int err_code = -1; struct tpsocket_buf *nbuf = NULL; struct blob_buf b = {NULL}, b_tmp = {NULL}; char *pstr = NULL; DBG_DBG("socket event = %s\n", tpsocket_event_name(event)); #ifdef PROTOCOL_SECURE switch (event) { case TPSOCKET_EVENT_CONNECTED: if (session) { if (LOGIN_INIT == session->login_state) { if (false == udsd_secure_login_first_post(sock)) { tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return false; } session->login_cnt++; } else if (LOGIN_FIRST_POST == session->login_state) { if (false == udsd_secure_login_second_post(sock)) { tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return false; } session->login_cnt++; } else { /* finish login and send request to camera */ if (false == udsd_send_request_to_camera(session, sock->path)) { tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return false; } } } tpsocket_set_timeout(sock, 3000); break; case TPSOCKET_EVENT_RSP_HEAD: return true; case TPSOCKET_EVENT_STREAM: return true; case TPSOCKET_EVENT_MESSAGE: nbuf = tpsocket_merge_buf(buf); blobmsg_buf_init(&b); blobmsg_buf_init(&b_tmp); if (!nbuf || !session) { goto end; } pstr = strstr(tpbuf_data(nbuf), "{"); if (!pstr) { goto end; } if (blobmsg_add_json_from_string(&b, pstr)) { struct blobmsg ( blobmsg_int32 error_code, blobmsg_table result, ) (resp, &b); if (!resp.error_code) { goto end; } err_code = blobmsg_get_u32(resp.error_code); session->response = err_code; if (!resp.result) { DBG_DBG("response no result and not parse\n"); goto end; } if (session->login_state == LOGIN_INIT) { char device_confirm[CONFIRM_LEN + 1] = {0}; char *nonce = NULL; struct auth_info_list_t *auth_info = NULL; char *config_passwd = NULL; struct blobmsg ( blobmsg_table data, ) (res, resp.result, false); if (res.data) { struct blobmsg ( blobmsg_string nonce, blobmsg_string device_confirm, ) (data, res.data, false); if (!data.nonce || !data.device_confirm) { DBG_ERR("param error\n"); goto end; } nonce = blobmsg_get_string(data.nonce); update_client_auth_info(sock->addr, nonce, NULL, NULL, NULL, -1, false); auth_info = get_client_auth_info(sock->addr, 0); config_passwd = blobuci_get(account, account, general_password, blobmsg_get_string, &b_tmp); if (!config_passwd) { DBG_ERR("no general password\n"); goto end; } if (auth_info) { cal_device_confirm((char *)auth_info->cnonce, (char *)auth_info->nonce, config_passwd, device_confirm, sizeof(device_confirm)); /* check device confirm */ if (!strncmp(device_confirm, blobmsg_get_string(data.device_confirm), sizeof(device_confirm) - 1)) { session->login_state = LOGIN_FIRST_POST; } else { DBG_ERR("device confirm failed,cnonce:%s, nonce:%s\nget:%s\ncal:%s\n", auth_info->cnonce, auth_info->nonce, blobmsg_get_string(data.device_confirm), device_confirm); } } } } else if (session->login_state == LOGIN_FIRST_POST) { struct blobmsg ( blobmsg_string stok, blobmsg_string user_group, blobmsg_int32 start_seq, ) (res, resp.result, false); if (res.stok && res.user_group && res.start_seq) { update_client_auth_info(sock->addr, NULL, NULL, NULL, NULL, blobmsg_get_u32(res.start_seq), true); session->login_state = LOGIN_FINISH; update_camera_token_file(sock->addr, "admin", blobmsg_get_string(res.user_group), blobmsg_get_string(res.stok)); } } } end: blob_buf_free(&b_tmp); blob_buf_free(&b); tpsocket_close(sock); break; case TPSOCKET_EVENT_TIMEOUT: break; case TPSOCKET_EVENT_ERROR: break; case TPSOCKET_EVENT_CLOSED: if (session) { session->sock = NULL; sock->handler.priv = NULL; DBG_DBG("resp code:%d, login state:%d, login_cnt:%d\n", session->response, session->login_state, session->login_cnt); if (session->response == -1) { DBG_ERR("connect to camera failed or get no response, current retry times:%d\n", session->retry_times); if (0 == session->retry_times) { /* retry later */ uloop_timeout_set(&session->retry_timer, LOGIN_RETRY_INTERVAL); } else { udsd_camera_session_free(session, true); } break; } if (LOGIN_INIT == session->login_state) { DBG_ERR("login %s failed\n", sock->addr); if (0 == session->retry_times) { /* retry later */ uloop_timeout_set(&session->retry_timer, LOGIN_RETRY_INTERVAL); } else { udsd_camera_session_free(session, true); } } else if (LOGIN_FIRST_POST == session->login_state) { if (session->login_cnt < 2) { DBG_DBG("next login second step"); udsd_create_session_sock(session); } else { DBG_ERR("login %s failed\n", sock->addr); if (0 == session->retry_times) { /* retry later */ uloop_timeout_set(&session->retry_timer, LOGIN_RETRY_INTERVAL); } else { udsd_camera_session_free(session, true); } } } else { if (session->send_req) { /* get wrong response and restart login */ if (session->response != 0 && session->login_cnt <= 2) { DBG_DBG("restart login\n"); session->login_state = LOGIN_INIT; session->send_req = false; udsd_create_session_sock(session); } else { udsd_camera_session_free(session, true); } } else { udsd_create_session_sock(session); } } } break; default: break; } #endif tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return true; } static void ubus_get_storage_media_status_cb(struct ubus_app *app, struct blob_attr *msg, int ret, void *priv) { if (!msg || !priv) { DBG_ERR("param error, ret %d\n", ret); return; } int *storage_media_status = (int *)priv; struct blobmsg ( blobmsg_int32 storage_media_status, ) (resp, msg, false); if (resp.storage_media_status) { *storage_media_status = blobmsg_get_u32(resp.storage_media_status); } } static void udsd_get_general_device_list(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg, int *error_code) { struct blob_buf b_res = {0}, b = {0}; struct blob_attr *cur = NULL; int rem = 0; void *array = NULL; void *table = NULL; void *domain = NULL; int ai_hub_support = 0; int wifi_backup_enabled = 0; int hub_storage_enabled = 0; if (!bBuf || !error_code || !msg) { DBG_ERR("arg err\n"); *error_code = IPC_INTF_PARA_ERR; return; } domain = blobmsg_open_table(bBuf, "general_camera_manage"); if (domain) { blobmsg_buf_init(&b_res); ubus_app_invoke(svr_ubus_app, "devmgr", "get_general_sysinfo", NULL, 10000, ubus_get_general_sysinfo_cb, &b_res); struct blobmsg ( blobmsg_int32 max_bound, blobmsg_int32 current_bound, blobmsg_int32 max_24h_record_dev, blobmsg_int32 cur_24h_record_dev, blobmsg_array paired_general_device_list, ) (list, &b_res); if (list.max_bound) { blobmsg_add_blob(bBuf, list.max_bound); } if (list.current_bound) { blobmsg_add_blob(bBuf, list.current_bound); } if (list.max_24h_record_dev) { blobmsg_add_blob(bBuf, list.max_24h_record_dev); } if (list.cur_24h_record_dev) { blobmsg_add_blob(bBuf, list.cur_24h_record_dev); } array = blobmsg_open_array(bBuf, "paired_general_device_list"); if (array) { if (list.paired_general_device_list) { blobmsg_for_each_attr(cur, list.paired_general_device_list, rem) { struct blobmsg ( blobmsg_string model, //change key blobmsg_string type, //change key blobmsg_string ipaddr, //not return to app blobmsg_string alias, blobmsg_string mac, blobmsg_string device_id, blobmsg_string network_mode, blobmsg_bool wifi_backup_enabled, blobmsg_string backup_wifi, blobmsg_bool hub_storage_enabled, blobmsg_int32 ai_camera_support, blobmsg_int32 ai_enhance, blobmsg_bool AI_enhance_enabled, blobmsg_bool plan_24h_record, ) (info, cur, false); if (info.wifi_backup_enabled) { wifi_backup_enabled = blobmsg_get_bool(info.wifi_backup_enabled); } else { wifi_backup_enabled = 0; } if (info.hub_storage_enabled) { hub_storage_enabled = blobmsg_get_bool(info.hub_storage_enabled); } else { hub_storage_enabled = 0; } if (!wifi_backup_enabled && !hub_storage_enabled) { continue; } table = blobmsg_open_table(bBuf, ""); if (table) { if (info.alias) { blobmsg_add_blob(bBuf, info.alias); } if (info.mac) { blobmsg_add_blob(bBuf, info.mac); } if (info.device_id) { blobmsg_add_blob(bBuf, info.device_id); } if (info.network_mode) { blobmsg_add_blob(bBuf, info.network_mode); } blobmsg_add_u8(bBuf, "wifi_backup_enabled", wifi_backup_enabled); if (info.backup_wifi) { blobmsg_add_blob(bBuf, info.backup_wifi); } blobmsg_add_u8(bBuf, "hub_storage_enabled", hub_storage_enabled); if (info.plan_24h_record) { blobmsg_add_blob(bBuf, info.plan_24h_record); } if (info.ai_camera_support) { blobmsg_add_blob(bBuf, info.ai_camera_support); } if (info.ai_enhance) { blobmsg_add_blob(bBuf, info.ai_enhance); } if (info.AI_enhance_enabled) { blobmsg_add_blob(bBuf, info.AI_enhance_enabled); } blobmsg_buf_init(&b); if (blobuci_load_state(&b, "system", "/var/state/")) { struct blobmsg ( blobmsg_section firm, ) (sys, &b); if (sys.firm) { struct blobmsg ( blobmsg_option DEV_ID, ) (firm, sys.firm, false); if (firm.DEV_ID) { blobmsg_add_string(bBuf, "parent_device_id", blobmsg_get_string(firm.DEV_ID)); } } } blob_buf_free(&b); if (info.model) { blobmsg_add_string(bBuf, "device_model", blobmsg_get_string(info.model)); } if (info.type) { /* "SMART.IPBATTERYCAMERA" is only for hub to send tddp packet to 20004(battery camera) or 20002. * it need be converted to "SMART.IPCAMERA" for APP. */ if (!strncmp(blobmsg_get_string(info.type), "SMART.IPBATTERYCAMERA", strlen("SMART.IPBATTERYCAMERA"))) { blobmsg_add_string(bBuf, "device_type", "SMART.IPCAMERA"); } else { blobmsg_add_string(bBuf, "device_type", blobmsg_get_string(info.type)); } } blobmsg_add_string(bBuf, "category", "camera"); #ifdef AIENHANCE_SUPPORT ubus_app_invoke(svr_ubus_app, "devmgr", "get_AIEnhance_info", NULL, 3000, ubus_get_ai_result_cb, &ai_hub_support); #endif blobmsg_add_u32(bBuf, "ai_hub_support", ai_hub_support); blobmsg_close_table(bBuf, table); } } } blobmsg_close_array(bBuf, array); } blob_buf_free(&b_res); blobmsg_close_table(bBuf, domain); } *error_code = SLP_ENONE; return; } static int udsd_add_general_device(struct ubus_app *svr_ubus_app, struct blob_attr *attr, struct blob_buf *out) { struct blob_buf b = {0}; void *table = NULL, *table1 = NULL, *table2 = NULL; struct blob_attr *cur = NULL; int rem = 0; int err = 0; char mac_str[MAC_STR_LEN + 1] = {0}; int ai_hub_support = 0; int storage_media_status = 0; ubus_app_invoke(svr_ubus_app, "mediacenter", "storage_get_storage_media_status", NULL, 3000, ubus_get_storage_media_status_cb, &storage_media_status); struct blobmsg ( blobmsg_string network_mode, blobmsg_string auth_type, blobmsg_string ai_camera_support, blobmsg_string resolution, blobmsg_int32 storage_schedule_status, blobmsg_string hub_storage_enabled, blobmsg_string wifi_backup_enabled, blobmsg_string backup, ) (info, attr, false); blobmsg_buf_init(&b); table = blobmsg_open_table(&b, "device_info"); if (table) { blobmsg_for_each_attr(cur, attr, rem) { blobmsg_add_blob(&b, cur); } blobmsg_add_u32(&b, "storage_media_status", storage_media_status); blobmsg_close_table(&b, table); } table = blobmsg_open_table(&b, "config"); if (table) { if ((table1 = blobmsg_open_table(&b, "device_info"))) { if ((table2 = blobmsg_open_table(&b, "basic_info"))) { if (info.network_mode) { blobmsg_add_string(&b, "network_mode", blobmsg_get_string(info.network_mode)); } else { blobmsg_add_string(&b, "network_mode", "NONE"); } if (info.auth_type) { blobmsg_add_string(&b, "auth_type", blobmsg_get_string(info.auth_type)); } else { blobmsg_add_string(&b, "auth_type", "NONE"); } blobmsg_close_table(&b, table2); } if ((table2 = blobmsg_open_table(&b, "wifi_backup"))) { if (info.wifi_backup_enabled) { if (!strncmp(blobmsg_get_string(info.wifi_backup_enabled), "on", strlen("on"))) { blobmsg_add_u32(&b, "enable", 1); } else { blobmsg_add_u32(&b, "enable", 0); } } if (info.backup) { blobmsg_add_string(&b, "backup_wifi", blobmsg_get_string(info.backup)); } blobmsg_close_table(&b, table2); } blobmsg_close_table(&b, table1); } if ((table1 = blobmsg_open_table(&b, "hub_storage"))) { if ((table2 = blobmsg_open_table(&b, "storage_info"))) { if (info.resolution) { blobmsg_add_blob(&b, info.resolution); } if (info.storage_schedule_status) { blobmsg_add_blob(&b, info.storage_schedule_status); } if (info.hub_storage_enabled) { if (!strncmp(blobmsg_get_string(info.hub_storage_enabled), "on", strlen("on"))) { blobmsg_add_u32(&b, "enable", 1); } else { blobmsg_add_u32(&b, "enable", 0); } } blobmsg_add_u32(&b, "storage_media_status", storage_media_status); blobmsg_close_table(&b, table2); } blobmsg_close_table(&b, table1); } /* not generate AIEnhance config until call setAIEnhanceConfig */ blobmsg_close_table(&b, table); } /* send cam info to devmgr */ ubus_app_invoke(svr_ubus_app, "devmgr", "dev_add_general", b.head, 0, NULL, NULL); /* return info */ blobmsg_buf_init(&b); if (blobuci_load_state(&b, "system", "/var/state/")) { struct blobmsg ( blobmsg_section firm, ) (sys, &b); if (sys.firm) { struct blobmsg ( blobmsg_option REGION, //US blobmsg_option MAC, //28-87-BA-9A-3D-4F blobmsg_option DEV_ID, blobmsg_option MODEL, //H200 blobmsg_option ALIAS, blobmsg_option CATEGORY,//SMART.TAPOHUB ) (firm, sys.firm, false); if (firm.MAC) { if (!mac_format_change(blobmsg_get_string(firm.MAC), mac_str, sizeof(mac_str), true)) { blobmsg_add_string(out, "mac", mac_str); } } if (firm.DEV_ID) { blobmsg_add_string(out, "device_id", blobmsg_get_string(firm.DEV_ID)); } if (firm.MODEL) { blobmsg_add_string(out, "model", blobmsg_get_string(firm.MODEL)); } if (firm.ALIAS) { blobmsg_add_string(out, "alias", blobmsg_get_string(firm.ALIAS)); } if (firm.CATEGORY) { blobmsg_add_string(out, "type", blobmsg_get_string(firm.CATEGORY)); } } } blobmsg_buf_init(&b); if (blobuci_load(&b, "network")) { struct blobmsg ( blobmsg_section wan, ) (data, &b); if (data.wan) { struct blobmsg ( blobmsg_option ipaddr, ) (wan, data.wan, false); if (wan.ipaddr) { blobmsg_add_string(out, "ipaddr", blobmsg_get_string(wan.ipaddr)); } } } /* return general password */ blobmsg_buf_init(&b); if (blobuci_load(&b, "account")) { struct blobmsg ( blobmsg_section account, ) (account, &b); if (account.account) { struct blobmsg ( blobmsg_option general_password, blobmsg_option tddp_hub_id, ) (auth, account.account, false); if (auth.general_password) { blobmsg_add_string(out, "general_password", blobmsg_get_string(auth.general_password)); } if (auth.tddp_hub_id) { blobmsg_add_string(out, "hub_id", blobmsg_get_string(auth.tddp_hub_id)); } } } blob_buf_free(&b); /* auth type: todo */ blobmsg_add_string(out, "auth_type", "3"); /* ai enhance */ #ifdef AIENHANCE_SUPPORT ubus_app_invoke(svr_ubus_app, "devmgr", "get_AIEnhance_info", NULL, 3000, ubus_get_ai_result_cb, &ai_hub_support); #endif blobmsg_add_u32(out, "ai_hub_support", ai_hub_support); /* storage status */ blobmsg_add_u32(out, "storage_media_status", storage_media_status); return err; } static void udsd_wifi_backup_info_request(struct ubus_app *svr_ubus_app, struct blob_buf *bBuf, struct blob_attr *msg, int *error_code) { struct blob_buf b = {0}; void *table = NULL, *table1 = NULL, *domain = NULL; char bssid[MAC_STR_LEN + 1] = {0}; #ifndef HARDDISK_STORAGE char mac_data[18] = {0}; char mac_ra0[18] = {0}; int data_len = 0; #endif struct blob_attr *cur = NULL; int rem = 0; char *enabled = NULL; *error_code = SLP_ENONE; if (!bBuf || !error_code || !msg) { DBG_ERR("arg err\n"); *error_code = IPC_INTF_PARA_ERR; return; } struct blobmsg ( blobmsg_table backup_wifi_info, ) (arg, msg, false); if (!arg.backup_wifi_info) { DBG_ERR("arg err\n"); *error_code = IPC_INTF_PARA_ERR; return; } struct blobmsg ( blobmsg_string mac, blobmsg_string enabled, blobmsg_string backup, blobmsg_string dport, blobmsg_table basic_info, ) (info, arg.backup_wifi_info, false); if (!info.mac || !info.enabled) { DBG_ERR("arg err\n"); *error_code = IPC_INTF_PARA_ERR; return; } domain = blobmsg_open_table(bBuf, "general_camera_manage"); if (!domain) { DBG_ERR("open table error\n"); *error_code = SLP_ESYSTEM; return; } table = blobmsg_open_table(bBuf, "backup_wifi_info"); if (!table) { DBG_ERR("open table error\n"); *error_code = SLP_ESYSTEM; return; } blobmsg_buf_init(&b); if (blobuci_load(&b, "wireless")) { #ifdef HARDDISK_STORAGE struct blobmsg ( blobmsg_section wifi_backup, ) (data, &b); if (data.wifi_backup) { struct blobmsg ( blobmsg_option ssid, blobmsg_option psk, blobmsg_option bssid, ) (wifi_backup, data.wifi_backup, false); if (!wifi_backup.ssid || !wifi_backup.psk || !wifi_backup.bssid) { DBG_ERR("params NULL\n"); *error_code = IPC_INTF_PARA_ERR;; blobmsg_close_table(bBuf, table); blob_buf_free(&b); return; } blobmsg_add_string(bBuf, "ssid", blobmsg_get_string(wifi_backup.ssid)); blobmsg_add_string(bBuf, "password", blobmsg_get_string(wifi_backup.psk)); mac_format_change(blobmsg_get_string(wifi_backup.bssid), bssid, sizeof(bssid), true); blobmsg_add_string(bBuf, "bssid", bssid); } #else struct blobmsg ( blobmsg_section ra1, ) (data, &b); if (data.ra1) { struct blobmsg ( blobmsg_option ssid, blobmsg_option psk_key, ) (ra1, data.ra1, false); if (!ra1.ssid || !ra1.psk_key) { DBG_ERR("param error\n"); *error_code =IPC_INTF_PARA_ERR; blobmsg_close_table(bBuf, table); blob_buf_free(&b); return; } blobmsg_add_string(bBuf, "ssid", blobmsg_get_string(ra1.ssid)); blobmsg_add_string(bBuf, "password", blobmsg_get_string(ra1.psk_key)); } /* get bssid */ read_file_data("/sys/class/net/ra0/address", mac_ra0, sizeof(mac_ra0) - 1); data_len = read_file_data("/sys/class/net/ra1/address", mac_data, sizeof(mac_data) - 1); if (!strncmp(mac_ra0, mac_data, strlen(mac_ra0))) { data_len = read_file_data("/sys/class/net/ra2/address", mac_data, sizeof(mac_data) - 1); DBG_ERR("ra1 is same as ra0:%s, then use ra2:%s\n", mac_ra0, mac_data); } if (data_len > 0) { mac_format_change(mac_data, bssid, sizeof(bssid), true); blobmsg_add_string(bBuf, "bssid", bssid); } #endif } blobmsg_buf_init(&b); if (info.dport) { blobmsg_add_blob(&b, info.dport); ubus_app_invoke(svr_ubus_app, "unetwork", "ebtable_reload", b.head, 0, NULL, NULL); } blobmsg_buf_init(&b); enabled = blobmsg_get_string(info.enabled); if (info.basic_info && !strncmp(enabled, "on", strlen("on"))) { if ((table1 = blobmsg_open_table(bBuf, "basic_info"))) { blobmsg_for_each_attr(cur, info.basic_info, rem) { blobmsg_add_blob(&b, cur); } blobmsg_add_blob(&b, info.mac); if (info.backup) { blobmsg_add_blob(&b, info.backup); } blobmsg_add_string(&b, "wifi_backup_enabled", blobmsg_get_string(info.enabled)); udsd_add_general_device(svr_ubus_app, b.head, bBuf); blobmsg_close_table(bBuf, table1); } } else { blobmsg_add_blob(&b, info.mac); blobmsg_add_blob(&b, info.enabled); if (info.backup) { blobmsg_add_blob(&b, info.backup); } ubus_app_event(svr_ubus_app, "general_wifi_backup", b.head); } blob_buf_free(&b); blobmsg_close_table(bBuf, table); blobmsg_close_table(bBuf, domain); return; } static void udsd_storage_hub_request(struct ubus_app *svr_ubus_app, struct blob_buf *bBuf, struct blob_attr *msg, int *error_code) { /* including bind hub request */ struct blob_buf b = {0}; void *table = NULL, *table1 = NULL, *domain = NULL; struct blob_attr *cur = NULL; int rem = 0; int storage_media_status = 0; char *enabled = NULL; *error_code = SLP_ENONE; if (!bBuf || !error_code || !msg) { DBG_ERR("arg err\n"); *error_code =IPC_INTF_PARA_ERR; return; } struct blobmsg ( blobmsg_table storage_info, ) (arg, msg, false); if (!arg.storage_info) { DBG_ERR("no storage info\n"); *error_code = IPC_INTF_PARA_ERR; return; } struct blobmsg ( blobmsg_string enabled, /* on, off */ blobmsg_string mac, blobmsg_table basic_info, ) (info, arg.storage_info, false); if (!info.enabled || !info.mac) { DBG_ERR("param error\n"); *error_code = IPC_INTF_PARA_ERR; return; } blobmsg_buf_init(&b); enabled = blobmsg_get_string(info.enabled); #ifdef HARDDISK_STORAGE struct blob_buf dev_buf = {0}; char *mac_str = blobmsg_get_string(info.mac); blobmsg_buf_init(&dev_buf); blobmsg_add_string(&dev_buf, "device_mac", mac_str); ubus_app_invoke(svr_ubus_app, "mediacenter", "storage_get_storage_media_status", dev_buf.head, 3000, ubus_get_storage_media_status_cb, &storage_media_status); blob_buf_free(&dev_buf); #else ubus_app_invoke(svr_ubus_app, "mediacenter", "storage_get_storage_media_status", NULL, 3000, ubus_get_storage_media_status_cb, &storage_media_status); #endif domain = blobmsg_open_table(bBuf, "general_camera_manage"); if (domain) { table = blobmsg_open_table(bBuf, "storage_info"); if (table) { if (info.basic_info && !strncmp(enabled, "on", strlen("on"))) { /* return hub info */ table1 = blobmsg_open_table(bBuf, "basic_info"); if (table1) { blobmsg_for_each_attr(cur, info.basic_info, rem) { blobmsg_add_blob(&b, cur); } blobmsg_for_each_attr(cur, arg.storage_info, rem) { if (strncmp(blobmsg_name(cur), "basic_info", strlen("basic_info"))) { blobmsg_add_blob(&b, cur); } } if (info.enabled) { blobmsg_add_string(&b, "hub_storage_enabled", blobmsg_get_string(info.enabled)); } udsd_add_general_device(svr_ubus_app, b.head, bBuf); blobmsg_close_table(bBuf, table1); } } else { /* update :mac, enabled */ blobmsg_for_each_attr(cur, arg.storage_info, rem) { if (strncmp(blobmsg_name(cur), "basic_info", strlen("basic_info"))) { blobmsg_add_blob(&b, cur); } } if(!strncmp(enabled, "off", strlen("off"))){ blobmsg_add_u32(&b, "enable", 0); }else{ blobmsg_add_u32(&b, "enable", 1); } blobmsg_add_u32(&b, "storage_media_status", storage_media_status); ubus_app_invoke(svr_ubus_app, "devmgr", "general_hub_storage_update", b.head, 0, NULL, NULL); } blobmsg_add_u32(bBuf, "storage_media_status", storage_media_status); blobmsg_add_u32(bBuf, "long_conn_support", 1); blobmsg_close_table(bBuf, table); } blobmsg_close_table(bBuf, domain); } blob_buf_free(&b); return; } static void udsd_sync_hub_request(struct ubus_app *svr_ubus_app, struct blob_buf *bBuf, struct blob_attr *msg, int *error_code) { struct blob_buf b = {0}; struct blob_attr *cur = NULL; int rem = 0; void *table = NULL, *domain = NULL; blobmsg_buf_init(&b); if (!bBuf || !error_code || !msg) { DBG_ERR("arg err\n"); if (error_code) { *error_code = IPC_INTF_PARA_ERR; } goto RTN; } struct blobmsg ( blobmsg_table config_sync, ) (arg, msg, false); if (!arg.config_sync) { DBG_ERR("no config_sync\n"); goto RTN; } domain = blobmsg_open_table(bBuf, "general_camera_manage"); if (domain) { if ((table = blobmsg_open_table(bBuf, "config_sync"))) { blobmsg_for_each_attr(cur, arg.config_sync, rem) { blobmsg_add_blob(&b, cur); } ubus_app_invoke(svr_ubus_app, "devmgr", "sync_config_from_camera", b.head, 3000, ubus_sync_AIEnhance_cb, bBuf); blobmsg_add_u32(bBuf, "long_conn_support", 1); blobmsg_close_table(bBuf, table); } blobmsg_close_table(bBuf, domain); } if (error_code) { *error_code = SLP_ENONE; } RTN: blob_buf_free(&b); return; } static void udsd_get_general_device_scan_list(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg, int *error_code) { void *domain = NULL; if (!bBuf || !error_code || !msg) { DBG_ERR("arg err\n"); if (error_code) { *error_code = IPC_INTF_PARA_ERR; } return; } struct blobmsg( blobmsg_table general_device_list ) (arg, msg, false); if (!arg.general_device_list) { DBG_ERR("no general_device_list item in request arg\n"); *error_code = IPC_INTF_PARA_ERR; return; } domain = blobmsg_open_table(bBuf, "general_camera_manage"); if (!domain) { DBG_ERR("open domain failed\n"); *error_code = IPC_INTF_PARA_ERR; return; } ubus_app_invoke(svr_ubus_app, "tddp-mmt", "get_scan_cam_list", NULL, 10000, ubus_get_scan_device_cb, bBuf); blobmsg_close_table(bBuf, domain); *error_code = SLP_ENONE; return; } static void udsd_start_scan_general_device(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg, int *error_code) { if (!error_code) { DBG_ERR("arg err\n"); return; } if (!msg) { *error_code = SLP_EINVARG; return; } struct blobmsg( blobmsg_table start_scan ) (arg, msg, false); if (!arg.start_scan) { DBG_ERR("no start_scan item in request arg\n"); *error_code = IPC_INTF_PARA_ERR; return; } ubus_app_invoke(svr_ubus_app, "tddp-mmt", "start_scan_cam", NULL, 0, NULL, NULL); *error_code = SLP_ENONE; return; } static void udsd_stop_scan_general_device(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg, int *error_code) { if (!error_code) { DBG_ERR("arg err\n"); return; } if (!msg) { *error_code = SLP_EINVARG; return; } struct blobmsg( blobmsg_table stop_scan ) (arg, msg, false); if (!arg.stop_scan) { DBG_ERR("no stop_scan item in request arg\n"); *error_code = IPC_INTF_PARA_ERR; return; } ubus_app_invoke(svr_ubus_app, "tddp-mmt", "stop_scan_cam", NULL, 0, NULL, NULL); *error_code = SLP_ENONE; return; } static const UDSD_MOD_CMD g_udsd_cmds_general_camera_manage[] = { /* for general camera */ {UDSD_CMD_R, "backupWifiRequest", udsd_wifi_backup_info_request}, {UDSD_CMD_W, "storageHubRequest", udsd_storage_hub_request}, {UDSD_CMD_W, "syncHubRequest", udsd_sync_hub_request}, /* for app */ {UDSD_CMD_R, "getGeneralDeviceList", udsd_get_general_device_list}, {UDSD_CMD_R, "getScanGeneralDeviceList", udsd_get_general_device_scan_list}, {UDSD_CMD_W, "startScanGeneralDevice", udsd_start_scan_general_device}, {UDSD_CMD_W, "stopScanGeneralDevice", udsd_stop_scan_general_device}, {UDSD_CMD_N, NULL, NULL} }; UDSD_CMD_ADD(general_camera_manage, "general_camera_manage", g_udsd_cmds_general_camera_manage);
09-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值