设备节点被创建在/dev下,是连接内核与用户层的枢纽,用户层通过open等操作实现对驱动的交互(具体通过设备号与对应驱动的设备建立联系)。
Linux下的设备通常分为三类,字符设备,块设备和网络设备。我们常用的主要是字符设备.
查看设备号:
cat /proc/devices
查看杂项设备:
cat /proc/misc
生成字符设备有两种方法:
1.注册杂项设备
注册函数:
int misc_register(struct miscdevice * misc);
注销函数:
int misc_deregister(struct miscdevice *misc);
杂项设备结构体:
struct miscdevice {
int minor;//次设备号(主设备号为10),赋MISC_DYNAMIC_MINOR自动分配
const char *name;//设备节点名
const struct file_operations *fops;//用于建立与应用层操作联系的方法结构体
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
关于file_operations结构体比较重要的成员:
struct module *owner;//THIS_MODULE
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//应用层read时对应的驱动动作
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//应用write时对应的驱动动作
int (*open) (struct inode *, struct file *);//应用open设备节点时驱动中的动作
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);//用于异步通知
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
杂项设备示例:
/***********驱动*********/
#include<linux/init.h>
#include<linux/module.h>
/*The header of driver register,include struct ,register function and unregister function about driver*/
#include<linux/platform_device.h>
/*incude header about miscdevice register*/
#include<linux/miscdevice.h>
/*include file struct about device noderegister*/
#include<linux/fs.h>
#define DRIVER_NAME "hello_ctl"
#define DEVICE_NAME "hello_ctl1"
MODULE_LICENSE("Dual BSD/GPL");
//MODULE_AUTHOR("ltx");
static int hello_open(struct inode *indoe,struct file *file)
{
printk(KERN_EMERG"hello open\n");
return 0;
}
static int hello_release(struct inode *inode,struct file *file)
{
printk(KERN_EMERG"hello release\n");
return 0;
}
static long hello_ctl(struct file *f,unsigned int cmd,unsigned long arg)
{
printk(KERN_EMERG"cmd is %d,arg is %ld\n",cmd,arg);
return 0;
}
static struct file_operations hello_ops = {
.owner=THIS_MODULE,
.open=hello_open,
.release=hello_release,
.unlocked_ioctl=hello_ctl,
};
static struct miscdevice misc_device = {
.minor=MISC_DYNAMIC_MINOR,
.name=DEVICE_NAME,
.fops=&hello_ops,
};
static int hello_probe(struct platform_device *p)
{
printk(KERN_EMERG "\tinitial\n");
misc_register(&misc_device);
return 0;
}
static int hello_remove(struct platform_device *p)
{
printk(KERN_EMERG "\tremove\n");
misc_deregister(&misc_device);
return 0;
}
static void hello_shutdown(struct platform_device *p)
{
;
}
static int hello_suspend(struct platform_device *p,pm_message_t state)
{
return 0;
}
static int hello_resume(struct platform_device *p)
{
return 0;
}
static struct platform_driver hello_driver={
.probe=hello_probe,
.remove=hello_remove,
.shutdown=hello_shutdown,
.suspend=hello_suspend,
.resume=hello_resume,
.driver={
.name=DRIVER_NAME,
.owner=THIS_MODULE,
}
};
static int hello_init(void)
{
int driver_state;
printk(KERN_EMERG "hello world enter!\n");
driver_state=platform_driver_register(&hello_driver);
printk(KERN_EMERG "\tdriver state is %d\n",driver_state);
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "hello world exit!\n");
platform_driver_unregister(&hello_driver);
}
//module enter
module_init(hello_init);
//module exit
module_exit(hello_exit);
/*************************************************************************************/
/*************测试应用*********/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/ioctl.h>
int main()
{
int fd=-1;
unsigned int cmd=1;//usually cmd is 0 or 1
unsigned long arg=6;
char *file_node="/dev/hello_ctl1";
fd=open(file_node,O_RDWR|O_NDELAY);
if(fd<0)
{
printf("%s open failed\n",file_node);
}
else
{
printf("%s open success\n",file_node);
if(ioctl(fd,cmd,arg)<0)
printf("ioctl error!\n");
}
close(fd);
return 0;
}
2.注册字符设备与生成设备节点
1.字符类设备号
dev_t 高12位为主设备号,低20位为次设备号
创建设备号:
dev_t dev = MKDEV(major,minor)
提取:
MAJOR(dev)MINOR(dev)
2.设备号注册与注销
注册连续多个不同主设备号的设备(次设备号为0):
int register_chrdev_region(dev_t from, unsigned count, const char *name);
/*
from: the first in the desired range of device numbers;
count: the number of consecutive device numbers required
name: the name of the device or driver.
传入指定主设备号。
如果申请的设备编号范围跨越了主设备号,它会把分配范围内的编号按主设备号分割成较小的子范围,并在每个子范围上调用 __register_chrdev_region() 。如果其中有一次分配失败的话,那会把之前成功分配的都全部退回。
*/
注册同一主设备号不同次设备号的设备:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
/*
dev: output parameter for first assigned number
baseminor: first of the requested range of minor
count: the number of minor numbers required
name: the name of the associated device or driver
通过传出参数获得动态分配的设备号
*/
注销:
void unregister_chrdev_region(dev_t from, unsigned count);
3.字符类设备注册
字符类结构体:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
a.初始化cdev中成员(同时初始化file_operations结构体)
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
(常用先声明cdev结构体指针,再由kmalloc分配内存,kmalloc用于小内存分配,最大为128k,kmalloc(size,GFP_KERNEL))
然后手动初始化成员owner。
b.注册设备
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
注销:void cdev_del(struct cdev *p);
4.生成设备节点(两种方式)
a.内核函数生成
//先创建class结构体
struct class *class_create(struct module *owner,const char *name);
//class_create本身为一个宏定义实质调用__class_create。
对应void class_destroy(struct class *cls);
//创建设备节点注册到文件系统
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
//调用device_create(class,NULL,设备号,NULL,设备名)设备名可以为“name.%d"类似printf
对应注销void device_destroy(struct class *class, dev_t devt);
b.mknod命令
mknod 设备名 设备类型(字符:c,块:b) 主设备号 从设备号
字符设备示例:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/stat.h>
#include<linux/fs.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
/*include kmalloc*/
#include<linux/slab.h>
/*include class and device create*/
#include<linux/device.h>
/*some header about gpio*/
#include<linux/gpio.h>
#include<plat/gpio-cfg.h>
#include<mach/gpio.h>
#include<mach/gpio-exynos4.h>
#define DEV_NAME "myled"
#define DEV_MAJOR 10
#define DEV_MINOR 0
#define DEV_MINOR_NUM 2
#define DEV_BUFF_SIZE 4096
static int led_gpios[]={
EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),
};
#define LED_NUM ARRAY_SIZE(led_gpios)
MODULE_LICENSE("Dual BSD/GPL");
//MODULE_AUTHOR("ltx");
int devnum_major=DEV_MAJOR;
int devnum_minor=DEV_MINOR;
//afferent parameter
module_param(devnum_major,int,S_IRUSR);
module_param(devnum_minor,int,S_IRUSR);
/*device struct is used as buff and describle*/
struct dev_str
{
char *buff;
unsigned long size;//buff size
struct cdev cdev;
};
struct dev_str *dev;
static int led_open(struct inode *inode,struct file *file)
{
printk(KERN_EMERG "led_node open!\n");
return 0;
}
static int led_release(struct inode *inode,struct file *fie)
{
int i;
/*release the gpio requested when close app*/
for(i=0;i<LED_NUM;i++)
gpio_free(led_gpios[i]);
printk(KERN_EMERG "led_node close!\n");
return 0;
}
static long led_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
if((arg>LED_NUM-1)|(cmd>LED_NUM-1))
{
printk(KERN_EMERG "cmd is 0 or 1,and arg is 0 or 1");
return -EINVAL;
}
gpio_set_value(led_gpios[arg],cmd);
return 0;
}
static ssize_t led_read(struct file *file,char __user *buf,size_t count,loff_t *f_ops)
{
return 0;
}
static ssize_t led_write(struct file *file,const char __user *buf,size_t count,loff_t *f_ops)
{
return 0;
}
static loff_t led_llseek(struct file *file,loff_t offset,int ence)
{
return 0;
}
struct file_operations my_ops={
.owner=THIS_MODULE,
.open=led_open,
.release=led_release,
.unlocked_ioctl=led_ioctl,
.read=led_read,
.write=led_write,
.llseek=led_llseek,
};
static struct class *myclass;
/*fill cdev struct and add device into system*/
static void cdev_register(struct dev_str *dev,int index)
{
int ret=0;
dev_t devnum=MKDEV(devnum_major,devnum_minor+index);
cdev_init(&(dev->cdev),&my_ops);
dev->cdev.owner=THIS_MODULE;
//changing different ops can realize different function in different child device
dev->cdev.ops=&my_ops;
/*register into system*/
ret=cdev_add(&(dev->cdev),devnum,1);
if(ret)
{
printk(KERN_EMERG "cdev_add add %d failed with error %d!\n",index,ret);
}
else
{
printk(KERN_EMERG "cdev_add add %d successfully!\n",index);
}
}
/*gpio initial*/
static int gpio_init(void)
{
int i,ret;
for(i=0;i<LED_NUM;i++)
{
ret=gpio_request(led_gpios[i],"LED");
if(ret)
{
printk(KERN_EMERG "%d gpio request failed!\n",led_gpios[i]);
return -1;
}
else
{
s3c_gpio_cfgpin(led_gpios[i],S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i],0);
}
}
return 0;
}
/*module enter function*/
static int chdev_init(void)
{
int ret=0,i=0;
dev_t devnum;
devnum=MKDEV(devnum_major,devnum_minor);
printk(KERN_EMERG "char device number register enter!\n");
ret=register_chrdev_region(devnum,DEV_MINOR_NUM,DEV_NAME);
if(ret==0)
{
printk(KERN_EMERG "The major device number requested is %d\n",devnum_major);
}
else
{
alloc_chrdev_region(&devnum,devnum_minor,DEV_MINOR_NUM,DEV_NAME);
devnum_major=MAJOR(devnum);
devnum_minor=MINOR(devnum);
printk(KERN_EMERG"The major device number requested is %d \n",devnum_major);
}
myclass=class_create(THIS_MODULE,DEV_NAME);
/*request space for struct dev_str*/
dev=kmalloc(DEV_MINOR_NUM*sizeof(struct dev_str),GFP_KERNEL);
if(!dev)
{
ret=-ENOMEM;
goto fail;
}
memset(dev,0,DEV_MINOR_NUM*sizeof(struct dev_str));
/*device initial*/
for(i=0;i<DEV_MINOR_NUM;i++)
{
/*request space for the devce buffer*/
dev[i].buff=kmalloc(DEV_BUFF_SIZE,GFP_KERNEL);
memset(dev[i].buff,0,DEV_BUFF_SIZE);
/*register the device into system*/
cdev_register(&dev[i],i);
/*create device node*/
device_create(myclass,NULL,MKDEV(devnum_major,devnum_minor+i),NULL,DEV_NAME"%d",i);
}
/*init gpio*/
ret=gpio_init();
if(ret)
{
printk(KERN_EMERG "gpio init failed!\n");
}
return 0;
fail:
/*unregister the device number*/
unregister_chrdev_region(MKDEV(devnum_major,devnum_minor),DEV_MINOR_NUM);
printk(KERN_EMERG "kmalloc failed!\n");
return ret;
}
/*module exit function*/
static void chdev_exit(void)
{
int i;
for(i=0;i<DEV_MINOR_NUM;i++)
{
cdev_del(&(dev[i].cdev));
device_destroy(myclass,MKDEV(devnum_major,devnum_minor+i));
kfree(dev[i].buff);
}
class_destroy(myclass);
kfree(dev);
unregister_chrdev_region(MKDEV(devnum_major,devnum_minor),DEV_MINOR_NUM);
printk(KERN_EMERG "char deivce number register exit!\n");
}
//module enter
module_init(chdev_init);
//module exit
module_exit(chdev_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 fd=0,count=4;
/*myled0 is same as myled1*/
char *led_node="/dev/myled0";
fd=open(led_node,O_RDWR|O_NDELAY);
if (fd<0)
{
printf("open %s failed!\n",led_node);
return -1;
}
else
{
//ioctl(fd,unsigned int cmd,unsigned long arg);
ioctl(fd,1,0);
while(count--)
{
sleep(1);
ioctl(fd,0,0);
ioctl(fd,1,1);
sleep(1);
ioctl(fd,0,1);
ioctl(fd,1,0);
}
}
close(fd);
return 0;
}