Re: char interface to sdio, chdio.c

From: Arnd Bergmann
Date: Thu Jul 17 2014 - 06:17:12 EST


On Wednesday 16 July 2014 22:33:47 Greg KH wrote:
> + mmc: Char SDIO Device Driver

A few comments on the ioctl interface, since I suppose we'll have
to live with that once it gets merged.

> +Interface
> +=========
> +The Char SDIO Device Driver has two char device interfaces:
> + - Control Interface;
> + - Function Interface.
> +
> +Char SDIO Device Driver Control Interface consists of:
> + - open() - device node is /dev/csdio0;
> + - close()
> + - ioctl() - the following options are available:
> + - CSDIO_IOC_ENABLE_HIGHSPEED_MODE;
> + - CSDIO_IOC_SET_DATA_TRANSFER_CLOCKS;
> + - CSDIO_IOC_ENABLE_ISR;
> + - CSDIO_IOC_DISABLE_ISR.


The documentation doesn't match the code.:

CSDIO_IOC_GET/SET_VDD are implemented in the control device,
not the function device, and that is probably a good idea.
CSDIO_IOC_ENABLE_HIGHSPEED_MODE is not implemented
at all. CSDIO_IOC_ENABLE_ISR seems redundant with what
the CSDIO_IOC_{DIS,}CONNECT_ISR functions do, but I could be
wrong here.

The sysfs interface may actually be more appropriate than the
control node, if I was to pick one of the two.

> +Char SDIO Device Driver Function Interface consists of:
> + - open() - device node is /dev/csdiofX, where X is Function Id;
> + - close()
> + - read() - send CMD53 read;
> + - write() - send CMD53 write;
> + - ioctl() - the following options are available:
> + - CSDIO_IOC_SET_OP_CODE - 0 fixed adrress, 1 autoincrement.
> + - CSDIO_IOC_FUNCTION_SET_BLOCK_SIZE;
> + - CSDIO_IOC_SET_BLOCK_MODE - 0 byte mode, 1 block mode;
> + - CSDIO_IOC_CMD52 - execute CMD52, receives the
> + following structure as a parameter:
> + struct csdio_cmd52_ctrl_t {
> + uint32_t m_write; // 0 - read, 1 -write
> + uint32_t m_address;
> + uint32_t m_data; // data to write or read data
> + uint32_t m_ret; // command execution status
> + }__attribute__ ((packed));
> + - CSDIO_IOC_CMD53 - setup CMD53 data transfer, receives the
> + following structure as a parameter:
> + struct csdio_cmd53_ctrl_t {
> + uint32_t m_block_mode;
> + uint32_t m_op_code;
> + uint32_t m_address;
> + }__attribute__ ((packed));
> + - CSDIO_IOC_CONNECT_ISR;
> + - CSDIO_IOC_DISCONNECT_ISR;
> + - CSDIO_IOC_GET_VDD;
> + - CSDIO_IOC_SET_VDD.
> +
> +Additionally, user space application can use fcntl system call with
> +parameters F_SETOWN and F_SETFL in order to set an asynchronous
> +callback for SDIO interrupt.

The read/write API seems very strange. SDIO has multiple ways of
passing data between OS and device using CMD52 and CMD53, and they
are all done differently here:

- A one-byte read or write uses a single CSDIO_IOC_CMD52 ioctl.
This could be done using pread/pwrite with length '1' rather than
an ioctl, if we want to.

- a byte-addressed multi-byte read/write needs to first 'seek' using
CSDIO_IOC_CMD53 or and then transfer using read() or write().

- a block-addressed read/write is similar, but the number of bytes
copied to/from user space is now multiplied with the block size (!).
The block size needs to be set using both CSDIO_IOC_SET_BLOCK_MODE
and CSDIO_IOC_CMD53, using the same value.

- to do a multi-byte read/write at a constant address rather than
a block copy, CSDIO_IOC_CMD53 needs to set at least one bit (not
necessarily the one used in the actual command) in m_op_code.

I guess all of this could be done using pread/pwrite, using the
loff_t value to encode the m_address in the lower bits and the
mode (single-byte, multi-byte, multi-byte auto-increment,
block, block auto-increment) in some of the upper bits.

Alternatively, the read/write functions could be removed completely
and everything be done in the ioctls, but in a more consistent
way.

> +
> +#define CSDIO_IOC_MAGIC 'm'
> +
> +#define CSDIO_IOC_ENABLE_HIGHSPEED_MODE _IO(CSDIO_IOC_MAGIC, 0)
> +#define CSDIO_IOC_SET_DATA_TRANSFER_CLOCKS _IO(CSDIO_IOC_MAGIC, 1)
> +#define CSDIO_IOC_SET_OP_CODE _IO(CSDIO_IOC_MAGIC, 2)
> +#define CSDIO_IOC_FUNCTION_SET_BLOCK_SIZE _IO(CSDIO_IOC_MAGIC, 3)
> +#define CSDIO_IOC_SET_BLOCK_MODE _IO(CSDIO_IOC_MAGIC, 4)
> +#define CSDIO_IOC_CONNECT_ISR _IO(CSDIO_IOC_MAGIC, 5)
> +#define CSDIO_IOC_DISCONNECT_ISR _IO(CSDIO_IOC_MAGIC, 6)
> +#define CSDIO_IOC_CMD52 _IO(CSDIO_IOC_MAGIC, 7)
> +#define CSDIO_IOC_CMD53 _IO(CSDIO_IOC_MAGIC, 8)
> +#define CSDIO_IOC_ENABLE_ISR _IO(CSDIO_IOC_MAGIC, 9)
> +#define CSDIO_IOC_DISABLE_ISR _IO(CSDIO_IOC_MAGIC, 10)
> +#define CSDIO_IOC_SET_VDD _IO(CSDIO_IOC_MAGIC, 11)
> +#define CSDIO_IOC_GET_VDD _IO(CSDIO_IOC_MAGIC, 12)
> +
> +#define CSDIO_IOC_MAXNR 12

The command codes above should use _IOR or _IOW where appropriate to
encode the size of the transferred data.

> +
> +struct csdio_cmd53_ctrl_t {
> + uint32_t m_block_mode; /* data tran. byte(0)/block(1) mode */
> + uint32_t m_op_code; /* address auto increment flag */
> + uint32_t m_address;
> +} __attribute__ ((packed));
> +
> +struct csdio_cmd52_ctrl_t {
> + uint32_t m_write;
> + uint32_t m_address;
> + uint32_t m_data;
> + uint32_t m_ret;
> +} __attribute__ ((packed));

The packing is not necessary here, but at least all of these are
compatible between 32 and 64 bit, so enabling compat mode is a one-line
change and requires no conversion.

Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/