blob: a1e43b0a037fff74de2ffecf3fb35414170d0d13 [file] [log] [blame]
Harald Welte76105252019-04-17 16:31:27 +02001/**
2 * \file
3 *
4 * \brief USB Device Stack CCID Function Implementation.
5 *
6 * Copyroght (c) 2019 by Harald Welte <laforge@gnumonks.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include "ccid_df.h"
24
25#ifndef USB_CLASS_CCID
26#define USB_CLASS_CCID 11
27#endif
28
29struct ccid_df_func_data {
30 uint8_t func_iface; /*!< interface number */
31 uint8_t func_ep_in; /*!< IN endpoint number */
32 uint8_t func_ep_out; /*!< OUT endpoint number */
33 uint8_t func_ep_irq; /*!< IRQ endpoint number */
34 bool enabled; /*!< is this driver/function enabled? */
35};
36
37static struct usbdf_driver _ccid_df;
38static struct ccid_df_func_data _ccid_df_funcd;
39
40static int32_t ccid_df_enable(struct usbdf_driver *drv, struct usbd_descriptors *desc)
41{
42 struct ccid_df_func_data *func_data = (struct ccid_df_func_data *)(drv->func_data);
43 usb_iface_desc_t ifc_desc;
44 uint8_t *ifc, *ep;
45
46 ifc = desc->sod;
47 /* FIXME: iterate over multiple interfaces? */
48
49 if (!ifc)
50 return ERR_NOT_FOUND;
51
52 ifc_desc.bInterfaceNumber = ifc[2];
53 ifc_desc.bInterfaceClass = ifc[5];
54
55 if (ifc_desc.bInterfaceClass != USB_CLASS_CCID)
56 return ERR_NOT_FOUND;
57
58 if (func_data->func_iface == ifc_desc.bInterfaceNumber)
59 return ERR_ALREADY_INITIALIZED;
60 else if (func_data->func_iface != 0xff)
61 return ERR_NO_RESOURCE;
62
63 func_data->func_iface = ifc_desc.bInterfaceNumber;
64
65 ep = usb_find_desc(ifc, desc->eod, USB_DT_ENDPOINT);
66 while (NULL != ep) {
67 usb_ep_desc_t ep_desc;
68 ep_desc.bEndpointAddress = ep[2];
69 ep_desc.bmAttributes = ep[3];
70 ep_desc.wMaxPacketSize = usb_get_u16(ep + 4);
71 if (usb_d_ep_init(ep_desc.bEndpointAddress, ep_desc.bmAttributes, ep_desc.wMaxPacketSize))
72 return ERR_NOT_INITIALIZED;
73 if (ep_desc.bEndpointAddress & USB_EP_DIR_IN) {
74 func_data->func_ep_in = ep_desc.bEndpointAddress;
75 /* FIXME: interrupt? */
76 } else {
77 func_data->func_ep_out = ep_desc.bEndpointAddress;
78 }
79 usb_d_ep_enable(ep_desc.bEndpointAddress);
80 desc->sod = ep;
81 ep = usb_find_ep_desc(usb_desc_next(desc->sod), desc->eod);
82 }
83
84 _ccid_df_funcd.enabled = true;
85 return ERR_NONE;
86}
87
88static int32_t ccid_df_disable(struct usbdf_driver *drv, struct usbd_descriptors *desc)
89{
90 struct ccid_df_func_data *func_data = (struct ccid_df_func_data *)(drv->func_data);
91 usb_iface_desc_t ifc_desc;
92
93 if (desc) {
94 ifc_desc.bInterfaceClass = desc->sod[5];
95 if (ifc_desc.bInterfaceClass != USB_CLASS_CCID)
96 return ERR_NOT_FOUND;
97 }
98
99 func_data->func_iface = 0xff;
100 if (func_data->func_ep_in != 0xff) {
101 func_data->func_ep_in = 0xff;
102 usb_d_ep_deinit(func_data->func_ep_in);
103 }
104 if (func_data->func_ep_out != 0xff) {
105 func_data->func_ep_out = 0xff;
106 usb_d_ep_deinit(func_data->func_ep_out);
107 }
108 if (func_data->func_ep_irq != 0xff) {
109 func_data->func_ep_irq = 0xff;
110 usb_d_ep_deinit(func_data->func_ep_irq);
111 }
112
113 _ccid_df_funcd.enabled = true;
114 return ERR_NONE;
115}
116
117/*! \brief CCID Control Function (callback with USB core) */
118static int32_t ccid_df_ctrl(struct usbdf_driver *drv, enum usbdf_control ctrl, void *param)
119{
120 switch (ctrl) {
121 case USBDF_ENABLE:
122 return ccid_df_enable(drv, (struct usbd_descriptors *)param);
123 case USBDF_DISABLE:
124 return ccid_df_disable(drv, (struct usbd_descriptors *)param);
125 case USBDF_GET_IFACE:
126 return ERR_UNSUPPORTED_OP;
127 default:
128 return ERR_INVALID_ARG;
129 }
130}
131
132
133/* process a control endpoint request */
134static int32_t ccid_df_ctrl_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
135{
136 return ERR_NOT_FOUND;
137}
138
139static struct usbdc_handler ccid_df_req_h = { NULL, (FUNC_PTR) ccid_df_ctrl_req };
140
141int32_t ccid_df_init(void)
142{
143 if (usbdc_get_state() > USBD_S_POWER)
144 return ERR_DENIED;
145
146 _ccid_df.ctrl = ccid_df_ctrl;
147 _ccid_df.func_data = &_ccid_df_funcd;
148
149 /* register the actual USB Function */
150 usbdc_register_function(&_ccid_df);
151 /* register the call-back for control endpoint handling */
152 usbdc_register_handler(USBDC_HDL_REQ, &ccid_df_req_h);
153
154 return ERR_NONE;
155}
156
157void ccid_df_deinit(void)
158{
159 usb_d_ep_deinit(_ccid_df_funcd.func_ep_in);
160 usb_d_ep_deinit(_ccid_df_funcd.func_ep_out);
161 usb_d_ep_deinit(_ccid_df_funcd.func_ep_irq);
162}
163
164int32_t ccid_df_register_callback(enum ccid_df_cb_type cb_type, FUNC_PTR func)
165{
166 switch (cb_type) {
167 case CCID_DF_CB_READ_OUT:
168 usb_d_ep_register_callback(_ccid_df_funcd.func_ep_out, USB_D_EP_CB_XFER, func);
169 break;
170 case CCID_DF_CB_WRITE_IN:
171 usb_d_ep_register_callback(_ccid_df_funcd.func_ep_in, USB_D_EP_CB_XFER, func);
172 break;
173 case CCID_DF_CB_WRITE_IRQ:
174 usb_d_ep_register_callback(_ccid_df_funcd.func_ep_irq, USB_D_EP_CB_XFER, func);
175 break;
176 default:
177 return ERR_INVALID_ARG;
178 }
179 return ERR_NONE;
180}