X Server移植指导之二 输入层移植 (XServer Porting Guide)

1.                 Code Analysis of DDX Input Layer

抱歉这篇报告是用英文写的,有兴趣的朋友可以参考。

1.1            A Big Picture of Maemo DDX Input Layer

From the above description , following is the big picture of it:

                                          Figure 3: DDX Porting I n put Layer Big Picture of Maemo

1.2            Start up procedure of Maemo DDX Input Layer

Based on our current understanding, Maemo uses Tiny X instead of standard XFree86 X Server. Tiny X uses an model of Kdrive to finish the DDX work, which is described step by step as following:

Following is a snap code of main function in dix/main.c:

main(int argc, char *argv[], char *envp[])

{

……

    while(1)

    {

              ……

    P rocessCommandLine(argc, argv);

……

              OsInit();

              ……

     InitCoreDevices();

              InitInput(argc, argv);

              InitAndStartDevices() ;

              ……

}

From this snap code, we can see that InitInput function is called from Main function, which is implemented in DDX Layer. For Maemo, it should be InitInput (int argc, char **argv) in Omapinit.c, which is as following:

void

InitInput (int argc, char **argv)

{

    KdKeyboardInfo *ki = NULL;

    ENTER();

    KdAddKeyboardDriver(&LinuxKeyboardDriver);

    KdAddPointerDriver(&TsDriver);

    ki = KdParseKeyboard("keyboard");

    KdAddKeyboard(ki);

    KdInitInput();

    LEAVE();

}

First of all, two drivers are added: LinuxKeyboardDriver is added as keyboard driver, and TsDriver is added as mouse driver. These two drivers are added to Kdrive global variables kdKeyboardDrivers and kdPointerDrivers , which should list all keyboard and pointer drivers in Kdrive model. They are defined as following:

KdKeyboardDriver LinuxKeyboardDriver = {

    "keyboard",

    LinuxKeyboardInit,

    LinuxKeyboardEnable,

    LinuxKeyboardLeds,

    LinuxKeyboardBell,

    LinuxKeyboardDisable,

    LinuxKeyboardFini,

    NULL,

    NULL,

};

KdPointerDriver TsDriver = {

    "tslib",

    TslibInit,

    TslibEnable,

    TslibDisable,

    TslibFini,

    TslibCtrl,

    NULL,

}; //TsDriver has two definitions, I think this one should be the used one.

And then, KdAddKeyboard is invoked to add a concrete device into system. KdParseKeyboard is very simple, which just new a KdKeyboardInfo structure and return it. It is defined as following:

typedef struct _KdKeyboardInfo KdKeyboardInfo;

struct _KdKeyboardInfo {

    struct _KdKeyboardInfo *next;

    DeviceIntPtr        dixdev;

    void                *closure;

    char                *name;

    char                *path;

    int                 inputClass;

#ifdef XKB

    XkbDescPtr          xkb;

#endif

    int                 LockLed;

    CARD8               keyState[KD_KEY_COUNT/8];

    int                 minScanCode;

    int                 maxScanCode;

    CARD8               modmap[MAP_LENGTH];

    KeySymsRec          keySyms;

    int                 leds;

    int                 bellPitch;

    int                 bellDuration;

    InputOption         *options;

    KdKeyboardDriver    *driver;

    void                *driverPrivate;

};

Please notice that keyboard is set to member driverPrivate in this data structure, which is used for finding driver in future.

KdAddKeyboard first invoke AddInputDevice to add a general DIX Input device. This function new a DeviceIntPtr data structure, and please notice that its first member is: DeviceRec . Also please notice that this new added device is put into the list of member off_devices in DIX global variable inputInfo , which list all devices in X Server. They are defined as following:

typedef struct {

    int                                           numDevices;               /* total number of devices */

    DeviceIntPtr               devices;               /* all devices turned on */

    DeviceIntPtr               off_devices;               /* all devices turned off */

    DeviceIntPtr               keyboard;               /* the main one for the server */

    DeviceIntPtr               pointer;

} InputInfo;

 

typedef struct _DeviceIntRec *DeviceIntPtr;

typedef struct _DeviceIntRec {

    DeviceRec               public;

    DeviceIntPtr next;

    TimeStamp               grabTime;

    Bool               startup;                             /* true if needs to be turned on at                 server intialization time */

    DeviceProc               deviceProc;               /* proc(DevicePtr, DEVICE_xx). It is used to initialize, turn on, or turn off the device */

    Bool               inited;                                           /* TRUE if INIT returns Success */

    Bool        coreEvents;             /* TRUE if device also sends core */

    GrabPtr               grab;                                           /* the grabber - used by DIX */

    struct {

              Bool                             frozen;

              int                             state;

              GrabPtr                             other;                             /* if other grab has this frozen */

              xEvent                             *event;                             /* saved to be replayed */

              int                             evcount;

    } sync;

    Atom                             type;

    char                             *name;

    CARD8                             id;

    CARD8                             activatingKey;

    Bool                             fromPassiveGrab;

    GrabRec                             activeGrab;

    void                             (*ActivateGrab) (

                                          DeviceIntPtr /*device*/,

                                          GrabPtr /*grab*/,

                                          TimeStamp /*time*/,

                                          Bool /*autoGrab*/);

    void                             (*DeactivateGrab)(

                                          DeviceIntPtr /*device*/);

    KeyClassPtr                             key;

    ValuatorClassPtr               valuator;

    ButtonClassPtr               button;

    FocusClassPtr               focus;

    ProximityClassPtr               proximity;

    TouchscreenClassPtr touchscreen;

    KbdFeedbackPtr               kbdfeed;

    PtrFeedbackPtr               ptrfeed;

    IntegerFeedbackPtr               intfeed;

    StringFeedbackPtr               stringfeed;

    BellFeedbackPtr               bell;

    LedFeedbackPtr               leds;

#ifdef XKB

    struct _XkbInterest *               xkb_interest;

#endif

    DevUnion                             *devPrivates;

    int                                           nPrivates;

    DeviceUnwrapProc    unwrapProc;

} DeviceIntRec;

 

typedef struct _DeviceRec {

    pointer               devicePrivate;

    ProcessInputProc processInputProc;               /* current */

    ProcessInputProc realInputProc;               /* deliver */

    ProcessInputProc enqueueInputProc;               /* enqueue */

    Bool               on;                                           /* used by DDX to keep state */

} DeviceRec, *DevicePtr;

We should notice that when KdAddKeyboard invoke s AddInputDevice , function KdKeyboardProc is passed as a parameter, which is set to member deviceProc of DeviceIntRec . This is very important since future device status changes are all passed to this function as a call back.

Function RegisterOtherDevice is then invoked by KdAddKeyboard . This function should be invoked since Macro XINPUT should be defined, but I have not gone into this function s code.

At last, function KdAddKeyboard set the new added keyboard device to kdKeyboards , which should be the variable that lists all keyboard devices in Kdrive model.

Function InitAndStartDevices in Main function is then invoked; it first calls ActivateDevice to all devices in inputInfo.off_devices list. This function invokes the callback which is registered when adding new device by passing parameter DEVICE_INIT , which should be KdKeyboardProc in our case.

Let s go into case DEVICE_INIT   in function KdKeyboardProc . It first find the proper driver for this off device, since driverPrivate of this just added device is keyboard , then LinuxKeyboardDriver is found. After driver is found, driverPrivate is set to NULL for future use. Init function of LinuxKeyboardDriver is called, by passing device as the parameter, which is LinuxKeyboardInit in this case. Following is the code:

static Status

LinuxKeyboardInit (KdKeyboardInfo *ki)

{

    if (!ki)

        return !Success;

    if (ki->path)

        xfree(ki->path);

    ki->path = KdSaveString("console");

    if (ki->name)

        xfree(ki->name);

    ki->name = KdSaveString("Linux console keyboard");

    readKernelMapping (ki);

    return Success;

}

Therefore, ki->path now is "console" , and ki->name now is "Linux console keyboard" . I have not gone to details of readKernelMapping , which seemed to equip the map function for keys.

Function KdInitModMap / KdInitAutoRepeats / InitKeyboardDeviceStruct / KdResetInputMachine have not been studied yet. F rom a rough view, InitKeyboardDeviceStruct is used to new KeyClassDevice/ FocusDevice/ KbdFeedbackClassDevice . They should be used in DIX core, yet I do not their concrete meaning since I still have not touch any of them.

After ActivateDevice is called, EnableDevice is then called for all devices in inputInfo.off_devices list. Similar to ActivateDevice , this function invokes the callback which is registered when adding new device by passing parameter DEVICE_ON , which should be KdKeyboardProc in our case.

Let s go into case DEVICE_ ON in function KdKeyboardProc . It just calls the function Enable , which should be LinuxKeyboardEnable in this case. Following is the code:

static Status

LinuxKeyboardEnable (KdKeyboardInfo *ki)

{

    struct termios nTty;

    unsigned char   buf[256];

    int                                 n;

    int             fd;

    if (!ki)

        return !Success;

    fd = LinuxConsoleFd;

    ki->driverPrivate = (void *) fd;

    ioctl (fd, KDGKBMODE, &LinuxKbdTrans);

    tcgetattr (fd, &LinuxTermios);

#ifdef XKB

    if (!noXkbExtension)

        ioctl(fd, KDSKBMODE, K_RAW);

    else

#else

        ioctl(fd, KDSKBMODE, K_MEDIUMRAW);

#endif

    nTty = LinuxTermios;

    nTty.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);

    nTty.c_oflag = 0;

    nTty.c_cflag = CREAD | CS8;

    nTty.c_lflag = 0;

    nTty.c_cc[VTIME]=0;

    nTty.c_cc[VMIN]=1;

    cfsetispeed(&nTty, 9600);

    cfsetospeed(&nTty, 9600);

    tcsetattr(fd, TCSANOW, &nTty);

    /* Our kernel cleverly ignores O_NONBLOCK.  Sigh. */

#if 0

    /*

     * Flush any pending keystrokes

     */

    while ((n = read (fd, buf, sizeof (buf))) > 0)

              ;

#endif

    KdRegisterFd (fd, LinuxKeyboardRead, ki);

    return Success;

}

LinuxConsoleFd is set in function LinuxInit in Kdrive/linux/linux.c as following:

LinuxConsoleFd = open(vtname, O_RDWR|O_NDELAY, 0) . This function is called during Main function in very beginning phase; we will not go to details of it now, since it is another long story. I only describe the invoke stack of it here:

              Main Function (dix/main.c) calls OsInit in os/osinit.c;

              OsInit Function calls OsVendorInit , which is in kdrive/linux/linux.c in this case;

              OsVendorInit Function calls KdOsInit in kdrive/src by passing LinuxFuncs as parameter;

              KdOsInit Function set LinuxFuncs to glocal Kdrive variable kdOsFuncs , and calls Init in LinuxFuncs , which is LinuxInit in this case;

              LinuxInit Function calls LinuxConsoleFd = open(vtname, O_RDWR|O_NDELAY, 0) , as described above.

The procedure that vtname is set is a little strange . First of all, /dev/tty0 is opened, and then ioctl by command VT_OPENQRY is used to get the vtno , which is then used to generate vtname by concatenate "/dev/tty" and vtno . Please refer to LinuxInit Function for details. ( ioctl VT_OPENQRY is used to find the number of the first available virtual console .)

Let s come back to function LinuxKeyboardEnable , which then uses a series parameter to set console LinuxConsoleFd , and at last, it invokes Function KdRegisterFd . This function record the file descriptor in global Kdirve variable kdInputFds , set function read to kdInputFds[kdNumInputFds].read , which in this case is LinuxKeyboardRead , and then invoke function KdAddFd . Function KdAddFd is described as following:

static void

KdAddFd (int fd)

{

    struct sigaction               act;

    sigset_t                             set;

    

    kdnFds++;

    fcntl (fd, F_SETOWN, getpid());

    KdNonBlockFd (fd);

    AddEnabledDevice (fd);

    memset (&act, '0', sizeof act);

    act.sa_handler = KdSigio;

    sigemptyset (&act.sa_mask);

    sigaddset (&act.sa_mask, SIGIO);

    sigaddset (&act.sa_mask, SIGALRM);

    sigaddset (&act.sa_mask, SIGVTALRM);

    sigaction (SIGIO, &act, 0);

    sigemptyset (&set);

    sigprocmask (SIG_SETMASK, &set, 0);

}

It first uses AddEnabledDevice (in os/Connection.c) to add a new file descriptor to global OS Model variable EnabledDevices (which is an fd_set used in asynchronous device or socket invoke). And then KdSigio is registered as the callback function when there is a change.

static void

KdSigio (int sig)

{

    int               i;

    for (i = 0; i < kdNumInputFds; i++)

              (*kdInputFds[i].read) (kdInputFds[i].fd, kdInputFds[i].closure);

}

The read function is LinuxKeyboardRead that we just mentioned.

Fuction LinuxKeyboardRead is a long function which read key code from file descriptor , mapping the key, and then enqueuee it, which is listed as following by invoking function KdEnqueueKeyboardEvent :

static void

LinuxKeyboardRead (int fd, void *closure)

{

    unsigned char   buf[256], *b;

    int                                 n;

    unsigned char   prefix = 0, scancode = 0;

    while ((n = read (fd, buf, sizeof (buf))) > 0) {

              b = buf;

              while (n--) {

#ifdef XKB

            if (!noXkbExtension) {

                /*

                 * With xkb we use RAW mode for reading the console, which allows us

                 * process extended scancodes.

                 *

                 * See if this is a prefix extending the following keycode

                 */

                if (!prefix && ((b[0] & 0x 7f ) == KEY_Prefix0))

                {

                        prefix = KEY_Prefix0;

#ifdef DEBUG

                        ErrorF("Prefix0");

#endif

                        /* swallow this up */

                        b++;

                        continue;

                }

                else if (!prefix && ((b[0] & 0x 7f ) == KEY_Prefix1))

                {

                        prefix = KEY_Prefix1;

                        ErrorF("Prefix1");

                        /* swallow this up */

                        b++;

                        continue;

                }

                scancode  = b[0] & 0x 7f ;

                switch (prefix) {

                        /* from xf86Events.c */

                        case KEY_Prefix0:

                        {

#ifdef DEBUG

                            ErrorF("Prefix0 scancode: 0x%02xn", scancode);

#endif

                            switch (scancode) {

                                case KEY_KP_7:

                                    scancode = KEY_Home;      break;  /* curs home */

                                case KEY_KP_8:

                                    scancode = KEY_Up;        break;  /* curs up */

                                case KEY_KP_9:

                                    scancode = KEY_PgUp;      break;  /* curs pgup */

                                case KEY_KP_4:

                                    scancode = KEY_Left;      break;  /* curs left */

                                case KEY_KP_5:

                                    scancode = KEY_Begin;     break;  /* curs begin */

                                case KEY_KP_6:

                                    scancode = KEY_Right;     break;  /* curs right */

                                case KEY_KP_1:

                                    scancode = KEY_End;       break;  /* curs end */

                                case KEY_KP_2:

                                    scancode = KEY_Down;      break;  /* curs down */

                                case KEY_KP_3:

                                    scancode = KEY_PgDown;    break;  /* curs pgdown */

                                case KEY_KP_0:

                                    scancode = KEY_Insert;    break;  /* curs insert */

                                case KEY_KP_Decimal:

                                    scancode = KEY_Delete;    break;  /* curs delete */

                                case KEY_Enter:

                                    scancode = KEY_KP_Enter;  break;  /* keypad enter */

                                case KEY_LCtrl:

                                    scancode = KEY_RCtrl;     break;  /* right ctrl */

                                case KEY_KP_Multiply:

                                    scancode = KEY_Print;     break;  /* print */

                                case KEY_Slash:

                                    scancode = KEY_KP_Divide; break;  /* keyp divide */

                                case KEY_Alt:

                                    scancode = KEY_AltLang;   break;  /* right alt */

                                case KEY_ScrollLock:

                                    scancode = KEY_Break;     break;  /* curs break */

                                case 0x5b:

                                    scancode = KEY_LMeta;     break;

                                case 0x 5c :

                                    scancode = KEY_RMeta;     break;

                                case 0x5d:

                                    scancode = KEY_Menu;      break;

                                case KEY_F3:

                                    scancode = KEY_F13;       break;

                                case KEY_F4:

                                    scancode = KEY_F14;       break;

                                case KEY_F5:

                                    scancode = KEY_F15;       break;

                                case KEY_F6:

                                    scancode = KEY_F16;       break;

                                case KEY_F7:

                                    scancode = KEY_F17;       break;

                                case KEY_KP_Plus:

                                    scancode = KEY_KP_DEC;    break;

                                /* Ignore virtual shifts (E0 2A , E0 AA, E0 36, E0 B6) */

                                case 0x 2A :

                                case 0x36:

                                    b++;

                                    prefix = 0;

                                    continue;

                                default:

#ifdef DEBUG

                                    ErrorF("Unreported Prefix0 scancode: 0x%02xn",

                                           scancode);

#endif

                                     /*

                                      * "Internet" keyboards are generating lots of new

                                      * codes.  Let them pass.  There is little consistency

                                      * between them, so don't bother with symbolic names at

                                      * this level.

                                      */

                                    scancode += 0x78;

                            }

                            break;

                        }

                        case KEY_Prefix1:

                        {

                            /* we do no handle these */

#ifdef DEBUG

                            ErrorF("Prefix1 scancode: 0x%02xn", scancode);

#endif

                            b++;

                            prefix = 0;

                            continue;

                        }

                        default: /* should not happen*/

                        case 0: /* do nothing */

#ifdef DEBUG

                            ErrorF("Plain scancode: 0x%02xn", scancode);

#endif

                            ;

                }

                prefix = 0;

            }

            /* without xkb we use mediumraw mode -- enqueue the scancode as is */

            else

#endif

                scancode = b[0] & 0x 7f ;

                  KdEnqueueKeyboardEvent (closure, scancode, b[0] & 0x80);

                  b++;

              }

    }

}

1.2.1                  Pointer Device Register

Go back to the main() function in dix.c, P rocessCommandLine will first be called to interpret command line arguments passed to start X server. ddxProcessArgument in omapinit.c will then be called, which calls KdProcessArgument to take corresponding action according to different arguments. We can notice that there is a statement if (!strcmp (argv[i], "-mouse") || !strcmp (argv[i], "-pointer")) which invokes KdAddConfigPointer taking argv[i+1] as it s parameter, which news a struct KdConfigDevice appended to the last of gobal variable kdConfigPointers with member line assigned to  argv[i+1] for future use.

After keyboard is registered  using KdAddKeyboard in InitInput(omapinit.c), KdInitInput   is called to registered other devices kept in kdConfigPointers and kdConfigKeyboards . This is similar to how keyboard is registered and we will not go any further here.

Since the driver name for pointer device is tslib , we can conclude that X server will be started at least wi t h -mouse tslib or -pointer tslib .

As mentioned above, function InitAndStartDevices in main(dix.c) function is invoked; it first calls ActivateDevice to all devices in inputInfo.off_devices list. This function invokes the callback which is registered when adding new device by passing parameter DEVICE_INIT , which should be KdPointerProc in our case.

Similarly, case DEVICE_INIT will first be covered, which finds according driver using tslib in our case, then call the driver s callback TslibInit , see following code:

static void TslibInit (KdPointerInfo *pi)

{

    int                                     fd = 0, i = 0;

    char                devpath[PATH_MAX], devname[TS_NAME_SIZE];

    DIR                 *inputdir = NULL;

    struct dirent       *inputent = NULL;

    struct TslibPrivate *private = NULL;

 

    if (!pi || !pi->dixdev)

        return !Success;

    

    if (!pi->path || strcmp(pi->path, "auto") == 0) {

        if (!(inputdir = opendir("/dev/input"))) {

            ErrorF("[tslib/TslibInit]: couldn't open /dev/input!n");

            return BadMatch;

        }

 

        while ((inputent = readdir(inputdir))) {

            if (strncmp(inputent->d_name, "event", 5) != 0)

                continue;

 

            snprintf(devpath, PATH_MAX, "/dev/input/%s", inputent->d_name);

            fd = open(devpath, O_RDWR);

 

            if (!ioctl(fd, EVIOCGNAME(sizeof(devname)), devname)) {

                close(fd);

                continue;

            }

            close(fd);

 

            for (i = 0; valid_ts_names[i]; i++) {

                if (strcmp(devname, valid_ts_names[i]) == 0) {

                    pi->path = KdSaveString(devpath);

                    break;

                }

            }

        }

                

        closedir(inputdir);

    }

 

    if (!pi->path || strcmp(pi->path, "auto") == 0) {

        ErrorF("[tslib/TslibInit]: couldn't find device!n");

        return BadMatch;

    }

 

    pi->driverPrivate = (struct TslibPrivate *)

                        xcalloc(sizeof(struct TslibPrivate), 1);

    if (!pi->driverPrivate)

        return !Success;

 

    private = pi->driverPrivate;

    /* hacktastic */

    private->phys_screen = 0;

    private->raw_event_hook = NULL;

    private->raw_event_closure = NULL;

    pi->nAxes = 3;

    pi->nButtons = 8;

    pi->name = KdSaveString("Touchscreen");

    pi->inputClass = KD_TOUCHSCREEN;

    DebugF("[tslib/TslibInit] successfully inited for device %sn", pi->path);

 

    return Success;

}

It iterators through /dev/input , using ioctl to get device name of devices with name /dev/input/eventN(N=0, 1, 2, ), if one matches one of the items in valid_ts_names , this device is the target one and the path is stored into pi->path for next step.

Then case DEVICE_ON is covered, which invokes TslibEnable . Here our pointer device is opened followed by KdRegisterFd , passing TsRead as it s argument.

When there is a SIGIO, TsRead will also be invoked . Since there will be infinite events theoretically, ts_read is used to get an sample. This sampled event will be passed to KdEnqueuePointerEvent , which does some matrix transform before enqueued.

Seeing from above, there is little needed to be done for touch screen porting.

1.3            Strange Points of Maemo DDX Input Layer

First of all, the keyboard driver we found it here likes something of virtual terminal, not a real physical keyboard on devices.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值