11.LED驱动程序设计(1)-字符设备控制

来源:互联网 发布:哈萨克所有软件 编辑:程序博客网 时间:2024/05/16 09:00

1.设备控制理论

1.1 作用

大部分驱 动程 序除了需要提供 读写设备 的 能 力 外, 还需要具 备 控制设备的 能 力 。 比如:  改变波特率 

1.2 应用程序接口

在用 户空间 ,使用ioctl 系统调 用来控制设备, 原型如下:
int ioctl(int fd,unsigned long cmd,...)
fd: 要 要 控制的设备 文 件 描 述符
cmd:  发送给 设备的控制 命令
…:  第3个 个 参数是可选 的 参数 , 存 在 与否是依赖于 控制 命令(第 第 2 个 个数 参数 )。

1.3 设备驱动方法

当 应用程 序 使用ioctl 系统调 用 时 , 驱 动程 序 将 由如下函数 来 响 应:
1: 2.6.36  之前 的内 核
long (*ioctl) (struct inode* node ,struct file* filp, unsigned int cmd,unsigned long arg)
2 :2.6.36 之后 的内 核
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg)
参数cmd:  通过 应用 函数ioctl 传递下 来的 命令

2.设备控制实现

2.1 定义命令

命令 从其 实质而言就是一 个 整数,  但为了让这 个 整数具 备 更好 的可读性 ,我们 通常会把这 个 整数分为几 个 段 : 类 型(8位) , 序号 ,参数传送 方向 , 参数 长度 。

? Type(类 类型 型/幻 幻数 数): 表 表 明 这是 属 于 哪 个设备的 命令 。
? Number( 序号) ,用来 区 分 同 一 设备的不 同 命令
? Direction: : 参数传送 的 方向 , 可能 的 值是 是 _IOC_NONE(没 没 有数 据 传输 输), _IOC_READ, _IOC_WRITE (向 设备 写 入 参数 )
? Size: :  参数 长度

Linux 系统提供了下 面的 宏 来 帮助 定义命令:
? _IO(type,nr) :不 带 参数 的 命令
? _IOR(type,nr,datatype) :从设备 中 读参数 的 命令
? _IOW(type,nr,datatype): : 向 设备 写 入 参数 的 命令
例 :
#define MEM_MAGIC ‘m’ // 定义 幻 数
#define MEM_SET _IOW(MEM_MAGIC, 0, int)

2.2 实现设备方法

unlocked_ioctl 函数 的 实现通常是 根据 命令 执行 的 一 个switch 语句 。 但是 , 当命令号 不 能 匹配 任何 一 个设备所支持 的 命令时 , 返回-EINVAL.
编 程 模 型 :
Switch cmd
Case  命令A: :
// 执行A对 对 应的 操 作
Case  命令B: :
// 执行B对 对 应的 操 作
Default:
// return -EINVAL

3.自己动手写驱动

3.1 memdev.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>

#include "memdev.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;
}

long mem_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case MEM_RESTART:
printk("restart device \n");
return 0;
case MEM_SET:
printk("arg is %d\n",arg);
return 0;
default:
return -EINVAL;
}
}

/*文件操作结构体*/
static const struct file_operations mem_fops =
{
  .llseek = mem_llseek,
  .read = mem_read,
  .write = mem_write,
  .open = mem_open,
  .release = mem_release,
  .unlocked_ioctl = mem_ioctl;
};

/*设备驱动模块加载函数*/
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.2 memdev.h

#define MEM_MAGIC 'm'
#define MEM_RESTART _IO(MEM_MAGIC,0)
#define MEM_SET     _IOW(MEM_MAGIC,1,int)

3.3 mem_ctl.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include "memdev.h"

int main()
{
int fd;
fd = open("/dev/memdev0",O_RDWR);
ioctl(fd,MEM_SET,115200);

ioctl(fd,MEM_RESTART);
return 0;
}

253 memdev
mknod /dev/memdev0 c 253 0
0 0
原创粉丝点击