《linux设备驱动开发详解》上的globalmem字符设备驱动程序

#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
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 254
static int globalmem_major = GLOBALMEM_MAJOR;

struct globalmem_dev
{
  struct cdev cdev;
/***************************************
  struct cdev
   {
      struct kobject koj; //内嵌的kobiject对象
      struct module *owner;//所属模块
     struct file_operations *ops;//文件操作结构体
      struct list_head list;
      dev_t dev;//设备号
      unsigned int count;
    };
******************************************/
  unsigned char mem[GLOBALMEM_SIZE];  
};
struct globalmem_dev *globalmem_devp;
int globalmem_open(struct inode *inode,struct file *filep)
{
  filep->private_data=globalmem_devp;
  return 0;
}
int globalmem_release(struct inode *inode,struct file *filep)
{
  return 0;
}
static int globalmem_ioctl(struct inode *inodep,struct file *filep,unsigned int cmd,unsigned long arg)
{
 struct globalmem_dev *dev=filep->private_data;
 switch(cmd)
 {
  case MEM_CLEAR:
    memset(dev->mem,0,GLOBALMEM_SIZE);
    printk(KERN_INFO "globalmem is set to zero");
    break;
  default:
    return -EINVAL;
 }
 return 0;
}
static ssize_t globalmem_read(struct file *filep,char __user *buf,size_t size,loff_t *ppos)
{
  unsigned long p=*ppos;
  unsigned int count=size;
  int ret=0;
  struct globalmem_dev *dev=filep->private_data;
  if(p>=GLOBALMEM_SIZE)        //要读的偏移位置越界
    return count ? - ENXIO:0;
  if(count>GLOBALMEM_SIZE-p)   //要读的字节数太大
	count= GLOBALMEM_SIZE-p;
	/*内核空间—>用户空间:将内核空间中首地址为dev->mem+p的count个字节拷贝到用户空间的首地址为buf的字符串里面去*/
  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",(int)count,(int)p);
  }
  return ret;
}
static ssize_t globalmem_write(struct file *filep,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=filep->private_data;
  if(p>=GLOBALMEM_SIZE)       //要写的偏移位置越界
   return count ? -ENXIO:0;
  if(count>GLOBALMEM_SIZE-p)   //要写的字节数太大
   count=GLOBALMEM_SIZE-p;
   /*用户空间->内核空间:将用户空间中首地址为buf的count个字节拷贝到内核空间首地址为dev-mem+p的存储空间去*/
  if(copy_from_user(dev->mem+p,buf,count))
   ret=-EFAULT;
  else
   {
    *ppos+=count;
    ret=count;
    printk(KERN_INFO "written %d bytes from %d\n",(int)count,(int)p);
   }
  return ret;
}
static loff_t globalmem_llseek(struct file *filep,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;
     }
     filep->f_pos=(unsigned int)offset;
     ret =filep->f_pos;
     break;
   case 1:               /*从当前位置开始偏移*/
     if(((filep->f_pos+offset)>GLOBALMEM_SIZE)||((filep->f_pos+offset)<0))  //偏移越界
     {
       ret=-EINVAL;
       break;
     }
     filep->f_pos+=offset;
     ret=filep->f_pos;
     break;
   default:
     ret=-EINVAL;
     break;
   }
   return ret;
}
/*

通过file_operations方法将设备类型的差异屏蔽了,
这就是linux能够将所有设备都理解为文件的原因。
*/
static const struct file_operations globalmem_fops=
{
  /*指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES*/
  .owner=THIS_MODULE,
   /*llseek用来修改文件当前的读写位置,返回新位置*/
  .llseek=globalmem_llseek,
   /*从设备中同步读取数据。读取成功返回读取的字节数。设置为NULL,调用时返回-EINVAL*/
  .read=globalmem_read,
   /*向设备发送数据*/
  .write=globalmem_write,
   /*提供一种执行设备特殊命令的方法*/
  .ioctl=globalmem_ioctl,
   /*由VFS调用,当VFS打开一个文件,即建立了一个新的"struct file",之后调用open方法分配文件结构。*/
  .open=globalmem_open,
   /*file结构释放时,将调用此指针函数,release与open相同可设置为NULL*/
  .release=globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{
  int err,devno=MKDEV(globalmem_major,index);

  /*初始化cdev,并建立cdev和file_operations之间的连接*/
  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);
}
/*globalmem 设备驱动模块加载函数*/
int globalmem_init(void)
{ int result;
  dev_t devno=MKDEV(globalmem_major,0);
  /*主设备号是globalmem_major,
	从设备号是0,
	通过#define MKDEV(ma,mi) (((ma)<<MINORBITS)|(mi))宏定义生成设备号devno
   */
  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);
	/*
	   kmalloc的特殊之处在于分配的内存在物理地址上是连续的
	 */
  if(!globalmem_devp)
  {
    result = -ENOMEM;
    goto fail_malloc;
  }
  memset(globalmem_devp,0,sizeof(struct globalmem_dev));
  /**/
  globalmem_setup_cdev(globalmem_devp,0);
  /*初始化并添加cdev结构体*/
  return 0;
  fail_malloc:unregister_chrdev_region(devno,1);
  return result;
}
/*globalmem设备驱动模块卸载函数*/
void globalmem_exit(void)
{ 
  /*注销设备,通常发生在驱动模块的卸载函数中*/
  cdev_del(&globalmem_devp->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、付费专栏及课程。

余额充值