xillybus_class.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2021 Xillybus Ltd, http://xillybus.com
  4. *
  5. * Driver for the Xillybus class
  6. */
  7. #include <linux/types.h>
  8. #include <linux/module.h>
  9. #include <linux/device.h>
  10. #include <linux/fs.h>
  11. #include <linux/cdev.h>
  12. #include <linux/slab.h>
  13. #include <linux/list.h>
  14. #include <linux/mutex.h>
  15. #include "xillybus_class.h"
  16. MODULE_DESCRIPTION("Driver for Xillybus class");
  17. MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
  18. MODULE_ALIAS("xillybus_class");
  19. MODULE_LICENSE("GPL v2");
  20. static DEFINE_MUTEX(unit_mutex);
  21. static LIST_HEAD(unit_list);
  22. static const struct class xillybus_class = {
  23. .name = "xillybus",
  24. };
  25. #define UNITNAMELEN 16
  26. struct xilly_unit {
  27. struct list_head list_entry;
  28. void *private_data;
  29. struct cdev *cdev;
  30. char name[UNITNAMELEN];
  31. int major;
  32. int lowest_minor;
  33. int num_nodes;
  34. };
  35. int xillybus_init_chrdev(struct device *dev,
  36. const struct file_operations *fops,
  37. struct module *owner,
  38. void *private_data,
  39. unsigned char *idt, unsigned int len,
  40. int num_nodes,
  41. const char *prefix, bool enumerate)
  42. {
  43. int rc;
  44. dev_t mdev;
  45. int i;
  46. char devname[48];
  47. struct device *device;
  48. size_t namelen;
  49. struct xilly_unit *unit, *u;
  50. unit = kzalloc_obj(*unit);
  51. if (!unit)
  52. return -ENOMEM;
  53. mutex_lock(&unit_mutex);
  54. if (!enumerate)
  55. snprintf(unit->name, UNITNAMELEN, "%s", prefix);
  56. for (i = 0; enumerate; i++) {
  57. snprintf(unit->name, UNITNAMELEN, "%s_%02d",
  58. prefix, i);
  59. enumerate = false;
  60. list_for_each_entry(u, &unit_list, list_entry)
  61. if (!strcmp(unit->name, u->name)) {
  62. enumerate = true;
  63. break;
  64. }
  65. }
  66. rc = alloc_chrdev_region(&mdev, 0, num_nodes, unit->name);
  67. if (rc) {
  68. dev_warn(dev, "Failed to obtain major/minors");
  69. goto fail_obtain;
  70. }
  71. unit->major = MAJOR(mdev);
  72. unit->lowest_minor = MINOR(mdev);
  73. unit->num_nodes = num_nodes;
  74. unit->private_data = private_data;
  75. unit->cdev = cdev_alloc();
  76. if (!unit->cdev) {
  77. rc = -ENOMEM;
  78. goto unregister_chrdev;
  79. }
  80. unit->cdev->ops = fops;
  81. unit->cdev->owner = owner;
  82. rc = cdev_add(unit->cdev, MKDEV(unit->major, unit->lowest_minor),
  83. unit->num_nodes);
  84. if (rc) {
  85. dev_err(dev, "Failed to add cdev.\n");
  86. /* kobject_put() is normally done by cdev_del() */
  87. kobject_put(&unit->cdev->kobj);
  88. goto unregister_chrdev;
  89. }
  90. for (i = 0; i < num_nodes; i++) {
  91. namelen = strnlen(idt, len);
  92. if (namelen == len) {
  93. dev_err(dev, "IDT's list of names is too short. This is exceptionally weird, because its CRC is OK\n");
  94. rc = -ENODEV;
  95. goto unroll_device_create;
  96. }
  97. snprintf(devname, sizeof(devname), "%s_%s",
  98. unit->name, idt);
  99. len -= namelen + 1;
  100. idt += namelen + 1;
  101. device = device_create(&xillybus_class,
  102. NULL,
  103. MKDEV(unit->major,
  104. i + unit->lowest_minor),
  105. NULL,
  106. "%s", devname);
  107. if (IS_ERR(device)) {
  108. dev_err(dev, "Failed to create %s device. Aborting.\n",
  109. devname);
  110. rc = -ENODEV;
  111. goto unroll_device_create;
  112. }
  113. }
  114. if (len) {
  115. dev_err(dev, "IDT's list of names is too long. This is exceptionally weird, because its CRC is OK\n");
  116. rc = -ENODEV;
  117. goto unroll_device_create;
  118. }
  119. list_add_tail(&unit->list_entry, &unit_list);
  120. dev_info(dev, "Created %d device files.\n", num_nodes);
  121. mutex_unlock(&unit_mutex);
  122. return 0;
  123. unroll_device_create:
  124. for (i--; i >= 0; i--)
  125. device_destroy(&xillybus_class, MKDEV(unit->major,
  126. i + unit->lowest_minor));
  127. cdev_del(unit->cdev);
  128. unregister_chrdev:
  129. unregister_chrdev_region(MKDEV(unit->major, unit->lowest_minor),
  130. unit->num_nodes);
  131. fail_obtain:
  132. mutex_unlock(&unit_mutex);
  133. kfree(unit);
  134. return rc;
  135. }
  136. EXPORT_SYMBOL(xillybus_init_chrdev);
  137. void xillybus_cleanup_chrdev(void *private_data,
  138. struct device *dev)
  139. {
  140. int minor;
  141. struct xilly_unit *unit = NULL, *iter;
  142. mutex_lock(&unit_mutex);
  143. list_for_each_entry(iter, &unit_list, list_entry)
  144. if (iter->private_data == private_data) {
  145. unit = iter;
  146. break;
  147. }
  148. if (!unit) {
  149. dev_err(dev, "Weird bug: Failed to find unit\n");
  150. mutex_unlock(&unit_mutex);
  151. return;
  152. }
  153. for (minor = unit->lowest_minor;
  154. minor < (unit->lowest_minor + unit->num_nodes);
  155. minor++)
  156. device_destroy(&xillybus_class, MKDEV(unit->major, minor));
  157. cdev_del(unit->cdev);
  158. unregister_chrdev_region(MKDEV(unit->major, unit->lowest_minor),
  159. unit->num_nodes);
  160. dev_info(dev, "Removed %d device files.\n",
  161. unit->num_nodes);
  162. list_del(&unit->list_entry);
  163. kfree(unit);
  164. mutex_unlock(&unit_mutex);
  165. }
  166. EXPORT_SYMBOL(xillybus_cleanup_chrdev);
  167. int xillybus_find_inode(struct inode *inode,
  168. void **private_data, int *index)
  169. {
  170. int minor = iminor(inode);
  171. int major = imajor(inode);
  172. struct xilly_unit *unit = NULL, *iter;
  173. mutex_lock(&unit_mutex);
  174. list_for_each_entry(iter, &unit_list, list_entry)
  175. if (iter->major == major &&
  176. minor >= iter->lowest_minor &&
  177. minor < (iter->lowest_minor + iter->num_nodes)) {
  178. unit = iter;
  179. break;
  180. }
  181. if (!unit) {
  182. mutex_unlock(&unit_mutex);
  183. return -ENODEV;
  184. }
  185. *private_data = unit->private_data;
  186. *index = minor - unit->lowest_minor;
  187. mutex_unlock(&unit_mutex);
  188. return 0;
  189. }
  190. EXPORT_SYMBOL(xillybus_find_inode);
  191. static int __init xillybus_class_init(void)
  192. {
  193. return class_register(&xillybus_class);
  194. }
  195. static void __exit xillybus_class_exit(void)
  196. {
  197. class_unregister(&xillybus_class);
  198. }
  199. module_init(xillybus_class_init);
  200. module_exit(xillybus_class_exit);