/* * This supports access to SPI devices using normal userspace I/O calls. * Note that while traditional UNIX/POSIX I/O semantics are half duplex, * and often mask message boundaries, full SPI support requires full duplex * transfers. There are several kinds of internal message boundaries to * handle chipselect management and other protocol options. * * SPI has a character major number assigned. We allocate minor numbers * dynamically using a bitmask. You must use hotplug tools, such as udev * (or mdev with busybox) to create and destroy the /dev/spidevB.C device * nodes, since there is no fixed association of minor numbers with any * particular SPI bus or device.
*/ #define SPIDEV_MAJOR 153 /* assigned */ #define N_SPI_MINORS 32 /* ... up to 256 */
/* Bit masks for spi_device.mode management. Note that incorrect * settings for some settings can cause *lots* of trouble for other * devices on a shared bus: * * - CS_HIGH ... this device will be active when it shouldn't be * - 3WIRE ... when active, it won't behave as it should * - NO_CS ... there will be no explicit message boundaries; this * is completely incompatible with the shared bus model * - READY ... transfers may proceed when they shouldn't. * * REVISIT should changing those flags be privileged?
*/ #define SPI_MODE_MASK (SPI_MODE_X_MASK | SPI_CS_HIGH \
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
| SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \
| SPI_RX_QUAD | SPI_RX_OCTAL \
| SPI_RX_CPHA_FLIP | SPI_3WIRE_HIZ \
| SPI_MOSI_IDLE_LOW)
/* chipselect only toggles at start or end of operation */ if (count > bufsiz) return -EMSGSIZE;
spidev = filp->private_data;
mutex_lock(&spidev->buf_lock);
status = spidev_sync_read(spidev, count); if (status > 0) { unsignedlong missing;
missing = copy_to_user(buf, spidev->rx_buffer, status); if (missing == status)
status = -EFAULT; else
status = status - missing;
}
mutex_unlock(&spidev->buf_lock);
/* Construct spi_message, copying any tx data to bounce buffer. * We walk the array of user-provided transfers, using each one * to initialize a kernel version of the same transfer.
*/
tx_buf = spidev->tx_buffer;
rx_buf = spidev->rx_buffer;
total = 0;
tx_total = 0;
rx_total = 0; for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
n;
n--, k_tmp++, u_tmp++) { /* Ensure that also following allocations from rx_buf/tx_buf will meet * DMA alignment requirements.
*/ unsignedint len_aligned = ALIGN(u_tmp->len, ARCH_DMA_MINALIGN);
k_tmp->len = u_tmp->len;
total += k_tmp->len; /* Since the function returns the total length of transfers * on success, restrict the total to positive int values to * avoid the return value looking like an error. Also check * each transfer length to avoid arithmetic overflow.
*/ if (total > INT_MAX || k_tmp->len > INT_MAX) {
status = -EMSGSIZE; goto done;
}
if (u_tmp->rx_buf) { /* this transfer needs space in RX bounce buffer */
rx_total += len_aligned; if (rx_total > bufsiz) {
status = -EMSGSIZE; goto done;
}
k_tmp->rx_buf = rx_buf;
rx_buf += len_aligned;
} if (u_tmp->tx_buf) { /* this transfer needs space in TX bounce buffer */
tx_total += len_aligned; if (tx_total > bufsiz) {
status = -EMSGSIZE; goto done;
}
k_tmp->tx_buf = tx_buf; if (copy_from_user(tx_buf, (const u8 __user *)
(uintptr_t) u_tmp->tx_buf,
u_tmp->len)) goto done;
tx_buf += len_aligned;
}
/* Check type and command number */ if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) return -ENOTTY;
/* guard against device removal before, or while, * we issue this ioctl.
*/
spidev = filp->private_data;
mutex_lock(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi); if (spi == NULL) {
mutex_unlock(&spidev->spi_lock); return -ESHUTDOWN;
}
ctlr = spi->controller;
/* use the buffer lock here for triple duty: * - prevent I/O (from us) so calling spi_setup() is safe; * - prevent concurrent SPI_IOC_WR_* from morphing * data fields while SPI_IOC_RD_* reads them; * - SPI_IOC_MESSAGE needs the buffer locked "normally".
*/
mutex_lock(&spidev->buf_lock);
switch (cmd) { /* read requests */ case SPI_IOC_RD_MODE: case SPI_IOC_RD_MODE32:
tmp = spi->mode & SPI_MODE_MASK;
if (ctlr->use_gpio_descriptors && spi_get_csgpiod(spi, 0))
tmp &= ~SPI_CS_HIGH;
/* guard against device removal before, or while, * we issue this ioctl.
*/
spidev = filp->private_data;
mutex_lock(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi); if (spi == NULL) {
mutex_unlock(&spidev->spi_lock); return -ESHUTDOWN;
}
/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
mutex_lock(&spidev->buf_lock);
/* Check message and copy into scratch area */
ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc); if (IS_ERR(ioc)) {
retval = PTR_ERR(ioc); goto done;
} if (!ioc) goto done; /* n_ioc is also 0 */
/* The main reason to have this class is to make mdev/udev create the * /dev/spidevB.C character device nodes exposing our userspace API. * It also simplifies memory management.
*/
/* * spidev should never be referenced in DT without a specific compatible string, * it is a Linux implementation thing rather than a description of the hardware.
*/ staticint spidev_of_check(struct device *dev)
{ if (device_property_match_string(dev, "compatible", "spidev") < 0) return 0;
dev_err(dev, "spidev listed directly in DT is not supported\n"); return -EINVAL;
}
/* Dummy SPI devices not to be used in production systems */ staticint spidev_acpi_check(struct device *dev)
{
dev_warn(dev, "do not use this driver in production systems!\n"); return 0;
}
staticconststruct acpi_device_id spidev_acpi_ids[] = { /* * The ACPI SPT000* devices are only meant for development and * testing. Systems used in production should have a proper ACPI * description of the connected peripheral and they should also use * a proper driver instead of poking directly to the SPI bus.
*/
{ "SPT0001", (kernel_ulong_t)&spidev_acpi_check },
{ "SPT0002", (kernel_ulong_t)&spidev_acpi_check },
{ "SPT0003", (kernel_ulong_t)&spidev_acpi_check },
{},
};
MODULE_DEVICE_TABLE(acpi, spidev_acpi_ids);
staticint spidev_probe(struct spi_device *spi)
{ int (*match)(struct device *dev); struct spidev_data *spidev; int status; unsignedlong minor;
match = device_get_match_data(&spi->dev); if (match) {
status = match(&spi->dev); if (status) return status;
}
/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) return -ENOMEM;
/* Initialize the driver data */
spidev->spi = spi;
mutex_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry);
/* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working.
*/
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS); if (minor < N_SPI_MINORS) { struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
dev = device_create(&spidev_class, &spi->dev, spidev->devt,
spidev, "spidev%d.%d",
spi->controller->bus_num, spi_get_chipselect(spi, 0));
status = PTR_ERR_OR_ZERO(dev);
} else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
} if (status == 0) {
set_bit(minor, minors);
list_add(&spidev->device_entry, &device_list);
}
mutex_unlock(&device_list_lock);
spidev->speed_hz = spi->max_speed_hz;
if (status == 0)
spi_set_drvdata(spi, spidev); else
kfree(spidev);
/* NOTE: suspend/resume methods are not necessary here. * We don't do anything except pass the requests to/from * the underlying controller. The refrigerator handles * most issues; the controller driver handles the rest.
*/
};
/* Claim our 256 reserved device numbers. Then register a class * that will key udev/mdev to add/remove /dev nodes. Last, register * the driver which manages those device numbers.
*/
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); if (status < 0) return status;
status = class_register(&spidev_class); if (status) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); return status;
}
status = spi_register_driver(&spidev_spi_driver); if (status < 0) {
class_unregister(&spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
} return status;
}
module_init(spidev_init);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.