|
9 | 9 |
|
10 | 10 | #![allow(dead_code)]
|
11 | 11 |
|
12 |
| -use crate::{bindings, error::from_kernel_result, types::PointerWrapper, Error, Result}; |
13 |
| -use core::ops::Deref; |
| 12 | +use crate::{ |
| 13 | + bindings, |
| 14 | + error::{from_kernel_result, to_result}, |
| 15 | + str::CString, |
| 16 | + types::PointerWrapper, |
| 17 | + Error, Result, ScopeGuard, |
| 18 | +}; |
| 19 | +use core::{fmt, marker::PhantomData, ops::Deref}; |
14 | 20 |
|
15 | 21 | /// The type of irq hardware numbers.
|
16 | 22 | pub type HwNumber = bindings::irq_hw_number_t;
|
@@ -329,6 +335,301 @@ impl Descriptor {
|
329 | 335 | }
|
330 | 336 | }
|
331 | 337 |
|
| 338 | +struct InternalRegistration<T: PointerWrapper> { |
| 339 | + irq: u32, |
| 340 | + data: *mut core::ffi::c_void, |
| 341 | + name: CString, |
| 342 | + _p: PhantomData<T>, |
| 343 | +} |
| 344 | + |
| 345 | +impl<T: PointerWrapper> InternalRegistration<T> { |
| 346 | + /// Registers a new irq handler. |
| 347 | + /// |
| 348 | + /// # Safety |
| 349 | + /// |
| 350 | + /// Callers must ensure that `handler` and `thread_fn` are compatible with the registration, |
| 351 | + /// that is, that they only use their second argument while the call is happening and that they |
| 352 | + /// only call [`T::borrow`] on it (e.g., they shouldn't call [`T::from_pointer`] and consume |
| 353 | + /// it). |
| 354 | + unsafe fn try_new( |
| 355 | + irq: core::ffi::c_uint, |
| 356 | + handler: bindings::irq_handler_t, |
| 357 | + thread_fn: bindings::irq_handler_t, |
| 358 | + flags: usize, |
| 359 | + data: T, |
| 360 | + name: fmt::Arguments<'_>, |
| 361 | + ) -> Result<Self> { |
| 362 | + let ptr = data.into_pointer() as *mut _; |
| 363 | + let name = CString::try_from_fmt(name)?; |
| 364 | + let guard = ScopeGuard::new(|| { |
| 365 | + // SAFETY: `ptr` came from a previous call to `into_pointer`. |
| 366 | + unsafe { T::from_pointer(ptr) }; |
| 367 | + }); |
| 368 | + // SAFETY: `name` and `ptr` remain valid as long as the registration is alive. |
| 369 | + to_result(|| unsafe { |
| 370 | + bindings::request_threaded_irq( |
| 371 | + irq, |
| 372 | + handler, |
| 373 | + thread_fn, |
| 374 | + flags as _, |
| 375 | + name.as_char_ptr(), |
| 376 | + ptr, |
| 377 | + ) |
| 378 | + })?; |
| 379 | + guard.dismiss(); |
| 380 | + Ok(Self { |
| 381 | + irq, |
| 382 | + name, |
| 383 | + data: ptr, |
| 384 | + _p: PhantomData, |
| 385 | + }) |
| 386 | + } |
| 387 | +} |
| 388 | + |
| 389 | +impl<T: PointerWrapper> Drop for InternalRegistration<T> { |
| 390 | + fn drop(&mut self) { |
| 391 | + // Unregister irq handler. |
| 392 | + // |
| 393 | + // SAFETY: When `try_new` succeeds, the irq was successfully requested, so it is ok to free |
| 394 | + // it here. |
| 395 | + unsafe { bindings::free_irq(self.irq, self.data) }; |
| 396 | + |
| 397 | + // Free context data. |
| 398 | + // |
| 399 | + // SAFETY: This matches the call to `into_pointer` from `try_new` in the success case. |
| 400 | + unsafe { T::from_pointer(self.data) }; |
| 401 | + } |
| 402 | +} |
| 403 | + |
| 404 | +/// An irq handler. |
| 405 | +pub trait Handler { |
| 406 | + /// The context data associated with and made available to the handler. |
| 407 | + type Data: PointerWrapper; |
| 408 | + |
| 409 | + /// Called from interrupt context when the irq happens. |
| 410 | + fn handle_irq(data: <Self::Data as PointerWrapper>::Borrowed<'_>) -> Return; |
| 411 | +} |
| 412 | + |
| 413 | +/// The registration of an interrupt handler. |
| 414 | +/// |
| 415 | +/// # Examples |
| 416 | +/// |
| 417 | +/// The following is an example of a regular handler with a boxed `u32` as data. |
| 418 | +/// |
| 419 | +/// ``` |
| 420 | +/// # use kernel::prelude::*; |
| 421 | +/// use kernel::irq; |
| 422 | +/// |
| 423 | +/// struct Example; |
| 424 | +/// |
| 425 | +/// impl irq::Handler for Example { |
| 426 | +/// type Data = Box<u32>; |
| 427 | +/// |
| 428 | +/// fn handle_irq(_data: &u32) -> irq::Return { |
| 429 | +/// irq::Return::None |
| 430 | +/// } |
| 431 | +/// } |
| 432 | +/// |
| 433 | +/// fn request_irq(irq: u32, data: Box<u32>) -> Result<irq::Registration<Example>> { |
| 434 | +/// irq::Registration::try_new( |
| 435 | +/// irq, data, irq::flags::SHARED, fmt!("example_{irq}")) |
| 436 | +/// } |
| 437 | +/// ``` |
| 438 | +pub struct Registration<H: Handler>(InternalRegistration<H::Data>); |
| 439 | + |
| 440 | +impl<H: Handler> Registration<H> { |
| 441 | + /// Registers a new irq handler. |
| 442 | + /// |
| 443 | + /// The valid values of `flags` come from the [`flags`] module. |
| 444 | + pub fn try_new( |
| 445 | + irq: u32, |
| 446 | + data: H::Data, |
| 447 | + flags: usize, |
| 448 | + name: fmt::Arguments<'_>, |
| 449 | + ) -> Result<Self> { |
| 450 | + // SAFETY: `handler` only calls `H::Data::borrow` on `raw_data`. |
| 451 | + Ok(Self(unsafe { |
| 452 | + InternalRegistration::try_new(irq, Some(Self::handler), None, flags, data, name)? |
| 453 | + })) |
| 454 | + } |
| 455 | + |
| 456 | + unsafe extern "C" fn handler( |
| 457 | + _irq: core::ffi::c_int, |
| 458 | + raw_data: *mut core::ffi::c_void, |
| 459 | + ) -> bindings::irqreturn_t { |
| 460 | + // SAFETY: On registration, `into_pointer` was called, so it is safe to borrow from it here |
| 461 | + // because `from_pointer` is called only after the irq is unregistered. |
| 462 | + let data = unsafe { H::Data::borrow(raw_data) }; |
| 463 | + H::handle_irq(data) as _ |
| 464 | + } |
| 465 | +} |
| 466 | + |
| 467 | +/// A threaded irq handler. |
| 468 | +pub trait ThreadedHandler { |
| 469 | + /// The context data associated with and made available to the handlers. |
| 470 | + type Data: PointerWrapper; |
| 471 | + |
| 472 | + /// Called from interrupt context when the irq first happens. |
| 473 | + fn handle_primary_irq(_data: <Self::Data as PointerWrapper>::Borrowed<'_>) -> Return { |
| 474 | + Return::WakeThread |
| 475 | + } |
| 476 | + |
| 477 | + /// Called from the handler thread. |
| 478 | + fn handle_threaded_irq(data: <Self::Data as PointerWrapper>::Borrowed<'_>) -> Return; |
| 479 | +} |
| 480 | + |
| 481 | +/// The registration of a threaded interrupt handler. |
| 482 | +/// |
| 483 | +/// # Examples |
| 484 | +/// |
| 485 | +/// The following is an example of a threaded handler with a ref-counted u32 as data: |
| 486 | +/// |
| 487 | +/// ``` |
| 488 | +/// # use kernel::prelude::*; |
| 489 | +/// use kernel::{irq, sync::{Ref, RefBorrow}}; |
| 490 | +/// |
| 491 | +/// struct Example; |
| 492 | +/// |
| 493 | +/// impl irq::ThreadedHandler for Example { |
| 494 | +/// type Data = Ref<u32>; |
| 495 | +/// |
| 496 | +/// fn handle_threaded_irq(_data: RefBorrow<'_, u32>) -> irq::Return { |
| 497 | +/// irq::Return::None |
| 498 | +/// } |
| 499 | +/// } |
| 500 | +/// |
| 501 | +/// fn request_irq(irq: u32, data: Ref<u32>) -> Result<irq::ThreadedRegistration<Example>> { |
| 502 | +/// irq::ThreadedRegistration::try_new( |
| 503 | +/// irq, data, irq::flags::SHARED, fmt!("example_{irq}")) |
| 504 | +/// } |
| 505 | +/// ``` |
| 506 | +pub struct ThreadedRegistration<H: ThreadedHandler>(InternalRegistration<H::Data>); |
| 507 | + |
| 508 | +impl<H: ThreadedHandler> ThreadedRegistration<H> { |
| 509 | + /// Registers a new threaded irq handler. |
| 510 | + /// |
| 511 | + /// The valid values of `flags` come from the [`flags`] module. |
| 512 | + pub fn try_new( |
| 513 | + irq: u32, |
| 514 | + data: H::Data, |
| 515 | + flags: usize, |
| 516 | + name: fmt::Arguments<'_>, |
| 517 | + ) -> Result<Self> { |
| 518 | + // SAFETY: both `primary_handler` and `threaded_handler` only call `H::Data::borrow` on |
| 519 | + // `raw_data`. |
| 520 | + Ok(Self(unsafe { |
| 521 | + InternalRegistration::try_new( |
| 522 | + irq, |
| 523 | + Some(Self::primary_handler), |
| 524 | + Some(Self::threaded_handler), |
| 525 | + flags, |
| 526 | + data, |
| 527 | + name, |
| 528 | + )? |
| 529 | + })) |
| 530 | + } |
| 531 | + |
| 532 | + unsafe extern "C" fn primary_handler( |
| 533 | + _irq: core::ffi::c_int, |
| 534 | + raw_data: *mut core::ffi::c_void, |
| 535 | + ) -> bindings::irqreturn_t { |
| 536 | + // SAFETY: On registration, `into_pointer` was called, so it is safe to borrow from it here |
| 537 | + // because `from_pointer` is called only after the irq is unregistered. |
| 538 | + let data = unsafe { H::Data::borrow(raw_data) }; |
| 539 | + H::handle_primary_irq(data) as _ |
| 540 | + } |
| 541 | + |
| 542 | + unsafe extern "C" fn threaded_handler( |
| 543 | + _irq: core::ffi::c_int, |
| 544 | + raw_data: *mut core::ffi::c_void, |
| 545 | + ) -> bindings::irqreturn_t { |
| 546 | + // SAFETY: On registration, `into_pointer` was called, so it is safe to borrow from it here |
| 547 | + // because `from_pointer` is called only after the irq is unregistered. |
| 548 | + let data = unsafe { H::Data::borrow(raw_data) }; |
| 549 | + H::handle_threaded_irq(data) as _ |
| 550 | + } |
| 551 | +} |
| 552 | + |
| 553 | +/// The return value from interrupt handlers. |
| 554 | +pub enum Return { |
| 555 | + /// The interrupt was not from this device or was not handled. |
| 556 | + None = bindings::irqreturn_IRQ_NONE as _, |
| 557 | + |
| 558 | + /// The interrupt was handled by this device. |
| 559 | + Handled = bindings::irqreturn_IRQ_HANDLED as _, |
| 560 | + |
| 561 | + /// The handler wants the handler thread to wake up. |
| 562 | + WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD as _, |
| 563 | +} |
| 564 | + |
| 565 | +/// Container for interrupt flags. |
| 566 | +pub mod flags { |
| 567 | + use crate::bindings; |
| 568 | + |
| 569 | + /// Use the interrupt line as already configured. |
| 570 | + pub const TRIGGER_NONE: usize = bindings::IRQF_TRIGGER_NONE as _; |
| 571 | + |
| 572 | + /// The interrupt is triggered when the signal goes from low to high. |
| 573 | + pub const TRIGGER_RISING: usize = bindings::IRQF_TRIGGER_RISING as _; |
| 574 | + |
| 575 | + /// The interrupt is triggered when the signal goes from high to low. |
| 576 | + pub const TRIGGER_FALLING: usize = bindings::IRQF_TRIGGER_FALLING as _; |
| 577 | + |
| 578 | + /// The interrupt is triggered while the signal is held high. |
| 579 | + pub const TRIGGER_HIGH: usize = bindings::IRQF_TRIGGER_HIGH as _; |
| 580 | + |
| 581 | + /// The interrupt is triggered while the signal is held low. |
| 582 | + pub const TRIGGER_LOW: usize = bindings::IRQF_TRIGGER_LOW as _; |
| 583 | + |
| 584 | + /// Allow sharing the irq among several devices. |
| 585 | + pub const SHARED: usize = bindings::IRQF_SHARED as _; |
| 586 | + |
| 587 | + /// Set by callers when they expect sharing mismatches to occur. |
| 588 | + pub const PROBE_SHARED: usize = bindings::IRQF_PROBE_SHARED as _; |
| 589 | + |
| 590 | + /// Flag to mark this interrupt as timer interrupt. |
| 591 | + pub const TIMER: usize = bindings::IRQF_TIMER as _; |
| 592 | + |
| 593 | + /// Interrupt is per cpu. |
| 594 | + pub const PERCPU: usize = bindings::IRQF_PERCPU as _; |
| 595 | + |
| 596 | + /// Flag to exclude this interrupt from irq balancing. |
| 597 | + pub const NOBALANCING: usize = bindings::IRQF_NOBALANCING as _; |
| 598 | + |
| 599 | + /// Interrupt is used for polling (only the interrupt that is registered first in a shared |
| 600 | + /// interrupt is considered for performance reasons). |
| 601 | + pub const IRQPOLL: usize = bindings::IRQF_IRQPOLL as _; |
| 602 | + |
| 603 | + /// Interrupt is not reenabled after the hardirq handler finished. Used by threaded interrupts |
| 604 | + /// which need to keep the irq line disabled until the threaded handler has been run. |
| 605 | + pub const ONESHOT: usize = bindings::IRQF_ONESHOT as _; |
| 606 | + |
| 607 | + /// Do not disable this IRQ during suspend. Does not guarantee that this interrupt will wake |
| 608 | + /// the system from a suspended state. |
| 609 | + pub const NO_SUSPEND: usize = bindings::IRQF_NO_SUSPEND as _; |
| 610 | + |
| 611 | + /// Force enable it on resume even if [`NO_SUSPEND`] is set. |
| 612 | + pub const FORCE_RESUME: usize = bindings::IRQF_FORCE_RESUME as _; |
| 613 | + |
| 614 | + /// Interrupt cannot be threaded. |
| 615 | + pub const NO_THREAD: usize = bindings::IRQF_NO_THREAD as _; |
| 616 | + |
| 617 | + /// Resume IRQ early during syscore instead of at device resume time. |
| 618 | + pub const EARLY_RESUME: usize = bindings::IRQF_EARLY_RESUME as _; |
| 619 | + |
| 620 | + /// If the IRQ is shared with a NO_SUSPEND user, execute this interrupt handler after |
| 621 | + /// suspending interrupts. For system wakeup devices users need to implement wakeup detection |
| 622 | + /// in their interrupt handlers. |
| 623 | + pub const COND_SUSPEND: usize = bindings::IRQF_COND_SUSPEND as _; |
| 624 | + |
| 625 | + /// Don't enable IRQ or NMI automatically when users request it. Users will enable it |
| 626 | + /// explicitly by `enable_irq` or `enable_nmi` later. |
| 627 | + pub const NO_AUTOEN: usize = bindings::IRQF_NO_AUTOEN as _; |
| 628 | + |
| 629 | + /// Exclude from runnaway detection for IPI and similar handlers, depends on `PERCPU`. |
| 630 | + pub const NO_DEBUG: usize = bindings::IRQF_NO_DEBUG as _; |
| 631 | +} |
| 632 | + |
332 | 633 | /// A guard to call `chained_irq_exit` after `chained_irq_enter` was called.
|
333 | 634 | ///
|
334 | 635 | /// It is also used as evidence that a previous `chained_irq_enter` was called. So there are no
|
|
0 commit comments