Linux字符设备驱动相关处理

1.Linux字符设备注册相关步骤:

1.1 初始化cdev:

一个 cdev 一般有两种定义初始化方式:静态的和动态的。
静态内存定义初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;

动态内存定义初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;

1.2 注册设备号:

主要涉及到的函数:

int register_chrdev_region(dev_t, unsigned, const char *); //静态的申请和注册设备号 

int  alloc_chrdev_region(dev_t, unsigned, const char *);     //动态的申请注册一个设备号

int register_chrdev(unsigned int, const char *,struct file_operations *);//int为0时候动态注册,非零时候静态注册。
 

静态注册:

1.首先需要定义一个dev_t变量来作为一个设备号,

dev_t   dev_num; 

2.要想注册一个设备则需要一个主设备号。根据主设备号获取设备号。

dev_num=MKDEV(major,minor);  

major是一个表示设备号的主设备号,minor次设备号

3.注册:

register_chrdev_region(dev_num,2,"dev_name");

动态注册:

如果我们提前知道设备的编号,那么就用register_chrdev_region(),但是如果我们不知道呢,我们就使用动态申请设备编号。

1.首先需要定义一个dev_t变量来作为一个设备号,

dev_t   dev_num; 

2.动态分配设备号:

alloc_chrdev_region(&dev_num, minor, 2, "dev_name");

第一个参数保存生成的设备号,第二个参数注册设备的数目,第三个参数表示设备名称。

3.根据设备号获取主设备号:
dev_major = MAJOR(dev_num); 
 

3.向内核增加设备:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

参数1:字符结构体,参数2:设备号,参数3:个数

返回0表示注册成功,不然表示注册失败

4.注销设备

int unregister_chrdev(unsigned int, const char *);   //注销设备号

void unregister_chrdev_region(dev_t, unsigned);   //注销设备号

5.app访问设备

运行代码后发现/dev目录下没有长出设备节点,原因如下:

在2.6内核之前通过函数cdev_init和cdev_add添加字符设备,另外还需要手动创建设备节点;

在2.6之后的内核,通过cdev_init和cdev_add添加字符设备,通过class_create和device_create函数往sys文件系统中添加设备,udev检测到/sys目录的变动会根据变化在/dev目录下创建对应的设备节点。
 

因为在linux里面有两种设备文件系统,devfs和sysfs 
devfs 
1) 需要手动创建设备节点mknod fasync_dev c 250 0 
2) 或者在fasync_dev_init函数中添加: 
#ifdef CONFIG_DEVFS_FS //支持devfs文件系统,在内核里面配置 
devfs_mk_cdev(设备号, S_IFCHR | S_IRUGO | S_IWUSR, 设备名称) 
#endif

sysfs 
sysfs通过class_create和device_create在设备树中创建相应的设备,应用层udev会自动根据设备树的变化生成相应的设备节点。
————————————————
版权声明:本文为CSDN博主「luckywang1103」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luckywang1103/article/details/47860805/

2.示例代码:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
 
int dev1_registers[5];
int dev2_registers[5];
 
struct cdev cdev; 
dev_t devno;
 
/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
    
    /*获取次设备号*/
    int num = MINOR(inode->i_rdev);
    
    if (num==0)
        filp->private_data = dev1_registers;
    else if(num == 1)
        filp->private_data = dev2_registers;
    else
        return -ENODEV;  //无效的次设备号
    
    return 0; 
}
 
/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
  return 0;
}
 
/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
  unsigned long p =  *ppos;
  unsigned int count = size;
  int ret = 0;
  int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/
 
  /*判断读位置是否有效*/
  if (p >= 5*sizeof(int))
    return 0;
  if (count > 5*sizeof(int) - p)
    count = 5*sizeof(int) - p;
 
  /*读数据到用户空间*/
  if (copy_to_user(buf, register_addr+p, count))
  {
    ret = -EFAULT;
  }
  else
  {
    *ppos += count;
    ret = count;
  }
 
  return ret;
}
 
/*写函数*/
static ssize_t mem_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;
  int *register_addr = filp->private_data; /*获取设备的寄存器地址*/
  
  /*分析和获取有效的写长度*/
  if (p >= 5*sizeof(int))
    return 0;
  if (count > 5*sizeof(int) - p)
    count = 5*sizeof(int) - p;
    
  /*从用户空间写入数据*/
  if (copy_from_user(register_addr + p, buf, count))
    ret = -EFAULT;
  else
  {
    *ppos += count;
    ret = count;
  }
 
  return ret;
}
 
/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{ 
    loff_t newpos;
 
    switch(whence) {
      case SEEK_SET: 
        newpos = offset;
        break;
 
      case SEEK_CUR: 
        newpos = filp->f_pos + offset;
        break;
 
      case SEEK_END: 
        newpos = 5*sizeof(int)-1 + offset;
        break;
 
      default: 
        return -EINVAL;
    }
    if ((newpos<0) || (newpos>5*sizeof(int)))
    	return -EINVAL;
    	
    filp->f_pos = newpos;
    return newpos;
 
}
 
/*文件操作结构体*/
static const struct file_operations mem_fops =
{
  .llseek = mem_llseek,
  .read = mem_read,
  .write = mem_write,
  .open = mem_open,
  .release = mem_release,
};
 
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
  /*初始化cdev结构*/
  cdev_init(&cdev, &mem_fops);
  
  /* 注册字符设备 */
  alloc_chrdev_region(&devno, 0, 2, "memdev");
  cdev_add(&cdev, devno, 2);
}
 
/*模块卸载函数*/
static void memdev_exit(void)
{
  cdev_del(&cdev);   /*注销设备*/
  unregister_chrdev_region(devno, 2); /*释放设备号*/
}
 
MODULE_LICENSE("GPL");
 
module_init(memdev_init);
module_exit(memdev_exit);

3.其他说明:

3.1错误的定义初始化方式:

struct cdev *ptr_my_cdev = cdev_alloc();//变量在heap中

cdev_init(ptr_my_cdev , &fops);

ptr_my_cdev ->owner = THIS_MODULE;

原因可以参考:

cdev_alloc和cdev_init 的使用_qinrenzhi的博客-CSDN博客

3.2其它参考

Linux应用程序访问字符设备驱动详细过程解析_coding__madman的博客-CSDN博客_字符设备驱动详细步骤

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值