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: Tue, 28 Apr 2026 14:14:44 +1000 Message-ID: In-Reply-To: <20260427-rust_serdev-v6-3-173798d5e1a3@posteo.de> References: <20260427-rust_serdev-v6-0-173798d5e1a3@posteo.de> <20260427-rust_serdev-v6-3-173798d5e1a3@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. Several observations: **1. `Parity` enum typo:** ```rust /// Even partiy. Even =3D bindings::serdev_parity_SERDEV_PARITY_EVEN, ``` Should be "Even parity." **2. `Timeout::Max` doc is misleading:** ```rust /// Wait as long as possible. /// /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`]. Max, ``` The actual value returned is 0, not `MAX_SCHEDULE_TIMEOUT` (which is `LONG_= MAX`). This works because the C serdev API (`serdev_device_write`) and tty = layer (`tty_wait_until_sent`) both convert `timeout =3D=3D 0` to `MAX_SCHED= ULE_TIMEOUT` internally. The doc should say something like "Passes 0 to the= C API, which treats it as 'wait indefinitely'" rather than claiming equiva= lence with `MAX_SCHEDULE_TIMEOUT`, which is a different numeric value. **3. `Timeout::into_jiffies()` overflow behavior:** ```rust 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() } ``` If a `Jiffies` value exceeds `isize::MAX`, `try_into()` fails and `unwrap_o= r_default()` returns 0. By the C convention, 0 means "wait forever," so a t= oo-large timeout becomes "wait forever." This happens to be reasonable beha= vior, but it's accidental =E2=80=94 the code looks like a bug where overflo= w silently becomes zero. A comment explaining why `unwrap_or_default()` (i.= e., 0 =3D wait forever) is the correct fallback would help future readers. = Alternatively, using `.unwrap_or(0)` with an explicit comment about the C c= onvention would be clearer. **4. Broken doc link in `write()`:** ```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 /// emptied. ``` Should be `[`Device::wait_until_sent`]` =E2=80=94 missing opening backtick = after `[`. **5. `PrivateData` and `UnsafeCell` synchronization:** ```rust #[pin_data] struct PrivateData { #[pin] probe_complete: Completion, error: UnsafeCell, } ``` The `Completion` provides the memory synchronization between the write (in = `probe_callback`) and read (in `receive_buf_callback`) of the `error` field= . This is correct at runtime. However, `UnsafeCell` makes `PrivateDat= a` `!Sync`, and the code creates shared `&PrivateData` references across th= reads via raw pointer casts. The SAFETY comments explain the synchronizatio= n correctly, but using `AtomicBool` with `Relaxed` ordering (since the `Com= pletion` provides the barrier) would make the type system happy and avoid t= he need for unsafe reasoning about `Sync`. **6. `probe_callback` structure =E2=80=94 ordering concern:** ```rust // Step 1: Register PrivateData via devres let private_data =3D devres::register(sdev.as_ref(), ...)?; // Step 2: Store pointer (*sdev.as_raw()).rust_private_data =3D ...; // Step 3: Set client ops (enables receive_buf callback) bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS); // Step 4: Open device (data can start flowing) bindings::devm_serdev_device_open(...)?; // Step 5: Driver probe let data =3D T::probe(sdev, id_info); // Step 6: Store driver data let result =3D sdev.as_ref().set_drvdata(data); // Step 7: Signal completion private_data.probe_complete.complete_all(); ``` The ordering of steps 3 and 4 is correct =E2=80=94 ops must be set before o= pening, and the `Completion` in `receive_buf_callback` prevents data access= before probe finishes. This is well-designed. One subtlety: if `T::probe()` itself calls `write_all` with `Timeout::Max` = (as shown in the doc example), this happens before `complete_all()`. If a r= esponse arrives from the device, `receive_buf_callback` would be invoked, w= hich calls `wait_for_completion()` =E2=80=94 but `complete_all()` hasn't be= en called yet, so this would **deadlock** if `receive_buf_callback` runs on= the same thread context. In practice, serdev receive callbacks run from an= interrupt/workqueue context different from the probe context, so this shou= ld be fine, but it's worth documenting this constraint. **7. `remove_callback` does not call `drvdata_borrow` through `Device`:** Looking at `remove_callback`: ```rust let sdev =3D unsafe { &*sdev.cast::>() }; let data =3D unsafe { sdev.as_ref().drvdata_borrow::() }; T::unbind(sdev, data); ``` But `unbind` is defined as: ```rust fn unbind(sdev: &Device, this: Pin<&Self>) { ... } ``` The `sdev` argument to `unbind` is `&Device`, but the trait d= eclares `&Device`. This works because of the deref coercion c= hain set up by `impl_device_context_deref!`. Fine. **8. `write_all` return type inconsistency with docs:** The doc says: ``` /// Returns the number of bytes written (less than `data.len()` if interrup= ted). /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::ERESTARTSY= S`] if interrupted /// before any bytes were written. ``` The second line reads like it describes error return values, but the format= ting makes it look like part of the success return description. It should b= e something like: "Returns `Err(ETIMEDOUT)` or `Err(ERESTARTSYS)` if interr= upted before any bytes were written." **9. `ret as i32` truncation in `write_all`:** ```rust let ret =3D unsafe { bindings::serdev_device_write(...) }; // returns ssize= _t (isize) to_result(ret as i32).map(|()| ret.unsigned_abs()) ``` On 64-bit, `ssize_t` is 64 bits. The `as i32` truncation is safe in practic= e because serial writes never exceed 2GB, but for correctness, converting t= he full `isize` error check would be more robust: e.g., `if ret < 0 { retur= n Err(...) }` instead of relying on `to_result(ret as i32)`. --- --- Generated by Claude Code Patch Reviewer