linux驱动写字符文件,自己写字符驱动示例linux2.4

1.最近研究了下字符驱动,现在将过程记录下来。

2.首先应该搞明白你要再那个内核下编写驱动,2.4x or 2.6x ???我现在是2.4编写驱动,然后了解结构file_operations,这个结构在linux/fs.h中定义。

struct file_operations {

struct module *owner;

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

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

ssize_t (*write) (struct file *, const char *, size_t, 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);

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

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

int (*flush) (struct file *);

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

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

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

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

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,

loff_t *);

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,

loff_t *);

};上面的结构来自于内核2.4.2,这个结构的作用是把你的设备执行相应操作的函数指针对应起来,比如你要执行read操作,你就应该有一个read函数;

后面会看到例子里面;

然后是给file_operations结构赋值,这里有两种不同的写法分别在2.4与2.6内核里;

2.4里面比较老的方法如下:

struct file_operations fops = {

read: device_read,

write: device_write,

open: device_open,

release: device_release

};2.6里面的赋值如下:

struct file_operations fops = {

.read = device_read,

.write = device_write,

.open = device_open,

.release = device_release

};如果你没有初始化结构成员的值,那么它会被gcc设置为NULL。

3.接下来我们需要注册一个设备;

字符设备是被当作为一个设备文件来操作的,这个设备文件在/dev目录下;主设备号告诉驱动应该和那个设备文件关联,副设备号是为了告诉驱动应该去操作

哪个具体的设备文件,当这个驱动与多个设备文件关联的时候;

int register_chrdev(unsigned int major, const char *name,

struct file_operations *fops);使用上面的函数注册一个设备,注册一个设备到系统,我们不需要传递副设备号给系统;

major主设备号是你请求的设备号,name 是你的设备名称,这个设备名称会出现在/proc/devices里面, fops为相应的file_operations结构指针;

如果major为0,系统会动态分配一个主设备号给我们,如果动态生成,我们还需要将设备文件与主设备号关联;

这里提供三种方法进行配对:

a.动态分配后,由驱动打印出主设备号,然后我们通过mknod手动创建设备文件;

b.动态分配后,通过自动脚本读取/proc/devices里面的设备号建立设备文件;

c.动态分配后,直接在驱动里面调用mknod命令创建设备文件;

4.卸载设备;

当你已经完成了字符设备的所有操作函数与注册设备后,你需要知道如何卸载一个设备;

在卸载设备前我们来认识下几个宏:这些宏定义在linux/modules.h里面

MOD_INC_USE_COUNT: 递增使用计数器

MOD_DEC_USE_COUNT: 递减使用计数器

MOD_IN_USE: 显示使用计数器 以上的计数器是用来记录有多少人在使用这个驱动,如果计数器的值不为0,你将无法卸载这个驱动;

unregister_chrdev(Major, DEVICE_NAME); 此函数用来卸载一个设备;

5.下面我们来看下例子代码

#include #include #include #include #include #include #include //#include #include #include #include #define BUFFERSIZE 200

#define DEVICE_MAJOR 250

#define DEVICE_NAME "mycdev"

#define SUCCESS 0

static int device_major = DEVICE_MAJOR;

static char msg[BUFFERSIZE];

static char *msg_Ptr;

static int Device_Open = 0;

static int init_module(void);

static void cleanup_module(void);

static int my_cdev_open(struct inode *, struct file *);

static int my_cdev_release(struct inode *, struct file *);

static size_t my_cdev_read(struct file *, char *, size_t, loff_t *);

static size_t my_cdev_write(struct file *, const char *, size_t, loff_t *);

static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig);

static const struct file_operations my_cdev_fops=

{

.owner = THIS_MODULE,

.open = my_cdev_open,

.release = my_cdev_release,

.read = my_cdev_read,

.write = my_cdev_write,

.llseek = my_cdev_llseek,

};

static int my_cdev_open(struct inode *node, struct file *filp)

{

static int counter = 0;

if(Device_Open) return -EBUSY;

Device_Open++;

sprintf(msg, "I already told you %d times Hello world!\n", counter++);

msg_Ptr = msg;

MOD_INC_USE_COUNT;

return 0;

}

static int my_cdev_release(struct inode* node, struct file* filp)

{

Device_Open--;

MOD_DEC_USE_COUNT;

return 0;

}

static size_t my_cdev_read(struct file *filp, char *buf, size_t size, loff_t *ppos)

{

unsigned long p = *ppos;

unsigned int count = size;

int ret = 0;

if(*msg_Ptr == 0)

return 0;

if(p >= BUFFERSIZE)

return count ? -ENXIO : 0;

if(copy_to_user(buf, (void*)(msg_Ptr+p), count))

{

ret = -EFAULT;

}else{

*ppos += count;

ret = count;

}

return ret;

}

static size_t my_cdev_write(struct file *filp, const char *buf, size_t size, loff_t *ppos)

{

unsigned long p = *ppos;

unsigned int count = size;

int ret = 0;

if(p >= BUFFERSIZE)

return count ? -ENXIO : 0;

if(count > BUFFERSIZE - p)

count = BUFFERSIZE -p;

if(copy_from_user(msg_Ptr, buf, count))

{

ret = -EFAULT;

}else{

*ppos += count;

ret = count;

}

return ret;

}

static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig)

{

loff_t ret = 0;

switch(orig)

{

case 0:

if(offset < 0)

{

ret = -EINVAL;

break;

}

if(offset > BUFFERSIZE)

{

ret = -EINVAL;

break;

}

filp->f_pos = (unsigned int)offset;

ret = filp->f_pos;

break;

default:

ret = -EINVAL;

break;

}

return ret;

}

/*

static void my_cdev_setup(struct my_cdev *dev, int index)

{

int err;

dev_t devno = MKDEV(DEVICE_MAJOR, index);

cdev_init(&dev->cdev, &my_cdev_fops);

dev->cdev.owner = THIS_MODULE;

dev->cdev.ops = &my_cdev_fops;

err = cdev_add(&dev->cdev, devno, 1);

if(err)

printk(KERN_NOTICE "Error %d adding LED%d", err, index);

}

*/

static int init_module(void)

{

device_major = register_chrdev(0, DEVICE_NAME, &my_cdev_fops);

if(device_major < 0)

{

printk("Register the character device failed with %d\n", device_major);

return device_major;

}

printk("<1>I was assigned major number %d.  To talk to\n", device_major);

printk("<1>the driver, create a dev file with\n");

printk("'mknod /dev/mycdev c %d 0'.\n", device_major);

printk("<1>Try various minor numbers.  Try to cat and echo to\n");

printk("the device file.\n");

printk("<1>Remove the device file and module when done.\n");

return 0;

}

static void cleanup_module(void)

{

int ret = unregister_chrdev(device_major, DEVICE_NAME);

if(ret < 0)

printk("Error in unregister_chrdev: %d\n", ret);

}

MODULE_AUTHOR("Robert Luo");

MODULE_LICENSE("Dual BSD/GPL");

请在你自己的系统上找到相应的编译器进行编译后,编译为模块加载形式,加载;

然后根据提示建立一个设备文件mknod /dev/mycdev c 254 0

最后我们写一段测试代码来看看驱动是否可以用了;

#include #include #include #include #define BUFFERSIZE 200

int main(void)

{

int fp =0;

char str[BUFFERSIZE];

fp = open("/dev/mycdev", O_RDWR);

if(!fp)

{

printf("Open device failed.\n");

return -1;

}

write(fp, "Hello, my devices", strlen("Hello, my devices"));

lseek(fp, 0, 0 );

read(fp, str, BUFFERSIZE);

printf("Read content: %s\n", str);

close(fp);

}

编译后运行,你应该会看到类似的显示,没允许一次提示的数字都会变,你发现这段显示是不是不对? 呵呵,自己看看原因吧。我懒得改了。

Read content: Hello, my devicesu 0 times Hello world!

Read content: Hello, my devicesu 1 times Hello world!

Read content: Hello, my devicesu 2 times Hello world!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值