📄 lirc_sir.c
字号:
/* * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk> * * lirc_sir - Device driver for use with SIR (serial infra red) * mode of IrDA on many notebooks. * * 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 * * * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> : * added timeout and relaxed pulse detection, removed gap bug * * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> : * added support for Tekram Irmate 210 (sending does not work yet, * kind of disappointing that nobody was able to implement that * before), * major clean-up * * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> : * added support for StrongARM SA1100 embedded microprocessor * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King */#include <linux/version.h>#if LINUX_VERSION_CODE >= 0x020100#define KERNEL_2_1#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)#define KERNEL_2_3#endif#else#define KERNEL_2_0#endif#include <linux/module.h>#ifdef HAVE_CONFIG_H# include <config.h>#endif #include <linux/config.h>#if !defined(LIRC_ON_SA1100) && !defined(CONFIG_SERIAL_MODULE)#warning "******************************************"#warning " Your serial port driver is compiled into "#warning " the kernel. You will have to release the "#warning " port you want to use for LIRC with: "#warning " setserial /dev/ttySx uart none "#warning "******************************************"#endif#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>#ifdef KERNEL_2_1#include <linux/poll.h>#endif#include <asm/system.h>#include <asm/segment.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/fcntl.h>#ifdef LIRC_ON_SA1100#include <asm/hardware.h>#ifdef CONFIG_SA1100_COLLIE#include <asm/arch/tc35143.h>#include <asm/ucb1200.h>#endif#endif#include <linux/timer.h>#include "drivers/lirc.h"/* SECTION: Definitions *//**************************** Tekram dongle ***************************/#ifdef LIRC_SIR_TEKRAM/* stolen from kernel source *//* definitions for Tekram dongle */#define TEKRAM_115200 0x00#define TEKRAM_57600 0x01#define TEKRAM_38400 0x02#define TEKRAM_19200 0x03#define TEKRAM_9600 0x04#define TEKRAM_2400 0x08#define TEKRAM_PW 0x10 /* Pulse select bit *//* 10bit * 1s/115200bit in milli seconds = 87ms*/#define TIME_CONST (10000000ul/115200ul)#endif#ifdef LIRC_SIR_ACTISYS_ACT200Lstatic void init_act200(void);#endif/******************************* SA1100 ********************************/#ifdef LIRC_ON_SA1100struct sa1100_ser2_registers{ /* HSSP control register */ unsigned char hscr0; /* UART registers */ unsigned char utcr0; unsigned char utcr1; unsigned char utcr2; unsigned char utcr3; unsigned char utcr4; unsigned char utdr; unsigned char utsr0; unsigned char utsr1;} sr;static int irq=IRQ_Ser2ICP;#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0/* pulse/space ratio of 50/50 */unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);/* 1000000/freq-pulse_width */unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);unsigned int freq = 38000; /* modulation frequency */unsigned int duty_cycle = 50; /* duty cycle of 50% */#endif#define RBUF_LEN 1024#define WBUF_LEN 1024#define LIRC_DRIVER_NAME "lirc_sir"#ifndef LIRC_SIR_TEKRAM#define PULSE '['/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/#define TIME_CONST (9000000ul/115200ul)#endif/* timeout for sequences in jiffies (=5/100s) *//* must be longer than TIME_CONST */#define SIR_TIMEOUT (HZ*5/100)static int major = LIRC_MAJOR;#ifndef LIRC_ON_SA1100static int io = LIRC_PORT;static int irq = LIRC_IRQ;#endifstatic 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;#ifdef KERNEL_2_3static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);#elsestatic struct wait_queue * lirc_read_queue = NULL;#endifstatic 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;#ifndef LIRC_SIR_TEKRAMstatic lirc_t tx_buf[WBUF_LEN];#endif/* SECTION: Prototypes *//* Communication with user-space */static int lirc_open(struct inode * inode, struct file * file);#ifdef KERNEL_2_1static int lirc_close(struct inode * inode, struct file *file);static unsigned int lirc_poll(struct file * file, poll_table * wait);#elsestatic void lirc_close(struct inode * inode, struct file *file);static int lirc_select(struct inode * inode, struct file * file, int type, select_table * wait);#endifstatic 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 sir_interrupt(int irq, void * dev_id, struct pt_regs * regs);#ifndef LIRC_SIR_TEKRAMstatic void send_space(unsigned long len);static void send_pulse(unsigned long len);#endifstatic 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);#ifdef LIRC_ON_SA1100void inline on(void){ PPSR|=PPC_TXD2;} void inline off(void){ PPSR&=~PPC_TXD2;}#elsestatic inline unsigned int sinp(int offset){ return inb(io + offset);}static inline void soutp(int offset, int value){ outb(value, io + offset);}#endif#ifndef MAX_UDELAY_MS#define MAX_UDELAY_US 5000#else#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)#endifstatic inline void safe_udelay(unsigned long usecs){ while(usecs>MAX_UDELAY_US) { udelay(MAX_UDELAY_US); usecs-=MAX_UDELAY_US; } udelay(usecs);}/* 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;}#ifdef KERNEL_2_1static int lirc_close(struct inode * inode, struct file *file)#elsestatic void lirc_close(struct inode * inode, struct file *file)#endif{ MOD_DEC_USE_COUNT;#ifdef KERNEL_2_1 return 0;#endif}#ifdef KERNEL_2_1static 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;}#elsestatic int lirc_select(struct inode * inode, struct file * file, int type, select_table * wait){ if (type != SEL_IN) return 0; if (rx_head != rx_tail) return 1; select_wait(&lirc_read_queue, wait); return 0;}#endifstatic ssize_t lirc_read(struct file * file, char * buf, size_t count, loff_t * ppos){ int n=0; int retval = 0;#ifdef KERNEL_2_3 DECLARE_WAITQUEUE(wait,current);#else struct wait_queue wait={current,NULL};#endif if(n%sizeof(lirc_t)) return(-EINVAL); add_wait_queue(&lirc_read_queue,&wait); current->state=TASK_INTERRUPTIBLE; while(n<count) { if(rx_head!=rx_tail) { retval=verify_area(VERIFY_WRITE, (void *) buf+n,sizeof(lirc_t)); if (retval) { return retval; }#ifdef KERNEL_2_1 copy_to_user((void *) buf+n,(void *) (rx_buf+rx_head), sizeof(lirc_t));#else memcpy_tofs((void *) buf+n,(void *) (rx_buf+rx_head), sizeof(lirc_t));#endif rx_head=(rx_head+1)&(RBUF_LEN-1); n+=sizeof(lirc_t); } else { if(file->f_flags & O_NONBLOCK) { retval=-EAGAIN; break; }# ifdef KERNEL_2_1 if(signal_pending(current)) { retval=-ERESTARTSYS; break; }# else if(current->signal & ~current->blocked) { retval=-EINTR; break; }# endif schedule(); current->state=TASK_INTERRUPTIBLE; } } remove_wait_queue(&lirc_read_queue,&wait); current->state=TASK_RUNNING; return (n ? n : retval);}static ssize_t lirc_write(struct file * file, const char * buf, size_t n, loff_t * pos){ unsigned long flags;#ifdef LIRC_SIR_TEKRAM return(-EBADF);#else 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);#ifdef LIRC_ON_SA1100 /* disable receiver */ Ser2UTCR3=0;#endif save_flags(flags);cli(); 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++; } restore_flags(flags);#ifdef LIRC_ON_SA1100 off(); udelay(1000); /* wait 1ms for IR diode to recover */ Ser2UTCR3=0; /* clear status register to prevent unwanted interrupts */ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); /* enable receiver */ Ser2UTCR3=UTCR3_RXE|UTCR3_RIE;#endif return n;#endif}static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd, unsigned long arg){ int retval = 0; unsigned long value = 0;#ifdef LIRC_ON_SA1100 unsigned int ivalue;#endif#ifdef LIRC_SIR_TEKRAM if (cmd == LIRC_GET_FEATURES) value = LIRC_CAN_REC_MODE2; else if (cmd == LIRC_GET_SEND_MODE) value = 0; else if (cmd == LIRC_GET_REC_MODE) value = LIRC_MODE_MODE2;#elif defined(LIRC_ON_SA1100) if (cmd == LIRC_GET_FEATURES) value = LIRC_CAN_SEND_PULSE | LIRC_CAN_SET_SEND_DUTY_CYCLE | 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;#else if (cmd == LIRC_GET_FEATURES) value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; else if (cmd == LIRC_GET_SEND_MODE) value = LIRC_MODE_PULSE; else if (cmd == LIRC_GET_REC_MODE)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -