437 lines
11 KiB
C
Executable File
437 lines
11 KiB
C
Executable File
#include <linux/kernel.h>
|
||
#include <linux/module.h>
|
||
#include <linux/platform_device.h>
|
||
#include <linux/time.h>
|
||
#include <linux/timer.h>
|
||
#include <linux/fs.h>
|
||
#include <linux/kobject.h>
|
||
#include <linux/of_gpio.h>
|
||
#include <linux/of_device.h>
|
||
#include <linux/regulator/consumer.h>
|
||
#include <linux/regmap.h>
|
||
#include <linux/slab.h>
|
||
#include <linux/string.h>
|
||
#include <linux/unistd.h>
|
||
#include <linux/vmalloc.h>
|
||
#include <linux/workqueue.h>
|
||
#include <linux/miscdevice.h>
|
||
#include <linux/uaccess.h>
|
||
#include <asm/uaccess.h>
|
||
#include <mydebug.h>
|
||
|
||
#define GPIO_NUM_MAX 4
|
||
|
||
struct gpio_info{
|
||
char name[64];
|
||
int pin;
|
||
int direction;
|
||
int out_def;
|
||
};
|
||
|
||
static struct gpio_info all_gpio_pin[GPIO_NUM_MAX]={
|
||
{
|
||
.name = "mcu_boot",
|
||
.pin = MCU_BOOT,
|
||
.direction = GPIO_DIR_OUT,
|
||
.out_def = GPIO_OUT_LOW,
|
||
},
|
||
{
|
||
.name = "mcu_reset",
|
||
.pin = MCU_RESET,
|
||
.direction = GPIO_DIR_OUT,
|
||
.out_def = GPIO_OUT_LOW,
|
||
},
|
||
{
|
||
.name = "mcu_pwr",
|
||
.pin = MCU_PWR,
|
||
.direction = GPIO_DIR_OUT,
|
||
.out_def = GPIO_OUT_LOW,
|
||
},
|
||
{
|
||
.name = "mcu_uart_pwr",
|
||
.pin = MCU_UART_PWR,
|
||
.direction = GPIO_DIR_OUT,
|
||
.out_def = GPIO_OUT_LOW,
|
||
},
|
||
};
|
||
|
||
|
||
static void rs_gpio_request(void)
|
||
{
|
||
int ret;
|
||
int index;
|
||
for(index=0; index<GPIO_NUM_MAX; index++){
|
||
if (gpio_is_valid(all_gpio_pin[index].pin)) {
|
||
ret = gpio_request(all_gpio_pin[index].pin, all_gpio_pin[index].name);
|
||
if (ret < 0) {
|
||
KERNEL_INFO("Unable to request gpio%d ret=%d\n", all_gpio_pin[index].pin, ret);
|
||
gpio_free(all_gpio_pin[index].pin);
|
||
}
|
||
|
||
if(all_gpio_pin[index].direction == GPIO_DIR_IN){
|
||
ret = gpio_direction_input(all_gpio_pin[index].pin);
|
||
if (ret < 0) {
|
||
KERNEL_INFO("Unable to set direction in for gpio%d ret=%d\n", all_gpio_pin[index].pin, ret);
|
||
gpio_free(all_gpio_pin[index].pin);
|
||
}
|
||
} else {
|
||
ret = gpio_direction_output(all_gpio_pin[index].pin, all_gpio_pin[index].out_def);
|
||
if (ret < 0) {
|
||
KERNEL_INFO("Unable to set direction out for gpio%d ret=%d\n", all_gpio_pin[index].pin, ret);
|
||
gpio_free(all_gpio_pin[index].pin);
|
||
}
|
||
}
|
||
|
||
ret = gpio_export(all_gpio_pin[index].pin, 1); // /sys/class/gpio/gpiox/
|
||
if (ret < 0) {
|
||
KERNEL_INFO("Unable to export gpio%d ret=%d\n", all_gpio_pin[index].pin, ret);
|
||
gpio_free(all_gpio_pin[index].pin);
|
||
}
|
||
KERNEL_INFO("request gpio%d success ret=%d\n", all_gpio_pin[index].pin, ret);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void rs_gpio_free(void)
|
||
{
|
||
int index;
|
||
for(index=0; index<GPIO_NUM_MAX; index++){
|
||
if (gpio_is_valid(all_gpio_pin[index].pin)) {
|
||
gpio_unexport(all_gpio_pin[index].pin);
|
||
gpio_free(all_gpio_pin[index].pin);
|
||
KERNEL_INFO("free gpio%d success\n", all_gpio_pin[index].pin);
|
||
}
|
||
}
|
||
}
|
||
|
||
void rs_gpio_set(int pin, int value)
|
||
{
|
||
int index;
|
||
for(index=0; index<GPIO_NUM_MAX; index++){
|
||
if(all_gpio_pin[index].pin != pin)
|
||
continue;
|
||
if (gpio_is_valid(all_gpio_pin[index].pin)) {
|
||
gpio_set_value(all_gpio_pin[index].pin, value);
|
||
KERNEL_INFO("name:%s %d", all_gpio_pin[index].name, value);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
EXPORT_SYMBOL(rs_gpio_set);
|
||
|
||
int rs_gpio_get(int pin)
|
||
{
|
||
int index;
|
||
int value = -1;
|
||
for(index=0; index<GPIO_NUM_MAX; index++){
|
||
if(all_gpio_pin[index].pin != pin)
|
||
continue;
|
||
if (gpio_is_valid(all_gpio_pin[index].pin)) {
|
||
value = gpio_get_value(all_gpio_pin[index].pin);
|
||
}
|
||
break;
|
||
}
|
||
KERNEL_INFO("name:%s %d", all_gpio_pin[index].name, value);
|
||
return value;
|
||
}
|
||
EXPORT_SYMBOL(rs_gpio_get);
|
||
|
||
|
||
int gpio_index = 0;
|
||
// 当用户试图从设备空间读取数据时,调用这个函数
|
||
ssize_t rs_gpio_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
|
||
{
|
||
ssize_t cnt = 0;
|
||
char send_msg[64]={0};
|
||
|
||
if(gpio_index >= GPIO_NUM_MAX){
|
||
KERNEL_INFO("ERROR gpio=%d when reading!!\n", gpio_index);
|
||
return -EFAULT;
|
||
}
|
||
|
||
sprintf(send_msg, "%u\n", gpio_get_value(all_gpio_pin[gpio_index].pin));;
|
||
cnt = copy_to_user(buf, send_msg, sizeof(send_msg));// 将内核空间的数据copy到用户空间
|
||
if(cnt){
|
||
KERNEL_INFO("ERROR occur when reading!!\n");
|
||
return -EFAULT;
|
||
}
|
||
KERNEL_INFO("name:%s %d", all_gpio_pin[gpio_index].name, gpio_get_value(all_gpio_pin[gpio_index].pin));
|
||
return cnt;
|
||
}
|
||
|
||
// 当用户往设备文件写数据时,调用这个函数
|
||
// counter 10 intervalgpio 30 intervaladc 30 bufsize 2048 gpiopin 总个数 0 122 124 threshold
|
||
ssize_t rs_gpio_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
|
||
{
|
||
int gpio = 0;
|
||
KERNEL_INFO("%s\n", ubuf);
|
||
if(!strncmp(ubuf, "SETGPIO ", 8) && count > 8){ // SETGPIO 1 1
|
||
gpio = ubuf[8];
|
||
if(gpio < GPIO_NUM_MAX){
|
||
if (gpio_is_valid(all_gpio_pin[gpio].pin)) {
|
||
if(count > 10){
|
||
gpio_set_value(all_gpio_pin[gpio].pin, ubuf[10]);
|
||
} else {
|
||
KERNEL_INFO("error pin=%d", all_gpio_pin[gpio].pin);
|
||
}
|
||
}
|
||
} else {
|
||
KERNEL_INFO("error gpio=%d", gpio);
|
||
}
|
||
|
||
}else if(!strncmp(ubuf, "GETGPIO ", 8) && count > 8){ // GETGPIO 1
|
||
gpio = ubuf[8];
|
||
if(gpio < GPIO_NUM_MAX){
|
||
gpio_index = gpio;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
|
||
|
||
// File opertion 结构体,我们通过这个结构体建立应用程序到内核之间操作的映射
|
||
static struct file_operations file_oprts = {
|
||
.owner = THIS_MODULE,
|
||
.read = rs_gpio_read,
|
||
.write = rs_gpio_write,
|
||
};
|
||
|
||
#define DEVICE_NAME "rsgpio" // Device 名称,对应/dev下的目录名称
|
||
static struct miscdevice rs_gpio_misc_device = {
|
||
.minor = MISC_DYNAMIC_MINOR,
|
||
.name = DEVICE_NAME,
|
||
.fops = &file_oprts
|
||
};
|
||
|
||
/*
|
||
static int rs_gpio_suspend(void)
|
||
{
|
||
bright = 0;
|
||
printk("bright %d gpio_get_value(88) = %d \n",bright,gpio_get_value(88));
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int rs_gpio_resume(void)
|
||
{
|
||
bright = 1;
|
||
printk("bright %d gpio_get_value(88) = %d \n",bright,gpio_get_value(88));
|
||
return 0;
|
||
}
|
||
|
||
static struct dev_pm_ops rs_gpio_pm_ops = {
|
||
.suspend = rs_gpio_suspend,
|
||
.resume = rs_gpio_resume,
|
||
};
|
||
*/
|
||
|
||
static void mcu_power_on(void)
|
||
{
|
||
// Init MCU_Boot = 0 && MCU_Reset = 1'b0
|
||
if (gpio_is_valid(MCU_BOOT)) {
|
||
gpio_direction_output(MCU_BOOT, GPIO_OUT_LOW);
|
||
}
|
||
|
||
if (gpio_is_valid(MCU_RESET)) {
|
||
gpio_direction_output(MCU_RESET, GPIO_OUT_LOW);
|
||
}
|
||
|
||
// Enable MCU 3.3
|
||
if (gpio_is_valid(MCU_PWR)) {
|
||
gpio_direction_output(MCU_PWR, GPIO_OUT_HIGH);
|
||
msleep(20);
|
||
}
|
||
|
||
// Enable OE of AW3911 - Level shifter
|
||
if (gpio_is_valid(MCU_UART_PWR)) {
|
||
gpio_direction_output(MCU_UART_PWR, GPIO_OUT_HIGH);
|
||
msleep(20);
|
||
}
|
||
|
||
if (gpio_is_valid(MCU_RESET)) {
|
||
gpio_direction_output(MCU_RESET, GPIO_OUT_HIGH);
|
||
}
|
||
}
|
||
|
||
static void mcu_power_off(void)
|
||
{
|
||
//rs_gpio_set(MCU_PWR, GPIO_OUT_LOW);
|
||
if (gpio_is_valid(MCU_PWR)) {
|
||
gpio_direction_output(MCU_PWR, GPIO_OUT_LOW);
|
||
}
|
||
//rs_gpio_set(MCU_UART_PWR, GPIO_OUT_LOW);
|
||
if (gpio_is_valid(MCU_UART_PWR)) {
|
||
gpio_direction_output(MCU_UART_PWR, GPIO_OUT_LOW);
|
||
}
|
||
if (gpio_is_valid(MCU_BOOT)) {
|
||
gpio_direction_output(MCU_BOOT, GPIO_OUT_LOW);
|
||
}
|
||
if (gpio_is_valid(MCU_RESET)) {
|
||
gpio_direction_output(MCU_RESET, GPIO_OUT_LOW);
|
||
}
|
||
}
|
||
/*
|
||
// android_print 表示Android层LOG打印开关
|
||
#ifdef CONFIG_ANDROID_LOG_CLOSED
|
||
static char android_print = 0; // 0表示不打印(user版本的默认不打印)
|
||
#else
|
||
static char android_print = 1; // 1表示打印
|
||
#endif
|
||
#define LOG_SEEK_OFFSET 8*1024*1024
|
||
#define LOG_PATH "/dev/block/sde65"
|
||
static int rwdev(char* buf, int len, int flag) {
|
||
struct file *log_filp;
|
||
mm_segment_t old_fs;
|
||
int length=0;
|
||
log_filp=filp_open(LOG_PATH,O_RDWR,0);
|
||
if(IS_ERR(log_filp)){
|
||
KERNEL_ERROR("open %s err:%d !\n",LOG_PATH,PTR_ERR(log_filp));
|
||
return -1;
|
||
}
|
||
|
||
old_fs = get_fs();
|
||
set_fs(KERNEL_DS);
|
||
log_filp->f_op->llseek(log_filp, LOG_SEEK_OFFSET, SEEK_SET);
|
||
if(flag==0)
|
||
length=vfs_read(log_filp, buf, len, &log_filp->f_pos);
|
||
else
|
||
length=vfs_write(log_filp, buf, len, &log_filp->f_pos);
|
||
set_fs(old_fs);
|
||
return length;
|
||
}
|
||
|
||
static int get_log_enable(void) {
|
||
char buf[2];
|
||
int length = rwdev(buf, 2, 0);
|
||
if(length < 2) {
|
||
KERNEL_ERROR("get_log_enable %s err!\n",LOG_PATH);
|
||
return -1;
|
||
}
|
||
if(buf[0] == '1') {
|
||
if(buf[1] == '1') {
|
||
return 1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
} else {
|
||
return android_print;
|
||
}
|
||
}
|
||
|
||
static int write_log_enable(char is_enable) {
|
||
char buf[2] = {'1',is_enable};
|
||
int length = rwdev(buf, 2, 1);
|
||
if(length < 2) {
|
||
KERNEL_ERROR("write_log_enable %s err!\n",LOG_PATH);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
*/
|
||
// 添加节点控制
|
||
static ssize_t sysfs_set_driver_ctl(struct device *dev, struct device_attribute *attr, const char *ubuf, size_t size)
|
||
{
|
||
KERNEL_ERROR("cmd_write:%s", ubuf);
|
||
if(!strncmp(ubuf, "mcupower ", 9) && size > 9){
|
||
if(ubuf[9] == '1'){
|
||
// 打开MCU的电
|
||
mcu_power_on();
|
||
} else {
|
||
// 关掉MCU的电
|
||
mcu_power_off();
|
||
}
|
||
|
||
} else if(!strncmp(ubuf, "logenable ", 10) && size > 10){
|
||
if(ubuf[10] == '1'){
|
||
//write_log_enable('1');
|
||
} else {
|
||
//write_log_enable('0');
|
||
}
|
||
} else {
|
||
KERNEL_ERROR(KERN_ERR "muxcmd error for cmd %s", ubuf);
|
||
}
|
||
|
||
return size;
|
||
}
|
||
|
||
static ssize_t sysfs_get_driver_ctl(struct device *dev, struct device_attribute *attr, char *ubuf)
|
||
{
|
||
ssize_t size = 0;//sprintf(ubuf, "%d", get_log_enable());
|
||
KERNEL_ERROR("cmd_read: size=%d, ubuf=%s", size, ubuf);
|
||
return size;
|
||
}
|
||
|
||
static DEVICE_ATTR(driver_ctl, 0644, sysfs_get_driver_ctl, sysfs_set_driver_ctl);
|
||
|
||
static void rs_gpio_shutdown(struct platform_device *device)
|
||
{
|
||
KERNEL_INFO("done!");
|
||
}
|
||
|
||
|
||
static int rs_gpio_probe(struct platform_device *pdev)
|
||
{
|
||
struct device *dev = &pdev->dev;
|
||
int ret = misc_register(&rs_gpio_misc_device);
|
||
if (ret) {
|
||
KERNEL_INFO("rs_gpio: Unable to register misc device ret=%d\n", ret);
|
||
}
|
||
|
||
rs_gpio_request();
|
||
device_create_file(dev, &dev_attr_driver_ctl);
|
||
|
||
//mcu_power_on();
|
||
KERNEL_INFO("\n");
|
||
return ret;
|
||
}
|
||
|
||
|
||
|
||
static int __exit rs_gpio_remove(struct platform_device *pdev)
|
||
{
|
||
struct device *dev = &pdev->dev;
|
||
KERNEL_INFO("\n");
|
||
rs_gpio_free();
|
||
misc_deregister(&rs_gpio_misc_device);
|
||
device_remove_file(dev, &dev_attr_driver_ctl);
|
||
return 0;
|
||
}
|
||
|
||
static struct platform_driver rs_gpio_driver = {
|
||
|
||
.driver = {
|
||
.owner = THIS_MODULE,
|
||
.name = DEVICE_NAME,
|
||
//.pm = &rs_gpio_pm_ops,
|
||
},
|
||
.shutdown = rs_gpio_shutdown,
|
||
.probe = rs_gpio_probe,
|
||
.remove = rs_gpio_remove,
|
||
};
|
||
|
||
static struct platform_device rs_gpio_dev = {
|
||
.name = DEVICE_NAME,
|
||
.id = -1,
|
||
};
|
||
|
||
int __init rs_gpio_init(void)
|
||
{
|
||
platform_device_register(&rs_gpio_dev);
|
||
platform_driver_register(&rs_gpio_driver);
|
||
KERNEL_INFO("driver init success!!\n");
|
||
return 0;
|
||
}
|
||
|
||
void __exit rs_gpio_exit(void)
|
||
{
|
||
platform_device_unregister(&rs_gpio_dev);
|
||
platform_driver_unregister(&rs_gpio_driver);
|
||
KERNEL_INFO("driver %s removed\n", DEVICE_NAME);
|
||
}
|
||
|
||
module_init(rs_gpio_init);
|
||
module_exit(rs_gpio_exit);
|
||
|
||
MODULE_AUTHOR("kevin");
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_DESCRIPTION("GPIO driver"); |