ldd3 编写scull尝试

快速参考:

#include <linux/types.h>
dev_t
dev_t is the type used to represent device numbers within the kernel.
int MAJOR(dev_t dev); int MINOR(dev_t dev); Macros that extract the major and minor numbers from a device number.
dev_t MKDEV(unsigned
int major, unsigned int minor); Macro that builds a dev_t data item from the major and minor numbers.
#include
<linux/fs.h> The “filesystem” header is the header required for writing device drivers. Many important functions and data structures are declared in here.
int register_chrdev_region(dev_t first, unsigned int count, char *name) int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name) void unregister_chrdev_region(dev_t first, unsigned int count);
Functions that allow a driver to allocate and
free ranges of device numbers. register_chrdev_region should be used when the desired major number is known in advance; for dynamic allocation, use alloc_chrdev_region instead. int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); The old (pre-2.6) char device registration routine. It is emulated in the 2.6 kernel but should not be used for new code. If the major number is not 0, it is used unchanged; otherwise a dynamic number is assigned for this device.

int
unregister_chrdev(unsigned int major, const char *name); Function that undoes a registration made with register_chrdev. Both major and the name string must contain the same values that were used to register the driver.
struct file_operations; struct file; struct inode; Three important data structures used by most device drivers. The file_operations structure holds a char driver’s methods; struct file represents an open file, and struct inode represents a file on disk.
#include
<linux/cdev.h> struct cdev *cdev_alloc(void); void cdev_init(struct cdev *dev, struct file_operations *fops); int cdev_add(struct cdev *dev, dev_t num, unsigned int count); void cdev_del(struct cdev *dev); Functions for the management of cdev structures, which represent char devices within the kernel. #include <linux/kernel.h> container_of(pointer, type, field); A convenience macro that may be used to obtain a pointer to a structure from a pointer to some other structure contained within it.
#include
<asm/uaccess.h> This include file declares functions used by kernel code to move data to and from user space. unsigned long copy_from_user (void *to, const void *from, unsigned long count); unsigned long copy_to_user (void *to, const void *from, unsigned long count);
Copy data between user space and kernel space.

 

 

第一节的测试:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>         /* dev_t */
#include <linux/kdev_t.h>        /* MAJOR(dev_t dev); MINOR(dev_t dev); */
#include <linux/fs.h>            /* chrdev_region */

#include "my_scull.h"

MODULE_AUTHOR("chen");
MODULE_LICENSE("Dual BSD/GPL");

int my_scull_major = MY_SCULL_MAJOR;
int my_scull_minor = 0;
int my_scull_nr_devs = MY_SCULL_NR_DEVS;

static int my_scull_init(void)
{
    int result;
    dev_t dev;
    printk(KERN_ALERT "my scull init\n");

/*
 * Get a range of minor numbers to work with, asking for a dynamic
 * major unless directed otherwise at load time.
 */
    if(my_scull_major) {
        dev = MKDEV(my_scull_major, my_scull_minor);
        result = register_chrdev_region(dev, my_scull_nr_devs, "my_scull");
    } else {
        result = alloc_chrdev_region(&dev, my_scull_minor, my_scull_nr_devs, "my_scull");
        my_scull_major = MAJOR(dev);
    }
    if(result < 0) {
        printk(KERN_WARNING "my scull:can't get major %d\n", my_scull_major);
        return result;
    }
    
    printk(KERN_ALERT "myscull dev:%d, major:%d, minor:%d\n", dev, my_scull_major, my_scull_minor);


    return 0;
}

static void my_scull_cleanup(void)
{
    dev_t devno = MKDEV(my_scull_major, my_scull_minor);
    
    /* my_scull_cleanup is never called if registering failed */
    unregister_chrdev_region(devno, my_scull_nr_devs);

    printk(KERN_ALERT "my scull clean up\n");
}

module_init(my_scull_init);
module_exit(my_scull_cleanup);
alloc_chrdev_region

my_scull_load:

#!/bin/sh
module="my_scull"
device="my_scull"
mode="644"

# invoke insmod with all arguments we got
# and use a pathname, as newer modutils don't look in. by default
/sbin/insmod ./$module.ko $* || exit 1

# remove stale nodes
rm -f /dev/${device}[0-3]

major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
echo $major
#echo "/dev/${device}0 c $major 0"
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

# give appropriate group/permissions, and change the group.
# Not all distributions have staff, some have "wheel" instead.
group="staff"
grep -q '^staff:' /etc/group || group="wheel"

chgrp $group /dev/${device}[0-3]
chmod $mode /dev/${device}[0-3]
my_scull_load

my_scull_unload:

#!/bin/sh
module="my_scull"
device="my_scull"

# invoke rmmod with all arguments we got
/sbin/rmmod $module $* || exit 1

# Remove stale nodes
rm -f /dev/${device} /dev/${device}[0-3]
View Code

 

看完第三章后和参考例程后的数据

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>         /* dev_t */
#include <linux/kdev_t.h>        /* MAJOR(dev_t dev); MINOR(dev_t dev); */
#include <linux/fs.h>            /* chrdev_region */
#include <asm/uaccess.h>

#include "my_scull.h"

MODULE_AUTHOR("chen");
MODULE_LICENSE("Dual BSD/GPL");

int my_scull_major   = MY_SCULL_MAJOR;
int my_scull_minor   = 0;
int my_scull_nr_devs = MY_SCULL_NR_DEVS;
int my_scull_quantum = MY_SCULL_QUANTUM;
int my_scull_qset     = MY_SCULL_QSET;

struct my_scull_dev *scull_devices;    /* allocated in my_scull_init */

static struct file_operations my_scull_fops = {
    .owner =   THIS_MODULE,
    .open =    my_scull_open,
    .release = my_scull_release,
    .write =   my_scull_write,
    .read =    my_scull_read,
//    .llseek =  my_scull_llseek,
//    .ioctl =   my_scull_ioctl,
};

/*
 * Empty out the scull device; must be called with the device
 * semaphore held.
 */
int my_scull_trim(struct my_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 = my_scull_quantum;
    dev->qset = my_scull_qset;
    dev->data = NULL;
    return 0;
}

/*
 * Set up the char dev structure for this device.
 */ 
static void my_scull_setup_cdev(struct my_scull_dev *dev, int index)
{
    int err, devno = MKDEV(my_scull_major, my_scull_minor+index);

    cdev_init(&dev->cdev, &my_scull_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &my_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);
}

/*
 * Open and close
 */
int my_scull_open(struct inode *inode, struct file *filp)
{
    struct my_scull_dev *dev;   /* device information */

    dev = container_of(inode->i_cdev, struct my_scull_dev, cdev);   /* from dev_t get my_scull_dev pointer */
    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) {
        my_scull_trim(dev);         /* ignore errors */
    }
    return 0;   /* success */
}

int my_scull_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/*
 * Follow the list
 */
struct scull_qset *my_scull_follow(struct my_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;    /* Nerver 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;    /* Nerver mind */
            memset(qs->next, 0, sizeof(struct scull_qset));
        }
        qs = qs->next;
        continue;
    }
    return qs;
}
/*
 * Data management: read and write
 */ 
ssize_t my_scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    struct my_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 = my_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 my_scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct my_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 = my_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;
}

static int my_scull_init(void)
{
    int result, i;
    dev_t dev;
    printk(KERN_ALERT "my scull init\n");

/*
 * Get a range of minor numbers to work with, asking for a dynamic
 * major unless directed otherwise at load time.
 */
    if(my_scull_major) {
        dev = MKDEV(my_scull_major, my_scull_minor);
        result = register_chrdev_region(dev, my_scull_nr_devs, "my_scull");
    } else {
        result = alloc_chrdev_region(&dev, my_scull_minor, my_scull_nr_devs, "my_scull");
        my_scull_major = MAJOR(dev);
    }
    if(result < 0) {
        printk(KERN_WARNING "my scull:can't get major %d\n", my_scull_major);
        return result;
    }
    
    printk(KERN_ALERT "myscull dev:%d, major:%d, minor:%d\n", dev, my_scull_major, my_scull_minor);
    
    /*
     * allocate the device -- we can't have them static, as the number
     * can be specified at load time
     */
    scull_devices = kmalloc(my_scull_nr_devs*sizeof(struct my_scull_dev), GFP_KERNEL);
    if(!scull_devices) {
        result = -ENOMEM;
        goto fail;      /* Make this more graceful */
    }
    memset(scull_devices, 0, my_scull_nr_devs * sizeof(struct my_scull_dev));

    /* Initialize each device */
    for(i=0;i<my_scull_nr_devs;i++) {
        scull_devices[i].quantum = my_scull_quantum;
        scull_devices[i].qset = my_scull_qset;
        init_MUTEX(&scull_devices[i].sem);
        my_scull_setup_cdev(&scull_devices[i], i);
    }
    
    dev = MKDEV(my_scull_major, my_scull_minor+my_scull_nr_devs);
//    dev += my_scull_p_init(dev);
//    dev += my_scull_access_init(dev);

    return 0;           /* succees */
fail:
    my_scull_cleanup();
}

static void my_scull_cleanup(void)
{
    dev_t devno = MKDEV(my_scull_major, my_scull_minor);
    int i;

    if(scull_devices) {
        for(i=0;i<my_scull_nr_devs;i++) {
            my_scull_trim(scull_devices + i);
            cdev_del(&scull_devices[i].cdev);
        }
        kfree(scull_devices);
    }

    /* my_scull_cleanup is never called if registering failed */
    unregister_chrdev_region(devno, my_scull_nr_devs);

    printk(KERN_ALERT "my scull clean up\n");
}

module_init(my_scull_init);
module_exit(my_scull_cleanup);
my_scull.c

头文件

#ifndef __MY_SCULL_H_
#define __MY_SCULL_H_

#include <linux/ioctl.h>
#include <linux/cdev.h>

#ifndef MY_SCULL_MAJOR
#define MY_SCULL_MAJOR 0        /* dynamic major by default */
#endif

#ifndef MY_SCULL_NR_DEVS
#define MY_SCULL_NR_DEVS 4      /* scull0 through scull3 */
#endif

#ifndef MY_SCULL_QUANTUM
#define MY_SCULL_QUANTUM 4000
#endif

#ifndef MY_SCULL_QSET
#define MY_SCULL_QSET    1000
#endif


/*
 *  Representation of scull quantum sets.
 */
struct scull_qset {
    void **data;
    struct scull_qset *next;
};

struct my_scull_dev {
    struct scull_qset *data;    /* Pointer to first quantum set */
    int quantum;                /* the current quantum size */
    int qset;                   /* the current arry size */
    unsigned long size;         /* amount of data sotred here */
    unsigned int access_key;    /* used by sculluid and scullpriv */
    struct semaphore sem;       /* mutual exclusion semaphore     */
    struct cdev cdev;           /* Char device structure */
};

int my_scull_trim(struct my_scull_dev *dev);
static void my_scull_setup_cdev(struct my_scull_dev *dev, int index);
int my_scull_open(struct inode *inode, struct file *filp);
int my_scull_release(struct inode *inode, struct file *filp);
struct scull_qset *my_scull_follow(struct my_scull_dev *dev, int n);
ssize_t my_scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t my_scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
static int my_scull_init(void);
static void my_scull_cleanup(void);

#endif
my_scull.h

 

posted @ 2018-06-09 16:31  习惯就好233  阅读(211)  评论(0编辑  收藏  举报