Chinaunix首页 | 论坛 | 博客
  • 博客访问: 558780
  • 博文数量: 105
  • 博客积分: 3274
  • 博客等级: 中校
  • 技术积分: 1161
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-21 12:14
文章分类

全部博文(105)

文章存档

2011年(1)

2010年(104)

分类: LINUX

2010-04-04 16:24:52

 
先上一个简化过的例子
 
//#include
#include
#include
#include
#include  /* printk() */
#include   /* kmalloc() */
#include   /* everything... */
#include  /* error codes */
#include  /* size_t */
#include
#include  /* O_ACCMODE */
#include
#include
#include   /* cli(), *_flags */
#include  /* 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]#
查看下主设备号
[\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了吧,次设备号就是0,1,2,3了,一共4个设备。
于是
[\u\@\h \W]# mknod /dev/scull0 c 251 0
按照书本的提示来吃下内存
cp /dev/zero /dev/scull0
开发板基本上就挂了。
 
换一种测试方式
[\u\@\h \W]# echo "hello" > /dev/scull0
[\u\@\h \W]# cat /dev/scull0
hello
[\u\@\h \W]#
 
这下就比较清晰了。
 
程序使用的是动态分配主设备号的方法,实际使用时基本上都是使用的awk这类工具从/proc/devices中获取信息,并在/dev目录中创建设备文件。
 
下面这个名为scull_load的脚本是scull发布的一部分。我们可以在系统的rc.local文件中调用这个脚本或是在需要手工调用。
#!/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]#
 

 
 
 
 
 
 
 
 
 
 
 
阅读(1279) | 评论(0) | 转发(0) |
0

上一篇:构造和运行模块

下一篇:awk用法

给主人留下些什么吧!~~