aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2016-10-25 10:28:10 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-10 11:03:36 -0500
commitf2ed287bcc9073d8edbf6561c389b282163edc78 (patch)
tree1c730107aa2181bc9fb5fe40500403e68fc03344
parentacbb910ae04b8eed9eec7a69ef4e0979f364ff46 (diff)
char/pcmcia: add scr24x_cs chip card interface driver
This implements only the very basic protocol "Mode A", just to make the device functional. Patches to implement "Mode C" that uses better bulking and is interrupt-driver may follow. The device essentially speaks the same protocol as USB CCID devices do over the bulk endpoints. The driver exchanges the command submissions and responses over a plain read()/write() interface, compatible with legacy OpenCT's pcmcia_block driver. Patches for the newer CCID driver are available: https://github.com/lkundrak/CCID/tree/lr/pcmcia_block Signed-off-by: Lubomir Rintel <lkundrak@v3.sk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/char/pcmcia/Kconfig11
-rw-r--r--drivers/char/pcmcia/Makefile1
-rw-r--r--drivers/char/pcmcia/scr24x_cs.c372
4 files changed, 389 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 2e8b8710ce5d..46f74871df09 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10699,6 +10699,11 @@ W: http://www.sunplus.com
10699S: Supported 10699S: Supported
10700F: arch/score/ 10700F: arch/score/
10701 10701
10702SCR24X CHIP CARD INTERFACE DRIVER
10703M: Lubomir Rintel <lkundrak@v3.sk>
10704S: Supported
10705F: drivers/char/pcmcia/scr24x_cs.c
10706
10702SYSTEM CONTROL & POWER INTERFACE (SCPI) Message Protocol drivers 10707SYSTEM CONTROL & POWER INTERFACE (SCPI) Message Protocol drivers
10703M: Sudeep Holla <sudeep.holla@arm.com> 10708M: Sudeep Holla <sudeep.holla@arm.com>
10704L: linux-arm-kernel@lists.infradead.org 10709L: linux-arm-kernel@lists.infradead.org
diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig
index 8d3dfb0c8a26..1d1e7da8ad27 100644
--- a/drivers/char/pcmcia/Kconfig
+++ b/drivers/char/pcmcia/Kconfig
@@ -43,6 +43,17 @@ config CARDMAN_4040
43 (http://www.omnikey.com/), or a current development version of OpenCT 43 (http://www.omnikey.com/), or a current development version of OpenCT
44 (http://www.opensc-project.org/opensc). 44 (http://www.opensc-project.org/opensc).
45 45
46config SCR24X
47 tristate "SCR24x Chip Card Interface support"
48 depends on PCMCIA
49 help
50 Enable support for the SCR24x PCMCIA Chip Card Interface.
51
52 To compile this driver as a module, choose M here.
53 The module will be called scr24x_cs..
54
55 If unsure say N.
56
46config IPWIRELESS 57config IPWIRELESS
47 tristate "IPWireless 3G UMTS PCMCIA card support" 58 tristate "IPWireless 3G UMTS PCMCIA card support"
48 depends on PCMCIA && NETDEVICES && TTY 59 depends on PCMCIA && NETDEVICES && TTY
diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile
index 0aae20985d57..5b836bc21406 100644
--- a/drivers/char/pcmcia/Makefile
+++ b/drivers/char/pcmcia/Makefile
@@ -7,3 +7,4 @@
7obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o 7obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
8obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o 8obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o
9obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o 9obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o
10obj-$(CONFIG_SCR24X) += scr24x_cs.o
diff --git a/drivers/char/pcmcia/scr24x_cs.c b/drivers/char/pcmcia/scr24x_cs.c
new file mode 100644
index 000000000000..4f215ce1a3be
--- /dev/null
+++ b/drivers/char/pcmcia/scr24x_cs.c
@@ -0,0 +1,372 @@
1/*
2 * SCR24x PCMCIA Smart Card Reader Driver
3 *
4 * Copyright (C) 2005-2006 TL Sudheendran
5 * Copyright (C) 2016 Lubomir Rintel
6 *
7 * Derived from "scr24x_v4.2.6_Release.tar.gz" driver by TL Sudheendran.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; see the file COPYING. If not, write to
21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include <linux/device.h>
25#include <linux/module.h>
26#include <linux/delay.h>
27#include <linux/cdev.h>
28#include <linux/slab.h>
29#include <linux/fs.h>
30#include <linux/uaccess.h>
31
32#include <pcmcia/cistpl.h>
33#include <pcmcia/ds.h>
34
35#define CCID_HEADER_SIZE 10
36#define CCID_LENGTH_OFFSET 1
37#define CCID_MAX_LEN 271
38
39#define SCR24X_DATA(n) (1 + n)
40#define SCR24X_CMD_STATUS 7
41#define CMD_START 0x40
42#define CMD_WRITE_BYTE 0x41
43#define CMD_READ_BYTE 0x42
44#define STATUS_BUSY 0x80
45
46struct scr24x_dev {
47 struct device *dev;
48 struct cdev c_dev;
49 unsigned char buf[CCID_MAX_LEN];
50 int devno;
51 struct mutex lock;
52 struct kref refcnt;
53 u8 __iomem *regs;
54};
55
56#define SCR24X_DEVS 8
57static DECLARE_BITMAP(scr24x_minors, SCR24X_DEVS);
58
59static struct class *scr24x_class;
60static dev_t scr24x_devt;
61
62static void scr24x_delete(struct kref *kref)
63{
64 struct scr24x_dev *dev = container_of(kref, struct scr24x_dev,
65 refcnt);
66
67 kfree(dev);
68}
69
70static int scr24x_wait_ready(struct scr24x_dev *dev)
71{
72 u_char status;
73 int timeout = 100;
74
75 do {
76 status = ioread8(dev->regs + SCR24X_CMD_STATUS);
77 if (!(status & STATUS_BUSY))
78 return 0;
79
80 msleep(20);
81 } while (--timeout);
82
83 return -EIO;
84}
85
86static int scr24x_open(struct inode *inode, struct file *filp)
87{
88 struct scr24x_dev *dev = container_of(inode->i_cdev,
89 struct scr24x_dev, c_dev);
90
91 kref_get(&dev->refcnt);
92 filp->private_data = dev;
93
94 return nonseekable_open(inode, filp);
95}
96
97static int scr24x_release(struct inode *inode, struct file *filp)
98{
99 struct scr24x_dev *dev = filp->private_data;
100
101 /* We must not take the dev->lock here as scr24x_delete()
102 * might be called to remove the dev structure altogether.
103 * We don't need the lock anyway, since after the reference
104 * acquired in probe() is released in remove() the chrdev
105 * is already unregistered and noone can possibly acquire
106 * a reference via open() anymore. */
107 kref_put(&dev->refcnt, scr24x_delete);
108 return 0;
109}
110
111static int read_chunk(struct scr24x_dev *dev, size_t offset, size_t limit)
112{
113 size_t i, y;
114 int ret;
115
116 for (i = offset; i < limit; i += 5) {
117 iowrite8(CMD_READ_BYTE, dev->regs + SCR24X_CMD_STATUS);
118 ret = scr24x_wait_ready(dev);
119 if (ret < 0)
120 return ret;
121
122 for (y = 0; y < 5 && i + y < limit; y++)
123 dev->buf[i + y] = ioread8(dev->regs + SCR24X_DATA(y));
124 }
125
126 return 0;
127}
128
129static ssize_t scr24x_read(struct file *filp, char __user *buf, size_t count,
130 loff_t *ppos)
131{
132 struct scr24x_dev *dev = filp->private_data;
133 int ret;
134 int len;
135
136 if (count < CCID_HEADER_SIZE)
137 return -EINVAL;
138
139 if (mutex_lock_interruptible(&dev->lock))
140 return -ERESTARTSYS;
141
142 if (!dev->dev) {
143 ret = -ENODEV;
144 goto out;
145 }
146
147 ret = scr24x_wait_ready(dev);
148 if (ret < 0)
149 goto out;
150 len = CCID_HEADER_SIZE;
151 ret = read_chunk(dev, 0, len);
152 if (ret < 0)
153 goto out;
154
155 len += le32_to_cpu(*(__le32 *)(&dev->buf[CCID_LENGTH_OFFSET]));
156 if (len > sizeof(dev->buf)) {
157 ret = -EIO;
158 goto out;
159 }
160 read_chunk(dev, CCID_HEADER_SIZE, len);
161 if (ret < 0)
162 goto out;
163
164 if (len < count)
165 count = len;
166
167 if (copy_to_user(buf, dev->buf, count)) {
168 ret = -EFAULT;
169 goto out;
170 }
171
172 ret = count;
173out:
174 mutex_unlock(&dev->lock);
175 return ret;
176}
177
178static ssize_t scr24x_write(struct file *filp, const char __user *buf,
179 size_t count, loff_t *ppos)
180{
181 struct scr24x_dev *dev = filp->private_data;
182 size_t i, y;
183 int ret;
184
185 if (mutex_lock_interruptible(&dev->lock))
186 return -ERESTARTSYS;
187
188 if (!dev->dev) {
189 ret = -ENODEV;
190 goto out;
191 }
192
193 if (count > sizeof(dev->buf)) {
194 ret = -EINVAL;
195 goto out;
196 }
197
198 if (copy_from_user(dev->buf, buf, count)) {
199 ret = -EFAULT;
200 goto out;
201 }
202
203 ret = scr24x_wait_ready(dev);
204 if (ret < 0)
205 goto out;
206
207 iowrite8(CMD_START, dev->regs + SCR24X_CMD_STATUS);
208 ret = scr24x_wait_ready(dev);
209 if (ret < 0)
210 goto out;
211
212 for (i = 0; i < count; i += 5) {
213 for (y = 0; y < 5 && i + y < count; y++)
214 iowrite8(dev->buf[i + y], dev->regs + SCR24X_DATA(y));
215
216 iowrite8(CMD_WRITE_BYTE, dev->regs + SCR24X_CMD_STATUS);
217 ret = scr24x_wait_ready(dev);
218 if (ret < 0)
219 goto out;
220 }
221
222 ret = count;
223out:
224 mutex_unlock(&dev->lock);
225 return ret;
226}
227
228static const struct file_operations scr24x_fops = {
229 .owner = THIS_MODULE,
230 .read = scr24x_read,
231 .write = scr24x_write,
232 .open = scr24x_open,
233 .release = scr24x_release,
234 .llseek = no_llseek,
235};
236
237static int scr24x_config_check(struct pcmcia_device *link, void *priv_data)
238{
239 if (resource_size(link->resource[PCMCIA_IOPORT_0]) != 0x11)
240 return -ENODEV;
241 return pcmcia_request_io(link);
242}
243
244static int scr24x_probe(struct pcmcia_device *link)
245{
246 struct scr24x_dev *dev;
247 int ret;
248
249 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
250 if (!dev)
251 return -ENOMEM;
252
253 dev->devno = find_first_zero_bit(scr24x_minors, SCR24X_DEVS);
254 if (dev->devno >= SCR24X_DEVS) {
255 ret = -EBUSY;
256 goto err;
257 }
258
259 mutex_init(&dev->lock);
260 kref_init(&dev->refcnt);
261
262 link->priv = dev;
263 link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
264
265 ret = pcmcia_loop_config(link, scr24x_config_check, NULL);
266 if (ret < 0)
267 goto err;
268
269 dev->dev = &link->dev;
270 dev->regs = devm_ioport_map(&link->dev,
271 link->resource[PCMCIA_IOPORT_0]->start,
272 resource_size(link->resource[PCMCIA_IOPORT_0]));
273 if (!dev->regs) {
274 ret = -EIO;
275 goto err;
276 }
277
278 cdev_init(&dev->c_dev, &scr24x_fops);
279 dev->c_dev.owner = THIS_MODULE;
280 dev->c_dev.ops = &scr24x_fops;
281 ret = cdev_add(&dev->c_dev, MKDEV(MAJOR(scr24x_devt), dev->devno), 1);
282 if (ret < 0)
283 goto err;
284
285 ret = pcmcia_enable_device(link);
286 if (ret < 0) {
287 pcmcia_disable_device(link);
288 goto err;
289 }
290
291 device_create(scr24x_class, NULL, MKDEV(MAJOR(scr24x_devt), dev->devno),
292 NULL, "scr24x%d", dev->devno);
293
294 dev_info(&link->dev, "SCR24x Chip Card Interface\n");
295 return 0;
296
297err:
298 if (dev->devno < SCR24X_DEVS)
299 clear_bit(dev->devno, scr24x_minors);
300 kfree (dev);
301 return ret;
302}
303
304static void scr24x_remove(struct pcmcia_device *link)
305{
306 struct scr24x_dev *dev = (struct scr24x_dev *)link->priv;
307
308 device_destroy(scr24x_class, MKDEV(MAJOR(scr24x_devt), dev->devno));
309 mutex_lock(&dev->lock);
310 pcmcia_disable_device(link);
311 cdev_del(&dev->c_dev);
312 clear_bit(dev->devno, scr24x_minors);
313 dev->dev = NULL;
314 mutex_unlock(&dev->lock);
315
316 kref_put(&dev->refcnt, scr24x_delete);
317}
318
319static const struct pcmcia_device_id scr24x_ids[] = {
320 PCMCIA_DEVICE_PROD_ID12("HP", "PC Card Smart Card Reader",
321 0x53cb94f9, 0xbfdf89a5),
322 PCMCIA_DEVICE_PROD_ID1("SCR241 PCMCIA", 0x6271efa3),
323 PCMCIA_DEVICE_PROD_ID1("SCR243 PCMCIA", 0x2054e8de),
324 PCMCIA_DEVICE_PROD_ID1("SCR24x PCMCIA", 0x54a33665),
325 PCMCIA_DEVICE_NULL
326};
327MODULE_DEVICE_TABLE(pcmcia, scr24x_ids);
328
329static struct pcmcia_driver scr24x_driver = {
330 .owner = THIS_MODULE,
331 .name = "scr24x_cs",
332 .probe = scr24x_probe,
333 .remove = scr24x_remove,
334 .id_table = scr24x_ids,
335};
336
337static int __init scr24x_init(void)
338{
339 int ret;
340
341 scr24x_class = class_create(THIS_MODULE, "scr24x");
342 if (IS_ERR(scr24x_class))
343 return PTR_ERR(scr24x_class);
344
345 ret = alloc_chrdev_region(&scr24x_devt, 0, SCR24X_DEVS, "scr24x");
346 if (ret < 0) {
347 class_destroy(scr24x_class);
348 return ret;
349 }
350
351 ret = pcmcia_register_driver(&scr24x_driver);
352 if (ret < 0) {
353 unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
354 class_destroy(scr24x_class);
355 }
356
357 return ret;
358}
359
360static void __exit scr24x_exit(void)
361{
362 pcmcia_unregister_driver(&scr24x_driver);
363 unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
364 class_destroy(scr24x_class);
365}
366
367module_init(scr24x_init);
368module_exit(scr24x_exit);
369
370MODULE_AUTHOR("Lubomir Rintel");
371MODULE_DESCRIPTION("SCR24x PCMCIA Smart Card Reader Driver");
372MODULE_LICENSE("GPL");