No clue what-all's sitting here.
- Started a bunch of crates. - Incomplete migration, or rewrites of most. - Some wild guessing with scsi. Probably better off coming up with my own solution if possible? Don't remember. Have to look into how the driver works again.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "dvd"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
derive_more.workspace = true
|
||||
libdvdcss.workspace = true
|
||||
media.workspace = true
|
||||
@@ -0,0 +1,31 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use media::Media;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub type DVD = libdvdcss::DVD;
|
||||
|
||||
// Probably wipe and restart this.
|
||||
// Not thought out yet.
|
||||
|
||||
impl TryFrom<PathBuf> for DVD {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
|
||||
libdvdcss::DVD::new(value).ok_or(Error::LibDvdCssOpenError)
|
||||
}
|
||||
}
|
||||
|
||||
impl Media for DVD {}
|
||||
|
||||
mod error {
|
||||
use derive_more::{Display, From};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, From, Display)]
|
||||
pub enum Error {
|
||||
LibDvdCssOpenError,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "mapping"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,14 @@
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "media"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,20 @@
|
||||
use std::io::Read;
|
||||
|
||||
pub trait Media {
|
||||
fn type_(&self) -> MediaType;
|
||||
|
||||
fn encryption(&self) -> Vec<Box<dyn Encryption>>;
|
||||
|
||||
fn has_bus_encryption(&self) -> bool;
|
||||
|
||||
fn read(&self) -> Box<dyn Read>;
|
||||
}
|
||||
|
||||
pub enum MediaType {
|
||||
BluRay { writable: bool },
|
||||
DVD { writable: bool },
|
||||
}
|
||||
|
||||
pub trait Encryption {
|
||||
fn is_bus_encryption(&self) -> bool;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "plugins"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
semver.workspace = true
|
||||
@@ -0,0 +1,180 @@
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
pub struct PluginCompatBuilder {
|
||||
name: String,
|
||||
plugin_version: Version,
|
||||
core_version: Option<VersionReq>,
|
||||
is_preprocessor: bool,
|
||||
|
||||
dependencies: Vec<DependencyCompat>,
|
||||
incompatibilities: Vec<IncompatibilityCompat>,
|
||||
}
|
||||
|
||||
impl PluginCompatBuilder {
|
||||
// Fails if the provided name is empty.
|
||||
pub fn new(name: String, version: Version) -> Option<Self> {
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
name,
|
||||
plugin_version: version,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn core_version(mut self, version: VersionReq) -> Self {
|
||||
self.core_version = Some(version);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn preprocessor(mut self, is_preprocessor: bool) -> Self {
|
||||
self.is_preprocessor = is_preprocessor;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dependency(mut self, dep: DependencyCompat) -> Self {
|
||||
self.dependencies.push(dep);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn incompatible(mut self, dep: IncompatibilityCompat) -> Self {
|
||||
self.incompatibilities.push(dep);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn finalize(self) -> PluginCompat {
|
||||
PluginCompat {
|
||||
name: self.name,
|
||||
plugin_version: self.plugin_version,
|
||||
core_version: self.core_version,
|
||||
is_preprocessor: self.is_preprocessor,
|
||||
dependencies: self.dependencies,
|
||||
incompatibilities: self.incompatibilities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PluginCompatBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::from("AnonymousPlugin"),
|
||||
plugin_version: Version::new(0, 1, 0),
|
||||
core_version: None,
|
||||
is_preprocessor: false,
|
||||
dependencies: vec![],
|
||||
incompatibilities: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginCompat {
|
||||
name: String,
|
||||
plugin_version: Version,
|
||||
core_version: Option<VersionReq>,
|
||||
is_preprocessor: bool,
|
||||
|
||||
dependencies: Vec<DependencyCompat>,
|
||||
incompatibilities: Vec<IncompatibilityCompat>,
|
||||
}
|
||||
|
||||
// Most things shouldn't ever be modified by the caller, so passing
|
||||
// references to reduce memory consumption should be preferable.
|
||||
//
|
||||
// Worst case, maybe a few common calls on Version/VersionReq require
|
||||
// mutability, in which case maybe they won't be by reference.
|
||||
impl PluginCompat {
|
||||
pub fn new_dep_or_incompatible(name: String, version: Version) -> Self {
|
||||
let mut def = Self::default();
|
||||
def.name = name;
|
||||
def.plugin_version = version;
|
||||
def
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
pub fn plugin_version(&self) -> &Version {
|
||||
&self.plugin_version
|
||||
}
|
||||
|
||||
pub fn core_version(&self) -> Option<&VersionReq> {
|
||||
self.core_version.as_ref()
|
||||
}
|
||||
|
||||
pub fn is_preprocessor(&self) -> bool {
|
||||
self.is_preprocessor
|
||||
}
|
||||
|
||||
pub fn is_postprocessor(&self) -> bool {
|
||||
!self.is_preprocessor()
|
||||
}
|
||||
|
||||
pub fn dependencies(&self) -> &[DependencyCompat] {
|
||||
self.dependencies.as_slice()
|
||||
}
|
||||
|
||||
pub fn incompatibilities(&self) -> &[IncompatibilityCompat] {
|
||||
self.incompatibilities.as_slice()
|
||||
}
|
||||
|
||||
/// Returns if there is *any* incompatibility between `self` and `plugin`.
|
||||
pub fn incompatible_with<C: AsRef<PluginCompat>>(&self, plugin: C) -> bool {
|
||||
return self.contains_incompatibility_with(&plugin)
|
||||
&& plugin.as_ref().contains_incompatibility_with(self);
|
||||
}
|
||||
|
||||
/// Returns if `self` has any incompatibility with `plugin`
|
||||
fn contains_incompatibility_with<C: AsRef<PluginCompat>>(&self, plugin: C) -> bool {
|
||||
if !self.incompatibilities.is_empty()
|
||||
&& self.incompatibilities.iter().any(|inc| {
|
||||
inc.name() == plugin.as_ref().name()
|
||||
&& !inc.version.matches(&plugin.as_ref().plugin_version)
|
||||
})
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PluginCompat {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::from("AnonymousPlugin"),
|
||||
plugin_version: Version::new(0, 1, 0),
|
||||
core_version: None,
|
||||
is_preprocessor: false,
|
||||
dependencies: vec![],
|
||||
incompatibilities: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Self> for PluginCompat {
|
||||
fn as_ref(&self) -> &Self {
|
||||
&self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DependencyCompat {
|
||||
name: String,
|
||||
version: VersionReq,
|
||||
}
|
||||
|
||||
impl DependencyCompat {
|
||||
pub fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
pub fn plugin_version(&self) -> &VersionReq {
|
||||
&self.version
|
||||
}
|
||||
}
|
||||
|
||||
pub type IncompatibilityCompat = DependencyCompat;
|
||||
@@ -0,0 +1,14 @@
|
||||
use semver::Version;
|
||||
|
||||
use crate::compat::PluginCompat;
|
||||
|
||||
pub mod compat;
|
||||
|
||||
// Surely there's a way to automate this against the cargo manifest?
|
||||
pub const CORE_VERSION: Version = Version::new(0, 1, 0);
|
||||
|
||||
pub trait Plugin {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
fn compatibility(&self) -> PluginCompat;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "scsi"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
derive_more.workspace = true
|
||||
nix = { version = "0.31.2", features = ["ioctl"] } # Re-exports a compatible libc version.
|
||||
packed_struct = "0.10"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
@@ -0,0 +1,24 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Path from which to search for shared libraries.
|
||||
// Is there no better (automated), platform-specific way?
|
||||
println!("cargo:rustc-link-search=/usr/lib");
|
||||
|
||||
// Locate and link the shared library.
|
||||
//println!("cargo:rustc-link-lib=sg3_utils");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("src/wrapper.h")
|
||||
// Invalidate the build if a header has changed.
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Failed to generate bindings.");
|
||||
|
||||
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use derive_more::{Display, From};
|
||||
use nix;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, From, Display)]
|
||||
pub enum Error {
|
||||
Nix(nix::Error),
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
mod error;
|
||||
mod sg;
|
||||
mod spc;
|
||||
|
||||
pub use error::Error;
|
||||
|
||||
use packed_struct::{
|
||||
derive::PackedStruct,
|
||||
types::{Integer, ReservedZero, bits::Bits},
|
||||
};
|
||||
|
||||
/*
|
||||
#[derive(Debug)]
|
||||
pub struct Command6 {
|
||||
opcode: u8,
|
||||
payload: [u8; 4],
|
||||
control: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Command10 {
|
||||
opcode: u8,
|
||||
payload: [u8; 8],
|
||||
control: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Command12 {
|
||||
opcode: u8,
|
||||
payload: [u8; 10],
|
||||
control: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Command16 {
|
||||
opcode: u8,
|
||||
payload: [u8; 14],
|
||||
control: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CommandVariable {
|
||||
opcode: u8,
|
||||
control: u8,
|
||||
payload: [u8; 4],
|
||||
additional_len: u8,
|
||||
service_action: [u8; 2],
|
||||
additional_payload: Vec<u8>,
|
||||
}
|
||||
|
||||
|
||||
// There's also XCDBs... but I'm not about to mess with that.
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SenseData {}
|
||||
*/
|
||||
|
||||
#[derive(Debug, Default, PackedStruct)]
|
||||
#[packed_struct(bit_numbering = "msb0")]
|
||||
pub struct Control {
|
||||
#[packed_field(bits = "0..2")]
|
||||
vendor_specific: Integer<u8, Bits<2>>,
|
||||
#[packed_field(bits = "2..5")]
|
||||
_reserved: ReservedZero<Bits<3>>,
|
||||
#[packed_field(bits = "5..6")]
|
||||
naca: bool,
|
||||
#[packed_field(bits = "6")]
|
||||
_obsolete_1: bool, // SCSI uses LSB0 bit ordering, so name accordingly.
|
||||
#[packed_field(bits = "7")]
|
||||
_obsolete_0: bool, // SCSI uses LSB0 bit ordering, so name accordingly.
|
||||
}
|
||||
|
||||
pub trait Command {}
|
||||
@@ -0,0 +1,85 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::{fs::File, io::Write, ops::Add};
|
||||
|
||||
use packed_struct::{
|
||||
PackedStruct,
|
||||
derive::PackedStruct,
|
||||
types::{Integer, ReservedZero, bits::Bits},
|
||||
};
|
||||
|
||||
use crate::{Command, spc::InquiryCommand};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
||||
#[derive(PackedStruct)]
|
||||
#[packed_struct(endian = "msb")]
|
||||
pub struct SgCommand {
|
||||
#[packed_field(element_size_bytes = "36")]
|
||||
header: Header,
|
||||
#[packed_field(element_size_bytes = "6")]
|
||||
scsi_cmd: InquiryCommand,
|
||||
}
|
||||
|
||||
impl From<InquiryCommand> for SgCommand {
|
||||
fn from(value: InquiryCommand) -> Self {
|
||||
SgCommand {
|
||||
header: Header::new(&value, 0, 0),
|
||||
scsi_cmd: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PackedStruct)]
|
||||
#[packed_struct(endian = "msb")]
|
||||
pub struct Header {
|
||||
_packet_len: u32,
|
||||
|
||||
reply_len: u32,
|
||||
|
||||
#[packed_field(element_size_bits = "32")]
|
||||
_pack_id: ReservedZero<Bits<32>>,
|
||||
|
||||
result: u32,
|
||||
|
||||
#[packed_field(element_size_bits = "1")]
|
||||
twelve_byte: bool,
|
||||
#[packed_field(element_size_bits = "5")]
|
||||
target_status: Integer<u8, Bits<5>>,
|
||||
#[packed_field(element_size_bits = "8")]
|
||||
host_status: u8,
|
||||
#[packed_field(element_size_bits = "8")]
|
||||
driver_status: u8,
|
||||
#[packed_field(element_size_bits = "10")]
|
||||
_other_flags: ReservedZero<Bits<10>>,
|
||||
|
||||
sense_buffer: [u8; 16],
|
||||
}
|
||||
|
||||
impl Header {
|
||||
fn new(relevant_cmd: &InquiryCommand, in_size: u32, out_size: u32) -> Self {
|
||||
Self {
|
||||
_packet_len: (size_of::<Header>() as u32
|
||||
+ size_of::<InquiryCommand>() as u32
|
||||
+ in_size)
|
||||
.into(),
|
||||
reply_len: size_of::<Header>() as u32 + out_size,
|
||||
_pack_id: ReservedZero::default(),
|
||||
result: 0,
|
||||
twelve_byte: size_of_val(relevant_cmd) == 12,
|
||||
target_status: 0.into(),
|
||||
host_status: 0,
|
||||
driver_status: 0,
|
||||
_other_flags: ReservedZero::default(),
|
||||
|
||||
sense_buffer: [0; 16],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_command(mut fd: File, cmd: SgCommand) -> Result<(), std::io::Error> {
|
||||
fd.write_all(&cmd.pack().unwrap())
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use packed_struct::derive::PackedStruct;
|
||||
use packed_struct::types::ReservedZero;
|
||||
use packed_struct::types::bits::Bits;
|
||||
|
||||
use crate::{Command, Control};
|
||||
|
||||
#[derive(Debug, PackedStruct)]
|
||||
#[packed_struct(endian = "msb")]
|
||||
pub struct InquiryCommand {
|
||||
opcode: u8,
|
||||
|
||||
#[packed_field(element_size_bits = "6")]
|
||||
_reserved: ReservedZero<Bits<6>>,
|
||||
#[packed_field(element_size_bits = "1")]
|
||||
_obsolete: ReservedZero<Bits<1>>,
|
||||
#[packed_field(element_size_bits = "1")]
|
||||
enable_vital_product_data: bool,
|
||||
|
||||
page_code: u8,
|
||||
|
||||
allocation_length: u16,
|
||||
|
||||
#[packed_field(element_size_bytes = "1")]
|
||||
control: Control,
|
||||
}
|
||||
|
||||
impl InquiryCommand {
|
||||
pub fn new(evpd: bool, page_code: u8, alloc_len: u16) -> Self {
|
||||
let mut cmd = Self::default();
|
||||
cmd.enable_vital_product_data = evpd;
|
||||
cmd.page_code = page_code;
|
||||
cmd.allocation_length = alloc_len;
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InquiryCommand {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
opcode: 0x12,
|
||||
_reserved: ReservedZero::default(),
|
||||
_obsolete: ReservedZero::default(),
|
||||
enable_vital_product_data: false,
|
||||
page_code: 0,
|
||||
allocation_length: 0,
|
||||
control: Control::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Command for InquiryCommand {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
use crate::sg::{SgCommand, send_command};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pack_inquiry() {
|
||||
let cmd = InquiryCommand::new(false, 0, 36);
|
||||
println!("{}", cmd.packed_struct_display_formatter());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_inquiry() {
|
||||
let fd = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/sr0")
|
||||
.unwrap();
|
||||
dbg!(&fd);
|
||||
|
||||
let cmd = SgCommand::from(InquiryCommand::new(false, 0, 36));
|
||||
|
||||
send_command(fd, cmd).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <scsi/sg.h>
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "scraper"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
plugins.workspace = true
|
||||
|
||||
semver.workspace = true
|
||||
@@ -0,0 +1,31 @@
|
||||
use plugins::compat::{PluginCompat, PluginCompatBuilder};
|
||||
use plugins::{self, Plugin};
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
const PLUGIN_NAME: &str = "Scraper";
|
||||
const PLUGIN_VERSION: Version = Version::new(0, 1, 0);
|
||||
|
||||
pub struct DynamicPlugin;
|
||||
|
||||
impl DynamicPlugin {
|
||||
#[allow(dead_code)]
|
||||
#[unsafe(no_mangle)]
|
||||
fn new() -> Box<dyn Plugin> {
|
||||
Box::new(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for DynamicPlugin {
|
||||
#[unsafe(no_mangle)]
|
||||
fn name(&self) -> &str {
|
||||
PLUGIN_NAME
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
fn compatibility(&self) -> PluginCompat {
|
||||
PluginCompatBuilder::new(PLUGIN_NAME.to_string(), PLUGIN_VERSION)
|
||||
.unwrap()
|
||||
.core_version(VersionReq::parse(plugins::CORE_VERSION.to_string().as_str()).unwrap())
|
||||
.finalize()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user