/****************************************************************************
*
* list_head,proc file system,GPIO,ioremap
*
* 声明:
* 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
* 不对齐,从而影响阅读.
* 2. 本文中有些源代码没有全部帖出来,主要是因为篇幅太大的原因;
* 3. 基于2中的原因,本文借鉴了python中的缩进代码风格进行代码的体现:
* 1. 有些代码中的"..."代表省略了不影响阅读的代码;
* 2. 如下代码缩进代表在一个函数内部的代码,至于在什么函数里,不影响阅读:
* ... //省略代码
* struct test_s {
* };
* ... //省略代码
*
* //进入临界区之前加锁 }
* spin_lock(&p->lock); |
* | |
* /* 有效代码 */ |-->|采用缩进,代表在一个函数内
* | |的代码
* //出临界区之后解锁 |
* spin_unlock(&p->lock); }
*
* ... //省略代码
* int __init test_init(void)
* {
* ... //省略代码
* }
* ... //省略代码
*
* -- 阴 深圳 尚观 Var 曾剑锋
****************************************************************************/ \\\\\\\\\\\--*目录*--//////////
| 一. list_head常用接口:
| 二. proc文件系统相关操作:
| 三. gpio_request相关操作:
| 四. ioremap相关操作:
| 五. LED驱动写法:
| 六. 测试LED驱动:
\\\\\\\\\\\\\\\\/////////////// 一. list_head常用接口:
. 定义内核链表头,并初始化:
LIST_HEAD(test_head);
. 两种链表添加方式:
. 头插: list_add();
. 尾插: list_add_tail();
. 两种特殊的for each循环遍历,内部使用了typeof查询类型,再加上container_of:
. list_for_each_entry();
. list_for_each_entry_reverse();
. 普通的for each循环遍历:
list_for_each();
. 内核链表Demo:
... // "..."代表省略一些不影响分析代码的代码
struct test_list {
int data;
struct list_head entry; //内核链表
}; //定义一个内核链表头,并初始化
LIST_HEAD(test_head); struct test_list item[NUM];
int __init test_init(void)
{
int i;
struct test_list *tail;
struct list_head *p; for(i = ; i < NUM; i++)
{
item[i].data = i;
//头插方式
/*list_add(&item[i].entry, &test_head);*/
//尾插方式
list_add_tal(&item[i].entry, &test_head);
} //遍历链表,这两个宏里面都使用了typeof来配合container_of获取结构体指针
/*list_for_each_entry(tail, &test_head, entry)*/
list_for_each_entry_reverse(tail, &test_head, entry)
{
printk("%d ", tail->data);
}
printk("\n"); //没有像上面那样得到结构体指针,需要自己使用container_of获取结构体指针
list_for_each(p, &test_head)
{
tail = container_of(p, struct test_list, entry);
printk("%d ", tail->data);
}
printk("\n"); return ;
}
... 二. proc文件系统相关操作: //这里的资料不足,需要另外参考网络资料
. 头文件:
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
. 创建文件: proc_create();
. 创建目录: proc_mkdir();
. 删除文件,目录: remove_proc_entry();
. 访问方法,此处的资料不够,需要另外查相关资料:
. seq_printf();
. single_open();
. single_release();
. seq_read();
. seq_lseek();
. proc文件系统访问Demo:
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h> static int num = ; static int my_show(struct seq_file *file, void *data)
{
return seq_printf(file, "num is %d\n", num);
} static int test_open(struct inode *inode, struct file *file)
{
return single_open(file, my_show, NULL);
} struct file_operations fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
}; struct proc_dir_entry *pde;
int __init test_init(void)
{
//在proc文件系统上创建空目录
pde = proc_mkdir("test_dir", NULL);
if(!pde)
return -EFAULT; //在proc文件系统指定目录上创建文件
/**
* 1. "proc_test" : 创建的文件名;
* 2. 0644 : 八进制,表示创建的文件权限;
* 3. pde : 前面创建文件夹的指针;
* 4. &fops : 文件操作指针;
*/
proc_create("proc_test", , pde, &fops); return ;
} void __exit test_exit(void)
{
//先删除proc文件系统上的目录
remove_proc_entry("proc_test", pde);
//删除proc文件系统上proc_test目录的文件
remove_proc_entry("test_dir", NULL);
} module_init(test_init);
module_exit(test_exit); MODULE_LICENSE("GPL"); 三. gpio_request相关操作:
. 头文件: #include <linux/gpio.h>
. 获取GPIO在系统中的编号: EXYNOS4X12_GPXn(nr),比如:获取GPM4的0号引脚:
unsigned int gpio = EXYNOS4X12_GPM4();
. 向系统申请引脚编号为gpio的引脚,并命名为"name",成功返回0,主要是为了查看引脚是否被占用:
int gpio_request(gpio, "name"),
. 将系统编号为gpio的引脚设置为输出管脚,并把管脚的值设置为data_value:
int gpio_direction_output(gpio, data_value);
. 将系统编号为gpio的引脚设置为输入管脚:
int gpio_direction_input(gpio);
. 三星提供的专门设置管脚的接口:
int s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(config_value));
. 将系统编号为gpio的引脚值设为data_value,主要是针对输出管脚:
void gpio_set_value(gpio, data_value);
. 获取系统编号为gpio的管脚值:
int gpio_get_value(gpio);
. 释放系统编号为gpio的管脚:
void gpio_free(gpio);
. gpio_request操作Demo大致操作:
... // "..."代表省略部分代码,但不影响阅读
struct test_s {
struct file_operations fops;
int major;
unsigned int led_tbl[LEDNO];
};
typedef struct test_s test_t;
struct test_s test = {
.fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
.unlocked_ioctl = test_ioctl,
},
.major = ,
.led_tbl = {
[] = EXYNOS4X12_GPM4(), //申请系统对芯片引脚的gpio编号
[] = EXYNOS4X12_GPM4(),
[] = EXYNOS4X12_GPM4(),
[] = EXYNOS4X12_GPM4(),
},
}; ... for(i = ; i < ARRAY_SIZE(p->led_tbl); i++)
{
ret = gpio_request(p->led_tbl[i], "led"); //主要是为了查看引脚是否被占用
if(ret)
{
for(--i; i >= ; i--) //释放之前成功申请的管脚
{
gpio_free(p->led_tbl[i]);
}
return ret;
}
} //把io口配置成输出功能,输出高电平
for(i = ; i < ARRAY_SIZE(p->led_tbl); i++)
gpio_direction_output(p->led_tbl[i], LED_OFF); //设置输出方向和初始化值 ... /* 通过ioctl来控制灯的两灭 */
static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int i;
test_t *p = file->private_data; if(arg < || arg > )
return -EINVAL; switch(cmd)
{
case LED_ON:
if(!arg)
{
for(i = ; i < ARRAY_SIZE(p->led_tbl); i++)
gpio_set_value(p->led_tbl[i], LED_ON);
}
else
gpio_set_value(p->led_tbl[arg - ], LED_ON);
break;
case LED_OFF:
if(!arg)
{
for(i = ; i < ARRAY_SIZE(p->led_tbl); i++)
gpio_set_value(p->led_tbl[i], LED_OFF);
}
else
gpio_set_value(p->led_tbl[arg - ], LED_OFF);
break;
default:
return -EINVAL;
} return ;
}
... 四. ioremap相关操作:
. 在内核中下面两个缩写代表的意义:
. pa = physical addr 物理地址
. va = virtual addr 虚拟地址
. 建立映射关系,给出实际引脚控制寄存器的物理地址,以及控制寄存器的大小size(占用字节数):
void *va = ioremap(pa, size);
. 访问外设地址:
. 向32位寄存器中读取,写入数据:
data = ioread32(addr);
iowrite32(data, addr);
. 向16位寄存器中读取,写入数据:
data = ioread16(addr);
iowrite16(data, addr);
. 向8位寄存器中读取,写入数据:
data = ioread8(addr);
iowrite8(data, addr);
. 取消映射关系:
iounmap(va); 五. LED驱动写法: #include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/io.h> #define rGPM4CON 0x110002E0
#define SIZE SZ_8 //因为GPM4CON和GPM4DAT都是32位寄存器,共8字节 #define GPM4CON 0 //GPM4CON相对rGPM4CON地址的偏移量
#define GPM4DAT 4 //GPM4CON相对rGPM4CON地址的偏移量 #define LED_ON 0
#define LED_OFF 1 #define DEV_NAME "test"
#define LEDNO 4 struct test_s {
struct file_operations fops;
int major;
void __iomem *reg;
};
typedef struct test_s test_t; static int test_open(struct inode *inode, struct file *file)
{
unsigned long val;
test_t *p;
p = container_of(file->f_op, test_t, fops); file->private_data = p; //映射外设地址,GPM4CON和GPM4DAT都是32位寄存器,共8字节
p->reg = ioremap(rGPM4CON, SIZE);
if(!p->reg) //映射出错
return -ENOMEM; //配置IO口为输出功能
val = ioread32(p->reg + GPM4CON);
val &= ~0xffff;
val |= 0x1111;
iowrite32(val, p->reg + GPM4CON); return ;
} static int test_close(struct inode *inode, struct file *file)
{
test_t *p = file->private_data; iounmap(p->reg); //取消io映射 return ;
} static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
int ret, i;
unsigned long val;
char kbuf[LEDNO];
test_t *p = file->private_data; if(count <= || count > LEDNO)
return -EINVAL; if(count > LEDNO - *pos) //防止获取数据时超出数组下标范围
count = LEDNO - *pos; val = ioread32(p->reg + GPM4DAT); for(i = ; i < count; i++)
{
if(test_bit(i, &val)) //测试val中对应的位是否是1
kbuf[i] = LED_OFF;
else
kbuf[i] = LED_ON;
} ret = copy_to_user(buf, kbuf + *pos, count);
if(ret)
return -EFAULT; *pos += count; return count;
} static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
int ret, i;
unsigned long val;
char kbuf[LEDNO];
test_t *p = file->private_data; if(count <= || count > LEDNO)
return -EINVAL; if(count > LEDNO - *pos) //和read中的原因一致
count = LEDNO - *pos; ret = copy_from_user(kbuf, buf, count);
if(ret)
return -EFAULT; val = ioread32(p->reg + GPM4DAT);
for(i = ; i < count; i++)
{
if(kbuf[i] == LED_ON)
val &= ~( << (i + *pos));
else
val |= ( << (i + *pos));
}
iowrite32(val, p->reg + GPM4DAT); *pos += count; return count;
} static loff_t test_llseek(struct file *file, loff_t offset, int whence)
{
loff_t pos = file->f_pos; switch(whence)
{
case SEEK_SET:
pos = offset;
break;
case SEEK_CUR:
pos += offset;
break;
case SEEK_END:
pos = LEDNO + offset;
break;
default:
return -EINVAL; //参数非法
} file->f_pos = pos; return pos;
} struct test_s test = {
.fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
.read = test_read,
.write = test_write,
.llseek = test_llseek,
},
.major = ,
}; int __init test_init(void)
{
int ret; ret = register_chrdev(test.major,
DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} return ret;
} void __exit test_exit(void)
{
unregister_chrdev(test.major, DEV_NAME);
} module_init(test_init);
module_exit(test_exit); MODULE_LICENSE("GPL"); 六. 测试LED驱动: #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h> #include "led.h" int main(int argc, char **argv)
{
int fd, ret, i, j;
char buf[]; fd = open(argv[], O_RDWR);
if(- == fd)
{
perror("open");
exit();
} while()
{
lseek(fd, , SEEK_SET);
memset(buf, LED_OFF, sizeof(buf));
write(fd, buf, sizeof(buf));
usleep( * ); lseek(fd, , SEEK_SET);
for(i = ; i < ; i++)
{
buf[] = LED_ON;
write(fd, buf, ); lseek(fd, , SEEK_SET);
ret = read(fd, buf, sizeof(buf));
if(- == ret)
perror("read"); for(j = ; j < sizeof(buf); j++)
{
if(LED_ON == buf[j])
printf("led%d is on\n", j + );
else
printf("led%d is off\n", j + );
} usleep( * );
lseek(fd, i + , SEEK_SET);
}
} close(fd);
return ;
}

Samsung_tiny4412(驱动笔记06)----list_head,proc file system,GPIO,ioremap的更多相关文章

  1. Samsung_tiny4412(驱动笔记04)----volatile,container_of,file_operations,file,inode

    /*********************************************************************************** * * volatile,co ...

  2. Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建

    /*********************************************************************************** * * linux 3.5,U ...

  3. Samsung_tiny4412(驱动笔记03)----字符设备驱动基本操作及调用流程

    /*********************************************************************************** * * 字符设备驱动基本操作及 ...

  4. Samsung_tiny4412(驱动笔记05)----Makefile,open,read,write,lseek,poll,ioctl,fasync

    /*********************************************************************************** * * Makefile,op ...

  5. Samsung_tiny4412(驱动笔记07)----spinlock,semaphore,atomic,mutex,completion,interrupt

    /*********************************************************************************** * * spinlock,se ...

  6. Samsung_tiny4412(驱动笔记10)----mdev,bus,device,driver,platform

    /*********************************************************************************** * * mdev,bus,de ...

  7. Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class

    /*********************************************************************************** * * alloc_pages ...

  8. Samsung_tiny4412(驱动笔记02)----ASM with C,MMU,Exception,GIC

    /**************************************************************************** * * ASM with C,MMU,Exc ...

  9. Samsung_tiny4412(驱动笔记08)----jiffies,timer,kthread,workqueue,tasklet

    /*********************************************************************************** * * jiffies,tim ...

随机推荐

  1. [luogu P2054] [AHOI2005]洗牌

    [luogu P2054] [AHOI2005]洗牌 题目描述 为了表彰小联为Samuel星球的探险所做出的贡献,小联被邀请参加Samuel星球近距离载人探险活动. 由于Samuel星球相当遥远,科学 ...

  2. X-Window/GNOME/KDE的关系

    名称 开发者 开发年份 许可证 语言 说明 X-Window MIT 1984     X-Window是一个C/S架构的桌面框架,KDE和GNOME都是其基础上的具体实现 KDE Trolltech ...

  3. Eclipse错误:The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path

    该报错是由于缺少servlet-api.jar造成的,将servlet-api.jar复制到项目下的WEB-INF/lib目录下即可 servlet-api.jar在tomcat的lib目录下有,可以 ...

  4. 在eclipse激活maven profile配置

    profile简介 profile可以让我们定义一系列的配置信息,然后指定其激活条件.这样我们就可以定义多个profile,然后每个profile对应不同的激活条件和配置信息,从而达到不同环境使用不同 ...

  5. MySql查询最近一个月,一周,一天

    最近一个月 SELECT * FROM table WHERE DATE_SUB(CURDATE(), INTERVAL 1 MONTH) <= date(time); 本月.当前月 SELEC ...

  6. git merge branch

    git branch   look at your branches git branch newbranch git checkout newbrach do something git check ...

  7. POJ 2243 Knight Moves(BFS)

    POJ 2243 Knight Moves A friend of you is doing research on the Traveling Knight Problem (TKP) where ...

  8. MAVEN 创建项目

    使用archetype生成项目骨架 MAVEN 创建项目JAR 和 MAVEN创建项目WAR中是使用特定的acrchetype来进行创建项目,如果使用其他的archetype来创建项目或是使用 mvn ...

  9. 前端常用长度单位(px,em,rem,pt)介绍

    CSS中有不少属性是以长度作为值的.盒模型的属性就是一些很明显的值属性:width.height.margin.padding.border.除此之外还有很多其他的CSS属性的值同样也是长度值,像偏移 ...

  10. CSS--margin塌陷

    margin塌陷 解决方法: 1.给父级顶加上一条线,不太合适. 2.bfc block format context 设定bfc后,特定的盒子会遵循另一套语法规则,解决了margin塌陷 触发bfc ...