一个简单的Linux字符驱动

这个是win驱动课的作业,题目是设计一个通用的io端口读写驱动,因为我的电脑配置太低无法运行虚拟机,就用Linux完成了作业。

read和write的处理并发读写的部分来自ldd3。

 

1.驱动程序

 

/*通用IO端口读写驱动*/

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <linux/fcntl.h>

 

#include <linux/slab.h>

#include <linux/mm.h>

#include <asm/uaccess.h>

 

#include <linux/cdev.h>

#include <linux/moduleparam.h>

#include <linux/proc_fs.h>

 

#include <asm/io.h>

#include <linux/wait.h>

#include <asm/current.h>

#include <linux/sched.h>

#include <linux/ioctl.h>

 

//ioctl的命令字定义

#define TEST_MAGIC_NUM 'k'

#define PORT_SET _IOW(TEST_MAGIC_NUM,1,int)

#define PORT_GET _IO(TEST_MAGIC_NUM,2)

#define PORT_LOCK _IO(TEST_MAGIC_NUM,3)

#define PORT_UNLOCK _IO(TEST_MAGIC_NUM,4)

 

#define MAXBUF 512 //自定义数据区大小为512

#define DEVICE_NAME "/dev/test_device"

#define DEVICE_SCCUESS 0

 

#define PORT_LOCKED -2

 

//全局变量尽量采用static变量,避免“污染”内核的变量的命名空间

static int major_number = 1000;

static int minor_number = 0;

static int port_number = 0x70;

 

module_param(major_number , int , S_IRUGO);

module_param(minor_number , int , S_IRUGO);

//module_param(port_number  , long , S_IRUGO);

 

static dev_t device_number;   //设备号

static struct cdev *my_cdev=NULL;

 

static void *pdev=NULL;    //自定义区域,装载模块的时候分配一片区域

 

//内核信号量,等待队列,用于实现读写的同步

static struct semaphore g_sem;

//static unsigned buf_flag=0;

static unsigned int port_flag = 0;

static DECLARE_WAIT_QUEUE_HEAD(g_queue);

 

//打开和读写操作,读写操作将实现阻塞I/O,为防止读写冲突,设备只允许一个进程的一个操作

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

{

printk("device open OK,process name %s,process ID %i\n",current->comm,current->pid);

return DEVICE_SCCUESS;

}

 

int test_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)

{

int i=0;

 

printk("operation: read device");

if(down_interruptible(&g_sem))//获得锁

{

printk(KERN_WARNING "Get semaphore error\n");

return -1;

}

 

while(port_flag)//端口被占用,等待

{

up(&g_sem);

if(filp->f_flags & O_NONBLOCK)

return -EAGAIN;

 

if(wait_event_interruptible(g_queue,0 == port_flag))

return -ERESTARTSYS;//可能是信号使得进程被唤醒

if(down_interruptible(&g_sem))

return -ERESTARTSYS;

}

 

//此时已经获得锁,且操作可以执行

//做标记,不允许修改端口

port_flag=1;

 

for(i=0;i<count;i++)

((char*)pdev)[i]=inb(port_number);

 

port_flag=0;//不再占用端口

up(&g_sem);//释放信号量

wake_up_interruptible(&g_queue);//唤醒等待队列中的进程

 

i=copy_to_user((void*)buf,pdev,count);//读出的数据拷贝给应用程序

if(i<0)

{

printk( KERN_WARNING "copy to user error\n");

return -EFAULT;

}

return count-i;

}

 

long test_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)

{

int retval=0;

printk("in test_ioctl\n");

switch(cmd)

{

case PORT_SET:

if(port_flag )

{

retval = PORT_LOCKED;

break;

}

else   //没有其他进程在IO

{

port_number=arg;

retval = DEVICE_SCCUESS;

printk("port number changed\n");

break;

}

case PORT_GET:

retval = port_number;

printk("port number returned\n");

break;

 

default: break;

}

 

return retval;

}

 

int test_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)

{

int i=0,j=0;

 

printk("operation: write device\n");

 

i=_copy_from_user(pdev,(void*)buf,count);//数据拷贝过来并,并将指定端口

if(i<0)

{

printk( KERN_WARNING "copy to user error\n");

return -EFAULT;

}

 

 

if(down_interruptible(&g_sem))//获得锁

{

printk(KERN_WARNING "Get semaphore error\n");

return -1;

}

 

while(port_flag)//操作无法执行,等待

{

up(&g_sem);

if(filp->f_flags & O_NONBLOCK)

return -EAGAIN;

 

if(wait_event_interruptible(g_queue,0 == port_flag))

return -ERESTARTSYS;//可能是信号使得进程被唤醒

if(down_interruptible(&g_sem))

return -ERESTARTSYS;

}

 

//此时已经获得锁,且操作可以执行

port_flag=1;

 

for(j=0;j<count-i;j++)

outb(((char*)pdev)[j],port_number);

 

port_flag=0;

up(&g_sem);

wake_up_interruptible(&g_queue);

 

 

return (count-i);

}

 

//用户空间进程设备关闭时执行,也就是对应的close操作

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

{

return 0;

}

 

static struct file_operations test_ops = {

.owner = THIS_MODULE,

.read = test_read,

.write = test_write,

.release = test_release,

.open = test_open,

.unlocked_ioctl=test_ioctl,

};

 

 

 

int test_init(void) //内核装载时执行,注册设备和分配自由存储区

{

int err=0;

 

//分配一块缓冲区

pdev = kmalloc(MAXBUF,GFP_KERNEL);

if(NULL == pdev)

{

printk(KERN_WARNING "kernel cannot mallocate memory\n");

return -1;

}

 

 

//注册设备

device_number = MKDEV(major_number,minor_number);

err = register_chrdev_region(device_number,1,DEVICE_NAME);

if( 0 > err)

{

 

printk( KERN_WARNING "cannot register device ,major %d,minor %d\n",major_number,minor_number);

kfree(pdev);//先撤销之前的动作

return err;

}

 

//分配一个cdev

my_cdev = cdev_alloc();

if(NULL == my_cdev)

{

printk(KERN_WARNING "cannot allocate cdev structure\n");

kfree(pdev);

unregister_chrdev_region(device_number,1);

return -1;

}

 

//添加设备

my_cdev->ops= &test_ops;

my_cdev->owner = THIS_MODULE;

err=cdev_add(my_cdev,device_number,1);//添加一个设备

 

if(0 > err)

{

printk(KERN_WARNING"cannot add the device\n");

kfree(pdev);

unregister_chrdev_region(device_number,1);

cdev_del(my_cdev);

return err;

}

//初始化信号量,等待队列已经初始化

sema_init(&g_sem,1);

 

//设置标志

//buf_flag=0;

port_flag=0;

 

printk("init io driver OK\n");

return DEVICE_SCCUESS; 

}

 

void test_exit(void) //内核卸载时候执行,注销设备和释放自由存储区

{

printk("remove the test module\n");

unregister_chrdev_region(device_number,1);

cdev_del(my_cdev);

kfree(pdev);

 

printk(" common io driver unload\n");

 

module_init(test_init);

module_exit(test_exit);

MODULE_LICENSE("GPL");

 

 

 

 

 

 

2. 应用测试

首先用make工具编译然后用insmod装载内核模块

然后执行下面的命令添加一个设备文件

mknod /dev/test_device c 1000 0

然后把一个发光二极管的两个引脚和串口的4、7针接触,再运行这个程序,可以

看到二极管的闪烁现象#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>
#include <linux/ioctl.h>//使用下面的宏
#define DEVICE_NAME "/dev/test_device"#define TEST_MAGIC_NUM 'k'#define PORT_SET _IOW(TEST_MAGIC_NUM,1,int)#define PORT_GET _IO(TEST_MAGIC_NUM,2)#define PORT_LOCK _IO(TEST_MAGIC_NUM,3)int main(void){ char buf[24]; int fd=open(DEVICE_NAME,O_RDWR); if(fd < 0) { perror("open device"); exit(fd); }

int i=0;

for(i=0;i<30;i++)

{ ioctl(fd, PORT_SET,0x3fc); buf[0]=1;//DTR=0;RTS=1; write(fd,(void *)buf,1);
sleep(1);
buf[0]=2;//DTR=1;RTS=0; write(fd,(void *)buf,1);
sleep(1);
}
close(retcode);

return 0;}


原文地址:http://hi.baidu.com/handsoul/item/4701bdd5c9031b9d270ae71f

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值