这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » FPGA » 读uclinux字符驱动笔记

共2条 1/1 1 跳转至

读uclinux字符驱动笔记

菜鸟
2006-12-25 18:50:11     打赏
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    

菜鸟
2006-12-27 06:45:00     打赏
2楼
支持,大力支持这样的文章。

共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]