Skip to content

Commit a9e47e9

Browse files
committed
init
0 parents  commit a9e47e9

File tree

5 files changed

+396
-0
lines changed

5 files changed

+396
-0
lines changed

Makefile

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
obj-m := dicedevice.o
2+
dicedevice-objs := dice.o
3+
4+
all: ko
5+
6+
ko:
7+
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
8+
9+
clean:
10+
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
11+
12+
transf:
13+
scp -rv ./dice [email protected]:/home/william/code/ve482-l9/dice
14+
15+
load:
16+
insmod ./dicedevice.ko
17+
cat /proc/modules | grep "dicedevice"
18+
cat /proc/devices | grep "Dice"
19+
20+
reg240:
21+
mknod /dev/dice0 c 240 0
22+
mknod /dev/dice1 c 240 1
23+
mknod /dev/dice2 c 240 2
24+
25+
rmdev:
26+
rm -f /dev/dice0
27+
rm -f /dev/dice1
28+
rm -f /dev/dice2
29+
rmmod dicedevice.ko
30+

README

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# simple-linux-kernel-module
2+
## Overview
3+
// TODO
4+
5+
Supported features:
6+
1. Printing messages to kernel debug log
7+
2. Registering as a char device driver with dynamic minor numbers
8+
3. Write to user space
9+
4. Read user's input
10+
11+
Demo:
12+
13+
## Future Plan
14+
1. `debugfs` user/kernel interface communication
15+
2. `sysfs` user/kernel interface communication
16+
3. `readdir` manipulation
17+
4. `release` manipulation
18+
5. `fsync` manipulation
19+
6. `lock` manipulation
20+
21+
## Known Issues
22+
1. For larger input number (e.g., 20), the ASCII-dice will not display nicely.

dice.c

+296
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
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");

dice.h

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// Created by cyx on 2020/10/5.
3+
// Modified by matrixpecker on 2020/12/29.
4+
//
5+
6+
#ifndef DEVICE_DICE_H
7+
#define DEVICE_DICE_H
8+
9+
#define DICE_DEVS 3
10+
11+
#define DICE_NUM 2
12+
#define GEN_SIDES 20 /* default number of sides of `arbitrary` dice game */
13+
14+
struct dice_dev{
15+
int num; /* number of dice in this device */
16+
int dice_type; /* type of dice game: regular; backgammon; generic */
17+
struct cdev d_cdev;
18+
};
19+
20+
/* dice_type */
21+
#define TYPE_REGULAR 11
22+
#define TYPE_BACKGAMMON 12
23+
#define TYPE_GENERIC 13
24+
25+
#endif //DEVICE_DICE_H

0 commit comments

Comments
 (0)