驱动中断子系统
demo.c
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define CNAME "mycdev_led_key"
#define COUNT 3
unsigned int number;
struct cdev* cdev;
int minor = 0;
#if 0
unsigned int major = 0;
#else
unsigned int major = 500;
#endif
char kbuf[128] = { 0 };
struct class* cls;
struct device* dev;
struct device_node* node;
struct property* pr;
int gpioled[3];
unsigned int irqno[3] = { 0 };
char* name[] = { "key1", "key2", "key3" };
int curno;
wait_queue_head_t wq;
int condition;
irqreturn_t key_irq(int irq, void* dev)
{
switch ((int)dev) {
case 0: {
printk("key1 down led1 invert\n");
number=gpio_get_value(gpioled[2]);
gpio_set_value(gpioled[2],!number);
} break;
case 1: {
printk("key2 down led2 invert\n");
number=gpio_get_value(gpioled[1]);
gpio_set_value(gpioled[1],!number);
} break;
case 2: {
printk("key3 down led3 invert\n");
number=gpio_get_value(gpioled[0]);
gpio_set_value(gpioled[0],!number);
} break;
}
number=!number;
condition=1;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
int mycdev_open(struct inode* inode, struct file* filp)
{
curno = MINOR(inode->i_rdev);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
if(filp->f_flags & O_NONBLOCK)
{
return -EINVAL;
}
else
{
ret = wait_event_interruptible(wq,condition);
if(ret==-ERESTARTSYS)
{
printk("接收阻塞休眠\n");
return ret;
}
}
if (size > sizeof(number))
size = sizeof(number);
ret = copy_to_user(ubuf, &number, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
condition=0;
return size;
}
ssize_t mycdev_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy data from user error\n");
return -EINVAL;
}
return size;
}
int mycdev_close(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int gpio_dtb_init(void)
{
int ret, i;
node = of_find_node_by_path("/mycdev");
if (node == NULL) {
printk("获取节点信息失败\n");
return ENODATA;
}
printk("获取节点信息成功\n");
gpioled[0] = of_get_named_gpio(node, "led1", 0);
if (gpioled[0] < 0) {
printk("获取extend-led1编号失败\n");
return gpioled[0];
}
printk("获取extend-led1编号成功\n");
gpioled[1] = of_get_named_gpio(node, "led2", 0);
if (gpioled[1] < 0) {
printk("获取extend-led2编号失败\n");
return gpioled[1];
}
printk("获取extend-led2编号成功\n");
gpioled[2] = of_get_named_gpio(node, "led3", 0);
if (gpioled[2] < 0) {
printk("获取extend-led3编号失败\n");
return gpioled[2];
}
printk("获取extend-led3编号成功\n");
for (i = 0; i < 3; i++) {
ret = gpio_request(gpioled[i], NULL);
if (ret) {
printk("申请设备编号失败\n");
return ret;
}
gpio_direction_output(gpioled[i], 0);
}
return 0;
}
static int gpio_dtb_exit(void)
{
int i;
for (i = 0; i < 3; i++) {
gpio_free(gpioled[i]);
}
return 0;
}
static int myirq_init(void)
{
int ret,i;
node = of_find_compatible_node(NULL,NULL,"mycdev,woo");
if(node ==NULL)
{
printk("get node error\n");
return -ENODATA;
}
for(i=0;i<ARRAY_SIZE(irqno);i++)
{
irqno[i]=irq_of_parse_and_map(node,i);
if(irqno[i]==0)
{
printk("get irq number error\n");
return -EAGAIN;
}
ret = request_irq(irqno[i],key_irq,IRQF_TRIGGER_FALLING,name[i],(void *)i);
if(ret)
{
printk("request irq error\n");
return ret;
}
}
return 0;
}
static int myirq_exit(void)
{
int i;
for(i=0;i<ARRAY_SIZE(irqno);i++)
{
free_irq(irqno[i],(void *)i);
}
return 0;
}
static int __init mycdev_init(void)
{
int ret, i;
dev_t devno;
cdev = cdev_alloc();
if (cdev == NULL) {
printk("alloc cdev memory error\n");
ret = -ENOMEM;
goto ERR1;
}
cdev_init(cdev, &fops);
if (major == 0) {
ret = alloc_chrdev_region(&devno, minor, COUNT, CNAME);
if (ret) {
printk("dynamic:request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
} else {
ret = register_chrdev_region(MKDEV(major, minor), COUNT, CNAME);
if (ret) {
printk("static:request device number error\n");
goto ERR2;
}
}
ret = cdev_add(cdev, MKDEV(major, minor), COUNT);
if (ret) {
printk("register chardev error\n");
goto ERR3;
}
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = minor; i < minor + COUNT; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "my_led%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
init_waitqueue_head(&wq);
gpio_dtb_init();
myirq_init();
return 0;
ERR5:
for (--i; i >= minor; i--) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), COUNT);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
int i;
gpio_dtb_exit();
myirq_exit();
for (i = minor; i < minor + 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
cdev_del(cdev);
unregister_chrdev_region(MKDEV(major, minor), COUNT);
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t pid;
char buf[128]="";
int num;
int fd=open("/dev/my_led0",O_RDWR);
int which;
if(fd<0)
{
printf("打开设备文件失败\n");
exit(-1);
}
printf("设备文件打开成功\n");
while(1)
{
read(fd,&num,sizeof(num));
printf("number:%d\n",num);
sleep(1);
}
close(fd);
return 0;
}
platform
demo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#define CNAME "mycdev_led_key"
#define COUNT 3
struct resource *res;
struct gpio_desc *gpioled[3];
unsigned int number;
struct cdev* cdev;
int minor = 0;
#if 0
unsigned int major = 0;
#else
unsigned int major = 500;
#endif
char kbuf[128] = { 0 };
struct class* cls;
struct device* dev;
struct device_node* node;
struct property* pr;
unsigned int irqno[3] = { 0 };
char* name[] = { "key1", "key2", "key3" };
int curno;
wait_queue_head_t wq;
int condition;
struct timer_list mytimer;
struct work_struct work;
void timer_handler(struct timer_list *timer)
{
schedule_work(&work);
}
void work_func(struct work_struct *work)
{
printk("-----timer and workqueue test-----\n");
mod_timer(&mytimer,jiffies+HZ);
}
irqreturn_t key_irq(int irq, void* dev)
{
switch ((int)dev) {
case 0: {
printk("key1 down led1 invert\n");
number=gpiod_get_value(gpioled[2]);
gpiod_set_value(gpioled[2],!number);
} break;
case 1: {
printk("key2 down led2 invert\n");
number=gpiod_get_value(gpioled[1]);
gpiod_set_value(gpioled[1],!number);
} break;
case 2: {
printk("key3 down led3 invert\n");
number=gpiod_get_value(gpioled[0]);
gpiod_set_value(gpioled[0],!number);
} break;
}
number=!number;
condition=1;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
int mycdev_open(struct inode* inode, struct file* filp)
{
curno = MINOR(inode->i_rdev);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
if(filp->f_flags & O_NONBLOCK)
{
return -EINVAL;
}
else
{
ret = wait_event_interruptible(wq,condition);
if(ret==-ERESTARTSYS)
{
printk("接收阻塞休眠\n");
return ret;
}
}
if (size > sizeof(number))
size = sizeof(number);
ret = copy_to_user(ubuf, &number, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
condition=0;
return size;
}
ssize_t mycdev_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy data from user error\n");
return -EINVAL;
}
return size;
}
int mycdev_close(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
int gpio_irq_init(struct device_node *node)
{
int i,ret;
for(i=0;i<ARRAY_SIZE(irqno);i++)
{
irqno[i]=irq_of_parse_and_map(node,i);
if(irqno[i]==0)
{
printk("get irq number error\n");
return -EAGAIN;
}
ret = request_irq(irqno[i],key_irq,IRQF_TRIGGER_FALLING,name[i],(void *)i);
if(ret)
{
printk("request irq error\n");
return ret;
}
}
gpioled[0]=gpiod_get_from_of_node(node,"led1",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(gpioled[0]))
{
printk("获取GPIO编号失败\n");
return PTR_ERR(gpioled[0]);
}
printk("获取extend-led1编号成功\n");
gpioled[1]=gpiod_get_from_of_node(node,"led2",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(gpioled[1]))
{
printk("获取GPIO编号失败\n");
return PTR_ERR(gpioled[1]);
}
printk("获取extend-led2编号成功\n");
gpioled[2]=gpiod_get_from_of_node(node,"led3",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(gpioled[2]))
{
printk("获取GPIO编号失败\n");
return PTR_ERR(gpioled[2]);
}
printk("获取extend-led3编号成功\n");
return 0;
}
void gpio_irq_exit(void)
{
int i;
for(i=0;i<ARRAY_SIZE(irqno);i++)
{
free_irq(irqno[i],(void *)i);
}
for (i = 0; i < 3; i++) {
gpiod_put(gpioled[i]);
}
}
int pdrv_probe(struct platform_device *pdr)
{
int ret, i;
dev_t devno;
ret=gpio_irq_init(pdr->dev.of_node);
if(ret!=0)
{
printk("初始化led和irq失败\n");
return ret;
}
INIT_WORK(&work,work_func);
cdev = cdev_alloc();
if (cdev == NULL) {
printk("alloc cdev memory error\n");
ret = -ENOMEM;
goto ERR1;
}
cdev_init(cdev, &fops);
if (major == 0) {
ret = alloc_chrdev_region(&devno, minor, COUNT, CNAME);
if (ret) {
printk("dynamic:request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
} else {
ret = register_chrdev_region(MKDEV(major, minor), COUNT, CNAME);
if (ret) {
printk("static:request device number error\n");
goto ERR2;
}
}
ret = cdev_add(cdev, MKDEV(major, minor), COUNT);
if (ret) {
printk("register chardev error\n");
goto ERR3;
}
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = minor; i < minor + COUNT; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "my_led%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
init_waitqueue_head(&wq);
mytimer.expires=jiffies+HZ;
timer_setup(&mytimer,timer_handler,0);
add_timer(&mytimer);
return 0;
ERR5:
for (--i; i >= minor; i--) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), COUNT);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
int pdrv_remove(struct platform_device *pdr)
{
int i;
printk("%s:%d\n",__FILE__,__LINE__);
cancel_work_sync(&work);
del_timer(&mytimer);
gpio_irq_exit();
for (i = minor; i < minor + 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
cdev_del(cdev);
unregister_chrdev_region(MKDEV(major, minor), COUNT);
kfree(cdev);
return 0;
}
struct of_device_id oftable[]={
{.compatible="jyp,myplatform"},
{},
};
MODULE_DEVICE_TABLE(jyp,oftable);
struct platform_driver pdrv = {
.probe=pdrv_probe,
.remove=pdrv_remove,
.driver={
.name = "test",
.of_match_table=oftable,
},
};
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jyp");
test.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, char const *argv[])
{
int num;
int fd=open("/dev/my_led0",O_RDWR);
int which;
if(fd<0)
{
printf("打开设备文件失败\n");
exit(-1);
}
printf("设备文件打开成功\n");
while(1)
{
read(fd,&num,sizeof(num));
printf("number:%d\n",num);
}
close(fd);
return 0;
}