/****************************************************************************
*
* 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. 【LeetCode】Anagram

    Anagram 指由颠倒字母顺序而构成的单词. e.g. 给出 ["eat", "tea", "tan", "ate", ...

  2. [转]一次CMS GC问题排查过程(理解原理+读懂GC日志)

    这个是之前处理过的一个线上问题,处理过程断断续续,经历了两周多的时间,中间各种尝试,总结如下.这篇文章分三部分: 1.问题的场景和处理过程:2.GC的一些理论东西:3.看懂GC的日志 先说一下问题吧 ...

  3. 使用外置的Servlet容器

    嵌入式Servlet容器: 优点:简单.便捷 缺点:默认不支持JSP.优化定制比较复杂(使用定制器[ServerProperties.自定义EmbeddedServletContainerCustom ...

  4. css 解决fixed 布局下不能滚动的问题

    如果我们布局的是后是fixed并且想要高度为100%的时候,我们一般会这样设置: div { display:fixed; height:%; overflow:scroll; } 但是这样并不会出现 ...

  5. MATLAB统计工具箱 转

    D:\Program Files\MATLAB\R2012b\toolbox\stats\stats MATLAB统计工具箱包括概率分布.方差分析.假设检验.分布检验.非参数检验.回归分析.判别分析. ...

  6. MySQL(二) MySQL基本操作

    数据库的基本操作 启动关闭 MySQL 服务 MySQL 安装好后,默认是当 Windows 启动.停止时,MySQL 也自动.停止.不过,用户可以使用 Windows 下的服务管理器或从命令行使用 ...

  7. laravel5.2 开发中打印sql语句

    在 AppServiceProvider 的boot方法中写入 use DB;use Event; if ( env('APP_ENV') == 'dev' ) { DB::connection()- ...

  8. :组合模式:Component

    #ifndef __COMPONENT_H__ #define __COMPONENT_H__ #include <iostream> #include <vector> us ...

  9. EEPROM读写学习笔记与I2C总线(转)

    reference:https://www.cnblogs.com/uiojhi/p/7565232.html 无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在 ...

  10. CentOS7安装配置Bacular

    参考: http://blog.51cto.com/molewan/2045602 https://blog.csdn.net/heshangkung/article/details/47955023 ...