qpmouse.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:8k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * linux/drivers/char/qpmouse.c
  3.  *
  4.  * Driver for a 82C710 C&T mouse interface chip.
  5.  *
  6.  * Based on the PS/2 driver by Johan Myreen.
  7.  *
  8.  * Corrections in device setup for some laptop mice & trackballs.
  9.  * 02Feb93  (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
  10.  *
  11.  * Modified by Johan Myreen (jem@iki.fi) 04Aug93
  12.  *   to include support for QuickPort mouse.
  13.  *
  14.  * Changed references to "QuickPort" with "82C710" since "QuickPort"
  15.  * is not what this driver is all about -- QuickPort is just a
  16.  * connector type, and this driver is for the mouse port on the Chips
  17.  * & Technologies 82C710 interface chip. 15Nov93 jem@iki.fi
  18.  *
  19.  * Added support for SIGIO. 28Jul95 jem@iki.fi
  20.  *
  21.  * Rearranged SIGIO support to use code from tty_io.  9Sept95 ctm@ardi.com
  22.  *
  23.  * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
  24.  */
  25. #include <linux/module.h>
  26. #include <linux/kernel.h>
  27. #include <linux/sched.h>
  28. #include <linux/interrupt.h>
  29. #include <linux/fcntl.h>
  30. #include <linux/errno.h>
  31. #include <linux/timer.h>
  32. #include <linux/slab.h>
  33. #include <linux/miscdevice.h>
  34. #include <linux/random.h>
  35. #include <linux/poll.h>
  36. #include <linux/init.h>
  37. #include <linux/smp_lock.h>
  38. #include <asm/io.h>
  39. #include <asm/uaccess.h>
  40. #include <asm/system.h>
  41. #include <asm/semaphore.h>
  42. #include <linux/pc_keyb.h> /* mouse enable command.. */
  43. /*
  44.  * We use the same minor number as the PS/2 mouse for (bad) historical
  45.  * reasons..
  46.  */
  47. #define PSMOUSE_MINOR      1         /* Minor device # for this mouse */
  48. #define QP_BUF_SIZE 2048
  49. struct qp_queue {
  50. unsigned long head;
  51. unsigned long tail;
  52. wait_queue_head_t proc_list;
  53. struct fasync_struct *fasync;
  54. unsigned char buf[QP_BUF_SIZE];
  55. };
  56. static struct qp_queue *queue;
  57. static unsigned int get_from_queue(void)
  58. {
  59. unsigned int result;
  60. unsigned long flags;
  61. save_flags(flags);
  62. cli();
  63. result = queue->buf[queue->tail];
  64. queue->tail = (queue->tail + 1) & (QP_BUF_SIZE-1);
  65. restore_flags(flags);
  66. return result;
  67. }
  68. static inline int queue_empty(void)
  69. {
  70. return queue->head == queue->tail;
  71. }
  72. static int fasync_qp(int fd, struct file *filp, int on)
  73. {
  74. int retval;
  75. retval = fasync_helper(fd, filp, on, &queue->fasync);
  76. if (retval < 0)
  77. return retval;
  78. return 0;
  79. }
  80. /*
  81.  * 82C710 Interface
  82.  */
  83. #define QP_DATA         0x310 /* Data Port I/O Address */
  84. #define QP_STATUS       0x311 /* Status Port I/O Address */
  85. #define QP_DEV_IDLE     0x01 /* Device Idle */
  86. #define QP_RX_FULL      0x02 /* Device Char received */
  87. #define QP_TX_IDLE      0x04 /* Device XMIT Idle */
  88. #define QP_RESET        0x08 /* Device Reset */
  89. #define QP_INTS_ON      0x10 /* Device Interrupt On */
  90. #define QP_ERROR_FLAG   0x20 /* Device Error */
  91. #define QP_CLEAR        0x40 /* Device Clear */
  92. #define QP_ENABLE       0x80 /* Device Enable */
  93. #define QP_IRQ          12
  94. static int qp_present;
  95. static int qp_count;
  96. static int qp_data = QP_DATA;
  97. static int qp_status = QP_STATUS;
  98. static int poll_qp_status(void);
  99. static int probe_qp(void);
  100. /*
  101.  * Interrupt handler for the 82C710 mouse port. A character
  102.  * is waiting in the 82C710.
  103.  */
  104. static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
  105. {
  106. int head = queue->head;
  107. int maxhead = (queue->tail-1) & (QP_BUF_SIZE-1);
  108. add_mouse_randomness(queue->buf[head] = inb(qp_data));
  109. if (head != maxhead) {
  110. head++;
  111. head &= QP_BUF_SIZE-1;
  112. }
  113. queue->head = head;
  114. kill_fasync(&queue->fasync, SIGIO, POLL_IN);
  115. wake_up_interruptible(&queue->proc_list);
  116. }
  117. static int release_qp(struct inode * inode, struct file * file)
  118. {
  119. unsigned char status;
  120. lock_kernel();
  121. fasync_qp(-1, file, 0);
  122. if (!--qp_count) {
  123. if (!poll_qp_status())
  124. printk(KERN_WARNING "Warning: Mouse device busy in release_qp()n");
  125. status = inb_p(qp_status);
  126. outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
  127. if (!poll_qp_status())
  128. printk(KERN_WARNING "Warning: Mouse device busy in release_qp()n");
  129. free_irq(QP_IRQ, NULL);
  130. }
  131. unlock_kernel();
  132. return 0;
  133. }
  134. /*
  135.  * Install interrupt handler.
  136.  * Enable the device, enable interrupts. 
  137.  */
  138. static int open_qp(struct inode * inode, struct file * file)
  139. {
  140. unsigned char status;
  141. if (!qp_present)
  142. return -EINVAL;
  143. if (qp_count++)
  144. return 0;
  145. if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) {
  146. qp_count--;
  147. return -EBUSY;
  148. }
  149. status = inb_p(qp_status);
  150. status |= (QP_ENABLE|QP_RESET);
  151. outb_p(status, qp_status);
  152. status &= ~(QP_RESET);
  153. outb_p(status, qp_status);
  154. queue->head = queue->tail = 0;          /* Flush input queue */
  155. status |= QP_INTS_ON;
  156. outb_p(status, qp_status);              /* Enable interrupts */
  157. while (!poll_qp_status()) {
  158. printk(KERN_ERR "Error: Mouse device busy in open_qp()n");
  159. qp_count--;
  160. status &= ~(QP_ENABLE|QP_INTS_ON);
  161. outb_p(status, qp_status);
  162. free_irq(QP_IRQ, NULL);
  163. return -EBUSY;
  164. }
  165. outb_p(AUX_ENABLE_DEV, qp_data); /* Wake up mouse */
  166. return 0;
  167. }
  168. /*
  169.  * Write to the 82C710 mouse device.
  170.  */
  171. static ssize_t write_qp(struct file * file, const char * buffer,
  172. size_t count, loff_t *ppos)
  173. {
  174. ssize_t i = count;
  175. while (i--) {
  176. char c;
  177. if (!poll_qp_status())
  178. return -EIO;
  179. get_user(c, buffer++);
  180. outb_p(c, qp_data);
  181. }
  182. file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
  183. return count;
  184. }
  185. static unsigned int poll_qp(struct file *file, poll_table * wait)
  186. {
  187. poll_wait(file, &queue->proc_list, wait);
  188. if (!queue_empty())
  189. return POLLIN | POLLRDNORM;
  190. return 0;
  191. }
  192. /*
  193.  * Wait for device to send output char and flush any input char.
  194.  */
  195. #define MAX_RETRIES (60)
  196. static int poll_qp_status(void)
  197. {
  198. int retries=0;
  199. while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
  200.        != (QP_DEV_IDLE|QP_TX_IDLE)
  201.        && retries < MAX_RETRIES) {
  202. if (inb_p(qp_status)&(QP_RX_FULL))
  203. inb_p(qp_data);
  204. current->state = TASK_INTERRUPTIBLE;
  205. schedule_timeout((5*HZ + 99) / 100);
  206. retries++;
  207. }
  208. return !(retries==MAX_RETRIES);
  209. }
  210. /*
  211.  * Put bytes from input queue to buffer.
  212.  */
  213. static ssize_t read_qp(struct file * file, char * buffer,
  214. size_t count, loff_t *ppos)
  215. {
  216. DECLARE_WAITQUEUE(wait, current);
  217. ssize_t i = count;
  218. unsigned char c;
  219. if (queue_empty()) {
  220. if (file->f_flags & O_NONBLOCK)
  221. return -EAGAIN;
  222. add_wait_queue(&queue->proc_list, &wait);
  223. repeat:
  224. set_current_state(TASK_INTERRUPTIBLE);
  225. if (queue_empty() && !signal_pending(current)) {
  226. schedule();
  227. goto repeat;
  228. }
  229. current->state = TASK_RUNNING;
  230. remove_wait_queue(&queue->proc_list, &wait);
  231. }
  232. while (i > 0 && !queue_empty()) {
  233. c = get_from_queue();
  234. put_user(c, buffer++);
  235. i--;
  236. }
  237. if (count-i) {
  238. file->f_dentry->d_inode->i_atime = CURRENT_TIME;
  239. return count-i;
  240. }
  241. if (signal_pending(current))
  242. return -ERESTARTSYS;
  243. return 0;
  244. }
  245. struct file_operations qp_fops = {
  246. owner: THIS_MODULE,
  247. read: read_qp,
  248. write: write_qp,
  249. poll: poll_qp,
  250. open: open_qp,
  251. release: release_qp,
  252. fasync: fasync_qp,
  253. };
  254. /*
  255.  * Initialize driver.
  256.  */
  257. static struct miscdevice qp_mouse = {
  258. minor: PSMOUSE_MINOR,
  259. name: "QPmouse",
  260. fops: &qp_fops,
  261. };
  262. /*
  263.  * Function to read register in 82C710.
  264.  */
  265. static inline unsigned char read_710(unsigned char index)
  266. {
  267. outb_p(index, 0x390); /* Write index */
  268. return inb_p(0x391); /* Read the data */
  269. }
  270. /*
  271.  * See if we can find a 82C710 device. Read mouse address.
  272.  */
  273. static int __init probe_qp(void)
  274. {
  275. outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */
  276. outb_p(0xaa, 0x3fa); /* Inverse of 55 */
  277. outb_p(0x36, 0x3fa); /* Address the chip */
  278. outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */
  279. outb_p(0x1b, 0x2fa); /* Inverse of e4 */
  280. if (read_710(0x0f) != 0xe4) /* Config address found? */
  281.   return 0; /* No: no 82C710 here */
  282. qp_data = read_710(0x0d)*4; /* Get mouse I/O address */
  283. qp_status = qp_data+1;
  284. outb_p(0x0f, 0x390);
  285. outb_p(0x0f, 0x391); /* Close config mode */
  286. return 1;
  287. }
  288. static char msg_banner[] __initdata = KERN_INFO "82C710 type pointing device detected -- driver installed.n";
  289. static char msg_nomem[]  __initdata = KERN_ERR "qpmouse: no queue memory.n";
  290. static int __init qpmouse_init_driver(void)
  291. {
  292. if (!probe_qp())
  293. return -EIO;
  294. printk(msg_banner);
  295. /* printk("82C710 address = %x (should be 0x310)n", qp_data); */
  296. queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
  297. if (queue == NULL) {
  298. printk(msg_nomem);
  299. return -ENOMEM;
  300. }
  301. qp_present = 1;
  302. misc_register(&qp_mouse);
  303. memset(queue, 0, sizeof(*queue));
  304. queue->head = queue->tail = 0;
  305. init_waitqueue_head(&queue->proc_list);
  306. return 0;
  307. }
  308. static void __exit qpmouse_exit_driver(void)
  309. {
  310. misc_deregister(&qp_mouse);
  311. kfree(queue);
  312. }
  313. module_init(qpmouse_init_driver);
  314. module_exit(qpmouse_exit_driver);
  315. MODULE_LICENSE("GPL");
  316. EXPORT_NO_SYMBOLS;