Some of those "from_raw" APIs exists because the types created don't have a stable layout, or because using literal "transmute" might seem too harsh. The available ways to create *mut [T; N] from *mut T (e. g. by casting), or &mut U from *mut U (e. g. by dereferencing) are less bad IMO, and there's even alternatives, e.g. the cast method to avoid coercion or the as_mut method for doing essentially &mut *x in a pointer, but with an additional null check.
(So as a consequence, you can re-write your implementation as &mut *data.cast::<[T; N]>() in case you'd prefer that. Or if you're brave and can rely on type inference, even &mut *data.cast().)
So I guess the main question here is whether or not converting *mut T to &mut [T; N]in a single step, rather than in two steps is an important enough operation to give it its own method/function. Perhaps that was already clear to you, I just wanted to point it out So we'd need to consider pros and cons of having a dedicated single-step conversion vs. doing it in two steps.
regarding your use-case, I haven't double-checked it, but I would assume [T; N] is FFI safe, so if you have a known fixed size, say length 42, array (pointer) then you could perhaps directly work with *mut [T; 42] in the first place instead ever using a *mut T?
Taking *mut [u8; 16] seems to be working flawlessly, in hindsight this is obvious.
One thing I noticed: I can also use &mut [u8; 16] in an ffi function (either calling C code or being called by C code) and Rust will not complain. Is this valid or should I rather use pointers?
Yes, that's valid, too. Of course you need to make sure to uphold the safety guarantees for &mut e. g. when calling from C for a &mut argument, or calling into C for a &mut return type, but that's the same if you wrap the function.