📄 lirc_it87.c
字号:
/* * LIRC driver for ITE IT8712/IT8705 CIR port * * Copyright (C) 2001 Hans-G黱ter L黷ke Uphues <hg_lu@web.de> * * 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. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * ITE IT8705 and IT8712(not tested) CIR-port support for lirc based * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula * * Attention: Sendmode only tested with debugging logs * * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> : * reimplemented read function */#include <linux/version.h>#include <linux/module.h>#ifdef HAVE_CONFIG_H# include <config.h>#endif #include <linux/config.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/serial_reg.h>#include <linux/time.h>#include <linux/string.h>#include <linux/types.h>#include <linux/wait.h>#include <linux/mm.h>#include <linux/delay.h>#include <linux/poll.h>#include <asm/system.h>#include <asm/segment.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/fcntl.h>#include <linux/timer.h>#include "drivers/lirc.h"#include "lirc_it87.h"static unsigned long it87_bits_in_byte_out = 0;static unsigned long it87_send_counter = 0;static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;#define RBUF_LEN 1024#define WBUF_LEN 1024#define LIRC_DRIVER_NAME "lirc_it87"/* timeout for sequences in jiffies (=5/100s) *//* must be longer than TIME_CONST */#define IT87_TIMEOUT (HZ*5/100)static int major = LIRC_MAJOR;static int io = IT87_CIR_DEFAULT_IOBASE;static int irq = IT87_CIR_DEFAULT_IRQ;static unsigned char it87_freq = 38; /* kHz *//* receiver demodulator default: off */static unsigned char it87_enable_demodulator = 0;static spinlock_t timer_lock = SPIN_LOCK_UNLOCKED;static struct timer_list timerlist;/* time of last signal change detected */static struct timeval last_tv = {0, 0};/* time of last UART data ready interrupt */static struct timeval last_intr_tv = {0, 0};static int last_value = 0;static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);static spinlock_t hardware_lock = SPIN_LOCK_UNLOCKED;static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;static lirc_t rx_buf[RBUF_LEN]; unsigned int rx_tail = 0, rx_head = 0;static lirc_t tx_buf[WBUF_LEN];/* SECTION: Prototypes *//* Communication with user-space */static int lirc_open(struct inode * inode, struct file * file);static int lirc_close(struct inode * inode, struct file *file);static unsigned int lirc_poll(struct file * file, poll_table * wait);static ssize_t lirc_read(struct file * file, char * buf, size_t count, loff_t * ppos);static ssize_t lirc_write(struct file * file, const char * buf, size_t n, loff_t * pos);static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd, unsigned long arg);static void add_read_queue(int flag, unsigned long val);#ifdef MODULEstatic int init_chrdev(void);static void drop_chrdev(void);#endif /* Hardware */static void it87_interrupt(int irq, void * dev_id, struct pt_regs * regs);static void send_space(unsigned long len);static void send_pulse(unsigned long len);static void init_send(void);static void terminate_send(unsigned long len);static int init_hardware(void);static void drop_hardware(void); /* Initialisation */static int init_port(void);static void drop_port(void);int init_module(void);void cleanup_module(void);/* SECTION: Communication with user-space */static int lirc_open(struct inode * inode, struct file * file){ spin_lock(&dev_lock); if (MOD_IN_USE) { spin_unlock(&dev_lock); return -EBUSY; } MOD_INC_USE_COUNT; spin_unlock(&dev_lock); return 0;}static int lirc_close(struct inode * inode, struct file *file){ MOD_DEC_USE_COUNT; return 0;}static unsigned int lirc_poll(struct file * file, poll_table * wait){ poll_wait(file, &lirc_read_queue, wait); if (rx_head != rx_tail) return POLLIN | POLLRDNORM; return 0;}static ssize_t lirc_read(struct file * file, char * buf, size_t count, loff_t * ppos){ int n=0; int retval=0; while(n<count) { if(file->f_flags & O_NONBLOCK && rx_head==rx_tail) { retval = -EAGAIN; break; } retval=wait_event_interruptible(lirc_read_queue, rx_head!=rx_tail); if(retval) { break; } retval=verify_area(VERIFY_WRITE,(void *) buf+n, sizeof(lirc_t)); if (retval) { return retval; } copy_to_user((void *) buf+n,(void *) (rx_buf+rx_head), sizeof(lirc_t)); rx_head=(rx_head+1)&(RBUF_LEN-1); n+=sizeof(lirc_t); } if(n) { return n; } return retval;}static ssize_t lirc_write(struct file * file, const char * buf, size_t n, loff_t * pos){ int i; int retval; if(n%sizeof(lirc_t) || (n/sizeof(lirc_t)) > WBUF_LEN) return(-EINVAL); retval = verify_area(VERIFY_READ, buf, n); if (retval) return retval; copy_from_user(tx_buf, buf, n); i = 0; n/=sizeof(lirc_t); init_send(); while (1) { if (i >= n) break; if (tx_buf[i]) send_pulse(tx_buf[i]); i++; if (i >= n) break; if (tx_buf[i]) send_space(tx_buf[i]); i++; } terminate_send(tx_buf[i-1]); return n;}static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd, unsigned long arg){ int retval = 0; unsigned long value = 0; unsigned int ivalue; if (cmd == LIRC_GET_FEATURES) value = LIRC_CAN_SEND_PULSE | LIRC_CAN_SET_SEND_CARRIER | LIRC_CAN_REC_MODE2; else if (cmd == LIRC_GET_SEND_MODE) value = LIRC_MODE_PULSE; else if (cmd == LIRC_GET_REC_MODE) value = LIRC_MODE_MODE2; switch (cmd) { case LIRC_GET_FEATURES: case LIRC_GET_SEND_MODE: case LIRC_GET_REC_MODE: retval = put_user(value, (unsigned long *) arg); break; case LIRC_SET_SEND_MODE: case LIRC_SET_REC_MODE: retval = get_user(value, (unsigned long *) arg); break; case LIRC_SET_SEND_CARRIER: retval=get_user(ivalue,(unsigned int *) arg); if(retval) return(retval); ivalue /= 1000; if (ivalue > IT87_CIR_FREQ_MAX || ivalue < IT87_CIR_FREQ_MIN) return(-EINVAL); it87_freq = ivalue; { unsigned long hw_flags; spin_lock_irqsave(&hardware_lock, hw_flags); outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) | (it87_freq - IT87_CIR_FREQ_MIN) << 3), io + IT87_CIR_TCR2); spin_unlock_irqrestore(&hardware_lock, hw_flags);#ifdef DEBUG printk(KERN_DEBUG LIRC_DRIVER_NAME " demodulation frequency: %d kHz\n", it87_freq);#endif } break; default: retval = -ENOIOCTLCMD; } if (retval) return retval; if (cmd == LIRC_SET_REC_MODE) { if (value != LIRC_MODE_MODE2) retval = -ENOSYS; } else if (cmd == LIRC_SET_SEND_MODE) { if (value != LIRC_MODE_PULSE) retval = -ENOSYS; } return retval;}static void add_read_queue(int flag, unsigned long val){ unsigned int new_rx_tail; lirc_t newval;#ifdef DEBUG_SIGNAL printk(KERN_DEBUG LIRC_DRIVER_NAME ": add flag %d with val %lu\n", flag,val);#endif newval = val & PULSE_MASK; /* statistically pulses are ~TIME_CONST/2 too long: we could maybe make this more exactly but this is good enough */ if(flag) /* pulse */ { if(newval>TIME_CONST/2) { newval-=TIME_CONST/2; } else /* should not ever happen */ { newval=1; } newval|=PULSE_BIT; } else { newval+=TIME_CONST/2; } new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); if (new_rx_tail == rx_head) {#ifdef DEBUG printk(KERN_WARNING LIRC_DRIVER_NAME ": Buffer overrun.\n");#endif return; } rx_buf[rx_tail] = newval; rx_tail = new_rx_tail; wake_up_interruptible(&lirc_read_queue);}static struct file_operations lirc_fops = { read: lirc_read, write: lirc_write, poll: lirc_poll, ioctl: lirc_ioctl, open: lirc_open, release: lirc_close,};#ifdef MODULEint init_chrdev(void){ int retval; retval = register_chrdev(major, LIRC_DRIVER_NAME, &lirc_fops); if (retval < 0) { printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); return retval; } return 0;}static void drop_chrdev(void){ unregister_chrdev(major, LIRC_DRIVER_NAME);}#endif/* SECTION: Hardware */static long delta(struct timeval * tv1, struct timeval * tv2){ unsigned long deltv; deltv = tv2->tv_sec - tv1->tv_sec; if (deltv > 15) deltv = 0xFFFFFF; else deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec; return deltv;}static void it87_timeout(unsigned long data) { /* if last received signal was a pulse, but receiving stopped within the 9 bit frame, we need to finish this pulse and simulate a signal change to from pulse to space. Otherwise upper layers will receive two sequences next time. */ unsigned long flags; unsigned long pulse_end; /* avoid interference with interrupt */ spin_lock_irqsave(&timer_lock, flags); if (last_value) { /* determine 'virtual' pulse end: */ pulse_end = delta(&last_tv, &last_intr_tv);#ifdef DEBUG_SIGNAL printk(KERN_DEBUG LIRC_DRIVER_NAME ": timeout add %d for %lu usec\n", last_value, pulse_end);#endif add_read_queue(last_value, pulse_end); last_value = 0; last_tv=last_intr_tv; } spin_unlock_irqrestore(&timer_lock, flags); }static void it87_interrupt(int irq, void * dev_id, struct pt_regs * regs){ unsigned char data; struct timeval curr_tv; static unsigned long deltv; unsigned long deltintrtv; unsigned long flags, hw_flags; int iir, lsr; int fifo = 0; iir = inb(io + IT87_CIR_IIR); switch (iir & IT87_CIR_IIR_IID) { case 0x4: case 0x6: lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO | IT87_CIR_RSR_RXFBC); fifo = lsr & IT87_CIR_RSR_RXFBC;#ifdef DEBUG_SIGNAL printk(KERN_DEBUG LIRC_DRIVER_NAME
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -