sysfs.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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 "compat.h"
  21. #include "config.h"
  22. #include "pciDriver.h"
  23. #include "common.h"
  24. #include "umem.h"
  25. #include "kmem.h"
  26. #include "sysfs.h"
  27. static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry);
  28. static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry);
  29. /**
  30. *
  31. * Initializes the sysfs attributes for an kmem/umem-entry
  32. *
  33. */
  34. static int _pcidriver_sysfs_initialize(pcidriver_privdata_t *privdata,
  35. int id,
  36. struct class_device_attribute *sysfs_attr,
  37. const char *fmtstring,
  38. SYSFS_GET_FUNCTION((*callback)))
  39. {
  40. /* sysfs attributes for kmem buffers don’t make sense before 2.6.13, as
  41. we have no mmap support before */
  42. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
  43. char namebuffer[16];
  44. /* allocate space for the name of the attribute */
  45. snprintf(namebuffer, sizeof(namebuffer), fmtstring, id);
  46. if ((sysfs_attr->attr.name = kstrdup(namebuffer, GFP_KERNEL)) == NULL)
  47. return -ENOMEM;
  48. sysfs_attr->attr.mode = S_IRUGO;
  49. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
  50. // DS: Shall we lock now while accessing driver data structures???
  51. sysfs_attr->attr.owner = THIS_MODULE;
  52. #endif
  53. sysfs_attr->show = callback;
  54. sysfs_attr->store = NULL;
  55. /* name and add attribute */
  56. if (class_device_create_file(privdata->class_dev, sysfs_attr) != 0)
  57. return -ENXIO; /* Device not configured. Not the really best choice, but hm. */
  58. #endif
  59. return 0;
  60. }
  61. int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr)
  62. {
  63. return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "kbuf%d", pcidriver_show_kmem_entry);
  64. }
  65. int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr)
  66. {
  67. return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "umem%d", pcidriver_show_umem_entry);
  68. }
  69. /**
  70. *
  71. * Removes the file from sysfs and frees the allocated (kstrdup()) memory.
  72. *
  73. */
  74. void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct class_device_attribute *sysfs_attr)
  75. {
  76. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
  77. class_device_remove_file(privdata->class_dev, sysfs_attr);
  78. kfree(sysfs_attr->attr.name);
  79. #endif
  80. }
  81. static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry)
  82. {
  83. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
  84. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  85. /* As we can be sure that attr.name contains a filename which we
  86. * created (see _pcidriver_sysfs_initialize), we do not need to have
  87. * sanity checks but can directly call simple_strtol() */
  88. int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10);
  89. pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id);
  90. if (entry) {
  91. unsigned long addr = entry->cpua;
  92. unsigned long dma_addr = entry->dma_handle;
  93. if (entry->size >= 16) {
  94. pcidriver_kmem_sync_entry(privdata, entry, PCILIB_KMEM_SYNC_FROMDEVICE);
  95. 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));
  96. } else
  97. 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);
  98. } else
  99. return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id);
  100. #else
  101. return 0;
  102. #endif
  103. }
  104. static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry)
  105. {
  106. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
  107. #if 0
  108. pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)cls->class_data;
  109. return snprintf(buf, PAGE_SIZE, "I am in the umem_entry show function, class_device_kobj_name: %s\n", cls->kobj.name);
  110. #endif
  111. return 0;
  112. #else
  113. return 0;
  114. #endif
  115. }
  116. #ifdef ENABLE_IRQ
  117. SYSFS_GET_FUNCTION(pcidriver_show_irq_count)
  118. {
  119. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  120. return snprintf(buf, PAGE_SIZE, "%d\n", privdata->irq_count);
  121. }
  122. SYSFS_GET_FUNCTION(pcidriver_show_irq_queues)
  123. {
  124. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  125. int i, offset;
  126. /* output will be truncated to PAGE_SIZE */
  127. offset = snprintf(buf, PAGE_SIZE, "Queue\tOutstanding IRQs\n");
  128. for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++)
  129. offset += snprintf(buf+offset, PAGE_SIZE-offset, "%d\t%d\n", i, atomic_read(&(privdata->irq_outstanding[i])) );
  130. return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
  131. }
  132. #endif
  133. SYSFS_GET_FUNCTION(pcidriver_show_mmap_mode)
  134. {
  135. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  136. return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_mode);
  137. }
  138. SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode)
  139. {
  140. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  141. int mode = -1;
  142. /* Set the mmap-mode if it is either PCIDRIVER_MMAP_PCI or PCIDRIVER_MMAP_KMEM */
  143. if (sscanf(buf, "%d", &mode) == 1 &&
  144. (mode == PCIDRIVER_MMAP_PCI || mode == PCIDRIVER_MMAP_KMEM))
  145. privdata->mmap_mode = mode;
  146. return strlen(buf);
  147. }
  148. SYSFS_GET_FUNCTION(pcidriver_show_mmap_area)
  149. {
  150. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  151. return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_area);
  152. }
  153. SYSFS_SET_FUNCTION(pcidriver_store_mmap_area)
  154. {
  155. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  156. int temp = -1;
  157. sscanf(buf, "%d", &temp);
  158. if ((temp >= PCIDRIVER_BAR0) && (temp <= PCIDRIVER_BAR5))
  159. privdata->mmap_area = temp;
  160. return strlen(buf);
  161. }
  162. SYSFS_GET_FUNCTION(pcidriver_show_kmem_count)
  163. {
  164. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  165. return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&(privdata->kmem_count)));
  166. }
  167. SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc)
  168. {
  169. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  170. kmem_handle_t kmem_handle;
  171. /* FIXME: guillermo: is validation of parsing an unsigned int enough? */
  172. if (sscanf(buf, "%lu", &kmem_handle.size) == 1)
  173. pcidriver_kmem_alloc(privdata, &kmem_handle);
  174. return strlen(buf);
  175. }
  176. SYSFS_SET_FUNCTION(pcidriver_store_kmem_free)
  177. {
  178. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  179. unsigned int id;
  180. pcidriver_kmem_entry_t *kmem_entry;
  181. /* Parse the ID of the kernel memory to be freed, check bounds */
  182. if (sscanf(buf, "%u", &id) != 1 ||
  183. (id >= atomic_read(&(privdata->kmem_count))))
  184. goto err;
  185. if ((kmem_entry = pcidriver_kmem_find_entry_id(privdata,id)) == NULL)
  186. goto err;
  187. pcidriver_kmem_free_entry(privdata, kmem_entry );
  188. err:
  189. return strlen(buf);
  190. }
  191. SYSFS_GET_FUNCTION(pcidriver_show_kbuffers)
  192. {
  193. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  194. int offset = 0;
  195. struct list_head *ptr;
  196. pcidriver_kmem_entry_t *entry;
  197. /* print the header */
  198. offset += snprintf(buf, PAGE_SIZE, "kbuf#\tcpu addr\tsize\n");
  199. spin_lock(&(privdata->kmemlist_lock));
  200. list_for_each(ptr, &(privdata->kmem_list)) {
  201. entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
  202. /* print entry info */
  203. if (offset > PAGE_SIZE) {
  204. spin_unlock( &(privdata->kmemlist_lock) );
  205. return PAGE_SIZE;
  206. }
  207. offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%08lx\t%lu\n", entry->id, (unsigned long)(entry->dma_handle), entry->size );
  208. }
  209. spin_unlock(&(privdata->kmemlist_lock));
  210. /* output will be truncated to PAGE_SIZE */
  211. return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
  212. }
  213. SYSFS_GET_FUNCTION(pcidriver_show_umappings)
  214. {
  215. int offset = 0;
  216. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  217. struct list_head *ptr;
  218. pcidriver_umem_entry_t *entry;
  219. /* print the header */
  220. offset += snprintf(buf, PAGE_SIZE, "umap#\tn_pages\tsg_ents\n");
  221. spin_lock( &(privdata->umemlist_lock) );
  222. list_for_each( ptr, &(privdata->umem_list) ) {
  223. entry = list_entry(ptr, pcidriver_umem_entry_t, list );
  224. /* print entry info */
  225. if (offset > PAGE_SIZE) {
  226. spin_unlock( &(privdata->umemlist_lock) );
  227. return PAGE_SIZE;
  228. }
  229. offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%lu\t%lu\n", entry->id,
  230. (unsigned long)(entry->nr_pages), (unsigned long)(entry->nents));
  231. }
  232. spin_unlock( &(privdata->umemlist_lock) );
  233. /* output will be truncated to PAGE_SIZE */
  234. return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
  235. }
  236. SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap)
  237. {
  238. pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
  239. pcidriver_umem_entry_t *umem_entry;
  240. unsigned int id;
  241. if (sscanf(buf, "%u", &id) != 1 ||
  242. (id >= atomic_read(&(privdata->umem_count))))
  243. goto err;
  244. if ((umem_entry = pcidriver_umem_find_entry_id(privdata, id)) == NULL)
  245. goto err;
  246. pcidriver_umem_sgunmap(privdata, umem_entry);
  247. err:
  248. return strlen(buf);
  249. }