From owner-freebsd-embedded@FreeBSD.ORG Sun Apr 26 20:33:52 2015 Return-Path: Delivered-To: freebsd-embedded@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id F087895C for ; Sun, 26 Apr 2015 20:33:51 +0000 (UTC) Received: from mail-wg0-x22a.google.com (mail-wg0-x22a.google.com [IPv6:2a00:1450:400c:c00::22a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority G2" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 7C86C1EC0 for ; Sun, 26 Apr 2015 20:33:51 +0000 (UTC) Received: by wgyo15 with SMTP id o15so97097589wgy.2 for ; Sun, 26 Apr 2015 13:33:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:date:from:to:subject:message-id:mime-version:content-type :content-disposition:hackerspace:user-agent; bh=0Sc01/jkXiBPSYcvc0eNJq/PoJ66oMCK31u4QLtmkU8=; b=e1BYGwpvmwIddNe26fSXMJgv+LLFFLSUy3zbb9dKuUjY9QIq6JkvlAVVb7X3h3jWlO TsOJVZ5+SHSO2Goz8oPdj/DjPLA2VaZm0hTuGqDhh4c0I6A4JwewIOqhabA4OvJZcC/I fVu7v77EfNuVIVqOYqz9ApXVDNgmv0IKhRx04WrRECTndljlYOlwVt5I47cA5LdcAA2d 8krRq1HPQ8BMvmqGK9It9MFSC3Kw4v3UM9dRZjQd+TX6mwCiloss/f2ji5YlERpUJmKh aDUFDqtcp6XJvDmYZfjYsGlm1Evj7Wg2N9D8G1GmaEqIwm+Ku9DEoByYd8RJjgQH83HG c3gw== X-Received: by 10.194.211.8 with SMTP id my8mr16708045wjc.90.1430080429901; Sun, 26 Apr 2015 13:33:49 -0700 (PDT) Received: from gmail.com ([84.92.175.249]) by mx.google.com with ESMTPSA id dq4sm8674595wid.17.2015.04.26.13.33.48 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Apr 2015 13:33:49 -0700 (PDT) Sender: Tom Jones Date: Sun, 26 Apr 2015 21:33:47 +0100 From: Tom Jones To: freebsd-embedded@freebsd.org Subject: SPI User Space Interface Message-ID: <20150426203344.GA27490@gmail.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="6c2NcOVqGQ03X4Wi" Content-Disposition: inline Hackerspace: 57North Hacklab User-Agent: Mutt/1.5.23 (2014-03-12) X-BeenThere: freebsd-embedded@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Dedicated and Embedded Systems List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 26 Apr 2015 20:33:52 -0000 --6c2NcOVqGQ03X4Wi Content-Type: text/plain; charset=us-ascii Content-Disposition: inline This atteched patch adds user space access to spi drivers. Access is possible via read, write and an ioctl call. I have tested this patch on a raspberry-pi B against an avr running a simple program and a small monochrome oled screen based on the ssd1306 display driver. The ioctl requires the use of two new structs. The spi_rdwr_data struct takes a number of spi_ioc_transfer structs, this is modeled on the iic driver. The spi_ioc_transfer struct is compatible with the linux spidev implementation. There is no support for ioctls to set spi device settings are there is in linux, this is due to the limited driver interface in spibus. Only the buffer and length options are used from the spi_ioc_transfer struct, this is from the lack of driver interface on spibus. struct spi_ioc_transfer { void *tx_buf; void *rx_buf; uint32_t len; uint32_t speed_hz; uint16_t delay_usecs; uint8_t bits_per_word; uint8_t cs_change; uint32_t pad; }; struct spi_rdwr_data { struct spi_ioc_transfer *msgs; uint32_t nmsgs; }; #define SPIRDWR _IOW('S', 1, struct spi_rdwr_data) I have only managed to test this code on one platform and against a limited number of spi devices. I think it needs more testing against real hardware, I don't have any lying around. -- Tom @adventureloop adventurist.me :wq --6c2NcOVqGQ03X4Wi Content-Type: text/x-diff; charset=us-ascii Content-Disposition: inline; filename="spidev.patch" Index: sys/conf/files =================================================================== --- sys/conf/files (revision 281862) +++ sys/conf/files (working copy) @@ -2295,6 +2295,7 @@ dev/spibus/spibus.c optional spibus \ dependency "spibus_if.h" dev/spibus/spibus_if.m optional spibus +dev/spibus/spidev.c optional spibus dev/ste/if_ste.c optional ste pci dev/stg/tmc18c30.c optional stg dev/stg/tmc18c30_isa.c optional stg isa Index: sys/dev/spibus/spi.h =================================================================== --- sys/dev/spibus/spi.h (revision 281862) +++ sys/dev/spibus/spi.h (working copy) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2006 M. Warner Losh + * Copyright (c) 2014 Tom Jones * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,3 +39,23 @@ }; #define SPI_CHIP_SELECT_HIGH 0x1 /* Chip select high (else low) */ + +struct spi_ioc_transfer { + void *tx_buf; + void *rx_buf; + + uint32_t len; + uint32_t speed_hz; + + uint16_t delay_usecs; + uint8_t bits_per_word; + uint8_t cs_change; + uint32_t pad; +}; + +struct spi_rdwr_data { + struct spi_ioc_transfer *msgs; + uint32_t nmsgs; +}; + +#define SPIRDWR _IOW('S', 1, struct spi_rdwr_data) Index: sys/dev/spibus/spidev.c =================================================================== --- sys/dev/spibus/spidev.c (revision 0) +++ sys/dev/spibus/spidev.c (working copy) @@ -0,0 +1,310 @@ +/*- + * Copyright (c) 2015 Tom Jones + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "spibus_if.h" + +#define BUFSIZE 1024 + +static MALLOC_DEFINE(M_SPIDEV, "spidev", "SPI data"); + +struct spidev_softc { + + device_t sc_dev; + device_t spi_dev; + int sc_count; /* >0 if device opened */ + + char sc_buffer[BUFSIZE]; /* output buffer */ + char sc_inbuf[BUFSIZE]; /* input buffer */ + + struct cdev *sc_devnode; + struct sx sc_lock; +}; + +static int spidev_probe(device_t); +static int spidev_attach(device_t); +static int spidev_detach(device_t); +static void spidev_identify(driver_t *driver, device_t parent); + +static devclass_t spidev_devclass; + +static device_method_t spidev_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, spidev_identify), + DEVMETHOD(device_probe, spidev_probe), + DEVMETHOD(device_attach, spidev_attach), + DEVMETHOD(device_detach, spidev_detach), + + { 0, 0 } +}; + +static driver_t spidev_driver = { + "spidev", + spidev_methods, + sizeof(struct spidev_softc), +}; + +static d_open_t spidevopen; +static d_close_t spidevclose; +static d_write_t spidevwrite; +static d_read_t spidevread; +static d_ioctl_t spidevioctl; + +static struct cdevsw spidev_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_TRACKCLOSE, + .d_open = spidevopen, + .d_close = spidevclose, + .d_read = spidevread, + .d_write = spidevwrite, + .d_ioctl = spidevioctl, + .d_name = "spidev", +}; + +static void +spidev_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, "spidev", -1) == NULL) + BUS_ADD_CHILD(parent, 0, "spidev", -1); + else + uprintf("SPIDEV IDENTIFY nothing found\n"); +} + +static int +spidev_probe(device_t dev) +{ + device_set_desc(dev, "SPI generic I/O"); + return (0); +} + +static int +spidev_attach(device_t dev) +{ + struct spidev_softc *sc = (struct spidev_softc *)device_get_softc(dev); + sc->sc_dev = dev; + + device_t spibus = device_get_parent(dev); + sc->spi_dev = device_get_parent(spibus); + + if(sc->spi_dev == NULL) { + device_printf(dev, "failed to attach to spi device\n"); + return (ENXIO); + } + sc->sc_devnode = make_dev(&spidev_cdevsw, device_get_unit(dev), + UID_ROOT, GID_WHEEL, + 0600, "spi%d", device_get_unit(dev)); + + if (sc->sc_devnode == NULL) { + device_printf(dev, "failed to create character device\n"); + return (ENXIO); + } + sc->sc_devnode->si_drv1 = sc; + + return (0); +} + +static int +spidev_detach(device_t dev) +{ + struct spidev_softc *sc = (struct spidev_softc *)device_get_softc(dev); + + if (sc->sc_devnode) + destroy_dev(sc->sc_devnode); + + return (0); +} + +static int +spidevopen(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + struct spidev_softc *sc = dev->si_drv1; + + if(sc->sc_count > 0) + return (EBUSY); + sc->sc_count++; + + return (0); +} + +static int +spidevclose(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + struct spidev_softc *sc = dev->si_drv1; + + sc->sc_count--; + if(sc->sc_count < 0) + panic("%s: spidev_count < 0!",__func__); + + return (0); +} + +static int +spidevwrite(struct cdev *dev, struct uio * uio, int ioflag) +{ + struct spidev_softc *sc = dev->si_drv1; + device_t spi_hw = sc->spi_dev; + int error; + + struct spi_command cmd; + size_t size = MIN(uio->uio_resid,BUFSIZE); + + cmd.rx_cmd_sz = cmd.tx_cmd_sz = 0; + + cmd.tx_data = sc->sc_inbuf; + cmd.rx_data = sc->sc_buffer; + cmd.rx_data_sz = cmd.tx_data_sz = size; + + memset(sc->sc_buffer, 0, size); + + error = uiomove(sc->sc_inbuf, size, uio); + if(error == 0) + error = SPIBUS_TRANSFER(spi_hw, sc->sc_dev, &cmd); + + return (error); +} + +static int +spidevread(struct cdev *dev, struct uio * uio, int ioflag) +{ + struct spidev_softc *sc = dev->si_drv1; + struct spi_command cmd; + device_t spi_hw = sc->spi_dev; + int error; + + size_t size = MIN(uio->uio_resid,BUFSIZE); + + cmd.rx_cmd_sz = cmd.tx_cmd_sz = 0; + + cmd.tx_data = sc->sc_inbuf; + cmd.rx_data = sc->sc_buffer; + cmd.rx_data_sz = cmd.tx_data_sz = size; + + error = SPIBUS_TRANSFER(spi_hw,sc->sc_dev, &cmd); + + if(error == 0) + error = uiomove(sc->sc_buffer, size, uio); + + return (error); +} + +static int +spidevioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) +{ + struct spidev_softc *sc = dev->si_drv1; + struct spi_command spicmd; + struct spi_ioc_transfer *s, *operations = NULL; + struct spi_rdwr_data *spi_data = (struct spi_rdwr_data *)data; + device_t spi_hw = sc->spi_dev; + int error; + + switch (cmd) { + case SPIRDWR: + if(spi_data->nmsgs <= 0) + return EINVAL; + + operations = malloc(spi_data->nmsgs * sizeof(struct spi_ioc_transfer), + M_SPIDEV, + M_WAITOK); + + error = copyin(spi_data->msgs, operations, + spi_data->nmsgs * sizeof(struct spi_ioc_transfer)); + + if(error) + goto errout; + + for(int i = 0; i < spi_data->nmsgs; i++) { + s = &operations[i]; + + spicmd.tx_cmd_sz = spicmd.rx_cmd_sz = 0; + + spicmd.tx_data_sz = s->len; + spicmd.rx_data_sz = s->len; + + spicmd.tx_data = malloc(s->len, M_SPIDEV, M_WAITOK); + spicmd.rx_data = malloc(s->len, M_SPIDEV, M_WAITOK); + + error = copyin(s->tx_buf, spicmd.tx_data, spicmd.tx_data_sz); + + if(error) + goto errout; + + error = SPIBUS_TRANSFER(spi_hw,sc->sc_dev, &spicmd); + + if(error) + goto errout; + + error = copyout(spicmd.rx_data, s->rx_buf, spicmd.rx_data_sz); + + if(error) + goto errout; + + if(spicmd.tx_data) { + free(spicmd.tx_data, M_SPIDEV); + spicmd.tx_data = NULL; + } + if(spicmd.rx_data) { + free(spicmd.rx_data, M_SPIDEV); + spicmd.rx_data = NULL; + } + } + + break; + default: + error = ENOTTY; + } + + errout: + if(operations != NULL) + free(operations, M_SPIDEV); + if(spicmd.tx_data) + free(spicmd.tx_data, M_SPIDEV); + if(spicmd.rx_data) + free(spicmd.rx_data, M_SPIDEV); + return (error); +} + +DRIVER_MODULE(spidev, spibus, spidev_driver, spidev_devclass, 0, 0); +MODULE_VERSION(spidev, 1); Property changes on: sys/dev/spibus/spidev.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property --6c2NcOVqGQ03X4Wi--