ioctl.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /**
  2. *
  3. * @file ioctl.c
  4. * @author Guillermo Marcus
  5. * @date 2009-04-05
  6. * @brief Contains the functions handling the different ioctl calls.
  7. *
  8. */
  9. #include <linux/version.h>
  10. #include <linux/string.h>
  11. #include <linux/slab.h>
  12. #include <linux/types.h>
  13. #include <linux/init.h>
  14. #include <linux/module.h>
  15. #include <linux/pci.h>
  16. #include <linux/kernel.h>
  17. #include <linux/errno.h>
  18. #include <linux/fs.h>
  19. #include <linux/cdev.h>
  20. #include <linux/sysfs.h>
  21. #include <asm/atomic.h>
  22. #include <linux/pagemap.h>
  23. #include <linux/spinlock.h>
  24. #include <linux/list.h>
  25. #include <linux/scatterlist.h>
  26. #include <linux/vmalloc.h>
  27. #include <linux/stat.h>
  28. #include <linux/interrupt.h>
  29. #include <linux/wait.h>
  30. #include <linux/sched.h>
  31. #include <linux/iommu.h>
  32. #include "pcilib/version.h"
  33. #include "base.h"
  34. /** Declares a variable of the given type with the given name and copies it from userspace */
  35. #define READ_FROM_USER(type, name) \
  36. type name; \
  37. if ((ret = copy_from_user(&name, (type*)arg, sizeof(name))) != 0) \
  38. return -EFAULT;
  39. /** Writes back the given variable with the given type to userspace */
  40. #define WRITE_TO_USER(type, name) \
  41. if ((ret = copy_to_user((type*)arg, &name, sizeof(name))) != 0) \
  42. return -EFAULT;
  43. /**
  44. *
  45. * Sets the mmap mode for following mmap() calls.
  46. *
  47. * @param arg Not a pointer, but either PCIDRIVER_MMAP_PCI or PCIDRIVER_MMAP_KMEM
  48. *
  49. */
  50. static int ioctl_mmap_mode(pcidriver_privdata_t *privdata, unsigned long arg)
  51. {
  52. if ((arg != PCIDRIVER_MMAP_PCI) && (arg != PCIDRIVER_MMAP_KMEM) && (arg != PCIDRIVER_MMAP_AREA))
  53. return -EINVAL;
  54. /* change the mode */
  55. privdata->mmap_mode = arg;
  56. return 0;
  57. }
  58. /**
  59. *
  60. * Sets the mmap area (BAR) for following mmap() calls.
  61. *
  62. */
  63. static int ioctl_mmap_area(pcidriver_privdata_t *privdata, unsigned long arg)
  64. {
  65. /* validate input */
  66. if ((arg < PCIDRIVER_BAR0) || (arg > PCIDRIVER_BAR5))
  67. return -EINVAL;
  68. /* change the PCI area to mmap */
  69. privdata->mmap_area = arg;
  70. return 0;
  71. }
  72. /**
  73. * Reads/writes a byte/word/dword of the device's PCI config.
  74. */
  75. static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned int cmd, unsigned long arg)
  76. {
  77. #ifdef PCIDRIVER_DUMMY_DEVICE
  78. return -ENXIO;
  79. #else /* PCIDRIVER_DUMMY_DEVICE */
  80. int ret;
  81. READ_FROM_USER(pci_cfg_cmd, pci_cmd);
  82. if (cmd == PCIDRIVER_IOC_PCI_CFG_RD) {
  83. switch (pci_cmd.size) {
  84. case PCIDRIVER_PCI_CFG_SZ_BYTE:
  85. ret = pci_read_config_byte( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.byte) );
  86. break;
  87. case PCIDRIVER_PCI_CFG_SZ_WORD:
  88. ret = pci_read_config_word( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.word) );
  89. break;
  90. case PCIDRIVER_PCI_CFG_SZ_DWORD:
  91. ret = pci_read_config_dword( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.dword) );
  92. break;
  93. default:
  94. return -EINVAL; /* Wrong size setting */
  95. }
  96. } else {
  97. switch (pci_cmd.size) {
  98. case PCIDRIVER_PCI_CFG_SZ_BYTE:
  99. ret = pci_write_config_byte( privdata->pdev, pci_cmd.addr, pci_cmd.val.byte );
  100. break;
  101. case PCIDRIVER_PCI_CFG_SZ_WORD:
  102. ret = pci_write_config_word( privdata->pdev, pci_cmd.addr, pci_cmd.val.word );
  103. break;
  104. case PCIDRIVER_PCI_CFG_SZ_DWORD:
  105. ret = pci_write_config_dword( privdata->pdev, pci_cmd.addr, pci_cmd.val.dword );
  106. break;
  107. default:
  108. return -EINVAL; /* Wrong size setting */
  109. break;
  110. }
  111. }
  112. WRITE_TO_USER(pci_cfg_cmd, pci_cmd);
  113. return 0;
  114. #endif /* PCIDRIVER_DUMMY_DEVICE */
  115. }
  116. /**
  117. * Gets the PCI information for the device.
  118. */
  119. static int ioctl_pci_info(pcidriver_privdata_t *privdata, unsigned long arg)
  120. {
  121. int ret;
  122. #ifdef PCIDRIVER_DUMMY_DEVICE
  123. READ_FROM_USER(pcilib_board_info_t, pci_info);
  124. memset(&pci_info, 0, sizeof(pci_info));
  125. WRITE_TO_USER(pcilib_board_info_t, pci_info);
  126. #else /* PCIDRIVER_DUMMY_DEVICE */
  127. int bar;
  128. READ_FROM_USER(pcilib_board_info_t, pci_info);
  129. pci_info.vendor_id = privdata->pdev->vendor;
  130. pci_info.device_id = privdata->pdev->device;
  131. pci_info.bus = privdata->pdev->bus->number;
  132. pci_info.slot = PCI_SLOT(privdata->pdev->devfn);
  133. pci_info.devfn = privdata->pdev->devfn;
  134. pci_info.func = PCI_FUNC(privdata->pdev->devfn);
  135. if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &(pci_info.interrupt_pin))) != 0)
  136. return ret;
  137. if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &(pci_info.interrupt_line))) != 0)
  138. return ret;
  139. for (bar = 0; bar < 6; bar++) {
  140. pci_info.bar_start[bar] = pci_resource_start(privdata->pdev, bar);
  141. pci_info.bar_length[bar] = pci_resource_len(privdata->pdev, bar);
  142. pci_info.bar_flags[bar] = pci_resource_flags(privdata->pdev, bar);
  143. }
  144. WRITE_TO_USER(pcilib_board_info_t, pci_info);
  145. #endif /* PCIDRIVER_DUMMY_DEVICE */
  146. return 0;
  147. }
  148. /**
  149. *
  150. * Allocates kernel memory.
  151. *
  152. * @see pcidriver_kmem_alloc
  153. *
  154. */
  155. static int ioctl_kmem_alloc(pcidriver_privdata_t *privdata, unsigned long arg)
  156. {
  157. int err, ret;
  158. READ_FROM_USER(kmem_handle_t, khandle);
  159. err = pcidriver_kmem_alloc(privdata, &khandle);
  160. WRITE_TO_USER(kmem_handle_t, khandle);
  161. return err;
  162. }
  163. /**
  164. *
  165. * Frees kernel memory.
  166. *
  167. * @see pcidriver_kmem_free
  168. *
  169. */
  170. static int ioctl_kmem_free(pcidriver_privdata_t *privdata, unsigned long arg)
  171. {
  172. int ret;
  173. READ_FROM_USER(kmem_handle_t, khandle);
  174. if ((ret = pcidriver_kmem_free(privdata, &khandle)) != 0)
  175. return ret;
  176. return 0;
  177. }
  178. /**
  179. *
  180. * Syncs kernel memory.
  181. *
  182. * @see pcidriver_kmem_sync
  183. *
  184. */
  185. static int ioctl_kmem_sync(pcidriver_privdata_t *privdata, unsigned long arg)
  186. {
  187. int ret;
  188. READ_FROM_USER(kmem_sync_t, ksync);
  189. if ((ret = pcidriver_kmem_sync(privdata, &ksync)) != 0)
  190. return ret;
  191. WRITE_TO_USER(kmem_sync_t, ksync);
  192. return 0;
  193. }
  194. /*
  195. *
  196. * Maps the given scatter/gather list from memory to PCI bus addresses.
  197. *
  198. * @see pcidriver_umem_sgmap
  199. *
  200. */
  201. static int ioctl_umem_sgmap(pcidriver_privdata_t *privdata, unsigned long arg)
  202. {
  203. int ret;
  204. READ_FROM_USER(umem_handle_t, uhandle);
  205. if ((ret = pcidriver_umem_sgmap(privdata, &uhandle)) != 0)
  206. return ret;
  207. WRITE_TO_USER(umem_handle_t, uhandle);
  208. return 0;
  209. }
  210. /**
  211. *
  212. * Unmaps the given scatter/gather list.
  213. *
  214. * @see pcidriver_umem_sgunmap
  215. *
  216. */
  217. static int ioctl_umem_sgunmap(pcidriver_privdata_t *privdata, unsigned long arg)
  218. {
  219. int ret;
  220. pcidriver_umem_entry_t *umem_entry;
  221. READ_FROM_USER(umem_handle_t, uhandle);
  222. /* Find the associated umem_entry for this buffer,
  223. * return -EINVAL if the specified handle id is invalid */
  224. if ((umem_entry = pcidriver_umem_find_entry_id(privdata, uhandle.handle_id)) == NULL)
  225. return -EINVAL;
  226. if ((ret = pcidriver_umem_sgunmap(privdata, umem_entry)) != 0)
  227. return ret;
  228. return 0;
  229. }
  230. /**
  231. *
  232. * Copies the scatter/gather list from kernelspace to userspace.
  233. *
  234. * @see pcidriver_umem_sgget
  235. *
  236. */
  237. static int ioctl_umem_sgget(pcidriver_privdata_t *privdata, unsigned long arg)
  238. {
  239. int ret;
  240. READ_FROM_USER(umem_sglist_t, usglist);
  241. /* The umem_sglist_t has a pointer to the scatter/gather list itself which
  242. * needs to be copied separately. The number of elements is stored in ->nents.
  243. * As the list can get very big, we need to use vmalloc. */
  244. if ((usglist.sg = vmalloc(usglist.nents * sizeof(umem_sgentry_t))) == NULL)
  245. return -ENOMEM;
  246. /* copy array to kernel structure */
  247. ret = copy_from_user(usglist.sg, ((umem_sglist_t *)arg)->sg, (usglist.nents)*sizeof(umem_sgentry_t));
  248. if (ret) return -EFAULT;
  249. if ((ret = pcidriver_umem_sgget(privdata, &usglist)) != 0)
  250. return ret;
  251. /* write data to user space */
  252. ret = copy_to_user(((umem_sglist_t *)arg)->sg, usglist.sg, (usglist.nents)*sizeof(umem_sgentry_t));
  253. if (ret) return -EFAULT;
  254. /* free array memory */
  255. vfree(usglist.sg);
  256. /* restore sg pointer to vma address in user space before copying */
  257. usglist.sg = ((umem_sglist_t *)arg)->sg;
  258. WRITE_TO_USER(umem_sglist_t, usglist);
  259. return 0;
  260. }
  261. /**
  262. *
  263. * Syncs user memory.
  264. *
  265. * @see pcidriver_umem_sync
  266. *
  267. */
  268. static int ioctl_umem_sync(pcidriver_privdata_t *privdata, unsigned long arg)
  269. {
  270. int ret;
  271. READ_FROM_USER(umem_handle_t, uhandle);
  272. return pcidriver_umem_sync( privdata, &uhandle );
  273. }
  274. /**
  275. *
  276. * Waits for an interrupt
  277. *
  278. * @param arg Not a pointer, but the irq source to wait for (unsigned int)
  279. *
  280. */
  281. static int ioctl_wait_interrupt(pcidriver_privdata_t *privdata, unsigned long arg)
  282. {
  283. #ifdef ENABLE_IRQ
  284. int ret;
  285. unsigned long timeout;
  286. unsigned int irq_source;
  287. unsigned long temp = 0;
  288. READ_FROM_USER(interrupt_wait_t, irq_handle);
  289. irq_source = irq_handle.source;
  290. if (irq_source >= PCIDRIVER_INT_MAXSOURCES)
  291. return -EFAULT; /* User tried to overrun the IRQ_SOURCES array */
  292. timeout = jiffies + (irq_handle.timeout * HZ / 1000000);
  293. /* Thanks to Joern for the correction and tips! */
  294. /* done this way to avoid wrong behaviour (endless loop) of the compiler in AMD platforms */
  295. do {
  296. /* We wait here with an interruptible timeout. This will be interrupted
  297. * by int.c:check_acknowledge_channel() as soon as in interrupt for
  298. * the specified source arrives. */
  299. wait_event_interruptible_timeout( (privdata->irq_queues[irq_source]), (atomic_read(&(privdata->irq_outstanding[irq_source])) > 0), (10*HZ/1000) );
  300. if (atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source])) )
  301. atomic_inc( &(privdata->irq_outstanding[irq_source]) );
  302. else
  303. temp = 1;
  304. } while ((!temp)&&(jiffies < timeout));
  305. if ((temp)&&(irq_handle.count)) {
  306. while (!atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source]))) temp++;
  307. atomic_inc( &(privdata->irq_outstanding[irq_source]) );
  308. }
  309. irq_handle.count = temp;
  310. WRITE_TO_USER(interrupt_wait_t, irq_handle);
  311. return 0;
  312. #else
  313. mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n");
  314. return -EFAULT;
  315. #endif
  316. }
  317. /**
  318. *
  319. * Clears the interrupt wait queue.
  320. *
  321. * @param arg Not a pointer, but the irq source (unsigned int)
  322. * @returns -EFAULT if the user specified an irq source out of range
  323. *
  324. */
  325. static int ioctl_clear_ioq(pcidriver_privdata_t *privdata, unsigned long arg)
  326. {
  327. #ifdef ENABLE_IRQ
  328. unsigned int irq_source;
  329. if (arg >= PCIDRIVER_INT_MAXSOURCES)
  330. return -EFAULT;
  331. irq_source = arg;
  332. atomic_set(&(privdata->irq_outstanding[irq_source]), 0);
  333. return 0;
  334. #else
  335. mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n");
  336. return -EFAULT;
  337. #endif
  338. }
  339. /**
  340. *
  341. * Gets the device and API versions.
  342. *
  343. * @see pcilib_driver_version_t
  344. *
  345. */
  346. static int ioctl_version(pcidriver_privdata_t *privdata, unsigned long arg)
  347. {
  348. int ret;
  349. pcilib_driver_version_t info;
  350. info = (pcilib_driver_version_t) {
  351. .version = PCILIB_VERSION,
  352. .interface = PCIDRIVER_INTERFACE_VERSION,
  353. .ioctls = PCIDRIVER_IOC_MAX + 1
  354. };
  355. WRITE_TO_USER(pcilib_driver_version_t, info);
  356. return 0;
  357. }
  358. /**
  359. *
  360. * Gets current device and driver configuration
  361. *
  362. * @see pcilib_device_state_t
  363. *
  364. */
  365. static int ioctl_device_state(pcidriver_privdata_t *privdata, unsigned long arg)
  366. {
  367. int ret;
  368. pcilib_device_state_t info;
  369. #ifdef PCIDRIVER_DUMMY_DEVICE
  370. memset(&info, 0, sizeof(info));
  371. #else /* PCIDRIVER_DUMMY_DEVICE */
  372. info = (pcilib_device_state_t) {
  373. .iommu = iommu_present(privdata->pdev->dev.bus),
  374. .mps = pcidriver_pcie_get_mps(privdata->pdev),
  375. .readrq = pcie_get_readrq(privdata->pdev),
  376. .dma_mask = privdata->pdev->dma_mask
  377. };
  378. #endif /* PCIDRIVER_DUMMY_DEVICE */
  379. WRITE_TO_USER(pcilib_device_state_t, info);
  380. return 0;
  381. }
  382. /**
  383. *
  384. * Sets DMA mask for the following DMA mappings.
  385. *
  386. * @param arg Not a pointer, but a number of bits
  387. *
  388. */
  389. static int ioctl_set_dma_mask(pcidriver_privdata_t *privdata, unsigned long arg)
  390. {
  391. #ifndef PCIDRIVER_DUMMY_DEVICE
  392. int err;
  393. if ((arg < 24) || (arg > 64))
  394. return -EINVAL;
  395. err = pci_set_dma_mask(privdata->pdev, DMA_BIT_MASK(arg));
  396. if (err < 0) {
  397. printk(KERN_ERR "pci_set_dma_mask(%lu) failed\n", arg);
  398. return err;
  399. }
  400. #endif /* ! PCIDRIVER_DUMMY_DEVICE */
  401. return 0;
  402. }
  403. /**
  404. *
  405. * Sets Max Payload Size.
  406. *
  407. * @param arg Not a pointer, but payload size in bits
  408. *
  409. */
  410. static int ioctl_set_mps(pcidriver_privdata_t *privdata, unsigned long arg)
  411. {
  412. #ifndef PCIDRIVER_DUMMY_DEVICE
  413. int err;
  414. if ((arg != 128) && (arg != 256) && (arg != 512))
  415. return -EINVAL;
  416. err = pcidriver_pcie_set_mps(privdata->pdev, arg);
  417. if (err < 0) {
  418. printk(KERN_ERR "pcie_set_mps(%lu) failed\n", arg);
  419. return err;
  420. }
  421. #endif /* ! PCIDRIVER_DUMMY_DEVICE */
  422. return 0;
  423. }
  424. /**
  425. *
  426. * This function handles all ioctl file operations.
  427. * Generally, the data of the ioctl is copied from userspace to kernelspace, a separate
  428. * function is called to handle the ioctl itself, then the data is copied back to userspace.
  429. *
  430. * @returns -EFAULT when an invalid memory pointer is passed
  431. *
  432. */
  433. long pcidriver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  434. {
  435. pcidriver_privdata_t *privdata = filp->private_data;
  436. /* Select the appropiate command */
  437. switch (cmd) {
  438. case PCIDRIVER_IOC_MMAP_MODE:
  439. return ioctl_mmap_mode(privdata, arg);
  440. case PCIDRIVER_IOC_MMAP_AREA:
  441. return ioctl_mmap_area(privdata, arg);
  442. case PCIDRIVER_IOC_PCI_CFG_RD:
  443. case PCIDRIVER_IOC_PCI_CFG_WR:
  444. return ioctl_pci_config_read_write(privdata, cmd, arg);
  445. case PCIDRIVER_IOC_PCI_INFO:
  446. return ioctl_pci_info(privdata, arg);
  447. case PCIDRIVER_IOC_KMEM_ALLOC:
  448. return ioctl_kmem_alloc(privdata, arg);
  449. case PCIDRIVER_IOC_KMEM_FREE:
  450. return ioctl_kmem_free(privdata, arg);
  451. case PCIDRIVER_IOC_KMEM_SYNC:
  452. return ioctl_kmem_sync(privdata, arg);
  453. case PCIDRIVER_IOC_UMEM_SGMAP:
  454. return ioctl_umem_sgmap(privdata, arg);
  455. case PCIDRIVER_IOC_UMEM_SGUNMAP:
  456. return ioctl_umem_sgunmap(privdata, arg);
  457. case PCIDRIVER_IOC_UMEM_SGGET:
  458. return ioctl_umem_sgget(privdata, arg);
  459. case PCIDRIVER_IOC_UMEM_SYNC:
  460. return ioctl_umem_sync(privdata, arg);
  461. case PCIDRIVER_IOC_WAITI:
  462. return ioctl_wait_interrupt(privdata, arg);
  463. case PCIDRIVER_IOC_CLEAR_IOQ:
  464. return ioctl_clear_ioq(privdata, arg);
  465. case PCIDRIVER_IOC_VERSION:
  466. return ioctl_version(privdata, arg);
  467. case PCIDRIVER_IOC_DEVICE_STATE:
  468. return ioctl_device_state(privdata, arg);
  469. case PCIDRIVER_IOC_DMA_MASK:
  470. return ioctl_set_dma_mask(privdata, arg);
  471. case PCIDRIVER_IOC_MPS:
  472. return ioctl_set_mps(privdata, arg);
  473. default:
  474. return -EINVAL;
  475. }
  476. }