| // SPDX-License-Identifier: GPL-2.0 |
| |
| //! Proc macro crate implementing the [`module!`] magic. |
| //! |
| //! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) |
| |
| #![deny(clippy::complexity)] |
| #![deny(clippy::correctness)] |
| #![deny(clippy::perf)] |
| #![deny(clippy::style)] |
| |
| use proc_macro::{token_stream, Delimiter, Group, TokenStream, TokenTree}; |
| |
| fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> { |
| if let Some(TokenTree::Ident(ident)) = it.next() { |
| Some(ident.to_string()) |
| } else { |
| None |
| } |
| } |
| |
| fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> { |
| if let Some(TokenTree::Literal(literal)) = it.next() { |
| Some(literal.to_string()) |
| } else { |
| None |
| } |
| } |
| |
| fn try_byte_string(it: &mut token_stream::IntoIter) -> Option<String> { |
| try_literal(it).and_then(|byte_string| { |
| if byte_string.starts_with("b\"") && byte_string.ends_with('\"') { |
| Some(byte_string[2..byte_string.len() - 1].to_string()) |
| } else { |
| None |
| } |
| }) |
| } |
| |
| fn expect_ident(it: &mut token_stream::IntoIter) -> String { |
| try_ident(it).expect("Expected Ident") |
| } |
| |
| fn expect_punct(it: &mut token_stream::IntoIter) -> char { |
| if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") { |
| punct.as_char() |
| } else { |
| panic!("Expected Punct"); |
| } |
| } |
| |
| fn expect_literal(it: &mut token_stream::IntoIter) -> String { |
| try_literal(it).expect("Expected Literal") |
| } |
| |
| fn expect_group(it: &mut token_stream::IntoIter) -> Group { |
| if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") { |
| group |
| } else { |
| panic!("Expected Group"); |
| } |
| } |
| |
| fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { |
| try_byte_string(it).expect("Expected byte string") |
| } |
| |
| #[derive(Clone, PartialEq)] |
| enum ParamType { |
| Ident(String), |
| Array { vals: String, max_length: usize }, |
| } |
| |
| fn expect_array_fields(it: &mut token_stream::IntoIter) -> ParamType { |
| assert_eq!(expect_punct(it), '<'); |
| let vals = expect_ident(it); |
| assert_eq!(expect_punct(it), ','); |
| let max_length_str = expect_literal(it); |
| let max_length = max_length_str |
| .parse::<usize>() |
| .expect("Expected usize length"); |
| assert_eq!(expect_punct(it), '>'); |
| ParamType::Array { vals, max_length } |
| } |
| |
| fn expect_type(it: &mut token_stream::IntoIter) -> ParamType { |
| if let TokenTree::Ident(ident) = it |
| .next() |
| .expect("Reached end of token stream for param type") |
| { |
| match ident.to_string().as_ref() { |
| "ArrayParam" => expect_array_fields(it), |
| _ => ParamType::Ident(ident.to_string()), |
| } |
| } else { |
| panic!("Expected Param Type") |
| } |
| } |
| |
| fn expect_end(it: &mut token_stream::IntoIter) { |
| if it.next().is_some() { |
| panic!("Expected end"); |
| } |
| } |
| |
| fn get_ident(it: &mut token_stream::IntoIter, expected_name: &str) -> String { |
| assert_eq!(expect_ident(it), expected_name); |
| assert_eq!(expect_punct(it), ':'); |
| let ident = expect_ident(it); |
| assert_eq!(expect_punct(it), ','); |
| ident |
| } |
| |
| fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { |
| assert_eq!(expect_ident(it), expected_name); |
| assert_eq!(expect_punct(it), ':'); |
| let literal = expect_literal(it); |
| assert_eq!(expect_punct(it), ','); |
| literal |
| } |
| |
| fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> Group { |
| assert_eq!(expect_ident(it), expected_name); |
| assert_eq!(expect_punct(it), ':'); |
| let group = expect_group(it); |
| assert_eq!(expect_punct(it), ','); |
| group |
| } |
| |
| fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String { |
| assert_eq!(expect_ident(it), expected_name); |
| assert_eq!(expect_punct(it), ':'); |
| let byte_string = expect_byte_string(it); |
| assert_eq!(expect_punct(it), ','); |
| byte_string |
| } |
| |
| fn __build_modinfo_string_base( |
| module: &str, |
| field: &str, |
| content: &str, |
| variable: &str, |
| builtin: bool, |
| ) -> String { |
| let string = if builtin { |
| // Built-in modules prefix their modinfo strings by `module.`. |
| format!( |
| "{module}.{field}={content}", |
| module = module, |
| field = field, |
| content = content |
| ) |
| } else { |
| // Loadable modules' modinfo strings go as-is. |
| format!("{field}={content}", field = field, content = content) |
| }; |
| |
| format!( |
| " |
| {cfg} |
| #[link_section = \".modinfo\"] |
| #[used] |
| pub static {variable}: [u8; {length}] = *b\"{string}\\0\"; |
| ", |
| cfg = if builtin { |
| "#[cfg(not(MODULE))]" |
| } else { |
| "#[cfg(MODULE)]" |
| }, |
| variable = variable, |
| length = string.len() + 1, |
| string = string, |
| ) |
| } |
| |
| fn __build_modinfo_string_variable(module: &str, field: &str) -> String { |
| format!("__{module}_{field}", module = module, field = field) |
| } |
| |
| fn build_modinfo_string_only_builtin(module: &str, field: &str, content: &str) -> String { |
| __build_modinfo_string_base( |
| module, |
| field, |
| content, |
| &__build_modinfo_string_variable(module, field), |
| true, |
| ) |
| } |
| |
| fn build_modinfo_string_only_loadable(module: &str, field: &str, content: &str) -> String { |
| __build_modinfo_string_base( |
| module, |
| field, |
| content, |
| &__build_modinfo_string_variable(module, field), |
| false, |
| ) |
| } |
| |
| fn build_modinfo_string(module: &str, field: &str, content: &str) -> String { |
| build_modinfo_string_only_builtin(module, field, content) |
| + &build_modinfo_string_only_loadable(module, field, content) |
| } |
| |
| fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &str) -> String { |
| let variable = format!( |
| "__{module}_{field}_{param}", |
| module = module, |
| field = field, |
| param = param |
| ); |
| let content = format!("{param}:{content}", param = param, content = content); |
| __build_modinfo_string_base(module, field, &content, &variable, true) |
| + &__build_modinfo_string_base(module, field, &content, &variable, false) |
| } |
| |
| fn permissions_are_readonly(perms: &str) -> bool { |
| let (radix, digits) = if let Some(n) = perms.strip_prefix("0x") { |
| (16, n) |
| } else if let Some(n) = perms.strip_prefix("0o") { |
| (8, n) |
| } else if let Some(n) = perms.strip_prefix("0b") { |
| (2, n) |
| } else { |
| (10, perms) |
| }; |
| match u32::from_str_radix(digits, radix) { |
| Ok(perms) => perms & 0o222 == 0, |
| Err(_) => false, |
| } |
| } |
| |
| fn param_ops_path(param_type: &str) -> &'static str { |
| match param_type { |
| "bool" => "kernel::module_param::PARAM_OPS_BOOL", |
| "i8" => "kernel::module_param::PARAM_OPS_I8", |
| "u8" => "kernel::module_param::PARAM_OPS_U8", |
| "i16" => "kernel::module_param::PARAM_OPS_I16", |
| "u16" => "kernel::module_param::PARAM_OPS_U16", |
| "i32" => "kernel::module_param::PARAM_OPS_I32", |
| "u32" => "kernel::module_param::PARAM_OPS_U32", |
| "i64" => "kernel::module_param::PARAM_OPS_I64", |
| "u64" => "kernel::module_param::PARAM_OPS_U64", |
| "isize" => "kernel::module_param::PARAM_OPS_ISIZE", |
| "usize" => "kernel::module_param::PARAM_OPS_USIZE", |
| "str" => "kernel::module_param::PARAM_OPS_STR", |
| t => panic!("Unrecognized type {}", t), |
| } |
| } |
| |
| fn try_simple_param_val( |
| param_type: &str, |
| ) -> Box<dyn Fn(&mut token_stream::IntoIter) -> Option<String>> { |
| match param_type { |
| "bool" => Box::new(|param_it| try_ident(param_it)), |
| "str" => Box::new(|param_it| { |
| try_byte_string(param_it) |
| .map(|s| format!("kernel::module_param::StringParam::Ref(b\"{}\")", s)) |
| }), |
| _ => Box::new(|param_it| try_literal(param_it)), |
| } |
| } |
| |
| fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> String { |
| let try_param_val = match param_type { |
| ParamType::Ident(ref param_type) |
| | ParamType::Array { |
| vals: ref param_type, |
| max_length: _, |
| } => try_simple_param_val(param_type), |
| }; |
| assert_eq!(expect_ident(param_it), "default"); |
| assert_eq!(expect_punct(param_it), ':'); |
| let default = match param_type { |
| ParamType::Ident(_) => try_param_val(param_it).expect("Expected default param value"), |
| ParamType::Array { |
| vals: _, |
| max_length: _, |
| } => { |
| let group = expect_group(param_it); |
| assert_eq!(group.delimiter(), Delimiter::Bracket); |
| let mut default_vals = Vec::new(); |
| let mut it = group.stream().into_iter(); |
| |
| while let Some(default_val) = try_param_val(&mut it) { |
| default_vals.push(default_val); |
| match it.next() { |
| Some(TokenTree::Punct(punct)) => assert_eq!(punct.as_char(), ','), |
| None => break, |
| _ => panic!("Expected ',' or end of array default values"), |
| } |
| } |
| |
| let mut default_array = "kernel::module_param::ArrayParam::create(&[".to_string(); |
| default_array.push_str( |
| &default_vals |
| .iter() |
| .map(|val| val.to_string()) |
| .collect::<Vec<String>>() |
| .join(","), |
| ); |
| default_array.push_str("])"); |
| default_array |
| } |
| }; |
| assert_eq!(expect_punct(param_it), ','); |
| default |
| } |
| |
| fn generated_array_ops_name(vals: &str, max_length: usize) -> String { |
| format!( |
| "__generated_array_ops_{vals}_{max_length}", |
| vals = vals, |
| max_length = max_length |
| ) |
| } |
| |
| /// Declares a kernel module. |
| /// |
| /// The `type` argument should be a type which implements the [`KernelModule`] |
| /// trait. Also accepts various forms of kernel metadata. |
| /// |
| /// [`KernelModule`]: ../kernel/trait.KernelModule.html |
| /// |
| /// # Examples |
| /// |
| /// ```rust,no_run |
| /// use kernel::prelude::*; |
| /// |
| /// module!{ |
| /// type: MyKernelModule, |
| /// name: b"my_kernel_module", |
| /// author: b"Rust for Linux Contributors", |
| /// description: b"My very own kernel module!", |
| /// license: b"GPL v2", |
| /// params: { |
| /// my_i32: i32 { |
| /// default: 42, |
| /// permissions: 0o000, |
| /// description: b"Example of i32", |
| /// }, |
| /// writeable_i32: i32 { |
| /// default: 42, |
| /// permissions: 0o644, |
| /// description: b"Example of i32", |
| /// }, |
| /// }, |
| /// } |
| /// |
| /// struct MyKernelModule; |
| /// |
| /// impl KernelModule for MyKernelModule { |
| /// fn init() -> KernelResult<Self> { |
| /// // If the parameter is writeable, then the kparam lock must be |
| /// // taken to read the parameter: |
| /// { |
| /// let lock = THIS_MODULE.kernel_param_lock(); |
| /// pr_info!("i32 param is: {}\n", writeable_i32.read(&lock)); |
| /// } |
| /// // If the parameter is read only, it can be read without locking |
| /// // the kernel parameters: |
| /// pr_info!("i32 param is: {}\n", my_i32.read()); |
| /// Ok(MyKernelModule) |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// # Supported parameter types |
| /// |
| /// - `bool`: Corresponds to C `bool` param type. |
| /// - `i8`: No equivalent C param type. |
| /// - `u8`: Corresponds to C `char` param type. |
| /// - `i16`: Corresponds to C `short` param type. |
| /// - `u16`: Corresponds to C `ushort` param type. |
| /// - `i32`: Corresponds to C `int` param type. |
| /// - `u32`: Corresponds to C `uint` param type. |
| /// - `i64`: No equivalent C param type. |
| /// - `u64`: Corresponds to C `ullong` param type. |
| /// - `isize`: No equivalent C param type. |
| /// - `usize`: No equivalent C param type. |
| /// - `str`: Corresponds to C `charp` param type. Reading returns a byte slice. |
| /// - `ArrayParam<T,N>`: Corresponds to C parameters created using `module_param_array`. An array |
| /// of `T`'s of length at **most** `N`. |
| /// |
| /// `invbool` is unsupported: it was only ever used in a few modules. |
| /// Consider using a `bool` and inverting the logic instead. |
| #[proc_macro] |
| pub fn module(ts: TokenStream) -> TokenStream { |
| let mut it = ts.into_iter(); |
| |
| let type_ = get_ident(&mut it, "type"); |
| let name = get_byte_string(&mut it, "name"); |
| let author = get_byte_string(&mut it, "author"); |
| let description = get_byte_string(&mut it, "description"); |
| let license = get_byte_string(&mut it, "license"); |
| let params = get_group(&mut it, "params"); |
| |
| expect_end(&mut it); |
| |
| assert_eq!(params.delimiter(), Delimiter::Brace); |
| |
| let mut it = params.stream().into_iter(); |
| |
| let mut params_modinfo = String::new(); |
| |
| let mut array_types_to_generate = Vec::new(); |
| |
| loop { |
| let param_name = match it.next() { |
| Some(TokenTree::Ident(ident)) => ident.to_string(), |
| Some(_) => panic!("Expected Ident or end"), |
| None => break, |
| }; |
| |
| assert_eq!(expect_punct(&mut it), ':'); |
| let param_type = expect_type(&mut it); |
| let group = expect_group(&mut it); |
| assert_eq!(expect_punct(&mut it), ','); |
| |
| assert_eq!(group.delimiter(), Delimiter::Brace); |
| |
| let mut param_it = group.stream().into_iter(); |
| let param_default = get_default(¶m_type, &mut param_it); |
| let param_permissions = get_literal(&mut param_it, "permissions"); |
| let param_description = get_byte_string(&mut param_it, "description"); |
| expect_end(&mut param_it); |
| |
| // TODO: more primitive types |
| // TODO: other kinds: unsafes, etc. |
| let (param_kernel_type, ops): (String, _) = match param_type { |
| ParamType::Ident(ref param_type) => ( |
| param_type.to_string(), |
| param_ops_path(¶m_type).to_string(), |
| ), |
| ParamType::Array { |
| ref vals, |
| max_length, |
| } => { |
| array_types_to_generate.push((vals.clone(), max_length)); |
| ( |
| format!("__rust_array_param_{}_{}", vals, max_length), |
| generated_array_ops_name(vals, max_length), |
| ) |
| } |
| }; |
| |
| params_modinfo.push_str(&build_modinfo_string_param( |
| &name, |
| "parmtype", |
| ¶m_name, |
| ¶m_kernel_type, |
| )); |
| params_modinfo.push_str(&build_modinfo_string_param( |
| &name, |
| "parm", |
| ¶m_name, |
| ¶m_description, |
| )); |
| let param_type_internal = match param_type { |
| ParamType::Ident(ref param_type) => match param_type.as_ref() { |
| "str" => "kernel::module_param::StringParam".to_string(), |
| other => other.to_string(), |
| }, |
| ParamType::Array { |
| ref vals, |
| max_length, |
| } => format!( |
| "kernel::module_param::ArrayParam<{vals}, {max_length}>", |
| vals = vals, |
| max_length = max_length |
| ), |
| }; |
| let read_func = if permissions_are_readonly(¶m_permissions) { |
| format!( |
| " |
| fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ |
| // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. |
| unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} |
| }} |
| ", |
| name = name, |
| param_name = param_name, |
| param_type_internal = param_type_internal, |
| ) |
| } else { |
| format!( |
| " |
| fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ |
| // SAFETY: Parameters are locked by `KParamGuard`. |
| unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} |
| }} |
| ", |
| name = name, |
| param_name = param_name, |
| param_type_internal = param_type_internal, |
| ) |
| }; |
| let kparam = format!( |
| " |
| kernel::bindings::kernel_param__bindgen_ty_1 {{ |
| arg: unsafe {{ &__{name}_{param_name}_value }} as *const _ as *mut kernel::c_types::c_void, |
| }}, |
| ", |
| name = name, |
| param_name = param_name, |
| ); |
| params_modinfo.push_str( |
| &format!( |
| " |
| static mut __{name}_{param_name}_value: {param_type_internal} = {param_default}; |
| |
| struct __{name}_{param_name}; |
| |
| impl __{name}_{param_name} {{ {read_func} }} |
| |
| const {param_name}: __{name}_{param_name} = __{name}_{param_name}; |
| |
| // Note: the C macro that generates the static structs for the `__param` section |
| // asks for them to be `aligned(sizeof(void *))`. However, that was put in place |
| // in 2003 in commit 38d5b085d2 (\"[PATCH] Fix over-alignment problem on x86-64\") |
| // to undo GCC over-alignment of static structs of >32 bytes. It seems that is |
| // not the case anymore, so we simplify to a transparent representation here |
| // in the expectation that it is not needed anymore. |
| // TODO: revisit this to confirm the above comment and remove it if it happened |
| #[repr(transparent)] |
| struct __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param); |
| |
| unsafe impl Sync for __{name}_{param_name}_RacyKernelParam {{ |
| }} |
| |
| #[cfg(not(MODULE))] |
| const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{name}.{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; |
| |
| #[cfg(MODULE)] |
| const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; |
| |
| #[link_section = \"__param\"] |
| #[used] |
| static __{name}_{param_name}_struct: __{name}_{param_name}_RacyKernelParam = __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param {{ |
| name: __{name}_{param_name}_name, |
| // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. |
| #[cfg(MODULE)] |
| mod_: unsafe {{ &kernel::bindings::__this_module as *const _ as *mut _ }}, |
| #[cfg(not(MODULE))] |
| mod_: core::ptr::null_mut(), |
| ops: unsafe {{ &{ops} }} as *const kernel::bindings::kernel_param_ops, |
| perm: {permissions}, |
| level: -1, |
| flags: 0, |
| __bindgen_anon_1: {kparam} |
| }}); |
| ", |
| name = name, |
| param_type_internal = param_type_internal, |
| read_func = read_func, |
| param_default = param_default, |
| param_name = param_name, |
| ops = ops, |
| permissions = param_permissions, |
| kparam = kparam, |
| ) |
| ); |
| } |
| |
| let mut generated_array_types = String::new(); |
| |
| for (vals, max_length) in array_types_to_generate { |
| let ops_name = generated_array_ops_name(&vals, max_length); |
| generated_array_types.push_str(&format!( |
| " |
| kernel::make_param_ops!( |
| {ops_name}, |
| kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}> |
| ); |
| ", |
| ops_name = ops_name, |
| vals = vals, |
| max_length = max_length, |
| )); |
| } |
| |
| let file = |
| std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable"); |
| |
| format!( |
| " |
| /// The module name. |
| /// |
| /// Used by the printing macros, e.g. [`info!`]. |
| const __LOG_PREFIX: &[u8] = b\"{name}\\0\"; |
| |
| static mut __MOD: Option<{type_}> = None; |
| |
| // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. |
| #[cfg(MODULE)] |
| static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(&kernel::bindings::__this_module as *const _ as *mut _) }}; |
| #[cfg(not(MODULE))] |
| static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(core::ptr::null_mut()) }}; |
| |
| // Loadable modules need to export the `{{init,cleanup}}_module` identifiers |
| #[cfg(MODULE)] |
| #[no_mangle] |
| pub extern \"C\" fn init_module() -> kernel::c_types::c_int {{ |
| __init() |
| }} |
| |
| #[cfg(MODULE)] |
| #[no_mangle] |
| pub extern \"C\" fn cleanup_module() {{ |
| __exit() |
| }} |
| |
| // Built-in modules are initialized through an initcall pointer |
| // and the identifiers need to be unique |
| #[cfg(not(MODULE))] |
| #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] |
| #[link_section = \"{initcall_section}\"] |
| #[used] |
| pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init; |
| |
| #[cfg(not(MODULE))] |
| #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] |
| global_asm!( |
| r#\".section \"{initcall_section}\", \"a\" |
| __{name}_initcall: |
| .long __{name}_init - . |
| .previous |
| \"# |
| ); |
| |
| #[cfg(not(MODULE))] |
| #[no_mangle] |
| pub extern \"C\" fn __{name}_init() -> kernel::c_types::c_int {{ |
| __init() |
| }} |
| |
| #[cfg(not(MODULE))] |
| #[no_mangle] |
| pub extern \"C\" fn __{name}_exit() {{ |
| __exit() |
| }} |
| |
| fn __init() -> kernel::c_types::c_int {{ |
| match <{type_} as kernel::KernelModule>::init() {{ |
| Ok(m) => {{ |
| unsafe {{ |
| __MOD = Some(m); |
| }} |
| return 0; |
| }} |
| Err(e) => {{ |
| return e.to_kernel_errno(); |
| }} |
| }} |
| }} |
| |
| fn __exit() {{ |
| unsafe {{ |
| // Invokes `drop()` on `__MOD`, which should be used for cleanup. |
| __MOD = None; |
| }} |
| }} |
| |
| {author} |
| {description} |
| {license} |
| |
| // Built-in modules also export the `file` modinfo string |
| {file} |
| |
| {params_modinfo} |
| |
| {generated_array_types} |
| ", |
| type_ = type_, |
| name = name, |
| author = &build_modinfo_string(&name, "author", &author), |
| description = &build_modinfo_string(&name, "description", &description), |
| license = &build_modinfo_string(&name, "license", &license), |
| file = &build_modinfo_string_only_builtin(&name, "file", &file), |
| params_modinfo = params_modinfo, |
| generated_array_types = generated_array_types, |
| initcall_section = ".initcall6.init" |
| ).parse().expect("Error parsing formatted string into token stream.") |
| } |
| |
| /// Declares a kernel module that exposes a single misc device. |
| /// |
| /// The `type` argument should be a type which implements the [`FileOpener`] trait. Also accepts |
| /// various forms of kernel metadata. |
| /// |
| /// [`FileOpener`]: ../kernel/file_operations/trait.FileOpener.html |
| /// |
| /// # Examples |
| /// |
| /// ```rust,no_run |
| /// use kernel::prelude::*; |
| /// |
| /// module_misc_device! { |
| /// type: MyFile, |
| /// name: b"my_miscdev_kernel_module", |
| /// author: b"Rust for Linux Contributors", |
| /// description: b"My very own misc device kernel module!", |
| /// license: b"GPL v2", |
| /// } |
| /// |
| /// #[derive(Default)] |
| /// struct MyFile; |
| /// |
| /// impl kernel::file_operations::FileOperations for MyFile { |
| /// kernel::declare_file_operations!(); |
| /// } |
| /// ``` |
| #[proc_macro] |
| pub fn module_misc_device(ts: TokenStream) -> TokenStream { |
| let mut it = ts.into_iter(); |
| |
| let type_ = get_ident(&mut it, "type"); |
| let name = get_byte_string(&mut it, "name"); |
| let author = get_byte_string(&mut it, "author"); |
| let description = get_byte_string(&mut it, "description"); |
| let license = get_byte_string(&mut it, "license"); |
| expect_end(&mut it); |
| |
| let module = format!("__internal_ModuleFor{}", type_); |
| |
| format!( |
| " |
| #[doc(hidden)] |
| struct {module} {{ |
| _dev: core::pin::Pin<alloc::boxed::Box<kernel::miscdev::Registration>>, |
| }} |
| |
| impl kernel::KernelModule for {module} {{ |
| fn init() -> kernel::KernelResult<Self> {{ |
| Ok(Self {{ |
| _dev: kernel::miscdev::Registration::new_pinned::<{type_}>( |
| kernel::cstr!(\"{name}\"), |
| None, |
| (), |
| )?, |
| }}) |
| }} |
| }} |
| |
| kernel::prelude::module! {{ |
| type: {module}, |
| name: b\"{name}\", |
| author: b\"{author}\", |
| description: b\"{description}\", |
| license: b\"{license}\", |
| params: {{}}, |
| }} |
| ", |
| module = module, |
| type_ = type_, |
| name = name, |
| author = author, |
| description = description, |
| license = license |
| ) |
| .parse() |
| .expect("Error parsing formatted string into token stream.") |
| } |