📄 sunkbd.c
字号:
}static void do_lock(unsigned char value, char up_flag){ if (up_flag || rep) return; chg_vc_kbd_lock(kbd, value);}/* * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, * or (ii) whatever pattern of lights people want to show using KDSETLED, * or (iii) specified bits of specified words in kernel memory. */static unsigned char ledstate = 0xff; /* undefined */static unsigned char ledioctl;unsigned char sun_getledstate(void) { return ledstate;}void sun_setledstate(struct kbd_struct *kbd, unsigned int led) { if (!(led & ~7)) { ledioctl = led; kbd->ledmode = LED_SHOW_IOCTL; } else kbd->ledmode = LED_SHOW_FLAGS; set_leds();}static struct ledptr { unsigned int *addr; unsigned int mask; unsigned char valid:1;} ledptrs[3];void register_leds(int console, unsigned int led, unsigned int *addr, unsigned int mask) { struct kbd_struct *kbd = kbd_table + console; if (led < 3) { ledptrs[led].addr = addr; ledptrs[led].mask = mask; ledptrs[led].valid = 1; kbd->ledmode = LED_SHOW_MEM; } else kbd->ledmode = LED_SHOW_FLAGS;}static inline unsigned char getleds(void){ struct kbd_struct *kbd = kbd_table + fg_console; unsigned char leds; if (kbd->ledmode == LED_SHOW_IOCTL) return ledioctl; leds = kbd->ledflagstate; if (kbd->ledmode == LED_SHOW_MEM) { if (ledptrs[0].valid) { if (*ledptrs[0].addr & ledptrs[0].mask) leds |= 1; else leds &= ~1; } if (ledptrs[1].valid) { if (*ledptrs[1].addr & ledptrs[1].mask) leds |= 2; else leds &= ~2; } if (ledptrs[2].valid) { if (*ledptrs[2].addr & ledptrs[2].mask) leds |= 4; else leds &= ~4; } } return leds;}/* * This routine is the bottom half of the keyboard interrupt * routine, and runs with all interrupts enabled. It does * console changing, led setting and copy_to_cooked, which can * take a reasonably long time. * * Aside from timing (which isn't really that important for * keyboard interrupts as they happen often), using the software * interrupt routines for this thing allows us to easily mask * this when we don't want any of the above to happen. Not yet * used, but this allows for easy and efficient race-condition * prevention later on. */static unsigned char sunkbd_ledstate = 0xff; /* undefined */void sun_kbd_bh(unsigned long dummy){ unsigned long flags; unsigned char leds, kbd_leds; spin_lock_irqsave(&sunkbd_lock, flags); leds = getleds(); kbd_leds = vcleds_to_sunkbd(leds); if (kbd_leds != sunkbd_ledstate) { ledstate = leds; sunkbd_ledstate = kbd_leds; send_cmd(SKBDCMD_SETLED); send_cmd(kbd_leds); } spin_unlock_irqrestore(&sunkbd_lock, flags);}/* Support for keyboard "beeps". */ /* Timer routine to turn off the beep after the interval expires. */static void sunkbd_kd_nosound(unsigned long __unused){ unsigned long flags; spin_lock_irqsave(&sunkbd_lock, flags); send_cmd(SKBDCMD_BELLOFF); spin_unlock_irqrestore(&sunkbd_lock, flags);}/* * Initiate a keyboard beep. If the frequency is zero, then we stop * the beep. Any other frequency will start a monotone beep. The beep * will be stopped by a timer after "ticks" jiffies. If ticks is 0, * then we do not start a timer. */static void sunkbd_kd_mksound(unsigned int hz, unsigned int ticks){ unsigned long flags; static struct timer_list sound_timer = { function: sunkbd_kd_nosound }; spin_lock_irqsave(&sunkbd_lock, flags); del_timer(&sound_timer); if (hz) { send_cmd(SKBDCMD_BELLON); if (ticks) { sound_timer.expires = jiffies + ticks; add_timer(&sound_timer); } } else send_cmd(SKBDCMD_BELLOFF); spin_unlock_irqrestore(&sunkbd_lock, flags);}extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);int __init sun_kbd_init(void){ int i, opt_node; struct kbd_struct kbd0; extern struct tty_driver console_driver; kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS; kbd0.ledmode = LED_SHOW_FLAGS; kbd0.lockstate = KBD_DEFLOCK; kbd0.slockstate = 0; kbd0.modeflags = KBD_DEFMODE; kbd0.kbdmode = VC_XLATE; for (i = 0 ; i < MAX_NR_CONSOLES ; i++) kbd_table[i] = kbd0; ttytab = console_driver.table; kd_mksound = sunkbd_kd_mksound; /* XXX Check keyboard-click? property in 'options' PROM node XXX */ if(sparc_cpu_model != sun4) { opt_node = prom_getchild(prom_root_node); opt_node = prom_searchsiblings(opt_node, "options"); i = prom_getintdefault(opt_node, "keyboard-click?", -1); if(i != -1) sunkbd_clickp = 1; else sunkbd_clickp = 0; } else { sunkbd_clickp = 0; } keyboard_tasklet.func = sun_kbd_bh; tasklet_enable(&keyboard_tasklet); tasklet_schedule(&keyboard_tasklet); return 0;}/* /dev/kbd support */#define KBD_QSIZE 32static Firm_event kbd_queue [KBD_QSIZE];static int kbd_head, kbd_tail;static spinlock_t kbd_queue_lock = SPIN_LOCK_UNLOCKED;char kbd_opened;static int kbd_active = 0;static DECLARE_WAIT_QUEUE_HEAD(kbd_wait);static struct fasync_struct *kb_fasync;voidpush_kbd (int scan){ unsigned long flags; int next; if (scan == KBD_IDLE) return; spin_lock_irqsave(&kbd_queue_lock, flags); next = (kbd_head + 1) % KBD_QSIZE; if (next != kbd_tail){ kbd_queue [kbd_head].id = scan & KBD_KEYMASK; kbd_queue [kbd_head].value=scan & KBD_UP ? VKEY_UP : VKEY_DOWN; kbd_queue [kbd_head].time = xtime; kbd_head = next; } spin_unlock_irqrestore(&kbd_queue_lock, flags); kill_fasync (&kb_fasync, SIGIO, POLL_IN); wake_up_interruptible (&kbd_wait);}static ssize_tkbd_read (struct file *f, char *buffer, size_t count, loff_t *ppos){ DECLARE_WAITQUEUE(wait, current); unsigned long flags; char *end, *p; /* Return EWOULDBLOCK, because this is what the X server expects */ if (kbd_head == kbd_tail){ if (f->f_flags & O_NONBLOCK) return -EWOULDBLOCK; add_wait_queue (&kbd_wait, &wait);repeat: set_current_state(TASK_INTERRUPTIBLE); if (kbd_head == kbd_tail && !signal_pending(current)) { schedule(); goto repeat; } current->state = TASK_RUNNING; remove_wait_queue (&kbd_wait, &wait); } /* There is data in the keyboard, fill the user buffer */ end = buffer+count; p = buffer; spin_lock_irqsave(&kbd_queue_lock, flags); for (; p < end && kbd_head != kbd_tail;){ Firm_event this_event = kbd_queue[kbd_tail]; kbd_tail = (kbd_tail + 1) % KBD_QSIZE; spin_unlock_irqrestore(&kbd_queue_lock, flags);#ifdef CONFIG_SPARC32_COMPAT if (current->thread.flags & SPARC_FLAG_32BIT) { if (copy_to_user((Firm_event *)p, &this_event, sizeof(Firm_event)-sizeof(struct timeval))) return -EFAULT; p += sizeof(Firm_event)-sizeof(struct timeval); if (__put_user(this_event.time.tv_sec, (u32 *)p)) return -EFAULT; p += sizeof(u32); if (__put_user(this_event.time.tv_usec, (u32 *)p)) return -EFAULT; p += sizeof(u32); } else#endif { if (copy_to_user((Firm_event *)p, &this_event, sizeof(Firm_event))) return -EFAULT; p += sizeof (Firm_event); }#ifdef KBD_DEBUG printk ("[%s]", this_event.value == VKEY_UP ? "UP" : "DOWN");#endif spin_lock_irqsave(&kbd_queue_lock, flags); } spin_unlock_irqrestore(&kbd_queue_lock, flags); return p-buffer;}/* Needed by X */static int kbd_fasync (int fd, struct file *filp, int on){ int retval; retval = fasync_helper (fd, filp, on, &kb_fasync); if (retval < 0) return retval; return 0;}static unsigned int kbd_poll (struct file *f, poll_table *wait){ poll_wait(f, &kbd_wait, wait); if (kbd_head != kbd_tail) return POLLIN | POLLRDNORM; return 0;}static intkbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg){ unsigned char c; unsigned char leds = 0; int value; switch (cmd){ case KIOCTYPE: /* return keyboard type */ if (put_user(sunkbd_type, (int *) arg)) return -EFAULT; break; case KIOCGTRANS: if (put_user(TR_UNTRANS_EVENT, (int *) arg)) return -EFAULT; break; case KIOCTRANS: if (get_user(value, (int *) arg)) return -EFAULT; if (value != TR_UNTRANS_EVENT) return -EINVAL; break; case KIOCLAYOUT: if (put_user(sunkbd_layout, (int *) arg)) return -EFAULT; break; case KIOCSDIRECT:#ifndef CODING_NEW_DRIVER if (get_user(value, (int *) arg)) return -EFAULT; if(value) kbd_redirected = fg_console + 1; else kbd_redirected = 0; kbd_table [fg_console].kbdmode = kbd_redirected ? VC_RAW : VC_XLATE;#endif break; case KIOCCMD: if (get_user(value, (int *) arg)) return -EFAULT; c = (unsigned char) value; switch (c) { case SKBDCMD_CLICK: case SKBDCMD_NOCLICK: spin_lock_irq(&sunkbd_lock); send_cmd(c); spin_unlock_irq(&sunkbd_lock); return 0; case SKBDCMD_BELLON: kd_mksound(1,0); return 0; case SKBDCMD_BELLOFF: kd_mksound(0,0); return 0; default: return -EINVAL; } case KIOCSLED: if (get_user(c, (unsigned char *) arg)) return -EFAULT; if (c & LED_SCRLCK) leds |= (1 << VC_SCROLLOCK); if (c & LED_NLOCK) leds |= (1 << VC_NUMLOCK); if (c & LED_CLOCK) leds |= (1 << VC_CAPSLOCK); compose_led_on = !!(c & LED_CMPOSE); sun_setledstate(kbd_table + fg_console, leds); break; case KIOCGLED: if (put_user(vcleds_to_sunkbd(getleds()), (unsigned char *) arg)) return -EFAULT; break; case KIOCGRATE: { struct kbd_rate rate; rate.delay = kbd_delay_ticks; if (kbd_rate_ticks) rate.rate = HZ / kbd_rate_ticks; else rate.rate = 0; if (copy_to_user((struct kbd_rate *)arg, &rate, sizeof(struct kbd_rate))) return -EFAULT; return 0; } case KIOCSRATE: { struct kbd_rate rate; if (verify_area(VERIFY_READ, (void *)arg, sizeof(struct kbd_rate))) return -EFAULT; copy_from_user(&rate, (struct kbd_rate *)arg, sizeof(struct kbd_rate)); if (rate.rate > 50) return -EINVAL; if (rate.rate == 0) kbd_rate_ticks = 0; else kbd_rate_ticks = HZ / rate.rate; kbd_delay_ticks = rate.delay; return 0; } case FIONREAD: /* return number of bytes in kbd queue */ { int count; count = kbd_head - kbd_tail; if (put_user((count < 0) ? KBD_QSIZE - count : count, (int *) arg)) return -EFAULT; return 0; } default: printk ("Unknown Keyboard ioctl: %8.8x\n", cmd); return -EINVAL; } return 0;}static intkbd_open (struct inode *i, struct file *f){ kbd_active++; if (kbd_opened) return 0; kbd_opened = fg_console + 1; spin_lock_irq(&kbd_queue_lock); kbd_head = kbd_tail = 0; spin_unlock_irq(&kbd_queue_lock); return 0;}static intkbd_close (struct inode *i, struct file *f){ lock_kernel(); if (!--kbd_active) { if (kbd_redirected) kbd_table [kbd_redirected-1].kbdmode = VC_XLATE; kbd_redirected = 0; kbd_opened = 0; kbd_fasync (-1, f, 0); } unlock_kernel(); return 0;}static struct file_operations kbd_fops ={ read: kbd_read, poll: kbd_poll, ioctl: kbd_ioctl, open: kbd_open, release: kbd_close, fasync: kbd_fasync,};void __init keyboard_zsinit(void (*put_char)(unsigned char)){ int timeout = 0; kbd_put_char = put_char; if (!kbd_put_char) panic("keyboard_zsinit: no put_char parameter"); /* Test out the leds */ sunkbd_type = 255; sunkbd_layout = 0; send_cmd(SKBDCMD_RESET); send_cmd(SKBDCMD_RESET); while((sunkbd_type==255) && timeout++ < 25000) { udelay(100); barrier(); } if(timeout>=25000) { printk("keyboard: not present\n"); return; } if(sunkbd_type != SUNKBD_TYPE4) { printk("Sun TYPE %d keyboard detected ", sunkbd_type); } else { timeout=0; while((sunkbd_layout==0) && timeout++ < 10000) { udelay(100); barrier(); } printk("Sun TYPE %d keyboard detected ", ((sunkbd_layout & SUNKBD_LOUT_TYP5_MASK) ? 5 : 4)); } if(sunkbd_type == SUNKBD_TYPE2) sunkbd_clickp = 0; spin_lock_irq(&sunkbd_lock); if(sunkbd_clickp) { send_cmd(SKBDCMD_CLICK); printk("with keyclick\n"); } else { send_cmd(SKBDCMD_NOCLICK); printk("without keyclick\n"); } /* Dork with led lights, then turn them all off */ send_cmd(SKBDCMD_SETLED); send_cmd(0xf); /* All on */ send_cmd(SKBDCMD_SETLED); send_cmd(0x0); /* All off */ spin_unlock_irq(&sunkbd_lock); /* Register the /dev/kbd interface */ devfs_register (NULL, "kbd", DEVFS_FL_DEFAULT, KBD_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &kbd_fops, NULL); if (devfs_register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){ printk ("Could not register /dev/kbd device\n"); return; } return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -