Linux设备驱动开发详解-第6章字符设备驱动(二)-支持2个globalmem

1 支持两个globalmem设备的驱动程序

1.1 globalmem_two.c

上一篇中globalmem仅仅作为使用private_data 的范例,直接访问全局变量globalmem_devp会更加结构清晰。如果globalmem不只包括一个设备,而是同时包括两个或两个以上的设备,采用private_data 的优势就会显现出来。

在不对上一篇中代码的globalmem_read()、globalmem_write()、 globalmem_ioctl()等重要函数及 globalmem_fops结构体等数据结构进行任何修改的前提下,只是简单地修改 globalmem_init()、globalmem_exit()和 globalmem_open(),就可以轻松地让globalmem驱动中包含两个同样的设备(次设备号分别为0和1) ,如下代码所示:

/*======================================================================
    A globalmem driver as an example of char device drivers
    There are two same globalmems in this driver  
    This example is to introduce the function of file->private_data
    
    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 globalmem_major = GLOBALMEM_MAJOR;
/*globalmem设备结构体*/
struct globalmem_dev                                     
{                                                        
  struct cdev cdev; /*cdev结构体*/                       
  unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/        
};

struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
/*文件打开函数*/
int globalmem_open(struct inode *inode, struct file *filp)
{
  /*将设备结构体指针赋值给文件私有数据指针*/
  struct globalmem_dev *dev;
  
  dev = container_of(inode->i_cdev,struct globalmem_dev,cdev);  
  filp->private_data = dev;  
  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:
      memset(dev->mem, 0, GLOBALMEM_SIZE);      
      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 (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);
  }

  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 (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);
  }

  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, 2, "globalmem");
  else  /* 动态申请设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, 2, "globalmem");
    globalmem_major = MAJOR(devno);
  }  
  if (result < 0)
    return result;
    
  /* 动态申请2个设备结构体的内存*/
  globalmem_devp = kmalloc(2*sizeof(struct globalmem_dev), GFP_KERNEL);
  if (!globalmem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(globalmem_devp, 0, 2*sizeof(struct globalmem_dev));
  
  globalmem_setup_cdev(&globalmem_devp[0], 0);
  globalmem_setup_cdev(&globalmem_devp[1], 1);
  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return result;
}

/*模块卸载函数*/
void globalmem_exit(void)
{
  cdev_del(&(globalmem_devp[0].cdev));   
  cdev_del(&(globalmem_devp[1].cdev));   /*注销cdev*/
  kfree(globalmem_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(globalmem_major, 0), 2); /*释放设备号*/
}

MODULE_AUTHOR("Song Baohua");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);
module_exit(globalmem_exit);

container_of()的作用是通过结构体成员的指针找到对应结构体的指针,这个技巧在 Linux 内核编程中十分常用。在container_of(inode->i_cdev,structglobalmem_dev,cdev)语句中,传给container_of()的第1个参数是结构体成员的指针,第2 个参数为整个结构体的类型,第3 个参数为传入的第1 个参数即结构体成员的类型,container_of()返回值为整个结构体的指针。

2 测试应用程序

//gmen_two_test.c
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h>
#include <fcntl.h>
#include <string.h>


#define MEM_CLEAR 0x1  /*清0全局内存*/ 
int main(int argc, char **argv)
{
	int fd0 = 0;
	int fd1 = 0;

	int ret = 0;
	int length = 0;
	char buffer[1024];

	fd0 = open("/dev/globalmem_two0",O_RDWR);	//以读写的方式打开
	if(fd0<0)
	{
		printf("Can not open /dev/leds\n");
		close(fd0);
		return 0;
	}
	
	//写入字符
	memset(buffer, 0, 1024); 
	strcpy(buffer,"test globalmem\n");
	length = strlen(buffer);
	printf("0 写入的字符length = %d, %s", length, buffer);
	ret = lseek(fd0, 0, SEEK_SET);	//定位为相对文件开头0处
	ret = write(fd0, buffer, length);
	
	//读取字符
	memset(buffer, 0, 1024);  
	ret = lseek(fd0, 0, SEEK_SET);	//定位为相对文件开头0处
	ret = read(fd0, buffer, length);
	if(ret>0)
	{
		printf("0 清除内存前读出的字符length = %d, %s", ret, buffer);
	}
	
	//清除字符
	memset(buffer, 0, 1024); 
	ret = lseek(fd0, 0, SEEK_SET);	//定位为相对文件开头0处	
	ret = ioctl(fd0, MEM_CLEAR, 0);
	ret = read(fd0, buffer, length);
	if(ret>0)
	{
		printf("0 清除内存后读出的字符length = %d, %s", ret, buffer);
	}
	
	close(fd0);

	//
	fd1 = open("/dev/globalmem_two1",O_RDWR);	//以读写的方式打开
	if(fd1<0)
	{
		printf("Can not open /dev/leds\n");
		close(fd1);
		return 0;
	}
	
	//写入字符
	memset(buffer, 0, 1024); 
	strcpy(buffer,"test globalmem\n");
	length = strlen(buffer);
	printf("\n\n1 写入的字符length = %d, %s", length, buffer);
	ret = lseek(fd1, 0, SEEK_SET);	//定位为相对文件开头0处
	ret = write(fd1, buffer, length);
	
	//读取字符
	memset(buffer, 0, 1024);  
	ret = lseek(fd1, 0, SEEK_SET);	//定位为相对文件开头0处
	ret = read(fd1, buffer, length);
	if(ret>0)
	{
		printf("1 清除内存前读出的字符length = %d, %s", ret, buffer);
	}
	
	//清除字符
	memset(buffer, 0, 1024); 
	ret = lseek(fd1, 0, SEEK_SET);	//定位为相对文件开头0处	
	ret = ioctl(fd1, MEM_CLEAR, 0);
	ret = read(fd1, buffer, length);
	if(ret>0)
	{
		printf("1 清除内存后读出的字符length = %d, %s", ret, buffer);
	}
	
	close(fd1);
	
	return 0;
}

3 globalmem驱动在用户空间的验证

3.1 编译加载globalmem驱动

[root@localhostglobalmem_two]# make

[root@localhostglobalmem_two]# insmod globalmem_two.ko

3.2 globalmem驱动设备节点

[root@localhostglobalmem_two]# mknod /dev/globalmem_two0 c 247 0

[root@localhostglobalmem_two]# mknod /dev/globalmem_two1 c 247 1

3.3 编译执行测试应用程序

[root@localhostglobalmem_two]# gcc -o gmen_two_test gmen_two_test.c

[root@localhostglobalmem_two]# ./gmen_two_test

0 写入的字符length = 15, test globalmem

0 清除内存前读出的字符length = 15, test globalmem

0 清除内存后读出的字符length = 15,

 

1 写入的字符length = 15, test globalmem

1 清除内存前读出的字符length = 15, test globalmem

1 清除内存后读出的字符length = 15, [root@localhost globalmem_two]#

4 扩展

源码地址: 点击打开链接

5 参考文献

[1] 宋宝华.Linux设备驱动开发详解





注意!此信息未认证,请谨慎判断信息的真实性!

全部评论
空

相关内容推荐

头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像 头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像 头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
点赞 收藏 评论
分享

全站热榜

正在热议