1 /*
2  * Copyright (c) 2008 NVIDIA, Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * 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 THE
18  * 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 
24 #define _GNU_SOURCE 1
25 #include <string.h>
26 
27 #define NEED_REPLIES
28 #include <stdlib.h>
29 #include <X11/Xlibint.h>
30 #include <X11/Xutil.h>
31 #include <X11/extensions/Xext.h>
32 #include <X11/extensions/extutil.h>
33 #include "va_nvctrl.h"
34 
35 #define NV_CONTROL_ERRORS 0
36 #define NV_CONTROL_EVENTS 5
37 #define NV_CONTROL_NAME "NV-CONTROL"
38 
39 #define NV_CTRL_TARGET_TYPE_X_SCREEN   0
40 #define NV_CTRL_TARGET_TYPE_GPU        1
41 #define NV_CTRL_TARGET_TYPE_FRAMELOCK  2
42 #define NV_CTRL_TARGET_TYPE_VCSC       3 /* Visual Computing System */
43 
44 #define NV_CTRL_STRING_NVIDIA_DRIVER_VERSION                    3  /* R--G */
45 
46 #define X_nvCtrlQueryExtension                      0
47 #define X_nvCtrlIsNv                                1
48 #define X_nvCtrlQueryStringAttribute                4
49 
50 typedef struct {
51     CARD8 reqType;
52     CARD8 nvReqType;
53     CARD16 length B16;
54 } xnvCtrlQueryExtensionReq;
55 #define sz_xnvCtrlQueryExtensionReq 4
56 
57 typedef struct {
58     BYTE type;   /* X_Reply */
59     CARD8 padb1;
60     CARD16 sequenceNumber B16;
61     CARD32 length B32;
62     CARD16 major B16;
63     CARD16 minor B16;
64     CARD32 padl4 B32;
65     CARD32 padl5 B32;
66     CARD32 padl6 B32;
67     CARD32 padl7 B32;
68     CARD32 padl8 B32;
69 } xnvCtrlQueryExtensionReply;
70 #define sz_xnvCtrlQueryExtensionReply 32
71 
72 typedef struct {
73     CARD8 reqType;
74     CARD8 nvReqType;
75     CARD16 length B16;
76     CARD32 screen B32;
77 } xnvCtrlIsNvReq;
78 #define sz_xnvCtrlIsNvReq 8
79 
80 typedef struct {
81     BYTE type;   /* X_Reply */
82     CARD8 padb1;
83     CARD16 sequenceNumber B16;
84     CARD32 length B32;
85     CARD32 isnv B32;
86     CARD32 padl4 B32;
87     CARD32 padl5 B32;
88     CARD32 padl6 B32;
89     CARD32 padl7 B32;
90     CARD32 padl8 B32;
91 } xnvCtrlIsNvReply;
92 #define sz_xnvCtrlIsNvReply 32
93 
94 typedef struct {
95     CARD8 reqType;
96     CARD8 nvReqType;
97     CARD16 length B16;
98     CARD16 target_id B16;    /* X screen number or GPU number */
99     CARD16 target_type B16;  /* X screen or GPU */
100     CARD32 display_mask B32;
101     CARD32 attribute B32;
102 } xnvCtrlQueryStringAttributeReq;
103 #define sz_xnvCtrlQueryStringAttributeReq 16
104 
105 typedef struct {
106     BYTE type;
107     BYTE pad0;
108     CARD16 sequenceNumber B16;
109     CARD32 length B32;
110     CARD32 flags B32;
111     CARD32 n B32;    /* Length of string */
112     CARD32 pad4 B32;
113     CARD32 pad5 B32;
114     CARD32 pad6 B32;
115     CARD32 pad7 B32;
116 } xnvCtrlQueryStringAttributeReply;
117 #define sz_xnvCtrlQueryStringAttributeReply 32
118 
119 #define NVCTRL_EXT_NEED_CHECK          (XPointer)(~0)
120 #define NVCTRL_EXT_NEED_NOTHING        (XPointer)(0)
121 #define NVCTRL_EXT_NEED_TARGET_SWAP    (XPointer)(1)
122 
123 static XExtensionInfo _nvctrl_ext_info_data;
124 static XExtensionInfo *nvctrl_ext_info = &_nvctrl_ext_info_data;
125 static /* const */ char *nvctrl_extension_name = NV_CONTROL_NAME;
126 
127 #define XNVCTRLCheckExtension(dpy,i,val) \
128   XextCheckExtension (dpy, i, nvctrl_extension_name, val)
129 #define XNVCTRLSimpleCheckExtension(dpy,i) \
130   XextSimpleCheckExtension (dpy, i, nvctrl_extension_name)
131 
132 static int close_display();
133 static /* const */ XExtensionHooks nvctrl_extension_hooks = {
134     NULL,                               /* create_gc */
135     NULL,                               /* copy_gc */
136     NULL,                               /* flush_gc */
137     NULL,                               /* free_gc */
138     NULL,                               /* create_font */
139     NULL,                               /* free_font */
140     close_display,                      /* close_display */
141     NULL,                               /* wire_to_event */
142     NULL,                               /* event_to_wire */
143     NULL,                               /* error */
144     NULL,                               /* error_string */
145 };
146 
147 static XEXT_GENERATE_FIND_DISPLAY (find_display, nvctrl_ext_info,
148                                    nvctrl_extension_name,
149                                    &nvctrl_extension_hooks,
150                                    NV_CONTROL_EVENTS, NVCTRL_EXT_NEED_CHECK)
151 
152 static XEXT_GENERATE_CLOSE_DISPLAY (close_display, nvctrl_ext_info)
153 
154 static Bool XNVCTRLQueryVersion (Display *dpy, int *major, int *minor);
155 
156 /*
157  * NV-CONTROL versions 1.8 and 1.9 pack the target_type and target_id
158  * fields in reversed order.  In order to talk to one of these servers,
159  * we need to swap these fields.
160  */
XNVCTRLCheckTargetData(Display * dpy,XExtDisplayInfo * info,int * target_type,int * target_id)161 static void XNVCTRLCheckTargetData(Display *dpy, XExtDisplayInfo *info,
162                                    int *target_type, int *target_id)
163 {
164     /* Find out what the server's NV-CONTROL version is and
165      * setup for swapping if we need to.
166      */
167     if (info->data == NVCTRL_EXT_NEED_CHECK) {
168         int major, minor;
169 
170         if (XNVCTRLQueryVersion(dpy, &major, &minor)) {
171             if (major == 1 &&
172                 (minor == 8 || minor == 9)) {
173                 info->data = NVCTRL_EXT_NEED_TARGET_SWAP;
174             } else {
175                 info->data = NVCTRL_EXT_NEED_NOTHING;
176             }
177         } else {
178             info->data = NVCTRL_EXT_NEED_NOTHING;
179         }
180     }
181 
182     /* We need to swap the target_type and target_id */
183     if (info->data == NVCTRL_EXT_NEED_TARGET_SWAP) {
184         int tmp;
185         tmp = *target_type;
186         *target_type = *target_id;
187         *target_id = tmp;
188     }
189 }
190 
191 
XNVCTRLQueryExtension(Display * dpy,int * event_basep,int * error_basep)192 static Bool XNVCTRLQueryExtension (
193     Display *dpy,
194     int *event_basep,
195     int *error_basep
196 ){
197     XExtDisplayInfo *info = find_display (dpy);
198 
199     if (XextHasExtension(info)) {
200         if (event_basep) *event_basep = info->codes->first_event;
201         if (error_basep) *error_basep = info->codes->first_error;
202         return True;
203     } else {
204         return False;
205     }
206 }
207 
208 
XNVCTRLQueryVersion(Display * dpy,int * major,int * minor)209 static Bool XNVCTRLQueryVersion (
210     Display *dpy,
211     int *major,
212     int *minor
213 ){
214     XExtDisplayInfo *info = find_display (dpy);
215     xnvCtrlQueryExtensionReply rep;
216     xnvCtrlQueryExtensionReq   *req;
217 
218     if(!XextHasExtension(info))
219         return False;
220 
221     XNVCTRLCheckExtension (dpy, info, False);
222 
223     LockDisplay (dpy);
224     GetReq (nvCtrlQueryExtension, req);
225     req->reqType = info->codes->major_opcode;
226     req->nvReqType = X_nvCtrlQueryExtension;
227     if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) {
228         UnlockDisplay (dpy);
229         SyncHandle ();
230         return False;
231     }
232     if (major) *major = rep.major;
233     if (minor) *minor = rep.minor;
234     UnlockDisplay (dpy);
235     SyncHandle ();
236     return True;
237 }
238 
239 
XNVCTRLIsNvScreen(Display * dpy,int screen)240 static Bool XNVCTRLIsNvScreen (
241     Display *dpy,
242     int screen
243 ){
244     XExtDisplayInfo *info = find_display (dpy);
245     xnvCtrlIsNvReply rep;
246     xnvCtrlIsNvReq   *req;
247     Bool isnv;
248 
249     if(!XextHasExtension(info))
250         return False;
251 
252     XNVCTRLCheckExtension (dpy, info, False);
253 
254     LockDisplay (dpy);
255     GetReq (nvCtrlIsNv, req);
256     req->reqType = info->codes->major_opcode;
257     req->nvReqType = X_nvCtrlIsNv;
258     req->screen = screen;
259     if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) {
260         UnlockDisplay (dpy);
261         SyncHandle ();
262         return False;
263     }
264     isnv = rep.isnv;
265     UnlockDisplay (dpy);
266     SyncHandle ();
267     return isnv;
268 }
269 
270 
XNVCTRLQueryTargetStringAttribute(Display * dpy,int target_type,int target_id,unsigned int display_mask,unsigned int attribute,char ** ptr)271 static Bool XNVCTRLQueryTargetStringAttribute (
272     Display *dpy,
273     int target_type,
274     int target_id,
275     unsigned int display_mask,
276     unsigned int attribute,
277     char **ptr
278 ){
279     XExtDisplayInfo *info = find_display (dpy);
280     xnvCtrlQueryStringAttributeReply rep;
281     xnvCtrlQueryStringAttributeReq   *req;
282     Bool exists;
283     int length, numbytes, slop;
284 
285     if (!ptr) return False;
286 
287     if(!XextHasExtension(info))
288         return False;
289 
290     XNVCTRLCheckExtension (dpy, info, False);
291     XNVCTRLCheckTargetData(dpy, info, &target_type, &target_id);
292 
293     LockDisplay (dpy);
294     GetReq (nvCtrlQueryStringAttribute, req);
295     req->reqType = info->codes->major_opcode;
296     req->nvReqType = X_nvCtrlQueryStringAttribute;
297     req->target_type = target_type;
298     req->target_id = target_id;
299     req->display_mask = display_mask;
300     req->attribute = attribute;
301     if (!_XReply (dpy, (xReply *) &rep, 0, False)) {
302         UnlockDisplay (dpy);
303         SyncHandle ();
304         return False;
305     }
306     length = rep.length;
307     numbytes = rep.n;
308     slop = numbytes & 3;
309     *ptr = (char *) Xmalloc(numbytes);
310     if (! *ptr) {
311         _XEatData(dpy, length);
312         UnlockDisplay (dpy);
313         SyncHandle ();
314         return False;
315     } else {
316         _XRead(dpy, (char *) *ptr, numbytes);
317         if (slop) _XEatData(dpy, 4-slop);
318     }
319     exists = rep.flags;
320     UnlockDisplay (dpy);
321     SyncHandle ();
322     return exists;
323 }
324 
XNVCTRLQueryStringAttribute(Display * dpy,int screen,unsigned int display_mask,unsigned int attribute,char ** ptr)325 static Bool XNVCTRLQueryStringAttribute (
326     Display *dpy,
327     int screen,
328     unsigned int display_mask,
329     unsigned int attribute,
330     char **ptr
331 ){
332     return XNVCTRLQueryTargetStringAttribute(dpy, NV_CTRL_TARGET_TYPE_X_SCREEN,
333                                              screen, display_mask,
334                                              attribute, ptr);
335 }
336 
337 
VA_NVCTRLQueryDirectRenderingCapable(Display * dpy,int screen,Bool * isCapable)338 Bool VA_NVCTRLQueryDirectRenderingCapable( Display *dpy, int screen,
339     Bool *isCapable )
340 {
341     int event_base;
342     int error_base;
343 
344     if (isCapable)
345         *isCapable = False;
346 
347     if (!XNVCTRLQueryExtension(dpy, &event_base, &error_base))
348         return False;
349 
350     if (isCapable && XNVCTRLIsNvScreen(dpy, screen))
351         *isCapable = True;
352 
353     return True;
354 }
355 
VA_NVCTRLGetClientDriverName(Display * dpy,int screen,int * ddxDriverMajorVersion,int * ddxDriverMinorVersion,int * ddxDriverPatchVersion,char ** clientDriverName)356 Bool VA_NVCTRLGetClientDriverName( Display *dpy, int screen,
357     int *ddxDriverMajorVersion, int *ddxDriverMinorVersion,
358     int *ddxDriverPatchVersion, char **clientDriverName )
359 {
360     if (ddxDriverMajorVersion)
361         *ddxDriverMajorVersion = 0;
362     if (ddxDriverMinorVersion)
363         *ddxDriverMinorVersion = 0;
364     if (ddxDriverPatchVersion)
365         *ddxDriverPatchVersion = 0;
366     if (clientDriverName)
367         *clientDriverName = NULL;
368 
369     char *nvidia_driver_version = NULL;
370     if (!XNVCTRLQueryStringAttribute(dpy, screen, 0, NV_CTRL_STRING_NVIDIA_DRIVER_VERSION, &nvidia_driver_version))
371         return False;
372 
373     char *end, *str = nvidia_driver_version;
374     unsigned long v = strtoul(str, &end, 10);
375     if (end && end != str) {
376         if (ddxDriverMajorVersion)
377             *ddxDriverMajorVersion = v;
378         if (*(str = end) == '.') {
379             v = strtoul(str + 1, &end, 10);
380             if (end && end != str && (*end == '.' || *end == '\0')) {
381                 if (ddxDriverMinorVersion)
382                     *ddxDriverMinorVersion = v;
383                 if (*(str = end) == '.') {
384                     v = strtoul(str + 1, &end, 10);
385                     if (end && end != str && *end == '\0') {
386                         if (ddxDriverPatchVersion)
387                             *ddxDriverPatchVersion = v;
388                     }
389                 }
390             }
391         }
392     }
393     Xfree(nvidia_driver_version);
394 
395     if (clientDriverName)
396         *clientDriverName = strdup("nvidia");
397 
398     return True;
399 }
400