LDE DRIVER 亲试有效
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#define DTS_PLATFROM_LED_CNT 1
#define DTS_PLATFROM_LED_NAME “led-wqw”
#define GPIO_DEVICE_NUM 3
struct gpioled_dev
{
dev_t devid; /* 设备号 /
int major; / 主设备号 /
int minor; / 次设备号 /
struct cdev cdev; / 字符设备 */
struct class class; / 类 */
struct device_node nd; / 设备节点 */
struct device device; / 设备 */
int led_gpio[GPIO_DEVICE_NUM];
};
struct gpioled_dev gpioled;
/*
static ssize_t gpioled_read(struct file *pfile,
char __user *buf, size_t cnt, loff_t *ppos){
int ret = 0,i;
unsigned char databuf[8]={0},val;
struct gpioled_dev *dev = pfile->private_data;printk("-----------%s-%d-----\n", __FUNCTION__,__LINE__);
printk("copy_to_user minor=%d!\r\n",minor);
for(i=0;i<GPIO_DEVICE_NUM;i++){val=gpio_get_value(dev->led_gpio[i]);databuf[0]=(databuf[0]<<(8-i))
}
ret = copy_to_user(databuf, databuf, 1);
if(ret < 0) {printk("copy_to_user failed!\r\n");return -EINVAL;
}
return ret;
}
*/
static ssize_t gpioled_write(struct file *pfile,
const char __user *buf, size_t cnt, loff_t *ppos){
int ret = 0,led_num,led_val;
unsigned char databuf[32]={0};
struct gpioled_dev *dev = pfile->private_data;ret = copy_from_user(databuf, buf, cnt);
if(ret < 0) {printk("copy_from_user failed!\r\n");return -EINVAL;
}led_num = databuf[0]-48;
led_val = databuf[1]-48;
printk("wqw copy_from_user led_num =%d----led_val=%d\r\n",led_num,led_val);gpio_set_value(dev->led_gpio[led_num], led_val);
return ret;
}
static int gpioled_open(struct inode *inode, struct file *pfile){
pfile->private_data = &gpioled;
return 0;
}
int gpioled_release(struct inode *pinode, struct file *pfile){
return 0;
}
static const struct file_operations gpioled_fops= {
.owner = THIS_MODULE,
.write = gpioled_write,
.open = gpioled_open,
.release = gpioled_release,
};
static int led_init(void){
int ret = 0,i;
enum of_gpio_flags flag;
/* 获取设备节点 /
gpioled.nd = of_find_node_by_path("/wqw-gpioled");
if (gpioled.nd == NULL) { / 失败 */
ret = -EINVAL;
}
printk(“find dts node successful!\r\n”);
for(i = 0;i < GPIO_DEVICE_NUM;i++)
{gpioled.led_gpio[i] = of_get_named_gpio_flags(gpioled.nd, "gpios", i, &flag);printk("<%s,%d>\r\n",__FUNCTION__,__LINE__);printk("LINE =%d debug:invalid gpio,gpio=%d\n", __LINE__,gpioled.led_gpio[i]);if(!gpio_is_valid(gpioled.led_gpio[i])){printk("LINE =%d debug:invalid gpio,gpio=%d\n", __LINE__,gpioled.led_gpio[i]);}gpio_direction_output(gpioled.led_gpio[i],1);if(gpio_request(gpioled.led_gpio[i], "led-gpio")){printk("<%s,%d>\r\n",__FUNCTION__,__LINE__);printk("debug:error pwm_gpio_init--%d\n",i);}
}
return ret;
}
/*
/{
wqw-gpioled {
compatible = “wqw-gpioled”;
gpios = <&msm_gpio 90 0x0>,
<&msm_gpio 92 0x0>,
<&msm_gpio 93 0x0>;
status = “okay”;
};
};
*/
static int gpioled_probe(struct platform_device *pla_dev){
int ret = 0;
printk("led driver and device was matched!\r\n");
/* 1,申请设备号 */
gpioled.major = 0; /* 设备号由内核分配 */
if(gpioled.major) { /* 定义了设备号 */gpioled.devid = MKDEV(gpioled.major, 0);ret = register_chrdev_region(gpioled.devid, DTS_PLATFROM_LED_CNT, DTS_PLATFROM_LED_NAME);
} else { /* 没有给定设备号 */ret = alloc_chrdev_region(&gpioled.devid, 0, DTS_PLATFROM_LED_CNT, DTS_PLATFROM_LED_NAME);gpioled.major = MAJOR(gpioled.devid);gpioled.minor = MINOR(gpioled.devid);
}
if(ret < 0) {goto fail_devid;
}
printk("gpioled_devid = %d\r\n", gpioled.devid);
printk("gpioled_major = %d\r\n", gpioled.major);
printk("gpioled_minor = %d\r\n", gpioled.minor);/* 2,添加字符设备 */
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
ret = cdev_add(&gpioled.cdev, gpioled.devid, DTS_PLATFROM_LED_CNT);
if(ret < 0) goto fail_cdev;
printk("cdev_add successful!\r\n");/* 3,自动创建设备节点 */
gpioled.class = class_create(THIS_MODULE, DTS_PLATFROM_LED_NAME);
if (IS_ERR(gpioled.class)) {ret = PTR_ERR(gpioled.class);goto fail_class;
}
printk("class_create successful!\r\n");gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, DTS_PLATFROM_LED_NAME);
if (IS_ERR(gpioled.device)) {ret = PTR_ERR(gpioled.device);goto fail_device;
}
printk("device_create successful!\r\n");
led_init();return 0;
fail_device:
class_destroy(gpioled.class);
fail_class:
cdev_del(&gpioled.cdev);
fail_cdev:
unregister_chrdev_region(gpioled.devid, DTS_PLATFROM_LED_CNT);
fail_devid:
return ret;
}
static int gpioled_remove(struct platform_device *pla_dev){
int i = 0;
for(i = 0;i < GPIO_DEVICE_NUM;i++)
{/* 关灯 */gpio_set_value(gpioled.led_gpio[i], 1);/* 释放IO */gpio_free(gpioled.led_gpio[i]);
}/* 删除字符设备 */
cdev_del(&gpioled.cdev);
printk("cdev_del successful!\r\n");/* 释放设备号 */
unregister_chrdev_region(gpioled.devid, DTS_PLATFROM_LED_CNT);
printk("unregister chrdev successful!\r\n");/* 摧毁设备*/
device_destroy(gpioled.class, gpioled.devid);
printk("device_destroy successful!\r\n");/* 摧毁类 */
class_destroy(gpioled.class);
printk("class_destroy successful!\r\n");
return 0;
}
/* 匹配列表 /
static const struct of_device_id led_of_match[] = {
{ .compatible = “wqw-gpioled” },
{ / Sentinel */ }
};
struct platform_driver dts_led_platfrom = {
.driver = {
.name = “wqw-gpioled”, /* 驱动名字,用于和设备匹配 /
.of_match_table = led_of_match, / 设备树匹配表 */
},
.probe = gpioled_probe,
.remove = gpioled_remove,
};
static int __init gpioled_init(void){
return platform_driver_register(&dts_led_platfrom);
}
static void __exit gpioled_exit(void){
platform_driver_unregister(&dts_led_platfrom);
}
module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“wqw”);