summaryrefslogblamecommitdiffstats
path: root/bsps/shared/dev/display/disp_hcms29xx.c
blob: 5ad7063865393ca6c3df40cdbe63bed7185ebf5a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                                   
                                                                   


                                                                   




                                                                   

                        
                               

                         
                                  


















                                                                      


                    


                

                                                 






















                                                                             
 

                                          
                   







                                 
               






                                                                        
                                                

                


                        













                                                                             
                    








                                                                             
                    

                              
                                     







                                                                             
                                                









                                                                             
                                                                             





                                                                             
 

                                          
                       






                     
                                 























                                                                    
                            

                                              
                                                           





                                    

   










                                                                    
                                                                          

















                                                                       
                                                                         


                
   





                                                                             
                                                      









                                                                             
                                                                             





                                                                             
 




                                          
                                 


































                                                                             
                                                      
















                                                                                       
                  

                                          
                 




















                                                              
                                                                  





                               


                                  

                                  
                      

                                                                
                                                                 








                                                                      
                           
       
                             



                                           
       
 
























                                                                             
                                                      





















                                                                             
                      






                                                

                                 




                                 

                                 





















                                                                  
                                                                      



























                                                                             
                                                         














                                                                             
                                       
 
                                                                             




                                                                             
                                           






                                                                             
                                









                                                                             
                                                          











                                                                                   
 


























                                                          
                                                        





                                                                       


                                                           
                                            
                                                          

                                   

                                                                         
       
        
























                                                                            
                        









                                                                                        
      












                                                                         
   
    
                                   





                                                                             
                                             















                                                                             
 



























                                                                                        
 





                                                                             
                                                




















                                                                             
                                                          




                                            
     

                                                   
     











                                                                              
                                                       













                                                                          
 











                                                                      
                                                        
                                                       

            
 



                                                                             
                                          















                                                                             
                                                          
    





                                                             
 


                                                                             
                                           
















                                                                             
               
                                                          


                                         
                                                            
       
                                                


                                          
                                                
                                                                  

                                                                                 
                                                           

                                                                       
                                            
     
      



                                                                         
                                                                             










                                                                             
                                           















                                                                             
 

                          




                                                       



                                                       




                                                    

                                                

                            








          





                                                      
/*===============================================================*\
| Project: display driver for HCMS29xx                            |
+-----------------------------------------------------------------+
| File: disp_hcms29xx.c                                           |
+-----------------------------------------------------------------+
|                    Copyright (c) 2008                           |
|                    Embedded Brains GmbH                         |
|                    Obere Lagerstr. 30                           |
|                    D-82178 Puchheim                             |
|                    Germany                                      |
|                    rtems@embedded-brains.de                     |
+-----------------------------------------------------------------+
| The license and distribution terms for this file may be         |
| found in the file LICENSE in this distribution or at            |
| http://www.rtems.org/license/LICENSE.                           |
+-----------------------------------------------------------------+
| this file contains the SPI based driver for a HCMS29xx 4 digit  |
| alphanumeric LED display                                        |
\*===============================================================*/

#include <string.h>
#include <stdlib.h>

#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/score/assert.h>
#include <bsp.h>
#include <rtems/libi2c.h>
#include <libchip/disp_hcms29xx.h>
#include "font_hcms29xx.h"
#define FONT_BASE font_hcms29xx_base


#define DISP_HCMS29XX_DIGIT_CNT (4)
#define DISP_HCMS29XX_SEMA_NAME      rtems_build_name('D','4','I','Q')
#define DISP_HCMS29XX_TRNS_SEMA_NAME rtems_build_name('D','4','T','R')
#define DISP_HCMS29XX_TIMER_NAME     rtems_build_name('D','4','T','M')
#define DISP_HCMS29XX_TASK_NAME      rtems_build_name('D','4','T','A')

#define DISP_HCMS29XX_EVENT_TIMER  RTEMS_EVENT_1
#define DISP_HCMS29XX_EVENT_NEWSTR RTEMS_EVENT_2


static disp_font_t disp_hcms29xx_font_normal;
static disp_font_t disp_hcms29xx_font_rotate;
const rtems_libi2c_tfr_mode_t spi_disphcms29xx_tfr_mode = {
  .baudrate = 1000000,
  .bits_per_char = 8,
  .lsb_first = true,
  .clock_inv = true,
  .clock_phs = true,
  .idle_char = 0
};

static disp_hcms29xx_drv_t disp_hcms29xx_drv_tbl;

/*=========================================
 * font management functions
 */

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_status_code disp_hcms29xx_font_struct_size
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   compute size of font data structure tree                                |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  disp_font_t            src,           /* source font                     */
  size_t                *dst_size       /* destination: size of font struct*/
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{

  rtems_status_code rc = RTEMS_SUCCESSFUL;
  size_t font_size = 0;
  size_t glyph_idx;
  /*
   * check parameters
   */
  if ((rc == RTEMS_SUCCESSFUL) &&
      (src == NULL)) {
    rc = RTEMS_INVALID_ADDRESS;
  }
  if (rc == RTEMS_SUCCESSFUL) {
    font_size =
      sizeof(*src);                      /* font_base structure       */
  }
  glyph_idx = 0;
  while ((rc == RTEMS_SUCCESSFUL) &&
	 (glyph_idx < (sizeof(src->latin1)/sizeof(src->latin1[0])))) {
    if (src->latin1[glyph_idx] != NULL) {
      font_size += sizeof(*(src->latin1[glyph_idx]))
	+ (size_t) src->latin1[glyph_idx]->bb.w;
    }
    glyph_idx++;
  }
  *dst_size = font_size;

  return rc;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static inline unsigned char disp_hcms29xx_bitswap
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   swap data bits in byte (7<->0 , 6<->1 etc)                              |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  unsigned char byte
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{
  unsigned char result = 0;
  int smsk,dmsk;
  for (smsk =  0x01,dmsk=0x80;
       smsk < 0x100;
       smsk<<=1   ,dmsk>>=1) {
    if ((byte & smsk) != 0) {
      result |= (unsigned char) dmsk;
    }
  }
  return result;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_status_code disp_hcms29xx_copy_font
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   copy font data from source to dest font structure                       |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  disp_font_t src,                      /* source font                     */
  struct disp_font_base *dst,           /* ptr to destination font         */
  int shift_cnt,                        /* shift count for font            */
  bool do_rotate                        /* rotate font, if true            */
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{

  rtems_status_code rc = RTEMS_SUCCESSFUL;
  char *alloc_next = (char *)dst;
  size_t glyph_idx = 0;
  int glyph_size;
  unsigned char byte;
  int bcnt;

  /*
   * check parameters
   */
  if ((rc == RTEMS_SUCCESSFUL) &&
      ((src == NULL) ||
       (dst == NULL))) {
    rc = RTEMS_INVALID_ADDRESS;
  }
  /*
   * copy font_base structure
   */
  if (rc == RTEMS_SUCCESSFUL) {
    *dst = *src;
    alloc_next += sizeof(*dst);
  }
  /*
   * for all glyphs: assign own glyph memory
   */
  glyph_idx = 0;
  while ((rc == RTEMS_SUCCESSFUL) &&
	 glyph_idx < (sizeof(src->latin1)/sizeof(src->latin1[0]))) {
    if (src->latin1[glyph_idx] != NULL) {
      /*
       * allocate space for glyph
       */
      dst->latin1[glyph_idx] = (struct disp_font_glyph *)alloc_next;
      alloc_next += sizeof(*(dst->latin1[glyph_idx]));
      /*
       * copy source values.
       * Note: bitmap will be reassigned later
       */
      *(struct disp_font_glyph *)(dst->latin1[glyph_idx]) =
	*(src->latin1[glyph_idx]);
    }
    else {
      dst->latin1[glyph_idx] = NULL;
    }
    glyph_idx++;
  }

  /*
   * for all glyphs: reassign bitmap
   */
  glyph_idx = 0;
  while ((rc == RTEMS_SUCCESSFUL) &&
	 glyph_idx < (sizeof(src->latin1)/sizeof(src->latin1[0]))) {
    if (src->latin1[glyph_idx] != NULL) {
      glyph_size = src->latin1[glyph_idx]->bb.w;
      /*
       * allocate space for glyph_bitmap
       */
      dst->latin1[glyph_idx]->bitmap = (const unsigned char *) alloc_next;
      alloc_next += glyph_size;
      /*
       * copy/transform bitmap
       */
      for (bcnt = 0;bcnt < glyph_size;bcnt++) {
	if (do_rotate) {
	  byte = src->latin1[glyph_idx]->bitmap[glyph_size - 1 - bcnt];
	  byte = disp_hcms29xx_bitswap(byte);
	}
	else {
	  byte = src->latin1[glyph_idx]->bitmap[bcnt];
	}
	if (shift_cnt < 0) {
	  byte = byte >> shift_cnt;
	}
	else if (shift_cnt > 0) {
	  byte = byte >> shift_cnt;
	}
	((unsigned char *)(dst->latin1[glyph_idx]->bitmap))[bcnt] = byte;
      }
    }
    glyph_idx++;
  }
  return rc;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_status_code disp_hcms29xx_alloc_copy_font
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   copy font data from source to dest font structure, alloc all data       |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  const disp_font_t src,                /* source font                     */
  disp_font_t *dst,                     /* ptr to destination font         */
  int shift_cnt,                        /* shift count for font            */
  bool do_rotate                        /* rotate font, if true            */
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{

  rtems_status_code rc = RTEMS_SUCCESSFUL;
  size_t src_size = 0;
  /*
   * check parameters
   */
  if ((rc == RTEMS_SUCCESSFUL) &&
      ((src == NULL)
       || (dst == NULL))) {
    rc = RTEMS_INVALID_ADDRESS;
  }
  /*
   * determine size of source data
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = disp_hcms29xx_font_struct_size(src,&src_size);
  }
  /*
   * allocate proper data area
   */
  if (rc == RTEMS_SUCCESSFUL) {
    *dst = malloc(src_size);
    if (*dst == NULL) {
      rc = RTEMS_UNSATISFIED;
    }
  }
  /*
   * scan through source data, copy to dest
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = disp_hcms29xx_copy_font(src,*dst,shift_cnt,do_rotate);
  }
  return rc;
}

/*=========================================
 * SPI communication functions
 */

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_status_code disp_hcms29xx_send_to_display
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   request access semaphore to SPI, prepare buffer descriptors, start      |
|   transfer via SPI to display                                             |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
   disp_hcms29xx_drv_t *softc_ptr,
   const volatile char *disp_buffer /* start of chars to display (4 chars or 'til \0)*/
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{
  rtems_status_code rc = RTEMS_SUCCESSFUL;
  bool char_avail;
  const struct disp_font_glyph *glyph_ptr;
  disp_font_t curr_font;
  int i, ret_cnt;
  unsigned char c;

  /*
   * select device, set transfer mode, address device
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_libi2c_send_start(softc_ptr->disp_param.minor);
  }
  /*
   * set transfer mode
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = -rtems_libi2c_ioctl(softc_ptr->disp_param.minor,
			     RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
			     &spi_disphcms29xx_tfr_mode);
  }

  /*
   * address device
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_libi2c_send_addr(softc_ptr->disp_param.minor,true);
  }

  /*
   * send data
   */
  if (rc == RTEMS_SUCCESSFUL) {
    curr_font =
      softc_ptr->disp_param.rotate
      ? disp_hcms29xx_font_rotate
      : disp_hcms29xx_font_normal;

    char_avail = true;
    /*
     * FIXME: for rotated display, write last character first...
     * maybe we should copy everything to a common buffer and use
     * ONE SPI transfer?
     */
    for (i = 0;
	 ((rc == RTEMS_SUCCESSFUL) &&
	  (i < DISP_HCMS29XX_DIGIT_CNT));
	  i++) {
      /* test for end of string... */
      c = disp_buffer[i]; /* perform consistent read of disp_buffer */
      if (char_avail && (c == '\0')) {
	char_avail = false;
      }
      glyph_ptr = (char_avail
		   ? curr_font->latin1[c]
		   : NULL);
      if (glyph_ptr == NULL) {
	glyph_ptr = curr_font->latin1[' '];
      }

      /*
       * send 5 bytes from (char *)glyph_ptr->bitmap to SPI
       */
      if (rc == RTEMS_SUCCESSFUL) {
	ret_cnt = rtems_libi2c_write_bytes(softc_ptr->disp_param.minor,
					   glyph_ptr->bitmap,5);
	if (ret_cnt < 0) {
	  rc = -ret_cnt;
	}
      }
    }
  }
  /*
   * finish transfer
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_libi2c_send_stop(softc_ptr->disp_param.minor);
  }

  return rc;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_status_code disp_hcms29xx_send_to_control
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   request access semaphore to SPI, prepare buffer descriptors, start      |
|   transfer via SPI to display                                             |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
   disp_hcms29xx_drv_t *softc_ptr,
   int pwm,   /* value for pwm of LEDs, 0..15 */
   int peak,  /* value for peak current for LEDs, 0..3 */
   int sleep, /* value to make display "sleep" (0..1 */
   int div,   /* divider for external osc input, unused here */
   int chain  /* mode to drive other displays, unused here */
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{
  rtems_status_code rc = RTEMS_SUCCESSFUL;
  int run, ret_cnt;
  uint8_t ctrl_buffer;

  /* two accesses, control word 0 and 1 */
  for (run = 0;
       ((rc == RTEMS_SUCCESSFUL) && (run <= 1));
       run++) {
    if (rc == RTEMS_SUCCESSFUL) {
      if (run == 0) {
	ctrl_buffer =
	  (0              << 7) |
	  ((sleep & 0x01) << 6) |
	  ((peak  & 0x03) << 4) |
	  ((pwm & 0x0f)   << 0);
      }
      else {
	ctrl_buffer =
	  (1              << 7) |
	  ((div   & 0x01) << 1) |
	  ((chain & 0x01) << 0);
      }
      /*
       * select device, set transfer mode, address device
       */
      if (rc == RTEMS_SUCCESSFUL) {
	rc = rtems_libi2c_send_start(softc_ptr->disp_param.minor);
      }
      /*
       * set transfer mode
       */
      if (rc == RTEMS_SUCCESSFUL) {
	rc = -rtems_libi2c_ioctl(softc_ptr->disp_param.minor,
				 RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
				 &spi_disphcms29xx_tfr_mode);
      }

      /*
       * address device
       */
      if (rc == RTEMS_SUCCESSFUL) {
	rc = rtems_libi2c_send_addr(softc_ptr->disp_param.minor,true);
      }

      /*
       * send 1 byte from ctrl_buffer
       */
      if (rc == RTEMS_SUCCESSFUL) {
	ret_cnt = rtems_libi2c_write_bytes(softc_ptr->disp_param.minor,
					   &ctrl_buffer,1);
	if (ret_cnt < 0) {
	  rc = -ret_cnt;
	}
      }
    }
  } /* next run ... */

  /*
   * finish transfer
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_libi2c_send_stop(softc_ptr->disp_param.minor);
  }

  return rc;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_timer_service_routine disp_hcms29xx_timer_sr
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   this task updates the string in the display                             |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
(
 rtems_id id, /* ID of timer, not used  */
 void * arg   /* calling arg: softc_ptr */
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    <none used>                                                            |
\*=========================================================================*/
{
  disp_hcms29xx_drv_t *softc_ptr = arg;

  rtems_event_send(softc_ptr->disp_param.task_id, DISP_HCMS29XX_EVENT_TIMER);
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_task disp_hcms29xx_update_task
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   this task updates the string in the display                             |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
   rtems_task_argument argument 
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    <never exits>                                                          |
\*=========================================================================*/
{
  rtems_event_set  my_events;
  rtems_status_code rc = RTEMS_SUCCESSFUL;
  int disp_offset = 0;
  rtems_id disp_hcms29xx_timer_id;
  disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;

  /*
   * initialize display:
   */
  /*
   * set control attributes for display
   * maximum brightness...
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = disp_hcms29xx_send_to_control(softc_ptr,
				       14,3,1,0,0);/* pwm/peak/nosleep/div/chain */
  }

  /*
   * set display to blank
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = disp_hcms29xx_send_to_display(softc_ptr,
				       "");
  }

  /*
   * create timer for scrolling
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_timer_create(DISP_HCMS29XX_TIMER_NAME,
			    &disp_hcms29xx_timer_id);
  }

  while (rc == RTEMS_SUCCESSFUL) {
    /*
     * wait for any event
     */
    rc = rtems_event_receive(DISP_HCMS29XX_EVENT_NEWSTR |
			     DISP_HCMS29XX_EVENT_TIMER ,
			     RTEMS_WAIT | RTEMS_EVENT_ANY,
			     RTEMS_NO_TIMEOUT,
			     &my_events);
    if (my_events & DISP_HCMS29XX_EVENT_NEWSTR) {
      /*
       * fetch new string consistently into local buffer
       */
      if (rc == RTEMS_SUCCESSFUL) {
	rc = rtems_semaphore_obtain(softc_ptr->disp_param.trns_sema_id,
				    RTEMS_WAIT,RTEMS_NO_TIMEOUT);
      }
      if (rc == RTEMS_SUCCESSFUL) {
        strlcpy(softc_ptr->disp_param.disp_buffer,
                softc_ptr->disp_param.trns_buffer,
                sizeof(softc_ptr->disp_param.disp_buffer));
	softc_ptr->disp_param.disp_buf_cnt =
	  (int) strlen(softc_ptr->disp_param.disp_buffer);
      }
      if (rc == RTEMS_SUCCESSFUL) {
        rc = rtems_semaphore_release(softc_ptr->disp_param.trns_sema_id);
        _Assert_Unused_variable_equals(rc, RTEMS_SUCCESSFUL);
      }
      /*
       * set initial offset to negative value
       * to make string static for some ticks
       */
      disp_offset = -4;
    }
    if (my_events & DISP_HCMS29XX_EVENT_TIMER) {
      /*
       * increase disp_offset, if possible, otherwise reset it
       */
      if ((disp_offset < 0) ||
	  (disp_offset < softc_ptr->disp_param.disp_buf_cnt-
	   DISP_HCMS29XX_DIGIT_CNT/2)) {
	disp_offset++;
      }
      else {
	disp_offset = -4;
      }
    }
    /*
     * display string, starting from disp_offset
     */
    if (disp_offset < 0) {
      rc = disp_hcms29xx_send_to_display(softc_ptr,
					 softc_ptr->disp_param.disp_buffer);
    }
    else if (disp_offset
	     < (softc_ptr->disp_param.disp_buf_cnt - DISP_HCMS29XX_DIGIT_CNT)) {
      rc = disp_hcms29xx_send_to_display(softc_ptr,
					 softc_ptr->disp_param.disp_buffer+disp_offset);
    }
    else {
      rc = disp_hcms29xx_send_to_display(softc_ptr,
					 softc_ptr->disp_param.disp_buffer
					 + softc_ptr->disp_param.disp_buf_cnt
					 - DISP_HCMS29XX_DIGIT_CNT);
    }
    /*
     * activate timer, if needed
     */
    if (rc == RTEMS_SUCCESSFUL) {
      if (softc_ptr->disp_param.disp_buf_cnt > DISP_HCMS29XX_DIGIT_CNT) {
	rc = rtems_timer_fire_after(disp_hcms29xx_timer_id,
				    50,
				    disp_hcms29xx_timer_sr,
				    NULL);
      }
      else {
	rc = rtems_timer_cancel(disp_hcms29xx_timer_id);
      }
    }
  }
  /*
   * FIXME: display task is dead...
   */
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
static rtems_status_code disp_hcms29xx_update
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   move given string to display task                                       |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
   disp_hcms29xx_drv_t *softc_ptr,
   const char *src
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{
  rtems_status_code rc = RTEMS_SUCCESSFUL;

  /*
   * obtain trns semaphore
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_semaphore_obtain(softc_ptr->disp_param.trns_sema_id,
				RTEMS_WAIT,RTEMS_NO_TIMEOUT);
  }
  /*
   * copy string...
   */
  strncpy(softc_ptr->disp_param.trns_buffer,src,
	  sizeof(softc_ptr->disp_param.trns_buffer));
  softc_ptr->disp_param.trns_buffer[sizeof(softc_ptr->disp_param.trns_buffer)-1] = '\0';

  /*
   * release trns semaphore
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_semaphore_release(softc_ptr->disp_param.trns_sema_id);
  }

  /*
   * send event to task
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_event_send(softc_ptr->disp_param.task_id,
			  DISP_HCMS29XX_EVENT_NEWSTR);
  }

  return rc;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
rtems_device_driver disp_hcms29xx_dev_initialize
  (
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   prepare the display device driver to accept write calls                 |
|   register device with its name                                           |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  rtems_device_major_number  major,
  rtems_device_minor_number  minor,
  void                      *arg
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
/*
 * Initialize and register the device
 */
{
  rtems_status_code rc = RTEMS_SUCCESSFUL;
  disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;

  /*
   * initialize font management
   * FIXME: check, that default glyph exists
   * FIXME: check font size to be 5x7
   */
  /*
   * translate font according to direction/baseline
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = disp_hcms29xx_alloc_copy_font(
			       &FONT_BASE,
			       &disp_hcms29xx_font_normal,
			       FONT_BASE.descent, /* shift to visibility... */
			       FALSE); /* do not rotate */
  }
  /* FIXME: translate font for rotation */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = disp_hcms29xx_alloc_copy_font(&FONT_BASE,
			       &disp_hcms29xx_font_rotate,
			       0, /* do not shift */
			       true); /* rotate font */
  }
  /*
   * create the trns_buffer semaphore
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_semaphore_create (DISP_HCMS29XX_TRNS_SEMA_NAME,1,
				     RTEMS_PRIORITY
				     |RTEMS_BINARY_SEMAPHORE
				     |RTEMS_INHERIT_PRIORITY
				     |RTEMS_NO_PRIORITY_CEILING
				     |RTEMS_LOCAL,
				     0,
				     &softc_ptr->disp_param.trns_sema_id);
  }

  /*
   * create and start display task
   */
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_task_create(DISP_HCMS29XX_TASK_NAME,
			   20,
			   RTEMS_MINIMUM_STACK_SIZE,
			   RTEMS_INTERRUPT_LEVEL(0) | RTEMS_TIMESLICE,
			   RTEMS_DEFAULT_ATTRIBUTES,
			   &softc_ptr->disp_param.task_id);
  }
  if (rc == RTEMS_SUCCESSFUL) {
    rc = rtems_task_start(softc_ptr->disp_param.task_id,
			  disp_hcms29xx_update_task,0);
  }
  return rc;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
rtems_device_driver disp_hcms29xx_dev_open
(
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   open the display device                                                 |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  rtems_device_major_number  major,
  rtems_device_minor_number  minor,
  void                      *arg
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{
  disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;
  /*
   * ensure, that disp_hcms29xx device is assumed to be empty
   */
  softc_ptr->disp_param.dev_buf_cnt = 0;

  return RTEMS_SUCCESSFUL;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
rtems_device_driver disp_hcms29xx_dev_write
(
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   write to display device                                                 |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  rtems_device_major_number  major,
  rtems_device_minor_number  minor,
  void                      *arg
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{
  rtems_libio_rw_args_t *args = arg;
  uint32_t cnt;
  disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;

  for (cnt = 0;cnt < args->count;cnt++) {
    /*
     * accumulate characters written into display dev buffer
     */
    if (((softc_ptr->disp_param.dev_buf_cnt > 0)
	&&((args->buffer[cnt] == '\n')
	   || (args->buffer[cnt] == '\0'))
	 )
	||( softc_ptr->disp_param.dev_buf_cnt >=
	    (int) sizeof(softc_ptr->disp_param.dev_buffer) - 1)) {
      softc_ptr->disp_param.dev_buffer[softc_ptr->disp_param.dev_buf_cnt] = '\0';
      /*
       * transfer string to display string, redisplay it...
       */
      disp_hcms29xx_update(softc_ptr,softc_ptr->disp_param.dev_buffer);
      softc_ptr->disp_param.dev_buf_cnt = 0;
    }
    /*
     * write to dev_buf, if '\n' occured or display device buffer is full
     */
    if ((args->buffer[cnt] != '\n') &&
	(args->buffer[cnt] != '\0')) {
      softc_ptr->disp_param.dev_buffer[softc_ptr->disp_param.dev_buf_cnt++] =
	args->buffer[cnt];
    }
  }
  args->bytes_moved = args->count;

  return RTEMS_SUCCESSFUL;
}

/*=========================================================================*\
| Function:                                                                 |
\*-------------------------------------------------------------------------*/
rtems_device_driver disp_hcms29xx_dev_close
(
/*-------------------------------------------------------------------------*\
| Purpose:                                                                  |
|   close the display device                                                |
+---------------------------------------------------------------------------+
| Input Parameters:                                                         |
\*-------------------------------------------------------------------------*/
  rtems_device_major_number  major,
  rtems_device_minor_number  minor,
  void                      *arg
)
/*-------------------------------------------------------------------------*\
| Return Value:                                                             |
|    rtems_status_code                                                      |
\*=========================================================================*/
{

  return RTEMS_SUCCESSFUL;
}

/*
 * driver operation tables
 */
static rtems_driver_address_table disp_hcms29xx_ops = {
  .initialization_entry = disp_hcms29xx_dev_initialize,
  .open_entry =           disp_hcms29xx_dev_open,
  .write_entry =          disp_hcms29xx_dev_write,
  .close_entry =          disp_hcms29xx_dev_close
};


static disp_hcms29xx_drv_t disp_hcms29xx_drv_tbl = {
  {/* public fields */
    .ops =         &disp_hcms29xx_ops, 
    .size =        sizeof (disp_hcms29xx_drv_t),
  },
  { /* our private fields */
    0,
    { 0 },
    0,
    { 0 },
    { 0 },
    0,
    0,
    0,
    false
  }
};

rtems_libi2c_drv_t *disp_hcms29xx_driver_descriptor = 
  &disp_hcms29xx_drv_tbl.libi2c_drv_entry;