1 /*
2  * Copyright © 2006 Keith Packard
3  * Copyright © 2008 Red Hat, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting documentation, and
9  * that the name of the copyright holders not be used in advertising or
10  * publicity pertaining to distribution of the software without specific,
11  * written prior permission.  The copyright holders make no representations
12  * about the suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21  * OF THIS SOFTWARE.
22  */
23 
24 #include "randrstr.h"
25 #include <X11/Xatom.h>
26 
27 RESTYPE RROutputType;
28 
29 /*
30  * Notify the output of some change
31  */
32 void
RROutputChanged(RROutputPtr output,Bool configChanged)33 RROutputChanged(RROutputPtr output, Bool configChanged)
34 {
35     /* set changed bits on the primary screen only */
36     ScreenPtr pScreen = output->pScreen;
37     rrScrPrivPtr primarysp;
38 
39     output->changed = TRUE;
40     if (!pScreen)
41         return;
42 
43     if (pScreen->isGPU) {
44         ScreenPtr primary = pScreen->current_primary;
45         if (!primary)
46             return;
47         primarysp = rrGetScrPriv(primary);
48     }
49     else {
50         primarysp = rrGetScrPriv(pScreen);
51     }
52 
53     RRSetChanged(pScreen);
54     if (configChanged)
55         primarysp->configChanged = TRUE;
56 }
57 
58 /*
59  * Create an output
60  */
61 
62 RROutputPtr
RROutputCreate(ScreenPtr pScreen,const char * name,int nameLength,void * devPrivate)63 RROutputCreate(ScreenPtr pScreen,
64                const char *name, int nameLength, void *devPrivate)
65 {
66     RROutputPtr output;
67     RROutputPtr *outputs;
68     rrScrPrivPtr pScrPriv;
69     Atom nonDesktopAtom;
70 
71     if (!RRInit())
72         return NULL;
73 
74     pScrPriv = rrGetScrPriv(pScreen);
75 
76     outputs = reallocarray(pScrPriv->outputs,
77                            pScrPriv->numOutputs + 1, sizeof(RROutputPtr));
78     if (!outputs)
79         return NULL;
80 
81     pScrPriv->outputs = outputs;
82 
83     output = malloc(sizeof(RROutputRec) + nameLength + 1);
84     if (!output)
85         return NULL;
86     output->id = FakeClientID(0);
87     output->pScreen = pScreen;
88     output->name = (char *) (output + 1);
89     output->nameLength = nameLength;
90     memcpy(output->name, name, nameLength);
91     output->name[nameLength] = '\0';
92     output->connection = RR_UnknownConnection;
93     output->subpixelOrder = SubPixelUnknown;
94     output->mmWidth = 0;
95     output->mmHeight = 0;
96     output->crtc = NULL;
97     output->numCrtcs = 0;
98     output->crtcs = NULL;
99     output->numClones = 0;
100     output->clones = NULL;
101     output->numModes = 0;
102     output->numPreferred = 0;
103     output->modes = NULL;
104     output->numUserModes = 0;
105     output->userModes = NULL;
106     output->properties = NULL;
107     output->pendingProperties = FALSE;
108     output->changed = FALSE;
109     output->nonDesktop = FALSE;
110     output->devPrivate = devPrivate;
111 
112     if (!AddResource(output->id, RROutputType, (void *) output))
113         return NULL;
114 
115     pScrPriv->outputs[pScrPriv->numOutputs++] = output;
116 
117     nonDesktopAtom = MakeAtom(RR_PROPERTY_NON_DESKTOP, strlen(RR_PROPERTY_NON_DESKTOP), TRUE);
118     if (nonDesktopAtom != BAD_RESOURCE) {
119         static const INT32 values[2] = { 0, 1 };
120         (void) RRConfigureOutputProperty(output, nonDesktopAtom, FALSE, FALSE, FALSE,
121                                             2, values);
122     }
123     RROutputSetNonDesktop(output, FALSE);
124     RRResourcesChanged(pScreen);
125 
126     return output;
127 }
128 
129 /*
130  * Notify extension that output parameters have been changed
131  */
132 Bool
RROutputSetClones(RROutputPtr output,RROutputPtr * clones,int numClones)133 RROutputSetClones(RROutputPtr output, RROutputPtr * clones, int numClones)
134 {
135     RROutputPtr *newClones;
136     int i;
137 
138     if (numClones == output->numClones) {
139         for (i = 0; i < numClones; i++)
140             if (output->clones[i] != clones[i])
141                 break;
142         if (i == numClones)
143             return TRUE;
144     }
145     if (numClones) {
146         newClones = xallocarray(numClones, sizeof(RROutputPtr));
147         if (!newClones)
148             return FALSE;
149     }
150     else
151         newClones = NULL;
152     free(output->clones);
153     memcpy(newClones, clones, numClones * sizeof(RROutputPtr));
154     output->clones = newClones;
155     output->numClones = numClones;
156     RROutputChanged(output, TRUE);
157     return TRUE;
158 }
159 
160 Bool
RROutputSetModes(RROutputPtr output,RRModePtr * modes,int numModes,int numPreferred)161 RROutputSetModes(RROutputPtr output,
162                  RRModePtr * modes, int numModes, int numPreferred)
163 {
164     RRModePtr *newModes;
165     int i;
166 
167     if (numModes == output->numModes && numPreferred == output->numPreferred) {
168         for (i = 0; i < numModes; i++)
169             if (output->modes[i] != modes[i])
170                 break;
171         if (i == numModes) {
172             for (i = 0; i < numModes; i++)
173                 RRModeDestroy(modes[i]);
174             return TRUE;
175         }
176     }
177 
178     if (numModes) {
179         newModes = xallocarray(numModes, sizeof(RRModePtr));
180         if (!newModes)
181             return FALSE;
182     }
183     else
184         newModes = NULL;
185     if (output->modes) {
186         for (i = 0; i < output->numModes; i++)
187             RRModeDestroy(output->modes[i]);
188         free(output->modes);
189     }
190     memcpy(newModes, modes, numModes * sizeof(RRModePtr));
191     output->modes = newModes;
192     output->numModes = numModes;
193     output->numPreferred = numPreferred;
194     RROutputChanged(output, TRUE);
195     return TRUE;
196 }
197 
198 int
RROutputAddUserMode(RROutputPtr output,RRModePtr mode)199 RROutputAddUserMode(RROutputPtr output, RRModePtr mode)
200 {
201     int m;
202     ScreenPtr pScreen = output->pScreen;
203 
204     rrScrPriv(pScreen);
205     RRModePtr *newModes;
206 
207     /* Check to see if this mode is already listed for this output */
208     for (m = 0; m < output->numModes + output->numUserModes; m++) {
209         RRModePtr e = (m < output->numModes ?
210                        output->modes[m] :
211                        output->userModes[m - output->numModes]);
212         if (mode == e)
213             return Success;
214     }
215 
216     /* Check with the DDX to see if this mode is OK */
217     if (pScrPriv->rrOutputValidateMode)
218         if (!pScrPriv->rrOutputValidateMode(pScreen, output, mode))
219             return BadMatch;
220 
221     if (output->userModes)
222         newModes = reallocarray(output->userModes,
223                                 output->numUserModes + 1, sizeof(RRModePtr));
224     else
225         newModes = malloc(sizeof(RRModePtr));
226     if (!newModes)
227         return BadAlloc;
228 
229     output->userModes = newModes;
230     output->userModes[output->numUserModes++] = mode;
231     ++mode->refcnt;
232     RROutputChanged(output, TRUE);
233     RRTellChanged(pScreen);
234     return Success;
235 }
236 
237 int
RROutputDeleteUserMode(RROutputPtr output,RRModePtr mode)238 RROutputDeleteUserMode(RROutputPtr output, RRModePtr mode)
239 {
240     int m;
241 
242     /* Find this mode in the user mode list */
243     for (m = 0; m < output->numUserModes; m++) {
244         RRModePtr e = output->userModes[m];
245 
246         if (mode == e)
247             break;
248     }
249     /* Not there, access error */
250     if (m == output->numUserModes)
251         return BadAccess;
252 
253     /* make sure the mode isn't active for this output */
254     if (output->crtc && output->crtc->mode == mode)
255         return BadMatch;
256 
257     memmove(output->userModes + m, output->userModes + m + 1,
258             (output->numUserModes - m - 1) * sizeof(RRModePtr));
259     output->numUserModes--;
260     RRModeDestroy(mode);
261     return Success;
262 }
263 
264 Bool
RROutputSetCrtcs(RROutputPtr output,RRCrtcPtr * crtcs,int numCrtcs)265 RROutputSetCrtcs(RROutputPtr output, RRCrtcPtr * crtcs, int numCrtcs)
266 {
267     RRCrtcPtr *newCrtcs;
268     int i;
269 
270     if (numCrtcs == output->numCrtcs) {
271         for (i = 0; i < numCrtcs; i++)
272             if (output->crtcs[i] != crtcs[i])
273                 break;
274         if (i == numCrtcs)
275             return TRUE;
276     }
277     if (numCrtcs) {
278         newCrtcs = xallocarray(numCrtcs, sizeof(RRCrtcPtr));
279         if (!newCrtcs)
280             return FALSE;
281     }
282     else
283         newCrtcs = NULL;
284     free(output->crtcs);
285     memcpy(newCrtcs, crtcs, numCrtcs * sizeof(RRCrtcPtr));
286     output->crtcs = newCrtcs;
287     output->numCrtcs = numCrtcs;
288     RROutputChanged(output, TRUE);
289     return TRUE;
290 }
291 
292 Bool
RROutputSetConnection(RROutputPtr output,CARD8 connection)293 RROutputSetConnection(RROutputPtr output, CARD8 connection)
294 {
295     if (output->connection == connection)
296         return TRUE;
297     output->connection = connection;
298     RROutputChanged(output, TRUE);
299     return TRUE;
300 }
301 
302 Bool
RROutputSetSubpixelOrder(RROutputPtr output,int subpixelOrder)303 RROutputSetSubpixelOrder(RROutputPtr output, int subpixelOrder)
304 {
305     if (output->subpixelOrder == subpixelOrder)
306         return TRUE;
307 
308     output->subpixelOrder = subpixelOrder;
309     RROutputChanged(output, FALSE);
310     return TRUE;
311 }
312 
313 Bool
RROutputSetPhysicalSize(RROutputPtr output,int mmWidth,int mmHeight)314 RROutputSetPhysicalSize(RROutputPtr output, int mmWidth, int mmHeight)
315 {
316     if (output->mmWidth == mmWidth && output->mmHeight == mmHeight)
317         return TRUE;
318     output->mmWidth = mmWidth;
319     output->mmHeight = mmHeight;
320     RROutputChanged(output, FALSE);
321     return TRUE;
322 }
323 
324 Bool
RROutputSetNonDesktop(RROutputPtr output,Bool nonDesktop)325 RROutputSetNonDesktop(RROutputPtr output, Bool nonDesktop)
326 {
327     const char *nonDesktopStr = RR_PROPERTY_NON_DESKTOP;
328     Atom nonDesktopProp = MakeAtom(nonDesktopStr, strlen(nonDesktopStr), TRUE);
329     uint32_t value = nonDesktop ? 1 : 0;
330 
331     if (nonDesktopProp == None || nonDesktopProp == BAD_RESOURCE)
332         return FALSE;
333 
334     return RRChangeOutputProperty(output, nonDesktopProp, XA_INTEGER, 32,
335                                   PropModeReplace, 1, &value, TRUE, FALSE) == Success;
336 }
337 
338 void
RRDeliverOutputEvent(ClientPtr client,WindowPtr pWin,RROutputPtr output)339 RRDeliverOutputEvent(ClientPtr client, WindowPtr pWin, RROutputPtr output)
340 {
341     ScreenPtr pScreen = pWin->drawable.pScreen;
342 
343     rrScrPriv(pScreen);
344     RRCrtcPtr crtc = output->crtc;
345     RRModePtr mode = crtc ? crtc->mode : NULL;
346 
347     xRROutputChangeNotifyEvent oe = {
348         .type = RRNotify + RREventBase,
349         .subCode = RRNotify_OutputChange,
350         .timestamp = pScrPriv->lastSetTime.milliseconds,
351         .configTimestamp = pScrPriv->lastConfigTime.milliseconds,
352         .window = pWin->drawable.id,
353         .output = output->id,
354         .crtc = crtc ? crtc->id : None,
355         .mode = mode ? mode->mode.id : None,
356         .rotation = crtc ? crtc->rotation : RR_Rotate_0,
357         .connection = output->nonDesktop ? RR_Disconnected : output->connection,
358         .subpixelOrder = output->subpixelOrder
359     };
360     WriteEventsToClient(client, 1, (xEvent *) &oe);
361 }
362 
363 /*
364  * Destroy a Output at shutdown
365  */
366 void
RROutputDestroy(RROutputPtr output)367 RROutputDestroy(RROutputPtr output)
368 {
369     FreeResource(output->id, 0);
370 }
371 
372 static int
RROutputDestroyResource(void * value,XID pid)373 RROutputDestroyResource(void *value, XID pid)
374 {
375     RROutputPtr output = (RROutputPtr) value;
376     ScreenPtr pScreen = output->pScreen;
377     int m;
378 
379     if (pScreen) {
380         rrScrPriv(pScreen);
381         int i;
382         RRLeasePtr lease, next;
383 
384         xorg_list_for_each_entry_safe(lease, next, &pScrPriv->leases, list) {
385             int o;
386             for (o = 0; o < lease->numOutputs; o++) {
387                 if (lease->outputs[o] == output) {
388                     RRTerminateLease(lease);
389                     break;
390                 }
391             }
392         }
393 
394         if (pScrPriv->primaryOutput == output)
395             pScrPriv->primaryOutput = NULL;
396 
397         for (i = 0; i < pScrPriv->numOutputs; i++) {
398             if (pScrPriv->outputs[i] == output) {
399                 memmove(pScrPriv->outputs + i, pScrPriv->outputs + i + 1,
400                         (pScrPriv->numOutputs - (i + 1)) * sizeof(RROutputPtr));
401                 --pScrPriv->numOutputs;
402                 break;
403             }
404         }
405 
406         RRResourcesChanged(pScreen);
407     }
408     if (output->modes) {
409         for (m = 0; m < output->numModes; m++)
410             RRModeDestroy(output->modes[m]);
411         free(output->modes);
412     }
413 
414     for (m = 0; m < output->numUserModes; m++)
415         RRModeDestroy(output->userModes[m]);
416     free(output->userModes);
417 
418     free(output->crtcs);
419     free(output->clones);
420     RRDeleteAllOutputProperties(output);
421     free(output);
422     return 1;
423 }
424 
425 /*
426  * Initialize output type
427  */
428 Bool
RROutputInit(void)429 RROutputInit(void)
430 {
431     RROutputType = CreateNewResourceType(RROutputDestroyResource, "OUTPUT");
432     if (!RROutputType)
433         return FALSE;
434 
435     return TRUE;
436 }
437 
438 /*
439  * Initialize output type error value
440  */
441 void
RROutputInitErrorValue(void)442 RROutputInitErrorValue(void)
443 {
444     SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput);
445 }
446 
447 #define OutputInfoExtra	(SIZEOF(xRRGetOutputInfoReply) - 32)
448 
449 int
ProcRRGetOutputInfo(ClientPtr client)450 ProcRRGetOutputInfo(ClientPtr client)
451 {
452     REQUEST(xRRGetOutputInfoReq);
453     xRRGetOutputInfoReply rep;
454     RROutputPtr output;
455     CARD8 *extra;
456     unsigned long extraLen;
457     ScreenPtr pScreen;
458     rrScrPrivPtr pScrPriv;
459     RRCrtc *crtcs;
460     RRMode *modes;
461     RROutput *clones;
462     char *name;
463     int i;
464     Bool leased;
465 
466     REQUEST_SIZE_MATCH(xRRGetOutputInfoReq);
467     VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
468 
469     leased = RROutputIsLeased(output);
470 
471     pScreen = output->pScreen;
472     pScrPriv = rrGetScrPriv(pScreen);
473 
474     if (leased) {
475         rep = (xRRGetOutputInfoReply) {
476             .type = X_Reply,
477             .status = RRSetConfigSuccess,
478             .sequenceNumber = client->sequence,
479             .length = bytes_to_int32(OutputInfoExtra),
480             .timestamp = pScrPriv->lastSetTime.milliseconds,
481             .crtc = None,
482             .mmWidth = 0,
483             .mmHeight = 0,
484             .connection = RR_Disconnected,
485             .subpixelOrder = SubPixelUnknown,
486             .nCrtcs = 0,
487             .nModes = 0,
488             .nPreferred = 0,
489             .nClones = 0,
490             .nameLength = output->nameLength
491         };
492         extraLen = bytes_to_int32(rep.nameLength) << 2;
493         if (extraLen) {
494             rep.length += bytes_to_int32(extraLen);
495             extra = calloc(1, extraLen);
496             if (!extra)
497                 return BadAlloc;
498         }
499         else
500             extra = NULL;
501 
502         name = (char *) extra;
503     } else {
504         rep = (xRRGetOutputInfoReply) {
505             .type = X_Reply,
506             .status = RRSetConfigSuccess,
507             .sequenceNumber = client->sequence,
508             .length = bytes_to_int32(OutputInfoExtra),
509             .timestamp = pScrPriv->lastSetTime.milliseconds,
510             .crtc = output->crtc ? output->crtc->id : None,
511             .mmWidth = output->mmWidth,
512             .mmHeight = output->mmHeight,
513             .connection = output->nonDesktop ? RR_Disconnected : output->connection,
514             .subpixelOrder = output->subpixelOrder,
515             .nCrtcs = output->numCrtcs,
516             .nModes = output->numModes + output->numUserModes,
517             .nPreferred = output->numPreferred,
518             .nClones = output->numClones,
519             .nameLength = output->nameLength
520         };
521         extraLen = ((output->numCrtcs +
522                      output->numModes + output->numUserModes +
523                      output->numClones + bytes_to_int32(rep.nameLength)) << 2);
524 
525         if (extraLen) {
526             rep.length += bytes_to_int32(extraLen);
527             extra = calloc(1, extraLen);
528             if (!extra)
529                 return BadAlloc;
530         }
531         else
532             extra = NULL;
533 
534         crtcs = (RRCrtc *) extra;
535         modes = (RRMode *) (crtcs + output->numCrtcs);
536         clones = (RROutput *) (modes + output->numModes + output->numUserModes);
537         name = (char *) (clones + output->numClones);
538 
539         for (i = 0; i < output->numCrtcs; i++) {
540             crtcs[i] = output->crtcs[i]->id;
541             if (client->swapped)
542                 swapl(&crtcs[i]);
543         }
544         for (i = 0; i < output->numModes + output->numUserModes; i++) {
545             if (i < output->numModes)
546                 modes[i] = output->modes[i]->mode.id;
547             else
548                 modes[i] = output->userModes[i - output->numModes]->mode.id;
549             if (client->swapped)
550                 swapl(&modes[i]);
551         }
552         for (i = 0; i < output->numClones; i++) {
553             clones[i] = output->clones[i]->id;
554             if (client->swapped)
555                 swapl(&clones[i]);
556         }
557     }
558     memcpy(name, output->name, output->nameLength);
559     if (client->swapped) {
560         swaps(&rep.sequenceNumber);
561         swapl(&rep.length);
562         swapl(&rep.timestamp);
563         swapl(&rep.crtc);
564         swapl(&rep.mmWidth);
565         swapl(&rep.mmHeight);
566         swaps(&rep.nCrtcs);
567         swaps(&rep.nModes);
568         swaps(&rep.nPreferred);
569         swaps(&rep.nClones);
570         swaps(&rep.nameLength);
571     }
572     WriteToClient(client, sizeof(xRRGetOutputInfoReply), &rep);
573     if (extraLen) {
574         WriteToClient(client, extraLen, extra);
575         free(extra);
576     }
577 
578     return Success;
579 }
580 
581 static void
RRSetPrimaryOutput(ScreenPtr pScreen,rrScrPrivPtr pScrPriv,RROutputPtr output)582 RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv, RROutputPtr output)
583 {
584     if (pScrPriv->primaryOutput == output)
585         return;
586 
587     /* clear the old primary */
588     if (pScrPriv->primaryOutput) {
589         RROutputChanged(pScrPriv->primaryOutput, 0);
590         pScrPriv->primaryOutput = NULL;
591     }
592 
593     /* set the new primary */
594     if (output) {
595         pScrPriv->primaryOutput = output;
596         RROutputChanged(output, 0);
597     }
598 
599     pScrPriv->layoutChanged = TRUE;
600 
601     RRTellChanged(pScreen);
602 }
603 
604 int
ProcRRSetOutputPrimary(ClientPtr client)605 ProcRRSetOutputPrimary(ClientPtr client)
606 {
607     REQUEST(xRRSetOutputPrimaryReq);
608     RROutputPtr output = NULL;
609     WindowPtr pWin;
610     rrScrPrivPtr pScrPriv;
611     int ret;
612     ScreenPtr secondary;
613 
614     REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq);
615 
616     ret = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
617     if (ret != Success)
618         return ret;
619 
620     if (stuff->output) {
621         VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
622 
623         if (RROutputIsLeased(output))
624             return BadAccess;
625 
626         if (!output->pScreen->isGPU && output->pScreen != pWin->drawable.pScreen) {
627             client->errorValue = stuff->window;
628             return BadMatch;
629         }
630         if (output->pScreen->isGPU && output->pScreen->current_primary != pWin->drawable.pScreen) {
631             client->errorValue = stuff->window;
632             return BadMatch;
633         }
634     }
635 
636     pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
637     if (pScrPriv)
638     {
639         RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output);
640 
641         xorg_list_for_each_entry(secondary,
642                                  &pWin->drawable.pScreen->secondary_list,
643                                  secondary_head) {
644             if (secondary->is_output_secondary)
645                 RRSetPrimaryOutput(secondary, rrGetScrPriv(secondary), output);
646         }
647     }
648 
649     return Success;
650 }
651 
652 int
ProcRRGetOutputPrimary(ClientPtr client)653 ProcRRGetOutputPrimary(ClientPtr client)
654 {
655     REQUEST(xRRGetOutputPrimaryReq);
656     WindowPtr pWin;
657     rrScrPrivPtr pScrPriv;
658     xRRGetOutputPrimaryReply rep;
659     RROutputPtr primary = NULL;
660     int rc;
661 
662     REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq);
663 
664     rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
665     if (rc != Success)
666         return rc;
667 
668     pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
669     if (pScrPriv)
670         primary = pScrPriv->primaryOutput;
671 
672     rep = (xRRGetOutputPrimaryReply) {
673         .type = X_Reply,
674         .sequenceNumber = client->sequence,
675         .output = primary ? primary->id : None
676     };
677 
678     if (client->swapped) {
679         swaps(&rep.sequenceNumber);
680         swapl(&rep.output);
681     }
682 
683     WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep);
684 
685     return Success;
686 }
687