Драйвер devh-egalax для ЗОСРВ "Нейтрино" редакции 2020

This commit is contained in:
commit 13333b0126
66 changed files with 3196 additions and 0 deletions

2
Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=hardware
include recurse.mk

93
README.md Normal file
View File

@ -0,0 +1,93 @@
## Общая структура USB-стека
```
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ │ │ │
│ Контроллер USB шины ◂────*────▸ USB устройство │
│ │ │ │
└──────────────▴──────────────┘ └─────────────────────────────┘
┌──────────────┴──────────────┐
│ │
│ Драйвер контроллера │
│ USB шины (devu-*hci.so) │
│ │
└──────────────▴──────────────┘
*
┌──────────────┴──────────────┐ ┌───────────────────────────┐
│ │ │ │
│ Менеджер USB-стека (io-usb) ◂─── * ───┤ USB драйвер (devu-*) ◂───▸ /dev/usb***X
│ │ │ │ ▴
└─────────────────────────────┘ ▲ └───────────────────────────┘ │
│ │
│ ┌─────────────▾─────────────┐
Интерфейс libusbdi ───────┘ │ │
│ Клиентское приложение │
│ │
└───────────────────────────┘
```
## Дерево исходных кодов
```
|- devu/
| |- keyboard/ - Исходный код драйвера поддержки USB-клавиатур
| |- mouse/ - Исходный код драйвера USB-мыши
| |- printer/ - Исходный код драйвера класса для USB-принтеров
| |- Makefile - Правила сборки дерева исходников
| `- common.mk - Параметры сборки драйверов
|
`- Makefile - Правила сборки дерева исходников
```
## Сборка драйвера
- Установить и настроить [комплект разработчика](https://help.kpda.ru/help/topic/ru.kpda.doc.dev_tools_ru/html/devkit/devkit.html) для [ЗОСРВ "Нейтрино" редакции 2020](https://help.kpda.ru/help/index.jsp).
- Выполнить команду:
```
make
```
## Запуск драйвера
Общая схема запуска драйвера (не относится к драйверам контроллеров USB шин):
```
devu-* <опции_драйвера> &
```
Примеры:
```
devu-kbd -n /dev/usbkbd0 &
└───┬───┘ └───────┬───────┘
│ │
│ Опции
Драйвер
devu-mouse -n /dev/usbmouse0 -vvvv &
└────┬────┘ └───────────┬───────────┘
│ │
│ Опции
Драйвер
devu-prn -n /dev/usbpar0 -s /dev/io-usb/io-usb &
└───┬───┘ └──────────────────┬──────────────────┘
│ │
│ Опции
Драйвер
```

2
devu/Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=USB
include recurse.mk

2
devu/class/Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=CLASS
include recurse.mk

53
devu/class/common.mk Normal file
View File

@ -0,0 +1,53 @@
#
# (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru
#
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
LIB_VARIANT = $(patsubst so.,so,$(subst $(space),.,so $(filter wcc be le amanda,$(VARIANTS))))
BOARD_VARIANT = $(patsubst amanda,-amanda,$(subst $(space),,$(filter wcc amanda,$(VARIANTS))))
EXTRA_CCDEPS = $(wildcard $(PROJECT_ROOT)/*.h $(SECTION_ROOT)/*.h $(PRODUCT_ROOT)/usbdi/public/sys/*.h $(PRODUCT_ROOT)/usbdi/internals.h )
EXTRA_INCVPATH = $(PRODUCT_ROOT)/devu $(PRODUCT_ROOT)/usbdi/public $(PRODUCT_ROOT)/include $(PRODUCT_ROOT)/usbdi $(PRODUCT_ROOT)/hcd
EXTRA_LIBVPATH += $(PRODUCT_ROOT)/usbdi/$(CPU)/$(LIB_VARIANT)
define PINFO
PINFO DESCRIPTION=
endef
include $(MKFILES_ROOT)/qmacros.mk
TYPE = $(firstword $(filter a o, $(VARIANTS)) o)
USEFILE_o = $(SECTION_ROOT)/devu-$(SECTION).use
USEFILE_a =
USEFILE = $(USEFILE_$(TYPE))
INSTALLDIR_a = lib
INSTALLDIR_o = sbin
INSTALLDIR = $(INSTALLDIR_$(TYPE))
NAME_o = devu-$(SECTION)
EXTRA_SILENT_VARIANTS+=$(VARIANT3)
NAME_a = $(SECTION)
EXTRA_SILENT_VARIANTS+=$(VARIANT3)
NAME = $(NAME_$(TYPE))
EXTRA_SILENT_VARIANTS+=$(VARIANT3)
LIBS_o = usbdi$(BOARD_VARIANT)
LIBS_a =
LIBS = $(LIBS_$(TYPE))
CCFLAGS_o =
CCFLAGS_a = -Dmain=main_$(SECTION)
CCFLAGS += $(CCFLAGS_$(TYPE))
CCFLAGS_e2k += -fkernel
CCFLAGS += $(CCFLAGS_$(CPU))
-include $(SECTION_ROOT)/override.mk
include $(MKFILES_ROOT)/qtargets.mk
-include $(SECTION_ROOT)/pinfo.mk

View File

@ -0,0 +1,2 @@
LIST=CPU
include recurse.mk

View File

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,197 @@
/*
* (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX_BUFF_BYTES 1024
typedef struct buffer buffer_t;
#define BUFF_OVERWRITE 0x01
struct buffer {
int head;
int tail;
int bufsize;
int recsize;
char *ptr;
unsigned mode;
int min;
};
extern buffer_t *buff_create( unsigned mode, unsigned size, unsigned rsize );
extern int buff_destroy( buffer_t *bptr );
extern int buff_append( buffer_t *bptr, char *dptr, int n );
extern int buff_delete( buffer_t *bptr, char *dptr, int n );
extern int buff_flush( buffer_t *bptr );
extern int buff_putc( buffer_t *bptr, char c );
extern int buff_getc( buffer_t *bptr );
extern int buff_waiting( buffer_t *bptr );
int buff_destroy( buffer_t *bptr )
{
free( bptr->ptr );
free( bptr );
return( 0 );
}
buffer_t *buff_create( unsigned mode, unsigned size, unsigned rsize )
{
struct buffer *cbptr;
if( cbptr = calloc( sizeof( *cbptr ), 1 ) ) {
cbptr->recsize = rsize ? rsize : ( rsize = 1 );
cbptr->bufsize = size ? size * rsize : MAX_BUFF_BYTES - ( MAX_BUFF_BYTES % rsize );
if( cbptr->ptr = calloc( cbptr->bufsize, 1 ) ) {
cbptr->mode = mode;
}
else {
free( cbptr );
cbptr = 0;
}
}
return( cbptr );
}
int buff_append( buffer_t *bptr, char *dptr, int n )
{
int t;
pthread_sleepon_lock( );
n *= bptr->recsize;
if( ( t = bptr->tail - bptr->head ) <= 0 ) {
t += bptr->bufsize; // calc free size
}
if( t <= n ) {
if( bptr->mode & BUFF_OVERWRITE ) {
bptr->tail += ( n - t );
if( bptr->tail >= bptr->bufsize ) {
bptr->tail = 0;
}
}
else {
n = t;
}
}
while( n ) {
t = min( n, bptr->bufsize - bptr->head );
if( t ) {
memcpy( bptr->ptr + bptr->head, dptr, t );
bptr->head += t;
if( bptr->head >= bptr->bufsize ) {
bptr->head = 0;
}
dptr += t;
n -= t;
}
}
if( ( t = bptr->head - bptr->tail ) < 0 ) {
t += bptr->bufsize;
}
pthread_sleepon_unlock( );
return( t );
}
int buff_delete( buffer_t *bptr, char *dptr, int n )
{
int t, v = 0;
pthread_sleepon_lock( );
n *= bptr->recsize;
if( ( t = bptr->head - bptr->tail ) < 0 ) {
t += bptr->bufsize;
}
if( n > t ) {
n = t;
}
while( n ) {
t = min( n, max( 0, bptr->bufsize - bptr->tail ) );
if( t ) {
memcpy( dptr, bptr->ptr + bptr->tail, t );
bptr->tail += t;
if( bptr->tail >= bptr->bufsize ) {
bptr->tail = 0;
}
dptr += t;
n -= t;
v += t;
}
else {
n = 0;
}
}
pthread_sleepon_unlock( );
return( v );
}
int buff_flush( buffer_t *bptr )
{
pthread_sleepon_lock( );
bptr->tail = bptr->head;
pthread_sleepon_unlock( );
if( bptr->min < 0 ) {
bptr->min = 1;
}
return( 0 );
}
int buff_putc( buffer_t *bptr, char c )
{
int t;
pthread_sleepon_lock( );
if( bptr->recsize > 1 ) {
pthread_sleepon_unlock( );
return( -1 );
}
if( ( t = bptr->tail - bptr->head ) <= 0 ) {
t += bptr->bufsize; // calc free size
}
if( t <= 1 ) {
if( bptr->mode & BUFF_OVERWRITE ) {
if( ++bptr->tail >= bptr->bufsize ) {
bptr->tail = 0;
}
t++;
}
}
if( t ) {
t--;
bptr->ptr[bptr->head++] = c;
if( bptr->head >= bptr->bufsize ) {
bptr->head = 0;
}
}
pthread_sleepon_unlock( );
return( bptr->bufsize - t );
}
int buff_getc( buffer_t *bptr )
{
int c;
pthread_sleepon_lock( );
if( bptr->recsize > 1 || bptr->head == bptr->tail ) {
pthread_sleepon_unlock( );
return( -1 );
}
c = bptr->ptr[bptr->tail];
if( ++bptr->tail >= bptr->bufsize ) {
bptr->tail = 0;
}
pthread_sleepon_unlock( );
return( c );
}
int buff_waiting( buffer_t *bptr )
{
int t;
// pthread_sleepon_lock( );
if( ( t = bptr->head - bptr->tail ) < 0 ) {
t += bptr->bufsize;
}
// pthread_sleepon_unlock( );
return( t / bptr->recsize );
}

View File

@ -0,0 +1,13 @@
%C Class Driver for USB keyboards (BOOT mode HID).
Syntax:
devu-kbd [options*] &
Options:
-n name Set device name. default "/dev/usbkbd0"
-v Be verbose.
-w sec Wait 'sec' seconds for USB stack (default 60 seconds).
-s stack Name of stack to attach to (default /dev/usb).
Examples:
devu-kbd &

View File

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

806
devu/class/keyboard/keyb.c Normal file
View File

@ -0,0 +1,806 @@
/*
* (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
// Portions Copyright (c) 1998 The NetBSD Foundation, Inc.
// All rights reserved.
//
// This code is derived from software contributed to The NetBSD Foundation
// by Lennart Augustsson (lennart@augustsson.net) at
// Carlstedt Research & Technology.
//
// 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. All advertising materials mentioning features or use of this software
// must display the following acknowledgement:
// This product includes software developed by the NetBSD
// Foundation, Inc. and its contributors.
// 4. Neither the name of The NetBSD Foundation nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
// ``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 FOUNDATION OR CONTRIBUTORS
// 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.
// Module Description: keyboard class driver
#include <keyb.h>
keyb_ctrl_t KeybCtrl;
int keyb_free( keyb_t *keyb )
{
int status;
keyb->status &= ~( KEYBOARD_PRESENT | KEYBOARD_IN_USE );
if( keyb->ep_int ) { // Stop the Interrupt In endpoint
usbd_abort_pipe( keyb->ep_int );
}
pthread_sleepon_lock( );
pthread_sleepon_signal( keyb->rbuf );
pthread_sleepon_unlock( );
if( status = rsrcdbmgr_devno_detach( keyb->attr.rdev, 0 ) ) {
fprintf( stderr, "keyb_free: rsrcddbmgr_devno_detach %d\n", errno );
}
if( status = resmgr_detach( KeybCtrl.dispatch, keyb->resmgr_id, _RESMGR_DETACH_ALL | _RESMGR_DETACH_CLOSE ) ) {
fprintf( stderr, "keyb_free: resmgr_detach %d\n", status );
}
usbd_free_urb( keyb->urb );
usbd_free( keyb->ks_ndata );
usbd_free( keyb->ks_odata );
buff_destroy( keyb->rbuf );
TAILQ_REMOVE( &KeybCtrl.dlist, keyb, dlink );
if( status = usbd_detach( keyb->device ) ) {
fprintf( stderr, "keyb_free: usbd_detach %x\n", status );
}
return( status );
}
void keyb_signal_handler( )
{
siginfo_t info;
sigset_t set;
keyb_t *keyb;
keyb_t *nkeyb;
sigfillset( &set );
sigdelset( &set, SIGTERM );
sigprocmask( SIG_BLOCK, &set, NULL );
sigemptyset( &set );
sigaddset( &set, SIGTERM );
while( SignalWaitinfo( &set, &info ) == -1 )
;
for( keyb = TAILQ_FIRST( &KeybCtrl.dlist ); keyb; keyb = nkeyb ) {
nkeyb = TAILQ_NEXT( keyb, dlink );
keyb_free( keyb );
}
}
int keyb_process_keys( keyb_t *keyb )
{
keyb_report_t *nd;
keyb_report_t *od;
struct timespec tv;
_uint32 now;
int mod, omod;
_uint16 ibuf[MAXKEYS]; // char events
int nkeys, i, j;
int key;
#define ADDKEY(c) ibuf[nkeys++] = (c)
nd = (keyb_report_t *)keyb->ks_ndata;
od = (keyb_report_t *)keyb->ks_odata;
if( nd->keycode[0] == KEY_ERROR ) {
return( EOK ); // ignore
}
clock_gettime( CLOCK_REALTIME, &tv );
now = (_uint32)tv.tv_sec * 1000 + (_uint32)tv.tv_nsec / 1000000;
nkeys = 0;
mod = nd->modifiers;
omod = od->modifiers;
if( mod != omod ) {
for (i = 0; i < NMOD; i++) {
if (( mod & ukbd_mods[i].mask) != (omod & ukbd_mods[i].mask ) ) {
ADDKEY( ukbd_mods[i].key | ( mod & ukbd_mods[i].mask ? PRESS : RELEASE ) );
}
}
}
if( memcmp( nd->keycode, od->keycode, NKEYCODE) != 0 ) {
// Check for released keys
for( i = 0; i < NKEYCODE; i++) {
key = od->keycode[i];
if( key == 0 ) {
continue;
}
for( j = 0; j < NKEYCODE; j++ ) {
if( key == nd->keycode[j] ) {
goto rfound;
}
}
ADDKEY( key | RELEASE );
rfound:
;
}
/* Check for pressed keys. */
for( i = 0; i < NKEYCODE; i++ ) {
key = nd->keycode[i];
if( key == 0 ) {
continue;
}
for( j = 0; j < NKEYCODE; j++ ) {
if( key == od->keycode[j] ) {
goto pfound;
}
}
ADDKEY( key | PRESS );
pfound:
;
}
}
else {
if( keyb->ks_nrep && now > keyb->ks_ntime ) {
// buff_append( keyb->rbuf, keyb->ks_rep, 1 );
buff_append( keyb->rbuf, keyb->ks_rep, keyb->ks_nrep );
keyb->ks_ntime = now + 10;
// wakeup anyone blocked
pthread_sleepon_lock( );
pthread_sleepon_signal( keyb->rbuf );
pthread_sleepon_unlock( );
return( EOK );
}
}
*od = *nd;
if( nkeys == 0 ) {
return( EOK );
}
if( 1 ) {
char cbuf[MAXKEYS * 2];
int c;
int npress;
for( npress = i = j = 0; i < nkeys; i++ ) {
key = ibuf[i];
c = ukbd_trtab[key & CODEMASK];
if( c == NN ) {
continue;
}
// special case control break/pause
if( c == 0x7f ) {
if( ( mod & ( MOD_CONTROL_L | MOD_CONTROL_R ) ) ) {
cbuf[j++] = 0xe0, cbuf[j++] = 0x46;
cbuf[j++] = 0xe0, cbuf[j++] = 0xc6;
break;
}
else {
cbuf[j++] = 0xe1, cbuf[j++] = 0x1d, cbuf[j++] = 0x45;
cbuf[j++] = 0xe1, cbuf[j++] = 0x9d, cbuf[j++] = 0xc5;
break;
}
}
if( c & 0x80 ) {
cbuf[j++] = 0xe0;
}
cbuf[j] = c & 0x7f;
if( key & RELEASE ) {
cbuf[j] |= 0x80;
}
else {
// remember pressed keys for autorepeat
if( c & 0x80 ) {
keyb->ks_rep[npress++] = 0xe0;
}
keyb->ks_rep[npress++] = c & 0x7f;
}
j++;
}
buff_append( keyb->rbuf, cbuf, j );
if( npress != 0 ) {
keyb->ks_nrep = npress;
keyb->ks_ntime = now + 200;
}
else {
keyb->ks_nrep = 0;
}
// wakeup anyone blocked
pthread_sleepon_lock( );
pthread_sleepon_signal( keyb->rbuf );
pthread_sleepon_unlock( );
}
return( EOK );
}
void keyb_int_cbf( struct usbd_urb *urb, struct usbd_pipe *pipe, void *hdl )
{
keyb_t *keyb;
_uint32 ustatus;
_uint32 alen;
keyb = (keyb_t *)hdl;
usbd_urb_status( urb, &ustatus, &alen );
if( ( ustatus & USBD_URB_STATUS_MASK ) != USBD_STATUS_CMP ) {
// check for a stall condition and clear it
if( ( ustatus & USBD_USB_STATUS_MASK ) == USBD_STATUS_STALL ) {
usbd_reset_pipe( pipe );
}
if( KeybCtrl.verbose ) {
fprintf( stderr, "keyb_int_cbf: ustatus %x\n", ustatus );
}
}
else {
keyb_process_keys( keyb );
}
if( keyb->status & KEYBOARD_IN_USE ) { // re-hook interrupt endpoint
usbd_setup_interrupt( keyb->urb, URB_DIR_IN, keyb->ks_ndata, keyb->ep_int_size );
if( usbd_io( keyb->urb, keyb->ep_int, keyb_int_cbf, keyb, USBD_TIME_INFINITY ) ) {
keyb->status &= ~( KEYBOARD_IN_USE | KEYBOARD_PRESENT );
}
}
}
int keyb_set_idle( keyb_t *keyb )
{
_uint32 status;
struct usbd_urb *urb;
_uint8 *buffer;
int retry=3;
status = ENOMEM;
if( !(urb = usbd_alloc_urb( NULL ) ) )
return( status );
if ( (buffer = usbd_alloc( 1 ) ) ) {
while ( retry-- ) {
usbd_setup_vendor( urb, URB_DIR_OUT, 10, USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE, 8 << 8, 0, NULL, 0 );
if( ( status = usbd_io( urb, keyb->ep_cntl, NULL, keyb, USBD_TIME_INFINITY ) ) == EOK ) {
// get idle and make sure it was set.. fix for IBM Sillitek keayboard
usbd_setup_vendor( urb, URB_DIR_IN, 2, USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE, 0, 0, buffer, 1 );
if( ( ( status = usbd_io( urb, keyb->ep_cntl, NULL, keyb, USBD_TIME_INFINITY ) ) == EOK ) && (*buffer != 0) ) {
break;
}
}
}
usbd_free( buffer );
}
usbd_free_urb( urb );
return( status );
}
int keyb_set_report( keyb_t *keyb, _uint32 lchng )
{
_uint32 status;
struct usbd_urb *urb;
_uint8 *buffer;
status = ENOMEM;
if( urb = usbd_alloc_urb( NULL ) ) {
if( buffer = usbd_alloc( 1 ) ) {
*buffer = lchng;
usbd_setup_vendor( urb, URB_DIR_OUT, HID_SET_REPORT, USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE, HID_REPORT_OUT, keyb->instance.iface, buffer, 1 );
if( ( status = usbd_io( urb, keyb->ep_cntl, NULL, keyb, USBD_TIME_INFINITY ) ) == EOK ) {
keyb->leds = *buffer;
}
usbd_free_urb( urb );
}
usbd_free( buffer );
}
return( status );
}
int keyb_io_unblock( resmgr_context_t *ctp, io_pulse_t *msg, RESMGR_OCB_T *ocb )
{
keyb_t *keyb;
keyb = (keyb_t *)ocb->attr;
pthread_sleepon_lock( );
keyb->status |= KEYBOARD_UNBLOCK;
pthread_sleepon_signal( keyb->rbuf );
pthread_sleepon_unlock( );
return( _RESMGR_NOREPLY );
}
int keyb_io_open( resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra )
{
keyb_t *keyb;
int status;
keyb = (keyb_t *)handle;
if( msg->connect.ioflag & _IO_FLAG_RD ) {
msg->connect.sflag = SH_DENYRD;
}
if( msg->connect.ioflag & _IO_FLAG_WR ) {
msg->connect.sflag = SH_DENYWR;
}
if( status = iofunc_open_default( ctp, msg, handle, extra ) ) {
return( status );
}
if( !( keyb->status & KEYBOARD_IN_USE ) ) { // hook callback
usbd_setup_interrupt( keyb->urb, URB_DIR_IN, keyb->ks_ndata, keyb->ep_int_size );
if( status = usbd_io( keyb->urb, keyb->ep_int, keyb_int_cbf, keyb, USBD_TIME_INFINITY ) ) {
return( status );
}
keyb->status |= KEYBOARD_IN_USE;
}
return( EOK );
}
int keyb_io_close( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb )
{
keyb_t *keyb;
keyb = (keyb_t *)ocb->attr;
iofunc_attr_lock( ocb->attr );
if( iofunc_ocb_detach( ctp, ocb ) & IOFUNC_OCB_LAST_READER ) {
keyb->status &= ~KEYBOARD_IN_USE;
}
iofunc_ocb_free( ocb );
iofunc_attr_unlock( &keyb->attr );
return( EOK );
}
int keyb_io_read( resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb )
{
keyb_t *keyb;
buffer_t *bptr;
char kbuf[1];
int pos;
int nbytes;
keyb = (keyb_t *)ocb->attr;
bptr = keyb->rbuf;
pos = 0;
nbytes = msg->i.nbytes;
keyb->status &= ~KEYBOARD_UNBLOCK;
if( !( keyb->status & KEYBOARD_PRESENT ) ) {
return( ENODEV );
}
if( ( msg->i.xtype & _IO_XTYPE_MASK ) != _IO_XTYPE_NONE ) {
return( EINVAL );
}
if( !( ocb->ioflag & _IO_FLAG_RD ) ) {
return( EBADF );
}
ocb->flags |= IOFUNC_ATTR_ATIME;
if( nbytes == 0 ) {
_IO_SET_READ_NBYTES( ctp, 0 );
return( EOK );
}
while( 1 ) {
// Fix: optimization buff_delete more than one packet at a time
while( nbytes && buff_delete( bptr, kbuf, 1 ) > 0 ) {
resmgr_msgwrite( ctp, kbuf, 1, pos );
nbytes--;
pos++;
}
if( !nbytes || pos >= keyb->tios.c_cc[VMIN] ) {
break;
}
pthread_sleepon_lock( );
while( buff_waiting( bptr ) == 0 ) {
if( keyb->status & KEYBOARD_UNBLOCK ) {
pthread_sleepon_unlock( );
return( EINTR );
}
if( !( keyb->status & KEYBOARD_PRESENT ) ) {
pthread_sleepon_unlock( );
return( ENODEV );
}
pthread_sleepon_wait( bptr );
}
pthread_sleepon_unlock( );
}
_IO_SET_READ_NBYTES( ctp, pos );
return( EOK );
}
int keyb_io_devctl( resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb )
{
keyb_t *keyb;
_uint32 *data;
int nbytes;
int status;
nbytes = 0;
data = (_uint32 *)_DEVCTL_DATA( msg->i );
keyb = (keyb_t *)ocb->attr;
if( !( keyb->status & KEYBOARD_PRESENT ) ) {
return( ENODEV );
}
// Let common code handle DCMD_ALL_* cases
if( ( status = iofunc_devctl_default( ctp, msg, ocb ) ) != _RESMGR_DEFAULT ) {
return( status );
}
status = EOK;
switch( msg->i.dcmd ) {
case DCMD_CHR_TCGETATTR:
nbytes = sizeof( struct termios );
memcpy( data, &keyb->tios, nbytes );
break;
case DCMD_CHR_TCSETATTR:
nbytes = sizeof( struct termios );
memcpy( &keyb->tios, data, nbytes );
break;
case DCMD_CHR_TCFLUSH:
buff_flush( keyb->rbuf );
break;
case DCMD_CHR_TTYINFO: {
struct _ttyinfo *ttyinfo;
ttyinfo = (struct _ttyinfo *)data;
strcpy( ttyinfo->ttyname, "kbd" );
ttyinfo->opencount = keyb->attr.count;
nbytes = sizeof( *ttyinfo );
break;
}
case DCMD_CHR_LINESTATUS:
if( keyb->leds & SCROLLLOCK_MAP ) {
*data |= _LINESTATUS_CON_SCROLL;
}
if( keyb->leds & NUMLOCK_MAP ) {
*data |= _LINESTATUS_CON_NUM;
}
if( keyb->leds & CAPSLOCK_MAP ) {
*data |= _LINESTATUS_CON_CAPS;
}
nbytes = sizeof( *data );
break;
case DCMD_CHR_SERCTL: {
_uint32 leds = 0;
if( *data & _CONCTL_NUM ) leds |= NUMLOCK_MAP;
if( *data & _CONCTL_CAPS ) leds |= CAPSLOCK_MAP;
if( *data & _CONCTL_SCROLL ) leds |= SCROLLLOCK_MAP;
keyb_set_report( keyb, leds );
break;
}
default:
status = ENOSYS;
break;
}
msg->o.ret_val = status;
return( _RESMGR_PTR( ctp, &msg->o, sizeof( msg->o ) + nbytes ) );
}
int keyb_io_lock_ocb( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb )
{
return( EOK );
}
int keyb_io_unlock_ocb( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb )
{
return( EOK );
}
int keyb_get_devnum( )
{
int cnt;
int match;
keyb_t *keyb;
for( cnt = 0; cnt < 16; cnt++ ) {
for( match = 0, keyb = TAILQ_FIRST( &KeybCtrl.dlist ); keyb; keyb = TAILQ_NEXT( keyb, dlink ) ) {
if( minor( keyb->devnum ) == cnt ) {
match = 1;
break;
}
}
if( !match ) {
break;
}
}
if( cnt ) {
fprintf( stderr, "get_devnum\n" );
}
return( cnt );
}
int keyb_resmgr_attach( keyb_t *keyb )
{
char name[255];
// setup termios structure
keyb->tios.c_cc[VMIN] = 1;
iofunc_attr_init( &keyb->attr, 0600 | S_IFCHR, 0, 0 );
iofunc_func_init( _RESMGR_CONNECT_NFUNCS, &keyb->resmgr_connect_funcs, _RESMGR_IO_NFUNCS, &keyb->resmgr_io_funcs );
if( ( keyb->attr.rdev = rsrcdbmgr_devno_attach( "usb", -1, 0 ) ) != -1 ) {
keyb->devnum = keyb_get_devnum( );
sprintf( name, "/dev/%s%d", KeybCtrl.prefix, keyb->devnum );
keyb->resmgr_connect_funcs.open = keyb_io_open;
keyb->resmgr_io_funcs.close_ocb = keyb_io_close;
keyb->resmgr_io_funcs.unlock_ocb = keyb_io_unlock_ocb;
keyb->resmgr_io_funcs.lock_ocb = keyb_io_lock_ocb;
keyb->resmgr_io_funcs.read = keyb_io_read;
keyb->resmgr_io_funcs.devctl = keyb_io_devctl;
keyb->resmgr_io_funcs.unblock = keyb_io_unblock;
if( ( keyb->resmgr_id = resmgr_attach( KeybCtrl.dispatch, NULL, name, _FTYPE_ANY, 0,
&keyb->resmgr_connect_funcs, &keyb->resmgr_io_funcs, &keyb->attr ) ) != -1 ) {
resmgr_devino( keyb->resmgr_id, &keyb->io_mount.dev, &keyb->attr.inode );
if( ( keyb->rbuf = buff_create( 0, 64, 1 ) ) ) {
return( EOK );
}
else {
fprintf( stderr, "Keyb: cannot alloc ring buffer\n" );
}
}
else {
fprintf( stderr, "Keyb: resmgr_attach\n" );
}
rsrcdbmgr_devno_detach( keyb->attr.rdev, 0 );
}
else {
fprintf( stderr, "Unable to acquire devno\n" );
}
return( EIO );
}
int keyb_parse_descriptors( keyb_t *keyb )
{
_uint32 eix;
_uint32 scan;
_uint32 found;
usbd_descriptors_t *desc;
struct usbd_desc_node *ifc, *ept;
scan = KEYBOARD_CONTROL_EP | KEYBOARD_INTIN_EP;
found = 0;
if( usbd_interface_descriptor( keyb->device, keyb->instance.config, keyb->instance.iface, keyb->instance.alternate, &ifc ) ) {
for( eix = 0; ( desc = usbd_parse_descriptors( keyb->device, ifc, USB_DESC_ENDPOINT, eix, &ept ) ) != NULL; ++eix ) {
switch( desc->endpoint.bmAttributes ) {
case USB_ATTRIB_CONTROL:
if( usbd_open_pipe( keyb->device, desc, &keyb->ep_cntl ) == EOK ) {
found |= KEYBOARD_CONTROL_EP;
}
break;
case USB_ATTRIB_ISOCHRONOUS:
break;
case USB_ATTRIB_BULK:
break;
case USB_ATTRIB_INTERRUPT:
switch( desc->endpoint.bEndpointAddress & USB_ENDPOINT_IN ) {
case USB_ENDPOINT_OUT:
break;
case USB_ENDPOINT_IN:
if( usbd_open_pipe( keyb->device, desc,
&keyb->ep_int ) == EOK ) {
keyb->ep_int_size = desc->endpoint.wMaxPacketSize;
found |= KEYBOARD_INTIN_EP;
}
break;
}
break;
}
}
}
return( ( found == scan ) ? EOK : ENODEV );
}
void keyb_insertion( struct usbd_connection *connection, usbd_device_instance_t *instance )
{
keyb_t *keyb;
struct usbd_device *device;
int status;
if( ( status = usbd_attach( connection, instance, sizeof( keyb_t ), &device ) ) == EOK ) {
keyb = usbd_device_extra( device );
keyb->device = device;
keyb->instance = *instance;
if( keyb_parse_descriptors( keyb ) == EOK ) {
if( keyb->urb = usbd_alloc_urb( NULL ) ) {
if( ( keyb->ks_ndata = usbd_alloc( keyb->ep_int_size ) ) ) {
if( ( keyb->ks_odata = usbd_alloc( keyb->ep_int_size ) ) ) {
keyb_set_idle( keyb );
if( keyb_resmgr_attach( keyb ) == EOK ) {
keyb->status = KEYBOARD_PRESENT;
TAILQ_INSERT_TAIL( &KeybCtrl.dlist, keyb, dlink );
return;
}
usbd_free( keyb->ks_odata );
}
usbd_free( keyb->ks_ndata );
}
usbd_free_urb( keyb->urb );
}
}
usbd_detach( device );
}
}
void keyb_removal( struct usbd_connection *connection, usbd_device_instance_t *instance )
{
struct usbd_device *device;
keyb_t *keyb;
if( ( device = usbd_device_lookup( connection, instance ) ) != NULL ) {
keyb = usbd_device_extra( device );
keyb_free( keyb );
}
}
int keyb_resmgr_init( )
{
resmgr_attr_t attr;
if( procmgr_daemon( EXIT_SUCCESS, PROCMGR_DAEMON_NOCLOSE | PROCMGR_DAEMON_NODEVNULL ) == -1 ) {
fprintf( stderr, "Keyb: procmgr_daemon\n" );
return( -1 );
}
if( ( KeybCtrl.dispatch = dispatch_create( ) ) == NULL ) {
fprintf( stderr, "Unable to allocate dispatch context\n" );
return( -1 );
}
memset( &KeybCtrl.pool_attr, 0, sizeof( KeybCtrl.pool_attr ) );
if( ( KeybCtrl.pool_attr.attr = malloc( sizeof( *KeybCtrl.pool_attr.attr ) ) ) == NULL ) {
fprintf( stderr, "Unable to alloc pool attr\n" );
return( -1 );
}
pthread_attr_init( KeybCtrl.pool_attr.attr );
pthread_attr_setstacksize( KeybCtrl.pool_attr.attr, 8192 );
KeybCtrl.pool_attr.handle = KeybCtrl.dispatch;
KeybCtrl.pool_attr.context_alloc = resmgr_context_alloc;
KeybCtrl.pool_attr.block_func = resmgr_block;
KeybCtrl.pool_attr.handler_func = (void *)dispatch_handler;
KeybCtrl.pool_attr.context_free = resmgr_context_free;
KeybCtrl.pool_attr.lo_water = 1;
KeybCtrl.pool_attr.hi_water = 2;
KeybCtrl.pool_attr.increment = 1;
KeybCtrl.pool_attr.maximum = 2;
if( ( KeybCtrl.thread_pool = thread_pool_create( &KeybCtrl.pool_attr, 0 ) ) == NULL ) {
fprintf( stderr, "Unable to initialize thread pool\n" );
return( -1 );
}
memset( &attr, 0, sizeof( attr ) );
attr.nparts_max = 10;
attr.msg_max_size = 0;
if( resmgr_attach( KeybCtrl.dispatch, &attr, NULL, 0, 0, NULL, NULL, NULL ) == -1 ) {
fprintf( stderr, "Unable to attach name\n" );
return( -1 );
}
if( thread_pool_start( KeybCtrl.thread_pool ) ) {
fprintf( stderr, "Unable to initialize thread_pool\n" );
return( -1 );
}
return( 0 );
}
int keyb_start_driver( int argc, char *argv[] )
{
struct usbd_connection *connection;
usbd_funcs_t funcs = { _USBDI_NFUNCS, keyb_insertion, keyb_removal, NULL };
usbd_device_ident_t interest = { USBD_CONNECT_WILDCARD, USBD_CONNECT_WILDCARD, USB_CLASS_HID, USB_SUBCLASS_HID_BOOT, USB_PROTOCOL_KEYBOARD };
usbd_connect_parm_t parm = { NULL, USB_VERSION, USBD_VERSION, 0, argc, argv, 0, &interest, &funcs, USBD_CONNECT_WAIT };
int status;
int opt;
sigset_t signals;
sigfillset( &signals ); // No signals to resmgr threads
pthread_sigmask( SIG_BLOCK, &signals, NULL );
TAILQ_INIT( &KeybCtrl.dlist );
KeybCtrl.prefix = "usbkbd";
while( ( opt = getopt( argc, argv, "vn:s:w:" ) ) != -1 ) {
switch( opt ) {
case 'n':
KeybCtrl.prefix = optarg;
break;
case 'v':
KeybCtrl.verbose++;
break;
case 's':
parm.path = optarg;
break;
case 'w':
parm.connect_wait = strtol( optarg, 0, 0 );
break;
default:
break;
}
}
if( ( KeybCtrl.chid = ChannelCreate( _NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK ) ) == -1 ||
( KeybCtrl.coid = ConnectAttach( 0, 0, KeybCtrl.chid, _NTO_SIDE_CHANNEL, 0 ) ) == -1 ) {
fprintf( stderr, "Unable to attach channel and connection\n" );
return( errno );
}
if( keyb_resmgr_init( ) ) {
fprintf( stderr, "Keyb: Resmgr Init Failure\n" );
return( errno );
}
if( ( status = usbd_connect( &parm, &connection ) ) != EOK ) {
fprintf( stderr, "Keyb Class/init - %s\n", strerror( status ) );
return( status );
}
return( status );
}
int main( int argc, char *argv[] )
{
if( keyb_start_driver( argc, argv ) ) {
exit( EXIT_FAILURE );
}
keyb_signal_handler( );
exit( EXIT_SUCCESS );
}

191
devu/class/keyboard/keyb.h Normal file
View File

@ -0,0 +1,191 @@
/*
* (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
// Module Description: header for keyboard class driver
#include <share.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <gulliver.h>
#include <sys/usbdi.h>
#include <sys/iofunc.h>
#include <sys/resmgr.h>
#include <sys/procmgr.h>
#include <sys/dcmd_chr.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>
#include <sys/rsrcdbmgr.h>
// private headers
#include <queue.h>
typedef struct buffer buffer_t;
#define BUFF_OVERWRITE 0x01
struct buffer {
int head;
int tail;
int bufsize;
int recsize;
char *ptr;
unsigned mode;
int min;
};
// KEYBOARD BUFFER REPORT DEFINITION
#define NKEYCODE 6
#define NMOD 8
#define PRESS 0x000
#define RELEASE 0x100
#define CODEMASK 0x0ff
#define KEY_ERROR 0x01
#define MAXKEYS (NMOD+2*NKEYCODE)
#define MOD_CONTROL_L 0x01
#define MOD_CONTROL_R 0x10
#define MOD_SHIFT_L 0x02
#define MOD_SHIFT_R 0x20
#define MOD_ALT_L 0x04
#define MOD_ALT_R 0x40
#define MOD_WIN_L 0x08
#define MOD_WIN_R 0x80
typedef struct _keyb_report {
uint8_t modifiers;
uint8_t reserved;
uint8_t keycode[NKEYCODE];
} keyb_report_t;
typedef struct _keyb {
iofunc_attr_t attr;
TAILQ_ENTRY( _keyb ) dlink;
int resmgr_id;
_uint32 devnum;
resmgr_connect_funcs_t resmgr_connect_funcs;
resmgr_io_funcs_t resmgr_io_funcs;
iofunc_mount_t io_mount;
struct termios tios;
struct usbd_connection *connection;
usbd_device_instance_t instance;
struct usbd_device *device;
_uint32 status;
struct usbd_pipe *ep_cntl;
struct usbd_pipe *ep_int;
uint32_t ep_int_size;
struct usbd_urb *urb;
_uint8 *ep_int_buf;
buffer_t *rbuf; // ring buffer
uint8_t leds;
uint8_t rsvd[3];
keyb_report_t *ks_ndata;
keyb_report_t *ks_odata;
uint32_t ks_ntime;
int ks_nrep;
char ks_rep[MAXKEYS];
} keyb_t;
typedef struct _keyb_ctrl {
TAILQ_HEAD(,_keyb) dlist; // linked list of mice
_uint32 cflags;
_uint32 verbose;
char *prefix;
int coid;
int chid;
thread_pool_t *thread_pool;
dispatch_t *dispatch;
thread_pool_attr_t pool_attr;
} keyb_ctrl_t;
static struct {
int mask, key;
} ukbd_mods[NMOD] = {
{ MOD_CONTROL_L, 0xe0 },
{ MOD_CONTROL_R, 0xe4 },
{ MOD_SHIFT_L, 0xe1 },
{ MOD_SHIFT_R, 0xe5 },
{ MOD_ALT_L, 0xe2 },
{ MOD_ALT_R, 0xe6 },
{ MOD_WIN_L, 0xe3 },
{ MOD_WIN_R, 0xe7 },
};
#define NN 0 // no translation
// Translate USB keycodes to US keyboard AT/XT scancodes.
// Scancodes >= 128 represent EXTENDED keycodes.
static _uint8 ukbd_trtab[256] = {
NN, NN, NN, NN, 30, 48, 46, 32, /* 00 - 07 */
18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
65, 66, 67, 68, 87, 88, 183, 70, /* 40 - 47 */
127, 210, 199, 201, 211, 207, 209, 205, /* 48 - 4F */
203, 208, 200, 69, 181, 55, 74, 78, /* 50 - 57 */
156, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
72, 73, 82, 83, 86, 221, NN, NN, /* 60 - 67 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
NN, NN, NN, NN, NN, NN, NN, NN, /* 70 - 77 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 78 - 7F */
NN, NN, NN, NN, NN, NN, NN, NN, /* 80 - 87 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 88 - 8F */
NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
29, 42, 56, 219, 157, 54, 184,220, /* E0 - E7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
};
// KEYBOARD STATUS DEFINITION
#define KEYBOARD_NOT_IN_USE 0
#define KEYBOARD_IN_USE 1
#define KEYBOARD_IN_SHUTDOWN 2
#define KEYBOARD_UNBLOCK 4
#define KEYBOARD_PRESENT 8
#define KEYBOARD_CONTROL_EP 1
#define KEYBOARD_INTIN_EP 2
// KEYBOARD HID DEFINITION
#define USB_SUBCLASS_HID_BOOT 1
#define USB_PROTOCOL_KEYBOARD 1
// REPORTS EQUIVALENCES
#define HID_SET_REPORT 9
#define HID_REPORT_IN (1 << 8)
#define HID_REPORT_OUT (2 << 8)
#define HID_REPORT_FEATURE (3 << 8)
// KEYBOARD LEDS EQUIVALENCES
#define NUMLOCK_MAP 1
#define CAPSLOCK_MAP 2
#define SCROLLLOCK_MAP 4
extern buffer_t *buff_create( unsigned mode, unsigned size, unsigned rsize );
extern int buff_destroy( buffer_t *bptr );
extern int buff_append( buffer_t *bptr, char *dptr, int n );
extern int buff_delete( buffer_t *bptr, char *dptr, int n );
extern int buff_flush( buffer_t *bptr );
extern int buff_putc( buffer_t *bptr, char c );
extern int buff_getc( buffer_t *bptr );
extern int buff_waiting( buffer_t *bptr );

View File

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,5 @@
#
# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
#
SECTION=kbd

View File

@ -0,0 +1,7 @@
#
# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
#
define PINFO
PINFO DESCRIPTION=Keyboard class driver
endef

View File

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk