linux2.6字符设备驱动编程第一例:globalmem

view plaincopy to clipboardprint?
/*====================================================================== 
    A globalmem driver as an example of char device drivers   
    This example is to introduce how to use locks to avoid race conditions 
     
    The initial developer of the original code is Baohua Song 
    <author@linuxdriver.cn>. All Rights Reserved. 
======================================================================*/
#include <linux/module.h> 
#include <linux/types.h> 
#include <linux/fs.h> 
#include <linux/errno.h> 
#include <linux/mm.h> 
#include <linux/sched.h> 
#include <linux/init.h> 
#include <linux/cdev.h> 
#include <asm/io.h> 
#include <asm/system.h> 
#include <asm/uaccess.h> 
 
#define GLOBALMEM_SIZE  0x1000  /*全局内存最大4K字节*/ 
#define MEM_CLEAR 0x1  /*清0全局内存*/ 
#define GLOBALMEM_MAJOR 254    /*预设的globalmem的主设备号*/  
 
static int globalmem_major = GLOBALMEM_MAJOR;  
/*globalmem设备结构体*/ 
struct globalmem_dev                                       
{                                                          
  struct cdev cdev; /*cdev结构体*/                         
  unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/       
  struct semaphore sem; /*并发控制用的信号量*/      
};  
 
struct globalmem_dev *globalmem_devp; /*设备结构体指针*/ 
 
/*文件打开函数*/ 
int globalmem_open(struct inode *inode, struct file *filp)  
{  
  /*将设备结构体指针赋值给文件私有数据指针*/ 
  filp->private_data = globalmem_devp;  
  return 0;  
}  
 
/*文件释放函数*/ 
int globalmem_release(struct inode *inode, struct file *filp)  
{  
  return 0;  
}  
 
/* ioctl设备控制函数 */ 
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned  
  int cmd, unsigned long arg)  
{  
  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/ 
 
  switch (cmd)  
  {  
    case MEM_CLEAR:  
      if (down_interruptible(&dev->sem))  
      {  
        return  - ERESTARTSYS;  
      }  
      memset(dev->mem, 0, GLOBALMEM_SIZE);  
      up(&dev->sem); //释放信号量  
 
      printk(KERN_INFO "globalmem is set to zero/n");  
      break;  
 
    default:  
      return  - EINVAL;  
  }  
  return 0;  
}  
 
/*读函数*/ 
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,  
  loff_t *ppos)  
{  
  unsigned long p =  *ppos;  
  unsigned int count = size;  
  int ret = 0;  
  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/ 
 
  /*分析和获取有效的写长度*/ 
  if (p >= GLOBALMEM_SIZE)  
    return count ?  - ENXIO: 0;  
  if (count > GLOBALMEM_SIZE - p)  
    count = GLOBALMEM_SIZE - p;  
 
  if (down_interruptible(&dev->sem))  
  {  
    return  - ERESTARTSYS;  
  }  
 
  /*内核空间->用户空间*/ 
  if (copy_to_user(buf, (void*)(dev->mem + p), count))  
  {  
    ret =  - EFAULT;  
  }  
  else 
  {  
    *ppos += count;  
    ret = count;  
 
    printk(KERN_INFO "read %d bytes(s) from %d/n", count, p);  
  }  
  up(&dev->sem); //释放信号量  
 
  return ret;  
}  
 
/*写函数*/ 
static ssize_t globalmem_write(struct file *filp, const char __user *buf,  
  size_t size, loff_t *ppos)  
{  
  unsigned long p =  *ppos;  
  unsigned int count = size;  
  int ret = 0;  
  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/ 
 
  /*分析和获取有效的写长度*/ 
  if (p >= GLOBALMEM_SIZE)  
    return count ?  - ENXIO: 0;  
  if (count > GLOBALMEM_SIZE - p)  
    count = GLOBALMEM_SIZE - p;  
 
  if (down_interruptible(&dev->sem))//获得信号量  
  {  
    return  - ERESTARTSYS;  
  }  
  /*用户空间->内核空间*/ 
  if (copy_from_user(dev->mem + p, buf, count))  
    ret =  - EFAULT;  
  else 
  {  
    *ppos += count;  
    ret = count;  
 
    printk(KERN_INFO "written %d bytes(s) from %d/n", count, p);  
  }  
  up(&dev->sem); //释放信号量  
  return ret;  
}  
 
/* seek文件定位函数 */ 
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)  
{  
  loff_t ret = 0;  
  switch (orig)  
  {  
    case 0:   /*相对文件开始位置偏移*/ 
      if (offset < 0)  
      {  
        ret =  - EINVAL;  
        break;  
      }  
      if ((unsigned int)offset > GLOBALMEM_SIZE)  
      {  
        ret =  - EINVAL;  
        break;  
      }  
      filp->f_pos = (unsigned int)offset;  
      ret = filp->f_pos;  
      break;  
    case 1:   /*相对文件当前位置偏移*/ 
      if ((filp->f_pos + offset) > GLOBALMEM_SIZE)  
      {  
        ret =  - EINVAL;  
        break;  
      }  
      if ((filp->f_pos + offset) < 0)  
      {  
        ret =  - EINVAL;  
        break;  
      }  
      filp->f_pos += offset;  
      ret = filp->f_pos;  
      break;  
    default:  
      ret =  - EINVAL;  
      break;  
  }  
  return ret;  
}  
 
/*文件操作结构体*/ 
static const struct file_operations globalmem_fops =  
{  
  .owner = THIS_MODULE,  
  .llseek = globalmem_llseek,  
  .read = globalmem_read,  
  .write = globalmem_write,  
  .ioctl = globalmem_ioctl,  
  .open = globalmem_open,  
  .release = globalmem_release,  
};  
 
/*初始化并注册cdev*/ 
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)  
{  
  int err, devno = MKDEV(globalmem_major, index);  
 
  cdev_init(&dev->cdev, &globalmem_fops);  
  dev->cdev.owner = THIS_MODULE;  
  dev->cdev.ops = &globalmem_fops;  
  err = cdev_add(&dev->cdev, devno, 1);  
  if (err)  
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);  
}  
 
/*设备驱动模块加载函数*/ 
int globalmem_init(void)  
{  
  int result;  
  dev_t devno = MKDEV(globalmem_major, 0);  
 
  /* 申请设备号*/ 
  if (globalmem_major)  
    result = register_chrdev_region(devno, 1, "globalmem");  
  else  /* 动态申请设备号 */ 
  {  
    result = alloc_chrdev_region(&devno, 0, 1, "globalmem");  
    globalmem_major = MAJOR(devno);  
  }    
  if (result < 0)  
    return result;  
      
  /* 动态申请设备结构体的内存*/ 
  globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);  
  if (!globalmem_devp)    /*申请失败*/ 
  {  
    result =  - ENOMEM;  
    goto fail_malloc;  
  }  
  memset(globalmem_devp, 0, sizeof(struct globalmem_dev));  
    
  globalmem_setup_cdev(globalmem_devp, 0);  
  init_MUTEX(&globalmem_devp->sem);   /*初始化信号量*/    
  return 0;  
 
  fail_malloc: unregister_chrdev_region(devno, 1);  
  return result;  
}  
 
/*模块卸载函数*/ 
void globalmem_exit(void)  
{  
  cdev_del(&globalmem_devp->cdev);   /*注销cdev*/ 
  kfree(globalmem_devp);     /*释放设备结构体内存*/ 
  unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/ 
}  
 
MODULE_AUTHOR("Song Baohua");  
MODULE_LICENSE("Dual BSD/GPL");  
 
module_param(globalmem_major, int, S_IRUGO);  
 
module_init(globalmem_init);  
module_exit(globalmem_exit); 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以!以下是一个可能的实现,假设你要实现的函数名为 `globalmem_read_data`。 首先,在 `globalmem.c` 中定义函数: ```c ssize_t globalmem_read_data(struct globalmem_dev *dev, char *buf, size_t count, loff_t *ppos) { ssize_t ret = 0; if (mutex_lock_interruptible(&dev->mutex)) return -ERESTARTSYS; if (*ppos >= GLOBALMEM_SIZE) goto out; if (*ppos + count > GLOBALMEM_SIZE) count = GLOBALMEM_SIZE - *ppos; if (!count) goto out; if (copy_to_user(buf, dev->mem + *ppos, count)) { ret = -EFAULT; goto out; } *ppos += count; ret = count; out: mutex_unlock(&dev->mutex); return ret; } ``` 这个函数和 `globalmem_read` 函数很相似,但它只是读取 `dev->mem` 中的数据并将其复制到用户提供的缓冲区中,而不需要处理 `dev->current_len` 和 `dev->mem_end`。因此,这个函数会更简单一些。 然后,你需要在 `globalmem.h` 中声明这个函数: ```c ssize_t globalmem_read_data(struct globalmem_dev *dev, char *buf, size_t count, loff_t *ppos); ``` 最后,你可以写一个测试程序,调用这个函数来读取驱动程序中的数据: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define DEVICE_NAME "/dev/globalmem" #define BUF_SIZE 1024 int main() { int fd; char buf[BUF_SIZE]; ssize_t size; fd = open(DEVICE_NAME, O_RDONLY); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); } size = read(fd, buf, BUF_SIZE); if (size == -1) { perror("read"); close(fd); exit(EXIT_FAILURE); } printf("Read %ld bytes from device:\n%s\n", size, buf); close(fd); exit(EXIT_SUCCESS); } ``` 这个测试程序打开 `/dev/globalmem` 设备文件,调用 `read` 函数来读取数据,并将读取到的数据打印到标准输出中。你可以编译并运行这个测试程序来验证 `globalmem_read_data` 函数的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值