From mboxrd@z Thu Jan 1 00:00:00 1970 From: Claude Code Review Bot To: dri-devel-reviews@example.com Subject: Claude review: rust: add basic serial device bus abstractions Date: Thu, 04 Jun 2026 15:07:54 +1000 Message-ID: In-Reply-To: <20260531-rust_serdev-v11-1-dee8e0d830f1@posteo.de> References: <20260531-rust_serdev-v11-0-dee8e0d830f1@posteo.de> <20260531-rust_serdev-v11-1-dee8e0d830f1@posteo.de> X-Mailer: Claude Code Patch Reviewer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Patch Review **Typo in `Parity` enum doc comment:** ```rust /// Even partiy. Even =3D bindings::serdev_parity_SERDEV_PARITY_EVEN, ``` Should be "Even parity." (typo: "partiy" =E2=86=92 "parity"). **`Timeout::into_jiffies` returns `isize` but C functions take `long`:** The `into_jiffies` method returns `isize`, which matches Rust's `c_long` on= LP64 platforms. However, the `unwrap_or_default()` fallback for the `Jiffi= es` and `Milliseconds` variants returns 0 on overflow, which silently maps = to `MAX_SCHEDULE_TIMEOUT` (wait forever). This is unlikely in practice but = surprising =E2=80=94 a caller providing a specific timeout would get an inf= inite wait if the conversion overflowed. Worth a comment or documenting the= behavior. **`write()` return type is `Result` but `write_buf` returns `int`:** ```rust pub fn write(&self, data: &[u8]) -> Result { let ret =3D unsafe { bindings::serdev_device_write_buf(self.as_raw(), data.as_p= tr(), data.len()) }; to_result(ret as i32).map(|()| ret.unsigned_abs()) } ``` The C function `serdev_device_write_buf()` returns `int`. The `ret` here wo= uld be `c_int` (i32). Calling `ret.unsigned_abs()` on a `c_int` returns `u3= 2`, which is fine. But `to_result(ret as i32)` followed by `ret.unsigned_ab= s()` =E2=80=94 on success, `ret` could be 0 (zero bytes written), and `to_r= esult(0)` returns `Ok(())`, so `unsigned_abs()` would return 0. That's corr= ect. Looks sound. **`write_all()` return type cast comment says "negative return values":** ```rust // CAST: negative return values are guaranteed to be between `-MAX_ERRNO` a= nd `-1`, // which always fit into a `i32`. to_result(ret as i32).map(|()| ret.unsigned_abs()) ``` `serdev_device_write()` returns `ssize_t`. On success it returns a positive= `size_t` value cast to `ssize_t`. The `ret as i32` cast is safe for error = codes, but for large successful writes (> 2^31 bytes), truncating `ssize_t`= to `i32` could wrap to negative and be incorrectly treated as an error. In= practice, serial writes are small, but this is technically unsound for ver= y large writes. Since serdev buffer sizes are realistically bounded well be= low `i32::MAX`, this is acceptable, but a defensive comment would be worthw= hile. **`Device` methods but `probe` uses `Device>`:** `set_baudrate`, `set_flow_control`, `set_parity`, `write_all`, `write`, `wr= ite_flush`, `wait_until_sent` are all only available on `Device`. Bu= t `probe` receives `Device>`, and `Core` derefs to `Bound`, so the= sample can call `sdev.set_baudrate(...)` through auto-deref. This is corre= ct and follows the device context pattern. **`receive_buf_callback` holding mutex across `T::receive`:** ```rust let active =3D private_data.active.lock(); if !*active { return length; } // ... T::receive(sdev, data_pinned, buf) ``` The lock on `active` is held for the entire duration of `T::receive`. This = means `receive_buf` and `PinnedDrop` are mutually exclusive, which is the i= ntended safety property. However, this means `T::receive` runs under a mute= x =E2=80=94 the C documentation says `receive_buf` "may sleep", so using a = `Mutex` (which is sleepable in the kernel) is correct here. Good design. **When `!*active`, returning `length` (consuming all data):** ```rust if !*active { return length; } ``` This silently discards all received data when the driver has been deactivat= ed. This is reasonable =E2=80=94 there's no meaningful consumer for the dat= a anymore =E2=80=94 but worth noting that the C serdev core will consider t= his data "consumed." **Missing `[ ]` bracket in doc comment:** ```rust /// Note that any accepted data has only been buffered by the controller. U= se /// [ Device::wait_until_sent`] to make sure the controller write buffer ha= s actually been ``` The opening bracket is separated from the backtick: `[ Device::wait_until_s= ent`]` should be `[`Device::wait_until_sent`]`. This will produce a broken = rustdoc link. **`AlwaysRefCounted` `dec_ref` implementation:** ```rust unsafe fn dec_ref(obj: NonNull) { unsafe { bindings::serdev_device_put(obj.cast().as_ptr()) } } ``` This casts `NonNull` to `NonNull` via `.ca= st()`. Since `Device` is `#[repr(transparent)]` wrapping `Opaque`, the cast is valid. **`Sync` for `Device` but not for other contexts explicitly:** ```rust unsafe impl Sync for Device {} unsafe impl Sync for Device {} ``` `Device` (i.e., `Device`) and `Device` both get `Sync`. `Dev= ice>` and `Device>` do not have explicit `Sync` i= mpls. Since `Core` and `CoreInternal` are only used during probe/remove (si= ngle-threaded contexts), this seems intentional and correct. **`probe_callback` acquires and dismisses `ScopeGuard` for cleanup:** The probe error path is well structured: 1. `set_drvdata` installs `PrivateData` 2. `ScopeGuard` is created to clean up via `drvdata_obtain` if probe fails 3. If `T::probe` (the `__pinned_init` call) succeeds, `dismiss()` prevents = cleanup 4. `*active =3D true` is set only on success This correctly handles the memory leak fix mentioned in the v11 changelog. --- Generated by Claude Code Patch Reviewer