Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / kdrive / linux / linux.c
CommitLineData
a09e091a
JB
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
37extern KdPointerDriver LinuxMouseDriver;
38extern KdPointerDriver Ps2MouseDriver;
39extern KdPointerDriver MsMouseDriver;
40#endif
41#ifdef TSLIB
42extern KdPointerDriver TsDriver;
43#endif
44#ifdef KDRIVE_EVDEV
45extern KdPointerDriver LinuxEvdevMouseDriver;
46extern KdKeyboardDriver LinuxEvdevKeyboardDriver;
47#endif
48#ifdef KDRIVE_KBD
49extern KdKeyboardDriver LinuxKeyboardDriver;
50#endif
51
52static int vtno;
53int LinuxConsoleFd;
54int LinuxApmFd = -1;
55static int activeVT;
56static Bool enabled;
57
58static void
59LinuxVTRequest(int sig)
60{
61 kdSwitchPending = TRUE;
62}
63
64/* Check before chowning -- this avoids touching the file system */
65static void
66LinuxCheckChown(const 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
80static int
81LinuxInit(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 FatalError("LinuxInit: Server must be suid root\n");
91 }
92
93 if (kdVirtualTerminal >= 0)
94 vtno = kdVirtualTerminal;
95 else {
96 if ((fd = open("/dev/tty0", O_WRONLY, 0)) < 0) {
97 FatalError("LinuxInit: Cannot open /dev/tty0 (%s)\n",
98 strerror(errno));
99 }
100 if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) || (vtno == -1)) {
101 FatalError("xf86OpenConsole: Cannot find a free VT\n");
102 }
103 close(fd);
104 }
105
106 snprintf(vtname, sizeof(vtname), "/dev/tty%d", vtno); /* /dev/tty1-64 */
107
108 if ((LinuxConsoleFd = open(vtname, O_RDWR | O_NDELAY, 0)) < 0) {
109 FatalError("LinuxInit: Cannot open %s (%s)\n", vtname, strerror(errno));
110 }
111
112 /* change ownership of the vt */
113 LinuxCheckChown(vtname);
114
115 /*
116 * the current VT device we're running on is not "console", we want
117 * to grab all consoles too
118 *
119 * Why is this needed?
120 */
121 LinuxCheckChown("/dev/tty0");
122 /*
123 * Linux doesn't switch to an active vt after the last close of a vt,
124 * so we do this ourselves by remembering which is active now.
125 */
126 memset(&vts, '\0', sizeof(vts)); /* valgrind */
127 if (ioctl(LinuxConsoleFd, VT_GETSTATE, &vts) == 0) {
128 activeVT = vts.v_active;
129 }
130
131 return 1;
132}
133
134static void
135LinuxSetSwitchMode(int mode)
136{
137 struct sigaction act;
138 struct vt_mode VT;
139
140 if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) < 0) {
141 FatalError("LinuxInit: VT_GETMODE failed\n");
142 }
143
144 if (mode == VT_PROCESS) {
145 act.sa_handler = LinuxVTRequest;
146 sigemptyset(&act.sa_mask);
147 act.sa_flags = 0;
148 sigaction(SIGUSR1, &act, 0);
149
150 VT.mode = mode;
151 VT.relsig = SIGUSR1;
152 VT.acqsig = SIGUSR1;
153 }
154 else {
155 act.sa_handler = SIG_IGN;
156 sigemptyset(&act.sa_mask);
157 act.sa_flags = 0;
158 sigaction(SIGUSR1, &act, 0);
159
160 VT.mode = mode;
161 VT.relsig = 0;
162 VT.acqsig = 0;
163 }
164 if (ioctl(LinuxConsoleFd, VT_SETMODE, &VT) < 0) {
165 FatalError("LinuxInit: VT_SETMODE failed\n");
166 }
167}
168
169static void
170LinuxApmBlock(pointer blockData, OSTimePtr pTimeout, pointer pReadmask)
171{
172}
173
174static Bool LinuxApmRunning;
175
176static void
177LinuxApmWakeup(pointer blockData, int result, pointer pReadmask)
178{
179 fd_set *readmask = (fd_set *) pReadmask;
180
181 if (result > 0 && LinuxApmFd >= 0 && FD_ISSET(LinuxApmFd, readmask)) {
182 apm_event_t event;
183 Bool running = LinuxApmRunning;
184 int cmd = APM_IOC_SUSPEND;
185
186 while (read(LinuxApmFd, &event, sizeof(event)) == sizeof(event)) {
187 switch (event) {
188 case APM_SYS_STANDBY:
189 case APM_USER_STANDBY:
190 running = FALSE;
191 cmd = APM_IOC_STANDBY;
192 break;
193 case APM_SYS_SUSPEND:
194 case APM_USER_SUSPEND:
195 case APM_CRITICAL_SUSPEND:
196 running = FALSE;
197 cmd = APM_IOC_SUSPEND;
198 break;
199 case APM_NORMAL_RESUME:
200 case APM_CRITICAL_RESUME:
201 case APM_STANDBY_RESUME:
202 running = TRUE;
203 break;
204 }
205 }
206 if (running && !LinuxApmRunning) {
207 KdResume();
208 LinuxApmRunning = TRUE;
209 }
210 else if (!running && LinuxApmRunning) {
211 KdSuspend();
212 LinuxApmRunning = FALSE;
213 ioctl(LinuxApmFd, cmd, 0);
214 }
215 }
216}
217
218#ifdef FNONBLOCK
219#define NOBLOCK FNONBLOCK
220#else
221#define NOBLOCK FNDELAY
222#endif
223
224static void
225LinuxEnable(void)
226{
227 if (enabled)
228 return;
229 if (kdSwitchPending) {
230 kdSwitchPending = FALSE;
231 ioctl(LinuxConsoleFd, VT_RELDISP, VT_ACKACQ);
232 }
233 /*
234 * Open the APM driver
235 */
236 LinuxApmFd = open("/dev/apm_bios", 2);
237 if (LinuxApmFd < 0 && errno == ENOENT)
238 LinuxApmFd = open("/dev/misc/apm_bios", 2);
239 if (LinuxApmFd >= 0) {
240 LinuxApmRunning = TRUE;
241 fcntl(LinuxApmFd, F_SETFL, fcntl(LinuxApmFd, F_GETFL) | NOBLOCK);
242 RegisterBlockAndWakeupHandlers(LinuxApmBlock, LinuxApmWakeup, 0);
243 AddEnabledDevice(LinuxApmFd);
244 }
245
246 /*
247 * now get the VT
248 */
249 LinuxSetSwitchMode(VT_AUTO);
250 if (ioctl(LinuxConsoleFd, VT_ACTIVATE, vtno) != 0) {
251 FatalError("LinuxInit: VT_ACTIVATE failed\n");
252 }
253 if (ioctl(LinuxConsoleFd, VT_WAITACTIVE, vtno) != 0) {
254 FatalError("LinuxInit: VT_WAITACTIVE failed\n");
255 }
256 LinuxSetSwitchMode(VT_PROCESS);
257 if (ioctl(LinuxConsoleFd, KDSETMODE, KD_GRAPHICS) < 0) {
258 FatalError("LinuxInit: KDSETMODE KD_GRAPHICS failed\n");
259 }
260 enabled = TRUE;
261}
262
263static void
264LinuxDisable(void)
265{
266 ioctl(LinuxConsoleFd, KDSETMODE, KD_TEXT); /* Back to text mode ... */
267 if (kdSwitchPending) {
268 kdSwitchPending = FALSE;
269 ioctl(LinuxConsoleFd, VT_RELDISP, 1);
270 }
271 enabled = FALSE;
272 if (LinuxApmFd >= 0) {
273 RemoveBlockAndWakeupHandlers(LinuxApmBlock, LinuxApmWakeup, 0);
274 RemoveEnabledDevice(LinuxApmFd);
275 close(LinuxApmFd);
276 LinuxApmFd = -1;
277 }
278}
279
280static void
281LinuxFini(void)
282{
283 struct vt_mode VT;
284 struct vt_stat vts;
285 int fd;
286
287 if (LinuxConsoleFd < 0)
288 return;
289
290 if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) != -1) {
291 VT.mode = VT_AUTO;
292 ioctl(LinuxConsoleFd, VT_SETMODE, &VT); /* set dflt vt handling */
293 }
294 memset(&vts, '\0', sizeof(vts)); /* valgrind */
295 ioctl(LinuxConsoleFd, VT_GETSTATE, &vts);
296 if (vtno == vts.v_active) {
297 /*
298 * Find a legal VT to switch to, either the one we started from
299 * or the lowest active one that isn't ours
300 */
301 if (activeVT < 0 ||
302 activeVT == vts.v_active || !(vts.v_state & (1 << activeVT))) {
303 for (activeVT = 1; activeVT < 16; activeVT++)
304 if (activeVT != vtno && (vts.v_state & (1 << activeVT)))
305 break;
306 if (activeVT == 16)
307 activeVT = -1;
308 }
309 /*
310 * Perform a switch back to the active VT when we were started
311 */
312 if (activeVT >= -1) {
313 ioctl(LinuxConsoleFd, VT_ACTIVATE, activeVT);
314 ioctl(LinuxConsoleFd, VT_WAITACTIVE, activeVT);
315 activeVT = -1;
316 }
317 }
318 close(LinuxConsoleFd); /* make the vt-manager happy */
319 LinuxConsoleFd = -1;
320 fd = open("/dev/tty0", O_RDWR | O_NDELAY, 0);
321 if (fd >= 0) {
322 memset(&vts, '\0', sizeof(vts)); /* valgrind */
323 ioctl(fd, VT_GETSTATE, &vts);
324 if (ioctl(fd, VT_DISALLOCATE, vtno) < 0)
325 fprintf(stderr, "Can't deallocate console %d %s\n", vtno,
326 strerror(errno));
327 close(fd);
328 }
329 return;
330}
331
332void
333KdOsAddInputDrivers(void)
334{
335#ifdef KDRIVE_MOUSE
336 KdAddPointerDriver(&LinuxMouseDriver);
337 KdAddPointerDriver(&MsMouseDriver);
338 KdAddPointerDriver(&Ps2MouseDriver);
339#endif
340#ifdef TSLIB
341 KdAddPointerDriver(&TsDriver);
342#endif
343#ifdef KDRIVE_EVDEV
344 KdAddPointerDriver(&LinuxEvdevMouseDriver);
345 KdAddKeyboardDriver(&LinuxEvdevKeyboardDriver);
346#endif
347#ifdef KDRIVE_KBD
348 KdAddKeyboardDriver(&LinuxKeyboardDriver);
349#endif
350}
351
352static void
353LinuxBell(int volume, int pitch, int duration)
354{
355 if (volume && pitch)
356 ioctl(LinuxConsoleFd, KDMKTONE, ((1193190 / pitch) & 0xffff) |
357 (((unsigned long) duration * volume / 50) << 16));
358}
359
360KdOsFuncs LinuxFuncs = {
361 .Init = LinuxInit,
362 .Enable = LinuxEnable,
363 .Disable = LinuxDisable,
364 .Fini = LinuxFini,
365 .Bell = LinuxBell,
366};
367
368void
369OsVendorInit(void)
370{
371 KdOsInit(&LinuxFuncs);
372}