# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
# Copyright (c) 2021 Nvidia, Inc. All rights reserved. See COPYING file
"""
This module wraps mlx5dv_<obj> C structs, such as mlx5dv_cq, mlx5dv_qp etc.
It exposes to the users the mlx5 driver-specific attributes for ibv objects by
extracting them via mlx5dv_init_obj() API by using Mlx5DvObj class, which holds
all the (currently) supported Mlx5Dv<Obj> objects.
Note: This is not be confused with Mlx5<Obj> which holds the ibv_<obj>_ex that
      was created using mlx5dv_create_<obj>().
"""

from libc.stdint cimport uintptr_t, uint32_t

from pyverbs.pyverbs_error import PyverbsUserError, PyverbsRDMAError
cimport pyverbs.providers.mlx5.mlx5_enums as dve
cimport pyverbs.libibverbs as v


cdef class Mlx5DvPD(PyverbsObject):

    @property
    def pdn(self):
        """ The protection domain object number """
        return self.dv_pd.pdn

    @property
    def comp_mask(self):
        return self.dv_pd.comp_mask


cdef class Mlx5DvCQ(PyverbsObject):

    @property
    def cqe_size(self):
        return self.dv_cq.cqe_size

    @property
    def comp_mask(self):
        return self.dv_cq.comp_mask

    @property
    def cqn(self):
        return self.dv_cq.cqn

    @property
    def buf(self):
        return <uintptr_t><void*>self.dv_cq.buf

    @property
    def cq_uar(self):
        return <uintptr_t><void*>self.dv_cq.cq_uar

    @property
    def dbrec(self):
        return <uintptr_t><uint32_t*>self.dv_cq.dbrec

    @property
    def cqe_cnt(self):
        return self.dv_cq.cqe_cnt


cdef class Mlx5DvQP(PyverbsObject):

    @property
    def rqn(self):
        """ The receive queue number of the QP"""
        return self.dv_qp.rqn

    @property
    def sqn(self):
        """ The send queue number of the QP"""
        return self.dv_qp.sqn

    @property
    def tirn(self):
        """
        The number of the transport interface receive object that attached
        to the RQ of the QP
        """
        return self.dv_qp.tirn

    @property
    def tisn(self):
        """
        The number of the transport interface send object that attached
        to the SQ of the QP
        """
        return self.dv_qp.tisn

    @property
    def comp_mask(self):
        return self.dv_qp.comp_mask
    @comp_mask.setter
    def comp_mask(self, val):
        self.dv_qp.comp_mask = val

    @property
    def uar_mmap_offset(self):
        return self.uar_mmap_offset


cdef class Mlx5DvSRQ(PyverbsObject):

    @property
    def stride(self):
        return self.dv_srq.stride

    @property
    def head(self):
        return self.dv_srq.stride

    @property
    def tail(self):
        return self.dv_srq.stride

    @property
    def comp_mask(self):
        return self.dv_srq.comp_mask
    @comp_mask.setter
    def comp_mask(self, val):
        self.dv_srq.comp_mask = val

    @property
    def srqn(self):
        """ The shared receive queue object number """
        return self.dv_srq.srqn


cdef class Mlx5DvObj(PyverbsObject):
    """
    Mlx5DvObj represents mlx5dv_obj C struct.
    """
    def __init__(self, obj_type=None, **kwargs):
        """
        Retrieves DV objects from ibv object to be able to extract attributes
        (such as cqe_size of a CQ).
        Currently supports CQ, QP, PD and SRQ objects.
        The initialized objects can be accessed using self.dvobj (e.g. self.dvcq).
        :param obj_type: Bitmask which defines what objects was provided.
                         Currently it supports: MLX5DV_OBJ_CQ, MLX5DV_OBJ_QP,
                         MLX5DV_OBJ_SRQ and MLX5DV_OBJ_PD.
        :param kwargs: List of objects (cq, qp, pd, srq) from which to extract
                       data and their comp_masks if applicable. If comp_mask is
                       not provided by user, mask all by default.
        """
        self.dv_pd = self.dv_cq = self.dv_qp = self.dv_srq = None
        if obj_type is None:
            return
        self.init_obj(obj_type, **kwargs)

    def init_obj(self, obj_type, **kwargs):
        """
        Initialize DV objects.
        The objects are re-initialized if they're already extracted.
        """
        supported_obj_types = dve.MLX5DV_OBJ_CQ | dve.MLX5DV_OBJ_QP | \
                         dve.MLX5DV_OBJ_PD | dve.MLX5DV_OBJ_SRQ
        if obj_type & supported_obj_types is False:
            raise PyverbsUserError('Invalid obj_type was provided')

        cq = kwargs.get('cq') if obj_type | dve.MLX5DV_OBJ_CQ else None
        qp = kwargs.get('qp') if obj_type | dve.MLX5DV_OBJ_QP else None
        pd = kwargs.get('pd') if obj_type | dve.MLX5DV_OBJ_PD else None
        srq = kwargs.get('srq') if obj_type | dve.MLX5DV_OBJ_SRQ else None
        if cq is qp is pd is srq is None:
            raise PyverbsUserError("No supported object was provided.")

        if cq:
            dv_cq = Mlx5DvCQ()
            self.obj.cq.in_ = <v.ibv_cq*>cq.cq
            self.obj.cq.out = &(dv_cq.dv_cq)
            self.dv_cq = dv_cq
        if qp:
            dv_qp = Mlx5DvQP()
            comp_mask = kwargs.get('qp_comp_mask')
            dv_qp.comp_mask = comp_mask if comp_mask else \
                dv.MLX5DV_QP_MASK_UAR_MMAP_OFFSET | \
                dv.MLX5DV_QP_MASK_RAW_QP_HANDLES | \
                dv.MLX5DV_QP_MASK_RAW_QP_TIR_ADDR
            self.obj.qp.in_ = <v.ibv_qp*>qp.qp
            self.obj.qp.out = &(dv_qp.dv_qp)
            self.dv_qp = dv_qp
        if pd:
            dv_pd = Mlx5DvPD()
            self.obj.pd.in_ = <v.ibv_pd*>pd.pd
            self.obj.pd.out = &(dv_pd.dv_pd)
            self.dv_pd = dv_pd
        if srq:
            dv_srq = Mlx5DvSRQ()
            comp_mask = kwargs.get('srq_comp_mask')
            dv_srq.comp_mask = comp_mask if comp_mask else dv.MLX5DV_SRQ_MASK_SRQN
            self.obj.srq.in_ = <v.ibv_srq*>srq.srq
            self.obj.srq.out = &(dv_srq.dv_srq)
            self.dv_srq = dv_srq

        rc = dv.mlx5dv_init_obj(&self.obj, obj_type)
        if rc != 0:
            raise PyverbsRDMAError("Failed to initialize Mlx5DvObj", rc)

    @property
    def dvcq(self):
        return self.dv_cq

    @property
    def dvqp(self):
        return self.dv_qp

    @property
    def dvpd(self):
        return self.dv_pd

    @property
    def dvsrq(self):
        return self.dv_srq
