1/* 2 * SBUS and OpenPROM access functions. 3 * 4 * Copyright (C) 2000 Jakub Jelinek (jakub@redhat.com) 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * JAKUB JELINEK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 24#ifdef HAVE_XORG_CONFIG_H 25#include <xorg-config.h> 26#endif 27 28#include <fcntl.h> 29#include <stdio.h> 30#include <unistd.h> 31#include <stdlib.h> 32#include <sys/ioctl.h> 33#include <sys/mman.h> 34#ifdef sun 35#include <sys/utsname.h> 36#endif 37#include "xf86.h" 38#include "xf86Priv.h" 39#include "xf86_OSlib.h" 40 41#include "xf86sbusBus.h" 42#include "xf86Sbus.h" 43 44int promRootNode; 45 46static int promFd = -1; 47static int promCurrentNode; 48static int promOpenCount = 0; 49static int promP1275 = -1; 50#define MAX_PROP 128 51#define MAX_VAL (4096-128-4) 52static struct openpromio *promOpio; 53 54sbusDevicePtr *xf86SbusInfo = NULL; 55 56struct sbus_devtable sbusDeviceTable[] = { 57 { SBUS_DEVICE_BW2, FBTYPE_SUN2BW, "bwtwo", "sunbw2", "Sun Monochrome (bwtwo)" }, 58 { SBUS_DEVICE_CG2, FBTYPE_SUN2COLOR, "cgtwo", NULL, "Sun Color2 (cgtwo)" }, 59 { SBUS_DEVICE_CG3, FBTYPE_SUN3COLOR, "cgthree", "suncg3", "Sun Color3 (cgthree)" }, 60 { SBUS_DEVICE_CG4, FBTYPE_SUN4COLOR, "cgfour", NULL, "Sun Color4 (cgfour)" }, 61 { SBUS_DEVICE_CG6, FBTYPE_SUNFAST_COLOR, "cgsix", "suncg6", "Sun GX" }, 62 { SBUS_DEVICE_CG8, FBTYPE_MEMCOLOR, "cgeight", NULL, "Sun CG8/RasterOps" }, 63 { SBUS_DEVICE_CG12, FBTYPE_SUNGP3, "cgtwelve", NULL, "Sun GS (cgtwelve)" }, 64 { SBUS_DEVICE_CG14, FBTYPE_MDICOLOR, "cgfourteen", "suncg14", "Sun SX" }, 65 { SBUS_DEVICE_GT, FBTYPE_SUNGT, "gt", NULL, "Sun Graphics Tower" }, 66 { SBUS_DEVICE_MGX, -1, "mgx", NULL, "Quantum 3D MGXplus" }, 67 { SBUS_DEVICE_LEO, FBTYPE_SUNLEO, "leo", "sunleo", "Sun ZX or Turbo ZX" }, 68 { SBUS_DEVICE_TCX, FBTYPE_TCXCOLOR, "tcx", "suntcx", "Sun TCX" }, 69 { SBUS_DEVICE_FFB, FBTYPE_CREATOR, "ffb", "sunffb", "Sun FFB" }, 70 { SBUS_DEVICE_FFB, FBTYPE_CREATOR, "afb", "sunffb", "Sun Elite3D" }, 71 { 0, 0, NULL } 72}; 73 74int 75promGetSibling(int node) 76{ 77 promOpio->oprom_size = sizeof(int); 78 79 if (node == -1) return 0; 80 *(int *)promOpio->oprom_array = node; 81 if (ioctl(promFd, OPROMNEXT, promOpio) < 0) 82 return 0; 83 promCurrentNode = *(int *)promOpio->oprom_array; 84 return *(int *)promOpio->oprom_array; 85} 86 87int 88promGetChild(int node) 89{ 90 promOpio->oprom_size = sizeof(int); 91 92 if (!node || node == -1) return 0; 93 *(int *)promOpio->oprom_array = node; 94 if (ioctl(promFd, OPROMCHILD, promOpio) < 0) 95 return 0; 96 promCurrentNode = *(int *)promOpio->oprom_array; 97 return *(int *)promOpio->oprom_array; 98} 99 100char * 101promGetProperty(const char *prop, int *lenp) 102{ 103 promOpio->oprom_size = MAX_VAL; 104 105 strcpy(promOpio->oprom_array, prop); 106 if (ioctl(promFd, OPROMGETPROP, promOpio) < 0) 107 return 0; 108 if (lenp) *lenp = promOpio->oprom_size; 109 return promOpio->oprom_array; 110} 111 112int 113promGetBool(const char *prop) 114{ 115 promOpio->oprom_size = 0; 116 117 *(int *)promOpio->oprom_array = 0; 118 for (;;) { 119 promOpio->oprom_size = MAX_PROP; 120 if (ioctl(promFd, OPROMNXTPROP, promOpio) < 0) 121 return 0; 122 if (!promOpio->oprom_size) 123 return 0; 124 if (!strcmp(promOpio->oprom_array, prop)) 125 return 1; 126 } 127} 128 129#define PROM_NODE_SIBLING 0x01 130#define PROM_NODE_PREF 0x02 131#define PROM_NODE_SBUS 0x04 132#define PROM_NODE_EBUS 0x08 133#define PROM_NODE_PCI 0x10 134 135static int 136promSetNode(sbusPromNodePtr pnode) 137{ 138 int node; 139 140 if (!pnode->node || pnode->node == -1) 141 return -1; 142 if (pnode->cookie[0] & PROM_NODE_SIBLING) 143 node = promGetSibling(pnode->cookie[1]); 144 else 145 node = promGetChild(pnode->cookie[1]); 146 if (pnode->node != node) 147 return -1; 148 return 0; 149} 150 151static void 152promIsP1275(void) 153{ 154#ifdef linux 155 FILE *f; 156 char buffer[1024]; 157 158 if (promP1275 != -1) 159 return; 160 promP1275 = 0; 161 f = fopen("/proc/cpuinfo","r"); 162 if (!f) return; 163 while (fgets(buffer, 1024, f) != NULL) 164 if (!strncmp (buffer, "type", 4) && strstr (buffer, "sun4u")) { 165 promP1275 = 1; 166 break; 167 } 168 fclose(f); 169#elif defined(sun) 170 struct utsname buffer; 171 172 if ((uname(&buffer) >= 0) && !strcmp(buffer.machine, "sun4u")) 173 promP1275 = TRUE; 174 else 175 promP1275 = FALSE; 176#elif defined(__FreeBSD__) 177 promP1275 = TRUE; 178#else 179#error Missing promIsP1275() function for this OS 180#endif 181} 182 183void 184sparcPromClose(void) 185{ 186 if (promOpenCount > 1) { 187 promOpenCount--; 188 return; 189 } 190 if (promFd != -1) { 191 close(promFd); 192 promFd = -1; 193 } 194 free(promOpio); 195 promOpio = NULL; 196 promOpenCount = 0; 197} 198 199int 200sparcPromInit(void) 201{ 202 if (promOpenCount) { 203 promOpenCount++; 204 return 0; 205 } 206 promFd = open("/dev/openprom", O_RDONLY, 0); 207 if (promFd == -1) 208 return -1; 209 promOpio = (struct openpromio *)malloc(4096); 210 if (!promOpio) { 211 sparcPromClose(); 212 return -1; 213 } 214 promRootNode = promGetSibling(0); 215 if (!promRootNode) { 216 sparcPromClose(); 217 return -1; 218 } 219 promIsP1275(); 220 promOpenCount++; 221 222 return 0; 223} 224 225char * 226sparcPromGetProperty(sbusPromNodePtr pnode, const char *prop, int *lenp) 227{ 228 if (promSetNode(pnode)) 229 return NULL; 230 return promGetProperty(prop, lenp); 231} 232 233int 234sparcPromGetBool(sbusPromNodePtr pnode, const char *prop) 235{ 236 if (promSetNode(pnode)) 237 return 0; 238 return promGetBool(prop); 239} 240 241static char * 242promWalkGetDriverName(int node, int oldnode) 243{ 244 int nextnode; 245 int len; 246 char *prop; 247 int devId, i; 248 249 prop = promGetProperty("device_type", &len); 250 if (prop && (len > 0)) do { 251 if (!strcmp(prop, "display")) { 252 prop = promGetProperty("name", &len); 253 if (!prop || len <= 0) 254 break; 255 while ((*prop >= 'A' && *prop <= 'Z') || *prop == ',') 256 prop++; 257 for (i = 0; sbusDeviceTable[i].devId; i++) 258 if (!strcmp(prop, sbusDeviceTable[i].promName)) 259 break; 260 devId = sbusDeviceTable[i].devId; 261 if (!devId) 262 break; 263 if (sbusDeviceTable[i].driverName) 264 return sbusDeviceTable[i].driverName; 265 } 266 } while (0); 267 268 nextnode = promGetChild(node); 269 if (nextnode) { 270 char *name; 271 name = promWalkGetDriverName(nextnode, node); 272 if (name) 273 return name; 274 } 275 276 nextnode = promGetSibling(node); 277 if (nextnode) 278 return promWalkGetDriverName(nextnode, node); 279 return NULL; 280} 281 282char * 283sparcDriverName(void) 284{ 285 char *name; 286 287 if (sparcPromInit() < 0) 288 return NULL; 289 promGetSibling(0); 290 name = promWalkGetDriverName(promRootNode, 0); 291 sparcPromClose(); 292 return name; 293} 294 295static void 296promWalkAssignNodes(int node, int oldnode, int flags, sbusDevicePtr *devicePtrs) 297{ 298 int nextnode; 299 int len, sbus = flags & PROM_NODE_SBUS; 300 char *prop; 301 int devId, i, j; 302 sbusPromNode pNode, pNode2; 303 304 prop = promGetProperty("device_type", &len); 305 if (prop && (len > 0)) do { 306 if (!strcmp(prop, "display")) { 307 prop = promGetProperty("name", &len); 308 if (!prop || len <= 0) 309 break; 310 while ((*prop >= 'A' && *prop <= 'Z') || *prop == ',') 311 prop++; 312 for (i = 0; sbusDeviceTable[i].devId; i++) 313 if (!strcmp(prop, sbusDeviceTable[i].promName)) 314 break; 315 devId = sbusDeviceTable[i].devId; 316 if (!devId) 317 break; 318 if (!sbus) { 319 if (devId == SBUS_DEVICE_FFB) { 320 /* 321 * All /SUNW,ffb outside of SBUS tree come before all 322 * /SUNW,afb outside of SBUS tree in Linux. 323 */ 324 if (!strcmp(prop, "afb")) 325 flags |= PROM_NODE_PREF; 326 } else if (devId != SBUS_DEVICE_CG14) 327 break; 328 } 329 for (i = 0; i < 32; i++) { 330 if (!devicePtrs[i] || devicePtrs[i]->devId != devId) 331 continue; 332 if (devicePtrs[i]->node.node) { 333 if ((devicePtrs[i]->node.cookie[0] & ~PROM_NODE_SIBLING) <= 334 (flags & ~PROM_NODE_SIBLING)) 335 continue; 336 for (j = i + 1, pNode = devicePtrs[i]->node; j < 32; j++) { 337 if (!devicePtrs[j] || devicePtrs[j]->devId != devId) 338 continue; 339 pNode2 = devicePtrs[j]->node; 340 devicePtrs[j]->node = pNode; 341 pNode = pNode2; 342 } 343 } 344 devicePtrs[i]->node.node = node; 345 devicePtrs[i]->node.cookie[0] = flags; 346 devicePtrs[i]->node.cookie[1] = oldnode; 347 break; 348 } 349 break; 350 } 351 } while (0); 352 353 prop = promGetProperty("name", &len); 354 if (prop && len > 0) { 355 if (!strcmp(prop, "sbus") || !strcmp(prop, "sbi")) 356 sbus = PROM_NODE_SBUS; 357 } 358 359 nextnode = promGetChild(node); 360 if (nextnode) 361 promWalkAssignNodes(nextnode, node, sbus, devicePtrs); 362 363 nextnode = promGetSibling(node); 364 if (nextnode) 365 promWalkAssignNodes(nextnode, node, PROM_NODE_SIBLING | sbus, devicePtrs); 366} 367 368void 369sparcPromAssignNodes(void) 370{ 371 sbusDevicePtr psdp, *psdpp; 372 int n, holes = 0, i, j; 373 FILE *f; 374 sbusDevicePtr devicePtrs[32]; 375 376 memset(devicePtrs, 0, sizeof(devicePtrs)); 377 for (psdpp = xf86SbusInfo, n = 0; (psdp = *psdpp); psdpp++, n++) { 378 if (psdp->fbNum != n) 379 holes = 1; 380 devicePtrs[psdp->fbNum] = psdp; 381 } 382 if (holes && (f = fopen("/proc/fb", "r")) != NULL) { 383 /* We could not open one of fb devices, check /proc/fb to see what 384 * were the types of the cards missed. */ 385 char buffer[64]; 386 int fbNum, devId; 387 static struct { 388 int devId; 389 char *prefix; 390 } procFbPrefixes[] = { 391 { SBUS_DEVICE_BW2, "BWtwo" }, 392 { SBUS_DEVICE_CG14, "CGfourteen" }, 393 { SBUS_DEVICE_CG6, "CGsix" }, 394 { SBUS_DEVICE_CG3, "CGthree" }, 395 { SBUS_DEVICE_FFB, "Creator" }, 396 { SBUS_DEVICE_FFB, "Elite 3D" }, 397 { SBUS_DEVICE_LEO, "Leo" }, 398 { SBUS_DEVICE_TCX, "TCX" }, 399 { 0, NULL }, 400 }; 401 402 while (fscanf(f, "%d %63s\n", &fbNum, buffer) == 2) { 403 for (i = 0; procFbPrefixes[i].devId; i++) 404 if (! strncmp(procFbPrefixes[i].prefix, buffer, 405 strlen(procFbPrefixes[i].prefix))) 406 break; 407 devId = procFbPrefixes[i].devId; 408 if (! devId) continue; 409 if (devicePtrs[fbNum]) { 410 if (devicePtrs[fbNum]->devId != devId) 411 xf86ErrorF("Inconsistent /proc/fb with FBIOGATTR\n"); 412 } else if (!devicePtrs[fbNum]) { 413 devicePtrs[fbNum] = psdp = xnfcalloc(sizeof (sbusDevice), 1); 414 psdp->devId = devId; 415 psdp->fbNum = fbNum; 416 psdp->fd = -2; 417 } 418 } 419 fclose(f); 420 } 421 promGetSibling(0); 422 promWalkAssignNodes(promRootNode, 0, PROM_NODE_PREF, devicePtrs); 423 for (i = 0, j = 0; i < 32; i++) 424 if (devicePtrs[i] && devicePtrs[i]->fbNum == -1) 425 j++; 426 xf86SbusInfo = xnfrealloc(xf86SbusInfo, sizeof(psdp) * (n + j + 1)); 427 for (i = 0, psdpp = xf86SbusInfo; i < 32; i++) 428 if (devicePtrs[i]) { 429 if (devicePtrs[i]->fbNum == -1) { 430 memmove(psdpp + 1, psdpp, sizeof(psdpp) * (n + 1)); 431 *psdpp = devicePtrs[i]; 432 } else 433 n--; 434 } 435} 436 437static char * 438promGetReg(int type) 439{ 440 char *prop; 441 int len; 442 static char regstr[40]; 443 444 regstr[0] = 0; 445 prop = promGetProperty("reg", &len); 446 if (prop && len >= 4) { 447 unsigned int *reg = (unsigned int *)prop; 448 if (!promP1275 || (type == PROM_NODE_SBUS) || (type == PROM_NODE_EBUS)) 449 sprintf (regstr, "@%x,%x", reg[0], reg[1]); 450 else if (type == PROM_NODE_PCI) { 451 if ((reg[0] >> 8) & 7) 452 sprintf (regstr, "@%x,%x", (reg[0] >> 11) & 0x1f, (reg[0] >> 8) & 7); 453 else 454 sprintf (regstr, "@%x", (reg[0] >> 11) & 0x1f); 455 } else if (len == 4) 456 sprintf (regstr, "@%x", reg[0]); 457 else { 458 unsigned int regs[2]; 459 460 /* Things get more complicated on UPA. If upa-portid exists, 461 then address is @upa-portid,second-int-in-reg, otherwise 462 it is @first-int-in-reg/16,second-int-in-reg (well, probably 463 upa-portid always exists, but just to be safe). */ 464 memcpy (regs, reg, sizeof(regs)); 465 prop = promGetProperty("upa-portid", &len); 466 if (prop && len == 4) { 467 reg = (unsigned int *)prop; 468 sprintf (regstr, "@%x,%x", reg[0], regs[1]); 469 } else 470 sprintf (regstr, "@%x,%x", regs[0] >> 4, regs[1]); 471 } 472 } 473 return regstr; 474} 475 476static int 477promWalkNode2Pathname(char *path, int parent, int node, int searchNode, int type) 478{ 479 int nextnode; 480 int len, ntype = type; 481 char *prop, *p; 482 483 prop = promGetProperty("name", &len); 484 *path = '/'; 485 if (!prop || len <= 0) 486 return 0; 487 if ((!strcmp(prop, "sbus") || !strcmp(prop, "sbi")) && !type) 488 ntype = PROM_NODE_SBUS; 489 else if (!strcmp(prop, "ebus") && type == PROM_NODE_PCI) 490 ntype = PROM_NODE_EBUS; 491 else if (!strcmp(prop, "pci") && !type) 492 ntype = PROM_NODE_PCI; 493 strcpy (path + 1, prop); 494 p = promGetReg(type); 495 if (*p) 496 strcat (path, p); 497 if (node == searchNode) 498 return 1; 499 nextnode = promGetChild(node); 500 if (nextnode && 501 promWalkNode2Pathname(strchr(path, 0), node, nextnode, searchNode, ntype)) 502 return 1; 503 nextnode = promGetSibling(node); 504 if (nextnode && 505 promWalkNode2Pathname(path, parent, nextnode, searchNode, type)) 506 return 1; 507 return 0; 508} 509 510char * 511sparcPromNode2Pathname(sbusPromNodePtr pnode) 512{ 513 char *ret; 514 515 if (!pnode->node) return NULL; 516 ret = malloc(4096); 517 if (!ret) return NULL; 518 if (promWalkNode2Pathname(ret, promRootNode, promGetChild(promRootNode), pnode->node, 0)) 519 return ret; 520 free(ret); 521 return NULL; 522} 523 524static int 525promWalkPathname2Node(char *name, char *regstr, int parent, int type) 526{ 527 int len, node, ret; 528 char *prop, *p; 529 530 for (;;) { 531 prop = promGetProperty("name", &len); 532 if (!prop || len <= 0) 533 return 0; 534 if ((!strcmp(prop, "sbus") || !strcmp(prop, "sbi")) && !type) 535 type = PROM_NODE_SBUS; 536 else if (!strcmp(prop, "ebus") && type == PROM_NODE_PCI) 537 type = PROM_NODE_EBUS; 538 else if (!strcmp(prop, "pci") && !type) 539 type = PROM_NODE_PCI; 540 for (node = promGetChild(parent); node; node = promGetSibling(node)) { 541 prop = promGetProperty("name", &len); 542 if (!prop || len <= 0) 543 continue; 544 if (*name && strcmp(name, prop)) 545 continue; 546 if (*regstr) { 547 p = promGetReg(type); 548 if (! *p || strcmp(p + 1, regstr)) 549 continue; 550 } 551 break; 552 } 553 if (!node) { 554 for (node = promGetChild(parent); node; node = promGetSibling(node)) { 555 ret = promWalkPathname2Node(name, regstr, node, type); 556 if (ret) return ret; 557 } 558 return 0; 559 } 560 name = strchr(regstr, 0) + 1; 561 if (! *name) 562 return node; 563 p = strchr(name, '/'); 564 if (p) 565 *p = 0; 566 else 567 p = strchr(name, 0); 568 regstr = strchr(name, '@'); 569 if (regstr) 570 *regstr++ = 0; 571 else 572 regstr = p; 573 if (name == regstr) 574 return 0; 575 parent = node; 576 } 577} 578 579int 580sparcPromPathname2Node(const char *pathName) 581{ 582 int i; 583 char *name, *regstr, *p; 584 585 i = strlen(pathName); 586 name = malloc(i + 2); 587 if (! name) return 0; 588 strcpy (name, pathName); 589 name [i + 1] = 0; 590 if (name[0] != '/') 591 return 0; 592 p = strchr(name + 1, '/'); 593 if (p) 594 *p = 0; 595 else 596 p = strchr(name, 0); 597 regstr = strchr(name, '@'); 598 if (regstr) 599 *regstr++ = 0; 600 else 601 regstr = p; 602 if (name + 1 == regstr) 603 return 0; 604 promGetSibling(0); 605 i = promWalkPathname2Node(name + 1, regstr, promRootNode, 0); 606 free(name); 607 return i; 608} 609 610pointer 611xf86MapSbusMem(sbusDevicePtr psdp, unsigned long offset, unsigned long size) 612{ 613 pointer ret; 614 unsigned long pagemask = getpagesize() - 1; 615 unsigned long off = offset & ~pagemask; 616 unsigned long len = ((offset + size + pagemask) & ~pagemask) - off; 617 618 if (psdp->fd == -1) { 619 psdp->fd = open(psdp->device, O_RDWR); 620 if (psdp->fd == -1) 621 return NULL; 622 } else if (psdp->fd < 0) 623 return NULL; 624 625 ret = (pointer) mmap (NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, 626 psdp->fd, off); 627 if (ret == (pointer) -1) { 628 ret = (pointer) mmap (NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, 629 psdp->fd, off); 630 } 631 if (ret == (pointer) -1) 632 return NULL; 633 634 return (char *)ret + (offset - off); 635} 636 637void 638xf86UnmapSbusMem(sbusDevicePtr psdp, pointer addr, unsigned long size) 639{ 640 unsigned long mask = getpagesize() - 1; 641 unsigned long base = (unsigned long)addr & ~mask; 642 unsigned long len = (((unsigned long)addr + size + mask) & ~mask) - base; 643 644 munmap ((pointer)base, len); 645} 646 647/* Tell OS that we are driving the HW cursor ourselves. */ 648void 649xf86SbusHideOsHwCursor(sbusDevicePtr psdp) 650{ 651 struct fbcursor fbcursor; 652 unsigned char zeros[8]; 653 654 memset(&fbcursor, 0, sizeof(fbcursor)); 655 memset(&zeros, 0, sizeof(zeros)); 656 fbcursor.cmap.count = 2; 657 fbcursor.cmap.red = zeros; 658 fbcursor.cmap.green = zeros; 659 fbcursor.cmap.blue = zeros; 660 fbcursor.image = (char *)zeros; 661 fbcursor.mask = (char *)zeros; 662 fbcursor.size.x = 32; 663 fbcursor.size.y = 1; 664 fbcursor.set = FB_CUR_SETALL; 665 ioctl(psdp->fd, FBIOSCURSOR, &fbcursor); 666} 667 668/* Set HW cursor colormap. */ 669void 670xf86SbusSetOsHwCursorCmap(sbusDevicePtr psdp, int bg, int fg) 671{ 672 struct fbcursor fbcursor; 673 unsigned char red[2], green[2], blue[2]; 674 675 memset(&fbcursor, 0, sizeof(fbcursor)); 676 red[0] = bg >> 16; 677 green[0] = bg >> 8; 678 blue[0] = bg; 679 red[1] = fg >> 16; 680 green[1] = fg >> 8; 681 blue[1] = fg; 682 fbcursor.cmap.count = 2; 683 fbcursor.cmap.red = red; 684 fbcursor.cmap.green = green; 685 fbcursor.cmap.blue = blue; 686 fbcursor.set = FB_CUR_SETCMAP; 687 ioctl(psdp->fd, FBIOSCURSOR, &fbcursor); 688} 689