Linux字符设备驱动编写基本流程

   ---简介

  Linux下的MISC简单字符设备驱动虽然使用简单,但却不灵活。

  只能建立主设备号为10的设备文件。字符设备比较容易理解,同时也能够满足大多数简单的硬件设备,字符设备通过文件系          统中的名字来读取。这些名字就是文件系统中的特殊文件或者称为设备文件、文件系统的简单结点,一般位于/dev/目录下          使用ls进行查看会显示以C开头证明这是字符设备文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。第一个数字是主设备                号,第二个数字是次设备号。

  ---分配和释放设备编号

  1)在建立字符设备驱动时首先要获取设备号,为此目的的必要的函数是register_chrdev_region,在linux/fs.h中声明:int                register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想要分配的起始设备编号,first的次编号通            常是0,count是你请求的连续设备编号的总数。count如果太大会溢出到下一个主设备号中。name是设备的名字,他会出          现在/proc/devices 和sysfs中。操作成功返回0,如果失败会返回一个负的错误码。

  2)如果明确知道设备号可用那么上一个方法可行,否则我们可以使用内核动态分配的设备号int alloc_chrdev_region(dev_t            *dev, unsigned int firstminor,unsigned int count, char *name);dev是个只输出的参数,firstminor请求的第一个要用的次            编号,count和name的作用如上1)对于新驱动,最好的方法是进行动态分配

  3)释放设备号,void unregister_chrdev_region(dev_t first unsigned int count);

  ---文件操作file_operations结构体,内部连接了多个设备具体操作函数。该变量内部的函数指针指向驱动程序中的具体操           作,没有对应动作的指针设置为NULL。

  1)fops的第一个成员是struct module *owner 通常都是设置成THIS_MODULE。

  linux/module.h中定义的宏。用来在他的操作还在被使用时阻止模块被卸载。

  2)loff_t (*llseek) (struct file *, loff_t, int);该方法用以改变文件中的当前读/写位置

  返回新位置。

  3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);该函数用以从设备文件

  中读取数据,读取成功返回读取的字节数。

  4)ssize_t (*write) (struct file *, const char __user *,size_t , loff_t *);该函数用以向设备

  写入数据,如果成功返回写入的字节数。

  5)int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);ioctl系统调用提供

  发出设备特定命令的方法。

  6)int (*open) (struct inode *, struct file *);设备文件进行的第一个操作,打开设备文件。

  7)int (*release) (struct inode *, struct file *);释放文件结构函数指针。

  一般初始化该结构体如下:

  struct file_operations fops = {

  .owner = THIS_MODULE, .llseek = xxx_llseek, .read = xxx_read, .write = xxx_write,

  .ioctl = xxx_ioctl, .open = xxx_open, .release = xxx_release };

  PS:以上的文件操作函数指针并不是全部,只是介绍了几个常用的操作。

  ---文件结构

  struct file定义在linux/fs.h中,是设备驱动中第二个最重要的数据结构,此处的file和

  用户空间程序中的FILE指针没有关系。前者位于内核空间,后者位于用户控件。

  文件结构代表一个打开的文件。(他不特定给设备驱动;系统中每个打开的文件

  有一个关联的struct file在内核空间)。它由内核在open时创建,并可以传递给文件件

  操作函数,文件关闭之后,内核释放数据结构。

  1)mode_t f_mode。确定文件读写模式

  2)loff_t f_ops。当前读写位置

  3)unsigned int f_flags 。文件标志,O_RDONLY、O_NONBLOCK,

  4)struct file_operations *f_op。关联文件相关操作

  5)void *private_data。open系统调用设置该指针NULL,指向分配的数据。

  6)struct dentry *f_dentry。关联到文件的目录入口dentry结构。

  ---inode结构

  inode结构由内核在内部用来表示文件。它和代表打开文件描述符的文件结构是不

  同的。inode结构包含大量关于文件的信息。作为通用规则,这个结构只有两个成

  员对驱动代码有作用。

  dev_t i_rdev。对于代表设备文件的节点,这个成员包含实际的设备编号。

  struct cdev *i_cdev。内核内部结构,代表字符设备。

  ---字符设备注册

  在内核调用你的设备操作前,你编写分配并注册一个或几个struct cdev.

  struct cdev *my_cdev = cdev_alloc(); my_cdev->ops = &my_fops;

  或者定义成static均可。

  对定义的cdev变量进行初始化,可以使用专门的函数,或者使用如上的方法。

  cdev_init( my_cdev, &my_fops); 其实上边的两行代码就是做了这个函数的工作。

  最后告诉内核该cdev。

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

  /*上述总结,到此关于设备文件相关的结构数据以及如何注册销毁等操作相关的

  函数基本上都已经介绍完毕。主要的还是要设计具体操作的函数来实现具体的

  逻辑操作*/

  以下代码整理、摘录自《Android深度探索HAL与驱动开发-李宁》LED驱动篇

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #deifne DEVICE_NAME "s3c6410_leds"

  #define DEVICE_COUNT 1

  #define S3C6410_LEDS_MAJOR 0

  #define S3C6410_LEDS_MINOR 234

  #define PARAM_SIZE 3

  static int major = S3C6410_LEDS_MAJOR;

  static int minor = S3C6410_LEDS_MINOR;

  static dev_t dev_number;

  static int leds_state = 1;

  static char *params[] = {"string1","string2","string3"};

  static iint param_size = PARAM_SIZE;

  static struct class *leds_class = NULL;

  static int s3c6410_leds_ioctl (struct file *file, unsigned int cmd, unsigned long arg)

  {

  switch (cmd)

  {

  unsigned tmp;

  case 0:

  case 1:

  if (arg > 4)

  return -EINVAL;

  tmp = ioread32 (S3C64XX_GPMDAT);

  if (cmd == 1)

  tmp &= (~(1 << arg));

  else

  tmp |= (1 << arg);

  iowrite32 (tmp, S3C64XX_GPMDAT);

  return 0;

  default : return -EINVAL;

  }

  }

  static ssize_t s3c6410_leds_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)

  {

  unsigned tmp = count;

  unsigned long i = 0;

  memset(mem, 0, 4);

  if (count > 4)

  tmp = 4;

  if (copy_from_user (mem, buf, tmp) )

  return -EFAULT;

  else{

  for( i=0; i<4; i++)

  {

  tmp = ioread32(S3C64XX_GPMDAT);

  if (mem[i] == '1')

  tmp &= (~(1 << i));

  else

  tmp |= (1 << i);

  iowrite32(tmp, S3C64XX_GPMDAT);

  }

  return count;

  }

  }

  static struct file_operations dev_fops =

  {.owner = THIS_MODULE, .unlocked_ioctl = s3c6410_leds_ioctl, .write = s3c6410_leds_write};

  static struct cdev leds_cdev;

  static int leds_create_device(void)

  {

  int ret = 0;

  int err = 0;

  cdev_init (&leds_cdev, &dev_fops);

  leds_cdev.owner = THIS_MODULE;

  if (major > 0)

  {

  dev_number = MKDEV(major,minor);

  err = register_chrdev_region(dev_number, DEVICE_COUNT, DEVICE_NAME);

  if (err < 0)

  {

  printk(KERN_WANRING "register_chrdev_region errorn");

  return err

  }

  }

  else{

  err = alloc_chrdev_region(&leds_cdev.dev, 10, DEVICE_COUNT, DEVICE_NAME);

  if(err < 0)

  {

  printk (KERN_WARNING "alloc_chrdev_region errorn");

  return err;

  }

  major = MAJOR(leds_cdev.dev);

  major = MINOR(leds_cdev.dev);

  dev_number = leds_cdev.dev;

  }

  ret = cdev_add(&leds_cdev,dev_number, DEVICE_COUNT);

  leds_class = class_create (THIS_MODULE, DEVICE_NAME);

  device_create (leds_class, NULL, dev_number, NULL, DEVICE_NAME);

  return ret;

  }

  static void leds_init_gpm(int leds_default){

  int tmp = 0;

  tmp = ioread32(S3C64XX_GPMCON);

  tmp &= (~0xffff);

  tmp |= 0x1111;

  iowrite32(tmp,S3C64XX_GPMCON);

  tmp = ioread32(S3C64XX_GPMPUD);

  tmp &= (~0XFF);

  tmp |= 0xaa;

  iowrite32(tmp,S3C64XX_GPMPUD);

  tmp = ioread32(S3C64XX_GPMDAT);

  tmp &= (~0xf);

  tmp |= leds_default;

  iowrite32(tmp, S3C64XX_GPMDAT);

  }

  static leds_init( void)

  {

  int ret;

  ret = leds_create_device();

  leds_init_gpm (~leds_state);

  printk(DEVICE_NAME"tinitializedn");

  return ret;

  }

  static void leds_destroy_device(void)

  {

  device_destroy(leds_class, dev_number);

  if(leds_class)

  class_destroy(leds_class);

  unregister_chrdev_region(dev_number, DEVICE_NAME);

  }

  static void leds_exit(void)

  {

  leds_destroy_device();

  printk(DEVICE_NAME"texitn");

  }

  module_init(leds_init);

  module_exit(leds_exit);

  module_param(leds_state, int, S_IRUGO|S_IWUSR);

  module_param_array(params, charp, ?m_size, S_IRUGO|S_IWUSR);

  MODULE_LICENSE("GPL");

  MODULE_AUTHOR("lining");

时间: 2024-10-28 06:50:34

Linux字符设备驱动编写基本流程的相关文章

Linux字符设备驱动编写流程

驱动程序编写基本流程: 1.首先是一些版本信息,没什么用,但是不能少 #define __NO_VERSION__ #include <linux/modules.h> #include <linux/version.h> char kernel_version[] = UTS_RELEASE; 2.为了把系统调用和驱动程序关联起来,需要一个非常关键的数据结构:struct file_operations.file_operations结构的每一个成员的名字都对应着一个系统调用.用

Linux 字符设备驱动框架详细介绍_Linux

Linux 字符设备驱动框架 字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l /dev的时候,就能看到大量的设备文件,c就是字符设备,b就是块设备,网络设备没有对应的设备文件.编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码. 驱动模型 Linux一切皆文件,那么作为一个设备文件,它的操作方

Linux字符设备驱动

1.预备知识: 应用程序.库.内核.驱动程序的关系 应用程序调用应用程序函数库完成功能应用程序以文件形式访问各种资源应用程序函数库部分函数直接完成功能     部分函数通过系统调用由内核完成   内核处理系统调用,调用设备驱动程序   设备驱动直接与硬件通信 设备类型 字符设备     对字符设备发出读/写请求时,实际的硬件I/O操作一般紧接着发生 块设备     块设备与之相反,它利用系统内存作为缓冲区 网络设备     网络设备是一类特殊的设备,它不像字符设备或块设备那样通过对应的设备文件节

linux驱动-linux字符设备驱动求助:设备号无法释放

问题描述 linux字符设备驱动求助:设备号无法释放 我在驱动中,资源释放时调用了unregister_chrdev_region函数,为什么用rmmod卸载驱动模块之后,/proc/devices里边仍能够显示我的驱动设备啊? lsmod中已经没有我写的驱动模块了. 是因为设备号没能正确释放么? 解决方案 当应用程序打开设备节点时,内核调用相应驱动程序的open()函数.可以在shell中执行以下代码来触发cmos_open()的执行: bash> cat /dev/cmos/0 当应用程序关

Linux字符设备驱动框架

字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l /dev的时候,就能看到大量的设备文件,c就是字符设备,b就是块设备,网络设备没有对应的设备文件.编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码. 驱动模型 Linux一切皆文件,那么作为一个设备文件,它的操作方法接口封装在struct fi

Linux字符设备驱动之cdev_init()

1.内核中每个字符设备都对应一个 cdev 结构的变量,下面是它的定义: linux-2.6.22/include/linux/cdev.h struct cdev {  13        struct kobject kobj;  14        struct module *owner;  15        const struct file_operations *ops;  16        struct list_head list;  17        dev_t dev

Linux字符设备驱动之异步通知

学习到这里到这里我们觉得这下这个驱动应该就很完善了吧,但是不仅然,我们是不是想当有按键按下的时候,这个时候再去通知用户空间的read函数来读,这样是不是更方便的都,免得函数也老是在哪里休眠.在这里说下:我是不会讲代码的,这些代码比较简单,这只是一些基础的字符设备驱动驱动,到以后我也会讲一些高级点的驱动.这些代码已经讲得很清楚了,全是本人自己写的,是没有错误的,只是自己写的时候注意下自己的内核版本就可以了,以免有一些函数是不能被发现. 上一篇文章的链接:http://blog.csdn.net/q

linux字符设备驱动开发模板及Makefile

linux2.6字符设备驱动开发模板 #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> //=======================字符设备驱动模板开始 ===========================// #define CHAR_DEV_D

Linux字符设备驱动总结程序(二)

前面我们讲解了字符设备驱动的一些写法,但是那样写出来的程序只能我们自己用或者自己公司用.因为你没有统一接口,别人不知道你的设备接口是什么,现在我们讲解几种常用的设设备模型. 第一:input输入子系统(键盘,鼠标,触摸屏等等) static struct input_dev *s3c_ts_dev;//定义一个 input_dev结构体s3c_ts_dev = input_allocate_device();//分配input_dev结构体input_register_device(s3c_ts