xillybus_class.c 5.4 KB

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