From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE61B30100E for ; Fri, 3 Jul 2026 03:02:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.42 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783047723; cv=none; b=E+IFSGSJG41gGzs6d2tu/yOtK1kWPkfCH2tysTKH6EZlB8EyNGjQm8bdCZXnXGyJhpBcvMbH1soU5ACGGq2qWFeALRNzaxxErPY4C0WxVB6e9YckRgGvTZrBYlfKhf8tpwT6kT5A8/HVEwtFF20aQBs0Eodv+FKtLPn8eyIunyY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783047723; c=relaxed/simple; bh=KO0FTjJOVLLsLn7UvcJmS/2HPJY87y5uWlvw2Ys58wA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=u1+XBcNV0sw+LI7klc0NKtSyXg8AJkqISMU92wGiBtzuIxbxx9j26aegLLNPm2ZP5+CkwsyaGARnvyaXAcY95QDMvmKQ6MKSLh45mP7uHuLbAbzW+ppTxN7m85Iq3qX0YiWTRzrJ/9hLp2iTydAf+5umaxOs6DJmFKuq3yiYfQM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fireburn.co.uk; spf=none smtp.mailfrom=fireburn.co.uk; dkim=pass (2048-bit key) header.d=fireburn-co-uk.20251104.gappssmtp.com header.i=@fireburn-co-uk.20251104.gappssmtp.com header.b=LJk3GBc3; arc=none smtp.client-ip=209.85.221.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fireburn.co.uk Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=fireburn.co.uk Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fireburn-co-uk.20251104.gappssmtp.com header.i=@fireburn-co-uk.20251104.gappssmtp.com header.b="LJk3GBc3" Received: by mail-wr1-f42.google.com with SMTP id ffacd0b85a97d-471eeac43bfso110548f8f.3 for ; Thu, 02 Jul 2026 20:02:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fireburn-co-uk.20251104.gappssmtp.com; s=20251104; t=1783047719; x=1783652519; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=gWYaWvLBgitKBD1DvypUSuQXP5THaJX3kX34tELc4Xg=; b=LJk3GBc3xlogoqE95lqrRD6Poz4dyONZJFeGI/ah/ugQogyRqB970woEjmQeoLqk+8 Ywo+YvbxKpNuc8HCtsOcSeBAhb/hOvq8wYFs2UCiKMYHZLBkzY0Fde820Y3vZv5ubu1i zprkpJv07zkknfRuT03Xqa7rupWMb+9zlPCbAoQ+TnHDRR/ECJlNkYHon9bFiS9XQoQ1 eU/VXc9p2BebO9HAoXsBYbuLdc9tiQocYnSrHT8r5CjZpi01WqQnXzNoVdKFRFpvBBe5 wsIGajTSqbbspCPQi9P7t1EjPd3kkdNU3rJcGyk3/ExqxXpp+CtD+si+tXhCQkonTIXe 6Icg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783047719; x=1783652519; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=gWYaWvLBgitKBD1DvypUSuQXP5THaJX3kX34tELc4Xg=; b=Zc+1Z8ovAddjm3IevbfiGoXrhIrmJqBclstwCV/lfMuUt84Amn6YnXYTUaM51mPMR2 FCZXR/eymWe5FN8OS5kL/0qSk6F6f0sHT8JLWrne/uBYMaMNrYJw59pW7+m35MxUZE2u 1xELApX0FKcH4crKn7o5abh0UgYpdFRy5zP1QyZVJm81ZhmdUuFgHGW+xbHKE3gBoWeM U9l1M2rYIv9MXnCQgKjKyDJNJUVGpesxl/oD0LDTfAbrg3hT0D/BEMoB/I2TyT2I6NX6 tPVTKjtmRPC00r5gjc0UpOhDqMQd++ZWJdt5co+B6LGWXMvXgX+eTLNjcQox897B485x dkxw== X-Forwarded-Encrypted: i=1; AHgh+RpAgtsvRxscA8bvxOrp87kWYNecPX0LohJokUKoNBctJcIfzBE6Gt5TJejuLu+is5v5yKCGp+A50CkwTn0=@vger.kernel.org X-Gm-Message-State: AOJu0Yx0RM+NDVn+pgBiQVoBYvDDs55j/eKJKMqH/eYbEou8nKXbENOW /iPrzFwdJQY2MIPHX0LVHk1gfNHb2I1kedGrLTE+78502W79UJ4LnO4JR7MphUUncWVceXFGbLM /jzM5q307 X-Gm-Gg: AfdE7cl3Z51cSUKzX/l3L0H/fsdLlOcBGWUPj1PRJYwRbJAmTJt2VTMYYM+s0qPKIWQ RAW4GvXPqZ+Ma0k69WZg6oQdnoFrS649kjVpFnrCy1AsbROAZCktug50Cp8gN0FxG0SOSDpUETp Tb7FrUz6JA91hcYRnp/TOdkc9NkRRxFC/0Oi2p9WlQk4EzVyD9nfYnsi7cB3gKyH00FTJMSMWGb GSErKPRjNNTXC2IKZFL66i4/q6rdbqyH7KIT+RkvKrmikXibE0NpUlRsMpL921ZhK18Sy5734eA MqMEvMB75d19fy2V3fhsDx/8blaMXWL2FU6YS0KRsHHEaK2a/Atz62nWdfuaEl4vbjhRAiWn6aA 57Rat9f7C2oFz0j8+thBx5ghjkP3Nm1KzHRCYpzJ0m7IZ3Og4OtwSDCo7HHDJ6jlsa7RxBmES05 6/0jq8x+EvjCXuuMx/yyo4cWAcm5auOXyI34sPvb74TW6nmkOPAO2KgZby X-Received: by 2002:a05:6000:2407:b0:475:f0c2:75a3 with SMTP id ffacd0b85a97d-4775ac9f53dmr13275064f8f.52.1783047719231; Thu, 02 Jul 2026 20:01:59 -0700 (PDT) Received: from axion.fireburn.co.uk ([137.220.119.72]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-477db3dbacbsm14466500f8f.4.2026.07.02.20.01.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Jul 2026 20:01:57 -0700 (PDT) From: Mike Lothian To: rust-for-linux@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, David Airlie , Simona Vetter , Danilo Krummrich , Lyude Paul , Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , linux-kernel@vger.kernel.org, Mike Lothian Subject: [RFC PATCH v2 11/18] rust: platform: add runtime platform device creation Date: Fri, 3 Jul 2026 04:00:58 +0100 Message-ID: <20260703030123.2814-12-mike@fireburn.co.uk> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260703030123.2814-1-mike@fireburn.co.uk> References: <20260617150232.2210-1-mike@fireburn.co.uk> <20260703030123.2814-1-mike@fireburn.co.uk> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The platform abstraction can register a driver (platform::Driver) but not create a device for it to bind to. Virtual drivers that spawn their own devices -- for instance DisplayLink's evdi, which creates one platform device per virtual display card from a sysfs attribute or at module load -- need the provider side. Add platform::RegisteredDevice, a thin owner around platform_device_register_full() whose Drop calls platform_device_unregister() (which unbinds any bound driver first), plus DEVID_AUTO/DEVID_NONE constants and a name/id/parent/dma_mask constructor. The non-generic methods are #[inline] so out-of-tree modules can use them without an exported symbol. Signed-off-by: Mike Lothian Assisted-by: Claude:claude-opus-4-8 [Claude-Code] --- rust/kernel/platform.rs | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 9b362e0495d3..4c2ded2e264f 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -571,3 +571,80 @@ unsafe impl Sync for Device {} // SAFETY: Same as `Device` -- the underlying `struct platform_device` is the same; // `Bound` is a zero-sized type-state marker that does not affect thread safety. unsafe impl Sync for Device {} + +/// Auto-assign a platform device instance id (`PLATFORM_DEVID_AUTO`). +pub const DEVID_AUTO: i32 = bindings::PLATFORM_DEVID_AUTO; +/// Create a platform device with no instance id (`PLATFORM_DEVID_NONE`). +pub const DEVID_NONE: i32 = bindings::PLATFORM_DEVID_NONE; + +/// An owned platform device created and registered at runtime. +/// +/// This is the *provider* side of the platform bus: whereas [`Driver`] binds to devices the bus +/// hands it, [`RegisteredDevice`] *creates* a `struct platform_device` and adds it to the bus, +/// which then binds any matching [`Driver`] (calling its `probe`). Virtual drivers such as +/// DisplayLink's `evdi` use this to spawn their DRM card devices on demand (from a sysfs `add` +/// attribute or at module init). +/// +/// [`RegisteredDevice::new`] wraps `platform_device_register_full()`; dropping the handle calls +/// `platform_device_unregister()`, which unbinds the bound driver and releases the device. +pub struct RegisteredDevice { + pdev: NonNull, +} + +// SAFETY: `platform_device_register_full()`/`platform_device_unregister()` are internally +// synchronized by the driver core and safe to call from any thread; the handle only unregisters. +unsafe impl Send for RegisteredDevice {} +// SAFETY: See `Send`; `&RegisteredDevice` exposes only the thread-safe `Device`. +unsafe impl Sync for RegisteredDevice {} + +impl RegisteredDevice { + /// Create and register a new platform device. + /// + /// `name` is the platform device name, which the platform bus also uses to match a [`Driver`]. + /// `id` is the instance id: [`DEVID_AUTO`] to auto-assign a unique one, [`DEVID_NONE`] for an + /// unnumbered device, or a fixed non-negative value. If `parent` is given, the new device is + /// created as its child. `dma_mask` requests a coherent DMA mask of that many bits (pass `0` + /// to leave the platform default). + #[inline] + pub fn new( + name: &CStr, + id: i32, + parent: Option<&device::Device>, + dma_mask: u64, + ) -> Result { + let info = bindings::platform_device_info { + parent: parent.map_or(core::ptr::null_mut(), |p| p.as_raw()), + name: name.as_char_ptr(), + id, + dma_mask, + ..Default::default() + }; + + // SAFETY: `info` is fully initialized (all remaining fields zeroed) and only borrowed for + // the duration of the call, which copies out the fields it needs. + let pdev = unsafe { bindings::platform_device_register_full(&info) }; + let pdev = crate::error::from_err_ptr(pdev)?; + + Ok(Self { + // A successful `platform_device_register_full()` returns a valid, non-null pointer. + pdev: NonNull::new(pdev).ok_or(ENOMEM)?, + }) + } + + /// Return a reference to the created platform [`Device`]. + #[inline] + pub fn device(&self) -> &Device { + // SAFETY: `self.pdev` points to a valid, registered `struct platform_device` owned by + // `self`; `Device` is a transparent wrapper over `struct platform_device`. + unsafe { &*self.pdev.as_ptr().cast::() } + } +} + +impl Drop for RegisteredDevice { + #[inline] + fn drop(&mut self) { + // SAFETY: `self.pdev` was returned by `platform_device_register_full()` and has not been + // unregistered yet; this consumes that registration exactly once. + unsafe { bindings::platform_device_unregister(self.pdev.as_ptr()) }; + } +} -- 2.55.0