mon_dma.c 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. /*
  2. * The USB Monitor, inspired by Dave Harding's USBMon.
  3. *
  4. * mon_dma.c: Library which snoops on DMA areas.
  5. *
  6. * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com)
  7. */
  8. #include <linux/kernel.h>
  9. #include <linux/list.h>
  10. #include <linux/highmem.h>
  11. #include <asm/page.h>
  12. #include <linux/usb.h> /* Only needed for declarations in usb_mon.h */
  13. #include "usb_mon.h"
  14. /*
  15. * PC-compatibles, are, fortunately, sufficiently cache-coherent for this.
  16. */
  17. #if defined(__i386__) || defined(__x86_64__) /* CONFIG_ARCH_I386 doesn't exit */
  18. #define MON_HAS_UNMAP 1
  19. #define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT)
  20. char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
  21. {
  22. struct page *pg;
  23. unsigned long flags;
  24. unsigned char *map;
  25. unsigned char *ptr;
  26. /*
  27. * On i386, a DMA handle is the "physical" address of a page.
  28. * In other words, the bus address is equal to physical address.
  29. * There is no IOMMU.
  30. */
  31. pg = phys_to_page(dma_addr);
  32. /*
  33. * We are called from hardware IRQs in case of callbacks.
  34. * But we can be called from softirq or process context in case
  35. * of submissions. In such case, we need to protect KM_IRQ0.
  36. */
  37. local_irq_save(flags);
  38. map = kmap_atomic(pg, KM_IRQ0);
  39. ptr = map + (dma_addr & (PAGE_SIZE-1));
  40. memcpy(dst, ptr, len);
  41. kunmap_atomic(map, KM_IRQ0);
  42. local_irq_restore(flags);
  43. return 0;
  44. }
  45. void mon_dmapeek_vec(const struct mon_reader_bin *rp,
  46. unsigned int offset, dma_addr_t dma_addr, unsigned int length)
  47. {
  48. unsigned long flags;
  49. unsigned int step_len;
  50. struct page *pg;
  51. unsigned char *map;
  52. unsigned long page_off, page_len;
  53. local_irq_save(flags);
  54. while (length) {
  55. /* compute number of bytes we are going to copy in this page */
  56. step_len = length;
  57. page_off = dma_addr & (PAGE_SIZE-1);
  58. page_len = PAGE_SIZE - page_off;
  59. if (page_len < step_len)
  60. step_len = page_len;
  61. /* copy data and advance pointers */
  62. pg = phys_to_page(dma_addr);
  63. map = kmap_atomic(pg, KM_IRQ0);
  64. offset = mon_copy_to_buff(rp, offset, map + page_off, step_len);
  65. kunmap_atomic(map, KM_IRQ0);
  66. dma_addr += step_len;
  67. length -= step_len;
  68. }
  69. local_irq_restore(flags);
  70. }
  71. #endif /* __i386__ */
  72. #ifndef MON_HAS_UNMAP
  73. char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
  74. {
  75. return 'D';
  76. }
  77. void mon_dmapeek_vec(const struct mon_reader_bin *rp,
  78. unsigned int offset, dma_addr_t dma_addr, unsigned int length)
  79. {
  80. ;
  81. }
  82. #endif /* MON_HAS_UNMAP */