blob: 8e93c5b8b83b740766ece61906ade9fcb54c3065 [file] [log] [blame]
/**
* \file
*
* \brief Sample of IEE11073 Communication Model
*
* Copyright (C) 2009 Atmel Corporation. All rights reserved.
*
* \page License
*
* 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. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel AVR product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
*/
#include "compiler.h"
#include "conf_usb.h"
#include "udi_phdc.h"
#include "communication_model.h"
//! @defgroup IEEE11073_COM_MODEL_PHDC Structure used by Communication Model
//! to connect a USB PHDC Device
//! @{
#define IEEE11073_LGT_OPAQUEDATA_IN 10
#define IEEE11073_LGT_OPAQUEDATA_OUT 10
#define IEEE11073_LGT_METADATA_IN 80
#define IEEE11073_LGT_METADATA_OUT 80
udi_phdc_metadata_t ieee11073_g_phdc_metadata_in;
udi_phdc_metadata_t ieee11073_g_phdc_metadata_out;
uint8_t ieee11073_g_opaquedata_in[IEEE11073_LGT_OPAQUEDATA_IN];
uint8_t ieee11073_g_opaquedata_out[IEEE11073_LGT_OPAQUEDATA_OUT];
uint8_t ieee11073_g_metadata_in[IEEE11073_LGT_METADATA_IN];
uint8_t ieee11073_g_metadata_out[IEEE11073_LGT_METADATA_OUT];
//! @}
//! @defgroup IEEE11073_COM_MODEL_STATE Communication model processus states
//! @{
#define IEEE11073_STATE_DISABLE 0
#define IEEE11073_STATE_WAIT_ASSOC 1
#define IEEE11073_STATE_RUNNING 2
uint8_t ieee11073_g_state = IEEE11073_STATE_DISABLE;
//! @}
//! @defgroup IEEE11073_COM_MODEL_EVENT Internal events
//! for communication model processus
//! @{
#define IEEE11073_EVENT_NONE 0
#define IEEE11073_EVENT_ENABLE 1
#define IEEE11073_EVENT_RECEIVED 3
#define IEEE11073_EVENT_RETRY_RECEP 4
#define IEEE11073_EVENT_SENDING 5
#define IEEE11073_EVENT_SEND_ABORT 6
#define IEEE11073_EVENT_MEASURE_1 7
#define IEEE11073_EVENT_MEASURE_2 8
uint8_t ieee11073_g_event;
//! @}
//! @defgroup PHD_DATA Array with IEEE11073 data
//! @{
//! Association Data Request
uint8_t phd_assoc_req[] = {
0xE2, 0x00, 0x00, 0x32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2A,
0x50, 0x79,
0x00, 0x26, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x08,
0x4C, 0x4E, 0x49, 0x41, 0x47, 0x45, 0x4E, 0x54, 0x40, 0x00,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
};
//! Sample of measure
uint8_t phd_measure[] = {
0xE7, 0x00, 0x00, 0x5A, 0x00, 0x58, 0x12, 0x36, 0x01, 0x01, 0x00, 0x52,
0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0D, 0x1D, 0x00, 0x48, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x04,
0x00, 0x40, 0x00, 0x01, 0x00, 0x0C, 0xFF, 0x00, 0x02, 0xFA, 0x20, 0x07,
0x12, 0x06,
0x12, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0C, 0xFF, 0x00, 0x00, 0xF3,
0x20, 0x09, 0x06, 0x12,
0x12, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0C, 0xFF, 0x00, 0x02, 0xF8,
0x20, 0x09, 0x06, 0x12,
0x20, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0C, 0xFF, 0x00, 0x00, 0xF2,
0x20, 0x09, 0x06, 0x12,
0x20, 0x05, 0x00, 0x00
};
//! Device Attributes
uint8_t phd_attr[] = {
0xE7, 0x00, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x02, 0x02, 0x03, 0x00, 0x66,
0x00, 0x00, 0x00, 0x06,
0x00, 0x60, 0x0A, 0x5A, 0x00, 0x08, 0x00, 0x01, 0x00, 0x04, 0x10, 0x0F,
0x00, 0x01, 0x09, 0x28,
0x00, 0x1A, 0x00, 0x0A, 0x46, 0x72, 0x65, 0x65, 0x73, 0x63, 0x61, 0x6C,
0x65, 0x20, 0x00, 0x0C, 0x4D, 0x65,
0x64, 0x69, 0x63, 0x61, 0x6C, 0x20, 0x43, 0x46, 0x56, 0x31, 0x09, 0x84,
0x00, 0x0A,
0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x0a, 0x44,
0x00, 0x02, 0x40, 0x00,
0x09, 0x2D, 0x00, 0x12, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00,
0x00, 0x08, 0x44, 0x45,
0x31, 0x32, 0x34, 0x35, 0x36, 0x37, 0x09, 0x87, 0x00, 0x08, 0x20, 0x09,
0x06, 0x12,
0x12, 0x05, 0x00, 0x00
};
//! @}
//! Internal routines
static bool ieee11073_enable_reception(void);
static bool ieee11073_decode_metadata(void);
void ieee11073_received(uint16_t nb_received);
void ieee11073_sent(uint16_t nb_send);
/*! \brief Enable the communication model processus
*
* \retval TRUE
*/
bool ieee11073_com_model_enable(void)
{
ieee11073_g_event = IEEE11073_EVENT_ENABLE;
return TRUE;
}
/*! \brief Disable the communication model processus
*/
void ieee11073_com_model_disable(void)
{
ieee11073_g_event = IEEE11073_EVENT_NONE;
ieee11073_g_state = IEEE11073_STATE_DISABLE;
}
/*! \brief Send a measure on communication line
*/
void ieee11073_com_model_send_measure_1(void)
{
if (IEEE11073_EVENT_NONE != ieee11073_g_event)
return;
ieee11073_g_event = IEEE11073_EVENT_MEASURE_1;
}
/*! \brief Send a measure on communication line
*/
void ieee11073_com_model_send_measure_2(void)
{
if (IEEE11073_EVENT_NONE != ieee11073_g_event)
return;
ieee11073_g_event = IEEE11073_EVENT_MEASURE_2;
}
/*! \brief Callback called when a data is received
*
* \param nb_received Number of data received
*/
void ieee11073_received(uint16_t nb_received)
{
ieee11073_g_phdc_metadata_out.metadata_size = nb_received;
ieee11073_g_event = IEEE11073_EVENT_RECEIVED;
}
/*! \brief Callback called when a data is sent
*
* \param nb_send Number of data sent
*/
void ieee11073_sent(uint16_t nb_send)
{
if (0 == nb_send) {
ieee11073_g_event = IEEE11073_EVENT_SEND_ABORT;
return;
}
ieee11073_g_phdc_metadata_in.metadata_size = nb_send;
ieee11073_g_event = IEEE11073_EVENT_SENDING;
}
/*! \brief Function to process communication model
* Must be scheduled by other process
*/
bool ieee11073_com_model_process(void)
{
if (IEEE11073_EVENT_NONE == ieee11073_g_event)
goto ieee11073_com_model_process_end;
if (IEEE11073_EVENT_RETRY_RECEP == ieee11073_g_event) {
ieee11073_g_event = IEEE11073_EVENT_NONE;
ieee11073_enable_reception();
goto ieee11073_com_model_process_end;
}
switch (ieee11073_g_state) {
case IEEE11073_STATE_DISABLE:
if (IEEE11073_EVENT_ENABLE != ieee11073_g_event)
break;
ieee11073_g_phdc_metadata_in.qos = USB_PHDC_QOS_MEDIUM_BEST;
ieee11073_g_phdc_metadata_in.opaque_size = 0;
ieee11073_g_phdc_metadata_in.metadata = phd_assoc_req;
ieee11073_g_phdc_metadata_in.metadata_size =
sizeof(phd_assoc_req);
if (!udi_phdc_senddata(&ieee11073_g_phdc_metadata_in,
ieee11073_sent)) {
break;
}
ieee11073_g_state = IEEE11073_STATE_WAIT_ASSOC;
break;
case IEEE11073_STATE_WAIT_ASSOC:
switch (ieee11073_g_event) {
case IEEE11073_EVENT_SEND_ABORT:
// Descriptor abort then resend it
ieee11073_g_phdc_metadata_in.qos =
USB_PHDC_QOS_MEDIUM_BEST;
ieee11073_g_phdc_metadata_in.opaque_size = 0;
ieee11073_g_phdc_metadata_in.metadata = phd_assoc_req;
ieee11073_g_phdc_metadata_in.metadata_size =
sizeof(phd_assoc_req);
if (!udi_phdc_senddata(&ieee11073_g_phdc_metadata_in,
ieee11073_sent)) {
break;
}
break;
case IEEE11073_EVENT_SENDING:
// Descriptor sending, OK
// Now wait for association to be accepted
if (!ieee11073_enable_reception())
goto ieee11073_com_model_process_end;
break;
case IEEE11073_EVENT_RECEIVED:
ieee11073_decode_metadata();
if (!ieee11073_enable_reception())
goto ieee11073_com_model_process_end;
break;
}
break;
case IEEE11073_STATE_RUNNING:
switch (ieee11073_g_event) {
case IEEE11073_EVENT_RECEIVED:
ieee11073_decode_metadata();
// Restart recption
if (!ieee11073_enable_reception())
goto ieee11073_com_model_process_end;
break;
case IEEE11073_EVENT_MEASURE_1:
case IEEE11073_EVENT_MEASURE_2:
ieee11073_g_phdc_metadata_in.opaque_size = 0;
ieee11073_g_phdc_metadata_in.metadata = phd_measure;
ieee11073_g_phdc_metadata_in.metadata_size =
sizeof(phd_measure);
if (IEEE11073_EVENT_MEASURE_1 == ieee11073_g_event)
ieee11073_g_phdc_metadata_in.qos =
USB_PHDC_QOS_LOW_GOOD;
else
ieee11073_g_phdc_metadata_in.qos =
USB_PHDC_QOS_MEDIUM_BETTER;
if (!udi_phdc_senddata(&ieee11073_g_phdc_metadata_in,
ieee11073_sent))
break;
}
}
ieee11073_g_event = IEEE11073_EVENT_NONE;
ieee11073_com_model_process_end:
return (ieee11073_g_state == IEEE11073_STATE_RUNNING);
}
/*! \brief Start the reception of data on communication line
*
* \retval TRUE Reception started
* \retval FALSE Impossible to start reception
*/
static bool ieee11073_enable_reception(void)
{
ieee11073_g_phdc_metadata_out.opaquedata = ieee11073_g_opaquedata_out;
ieee11073_g_phdc_metadata_out.opaque_size =
IEEE11073_LGT_OPAQUEDATA_OUT;
ieee11073_g_phdc_metadata_out.metadata = ieee11073_g_metadata_out;
ieee11073_g_phdc_metadata_out.metadata_size =
IEEE11073_LGT_METADATA_OUT;
if (!udi_phdc_waitdata(&ieee11073_g_phdc_metadata_out,
ieee11073_received)) {
ieee11073_g_event = IEEE11073_EVENT_RETRY_RECEP;
return FALSE;
}
return TRUE;
}
/*! \brief Start the data reception on communication line
*
* \retval TRUE Metadata correct
* \retval FALSE Metadata unknow
*/
static bool ieee11073_decode_metadata(void)
{
typedef struct {
uint16_t choice;
uint16_t length;
uint8_t values[1];
} Phd_t;
Phd_t *phd = (Phd_t *) ieee11073_g_phdc_metadata_out.metadata;
if (0 == ieee11073_g_phdc_metadata_out.metadata_size)
return FALSE; // Error reception
if (ieee11073_g_phdc_metadata_out.metadata_size !=
(4 + be16_to_cpu(phd->length)))
return FALSE; // Error on length
switch (be16_to_cpu(phd->choice)) {
case 0xE300:
if (0 == be16_to_cpu(*((uint16_t *) & phd->values[0]))) {
ieee11073_g_state = IEEE11073_STATE_RUNNING;
return TRUE;
}
break;
case 0xE700:
{
uint16_t invoke_id;
uint16_t choice;
if (ieee11073_g_phdc_metadata_out.metadata_size !=
(6 + be16_to_cpu(*((uint16_t *) & phd->values[0]))))
return FALSE; // Error on length
invoke_id = *((uint16_t *) & phd->values[2]);
choice = *((uint16_t *) & phd->values[4]);
if (be16_to_cpu(choice) == 0x0103) {
invoke_id = be16_to_cpu(invoke_id);
phd_attr[6] = (uint8_t) (invoke_id >> 8);
phd_attr[7] = (uint8_t) (invoke_id & 0xFF);
ieee11073_g_phdc_metadata_in.qos =
USB_PHDC_QOS_MEDIUM_BEST;
ieee11073_g_phdc_metadata_in.opaque_size = 0;
ieee11073_g_phdc_metadata_in.metadata =
phd_attr;
ieee11073_g_phdc_metadata_in.metadata_size =
sizeof(phd_attr);
if (!udi_phdc_senddata
(&ieee11073_g_phdc_metadata_in,
ieee11073_sent))
{
break;
}
return TRUE;
}
}
break;
}
return FALSE;
}