3516. Jumbo Tiny-X patch with Itsy support (#3527, Keith Packard).
[gstreamer-omap:xserver.git] / hw / kdrive / linux.c
1 /*
2  * $Id$
3  *
4  * Copyright © 1999 Keith Packard
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24 /* $XFree86: $ */
25
26 #include "kdrive.h"
27 #include <errno.h>
28 #include <signal.h>
29 #include <linux/vt.h>
30 #include <linux/kd.h>
31 #include <sys/stat.h>
32 #include <keysym.h>
33
34 static int  vtno;
35 int  LinuxConsoleFd;
36 static int  activeVT;
37 static Bool enabled;
38
39 void
40 LinuxVTRequest (int sig)
41 {
42     kdSwitchPending = TRUE;
43 }
44
45 /* Check before chowning -- this avoids touching the file system */
46 void
47 LinuxCheckChown (char *file)
48 {
49     struct stat     st;
50     __uid_t         u;
51     __gid_t         g;
52
53     if (stat (file, &st) < 0)
54         return;
55     u = getuid ();
56     g = getgid ();
57     if (st.st_uid != u || st.st_gid != g)
58         chown (file, u, g);
59 }
60
61 int
62 LinuxInit ()
63 {
64     int i, fd;
65     char vtname[11];
66     struct vt_stat vts;
67     struct stat statb;
68
69     LinuxConsoleFd = -1;
70     /* check if we're run with euid==0 */
71     if (geteuid() != 0)
72     {
73         FatalError("LinuxInit: Server must be suid root\n");
74     }
75
76     if ((fd = open("/dev/tty0",O_WRONLY,0)) < 0) 
77     {
78         FatalError(
79             "LinuxInit: Cannot open /dev/tty0 (%s)\n",
80             strerror(errno));
81     }
82     if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) ||
83         (vtno == -1))
84     {
85         FatalError("xf86OpenConsole: Cannot find a free VT\n");
86     }
87     close(fd);
88
89 /*    ErrorF("(using VT number %d)\n\n", vtno); */
90
91     sprintf(vtname,"/dev/tty%d",vtno); /* /dev/tty1-64 */
92
93     if ((LinuxConsoleFd = open(vtname, O_RDWR|O_NDELAY, 0)) < 0)
94     {
95         FatalError("LinuxInit: Cannot open %s (%s)\n",
96                    vtname, strerror(errno));
97     }
98
99     /* change ownership of the vt */
100     LinuxCheckChown (vtname);
101
102     /*
103      * the current VT device we're running on is not "console", we want
104      * to grab all consoles too
105      *
106      * Why is this needed?
107      */
108     LinuxCheckChown ("/dev/tty0");
109     /*
110      * Linux doesn't switch to an active vt after the last close of a vt,
111      * so we do this ourselves by remembering which is active now.
112      */
113     if (ioctl(LinuxConsoleFd, VT_GETSTATE, &vts) == 0)
114     {
115         activeVT = vts.v_active;
116     }
117
118     return 1;
119 }
120
121 Bool
122 LinuxFindPci (CARD16 vendor, CARD16 device, CARD32 count, KdCardAttr *attr)
123 {
124     FILE    *f;
125     char    line[2048], *l, *end;
126     CARD32  bus, id, mode, addr;
127     int     n;
128     CARD32  ven_dev;
129     Bool    ret = FALSE;
130
131     ven_dev = (((CARD32) vendor) << 16) | ((CARD32) device);
132     f = fopen ("/proc/bus/pci/devices", "r");
133     if (!f)
134         return FALSE;
135     while (fgets (line, sizeof (line)-1, f))
136     {
137         line[sizeof(line)-1] = '\0';
138         l = line;
139         bus = strtoul (l, &end, 16);
140         if (end == l)
141             continue;
142         l = end;
143         id = strtoul (l, &end, 16);
144         if (end == l)
145             continue;
146         l = end;
147         if (id != ven_dev)
148             continue;
149         if (count--)
150             continue;
151         (void) strtoul (l, &end, 16);
152         if (end == l)
153             continue;
154         l = end;
155         n = 0;
156         for (;;)
157         {
158             addr = strtoul (l, &end, 16);
159             if (end == l)
160                 break;
161             if (addr & 1)
162                 attr->io = addr & ~0xf;
163             else
164             {
165                 if (n == KD_MAX_CARD_ADDRESS)
166                     break;
167                 attr->address[n++] = addr & ~0xf;
168             }
169             l = end;
170         }
171         while (n > 0)
172         {
173             if (attr->address[n-1] != 0)
174                 break;
175             n--;
176         }
177         attr->naddr = n;
178         ret = TRUE;
179         break;
180     }
181     fclose (f);
182     return ret;
183 }
184
185 void
186 LinuxEnable (void)
187 {
188     struct sigaction    act;
189     struct vt_mode      VT;
190     
191     if (enabled)
192         return;
193     if (kdSwitchPending)
194     {
195         kdSwitchPending = FALSE;
196         ioctl (LinuxConsoleFd, VT_RELDISP, VT_ACKACQ);
197     }
198     /*
199      * now get the VT
200      */
201     if (ioctl(LinuxConsoleFd, VT_ACTIVATE, vtno) != 0)
202     {
203         ErrorF("LinuxInit: VT_ACTIVATE failed\n");
204     }
205     if (ioctl(LinuxConsoleFd, VT_WAITACTIVE, vtno) != 0)
206     {
207         ErrorF("LinuxInit: VT_WAITACTIVE failed\n");
208     }
209     if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) < 0) 
210     {
211         FatalError ("LinuxInit: VT_GETMODE failed\n");
212     }
213
214     act.sa_handler = LinuxVTRequest;
215     sigemptyset (&act.sa_mask);
216     act.sa_flags = 0;
217     act.sa_restorer = 0;
218     sigaction (SIGUSR1, &act, 0);
219
220     VT.mode = VT_PROCESS;
221     VT.relsig = SIGUSR1;
222     VT.acqsig = SIGUSR1;
223     if (ioctl(LinuxConsoleFd, VT_SETMODE, &VT) < 0) 
224     {
225         FatalError("LinuxInit: VT_SETMODE VT_PROCESS failed\n");
226     }
227     if (ioctl(LinuxConsoleFd, KDSETMODE, KD_GRAPHICS) < 0)
228     {
229         FatalError("LinuxInit: KDSETMODE KD_GRAPHICS failed\n");
230     }
231     enabled = TRUE;
232 }
233
234 Bool
235 LinuxSpecialKey (KeySym sym)
236 {
237     struct vt_stat  vts;
238     int             con;
239     
240     if (XK_F1 <= sym && sym <= XK_F12)
241     {
242         con = sym - XK_F1 + 1;
243         ioctl (LinuxConsoleFd, VT_GETSTATE, &vts);
244         if (con != vts.v_active && (vts.v_state & (1 << con)))
245         {
246             ioctl (LinuxConsoleFd, VT_ACTIVATE, con);
247             return TRUE;
248         }
249     }
250     return FALSE;
251 }
252
253 void
254 LinuxDisable (void)
255 {
256     ioctl(LinuxConsoleFd, KDSETMODE, KD_TEXT);  /* Back to text mode ... */
257     if (kdSwitchPending)
258     {
259         kdSwitchPending = FALSE;
260         ioctl (LinuxConsoleFd, VT_RELDISP, 1);
261     }
262     enabled = FALSE;
263 }
264
265 void
266 LinuxFini (void)
267 {
268     struct vt_mode   VT;
269     struct vt_stat  vts;
270     int             fd;
271
272     if (LinuxConsoleFd < 0)
273         return;
274
275     if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) != -1)
276     {
277         VT.mode = VT_AUTO;
278         ioctl(LinuxConsoleFd, VT_SETMODE, &VT); /* set dflt vt handling */
279     }
280     ioctl (LinuxConsoleFd, VT_GETSTATE, &vts);
281     /*
282      * Find a legal VT to switch to, either the one we started from
283      * or the lowest active one that isn't ours
284      */
285     if (activeVT < 0 || 
286         activeVT == vts.v_active || 
287         !(vts.v_state & (1 << activeVT)))
288     {
289         for (activeVT = 1; activeVT < 16; activeVT++)
290             if (activeVT != vtno && (vts.v_state & (1 << activeVT)))
291                 break;
292         if (activeVT == 16)
293             activeVT = -1;
294     }
295     /*
296      * Perform a switch back to the active VT when we were started
297      */
298     if (activeVT >= -1)
299     {
300         ioctl (LinuxConsoleFd, VT_ACTIVATE, activeVT);
301         ioctl (LinuxConsoleFd, VT_WAITACTIVE, activeVT);
302         activeVT = -1;
303     }
304     close(LinuxConsoleFd);                /* make the vt-manager happy */
305     fd = open ("/dev/tty0", O_RDWR|O_NDELAY, 0);
306     if (fd >= 0)
307     {
308         ioctl (fd, VT_GETSTATE, &vts);
309         if (ioctl (fd, VT_DISALLOCATE, vtno) < 0)
310             fprintf (stderr, "Can't deallocate console %d errno %d\n", vtno, errno);
311         close (fd);
312     }
313     return;
314 }
315
316 KdOsFuncs   LinuxFuncs = {
317     LinuxInit,
318     LinuxEnable,
319     LinuxSpecialKey,
320     LinuxDisable,
321     LinuxFini,
322 };