// type 指定打开设备的的类型,主要有以下三类:
#define DRM_NODE_PRIMARY 0
#define DRM_NODE_CONTROL 1
#define DRM_NODE_RENDER 2
drm_public int drmOpenWithType(const char *name, const char *busid, int type)
{
if (name != NULL && drm_server_info && drm_server_info->load_module && !drmAvailable()) {
// 试图去加载核模块
if (!drm_server_info->load_module(name)) {
drmMsg("[drm] failed to load kernel module \"%s\"\n", name);
return -1;
}
}
// 如果提供了busid,name参数就不用了,使用busid去打开设备节点
if (busid) {
int fd = drmOpenByBusid(busid, type);
if (fd >= 0)
return fd;
}
// 最后使用指定的设备名称去打开
if (name)
return drmOpenByName(name, type);
return -1;
}
/**
* 第一个参数 name 为驱动的名称, 第二个参数为设备节点的类型.
* 函数调用成功则返回一个文件描述符, 调用失败返回一个负的值.
*
* \internal
* This function opens the first minor number that matches the driver name and isn't already in use.
* If it's in use it then it will already have a bus ID assigned.
*
* 通过调用 drmOpenMinor(), drmGetVersion() and drmGetBusid(). 来实现的
*/
static int drmOpenByName(const char *name, int type)
{
int i;
int fd;
drmVersionPtr version;
char * id;
int base = drmGetMinorBase(type);
if (base < 0)
return -1;
/*
* Open the first minor number that matches the driver name and isn't
* already in use. If it's in use it will have a busid assigned already.
*/
// DRM_MAX_MINOR = 16
for (i = base; i < base + DRM_MAX_MINOR; i++)
{
if ((fd = drmOpenMinor(i, 1, type)) >= 0)
{
// 从内核态获得drm的名字,但是怎么区分 loongson-drm 和 etnaviv 呢 ?
// etnaviv 只是一个renderer node,用上边的type来区分。
if ((version = drmGetVersion(fd)))
{
if (!strcmp(version->name, name))
{
drmFreeVersion(version);
id = drmGetBusid(fd);
drmMsg("drmGetBusid returned '%s'\n", id ? id : "NULL");
if (!id || !*id) {
if (id)
drmFreeBusid(id);
return fd;
}
else
drmFreeBusid(id);
}
else
drmFreeVersion(version);
}
close(fd);
}
}
#ifdef __linux__
// 后向兼容性的东西,打开/proc/dri/i/name 。。。
#endif
return -1;
}
/**
* Open the DRM device
*
* \param minor device minor number.
* \param 如果 create 置位则调用drmOpenDevice(), 允许创建设备 ?.
*
* 成功返回一个文件描述符,失败则返回一个负的值.
*
* \internal
* Calls if \p create is set, otherwise assembles the device
* name from \p minor and opens it.
*/
static int drmOpenMinor(int minor, int create, int type)
{
// 获取设备节点的名称,如果type = DRM_NODE_RENDER, /dev/dri/renderD128,129,...,143
const char *dev_name = drmGetDeviceName(type);
// 上边函数调用的create为1, 这里就只分析create为1的情况
if (create)
return drmOpenDevice(makedev(DRM_MAJOR, minor), minor, type);
// 。。。
}
static const char *drmGetDeviceName(int type)
{
switch (type) {
case DRM_NODE_PRIMARY:
return DRM_DEV_NAME;
case DRM_NODE_CONTROL:
return DRM_CONTROL_DEV_NAME;
case DRM_NODE_RENDER:
return DRM_RENDER_DEV_NAME;
}
return NULL;
}
/**
* Query the driver version information.
*
* \param fd file descriptor.
*
* \return pointer to a drmVersion structure which should be freed with
* drmFreeVersion().
*
* \note Similar information is available via /proc/dri.
*
* \internal
* It gets the version information via successive DRM_IOCTL_VERSION ioctls,
* first with zeros to get the string lengths, and then the actually strings.
* It also null-terminates them since they might not be already.
*/
drm_public drmVersionPtr drmGetVersion(int fd)
{
drmVersionPtr retval;
drm_version_t *version = drmMalloc(sizeof(*version));
if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
drmFreeKernelVersion(version);
return NULL;
}
if (version->name_len)
version->name = drmMalloc(version->name_len + 1);
if (version->date_len)
version->date = drmMalloc(version->date_len + 1);
if (version->desc_len)
version->desc = drmMalloc(version->desc_len + 1);
if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
drmMsg("DRM_IOCTL_VERSION: %s\n", strerror(errno));
drmFreeKernelVersion(version);
return NULL;
}
/* The results might not be null-terminated strings, so terminate them. */
if (version->name_len) version->name[version->name_len] = '\0';
if (version->date_len) version->date[version->date_len] = '\0';
if (version->desc_len) version->desc[version->desc_len] = '\0';
retval = drmMalloc(sizeof(*retval));
drmCopyVersion(retval, version);
drmFreeKernelVersion(version);
return retval;
}
#include <sys/sysmacros.h>
dev_t makedev(unsigned int maj, unsigned int min);
unsigned int major(dev_t dev);
unsigned int minor(dev_t dev);
设备 ID 由两部分组成 主ID和子ID,主ID用来辨识设备的类别,子ID用来标识这个类别里面设备的一个特定实例
设备 ID 用 dev_t 来表示。 makedev()用给予的主ID和子ID去生成一个设备ID,这个设别ID是可以传入 mknod的。
major() and minor() 函数则执行相反的工作,给予一个设备id,返回主ID和子ID。
#ifndef DRM_MAJOR
#define DRM_MAJOR 226 /* Linux */
#endif
UDEV 在meson 的 config.h中是定义了的, 所以我们只看使用udev的代码,非udev的我给去掉了。
/**
* Open the DRM device, creating it if necessary.
*
* \param dev major and minor numbers of the device.
* \param minor minor number of the device.
*
* \return a file descriptor on success, or a negative value on error.
*
* \internal
* Assembles the device name from \p minor and opens it, creating the device
* special file node with the major and minor numbers specified by \p dev and
* parent directory if necessary and was called by root.
*/
static int drmOpenDevice(dev_t dev, int minor, int type)
{
stat_t st;
char buf[DRM_NODE_NAME_MAX];
int fd;
mode_t devmode = DRM_DEV_MODE, serv_mode;
gid_t serv_group;
// 这块还是要整出 /dev/dri/renderD128
const char * dev_name = drmGetDeviceName(type);
sprintf(buf, dev_name, DRM_DIR_NAME, minor);
drmMsg("drmOpenDevice: node name is %s\n", buf);
if (drm_server_info && drm_server_info->get_perms) {
drm_server_info->get_perms(&serv_group, &serv_mode);
devmode = serv_mode ? serv_mode : DRM_DEV_MODE;
devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
}
/* if we modprobed then wait for udev */
{
int udev_count = 0;
wait_for_udev:
if (stat(DRM_DIR_NAME, &st)) {
usleep(20);
udev_count++;
if (udev_count == 50)
return -1;
goto wait_for_udev;
}
if (stat(buf, &st)) {
usleep(20);
udev_count++;
if (udev_count == 50)
return -1;
goto wait_for_udev;
}
}
// 核心就是打开 /dev/dri/renderD128,返回一个 fd
fd = open(buf, O_RDWR | O_CLOEXEC, 0);
drmMsg("drmOpenDevice: open result is %d, (%s)\n", fd, fd < 0 ? strerror(errno) : "OK");
if (fd >= 0)
return fd;
return -errno;
}