[Disclaimer: All Rights Reserved. You are welcome to reprint it. Do not use it for commercial purposes. Contact Email: feixiaoxing @ 163.com]
We wrote a simple character device last time, but it involves less content. There are only two functions: open and read. Today, we plan to expand the content on this basis. The basic idea is as follows: (1) compile various functions to be processed under the character device, including open, release, read, write, ioctl, and lseek functions; (2) compile a user-side program to verify that the driver function we wrote is correct. Of course, the code we have written can be explained here by referring to Mr. Song Baohua's "Linux device driver development details.
Before getting started with today's content, you can share some other things with us. I have always had a point of view since my work. That's a high level of knowledge about how to use simple code to illustrate problems in development or explain the principles in software. Some of the reasons seem unclear in the cloud, but they can all be verified by writing code. The OS can, CPU can, cache can, compiler can, and network protocol can also be used. A lot of content can be clearly expressed through several lines of code, but we didn't actually do that. I think the reason is nothing more than that. As a result, the teacher's learning of relevant knowledge is still on the concept. Secondly, our learning is too rigid and dogma, too concerned with knowledge, and not asking for practice, third, the learner lacks the ability to think, the ability to reflect on himself, and does not need to understand many things. For simple Linux devices, we can use these lines of code to clarify the problem, so that you do not have to pursue it.
Now let's take a look at how the driver code is modified.
#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/cdev.h>#include <asm/io.h>#include <asm/system.h>#include <asm/uaccess.h>#define CHRMEM_SIZE 0x1000#define MEM_CLEAR 0x1static int chr_major;struct chr_dev{struct cdev cdev;unsigned char mem[CHRMEM_SIZE];};struct chr_dev* char_devp;int chr_open(struct inode* inode, struct file* filp){filp->private_data = char_devp;return 0;}int chr_release(struct inode* inode, struct file* filp){return 0;}static int chr_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg){struct chr_dev* dev = filp->private_data;switch(cmd){case MEM_CLEAR:memset(dev->mem, 0, CHRMEM_SIZE);break;default:return -EINVAL;}return 0;}static ssize_t chr_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos){unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct chr_dev* dev = filp->private_data;if(p >= CHRMEM_SIZE){return 0;}if(count > CHRMEM_SIZE - p){return 0;}if(copy_to_user(buf, (void*)(dev->mem + p), count)){return -EINVAL;}else{*ppos += count;ret = count;}return ret;}static ssize_t chr_write(struct file* filp, const char __user* buf, ssize_t size, loff_t *ppos){unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct chr_dev* dev = filp->private_data;if(p >= CHRMEM_SIZE){return 0;}if(count > CHRMEM_SIZE - p){count = CHRMEM_SIZE - p;}if(copy_from_user(dev->mem + p, buf, count)){ret = -EINVAL;}else{*ppos += count;ret = count;}return ret;}static loff_t chr_llseek(struct file* filp, loff_t offset, int orig){loff_t ret = 0;/* orig can be SEEK_SET, SEEK_CUR, SEEK_END */switch(orig){case 0:if(offset < 0){ret = -EINVAL;break;}if((unsigned int) offset > CHRMEM_SIZE){ret = -EINVAL;break;}filp->f_pos = (unsigned int) offset;ret = filp->f_pos;break;case 1:if((filp->f_pos + offset) > CHRMEM_SIZE){ret = -EINVAL;break;}if((filp->f_pos + offset) < 0){ret = -EINVAL;break;}filp->f_pos += offset;ret = filp->f_pos;break;default:ret = - EINVAL;break;}return ret;}static const struct file_operations chr_ops = {.owner = THIS_MODULE,.llseek = chr_llseek,.read = chr_read,.write = chr_write,.ioctl = chr_ioctl,.open = chr_open,.release = chr_release};static void chr_setup_cdev(struct chr_dev* dev, int index){int err;int devno = MKDEV(chr_major, index);cdev_init(&dev->cdev, &chr_ops);dev->cdev.owner = THIS_MODULE;err = cdev_add(&dev->cdev, devno, 1);if(err){printk(KERN_NOTICE "Error happend!\n");}}int chr_init(void){int result;dev_t ndev;result = alloc_chrdev_region(&ndev, 0, 1, "chr_dev"); if(result < 0 ) { return result; } printk("chr_init(): major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev)); chr_major = MAJOR(ndev);char_devp = kmalloc(sizeof(struct chr_dev), GFP_KERNEL);if(!char_devp){result = -ENOMEM;goto final;}memset(char_devp, 0, sizeof(struct chr_dev));chr_setup_cdev(char_devp, 0);return 0;final:unregister_chrdev_region(ndev, 1);return 0;}void chr_exit(){cdev_del(&char_devp->cdev);kfree(char_devp);unregister_chrdev_region(MKDEV(chr_major, 0), 1);}module_init(chr_init);module_exit(chr_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("feixiaoxing!163.com");MODULE_DESCRIPTION("A simple device example!");
It is undeniable that our code has more content, but the basic framework is consistent. The difference is that we have added a new processing function based on the original one. The main operations we perform on devices are as follows. If you are very familiar with this concept, you will be able to learn more easily later. Of course, like the previous driver, we also need make & insmod Char. Ko & mknod/dev/chr_dev C 249 0. Next, to verify whether the above content is correct, it is essential to write a simple test code.
#include <stdio.h>#include <fcntl.h>#include <unistd.h>#define MEM_CLEAR 0x01#define CHAR_DEV_NAME "/dev/chr_dev"int main(){ int ret; int fd; int index; char buf[32];/* open device */ fd = open(CHAR_DEV_NAME, O_RDWR | O_NONBLOCK); if(fd < 0) { printf("open failed!\n"); return -1; }/* set buffer data, which will be stored into device */ for(index = 0; index < 32; index ++) { buf[index] = index; }/* write data */ write(fd, buf, 32); memset(buf, 0, 32);/* read data */ lseek(fd, 0, SEEK_SET); read(fd, buf, 32); for(index = 0; index < 32; index ++) { printf("data[%d] = %d\n", index, buf[index]); }/* reset all data to zero, read it and check whether it is ok */ioctl(fd, MEM_CLEAR, NULL);lseek(fd, 0, SEEK_SET); read(fd, buf, 32); for(index = 0; index < 32; index ++) { printf("data[%d] = %d\n", index, buf[index]); } close(fd); return 0;}
Careful friends may find that we have used a lot of processing functions in the user-side code, basically including open, release, read, write, lseek, and IOCTL. The process of testing code processing is also very simple. First, open the device, then write data, and then read data. Finally, use IOCTL to clear the data and the program returns the data. Because the Code contains comments, we will not go into detail here. You should understand and understand the code.
I hope the above content will help you.