今天做了一个比较重大的决定,因为导师的需要开始研究Understanding Linux Network Internals.但是是全英文的。拿着金山词霸一边翻译一边看感觉还不错,但是想看第二遍的时候就不好找了,而且还是电子书。所以,我决定,翻译他。也算是对自己的一个要求吧。
然后写研究了下Linux的驱动编程,看了下字符设备的编程。下面是在网上找的一个字符设备,这里也做下介绍。作为第一个驱动程序笔记
2.4内核下
#ifndef __KERNEL__
# define __KERNEL__ //按内核模块编译
#endif
//内核头文件中的许多声明仅仅与内核本身有关,不应暴露给用户空间的应用程序,我们用上面这段代码块来保护这些声明
#ifndef MODULE
# define MODULE //设备驱动程序模块编译
#endif
//上面这段代码段必须定义在包含<linux/module.h>之前
#define DEVICE_NAME "MyDev"
#define OPENSPK 1
#define CLOSESPK 2
//必要的头文件
#include <linux/module.h> //同kernel.h,最基本的内核模块头文件
#include <linux/kernel.h> //同module.h,最基本的内核模块头文件
#include <linux/sched.h> //这里包含了进行正确性检查的宏
#include <linux/fs.h> //文件系统所必需的头文件
#include <asm/uaccess.h> //这里包含了内核空间与用户空间进行数据交换时的函数宏
#include <asm/io.h> //I/O访问
int my_major=0; //主设备号,设置为0,如果为0则表示由系统自己分配
static int Device_Open=0; //用来识别该设备是否已经被打开
static char Message[]="This is from device driver";
char *Message_Ptr;
int my_open(struct inode *inode, struct file *file)//内核用inode结构在内部表示文件,该指针指向真正的文件,file只是文件描述符
{ //每当应用程序用open打开设备时,此函数被调用
printk ("ndevice_open(%p,%p)n", inode, file);
if (Device_Open)
return -EBUSY; //同时只能由一个应用程序打开
Device_Open++;
MOD_INC_USE_COUNT; //设备打开期间禁止卸载,模块计数器加一
return 0;
}
static void my_release(struct inode *inode, struct file *file)
{ //每当应用程序用close关闭设备时,此函数被调用
printk ("ndevice_release(%p,%p)n", inode, file);
Device_Open --;
MOD_DEC_USE_COUNT; //引用计数减1
}
ssize_t my_read (struct file *f,char *buf,int size,loff_t off) //buf为用户空间的内存起始地址,size为要读取的大小
{ //每当应用程序用read访问设备时,此函数被调用
int bytes_read=0;
#ifdef DEBUG
printk("nmy_read is called. User buffer is %p,size is %dn",buf,size);
#endif
if (verify_area(VERIFY_WRITE,buf,size)==-EFAULT) //因为buf是用户空间的内存,所以要用verify_area来确定该内存是否可用
return -EFAULT;
Message_Ptr=Message;
while(size && *Message_Ptr)
{
if(put_user(*(Message_Ptr++),buf++)) //写数据到用户空间,如果返回的不是0,则调用错误
return -EINVAL;
size --;
bytes_read++;
}
return bytes_read;
}
ssize_t my_write (struct file *f,const char *buf, int size,loff_t off) //buf为用户空间的内存起始地址,size为要读取的大小
{ //每当应用程序用write访问设备时,此函数被调用
int i;
unsigned char uc;
#ifdef DEBUG
printk("nmy_write is called. User buffer is %p,size is %dn",buf,size);
#endif
if (verify_area(VERIFY_WRITE,buf,size)==-EFAULT)
return -EFAULT;
printk("nData below is from user program:n");
for (i=0;i<size;i++)
if(!get_user(uc,buf++)) //从用户空间读数据
printk("%02x ",uc);
return size;
}
int my_ioctl(struct inode *inod,struct file *f,unsigned int arg1,
unsigned int arg2)
{ //每当应用程序用ioctl访问设备时,此函数被调用
#ifdef DEBUG
printk("nmy_ioctl is called. Parameter is %p,size is %dn",arg1);
#endif
switch (arg1)
{
case OPENSPK:
printk("nNow,open PC's speaker.n");
outb(inb(0x61)|3,0x61); //打开计算机的扬声器 inb()是将数据写入寄存器, outb()是将数据读出寄存器
break;
case CLOSESPK:
printk("nNow,close PC's speaker.");
outb(inb(0x61)&0xfc,0x61); //关闭计算机的扬声器
break;
}
}
struct file_operations my_fops = {
NULL, /* lseek */
my_read,
my_write,
NULL,
NULL,
my_ioctl,
NULL,
my_open,
my_release,
/* nothing more, fill with NULLs */
};
int init_module(void)
{ //每当装配设备驱动程序时,系统自动调用此函数,但使用 insmod 时系统自动调用该函数
int result;
result = register_chrdev(my_major,DEVICE_NAME,&my_fops); //注册该字符设备,返回注册的主设备号
if (result < 0) return result; //如果返回结果小于0,则表示主设备好没有分配成功
if (my_major == 0)
my_major = result; //将系统分配的设备号赋给该模块的主设备号
printk("nRegister Ok. major-number=%dn",result);
return 0;
}
void cleanup_module(void)
{ //每当卸载设备驱动程序时,系统自动调用此函数
printk("nunloadn");
unregister_chrdev(my_major, DEVICE_NAME);
}