#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhanglong");
/*
*板子上的led1,2,4,8分别对应连在GPF4,5,6,7上
*
*GPFCON ==> 0x56000050
*GPFDAT ==> 0x56000054
*GPFUP ==> 0x56000058
*
*/
#define IO_PHYS 0x56000000
#define GPFCON_OFFSET 0x50
#define GPFDAT_OFFSET 0x54
#define GPFUP_OFFSET 0x58
struct led_device_s {
struct cdev dev;
dev_t no;
struct class_device *led_classdev;
short *io_gpfcon;
char *io_gpfdat;
char *io_gpfup;
};
struct led_device_s my_led;
void zl_release(struct device *dev)
{
printk("device release.\n");
}
struct resource zl_res[] = {
{
.start = IO_PHYS,
.end = IO_PHYS + SZ_4K,
.flags = IORESOURCE_IO,
},
#if 0
{
.start = IRQ_EINT2,
.end = IRQ_EINT2,
.flags = IORESOURCE_IRQ,
},
#endif
};
struct platform_device zl_dev = {
.name = "zl_device", //this string will be show as /sys/devices/platform/zl_device.0
.dev = {
.release = zl_release,
.platform_data = &my_led,
},
.num_resources = ARRAY_SIZE(zl_res),
.resource = zl_res,
};
unsigned int devnum = 0;
struct class *my_class;
ssize_t my_read(struct file *fp, char __user *buf, size_t count, loff_t *off)
{
return 4; //不能返回0, 否则读相关设备时会卡住.
}
ssize_t my_write(struct file *fp, const char __user *buf, size_t count, loff_t *off)
{
struct led_device_s *get = fp->private_data;
unsigned int minor;
char value;
copy_from_user(&value, buf, 1);
#if 0
minor = MINOR(get->no);
*(get->io_gpfcon) &= ~(3 << ((minor * 2) + 8));
*(get->io_gpfcon) |= (1 << ((minor * 2) + 8));
*(get->io_gpfup) &= ~(1 << (minor + 4));
if((*buf == 0) || (*buf == 48)) {
*(get->io_gpfdat) &= ~(1 << (minor + 4));
} else {
*(get->io_gpfdat) |= (1 << (minor + 4));
}
#else
*(get->io_gpfcon) &= ~(0xff << 8);
*(get->io_gpfcon) |= (0x55 << 8);
*(get->io_gpfup) &= ~(0xf0);
if((value >= 0) && (value <= 15)) {
*(get->io_gpfdat) = ((value & 0xf) << 4);
} else if((value >= '0') && (value <= '9')){
*(get->io_gpfdat) = ((value & 0xf) << 4);
} else if((value >= 'a') && (value <= 'f')){
*(get->io_gpfdat) = ((value - 97 + 10) << 4);
} else if((value >= 'A') && (value <= 'F')){
*(get->io_gpfdat) = ((value - 65 + 10) << 4);
} else {
}
#endif
return 4;
}
int my_open(struct inode *no, struct file *fp)
{
fp->private_data = container_of(no->i_cdev, struct led_device_s, dev);
//printk(" kernel: open.\n");
return 0;
}
int my_release(struct inode *no, struct file *fp)
{
//printk(" kernel: release.\n");
return 0;
}
struct file_operations my_ops = {
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
int zl_probe(struct device * dev)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
struct resource *res = pdev->resource;
struct led_device_s *pled = pdev->dev.platform_data;
int ret = 0;
char *virt = NULL;
u32 i;
for(i = 0; i < pdev->num_resources; i++) {
if(IORESOURCE_IO & res[i].flags) {
virt = ioremap(res[i].start, (res[i].end - res[i].start));
break;
}
}
if(virt == NULL){
printk("[%s-L%d]:virt == NULL\n", __func__, __LINE__);
return -1;
}
pled->io_gpfcon = virt + GPFCON_OFFSET;
pled->io_gpfdat = virt + GPFDAT_OFFSET;
pled->io_gpfup = virt + GPFUP_OFFSET;
pled->no = MKDEV(52, devnum);
cdev_init(&pled->dev, &my_ops);
cdev_add(&pled->dev, pled->no, 1);
pled->led_classdev = device_create(my_class, NULL, pled->no, &(pled->dev), "mydev_led.%d", devnum);
if(IS_ERR(pled->led_classdev)) {
printk("[%s-L%d]: device_create.\n", __func__, __LINE__);
return PTR_ERR(pled->led_classdev);
}
devnum++;
printk("driver zl_probe.\n");
return ret;
}
int zl_remove(struct device * dev)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
struct led_device_s *pled = pdev->dev.platform_data;
device_destroy(my_class, pled->no);
cdev_del(&pled->dev);
printk("driver zl_remove.\n");
return 0;
}
struct device_driver zl_driver_struct = {
.name = "zl_device", //this string will be show as /sys/bus/platform/drivers/zl_device
.bus = &platform_bus_type,
.probe = zl_probe,
.remove = zl_remove,
};
int test_init(void)
{
int ret = 0;
driver_register(&zl_driver_struct);
my_class = class_create(THIS_MODULE, "myclass_led");
if (IS_ERR(my_class)) {
printk("[%s-%d]: class_create.\n", __func__, __LINE__);
return PTR_ERR(my_class);
}
ret = register_chrdev_region(MKDEV(52,0), 4, "my_led");
if (ret) {
printk("[%s-L%d]: register_chrdev_region.\n", __func__, __LINE__);
return ret;
}
platform_device_register(&zl_dev);
return ret;
}
void test_exit(void)
{
platform_device_unregister(&zl_dev);
class_destroy(my_class);
driver_unregister(&zl_driver_struct);
}
module_init(test_init);
module_exit(test_exit);
|