fmc-chardev.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. * Copyright (C) 2012 CERN (www.cern.ch)
  3. * Author: Alessandro Rubini <rubini@gnudd.com>
  4. *
  5. * Released according to the GNU GPL, version 2 or any later version.
  6. *
  7. * This work is part of the White Rabbit project, a research effort led
  8. * by CERN, the European Institute for Nuclear Research.
  9. */
  10. #include <linux/module.h>
  11. #include <linux/init.h>
  12. #include <linux/list.h>
  13. #include <linux/slab.h>
  14. #include <linux/fs.h>
  15. #include <linux/miscdevice.h>
  16. #include <linux/spinlock.h>
  17. #include <linux/fmc.h>
  18. #include <linux/uaccess.h>
  19. static LIST_HEAD(fc_devices);
  20. static DEFINE_SPINLOCK(fc_lock);
  21. struct fc_instance {
  22. struct list_head list;
  23. struct fmc_device *fmc;
  24. struct miscdevice misc;
  25. };
  26. /* at open time, we must identify our device */
  27. static int fc_open(struct inode *ino, struct file *f)
  28. {
  29. struct fmc_device *fmc;
  30. struct fc_instance *fc;
  31. int minor = iminor(ino);
  32. list_for_each_entry(fc, &fc_devices, list)
  33. if (fc->misc.minor == minor)
  34. break;
  35. if (fc->misc.minor != minor)
  36. return -ENODEV;
  37. fmc = fc->fmc;
  38. if (try_module_get(fmc->owner) == 0)
  39. return -ENODEV;
  40. f->private_data = fmc;
  41. return 0;
  42. }
  43. static int fc_release(struct inode *ino, struct file *f)
  44. {
  45. struct fmc_device *fmc = f->private_data;
  46. module_put(fmc->owner);
  47. return 0;
  48. }
  49. /* read and write are simple after the default llseek has been used */
  50. static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
  51. loff_t *offp)
  52. {
  53. struct fmc_device *fmc = f->private_data;
  54. unsigned long addr;
  55. uint32_t val;
  56. if (count < sizeof(val))
  57. return -EINVAL;
  58. count = sizeof(val);
  59. addr = *offp;
  60. if (addr > fmc->memlen)
  61. return -ESPIPE; /* Illegal seek */
  62. val = fmc_readl(fmc, addr);
  63. if (copy_to_user(buf, &val, count))
  64. return -EFAULT;
  65. *offp += count;
  66. return count;
  67. }
  68. static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
  69. loff_t *offp)
  70. {
  71. struct fmc_device *fmc = f->private_data;
  72. unsigned long addr;
  73. uint32_t val;
  74. if (count < sizeof(val))
  75. return -EINVAL;
  76. count = sizeof(val);
  77. addr = *offp;
  78. if (addr > fmc->memlen)
  79. return -ESPIPE; /* Illegal seek */
  80. if (copy_from_user(&val, buf, count))
  81. return -EFAULT;
  82. fmc_writel(fmc, val, addr);
  83. *offp += count;
  84. return count;
  85. }
  86. static const struct file_operations fc_fops = {
  87. .owner = THIS_MODULE,
  88. .open = fc_open,
  89. .release = fc_release,
  90. .llseek = generic_file_llseek,
  91. .read = fc_read,
  92. .write = fc_write,
  93. };
  94. /* Device part .. */
  95. static int fc_probe(struct fmc_device *fmc);
  96. static int fc_remove(struct fmc_device *fmc);
  97. static struct fmc_driver fc_drv = {
  98. .version = FMC_VERSION,
  99. .driver.name = KBUILD_MODNAME,
  100. .probe = fc_probe,
  101. .remove = fc_remove,
  102. /* no table: we want to match everything */
  103. };
  104. /* We accept the generic busid parameter */
  105. FMC_PARAM_BUSID(fc_drv);
  106. /* probe and remove must allocate and release a misc device */
  107. static int fc_probe(struct fmc_device *fmc)
  108. {
  109. int ret;
  110. int index = 0;
  111. struct fc_instance *fc;
  112. if (fmc->op->validate)
  113. index = fmc->op->validate(fmc, &fc_drv);
  114. if (index < 0)
  115. return -EINVAL; /* not our device: invalid */
  116. /* Create a char device: we want to create it anew */
  117. fc = kzalloc(sizeof(*fc), GFP_KERNEL);
  118. if (!fc)
  119. return -ENOMEM;
  120. fc->fmc = fmc;
  121. fc->misc.minor = MISC_DYNAMIC_MINOR;
  122. fc->misc.fops = &fc_fops;
  123. fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
  124. ret = misc_register(&fc->misc);
  125. if (ret < 0)
  126. goto out;
  127. spin_lock(&fc_lock);
  128. list_add(&fc->list, &fc_devices);
  129. spin_unlock(&fc_lock);
  130. dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
  131. fc->misc.name);
  132. return 0;
  133. out:
  134. kfree(fc->misc.name);
  135. kfree(fc);
  136. return ret;
  137. }
  138. static int fc_remove(struct fmc_device *fmc)
  139. {
  140. struct fc_instance *fc;
  141. list_for_each_entry(fc, &fc_devices, list)
  142. if (fc->fmc == fmc)
  143. break;
  144. if (fc->fmc != fmc) {
  145. dev_err(&fmc->dev, "remove called but not found\n");
  146. return -ENODEV;
  147. }
  148. spin_lock(&fc_lock);
  149. list_del(&fc->list);
  150. spin_unlock(&fc_lock);
  151. misc_deregister(&fc->misc);
  152. kfree(fc->misc.name);
  153. kfree(fc);
  154. return 0;
  155. }
  156. static int fc_init(void)
  157. {
  158. int ret;
  159. ret = fmc_driver_register(&fc_drv);
  160. return ret;
  161. }
  162. static void fc_exit(void)
  163. {
  164. fmc_driver_unregister(&fc_drv);
  165. }
  166. module_init(fc_init);
  167. module_exit(fc_exit);
  168. MODULE_LICENSE("GPL");