先上一個(gè)簡(jiǎn)化過的例子
//#include <linux/config.h>
#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("Alessandro Rubini, Jonathan Corbet");
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, .read = scull_read, .write = scull_write, .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); } return 0; /* succeed */
fail:
scull_cleanup_module(); return result; } module_init(scull_init_module);
module_exit(scull_cleanup_module); makefile文件依然用下面的
ARCH=arm
CROSS_COMPILE=arm-none-linux-gnueabi- obj-m := scull.o # KERNELDIR ?= /lib/modules/$(shell uname -r)/build KERNELDIR ?= /home/omap/linux-2.6.28-omap # The current directory is passed to sub-makes as argument PWD := $(shell pwd) modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions 只是把.o文件改變了下。
[omap@localhost ldd3]$ make clean
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions [omap@localhost ldd3]$ make make -C /home/omap/linux-2.6.28-omap M=/mnt/hgfs/linux_share/ldd3 modules make[1]: Entering directory `/home/omap/linux-2.6.28-omap' CC [M] /mnt/hgfs/linux_share/ldd3/scull.o Building modules, stage 2. MODPOST 1 modules CC /mnt/hgfs/linux_share/ldd3/scull.mod.o LD [M] /mnt/hgfs/linux_share/ldd3/scull.ko make[1]: Leaving directory `/home/omap/linux-2.6.28-omap' [omap@localhost ldd3]$ 放到devkit8000里面
[\u\@\h \W]# ls
hello.ko scull.ko [\u\@\h \W]# insmod scull.ko [\u\@\h \W]# lsmod scull 5228 0 - Live 0xbf000000 [\u\@\h \W]# 查看下主設(shè)備號(hào)
[\u\@\h \W]# cat /proc/devices
Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 29 fb 81 video4linux 89 i2c 90 mtd 108 ppp 116 alsa 128 ptm 136 pts 180 usb 189 usb_device 251 scull 252 hidraw 253 usb_endpoint 254 rtc Block devices: 1 ramdisk 259 blkext 7 loop 8 sd 31 mtdblock 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 sd 134 sd 135 sd 179 mmc [\u\@\h \W]# 看到251 scull了吧,次設(shè)備號(hào)就是0,1,2,3了,一共4個(gè)設(shè)備。
于是
[\u\@\h \W]# mknod /dev/scull0 c 251 0
按照書本的提示來吃下內(nèi)存
cp /dev/zero /dev/scull0
開發(fā)板基本上就掛了。
換一種測(cè)試方式
[\u\@\h \W]# echo "hello" > /dev/scull0
[\u\@\h \W]# cat /dev/scull0 hello [\u\@\h \W]# 這下就比較清晰了。
程序使用的是動(dòng)態(tài)分配主設(shè)備號(hào)的方法,實(shí)際使用時(shí)基本上都是使用的awk這類工具從/proc/devices中獲取信息,并在/dev目錄中創(chuàng)建設(shè)備文件。
下面這個(gè)名為scull_load的腳本是scull發(fā)布的一部分。我們可以在系統(tǒng)的rc.local文件中調(diào)用這個(gè)腳本或是在需要手工調(diào)用。
#!/bin/sh
# $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $ module="scull" device="scull" mode="664" # Group: since distributions do it differently, look for wheel or use staff
if grep -q '^staff:' /etc/group; then group="staff" else group="wheel" fi # invoke insmod with all arguments we got
# and use a pathname, as insmod doesn't look in . by default /sbin/insmod ./$module.ko $* || exit 1 # retrieve major number
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) # Remove stale nodes and replace them, then give gid and perms
# Usually the script is shorter, it's scull that has several devices in it. rm -f /dev/${device}[0-3]
mknod /dev/${device}0 c $major 0 mknod /dev/${device}1 c $major 1 mknod /dev/${device}2 c $major 2 mknod /dev/${device}3 c $major 3 ln -sf ${device}0 /dev/${device} chgrp $group /dev/${device}[0-3] chmod $mode /dev/${device}[0-3] 卸載用下面的文件
#!/bin/sh
module="scull" device="scull" # invoke rmmod with all arguments we got
/sbin/rmmod $module $* || exit 1 # Remove stale nodes
rm -f /dev/${device} /dev/${device}[0-3]
在devkit8000上看下效果
[\u\@\h \W]# sh ./scull_load
[\u\@\h \W]# lsmod scull 5228 0 - Live 0xbf004000 [\u\@\h \W]# ls -al /dev/scull* lrwxrwxrwx 1 root root 6 Jan 1 00:57 /dev/scull -> scull0 crw-rw-r-- 1 root wheel 251, 0 Jan 1 00:57 /dev/scull0 crw-rw-r-- 1 root wheel 251, 1 Jan 1 00:57 /dev/scull1 crw-rw-r-- 1 root wheel 251, 2 Jan 1 00:57 /dev/scull2 crw-rw-r-- 1 root wheel 251, 3 Jan 1 00:57 /dev/scull3 [\u\@\h \W]# 然后試下
[\u\@\h \W]# sh ./scull_unload
rmmod: module 'scull' not found [\u\@\h \W]# lsmod [\u\@\h \W]# ls -al /dev/scull* ls: /dev/scull*: No such file or directory [\u\@\h \W]# |
|