简单linux字符设备驱动程序

本文代码参考《LINUX设备驱动程序》第三章 字符设备驱动程序
本文中的“字符设备”是一段大小为PAGE_SIZE的内存空间
功能:向字符设备写入字符串;从字符设备读出字符串
代码:
1. scull.c
 1 #include <linux/init.h>

 2 #include <linux/module.h>

 3 #include <linux/fs.h>

 4 #include <linux/slab.h>

 5 #include <linux/errno.h>

 6 #include <linux/types.h>

 7 #include <linux/cdev.h>

 8 #include <asm/uaccess.h>

 9 

 10 #include "scull.h"

 11 

 12 int scull_major = SCULL_MAJOR;  13 int scull_minor = 0;  14 

 15 module_param(scull_major, int, S_IRUGO);  16 module_param(scull_minor, int, S_IRUGO);  17 

 18 struct scull_dev *scull_device;  19 

 20 int scull_trim(struct scull_dev *dev)  21 {  22     if (dev)  23  {  24         if (dev->data)  25  {  26             kfree(dev->data);  27  }  28         dev->data = NULL;  29         dev->size = 0;  30  }  31     return 0;  32 }  33 

 34 int scull_open(struct inode *inode, struct file *filp)  35 {  36     struct scull_dev *dev;  37 

 38     dev = container_of(inode->i_cdev, struct scull_dev, cdev);  39     filp->private_data = dev;  40 

 41     if ((filp->f_flags & O_ACCMODE) == O_WRONLY)  42  {  43         if (down_interruptible(&dev->sem))  44  {  45             return -ERESTARTSYS;  46  }  47  scull_trim(dev);  48         up(&dev->sem);  49  }  50 

 51     return 0;  52 }  53 

 54 int scull_release(struct inode *inode, struct file *filp)  55 {  56     return 0;  57 }  58 

 59 ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)  60 {  61     struct scull_dev *dev = filp->private_data;  62     ssize_t retval = 0;  63 

 64     if (down_interruptible(&dev->sem))  65  {  66         return -ERESTARTSYS;  67  }  68     if (*f_pos >= dev->size)  69  {  70         goto out;  71  }  72     if (*f_pos + count > dev->size)  73  {  74         count = dev->size - *f_pos;  75  }  76 

 77     if (!dev->data)  78  {  79         goto out;  80  }  81 

 82     if (copy_to_user(buf, dev->data + *f_pos, count))  83  {  84         retval = -EFAULT;  85         goto out;  86  }  87 

 88     *f_pos += count;  89     retval = count;  90 

 91     out:  92         up(&dev->sem);  93         return retval;  94 }  95 

 96 ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)  97 {  98     struct scull_dev *dev = filp->private_data;  99     ssize_t retval = -ENOMEM; 100 

101     if (down_interruptible(&dev->sem)) 102  { 103         return -ERESTARTSYS; 104  } 105 

106     if (!dev->data) 107  { 108         dev->data = kmalloc(SCULL_BUFFER_SIZE, GFP_KERNEL); 109         if (!dev->data) 110  { 111             goto out; 112  } 113         memset(dev->data, 0, SCULL_BUFFER_SIZE); 114  } 115 

116     if (count > SCULL_BUFFER_SIZE - dev->size) 117  { 118         count = SCULL_BUFFER_SIZE - dev->size; 119  } 120 

121     if (copy_from_user(dev->data + dev->size, buf, count)) 122  { 123         retval = -EFAULT; 124         goto out; 125  } 126     

127     dev->size += count; 128     retval = count; 129 

130     out: 131         up(&dev->sem); 132         return retval; 133 } 134 

135 loff_t scull_llseek(struct file *filp, loff_t off, int whence) 136 { 137     struct scull_dev *dev = filp->private_data; 138  loff_t newpos; 139 

140     switch(whence) 141  { 142         case 0: 143             newpos = off; 144             break; 145         case 1: 146             newpos = filp->f_pos + off; 147             break; 148         case 2: 149             newpos = dev->size + off; 150             break; 151         default: 152             return -EINVAL; 153  } 154     if (newpos < 0) 155  { 156         return -EINVAL; 157  } 158     filp->f_pos = newpos; 159     return newpos; 160 } 161 

162 struct file_operations scull_fops = { 163     .owner = THIS_MODULE, 164     .llseek = scull_llseek, 165     .read = scull_read, 166     .write = scull_write, 167     .open = scull_open, 168     .release = scull_release, 169 }; 170 

171 void  scull_cleanup_module(void) 172 { 173     dev_t devno = MKDEV(scull_major, scull_minor); 174 

175     if (scull_device) 176  { 177  scull_trim(scull_device); 178         cdev_del(&scull_device->cdev); 179  kfree(scull_device); 180  } 181     unregister_chrdev_region(devno, 1); 182 } 183 

184 static void scull_setup_cdev(struct scull_dev *dev) 185 { 186     int err, devno = MKDEV(scull_major, scull_minor); 187 

188     cdev_init(&dev->cdev, &scull_fops); 189     dev->cdev.owner = THIS_MODULE; 190     dev->cdev.ops = &scull_fops; 191     err = cdev_add(&dev->cdev, devno, 1); 192 

193     if (err) 194  { 195         printk(KERN_NOTICE "Error %d adding scull", err); 196  } 197 } 198 

199 static int __init scull_init_module(void) 200 { 201     int result; 202     dev_t dev = 0; 203 

204     if (scull_major) 205  { 206         dev = MKDEV(scull_major, scull_minor); 207         result = register_chrdev_region(dev, 1, "scull"); 208  } 209     else

210  { 211         result = alloc_chrdev_region(&dev, scull_minor, 1, "scull"); 212         scull_major = MAJOR(dev); 213  } 214     if (result < 0) 215  { 216         printk(KERN_WARNING "scull: can't get major %d\n", scull_major); 217         return result; 218  } 219 

220     scull_device = kmalloc(sizeof(struct scull_dev), GFP_KERNEL); 221     if (!scull_device) 222  { 223         result = -ENOMEM; 224         goto fail; 225  } 226     memset(scull_device, 0, sizeof(struct scull_dev)); 227 

228     init_MUTEX(&scull_device->sem); 229 

230  scull_setup_cdev(scull_device); 231 

232     return 0; 233 

234  fail: 235  scull_cleanup_module(); 236         return result; 237 } 238 

239 module_init(scull_init_module); 240 module_exit(scull_cleanup_module); 241 

242 MODULE_LICENSE("GPL");

2. scull.h

 1 #ifndef _SCULL_H  2 #define _SCULL_H

 3 

 4 #define SCULL_MAJOR 0

 5 #define SCULL_BUFFER_SIZE PAGE_SIZE

 6 

 7 struct scull_dev {  8     char *data;  9     unsigned long size; 10     struct semaphore sem; 11     struct cdev cdev; 12 }; 13 

14 #endif

3. Makefile

 1 obj-m += scull.o  2 

 3 CURRENT_PATH:=$(shell pwd)  4 LINUX_KERNEL:=$(shell uname -r)  5 LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)  6 

 7 all:  8     make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules  9 clean: 10     make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

4. 验证效果

1) make

2) insmod scull.ko

3) cat /proc/devices | grep scull    //获取字符设备scull的主设备号,我的是248

4) cd /dev/

5) mknod scull c 248 0

6) echo hello, world > scull    //向字符设备写入“hello, world”

7) cat scull    //读出字符设备中的内容,这里应该显示“hello, world”

你可能感兴趣的:(linux)