📄 pnxled.c
字号:
/* func : pnx8550 LED driver
* author : zhu weihua(zhuwas@163.com)
* 2008-03-11T15:12:05+0800
*/
/*-----basic support for kernel module-----*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/stat.h>
#include <linux/miscdevice.h>
/*-----module will use the header file below-----*/
#include <asm/uaccess.h> /*copy content between user and kernel*/
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/string.h>
//FOR MACRO DEFINE
#define NAME "pnxled"
#define FPSCK0 pled->GPIO_MD2 = 0x01000000
#define FPSCK1 pled->GPIO_MD2 = 0x01000100
#define FPSTB0 pled->GPIO_MD2 = 0x02000000
#define FPSTB1 pled->GPIO_MD2 = 0x02000200
#define FPDATA0 pled->GPIO_MD2 = 0x08000000
#define FPDATA1 pled->GPIO_MD2 = 0x08000800
/*
//used for ioctrl.
#define GPIO_IOC_MAGIC 'I'
#define GPIO_IOCSETWAITPERIOD _IO(GPIO_IOC_MAGIC,0)
#define GPIO_IOCGETWAITPERIOD _IO(GPIO_IOC_MAGIC,1)
*/
MODULE_AUTHOR("zhu weihua<zhuwas@163.com>");
MODULE_DESCRIPTION ("pnx8550 driver");
MODULE_LICENSE("Dual BSD/GPL");
/*private data structure*/
struct pnxled_private {
unsigned long *ref_cnt; /*reference count*/
spinlock_t lock;
unsigned char b_mode; /*blocking mode or not*/
};
//register for the GPIO
typedef volatile struct {
__u32 GPIO_MC0;
__u32 GPIO_MC1;
__u32 GPIO_MC2;
__u32 GPIO_MC3;
__u32 GPIO_MD0;
__u32 GPIO_MD1;
__u32 GPIO_MD2;
__u32 GPIO_MD3;
}PNXLEDRegs;
/*support interface*/
static void DelayNS(unsigned int dly);
static void Led_Single(unsigned char dig, unsigned char num);
static unsigned char getled_value(char str);
static void do_ledwrite(void);
static ssize_t pnxled_write(struct file *file,const char *data,size_t len,loff_t *ppos);
static int pnxled_ioctl(struct inode *inode,struct file *file,unsigned cmd,unsigned long arg);
static int pnxled_open(struct inode *inode,struct file *file);
static int pnxled_release(struct inode *inode,struct file *file);
/* Global data */
static struct file_operations pnxled_fops = {
.owner = THIS_MODULE,
.write = pnxled_write,
.ioctl = pnxled_ioctl,
.open = pnxled_open,
.release= pnxled_release,
};
static struct miscdevice misc_pnxled = {
.minor = MISC_DYNAMIC_MINOR,
.name = NAME,
.fops = &pnxled_fops
};
static struct pnxled_private pnxled_priv;
static unsigned int old_GPIO_MC2;
static PNXLEDRegs *pled = (PNXLEDRegs*)(0xBBE00000 + 0x00104000);
static unsigned char led_d[4]={0x0b,0x0d,0x0e,0x07};
struct timer_list led_timer;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static volatile int flag = 0;
static volatile unsigned long long led_i = 0;
static volatile unsigned char buf[8]={'*','*','*','*','*','*','*','*'};
//map the ascii char to the keycode.
static unsigned char getled_value(char str)
{
unsigned char Code;
switch(str) {
case '0':
Code = 0x82;
break;
case '1':
Code = 0xbe;
break;
case '2':
Code = 0x23;
break;
case '3':
Code = 0x2a;
break;
case '4':
Code = 0x1e;
break;
case '5':
Code = 0x4a;
break;
case '6':
Code = 0x42;
break;
case '7':
Code = 0xae;
break;
case '8':
Code = 0x02;
break;
case '9':
Code = 0x0a;
break;
case 'A':
case 'a':
Code = 0x06;
break;
case 'B':
case 'b':
Code = 0x52;
break;
case 'C':
case 'c':
Code = 0xc3;
break;
case 'D':
case 'd':
Code = 0x32;
break;
case 'E':
case 'e':
Code = 0x43;
break;
case 'F':
case 'f':
Code = 0x47;
break;
case 'H':
Code = 0x16;
break;
case 'h':
Code = 0x56;
break;
case 'L':
case 'l':
Code = 0xd3;
break;
case 'N':
case 'n':
Code = 0x86;
break;
case 'P':
case 'p':
Code = 0x07;
break;
case 'U':
case 'u':
case 'V':
case 'v':
Code = 0x92;
break;
case '-':
Code = 0x7f;
break;
case '_':
Code = 0xfb;
break;
case '.':
Code = 0xfd;
break;
case '*':
Code = 0xff;
break;
default:
Code = 0x0;
break;
}
return Code;
}
static void DelayNS(unsigned int dly)
{
unsigned int i;
for(; dly>0; dly--)
for(i=0; i<50000; i++);
}
static void Led_Single(unsigned char dig, unsigned char num)
{
unsigned int i;
for(i=0;i<4;i++){
if(dig&(0x01<<i))
{FPSCK0;FPDATA1;FPSCK1;}
else
{FPSCK0;FPDATA0;FPSCK1;}
}
for(i=0;i<8;i++){
if(num&(0x01<<i))
{FPSCK0;FPDATA1;FPSCK1;}
else
{FPSCK0;FPDATA0;FPSCK1;}
}
FPSTB0;
DelayNS(1);
FPSTB1;
}
//will do write by kernel timer
static void do_ledwrite(void)
{
spin_lock(&pnxled_priv.lock);
switch (led_i%8) {
case 0:
Led_Single(led_d[0],getled_value(buf[0]));
break;
case 1:
if (buf[1]=='.') {
Led_Single(led_d[0],getled_value(buf[1]));
}
break;
case 2:
Led_Single(led_d[1],getled_value(buf[2]));
break;
case 3:
if (buf[3]=='.') {
Led_Single(led_d[1],getled_value(buf[3]));
}
break;
case 4:
Led_Single(led_d[2],getled_value(buf[4]));
break;
case 5:
if (buf[5]=='.') {
Led_Single(led_d[2],getled_value(buf[5]));
}
break;
case 6:
Led_Single(led_d[3],getled_value(buf[6]));
break;
case 7:
if (buf[7]=='.') {
Led_Single(led_d[3],getled_value(buf[7]));
}
break;
default:
break;
}
spin_unlock(&pnxled_priv.lock);
led_i++;
flag = 1;
wake_up_interruptible(&wq);
mod_timer(&led_timer,jiffies+1);
}
static ssize_t pnxled_write(struct file *file,const char *data,size_t len,loff_t *ppos)
{
//write the buf and let do_ledwrite to display by herslef
memset((void *)buf,'*',8);
memcpy((void *)buf,data,len);
del_timer(&led_timer);
led_timer.expires = jiffies+1;
//led_timer.data = (unsigned long);
led_timer.function = (void *)do_ledwrite;
add_timer(&led_timer);
wait_event_interruptible(wq, flag!=0);
flag = 0;
led_i = 0;
return 0;
}
static int pnxled_ioctl(struct inode *inode,struct file *file,unsigned cmd,unsigned long arg)
{
unsigned long *ptr = (unsigned long *)arg;
if(ptr == NULL)
return (-EIO);
switch(cmd) {
/*
case GPIO_IOCSETWAITPERIOD:
sleeptime = arg;
break;
case GPIO_IOCGETWAITPERIOD:
copy_to_user(ptr, &sleeptime, sizeof(unsigned long));
break;
*/
default:
return (-EIO);
}
return 0;
}
static int pnxled_open(struct inode *inode,struct file *file)
{
/*if (pnx_priv.ref_cnt != 0) {
printk(KERN_WARNING NAME": exclusive access only\n");
return (-EIO);
}*/
if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
printk(KERN_WARNING "\n"NAME" is a write only device\n");
return (-EIO);
}
/*set the block mode and increase reference count*/
//pnxled_priv.b_mode = ((file->f_flags & O_NONBLOCK) ? 0:1 );
pnxled_priv.ref_cnt++;
file->f_op = &pnxled_fops;
file->private_data = (void *)&pnxled_priv;
return 0;
}
//when close the device file
static int pnxled_release(struct inode *inode,struct file *file)
{
pnxled_priv.ref_cnt--;
return 0;
}
//exe when insmod the module
static int __init pnxled_init(void)
{
/* do some usefull init work there*/
int i;
int miscret;
memset(&pnxled_priv, 0, sizeof(struct pnxled_private));
spin_lock_init(&pnxled_priv.lock);
init_timer(&led_timer);
old_GPIO_MC2 = pled->GPIO_MC2;
pled->GPIO_MC2 = 0x008A0000;
for(i=0;i<4;i++) {
Led_Single(led_d[i], getled_value('8'));
mdelay(100);
}
for(i=0;i<4;i++) {
Led_Single(led_d[i], 0xff);
}
miscret = misc_register( &misc_pnxled );
if (miscret < 0) {
printk(KERN_INFO NAME ": can't register misc pnx8550 led devices driver\n");
return miscret;
}
printk(KERN_INFO NAME ": pnx8550 led devices driver\n");
return 0;
}
//exe when unload the module
static void __exit pnxled_cleanup(void)
{
/*do some clean work there*/
int i;
del_timer_sync(&led_timer);
for(i=0;i<4;i++) {
Led_Single(led_d[i], getled_value('8'));
mdelay(100);
}
for(i=0;i<4;i++) {
Led_Single(led_d[i], 0xff);
}
misc_deregister(&misc_pnxled);
pled->GPIO_MC2 = old_GPIO_MC2;
printk(KERN_INFO NAME ": unload pnx8550 led devices driver now\n");
}
module_init(pnxled_init);
module_exit(pnxled_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -