second_dri.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of_device.h>
#include <linux/delay.h>
#define SECOND_MAJOR 237
#define USE_MUTEX
static int second_major = SECOND_MAJOR;
module_param(second_major, int, S_IRUGO);
static struct class *second_class;
struct second_dev *second_devp;
struct second_dev {
struct cdev cdev;
#ifndef USE_MUTEX
atomic_t counter;
#else
int counter;
#endif
unsigned long state; // 1 start timer, 0 timer stop
struct mutex mutex;
struct timer_list s_timer;
struct device *dev;
struct miscdevice miscdev;
const struct attribute_group **groups;
struct work_struct mywork;
};
//static struct second_dev *second_devp_;
static void second_timer_handler(unsigned long arg)
{
struct second_dev *dev = (void *) arg;
mod_timer(&dev->s_timer, jiffies + HZ);
#ifndef USE_MUTEX
atomic_inc(&dev->counter);
#else
printk(KERN_INFO "%s mutex lock\n", __func__);
mutex_lock(&dev->mutex);
dev->counter++;
mutex_unlock(&dev->mutex);
#endif
// printk(KERN_INFO "current jiffies is %ld\n", jiffies);
}
static void second_timer_ctl(struct work_struct *ws)
{
struct second_dev *dev =
container_of(ws, struct second_dev, mywork);
int ret = 0;
printk(KERN_INFO "%s trigger_store test\n", __func__);
msleep(500);
return;
}
static int second_open(struct inode *inode, struct file *filp)
{
/* init_timer(&second_devp->s_timer);
second_devp->s_timer.function = &second_timer_handler;
second_devp->s_timer.expires = jiffies + HZ;
add_timer(&second_devp->s_timer);
*/
// struct second_dev *second_devp = container_of(filp->private_data,
// struct second_dev, miscdev);
// struct second_dev *second_devp = container_of(inode->i_cdev, struct second_dev, cdev);
mutex_lock(&second_devp->mutex);
if(second_devp->state == 0)
{
setup_timer(&second_devp->s_timer, second_timer_handler, (unsigned long)second_devp);
add_timer(&second_devp->s_timer);
second_devp->state = 1;
}
mutex_unlock(&second_devp->mutex);
// filp->private_data = second_devp;
printk(KERN_INFO "%s-open 1\n", __func__);
return 0;
}
static int second_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t second_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
int counter;
// struct second_dev *second_devp = filp->private_data;
// struct second_dev *second_devp = container_of(filp->private_data,
// struct second_dev, miscdev);
#ifndef USE_MUTEX
counter = atomic_read(&second_devp->counter);
#else
printk(KERN_INFO "%s mutex lock\n", __func__);
mutex_lock(&second_devp->mutex);
counter = second_devp->counter;
mutex_unlock(&second_devp->mutex);
#endif
if(put_user(counter, (int *)buf))
return -EFAULT;
else
return sizeof(unsigned int);
}
static ssize_t second_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
ssize_t trigger_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct second_dev *sdev = dev_get_drvdata(dev);
unsigned long state;
ssize_t ret = 0;
printk(KERN_INFO "%s: 0 -state \n", __func__);
return 0;
#ifdef USE_MUTEX
mutex_lock(&sdev->mutex);
ret = kstrtoul(buf, 10, &state);
if (ret)
goto unlock;
printk(KERN_INFO "%s: 0 - mutex lock, state = %ld\n", __func__, state);
schedule_work(&sdev->mywork);
goto unlock;
if (state == 1)
{
printk(KERN_INFO "%s: 1 - mutex lock, state = %ld\n", __func__, state);
if (sdev->state == 0)
{
setup_timer(&second_devp->s_timer, second_timer_handler, (unsigned long)second_devp);
add_timer(&sdev->s_timer);
sdev->state = 1;
}
}
else
{
printk(KERN_INFO "%s: 2 - mutex lock, state = %ld\n", __func__, state);
if(sdev->state == 1)
{
del_timer(&sdev->s_timer);
sdev->state = 0;
}
}
printk(KERN_INFO "%s: 3 - mutex lock, state = %ld\n", __func__, state);
unlock:
mutex_unlock(&dev->mutex);
#endif
return ret;
}
ssize_t trigger_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int cnt = -1;
struct second_dev *sdev = dev_get_drvdata(dev);
// int *counter = dev_get_drvdata(dev);
//printk(KERN_INFO "counter_addr=%p", counter);
#ifndef USE_MUTEX
cnt = atomic_read(&sdev->counter);
#else
printk(KERN_INFO "%s mutex lock\n", __func__);
mutex_lock(&sdev->mutex);
// mutex_lock_interruptible(&second_devp->mutex);
cnt = sdev->counter;
mutex_unlock(&sdev->mutex);
#endif
return sprintf(buf, "second_trigger_show: count =%d\n", cnt);
// return sprintf(buf, "second_trigger_show: count = %d, counter_addr=%p\n", *counter, counter);
}
//static DEVICE_ATTR_RW(trigger);
static DEVICE_ATTR(trigger, 0644, trigger_show, trigger_store);
static struct attribute *second_trigger_attrs[] = {
&dev_attr_trigger.attr,
NULL,
};
static const struct attribute_group second_trigger_group = {
.attrs = second_trigger_attrs,
};
static const struct attribute_group *second_groups[] = {
&second_trigger_group,
};
static const struct file_operations second_fops = {
.owner = THIS_MODULE,
.open = second_open,
.release= second_release,
.read = second_read,
.write = second_write,
};
static int second_probe(struct platform_device *pdev)
{
int ret, err;
struct device *dev = &pdev->dev;
dev_t devno = MKDEV(SECOND_MAJOR, 0);
if(SECOND_MAJOR) {
ret = register_chrdev_region(devno, 1, "second");
} else {
ret = alloc_chrdev_region(&devno, 0, 1, "second");
second_major = MAJOR(devno);
}
if(ret < 0)
return ret;
second_devp = devm_kzalloc(dev, sizeof(*second_devp), GFP_KERNEL);
if(!second_devp)
{
dev_info(&pdev->dev, "second_devp devm_kzalloc failed\n");
return -ENOMEM;
}
#ifndef USE_MUTEX
atomic_set(&second_devp->counter, 0);
#else
printk(KERN_INFO "%s mutex lock\n", __func__);
mutex_init(&second_devp->mutex);
mutex_lock(&second_devp->mutex);
second_devp->counter = 0;
second_devp->state = 0;
mutex_unlock(&second_devp->mutex);
#endif
INIT_WORK(&second_devp->mywork, second_timer_ctl);
cdev_init(&second_devp->cdev, &second_fops);
second_devp->cdev.owner = THIS_MODULE;
err = cdev_add(&second_devp->cdev, devno, 1);
if(err)
{
printk(KERN_DEBUG "Error %d adding second\n", err);
goto fail_malloc;
}
second_devp->dev = device_create(second_class, dev,
MKDEV(SECOND_MAJOR, 0), second_devp, "second");
// platform_set_drvdata(pdev, second_devp);
dev_info(&pdev->dev, "second_dev drv proded\n");
return 0;
fail_malloc:
printk(KERN_INFO "6 - probe failed\n");
unregister_chrdev_region( MKDEV(SECOND_MAJOR, 0), 1);
return ret;
}
static int second_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver second_driver = {
.driver = {
.name = "second_dev",
.owner = THIS_MODULE,
},
.probe = second_probe,
.remove = second_remove,
};
static int __init second_init(void)
{
int ret;
second_class = class_create(THIS_MODULE, "second");
if (IS_ERR(second_class))
return PTR_ERR(second_class);
second_class->dev_groups = second_groups;
ret = platform_driver_register(&second_driver);
if (ret)
printk(KERN_ERR "second: probe failed: %d\n", ret);
return ret;
}
module_init(second_init);
static void __exit second_exit(void)
{
if(second_devp)
{
cdev_del(&second_devp->cdev);
del_timer(&second_devp->s_timer);
}
device_destroy(second_class, MKDEV(SECOND_MAJOR, 0));
class_destroy(second_class);
unregister_chrdev_region(MKDEV(SECOND_MAJOR, 0), 1);
platform_driver_unregister(&second_driver);
printk(KERN_INFO "second: exit\n");
}
module_exit(second_exit);
//module_platform_driver(second_driver);
MODULE_AUTHOR("Bao hua");
MODULE_LICENSE("GPL v2");
second_dev.c
#include <linux/module.h>
#include <linux/of_device.h>
static struct platform_device *second_pdev;
static int __init seconddev_init(void)
{
int ret;
second_pdev = platform_device_alloc("second_dev", -1);
if(!second_pdev)
return -ENOMEM;
ret = platform_device_add(second_pdev);
if(ret) {
platform_device_put(second_pdev);
return ret;
}
return 0;
}
module_init(seconddev_init);
static void __exit seconddev_exit(void)
{
platform_device_unregister(second_pdev);
}
module_exit(seconddev_exit);
MODULE_AUTHOR("Bao hua");
MODULE_LICENSE("GPL v2");
Makefile
KVERS = $(shell uname -r)
#Kernel modules
obj-m += second.o
obj-m += second_test.o
#obj-m += second_mutex.o
obj-m += second_dri.o
obj-m += second_dev.o
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
user_test:
gcc test.c -o test
clean:
make -c /lib/modules/$(KVERS)/build M=$(CURDIR) clean
驱动与设备分开, 此驱动还有缺陷 当 通过echo 0 >/sys/class/second/second/trigger , 会一直进行这个重复操作,原因待查。
globalfifo.c
/*
* a simple char device driver: globalfifo
*
* Copyright (C) 2014 Barry Song (baohua@kernel.org)
*
* Licensed under GPLv2 or later.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of_device.h>
#define GLOBALFIFO_SIZE 0x1000
#define FIFO_CLEAR 0x1
#define GLOBALFIFO_MAJOR 235
static int globalfifo_major = GLOBALFIFO_MAJOR;
module_param(globalfifo_major, int, S_IRUGO);
static struct class *globalfifo_class;
struct globalfifo_dev {
struct cdev cdev;
unsigned int current_len;
unsigned char mem[GLOBALFIFO_SIZE];
struct mutex mutex;
wait_queue_head_t r_wait;
wait_queue_head_t w_wait;
struct device *dev;
struct fasync_struct *async_queue;
};
struct globalfifo_dev *globalfifo_devp;
static int globalfifo_fasync(int fd, struct file *filp, int mode)
{
struct globalfifo_dev *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
static int globalfifo_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalfifo_devp;
return 0;
}
static int globalfifo_release(struct inode *inode, struct file *filp)
{
globalfifo_fasync(-1, filp, 0);
return 0;
}
static long globalfifo_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data;
switch (cmd) {
case FIFO_CLEAR:
mutex_lock(&dev->mutex);
dev->current_len = 0;
memset(dev->mem, 0, GLOBALFIFO_SIZE);
mutex_unlock(&dev->mutex);
printk(KERN_INFO "globalfifo is set to zero\n");
break;
default:
return -EINVAL;
}
return 0;
}
static unsigned int globalfifo_poll(struct file *filp, poll_table * wait)
{
unsigned int mask = 0;
struct globalfifo_dev *dev = filp->private_data;
mutex_lock(&dev->mutex);
poll_wait(filp, &dev->r_wait, wait);
poll_wait(filp, &dev->w_wait, wait);
if (dev->current_len != 0) {
mask |= POLLIN | POLLRDNORM;
}
if (dev->current_len != GLOBALFIFO_SIZE) {
mask |= POLLOUT | POLLWRNORM;
}
mutex_unlock(&dev->mutex);
return mask;
}
static ssize_t globalfifo_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int ret;
struct globalfifo_dev *dev = filp->private_data;
DECLARE_WAITQUEUE(wait, current);
mutex_lock(&dev->mutex);
add_wait_queue(&dev->r_wait, &wait);
while (dev->current_len == 0) {
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex);
schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex);
}
if (count > dev->current_len)
count = dev->current_len;
if (copy_to_user(buf, dev->mem, count)) {
ret = -EFAULT;
goto out;
} else {
memcpy(dev->mem, dev->mem + count, dev->current_len - count);
dev->current_len -= count;
printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count,
dev->current_len);
wake_up_interruptible(&dev->w_wait);
ret = count;
}
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->w_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct globalfifo_dev *dev = filp->private_data;
int ret;
DECLARE_WAITQUEUE(wait, current);
mutex_lock(&dev->mutex);
add_wait_queue(&dev->w_wait, &wait);
while (dev->current_len == GLOBALFIFO_SIZE) {
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex);
schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex);
}
if (count > GLOBALFIFO_SIZE - dev->current_len)
count = GLOBALFIFO_SIZE - dev->current_len;
if (copy_from_user(dev->mem + dev->current_len, buf, count)) {
ret = -EFAULT;
goto out;
} else {
dev->current_len += count;
printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count,
dev->current_len);
wake_up_interruptible(&dev->r_wait);
if (dev->async_queue) {
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
printk(KERN_DEBUG "%s kill SIGIO\n", __func__);
}
ret = count;
}
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->w_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
ssize_t globalfifo_trigger_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int cnt = -1;
struct globalfifo_dev *gdev = dev_get_drvdata(dev);
mutex_lock(&gdev->mutex);
// count = gdev->current_len;
printk(KERN_INFO "%s - buf=%s, len=%d\n", __func__, buf, count);
// cnt = copy_from_user(gdev->mem + gdev->current_len, buf, count);
count = snprintf(gdev->mem + gdev->current_len, GLOBALFIFO_SIZE, "%s", buf);
gdev->current_len += count;
mutex_unlock(&gdev->mutex);
return count;
}
ssize_t globalfifo_trigger_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int count;
struct globalfifo_dev *gdev = dev_get_drvdata(dev);
mutex_lock(&gdev->mutex);
count = gdev->current_len;
if(count == 0 )
{
count = sprintf(buf, "NO data put in. current_len=0\n");
}
else
{
count = sprintf(buf, "current_len=%d, data=%s\n", count, gdev->mem);
buf[count] = 0;
}
mutex_unlock(&gdev->mutex);
return count;
// return sprintf(buf, "globalfifo_trigger_show: count = %d, counter_addr=%p\n", *counter, counter);
}
static DEVICE_ATTR(trigger, 0644, globalfifo_trigger_show, globalfifo_trigger_store);
static struct attribute *globalfifo_trigger_attrs[] = {
&dev_attr_trigger.attr,
NULL,
};
static const struct attribute_group globalfifo_trigger_group = {
.attrs = globalfifo_trigger_attrs,
};
static const struct attribute_group *globalfifo_groups[] = {
&globalfifo_trigger_group,
};
static const struct file_operations globalfifo_fops = {
.owner = THIS_MODULE,
.read = globalfifo_read,
.write = globalfifo_write,
.unlocked_ioctl = globalfifo_ioctl,
.poll = globalfifo_poll,
.fasync = globalfifo_fasync,
.open = globalfifo_open,
.release = globalfifo_release,
};
static int globalfifo_prove(struct platform_device *pdev)
{
// struct globalfifo_dev *gl;
int ret;
dev_t devno = MKDEV(GLOBALFIFO_MAJOR, 0);
printk(KERN_INFO "0 %s - probe failed\n", __func__);
if(GLOBALFIFO_MAJOR) {
printk(KERN_INFO "00 %s - probe failed\n", __func__);
ret = register_chrdev_region(devno, 1, "gblobalfifo");
printk(KERN_INFO "000 %s - probe failed\n", __func__);
} else {
printk(KERN_INFO "01 %s - probe failed\n", __func__);
ret = alloc_chrdev_region(&devno, 0, 1, "gblobalfifo");
globalfifo_major = MAJOR(devno);
}
if(ret < 0)
return ret;
printk(KERN_INFO "1 %s - probe failed\n", __func__);
globalfifo_devp = devm_kzalloc(&pdev->dev, sizeof(*globalfifo_devp), GFP_KERNEL);
if(!globalfifo_devp) {
ret = -ENOMEM;
goto fail_malloc;
}
printk(KERN_INFO "2 %s - probe failed\n", __func__);
cdev_init(&globalfifo_devp->cdev, &globalfifo_fops);
globalfifo_devp->cdev.owner = THIS_MODULE;
ret = cdev_add(&globalfifo_devp->cdev, devno, 1);
if(ret < 0)
{
printk(KERN_INFO "Error %d adding globalfifo\n", ret);
goto fail_malloc;
}
printk(KERN_INFO "5 %s - probe failed\n", __func__);
mutex_init(&globalfifo_devp->mutex);
init_waitqueue_head(&globalfifo_devp->r_wait);
init_waitqueue_head(&globalfifo_devp->w_wait);
globalfifo_devp->dev = device_create(globalfifo_class, &pdev->dev,
MKDEV(GLOBALFIFO_MAJOR, 0), globalfifo_devp, "globalfifo");
dev_info(&pdev->dev, "globalfifo drv proded\n");
return 0;
fail_malloc:
printk(KERN_INFO "6 - probe failed\n");
unregister_chrdev_region(devno, 1);
return ret;
}
static int globalfifo_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver globalfifo_driver = {
.driver = {
.name = "globalfifo",
.owner = THIS_MODULE,
},
.probe = globalfifo_prove,
.remove = globalfifo_remove,
};
static int __init globalfifo_init(void)
{
int ret;
globalfifo_class = class_create(THIS_MODULE, "globalfifo");
if (IS_ERR(globalfifo_class))
return PTR_ERR(globalfifo_class);
globalfifo_class->dev_groups = globalfifo_groups;
ret = platform_driver_register(&globalfifo_driver);
if (ret)
printk(KERN_ERR "globalfifo: probe failed: %d\n", ret);
return ret;
}
module_init(globalfifo_init);
static void __exit globalfifo_exit(void)
{
if(globalfifo_devp)
cdev_del(&globalfifo_devp->cdev);
device_destroy(globalfifo_class, MKDEV(GLOBALFIFO_MAJOR, 0));
unregister_chrdev_region(MKDEV(GLOBALFIFO_MAJOR, 0), 1);
class_destroy(globalfifo_class);
platform_driver_unregister(&globalfifo_driver);
printk(KERN_INFO "globalfifo: exit\n");
}
module_exit(globalfifo_exit);
MODULE_AUTHOR("Barry Song <baohua@kernel.org>");
MODULE_LICENSE("GPL v2");
globalfifo-dev.c
#include <linux/module.h>
#include <linux/of_device.h>
static struct platform_device *globalfifo_pdev;
static int __init globalfifodev_init(void)
{
int ret;
globalfifo_pdev = platform_device_alloc("globalfifo", -1);
if(!globalfifo_pdev)
return -ENOMEM;
ret = platform_device_add(globalfifo_pdev);
if(ret) {
platform_device_put(globalfifo_pdev);
return ret;
}
return 0;
}
module_init(globalfifodev_init);
static void __exit globalfifodev_exit(void)
{
platform_device_unregister(globalfifo_pdev);
}
module_exit(globalfifodev_exit);
MODULE_AUTHOR("Bao hua");
MODULE_LICENSE("GPL v2");