Hypervisor Display架构

Hypervisor Display架构部分

1,所有LA侧的APP与显示相关的调用最终都会交由SurfaceFlinger处理

2,SurfaceFlinger会最终调用android.hardware.graphics.composer@2.4-service服务

3,android.hardware.graphics.composer@2.4-service服务会调用GPU&DRM&Qcom的一些列库文件(很遗憾这些库相当一大部分源码是不开源的)

4,libdrm库调用open/ioctl等函数会经过lib_drm_fe库的转接,当然lib_drm_fe会做很多其他适配的工作

5,lib_drm_fe会调用内核HGSL驱动中的hab通信接口与QNX侧的wfd_be服务进行通信

6,wfd_be服务会解析接收到的LA侧的数据包

7,根据数据包中的命令类型调用不同的openwfd接口,需要注意的是,这里的接口是做了一层转换的,举个例子wfdEnumerateDevices_Host,这个接口会进行转换之后真正调用wfdEnumerateDevices函数

8,所有的操作都执行完成之后,如果有必要会唤醒wfd_be的commit&vsync现场,通知openwfd刷新画面

lib_drm_fe会调用内核HGSL驱动中的hab通信接口与QNX侧的wfd_be服务进行通信

 sa8295 dtsi配置

direwolf-vm.dtsi
msm_gpu_hyp: qcom,hgsl@0x3d00000 {
                compatible = "qcom,hgsl";
                reg = <0x3d00000 0x8>, <0x3d8f000 0x4>;
                reg-names = "hgsl_reg_hwinf", "hgsl_reg_gmucx";

                qcom,glb-db-senders = <&hgsl_tcsr_sender0
                                        &hgsl_tcsr_sender1>;
                qcom,glb-db-receivers = <&hgsl_tcsr_receiver0
                                        &hgsl_tcsr_receiver1>;
        };

kernel/msm-5.4/drivers/soc/qcom/hgsl/hgsl 设备树匹配

static const struct of_device_id qcom_hgsl_of_match[] = {
        { .compatible = "qcom,hgsl" },
        {}
};
MODULE_DEVICE_TABLE(of, qcom_hgsl_of_match);

static struct platform_driver qcom_hgsl_driver = {
        .probe = qcom_hgsl_probe,
        .remove = qcom_hgsl_remove,
        .driver  = {
                .name  = "qcom-hgsl",
                .of_match_table = qcom_hgsl_of_match,
        },
};
module_platform_driver(qcom_hgsl_driver);

lagvm/LINUX/android/kernel/msm-5.4/drivers/soc/qcom/hgsl/hgsl.c

static int qcom_hgsl_probe(struct platform_device *pdev)
{
        struct qcom_hgsl *hgsl_dev;
        int ret;
        int i;

        hgsl_dev = devm_kzalloc(&pdev->dev, sizeof(*hgsl_dev), GFP_KERNEL);
        if (!hgsl_dev)
                return -ENOMEM;

        hgsl_dev->dev = &pdev->dev;

        ret = qcom_hgsl_register(pdev, hgsl_dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "qcom_hgsl_register failed, ret %d\n",
                                                                        ret);
                return ret;
        }

        ret = hgsl_init_context(hgsl_dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "hgsl_init_context failed, ret %d\n",
                                                                        ret);
                goto exit_dereg;
        }
        //创建hgsl-release-wq workqueue
        ret = hgsl_init_release_wq(hgsl_dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "hgsl_init_release_wq failed, ret %d\n",
                                                                        ret);
                goto exit_dereg;
        }

        hgsl_dev->db_off = hgsl_is_db_off(pdev);
        idr_init(&hgsl_dev->isync_timeline_idr);
        spin_lock_init(&hgsl_dev->isync_timeline_lock);

        for (i = 0; i < MAX_DB_QUEUE; i++) {
                mutex_init(&hgsl_dev->dbq[i].lock);
                hgsl_dev->dbq[i].state = DB_STATE_Q_UNINIT;
        }

        if (!hgsl_dev->db_off)
                hgsl_init_global_hyp_channel(hgsl_dev);

        platform_set_drvdata(pdev, hgsl_dev);

        return 0;

exit_dereg:
        qcom_hgsl_deregister(pdev);
        return ret;
}

 

static int qcom_hgsl_register(struct platform_device *pdev,
                                struct qcom_hgsl *hgsl_dev)
{
        int ret;

        ret = alloc_chrdev_region(&hgsl_dev->device_no, 0,
                                                HGSL_DEV_NUM,
                                                HGSL_DEVICE_NAME);
        if (ret < 0) {
                dev_err(&pdev->dev, "alloc_chrdev_region failed %d\n", ret);
                return ret;
        }
       //创建一个hgsl class节点
        hgsl_dev->driver_class = class_create(THIS_MODULE, HGSL_DEVICE_NAME);
        if (IS_ERR(hgsl_dev->driver_class)) {
                ret = -ENOMEM;
                dev_err(&pdev->dev, "class_create failed %d\n", ret);
                goto exit_unreg_chrdev_region;
        }
       //创建一个hgsl 设备
        hgsl_dev->class_dev = device_create(hgsl_dev->driver_class,
                                        NULL,
                                        hgsl_dev->device_no,
                                        hgsl_dev, HGSL_DEVICE_NAME);

        if (IS_ERR(hgsl_dev->class_dev)) {
                dev_err(&pdev->dev, "class_device_create failed %d\n", ret);
                ret = -ENOMEM;
                goto exit_destroy_class;
        }
        //注册hgsl 设备操作接口
        cdev_init(&hgsl_dev->cdev, &hgsl_fops);

        hgsl_dev->cdev.owner = THIS_MODULE;
        //注册hgsl设备
        ret = cdev_add(&hgsl_dev->cdev,
                                        MKDEV(MAJOR(hgsl_dev->device_no), 0),
                                        1);
        if (ret < 0) {
                dev_err(&pdev->dev, "cdev_add failed %d\n", ret);
                goto exit_destroy_device;
        }

        ret = dma_coerce_mask_and_coherent(hgsl_dev->dev, DMA_BIT_MASK(64));
        if (ret)
                LOGW("Failed to set dma mask to 64 bits, ret = %d", ret);

        return 0;

exit_destroy_device:
        device_destroy(hgsl_dev->driver_class, hgsl_dev->device_no);
exit_destroy_class:
        class_destroy(hgsl_dev->driver_class);
exit_unreg_chrdev_region:
        unregister_chrdev_region(hgsl_dev->device_no, 1);
        return ret;
}

 

设备注册的操作接口hgsl_fops

static const struct file_operations hgsl_fops = {
        .owner = THIS_MODULE,
        .open = hgsl_open,
        .release = hgsl_release,
        .read = hgsl_read,
        .unlocked_ioctl = hgsl_ioctl,
        .compat_ioctl = hgsl_compat_ioctl
};

 

static int hgsl_open(struct inode *inodep, struct file *filep)
{
        struct hgsl_priv *priv = hgsl_zalloc(sizeof(*priv));
        struct qcom_hgsl  *hgsl = container_of(inodep->i_cdev,
                                               struct qcom_hgsl, cdev);
        //获取当前进程的pid 和task_struct
        struct pid *pid = task_tgid(current);
        struct task_struct *task = pid_task(pid, PIDTYPE_PID);
        int ret = 0;

        if (!priv)
                return -ENOMEM;

        if (!task) {
                ret = -EINVAL;
                goto out;
        }

        INIT_LIST_HEAD(&priv->mem_mapped);
        INIT_LIST_HEAD(&priv->mem_allocated);
        mutex_init(&priv->lock);
        priv->pid = task_pid_nr(task);
        
        ret = hgsl_hyp_init(&priv->hyp_priv, hgsl->dev,
                priv->pid, task->comm);
        if (ret != 0)
                goto out;

        priv->dev = hgsl;
        filep->private_data = priv;

out:
        if (ret != 0)
                kfree(priv);
        return ret;
}

 

int hgsl_hyp_init(struct hgsl_hyp_priv_t *priv, struct device *dev,
        int client_pid, const char * const client_name)
{
        priv->dev = dev;
        return hgsl_hyp_channel_pool_init(priv, client_pid, client_name);
}
//将task_struct 信息与hgsl_hyp_priv_t 绑定
static int hgsl_hyp_channel_pool_init(struct hgsl_hyp_priv_t *priv,
        int client_pid, const char * const client_name)
{
        INIT_LIST_HEAD(&priv->free_channels);
        INIT_LIST_HEAD(&priv->busy_channels);
        mutex_init(&priv->lock);
        priv->conn_id = 0;
        strlcpy(priv->client_name, client_name, sizeof(priv->client_name));
        priv->client_pid = client_pid;
        idr_init(&priv->channel_idr);

        LOGD("pid %d, task name %s"
                , (int) priv->client_pid, priv->client_name);

        return 0;
}

Libgsl 会使与驱动hgsl交互

static long hgsl_compat_ioctl(struct file *filep, unsigned int cmd,
        unsigned long arg)
{
        return hgsl_ioctl(filep, cmd, arg);
}

static long hgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
        int ret;

        switch (cmd) {
        case HGSL_IOCTL_ISSUE_IB:
                ret = hgsl_ioctl_issueib(filep, arg);
                break;
        case HGSL_IOCTL_CTXT_CREATE:
                ret = hgsl_ioctl_ctxt_create(filep, arg);
                break;
        case HGSL_IOCTL_CTXT_DESTROY:
                ret = hgsl_ioctl_ctxt_destroy(filep, arg);
                break;
        case HGSL_IOCTL_WAIT_TIMESTAMP:
                ret = hgsl_ioctl_wait_timestamp(filep, arg);
                break;
        case HGSL_IOCTL_READ_TIMESTAMP:
                ret = hgsl_ioctl_read_timestamp(filep, arg);
                break;
        case HGSL_IOCTL_CHECK_TIMESTAMP:
                ret = hgsl_ioctl_check_timestamp(filep, arg);
                break;
        case HGSL_IOCTL_HYP_GENERIC_TRANSACTION:
                ret = hgsl_ioctl_hyp_generic_transaction(filep, arg);
                break;
        case HGSL_IOCTL_GET_SHADOWTS_MEM:
                ret = hgsl_ioctl_get_shadowts_mem(filep, arg);
                break;
        case HGSL_IOCTL_PUT_SHADOWTS_MEM:
                ret = hgsl_ioctl_put_shadowts_mem(filep, arg);
                break;
        case HGSL_IOCTL_MEM_ALLOC:
                ret = hgsl_ioctl_mem_alloc(filep, arg);
                break;
        case HGSL_IOCTL_MEM_FREE:
                ret = hgsl_ioctl_mem_free(filep, arg);
                break;
        case HGSL_IOCTL_MEM_MAP_SMMU:
                ret = hgsl_ioctl_mem_map_smmu(filep, arg);
                break;
        case HGSL_IOCTL_MEM_UNMAP_SMMU:
                ret = hgsl_ioctl_mem_unmap_smmu(filep, arg);
                break;
        case HGSL_IOCTL_MEM_CACHE_OPERATION:
                ret = hgsl_ioctl_mem_cache_operation(filep, arg);
                break;
        case HGSL_IOCTL_ISSUIB_WITH_ALLOC_LIST:
                ret = hgsl_ioctl_issueib_with_alloc_list(filep, arg);
                break;
        case HGSL_IOCTL_GET_SYSTEM_TIME:
                ret = hgsl_ioctl_get_system_time(filep, arg);
                break;
        case HGSL_IOCTL_SYNCOBJ_WAIT_MULTIPLE:
                ret = hgsl_ioctl_syncobj_wait_multiple(filep, arg);
                break;
        case HGSL_IOCTL_PERFCOUNTER_SELECT:
                ret = hgsl_ioctl_perfcounter_select(filep, arg);
                break;
        case HGSL_IOCTL_PERFCOUNTER_DESELECT:
                ret = hgsl_ioctl_perfcounter_deselect(filep, arg);
                break;
        case HGSL_IOCTL_PERFCOUNTER_QUERY_SELECTION:
                ret = hgsl_ioctl_perfcounter_query_selection(filep, arg);
                break;
        case HGSL_IOCTL_PERFCOUNTER_READ:
                ret = hgsl_ioctl_perfcounter_read(filep, arg);
                break;
        case HGSL_IOCTL_SET_METAINFO:
                ret = hgsl_ioctl_set_metainfo(filep, arg);
                break;
        case HGSL_IOCTL_HSYNC_FENCE_CREATE:
                ret = hgsl_ioctl_hsync_fence_create(filep, arg);
                break;
        case HGSL_IOCTL_ISYNC_TIMELINE_CREATE:
                ret = hgsl_ioctl_isync_timeline_create(filep, arg);
                break;
        case HGSL_IOCTL_ISYNC_TIMELINE_DESTROY:
                ret = hgsl_ioctl_isync_timeline_destroy(filep, arg);
                break;
        case HGSL_IOCTL_ISYNC_FENCE_CREATE:
                ret = hgsl_ioctl_isync_fence_create(filep, arg);
                break;
        case HGSL_IOCTL_ISYNC_FENCE_SIGNAL:
                ret = hgsl_ioctl_isync_fence_signal(filep, arg);
                break;
        case HGSL_IOCTL_ISYNC_FORWARD:
                ret = hgsl_ioctl_isync_forward(filep, arg);
                break;
        case HGSL_IOCTL_TIMELINE_CREATE:
                ret = hgsl_ioctl_timeline_create(filep, arg);
                break;
        case HGSL_IOCTL_TIMELINE_SIGNAL:
                ret = hgsl_ioctl_timeline_signal(filep, arg);
                break;
        case HGSL_IOCTL_TIMELINE_QUERY:
                ret = hgsl_ioctl_timeline_query(filep, arg);
                break;
        case HGSL_IOCTL_TIMELINE_WAIT:
                ret = hgsl_ioctl_timeline_wait(filep, arg);
                break;

        default:
                ret = -ENOIOCTLCMD;
        }

        return ret;
}
static int hgsl_ioctl_ctxt_create(struct file *filep, unsigned long arg)
{
        struct hgsl_priv *priv = filep->private_data;
        struct qcom_hgsl *hgsl = priv->dev;
        struct hgsl_ioctl_ctxt_create_params params;
        struct hgsl_context *ctxt = NULL;
        int ret = 0;
        struct hgsl_hab_channel_t *hab_channel = NULL;
        bool ctxt_created = false;

        if (copy_from_user(&params, USRPTR(arg), sizeof(params))) {
                LOGE("failed to copy params from user");
                ret = -EFAULT;
                return ret;
        }
         //建立hab socket
        ret = hgsl_hyp_channel_pool_get(&priv->hyp_priv, 0, &hab_channel);
        if (ret) {
                LOGE("Failed to get hab channel %d", ret);
                goto out;
        }

        ctxt = hgsl_zalloc(sizeof(*ctxt));
        if (ctxt == NULL) {
                ret = -ENOMEM;
                return ret;
        }

        if (params.flags & GSL_CONTEXT_FLAG_CLIENT_GENERATED_TS)
                params.flags |= GSL_CONTEXT_FLAG_USER_GENERATED_TS;

        if (params.flags & GSL_CONTEXT_FLAG_BIND) {
                params.flags &= ~GSL_CONTEXT_FLAG_CLIENT_GENERATED_TS;
                params.flags |= GSL_CONTEXT_FLAG_USER_GENERATED_TS;
        }
         //使用hab socket发送数据
        ret = hgsl_hyp_ctxt_create(hab_channel, &params);
        if (ret)
                goto out;

        if (params.ctxthandle >= HGSL_CONTEXT_NUM) {
                LOGE("invalid ctxt id %d", params.ctxthandle);
                ret = -EINVAL;
                goto out;
        }

        ctxt->context_id = params.ctxthandle;
        ctxt->devhandle = params.devhandle;
        ctxt->pid = priv->pid;
        ctxt->priv = priv;
        ctxt->flags = params.flags;
        hgsl_get_shadowts_mem(hab_channel, ctxt);
        if (hgsl->global_hyp_inited && !hgsl->db_off)
                hgsl_ctxt_create_dbq(priv, hab_channel, ctxt);

        kref_init(&ctxt->kref);
        init_waitqueue_head(&ctxt->wait_q);

        write_lock(&hgsl->ctxt_lock);
        if (hgsl->contexts[ctxt->context_id] != NULL) {
                LOGE("context id %d already created",
                        ctxt->context_id);
                ret = -EBUSY;
                write_unlock(&hgsl->ctxt_lock);
                goto out;
        }

        hgsl->contexts[ctxt->context_id] = ctxt;
        write_unlock(&hgsl->ctxt_lock);
        ctxt_created = true;

        if (hgsl_ctxt_use_global_dbq(ctxt)) {
                ret = hgsl_hsync_timeline_create(ctxt);
                if (ret < 0)
                        LOGE("hsync timeline failed for context %d", params.ctxthandle);
        }

        if (ctxt->timeline)
                params.sync_type = HGSL_SYNC_TYPE_HSYNC;
        else
                params.sync_type = HGSL_SYNC_TYPE_ISYNC;

        if (copy_to_user(USRPTR(arg), &params, sizeof(params))) {
                ret = -EFAULT;
                goto out;
        }

out:
        LOGD("%d", params.ctxthandle);
        if (ret) {
                if (ctxt_created)
                        hgsl_ctxt_destroy(priv, hab_channel, params.ctxthandle, NULL, false);
                else if (ctxt && (params.ctxthandle < HGSL_CONTEXT_NUM)) {
                        _unmap_shadow(ctxt);
                        hgsl_hyp_put_shadowts_mem(hab_channel, &ctxt->shadow_ts_node);
                        hgsl_hyp_ctxt_destroy(hab_channel, ctxt->devhandle, ctxt->context_id, NULL);
                        kfree(ctxt);
                }
                LOGE("failed to create context");
        }

        hgsl_hyp_channel_pool_put(hab_channel);
        return ret;
}
int hgsl_hyp_channel_pool_get(
        struct hgsl_hyp_priv_t *priv, int id, struct hgsl_hab_channel_t **channel)
{
        struct hgsl_hab_channel_t *hab_channel = NULL;
        int ret = 0;

        if (!channel)
                return -EINVAL;

        mutex_lock(&priv->lock);
        if (id) {
                ret = hgsl_hyp_channel_pool_get_by_id(priv, id, &hab_channel);
                if (ret)
                        LOGE("Failed to find channel %d, ret %d", id, ret);
        } else {
                if (list_empty(&priv->free_channels)) {
                        //创建一个channel 
                        ret = hgsl_rpc_create_channel(priv, &hab_channel);
                        LOGD("hgsl_rpc_create_channel returned, ret %d hab_channel %p",
                                ret, hab_channel);
                } else {
                        hab_channel = container_of(priv->free_channels.next,
                                struct hgsl_hab_channel_t, node);
                        if (hab_channel != NULL) {
                                list_del(&hab_channel->node);
                                LOGD("get %p from free pool", hab_channel);
                        } else {
                                ret = -EINVAL;
                                LOGE("invalid hab_channel in the list");
                        }
                }

                if (!ret)
                        list_add_tail(&hab_channel->node, &priv->busy_channels);
        }

        if (!ret) {
                *channel = hab_channel;
                hab_channel->busy = true;
        }
        mutex_unlock(&priv->lock);

        if ((!ret) && (!id)) {
                ret = hgsl_rpc_parcel_reset(hab_channel);
                if (ret) {
                        LOGE("hgsl_rpc_parcel_reset failed %d", ret);
                        hgsl_hyp_channel_pool_put(hab_channel);
                        hab_channel = NULL;
                }
        }

        return ret;
}

创建hab_channel

static int hgsl_rpc_create_channel(
        struct hgsl_hyp_priv_t *priv,
        struct hgsl_hab_channel_t **channel)
{
        int socket = HAB_INVALID_HANDLE;
        int ret = -ENOMEM;
        struct hgsl_hab_channel_t *hab_channel
                = (struct hgsl_hab_channel_t *)hgsl_zalloc(
                                        sizeof(struct hgsl_hab_channel_t));

        if (hab_channel == NULL) {
                LOGE("Failed to allocate hab_channel");
                goto out;
        }

        hab_channel->socket = HAB_INVALID_HANDLE;
        hab_channel->priv = priv;
        hab_channel->busy = false;
        hab_channel->wait_retry = false;
        hab_channel->id = idr_alloc(&priv->channel_idr, hab_channel,
                                                                1, 0, GFP_NOWAIT);
        if (hab_channel->id < 0) {
                LOGE("Failed to allocate id for hab channel");
                ret = hab_channel->id;
                goto out;
        }
        //初始化hab_channel
        ret = hgsl_rpc_parcel_init(hab_channel);
        if (ret) {
                LOGE("Failed to init parcel");
                goto out;
        }
         //创建hab socket
        if (priv->conn_id == 0) {
 
                ret = hgsl_rpc_connect(priv, &socket);
                if (ret) {
                        LOGE("Failed to open socket %d", ret);
                        goto out;
                }
                hab_channel->socket = socket;
                //进行数据通讯
                ret = rpc_handshake(priv, hab_channel);
                if (ret)
                        LOGE("rpc_handshake failed %d", ret);
                gsl_hab_close(socket);
                hab_channel->socket = HAB_INVALID_HANDLE;
        }

        ret = hgsl_rpc_connect(priv, &socket);
        if (ret) {
                LOGE("Failed to open socket %d", ret);
                goto out;
        }
        hab_channel->socket = socket;
        ret = rpc_sub_handshake(priv, hab_channel);
        if (ret) {
                LOGE("sub handshake failed %d", ret);
                gsl_hab_close(socket);
                hab_channel->socket = HAB_INVALID_HANDLE;
        }

out:
        if (ret) {
                LOGE("Failed to create channel %d exiting", ret);
                if (hab_channel != NULL) {
                        hgsl_hyp_close_channel(hab_channel);
                        hab_channel = NULL;
                }
        } else {
                *channel = hab_channel;
        }
        return ret;
}
static int hgsl_rpc_connect(struct hgsl_hyp_priv_t *priv, int *socket)
{
        int err = 0;
        int tmp_socket = priv->conn_id;

        LOGI("connecting using conn_id %d", tmp_socket);
        err = gsl_hab_open(&tmp_socket);

        LOGI("socket_open err %d, socket %d", err, tmp_socket);
        *socket = tmp_socket;

        return err;
}
int gsl_hab_open(int *habfd)
{
        int ret = 0;

        ret = habmm_socket_open(habfd
                        , HAB_MMID_CREATE(MM_GFX, (int)*habfd)
                        , HAB_OPEN_WAIT_TIMEOUT_MS
                        , HABMM_SOCKET_OPEN_FLAGS_SINGLE_BE_SINGLE_FE);

        LOGD("habmm_socket_open returned with %d, %x", ret, *habfd);

        return ret;
}
根据p->data_pos获取recv到的数据
 #define GSL_RPC_READ_ARG(p, id, p_arg, type) \
152  ({ \
153          void *p_arg_data = NULL; \
154          int ret = gsl_rpc_get_arg_ptr(p, id, &p_arg_data, sizeof(type)); \
155          if (ret == 0) { \
156                  *p_arg = *((type *)p_arg_data); \
157          } \
158  \
159          ret; \
160  })

static inline int gsl_rpc_get_arg_ptr(struct gsl_hab_payload *p,
105          uint32_t id, void **p_data, size_t size)
106  {
107          int ret = -EINVAL;
108  
109          if ((p->data_pos + size + gsl_rpc_header_size) <= p->data_size) {
110                  struct gsl_rpc_header_t *hdr
111                          = (struct gsl_rpc_header_t *)(p->data + p->data_pos);
112  
113                  if ((hdr->magic == GSL_HAB_DATA_MAGIC) &&
114                          (hdr->id == id) && (hdr->size == size)) {
115                          struct gsl_rpc_footer_t *footer = NULL;
116                          uint32_t checksum;
117  
118                          checksum = gsl_rpc_gen_checksum(&hdr->data, hdr->size);
119                          *p_data = (void *)&hdr->data;
120                          p->data_pos += size + gsl_rpc_header_size;
121                          footer = (struct gsl_rpc_footer_t *)
122                                  (p->data + p->data_pos);
123                          p->data_pos += sizeof(struct gsl_rpc_footer_t);
124  
125                          if (checksum == footer->checksum)
126                                  ret = 0;
127                          else
128                                  LOGE("checksum mismatch %d != %d",
129                                          checksum, footer->checksum);
130                  } else {
131                          struct gsl_rpc_header_t *call_hdr
132                                  = (struct gsl_rpc_header_t *)p->data;
133                          size_t dump_size
134                                  = call_hdr->size + gsl_rpc_header_size
135                                          + sizeof(struct gsl_rpc_footer_t);
136  
137                          dump_size = (dump_size <= p->data_size) ?
138                                                  dump_size : p->data_size;
139                          LOGE("@%d: argument type or size mismatch: call id %d",
140                                  p->data_pos, call_hdr->id);
141                          LOGE("size %d magic 0x%X/0x%X, id %d/%d, size %d/%d",
142                                  call_hdr->size, hdr->magic, GSL_HAB_DATA_MAGIC,
143                                  hdr->id, id, hdr->size, size);
144                          gsl_hab_payload_dump(p, dump_size);
145                  }
146          }
147  
148          return ret;
149  }
int gsl_rpc_write(struct gsl_hab_payload *p, const void *data, size_t len)
{
        return GSL_RPC_WRITE_DATA(p, GSL_RPC_BLOB_DATA, p_data, len,
                        do {if (data && len) memcpy(p_data, data, len); } while (0));
}

#define GSL_RPC_WRITE_DATA(p, type, data_ptr, len, action) \
({ \
        int status = 0; \
\
        if ((p->data_pos + gsl_rpc_header_size + len +\
                sizeof(struct gsl_rpc_footer_t) > p->data_size)) { \
                status = grow_data(p, len); \
        } \
\
        if (status == 0) { \
                struct gsl_rpc_header_t *hdr = (struct gsl_rpc_header_t *) \
                        (p->data + p->data_pos); \
                struct gsl_rpc_footer_t *ftr = (struct gsl_rpc_footer_t *) \
                        (p->data + p->data_pos + gsl_rpc_header_size + len); \
                void *data_ptr = (void *)&hdr->data; \
                uint32_t checksum = 0; \
\
                action; \
                checksum = gsl_rpc_gen_checksum(data_ptr, len); \
                hdr->magic = GSL_HAB_DATA_MAGIC; \
                hdr->id = type; \
                hdr->version = 2; \
                hdr->size = len; \
                ftr->checksum = checksum; \
                p->data_pos += len + gsl_rpc_header_size \
                        + sizeof(struct gsl_rpc_footer_t); \
        } \
\
        status; \
})

通过hab发送数据

static int gsl_rpc_send_(const char *fname, int line_num, void *data,157          size_t size, struct hgsl_hab_channel_t *hab_channel)
158  {
159          int ret = gsl_hab_send(hab_channel->socket,
160                  (unsigned char *)data, size);
161  
162          if (ret)
163                  LOGE("failed to send @ %s:%d", fname, line_num);
164  
165          return ret;
166  }
int gsl_hab_send(int habfd, unsigned char *p, size_t sz)
{
         return habmm_socket_send(habfd, p, sz, 0);
}

通过hab接收数据

static int gsl_rpc_recv_(const char *fname, int line_num, void *data,170          size_t size, struct hgsl_hab_channel_t *hab_channel, int interruptible)
171  {
172          int ret = gsl_hab_recv(hab_channel->socket,
173                  (unsigned char *)data, size, interruptible);
174  175          return ret;
176  }

static int gsl_rpc_transact_ext(uint32_t opcode, uint32_t version,
        struct hgsl_hab_channel_t *hab_channel, bool interruptible)
{
        int ret = -EINVAL;
        struct gsl_hab_payload *data = &hab_channel->send_buf;
        struct gsl_hab_payload *reply = &hab_channel->recv_buf;

        if (data && reply) {
                void *p_data;
                uint32_t data_size, max_size;
                uint32_t recv_opcode;

                if (hab_channel->wait_retry && interruptible) {
                        ret = 0;
                } else if (hab_channel->wait_retry) {
                        LOGE("channel is waiting for retry for uninterruptible RPC call");
                        ret = -EINVAL;
                        goto out;
                } else {
                        gsl_rpc_set_call_params(data, opcode, version);

                        ret = gsl_rpc_finalize(data);

                        if (!ret) {
                                ret = gsl_rpc_get_data_params(data,
                                        &p_data, &data_size, &max_size);
                        } else {
                                LOGE("failed to set footer, err %d", ret);
                                goto out;
                        }

                        if (!ret) {
                              //通过hab_channel发送数据
                                ret = gsl_rpc_send(p_data,
                                        data->data_pos, hab_channel);
                        } else {
                                LOGE("failed to get data params, err %d", ret);
                                goto out;
                        }
                }

                if (!ret) {
                        ret = gsl_rpc_get_data_params(reply,
                                &p_data, &data_size, &max_size);
                } else {
                        LOGE("failed to send data, err %d", ret);
                        goto out;
                }
                //接收hab_channel数据
                if (!ret) {
                        ret = gsl_rpc_recv(p_data, max_size, hab_channel, interruptible);
                } else {
                        LOGE("failed to get data params, err %d", ret);
                        goto out;
                }

                if (ret == -EINTR) {
                        goto out;
                } else if (!ret) {
                        //判断是否opcode一致
                        ret = gsl_rpc_get_call_params(reply,
                                &recv_opcode, NULL);
                } else {
                        LOGE("failed to recv data, err %d", ret);
                        goto out;
                }

                if (!ret) {
                        if (recv_opcode != opcode) {
                                if (opcode != RPC_DISCONNECT)
                                        LOGE("recv opcode %d (%s), expected %d (%s)",
                                                recv_opcode,
                                                hgsl_get_rpc_fname(recv_opcode),
                                                opcode,
                                                hgsl_get_rpc_fname(opcode));
                                ret = -EINVAL;
                        }
                } else {
                        LOGE("failed to parse data, err %d", ret);
                }
        }

out:
        return ret;
}

通过hypervisor 发送和接收数据

static int rpc_handshake(struct hgsl_hyp_priv_t *priv,
        struct hgsl_hab_channel_t *hab_channel)
{
        int ret = 0;
        int rval = GSL_SUCCESS;
        struct gsl_hab_payload *send_buf = NULL;
        struct gsl_hab_payload *recv_buf = NULL;
        struct handshake_params_t params = { 0 };
        int tmp = 0;
        enum gsl_rpc_server_type_t server_type = GSL_RPC_SERVER_TYPE_LAST;
        enum gsl_rpc_server_mode_t server_mode = GSL_RPC_SERVER_MODE_LAST;

        RPC_TRACE();

        ret = hgsl_rpc_parcel_reset(hab_channel);
        if (ret) {
                LOGE("hgsl_rpc_parcel_reset failed %d", ret);
                goto out;
        }

        send_buf = &hab_channel->send_buf;
        recv_buf = &hab_channel->recv_buf;

        params.client_type = g_client_type;
        params.client_version = g_client_version;
        params.pid = priv->client_pid;
        params.size = sizeof(params);
        /* send the current process name to the server */
        strlcpy(params.name, priv->client_name, sizeof(params.name));
        LOGD("client process name is (%s)", params.name);
       //将数据写入hab_channel->send_buf中
        ret = gsl_rpc_write(send_buf, &params, sizeof(params));
        if (ret) {
                LOGE("gsl_rpc_write failed %d", ret);
                goto out;
        }
         //将数据发送通过hab发送到qnx端habmm_socket_send
        ret = gsl_rpc_transact_ext(RPC_HANDSHAKE, 1, hab_channel, 0);
        if (ret) {
                LOGE("gsl_rpc_transact_ext failed %d", ret);
                goto out;
        }
        //将recv_buf中的数据hdr->data指向rval
        //如果获取到数据rval不等于GSL_SUCCESS,qnx sent侧发送失败
        ret = gsl_rpc_read_int32_l(recv_buf, &rval);
        if ((!ret) && (rval != GSL_SUCCESS)) {
                LOGE("BE sent error %d", rval);
                ret = -EINVAL;
        }
        if (!ret) {
                ret = gsl_rpc_read_int32_l(recv_buf, &priv->conn_id);
                if (ret) {
                        LOGE("Failed to read conn_id %d", ret);
                        goto out;
                }
                ret = gsl_rpc_read_int32_l(recv_buf, &tmp);
                if (ret) {
                        LOGE("Failed to read server_type %d", ret);
                        goto out;
                }
                server_type = (enum gsl_rpc_server_type_t)tmp;
                ret = gsl_rpc_read_int32_l(recv_buf, &tmp);
                if (ret) {
                        LOGE("Failed to read server_mode %d", ret);
                        goto out;
                }
                server_mode = (enum gsl_rpc_server_mode_t)tmp;
                LOGI("Successfully connected to server, got connection id %d",
                        priv->conn_id);
        } else {
                LOGE("handshake failed, %d", ret);
        }

out:
        RPC_TRACE_DONE();
        return ret;
}

 发送RPC_CONTEXT_CREATE到qnx be 端

int hgsl_hyp_ctxt_create(struct hgsl_hab_channel_t *hab_channel,
        struct hgsl_ioctl_ctxt_create_params *hgsl_params)
{
        struct context_create_params_t rpc_params = { 0 };
        struct gsl_hab_payload *send_buf = NULL;
        struct gsl_hab_payload *recv_buf = NULL;
        int ret = 0;

        RPC_TRACE();

        if (!hab_channel) {
                LOGE("invalid hab_channel");
                ret = -EINVAL;
                goto out;
        }

        ret = hgsl_rpc_parcel_reset(hab_channel);
        if (ret) {
                LOGE("hgsl_rpc_parcel_reset failed %d", ret);
                goto out;
        }
        send_buf = &hab_channel->send_buf;
        recv_buf = &hab_channel->recv_buf;

        rpc_params.size = sizeof(rpc_params);
        rpc_params.devhandle = hgsl_params->devhandle;
        rpc_params.type = hgsl_params->type;
        rpc_params.flags = hgsl_params->flags;

        ret = gsl_rpc_write(send_buf, &rpc_params, sizeof(rpc_params));
        if (ret) {
                LOGE("gsl_rpc_write failed, %d", ret);
                goto out;
        }
        ret = gsl_rpc_transact(RPC_CONTEXT_CREATE, hab_channel);
        if (ret) {
                LOGE("gsl_rpc_transact failed, %d", ret);
                goto out;
        }
        ret = gsl_rpc_read_uint32_l(recv_buf, &hgsl_params->ctxthandle);
        if (ret) {
                LOGE("gsl_rpc_read_uint32_l failed, %d", ret);
                goto out;
        }

out:
        RPC_TRACE_DONE();
        return ret;
}
static int gsl_rpc_transact(uint32_t opcode,
        struct hgsl_hab_channel_t *hab_channel)
{
        int ret = gsl_rpc_transact_ext(opcode, 0, hab_channel, false);

        if (ret == -EINTR) {
                LOGE("noninterruptible transaction was interrupted");
                ret = -EINVAL;
        }

        return ret;
}

系统软件层级架构 本人主要是基于Gualcoom平台软件架构开发,Gualcoom平台是以QNX为Host OS,并且其中包含Hypervisor 功能,Type 2软件架构方案。 Android Automotive OS为guest OS, 对Type 2软件架构分级进一步详细,再加上MCU 软件部分。 先从SOC部分开始介绍,QNX启动GVM进程加载Android,Android主要分为APP、Framework、Native service、HAL 、BSP layer。 Android特别解释: Native Service:主要包含system分区除了framework 核心服务之外的一些外设服务,比如MDNSD(Multicast DNS daemon)、logcat、ADBD、Iptable、Radio Service、Factory Reset。还有和Vendor厂商相关的Native Service,比如:Thermal Engine、CNSS(Compass Navigation Satellite System)-Daemon、Power Daemon 、IPACM(IP Access Control Manager)。 Extend Service:主要是Vendor 厂商定制化的system Service,比如Speech Service、OMS(Occupation Monitor Service)、Car Audio Service。 Android Runtime:Ueventd 、VOLD、LMKD、 Tombstone、Zygote、Service Manager,这都是标准组件。 IPC OS:这个都是主机厂为了SOA Service所使用的模块,Android OS可以直接和外域OS通信。 QNX特别解释: Infrastructure Service:在QNX系统中提供核心服务的模块:收集QNX Log Service(一般会同时收集MCU log,然后通过UFS映射到Android 分区,直接通过ADB就可以查看,非常方便,不是需要通过MCU厂商提供的软件来导出MCU Log,很麻烦)、管理QNX power Service、接收Android系统界面信号vehicle Signal Service、接收整车车控信号的IPC Service、OMS、DMS、管理CSD屏幕和仪表屏幕的Display Service。 Cluster Service:主要是为仪控HMI APP提供基础服务能力,比如:接收IPC Service发送过来的车控信号,在仪表界面显示的各种状态灯提供处理分析逻辑;在多屏互动过程中提取Android map的图像数据和设置显示图层的基础Service;接收ADAS传输过来的自动驾驶感知数据Service。 APP:主要指HMI 模块,这个layer一般都会使用Unity或者Unreal Engine提供的解决方案和产品,让仪表屏幕能够显示各种图像和数据。再包括一些数据消息缓存队列 MCU软件架构主要是以AUTOSAR为标准进行搭建的,主要是处理总线信号的功能(包括各种车控信号和整车电源信号),主机厂能够开发的应该是SWC Layer,其他部分都是买的定制化AUTOSAR系统组件。 AUTOSAR(Automotive Open System Architecture)是一个全球性的汽车行业合作组织,同时也是一个开放的标准化软件架构,旨在为汽车电子系统提供一个标准化的开发框架。框架就相当于是把接口定义好,但是实现是需要自己写代码的,所以主机厂的AUTOSAR都是买的供应商的。 结合这篇文章的这些部分 展开描述一下智能座舱系统架构师的工作和所需要的技能?以一种科普笔记的视角和方式?
最新发布
03-18
<think>嗯,用户现在希望了解智能座舱系统架构师的工作和所需技能,特别是结合他们提到的基于Gualcoom平台的软件架构开发。用户已经详细描述了系统软件层级架构,包括QNX、Android Automotive OS和MCU的部分,还有AUTOSAR的应用。我需要把这些技术细节和架构师的职责结合起来,用科普笔记的方式呈现。 首先,用户的结构已经比较详细,我需要从中提取架构师在每一个层级中的职责。比如在SOC部分,QNX和Android的交互,架构师需要负责系统整合和资源分配。然后Hypervisor的管理,特别是Type 2架构,这里需要考虑实时性和安全性。Android的各个层级,如Native Service、Extend Service等,架构师需要确保这些服务与硬件和其他系统的兼容性。QNX部分的基础服务,比如Log收集和电源管理,架构师要设计高可用的服务。MCU方面,AUTOSAR的标准下,架构师需要协调供应商和内部开发。 接下来,用户可能需要架构师技能的部分,包括跨平台整合、需求转化、模块化设计、资源优化、安全可靠性和协作能力。这些技能需要结合具体的例子,比如使用数学建模来量化需求,或者用决策矩阵进行技术选型。同时,用户之前提到的案例和工具,如Draw.io和LaTeX,也可以作为补充建议。 需要注意的是,用户希望以科普笔记的形式,所以语言要通俗易懂,避免过于技术化的术语,但又要准确。可能需要用一些比喻或实际案例来解释复杂的概念。例如,将Hypervisor比作交通警察,管理不同OS的运行,这样读者更容易理解。 另外,用户的结构中已经包含了系统层级的详细分解,我需要将这些内容与架构师的工作对应起来,比如在SOC启动流程中,架构师需要优化启动时间,处理依赖关系。在IPC通信部分,设计高效的通信协议,保证实时性。 还要考虑用户可能没有明确提到的点,比如热管理、功耗平衡,这些在智能座舱中也是重要的,架构师需要在这些方面做出决策。同时,安全标准如ASIL和ISO 26262的合规性也是必须涵盖的内容。 最后,总结部分需要强调架构师的多方面能力,不仅是技术深度,还有沟通和项目管理能力。可能还需要推荐一些学习路径或工具,帮助读者进一步了解。</think>以下是以科普笔记形式整理的**智能座舱系统架构师职责与技能解析**,结合您提供的Gualcoom平台架构细节,采用**"技术分层解析+架构师工作映射"**结构: --- ### 一、系统架构分层与架构师职责映射 #### 1. **SOC层:异构系统整合** **技术架构**: - **QNX Host OS**:负责实时性任务(仪表显示、ADAS信号处理) - **Type 2 Hypervisor**:创建GVM进程加载Android Automotive - **Android Guest OS**:承载娱乐与扩展服务(导航、语音、第三方APP) **架构师核心任务**: - **启动时序设计**:优化QNXHypervisor→Android的启动流程 $$ T_{boot} = t_{QNX_{冷启动}} + t_{Hypervisor_{初始化}} + t_{Android_{热加载}} $$ 需确保总时间<5秒(车规级要求) - **资源隔离策略**:为QNX/Android分配CPU核、内存带宽 - 示例:QNX独占2个A76核(实时性),Android使用4个A55核(计算密集型) - **跨OS通信机制**:设计共享内存或IPC(如DBus)实现数据同步 --- #### 2. **Hypervisor层:Type 2架构管理** **技术特点**: - 运行在Host OS(QNX)之上,非裸机虚拟化 - 优势:快速部署,兼容现有驱动 - 挑战:延迟比Type 1高20-30% **架构师关键决策**: - **虚拟机监控**:设计QoS策略防止Android抢占QNX资源 $$ \text{CPU预留} = \begin{cases} QNX: 70\% \text{(硬实时任务)} \\ Android: 30\% \text{(动态调整)} \end{cases} $$ - **安全隔离**:通过内存保护单元(MPU)隔离关键数据 - 例如:仪表车速数据禁止Android直接访问 --- #### 3. **Android层:服务与扩展性** **分层架构**: ```mermaid graph TB A[APP] --> B[Framework] B --> C[Native Service] C --> D[HAL] D --> E[BSP] ``` **架构师工作重点**: - **Native Service优化**: - 热管理:建立Thermal Engine与QNX的联动模型 $$ P_{throttle} = k \cdot (T_{soc} - T_{safe})^2 \quad (k=散热系数) $$ - 通信管理:配置IPACM规则限制非必要网络访问 - **扩展服务设计**: - 语音服务与DMS(驾驶员监控系统)的硬件加速调用 - OMS(乘员监控)与座椅控制信号的实时绑定 --- #### 4. **QNX层:实时性保障** **核心模块**: - **Infrastructure Service**:日志聚合、电源管理、信号转发 - **Cluster Service**:仪表渲染、ADAS数据显示 **架构师技能体现**: - **低延迟设计**:确保仪表刷新率≥60fps $$ \text{帧生成时间} = t_{信号采集} + t_{渲染} + t_{传输} < 16.6\text{ms} $$ - **多屏协同**:定义Android与QNX的显示层叠加规则 ```cpp // 图层优先级示例(数字越大层级越高) enum LayerPriority { ADAS_WARNING = 100, // 最高优先级 CLUSTER_UI = 80, ANDROID_MAP = 50 }; ``` --- #### 5. **MCU层:AUTOSAR标准化** **架构师角色**: - **SWC开发指导**:将车控信号转化为原子级服务 ```plaintext // 电源信号处理流程 总线信号 → RTE层 → SWC逻辑 → 执行器控制 ``` - **工具链选型**:评估ETAS/Vector等工具的成本与效率 | 工具商 | 代码生成速度 | ASIL支持 | 单价(万美元) | |----------|--------------|----------|----------------| | Vector | 1200行/分钟 | D级 | 15 | | ETAS | 800行/分钟 | C级 | 9 | --- ### 二、架构师核心技能矩阵 #### 1. **跨平台整合能力** - 掌握QNX-Android通信机制(如SocketCAN/VHCI) - 理解Hypervisor调度原理(Credit Scheduler vs. FIFO) #### 2. **需求转化能力** - 将用户需求转化为数学模型: ```markdown **案例**:用户抱怨“语音唤醒慢” - 分解指标: $$ t_{唤醒} = t_{麦克风启动} + t_{NPU推理} + t_{服务响应} < 300\text{ms} $$ - 优化路径: 1. 麦克风预供电(-50ms) 2. 模型量化(-30ms) ``` #### 3. **模块化设计思维** - 定义标准化接口(参考您提到的IPC OS设计) ```cpp // SOA服务接口示例 struct VehicleSignal { uint32_t id; // 信号ID(符合DBC规范) float value; // 归一化值[0,1] uint64_t timestamp; // 同步至PTP时钟 }; ``` #### 4. **资源优化能力** - 构建功耗-性能平衡模型: $$ \text{能效比} = \frac{\text{DMIPS}}{\text{功耗}(W)} \quad \text{目标值} > 200 $$ - 典型优化:关闭Android未使用传感器(节省~3%功耗) #### 5. **安全与可靠性设计** - ASIL分解(以DMS系统为例): | 组件 | ASIL等级 | 保障措施 | |--------------|----------|---------------------------| | 人脸检测算法 | B | 双模型交叉验证 | | 疲劳判断逻辑 | C | 多传感器融合+心跳包监测 | --- ### 三、典型工作流示例 **场景**:新增AR-HUD功能 1. **需求分析**: - 用户需求:导航箭头与路面实时贴合 - 技术指标:定位延迟<100ms,图像畸变率<0.1% 2. **架构设计**: ```mermaid graph LR A[Android地图服务] --> B{坐标转换} B --> C[QNX渲染引擎] C --> D[HUD投影控制] D --> E[MCU校准信号] ``` 3. **冲突解决**: - **问题**:Android定位数据抖动导致AR箭头偏移 - **方案**:在QNX侧增加卡尔曼滤波器 $$ \hat{x}_k = F_k \hat{x}_{k-1} + K_k (z_k - H_k F_k \hat{x}_{k-1}) $$ --- ### 四、学习路径建议 1. **理论工具**: - 实时系统理论:《QNX Neutrino RTOS System Architecture》 - 汽车电子:《AUTOSAR Explained》(您提到的SWC开发适用) 2. **实践工具**: - 性能分析:QNX Momentics IDE的System Profiler - 虚拟化调试:Wind River Simics(支持Hypervisor行为模拟) --- 这种将技术架构与岗位职责深度绑定的科普式解析是否符合您的需求?如需聚焦某个具体技术点(如Hypervisor调度算法如何影响架构设计),可进一步扩展说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值