guowenxue
2024-09-26 7c35a51bbd38a171ee63c79d3d70966181859021
commit | author | age
f5c330 1
G 2 #include <linux/module.h>
3 #include <linux/init.h>
4 #include <linux/kernel.h>   /* printk() */
5 #include <linux/slab.h>     /* kmalloc() */
6 #include <linux/fs.h>       /* everything... */
7 #include <linux/errno.h>    /* error codes */
8 #include <linux/types.h>    /* size_t */
9 #include <linux/cdev.h>     /* cdev */
10 #include <linux/fcntl.h>    /* O_ACCMODE */
11 #include <linux/uaccess.h>  /* copy_*_user */
12 #include <linux/version.h>  /* kernel version code */
13 #include <linux/moduleparam.h>
14
15 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
16 #define access_ok_wrapper(type,arg,cmd) access_ok(type, arg, cmd)
17 #else
18 #define access_ok_wrapper(type,arg,cmd) access_ok(arg, cmd)
19 #endif
20
21 /* ioctl definitions, use 'z' as magic number */
22 #define CHRDEV_IOC_MAGIC  'z'
23 #define CHRDEV_IOCRESET    _IO(CHRDEV_IOC_MAGIC, 0)
24 #define CHRDEV_IOCSET      _IOW(CHRDEV_IOC_MAGIC, 1, int)
25 #define CHRDEV_IOCGET      _IOR(CHRDEV_IOC_MAGIC, 2, int)
26 #define CHRDEV_IOC_MAXNR   3
27
28 /* device name and major number */
29 #define DEV_NAME         "chrdev"
30 #define DEV_SIZE         1024
31 #define CONFIG_AUTODEV   1 /* Auto create device node in driver or not */
32
33 //#define DEV_MAJOR  79
34 #ifndef DEV_MAJOR
35 #define DEV_MAJOR  0
36 #endif
37
38 int dev_major = DEV_MAJOR;
39 module_param(dev_major, int, S_IRUGO);
40
41 typedef struct chrdev_s
42 {
43     struct cdev    cdev;
44 #ifdef CONFIG_AUTODEV
45     struct class  *class;
46     struct device *device;
47 #endif
48     char          *data;   /* data buffer */
49     uint32_t       size;   /* data buffer size */
50     uint32_t       bytes;  /* data bytes in the buffer */
51 } chrdev_t;
52
53 static struct chrdev_s   dev;
54
55 static ssize_t chrdev_read (struct file *file, char __user *buf, size_t count, loff_t *f_pos)
56 {
57     struct chrdev_s   *dev = file->private_data;
58     ssize_t nbytes;
59     ssize_t rv = 0;
60
61     /* no data in buffer  */
62     if( !dev->bytes )
63         return 0;
64
65     /* copy data to user space */
66     nbytes = count>dev->bytes ? dev->bytes : count;
67     if( copy_to_user(buf, dev->data, nbytes) )
68     {
69         rv = -EFAULT;
70         goto out;
71     }
72
73     /* update return value and data bytes in buffer */
74     rv = nbytes;
75     dev->bytes -= nbytes;
76
77 out:
78     return rv;
79 }
80
81 static ssize_t chrdev_write (struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
82 {
83     struct chrdev_s   *dev = file->private_data;
84     ssize_t nbytes;
85     ssize_t rv = 0;
86
87     /* no space left */
88     if( dev->bytes >= dev->size )
89         return -ENOSPC;
90
91     /* check copy data bytes */
92     if( dev->size - dev->bytes < count)
93         nbytes = dev->size - dev->bytes;
94     else
95         nbytes = count;
96
97     /* copy data from user space  */
98     if( copy_from_user(&dev->data[dev->bytes], buf, nbytes) )
99     {
100         rv = -EFAULT;
101         goto out;
102     }
103
104     /* update return value and data bytes in buffer */
105     rv = nbytes;
106     dev->bytes += nbytes;
107
108 out:
109     return rv;
110 }
111
112 /* The ioctl() implementation */
113 long chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
114 {
115     struct chrdev_s *dev = file->private_data;
116     int rv, data;
117     size_t  bytes = sizeof(data);
118
119     /*
120      * extract the type and number bitfields, and don't decode
121      * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
122      */
123     if (_IOC_TYPE(cmd) != CHRDEV_IOC_MAGIC) return -ENOTTY;
124     if (_IOC_NR(cmd) > CHRDEV_IOC_MAXNR) return -ENOTTY;
125
126     /*
127      * the direction is a bitmask, and VERIFY_WRITE catches R/W transfers.
128      * `Type' is user-oriented, while access_ok is kernel-oriented,
129      * so the concept of "read" and "write" is reversed
130      */
131     if (_IOC_DIR(cmd) & _IOC_READ)
132         rv = !access_ok_wrapper(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
133     else if (_IOC_DIR(cmd) & _IOC_WRITE)
134         rv =  !access_ok_wrapper(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
135     if (rv) return -EFAULT;
136
137     switch(cmd) {
138         case CHRDEV_IOCRESET:
139             dev->bytes = 0;
140             memset(dev->data, 0, dev->size);
141             rv = 0;
142             break;
143
144         /* Last 4 bytes in the buffer used to save ioctl() data*/
145         case CHRDEV_IOCSET:
146             rv = __get_user(data, (int __user *)arg);
147             if( !rv )
148                  memcpy(&dev->data[dev->size-bytes], &data, bytes);
149             break;
150
151         case CHRDEV_IOCGET:
152             memcpy(&data, &dev->data[dev->size-bytes], bytes);
153             rv = __put_user(data, (int __user *)arg);
154             break;
155
156         default:
157             return -ENOTTY;
158     }
159
160     return rv;
161 }
162
163 static int chrdev_open (struct inode *inode, struct file *file)
164 {
165     struct chrdev_s    *dev; /* device struct address */
166
167     /* get the device struct address by container_of() */
168     dev = container_of(inode->i_cdev, struct chrdev_s, cdev);
169
170     /* save the device struct address for other methods */
171     file->private_data = dev;
172
173     return 0;
174 }
175
176 static int chrdev_close (struct inode *node, struct file *file)
177 {
178     return 0;
179 }
180
181 static struct file_operations chrdev_fops = {
182     .owner   = THIS_MODULE,
183     .open    = chrdev_open,          /* open()  implementation */
184     .read    = chrdev_read,          /* read()  implementation */
185     .write   = chrdev_write,         /* write() implementation */
186     .release = chrdev_close,         /* close() implementation */
187     .unlocked_ioctl = chrdev_ioctl,  /* ioctl() implementation */
188 };
189
190 static int __init chrdev_init(void)
191 {
192     dev_t      devno;
193     int        rv;
194
195     /* malloc and initial device read/write buffer */
196     dev.data = kmalloc(DEV_SIZE, GFP_KERNEL);
197     if( !dev.data )
198     {
199         printk(KERN_ERR " %s driver kmalloc() failed\n", DEV_NAME);
200         return -ENOMEM;
201     }
202     dev.size = DEV_SIZE;
203     dev.bytes = 0;
204     memset(dev.data, 0, dev.size);
205
206     /* dynamic alloc device node major number if not set */
207     if(0 != dev_major)
208     {
209         devno = MKDEV(dev_major, 0);
210         rv = register_chrdev_region(devno, 1, DEV_NAME);
211     }
212     else
213     {
214         rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
215         dev_major = MAJOR(devno);
216     }
217
218     if(rv < 0)
219     {
220         printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major);
221         return -ENODEV;
222     }
223
224     /* setup and register cdev into kernel */
225     cdev_init(&dev.cdev, &chrdev_fops);
226     dev.cdev.owner = THIS_MODULE;
227     rv = cdev_add(&dev.cdev, devno, 1);
228     if( rv )
229     {
230         rv = -ENODEV;
231         printk(KERN_ERR "%s driver regist failed, rv=%d\n", DEV_NAME, rv);
232         goto failed1;
233     }
234
235 #ifdef CONFIG_AUTODEV
236     /* create device node in user space */
237 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
238     dev.class = class_create(DEV_NAME);
239 #else
240     dev.class = class_create(THIS_MODULE, DEV_NAME);
241 #endif
242     if (IS_ERR(dev.class)) {
243         rv = PTR_ERR(dev.class);
244         goto failed2;
245     }
246
247     dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME);
248     if( !dev.device )
249     {
250         rv = -ENODEV;
251         printk(KERN_ERR "%s driver create device failed\n", DEV_NAME);
252         goto failed3;
253     }
254 #endif
255
256     printk(KERN_INFO "%s driver on major[%d] installed.\n", DEV_NAME, dev_major);
257     return 0;
258
259 #ifdef CONFIG_AUTODEV
260 failed3:
261     class_destroy(dev.class);
262
263 failed2:
264     cdev_del(&dev.cdev);
265 #endif
266
267 failed1:
268     unregister_chrdev_region(devno, 1);
269     kfree(dev.data);
270
271     printk(KERN_ERR "%s driver installed failed.\n", DEV_NAME);
272     return rv;
273 }
274
275 static void __exit chrdev_exit(void)
276 {
277 #ifdef CONFIG_AUTODEV
278     device_del(dev.device);
279     class_destroy(dev.class);
280 #endif
281
282     cdev_del(&dev.cdev);
283     unregister_chrdev_region(MKDEV(dev_major,0), 1);
284
285     kfree(dev.data);
286
287     printk(KERN_INFO "%s driver removed!\n", DEV_NAME);
288     return;
289 }
290
291 module_init(chrdev_init);
292 module_exit(chrdev_exit);
293
294 MODULE_LICENSE("Dual BSD/GPL");
295 MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>");