linux字符设备驱动
字符设备驱动的结构、字符设备驱动与字符设备以及字符设备驱动与用户空间访问该设备的程序之间的关系。
申请字符类设备号
设备号包括主设备号和此设备号,前面的杂项设备的主次设备号已经固定。
ls -l /dev/ 可查询设备号
结果:crw-rw---- 1 root 0 10, 51 Mar 30 07:00 alarm
申请设备号函数:带逗号隔开的数字,前者是主设备号,后者是次设备号。
申请设备号函数:(include/linux/fs.h)
- 静态申请:register_chrdev_region(dev_t *, unsigned, unsigned, const char *) 提前知道设备号,再去申请;
- 动态申请:alloc_chrdev_region(dev_t, unsigned, const char *) 动态分配主次设备号。
设备号必须要一个“dev_t”类型来描述,高12位是主设备号,低20位是次设备号
include/linux/kdev_t.h 有一系列的设备号处理宏命令:
- #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) 合成主设备号和次设备号到一个dev_t类型
- #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) 提取dev_t数据中的主设备号
- #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))提取dev_t数据中的次设备号
静态申请设备号函数:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
静态申请设备号参数:
- 设备号
- 此设备号数量 (填1)
- 设备名称
返回值小于0,申请失败。
动态申请设备号函数:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
动态申请设备号参数:
- 自动分配的设备号
- 次设备号
- 设备号数量
- 设备名称
卸载设备函数:
void unregister_chrdev_region(dev_t from, unsigned count)
注册字符类设备
cdev结构体
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
字符设备初始化函数:
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数:
- 设备描述cdev结构体
- 文件操作file_operations结构体指针
字符设备注册函数:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
参数:
- 设备描述cdev结构体指针
- 设备号
- 设备范围大小 1
卸载设备函数:
void cdev_del(struct cdev *p)
参数:
- 设备描述cdev结构体指针
生成字符类设备节点
设备类:设备中的模型:bus,device,driver,都是有比较明确的定义。bus代表总线,device代表实际的设备和接口,driver代表驱动。• class是设备类,它是一个抽象的概念,没有对应的实体。它是提供给用户接口相似的一类设备的集合。常见的有输入子系统input、usb、串口tty、块设备block等。
设备类创建函数:(include/linux/device.h)
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
参数:
- 一般是THIS_MODULE
- 准备名称
返回值:struct class类型
删除设备类函数:
class_destroy(struct class *cls)
创建设备节点函数:
struct device *device_create(
struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt, ...)
参数:
- 设备所属于的类
- 设备的浮设备,填NULL
- 设备号
- 设备数据,填NULL
- 设备名称
删除设备节点:
void device_destroy(struct class *class, dev_t devt)
参数:
- 所属的类
- 设备号
cat /proc/devices 查看已被注册的主设备
ls /sys/class 查看生成的class
ls /dev 查看生成的设备节点
具体程序
LED2→KP_COL0→GPL2_0
LED3→VDD50_EN→GPK1_1
示例驱动代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-exynos4.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("SZA");
#define CHR_NAME "pocky330"
#define CLS_NAME "pocky_cls"
#define DEV_NAME "pocky_dev"
static dev_t dev; //设备号
static int major_num; //主设备号
static int minor_num; //次设备号
static struct cdev pocky_cdev;
static struct class *pocky_cls;
static unsigned leds[] = {EXYNOS4_GPL2(0),EXYNOS4_GPK1(1)};
static void led_gpio_init()
{
int i;
for(i = 0;i<2;i++)
{
gpio_request(leds[0],"LED2");
s3c_gpio_cfgpin(leds[0],S3C_GPIO_OUTPUT);
gpio_set_value(leds[0],0);
}
}
static int pocky_open(struct inode *inode, struct file *file)
{
printk("pocky_open----\n");
led_gpio_init();
return 0;
}
static int pocky_release(struct inode *inode, struct file *file)
{
printk("pocky_release----\n");
gpio_free(leds[0]);
gpio_free(leds[1]);
return 0;
}
static long pocky_unlocked_ioctl(struct file *file, unsigned int cmd , unsigned long arg)
{
if(cmd == 0 && arg <= 1 && arg >=0)
{
gpio_set_value(leds[0],arg);
}
else if(cmd == 1 && arg <= 1 && arg >=0)
{
gpio_set_value(leds[1],arg);
}
else
printk("para err!!! \n");
return 0;
}
static struct file_operations pocky_fop= {
.owner = THIS_MODULE,
.open = pocky_open,
.release = pocky_release,
.unlocked_ioctl = pocky_unlocked_ioctl,
};
static int char330_init(void)
{
int ret;
ret = alloc_chrdev_region(&dev,minor_num,1,CHR_NAME);
if(ret < 0)
{
printk("alloc_chrdev_region fail!!! \n");
}
cdev_init(&pocky_cdev,&pocky_fop);
cdev_add(&pocky_cdev,dev,1);
pocky_cls = class_create(THIS_MODULE,CLS_NAME);
device_create(pocky_cls,NULL,dev,NULL,DEV_NAME);
return 0;
}
static void char330_exit(void)
{
cdev_del(&pocky_cdev);
device_destroy(pocky_cls,dev);
class_destroy(pocky_cls);
unregister_chrdev_region(dev,1);
}
module_init(char330_init);
module_exit(char330_exit);
示例应用代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc , char **argv){
int fd;
char *path = "/dev/pocky330";
fd = open(path,O_RDWR|O_NDELAY);
if(fd < 0)
{
printf("open led failed!!");
}
else
{
ioctl(fd,atoi(argv[1]),atoi(argv[2]));
}
close(fd);
return 0;
}
ls /sys/class 查看class
ls -l /dev 查看生成的设备节点和主次设备号
cat /proc/devices 查看已被注册的主设备