#define __KERNEL__ #define MODULE #include #include #include #include #include #include #include #include #include #include #include #include #include #define true 1 #define false 0 /* This will be the name we choose for our device. We will also use this as a prefix on functions such as the entry points appearing in the file_operations struct. */ #define DEV_NAME "pp" static int Major; /* These are prototypes for residents of the file_operations struct */ static ssize_t pp_read(struct file *, char *, size_t, loff_t *); static ssize_t pp_write(struct file *, const char *, size_t, loff_t *); static int pp_open(struct inode *, struct file *); static int pp_close(struct inode *, struct file *); /* This is the file_operations struct. The init_module function will register this with the kernel so the kernel will know all the entry points it contains. */ struct file_operations Fops = { owner: THIS_MODULE, read: pp_read, write: pp_write, open: pp_open, release: pp_close, }; /* The pp_probe function does nothing here, but reminds us that a 'real' driver may need to probe for hardware resources. These resources might later be allocated in init_module. */ static int pp_probe(void){ return 0; } /* The pp_read function is a stub, but at least does a printk, for tracing purposes, when it is called. */ static ssize_t pp_read(struct file *file, char *buff, size_t ctr, loff_t *woof) { printk(KERN_ALERT "\npp_read active.\n"); return 0; } /* The pp_write function is a stub, but at least does a printk, for tracing purposes, when it is called. */ static ssize_t pp_write(struct file *file, const char *buff, size_t ctr, loff_t *woof) { printk(KERN_ALERT "\npp_write active.\n"); return 0; } /* The pp_open function does a printk for tracing purposes. */ static int pp_open(struct inode *inode, struct file *file) { printk(KERN_ALERT "\nAn instance of %s has been opened.\n", DEV_NAME); return 0; } /* The pp_close function does a printk for tracing purposes. */ static int pp_close(struct inode *inode, struct file *file) { printk(KERN_ALERT "\nOne instance of %s has been closed.\n", DEV_NAME); return 0; } /* Next we'll see that that init_module * registers the file_operations struct so the kernel will know about the entry points therein * gets back a major number * calls pp_probe, to look for hardware resources Had hardware resources been found, they would need to be allocated for use by this driver, probably within the scope of init_module. */ int init_module(void) { Major = register_chrdev( 0, DEV_NAME, &Fops); if (Major < 0) { printk("Registration Failure!\n"); return Major; } if (pp_probe() < 0) { unregister_chrdev(Major, DEV_NAME); printk(KERN_ALERT "pp_probe() failure!\n"); return -1; } printk(KERN_ALERT "\nRegistered %s, at major number = %d.\n\n", DEV_NAME, Major); printk("To use %s, you must create a device file.\n", DEV_NAME); printk("If this has not already been done, then enter:\n"); printk(" mknod /dev/%s c %d 0\n\n", DEV_NAME, Major); printk("Also set appropriate permissions for /dev/%s.\n\n", DEV_NAME); return 0; } /* The cleanup_module function unregisters the driver and, in a 'real' driver would free up any resources allocated by init_module. */ void cleanup_module(void) { int ret; ret = unregister_chrdev(Major, DEV_NAME); if (ret < 0) printk(KERN_ALERT "\nUnregistration problem where ret = %d\n\n", ret); else printk(KERN_ALERT "\nUnregistered %s, at major number = %d\n\n", DEV_NAME, Major); }