Linux字符设备驱动模版

本文将给出Linux字符设备驱动模版。大家可以根据实际需求修改本模版。

驱动名为hello,内部提供一个简单的buffer,用户可以调用write向设备写入数据,并调用read从设备读出数据。
请注意:1. 若连续调用N次write,buffer只会保留最后一次write的数据。
             2. 每次read都将清空buffer。因此,必须先写write设备,再read设备。

                 3. 目前驱动只能有一个进程访问,允许多个线程访问,并且多个线程共享一个buffer。

下面的驱动代码适用于内核版本2.6.32,未在其他内核版本上经过测试。

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #define DEBUG #include 
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              #define DEVICE_NAME "hello" static unsigned bufsize = 512; module_param(bufsize, uint, S_IRUGO); MODULE_PARM_DESC(bufsize, "data bytes in biggest supported hello buffer"); struct hello_data{ char *name; struct cdev chrdev; struct class *cls; dev_t devt; struct device *dev; atomic_t available; struct mutex mlock; /* Protect @buf */ char *buf; size_t bufsize; size_t actual_len; }; static struct hello_data * hello; static int hello_open(struct inode *inode, struct file *file) { struct hello_data *hello = container_of(inode->i_cdev, struct hello_data, chrdev); /* Only ONE user can open the device. */ if(!atomic_dec_and_test(&hello->available)){ atomic_inc(&hello->available); return -EBUSY; } dev_dbg(hello->dev, "Hello open\n"); if(!strcmp(hello->name, DEVICE_NAME)) dev_dbg(hello->dev, "This is hello\n"); file->private_data = hello; return nonseekable_open(inode, file); } static int hello_close(struct inode *inode, struct file *file) { struct hello_data *hello = file->private_data; dev_dbg(hello->dev, "Hello close\n"); file->private_data = NULL; atomic_inc(&hello->available); return 0; } static int hello_read(struct file *file, char __user *buf, size_t count, loff_t *offp) { struct hello_data *hello = file->private_data; unsigned long ret; u64 len; if(hello->actual_len == 0 || count > hello->actual_len) return -EINVAL; len = min(count, hello->actual_len); mutex_lock(&hello->mlock); ret = copy_to_user(buf, (void *)hello->buf, len); if(ret < 0){ ret = -EFAULT; goto out; } hello->actual_len = 0; ret = len; out: mutex_unlock(&hello->mlock); return ret; } static int hello_write(struct file *file, const char __user *buf, size_t count, loff_t *offp) { struct hello_data *hello = file->private_data; unsigned long ret; u64 len; if(count > hello->bufsize) return -EINVAL; len = min(hello->bufsize, count); mutex_lock(&hello->mlock); ret = copy_from_user(hello->buf, buf, len); if(ret < 0){ ret = -EFAULT; goto out; } hello->actual_len = len; ret = len; out: mutex_unlock(&hello->mlock); return ret; } static struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .release= hello_close, .read = hello_read, .write = hello_write, .llseek = no_llseek, }; static int __init hello_init(void) { int ret; hello = kmalloc(sizeof(struct hello_data), GFP_KERNEL); if(hello == NULL){ printk(KERN_ERR "Unable to malloc struct hello_data\n"); return -ENOMEM; } hello->name = DEVICE_NAME; mutex_init(&hello->mlock); atomic_set(&hello->available, 1); hello->buf = kmalloc(bufsize, GFP_KERNEL); if(hello->buf == NULL){ printk(KERN_ERR "Unable to malloc buf\n"); ret = -ENOMEM; goto err_no_mem; } hello->bufsize = bufsize; printk(KERN_DEBUG "Buffer size: %d \n", hello->bufsize); /* Alloc the device number dynamically which is stored in the first parameter */ ret = alloc_chrdev_region(&hello->devt, 0, 1, DEVICE_NAME); if(ret < 0){ printk("Alloc chrdev region error\n"); goto error_alloc_region; } printk(KERN_DEBUG "Major = %d, minor = %d\n", MAJOR(hello->devt), MINOR(hello->devt)); cdev_init(&hello->chrdev, &hello_fops); hello->chrdev.owner = THIS_MODULE; hello->chrdev.ops = &hello_fops; ret = cdev_add(&hello->chrdev, hello->devt, 1); if(ret < 0){ printk(KERN_ERR "Cdev add error\n"); goto error_cdev_add; } hello->cls = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(hello->cls)){ printk(KERN_ERR "Class create error\n"); ret = -1; goto error_class_create; } hello->dev = device_create(hello->cls, NULL, hello->devt, NULL, DEVICE_NAME); if(IS_ERR(hello->dev)){ printk(KERN_ERR "Create device error\n"); ret = -1; goto error_device_create; } return 0; error_device_create: error_class_create: cdev_del(&hello->chrdev); error_cdev_add: unregister_chrdev_region(hello->devt, 1); error_alloc_region: kfree(hello->buf); err_no_mem: kfree(hello); return ret; } static void __exit hello_exit(void) { dev_dbg(hello->dev, "Hello world exits\n"); cdev_del(&hello->chrdev); unregister_chrdev_region(hello->devt, 1); device_destroy(hello->cls, hello->devt); class_destroy(hello->cls); kfree(hello->buf); kfree(hello); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("yj4231@hotmail.com"); MODULE_DESCRIPTION("Hello template driver"); 
             
            
           
          
         
       
      
      
     
     
    
    
   
   


对应的测试程序如下:
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          #include 
         
           #include 
          
            int main(int argc, char **argv) { int fd, ret; char data[512+1] = "Hello test"; char buf[512+1]; int n; fd = open("/dev/hello", O_RDWR); if(fd < 0){ perror("Open device fail"); return -1; } n = strlen(data); ret = write(fd, data, n); if(ret != n){ printf("Write failed\n"); exit(-1); } printf("Write retval: %d\n", ret); ret = read(fd, buf, n); if(ret != n){ printf("Read failed\n"); exit(-1); } buf[n] = '\0'; printf("Read retval %d, %s\n", ret, buf); sleep(1); close(fd); } 
           
          
        
       
       
      
      
     
     
    
    


History:
2014.02.15  单进程,多线程共享buffer版本(2.6.32)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值