Besides functions defined by the file_operations structure, there is at least one other function that you will have to write, the foo_init() function. You will have to change chr_dev_init() in chr_drv/mem.c to call your foo_init() function. foo_init() will take one argument, long mem_start, which will be the address of the current end of allocated memory. If your driver needs to allocate more than 4K of contiguous space at runtime, here is the place. Simply save mem_start in an appropriate variable, add however much space you need to mem_start, and return the new value. Your driver will now have exclusive access to the memory between the old and new values of mem_start.
foo_init() should first call register_chrdev() to register itself and avoid device number contention. register_chrdev() takes three arguments:
Generally, the foo_init() routine will then attempt to detect the hardware that it is supposed to be driving. It should make sure that all necessary data structures are filled out for all present hardware, and have some way of ensuring that non-present hardware does not get accessed. [detail different ways of doing this.]