/****************************************************************************
*
* Copyright ( c ) 2016 PX4 Development Team . All rights reserved .
*
* 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. Neither the name PX4 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 COPYRIGHT HOLDERS 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
* COPYRIGHT OWNER 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 .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* @ file syslink_main . cpp
* Entry point for syslink module used to communicate with the NRF module on a Crazyflie
*/
# include <px4_platform_common/px4_config.h>
# include <px4_platform_common/tasks.h>
# include <px4_platform_common/posix.h>
# include <px4_platform_common/defines.h>
# include <unistd.h>
# include <stdio.h>
# include <poll.h>
# include <string.h>
# include <fcntl.h>
# include <sys/ioctl.h>
# include <termios.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <mqueue.h>
# include <drivers/drv_rc_input.h>
# include <drivers/drv_board_led.h>
# include <systemlib/err.h>
# include <board_config.h>
# include "crtp.h"
# include "syslink_main.h"
# include "drv_deck.h"
__BEGIN_DECLS
extern void led_init ( void ) ;
extern void led_on ( int led ) ;
extern void led_off ( int led ) ;
extern void led_toggle ( int led ) ;
__END_DECLS
extern " C " { __EXPORT int syslink_main ( int argc , char * argv [ ] ) ; }
Syslink * g_syslink = nullptr ;
Syslink : : Syslink ( ) :
pktrate ( 0 ) ,
nullrate ( 0 ) ,
rxrate ( 0 ) ,
txrate ( 0 ) ,
_syslink_task ( - 1 ) ,
_task_running ( false ) ,
_bootloader_mode ( false ) ,
_count ( 0 ) ,
_null_count ( 0 ) ,
_count_in ( 0 ) ,
_count_out ( 0 ) ,
_lasttime ( 0 ) ,
_lasttxtime ( 0 ) ,
_lastrxtime ( 0 ) ,
_fd ( 0 ) ,
_queue ( 2 , sizeof ( syslink_message_t ) ) ,
_writebuffer ( 16 , sizeof ( crtp_message_t ) ) ,
_rssi ( RC_INPUT_RSSI_MAX ) ,
_bstate ( BAT_DISCHARGING )
{
px4_sem_init ( & memory_sem , 0 , 0 ) ;
/* memory_sem use case is a signal */
px4_sem_setprotocol ( & memory_sem , SEM_PRIO_NONE ) ;
}
int
Syslink : : start ( )
{
_task_running = true ;
_syslink_task = px4_task_spawn_cmd (
" syslink " ,
SCHED_DEFAULT ,
SCHED_PRIORITY_DEFAULT ,
1500 ,
Syslink : : task_main_trampoline ,
NULL
) ;
return 0 ;
}
int
Syslink : : set_datarate ( uint8_t datarate )
{
syslink_message_t msg ;
msg . type = SYSLINK_RADIO_DATARATE ;
msg . length = 1 ;
msg . data [ 0 ] = datarate ;
return send_message ( & msg ) ;
}
int
Syslink : : set_channel ( uint8_t channel )
{
syslink_message_t msg ;
msg . type = SYSLINK_RADIO_CHANNEL ;
msg . length = 1 ;
msg . data [ 0 ] = channel ;
return send_message ( & msg ) ;
}
int
Syslink : : set_address ( uint64_t addr )
{
syslink_message_t msg ;
msg . type = SYSLINK_RADIO_ADDRESS ;
msg . length = 5 ;
memcpy ( & msg . data , & addr , 5 ) ;
return send_message ( & msg ) ;
}
int
Syslink : : send_queued_raw_message ( )
{
if ( _writebuffer . empty ( ) ) {
return 0 ;
}
_lasttxtime = hrt_absolute_time ( ) ;
syslink_message_t msg ;
msg . type = SYSLINK_RADIO_RAW ;
_count_out + + ;
_writebuffer . get ( & msg . length , sizeof ( crtp_message_t ) ) ;
return send_message ( & msg ) ;
}
void
Syslink : : update_params ( bool force_set )
{
param_t _param_radio_channel = param_find ( " SLNK_RADIO_CHAN " ) ;
param_t _param_radio_rate = param_find ( " SLNK_RADIO_RATE " ) ;
param_t _param_radio_addr1 = param_find ( " SLNK_RADIO_ADDR1 " ) ;
param_t _param_radio_addr2 = param_find ( " SLNK_RADIO_ADDR2 " ) ;
// reading parameter values into temp variables
int32_t channel , rate , addr1 , addr2 ;
uint64_t addr = 0 ;
param_get ( _param_radio_channel , & channel ) ;
param_get ( _param_radio_rate , & rate ) ;
param_get ( _param_radio_addr1 , & addr1 ) ;
param_get ( _param_radio_addr2 , & addr2 ) ;
memcpy ( & addr , & addr2 , 4 ) ;
memcpy ( ( ( char * ) & addr ) + 4 , & addr1 , 4 ) ;
hrt_abstime t = hrt_absolute_time ( ) ;
// request updates if needed
if ( channel ! = this - > _channel | | force_set ) {
this - > _channel = channel ;
set_channel ( channel ) ;
this - > _params_update [ 0 ] = t ;
this - > _params_ack [ 0 ] = 0 ;
}
if ( rate ! = this - > _rate | | force_set ) {
this - > _rate = rate ;
set_datarate ( rate ) ;
this - > _params_update [ 1 ] = t ;
this - > _params_ack [ 1 ] = 0 ;
}
if ( addr ! = this - > _addr | | force_set ) {
this - > _addr = addr ;
set_address ( addr ) ;
this - > _params_update [ 2 ] = t ;
this - > _params_ack [ 2 ] = 0 ;
}
}
// 1M 8N1 serial connection to NRF51
int
Syslink : : open_serial ( const char * dev )
{
# ifndef B1000000
# define B1000000 1000000
# endif
int rate = B1000000 ;
// open uart
int fd = px4_open ( dev , O_RDWR | O_NOCTTY ) ;
int termios_state = - 1 ;
if ( fd < 0 ) {
PX4_ERR ( " failed to open uart device! " ) ;
return - 1 ;
}
// set baud rate
struct termios config ;
tcgetattr ( fd , & config ) ;
// clear ONLCR flag (which appends a CR for every LF)
config . c_oflag = 0 ;
config . c_lflag & = ~ ( ECHO | ECHONL | ICANON | IEXTEN | ISIG ) ;
// Disable hardware flow control
config . c_cflag & = ~ CRTSCTS ;
/* Set baud rate */
if ( cfsetispeed ( & config , rate ) < 0 | | cfsetospeed ( & config , rate ) < 0 ) {
warnx ( " ERR SET BAUD %s: %d \n " , dev , termios_state ) ;
px4_close ( fd ) ;
return - 1 ;
}
if ( ( termios_state = tcsetattr ( fd , TCSANOW , & config ) ) < 0 ) {
PX4_WARN ( " ERR SET CONF %s \n " , dev ) ;
px4_close ( fd ) ;
return - 1 ;
}
return fd ;
}
int
Syslink : : task_main_trampoline ( int argc , char * argv [ ] )
{
g_syslink - > task_main ( ) ;
return 0 ;
}
void
Syslink : : task_main ( )
{
_bridge = new SyslinkBridge ( this ) ;
_bridge - > init ( ) ;
_memory = new SyslinkMemory ( this ) ;
_memory - > init ( ) ;
_battery . reset ( ) ;
// int ret;
/* Open serial port */
const char * dev = " /dev/ttyS2 " ;
_fd = open_serial ( dev ) ;
if ( _fd < 0 ) {
err ( 1 , " can't open %s " , dev ) ;
return ;
}
/* Set non-blocking */
/*
int flags = fcntl ( _fd , F_GETFL , 0 ) ;
fcntl ( _fd , F_SETFL , flags | O_NONBLOCK ) ;
*/
px4_arch_configgpio ( GPIO_NRF_TXEN ) ;
px4_pollfd_struct_t fds [ 2 ] ;
fds [ 0 ] . fd = _fd ;
fds [ 0 ] . events = POLLIN ;
_params_sub = orb_subscribe ( ORB_ID ( parameter_update ) ) ;
fds [ 1 ] . fd = _params_sub ;
fds [ 1 ] . events = POLLIN ;
int error_counter = 0 ;
char buf [ 64 ] ;
int nread ;
syslink_parse_state state ;
syslink_message_t msg ;
syslink_parse_init ( & state ) ;
//setup initial parameters
update_params ( true ) ;
while ( _task_running ) {
int poll_ret = px4_poll ( fds , 2 , 500 ) ;
/* handle the poll result */
if ( poll_ret = = 0 ) {
/* timeout: this means none of our providers is giving us data */
} else if ( poll_ret < 0 ) {
/* this is seriously bad - should be an emergency */
if ( error_counter < 10 | | error_counter % 50 = = 0 ) {
/* use a counter to prevent flooding (and slowing us down) */
PX4_ERR ( " [syslink] ERROR return value from poll(): %d "
, poll_ret ) ;
}
error_counter + + ;
} else {
if ( fds [ 0 ] . revents & POLLIN ) {
if ( ( nread = read ( _fd , buf , sizeof ( buf ) ) ) < 0 ) {
continue ;
}
for ( int i = 0 ; i < nread ; i + + ) {
if ( syslink_parse_char ( & state , buf [ i ] , & msg ) ) {
handle_message ( & msg ) ;
}
}
}
if ( fds [ 1 ] . revents & POLLIN ) {
parameter_update_s update ;
orb_copy ( ORB_ID ( parameter_update ) , _params_sub , & update ) ;
update_params ( false ) ;
}
}
}
close ( _fd ) ;
}
void
Syslink : : handle_message ( syslink_message_t * msg )
{
hrt_abstime t = hrt_absolute_time ( ) ;
if ( t - _lasttime > 1000000 ) {
pktrate = _count ;
rxrate = _count_in ;
txrate = _count_out ;
nullrate = _null_count ;
_lasttime = t ;
_count = 0 ;
_null_count = 0 ;
_count_in = 0 ;
_count_out = 0 ;
}
_count + + ;
if ( msg - > type = = SYSLINK_PM_ONOFF_SWITCHOFF ) {
// When the power button is hit
} else if ( msg - > type = = SYSLINK_PM_BATTERY_STATE ) {
if ( msg - > length ! = 9 ) {
return ;
}
uint8_t flags = msg - > data [ 0 ] ;
int charging = flags & 1 ;
int powered = flags & 2 ;
float vbat ; //, iset;
memcpy ( & vbat , & msg - > data [ 1 ] , sizeof ( float ) ) ;
//memcpy(&iset, &msg->data[5], sizeof(float));
_battery . updateBatteryStatus ( t , vbat , - 1 , true ,
battery_status_s : : BATTERY_SOURCE_POWER_MODULE , 0 , 0 ) ;
// Update battery charge state
if ( charging ) {
_bstate = BAT_CHARGING ;
}
/* With the usb plugged in and battery disconnected, it appears to be charged. The voltage check ensures that a battery is connected */
else if ( powered & & ! charging & & vbat > 3.7f ) {
_bstate = BAT_CHARGED ;
} else {
_bstate = BAT_DISCHARGING ;
}
} else if ( msg - > type = = SYSLINK_RADIO_RSSI ) {
uint8_t rssi = msg - > data [ 0 ] ; // Between 40 and 100 meaning -40 dBm to -100 dBm
_rssi = 140 - rssi * 100 / ( 100 - 40 ) ;
} else if ( msg - > type = = SYSLINK_RADIO_RAW ) {
handle_raw ( msg ) ;
_lastrxtime = t ;
} else if ( ( msg - > type & SYSLINK_GROUP ) = = SYSLINK_RADIO ) {
handle_radio ( msg ) ;
} else if ( ( msg - > type & SYSLINK_GROUP ) = = SYSLINK_OW ) {
memcpy ( & _memory - > msgbuf , msg , sizeof ( syslink_message_t ) ) ;
px4_sem_post ( & memory_sem ) ;
} else {
PX4_INFO ( " GOT %d " , msg - > type ) ;
}
//Send queued messages
if ( ! _queue . empty ( ) ) {
_queue . get ( msg , sizeof ( syslink_message_t ) ) ;
send_message ( msg ) ;
}
float p = ( t % 500000 ) / 500000.0f ;
/* Use LED_GREEN for charging indicator */
if ( _bstate = = BAT_CHARGED ) {
led_on ( LED_GREEN ) ;
} else if ( _bstate = = BAT_CHARGING & & p < 0.25f ) {
led_on ( LED_GREEN ) ;
} else {
led_off ( LED_GREEN ) ;
}
/* Alternate RX/TX LEDS when transfering */
bool rx = t - _lastrxtime < 200000 ,
tx = t - _lasttxtime < 200000 ;
if ( rx & & p < 0.25f ) {
led_on ( LED_RX ) ;
} else {
led_off ( LED_RX ) ;
}
if ( tx & & p > 0.5f & & p > 0.75f ) {
led_on ( LED_TX ) ;
} else {
led_off ( LED_TX ) ;
}
// resend parameters if they haven't been acknowledged
if ( _params_ack [ 0 ] = = 0 & & t - _params_update [ 0 ] > 10000 ) {
set_channel ( _channel ) ;
} else if ( _params_ack [ 1 ] = = 0 & & t - _params_update [ 1 ] > 10000 ) {
set_datarate ( _rate ) ;
} else if ( _params_ack [ 2 ] = = 0 & & t - _params_update [ 2 ] > 10000 ) {
set_address ( _addr ) ;
}
}
void
Syslink : : handle_radio ( syslink_message_t * sys )
{
hrt_abstime t = hrt_absolute_time ( ) ;
// record acknowlegements to parameter messages
if ( sys - > type = = SYSLINK_RADIO_CHANNEL ) {
_params_ack [ 0 ] = t ;
} else if ( sys - > type = = SYSLINK_RADIO_DATARATE ) {
_params_ack [ 1 ] = t ;
} else if ( sys - > type = = SYSLINK_RADIO_ADDRESS ) {
_params_ack [ 2 ] = t ;
}
}
void
Syslink : : handle_raw ( syslink_message_t * sys )
{
crtp_message_t * c = ( crtp_message_t * ) & sys - > length ;
if ( CRTP_NULL ( * c ) ) {
if ( c - > size > = 3 ) {
handle_bootloader ( sys ) ;
}
_null_count + + ;
} else if ( c - > port = = CRTP_PORT_COMMANDER ) {
crtp_commander * cmd = ( crtp_commander * ) & c - > data [ 0 ] ;
input_rc_s rc = { } ;
rc . timestamp = hrt_absolute_time ( ) ;
rc . timestamp_last_signal = rc . timestamp ;
rc . channel_count = 5 ;
rc . rc_failsafe = false ;
rc . rc_lost = false ;
rc . rc_lost_frame_count = 0 ;
rc . rc_total_frame_count = 1 ;
rc . rc_ppm_frame_length = 0 ;
rc . input_source = input_rc_s : : RC_INPUT_SOURCE_MAVLINK ;
rc . rssi = _rssi ;
double pitch = cmd - > pitch , roll = cmd - > roll , yaw = cmd - > yaw ;
/* channels (scaled to rc limits) */
rc . values [ 0 ] = pitch * 500 / 20 + 1500 ;
rc . values [ 1 ] = roll * 500 / 20 + 1500 ;
rc . values [ 2 ] = yaw * 500 / 150 + 1500 ;
rc . values [ 3 ] = cmd - > thrust * 1000 / USHRT_MAX + 1000 ;
rc . values [ 4 ] = 1000 ; // Dummy channel as px4 needs at least 5
_rc_pub . publish ( rc ) ;
} else if ( c - > port = = CRTP_PORT_MAVLINK ) {
_count_in + + ;
/* Pipe to Mavlink bridge */
_bridge - > pipe_message ( c ) ;
} else {
handle_raw_other ( sys ) ;
}
// Block all non-requested messaged in bootloader mode
if ( _bootloader_mode ) {
return ;
}
// Allow one raw message to be sent from the queue
send_queued_raw_message ( ) ;
}
void
Syslink : : handle_bootloader ( syslink_message_t * sys )
{
// Minimal bootloader emulation for being detectable
// To the bitcraze utilities, the STM32 will appear to have no flashable pages
// Upon receiving a bootloader message, all outbound packets are blocked in 'bootloader mode' due to how fragile the aforementioned utilities are to extra packets
crtp_message_t * c = ( crtp_message_t * ) & sys - > length ;
uint8_t target = c - > data [ 0 ] ;
uint8_t cmd = c - > data [ 1 ] ;
if ( target ! = 0xFF ) { // CF2 STM32 target
return ;
}
_bootloader_mode = true ;
if ( cmd = = 0x10 ) { // GET_INFO
c - > size = 1 + 23 ;
memset ( & c - > data [ 2 ] , 0 , 21 ) ;
c - > data [ 22 ] = 0x10 ; // Protocol version
send_message ( sys ) ;
}
}
void
Syslink : : handle_raw_other ( syslink_message_t * sys )
{
// This function doesn't actually do anything
// It is just here to return null responses to most standard messages
crtp_message_t * c = ( crtp_message_t * ) & sys - > length ;
if ( c - > port = = CRTP_PORT_LOG ) {
PX4_INFO ( " Log: %d %d " , c - > channel , c - > data [ 0 ] ) ;
if ( c - > channel = = 0 ) { // Table of Contents Access
uint8_t cmd = c - > data [ 0 ] ;
if ( cmd = = 0 ) { // GET_ITEM
//int id = c->data[1];
memset ( & c - > data [ 2 ] , 0 , 3 ) ;
c - > data [ 2 ] = 1 ; // type
c - > size = 1 + 5 ;
send_message ( sys ) ;
} else if ( cmd = = 1 ) { // GET_INFO
memset ( & c - > data [ 1 ] , 0 , 7 ) ;
c - > size = 1 + 8 ;
send_message ( sys ) ;
}
} else if ( c - > channel = = 1 ) { // Log control
uint8_t cmd = c - > data [ 0 ] ;
PX4_INFO ( " Responding to cmd: %d " , cmd ) ;
c - > data [ 2 ] = 0 ; // Success
c - > size = 3 + 1 ;
// resend message
send_message ( sys ) ;
} else if ( c - > channel = = 2 ) { // Log data
}
} else if ( c - > port = = CRTP_PORT_MEM ) {
if ( c - > channel = = 0 ) { // Info
int cmd = c - > data [ 0 ] ;
if ( cmd = = 1 ) { // GET_NBR_OF_MEMS
c - > data [ 1 ] = 0 ;
c - > size = 2 + 1 ;
// resend message
send_message ( sys ) ;
}
}
} else if ( c - > port = = CRTP_PORT_PARAM ) {
if ( c - > channel = = 0 ) { // TOC Access
// uint8_t msgId = c->data[0];
c - > data [ 1 ] = 0 ; // Last parameter (id = 0)
memset ( & c - > data [ 2 ] , 0 , 10 ) ;
c - > size = 1 + 8 ;
send_message ( sys ) ;
}
else if ( c - > channel = = 1 ) { // Param read
// 0 is ok
c - > data [ 1 ] = 0 ; // value
c - > size = 1 + 2 ;
send_message ( sys ) ;
}
} else {
PX4_INFO ( " Got raw: %d " , c - > port ) ;
}
}
int
Syslink : : send_bytes ( const void * data , size_t len )
{
// TODO: This could be way more efficient
// Using interrupts/DMA/polling would be much better
for ( size_t i = 0 ; i < len ; i + + ) {
// Block until we can send a byte
while ( px4_arch_gpioread ( GPIO_NRF_TXEN ) ) ;
write ( _fd , ( ( const char * ) data ) + i , 1 ) ;
}
return 0 ;
}
int
Syslink : : send_message ( syslink_message_t * msg )
{
syslink_compute_cksum ( msg ) ;
send_bytes ( syslink_magic , 2 ) ;
send_bytes ( & msg - > type , sizeof ( msg - > type ) ) ;
send_bytes ( & msg - > length , sizeof ( msg - > length ) ) ;
send_bytes ( & msg - > data , msg - > length ) ;
send_bytes ( & msg - > cksum , sizeof ( msg - > cksum ) ) ;
return 0 ;
}
namespace syslink
{
void usage ( ) ;
void start ( ) ;
void status ( ) ;
void test ( ) ;
void attached ( int pid ) ;
void usage ( )
{
warnx ( " missing command: try 'start', 'status', 'attached', 'test' " ) ;
}
void start ( )
{
if ( g_syslink ! = nullptr ) {
printf ( " Already started \n " ) ;
exit ( 1 ) ;
}
g_syslink = new Syslink ( ) ;
g_syslink - > start ( ) ;
// Wait for task and bridge to start
usleep ( 5000 ) ;
warnx ( " Started syslink on /dev/ttyS2 \n " ) ;
exit ( 0 ) ;
}
void status ( )
{
if ( g_syslink = = nullptr ) {
printf ( " Please start syslink first \n " ) ;
exit ( 1 ) ;
}
printf ( " Connection activity: \n " ) ;
printf ( " - total rx: %d p/s \n " , g_syslink - > pktrate ) ;
printf ( " - radio rx: %d p/s (%d null) \n " , g_syslink - > rxrate , g_syslink - > nullrate ) ;
printf ( " - radio tx: %d p/s \n \n " , g_syslink - > txrate ) ;
printf ( " Parameter Status: \n " ) ;
const char * goodParam = " good " ;
const char * badParam = " fail! " ;
printf ( " - channel: %s \n " , g_syslink - > is_good ( 0 ) ? goodParam : badParam ) ;
printf ( " - data rate: %s \n " , g_syslink - > is_good ( 1 ) ! = 0 ? goodParam : badParam ) ;
printf ( " - address: %s \n \n " , g_syslink - > is_good ( 2 ) ! = 0 ? goodParam : badParam ) ;
int deckfd = open ( DECK_DEVICE_PATH , O_RDONLY ) ;
int ndecks = 0 ; ioctl ( deckfd , DECKIOGNUM , ( unsigned long ) & ndecks ) ;
printf ( " Deck scan: (found %d) \n " , ndecks ) ;
for ( int i = 0 ; i < ndecks ; i + + ) {
ioctl ( deckfd , DECKIOSNUM , ( unsigned long ) & i ) ;
uint8_t * id ;
int idlen = ioctl ( deckfd , DECKIOID , ( unsigned long ) & id ) ;
// TODO: Validate the ID
printf ( " %i: ROM ID: " , i ) ;
for ( int idi = 0 ; idi < idlen ; idi + + ) {
printf ( " %02X " , id [ idi ] ) ;
}
deck_descriptor_t desc ;
read ( deckfd , & desc , sizeof ( desc ) ) ;
printf ( " , VID: %02X , PID: %02X \n " , desc . header , desc . vendorId , desc . productId ) ;
// Print pages of memory
for ( size_t di = 0 ; di < sizeof ( desc ) ; di + + ) {
if ( di % 16 = = 0 ) {
printf ( " \n " ) ;
}
printf ( " %02X " , ( ( uint8_t * ) & desc ) [ di ] ) ;
}
printf ( " \n \n " ) ;
}
close ( deckfd ) ;
exit ( 0 ) ;
}
void attached ( int pid )
{
bool found = false ;
int deckfd = open ( DECK_DEVICE_PATH , O_RDONLY ) ;
int ndecks = 0 ; ioctl ( deckfd , DECKIOGNUM , ( unsigned long ) & ndecks ) ;
for ( int i = 0 ; i < ndecks ; i + + ) {
ioctl ( deckfd , DECKIOSNUM , ( unsigned long ) & i ) ;
deck_descriptor_t desc ;
read ( deckfd , & desc , sizeof ( desc ) ) ;
if ( desc . productId = = pid ) {
found = true ;
break ;
}
}
close ( deckfd ) ;
exit ( found ? 1 : 0 ) ;
}
void test ( )
{
// TODO: Ensure battery messages are recent
// TODO: Read and write from memory to ensure it is working
}
} // namespace syslink
int syslink_main ( int argc , char * argv [ ] )
{
if ( argc < 2 ) {
syslink : : usage ( ) ;
exit ( 1 ) ;
}
const char * verb = argv [ 1 ] ;
if ( ! strcmp ( verb , " start " ) ) {
syslink : : start ( ) ;
}
if ( ! strcmp ( verb , " status " ) ) {
syslink : : status ( ) ;
}
if ( ! strcmp ( verb , " attached " ) ) {
if ( argc ! = 3 ) {
errx ( 1 , " usage: syslink attached [deck_pid] " ) ;
}
int pid = atoi ( argv [ 2 ] ) ;
syslink : : attached ( pid ) ;
}
if ( ! strcmp ( verb , " test " ) ) {
syslink : : test ( ) ;
}
syslink : : usage ( ) ;
exit ( 1 ) ;
return 0 ;
}