本文参考百度文库“linux驱动开发入门”点击打开链接和转载博文:点击打开链接。
一、基本知识
Linux设备驱动分为:字符设备、块设备和网络设备。原理图如下:
二、示例
示例主要转载自博客园的博客,见上。只是我采用的的Linux内核版本比那篇博文的新,有小许改动,粘贴代码如下:
内核版本:
ry@ubuntu:/$ uname -a
Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
文件:hello_mod.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
- *
- * =====================================================================================
- */
- #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 250
- #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 DEFINE_SPINLOCK(spin);
- 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 long hello_mod_ioctl(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,
- .unlocked_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 long hello_mod_ioctl(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);
- sema_init(&sem, 1);
- 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”);
/*
* =====================================================================================
*
* 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
*
* =====================================================================================
*/
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 250
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 DEFINE_SPINLOCK(spin);
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 long hello_mod_ioctl(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,
.unlocked_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 long hello_mod_ioctl(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);
sema_init(&sem, 1);
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”);
头文件:hello_mod_ioctl.h
- /*
- * =====================================================================================
- *
- * Filename: hello_mod_ioctl.h
- *
- * Description: define the cmd supported by hello_mod
- *
- * Version: 1.0
- * Created: 06/19/2011 10:24:20 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
/*
* =====================================================================================
*
* Filename: hello_mod_ioctl.h
*
* Description: define the cmd supported by hello_mod
*
* Version: 1.0
* Created: 06/19/2011 10:24:20 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
Makefile文件:(注意:Makefile的’M’要大写)
- #**********************************************
- # 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
- #**********************************************
- obj-m += hello_mod.o
- CURRENT_PATH := (shell pwd) </span></li><li class="alt"><span>LINUX_KERNEL := (shell pwd) </span></li><li class="alt"><span>LINUX_KERNEL := (shell uname -r)
- LINUX_KERNEL_PATH := /usr/src/linux-headers-(LINUX_KERNEL) </span></li><li class="alt"><span>all: </span></li><li class=""><span> make -C (LINUX_KERNEL) </span></li><li class="alt"><span>all: </span></li><li class=""><span> make -C (LINUX_KERNEL_PATH) M=(CURRENT_PATH) modules </span></li><li class="alt"><span>clean: </span></li><li class=""><span> make -C (CURRENT_PATH) modules </span></li><li class="alt"><span>clean: </span></li><li class=""><span> make -C (LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
- install:
- insmod hello_mod.ko
- unistall:
- rmmod hello_mod
#**********************************************
# 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 #********************************************** obj-m += hello_mod.o CURRENT_PATH :=
(shellpwd)LINUXKERNEL:=
(
s
h
e
l
l
p
w
d
)
L
I
N
U
X
K
E
R
N
E
L
:=
(shell uname -r) LINUX_KERNEL_PATH := /usr/src/linux-headers-
(LINUXKERNEL)all:make−C
(
L
I
N
U
X
K
E
R
N
E
L
)
a
l
l
:
m
a
k
e
−
C
(LINUX_KERNEL_PATH) M=
(CURRENTPATH)modulesclean:make−C
(
C
U
R
R
E
N
T
P
A
T
H
)
m
o
d
u
l
e
s
c
l
e
a
n
:
m
a
k
e
−
C
(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean install: insmod hello_mod.ko unistall: rmmod hello_mod
以上三个文件即为驱动代码
另外,再给一个应用层测试代码,并共用驱动的头文件
hello_mod_test.c文件:
- /*
- * =====================================================================================
- *
- * Filename: hell_mod_test.c
- *
- * Description: hell_mod test app
- *
- * Version: 1.0
- * Created: 06/20/2011 01:44:11 AM
- * Revision: none
- * Compiler: gcc
- *
- * Author: Tishion (shion), tishion@163.com
- * Company: LIM
- *
- * =====================================================================================
- */
- #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;
- }
/*
* =====================================================================================
*
* Filename: hell_mod_test.c
*
* Description: hell_mod test app
*
* Version: 1.0
* Created: 06/20/2011 01:44:11 AM
* Revision: none
* Compiler: gcc
*
* Author: Tishion (shion), tishion@163.com
* Company: LIM
*
* =====================================================================================
*/
#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;
}
最后,安装原博客编译和查看结果。
需要注意的是,需要到“/var/log/”目录下着“syslog”文件。
</div>
</div>
转载:https://blog.csdn.net/sagittarius_warrior/article/details/51067518