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