tty/serial_core: add ISO7816 infrastructure
authorNicolas Ferre <nicolas.ferre@microchip.com>
Wed, 26 Sep 2018 12:58:47 +0000 (14:58 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 2 Oct 2018 20:38:55 +0000 (13:38 -0700)
Add the ISO7816 ioctl and associated accessors and data structure.
Drivers can then use this common implementation to handle ISO7816
(smart cards).

Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
[ludovic.desroches@microchip.com: squash and rebase, removal of gpios, checkpatch fixes]
Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
12 files changed:
Documentation/serial/serial-iso7816.txt [new file with mode: 0644]
arch/alpha/include/uapi/asm/ioctls.h
arch/mips/include/uapi/asm/ioctls.h
arch/parisc/include/uapi/asm/ioctls.h
arch/powerpc/include/uapi/asm/ioctls.h
arch/sh/include/uapi/asm/ioctls.h
arch/sparc/include/uapi/asm/ioctls.h
arch/xtensa/include/uapi/asm/ioctls.h
drivers/tty/serial/serial_core.c
include/linux/serial_core.h
include/uapi/asm-generic/ioctls.h
include/uapi/linux/serial.h

diff --git a/Documentation/serial/serial-iso7816.txt b/Documentation/serial/serial-iso7816.txt
new file mode 100644 (file)
index 0000000..3193d24
--- /dev/null
@@ -0,0 +1,83 @@
+                        ISO7816 SERIAL COMMUNICATIONS
+
+1. INTRODUCTION
+
+  ISO/IEC7816 is a series of standards specifying integrated circuit cards (ICC)
+  also known as smart cards.
+
+2. HARDWARE-RELATED CONSIDERATIONS
+
+  Some CPUs/UARTs (e.g., Microchip AT91) contain a built-in mode capable of
+  handling communication with a smart card.
+
+  For these microcontrollers, the Linux driver should be made capable of
+  working in both modes, and proper ioctls (see later) should be made
+  available at user-level to allow switching from one mode to the other, and
+  vice versa.
+
+3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
+
+  The Linux kernel provides the serial_iso7816 structure (see [1]) to handle
+  ISO7816 communications. This data structure is used to set and configure
+  ISO7816 parameters in ioctls.
+
+  Any driver for devices capable of working both as RS232 and ISO7816 should
+  implement the iso7816_config callback in the uart_port structure. The
+  serial_core calls iso7816_config to do the device specific part in response
+  to TIOCGISO7816 and TIOCSISO7816 ioctls (see below). The iso7816_config
+  callback receives a pointer to struct serial_iso7816.
+
+4. USAGE FROM USER-LEVEL
+
+  From user-level, ISO7816 configuration can be get/set using the previous
+  ioctls. For instance, to set ISO7816 you can use the following code:
+
+       #include <linux/serial.h>
+
+       /* Include definition for ISO7816 ioctls: TIOCSISO7816 and TIOCGISO7816 */
+       #include <sys/ioctl.h>
+
+       /* Open your specific device (e.g., /dev/mydevice): */
+       int fd = open ("/dev/mydevice", O_RDWR);
+       if (fd < 0) {
+               /* Error handling. See errno. */
+       }
+
+       struct serial_iso7816 iso7816conf;
+
+       /* Reserved fields as to be zeroed */
+       memset(&iso7816conf, 0, sizeof(iso7816conf));
+
+       /* Enable ISO7816 mode: */
+       iso7816conf.flags |= SER_ISO7816_ENABLED;
+
+       /* Select the protocol: */
+       /* T=0 */
+       iso7816conf.flags |= SER_ISO7816_T(0);
+       /* or T=1 */
+       iso7816conf.flags |= SER_ISO7816_T(1);
+
+       /* Set the guard time: */
+       iso7816conf.tg = 2;
+
+       /* Set the clock frequency*/
+       iso7816conf.clk = 3571200;
+
+       /* Set transmission factors: */
+       iso7816conf.sc_fi = 372;
+       iso7816conf.sc_di = 1;
+
+       if (ioctl(fd_usart, TIOCSISO7816, &iso7816conf) < 0) {
+               /* Error handling. See errno. */
+       }
+
+       /* Use read() and write() syscalls here... */
+
+       /* Close the device when finished: */
+       if (close (fd) < 0) {
+               /* Error handling. See errno. */
+       }
+
+5. REFERENCES
+
+ [1]    include/uapi/linux/serial.h
index 3729d92d3fa854599a99ba57c56aa33b40b4e0c8..1e9121c9b3c74c16d129ce6fac97f614080dca94 100644 (file)
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 #define TIOCSERCONFIG  0x5453
 #define TIOCSERGWILD   0x5454
index 890245a9f0c4d4064588596ea86c76219f5d2e1b..16aa8a766aec229839602893e245ee020b3c19c2 100644 (file)
@@ -93,6 +93,8 @@
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 /* I hope the range from 0x5480 on is free ... */
 #define TIOCSCTTY      0x5480          /* become controlling tty */
index aafb1c0ca0af63e4aefcc35687a9dfff78370b1e..82d1148c6379a50f0eae5622d7ab8fd71ffb80c1 100644 (file)
@@ -62,6 +62,8 @@
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 #define FIONCLEX       0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX                0x5451
index 41b1a5c1573403b7c0ceea122a37f018eab74f9d..2c145da3b774a136044e9106e96e38559c9e5832 100644 (file)
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 #define TIOCSERCONFIG  0x5453
 #define TIOCSERGWILD   0x5454
index cc62f6f981036b539387abf9205cc8c43e5f18ec..11866d4f60e1679f4b4836276a20a858c7365563 100644 (file)
@@ -95,6 +95,8 @@
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 #define TIOCSERCONFIG  _IO('T', 83) /* 0x5453 */
 #define TIOCSERGWILD   _IOR('T', 84,  int) /* 0x5454 */
index 2df52711e17033f5f725b682d9719306c7f35ec7..7fd2f5873c9e7a9f5878f20be1e47a68666759a3 100644 (file)
@@ -27,6 +27,8 @@
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGRS485     _IOR('T', 0x41, struct serial_rs485)
 #define TIOCSRS485     _IOWR('T', 0x42, struct serial_rs485)
+#define TIOCGISO7816   _IOR('T', 0x43, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x44, struct serial_iso7816)
 
 /* Note that all the ioctls that are not available in Linux have a
  * double underscore on the front to: a) avoid some programs to
index ec43609cbfc533c411e6ddb1bbcda8a3089ebf43..6d4a87296c95cc51636f3109bd213732dde5c6d4 100644 (file)
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 #define TIOCSERCONFIG  _IO('T', 83)
 #define TIOCSERGWILD   _IOR('T', 84,  int)
index ed0133395cc7881f0c8cef725a0d93272a8bee43..0a4e6eeb5ff3d98d101bd2f4b78f5c2c622d267f 100644 (file)
@@ -1308,6 +1308,58 @@ static int uart_set_rs485_config(struct uart_port *port,
        return 0;
 }
 
+static int uart_get_iso7816_config(struct uart_port *port,
+                                  struct serial_iso7816 __user *iso7816)
+{
+       unsigned long flags;
+       struct serial_iso7816 aux;
+
+       if (!port->iso7816_config)
+               return -ENOIOCTLCMD;
+
+       spin_lock_irqsave(&port->lock, flags);
+       aux = port->iso7816;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       if (copy_to_user(iso7816, &aux, sizeof(aux)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int uart_set_iso7816_config(struct uart_port *port,
+                                  struct serial_iso7816 __user *iso7816_user)
+{
+       struct serial_iso7816 iso7816;
+       int i, ret;
+       unsigned long flags;
+
+       if (!port->iso7816_config)
+               return -ENOIOCTLCMD;
+
+       if (copy_from_user(&iso7816, iso7816_user, sizeof(*iso7816_user)))
+               return -EFAULT;
+
+       /*
+        * There are 5 words reserved for future use. Check that userspace
+        * doesn't put stuff in there to prevent breakages in the future.
+        */
+       for (i = 0; i < 5; i++)
+               if (iso7816.reserved[i])
+                       return -EINVAL;
+
+       spin_lock_irqsave(&port->lock, flags);
+       ret = port->iso7816_config(port, &iso7816);
+       spin_unlock_irqrestore(&port->lock, flags);
+       if (ret)
+               return ret;
+
+       if (copy_to_user(iso7816_user, &port->iso7816, sizeof(port->iso7816)))
+               return -EFAULT;
+
+       return 0;
+}
+
 /*
  * Called via sys_ioctl.  We can use spin_lock_irq() here.
  */
@@ -1392,6 +1444,14 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
        case TIOCSRS485:
                ret = uart_set_rs485_config(uport, uarg);
                break;
+
+       case TIOCSISO7816:
+               ret = uart_set_iso7816_config(state->uart_port, uarg);
+               break;
+
+       case TIOCGISO7816:
+               ret = uart_get_iso7816_config(state->uart_port, uarg);
+               break;
        default:
                if (uport->ops->ioctl)
                        ret = uport->ops->ioctl(uport, cmd, arg);
index 079793e5d3fa7f323cd2a13f224c8b32585fe37a..4e2ba4894dcc389317fb4e17d6ee296f4d5ae470 100644 (file)
@@ -144,6 +144,8 @@ struct uart_port {
        void                    (*handle_break)(struct uart_port *);
        int                     (*rs485_config)(struct uart_port *,
                                                struct serial_rs485 *rs485);
+       int                     (*iso7816_config)(struct uart_port *,
+                                                 struct serial_iso7816 *iso7816);
        unsigned int            irq;                    /* irq number */
        unsigned long           irqflags;               /* irq flags  */
        unsigned int            uartclk;                /* base uart clock */
@@ -261,6 +263,7 @@ struct uart_port {
        struct attribute_group  *attr_group;            /* port specific attributes */
        const struct attribute_group **tty_groups;      /* all attributes (serial core use only) */
        struct serial_rs485     rs485;
+       struct serial_iso7816   iso7816;
        void                    *private_data;          /* generic platform data pointer */
 };
 
index 040651735662983693d51f5645f311f128fe63f4..cdc9f4ca8c27504d7058903a2f8fc4de877a82bd 100644 (file)
@@ -79,6 +79,8 @@
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 #define TIOCGPTPEER    _IO('T', 0x41) /* Safely open the slave */
+#define TIOCGISO7816   _IOR('T', 0x42, struct serial_iso7816)
+#define TIOCSISO7816   _IOWR('T', 0x43, struct serial_iso7816)
 
 #define FIONCLEX       0x5450
 #define FIOCLEX                0x5451
index 3fdd0dee8b41bb053c02b8b1ece649e386e7fb95..93eb3c496ff1e85a6af59c539596775c4c9bf35e 100644 (file)
@@ -132,4 +132,21 @@ struct serial_rs485 {
                                           are a royal PITA .. */
 };
 
+/*
+ * Serial interface for controlling ISO7816 settings on chips with suitable
+ * support. Set with TIOCSISO7816 and get with TIOCGISO7816 if supported by
+ * your platform.
+ */
+struct serial_iso7816 {
+       __u32   flags;                  /* ISO7816 feature flags */
+#define SER_ISO7816_ENABLED            (1 << 0)
+#define SER_ISO7816_T_PARAM            (0x0f << 4)
+#define SER_ISO7816_T(t)               (((t) & 0x0f) << 4)
+       __u32   tg;
+       __u32   sc_fi;
+       __u32   sc_di;
+       __u32   clk;
+       __u32   reserved[5];
+};
+
 #endif /* _UAPI_LINUX_SERIAL_H */