200 likes | 271 Views
编写 Linux 下的设备驱动程序. What we have learned?. 用 module 实现设备驱动程序 init_module, cleanup_module 设备也是文件;设备由主设备号、次设备号唯一标识 mknod /dev/status c <major_num> 0 登记 / 注销设备 register_chrdev, unregister_chrdev struct file_operations (include/linux/fs.h) 实现 file_operations 结构中指定的操作.
E N D
What we have learned? • 用module实现设备驱动程序 • init_module, cleanup_module • 设备也是文件;设备由主设备号、次设备号唯一标识 • mknod /dev/status c <major_num> 0 • 登记/注销设备 • register_chrdev, unregister_chrdev • struct file_operations (include/linux/fs.h) • 实现file_operations结构中指定的操作
What we have learned? (cont’d) • 拷贝数据to/from用户空间 • copy_to_user, copy_from_user • 使用计数(usage count) • 每个module保留一个usage count • 宏:MOD_INC_USE_COUNT, MOD_DEC_USE_COUNT, MOD_IN_USE
<linux/fs.h>: int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); int unregister_chrdev(unsigned int major, const char *name);
<asm/uaccess.h>: unsigned long copy_to_user(void *to, const void *from, unsigned long count); unsigned long copy_from_user(void *to, const void *from, unsigned long count);
<linux/fs.h>: struct file_operations { int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); … };
<linux/fs.h>: struct file的几个关键字段 mode_t f_mode; loff_t f_pos; unsigned int f_flags; struct file_operations *f_op; void *private_data; struct dentry *f_dentry;
What are we still to learn? • Timing • Hardware management • I/O ports & I/O memory • Interrupt handling
Programming I/O • Two types of instructions can support I/O: • special-purpose I/O instructions; • memory-mapped load/store instructions. • Intel x86 provides in, out instructions. Most other CPUs use memory-mapped I/O. • I/O instructions do not preclude memory-mapped I/O.
Using I/O ports <linux/ioport.h>: int check_region(unsigned long start, unsigned long len); struct resource *request_region(unsigned long start, unsigned long len, char *name); void release_region(unsigned long start, unsigned long len);
Using I/O ports (cont’d) <asm/io.h>: • Read/write 8-bit ports (byte width): • unsigned intb(unsigned port); • unsigned outb(unsigned char byte, unsigned port); • Read/write 16-bit ports (word width): • unsigned intw(unsigned port); • unsigned outw(unsigned short word, unsigned port); • Read/write 32-bit ports: • unsigned intl(unsigned port); • unsigned outl(unsigned longword, unsigned port);
Interrupt handling • Installing an interrupt handler • Implementing a handler
Installing an Interrupt Handler • request_irq, free_irq int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id); void free_irq(unsigned int irq, void *dev_id); • 调用时机
Implementing a handler • static void sample_interrupt(int irq, void *dev_id, struct pt_regs *regs); • irq: interrupt number • dev_id: client data (private data used by the driver) • regs: (rarely used) a snapshot of the processor’s context before the processor entered interrupt code
Implementing a handler (cont’d) • 等待队列(wait queue) • <linux/sched.h><linux/wait.h> • wait_queue_head_t类型 • init_waitqueue_head • interruptible_sleep_on • wake_up_interruptible
Code examples void short_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct timeval tv; int written; do_gettimeofday(&tv); /* Write a 16-byte record. Assume PAGE_SIZE is a multiple of 16 */ written = sprintf((char *)short_head,"%08u.%06u\n", (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec)); short_incr_bp(&short_head, written); wake_up_interruptible(&short_queue); }
Code examples (cont’d) ssize_t short_i_read (struct file *filp, char *buf, size_t count, loff_t *f_pos) { int count0; while (short_head == short_tail) { interruptible_sleep_on(&short_queue); if (signal_pending (current)) /* a signal arrived */ return -ERESTARTSYS; /* tell the fs layer to handle it */ } count0 = short_head - short_tail; if (count0 < 0) /* wrapped */ count0 = short_buffer + PAGE_SIZE - short_tail; if (count0 < count) count = count0; if (copy_to_user(buf, (char *)short_tail, count)) return -EFAULT; short_incr_bp (&short_tail, count); return count; }
Bottom-Half (BH) Processing • 中断处理程序尽量短小 • top half: 实际响应中断的例程 • bottom half: 被top half 调度,并在稍后更安全的时候运行的例程 • 将任务延迟到以后处理 • task queue • tasklet • 内核定时器
Race condition • 解决办法: • 使用循环缓冲区并避免共享变量; • 使用自旋锁(spinlock)强制实行互斥访问 • 使用原子操作递增或递减锁变量 • 尽量不要在中断处理程序中将信号量用于互斥
Source codes study • A touch screen driver • /usr/src/linux/drivers/char/au1000_ts.c