现在的位置: 首页 > 综合 > 正文

闲聊linux中的input设备(1)Linux中的设备大家族

2013年01月19日 ⁄ 综合 ⁄ 共 6894字 ⁄ 字号 评论关闭

  用过linux的哥们都知道,linux所有的设备都是以文件的形式实现的,要访问一个设备,我们只需要以openreadwrite的形式对设备的进行操作就可以了。在linux系统的/dev目录下,罗列了当前系统支持的所有设备。运行 ls /dev一下,着实吓了一大跳,

[root@localhost ~]# ls /dev

adsp        full     midi      ram9        tty15  tty42  ttyS3

agpgart     fuse     mixer     ramdisk     tty16  tty43  urandom

audio       hpet     net       random      tty17  tty44  usbdev1.1_ep00

bsg         hvc0     null      root        tty18  tty45  usbdev1.1_ep81

bus         hvc1     nvram     rtc         tty19  tty46  usbmon0

cdrom       hvc2     oldmem    scd0        tty2   tty47  usbmon1

console     hvc3     parport0  sda         tty20  tty48  vcs

core        hvc4     parport1  sda1        tty21  tty49  vcs1

disk        hvc5     parport2  sda2        tty22  tty5   vcs2

dmmidi      hvc6     parport3  sda3        tty23  tty50  vcs3

dsp         hvc7     port      sequencer   tty24  tty51  vcs4

fd          initctl  ppp       sequencer2  tty25  tty52  vcs5

fd0         input    ptmx      sg0         tty26  tty53  vcs6

fd0u1040    kmsg     pts       sg1         tty27  tty54  vcs7

fd0u1120    log      ram       shm         tty28  tty55  vcs8

fd0u1440    loop0    ram0      snapshot    tty29  tty56  vcsa

fd0u1600    loop1    ram1      snd         tty3   tty57  vcsa1

fd0u1680    loop2    ram10     sr0         tty30  tty58  vcsa2

fd0u1722    loop3    ram11     stderr      tty31  tty59  vcsa3

fd0u1743    loop4    ram12     stdin       tty32  tty6   vcsa4

fd0u1760    loop5    ram13     stdout      tty33  tty60  vcsa5

fd0u1840    loop6    ram14     systty      tty34  tty61  vcsa6

fd0u1920    loop7    ram15     tty         tty35  tty62  vcsa7

fd0u360     lp0      ram2      tty0        tty36  tty63  vcsa8

fd0u720     lp1      ram3      tty1        tty37  tty7   X0R

fd0u800     lp2      ram4      tty10       tty38  tty8   XOR

fd0u820     lp3      ram5      tty11       tty39  tty9   zero

fd0u830     MAKEDEV  ram6      tty12       tty4   ttyS0

floppy      mapper   ram7      tty13       tty40  ttyS1

floppy-fd0  mem      ram8      tty14       tty41  ttyS2

这么多设备那么管理起来是不是很麻烦,linux内核的开发者智商自然在你我之上,他们把所有的这些设备归为三大类,即平时我们熟悉的字符设备、块设备、网络设备。运行 ls –l /dev(这里我只取一部分显示信息)

crw-rw----+ 1 root   root    14,  12 12-16 00:57 adsp

crw-------  1 root   root    10, 175 12-16 00:57 agpgart

crw-rw----+ 1 root   root    14,   4 12-16 00:57 audio

drwxr-xr-x  2 root   root         80 12-16 00:57 bsg

drwxr-xr-x  3 root   root         60 12-16 00:57 bus

lrwxrwxrwx  1 root   root          3 12-16 00:57 cdrom -> sr0

crw-------  1 lmm670 root     5,   1 12-16 00:57 console

lrwxrwxrwx  1 root   root         11 12-16 00:57 core -> /proc/kcore

drwxr-xr-x  5 root   root        100 12-16 00:57 disk

crw-rw----  1 root   root    14,   9 12-16 00:57 dmmidi

crw-rw----+ 1 root   root    14,   3 12-16 00:57 dsp

lrwxrwxrwx  1 root   root         13 12-16 00:57 fd -> /proc/self/fd

brw-r-----  1 root   floppy   2,   0 12-16 00:57 fd0

brw-r-----  1 root   floppy   2,  84 12-16 00:57 fd0u1040

brw-r-----  1 root   floppy   2,  88 12-16 00:57 fd0u1120

brw-r-----  1 root   floppy   2,  28 12-16 00:57 fd0u1440

brw-r-----  1 root   floppy   2, 124 12-16 00:57 fd0u1600

brw-r-----  1 root   floppy   2,  44 12-16 00:57 fd0u1680

brw-r-----  1 root   floppy   2,  60 12-16 00:57 fd0u1722

brw-r-----  1 root   floppy   2,  76 12-16 00:57 fd0u1743

brw-r-----  1 root   floppy   2,  96 12-16 00:57 fd0u1760

brw-r-----  1 root   floppy   2, 116 12-16 00:57 fd0u1840

brw-r-----  1 root   floppy   2, 100 12-16 00:57 fd0u1920

brw-r-----  1 root   floppy   2,  12 12-16 00:57 fd0u360

brw-r-----  1 root   floppy   2,  16 12-16 00:57 fd0u720

brw-r-----  1 root   floppy   2, 120 12-16 00:57 fd0u800

brw-r-----  1 root   floppy   2,  52 12-16 00:57 fd0u820

brw-r-----  1 root   floppy   2,  68 12-16 00:57 fd0u830

lrwxrwxrwx  1 root   root          3 12-16 00:57 floppy -> fd0

lrwxrwxrwx  1 root   root          3 12-16 00:57 floppy-fd0 -> fd0

crw-rw-rw-  1 root   root     1,   7 12-16 00:57 full

crw-rw----  1 root   fuse    10, 229 12-16 00:57 fuse

crw-rw----  1 root   root    10, 228 12-16 00:57 hpet

crw-rw----  1 root   uucp   229,   0 12-16 00:57 hvc0

crw-rw----  1 root   uucp   229,   1 12-16 00:57 hvc1

crw-rw----  1 root   uucp   229,   2 12-16 00:57 hvc2

crw-rw----  1 root   uucp   229,   3 12-16 00:57 hvc3

crw-rw----  1 root   uucp   229,   4 12-16 00:57 hvc4

crw-rw----  1 root   uucp   229,   5 12-16 00:57 hvc5

crw-rw----  1 root   uucp   229,   6 12-16 00:57 hvc6

crw-rw----  1 root   uucp   229,   7 12-16 00:57 hvc7

prw-------  1 root   root          0 12-16 00:58 initctl

drwxr-xr-x  3 root   root        200 12-16 00:57 input

crw-rw----  1 root   root     1,  11 12-16 00:57 kmsg

srw-rw-rw-  1 root   root          0 12-16 00:58 log

brw-r-----  1 root   disk     7,   0 12-16 00:57 loop0

大家可以看到,每一行的第一个字母,代表着此文件的类型。c表示字符设备,b表示块设备,s表示网络设备,细心的哥们会问,不是说只有三类设备吗,怎么还有其他类型开头的呢?比如dl等等。对不起,这里讲的是文件类型,d表示是一个目录文件,l表示一个链接文件。至于这三者之间的区别,我就不在这啰嗦了,Google一下一大堆。我要强调的是,无论上面三个设备中的任何一种设备,要想在linux实现它的设备驱动,首先要对它进行一系列的初始化工作,然后需给它提供一个设备操作集合(或者更简单一点理解:接口函数),用来提供给我们的上层程序进行访问,比如openreadwrite等等。要不然,我要你这个设备驱动干嘛。在字符设备驱动中,我们的操作集函数是这样的

struct file_operations {

       struct module *owner;

       loff_t (*llseek) (struct file *, loff_t, int);

       ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

       ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

       ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       int (*readdir) (struct file *, void *, filldir_t);

       unsigned int (*poll) (struct file *, struct poll_table_struct *);

       int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

       long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

       long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

       int (*mmap) (struct file *, struct vm_area_struct *);

       int (*open) (struct inode *, struct file *);

       int (*flush) (struct file *, fl_owner_t id);

       int (*release) (struct inode *, struct file *);

       int (*fsync) (struct file *, struct dentry *, int datasync);

       int (*aio_fsync) (struct kiocb *, int datasync);

       int (*fasync) (int, struct file *, int);

       int (*lock) (struct file *, int, struct file_lock *);

       ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

       unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

       int (*check_flags)(int);

       int (*flock) (struct file *, int, struct file_lock *);

       ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

       ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

       int (*setlease)(struct file *, long, struct file_lock **);

};

网名为“卖血去上网”的兄弟要说了,写一个驱动接口函数怎么这么复杂,要提供这么多接口函数。其实不然,一般的设备驱动接口函数并不需要把上面的所有都实现,只需要实现里面的一些:比如我们这里:

static const struct file_operations evdev_fops = {

       .owner           = THIS_MODULE,

       .read              = evdev_read,

       .write             = evdev_write,

       .poll        = evdev_poll,

       .open             = evdev_open,

       .release    = evdev_release,

       .unlocked_ioctl      = evdev_ioctl,

#ifdef CONFIG_COMPAT

       .compat_ioctl  = evdev_ioctl_compat,

#endif

       .fasync           = evdev_fasync,

       .flush             = evdev_flush

};

兄弟们注意了,这个结构体就是前面那个结构体的实现,类似于c++中的类和对象。结构中若干个等式中,我们最终要实现的是右边的那些函数。这样做的目的大家都清楚:实现统一接口,增加程序的可移植性。上层代码每次open evdev这个设备的时候最终都会通过file_operations落实到我们的evdev_open函数。鲁迅先生曾说过:驱动代码写起来其实并不难,当接口函数多了就变难了。说了一大推好像被忽悠了,怎么没提到一点关于input设备的信息,关于input设备我现在只提一句,input设备的接口函数,linux内核已经为我们写好了。毕竟时代在进步,内核在更新,鲁先生的话也可以改一下了:设备驱动的实现本身很难,自从有了input设备子系统,就变得不难了(当然只针对input设备)。到底何谓input设备呢?请看下一节。

 

抱歉!评论已关闭.