• 设备访问控制


    1 、控制情况主要有:

    2. 独享设备——一次只允许一个用户的一个进程访问(代码参考ldd3)

    scull 的驱动程序: 需要一个原子变量atomic_t

    //scull.h
    
    #ifndef _SCULL_H_
    #define _SCULL_H_
    
    #include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */
    
    
    
    
    
    #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
    
    
    
    /*
     * The bare device is a variable-length region of memory.
     * Use a linked list of indirect blocks.
     *
     * "scull_dev->data" points to an array of pointers, each
     * pointer refers to a memory area of SCULL_QUANTUM bytes.
     *
     * The array (quantum-set) is SCULL_QSET long.
     */
    #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;
    
    
    
    
    /*
     * Prototypes for shared functions
     */
    
    
    int     scull_access_init(dev_t dev);
    void    scull_access_cleanup(void);
    
    int     scull_trim(struct scull_dev *dev);
    
    ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                       loff_t *f_pos);
    ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                        loff_t *f_pos);
    loff_t  scull_llseek(struct file *filp, loff_t off, int whence);
    int     scull_ioctl(struct inode *inode, struct file *filp,
                        unsigned int cmd, unsigned long arg);
    
    
    /*
     * Ioctl definitions
     */
    
    /* Use 'k' as magic number */
    #define SCULL_IOC_MAGIC  'k'
    /* Please use a different 8-bit number in your code */
    
    #define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)
    
    /*
     * S means "Set" through a ptr,
     * T means "Tell" directly with the argument value
     * G means "Get": reply by setting through a pointer
     * Q means "Query": response is on the return value
     * X means "eXchange": switch G and S atomically
     * H means "sHift": switch T and Q atomically
     */
    #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)
    #define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)
    #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)
    #define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)
    #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)
    #define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)
    #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)
    #define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)
    #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
    #define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)
    #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)
    #define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)
    
    /*
     * The other entities only have "Tell" and "Query", because they're
     * not printed in the book, and there's no need to have all six.
     * (The previous stuff was only there to show different ways to do it.
     */
    #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)
    #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)
    /* ... more to come */
    
    #define SCULL_IOC_MAXNR 14
    
    #endif /* _SCULL_H_ */
    View Code
    //scull.c
    //#include <linux/config.h>
    #include <linux/sched.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"
    
    
    /*
     * 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");
    
    static atomic_t scull_s_available = ATOMIC_INIT(1);
    
    struct scull_dev *scull_devices;    /* allocated in scull_init_module */
    
    
    
    /*
     * The proc filesystem: function to read and entry
     */
    
    int scull_read_procmem(char *buf, char **start, off_t offset,
                       int count, int *eof, void *data)
    {
        int i, j, len = 0;
        int limit = count - 80; /* Don't print more than this */
    
        for (i = 0; i < scull_nr_devs && len <= limit; i++) {
            struct scull_dev *d = &scull_devices[i];
            struct scull_qset *qs = d->data;
            if (down_interruptible(&d->sem))
                return -ERESTARTSYS;
            len += sprintf(buf+len,"
    Device %i: qset %i, q %i, sz %li
    ",
                    i, d->qset, d->quantum, d->size);
            for (; qs && len <= limit; qs = qs->next) { /* scan the list */
                len += sprintf(buf + len, "  item at %p, qset at %p
    ",
                        qs, qs->data);
                if (qs->data && !qs->next) /* dump only the last item */
                    for (j = 0; j < d->qset; j++) {
                        if (qs->data[j])
                            len += sprintf(buf + len,
                                    "    % 4i: %8p
    ",
                                    j, qs->data[j]);
                    }
            }
            up(&scull_devices[i].sem);
        }
        *eof = 1;
        return len;
    }
    
    
    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_s_open(struct inode *inode, struct file *filp)
    {
        struct scull_dev *dev; /* device information */
        if (! atomic_dec_and_test (&scull_s_available)){ /*if scull_s_available change to 0 then return true */
        atomic_inc(&scull_s_available); /*The first process change scull_s_available = 0 when second process come,the value will equal -1*/
        return -EBUSY;
        }
        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_s_release(struct inode *inode, struct file *filp)
    {
        atomic_inc(&scull_s_available);
        return 0;
    }
    
    
    /*
     * Data management: read and write
     */
    
    /*
     * 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;
    }
    
    
    /*
     * The "extended" operations -- only seek
     */
    
    loff_t scull_llseek(struct file *filp, loff_t off, int whence)
    {
        struct scull_dev *dev = filp->private_data;
        loff_t newpos;
    
        switch(whence) {
          case 0: /* SEEK_SET */
            newpos = off;
            break;
    
          case 1: /* SEEK_CUR */
            newpos = filp->f_pos + off;
            break;
    
          case 2: /* SEEK_END */
            newpos = dev->size + off;
            break;
    
          default: /* can't happen */
            return -EINVAL;
        }
        if (newpos < 0) return -EINVAL;
        filp->f_pos = newpos;
        return newpos;
    }
    
    
    /*
     * The ioctl() implementation
     */
    
    int scull_ioctl(struct inode *inode, struct file *filp,
                     unsigned int cmd, unsigned long arg)
    {
    
        int err = 0, tmp;
        int retval = 0;
        
        /*
         * extract the type and number bitfields, and don't decode
         * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
         */
        if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
        if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
    
        /*
         * the direction is a bitmask, and VERIFY_WRITE catches R/W
         * transfers. `Type' is user-oriented, while
         * access_ok is kernel-oriented, so the concept of "read" and
         * "write" is reversed
         */
        if (_IOC_DIR(cmd) & _IOC_READ) /*through access_ok() check the addr is legal userspace address */
            err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
        else if (_IOC_DIR(cmd) & _IOC_WRITE)
            err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
        if (err) return -EFAULT;
    
        switch(cmd) {
    
          case SCULL_IOCRESET:
            scull_quantum = SCULL_QUANTUM;
            scull_qset = SCULL_QSET;
            break;
            
          case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_quantum = arg;
            break;
    
          case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
            retval = __put_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
            return scull_quantum;
    
          case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            retval = __get_user(scull_quantum, (int __user *)arg);
            if (retval == 0)
                retval = __put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            scull_quantum = arg;
            return tmp;
            
          case SCULL_IOCSQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCTQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_qset = arg;
            break;
    
          case SCULL_IOCGQSET:
            retval = __put_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCQQSET:
            return scull_qset;
    
          case SCULL_IOCXQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            retval = __get_user(scull_qset, (int __user *)arg);
            if (retval == 0)
                retval = put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            scull_qset = arg;
            return tmp;
    
            /*
             * The following two change the buffer size for scullpipe.
             * The scullpipe device uses this same ioctl method, just to
             * write less code. Actually, it's the same driver, isn't it?
             */
    /*
          case SCULL_P_IOCTSIZE:
            scull_p_buffer = arg;
            break;
    
          case SCULL_P_IOCQSIZE:
            return scull_p_buffer;
    */
    
          default:  /* redundant, as cmd was checked against MAXNR */
            return -ENOTTY;
        }
        return retval;
    
    }
    
    
    
    struct file_operations scull_fops = { //The function of system call should obtain the semaphore to protect the sharing the resource
        .owner =    THIS_MODULE,      
        .llseek =   scull_llseek,
        .read =     scull_read,
        .write =    scull_write,
        .ioctl =    scull_ioctl,
        .open =     scull_s_open,
        .release =  scull_s_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);
        }
    
    #ifdef SCULL_DEBUG /* use proc only if debugging */
        scull_remove_proc();
    #endif
        remove_proc_entry("scullmem", NULL /* parent dir */);
    
        /* cleanup_module is never called if registering failed */
        unregister_chrdev_region(devno, scull_nr_devs);
    
        /* and call the cleanup functions for friend devices */
    
    
    }
    
    /*
     * 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
    ", 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);    //The semaphore should be initialise before the scull device could be used 
            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);
    /*debug*/
        create_proc_read_entry("scullmem", 0 /* default mode */,
                NULL /* parent dir */, scull_read_procmem,
                NULL /* client data */);
    
    
        return 0; /* succeed */
    
      fail:
        scull_cleanup_module();
        return result;
    }
    
    module_init(scull_init_module);
    module_exit(scull_cleanup_module);
    View Code

    测试程序

    //test.c
    #include <stdio.h>
    #include <sys/types.h> 
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <unistd.h> //_exit
    #include <stdlib.h> // exit
    int main(int argc,char **argv)
    {
        int fd,size;
        char s[] = "Hello World";
        char buffer[80] = {""};
        if((fd = open("/dev/scull0",O_RDWR)) < 0 ){
            printf("errno = %d
    ",errno);
            exit(0);
        }
        size = write(fd,s,sizeof(s));
        printf("write in %d bytes
    ",size);
        close(fd);
        fd = open("/dev/scull0",O_RDWR);
        size = read(fd,buffer,sizeof(buffer));
        printf("read out %d bytes
    ",size);
        printf("%s
    ",buffer);
        sleep(100);
        close(fd);
        return 0;
    
    }
    View Code

    3. 允许一个用户访问多个线程访问(终端设备访问控制)

    scull 的驱动程序: 需要两个数据项--打开计数和设备宿主UID

    //scull.h
    #ifndef _SCULL_H_
    #define _SCULL_H_
    
    #include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */
    
    
    
    
    
    #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
    
    
    
    /*
     * The bare device is a variable-length region of memory.
     * Use a linked list of indirect blocks.
     *
     * "scull_dev->data" points to an array of pointers, each
     * pointer refers to a memory area of SCULL_QUANTUM bytes.
     *
     * The array (quantum-set) is SCULL_QSET long.
     */
    #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     */
        spinlock_t scull_u_lock;
        int scull_u_count;
        uid_t scull_u_owner;
        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;
    
    
    
    
    /*
     * Prototypes for shared functions
     */
    
    
    int     scull_access_init(dev_t dev);
    void    scull_access_cleanup(void);
    
    int     scull_trim(struct scull_dev *dev);
    
    ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                       loff_t *f_pos);
    ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                        loff_t *f_pos);
    loff_t  scull_llseek(struct file *filp, loff_t off, int whence);
    int     scull_ioctl(struct inode *inode, struct file *filp,
                        unsigned int cmd, unsigned long arg);
    
    
    /*
     * Ioctl definitions
     */
    
    /* Use 'k' as magic number */
    #define SCULL_IOC_MAGIC  'k'
    /* Please use a different 8-bit number in your code */
    
    #define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)
    
    /*
     * S means "Set" through a ptr,
     * T means "Tell" directly with the argument value
     * G means "Get": reply by setting through a pointer
     * Q means "Query": response is on the return value
     * X means "eXchange": switch G and S atomically
     * H means "sHift": switch T and Q atomically
     */
    #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)
    #define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)
    #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)
    #define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)
    #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)
    #define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)
    #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)
    #define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)
    #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
    #define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)
    #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)
    #define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)
    
    /*
     * The other entities only have "Tell" and "Query", because they're
     * not printed in the book, and there's no need to have all six.
     * (The previous stuff was only there to show different ways to do it.
     */
    #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)
    #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)
    /* ... more to come */
    
    #define SCULL_IOC_MAXNR 14
    
    #endif /* _SCULL_H_ */
    View Code
    //scull.c
    //#include <linux/config.h>
    #include <linux/sched.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 <linux/spinlock.h>
    #include "scull.h"
    
    
    /*
     * 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 */
    
    
    
    /*
     * The proc filesystem: function to read and entry
     */
    
    int scull_read_procmem(char *buf, char **start, off_t offset,
                       int count, int *eof, void *data)
    {
        int i, j, len = 0;
        int limit = count - 80; /* Don't print more than this */
    
        for (i = 0; i < scull_nr_devs && len <= limit; i++) {
            struct scull_dev *d = &scull_devices[i];
            struct scull_qset *qs = d->data;
            if (down_interruptible(&d->sem))
                return -ERESTARTSYS;
            len += sprintf(buf+len,"
    Device %i: qset %i, q %i, sz %li
    ",
                    i, d->qset, d->quantum, d->size);
            for (; qs && len <= limit; qs = qs->next) { /* scan the list */
                len += sprintf(buf + len, "  item at %p, qset at %p
    ",
                        qs, qs->data);
                if (qs->data && !qs->next) /* dump only the last item */
                    for (j = 0; j < d->qset; j++) {
                        if (qs->data[j])
                            len += sprintf(buf + len,
                                    "    % 4i: %8p
    ",
                                    j, qs->data[j]);
                    }
            }
            up(&scull_devices[i].sem);
        }
        *eof = 1;
        return len;
    }
    
    
    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_u_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 */
        printk(KERN_DEBUG "coming here uid error12
    ");
        spin_lock(&dev->scull_u_lock);
        
        if (dev->scull_u_count == 0){
            dev->scull_u_owner = current->uid;
            dev->scull_u_count++;
        }
        /*
        if (dev->scull_u_count && (dev->scull_u_owner != current->uid) && (dev->scull_u_owner != current->euid) && !capable(CAP_DAC_OVERRIDE) ){
            spin_unlock(&dev->scull_u_lock);
            printk(KERN_DEBUG "coming here uid error2
    ");    
            return -EBUSY;
        }
        spin_unlock(&dev->scull_u_lock);
        */
        if ((dev->scull_u_owner != current->uid) ) {
            spin_unlock(&dev->scull_u_lock);
            printk(KERN_DEBUG "coming here uid error22
    ");    
            return -EBUSY;
        }
        spin_unlock(&dev->scull_u_lock);    
        
        /* 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_u_release(struct inode *inode, struct file *filp)
    {
        struct scull_dev *dev = filp->private_data;
        printk(KERN_DEBUG "coming here release
    ");    
        spin_lock(&dev->scull_u_lock);
        dev->scull_u_count--;
        spin_unlock(&dev->scull_u_lock);
        return 0;
    }
    
    
    /*
     * Data management: read and write
     */
    
    /*
     * 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;
    }
    
    
    /*
     * The "extended" operations -- only seek
     */
    
    loff_t scull_llseek(struct file *filp, loff_t off, int whence)
    {
        struct scull_dev *dev = filp->private_data;
        loff_t newpos;
    
        switch(whence) {
          case 0: /* SEEK_SET */
            newpos = off;
            break;
    
          case 1: /* SEEK_CUR */
            newpos = filp->f_pos + off;
            break;
    
          case 2: /* SEEK_END */
            newpos = dev->size + off;
            break;
    
          default: /* can't happen */
            return -EINVAL;
        }
        if (newpos < 0) return -EINVAL;
        filp->f_pos = newpos;
        return newpos;
    }
    
    
    /*
     * The ioctl() implementation
     */
    
    int scull_ioctl(struct inode *inode, struct file *filp,
                     unsigned int cmd, unsigned long arg)
    {
    
        int err = 0, tmp;
        int retval = 0;
        
        /*
         * extract the type and number bitfields, and don't decode
         * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
         */
        if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
        if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
    
        /*
         * the direction is a bitmask, and VERIFY_WRITE catches R/W
         * transfers. `Type' is user-oriented, while
         * access_ok is kernel-oriented, so the concept of "read" and
         * "write" is reversed
         */
        if (_IOC_DIR(cmd) & _IOC_READ) /*through access_ok() check the addr is legal userspace address */
            err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
        else if (_IOC_DIR(cmd) & _IOC_WRITE)
            err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
        if (err) return -EFAULT;
    
        switch(cmd) {
    
          case SCULL_IOCRESET:
            scull_quantum = SCULL_QUANTUM;
            scull_qset = SCULL_QSET;
            break;
            
          case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_quantum = arg;
            break;
    
          case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
            retval = __put_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
            return scull_quantum;
    
          case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            retval = __get_user(scull_quantum, (int __user *)arg);
            if (retval == 0)
                retval = __put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            scull_quantum = arg;
            return tmp;
            
          case SCULL_IOCSQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCTQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_qset = arg;
            break;
    
          case SCULL_IOCGQSET:
            retval = __put_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCQQSET:
            return scull_qset;
    
          case SCULL_IOCXQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            retval = __get_user(scull_qset, (int __user *)arg);
            if (retval == 0)
                retval = put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            scull_qset = arg;
            return tmp;
    
            /*
             * The following two change the buffer size for scullpipe.
             * The scullpipe device uses this same ioctl method, just to
             * write less code. Actually, it's the same driver, isn't it?
             */
    /*
          case SCULL_P_IOCTSIZE:
            scull_p_buffer = arg;
            break;
    
          case SCULL_P_IOCQSIZE:
            return scull_p_buffer;
    */
    
          default:  /* redundant, as cmd was checked against MAXNR */
            return -ENOTTY;
        }
        return retval;
    
    }
    
    
    
    struct file_operations scull_fops = { //The function of system call should obtain the semaphore to protect the sharing the resource
        .owner =    THIS_MODULE,      
        .llseek =   scull_llseek,
        .read =     scull_read,
        .write =    scull_write,
        .ioctl =    scull_ioctl,
        .open =     scull_u_open,
        .release =  scull_u_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);
        }
    
    #ifdef SCULL_DEBUG /* use proc only if debugging */
        scull_remove_proc();
    #endif
        remove_proc_entry("scullmem", NULL /* parent dir */);
    
        /* cleanup_module is never called if registering failed */
        unregister_chrdev_region(devno, scull_nr_devs);
    
        /* and call the cleanup functions for friend devices */
    
    
    }
    
    /*
     * 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
    ", 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;
            spin_lock_init(&scull_devices[i].scull_u_lock);
            scull_devices[i].scull_u_count = 0;
            init_MUTEX(&scull_devices[i].sem);    //The semaphore should be initialise before the scull device could be used 
            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);
    /*debug*/
        create_proc_read_entry("scullmem", 0 /* default mode */,
                NULL /* parent dir */, scull_read_procmem,
                NULL /* client data */);
    
    
        return 0; /* succeed */
    
      fail:
        scull_cleanup_module();
        return result;
    }
    
    module_init(scull_init_module);
    module_exit(scull_cleanup_module);
    View Code

    测试程序

    //test.c
    #include <stdio.h>
    #include <sys/types.h> 
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <unistd.h> //_exit
    #include <stdlib.h> // exit
    int main(int argc,char **argv)
    {
        int fd,size;
        char s[] = "Hello World";
        char buffer[80] = {""};
        if((fd = open("/dev/scull0",O_RDWR)) < 0 ){
            printf("errno = %d
    ",errno);
            exit(0);
        }
        size = write(fd,s,sizeof(s));
        printf("write in %d bytes
    ",size);
        close(fd);
        fd = open("/dev/scull0",O_RDWR);
        size = read(fd,buffer,sizeof(buffer));
        printf("read out %d bytes
    ",size);
        printf("%s
    ",buffer);
        sleep(100);
        close(fd);
        
        
        return 0;
    
    }
    View Code

    分析: 在用户空间只能有一个用户运行 test 程序成功,并且可以多次运行,但不能两个用户同时运行。

     4. 替代EBUSY 的阻塞型open

    驱动程序: 当设备不能访问时阻塞进程,而不是返回错误退出。

    //scull.h
    #ifndef _SCULL_H_
    #define _SCULL_H_
    
    #include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */
    
    
    
    
    
    #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
    
    
    
    /*
     * The bare device is a variable-length region of memory.
     * Use a linked list of indirect blocks.
     *
     * "scull_dev->data" points to an array of pointers, each
     * pointer refers to a memory area of SCULL_QUANTUM bytes.
     *
     * The array (quantum-set) is SCULL_QSET long.
     */
    #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     */
        spinlock_t scull_u_lock;
        int scull_u_count;
        uid_t scull_u_owner;
        wait_queue_head_t scull_w_wait;
        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;
    
    
    
    
    /*
     * Prototypes for shared functions
     */
    
    
    int     scull_access_init(dev_t dev);
    void    scull_access_cleanup(void);
    
    int     scull_trim(struct scull_dev *dev);
    
    ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                       loff_t *f_pos);
    ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                        loff_t *f_pos);
    loff_t  scull_llseek(struct file *filp, loff_t off, int whence);
    int     scull_ioctl(struct inode *inode, struct file *filp,
                        unsigned int cmd, unsigned long arg);
    
    
    /*
     * Ioctl definitions
     */
    
    /* Use 'k' as magic number */
    #define SCULL_IOC_MAGIC  'k'
    /* Please use a different 8-bit number in your code */
    
    #define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)
    
    /*
     * S means "Set" through a ptr,
     * T means "Tell" directly with the argument value
     * G means "Get": reply by setting through a pointer
     * Q means "Query": response is on the return value
     * X means "eXchange": switch G and S atomically
     * H means "sHift": switch T and Q atomically
     */
    #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)
    #define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)
    #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)
    #define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)
    #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)
    #define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)
    #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)
    #define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)
    #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
    #define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)
    #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)
    #define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)
    
    /*
     * The other entities only have "Tell" and "Query", because they're
     * not printed in the book, and there's no need to have all six.
     * (The previous stuff was only there to show different ways to do it.
     */
    #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)
    #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)
    /* ... more to come */
    
    #define SCULL_IOC_MAXNR 14
    
    #endif /* _SCULL_H_ */
    View Code
    //scull.c
    //#include <linux/config.h>
    #include <linux/sched.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 <linux/spinlock.h>
    #include "scull.h"
    
    
    /*
     * 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 */
    
    
    
    /*
     * The proc filesystem: function to read and entry
     */
    
    int scull_read_procmem(char *buf, char **start, off_t offset,
                       int count, int *eof, void *data)
    {
        int i, j, len = 0;
        int limit = count - 80; /* Don't print more than this */
    
        for (i = 0; i < scull_nr_devs && len <= limit; i++) {
            struct scull_dev *d = &scull_devices[i];
            struct scull_qset *qs = d->data;
            if (down_interruptible(&d->sem))
                return -ERESTARTSYS;
            len += sprintf(buf+len,"
    Device %i: qset %i, q %i, sz %li
    ",
                    i, d->qset, d->quantum, d->size);
            for (; qs && len <= limit; qs = qs->next) { /* scan the list */
                len += sprintf(buf + len, "  item at %p, qset at %p
    ",
                        qs, qs->data);
                if (qs->data && !qs->next) /* dump only the last item */
                    for (j = 0; j < d->qset; j++) {
                        if (qs->data[j])
                            len += sprintf(buf + len,
                                    "    % 4i: %8p
    ",
                                    j, qs->data[j]);
                    }
            }
            up(&scull_devices[i].sem);
        }
        *eof = 1;
        return len;
    }
    
    
    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_u_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 */
        printk(KERN_DEBUG "coming here uid error12
    ");
        spin_lock(&dev->scull_u_lock);
        
        if (dev->scull_u_count == 0){
            dev->scull_u_owner = current->uid;
            dev->scull_u_count++;
        }
        /*
        if (dev->scull_u_count && (dev->scull_u_owner != current->uid) && (dev->scull_u_owner != current->euid) && !capable(CAP_DAC_OVERRIDE) ){
            spin_unlock(&dev->scull_u_lock);
            printk(KERN_DEBUG "coming here uid error2
    ");    
            return -EBUSY;
        }
        spin_unlock(&dev->scull_u_lock);
        */
        /*
        if ((dev->scull_u_owner != current->uid) ) {
            spin_unlock(&dev->scull_u_lock);
            printk(KERN_DEBUG "coming here uid error22
    ");    
            return -EBUSY;
        }
        spin_unlock(&dev->scull_u_lock);
        */
        
        while (dev->scull_u_owner != current->uid)  {
            spin_unlock(&dev->scull_u_lock);
            printk(KERN_DEBUG "coming here uid error22
    ");    
            if (filp->f_flags & O_NONBLOCK)
                return -EAGAIN;
            printk(KERN_DEBUG "coming here front wait
    ");    
            if (wait_event_interruptible(dev->scull_w_wait,dev->scull_u_count==0))/*the condition need to deeply think*/
            /*if the condition is (dev->scull_u_owner != current->uid) the program won't be run properly*/
                return -ERESTARTSYS;
            if (dev->scull_u_count == 0){
            dev->scull_u_owner = current->uid;
            dev->scull_u_count++;
            }    
            printk(KERN_DEBUG "coming here back wait
    ");
            spin_lock(&dev->scull_u_lock);        
        }
        spin_unlock(&dev->scull_u_lock);        
        
        /* 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_u_release(struct inode *inode, struct file *filp)
    {
        struct scull_dev *dev = filp->private_data;
        int temp;
        printk(KERN_DEBUG "coming here release
    ");    
        spin_lock(&dev->scull_u_lock);
        dev->scull_u_count--;
        temp = dev->scull_u_count;
        spin_unlock(&dev->scull_u_lock);
        if (temp == 0)
            wake_up_interruptible_sync(&dev->scull_w_wait);
        return 0;
    }
    
    
    /*
     * Data management: read and write
     */
    
    /*
     * 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;
    }
    
    
    /*
     * The "extended" operations -- only seek
     */
    
    loff_t scull_llseek(struct file *filp, loff_t off, int whence)
    {
        struct scull_dev *dev = filp->private_data;
        loff_t newpos;
    
        switch(whence) {
          case 0: /* SEEK_SET */
            newpos = off;
            break;
    
          case 1: /* SEEK_CUR */
            newpos = filp->f_pos + off;
            break;
    
          case 2: /* SEEK_END */
            newpos = dev->size + off;
            break;
    
          default: /* can't happen */
            return -EINVAL;
        }
        if (newpos < 0) return -EINVAL;
        filp->f_pos = newpos;
        return newpos;
    }
    
    
    /*
     * The ioctl() implementation
     */
    
    int scull_ioctl(struct inode *inode, struct file *filp,
                     unsigned int cmd, unsigned long arg)
    {
    
        int err = 0, tmp;
        int retval = 0;
        
        /*
         * extract the type and number bitfields, and don't decode
         * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
         */
        if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
        if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
    
        /*
         * the direction is a bitmask, and VERIFY_WRITE catches R/W
         * transfers. `Type' is user-oriented, while
         * access_ok is kernel-oriented, so the concept of "read" and
         * "write" is reversed
         */
        if (_IOC_DIR(cmd) & _IOC_READ) /*through access_ok() check the addr is legal userspace address */
            err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
        else if (_IOC_DIR(cmd) & _IOC_WRITE)
            err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
        if (err) return -EFAULT;
    
        switch(cmd) {
    
          case SCULL_IOCRESET:
            scull_quantum = SCULL_QUANTUM;
            scull_qset = SCULL_QSET;
            break;
            
          case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_quantum = arg;
            break;
    
          case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
            retval = __put_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
            return scull_quantum;
    
          case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            retval = __get_user(scull_quantum, (int __user *)arg);
            if (retval == 0)
                retval = __put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            scull_quantum = arg;
            return tmp;
            
          case SCULL_IOCSQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCTQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_qset = arg;
            break;
    
          case SCULL_IOCGQSET:
            retval = __put_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCQQSET:
            return scull_qset;
    
          case SCULL_IOCXQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            retval = __get_user(scull_qset, (int __user *)arg);
            if (retval == 0)
                retval = put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            scull_qset = arg;
            return tmp;
    
            /*
             * The following two change the buffer size for scullpipe.
             * The scullpipe device uses this same ioctl method, just to
             * write less code. Actually, it's the same driver, isn't it?
             */
    /*
          case SCULL_P_IOCTSIZE:
            scull_p_buffer = arg;
            break;
    
          case SCULL_P_IOCQSIZE:
            return scull_p_buffer;
    */
    
          default:  /* redundant, as cmd was checked against MAXNR */
            return -ENOTTY;
        }
        return retval;
    
    }
    
    
    
    struct file_operations scull_fops = { //The function of system call should obtain the semaphore to protect the sharing the resource
        .owner =    THIS_MODULE,      
        .llseek =   scull_llseek,
        .read =     scull_read,
        .write =    scull_write,
        .ioctl =    scull_ioctl,
        .open =     scull_u_open,
        .release =  scull_u_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);
        }
    
    #ifdef SCULL_DEBUG /* use proc only if debugging */
        scull_remove_proc();
    #endif
        remove_proc_entry("scullmem", NULL /* parent dir */);
    
        /* cleanup_module is never called if registering failed */
        unregister_chrdev_region(devno, scull_nr_devs);
    
        /* and call the cleanup functions for friend devices */
    
    
    }
    
    /*
     * 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
    ", 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_waitqueue_head(&scull_devices[i].scull_w_wait);
            spin_lock_init(&scull_devices[i].scull_u_lock);
            scull_devices[i].scull_u_count = 0;
            init_MUTEX(&scull_devices[i].sem);    //The semaphore should be initialise before the scull device could be used 
            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);
    /*debug*/
        create_proc_read_entry("scullmem", 0 /* default mode */,
                NULL /* parent dir */, scull_read_procmem,
                NULL /* client data */);
    
    
        return 0; /* succeed */
    
      fail:
        scull_cleanup_module();
        return result;
    }
    
    module_init(scull_init_module);
    module_exit(scull_cleanup_module);
    View Code

    测试程序:

    //test.c
    #include <stdio.h>
    #include <sys/types.h> 
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <unistd.h> //_exit
    #include <stdlib.h> // exit
    int main(int argc,char **argv)
    {
        int fd,size;
        char s[] = "Hello World";
        char buffer[80] = {""};
        if((fd = open("/dev/scull0",O_RDWR)) < 0 ){
            printf("errno = %d
    ",errno);
            exit(0);
        }
        size = write(fd,s,sizeof(s));
        printf("write in %d bytes
    ",size);
        sleep(15);
        close(fd);
        
        
        
        
        
        return 0;
    
    }
    View Code

    分析:当有两个用户同时访问时,其中一个将无法访问,但此时不是返回错误退出,而是等待设备可以使用。难点是对休眠条件的设置。

    5. 打开时复制设备

    驱动:打开时建立的设备仅有设备资源可操作,也即没有复制设备设施,不是复制设备全部。

    //scull.h
    #ifndef _SCULL_H_
    #define _SCULL_H_
    
    #include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */
    
    
    
    
    
    #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
    
    
    
    /*
     * The bare device is a variable-length region of memory.
     * Use a linked list of indirect blocks.
     *
     * "scull_dev->data" points to an array of pointers, each
     * pointer refers to a memory area of SCULL_QUANTUM bytes.
     *
     * The array (quantum-set) is SCULL_QSET long.
     */
    #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;
    
    
    
    
    /*
     * Prototypes for shared functions
     */
    
    
    int     scull_access_init(dev_t dev);
    void    scull_access_cleanup(void);
    
    int     scull_trim(struct scull_dev *dev);
    
    ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                       loff_t *f_pos);
    ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                        loff_t *f_pos);
    loff_t  scull_llseek(struct file *filp, loff_t off, int whence);
    int     scull_ioctl(struct inode *inode, struct file *filp,
                        unsigned int cmd, unsigned long arg);
    
    
    /*
     * Ioctl definitions
     */
    
    /* Use 'k' as magic number */
    #define SCULL_IOC_MAGIC  'k'
    /* Please use a different 8-bit number in your code */
    
    #define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)
    
    /*
     * S means "Set" through a ptr,
     * T means "Tell" directly with the argument value
     * G means "Get": reply by setting through a pointer
     * Q means "Query": response is on the return value
     * X means "eXchange": switch G and S atomically
     * H means "sHift": switch T and Q atomically
     */
    #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)
    #define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)
    #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)
    #define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)
    #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)
    #define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)
    #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)
    #define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)
    #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
    #define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)
    #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)
    #define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)
    
    /*
     * The other entities only have "Tell" and "Query", because they're
     * not printed in the book, and there's no need to have all six.
     * (The previous stuff was only there to show different ways to do it.
     */
    #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)
    #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)
    /* ... more to come */
    
    #define SCULL_IOC_MAXNR 14
    
    #endif /* _SCULL_H_ */
    View Code
    //scull.c
    //#include <linux/config.h>
    #include <linux/sched.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 <linux/list.h>
    #include <linux/tty.h>
    
    #include <asm/system.h>        /* cli(), *_flags */
    #include <asm/uaccess.h>    /* copy_*_user */
    #include "scull.h"
    
    
    /*
     * 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 */
    
    
    
    /*
     * The proc filesystem: function to read and entry
     */
    
    int scull_read_procmem(char *buf, char **start, off_t offset,
                       int count, int *eof, void *data)
    {
        int i, j, len = 0;
        int limit = count - 80; /* Don't print more than this */
    
        for (i = 0; i < scull_nr_devs && len <= limit; i++) {
            struct scull_dev *d = &scull_devices[i];
            struct scull_qset *qs = d->data;
            if (down_interruptible(&d->sem))
                return -ERESTARTSYS;
            len += sprintf(buf+len,"
    Device %i: qset %i, q %i, sz %li
    ",
                    i, d->qset, d->quantum, d->size);
            for (; qs && len <= limit; qs = qs->next) { /* scan the list */
                len += sprintf(buf + len, "  item at %p, qset at %p
    ",
                        qs, qs->data);
                if (qs->data && !qs->next) /* dump only the last item */
                    for (j = 0; j < d->qset; j++) {
                        if (qs->data[j])
                            len += sprintf(buf + len,
                                    "    % 4i: %8p
    ",
                                    j, qs->data[j]);
                    }
            }
            up(&scull_devices[i].sem);
        }
        *eof = 1;
        return len;
    }
    
    
    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;
    }
    
    
    struct scull_listitem {
        struct scull_dev device;
        dev_t key;
        struct list_head list;
        
    };                /*list struct*/
    
    /* The list of devices, and a lock to protect it */
    static LIST_HEAD(scull_c_list);
    static spinlock_t scull_c_lock = SPIN_LOCK_UNLOCKED;
    
    /* A placeholder scull_dev which really just holds the cdev stuff. */
    //static struct scull_dev scull_c_device;   
    
    /* Look for a device or create one if missing */
    static struct scull_dev *scull_c_lookfor_device(dev_t key)
    {
        struct scull_listitem *lptr;
    
        list_for_each_entry(lptr, &scull_c_list, list) {
            if (lptr->key == key)
                return &(lptr->device);
        }
        printk(KERN_DEBUG "The key %d
    ",key);
    
        /* not found */
        lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL);
        if (!lptr)
            return NULL;
    
        /* initialize the device */
        memset(lptr, 0, sizeof(struct scull_listitem));
        lptr->key = key;
        scull_trim(&(lptr->device)); /* initialize it */
        init_MUTEX(&(lptr->device.sem));
    
        /* place it in the list */
        list_add(&lptr->list, &scull_c_list);
    
        return &(lptr->device);
    }
    
    static int scull_c_open(struct inode *inode, struct file *filp)
    {
        struct scull_dev *dev;
        dev_t key;
         printk(KERN_DEBUG "coming here
    ");
        if (!current->signal->tty) { 
    //        PDEBUG("Process "%s" has no ctl tty
    ", current->comm);
            return -EINVAL;
        }
        key = tty_devnum(current->signal->tty);
    
        /* look for a scullc device in the list */
        spin_lock(&scull_c_lock);
        dev = scull_c_lookfor_device(key);
        spin_unlock(&scull_c_lock);
    
        if (!dev)
            return -ENOMEM;
    
        /* then, everything else is copied from the bare scull device */
        if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
            scull_trim(dev);
        filp->private_data = dev;
        return 0;          /* success */
    }
    
    static int scull_c_release(struct inode *inode, struct file *filp)
    {
        /*
         * Nothing to do, because the device is persistent.
         * A `real' cloned device should be freed on last close
         */
        return 0;
    }
    
    
    
    /*
     * Data management: read and write
     */
    
    /*
     * 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;
    }
    
    
    /*
     * The "extended" operations -- only seek
     */
    
    loff_t scull_llseek(struct file *filp, loff_t off, int whence)
    {
        struct scull_dev *dev = filp->private_data;
        loff_t newpos;
    
        switch(whence) {
          case 0: /* SEEK_SET */
            newpos = off;
            break;
    
          case 1: /* SEEK_CUR */
            newpos = filp->f_pos + off;
            break;
    
          case 2: /* SEEK_END */
            newpos = dev->size + off;
            break;
    
          default: /* can't happen */
            return -EINVAL;
        }
        if (newpos < 0) return -EINVAL;
        filp->f_pos = newpos;
        return newpos;
    }
    
    
    /*
     * The ioctl() implementation
     */
    
    int scull_ioctl(struct inode *inode, struct file *filp,
                     unsigned int cmd, unsigned long arg)
    {
    
        int err = 0, tmp;
        int retval = 0;
        
        /*
         * extract the type and number bitfields, and don't decode
         * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
         */
        if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
        if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
    
        /*
         * the direction is a bitmask, and VERIFY_WRITE catches R/W
         * transfers. `Type' is user-oriented, while
         * access_ok is kernel-oriented, so the concept of "read" and
         * "write" is reversed
         */
        if (_IOC_DIR(cmd) & _IOC_READ) /*through access_ok() check the addr is legal userspace address */
            err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
        else if (_IOC_DIR(cmd) & _IOC_WRITE)
            err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
        if (err) return -EFAULT;
    
        switch(cmd) {
    
          case SCULL_IOCRESET:
            scull_quantum = SCULL_QUANTUM;
            scull_qset = SCULL_QSET;
            break;
            
          case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_quantum = arg;
            break;
    
          case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
            retval = __put_user(scull_quantum, (int __user *)arg);
            break;
    
          case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
            return scull_quantum;
    
          case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            retval = __get_user(scull_quantum, (int __user *)arg);
            if (retval == 0)
                retval = __put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_quantum;
            scull_quantum = arg;
            return tmp;
            
          case SCULL_IOCSQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            retval = __get_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCTQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            scull_qset = arg;
            break;
    
          case SCULL_IOCGQSET:
            retval = __put_user(scull_qset, (int __user *)arg);
            break;
    
          case SCULL_IOCQQSET:
            return scull_qset;
    
          case SCULL_IOCXQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            retval = __get_user(scull_qset, (int __user *)arg);
            if (retval == 0)
                retval = put_user(tmp, (int __user *)arg);
            break;
    
          case SCULL_IOCHQSET:
            if (! capable (CAP_SYS_ADMIN))
                return -EPERM;
            tmp = scull_qset;
            scull_qset = arg;
            return tmp;
    
            /*
             * The following two change the buffer size for scullpipe.
             * The scullpipe device uses this same ioctl method, just to
             * write less code. Actually, it's the same driver, isn't it?
             */
    /*
          case SCULL_P_IOCTSIZE:
            scull_p_buffer = arg;
            break;
    
          case SCULL_P_IOCQSIZE:
            return scull_p_buffer;
    */
    
          default:  /* redundant, as cmd was checked against MAXNR */
            return -ENOTTY;
        }
        return retval;
    
    }
    
    
    
    struct file_operations scull_fops = { //The function of system call should obtain the semaphore to protect the sharing the resource
        .owner =    THIS_MODULE,      
        .llseek =   scull_llseek,
        .read =     scull_read,
        .write =    scull_write,
        .ioctl =    scull_ioctl,
        .open =     scull_c_open,
        .release =  scull_c_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);
        }
    
    #ifdef SCULL_DEBUG /* use proc only if debugging */
        scull_remove_proc();
    #endif
        remove_proc_entry("scullmem", NULL /* parent dir */);
    
        /* cleanup_module is never called if registering failed */
        unregister_chrdev_region(devno, scull_nr_devs);
    
        /* and call the cleanup functions for friend devices */
    
    
    }
    
    /*
     * 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
    ", 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);    //The semaphore should be initialise before the scull device could be used 
            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);
    /*debug*/
        create_proc_read_entry("scullmem", 0 /* default mode */,
                NULL /* parent dir */, scull_read_procmem,
                NULL /* client data */);
    
    
        return 0; /* succeed */
    
      fail:
        scull_cleanup_module();
        return result;
    }
    
    module_init(scull_init_module);
    module_exit(scull_cleanup_module);
    View Code

    测试程序:

    //test.c
    #include <stdio.h>
    #include <sys/types.h> 
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <unistd.h> //_exit
    #include <stdlib.h> // exit
    int main(int argc,char **argv)
    {
        int fd,size;
        char s[] = "Hello World";
        char buffer[80] = {""};
        if((fd = open("/dev/scull0",O_RDWR)) < 0 ){
            printf("errno = %d
    ",errno);
            exit(0);
        }
        size = write(fd,s,sizeof(s));
        printf("write in %d bytes
    ",size);
        close(fd);
        fd = open("/dev/scull0",O_RDWR);
        size = read(fd,buffer,sizeof(buffer));
        printf("read out %d bytes
    ",size);
        printf("%s
    ",buffer);
        sleep(10);
        close(fd);
        return 0;
    
    }
    View Code

    分析:对于不同得进程终端得到不同得设备资源进行操作,上面的驱动由于设备键值是全局变量,故在输出的key 中只会看到新建时一次,驱动不卸载,将一直保持存在。

  • 相关阅读:
    醒醒吧少年,只用Cucumber不能帮助你BDD
    【Python3 爬虫】06_robots.txt查看网站爬取限制情况
    【Python3 爬虫】05_安装Scrapy
    【基尼系数】基尼系数的计算方法与计算案例
    【Python3 爬虫】04_urllib.request.urlretrieve
    【Python3 爬虫】03_urllib.error异常处理
    【Python3 爬虫】02_利用urllib.urlopen向百度翻译发送数据并返回结果
    【Python】help与dir的用法
    在线激活Pycharm(亲测有效)
    【Python3 爬虫】01_简单页面抓取
  • 原文地址:https://www.cnblogs.com/youngvoice/p/4853058.html
Copyright © 2020-2023  润新知