Linux设备驱动之字符设备

发表于:2016-12-12 09:53

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:archiexie    来源:51Testing软件测试网采编

  字符设备是3大类设备(字符设备、块设备和网络设备)中较简单的一类设备,其驱动程序中完成的主要工作是初始化、添加和删除cdev结构体,申请和释放设备号,以及填充 file_operations结构体中的操作函数,实现file_operations结构体中的read()、write()和ioctl()等函数是驱动设计的主体工作。
  参考例程
  源码
/*
* 虚拟字符设备globalmem实例:
*  在globalmem字符设备驱动中会分配一片大小为 GLOBALMEM_SIZE(4KB)
*  的内存空间,并在驱动中提供针对该片内存的读写、控制和定位函数,以供用户空间的进程能通过
*  Linux系统调用访问这片内存。
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define DEV_NAME "globalmem" /* /dev中显示的设备名 */
#define DEV_MAJOR 0 /* 指定主设备号,为0则动态获取 */
/* ioctl用的控制字 */
#define GLOBALMEM_MAGIC 'M'
#define MEM_CLEAR _IO(GLOBALMEM_MAGIC, 0)
/*--------------------------------------------------------------------- local vars */
/*globalmem设备结构体*/
typedef struct {
struct cdev cdev; /* 字符设备cdev结构体*/
#define MEM_SIZE 0x1000 /*全局内存最大4K字节*/
unsigned char mem[MEM_SIZE]; /*全局内存*/
struct semaphore sem; /*并发控制用的信号量*/
} globalmem_dev_t;
static int globalmem_major = DEV_MAJOR;
static globalmem_dev_t *globalmem_devp; /*设备结构体指针*/
/*--------------------------------------------------------------------- file operations */
/*文件打开函数*/
static int globalmem_open(struct inode *inodep, struct file *filep)
{
/* 获取dev指针 */
globalmem_dev_t *dev = container_of(inodep->i_cdev, globalmem_dev_t, cdev);
filep->private_data = dev;
return 0;
}
/*文件释放函数*/
static int globalmem_release(struct inode *inodep, struct file *filep)
{
return 0;
}
/*读函数*/
static ssize_t globalmem_read(struct file *filep, char __user *buf, size_t len, loff_t *ppos)
{
globalmem_dev_t *dev = filep->private_data;
unsigned long p = *ppos;
int ret = 0;
/*分析和获取有效的长度*/
if (p > MEM_SIZE) {
printk(KERN_EMERG "%s: overflow!\n", __func__);
return - ENOMEM;
}
if (len > MEM_SIZE - p) {
len = MEM_SIZE - p;
}
if (down_interruptible(&dev->sem)) /* 获得信号量*/
return  - ERESTARTSYS;
/*内核空间->用户空间*/
if (copy_to_user(buf, (void*)(dev->mem + p), len)) {
ret = - EFAULT;
}else{
*ppos += len;
printk(KERN_INFO "%s: read %d bytes from %d\n", DEV_NAME, (int)len, (int)p);
ret = len;
}
up(&dev->sem); /* 释放信号量*/
return ret;
}
/*写函数*/
static ssize_t globalmem_write(struct file *filep, const char __user *buf, size_t len, loff_t *ppos)
{
globalmem_dev_t *dev = filep->private_data;
int ret = 0;
unsigned long p = *ppos;
if (p > MEM_SIZE) {
printk(KERN_EMERG "%s: overflow!\n", __func__);
return - ENOMEM;
}
if (len > MEM_SIZE - p) {
len = MEM_SIZE - p;
}
if (down_interruptible(&dev->sem)) /* 获得信号量*/
return  - ERESTARTSYS;
/*用户空间->内核空间*/
if (copy_from_user(dev->mem + p, buf, len)) {
ret =  - EFAULT;
}else{
*ppos += len;
printk(KERN_INFO "%s: written %d bytes from %d\n", DEV_NAME, (int)len, (int)p);
ret = len;
}
up(&dev->sem); /* 释放信号量*/
return ret;
}
/* seek文件定位函数 */
static loff_t globalmem_llseek(struct file *filep, loff_t offset, int start)
{
globalmem_dev_t *dev = filep->private_data;
int ret = 0;
if (down_interruptible(&dev->sem)) /* 获得信号量*/
return  - ERESTARTSYS;
switch (start) {
case SEEK_SET:
if (offset < 0 || offset > MEM_SIZE) {
printk(KERN_EMERG "%s: overflow!\n", __func__);
return - ENOMEM;
}
ret = filep->f_pos = offset;
break;
case SEEK_CUR:
if ((filep->f_pos + offset) < 0 || (filep->f_pos + offset) > MEM_SIZE) {
printk(KERN_EMERG "%s: overflow!\n", __func__);
return - ENOMEM;
}
ret = filep->f_pos += offset;
break;
default:
return - EINVAL;
break;
}
up(&dev->sem); /* 释放信号量*/
printk(KERN_INFO "%s: set cur to %d.\n", DEV_NAME, ret);
return ret;
}
/* ioctl设备控制函数 */
static long globalmem_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
globalmem_dev_t *dev = filep->private_data;
switch (cmd) {
case MEM_CLEAR:
if (down_interruptible(&dev->sem)) /* 获得信号量*/
return  - ERESTARTSYS;
memset(dev->mem, 0, MEM_SIZE);
up(&dev->sem); /* 释放信号量*/
printk(KERN_INFO "%s: clear.\n", DEV_NAME);
break;
default:
return - EINVAL;
}
return 0;
}
/*文件操作结构体*/
static const struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
.open         = globalmem_open,
.release      = globalmem_release,
.read         = globalmem_read,
.write        = globalmem_write,
.llseek       = globalmem_llseek,
.compat_ioctl = globalmem_ioctl
};
/*---------------------------------------------------------------------*/
/*初始化并注册cdev*/
static int globalmem_setup(globalmem_dev_t *dev, int minor)
{
int ret = 0;
dev_t devno = MKDEV(globalmem_major, minor);
cdev_init(&dev->cdev, &globalmem_fops);
dev->cdev.owner = THIS_MODULE;
ret = cdev_add(&dev->cdev, devno, 1);
if (ret) {
printk(KERN_NOTICE "%s: Error %d dev %d.\n", DEV_NAME, ret, minor);
}
return ret;
}
/*设备驱动模块加载函数*/
static int __init globalmem_init(void)
{
int ret = 0;
dev_t devno;
/* 申请设备号*/
if(globalmem_major){
devno = MKDEV(globalmem_major, 0);
ret = register_chrdev_region(devno, 2, DEV_NAME);
}else{ /* 动态申请设备号 */
ret = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
globalmem_major = MAJOR(devno);
}
if (ret < 0) {
return ret;
}
/* 动态申请设备结构体的内存,创建两个设备 */
globalmem_devp = kmalloc(2*sizeof(globalmem_dev_t), GFP_KERNEL);
if (!globalmem_devp) {
unregister_chrdev_region(devno, 2);
return - ENOMEM;
}
ret |= globalmem_setup(&globalmem_devp[0], 0); /* globalmem0 */
ret |= globalmem_setup(&globalmem_devp[1], 1); /* globalmem1 */
if(ret)
return ret;
init_MUTEX(&globalmem_devp[0].sem); /*初始化信号量*/
init_MUTEX(&globalmem_devp[1].sem);
printk(KERN_INFO "globalmem: up %d,%d.\n", MAJOR(devno), MINOR(devno));
return 0;
}
/*模块卸载函数*/
static void __exit globalmem_exit(void)
{
cdev_del(&globalmem_devp[0].cdev);
cdev_del(&globalmem_devp[1].cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major, 0), 2);
printk(KERN_INFO "globalmem: down.\n");
}
/* 定义参数 */
module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);
/* 模块描述及声明 */
MODULE_AUTHOR("Archie Xie <archixie@cnblogs.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A char device module just for demo.");
MODULE_ALIAS("cdev gmem");
MODULE_VERSION("1.0");
  用户空间验证
  切换到root用户
  插入模块
  insmod globalmem.ko
  创建设备节点(后续例程会展示自动创建节点的方法)
  cat /proc/devices 找到主设备号major
  mknod /dev/globalmem0 c major 0 和 /dev/globalmem1 c major 1
  读写测试
  echo "hello world" > /dev/globalmem
  cat /dev/globalmem
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号