tty/serial_core: add ISO7816 infrastructure

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>
This commit is contained in:
Nicolas Ferre 2018-09-26 14:58:47 +02:00 committed by Greg Kroah-Hartman
parent c550f01c81
commit ad8c0eaa0a
12 changed files with 179 additions and 0 deletions

View File

@ -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

View File

@ -102,6 +102,8 @@
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ #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 TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454 #define TIOCSERGWILD 0x5454

View File

@ -93,6 +93,8 @@
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ #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 ... */ /* I hope the range from 0x5480 on is free ... */
#define TIOCSCTTY 0x5480 /* become controlling tty */ #define TIOCSCTTY 0x5480 /* become controlling tty */

View File

@ -62,6 +62,8 @@
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ #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 FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451 #define FIOCLEX 0x5451

View File

@ -102,6 +102,8 @@
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ #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 TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454 #define TIOCSERGWILD 0x5454

View File

@ -95,6 +95,8 @@
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ #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 TIOCSERCONFIG _IO('T', 83) /* 0x5453 */
#define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */

View File

@ -27,6 +27,8 @@
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485) #define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485)
#define TIOCSRS485 _IOWR('T', 0x42, 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 /* Note that all the ioctls that are not available in Linux have a
* double underscore on the front to: a) avoid some programs to * double underscore on the front to: a) avoid some programs to

View File

@ -107,6 +107,8 @@
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ #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 TIOCSERCONFIG _IO('T', 83)
#define TIOCSERGWILD _IOR('T', 84, int) #define TIOCSERGWILD _IOR('T', 84, int)

View File

@ -1308,6 +1308,58 @@ static int uart_set_rs485_config(struct uart_port *port,
return 0; 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. * 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: case TIOCSRS485:
ret = uart_set_rs485_config(uport, uarg); ret = uart_set_rs485_config(uport, uarg);
break; 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: default:
if (uport->ops->ioctl) if (uport->ops->ioctl)
ret = uport->ops->ioctl(uport, cmd, arg); ret = uport->ops->ioctl(uport, cmd, arg);

View File

@ -144,6 +144,8 @@ struct uart_port {
void (*handle_break)(struct uart_port *); void (*handle_break)(struct uart_port *);
int (*rs485_config)(struct uart_port *, int (*rs485_config)(struct uart_port *,
struct serial_rs485 *rs485); struct serial_rs485 *rs485);
int (*iso7816_config)(struct uart_port *,
struct serial_iso7816 *iso7816);
unsigned int irq; /* irq number */ unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */ unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */ unsigned int uartclk; /* base uart clock */
@ -261,6 +263,7 @@ struct uart_port {
struct attribute_group *attr_group; /* port specific attributes */ struct attribute_group *attr_group; /* port specific attributes */
const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */
struct serial_rs485 rs485; struct serial_rs485 rs485;
struct serial_iso7816 iso7816;
void *private_data; /* generic platform data pointer */ void *private_data; /* generic platform data pointer */
}; };

View File

@ -79,6 +79,8 @@
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ #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 FIONCLEX 0x5450
#define FIOCLEX 0x5451 #define FIOCLEX 0x5451

View File

@ -132,4 +132,21 @@ struct serial_rs485 {
are a royal PITA .. */ 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 */ #endif /* _UAPI_LINUX_SERIAL_H */