2005-04-17 02:20:36 +04:00
/*
* $ Id : usbkbd . c , v 1.27 2001 / 12 / 27 10 : 37 : 41 vojtech Exp $
*
* Copyright ( c ) 1999 - 2001 Vojtech Pavlik
*
* USB HIDBP Keyboard support
*/
/*
* 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
2005-05-29 11:29:01 +04:00
* the Free Software Foundation ; either version 2 of the License , or
2005-04-17 02:20:36 +04:00
* ( at your option ) any later version .
2005-05-29 11:29:01 +04:00
*
2005-04-17 02:20:36 +04:00
* 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 .
2005-05-29 11:29:01 +04:00
*
2005-04-17 02:20:36 +04:00
* 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
2005-05-29 11:29:01 +04:00
*
2005-04-17 02:20:36 +04:00
* Should you need to contact me , the author , you can do so either by
* e - mail - mail your message to < vojtech @ ucw . cz > , or by paper mail :
* Vojtech Pavlik , Simunkova 1594 , Prague 8 , 182 00 Czech Republic
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/input.h>
# include <linux/init.h>
# include <linux/usb.h>
2005-06-30 09:49:08 +04:00
# include <linux/usb_input.h>
2005-04-17 02:20:36 +04:00
/*
* Version Information
*/
# define DRIVER_VERSION ""
# define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
# define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
# define DRIVER_LICENSE "GPL"
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( DRIVER_LICENSE ) ;
static unsigned char usb_kbd_keycode [ 256 ] = {
0 , 0 , 0 , 0 , 30 , 48 , 46 , 32 , 18 , 33 , 34 , 35 , 23 , 36 , 37 , 38 ,
50 , 49 , 24 , 25 , 16 , 19 , 31 , 20 , 22 , 47 , 17 , 45 , 21 , 44 , 2 , 3 ,
4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 28 , 1 , 14 , 15 , 57 , 12 , 13 , 26 ,
27 , 43 , 43 , 39 , 40 , 41 , 51 , 52 , 53 , 58 , 59 , 60 , 61 , 62 , 63 , 64 ,
65 , 66 , 67 , 68 , 87 , 88 , 99 , 70 , 119 , 110 , 102 , 104 , 111 , 107 , 109 , 106 ,
105 , 108 , 103 , 69 , 98 , 55 , 74 , 78 , 96 , 79 , 80 , 81 , 75 , 76 , 77 , 71 ,
72 , 73 , 82 , 83 , 86 , 127 , 116 , 117 , 183 , 184 , 185 , 186 , 187 , 188 , 189 , 190 ,
191 , 192 , 193 , 194 , 134 , 138 , 130 , 132 , 128 , 129 , 131 , 137 , 133 , 135 , 136 , 113 ,
115 , 114 , 0 , 0 , 0 , 121 , 0 , 89 , 93 , 124 , 92 , 94 , 95 , 0 , 0 , 0 ,
122 , 123 , 90 , 91 , 85 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
29 , 42 , 56 , 125 , 97 , 54 , 100 , 126 , 164 , 166 , 165 , 163 , 161 , 115 , 114 , 113 ,
150 , 158 , 159 , 128 , 136 , 177 , 178 , 176 , 142 , 152 , 173 , 140
} ;
struct usb_kbd {
2005-09-15 11:01:47 +04:00
struct input_dev * dev ;
2005-04-17 02:20:36 +04:00
struct usb_device * usbdev ;
unsigned char old [ 8 ] ;
struct urb * irq , * led ;
unsigned char newleds ;
char name [ 128 ] ;
char phys [ 64 ] ;
unsigned char * new ;
struct usb_ctrlrequest * cr ;
unsigned char * leds ;
dma_addr_t cr_dma ;
dma_addr_t new_dma ;
dma_addr_t leds_dma ;
} ;
static void usb_kbd_irq ( struct urb * urb , struct pt_regs * regs )
{
struct usb_kbd * kbd = urb - > context ;
int i ;
switch ( urb - > status ) {
case 0 : /* success */
break ;
case - ECONNRESET : /* unlink */
case - ENOENT :
case - ESHUTDOWN :
return ;
/* -EPIPE: should clear the halt */
default : /* error */
goto resubmit ;
}
2005-09-15 11:01:47 +04:00
input_regs ( kbd - > dev , regs ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 8 ; i + + )
2005-09-15 11:01:47 +04:00
input_report_key ( kbd - > dev , usb_kbd_keycode [ i + 224 ] , ( kbd - > new [ 0 ] > > i ) & 1 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 2 ; i < 8 ; i + + ) {
if ( kbd - > old [ i ] > 3 & & memscan ( kbd - > new + 2 , kbd - > old [ i ] , 6 ) = = kbd - > new + 8 ) {
if ( usb_kbd_keycode [ kbd - > old [ i ] ] )
2005-09-15 11:01:47 +04:00
input_report_key ( kbd - > dev , usb_kbd_keycode [ kbd - > old [ i ] ] , 0 ) ;
2005-04-17 02:20:36 +04:00
else
info ( " Unknown key (scancode %#x) released. " , kbd - > old [ i ] ) ;
}
if ( kbd - > new [ i ] > 3 & & memscan ( kbd - > old + 2 , kbd - > new [ i ] , 6 ) = = kbd - > old + 8 ) {
if ( usb_kbd_keycode [ kbd - > new [ i ] ] )
2005-09-15 11:01:47 +04:00
input_report_key ( kbd - > dev , usb_kbd_keycode [ kbd - > new [ i ] ] , 1 ) ;
2005-04-17 02:20:36 +04:00
else
info ( " Unknown key (scancode %#x) pressed. " , kbd - > new [ i ] ) ;
}
}
2005-09-15 11:01:47 +04:00
input_sync ( kbd - > dev ) ;
2005-04-17 02:20:36 +04:00
memcpy ( kbd - > old , kbd - > new , 8 ) ;
resubmit :
i = usb_submit_urb ( urb , SLAB_ATOMIC ) ;
if ( i )
err ( " can't resubmit intr, %s-%s/input0, status %d " ,
kbd - > usbdev - > bus - > bus_name ,
kbd - > usbdev - > devpath , i ) ;
}
2005-04-23 02:07:00 +04:00
static int usb_kbd_event ( struct input_dev * dev , unsigned int type ,
unsigned int code , int value )
2005-04-17 02:20:36 +04:00
{
struct usb_kbd * kbd = dev - > private ;
if ( type ! = EV_LED )
return - 1 ;
kbd - > newleds = ( ! ! test_bit ( LED_KANA , dev - > led ) < < 3 ) | ( ! ! test_bit ( LED_COMPOSE , dev - > led ) < < 3 ) |
( ! ! test_bit ( LED_SCROLLL , dev - > led ) < < 2 ) | ( ! ! test_bit ( LED_CAPSL , dev - > led ) < < 1 ) |
( ! ! test_bit ( LED_NUML , dev - > led ) ) ;
if ( kbd - > led - > status = = - EINPROGRESS )
return 0 ;
if ( * ( kbd - > leds ) = = kbd - > newleds )
return 0 ;
* ( kbd - > leds ) = kbd - > newleds ;
kbd - > led - > dev = kbd - > usbdev ;
if ( usb_submit_urb ( kbd - > led , GFP_ATOMIC ) )
err ( " usb_submit_urb(leds) failed " ) ;
return 0 ;
}
static void usb_kbd_led ( struct urb * urb , struct pt_regs * regs )
{
struct usb_kbd * kbd = urb - > context ;
if ( urb - > status )
warn ( " led urb status %d received " , urb - > status ) ;
2005-05-29 11:29:01 +04:00
2005-04-17 02:20:36 +04:00
if ( * ( kbd - > leds ) = = kbd - > newleds )
return ;
* ( kbd - > leds ) = kbd - > newleds ;
kbd - > led - > dev = kbd - > usbdev ;
if ( usb_submit_urb ( kbd - > led , GFP_ATOMIC ) )
err ( " usb_submit_urb(leds) failed " ) ;
}
static int usb_kbd_open ( struct input_dev * dev )
{
struct usb_kbd * kbd = dev - > private ;
kbd - > irq - > dev = kbd - > usbdev ;
2005-05-29 11:29:38 +04:00
if ( usb_submit_urb ( kbd - > irq , GFP_KERNEL ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
return 0 ;
}
static void usb_kbd_close ( struct input_dev * dev )
{
struct usb_kbd * kbd = dev - > private ;
2005-05-29 11:29:38 +04:00
usb_kill_urb ( kbd - > irq ) ;
2005-04-17 02:20:36 +04:00
}
static int usb_kbd_alloc_mem ( struct usb_device * dev , struct usb_kbd * kbd )
{
if ( ! ( kbd - > irq = usb_alloc_urb ( 0 , GFP_KERNEL ) ) )
return - 1 ;
if ( ! ( kbd - > led = usb_alloc_urb ( 0 , GFP_KERNEL ) ) )
return - 1 ;
if ( ! ( kbd - > new = usb_buffer_alloc ( dev , 8 , SLAB_ATOMIC , & kbd - > new_dma ) ) )
return - 1 ;
if ( ! ( kbd - > cr = usb_buffer_alloc ( dev , sizeof ( struct usb_ctrlrequest ) , SLAB_ATOMIC , & kbd - > cr_dma ) ) )
return - 1 ;
if ( ! ( kbd - > leds = usb_buffer_alloc ( dev , 1 , SLAB_ATOMIC , & kbd - > leds_dma ) ) )
return - 1 ;
return 0 ;
}
static void usb_kbd_free_mem ( struct usb_device * dev , struct usb_kbd * kbd )
{
if ( kbd - > irq )
usb_free_urb ( kbd - > irq ) ;
if ( kbd - > led )
usb_free_urb ( kbd - > led ) ;
if ( kbd - > new )
usb_buffer_free ( dev , 8 , kbd - > new , kbd - > new_dma ) ;
if ( kbd - > cr )
usb_buffer_free ( dev , sizeof ( struct usb_ctrlrequest ) , kbd - > cr , kbd - > cr_dma ) ;
if ( kbd - > leds )
usb_buffer_free ( dev , 1 , kbd - > leds , kbd - > leds_dma ) ;
}
2005-05-29 11:29:01 +04:00
static int usb_kbd_probe ( struct usb_interface * iface ,
2005-04-17 02:20:36 +04:00
const struct usb_device_id * id )
{
2005-09-15 11:01:47 +04:00
struct usb_device * dev = interface_to_usbdev ( iface ) ;
2005-04-17 02:20:36 +04:00
struct usb_host_interface * interface ;
struct usb_endpoint_descriptor * endpoint ;
struct usb_kbd * kbd ;
2005-09-15 11:01:47 +04:00
struct input_dev * input_dev ;
2005-04-17 02:20:36 +04:00
int i , pipe , maxp ;
interface = iface - > cur_altsetting ;
if ( interface - > desc . bNumEndpoints ! = 1 )
return - ENODEV ;
endpoint = & interface - > endpoint [ 0 ] . desc ;
2005-09-15 11:01:47 +04:00
if ( ! ( endpoint - > bEndpointAddress & USB_DIR_IN ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-09-15 11:01:47 +04:00
if ( ( endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) ! = USB_ENDPOINT_XFER_INT )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
pipe = usb_rcvintpipe ( dev , endpoint - > bEndpointAddress ) ;
maxp = usb_maxpacket ( dev , pipe , usb_pipeout ( pipe ) ) ;
2005-09-15 11:01:47 +04:00
kbd = kzalloc ( sizeof ( struct usb_kbd ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! kbd | | ! input_dev )
goto fail1 ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:47 +04:00
if ( usb_kbd_alloc_mem ( dev , kbd ) )
goto fail2 ;
2005-04-17 02:20:36 +04:00
kbd - > usbdev = dev ;
2005-09-15 11:01:47 +04:00
kbd - > dev = input_dev ;
if ( dev - > manufacturer )
strlcpy ( kbd - > name , dev - > manufacturer , sizeof ( kbd - > name ) ) ;
if ( dev - > product ) {
if ( dev - > manufacturer )
strlcat ( kbd - > name , " " , sizeof ( kbd - > name ) ) ;
strlcat ( kbd - > name , dev - > product , sizeof ( kbd - > name ) ) ;
}
if ( ! strlen ( kbd - > name ) )
snprintf ( kbd - > name , sizeof ( kbd - > name ) ,
" USB HIDBP Keyboard %04x:%04x " ,
le16_to_cpu ( dev - > descriptor . idVendor ) ,
le16_to_cpu ( dev - > descriptor . idProduct ) ) ;
usb_make_path ( dev , kbd - > phys , sizeof ( kbd - > phys ) ) ;
strlcpy ( kbd - > phys , " /input0 " , sizeof ( kbd - > phys ) ) ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:47 +04:00
input_dev - > name = kbd - > name ;
input_dev - > phys = kbd - > phys ;
usb_to_input_id ( dev , & input_dev - > id ) ;
input_dev - > cdev . dev = & iface - > dev ;
input_dev - > private = kbd ;
input_dev - > evbit [ 0 ] = BIT ( EV_KEY ) | BIT ( EV_LED ) | BIT ( EV_REP ) ;
input_dev - > ledbit [ 0 ] = BIT ( LED_NUML ) | BIT ( LED_CAPSL ) | BIT ( LED_SCROLLL ) | BIT ( LED_COMPOSE ) | BIT ( LED_KANA ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 255 ; i + + )
2005-09-15 11:01:47 +04:00
set_bit ( usb_kbd_keycode [ i ] , input_dev - > keybit ) ;
clear_bit ( 0 , input_dev - > keybit ) ;
2005-05-29 11:29:01 +04:00
2005-09-15 11:01:47 +04:00
input_dev - > event = usb_kbd_event ;
input_dev - > open = usb_kbd_open ;
input_dev - > close = usb_kbd_close ;
2005-04-17 02:20:36 +04:00
usb_fill_int_urb ( kbd - > irq , dev , pipe ,
kbd - > new , ( maxp > 8 ? 8 : maxp ) ,
usb_kbd_irq , kbd , endpoint - > bInterval ) ;
kbd - > irq - > transfer_dma = kbd - > new_dma ;
kbd - > irq - > transfer_flags | = URB_NO_TRANSFER_DMA_MAP ;
kbd - > cr - > bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE ;
kbd - > cr - > bRequest = 0x09 ;
kbd - > cr - > wValue = cpu_to_le16 ( 0x200 ) ;
kbd - > cr - > wIndex = cpu_to_le16 ( interface - > desc . bInterfaceNumber ) ;
kbd - > cr - > wLength = cpu_to_le16 ( 1 ) ;
usb_fill_control_urb ( kbd - > led , dev , usb_sndctrlpipe ( dev , 0 ) ,
( void * ) kbd - > cr , kbd - > leds , 1 ,
usb_kbd_led , kbd ) ;
kbd - > led - > setup_dma = kbd - > cr_dma ;
kbd - > led - > transfer_dma = kbd - > leds_dma ;
2005-09-15 11:01:47 +04:00
kbd - > led - > transfer_flags | = ( URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP ) ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:47 +04:00
input_register_device ( kbd - > dev ) ;
2005-04-17 02:20:36 +04:00
usb_set_intfdata ( iface , kbd ) ;
return 0 ;
2005-09-15 11:01:47 +04:00
fail2 : usb_kbd_free_mem ( dev , kbd ) ;
fail1 : input_free_device ( input_dev ) ;
kfree ( kbd ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
static void usb_kbd_disconnect ( struct usb_interface * intf )
{
struct usb_kbd * kbd = usb_get_intfdata ( intf ) ;
2005-05-29 11:29:01 +04:00
2005-04-17 02:20:36 +04:00
usb_set_intfdata ( intf , NULL ) ;
if ( kbd ) {
usb_kill_urb ( kbd - > irq ) ;
2005-09-15 11:01:47 +04:00
input_unregister_device ( kbd - > dev ) ;
2005-04-17 02:20:36 +04:00
usb_kbd_free_mem ( interface_to_usbdev ( intf ) , kbd ) ;
kfree ( kbd ) ;
}
}
static struct usb_device_id usb_kbd_id_table [ ] = {
{ USB_INTERFACE_INFO ( 3 , 1 , 1 ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , usb_kbd_id_table ) ;
static struct usb_driver usb_kbd_driver = {
. name = " usbkbd " ,
. probe = usb_kbd_probe ,
. disconnect = usb_kbd_disconnect ,
. id_table = usb_kbd_id_table ,
} ;
static int __init usb_kbd_init ( void )
{
int result = usb_register ( & usb_kbd_driver ) ;
if ( result = = 0 )
info ( DRIVER_VERSION " : " DRIVER_DESC ) ;
return result ;
}
static void __exit usb_kbd_exit ( void )
{
usb_deregister ( & usb_kbd_driver ) ;
}
module_init ( usb_kbd_init ) ;
module_exit ( usb_kbd_exit ) ;