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:18:43 +1000 Message-ID: In-Reply-To: <20260530-rust_serdev-v10-3-65d1d5db876c@posteo.de> References: <20260530-rust_serdev-v10-0-65d1d5db876c@posteo.de> <20260530-rust_serdev-v10-3-65d1d5db876c@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 This is the main patch at 553 lines. Several observations: **1. Typo in `Parity` enum:** ```rust + /// Even partiy. + Even =3D bindings::serdev_parity_SERDEV_PARITY_EVEN, ``` "partiy" should be "parity". **2. `Timeout::into_jiffies` and the `Max` variant:** ```rust +impl Timeout { + fn into_jiffies(self) -> isize { + match self { + Self::Jiffies(value) =3D> value.get().try_into().unwrap_or_def= ault(), + Self::Milliseconds(value) =3D> { + msecs_to_jiffies(value.get()).try_into().unwrap_or_default= () + } + Self::Max =3D> 0, + } + } +} ``` `Timeout::Max` returns 0, which works because the C `serdev_device_write()`= treats `timeout =3D=3D 0` as `MAX_SCHEDULE_TIMEOUT`. However, `serdev_devi= ce_wait_until_sent()` passes this timeout directly to `tty_wait_until_sent(= )`, which calls `schedule_timeout()`. For `schedule_timeout()`, a timeout o= f 0 means "don't wait at all" =E2=80=94 the opposite of "wait forever." So = `Timeout::Max` passed to `wait_until_sent()` would result in no waiting, wh= ich is a bug. This should be documented or handled differently. Consider mapping `Max` to= `isize::MAX` (which is `LONG_MAX` =3D `MAX_SCHEDULE_TIMEOUT` on the C side= ) and only keeping the special 0 mapping for `write_all` if that's what's i= ntended, or making `Timeout` method-specific. **3. `unwrap_or_default()` for overflow:** ```rust + Self::Jiffies(value) =3D> value.get().try_into().unwrap_or_def= ault(), ``` If the conversion from `Jiffies` (likely `u64`) to `isize` overflows, it si= lently becomes 0. Combined with `serdev_device_write`'s treatment of 0 as `= MAX_SCHEDULE_TIMEOUT`, an overflow would silently become an infinite wait. = This is surprising =E2=80=94 a saturating conversion to `isize::MAX` would = be safer. **4. Probe callback and `PrivateData` registration:** ```rust + extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> ke= rnel::ffi::c_int { + let sdev =3D unsafe { &*sdev.cast::>>() }; + ... + let private_data =3D devres::register( + sdev.as_ref(), + try_pin_init!(PrivateData { + active <- new_mutex!(false), + }), + GFP_KERNEL, + )?; + let mut active =3D private_data.active.lock(); + ... + unsafe { + (*sdev.as_raw()).rust_private_data =3D + (&raw const *private_data).cast::().cast_mut() + }; ``` The `PrivateData` is registered via `devres::register`, so it will be clean= ed up when the device is unregistered. The pointer is stored in `rust_priva= te_data` after registration. The mutex is locked before `set_drvdata` to sy= nchronize with `receive_buf_callback`, which is good. However, the sequencing is important: `serdev_device_set_client_ops` is cal= led *after* storing `rust_private_data`, so there's no race window where `r= eceive_buf` could fire with `rust_private_data` unset. Good. **5. `remove_callback` synchronization with `receive_buf`:** ```rust + let private_data =3D unsafe { &*(*sdev.as_raw()).rust_private_data= .cast::() }; + *private_data.active.lock() =3D false; + T::unbind(sdev, data); ``` The `active` flag is set to `false` under the mutex before calling `unbind`= . In `receive_buf_callback`, the mutex is held while checking `active` and = calling `T::receive`. This ensures `receive` won't be called after `remove_= callback` starts. However, there's a subtlety: `drvdata_borrow` is called *= before* the `active` check in `remove_callback`, but *after* it in `receive= _buf_callback`. In `remove_callback`, `drvdata_borrow` is called before set= ting `active =3D false` =E2=80=94 this is fine since the driver data hasn't= been dropped yet. The ordering looks correct. **6. `write_all` return value handling:** ```rust + pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result { + let ret =3D unsafe { + bindings::serdev_device_write(...) + }; + to_result(ret as i32).map(|()| ret.unsigned_abs()) + } ``` `serdev_device_write` returns `ssize_t`. Casting to `i32` could truncate on= platforms where `ssize_t` is 64-bit. If a write succeeds with more than `i= 32::MAX` bytes (theoretically), the cast could produce a negative value and= falsely trigger an error. In practice serdev writes won't be that large, b= ut the cast is technically lossy. The `ret.unsigned_abs()` on the return is= also on the original `ssize_t`, which correctly preserves the full value. = The issue is only in the error check path. **7. `write` return type is `Result`:** ```rust + pub fn write(&self, data: &[u8]) -> Result { ``` The C function `serdev_device_write_buf` returns `int`. Returning `u32` for= the count is fine since successful values are non-negative, but the same `= ret as i32` cast concern applies since `ret` comes from an `int` which is a= lready `i32`-sized in practice on Linux, so this one is actually fine. **8. Missing `[ ` bracket in doc comment:** ```rust + /// Note that any accepted data has only been buffered by the controll= er. Use + /// [ Device::wait_until_sent`] to make sure the controller write buff= er has actually been ``` There's a broken rustdoc link: `[ Device::wait_until_sent`]` should be `[`D= evice::wait_until_sent`]` (missing backtick after `[`). **9. `set_baudrate` returns `Result<(), u32>`:** ```rust + pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> { ``` This is unusual =E2=80=94 most Rust-for-Linux abstractions use `Result` (i.= e., `Result`). Returning `Err(actual_baudrate)` when the requeste= d baudrate isn't exactly matched is potentially confusing, as the C `serdev= _device_set_baudrate` returns the actual baudrate set (which might be close= but not exact). The sample driver treats any mismatch as `EINVAL`. Conside= r whether a baudrate mismatch (e.g., requesting 115200 and getting 115201) = should really be an error or just a warning. This is arguably a design choi= ce that should be documented. **10. `set_baudrate` and other config methods on `Device` only:** All the configuration methods are on `impl Device`, but in t= he sample driver, `probe` receives `&'bound serdev::Device>`. The = sample calls `sdev.set_baudrate(...)` directly, which means `Device` = must deref to `Device` (via `impl_device_context_deref!`). This work= s, but it's worth noting that `Core` impls `Bound` methods through deref. --- Generated by Claude Code Patch Reviewer