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

        用过linux的哥们都知道,linux所有的设备都是以文件的形式实现的,要访问一个设备,我们只需要以open、read、write的形式对设备的进行操作就可以了。在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表示网络设备,细心的哥们会问,不是说只有三类设备吗,怎么还有其他类型开头的呢?比如d、l等等。对不起,这里讲的是文件类型,d表示是一个目录文件,l表示一个链接文件。至于这三者之间的区别,我就不在这啰嗦了,Google一下一大堆。我要强调的是,无论上面三个设备中的任何一种设备,要想在linux实现它的设备驱动,首先要对它进行一系列的初始化工作,然后需给它提供一个设备操作集合(或者更简单一点理解:接口函数),用来提供给我们的上层程序进行访问,比如open,read,write等等。要不然,我要你这个设备驱动干嘛。在字符设备驱动中,我们的操作集函数是这样的

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设备呢?请看下一节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值