混杂设备驱动的ioremap见ok6410学习笔记(9.混杂设备驱动及硬件访问)
简单字符驱动的ioremap驱动memdev.c
[cpp] view plaincopy
- /**************************************************************************
- 文件名: memdev.c
- 日期: 2013/06/07
- 头文件: memdev.h
- 功能: ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分
- 环境: Redhat企业版5 内核版本2.6.18-53.el5
- 作者: Hao
- 流程: 1.分配设备号(a.静态申请 b.动态分配)
- 2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名” b.自动创建)
- 3.设备注册(a.设备注册分配 b.设备注册初始化 c.设备注册添加)
- 4.实现file_operation中的函数
- ioctl驱动
- a.初始化cmd
- b.无参数的命令
- c.读方向的命令
- d.写方向的命令
- e.在ioctl函数中命令的实现
- 5.设备注销(cdev_del)
- 6.设备号注销
- ***************************************************************************/
- #include <linux/module.h>
- #include <linux/miscdevice.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 <linux/slab.h>
- #include <linux/poll.h>
- #include <linux/device.h>
- #include <linux/ioport.h>
- #include <asm/io.h>
- #include <asm/system.h>
- #include <asm/uaccess.h>
- #include <asm/atomic.h>
- #include <linux/ioctl.h>
- #include <linux/kernel.h>
- #include "memdev.h"
- static
int mem_major = MEMDEV_MAJOR; - module_param(mem_major, int, S_IRUGO);
- struct cdev c_dev;
- struct
class *mem_class; /*设备的类*/
- int kernel_num=1991;//用一个全局变量吧 要不不能把先写入的数据保存下来
- char kernel_buf[40]="hello kernel!!!";
- volatile unsigned long GPIOM_VA_BASE;//定义一个全局变量 保存ioremap映射的地址
- #define GPIOM_CON_VA GPIOM_VA_BASE
- #define GPIOM_DAT_VA (GPIOM_VA_BASE+0x4)
- #define GPIOM_PUD_VA (GPIOM_VA_BASE+0x8)
- MODULE_AUTHOR("Hao");
- MODULE_LICENSE("GPL");
- /**************************************************************************
- 函数名: ok6410_led_setup
- 函数功能: ioremap映射 gpio初始化
- 函数参数: 无
- 函数返回值: 无
- ***************************************************************************/
- static
void ok6410_led_setup(void) - {
- unsigned long temp;
- request_mem_region(0x7f008820,0x10,"led_name");//申请i/o内存
- GPIOM_VA_BASE = (volatile unsigned long)ioremap(0x7f008820, 0x10);//
- /*temp&=~0xffff;
- temp|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
- (*(volatile unsigned long *)GPIOM_VA_BASE)=temp;*/
- (*(volatile unsigned long *)GPIOM_VA_BASE)&=~0xffff;
- (*(volatile unsigned long *)GPIOM_VA_BASE)|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
- temp=0;
- (*(volatile unsigned long *)GPIOM_DAT_VA)=temp;
- //GPIOM_DAT_VA|=0xf;
- //GPIOM_DAT_VA=0x00;
- /*******************或者使用这种方式****************************/
- /* temp&=~0xffff;
- temp|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
- writel(temp, GPIOM_CON_VA);
- temp|=0xf;
- temp=0;
- writel(temp, GPIOM_DAT_VA);*/
- }
- /**************************************************************************
- 函数名: ok6410_led_release
- 函数功能: iounmap撤销映射 release_mem_region撤销申请
- 函数参数: 无
- 函数返回值: 无
- ***************************************************************************/
- static
void ok6410_led_release(void) - {
- iounmap((void*)GPIOM_VA_BASE);
- release_mem_region(0x7f008820,0x10);
- }
- /**************************************************************************
- 函数名: memdev_ioctl
- 函数功能: ioctl实现函数 命令实习函数
- 函数参数: 无
- 函数返回值: 返回ret为正常执行 返回-EINVAL命令号不正确
- ***************************************************************************/
- static
long memdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) - {
- int ret=0;
- int err=0;
- int kernel_num=1991;
- //char kernel_buf[20]="hello kernel!!!";
- /*先判断命令号是否正确*/
- if (_IOC_TYPE(cmd) != CMD_KTYPE) //获得命令的type类型是否正确
- return -EINVAL;
- if (_IOC_NR(cmd) > LED_KCMD) //获得命令的num类型 是否小于命令个数
- return -EINVAL;
- /*获命令的数据传输方向 根据各自的方向判断*/
- if (_IOC_DIR(cmd) & _IOC_READ)
- err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));/*此函数是根据
- 内核空间写的 是用来判断 arg应用程序传来的用户空间 是否有效的 所以对于用户空间来说是写*/
- else
if (_IOC_DIR(cmd) & _IOC_WRITE) - err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));//对于用户空间来说是读 成功返回1 失败返回0
- if (err)
- return -EFAULT;
- /*实现CMD的用法*/
- switch(cmd)
- {
- case LEDR_KCMD:
- ret=__put_user(kernel_num, (int *)arg); //把内核中的int型数据读入用户空间 unsigned long arg就是一个地址值 kernel->arg
- break;
- case LEDW_KCMD:
- ret=__get_user(kernel_num, (int *)arg); //arg->kernel_num 把用户空间的数据传递给kernel_num
- printk(KERN_EMERG "WRITE_KCMD is in kernel!!! kernel_num:%d \n",kernel_num);
- if(1==kernel_num)
- {
- writel(0x0, GPIOM_DAT_VA);//将4个led全部点亮
- }
- if(0==kernel_num)
- {
- writel(0x1f, GPIOM_DAT_VA);//将4个led全部熄灭
- }
- break;
- default:
- return -EINVAL;
- break;
- }
- }
- int mem_release(struct inode *inode, struct file *filp)
- {
- return 0;
- }
- int mem_open(struct inode *inode,struct file *filp)
- {
- return 0;
- }
- static
const
struct file_operations mem_fops = //定义此字符设备的file_operations
- { //这里是对结构体整体赋值的方式
- .owner = THIS_MODULE, //函数名都可以自己定义 都是函数指针
- .open = mem_open,
- .release = mem_release,
- .unlocked_ioctl=memdev_ioctl,
- };
- static
int memdev_init(void) - {
- ok6410_led_setup();
- int result;
- dev_t dev_num=MKDEV(mem_major,0);//将主设备号和次设备号转换成32位的设备号给 静态申请设备号用的
- if(mem_major)
- result=register_chrdev_region(dev_num,2,"newmemdev");//静态申请设备号为dev_num 2个设备 设备名为“newmemdev”
- else
- {
- result=alloc_chrdev_region(&dev_num,0,2,"newmemdev");//动态分配设备号 设备名可以在/proc/devices文件中找 与/dev路径找文件不同 因为一个设备文件 一个次设备号
- mem_major = MAJOR(dev_num);//获得主设备号
- }
- if (result < 0)
- {
- return result; //判断动态分配是否成功 不成功则退出函数
- }
- mem_class = class_create(THIS_MODULE, "mem_class_name");//创建设备类
- device_create(mem_class, NULL, MKDEV(mem_major, 0), NULL, "memdev0"); //创建设备文件 文件名为memdev0
- /*设备注册 分配已经在前面完成了 为全局变量*/
- cdev_init(&c_dev,&mem_fops);//设备注册初始化 将设备号dev_num和file_operation建立联系
- c_dev.owner = THIS_MODULE;//*****************貌似可以屏蔽吧*********************
- cdev_add(&c_dev,dev_num,2);//设备注册 添加 设备号为dev_num 设备数为2个
- return 0;
- }
- static
int memdev_exit(void) - {
- ok6410_led_release();
- cdev_del(&c_dev);//设备注销
- device_destroy(mem_class, MKDEV(mem_major, 0));/*注销设备*/
- class_destroy(mem_class); /*删除类*/
- unregister_chrdev_region(MKDEV(mem_major, 0), 2);//设备号注销
- return 0;
- }
- module_init(memdev_init);
- module_exit(memdev_exit);
memdev.h
[cpp] view plaincopy
- /**************************************************************************
- 文件名: memdev.h
- 日期: 2013/06/07
- 头文件: memdev.h
- 功能: ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分头文件
- 定义三个命令
- 环境: Redhat企业版5 内核版本2.6.18-53.el5
- 作者: Hao
- 流程: 1.分配设备号(a.静态申请 b.动态分配)
- 2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名” b.自动创建)
- 3.设备注册(a.设备注册分配 b.设备注册初始化 c.设备注册添加)
- 4.实现file_operation中的函数
- ioctl驱动
- a.初始化cmd
- b.无参数的命令
- c.读方向的命令
- d.写方向的命令
- 5.设备注销(cdev_del)
- 6.设备号注销
- ***************************************************************************/
- #ifndef _MEMDEV_H_
- #define _MEMDEV_H_
- #include <linux/ioctl.h>
- #ifndef MEMDEV_MAJOR
- #define MEMDEV_MAJOR 251 /*预设的mem的主设备号*/
- #endif
- #ifndef MEMDEV_NR_DEVS
- #define MEMDEV_NR_DEVS 2 /*设备数*/
- #endif
- #ifndef MEMDEV_SIZE
- #define MEMDEV_SIZE 4096 /*开辟的内存大小*/
- #endif
- /*mem设备描述结构体*/
- struct mem_dev
- {
- char *data;
- unsigned long size;
- };
- #define CMD_KTYPE 'k' //定义命令幻数 也叫命令类型
- #define LEDR_KCMD _IOR(CMD_KTYPE,1,int) //定义读方向的命令
- #define LEDW_KCMD _IOW(CMD_KTYPE,2,int) //定义写方向的命令
- #define LED_KCMD 2 //命令个数 后面判断命令是否有效 用的
- #endif /* _MEMDEV_H_ */
app_led.c
[cpp] view plaincopy
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include "memdev.h"
- int main(int argc, char *argv[])
- {
- int fd=0;
- printf("\n%d\n",*argv[1]);
- unsigned int arg=(unsigned int)(*argv[1]-'0');
- char buf[40]="WRITE_STR_KCMD is in kernel";
- if(-1==(fd=open("/dev/memdev0",O_RDWR))) //设备节点名称为memdev0
- {
- printf("Open Dev Mem0 Error!\n");
- _exit(EXIT_FAILURE);
- }
- printf("begin WRITE_KCMD!!!\n"); //写入一个int型arg
- ioctl(fd,LEDW_KCMD,&arg);
- close(fd);
- return 0;
- }
注意的还是ioremap的返回值的利用。
对应飞凌驱动的测试程序[cpp] view plaincopy
- /*此为ok6410 飞凌提供的驱动的测试程序 */
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- int main(int argc,char *argv[])
- {
- int fd=0;
- printf("\n%d\n",*argv[1]);
- unsigned int arg=(unsigned int)(*argv[1]-'0'); //驱动中规定有两个参数第一个当命令用 为0的时候是关灯 为1的时候是开灯
- printf("%d\n",arg);
- unsigned int arg1=(unsigned int)(*argv[2]-'0'); //第二参数是第几个灯进行开关
- printf("%d\n",arg1);
- char buf[40]="WRITE_STR_KCMD is in kernel";
- if(-1==(fd=open("/dev/leds",O_RDWR))) //设备节点名称为memdev0
- {
- printf("Open Dev Mem0 Error!\n");
- exit(EXIT_FAILURE);
- }
- printf("begin WRITE_KCMD!!!\n"); //写入一个int型arg
- ioctl(fd,arg,arg1);
- close(fd);
- return 0;
- }
驱动在/drivers/char/s3c6410_leds.c
[cpp] view plaincopy
- #include <linux/miscdevice.h>
- #include <linux/delay.h>
- #include <asm/irq.h>
- //#include <mach/regs-gpio.h>
- #include <mach/hardware.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/mm.h>
- #include <linux/fs.h>
- #include <linux/types.h>
- #include <linux/delay.h>
- #include <linux/moduleparam.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/ioctl.h>
- #include <linux/cdev.h>
- #include <linux/string.h>
- #include <linux/list.h>
- #include <linux/pci.h>
- #include <asm/uaccess.h>
- #include <asm/atomic.h>
- #include <asm/unistd.h>
- #include <mach/map.h>
- #include <mach/regs-clock.h>
- #include <mach/regs-gpio.h>
- #include <plat/gpio-cfg.h>
- #include <mach/gpio-bank-e.h>
- #include <mach/gpio-bank-m.h>
- #define DEVICE_NAME "leds"
- static
long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) - {
- switch(cmd) {
- unsigned tmp;
- case 0:
- case 1:
- if (arg > 4)
- {
- return -EINVAL;
- }
- tmp = readl(S3C64XX_GPMDAT);
- if(cmd==0) //close light
- {
- tmp &= (~(1<<arg));
- }
- else
//open light
- {
- tmp |= (1<<arg);
- }
- writel(tmp,S3C64XX_GPMDAT);
- printk (DEVICE_NAME": %d %d\n", arg, cmd);
- return 0;
- default:
- return -EINVAL;
- }
- }
- static
struct file_operations dev_fops = { - .owner = THIS_MODULE,
- .unlocked_ioctl = s3c6410_leds_ioctl,
- };
- static
struct miscdevice misc = { - .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &dev_fops,
- };
- static
int __init dev_init(void) - {
- int ret;
- unsigned tmp;
- //gpm0-3 pull up
- tmp = readl(S3C64XX_GPMPUD);
- tmp &= (~0xFF);
- tmp |= 0xaa;
- writel(tmp,S3C64XX_GPMPUD);
- //gpm0-3 output mode
- tmp =readl(S3C64XX_GPMCON);
- tmp &= (~0xFFFF);
- tmp |= 0x1111;
- writel(tmp,S3C64XX_GPMCON);
- //gpm0-3 output 0
- tmp = __raw_readl(S3C64XX_GPMDAT);
- tmp |= 0x10;
- writel(tmp,S3C64XX_GPMDAT);
- ret = misc_register(&misc);
- printk (DEVICE_NAME"\tinitialized\n");
- return ret;
- }
- static
void __exit dev_exit(void) - {
- misc_deregister(&misc);
- }
- module_init(dev_init);
- module_exit(dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("FORLINX Inc.");
这个驱动是不能改的,因为是编译进内核的,所有只能用测试程序去适应驱动。2440也有对应的驱动,在国嵌和友善的内核里面。 |