When a parallel port device driver (such as lp) initialises it tells the sharing layer about itself using parport_register_driver. The information is put into a struct parport_driver, which is put into a linked list. The information in a struct parport_driver really just amounts to some function pointers to callbacks in the parallel port device driver.
During its initialisation, a low-level port driver tells the sharing layer about all the ports that it has found (using parport_register_port), and the sharing layer creates a struct parport for each of them. Each struct parport contains (among other things) a pointer to a struct parport_operations, which is a list of function pointers for the various operations that can be performed on a port. You can think of a struct parport as a parallel port "object", if "object-orientated" programming is your thing. The parport structures are chained in a linked list, whose head is portlist (in drivers/parport/share.c).
Once the port has been registered, the low-level port driver announces it. The parport_announce_port function walks down the list of parallel port device drivers (struct parport_drivers) calling the attach function of each.
Similarly, a low-level port driver can undo the effect of registering a port with the parport_unregister_port function, and device drivers are notified using the detach callback.
Device drivers can undo the effect of registering themselves with the parport_unregister_driver function.