imx6ull _linux驱动成长笔记
文章目录
1.挂载问题
1.1开机自动挂载
修改/etc/fstab文件
cat /etc/fstab //文件内容
vim /etc/fstab //文件内容
查看nfs相关进程
ps -A | grep nfs
检查是否有其他进程正在使用 /mnt 目录下的文件或资源
lsof | grep /mnt
遇到的问题,时常在/mnt目录里面查看不到nfs信息
有可能是重复挂载,直接umount /mnt 重新挂载
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/psd/nfs_rootfs /mnt
2.驱动程序基础操作
手动安装驱动程序
insmod demo.ko
卸载驱动
rmmod demo
查看驱动
cat /proc/devices //没创建设备节点的驱动程序这里面不会存在
字符设备节点创建成功,在/dev/目录下也会产生对应设备
打印内核log
dmesg (从内核启动到当前时候内核打印的所有信息)
sudo dmesg -C //直接清除打印信息
sudo dmesg -c //先将信息显示到终端上然后在清除
查看现象信息
cat /proc/devices
打印设备是否正确安装
编译
arm-buildroot-linux-gnueabihf-gcc usr_led_ctrl.c
注意:编译过程中,modname必须是文件名,不然会编译失败
错误码:
-ENOMEM:内存不足,无法完成字符设备的注册。
-ENODEV:未找到对应的设备节点或设备文件。
-ENXIO:无效的设备或设备不存在。
-EEXIST:字符设备已经存在,不能重复注册。
-EBUSY:字符设备被占用,无法注册。
-EINVAL:无效的参数或配置选项。
-ENFILE:系统打开的文件数已达到上限。
-ENOMEM:内存不足。
-ENAMETOOLONG:设备名称过长。
3. led控制部分
3.1 ioremap方式
ioremap/iounmap函数的API
#include <linux/io.h>
void *ioremap(phys_addr_t offset, unsigned long size)
功能:地址映射工作
参数:
@offset:物理地址
@size:映射的大小(单位是字节)
返回值:成功返回虚拟地址,失败返回NULL
void iounmap(void *addr)
功能:取消地址映射
参数:
@addr:虚拟地址
返回值:无
数据传输的API
#include <linux/uaccess.h>
int copy_from_user(void *to, const void __user volatile *from,
unsigned long n)
功能:将数据从用户空间拷贝到内核空间 (驱动的write)
参数:
@to:内核空间地址
@from:用户空间的地址
@n:拷贝的大小(单位字节)
返回值:成功返回0,失败返回未拷贝的字节的个数
int copy_to_user(void __user volatile *to, const void *from,
unsigned long n)
功能:将数据从内核空间拷贝到用户空间 (驱动的read函数中)
参数:
@to:用户空间地址
@from:内核空间的地址
@n:拷贝的大小(单位字节)
返回值:成功返回0,失败返回未拷贝的字节的个数
字符设备驱动的API
#include <linux/fs.h>
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
功能:注册字符设备驱动
参数:
@major:主设备号 次设备号[0-255]
major > 0 :静态指定设备号 主:0-511
major = 0 :动态分配主设备号
@name:驱动的名字
cat /proc/devices
1 mem
4 /dev/vc/0
| |
主设备号 名字
@fops:操作方法结构体
struct file_operations {
int (*open) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*release) (struct inode *, struct file *);
};
返回值:
major > 0 成功返回0,失败返回错误码
major = 0 成功返回主设备号,失败返回错误码
void unregister_chrdev(unsigned int major, const char *name)
功能:注销字符设备驱动
参数:
@major:主设备号
@name:名字
返回值:无
用户空间的API
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:打开文件
参数:
@pathname:打开文件的路径及名字
@flags :打开文件的方式
O_RDONLY :只读
O_WRONLY :只写
O_RDWR :读写
O_APPEND :追加
O_CREAT :创建,如果flags中填写了O_CREAT,就必须填写第三个参数,
第三个参数代表创建文件的权限
O_TRUNC :清空
O_EXCL :和O_CREAT结合使用,在创建文件的时候加上这个选项,如果文件存
在就返回EEXIST,如果文件不存在就会不返回错误了
@mode:创建文件的权限
实际创建出来的文件的权限 = (mode & ~umask)
umask:文件的掩码:通过umask命令可以查看掩码的默认值是0002
~umask:文件掩码的取反是在最大文件的权限的基础上取反的,最大文件的权限是0666
实际创建出来的文件的权限 = (0666 & ~(0002)) = (0666 & 0664) = 0664
返回值:成功返回文件描述符,失败返回-1置位错误码
ssize_t read(int fd, void *buf, size_t count);
功能:读取文件中的内容
参数:
@fd:文件描述符
@buf:存储读取到数据的首地址
@count:想要读取的字节的个数
返回值:成功返回读取到字节的个数,如果返回0代表读取到文件的结尾了。
失败返回-1置位错误码
ssize_t write(int fd, const void *buf, size_t count);
功能:向文件中写数据
参数:
@fd:文件描述符
@buf:想要写的数据的首地址
@count:想要写的字节的个数
返回值:成功返回写入的字节的个数,0表示不会写任何的数据
失败返回-1置位错误码
#include <unistd.h>
int close(int fd);
功能:关闭文件
参数:
@fd:文件描述符
返回值:成功返回0,失败返回-1置位错误码
led_ctrl.h
#ifndef __LED_CTRL_DRV_H__
#define __LED_CTRL_DRV_H__
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <asm/io.h>
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
// GPIO5_GDIR 地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;
//GPIO5_DR 地址:0x020AC000
static volatile unsigned int *GPIO5_DR;
/* registers */
#define IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3_REG 0x02290000 + 0x14
#define GPIO5_GDIR_REG 0x020AC004
#define GPIO5_DR_REG 0x020AC000
static int led_init(void)
{
/* ioremap */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3_REG, 4);
// GPIO5_GDIR 地址:0x020AC004
GPIO5_GDIR = ioremap(GPIO5_GDIR_REG, 4);
//GPIO5_DR 地址:0x020AC000
GPIO5_DR = ioremap(GPIO5_DR_REG, 4);
if(!IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 || !GPIO5_DR || !GPIO5_GDIR)
{
printk("ioremap error \n");
return -1;
}
/* enable gpio5
* configure gpio5_io3 as gpio
* configure gpio5_io3 as output
*/
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;
*GPIO5_GDIR |= (1<<3);
return 0;
}
static int led_exit(void)
{
iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
iounmap(GPIO5_GDIR);
iounmap(GPIO5_DR);
return 0;
}
static int led_on(void)
{
/* set gpio to let led on */
*GPIO5_DR &= ~(1<<3);
return 0;
}
static int led_off(void)
{
/* set gpio to let led off */
*GPIO5_DR |= (1<<3);
return 0;
}
#endif
led_ctrl_drv.c
#include "led_ctrl_drv.h"
#define HELLO_DRV_NAME "led_ctrl_drv"
#define MIN(a,b) (a < b ? a : b)
#define SIZE_KERNEL_BUF 1024
static int MAJOR;
static struct class *chr_cls;
struct device * dev;
static char kernel_buf[SIZE_KERNEL_BUF];
static int chrdev_open(struct inode *node, struct file *file)
{
printk("led_ctrl_drv open:%s:%s:%d\n",__FILE__,__func__,__LINE__);
if(led_init())
{
printk("%s:%s:%d:led_exit error \n",__FILE__,__func__,__LINE__);
return -ENAVAIL;
}
return 0;
}
static ssize_t chrdev_read(struct file *file, char __user *usr_buf, size_t size_usr, loff_t *off)
{
if(copy_to_user(usr_buf,kernel_buf,MIN(SIZE_KERNEL_BUF,size_usr)))
{
printk("copy_to_user err\n");
return -EIO;
}
printk("chrdev_read:%s:%s:%d\n",__FILE__,__func__,__LINE__);
return MIN(SIZE_KERNEL_BUF,size_usr);
}
static ssize_t chrdev_write(struct file *file, const char __user *usr, size_t size_usr, loff_t *off)
{
char val;
if(copy_from_user(&val, usr, MIN(sizeof(val),size_usr)))
{
printk("copy_from_user err\n");
return -EIO;
}else if(val)
{
if(!led_on())
{
printk("%s:%s:%d: led_on error\n",__FILE__,__func__,__LINE__);
return -EIO;
}
}else
{
if(!led_off())
{
printk("%s:%s:%d: led_off error\n",__FILE__,__func__,__LINE__);
return -EIO;
}
}
printk("chrdev_write:%s:%s:%d\n",__FILE__,__func__,__LINE__);
return MIN(SIZE_KERNEL_BUF,size_usr);
}
static int chrdev_close(struct inode * node, struct file * file)
{
if(led_exit()){
printk("%s:%s:%d:led_exit error \n",__FILE__,__func__,__LINE__);
return -ENAVAIL;
}
printk("chrdev_close:%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
static const struct file_operations led_ctrl_fops = {
.open = chrdev_open,
.read = chrdev_read,
.write = chrdev_write,
.release = chrdev_close,
};
static int __init hello_drv_init(void)
{
MAJOR = register_chrdev(0, HELLO_DRV_NAME,&led_ctrl_fops);
if(MAJOR < 0)
{
printk("register_chrdev error %s:%s:%d\n",__FILE__,__func__,__LINE__);
return MAJOR;
}
chr_cls = class_create(THIS_MODULE,HELLO_DRV_NAME);
if(IS_ERR(chr_cls))
{
printk("class create error\n");
return PTR_ERR(chr_cls);
}
dev = device_create(chr_cls, NULL,MKDEV(MAJOR, 0), NULL, HELLO_DRV_NAME);
if(IS_ERR(dev))
{
printk("device create error\n");
return PTR_ERR(dev);
}
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
static void __exit hello_drv_exit(void)
{
led_exit();
device_destroy(chr_cls, MKDEV(MAJOR, 0));
class_destroy(chr_cls);
unregister_chrdev(MAJOR, HELLO_DRV_NAME);
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}
module_init(hello_drv_init);
module_exit(hello_drv_exit);
MODULE_DESCRIPTION("led_ctrl driver");
MODULE_AUTHOR("psd 1614811057@qq.com");
MODULE_LICENSE("GPL");
usr_led_ctrl.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc,const char **argv)
{
int fd;
char status = 0;
if(argc != 3)
{
printf("Usage: %s <dev> <on|off>\n", argv[0]);
printf(" eg: %s /dev/myled on\n", argv[0]);
printf(" eg: %s /dev/myled off\n", argv[0]);
return -1;
}
fd = open(argv[1],O_RDWR);
if (fd < 0)
{
printf("can not open %s\n", argv[0]);
return -1;
}
if(!strcmp("on",argv[2]))
{
status = 1;
}
write(fd,&status,sizeof(status));
close(fd);
return 0;
}
3.2设备树方式
设备树的处理过程是: dtb -> device_node -> platform_device。
3.2.1设备树基础操作
路径:
设备树存放路径:
arch/arm/boot/dts
arch/arm/boot/dts/100ask_imx6ull-14x14.dts
内核文档 Documentation/devicetree/bindings/
操作:
系统运行后设备树解析信息存放路径
cd /sys/firmware/devicetree/base
/proc/device-tree/
字符串信息 使用cat
数字信息 使用hexdump
基础知识:
设备树属性就是键值对,键值对的组成格式如下:
文本字符串(以 null 结尾)用双引号表示:
string-property = "a string"; #字符串
'Cells' 是由尖括号分隔的 32 位无符号整数:
cell-property = <0xbeef 123 0xabcd1234>; #无符号32bit数
二进制数据用方括号分隔:
binary-property = [01 23 45 67]; #16进程表示的单字节的数
不同表示的数据可以使用逗号连接在一起:
mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
逗号也用于创建字符串列表:
string-list = "red fish", "blue fish";
1.设备树每个节点对应的都是device_node结构体
struct device_node {
const char *name; //节点名 "mynode"
const char *full_name; //节点全名 "mynode@0x12345678"
struct device_node *parent; //父节点
struct device_node *child; //子节点
struct device_node *sibling; //兄弟节点
struct property *properties;
//节点内的所有的属性通过property构成单链表,每一个键值对都对应一个property
};
2.属性结构体
struct property {
char *name; //键
int length; //值的长度 ,单位字节
void *value; //值的首地址
struct property *next; //指向下一个键值对
};
3.节点获取的函数
struct device_node *of_find_node_by_path(const char *path)
功能:通过路径获取节点
参数:
@path:路径 "/mynode@0x12345678"
返回值:成功返回节点首地址,失败返回NULL
struct device_node *of_find_node_by_name(struct device_node *from,
const char *name)
功能:通过名字获取节点
参数:
@from:NULL,从根节点开始解析
@name:节点名的字符串
返回值:成功返回节点首地址,失败返回NULL
3.2.2设备树节点特性
-
节点取别名
demo:demo@0x80000000{ //给demo@0x80000000取了别名,名字是demo }; aliases{ serial0=&uart4; };
3.2.3节点引用
demo:demo@0x80000000{ //给demo@0x80000000取了别名,名字是demo property1=value1; }; &demo{ //引用上述节点,会将上述节点内的属性继承过来, //将property2=value2;添加进demo节点 property2=value2; };
-
节点合并(同名节点合并)
3.2.3几个特殊属性
-
compatible属性
compatible = “厂商名,设备名”;
ethernet{ compatible = "3com,509"; }; 含有 compatile 属性的子节点会被解析转换成paltform_device结构体
-
reg属性
可寻址设备使用以下属性将地址信息编码到设备树中:
reg
#address-cells
#size-cells
每个可寻址设备都会得到一个
reg
元组列表,格式为reg = <address1 length1 [address2 length2] [address3 length3] ... >
。每个元组代表设备使用的地址范围。每个地址值都是一个或多个称为单元格的 32 位整数的列表。同样,长度值可以是单元格列表,也可以是空的。由于地址和长度字段都是可变大小的,因此父节点中的
#address-cells
和#size-cells
属性用于说明每个字段中有多少个单元格。或者换句话说,正确解释 reg 属性需要父节点的#address-cells 和#size-cells 值。要查看这一切是如何工作的,让我们将寻址属性添加到示例设备树中,从 CPU 开始。/ { #address-cells = <1>; #size-cells = <1>; serial@101f0000 { compatible = "arm,pl011"; reg = <0x101f0000 0x1000>; }; i2c1{ #address-cells = <1>; #size-cells = <0>; si7006@40{ reg = <0x40>; }; } };
-
节点内属性删除
/delete-property/键;
3.2.4节点获取函数
struct device_node *of_find_node_by_path(const char *path)
功能:通过路径获取节点
参数:
@path:路径 "/mynode@0x12345678"
返回值:成功返回节点首地址,失败返回NULL
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
功能:通过名字获取节点
参数:
@from:NULL,从根节点开始解析
@name:节点名的字符串
返回值:成功返回节点首地址,失败返回NULL
struct device_node *of_find_compatible_node(
struct device_node *from,
const char *type,
const char *compat)
功能:通过compatible获取节点
参数:
@from:NULL,从根节点开始解析
@type:NULL
@compat:设备树中compatible填写的成员
返回值:成功返回节点首地址,失败返回NULL
3.2.5属性获取函数
struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
功能:获取节点内的属性
参数:
@np:节点的指针
@name:属性名
@lenp:值的长度
返回值:成功返回property的结构体指针,失败返回NULL
static inline int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz)
功能: 得到属性值中指定标号的8位数组的值
参数:
@np - 设备节点指针
@propname - 属性名称
@out_values- 输出数据的首地址
@sz - 成员个数
返回值: 成功返回0,出错错误码
int of_property_read_u32_array(const struct device_node *np,
const char *propname,
u32 *out_values, size_t sz)
功能: 得到属性值中指定标号的32位数组的值
参数:
@np - 设备节点指针
@propname - 属性名称
@out_values- 输出数据的首地址
@sz - 成员个数
返回值: 成功返回0,出错错误码
int of_property_read_string(struct device_node *np, const char *propname,
const char **out_string)
功能:已知字符串属性健值对的名称,得到字符串的值
参数:
np -设备节点的地址
propname -属性健值对的名称
out_string -填充字符串的值
返回值:成功0,出错错误码
3.2.6设备树操作led代码部分
100ask:
#define GROUP_PIN(g,p) ((g<<16) | (p))
/ {
100ask_led@0 {
compatible = "100as,leddrv";
pin = <GROUP_PIN(3, 1)>;
};
100ask_led@1 {
compatible = "100as,leddrv";
pin = <GROUP_PIN(5, 8)>;
};
};
cpu灯:
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_leds>;
status = "disabled";
led0: cpu {
label = "cpu";
gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;
default-state = "on";
linux,default-trigger = "heartbeat";
};
};
dts:
psd_leds{
compatible = "psd,leds";
cpu_led {
compatible = "psd,cpu_led";
led1 = <&gpio5 3 0>;
//default-state = "off";
status = "disable";
};
gpio_leds{
compatible = "psd,gpio_leds";
led1 = <&gpio3 1 0>;
led2 = <&gpio5 8 0>;
status = "disable";
};
};
命令:
cd /home/psd/code_space/100ask_imx6ull-sdk/Linux-4.9.88
make dtbs
cp /home/psd/code_space/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dtb /home/psd/nfs_rootfs
过程:
自动创建设备节点的API
#include <linux/device.h>
/sys/class路径下创建目录name
struct class * class_create(owner, name)
功能:向上提交目录名
参数:
@owner:都填写为THIS_MODULE
@name:目录名
返回值:成功返回class结构体指针,失败返回错误码指针
/sys/class/name目录下创建文件
struct device *device_create(struct class *class,
struct device *parent,dev_t devt,
void *drvdata, const char *fmt, ...)
功能:向上提交创建节点的信息
参数:
@class:cls的结构体指针
@parent:一般填写为NULL
@devt:设备号
MKDEV(major,minor) //根据主次设备号合成设备号
MAJOR(devno) //根据设备号获取主设备号
MINOR(devno) //根据设备号获取次设备号
@drvdata:一般填写为NULL
@fmt,...:设备节点的名字 "mouse%d",i
返回值:成功返回结构体指针,失败返回错误码指针
void class_destroy(struct class *cls)
功能:销毁class
参数:
@cls:class结构体指针
返回值:无
void device_destroy(struct class *class, dev_t devt)
功能:销毁device
参数:
@cls:class结构体指针
@devt:设备号
返回值:无
最终,文件系统会将上面创建的信息重新在/dev目录下再创建一个类似的目录和文件
ISERR()判断是否是错误码 是则为真
PTR_ERR()返回错误码
if(IS_ERR(cls)){ //如果是错误IS_ERR返回真,否则返回假
printk("class create error\n");
return PTR_ERR(cls); //返回错误码
}
1.init();
注册字符设备register_chrdev(主设备号,设备名,文件操作结构体指针)
创建设备和驱动程序关联类class_create()
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
功能:设备的控制
参数:
@fd:文件描述符
@request:请求码
@...:可变参数,可写(地址),可不写
返回值:成功返回0,失败返回-1置位错误码
3.3 gpio子系统的API
struct device_node *of_find_node_by_path(const char *path)
功能:通过路径获取节点
参数:
@path:路径 "/mynode@0x12345678"
返回值:成功返回节点的指针,失败返回NULL
int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
功能:获取gpio的编号
参数:
@np:节点的指针
@propname:键名
@index:编号
返回值:成功返回gpio的编号,失败返回错误码
int gpio_request(unsigned gpio, const char *label)
功能:申请要使用的gpio
参数:
@gpio:gpio编号(设备树)
@label:名字(NULL)
返回值:成功返回0,失败返回错误码
int gpio_direction_input(unsigned gpio)
功能:设置管脚的方向为输入
参数:
@gpio:gpio编号
返回值:成功返回0,失败返回错误码
int gpio_direction_output(unsigned gpio, int value)
功能:设置管脚的方向为输出
参数:
@gpio:gpio编号
@value:1高 0低电平
返回值:成功返回0,失败返回错误码
void gpio_set_value(unsigned gpio, int value)
功能:设置电平的状态
参数:
@gpio:gpio编号
@value:1高 0低电平
返回值:无
int gpio_get_value(unsigned gpio)
功能:获取电平的状态
参数:
@gpio:gpio编号
返回值:1高电平,0低电平
void gpio_free(unsigned gpio)
功能:释放gpio
参数:
@gpio:gpio编号
返回值:无
成品:
kmd:
.h
#ifndef __LED_CTRL_H__
#define __LED_CTRL_H__
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#define CNAME "led_gpio_ctrl_drv"
#define LED_NUM 2
enum leds{
LED1,
LED2,
LED3
};
enum leds_status{
LED_ON,
LED_OFF
};
#define ON _IOW('a',1,int)
#define OFF _IOW('a',0,int)
#define GET_CMD_SIZE(cmd) ((cmd>>16) & 0x3fff)
#define MIN(a,b) (a < b ? a : b)
typedef struct led_ctrl {
char dev_name[20];
int major;
struct class * led_dir;
struct device * led_file;
struct device_node * led_device_node[20];
int gpiono[20];
int led_num;
// int (* led_ctrl_deinit) (void);
} led_ctrl_t;
static void KMD_ERR(const char *msg);
static int led_ctrl_init(void);
static int led_ctrl_deinit(void);
static int get_dt_data(void);
static int LedCtrl(const int which, const int status);
#endif
.c
#include"led_ctrl.h"
#include <linux/init.h>
#include <linux/module.h>
// psd_leds {
// compatible = "psd,psd_leds";
// cpu_led {
// compatible = "psd,cpu_led";
// led1 = <&gpio5 3 0>;
// //default-state = "off";
// status = "disable";
// };
// gpio_leds{
// compatible = "psd,gpio_leds";
// led1 = <&gpio3 1 0>;
// led2 = <&gpio5 8 0>;
// status = "disable";
// };
// };
led_ctrl_t psd_led_ctrl = {
.dev_name = CNAME,
.led_num = LED_NUM,
};
static int get_dt_data(void)
{
int ret;
psd_led_ctrl.led_device_node[0] = of_find_node_by_path("/psd_leds/cpu_led");
psd_led_ctrl.led_device_node[1] = of_find_node_by_path("/psd_leds/gpio_leds");
if((psd_led_ctrl.led_device_node[0] == NULL) || (psd_led_ctrl.led_device_node[1] == NULL))
{
KMD_ERR("of_find_node_by_path error");
return -ENODATA;
}
psd_led_ctrl.gpiono[0] = of_get_named_gpio(psd_led_ctrl.led_device_node[0],"led1",0);
psd_led_ctrl.gpiono[1] = of_get_named_gpio(psd_led_ctrl.led_device_node[1],"led1",0);
psd_led_ctrl.gpiono[2] = of_get_named_gpio(psd_led_ctrl.led_device_node[1],"led2",0);
if((psd_led_ctrl.gpiono[0] < 0) || (psd_led_ctrl.gpiono[1] < 0) || (psd_led_ctrl.gpiono[2] < 0))
{
KMD_ERR("of_get_named_gpio error");
return -ENODATA;
}
if ((ret = gpio_request(psd_led_ctrl.gpiono[0], NULL)) < 0) {
KMD_ERR("gpio_request psd_led_ctrl.gpiono[0] error");
return ret;
}
if ((ret = gpio_request(psd_led_ctrl.gpiono[1], NULL)) < 0) {
KMD_ERR("gpio_request psd_led_ctrl.gpiono[1] error");
return ret;
}
// if ((ret = gpio_request(psd_led_ctrl.gpiono[2], NULL)) < 0) {
// KMD_ERR("gpio_request psd_led_ctrl.gpiono[2] error");
// return ret;
// }
if (((ret = gpio_direction_output(psd_led_ctrl.gpiono[0], 1)) < 0) ||
((ret = gpio_direction_output(psd_led_ctrl.gpiono[1], 1)) < 0) ) {
KMD_ERR("gpio_direction_output error");
return ret;
}
return 0;
}
static int LedCtrl(const int which,const int status)
{
int led_num = which;
switch(led_num)
{
case LED1:
gpio_set_value(psd_led_ctrl.gpiono[0], status);
break;
case LED2:
gpio_set_value(psd_led_ctrl.gpiono[1], status);
break;
// case LED3:
// gpio_set_value(psd_led_ctrl.gpiono[2], status);
break;
}
return 0;
}
int psd_led_open(struct inode * node, struct file * file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
long psd_led_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
int ret;
int which;
if(copy_from_user(&which, (void *)arg, GET_CMD_SIZE(arg)))
{
KMD_ERR("copy_from_user ERROR");
ret = -EFAULT;
}
switch(cmd)
{
case ON:
LedCtrl(which, LED_ON);
break;
case OFF:
LedCtrl(which, LED_OFF);
break;
default: // 处理未知命令
ret = -EINVAL;
break;
}
return ret;
}
int psd_led_release(struct inode * node, struct file * file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
static const struct file_operations psd_led_file_operations = {
.open = psd_led_open,
.unlocked_ioctl = psd_led_ioctl,
.release = psd_led_release,
};
static int led_ctrl_init(void)
{
//register chrdev
psd_led_ctrl.major = register_chrdev(0,psd_led_ctrl.dev_name,&psd_led_file_operations);
if(psd_led_ctrl.major < 0)
{
KMD_ERR("register_chrdev error");
return -EBUSY;
}
psd_led_ctrl.led_dir = class_create(THIS_MODULE, psd_led_ctrl.dev_name);
if(IS_ERR(psd_led_ctrl.led_dir))
{
KMD_ERR("class_create Error");
goto ERR1;
}
psd_led_ctrl.led_file = device_create(psd_led_ctrl.led_dir, NULL,
MKDEV(psd_led_ctrl.major, 0), NULL,psd_led_ctrl.dev_name);
if(IS_ERR(psd_led_ctrl.led_file))
{
KMD_ERR("device_create Error");
goto ERR2;
}
return 0;
ERR2:
class_destroy(psd_led_ctrl.led_dir);
ERR1:
unregister_chrdev(psd_led_ctrl.major, psd_led_ctrl.dev_name);
return PTR_ERR(psd_led_ctrl.led_dir);
}
static int led_ctrl_deinit(void)
{
int i;
device_destroy(psd_led_ctrl.led_dir, MKDEV(psd_led_ctrl.major, 0));
class_destroy(psd_led_ctrl.led_dir);
unregister_chrdev(psd_led_ctrl.major, psd_led_ctrl.dev_name);
for(i = 0; i < LED_NUM; i++)
{
gpio_set_value(psd_led_ctrl.gpiono[i], 0);
gpio_free(psd_led_ctrl.gpiono[i]);
}
return 0;
}
static void KMD_ERR(const char *msg)
{
printk(KERN_ERR "Error: %s\n", msg);
}
static int __init led_gpio_ctrl_init(void)
{
int ret;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
ret = led_ctrl_init();
if(ret)
{
return ret;
}
ret = get_dt_data();
if(ret)
{
return ret;
}
// gpio_set_value(psd_led_ctrl.gpiono[1], 0);
// gpio_set_value(psd_led_ctrl.gpiono[0], 1);
return 0;
}
static void __exit led_gpio_ctrl_exit(void)
{
led_ctrl_deinit();
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}
module_init(led_gpio_ctrl_init);
module_exit(led_gpio_ctrl_exit);
MODULE_LICENSE("GPL");
usr:
.h
#ifndef __LED_H__
#define __LED_H__
#define LED_NUM 2
enum leds{
LED1,
LED2,
LED3
};
#define ON _IOW('a',1,int)
#define OFF _IOW('a',0,int)
enum leds_status{
LED_ON,
LED_OFF
};
#endif
.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include "led.h"
#define LED_DEVICE "/dev/led_gpio_ctrl_drv"
int main(int argc,const char **argv)
{
int fd;
char status = 0;
if(argc != 3)
{
printf(" eg: ./a.out ledx on/off\n");
return -1;
}
fd = open(LED_DEVICE,O_RDWR);
if (fd < 0)
{
printf("can not open %s\n", argv[0]);
return -1;
}
if((!strcmp("led1",argv[1])) || (!strcmp("on",argv[2])) )
{
if(ioctl(fd, ON, LED1))
{
printf("IOCTL ERROR \n");
return -1;
}
}else if((!strcmp("led1",argv[1])) || (!strcmp("off",argv[2])))
{
if(ioctl(fd, OFF, LED1))
{
printf("IOCTL ERROR \n");
return -1;
}
}
if((!strcmp("led2",argv[1])) || (!strcmp("on",argv[2])) )
{
if(ioctl(fd, ON, LED2))
{
printf("IOCTL ERROR \n");
return -1;
}
}else if((!strcmp("led2",argv[1])) || (!strcmp("off",argv[2])))
{
if(ioctl(fd, OFF, LED2))
{
printf("IOCTL ERROR \n");
return -1;
}
}
close(fd);
return 0;
}