共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							跳转至页
		回复
| 有奖活动 | |
|---|---|
| 硬核工程师专属补给计划——填盲盒 | |
| “我踩过的那些坑”主题活动——第002期 | |
| 【EEPW电子工程师创研计划】技术变现通道已开启~ | |
| 发原创文章 【每月瓜分千元赏金 凭实力攒钱买好礼~】 | |
| 【EEPW在线】E起听工程师的声音! | |
| 高校联络员开始招募啦!有惊喜!! | |
| 【工程师专属福利】每天30秒,积分轻松拿!EEPW宠粉打卡计划启动! | |
| 送您一块开发板,2025年“我要开发板活动”又开始了! | |


 
			
			
			
						
			 我要赚赏金
 我要赚赏金 STM32
STM32 MCU
MCU 通讯及无线技术
通讯及无线技术 物联网技术
物联网技术 电子DIY
电子DIY 板卡试用
板卡试用 基础知识
基础知识 软件与操作系统
软件与操作系统 我爱生活
我爱生活 小e食堂
小e食堂

