1 /*
2 * Copyright © 2017 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23 #include "randrstr.h"
24 #include "swaprep.h"
25 #include <unistd.h>
26
27 RESTYPE RRLeaseType;
28
29 /*
30 * Notify of some lease change
31 */
32 void
RRDeliverLeaseEvent(ClientPtr client,WindowPtr window)33 RRDeliverLeaseEvent(ClientPtr client, WindowPtr window)
34 {
35 ScreenPtr screen = window->drawable.pScreen;
36 rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
37 RRLeasePtr lease;
38
39 UpdateCurrentTimeIf();
40 xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
41 if (lease->id != None && (lease->state == RRLeaseCreating ||
42 lease->state == RRLeaseTerminating))
43 {
44 xRRLeaseNotifyEvent le = (xRRLeaseNotifyEvent) {
45 .type = RRNotify + RREventBase,
46 .subCode = RRNotify_Lease,
47 .timestamp = currentTime.milliseconds,
48 .window = window->drawable.id,
49 .lease = lease->id,
50 .created = lease->state == RRLeaseCreating,
51 };
52 WriteEventsToClient(client, 1, (xEvent *) &le);
53 }
54 }
55 }
56
57 /*
58 * Change the state of a lease and let anyone watching leases know
59 */
60 static void
RRLeaseChangeState(RRLeasePtr lease,RRLeaseState old,RRLeaseState new)61 RRLeaseChangeState(RRLeasePtr lease, RRLeaseState old, RRLeaseState new)
62 {
63 ScreenPtr screen = lease->screen;
64 rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
65
66 lease->state = old;
67 scr_priv->leasesChanged = TRUE;
68 RRSetChanged(lease->screen);
69 RRTellChanged(lease->screen);
70 scr_priv->leasesChanged = FALSE;
71 lease->state = new;
72 }
73
74 /*
75 * Allocate and initialize a lease
76 */
77 static RRLeasePtr
RRLeaseAlloc(ScreenPtr screen,RRLease lid,int numCrtcs,int numOutputs)78 RRLeaseAlloc(ScreenPtr screen, RRLease lid, int numCrtcs, int numOutputs)
79 {
80 RRLeasePtr lease;
81 lease = calloc(1,
82 sizeof(RRLeaseRec) +
83 numCrtcs * sizeof (RRCrtcPtr) +
84 numOutputs * sizeof(RROutputPtr));
85 if (!lease)
86 return NULL;
87 lease->screen = screen;
88 xorg_list_init(&lease->list);
89 lease->id = lid;
90 lease->state = RRLeaseCreating;
91 lease->numCrtcs = numCrtcs;
92 lease->numOutputs = numOutputs;
93 lease->crtcs = (RRCrtcPtr *) (lease + 1);
94 lease->outputs = (RROutputPtr *) (lease->crtcs + numCrtcs);
95 return lease;
96 }
97
98 /*
99 * Check if a crtc is leased
100 */
101 Bool
RRCrtcIsLeased(RRCrtcPtr crtc)102 RRCrtcIsLeased(RRCrtcPtr crtc)
103 {
104 ScreenPtr screen = crtc->pScreen;
105 rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
106 RRLeasePtr lease;
107 int c;
108
109 xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
110 for (c = 0; c < lease->numCrtcs; c++)
111 if (lease->crtcs[c] == crtc)
112 return TRUE;
113 }
114 return FALSE;
115 }
116
117 /*
118 * Check if an output is leased
119 */
120 Bool
RROutputIsLeased(RROutputPtr output)121 RROutputIsLeased(RROutputPtr output)
122 {
123 ScreenPtr screen = output->pScreen;
124 rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
125 RRLeasePtr lease;
126 int o;
127
128 xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
129 for (o = 0; o < lease->numOutputs; o++)
130 if (lease->outputs[o] == output)
131 return TRUE;
132 }
133 return FALSE;
134 }
135
136 /*
137 * A lease has been terminated.
138 * The driver is responsible for noticing and
139 * calling this function when that happens
140 */
141
142 void
RRLeaseTerminated(RRLeasePtr lease)143 RRLeaseTerminated(RRLeasePtr lease)
144 {
145 /* Notify clients with events, but only if this isn't during lease creation */
146 if (lease->state == RRLeaseRunning)
147 RRLeaseChangeState(lease, RRLeaseTerminating, RRLeaseTerminating);
148
149 if (lease->id != None)
150 FreeResource(lease->id, RT_NONE);
151
152 xorg_list_del(&lease->list);
153 }
154
155 /*
156 * A lease is completely shut down and is
157 * ready to be deallocated
158 */
159
160 void
RRLeaseFree(RRLeasePtr lease)161 RRLeaseFree(RRLeasePtr lease)
162 {
163 free(lease);
164 }
165
166 /*
167 * Ask the driver to terminate a lease. The
168 * driver will call RRLeaseTerminated when that has
169 * finished, which may be some time after this function returns
170 * if the driver operation is asynchronous
171 */
172 void
RRTerminateLease(RRLeasePtr lease)173 RRTerminateLease(RRLeasePtr lease)
174 {
175 ScreenPtr screen = lease->screen;
176 rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
177
178 scr_priv->rrTerminateLease(screen, lease);
179 }
180
181 /*
182 * Destroy a lease resource ID. All this
183 * does is note that the lease no longer has an ID, and
184 * so doesn't appear over the protocol anymore.
185 */
186 static int
RRLeaseDestroyResource(void * value,XID pid)187 RRLeaseDestroyResource(void *value, XID pid)
188 {
189 RRLeasePtr lease = value;
190
191 lease->id = None;
192 return 1;
193 }
194
195 /*
196 * Create the lease resource type during server initialization
197 */
198 Bool
RRLeaseInit(void)199 RRLeaseInit(void)
200 {
201 RRLeaseType = CreateNewResourceType(RRLeaseDestroyResource, "LEASE");
202 if (!RRLeaseType)
203 return FALSE;
204 return TRUE;
205 }
206
207 int
ProcRRCreateLease(ClientPtr client)208 ProcRRCreateLease(ClientPtr client)
209 {
210 REQUEST(xRRCreateLeaseReq);
211 xRRCreateLeaseReply rep;
212 WindowPtr window;
213 ScreenPtr screen;
214 rrScrPrivPtr scr_priv;
215 RRLeasePtr lease;
216 RRCrtc *crtcIds;
217 RROutput *outputIds;
218 int fd;
219 int rc;
220 unsigned long len;
221 int c, o;
222
223 REQUEST_AT_LEAST_SIZE(xRRCreateLeaseReq);
224
225 LEGAL_NEW_RESOURCE(stuff->lid, client);
226
227 rc = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess);
228 if (rc != Success)
229 return rc;
230
231 len = client->req_len - bytes_to_int32(sizeof(xRRCreateLeaseReq));
232
233 if (len != stuff->nCrtcs + stuff->nOutputs)
234 return BadLength;
235
236 screen = window->drawable.pScreen;
237 scr_priv = rrGetScrPriv(screen);
238
239 if (!scr_priv)
240 return BadMatch;
241
242 if (!scr_priv->rrCreateLease)
243 return BadMatch;
244
245 /* Allocate a structure to hold all of the lease information */
246
247 lease = RRLeaseAlloc(screen, stuff->lid, stuff->nCrtcs, stuff->nOutputs);
248 if (!lease)
249 return BadAlloc;
250
251 /* Look up all of the crtcs */
252 crtcIds = (RRCrtc *) (stuff + 1);
253 for (c = 0; c < stuff->nCrtcs; c++) {
254 RRCrtcPtr crtc;
255
256 rc = dixLookupResourceByType((void **)&crtc, crtcIds[c],
257 RRCrtcType, client, DixSetAttrAccess);
258
259 if (rc != Success) {
260 client->errorValue = crtcIds[c];
261 goto bail_lease;
262 }
263
264 if (RRCrtcIsLeased(crtc)) {
265 client->errorValue = crtcIds[c];
266 rc = BadAccess;
267 goto bail_lease;
268 }
269
270 lease->crtcs[c] = crtc;
271 }
272
273 /* Look up all of the outputs */
274 outputIds = (RROutput *) (crtcIds + stuff->nCrtcs);
275 for (o = 0; o < stuff->nOutputs; o++) {
276 RROutputPtr output;
277
278 rc = dixLookupResourceByType((void **)&output, outputIds[o],
279 RROutputType, client, DixSetAttrAccess);
280 if (rc != Success) {
281 client->errorValue = outputIds[o];
282 goto bail_lease;
283 }
284
285 if (RROutputIsLeased(output)) {
286 client->errorValue = outputIds[o];
287 rc = BadAccess;
288 goto bail_lease;
289 }
290
291 lease->outputs[o] = output;
292 }
293
294 rc = scr_priv->rrCreateLease(screen, lease, &fd);
295 if (rc != Success)
296 goto bail_lease;
297
298 xorg_list_add(&scr_priv->leases, &lease->list);
299
300 if (!AddResource(stuff->lid, RRLeaseType, lease)) {
301 close(fd);
302 return BadAlloc;
303 }
304
305 if (WriteFdToClient(client, fd, TRUE) < 0) {
306 RRTerminateLease(lease);
307 close(fd);
308 return BadAlloc;
309 }
310
311 RRLeaseChangeState(lease, RRLeaseCreating, RRLeaseRunning);
312
313 rep = (xRRCreateLeaseReply) {
314 .type = X_Reply,
315 .nfd = 1,
316 .sequenceNumber = client->sequence,
317 .length = 0,
318 };
319
320 if (client->swapped) {
321 swaps(&rep.sequenceNumber);
322 swapl(&rep.length);
323 }
324
325 WriteToClient(client, sizeof (rep), &rep);
326
327 return Success;
328
329 bail_lease:
330 free(lease);
331 return rc;
332 }
333
334 int
ProcRRFreeLease(ClientPtr client)335 ProcRRFreeLease(ClientPtr client)
336 {
337 REQUEST(xRRFreeLeaseReq);
338 RRLeasePtr lease;
339
340 REQUEST_SIZE_MATCH(xRRFreeLeaseReq);
341
342 VERIFY_RR_LEASE(stuff->lid, lease, DixDestroyAccess);
343
344 if (stuff->terminate)
345 RRTerminateLease(lease);
346 else
347 /* Get rid of the resource database entry */
348 FreeResource(stuff->lid, RT_NONE);
349
350 return Success;
351 }
352