commit 13333b01260fbaaa5cdc66761a86d70cdc619818 Author: cbdbc Date: Fri Sep 16 15:43:59 2022 +0300 Драйвер devh-egalax для ЗОСРВ "Нейтрино" редакции 2020 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..83c6875 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +LIST=hardware +include recurse.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3f10aa --- /dev/null +++ b/README.md @@ -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 & +└───┬───┘ └──────────────────┬──────────────────┘ + │ │ + │ Опции + Драйвер +``` + diff --git a/devu/Makefile b/devu/Makefile new file mode 100644 index 0000000..3c237c3 --- /dev/null +++ b/devu/Makefile @@ -0,0 +1,2 @@ +LIST=USB +include recurse.mk diff --git a/devu/class/Makefile b/devu/class/Makefile new file mode 100644 index 0000000..e95ccd5 --- /dev/null +++ b/devu/class/Makefile @@ -0,0 +1,2 @@ +LIST=CLASS +include recurse.mk diff --git a/devu/class/common.mk b/devu/class/common.mk new file mode 100644 index 0000000..15c3e17 --- /dev/null +++ b/devu/class/common.mk @@ -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 diff --git a/devu/class/keyboard/Makefile b/devu/class/keyboard/Makefile new file mode 100644 index 0000000..074b0d5 --- /dev/null +++ b/devu/class/keyboard/Makefile @@ -0,0 +1,2 @@ +LIST=CPU +include recurse.mk diff --git a/devu/class/keyboard/arm/Makefile b/devu/class/keyboard/arm/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/keyboard/arm/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/keyboard/arm/le.v7/Makefile b/devu/class/keyboard/arm/le.v7/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/arm/le.v7/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/arm/le/Makefile b/devu/class/keyboard/arm/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/arm/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/buffer.c b/devu/class/keyboard/buffer.c new file mode 100644 index 0000000..3239aca --- /dev/null +++ b/devu/class/keyboard/buffer.c @@ -0,0 +1,197 @@ +/* + * (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru + */ + +#include +#include +#include + +#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 ); +} diff --git a/devu/class/keyboard/devu-kbd.use b/devu/class/keyboard/devu-kbd.use new file mode 100644 index 0000000..7061b72 --- /dev/null +++ b/devu/class/keyboard/devu-kbd.use @@ -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 & diff --git a/devu/class/keyboard/e2k/Makefile b/devu/class/keyboard/e2k/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/keyboard/e2k/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/keyboard/e2k/le/Makefile b/devu/class/keyboard/e2k/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/e2k/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/keyb.c b/devu/class/keyboard/keyb.c new file mode 100644 index 0000000..7ce5db0 --- /dev/null +++ b/devu/class/keyboard/keyb.c @@ -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_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 ); +} diff --git a/devu/class/keyboard/keyb.h b/devu/class/keyboard/keyb.h new file mode 100644 index 0000000..74acadd --- /dev/null +++ b/devu/class/keyboard/keyb.h @@ -0,0 +1,191 @@ +/* + * (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru + */ + +// Module Description: header for keyboard class driver + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// private headers +#include + +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 ); diff --git a/devu/class/keyboard/mips/Makefile b/devu/class/keyboard/mips/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/keyboard/mips/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/keyboard/mips/be/Makefile b/devu/class/keyboard/mips/be/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/mips/be/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/mips/le.mc/Makefile b/devu/class/keyboard/mips/le.mc/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/mips/le.mc/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/mips/le/Makefile b/devu/class/keyboard/mips/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/mips/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/override.mk b/devu/class/keyboard/override.mk new file mode 100644 index 0000000..13f8f20 --- /dev/null +++ b/devu/class/keyboard/override.mk @@ -0,0 +1,5 @@ +# +# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru +# + +SECTION=kbd diff --git a/devu/class/keyboard/pinfo.mk b/devu/class/keyboard/pinfo.mk new file mode 100644 index 0000000..8a33736 --- /dev/null +++ b/devu/class/keyboard/pinfo.mk @@ -0,0 +1,7 @@ +# +# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru +# + +define PINFO +PINFO DESCRIPTION=Keyboard class driver +endef diff --git a/devu/class/keyboard/ppc/Makefile b/devu/class/keyboard/ppc/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/keyboard/ppc/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/keyboard/ppc/be.spe/Makefile b/devu/class/keyboard/ppc/be.spe/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/ppc/be.spe/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/ppc/be/Makefile b/devu/class/keyboard/ppc/be/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/ppc/be/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/keyboard/x86/Makefile b/devu/class/keyboard/x86/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/keyboard/x86/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/keyboard/x86/o/Makefile b/devu/class/keyboard/x86/o/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/keyboard/x86/o/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/Makefile b/devu/class/mouse/Makefile new file mode 100644 index 0000000..074b0d5 --- /dev/null +++ b/devu/class/mouse/Makefile @@ -0,0 +1,2 @@ +LIST=CPU +include recurse.mk diff --git a/devu/class/mouse/arm/Makefile b/devu/class/mouse/arm/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/mouse/arm/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/mouse/arm/le.v7/Makefile b/devu/class/mouse/arm/le.v7/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/arm/le.v7/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/arm/le/Makefile b/devu/class/mouse/arm/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/arm/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/buffer.c b/devu/class/mouse/buffer.c new file mode 100644 index 0000000..3239aca --- /dev/null +++ b/devu/class/mouse/buffer.c @@ -0,0 +1,197 @@ +/* + * (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru + */ + +#include +#include +#include + +#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 ); +} diff --git a/devu/class/mouse/devu-mouse.use b/devu/class/mouse/devu-mouse.use new file mode 100644 index 0000000..e152741 --- /dev/null +++ b/devu/class/mouse/devu-mouse.use @@ -0,0 +1,13 @@ +%C Class Driver for USB mice (BOOT mode HID). + +Syntax: +devu-mouse [options*] & + +Options: + -n name Set device name. default "/dev/usbmouse0" + -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-mouse & diff --git a/devu/class/mouse/e2k/Makefile b/devu/class/mouse/e2k/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/mouse/e2k/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/mouse/e2k/le/Makefile b/devu/class/mouse/e2k/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/e2k/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/mips/Makefile b/devu/class/mouse/mips/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/mouse/mips/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/mouse/mips/be/Makefile b/devu/class/mouse/mips/be/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/mips/be/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/mips/le.mc/Makefile b/devu/class/mouse/mips/le.mc/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/mips/le.mc/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/mips/le/Makefile b/devu/class/mouse/mips/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/mips/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/mouse.c b/devu/class/mouse/mouse.c new file mode 100644 index 0000000..effaaf3 --- /dev/null +++ b/devu/class/mouse/mouse.c @@ -0,0 +1,574 @@ +/* + * (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru + */ + +// Module Description: mouse class driver + +#include + +mouse_ctrl_t MouseCtrl; + +int mouse_free( mouse_t *mouse ) +{ + int status; + + mouse->status &= ~( MOUSE_PRESENT | MOUSE_ON ); + + if( mouse->ep_int ) { // Stop the Interrupt In endpoint + usbd_abort_pipe( mouse->ep_int ); + } + + pthread_sleepon_lock( ); + pthread_sleepon_signal( mouse->rbuf ); + pthread_sleepon_unlock( ); + + if( status = rsrcdbmgr_devno_detach( mouse->attr.rdev, 0 ) ) { + fprintf( stderr, "mouse_free: rsrcddbmgr_devno_detach %d\n", errno ); + } + if( status = resmgr_detach( MouseCtrl.dispatch, mouse->resmgr_id, _RESMGR_DETACH_ALL | _RESMGR_DETACH_CLOSE ) ) { + fprintf( stderr, "mouse_free: resmgr_detach %d\n", status ); + } + usbd_free_urb( mouse->urb ); + usbd_free( mouse->ep_int_buf ); + buff_destroy( mouse->rbuf ); + TAILQ_REMOVE( &MouseCtrl.dlist, mouse, dlink ); + if( status = usbd_detach( mouse->device ) ) { + fprintf( stderr, "mouse_free: usbd_detach %x\n", status ); + } + return( status ); +} + +void mouse_signal_handler( ) +{ + siginfo_t info; + sigset_t set; + mouse_t *mouse; + mouse_t *nmouse; + + sigfillset( &set ); + sigdelset( &set, SIGTERM ); + sigprocmask( SIG_BLOCK, &set, NULL ); + + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + while( SignalWaitinfo( &set, &info ) == -1 ) + ; + + for( mouse = TAILQ_FIRST( &MouseCtrl.dlist ); mouse; mouse = nmouse ) { + nmouse = TAILQ_NEXT( mouse, dlink ); + mouse_free( mouse ); + } +} + +int mouse_process( mouse_t *mouse ) +{ + uint8_t mp[3]; + uint8_t *p; + + // convert report packet to microsoft packet + p = mouse->ep_int_buf; + mp[0] = 0x40; + mp[0] |= (*(p+1) >> 6) & 0x03; + mp[0] |= (*(p+2) >> 4) & 0x0C; + mp[0] |= (*(p+0) & 0x01) ? 0x20: 0x00; // left mouse button + mp[0] |= (*(p+0) & 0x02) ? 0x10: 0x00; // right mouse button + mp[1] = *(p+1) & 0x3F; + mp[2] = *(p+2) & 0x3F; +// mp[3] = (*(p+0) & 0x04) ? 0x10: 0x00; // middle mouse button + + buff_append( mouse->rbuf, (char *)mp, 1 ); + + pthread_sleepon_lock( ); // wakeup anyone blocked + pthread_sleepon_signal( mouse->rbuf ); + pthread_sleepon_unlock( ); + + return( EOK ); +} + +void mouse_int_cbf( struct usbd_urb *urb, struct usbd_pipe *pipe, void *hdl ) +{ + mouse_t *mouse; + _uint32 ustatus; + _uint32 alen; + + mouse = (mouse_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( MouseCtrl.verbose ) { + fprintf( stderr, "mouse_int_cbf: ustatus %x\n", ustatus ); + } + } + else { + mouse_process( mouse ); + } + + if( mouse->status & MOUSE_ON ) { // re-hook interrupt endpoint + usbd_setup_interrupt( mouse->urb, URB_DIR_IN|URB_SHORT_XFER_OK, mouse->ep_int_buf, mouse->ep_int_size ); + if( usbd_io( mouse->urb, mouse->ep_int, mouse_int_cbf, mouse, USBD_TIME_INFINITY ) ) { + pthread_sleepon_lock( ); + mouse->status &= ~( MOUSE_ON | MOUSE_PRESENT ); + pthread_sleepon_signal( mouse->rbuf ); + pthread_sleepon_unlock( ); + } + } +} + +int mouse_io_unblock( resmgr_context_t *ctp, io_pulse_t *msg, RESMGR_OCB_T *ocb ) +{ + mouse_t *mouse; + + mouse = (mouse_t *)ocb->attr; + + pthread_sleepon_lock( ); + mouse->status |= MOUSE_UNBLOCK; + pthread_sleepon_signal( mouse->rbuf ); + pthread_sleepon_unlock( ); + + return( _RESMGR_NOREPLY ); +} + +int mouse_io_open( resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra ) +{ + mouse_t *mouse; + int status; + + mouse = (mouse_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( !( mouse->status & MOUSE_ON ) ) { // hook callback + usbd_setup_interrupt( mouse->urb, URB_DIR_IN|URB_SHORT_XFER_OK, mouse->ep_int_buf, mouse->ep_int_size ); + if( status = usbd_io( mouse->urb, mouse->ep_int, mouse_int_cbf, mouse, USBD_TIME_INFINITY ) ) { + return( status ); + } + mouse->status |= MOUSE_ON; + } + return( EOK ); +} + +int mouse_io_close( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb ) +{ + mouse_t *mouse; + + mouse = (mouse_t *)ocb->attr; + + iofunc_attr_lock( ocb->attr ); + if( iofunc_ocb_detach( ctp, ocb ) & IOFUNC_OCB_LAST_READER ) { + mouse->status &= ~MOUSE_ON; + } + iofunc_ocb_free( ocb ); + iofunc_attr_unlock( &mouse->attr ); + return( EOK ); +} + +int mouse_io_read( resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb ) +{ + mouse_t *mouse; + buffer_t *bptr; + char mbuf[3]; + int pos; + int nbytes; + + mouse = (mouse_t *)ocb->attr; + bptr = mouse->rbuf; + pos = 0; + nbytes = msg->i.nbytes; + mouse->status &= ~MOUSE_UNBLOCK; + + if( !( mouse->status & MOUSE_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 >= 3 && buff_delete( bptr, mbuf, 1 ) > 0 ) { + resmgr_msgwrite( ctp, mbuf, 3, pos ); + nbytes -=3; + pos += 3; + } + + if( !nbytes || pos >= mouse->tios.c_cc[VMIN] || nbytes < 3 ) { + break; + } + pthread_sleepon_lock( ); + while( buff_waiting( bptr ) == 0 ) { + if( mouse->status & MOUSE_UNBLOCK ) { + pthread_sleepon_unlock( ); + return( EINTR ); + } + if( !( mouse->status & MOUSE_PRESENT ) ) { + pthread_sleepon_unlock( ); + return( ENODEV ); + } + pthread_sleepon_wait( bptr ); + } + pthread_sleepon_unlock( ); + } + + _IO_SET_READ_NBYTES( ctp, pos ); + return( EOK ); +} + +int mouse_io_devctl( resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb ) +{ + mouse_t *mouse; + uint32_t *data; + int nbytes; + int status; + + nbytes = 0; + data = (uint32_t *)_DEVCTL_DATA( msg->i ); + mouse = (mouse_t *)ocb->attr; + + if( !( mouse->status & MOUSE_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, &mouse->tios, nbytes ); + break; + + case DCMD_CHR_TCSETATTR: + nbytes = sizeof( struct termios ); + memcpy( &mouse->tios, data, nbytes ); + break; + + case DCMD_CHR_TCFLUSH: + buff_flush( mouse->rbuf ); + break; + + case DCMD_CHR_TTYINFO: { + struct _ttyinfo *ttyinfo; + + ttyinfo = (struct _ttyinfo *)data; + strcpy( ttyinfo->ttyname, "ser" ); + ttyinfo->opencount = mouse->attr.count; + nbytes = sizeof( *ttyinfo ); + break; + } + + case DCMD_CHR_LINESTATUS: + case DCMD_CHR_SERCTL: + default: + status = ENOSYS; + break; + } + + msg->o.ret_val = status; + return( _RESMGR_PTR( ctp, &msg->o, sizeof( msg->o ) + nbytes ) ); +} + +int mouse_get_devnum( ) +{ + int cnt; + int match; + mouse_t *mouse; + + for( cnt = 0; cnt < 16; cnt++ ) { + for( match = 0, mouse = TAILQ_FIRST( &MouseCtrl.dlist ); mouse; mouse = TAILQ_NEXT( mouse, dlink ) ) { + if( minor( mouse->devnum ) == cnt ) { + match = 1; + break; + } + } + if( !match ) { + break; + } + } + return( cnt ); +} + +int mouse_io_lock_ocb( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb ) +{ + return( EOK ); +} + +int mouse_io_unlock_ocb( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb ) +{ + return( EOK ); +} + +int mouse_resmgr_attach( mouse_t *mouse ) +{ + char name[255]; + + // setup termios structure + mouse->tios.c_cc[VMIN] = 3; + iofunc_attr_init( &mouse->attr, 0600 | S_IFCHR, 0, 0 ); + iofunc_func_init( _RESMGR_CONNECT_NFUNCS, &mouse->resmgr_connect_funcs, _RESMGR_IO_NFUNCS, &mouse->resmgr_io_funcs ); + if( ( mouse->attr.rdev = rsrcdbmgr_devno_attach( "usb", -1, 0 ) ) != -1 ) { + mouse->devnum = mouse_get_devnum( ); + sprintf( name, "/dev/%s%d", MouseCtrl.prefix, mouse->devnum ); + mouse->resmgr_connect_funcs.open = mouse_io_open; + mouse->resmgr_io_funcs.close_ocb = mouse_io_close; + mouse->resmgr_io_funcs.unlock_ocb = mouse_io_unlock_ocb; + mouse->resmgr_io_funcs.lock_ocb = mouse_io_lock_ocb; + mouse->resmgr_io_funcs.read = mouse_io_read; + mouse->resmgr_io_funcs.devctl = mouse_io_devctl; + mouse->resmgr_io_funcs.unblock = mouse_io_unblock; + if( ( mouse->resmgr_id = resmgr_attach( MouseCtrl.dispatch, NULL, name, _FTYPE_ANY, 0, + &mouse->resmgr_connect_funcs, &mouse->resmgr_io_funcs, &mouse->attr ) ) != -1 ) { + resmgr_devino( mouse->resmgr_id, &mouse->io_mount.dev, &mouse->attr.inode ); + if( ( mouse->rbuf = buff_create( BUFF_OVERWRITE, 3 * 64, 3 ) ) ) { + return( EOK ); + } + else { + fprintf( stderr, "Mouse: cannot alloc ring buffer\n" ); + } + } + else { + fprintf( stderr, "Mouse: resmgr_attach\n" ); + } + rsrcdbmgr_devno_detach( mouse->attr.rdev, 0 ); + } + else { + fprintf( stderr, "Unable to acquire devno\n" ); + } + + return( EIO ); +} + +int mouse_parse_descriptors( mouse_t *mouse ) +{ + _uint32 eix; + _uint32 scan; + _uint32 found; + usbd_descriptors_t *desc; + struct usbd_desc_node *ifc, *ept; + + scan = MOUSE_CONTROL_EP | MOUSE_INTIN_EP; + found = 0; + + if( usbd_interface_descriptor( mouse->device, mouse->instance.config, mouse->instance.iface, mouse->instance.alternate, &ifc ) ) { + for( eix = 0; ( desc = usbd_parse_descriptors( mouse->device, ifc, USB_DESC_ENDPOINT, eix, &ept ) ) != NULL; ++eix ) { + switch( desc->endpoint.bmAttributes ) { + case USB_ATTRIB_CONTROL: + if( usbd_open_pipe( mouse->device, desc, &mouse->ep_cntl ) == EOK ) { + found |= MOUSE_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( mouse->device, desc, + &mouse->ep_int ) == EOK ) { + mouse->ep_int_size = desc->endpoint.wMaxPacketSize; + found |= MOUSE_INTIN_EP; + } + break; + } + break; + } + } + } + return( ( found == scan ) ? EOK : ENODEV ); +} + +void mouse_insertion( struct usbd_connection *connection, usbd_device_instance_t *instance ) +{ + mouse_t *mouse; + struct usbd_device *device; + int status; + + if( ( status = usbd_attach( connection, instance, sizeof( mouse_t ), &device ) ) == EOK ) { + mouse = usbd_device_extra( device ); + mouse->device = device; + mouse->instance = *instance; + if( mouse_parse_descriptors( mouse ) == EOK ) { + if( mouse->ep_int_buf = usbd_alloc( mouse->ep_int_size ) ) { + if( mouse->urb = usbd_alloc_urb( NULL ) ) { + if( mouse_resmgr_attach( mouse ) == EOK ) { + mouse->status = MOUSE_PRESENT; + TAILQ_INSERT_TAIL( &MouseCtrl.dlist, mouse, dlink ); + return; + } + usbd_free_urb( mouse->urb ); + } + usbd_free( mouse->ep_int_buf ); + } + } + usbd_detach( device ); + } +} + +void mouse_removal( struct usbd_connection *connection, usbd_device_instance_t *instance ) +{ + struct usbd_device *device; + mouse_t *mouse; + + if( ( device = usbd_device_lookup( connection, instance ) ) != NULL ) { + mouse = usbd_device_extra( device ); + mouse_free( mouse ); + } +} + +int mouse_resmgr_init( ) +{ + resmgr_attr_t attr; + + if( procmgr_daemon( EXIT_SUCCESS, PROCMGR_DAEMON_NOCLOSE | PROCMGR_DAEMON_NODEVNULL ) == -1 ) { + fprintf( stderr, "Mouse: procmgr_daemon\n" ); + return( -1 ); + } + + if( ( MouseCtrl.dispatch = dispatch_create( ) ) == NULL ) { + fprintf( stderr, "Unable to allocate dispatch context\n" ); + return( -1 ); + } + + memset( &MouseCtrl.pool_attr, 0, sizeof( MouseCtrl.pool_attr ) ); + if( ( MouseCtrl.pool_attr.attr = malloc( sizeof( *MouseCtrl.pool_attr.attr ) ) ) == NULL ) { + fprintf( stderr, "Unable to alloc pool attr\n" ); + return( -1 ); + } + + pthread_attr_init( MouseCtrl.pool_attr.attr ); + pthread_attr_setstacksize( MouseCtrl.pool_attr.attr, 8192 ); + + MouseCtrl.pool_attr.handle = MouseCtrl.dispatch; + MouseCtrl.pool_attr.context_alloc = resmgr_context_alloc; + MouseCtrl.pool_attr.block_func = resmgr_block; + MouseCtrl.pool_attr.handler_func = (void *)dispatch_handler; + MouseCtrl.pool_attr.context_free = resmgr_context_free; + MouseCtrl.pool_attr.lo_water = 1; + MouseCtrl.pool_attr.hi_water = 2; + MouseCtrl.pool_attr.increment = 1; + MouseCtrl.pool_attr.maximum = 2; + + if( ( MouseCtrl.thread_pool = thread_pool_create( &MouseCtrl.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( MouseCtrl.dispatch, &attr, NULL, 0, 0, NULL, NULL, NULL ) == -1 ) { + fprintf( stderr, "Unable to attach name\n" ); + return( -1 ); + } + + if( thread_pool_start( MouseCtrl.thread_pool ) ) { + fprintf( stderr, "Unable to initialize thread_pool\n" ); + return( -1 ); + } + + return( 0 ); +} + +int mouse_start_driver( int argc, char *argv[] ) +{ + struct usbd_connection *connection; + usbd_funcs_t funcs = { _USBDI_NFUNCS, mouse_insertion, mouse_removal, NULL }; + usbd_device_ident_t interest = { USBD_CONNECT_WILDCARD, USBD_CONNECT_WILDCARD, USB_CLASS_HID, USB_SUBCLASS_HID_BOOT, USB_PROTOCOL_MOUSE }; + 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( &MouseCtrl.dlist ); + MouseCtrl.prefix = "usbmouse"; + + while( ( opt = getopt( argc, argv, "vn:s:w:" ) ) != -1 ) { + switch( opt ) { + case 'n': + MouseCtrl.prefix = optarg; + break; + + case 'v': + MouseCtrl.verbose++; + break; + + case 's': + parm.path = optarg; + break; + + case 'w': + parm.connect_wait = strtol( optarg, 0, 0 ); + break; + + default: + break; + } + } + + if( ( MouseCtrl.chid = ChannelCreate( _NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK ) ) == -1 || + ( MouseCtrl.coid = ConnectAttach( 0, 0, MouseCtrl.chid, _NTO_SIDE_CHANNEL, 0 ) ) == -1 ) { + fprintf( stderr, "Unable to attach channel and connection\n" ); + return( errno ); + } + + if( mouse_resmgr_init( ) ) { + fprintf( stderr, "Mouse: Resmgr Init Failure\n" ); + return( errno ); + } + + if( ( status = usbd_connect( &parm, &connection ) ) != EOK ) { + fprintf( stderr, "Mouse Class/init - %s\n", strerror( status ) ); + return( status ); + } + return( status ); +} + +int main( int argc, char *argv[] ) +{ + if( mouse_start_driver( argc, argv ) ) { + exit( EXIT_FAILURE ); + } + mouse_signal_handler( ); + exit( EXIT_SUCCESS ); +} diff --git a/devu/class/mouse/mouse.h b/devu/class/mouse/mouse.h new file mode 100644 index 0000000..c1b990a --- /dev/null +++ b/devu/class/mouse/mouse.h @@ -0,0 +1,117 @@ +/* + * (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru + */ + +// Module Description: header for mouse class driver + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// private headers +#include + +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; +}; + +// MOUSE CLASS DRIVER STRUCTURE +typedef struct _mouse { + iofunc_attr_t attr; + TAILQ_ENTRY( _mouse ) 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 +} mouse_t; + +typedef struct _mouse_ctrl { + TAILQ_HEAD(,_mouse) 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; +} mouse_ctrl_t; + +// MOUSE HID DEFINITION +#define USB_SUBCLASS_HID_BOOT 1 +#define USB_PROTOCOL_MOUSE 2 + +// MOUSE BUFFER REPORT DEFINITION +typedef struct _mouse_report { + _uint8 buttons; + _uint8 horizontal_position; + _uint8 vertical_position; +} mouse_report_t; + +// MOUSE DEFINITION +#define MOUSE_OFF 0 +#define MOUSE_ON 1 +#define MOUSE_UNBLOCK 2 +#define MOUSE_PRESENT 4 + +#define MOUSE_CONTROL_EP 1 +#define MOUSE_INTIN_EP 2 + +#define MOUSE_BUTTON_1 1 +#define MOUSE_BUTTON_2 2 +#define MOUSE_BUTTON_3 4 +#define MOUSE_BUTTON_LAST_1 8 +#define MOUSE_BUTTON_LAST_2 0x10 +#define MOUSE_BUTTON_LAST_3 0x20 +#define MOUSE_INCREMENTS 5 + +#define MOUSE_MOVE 1 +#define MOUSE_LEFT_PRESSED 2 +#define MOUSE_LEFT_RELEASED 4 +#define MOUSE_RIGHT_PRESSED 8 +#define MOUSE_RIGHT_RELEASED 0x10 +#define MOUSE_MIDDLE_PRESSED 0x20 +#define MOUSE_MIDDLE_RELEASED 0x40 + +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 ); diff --git a/devu/class/mouse/pinfo.mk b/devu/class/mouse/pinfo.mk new file mode 100644 index 0000000..a4323a6 --- /dev/null +++ b/devu/class/mouse/pinfo.mk @@ -0,0 +1,7 @@ +# +# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru +# + +define PINFO +PINFO DESCRIPTION=Mouse class driver +endef diff --git a/devu/class/mouse/ppc/Makefile b/devu/class/mouse/ppc/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/mouse/ppc/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/mouse/ppc/be.spe/Makefile b/devu/class/mouse/ppc/be.spe/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/ppc/be.spe/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/ppc/be/Makefile b/devu/class/mouse/ppc/be/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/ppc/be/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/mouse/x86/Makefile b/devu/class/mouse/x86/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/mouse/x86/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/mouse/x86/o/Makefile b/devu/class/mouse/x86/o/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/mouse/x86/o/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/Makefile b/devu/class/printer/Makefile new file mode 100644 index 0000000..074b0d5 --- /dev/null +++ b/devu/class/printer/Makefile @@ -0,0 +1,2 @@ +LIST=CPU +include recurse.mk diff --git a/devu/class/printer/arm/Makefile b/devu/class/printer/arm/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/printer/arm/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/printer/arm/le.v7/Makefile b/devu/class/printer/arm/le.v7/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/arm/le.v7/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/arm/le/Makefile b/devu/class/printer/arm/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/arm/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/devu-prn.use b/devu/class/printer/devu-prn.use new file mode 100644 index 0000000..d0b70e0 --- /dev/null +++ b/devu/class/printer/devu-prn.use @@ -0,0 +1,13 @@ +%C Class Driver for USB printers. + +Syntax: +devu-prn [options*] & + +Options: + -n name Set device name. default "/dev/usbpar0" + -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-prn & diff --git a/devu/class/printer/e2k/Makefile b/devu/class/printer/e2k/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/printer/e2k/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/printer/e2k/le/Makefile b/devu/class/printer/e2k/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/e2k/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/mips/Makefile b/devu/class/printer/mips/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/printer/mips/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/printer/mips/be/Makefile b/devu/class/printer/mips/be/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/mips/be/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/mips/le.mc/Makefile b/devu/class/printer/mips/le.mc/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/mips/le.mc/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/mips/le/Makefile b/devu/class/printer/mips/le/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/mips/le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/override.mk b/devu/class/printer/override.mk new file mode 100644 index 0000000..5f02e2e --- /dev/null +++ b/devu/class/printer/override.mk @@ -0,0 +1,5 @@ +# +# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru +# + +SECTION=prn diff --git a/devu/class/printer/pinfo.mk b/devu/class/printer/pinfo.mk new file mode 100644 index 0000000..7deee30 --- /dev/null +++ b/devu/class/printer/pinfo.mk @@ -0,0 +1,7 @@ +# +# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru +# + +define PINFO +PINFO DESCRIPTION=Printer class driver +endef diff --git a/devu/class/printer/ppc/Makefile b/devu/class/printer/ppc/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/printer/ppc/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/printer/ppc/be.spe/Makefile b/devu/class/printer/ppc/be.spe/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/ppc/be.spe/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/ppc/be/Makefile b/devu/class/printer/ppc/be/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/ppc/be/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/devu/class/printer/printer.c b/devu/class/printer/printer.c new file mode 100644 index 0000000..71a33d0 --- /dev/null +++ b/devu/class/printer/printer.c @@ -0,0 +1,736 @@ +/* + * (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru + */ + +// Module Description: printer class driver + +#include + +prn_ctrl_t PrnCtrl; + +int prn_free( prn_t *prn ) +{ + int status; + + if( prn->ep_out ) { // Stop the Bulk Out endpoint + usbd_abort_pipe( prn->ep_out ); + } + + if( prn->ep_in ) { // Stop the Bulk In endpoint + usbd_abort_pipe( prn->ep_in ); + } + + pthread_sleepon_lock( ); + prn->status &= ~PRN_PRESENT; + pthread_sleepon_signal( &prn->urb ); + pthread_sleepon_unlock( ); + + if( status = rsrcdbmgr_devno_detach( prn->attr.rdev, 0 ) ) { + fprintf( stderr, "prn_free: rsrcddbmgr_devno_detach %d\n", errno ); + } + if( status = resmgr_detach( PrnCtrl.dispatch, prn->resmgr_id, _RESMGR_DETACH_ALL | _RESMGR_DETACH_CLOSE ) ) { + fprintf( stderr, "prn_free: resmgr_detach %d\n", status ); + } + usbd_free( prn->buffer ); + usbd_free_urb( prn->urb ); + TAILQ_REMOVE( &PrnCtrl.dlist, prn, dlink ); + if( status = usbd_detach( prn->device ) ) { + fprintf( stderr, "prn_free: pending I/O\n" ); + } + return( status ); +} + +void prn_signal_handler( ) +{ + siginfo_t info; + sigset_t set; + prn_t *prn; + prn_t *nprn; + + sigfillset( &set ); + sigdelset( &set, SIGTERM ); + sigprocmask( SIG_BLOCK, &set, NULL ); + + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + while( SignalWaitinfo( &set, &info ) == -1 ) + ; + + for( prn = TAILQ_FIRST( &PrnCtrl.dlist ); prn; prn = nprn ) { + nprn = TAILQ_NEXT( prn, dlink ); + prn_free( prn ); + } +} + +void prn_bulk_cbf( struct usbd_urb *urb, struct usbd_pipe *pipe, void *hdl ) +{ + pipe = pipe, hdl = hdl; + + pthread_sleepon_lock( ); + pthread_sleepon_signal( urb ); + pthread_sleepon_unlock( ); + + return; +} + +int prn_bulk_wait( prn_t *prn, struct usbd_urb *urb, struct usbd_pipe *pipe ) +{ + _uint32 ustatus; + _uint32 alen; + + pthread_sleepon_lock( ); + while( usbd_urb_status( urb, &ustatus, &alen ) == EBUSY && + !( prn->status & PRN_UNBLOCK ) ) { + pthread_sleepon_wait( urb ); + } + pthread_sleepon_unlock( ); + + // check for a stall condition and clear it + if( ( ustatus & USBD_USB_STATUS_MASK ) == USBD_STATUS_STALL ) { + usbd_reset_pipe( pipe ); + } + + if( prn->status & PRN_UNBLOCK ) { + return( EINTR ); + } + + return( ( ustatus & USBD_URB_STATUS_MASK ) == USBD_STATUS_CMP ? EOK : EIO ); +} + +int prn_io( prn_t *prn, struct usbd_pipe *pipe, _uint32 flags, _uint8 *buffer, _uint32 length ) +{ + _uint32 status; + + usbd_setup_bulk( prn->urb, flags, buffer, length ); + if( ( status = usbd_io( prn->urb, pipe, prn_bulk_cbf, prn, USBD_TIME_INFINITY ) ) == EOK ) { + return( prn_bulk_wait( prn, prn->urb, pipe ) ); + } + return( status ); +} + +int prn_device_id( prn_t *prn, char *buffer, uint32_t length ) +{ + _uint32 status; + struct usbd_urb *urb; + + if( urb = usbd_alloc_urb( NULL ) ) { + usbd_setup_vendor( urb, URB_DIR_IN, 0, USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE, 0, 0, buffer, length ); + if( ( status = usbd_io( urb, prn->ep_cntl, NULL, prn, USBD_TIME_INFINITY ) ) == EOK ) { + } + usbd_free_urb( urb ); + } + else { + status = ENOMEM; + } + + return( status ); +} + +int prn_reset( prn_t *prn ) +{ + _uint32 status; + struct usbd_urb *urb; + +// Note: version 1.0 of the spec incorrectly stated that +// the recipient should be other, version 1.1 states that +// it should be interface + + status = ENOMEM; + if( urb = usbd_alloc_urb( NULL ) ) { + usbd_setup_vendor( urb, URB_DIR_OUT, PRN_SOFT_RESET, USB_TYPE_CLASS | + ( ( prn->bcdUSB & 0x10 ) ? USB_RECIPIENT_INTERFACE : USB_RECIPIENT_OTHER ), + 0, 0, NULL, 0 ); + if( ( status = usbd_io( urb, prn->ep_cntl, NULL, prn, USBD_TIME_INFINITY ) ) == EOK ) { + } + usbd_free_urb( urb ); + } + + return( status ); +} + +int prn_status( prn_t *prn ) +{ + _uint32 status; + struct usbd_urb *urb; + _uint8 *buffer; + + status = ENOMEM; + if( urb = usbd_alloc_urb( NULL ) ) { + if( buffer = usbd_alloc( 1 ) ) { + usbd_setup_vendor( urb, URB_DIR_IN, 1, USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE, 0, 0, buffer, 1 ); + if( ( status = usbd_io( urb, prn->ep_cntl, NULL, prn, USBD_TIME_INFINITY ) ) == EOK ) { + prn->query_status = ( prn->query_status & ~0xff ) | *buffer; + } + usbd_free( buffer ); + } + usbd_free_urb( urb ); + } + + return( status ); +} + +int prn_io_unblock( resmgr_context_t *ctp, io_pulse_t *msg, RESMGR_OCB_T *ocb ) +{ + prn_t *prn; + + prn = (prn_t *)ocb->attr; + + pthread_sleepon_lock( ); + prn->status |= PRN_UNBLOCK; + pthread_sleepon_signal( prn->urb ); + pthread_sleepon_unlock( ); + + return( _RESMGR_NOREPLY ); +} + +int prn_io_open( resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra ) +{ + prn_t *prn; + int status; + + prn = (prn_t *)handle; + + if( !( prn->status & PRN_PRESENT ) ) { + return( ENODEV ); + } + + if( status = iofunc_open_default( ctp, msg, handle, extra ) ) { + return( status ); + } + + return( EOK ); +} + +int prn_io_close( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb ) +{ + prn_t *prn; + + prn = (prn_t *)ocb->attr; + + iofunc_attr_lock( ocb->attr ); + if( iofunc_ocb_detach( ctp, ocb ) & IOFUNC_OCB_LAST_WRITER ) { + // terminate the printer transfer with a null Bulk Out packet + if( prn->status & PRN_PRESENT ) { + if( prn_io( prn, prn->ep_out, URB_DIR_OUT, prn->buffer, 0 ) ) { + } + } + } + iofunc_ocb_free( ocb ); + iofunc_attr_unlock( &prn->attr ); + return( EOK ); +} + +int prn_io_read( resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb ) +{ + prn_t *prn; + _uint32 pos; + _uint8 *ibuf; + _uint32 nbytes; + _uint32 status; + _uint32 alen; + + prn = (prn_t *)ocb->attr; + pos = 0; + ibuf = prn->buffer; + nbytes = msg->i.nbytes; + + if( !( prn->status & PRN_PRESENT ) ) { + return( ENODEV ); + } + + if( prn->protocol != USB_PROTOCOL_PRN_BIDIR ) { + return( ENXIO ); + } + + 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 ); + } + + if( atomic_set_value( &prn->status, PRN_READING ) & PRN_READING ) { + return( EBUSY ); + } + + prn->status &= ~PRN_UNBLOCK; + while( 1 ) { + if( status = prn_io( prn, prn->ep_in, URB_DIR_IN, ibuf, min( nbytes, MAX_PRN_BUFFER ) ) ) { + break; + } + + usbd_urb_status( prn->urb, NULL, &alen ); + resmgr_msgwrite( ctp, ibuf, alen, pos ); + nbytes -= alen; + pos += alen; + + if( prn->status & PRN_UNBLOCK ) { + status = EINTR; + break; + } + + if( !nbytes || !alen ) { + atomic_clr_value( &prn->status, PRN_READING ); + _IO_SET_READ_NBYTES( ctp, pos ); + return( EOK ); + } + } + + atomic_clr_value( &prn->status, PRN_READING ); + return( status ); +} + +int prn_io_write( resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb ) +{ + prn_t *prn; + uint8_t *ibuf; + uint8_t *obuf; + int off, iidx, oidx; + int rlen; + int ilen; + int opost; + int olen; + int status; + + prn = (prn_t *)ocb->attr; + rlen = msg->i.nbytes; + + if( !( prn->status & PRN_PRESENT ) ) { + return( ENODEV ); + } + + if( ( msg->i.xtype & _IO_XTYPE_MASK ) != _IO_XTYPE_NONE ) { + return( EINVAL ); + } + + if( !( ocb->ioflag & _IO_FLAG_WR ) ) { + return( EBADF ); + } + + ocb->flags |= IOFUNC_ATTR_ATIME; + + if( rlen == 0 ) { + _IO_SET_WRITE_NBYTES( ctp, 0 ); + return( EOK ); + } + + if( atomic_set_value( &prn->status, PRN_WRITING ) & PRN_WRITING ) { + return( EBUSY ); + } + + prn->status &= ~PRN_UNBLOCK; + opost = prn->tios.c_oflag & OPOST; + off = sizeof( io_write_t ); + ibuf = prn->buffer; + obuf = ibuf + ( opost ? 512 : 0 ); + while( rlen ) { + ilen = ( rlen <= MAX_PRN_BUFFER ) ? rlen : MAX_PRN_BUFFER; + if( ( olen = resmgr_msgread( ctp, ibuf, ilen, off ) ) != ilen ) { + break; + } + + if( opost ) { + for( iidx = 0, oidx = 0; iidx < ilen; iidx++, oidx++ ) { + if( ibuf[iidx] == 0xa ) { + obuf[oidx++] = 0xd; + olen++; + } + obuf[oidx] = ibuf[iidx]; + } + } + + if( status = prn_io( prn, prn->ep_out, URB_DIR_OUT, obuf, olen ) ) { + atomic_clr_value( &prn->status, PRN_WRITING ); + return( status ); + } + rlen -= ilen; + off += MAX_PRN_BUFFER; + } + + atomic_clr_value( &prn->status, PRN_WRITING ); + _IO_SET_WRITE_NBYTES( ctp, msg->i.nbytes - rlen ); + return( EOK ); +} + +int prn_io_devctl( resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb ) +{ + prn_t *prn; + uint32_t *data; + int nbytes; + int status; + + nbytes = 0; + data = (uint32_t *)_DEVCTL_DATA( msg->i ); + prn = (prn_t *)ocb->attr; + + if( !( prn->status & PRN_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, &prn->tios, nbytes ); + break; + + case DCMD_CHR_TCSETATTR: + nbytes = sizeof( struct termios ); + memcpy( &prn->tios, data, nbytes ); + break; + + case DCMD_CHR_TCFLUSH: + break; + + case DCMD_CHR_TTYINFO: { + struct _ttyinfo *ttyinfo; + + ttyinfo = (struct _ttyinfo *)data; + sprintf( ttyinfo->ttyname, "/dev/%s%d", PrnCtrl.prefix, prn->devnum ); + ttyinfo->opencount = prn->attr.count; + nbytes = sizeof( *ttyinfo ); + break; + } + + case DCMD_CHR_LINESTATUS: + prn_status( prn ); + *data = prn->query_status; + nbytes = sizeof( *data ); + break; + + case DCMD_CHR_PARCTL: + if( *data & 0x4 ) { + status = prn_reset( prn ); + } + break; + + case DCMD_CHR_SERCTL: + break; + + case DCMD_CHR_PNPTEXT: { + char *id; + + if( id = usbd_alloc( MAX_PRN_DEVICE_NAME ) ) { + prn_device_id( prn, id, MAX_PRN_DEVICE_NAME ); + nbytes = min( ENDIAN_BE16( *((uint16_t *)id ) ), msg->i.nbytes ); + strncpy( (void *)data, id + 2, nbytes ); + usbd_free( id ); + } + break; + } + + default: + status = ENOSYS; + break; + } + + msg->o.ret_val = status; + return( _RESMGR_PTR( ctp, &msg->o, sizeof( msg->o ) + nbytes ) ); +} + +int prn_get_devnum( ) +{ + int cnt; + int match; + prn_t *prn; + + for( cnt = 0; cnt < 16; cnt++ ) { + for( match = 0, prn = TAILQ_FIRST( &PrnCtrl.dlist ); prn; prn = TAILQ_NEXT( prn, dlink ) ) { + if( minor( prn->devnum ) == cnt ) { + match = 1; + break; + } + } + if( !match ) { + break; + } + } + return( cnt ); +} + +int prn_io_lock_ocb( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb ) +{ + return( EOK ); +} + +int prn_io_unlock_ocb( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb ) +{ + return( EOK ); +} + +int prn_resmgr_attach( prn_t *prn ) +{ + char name[255]; + + // setup termios structure + prn->tios.c_oflag = 0; // OPOST; + prn->query_status = 0x418; + iofunc_attr_init( &prn->attr, 0600 | S_IFCHR, 0, 0 ); + iofunc_func_init( _RESMGR_CONNECT_NFUNCS, &prn->resmgr_connect_funcs, _RESMGR_IO_NFUNCS, &prn->resmgr_io_funcs ); + if( ( prn->attr.rdev = rsrcdbmgr_devno_attach( "usb", -1, 0 ) ) != -1 ) { + prn->devnum = prn_get_devnum( ); + sprintf( name, "/dev/%s%d", PrnCtrl.prefix, prn->devnum ); + prn->resmgr_connect_funcs.open = prn_io_open; + prn->resmgr_io_funcs.close_ocb = prn_io_close; + prn->resmgr_io_funcs.unlock_ocb = prn_io_unlock_ocb; + prn->resmgr_io_funcs.lock_ocb = prn_io_lock_ocb; + prn->resmgr_io_funcs.read = prn_io_read; + prn->resmgr_io_funcs.write = prn_io_write; + prn->resmgr_io_funcs.devctl = prn_io_devctl; + prn->resmgr_io_funcs.unblock = prn_io_unblock; + if( ( prn->resmgr_id = resmgr_attach( PrnCtrl.dispatch, NULL, name, _FTYPE_ANY, 0, + &prn->resmgr_connect_funcs, &prn->resmgr_io_funcs, &prn->attr ) ) != -1 ) { + resmgr_devino( prn->resmgr_id, &prn->io_mount.dev, &prn->attr.inode ); + return( EOK ); + } + else { + fprintf( stderr, "PRN: resmgr_attach\n" ); + } + rsrcdbmgr_devno_detach( prn->attr.rdev, 0 ); + } + else { + fprintf( stderr, "Unable to acquire devno\n" ); + } + + return( EIO ); +} + +int prn_parse_descriptors( prn_t *prn ) +{ + _uint32 eix; + _uint32 scan; + _uint32 found; + usbd_descriptors_t *desc; + usbd_device_descriptor_t *ddesc; + usbd_interface_descriptor_t *idesc; + struct usbd_desc_node *dev, *ifc, *ept; + int alternate; + + scan = PRN_CONTROL_EP | PRN_BULKOUT_EP; + found = 0; + + if( ddesc = usbd_device_descriptor( prn->device, &dev ) ) { + prn->bcdUSB = ddesc->bcdUSB; + // look for interface with bi-directional or uni-directional protocol (No support for IEEE 1284.4 multi function devices) + for ( alternate = 0 ; ( idesc = usbd_interface_descriptor( prn->device, prn->instance.config, prn->instance.iface, alternate, &ifc ) ) ; alternate++ ) { + if( (idesc->bInterfaceProtocol != USB_PROTOCOL_PRN_BIDIR) && (idesc->bInterfaceProtocol != USB_PROTOCOL_PRN_UNIDIR) ) + continue; + + if( ( prn->protocol = idesc->bInterfaceProtocol ) == USB_PROTOCOL_PRN_BIDIR ) { + scan |= PRN_BULKIN_EP; + } + usbd_select_interface( prn->device, prn->instance.iface, alternate ); + for( eix = 0; ( desc = usbd_parse_descriptors( prn->device, ifc, USB_DESC_ENDPOINT, eix, &ept ) ) != NULL; ++eix ) { + switch( desc->endpoint.bmAttributes ) { + case USB_ATTRIB_CONTROL: + if( usbd_open_pipe( prn->device, desc, &prn->ep_cntl ) == EOK ) { + found |= PRN_CONTROL_EP; + } + break; + + case USB_ATTRIB_ISOCHRONOUS: + break; + + case USB_ATTRIB_BULK: + switch( desc->endpoint.bEndpointAddress & USB_ENDPOINT_IN ) { + case USB_ENDPOINT_OUT: + if( usbd_open_pipe( prn->device, desc, + &prn->ep_out ) == EOK ) { + found |= PRN_BULKOUT_EP; + } + break; + + case USB_ENDPOINT_IN: + if( ( scan & PRN_BULKIN_EP ) && + usbd_open_pipe( prn->device, desc, + &prn->ep_in ) == EOK ) { + found |= PRN_BULKIN_EP; + } + break; + } + break; + + case USB_ATTRIB_INTERRUPT: + break; + } + } + if( found == scan ) + break; + } + } + + return( ( found == scan ) ? EOK : ENODEV ); +} + +void prn_insertion( struct usbd_connection *connection, usbd_device_instance_t *instance ) +{ + prn_t *prn; + struct usbd_device *device; + int status; + if( ( status = usbd_attach( connection, instance, sizeof( prn_t ), &device ) ) == EOK ) { + prn = usbd_device_extra( device ); + prn->device = device; + prn->instance = *instance; + if( prn_parse_descriptors( prn ) == EOK ) { + if( prn->buffer = usbd_alloc( PRN_BUFFER_SIZE ) ) { + if( prn->urb = usbd_alloc_urb( NULL ) ) { + if( prn_resmgr_attach( prn ) == EOK ) { + prn->status = PRN_PRESENT; + TAILQ_INSERT_TAIL( &PrnCtrl.dlist, prn, dlink ); + return; + } + usbd_free_urb( prn->urb ); + } + usbd_free( prn->buffer ); + } + } + usbd_detach( device ); + } +} + +void prn_removal( struct usbd_connection *connection, usbd_device_instance_t *instance ) +{ + struct usbd_device *device; + prn_t *prn; + + if( ( device = usbd_device_lookup( connection, instance ) ) != NULL ) { + prn = usbd_device_extra( device ); + prn_free( prn ); + } +} + +int prn_resmgr_init( ) +{ + resmgr_attr_t attr; + + if( procmgr_daemon( EXIT_SUCCESS, PROCMGR_DAEMON_NOCLOSE | PROCMGR_DAEMON_NODEVNULL ) == -1 ) { + fprintf( stderr, "Prn: procmgr_daemon\n" ); + return( -1 ); + } + + if( ( PrnCtrl.dispatch = dispatch_create( ) ) == NULL ) { + fprintf( stderr, "Unable to allocate dispatch context\n" ); + return( -1 ); + } + + memset( &PrnCtrl.pool_attr, 0, sizeof( PrnCtrl.pool_attr ) ); + if( ( PrnCtrl.pool_attr.attr = malloc( sizeof( *PrnCtrl.pool_attr.attr ) ) ) == NULL ) { + fprintf( stderr, "Unable to alloc pool attr\n" ); + return( -1 ); + } + + pthread_attr_init( PrnCtrl.pool_attr.attr ); + pthread_attr_setstacksize( PrnCtrl.pool_attr.attr, 8192 ); + + PrnCtrl.pool_attr.handle = PrnCtrl.dispatch; + PrnCtrl.pool_attr.context_alloc = resmgr_context_alloc; + PrnCtrl.pool_attr.block_func = resmgr_block; + PrnCtrl.pool_attr.handler_func = (void *)dispatch_handler; + PrnCtrl.pool_attr.context_free = resmgr_context_free; + PrnCtrl.pool_attr.lo_water = 1; + PrnCtrl.pool_attr.hi_water = 2; + PrnCtrl.pool_attr.increment = 1; + PrnCtrl.pool_attr.maximum = 2; + + if( ( PrnCtrl.thread_pool = thread_pool_create( &PrnCtrl.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( PrnCtrl.dispatch, &attr, NULL, 0, 0, NULL, NULL, NULL ) == -1 ) { + fprintf( stderr, "Unable to attach name\n" ); + return( -1 ); + } + + if( thread_pool_start( PrnCtrl.thread_pool ) ) { + fprintf( stderr, "Unable to initialize thread_pool\n" ); + return( -1 ); + } + + return( 0 ); +} + +int prn_start_driver( int argc, char *argv[] ) +{ + struct usbd_connection *connection; + usbd_funcs_t funcs = { _USBDI_NFUNCS, prn_insertion, prn_removal, NULL }; + usbd_device_ident_t interest = { USBD_CONNECT_WILDCARD, USBD_CONNECT_WILDCARD, USB_CLASS_PRN, USB_SUBCLASS_PRN, USBD_CONNECT_WILDCARD }; + 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( &PrnCtrl.dlist ); + PrnCtrl.prefix = "usbpar"; + + while( ( opt = getopt( argc, argv, "vn:s:w:" ) ) != -1 ) { + switch( opt ) { + case 'n': + PrnCtrl.prefix = optarg; + break; + + case 'v': + PrnCtrl.verbose++; + break; + + case 's': + parm.path = optarg; + break; + + case 'w': + parm.connect_wait = strtol( optarg, 0, 0 ); + break; + + default: + break; + } + } + + if( ( PrnCtrl.chid = ChannelCreate( _NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK ) ) == -1 || + ( PrnCtrl.coid = ConnectAttach( 0, 0, PrnCtrl.chid, _NTO_SIDE_CHANNEL, 0 ) ) == -1 ) { + fprintf( stderr, "Unable to attach channel and connection\n" ); + return( errno ); + } + + if( prn_resmgr_init( ) ) { + fprintf( stderr, "Printer: Resmgr Init Failure\n" ); + return( errno ); + } + + if( ( status = usbd_connect( &parm, &connection ) ) != EOK ) { + fprintf( stderr, "Prn Class/init - %s\n", strerror( status ) ); + return( status ); + } + + return( status ); +} + +int main( int argc, char *argv[] ) +{ + if( prn_start_driver( argc, argv ) ) { + exit( EXIT_FAILURE ); + } + prn_signal_handler( ); + exit( EXIT_SUCCESS ); +} diff --git a/devu/class/printer/printer.h b/devu/class/printer/printer.h new file mode 100644 index 0000000..f487b4e --- /dev/null +++ b/devu/class/printer/printer.h @@ -0,0 +1,93 @@ +/* + * (c) 2010-2019, SWD Embedded Systems Limited, http://www.kpda.ru + */ + +// Module Description: header for printer class driver + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// private headers +#include + +// PRINTER CLASS DRIVER STRUCTURE +typedef struct _prn { + iofunc_attr_t attr; + TAILQ_ENTRY( _prn ) 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_in; + struct usbd_pipe *ep_out; + struct usbd_urb *urb; + _uint32 bcdUSB; + _uint32 protocol; + _uint32 query_status; + _uint8 *buffer; +} prn_t; + +typedef struct _prn_ctrl { + TAILQ_HEAD(,_prn) dlist; // linked list of prn's + _uint32 cflags; + _uint32 verbose; + char *prefix; + int coid; + int chid; + thread_pool_t *thread_pool; + dispatch_t *dispatch; + thread_pool_attr_t pool_attr; +} prn_ctrl_t; + +// PRINTER CLASS DEFINITION +#ifndef USB_CLASS_PRN +#define USB_CLASS_PRN 7 +#endif +#define USB_SUBCLASS_PRN 1 +#define USB_PROTOCOL_PRN_UNIDIR 1 +#define USB_PROTOCOL_PRN_BIDIR 2 +#define GET_DEVICE_ID 0 +#define GET_PORT_STATUS 1 +#define SOFT_RESET 2 + +#define PRN_OFF 0x00 +#define PRN_ON 0x01 +#define PRN_UNBLOCK 0x02 +#define PRN_PRESENT 0x04 +#define PRN_READING 0x08 +#define PRN_WRITING 0x10 + +#define PRN_CONTROL_EP 1 +#define PRN_BULKIN_EP 2 +#define PRN_BULKOUT_EP 4 + +#define MAX_PRN_BUFFER 512 +#define MAX_PRN_DEVICE_NAME 512 +#define PRN_MAX_ENDPOINT 16 +#define PRN_BUFFER_SIZE 1536 + +#define PRN_GET_DEVICE_ID 0 +#define PRN_GET_PORT_STATUS 1 +#define PRN_SOFT_RESET 2 diff --git a/devu/class/printer/x86/Makefile b/devu/class/printer/x86/Makefile new file mode 100644 index 0000000..c95a666 --- /dev/null +++ b/devu/class/printer/x86/Makefile @@ -0,0 +1,2 @@ +LIST=VARIANT +include recurse.mk diff --git a/devu/class/printer/x86/o/Makefile b/devu/class/printer/x86/o/Makefile new file mode 100644 index 0000000..0f9d7b9 --- /dev/null +++ b/devu/class/printer/x86/o/Makefile @@ -0,0 +1 @@ +include ../../../common.mk