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