LINUX2.6设备注册与GPIO相关的API
LINUX2.6设备注册与GPIO相关的API
linux2.6 设备注册和杂项设备注册的区别
杂项设备的特点
这种设备注册方法相对来说比较简单
杂项设备的主设备号固定为 10
次设备号 0-255
杂项设备注册成功之后会在/dev 目录下生成设备文件
linux2.6 设备注册的特点
linux2.6 设备注册相对比较复杂
这种设备注册方法比较流行
设备注册完成之后不会生成设备文件
主设备号和次设备号都是没有限制的
主设备号范围
0-2^12
次设备号
0-2^20
linux2.6 设备注册设备号存在的问题
由于主设备号和次设备号都是没有限制的
如果直接的指定主设备号和次设备号会造成
设备号冲突的问题
为了解决设备号存在的问题,在使用设备号的时候
一般不直接指定 而是借助于内核提供的函数来来向内核申请
linux2.6 的设备号的申请
函数的功能
向内核申请一个设备号
函数的头文件
linux/fs.h
函数的原型
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
函数的参数
dev_t *dev, 用来存放申请到的设备号的缓冲区
unsigned baseminor, 次设备号起始的位置
unsigned count, 要申请的设备号的个数
const char *name 设备号的名字 /proc/devices 文件里
函数的返回值
成功返回 0
失败返回 -1
linux2.6 设备注册相关的 API
cdev_init函数的功能
初始化一个 linux2.6 的核心结构体
函数的头文件
linux/cdev.h
函数的原型
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
函数的参数
struct cdev *cdev, linux2.6 的核心结构体
const struct file_operations *fops 操作集合结构体
函数的返回值
无
cdev_add函数的功能
向内核注册一个 linux2.6 的核心结构体
函数的头文件
linux/cdev.h
函数的原型
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
函数的参数
struct cdev *p, linux2.6 的核心结构体指针
dev_t dev, 设备号
unsigned count 要注册的 linux2.6 的设备的个数 1
函数的返回值
成功返回 0
失败返回 -1
cdev_del函数的功能
取消 linux2.6 的设备的注册
函数的头文件
linux/cdev.h
函数的原型
void cdev_del(struct cdev *p)
函数的参数
struct cdev *p:linux2.6 的核心结构体
函数的返回值
无
自动生成设备文件
通过验证可知 linux2.6 设备注册成功以后不会自动的生成设备文件,不能生成设备文件就会导致别的程序无法使用这个驱动,为了解决这个问题,就需要搞定驱动对应的设备文件,生成设备文件的方法主要有两种
手动生成
需要在终端执行命令
在执行命令之前需要了解主设备号和次设备号
执行如下指令
mknod /dev/myled c 234 0
借助于内核提供的 api 来实现自动的生成设备文件
在自动生成设备文件的时候需要使用一个类结构体,这个类结构体申请需要借助于内核提供的函数
class_create函数的功能
创建一个类结构体
函数的头文件
linux/device.h
函数的原型
struct class * class_create(struct module *owner, const char *name)
函数的参数
struct module *owner, 模块
const char *name 类结构体的名字
函数的返回值
成功返回 类结构体的指针
失败返回 NULL
ps:
类结构体是可以复用的
class_destroy函数的功能
销毁类结构体
函数的头文件
linux/device.h
函数的原型
void class_destroy(struct class *cls)
函数的参数
struct class *cls:类结构体的指针
函数的返回值
无
device_create函数的功能
自动生成设备文件
函数的头文件
linux/device.h
函数的原型
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
函数的参数
struct class *class, 类结构体指针
struct device *parent, 父节点的信息 NULL
dev_t devt, 设备号
void *drvdata, 私有数据 NULL
const char *fmt, … 设备名 会出现在/dev 目录下
函数的返回值
成功返回 设备的结构体指针
失败返回 NULL
device_destroy函数的功能
销毁设备文件
函数的头文件
linux/device.h
函数的原型
void device_destroy(struct class *class, dev_t devt)
函数的参数
struct class *class, 类结构体指针
dev_t devt 设备号
函数的返回值
无
linux2.6 设备注册的总结
注册的流程
申请设备号
初始化 linux2.6 的核心结构体
向内核注册一个 linux2.6 的核心结构体
创建类结构体
使用类结构体创建设备文件
取消注册的流程
销毁设备
销毁类结构体
取消 linux2.6 的设备注册
实例
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>dev_t mydev;
struct cdev mycdev;
struct file_operations myfops;
struct class * myclass=NULL;
struct device * mydevice=NULL;int myopen (struct inode *inode, struct file * file)
{printk("打开\n");return 0;
}
int myclose (struct inode *inode, struct file * file)
{printk("关闭\n");return 0;
}static int __init myled_init(void)
{//1申请设备号alloc_chrdev_region(&mydev, 0, 1, "myled");printk("设备号:%d\n",mydev);printk("主设备号:%d\n",(mydev >> 20));printk("次设备号:%d\n",(mydev)&0xFFFFF);//2初始化linux2.6核心结构体myfops.owner = THIS_MODULE;myfops.open = myopen;myfops.release = myclose;cdev_init(&mycdev, &myfops);//3向内核注册linux2.6核心结构体cdev_add(&mycdev, mydev, 1);//创建类结构体myclass=class_create(THIS_MODULE,"myled");//创建设备mydevice=device_create(myclass, NULL,mydev,NULL, "myled");return 0;
}static void __exit myled_exit(void)
{//销毁设备文件device_destroy(myclass, mydev);//取消类结构体class_destroy(myclass);//取消Linux2.6的注册cdev_del(&mycdev);
}module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
应用层
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int fd;
int main()
{while(1){fd=open("/dev/myled",O_RDWR);sleep(1);close(fd);sleep(1);}return 0;
}
gpio 子系统相关的 API
gpio函数
gpio_request函数的功能
申请一个 GPIO 口
函数的头文件
linux/gpio.h
函数的原型
int gpio_request(unsigned gpio, const char *label);
函数的参数
unsigned gpio, gpio 口引脚的编号
const char *label 标签
函数的返回值
成功返回 0
失败返回 -1
gpio_direction_output函数的功能
设置 gpio 口的工作模式为输出
函数的头文件
linux/gpio.h
函数的原型
int gpio_direction_output(unsigned gpio, int value)
函数的参数
unsigned gpio, gpio 口引脚的编号
int value 默认的输出的值 一般是有效电平取反
函数的返回值
成功返回 0
失败返回 -1
gpio_direction_input函数的功能
设置 gpio 口引脚的工作模式为输入
函数的头文件
linux/gpio.h
函数的原型
int gpio_direction_input(unsigned gpio)
函数的参数
unsigned gpio:gpio 口引脚的编号
函数的返回值
成功返回 0
失败返回 -1
gpio_set_value函数的功能
设置 gpio 口引脚的值
函数的头文件
linux/gpio.h
函数的原型
void gpio_set_value(unsigned gpio, int value)
函数的参数
unsigned gpio, gpio 口引脚的编号
int value 要设置的值
函数的返回值
无
gpio_get_value函数的功能
获取 gpio 口引脚的值
函数的头文件
linux/gpio.h
函数的原型
int gpio_get_value(unsigned gpio)
函数的参数
unsigned gpio:gpio 引脚的编号
函数的返回值
成功返回 引脚值
实例
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>dev_t mydev;
struct cdev mycdev;
struct file_operations myfops;
struct class * myclass=NULL;
struct device * mydevice=NULL;int myopen (struct inode *inode, struct file * file)
{
//GPIO1_A4 1*32+A-A +4printk("打开\n");gpio_set_value(21, 1);gpio_set_value(22, 1);gpio_set_value(36, 1);return 0;
}
int myclose (struct inode *inode, struct file * file)
{printk("关闭\n");gpio_set_value(21, 0);gpio_set_value(22, 0);gpio_set_value(36, 0);return 0;
}static int __init myled_init(void)
{gpio_request(21, "led21");gpio_request(22, "led22");gpio_request(36, "beep");gpio_direction_output(21, 0);gpio_direction_output(22, 0);gpio_direction_output(36, 0);//1申请设备号alloc_chrdev_region(&mydev, 0, 1, "myled");printk("设备号:%d\n",mydev);printk("主设备号:%d\n",(mydev >> 20));printk("次设备号:%d\n",(mydev)&0xFFFFF);//2初始化linux2.6核心结构体myfops.owner = THIS_MODULE;myfops.open = myopen;myfops.release = myclose;cdev_init(&mycdev, &myfops);//3向内核注册linux2.6核心结构体cdev_add(&mycdev, mydev, 1);//创建类结构体myclass=class_create(THIS_MODULE,"myled");//创建设备mydevice=device_create(myclass, NULL,mydev,NULL, "myled");return 0;
}static void __exit myled_exit(void)
{//销毁设备文件device_destroy(myclass, mydev);//取消类结构体class_destroy(myclass);//取消Linux2.6的注册cdev_del(&mycdev);
}module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");