guowenxue
2024-05-06 435024a55a8a3f64af6ebeff596332aebd15b7e9
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 /* device name and major number */
16 #define DEV_NAME         "chrdev"
17 #define DEV_SIZE         1024
18 #define CONFIG_AUTODEV   1 /* Auto create device node in driver or not */
19
20 //#define DEV_MAJOR  79
21 #ifndef DEV_MAJOR
22 #define DEV_MAJOR  0
23 #endif
24
25 int dev_major = DEV_MAJOR;
26 module_param(dev_major, int, S_IRUGO);
27
28 typedef struct chrdev_s
29 {
30     struct cdev    cdev;
31 #ifdef CONFIG_AUTODEV
32     struct class  *class;
33     struct device *device;
34 #endif
35     char          *data;   /* data buffer */
36     uint32_t       size;   /* data buffer size */
37     uint32_t       bytes;  /* data bytes in the buffer */
38 } chrdev_t;
39
40 static struct chrdev_s   dev;
41
42 static ssize_t chrdev_read (struct file *file, char __user *buf, size_t count, loff_t *f_pos)
43 {
44     struct chrdev_s   *dev = file->private_data;
45     ssize_t nbytes;
46     ssize_t rv = 0;
47
48     /* no data in buffer  */
49     if( !dev->bytes )
50         return 0;
51
52     /* copy data to user space */
53     nbytes = count>dev->bytes ? dev->bytes : count;
54     if( copy_to_user(buf, dev->data, nbytes) )
55     {
56         rv = -EFAULT;
57         goto out;
58     }
59
60     /* update return value and data bytes in buffer */
61     rv = nbytes;
62     dev->bytes -= nbytes;
63
64 out:
65     return rv;
66 }
67
68 static ssize_t chrdev_write (struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
69 {
70     struct chrdev_s   *dev = file->private_data;
71     ssize_t nbytes;
72     ssize_t rv = 0;
73
74     /* no space left */
75     if( dev->bytes >= dev->size )
76         return -ENOSPC;
77
78     /* check copy data bytes */
79     if( dev->size - dev->bytes < count)
80         nbytes = dev->size - dev->bytes;
81     else
82         nbytes = count;
83
84     /* copy data from user space  */
85     if( copy_from_user(&dev->data[dev->bytes], buf, nbytes) )
86     {
87         rv = -EFAULT;
88         goto out;
89     }
90
91     /* update return value and data bytes in buffer */
92     rv = nbytes;
93     dev->bytes += nbytes;
94
95 out:
96     return rv;
97 }
98
99 static int chrdev_open (struct inode *inode, struct file *file)
100 {
101     struct chrdev_s    *dev; /* device struct address */
102
103     /* get the device struct address by container_of() */
104     dev = container_of(inode->i_cdev, struct chrdev_s, cdev);
105
106     /* save the device struct address for other methods */
107     file->private_data = dev;
108
109     return 0;
110 }
111
112 static int chrdev_close (struct inode *node, struct file *file)
113 {
114     return 0;
115 }
116
117 static struct file_operations chrdev_fops = {
118     .owner   = THIS_MODULE,
119     .open    = chrdev_open,          /* open()  implementation */
120     .read    = chrdev_read,          /* read()  implementation */
121     .write   = chrdev_write,         /* write() implementation */
122     .release = chrdev_close,         /* close() implementation */
123 };
124
125 static int __init chrdev_init(void)
126 {
127     dev_t      devno;
128     int        rv;
129
130     /* malloc and initial device read/write buffer */
131     dev.data = kmalloc(DEV_SIZE, GFP_KERNEL);
132     if( !dev.data )
133     {
134         printk(KERN_ERR " %s driver kmalloc() failed\n", DEV_NAME);
135         return -ENOMEM;
136     }
137     dev.size = DEV_SIZE;
138     dev.bytes = 0;
139     memset(dev.data, 0, dev.size);
140
141     /* dynamic alloc device node major number if not set */
142     if(0 != dev_major)
143     {
144         devno = MKDEV(dev_major, 0);
145         rv = register_chrdev_region(devno, 1, DEV_NAME);
146     }
147     else
148     {
149         rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
150         dev_major = MAJOR(devno);
151     }
152
153     if(rv < 0)
154     {
155         printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major);
156         return -ENODEV;
157     }
158
159     /* setup and register cdev into kernel */
160     cdev_init(&dev.cdev, &chrdev_fops);
161     dev.cdev.owner = THIS_MODULE;
162     rv = cdev_add(&dev.cdev, devno, 1);
163     if( rv )
164     {
165         rv = -ENODEV;
166         printk(KERN_ERR "%s driver regist failed, rv=%d\n", DEV_NAME, rv);
167         goto failed1;
168     }
169
170 #ifdef CONFIG_AUTODEV
171     /* create device node in user space */
172 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
173     dev.class = class_create(DEV_NAME);
174 #else
175     dev.class = class_create(THIS_MODULE, DEV_NAME);
176 #endif
177     if (IS_ERR(dev.class)) {
178         rv = PTR_ERR(dev.class);
179         goto failed2;
180     }
181
182     dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME);
183     if( !dev.device )
184     {
185         rv = -ENODEV;
186         printk(KERN_ERR "%s driver create device failed\n", DEV_NAME);
187         goto failed3;
188     }
189 #endif
190
191     printk(KERN_INFO "%s driver on major[%d] installed.\n", DEV_NAME, dev_major);
192     return 0;
193
194 #ifdef CONFIG_AUTODEV
195 failed3:
196     class_destroy(dev.class);
197
198 failed2:
199     cdev_del(&dev.cdev);
200 #endif
201
202 failed1:
203     unregister_chrdev_region(devno, 1);
204     kfree(dev.data);
205
206     printk(KERN_ERR "%s driver installed failed.\n", DEV_NAME);
207     return rv;
208 }
209
210 static void __exit chrdev_exit(void)
211 {
212 #ifdef CONFIG_AUTODEV
213     device_del(dev.device);
214     class_destroy(dev.class);
215 #endif
216
217     cdev_del(&dev.cdev);
218     unregister_chrdev_region(MKDEV(dev_major,0), 1);
219
220     kfree(dev.data);
221
222     printk(KERN_INFO "%s driver removed!\n", DEV_NAME);
223     return;
224 }
225
226 module_init(chrdev_init);
227 module_exit(chrdev_exit);
228
229 MODULE_LICENSE("Dual BSD/GPL");
230 MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>");