1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* Minimum Linux Character Driver Demo
 *
 * This is a character driver, which is used to demostrate Linux character driver's structure
 */
  
/* Main header files */
#include <linux/init.h>
#include <linux/module.h>
/* Useful header files */
#include <linux/kernel.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
  
/* Definitions, you can put them in a separated header(*.h) file and use #include "xxx.h" to include them */
//DBGPRINT() is used to print debug messages, comment #define IS_IN_DEBUG to disable them
#define IS_IN_DEBUG
#ifdef IS_IN_DEBUG
#define DBGPRINT(sInfo...) printk(KERN_INFO "MinimumCharDriver: " sInfo)
#else
#define DBGPRINT(sInfo...)
#endif
//printk() in different levels
#define NFOPRINT(sInfo...) printk(KERN_INFO "MinimumCharDriver: " sInfo)
#define WRNPRINT(sInfo...) printk(KERN_WARNING "MinimumCharDriver: " sInfo)
#define ERRPRINT(sInfo...) printk(KERN_ERR "MinimumCharDriver: " sInfo)
  
// Name strings
#define DRIVER_NAME "minimum-char-driver"
#define NODE_NAME "minimum-char-driver"
#define CLASS_NAME "minimum-char-driver-class"
/* End of definitions */
  
struct class *clsDriver; //Device node 
static int iMajorDeviceNumber = 0; //Set to 0 to allocate device number automatically
static struct cdev cdevDriver; //cdev structure
  
/* Character Driver related functions */
int minimum_char_driver_open(struct inode * lpNode, struct file * lpFile){
    DBGPRINT("Device file opending...\n");
    return 0;
}
  
static int minimum_char_driver_release (struct inode * lpNode, struct file * lpFile){
    DBGPRINT("Device file closing...\n");
    return 0;
}
  
ssize_t minimum_char_driver_read(struct file * lpFile, char __user * lpszBuffer, size_t iSize, loff_t * lpOffset){
    DBGPRINT("Reading data from device file...\n");
    return 0;
}
  
ssize_t minimum_char_driver_write(struct file * lpFile, const char __user * lpszBuffer, size_t iSize,loff_t * lpOffset){
    DBGPRINT("Wrtiting data to device file...\n");
    return 0;
}
  
static long minimum_char_driver_unlocked_ioctl(struct file * lpFile, unsigned int iIoControlCommand, unsigned long lpIoControlParameters){  
    DBGPRINT("Unlocked IOControl command %u with argument %lu received.\n", iIoControlCommand, lpIoControlParameters);
    return 0;
}
  
/* For kernels before 2.6.36
static int minimum_char_driver_unlocked_ioctl(struct inode * lpNode, struct file *file, unsigned int iIoControlCommand, unsigned long lpIoControlParameters){  
    DBGPRINT("IOControl command %u with argument %lu received.\n", iIoControlCommand, lpIoControlParameters);
    return 0;
}
*/
  
static struct file_operations minimum_char_driver_driver_file_operations = {
    .owner = THIS_MODULE,
    .open  = minimum_char_driver_open,  //Open device, executed when calling open()
    .release = minimum_char_driver_release, //Release device, executed when calling close()
    .read  = minimum_char_driver_read, //Read operations, executed when calling read()
    .write = minimum_char_driver_write, //Write operations, executed when calling write()
    .unlocked_ioctl = minimum_char_driver_unlocked_ioctl, //Unlocked IOControl, executed when calling ioctl()
    //.ioctl = minimum_char_driver_ioctl, //For kernels before 2.6.36, use .ioctl and comment .unlocked_ioctl
};
  
/* Platform Device related functions */
static int minimum_char_driver_probe(struct platform_device * lpPlatformDevice){
    DBGPRINT("Initializing...\n");
    return 0;
}
  
static int minimum_char_driver_remove(struct platform_device * lpPlatformDevice){
    DBGPRINT("Removing...\n");
    return 0;
}
  
void minimum_char_driver_shutdown(struct platform_device * lpPlatformDevice){
    DBGPRINT("Shutting down...\n");
    return;
}
  
static int minimum_char_driver_suspend(struct platform_device * lpPlatformDevice, pm_message_t iState){
    DBGPRINT("Suspending...\n");
    return 0;
}
  
static int minimum_char_driver_resume(struct platform_device * lpPlatformDevice){
    DBGPRINT("Resuming...\n");
    return 0;
}
  
/* Init & Exit functions */
static void minimum_char_driver_setup_cdev(struct cdev * lpCharDevice, int iMinorDeviceNumber, struct file_operations * lpFileOperations){ //Device setup function, called by init()
    int iError, iDeviceDeviceNumber = MKDEV(iMajorDeviceNumber, iMinorDeviceNumber);
    cdev_init(lpCharDevice, lpFileOperations); //Initialize cdev
    lpCharDevice->owner = THIS_MODULE;
    lpCharDevice->ops = lpFileOperations;
    iError = cdev_add(lpCharDevice, iDeviceDeviceNumber, 1);
    if (iError){
        WRNPRINT("Error %d adding device  %d.\n", iError, iMinorDeviceNumber);
    }
    NFOPRINT("Device setup process finished.\n");
}
  
static int __init minimum_char_driver_init(void){
    NFOPRINT("Initializing...\n");
    int iResult;
    dev_t dev = MKDEV(iMajorDeviceNumber, 0);
    if (iMajorDeviceNumber){
        //Static device number
        iResult = register_chrdev_region(dev, 1, DRIVER_NAME);
        DBGPRINT("register_chrdev_region().\n");
    }
    else{
        //Allocate device number
        iResult = alloc_chrdev_region(&dev, 0, 1, DRIVER_NAME);
        DBGPRINT("alloc_chrdev_region().\n");
        iMajorDeviceNumber = MAJOR(dev);
    }
    if (iResult < 0){ //Errors occurred
        WRNPRINT("alloc_chrdev_region() failed.\n");
        return iResult;
    }
    minimum_char_driver_setup_cdev(&cdevDriver, 0, &minimum_char_driver_driver_file_operations);
    DBGPRINT("The major device number of this device is %d.\n", iMajorDeviceNumber);
    //Use request_irq() to register interrupts here
  
    //Create device node
    clsDriver = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(clsDriver)){
        WRNPRINT("failed in creating device class.\n");
        return 0;
    }
    device_create(clsDriver, NULL, dev, NULL, NODE_NAME);
    return 0;
}
  
static void __exit minimum_char_driver_exit(void){
    NFOPRINT("Exiting...\n");
    device_destroy(clsDriver,MKDEV(iMajorDeviceNumber, 0));
    class_destroy(clsDriver);
    cdev_del(&cdevDriver);
    unregister_chrdev_region(MKDEV(iMajorDeviceNumber, 0), 1);
    //Use free_irq() to unregister interrupts here
  
    return;
}
  
/* Init function & Exit function */
module_init(minimum_char_driver_init);
module_exit(minimum_char_driver_exit);
  
/* Platform Driver related functions */
struct platform_driver minimum_char_driver_driver = {
    .probe = minimum_char_driver_probe,
    .remove = minimum_char_driver_remove,
    .shutdown = minimum_char_driver_shutdown,
    .suspend = minimum_char_driver_suspend,
    .resume = minimum_char_driver_resume,
    .driver = {
        .name = DRIVER_NAME
        //.owner = THIS_MODULE
    }
};
  
/* Licenses, Authors & Descriptions */
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Picsell-Dois@SEU-BME");
MODULE_DESCRIPTION("Minimum Linux character driver demo");