The C64 kernel (or KERNAL as it’s known in the C64 community) routines work via a jump vector table stored in page $03xx. A common programming paradigm to create something like a "device driver" would be to hook the kernal routine by replacing the appropriate jump vector table entry with a pointer to your own custom code. So kernal calls just jump to your code instead of the routine in ROM.
What is the best approach and memory location(s) for stashing your custom "device driver" code, so that compatibility is as good as possible? Assume you want the BASIC interpreter to continue to work, normal DOS functionality to not be impacted, and minimize the chance of some other software over-writing your driver. Also, what needs to change to have the same functionality in C128 mode?