LED驱动实例5(信号量+udev)
来源:互联网 发布:个性定制软件 编辑:程序博客网 时间:2024/04/27 17:08
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define CMD_LED_GET_STATE 2
#define LEDS_NUM 4
#define S3C24XX_LEDS_PHY_BASE 0x56000050
#define GPFCON_OFFSET 0x0
#define GPFDAT_OFFSET 0x1
#define GPFUP_OFFSET 0x2
static int major = 0;
module_param(major, int, S_IRUGO|S_IWUGO);
MODULE_PARM_DESC(major,"s3c24xx leds major number");
struct s3c24xx_leds_dev { //设备结构体
unsigned char led_state; //led_state [4-7], 1: on, 0: off
unsigned long *pBase;
//...
//spinlock_t spin;
struct semaphore sem; //信号量
};
struct s3c24xx_leds_dev *devs =NULL;
static struct class *s3c24xx_leds_class; //创建LED 类
static int s3c24xx_leds_open (struct inode *inode, struct file *filp)
{
unsigned long * pReg = devs->pBase;
filp->private_data = (void *)devs;
//配置寄存器
//GPFCON output.
writel( (readl(pReg +GPFCON_OFFSET) &~0xff00) |0x5500, pReg +GPFCON_OFFSET);
//GPFUP disable pullup.
writel( (readl(pReg +GPFUP_OFFSET) &~0xff00) |0x5500, pReg +GPFUP_OFFSET);
return 0;
}
static int s3c24xx_leds_close (struct inode *inode, struct file *filp)
{
filp->private_data = NULL;
return 0;
}
static ssize_t s3c24xx_leds_read (struct file * filp, char __user *buf, size_t len, loff_t *loff)
{
struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;
unsigned char kbuff;
// spin_lock(&pdev->spin);
down(&pdev->sem); // 该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用
kbuff = pdev->led_state >>4;
// spin_unlock(&pdev->spin);
up(&pdev->sem); //释放信号量
if (copy_to_user((void __user *)buf, &kbuff, 1) )
{
return -EFAULT;
}
return 0;
}
static ssize_t s3c24xx_leds_write (struct file * filp, char __user *buf, size_t len, loff_t *loff)
{
struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;
unsigned char kbuff;
if (copy_from_user(&kbuff, (void __user *)buf, 1))
{
return -EFAULT;
}
kbuff = kbuff<<4;
//真正的LED操作
// spin_lock(&pdev->spin);
down(&pdev->sem);
if ( pdev->led_state != kbuff ) {
//...
int i;
for (i = 0; i< LEDS_NUM;i++) {
if( (pdev->led_state & (1<< (4+i))) ^ (kbuff & (1<<( 4+i))) ) {
if (pdev->led_state & (1<< (4+i)) ) { // orignal led state is on,now set to off
writel( (readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0) | (1<< ( 4+i )), pdev->pBase+GPFDAT_OFFSET);
} else { // orignal led state is off,now set to on
writel( ((readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0)) | ~(1<< ( 4+i)) & 0xf0, pdev->pBase+GPFDAT_OFFSET);
}
}
}
}
pdev->led_state = kbuff;
// spin_unlock(&pdev->spin);
up(&pdev->sem);
return 0;
}
static int s3c24xx_leds_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;
unsigned char state;
//参数正确性验证
if (arg <1 || arg >4)
return -EINVAL;
//权限管理
if (!capable (CAP_SYS_ADMIN)) {
return -EPERM;
}
switch (cmd) {
case CMD_LED_ON:
//...
// spin_lock(&pdev->spin);
down(&pdev->sem);
// writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 |0xe0, pdev->pBase+GPFDAT_OFFSET);
writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 | ~(1<< ( 4+arg -1)) & 0xf0, pdev->pBase+GPFDAT_OFFSET);
pdev->led_state |= 1<< ( 4+arg -1);
// spin_unlock(&pdev->spin);
up(&pdev->sem);
break;
case CMD_LED_OFF:
// spin_lock(&pdev->spin);
down(&pdev->sem);
//writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 |0xf0, pdev->pBase+GPFDAT_OFFSET);
writel( readl(pdev->pBase +GPFDAT_OFFSET) | 1<< ( 4+arg -1), pdev->pBase+GPFDAT_OFFSET);
pdev->led_state &= ~(1<< ( 4+arg -1));
// spin_unlock(&pdev->spin);
up(&pdev->sem);
break;
case CMD_LED_GET_STATE:
// spin_lock(&pdev->spin);
down(&pdev->sem);
state = pdev->led_state >>4;
// spin_unlock(&pdev->spin);
up(&pdev->sem);
printk("driver: led_sate:%0x", state& 0x0f );
return copy_to_user((void *) arg, &state, 1) ? -EFAULT : 0;
break;
default:
printk("Unknown command\n");
break;
}
return 0;
}
static struct file_operations s3c24xx_leds_ops = {
.owner = THIS_MODULE,
.open = s3c24xx_leds_open,
.release = s3c24xx_leds_close,
.read = s3c24xx_leds_read,
.write = s3c24xx_leds_write,
.ioctl = s3c24xx_leds_ioctl,
//...
};
static int __init s3c24xx_leds_init(void)
{
dev_t dev_no = MKDEV(major, 0);
int itmp = -1, ret = -1;
unsigned long *pmem = NULL;
devs = kmalloc(sizeof(struct s3c24xx_leds_dev), GFP_KERNEL);
if (!devs) {
ret = -ENOMEM;
goto out;
}
itmp = register_chrdev(dev_no, "leds", &s3c24xx_leds_ops);
if(itmp < 0) {
ret = -ENODEV;
goto out1;
}
if( itmp > 0 )
major = itmp;
printk("major = %d\n", major);
s3c24xx_leds_class = class_create(THIS_MODULE, "leds");//在驱动初始化的代码里调用class_create为该设备创建一个class,
//再为每个设备调用 class_device_create创建对应的设备
if(IS_ERR(s3c24xx_leds_class)) {
printk("Err: failed in creating class.\n");
ret = -EINVAL;
goto out2;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
class_device_create(s3c24xx_leds_class,MKDEV(major, 0), NULL, "leds");
#else
device_create(s3c24xx_leds_class,NULL,MKDEV(major, 0), NULL, "leds");//在/dev目录下创建相应的设备节点
#endif
//地址映射
pmem = ioremap(S3C24XX_LEDS_PHY_BASE, 12);
if (!pmem) {
ret = -ENOMEM;
goto out2;
}
devs->pBase = pmem;
// spin_lock_init(&devs->lock);
//semaphore initialize.
// sem_init(&devs->sem, 1);
init_MUTEX(&devs->sem);
return 0;
out2:
unregister_chrdev(dev_no, "leds");
out1:
kfree(devs);
out:
return ret;
}
static void __exit s3c24xx_leds_exit(void)
{
dev_t dev_no = MKDEV(major, 0);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
class_device_destroy(s3c24xx_leds_class, MKDEV(major, 0));
#else
device_destroy(s3c24xx_leds_class, MKDEV(major, 0));
#endif
class_destroy(s3c24xx_leds_class);
iounmap(devs->pBase);
unregister_chrdev(dev_no, "leds");
kfree(devs);
devs = NULL;
}
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("StephenYee(stephenyee@farsight.com.cn)");
应用程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define LEDS_NAME "/dev/leds"
#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define CMD_LED_GET_STATE 2
#define LEDS_NUM 4
int main (int argc, char **argv)
{
int fd = -1, ret= -1;
int i;
int leds;
unsigned char buf;
fd = open (LEDS_NAME, O_RDWR);
if (fd < 0) {
printf ("Can't open %s\n", LEDS_NAME);
return -1;
}
//读取当前LED状态
ret = read(fd, &buf, 1);
if (ret < 0) {
printf ("Can't read %s\n", LEDS_NAME);
}
for (i = 0; i< LEDS_NUM; i++) {
printf( "LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
}
while (1) {
//通过IOCTL循环点D11
ioctl (fd, CMD_LED_ON, 2);
ret = read(fd, &buf, 1);
for (i = 0; i< LEDS_NUM; i++) {
printf( "1: LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
}
sleep (1);
ioctl (fd, CMD_LED_OFF, 2);
ret = read(fd, &buf, 1);
for (i = 0; i< LEDS_NUM; i++) {
printf( "2: LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
}
sleep (1);
ioctl (fd, CMD_LED_ON, 3);
// Get state
ret = ioctl (fd, CMD_LED_GET_STATE, &leds);
leds &= 0x0f;
printf("ret = %d, leds =%d\n",ret, leds);
for (i = 0; i< LEDS_NUM; i++) {
printf( " 3: LED%d state is:%s\n", i+1, leds & (1<<i)? "On" :"Off");
}
sleep(1);
//设置状态:
buf ^= (1<<3); //改变D11的状态
write(fd, &buf, 1);
sleep(1);
//设置状态:
buf ^= (1<<1); //改变D11的状态
write(fd, &buf, 1);
}
sleep(1);
return 0;
}
- LED驱动实例5(信号量+udev)
- LED驱动实例3(自旋锁)
- LED驱动实例4(mmap)
- 字符设备驱动(四)-led实例
- LED驱动实例
- LED驱动实例2
- uClinux驱动实例 LED
- 实例操作led驱动
- led驱动实例
- 2-4 LED驱动实例
- 3-2信号量驱动实例
- LedHAL实例架构分析(Led字符型驱动)
- udev使用方法(附实例)
- led驱动(一)
- platform驱动学习一之led实例
- platform总线、设备、驱动模型之led驱动实例
- LED驱动分析(mini2440)
- led驱动(例子:8)
- WM_LBUTTONDBLCLK和WM_LBUTTONDOWN的触发解决方法
- 本来想写点什么的,可是近阶段忙着项目,没有时间
- ASP.net实现无扩展名的URL重写。简单、方便、无需ISAPI .
- android adb常用指令
- 程序如何在32位机子上支持大文件读写
- LED驱动实例5(信号量+udev)
- 电脑/计算机快捷键
- APSys'2011见闻
- A+B for Input-Output Practice (I)
- sha1加密(C# VS2005)(转自:http://yardan.blog.51cto.com/304821/60705)
- Xapian高性能测试-单库VS多库
- 十六进制字符串转换为十进制数值
- 一个请求从 URL 字符串到 HTML 代码的“漫长曲折”之路 .
- Filter 权限管理