共2条
1/1 1 跳转至页
读uclinux字符驱动笔记
altera nios2 uClinux源码中包含一个linux/drivers/char/altera_pio_button.c 字符驱动,一个标准的字符中断设备,涉及多个linux内核编程技巧,说难不难,但也非易,细细品之,定有收获。
看nios2的uClinux源码时把一些自己的理解加进了注释中,算是一片读书笔记,如果有描述不对的地方,希望大家指正,注释将不断完善。
阅读技巧:贴出来的只是断章取意,应该结合源码包看。但linux内核相当雄伟,最好借助SourceInsight等工具,可事半功倍。
落笔: 2006 12月11日 午饭后
郭任
于杭州穿山甲电子
/*
* linux/drivers/char/altera_pio_button.c
* A simple character driver that takes buttons on Nios Development
* Kit as an input device (major 62)
*
* The characters input can be '1', '2', '4' or '8'
*
* Copyright (C) 2004 Microtronix Datacom Ltd
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Written by Wentao Xu
*/
/*
添加中文注释:
2006 11.15 added by 郭任
杭州穿山甲电子
www.epangolin.com
*/
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#define BUTTON_MAJOR 62 //设置主设备号,注意在/dev/目录下建立同样设备文件
int button_major = BUTTON_MAJOR;
int button_minor = 0;
#define PIO_BUTTON_BASE na_button_pio //在system.h中定义,每次编译时都会通过Arch/Nios2nommu/Scripts/Nios2_system.h的脚本生成.
#define PIO_BUTTON_IRQ na_button_pio_irq
#define PIO_BUTTON_SIZE sizeof(np_pio)
#define BUTTON_BUF_SIZE 100
struct button_dev { //典型的按键结构体定义
int count;
int head;
int tail;
char buf[BUTTON_BUF_SIZE];
int started;
struct cdev cdev;
wait_queue_head_t rxq;
struct semaphore mutex; //信号量机制
} _button_dev;
static void button_handle_event(void *dev_id)
{
static int old = 0;
int status, key;
struct button_dev * dev=(struct button_dev*)dev_id; //传递接口体指针,具体指button_init()中dev_t devno
np_pio* pio = (np_pio *)(PIO_BUTTON_BASE);
/*
np_pio 定义如下:
typedef volatile struct
{
int np_piodata; // read/write, up to 32 bits
int np_piodirection; // write/readable, up to 32 bits, 1->output bit
int np_piointerruptmask; // write/readable, up to 32 bits, 1->enable interrupt
int np_pioedgecapture; // read, up to 32 bits, cleared by any write
} np_pio;
*/
outl(0, &pio->np_pioedgecapture); //置pioedgecapture寄存器0 使得外部按键不改变寄存器变化,保障当前寄存器值
/* read input, check 4 buttons */
status = (~inl(&pio->np_piodata)) & 0xF;
key = status - old;
old = status;
if (key > 0) {
down(&dev->mutex); //信号量同步操作,加锁
/* we simply discard new inputs if buffer overflows */
if (dev->count < BUTTON_BUF_SIZE) {
dev->buf[dev->tail] = key + '0'; //传递buf[]数组中一个字符为ascii中的数字
dev->tail = (dev->tail+1) % BUTTON_BUF_SIZE;//下一个buf[]字符
dev->count++; //count用来标记当前buf使用量,避免指针溢出
}
up(&dev->mutex); //信号量同步操作,解锁
/* wake up any waiting reader */
if (waitqueue_active(&dev->rxq)) {
wake_up(&dev->rxq);
/*
linux中的调度策略技巧,使pio响应加快
waitqueue_active定义如下:
static inline int waitqueue_active(wait_queue_head_t *q)
{
return !list_empty(&q->task_list);
}
wake_up定义如下:
#define wake_up(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
kernel/Sched.c 中
void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, void *key)
{
unsigned long flags;
spin_lock_irqsave(&q->lock, flags);
__wake_up_common(q, mode, nr_exclusive, 0, key);
spin_unlock_irqrestore(&q->lock, flags);
}
*/
}
}
/* re-enable interrupts */
outl(-1, &pio->np_piointerruptmask); //重置中断
}
static DECLARE_WORK(button_work, button_handle_event, (void*)&_button_dev);
static irqreturn_t pio_button_isr(int irq, void *dev_id, struct pt_regs *regs)
{
np_pio* pio = (np_pio *)PIO_BUTTON_BASE;
if (!pio)
return IRQ_NONE;
/* disable interrupt */
outl(0, &pio->np_pioedgecapture);
outl(0, &pio->np_piointerruptmask);
/* activate the bottom half */
schedule_work(&button_work);
return IRQ_HANDLED;
}
static int button_start(struct button_dev *dev)
{
np_pio *pio=(np_pio *)(PIO_BUTTON_BASE);
outl(0, &pio->np_pioedgecapture);
outl(0, &pio->np_piodirection);
/* register interrupt */
if (request_irq(PIO_BUTTON_IRQ, pio_button_isr, SA_INTERRUPT, "pio_button",
(void*)(dev))) {
printk("pio_button: unable to register interrupt %d\n", PIO_BUTTON_IRQ);
return -1;
}
outl(-1, &pio->np_piointerruptmask);
return 0;
}
/*
* Open/close .
*/
static int button_open(struct inode *inode, struct file *filp)
{
struct button_dev *dev;
dev = container_of(inode->i_cdev, struct button_dev, cdev);
filp->private_data = dev;
preempt_disable();
dev->started++;
if (dev->started!=1) {
preempt_enable();
return 0;
}
/* init buffon info */
dev->count=0;
dev->head=0;
dev->tail=0;
init_waitqueue_head(&dev->rxq);
init_MUTEX(&dev->mutex);
/* init buttons */
button_start(dev);
preempt_enable();
/*
preempt_enable定义如下:
#define preempt_enable() do { } while (0)
如果不用close,则字符会不停的通过管道出来
典型的体验是在shell 下执行cat /dev/button,
*/
return 0;
}
/*
*/
static int button_release(struct inode *inode, struct file *filp) //关闭设备
{
np_pio *pio=(np_pio *)(PIO_BUTTON_BASE);
struct button_dev *dev = (struct button_dev*)filp->private_data;
preempt_disable();
dev->started--;
if (dev->started != 0) {
preempt_enable();
return 0;
}
preempt_enable();
/*disable this interrupts */
outl(0, &pio->np_piointerruptmask);
free_irq(PIO_BUTTON_IRQ, (void*)(dev));
return 0;
}
/*
*/
static int
button_ioctl(struct inode *inode, struct file *filp,
unsigned int command, unsigned long arg) //未使用
{
return -EINVAL;
}
static ssize_t button_read(struct file *filp, char *buf,
size_t count, loff_t * ppos)
{
int i, total;
struct button_dev *dev = (struct button_dev*)filp->private_data;
if (dev->count==0) {
DEFINE_WAIT(wait);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
while (!signal_pending(current) && (dev->count == 0)) {
prepare_to_wait(&dev->rxq, &wait, TASK_INTERRUPTIBLE);
if (!signal_pending(current) && (dev->count == 0))
schedule();
finish_wait(&dev->rxq, &wait);
}
if (signal_pending(current) && (dev->count == 0))
return -ERESTARTSYS;
}
if (down_interruptible(&dev->mutex))
return -ERESTARTSYS;
/* return data */
total = (count < dev->count) ? count : dev->count;
for (i=0; i < total; i++) {
put_user(dev->buf[dev->head], buf+i);
dev->head = (dev->head + 1) % BUTTON_BUF_SIZE;
dev->count--;
}
up(&dev->mutex);
return total;
}
static unsigned int button_poll(struct file *filp, poll_table *wait)
{
struct button_dev *dev = (struct button_dev*)filp->private_data;
unsigned int mask = 0;
poll_wait(filp, &dev->rxq, wait);
if (dev->count > 0)
mask |= POLLIN | POLLRDNORM; /* readable */
return mask;
}
static struct file_operations button_fops = {
.read = button_read,
.open = button_open,
.release= button_release,
.ioctl = button_ioctl,
.poll = button_poll,
.owner = THIS_MODULE,
};
static int __init button_init(void)
{
int i;
dev_t devno;
if (!(request_mem_region((unsigned long)PIO_BUTTON_BASE, PIO_BUTTON_SIZE, "pio_button")))
return -1;
devno = MKDEV(button_major, button_minor);
cdev_init(&_button_dev.cdev, &button_fops);
_button_dev.cdev.owner = THIS_MODULE;
_button_dev.started = 0;
i = register_chrdev_region(devno, 1, "pio_button");
if (i) {
printk(KERN_NOTICE "Can't get major %d for PIO buttons", button_major);
goto error1;
}
i = cdev_add(&_button_dev.cdev, devno, 1);
if (i) {
printk(KERN_NOTICE "Error %d adding PIO buttons", i);
goto error2;
}
error2:
unregister_chrdev_region(devno, 1);
error1:
release_mem_region((unsigned long)PIO_BUTTON_BASE, PIO_BUTTON_SIZE);
return i;
}
static void __exit button_exit(void)
{
cdev_del(&_button_dev.cdev);
unregister_chrdev_region(MKDEV(button_major, button_minor), 1);
release_mem_region((unsigned long)PIO_BUTTON_BASE, PIO_BUTTON_SIZE);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
关键词: uclinux 字符 驱动 笔记 nclude B
共2条
1/1 1 跳转至页
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
与电子爱好者谈读图二被打赏50分 | |
【FRDM-MCXN947评测】Core1适配运行FreeRtos被打赏50分 | |
【FRDM-MCXN947评测】双核调试被打赏50分 | |
【CPKCORRA8D1B评测】---移植CoreMark被打赏50分 | |
【CPKCORRA8D1B评测】---打开硬件定时器被打赏50分 | |
【FRDM-MCXA156评测】4、CAN loopback模式测试被打赏50分 | |
【CPKcorRA8D1评测】--搭建初始环境被打赏50分 | |
【FRDM-MCXA156评测】3、使用FlexIO模拟UART被打赏50分 | |
【FRDM-MCXA156评测】2、rt-thread MCXA156 BSP制作被打赏50分 | |
【FRDM-MCXN947评测】核间通信MUTEX被打赏50分 |