已经大三了开始进行专业课的学习了,一学年的时间要学习完linux应用程序和linux驱动程序,按照进度感觉什么也学习不到。进入大三也有些烦躁,学习也有些急功近利,必须让自己沉下心读读书、敲敲代码了。
刚开始学习嵌入式linux驱动程序,看了些以前国嵌的视频发现一些函数根本找不到,很多的函数已经更新了。linux内核代码更新比较快,很多新的技术会很快取代旧的技术,这也是linux作为开源系统的特点。学习linux不能只记函数,而是要更深层次的了解其原理
在字符驱动中struct file、struct inode、struct cdev、struct file_operations、unsigned long copy_from_user、unsigned long copy_to_user是比较重要的几个结构体和函数,它们是内核空间与用户空间连接的桥梁。
基于mini2440的嵌入式linux的led驱动程序:
/*
* led_device.c
*
* Created on: 2016年11月3日
* Author: chy
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct leds { /*led描述*/
struct cdev led_dev;
char val[2];
volatile unsigned long *gpio_coun;
volatile unsigned long *gpio_data;
};
static struct leds led;
static ssize_t open_leds(struct inode *led_inode,struct file *file_led) /*打开文件*/
{
*led.gpio_coun &= (~(0x3 << 5 | 0x3 << 6 | 0x3 << 7 | 0x3 << 8));
*led.gpio_coun |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);
*led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8));
*led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);
return 0;
}
static ssize_t write_leds(struct file *file_led,const char __user *bufer,size_t size,loff_t *off) /*点亮所有led*/
{
int val,ssize;
ssize = copy_from_user(&led.val,bufer,strlen(bufer)); /*将数据由用户区拷贝到内核区*/
if(ssize < 0)
return -1;
val = led.val[0] - '0';
if(!val)
*led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8));
else
*led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);
return 0;
}
static ssize_t read_leds(struct file *file_led,char __user *bufer,size_t size,loff_t *off) /*读取数据*/
{
int f;
f = copy_to_user((void *)bufer,&led.val,strlen(led.val)); /*将数据由内核区拷贝到用户区*/
if(f < 0)
return -1;
return strlen(bufer);
}
static long ioctl_leds(struct file *file_led,unsigned int cmd,unsigned long arg) /*发送文件命令*/
{
if((cmd !=0 && cmd != 1) || (arg > 3 || arg < 0))
return -1;
switch(cmd){
case 0:
*led.gpio_data &= ~(0x1 << (arg + 5)); break;
case 1:
*led.gpio_data |= (0x1 << (arg + 5)); break;
}
return 0;
}
static int close_file(struct inode *inode,struct file *filep) /*关闭文件*/
{
*led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8));
*led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);
return 0;
}
struct file_operations file_oper = {
.owner = THIS_MODULE,
.open = open_leds,
.write = write_leds,
.read = read_leds,
.unlocked_ioctl = ioctl_leds,
.release = close_file,
};
dev_t dev_num;
static struct class *class_file = NULL;
static int init_leds_device(void) /*加载驱动模块*/
{
alloc_chrdev_region(&dev_num,0,1,"my_leds"); /*动态分配设备号*/
led.led_dev.owner = THIS_MODULE; /*设备模板*/
cdev_init(&led.led_dev,&file_oper); /*初始化cdev*/
cdev_add(&led.led_dev,dev_num,1); /*注册设备*/
led.gpio_coun = (volatile unsigned long *)ioremap(0x56000010, 4); /*led端口映射*/
led.gpio_data = led.gpio_coun + 1;
/*自动创建设备文件l*/
class_file = class_create(THIS_MODULE,"my_leds");
device_create(class_file,NULL,dev_num,NULL,"my_leds");
return 0;
}
static void exit_leds_device(void) /*卸载驱动模块*/
{
cdev_del(&led.led_dev); /*注销设备*/
/*删除设备文件*/
device_destroy(class_file, dev_num);
class_destroy(class_file);
unregister_chrdev_region(dev_num,1); /*注销设备号*/
/*取消端口映射*/
iounmap(led.gpio_coun);
return;
}
MODULE_LICENSE("GPL");
module_init(init_leds_device); /*加载模块*/
module_exit(exit_leds_device); /*卸载模块*/
驱动程序的Makefile:
obj-m:=./src/led_device.o
all:
make -C /home/chy/linux-kernel/ M=$(PWD) modules
cp -f ./src/led_device.ko ~/NFS/qu_dong/
clean:
make -C /home/chy/linux-kernel/ M=$(PWD) clean
rm -f ~/NFS/qu_dong/led_device.ko
/home/chy/linux-kernel/ 是开发板linux内核的路径,linux内核必须为编译过的且和开发板上跑的内核配置是一样的。不要使用友善之臂提供编译内核的默认方法,自己重新配置内核时去掉友善之臂的led驱动模块。
验证驱动程序:
/*
* led_test.c
*
* Created on: 2016年11月3日
* Author: chy
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define Max 256
#define NO 0
#define OFF 1
#define ERR(msg,f) {
if(f < 0) {
fprintf(stderr,"%s",msg);
exit(1);
}
}
int main(int argc,char *argv[])
{
int file_id,write_num;
char buf[Max] = "0";
file_id = open("/dev/my_leds", O_RDWR); /*打开文件*/
ERR("open file failed
",file_id);
printf("Led All NO
");
ERR("write file failed
",write(file_id,"0",Max)); /*点亮所有led*/
sleep(2); /*进程睡眠2秒*/
close(file_id); /*关闭文件*/
sleep(1);
file_id = open("/dev/my_leds", O_RDWR);/*打开文件*/
ERR("open file failed
",file_id);
int i;
/* 流水点亮、熄灭 */
printf("流水
");
for(i = 0; i < 4; i++){
ERR("ioctl failed
",ioctl(file_id, NO,i));
sleep(1);
ERR("ioctl failed
",ioctl(file_id, OFF,i));
}
/* 读取数据 */
ERR("read file faile
",read(file_id,&buf,Max));
printf("read data is: %s
",buf);
close(file_id);
exit(0);
}