chapter 4 Advanced char driver operations chenbo 2008ustc
chapter 4 -Advanced char driver operations chenbo 2008@ustc. edu. cn 中国科学技术大学软件学院
1 Conucrrency and race conditions 2 Block and Non-Block 3 Interrupt 4 Timer and clocks
Atomic static atomic_t xxx_available = ATOMIC_INIT(1); static int xxx_open() {. . if(!atomic_dec_and_test(&xxx_available )){ atomic_inc(&xxx_available); return BUSY; //已经打开 }. . . . return 0; //成功 } int release() { atomic_inc(&xxx_available); //释放设备 }
spin lock int xxx_count = 0; static int xxx_open(struct inode *inode, struct file *flip) { spin_lock(&xxx_lock); if (xxx_count) { spin_unlock(&xxx_lock); return - EBUSY; } xxx_count++; spin_unlock(&xxx_lock); . . . return 0; }
spin lock static int xxx_release(struct inode *inode, struct file *flip) {. . spin_lock(&xxx_lock); xxx_count--; spin_unlock(&xxx_lock); return 0; }
scull /* Initialize each device. */ for (i = 0; i < scull_nr_devs; i++) { scull_devices[i]. quantum = scull_quantum; scull_devices[i]. qset = scull_qset; init_MUTEX(&scull_devices[i]. sem); } scull_setup_cdev(&scull_devices[i], i);
scull int scull_open(struct inode *inode, struct file *filp) { struct scull_dev *dev; /* device information */ dev = container_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev; /* for other methods */ /* now trim to 0 the length of the device if open was write-only */ if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { if (down_interruptible(&dev->sem)) return -ERESTARTSYS; scull_trim(dev); /* ignore errors */ up(&dev->sem); } return 0; /* success */ }
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scull_dev *dev = filp->private_data; struct scull_qset *dptr; /* the first listitem */ int quantum = dev->quantum, qset = dev->qset; int itemsize = quantum * qset; /* how many bytes in the listitem */ int item, s_pos, q_pos, rest; ssize_t retval = 0; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (*f_pos >= dev->size) goto out; if (*f_pos + count > dev->size) count = dev->size - *f_pos; item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; dptr = scull_follow(dev, item); if (dptr == NULL || !dptr->data || ! dptr->data[s_pos]) goto out; if (count > quantum - q_pos) count = quantum - q_pos; if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; out: up(&dev->sem); return retval; }
block char buf; fd = open("/dev/tty. S 1", O_RDWR); . . . res = read(fd, &buf, 1); if(res==1) printf ("%cn", buf); char buf; fd = open("/dev/tty. S 1", O_RDWR | O_NONBLOCK); . . . while(read(fd, &buf, 1) != 1); printf ("%c n", buf);
wait queue wait_event(queue, condition); wait_event_interruptible(queue, condition); wait_event_timeout(queue, condition, timeout); wait_event_interruptible_timeout(queue, condition, timeout);
wait queue wake_up_interruptible (wait_queue_head_t *q);
example-block
example-block
example-block
example-non-block
example-non-block
example-non-block
Interrupt short_irq: if (short_irq >= 0) { result = request_irq(short_irq, short_interrupt, SA_INTERRUPT, "short", NULL); if (result) { printk(KERN_INFO "short: can't get assigned irq %in", short_irq); short_irq = -1; } else { /* actually enable it -- assume this *is* a parallel port */ outb(0 x 10, short_base+2); } }
Interrupt int can_request_irq(unsigned int irq, unsigned long flags);
Interrupt static void sample_open(struct inode *inode, struct file *filp) { struct sample_dev *dev = hwinfo + MINOR(inode->i_rdev); request_irq(dev->irq, sample_interrupt, 0 /* flags */, "sample", dev /* dev_id */); / *. . */ return 0; }
tasklet void xxx_do_tasklet(unsigned long); DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0); void xxx_do_tasklet(unsigned long) {. . . } irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) {. . . tasklet_schedule(&xxx_tasklet); //调度tasklet. . . }
tasklet int __init xxx_int(void) {. . . result = request_irq(xxx_irq, xxx_interrupt, SA_INTERRUPT, "xxx", NULL); . . } void __exit xxx_exit(void) {. . . free_irq(xxx_irq, xxx_interrupt); . . . }
S 3 C 2410 -Interrupt static int s 3 c 2410_rtc_open(void) { int ret; ret = request_irq(s 3 c 2410_rtc_alarmno, s 3 c 2410_rtc_alarmirq, SA_INTERRUPT, "s 3 c 2410 -rtc alarm", NULL); if (ret) printk(KERN_ERR "IRQ%d already in usen", s 3 c 2410_rtc_alarmno); ret = request_irq(s 3 c 2410_rtc_tickno, s 3 c 2410_rtc_tickirq, SA_INTERRUPT, "s 3 c 2410 -rtc tick", NULL); if (ret) { printk(KERN_ERR "IRQ%d already in usen", s 3 c 2410_rtc_tickno); goto tick_err; } return ret; tick_err: free_irq(s 3 c 2410_rtc_alarmno, NULL); return ret; }
S 3 C 2410 -Interrupt static void s 3 c 2410_rtc_release(void) { /* do not clear AIE here, it may be needed for wake */ s 3 c 2410_rtc_setpie(0); free_irq(s 3 c 2410_rtc_alarmno, NULL); free_irq(s 3 c 2410_rtc_tickno, NULL); } static irqreturn_t s 3 c 2410_rtc_alarmirq(int irq, void *id, struct pt_regs*r) { rtc_update(1, RTC_AF | RTC_IRQF); return IRQ_HANDLED; } static irqreturn_t s 3 c 2410_rtc_tickirq(int irq, void *id, struct pt_regs *r) { rtc_update(1, RTC_PF | RTC_IRQF); return IRQ_HANDLED; }
S 3 C 2410 -Interrupt void rtc_update(unsigned long num, unsigned long events) { spin_lock(&rtc_lock); rtc_irq_data = (rtc_irq_data + (num << 8)) | events; spin_unlock(&rtc_lock); wake_up_interruptible(&rtc_wait); kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); }
Timer struct xxx_dev { struct cdev; --timer_list xxx_timer; } xxx_func 1(---) { struct xxx_dev *dev = filep->private_data; --init_timer(&dev->xxx_timer); dev->xxx_timer. function = &xxx_do_timer; dev->xxx_timer. data = (unsigned long)dev; dev->xxx_timer. expires = jiffies + delay; add_timer(&dev->xxx_timer); --}
Timer xxx_func 2(---) { struct xxx_dev *dev = filep->private_data; --del_timer(&dev->xxx_timer); --} static void xxx_do_timer(unsigned long arg) { struct xxx_devices *dev = (struct xxx_devices *)(arg); --dev->xxx_timer. expires = jiffies + delay; add_timer(&dev->xxx_timer); --}
Example-Timer #include #include #include <linux/module. h> <linux/types. h> <linux/fs. h> <linux/errno. h> <linux/mm. h> <linux/sched. h> <linux/init. h> <linux/cdev. h> <asm/io. h> <asm/system. h> <asm/uaccess. h> <linux/timer. h>
Example-Timer #define SECOND_MAJOR 252 static int second_major = SECOND_MAJOR; struct second_dev { struct cdev; atomic_t counter; struct timer_list s_timer; }; struct second_dev *second_devp; static void second_timer_handle(unsigned long arg) { mod_timer(&second_devp->s_timer, jiffies + HZ); atomic_inc(&second_devp->counter); printk(KERN_NOTICE "current jiffies is %ldn", jiffies); }
Example-Timer int second_open(struct inode *inode, struct file *filp) { init_timer(&second_devp->s_timer); second_devp->s_timer. function = &second_timer_handle; second_devp->s_timer. expires = jiffies + HZ; add_timer(&second_devp->s_timer); atomic_set(&second_devp->counter, 0); return 0; } int second_release(struct inode *inode, struct file *filp) { del_timer(&second_devp->s_timer); return 0; }
Example-Timer static ssize_t second_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { int counter; counter = atomic_read(&second_devp->counter); if(put_user(counter, (int*)buf)) return - EFAULT; else return sizeof(unsigned int); }
Example-Timer static const struct file_operations second_fops = {. owner = THIS_MODULE, . open = second_open, . release = second_release, . read = second_read, };
Example-Timer static void second_setup_cdev(struct second_dev *dev, int index) { int err, devno = MKDEV(second_major, index); cdev_init(&dev->cdev, &second_fops); dev->cdev. owner = THIS_MODULE; dev->cdev. ops = &second_fops; err = cdev_add(&dev->cdev, devno, 1); if (err) printk(KERN_NOTICE "Error %d adding LED%d", err, index); }
Example-Timer
thank you !
- Slides: 39