Imported Upstream version 1.11.4
[ubuntu-omap:xserver.git] / hw / kdrive / linux / linux.c
1 /*
2  * Copyright © 1999 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Keith Packard not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  Keith Packard makes no
11  * representations about the suitability of this software for any purpose.  It
12  * is provided "as is" without express or implied warranty.
13  *
14  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <kdrive-config.h>
25 #endif
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 <sys/ioctl.h>
33 #include <X11/keysym.h>
34 #include <linux/apm_bios.h>
35
36 #ifdef KDRIVE_MOUSE
37 extern KdPointerDriver  LinuxMouseDriver;
38 extern KdPointerDriver  Ps2MouseDriver;
39 extern KdPointerDriver  MsMouseDriver;
40 #endif
41 #ifdef TSLIB
42 extern KdPointerDriver  TsDriver;
43 #endif
44 #ifdef KDRIVE_EVDEV
45 extern KdPointerDriver  LinuxEvdevMouseDriver;
46 extern KdKeyboardDriver LinuxEvdevKeyboardDriver;
47 #endif
48 #ifdef KDRIVE_KBD
49 extern KdKeyboardDriver LinuxKeyboardDriver;
50 #endif
51
52 static int  vtno;
53 int  LinuxConsoleFd;
54 int  LinuxApmFd = -1;
55 static int  activeVT;
56 static Bool enabled;
57
58 static void
59 LinuxVTRequest (int sig)
60 {
61     kdSwitchPending = TRUE;
62 }
63
64 /* Check before chowning -- this avoids touching the file system */
65 static void
66 LinuxCheckChown (char *file)
67 {
68     struct stat     st;
69     __uid_t         u;
70     __gid_t         g;
71
72     if (stat (file, &st) < 0)
73         return;
74     u = getuid ();
75     g = getgid ();
76     if (st.st_uid != u || st.st_gid != g)
77         chown (file, u, g);
78 }
79
80 static int
81 LinuxInit (void)
82 {
83     int fd = -1;
84     char vtname[11];
85     struct vt_stat vts;
86
87     LinuxConsoleFd = -1;
88     /* check if we're run with euid==0 */
89     if (geteuid() != 0)
90     {
91         FatalError("LinuxInit: Server must be suid root\n");
92     }
93
94     if (kdVirtualTerminal >= 0)
95         vtno = kdVirtualTerminal;
96     else
97     {
98         if ((fd = open("/dev/tty0",O_WRONLY,0)) < 0)
99         {
100             FatalError(
101                        "LinuxInit: Cannot open /dev/tty0 (%s)\n",
102                        strerror(errno));
103         }
104         if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) ||
105             (vtno == -1))
106         {
107             FatalError("xf86OpenConsole: Cannot find a free VT\n");
108         }
109         close(fd);
110     }
111
112     sprintf(vtname,"/dev/tty%d",vtno); /* /dev/tty1-64 */
113
114     if ((LinuxConsoleFd = open(vtname, O_RDWR|O_NDELAY, 0)) < 0)
115     {
116         FatalError("LinuxInit: Cannot open %s (%s)\n",
117                    vtname, strerror(errno));
118     }
119
120     /* change ownership of the vt */
121     LinuxCheckChown (vtname);
122
123     /*
124      * the current VT device we're running on is not "console", we want
125      * to grab all consoles too
126      *
127      * Why is this needed?
128      */
129     LinuxCheckChown ("/dev/tty0");
130     /*
131      * Linux doesn't switch to an active vt after the last close of a vt,
132      * so we do this ourselves by remembering which is active now.
133      */
134     memset (&vts, '\0', sizeof (vts));  /* valgrind */
135     if (ioctl(LinuxConsoleFd, VT_GETSTATE, &vts) == 0)
136     {
137         activeVT = vts.v_active;
138     }
139
140     return 1;
141 }
142
143 static void
144 LinuxSetSwitchMode (int mode)
145 {
146     struct sigaction    act;
147     struct vt_mode      VT;
148
149     if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) < 0)
150     {
151         FatalError ("LinuxInit: VT_GETMODE failed\n");
152     }
153
154     if (mode == VT_PROCESS)
155     {
156         act.sa_handler = LinuxVTRequest;
157         sigemptyset (&act.sa_mask);
158         act.sa_flags = 0;
159         sigaction (SIGUSR1, &act, 0);
160
161         VT.mode = mode;
162         VT.relsig = SIGUSR1;
163         VT.acqsig = SIGUSR1;
164     }
165     else
166     {
167         act.sa_handler = SIG_IGN;
168         sigemptyset (&act.sa_mask);
169         act.sa_flags = 0;
170         sigaction (SIGUSR1, &act, 0);
171
172         VT.mode = mode;
173         VT.relsig = 0;
174         VT.acqsig = 0;
175     }
176     if (ioctl(LinuxConsoleFd, VT_SETMODE, &VT) < 0)
177     {
178         FatalError("LinuxInit: VT_SETMODE failed\n");
179     }
180 }
181
182 static void
183 LinuxApmBlock (pointer blockData, OSTimePtr pTimeout, pointer pReadmask)
184 {
185 }
186
187 static Bool LinuxApmRunning;
188
189 static void
190 LinuxApmWakeup (pointer blockData, int result, pointer pReadmask)
191 {
192     fd_set  *readmask = (fd_set *) pReadmask;
193
194     if (result > 0 && LinuxApmFd >= 0 && FD_ISSET (LinuxApmFd, readmask))
195     {
196         apm_event_t event;
197         Bool        running = LinuxApmRunning;
198         int         cmd = APM_IOC_SUSPEND;
199
200         while (read (LinuxApmFd, &event, sizeof (event)) == sizeof (event))
201         {
202             switch (event) {
203             case APM_SYS_STANDBY:
204             case APM_USER_STANDBY:
205                 running = FALSE;
206                 cmd = APM_IOC_STANDBY;
207                 break;
208             case APM_SYS_SUSPEND:
209             case APM_USER_SUSPEND:
210             case APM_CRITICAL_SUSPEND:
211                 running = FALSE;
212                 cmd = APM_IOC_SUSPEND;
213                 break;
214             case APM_NORMAL_RESUME:
215             case APM_CRITICAL_RESUME:
216             case APM_STANDBY_RESUME:
217                 running = TRUE;
218                 break;
219             }
220         }
221         if (running && !LinuxApmRunning)
222         {
223             KdResume ();
224             LinuxApmRunning = TRUE;
225         }
226         else if (!running && LinuxApmRunning)
227         {
228             KdSuspend ();
229             LinuxApmRunning = FALSE;
230             ioctl (LinuxApmFd, cmd, 0);
231         }
232     }
233 }
234
235 #ifdef FNONBLOCK
236 #define NOBLOCK FNONBLOCK
237 #else
238 #define NOBLOCK FNDELAY
239 #endif
240
241 static void
242 LinuxEnable (void)
243 {
244     if (enabled)
245         return;
246     if (kdSwitchPending)
247     {
248         kdSwitchPending = FALSE;
249         ioctl (LinuxConsoleFd, VT_RELDISP, VT_ACKACQ);
250     }
251     /*
252      * Open the APM driver
253      */
254     LinuxApmFd = open ("/dev/apm_bios", 2);
255     if (LinuxApmFd < 0 && errno == ENOENT)
256         LinuxApmFd = open ("/dev/misc/apm_bios", 2);
257     if (LinuxApmFd >= 0)
258     {
259         LinuxApmRunning = TRUE;
260         fcntl (LinuxApmFd, F_SETFL, fcntl (LinuxApmFd, F_GETFL) | NOBLOCK);
261         RegisterBlockAndWakeupHandlers (LinuxApmBlock, LinuxApmWakeup, 0);
262         AddEnabledDevice (LinuxApmFd);
263     }
264
265     /*
266      * now get the VT
267      */
268     LinuxSetSwitchMode (VT_AUTO);
269     if (ioctl(LinuxConsoleFd, VT_ACTIVATE, vtno) != 0)
270     {
271         FatalError("LinuxInit: VT_ACTIVATE failed\n");
272     }
273     if (ioctl(LinuxConsoleFd, VT_WAITACTIVE, vtno) != 0)
274     {
275         FatalError("LinuxInit: VT_WAITACTIVE failed\n");
276     }
277     LinuxSetSwitchMode (VT_PROCESS);
278     if (ioctl(LinuxConsoleFd, KDSETMODE, KD_GRAPHICS) < 0)
279     {
280         FatalError("LinuxInit: KDSETMODE KD_GRAPHICS failed\n");
281     }
282     enabled = TRUE;
283 }
284
285 static void
286 LinuxDisable (void)
287 {
288     ioctl(LinuxConsoleFd, KDSETMODE, KD_TEXT);  /* Back to text mode ... */
289     if (kdSwitchPending)
290     {
291         kdSwitchPending = FALSE;
292         ioctl (LinuxConsoleFd, VT_RELDISP, 1);
293     }
294     enabled = FALSE;
295     if (LinuxApmFd >= 0)
296     {
297         RemoveBlockAndWakeupHandlers (LinuxApmBlock, LinuxApmWakeup, 0);
298         RemoveEnabledDevice (LinuxApmFd);
299         close (LinuxApmFd);
300         LinuxApmFd = -1;
301     }
302 }
303
304 static void
305 LinuxFini (void)
306 {
307     struct vt_mode   VT;
308     struct vt_stat  vts;
309     int             fd;
310
311     if (LinuxConsoleFd < 0)
312         return;
313
314     if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) != -1)
315     {
316         VT.mode = VT_AUTO;
317         ioctl(LinuxConsoleFd, VT_SETMODE, &VT); /* set dflt vt handling */
318     }
319     memset (&vts, '\0', sizeof (vts));  /* valgrind */
320     ioctl (LinuxConsoleFd, VT_GETSTATE, &vts);
321     if (vtno == vts.v_active)
322     {
323         /*
324          * Find a legal VT to switch to, either the one we started from
325          * or the lowest active one that isn't ours
326          */
327         if (activeVT < 0 ||
328             activeVT == vts.v_active ||
329             !(vts.v_state & (1 << activeVT)))
330         {
331             for (activeVT = 1; activeVT < 16; activeVT++)
332                 if (activeVT != vtno && (vts.v_state & (1 << activeVT)))
333                     break;
334             if (activeVT == 16)
335                 activeVT = -1;
336         }
337         /*
338          * Perform a switch back to the active VT when we were started
339          */
340         if (activeVT >= -1)
341         {
342             ioctl (LinuxConsoleFd, VT_ACTIVATE, activeVT);
343             ioctl (LinuxConsoleFd, VT_WAITACTIVE, activeVT);
344             activeVT = -1;
345         }
346     }
347     close(LinuxConsoleFd);                /* make the vt-manager happy */
348     LinuxConsoleFd = -1;
349     fd = open ("/dev/tty0", O_RDWR|O_NDELAY, 0);
350     if (fd >= 0)
351     {
352         memset (&vts, '\0', sizeof (vts));      /* valgrind */
353         ioctl (fd, VT_GETSTATE, &vts);
354         if (ioctl (fd, VT_DISALLOCATE, vtno) < 0)
355             fprintf (stderr, "Can't deallocate console %d %s\n", vtno, strerror(errno));
356         close (fd);
357     }
358     return;
359 }
360
361 void
362 KdOsAddInputDrivers (void)
363 {
364 #ifdef KDRIVE_MOUSE
365     KdAddPointerDriver(&LinuxMouseDriver);
366     KdAddPointerDriver(&MsMouseDriver);
367     KdAddPointerDriver(&Ps2MouseDriver);
368 #endif
369 #ifdef TSLIB
370     KdAddPointerDriver(&TsDriver);
371 #endif
372 #ifdef KDRIVE_EVDEV
373     KdAddPointerDriver(&LinuxEvdevMouseDriver);
374     KdAddKeyboardDriver(&LinuxEvdevKeyboardDriver);
375 #endif
376 #ifdef KDRIVE_KBD
377     KdAddKeyboardDriver(&LinuxKeyboardDriver);
378 #endif
379 }
380
381 static void
382 LinuxBell(int volume, int pitch, int duration)
383 {
384     if (volume && pitch)
385         ioctl(LinuxConsoleFd, KDMKTONE, ((1193190 / pitch) & 0xffff) |
386               (((unsigned long)duration * volume / 50) << 16));
387 }
388
389 KdOsFuncs   LinuxFuncs = {
390     .Init = LinuxInit,
391     .Enable = LinuxEnable,
392     .Disable = LinuxDisable,
393     .Fini = LinuxFini,
394     .Bell = LinuxBell,
395 };
396
397 void
398 OsVendorInit (void)
399 {
400     KdOsInit (&LinuxFuncs);
401 }