字符设备实例,根据《linux设备驱动开发详解》globalmem驱动加上个人理解的注释

来源:互联网 发布:淘宝主营类目在哪里看 编辑:程序博客网 时间:2024/05/16 05:59
#include <linux/module.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/cdev.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/sched.h>```````#define GLOBALMEM_SIZE 0x1000#define MEM_CLEAR 0x1#define GLOBALMEM_MAJOR 230#define ULONG  unsigned long#define USHORT unsigned short#define UCHAR  unsigned char#define UINT   unsigned intstatic int globalmem_major = GLOBALMEM_MAJOR;/*insmod的时候用作向模块传递参数使用,传递的参数保存在globalmem_major中  使用示例:  insmod xxx.ko globalmem_major=xxx times=cnt  其中,times表示传参的次数*/module_param(globalmem_major, int, S_IRUGO);struct globalmem_dev {struct cdev cdev;UINT current_len;UCHAR mem[GLOBALMEM_SIZE];struct mutex mutex;wait_queue_head_t r_wait;wait_queue_head_t w_wait;};struct globalmem_dev *globalmem_devp;/**************************************************************************************Description: 上层调用open()/fopen()函数对此字符设备进行操作的时候会调用此函数***************************************************************************************/static int globalmem_open(struct inode *inode, struct file *filp){printk(KERN_INFO "Globalmem_open called.\n");filp->private_data = globalmem_devp;return 0;}/**************************************************************************************Description: 上层调用close()/fclose()函数对此字符设备进行操作的时候会调用此函数***************************************************************************************/static int globalmem_release(struct inode *inode, struct file *filp){filp->private_data = NULL;printk(KERN_INFO "Globalmem_release called.\n");return 0;}static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){struct globalmem_dev *dev = filp->private_data;switch(cmd){case MEM_CLEAR:mutex_lock(&dev->mutex);memset(dev->mem, 0, GLOBALMEM_SIZE);printk(KERN_INFO "globalmem is set to zero\n");mutex_unlock(&dev->mutex);break;default:return -EINVAL;}return 0;}/*poos是一个指向"long offset type"对象, 它指出用户正在存取的文件位置*/static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){ULONG p = *ppos; /*offset from the file beginning*/UINT count = size;int ret = 0;struct globalmem_dev *dev = filp->private_data;/*Declare a waitlist,  DECLARE_WAITQUEUE(name, task)*/DECLARE_WAITQUEUE(wait, current);if (p >= GLOBALMEM_SIZE){return 0;}if (count > GLOBALMEM_SIZE -p)count = GLOBALMEM_SIZE -p;/*copy_to_user返回未拷贝成功的字节数,如果返回0则表示拷贝成功*/mutex_lock(&dev->mutex);add_wait_queue(&dev->r_wait, &wait);/*如果当前buf中的存储内容为0,那么读取需要阻塞,如果使用了非阻塞的读取的方式,那么就直接返回不阻塞等待*/while (0 == dev->current_len){if (filp->f_flags & O_NONBLOCK){ret = -EAGAIN;/*非阻塞方式打开,返回错误后需要打开mutex*/goto out;}/*TASK_INTERRUPTIBLE : 可中断的等待状态,进程被挂起(睡眠),直到某个条件为真。产生一个硬件中断,释放进程正在等待的系统资源,或传递一个信号都是可以唤醒进程的条件(把进程的状态放回到TASK_RUNNING)*/__set_current_state(TASK_INTERRUPTIBLE);mutex_unlock(&dev->mutex);schedule();if (signal_pending(current)) {ret = -ERESTARTSYS;goto out2;}mutex_lock(&dev->mutex);}if (count > dev->current_len)count = dev->current_len;if (copy_to_user(buf, dev->mem, count)){ret = -EFAULT;goto out;}else{/*拷贝成功则将前count个字符删除掉*/memcpy(dev->mem, dev->mem + count, dev->current_len -count);dev->current_len -= count;printk(KERN_INFO "read %d bytes(s), current_len:%d\n", count, dev->current_len);wake_up_interruptible(&dev->w_wait);ret = count;}out:mutex_unlock(&dev->mutex);out2:remove_wait_queue(&dev->w_wait, &wait);set_current_state(TASK_RUNNING);return ret;}static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos){ULONG p = *ppos;UINT count = size;int ret = 0;struct globalmem_dev *dev = filp->private_data;DECLARE_WAITQUEUE(wait, current);if (p >= GLOBALMEM_SIZE)return 0;if (count > GLOBALMEM_SIZE - dev->current_len)count = GLOBALMEM_SIZE -dev->current_len;/*在多线程或多进程环境下,对于临界资源可能存在多个访问,使用自旋锁可以防止在执行拷贝操作的时候内容被修改*/mutex_lock(&dev->mutex);add_wait_queue(&dev->w_wait, &wait);while (dev->current_len == GLOBALMEM_SIZE){if (filp->f_flags & O_NONBLOCK){ret = -EAGAIN;goto out;}__set_current_state(TASK_INTERRUPTIBLE);mutex_unlock(&dev->mutex);/*调度实现函数,该函数的作用是从运行队列的链表rq中找到一个进程,并随后将CPU分配给这个进程*/schedule();/*检查进程中是否有信号需要处理,返回不为0表示有信号需要处理*/if (signal_pending(current)) {/*返回-ERESTARTSYS表示函数处理完毕后重新执行信号函数前的某个系统调用*/ret = -ERESTARTSYS;goto out2;}mutex_lock(&dev->mutex);}/*需要偏移过当前已经保存的空间*/if (copy_from_user(dev->mem + dev->current_len, buf, count)) {/*拷贝失败返回错误信息*/return -EFAULT;goto out;}else{/*拷贝成功刷新数值*/dev->current_len += count;printk(KERN_INFO "Written %u to bytes(s) form %u\n", count, dev->current_len);wake_up_interruptible(&dev->r_wait);ret = count;}out:mutex_unlock(&dev->mutex);out2:remove_wait_queue(&dev->w_wait, &wait);set_current_state(TASK_RUNNING);return ret;}/****************************************************************************************Description: 上层函数调用lseek()的时候调用此函数Parameter  : 参数orig来源于lseek()函数中的whence参数? SEEK_SET : 参数offset即为新的读写位置 SEEK_CUR : 以目前的读写位置往后增加offset个位移量 SEEK_END : 将读写位置指向文件尾再增加offset个位移量 当whence值为SEEK_CUR或SEEK_END时,参数offset允许负值的出现*****************************************************************************************/static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig){loff_t ret = 0;switch (orig){/*f_ops: 当前的读写位置*/case SEEK_SET: if (offset < 0){/*invalid argument*/return -EINVAL;  break;}if ((unsigned int)offset > GLOBALMEM_SIZE){ret = -EINVAL;break;}filp->f_pos = (unsigned int)offset;ret = filp->f_pos;break;case SEEK_CUR:if ((filp->f_pos + offset) > GLOBALMEM_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 globalmem_fops = {.owner  = THIS_MODULE,.llseek = globalmem_llseek,.read= globalmem_read,.write= globalmem_write,.unlocked_ioctl = globalmem_ioctl,.open= globalmem_open,.release= globalmem_release,};static void globalmem_setup_cdev(struct globalmem_dev *dev, int index){int err, devno = MKDEV(globalmem_major, index);cdev_init(&dev->cdev, &globalmem_fops);dev->cdev.owner = THIS_MODULE;err = cdev_add(&dev->cdev, devno, 1);if (err){printk(KERN_NOTICE "Error %d adding globalmem %d", err, index);}}/***********************************************************************************Function name: globalmem_initAuthor       : zjwDescription  : module entrance function************************************************************************************/static int __init globalmem_init(void){int ret;dev_t devno = MKDEV(globalmem_major, 0);if (globalmem_major){/*已知主设备号的时候使用这个函数*/ret = register_chrdev_region(devno, 1, "globalmem");}else{/*自动分配主设备号,第二个参数是次设备号*/ret = alloc_chrdev_region(&devno, 0, 1, "globalmem");globalmem_major = MAJOR(devno);}if (ret < 0){return ret;}globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);if (!globalmem_devp){ret = -ENOMEM;goto fail_malloc;}/*init mutex*/mutex_init(&globalmem_devp->mutex);/*waitlist init*/init_waitqueue_head(&globalmem_devp->r_wait);init_waitqueue_head(&globalmem_devp->w_wait);/*进行字符设备相关设置*/globalmem_setup_cdev(globalmem_devp, 0);return 0;fail_malloc:unregister_chrdev_region(devno, 1);return ret;}/*设置insmod xxx.ko后模块调用入口函数*/module_init(globalmem_init);static void __exit globalmem_exit(void){cdev_del(&globalmem_devp->cdev);kfree(globalmem_devp);unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);}/*设置rmmod xxx.ko后模块调用出口函数*/module_exit(globalmem_exit);MODULE_AUTHOR("jiawei.zhang@cs.sunrex.com.cn");MODULE_LICENSE("GPL v2");


阅读全文
1 0
原创粉丝点击