문자 디바이스 드라이버 - 조명 드라이브
I. 서론
다음 프레임을 기입하는 간단한 문자 디바이스 드라이버 앞의 (a) , (b) 다음 광 간단한 하드웨어의 동작 --led
둘째, 회로도
(GPF4 개발 보드 LEDl, LED2 GPF5 개발 보드, LED4 GPF6 개발 보드)
셋째, 드라이버
드라이버는 이전의 기입 (프레임 A) 만 작동 레지스터의 증가, 프로그램 (S3C2440 데이터 시트를 읽어 구체적 설명을 등록하지 여기서 설명)
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/uaccess.h>
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
static int myled_open (struct inode * inode, struct file * file){
/* 配置GPF4,5,6为输出 */
*gpfcon &= ~((0x3<<(4*2))|(0x3<<(5*2))|(0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2))|(0x1<<(5*2))|(0x1<<(6*2)));
/* 如果不想操作寄存器,还可以直接使用linux 提供的接口,使用个函数,就不用寄存器映射了 */
/*
s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
*/
return 0;
}
ssize_t myled_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){
int val;
/* 内核空间和用户空间传递数据的方法 */
if(copy_from_user(&val, buf, size)){
printk("copy failed\n");
return -EFAULT;
}
if(val == 1){
//点灯,将GPF4,5,6拉低
*gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
/*也可以使用下面的函数设置gpio口*/
//s3c2410_gpio_setpin(S3C2410_GPF4, 1);
}else{
//灭灯,将GPF4,5,6拉高
*gpfdat |= ((1<<4)|(1<<5)|(1<<6));
}
return size;
}
/* file_operations 用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。
* 该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址。
*/
static struct file_operations myled_fop = {
.owner = THIS_MODULE,
.open = myled_open,
.write = myled_write,
};
static struct cdev *myled_cdev;
static struct class *myled_class;
static dev_t device;
/* 驱动模块的入口函数 */
static int myled_test_init(void){
/* 映射到0x56000050物理地址 */
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon+1;
if (device){
/* 静态申请设备号 */
register_myleddev_region(device,1,"myled_dev");
}else{
/* 动态申请设备号 */
alloc_myleddev_region(&device,0, 1,"myled_dev");
}
/* 申请空间 */
myled_cdev = cdev_alloc();
/* 注册字符设备驱动 */
cdev_init(myled_cdev,&myled_fop);
cdev_add(myled_cdev, device, 1);
/* 创建设备节点 */
myled_class = class_create(THIS_MODULE, "mydev");
class_device_create(myled_class, NULL, device, NULL,"mydev");
return 0;
}
/* 驱动模块的退出函数 */
static void myled_test_exit(void){
/* 注销映射 */
iounmap(gpfcon);
/* 删除设备节点 */
class_device_destroy(myled_class, device);
class_destroy(myled_class);
/* 注销字符设备驱动 */
cdev_del(myled_cdev);
/* 释放空间 */
cdev_put(myled_cdev);
/* 释放设备号和相应的设备名 */
unregister_myleddev_region(device, 1);
}
/* 这个宏将 myled_test_init 函数修饰为模块的入口函数 */
module_init(myled_test_init);
/* 这个宏将 myled_test_exit 函数修饰为模块的退出函数 */
module_exit(myled_test_exit);
/* 遵循GPL协议 */
MODULE_LICENSE("GPL");
넷째, makefile을 쓰기
#内核源码树路径
KERN_DIR = /work/system/kernel/source/linux-2.6.22.6
#目标文件
obj-m += myled.o
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
다섯째, 응용 프로그램을 테스트
단말기가 수행 할 필요로 프로그램을 조명 프로그램을 시험, 커맨드 라인 파라미터. 예를 들면 : ./led on
또는./led off
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char **argv){
int fd;
int val = 1;
fd = open("/dev/mydev",O_RDWR);
if(fd < 0){
printf("can't open\n");
return -1;
}
if(argc != 2){
printf("Usage :\n");
printf("%s <on|off>\n",argv[0]);
return 0;
}
if(strcmp(argv[1],"on") == 0){
val = 1;
}else{
val = 0;
}
write(fd,&val,4);
return 0;
}
여섯째, 테스트를 컴파일
시험 방법 (프레임)의 시험 방법과 유사하다.
실험 현상 :
명령 라인 단말기에서 실행이 때 ./led on
, 세 개의 조명 (내 개발 보드) 개발 보드에있는,
명령 라인 단말기에서 실행될 때 수행 ./led off
세 조명 개발 보드 꺼질 때.