⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lad_pad.c

📁 Linux嵌入式设计配套光盘,学习嵌入式设计可参考
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  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 + -