13464ebd5Sriastradh/* 23464ebd5Sriastradh * Copyright © 2010 Intel Corporation 33464ebd5Sriastradh * 43464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a 53464ebd5Sriastradh * copy of this software and associated documentation files (the "Soft- 63464ebd5Sriastradh * ware"), to deal in the Software without restriction, including without 73464ebd5Sriastradh * limitation the rights to use, copy, modify, merge, publish, distribute, 83464ebd5Sriastradh * and/or sell copies of the Software, and to permit persons to whom the 93464ebd5Sriastradh * Software is furnished to do so, provided that the above copyright 103464ebd5Sriastradh * notice(s) and this permission notice appear in all copies of the Soft- 113464ebd5Sriastradh * ware and that both the above copyright notice(s) and this permission 123464ebd5Sriastradh * notice appear in supporting documentation. 133464ebd5Sriastradh * 143464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 153464ebd5Sriastradh * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 163464ebd5Sriastradh * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY 173464ebd5Sriastradh * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN 183464ebd5Sriastradh * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE- 193464ebd5Sriastradh * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 203464ebd5Sriastradh * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 213464ebd5Sriastradh * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR- 223464ebd5Sriastradh * MANCE OF THIS SOFTWARE. 233464ebd5Sriastradh * 243464ebd5Sriastradh * Except as contained in this notice, the name of a copyright holder shall 253464ebd5Sriastradh * not be used in advertising or otherwise to promote the sale, use or 263464ebd5Sriastradh * other dealings in this Software without prior written authorization of 273464ebd5Sriastradh * the copyright holder. 283464ebd5Sriastradh * 293464ebd5Sriastradh * Authors: 303464ebd5Sriastradh * Kristian Høgsberg (krh@bitplanet.net) 313464ebd5Sriastradh */ 323464ebd5Sriastradh 3301e04c3fSmrg#include <stdbool.h> 3401e04c3fSmrg 353464ebd5Sriastradh#include "glapi.h" 363464ebd5Sriastradh#include "glxclient.h" 377ec681f3Smrg#include "indirect.h" 3801e04c3fSmrg#include "util/debug.h" 3901e04c3fSmrg 40af69d88dSmrg#ifndef GLX_USE_APPLEGL 41af69d88dSmrg 423464ebd5Sriastradhextern struct _glapi_table *__glXNewIndirectAPI(void); 433464ebd5Sriastradh 443464ebd5Sriastradh/* 453464ebd5Sriastradh** All indirect rendering contexts will share the same indirect dispatch table. 463464ebd5Sriastradh*/ 473464ebd5Sriastradhstatic struct _glapi_table *IndirectAPI = NULL; 483464ebd5Sriastradh 497ec681f3Smrgstatic void 507ec681f3Smrg__glFreeAttributeState(struct glx_context * gc) 517ec681f3Smrg{ 527ec681f3Smrg __GLXattribute *sp, **spp; 537ec681f3Smrg 547ec681f3Smrg for (spp = &gc->attributes.stack[0]; 557ec681f3Smrg spp < &gc->attributes.stack[__GL_CLIENT_ATTRIB_STACK_DEPTH]; spp++) { 567ec681f3Smrg sp = *spp; 577ec681f3Smrg if (sp) { 587ec681f3Smrg free((char *) sp); 597ec681f3Smrg } 607ec681f3Smrg else { 617ec681f3Smrg break; 627ec681f3Smrg } 637ec681f3Smrg } 647ec681f3Smrg} 657ec681f3Smrg 663464ebd5Sriastradhstatic void 673464ebd5Sriastradhindirect_destroy_context(struct glx_context *gc) 683464ebd5Sriastradh{ 693464ebd5Sriastradh __glXFreeVertexArrayState(gc); 703464ebd5Sriastradh 71af69d88dSmrg free((char *) gc->vendor); 72af69d88dSmrg free((char *) gc->renderer); 73af69d88dSmrg free((char *) gc->version); 74af69d88dSmrg free((char *) gc->extensions); 753464ebd5Sriastradh __glFreeAttributeState(gc); 76af69d88dSmrg free((char *) gc->buf); 77af69d88dSmrg free((char *) gc->client_state_private); 78af69d88dSmrg free((char *) gc); 793464ebd5Sriastradh} 803464ebd5Sriastradh 813464ebd5Sriastradhstatic Bool 8201e04c3fSmrgSendMakeCurrentRequest(Display * dpy, GLXContextID gc_id, 8301e04c3fSmrg GLXContextTag gc_tag, GLXDrawable draw, 8401e04c3fSmrg GLXDrawable read, GLXContextTag *out_tag) 853464ebd5Sriastradh{ 86af69d88dSmrg xGLXMakeCurrentReply reply; 873464ebd5Sriastradh Bool ret; 8801e04c3fSmrg int opcode = __glXSetupForCommand(dpy); 893464ebd5Sriastradh 903464ebd5Sriastradh LockDisplay(dpy); 913464ebd5Sriastradh 923464ebd5Sriastradh if (draw == read) { 933464ebd5Sriastradh xGLXMakeCurrentReq *req; 943464ebd5Sriastradh 953464ebd5Sriastradh GetReq(GLXMakeCurrent, req); 963464ebd5Sriastradh req->reqType = opcode; 973464ebd5Sriastradh req->glxCode = X_GLXMakeCurrent; 983464ebd5Sriastradh req->drawable = draw; 993464ebd5Sriastradh req->context = gc_id; 1003464ebd5Sriastradh req->oldContextTag = gc_tag; 1013464ebd5Sriastradh } 1023464ebd5Sriastradh else { 1033464ebd5Sriastradh struct glx_display *priv = __glXInitialize(dpy); 1043464ebd5Sriastradh 1053464ebd5Sriastradh /* If the server can support the GLX 1.3 version, we should 1063464ebd5Sriastradh * perfer that. Not only that, some servers support GLX 1.3 but 1073464ebd5Sriastradh * not the SGI extension. 1083464ebd5Sriastradh */ 1093464ebd5Sriastradh 1107ec681f3Smrg if (priv->minorVersion >= 3) { 1113464ebd5Sriastradh xGLXMakeContextCurrentReq *req; 1123464ebd5Sriastradh 1133464ebd5Sriastradh GetReq(GLXMakeContextCurrent, req); 1143464ebd5Sriastradh req->reqType = opcode; 1153464ebd5Sriastradh req->glxCode = X_GLXMakeContextCurrent; 1163464ebd5Sriastradh req->drawable = draw; 1173464ebd5Sriastradh req->readdrawable = read; 1183464ebd5Sriastradh req->context = gc_id; 1193464ebd5Sriastradh req->oldContextTag = gc_tag; 1203464ebd5Sriastradh } 1213464ebd5Sriastradh else { 1223464ebd5Sriastradh xGLXVendorPrivateWithReplyReq *vpreq; 1233464ebd5Sriastradh xGLXMakeCurrentReadSGIReq *req; 1243464ebd5Sriastradh 1253464ebd5Sriastradh GetReqExtra(GLXVendorPrivateWithReply, 1263464ebd5Sriastradh sz_xGLXMakeCurrentReadSGIReq - 1273464ebd5Sriastradh sz_xGLXVendorPrivateWithReplyReq, vpreq); 1283464ebd5Sriastradh req = (xGLXMakeCurrentReadSGIReq *) vpreq; 1293464ebd5Sriastradh req->reqType = opcode; 1303464ebd5Sriastradh req->glxCode = X_GLXVendorPrivateWithReply; 1313464ebd5Sriastradh req->vendorCode = X_GLXvop_MakeCurrentReadSGI; 1323464ebd5Sriastradh req->drawable = draw; 1333464ebd5Sriastradh req->readable = read; 1343464ebd5Sriastradh req->context = gc_id; 1353464ebd5Sriastradh req->oldContextTag = gc_tag; 1363464ebd5Sriastradh } 1373464ebd5Sriastradh } 1383464ebd5Sriastradh 139af69d88dSmrg ret = _XReply(dpy, (xReply *) &reply, 0, False); 140af69d88dSmrg 141af69d88dSmrg if (out_tag) 142af69d88dSmrg *out_tag = reply.contextTag; 1433464ebd5Sriastradh 1443464ebd5Sriastradh UnlockDisplay(dpy); 1453464ebd5Sriastradh SyncHandle(); 1463464ebd5Sriastradh 1473464ebd5Sriastradh return ret; 1483464ebd5Sriastradh} 1493464ebd5Sriastradh 1503464ebd5Sriastradhstatic int 1513464ebd5Sriastradhindirect_bind_context(struct glx_context *gc, struct glx_context *old, 1523464ebd5Sriastradh GLXDrawable draw, GLXDrawable read) 1533464ebd5Sriastradh{ 1543464ebd5Sriastradh GLXContextTag tag; 1553464ebd5Sriastradh Display *dpy = gc->psc->dpy; 156af69d88dSmrg Bool sent; 1573464ebd5Sriastradh 1583464ebd5Sriastradh if (old != &dummyContext && !old->isDirect && old->psc->dpy == dpy) { 1593464ebd5Sriastradh tag = old->currentContextTag; 1603464ebd5Sriastradh old->currentContextTag = 0; 1613464ebd5Sriastradh } else { 1623464ebd5Sriastradh tag = 0; 1633464ebd5Sriastradh } 1643464ebd5Sriastradh 16501e04c3fSmrg sent = SendMakeCurrentRequest(dpy, gc->xid, tag, draw, read, 166af69d88dSmrg &gc->currentContextTag); 1673464ebd5Sriastradh 1687ec681f3Smrg if (sent) { 1697ec681f3Smrg if (!IndirectAPI) 1707ec681f3Smrg IndirectAPI = __glXNewIndirectAPI(); 1717ec681f3Smrg _glapi_set_dispatch(IndirectAPI); 1727ec681f3Smrg 1737ec681f3Smrg /* The indirect vertex array state must to be initialised after we 1747ec681f3Smrg * have setup the context, as it needs to query server attributes. 1757ec681f3Smrg * 1767ec681f3Smrg * At the point this is called gc->currentDpy is not initialized 1777ec681f3Smrg * nor is the thread's current context actually set. Hence the 1787ec681f3Smrg * cleverness before the GetString calls. 1797ec681f3Smrg */ 1807ec681f3Smrg __GLXattribute *state = gc->client_state_private; 1817ec681f3Smrg if (state && state->array_state == NULL) { 1827ec681f3Smrg gc->currentDpy = gc->psc->dpy; 1837ec681f3Smrg __glXSetCurrentContext(gc); 1847ec681f3Smrg __indirect_glGetString(GL_EXTENSIONS); 1857ec681f3Smrg __indirect_glGetString(GL_VERSION); 1867ec681f3Smrg __glXInitVertexArrayState(gc); 1877ec681f3Smrg } 1887ec681f3Smrg } 1893464ebd5Sriastradh 190af69d88dSmrg return !sent; 1913464ebd5Sriastradh} 1923464ebd5Sriastradh 1933464ebd5Sriastradhstatic void 1943464ebd5Sriastradhindirect_unbind_context(struct glx_context *gc, struct glx_context *new) 1953464ebd5Sriastradh{ 1963464ebd5Sriastradh Display *dpy = gc->psc->dpy; 1973464ebd5Sriastradh 1983464ebd5Sriastradh if (gc == new) 1993464ebd5Sriastradh return; 2003464ebd5Sriastradh 20101e04c3fSmrg /* We are either switching to no context, away from an indirect 2023464ebd5Sriastradh * context to a direct context or from one dpy to another and have 2033464ebd5Sriastradh * to send a request to the dpy to unbind the previous context. 2043464ebd5Sriastradh */ 2053464ebd5Sriastradh if (!new || new->isDirect || new->psc->dpy != dpy) { 20601e04c3fSmrg SendMakeCurrentRequest(dpy, None, gc->currentContextTag, None, None, 20701e04c3fSmrg NULL); 2083464ebd5Sriastradh gc->currentContextTag = 0; 2093464ebd5Sriastradh } 2103464ebd5Sriastradh} 2113464ebd5Sriastradh 2123464ebd5Sriastradhstatic void 2133464ebd5Sriastradhindirect_wait_gl(struct glx_context *gc) 2143464ebd5Sriastradh{ 2153464ebd5Sriastradh xGLXWaitGLReq *req; 2163464ebd5Sriastradh Display *dpy = gc->currentDpy; 2173464ebd5Sriastradh 2183464ebd5Sriastradh /* Flush any pending commands out */ 2193464ebd5Sriastradh __glXFlushRenderBuffer(gc, gc->pc); 2203464ebd5Sriastradh 2213464ebd5Sriastradh /* Send the glXWaitGL request */ 2223464ebd5Sriastradh LockDisplay(dpy); 2233464ebd5Sriastradh GetReq(GLXWaitGL, req); 2243464ebd5Sriastradh req->reqType = gc->majorOpcode; 2253464ebd5Sriastradh req->glxCode = X_GLXWaitGL; 2263464ebd5Sriastradh req->contextTag = gc->currentContextTag; 2273464ebd5Sriastradh UnlockDisplay(dpy); 2283464ebd5Sriastradh SyncHandle(); 2293464ebd5Sriastradh} 2303464ebd5Sriastradh 2313464ebd5Sriastradhstatic void 2323464ebd5Sriastradhindirect_wait_x(struct glx_context *gc) 2333464ebd5Sriastradh{ 2343464ebd5Sriastradh xGLXWaitXReq *req; 2353464ebd5Sriastradh Display *dpy = gc->currentDpy; 2363464ebd5Sriastradh 2373464ebd5Sriastradh /* Flush any pending commands out */ 2383464ebd5Sriastradh __glXFlushRenderBuffer(gc, gc->pc); 2393464ebd5Sriastradh 2403464ebd5Sriastradh LockDisplay(dpy); 2413464ebd5Sriastradh GetReq(GLXWaitX, req); 2423464ebd5Sriastradh req->reqType = gc->majorOpcode; 2433464ebd5Sriastradh req->glxCode = X_GLXWaitX; 2443464ebd5Sriastradh req->contextTag = gc->currentContextTag; 2453464ebd5Sriastradh UnlockDisplay(dpy); 2463464ebd5Sriastradh SyncHandle(); 2473464ebd5Sriastradh} 2483464ebd5Sriastradh 2493464ebd5Sriastradhstatic const struct glx_context_vtable indirect_context_vtable = { 250af69d88dSmrg .destroy = indirect_destroy_context, 251af69d88dSmrg .bind = indirect_bind_context, 252af69d88dSmrg .unbind = indirect_unbind_context, 253af69d88dSmrg .wait_gl = indirect_wait_gl, 254af69d88dSmrg .wait_x = indirect_wait_x, 2553464ebd5Sriastradh}; 2563464ebd5Sriastradh 2577ec681f3Smrg_X_HIDDEN struct glx_context * 2587ec681f3Smrgindirect_create_context(struct glx_screen *psc, 2597ec681f3Smrg struct glx_config *mode, 2607ec681f3Smrg struct glx_context *shareList, int renderType) 2617ec681f3Smrg{ 2627ec681f3Smrg unsigned error = 0; 2637ec681f3Smrg const uint32_t attribs[] = { GLX_RENDER_TYPE, renderType }; 2647ec681f3Smrg 2657ec681f3Smrg return indirect_create_context_attribs(psc, mode, shareList, 2667ec681f3Smrg 1, attribs, &error); 2677ec681f3Smrg} 2687ec681f3Smrg 2693464ebd5Sriastradh/** 2703464ebd5Sriastradh * \todo Eliminate \c __glXInitVertexArrayState. Replace it with a new 2713464ebd5Sriastradh * function called \c __glXAllocateClientState that allocates the memory and 2723464ebd5Sriastradh * does all the initialization (including the pixel pack / unpack). 2733464ebd5Sriastradh */ 2743464ebd5Sriastradh_X_HIDDEN struct glx_context * 2757ec681f3Smrgindirect_create_context_attribs(struct glx_screen *psc, 2767ec681f3Smrg struct glx_config *mode, 2777ec681f3Smrg struct glx_context *shareList, 2787ec681f3Smrg unsigned num_attribs, 2797ec681f3Smrg const uint32_t *attribs, 2807ec681f3Smrg unsigned *error) 2813464ebd5Sriastradh{ 2823464ebd5Sriastradh struct glx_context *gc; 2833464ebd5Sriastradh int bufSize; 2843464ebd5Sriastradh CARD8 opcode; 2853464ebd5Sriastradh __GLXattribute *state; 2867ec681f3Smrg int i, renderType = GLX_RGBA_TYPE; 2877ec681f3Smrg uint32_t mask = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 2887ec681f3Smrg uint32_t major = 1; 2897ec681f3Smrg uint32_t minor = 0; 2903464ebd5Sriastradh 2913464ebd5Sriastradh opcode = __glXSetupForCommand(psc->dpy); 2923464ebd5Sriastradh if (!opcode) { 2933464ebd5Sriastradh return NULL; 2943464ebd5Sriastradh } 2953464ebd5Sriastradh 2967ec681f3Smrg for (i = 0; i < num_attribs; i++) { 2977ec681f3Smrg uint32_t attr = attribs[i*2], val = attribs[i*2 + 1]; 2987ec681f3Smrg 2997ec681f3Smrg if (attr == GLX_RENDER_TYPE) 3007ec681f3Smrg renderType = val; 3017ec681f3Smrg if (attr == GLX_CONTEXT_PROFILE_MASK_ARB) 3027ec681f3Smrg mask = val; 3037ec681f3Smrg if (attr == GLX_CONTEXT_MAJOR_VERSION_ARB) 3047ec681f3Smrg major = val; 3057ec681f3Smrg if (attr == GLX_CONTEXT_MINOR_VERSION_ARB) 3067ec681f3Smrg minor = val; 3077ec681f3Smrg } 3087ec681f3Smrg 3097ec681f3Smrg /* We have no indirect support for core or ES contexts, and our compat 3107ec681f3Smrg * context support is limited to GL 1.4. 3117ec681f3Smrg */ 3127ec681f3Smrg if (mask != GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB || 3137ec681f3Smrg major != 1 || 3147ec681f3Smrg minor > 4) { 3157ec681f3Smrg return NULL; 3167ec681f3Smrg } 3177ec681f3Smrg 3187ec681f3Smrg /* We can't share with a direct context */ 3197ec681f3Smrg if (shareList && shareList->isDirect) 3207ec681f3Smrg return NULL; 3217ec681f3Smrg 3223464ebd5Sriastradh /* Allocate our context record */ 323af69d88dSmrg gc = calloc(1, sizeof *gc); 3243464ebd5Sriastradh if (!gc) { 3253464ebd5Sriastradh /* Out of memory */ 3263464ebd5Sriastradh return NULL; 3273464ebd5Sriastradh } 3283464ebd5Sriastradh 3293464ebd5Sriastradh glx_context_init(gc, psc, mode); 3303464ebd5Sriastradh gc->isDirect = GL_FALSE; 3313464ebd5Sriastradh gc->vtable = &indirect_context_vtable; 332af69d88dSmrg state = calloc(1, sizeof(struct __GLXattributeRec)); 333af69d88dSmrg gc->renderType = renderType; 334af69d88dSmrg 3353464ebd5Sriastradh if (state == NULL) { 3363464ebd5Sriastradh /* Out of memory */ 337af69d88dSmrg free(gc); 3383464ebd5Sriastradh return NULL; 3393464ebd5Sriastradh } 3403464ebd5Sriastradh gc->client_state_private = state; 34101e04c3fSmrg state->NoDrawArraysProtocol = env_var_as_boolean("LIBGL_NO_DRAWARRAYS", false); 3423464ebd5Sriastradh 3433464ebd5Sriastradh /* 3443464ebd5Sriastradh ** Create a temporary buffer to hold GLX rendering commands. The size 3453464ebd5Sriastradh ** of the buffer is selected so that the maximum number of GLX rendering 3463464ebd5Sriastradh ** commands can fit in a single X packet and still have room in the X 3473464ebd5Sriastradh ** packet for the GLXRenderReq header. 3483464ebd5Sriastradh */ 3493464ebd5Sriastradh 3503464ebd5Sriastradh bufSize = (XMaxRequestSize(psc->dpy) * 4) - sz_xGLXRenderReq; 351af69d88dSmrg gc->buf = malloc(bufSize); 3523464ebd5Sriastradh if (!gc->buf) { 353af69d88dSmrg free(gc->client_state_private); 354af69d88dSmrg free(gc); 3553464ebd5Sriastradh return NULL; 3563464ebd5Sriastradh } 3573464ebd5Sriastradh gc->bufSize = bufSize; 3583464ebd5Sriastradh 3593464ebd5Sriastradh /* Fill in the new context */ 3603464ebd5Sriastradh gc->renderMode = GL_RENDER; 3613464ebd5Sriastradh 3623464ebd5Sriastradh state->storePack.alignment = 4; 3633464ebd5Sriastradh state->storeUnpack.alignment = 4; 3643464ebd5Sriastradh 3653464ebd5Sriastradh gc->attributes.stackPointer = &gc->attributes.stack[0]; 3663464ebd5Sriastradh 3673464ebd5Sriastradh gc->pc = gc->buf; 3683464ebd5Sriastradh gc->bufEnd = gc->buf + bufSize; 3693464ebd5Sriastradh gc->isDirect = GL_FALSE; 3703464ebd5Sriastradh if (__glXDebug) { 3713464ebd5Sriastradh /* 3723464ebd5Sriastradh ** Set limit register so that there will be one command per packet 3733464ebd5Sriastradh */ 3743464ebd5Sriastradh gc->limit = gc->buf; 3753464ebd5Sriastradh } 3763464ebd5Sriastradh else { 3773464ebd5Sriastradh gc->limit = gc->buf + bufSize - __GLX_BUFFER_LIMIT_SIZE; 3783464ebd5Sriastradh } 3793464ebd5Sriastradh gc->majorOpcode = opcode; 3803464ebd5Sriastradh 3813464ebd5Sriastradh /* 3823464ebd5Sriastradh ** Constrain the maximum drawing command size allowed to be 3837ec681f3Smrg ** transferred using the X_GLXRender protocol request. First 3847ec681f3Smrg ** constrain by a software limit, then constrain by the protocol 3853464ebd5Sriastradh ** limit. 3863464ebd5Sriastradh */ 3877ec681f3Smrg gc->maxSmallRenderCommandSize = MIN3(bufSize, __GLX_RENDER_CMD_SIZE_LIMIT, 3887ec681f3Smrg __GLX_MAX_RENDER_CMD_SIZE); 389af69d88dSmrg 390af69d88dSmrg 3917ec681f3Smrg return gc; 392af69d88dSmrg} 393af69d88dSmrg 394af69d88dSmrgstatic const struct glx_screen_vtable indirect_screen_vtable = { 395af69d88dSmrg .create_context = indirect_create_context, 396af69d88dSmrg .create_context_attribs = indirect_create_context_attribs, 397af69d88dSmrg .query_renderer_integer = NULL, 398af69d88dSmrg .query_renderer_string = NULL, 3993464ebd5Sriastradh}; 4003464ebd5Sriastradh 4013464ebd5Sriastradh_X_HIDDEN struct glx_screen * 4023464ebd5Sriastradhindirect_create_screen(int screen, struct glx_display * priv) 4033464ebd5Sriastradh{ 4043464ebd5Sriastradh struct glx_screen *psc; 4053464ebd5Sriastradh 406af69d88dSmrg psc = calloc(1, sizeof *psc); 4073464ebd5Sriastradh if (psc == NULL) 4083464ebd5Sriastradh return NULL; 4093464ebd5Sriastradh 4103464ebd5Sriastradh glx_screen_init(psc, screen, priv); 4113464ebd5Sriastradh psc->vtable = &indirect_screen_vtable; 4123464ebd5Sriastradh 4133464ebd5Sriastradh return psc; 4143464ebd5Sriastradh} 415af69d88dSmrg 416af69d88dSmrg#endif 417