第一个Linux字符设备驱动

目的

最近几天学习了基本的字符设备驱动,今天特地写了个字符设备驱动小demo巩固一下。其中也遇到了些许问题,所以就整理了篇日志来记录自己学习的点点滴滴。最初的感悟,刚开始接触字符设备驱动的时候,感觉也好复杂。需要了解register_chrdev_region()、alloc_chrdev_region()、struct file_operations、cdev_init()、cdev_add()、struct cdev、等好多的函数、结构体。硬着头皮写了几天代码,基本的流程也就掌握了。下面贴代码。

代码

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "cortexA9"
#define DEVICE_COUNT 1

static int dev_num;
static int major = 0;
static int minor = 0;

struct chrdev
{
  struct cdev cdev;
  char mem[100];
};

static struct chrdev *cortex_dev;
static struct class *cortex_class;

static ssize_t cortex_open (struct inode *inode, struct file *filp)
{
  struct chrdev *dev;
  
  dev = container_of (inode->i_cdev, struct chrdev, cdev);
  filp->private_data = dev;
  return 0;
}

static ssize_t cortex_release (struct inode *inode, struct file *filp)
{
  return 0;
}

static ssize_t cortex_write (struct file *filp, const char __user *buf, size_t count, loff_t foppt)
{
  struct chrdev *dev;

  dev = filp->private_data;
  if (copy_from_user (dev->mem, buf, count))
    return -EFAULT;
  return count; 
}

static ssize_t cortex_read (struct file *filp, char __user *buf, size_t count, loff_t *foppt)
{
  struct chrdev *dev;

  dev = filp->private_data;
  if (copy_to_user (buf, (void *)dev->mem, count))
    return -EFAULT;
  return count;
}

static struct file_operations f_opt =
{
  .owner = THIS_MODULE, .open = cortex_open,
  .release = cortex_release, .write = cortex_write,
  .read = cortex_read
};

static int cortex_init (void)
{
  int err,ret;
  
  cortex_dev = kmalloc (sizeof(struct chrdev), GFP_KERNEL);
  if (cortex_dev == NULL)
    return -ENOMEM;
  cdev_init (&cortex_dev->cdev, &f_opt);
  cortex_dev->cdev.owner = THIS_MODULE;
  if (major > 0)
  {
    dev_num = MKDEV (major, minor);
    err = register_chrdev_region (dev_num, DEVICE_COUNT, DEVICE_NAME);
  }  
  else
  {
    err = alloc_chrdev_region (&(cortex_dev->cdev.dev), 10, DEVICE_COUNT, DEVICE_NAME);
    dev_num = cortex_dev->cdev.dev;
  }
  if (err < 0)
    return -EFAULT;
  cortex_class = class_create (THIS_MODULE, DEVICE_NAME);
  ret = cdev_add (&cortex_dev->cdev, dev_num, DEVICE_COUNT);
  device_create (cortex_class, NULL,dev_num, NULL,DEVICE_NAME);
  return ret;
}

static void cortex_exit (void)
{
  if (cortex_dev != NULL)
    kfree (cortex_dev);
  class_destroy (cortex_class);
  unregister_chrdev_region (dev_num, DEVICE_COUNT);
}

module_init (cortex_init);
module_exit (cortex_exit);

MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Jack");

分析

代码中红色的两行是后来添上的。下面描述下我遇到的问题,没有添加这两行的时候,能够正常的生成ko文件,insmod模块也是正常的,但是在/dev/下没有生成cortexA9字符设备。去/proc/devices查看也能找到主设备号249 cortexA9。于是很好奇,以前我也没手动创建过设备节点。只好去百度,原来才弄懂原理是这样的。
在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。

内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。

struct class和device_create(…) 以及device_create(…)都定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。

引自 点击打开链接

问题2

当添加了代码之后,在/dev/下生成了cortexA9字符设备,于是我使用下边的测试代码对设备进行读写:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
 
void main (int argc, char **argv)
{
  int fd;
  char *tmp;
  fd = open ("/dev/cortexA9", O_RDWR);
  if (fd < 0)
  {
    printf ("open file error\n");
    exit (1); 
  }
  puts ("hi hello\n");
  write (fd, "echo", 5);
  read (fd, tmp, 5);
  if (strcmp (tmp,"echo") == 0)
    puts("the same string");
  puts (tmp);
}

显示能够正常的打开设备,但是无法对设备进行正常的读写。能够读出几个乱码,对于验证写入读出相等时输出的字符串也迟迟部件身影。当使用cat  /dev/cortexA9查看的时候终端却一直显示个不停,当然了也是显示乱码。这个问题谁遇到过呢?

结论:

原本这些文字问题想做成论坛提问的,但是考虑到篇幅过长可能导致不会有人献计献策。所以写成了博客,另一方面,这个问题我也打算持续解决,知道掌握为止,希望各位提出自己的意见,谢谢。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值