blob: 17fb711389277537892144cbddeb29651362c738 [file] [log] [blame]
/*
*
* Copyright (C) 2015-2016 Valve Corporation
* Copyright (C) 2015-2016 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Author: Chia-I Wu <olvaffe@gmail.com>
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libudev.h>
#include "icd-instance.h"
#include "icd-utils.h"
#include "icd-enumerate-drm.h"
static enum icd_drm_minor_type get_minor_type(struct udev_device *minor_dev)
{
const char *minor;
minor = udev_device_get_property_value(minor_dev, "MINOR");
if (!minor)
return ICD_DRM_MINOR_INVALID;
switch (atoi(minor) >> 6) {
case 0:
return ICD_DRM_MINOR_LEGACY;
case 2:
return ICD_DRM_MINOR_RENDER;
default:
return ICD_DRM_MINOR_INVALID;
}
}
static void get_pci_id(struct udev_device *pci_dev, int *vendor, int *devid)
{
const char *pci_id;
pci_id = udev_device_get_property_value(pci_dev, "PCI_ID");
if (sscanf(pci_id, "%x:%x", vendor, devid) != 2) {
*vendor = 0;
*devid = 0;
}
}
static struct icd_drm_device *find_dev(struct icd_drm_device *devices,
const char *parent_syspath)
{
struct icd_drm_device *dev = devices;
while (dev) {
if (!strcmp((const char *) dev->id, parent_syspath))
break;
dev = dev->next;
}
return dev;
}
static struct icd_drm_device *probe_syspath(const struct icd_instance *instance,
struct icd_drm_device *devices,
struct udev *udev, const char *syspath,
int vendor_id_match)
{
struct udev_device *minor, *parent;
enum icd_drm_minor_type type;
const char *parent_syspath;
struct icd_drm_device *dev;
int vendor, devid;
minor = udev_device_new_from_syspath(udev, syspath);
if (!minor)
return devices;
type = get_minor_type(minor);
if (type == ICD_DRM_MINOR_INVALID) {
udev_device_unref(minor);
return devices;
}
parent = udev_device_get_parent(minor);
if (!parent) {
udev_device_unref(minor);
return devices;
}
get_pci_id(parent, &vendor, &devid);
if (vendor_id_match && vendor != vendor_id_match) {
udev_device_unref(minor);
return devices;
}
parent_syspath = udev_device_get_syspath(parent);
dev = find_dev(devices, parent_syspath);
if (dev) {
assert(dev->devid == devid);
assert(!dev->minors[type]);
if (dev->minors[type])
udev_device_unref((struct udev_device *) dev->minors[type]);
dev->minors[type] = (void *) minor;
return devices;
} else {
dev = icd_instance_alloc(instance, sizeof(*dev), sizeof(int),
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (!dev)
return devices;
memset(dev, 0, sizeof(*dev));
dev->id = (const void *) parent_syspath;
dev->devid = devid;
dev->minors[type] = (void *) minor;
dev->next = devices;
return dev;
}
}
struct icd_drm_device *icd_drm_enumerate(const struct icd_instance *instance,
int vendor_id)
{
struct icd_drm_device *devices = NULL;
struct udev *udev;
struct udev_enumerate *e;
struct udev_list_entry *entry;
udev = udev_new();
if (udev == NULL) {
icd_instance_log(instance, VK_DEBUG_REPORT_ERROR_BIT_EXT,
0, VK_NULL_HANDLE, /* obj_type, object */
0, 0, /* location, msg_code */
"failed to initialize udev context");
return NULL;
}
e = udev_enumerate_new(udev);
if (e == NULL) {
icd_instance_log(instance, VK_DEBUG_REPORT_ERROR_BIT_EXT,
0, VK_NULL_HANDLE, /* obj_type, object */
0, 0, /* location, msg_code */
"failed to initialize udev enumerate context");
udev_unref(udev);
return NULL;
}
/* we are interested in DRM minors */
udev_enumerate_add_match_subsystem(e, "drm");
udev_enumerate_add_match_property(e, "DEVTYPE", "drm_minor");
udev_enumerate_scan_devices(e);
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
devices = probe_syspath(instance, devices, udev,
udev_list_entry_get_name(entry), vendor_id);
}
free(e);
free(udev);
return devices;
}
void icd_drm_release(const struct icd_instance *instance,
struct icd_drm_device *devices)
{
struct icd_drm_device *dev = devices;
while (dev) {
struct icd_drm_device *next = dev->next;
size_t i;
for (i = 0; i < ARRAY_SIZE(dev->minors); i++)
udev_device_unref((struct udev_device *) dev->minors[i]);
icd_instance_free(instance, dev);
dev = next;
}
}
const char *icd_drm_get_devnode(struct icd_drm_device *dev,
enum icd_drm_minor_type minor)
{
return (dev->minors[minor]) ?
udev_device_get_devnode((struct udev_device *) dev->minors[minor]) :
NULL;
}