这里使用miscdevice设备的方式编写,关键结构体与API
#define MISC_DYNAMIC_MINOR 255
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
extern int misc_register(struct miscdevice *misc);
extern void misc_deregister(struct miscdevice *misc);
注册misc设备:
extern int misc_register(struct miscdevice *misc)
注销misc设备:
extern void misc_deregister(struct miscdevice *misc);
gpiolib的API接口:
static inline void gpio_set_value(unsigned gpio, int value)
static inline int gpio_get_value(unsigned gpio)
static inline int gpio_request(unsigned gpio, const char *label)
static inline int gpio_direction_output(unsigned gpio, int value)
static inline int gpio_direction_input(unsigned gpio)
static inline void gpio_free(unsigned gpio)
混杂设备注册起来比较方便不需要使用复杂的设备和类创建,驱动程序代码:
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/cdev.h"
#include "asm-generic/gpio.h"
#include "linux/gpio.h"
#include "linux/miscdevice.h"
#include "linux/file.h"
#include <asm/uaccess.h>
#define print_ret(ret) printk("line:%d,fun:%s,ret:%d \n",__LINE__,__FUNCTION__,ret)
#define GET_GPIO_VALUE 0x01
#define SET_GPIO_VALUE 0x02
static ssize_t gpio_read(struct file *fp, char __user *buff, size_t size, loff_t *off);
static int gpio_open(struct inode *i, struct file *fp);
static ssize_t gpio_write(struct file *fp, const char __user *buff, size_t size, loff_t *off);
static int gpio_release(struct inode *i, struct file *fp);
static long gpio_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
struct gpio_device{
struct miscdevice *gp;
unsigned gpio;
const char *label;
};
static struct file_operations fp={
.owner = THIS_MODULE,
.open = gpio_open,
.release = gpio_release,
.write = gpio_write,
.read = gpio_read,
.compat_ioctl = gpio_compat_ioctl,
};
static struct miscdevice gpio_md={
.minor = MISC_DYNAMIC_MINOR,
.name = "gpio_device",
.fops = &fp,
};
static struct gpio_device gd={
.gpio = 0xC0,//NUC980_PG0 (0xC0 + 0)
.label = "NUC980_PG0",
.gp = &gpio_md,
};
static ssize_t gpio_read(struct file *fp, char __user *buff, size_t size, loff_t *off)
{
ssize_t ret;
struct gpio_device *g = (struct gpio_device *)fp->private_data;
ret = gpio_get_value(g->gpio);
ret = copy_to_user(buff,&ret,sizeof(ret));
return ret;
}
static int gpio_open(struct inode *i, struct file *fp)
{
int ret=0;
fp->private_data = &gd;
return ret;
}
static ssize_t gpio_write(struct file *fp, const char __user *buff, size_t size, loff_t *off)
{
ssize_t ret=0;
char value[1];
struct gpio_device *g = (struct gpio_device *)fp->private_data;
ret=copy_from_user(value,buff,sizeof(value));
gpio_set_value(g->gpio,value[0]);
return ret;
}
static int gpio_release(struct inode *i, struct file *fp)
{
int ret=0;
return ret;
}
static long gpio_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
long ret=0;
unsigned long *p = (unsigned long *)arg;
struct gpio_device *g = (struct gpio_device *)fp->private_data;
switch (cmd){
case GET_GPIO_VALUE:
ret = gpio_get_value(g->gpio);
*p = ret;
break;
case SET_GPIO_VALUE:
gpio_set_value(g->gpio,*p);
break;
default:
ret = -1;break;
}
return ret;
}
int __init gpio_init(void)
{
int ret;
// printk("%s\n%d,%s exec \n",__FILE__,__LINE__,__FUNCTION__);
ret = gpio_request(gd.gpio,gd.label);
print_ret(ret);
ret = gpio_direction_output(gd.gpio,1);
print_ret(ret);
ret = misc_register(&gpio_md);
print_ret(ret);
return 0;
}
void __exit gpio_clean(void)
{
gpio_free(gd.gpio);
misc_deregister(&gpio_md);
printk("%s\n%d,%s exit \n",__FILE__,__LINE__,__FUNCTION__);
}
module_init(gpio_init);
module_exit(gpio_clean);
MODULE_AUTHOR("klp");
MODULE_DESCRIPTION("ubuntu test");
MODULE_LICENSE("GPL");
MODULE_ALIAS("mod:test");
应用测试代码:
#include "stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char*argv[])
{
printf("%s,%d main app exit \n",__FILE__,__LINE__);
char buffer[1]={0};
int fd = open("/dev/gpio_device",O_RDWR);
while(1){
buffer[0]=1;
write(fd,buffer,1);
sleep(1);
buffer[0]=0;
write(fd,buffer,1);
sleep(1);
}
close(fd);
return 0;
}
也可以使用cdev的方式注册设备只不过相对复杂一些。
需要注意的API:
struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...);
extern void device_destroy(struct class *cls, dev_t devt);
#define class_create(owner, name)
void class_destroy(struct class *cls);
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
int register_chrdev_region(dev_t from, unsigned count, const char *name)
void unregister_chrdev_region(dev_t from, unsigned count)
cdev方式设备注册源码:
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/cdev.h"
#include "asm-generic/gpio.h"
#include "linux/gpio.h"
#include "linux/cdev.h"
#include "linux/file.h"
#include <asm/uaccess.h>
#define print_ret(ret) printk("line:%d,fun:%s,ret:%d \n",__LINE__,__FUNCTION__,ret)
#define GET_GPIO_VALUE 0x01
#define SET_GPIO_VALUE 0x02
static ssize_t gpio_read(struct file *fp, char __user *buff, size_t size, loff_t *off);
static int gpio_open(struct inode *i, struct file *fp);
static ssize_t gpio_write(struct file *fp, const char __user *buff, size_t size, loff_t *off);
static int gpio_release(struct inode *i, struct file *fp);
static long gpio_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
struct gpio_device{
struct cdev * gp;
dev_t devid;
unsigned gpio;
const char *label;
struct class *gpio_class;;
};
static struct file_operations fp={
.owner = THIS_MODULE,
.open = gpio_open,
.release = gpio_release,
.write = gpio_write,
.read = gpio_read,
.compat_ioctl = gpio_compat_ioctl,
};
static struct gpio_device gd={
.gpio = 0xC0,//NUC980_PG0 (0xC0 + 0)
.label = "gpio_device",
};
static ssize_t gpio_read(struct file *fp, char __user *buff, size_t size, loff_t *off)
{
ssize_t ret;
struct gpio_device *g = (struct gpio_device *)fp->private_data;
ret = gpio_get_value(g->gpio);
ret = copy_to_user(buff,&ret,sizeof(ret));
return ret;
}
static int gpio_open(struct inode *i, struct file *fp)
{
int ret=0;
fp->private_data = &gd;
return ret;
}
static ssize_t gpio_write(struct file *fp, const char __user *buff, size_t size, loff_t *off)
{
ssize_t ret=0;
char value[1];
struct gpio_device *g = (struct gpio_device *)fp->private_data;
ret=copy_from_user(value,buff,sizeof(value));
gpio_set_value(g->gpio,value[0]);
return ret;
}
static int gpio_release(struct inode *i, struct file *fp)
{
int ret=0;
return ret;
}
static long gpio_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
long ret=0;
unsigned long *p = (unsigned long *)arg;
struct gpio_device *g = (struct gpio_device *)fp->private_data;
switch (cmd){
case GET_GPIO_VALUE:
ret = gpio_get_value(g->gpio);
*p = ret;
break;
case SET_GPIO_VALUE:
gpio_set_value(g->gpio,*p);
break;
default:
ret = -1;break;
}
return ret;
}
int __init gpio_init(void)
{
int ret;
printk("%s\n%d,%s exec \n",__FILE__,__LINE__,__FUNCTION__);
ret = gpio_request(gd.gpio,gd.label);
print_ret(ret);
ret = gpio_direction_output(gd.gpio,1);
print_ret(ret);
ret = alloc_chrdev_region(&gd.devid, 0, 1,gd.label);
print_ret(ret);
gd.gp = cdev_alloc();
gd.gp->owner = THIS_MODULE;
gd.gp->ops = &fp;
ret = cdev_add(gd.gp,gd.devid,1);
print_ret(ret);
gd.gpio_class = class_create(THIS_MODULE, gd.label);
device_create(gd.gpio_class, NULL, gd.devid,NULL, gd.label);
return 0;
}
void __exit gpio_clean(void)
{
gpio_free(gd.gpio);
unregister_chrdev_region(gd.devid,1);
device_destroy(gd.gpio_class, gd.devid);
class_destroy(gd.gpio_class);
printk("%s\n%d,%s exit \n",__FILE__,__LINE__,__FUNCTION__);
}
module_init(gpio_init);
module_exit(gpio_clean);
MODULE_AUTHOR("klp");
MODULE_DESCRIPTION("ubuntu test");
MODULE_LICENSE("GPL");
MODULE_ALIAS("mod:test");
编译设备驱动以及应用的makefile:
obj-m := gpiocontrolcdev.o
#gpiotest-objs := *.o
kernel_path := /home/klppc/nuc980/NUC980-linux-4.4.y
PWD := $(shell pwd)
all:default
arm-linux-gcc -o main main.c
cp main /nfsroot/
default:
$(MAKE) -C $(kernel_path) M=$(PWD) modules
cp *.ko /nfsroot/
clean:
$(MAKE) -C $(kernel_path) M=$(PWD) clean
这个makefile编译模块的同时,编译main应用程序,编译指定模块的时候需要修改 obj-m:= file.o 这里的file为指定.c文件的文件名字。
gitee地址:https://gitee.com/already_use/nuc980-linux-driver-learning
注意:使用混杂设备注册的设备使用 cat /proc/devices 这里面不会有注册的设备文件名字显示只有一个misc总设备名字,可以在 dev/目录里面找到注册设备名字,注册设备时使用的次设备号是 MISC_DYNAMIC_MINOR 为 0xFF,表示动态分配。