14debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* 24debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * Copyright (C) 2012 CERN (www.cern.ch) 34debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * Author: Alessandro Rubini <rubini@gnudd.com> 44debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * 54debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * Released according to the GNU GPL, version 2 or any later version. 64debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * 74debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * This work is part of the White Rabbit project, a research effort led 84debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * by CERN, the European Institute for Nuclear Research. 94debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini */ 104debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/module.h> 114debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/init.h> 124debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/list.h> 134debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/slab.h> 144debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/fs.h> 154debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/miscdevice.h> 164debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/spinlock.h> 174debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/fmc.h> 184debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/uaccess.h> 194debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 204debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic LIST_HEAD(fc_devices); 214debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic DEFINE_SPINLOCK(fc_lock); 224debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 234debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistruct fc_instance { 244debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct list_head list; 254debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fmc_device *fmc; 264debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct miscdevice misc; 274debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}; 284debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 294debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* at open time, we must identify our device */ 304debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_open(struct inode *ino, struct file *f) 314debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 324debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fmc_device *fmc; 334debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fc_instance *fc; 344debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini int minor = iminor(ino); 354debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 364debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini list_for_each_entry(fc, &fc_devices, list) 374debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (fc->misc.minor == minor) 384debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini break; 394debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (fc->misc.minor != minor) 404debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -ENODEV; 414debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fmc = fc->fmc; 424debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (try_module_get(fmc->owner) == 0) 434debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -ENODEV; 444debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 454debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini f->private_data = fmc; 464debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return 0; 474debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 484debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 494debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_release(struct inode *ino, struct file *f) 504debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 514debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fmc_device *fmc = f->private_data; 524debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini module_put(fmc->owner); 534debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return 0; 544debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 554debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 564debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* read and write are simple after the default llseek has been used */ 574debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic ssize_t fc_read(struct file *f, char __user *buf, size_t count, 584debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini loff_t *offp) 594debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 604debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fmc_device *fmc = f->private_data; 614debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini unsigned long addr; 624debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini uint32_t val; 634debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 644debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (count < sizeof(val)) 654debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -EINVAL; 664debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini count = sizeof(val); 674debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 684debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini addr = *offp; 694debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (addr > fmc->memlen) 704debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -ESPIPE; /* Illegal seek */ 714debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini val = fmc_readl(fmc, addr); 724debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (copy_to_user(buf, &val, count)) 734debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -EFAULT; 744debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini *offp += count; 754debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return count; 764debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 774debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 784debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic ssize_t fc_write(struct file *f, const char __user *buf, size_t count, 794debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini loff_t *offp) 804debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 814debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fmc_device *fmc = f->private_data; 824debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini unsigned long addr; 834debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini uint32_t val; 844debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 854debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (count < sizeof(val)) 864debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -EINVAL; 874debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini count = sizeof(val); 884debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 894debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini addr = *offp; 904debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (addr > fmc->memlen) 914debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -ESPIPE; /* Illegal seek */ 924debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (copy_from_user(&val, buf, count)) 934debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -EFAULT; 944debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fmc_writel(fmc, val, addr); 954debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini *offp += count; 964debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return count; 974debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 984debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 994debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic const struct file_operations fc_fops = { 1004debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .owner = THIS_MODULE, 1014debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .open = fc_open, 1024debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .release = fc_release, 1034debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .llseek = generic_file_llseek, 1044debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .read = fc_read, 1054debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .write = fc_write, 1064debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}; 1074debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1084debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1094debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* Device part .. */ 1104debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_probe(struct fmc_device *fmc); 1114debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_remove(struct fmc_device *fmc); 1124debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1134debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic struct fmc_driver fc_drv = { 1144debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .version = FMC_VERSION, 1154debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .driver.name = KBUILD_MODNAME, 1164debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .probe = fc_probe, 1174debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini .remove = fc_remove, 1184debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini /* no table: we want to match everything */ 1194debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}; 1204debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1214debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* We accept the generic busid parameter */ 1224debfe409b6e550032bfef9733e9f6f7c5613617Alessandro RubiniFMC_PARAM_BUSID(fc_drv); 1234debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1244debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* probe and remove must allocate and release a misc device */ 1254debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_probe(struct fmc_device *fmc) 1264debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 1274debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini int ret; 1284debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini int index = 0; 1294debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1304debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fc_instance *fc; 1314debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1324debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (fmc->op->validate) 1334debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini index = fmc->op->validate(fmc, &fc_drv); 1344debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (index < 0) 1354debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -EINVAL; /* not our device: invalid */ 1364debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1374debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini /* Create a char device: we want to create it anew */ 1384debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fc = kzalloc(sizeof(*fc), GFP_KERNEL); 1394640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter if (!fc) 1404640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter return -ENOMEM; 1414debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fc->fmc = fmc; 1424debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fc->misc.minor = MISC_DYNAMIC_MINOR; 1434debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fc->misc.fops = &fc_fops; 1444debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL); 1454debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1464debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini ret = misc_register(&fc->misc); 1474640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter if (ret < 0) 148783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubini goto out; 149783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubini spin_lock(&fc_lock); 1504640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter list_add(&fc->list, &fc_devices); 1514debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini spin_unlock(&fc_lock); 1524debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n", 1534debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fc->misc.name); 1544640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter return 0; 1554640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter 156783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubiniout: 1574640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter kfree(fc->misc.name); 1584640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter kfree(fc); 1594debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return ret; 1604debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 1614debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1624debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_remove(struct fmc_device *fmc) 1634debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 1644debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini struct fc_instance *fc; 1654debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1664debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini list_for_each_entry(fc, &fc_devices, list) 1674debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (fc->fmc == fmc) 1684debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini break; 1694debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini if (fc->fmc != fmc) { 1704debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini dev_err(&fmc->dev, "remove called but not found\n"); 1714debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return -ENODEV; 1724debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini } 1734debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1744debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini spin_lock(&fc_lock); 1754debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini list_del(&fc->list); 176783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubini spin_unlock(&fc_lock); 1774debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini misc_deregister(&fc->misc); 1784debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini kfree(fc->misc.name); 1794debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini kfree(fc); 1804debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1814debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return 0; 1824debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 1834debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1844debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1854debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_init(void) 1864debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 1874debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini int ret; 1884debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1894debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini ret = fmc_driver_register(&fc_drv); 1904debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini return ret; 1914debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 1924debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1934debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic void fc_exit(void) 1944debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{ 1954debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini fmc_driver_unregister(&fc_drv); 1964debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini} 1974debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 1984debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinimodule_init(fc_init); 1994debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinimodule_exit(fc_exit); 2004debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini 2014debfe409b6e550032bfef9733e9f6f7c5613617Alessandro RubiniMODULE_LICENSE("GPL"); 202