C Linux

From Cricut Hacking Wiki
Jump to: navigation, search

Code examples

Current alpha version of Linux sources on SourceForge at http://licut.sourceforge.net

This Linux-specific code snippet opens /dev/ttyUSB0 and sets the rate to 200kbps and returns a handle for use by read() and write():

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <linux/serial.h>

int open_port_200kbps()
{
	int handle = open( "/dev/ttyUSB0", O_RDWR | O_NOCTTY );
	if (handle <= 0)
	{
		printf( "Failed to open %s - %d (%s)\n", "/dev/ttyUSB0", errno, strerror(errno) );
		return -1;
	}

        struct termios oldtio,newtio;
        
        tcgetattr( handle, &oldtio ); /* save current port settings */
    
        bzero( &newtio, sizeof(newtio) );
	// Set custom rate to 200kbps 8N1 - we're actually sending 8N2 but get 8N1 back
        newtio.c_cflag = B38400 | /*CRTSCTS |*/ CS8 | CLOCAL | CREAD;
        newtio.c_iflag = IGNPAR;
        newtio.c_oflag = 0;
        
        /* set input mode (non-canonical, no echo,...) */
        newtio.c_lflag = 0;
         
        newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
        newtio.c_cc[VMIN]     = 5;   /* blocking read until 5 chars received */
        
        tcflush( handle, TCIFLUSH );
        tcsetattr( handle, TCSANOW, &newtio );

	// Now use TIOCSSERIAL ioctl to set custom divisor
	// FTDI uses base_baud 24000000 so in theory a divisor
	// of 120 should give us 200000 baud...
	struct serial_struct sio; // From /usr/include/linux/serial.h
	int ioctl_res = ioctl( handle, TIOCGSERIAL, &sio );
	if (ioctl_res < 0)
	{
		printf( "Failed TIOCGSERIAL ioctl: error %d (%s)\n", errno, strerror(errno) );
		close( handle );
		return -1;
	}

	sio.flags = ((sio.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST);
	sio.custom_divisor = sio.baud_base / 200000;
	ioctl_res = ioctl( handle, TIOCSSERIAL, &sio );
	if (ioctl_res < 0)
	{
		printf( "Failed TIOCSSERIAL ioctl: error %d (%s)\n", errno, strerror(errno) );
		close( handle );
		return -1;
	}
        return handle;
}

lio.Send is a simple function that adds an intercharacter delay of 1ms (running on Ubuntu 9.04):

int LicutIO::Send(  const unsigned char *bytes, int length )
{
	int n;
	int actual_sent = 0;
	for (n = 0; n < length; n++)
	{
		int write_res = write( m_handle, &bytes[n], 1 );
		if (write_res < 1)
		{
			printf( "%s(%p,%u) - write returned %d, errno=%d (%s)\n", __FUNCTION__, bytes, length, write_res, errno, strerror(errno) );
		}
		else
		{
			actual_sent++;
		}
		// Add intercharacter delay after each character, including the last
		usleep( 1000 );
	}
	return actual_sent;
}

After reading the results of each status command, I'm forcing an additional 250ms timeout.