momo zone

调核人的blog

xorg和linux input system

在 /proc内有进程打开的文件信息, 比如简单看看Xorg打开了什么文件, 可以这样:

$ps -A | grep Xorg
 6245 tty7     00:56:59 Xorg

 sudo ls /proc/6245/fd -l
[sudo] password for yhe:
total 0
l-wx—— 1 root root 64 2009-04-14 14:10 0 -> /var/log/Xorg.0.log
lrwx—— 1 root root 64 2009-04-14 14:10 1 -> socket:[16061]
lrwx—— 1 root root 64 2009-04-14 14:10 10 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 11 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 12 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 13 -> socket:[16387]
lrwx—— 1 root root 64 2009-04-14 14:10 14 -> /dev/nvidiactl
lrwx—— 1 root root 64 2009-04-14 14:10 15 -> /dev/nvidia0
lr-x—— 1 root root 64 2009-04-14 14:10 16 -> /proc/acpi/video/VID1/LCD/state
lr-x—— 1 root root 64 2009-04-14 14:10 17 -> /proc/acpi/video/VID1/LCD/info
lr-x—— 1 root root 64 2009-04-14 14:10 18 -> /proc/acpi/video/VID1/CRT/state
lr-x—— 1 root root 64 2009-04-14 14:10 19 -> /proc/acpi/video/VID1/CRT/info
l-wx—— 1 root root 64 2009-04-14 14:10 2 -> /var/log/gdm/:0.log
lr-x—— 1 root root 64 2009-04-14 14:10 20 -> /proc/acpi/video/VID1/TV/state
lr-x—— 1 root root 64 2009-04-14 14:10 21 -> /proc/acpi/video/VID1/TV/info
lr-x—— 1 root root 64 2009-04-14 14:10 22 -> /proc/acpi/video/VID/DVI/state
lr-x—— 1 root root 64 2009-04-14 14:10 23 -> /proc/acpi/video/VID/DVI/info
lr-x—— 1 root root 64 2009-04-14 14:10 24 -> /proc/acpi/video/VID/LCD/state
lr-x—— 1 root root 64 2009-04-14 14:10 25 -> /proc/acpi/video/VID/LCD/info
lr-x—— 1 root root 64 2009-04-14 14:10 26 -> /proc/acpi/video/VID/CRT/state
lr-x—— 1 root root 64 2009-04-14 14:10 27 -> /proc/acpi/video/VID/CRT/info
lr-x—— 1 root root 64 2009-04-14 14:10 28 -> /proc/acpi/video/VID/TV/state
lr-x—— 1 root root 64 2009-04-14 14:10 29 -> /proc/acpi/video/VID/TV/info
lrwx—— 1 root root 64 2009-04-14 14:10 3 -> socket:[16082]
lrwx—— 1 root root 64 2009-04-14 14:10 30 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 31 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 32 -> /dev/psaux
lrwx—— 1 root root 64 2009-04-14 14:10 33 -> /dev/nvidiactl
lrwx—— 1 root root 64 2009-04-14 14:10 34 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 35 -> /dev/nvidia0
lrwx—— 1 root root 64 2009-04-14 14:10 36 -> socket:[16545]
lrwx—— 1 root root 64 2009-04-14 14:10 37 -> socket:[16547]
lrwx—— 1 root root 64 2009-04-14 14:10 38 -> socket:[17674]
lrwx—— 1 root root 64 2009-04-14 14:10 39 -> socket:[17679]
lrwx—— 1 root root 64 2009-04-14 14:10 4 -> /dev/tty7
…..
lrwx—— 1 root root 64 2009-04-14 14:10 5 -> /proc/bus/pci/01/00.0
….
lrwx—— 1 root root 64 2009-04-14 14:10 6 -> /dev/nvidiactl

非常值得注意的是, /dev/nvidia0, /dev/nvidiactl是NV 官方驱动引入的两个设备文件, 而/dev/tty7的含义也很明显, 为什么ctrl+alt+f7 可以切换到X系统就很明显拉.  同时Xorg这个进程还打开了acpi的CRT以及LCD相关文件, 可以获取电源管理的一些信息. /dev/psaux 则是一个虚拟设备, 由mousedev.c创建, 他获取系统的鼠标设备事件, 以PS2协议提供设备/dev/psaux给use space使用, 这个mousedev可以产生三种PS2协议: PS/2, ImPS2(Microsoft ItelliMouse, 支持滚轮), ExplorerPS/2( 支持多个鼠标button).  mousedev和linux新的input系统共存, 是一个临时的hack 行为。

/proc/bus/pci/01/00.0, 对应pci bus 1, 设备00.0,  从可以看到这设备正是nvida显卡.
$ls /sys/bus/pci/devices/0000:01:00.0/ -l
total 0
-rw-r–r– 1 root root      4096 2009-04-15 09:58 broken_parity_status
-r–r–r– 1 root root      4096 2009-04-15 09:58 class
-rw-r–r– 1 root root       256 2009-04-15 09:58 config
-r–r–r– 1 root root      4096 2009-04-15 09:58 device
lrwxrwxrwx 1 root root         0 2009-04-15 09:58 driver -> ../../../../bus/pci/drivers/nvidia
-rw——- 1 root root      4096 2009-04-15 09:58 enable
drwxr-xr-x 5 root root         0 2009-04-15 09:58 i2c-adapter
-r–r–r– 1 root root      4096 2009-04-15 09:58 irq
-r–r–r– 1 root root      4096 2009-04-15 09:58 local_cpus
-r–r–r– 1 root root      4096 2009-04-15 09:58 modalias
-rw-r–r– 1 root root      4096 2009-04-15 09:58 msi_bus
drwxr-xr-x 2 root root         0 2009-04-15 09:58 power
-r–r–r– 1 root root      4096 2009-04-15 09:58 resource
-rw——- 1 root root  16777216 2009-04-15 09:58 resource0
-rw——- 1 root root 268435456 2009-04-15 09:58 resource1
-rw——- 1 root root  33554432 2009-04-15 09:58 resource3
-rw——- 1 root root       128 2009-04-15 09:58 resource5
-r——– 1 root root    131072 2009-04-15 09:58 rom
lrwxrwxrwx 1 root root         0 2009-04-15 09:58 subsystem -> ../../../../bus/pci
-r–r–r– 1 root root      4096 2009-04-15 09:58 subsystem_device
-r–r–r– 1 root root      4096 2009-04-15 09:58 subsystem_vendor
-rw-r–r– 1 root root      4096 2009-04-15 09:58 uevent
-r–r–r– 1 root root      4096 2009-04-15 09:58 vendor

然后可以获得verndor 和device 的名字.
$cat /usr/share/misc/pci.ids | grep  10de
10de  nVidia Corporation

$cat /sys/bus/pci/devices/0000:01:00.0/device
0x042b
$ cat /usr/share/misc/pci.ids | grep  042b
    042b  Quadro NVS 135M

但是这些过程不用这么麻烦, 有现成的工具, 做得更好.

$ ps -A | grep Xorg
 6134 tty7     00:05:02 Xorg
$ sudo lsof -p 6134

然后查看硬件信息如nvidia, 可以
lspci -d 10de: -vvv -xxx -b
01:00.0 VGA compatible controller: nVidia Corporation Quadro NVS 135M (rev a1) (prog-if 00 [VGA controller])
    Subsystem: Dell Unknown device 01f9
    Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B-
    Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR-
    Latency: 0
    Interrupt: pin A routed to IRQ 5
    Region 0: Memory at f5000000 (32-bit, non-prefetchable)
    Region 1: Memory at 00000000e0000000 (64-bit, prefetchable)
    Region 3: Memory at 00000000f2000000 (64-bit, non-prefetchable)
    Region 5: I/O ports at ef00
    Capabilities: <access denied>
00: de 10 2b 04 07 00 10 00 a1 00 00 03 00 00 00 00
10: 00 00 00 f5 0c 00 00 e0 00 00 00 00 04 00 00 f2
20: 00 00 00 00 01 ef 00 00 00 00 00 00 28 10 f9 01
30: 00 00 00 00 60 00 00 00 00 00 00 00 05 01 00 00


稍微深入看Xorg如何处理鼠标键盘初始化,需要看看xorg的代码。从本文分析的具体环境如下, 从/var/log/Xorg.0.log获取:

X.Org X Server 1.4.0.90
Release Date: 5 September 2007
X Protocol Version 11, Revision 0
Build Operating System: Linux Ubuntu (xorg-server 2:1.4.1~git20080131-1ubuntu9.2)
Current Operating System: Linux yhe-laptop 2.6.24-23-generic #1 SMP Thu Feb 5 15:00:25 UTC 2009 i686

首先安装source code:sudo apt-get  source xorg-server,就能得到源代码xorg-server-1.4.1~git20080131. 不深入看xorg代码,从config 解析看看鼠标键盘的初始化吧.先看看log记录:

Markers: (–) probed, (**) from config file, (==) default setting,
        (++) from command line, (!!) notice, (II) informational,
        (WW) warning, (EE) error, (NI) not implemented, (??) unknown.

(==) Log file: "/var/log/Xorg.0.log", Time: Mon May  4 10:40:15 2009
(==) Using config file: "/etc/X11/xorg.conf"  —> xf86HandleConfigFile

(==) ServerLayout "Layout0"  —–>xf86parseLayoutSection
(**) |–>Screen "Screen0" (0)
(**) |   |–>Monitor "Monitor0"
(**) |   |–>Device "Device0"
(**) |–>Input Device "Keyboard0"
(**) |–>Input Device "Mouse0"
(==) Automatically adding devices
(==) Automatically enabling devices

对应代码是hw/xfree86/parser/scan.c , xf86openConfigFile, 通过两个宏来搜索配置文件:
#define DEFAULT_CONF_PATH    "/etc/X11/%S,"
                                                     …………………
#define XCONFIGFILE    "xorg.conf"

继续深入前,看看这个环境的xorg.conf 对应输入部分的内容:

Section "ServerLayout"
    Identifier     "Layout0"
    Screen      0  "Screen0"
    InputDevice    "Keyboard0" "CoreKeyboard"
    InputDevice    "Mouse0" "CorePointer"
EndSection

Section "InputDevice"
    # generated from default
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/psaux"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection

Section "InputDevice"
    # generated from default
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection

从上面提到的函数可以追踪到键盘配置的解析过程xf86parseInputSection,将鼠标键盘的配置记录下来,接着看log:
(II) Module ABI versions:
        X.Org ANSI C Emulation: 0.3
        X.Org Video Driver: 2.0
        X.Org XInput driver : 2.0
        X.Org Server Extension : 0.3
        X.Org Font Renderer : 0.5
(II) Loader running on linux  —>xfree86/common/xf86init.c->InitOutput->LoaderInit
….
(++) using VT number 7  —>   
xf86OpenConsole();

(II) LoadModule: "kbd"
(II) Loading /usr/lib/xorg/modules/input//kbd_drv.so
(II) Module kbd: vendor="X.Org Foundation"
        compiled for 1.4.0, module version = 1.2.2
        Module class: X.Org XInput Driver
        ABI class: X.Org XInput driver, version 2.0
(II) LoadModule: "mouse"
(II) Loading /usr/lib/xorg/modules/input//mouse_drv.so
(II) Module mouse: vendor="X.Org Foundation"
        compiled for 1.4.0, module version = 1.2.3
        Module class: X.Org XInput Driver
        ABI class: X.Org XInput driver, version 2.0
……
(II) Initializing built-in extension XInputExtension
(II) Initializing built-in extension XTEST

从log可以看到, kbd是加载的kbd_drv.so, 这个driver在另外一个包中, 需要单独获取源代码:
sudo apt-get source xserver-xorg-input-kbd. 编译以下就知道是哪几个C文件构成这个xserver 的drv了:
gcc -shared  .libs/kbd.o .libs/lnx_KbdMap.o .libs/lnx_kbd.o .libs/at_scancode.o   -Wl,-soname -Wl,kbd_drv.so -o .libs/kbd_drv.so
也可以参考 man 4 kbd. 我们的配置文件如下, 选择的就是这个驱动, 在man的信息里提到,默认(没有指定device参数的)情况下, 使用tty7来作为输入设备.
Section "InputDevice"
    # generated from default
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection
打开设备的代码如下, 可以看到在么有指定device的情况下,就用tty7作为键盘输入源.

static Bool
OpenKeyboard(InputInfoPtr pInfo)
{
    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
    int i;
    KbdProtocolId prot = PROT_UNKNOWN_KBD;
    char *s;

    s = xf86SetStrOption(pInfo->options, "Protocol", NULL);
    for (i = 0; protocols[i].name; i++) {
        if (xf86NameCmp(s, protocols[i].name) == 0) {
           prot = protocols[i].id;
           break;
        }
    }

    switch (prot) {
        case PROT_STD:
           pInfo->read_input = stdReadInput;
           break;
        default:
           xf86Msg(X_ERROR,""%s" is not a valid keyboard protocol namen", s);
           xfree(s);
           return FALSE;
    }

    xf86Msg(X_CONFIG, "%s: Protocol: %sn", pInfo->name, s);
    xfree(s);

    s = xf86SetStrOption(pInfo->options, "Device", NULL);
    if (s == NULL) {
       pInfo->fd = xf86Info.consoleFd;

       pKbd->isConsole = TRUE;

    } else {
       pInfo->fd = open(s, O_RDONLY | O_NONBLOCK | O_EXCL);
       if (pInfo->fd == -1) {
           xf86Msg(X_ERROR, "%s: cannot open "%s"n", pInfo->name, s);
           xfree(s);
           return FALSE;
       }
       pKbd->isConsole = FALSE;
       xfree(s);
    }

    if (pKbd->isConsole)
         pKbd->vtSwitchSupported = TRUE;

    return TRUE;
}

……….
(**) Option "CoreKeyboard"
(**) Keyboard0: always reports core events
(**) Option "Protocol" "standard" 
(**) Keyboard0: Protocol: standard  xserver-xorg-input-kbd/src/lnx_kbd.c ->OpenKeyboard

(**) Option "AutoRepeat" "500 30"
(**) Option "XkbRules" "xorg"
(**) Keyboard0: XkbRules: "xorg"
(**) Option "XkbModel" "pc105"
(**) Keyboard0: XkbModel: "pc105"
(**) Option "XkbLayout" "us"
(**) Keyboard0: XkbLayout: "us"
(**) Option "CustomKeycodes" "off"
(**) Keyboard0: CustomKeycodes disabled

鼠标类似,也是单独下载驱动的源码:sudo apt-get source xserver-xorg-input-mouse, make 然后知道其组成:
gcc -shared  .libs/mouse.o .libs/pnp.o   -Wl,-soname -Wl,mouse_drv.so -o .libs/mouse_drv.so

Section "InputDevice"
    # generated from default
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/psaux"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection


(**) Option "Protocol" "auto"
(**) Mouse0: Device: "/dev/psaux"  xserver-xorg-input-mouse/src/mouse.c:MousePreInit

(**) Mouse0: Protocol: "auto"
(**) Option "CorePointer"
(**) Mouse0: always reports core events
(**) Option "Device" "/dev/psaux"
(**) Option "Emulate3Buttons" "no"
(**) Option "ZAxisMapping" "4 5"
(**) Mouse0: ZAxisMapping: buttons 4 and 5
(**) Mouse0: Buttons: 9
(**) Mouse0: Sensitivity: 1
(II) evaluating device (Mouse0)
(II) XINPUT: Adding extended input device "Mouse0" (type: MOUSE)
(II) XINPUT: Adding extended input device "Mouse0" (type: MOUSE)
(II) evaluating device (Keyboard0)
(II) XINPUT: Adding extended input device "Keyboard0" (type: KEYBOARD)
(–) Mouse0: PnP-detected protocol: "ExplorerPS/2"
(II) Mouse0: ps2EnableDataReporting: succeeded


Xorg 键盘鼠标输入处理流程

先总结后分析: xorg 处理两种输入, 一种是鼠标键盘这种设备的输入, 另一种是client的socket传入的请求. socket当然采用的是select, 来进行异步处理. 本环境下鼠标键盘的x驱动是通过tty的fd进行select. 有的系统, 鼠标输入采用异步IO, 采用的信号是SIGIO, 比如BSD. 采用SIGIO的还有些DRI设备(Direct Rendering Infrastructure 参考:http://en.wikipedia.org/wiki/Direct_Rendering_Infrastructure )

这里采用的xorg环境上面已经有所提及, 这里提及一点历史:  xfree86 原来专用于x86,后来发展成极为通用的X系统, 但是XFree86从2004年发布的版本4.4起不再遵从GPL许可证发行, 同年Xorg发布了基于4.4版本的Xorg X系统.

所以你看到xorg代码中有多个相同定义时, 对我们的环境, 它是xfree86所定义的那个函数. 此外预先介绍下xorg Xserver的几个相关组成部分(google即得):
*xserver/dix:   Device Independent
                 1) 资源管理(字体,位图,光标之类    2. dispatch, 处理client请求(events.c)   3.处理input事件
* xserver/mi … machine independent X. Mouse cursor rendering stuff.
* xserver/hw/xfree86/common … some additional stuff, especially for X Input.
* xserver/Xi … X Input Extension protocol stuff.
文章推荐:
http://wearables.unisa.edu.au/mpx/?q=eventprocessing   MP X server 项目文档 此文档有个如何处理鼠标输入的图, 非常直观, 先奉上. 这个例子里鼠标采用的是在SIGIO信号内处理鼠标事件,和这里讨论的具体环境有差异,仅供参考. (单击看大图了)

先看对client的服务是如何分发的, 前面提到是select模式, 非常有意义的一个变量是定义在 xserver/os/connection.c中,
fd_set AllSockets;        /* select on this */
新建立的链接会吧自己的fd加入这个set, 而Dispatch会直接select on 这个set. 在同一个文件还有另外一个重要的变量:
fd_set EnabledDevices;        /* mask for input devices that are on */
用于记录打开的输入设备, 比如我们所讨论的鼠标和键盘.

还是先从main开始吧, 在server/dix/main.c中

int main(int argc, char *argv[], char *envp[])
{
……..
 while(1)
    {
    OsInit();

        config_init();
    if(serverGeneration == 1)
    {
        CreateWellKnownSockets(); /*打开server端口并加入AllSockets*/
             …..
       }
……….
    InitOutput(&screenInfo, argc, argv); /*读取配置文件, 打开console等,载入input设备驱动和各种模块
                                           (xfree86/common/xf86init.c), 注册
xf86Wakeup 到wakup handlers*/
#ifdef XPRINT
    PrinterInitOutput(&screenInfo, argc, argv);
#endif

    if (screenInfo.numScreens < 1)
        FatalError("no screens found");
    if (screenInfo.numVideoScreens < 0)
        screenInfo.numVideoScreens = screenInfo.numScreens;
    InitExtensions(argc, argv);
    if (!InitClientPrivates(serverClient))
        FatalError("failed to allocate serverClient devprivates");
    for (i = 0; i < screenInfo.numScreens; i++)
    {
        ScreenPtr pScreen = screenInfo.screens[i];
        if (!CreateScratchPixmapsForScreen(i))
        FatalError("failed to create scratch pixmaps");
        if (pScreen->CreateScreenResources &&
        !(*pScreen->CreateScreenResources)(pScreen))
        FatalError("failed to create screen resources");
        if (!CreateGCperDepth(i))
        FatalError("failed to create scratch GCs");
        if (!CreateDefaultStipple(i))
        FatalError("failed to create default stipple");
        if (!CreateRootWindow(pScreen))
        FatalError("failed to create root window");
    }
    InitCoreDevices();
    InitInput(argc, argv); /*调用每个输入设备的PreInit函数, mieqInit*/
    if (InitAndStartDevices() != Success) /*向鼠标键盘发送DEVICE_ON命令, 注册到全局输入处理表:handlers*/
        FatalError("failed to initialize core devices");

    InitFonts();

    Dispatch(); /*WaitForSomething select on AllSockets, 有键盘鼠标输入也会被信号唤醒,并处理*/

    /* Now free up whatever must be freed */
……….
}

键盘鼠标的输入函数注册
InitAndStartDevices -> KbdProc(xserver-xorg-input-kbd-1.22/src/kbc.c)(鼠标的亦同)  -> AddEnabledDevice(pInfo->fd)  : 把fd同时加入EnabledDevices, 和AllSockets 全局fdset中, 并在全局

读取数据:
InitOutput -> RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, xf86Wakeup,NULL);
    注册xf86Wakup到全局表handlers 中,在任务被唤醒后得到调用.
os/WaitFor.c :WaitForSomething -> WakeupHandler ->(*handlers[i].WakeupHandler) (handlers[i].blockData,..) -> xf86Wakeup
     根据select结果, 在全局输入设备表xf86InputDevs 中找到此设备调用其read_input函数.

唤醒:
内核根据select 结果,唤醒Xorg.

xclient传递的消息在Dispatch函数中处理.  至于鼠标键盘的read_input读取键盘/鼠标输入然后分发给xclient的过程就不做研究了.

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: