~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/nfc/nci/spi.c

Version: ~ [ linux-5.4-rc3 ] ~ [ linux-5.3.6 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.79 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.149 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.196 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.196 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.75 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Copyright (C) 2013  Intel Corporation. All rights reserved.
  3  *
  4  * This program is free software; you can redistribute it and/or modify it
  5  * under the terms and conditions of the GNU General Public License,
  6  * version 2, as published by the Free Software Foundation.
  7  *
  8  * This program is distributed in the hope it will be useful, but WITHOUT
  9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 11  * more details.
 12  *
 13  * You should have received a copy of the GNU General Public License along with
 14  * this program; if not, write to the Free Software Foundation, Inc.,
 15  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 16  *
 17  */
 18 
 19 #define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
 20 
 21 #include <linux/module.h>
 22 
 23 #include <linux/export.h>
 24 #include <linux/spi/spi.h>
 25 #include <linux/crc-ccitt.h>
 26 #include <net/nfc/nci_core.h>
 27 
 28 #define NCI_SPI_ACK_SHIFT               6
 29 #define NCI_SPI_MSB_PAYLOAD_MASK        0x3F
 30 
 31 #define NCI_SPI_SEND_TIMEOUT    (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
 32                                         NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
 33 
 34 #define NCI_SPI_DIRECT_WRITE    0x01
 35 #define NCI_SPI_DIRECT_READ     0x02
 36 
 37 #define ACKNOWLEDGE_NONE        0
 38 #define ACKNOWLEDGE_ACK         1
 39 #define ACKNOWLEDGE_NACK        2
 40 
 41 #define CRC_INIT                0xFFFF
 42 
 43 static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb,
 44                           int cs_change)
 45 {
 46         struct spi_message m;
 47         struct spi_transfer t;
 48 
 49         memset(&t, 0, sizeof(struct spi_transfer));
 50         /* a NULL skb means we just want the SPI chip select line to raise */
 51         if (skb) {
 52                 t.tx_buf = skb->data;
 53                 t.len = skb->len;
 54         } else {
 55                 /* still set tx_buf non NULL to make the driver happy */
 56                 t.tx_buf = &t;
 57                 t.len = 0;
 58         }
 59         t.cs_change = cs_change;
 60         t.delay_usecs = nspi->xfer_udelay;
 61         t.speed_hz = nspi->xfer_speed_hz;
 62 
 63         spi_message_init(&m);
 64         spi_message_add_tail(&t, &m);
 65 
 66         return spi_sync(nspi->spi, &m);
 67 }
 68 
 69 int nci_spi_send(struct nci_spi *nspi,
 70                  struct completion *write_handshake_completion,
 71                  struct sk_buff *skb)
 72 {
 73         unsigned int payload_len = skb->len;
 74         unsigned char *hdr;
 75         int ret;
 76         long completion_rc;
 77 
 78         /* add the NCI SPI header to the start of the buffer */
 79         hdr = skb_push(skb, NCI_SPI_HDR_LEN);
 80         hdr[0] = NCI_SPI_DIRECT_WRITE;
 81         hdr[1] = nspi->acknowledge_mode;
 82         hdr[2] = payload_len >> 8;
 83         hdr[3] = payload_len & 0xFF;
 84 
 85         if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
 86                 u16 crc;
 87 
 88                 crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
 89                 skb_put_u8(skb, crc >> 8);
 90                 skb_put_u8(skb, crc & 0xFF);
 91         }
 92 
 93         if (write_handshake_completion) {
 94                 /* Trick SPI driver to raise chip select */
 95                 ret = __nci_spi_send(nspi, NULL, 1);
 96                 if (ret)
 97                         goto done;
 98 
 99                 /* wait for NFC chip hardware handshake to complete */
100                 if (wait_for_completion_timeout(write_handshake_completion,
101                                                 msecs_to_jiffies(1000)) == 0) {
102                         ret = -ETIME;
103                         goto done;
104                 }
105         }
106 
107         ret = __nci_spi_send(nspi, skb, 0);
108         if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
109                 goto done;
110 
111         reinit_completion(&nspi->req_completion);
112         completion_rc = wait_for_completion_interruptible_timeout(
113                                                         &nspi->req_completion,
114                                                         NCI_SPI_SEND_TIMEOUT);
115 
116         if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)
117                 ret = -EIO;
118 
119 done:
120         kfree_skb(skb);
121 
122         return ret;
123 }
124 EXPORT_SYMBOL_GPL(nci_spi_send);
125 
126 /* ---- Interface to NCI SPI drivers ---- */
127 
128 /**
129  * nci_spi_allocate_spi - allocate a new nci spi
130  *
131  * @spi: SPI device
132  * @acknowledge_mode: Acknowledge mode used by the NFC device
133  * @delay: delay between transactions in us
134  * @ndev: nci dev to send incoming nci frames to
135  */
136 struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
137                                      u8 acknowledge_mode, unsigned int delay,
138                                      struct nci_dev *ndev)
139 {
140         struct nci_spi *nspi;
141 
142         nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
143         if (!nspi)
144                 return NULL;
145 
146         nspi->acknowledge_mode = acknowledge_mode;
147         nspi->xfer_udelay = delay;
148         /* Use controller max SPI speed by default */
149         nspi->xfer_speed_hz = 0;
150         nspi->spi = spi;
151         nspi->ndev = ndev;
152         init_completion(&nspi->req_completion);
153 
154         return nspi;
155 }
156 EXPORT_SYMBOL_GPL(nci_spi_allocate_spi);
157 
158 static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
159 {
160         struct sk_buff *skb;
161         unsigned char *hdr;
162         u16 crc;
163         int ret;
164 
165         skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);
166 
167         /* add the NCI SPI header to the start of the buffer */
168         hdr = skb_push(skb, NCI_SPI_HDR_LEN);
169         hdr[0] = NCI_SPI_DIRECT_WRITE;
170         hdr[1] = NCI_SPI_CRC_ENABLED;
171         hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
172         hdr[3] = 0;
173 
174         crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
175         skb_put_u8(skb, crc >> 8);
176         skb_put_u8(skb, crc & 0xFF);
177 
178         ret = __nci_spi_send(nspi, skb, 0);
179 
180         kfree_skb(skb);
181 
182         return ret;
183 }
184 
185 static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
186 {
187         struct sk_buff *skb;
188         struct spi_message m;
189         unsigned char req[2], resp_hdr[2];
190         struct spi_transfer tx, rx;
191         unsigned short rx_len = 0;
192         int ret;
193 
194         spi_message_init(&m);
195 
196         memset(&tx, 0, sizeof(struct spi_transfer));
197         req[0] = NCI_SPI_DIRECT_READ;
198         req[1] = nspi->acknowledge_mode;
199         tx.tx_buf = req;
200         tx.len = 2;
201         tx.cs_change = 0;
202         tx.speed_hz = nspi->xfer_speed_hz;
203         spi_message_add_tail(&tx, &m);
204 
205         memset(&rx, 0, sizeof(struct spi_transfer));
206         rx.rx_buf = resp_hdr;
207         rx.len = 2;
208         rx.cs_change = 1;
209         rx.speed_hz = nspi->xfer_speed_hz;
210         spi_message_add_tail(&rx, &m);
211 
212         ret = spi_sync(nspi->spi, &m);
213         if (ret)
214                 return NULL;
215 
216         if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
217                 rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
218                                 resp_hdr[1] + NCI_SPI_CRC_LEN;
219         else
220                 rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
221 
222         skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);
223         if (!skb)
224                 return NULL;
225 
226         spi_message_init(&m);
227 
228         memset(&rx, 0, sizeof(struct spi_transfer));
229         rx.rx_buf = skb_put(skb, rx_len);
230         rx.len = rx_len;
231         rx.cs_change = 0;
232         rx.delay_usecs = nspi->xfer_udelay;
233         rx.speed_hz = nspi->xfer_speed_hz;
234         spi_message_add_tail(&rx, &m);
235 
236         ret = spi_sync(nspi->spi, &m);
237         if (ret)
238                 goto receive_error;
239 
240         if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
241                 *(u8 *)skb_push(skb, 1) = resp_hdr[1];
242                 *(u8 *)skb_push(skb, 1) = resp_hdr[0];
243         }
244 
245         return skb;
246 
247 receive_error:
248         kfree_skb(skb);
249 
250         return NULL;
251 }
252 
253 static int nci_spi_check_crc(struct sk_buff *skb)
254 {
255         u16 crc_data = (skb->data[skb->len - 2] << 8) |
256                         skb->data[skb->len - 1];
257         int ret;
258 
259         ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
260                         == crc_data);
261 
262         skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
263 
264         return ret;
265 }
266 
267 static u8 nci_spi_get_ack(struct sk_buff *skb)
268 {
269         u8 ret;
270 
271         ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
272 
273         /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
274         skb_pull(skb, 2);
275 
276         return ret;
277 }
278 
279 /**
280  * nci_spi_read - read frame from NCI SPI drivers
281  *
282  * @nspi: The nci spi
283  * Context: can sleep
284  *
285  * This call may only be used from a context that may sleep.  The sleep
286  * is non-interruptible, and has no timeout.
287  *
288  * It returns an allocated skb containing the frame on success, or NULL.
289  */
290 struct sk_buff *nci_spi_read(struct nci_spi *nspi)
291 {
292         struct sk_buff *skb;
293 
294         /* Retrieve frame from SPI */
295         skb = __nci_spi_read(nspi);
296         if (!skb)
297                 goto done;
298 
299         if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
300                 if (!nci_spi_check_crc(skb)) {
301                         send_acknowledge(nspi, ACKNOWLEDGE_NACK);
302                         goto done;
303                 }
304 
305                 /* In case of acknowledged mode: if ACK or NACK received,
306                  * unblock completion of latest frame sent.
307                  */
308                 nspi->req_result = nci_spi_get_ack(skb);
309                 if (nspi->req_result)
310                         complete(&nspi->req_completion);
311         }
312 
313         /* If there is no payload (ACK/NACK only frame),
314          * free the socket buffer
315          */
316         if (!skb->len) {
317                 kfree_skb(skb);
318                 skb = NULL;
319                 goto done;
320         }
321 
322         if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
323                 send_acknowledge(nspi, ACKNOWLEDGE_ACK);
324 
325 done:
326 
327         return skb;
328 }
329 EXPORT_SYMBOL_GPL(nci_spi_read);
330 
331 MODULE_LICENSE("GPL");
332 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp