Imported Upstream version 1.15.1
[deb_xorg-server.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(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
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 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
134 static void
135 LinuxSetSwitchMode(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
169 static void
170 LinuxApmBlock(pointer blockData, OSTimePtr pTimeout, pointer pReadmask)
171 {
172 }
173
174 static Bool LinuxApmRunning;
175
176 static void
177 LinuxApmWakeup(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
224 static void
225 LinuxEnable(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
263 static void
264 LinuxDisable(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
280 static void
281 LinuxFini(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
332 void
333 KdOsAddInputDrivers(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
352 static void
353 LinuxBell(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
360 KdOsFuncs LinuxFuncs = {
361 .Init = LinuxInit,
362 .Enable = LinuxEnable,
363 .Disable = LinuxDisable,
364 .Fini = LinuxFini,
365 .Bell = LinuxBell,
366 };
367
368 void
369 OsVendorInit(void)
370 {
371 KdOsInit(&LinuxFuncs);
372 }