+ All Categories
Home > Documents > 8_Character Device Drivers

8_Character Device Drivers

Date post: 05-Apr-2018
Category:
Upload: venkipinki
View: 222 times
Download: 0 times
Share this document with a friend

of 44

Transcript
  • 7/31/2019 8_Character Device Drivers

    1/44

    Character Device Drivers

    CSIE, NCKU

    The information on the slides are fromLinux Device Drivers, Third Edition, by Jonathan Corbet, Alessandro Rubini, and GregKroah-Hartman. Copyright 2005 OReilly Media, Inc., 0-596-00590-3.

  • 7/31/2019 8_Character Device Drivers

    2/44

    The Scull Char Driver

    We present code fragments of a chardevice driver: scull

    acts on a memory area, not traditional IO

    devices Hardware independent

    Portable

    Can be a template

    But, only shows the kernel interface

    The device interface is not shown

  • 7/31/2019 8_Character Device Drivers

    3/44

    The Scull Char Driver

    Four devices implemented by scull scull0~3

    each device

    Contains a global and persistent memory area

    Global

    Data of an area can be shared by multiple users

    Persistent Data remains even when the device is closed

  • 7/31/2019 8_Character Device Drivers

    4/44

    Major and Minor Numbers

    Char devices are accessed through devicefiles in the filesystem

    Device files, Special files, or Nodes

    Located in the/dev directory

    Char devices Major Minor Device name

  • 7/31/2019 8_Character Device Drivers

    5/44

    Major and Minor Numbers

    Why we need major & minor numbers? For identifying a device

    Major: the device type

    Minor: the device number in that type

    Traditionally, each major number is managed by

    a driver

    kernel seldom handles minor numbers But Linux allows multiple drivers share the same

    major number

  • 7/31/2019 8_Character Device Drivers

    6/44

    Internal Representation of Device

    Numbers

    dev_t is used to hold a device number Major (12bits) , minor (20bits) for kernel 2.6.0

    The real representation can be changed.So,

    Use the following macros

    MAJOR(dev_t dev);

    MINOR(dev_t dev);

    MKDEV(int major, int minor);

  • 7/31/2019 8_Character Device Drivers

    7/44

    Allocating and Freeing Device

    Numbers

    Allocating a range of device numbers to manage

    If you know the major number

    register_chrdev_region

    Traditionally, major numbers are statically assigned

    You can pick an unused number in

    Documentation/devices.txt

    Major number conflict may happen when you deploy your driver

    If you want the kernel to give you a major numberdynamically

    alloc_chrdev_region

  • 7/31/2019 8_Character Device Drivers

    8/44

    Allocating and Freeing Device

    Numbers

    int register_chrdev_region(dev_t first,unsigned int count, char *name);

    first: the first dev_t

    Include major + first minor

    count: total number of contiguous device

    numbers you are requesting

    name: the name of the device

  • 7/31/2019 8_Character Device Drivers

    9/44

    Allocating and Freeing Device

    Numbers

    int alloc_chrdev_region(dev_t *dev,unsigned int firstminor, unsigned int count,

    char *name);

    dev: output parameter

    The dev_t indicating the (major, first minor) is

    setup when the function successfully returns

    Ch e ck / p r o c/ d e vice s o r s ys fs a ft e r t h e in vo ca t io n o f t h e

    a b ove tw o fu n ct ion s

  • 7/31/2019 8_Character Device Drivers

    10/44

    An Example of /proc/devices

    Ch a r a c t e r d e vic e s :

    1 m em

    2 p t y

    3 t t yp

    4 t tyS6 lp

    7 vcs

    10 m is c

    13 i n pu t

    14 s o u n d

    2 1 sg

    18 0 u s b

    Block d evices :

    2 fd

    8 sd

    11 s r

    65 sd6 6 s d

  • 7/31/2019 8_Character Device Drivers

    11/44

    Allocating and Freeing Device

    Numbers

    Free the device numbers void unregister_chrdev_region(dev_t first,

    unsigned int count);

  • 7/31/2019 8_Character Device Drivers

    12/44

    Dynamic Allocation of Major

    Numbers

    More flexible, no conflict However, the device files can not be

    created in advance

    Create the device files after insmod

    Creating ${device}0-3 after you got the $major

  • 7/31/2019 8_Character Device Drivers

    13/44

    Where Are We?

    Now, the driver have some devicenumbers

    And, we have device files

    mknod

    Users can access these files

    But, how these accesses finally go to thedriver ?

  • 7/31/2019 8_Character Device Drivers

    14/44

    Some Important Data Structures

    file_operations file

    inode

  • 7/31/2019 8_Character Device Drivers

    15/44

    File Operations

    Let the accesses go to the driver Why? .. because device FILE

    A collection of function pointers

    read, write, Each driver should implement this set of functions

    (some of them may be NULL)

    Each open file is associated with its own set offunctions

    File: object, file operation: method

  • 7/31/2019 8_Character Device Drivers

    16/44

    File Operations: An Example

    ssize_t (*read) (struct file *, char __user *,size_t, loff_t *);

    corresponds to the read() system call

    __user

    user-space address

    cannot be directly dereferenced

  • 7/31/2019 8_Character Device Drivers

    17/44

    File Operations of the scull

    Driver

    struct file_operations scull_fops = {

    .owner = THIS_MODULE,

    .llseek = scull_llseek,

    .read = scull_read,

    .write = scull_write,

    .ioctl = scull_ioctl,

    .open = scull_open,

    .release = scull_release,

    };

    O th e r s fu n ct io n s a r e n o t im p l em e n t e d b y t h e s c u ll d r ive r

    H a n d le d b y th e k e r n e l d e fa u lt h a n d le r

    The syntax is more portable acrosschanges in the definitions of thestructures

  • 7/31/2019 8_Character Device Drivers

    18/44

    The file Structure

    Represents an open instance of a file not specific to device drivers

    created by the kernel on open

    released when the last user close the file

    Important fields

    mode_t f_mode read/write permission

  • 7/31/2019 8_Character Device Drivers

    19/44

    The file Structure

    Important fields loff_t f_pos

    current reading or writing position

    unsigned int f_flags E.g., O_RDONLY, O_NONBLOCK, O_SYNC

    A driver should check the O_NONBLOCK flag to

    see if nonblocking operation has been requested

  • 7/31/2019 8_Character Device Drivers

    20/44

    The file Structure

    Important fields struct file_operations *f_op

    operations associated with the file

    assign the operations during open

    you can change the file operations E.g., you can assign a different set of file operations for each

    minor number during the open method

    void *private_data

    For your private use Usually be used to preserve state information across system

    calls

  • 7/31/2019 8_Character Device Drivers

    21/44

    The file Structure

    Important fields struct dentry *f_dentry

    The directory entry (dentry) structure

    A directory is a file that records a set of directory entries

    Usually be used to access inode filp->f_dentry->d_inode filp is a ptr to structfile

    i_no some fields (name_len,) file/sudir name

    Dentry format:

    100 some fields (5,) file1

    2037 some fields (7,) subdir1

    80 some fields (7,) firefox

  • 7/31/2019 8_Character Device Drivers

    22/44

    The inode Structure

    Each file has an inode Record file information Data blocks, timing information, size.

    If a file is opened by two users

    Twofile structures and one inode structure How to find the file /usr/local/bin/foo ?

    Important fields dev_t i_rdev

    the actual device number ( for a device file )

    struct cdev *i_cdev pointer to the cdev, which represents a char device

  • 7/31/2019 8_Character Device Drivers

    23/44

    Char Device Registration

    Allocating & Initializing cdev Method 1

    struct cdev *my_cdev = cdev_alloc( );

    my_cdev->ops = &my_fops; Method 2

    void cdev_init(struct cdev *cdev, structfile_operations *fops);

    In either way, my_cdev->owner = THIS_MODULE;

  • 7/31/2019 8_Character Device Drivers

    24/44

    Char Device Registration

    Registering to the kernel int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

    num : first device number

    count: number of device numbers for the device

    Usually, 1

    The registration may fail should check it

    Once the registration succeeds, kernel will call youroperations prepare for that

    Remove a char device void cdev_del(struct cdev *dev);

  • 7/31/2019 8_Character Device Drivers

    25/44

    Device Registration in scull

    scull represents each device with astructure of type struct scull_dev

    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 */

    };

  • 7/31/2019 8_Character Device Drivers

    26/44

    Device Registration in scullstatic 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); //init the cdev

    dev->cdev.owner = THIS_MODULE;

    dev->cdev.ops = &scull_fops;err = cdev_add (&dev->cdev, devno, 1);// register to kernel

    /* Fail gracefully if need be */

    if (err)printk(KERN_NOTICE "Error %d adding scull%d", err, index);

    }The caller :

    for (i = 0; i < scull_nr_devs; i++) {

    ..

    scull_setup_cdev(&scull_devices[i], i);

    }

  • 7/31/2019 8_Character Device Drivers

    27/44

    The Older Way

    Registration int register_chrdev(unsigned int major, const

    char *name, struct file_operations *fops);

    registers minor numbers 0255 for the givenmajor number sets up a default cdev structure for each minor number

    Unregistration int unregister_chrdev(unsigned int major,

    const char *name);

  • 7/31/2019 8_Character Device Drivers

    28/44

    Open Method

    Initialization and preparation for lateroperations

    Usually perform the following Jobs

    Check for device-specific errors Initialize the device

    Update the f_op pointer, if necessary

    Allocate and fill any data structure to be putin filp->private_data

  • 7/31/2019 8_Character Device Drivers

    29/44

  • 7/31/2019 8_Character Device Drivers

    30/44

    scull_open()

    Introduced later

  • 7/31/2019 8_Character Device Drivers

    31/44

    The release Method

    Usually perform the following tasks Deallocate anything that open allocated in filp->

    private_data

    Shut down the device on last close

    int scull_release(struct inode *inode, struct file *filp)

    {

    return 0;

    }

    Scull has no hardware to shut down

  • 7/31/2019 8_Character Device Drivers

    32/44

    Memory Usage of the scull

    Driver

    In scull, the device is memory Variable sized

    Dynamically expanded

    Use kmalloc() and kfree()

    void *kmalloc(size_t size, int flags);

    Flags = = GFP_KERNEL void kfree(void *ptr);

  • 7/31/2019 8_Character Device Drivers

    33/44

    Memory Usage of the scull

    Driver

    Each device is a linked list of pointers

    Each pointer points to a scull_qset structure

    scull_dev.qset

    scull_dev.quantum

  • 7/31/2019 8_Character Device Drivers

    34/44

    Scull_trim()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]); // free the quantums in a qsetkfree(dptr->data); // free qsets data

    dptr->data = NULL;

    }

    next = dptr->next;

    kfree(dptr); // free the qset data structure

    }dev->size = 0;

    dev->quantum = scull_quantum;

    dev->qset = scull_qset;

    dev->data = NULL;

    return 0;}

  • 7/31/2019 8_Character Device Drivers

    35/44

    The read and write Methods

    ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t

    *offp);

    ssize_t write(struct file *filp, const char __user *buff, size_t count,

    loff_t *offp);

    The buff argument is user-space pointer May not be directly accessible by kernel

    May be paged out

    Oops on directly access

    May be a wrong/invalid pointer

    Kernel does not trust the users

  • 7/31/2019 8_Character Device Drivers

    36/44

    Accessing User Space Data

    Transferring data with user space unsigned long copy_to_user(void __user *to, const void *from,unsigned long count);

    unsigned long copy_from_user(void *to, const void __user *from,unsigned long count);

    The above functions check whether the user space pointer is valid

    Returns number of bytes that could NOT be copied perform data transfer

    may sleep For paging in user pages

  • 7/31/2019 8_Character Device Drivers

    37/44

    Accessing User Space Data

    If you are sure the user addresses are fine Use __copy_to_user()/__copy_from_user()

    No checks, faster

    Be careful Kernel crashes

    Security holes

  • 7/31/2019 8_Character Device Drivers

    38/44

    Coming Back to

    The read and write Methods

  • 7/31/2019 8_Character Device Drivers

    39/44

    The Read Method

    The return value R of read R = count ( > 0 )

    The specified amount of data is read

    0 < R < count Partial of the specified amount of data is read

    R = 0 EOF

    R < 0 Error

  • 7/31/2019 8_Character Device Drivers

    40/44

    Scull_read()

    // EOF, return 0

  • 7/31/2019 8_Character Device Drivers

    41/44

    Scull_read()

    // f_pos should reflect the current RW head

  • 7/31/2019 8_Character Device Drivers

    42/44

    Scull_write()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 */

    find listitem, qset index and offset in the quantum skipped

    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; // no free memory!!!

    memset (dptr->data, 0, qset * sizeof(char *));

    }

  • 7/31/2019 8_Character Device Drivers

    43/44

    Scull_write()

    if (!dptr->data[s_pos]) {

    dptr->data[s_pos] = kmalloc (quantum, GFP_KERNEL);//allocate the quantum

    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; // f_pos should reflect the current RW head

    /* update the size */

    if (dev->size < *f_pos) dev->size = *f_pos;

    out:

    up(&dev->sem);

    return retval;}

  • 7/31/2019 8_Character Device Drivers

    44/44

    Scatter-Gather Read/Write

    Read/write data from/to multiple buffers in asingle operation readv and writev

    iovec structure Used to specify the address range of a buffer

    An array of iovec structures is passed to readv()or writev()

    A driver can implement readv() and writev()methods if it benefits from scatter-gather IO


Recommended