0

I want to monitor the HDMI cable state in my code. there is a file that changes when the cable gets connected and disconnected.

$cat /sys/devices/soc0/soc/20e0000.hdmi_video/cable_state
plugin
$cat /sys/devices/soc0/soc/20e0000.hdmi_video/cable_state
plugout

I used QFileSystemWatcher to monitor this file, but it didn't work.

    QFileSystemWatcher watcher;
    watcher.addPath("/sys/devices/soc0/soc/20e0000.hdmi_video/cable_state");
    QObject::connect(&watcher, &QFileSystemWatcher::fileChanged,
    [this]( const QString& path ) {
        qDebug() << path;
        QFile file(path);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return;
        auto line = file.readLine();
        qDebug() << line;
    });

I think that it doesn't work because this file belongs to the sysfs and is not an ordinary file. Is there any way to access a platform device attribute and get notified, without file monitoring, with code?

part of the code that define cable_state attribute in kernel:

static ssize_t mxc_hdmi_show_state(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    struct mxc_hdmi *hdmi = dev_get_drvdata(dev);

    if (hdmi->cable_plugin == false)
        strcpy(buf, "plugout\n");
    else
        strcpy(buf, "plugin\n");

    return strlen(buf);
}

static DEVICE_ATTR(cable_state, S_IRUGO, mxc_hdmi_show_state, NULL);
Nader
  • 318
  • 1
  • 6
  • 16

2 Answers2

1

You are correct: files in /sys are not regular files. Their contents are magic'ed up by the kernel when you actually read them.

You will have to resort to polling instead:

class HDMIWatcher : public QObject {
Q_OBJECT
public:
  HDMIWatcher(const QString& path, QObject* parent) : QObject(parent), path(file), timer(new QTimer(this)) {
    QObject::connect(timer, &QTimer::timeout, this, &HDMIWatcher::poll);
    timer->setInterval(5000);
    poll();
  }

public slots:
  void start() {
    timer->start();
  }

  void poll() {
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;
    auto line = file.readLine();
    if (last.isEmpty()) {
      last = line;
    }
    if (line != last) {
      emit changeDetected(last, line);
      last = line;
    }
  }

public signals:
  void changeDetected(const QString& old, const QString& new);

private:
  QString last;
};
Botje
  • 26,269
  • 3
  • 31
  • 41
  • thank you for your answer. I have written something like your code. I used qthread instead of qtimer. I want to know that is there any way to access the platform device attribute directly from the code. – Nader Dec 12 '22 at 12:09
  • No idea what a "platform device attribute is". Best to ask a follow-up question. – Botje Dec 12 '22 at 12:38
  • @nader, yes. From the callbacks in the Linux kernel code. Otherwise you need to supply address to some other data structures beforehand. Or do you ask about your user space code? Then definitely no way except reading sysfs. – 0andriy Dec 12 '22 at 15:16
  • @0andriy I ask about the user space access. Then polling is the only way that I can use? – Nader Dec 12 '22 at 19:31
  • @nader, The function in your code is absent in the vanilla Linux kernel, that's why I can't be 100% sure, since your kernel is patches by 3rd party. – 0andriy Dec 12 '22 at 21:22
  • 1
    @nader But it looks like that code uses standard sysfs attribute mechanism: https://elixir.bootlin.com/linux/latest/source/fs/sysfs/file.c#L205 from which you may deduct the supported file operations on those files: https://elixir.bootlin.com/linux/latest/source/fs/kernfs/file.c#L1001. The rest I leave to you for the homework on how to improve efficiency of your user space code. – 0andriy Dec 12 '22 at 21:30
0

The sysfs entry you are working with could be made to "appear" to have updated which would make your QFileSystemWatcher work the way you would expect, if only the Linux HDMI driver had made use of the function sysfs_notify when the "behind the scenes" data in the driver gets updated as a result of physically "plugging in" or "plugging out" the HDMI cable.

Short of patching the driver, and assuming you are using the mxc_hdmi driver like the driver code in your example seems to match up with...

You could still use the QFileSystemWatcher if you create a udev rule to update a file (of your choosing) whenever the HDMI "cable_connected" state changed. Then you would use that QFileSystemWatcher class to "watch" the file that the udev rule updates.

If you were willing to do a bit more legwork, you could cut out the udev rule and just monitor the udev signals yourself using libudev or sd-device. It might be a more "self-contained" solution, but it probably requires a bit more complexity in your userspace code.

Robert McLean
  • 196
  • 1
  • 8