Release 4.7 drivers/fmc/fmc-chardev.c
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/fmc.h>
#include <linux/uaccess.h>
static LIST_HEAD(fc_devices);
static DEFINE_SPINLOCK(fc_lock);
struct fc_instance {
struct list_head list;
struct fmc_device *fmc;
struct miscdevice misc;
};
/* at open time, we must identify our device */
static int fc_open(struct inode *ino, struct file *f)
{
struct fmc_device *fmc;
struct fc_instance *fc;
int minor = iminor(ino);
list_for_each_entry(fc, &fc_devices, list)
if (fc->misc.minor == minor)
break;
if (fc->misc.minor != minor)
return -ENODEV;
fmc = fc->fmc;
if (try_module_get(fmc->owner) == 0)
return -ENODEV;
f->private_data = fmc;
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 97 | 100.00% | 1 | 100.00% |
| Total | 97 | 100.00% | 1 | 100.00% |
static int fc_release(struct inode *ino, struct file *f)
{
struct fmc_device *fmc = f->private_data;
module_put(fmc->owner);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 35 | 100.00% | 1 | 100.00% |
| Total | 35 | 100.00% | 1 | 100.00% |
/* read and write are simple after the default llseek has been used */
static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
loff_t *offp)
{
struct fmc_device *fmc = f->private_data;
unsigned long addr;
uint32_t val;
if (count < sizeof(val))
return -EINVAL;
count = sizeof(val);
addr = *offp;
if (addr > fmc->memlen)
return -ESPIPE; /* Illegal seek */
val = fmc_readl(fmc, addr);
if (copy_to_user(buf, &val, count))
return -EFAULT;
*offp += count;
return count;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 110 | 100.00% | 1 | 100.00% |
| Total | 110 | 100.00% | 1 | 100.00% |
static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
loff_t *offp)
{
struct fmc_device *fmc = f->private_data;
unsigned long addr;
uint32_t val;
if (count < sizeof(val))
return -EINVAL;
count = sizeof(val);
addr = *offp;
if (addr > fmc->memlen)
return -ESPIPE; /* Illegal seek */
if (copy_from_user(&val, buf, count))
return -EFAULT;
fmc_writel(fmc, val, addr);
*offp += count;
return count;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 111 | 100.00% | 1 | 100.00% |
| Total | 111 | 100.00% | 1 | 100.00% |
static const struct file_operations fc_fops = {
.owner = THIS_MODULE,
.open = fc_open,
.release = fc_release,
.llseek = generic_file_llseek,
.read = fc_read,
.write = fc_write,
};
/* Device part .. */
static int fc_probe(struct fmc_device *fmc);
static int fc_remove(struct fmc_device *fmc);
static struct fmc_driver fc_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = fc_probe,
.remove = fc_remove,
/* no table: we want to match everything */
};
/* We accept the generic busid parameter */
FMC_PARAM_BUSID(fc_drv);
/* probe and remove must allocate and release a misc device */
static int fc_probe(struct fmc_device *fmc)
{
int ret;
int index = 0;
struct fc_instance *fc;
if (fmc->op->validate)
index = fmc->op->validate(fmc, &fc_drv);
if (index < 0)
return -EINVAL; /* not our device: invalid */
/* Create a char device: we want to create it anew */
fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (!fc)
return -ENOMEM;
fc->fmc = fmc;
fc->misc.minor = MISC_DYNAMIC_MINOR;
fc->misc.fops = &fc_fops;
fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
ret = misc_register(&fc->misc);
if (ret < 0)
goto out;
spin_lock(&fc_lock);
list_add(&fc->list, &fc_devices);
spin_unlock(&fc_lock);
dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
fc->misc.name);
return 0;
out:
kfree(fc->misc.name);
kfree(fc);
return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 175 | 85.78% | 2 | 66.67% |
dan carpenter | dan carpenter | 29 | 14.22% | 1 | 33.33% |
| Total | 204 | 100.00% | 3 | 100.00% |
static int fc_remove(struct fmc_device *fmc)
{
struct fc_instance *fc;
list_for_each_entry(fc, &fc_devices, list)
if (fc->fmc == fmc)
break;
if (fc->fmc != fmc) {
dev_err(&fmc->dev, "remove called but not found\n");
return -ENODEV;
}
spin_lock(&fc_lock);
list_del(&fc->list);
spin_unlock(&fc_lock);
misc_deregister(&fc->misc);
kfree(fc->misc.name);
kfree(fc);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 102 | 100.00% | 2 | 100.00% |
| Total | 102 | 100.00% | 2 | 100.00% |
static int fc_init(void)
{
int ret;
ret = fmc_driver_register(&fc_drv);
return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 22 | 100.00% | 1 | 100.00% |
| Total | 22 | 100.00% | 1 | 100.00% |
static void fc_exit(void)
{
fmc_driver_unregister(&fc_drv);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 14 | 100.00% | 1 | 100.00% |
| Total | 14 | 100.00% | 1 | 100.00% |
module_init(fc_init);
module_exit(fc_exit);
MODULE_LICENSE("GPL");
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp |
alessandro rubini | alessandro rubini | 838 | 96.66% | 2 | 66.67% |
dan carpenter | dan carpenter | 29 | 3.34% | 1 | 33.33% |
| Total | 867 | 100.00% | 3 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.