/*
接口说明:
SET_MODE: 用来设设置595 级联,每增加一个 595 加 8,初始值为16(支持两个 595) TURN_OFF: 具体关闭那个继电器
TURN_ON: 具体打开那个继电器
TURN_ALL_OFF: 关闭所有继电器,第三个参数无效
TURN_ALL_ON: 打开所有继电器,第三个参数无效
*/
#include <linux/module.h>
#include<linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/s3c6410.h>
#include <plat/gpio-cfg.h>
#include <asm/gpio.h>
#include <mach/gpio-bank-n.h>
#include <linux/pci.h>
#include <mach/map.h>
#define HC595_EN S3C64XX_GPN(10)
#define HC595_SRCLR S3C64XX_GPN(6)
#define HC595_SRCLK S3C64XX_GPN(5)
#define HC595_SER S3C64XX_GPN(2)
#define TURN_OFF 0
#define TURN_ON 1
#define TURN_ALL_OFF 3
#define TURN_ALL_ON 4
#define SET_MODE 5
staticint mode;
static int dev_id;
static struct cdev hc595_cdev; //字符设备
static struct class *hc595_class;
static struct device *hc595_device;
static volatile long *p_GPNCON = NULL;
//static volatile long *p_GPNDATA = NULL;
static void gpio_init(void)
{
/*将GPN2,5,6,10 配置为输出状态*/
*p_GPNCON&= ~((3<<2*2) | (3<<2*5) | (3<<2*6) | (3<<2*10));
*p_GPNCON |=(1<<2*2) | (1<<2*5) | (1<<2*6) | (1<<2*10);
}
staticint hc595_open(struct inode * inode, struct file * file)
{
printk("fromkernel: open successfully!\n");
gpio_init();
mode = 16;
return 0;
}
staticint write_to_hc595(int hc595_data)
{
int i;
gpio_init();
gpio_set_value(HC595_SRCLR,1);/*禁止清空寄存器*/
for(i=0;i<mode; i++)
{
if(((hc595_data)& (1<<(mode -1 -i))) == (1<<(mode -1 -i)))
{
gpio_set_value(HC595_SER,1);/*准备串行数据*/ /*创造上升沿时序*/gpio_set_value(HC595_SRCLK, 0);
udelay(5);
gpio_set_value(HC595_SRCLK,1);
udelay(5);
printk(KERN_ALERT"hight:i=%d\n", (mode -1 -i));
}
else
{
gpio_set_value(HC595_SER,0);/*准备串行数据*/
udelay(5);
/*创造上升沿时序*/
gpio_set_value(HC595_SRCLK,0);
udelay(5);
gpio_set_value(HC595_SRCLK,1);
udelay(5);
printk(KERN_ALERT"level:i=%d\n", (mode -1 -i));
}
}
gpio_set_value(HC595_EN,0);/*产生并行输出上升时序*/
udelay(5);
gpio_set_value(HC595_EN,1);/*产生并行输出上升时序*/
udelay(5);
return0;
}
staticlong hc595_ioctl( struct file *file,unsigned int cmd, unsigned long arg)
{
//int err = 0;
static intold_val = 0;
if(arg<0 || arg>16)
{
printk(KERN_ALERT"the num you enter is invalue!\n");
return-1;
}
printk(KERN_ALERT"from kern: cmd=%d num=%d\n",cmd, arg);
switch(cmd)
{
caseTURN_ON:
{
printk("hello:TURN_ON\n");
old_val|= (1<<arg);/*设置相应位为'1'*/
write_to_hc595(old_val);
break;
}
caseTURN_OFF:
{
printk("hello:TURN_OFF\n");
old_val&= ~(1<<arg);/*设置相应位为'0'*/
write_to_hc595(old_val);
break;
}
caseTURN_ALL_OFF:
{
printk("hello:TURN_ALL_OFF\n");
old_val= 0;
write_to_hc595(old_val);
break;
}
caseTURN_ALL_ON:
{
printk("hello:TURN_ALL_ON\n");
old_val= 0xffff;
write_to_hc595(old_val);
break;
}
caseSET_MODE:
{
mode= arg;
}
default:
{
printk(KERN_ALERT"cmd is invalue!\n");
break;
}
}
return 0;
}
staticint hc595_release(struct inode *inode, struct file *file)
{
return 0;
}
staticstruct file_operations hc595_fops=
{
.owner =THIS_MODULE,
.open =hc595_open,
.unlocked_ioctl= hc595_ioctl,
.release =hc595_release,
};
staticint register_cdev(dev_t dev_id)
{
int ret;
cdev_init(&hc595_cdev,&hc595_fops);
hc595_cdev.owner= THIS_MODULE;
ret =cdev_add(&hc595_cdev, dev_id, 1);
return ret;
}
static int hc595_init(void)
{
int ret =alloc_chrdev_region(&dev_id, 0, 1, "hc595_drv");
if(ret < 0)
{
printk(KERN_INFO"alloc_chrdev_region failed!\n");
return-1;
}
ret =register_cdev(dev_id);
if(ret < 0)
{
printk(KERN_INFO"register_cdev failed!\n");
return-1;
}
hc595_class =class_create(THIS_MODULE, "hc595_class");
hc595_device =device_create(hc595_class, NULL, dev_id, NULL, "hc595_device");
p_GPNCON= (volatile unsigned long*)ioremap(0X7F008830, 8);
printk(KERN_ALERT"insmod successfully!\n"); return 0;
}
static void hc595_exit(void)
{
cdev_del(&hc595_cdev);
unregister_chrdev_region(dev_id,1);
device_destroy(hc595_class,dev_id);
class_destroy(hc595_class);
iounmap(p_GPNCON);
printk(KERN_INFO"rmmod succeful!!\n");
}
module_init(hc595_init);
module_exit(hc595_exit);
MODULE_LICENSE("GPL");