|
| 1 | +// |
| 2 | +// Created by matrixpecker on 2020/11/29. |
| 3 | +// Modified by matrixpecker on 2020/12/29. |
| 4 | +// |
| 5 | + |
| 6 | +#include <linux/kernel.h> |
| 7 | +#include <linux/errno.h> |
| 8 | +#include <linux/cdev.h> /* kernel device */ |
| 9 | +#include <linux/init.h> /* km init */ |
| 10 | +#include <linux/module.h> /* kernel module */ |
| 11 | +#include <linux/fs.h> /* file_operations structure */ |
| 12 | +#include <linux/uaccess.h> /* copy_to_user */ |
| 13 | +#include <linux/kdev_t.h> /* MKDEV */ |
| 14 | +#include <linux/export.h> /* THIS_MODULE */ |
| 15 | +#include <linux/time64.h> |
| 16 | +#include <linux/timekeeping.h> |
| 17 | +#include <linux/random.h> /* get_random_bytes */ |
| 18 | +#include <linux/slab.h> /* kmalloc */ |
| 19 | + |
| 20 | +#include "dice.h" |
| 21 | + |
| 22 | +int dice_major; /* major number of the device */ |
| 23 | +static const char dice_name[] = "Dice"; |
| 24 | +int dice_devs = DICE_DEVS; /* number of devices */ |
| 25 | +int dice_num = DICE_NUM; /* initial number of dice in a device */ |
| 26 | +int gen_sides = GEN_SIDES; /* initial number of sides of `arbitrary` dice game */ |
| 27 | + |
| 28 | +module_param(gen_sides, int, 0); |
| 29 | + |
| 30 | +// struct dice_dev *dice_devices; |
| 31 | +struct dice_dev dice_devices[DICE_DEVS]; |
| 32 | + |
| 33 | +static int dice_open(struct inode *inode, struct file *filp) |
| 34 | +/* |
| 35 | + * EFFECTS: called when device is opened; |
| 36 | + * sets up `filp->private_data` as current device |
| 37 | + */ |
| 38 | +{ |
| 39 | + // hints: container_of |
| 40 | + unsigned int minor_num = MINOR(inode->i_rdev); |
| 41 | + printk(KERN_NOTICE "Dice: opening device with minor %d\n",minor_num); |
| 42 | + filp->private_data = &dice_devices[minor_num]; |
| 43 | + struct dice_dev *dev = (struct dice_dev *)filp->private_data; |
| 44 | + |
| 45 | + return 0; |
| 46 | +} |
| 47 | + |
| 48 | +static int dice_release(struct inode *inode, struct file *filp) |
| 49 | +/* |
| 50 | + * EFFECTS: called when device is closed; do nothing here |
| 51 | + */ |
| 52 | +{ |
| 53 | + return 0; |
| 54 | +} |
| 55 | + |
| 56 | +static ssize_t dice_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) |
| 57 | +/* |
| 58 | + * EFFECTS: called when `read` is performed on the device; |
| 59 | + * output `dice_num` dice, with faces randomly generated |
| 60 | + * according to `dice_num` and `dice_type` stored in `filp->private_data` |
| 61 | + */ |
| 62 | +{ |
| 63 | + // hints: __put_user |
| 64 | + printk(KERN_NOTICE "Dice: outputing data\n"); |
| 65 | + |
| 66 | + struct dice_dev *dev = (struct dice_dev *)filp->private_data; |
| 67 | + int dice_type = dev->dice_type; |
| 68 | + dice_num = dev->num; |
| 69 | + |
| 70 | + int strcount = 0; |
| 71 | +#define MAX_DICE_STR 10240 |
| 72 | + char str[MAX_DICE_STR]; |
| 73 | + |
| 74 | + int rd[dice_num]; |
| 75 | + int i; |
| 76 | + int err = 0; |
| 77 | + |
| 78 | + if (dice_type == TYPE_REGULAR){ |
| 79 | + // regular |
| 80 | + printk(KERN_NOTICE "Dice: outputing regular dice | "); |
| 81 | + for(i=0;i<dice_num;i++){ |
| 82 | + get_random_bytes(&rd[i], sizeof(int)); |
| 83 | + rd[i] = ((rd[i] % 6) + 12) % 6; // 0~5 |
| 84 | + // printk("%d ",rd[i]); |
| 85 | + } |
| 86 | + // printk("\n"); |
| 87 | + |
| 88 | + static const char DICE_REG_PATTERN[3][6][12] = { |
| 89 | + {"| | ","| | ","| o | ","| o o | ","| o o | ","| o o | "}, |
| 90 | + {"| o | ","| o o | ","| | ","| | ","| o | ","| o o | "}, |
| 91 | + {"| | ","| | ","| o o | ","| o o | ","| o o | ","| o o | "} |
| 92 | + }; |
| 93 | + |
| 94 | + for(i=0;i<dice_num;i++) err += snprintf(str+err,MAX_DICE_STR,"------- "); |
| 95 | + err += snprintf(str+err,MAX_DICE_STR,"\n"); |
| 96 | + for(i=0;i<dice_num;i++) err += snprintf(str+err,MAX_DICE_STR,"%s",DICE_REG_PATTERN[0][rd[i]]); |
| 97 | + err += snprintf(str+err,MAX_DICE_STR,"\n"); |
| 98 | + for(i=0;i<dice_num;i++) err += snprintf(str+err,MAX_DICE_STR,"%s",DICE_REG_PATTERN[1][rd[i]]); |
| 99 | + err += snprintf(str+err,MAX_DICE_STR,"\n"); |
| 100 | + for(i=0;i<dice_num;i++) err += snprintf(str+err,MAX_DICE_STR,"%s",DICE_REG_PATTERN[2][rd[i]]); |
| 101 | + err += snprintf(str+err,MAX_DICE_STR,"\n"); |
| 102 | + for(i=0;i<dice_num;i++) err += snprintf(str+err,MAX_DICE_STR,"------- "); |
| 103 | + err += snprintf(str+err,MAX_DICE_STR,"\n"); |
| 104 | + } |
| 105 | + else if (dice_type == TYPE_BACKGAMMON){ |
| 106 | + // backgammon |
| 107 | + printk(KERN_NOTICE "Dice: outputing backgammon dice | "); |
| 108 | + for(i=0;i<dice_num;i++){ |
| 109 | + get_random_bytes(&rd[i], sizeof(int)); |
| 110 | + rd[i] = ((rd[i] % 6) + 12) % 6; // 0~5 |
| 111 | + // printk("%d ",rd[i]); |
| 112 | + } |
| 113 | + // printk("\n"); |
| 114 | + |
| 115 | + static const char DICE_BACKGAMMON[6][4] = { |
| 116 | + "2","4","8","16","32","64" |
| 117 | + }; |
| 118 | + |
| 119 | + for(i=0;i<dice_num;i++) err += snprintf(str+err,MAX_DICE_STR,"%s ",DICE_BACKGAMMON[rd[i]]); |
| 120 | + err += snprintf(str+err,MAX_DICE_STR,"\n"); |
| 121 | + } |
| 122 | + else if (dice_type == TYPE_GENERIC){ |
| 123 | + // arbitrary number of sides |
| 124 | + printk(KERN_NOTICE "Dice: outputing generic dice | "); |
| 125 | + for(i=0;i<dice_num;i++){ |
| 126 | + get_random_bytes(&rd[i], sizeof(int)); |
| 127 | + rd[i] = ((rd[i] % gen_sides) + 2*gen_sides) % gen_sides + 1; // 0~5 |
| 128 | + // printk("%d ",rd[i]); |
| 129 | + } |
| 130 | + // printk("\n"); |
| 131 | + |
| 132 | + for(i=0;i<dice_num;i++) err += snprintf(str+err,MAX_DICE_STR,"%d ",rd[i]); |
| 133 | + err += snprintf(str+err,MAX_DICE_STR,"\n"); |
| 134 | + } |
| 135 | + |
| 136 | + /* examing output to user space */ |
| 137 | + if (err<0){ |
| 138 | + printk(KERN_NOTICE "Dice: error in snprintf\n"); |
| 139 | + } |
| 140 | + strcount = err; |
| 141 | + if ( *offp >= strcount ) { |
| 142 | + printk(KERN_NOTICE "Dice: printer reaches ending, aborting\n"); |
| 143 | + return 0; |
| 144 | + } |
| 145 | + if ( *offp + count > strcount ){ |
| 146 | + count = strcount - *offp; |
| 147 | + } |
| 148 | + if ( copy_to_user(buff, str+*offp, count) != 0 ){ |
| 149 | + printk(KERN_NOTICE "Dice: copy_to_user error, aborting\n"); |
| 150 | + return -EFAULT; |
| 151 | + } |
| 152 | + *offp += count; |
| 153 | + |
| 154 | + return count; |
| 155 | +} |
| 156 | + |
| 157 | +static ssize_t dice_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp) |
| 158 | +/* |
| 159 | + * EFFECTS: called when `write` is performed on the device; |
| 160 | + * change `dice_num` according to the number written |
| 161 | + */ |
| 162 | +{ |
| 163 | + // hints: __get_user |
| 164 | + // int retval = 0; // return value |
| 165 | + struct dice_dev *dev = (struct dice_dev *)filp->private_data; |
| 166 | +#define MAX_DICE_IN 10 |
| 167 | + char input_str[MAX_DICE_IN]; |
| 168 | + long int tmp_num = 0; |
| 169 | + // retval = __get_user(dice_num, buff); |
| 170 | + // if(retval != 0){ // error |
| 171 | + // printk(KERN_NOTICE "Dice: error on getting user's input!\n"); |
| 172 | + // } |
| 173 | + // if(dice_num == '\n') return 1; |
| 174 | + // retval = 1; // return number of bytes written |
| 175 | + if (count > MAX_DICE_IN){ |
| 176 | + printk(KERN_NOTICE "Dice: too much input"); |
| 177 | + return -EINVAL; |
| 178 | + } |
| 179 | + if ( copy_from_user(input_str, buff, count) != 0 ){ |
| 180 | + printk(KERN_NOTICE "Dice: copy_from_user error!\n"); |
| 181 | + return -EINVAL; |
| 182 | + } |
| 183 | + input_str[count-1] = '\0'; |
| 184 | + if (kstrtol(input_str, 10, &tmp_num) != 0){ |
| 185 | + printk(KERN_NOTICE "Dice: kstrtol error handling <%s> with count %d!\n", input_str, (int)count); |
| 186 | + } |
| 187 | + dice_num = (int) tmp_num; |
| 188 | + // dice_num = dice_num - '0'; |
| 189 | + printk(KERN_NOTICE "Dice: new dice number assigned: %d\n",dice_num); |
| 190 | + // printk("return: %d ", retval); |
| 191 | + dev->num = dice_num; |
| 192 | + // return retval; |
| 193 | + return count; |
| 194 | +} |
| 195 | + |
| 196 | +/** |
| 197 | + * @brief file_operations structure |
| 198 | + * |
| 199 | + */ |
| 200 | +static struct file_operations fops = { |
| 201 | + .read = dice_read, |
| 202 | + .write = dice_write, |
| 203 | + .open = dice_open, |
| 204 | + .release = dice_release, |
| 205 | +}; |
| 206 | + |
| 207 | +static void dice_setup_cdev(struct cdev *dev, int i) |
| 208 | +/* |
| 209 | + * EFFECTS: registers `dev` with major number `dice_major` and minor number `i` |
| 210 | + */ |
| 211 | +{ |
| 212 | + int err, devno = MKDEV(dice_major, i); |
| 213 | + |
| 214 | + cdev_init(dev, &fops); |
| 215 | + dev->owner = THIS_MODULE; |
| 216 | + dev->ops = &fops; |
| 217 | + err = cdev_add(dev, devno, 1); |
| 218 | + /* Fail gracefully if need be */ |
| 219 | + if (err) |
| 220 | + printk(KERN_NOTICE "Error %d adding dice%d", err, i); |
| 221 | +} |
| 222 | + |
| 223 | +static int __init dice_initialization(void) |
| 224 | +/* |
| 225 | + * EFFECTS: Initialization; called when insmod |
| 226 | + */ |
| 227 | +{ |
| 228 | + /* Register your major dynamically */ |
| 229 | + printk("Initializing Grandpa\'s Dice Module...\n"); |
| 230 | + int reg_result = 0; |
| 231 | + dev_t dev = MKDEV(dice_major, 0); |
| 232 | + /* kernel memory allocation */ |
| 233 | + // dice_devices = kmalloc(dice_devs * sizeof(struct dice_dev), GFP_KERNEL); |
| 234 | + |
| 235 | + /* figure out the device number */ |
| 236 | + if (dice_major) reg_result = register_chrdev_region(dev, dice_devs, dice_name); |
| 237 | + else{ |
| 238 | + // baseminor: 0, count: DICE_DEVS, name: dice_name |
| 239 | + reg_result = alloc_chrdev_region(&dev, 0, dice_devs, dice_name); |
| 240 | + dice_major = MAJOR(dev); |
| 241 | + // reg_result = register_chrdev(0, dice_name, &fops); |
| 242 | + } |
| 243 | + /* err handling */ |
| 244 | + if (reg_result < 0){ |
| 245 | + printk(KERN_WARNING "Dice: fail to register the device with error %d\n", reg_result); |
| 246 | + return reg_result; |
| 247 | + } |
| 248 | + if (dice_major == 0) dice_major = reg_result; |
| 249 | + |
| 250 | + /* Char device registration; three devices (three minor numbers) for three types of dice */ |
| 251 | + int i; |
| 252 | + for (i=0; i<DICE_DEVS; i++){ |
| 253 | + // dice_setup_cdev(&((dice_devices + i)->d_cdev), i); |
| 254 | + dice_setup_cdev(&(dice_devices[i].d_cdev), i); |
| 255 | + } |
| 256 | + dice_devices[0].dice_type = TYPE_REGULAR; |
| 257 | + dice_devices[1].dice_type = TYPE_BACKGAMMON; |
| 258 | + dice_devices[2].dice_type = TYPE_GENERIC; |
| 259 | + dice_devices[0].num = dice_num; |
| 260 | + dice_devices[1].num = dice_num; |
| 261 | + dice_devices[2].num = dice_num; |
| 262 | + printk(KERN_NOTICE "Dice: successfully register the device with major number %d\n",dice_major); |
| 263 | + printk("Hi Grandpa!\n"); |
| 264 | + /* Initialize random number; we omit it here */ |
| 265 | + |
| 266 | + return 0; |
| 267 | +} |
| 268 | + |
| 269 | +static void __exit dice_cleanup(void) |
| 270 | +/* |
| 271 | + * EFFECTS: Cleanup; called when rmmod |
| 272 | + */ |
| 273 | +{ |
| 274 | + /* Free char device */ |
| 275 | + int i; |
| 276 | + for (i=0; i<DICE_DEVS; i++){ |
| 277 | + // cdev_del(&((dice_devices + i)->d_cdev)); |
| 278 | + cdev_del(&(dice_devices[i].d_cdev)); |
| 279 | + } |
| 280 | + |
| 281 | + // kfree(dice_devices); |
| 282 | + |
| 283 | + /* Unregister */ |
| 284 | + printk(KERN_WARNING "Grandpa, professor Horst told us that do not gamble, probably you should listen to him.\n"); |
| 285 | + printk(KERN_NOTICE "Dice: unregistering the device...\n"); |
| 286 | + unregister_chrdev(dice_major, dice_name); |
| 287 | + unregister_chrdev_region(MKDEV(dice_major, 0), dice_devs); |
| 288 | + printk("Hope Mum is not at home. Enjoy the day, goodbye!\n"); |
| 289 | +} |
| 290 | + |
| 291 | +module_init(dice_initialization); |
| 292 | +module_exit(dice_cleanup); |
| 293 | + |
| 294 | +MODULE_DESCRIPTION("Grandpa's Dice Driver"); |
| 295 | +MODULE_LICENSE("GPL"); |
| 296 | +MODULE_AUTHOR("matrixpecker"); |
0 commit comments