用户空间驱动
Posted lytwajue
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用户空间驱动相关的知识,希望对你有一定的参考价值。
一个第一次涉及内核问题的 Unix 程序猿, 可能会紧张写一个模块. 编写一个用户程序来
直接读写设备port可能easy些.
确实, 有几个论据倾向于用户空间编程, 有时编写一个所谓的用户空间设备驱动对照钻研
内核是一个明智的选择. 在本节, 我们讨论几个理由, 为什么你可能在用户空间编写驱动.
本书是关于内核空间驱动的, 可是, 所以我们不超越这个介绍性的讨论.
用户空间驱动的优点在于:
? 完整的 C 库能够连接. 驱动能够进行很多奇怪的任务, 不用依靠外面的程序(实现
使用策略的工具程序, 经常随着驱动自身公布).
? 程序猿能够在驱动代码上执行经常使用的调试器, 而不必走调试一个执行中的内核的弯
路.
? 假设一个用户空间驱动挂起了, 你可简单地杀掉它. 驱动的问题不可能挂起整个系
统, 除非被控制的硬件真的疯掉了.
? 用户内存是可交换的, 不象内核内存. 一个不常使用的却有非常大一个驱动的设备不
会占领别的程序能够用到的 RAM, 除了在它实际在用时.
? 一个精心设计的驱动程序仍然能够, 如同内核空间驱动, 同意对设备的并行存取.
? 假设你必须编写一个封闭源代码的驱动, 用户空间的选项使你easy避免不明朗的许可
的情况和改变的内核接口带来的问题.
比如, USB 驱动能够在用户空间编写; 看(仍然年幼) libusb 项目, 在
libusb.sourceforge.net 和 “gadgetfs” 在内核源代码里. 还有一个样例是 X server: 它确
切地知道它能处理哪些硬件, 哪些不能, 并且它提供图形资源给全部的 X 客户. 注意, 然
而, 有一个缓慢可是固定的漂移向着基于 frame-buffer 的图形环境, X server仅仅是作为
一个server, 基于一个内核空间的真实的设备驱动, 这个驱动负责真正的图形操作.
经常, 用户空间驱动的编写者完毕一个server进程, 从内核接管作为单个代理的负责硬件
控制的任务. 客户应用程序就能够连接到server来进行实际的操作; 因此, 一个聪明的驱
动经常能够同意对设备的并行存取. 这就是 X server怎样工作的.
可是用户空间的设备驱动的方法有几个缺点. 最重要的是:
? 中断在用户空间无法用. 在某些平台上有对这个限制的解决方法, 比如在 IA32 体
系上的 vm86 系统调用.
? 仅仅可能通过内存映射 /dev/mem 来使用 DMA, 并且仅仅有特权用户能够这样做.
? 存取 I/O port仅仅能在调用 ioperm 或者 iopl 之后. 此外, 不是全部的平台支持这
些系统调用, 而存取/dev/port 可能太慢而无效率. 这些系统调用和设备文件都要
求特权用户.
? 响应时间慢, 由于须要上下文切换在客户和硬件之间传递信息或动作.
? 更不好的是, 假设驱动已被交换到硬盘, 响应时间会长到不可接受. 使用 mlock 系
统调用可能会有帮助, 可是经常的你将须要锁住很多内存页, 由于一个用户空间程
序依赖大量的库代码. mlock, 也, 限制在授权用户上.
? 最重要的设备不能在用户空间处理, 包含但不限于, 网络接口和块设备.
如你所见, 用户空间驱动不能做的事情毕竟太多. 感兴趣的应用程序还是存在: 比如, 对
SCSI 扫描器设备的支持( 由 SANE 包实现 )和 CD 刻录器 ( 由 cdrecord 和别的工具实
现 ). 在两种情况下, 用户级别的设备情况依赖 “SCSI gneric” 内核驱动, 它输出了低层
的 SCSI 功能给用户程序, 因此它们能够驱动它们自己的硬件.
一种在用户空间工作的情况可能是有意义的, 当你開始处理新的没实用过的硬件时. 这样
你能够学习去管理你的硬件, 不必操心挂起整个系统. 一旦你完毕了, 在一个内核模块中
封装软件就会是一个简单操作了.
用户空间驱动的优缺点:
用户空间驱动的优点:
1 完整的 C 库能够连接. 驱动能够进行很多奇怪的任务, 不用依靠外面的程序(实现使用策略的工具程序, 经常随着驱动自身公布);
2 程序猿能够在驱动代码上执行经常使用的调试器, 而不必走调试一个执行中的内核的弯路;
3 假设一个用户空间驱动挂起了, 你可简单地杀掉它. 驱动的问题不可能挂起整个系统, 除非被控制的硬件真的疯掉了。
4 用户内存是可交换的, 不象内核内存. 一个不常使用的却有非常大一个驱动的设备不会占领别的程序能够用到的 RAM, 除了在它实际在用时;
5 一个精心设计的驱动程序仍然能够, 如同内核空间驱动, 同意对设备的并行存取;
6 假设你必须编写一个封闭源代码的驱动, 用户空间的选项使你easy避免不明朗的许可的情况和改变的内核接口带来的问题;
7 一种在用户空间工作的情况可能是有意义的, 当你開始处理新的没实用过的硬件时. 这样你能够学习去管理你的硬件, 不必操心挂起整个系统. 一旦你完毕了, 在一个内核模块中封装软件就会是一个简单操作了。
用户空间驱动的缺点:
1 中断在用户空间无法用. 在某些平台上有对这个限制的解决方法, 比如在 IA32体系上的 vm86 系统调用;
2 仅仅可能通过内存映射 /dev/mem 来使用 DMA, 并且仅仅有特权用户能够这样做;
3 存取 I/O port仅仅能在调用 ioperm 或者 iopl 之后. 此外,不是全部的平台支持这些系统调用, 而存取/dev/port可能太慢而无效率. 这些系统调用和设备文件都要求特权用户;
4 响应时间慢, 由于须要上下文切换在客户和硬件之间传递信息或动作。
5 更不好的是。假设驱动已被交换到硬盘, 响应时间会长到不可接受。 使用 mlock 系统调用可能会有帮助。可是经常的你将须要锁住很多内存页, 由于一个用户空间程序依赖大量的库代码. mlock, 也, 限制在授权用户上。最重要的设备不能在用户空间处理, 包含但不限于, 网络接口和块设备。
以下来看在前面的基础上加上中断的情况。由于用户空间不能操作中断,所以仅仅能使用内核驱动,以下贴出代码,详细解析我在《GPIO中断程序》一文中有讲述,此处不再赘述。
/*
* PB18_IRQTest.c
* This is a test program for sam9260, using PB19(J5_18 pin) input a signal to PB18(J5_16 pin),
* PB18 receive this signal as IRQ and make the LED linking on PB17((J5_14 pin)) turn on or turn off
*
* @Author: Cun Tian Rui
* @Date :March.18.2011
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/arch/board.h>
#include <linux/cdev.h>
#include <asm/arch/gpio.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/arch/at91_pio.h>
#include <asm/arch/at91_aic.h>
#include <asm/arch/at91_pmc.h>
void led_on()
{
at91_set_gpio_output(AT91_PIN_PB17,1);
}
void led_off()
{
at91_set_gpio_output(AT91_PIN_PB17 ,0);
}
struct light_dev *light_devp;
int light_major = 200;
struct light_dev
{
struct cdev cdev;
unsigned char value;
};
MODULE_AUTHOR("Cun Tian Rui");
MODULE_LICENSE("Dual BSD/GPL");
static void io_init(void)
{
at91_set_gpio_input(AT91_PIN_PB18, 1);
at91_set_deglitch(AT91_PIN_PB18, 1);
at91_sys_write(1 + PIO_IDR, 1<<18);
at91_sys_write(1 + PIO_IER, (~(1<<18)));
at91_sys_write(AT91_PMC_PCER, 1 << 3);
}
struct gpio_irq_desc
{
int irq;
unsigned long flags;
char *name;
};
static struct gpio_irq_desc PB18_IRQ={AT91_PIN_PB18,AT91_AIC_SRCTYPE_LOW,"PB18"};
static irqreturn_t PB18_intHandle(int irq, void *dev_id)
{
led_on();
return IRQ_RETVAL(IRQ_HANDLED);
}
int light_open(struct inode *inode,struct file *filp)
{
int err;
struct light_dev *dev;
dev = container_of(inode->i_cdev,struct light_dev,cdev);
filp->private_data = dev;
io_init();
err = request_irq(PB18_IRQ.irq,PB18_intHandle,PB18_IRQ.flags,PB18_IRQ.name,(void*)0);
if(err)
{
free_irq(PB18_IRQ.irq,(void*)0);
return -EBUSY;
}
return 0;
}
int light_release(struct inode *inode,struct file *filp)
{
free_irq(PB18_IRQ.irq,(void*)0);
return 0;
}
// ioctl
int light_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,
unsigned long arg)
{
struct light_dev *dev = filp->private_data;
switch(cmd)
{
case 0:
at91_set_gpio_output(AT91_PIN_PB19,0);
break;
case 1:
at91_set_gpio_output(AT91_PIN_PB19,1);
led_off();
break;
default:
return -ENOTTY;
// break;
}
return 0;
}
struct file_operations light_fops =
{
.owner = THIS_MODULE,
.ioctl = light_ioctl,
.open = light_open,
.release = light_release,
};
static void light_setup_cdev(struct light_dev *dev,int index)
{
int err,devno = MKDEV(light_major,index);
cdev_init(&dev->cdev,&light_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &light_fops;
err = cdev_add(&dev->cdev,devno,1);
if(err)
{
printk(KERN_NOTICE "Error %d adding LED%d",err,index);
}
}
int light_init(void)
{
int result;
dev_t dev = MKDEV(light_major,0);
if(light_major)
{
result = register_chrdev_region(dev,1,"PB18_IRQTest");
}
if(result < 0)
{
return result;
}
light_devp = kmalloc(sizeof(struct light_dev),GFP_KERNEL);
if(!light_devp)
{
result = - ENOMEM;
goto fail_malloc;
}
memset(light_devp,0,sizeof(struct light_dev));
light_setup_cdev(light_devp,0);
return 0;
fail_malloc:unregister_chrdev_region(dev,light_devp);
return result;
}
void light_cleanup(void)
{
cdev_del(&light_devp->cdev);
kfree(light_devp);
unregister_chrdev_region(MKDEV(light_major,0),1);
}
module_init(light_init);
module_exit(light_cleanup);
最后给出自己写的一个在用户层读写AT91sam9260SDRAM某一个位置的小程序结束本文。
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SDRAM_PHY_Position 0x20800000
int main (int args, char* arg[])
{
int i;
int fd;
char* mem;
char *buff = "HELLO";
//open /dev/mem with read and write mode
if ((fd = open ("/dev/mem", O_RDWR)) < 0)
{
perror ("open error");
return -1;
}
//map physical memory 0-10 bytes
mem = mmap (0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, SDRAM_PHY_Position);
if (mem == MAP_FAILED) {
perror ("mmap error:");
return 1;
}
//Read old value
for (i = 0; i < 5; i++)
{
printf("\nold mem[%d]:%d", i, mem[i]);
}
//write memory
memcpy(mem, buff, 5);
//Read new value
for (i = 0; i<5 ; i++)
{
printf("\nnew mem[%d]:%c", i, mem[i]);
}
printf("\n");
munmap (mem, 10); //destroy map memory
close (fd); //close file
return 0;
}
以上是关于用户空间驱动的主要内容,如果未能解决你的问题,请参考以下文章
[架构之路-46]:目标系统 - 系统软件 - Linux OS硬件设备驱动-UIO用户空间IO驱动框架与用户空间协议栈