1
2/*
3 * Copyright 1999 by The XFree86 Project, Inc.
4 */
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif
9
10#include <xorg-server.h>
11#include <X11/X.h>
12#include "xf86.h"
13#include "xf86Xinput.h"
14#include "mouse.h"
15#include "xf86_OSlib.h"
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <unistd.h>
19
20static int
21SupportedInterfaces(void)
22{
23    return MSE_SERIAL | MSE_BUS | MSE_PS2 | MSE_XPS2 | MSE_AUTO;
24}
25
26static const char *
27DefaultProtocol(void)
28{
29    return "Auto";
30}
31
32#define DEFAULT_MOUSE_DEV               "/dev/input/mice"
33#define DEFAULT_PS2_DEV                 "/dev/psaux"
34#define DEFAULT_GPM_DATA_DEV            "/dev/gpmdata"
35#define DEFAULT_GPM_CTL_DEV             "/dev/gpmdata"
36
37static const char *mouseDevs[] = {
38        DEFAULT_MOUSE_DEV,
39        DEFAULT_PS2_DEV,
40        DEFAULT_GPM_DATA_DEV,
41        NULL
42};
43
44typedef enum {
45        MOUSE_PROTO_UNKNOWN = 0,
46        MOUSE_PROTO_SERIAL,
47        MOUSE_PROTO_PS2,
48        MOUSE_PROTO_MSC,
49        MOUSE_PROTO_GPM,
50        MOUSE_PROTO_EXPPS2,
51} protocolTypes;
52
53static struct {
54        protocolTypes proto;
55        const char *name;
56} devproto[] = {
57        { MOUSE_PROTO_UNKNOWN,  NULL },
58        { MOUSE_PROTO_PS2,      "PS/2" },
59        { MOUSE_PROTO_MSC,      "MouseSystems" },
60        { MOUSE_PROTO_GPM,      "GPM" },
61        { MOUSE_PROTO_EXPPS2,   "ExplorerPS/2" },
62};
63
64static const char *
65FindDevice(InputInfoPtr pInfo, const char *protocol, int flags)
66{
67    int fd = -1;
68    const char **pdev;
69
70    for (pdev = mouseDevs; *pdev; pdev++) {
71        SYSCALL (fd = open(*pdev, O_RDWR | O_NONBLOCK | O_EXCL));
72        if (fd == -1) {
73#ifdef DEBUG
74            ErrorF("Cannot open %s (%s)\n", *pdev, strerror(errno));
75#endif
76        } else
77            break;
78    }
79
80    if (*pdev) {
81        close(fd);
82        /* Set the Device option. */
83        pInfo->options =
84            xf86AddNewOption(pInfo->options, "Device", *pdev);
85        xf86Msg(X_INFO, "%s: Setting Device option to \"%s\"\n",
86                pInfo->name, *pdev);
87    }
88
89    return *pdev;
90}
91
92static const char *
93lnxMouseMagic(InputInfoPtr pInfo)
94{
95    int fd = -1;
96    const char *dev;
97    char *realdev;
98    struct stat sbuf;
99    int i;
100    int proto = MOUSE_PROTO_UNKNOWN;
101
102    dev = xf86SetStrOption(pInfo->options, "Device", NULL);
103    if (!dev) {
104#ifdef DEBUG
105        ErrorF("xf86SetStrOption failed to return the device name\n");
106#endif
107        return NULL;
108    }
109    /* Look at the device name to guess the protocol. */
110    realdev = NULL;
111    if (strcmp(dev, DEFAULT_MOUSE_DEV) == 0) {
112        if (lstat(dev, &sbuf) != 0) {
113#ifdef DEBUG
114            ErrorF("lstat failed for %s (%s)\n", dev, strerror(errno));
115#endif
116            return NULL;
117        }
118        if (S_ISLNK(sbuf.st_mode)) {
119            realdev = xnfalloc(PATH_MAX + 1);
120            i = readlink(dev, realdev, PATH_MAX);
121            if (i <= 0) {
122#ifdef DEBUG
123                ErrorF("readlink failed for %s (%s)\n", dev, strerror(errno));
124#endif
125                free(realdev);
126                return NULL;
127            }
128            realdev[i] = '\0';
129        }
130    }
131    if (!realdev)
132        realdev = xnfstrdup(dev);
133    else {
134        /* If realdev doesn't contain a '/' then prepend "/dev/" */
135        if (!strchr(realdev, '/')) {
136            char *tmp = xnfalloc(strlen(realdev) + 5 + 1);
137            sprintf(tmp, "/dev/%s", realdev);
138            free(realdev);
139            realdev = tmp;
140        }
141    }
142
143    if (strcmp(realdev, DEFAULT_MOUSE_DEV) == 0)
144        proto = MOUSE_PROTO_EXPPS2;
145    else if (strcmp(realdev, DEFAULT_PS2_DEV) == 0)
146        proto = MOUSE_PROTO_EXPPS2;
147    else if (strcmp(realdev, DEFAULT_GPM_DATA_DEV) == 0)
148        proto = MOUSE_PROTO_MSC;
149    else if (strcmp(realdev, DEFAULT_GPM_CTL_DEV) == 0)
150        proto = MOUSE_PROTO_GPM;
151    free(realdev);
152    /*
153     * If the protocol can't be guessed from the device name,
154     * try to characterise it.
155     */
156    if (proto == MOUSE_PROTO_UNKNOWN) {
157        SYSCALL (fd = open(dev, O_RDWR | O_NONBLOCK | O_EXCL));
158        if (isatty(fd)) {
159            /* Serial PnP has already failed, so give up. */
160        } else {
161            if (fstat(fd, &sbuf) != 0) {
162#ifdef DEBUG
163                ErrorF("fstat failed for %s (%s)\n", dev, strerror(errno));
164#endif
165                close(fd);
166                return NULL;
167            }
168            if (S_ISFIFO(sbuf.st_mode)) {
169                /* Assume GPM data in MSC format. */
170                proto = MOUSE_PROTO_MSC;
171            } else {
172                /* Default to PS/2 */
173                proto = MOUSE_PROTO_PS2;
174            }
175        }
176        close(fd);
177    }
178    if (proto == MOUSE_PROTO_UNKNOWN) {
179        xf86Msg(X_ERROR, "%s: Cannot find mouse protocol.\n",
180                pInfo->name);
181        return NULL;
182    } else {
183        for (i = 0; i < sizeof(devproto)/sizeof(devproto[0]); i++) {
184            if (devproto[i].proto == proto) {
185                xf86Msg(X_INFO,
186                        "%s: Setting mouse protocol to \"%s\"\n",
187                        pInfo->name, devproto[i].name);
188                return devproto[i].name;
189            }
190        }
191    }
192    return NULL;
193}
194
195static const char *
196GuessProtocol(InputInfoPtr pInfo, int flags)
197{
198    return lnxMouseMagic(pInfo);
199}
200
201static const char *
202SetupAuto(InputInfoPtr pInfo, int *protoPara)
203{
204    return lnxMouseMagic(pInfo);
205}
206
207OSMouseInfoPtr
208OSMouseInit(int flags)
209{
210    OSMouseInfoPtr p;
211
212    p = calloc(sizeof(OSMouseInfoRec), 1);
213    if (!p)
214        return NULL;
215    p->SupportedInterfaces = SupportedInterfaces;
216    p->DefaultProtocol = DefaultProtocol;
217    p->FindDevice = FindDevice;
218    p->GuessProtocol = GuessProtocol;
219    p->SetupAuto = SetupAuto;
220    return p;
221}
222
223