Add internal locking to the Xhci struct.
This commit is contained in:
Generated
+172
@@ -141,6 +141,15 @@ name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "chashmap"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
@@ -259,6 +268,88 @@ name = "futures"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpt"
|
||||
version = "0.6.3"
|
||||
@@ -419,6 +510,11 @@ name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.3"
|
||||
@@ -627,6 +723,14 @@ dependencies = [
|
||||
"sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owning_ref"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owning_ref"
|
||||
version = "0.4.0"
|
||||
@@ -635,6 +739,15 @@ dependencies = [
|
||||
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.7.1"
|
||||
@@ -644,6 +757,17 @@ dependencies = [
|
||||
"parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.4.0"
|
||||
@@ -716,11 +840,26 @@ name = "percent-encoding"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0-alpha.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.8"
|
||||
@@ -747,6 +886,18 @@ dependencies = [
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
@@ -1527,6 +1678,9 @@ name = "xhcid"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pcid 0.1.0",
|
||||
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1558,6 +1712,7 @@ dependencies = [
|
||||
"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
|
||||
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff41a3c2c1e39921b9003de14bf0439c7b63a9039637c291e1a64925d8ddfa45"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
|
||||
@@ -1571,6 +1726,15 @@ dependencies = [
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
|
||||
"checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780"
|
||||
"checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8"
|
||||
"checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
|
||||
"checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba"
|
||||
"checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
|
||||
"checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7"
|
||||
"checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6"
|
||||
"checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
|
||||
"checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
|
||||
"checksum gpt 0.6.3 (git+https://gitlab.redox-os.org/redox-os/gpt)" = "<none>"
|
||||
"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
|
||||
"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
|
||||
@@ -1589,6 +1753,7 @@ dependencies = [
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
|
||||
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
|
||||
"checksum mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)" = "<none>"
|
||||
@@ -1607,16 +1772,23 @@ dependencies = [
|
||||
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
|
||||
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
"checksum orbclient 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b18f57ab94fbd058e30aa57f712ec423c0bb7403f8493a6c58eef0c36d9402"
|
||||
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
||||
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
|
||||
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
|
||||
"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
|
||||
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
|
||||
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
|
||||
"checksum partitionlib 0.1.0 (git+https://gitlab.redox-os.org/redox-os/partitionlib.git)" = "<none>"
|
||||
"checksum pbr 1.0.2 (git+https://github.com/a8m/pb)" = "<none>"
|
||||
"checksum pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4403eb718d70c03ee279e51737782902c68cca01e870a33b6a2f9dfb50b9cd83"
|
||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
|
||||
"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
"checksum proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420"
|
||||
"checksum proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
|
||||
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
|
||||
@@ -10,21 +10,42 @@ use thiserror::Error;
|
||||
pub use crate::pci::PciBar;
|
||||
pub use crate::pci::msi;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[repr(u8)]
|
||||
pub enum LegacyInterruptPin {
|
||||
/// INTa#
|
||||
IntA = 1,
|
||||
/// INTb#
|
||||
IntB = 2,
|
||||
/// INTc#
|
||||
IntC = 3,
|
||||
/// INTd#
|
||||
IntD = 4,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct PciFunction {
|
||||
/// Number of PCI bus
|
||||
pub bus_num: u8,
|
||||
|
||||
/// Number of PCI device
|
||||
pub dev_num: u8,
|
||||
|
||||
/// Number of PCI function
|
||||
pub func_num: u8,
|
||||
|
||||
/// PCI Base Address Registers
|
||||
pub bars: [PciBar; 6],
|
||||
|
||||
/// BAR sizes
|
||||
pub bar_sizes: [u32; 6],
|
||||
|
||||
/// Legacy IRQ line
|
||||
// TODO: Stop using legacy IRQ lines, and physical pins, but MSI/MSI-X instead.
|
||||
pub legacy_interrupt_line: u8,
|
||||
|
||||
/// Legacy interrupt pin (INTx#), none if INTx# interrupts aren't supported at all.
|
||||
pub legacy_interrupt_pin: Option<LegacyInterruptPin>,
|
||||
|
||||
/// Vendor ID
|
||||
pub venid: u16,
|
||||
/// Device ID
|
||||
|
||||
+22
-1
@@ -75,7 +75,10 @@ impl DriverHandler {
|
||||
None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)),
|
||||
};
|
||||
unsafe {
|
||||
with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, |func| capability.set_enabled(func, offset, true));
|
||||
with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, |func| {
|
||||
capability.set_enabled(true);
|
||||
capability.write_message_control(func, offset);
|
||||
});
|
||||
}
|
||||
PcidClientResponse::FeatureEnabled(feature)
|
||||
}
|
||||
@@ -255,6 +258,8 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
pci.write(bus_num, dev_num, func_num, 0x3C, data);
|
||||
}
|
||||
|
||||
let interrupt_pin = unsafe { pci.read(bus_num, dev_num, func_num, 0x3B) };
|
||||
|
||||
// Find BAR sizes
|
||||
let mut bars = [PciBar::None; 6];
|
||||
let mut bar_sizes = [0; 6];
|
||||
@@ -308,6 +313,21 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
};
|
||||
println!("PCI DEVICE CAPABILITIES for {}: {:?}", args.iter().map(|string| string.as_ref()).nth(0).unwrap_or("[unknown]"), capabilities);
|
||||
|
||||
use driver_interface::LegacyInterruptPin;
|
||||
|
||||
let legacy_interrupt_pin = match interrupt_pin {
|
||||
0 => None,
|
||||
1 => Some(LegacyInterruptPin::IntA),
|
||||
2 => Some(LegacyInterruptPin::IntB),
|
||||
3 => Some(LegacyInterruptPin::IntC),
|
||||
4 => Some(LegacyInterruptPin::IntD),
|
||||
|
||||
other => {
|
||||
println!("pcid: invalid interrupt pin: {}", other);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let func = driver_interface::PciFunction {
|
||||
bars,
|
||||
bar_sizes,
|
||||
@@ -316,6 +336,7 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
func_num,
|
||||
devid: header.device_id(),
|
||||
legacy_interrupt_line: irq,
|
||||
legacy_interrupt_pin,
|
||||
venid: header.vendor_id(),
|
||||
};
|
||||
|
||||
|
||||
+11
-4
@@ -68,11 +68,10 @@ impl MsiCapability {
|
||||
pub fn message_control(&self) -> u16 {
|
||||
(self.message_control_raw() >> 16) as u16
|
||||
}
|
||||
pub unsafe fn set_message_control<W: ConfigWriter>(&mut self, writer: &W, offset: u8, value: u16) {
|
||||
pub fn set_message_control(&mut self, value: u16) {
|
||||
let mut new_message_control = self.message_control_raw();
|
||||
new_message_control &= 0x0000_FFFF;
|
||||
new_message_control |= u32::from(value) << 16;
|
||||
writer.write_u32(offset, new_message_control);
|
||||
|
||||
match self {
|
||||
Self::_32BitAddress { ref mut message_control, .. }
|
||||
@@ -81,6 +80,9 @@ impl MsiCapability {
|
||||
| Self::_64BitAddressWithPvm { ref mut message_control, .. } => *message_control = new_message_control,
|
||||
}
|
||||
}
|
||||
pub unsafe fn write_message_control<W: ConfigWriter>(&mut self, writer: &W, offset: u8) {
|
||||
writer.write_u32(offset, self.message_control_raw());
|
||||
}
|
||||
pub fn is_pvt_capable(&self) -> bool {
|
||||
self.message_control() & Self::MC_PVT_CAPABLE_BIT != 0
|
||||
}
|
||||
@@ -90,10 +92,10 @@ impl MsiCapability {
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.message_control() & Self::MC_MSI_ENABLED_BIT != 0
|
||||
}
|
||||
pub unsafe fn set_enabled<W: ConfigWriter>(&mut self, writer: &W, offset: u8, enabled: bool) {
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
let mut new_message_control = self.message_control() & (!Self::MC_MSI_ENABLED_BIT);
|
||||
new_message_control |= u16::from(enabled);
|
||||
self.set_message_control(writer, offset, new_message_control)
|
||||
self.set_message_control(new_message_control);
|
||||
}
|
||||
pub fn multi_message_capable(&self) -> u8 {
|
||||
((self.message_control() & Self::MC_MULTI_MESSAGE_MASK) >> Self::MC_MULTI_MESSAGE_SHIFT) as u8
|
||||
@@ -101,6 +103,11 @@ impl MsiCapability {
|
||||
pub fn multi_message_enabled(&self) -> u8 {
|
||||
((self.message_control() & Self::MC_MULTI_MESSAGE_ENABLE_MASK) >> Self::MC_MULTI_MESSAGE_ENABLE_SHIFT) as u8
|
||||
}
|
||||
pub fn set_multi_message_enabled(&mut self, log_mme: u8) {
|
||||
let mut new_message_control = self.message_control() & (!Self::MC_MULTI_MESSAGE_ENABLE_MASK);
|
||||
new_message_control |= (u16::from(log_mme) << Self::MC_MULTI_MESSAGE_ENABLE_SHIFT);
|
||||
self.set_message_control(new_message_control);
|
||||
}
|
||||
}
|
||||
|
||||
impl MsixCapability {
|
||||
|
||||
@@ -13,6 +13,9 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1"
|
||||
chashmap = "2.2.2"
|
||||
crossbeam-channel = "0.4"
|
||||
futures = "0.3"
|
||||
plain = "0.2"
|
||||
lazy_static = "1.4"
|
||||
spin = "0.4"
|
||||
|
||||
+82
-59
@@ -8,13 +8,14 @@ use pcid_interface::{PcidServerHandle, PciFeature, PciFeatureInfo};
|
||||
use pcid_interface::msi::{MsiCapability, MsixCapability, MsixTableEntry};
|
||||
|
||||
use event::{Event, EventQueue};
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::{self, File};
|
||||
use std::future::Future;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::pin::Pin;
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::env;
|
||||
use syscall::data::Packet;
|
||||
use syscall::error::EWOULDBLOCK;
|
||||
@@ -22,7 +23,7 @@ use syscall::flag::{CloneFlags, PHYSMAP_NO_CACHE, PHYSMAP_WRITE};
|
||||
use syscall::scheme::SchemeMut;
|
||||
use syscall::io::Io;
|
||||
|
||||
use crate::xhci::Xhci;
|
||||
use crate::xhci::{InterruptMethod, Xhci};
|
||||
|
||||
mod driver_interface;
|
||||
mod usb;
|
||||
@@ -71,6 +72,10 @@ fn allocate_interrupt_vector() -> io::Result<Option<(u8, File)>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn handle_packet(hci: Arc<Xhci>, packet: Packet) -> Packet {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Daemonize
|
||||
if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 {
|
||||
@@ -103,15 +108,26 @@ fn main() {
|
||||
dbg!(has_msi, msi_enabled);
|
||||
dbg!(has_msix, msix_enabled);
|
||||
|
||||
if has_msi && !msi_enabled {
|
||||
if has_msi && !msi_enabled && !has_msix {
|
||||
pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI");
|
||||
println!("Enabled MSI");
|
||||
msi_enabled = true;
|
||||
}
|
||||
if has_msix && !msix_enabled {
|
||||
pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X");
|
||||
println!("Enabled MSI-X");
|
||||
msix_enabled = true;
|
||||
}
|
||||
|
||||
let (mut irq_file, msix_info) = if msi_enabled && !msix_enabled {
|
||||
todo!("only msi-x is currently implemented")
|
||||
let (mut irq_file, interrupt_method) = if msi_enabled && !msix_enabled {
|
||||
let mut capability = match pcid_handle.feature_info(PciFeature::MsiX).expect("xhcid: failed to retrieve the MSI capability structure from pcid") {
|
||||
PciFeatureInfo::Msi(s) => s,
|
||||
PciFeatureInfo::MsiX(_) => panic!(),
|
||||
};
|
||||
// use one vector
|
||||
capability.set_multi_message_enabled(0);
|
||||
|
||||
todo!("msi (msix is implemented though)")
|
||||
} else if msix_enabled {
|
||||
let capability = match pcid_handle.feature_info(PciFeature::MsiX).expect("xhcid: failed to retrieve the MSI-X capability structure from pcid") {
|
||||
PciFeatureInfo::Msi(_) => panic!(),
|
||||
@@ -160,17 +176,19 @@ fn main() {
|
||||
let (vector, interrupt_handle) = allocate_interrupt_vector().expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left");
|
||||
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
|
||||
|
||||
dbg!(vector, destination_id);
|
||||
|
||||
table_entry_pointer.addr_lo.write(addr);
|
||||
table_entry_pointer.addr_hi.write(0);
|
||||
table_entry_pointer.msg_data.write(msg_data);
|
||||
table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false);
|
||||
|
||||
(interrupt_handle, Some(info))
|
||||
(Some(interrupt_handle), InterruptMethod::MsiX(info))
|
||||
}
|
||||
} else if pci_config.func.legacy_interrupt_pin.is_some() {
|
||||
// legacy INTx# interrupt pins.
|
||||
(Some(File::open(format!("irq:{}", irq)).expect("xhcid: failed to open legacy IRQ file")), InterruptMethod::Intx)
|
||||
} else {
|
||||
(File::open(format!("irq:{}", irq)).expect("xhcid: failed to open legacy IRQ file"), None)
|
||||
// no interrupts at all
|
||||
(None, InterruptMethod::Polling)
|
||||
};
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(300));
|
||||
@@ -187,27 +205,25 @@ fn main() {
|
||||
|
||||
let socket_fd = syscall::open(
|
||||
format!(":usb/{}", name),
|
||||
syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK,
|
||||
syscall::O_RDWR | syscall::O_CREAT,
|
||||
)
|
||||
.expect("xhcid: failed to create usb scheme");
|
||||
let socket = Arc::new(RefCell::new(unsafe {
|
||||
let socket = Arc::new(Mutex::new(unsafe {
|
||||
File::from_raw_fd(socket_fd as RawFd)
|
||||
}));
|
||||
|
||||
{
|
||||
let hci = Arc::new(RefCell::new(
|
||||
Xhci::new(name, address, msi_enabled, msix_enabled, msix_info, pcid_handle).expect("xhcid: failed to allocate device"),
|
||||
));
|
||||
let hci = Arc::new(Xhci::new(name, address, interrupt_method, irq_file.as_mut(), pcid_handle).expect("xhcid: failed to allocate device"));
|
||||
hci.probe().expect("xhcid: failed to probe");
|
||||
|
||||
hci.borrow_mut().probe().expect("xhcid: failed to probe");
|
||||
let mut event_queue =
|
||||
EventQueue::<()>::new().expect("xhcid: failed to create event queue");
|
||||
|
||||
let mut event_queue =
|
||||
EventQueue::<()>::new().expect("xhcid: failed to create event queue");
|
||||
syscall::setrens(0, 0).expect("xhcid: failed to enter null namespace");
|
||||
|
||||
syscall::setrens(0, 0).expect("xhcid: failed to enter null namespace");
|
||||
|
||||
let todo = Arc::new(RefCell::new(Vec::<Packet>::new()));
|
||||
let todo = Arc::new(Mutex::new(Vec::<Packet>::new()));
|
||||
let todo_futures = Arc::new(Mutex::new(Vec::<Pin<Box<dyn Future<Output = usize> + Send + Sync + 'static>>>::new()));
|
||||
|
||||
if let Some(irq_file) = irq_file {
|
||||
let hci_irq = hci.clone();
|
||||
let socket_irq = socket.clone();
|
||||
let todo_irq = todo.clone();
|
||||
@@ -216,21 +232,24 @@ fn main() {
|
||||
let mut irq = [0; 8];
|
||||
irq_file.read(&mut irq)?;
|
||||
|
||||
if hci_irq.borrow_mut().received_irq() {
|
||||
hci_irq.borrow_mut().on_irq();
|
||||
let hci = hci_irq.lock().unwrap();
|
||||
let socket = socket_irq.lock().unwrap();
|
||||
let todo = todo_irq.lock().unwrap();
|
||||
|
||||
if hci.received_irq() {
|
||||
hci.on_irq();
|
||||
|
||||
irq_file.write(&mut irq)?;
|
||||
|
||||
let mut todo = todo_irq.borrow_mut();
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
let a = todo[i].a;
|
||||
hci_irq.borrow_mut().handle(&mut todo[i]);
|
||||
hci.handle(&mut todo[i]);
|
||||
if todo[i].a == (-EWOULDBLOCK) as usize {
|
||||
todo[i].a = a;
|
||||
i += 1;
|
||||
} else {
|
||||
socket_irq.borrow_mut().write(&mut todo[i])?;
|
||||
socket.write(&todo[i])?;
|
||||
todo.remove(i);
|
||||
}
|
||||
}
|
||||
@@ -239,39 +258,43 @@ fn main() {
|
||||
Ok(None)
|
||||
})
|
||||
.expect("xhcid: failed to catch events on IRQ file");
|
||||
|
||||
let socket_fd = socket.borrow().as_raw_fd();
|
||||
let socket_packet = socket.clone();
|
||||
event_queue
|
||||
.add(socket_fd, move |_| -> io::Result<Option<()>> {
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
match socket_packet.borrow_mut().read(&mut packet) {
|
||||
Ok(0) => break,
|
||||
Err(err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Ok(_) => (),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
|
||||
let a = packet.a;
|
||||
hci.borrow_mut().handle(&mut packet);
|
||||
if packet.a == (-EWOULDBLOCK) as usize {
|
||||
packet.a = a;
|
||||
todo.borrow_mut().push(packet);
|
||||
} else {
|
||||
socket_packet.borrow_mut().write(&mut packet)?;
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
})
|
||||
.expect("xhcid: failed to catch events on scheme file");
|
||||
|
||||
event_queue
|
||||
.trigger_all(Event { fd: 0, flags: 0 })
|
||||
.expect("xhcid: failed to trigger events");
|
||||
|
||||
event_queue.run().expect("xhcid: failed to handle events");
|
||||
}
|
||||
|
||||
let socket_fd = socket.lock().unwrap().as_raw_fd();
|
||||
let socket_packet = socket.clone();
|
||||
event_queue
|
||||
.add(socket_fd, move |_| -> io::Result<Option<()>> {
|
||||
let mut socket = socket_packet.lock().unwrap();
|
||||
let mut hci = hci.lock().unwrap();
|
||||
let mut todo = todo.lock().unwrap();
|
||||
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
match socket.read(&mut packet) {
|
||||
Ok(0) => break,
|
||||
Ok(_) => (),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
|
||||
let a = packet.a;
|
||||
hci.handle(&mut packet);
|
||||
if packet.a == (-EWOULDBLOCK) as usize {
|
||||
packet.a = a;
|
||||
todo.push(packet);
|
||||
} else {
|
||||
socket.write(&packet)?;
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
})
|
||||
.expect("xhcid: failed to catch events on scheme file");
|
||||
|
||||
event_queue
|
||||
.trigger_all(Event { fd: 0, flags: 0 })
|
||||
.expect("xhcid: failed to trigger events");
|
||||
|
||||
event_queue.run().expect("xhcid: failed to handle events");
|
||||
|
||||
unsafe {
|
||||
let _ = syscall::physunmap(address);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,13 @@ pub const HCS_PARAMS1_MAX_PORTS_SHIFT: u8 = 24;
|
||||
pub const HCS_PARAMS1_MAX_SLOTS_MASK: u32 = 0x0000_00FF;
|
||||
pub const HCS_PARAMS1_MAX_SLOTS_SHIFT: u8 = 0;
|
||||
|
||||
pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_MASK: u32 = 0xF800_0000;
|
||||
pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_SHIFT: u8 = 27;
|
||||
pub const HCS_PARAMS2_SPR_BIT: u32 = 1 << HCS_PARAMS2_SPR_SHIFT;
|
||||
pub const HCS_PARAMS2_SPR_SHIFT: u8 = 26;
|
||||
pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_MASK: u32 = 0x03E0_0000;
|
||||
pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_SHIFT: u8 = 21;
|
||||
|
||||
impl CapabilityRegs {
|
||||
pub fn lec(&self) -> bool {
|
||||
self.hcc_params2.readf(HCC_PARAMS2_LEC_BIT)
|
||||
@@ -48,4 +55,17 @@ impl CapabilityRegs {
|
||||
pub fn ext_caps_ptr_in_dwords(&self) -> u16 {
|
||||
((self.hcc_params1.read() & HCC_PARAMS1_XECP_MASK) >> HCC_PARAMS1_XECP_SHIFT) as u16
|
||||
}
|
||||
pub fn max_scratchpad_bufs_lo(&self) -> u8 {
|
||||
((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_MASK) >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_SHIFT)
|
||||
}
|
||||
pub fn spr(&self) -> bool {
|
||||
self.hcs_params2.readf(HCS_PARAMS2_SPR_BIT)
|
||||
}
|
||||
pub fn max_scratchpad_bufs_hi(&self) -> u8 {
|
||||
((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_MASK) >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_SHIFT)
|
||||
}
|
||||
pub fn max_scratchpad_bufs(&self) -> u16 {
|
||||
u16::from(self.max_scratchpad_bufs_lo())
|
||||
| (u16::from(self.max_scratchpad_bufs_hi()) << 5)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
use syscall::error::Result;
|
||||
|
||||
use super::event::EventRing;
|
||||
use super::ring::Ring;
|
||||
use super::trb::Trb;
|
||||
|
||||
pub struct CommandRing {
|
||||
pub ring: Ring,
|
||||
pub events: EventRing,
|
||||
}
|
||||
|
||||
impl CommandRing {
|
||||
pub fn new() -> Result<CommandRing> {
|
||||
Ok(CommandRing {
|
||||
ring: Ring::new(16, true)?,
|
||||
events: EventRing::new()?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn crcr(&self) -> u64 {
|
||||
self.ring.register()
|
||||
}
|
||||
|
||||
pub fn erdp(&self) -> u64 {
|
||||
let address = self.events.ring.register();
|
||||
address & 0xFFFF_FFFF_FFFF_FFF0
|
||||
}
|
||||
|
||||
pub fn erstba(&self) -> u64 {
|
||||
self.events.ste.physical() as u64
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> (&mut Trb, bool, &mut Trb) {
|
||||
let cmd = self.ring.next();
|
||||
let event = self.events.next();
|
||||
(cmd.0, cmd.1, event)
|
||||
}
|
||||
|
||||
pub fn next_cmd(&mut self) -> (&mut Trb, bool) {
|
||||
self.ring.next()
|
||||
}
|
||||
|
||||
pub fn next_event(&mut self) -> &mut Trb {
|
||||
self.events.next()
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ impl InputContext {
|
||||
|
||||
pub struct DeviceContextList {
|
||||
pub dcbaa: Dma<[u64; 256]>,
|
||||
pub contexts: Vec<Dma<DeviceContext>>,
|
||||
pub contexts: Box<[Dma<DeviceContext>]>,
|
||||
}
|
||||
|
||||
impl DeviceContextList {
|
||||
@@ -91,8 +91,8 @@ impl DeviceContextList {
|
||||
}
|
||||
|
||||
Ok(DeviceContextList {
|
||||
dcbaa: dcbaa,
|
||||
contexts: contexts,
|
||||
dcbaa,
|
||||
contexts: contexts.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -159,3 +159,41 @@ impl StreamContextArray {
|
||||
self.contexts.physical() as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ScratchpadBufferEntry {
|
||||
pub value: Mmio<u64>,
|
||||
}
|
||||
impl ScratchpadBufferEntry {
|
||||
pub fn set_addr(&mut self, addr: u64) {
|
||||
self.value.write(addr);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScratchpadBufferArray {
|
||||
pub entries: Dma<[ScratchpadBufferEntry]>,
|
||||
pub pages: Vec<usize>,
|
||||
}
|
||||
impl ScratchpadBufferArray {
|
||||
pub fn new(entries: u16) -> Result<Self> {
|
||||
let mut entries = unsafe { Dma::zeroed_unsized(entries as usize)? };
|
||||
|
||||
let pages = entries.iter_mut().map(|entry| {
|
||||
// TODO: When a better memory allocation API arrives (like the `mem:` scheme which I think is
|
||||
// being worked on), no assumptions about the page size always being 4k will have to be
|
||||
// made.
|
||||
let pointer = syscall::physalloc(4096);
|
||||
assert_eq!(pointer & 0xFFFF_FFFF_FFFF_F000, pointer, "physically allocated pointer (physalloc) wasn't 4k page-aligned");
|
||||
entry.set_addr(pointer);
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
entries,
|
||||
pages,
|
||||
})
|
||||
}
|
||||
pub fn register(&self) -> u64 {
|
||||
self.entries.physical()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ pub struct EventRingSte {
|
||||
_rsvd2: Mmio<u32>,
|
||||
}
|
||||
|
||||
// TODO: Use atomic operations, and perhaps an occasional lock for reallocating.
|
||||
pub struct EventRing {
|
||||
pub ste: Dma<[EventRingSte]>,
|
||||
pub ring: Ring,
|
||||
@@ -33,4 +34,10 @@ impl EventRing {
|
||||
pub fn next(&mut self) -> &mut Trb {
|
||||
self.ring.next().0
|
||||
}
|
||||
pub fn erdp(&self) -> u64 {
|
||||
self.ring.register() & 0xFFFF_FFFF_FFFF_FFF0
|
||||
}
|
||||
pub fn erstba(&self) -> u64 {
|
||||
self.ste.physical() as u64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
use super::Xhci;
|
||||
@@ -0,0 +1,395 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
use std::{mem, task, thread};
|
||||
|
||||
use crossbeam_channel::{Sender, Receiver};
|
||||
use futures::Stream;
|
||||
|
||||
use super::Xhci;
|
||||
use super::ring::Ring;
|
||||
use super::trb::{Trb, TrbCompletionCode, TrbType};
|
||||
|
||||
/// Short-term states (as in, they are removed when the waker is consumed, but probably pushed back
|
||||
/// by the future unless it completed).
|
||||
pub struct State {
|
||||
waker: task::Waker,
|
||||
kind: StateKind,
|
||||
message: Arc<Mutex<Option<NextEventTrb>>>,
|
||||
is_isoch_or_vf: bool,
|
||||
}
|
||||
|
||||
pub struct NextEventTrb {
|
||||
pub event_trb: Trb,
|
||||
pub src_trb: Option<Trb>,
|
||||
}
|
||||
|
||||
// TODO: Perhaps all of the transfer rings used by the xHC should be stored linearly, and then
|
||||
// indexed using this struct instead.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct RingId {
|
||||
pub slot: u8,
|
||||
pub endpoint_num: u8,
|
||||
pub stream_id: u16,
|
||||
}
|
||||
|
||||
/// The state specific to a TRB-type. Since some of the event TDs may asynchronously appear, for
|
||||
/// example the Command Completion Event and the Transfer Event TDs, they have to be
|
||||
/// distinguishable. Luckily, the xHC also gives us the actual (physical) pointer to the source
|
||||
/// TRB, from the command ring, unless the event TD has one the completion codes Ring Underrun,
|
||||
/// Ring Overrun, or VF Event Ring Full Error. When these errors are encountered, it simply
|
||||
/// indicates that the commands causing the errors continue to be pending, and thus no information
|
||||
/// is lost.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum StateKind {
|
||||
CommandCompletion { phys_ptr: u64 },
|
||||
Transfer { phys_ptr: u64, ring_id: RingId },
|
||||
Other(TrbType),
|
||||
}
|
||||
|
||||
impl StateKind {
|
||||
pub fn trb_type(&self) -> TrbType {
|
||||
match self.kind {
|
||||
Self::CommandCompletion { .. } => TrbType::CommandCompletion,
|
||||
Self::Transfer { .. } => TrbType::Transfer,
|
||||
Self::Other(ty) => ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct IrqReactor {
|
||||
hci: Arc<Xhci>,
|
||||
current_count: Arc<AtomicUsize>,
|
||||
irq_file: Option<File>,
|
||||
receiver: Receiver<NewPendingTrb>,
|
||||
|
||||
states: Vec<State>,
|
||||
|
||||
// TODO: Since the IRQ reactor is the only part of this driver that gets event TRBs, perhaps
|
||||
// the event ring should be owned here?
|
||||
}
|
||||
|
||||
pub type NewPendingTrb = State;
|
||||
|
||||
pub fn start_irq_reactor(hci: Arc<Xhci>, irq_file: Option<File>) -> thread::JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
IrqReactor::new(hci, irq_file).run()
|
||||
})
|
||||
}
|
||||
|
||||
impl IrqReactor {
|
||||
pub fn new(hci: Arc<Xhci>, irq_file: Option<File>) -> Self {
|
||||
Self {
|
||||
hci,
|
||||
irq_file,
|
||||
current_count: Arc::new(AtomicUsize::new()),
|
||||
}
|
||||
}
|
||||
// TODO: Configure the amount of time to be awaited when no more work can be done.
|
||||
fn pause(&self) {
|
||||
std::thread::yield_now();
|
||||
}
|
||||
fn run_polling(mut self) {
|
||||
loop {
|
||||
self.handle_requests();
|
||||
|
||||
let index = self.hci.primary_event_ring.lock().unwrap().next_index();
|
||||
|
||||
let mut trb;
|
||||
|
||||
'busy_waiting: loop {
|
||||
trb = self.hci.primary_event_ring.lock().unwrap().trbs[index];
|
||||
|
||||
if trb.completion_code() == TrbCompletionCode::Invalid as u8 {
|
||||
self.pause();
|
||||
continue 'busy_waiting;
|
||||
}
|
||||
}
|
||||
self.acknowledge(trb);
|
||||
self.update_erdp();
|
||||
}
|
||||
}
|
||||
fn run_with_irq_file(mut self) {
|
||||
'event_loop: loop {
|
||||
self.handle_requests();
|
||||
|
||||
let irq_file = self.irq_file.as_mut().unwrap();
|
||||
|
||||
let mut buffer = [0u8; 8];
|
||||
let bytes_read = self.irq_file.read(&mut buffer).expect("Failed to read from irq scheme");
|
||||
if bytes_read < mem::size_of::<usize>() {
|
||||
panic!("wrong number of bytes read from `irq:`: expected {}, got {}", mem::size_of::<usize>(), bytes_read);
|
||||
}
|
||||
|
||||
if !self.hci.received_irq() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let _ = self.irq_file.write(&buffer);
|
||||
|
||||
// TODO: More event rings, probably even with different IRQs.
|
||||
|
||||
let event_ring = self.hci.primary_event_ring.lock().unwrap();
|
||||
let trb = event_ring.next();
|
||||
|
||||
if trb.completion_code() == TrbCompletionCode::Invalid as u8 {
|
||||
println!("xhci: Received interrupt, but no event was found in the event ring. Ignoring interrupt.");
|
||||
continue 'event_loop;
|
||||
}
|
||||
|
||||
self.acknowledge(*trb);
|
||||
trb.reserved(false);
|
||||
|
||||
self.update_erdp();
|
||||
}
|
||||
}
|
||||
fn update_erdp(&self) {
|
||||
let dequeue_pointer_and_dcs = self.hci.primary_event_ring.lock().unwrap().register();
|
||||
let dequeue_pointer = dequeue_pointer_and_dcs & 0xFFFF_FFFF_FFFF_FFFE;
|
||||
assert_eq!(dequeue_pointer & 0xFFFF_FFFF_FFFF_FFF0, dequeue_pointer, "unaligned ERDP received from primary event ring");
|
||||
|
||||
self.xhci.run.lock().unwrap().ints[0].erdp.write(dequeue_pointer);
|
||||
}
|
||||
fn handle_requests(&mut self) {
|
||||
self.states.extend(self.receiver.try_iter());
|
||||
}
|
||||
fn acknowledge(&mut self, trb: Trb) {
|
||||
let mut index = 0;
|
||||
|
||||
loop {
|
||||
match self.states[index].kind {
|
||||
StateKind::CommandCompletion { phys_ptr } if trb.trb_type() == TrbType::CommandCompletion as u8 => if trb.completion_trb_pointer() == Some(phys_ptr) {
|
||||
let state = self.states.remove(index).unwrap();
|
||||
|
||||
// Before waking, it's crucial that the command TRB that generated this event
|
||||
// be fetched before removing this event TRB from the queue.
|
||||
let command_trb = match self.hci.command_ring.lock().unwrap().ring.phys_addr_to_entry_mut(phys_ptr) {
|
||||
Some(command_trb) => {
|
||||
let t = command_trb.clone();
|
||||
command_trb.reserved(false);
|
||||
t
|
||||
},
|
||||
None => {
|
||||
println!("The xHC supplied a pointer to a command TRB that was outside the known command ring bounds. Ignoring event TRB {:?}.", trb);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Validate the command TRB.
|
||||
*state.message.lock().unwrap() = Some(NextEventTrb {
|
||||
src_trb: command_trb.clone(),
|
||||
event_trb: trb,
|
||||
});
|
||||
|
||||
state.waker.wake();
|
||||
} else if trb.completion_trb_pointer().is_none() {
|
||||
println!("Command TRB somehow resulted in an error that only can be caused by transfer TRBs. Ignoring event TRB: {:?}.", trb);
|
||||
continue;
|
||||
} else {
|
||||
// The event TRB simply didn't match the current future
|
||||
continue;
|
||||
}
|
||||
|
||||
StateKind::Transfer { phys_ptr, ring_id } if trb.trb_type() == TrbType::Transfer as u8 => if let Some(src_trb) = self.xhc.lock().unwrap().get_transfer_trb(trb.transfer_event_trb_pointer(), ring_id) {
|
||||
if trb.transfer_event_trb_pointer() == Some(phys_ptr) {
|
||||
// Give the source transfer TRB together with the event TRB, to the future.
|
||||
|
||||
let state = self.states.remove(index).unwrap();
|
||||
*state.message.lock().unwrap() = Some(NextEventTrb {
|
||||
src_trb,
|
||||
event_trb: trb,
|
||||
});
|
||||
state.waker.wake();
|
||||
} else if trb.transfer_event_trb_pointer().is_none() {
|
||||
// Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full.
|
||||
//
|
||||
// These errors are caused when either an isoch transfer that shall write data, doesn't
|
||||
// have any data since the ring is empty, or if an isoch receive is impossible due to a
|
||||
// full ring. The Virtual Function Event Ring Full is only for Virtual Machine
|
||||
// Managers, and since this isn't implemented yet, they are irrelevant.
|
||||
//
|
||||
// The best solution here is to differentiate between isoch transfers (and
|
||||
// virtual function event rings when virtualization gets implemented), with
|
||||
// regular commands and transfers, and send the error TRB to all of them, or
|
||||
// possibly an error code wrapped in a Result.
|
||||
self.acknowledge_failed_transfer_trbs(trb);
|
||||
return;
|
||||
} else {
|
||||
// The event TRB simply didn't match the current future
|
||||
continue;
|
||||
}
|
||||
} else { continue }
|
||||
|
||||
StateKind::Other(trb_type) if trb_type as u8 == trb.trb_type() => {
|
||||
let state = self.states.remove(index).unwrap();
|
||||
state.waker.wake();
|
||||
}
|
||||
|
||||
_ => {
|
||||
index += 1;
|
||||
if index >= self.states.len() {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn acknowledge_failed_transfer_trbs(&mut self, trb: Trb) {
|
||||
let mut index = 0;
|
||||
|
||||
loop {
|
||||
if ! self.states[index].is_isoch_or_vf {
|
||||
index += 1;
|
||||
if index >= self.states.len() {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let state = self.states.remove(index).unwrap();
|
||||
*state.message.lock().unwrap() = Some(NextEventTrb {
|
||||
event_trb: trb,
|
||||
src_trb: None,
|
||||
});
|
||||
state.waker.wake();
|
||||
}
|
||||
}
|
||||
pub fn run(mut self) {
|
||||
if self.irq_file.is_some() {
|
||||
self.run_with_irq_file();
|
||||
} else {
|
||||
self.run_polling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FutureState {
|
||||
message: Arc<Mutex<Option<NextEventTrb>>>,
|
||||
is_isoch_or_vf: bool,
|
||||
state_kind: StateKind,
|
||||
}
|
||||
|
||||
enum EventTrbFuture {
|
||||
Pending { state: FutureState, sender: Sender<State>, },
|
||||
Finished,
|
||||
}
|
||||
|
||||
impl Future for EventTrbFuture {
|
||||
type Output = NextEventTrb;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, context: &mut task::Context) -> task::Poll<Self::Output> {
|
||||
match self.get_mut() {
|
||||
&mut Self::Pending { ref mut state, ref mut sender } => if let Some(message) = state.message.lock().unwrap().take() {
|
||||
*self.get_mut() = Self::Finished;
|
||||
|
||||
task::Poll::Ready(message)
|
||||
} else {
|
||||
sender.send(State {
|
||||
message: Arc::clone(&state.message),
|
||||
is_isoch_or_vf: state.is_isoch_or_vf,
|
||||
state_kind: state.state_kind,
|
||||
waker: context.waker().clone(),
|
||||
}).expect("IRQ reactor thread unexpectedly stopped");
|
||||
|
||||
task::Poll::Pending
|
||||
}
|
||||
&mut Self::Finished => panic!("Polling finished EventTrbFuture again."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
pub fn get_transfer_trb(&self, paddr: u64, id: RingId) -> Option<Trb> {
|
||||
self.with_ring(id, |ring| ring.phys_addr_to_entry(paddr))
|
||||
}
|
||||
pub fn with_ring<T, F: FnOnce(&Ring) -> T>(&self, id: RingId, function: F) -> T {
|
||||
use super::RingOrStreams;
|
||||
|
||||
let slot_state = self.slot_states.get(&id.slot)?;
|
||||
let endpoint_state = slot_state.endpoint_states.get(&id.endpoint_num)?;
|
||||
|
||||
let ring_ref = match endpoint_state.transfer {
|
||||
RingOrStreams::Ring(ref ring) => ring,
|
||||
RingOrStreams::Streams(ref ctx_arr) => ctx_arr.rings.get(&id.stream_id)?,
|
||||
};
|
||||
|
||||
function(ring_ref)
|
||||
}
|
||||
pub fn with_ring_mut<T, F: FnOnce(&mut Ring) -> T>(&self, id: RingId, function: F) -> T {
|
||||
use super::RingOrStreams;
|
||||
|
||||
let slot_state = self.slot_states.get(&id.slot)?;
|
||||
let endpoint_state = slot_state.endpoint_states.get_mut(&id.endpoint_num)?;
|
||||
|
||||
let ring_ref = match endpoint_state.transfer {
|
||||
RingOrStreams::Ring(ref mut ring) => ring,
|
||||
RingOrStreams::Streams(ref mut ctx_arr) => ctx_arr.rings.get_mut(&id.stream_id)?,
|
||||
};
|
||||
|
||||
function(ring_ref)
|
||||
}
|
||||
pub fn next_transfer_event_trb(&self, ring_id: RingId, trb: &Trb) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
||||
if ! trb.is_transfer_trb() {
|
||||
panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", trb.trb_type(), trb)
|
||||
}
|
||||
|
||||
let is_isoch_or_vf = trb.trb_type() == TrbType::Isoch as u8;
|
||||
|
||||
EventTrbFuture::Pending {
|
||||
state: FutureState {
|
||||
is_isoch_or_vf,
|
||||
state_kind: StateKind::Transfer {
|
||||
ring_id,
|
||||
phys_ptr: self.with_ring(ring_id, |ring| ring.trb_phys_ptr(trb)/*.expect("Invalid TRB: transfer TRB wasn't in the ring specified. Only direct references to the TRBs of a ring can be used (ring address range: {:p}-{:p}).", ring.start_addr(), ring.end_addr())*/),
|
||||
},
|
||||
message: Arc::new(Mutex::new(None)),
|
||||
},
|
||||
sender: self.irq_reactor_sender.clone(),
|
||||
}
|
||||
}
|
||||
pub fn next_command_completion_event_trb(&self, trb: &Trb) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
||||
if ! trb.is_command_trb() {
|
||||
panic!("Invalid TRB type given to next_command_completion_event_trb(): {} (TRB {:?}. Expected command TRB.", trb.trb_type(), trb)
|
||||
}
|
||||
|
||||
let command_ring = self.cmd.lock().unwrap();
|
||||
let ring = &command_ring.ring;
|
||||
|
||||
EventTrbFuture::Pending {
|
||||
state: FutureState {
|
||||
// This is only possible for transfers if they are isochronous, or for Force Event TRBs (virtualization).
|
||||
is_isoch_or_vf: false,
|
||||
state_kind: StateKind::CommandCompletion {
|
||||
phys_ptr: ring.trb_phys_ptr(trb),//.expect("Invalid TRB: expected a command TRB within the address range of the command TRB ({:p} {:p}), found TRB {:?} at {:p}", ring.start_addr(), ring.end_addr(), trb, trb)
|
||||
},
|
||||
message: Arc::new(Mutex::new(None)),
|
||||
},
|
||||
sender: self.irq_reactor_sender.clone(),
|
||||
}
|
||||
}
|
||||
pub fn next_misc_event_trb(&self, trb_type: TrbType) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
||||
let valid_trb_types = [
|
||||
TrbType::PortStatusChange as u8,
|
||||
TrbType::BandwidthRequest as u8,
|
||||
TrbType::Doorbell as u8,
|
||||
TrbType::HostController as u8,
|
||||
TrbType::DeviceNotification as u8,
|
||||
TrbType::MfindexWrap as u8,
|
||||
];
|
||||
if ! valid_trb_types.contains(&trb_type) {
|
||||
panic!("Invalid TRB type given to next_misc_event_trb(): {:?}. Only event TRB types that are neither transfer events or command completion events can be used.", trb_type)
|
||||
}
|
||||
EventTrbFuture::Pending {
|
||||
state: FutureState {
|
||||
is_isoch_or_vf: false,
|
||||
state_kind: StateKind::Other(trb_type),
|
||||
message: Arc::new(Mutex::new(None)),
|
||||
},
|
||||
sender: self.irq_reactor_sender.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
+134
-145
@@ -1,10 +1,14 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::{atomic::AtomicBool, Arc, Mutex, Weak};
|
||||
use std::{mem, process, slice, sync::atomic, task};
|
||||
use std::{mem, process, slice, sync::atomic, task, thread};
|
||||
|
||||
use chashmap::CHashMap;
|
||||
use crossbeam_channel::Sender;
|
||||
use serde::Deserialize;
|
||||
use syscall::error::{Error, Result, EBADF, EBADMSG, ENOENT};
|
||||
use syscall::io::{Dma, Io};
|
||||
@@ -15,12 +19,11 @@ use pcid_interface::msi::{MsixTableEntry, MsixCapability};
|
||||
use pcid_interface::{PcidServerHandle, PciFeature};
|
||||
|
||||
mod capability;
|
||||
mod command;
|
||||
mod context;
|
||||
mod doorbell;
|
||||
mod event;
|
||||
pub mod executor;
|
||||
mod extended;
|
||||
pub mod irq_reactor;
|
||||
mod operational;
|
||||
mod port;
|
||||
mod ring;
|
||||
@@ -29,9 +32,10 @@ pub mod scheme;
|
||||
mod trb;
|
||||
|
||||
use self::capability::CapabilityRegs;
|
||||
use self::command::CommandRing;
|
||||
use self::context::{DeviceContextList, InputContext, StreamContextArray};
|
||||
use self::context::{DeviceContextList, InputContext, ScratchpadBufferArray, StreamContextArray};
|
||||
use self::doorbell::Doorbell;
|
||||
use self::irq_reactor::NewPendingTrb;
|
||||
use self::event::EventRing;
|
||||
use self::extended::{CapabilityId, ExtendedCapabilitiesIter, ProtocolSpeed, SupportedProtoCap};
|
||||
use self::operational::OperationalRegs;
|
||||
use self::port::Port;
|
||||
@@ -43,6 +47,20 @@ use self::scheme::EndpIfState;
|
||||
|
||||
use crate::driver_interface::*;
|
||||
|
||||
pub enum InterruptMethod {
|
||||
/// No interrupts whatsoever; the driver will instead rely on polling event rings.
|
||||
Polling,
|
||||
|
||||
/// Legacy PCI INTx# interrupt pin.
|
||||
Intx,
|
||||
|
||||
/// Message signaled interrupts.
|
||||
Msi,
|
||||
|
||||
/// Extended message signaled interrupts.
|
||||
MsiX(Mutex<MsixInfo>),
|
||||
}
|
||||
|
||||
pub struct MsixInfo {
|
||||
pub virt_table_base: NonNull<MsixTableEntry>,
|
||||
pub virt_pba_base: NonNull<u64>,
|
||||
@@ -72,7 +90,7 @@ impl MsixInfo {
|
||||
|
||||
struct Device<'a> {
|
||||
ring: &'a mut Ring,
|
||||
cmd: &'a mut CommandRing,
|
||||
cmd: &'a mut Ring,
|
||||
db: &'a mut Doorbell,
|
||||
int: &'a mut Interrupter,
|
||||
}
|
||||
@@ -104,6 +122,7 @@ impl<'a> Device<'a> {
|
||||
|
||||
{
|
||||
let event = self.cmd.next_event();
|
||||
// TODO: Replace polling here as well.
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
@@ -144,43 +163,43 @@ impl<'a> Device<'a> {
|
||||
}
|
||||
|
||||
pub struct Xhci {
|
||||
cap: &'static mut CapabilityRegs,
|
||||
op: &'static mut OperationalRegs,
|
||||
ports: &'static mut [Port],
|
||||
dbs: &'static mut [Doorbell],
|
||||
run: &'static mut RuntimeRegs,
|
||||
dev_ctx: DeviceContextList,
|
||||
cmd: CommandRing,
|
||||
// immutable
|
||||
cap: &'static CapabilityRegs,
|
||||
|
||||
// XXX: It would be really useful to be able to mutably access individual elements of a slice,
|
||||
// without having to wrap every element in a lock (which wouldn't work since they're packed).
|
||||
op: Mutex<&'static mut OperationalRegs>,
|
||||
ports: Mutex<&'static mut [Port]>,
|
||||
dbs: Mutex<&'static mut [Doorbell]>,
|
||||
run: Mutex<&'static mut RuntimeRegs>,
|
||||
cmd: Mutex<Ring>,
|
||||
primary_event_ring: Mutex<EventRing>,
|
||||
|
||||
// immutable
|
||||
dev_ctx: DeviceContextList,
|
||||
scratchpad_buf_arr: Option<ScratchpadBufferArray>,
|
||||
|
||||
// used for the extended capabilities, and so far none of them are mutated, and thus no lock.
|
||||
base: *const u8,
|
||||
|
||||
handles: BTreeMap<usize, scheme::Handle>,
|
||||
handles: CHashMap<usize, scheme::Handle>,
|
||||
next_handle: usize,
|
||||
port_states: BTreeMap<usize, PortState>,
|
||||
port_states: CHashMap<usize, PortState>,
|
||||
|
||||
// TODO: Is this the correct implementation? I mean, there will be a really limited number of
|
||||
// IRQs, if not just one, and since we probably wont use a thread pool scheduler like those of
|
||||
// async-std or tokio, one could possibly assume that the futures themselves won't have to push
|
||||
// all the wakers.
|
||||
// TODO: This should probably be a BTreeMap (or just a VecMap) of states for each IRQ number,
|
||||
// if more than one are used. I'm not sure if the XHCI interrupters actually use different
|
||||
// IRQs, but it would make sense in case the hub has both isochronous (which trigger interrupts
|
||||
// reapeatedly with some time in between), bulk, control, etc. I might be wrong though...
|
||||
irq_state: Arc<IrqState>,
|
||||
|
||||
drivers: BTreeMap<usize, process::Child>,
|
||||
drivers: CHashMap<usize, process::Child>,
|
||||
scheme_name: String,
|
||||
|
||||
msi: bool,
|
||||
msix: bool,
|
||||
msix_info: Option<MsixInfo>,
|
||||
|
||||
pcid_handle: PcidServerHandle,
|
||||
interrupt_method: InterruptMethod,
|
||||
pcid_handle: Mutex<PcidServerHandle>,
|
||||
irq_reactor: Option<thread::JoinHandle<()>>,
|
||||
irq_reactor_sender: Sender<NewPendingTrb>,
|
||||
}
|
||||
|
||||
struct PortState {
|
||||
slot: u8,
|
||||
input_context: Dma<InputContext>,
|
||||
cfg_idx: Option<u8>,
|
||||
if_idx: Option<u8>,
|
||||
input_context: Mutex<Dma<InputContext>>,
|
||||
dev_desc: DevDesc,
|
||||
endpoint_states: BTreeMap<u8, EndpointState>,
|
||||
}
|
||||
@@ -204,7 +223,7 @@ impl EndpointState {
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
pub fn new(scheme_name: String, address: usize, msi: bool, msix: bool, msix_info: Option<MsixInfo>, handle: PcidServerHandle) -> Result<Xhci> {
|
||||
pub fn new(scheme_name: String, address: usize, interrupt_method: InterruptMethod, pcid_handle: PcidServerHandle) -> Result<Xhci> {
|
||||
let cap = unsafe { &mut *(address as *mut CapabilityRegs) };
|
||||
println!(" - CAP {:X}", address);
|
||||
|
||||
@@ -265,21 +284,17 @@ impl Xhci {
|
||||
dbs,
|
||||
run,
|
||||
dev_ctx: DeviceContextList::new(max_slots)?,
|
||||
cmd: CommandRing::new()?,
|
||||
cmd: Ring::new(),
|
||||
primary_event_ring: EventRing::new(),
|
||||
handles: BTreeMap::new(),
|
||||
next_handle: 0,
|
||||
port_states: BTreeMap::new(),
|
||||
|
||||
irq_state: Arc::new(IrqState {
|
||||
triggered: AtomicBool::new(false),
|
||||
wakers: Mutex::new(Vec::new()),
|
||||
}),
|
||||
drivers: BTreeMap::new(),
|
||||
scheme_name,
|
||||
msi,
|
||||
msix,
|
||||
msix_info,
|
||||
pcid_handle: handle,
|
||||
|
||||
interrupt_method,
|
||||
pcid_handle: Mutex::new(pcid_handle),
|
||||
};
|
||||
|
||||
xhci.init(max_slots);
|
||||
@@ -290,87 +305,87 @@ impl Xhci {
|
||||
pub fn init(&mut self, max_slots: u8) {
|
||||
// Set enabled slots
|
||||
println!(" - Set enabled slots to {}", max_slots);
|
||||
self.op.config.write(max_slots as u32);
|
||||
println!(" - Enabled Slots: {}", self.op.config.read() & 0xFF);
|
||||
self.op.get_mut().config.write(max_slots as u32);
|
||||
println!(" - Enabled Slots: {}", self.op.get_mut().config.read() & 0xFF);
|
||||
|
||||
// Set device context address array pointer
|
||||
let dcbaap = self.dev_ctx.dcbaap();
|
||||
println!(" - Write DCBAAP: {:X}", dcbaap);
|
||||
self.op.dcbaap.write(dcbaap as u64);
|
||||
self.op.get_mut().dcbaap.write(dcbaap as u64);
|
||||
|
||||
// Set command ring control register
|
||||
let crcr = self.cmd.crcr();
|
||||
let crcr = self.cmd.get_mut().register();
|
||||
assert_eq!(crcr & 0xFFFF_FFFF_FFFF_FFC1, crcr, "unaligned CRCR");
|
||||
println!(" - Write CRCR: {:X}", crcr);
|
||||
self.op.crcr.write(crcr as u64);
|
||||
self.op.get_mut().crcr.write(crcr as u64);
|
||||
|
||||
// Set event ring segment table registers
|
||||
println!(" - Interrupter 0: {:X}", self.run.ints.as_ptr() as usize);
|
||||
{
|
||||
self.run.ints[0].iman.writef(1, true); // clear interrupt pending if set earlier by the BIOS
|
||||
|
||||
println!("IP={}", self.run.ints[0].iman.readf(1));
|
||||
|
||||
/*if self.msi {
|
||||
self.pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI");
|
||||
println!("Enabled MSI");
|
||||
}*/
|
||||
if self.msix {
|
||||
self.pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X");
|
||||
println!("Enabled MSI-X");
|
||||
}
|
||||
|
||||
dbg!(self.pcid_handle.feature_info(PciFeature::MsiX).unwrap());
|
||||
dbg!(self.pcid_handle.feature_info(PciFeature::Msi).unwrap());
|
||||
let int = &mut self.run.get_mut().ints[0];
|
||||
|
||||
let erstz = 1;
|
||||
println!(" - Write ERSTZ: {}", erstz);
|
||||
self.run.ints[0].erstsz.write(erstz);
|
||||
int.erstsz.write(erstz);
|
||||
|
||||
let erdp = self.cmd.erdp();
|
||||
let erdp = self.primary_event_ring.get_mut().erdp();
|
||||
println!(" - Write ERDP: {:X}", erdp);
|
||||
self.run.ints[0].erdp.write(erdp as u64 | (1 << 3));
|
||||
int.erdp.write(erdp as u64 | (1 << 3));
|
||||
|
||||
let erstba = self.cmd.erstba();
|
||||
let erstba = self.primary_event_ring.get_mut().erstba();
|
||||
println!(" - Write ERSTBA: {:X}", erstba);
|
||||
self.run.ints[0].erstba.write(erstba as u64);
|
||||
int.erstba.write(erstba as u64);
|
||||
|
||||
println!(" - Write IMODC and IMODI: {} and {}", 0, 0);
|
||||
self.run.ints[0].imod.write(0);
|
||||
int.imod.write(0);
|
||||
|
||||
println!(" - Enable interrupts");
|
||||
self.run.ints[0].iman.writef(1 << 1, true);
|
||||
int.iman.writef(1 << 1, true);
|
||||
|
||||
}
|
||||
self.op.usb_cmd.writef(1 << 2, true);
|
||||
self.op.get_mut().usb_cmd.writef(1 << 2, true);
|
||||
|
||||
// Setup the scratchpad buffers that are required for the xHC to function.
|
||||
self.setup_scratchpads();
|
||||
|
||||
// Set run/stop to 1
|
||||
println!(" - Start");
|
||||
self.op.usb_cmd.writef(1, true);
|
||||
self.op.get_mut().usb_cmd.writef(1, true);
|
||||
|
||||
// Wait until controller is running
|
||||
println!(" - Wait for running");
|
||||
while self.op.usb_sts.readf(1) {
|
||||
while self.op.get_mut().usb_sts.readf(1) {
|
||||
println!(" - Waiting for XHCI running");
|
||||
}
|
||||
|
||||
println!("IP={}", self.run.ints[0].iman.readf(1));
|
||||
println!("IP={}", self.run.get_mut().ints[0].iman.readf(1));
|
||||
|
||||
// Ring command doorbell
|
||||
println!(" - Ring doorbell");
|
||||
self.dbs[0].write(0);
|
||||
self.dbs.get_mut()[0].write(0);
|
||||
|
||||
println!(" - XHCI initialized");
|
||||
}
|
||||
|
||||
pub fn setup_scratchpads(&mut self) -> Result<()> {
|
||||
let buf_count = self.cap.max_scratchpad_bufs();
|
||||
|
||||
if buf_count == 0 {
|
||||
return;
|
||||
}
|
||||
self.scratchpad_buf_arr = Some(ScratchpadBufferArray::new(buf_count)?);
|
||||
self.dev_ctx.dcbaa[0] = self.scratchpad_buf_arr.register();
|
||||
}
|
||||
|
||||
pub fn enable_port_slot(&mut self, slot_ty: u8) -> Result<u8> {
|
||||
assert_eq!(slot_ty & 0x1F, slot_ty);
|
||||
|
||||
let cloned_event_trb =
|
||||
self.execute_command("ENABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?;
|
||||
self.execute_command("ENABLE_SLOT", |cmd, cycle| cmd.enable_slot(slot_ty, cycle))?;
|
||||
Ok(cloned_event_trb.event_slot())
|
||||
}
|
||||
pub fn disable_port_slot(&mut self, slot: u8) -> Result<()> {
|
||||
self.execute_command("DISABLE_SLOT", |cmd, cycle| cmd.disable_slot(0, cycle))?;
|
||||
self.execute_command("DISABLE_SLOT", |cmd, cycle| cmd.disable_slot(slot, cycle))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -378,11 +393,12 @@ impl Xhci {
|
||||
self.dev_ctx.contexts[slot].slot.state()
|
||||
}
|
||||
|
||||
pub fn probe(&mut self) -> Result<()> {
|
||||
pub async fn probe(&self) -> Result<()> {
|
||||
println!("XHCI capabilities: {:?}", self.capabilities_iter().collect::<Vec<_>>());
|
||||
for i in 0..self.ports.len() {
|
||||
|
||||
for i in 0..self.ports.lock().unwrap().len() {
|
||||
let (data, state, speed, flags) = {
|
||||
let port = &self.ports[i];
|
||||
let port = &self.ports.lock().unwrap()[i];
|
||||
(port.read(), port.state(), port.speed(), port.flags())
|
||||
};
|
||||
println!(
|
||||
@@ -404,13 +420,13 @@ impl Xhci {
|
||||
println!(" - Slot {}", slot);
|
||||
|
||||
let mut input = Dma::<InputContext>::zeroed()?;
|
||||
let mut ring = self.address_device(&mut input, i, slot_ty, slot, speed)?;
|
||||
let mut ring = self.address_device(&mut input, i, slot_ty, slot, speed).await?;
|
||||
|
||||
let dev_desc = Self::get_dev_desc_raw(
|
||||
&mut self.ports,
|
||||
&mut self.run,
|
||||
&mut self.cmd,
|
||||
&mut self.dbs,
|
||||
&mut *self.ports.lock().unwrap(),
|
||||
&mut *self.run.lock().unwrap(),
|
||||
&mut *self.cmd.lock().unwrap(),
|
||||
&mut *self.dbs.lock().unwrap(),
|
||||
i,
|
||||
slot,
|
||||
&mut ring,
|
||||
@@ -420,8 +436,10 @@ impl Xhci {
|
||||
|
||||
let mut port_state = PortState {
|
||||
slot,
|
||||
input_context: input,
|
||||
input_context: Mutex::new(input),
|
||||
dev_desc,
|
||||
cfg_idx: None,
|
||||
if_idx: None,
|
||||
endpoint_states: std::iter::once((
|
||||
0,
|
||||
EndpointState {
|
||||
@@ -433,7 +451,7 @@ impl Xhci {
|
||||
};
|
||||
|
||||
if self.cap.cic() {
|
||||
self.op.set_cie(true);
|
||||
self.op.lock().unwrap().set_cie(true);
|
||||
}
|
||||
|
||||
/*match self.spawn_drivers(i, &mut port_state) {
|
||||
@@ -474,8 +492,8 @@ impl Xhci {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn address_device(
|
||||
&mut self,
|
||||
pub async fn address_device(
|
||||
&self,
|
||||
input_context: &mut Dma<InputContext>,
|
||||
i: usize,
|
||||
slot_ty: u8,
|
||||
@@ -563,23 +581,45 @@ impl Xhci {
|
||||
|
||||
let input_context_physical = input_context.physical();
|
||||
|
||||
self.execute_command("ADDRESS_DEVICE", |trb, cycle| {
|
||||
let (event_trb, _) = self.execute_command(|trb, cycle| {
|
||||
trb.address_device(slot, input_context_physical, false, cycle)
|
||||
})
|
||||
.expect("ADDRESS_DEVICE failed");
|
||||
}).await;
|
||||
|
||||
if event_trb.completion_code() != TrbCompletionCode::Success as u8 {
|
||||
println!("Failed to address device at slot {} (port {})", slot, i);
|
||||
}
|
||||
|
||||
Ok(ring)
|
||||
}
|
||||
|
||||
pub fn uses_msi(&self) -> bool {
|
||||
if let InterruptMethod::Msi = self.interrupt_method { true } else { false }
|
||||
}
|
||||
pub fn uses_msix(&self) -> bool {
|
||||
if let InterruptMethod::MsiX(_) = self.interrupt_method { true } else { false }
|
||||
}
|
||||
pub fn msix_info(&self) -> Option<&MsixInfo> {
|
||||
match self.interrupt_method {
|
||||
InterruptMethod::MsiX(ref info) => Some(info),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn msix_info_mut(&mut self) -> Option<&mut MsixInfo> {
|
||||
match self.interrupt_method {
|
||||
InterruptMethod::MsiX(ref mut info) => Some(info),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether an IRQ has been received from *this* device, in case of an interrupt. Always
|
||||
/// true when using MSI/MSI-X.
|
||||
pub fn received_irq(&mut self) -> bool {
|
||||
if self.msi || self.msix {
|
||||
if self.uses_msi() || self.uses_msix() {
|
||||
// Since using MSI and MSI-X implies having no IRQ sharing whatsoever, the IP bit
|
||||
// doesn't have to be touched.
|
||||
println!("Successfully received MSI/MSI-X interrupt, IP={}, EHB={}", self.run.ints[0].iman.readf(1), self.run.ints[0].erdp.readf(3));
|
||||
println!("MSI-X PB={}", self.msix_info.as_mut().unwrap().pba(0));
|
||||
let entry = self.msix_info.as_mut().unwrap().table_entry_pointer(0);
|
||||
println!("MSI-X PB={}", self.msix_info_mut().unwrap().pba(0));
|
||||
let entry = self.msix_info_mut().unwrap().table_entry_pointer(0);
|
||||
println!("MSI-X entry (addr_lo, addr_hi, msg_data, vec_ctl: {:#0x} {:#0x} {:#0x} {:#0x}", entry.addr_lo.read(), entry.addr_hi.read(), entry.msg_data.read(), entry.vec_ctl.read());
|
||||
true
|
||||
} else if self.run.ints[0].iman.readf(1) {
|
||||
@@ -595,17 +635,7 @@ impl Xhci {
|
||||
}
|
||||
|
||||
}
|
||||
/// Handle an IRQ event.
|
||||
pub fn on_irq(&mut self) {
|
||||
// Wake all futures awaiting the IRQ.
|
||||
for waker in self.irq_state.wakers.lock().unwrap().drain(..) {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
pub(crate) fn irq(&self) -> IrqFuture {
|
||||
IrqFuture {
|
||||
state: IrqFutureState::Pending(Arc::downgrade(&self.irq_state)),
|
||||
}
|
||||
}
|
||||
fn spawn_drivers(&mut self, port: usize, ps: &mut PortState) -> Result<()> {
|
||||
// TODO: There should probably be a way to select alternate interfaces, and not just the
|
||||
@@ -784,44 +814,3 @@ lazy_static! {
|
||||
toml::from_slice::<DriversConfig>(TOML).expect("Failed to parse internally embedded config file")
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) struct IrqFuture {
|
||||
state: IrqFutureState,
|
||||
}
|
||||
|
||||
struct IrqState {
|
||||
triggered: AtomicBool,
|
||||
// TODO: Perhaps a channel?
|
||||
wakers: Mutex<Vec<task::Waker>>,
|
||||
}
|
||||
|
||||
enum IrqFutureState {
|
||||
Pending(Weak<IrqState>),
|
||||
Finished,
|
||||
}
|
||||
|
||||
impl std::future::Future for IrqFuture {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, context: &mut task::Context) -> task::Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
|
||||
match &mut this.state {
|
||||
// TODO: Ordering?
|
||||
IrqFutureState::Pending(state_weak) => {
|
||||
let state = state_weak.upgrade().expect(
|
||||
"IRQ futures keep getting polled even after the driver has been deinitialized",
|
||||
);
|
||||
|
||||
if state.triggered.load(atomic::Ordering::SeqCst) {
|
||||
this.state = IrqFutureState::Finished;
|
||||
task::Poll::Ready(())
|
||||
} else {
|
||||
state.wakers.lock().unwrap().push(context.waker().clone());
|
||||
task::Poll::Pending
|
||||
}
|
||||
}
|
||||
IrqFutureState::Finished => panic!("polling finished future"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+52
-1
@@ -1,3 +1,5 @@
|
||||
use std::mem;
|
||||
|
||||
use syscall::error::Result;
|
||||
use syscall::io::Dma;
|
||||
|
||||
@@ -26,7 +28,7 @@ impl Ring {
|
||||
addr as u64 | self.cycle as u64
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> (&mut Trb, bool) {
|
||||
pub fn next_index(&mut self) -> usize {
|
||||
let mut i;
|
||||
loop {
|
||||
i = self.i;
|
||||
@@ -45,7 +47,11 @@ impl Ring {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> (&mut Trb, bool) {
|
||||
let i = self.next_index();
|
||||
(&mut self.trbs[i], self.cycle)
|
||||
}
|
||||
/// Endless iterator that iterates through the ring items, over and over again. The iterator
|
||||
@@ -53,6 +59,51 @@ impl Ring {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Trb> + '_ {
|
||||
Iter { ring: self, i: self.i }
|
||||
}
|
||||
/// Takes a physical address and returns the index into this ring, that the index represents.
|
||||
/// Returns `None` if the address is outside the bounds of this ring.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if paddr is not a multiple of 16 bytes, i.e. the size of a TRB.
|
||||
pub fn phys_addr_to_index(&self, paddr: u64) -> Option<usize> {
|
||||
let base = self.trbs.physical();
|
||||
let offset = paddr.checked_sub(base)?;
|
||||
|
||||
assert_eq!(offset % mem::size_of::<Trb>(), 0, "unaligned TRB physical address");
|
||||
|
||||
let index = offset / mem::size_of::<Trb>();
|
||||
|
||||
if index > self.trbs.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(index)
|
||||
}
|
||||
pub fn phys_addr_to_entry_ref(&self, paddr: u64) -> Option<&Trb> {
|
||||
&self.trbs[self.phys_addr_to_index(paddr)]
|
||||
}
|
||||
pub fn phys_addr_to_entry_mut(&self, paddr: u64) -> Option<&mut Trb> {
|
||||
&mut self.trbs[self.phys_addr_to_index(paddr)]
|
||||
}
|
||||
pub fn phys_addr_to_entry(&self, paddr: u64) -> Option<Trb> {
|
||||
self.trbs[self.phys_addr_to_index(paddr)].clone()
|
||||
}
|
||||
pub(crate) fn start_virt_addr(&self) -> *const Trb {
|
||||
self.trbs.as_ptr()
|
||||
}
|
||||
pub(crate) fn end_virt_addr(&self) -> *const Trb {
|
||||
unsafe { self.start_virt_addr().offset(self.trbs.len()) }
|
||||
}
|
||||
pub fn trb_phys_ptr(&self, trb: &Trb) -> u64 {
|
||||
let trb_virt_pointer = trb as *const Trb;
|
||||
let trbs_base_virt_pointer = self.trbs.as_ptr();
|
||||
|
||||
if trb_virt_pointer < trbs_base_virt_pointer || trb_virt_pointer > trbs_base_virt_pointer + self.trbs.len() * mem::size_of::<Trb>() {
|
||||
panic!("Gave a TRB outside of the ring, when retrieving its physical address in that ring. TRB: {:?} (at address {:p})", trb, trb);
|
||||
}
|
||||
let trbs_base_phys_ptr = self.trbs.physical();
|
||||
let trb_phys_ptr = trb_virt_pointer - trbs_base_phys_ptr;
|
||||
trb_phys_ptr
|
||||
}
|
||||
/*
|
||||
/// Endless mutable iterator that iterates through the ring items, over and over again. The
|
||||
/// iterator doesn't enqueue or dequeue anything, but the trbs are mutably borrowed.
|
||||
|
||||
+191
-192
@@ -17,7 +17,6 @@ use syscall::{
|
||||
use super::{port, usb};
|
||||
use super::{Device, EndpointState, Xhci};
|
||||
|
||||
use super::command::CommandRing;
|
||||
use super::context::{
|
||||
InputContext, SlotState, StreamContext, StreamContextArray, StreamContextType,
|
||||
ENDPOINT_CONTEXT_STATUS_MASK,
|
||||
@@ -211,40 +210,38 @@ impl AnyDescriptor {
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
pub fn execute_command<F: FnOnce(&mut Trb, bool)>(
|
||||
&mut self,
|
||||
cmd_name: &str,
|
||||
f: F,
|
||||
) -> Result<Trb> {
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp() | (1 << 3));
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
|
||||
pub fn execute_command_noreply<F: FnOnce(&mut Trb, bool)>(&self, f: F) {
|
||||
let (cmd, cycle) = self.cmd.lock().unwrap();
|
||||
f(cmd, cycle);
|
||||
|
||||
println!("INTE={}", self.op.usb_cmd.readf(1 << 2));
|
||||
println!("IP={}", self.run.ints[0].iman.readf(1));
|
||||
self.dbs[0].write(0);
|
||||
println!("IP={}", self.run.ints[0].iman.readf(1));
|
||||
self.dbs.lock().unwrap()[0].write(0);
|
||||
|
||||
while event.data.read() == 0/* || self.run.ints[0].iman.readf(1)*/ {
|
||||
println!(" - {} Waiting for event", cmd_name);
|
||||
}
|
||||
println!("IP={}", self.run.ints[0].iman.readf(1));
|
||||
// TODO: It's still possible not to reset the TRB, right?
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
println!("{} failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", cmd_name, event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read());
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
pub async fn execute_command<F: FnOnce(&mut Trb, bool)>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> (Trb, Trb) {
|
||||
let next_event = {
|
||||
let command_ring = self.cmd.lock().unwrap();
|
||||
|
||||
let ret = event.clone();
|
||||
let (cmd, cycle) = command_ring.next();
|
||||
f(cmd, cycle);
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
// get the future here before awaiting, to destroy the lock before deadlock
|
||||
self.next_command_completion_event_trb(&cmd)
|
||||
};
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp() | (1 << 3));
|
||||
Ok(ret)
|
||||
self.dbs.lock().unwrap()[0].write(0);
|
||||
|
||||
let trbs = next_event.await;
|
||||
let event_trb = trbs.event_trb;
|
||||
let command_trb = trbs.src_trb.expect("Command completion event TRBs shall always have a valid pointer to a valid source command TRB");
|
||||
|
||||
assert_eq!(command_trb.trb_type(), TrbType::CommandCompletion as u8, "The IRQ reactor (or the xHC) gave an invalid event TRB");
|
||||
|
||||
(event_trb, command_trb)
|
||||
}
|
||||
pub fn execute_control_transfer<D>(
|
||||
&mut self,
|
||||
@@ -418,8 +415,16 @@ impl Xhci {
|
||||
)
|
||||
}
|
||||
|
||||
fn reset_endpoint(&mut self, port_num: usize, endp_num: u8, tsp: bool) -> Result<()> {
|
||||
let endp_num_xhc = Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?);
|
||||
async fn reset_endpoint(&mut self, port_num: usize, endp_num: u8, tsp: bool) -> Result<()> {
|
||||
let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let (cfg_idx, if_idx) = match (port_state.cfg_idx, port_state.if_idx) {
|
||||
(Some(c), Some(i)) => (c, i),
|
||||
_ => return Err(Error::new(EIO)),
|
||||
};
|
||||
|
||||
let endp_desc = port_state.dev_desc.config_descs[usize::from(cfg_idx)].interface_descs[usize::from(if_idx)].endpoints.get(usize::from(endp_num)).ok_or(Error::new(EBADFD))?;
|
||||
let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc);
|
||||
|
||||
let slot = self
|
||||
.port_states
|
||||
@@ -427,10 +432,10 @@ impl Xhci {
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.slot;
|
||||
|
||||
self.execute_command("RESET_ENDPOINT", |trb, cycle| {
|
||||
trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle)
|
||||
})?;
|
||||
Ok(())
|
||||
let (event_trb, command_trb) = self.execute_command(|trb, cycle| {
|
||||
trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle);
|
||||
}).await;
|
||||
handle_event_trb("RESET_ENDPOINT", &event_trb, &command_trb)
|
||||
}
|
||||
|
||||
fn endp_ctx_interval(speed_id: &ProtocolSpeed, endp_desc: &EndpDesc) -> u8 {
|
||||
@@ -507,62 +512,14 @@ impl Xhci {
|
||||
}
|
||||
}
|
||||
|
||||
fn port_state(&self, port: usize) -> Result<&super::PortState> {
|
||||
fn port_state(&self, port: usize) -> Result<chashmap::ReadGuard<'_, usize, PortState>> {
|
||||
self.port_states.get(&port).ok_or(Error::new(EBADF))
|
||||
}
|
||||
fn port_state_mut(&mut self, port: usize) -> Result<&mut super::PortState> {
|
||||
self.port_states.get_mut(&port).ok_or(Error::new(EBADF))
|
||||
}
|
||||
fn endpoint_state_mut(&mut self, port: usize, endp_num: u8) -> Result<&mut EndpointState> {
|
||||
self.port_state_mut(port)?
|
||||
.endpoint_states
|
||||
.get_mut(&endp_num)
|
||||
.ok_or(Error::new(EBADF))
|
||||
}
|
||||
fn input_context(&mut self, port: usize) -> Result<&mut Dma<InputContext>> {
|
||||
Ok(&mut self.port_state_mut(port)?.input_context)
|
||||
}
|
||||
fn endp_ctx(
|
||||
&mut self,
|
||||
port: usize,
|
||||
endp_num_xhc: u8,
|
||||
) -> Result<&mut super::context::EndpointContext> {
|
||||
Ok(self
|
||||
.input_context(port)?
|
||||
.device
|
||||
.endpoints
|
||||
.get_mut(endp_num_xhc as usize - 1)
|
||||
.ok_or(Error::new(EIO))?)
|
||||
}
|
||||
fn dev_desc(&self, port: usize) -> Result<&DevDesc> {
|
||||
Ok(&self.port_state(port)?.dev_desc)
|
||||
}
|
||||
fn config_descs(&self, port: usize) -> Result<&[ConfDesc]> {
|
||||
Ok(&self.dev_desc(port)?.config_descs)
|
||||
}
|
||||
fn config_desc(&self, port: usize, desc: u8) -> Result<&ConfDesc> {
|
||||
Ok(self
|
||||
.config_descs(port)?
|
||||
.get(usize::from(desc))
|
||||
.ok_or(Error::new(EBADF))?)
|
||||
}
|
||||
fn endp_descs(&self, port: usize, config_desc: u8, if_desc: u8) -> Result<&[EndpDesc]> {
|
||||
Ok(&self
|
||||
.port_state(port)?
|
||||
.dev_desc
|
||||
.config_descs
|
||||
.get(usize::from(config_desc))
|
||||
.ok_or(Error::new(EIO))?
|
||||
.interface_descs
|
||||
.get(usize::from(if_desc))
|
||||
.ok_or(Error::new(EIO))?
|
||||
.endpoints)
|
||||
}
|
||||
fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> {
|
||||
async fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> {
|
||||
let mut req: ConfigureEndpointsReq =
|
||||
serde_json::from_slice(json_buf).or(Err(Error::new(EBADMSG)))?;
|
||||
|
||||
if (!self.cap.cic() || !self.op.cie())
|
||||
if (!self.cap.cic() || !self.op.lock().unwrap().cie())
|
||||
&& (req.config_desc != 0 || req.interface_desc != None || req.alternate_setting != None)
|
||||
{
|
||||
//return Err(Error::new(EOPNOTSUPP));
|
||||
@@ -574,9 +531,11 @@ impl Xhci {
|
||||
return Err(Error::new(EBADMSG));
|
||||
}
|
||||
|
||||
let (endp_desc_count, new_context_entries) = {
|
||||
let endpoints =
|
||||
self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?;
|
||||
let (endp_desc_count, new_context_entries, configuration_value) = {
|
||||
let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?;
|
||||
let config_desc = port_state.dev_desc.config_descs.get(usize::from(req.config_desc)).ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let endpoints = config_desc.interface_descs.get(usize::from(req.interface_desc.unwrap_or(0))).ok_or(Error::new(EBADFD))?.endpoints;
|
||||
|
||||
if endpoints.len() >= 31 {
|
||||
return Err(Error::new(EIO));
|
||||
@@ -588,18 +547,20 @@ impl Xhci {
|
||||
Some(l) => Self::endp_num_to_dci(endpoints.len() as u8, l),
|
||||
None => 1,
|
||||
}) + 1,
|
||||
config_desc.configuration_value,
|
||||
)
|
||||
};
|
||||
let lec = self.cap.lec();
|
||||
let log_max_psa_size = self.cap.max_psa_size();
|
||||
|
||||
let port_speed_id = self.ports[port].speed();
|
||||
let port_speed_id = self.ports.lock().unwrap()[port].speed();
|
||||
let speed_id: &ProtocolSpeed = self
|
||||
.lookup_psiv(port as u8, port_speed_id)
|
||||
.ok_or(Error::new(EIO))?;
|
||||
|
||||
{
|
||||
let input_context = self.input_context(port)?;
|
||||
let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?;
|
||||
let input_context = port_state.input_context.lock().unwrap();
|
||||
|
||||
// Configure the slot context as well, which holds the last index of the endp descs.
|
||||
input_context.add_context.write(1);
|
||||
@@ -625,9 +586,9 @@ impl Xhci {
|
||||
for endp_idx in 0..endp_desc_count as u8 {
|
||||
let endp_num = endp_idx + 1;
|
||||
|
||||
let endpoints =
|
||||
self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?;
|
||||
let dev_desc = self.dev_desc(port)?;
|
||||
let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?;
|
||||
let dev_desc = &port_state.dev_desc;
|
||||
let endpoints = &dev_desc.config_descs.get(usize::from(req.config_desc)).ok_or(Error::new(EBADFD))?.interface_descs.get(usize::from(req.interface_desc.unwrap_or(0))).ok_or(Error::new(EBADFD))?.endpoints;
|
||||
let endp_desc = endpoints.get(endp_idx as usize).ok_or(Error::new(EIO))?;
|
||||
|
||||
let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc);
|
||||
@@ -685,7 +646,7 @@ impl Xhci {
|
||||
assert_eq!(max_error_count & 0x3, max_error_count);
|
||||
assert_ne!(ep_ty, 0); // 0 means invalid.
|
||||
|
||||
let port_state = self.port_state_mut(port)?;
|
||||
let port_state = self.port_states.get_mut(&port).ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let ring_ptr = if usb_log_max_streams.is_some() {
|
||||
let mut array = StreamContextArray::new(1 << (primary_streams + 1))?;
|
||||
@@ -728,10 +689,11 @@ impl Xhci {
|
||||
};
|
||||
assert_eq!(primary_streams & 0x1F, primary_streams);
|
||||
|
||||
let input_context = self.input_context(port)?;
|
||||
let port_state = self.port_states.get_mut(&port).ok_or(Error::new(EBADFD))?;
|
||||
let input_context = port_state.input_context.lock().unwrap();
|
||||
input_context.add_context.writef(1 << endp_num_xhc, true);
|
||||
|
||||
let endp_ctx = self.endp_ctx(port, endp_num_xhc)?;
|
||||
let endp_ctx = input_context.device.endpoints.get_mut(endp_num_xhc as usize - 1).ok_or(Error::new(EIO))?;
|
||||
|
||||
endp_ctx.a.write(
|
||||
u32::from(mult) << 8
|
||||
@@ -756,14 +718,17 @@ impl Xhci {
|
||||
.write(u32::from(avg_trb_len) | (u32::from(max_esit_payload_lo) << 16));
|
||||
}
|
||||
|
||||
let slot = self.port_state(port)?.slot;
|
||||
let input_context_physical = self.input_context(port)?.physical();
|
||||
self.execute_command("CONFIGURE_ENDPOINT", |trb, cycle| {
|
||||
let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?;
|
||||
let slot = port_state.slot;
|
||||
let input_context_physical = port_state.input_context.lock().unwrap().physical();
|
||||
|
||||
let (event_trb, command_trb) = self.execute_command(|trb, cycle| {
|
||||
trb.configure_endpoint(slot, input_context_physical, cycle)
|
||||
})?;
|
||||
}).await;
|
||||
|
||||
handle_event_trb("CONFIGURE_ENDPOINT", &event_trb, &command_trb)?;
|
||||
|
||||
// Tell the device about this configuration.
|
||||
let configuration_value = self.config_desc(port, req.config_desc)?.configuration_value;
|
||||
self.set_configuration(port, configuration_value)?;
|
||||
|
||||
if let (Some(interface_num), Some(alternate_setting)) =
|
||||
@@ -819,12 +784,6 @@ impl Xhci {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
fn endp_desc(&self, port_num: usize, endp_num: u8) -> Result<&EndpDesc> {
|
||||
Ok(self
|
||||
.endp_descs(port_num, 0, 0)?
|
||||
.get(usize::from(endp_num) - 1)
|
||||
.ok_or(Error::new(EBADF))?)
|
||||
}
|
||||
fn endp_doorbell(endp_num: u8, desc: &EndpDesc, stream_id: u16) -> u32 {
|
||||
let db_target = Self::endp_num_to_dci(endp_num, desc);
|
||||
let db_task_id: u16 = stream_id;
|
||||
@@ -969,11 +928,16 @@ impl Xhci {
|
||||
.port_states
|
||||
.get_mut(&port_id)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
let ports = self.ports.lock().unwrap();
|
||||
let run = self.run.lock().unwrap();
|
||||
let cmd = self.cmd.lock().unwrap();
|
||||
let dbs = self.dbs.lock().unwrap();
|
||||
|
||||
Self::get_dev_desc_raw(
|
||||
&mut self.ports,
|
||||
&mut self.run,
|
||||
&mut self.cmd,
|
||||
&mut self.dbs,
|
||||
&mut *ports,
|
||||
&mut *run,
|
||||
&mut *cmd,
|
||||
&mut *dbs,
|
||||
port_id,
|
||||
st.slot,
|
||||
st.endpoint_states
|
||||
@@ -986,7 +950,7 @@ impl Xhci {
|
||||
pub(crate) fn get_dev_desc_raw(
|
||||
ports: &mut [port::Port],
|
||||
run: &mut RuntimeRegs,
|
||||
cmd: &mut CommandRing,
|
||||
cmd: &mut Ring,
|
||||
dbs: &mut [Doorbell],
|
||||
port_id: usize,
|
||||
slot: u8,
|
||||
@@ -999,7 +963,7 @@ impl Xhci {
|
||||
|
||||
// TODO: Should the descriptors be stored in PortState?
|
||||
|
||||
run.ints[0].erdp.write(cmd.erdp() | (1 << 3));
|
||||
run.ints[0].erdp.write(cmd.register() | (1 << 3));
|
||||
|
||||
let mut dev = Device {
|
||||
ring,
|
||||
@@ -1148,11 +1112,6 @@ impl Xhci {
|
||||
setup: usb::Setup,
|
||||
transfer_kind: TransferKind,
|
||||
) -> Result<()> {
|
||||
// TODO: This json format might be too high level, but is useful for debugging,
|
||||
// but when actual device-specific drivers are written, a binary format would
|
||||
// be better. Maybe something simple like bincode could be used, if a custom binary struct
|
||||
// is too much overkill.
|
||||
|
||||
self.execute_control_transfer(
|
||||
port_num,
|
||||
setup,
|
||||
@@ -1242,7 +1201,8 @@ impl Xhci {
|
||||
PortReqState::WaitingForDeviceBytes(_, _) => return Err(Error::new(EBADF)),
|
||||
PortReqState::Tmp | PortReqState::TmpSetup(_) => unreachable!(),
|
||||
};
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
let guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
match &mut *guard {
|
||||
Handle::PortReq(_, ref mut state) => *state = st,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -1272,7 +1232,9 @@ impl Xhci {
|
||||
}
|
||||
PortReqState::Tmp | PortReqState::TmpSetup(_) => unreachable!(),
|
||||
};
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
|
||||
let guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
match &mut *guard {
|
||||
Handle::PortReq(_, ref mut state) => *state = st,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -1306,8 +1268,9 @@ impl SchemeMut for Xhci {
|
||||
if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 {
|
||||
let mut contents = Vec::new();
|
||||
|
||||
for (index, _) in self
|
||||
.ports
|
||||
let ports_guard = self.ports.lock().unwrap();
|
||||
|
||||
for (index, _) in ports_guard
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, port)| port.flags().contains(port::PortFlags::PORT_CCS))
|
||||
@@ -1455,34 +1418,36 @@ impl SchemeMut for Xhci {
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
match self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::TopLevel(_, ref buf)
|
||||
| Handle::Port(_, _, ref buf)
|
||||
| Handle::Endpoints(_, _, ref buf) => {
|
||||
let guard = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match &*guard {
|
||||
&Handle::TopLevel(_, ref buf)
|
||||
| &Handle::Port(_, _, ref buf)
|
||||
| &Handle::Endpoints(_, _, ref buf) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = buf.len() as u64;
|
||||
}
|
||||
Handle::PortDesc(_, _, ref buf) => {
|
||||
&Handle::PortDesc(_, _, ref buf) => {
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = buf.len() as u64;
|
||||
}
|
||||
Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _))
|
||||
| Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => {
|
||||
&Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _))
|
||||
| &Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => {
|
||||
stat.st_mode = MODE_CHR;
|
||||
stat.st_size = buf.len() as u64;
|
||||
}
|
||||
Handle::PortReq(_, PortReqState::Tmp)
|
||||
| Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(),
|
||||
&Handle::PortReq(_, PortReqState::Tmp)
|
||||
| &Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(),
|
||||
|
||||
Handle::PortState(_, _) | Handle::PortReq(_, _) => stat.st_mode = MODE_CHR,
|
||||
Handle::Endpoint(_, _, st) => match st {
|
||||
EndpointHandleTy::Ctl | EndpointHandleTy::Data => stat.st_mode = MODE_CHR,
|
||||
EndpointHandleTy::Root(_, ref buf) => {
|
||||
&Handle::PortState(_, _) | &Handle::PortReq(_, _) => stat.st_mode = MODE_CHR,
|
||||
&Handle::Endpoint(_, _, ref st) => match st {
|
||||
&EndpointHandleTy::Ctl | &EndpointHandleTy::Data => stat.st_mode = MODE_CHR,
|
||||
&EndpointHandleTy::Root(_, ref buf) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = buf.len() as u64;
|
||||
}
|
||||
},
|
||||
Handle::ConfigureEndpoints(_) => {
|
||||
&Handle::ConfigureEndpoints(_) => {
|
||||
stat.st_mode = MODE_CHR | 0o200; // write only
|
||||
}
|
||||
}
|
||||
@@ -1490,21 +1455,22 @@ impl SchemeMut for Xhci {
|
||||
}
|
||||
|
||||
fn fpath(&mut self, fd: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
// XXX: write!() should return the length instead of ().
|
||||
let mut src = Vec::<u8>::new();
|
||||
match self.handles.get(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::TopLevel(_, _) => write!(src, "/").unwrap(),
|
||||
Handle::Port(port_num, _, _) => write!(src, "/port{}/", port_num).unwrap(),
|
||||
Handle::PortDesc(port_num, _, _) => {
|
||||
write!(src, "/port{}/descriptors", port_num).unwrap()
|
||||
let cursor = io::Cursor::new(buffer);
|
||||
|
||||
let guard = self.handles.get(&fd).ok_or(Error::new(EBADF))?;
|
||||
match &*guard {
|
||||
&Handle::TopLevel(_, _) => write!(cursor, "/").unwrap(),
|
||||
&Handle::Port(port_num, _, _) => write!(cursor, "/port{}/", port_num).unwrap(),
|
||||
&Handle::PortDesc(port_num, _, _) => {
|
||||
write!(cursor, "/port{}/descriptors", port_num).unwrap()
|
||||
}
|
||||
Handle::PortState(port_num, _) => write!(src, "/port{}/state", port_num).unwrap(),
|
||||
Handle::PortReq(port_num, _) => write!(src, "/port{}/request", port_num).unwrap(),
|
||||
Handle::Endpoints(port_num, _, _) => {
|
||||
write!(src, "/port{}/endpoints/", port_num).unwrap()
|
||||
&Handle::PortState(port_num, _) => write!(cursor, "/port{}/state", port_num).unwrap(),
|
||||
&Handle::PortReq(port_num, _) => write!(cursor, "/port{}/request", port_num).unwrap(),
|
||||
&Handle::Endpoints(port_num, _, _) => {
|
||||
write!(cursor, "/port{}/endpoints/", port_num).unwrap()
|
||||
}
|
||||
Handle::Endpoint(port_num, endp_num, st) => write!(
|
||||
src,
|
||||
&Handle::Endpoint(port_num, endp_num, st) => write!(
|
||||
cursor,
|
||||
"/port{}/endpoints/{}/{}",
|
||||
port_num,
|
||||
endp_num,
|
||||
@@ -1515,17 +1481,17 @@ impl SchemeMut for Xhci {
|
||||
}
|
||||
)
|
||||
.unwrap(),
|
||||
Handle::ConfigureEndpoints(port_num) => {
|
||||
write!(src, "/port{}/configure", port_num).unwrap()
|
||||
&Handle::ConfigureEndpoints(port_num) => {
|
||||
write!(cursor, "/port{}/configure", port_num).unwrap()
|
||||
}
|
||||
}
|
||||
let bytes_to_read = cmp::min(src.len(), buffer.len());
|
||||
buffer[..bytes_to_read].copy_from_slice(&src[..bytes_to_read]);
|
||||
Ok(bytes_to_read)
|
||||
let src_len = usize::try_from(cursor.seek(io::SeekFrom::End(0)).unwrap()).unwrap();
|
||||
Ok(src_len)
|
||||
}
|
||||
|
||||
fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
let guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
match &mut *guard {
|
||||
// Directories, or fixed files
|
||||
Handle::TopLevel(ref mut offset, ref buf)
|
||||
| Handle::Port(_, ref mut offset, ref buf)
|
||||
@@ -1557,7 +1523,8 @@ impl SchemeMut for Xhci {
|
||||
}
|
||||
|
||||
fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
let guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
match &mut *guard {
|
||||
Handle::TopLevel(ref mut offset, ref src_buf)
|
||||
| Handle::Port(_, ref mut offset, ref src_buf)
|
||||
| Handle::PortDesc(_, ref mut offset, ref src_buf)
|
||||
@@ -1609,7 +1576,8 @@ impl SchemeMut for Xhci {
|
||||
}
|
||||
}
|
||||
fn write(&mut self, fd: usize, buf: &[u8]) -> Result<usize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
let guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
match &mut *guard {
|
||||
&mut Handle::ConfigureEndpoints(port_num) => {
|
||||
self.configure_endpoints(port_num, buf)?;
|
||||
Ok(buf.len())
|
||||
@@ -1667,7 +1635,7 @@ impl Xhci {
|
||||
_ => return Err(Error::new(EIO)),
|
||||
})
|
||||
}
|
||||
pub fn on_req_reset_device(
|
||||
pub async fn on_req_reset_device(
|
||||
&mut self,
|
||||
port_num: usize,
|
||||
endp_num: u8,
|
||||
@@ -1678,8 +1646,8 @@ impl Xhci {
|
||||
}
|
||||
// Change the endpoint state from anything, but most likely HALTED (otherwise resetting
|
||||
// would be quite meaningless), to stopped.
|
||||
self.reset_endpoint(port_num, endp_num, false)?;
|
||||
self.restart_endpoint(port_num, endp_num)?;
|
||||
self.reset_endpoint(port_num, endp_num, false).await?;
|
||||
self.restart_endpoint(port_num, endp_num).await?;
|
||||
|
||||
if clear_feature {
|
||||
self.device_req_no_data(
|
||||
@@ -1695,37 +1663,46 @@ impl Xhci {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn restart_endpoint(&mut self, port_num: usize, endp_num: u8) -> Result<()> {
|
||||
pub async fn restart_endpoint(&self, port_num: usize, endp_num: u8) -> Result<()> {
|
||||
let port_state = self
|
||||
.port_states
|
||||
.get_mut(&port_num)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let endp_desc = port_state
|
||||
.dev_desc
|
||||
.config_descs
|
||||
.get(0)
|
||||
.ok_or(Error::new(EIO))?
|
||||
.interface_descs
|
||||
.get(0)
|
||||
.ok_or(Error::new(EIO))?
|
||||
.endpoints
|
||||
.get(endp_num as usize - 1)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let direction = if endp_num != 0 {
|
||||
let endp_desc = port_state
|
||||
.dev_desc
|
||||
.config_descs
|
||||
.get(0)
|
||||
.ok_or(Error::new(EIO))?
|
||||
.interface_descs
|
||||
.get(0)
|
||||
.ok_or(Error::new(EIO))?
|
||||
.endpoints
|
||||
.get(endp_num as usize - 1)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
endp_desc.direction()
|
||||
} else {
|
||||
EndpDirection::Bidirectional
|
||||
};
|
||||
let endpoint_state: &mut EndpointState = port_state
|
||||
|
||||
let endpoint_state = port_state
|
||||
.endpoint_states
|
||||
.get_mut(&endp_num)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let (has_streams, ring) = match &mut endpoint_state.transfer {
|
||||
&mut super::RingOrStreams::Ring(ref mut ring) => (false, ring),
|
||||
&mut super::RingOrStreams::Streams(ref mut arr) => {
|
||||
(true, arr.rings.get_mut(&1).ok_or(Error::new(EBADFD))?)
|
||||
}
|
||||
};
|
||||
let doorbell = Self::endp_doorbell(
|
||||
endp_num,
|
||||
endp_desc,
|
||||
if has_streams { stream_id } else { 0 },
|
||||
);
|
||||
|
||||
let (cmd, cycle) = ring.next();
|
||||
cmd.transfer_no_op(0, false, false, false, cycle);
|
||||
@@ -1733,14 +1710,12 @@ impl Xhci {
|
||||
let deque_ptr_and_cycle = ring.register();
|
||||
let slot = port_state.slot;
|
||||
|
||||
self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle)?;
|
||||
self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle).await?;
|
||||
|
||||
|
||||
|
||||
let stream_id = 1u16;
|
||||
self.dbs[slot as usize].write(Self::endp_doorbell(
|
||||
endp_num,
|
||||
self.endp_desc(port_num, endp_num)?,
|
||||
if has_streams { stream_id } else { 0 },
|
||||
));
|
||||
self.dbs.lock().unwrap()[slot as usize].write(doorbell);
|
||||
Ok(())
|
||||
}
|
||||
pub fn endp_direction(&self, port_num: usize, endp_num: u8) -> Result<EndpDirection> {
|
||||
@@ -1763,16 +1738,24 @@ impl Xhci {
|
||||
pub fn slot(&self, port_num: usize) -> Result<u8> {
|
||||
Ok(self.port_states.get(&port_num).ok_or(Error::new(EIO))?.slot)
|
||||
}
|
||||
pub fn set_tr_deque_ptr(
|
||||
&mut self,
|
||||
pub async fn set_tr_deque_ptr(
|
||||
&self,
|
||||
port_num: usize,
|
||||
endp_num: u8,
|
||||
deque_ptr_and_cycle: u64,
|
||||
) -> Result<()> {
|
||||
let slot = self.slot(port_num)?;
|
||||
let endp_num_xhc = Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?);
|
||||
let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?;
|
||||
let slot = port_state.slot;
|
||||
|
||||
self.execute_command("SET_TR_DEQUEUE_POINTER", |trb, cycle| {
|
||||
let (cfg_idx, if_idx) = match (port_state.cfg_idx, port_state.if_idx) {
|
||||
(Some(c), Some(i)) => (c, i),
|
||||
_ => return Err(Error::new(EIO)),
|
||||
};
|
||||
|
||||
let endp_desc = port_state.dev_desc.config_descs[usize::from(cfg_idx)].interface_descs[usize::from(if_idx)].endpoints.get(usize::from(endp_num)).ok_or(Error::new(EBADFD))?;
|
||||
let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc);
|
||||
|
||||
let (event_trb, command_trb) = self.execute_command(|trb, cycle| {
|
||||
trb.set_tr_deque_ptr(
|
||||
deque_ptr_and_cycle,
|
||||
cycle,
|
||||
@@ -1781,11 +1764,11 @@ impl Xhci {
|
||||
endp_num_xhc,
|
||||
slot,
|
||||
)
|
||||
})?;
|
||||
}).await;
|
||||
|
||||
Ok(())
|
||||
handle_event_trb("SET_TR_DEQUEUE_PTR", &event_trb, &command_trb)
|
||||
}
|
||||
pub fn on_write_endp_ctl(
|
||||
pub async fn on_write_endp_ctl(
|
||||
&mut self,
|
||||
port_num: usize,
|
||||
endp_num: u8,
|
||||
@@ -1808,9 +1791,7 @@ impl Xhci {
|
||||
}
|
||||
},
|
||||
XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state {
|
||||
EndpIfState::Init => {
|
||||
self.on_req_reset_device(port_num, endp_num, !no_clear_feature)?
|
||||
}
|
||||
EndpIfState::Init => self.on_req_reset_device(port_num, endp_num, !no_clear_feature).await?,
|
||||
other => {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
@@ -1874,7 +1855,11 @@ impl Xhci {
|
||||
endp_num: u8,
|
||||
buf: &[u8],
|
||||
) -> Result<usize> {
|
||||
let ep_if_state = &mut self.endpoint_state_mut(port_num, endp_num)?.driver_if_state;
|
||||
let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?;
|
||||
let endpoint_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let ep_if_state = &mut endpoint_state.driver_if_state;
|
||||
|
||||
match ep_if_state {
|
||||
&mut EndpIfState::WaitingForDataPipe {
|
||||
direction: XhciEndpCtlDirection::Out,
|
||||
@@ -1892,7 +1877,9 @@ impl Xhci {
|
||||
// invoking further data transfer calls if any single transfer returns fewer bytes
|
||||
// than requested.
|
||||
|
||||
let ep_if_state = &mut self.endpoint_state_mut(port_num, endp_num)?.driver_if_state;
|
||||
let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?;
|
||||
let endpoint_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?;
|
||||
let ep_if_state = &mut endpoint_state.driver_if_state;
|
||||
|
||||
if let &mut EndpIfState::WaitingForDataPipe {
|
||||
direction: XhciEndpCtlDirection::Out,
|
||||
@@ -2005,6 +1992,18 @@ impl Xhci {
|
||||
_ => return Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
pub fn event_handler_finished(&self) {
|
||||
// write 1 to EHB to clear it
|
||||
self.run.lock().unwrap().ints[0].erdp.writef(1 << 3, true);
|
||||
}
|
||||
}
|
||||
fn handle_event_trb(name: &str, event_trb: &Trb, command_trb: &Trb) -> Result<()> {
|
||||
if event_trb.completion_code() == TrbCompletionCode::Success as u8 {
|
||||
Ok(())
|
||||
} else {
|
||||
println!("{} command (TRB {:?}) failed with event trb {:?}", name, command_trb, event_trb);
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
use std::ops::{Add, Div, Rem};
|
||||
pub fn div_round_up<T>(a: T, b: T) -> T
|
||||
|
||||
+69
-2
@@ -5,6 +5,7 @@ use syscall::io::{Io, Mmio};
|
||||
use super::context::StreamContextType;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum TrbType {
|
||||
Reserved,
|
||||
/* Transfer */
|
||||
@@ -33,8 +34,8 @@ pub enum TrbType {
|
||||
ForceHeader,
|
||||
NoOpCmd,
|
||||
/* Reserved */
|
||||
Rsv24,
|
||||
Rsv25,
|
||||
GetExtendedProperty,
|
||||
SetExtendedProperty,
|
||||
Rsv26,
|
||||
Rsv27,
|
||||
Rsv28,
|
||||
@@ -54,6 +55,7 @@ pub enum TrbType {
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum TrbCompletionCode {
|
||||
Invalid,
|
||||
Success,
|
||||
@@ -157,6 +159,34 @@ impl Trb {
|
||||
pub fn completion_param(&self) -> u32 {
|
||||
self.status.read() & TRB_STATUS_COMPLETION_PARAM_MASK
|
||||
}
|
||||
fn has_completion_trb_pointer(&self) -> bool {
|
||||
if self.completion_code() == TrbCompletionCode::RingUnderrun as u8 || self.completion_code() == TrbCompletionCode::RingOverrun as u8 {
|
||||
false
|
||||
} else if self.completion_code() == TrbCompletionCode::VfEventRingFull as u8 {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
pub fn completion_trb_pointer(&self) -> Option<u64> {
|
||||
debug_assert_eq!(self.trb_type(), TrbType::CommandCompletion as u8);
|
||||
|
||||
if self.has_completion_trb_pointer() {
|
||||
Some(self.data.read())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn transfer_event_trb_pointer(&self) -> Option<u64> {
|
||||
debug_assert_eq!(self.trb_type(), TrbType::Transfer as u8);
|
||||
|
||||
if self.has_completion_trb_pointer() {
|
||||
Some(self.data.read())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_slot(&self) -> u8 {
|
||||
(self.control.read() >> 24) as u8
|
||||
}
|
||||
@@ -383,6 +413,43 @@ impl Trb {
|
||||
| ((TrbType::Normal as u32) << 10),
|
||||
)
|
||||
}
|
||||
pub fn is_command_trb(&self) -> bool {
|
||||
let valid_trb_types = [
|
||||
TrbType::NoOpCmd as u8,
|
||||
TrbType::EnableSlot as u8,
|
||||
TrbType::DisableSlot as u8,
|
||||
TrbType::AddressDevice as u8,
|
||||
TrbType::ConfigureEndpoint as u8,
|
||||
TrbType::EvaluateContext as u8,
|
||||
TrbType::ResetEndpoint as u8,
|
||||
TrbType::StopEndpoint as u8,
|
||||
TrbType::SetTrDequeuePointer as u8,
|
||||
TrbType::ResetDevice as u8,
|
||||
TrbType::ForceEvent as u8,
|
||||
TrbType::NegotiateBandwidth as u8,
|
||||
TrbType::SetLatencyToleranceValue as u8,
|
||||
TrbType::GetPortBandwidth as u8,
|
||||
TrbType::ForceHeader as u8,
|
||||
TrbType::GetExtendedProperty as u8,
|
||||
TrbType::SetExtendedProperty as u8,
|
||||
];
|
||||
valid_trb_types.contains(&self.trb_type())
|
||||
}
|
||||
pub fn is_transfer_trb(&self) -> bool {
|
||||
// XXX: Unfortunately, the only way to use match statements with integer constants, is to
|
||||
// precast them into valid enum values, which either requires a derive macro such as
|
||||
// num_traits's #[derive(FromPrimitive)], or manually writing the reverse match statement
|
||||
// first.
|
||||
let valid_trb_types = [
|
||||
TrbType::Normal as u8,
|
||||
TrbType::SetupStage as u8,
|
||||
TrbType::DataStage as u8,
|
||||
TrbType::StatusStage as u8,
|
||||
TrbType::Isoch as u8,
|
||||
TrbType::NoOp as u8,
|
||||
];
|
||||
valid_trb_types.contains(&self.trb_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Trb {
|
||||
|
||||
Reference in New Issue
Block a user