For your first solution, you could look into using the "tap" driver. See Documentation/networking/tuntap.txt
in the kernel source for full details, but the basic idea is that you create a "tap" network interface, and any ethernet packets that the kernel stack wants to send via that interface are actually delivered to your userspace process; similarly, your userspace process can inject packets into the tap interface and the kernel will treate them as being received by the networking stack. This would solve your problem of being able to glue a userspace networking implementation into the main kernel stack.
However, implementing everything in the kernel should be doable too. This is after all essentially what the PPP or SLIP implementations do: they create a network interface that runs via a serial interface. The key concept here is "line discipline" -- rather than trying to open the serial interface in your kernel driver, you want some userspace control that does the opening and then sets the line discipline. You should be able to model this on something like PPP or IrDA in the mainline kernel (and I believe searching the web for "linux line discipline" will give some documentation on writing one).