sysfs.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /**
  2. *
  3. * @file sysfs.c
  4. * @brief This file contains the functions providing the SysFS-interface.
  5. * @author Guillermo Marcus
  6. * @date 2010-03-01
  7. *
  8. */
  9. #include <linux/version.h>
  10. #include <linux/string.h>
  11. #include <linux/types.h>
  12. #include <linux/list.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/pci.h>
  15. #include <linux/cdev.h>
  16. #include <linux/wait.h>
  17. #include <linux/mm.h>
  18. #include <linux/pagemap.h>
  19. #include <linux/kernel.h>
  20. #include "base.h"
  21. #define SYSFS_GET_PRIVDATA dev_get_drvdata(dev)
  22. #define SYSFS_GET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, char *buf)
  23. #define SYSFS_SET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
  24. #define SYSFS_ATTR_NAME(name) (dev_attr_##name)
  25. #define SYSFS_ATTR_CREATE(name) do { \
  26. int err = device_create_file(privdata->class_dev, &SYSFS_ATTR_NAME(name)); \
  27. if (err != 0) return err; \
  28. } while (0)
  29. #define SYSFS_ATTR_REMOVE(name) do { \
  30. device_remove_file(privdata->class_dev, &SYSFS_ATTR_NAME(name)); \
  31. } while (0)
  32. #ifdef ENABLE_IRQ
  33. static SYSFS_GET_FUNCTION(pcidriver_show_irq_count)
  34. {
  35. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  36. return snprintf(buf, PAGE_SIZE, "%d\n", privdata->irq_count);
  37. }
  38. static SYSFS_GET_FUNCTION(pcidriver_show_irq_queues)
  39. {
  40. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  41. int i, offset;
  42. /* output will be truncated to PAGE_SIZE */
  43. offset = snprintf(buf, PAGE_SIZE, "Queue\tOutstanding IRQs\n");
  44. for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++)
  45. offset += snprintf(buf+offset, PAGE_SIZE-offset, "%d\t%d\n", i, atomic_read(&(privdata->irq_outstanding[i])) );
  46. return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
  47. }
  48. #endif
  49. static SYSFS_GET_FUNCTION(pcidriver_show_mmap_mode)
  50. {
  51. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  52. return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_mode);
  53. }
  54. static SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode)
  55. {
  56. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  57. int mode = -1;
  58. /* Set the mmap-mode if it is either PCIDRIVER_MMAP_PCI or PCIDRIVER_MMAP_KMEM */
  59. if (sscanf(buf, "%d", &mode) == 1 &&
  60. (mode == PCIDRIVER_MMAP_PCI || mode == PCIDRIVER_MMAP_KMEM))
  61. privdata->mmap_mode = mode;
  62. return strlen(buf);
  63. }
  64. static SYSFS_GET_FUNCTION(pcidriver_show_mmap_area)
  65. {
  66. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  67. return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_area);
  68. }
  69. static SYSFS_SET_FUNCTION(pcidriver_store_mmap_area)
  70. {
  71. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  72. int temp = -1;
  73. sscanf(buf, "%d", &temp);
  74. if ((temp >= PCIDRIVER_BAR0) && (temp <= PCIDRIVER_BAR5))
  75. privdata->mmap_area = temp;
  76. return strlen(buf);
  77. }
  78. static SYSFS_GET_FUNCTION(pcidriver_show_kmem_count)
  79. {
  80. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  81. return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&(privdata->kmem_count)));
  82. }
  83. static SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc)
  84. {
  85. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  86. kmem_handle_t kmem_handle;
  87. /* FIXME: guillermo: is validation of parsing an unsigned int enough? */
  88. if (sscanf(buf, "%lu", &kmem_handle.size) == 1)
  89. pcidriver_kmem_alloc(privdata, &kmem_handle);
  90. return strlen(buf);
  91. }
  92. static SYSFS_SET_FUNCTION(pcidriver_store_kmem_free)
  93. {
  94. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  95. unsigned int id;
  96. pcidriver_kmem_entry_t *kmem_entry;
  97. /* Parse the ID of the kernel memory to be freed, check bounds */
  98. if (sscanf(buf, "%u", &id) != 1 ||
  99. (id >= atomic_read(&(privdata->kmem_count))))
  100. goto err;
  101. if ((kmem_entry = pcidriver_kmem_find_entry_id(privdata,id)) == NULL)
  102. goto err;
  103. pcidriver_kmem_free_entry(privdata, kmem_entry );
  104. err:
  105. return strlen(buf);
  106. }
  107. static SYSFS_GET_FUNCTION(pcidriver_show_kbuffers)
  108. {
  109. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  110. int offset = 0;
  111. struct list_head *ptr;
  112. pcidriver_kmem_entry_t *entry;
  113. /* print the header */
  114. offset += snprintf(buf, PAGE_SIZE, "kbuf#\tcpu addr\tsize\n");
  115. spin_lock(&(privdata->kmemlist_lock));
  116. list_for_each(ptr, &(privdata->kmem_list)) {
  117. entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
  118. /* print entry info */
  119. if (offset > PAGE_SIZE) {
  120. spin_unlock( &(privdata->kmemlist_lock) );
  121. return PAGE_SIZE;
  122. }
  123. offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%08lx\t%lu\n", entry->id, (unsigned long)(entry->dma_handle), entry->size );
  124. }
  125. spin_unlock(&(privdata->kmemlist_lock));
  126. /* output will be truncated to PAGE_SIZE */
  127. return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
  128. }
  129. static SYSFS_GET_FUNCTION(pcidriver_show_umappings)
  130. {
  131. int offset = 0;
  132. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  133. struct list_head *ptr;
  134. pcidriver_umem_entry_t *entry;
  135. /* print the header */
  136. offset += snprintf(buf, PAGE_SIZE, "umap#\tn_pages\tsg_ents\n");
  137. spin_lock( &(privdata->umemlist_lock) );
  138. list_for_each( ptr, &(privdata->umem_list) ) {
  139. entry = list_entry(ptr, pcidriver_umem_entry_t, list );
  140. /* print entry info */
  141. if (offset > PAGE_SIZE) {
  142. spin_unlock( &(privdata->umemlist_lock) );
  143. return PAGE_SIZE;
  144. }
  145. offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%lu\t%lu\n", entry->id,
  146. (unsigned long)(entry->nr_pages), (unsigned long)(entry->nents));
  147. }
  148. spin_unlock( &(privdata->umemlist_lock) );
  149. /* output will be truncated to PAGE_SIZE */
  150. return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
  151. }
  152. static SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap)
  153. {
  154. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  155. pcidriver_umem_entry_t *umem_entry;
  156. unsigned int id;
  157. if (sscanf(buf, "%u", &id) != 1 ||
  158. (id >= atomic_read(&(privdata->umem_count))))
  159. goto err;
  160. if ((umem_entry = pcidriver_umem_find_entry_id(privdata, id)) == NULL)
  161. goto err;
  162. pcidriver_umem_sgunmap(privdata, umem_entry);
  163. err:
  164. return strlen(buf);
  165. }
  166. static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry)
  167. {
  168. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  169. /* As we can be sure that attr.name contains a filename which we
  170. * created (see _pcidriver_sysfs_initialize), we do not need to have
  171. * sanity checks but can directly call simple_strtol() */
  172. int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10);
  173. pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id);
  174. if (entry) {
  175. unsigned long addr = entry->cpua;
  176. unsigned long dma_addr = entry->dma_handle;
  177. if (entry->size >= 16) {
  178. pcidriver_kmem_sync_entry(privdata, entry, PCILIB_KMEM_SYNC_FROMDEVICE);
  179. return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\ndata: %8x %8x %8x %8x\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode, *(u32*)(entry->cpua), *(u32*)(entry->cpua + 4), *(u32*)(entry->cpua + 8), *(u32*)(entry->cpua + 12));
  180. } else
  181. return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode);
  182. } else
  183. return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id);
  184. }
  185. static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry)
  186. {
  187. return 0;
  188. }
  189. #ifdef ENABLE_IRQ
  190. static DEVICE_ATTR(irq_count, S_IRUGO, pcidriver_show_irq_count, NULL);
  191. static DEVICE_ATTR(irq_queues, S_IRUGO, pcidriver_show_irq_queues, NULL);
  192. #endif
  193. static DEVICE_ATTR(mmap_mode, 0664, pcidriver_show_mmap_mode, pcidriver_store_mmap_mode);
  194. static DEVICE_ATTR(mmap_area, 0664, pcidriver_show_mmap_area, pcidriver_store_mmap_area);
  195. static DEVICE_ATTR(kmem_count, 0444, pcidriver_show_kmem_count, NULL);
  196. static DEVICE_ATTR(kbuffers, 0444, pcidriver_show_kbuffers, NULL);
  197. static DEVICE_ATTR(kmem_alloc, 0220, NULL, pcidriver_store_kmem_alloc);
  198. static DEVICE_ATTR(kmem_free, 0220, NULL, pcidriver_store_kmem_free);
  199. static DEVICE_ATTR(umappings, 0444, pcidriver_show_umappings, NULL);
  200. static DEVICE_ATTR(umem_unmap, 0220, NULL, pcidriver_store_umem_unmap);
  201. int pcidriver_create_sysfs_attributes(pcidriver_privdata_t *privdata) {
  202. #ifdef ENABLE_IRQ
  203. SYSFS_ATTR_CREATE(irq_count);
  204. SYSFS_ATTR_CREATE(irq_queues);
  205. #endif
  206. SYSFS_ATTR_CREATE(mmap_mode);
  207. SYSFS_ATTR_CREATE(mmap_area);
  208. SYSFS_ATTR_CREATE(kmem_count);
  209. SYSFS_ATTR_CREATE(kmem_alloc);
  210. SYSFS_ATTR_CREATE(kmem_free);
  211. SYSFS_ATTR_CREATE(kbuffers);
  212. SYSFS_ATTR_CREATE(umappings);
  213. SYSFS_ATTR_CREATE(umem_unmap);
  214. return 0;
  215. }
  216. void pcidriver_remove_sysfs_attributes(pcidriver_privdata_t *privdata) {
  217. #ifdef ENABLE_IRQ
  218. SYSFS_ATTR_REMOVE(irq_count);
  219. SYSFS_ATTR_REMOVE(irq_queues);
  220. #endif
  221. SYSFS_ATTR_REMOVE(mmap_mode);
  222. SYSFS_ATTR_REMOVE(mmap_area);
  223. SYSFS_ATTR_REMOVE(kmem_count);
  224. SYSFS_ATTR_REMOVE(kmem_alloc);
  225. SYSFS_ATTR_REMOVE(kmem_free);
  226. SYSFS_ATTR_REMOVE(kbuffers);
  227. SYSFS_ATTR_REMOVE(umappings);
  228. SYSFS_ATTR_REMOVE(umem_unmap);
  229. }
  230. /**
  231. *
  232. * Removes the file from sysfs and frees the allocated (kstrdup()) memory.
  233. *
  234. */
  235. void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct device_attribute *sysfs_attr)
  236. {
  237. device_remove_file(privdata->class_dev, sysfs_attr);
  238. kfree(sysfs_attr->attr.name);
  239. }
  240. /**
  241. *
  242. * Initializes the sysfs attributes for an kmem/umem-entry
  243. *
  244. */
  245. static int _pcidriver_sysfs_initialize(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr, const char *fmtstring, SYSFS_GET_FUNCTION((*callback)))
  246. {
  247. /* sysfs attributes for kmem buffers don’t make sense before 2.6.13, as
  248. we have no mmap support before */
  249. char namebuffer[16];
  250. /* allocate space for the name of the attribute */
  251. snprintf(namebuffer, sizeof(namebuffer), fmtstring, id);
  252. if ((sysfs_attr->attr.name = kstrdup(namebuffer, GFP_KERNEL)) == NULL)
  253. return -ENOMEM;
  254. sysfs_attr->attr.mode = S_IRUGO;
  255. sysfs_attr->show = callback;
  256. sysfs_attr->store = NULL;
  257. /* name and add attribute */
  258. if (device_create_file(privdata->class_dev, sysfs_attr) != 0)
  259. return -ENXIO; /* Device not configured. Not the really best choice, but hm. */
  260. return 0;
  261. }
  262. int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr)
  263. {
  264. return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "kbuf%d", pcidriver_show_kmem_entry);
  265. }
  266. int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr)
  267. {
  268. return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "umem%d", pcidriver_show_umem_entry);
  269. }