1 /*
2  * Copyright (c) 2015 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  * To compile standalone: gcc -o dri3info dri3info.c `pkg-config --cflags --libs xcb-dri3 x11-xcb xrandr xxf86vm libdrm`
24  */
25 
26 #include <X11/Xlib.h>
27 #include <X11/Xlib-xcb.h>
28 #include <xcb/xcb.h>
29 #include <xcb/dri3.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <drm.h>
37 #include <xf86drm.h>
38 
39 #include <X11/extensions/Xrandr.h>
40 #include <X11/extensions/xf86vmode.h>
41 
dri3_query_version(Display * dpy,int * major,int * minor)42 static int dri3_query_version(Display *dpy, int *major, int *minor)
43 {
44 	xcb_connection_t *c = XGetXCBConnection(dpy);
45 	xcb_dri3_query_version_reply_t *reply;
46 	xcb_generic_error_t *error;
47 
48 	*major = *minor = -1;
49 
50 	reply = xcb_dri3_query_version_reply(c,
51 					     xcb_dri3_query_version(c,
52 								    XCB_DRI3_MAJOR_VERSION,
53 								    XCB_DRI3_MINOR_VERSION),
54 					     &error);
55 	free(error);
56 	if (reply == NULL)
57 		return -1;
58 
59 	*major = reply->major_version;
60 	*minor = reply->minor_version;
61 	free(reply);
62 
63 	return 0;
64 }
65 
dri3_exists(Display * dpy)66 static int dri3_exists(Display *dpy)
67 {
68 	const xcb_query_extension_reply_t *ext;
69 	int major, minor;
70 
71 	ext = xcb_get_extension_data(XGetXCBConnection(dpy), &xcb_dri3_id);
72 	if (ext == NULL || !ext->present)
73 		return 0;
74 
75 	if (dri3_query_version(dpy, &major, &minor) < 0)
76 		return 0;
77 
78 	return major >= 0;
79 }
80 
dri3_open(Display * dpy)81 static int dri3_open(Display *dpy)
82 {
83 	xcb_connection_t *c = XGetXCBConnection(dpy);
84 	xcb_dri3_open_cookie_t cookie;
85 	xcb_dri3_open_reply_t *reply;
86 
87 	if (!dri3_exists(dpy))
88 		return -1;
89 
90 	cookie = xcb_dri3_open(c, RootWindow(dpy, DefaultScreen(dpy)), None);
91 	reply = xcb_dri3_open_reply(c, cookie, NULL);
92 
93 	if (!reply)
94 		return -1;
95 
96 	if (reply->nfd != 1)
97 		return -1;
98 
99 	return xcb_dri3_open_reply_fds(c, reply)[0];
100 }
101 
get_device_path(int fd,char * buf,int len)102 static void get_device_path(int fd, char *buf, int len)
103 {
104 	struct stat remote, local;
105 	int i;
106 
107 	if (fstat(fd, &remote))
108 		goto out;
109 
110 	for (i = 0; i < 16; i++) {
111 		snprintf(buf, len, "/dev/dri/card%d", i);
112 		if (stat(buf, &local))
113 			continue;
114 
115 		if (local.st_mode == remote.st_mode &&
116 		    local.st_rdev == remote.st_rdev)
117 			return;
118 
119 		snprintf(buf, len, "/dev/dri/renderD%d", i + 128);
120 		if (stat(buf, &local))
121 			continue;
122 
123 		if (local.st_mode == remote.st_mode &&
124 		    local.st_rdev == remote.st_rdev)
125 			return;
126 	}
127 
128 out:
129 	strncpy(buf, "unknown path", len);
130 }
131 
get_driver_name(int fd,char * name,int len)132 static void get_driver_name(int fd, char *name, int len)
133 {
134 	drm_version_t version;
135 
136 	memset(name, 0, len);
137 	memset(&version, 0, sizeof(version));
138 	version.name_len = len;
139 	version.name = name;
140 
141 	(void)drmIoctl(fd, DRM_IOCTL_VERSION, &version);
142 }
143 
compute_refresh_rate_from_mode(long n,long d,unsigned flags,int32_t * numerator,int32_t * denominator)144 static int compute_refresh_rate_from_mode(long n, long d, unsigned flags,
145 					   int32_t *numerator,
146 					   int32_t *denominator)
147 {
148 	int i;
149 
150 	/* The mode flags are only defined privately to the Xserver (in xf86str.h)
151 	 * but they at least bit compatible between VidMode, RandR and DRM.
152 	 */
153 # define V_INTERLACE 0x010
154 # define V_DBLSCAN   0x020
155 
156 	if (flags & V_INTERLACE)
157 		n *= 2;
158 	else if (flags & V_DBLSCAN)
159 		d *= 2;
160 
161 	/* The OML_sync_control spec requires that if the refresh rate is a
162 	 * whole number, that the returned numerator be equal to the refresh
163 	 * rate and the denominator be 1.
164 	 */
165 
166 	if (n % d == 0) {
167 		n /= d;
168 		d = 1;
169 	}
170 	else {
171 		static const unsigned f[] = { 13, 11, 7, 5, 3, 2, 0 };
172 
173 		/* This is a poor man's way to reduce a fraction.  It's far from
174 		 * perfect, but it will work well enough for this situation.
175 		 */
176 
177 		for (i = 0; f[i] != 0; i++) {
178 			while (n % f[i] == 0 && d % f[i] == 0) {
179 				d /= f[i];
180 				n /= f[i];
181 			}
182 		}
183 	}
184 
185 	*numerator = n;
186 	*denominator = d;
187 	return 1;
188 }
189 
RRGetMscRate(Display * dpy,int32_t * numerator,int32_t * denominator)190 static int RRGetMscRate(Display *dpy, int32_t *numerator, int32_t *denominator)
191 {
192 	int ret = 0;
193 	Window root = RootWindow(dpy, DefaultScreen(dpy));
194 	XRRScreenResources *res;
195 	int rr_event, rr_error;
196 	RROutput primary;
197 	RRMode mode = 0;
198 	int n;
199 
200 	if (!XRRQueryExtension(dpy, &rr_event, &rr_error))
201 		return ret;
202 
203 	res = XRRGetScreenResourcesCurrent(dpy, root);
204 	if (res == NULL)
205 		return ret;
206 
207 	/* Use the primary output if specified, otherwise
208 	 * use the mode on the first enabled crtc.
209 	 */
210 	primary = XRRGetOutputPrimary(dpy, root);
211 	if (primary) {
212 		XRROutputInfo *output;
213 
214 		output = XRRGetOutputInfo(dpy, res, primary);
215 		if (output != NULL) {
216 			if (output->crtc) {
217 				XRRCrtcInfo *crtc;
218 
219 				crtc = XRRGetCrtcInfo(dpy, res, output->crtc);
220 				if (crtc) {
221 					mode = crtc->mode;
222 					XRRFreeCrtcInfo(crtc);
223 				}
224 			}
225 			XRRFreeOutputInfo(output);
226 		}
227 	}
228 
229 	for (n = 0; mode == 0 && n < res->ncrtc; n++) {
230 		XRRCrtcInfo *crtc;
231 
232 		crtc = XRRGetCrtcInfo(dpy, res, res->crtcs[n]);
233 		if (crtc) {
234 			mode = crtc->mode;
235 			XRRFreeCrtcInfo(crtc);
236 		}
237 	}
238 
239 	for (n = 0; n < res->nmode; n++) {
240 		if (res->modes[n].id == mode) {
241 			ret = compute_refresh_rate_from_mode(res->modes[n].dotClock,
242 							     res->modes[n].hTotal*res->modes[n].vTotal,
243 							     res->modes[n].modeFlags,
244 							     numerator, denominator);
245 			break;
246 		}
247 	}
248 
249 	XRRFreeScreenResources(res);
250 	return ret;
251 }
252 
VMGetMscRate(Display * dpy,int32_t * numerator,int32_t * denominator)253 static int VMGetMscRate(Display *dpy, int32_t *numerator, int32_t *denominator)
254 {
255 	XF86VidModeModeLine mode_line;
256 	int dot_clock;
257 	int i;
258 
259 	if (XF86VidModeQueryVersion(dpy, &i, &i) &&
260 	    XF86VidModeGetModeLine(dpy, DefaultScreen(dpy), &dot_clock, &mode_line))
261 		return compute_refresh_rate_from_mode(dot_clock * 1000,
262 						      mode_line.vtotal * mode_line.htotal,
263 						      mode_line.flags,
264 						      numerator, denominator);
265 
266 	return 0;
267 }
268 
get_refresh_rate(Display * dpy,int32_t * numerator,int32_t * denominator)269 static int get_refresh_rate(Display *dpy,
270 			     int32_t *numerator,
271 			     int32_t *denominator)
272 {
273 	if (RRGetMscRate(dpy, numerator, denominator))
274 		return 1;
275 
276 	if (VMGetMscRate(dpy, numerator, denominator))
277 		return 1;
278 
279 	return 0;
280 }
281 
info(const char * dpyname)282 static void info(const char *dpyname)
283 {
284 	Display *dpy;
285 	int device;
286 	int32_t numerator, denominator;
287 
288 	dpy = XOpenDisplay(dpyname);
289 	if (dpy == NULL) {
290 		printf("Unable to connect to display '%s'\n",
291 		       dpyname ?: getenv("DISPLAY") ?: "unset");
292 		return;
293 	}
294 
295 	printf("Display '%s'\n", DisplayString(dpy));
296 	device = dri3_open(dpy);
297 	if (device < 0) {
298 		printf("\tUnable to connect to DRI3\n");
299 	} else {
300 		char device_path[1024];
301 		char driver_name[1024];
302 
303 		get_device_path(device, device_path, sizeof(device_path));
304 		get_driver_name(device, driver_name, sizeof(driver_name));
305 
306 		printf("Connected to DRI3, using fd %d which matches %s, driver %s\n",
307 		       device, device_path, driver_name);
308 		close(device);
309 	}
310 
311 	if (get_refresh_rate(dpy, &numerator, &denominator))
312 		printf("\tPrimary refresh rate: %d/%d (%.1fHz)\n",
313 		       numerator, denominator, numerator/(float)denominator);
314 
315 	XCloseDisplay(dpy);
316 }
317 
main(int argc,char ** argv)318 int main(int argc, char **argv)
319 {
320 	int i;
321 
322 	if (argc > 1) {
323 		for (i = 1; i < argc; i++)
324 			info(argv[i]);
325 	} else
326 		info(NULL);
327 
328 	return 0;
329 }
330