shared_dma.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. * This file is free software: you can redistribute it and/or modify it
  3. * under the terms of the GNU General Public License as published by the
  4. * Free Software Foundation, either version 3 of the License, or
  5. * (at your option) any later version.
  6. *
  7. * This file is distributed in the hope that it will be useful, but
  8. * WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. * See the GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License along
  13. * with this program. If not, see <http://www.gnu.org/licenses/>.
  14. *
  15. * Code by Andrew Tridgell and Siddharth Bharat Purohit
  16. */
  17. #include "shared_dma.h"
  18. /*
  19. code to handle sharing of DMA channels between peripherals
  20. */
  21. #if CH_CFG_USE_SEMAPHORES == TRUE
  22. using namespace ChibiOS;
  23. Shared_DMA::dma_lock Shared_DMA::locks[SHARED_DMA_MAX_STREAM_ID+1];
  24. void Shared_DMA::init(void)
  25. {
  26. for (uint8_t i=0; i<SHARED_DMA_MAX_STREAM_ID; i++) {
  27. chBSemObjectInit(&locks[i].semaphore, false);
  28. }
  29. }
  30. // constructor
  31. Shared_DMA::Shared_DMA(uint8_t _stream_id1,
  32. uint8_t _stream_id2,
  33. dma_allocate_fn_t _allocate,
  34. dma_deallocate_fn_t _deallocate)
  35. {
  36. stream_id1 = _stream_id1;
  37. stream_id2 = _stream_id2;
  38. allocate = _allocate;
  39. deallocate = _deallocate;
  40. }
  41. //remove any assigned deallocator or allocator
  42. void Shared_DMA::unregister()
  43. {
  44. if (stream_id1 < SHARED_DMA_MAX_STREAM_ID &&
  45. locks[stream_id1].obj == this) {
  46. locks[stream_id1].deallocate(this);
  47. locks[stream_id1].obj = nullptr;
  48. }
  49. if (stream_id2 < SHARED_DMA_MAX_STREAM_ID &&
  50. locks[stream_id2].obj == this) {
  51. locks[stream_id2].deallocate(this);
  52. locks[stream_id2].obj = nullptr;
  53. }
  54. }
  55. // lock one stream
  56. void Shared_DMA::lock_stream(uint8_t stream_id)
  57. {
  58. if (stream_id < SHARED_DMA_MAX_STREAM_ID) {
  59. chBSemWait(&locks[stream_id].semaphore);
  60. }
  61. }
  62. // unlock one stream
  63. void Shared_DMA::unlock_stream(uint8_t stream_id)
  64. {
  65. if (stream_id < SHARED_DMA_MAX_STREAM_ID) {
  66. chBSemSignal(&locks[stream_id].semaphore);
  67. }
  68. }
  69. // unlock one stream from an IRQ handler
  70. void Shared_DMA::unlock_stream_from_IRQ(uint8_t stream_id)
  71. {
  72. if (stream_id < SHARED_DMA_MAX_STREAM_ID) {
  73. chBSemSignalI(&locks[stream_id].semaphore);
  74. }
  75. }
  76. // lock one stream, non-blocking
  77. bool Shared_DMA::lock_stream_nonblocking(uint8_t stream_id)
  78. {
  79. if (stream_id < SHARED_DMA_MAX_STREAM_ID) {
  80. return chBSemWaitTimeout(&locks[stream_id].semaphore, 1) == MSG_OK;
  81. }
  82. return true;
  83. }
  84. // lock the DMA channels
  85. void Shared_DMA::lock_core(void)
  86. {
  87. // see if another driver has DMA allocated. If so, call their
  88. // deallocation function
  89. if (stream_id1 < SHARED_DMA_MAX_STREAM_ID &&
  90. locks[stream_id1].obj && locks[stream_id1].obj != this) {
  91. locks[stream_id1].deallocate(locks[stream_id1].obj);
  92. locks[stream_id1].obj = nullptr;
  93. }
  94. if (stream_id2 < SHARED_DMA_MAX_STREAM_ID &&
  95. locks[stream_id2].obj && locks[stream_id2].obj != this) {
  96. locks[stream_id2].deallocate(locks[stream_id2].obj);
  97. locks[stream_id2].obj = nullptr;
  98. }
  99. if ((stream_id1 < SHARED_DMA_MAX_STREAM_ID && locks[stream_id1].obj == nullptr) ||
  100. (stream_id2 < SHARED_DMA_MAX_STREAM_ID && locks[stream_id2].obj == nullptr)) {
  101. // allocate the DMA channels and put our deallocation function in place
  102. allocate(this);
  103. if (stream_id1 < SHARED_DMA_MAX_STREAM_ID) {
  104. locks[stream_id1].deallocate = deallocate;
  105. locks[stream_id1].obj = this;
  106. }
  107. if (stream_id2 < SHARED_DMA_MAX_STREAM_ID) {
  108. locks[stream_id2].deallocate = deallocate;
  109. locks[stream_id2].obj = this;
  110. }
  111. }
  112. #ifdef STM32_DMA_STREAM_ID_ANY
  113. else if (stream_id1 == STM32_DMA_STREAM_ID_ANY ||
  114. stream_id2 == STM32_DMA_STREAM_ID_ANY) {
  115. // call allocator without needing locking
  116. allocate(this);
  117. }
  118. #endif
  119. have_lock = true;
  120. }
  121. // lock the DMA channels, blocking method
  122. void Shared_DMA::lock(void)
  123. {
  124. lock_stream(stream_id1);
  125. lock_stream(stream_id2);
  126. lock_core();
  127. }
  128. // lock the DMA channels, non-blocking
  129. bool Shared_DMA::lock_nonblock(void)
  130. {
  131. if (!lock_stream_nonblocking(stream_id1)) {
  132. chSysDisable();
  133. if (locks[stream_id1].obj != nullptr && locks[stream_id1].obj != this) {
  134. locks[stream_id1].obj->contention = true;
  135. }
  136. chSysEnable();
  137. contention = true;
  138. return false;
  139. }
  140. if (!lock_stream_nonblocking(stream_id2)) {
  141. unlock_stream(stream_id1);
  142. chSysDisable();
  143. if (locks[stream_id2].obj != nullptr && locks[stream_id2].obj != this) {
  144. locks[stream_id2].obj->contention = true;
  145. }
  146. chSysEnable();
  147. contention = true;
  148. return false;
  149. }
  150. lock_core();
  151. return true;
  152. }
  153. // unlock the DMA channels
  154. void Shared_DMA::unlock(void)
  155. {
  156. osalDbgAssert(have_lock, "must have lock");
  157. unlock_stream(stream_id2);
  158. unlock_stream(stream_id1);
  159. have_lock = false;
  160. }
  161. // unlock the DMA channels from a lock zone
  162. void Shared_DMA::unlock_from_lockzone(void)
  163. {
  164. osalDbgAssert(have_lock, "must have lock");
  165. if (stream_id2 < SHARED_DMA_MAX_STREAM_ID) {
  166. unlock_stream_from_IRQ(stream_id2);
  167. chSchRescheduleS();
  168. }
  169. if (stream_id1 < SHARED_DMA_MAX_STREAM_ID) {
  170. unlock_stream_from_IRQ(stream_id1);
  171. chSchRescheduleS();
  172. }
  173. have_lock = false;
  174. }
  175. // unlock the DMA channels from an IRQ
  176. void Shared_DMA::unlock_from_IRQ(void)
  177. {
  178. osalDbgAssert(have_lock, "must have lock");
  179. unlock_stream_from_IRQ(stream_id2);
  180. unlock_stream_from_IRQ(stream_id1);
  181. have_lock = false;
  182. }
  183. /*
  184. lock all channels - used on reboot to ensure no sensor DMA is in
  185. progress
  186. */
  187. void Shared_DMA::lock_all(void)
  188. {
  189. for (uint8_t i=0; i<SHARED_DMA_MAX_STREAM_ID; i++) {
  190. lock_stream(i);
  191. }
  192. }
  193. #endif // CH_CFG_USE_SEMAPHORES