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 11:33:17 +1000 Message-ID: In-Reply-To: <20260603-rust_serdev-v12-1-3400ffb88b12@posteo.de> References: <20260603-rust_serdev-v12-0-3400ffb88b12@posteo.de> <20260603-rust_serdev-v12-1-3400ffb88b12@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 adding `rust/kernel/serdev.rs`, C helpers, Kconfig, = and bindings. **1. Typo in `Parity::Even` doc (line 47 of serdev.rs):** ```rust /// Even partiy. Even =3D bindings::serdev_parity_SERDEV_PARITY_EVEN, ``` "partiy" should be "parity". **2. `Timeout::into_jiffies` =E2=80=94 silent overflow to infinite wait (li= nes 66=E2=80=9374):** ```rust fn into_jiffies(self) -> isize { match self { Self::Jiffies(value) =3D> value.get().try_into().unwrap_or_default(= ), Self::Milliseconds(value) =3D> { msecs_to_jiffies(value.get()).try_into().unwrap_or_default() } Self::Max =3D> 0, } } ``` `Jiffies` is `c_ulong` (u64 on 64-bit). If a caller passes a `NonZero` value exceeding `isize::MAX`, `try_into()` fails and `unwrap_or_defaul= t()` returns 0. Since `serdev_device_write()` in C treats `timeout =3D=3D 0= ` as `MAX_SCHEDULE_TIMEOUT`, this silently converts a large-but-finite time= out into an *infinite* timeout. The user explicitly used `Timeout::Jiffies(= ...)` =E2=80=94 they did not ask for `Timeout::Max`. Consider saturating to= `isize::MAX` instead: ```rust Self::Jiffies(value) =3D> value.get().try_into().unwrap_or(isize::MAX), ``` This would cap at the largest representable finite timeout rather than sile= ntly going infinite. The same applies to the `Milliseconds` arm. **3. `Timeout` doc string is misleading (line 53):** ```rust /// Timeout in Jiffies. pub enum Timeout { ``` The enum supports Jiffies, Milliseconds, and Max. The doc should say someth= ing like "Timeout for serial device operations." **4. `write_all()` =E2=80=94 theoretical i32 truncation of ssize_t (lines 4= 89=E2=80=93504):** ```rust pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result { let ret =3D unsafe { bindings::serdev_device_write( self.as_raw(), data.as_ptr(), data.len(), timeout.into_jiffies(= ), ) }; to_result(ret as i32).map(|()| ret.unsigned_abs()) } ``` `serdev_device_write` returns `ssize_t` (i64 on 64-bit). The cast `ret as i= 32` truncates on 64-bit. If the return value were in the range `[0x80000000= , 0xFFFFFFFF]` (2=E2=80=934 GB), the truncated i32 would be negative and `t= o_result` would incorrectly treat it as an error code. This is academic for= serial devices (serial writes are never that large), but it's a latent cor= rectness issue. The comment "negative return values are guaranteed to be be= tween `-MAX_ERRNO` and `-1`" only covers the error case. A cleaner pattern = would be: ```rust if ret < 0 { return to_result(ret as i32).map(|()| 0); } Ok(ret as usize) ``` **5. `write()` doc =E2=80=94 missing `[` in cross-reference (line 513):** ```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 ``` Should be `[`Device::wait_until_sent`]` (missing opening backtick-bracket). **6. `unbind()` doc =E2=80=94 incomplete sentence (line 403):** ```rust /// This callback serves as a place for drivers to perform teardown operati= ons that require a /// `&Device` or `&Device` reference. For instance. ``` "For instance." is an incomplete sentence. Either provide an example or rem= ove it. **7. `remove_callback` does not drop PrivateData (lines 223=E2=80=93244):** ```rust extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) { let sdev =3D unsafe { &*sdev.cast::>>()= }; let private_data =3D unsafe { sdev.as_ref().drvdata_borrow::>() }; // ... calls T::unbind, but never calls drvdata_obtain to free PrivateD= ata } ``` This relies on the driver-lifetime framework to drop PrivateData after the = remove callback returns. This is correct per the framework design, but wort= h noting that the driver data (`T::Data`) is dropped via `PinnedDrop` on `P= rivateData`, not via `unbind`. The ordering is: `remove_callback` calls `un= bind` =E2=86=92 framework drops `PrivateData` =E2=86=92 `PinnedDrop` drops = `T::Data` then closes the device. This is fine. **8. Probe: receive_buf can fire before T::probe completes (lines 194=E2=80= =93210):** The probe sequence is: set drvdata =E2=86=92 set client ops =E2=86=92 open = device =E2=86=92 call `T::probe`. Since `active` starts as `false`, any `re= ceive_buf_callback` firing between `serdev_device_open` and probe completio= n will see `!*active` and return `length` (discarding data). This is intent= ional and matches the v11 changelog ("simplify abstraction by removing abil= ity to receive the initial transmission"). Just noting this is a design cho= ice visible to driver authors =E2=80=94 data arriving during probe is silen= tly consumed and discarded. **9. C helpers look correct:** The three helpers (`serdev_device_driver_unregister`, `serdev_device_put`, = `serdev_device_set_client_ops`) wrap inline functions / macros that can't b= e called directly from Rust bindgen. These are straightforward and correct. **10. `Device` but not `Device` for serial ops:** All the serial operations (`set_baudrate`, `set_flow_control`, `write`, etc= .) are only available on `Device`. However, the `probe` callback rec= eives `Device>` and `Core` derefs to `Bound`, so the driver can ca= ll these methods during probe. This is consistent with the driver-lifetime = pattern. --- --- Generated by Claude Code Patch Reviewer