1. scull.h
#ifndef _SCULL_H_ #define _SCULL_H_ #ifndef SCULL_MAJOR #define SCULL_MAJOR 0 /* dynamic major by default */ #endif #ifndef SCULL_NR_DEVS #define SCULL_NR_DEVS 4 /* scull0 through scull3 */ #endif #ifndef SCULL_QUANTUM #define SCULL_QUANTUM 4000 #endif #ifndef SCULL_QSET #define SCULL_QSET 1000 #endif /* * Representation of scull quantum sets. */ struct scull_qset { void **data; struct scull_qset *next; }; struct scull_dev { struct scull_qset *data; /* Pointer to first quantum set */ int quantum; /* the current quantum size */ int qset; /* the current array size */ unsigned long size; /* amount of data stored here */ unsigned int access_key; /* used by sculluid and scullpriv */ struct semaphore sem; /* mutual exclusion semaphore */ struct cdev cdev; /* Char device structure */ }; /* * The different configurable parameters */ extern int scull_major; /* main.c */ extern int scull_nr_devs; extern int scull_quantum; extern int scull_qset; #endif /* _SCULL_H_ */
2 scull.c
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/seq_file.h> #include <linux/cdev.h> #include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> /* copy_*_user */ #include "scull.h" /* local definitions */ /* * Our parameters which can be set at load time. */ int scull_major = SCULL_MAJOR; int scull_minor = 0; int scull_nr_devs = SCULL_NR_DEVS; /* number of bare scull devices */ int scull_quantum = SCULL_QUANTUM; int scull_qset = SCULL_QSET; module_param(scull_major, int, S_IRUGO); module_param(scull_minor, int, S_IRUGO); module_param(scull_nr_devs, int, S_IRUGO); module_param(scull_quantum, int, S_IRUGO); module_param(scull_qset, int, S_IRUGO); MODULE_AUTHOR("victorsummer"); MODULE_LICENSE("Dual BSD/GPL"); struct scull_dev *scull_devices; /* allocated in scull_init_module */ /* * Empty out the scull device; must be called with the device * semaphore held. */ int scull_trim(struct scull_dev *dev) { struct scull_qset *next, *dptr; int qset = dev->qset; /* "dev" is not-null */ int i; for (dptr = dev->data; dptr; dptr = next) { /* all the list items */ if (dptr->data) { for (i = 0; i < qset; i++) kfree(dptr->data[i]); kfree(dptr->data); dptr->data = NULL; } next = dptr->next; kfree(dptr); } dev->size = 0; dev->quantum = scull_quantum; dev->qset = scull_qset; dev->data = NULL; return 0; } /* * Open and close */ int scull_open(struct inode *inode, struct file *filp) { struct scull_dev *dev; /* device information */ dev = container_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev; /* for other methods */ /* now trim to 0 the length of the device if open was write-only */ if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { if (down_interruptible(&dev->sem)) return -ERESTARTSYS; scull_trim(dev); /* ignore errors */ up(&dev->sem); } return 0; /* success */ } int scull_release(struct inode *inode, struct file *filp) { return 0; } /* * Follow the list */ struct scull_qset *scull_follow(struct scull_dev *dev, int n) { struct scull_qset *qs = dev->data; /* Allocate first qset explicitly if need be */ if (! qs) { qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (qs == NULL) return NULL; /* Never mind */ memset(qs, 0, sizeof(struct scull_qset)); } /* Then follow the list */ while (n--) { if (!qs->next) { qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (qs->next == NULL) return NULL; /* Never mind */ memset(qs->next, 0, sizeof(struct scull_qset)); } qs = qs->next; continue; } return qs; } /* * Data management: read and write */ ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scull_dev *dev = filp->private_data; struct scull_qset *dptr; /* the first listitem */ int quantum = dev->quantum, qset = dev->qset; int itemsize = quantum * qset; /* how many bytes in the listitem */ int item, s_pos, q_pos, rest; ssize_t retval = 0; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (*f_pos >= dev->size) goto out; if (*f_pos + count > dev->size) count = dev->size - *f_pos; /* find listitem, qset index, and offset in the quantum */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position (defined elsewhere) */ dptr = scull_follow(dev, item); if (dptr == NULL || !dptr->data || ! dptr->data[s_pos]) goto out; /* don't fill holes */ /* read only up to the end of this quantum */ if (count > quantum - q_pos) count = quantum - q_pos; if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; out: up(&dev->sem); return retval; } ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct scull_dev *dev = filp->private_data; struct scull_qset *dptr; int quantum = dev->quantum, qset = dev->qset; int itemsize = quantum * qset; int item, s_pos, q_pos, rest; ssize_t retval = -ENOMEM; /* value used in "goto out" statements */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* find listitem, qset index and offset in the quantum */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position */ dptr = scull_follow(dev, item); if (dptr == NULL) goto out; if (!dptr->data) { dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL); if (!dptr->data) goto out; memset(dptr->data, 0, qset * sizeof(char *)); } if (!dptr->data[s_pos]) { dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); if (!dptr->data[s_pos]) goto out; } /* write only up to the end of this quantum */ if (count > quantum - q_pos) count = quantum - q_pos; if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; /* update the size */ if (dev->size < *f_pos) dev->size = *f_pos; out: up(&dev->sem); return retval; } struct file_operations scull_fops = { .owner = THIS_MODULE, .llseek = NULL, .read = scull_read, .write = scull_write, .ioctl = NULL, .open = scull_open, .release = scull_release, }; /* * Finally, the module stuff */ /* * The cleanup function is used to handle initialization failures as well. * Thefore, it must be careful to work correctly even if some of the items * have not been initialized */ void scull_cleanup_module(void) { int i; dev_t devno = MKDEV(scull_major, scull_minor); /* Get rid of our char dev entries */ if (scull_devices) { for (i = 0; i < scull_nr_devs; i++) { scull_trim(scull_devices + i); cdev_del(&scull_devices[i].cdev); } kfree(scull_devices); } /* cleanup_module is never called if registering failed */ unregister_chrdev_region(devno, scull_nr_devs); } /* * Set up the char_dev structure for this device. */ static void scull_setup_cdev(struct scull_dev *dev, int index) { int err, devno = MKDEV(scull_major, scull_minor + index); cdev_init(&dev->cdev, &scull_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &scull_fops; err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding scull%d", err, index); } int scull_init_module(void) { int result, i; dev_t dev = 0; /* * Get a range of minor numbers to work with, asking for a dynamic * major unless directed otherwise at load time. */ if (scull_major) { dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "scull: can't get major %d/n", scull_major); return result; } /* * allocate the devices -- we can't have them static, as the number * can be specified at load time */ scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL); if (!scull_devices) { result = -ENOMEM; goto fail; /* Make this more graceful */ } memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev)); /* Initialize each device. */ for (i = 0; i < scull_nr_devs; i++) { scull_devices[i].quantum = scull_quantum; scull_devices[i].qset = scull_qset; init_MUTEX(&scull_devices[i].sem); scull_setup_cdev(&scull_devices[i], i); } /* At this point call the init function for any friend device */ dev = MKDEV(scull_major, scull_minor + scull_nr_devs); return 0; /* succeed */ fail: scull_cleanup_module(); return result; } module_init(scull_init_module); module_exit(scull_cleanup_module);
3 Makefile
KERNELDIR = /usr/src/linux-headers-2.6.35-30-generic /*改成你内核头文件所在的目录和名称 */ PWD := $(shell pwd) obj-m := scull.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules //这行前面要用Tab键空格
4 编译
Lenovo:~/LDD$ make //在文件所在的目录下
Lenovo:~/LDD$ insmod ./scull.ko //make之后生成ko文件,insmod之后要建立节点才能被应用程序读写访问:
cat /proc/devices //在文件里面可以查看到scull的主设备号
root@caoyin-Lenovo:/home/caoyin/LDD# sudo su
root@caoyin-Lenovo:/home/caoyin/LDD# mknod /dev/scull0 c 250 0
root@caoyin-Lenovo:/home/caoyin/LDD# mknod /dev/scull1 c 250 1
root@caoyin-Lenovo:/home/caoyin/LDD# mknod /dev/scull2 c 250 2
root@caoyin-Lenovo:/home/caoyin/LDD# mknod /dev/scull3 c 250 3
root@caoyin-Lenovo:/home/caoyin/LDD# chgrp staff /dev/scull[0-3]
root@caoyin-Lenovo:/home/caoyin/LDD# chmod 664 /dev/scull[0-3]
在 /dev下面会发先产生了scull0-scull3终端
5 测试程序scull_test.c
#include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main() { char *write_buffer = "Hello, Character driver!"; char *read_buffer = malloc(sizeof(char*)*30); int scull_fd = 0; int code, back = 0; // write data to scull scull_fd = open("/dev/scull1", O_WRONLY); while(*write_buffer != '\0') { code = write(scull_fd , write_buffer , 8); printf("Write %d bytes to scull/n", code); write_buffer += code; } close(scull_fd); // read data from scull scull_fd = open("/dev/scull1", O_RDONLY); while((code = read(scull_fd , read_buffer , 8)) != 0) { printf("Read %d bytes from scull/n", code); read_buffer += code; back += code; } read_buffer -= back; printf("Read %s from scull /n", read_buffer); close(scull_fd); exit(0); return 0; }
运行:
root@caoyin-Lenovo:/home/caoyin/LDD# gcc scull_test.c -o scull_test.o
root@caoyin-Lenovo:/home/caoyin/LDD# sudo ./scull_test.o
结果:
Write 8 bytes to scull/nWrite 8 bytes to scull/nWrite 8 bytes to scull/nRead 8 bytes from scull/nRead 8 bytes from scull/nRead 8 bytes from scull/nRead Hello, C