nanoBSD Driver Build SUCCESS

Thomas D. Dean tomdean at speakeasy.org
Wed May 30 00:50:09 UTC 2012


On 05/29/12 17:22, Thomas D. Dean wrote:

It works!.. lots to tune/change, but, the hard part functions

Thanks, phk for naonBSD and elan_mmcr.

Tom Dean

// lcd_socket.c - driver for the TS5700 spi and lcd

// 2012 05 27 tomdean - initial version

// GPL

#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/ioccom.h>
#include <sys/mman.h>        /* mmap */
#include <elan_mmcr.h>
#include <ts5700.h>
#include <lcd_socket.h>

MALLOC_DEFINE(M_LCD_SOCKET, "lcd_socket_buffer", "buffer for 
lcd_socket_driver");

// all the work is done in ioctl
static d_read_t   lcd_socket_read;
static d_write_t  lcd_socket_write;
static d_ioctl_t  lcd_socket_ioctl;

static struct cdevsw lcd_socket_cdevsw = {
.d_version =  D_VERSION,
.d_read =     lcd_socket_read,
.d_write =    lcd_socket_write,
.d_ioctl =    lcd_socket_ioctl,
.d_name =     "lcd_socket"
};

static lcd_socket_t *lcd_socket;
static struct cdev *lcd_socket_dev;

//static FILE *lcd_fd;
//static struct elan_mmcr *lcd_mmcr;
static uint8_t reg, reg_up;

//extern uint16_t *elan_mmcr;

// struct uio {
//   struct  iovec *uio_iov;         /* scatter/gather list */
//   int     uio_iovcnt;             /* length of scatter/gather list */
//   off_t   uio_offset;             /* offset in target object */
//   ssize_t uio_resid;              /* remaining bytes to copy */
//   enum    uio_seg uio_segflg;     /* address space */
//   enum    uio_rw uio_rw;          /* operation */
//   struct  thread *uio_td;         /* owner */
// };

static int lcd_socket_write(struct cdev *dev, struct uio *uio, int ioflag)
{
   int error = 0;
   int amount;
   int idx;
   amount = MIN(uio->uio_resid,
			   (lcd_socket->buffer_size - 1 - uio->uio_offset > 0) ?
			   lcd_socket->buffer_size - 1 - uio->uio_offset : 0);
   if (amount == 0)
	return (error);
   error = uiomove(lcd_socket->buffer, amount, uio);
   if (error != 0) {
	uprintf("Write failed.\n");
	return (error);
   }
   uprintf(lcd_socket->buffer);
   // send avr select command
   // set lcd_data
   outb(0x0072, AVR_SELECT_COMAND);
   // lcd_en up
   outb(0x0073, reg_up);
   // lcd_en down
   outb(0x0073, reg);
   // assert chip select
   // loop
   uprintf("lcd socket write %d bytes\n",amount-1);
   for (idx=0; idx<amount-1; idx++) {
	// set spi data = lcd_socket->buffer[idx];
	elan_mmcr->SSIXMIT = lcd_socket->buffer[idx];
	// send the byte - wait until not busy then start xmit
	while((elan_mmcr->SSISTA & SSI_BUSY) != 0);
	elan_mmcr->SSICMD = TX_START|RX_START;
   }
   // de-assert chip select
   return (error);
}

static int lcd_socket_read(struct cdev *dev, struct uio *uio, int ioflag)
{
   int error = 0;
   int amount;
   int idx;
   amount = MIN(uio->uio_resid,
			   (lcd_socket->length - uio->uio_offset > 0) ?
			   lcd_socket->length - uio->uio_offset : 0);
   error = uiomove(lcd_socket->buffer + uio->uio_offset, amount, uio);
   if (error != 0)
	uprintf("Read failed.\n");
   // send avr select datalog
   // set lcd_data
   outb(0x0072, AVR_SELECT_DATLOG);
   // lcd_en up
   outb(0x0073, reg_up);
   // lcd_en down
   outb(0x0073, reg);
   // assert chip select
   // set spi xmit = 0 - this is a read only
   elan_mmcr->SSIXMIT = 0;
   // loop
   uprintf("lcd socket read %d bytes\n",amount);
   for (idx=0; idx<lcd_socket->length; idx++) {
	// start the spi transaction
	elan_mmcr->SSICMD = TX_START|RX_START;
	// wait for spi done
	while((elan_mmcr->SSISTA & SSI_BUSY) != 0);
	lcd_socket->buffer[idx] = elan_mmcr->SSIRCV;
   }
   // de-assert chip select
    return (error);
}

static int lcd_socket_ioctl(struct cdev *dev,
							u_long cmd,
							caddr_t data,
							int fflag,
							struct thread *td)
{
   int error = 0;
   uint8_t lcd_data;
   switch (cmd) {
   case LCD_SOCKET_CMD:
	lcd_data = *(uint8_t *)data;
	// DO NOT ALLOW SETTING EITHER DATLOG OR COMAND POINTERS
	if ((lcd_data == AVR_SELECT_DATLOG) | (lcd_data == AVR_SELECT_COMAND)) {
	  return EINVAL;
	}
	break;
   default:
	error = ENOTTY;
	break;
   }
   return (error);
}

static int lcd_socket_modevent(module_t mod __unused, int event, void 
*arg __unused)
{
   int error = 0;
   switch (event) {
   case MOD_LOAD:
	// make sure the lcd port is in the dio mode
	reg = inb(0x007d);
	reg &= ~_BV(4);
	outb(0x007d, reg);
	// set the lcd data bits to ouput
	reg = inb(0x0073) | _BV(2) | _BV(3);
	outb(0x0073, reg);

	// the mmcr pointer is defined in the elan_mmcr driver
	// which is built into the kernel
	uprintf("elan mmcr %04x %02x\n",elan_mmcr->REVID, elan_mmcr->CPUCTL);
	elan_mmcr->SSICTL = PHS_INV | CLK_INV | MSBF_ENB;
	
	lcd_socket = malloc(sizeof(lcd_socket_t), M_LCD_SOCKET, M_WAITOK);
	lcd_socket->buffer_size = 256;
	lcd_socket->buffer = malloc(lcd_socket->buffer_size,
										M_LCD_SOCKET, M_WAITOK);
	lcd_socket_dev = make_dev(&lcd_socket_cdevsw, 0, UID_ROOT, GID_WHEEL,
							  0600, "lcd_socket");
	uprintf("Lcd_Socket driver loaded.\n");
	break;
   case MOD_UNLOAD:
	destroy_dev(lcd_socket_dev);
	free(lcd_socket->buffer, M_LCD_SOCKET);
	free(lcd_socket, M_LCD_SOCKET);
	uprintf("Lcd_Socket driver unloaded.\n");
	break;
   default:
	error = EOPNOTSUPP;
	break;
   }
   return (error);
}

DEV_MODULE(lcd_socket, lcd_socket_modevent, NULL);


More information about the freebsd-drivers mailing list