The Pedigree Project 0.1
CdiIrq.cc
1/*
2 * Copyright (c) 2007 Kevin Wolf
3 *
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9 */
10
11#include <stddef.h>
12
13#include "pedigree/kernel/Atomic.h"
14#include "pedigree/kernel/LockGuard.h"
15#include "pedigree/kernel/Log.h"
16#include "pedigree/kernel/compiler.h"
17#include "pedigree/kernel/machine/IrqHandler.h"
18#include "pedigree/kernel/machine/IrqManager.h"
19#include "pedigree/kernel/machine/Machine.h"
20#include "pedigree/kernel/machine/types.h"
21#include "pedigree/kernel/process/Mutex.h"
22#include "pedigree/kernel/process/Semaphore.h"
23#include "pedigree/kernel/processor/types.h"
24
25struct cdi_device;
26
28{
29 public:
30 IrqDisposition irq(irq_id_t number) override;
31};
32
33static CdiIrqHandler cdi_irq_handler;
34
36#define IRQ_COUNT 0x10
37
39{
41 : handler(nullptr), device(nullptr), registration(0), counter(0)
42 {
43 }
44
45 void (*handler)(struct cdi_device *);
46 struct cdi_device *device;
47 irq_id_t registration;
48 Semaphore counter;
49};
50
51static Mutex irqSlotLock;
52static CdiIrqSlot driverIrqs[IRQ_COUNT];
53
54namespace
55{
56size_t resetIrqCounter(Semaphore &counter)
57{
58 return counter.drainAvailable();
59}
60
61int waitForIrqCounter(Semaphore &counter, uint32_t timeout)
62{
63 if (timeout == 0)
64 {
65 return counter.tryAcquire() ? 0 : -3;
66 }
67
68 Semaphore::SemaphoreError error = Semaphore::NoError;
69 const bool acquired = counter.acquireWithError(
70 1, timeout / 1000, (timeout % 1000) * 1000, error);
71 return acquired ? 0 : -3;
72}
73} // namespace
74
79{
80 if (irq >= IRQ_COUNT)
81 {
82 return IrqDisposition::NotHandled;
83 }
84
85 void (*handler)(struct cdi_device *) = nullptr;
86 struct cdi_device *device = nullptr;
87 {
88 LockGuard<Mutex> lock(irqSlotLock);
89 handler = driverIrqs[irq].handler;
90 device = driverIrqs[irq].device;
91 }
92
93 if (!handler)
94 return IrqDisposition::NotHandled;
95
96 driverIrqs[irq].counter.release();
97 handler(device);
98 return IrqDisposition::Handled;
99}
100
101extern "C" {
109EXPORTED_PUBLIC void cdi_register_irq(
110 uint8_t irq, void (*handler)(struct cdi_device *),
111 struct cdi_device *device)
112{
113 if (irq >= IRQ_COUNT)
114 {
115 // FIXME: Eigentlich sollte diese Funktion etwas weniger optimistisch
116 // sein, und einen Rueckgabewert haben.
117 return;
118 }
119 if (!handler)
120 return;
121
122 {
123 LockGuard<Mutex> lock(irqSlotLock);
124 // Der Interrupt wurde schon mal registriert
125 if (driverIrqs[irq].handler)
126 {
127 NOTICE("cdi: Versuch IRQ " << irq << " mehrfach zu registrieren");
128 return;
129 }
130
131 const size_t discarded = resetIrqCounter(driverIrqs[irq].counter);
132 (void) discarded;
133 driverIrqs[irq].handler = handler;
134 driverIrqs[irq].device = device;
135 }
136
137 const irq_id_t registration =
138 Machine::instance().getIrqManager()->registerIsaIrqHandler(
139 irq, static_cast<IrqHandler *>(&cdi_irq_handler),
140 IrqPolicy::levelThreaded());
141 if (!registration)
142 {
143 LockGuard<Mutex> lock(irqSlotLock);
144 driverIrqs[irq].handler = nullptr;
145 driverIrqs[irq].device = nullptr;
146 ERROR("cdi: IRQ " << irq << " konnte nicht registriert werden");
147 return;
148 }
149
150 LockGuard<Mutex> lock(irqSlotLock);
151 driverIrqs[irq].registration = registration;
152}
153
161EXPORTED_PUBLIC int cdi_reset_wait_irq(uint8_t irq)
162{
163 if (irq >= IRQ_COUNT)
164 {
165 return -1;
166 }
167
168 LockGuard<Mutex> lock(irqSlotLock);
169 if (!driverIrqs[irq].handler)
170 {
171 return -1;
172 }
173
174 const size_t discarded = resetIrqCounter(driverIrqs[irq].counter);
175 (void) discarded;
176 return 0;
177}
178
192EXPORTED_PUBLIC int cdi_wait_irq(uint8_t irq, uint32_t timeout)
193{
194 if (irq >= IRQ_COUNT)
195 {
196 return -1;
197 }
198
199 {
200 LockGuard<Mutex> lock(irqSlotLock);
201 if (!driverIrqs[irq].handler)
202 return -2;
203 }
204
205 return waitForIrqCounter(driverIrqs[irq].counter, timeout);
206}
207}
208
209#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
210namespace
211{
212Atomic<size_t> g_HostedCdiIrqCalls(0);
213
214void hostedCdiIrqHandler(struct cdi_device *)
215{
216 g_HostedCdiIrqCalls += 1;
217}
218} // namespace
219
220bool runHostedCdiIrqRegressions()
221{
222 constexpr uint8_t TestIrq = IRQ_COUNT - 1;
223 {
224 LockGuard<Mutex> lock(irqSlotLock);
225 g_HostedCdiIrqCalls = 0;
226 const size_t discarded = resetIrqCounter(driverIrqs[TestIrq].counter);
227 (void) discarded;
228 driverIrqs[TestIrq].handler = hostedCdiIrqHandler;
229 driverIrqs[TestIrq].device = nullptr;
230 }
231
232 const IrqDisposition disposition = cdi_irq_handler.irq(TestIrq);
233 const bool reset = cdi_reset_wait_irq(TestIrq) == 0;
234 const bool emptyAfterReset = !driverIrqs[TestIrq].counter.tryAcquire();
235 const bool zeroTimeout = cdi_wait_irq(TestIrq, 0) == -3;
236 driverIrqs[TestIrq].counter.release();
237 const bool zeroSuccess = cdi_wait_irq(TestIrq, 0) == 0;
238 driverIrqs[TestIrq].counter.release();
239 const bool finiteSuccess = cdi_wait_irq(TestIrq, 1) == 0;
240 const bool finiteTimeout = cdi_wait_irq(TestIrq, 1) == -3;
241 const bool invalidReset = cdi_reset_wait_irq(IRQ_COUNT) == -1;
242 const bool invalidWait = cdi_wait_irq(IRQ_COUNT, 0) == -1;
243
244 {
245 LockGuard<Mutex> lock(irqSlotLock);
246 driverIrqs[TestIrq].handler = nullptr;
247 driverIrqs[TestIrq].device = nullptr;
248 const size_t discarded = resetIrqCounter(driverIrqs[TestIrq].counter);
249 (void) discarded;
250 }
251
252 const bool passed = disposition == IrqDisposition::Handled &&
253 g_HostedCdiIrqCalls == 1 && reset && emptyAfterReset &&
254 zeroTimeout && zeroSuccess && finiteSuccess &&
255 finiteTimeout && invalidReset && invalidWait;
256 if (passed)
257 {
258 NOTICE("HOSTED-WAIT-TEST: PASS cdi-irq-wait-contract");
259 }
260 else
261 {
262 ERROR("HOSTED-WAIT-TEST: FAIL cdi-irq-wait-contract");
263 }
264 return passed;
265}
266#endif
IrqDisposition irq(irq_id_t number) override
Definition CdiIrq.cc:78
virtual irq_id_t registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, const IrqPolicy &policy)=0
Definition Mutex.h:56
void release(size_t n=1)
Definition Semaphore.cc:546
MUST_USE_RESULT bool acquireWithError(size_t n, size_t timeoutSecs, size_t timeoutUsecs, SemaphoreError &error)
Definition Semaphore.cc:357
MUST_USE_RESULT size_t drainAvailable()
Definition Semaphore.cc:528
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:481
IrqDisposition
Definition IrqHandler.h:31