icE1usb fw: Expose the GPS NMEA data as a CDC ACM
Note that this is read-only. We drop all data from the host because we can't have the host reconfigure the module ... Signed-off-by: Sylvain Munaut <tnt@246tNt.com> Change-Id: Ieb6a653ece882c5f90ab27da1bca04c94184dc5a
This commit is contained in:
parent
ef5fe385fe
commit
70c10f05cf
|
@ -54,6 +54,7 @@ HEADERS_app=\
|
||||||
usb_desc_ids.h \
|
usb_desc_ids.h \
|
||||||
usb_dev.h \
|
usb_dev.h \
|
||||||
usb_e1.h \
|
usb_e1.h \
|
||||||
|
usb_gps.h \
|
||||||
usb_str_app.gen.h \
|
usb_str_app.gen.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
@ -65,6 +66,7 @@ SOURCES_app=\
|
||||||
usb_desc_app.c \
|
usb_desc_app.c \
|
||||||
usb_dev.c \
|
usb_dev.c \
|
||||||
usb_e1.c \
|
usb_e1.c \
|
||||||
|
usb_gps.c \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "spi.h"
|
#include "spi.h"
|
||||||
#include "usb_dev.h"
|
#include "usb_dev.h"
|
||||||
#include "usb_e1.h"
|
#include "usb_e1.h"
|
||||||
|
#include "usb_gps.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,6 +106,7 @@ void main()
|
||||||
usb_dev_init();
|
usb_dev_init();
|
||||||
usb_dfu_rt_init();
|
usb_dfu_rt_init();
|
||||||
usb_e1_init();
|
usb_e1_init();
|
||||||
|
usb_gps_init();
|
||||||
|
|
||||||
/* Start */
|
/* Start */
|
||||||
led_state(true);
|
led_state(true);
|
||||||
|
@ -168,5 +170,6 @@ void main()
|
||||||
|
|
||||||
/* GPS poll */
|
/* GPS poll */
|
||||||
gps_poll();
|
gps_poll();
|
||||||
|
usb_gps_poll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "gps.h"
|
#include "gps.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "usb_gps.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
@ -217,6 +218,9 @@ gps_poll(void)
|
||||||
|
|
||||||
/* If we do, process it locally to update our state */
|
/* If we do, process it locally to update our state */
|
||||||
_gps_parse_nmea(nmea);
|
_gps_parse_nmea(nmea);
|
||||||
|
|
||||||
|
/* And queue it for USB */
|
||||||
|
usb_gps_puts(nmea);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -42,7 +42,6 @@ static const struct {
|
||||||
} __attribute__ ((packed)) e1[2];
|
} __attribute__ ((packed)) e1[2];
|
||||||
|
|
||||||
/* CDC */
|
/* CDC */
|
||||||
#if 0
|
|
||||||
struct {
|
struct {
|
||||||
struct usb_intf_desc intf_ctl;
|
struct usb_intf_desc intf_ctl;
|
||||||
struct usb_cdc_hdr_desc cdc_hdr;
|
struct usb_cdc_hdr_desc cdc_hdr;
|
||||||
|
@ -53,7 +52,6 @@ static const struct {
|
||||||
struct usb_ep_desc ep_data_out;
|
struct usb_ep_desc ep_data_out;
|
||||||
struct usb_ep_desc ep_data_in;
|
struct usb_ep_desc ep_data_in;
|
||||||
} __attribute__ ((packed)) cdc;
|
} __attribute__ ((packed)) cdc;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* DFU Runtime */
|
/* DFU Runtime */
|
||||||
struct {
|
struct {
|
||||||
|
@ -207,7 +205,6 @@ static const struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
#if 0
|
|
||||||
.cdc = {
|
.cdc = {
|
||||||
.intf_ctl = {
|
.intf_ctl = {
|
||||||
.bLength = sizeof(struct usb_intf_desc),
|
.bLength = sizeof(struct usb_intf_desc),
|
||||||
|
@ -230,21 +227,24 @@ static const struct {
|
||||||
.bLength = sizeof(struct usb_cdc_acm_desc),
|
.bLength = sizeof(struct usb_cdc_acm_desc),
|
||||||
.bDescriptorType = USB_CS_DT_INTF,
|
.bDescriptorType = USB_CS_DT_INTF,
|
||||||
.bDescriptorsubtype = USB_CDC_DST_ACM,
|
.bDescriptorsubtype = USB_CDC_DST_ACM,
|
||||||
|
/* Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, */
|
||||||
|
/* and the notification Serial_State */
|
||||||
.bmCapabilities = 0x02,
|
.bmCapabilities = 0x02,
|
||||||
},
|
},
|
||||||
.cdc_union = {
|
.cdc_union = {
|
||||||
.bLength = sizeof(struct usb_cdc_union_desc) + 1,
|
.bLength = sizeof(struct usb_cdc_union_desc) + 1,
|
||||||
.bDescriptorType = USB_CS_DT_INTF,
|
.bDescriptorType = USB_CS_DT_INTF,
|
||||||
.bDescriptorsubtype = USB_CDC_DST_UNION,
|
.bDescriptorsubtype = USB_CDC_DST_UNION,
|
||||||
.bMasterInterface = 1,
|
.bMasterInterface = USB_INTF_GPS_CDC_CTL,
|
||||||
.bSlaveInterface = { 2 },
|
.bSlaveInterface = { USB_INTF_GPS_CDC_DATA },
|
||||||
},
|
},
|
||||||
.ep_ctl = {
|
.ep_ctl = {
|
||||||
.bLength = sizeof(struct usb_ep_desc),
|
.bLength = sizeof(struct usb_ep_desc),
|
||||||
.bDescriptorType = USB_DT_EP,
|
.bDescriptorType = USB_DT_EP,
|
||||||
.bEndpointAddress = USB_EP_GPS_CDC_CTL,
|
.bEndpointAddress = USB_EP_GPS_CDC_CTL,
|
||||||
.bmAttributes = 0x03,
|
.bmAttributes = 0x03,
|
||||||
.wMaxPacketSize = 64,
|
/* Longest notif is SERIAL_STATE with 2 data bytes */
|
||||||
|
.wMaxPacketSize = sizeof(struct usb_ctrl_req) + 2,
|
||||||
.bInterval = 0x40,
|
.bInterval = 0x40,
|
||||||
},
|
},
|
||||||
.intf_data = {
|
.intf_data = {
|
||||||
|
@ -275,7 +275,6 @@ static const struct {
|
||||||
.bInterval = 0x00,
|
.bInterval = 0x00,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
#endif
|
|
||||||
.dfu = {
|
.dfu = {
|
||||||
.intf = {
|
.intf = {
|
||||||
.bLength = sizeof(struct usb_intf_desc),
|
.bLength = sizeof(struct usb_intf_desc),
|
||||||
|
|
|
@ -9,9 +9,15 @@
|
||||||
|
|
||||||
#define USB_INTF_E1(p) (0 + (p))
|
#define USB_INTF_E1(p) (0 + (p))
|
||||||
#define USB_INTF_DFU 2
|
#define USB_INTF_DFU 2
|
||||||
#define USB_INTF_NUM 3
|
#define USB_INTF_GPS_CDC_CTL 3
|
||||||
|
#define USB_INTF_GPS_CDC_DATA 4
|
||||||
|
#define USB_INTF_NUM 5
|
||||||
|
|
||||||
#define USB_EP_E1_IN(p) (0x82 + (3 * (p)))
|
#define USB_EP_E1_IN(p) (0x82 + (3 * (p)))
|
||||||
#define USB_EP_E1_OUT(p) (0x01 + (3 * (p)))
|
#define USB_EP_E1_OUT(p) (0x01 + (3 * (p)))
|
||||||
#define USB_EP_E1_FB(p) (0x81 + (3 * (p)))
|
#define USB_EP_E1_FB(p) (0x81 + (3 * (p)))
|
||||||
#define USB_EP_E1_INT(p) (0x83 + (3 * (p)))
|
#define USB_EP_E1_INT(p) (0x83 + (3 * (p)))
|
||||||
|
|
||||||
|
#define USB_EP_GPS_CDC_CTL 0x88
|
||||||
|
#define USB_EP_GPS_CDC_OUT 0x09
|
||||||
|
#define USB_EP_GPS_CDC_IN 0x89
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* usb_gps.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2022 Sylvain Munaut <tnt@246tNt.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <no2usb/usb.h>
|
||||||
|
#include <no2usb/usb_hw.h>
|
||||||
|
#include <no2usb/usb_priv.h>
|
||||||
|
|
||||||
|
#include <no2usb/usb_proto.h>
|
||||||
|
#include <no2usb/usb_cdc_proto.h>
|
||||||
|
|
||||||
|
#include "console.h"
|
||||||
|
#include "usb_desc_ids.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define BUF_SIZE_LOG 9
|
||||||
|
#define BUF_SIZE (1 << BUF_SIZE_LOG)
|
||||||
|
#define BUF_MASK (BUF_SIZE - 1)
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
/* State */
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
/* Buffer */
|
||||||
|
struct {
|
||||||
|
unsigned int wr;
|
||||||
|
unsigned int rd;
|
||||||
|
char data[BUF_SIZE] __attribute__((aligned(4)));
|
||||||
|
} buf;
|
||||||
|
} g_usb_gps;
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
_usb_gps_set_active(bool active)
|
||||||
|
{
|
||||||
|
/* Save new state */
|
||||||
|
g_usb_gps.active = active;
|
||||||
|
|
||||||
|
/* Reset FIFO if disabled */
|
||||||
|
if (!active)
|
||||||
|
g_usb_gps.buf.wr = g_usb_gps.buf.rd = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_usb_gps_fill_packet(unsigned int dst_ofs)
|
||||||
|
{
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
|
/* Len available, limited to 64 */
|
||||||
|
len = (g_usb_gps.buf.wr - g_usb_gps.buf.rd) & BUF_MASK;
|
||||||
|
|
||||||
|
if (len > 64)
|
||||||
|
len = 64;
|
||||||
|
|
||||||
|
/* Copy block */
|
||||||
|
usb_data_write(dst_ofs, &g_usb_gps.buf.data[g_usb_gps.buf.rd], (len + 3) & ~3);
|
||||||
|
|
||||||
|
/* Increment read pointer */
|
||||||
|
g_usb_gps.buf.rd = (g_usb_gps.buf.rd + len) & BUF_MASK;
|
||||||
|
|
||||||
|
/* If length was not multiple of 4, we emptied the FIFO,
|
||||||
|
* so we reset it to 0 so we're aligned again */
|
||||||
|
if (len & 3)
|
||||||
|
g_usb_gps.buf.wr = g_usb_gps.buf.rd = 0;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_gps_puts(const char *str)
|
||||||
|
{
|
||||||
|
unsigned int nxt;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (!g_usb_gps.active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((c = *str++) != '\0')
|
||||||
|
{
|
||||||
|
/* Next write pointer pos and full check */
|
||||||
|
nxt = (g_usb_gps.buf.wr + 1) & BUF_MASK;
|
||||||
|
if (nxt == g_usb_gps.buf.rd)
|
||||||
|
/* If overflow, we keep the latest content ... */
|
||||||
|
g_usb_gps.buf.rd = (g_usb_gps.buf.rd + 1) & BUF_MASK;
|
||||||
|
|
||||||
|
/* Write data */
|
||||||
|
g_usb_gps.buf.data[g_usb_gps.buf.wr] = c;
|
||||||
|
|
||||||
|
/* Update write pointer */
|
||||||
|
g_usb_gps.buf.wr = nxt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_gps_poll(void)
|
||||||
|
{
|
||||||
|
volatile struct usb_ep *ep_regs;
|
||||||
|
|
||||||
|
/* OUT EP: We accept data and throw it away */
|
||||||
|
ep_regs = &usb_ep_regs[USB_EP_GPS_CDC_OUT & 0x1f].out;
|
||||||
|
ep_regs->bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(64);
|
||||||
|
|
||||||
|
/* IN EP: Send whatever is queued */
|
||||||
|
ep_regs = &usb_ep_regs[USB_EP_GPS_CDC_OUT & 0x1f].in;
|
||||||
|
|
||||||
|
if ((ep_regs->bd[0].csr & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_DATA)
|
||||||
|
{
|
||||||
|
int len = _usb_gps_fill_packet(ep_regs->bd[0].ptr);
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
ep_regs->bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(len);
|
||||||
|
else
|
||||||
|
ep_regs->bd[0].csr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum usb_fnd_resp
|
||||||
|
_cdc_set_conf(const struct usb_conf_desc *conf)
|
||||||
|
{
|
||||||
|
const struct usb_intf_desc *intf;
|
||||||
|
|
||||||
|
if (!conf)
|
||||||
|
return USB_FND_SUCCESS;
|
||||||
|
|
||||||
|
/* Configure control interface */
|
||||||
|
intf = usb_desc_find_intf(conf, USB_INTF_GPS_CDC_CTL, 0, NULL);
|
||||||
|
if (!intf)
|
||||||
|
return USB_FND_ERROR;
|
||||||
|
|
||||||
|
usb_ep_boot(intf, USB_EP_GPS_CDC_CTL, false);
|
||||||
|
|
||||||
|
/* Configure data interface */
|
||||||
|
intf = usb_desc_find_intf(conf, USB_INTF_GPS_CDC_DATA, 0, NULL);
|
||||||
|
if (!intf)
|
||||||
|
return USB_FND_ERROR;
|
||||||
|
|
||||||
|
usb_ep_boot(intf, USB_EP_GPS_CDC_OUT, false);
|
||||||
|
usb_ep_boot(intf, USB_EP_GPS_CDC_IN, false);
|
||||||
|
|
||||||
|
return USB_FND_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum usb_fnd_resp
|
||||||
|
_cdc_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
|
||||||
|
{
|
||||||
|
static const struct usb_cdc_line_coding linecoding = {
|
||||||
|
.dwDTERate = 115200,
|
||||||
|
.bCharFormat = 2,
|
||||||
|
.bParityType = 0,
|
||||||
|
.bDataBits = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Check this is handled here */
|
||||||
|
if (USB_REQ_TYPE_RCPT(req) != (USB_REQ_TYPE_CLASS | USB_REQ_RCPT_INTF))
|
||||||
|
return USB_FND_CONTINUE;
|
||||||
|
|
||||||
|
if (req->wIndex != USB_INTF_GPS_CDC_CTL)
|
||||||
|
return USB_FND_CONTINUE;
|
||||||
|
|
||||||
|
/* Process request */
|
||||||
|
switch (req->bRequest) {
|
||||||
|
case USB_REQ_CDC_SEND_ENCAPSULATED_COMMAND:
|
||||||
|
/* We don't support any, so just accept and don't care */
|
||||||
|
return USB_FND_SUCCESS;
|
||||||
|
|
||||||
|
case USB_REQ_CDC_GET_ENCAPSULATED_RESPONSE:
|
||||||
|
/* Never anything to return */
|
||||||
|
xfer->len = 0;
|
||||||
|
return USB_FND_SUCCESS;
|
||||||
|
|
||||||
|
case USB_REQ_CDC_SET_LINE_CODING:
|
||||||
|
/* We only support 1 config, doesn't matter what the hosts sends */
|
||||||
|
return USB_FND_SUCCESS;
|
||||||
|
|
||||||
|
case USB_REQ_CDC_GET_LINE_CODING:
|
||||||
|
/* We only support 1 config, send that back */
|
||||||
|
xfer->data = (void*)&linecoding;
|
||||||
|
xfer->len = sizeof(linecoding);
|
||||||
|
return USB_FND_ERROR;
|
||||||
|
|
||||||
|
case USB_REQ_CDC_SET_CONTROL_LINE_STATE:
|
||||||
|
/* Enable if DTR is set */
|
||||||
|
_usb_gps_set_active((req->wValue & 1) != 0);
|
||||||
|
return USB_FND_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Anything else is not handled */
|
||||||
|
return USB_FND_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct usb_fn_drv _cdc_drv = {
|
||||||
|
.set_conf = _cdc_set_conf,
|
||||||
|
.ctrl_req = _cdc_ctrl_req,
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_gps_init(void)
|
||||||
|
{
|
||||||
|
memset(&g_usb_gps, 0x00, sizeof(g_usb_gps));
|
||||||
|
usb_register_function_driver(&_cdc_drv);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* usb_gps.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2022 Sylvain Munaut <tnt@246tNt.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void usb_gps_puts(const char *str);
|
||||||
|
|
||||||
|
void usb_gps_poll(void);
|
||||||
|
void usb_gps_init(void);
|
|
@ -1 +1 @@
|
||||||
Subproject commit e9d1fbb8a1796444091cd8a39e2a4318530b4daf
|
Subproject commit fdf42a6571a4ae49556626e6fffca1582796f7e8
|
Loading…
Reference in New Issue