|
| 1 | +/* fake char device */ |
| 2 | +/* Usage: |
| 3 | + % make |
| 4 | + % insmod testCharDevice.ko |
| 5 | + % sudo mknod /dev/<device_name> major_number 0 |
| 6 | + */ |
| 7 | + |
| 8 | +#include<linux/init.h> /* to initiate a module */ |
| 9 | +#include<linux/module.h> /* to recognise this module as a mmodule */ |
| 10 | +#include<linux/fs.h> /* file operation structure, which |
| 11 | + allows to use to open/close and |
| 12 | + read/write the device */ |
| 13 | +#include<linux/cdev.h> /* needed for char device, make cdev |
| 14 | + available. userspace to kernel |
| 15 | + space */ |
| 16 | +#include<linux/semaphore.h> /* used acces to semaphore, process management |
| 17 | + syncronization behaviour */ |
| 18 | +#include<asm/uaccess.h> /* copy_to_user;copy_from_user */ |
| 19 | + |
| 20 | + |
| 21 | +/* additional module info. Can be displayed when calling 'modinfo' */ |
| 22 | +MODULE_LICENSE("GPL"); /* license */ |
| 23 | +MODULE_AUTHOR("Tas Devil"); /* who wrote it */ |
| 24 | +MODULE_DESCRIPTION("Kernel module for character device"); /* purpose of module */ |
| 25 | + |
| 26 | +/* (1)create structure for fake char device */ |
| 27 | +struct fake_device |
| 28 | +{ |
| 29 | + char data[100]; |
| 30 | + struct semaphore sem; |
| 31 | +} virtual_device; |
| 32 | + |
| 33 | +/* (2) to register our device */ |
| 34 | +/* we need a cdev object and some other variables */ |
| 35 | +struct cdev *mcdev; /* m --> my */ |
| 36 | +int major_number; /* will store a major number extracted |
| 37 | + from dev_t using macro |
| 38 | + mknod /dev/file c major minor */ |
| 39 | +int ret; /* will hold return value of the |
| 40 | + function; this is because the |
| 41 | + kernel stack is very small so |
| 42 | + declaring variable will eat up the |
| 43 | + stack very fast */ |
| 44 | +dev_t dev_num; /* will hold the majjor number that |
| 45 | + the kernel gives; name appears in |
| 46 | + /proc/devices */ |
| 47 | + |
| 48 | +#define DEVICE_NAME "testCharDevice" |
| 49 | + |
| 50 | +/* (7) called on device_open |
| 51 | + inode refer to file in disk |
| 52 | + and contains info about that file |
| 53 | + struct file : is refer to an abstract open file |
| 54 | + */ |
| 55 | +int device_open(struct inode *inode, struct file *filp) { |
| 56 | + /* only allow one person to open this device by using a semaphore as |
| 57 | + mutual exclusive lock - mutex */ |
| 58 | + if(down_interruptible(&virtual_device.sem) != 0) { |
| 59 | + printk(KERN_ALERT "testCharDevice: could not lock device during open"); |
| 60 | + return -1; |
| 61 | + } |
| 62 | + |
| 63 | + printk(KERN_INFO "testCharDevice: opened device"); |
| 64 | + return 0; |
| 65 | +} |
| 66 | + |
| 67 | +/* (8) called when user want to get information about device */ |
| 68 | +ssize_t device_read(struct file* filp, char* bufStoreData, size_t bufCount, loff_t* curOffset) { |
| 69 | + /* take data from kernel(device) to user space(process) */ |
| 70 | + /* copy_to_user(destination, source, sizeToTransfer) */ |
| 71 | + printk(KERN_INFO "testCharDevice: reading from device"); |
| 72 | + ret = copy_to_user(bufStoreData, virtual_device.data, bufCount); |
| 73 | + return ret; |
| 74 | +} |
| 75 | + |
| 76 | +/* (9) called when user wants to send information to device */ |
| 77 | +ssize_t device_write(struct file* filp, const char* bufSourceData, size_t bufCount, loff_t* curOffset){ |
| 78 | + /* send data from user to kernel */ |
| 79 | + /* copy_from_user(dest, source, count) */ |
| 80 | + printk(KERN_INFO "testCharDevice: writing to device"); |
| 81 | + ret = copy_from_user(virtual_device.data, bufSourceData, bufCount); |
| 82 | + return ret; |
| 83 | +} |
| 84 | + |
| 85 | +/* (10) called upon user close */ |
| 86 | +int device_close(struct inode *inode, struct file *filp) { |
| 87 | + /* by calling up, which is opposite to down of semaphore, we release |
| 88 | + the mutex that we obtained at device open. This has the effect of |
| 89 | + other process can access thr device now */ |
| 90 | + up(&virtual_device.sem); |
| 91 | + printk(KERN_INFO "testCharDevice: closed device"); |
| 92 | + return 0; |
| 93 | +} |
| 94 | + |
| 95 | +/* (6)tells the kernel which function to call when user operates on our |
| 96 | + device file */ |
| 97 | +struct file_operations fops = |
| 98 | +{ |
| 99 | + .owner = THIS_MODULE, /* prevent unloading of this module when operatons are in use */ |
| 100 | + .open = device_open, /* points to the method to call opening the device */ |
| 101 | + .release = device_close, /* closing the device */ |
| 102 | + .write = device_write, /* writing a device */ |
| 103 | + .read = device_read /* reading from device */ |
| 104 | +}; |
| 105 | + |
| 106 | +static int driver_entry(void) { |
| 107 | + /* (3) r3gister device isa two step process: */ |
| 108 | + |
| 109 | + /* ---------(1)----------- */ |
| 110 | + /* use dynamic allocation to assign our device a major number |
| 111 | + -- |
| 112 | + alloc_chrdev_region(dev_t*,uint fminor, uint count, char* name); |
| 113 | + */ |
| 114 | + |
| 115 | + ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME); |
| 116 | + /* dev_name will hold both major number and minor number */ |
| 117 | + /* start from 0 and count till 1 */ |
| 118 | + /* DEVICE_NAME */ |
| 119 | + if(ret<0) { |
| 120 | + /* if kernel returns a negative number, means failed to allocate |
| 121 | + major number */ |
| 122 | + printk(KERN_ALERT "testCharDevice: failed to allocate major number"); |
| 123 | + return ret; /* error */ |
| 124 | + } |
| 125 | + major_number = MAJOR(dev_num); /* extract major number from dev_num |
| 126 | + and store it in our variable */ |
| 127 | + printk(KERN_INFO "testCharDevice: major number is: %d", major_number); |
| 128 | + printk(KERN_INFO "testCharDevice: device file name: %s", DEVICE_NAME); /* dmesg */ |
| 129 | + |
| 130 | + /* ----------(2)---------- */ |
| 131 | + mcdev = cdev_alloc(); /* create our cdev structure; |
| 132 | + initialize cdev and allocate using |
| 133 | + cdev_alloc() */ |
| 134 | + mcdev->ops = &fops; /* struct file operation */ |
| 135 | + mcdev->owner = THIS_MODULE; |
| 136 | + |
| 137 | + /* now that we have created our cdev, we have to add it to kernel */ |
| 138 | + /* int cdev_add(struct cdev*, dev, dev_t num, unsigned int count) */ |
| 139 | + ret = cdev_add(mcdev, dev_num, 1); |
| 140 | + if(ret<0) { |
| 141 | + /* check errors */ |
| 142 | + printk(KERN_ALERT "testCharDevice: unable to add cdev to kernel"); |
| 143 | + return ret; |
| 144 | + } |
| 145 | + |
| 146 | + /* (4) Initialize our semaphore */ |
| 147 | + sema_init(&virtual_device.sem,1); /* initial value of one */ |
| 148 | + |
| 149 | + return 0; |
| 150 | +} |
| 151 | + |
| 152 | +static void driver_exit(void) { |
| 153 | + /* (5) unregister everything in reverse order */ |
| 154 | + // (a) |
| 155 | + cdev_del(mcdev); |
| 156 | + |
| 157 | + // (b) |
| 158 | + unregister_chrdev_region(dev_num, 1); |
| 159 | + printk(KERN_INFO "testCharDevice: unloaded module"); |
| 160 | + |
| 161 | +} |
| 162 | + |
| 163 | +/* inform kernel wher to start and stop our module */ |
| 164 | +module_init(driver_entry); |
| 165 | +module_exit(driver_exit); |
| 166 | + |
0 commit comments