安卓11-HDMI插拔检测流程

hdmi从插入到拔出经过底层一系列检测到应用层,应用层获取hdmi插入状态后又会做出一系列相应的动作,下面梳理了从应用层到底层一步步追踪到芯片的hpd-pin的检测过程。其大致原理就是framework层通过检测/sys/class/extcon/hdmi



frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java +775


    private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
   
        private static final String HDMI_EXIST = "HDMI=1";
        private static final String NAME = "hdmi";
        private final ExtconInfo mHdmi = new ExtconInfo(NAME);  //判断/sys/class/extcon/hdmi文件是否存在

        private boolean init() {
   
            boolean plugged = false;
            try {
   
                plugged = parseStateFromFile(mHdmi); //解析sys/class/extcon/hdmi/state这个文件内容
            } catch (FileNotFoundException e) {
   
                Slog.w(TAG, mHdmi.getStatePath()
                        + " not found while attempting to determine initial state", e);
            } catch (IOException e) {
   
                Slog.e(
                        TAG,
                        "Error reading " + mHdmi.getStatePath()
                                + " while attempting to determine initial state",
                        e);
            }
            startObserving(mHdmi);  //开始监控/sys/class/extcon/hdmi
            return plugged;
        }

        @Override
        public void updateState(ExtconInfo extconInfo, String eventName, Boolean state) {
   //通过这里更新hdmi plug状态,这个转态来自hal层
                 //这里的变化来自hdmicec_event.cpp 
						mDefaultDisplayPolicy.setHdmiPlugged(state);
        }

        @Override
        public Boolean parseState(ExtconInfo extconIfno, String state) {
   
            // extcon event state changes from kernel4.9
            // new state will be like STATE=HDMI=1
            return state.contains(HDMI_EXIST);
        }
    }

    void initializeHdmiStateInternal() {
   
        boolean plugged = false;
        // watch for HDMI plug messages if the hdmi switch exists
        if (new File("/sys/devices/virtual/switch/hdmi/state").exists()) {
   
            mHDMIObserver.startObserving("DEVPATH=/devices/virtual/switch/hdmi");

            final String filename = "/sys/class/switch/hdmi/state";
            FileReader reader = null;
            try {
   
                reader = new FileReader(filename);
                char[] buf = new char[15];
                int n = reader.read(buf);
                if (n > 1) {
   
                    plugged = 0 != Integer.parseInt(new String(buf, 0, n - 1));
                }
            } catch (IOException ex) {
   
                Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
            } catch (NumberFormatException ex) {
   
                Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
            } finally {
   
                if (reader != null) {
   
                    try {
   
                        reader.close();
                    } catch (IOException ex) {
   
                    }
                }
            }
        } else if (ExtconUEventObserver.extconExists()   //走这里  判断sys/class/extcon是否存在
                && ExtconUEventObserver.namedExtconDirExists(HdmiVideoExtconUEventObserver.NAME)) {
   
            Log.i("fan","xtconUEventObserver.extconExists");
            HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();//新建一个hdmi观察者,检测hdmi hpd引脚的变化
            plugged = observer.init();
            mHDMIObserver = observer;
        } else if (localLOGV) {
   
            Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
        }

        // This dance forces the code in setHdmiPlugged to run.
        // Always do this so the sticky intent is stuck (to false) if there is no hdmi.
        mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
    }
frameworks/base/core/java/android/view/WindowManagerPolicyConstants.java:78    
	String ACTION_HDMI_PLUGGED = "android.intent.action.HDMI_PLUGGED";

    public void setHdmiPlugged(boolean plugged) {
   
        setHdmiPlugged(plugged, false /* force */);
    }

    public void setHdmiPlugged(boolean plugged, boolean force) {
   
        if (force || mHdmiPlugged != plugged) {
   
            mHdmiPlugged = plugged;
            mService.updateRotation(true /* alwaysSendConfiguration */, true /* forceRelayout */);
            final Intent intent = new Intent(ACTION_HDMI_PLUGGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);//通知系统hdmi插入状态
        }
    }

    boolean isHdmiPlugged() {
   
        return mHdmiPlugged;
    }    
frameworks/base/services/core/java/com/android/server/ExtconUEventObserver.java    
     public static boolean extconExists() {
   
        File extconDir = new File("/sys/class/extcon"); //检查这个文件是否存在,对应上面的else if (ExtconUEventObserver.extconExists() 
        return extconDir.exists() && extconDir.isDirectory();
    }

\hardware\rockchip\hdmicec\hdmicec_event.cpp
static void *uevent_loop(void *param)
{
   
	hdmi_cec_context_t * ctx = reinterpret_cast<hdmi_cec_context_t *>(param);
	char thread_name[64] = HDMI_CEC_UEVENT_THREAD_NAME;
	hdmi_event_t cec_event;
	struct pollfd pfd[2];
	int fd[2];
	int ret, i;

	prctl(PR_SET_NAME, (unsigned long) &thread_name, 0, 0, 0);
	setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);

	fd[0] = ctx->fd;
	if (fd[0] < 0) {
   
		ALOGE ("%s:not able to open cec state node", __func__);
		return NULL;
	}

	pfd[0].fd = fd[0];
	if (pfd[0].fd >= 0)
		pfd[0].events = POLLIN | POLLRDNORM | POLLPRI;

	while (true) {
   
                usleep(1000);
		int err = poll(&pfd[0], 1, 20);

		if (!err) {
   
			continue;
		} else if(err > 0) {
   
			if (!ctx->enable || !ctx->system_control)
				continue;
			ALOGD("poll revent:%02x\n", pfd[0].revents);
			memset(&cec_event, 0, sizeof(hdmi_event_t));
			if (pfd[0].revents & (POLLIN)) {
   
				struct cec_msg cecframe;

				ALOGD("poll receive msg\n");
				ret = ioctl(pfd[0].fd, CEC_RECEIVE, &cecframe);
				if (!ret) {
   
					cec_event.type = HDMI_EVENT_CEC_MESSAGE;
					cec_event.dev = &ctx->device;
					cec_event.cec.initiator = (cec_logical_address_t)(cecframe.msg[0] >> 4);
					cec_event.cec.destination = (cec_logical_address_t)(cecframe.msg[0] & 0x0f);
					cec_event.cec.length = cecframe.len - 1;
					cec_event.cec.body[0] = cecframe.msg[1];
					if (!validcecmessage(cec_event)) {
   
						for (ret = 0; ret < cec_event.cec.length; ret++)
						     cec_event.cec.body [ret + 1] = cecframe.msg[ret + 2];
						for (i = 0; i < cecframe.len; i++)
							ALOGD("poll receive msg[%d]:%02x\n", i, cecframe.msg[i]);
						if (ctx->event_callback)
							ctx->event_callback(&cec_event, ctx->cec_arg);
					} else {
   
						ALOGE("%s cec_event length > 15 ", __func__);
					}
				} else {
   
					ALOGE("%s hdmi cec read error", __FUNCTION__);
				}
			}

			if (pfd[0].revents & (POLLPRI)) {
   
				int state = -1;
				struct cec_event event;

				ALOGI("poll receive event\n");
				ret = ioctl(pfd[0].fd, CEC_DQEVENT, &event);//取得一个cec事件,然后判断事件的状态,此部分内容在内核层
				if (!ret) {
   
					ALOGD("event:%d\n", event.event);
					if (event.event == CEC_EVENT_PIN_HPD_LOW) {
   //获取底层hpdin管教状态
						ALOGI("CEC_EVENT_PIN_HPD_LOW\n");
						ctx->hotplug = false;
						cec_event.type = HDMI_EVENT_HOT_PLUG;
						cec_event.dev = &ctx->device;
						cec_event.hotplug.connected = HDMI_NOT_CONNECTED;
						cec_event.hotplug.port_id = HDMI_CEC_PORT_ID;
						if (ctx->event_callback)
							ctx->event_callback(&cec_event, ctx->cec_arg);
					} else if (event.event == CEC_EVENT_PIN_HPD_HIGH) {
   //高为连接
						ALOGI("CEC_EVENT_PIN_HPD_HIGH\n");
						ctx->hotplug = true;
						cec_event.type = HDMI_EVENT_HOT_PLUG;
						cec_event.dev = &ctx->device;
						cec_event.hotplug.connected = HDMI_CONNECTED;
						cec_event.hotplug.port_id = HDMI_CEC_PORT_ID;
						if (ctx->event_callback)
							ctx->event_callback(&cec_event, ctx->cec_arg);
					} else if (event.event == CEC_EVENT_STATE_CHANGE) {
   
						ALOGD("adapt state change,phy_addr:%x,flags:%x\n", event.state_change.phys_addr, event.flags);

						/*
						 * Before cec HAL is initialized, hdmi hpd state may be
						 * changed. So we should confirm the hpd status
						 * after cec is initialized(Kernel will report
						 * CEC_EVENT_FL_INITIAL_STATE to notify HAL that
						 * initialization is done).
						 */
						if (event.flags & CEC_EVENT_FL_INITIAL_STATE) {
   
							ALOGD("cec adapter init complete, get connect state\n");
							ctx->hotplug = get_hpd_state_from_node(ctx);
							ctx->cec_init = true;

							/*
							 * Framework will start la polling when box turn on,
							 * In addition, as soon as framewrok receives hdmi
							 * plug in, it will start la polling immediately.
							 * There is not need to report plug in event if hdmi
							 * is connecting when box turn on. So we should report
							 * hdmi plug out only.
							 */
							if (!ctx->hotplug)
								report_hdp_event(ctx, ctx->hotplug);
						}
						ctx->phy_addr = event.state_change.phys_addr;
					}
				} else {
   
					ALOGE("%s cec event get err, ret:%d\n", __func__, ret);
				}
			}
		} else {
   
			ALOGE("%s: cec poll failed errno: %s", __FUNCTION__,
			strerror(errno));
			continue;
		}
	}
	return NULL;
}
hardware/rockchip/hdmicec/hdmicec_event.cpp
static void report_hdp_event(hdmi_cec_context_t* ctx, bool hpd)
{
   
        hdmi_event_t cec_event;

        cec_event.type = HDMI_EVENT_HOT_PLUG;
        cec_event.dev = &ctx->device;
        if (hpd)
                cec_event.hotplug.connected = HDMI_CONNECTED;
        else
                cec_event.hotplug.connected = HDMI_NOT_CONNECTED;
        cec_event.hotplug.port_id = HDMI_CEC_PORT_ID;
        if (ctx->event_callback)
                ctx->event_callback(&cec_event, ctx->cec_arg);
}
hardware/rockchip/hdmicec/hdmi_cec.cpp
static void hdmi_cec_register_event_callback(const struct hdmi_cec_device* dev,
                                             event_callback_t callback, void* arg)
{
   
        struct hdmi_cec_context_t* ctx = (struct hdmi_cec_context_t*)dev;

        ALOGI("%s", __func__);
        ctx->event_callback = callback;
        ctx->cec_arg = arg;
}	
static int hdmi_cec_device_open(const struct hw_module_t* module, const char* name,
                                struct hw_device_t** device)
{
     
	....
	  dev->device.register_event_callback = hdmi_cec_register_event_callback;
	  ......
hardware/interfaces/tv/cec/2.0/default/HdmiCec.cpp 
IHdmiCec* HIDL_FETCH_IHdmiCec(const char* hal) {
     
    hdmi_cec_device_t* hdmi_cec_device;
    int ret = 0;
    const hw_module_t* hw_module = nullptr;
    hdmi_cec_device_t* hdmi_cec_device;
    int ret = 0;
    const hw_module_t* hw_module = nullptr;

    ret = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, &hw_module);  //找到ECE模块
    if (ret == 0) {
   
        ret = hdmi_cec_open(hw_module, &hdmi_cec_device); //打开设备
        if (ret != 0) {
   
            LOG(ERROR) << "hdmi_cec_open " << hal << " failed: " << ret;
        }
    } else {
   
        LOG(ERROR) << "hw_get_module " << hal << " failed: " << ret;
    }

    if (ret == 0) {
   
        return new HdmiCec(hdmi_cec_device);
    } else {
   
        LOG(ERROR) << "Passthrough failed to load legacy HAL.";
        return nullptr;
    }
}
	  
Return<void> HdmiCec::setCallback(const sp<IHdmiCecCallback>& callback) {
   
    if (mCallback != nullptr) {
   
        mCallback->unlinkToDeath(this);
        mCallback = nullptr;
    }

    if (callback != nullptr) {
   
        mCallback = callback;
        mCallback->linkToDeath(this, 0 /*cookie*/);
        mDevice->register_event_callback(mDevice, eventCallback, nullptr);  //注册回调通知framework层
    }
    return Void();
}
	  
Kernel/drivers/media/cec/cec-api.c 
static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 case CEC_DQEVENT:
                return cec_dqevent(adap, fh, block, parg);  //hal层调用这里获取一个cec事件,既然有获取事件就有把事件放入事件队列的地方
 drivers/media/cec/cec-adap.c
                
void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts)
{
   
        struct cec_event ev = {
   
                .event = is_high ? CEC_EVENT_PIN_HPD_HIGH :
                                   CEC_EVENT_PIN_HPD_LOW,
        };
        struct cec_fh *fh;

        if (!adap)
                return;
        /* hdmi HPD may occur before devnode is registered */
        if (!adap->devnode.registered)
                return;
        mutex_lock(&adap->devnode.lock);
        list_for_each_entry(fh, &adap->devnode.fhs, list)
                cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); //插入一个cec事件,把这个事件放入到ece事件队列,供hal层获取,hal层获取后传到framework层
        mutex_unlock(&adap->devnode.lock);
}
	
	

	
	

	
	

相关推荐

  1. 11-HDMI检测流程

    2024-02-21 17:40:03       43 阅读
  2. 11菜单实现hdmi-4K动态切换

    2024-02-21 17:40:03       51 阅读
  3. 11修改power按键功能

    2024-02-21 17:40:03       50 阅读
  4. Delphi v11 权限申请

    2024-02-21 17:40:03       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-21 17:40:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-21 17:40:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-02-21 17:40:03       87 阅读
  4. Python语言-面向对象

    2024-02-21 17:40:03       96 阅读

热门阅读

  1. Python 安装和使用的IDE

    2024-02-21 17:40:03       45 阅读
  2. 微服务开发工具及环境搭建

    2024-02-21 17:40:03       54 阅读
  3. php 函数 三 公钥私钥

    2024-02-21 17:40:03       54 阅读
  4. 【ES】es查询term、match、match_phrase、mast_not、mast...

    2024-02-21 17:40:03       52 阅读
  5. Python自动化部署与配置管理:Ansible与Docker

    2024-02-21 17:40:03       52 阅读
  6. python3调用阿里云openapi脚本 - 生产环境

    2024-02-21 17:40:03       49 阅读
  7. 本地模拟发送、接收RabbitMQ数据

    2024-02-21 17:40:03       56 阅读
  8. 怎样重置ubuntu mysql8密码

    2024-02-21 17:40:03       52 阅读
  9. ARM体系架构

    2024-02-21 17:40:03       45 阅读