📄 directio.c
字号:
/****************************************** * 这是一段含有基本功能的字符设备的驱动程序 * $$$$ 运行在内核空间 * 主要功能:对S3C2410的寄存器进行操作 * *****************************************/#include <linux/kernel.h>#include <linux/module.h>#include <linux/ioport.h> /* for verify_area */#include <linux/init.h> /* for module_init */#include <asm/uaccess.h> /* for get_user and put_user */#include <asm-arm/arch-s3c2410/S3C2410.h>#include <linux/slab.h>//#include <asm-arm/io.h> //与inl(),outl()配合使用#include "directio.h"#define DEVICE_NAME "char_dev"static int Device_Open = 0;int device_ioctl(struct inode *,struct file *,unsigned int, unsigned long);int request_port(unsigned int, int);int release_port(void);/************************************** * 当一个过程要打开设备文件总是要调用这个函数 * (对应于用户层的open()函数) * *************************************/static int device_open(struct inode *inode, struct file *file){ if (Device_Open) return -EBUSY; //不想同时与2个进程对话 Device_Open++; return 0;}/************************************** * 当一个过程要关闭设备文件就一定会调用本函数 * 该函数无返回值,因为它不能失败 * (对应于用户层的close()函数) * *************************************/static int device_release(struct inode *inode, struct file *file){ Device_Open --; // 准备下一次调用 return 0;}typedef struct port_stru{ unsigned int startaddr; unsigned int endaddr; struct port_stru * next;}port_stru; //定义数据链表结构体struct port_stru *port_head = NULL;struct port_stru *port_tail = NULL;/*********************************************** * 端口申请 * 参数: * port : port started * nums : port nums(bytes) * 返回值: * -1 : 参数错误 * -2 : 端口忙 * 0 : OK * 1 : 端口已经被本模块请求 ***********************************************/int request_port(unsigned int port, int nums){ struct port_stru *temp = port_head; if(nums <= 0) { printk("invalid ports\n"); return -1; } /* 在建好的链表中找出端口*/ while(temp) { if((port >= temp->startaddr && port <= temp->endaddr) && \ (port + nums - 1 >= temp->startaddr && port + nums -1 <= temp->endaddr)) { //看看端口地址是否落在链表给出的地址范围内 return 1; // 该端口已经在链表中already request by this module } temp = temp->next; } /*申请端口*/ if(check_region(port, nums)) { printk("%x is used by another module\n", port); return -2; }; request_region(port, nums, DEVICE_NAME); /* 把新端口添加到链表中 */ temp = (struct port_stru *)kmalloc(sizeof(struct port_stru), GFP_KERNEL); temp->next = NULL; temp->startaddr = port; //新端口的起始地址 temp->endaddr = port + nums -1; //新端口的结束地址 if(port_head == NULL) //如果是头节点 { port_tail = temp; port_head = port_tail; } else //如果不是头节点则要增加1个节点 { port_tail->next = temp; port_tail = port_tail->next; } return 0;}int release_port() //释放链表{ struct port_stru *temp = port_head; while(port_head) { temp = port_head->next; release_region(port_head->startaddr, port_head->endaddr - port_head->startaddr + 1); kfree(port_head); port_head = temp; } return 0;}/********************************************** * 当一个过程要对一个已经打开的设备文件进行I/o控制 * 就一定会调用本函数. * **********************************************/int device_ioctl( struct inode *inode, struct file *file, unsigned int ioctl_num, // ioctl的功能参数,在用户层指定 unsigned long ioctl_param) // 用户传过来的数{ port_data temp; port_data *data; /* 对应于ioctl()调用 */ switch (ioctl_num) { case IOCTL_SET_MSG: //接收到一个信息指针(在用户空间)用于对设备进行设置 printk("device_ioctl IOCTL_SET_MSG\n"); data = (struct port_data*) ioctl_param; //获取用户传来的数据的首址 copy_from_user(&temp, data, sizeof(struct port_data)); // 该函数的功能:把用户空间的数据放到内核空间 if(temp.cmd == CMD_IRQ) { ; } else { if(request_port(temp.startaddr, temp.nums) >= 0) { //写端口 switch(temp.opts) { case PORT_BYTE: //向端口写1个字节 (*(unsigned char *)temp.startaddr) = temp.value; break; case PORT_HWORD: //向端口写半个字(2字节) (*(unsigned short *)temp.startaddr)= temp.value; break; case PORT_WORD: //向端口写1个字(4字节) (*(unsigned int *)temp.startaddr) = temp.value; //outl(temp.value,temp.startaddr); break; } }else { printk("\nrequest port error\n"); return -1; } } break; case IOCTL_GET_MSG: data = (struct port_data*) ioctl_param; //获取用户传来的数据的首址 copy_from_user(&temp, data, sizeof(struct port_data)); // 该函数的功能:把用户空间的数据放到内核空间 if(request_port(temp.startaddr, temp.nums) >= 0) { //读端口 switch(temp.opts) { case PORT_BYTE: temp.value = (*(unsigned char *)temp.startaddr); break; case PORT_HWORD: temp.value = (*(unsigned short *)temp.startaddr); break; case PORT_WORD: temp.value = (*(unsigned int *)temp.startaddr); break; } }else { printk("\nrequest port error\n"); return -1; } copy_to_user((struct port_data *) ioctl_param, &temp, sizeof(struct port_data)); // 该函数的功能:把内核空间的数据放到用户空间 break; } return 0;};/************************************** * 模块声明:需要用到的函数的指针 * *************************************/struct file_operations Fops = { .open = device_open, .release = device_release, .ioctl = device_ioctl };// 初始化模块: 注册字符设备static int __init directio_module_init( void ){ int ret_val; // 注册字符设备 ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops); // 若值为负则表示错误 if (ret_val < 0) { printk ("%s failed with %d\n", "Sorry, registering the character device ", ret_val); return ret_val; } printk ("Directio: let user program to access io port using outx & inx\n"); return 0;};// 卸载字符设备: rmmod命令将会调用本函数static void __exit directio_module_cleanup(void){ int ret; // 注销字符设备 ret = unregister_chrdev(MAJOR_NUM, DEVICE_NAME); // 如果有错则报告 if (ret < 0) printk("Error in module_unregister_chrdev: %d\n", ret); else printk("Close the char device OK!\n"); release_port();};module_init(directio_module_init); //insmod命令调用module_exit(directio_module_cleanup); //rmmod命令调用MODULE_LICENSE("GPL");MODULE_AUTHOR("Cvtech Co., Ltd <http://www.cvtech.com.cn>");MODULE_DESCRIPTION("directio char driver");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -