📄 lad_pad.c
字号:
/* * ladpad.c: A device driver for the Laddie remote entry * station with a 16 button keypad, an LED, and a HD44780 * based alphanumeric LCD display. * * Copyright (c) 2005,2006 by Laddie Group, Inc * *//*************************************************************************** * LICENSE: * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***************************************************************************//* Updates for 2.6 and other minor mods (Bill): * (see lwn.net/Articles/driver-porting for porting to 2.6) * use parameter for cmd_delay * use macros to specify module init and exit functions * use the cdev structure for char devices * update *offset pointers for read and write * use get_user for the read function * use semaphores to prevent multiple reads or multiple writes * (but allow simultaneous read & write) * remove try_module_get, module_put, see lwn.net/Articles/22197/ * use kobject_set_name in ladpad_init */#include <linux/kernel.h>#include <linux/module.h>#include <linux/ioport.h> /* for request_region() */#include <linux/fs.h>#include <linux/poll.h> /* for poll_table */#include <linux/syscalls.h> /* for sys_mknod */#include <linux/cdev.h> /* for struct cdev */#include <asm/uaccess.h> /* for put_user */#include <asm/delay.h> /* for udelay */#include <asm/io.h>#include <asm/semaphore.h> /* for read and write semaphores */#include "lad_pad.h"/*************************************************************************** * - Limits and defines * Limits on the size and number of resources.... ***************************************************************************//* key down if (in(port) & ANYKEYDOWN) != 0) */#define ANYKEYMASK 0x78/* Name as it appears in /proc/devices */#define DEVNAME "ladpad"/* The states for interpreting the characters from the user */#define LAD_NORM 0#define LAD_CMD 1#define LAD_LED 2/* Defines to help set the bits in the control port. The * hardware is set up with bit0 equal the "E" strobe, bit1 * equal the RS control line, and bit2 equal the LED. The * first two of these pins are inverted in the hardware so * we invert their sense here. */#define E0 0x01#define E1 0x00#define RSCMD 0x02#define RSDATA 0x00#define LED0 0x00#define LED1 0x04/* The 44780 requires a delay between characters. We could * use udelay but to get the delays we need would waste more * time than we'd like. Instead we use a circular buffer to * hold the output characters and send out a single character * in the time out function while we scan the keypad. *//* The number of characters in the output buffer. Two lines * 16 characters plus some command codes and a little extra */#define CBUFSZ 50#define LADPAD_NR_PORTS 3 /* Three I/O addresses for parallel port *//*************************************************************************** * - Function prototypes ***************************************************************************/static int ladpad_init(void);static void ladpad_exit(void);static int ladpad_open(struct inode *, struct file *);static unsigned int ladpad_poll(struct file *, poll_table *);static int ladpad_close(struct inode *, struct file *);static ssize_t ladpad_read(struct file *, char *, size_t, loff_t *);static ssize_t ladpad_write(struct file *, const char *, size_t, loff_t *);static void keypad_scan(unsigned long);static void out44780(unsigned char);/*************************************************************************** * - Variable allocation and initialization ***************************************************************************//* Module stuff */static dev_t ladpad_dev; /* Dev no. assigned to our device driver */static struct cdev ladpad_cdev; /* Kernel char device structure. *//* We use semaphores to prevent multiple readers or multiple writers. We do * allow one reader and one writer to access the device simultaneously. * Initialize as unlocked. */DECLARE_MUTEX(ladpad_read_sem);DECLARE_MUTEX(ladpad_write_sem);/* Module parameters */static int io_base = 0x378; /* Par port base I/O address *//* The debug level. 0 suppresses all kernel messages *//* 1 gives only Alerts *//* 2 gives warnings *//* 3 gives all messages */static int debuglvl = 2; /* printk verbosity. Higher=more verbose *//* Keypad stuff */static struct timer_list keypad_timer; /* scan timer for keypad */DECLARE_WAIT_QUEUE_HEAD(WaitQ); /* Wait queue while waiting of key press */static char Key_Press = (char) 0; /* ASCII code of last key pressed */static int last_0 = 0; /* Used for keypad debounce */static int last_1 = 0; /* Used for keypad debounce */static int scan_time = 2; /* Hundredth of a sec between scans *//* LED stuff */static int led = 0; /* LED defaults to off */static int led_counter = 0; /* num keypad scans in this state */static int led_state = LED0; /* what to send to io port *//* LCD stuff */static int input_state = LAD_NORM; /* input state */static int e_state = E0; /* image of what is on E control line */static int rs_state = RSCMD; /* image of what is on RS control line *//* Circular buffer */static int iindx = 0; /* in index, where to store next char */static int oindx = 0; /* out index, where to get next char */static int ccnt = 0; /* number of characters in queue */static unsigned char cbuf[CBUFSZ]; /* a circular buffer */DECLARE_WAIT_QUEUE_HEAD(SendQ); /* Wait queue for output buffer space */static struct file_operations fops = { .owner = THIS_MODULE, .read = ladpad_read, .write = ladpad_write, .open = ladpad_open, .poll = ladpad_poll, .release = ladpad_close};MODULE_DESCRIPTION("Keypad, LED, and LCD display for Laddie.");MODULE_AUTHOR("Bob Smith");MODULE_LICENSE("GPL");module_param(io_base, int, S_IRUSR);MODULE_PARM_DESC(io_base, "Parallel port base I/O address. Default=0x378");module_param(debuglvl, int, S_IRUSR);MODULE_PARM_DESC(debuglvl, "Debug level. Higher=more verbose. Default=2");module_param(scan_time, int, S_IRUSR);MODULE_PARM_DESC(scan_time, "Hundredth of a sec between keypad scans");/*************************************************************************** * ladpad_init() - Set up hardware, variables, and timers for the * Laddie remote entry station. ***************************************************************************/static int ladpad_init(void){ int result; /* Allocate major number with single minor number starting at 0. */ result = alloc_chrdev_region(&ladpad_dev, 0, 1, DEVNAME); if(result < 0){ printk(KERN_ALERT "ladpad: failed to allocate major number\n"); return result; } /* Initialize and register the char device structure. */ /* cdev_init assigns the file operations, and initializes the embedded * kobject, setting the reference count to one */ cdev_init(&ladpad_cdev, &fops); kobject_set_name(&ladpad_cdev.kobj, DEVNAME); ladpad_cdev.owner = THIS_MODULE; /* The device is live after the following call (if successful). */ result = cdev_add(&ladpad_cdev, ladpad_dev, 1); /* 1 device number added */ if(result < 0){ printk(KERN_ALERT "ladpad: error %d adding char device\n", result); /* Ist the following necessary? */ kobject_put(&ladpad_cdev.kobj); /* Dec ref count and free kobject */ unregister_chrdev_region(ladpad_dev, 1); /* Free single device number */ return result; } /* Get the I/O port number */ if (!request_region(io_base, LADPAD_NR_PORTS, DEVNAME)) { printk(KERN_ALERT "ladpad: failed to allocate I/O port 0x%x\n", io_base); unregister_chrdev_region(ladpad_dev, 1); /* Free single device number */ return -ENODEV; /* failure */ } /* Init the 44780 Controller with a "clear" command. Set the * number of bits in the interface, the # of lines, and the font. * Set the cursor to move right after a new char. Give the "home" * command and turn on the display. */ cbuf[iindx++] = LADPAD_CMD; cbuf[iindx++] = 0x01; /* clear display */ cbuf[iindx++] = LADPAD_CMD; cbuf[iindx++] = 0x38; /* 8 bits / 2 lines / 5x7 font */ cbuf[iindx++] = LADPAD_CMD; cbuf[iindx++] = 0x14; /* move cursor / move right */ cbuf[iindx++] = LADPAD_CMD; cbuf[iindx++] = 0x03; /* home display */ cbuf[iindx++] = LADPAD_CMD; cbuf[iindx++] = 0x0C; /* Display=On, No cursor */ ccnt += 10; /* Set up control lines for LCD and LED */ rs_state = RSDATA; e_state = E0; outb((led_state | rs_state | e_state), io_base + 2); printk(KERN_INFO "ladpad: control: led=%d, RS=%d, E=%d\n", led_state, rs_state, e_state); /* Start a timer to do the keypad scan and LCD output */ init_timer(&keypad_timer); keypad_timer.function = keypad_scan; keypad_timer.data = (unsigned long) 0; keypad_timer.expires = jiffies + (scan_time * (HZ / 100)); add_timer(&keypad_timer); if (debuglvl >= 2) { printk(KERN_INFO "ladpad at 0x%x assigned to Major=%d\n", io_base, MAJOR(ladpad_dev)); } return 0; /* success */}/*************************************************************************** * ladpad_exit() - Cleanly remove the ladpad module from the kernel * ***************************************************************************/static void ladpad_exit(void){ /* Delete the keypad scan timer */ del_timer(&keypad_timer); /* Free the IO region for the parport */ /* release_resource(base_res);*/ release_region(io_base, LADPAD_NR_PORTS); /* Release I/O addresses */ /* Delete the char device */ cdev_del(&ladpad_cdev); /* Release the major device number */ unregister_chrdev_region(ladpad_dev, 1); /* Release 1 device number */ /* Log module unload */ if (debuglvl >= 2) { printk(KERN_INFO "ladpad removed: major number %d.\n", MAJOR(ladpad_dev)); }}/*************************************************************************** * ladpad_open() - Handles the open() of the ladpad device. * * This routine uses semaphores to prevent simultaneous writes or simultaneous * reads. ***************************************************************************/static int ladpad_open( struct inode *inode, struct file *pfile){ /* Take the semaphore(s) appropriate for the access requested. If a * semaphore is unavailable, don't block; return immediately with -EBUSY. */ if(pfile->f_mode & FMODE_READ){ if(down_trylock(&ladpad_read_sem)){ /* Not available for reading. */ return -EBUSY; } } if(pfile->f_mode & FMODE_WRITE){ if(down_trylock(&ladpad_write_sem)){ /* Not available for writing. */ /* If read semaphore taken, give it back before returning. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -