开源人linux系统运维培训中心

Linux 字符设备驱动模块开发

时间 : 17-09-24 栏目 : linux编程 作者 : admin 评论 : 1 点击 : 119 次

Linux 字符设备驱动模块开发学习案例,源于互联网,在读懂的基础上,调试运行,输出结果。

[root@nagios linux_kernel]# vim hello.c

/*
 * ======================================================
 *
 *       Filename:  hello.c
 *
 *    Description:  hello_mod
 *
 *        Version:  1.0
 *        Created:  01/28/2011 05:07:55 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Tishion (shion), tishion@163.com
 *        Company:  LIM
 *
 * ===========================================================
 *     Modify by lamp
 *     Date 2017-9-24
 *     Compiler: gcc
 *     Mail: 812711277@qq.com
 *     Web:  http://www.bdkyr.com
 * ===========================================================
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
#include "hello_mod_ioctl.h"

#define MAJOR_NUM 256
#define MINOR_NUM 0
#define IN_BUF_LEN 256
#define OUT_BUF_LEN 512

MODULE_AUTHOR("Tishion");
MODULE_DESCRIPTION("Hello_mod driver by tishion");

static struct class * hello_class;
static struct cdev hello_cdev;
static dev_t devnum = 0;
static char * modname = "hello_mod";
static char * devicename = "hello";
static char * classname = "hello_class";

static int open_count = 0;
static struct semaphore sem;
static spinlock_t spin = SPIN_LOCK_UNLOCKED;
static char * inbuffer = NULL;
static char * outbuffer = NULL;
static lang_t langtype;

static int hello_mod_open(struct inode *, struct file *);
static int hello_mod_release(struct inode *, struct file *);
static ssize_t hello_mod_read(struct file *, char *, size_t, loff_t *);
static ssize_t hello_mod_write(struct file *, const char *, size_t, loff_t *);
static int hello_mod_ioctl(struct inode *, struct file *, unsigned int, unsigned long);

struct file_operations hello_mod_fops =
{
    .owner = THIS_MODULE,
    .open = hello_mod_open,
    .read = hello_mod_read,
    .write = hello_mod_write,
    .ioctl = hello_mod_ioctl,
    .release = hello_mod_release,
};

static int hello_mod_open(struct inode *inode, struct file *pfile)
{
    printk("+hello_mod_open()!\n");
    spin_lock(&spin);
    if(open_count)
    {
        spin_unlock(&spin);
        return -EBUSY;
    }
 open_count++;
    spin_unlock(&spin);
    printk("-hello_mod_open()!\n");
    return 0;
}
static int hello_mod_release(struct inode *inode, struct file *pfile)
{
    printk("+hello_mod_release()!\n");
    open_count--;
    printk("-hello_mod_release()!\n");
    return 0;
}
static ssize_t hello_mod_read(struct file *pfile, char *user_buf, size_t len, loff_t *off)
{
    printk("+hello_mod_read()!\n");

    if(down_interruptible(&sem))
    {
        return -ERESTARTSYS;
    }
    memset(outbuffer, 0, OUT_BUF_LEN);
    printk("    +switch()\n");
    switch(langtype)
    {
        case english:
            printk("        >in case: english\n");
            sprintf(outbuffer, "Hello! %s.", inbuffer);
            break;
                
case chinese:
            printk("        >in case: chinese\n");
            sprintf(outbuffer, "你好! %s.", inbuffer);
            break;
        case pinyin:
            printk("        >in case: pinyin\n");
            sprintf(outbuffer, "ni hao! %s.", inbuffer);
            break;
        default:
            printk("        >in case: default\n");
            break;
    }
    printk("    -switch()\n");
    if(copy_to_user(user_buf, outbuffer, len))
    {
        up(&sem);
        return -EFAULT;
    }
    up(&sem);
    printk("-hello_mod_read()!\n");
    return 0;
}
static ssize_t hello_mod_write(struct file *pfile, const char *user_buf, size_t len, loff_t *off)
{
    printk("+hello_mod_write()!\n");
    if(down_interruptible(&sem))
    {
        return -ERESTARTSYS;
 }
    if(len > IN_BUF_LEN)
    {
        printk("Out of input buffer\n");
        return -ERESTARTSYS;
    }
    if(copy_from_user(inbuffer, user_buf, len))
    {
        up(&sem);
        return -EFAULT;
    }
    up(&sem);
    printk("-hello_mod_write()!\n");
    return 0;
}
static int hello_mod_ioctl(struct inode *inode, struct file *pfile, unsigned int cmd, unsigned long arg)
{
    int err = 0;
    printk("+hello_mod_ioctl()!\n");
    printk("    +switch()\n");
    switch(cmd)
    {
        case HELLO_IOCTL_RESETLANG:
            printk("        >in case: HELLO_IOCTL_RESETLANG\n");
            langtype = english;
            break;
        case HELLO_IOCTL_GETLANG:
            printk("        >in case: HELLO_IOCTL_GETLANG\n");
err = copy_to_user((int *)arg, &langtype, sizeof(int));
            break;
        case HELLO_IOCTL_SETLANG:
            printk("        >in case: HELLO_IOCTL_SETLANG\n");
            err = copy_from_user(&langtype,(int *)arg, sizeof(int));
            break;
        default:
            printk("        >in case: default\n");
            err = ENOTSUPP;
            break;
    }       
    printk("    -switch()\n");
    printk("-hello_mod_ioctl()!\n");
    return err;
}   
static int __init hello_mod_init(void)
{
    int result;
    printk("+hello_mod_init()!\n");
    devnum = MKDEV(MAJOR_NUM, MINOR_NUM);
    result = register_chrdev_region(devnum, 1, modname);

    if(result < 0)
    {
        printk("hello_mod : can't get major number!\n");
        return result;
    }
cdev_init(&hello_cdev, &hello_mod_fops);
    hello_cdev.owner = THIS_MODULE;
    hello_cdev.ops = &hello_mod_fops;
    result = cdev_add(&hello_cdev, devnum, 1);
    if(result)
        printk("Failed at cdev_add()");
    hello_class = class_create(THIS_MODULE, classname);
    if(IS_ERR(hello_class))
    {
        printk("Failed at class_create().Please exec [mknod] before operate the device\n");
    }
    else
    {
        device_create(hello_class, NULL, devnum,NULL, devicename);
    }

    open_count = 0;
    langtype = english;
    inbuffer = (char *)kmalloc(IN_BUF_LEN, GFP_KERNEL);
    outbuffer = (char *)kmalloc(OUT_BUF_LEN, GFP_KERNEL);
    init_MUTEX(&sem);
    printk("-hello_mod_init()!\n");
    return 0;
}

static void __exit hello_mod_exit(void)
{
    printk("+hello_mod_exit!\n");
kfree(inbuffer);
    kfree(outbuffer);
    cdev_del(&hello_cdev);
    device_destroy(hello_class, devnum);
    class_destroy(hello_class);
    unregister_chrdev_region(devnum, 1);
    printk("-hello_mod_exit!\n");
    return ;
}

module_init(hello_mod_init);
module_exit(hello_mod_exit);
MODULE_LICENSE("GPL");

[root@nagios linux_kernel]# vim hello_mod_ioctl.h 

/*
 * ==========================================================
 *
 *       Filename:  hello.c
 *
 *    Description:  hello_mod
 *
 *        Version:  1.0
 *        Created:  01/28/2011 05:07:55 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Tishion (shion), tishion@163.com
 *        Company:  LIM
 *
 * ===========================================================
 */

#ifndef __HELLO_MOD_IOCTL_H__
#define __HELLO_MOD_IOCTL_H__

#define HELLO_MAGIC    12
#define HELLO_IOCTL_RESETLANG    _IO(HELLO_MAGIC,0)        //set langtype = english
#define HELLO_IOCTL_GETLANG        _IOR(HELLO_MAGIC,1,int)    //get langtype
#define HELLO_IOCTL_SETLANG        _IOW(HELLO_MAGIC,2,int)    //set langtype

typedef enum _lang_t
{
english, chinese, pinyin
}lang_t;

#endif
[root@nagios linux_kernel]# vim Makefile

#**********************************************
# Makefile linux 2.6 Module 
# # This makefile is written for Ubuntu 10.10
# # It may not perfomance without erros on the
# # other version or distributions.
# #**********************************************
# #    BY:tishion
# #    Mail:tishion@163.com
# #    2011/06/19
# #**********************************************
# #    Modify BY: lamp
# #    Mail:812711277@qq.com
# #    2017/09/24
# #    Web: http://www.bdkyr.com
# #
# #    This makefile is written for Centos  6.9  2.6.32-696.10.2.el6.x86_64
# #    It may not perfomance without erros on the
# #    other version or distributions.
# #**********************************************
obj-m += hello.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /usr/src/kernels/$(LINUX_KERNEL)
all:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
install:
       insmod hello.ko
unistall:
        rmmod hello

[root@nagios linux_kernel]# make
[root@nagios linux_kernel]# make install 
insmod hello.ko
[root@nagios linux_kernel]# lsmod |grep hello
hello                   3997  0 

测试程序:

[root@nagios linux_kernel]# vim hell_mod_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/ioctl.h>
#include <string.h>
#include <errno.h>
#include "hello_mod_ioctl.h"

int main()
{
    char outbuf[512];
    char * myname = "tishion";
    lang_t langtype = english;
    int fd = open("/dev/hello", O_RDWR, S_IRUSR|S_IWUSR);
    if(fd != -1)
    {
        write(fd, myname, strlen(myname)+1);
        langtype = chinese;
        ioctl(fd, HELLO_IOCTL_SETLANG, &langtype);
        read(fd, outbuf, 512);
        printf("langtype=chinese:%s\n", outbuf);
        memset(outbuf, 0, 512);
        langtype = pinyin;
        ioctl(fd, HELLO_IOCTL_SETLANG, &langtype);
        read(fd, outbuf, 512);
        printf("langtype=pinyin:%s\n", outbuf);
memset(outbuf, 0, 512);
        ioctl(fd, HELLO_IOCTL_RESETLANG, &langtype);
        read(fd, outbuf, 512);
        printf("langtype=english:%s\n", outbuf);
    }   
    else
    {
        perror("Failed at open():");
    }   
    return 0;
}   
[root@nagios linux_kernel]# gcc hell_mod_test.c -o hell_mod_test


本文标签

除非注明,文章均为( admin )原创,转载请保留链接: http://www.bdkyr.com/2447.html

Linux 字符设备驱动模块开发:目前有1 条留言

  1. 沙发
    净水
    Post: 2017-09-28 下午2:31

    文章不错支持一下吧

发表评论

7 + 3 = ?


1