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
27RESTYPE RRLeaseType;
28
29/*
30 * Notify of some lease change
31 */
32void
33RRDeliverLeaseEvent(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 */
60static void
61RRLeaseChangeState(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 */
77static RRLeasePtr
78RRLeaseAlloc(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 */
101Bool
102RRCrtcIsLeased(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 */
120Bool
121RROutputIsLeased(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
142void
143RRLeaseTerminated(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
160void
161RRLeaseFree(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 */
172void
173RRTerminateLease(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 */
186static int
187RRLeaseDestroyResource(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 */
198Bool
199RRLeaseInit(void)
200{
201    RRLeaseType = CreateNewResourceType(RRLeaseDestroyResource, "LEASE");
202    if (!RRLeaseType)
203        return FALSE;
204    return TRUE;
205}
206
207int
208ProcRRCreateLease(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(&lease->list, &scr_priv->leases);
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
329bail_lease:
330    free(lease);
331    return rc;
332}
333
334int
335ProcRRFreeLease(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