ccid_proto.h: Add enum ccid_class_spec_req

This list the three class specific control requests specified in
the CCID spec.

Change-Id: Ib0016c48627462b189efe956d277b7200c74dd53
diff --git a/sysmoOCTSIM/gcc/Makefile b/sysmoOCTSIM/gcc/Makefile
index 8b47909..451ed35 100644
--- a/sysmoOCTSIM/gcc/Makefile
+++ b/sysmoOCTSIM/gcc/Makefile
@@ -3,7 +3,7 @@
 ################################################################################
 
 EXTRA_LIBS=/usr/local/arm-none-eabi/lib/libosmocore.a
-EXTRA_CFLAGS=-I/usr/local/arm-none-eabi/include
+EXTRA_CFLAGS=-I/usr/local/arm-none-eabi/include -I../../ccid
 
 ifdef SystemRoot
 	SHELL = cmd.exe
diff --git a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c
index a1e43b0..018208a 100644
--- a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c
+++ b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c
@@ -21,6 +21,8 @@
  */
 
 #include "ccid_df.h"
+#include "ccid_proto.h"
+#include "usb_includes.h"
 
 #ifndef USB_CLASS_CCID
 #define	USB_CLASS_CCID	11
@@ -32,11 +34,17 @@
 	uint8_t func_ep_out;	/*!< OUT endpoint number */
 	uint8_t func_ep_irq;	/*!< IRQ endpoint number */
 	bool enabled;		/*!< is this driver/function enabled? */
+	const struct usb_ccid_class_descriptor *ccid_cd;
 };
 
 static struct usbdf_driver _ccid_df;
 static struct ccid_df_func_data _ccid_df_funcd;
 
+/* FIXME: make those configurable, ensure they're sized according to
+ * bNumClockSupported / bNumDataRatesSupported */
+static uint32_t ccid_clock_frequencies[] = { LE32(20000) };
+static uint32_t ccid_baud_rates[] = { LE32(9600) };
+
 static int32_t ccid_df_enable(struct usbdf_driver *drv, struct usbd_descriptors *desc)
 {
 	struct ccid_df_func_data *func_data = (struct ccid_df_func_data *)(drv->func_data);
@@ -71,8 +79,10 @@
 		if (usb_d_ep_init(ep_desc.bEndpointAddress, ep_desc.bmAttributes, ep_desc.wMaxPacketSize))
 			return ERR_NOT_INITIALIZED;
 		if (ep_desc.bEndpointAddress & USB_EP_DIR_IN) {
-			func_data->func_ep_in = ep_desc.bEndpointAddress;
-			/* FIXME: interrupt? */
+			if ((ep_desc.bmAttributes & USB_EP_XTYPE_MASK) == USB_EP_XTYPE_INTERRUPT)
+				func_data->func_ep_irq = ep_desc.bEndpointAddress;
+			else
+				func_data->func_ep_in = ep_desc.bEndpointAddress;
 		} else {
 			func_data->func_ep_out = ep_desc.bEndpointAddress;
 		}
@@ -81,6 +91,10 @@
 		ep = usb_find_ep_desc(usb_desc_next(desc->sod), desc->eod);
 	}
 
+	ASSERT(func_data->func_ep_irq);
+	ASSERT(func_data->func_ep_in);
+	ASSERT(func_data->func_ep_out);
+
 	_ccid_df_funcd.enabled = true;
 	return ERR_NONE;
 }
@@ -129,11 +143,80 @@
 	}
 }
 
+/* Section 5.3.1: ABORT */
+static int32_t ccid_df_ctrl_req_ccid_abort(uint8_t ep, struct usb_req *req,
+					   enum usb_ctrl_stage stage)
+{
+	const struct usb_ccid_class_descriptor *ccid_cd = _ccid_df_funcd.ccid_cd;
+	uint8_t slot_nr = req->wValue & 0xff;
+
+	if (slot_nr > ccid_cd->bMaxSlotIndex)
+		return ERR_INVALID_ARG;
+	slot_nr = req->wValue;
+
+	/* FIXME: Implement Abort handling, particularly in combination with
+	 * the PC_to_RDR_Abort on the OUT EP */
+	return ERR_NONE;
+}
+
+/* Section 5.3.2: return array of DWORD containing clock frequencies in kHz */
+static int32_t ccid_df_ctrl_req_ccid_get_clock_freq(uint8_t ep, struct usb_req *req,
+						    enum usb_ctrl_stage stage)
+{
+	const struct usb_ccid_class_descriptor *ccid_cd = _ccid_df_funcd.ccid_cd;
+
+	if (stage != USB_DATA_STAGE)
+		return ERR_NONE;
+
+	if (req->wLength != ccid_cd->bNumClockSupported * sizeof(uint32_t))
+		return ERR_INVALID_DATA;
+
+	return usbdc_xfer(ep, (uint8_t *)ccid_clock_frequencies, req->wLength, false);
+}
+
+/* Section 5.3.3: return array of DWORD containing data rates in bps */
+static int32_t ccid_df_ctrl_req_ccid_get_data_rates(uint8_t ep, struct usb_req *req,
+						    enum usb_ctrl_stage stage)
+{
+	const struct usb_ccid_class_descriptor *ccid_cd = _ccid_df_funcd.ccid_cd;
+
+	if (stage != USB_DATA_STAGE)
+		return ERR_NONE;
+
+	if (req->wLength != ccid_cd->bNumDataRatesSupported * sizeof(uint32_t))
+		return ERR_INVALID_DATA;
+
+	return usbdc_xfer(ep, (uint8_t *)ccid_baud_rates, req->wLength, false);
+}
+
 
 /* process a control endpoint request */
 static int32_t ccid_df_ctrl_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
 {
-	return ERR_NOT_FOUND;
+	/* verify this is a class-specific request */
+	if (0x01 != ((req->bmRequestType >> 5) & 0x03))
+                return ERR_NOT_FOUND;
+
+	/* Verify req->wIndex == interface */
+	if (req->wIndex != _ccid_df_funcd.func_iface)
+                return ERR_NOT_FOUND;
+
+	switch (req->bRequest) {
+	case CLASS_SPEC_CCID_ABORT:
+		if (req->bmRequestType & USB_EP_DIR_IN)
+			return ERR_INVALID_ARG;
+		return ccid_df_ctrl_req_ccid_abort(ep, req, stage);
+	case CLASS_SPEC_CCID_GET_CLOCK_FREQ:
+		if (!(req->bmRequestType & USB_EP_DIR_IN))
+			return ERR_INVALID_ARG;
+		return ccid_df_ctrl_req_ccid_get_clock_freq(ep, req, stage);
+	case CLASS_SPEC_CCID_GET_DATA_RATES:
+		if (!(req->bmRequestType & USB_EP_DIR_IN))
+			return ERR_INVALID_ARG;
+		return ccid_df_ctrl_req_ccid_get_data_rates(ep, req, stage);
+	default:
+		return ERR_NOT_FOUND;
+	}
 }
 
 static struct usbdc_handler ccid_df_req_h = { NULL, (FUNC_PTR) ccid_df_ctrl_req };
@@ -161,6 +244,27 @@
 	usb_d_ep_deinit(_ccid_df_funcd.func_ep_irq);
 }
 
+int32_t ccid_df_read_out(uint8_t *buf, uint32_t size)
+{
+	if (!ccid_df_is_enabled())
+		return ERR_DENIED;
+	return usbdc_xfer(_ccid_df_funcd.func_ep_out, buf, size, false);
+}
+
+int32_t ccid_df_write_in(uint8_t *buf, uint32_t size)
+{
+	if (!ccid_df_is_enabled())
+		return ERR_DENIED;
+	return usbdc_xfer(_ccid_df_funcd.func_ep_in, buf, size, true);
+}
+
+int32_t ccid_df_write_irq(uint8_t *buf, uint32_t size)
+{
+	if (!ccid_df_is_enabled())
+		return ERR_DENIED;
+	return usbdc_xfer(_ccid_df_funcd.func_ep_irq, buf, size, true);
+}
+
 int32_t ccid_df_register_callback(enum ccid_df_cb_type cb_type, FUNC_PTR func)
 {
 	switch (cb_type) {
@@ -178,3 +282,8 @@
 	}
 	return ERR_NONE;
 }
+
+bool ccid_df_is_enabled(void)
+{
+	return _ccid_df_funcd.enabled;
+}
diff --git a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.h b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.h
index ea3a672..21fa99e 100644
--- a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.h
+++ b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.h
@@ -10,4 +10,8 @@
 
 int32_t ccid_df_init(void);
 void ccid_df_deinit(void);
+int32_t ccid_df_read_out(uint8_t *buf, uint32_t size);
+int32_t ccid_df_write_in(uint8_t *buf, uint32_t size);
+int32_t ccid_df_write_irq(uint8_t *buf, uint32_t size);
 int32_t ccid_df_register_callback(enum ccid_df_cb_type cb_type, FUNC_PTR ptr);
+bool ccid_df_is_enabled(void);